mirror of
https://github.com/deepseek-ai/deepseek-harness.git
synced 2026-09-12 04:01:20 +00:00
merge: bring master into feat/pwsh-persistent-pty
This commit is contained in:
@@ -19,11 +19,32 @@ from pathlib import Path
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
SDK_DISTRIBUTION = "deepseek-harness-sdk"
|
||||
RUNTIME_DISTRIBUTION = "deepseek-harness-runtime-bin"
|
||||
PLATFORMS = {
|
||||
"linux-x64": ("manylinux_2_28_x86_64", "dsh-jsonrpc-agent-pkg-linux-x64"),
|
||||
"linux-arm64": ("manylinux_2_28_aarch64", "dsh-jsonrpc-agent-pkg-linux-arm64"),
|
||||
"macos-arm64": ("macosx_11_0_arm64", "dsh-jsonrpc-agent-pkg-macos-arm64"),
|
||||
}
|
||||
PLATFORM_MANIFEST = ROOT / "python" / "sdk-runtime" / "platforms.json"
|
||||
|
||||
|
||||
def load_platforms(path: Path = PLATFORM_MANIFEST) -> dict[str, tuple[str, str]]:
|
||||
"""Load the release platform tag and executable pairs from the build manifest."""
|
||||
try:
|
||||
payload = json.loads(path.read_text())
|
||||
except (OSError, json.JSONDecodeError) as error:
|
||||
raise ValueError(f"could not read runtime platform manifest from {path}") from error
|
||||
if not isinstance(payload, dict) or not payload:
|
||||
raise ValueError(f"{path} must contain a non-empty platform object")
|
||||
platforms: dict[str, tuple[str, str]] = {}
|
||||
for name, raw in payload.items():
|
||||
if (
|
||||
not isinstance(name, str)
|
||||
or not isinstance(raw, dict)
|
||||
or set(raw) != {"tag", "executable"}
|
||||
or not isinstance(raw["tag"], str)
|
||||
or not isinstance(raw["executable"], str)
|
||||
):
|
||||
raise ValueError(f"{path} platform entries must contain string tag and executable fields")
|
||||
platforms[name] = (raw["tag"], raw["executable"])
|
||||
return platforms
|
||||
|
||||
|
||||
PLATFORMS = load_platforms()
|
||||
|
||||
|
||||
def runtime_suffixes(executable_name: str) -> tuple[str, ...]:
|
||||
@@ -35,7 +56,7 @@ def main() -> None:
|
||||
parser.add_argument("--package", choices=("sdk", "runtime"), required=True)
|
||||
parser.add_argument(
|
||||
"--tag",
|
||||
help="optional python-vX.Y.Z release tag; it must match package.json",
|
||||
help="optional python-v<repository-version> release tag; it must match package.json",
|
||||
)
|
||||
parser.add_argument("--output-dir", type=Path, required=True)
|
||||
parser.add_argument("--platform", choices=tuple(PLATFORMS))
|
||||
@@ -146,8 +167,29 @@ def rewrite_version(pyproject: Path, version: str) -> None:
|
||||
pyproject.write_text(text)
|
||||
|
||||
|
||||
def stage_license_files(destination: Path, *, include_notices: bool) -> None:
|
||||
"""Copy legal files and declare them as wheel license payloads."""
|
||||
shutil.copy2(ROOT / "LICENSE", destination / "LICENSE")
|
||||
license_files = '["LICENSE"]'
|
||||
if include_notices:
|
||||
shutil.copy2(ROOT / "THIRD_PARTY_NOTICES.md", destination / "THIRD_PARTY_NOTICES.md")
|
||||
license_files = '["LICENSE", "THIRD_PARTY_NOTICES.md"]'
|
||||
pyproject = destination / "pyproject.toml"
|
||||
text, count = re.subn(
|
||||
r'^(license = "[^"]+")$',
|
||||
rf"\1\nlicense-files = {license_files}",
|
||||
pyproject.read_text(),
|
||||
count=1,
|
||||
flags=re.MULTILINE,
|
||||
)
|
||||
if count != 1:
|
||||
raise RuntimeError(f"could not declare license files in {pyproject}")
|
||||
pyproject.write_text(text)
|
||||
|
||||
|
||||
def stage_sdk(destination: Path, version: str) -> None:
|
||||
copy_package(ROOT / "python" / "sdk", destination)
|
||||
stage_license_files(destination, include_notices=False)
|
||||
pyproject = destination / "pyproject.toml"
|
||||
rewrite_version(pyproject, version)
|
||||
text, count = re.subn(
|
||||
@@ -163,6 +205,7 @@ def stage_sdk(destination: Path, version: str) -> None:
|
||||
|
||||
def stage_runtime(destination: Path, version: str, executable: Path, executable_name: str) -> None:
|
||||
copy_package(ROOT / "python" / "sdk-runtime", destination)
|
||||
stage_license_files(destination, include_notices=True)
|
||||
rewrite_version(destination / "pyproject.toml", version)
|
||||
runtime_dir = destination / "src" / "deepseek_harness_runtime" / "runtime"
|
||||
runtime_dir.mkdir(parents=True, exist_ok=True)
|
||||
@@ -191,6 +234,16 @@ def verify_wheel(
|
||||
raise RuntimeError(
|
||||
f"{wheel} has distribution name {metadata.get('Name')}, expected {expected_distribution}"
|
||||
)
|
||||
if metadata.get("License-Expression") != "BSD-3-Clause":
|
||||
raise RuntimeError(
|
||||
f"{wheel} has license expression {metadata.get('License-Expression')}, expected BSD-3-Clause"
|
||||
)
|
||||
expected_license_files = ["LICENSE"] if package == "sdk" else ["LICENSE", "THIRD_PARTY_NOTICES.md"]
|
||||
license_files = [Path(name).name for name in metadata.get_all("License-File") or []]
|
||||
if license_files != expected_license_files:
|
||||
raise RuntimeError(
|
||||
f"{wheel} has license files {license_files}, expected {expected_license_files}"
|
||||
)
|
||||
runtime_files = [
|
||||
name for name in archive.namelist() if "/runtime/dsh-jsonrpc-agent-pkg-" in name
|
||||
]
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Reject runtime executables that require newer macOS than their wheel tag."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import re
|
||||
import runpy
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
RELEASE = runpy.run_path(str(ROOT / "scripts" / "build-python-release.py"))
|
||||
MACOS_PLATFORM_TAG = RELEASE["PLATFORMS"]["macos-arm64"][0]
|
||||
|
||||
|
||||
def parse_version(value: str) -> tuple[int, ...]:
|
||||
"""Parse a dot-separated numeric deployment version."""
|
||||
if re.fullmatch(r"\d+(?:\.\d+)*", value) is None:
|
||||
raise ValueError(f"invalid macOS deployment version: {value!r}")
|
||||
return tuple(int(part) for part in value.split("."))
|
||||
|
||||
|
||||
def claimed_version(platform_tag: str) -> tuple[int, ...]:
|
||||
"""Return the minimum macOS version encoded by a wheel platform tag."""
|
||||
match = re.fullmatch(r"macosx_(\d+)_(\d+)_arm64", platform_tag)
|
||||
if match is None:
|
||||
raise ValueError(f"unsupported macOS wheel platform tag: {platform_tag!r}")
|
||||
return int(match.group(1)), int(match.group(2))
|
||||
|
||||
|
||||
def parse_otool_deployment_target(output: str) -> tuple[int, ...]:
|
||||
"""Return the newest deployment target from one or more Mach-O slices."""
|
||||
versions = [
|
||||
parse_version(match.group(1))
|
||||
for match in re.finditer(r"^\s*minos\s+(\d+(?:\.\d+)*)\s*$", output, re.MULTILINE)
|
||||
]
|
||||
if not versions:
|
||||
raise ValueError("otool output contains no LC_BUILD_VERSION deployment target")
|
||||
return max(versions)
|
||||
|
||||
|
||||
def deployment_target(executable: Path) -> tuple[int, ...]:
|
||||
"""Read one Mach-O executable's deployment target with ``otool``."""
|
||||
if not executable.is_file():
|
||||
raise FileNotFoundError(f"runtime executable does not exist: {executable}")
|
||||
result = subprocess.run(
|
||||
["otool", "-l", str(executable)],
|
||||
check=True,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
)
|
||||
try:
|
||||
return parse_otool_deployment_target(result.stdout)
|
||||
except ValueError as error:
|
||||
raise ValueError(f"{executable}: {error}") from error
|
||||
|
||||
|
||||
def ensure_compatible(
|
||||
executable: Path, actual: tuple[int, ...], platform_tag: str
|
||||
) -> None:
|
||||
"""Reject an executable whose deployment target exceeds its wheel claim."""
|
||||
claimed = claimed_version(platform_tag)
|
||||
width = max(len(actual), len(claimed))
|
||||
padded_actual = actual + (0,) * (width - len(actual))
|
||||
padded_claimed = claimed + (0,) * (width - len(claimed))
|
||||
if padded_actual > padded_claimed:
|
||||
rendered = ".".join(str(part) for part in actual)
|
||||
raise RuntimeError(
|
||||
f"{executable} requires macOS {rendered} but the wheel claims {platform_tag}"
|
||||
)
|
||||
|
||||
|
||||
def validate_deployment_targets(
|
||||
executables: list[Path], platform_tag: str = MACOS_PLATFORM_TAG
|
||||
) -> list[tuple[Path, tuple[int, ...]]]:
|
||||
"""Validate every executable and return its measured deployment target."""
|
||||
measured = [(executable, deployment_target(executable)) for executable in executables]
|
||||
for executable, actual in measured:
|
||||
ensure_compatible(executable, actual, platform_tag)
|
||||
return measured
|
||||
|
||||
|
||||
def main() -> None:
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("executables", type=Path, nargs="+")
|
||||
args = parser.parse_args()
|
||||
for executable, version in validate_deployment_targets(args.executables):
|
||||
rendered = ".".join(str(part) for part in version)
|
||||
print(f"{executable}: macOS {rendered} <= {MACOS_PLATFORM_TAG}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -53,7 +53,9 @@ const releaseMemberDirectory = /^(?:packages\/[^/]+\/[^/]+|apps\/[^/]+|vendor\/[
|
||||
const localArtifactDirs = new Set(['node_modules'])
|
||||
const appPackageFiles: Readonly<Record<string, readonly string[]>> = {
|
||||
'@deepseek-ai/dsh': ['lib/*.js', 'config'],
|
||||
'@deepseek-ai/dsh-frontend': ['dist'],
|
||||
// The Web build emits sourcemaps for browser debugging; publishing them is
|
||||
// what the payload policy forbids, so the bundle ships without them.
|
||||
'@deepseek-ai/dsh-frontend': ['dist', '!dist/**/*.map'],
|
||||
}
|
||||
|
||||
/** The subset of package.json fields this constraint check cares about. */
|
||||
@@ -151,7 +153,6 @@ function sameStringList(actual: readonly string[] | undefined, expected: readonl
|
||||
|
||||
function expectedDshPackageFiles(manifest: PackageManifest): readonly string[] {
|
||||
const extras = manifest.name ? packageFileExtras[manifest.name] ?? [] : []
|
||||
const typeRTRemoteNavigation = hasTypeRTRemoteNavigation(manifest)
|
||||
return [
|
||||
'lib/index.js',
|
||||
// Every package publishes its invariant ownership companion as a separate
|
||||
@@ -184,13 +185,8 @@ function expectedDshPackageFiles(manifest: PackageManifest): readonly string[] {
|
||||
...hasExportPair(manifest, './client/typert', './lib/typert.client.d.ts', './lib/typert.client.js')
|
||||
? ['lib/typert.client.js', 'lib/typert.client.d.ts']
|
||||
: [],
|
||||
...typeRTRemoteNavigation
|
||||
? [
|
||||
'lib/typert.remote-client.js',
|
||||
'lib/typert.remote-client.d.ts',
|
||||
'lib/typert.remote-client.d.ts.map',
|
||||
'src',
|
||||
]
|
||||
...hasTypeRTRemoteNavigation(manifest)
|
||||
? ['lib/typert.remote-client.js', 'lib/typert.remote-client.d.ts']
|
||||
: [],
|
||||
]
|
||||
}
|
||||
@@ -269,9 +265,8 @@ function checkWorkspace({ dir, manifest }: WorkspaceManifest): string[] {
|
||||
|
||||
if (manifest.name?.startsWith('@deepseek-ai/')) {
|
||||
const allowedSources = publicationSourceAllowlist[manifest.name] ?? []
|
||||
const publicationPolicy = { typeRTRemoteNavigation: hasTypeRTRemoteNavigation(manifest) }
|
||||
for (const file of manifest.files ?? []) {
|
||||
if (isForbiddenPublicationFile(file, publicationPolicy) && !allowedSources.includes(file)) {
|
||||
if (isForbiddenPublicationFile(file) && !allowedSources.includes(file)) {
|
||||
errors.push(`${label}: package.json files must not publish ${JSON.stringify(file)}`)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,6 +84,73 @@ describe('CI workflow', () => {
|
||||
expect(aggregate.needs).not.toContain('serial-windows')
|
||||
})
|
||||
|
||||
it('exempts push from cancellation, so one master merge does not cancel the running drill', () => {
|
||||
const workflow = loadWorkflow('.github/workflows/ci.yml')
|
||||
if (!isRecord(workflow.jobs) || !isRecord(workflow.concurrency)) {
|
||||
throw new TypeError('CI workflow must define jobs and a workflow-level concurrency block')
|
||||
}
|
||||
|
||||
// Cancellation applies to the whole superseded RUN, so this has to be
|
||||
// decided at workflow level and gated on the event: a job-level group
|
||||
// cannot exempt its job from its run being cancelled. Only push is exempt —
|
||||
// a drill takes longer than the interval between master merges. The negated
|
||||
// form is load-bearing: `== 'pull_request'` would also stop cancelling
|
||||
// workflow_dispatch, and a re-dispatched runner benchmark holds up to 12
|
||||
// larger runners for 15 minutes in this same group on master. The
|
||||
// expression is evaluated against the NEWLY TRIGGERED run, so a dispatch on
|
||||
// master still cancels a mid-flight drill; the runbook records that bound.
|
||||
expect(workflow.concurrency['cancel-in-progress']).toBe("${{ github.event_name != 'push' }}")
|
||||
|
||||
// Neither drill may carry a job-level group: it would not exempt the job
|
||||
// from run-scoped cancellation.
|
||||
for (const name of ['serial-linux-selfhosted', 'serial-windows']) {
|
||||
const job = workflow.jobs[name]
|
||||
if (!isRecord(job)) throw new TypeError(`${name} must be defined`)
|
||||
expect(job.concurrency).toBeUndefined()
|
||||
// Both stay master-push-only; that is what makes the push carve-out safe.
|
||||
expect(job.if).toBe("github.event_name == 'push' && github.ref == 'refs/heads/master'")
|
||||
}
|
||||
|
||||
// What bounds the cost of exempting push: a master push may only carry the
|
||||
// cache seeder and the two drills. Any job reachable on push would start
|
||||
// accumulating uncancelled runs, so the set is pinned here.
|
||||
//
|
||||
// Classification is an exact allowlist of the conditions in use, not a
|
||||
// substring match: `github.event_name != 'pull_request'` mentions
|
||||
// `pull_request` yet IS push-reachable, so matching on the event name alone
|
||||
// would silently misclassify it as gated.
|
||||
const NOT_PUSH_REACHABLE = new Set([
|
||||
"github.event_name == 'pull_request'",
|
||||
"always() && github.event_name == 'pull_request'",
|
||||
"github.event_name == 'workflow_dispatch' && inputs.suite == 'larger-runner-benchmark'",
|
||||
"github.event_name == 'workflow_dispatch' && inputs.suite == 'consolidated-runner-benchmark'",
|
||||
])
|
||||
const pushReachable = Object.entries(workflow.jobs)
|
||||
.filter(([, job]) => {
|
||||
if (!isRecord(job)) return false
|
||||
if (job.if === undefined) return true // unconditional: runs on every event
|
||||
if (job.if === false) return false // `if: false` parses as a boolean
|
||||
if (typeof job.if !== 'string') return true // unrecognized shape: surface it
|
||||
return !NOT_PUSH_REACHABLE.has(job.if.trim())
|
||||
})
|
||||
.map(([name]) => name)
|
||||
.sort()
|
||||
expect(pushReachable).toEqual(['serial-linux-selfhosted', 'serial-windows', 'wine-apt-cache'])
|
||||
|
||||
// Why workflow_dispatch must keep cancelling: each benchmark fans out to a
|
||||
// dozen larger runners at once, in this same group on master. If it stopped
|
||||
// cancelling, a re-dispatch would queue ahead of a drill instead of
|
||||
// replacing the stale measurement.
|
||||
for (const name of ['larger-runner-benchmark', 'consolidated-runner-benchmark']) {
|
||||
const job = workflow.jobs[name]
|
||||
if (!isRecord(job) || !isRecord(job.strategy)) {
|
||||
throw new TypeError(`${name} must define a matrix strategy`)
|
||||
}
|
||||
expect(job.strategy['max-parallel']).toBe(12)
|
||||
expect(job['timeout-minutes']).toBe(15)
|
||||
}
|
||||
})
|
||||
|
||||
it('keeps supported LSP source under native Windows coverage', () => {
|
||||
const config = readFileSync(resolve(root, 'vitest.config.ts'), 'utf8')
|
||||
|
||||
@@ -92,6 +159,26 @@ describe('CI workflow', () => {
|
||||
expect(config).not.toContain('packages/lsp/lsp-local/src/instance.ts')
|
||||
})
|
||||
|
||||
it('requires one release-shaped Python runtime target on every pull request', () => {
|
||||
const workflow = loadWorkflow('.github/workflows/ci.yml')
|
||||
const pythonRuntime = workflowJob(workflow, 'python-runtime')
|
||||
const aggregate = workflowJob(workflow, 'all-checks-passed')
|
||||
if (!Array.isArray(aggregate.needs)) {
|
||||
throw new TypeError('CI aggregate must define required job dependencies')
|
||||
}
|
||||
|
||||
expect(pythonRuntime).toMatchObject({
|
||||
if: "github.event_name == 'pull_request'",
|
||||
name: 'python runtime / release-shaped Linux x64',
|
||||
uses: './.github/workflows/build-exe-for-python-sdk.yml',
|
||||
with: {
|
||||
targets: 'node24-linux-x64',
|
||||
ci: true,
|
||||
},
|
||||
})
|
||||
expect(aggregate.needs).toContain('python-runtime')
|
||||
})
|
||||
|
||||
it('keeps every Vitest project process-isolated on native Windows', () => {
|
||||
const config = readFileSync(resolve(root, 'vitest.config.ts'), 'utf8')
|
||||
|
||||
@@ -127,6 +214,142 @@ describe('E2B e2e workflow', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('Python release workflows', () => {
|
||||
it('keeps complete wheel validation separate from protected public publication', () => {
|
||||
const workflow = loadWorkflow('.github/workflows/python-release.yml')
|
||||
const dispatch = workflowEvent(workflow, 'workflow_dispatch')
|
||||
const pullRequest = workflowEvent(workflow, 'pull_request')
|
||||
const build = workflowJob(workflow, 'build')
|
||||
const pythonCompat = workflowJob(workflow, 'python-compat')
|
||||
const validate = workflowJob(workflow, 'validate')
|
||||
const publishRuntime = workflowJob(workflow, 'publish-runtime')
|
||||
const publishSdk = workflowJob(workflow, 'publish-sdk')
|
||||
if (!isRecord(dispatch.inputs)
|
||||
|| !isRecord(dispatch.inputs.publish)
|
||||
|| !Array.isArray(pythonCompat.steps)
|
||||
|| !Array.isArray(validate.steps)
|
||||
|| !Array.isArray(publishRuntime.steps)
|
||||
|| !Array.isArray(publishSdk.steps)) {
|
||||
throw new TypeError('Python release workflow must define publish input and release steps')
|
||||
}
|
||||
|
||||
expect(dispatch.inputs.publish).toMatchObject({ type: 'boolean', default: false })
|
||||
expect(pullRequest).toEqual({ types: ['labeled'] })
|
||||
expect(build).toMatchObject({
|
||||
if: "github.event_name == 'workflow_dispatch' || github.event.label.name == 'python-release-dry-run'",
|
||||
uses: './.github/workflows/build-exe-for-python-sdk.yml',
|
||||
with: {
|
||||
targets: 'node24-linux-x64,node24-linux-arm64,node24-macos-arm64',
|
||||
release: true,
|
||||
},
|
||||
})
|
||||
expect(pythonCompat.strategy).toMatchObject({ matrix: { python: ['3.10', '3.14'] } })
|
||||
expect(JSON.stringify(pythonCompat.steps)).toContain('deepseek-harness-sdk==${{ steps.compatibility-version.outputs.version }}')
|
||||
const validateSteps = JSON.stringify(validate.steps)
|
||||
const authorize = validate.steps.filter(isRecord).find(step => step.name === 'Authorize publication request')
|
||||
if (!isRecord(authorize) || typeof authorize.run !== 'string') {
|
||||
throw new TypeError('Python release validation must authorize publication requests')
|
||||
}
|
||||
expect(validateSteps).toContain('PUBLIC_PYPI_RELEASE_ENABLED')
|
||||
expect(authorize).toMatchObject({
|
||||
env: {
|
||||
PYPI_PUBLISHER_REPOSITORY: '${{ vars.PYPI_PUBLISHER_REPOSITORY }}',
|
||||
REPOSITORY: '${{ github.repository }}',
|
||||
},
|
||||
})
|
||||
expect(authorize.run).toContain('[ "$REPOSITORY" = "$PYPI_PUBLISHER_REPOSITORY" ]')
|
||||
expect(validateSteps).toContain('100000000')
|
||||
expect(publishRuntime).toMatchObject({
|
||||
if: "github.event_name == 'workflow_dispatch' && inputs.publish",
|
||||
needs: 'validate',
|
||||
environment: 'pypi-runtime',
|
||||
permissions: { contents: 'read', 'id-token': 'write' },
|
||||
})
|
||||
expect(publishSdk).toMatchObject({
|
||||
if: "github.event_name == 'workflow_dispatch' && inputs.publish",
|
||||
needs: ['validate', 'publish-runtime'],
|
||||
environment: 'pypi',
|
||||
permissions: { contents: 'read', 'id-token': 'write' },
|
||||
})
|
||||
const runtimeSteps = publishRuntime.steps.filter(isRecord)
|
||||
const sdkSteps = publishSdk.steps.filter(isRecord)
|
||||
const runtimePublish = runtimeSteps.find(step => step.name === 'Publish runtime wheels')
|
||||
const sdkPublish = sdkSteps.find(step => step.name === 'Publish SDK wheel')
|
||||
const runtimeHashes = runtimeSteps.find(step => step.name === 'Verify release artifact hashes')
|
||||
const sdkHashes = sdkSteps.find(step => step.name === 'Verify release artifact hashes')
|
||||
expect([...runtimeSteps, ...sdkSteps].some(
|
||||
step => typeof step.uses === 'string' && step.uses.startsWith('actions/checkout@'),
|
||||
)).toBe(false)
|
||||
expect([...runtimeSteps, ...sdkSteps].filter(
|
||||
step => step.uses === 'pypa/gh-action-pypi-publish@release/v1',
|
||||
)).toHaveLength(2)
|
||||
expect(runtimePublish).toMatchObject({
|
||||
with: { 'packages-dir': 'dist/runtime/', attestations: false },
|
||||
})
|
||||
expect(sdkPublish).toMatchObject({
|
||||
with: { 'packages-dir': 'dist/sdk/', attestations: false },
|
||||
})
|
||||
expect(runtimeHashes).toMatchObject({ run: 'cd dist && sha256sum -c SHA256SUMS' })
|
||||
expect(sdkHashes).toMatchObject({ run: 'cd dist && sha256sum -c SHA256SUMS' })
|
||||
})
|
||||
|
||||
it('exposes the native wheel builder to the release caller with normalized versions', () => {
|
||||
const workflow = loadWorkflow('.github/workflows/build-exe-for-python-sdk.yml')
|
||||
const call = workflowEvent(workflow, 'workflow_call')
|
||||
const plan = workflowJob(workflow, 'plan')
|
||||
const build = workflowJob(workflow, 'build')
|
||||
if (!isRecord(call.inputs) || !Array.isArray(plan.steps) || !Array.isArray(build.steps)) {
|
||||
throw new TypeError('Python wheel builder must define workflow_call inputs and plan steps')
|
||||
}
|
||||
|
||||
const buildSteps: unknown[] = build.steps
|
||||
const manylinuxAddon = buildSteps.find(step => isRecord(step) && step.name === 'Rebuild Linux node-pty against manylinux 2.28')
|
||||
const macosCheck = buildSteps.find(step => isRecord(step) && step.name === 'Check macOS deployment target')
|
||||
const manylinuxSmoke = buildSteps.find(step => isRecord(step) && step.name === 'Run wheel in a manylinux 2.28 container')
|
||||
expect(call.inputs).toHaveProperty('targets')
|
||||
expect(call.inputs).toMatchObject({
|
||||
ci: { type: 'boolean', default: false },
|
||||
release: { type: 'boolean', default: false },
|
||||
})
|
||||
expect(workflow.concurrency).toMatchObject({
|
||||
group: 'build-single-exe-${{ github.workflow }}-${{ github.ref }}',
|
||||
})
|
||||
expect(plan.if).toContain('inputs.ci')
|
||||
expect(plan.if).toContain('inputs.release')
|
||||
expect(JSON.stringify(plan.steps)).toContain('pep440_version')
|
||||
expect(JSON.stringify(workflow)).toContain('macosx_14_0_arm64')
|
||||
expect(manylinuxAddon).toMatchObject({ if: "runner.os == 'Linux'" })
|
||||
expect(JSON.stringify(manylinuxAddon)).toContain('manylinux_2_28_x86_64')
|
||||
expect(JSON.stringify(manylinuxAddon)).toContain('manylinux_2_28_aarch64')
|
||||
expect(JSON.stringify(manylinuxAddon)).toContain('$HOME/setup-pnpm:$HOME/setup-pnpm:ro')
|
||||
expect(JSON.stringify(manylinuxAddon)).toContain('node-pty-glibc-versions.txt')
|
||||
expect(JSON.stringify(manylinuxAddon)).toContain('le 2.28')
|
||||
expect(macosCheck).toMatchObject({ if: "runner.os == 'macOS'" })
|
||||
expect(JSON.stringify(macosCheck)).toContain('scripts/check-macos-deployment-target.py')
|
||||
expect(JSON.stringify(macosCheck)).toContain('$EXE-spawn-helper')
|
||||
expect(manylinuxSmoke).toMatchObject({ if: "runner.os == 'Linux'" })
|
||||
expect(JSON.stringify(manylinuxSmoke)).toContain('-e DSH_TELEMETRY_DISABLED')
|
||||
})
|
||||
|
||||
it('uses the shared macOS deployment-target check in GitLab', () => {
|
||||
const workflow = loadWorkflow('.gitlab-ci.yml')
|
||||
const runtimeWheel = workflow['.runtime-wheel']
|
||||
if (!isRecord(runtimeWheel) || !Array.isArray(runtimeWheel.script)) {
|
||||
throw new TypeError('GitLab CI must define the runtime wheel script')
|
||||
}
|
||||
const runtimeScript: unknown[] = runtimeWheel.script
|
||||
const macosCheck = runtimeScript.find(
|
||||
step => typeof step === 'string' && step.includes('PLATFORM" = macos-arm64'),
|
||||
)
|
||||
if (typeof macosCheck !== 'string') {
|
||||
throw new TypeError('GitLab CI must check the macOS deployment target')
|
||||
}
|
||||
|
||||
expect(macosCheck).toContain('scripts/check-macos-deployment-target.py')
|
||||
expect(macosCheck).toContain('"$EXE" "$EXE-spawn-helper"')
|
||||
})
|
||||
})
|
||||
|
||||
describe('Issue lifecycle workflow', () => {
|
||||
it('uses explicit review handoff events without rerunning when a draft becomes ready', () => {
|
||||
const lifecycle = loadWorkflow('.github/workflows/issue-lifecycle.yml')
|
||||
|
||||
@@ -7,5 +7,5 @@
|
||||
"docs/testing.md": 1150,
|
||||
"examples/AGENTS.md": 310,
|
||||
"packages/AGENTS.md": 675,
|
||||
"packages/README.md": 980
|
||||
"packages/README.md": 994
|
||||
}
|
||||
|
||||
@@ -133,6 +133,7 @@ export const SERVICE_WALK_EXEMPTIONS: Record<string, string> = {
|
||||
models: 'client-side interface-typed browser service — packages/client/ui-model/README.md owns the API',
|
||||
modules: 'client-side interface-typed browser service — packages/client/modules/README.md owns the API',
|
||||
remote: 'client-side interface-typed gateway accessor (ClientRemote) — packages/api/gateway/README.md owns the API',
|
||||
sessionExport: 'client-side browser download controller — packages/session-query/session-export/README.md owns the API',
|
||||
slash: 'client-side interface-typed browser service — packages/client/ui-slash/README.md owns the API',
|
||||
slots: 'client-side interface-typed browser service — packages/client/runtime/README.md owns the API',
|
||||
theme: 'client-side interface-typed browser service — packages/client/ui-theme/README.md owns the API',
|
||||
@@ -178,6 +179,7 @@ export const EVENT_SCOPE_PAGE: Record<string, string> = {
|
||||
* exemption cannot mask another declaration in that scope.
|
||||
*/
|
||||
export const EVENT_WALK_EXEMPTIONS: Record<string, string> = {
|
||||
'command/executed': 'client-face local command acknowledgment — packages/client/ui-command/README.md owns the API',
|
||||
'connection/reset': 'client-face transport signal — packages/client/runtime/README.md owns the API',
|
||||
'locale/change': 'client-face locale switch signal — packages/client/locale/README.md owns the API',
|
||||
'slash/input-begin-command': 'client-face slash-input protocol — packages/client/ui-slash/README.md owns the API',
|
||||
|
||||
@@ -1195,7 +1195,7 @@ function renderLifecycle(): string {
|
||||
const maintenance = 'curated Mermaid sequence; exact event signatures live in the generated Cordis catalog'
|
||||
return [
|
||||
...generatedHeader('Agent Turn And Step Lifecycle'),
|
||||
'This sequence is the visual companion to [architecture.md](architecture.md#default-loop-lifecycle). It keeps durable replay facts on `session/event` and live control/status on `agent/*`.',
|
||||
'This sequence is the visual companion to [architecture.md](architecture.md#turn-flow). It keeps durable replay facts on `session/event` and live control/status on `agent/*`.',
|
||||
'',
|
||||
'```mermaid',
|
||||
'sequenceDiagram',
|
||||
@@ -1213,15 +1213,15 @@ function renderLifecycle(): string {
|
||||
` Agent-->>SDK: ${mermaidCode('agent/inbox/inserted')} { message }`,
|
||||
' Agent->>Driver: queued work wakes driver',
|
||||
` Driver-->>SDK: ${mermaidCode('agent/status')} running`,
|
||||
` Driver->>Session: ${mermaidCode('turn/start')}`,
|
||||
' Note over Agent,Driver: claim pending next-step input plus one queued prompt',
|
||||
` Driver-->>SDK: ${mermaidCode('agent/inbox/spliced')} pure deletion`,
|
||||
` Driver-->>SDK: ${mermaidCode('agent/inbox/claimed')} { message, turn } per message`,
|
||||
` Driver->>Hooks: ${mermaidCode('agent/pre-step')} waterfall`,
|
||||
' Hooks-->>Driver: authoritative reject or enter(messages)',
|
||||
' alt proposed step rejected or pre-step failed',
|
||||
' Driver-->>Driver: claimed batch stays removed, no turn opens',
|
||||
' Driver-->>Driver: claimed batch stays removed, the open turn spends no step',
|
||||
' else enter proposed step',
|
||||
` Driver->>Session: ${mermaidCode('turn/start')}`,
|
||||
` Driver->>Session: ${mermaidCode('step/start')}`,
|
||||
` Driver->>Session: ${mermaidCode('user/message')} per entered message`,
|
||||
` Driver->>Prompt: ${mermaidCode('system-prompt/assemble')} waterfall`,
|
||||
@@ -1258,8 +1258,8 @@ function renderLifecycle(): string {
|
||||
' Hooks-->>Driver: authoritative reject or enter(messages)',
|
||||
' end',
|
||||
' end',
|
||||
` Driver->>Session: ${mermaidCode('turn/end')}`,
|
||||
' end',
|
||||
` Driver->>Session: ${mermaidCode('turn/end')}`,
|
||||
` Driver-->>SDK: ${mermaidCode('agent/status')} idle`,
|
||||
'```',
|
||||
'',
|
||||
|
||||
@@ -40,6 +40,7 @@ const LINK_MAP: Record<string, string> = {
|
||||
CallId: 'core.md',
|
||||
ContentBlock: 'core.md',
|
||||
MessageSource: 'core.md',
|
||||
ScheduleChange: 'schedule.md',
|
||||
StreamChunk: 'llm-streaming.md',
|
||||
TokenUsage: 'llm-streaming.md',
|
||||
TodoItem: 'session.md',
|
||||
|
||||
@@ -51,6 +51,7 @@ import * as ToolStrReplaceEditor from '@deepseek-ai/dsh-tool-str-replace-editor'
|
||||
import PtyService from '@deepseek-ai/dsh-pty'
|
||||
import * as ToolPty from '@deepseek-ai/dsh-tool-pty'
|
||||
import * as ToolGoal from '@deepseek-ai/dsh-tool-goal'
|
||||
import * as ToolSchedule from '@deepseek-ai/dsh-tool-schedule'
|
||||
import Lsp from '@deepseek-ai/dsh-lsp'
|
||||
import * as ToolLsp from '@deepseek-ai/dsh-tool-lsp'
|
||||
import * as ToolSkill from '@deepseek-ai/dsh-tool-skill'
|
||||
@@ -115,15 +116,18 @@ const catalogChildScopes = new WeakMap<Context, Agent>()
|
||||
* schema harvest, without starting a model, Agent loop, or persistence backend.
|
||||
* @param ctx - catalog context owning the scope.
|
||||
* @param mountScoped - package installer for the scoped context.
|
||||
* @param key - agent-like scope key exposed to the package's scope selector.
|
||||
* @param inject - services the package installer must await before mounting.
|
||||
*/
|
||||
async function mountCatalogChildScope(
|
||||
ctx: Context,
|
||||
mountScoped: (childCtx: Context) => void,
|
||||
key: Agent = { id: SessionId('tool-catalog-child') } as Agent,
|
||||
inject: string[] = ['tools', 'systemPrompt', 'subagents'],
|
||||
): Promise<void> {
|
||||
const key = { id: SessionId('tool-catalog-child') } as Agent
|
||||
await ctx.plugin(Object.assign((inner: Context) => {
|
||||
mountScoped(createScope(inner, key).ctx)
|
||||
}, { inject: ['tools', 'systemPrompt', 'subagents'] }))
|
||||
}, { inject }))
|
||||
catalogChildScopes.set(ctx, key)
|
||||
}
|
||||
|
||||
@@ -362,6 +366,27 @@ const TOOL_PACKAGES: ToolPackage[] = [
|
||||
note:
|
||||
'create, edit, pause, and resume require direct-human root authority; complete and blocked also accept the exact current goal round. The default blocked lower bound is three admitted rounds.',
|
||||
},
|
||||
{
|
||||
pkg: '@deepseek-ai/dsh-tool-schedule',
|
||||
dir: 'tool-schedule',
|
||||
source: 'packages/schedule/tool-schedule/src/tools.ts',
|
||||
requires: ['ctx.tools', 'ctx.sessions', 'Session persistence', 'a future live root Agent'],
|
||||
writes: ['tool/call', 'schedule/change create or delete', 'tool/result'],
|
||||
async mount(ctx) {
|
||||
await ctx.plugin(SessionStore)
|
||||
const session = ctx.sessions.create(SessionId('tool-catalog-schedule'))
|
||||
const agent = { id: session.id, session } as Agent
|
||||
await mountCatalogChildScope(ctx, (childCtx) => {
|
||||
ToolSchedule.registerScheduleTools(ctx, childCtx, agent, () => {})
|
||||
}, agent, ['tools', 'systemPrompt'])
|
||||
},
|
||||
scope: ctx => catalogChildScopes.get(ctx) as Agent,
|
||||
note:
|
||||
'Registered only inside live root Agent scopes created after the opt-in Schedule plugin loads. '
|
||||
+ 'Version 1 accepts after_seconds, explicit absolute at, and bounded fixed-rate every_seconds, '
|
||||
+ 'and discloses session-local delivery; '
|
||||
+ 'management reads and mutations require the shared Session persistence barrier.',
|
||||
},
|
||||
{
|
||||
pkg: '@deepseek-ai/dsh-tool-lsp',
|
||||
dir: 'tool-lsp',
|
||||
@@ -425,7 +450,7 @@ const TOOL_PACKAGES: ToolPackage[] = [
|
||||
pkg: '@deepseek-ai/dsh-tool-subagent',
|
||||
dir: 'tool-subagent',
|
||||
source: 'packages/subagent/tool-subagent/src/index.ts',
|
||||
requires: ['ctx.tools', 'ctx.subagents'],
|
||||
requires: ['ctx.tools', 'ctx.subagents', 'ctx.systemPrompt'],
|
||||
writes: ['tool/call', 'tool/result', 'child session events through the chosen provider'],
|
||||
shippedNames: ['subagent', 'subagent_fork'],
|
||||
async mount(ctx) {
|
||||
@@ -434,7 +459,7 @@ const TOOL_PACKAGES: ToolPackage[] = [
|
||||
await ctx.plugin(ToolSubagent, { provider: 'mock' })
|
||||
},
|
||||
note:
|
||||
'The registered tool name is the load-time `toolName` config (default `subagent`); the schema above is that default. The shipped compositions load this package once per subagent backend, so the model additionally sees `subagent_fork` bound to the fork backend. Each instance\'s description and `run_in_background` parameter follow its own `backgroundMode` and `enableRunInBackground`, so the two shipped schemas are not identical: `subagent` is `continuable`, while `subagent_fork` stays `one-shot` — see `packages/bundle/base/cordis.patch.yml` and `examples/acp-agent/cordis.yml`.',
|
||||
'The registered tool name is the load-time `toolName` config (default `subagent`); the schema above is that default. The shipped compositions load this package once per subagent backend, so the model additionally sees `subagent_fork` bound to the fork backend. Each instance\'s description, `run_in_background` parameter, and system-prompt policy follow its own `backgroundMode` and `enableRunInBackground`, so the two shipped schemas are not identical: `subagent` is `continuable` and defaults omitted calls to background with automatic settlement delivery, while `subagent_fork` stays `one-shot` and defaults them to foreground — see `packages/bundle/base/cordis.patch.yml` and `examples/acp-agent/cordis.yml`.',
|
||||
},
|
||||
{
|
||||
pkg: '@deepseek-ai/dsh-tool-subagent-control',
|
||||
|
||||
@@ -53,7 +53,9 @@ describe('Oxlint executable contract', () => {
|
||||
['host package source', 'packages/fs/fs-policy/src', 'packages/fs/fs-policy/tsconfig.json'],
|
||||
['host package test', 'packages/fs/fs-policy/tests', 'tsconfig.host.json'],
|
||||
['client package source', 'packages/client/ui-primitives/src', 'packages/client/ui-primitives/tsconfig.json'],
|
||||
['client package test', 'packages/client/ui-trajectory/tests', 'tsconfig.client.json'],
|
||||
// A test under packages/client states its face in the filename, so the
|
||||
// probe carries the Client suffix to reach the Client aggregate.
|
||||
['client package test', 'packages/client/ui-trajectory/tests', 'tsconfig.client.json', '.client.ts'],
|
||||
['example', 'examples/headless-agent/tests', 'tsconfig.host.json'],
|
||||
['website', 'website', 'tsconfig.host.json'],
|
||||
] as const
|
||||
@@ -66,8 +68,8 @@ probePromise()
|
||||
|
||||
try {
|
||||
const paths: Array<readonly [label: string, path: string, tsconfig: string]> = []
|
||||
for (const [label, parent, tsconfig] of probes) {
|
||||
const path = join(repositoryRoot, parent, `oxlint-contract-${suffix}.ts`)
|
||||
for (const [label, parent, tsconfig, extension = '.ts'] of probes) {
|
||||
const path = join(repositoryRoot, parent, `oxlint-contract-${suffix}${extension}`)
|
||||
await writeFile(path, source)
|
||||
paths.push([label, relative(repositoryRoot, path), tsconfig])
|
||||
}
|
||||
@@ -98,7 +100,8 @@ probePromise()
|
||||
expect(output).not.toContain('Unmatched file:')
|
||||
} finally {
|
||||
await Promise.all([
|
||||
...probes.map(([, parent]) => rm(join(repositoryRoot, parent, `oxlint-contract-${suffix}.ts`), { force: true })),
|
||||
...probes.map(([, parent, , extension = '.ts']) =>
|
||||
rm(join(repositoryRoot, parent, `oxlint-contract-${suffix}${extension}`), { force: true })),
|
||||
rm(configPath, { force: true }),
|
||||
])
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import { existsSync, globSync, mkdirSync, mkdtempSync, readFileSync, realpathSyn
|
||||
import { tmpdir } from 'node:os'
|
||||
import { basename, join, resolve } from 'node:path'
|
||||
import { afterEach, describe, expect, it } from 'vitest'
|
||||
import { docsPages, type DocsPage } from '../website/docs.ts'
|
||||
import { docsPages, landingLink, routeLink, sectionSpec, type DocsPage } from '../website/docs.ts'
|
||||
import {
|
||||
addProjectionFrontmatter, projectedPageContent, publishableImage, rewriteMarkdown,
|
||||
} from './project-doc-site.ts'
|
||||
@@ -307,7 +307,7 @@ describe('docsPages locale routes', () => {
|
||||
const translated = rootPages.filter(page => page.contentLocale === 'zh-CN')
|
||||
const fallbacks = rootPages.filter(page => page.contentLocale === 'en-US')
|
||||
|
||||
expect(translated).toHaveLength(42)
|
||||
expect(translated).toHaveLength(43)
|
||||
expect(translated.every(page => page.source.endsWith('.zh.md'))).toBe(true)
|
||||
expect(fallbacks).toEqual([])
|
||||
})
|
||||
@@ -364,6 +364,63 @@ describe('docsPages locale routes', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('sidebar ordering', () => {
|
||||
it('places every section a sidebar collection owns', () => {
|
||||
for (const page of docsPages) {
|
||||
if (page.sidebar === null) continue
|
||||
expect(() => sectionSpec(page.locale, page.section), page.route).not.toThrow()
|
||||
}
|
||||
})
|
||||
|
||||
it('refuses a section with no declared placement', () => {
|
||||
expect(() => sectionSpec('root', '数据结构'))
|
||||
.toThrow('Sidebar section "数据结构" has no placement in the root locale.')
|
||||
})
|
||||
|
||||
it('declares placements per locale rather than in one shared list', () => {
|
||||
// `SDK` labels a group in both locales, so one shared list would have to
|
||||
// rank it against `入门` and against `Guide` at the same position.
|
||||
expect(sectionSpec('root', 'SDK').index).toBeGreaterThan(sectionSpec('root', '入门').index)
|
||||
expect(sectionSpec('en', 'SDK').index).toBeGreaterThan(sectionSpec('en', 'Guide').index)
|
||||
expect(() => sectionSpec('en', '入门')).toThrow()
|
||||
expect(() => sectionSpec('root', 'Guide')).toThrow()
|
||||
})
|
||||
|
||||
it('lands every navigation item on a page the manifest publishes', () => {
|
||||
// The navigation bar named `/guide/` while the manifest published the guide's
|
||||
// first page at `guide/quickstart.md`, so the item served a 404.
|
||||
const collections = [
|
||||
['root', 'zh-guide'], ['root', 'zh-develop'], ['root', 'zh-reference'],
|
||||
['en', 'en-guide'], ['en', 'en-develop'], ['en', 'en-reference'],
|
||||
] as const
|
||||
const published = new Set(docsPages.map(page => routeLink(page.route)))
|
||||
for (const [locale, collection] of collections) {
|
||||
expect(published, `${locale}/${collection}`).toContain(landingLink(locale, collection))
|
||||
}
|
||||
})
|
||||
|
||||
it('collapses the subsystem groups and leaves the smaller ones open', () => {
|
||||
expect(sectionSpec('root', '执行与工具').collapsed).toBe(true)
|
||||
expect(sectionSpec('en', 'Execution and tools').collapsed).toBe(true)
|
||||
expect(sectionSpec('root', '概念').collapsed).toBeUndefined()
|
||||
})
|
||||
|
||||
it('gives each page its own position within a section', () => {
|
||||
// Sidebar entries sort by order alone, so a shared value leaves the two
|
||||
// pages ranked by whichever manifest block happens to be concatenated
|
||||
// first rather than by an intent the manifest states.
|
||||
const taken = new Map<string, string>()
|
||||
const collisions: string[] = []
|
||||
for (const page of docsPages) {
|
||||
const slot = `${page.locale}/${String(page.sidebar)}/${page.section}#${page.order}`
|
||||
const holder = taken.get(slot)
|
||||
if (holder === undefined) taken.set(slot, page.label)
|
||||
else collisions.push(`${slot}: ${holder} / ${page.label}`)
|
||||
}
|
||||
expect(collisions).toEqual([])
|
||||
})
|
||||
})
|
||||
|
||||
describe('addProjectionFrontmatter', () => {
|
||||
it('adds frontmatter to an ordinary Markdown page', () => {
|
||||
expect(addProjectionFrontmatter('# Guide\n', { source: 'docs/guide.md' })).toBe(
|
||||
@@ -411,6 +468,25 @@ describe('projectedPageContent', () => {
|
||||
expect(projectedPageContent(markdown, page('zh-guide'))).toBe(markdown)
|
||||
})
|
||||
|
||||
it('drops the language switcher the navigation bar already offers', () => {
|
||||
expect(projectedPageContent('# Guide\n\nEnglish | [中文](./en/guide)\n\nBody.\n', page('zh-guide')))
|
||||
.toBe('# Guide\n\nBody.\n')
|
||||
expect(projectedPageContent('# 指南\n\n[English](./en/guide) | 中文\n\n正文。\n', page('zh-guide')))
|
||||
.toBe('# 指南\n\n正文。\n')
|
||||
})
|
||||
|
||||
it('drops the repository badge every page links from its footer', () => {
|
||||
const badge = '[](https://github.com/deepseek-ai/deepseek-harness)'
|
||||
expect(projectedPageContent(`# Guide\n\nBody.\n\n${badge}\n`, page('zh-guide')))
|
||||
.toBe('# Guide\n\nBody.\n')
|
||||
})
|
||||
|
||||
it('keeps a switcher-shaped line that is not the page header', () => {
|
||||
// A tutorial showing the convention must still render the example.
|
||||
const sample = '# Guide\n\nA\n\nB\n\nC\n\nD\n\nE\n\nEnglish | [中文](./x)\n'
|
||||
expect(projectedPageContent(sample, page('zh-guide'))).toBe(sample)
|
||||
})
|
||||
|
||||
it('rejects a locale home source without frontmatter', () => {
|
||||
expect(() => projectedPageContent('# Harness\n', page(null)))
|
||||
.toThrow('locale home source "docs/index.zh.md" must start with YAML frontmatter')
|
||||
|
||||
@@ -292,6 +292,37 @@ export function addProjectionFrontmatter(markdown: string, page: Pick<DocsPage,
|
||||
return `---\n${fields}\n---\n\n${markdown}`
|
||||
}
|
||||
|
||||
/** The switcher line a canonical page carries so its GitHub reader can reach the other language. */
|
||||
const LANGUAGE_SWITCHER = /^(?:English \| \[中文\]\([^)]*\)|\[English\]\([^)]*\) \| 中文)$/
|
||||
|
||||
/** The repository badge a canonical page carries for its GitHub reader. */
|
||||
const REPOSITORY_BADGE = /^\[!\[[^\]]*\]\(https:\/\/img\.shields\.io\/[^)]*\)\]\([^)]*\)$/
|
||||
|
||||
/**
|
||||
* Drop the lines that address a canonical page's GitHub reader.
|
||||
*
|
||||
* The site carries a locale switcher in its navigation bar and links the
|
||||
* repository from every page, so projecting these lines would repeat both — the
|
||||
* switcher as the first element under each heading.
|
||||
*
|
||||
* @param markdown Rewritten canonical Markdown content.
|
||||
* @returns The content without the switcher line or the repository badge.
|
||||
*/
|
||||
function withoutRepositoryChrome(markdown: string): string {
|
||||
const lines = markdown.split('\n')
|
||||
const switcher = lines.findIndex(line => LANGUAGE_SWITCHER.test(line))
|
||||
// Only the switcher introducing the page qualifies; further down the same
|
||||
// text is prose or a sample rather than the page's own header.
|
||||
if (switcher !== -1 && switcher < 8) {
|
||||
lines.splice(switcher, lines[switcher + 1] === '' ? 2 : 1)
|
||||
}
|
||||
const badge = lines.findLastIndex(line => REPOSITORY_BADGE.test(line))
|
||||
if (badge !== -1) {
|
||||
lines.splice(lines[badge - 1] === '' ? badge - 1 : badge, lines[badge - 1] === '' ? 2 : 1)
|
||||
}
|
||||
return lines.join('\n')
|
||||
}
|
||||
|
||||
/**
|
||||
* Select the Markdown rendered for one published page.
|
||||
*
|
||||
@@ -300,7 +331,7 @@ export function addProjectionFrontmatter(markdown: string, page: Pick<DocsPage,
|
||||
* @returns Full Markdown for ordinary pages or frontmatter-only Markdown for a locale home page.
|
||||
*/
|
||||
export function projectedPageContent(markdown: string, page: DocsPage): string {
|
||||
if (page.sidebar !== null) return markdown
|
||||
if (page.sidebar !== null) return withoutRepositoryChrome(markdown)
|
||||
if (!markdown.startsWith('---\n')) {
|
||||
throw new Error(`project-doc-site: locale home source ${JSON.stringify(page.source)} must start with YAML frontmatter.`)
|
||||
}
|
||||
|
||||
@@ -29,6 +29,9 @@ describe('publication payload policy', () => {
|
||||
String.raw`src\index.ts`,
|
||||
'lib/types/index.d.ts.map',
|
||||
'./lib/types/index.d.ts.map',
|
||||
'lib/typert.remote-client.d.ts.map',
|
||||
'lib/client.js.map',
|
||||
'./lib/client.js.map',
|
||||
])('rejects static manifest path %s', (file) => {
|
||||
expect(isForbiddenPublicationFile(file)).toBe(true)
|
||||
})
|
||||
@@ -40,11 +43,19 @@ describe('publication payload policy', () => {
|
||||
])).toThrow('fixture.tgz publishes source file package/src/index.ts')
|
||||
})
|
||||
|
||||
it('rejects declaration maps in packed tarballs', () => {
|
||||
it('rejects source maps in packed tarballs', () => {
|
||||
expect(validateFixtureTarball([
|
||||
'package/package.json',
|
||||
'package/lib/types/index.d.ts.map',
|
||||
])).toThrow('fixture.tgz publishes declaration map package/lib/types/index.d.ts.map')
|
||||
])).toThrow('fixture.tgz publishes source map package/lib/types/index.d.ts.map')
|
||||
expect(validateFixtureTarball([
|
||||
'package/package.json',
|
||||
'package/lib/typert.remote-client.d.ts.map',
|
||||
])).toThrow('fixture.tgz publishes source map package/lib/typert.remote-client.d.ts.map')
|
||||
expect(validateFixtureTarball([
|
||||
'package/package.json',
|
||||
'package/lib/client.js.map',
|
||||
])).toThrow('fixture.tgz publishes source map package/lib/client.js.map')
|
||||
})
|
||||
|
||||
it('accepts a clean packed tarball', () => {
|
||||
@@ -56,19 +67,6 @@ describe('publication payload policy', () => {
|
||||
])).not.toThrow()
|
||||
})
|
||||
|
||||
it('allows only the TypeRT declaration map and its navigable source tree when requested', () => {
|
||||
const policy = { typeRTRemoteNavigation: true }
|
||||
expect(isForbiddenPublicationFile('src/index.ts', policy)).toBe(false)
|
||||
expect(isForbiddenPublicationFile('lib/typert.remote-client.d.ts.map', policy)).toBe(false)
|
||||
expect(isForbiddenPublicationFile('lib/types/index.d.ts.map', policy)).toBe(true)
|
||||
expect(() => {
|
||||
validateTarballPayload([
|
||||
'package/lib/typert.remote-client.d.ts.map',
|
||||
'package/src/index.ts',
|
||||
], 'fixture.tgz', policy)
|
||||
}).not.toThrow()
|
||||
})
|
||||
|
||||
it('recognizes only the canonical Host-for-Client export pair', () => {
|
||||
expect(hasTypeRTRemoteNavigation({
|
||||
exports: {
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
/** Publication payload policy shared by static manifests and packed tarballs. */
|
||||
|
||||
/** Publication exceptions required for TypeRT declaration-map navigation. */
|
||||
export interface PublicationPayloadPolicy {
|
||||
readonly typeRTRemoteNavigation?: boolean
|
||||
}
|
||||
|
||||
/** Whether a package manifest exports generated Host-for-Client metadata with source navigation. */
|
||||
/**
|
||||
* Whether a package manifest exports generated Host-for-Client metadata.
|
||||
* @param manifest - parsed package manifest to inspect.
|
||||
* @returns whether the canonical `./remote` export pair is present.
|
||||
*/
|
||||
export function hasTypeRTRemoteNavigation(manifest: unknown): boolean {
|
||||
if (manifest === null || typeof manifest !== 'object' || Array.isArray(manifest)) return false
|
||||
const exportsField = (manifest as Record<string, unknown>).exports
|
||||
@@ -23,35 +22,34 @@ function payloadPath(file: string): string {
|
||||
return normalized.startsWith('package/') ? normalized.slice('package/'.length) : normalized
|
||||
}
|
||||
|
||||
/** Whether a package payload path exposes source or declaration-map intermediates. */
|
||||
export function isForbiddenPublicationFile(
|
||||
file: string,
|
||||
policy: PublicationPayloadPolicy = {},
|
||||
): boolean {
|
||||
/**
|
||||
* Whether a package payload path exposes source or map intermediates. Maps
|
||||
* serve editor navigation during development, where a workspace consumer
|
||||
* resolves their source through the package link; a published map resolves
|
||||
* nothing, so no payload publishes one.
|
||||
* @param file - manifest path or tarball member to classify.
|
||||
* @returns whether publishing this path is forbidden.
|
||||
*/
|
||||
export function isForbiddenPublicationFile(file: string): boolean {
|
||||
const normalized = payloadPath(file)
|
||||
if (policy.typeRTRemoteNavigation === true
|
||||
&& (normalized === 'src'
|
||||
|| normalized.startsWith('src/')
|
||||
|| normalized === 'lib/typert.remote-client.d.ts.map')) {
|
||||
return false
|
||||
}
|
||||
return normalized === 'src'
|
||||
|| normalized.startsWith('src/')
|
||||
|| normalized.endsWith('.d.ts.map')
|
||||
|| normalized.endsWith('.js.map')
|
||||
}
|
||||
|
||||
/** Reject source and declaration-map members in a packed npm tarball. */
|
||||
export function validateTarballPayload(
|
||||
files: readonly string[],
|
||||
context: string,
|
||||
policy: PublicationPayloadPolicy = {},
|
||||
): void {
|
||||
/**
|
||||
* Reject source and map members in a packed npm tarball.
|
||||
* @param files - tarball members to validate.
|
||||
* @param context - tarball identity named in the failure.
|
||||
*/
|
||||
export function validateTarballPayload(files: readonly string[], context: string): void {
|
||||
for (const file of files) {
|
||||
if (!isForbiddenPublicationFile(file, policy)) continue
|
||||
if (!isForbiddenPublicationFile(file)) continue
|
||||
const normalized = payloadPath(file)
|
||||
if (normalized === 'src' || normalized.startsWith('src/')) {
|
||||
throw new Error(`${context} publishes source file ${file}`)
|
||||
}
|
||||
throw new Error(`${context} publishes declaration map ${file}`)
|
||||
throw new Error(`${context} publishes source map ${file}`)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ import { basename, dirname, isAbsolute, join, normalize, relative, resolve, sep
|
||||
import { createInterface } from 'node:readline/promises'
|
||||
import { pathToFileURL } from 'node:url'
|
||||
import { parseArgs } from 'node:util'
|
||||
import { hasTypeRTRemoteNavigation, validateTarballPayload } from './publication-payload.ts'
|
||||
import { validateTarballPayload } from './publication-payload.ts'
|
||||
|
||||
const DEFAULT_REGISTRY = 'https://registry.npm.harnessment.com'
|
||||
const DEFAULT_OUTPUT_DIRECTORY = '.artifacts/npm-baseline'
|
||||
@@ -323,9 +323,7 @@ class ReleaseBundle {
|
||||
throw new Error(`unexpected or duplicate packed package: ${artifact.name}`)
|
||||
}
|
||||
if (expected.origin === 'harness') {
|
||||
validateTarballPayload(artifact.files, tarball, {
|
||||
typeRTRemoteNavigation: hasTypeRTRemoteNavigation(artifact.manifest),
|
||||
})
|
||||
validateTarballPayload(artifact.files, tarball)
|
||||
}
|
||||
if (artifact.version !== version) {
|
||||
throw new Error(`${tarball} has version ${artifact.version}; expected ${version}`)
|
||||
@@ -401,9 +399,7 @@ class ReleaseBundle {
|
||||
}
|
||||
const artifact = inspectTarball(path, runner)
|
||||
if (pkg.origin === 'harness') {
|
||||
validateTarballPayload(artifact.files, pkg.tarball, {
|
||||
typeRTRemoteNavigation: hasTypeRTRemoteNavigation(artifact.manifest),
|
||||
})
|
||||
validateTarballPayload(artifact.files, pkg.tarball)
|
||||
}
|
||||
if (artifact.name !== pkg.name || artifact.version !== this.manifest.version) {
|
||||
throw new Error(`tarball identity mismatch: ${pkg.tarball}`)
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
import { globSync, readFileSync } from 'node:fs'
|
||||
import { resolve } from 'node:path'
|
||||
import { hasTypeRTRemoteNavigation, validateTarballPayload } from '../publication-payload.ts'
|
||||
import { validateTarballPayload } from '../publication-payload.ts'
|
||||
|
||||
/** Dependency sections that constrain publish order: a consumer must publish after its dependency. */
|
||||
const ORDER_SECTIONS = ['dependencies', 'optionalDependencies'] as const
|
||||
@@ -225,9 +225,7 @@ class DshFamily extends ReleaseFamily {
|
||||
* @param files - every path inside its tarball.
|
||||
*/
|
||||
validatePayload(member: ReleaseMember, files: readonly string[]): void {
|
||||
validateTarballPayload(files, member.name, {
|
||||
typeRTRemoteNavigation: hasTypeRTRemoteNavigation(member.manifest),
|
||||
})
|
||||
validateTarballPayload(files, member.name)
|
||||
}
|
||||
|
||||
readonly installedEntry = { packageName: '@deepseek-ai/dsh', binPath: 'lib/bin.js' }
|
||||
|
||||
@@ -89,9 +89,9 @@ const GENERIC_SKIPS: readonly GenericSkip[] = [
|
||||
// the creator flow stages and which id the roster reports.
|
||||
{ file: 'packages/client/ui-agent-preset/src/client/AgentPresetSection.tsx', upstream: ['cordis'] },
|
||||
{ file: 'packages/client/ui-agent-preset/src/client/index.ts', upstream: ['cordis'] },
|
||||
{ file: 'packages/client/ui-agent-preset/tests/apply.spec.ts', upstream: ['cordis'] },
|
||||
{ file: 'packages/client/ui-agent-preset/tests/locales.spec.ts', upstream: ['cordis'] },
|
||||
{ file: 'packages/client/ui-agent-preset/tests/section.spec.tsx', upstream: ['cordis'] },
|
||||
{ file: 'packages/client/ui-agent-preset/tests/apply.client.spec.ts', upstream: ['cordis'] },
|
||||
{ file: 'packages/client/ui-agent-preset/tests/locales.client.spec.ts', upstream: ['cordis'] },
|
||||
{ file: 'packages/client/ui-agent-preset/tests/section.client.spec.tsx', upstream: ['cordis'] },
|
||||
{ file: 'apps/cli/tests/web-agent-presets.e2e.ts', upstream: ['cordis'] },
|
||||
{ file: 'apps/web/tests/agent-preset-authoring.e2e.ts', upstream: ['cordis'] },
|
||||
{ file: 'packages/preset/agent-presets/tests/session.spec.ts', upstream: ['cordis'] },
|
||||
@@ -123,12 +123,12 @@ const POSTCONDITIONS: readonly PostCondition[] = [
|
||||
{ file: 'scripts/check-workspace-constraints.ts', text: '?.[\'@deepseek-ai/cordis\']', count: 2 },
|
||||
{ file: 'packages/boot/app-boot/tsdown.config.ts', text: '[\'@deepseek-ai/cordis-plugin-include\']', count: 1 },
|
||||
{ file: 'tsconfig.base.json', text: '"@deepseek-ai/cordis-plugin-loader": ["./vendor/loader/src"]', count: 1 },
|
||||
// One insertion, once: a duplicated log entry is what a non-idempotent apply produced.
|
||||
// The vendored README owns this required entry; reject its deletion or duplication.
|
||||
{ file: 'vendor/README.md', text: '17. **`@deepseek-ai` rescope**', count: 1 },
|
||||
{ file: 'knip.json', text: '@cordisjs', count: 0 },
|
||||
{ file: 'pnpm-workspace.yaml', text: 'cordis@4.0.0-rc.7', count: 0 },
|
||||
// The preset ids in this table are product data, not package names.
|
||||
{ file: 'packages/client/ui-agent-preset/tests/locales.spec.ts', text: '[\'cordis\', \'presetCordisName\'', count: 1 },
|
||||
{ file: 'packages/client/ui-agent-preset/tests/locales.client.spec.ts', text: '[\'cordis\', \'presetCordisName\'', count: 1 },
|
||||
// The preset id the shipped composition documents to its own model.
|
||||
{ file: 'apps/cli/config/agent-presets/cordis/agent.cordis.yml', text: 'The `cordis` agent preset', count: 1 },
|
||||
{ file: 'apps/cli/config/agent-presets/cordis/agent.cordis.yml', text: 'corrupting the `cordis` preset', count: 1 },
|
||||
@@ -241,13 +241,6 @@ const EXACT_EDITS: readonly ExactEdit[] = [
|
||||
replace: '| Directory | npm name | Upstream name | Version | Upstream repo | Commit |\n|---|---|---|---|---|---|',
|
||||
expect: 1,
|
||||
},
|
||||
{
|
||||
id: 'vendor-readme-local-modification-log',
|
||||
file: 'vendor/README.md',
|
||||
find: '\n16. **`cordis/package.json` publishes `src`**',
|
||||
replace: '\n16. **`cordis/package.json` publishes `src`**: added `src` to the `files` list, joining the other eight vendored packages. Cordis declares `"./src/*": "./src/*"` in its exports, so a tarball without `src` publishes an export map pointing at absent files; the release change judgement also reads `files` to decide whether a diff reaches the payload, and a package whose only published paths are build output has no tracked path to match.\n17. **`@deepseek-ai` rescope**: every vendored manifest `name`, every internal dependency entry among the vendored set, and every module specifier that reaches them use the scoped names in the manifest table\'s `npm name` column. Directory names, version numbers, and dependency ranges are unchanged, and no upstream runtime identifier is renamed — `Symbol.for(\'schemastery\')` and Schemastery\'s `vendor:` metadata field keep their upstream values. Re-apply with `pnpm run rescope-vendor --apply` after a sync; the table\'s two name columns are the mapping, restated for consumers in [docs/rescope.md](../docs/rescope.md).',
|
||||
expect: 1,
|
||||
},
|
||||
{
|
||||
// A plain fence listing the bundle's mounted tree: a bare token, no quotes.
|
||||
id: 'agent-spine-demo-mounted-tree',
|
||||
@@ -332,7 +325,7 @@ const VENDORED_LIBRARY = /^@deepseek-ai\\/(cosmokit|schemastery)(\\/|$)/
|
||||
{
|
||||
// The real package references in files whose other `cordis` strings are preset ids.
|
||||
id: 'agent-preset-spec-framework-import',
|
||||
file: 'packages/client/ui-agent-preset/tests/apply.spec.ts',
|
||||
file: 'packages/client/ui-agent-preset/tests/apply.client.spec.ts',
|
||||
find: "import { Context } from 'cordis'",
|
||||
replace: "import { Context } from '@deepseek-ai/cordis'",
|
||||
expect: 1,
|
||||
|
||||
@@ -155,15 +155,16 @@ def completion_chunks(body: dict[str, object]) -> list[dict[str, object]]:
|
||||
return text_chunks(WORKFLOW_WORKER_TEXT)
|
||||
raise AssertionError(f"unexpected tool follow-up: {tool_name}")
|
||||
|
||||
user_prompts = [
|
||||
message_text(message.get("content"))
|
||||
for message in reversed(messages)
|
||||
if isinstance(message, dict) and message.get("role") == "user"
|
||||
]
|
||||
minimal_prompt = next(
|
||||
(
|
||||
message_text(message.get("content"))
|
||||
for message in reversed(messages)
|
||||
if isinstance(message, dict)
|
||||
and message.get("role") == "user"
|
||||
and message_text(message.get("content")).startswith(
|
||||
f"{MINIMAL_PROMPT}\n{MINIMAL_EDITOR_PATH_PREFIX}"
|
||||
)
|
||||
prompt
|
||||
for prompt in user_prompts
|
||||
if prompt.startswith(f"{MINIMAL_PROMPT}\n{MINIMAL_EDITOR_PATH_PREFIX}")
|
||||
),
|
||||
None,
|
||||
)
|
||||
@@ -183,7 +184,17 @@ def completion_chunks(body: dict[str, object]) -> list[dict[str, object]]:
|
||||
"bash",
|
||||
{"command": MINIMAL_BASH_COMMAND},
|
||||
)
|
||||
prompt = message_text(latest.get("content"))
|
||||
scenario_prompts = {
|
||||
SNAPSHOT_DIRECT_CHILD_PROMPT,
|
||||
SNAPSHOT_WORKFLOW_CHILD_PROMPT,
|
||||
SNAPSHOT_PROMPT,
|
||||
CODE_PROMPT,
|
||||
WORKFLOW_PROMPT,
|
||||
}
|
||||
prompt = next(
|
||||
(candidate for candidate in user_prompts if candidate in scenario_prompts),
|
||||
message_text(latest.get("content")),
|
||||
)
|
||||
if prompt == SNAPSHOT_DIRECT_CHILD_PROMPT:
|
||||
return text_chunks("DIRECT_CHILD_OK")
|
||||
if prompt == SNAPSHOT_WORKFLOW_CHILD_PROMPT:
|
||||
@@ -781,6 +792,7 @@ def build_snapshot_files(
|
||||
) -> dict[str, str]:
|
||||
"""Render the SDK result and three persisted logs into stable expected outputs."""
|
||||
replacements = [(str(cwd), "{{cwd}}"), (SNAPSHOT_SESSION_ID, "{{parent}}")]
|
||||
replacements.append((snapshot_workflow_run_id(result), "{{workflow-run}}"))
|
||||
for index, child_id in enumerate(child_ids, start=1):
|
||||
replacements.append((child_id, f"{{{{child-{index}}}}}"))
|
||||
agent_id = snapshot_agent_id(result, child_id)
|
||||
@@ -813,6 +825,21 @@ def build_snapshot_files(
|
||||
return files
|
||||
|
||||
|
||||
def snapshot_workflow_run_id(result: "RunResult") -> str:
|
||||
"""Return the one workflow run id emitted by the advanced scenario."""
|
||||
run_ids: set[str] = set()
|
||||
for event in result.events:
|
||||
event_type = event.get("type")
|
||||
data = event.get("data")
|
||||
if not isinstance(event_type, str) or not event_type.startswith("tool-workflow/"):
|
||||
continue
|
||||
if isinstance(data, dict) and isinstance(data.get("runId"), str):
|
||||
run_ids.add(data["runId"])
|
||||
if len(run_ids) != 1:
|
||||
raise AssertionError(f"advanced snapshot expected one workflow run id: {sorted(run_ids)}")
|
||||
return next(iter(run_ids))
|
||||
|
||||
|
||||
def snapshot_agent_id(result: "RunResult", child_id: str) -> str:
|
||||
"""Find the successful subagent id paired with one child session."""
|
||||
for notification in result.notifications:
|
||||
|
||||
@@ -874,9 +874,49 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "tool/result",
|
||||
"type": "tool-workflow/run-start",
|
||||
"seq": 48,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"runId": "{{workflow-run}}",
|
||||
"name": "advanced-exe-snapshot"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "tool-workflow/agent-start",
|
||||
"seq": 49,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"runId": "{{workflow-run}}",
|
||||
"seq": 1,
|
||||
"label": "workflow-child",
|
||||
"phase": "Delegate",
|
||||
"childId": "{{child-2}}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "tool-workflow/agent-end",
|
||||
"seq": 50,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"runId": "{{workflow-run}}",
|
||||
"seq": 1,
|
||||
"outcome": "completed"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "tool-workflow/run-end",
|
||||
"seq": 51,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"runId": "{{workflow-run}}",
|
||||
"stopReason": "completed"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "tool/result",
|
||||
"seq": 52,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"turn": 1,
|
||||
"step": 4,
|
||||
@@ -909,7 +949,7 @@
|
||||
},
|
||||
{
|
||||
"type": "step/end",
|
||||
"seq": 49,
|
||||
"seq": 53,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"turn": 1,
|
||||
@@ -918,7 +958,7 @@
|
||||
},
|
||||
{
|
||||
"type": "step/start",
|
||||
"seq": 50,
|
||||
"seq": 54,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"turn": 1,
|
||||
@@ -927,7 +967,7 @@
|
||||
},
|
||||
{
|
||||
"type": "assistant/chunk",
|
||||
"seq": 51,
|
||||
"seq": 55,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"turn": 1,
|
||||
@@ -941,7 +981,7 @@
|
||||
},
|
||||
{
|
||||
"type": "assistant/chunk",
|
||||
"seq": 52,
|
||||
"seq": 56,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"turn": 1,
|
||||
@@ -957,7 +997,7 @@
|
||||
},
|
||||
{
|
||||
"type": "assistant/chunk",
|
||||
"seq": 53,
|
||||
"seq": 57,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"turn": 1,
|
||||
@@ -976,7 +1016,7 @@
|
||||
},
|
||||
{
|
||||
"type": "assistant/chunk",
|
||||
"seq": 54,
|
||||
"seq": 58,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"turn": 1,
|
||||
@@ -992,7 +1032,7 @@
|
||||
},
|
||||
{
|
||||
"type": "assistant/chunk",
|
||||
"seq": 55,
|
||||
"seq": 59,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"turn": 1,
|
||||
@@ -1007,7 +1047,7 @@
|
||||
},
|
||||
{
|
||||
"type": "assistant/message",
|
||||
"seq": 56,
|
||||
"seq": 60,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"turn": 1,
|
||||
@@ -1035,17 +1075,17 @@
|
||||
}
|
||||
},
|
||||
"sourceEventSeqs": [
|
||||
51,
|
||||
52,
|
||||
53,
|
||||
54,
|
||||
55
|
||||
55,
|
||||
56,
|
||||
57,
|
||||
58,
|
||||
59
|
||||
],
|
||||
"surfaceOp": "append"
|
||||
},
|
||||
{
|
||||
"type": "tool/call",
|
||||
"seq": 57,
|
||||
"seq": 61,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"turn": 1,
|
||||
@@ -1057,7 +1097,7 @@
|
||||
},
|
||||
{
|
||||
"type": "tool/result",
|
||||
"seq": 58,
|
||||
"seq": 62,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"turn": 1,
|
||||
@@ -1085,13 +1125,13 @@
|
||||
}
|
||||
},
|
||||
"sourceEventSeqs": [
|
||||
57
|
||||
61
|
||||
],
|
||||
"surfaceOp": "append"
|
||||
},
|
||||
{
|
||||
"type": "step/end",
|
||||
"seq": 59,
|
||||
"seq": 63,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"turn": 1,
|
||||
@@ -1100,7 +1140,7 @@
|
||||
},
|
||||
{
|
||||
"type": "step/start",
|
||||
"seq": 60,
|
||||
"seq": 64,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"turn": 1,
|
||||
@@ -1109,7 +1149,7 @@
|
||||
},
|
||||
{
|
||||
"type": "request/header",
|
||||
"seq": 61,
|
||||
"seq": 65,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"header": {
|
||||
@@ -1141,7 +1181,7 @@
|
||||
},
|
||||
{
|
||||
"type": "assistant/chunk",
|
||||
"seq": 62,
|
||||
"seq": 66,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"turn": 1,
|
||||
@@ -1155,7 +1195,7 @@
|
||||
},
|
||||
{
|
||||
"type": "assistant/chunk",
|
||||
"seq": 63,
|
||||
"seq": 67,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"turn": 1,
|
||||
@@ -1169,7 +1209,7 @@
|
||||
},
|
||||
{
|
||||
"type": "assistant/chunk",
|
||||
"seq": 64,
|
||||
"seq": 68,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"turn": 1,
|
||||
@@ -1186,7 +1226,7 @@
|
||||
},
|
||||
{
|
||||
"type": "assistant/chunk",
|
||||
"seq": 65,
|
||||
"seq": 69,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"turn": 1,
|
||||
@@ -1202,7 +1242,7 @@
|
||||
},
|
||||
{
|
||||
"type": "assistant/chunk",
|
||||
"seq": 66,
|
||||
"seq": 70,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"turn": 1,
|
||||
@@ -1217,7 +1257,7 @@
|
||||
},
|
||||
{
|
||||
"type": "assistant/message",
|
||||
"seq": 67,
|
||||
"seq": 71,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"turn": 1,
|
||||
@@ -1243,17 +1283,17 @@
|
||||
}
|
||||
},
|
||||
"sourceEventSeqs": [
|
||||
62,
|
||||
63,
|
||||
64,
|
||||
65,
|
||||
66
|
||||
66,
|
||||
67,
|
||||
68,
|
||||
69,
|
||||
70
|
||||
],
|
||||
"surfaceOp": "append"
|
||||
},
|
||||
{
|
||||
"type": "step/end",
|
||||
"seq": 68,
|
||||
"seq": 72,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"turn": 1,
|
||||
@@ -1262,7 +1302,7 @@
|
||||
},
|
||||
{
|
||||
"type": "turn/end",
|
||||
"seq": 69,
|
||||
"seq": 73,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"turn": 1,
|
||||
@@ -2334,9 +2374,42 @@
|
||||
"payload": {
|
||||
"sessionId": "{{child-1}}",
|
||||
"event": {
|
||||
"type": "session/title",
|
||||
"type": "user/message",
|
||||
"seq": 6,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"content": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "Current runtime context. This snapshot supersedes earlier runtime-context snapshots.\n\nYou are a delegated subagent: your permission scope was fixed when you were started and cannot be widened from inside this session — operations that require approval are rejected automatically. When the task needs access beyond that scope, do not retry the denied operation; state the limitation in your reply so the delegating agent can handle it."
|
||||
}
|
||||
],
|
||||
"source": {
|
||||
"kind": "plugin",
|
||||
"plugin": "@deepseek-ai/dsh-system-prompt",
|
||||
"form": "snapshot",
|
||||
"sections": [
|
||||
{
|
||||
"name": "subagent:delegation",
|
||||
"text": "You are a delegated subagent: your permission scope was fixed when you were started and cannot be widened from inside this session — operations that require approval are rejected automatically. When the task needs access beyond that scope, do not retry the denied operation; state the limitation in your reply so the delegating agent can handle it."
|
||||
}
|
||||
]
|
||||
},
|
||||
"role": "user",
|
||||
"id": "{{messageId}}"
|
||||
},
|
||||
"surfaceOp": "append"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"method": "session.event",
|
||||
"payload": {
|
||||
"sessionId": "{{child-1}}",
|
||||
"event": {
|
||||
"type": "session/title",
|
||||
"seq": 7,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"title": "Reply with exactly DIRECT_CHILD_OK and",
|
||||
"messageSeqs": [
|
||||
@@ -2355,7 +2428,7 @@
|
||||
"sessionId": "{{child-1}}",
|
||||
"event": {
|
||||
"type": "request/header",
|
||||
"seq": 7,
|
||||
"seq": 8,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"header": {
|
||||
@@ -2394,7 +2467,7 @@
|
||||
"sessionId": "{{child-1}}",
|
||||
"event": {
|
||||
"type": "request/context",
|
||||
"seq": 8,
|
||||
"seq": 9,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"provider": "deepseek-official",
|
||||
@@ -2410,7 +2483,7 @@
|
||||
"sessionId": "{{child-1}}",
|
||||
"event": {
|
||||
"type": "assistant/chunk",
|
||||
"seq": 9,
|
||||
"seq": 10,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"turn": 1,
|
||||
@@ -2430,7 +2503,7 @@
|
||||
"sessionId": "{{child-1}}",
|
||||
"event": {
|
||||
"type": "assistant/chunk",
|
||||
"seq": 10,
|
||||
"seq": 11,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"turn": 1,
|
||||
@@ -2450,7 +2523,7 @@
|
||||
"sessionId": "{{child-1}}",
|
||||
"event": {
|
||||
"type": "assistant/chunk",
|
||||
"seq": 11,
|
||||
"seq": 12,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"turn": 1,
|
||||
@@ -2473,7 +2546,7 @@
|
||||
"sessionId": "{{child-1}}",
|
||||
"event": {
|
||||
"type": "assistant/chunk",
|
||||
"seq": 12,
|
||||
"seq": 13,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"turn": 1,
|
||||
@@ -2495,7 +2568,7 @@
|
||||
"sessionId": "{{child-1}}",
|
||||
"event": {
|
||||
"type": "assistant/chunk",
|
||||
"seq": 13,
|
||||
"seq": 14,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"turn": 1,
|
||||
@@ -2516,7 +2589,7 @@
|
||||
"sessionId": "{{child-1}}",
|
||||
"event": {
|
||||
"type": "assistant/message",
|
||||
"seq": 14,
|
||||
"seq": 15,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"turn": 1,
|
||||
@@ -2542,11 +2615,11 @@
|
||||
}
|
||||
},
|
||||
"sourceEventSeqs": [
|
||||
9,
|
||||
10,
|
||||
11,
|
||||
12,
|
||||
13
|
||||
13,
|
||||
14
|
||||
],
|
||||
"surfaceOp": "append"
|
||||
}
|
||||
@@ -2558,7 +2631,7 @@
|
||||
"sessionId": "{{child-1}}",
|
||||
"event": {
|
||||
"type": "step/end",
|
||||
"seq": 15,
|
||||
"seq": 16,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"turn": 1,
|
||||
@@ -2573,7 +2646,7 @@
|
||||
"sessionId": "{{child-1}}",
|
||||
"event": {
|
||||
"type": "turn/end",
|
||||
"seq": 16,
|
||||
"seq": 17,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"turn": 1,
|
||||
@@ -2850,6 +2923,21 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"method": "session.event",
|
||||
"payload": {
|
||||
"sessionId": "{{parent}}",
|
||||
"event": {
|
||||
"type": "tool-workflow/run-start",
|
||||
"seq": 48,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"runId": "{{workflow-run}}",
|
||||
"name": "advanced-exe-snapshot"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"method": "subagent.started",
|
||||
"payload": {
|
||||
@@ -2986,9 +3074,42 @@
|
||||
"payload": {
|
||||
"sessionId": "{{child-2}}",
|
||||
"event": {
|
||||
"type": "session/title",
|
||||
"type": "user/message",
|
||||
"seq": 6,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"content": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "Current runtime context. This snapshot supersedes earlier runtime-context snapshots.\n\nYou are a delegated subagent: your permission scope was fixed when you were started and cannot be widened from inside this session — operations that require approval are rejected automatically. When the task needs access beyond that scope, do not retry the denied operation; state the limitation in your reply so the delegating agent can handle it."
|
||||
}
|
||||
],
|
||||
"source": {
|
||||
"kind": "plugin",
|
||||
"plugin": "@deepseek-ai/dsh-system-prompt",
|
||||
"form": "snapshot",
|
||||
"sections": [
|
||||
{
|
||||
"name": "subagent:delegation",
|
||||
"text": "You are a delegated subagent: your permission scope was fixed when you were started and cannot be widened from inside this session — operations that require approval are rejected automatically. When the task needs access beyond that scope, do not retry the denied operation; state the limitation in your reply so the delegating agent can handle it."
|
||||
}
|
||||
]
|
||||
},
|
||||
"role": "user",
|
||||
"id": "{{messageId}}"
|
||||
},
|
||||
"surfaceOp": "append"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"method": "session.event",
|
||||
"payload": {
|
||||
"sessionId": "{{child-2}}",
|
||||
"event": {
|
||||
"type": "session/title",
|
||||
"seq": 7,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"title": "Reply with exactly WORKFLOW_CHILD_OK and",
|
||||
"messageSeqs": [
|
||||
@@ -3007,7 +3128,7 @@
|
||||
"sessionId": "{{child-2}}",
|
||||
"event": {
|
||||
"type": "request/header",
|
||||
"seq": 7,
|
||||
"seq": 8,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"header": {
|
||||
@@ -3046,7 +3167,7 @@
|
||||
"sessionId": "{{child-2}}",
|
||||
"event": {
|
||||
"type": "request/context",
|
||||
"seq": 8,
|
||||
"seq": 9,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"provider": "deepseek-official",
|
||||
@@ -3056,13 +3177,31 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"method": "session.event",
|
||||
"payload": {
|
||||
"sessionId": "{{parent}}",
|
||||
"event": {
|
||||
"type": "tool-workflow/agent-start",
|
||||
"seq": 49,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"runId": "{{workflow-run}}",
|
||||
"seq": 1,
|
||||
"label": "workflow-child",
|
||||
"phase": "Delegate",
|
||||
"childId": "{{child-2}}"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"method": "session.event",
|
||||
"payload": {
|
||||
"sessionId": "{{child-2}}",
|
||||
"event": {
|
||||
"type": "assistant/chunk",
|
||||
"seq": 9,
|
||||
"seq": 10,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"turn": 1,
|
||||
@@ -3082,7 +3221,7 @@
|
||||
"sessionId": "{{child-2}}",
|
||||
"event": {
|
||||
"type": "assistant/chunk",
|
||||
"seq": 10,
|
||||
"seq": 11,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"turn": 1,
|
||||
@@ -3102,7 +3241,7 @@
|
||||
"sessionId": "{{child-2}}",
|
||||
"event": {
|
||||
"type": "assistant/chunk",
|
||||
"seq": 11,
|
||||
"seq": 12,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"turn": 1,
|
||||
@@ -3125,7 +3264,7 @@
|
||||
"sessionId": "{{child-2}}",
|
||||
"event": {
|
||||
"type": "assistant/chunk",
|
||||
"seq": 12,
|
||||
"seq": 13,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"turn": 1,
|
||||
@@ -3147,7 +3286,7 @@
|
||||
"sessionId": "{{child-2}}",
|
||||
"event": {
|
||||
"type": "assistant/chunk",
|
||||
"seq": 13,
|
||||
"seq": 14,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"turn": 1,
|
||||
@@ -3168,7 +3307,7 @@
|
||||
"sessionId": "{{child-2}}",
|
||||
"event": {
|
||||
"type": "assistant/message",
|
||||
"seq": 14,
|
||||
"seq": 15,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"turn": 1,
|
||||
@@ -3194,11 +3333,11 @@
|
||||
}
|
||||
},
|
||||
"sourceEventSeqs": [
|
||||
9,
|
||||
10,
|
||||
11,
|
||||
12,
|
||||
13
|
||||
13,
|
||||
14
|
||||
],
|
||||
"surfaceOp": "append"
|
||||
}
|
||||
@@ -3210,7 +3349,7 @@
|
||||
"sessionId": "{{child-2}}",
|
||||
"event": {
|
||||
"type": "step/end",
|
||||
"seq": 15,
|
||||
"seq": 16,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"turn": 1,
|
||||
@@ -3225,7 +3364,7 @@
|
||||
"sessionId": "{{child-2}}",
|
||||
"event": {
|
||||
"type": "turn/end",
|
||||
"seq": 16,
|
||||
"seq": 17,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"turn": 1,
|
||||
@@ -3260,13 +3399,44 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"method": "session.event",
|
||||
"payload": {
|
||||
"sessionId": "{{parent}}",
|
||||
"event": {
|
||||
"type": "tool-workflow/agent-end",
|
||||
"seq": 50,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"runId": "{{workflow-run}}",
|
||||
"seq": 1,
|
||||
"outcome": "completed"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"method": "session.event",
|
||||
"payload": {
|
||||
"sessionId": "{{parent}}",
|
||||
"event": {
|
||||
"type": "tool-workflow/run-end",
|
||||
"seq": 51,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"runId": "{{workflow-run}}",
|
||||
"stopReason": "completed"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"method": "session.event",
|
||||
"payload": {
|
||||
"sessionId": "{{parent}}",
|
||||
"event": {
|
||||
"type": "tool/result",
|
||||
"seq": 48,
|
||||
"seq": 52,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"turn": 1,
|
||||
@@ -3306,7 +3476,7 @@
|
||||
"sessionId": "{{parent}}",
|
||||
"event": {
|
||||
"type": "step/end",
|
||||
"seq": 49,
|
||||
"seq": 53,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"turn": 1,
|
||||
@@ -3321,7 +3491,7 @@
|
||||
"sessionId": "{{parent}}",
|
||||
"event": {
|
||||
"type": "step/start",
|
||||
"seq": 50,
|
||||
"seq": 54,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"turn": 1,
|
||||
@@ -3336,7 +3506,7 @@
|
||||
"sessionId": "{{parent}}",
|
||||
"event": {
|
||||
"type": "assistant/chunk",
|
||||
"seq": 51,
|
||||
"seq": 55,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"turn": 1,
|
||||
@@ -3356,7 +3526,7 @@
|
||||
"sessionId": "{{parent}}",
|
||||
"event": {
|
||||
"type": "assistant/chunk",
|
||||
"seq": 52,
|
||||
"seq": 56,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"turn": 1,
|
||||
@@ -3378,7 +3548,7 @@
|
||||
"sessionId": "{{parent}}",
|
||||
"event": {
|
||||
"type": "assistant/chunk",
|
||||
"seq": 53,
|
||||
"seq": 57,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"turn": 1,
|
||||
@@ -3403,7 +3573,7 @@
|
||||
"sessionId": "{{parent}}",
|
||||
"event": {
|
||||
"type": "assistant/chunk",
|
||||
"seq": 54,
|
||||
"seq": 58,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"turn": 1,
|
||||
@@ -3425,7 +3595,7 @@
|
||||
"sessionId": "{{parent}}",
|
||||
"event": {
|
||||
"type": "assistant/chunk",
|
||||
"seq": 55,
|
||||
"seq": 59,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"turn": 1,
|
||||
@@ -3446,7 +3616,7 @@
|
||||
"sessionId": "{{parent}}",
|
||||
"event": {
|
||||
"type": "assistant/message",
|
||||
"seq": 56,
|
||||
"seq": 60,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"turn": 1,
|
||||
@@ -3474,11 +3644,11 @@
|
||||
}
|
||||
},
|
||||
"sourceEventSeqs": [
|
||||
51,
|
||||
52,
|
||||
53,
|
||||
54,
|
||||
55
|
||||
55,
|
||||
56,
|
||||
57,
|
||||
58,
|
||||
59
|
||||
],
|
||||
"surfaceOp": "append"
|
||||
}
|
||||
@@ -3490,7 +3660,7 @@
|
||||
"sessionId": "{{parent}}",
|
||||
"event": {
|
||||
"type": "tool/call",
|
||||
"seq": 57,
|
||||
"seq": 61,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"turn": 1,
|
||||
@@ -3508,7 +3678,7 @@
|
||||
"sessionId": "{{parent}}",
|
||||
"event": {
|
||||
"type": "tool/result",
|
||||
"seq": 58,
|
||||
"seq": 62,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"turn": 1,
|
||||
@@ -3536,7 +3706,7 @@
|
||||
}
|
||||
},
|
||||
"sourceEventSeqs": [
|
||||
57
|
||||
61
|
||||
],
|
||||
"surfaceOp": "append"
|
||||
}
|
||||
@@ -3548,7 +3718,7 @@
|
||||
"sessionId": "{{parent}}",
|
||||
"event": {
|
||||
"type": "step/end",
|
||||
"seq": 59,
|
||||
"seq": 63,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"turn": 1,
|
||||
@@ -3563,7 +3733,7 @@
|
||||
"sessionId": "{{parent}}",
|
||||
"event": {
|
||||
"type": "step/start",
|
||||
"seq": 60,
|
||||
"seq": 64,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"turn": 1,
|
||||
@@ -3578,7 +3748,7 @@
|
||||
"sessionId": "{{parent}}",
|
||||
"event": {
|
||||
"type": "request/header",
|
||||
"seq": 61,
|
||||
"seq": 65,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"header": {
|
||||
@@ -3616,7 +3786,7 @@
|
||||
"sessionId": "{{parent}}",
|
||||
"event": {
|
||||
"type": "assistant/chunk",
|
||||
"seq": 62,
|
||||
"seq": 66,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"turn": 1,
|
||||
@@ -3636,7 +3806,7 @@
|
||||
"sessionId": "{{parent}}",
|
||||
"event": {
|
||||
"type": "assistant/chunk",
|
||||
"seq": 63,
|
||||
"seq": 67,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"turn": 1,
|
||||
@@ -3656,7 +3826,7 @@
|
||||
"sessionId": "{{parent}}",
|
||||
"event": {
|
||||
"type": "assistant/chunk",
|
||||
"seq": 64,
|
||||
"seq": 68,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"turn": 1,
|
||||
@@ -3679,7 +3849,7 @@
|
||||
"sessionId": "{{parent}}",
|
||||
"event": {
|
||||
"type": "assistant/chunk",
|
||||
"seq": 65,
|
||||
"seq": 69,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"turn": 1,
|
||||
@@ -3701,7 +3871,7 @@
|
||||
"sessionId": "{{parent}}",
|
||||
"event": {
|
||||
"type": "assistant/chunk",
|
||||
"seq": 66,
|
||||
"seq": 70,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"turn": 1,
|
||||
@@ -3722,7 +3892,7 @@
|
||||
"sessionId": "{{parent}}",
|
||||
"event": {
|
||||
"type": "assistant/message",
|
||||
"seq": 67,
|
||||
"seq": 71,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"turn": 1,
|
||||
@@ -3748,11 +3918,11 @@
|
||||
}
|
||||
},
|
||||
"sourceEventSeqs": [
|
||||
62,
|
||||
63,
|
||||
64,
|
||||
65,
|
||||
66
|
||||
66,
|
||||
67,
|
||||
68,
|
||||
69,
|
||||
70
|
||||
],
|
||||
"surfaceOp": "append"
|
||||
}
|
||||
@@ -3764,7 +3934,7 @@
|
||||
"sessionId": "{{parent}}",
|
||||
"event": {
|
||||
"type": "step/end",
|
||||
"seq": 68,
|
||||
"seq": 72,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"turn": 1,
|
||||
@@ -3779,7 +3949,7 @@
|
||||
"sessionId": "{{parent}}",
|
||||
"event": {
|
||||
"type": "turn/end",
|
||||
"seq": 69,
|
||||
"seq": 73,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"turn": 1,
|
||||
|
||||
@@ -5,14 +5,15 @@
|
||||
{"type":"subagent/descriptor","seq":3,"time":0,"data":{"version":2,"mode":"one-shot","provider":"spawn","label":"Check direct child"}}
|
||||
{"type":"step/start","seq":4,"time":0,"data":{"turn":1,"step":1}}
|
||||
{"type":"user/message","seq":5,"time":0,"data":{"content":[{"type":"text","text":"Reply with exactly DIRECT_CHILD_OK and nothing else."}],"source":{"kind":"user"},"role":"user","id":"{{messageId}}"},"surfaceOp":"append"}
|
||||
{"type":"session/title","seq":6,"time":0,"data":{"title":"Reply with exactly DIRECT_CHILD_OK and","messageSeqs":[5],"source":{"kind":"fallback"}}}
|
||||
{"type":"request/header","seq":7,"time":0,"data":{"header":{"config":{"provider":"deepseek-official","model":"smoke-model","maxTokens":256000,"reasoningEffort":"high"},"adapterDefaults":{"reasoningEffort":true,"maxTokens":true},"system":"{{system}}","tools":["cordis_inspect","cordis_mount","cordis_unmount","run_code","snapshot_double","subagent","task_kill","task_list","task_output","workflow"]},"reason":"initial"}}
|
||||
{"type":"request/context","seq":8,"time":0,"data":{"provider":"deepseek-official","model":"smoke-model","contextWindow":1000000}}
|
||||
{"type":"assistant/chunk","seq":9,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":0,"blockType":"text"}}}
|
||||
{"type":"assistant/chunk","seq":10,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"text-delta","index":0,"text":"DIRECT_CHILD_OK"}}}
|
||||
{"type":"assistant/chunk","seq":11,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":0,"block":{"type":"text","text":"DIRECT_CHILD_OK"}}}}
|
||||
{"type":"assistant/chunk","seq":12,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"usage","usage":{"inputTokens":3,"outputTokens":3}}}}
|
||||
{"type":"assistant/chunk","seq":13,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"finish","reason":{"kind":"stop"}}}}
|
||||
{"type":"assistant/message","seq":14,"time":0,"data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"text","text":"DIRECT_CHILD_OK"}],"source":{"kind":"model","provider":"deepseek-official","model":"smoke-model"},"id":"{{messageId}}"},"usage":{"inputTokens":3,"outputTokens":3}},"sourceEventSeqs":[9,10,11,12,13],"surfaceOp":"append"}
|
||||
{"type":"step/end","seq":15,"time":0,"data":{"turn":1,"step":1}}
|
||||
{"type":"turn/end","seq":16,"time":0,"data":{"turn":1,"reason":{"kind":"completed"}}}
|
||||
{"type":"user/message","seq":6,"time":0,"data":{"content":[{"type":"text","text":"Current runtime context. This snapshot supersedes earlier runtime-context snapshots.\n\nYou are a delegated subagent: your permission scope was fixed when you were started and cannot be widened from inside this session — operations that require approval are rejected automatically. When the task needs access beyond that scope, do not retry the denied operation; state the limitation in your reply so the delegating agent can handle it."}],"source":{"kind":"plugin","plugin":"@deepseek-ai/dsh-system-prompt","form":"snapshot","sections":[{"name":"subagent:delegation","text":"You are a delegated subagent: your permission scope was fixed when you were started and cannot be widened from inside this session — operations that require approval are rejected automatically. When the task needs access beyond that scope, do not retry the denied operation; state the limitation in your reply so the delegating agent can handle it."}]},"role":"user","id":"{{messageId}}"},"surfaceOp":"append"}
|
||||
{"type":"session/title","seq":7,"time":0,"data":{"title":"Reply with exactly DIRECT_CHILD_OK and","messageSeqs":[5],"source":{"kind":"fallback"}}}
|
||||
{"type":"request/header","seq":8,"time":0,"data":{"header":{"config":{"provider":"deepseek-official","model":"smoke-model","maxTokens":256000,"reasoningEffort":"high"},"adapterDefaults":{"reasoningEffort":true,"maxTokens":true},"system":"{{system}}","tools":["cordis_inspect","cordis_mount","cordis_unmount","run_code","snapshot_double","subagent","task_kill","task_list","task_output","workflow"]},"reason":"initial"}}
|
||||
{"type":"request/context","seq":9,"time":0,"data":{"provider":"deepseek-official","model":"smoke-model","contextWindow":1000000}}
|
||||
{"type":"assistant/chunk","seq":10,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":0,"blockType":"text"}}}
|
||||
{"type":"assistant/chunk","seq":11,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"text-delta","index":0,"text":"DIRECT_CHILD_OK"}}}
|
||||
{"type":"assistant/chunk","seq":12,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":0,"block":{"type":"text","text":"DIRECT_CHILD_OK"}}}}
|
||||
{"type":"assistant/chunk","seq":13,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"usage","usage":{"inputTokens":3,"outputTokens":3}}}}
|
||||
{"type":"assistant/chunk","seq":14,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"finish","reason":{"kind":"stop"}}}}
|
||||
{"type":"assistant/message","seq":15,"time":0,"data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"text","text":"DIRECT_CHILD_OK"}],"source":{"kind":"model","provider":"deepseek-official","model":"smoke-model"},"id":"{{messageId}}"},"usage":{"inputTokens":3,"outputTokens":3}},"sourceEventSeqs":[10,11,12,13,14],"surfaceOp":"append"}
|
||||
{"type":"step/end","seq":16,"time":0,"data":{"turn":1,"step":1}}
|
||||
{"type":"turn/end","seq":17,"time":0,"data":{"turn":1,"reason":{"kind":"completed"}}}
|
||||
|
||||
@@ -5,14 +5,15 @@
|
||||
{"type":"subagent/descriptor","seq":3,"time":0,"data":{"version":2,"mode":"one-shot","provider":"spawn"}}
|
||||
{"type":"step/start","seq":4,"time":0,"data":{"turn":1,"step":1}}
|
||||
{"type":"user/message","seq":5,"time":0,"data":{"content":[{"type":"text","text":"Reply with exactly WORKFLOW_CHILD_OK and nothing else."}],"source":{"kind":"user"},"role":"user","id":"{{messageId}}"},"surfaceOp":"append"}
|
||||
{"type":"session/title","seq":6,"time":0,"data":{"title":"Reply with exactly WORKFLOW_CHILD_OK and","messageSeqs":[5],"source":{"kind":"fallback"}}}
|
||||
{"type":"request/header","seq":7,"time":0,"data":{"header":{"config":{"provider":"deepseek-official","model":"smoke-model","maxTokens":256000,"reasoningEffort":"high"},"adapterDefaults":{"reasoningEffort":true,"maxTokens":true},"system":"{{system}}","tools":["cordis_inspect","cordis_mount","cordis_unmount","run_code","snapshot_double","subagent","task_kill","task_list","task_output","workflow"]},"reason":"initial"}}
|
||||
{"type":"request/context","seq":8,"time":0,"data":{"provider":"deepseek-official","model":"smoke-model","contextWindow":1000000}}
|
||||
{"type":"assistant/chunk","seq":9,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":0,"blockType":"text"}}}
|
||||
{"type":"assistant/chunk","seq":10,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"text-delta","index":0,"text":"WORKFLOW_CHILD_OK"}}}
|
||||
{"type":"assistant/chunk","seq":11,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":0,"block":{"type":"text","text":"WORKFLOW_CHILD_OK"}}}}
|
||||
{"type":"assistant/chunk","seq":12,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"usage","usage":{"inputTokens":3,"outputTokens":3}}}}
|
||||
{"type":"assistant/chunk","seq":13,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"finish","reason":{"kind":"stop"}}}}
|
||||
{"type":"assistant/message","seq":14,"time":0,"data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"text","text":"WORKFLOW_CHILD_OK"}],"source":{"kind":"model","provider":"deepseek-official","model":"smoke-model"},"id":"{{messageId}}"},"usage":{"inputTokens":3,"outputTokens":3}},"sourceEventSeqs":[9,10,11,12,13],"surfaceOp":"append"}
|
||||
{"type":"step/end","seq":15,"time":0,"data":{"turn":1,"step":1}}
|
||||
{"type":"turn/end","seq":16,"time":0,"data":{"turn":1,"reason":{"kind":"completed"}}}
|
||||
{"type":"user/message","seq":6,"time":0,"data":{"content":[{"type":"text","text":"Current runtime context. This snapshot supersedes earlier runtime-context snapshots.\n\nYou are a delegated subagent: your permission scope was fixed when you were started and cannot be widened from inside this session — operations that require approval are rejected automatically. When the task needs access beyond that scope, do not retry the denied operation; state the limitation in your reply so the delegating agent can handle it."}],"source":{"kind":"plugin","plugin":"@deepseek-ai/dsh-system-prompt","form":"snapshot","sections":[{"name":"subagent:delegation","text":"You are a delegated subagent: your permission scope was fixed when you were started and cannot be widened from inside this session — operations that require approval are rejected automatically. When the task needs access beyond that scope, do not retry the denied operation; state the limitation in your reply so the delegating agent can handle it."}]},"role":"user","id":"{{messageId}}"},"surfaceOp":"append"}
|
||||
{"type":"session/title","seq":7,"time":0,"data":{"title":"Reply with exactly WORKFLOW_CHILD_OK and","messageSeqs":[5],"source":{"kind":"fallback"}}}
|
||||
{"type":"request/header","seq":8,"time":0,"data":{"header":{"config":{"provider":"deepseek-official","model":"smoke-model","maxTokens":256000,"reasoningEffort":"high"},"adapterDefaults":{"reasoningEffort":true,"maxTokens":true},"system":"{{system}}","tools":["cordis_inspect","cordis_mount","cordis_unmount","run_code","snapshot_double","subagent","task_kill","task_list","task_output","workflow"]},"reason":"initial"}}
|
||||
{"type":"request/context","seq":9,"time":0,"data":{"provider":"deepseek-official","model":"smoke-model","contextWindow":1000000}}
|
||||
{"type":"assistant/chunk","seq":10,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":0,"blockType":"text"}}}
|
||||
{"type":"assistant/chunk","seq":11,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"text-delta","index":0,"text":"WORKFLOW_CHILD_OK"}}}
|
||||
{"type":"assistant/chunk","seq":12,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":0,"block":{"type":"text","text":"WORKFLOW_CHILD_OK"}}}}
|
||||
{"type":"assistant/chunk","seq":13,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"usage","usage":{"inputTokens":3,"outputTokens":3}}}}
|
||||
{"type":"assistant/chunk","seq":14,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"finish","reason":{"kind":"stop"}}}}
|
||||
{"type":"assistant/message","seq":15,"time":0,"data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"text","text":"WORKFLOW_CHILD_OK"}],"source":{"kind":"model","provider":"deepseek-official","model":"smoke-model"},"id":"{{messageId}}"},"usage":{"inputTokens":3,"outputTokens":3}},"sourceEventSeqs":[10,11,12,13,14],"surfaceOp":"append"}
|
||||
{"type":"step/end","seq":16,"time":0,"data":{"turn":1,"step":1}}
|
||||
{"type":"turn/end","seq":17,"time":0,"data":{"turn":1,"reason":{"kind":"completed"}}}
|
||||
|
||||
@@ -47,25 +47,29 @@
|
||||
{"type":"assistant/chunk","seq":45,"time":0,"data":{"turn":1,"step":4,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}}
|
||||
{"type":"assistant/message","seq":46,"time":0,"data":{"turn":1,"step":4,"message":{"role":"assistant","content":[{"type":"tool-call","id":"advanced-workflow","name":"workflow","arguments":"{\"script\": \"phase('Delegate')\\nconst reply = await agent('Reply with exactly WORKFLOW_CHILD_OK and nothing else.', { label: 'workflow-child' })\\nreturn { reply }\", \"meta\": {\"name\": \"advanced-exe-snapshot\", \"description\": \"exercise one packaged workflow child\"}}"}],"source":{"kind":"model","provider":"deepseek-official","model":"smoke-model"},"id":"{{messageId}}"},"usage":{"inputTokens":3,"outputTokens":3}},"sourceEventSeqs":[41,42,43,44,45],"surfaceOp":"append"}
|
||||
{"type":"tool/call","seq":47,"time":0,"data":{"turn":1,"step":4,"callId":"advanced-workflow","name":"workflow","arguments":"{\"script\": \"phase('Delegate')\\nconst reply = await agent('Reply with exactly WORKFLOW_CHILD_OK and nothing else.', { label: 'workflow-child' })\\nreturn { reply }\", \"meta\": {\"name\": \"advanced-exe-snapshot\", \"description\": \"exercise one packaged workflow child\"}}"}}
|
||||
{"type":"tool/result","seq":48,"time":0,"data":{"turn":1,"step":4,"message":{"source":{"kind":"tool","callId":"advanced-workflow"},"content":[{"type":"tool-result","toolCallId":"advanced-workflow","content":[{"type":"text","text":"workflow \"advanced-exe-snapshot\" completed (1 agent).\nReturn value:\n{\n \"reply\": \"WORKFLOW_CHILD_OK\"\n}"}],"isError":false}],"role":"user","id":"{{messageId}}"}},"sourceEventSeqs":[47],"surfaceOp":"append"}
|
||||
{"type":"step/end","seq":49,"time":0,"data":{"turn":1,"step":4}}
|
||||
{"type":"step/start","seq":50,"time":0,"data":{"turn":1,"step":5}}
|
||||
{"type":"assistant/chunk","seq":51,"time":0,"data":{"turn":1,"step":5,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}}}
|
||||
{"type":"assistant/chunk","seq":52,"time":0,"data":{"turn":1,"step":5,"chunk":{"type":"tool-call-delta","index":0,"id":"advanced-unmount","name":"cordis_unmount","argumentsDelta":"{\"id\": \"dyn-1\"}"}}}
|
||||
{"type":"assistant/chunk","seq":53,"time":0,"data":{"turn":1,"step":5,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"advanced-unmount","name":"cordis_unmount","arguments":"{\"id\": \"dyn-1\"}"}}}}
|
||||
{"type":"assistant/chunk","seq":54,"time":0,"data":{"turn":1,"step":5,"chunk":{"type":"usage","usage":{"inputTokens":3,"outputTokens":3}}}}
|
||||
{"type":"assistant/chunk","seq":55,"time":0,"data":{"turn":1,"step":5,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}}
|
||||
{"type":"assistant/message","seq":56,"time":0,"data":{"turn":1,"step":5,"message":{"role":"assistant","content":[{"type":"tool-call","id":"advanced-unmount","name":"cordis_unmount","arguments":"{\"id\": \"dyn-1\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"smoke-model"},"id":"{{messageId}}"},"usage":{"inputTokens":3,"outputTokens":3}},"sourceEventSeqs":[51,52,53,54,55],"surfaceOp":"append"}
|
||||
{"type":"tool/call","seq":57,"time":0,"data":{"turn":1,"step":5,"callId":"advanced-unmount","name":"cordis_unmount","arguments":"{\"id\": \"dyn-1\"}"}}
|
||||
{"type":"tool/result","seq":58,"time":0,"data":{"turn":1,"step":5,"message":{"source":{"kind":"tool","callId":"advanced-unmount"},"content":[{"type":"tool-result","toolCallId":"advanced-unmount","content":[{"type":"text","text":"Temporary Plugin dyn-1 was unmounted and removed."}],"isError":false}],"role":"user","id":"{{messageId}}"}},"sourceEventSeqs":[57],"surfaceOp":"append"}
|
||||
{"type":"step/end","seq":59,"time":0,"data":{"turn":1,"step":5}}
|
||||
{"type":"step/start","seq":60,"time":0,"data":{"turn":1,"step":6}}
|
||||
{"type":"request/header","seq":61,"time":0,"data":{"header":{"config":{"provider":"deepseek-official","model":"smoke-model","maxTokens":256000,"reasoningEffort":"high"},"adapterDefaults":{"reasoningEffort":true,"maxTokens":true},"system":"{{system}}","tools":["cordis_inspect","cordis_mount","cordis_unmount","run_code","subagent","task_kill","task_list","task_output","workflow"]},"reason":"change"}}
|
||||
{"type":"assistant/chunk","seq":62,"time":0,"data":{"turn":1,"step":6,"chunk":{"type":"block-start","index":0,"blockType":"text"}}}
|
||||
{"type":"assistant/chunk","seq":63,"time":0,"data":{"turn":1,"step":6,"chunk":{"type":"text-delta","index":0,"text":"ADVANCED_EXECUTABLE_OK"}}}
|
||||
{"type":"assistant/chunk","seq":64,"time":0,"data":{"turn":1,"step":6,"chunk":{"type":"block-end","index":0,"block":{"type":"text","text":"ADVANCED_EXECUTABLE_OK"}}}}
|
||||
{"type":"assistant/chunk","seq":65,"time":0,"data":{"turn":1,"step":6,"chunk":{"type":"usage","usage":{"inputTokens":3,"outputTokens":3}}}}
|
||||
{"type":"assistant/chunk","seq":66,"time":0,"data":{"turn":1,"step":6,"chunk":{"type":"finish","reason":{"kind":"stop"}}}}
|
||||
{"type":"assistant/message","seq":67,"time":0,"data":{"turn":1,"step":6,"message":{"role":"assistant","content":[{"type":"text","text":"ADVANCED_EXECUTABLE_OK"}],"source":{"kind":"model","provider":"deepseek-official","model":"smoke-model"},"id":"{{messageId}}"},"usage":{"inputTokens":3,"outputTokens":3}},"sourceEventSeqs":[62,63,64,65,66],"surfaceOp":"append"}
|
||||
{"type":"step/end","seq":68,"time":0,"data":{"turn":1,"step":6}}
|
||||
{"type":"turn/end","seq":69,"time":0,"data":{"turn":1,"reason":{"kind":"completed"}}}
|
||||
{"type":"tool-workflow/run-start","seq":48,"time":0,"data":{"runId":"{{workflow-run}}","name":"advanced-exe-snapshot"}}
|
||||
{"type":"tool-workflow/agent-start","seq":49,"time":0,"data":{"runId":"{{workflow-run}}","seq":1,"label":"workflow-child","phase":"Delegate","childId":"{{child-2}}"}}
|
||||
{"type":"tool-workflow/agent-end","seq":50,"time":0,"data":{"runId":"{{workflow-run}}","seq":1,"outcome":"completed"}}
|
||||
{"type":"tool-workflow/run-end","seq":51,"time":0,"data":{"runId":"{{workflow-run}}","stopReason":"completed"}}
|
||||
{"type":"tool/result","seq":52,"time":0,"data":{"turn":1,"step":4,"message":{"source":{"kind":"tool","callId":"advanced-workflow"},"content":[{"type":"tool-result","toolCallId":"advanced-workflow","content":[{"type":"text","text":"workflow \"advanced-exe-snapshot\" completed (1 agent).\nReturn value:\n{\n \"reply\": \"WORKFLOW_CHILD_OK\"\n}"}],"isError":false}],"role":"user","id":"{{messageId}}"}},"sourceEventSeqs":[47],"surfaceOp":"append"}
|
||||
{"type":"step/end","seq":53,"time":0,"data":{"turn":1,"step":4}}
|
||||
{"type":"step/start","seq":54,"time":0,"data":{"turn":1,"step":5}}
|
||||
{"type":"assistant/chunk","seq":55,"time":0,"data":{"turn":1,"step":5,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}}}
|
||||
{"type":"assistant/chunk","seq":56,"time":0,"data":{"turn":1,"step":5,"chunk":{"type":"tool-call-delta","index":0,"id":"advanced-unmount","name":"cordis_unmount","argumentsDelta":"{\"id\": \"dyn-1\"}"}}}
|
||||
{"type":"assistant/chunk","seq":57,"time":0,"data":{"turn":1,"step":5,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"advanced-unmount","name":"cordis_unmount","arguments":"{\"id\": \"dyn-1\"}"}}}}
|
||||
{"type":"assistant/chunk","seq":58,"time":0,"data":{"turn":1,"step":5,"chunk":{"type":"usage","usage":{"inputTokens":3,"outputTokens":3}}}}
|
||||
{"type":"assistant/chunk","seq":59,"time":0,"data":{"turn":1,"step":5,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}}
|
||||
{"type":"assistant/message","seq":60,"time":0,"data":{"turn":1,"step":5,"message":{"role":"assistant","content":[{"type":"tool-call","id":"advanced-unmount","name":"cordis_unmount","arguments":"{\"id\": \"dyn-1\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"smoke-model"},"id":"{{messageId}}"},"usage":{"inputTokens":3,"outputTokens":3}},"sourceEventSeqs":[55,56,57,58,59],"surfaceOp":"append"}
|
||||
{"type":"tool/call","seq":61,"time":0,"data":{"turn":1,"step":5,"callId":"advanced-unmount","name":"cordis_unmount","arguments":"{\"id\": \"dyn-1\"}"}}
|
||||
{"type":"tool/result","seq":62,"time":0,"data":{"turn":1,"step":5,"message":{"source":{"kind":"tool","callId":"advanced-unmount"},"content":[{"type":"tool-result","toolCallId":"advanced-unmount","content":[{"type":"text","text":"Temporary Plugin dyn-1 was unmounted and removed."}],"isError":false}],"role":"user","id":"{{messageId}}"}},"sourceEventSeqs":[61],"surfaceOp":"append"}
|
||||
{"type":"step/end","seq":63,"time":0,"data":{"turn":1,"step":5}}
|
||||
{"type":"step/start","seq":64,"time":0,"data":{"turn":1,"step":6}}
|
||||
{"type":"request/header","seq":65,"time":0,"data":{"header":{"config":{"provider":"deepseek-official","model":"smoke-model","maxTokens":256000,"reasoningEffort":"high"},"adapterDefaults":{"reasoningEffort":true,"maxTokens":true},"system":"{{system}}","tools":["cordis_inspect","cordis_mount","cordis_unmount","run_code","subagent","task_kill","task_list","task_output","workflow"]},"reason":"change"}}
|
||||
{"type":"assistant/chunk","seq":66,"time":0,"data":{"turn":1,"step":6,"chunk":{"type":"block-start","index":0,"blockType":"text"}}}
|
||||
{"type":"assistant/chunk","seq":67,"time":0,"data":{"turn":1,"step":6,"chunk":{"type":"text-delta","index":0,"text":"ADVANCED_EXECUTABLE_OK"}}}
|
||||
{"type":"assistant/chunk","seq":68,"time":0,"data":{"turn":1,"step":6,"chunk":{"type":"block-end","index":0,"block":{"type":"text","text":"ADVANCED_EXECUTABLE_OK"}}}}
|
||||
{"type":"assistant/chunk","seq":69,"time":0,"data":{"turn":1,"step":6,"chunk":{"type":"usage","usage":{"inputTokens":3,"outputTokens":3}}}}
|
||||
{"type":"assistant/chunk","seq":70,"time":0,"data":{"turn":1,"step":6,"chunk":{"type":"finish","reason":{"kind":"stop"}}}}
|
||||
{"type":"assistant/message","seq":71,"time":0,"data":{"turn":1,"step":6,"message":{"role":"assistant","content":[{"type":"text","text":"ADVANCED_EXECUTABLE_OK"}],"source":{"kind":"model","provider":"deepseek-official","model":"smoke-model"},"id":"{{messageId}}"},"usage":{"inputTokens":3,"outputTokens":3}},"sourceEventSeqs":[66,67,68,69,70],"surfaceOp":"append"}
|
||||
{"type":"step/end","seq":72,"time":0,"data":{"turn":1,"step":6}}
|
||||
{"type":"turn/end","seq":73,"time":0,"data":{"turn":1,"reason":{"kind":"completed"}}}
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -12,8 +12,9 @@ import {
|
||||
storeGitBlob,
|
||||
} from './translation-pairing-git.ts'
|
||||
import {
|
||||
linksTo,
|
||||
isTranslationScopeFile,
|
||||
languageSwitcherTargets,
|
||||
linksTo,
|
||||
parseTranslationMarkdown,
|
||||
requiresSourceLanguageSwitcher,
|
||||
translationStructureDiff,
|
||||
@@ -164,15 +165,17 @@ function loadRecordOwners(
|
||||
function assertMergedPairStructure(paths: TranslationPairPaths, source: Buffer, zh: Buffer): void {
|
||||
const sourceTree = parseTranslationMarkdown(source.toString('utf8'))
|
||||
const zhTree = parseTranslationMarkdown(zh.toString('utf8'))
|
||||
if (requiresSourceLanguageSwitcher(paths.source) && !linksTo(sourceTree, basename(paths.zh))) {
|
||||
const sourceSwitcherTargets = languageSwitcherTargets(paths.source)
|
||||
const zhSwitcherTargets = languageSwitcherTargets(paths.zh)
|
||||
if (requiresSourceLanguageSwitcher(paths.source) && !linksTo(sourceTree, zhSwitcherTargets)) {
|
||||
throw new Error(`${paths.source} clean merge lost its language-switcher link to ${basename(paths.zh)}`)
|
||||
}
|
||||
if (!linksTo(zhTree, basename(paths.source))) {
|
||||
if (!linksTo(zhTree, sourceSwitcherTargets)) {
|
||||
throw new Error(`${paths.zh} clean merge lost its language-switcher link to ${basename(paths.source)}`)
|
||||
}
|
||||
const divergences = translationStructureDiff(
|
||||
translationStructureSignature(sourceTree, basename(paths.zh)),
|
||||
translationStructureSignature(zhTree, basename(paths.source)),
|
||||
translationStructureSignature(sourceTree, zhSwitcherTargets),
|
||||
translationStructureSignature(zhTree, sourceSwitcherTargets),
|
||||
)
|
||||
if (divergences.length > 0) {
|
||||
throw new Error(`${paths.source} and ${paths.zh} clean merges diverge structurally: ${divergences.join('; ')}`)
|
||||
|
||||
@@ -14,6 +14,8 @@ import {
|
||||
import {
|
||||
blobHash,
|
||||
isTranslationScopeFile,
|
||||
languageSwitcherTargets,
|
||||
linksTo,
|
||||
pairAnchorOfArgument,
|
||||
parseTranslationMarkdown,
|
||||
parseTranslationPairingCliArgs,
|
||||
@@ -151,6 +153,20 @@ describe('translation pairing switchers', () => {
|
||||
expect(requiresSourceLanguageSwitcher('docs/architecture.md')).toBe(true)
|
||||
expect(requiresSourceLanguageSwitcher('packages/core/session/README.md')).toBe(true)
|
||||
})
|
||||
|
||||
it('accepts only the canonical public URL for an absolute switcher', () => {
|
||||
const targets = languageSwitcherTargets('python/sdk/README.zh.md')
|
||||
const canonical = parseTranslationMarkdown(
|
||||
'[中文](https://github.com/deepseek-ai/deepseek-harness/blob/master/python/sdk/README.zh.md)',
|
||||
)
|
||||
const wrongPath = parseTranslationMarkdown(
|
||||
'[中文](https://github.com/deepseek-ai/deepseek-harness/blob/master/other/README.zh.md)',
|
||||
)
|
||||
|
||||
expect(linksTo(canonical, targets)).toBe(true)
|
||||
expect(translationStructureSignature(canonical, targets).links).toEqual([])
|
||||
expect(linksTo(wrongPath, targets)).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe('translation pairing records', () => {
|
||||
|
||||
@@ -302,11 +302,19 @@ export function parseTranslationMarkdown(content: string): Nodes {
|
||||
return fromMarkdown(content, { extensions: [gfm()], mdastExtensions: [gfmFromMarkdown()] })
|
||||
}
|
||||
|
||||
/** Whether the tree contains a link to exactly `target`. */
|
||||
export function linksTo(tree: Nodes, target: string): boolean {
|
||||
const PUBLIC_REPOSITORY_BLOB_ROOT = 'https://github.com/deepseek-ai/deepseek-harness/blob/master/'
|
||||
|
||||
/** Return the accepted relative and public-repository links to one counterpart. */
|
||||
export function languageSwitcherTargets(counterpart: string): string[] {
|
||||
return [basename(counterpart), `${PUBLIC_REPOSITORY_BLOB_ROOT}${counterpart}`]
|
||||
}
|
||||
|
||||
/** Whether the tree contains a link to any accepted target. */
|
||||
export function linksTo(tree: Nodes, targets: string | readonly string[]): boolean {
|
||||
const accepted = new Set(typeof targets === 'string' ? [targets] : targets)
|
||||
let found = false
|
||||
const visit = (node: Nodes): void => {
|
||||
if (node.type === 'link' && node.url === target) found = true
|
||||
if (node.type === 'link' && accepted.has(node.url)) found = true
|
||||
if ('children' in node) for (const child of node.children) visit(child)
|
||||
}
|
||||
visit(tree)
|
||||
@@ -335,8 +343,14 @@ export function requiresSourceLanguageSwitcher(source: string): boolean {
|
||||
].includes(source)
|
||||
}
|
||||
|
||||
/** Collect the ordered structural signature, skipping one switcher target. */
|
||||
export function translationStructureSignature(tree: Nodes, switcherTarget: string): TranslationStructureSignature {
|
||||
/** Collect the ordered structural signature, skipping accepted switcher targets. */
|
||||
export function translationStructureSignature(
|
||||
tree: Nodes,
|
||||
switcherTargets: string | readonly string[],
|
||||
): TranslationStructureSignature {
|
||||
const acceptedSwitchers = new Set(
|
||||
typeof switcherTargets === 'string' ? [switcherTargets] : switcherTargets,
|
||||
)
|
||||
const sig: TranslationStructureSignature = { headings: [], code: [], tables: [], lists: [], links: [] }
|
||||
const visit = (node: Nodes): void => {
|
||||
switch (node.type) {
|
||||
@@ -355,7 +369,7 @@ export function translationStructureSignature(tree: Nodes, switcherTarget: strin
|
||||
: `bullet:items=${node.children.length}`)
|
||||
break
|
||||
case 'link':
|
||||
if (node.url !== switcherTarget) sig.links.push(node.url)
|
||||
if (!acceptedSwitchers.has(node.url)) sig.links.push(node.url)
|
||||
break
|
||||
default:
|
||||
// Every other node kind is prose or a container, not part of the signature.
|
||||
|
||||
@@ -221,10 +221,90 @@
|
||||
"symbol": "GoalChanged",
|
||||
"source": "packages/goal/goal/src/domain.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/subsystems/schedule.md",
|
||||
"symbol": "AfterScheduleRecord",
|
||||
"source": "packages/schedule/tool-schedule/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/subsystems/schedule.md",
|
||||
"symbol": "AtScheduleRecord",
|
||||
"source": "packages/schedule/tool-schedule/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/subsystems/schedule.md",
|
||||
"symbol": "EveryScheduleRecord",
|
||||
"source": "packages/schedule/tool-schedule/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/subsystems/schedule.md",
|
||||
"symbol": "LocalAtInput",
|
||||
"source": "packages/schedule/tool-schedule/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/subsystems/schedule.md",
|
||||
"symbol": "AtInput",
|
||||
"source": "packages/schedule/tool-schedule/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/subsystems/schedule.md",
|
||||
"symbol": "OneShotScheduleRecord",
|
||||
"source": "packages/schedule/tool-schedule/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/subsystems/schedule.md",
|
||||
"symbol": "ScheduleRecord",
|
||||
"source": "packages/schedule/tool-schedule/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/subsystems/schedule.md",
|
||||
"symbol": "ScheduleCreateChange",
|
||||
"source": "packages/schedule/tool-schedule/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/subsystems/schedule.md",
|
||||
"symbol": "ScheduleDeleteChange",
|
||||
"source": "packages/schedule/tool-schedule/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/subsystems/schedule.md",
|
||||
"symbol": "OneShotScheduleDispatchChange",
|
||||
"source": "packages/schedule/tool-schedule/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/subsystems/schedule.md",
|
||||
"symbol": "EveryScheduleDispatchChange",
|
||||
"source": "packages/schedule/tool-schedule/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/subsystems/schedule.md",
|
||||
"symbol": "ScheduleDispatchChange",
|
||||
"source": "packages/schedule/tool-schedule/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/subsystems/schedule.md",
|
||||
"symbol": "ScheduleChange",
|
||||
"source": "packages/schedule/tool-schedule/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/subsystems/schedule.md",
|
||||
"symbol": "ScheduleState",
|
||||
"source": "packages/schedule/tool-schedule/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/subsystems/schedule.md",
|
||||
"symbol": "ScheduleDeliveryMode",
|
||||
"source": "packages/schedule/tool-schedule/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/subsystems/schedule.md",
|
||||
"symbol": "ScheduleView",
|
||||
"source": "packages/schedule/tool-schedule/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/subsystems/commands.md",
|
||||
"symbol": "CommandInputDescriptor",
|
||||
"source": "packages/interaction/commands/src/index.ts"
|
||||
"source": "packages/interaction/commands/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/subsystems/commands.md",
|
||||
@@ -239,12 +319,12 @@
|
||||
{
|
||||
"doc": "docs/subsystems/commands.md",
|
||||
"symbol": "CommandResult",
|
||||
"source": "packages/interaction/commands/src/index.ts"
|
||||
"source": "packages/interaction/commands/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/subsystems/commands.md",
|
||||
"symbol": "CommandDescriptor",
|
||||
"source": "packages/interaction/commands/src/index.ts"
|
||||
"source": "packages/interaction/commands/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/subsystems/commands.md",
|
||||
|
||||
@@ -35,6 +35,7 @@ const root = resolve(import.meta.dirname, '..')
|
||||
// specifiers resolve from apps/cli rather than the examples workspace.
|
||||
const appOverlayFiles = new Set([
|
||||
'examples/web-cordis/cordis.yml',
|
||||
'examples/web-schedule/cordis.yml',
|
||||
...globSync('examples/mcp-memory/*.cordis.yml', { cwd: root }),
|
||||
])
|
||||
const metadataFields = ['id', 'name', 'group', 'inject', 'intercept', 'isolate'] as const
|
||||
@@ -43,14 +44,17 @@ const metadataFields = ['id', 'name', 'group', 'inject', 'intercept', 'isolate']
|
||||
const CHOOSER_PACKAGE = '@deepseek-ai/dsh-host-directory-picker-auto'
|
||||
|
||||
/**
|
||||
* The backends the chooser mounts by runtime string (mirror of its exported
|
||||
* `BACKEND_PACKAGES`), invisible to yml-row scanning: a composition mounting
|
||||
* the chooser must resolve both, or keyless Linux CI (which only ever
|
||||
* resolves `browse`) hides a dropped `-native` dependency until a macOS boot.
|
||||
* The packages the chooser mounts by runtime string (mirror of its exported
|
||||
* `BACKEND_PACKAGES` and `SURFACE_PACKAGES`), invisible to yml-row scanning: a
|
||||
* composition mounting the chooser must resolve every one, or keyless Linux CI
|
||||
* (which only ever resolves `browse`) hides a dropped `-native` dependency
|
||||
* until a macOS boot.
|
||||
*/
|
||||
const CHOOSER_BACKEND_PACKAGES = [
|
||||
'@deepseek-ai/dsh-host-directory-picker-native',
|
||||
'@deepseek-ai/dsh-host-directory-picker-browse',
|
||||
'@deepseek-ai/dsh-client-ui-directory-picker',
|
||||
'@deepseek-ai/dsh-client-ui-directory-picker-native',
|
||||
]
|
||||
const jsExprType = new yaml.Type('tag:yaml.org,2002:js', {
|
||||
kind: 'scalar',
|
||||
|
||||
@@ -71,6 +71,7 @@ const SENTENCE_MODEL_EXPERIENCE: Readonly<Record<string, SentenceContract>> = {
|
||||
'packages/client/ui-layout': { kind: 'none', reason: 'Browser-side UI plugin layer; registers nothing model-facing.' },
|
||||
'packages/client/ui-sidebar': { kind: 'none', reason: 'Browser-side UI plugin layer; registers nothing model-facing.' },
|
||||
'packages/client/ui-conversation': { kind: 'none', reason: 'Browser-side UI plugin layer; registers nothing model-facing.' },
|
||||
'packages/client/ui-feedback': { kind: 'none', reason: 'Browser-side controls over the message-feedback sidecar; ratings and notes never enter the Session log, model context, or telemetry.' },
|
||||
'packages/client/ui-tool': { kind: 'none', reason: 'Browser-side Tool presentation layer; renders logged calls without changing model context.' },
|
||||
'packages/client/ui-deliverables': { kind: 'none', reason: 'Browser-side UI plugin layer; registers nothing model-facing.' },
|
||||
'packages/client/ui-task': { kind: 'none', reason: 'Browser-side read-only projection of ctx.tasks records; dsh-tool-tasks owns the model-facing behavior.' },
|
||||
@@ -85,10 +86,13 @@ const SENTENCE_MODEL_EXPERIENCE: Readonly<Record<string, SentenceContract>> = {
|
||||
'packages/client/ui-question': { kind: 'indirect', reason: 'The package mounts dsh-tool-ask-user; that tool owns the model-visible schema and answer rendering.' },
|
||||
'packages/client/ui-trajectory': { kind: 'none', reason: 'Browser-side UI plugin layer; registers nothing model-facing.' },
|
||||
'packages/client/ui-workspace': { kind: 'none', reason: 'Browser-side UI plugin layer; registers nothing model-facing.' },
|
||||
'packages/client/ui-directory-picker': { kind: 'none', reason: 'Browser-side directory-browsing surface; registers nothing model-facing.' },
|
||||
'packages/client/ui-directory-picker-native': { kind: 'none', reason: 'Browser-side surface driving the host OS chooser; registers nothing model-facing.' },
|
||||
'packages/client/ui-theme': { kind: 'none', reason: 'Browser-side UI plugin layer; registers nothing model-facing.' },
|
||||
'packages/client/ui-settings': { kind: 'none', reason: 'Browser-side UI plugin layer; registers nothing model-facing.' },
|
||||
'packages/client/ui-settings-general': { kind: 'none', reason: 'Browser-side UI plugin layer; registers nothing model-facing.' },
|
||||
'packages/client/ui-models': { kind: 'none', reason: 'Browser-side UI plugin layer; registers nothing model-facing.' },
|
||||
'packages/client/ui-plugins': { kind: 'none', reason: 'Browser-side inventory projection; registers nothing model-facing.' },
|
||||
'packages/client/locale': { kind: 'none', reason: 'Browser-side UI plugin layer; registers nothing model-facing.' },
|
||||
'packages/client/web': { kind: 'none', reason: 'Browser-side UI plugin layer; registers nothing model-facing.' },
|
||||
'packages/examples/agent-spine-demo': { kind: 'indirect', reason: 'The bundle only mounts model-facing child plugins.' },
|
||||
@@ -103,6 +107,7 @@ const SENTENCE_MODEL_EXPERIENCE: Readonly<Record<string, SentenceContract>> = {
|
||||
'packages/host/directory-picker-native': { kind: 'none', reason: 'The GUI-host picking backend registers nothing model-facing.' },
|
||||
'packages/host/webserver': { kind: 'none', reason: 'The HTTP carrier bridges browser and API handler and registers nothing model-facing.' },
|
||||
'packages/host/frontend-static': { kind: 'none', reason: 'The SPA dist server answers browser asset requests and registers nothing model-facing.' },
|
||||
'packages/host/plugin-inventory': { kind: 'none', reason: 'Host-side read-only Loader projection; registers nothing model-facing.' },
|
||||
'packages/bundle/base': { kind: 'indirect', reason: 'The bundle is a patch-list carrier; each inserted row\'s package owns its model-facing behavior.' },
|
||||
'packages/bundle/headless': { kind: 'none', reason: 'The one-shot runner submits the task as an ordinary user message; prompts and tools belong to the composed base and headless bundles.' },
|
||||
'packages/llm/llm': { kind: 'none', reason: 'The adapter registry forwards already-assembled requests unchanged.' },
|
||||
@@ -118,6 +123,7 @@ const SENTENCE_MODEL_EXPERIENCE: Readonly<Record<string, SentenceContract>> = {
|
||||
'packages/sdk/protocol': { kind: 'none', reason: 'Client-facing wire library; the runtime plugins behind the serving entry own model-facing behavior.' },
|
||||
'packages/session/session-projection': { kind: 'none', reason: 'The projection registry serves client-facing read models of already-logged session state and registers nothing model-facing.' },
|
||||
'packages/session/session-projection-cache': { kind: 'none', reason: 'The persisted cache accelerates host-side cold reads of projection state and registers nothing model-facing.' },
|
||||
'packages/session/session-stats': { kind: 'none', reason: 'The sessionStats unit folds already-logged step boundaries into a client-facing read model and registers nothing model-facing.' },
|
||||
'packages/session-query/session-query': { kind: 'none', reason: 'The trusted query service exposes cloned records only to callers and registers nothing model-facing.' },
|
||||
'packages/session-query/session-query-sqlite': { kind: 'none', reason: 'The search backend returns hits only to callers and registers nothing model-facing.' },
|
||||
'packages/settings/settings': { kind: 'indirect', reason: 'The seam stores and resolves user settings; consumer plugins own any model-facing content fed by a value.' },
|
||||
@@ -127,7 +133,7 @@ const SENTENCE_MODEL_EXPERIENCE: Readonly<Record<string, SentenceContract>> = {
|
||||
'packages/util/atomic-write': { kind: 'none', reason: 'Pure filesystem write primitive; registers nothing model-facing.' },
|
||||
'packages/session/session-telemetry': { kind: 'none', reason: 'The seam observes the session stream and hands redacted copies outward; it registers nothing model-facing.' },
|
||||
'packages/session/session-telemetry-otel': { kind: 'none', reason: 'The backend forwards seam records into the OTel SDK pipeline and registers nothing model-facing.' },
|
||||
'packages/session/user-id': { kind: 'none', reason: 'The shared identifier appears only in telemetry metadata and a direct human command response; it registers nothing model-facing.' },
|
||||
'packages/session/user-id': { kind: 'none', reason: 'The shared identifier reaches DeepSeek only as model-hidden HTTP metadata; it registers nothing model-facing.' },
|
||||
'packages/skill/skill': { kind: 'indirect', reason: 'The provider registry delegates model rendering to dsh-tool-skill.' },
|
||||
'packages/skill/skill-badge': { kind: 'indirect', reason: 'The bundled provider delegates model rendering to dsh-tool-skill.' },
|
||||
'packages/skill/skill-local': { kind: 'indirect', reason: 'The provider backend delegates model rendering to dsh-tool-skill.' },
|
||||
|
||||
@@ -19,6 +19,7 @@ import {
|
||||
translationPairPaths,
|
||||
} from './translation-pairing-record.ts'
|
||||
import {
|
||||
languageSwitcherTargets,
|
||||
linksTo,
|
||||
parseTranslationMarkdown,
|
||||
parseTranslationPairingCliArgs,
|
||||
@@ -252,15 +253,17 @@ for (const source of [...pairAnchors].sort()) {
|
||||
|
||||
const sourceTree = parseTranslationMarkdown(sourceContent.toString('utf8'))
|
||||
const zhTree = parseTranslationMarkdown(zhContent.toString('utf8'))
|
||||
if (!linksTo(zhTree, basename(source))) {
|
||||
const sourceSwitcherTargets = languageSwitcherTargets(source)
|
||||
const zhSwitcherTargets = languageSwitcherTargets(zh)
|
||||
if (!linksTo(zhTree, sourceSwitcherTargets)) {
|
||||
errors.push(`${zh}: missing language switcher — no link to ${basename(source)}`)
|
||||
}
|
||||
if (requiresSourceLanguageSwitcher(source) && !linksTo(sourceTree, basename(zh))) {
|
||||
if (requiresSourceLanguageSwitcher(source) && !linksTo(sourceTree, zhSwitcherTargets)) {
|
||||
errors.push(`${source}: missing language switcher — no link back to ${basename(zh)}`)
|
||||
}
|
||||
for (const divergence of translationStructureDiff(
|
||||
translationStructureSignature(sourceTree, basename(zh)),
|
||||
translationStructureSignature(zhTree, basename(source)),
|
||||
translationStructureSignature(sourceTree, zhSwitcherTargets),
|
||||
translationStructureSignature(zhTree, sourceSwitcherTargets),
|
||||
)) {
|
||||
errors.push(`${source} ↔ ${zh}: ${divergence}`)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user