Merge updated master and helper identity into bundled Computer Use
This commit is contained in:
commit
7d9bc01a50
1349 files changed
+27206
-18734
No files matched your search
@@ -66,7 +66,7 @@ it('copies workspace package links as files and removes command shims', async ()
|
||||
expect(await listSymlinks(staging)).toEqual([])
|
||||
})
|
||||
|
||||
it('shares an identical workspace package while retaining different peers and third-party copies', async () => {
|
||||
it.each(['source', 'deployment'])('shares identical workspace packages with %s-relative metadata', async (base) => {
|
||||
const workspace = await fixture('dsh-python-staging-dedupe-')
|
||||
const staging = join(workspace, 'payload')
|
||||
const modules = join(staging, 'node_modules')
|
||||
@@ -79,14 +79,15 @@ it('shares an identical workspace package while retaining different peers and th
|
||||
await mkdir(directory, { recursive: true })
|
||||
await writeFile(join(directory, 'index.js'), 'exports.scheduler = Symbol("scheduler")\n')
|
||||
}
|
||||
const locations = (paths: string[]) => paths.map(path => relative(workspace, path))
|
||||
const metadataRoot = base === 'source' ? workspace : staging
|
||||
const locations = (paths: string[]) => paths.map(path => relative(metadataRoot, path))
|
||||
await writeFile(join(modules, '.modules.yaml'), JSON.stringify({ hoistedLocations: {
|
||||
'@fixture/tools@file:packages/tools(peer@1)': locations([root, duplicate]),
|
||||
'@fixture/tools@file:packages/tools(peer@2)': locations([differentPeers]),
|
||||
'[email protected](@fixture/tools@file:packages/tools)': locations([external, externalCopy]),
|
||||
} }))
|
||||
|
||||
await deduplicateStagedWorkspacePackages(staging, workspace)
|
||||
await deduplicateStagedWorkspacePackages(staging, metadataRoot)
|
||||
|
||||
expect(existsSync(duplicate)).toBe(false)
|
||||
for (const directory of [root, differentPeers, external, externalCopy]) {
|
||||
|
||||
@@ -14,9 +14,9 @@ function isHoistedLocations(value: unknown): value is Record<string, string[]> {
|
||||
* Different peer resolutions and third-party packages keep their installed locations.
|
||||
* All metadata paths are checked before deletion; the caller first materializes links.
|
||||
* @param staging - Symlink-free deployed payload root.
|
||||
* @param workspace - Workspace root relative to which pnpm records hoisted locations.
|
||||
* @param metadataRoot - Base for pnpm hoistedLocations: the workspace for legacy deploy, the deployment for default deploy.
|
||||
*/
|
||||
export async function deduplicateStagedWorkspacePackages(staging: string, workspace: string): Promise<void> {
|
||||
export async function deduplicateStagedWorkspacePackages(staging: string, metadataRoot: string): Promise<void> {
|
||||
const modules = resolve(staging, 'node_modules')
|
||||
const file = join(modules, '.modules.yaml')
|
||||
const manifest: unknown = load(await readFile(file, 'utf8'))
|
||||
@@ -27,7 +27,7 @@ export async function deduplicateStagedWorkspacePackages(staging: string, worksp
|
||||
const duplicates: string[] = []
|
||||
for (const [identity, locations] of Object.entries(manifest.hoistedLocations)) {
|
||||
const paths = locations.map((location) => {
|
||||
const path = resolve(workspace, location)
|
||||
const path = resolve(metadataRoot, location)
|
||||
const local = relative(modules, path)
|
||||
if (local === '' || local === '..' || local.startsWith(`..${sep}`) || isAbsolute(local)) {
|
||||
throw new Error(`${file}: hoistedLocations path is outside deployed node_modules: ${location}`)
|
||||
@@ -62,7 +62,7 @@ async function findSymlink(directory: string): Promise<string | undefined> {
|
||||
/**
|
||||
* Replace every deploy-time package link below the staged payload with real files.
|
||||
*
|
||||
* Legacy deploy links workspace packages into the target's `node_modules`, and
|
||||
* pnpm deploy can link workspace packages into the target's `node_modules`, and
|
||||
* those links must survive packaging as files. One link is not a package: when
|
||||
* the deploy target sits inside the deployed project, pnpm links that project
|
||||
* into its own `node_modules`. Dereferencing it would copy the payload into
|
||||
|
||||
@@ -136,6 +136,9 @@ describe('Python runtime executable builder CLI', () => {
|
||||
const deploy = result.stdout.split('\n').find(line => line.includes(' --filter dsh-python-runtime-closure deploy'))
|
||||
expect(deploy).toContain('--prod --config.allow-unused-patches=true')
|
||||
expect(deploy).toContain('--config.hoist-workspace-packages=false')
|
||||
expect(deploy).toContain(`--config.pnpmfile=${join(root, 'scripts/python-runtime-deploy.pnpmfile.mjs')}`)
|
||||
expect(deploy).not.toContain('--legacy')
|
||||
expect(deploy).not.toContain('--config.auto-install-peers=false')
|
||||
expect(result.stdout.split('--config.allow-unused-patches=true')).toHaveLength(2)
|
||||
expect(result.stdout).not.toContain(resolve(root, 'python/sdk-runtime/runtime-bootstrap.mjs'))
|
||||
expect(result.stdout).toContain('"bin":"runtime-bootstrap.mjs"')
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
import { createHash } from 'node:crypto'
|
||||
import { build as bundle } from 'tsdown'
|
||||
import { spawn } from 'node:child_process'
|
||||
import { pnpmInvocation, restoreLegacyHoists } from './executable-packaging.ts'
|
||||
import { pnpmInvocation, verifyStagedDependencies } from './executable-packaging.ts'
|
||||
import { existsSync, statSync } from 'node:fs'
|
||||
import { chmod, copyFile, cp, glob, mkdir, readFile, readdir, rm, writeFile } from 'node:fs/promises'
|
||||
import { basename, dirname, join, resolve, sep } from 'node:path'
|
||||
@@ -37,8 +37,6 @@ const OUT_DIR = 'dist-exe'
|
||||
const PYTHON_RUNTIME_DIR = 'python/sdk-runtime/src/deepseek_harness_runtime/runtime'
|
||||
/** The deployed closure doubles as the node-mode carrier. */
|
||||
const PYTHON_NODE_SUBDIR = 'node'
|
||||
/** Legacy deploy may hoist peer-specialized workspace packages back here. */
|
||||
const DEPLOY_SOURCE_NODE_MODULES = 'python/sdk-runtime/node_modules'
|
||||
/** Documentation excluded from the generated runtime directory. */
|
||||
const DEPLOY_ONLY_DOCS = ['README.md', 'README.zh.md', 'README.i18n.yaml']
|
||||
|
||||
@@ -304,24 +302,19 @@ class SingleExeBuild {
|
||||
}
|
||||
if (this.cli.dryRun) console.log(`build-exe-for-python-sdk: [dry-run] rm -rf ${this.staging}`)
|
||||
else await rm(this.staging, { recursive: true, force: true })
|
||||
try { await this.runPnpm('deploy', [
|
||||
await this.runPnpm('deploy', [
|
||||
'--filter',
|
||||
DEPLOY_ROOT_PACKAGE,
|
||||
'deploy',
|
||||
'--legacy',
|
||||
'--prod',
|
||||
// Production deployment omits workspace tooling such as Electron's patched signer.
|
||||
'--config.allow-unused-patches=true',
|
||||
'--config.node-linker=hoisted',
|
||||
'--config.auto-install-peers=false',
|
||||
`--config.pnpmfile=${join(root, 'scripts/python-runtime-deploy.pnpmfile.mjs')}`,
|
||||
'--config.link-workspace-packages=true',
|
||||
'--config.hoist-workspace-packages=false',
|
||||
this.staging,
|
||||
]) } finally {
|
||||
// Legacy deploy records production-only workspace state; restore the development installation before exec.
|
||||
await this.runPnpm('restore development dependencies', ['install', '--offline', '--frozen-lockfile', '--prod=false', '--ignore-scripts'])
|
||||
}
|
||||
await this.restoreLegacyHoists()
|
||||
])
|
||||
await this.materializeStagedLinks()
|
||||
if (this.cli.dryRun) {
|
||||
for (const name of DEPLOY_ONLY_DOCS) console.log(`build-exe-for-python-sdk: [dry-run] rm -f ${join(this.staging, name)}`)
|
||||
@@ -333,21 +326,6 @@ class SingleExeBuild {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore direct packages that pnpm's legacy hoister places beside the deploy
|
||||
* source instead of in the target. The runtime manifest supplies every peer,
|
||||
* so package-local node_modules trees are omitted to preserve one flat Cordis
|
||||
* instance and a symlink-free packaged payload.
|
||||
*/
|
||||
private async restoreLegacyHoists(): Promise<void> {
|
||||
if (this.cli.dryRun) {
|
||||
console.log('build-exe-for-python-sdk: [dry-run] restore direct dependencies omitted by legacy deploy')
|
||||
return
|
||||
}
|
||||
const restored = await restoreLegacyHoists(this.staging, resolve(root, DEPLOY_SOURCE_NODE_MODULES))
|
||||
if (restored.length > 0) console.log(`build-exe-for-python-sdk: restored legacy deploy hoists: ${restored.join(', ')}`)
|
||||
}
|
||||
|
||||
/** Replace deploy-time package links with files and reject any remaining link. */
|
||||
private async materializeStagedLinks(): Promise<void> {
|
||||
if (this.cli.dryRun) {
|
||||
@@ -355,7 +333,8 @@ class SingleExeBuild {
|
||||
return
|
||||
}
|
||||
await materializeStagedLinks(this.staging)
|
||||
await deduplicateStagedWorkspacePackages(this.staging, root)
|
||||
await deduplicateStagedWorkspacePackages(this.staging, this.staging)
|
||||
await verifyStagedDependencies(this.staging)
|
||||
}
|
||||
|
||||
/** Add the executable entry and pkg assets to the staged manifest. */
|
||||
|
||||
@@ -307,8 +307,8 @@ describe('package payload constraints', () => {
|
||||
])
|
||||
})
|
||||
|
||||
it('accepts the Agent Team icon payload and rejects its omission', () => {
|
||||
const dir = 'packages/experimental/agent-team-profile'
|
||||
it('accepts the Voice Input icon payload and rejects its omission', () => {
|
||||
const dir = 'packages/experimental/voice-input-bundle'
|
||||
const manifest = JSON.parse(readFileSync(new URL(`../${dir}/package.json`, import.meta.url), 'utf8')) as WorkspaceManifest['manifest']
|
||||
expect(checkWorkspaceManifest({ dir, manifest })).toEqual([])
|
||||
expect(checkWorkspaceManifest({ dir, manifest: { ...manifest, files: manifest.files!.filter(file => file !== 'icon.svg') } }))
|
||||
@@ -406,9 +406,9 @@ describe('package payload constraints', () => {
|
||||
})).toEqual(['lib/index.js', 'lib/types/**/*.d.ts'])
|
||||
})
|
||||
|
||||
it.each(['agent-team', 'agent-team-profile', 'auto-review', 'client-ui-agent-team', 'tool-agent-team'])(
|
||||
it.each(['team/agent-team', 'experimental/voice-input-bundle', 'experimental/auto-review', 'client/ui-agent-team', 'team/tool-agent-team'])(
|
||||
'accepts the published locale files for %s and rejects their omission', (name) => {
|
||||
const dir = `packages/experimental/${name}`
|
||||
const dir = `packages/${name}`
|
||||
const manifest = JSON.parse(readFileSync(new URL(`../${dir}/package.json`, import.meta.url), 'utf8')) as WorkspaceManifest['manifest']
|
||||
expect(checkWorkspaceManifest({ dir, manifest })).toEqual([])
|
||||
expect(checkWorkspaceManifest({ dir, manifest: {
|
||||
|
||||
@@ -1771,7 +1771,7 @@ describe('Policy workflow shells', () => {
|
||||
describe('Policy workflows', () => {
|
||||
const workflows = [
|
||||
['pr-check-on-head-branch.yml', ['master']],
|
||||
['pr-check-after-approve.yml', ['approve', 'review']],
|
||||
['pr-check-after-approve.yml', ['approve']],
|
||||
] as const
|
||||
|
||||
function stepsOf(job: Record<string, unknown>): Record<string, unknown>[] {
|
||||
@@ -1794,6 +1794,20 @@ describe('Policy workflows', () => {
|
||||
}
|
||||
})
|
||||
|
||||
it('decides the review verdict in one Approve Check job that can publish the check run', () => {
|
||||
const workflow = loadWorkflow('.github/workflows/pr-check-after-approve.yml')
|
||||
if (!isRecord(workflow.jobs)) throw new TypeError('the workflow must define jobs')
|
||||
expect(Object.keys(workflow.jobs)).toEqual(['approve'])
|
||||
const job = workflow.jobs.approve
|
||||
if (!isRecord(job) || !isRecord(job.permissions)) throw new TypeError('the approve job must declare permissions')
|
||||
expect(job.name).toBe('Approve Check')
|
||||
expect(job.permissions.checks).toBe('write')
|
||||
const check = stepsOf(job).find(step => typeof step.uses === 'string' && step.uses.startsWith('deepseek-harness/deepseek-harness-check@'))
|
||||
if (check === undefined || !isRecord(check.env)) throw new TypeError('the job must run the check action with env')
|
||||
expect(String(check.env.CHECK_MODE)).toContain('vars.MERGE_POLICY_HEAD_DIFF')
|
||||
expect(JSON.stringify(workflow)).not.toContain('MERGE_POLICY_REVIEW')
|
||||
})
|
||||
|
||||
it('keeps the pre-merge build behind the merge step, which honours the mode on its failures', () => {
|
||||
const steps = stepsOf(workflowJob(loadWorkflow('.github/workflows/pr-check-on-head-branch.yml'), 'pre-merge'))
|
||||
const merge = steps.findIndex(step => step.id === 'merge')
|
||||
|
||||
@@ -362,8 +362,8 @@ describe('client bundle purity gate', () => {
|
||||
})
|
||||
|
||||
describe('client bundle experimental input isolation', () => {
|
||||
const experimental = '@deepseek-ai/dsh-experimental-client-ui-agent-team'
|
||||
const experimentalDir = packageDir('experimental/client-ui-agent-team')
|
||||
const experimental = '@deepseek-ai/dsh-experimental-client-ui-voice-input'
|
||||
const experimentalDir = packageDir('experimental/client-ui-voice-input')
|
||||
|
||||
function fixture() {
|
||||
const root = mkdtempSync(join(tmpdir(), 'dsh-client-inputs-'))
|
||||
|
||||
@@ -80,7 +80,7 @@ def main():
|
||||
if method == "getCapabilities":
|
||||
assert result["platform"] == "macos", result
|
||||
else:
|
||||
assert result["launchApplication"]["name"] == "DSH Computer Use.app", result.get("launchApplication", {}).get("name")
|
||||
assert result["launchApplication"]["name"] == executable.parents[2].name, result.get("launchApplication", {}).get("name")
|
||||
assert isinstance(result["accessibility"], bool) and isinstance(result["screenRecording"], bool)
|
||||
connection.shutdown(socket.SHUT_WR)
|
||||
assert stream.read() == b"", "helper did not acknowledge connection cleanup"
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
/** Exercise developer signing tooling without reading or mutating real keychains. */
|
||||
import { execFileSync } from 'node:child_process'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import { expect, it } from 'vitest'
|
||||
|
||||
it.skipIf(process.platform === 'win32')('creates, reuses, and removes developer identities outside DSH home', () => {
|
||||
const output = execFileSync('python3', [fileURLToPath(new URL('../.agents/skills/dsh-cua-macos-signing/scripts/test_signing_identity.py', import.meta.url))], {
|
||||
encoding: 'utf8', timeout: 30_000, stdio: ['ignore', 'pipe', 'pipe'],
|
||||
})
|
||||
expect(output).toBe('')
|
||||
})
|
||||
@@ -1,5 +1,21 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { builtDeclarationPath } from './doc-typecheck-paths.ts'
|
||||
import { builtDeclarationPath, isFrozenUpgradeGuidePath } from './doc-typecheck-paths.ts'
|
||||
|
||||
describe('frozen upgrade-guide examples', () => {
|
||||
it('excludes earlier release guides in both languages from current API compilation', () => {
|
||||
expect(isFrozenUpgradeGuidePath('docs/upgrade-guide/v0.2.1-alpha.1/subagent-activations/guide.md', '0.2.1-rc.2')).toBe(true)
|
||||
expect(isFrozenUpgradeGuidePath('docs/upgrade-guide/v0.2.1-alpha.1/subagent-activations/guide.zh.md', '0.2.1-rc.2')).toBe(true)
|
||||
})
|
||||
|
||||
it.each([
|
||||
'docs/upgrade-guide/v0.2.1-rc.2/subagent-activations/guide.md',
|
||||
'docs/upgrade-guide/v0.2.1-rc.3/subagent-activations/guide.md',
|
||||
'docs/upgrade-guide/vinvalid/subagent-activations/guide.md',
|
||||
'docs/subsystems/subagent.md',
|
||||
])('keeps %s subject to current API compilation', (file) => {
|
||||
expect(isFrozenUpgradeGuidePath(file, '0.2.1-rc.2')).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe('builtDeclarationPath', () => {
|
||||
it('maps package source directories and exact entry files to built declarations', () => {
|
||||
|
||||
@@ -1,3 +1,16 @@
|
||||
import { lt, valid } from 'semver'
|
||||
|
||||
/**
|
||||
* Identify frozen upgrade guides whose examples target an earlier release.
|
||||
* @param file - Repository-relative path normalized to forward slashes.
|
||||
* @param currentVersion - Current root package version.
|
||||
* @returns Whether the guide predates the current upgrade-guide directory.
|
||||
*/
|
||||
export function isFrozenUpgradeGuidePath(file: string, currentVersion: string): boolean {
|
||||
const version = /^docs\/upgrade-guide\/v([^/]+)\/[^/]+\/guide(?:\.zh)?\.md$/.exec(file)?.[1]
|
||||
return version !== undefined && valid(version) !== null && lt(version, currentVersion)
|
||||
}
|
||||
|
||||
/** Map one workspace source alias target to its declaration-build target. */
|
||||
export function builtDeclarationPath(candidate: string): string {
|
||||
// Two workspace path forms exist: whole-package entries end in /src, subpath
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
/**
|
||||
* Typecheck Markdown `ts` fences against the workspace API. `ignore-check` fences are reported as
|
||||
* opt-outs; generated catalog fragments and source-equivalence blocks are skipped here because their
|
||||
* owning gates verify them. Byte-identical `.zh.md` copies reuse their unsuffixed sibling's check. A
|
||||
* owning gates verify them. Frozen upgrade guides retain their release-specific examples.
|
||||
* Byte-identical `.zh.md` copies reuse their unsuffixed sibling's check. A
|
||||
* build-coordinated mode consumes existing declarations without emit.
|
||||
*/
|
||||
|
||||
@@ -9,7 +10,7 @@ import { execFileSync } from 'node:child_process'
|
||||
import { globSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs'
|
||||
import { join, relative, resolve } from 'node:path'
|
||||
import ts from 'typescript'
|
||||
import { builtDeclarationPath } from './doc-typecheck-paths.ts'
|
||||
import { builtDeclarationPath, isFrozenUpgradeGuidePath } from './doc-typecheck-paths.ts'
|
||||
import { markdownFences } from './markdown.ts'
|
||||
import { partitionPairedMarkdownDerivatives } from './paired-markdown-derivatives.ts'
|
||||
import { isArchivedAgentNotePath } from './repo-files.ts'
|
||||
@@ -205,9 +206,11 @@ function remapBlockPaths(output: string, blocks: Block[]): string {
|
||||
const markdownGlobs = ['README.md', '.agents/notes/**/*.md', 'docs/**/*.md', 'packages/*/*.md', 'packages/*/*/*.md']
|
||||
|
||||
const files: string[] = []
|
||||
const { version } = JSON.parse(readFileSync(join(root, 'package.json'), 'utf8')) as { version: string }
|
||||
for (const pattern of markdownGlobs) {
|
||||
for (const match of globSync(pattern, { cwd: root })) {
|
||||
if (!isArchivedAgentNotePath(match)) files.push(resolve(root, match))
|
||||
const file = match.replaceAll('\\', '/')
|
||||
if (!isArchivedAgentNotePath(file) && !isFrozenUpgradeGuidePath(file, version)) files.push(resolve(root, file))
|
||||
}
|
||||
}
|
||||
files.sort()
|
||||
|
||||
@@ -2,7 +2,7 @@ import { lstat, mkdir, mkdtemp, readFile, rm, symlink, writeFile } from 'node:fs
|
||||
import { tmpdir } from 'node:os'
|
||||
import { join } from 'node:path'
|
||||
import { expect, it, onTestFinished } from 'vitest'
|
||||
import { restoreLegacyHoists } from './executable-packaging.ts'
|
||||
import { restoreLegacyHoists, verifyStagedDependencies } from './executable-packaging.ts'
|
||||
import { materializeStagedLinks } from './build-exe-for-python-sdk-staging.ts'
|
||||
|
||||
async function fixture() {
|
||||
@@ -45,3 +45,20 @@ it('materializes a package junction without deleting its source and drops linked
|
||||
expect(await readFile(join(test.source, 'peer/index.js'), 'utf8')).toBe('intact')
|
||||
await expect(lstat(join(test.modules, '.bin'))).rejects.toThrow('ENOENT')
|
||||
})
|
||||
|
||||
it('accepts complete direct dependency directories without requiring optional dependencies', async () => {
|
||||
const test = await fixture()
|
||||
await writeFile(join(test.staging, 'package.json'), JSON.stringify({
|
||||
dependencies: { '@scope/present': '1.0.0' }, optionalDependencies: { optional: '1.0.0' },
|
||||
}))
|
||||
await mkdir(join(test.modules, '@scope/present'), { recursive: true })
|
||||
await expect(verifyStagedDependencies(test.staging)).resolves.toBeUndefined()
|
||||
})
|
||||
|
||||
it.each(['absent', 'file'])('rejects a direct dependency that is %s instead of a directory', async (kind) => {
|
||||
const test = await fixture()
|
||||
await writeFile(join(test.staging, 'package.json'), JSON.stringify({ dependencies: { missing: '1.0.0' } }))
|
||||
if (kind === 'file') await writeFile(join(test.modules, 'missing'), '')
|
||||
await expect(verifyStagedDependencies(test.staging))
|
||||
.rejects.toThrow('staged dependencies remain missing: missing.')
|
||||
})
|
||||
@@ -1,5 +1,5 @@
|
||||
/** Shared production-deploy preparation for the SDK and SSH executable carriers. */
|
||||
import { existsSync } from 'node:fs'
|
||||
import { existsSync, statSync } from 'node:fs'
|
||||
import { cp, mkdir, readFile } from 'node:fs/promises'
|
||||
import { dirname, extname, join, resolve, sep } from 'node:path'
|
||||
|
||||
@@ -61,10 +61,22 @@ export async function restoreLegacyHoists(staging: string, sourceNodeModules: st
|
||||
})
|
||||
restored.push(dependency)
|
||||
}
|
||||
const stillMissing = Object.keys(manifest.dependencies ?? {})
|
||||
.filter(dependency => !existsSync(join(staging, 'node_modules', dependency)))
|
||||
if (stillMissing.length > 0) {
|
||||
throw new Error(`executable-packaging: staged dependencies remain missing: ${stillMissing.join(', ')}.`)
|
||||
}
|
||||
await verifyStagedDependencies(staging)
|
||||
return restored
|
||||
}
|
||||
|
||||
/**
|
||||
* Reject a deployed payload whose manifest names missing direct dependency directories.
|
||||
* @param staging - Deployed payload root containing package.json and node_modules.
|
||||
* @returns Resolves when every declared direct dependency has a directory.
|
||||
*/
|
||||
export async function verifyStagedDependencies(staging: string): Promise<void> {
|
||||
const manifest = JSON.parse(await readFile(join(staging, 'package.json'), 'utf8')) as {
|
||||
dependencies?: Record<string, string>
|
||||
}
|
||||
const missing = Object.keys(manifest.dependencies ?? {}).sort()
|
||||
.filter(dependency => !statSync(join(staging, 'node_modules', dependency), { throwIfNoEntry: false })?.isDirectory())
|
||||
if (missing.length > 0) {
|
||||
throw new Error(`executable-packaging: staged dependencies remain missing: ${missing.join(', ')}.`)
|
||||
}
|
||||
}
|
||||
@@ -86,6 +86,51 @@ export function apply(ctx: unknown, config: Config): void {}
|
||||
.toEqual(new Set(['mode', 'headless', 'endpoint']))
|
||||
})
|
||||
|
||||
it.each([false, true])('checks fields spread from an imported object schema (undeclared field: %s)', (invalid) => {
|
||||
const { root, write } = fixture(`
|
||||
import Schema from '@deepseek-ai/schemastery'
|
||||
export interface SharedOptions {
|
||||
/** Maximum result count. */
|
||||
limit: number
|
||||
}
|
||||
export const Shared = Schema.object({ limit: Schema.number(), ${invalid ? 'hidden: Schema.string(),' : ''} })
|
||||
`)
|
||||
write('packages/test/provider/src/index.ts', `
|
||||
import Schema from '@deepseek-ai/schemastery'
|
||||
import { Shared as SharedConfig, type SharedOptions } from '@test/runtime/config'
|
||||
export interface Config extends SharedOptions {
|
||||
/** Signature selected for a source build. */
|
||||
sourceSigning: string
|
||||
}
|
||||
export const Config = Schema.object({ ...SharedConfig.dict, sourceSigning: Schema.string() })
|
||||
export function apply(ctx: unknown, config: Config): void {}
|
||||
`)
|
||||
if (invalid) expect(() => collectConfigCatalog(root)).toThrow("schema validates key 'hidden'")
|
||||
else expect(new Set(collectConfigCatalog(root).find(entry => entry.pkg === '@test/provider')?.schemaKeys))
|
||||
.toEqual(new Set(['limit', 'sourceSigning']))
|
||||
})
|
||||
|
||||
it.each(['SharedConfig.dict', 'unknownFields'])('rejects a spread without an object schema: %s', (spread) => {
|
||||
const { root, write } = fixture()
|
||||
write('packages/test/provider/src/index.ts', `
|
||||
import Schema from '@deepseek-ai/schemastery'
|
||||
import { Shared as SharedConfig, type BrowserConfig } from '@test/runtime/config'
|
||||
export type Config = BrowserConfig
|
||||
export const Config = Schema.object({ ...${spread} })
|
||||
export function apply(ctx: unknown, config: Config): void {}
|
||||
`)
|
||||
expect(() => collectConfigCatalog(root)).toThrow('object schema')
|
||||
})
|
||||
|
||||
it('rejects a recursive object-schema dict spread', () => {
|
||||
const { root } = fixture(`
|
||||
import Schema from '@deepseek-ai/schemastery'
|
||||
export interface BrowserConfig { mode: string }
|
||||
export const Shared = Schema.object({ ...Shared.dict, mode: Schema.string() })
|
||||
`)
|
||||
expect(() => collectConfigCatalog(root)).toThrow('cyclic schema alias')
|
||||
})
|
||||
|
||||
it('rejects a schema field absent from the shared config type', () => {
|
||||
const { root } = fixture(sharedSchema.replace('headless: Schema.boolean()', 'headless: Schema.boolean(), hidden: Schema.string()'))
|
||||
expect(() => collectConfigCatalog(root)).toThrow("schema validates key 'hidden' but config type 'Config' declares no such member")
|
||||
|
||||
@@ -504,7 +504,7 @@ function unwrapExpr(expr: ts.Expression): ts.Expression {
|
||||
* packages whose schemas an intersect composes. A key path is the top-level
|
||||
* key or a nested path through object/array compositions (`agents[].id`).
|
||||
* Handles object/union calls, chained refinements, named const schema imports
|
||||
* through public workspace source paths, and `z.intersect([X.Config, …])`; errors on
|
||||
* through public workspace source paths, object-schema dict spreads, and `z.intersect([X.Config, …])`; errors on
|
||||
* anything else, so a schema the walk cannot see fails the gate instead of
|
||||
* silently thinning it. Nested values that are neither `object` nor `array`
|
||||
* compositions (primitives, unions, dynamic-key dicts) contribute no paths.
|
||||
@@ -570,6 +570,22 @@ function walkSchemaExpr(
|
||||
const key = ts.isStringLiteral(prop.name) ? prop.name.text : prop.name.getText(ctx.sf)
|
||||
keys.push(key)
|
||||
if (ts.isPropertyAssignment(prop)) collectValuePaths(prop.initializer, key)
|
||||
} else if (ts.isSpreadAssignment(prop)) {
|
||||
const spread = unwrapExpr(prop.expression)
|
||||
if (!ts.isPropertyAccessExpression(spread) || spread.name.text !== 'dict' || !ts.isIdentifier(spread.expression)) {
|
||||
violations.push(`${where}: schema spread '${prop.getText(ctx.sf)}' must name a const object schema's dict.`)
|
||||
continue
|
||||
}
|
||||
try {
|
||||
const target = schemaAlias(world, ctx, spread.expression.text)
|
||||
const object = unwrapExpr(target.expr)
|
||||
if (!ts.isCallExpression(object) || !ts.isPropertyAccessExpression(object.expression) || object.expression.name.text !== 'object') {
|
||||
throw new Error(`schema spread '${spread.getText(ctx.sf)}' must refer directly to an object schema`)
|
||||
}
|
||||
visit(spread.expression)
|
||||
} catch (error) {
|
||||
violations.push(`${where}: ${error instanceof Error ? error.message : String(error)}.`)
|
||||
}
|
||||
} else {
|
||||
violations.push(`${where}: schema object property '${prop.getText(ctx.sf)}' is not a plain key.`)
|
||||
}
|
||||
|
||||
@@ -804,18 +804,10 @@ export const LINK_MAP: Readonly<Record<string, string>> = {
|
||||
SpeechCatalog: 'voice-input.md',
|
||||
TranscriptionRequest: 'voice-input.md',
|
||||
CreateTeamTaskRequest: 'agent-team.md',
|
||||
SendTeamMessageRequest: 'agent-team.md',
|
||||
SendTeamMessageResult: 'agent-team.md',
|
||||
SpawnTeammateRequest: 'agent-team.md',
|
||||
SpawnTeammateResult: 'agent-team.md',
|
||||
TeamId: 'agent-team.md',
|
||||
TeamMemberView: 'agent-team.md',
|
||||
TeamMembership: 'agent-team.md',
|
||||
TeamTaskId: 'agent-team.md',
|
||||
TeamTaskView: 'agent-team.md',
|
||||
TeamProjection: 'agent-team.md',
|
||||
TeamMemberProjection: 'agent-team.md',
|
||||
TeamWaitResult: 'agent-team.md',
|
||||
UpdateTeamTaskRequest: 'agent-team.md',
|
||||
ModDefinition: 'claude-code-mods.md',
|
||||
SurfaceSnapshot: 'claude-code-mods.md',
|
||||
|
||||
@@ -845,11 +845,11 @@ const SERVICE_ROLES: ServiceRole[] = [
|
||||
},
|
||||
{
|
||||
key: 'agentTeams',
|
||||
pkg: 'experimental-agent-team',
|
||||
pkg: 'agent-team',
|
||||
title: 'Agent Teams coordination domain',
|
||||
mode: 'core',
|
||||
consumers: ['experimental-tool-agent-team'],
|
||||
note: 'Owns the implicit-root roster, direct peer inbox delivery, shared task DAG, and continuable-child lifecycle; tool-agent-team contributes model controls.',
|
||||
consumers: ['tool-agent-team'],
|
||||
note: 'Owns the shared task DAG for a subagent tree; tool-agent-team contributes task controls.',
|
||||
},
|
||||
{
|
||||
key: 'claudeCodeMods',
|
||||
|
||||
+28
-18
@@ -15,6 +15,7 @@ import AgentRegistry from '@deepseek-ai/dsh-agent'
|
||||
import type { Agent } from '@deepseek-ai/dsh-agent'
|
||||
import { createScope } from '@deepseek-ai/dsh-scope'
|
||||
import SessionStore, { SessionId } from '@deepseek-ai/dsh-session'
|
||||
import SessionPersistence from '@deepseek-ai/dsh-session-persistence'
|
||||
import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection'
|
||||
import SqliteSessionQueryEngine from '@deepseek-ai/dsh-session-query-sqlite'
|
||||
import GoalService from '@deepseek-ai/dsh-goal'
|
||||
@@ -70,8 +71,8 @@ import * as ToolSessionQuery from '@deepseek-ai/dsh-tool-session-query'
|
||||
import * as ToolJobs from '@deepseek-ai/dsh-tool-jobs'
|
||||
import BrowserUseRegistry from '@deepseek-ai/dsh-browser-use'
|
||||
import * as StagehandBrowserTools from '@deepseek-ai/dsh-experimental-browser-use-stagehand-native'
|
||||
import type TeamService from '@deepseek-ai/dsh-experimental-agent-team'
|
||||
import * as ToolTeam from '@deepseek-ai/dsh-experimental-tool-agent-team'
|
||||
import TeamService from '@deepseek-ai/dsh-agent-team'
|
||||
import * as ToolTeam from '@deepseek-ai/dsh-tool-agent-team'
|
||||
import * as ToolTodo from '@deepseek-ai/dsh-tool-todo'
|
||||
import type PluginManager from '@deepseek-ai/dsh-plugin-manager'
|
||||
import * as PluginManagerTools from '@deepseek-ai/dsh-plugin-manager/tools'
|
||||
@@ -123,6 +124,20 @@ class CatalogWorkflowEngine extends WorkflowEngine {
|
||||
}
|
||||
}
|
||||
|
||||
/** Schema collection needs the persistence service but must never access stored Sessions. */
|
||||
class CatalogSessionPersistence extends SessionPersistence {
|
||||
create = unavailableCatalogPersistence
|
||||
open = unavailableCatalogPersistence
|
||||
flush = unavailableCatalogPersistence
|
||||
stat = unavailableCatalogPersistence
|
||||
list = unavailableCatalogPersistence
|
||||
}
|
||||
|
||||
/** Storage operations are unreachable during schema collection. */
|
||||
function unavailableCatalogPersistence(): never {
|
||||
throw new Error('gen-tool-catalog: persistence access is unavailable during schema harvest')
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the descriptor needed to mount schema-producing consumers. Declares
|
||||
* the full capability set of the shipped in-process providers so consumers
|
||||
@@ -577,6 +592,7 @@ const TOOL_PACKAGES: ToolPackage[] = [
|
||||
interrupt_agent: 'packages/subagent/tool-subagent-control/src/index.ts',
|
||||
list_agents: 'packages/subagent/tool-subagent-control/src/list-agents.ts',
|
||||
send_message: 'packages/subagent/tool-subagent-control/src/index.ts',
|
||||
wait_agent: 'packages/subagent/tool-subagent-control/src/wait-agent.ts',
|
||||
},
|
||||
requires: ['ctx.tools', 'ctx.subagents', 'ctx.agents'],
|
||||
writes: ['tool/call', 'tool/result', 'child session events through ctx.subagents'],
|
||||
@@ -588,7 +604,7 @@ const TOOL_PACKAGES: ToolPackage[] = [
|
||||
await ctx.plugin(ToolSubagentControl)
|
||||
},
|
||||
note:
|
||||
'The globally named discovery, interruption, and local-message tools over managed subagents: provider-bound `tool-subagent` instances register distinct delegation tools, while one instance of this package registers `send_message`, `interrupt_agent`, and `list_agents`.',
|
||||
'The shared control tools discover and contact agents in the subagent tree, interrupt owned descendants, and wait for input in the caller’s inbox. Provider-bound `tool-subagent` instances supply delegation tools.',
|
||||
},
|
||||
{
|
||||
pkg: '@deepseek-ai/dsh-tool-jobs',
|
||||
@@ -604,26 +620,19 @@ const TOOL_PACKAGES: ToolPackage[] = [
|
||||
'The kind-agnostic background-job controller: background bash commands and PTY sends are read, listed, and killed through the same three tools. Loading the plugin attaches the controller that arms producers\' `ctx.jobs.start()`.',
|
||||
},
|
||||
{
|
||||
pkg: '@deepseek-ai/dsh-experimental-tool-agent-team',
|
||||
pkg: '@deepseek-ai/dsh-tool-agent-team',
|
||||
dir: 'tool-agent-team',
|
||||
source: 'packages/experimental/tool-agent-team/src/index.ts',
|
||||
requires: ['ctx.tools', 'ctx.systemPrompt', 'ctx.agentTeams', 'an exact live Team member Agent'],
|
||||
writes: ['tool/call', 'team/member', 'team/task', 'agent/inbox/spliced', 'user/message on inbox claim', 'tool/result'],
|
||||
source: 'packages/team/tool-agent-team/src/index.ts',
|
||||
requires: ['ctx.tools', 'ctx.systemPrompt', 'ctx.agentTeams', 'ctx.subagents', 'an exact live Team member Agent'],
|
||||
writes: ['tool/call', 'team/task', 'tool/result'],
|
||||
async mount(ctx) {
|
||||
await ctx.plugin(AgentRegistry)
|
||||
await ctx.plugin(SessionStore)
|
||||
await ctx.plugin(SubagentRuntime)
|
||||
await ctx.plugin(CatalogSessionPersistence)
|
||||
await ctx.plugin(TeamService)
|
||||
const session = ctx.sessions.create(SessionId('tool-catalog-team-lead'))
|
||||
let agent!: Agent
|
||||
const membership = {
|
||||
get root() { return agent },
|
||||
id: session.id,
|
||||
role: 'lead' as const,
|
||||
name: 'lead',
|
||||
}
|
||||
ctx.provide('agentTeams', {
|
||||
tryMembership: (candidate: Agent) => candidate === agent ? membership : undefined,
|
||||
membership: () => membership,
|
||||
} as unknown as TeamService)
|
||||
await ctx.plugin(Object.assign(async (inner: Context) => {
|
||||
agent = {
|
||||
id: session.id,
|
||||
@@ -634,12 +643,13 @@ const TOOL_PACKAGES: ToolPackage[] = [
|
||||
Object.assign(agent, { ctx: createScope(inner, agent).ctx })
|
||||
await inner.agents.register(agent)
|
||||
}, { inject: ['tools', 'systemPrompt', 'agents', 'agentTeams'] }))
|
||||
await ctx.agentTeams.setEnabled(agent, true)
|
||||
await ctx.plugin(ToolTeam)
|
||||
catalogChildScopes.set(ctx, agent)
|
||||
},
|
||||
scope: ctx => catalogChildScopes.get(ctx) as Agent,
|
||||
note:
|
||||
'All nine tools are scoped to implicit Team Leads and durable teammates. The shipped dsh-base bundle keeps the package disabled; the documented Agent Teams profile patch enables it while disabling the legacy continuable-child control names.',
|
||||
'Four task-board tools share the subagent tree’s root Session. Enabling Team mode on the root Session adds task-board guidance alongside standard subagent delegation and control tools. Standard subagent controls provide required identity independently of Team mode.',
|
||||
},
|
||||
{
|
||||
pkg: '@deepseek-ai/dsh-tool-todo',
|
||||
|
||||
@@ -257,9 +257,6 @@
|
||||
"packages/api/session-controller/tests/session-presets.host.spec.ts": {
|
||||
"c051663ae60f381f30367e71bae4da0ea88c490453bbc1d86230b71c0fde8d6f": 1
|
||||
},
|
||||
"packages/api/session-controller/tests/session-remote-rejection.client.spec.ts": {
|
||||
"01c40d09d9157679ffe02d7cce2d1761ffd42d4a569a7176d5eeec838a09edc1": 1
|
||||
},
|
||||
"packages/api/session-controller/tests/session.client.spec.ts": {
|
||||
"52faf4558fcd91bd27dbdfa7e497843e8eeb75ea5f68ee231335018d5b3f2053": 3
|
||||
},
|
||||
@@ -970,19 +967,6 @@
|
||||
"packages/document/office-to-pdf/tests/queue.spec.ts": {
|
||||
"bbe31b89b81048bb47c7158e78670deec5f2973286f9abc591290a8fdd851cf1": 1
|
||||
},
|
||||
"packages/experimental/agent-team/src/journal.ts": {
|
||||
"364930a7ddcc79316a9ffc126602f45ae56a3cecdb80484c5c8a79686f8f1057": 1
|
||||
},
|
||||
"packages/experimental/agent-team/tests/projection-events.spec.ts": {
|
||||
"71ffbcca0bb71d3b595143632491d4c5d580aaf6ac384c22116e038995b2f2b5": 1,
|
||||
"a66c8967969859328890e6049a0bd9e90403861eb19ed5d6937da5505aa79c05": 1,
|
||||
"bd3a42d7bc38a7deb366d7ad1364f2ab01be2af077aae04e49b9c8a61b9e1c7d": 1,
|
||||
"d8076a64f8d940fb0a5c3df15e9e1c8fb7ac05e84762a6198dbbfcffcf379821": 1
|
||||
},
|
||||
"packages/experimental/agent-team/tests/team.spec.ts": {
|
||||
"3ae979a4364fe22772c89133e79ea216cdb4f3163c7cb02c47eb1a5d4788e661": 3,
|
||||
"ae5776af8c1e9965a4eca94bcbc07b65da8d2e9e24e4570d2141b34d60b4f90f": 1
|
||||
},
|
||||
"packages/experimental/browser-use-runtime/src/index.ts": {
|
||||
"04bc8a44533574365dfedf563e2356458bcf95f990a00906938c977c1d683356": 1
|
||||
},
|
||||
@@ -1269,9 +1253,6 @@
|
||||
"744b30e13b6390439c34f4970441a292a07f4ac3d26f25e9be75d1b5c18f4f33": 1,
|
||||
"f33c4a795e2cda267cb28d2937c632d56134b8e6438d21e4bf74da2735b1c154": 1
|
||||
},
|
||||
"packages/interaction/user-questions/tests/user-questions.spec.ts": {
|
||||
"60655aea47d3d8667f90341b58e3faaf64d12670b9592af837aa40ecf7b83583": 1
|
||||
},
|
||||
"packages/jobs/jobs-local/tests/jobs.spec.ts": {
|
||||
"52e6bc7e2a6c58a465a5e1c7a1e92caa4f771235b19ebf4642df5c0845de72aa": 1
|
||||
},
|
||||
@@ -1759,9 +1740,6 @@
|
||||
"3e704e331ed2fc5d4c0855e2455b31b82924c525f2e267416e393cdbd36c251b": 1,
|
||||
"fdc5fcade0ba1abbc426ccccb07a1ad4998253799b8561815047d95178441cdf": 1
|
||||
},
|
||||
"packages/subagent/subagent/src/internal.ts": {
|
||||
"89b33a57a7c77261a02116dd015c08d9e6a5b1efb5ba0ebf49bc68a21e7f120a": 2
|
||||
},
|
||||
"packages/subagent/subagent/src/projection.ts": {
|
||||
"0cd01f87cd1ae24b558cfe5427d45cc016b499579e5ddd3eab125517339b7c71": 1
|
||||
},
|
||||
@@ -1777,10 +1755,6 @@
|
||||
"packages/subagent/subagent/tests/continuation.spec.ts": {
|
||||
"ae5776af8c1e9965a4eca94bcbc07b65da8d2e9e24e4570d2141b34d60b4f90f": 1
|
||||
},
|
||||
"packages/subagent/subagent/tests/control.spec.ts": {
|
||||
"52faf4558fcd91bd27dbdfa7e497843e8eeb75ea5f68ee231335018d5b3f2053": 1,
|
||||
"764cefa4ddc93e5b865cade70e780d8d471ef51e58148e838681ed4cf0bd7b4a": 1
|
||||
},
|
||||
"packages/subagent/subagent/tests/service.spec.ts": {
|
||||
"41a1501a36d265ae52c5893b26e9e92eacc1012a379afb2948a31d153bcbac2a": 1,
|
||||
"514d368a1007506da97412cee4b14191025685d1693f2c68442bb466b5054e02": 1,
|
||||
@@ -1848,6 +1822,10 @@
|
||||
"574bce326cab70f3080b19219844405565d1c4c23df4eec85e7838f9ec1038ed": 1,
|
||||
"f5b7ed19aea21153c86ef4e2596f39d1b667b3b8a1d1ba2b2a2a95bf90869b58": 1
|
||||
},
|
||||
"packages/team/agent-team/tests/projection-events.spec.ts": {
|
||||
"71ffbcca0bb71d3b595143632491d4c5d580aaf6ac384c22116e038995b2f2b5": 1,
|
||||
"a66c8967969859328890e6049a0bd9e90403861eb19ed5d6937da5505aa79c05": 1
|
||||
},
|
||||
"packages/terminal/terminal-bash/tests/index.spec.ts": {
|
||||
"2878860b606c2614696f97227e4e580a65b1ae1680b6261f233163cd8b838ee0": 1,
|
||||
"30c8c859832405858e95a7de1493488b75a9030e3baa8fcaf3647e071bd5bbf1": 1,
|
||||
@@ -1920,11 +1898,6 @@
|
||||
"da6ccb5610150a9169b5c3949017d7f5e0d60eadd999615e231db00470aa00e7": 1,
|
||||
"f772961d38eac5032c547a0485c4b941da2faa044c428f788934798230fc389a": 1
|
||||
},
|
||||
"packages/test-support/loader-smoke/tests/agent-turn.spec.ts": {
|
||||
"432811d8f7105d0bf1934d2b52456aeb8f202b334486810dc1b6c3d87349b5b1": 1,
|
||||
"efbd107cd9aad870467dc06957b0d0298798245cf063c9f8e2a8b46c10cb3189": 1,
|
||||
"effb170ab353dc903b037ff2e208a85ba87ff1da08f40306f2e292f78609c6c9": 1
|
||||
},
|
||||
"packages/test-support/session-snapshot/src/harness.ts": {
|
||||
"04bc8a44533574365dfedf563e2356458bcf95f990a00906938c977c1d683356": 1
|
||||
},
|
||||
@@ -2013,7 +1986,6 @@
|
||||
"f30cc8d63e02fd6608c2c80f9564bfc17f171581770ba2df4b1a62a8869a3105": 1
|
||||
},
|
||||
"scripts/gen-tool-catalog.ts": {
|
||||
"86969c58d27d2ec65c0607b449bbb5ab4644717f0627729a5a193937d51f419e": 1,
|
||||
"ebe146aded1e6f3e71f166a7f88720e560b336bbb92d19cea0b2bdab58c46f8a": 1
|
||||
},
|
||||
"scripts/persistence-changes.ts": {
|
||||
|
||||
@@ -23,8 +23,8 @@ describe('npm baseline package discovery', () => {
|
||||
const expected = [
|
||||
'apps/cli/package.json',
|
||||
'packages/core/session/package.json',
|
||||
'packages/experimental/agent-team/package.json',
|
||||
'packages/experimental/new-prototype/package.json',
|
||||
'packages/team/agent-team/package.json',
|
||||
'vendor/cordis/package.json',
|
||||
]
|
||||
const root = fixture([
|
||||
|
||||
@@ -15,7 +15,8 @@ function property(nodes: readonly SchemaNode[], index: number, name: string): nu
|
||||
return field.type
|
||||
}
|
||||
|
||||
it('requires compatibility review before request headers can carry retired system text', { timeout: 60_000 }, () => {
|
||||
// Whole-repository schema extraction needs the same budget as persistence-schema.spec.ts and Windows coverage.
|
||||
it('requires compatibility review before request headers can carry retired system text', { timeout: 90_000 }, () => {
|
||||
const inventory = extractPersistenceSchema(resolve(import.meta.dirname, '..'))
|
||||
const before = inventory.roots.find(root => root.key === 'event:request/header')
|
||||
if (before === undefined) throw new Error('generated schema omits request/header')
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
/** Workspace settings relevant to exact local build approvals. */
|
||||
export interface RuntimeDeployConfig {
|
||||
readonly allowBuilds?: Readonly<Record<string, boolean | string>>
|
||||
readonly [setting: string]: unknown
|
||||
}
|
||||
|
||||
/** Pnpm configuration hooks scoped to Python runtime deployment. */
|
||||
export const hooks: {
|
||||
/**
|
||||
* Translate local build identities without changing their approval status.
|
||||
* @param config - Source workspace configuration.
|
||||
* @returns Deployment configuration with exact file URL approvals.
|
||||
*/
|
||||
updateConfig(config: RuntimeDeployConfig): RuntimeDeployConfig
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
* Preserve workspace build approvals when pnpm deploy records local dependencies as file:// URLs.
|
||||
* This hook lives in the workspace root's scripts/ directory; relative file: approvals use that root.
|
||||
*/
|
||||
import { resolve } from 'node:path'
|
||||
import { pathToFileURL } from 'node:url'
|
||||
|
||||
/**
|
||||
* Translate local build identities without changing their approved or denied status.
|
||||
* @param {import('./python-runtime-deploy.pnpmfile.mjs').RuntimeDeployConfig} config - Source workspace configuration.
|
||||
* @returns {import('./python-runtime-deploy.pnpmfile.mjs').RuntimeDeployConfig} Deployment configuration with exact file URL approvals.
|
||||
*/
|
||||
function updateConfig(config) {
|
||||
if (config.allowBuilds === undefined) return config
|
||||
const allowBuilds = Object.fromEntries(Object.entries(config.allowBuilds).map(([key, value]) => {
|
||||
const marker = key.indexOf('@file:')
|
||||
if (marker < 0 || key.slice(marker + 6).startsWith('//')) return [key, value]
|
||||
const source = resolve(import.meta.dirname, '..', key.slice(marker + 6))
|
||||
return [`${key.slice(0, marker + 1)}${pathToFileURL(source).href}`, value]
|
||||
}))
|
||||
return { ...config, allowBuilds }
|
||||
}
|
||||
|
||||
/** Pnpm configuration hooks scoped to Python runtime deployment. */
|
||||
export const hooks = { updateConfig }
|
||||
@@ -0,0 +1,35 @@
|
||||
import { resolve } from 'node:path'
|
||||
import { pathToFileURL } from 'node:url'
|
||||
import { expect, it } from 'vitest'
|
||||
import { hooks } from './python-runtime-deploy.pnpmfile.mjs'
|
||||
|
||||
it('preserves exact local build approvals, denials, and unrelated configuration', () => {
|
||||
const config = {
|
||||
strictDepBuilds: true,
|
||||
allowBuilds: {
|
||||
'allowed@file:packages/allowed': true,
|
||||
'denied@file:packages/denied': false,
|
||||
'pending@file:packages/pending': 'set this to true or false',
|
||||
'absolute@file:///already/reviewed': true,
|
||||
'registry-package': false,
|
||||
},
|
||||
}
|
||||
const identity = (name: string) => `${name}@${pathToFileURL(resolve(import.meta.dirname, '..', 'packages', name)).href}`
|
||||
|
||||
expect(hooks.updateConfig(config)).toEqual({
|
||||
strictDepBuilds: true,
|
||||
allowBuilds: {
|
||||
[identity('allowed')]: true,
|
||||
[identity('denied')]: false,
|
||||
[identity('pending')]: 'set this to true or false',
|
||||
'absolute@file:///already/reviewed': true,
|
||||
'registry-package': false,
|
||||
},
|
||||
})
|
||||
expect(config.allowBuilds['allowed@file:packages/allowed']).toBe(true)
|
||||
})
|
||||
|
||||
it('leaves configuration without local build rules unchanged', () => {
|
||||
const config = { strictDepBuilds: true }
|
||||
expect(hooks.updateConfig(config)).toBe(config)
|
||||
})
|
||||
@@ -79,7 +79,7 @@ describe('advanced Python snapshot workflow ordering', () => {
|
||||
const parent = await ctx.agentLoop.create(SessionId(config.parentSessionId), { provider: 'mock', model: 'mock' })
|
||||
const run = await ctx.subagents.startActivation({
|
||||
provider: 'spawn', label: 'workflow-child', delivery: 'caller',
|
||||
request: { parent, prompt: [{ type: 'text', text: config.prompt }] }, signal: new AbortController().signal,
|
||||
parent, prompt: [{ type: 'text', text: config.prompt }], signal: new AbortController().signal,
|
||||
})
|
||||
cleanups.push(() => run.dispose())
|
||||
const child = await entered.promise
|
||||
|
||||
@@ -50,8 +50,6 @@ describe('release families', () => {
|
||||
.filter(member => member.directory.startsWith('packages/experimental/'))
|
||||
.map(member => member.name)).toEqual([
|
||||
'@deepseek-ai/dsh-action-cards',
|
||||
'@deepseek-ai/dsh-experimental-agent-team-profile',
|
||||
'@deepseek-ai/dsh-experimental-agent-team',
|
||||
'@deepseek-ai/dsh-experimental-api-speech-to-text',
|
||||
'@deepseek-ai/dsh-experimental-auto-review',
|
||||
'@deepseek-ai/dsh-experimental-badge-skill-bundle',
|
||||
@@ -61,7 +59,6 @@ describe('release families', () => {
|
||||
'@deepseek-ai/dsh-experimental-browser-use-stagehand-native',
|
||||
'@deepseek-ai/dsh-experimental-claude-code-mods',
|
||||
'@deepseek-ai/dsh-client-ui-action-cards',
|
||||
'@deepseek-ai/dsh-experimental-client-ui-agent-team',
|
||||
'@deepseek-ai/dsh-experimental-client-ui-claude-code-mods',
|
||||
'@deepseek-ai/dsh-experimental-client-ui-cot-translation',
|
||||
'@deepseek-ai/dsh-client-ui-creation-recommendations',
|
||||
@@ -101,7 +98,6 @@ describe('release families', () => {
|
||||
'@deepseek-ai/dsh-experimental-speech-to-text',
|
||||
'@deepseek-ai/dsh-experimental-subagent-oar',
|
||||
'@deepseek-ai/dsh-experimental-terminal-bundle',
|
||||
'@deepseek-ai/dsh-experimental-tool-agent-team',
|
||||
'@deepseek-ai/dsh-tool-ralph',
|
||||
'@deepseek-ai/dsh-tool-session-query',
|
||||
'@deepseek-ai/dsh-experimental-tool-subagent-oar',
|
||||
|
||||
@@ -1007,7 +1007,7 @@ describe('Node 24 lane ownership', () => {
|
||||
'packages/subprocess/subprocess-local/tests/spawn-runner-built.e2e.ts',
|
||||
'packages/subagent/subagent-codex/tests/loader-composition.e2e.ts',
|
||||
'packages/subagent/subagent-claude-code/tests/loader-composition.e2e.ts',
|
||||
'packages/experimental/agent-team/tests/built-lib.e2e.ts',
|
||||
'packages/team/agent-team/tests/built-lib.e2e.ts',
|
||||
]),
|
||||
)
|
||||
expect(subject.find(item => item.id === 'web-snapshot')).toMatchObject({
|
||||
|
||||
@@ -918,7 +918,7 @@ function builtBinSmokeGate(needs: string[] = ['build']): Gate {
|
||||
'packages/subagent/subagent-codex/tests/loader-composition.e2e.ts',
|
||||
'packages/subagent/subagent-claude-code/tests/loader-composition.e2e.ts',
|
||||
'packages/api/remotes/tests/built-lib.e2e.ts',
|
||||
'packages/experimental/agent-team/tests/built-lib.e2e.ts',
|
||||
'packages/team/agent-team/tests/built-lib.e2e.ts',
|
||||
// Built execution consumers: the only automated proof that package-name
|
||||
// imports reach their lib/ entrypoints under plain Node. The e2e lane runs
|
||||
// unbuilt, so these files self-skip there.
|
||||
|
||||
@@ -1842,6 +1842,11 @@ def smoke_sdk_snapshot_mode(
|
||||
expected_children = 2 if mode == "native" else 0
|
||||
if methods.count("subagent.started") != expected_children or methods.count("subagent.finished") != expected_children:
|
||||
raise AssertionError(f"{mode} advanced snapshot emitted unexpected subagent lifecycle: {methods}")
|
||||
creators = {item.payload["childSessionId"]: item.payload["parentSessionId"]
|
||||
for item in result.notifications if item.method == "subagent.started"}
|
||||
for item in result.notifications:
|
||||
if item.method == "subagent.finished":
|
||||
assert item.payload["parentSessionId"] == creators[item.payload["childSessionId"]]
|
||||
ptc_events = [event for event in result.events
|
||||
if event.get("type") in ("tool/ptc-dispatch-start", "tool/ptc-dispatch")]
|
||||
expected_ptc_calls = 2 if mode == "ptc" else 0
|
||||
|
||||
@@ -919,6 +919,10 @@
|
||||
],
|
||||
"isError": false,
|
||||
"id": "{{messageId}}"
|
||||
},
|
||||
"meta": {
|
||||
"kind": "activation",
|
||||
"subagentId": "{{child-1}}"
|
||||
}
|
||||
},
|
||||
"sourceEventSeqs": [
|
||||
@@ -981,6 +985,7 @@
|
||||
"kind": "subagent-settled",
|
||||
"form": "notice",
|
||||
"summary": "Background subagent {{child-1}} finished and will do no further work unless you send it more.",
|
||||
"stopReason": "completed",
|
||||
"senderSessionId": "{{child-1}}"
|
||||
},
|
||||
"role": "user",
|
||||
@@ -1258,6 +1263,7 @@
|
||||
"kind": "subagent-settled",
|
||||
"form": "notice",
|
||||
"summary": "Background subagent {{child-1}} finished and will do no further work unless you send it more.",
|
||||
"stopReason": "completed",
|
||||
"senderSessionId": "{{child-1}}"
|
||||
},
|
||||
"role": "user",
|
||||
@@ -2691,6 +2697,10 @@
|
||||
{
|
||||
"type": "text",
|
||||
"text": "Reply with exactly DIRECT_CHILD_OK and nothing else."
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"text": "Your final answer is delivered automatically if your parent is still online. Include the result and relevant evidence in that answer; the parent does not receive your full transcript, tool output, or reasoning. If your last action is a message to your parent, the completion notice omits your final answer; include the result and relevant evidence in that message."
|
||||
}
|
||||
],
|
||||
"source": {
|
||||
@@ -2803,6 +2813,10 @@
|
||||
],
|
||||
"isError": false,
|
||||
"id": "{{messageId}}"
|
||||
},
|
||||
"meta": {
|
||||
"kind": "activation",
|
||||
"subagentId": "{{child-1}}"
|
||||
}
|
||||
},
|
||||
"sourceEventSeqs": [
|
||||
@@ -2940,6 +2954,10 @@
|
||||
{
|
||||
"type": "text",
|
||||
"text": "Reply with exactly DIRECT_CHILD_OK and nothing else."
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"text": "Your final answer is delivered automatically if your parent is still online. Include the result and relevant evidence in that answer; the parent does not receive your full transcript, tool output, or reasoning. If your last action is a message to your parent, the completion notice omits your final answer; include the result and relevant evidence in that message."
|
||||
}
|
||||
],
|
||||
"source": {
|
||||
@@ -3272,6 +3290,7 @@
|
||||
"kind": "subagent-settled",
|
||||
"form": "notice",
|
||||
"summary": "Background subagent {{child-1}} finished and will do no further work unless you send it more.",
|
||||
"stopReason": "completed",
|
||||
"senderSessionId": "{{child-1}}"
|
||||
},
|
||||
"role": "user",
|
||||
@@ -4224,6 +4243,7 @@
|
||||
"kind": "subagent-settled",
|
||||
"form": "notice",
|
||||
"summary": "Background subagent {{child-1}} finished and will do no further work unless you send it more.",
|
||||
"stopReason": "completed",
|
||||
"senderSessionId": "{{child-1}}"
|
||||
},
|
||||
"role": "user",
|
||||
|
||||
@@ -4,14 +4,14 @@
|
||||
{"type":"approval/policy","data":{"policy":"never","source":"delegation"}}
|
||||
{"type":"permission/preset","data":{"preset":"danger-full-access"}}
|
||||
{"type":"agent/inbox/spliced","data":{"target":"next-step","start":0,"inserted":[{"id":"{{messageId}}","role":"user","content":[{"type":"text","text":"Serial agent creation completed."}],"source":{"kind":"plugin:serial-created-fixture"}}]}}
|
||||
{"type":"agent/inbox/spliced","data":{"target":"next-turn","start":0,"inserted":[{"content":[{"type":"text","text":"Reply with exactly DIRECT_CHILD_OK and nothing else."}],"source":{"kind":"user"},"role":"user","id":"{{messageId}}"}]}}
|
||||
{"type":"agent/inbox/spliced","data":{"target":"next-turn","start":0,"inserted":[{"content":[{"type":"text","text":"Reply with exactly DIRECT_CHILD_OK and nothing else."},{"type":"text","text":"Your final answer is delivered automatically if your parent is still online. Include the result and relevant evidence in that answer; the parent does not receive your full transcript, tool output, or reasoning. If your last action is a message to your parent, the completion notice omits your final answer; include the result and relevant evidence in that message."}],"source":{"kind":"user"},"role":"user","id":"{{messageId}}"}]}}
|
||||
{"type":"turn/start","data":{"turn":1}}
|
||||
{"type":"agent/inbox/spliced","data":{"target":"next-step","start":0,"removedCount":1,"inserted":[]}}
|
||||
{"type":"agent/inbox/spliced","data":{"target":"next-turn","start":0,"removedCount":1,"inserted":[]}}
|
||||
{"type":"step/start","data":{"turn":1,"step":1}}
|
||||
{"type":"system/message","data":{"turn":1,"step":1,"message":{"role":"system","content":[{"type":"text","text":"{{system}}"}],"source":{"kind":"system-prompt"},"id":"{{messageId}}"}},"surfaceOp":"append"}
|
||||
{"type":"user/message","data":{"id":"{{messageId}}","role":"user","content":[{"type":"text","text":"Serial agent creation completed."}],"source":{"kind":"plugin:serial-created-fixture"}},"surfaceOp":"append"}
|
||||
{"type":"user/message","data":{"content":[{"type":"text","text":"Reply with exactly DIRECT_CHILD_OK and nothing else."}],"source":{"kind":"user"},"role":"user","id":"{{messageId}}"},"surfaceOp":"append"}
|
||||
{"type":"user/message","data":{"content":[{"type":"text","text":"Reply with exactly DIRECT_CHILD_OK and nothing else."},{"type":"text","text":"Your final answer is delivered automatically if your parent is still online. Include the result and relevant evidence in that answer; the parent does not receive your full transcript, tool output, or reasoning. If your last action is a message to your parent, the completion notice omits your final answer; include the result and relevant evidence in that message."}],"source":{"kind":"user"},"role":"user","id":"{{messageId}}"},"surfaceOp":"append"}
|
||||
{"type":"user/message","data":{"content":[{"type":"text","text":"Current runtime context. This snapshot supersedes earlier runtime-context snapshots.\n\nCurrent working directory: \"{{cwd}}\".\n\nCurrent DSH file policy: danger-full-access. The DSH file sandbox does not restrict file modifications by available operations.\n\nApproval prompts are disabled in this session: actions that require approval are rejected automatically — do not request sandbox escalation (do not set `sandbox_permissions`).\n\nDelegation depth for `subagent`: current 1, maximum 1, remaining levels 0. Do not call `subagent` at this depth; carry out the work directly.\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":"runtime-context","form":"snapshot","sections":[{"name":"working-directory:current","text":"Current working directory: \"{{cwd}}\"."},{"name":"sandbox:policy","text":"Current DSH file policy: danger-full-access. The DSH file sandbox does not restrict file modifications by available operations."},{"name":"approval:policy","text":"Approval prompts are disabled in this session: actions that require approval are rejected automatically — do not request sandbox escalation (do not set `sandbox_permissions`)."},{"name":"tool:subagent:depth","text":"Delegation depth for `subagent`: current 1, maximum 1, remaining levels 0. Do not call `subagent` at this depth; carry out the work directly."},{"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":"request/header","data":{"header":{"config":{"provider":"deepseek-official","model":"smoke-model","reasoningEffort":"high","maxTokens":256000},"adapterDefaults":{"maxTokens":true},"tools":["cordis_inspect_list","cordis_inspect_query","job_kill","job_list","job_output","snapshot_double","subagent","workflow","working_directory"]},"reason":"initial"}}
|
||||
{"type":"request/context","data":{"provider":"deepseek-official","model":"smoke-model","contextWindow":1000000}}
|
||||
|
||||
@@ -37,11 +37,11 @@
|
||||
{"type":"assistant/message","data":{"turn":1,"step":3,"message":{"role":"assistant","content":[{"type":"tool-call","id":"advanced-direct-child","name":"subagent","arguments":"{\"description\": \"Check direct child\", \"prompt\": \"Reply with exactly DIRECT_CHILD_OK and nothing else.\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"smoke-model","replayState":{"response":{"kind":"deepseek-messages","version":1,"model":"smoke-model"},"blocks":[{"type":"tool-call"}]}},"id":"{{messageId}}"},"usage":{"inputTokens":3,"outputTokens":3,"totalTokens":6},"stream":[{"type":"chunk","time":0,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}},{"type":"tool-call-chunks","time0":0,"index":0,"dt":[],"id":"advanced-direct-child","name":"subagent","args":[""]},{"type":"tool-call-chunks","time0":0,"index":0,"dt":[],"id":"advanced-direct-child","args":["{\"description\": \"Check direct child\", \"prompt\": \"Reply with exactly DIRECT_CHILD_OK and nothing else.\"}"]},{"type":"chunk","time":0,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"advanced-direct-child","name":"subagent","arguments":"{\"description\": \"Check direct child\", \"prompt\": \"Reply with exactly DIRECT_CHILD_OK and nothing else.\"}"}}},{"type":"chunk","time":0,"chunk":{"type":"usage","usage":{"inputTokens":3,"outputTokens":3,"totalTokens":6}}},{"type":"chunk","time":0,"chunk":{"type":"finish","reason":{"kind":"tool-calls"},"replayState":{"response":{"kind":"deepseek-messages","version":1,"model":"smoke-model"},"blocks":[{"type":"tool-call"}]}}}]},"surfaceOp":"append"}
|
||||
{"type":"tool/call","data":{"turn":1,"step":3,"callId":"advanced-direct-child","name":"subagent","arguments":"{\"description\": \"Check direct child\", \"prompt\": \"Reply with exactly DIRECT_CHILD_OK and nothing else.\"}"}}
|
||||
{"type":"subagent/catalog","data":{"version":0,"childId":"{{child-1}}","childCreatedAt":0,"mode":"continuable","label":"Check direct child"}}
|
||||
{"type":"tool/result","data":{"turn":1,"step":3,"message":{"role":"tool","source":{"kind":"tool","callId":"advanced-direct-child"},"toolCallId":"advanced-direct-child","content":[{"type":"text","text":"started subagent {{child-1}}"}],"isError":false,"id":"{{messageId}}"}},"sourceEventSeqs":[36],"surfaceOp":"append"}
|
||||
{"type":"tool/result","data":{"turn":1,"step":3,"message":{"role":"tool","source":{"kind":"tool","callId":"advanced-direct-child"},"toolCallId":"advanced-direct-child","content":[{"type":"text","text":"started subagent {{child-1}}"}],"isError":false,"id":"{{messageId}}"},"meta":{"kind":"activation","subagentId":"{{child-1}}"}},"sourceEventSeqs":[36],"surfaceOp":"append"}
|
||||
{"type":"step/end","data":{"turn":1,"step":3}}
|
||||
{"type":"step/start","data":{"turn":1,"step":4}}
|
||||
{"type":"session-log-deepseek/delivery-accepted","data":{"sessionId":"{{parent}}","sessionFormatVersion":4,"throughSeq":40}}
|
||||
{"type":"agent/inbox/spliced","data":{"target":"next-step","start":0,"inserted":[{"content":[{"type":"text","text":"Background subagent {{child-1}} finished and will do no further work unless you send it more."},{"type":"text","text":"Its closing message:"},{"type":"text","text":"DIRECT_CHILD_OK"}],"source":{"kind":"subagent-settled","form":"notice","summary":"Background subagent {{child-1}} finished and will do no further work unless you send it more.","senderSessionId":"{{child-1}}"},"role":"user","id":"{{messageId}}"}]}}
|
||||
{"type":"agent/inbox/spliced","data":{"target":"next-step","start":0,"inserted":[{"content":[{"type":"text","text":"Background subagent {{child-1}} finished and will do no further work unless you send it more."},{"type":"text","text":"Its closing message:"},{"type":"text","text":"DIRECT_CHILD_OK"}],"source":{"kind":"subagent-settled","form":"notice","summary":"Background subagent {{child-1}} finished and will do no further work unless you send it more.","stopReason":"completed","senderSessionId":"{{child-1}}"},"role":"user","id":"{{messageId}}"}]}}
|
||||
{"type":"assistant/message","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","replayState":{"response":{"kind":"deepseek-messages","version":1,"model":"smoke-model"},"blocks":[{"type":"tool-call"}]}},"id":"{{messageId}}"},"usage":{"inputTokens":3,"outputTokens":3,"totalTokens":6},"stream":[{"type":"chunk","time":0,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}},{"type":"tool-call-chunks","time0":0,"index":0,"dt":[],"id":"advanced-workflow","name":"workflow","args":[""]},{"type":"tool-call-chunks","time0":0,"index":0,"dt":[],"id":"advanced-workflow","args":["{\"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":"chunk","time":0,"chunk":{"type":"block-end","index":0,"block":{"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\"}}"}}},{"type":"chunk","time":0,"chunk":{"type":"usage","usage":{"inputTokens":3,"outputTokens":3,"totalTokens":6}}},{"type":"chunk","time":0,"chunk":{"type":"finish","reason":{"kind":"tool-calls"},"replayState":{"response":{"kind":"deepseek-messages","version":1,"model":"smoke-model"},"blocks":[{"type":"tool-call"}]}}}]},"surfaceOp":"append"}
|
||||
{"type":"tool/call","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-workflow/run-start","data":{"runId":"{{workflow-run}}","name":"advanced-exe-snapshot"}}
|
||||
@@ -53,7 +53,7 @@
|
||||
{"type":"step/end","data":{"turn":1,"step":4}}
|
||||
{"type":"agent/inbox/spliced","data":{"target":"next-step","start":0,"removedCount":1,"inserted":[]}}
|
||||
{"type":"step/start","data":{"turn":1,"step":5}}
|
||||
{"type":"user/message","data":{"content":[{"type":"text","text":"Background subagent {{child-1}} finished and will do no further work unless you send it more."},{"type":"text","text":"Its closing message:"},{"type":"text","text":"DIRECT_CHILD_OK"}],"source":{"kind":"subagent-settled","form":"notice","summary":"Background subagent {{child-1}} finished and will do no further work unless you send it more.","senderSessionId":"{{child-1}}"},"role":"user","id":"{{messageId}}"},"surfaceOp":"append"}
|
||||
{"type":"user/message","data":{"content":[{"type":"text","text":"Background subagent {{child-1}} finished and will do no further work unless you send it more."},{"type":"text","text":"Its closing message:"},{"type":"text","text":"DIRECT_CHILD_OK"}],"source":{"kind":"subagent-settled","form":"notice","summary":"Background subagent {{child-1}} finished and will do no further work unless you send it more.","stopReason":"completed","senderSessionId":"{{child-1}}"},"role":"user","id":"{{messageId}}"},"surfaceOp":"append"}
|
||||
{"type":"session-log-deepseek/delivery-accepted","data":{"sessionId":"{{parent}}","sessionFormatVersion":4,"throughSeq":54}}
|
||||
{"type":"assistant/message","data":{"turn":1,"step":5,"message":{"role":"assistant","content":[{"type":"text","text":"ADVANCED_EXECUTABLE_OK"}],"source":{"kind":"model","provider":"deepseek-official","model":"smoke-model","replayState":{"response":{"kind":"deepseek-messages","version":1,"model":"smoke-model"},"blocks":[{"type":"text"}]}},"id":"{{message:1}}"},"usage":{"inputTokens":3,"outputTokens":3,"totalTokens":6},"stream":[{"type":"chunk","time":0,"chunk":{"type":"block-start","index":0,"blockType":"text"}},{"type":"text-chunks","time0":0,"index":0,"dt":[],"texts":["ADVANCED_EXECUTABLE_OK"]},{"type":"chunk","time":0,"chunk":{"type":"block-end","index":0,"block":{"type":"text","text":"ADVANCED_EXECUTABLE_OK"}}},{"type":"chunk","time":0,"chunk":{"type":"usage","usage":{"inputTokens":3,"outputTokens":3,"totalTokens":6}}},{"type":"chunk","time":0,"chunk":{"type":"finish","reason":{"kind":"stop"},"replayState":{"response":{"kind":"deepseek-messages","version":1,"model":"smoke-model"},"blocks":[{"type":"text"}]}}}]},"surfaceOp":"append"}
|
||||
{"type":"step/end","data":{"turn":1,"step":5}}
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
}
|
||||
},
|
||||
"catalogs": [
|
||||
"Query the packaged Python environment for Office authoring.Current runtime context. This snapshot supersedes earlier runtime-context snapshots.\n\nCurrent working directory: \"{{cwd}}\".\n\nCurrent DSH file policy: danger-full-access. The DSH file sandbox does not restrict file modifications by available operations.\n\nApproval prompts are disabled in this session: actions that require approval are rejected automatically — do not request sandbox escalation (do not set `sandbox_permissions`).<system-reminder>\nA skill is a reusable set of task-specific instructions. The following skills are available in this session:\n\n<available_skills>\n- `office`: Create, edit, inspect, or convert Office documents, spreadsheets, presentations, and PDFs. Load to choose the relevant workflow.\n- `office-docx`: Create, read, edit, and validate Word documents (.docx).\n- `office-export-pdf`: Convert an Office document to a requested PDF with LibreOffice Kit.\n- `office-pptx`: Create, read, edit, and validate PowerPoint presentations (.pptx).\n- `office-recalculate`: Refresh spreadsheet formula caches and verify calculated values with LibreOffice Kit.\n- `office-render`: Render requested document images or inspect visual layout with LibreOffice Kit.\n- `office-xlsx`: Read, create, edit, and validate Excel workbooks, data, and formulas (.xlsx).\n</available_skills>\n\nIf the user names a skill, or the task clearly matches a skill's description, call the `skill` tool with the exact skill name before taking task actions. Load all applicable skills, then follow their full instructions. This catalog contains summaries only; do not infer or follow a skill's instructions until it has been loaded.\nA user may also invoke a skill directly; its <skill_content> block then appears in this conversation. Follow it, and do not call the `skill` tool again for that skill.\n</system-reminder>Time sampled while preparing turn 1, step 1: 2026-10-16T00:00:00+00:00[UTC]\nBrowser time zone for this request: unavailable. Ask the user to clarify otherwise-unqualified dates and times.\nElapsed since the preceding model-visible message: unavailable."
|
||||
"Query the packaged Python environment for Office authoring.Current runtime context. This snapshot supersedes earlier runtime-context snapshots.\n\nCurrent working directory: \"{{cwd}}\".\n\nCurrent DSH file policy: danger-full-access. The DSH file sandbox does not restrict file modifications by available operations.\n\nApproval prompts are disabled in this session: actions that require approval are rejected automatically — do not request sandbox escalation (do not set `sandbox_permissions`).\n\nYou are the root agent. Your agent id is \"authoring\".\n\nTo message other agents, use send_message({ agent_id: \"<agent-id>\", message: \"...\" }).\nUse list_agents({}) to find your collaborator agents and their ids.<system-reminder>\nA skill is a reusable set of task-specific instructions. The following skills are available in this session:\n\n<available_skills>\n- `office`: Create, edit, inspect, or convert Office documents, spreadsheets, presentations, and PDFs. Load to choose the relevant workflow.\n- `office-docx`: Create, read, edit, and validate Word documents (.docx).\n- `office-export-pdf`: Convert an Office document to a requested PDF with LibreOffice Kit.\n- `office-pptx`: Create, read, edit, and validate PowerPoint presentations (.pptx).\n- `office-recalculate`: Refresh spreadsheet formula caches and verify calculated values with LibreOffice Kit.\n- `office-render`: Render requested document images or inspect visual layout with LibreOffice Kit.\n- `office-xlsx`: Read, create, edit, and validate Excel workbooks, data, and formulas (.xlsx).\n</available_skills>\n\nIf the user names a skill, or the task clearly matches a skill's description, call the `skill` tool with the exact skill name before taking task actions. Load all applicable skills, then follow their full instructions. This catalog contains summaries only; do not infer or follow a skill's instructions until it has been loaded.\nA user may also invoke a skill directly; its <skill_content> block then appears in this conversation. Follow it, and do not call the `skill` tool again for that skill.\n</system-reminder>Time sampled while preparing turn 1, step 1: 2026-10-16T00:00:00+00:00[UTC]\nBrowser time zone for this request: unavailable. Ask the user to clarify otherwise-unqualified dates and times.\nElapsed since the preceding model-visible message: unavailable."
|
||||
],
|
||||
"result": {
|
||||
"python": "{{python}}",
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
}
|
||||
},
|
||||
"catalogs": [
|
||||
"Query the packaged Python environment for Office authoring.Current runtime context. This snapshot supersedes earlier runtime-context snapshots.\n\nCurrent working directory: \"{{cwd}}\".\n\nCurrent DSH file policy: danger-full-access. The DSH file sandbox does not restrict file modifications by available operations.\n\nApproval prompts are disabled in this session: actions that require approval are rejected automatically — do not request sandbox escalation (do not set `sandbox_permissions`).<system-reminder>\nA skill is a reusable set of task-specific instructions. The following skills are available in this session:\n\n<available_skills>\n- `diagnose-windows-sandbox-acl`: Use on Windows for unexpected DSH sandbox access denials: workspace writes or listing fail, or an ordinarily readable path cannot be read. One bundled command inspects the path and every ancestor and repairs the ACL problems it proves in that same run. Expected confinement denials need no ACL repair.\n- `office`: Create, edit, inspect, or convert Office documents, spreadsheets, presentations, and PDFs. Load to choose the relevant workflow.\n- `office-docx`: Create, read, edit, and validate Word documents (.docx).\n- `office-export-pdf`: Convert an Office document to a requested PDF with LibreOffice Kit.\n- `office-pptx`: Create, read, edit, and validate PowerPoint presentations (.pptx).\n- `office-recalculate`: Refresh spreadsheet formula caches and verify calculated values with LibreOffice Kit.\n- `office-render`: Render requested document images or inspect visual layout with LibreOffice Kit.\n- `office-xlsx`: Read, create, edit, and validate Excel workbooks, data, and formulas (.xlsx).\n</available_skills>\n\nIf the user names a skill, or the task clearly matches a skill's description, call the `skill` tool with the exact skill name before taking task actions. Load all applicable skills, then follow their full instructions. This catalog contains summaries only; do not infer or follow a skill's instructions until it has been loaded.\nA user may also invoke a skill directly; its <skill_content> block then appears in this conversation. Follow it, and do not call the `skill` tool again for that skill.\n</system-reminder>Time sampled while preparing turn 1, step 1: 2026-10-16T00:00:00+00:00[UTC]\nBrowser time zone for this request: unavailable. Ask the user to clarify otherwise-unqualified dates and times.\nElapsed since the preceding model-visible message: unavailable."
|
||||
"Query the packaged Python environment for Office authoring.Current runtime context. This snapshot supersedes earlier runtime-context snapshots.\n\nCurrent working directory: \"{{cwd}}\".\n\nCurrent DSH file policy: danger-full-access. The DSH file sandbox does not restrict file modifications by available operations.\n\nApproval prompts are disabled in this session: actions that require approval are rejected automatically — do not request sandbox escalation (do not set `sandbox_permissions`).\n\nYou are the root agent. Your agent id is \"authoring\".\n\nTo message other agents, use send_message({ agent_id: \"<agent-id>\", message: \"...\" }).\nUse list_agents({}) to find your collaborator agents and their ids.<system-reminder>\nA skill is a reusable set of task-specific instructions. The following skills are available in this session:\n\n<available_skills>\n- `diagnose-windows-sandbox-acl`: Use on Windows for unexpected DSH sandbox access denials: workspace writes or listing fail, or an ordinarily readable path cannot be read. One bundled command inspects the path and every ancestor and repairs the ACL problems it proves in that same run. Expected confinement denials need no ACL repair.\n- `office`: Create, edit, inspect, or convert Office documents, spreadsheets, presentations, and PDFs. Load to choose the relevant workflow.\n- `office-docx`: Create, read, edit, and validate Word documents (.docx).\n- `office-export-pdf`: Convert an Office document to a requested PDF with LibreOffice Kit.\n- `office-pptx`: Create, read, edit, and validate PowerPoint presentations (.pptx).\n- `office-recalculate`: Refresh spreadsheet formula caches and verify calculated values with LibreOffice Kit.\n- `office-render`: Render requested document images or inspect visual layout with LibreOffice Kit.\n- `office-xlsx`: Read, create, edit, and validate Excel workbooks, data, and formulas (.xlsx).\n</available_skills>\n\nIf the user names a skill, or the task clearly matches a skill's description, call the `skill` tool with the exact skill name before taking task actions. Load all applicable skills, then follow their full instructions. This catalog contains summaries only; do not infer or follow a skill's instructions until it has been loaded.\nA user may also invoke a skill directly; its <skill_content> block then appears in this conversation. Follow it, and do not call the `skill` tool again for that skill.\n</system-reminder>Time sampled while preparing turn 1, step 1: 2026-10-16T00:00:00+00:00[UTC]\nBrowser time zone for this request: unavailable. Ask the user to clarify otherwise-unqualified dates and times.\nElapsed since the preceding model-visible message: unavailable."
|
||||
],
|
||||
"result": {
|
||||
"python": "{{python}}",
|
||||
|
||||
@@ -71,6 +71,7 @@
|
||||
"subagent_fork",
|
||||
"todo_write",
|
||||
"update_goal",
|
||||
"wait_agent",
|
||||
"web_fetch",
|
||||
"web_search",
|
||||
"workflow",
|
||||
@@ -135,6 +136,7 @@
|
||||
"subagent_fork",
|
||||
"todo_write",
|
||||
"update_goal",
|
||||
"wait_agent",
|
||||
"web_fetch",
|
||||
"web_search",
|
||||
"workflow",
|
||||
@@ -212,6 +214,7 @@
|
||||
"subagent_fork",
|
||||
"todo_write",
|
||||
"update_goal",
|
||||
"wait_agent",
|
||||
"web_fetch",
|
||||
"web_search",
|
||||
"workflow",
|
||||
|
||||
@@ -302,40 +302,35 @@
|
||||
"symbol": "GoalChanged",
|
||||
"source": "packages/goal/goal/src/domain.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/subsystems/agent-team.md",
|
||||
"symbol": "TeamMemberProjection",
|
||||
"source": "packages/experimental/agent-team/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/subsystems/agent-team.md",
|
||||
"symbol": "TeamTaskView",
|
||||
"source": "packages/experimental/agent-team/src/types.ts"
|
||||
"source": "packages/team/agent-team/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/subsystems/agent-team.md",
|
||||
"symbol": "TeamProjection",
|
||||
"source": "packages/experimental/agent-team/src/types.ts"
|
||||
"source": "packages/team/agent-team/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/subsystems/agent-team.md",
|
||||
"symbol": "TeamMemberSnapshot",
|
||||
"source": "packages/experimental/agent-team/src/types.ts"
|
||||
"source": "packages/team/agent-team/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/subsystems/agent-team.md",
|
||||
"symbol": "TeamMessageSnapshot",
|
||||
"source": "packages/experimental/agent-team/src/types.ts"
|
||||
"source": "packages/team/agent-team/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/subsystems/agent-team.md",
|
||||
"symbol": "TeamMessageSource",
|
||||
"source": "packages/experimental/agent-team/src/types.ts"
|
||||
"source": "packages/team/agent-team/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/subsystems/agent-team.md",
|
||||
"symbol": "TeamTaskSnapshot",
|
||||
"source": "packages/experimental/agent-team/src/types.ts"
|
||||
"source": "packages/team/agent-team/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/subsystems/commands.md",
|
||||
|
||||
@@ -138,7 +138,7 @@ const SENTENCE_MODEL_EXPERIENCE: Readonly<Record<string, SentenceContract>> = {
|
||||
'packages/client/file-upload': { kind: 'none', reason: 'Browser-side request-body transport; registers nothing model-facing.' },
|
||||
'packages/api/remotes': { kind: 'none', reason: 'The Remote BFF selects business methods and forwarded events; selected services own any model-visible effect.' },
|
||||
'packages/api/remote-access': { kind: 'none', reason: 'Remote access decisions for Client callers; registers nothing model-facing.' },
|
||||
'packages/experimental/client-ui-agent-team': { kind: 'none', reason: 'The browser projection and task controls register no model-facing input.' },
|
||||
'packages/client/ui-agent-team': { kind: 'none', reason: 'The browser projection and task controls register no model-facing input.' },
|
||||
'packages/experimental/client-ui-claude-code-mods': { kind: 'none', reason: 'The browser draws Host-held mod trees and relays clicks; the mods\' own hooks own any model-visible effect.' },
|
||||
'packages/client/shortcuts': { kind: 'none', reason: 'Browser keyboard routing; registers nothing model-facing.' },
|
||||
'packages/client/ui-shortcuts': { kind: 'none', reason: 'Read-only browser reference; registers nothing model-facing.' },
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
/**
|
||||
* Verify that the executable deploy manifest supplies every plugin referenced
|
||||
* by a shipped agent preset and every required workspace peer in its dependency
|
||||
* graph. With auto peer installation disabled, either omission can otherwise
|
||||
* fail only when Cordis loads the packaged plugin.
|
||||
* graph. Frozen deployment preserves the locked peer graph; this check also
|
||||
* requires explicit workspace peers so omissions fail before plugin loading.
|
||||
*/
|
||||
import { globSync } from 'node:fs'
|
||||
import { readFile } from 'node:fs/promises'
|
||||
|
||||
Reference in new issue
Block a user