Merge pull request #4394 from deepseek-harness/refactor/subagent
refactor(subagent): unify activations and reuse parent catalog
This commit is contained in:
commit
bf1c4e01ba
558 files changed
+13660
-9131
No files matched your search
@@ -0,0 +1,277 @@
|
||||
import { mkdtemp, mkdir, rm, writeFile } from 'node:fs/promises'
|
||||
import { tmpdir } from 'node:os'
|
||||
import { join, resolve } from 'node:path'
|
||||
import { runGate } from './run-gates.ts'
|
||||
import { PassThrough } from 'node:stream'
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest'
|
||||
import { ForksPoolWorker } from 'vitest/node'
|
||||
import type { PoolOptions, PoolWorker, TestProject, WorkerRequest, WorkerResponse } from 'vitest/node'
|
||||
import { coverageForkPool } from './coverage-fork-diagnostics.ts'
|
||||
|
||||
const roots: string[] = []
|
||||
const cleanups: Array<() => Promise<void>> = []
|
||||
|
||||
afterEach(async () => {
|
||||
const cleanup = cleanups.splice(0)
|
||||
const directories = roots.splice(0)
|
||||
const results = await Promise.allSettled(cleanup.map(dispose => dispose()))
|
||||
vi.restoreAllMocks()
|
||||
for (const directory of directories) await rm(directory, { recursive: true, force: true })
|
||||
const errors: unknown[] = []
|
||||
for (const result of results) {
|
||||
if (result.status === 'rejected') errors.push(result.reason)
|
||||
}
|
||||
if (errors.length > 0) throw new AggregateError(errors, 'fork fixture cleanup failed')
|
||||
})
|
||||
|
||||
async function fixture(exitCode: number, early = false): Promise<string> {
|
||||
const root = await mkdtemp(join(tmpdir(), 'dsh-coverage-fork-'))
|
||||
roots.push(root)
|
||||
await mkdir(join(root, 'workers'))
|
||||
// The public pool's distPath selects a plain Node protocol fixture, not a nested Vitest run.
|
||||
await writeFile(join(root, 'workers/forks.js'), early ? `process.exit(${exitCode})\n` : `
|
||||
process.on('message', (message) => {
|
||||
if (message.type === 'run' || message.type === 'collect') process.send({ kind: 'ready', pid: process.pid })
|
||||
if (message.type === 'cancel') process.exit(${exitCode})
|
||||
if (message.type === 'stop') process.exit(0)
|
||||
})
|
||||
`)
|
||||
return root
|
||||
}
|
||||
|
||||
function poolOptions(root: string) {
|
||||
const outputStream = new PassThrough()
|
||||
const errorStream = new PassThrough()
|
||||
const error = vi.fn<(message: string) => void>()
|
||||
// ForksPoolWorker needs only the logger and project name from the Vitest project.
|
||||
const logger: Pick<TestProject['vitest']['logger'], 'outputStream' | 'errorStream' | 'error'> = {
|
||||
outputStream, errorStream, error,
|
||||
}
|
||||
const project = { name: 'fixture', vitest: { logger } } as TestProject
|
||||
const options: PoolOptions = {
|
||||
distPath: root, project, method: 'run', environment: { name: 'node', options: null },
|
||||
execArgv: [], env: {},
|
||||
}
|
||||
return { options, error, outputStream, errorStream }
|
||||
}
|
||||
|
||||
async function start(root: string, diagnostic = true) {
|
||||
const { options, error, outputStream, errorStream } = poolOptions(root)
|
||||
const pool: PoolWorker = diagnostic ? coverageForkPool.createPoolWorker(options) : new ForksPoolWorker(options)
|
||||
let started = false
|
||||
const closed = Promise.withResolvers<undefined>()
|
||||
cleanups.push(async () => {
|
||||
if (started) {
|
||||
await pool.stop()
|
||||
await closed.promise
|
||||
}
|
||||
outputStream.destroy()
|
||||
errorStream.destroy()
|
||||
})
|
||||
await pool.start()
|
||||
started = true
|
||||
pool.on('close', () => { closed.resolve(undefined) })
|
||||
const exited = new Promise<number | null>((resolve) => { pool.on('exit', resolve) })
|
||||
const ready = new Promise<number>((resolve) => {
|
||||
pool.on('message', (message: unknown) => {
|
||||
const response = message as { kind: string; pid: number }
|
||||
if (response.kind === 'ready') resolve(response.pid)
|
||||
})
|
||||
})
|
||||
return { pool, ready, exited, error }
|
||||
}
|
||||
|
||||
function request(filepath: string, workerId: number): Extract<WorkerRequest, { type: 'run' }> {
|
||||
return {
|
||||
__vitest_worker_request__: true, type: 'run',
|
||||
context: { files: [{ filepath }], workerId, providedContext: {}, environment: { name: 'node', options: null } },
|
||||
}
|
||||
}
|
||||
|
||||
const cancel: WorkerRequest = { __vitest_worker_request__: true, type: 'cancel' }
|
||||
const stop: WorkerRequest = { __vitest_worker_request__: true, type: 'stop' }
|
||||
|
||||
async function configuredPool(pool: 'stock' | 'diagnostic', outcome: 'exit' | 'pass', signal: AbortSignal) {
|
||||
const cacheDir = await mkdtemp(join(tmpdir(), 'dsh-configured-fork-cache-'))
|
||||
roots.push(cacheDir)
|
||||
const controller = new AbortController()
|
||||
const result = runGate({
|
||||
id: `fork-${pool}`, label: 'fork fixture', displayCommand: 'Vitest pool fixture',
|
||||
command: process.execPath,
|
||||
args: [resolve('node_modules/vitest/vitest.mjs'), 'run', '--config', 'scripts/fixtures/coverage-fork.config.ts'],
|
||||
env: { DSH_TEST_FORK_POOL: pool, DSH_TEST_FORK_OUTCOME: outcome, DSH_TEST_FORK_CACHE: cacheDir },
|
||||
}, AbortSignal.any([signal, controller.signal]))
|
||||
let settled = false
|
||||
cleanups.push(async () => {
|
||||
if (!settled) controller.abort()
|
||||
await result
|
||||
})
|
||||
try {
|
||||
return await result
|
||||
} finally {
|
||||
settled = true
|
||||
}
|
||||
}
|
||||
|
||||
describe('coverage fork configuration entry', () => {
|
||||
it('preserves the real Vitest failure and adds metadata before its error report', async ({ signal }) => {
|
||||
const [stock, diagnostic] = await Promise.all([
|
||||
configuredPool('stock', 'exit', signal),
|
||||
configuredPool('diagnostic', 'exit', signal),
|
||||
])
|
||||
for (const result of [stock, diagnostic]) {
|
||||
expect(result.aborted).not.toBe(true)
|
||||
expect(result.error).toBeUndefined()
|
||||
expect(result.signalCode).toBeNull()
|
||||
expect(result.exitCode).toBe(1)
|
||||
expect(result.output.map(chunk => chunk.text).join('')).toContain('Worker exited unexpectedly')
|
||||
}
|
||||
const output = diagnostic.output.map(chunk => chunk.text).join('')
|
||||
expect(stock.output.map(chunk => chunk.text).join('')).not.toContain('coverage-worker-exit:')
|
||||
const encoded = /coverage-worker-exit: (\{[^\r\n]*\})/u.exec(output)?.[1]
|
||||
expect(encoded).toBeDefined()
|
||||
const record = JSON.parse(encoded!) as {
|
||||
pid: number
|
||||
project: string
|
||||
workerId: number
|
||||
files: string[]
|
||||
exitCode: number
|
||||
signalCode: null
|
||||
}
|
||||
const pid = Number(/coverage-fixture-pid: (\d+)/u.exec(output)?.[1])
|
||||
const anyNumber: unknown = expect.any(Number)
|
||||
const anyString: unknown = expect.any(String)
|
||||
expect(record).toEqual({
|
||||
pid, project: 'configured-fork', workerId: anyNumber,
|
||||
files: [anyString], exitCode: 23, signalCode: null,
|
||||
})
|
||||
expect(record.files.map(file => file.replaceAll('\\', '/')))
|
||||
.toEqual([resolve('scripts/fixtures/coverage-fork.fixture.ts').replaceAll('\\', '/')])
|
||||
expect(output.indexOf('coverage-worker-exit:')).toBeLessThan(output.indexOf('Worker exited unexpectedly'))
|
||||
})
|
||||
|
||||
it('keeps a successful configured run silent and successful', async ({ signal }) => {
|
||||
const result = await configuredPool('diagnostic', 'pass', signal)
|
||||
expect(result.aborted).not.toBe(true)
|
||||
expect(result.error).toBeUndefined()
|
||||
expect(result.signalCode).toBeNull()
|
||||
expect(result.exitCode).toBe(0)
|
||||
expect(result.output.map(chunk => chunk.text).join('')).not.toContain('coverage-worker-exit:')
|
||||
})
|
||||
})
|
||||
|
||||
describe('coverage fork exit callback', () => {
|
||||
it.each([
|
||||
['native receiver', { pid: 41 }, 41],
|
||||
['unavailable receiver', undefined, null],
|
||||
] as const)('retains a signal with %s and silences expected shutdown', async (_name, receiver, pid) => {
|
||||
let exitListener: ((value: unknown) => void) | undefined
|
||||
vi.spyOn(ForksPoolWorker.prototype, 'start').mockResolvedValue(undefined)
|
||||
vi.spyOn(ForksPoolWorker.prototype, 'stop').mockResolvedValue(undefined)
|
||||
vi.spyOn(ForksPoolWorker.prototype, 'send').mockImplementation(() => undefined)
|
||||
vi.spyOn(ForksPoolWorker.prototype, 'off').mockImplementation(() => undefined)
|
||||
vi.spyOn(ForksPoolWorker.prototype, 'on').mockImplementation((event, callback) => {
|
||||
if (event === 'exit') exitListener = callback
|
||||
})
|
||||
const { options, error, outputStream, errorStream } = poolOptions('/unused')
|
||||
const pool = coverageForkPool.createPoolWorker(options)
|
||||
cleanups.push(async () => {
|
||||
await pool.stop()
|
||||
outputStream.destroy()
|
||||
errorStream.destroy()
|
||||
})
|
||||
await pool.start()
|
||||
pool.send(request('/signal.spec.ts', 9))
|
||||
if (exitListener === undefined) throw new Error('exit observer was not registered')
|
||||
Reflect.apply(exitListener, receiver, [null, 'SIGTERM'])
|
||||
expect(error).toHaveBeenCalledWith(`coverage-worker-exit: ${JSON.stringify({
|
||||
pid, project: 'fixture', workerId: 9, files: ['/signal.spec.ts'], exitCode: null, signalCode: 'SIGTERM',
|
||||
})}`)
|
||||
error.mockClear()
|
||||
pool.send(stop)
|
||||
expect(() => { Reflect.apply(exitListener!, undefined, [null, 'SIGTERM']) }).not.toThrow()
|
||||
expect(error).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
|
||||
describe('coverage fork exit diagnostics', () => {
|
||||
it('attributes a later collect request instead of an earlier run request', async () => {
|
||||
const instance = await start(await fixture(23))
|
||||
instance.pool.send(request('/earlier.spec.ts', 4))
|
||||
const pid = await instance.ready
|
||||
instance.pool.send({ ...request('/collected.spec.ts', 5), type: 'collect' })
|
||||
const unrelated = { type: 'testfileFinished' }
|
||||
expect(instance.pool.deserialize(unrelated)).toBe(unrelated)
|
||||
expect(instance.pool.deserialize(null)).toBeNull()
|
||||
instance.pool.send(cancel)
|
||||
expect(await instance.exited).toBe(23)
|
||||
expect(instance.error).toHaveBeenCalledWith(`coverage-worker-exit: ${JSON.stringify({
|
||||
pid, project: 'fixture', workerId: 5, files: ['/collected.spec.ts'], exitCode: 23, signalCode: null,
|
||||
})}`)
|
||||
})
|
||||
|
||||
it('does not attribute a post-completion exit to a finished file', async () => {
|
||||
const instance = await start(await fixture(23))
|
||||
instance.pool.send(request('/finished.spec.ts', 6))
|
||||
const pid = await instance.ready
|
||||
const finished = {
|
||||
__vitest_worker_response__: true, type: 'testfileFinished',
|
||||
} satisfies Extract<WorkerResponse, { type: 'testfileFinished' }>
|
||||
expect(instance.pool.deserialize(finished)).toBe(finished)
|
||||
instance.pool.send(cancel)
|
||||
expect(await instance.exited).toBe(23)
|
||||
expect(instance.error).toHaveBeenCalledWith(`coverage-worker-exit: ${JSON.stringify({
|
||||
pid, project: 'fixture', workerId: null, files: [], exitCode: 23, signalCode: null,
|
||||
})}`)
|
||||
})
|
||||
|
||||
it('records the exact owner while an independent ready fork remains alive', async () => {
|
||||
const root = await fixture(23)
|
||||
const [first, second] = await Promise.all([start(root), start(root)])
|
||||
first.pool.send(request('/first.spec.ts', 1))
|
||||
second.pool.send(request('/second.spec.ts', 2))
|
||||
const [firstPid, secondPid] = await Promise.all([first.ready, second.ready])
|
||||
expect(firstPid).not.toBe(secondPid)
|
||||
first.pool.send(cancel)
|
||||
expect(await first.exited).toBe(23)
|
||||
expect(first.error.mock.calls).toEqual([[`coverage-worker-exit: ${JSON.stringify({
|
||||
pid: firstPid, project: 'fixture', workerId: 1, files: ['/first.spec.ts'], exitCode: 23, signalCode: null,
|
||||
})}`]])
|
||||
expect(second.error).not.toHaveBeenCalled()
|
||||
expect(() => process.kill(secondPid, 0)).not.toThrow()
|
||||
second.pool.send(stop)
|
||||
expect(await second.exited).toBe(0)
|
||||
expect(second.error).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('retains an unexpected zero exit instead of interpreting it as suite success', async () => {
|
||||
const instance = await start(await fixture(0))
|
||||
instance.pool.send(request('/unfinished.spec.ts', 3))
|
||||
const pid = await instance.ready
|
||||
instance.pool.send(cancel)
|
||||
expect(await instance.exited).toBe(0)
|
||||
expect(instance.error).toHaveBeenCalledWith(`coverage-worker-exit: ${JSON.stringify({
|
||||
pid, project: 'fixture', workerId: 3, files: ['/unfinished.spec.ts'], exitCode: 0, signalCode: null,
|
||||
})}`)
|
||||
})
|
||||
|
||||
it('records an exit before any file request without inventing a file owner', async () => {
|
||||
const instance = await start(await fixture(31, true))
|
||||
expect(await instance.exited).toBe(31)
|
||||
expect(instance.error).toHaveBeenCalledOnce()
|
||||
const record = JSON.parse(instance.error.mock.calls[0]![0].slice('coverage-worker-exit: '.length)) as Record<string, unknown>
|
||||
const anyNumber: unknown = expect.any(Number)
|
||||
expect(record).toEqual({
|
||||
pid: anyNumber, project: 'fixture', workerId: null, files: [], exitCode: 31, signalCode: null,
|
||||
})
|
||||
})
|
||||
|
||||
it('observes the same child failure without diagnostics under the stock pool negative control', async () => {
|
||||
const instance = await start(await fixture(23), false)
|
||||
instance.pool.send(request('/stock.spec.ts', 4))
|
||||
await instance.ready
|
||||
instance.pool.send(cancel)
|
||||
expect(await instance.exited).toBe(23)
|
||||
expect(instance.error).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,69 @@
|
||||
/** Add exit metadata without changing Vitest's fork scheduling or failure handling. */
|
||||
import type { ChildProcess } from 'node:child_process'
|
||||
import { ForksPoolWorker } from 'vitest/node'
|
||||
import type { PoolOptions, PoolRunnerInitializer, WorkerRequest, WorkerResponse } from 'vitest/node'
|
||||
|
||||
const FINISHED_RESPONSE_TYPE: WorkerResponse['type'] = 'testfileFinished'
|
||||
|
||||
/** Attribute only outstanding requests; stop silences exits, while cancel does not. */
|
||||
class DiagnosticForkWorker extends ForksPoolWorker {
|
||||
private expectedExit = false
|
||||
private observing = false
|
||||
private request: Extract<WorkerRequest, { type: 'run' | 'collect' }> | undefined
|
||||
private readonly reportExit: (this: ChildProcess | undefined, code: number | null, signal?: NodeJS.Signals | null) => void
|
||||
|
||||
constructor(options: PoolOptions) {
|
||||
super(options)
|
||||
const report = (child: ChildProcess | undefined, code: number | null, signal: NodeJS.Signals | null | undefined): void => {
|
||||
if (this.expectedExit) return
|
||||
options.project.vitest.logger.error(`coverage-worker-exit: ${JSON.stringify({
|
||||
pid: child?.pid ?? null,
|
||||
project: options.project.name,
|
||||
workerId: this.request?.context.workerId ?? null,
|
||||
files: this.request?.context.files.map(file => file.filepath) ?? [],
|
||||
exitCode: code,
|
||||
signalCode: signal ?? null,
|
||||
})}`)
|
||||
}
|
||||
// ForksPoolWorker.on forwards Node ChildProcess events and their receiver.
|
||||
this.reportExit = function (code, signal) { report(this, code, signal) }
|
||||
}
|
||||
|
||||
override async start(): Promise<void> {
|
||||
await super.start()
|
||||
this.on('exit', this.reportExit)
|
||||
this.observing = true
|
||||
}
|
||||
|
||||
override send(message: WorkerRequest): void {
|
||||
if (message.type === 'run' || message.type === 'collect') this.request = message
|
||||
if (message.type === 'stop') this.expectedExit = true
|
||||
super.send(message)
|
||||
}
|
||||
|
||||
/** Vitest routes worker responses here; completion covers the whole run/collect request. */
|
||||
override deserialize(data: unknown): unknown {
|
||||
const response = super.deserialize(data)
|
||||
if (typeof response === 'object' && response !== null
|
||||
&& '__vitest_worker_response__' in response && response.__vitest_worker_response__ === true
|
||||
&& 'type' in response && response.type === FINISHED_RESPONSE_TYPE) {
|
||||
this.request = undefined
|
||||
}
|
||||
return response
|
||||
}
|
||||
|
||||
override async stop(): Promise<void> {
|
||||
this.expectedExit = true
|
||||
if (this.observing) {
|
||||
this.off('exit', this.reportExit)
|
||||
this.observing = false
|
||||
}
|
||||
await super.stop()
|
||||
}
|
||||
}
|
||||
|
||||
/** Fork pool with additive process-exit diagnostics for coverage partitions. */
|
||||
export const coverageForkPool: PoolRunnerInitializer = {
|
||||
name: 'coverage-forks',
|
||||
createPoolWorker: options => new DiagnosticForkWorker(options),
|
||||
}
|
||||
@@ -540,6 +540,8 @@ describe('coverage partition coordinator', () => {
|
||||
expect(allConfigs).toContain('c.spec.ts')
|
||||
for (const source of partitionConfigs) {
|
||||
expect(source).toContain("from '../../vitest.config.ts'")
|
||||
expect(source).toContain("from '../../scripts/coverage-fork-diagnostics.ts'")
|
||||
expect(source).toContain('pool: coverageForkPool')
|
||||
}
|
||||
const mergeCommand = commands[3]
|
||||
if (mergeCommand === undefined) throw new Error('coverage merge command was not observed')
|
||||
|
||||
@@ -429,6 +429,7 @@ function partitionConfigSource(
|
||||
const processBound = JSON.stringify(files.filter(file => projectOf.get(file) === 'process-bound').map(file => file.split('\\').join('/')))
|
||||
return [
|
||||
"import base from '../../vitest.config.ts'",
|
||||
"import { coverageForkPool } from '../../scripts/coverage-fork-diagnostics.ts'",
|
||||
'export default {',
|
||||
' ...base,',
|
||||
' test: {',
|
||||
@@ -437,6 +438,7 @@ function partitionConfigSource(
|
||||
' ...project,',
|
||||
' test: {',
|
||||
' ...project.test,',
|
||||
' pool: coverageForkPool,',
|
||||
' include: project.test.name === \'process-bound\' ? ' + processBound + ' : ' + threadSafe + ',',
|
||||
' },',
|
||||
' })),',
|
||||
|
||||
@@ -77,7 +77,6 @@ const PACKAGE_LIBRARIES: Readonly<Record<string, string>> = {
|
||||
'packages/session/session-format-v3-to-v4': 'Released V3 codec reuse and adjacent migration library.',
|
||||
'packages/session/session-telemetry': 'Telemetry Service Definition and capture library; providers mount the backend.',
|
||||
'packages/session/session-title-llm': 'Shared LLM title-provider registration and request policy.',
|
||||
'packages/subagent/subagent-in-process-driver': 'Shared one-shot child-agent driver used by provider plugins.',
|
||||
'packages/subprocess/win32-process': 'Low-level Win32 process and Job Object primitives.',
|
||||
'packages/test-support/session-snapshot': 'Test infrastructure; mounts nothing into a product composition.',
|
||||
'packages/test-support/agent-loop-testkit': 'Test helper library; mounts nothing into a product composition.',
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
// Preserve Node's exit before Vitest installs its test-level process.exit guard.
|
||||
globalThis.__dshCoverageFixtureExit = process.exit.bind(process)
|
||||
@@ -0,0 +1,18 @@
|
||||
import { resolve } from 'node:path'
|
||||
import { defineConfig } from 'vitest/config'
|
||||
import { coverageForkPool } from '../coverage-fork-diagnostics.ts'
|
||||
|
||||
const cacheDir = process.env.DSH_TEST_FORK_CACHE
|
||||
if (cacheDir === undefined || cacheDir === '') throw new Error('fork fixture requires its private cache directory')
|
||||
|
||||
export default defineConfig({
|
||||
cacheDir,
|
||||
test: {
|
||||
name: 'configured-fork',
|
||||
include: ['scripts/fixtures/coverage-fork.fixture.ts'],
|
||||
pool: process.env.DSH_TEST_FORK_POOL === 'stock' ? 'forks' : coverageForkPool,
|
||||
execArgv: ['--require', resolve(import.meta.dirname, 'coverage-fork-exit.cjs')],
|
||||
maxWorkers: 1,
|
||||
cache: false,
|
||||
},
|
||||
})
|
||||
@@ -0,0 +1,15 @@
|
||||
import { writeSync } from 'node:fs'
|
||||
import { expect, it } from 'vitest'
|
||||
|
||||
declare global {
|
||||
/** Native process exit captured by this fixture's Node preload. */
|
||||
function __dshCoverageFixtureExit(code: number): never
|
||||
}
|
||||
|
||||
it('runs in the configured pool', () => {
|
||||
writeSync(2, `coverage-fixture-pid: ${process.pid}\n`)
|
||||
if (process.env.DSH_TEST_FORK_OUTCOME === 'exit') {
|
||||
globalThis.__dshCoverageFixtureExit(23)
|
||||
}
|
||||
expect(2 + 2).toBe(4)
|
||||
})
|
||||
@@ -1,14 +1,17 @@
|
||||
/** Hold the advanced workflow child's first step until its parent records membership. */
|
||||
/** Order child execution after the advanced parent records its corresponding handoff. */
|
||||
export const name = 'python-snapshot-workflow-order'
|
||||
|
||||
/**
|
||||
* @param {import('@deepseek-ai/cordis').Context} ctx - Scenario-local host context.
|
||||
* @param {{ parentSessionId: string, prompt: string }} config - Exact advanced scenario identities.
|
||||
* @param {{ parentSessionId: string, prompt: string, direct?: { prompt: string, callId: string } }} config - Exact advanced scenario identities.
|
||||
*/
|
||||
export function apply(ctx, config) {
|
||||
const started = new Set()
|
||||
const pending = new Map()
|
||||
let disposed = false
|
||||
let directResultSeq
|
||||
let directStep = false
|
||||
let directDelivered = false
|
||||
|
||||
ctx.effect(() => async () => {
|
||||
disposed = true
|
||||
@@ -18,19 +21,33 @@ export function apply(ctx, config) {
|
||||
started.clear()
|
||||
})
|
||||
ctx.on('session/event', (session, event) => {
|
||||
if (disposed || session.id !== config.parentSessionId || event.type !== 'tool-workflow/agent-start') return
|
||||
started.add(event.data.childId)
|
||||
pending.get(event.data.childId)?.resolve()
|
||||
if (disposed || session.id !== config.parentSessionId) return
|
||||
if (event.type === 'tool-workflow/agent-start') {
|
||||
started.add(event.data.childId)
|
||||
pending.get(event.data.childId)?.resolve()
|
||||
}
|
||||
if (config.direct === undefined) return
|
||||
if (event.type === 'tool/result' && event.data.message.source.callId === config.direct.callId) directResultSeq = event.seq
|
||||
if (directResultSeq === undefined) return
|
||||
if (event.type === 'step/start') directStep = true
|
||||
if (event.type === 'session-log-deepseek/delivery-accepted' && event.data.throughSeq >= directResultSeq) {
|
||||
directDelivered = true
|
||||
}
|
||||
if (directStep && directDelivered) {
|
||||
for (const wait of pending.values()) if (wait.direct) wait.resolve()
|
||||
}
|
||||
})
|
||||
ctx.on('agent/pre-step', async ({ agent, messages, turn, step, signal }, next) => {
|
||||
const hasPrompt = prompt => messages.some(message => message.content.some(block => block.type === 'text' && block.text === prompt))
|
||||
const direct = config.direct !== undefined && hasPrompt(config.direct.prompt)
|
||||
if (agent.session.header.parentSession !== config.parentSessionId || turn !== 1 || step !== 1
|
||||
|| !messages.some(message => message.content.some(block => block.type === 'text' && block.text === config.prompt))) {
|
||||
|| !direct && !hasPrompt(config.prompt)) {
|
||||
return next()
|
||||
}
|
||||
signal.throwIfAborted()
|
||||
if (disposed) throw new Error('workflow snapshot barrier disposed')
|
||||
if (!started.has(agent.id)) {
|
||||
const wait = Promise.withResolvers()
|
||||
if (direct ? !directStep || !directDelivered : !started.has(agent.id)) {
|
||||
const wait = { ...Promise.withResolvers(), direct }
|
||||
const abort = () => { wait.reject(signal.reason) }
|
||||
signal.addEventListener('abort', abort, { once: true })
|
||||
wait.done = wait.promise.finally(() => {
|
||||
|
||||
@@ -605,8 +605,8 @@ export const LINK_MAP: Readonly<Record<string, string>> = {
|
||||
SpillRef: 'spill.md',
|
||||
ContinuableCreateRequest: 'subagent.md',
|
||||
ContinuableCreateSpec: 'subagent.md',
|
||||
ContinuableStart: 'subagent.md',
|
||||
ContinuableStartSpec: 'subagent.md',
|
||||
SubagentActivation: 'subagent.md',
|
||||
SubagentActivationSpec: 'subagent.md',
|
||||
AgentMessageSource: 'subagent.md',
|
||||
SubagentCatalogEntry: 'subagent.md',
|
||||
SubagentCatalogState: 'subagent.md',
|
||||
|
||||
@@ -260,7 +260,7 @@ const SERVICE_ROLES: ServiceRole[] = [
|
||||
pkg: 'session',
|
||||
title: 'In-memory session store',
|
||||
mode: 'core',
|
||||
consumers: ['agent-loop', 'agent', 'session-persistence', 'session-query', 'session-query-sqlite', 'subagent-in-process-driver', 'message-feedback'],
|
||||
consumers: ['agent-loop', 'agent', 'session-persistence', 'session-query', 'session-query-sqlite', 'subagent', 'message-feedback'],
|
||||
note: 'Owns append-only Session instances and emits the durable session event feed.',
|
||||
},
|
||||
{
|
||||
@@ -601,7 +601,7 @@ const SERVICE_ROLES: ServiceRole[] = [
|
||||
pkg: 'agent',
|
||||
title: 'Agent service',
|
||||
mode: 'core',
|
||||
consumers: ['agent-loop', 'acp', 'subagent-in-process-driver'],
|
||||
consumers: ['agent-loop', 'acp', 'subagent'],
|
||||
note: 'Owns live Agent handles, the create/resume factory seam, and process-local initiator propagation.',
|
||||
},
|
||||
{
|
||||
@@ -747,7 +747,7 @@ const SERVICE_ROLES: ServiceRole[] = [
|
||||
mode: 'seam',
|
||||
implementations: ['subagent-spawn-in-process', 'subagent-fork-in-process', 'subagent-acp', 'subagent-codex', 'subagent-claude-code', 'subagent-dsh-sdk'],
|
||||
consumers: ['tool-subagent', 'tool-subagent-control', 'tool-ralph'],
|
||||
note: 'Providers implement transports; the service also owns optional Activation-based continuation orchestration, tool-subagent selects one-shot or continuable delegation, tool-subagent-control delivers follow-ups, and tool-ralph requires one fresh structured-output route.',
|
||||
note: 'Providers prepare local children or execute external tasks; one activation manager owns both lifecycles. The delegation tool selects a backend, control tools deliver local follow-ups, and workflows await activation results.',
|
||||
},
|
||||
{
|
||||
key: 'speechToText',
|
||||
@@ -786,8 +786,8 @@ const SERVICE_ROLES: ServiceRole[] = [
|
||||
title: 'Background job registry',
|
||||
mode: 'seam',
|
||||
implementations: ['jobs-local'],
|
||||
consumers: ['tool-bash', 'tool-pwsh', 'tool-terminal', 'tool-subagent', 'tool-jobs', 'api-job-controller'],
|
||||
note: 'Producers (background bash/pwsh, PTY sends, and subagent delegations) register running work; record-declaring jobs additionally stream raw output for non-consuming observers; tool-jobs is the model-facing controller that reads, lists, and kills it; jobs-local is the process-local registry.',
|
||||
consumers: ['tool-bash', 'tool-pwsh', 'tool-terminal', 'tool-jobs', 'api-job-controller'],
|
||||
note: 'Producers (background bash/pwsh and PTY sends) register running work; record-declaring jobs additionally stream raw output for non-consuming observers; tool-jobs is the model-facing controller that reads, lists, and kills it; jobs-local is the process-local registry.',
|
||||
},
|
||||
{
|
||||
key: 'web',
|
||||
|
||||
@@ -522,7 +522,7 @@ const TOOL_PACKAGES: ToolPackage[] = [
|
||||
registerListSubagentModels(ctx, { routes: [{ provider: 'mock', model: 'mock' }] })
|
||||
},
|
||||
note:
|
||||
'The registered delegation name is the load-time `toolName` config (default `subagent`); the default schema above has model selection off, while the discovery schema is shown as the fixed companion available in an enabled Session. Web presets sample the Plugins preference for each new top-level Session and preserve that decision for its child Sessions; `subagent_fork` remains fixed-route. Each instance independently controls whether it reads model-selection settings and its background behavior through `modelSelectionSettings`, `backgroundMode`, and `enableRunInBackground`.',
|
||||
'The registered delegation name is the load-time `toolName` config (default `subagent`); the default schema above has model selection off, while the discovery schema is shown as the fixed companion available in an enabled Session. Web presets sample the Plugins preference for each new top-level Session and preserve that decision for its child Sessions; `subagent_fork` remains fixed-route. Each instance selects its backend through `provider` and reads model-selection settings through `modelSelectionSettings`. Every delegation returns a child id promptly; local children accept follow-ups, while external backends report their single execution automatically.',
|
||||
},
|
||||
{
|
||||
pkg: '@deepseek-ai/dsh-tool-subagent-control',
|
||||
@@ -543,7 +543,7 @@ const TOOL_PACKAGES: ToolPackage[] = [
|
||||
await ctx.plugin(ToolSubagentListAgents)
|
||||
},
|
||||
note:
|
||||
'The globally named control tools over continuable background subagents: provider-bound `tool-subagent` instances register distinct delegation tools, while this package registers `send_message` and `interrupt_agent` once, plus `list_agents` from its separately loaded `/list-agents` plugin (whose catalog rows use the sessionProjections and live Agent registries).',
|
||||
'The globally named discovery, interruption, and local-message tools over managed subagents: provider-bound `tool-subagent` instances register distinct delegation tools, while this package registers `send_message` and `interrupt_agent` once, plus `list_agents` from its separately loaded `/list-agents` plugin (whose catalog rows use the sessionProjections and live Agent registries).',
|
||||
},
|
||||
{
|
||||
pkg: '@deepseek-ai/dsh-tool-jobs',
|
||||
@@ -556,7 +556,7 @@ const TOOL_PACKAGES: ToolPackage[] = [
|
||||
await ctx.plugin(ToolJobs)
|
||||
},
|
||||
note:
|
||||
'The kind-agnostic background-job controller: background bash commands, PTY sends, and subagents are read, listed, and killed through the same three tools. Loading the plugin attaches the controller that arms producers\' `ctx.jobs.start()`.',
|
||||
'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',
|
||||
|
||||
@@ -1523,12 +1523,11 @@
|
||||
"71fa45c63f1d2dc12e6a4da1120a9ca227ef1b1ffbce510cd0d8847d85f278c0": 9,
|
||||
"80fa2ee81263d11fc1fafe6b7dbb608e8ed6e7c7e8e7fa3e726b600e82425294": 1,
|
||||
"82025e3228e5dab025f1c3434c0303baf066b7f40f30352764abc7374a693928": 1,
|
||||
"9234f1edeb197a3cc5e423e04ea3b9d79da97e1f231db0bca529810ab3e0d100": 1,
|
||||
"926edbfe0199937da6ec9d119cfd16f6cbc674a242de8f433980da6e6ea8b13b": 1,
|
||||
"9b52ca4ca1a86eb1d34576ca3c210d2e09677553e2a0b0e505675782034c147e": 1,
|
||||
"9c3213cd8f09df05b9aceba2033b68f6b61c1abe2231167622ef3fa462d77424": 1,
|
||||
"a03ef245e286d905868294bee2e81ac282cfa0a5161a1edd6d8b0aa167932fb6": 1,
|
||||
"ae0162b189937f84fb9a162eabbc7496f7e196c00e960aef4fe8e5b3e4b87d8d": 4,
|
||||
"ae0162b189937f84fb9a162eabbc7496f7e196c00e960aef4fe8e5b3e4b87d8d": 3,
|
||||
"d54d74835af1518a4723a7cc803efc0d4e8e337ebb80b813b6ef76bdb813be17": 1
|
||||
},
|
||||
"packages/session-query/session-log-export/tests/archive.host.spec.ts": {
|
||||
@@ -1696,9 +1695,6 @@
|
||||
"packages/session/session-persistence-jsonl/tests/lease.spec.ts": {
|
||||
"94e6c4d15a19729cf60005d29c0985aa4e5bd984b92f40c2b5d8e996bb651997": 1
|
||||
},
|
||||
"packages/session/session-persistence-jsonl/tests/migration-verifier.spec.ts": {
|
||||
"6bad3f6cf7fc26c871be029dc2a4f12d01ce7a49f314b0d728453e7575af28d1": 1
|
||||
},
|
||||
"packages/session/session-persistence-jsonl/tests/native-source-admission.spec.ts": {
|
||||
"24b3f41708c436a3b161317d9b04439dff484aa8f5a913ed3c68a299f490e3b9": 1
|
||||
},
|
||||
@@ -1834,8 +1830,7 @@
|
||||
},
|
||||
"packages/subagent/subagent-claude-code/tests/subagent-claude-code.spec.ts": {
|
||||
"2cedee48f4fb8ce4bbd10567fdfbee69e8dd11dbc85af9b824de33c2b77abfbf": 1,
|
||||
"4e94c461ab08f535e20f71ebe8bb9a8c911a936c7068f24e259956c06d17c05d": 2,
|
||||
"aa1a9402165afaeb270967390b13cae2a59916bc52f13b4f600315d7a0fe2032": 1,
|
||||
"4e94c461ab08f535e20f71ebe8bb9a8c911a936c7068f24e259956c06d17c05d": 1,
|
||||
"cdcbd6cc73a09b6613864cdfb5b14d0cf66dc2484d26916cb32929deca1032de": 3,
|
||||
"f85c96616fff1bb1e21886bd2fad64e9e34d89d090ab96ab7804f702278cd23d": 4,
|
||||
"fa05a5e504ed74b5a07cb6281c787b507eb6c44db768803f4dad034dd69cb3b9": 1
|
||||
@@ -1849,27 +1844,12 @@
|
||||
"adda026f7ad49b47496f381a0c04997300597d040b8a81045a848573485ed299": 1
|
||||
},
|
||||
"packages/subagent/subagent-codex/tests/subagent-codex.spec.ts": {
|
||||
"aa1a9402165afaeb270967390b13cae2a59916bc52f13b4f600315d7a0fe2032": 1,
|
||||
"fa05a5e504ed74b5a07cb6281c787b507eb6c44db768803f4dad034dd69cb3b9": 1
|
||||
},
|
||||
"packages/subagent/subagent-dsh-sdk/tests/subagent-dsh-sdk.spec.ts": {
|
||||
"daa15206e19d6a8fbae646d678889b2e6933ece824e48dd06c95d2096861d5c1": 2,
|
||||
"f77e5cbd2d2d911087f7faecc4b0b5bffdfb44ca5d0bcf7823ba9777feca7799": 1
|
||||
},
|
||||
"packages/subagent/subagent-in-process-driver/src/structured.ts": {
|
||||
"2994ddac72096564a65b1d11cfe50c5b5f21de0cca775ed7b758d031b9a4df99": 1
|
||||
},
|
||||
"packages/subagent/subagent-in-process-driver/tests/structured.spec.ts": {
|
||||
"647bbba1ee35ea6acd23faa1e2988e24c49f0284be571ab3e716ab36a11cc38c": 1,
|
||||
"fd4628b951a78392c07e54a33588263377c3c85c90dc33ce0b7f78e904b1c2ad": 1
|
||||
},
|
||||
"packages/subagent/subagent-in-process-driver/tests/subagent-in-process-driver.spec.ts": {
|
||||
"5bc99a74ad50605215a43105e8b5d19ab8126149a99bcafcb6cf20c9992874d4": 1,
|
||||
"69d9dd6917cd45e70ef0ed273bea2e4a01fef7cb808458a7f0606b90f260cb2b": 1,
|
||||
"79d2d214c7600ab9023c27b97ec626b9e3ce33f0b42f3f6233dfd142387fbdcf": 1,
|
||||
"996a2d673209758e45b46af235339d8dccc9d20cfea97ea34f536d3ed8842812": 1,
|
||||
"b50b6842b3565ea7f1441afb3214ab0ccf027df0e44bb485005935849c46f8ea": 1
|
||||
},
|
||||
"packages/subagent/subagent/src/catalog.ts": {
|
||||
"3e704e331ed2fc5d4c0855e2455b31b82924c525f2e267416e393cdbd36c251b": 1,
|
||||
"fdc5fcade0ba1abbc426ccccb07a1ad4998253799b8561815047d95178441cdf": 1
|
||||
@@ -1889,11 +1869,6 @@
|
||||
"packages/subagent/subagent/tests/catalog.spec.ts": {
|
||||
"7e2e5f940dc4eaeefee0650619f7a736c5bead03fb248b2394c1a29a3328b105": 1
|
||||
},
|
||||
"packages/subagent/subagent/tests/continuation-internals.ts": {
|
||||
"3745fdae681f5fd4fa6825b3987604ceed308de0ef68181e9b3b8405a288b1b0": 1,
|
||||
"ae5776af8c1e9965a4eca94bcbc07b65da8d2e9e24e4570d2141b34d60b4f90f": 1,
|
||||
"dacc9babb26371c6c047d8a504e4c7da0e2032f83620ab63ec667276284d1c35": 1
|
||||
},
|
||||
"packages/subagent/subagent/tests/continuation.spec.ts": {
|
||||
"ae5776af8c1e9965a4eca94bcbc07b65da8d2e9e24e4570d2141b34d60b4f90f": 1
|
||||
},
|
||||
@@ -1907,12 +1882,8 @@
|
||||
"e770821c1f637aca0a346d5f6e6f0873ca4d7e3b070b557cddf773c160ec599a": 1
|
||||
},
|
||||
"packages/subagent/tool-subagent/src/index.ts": {
|
||||
"11233be4a60a9dfb99acc5fbf90c5185c2101516379f3224866557301bbb048c": 1,
|
||||
"7fe58280287ff63d40e979e72329f975e11015a95e9a8eec207a5a78748d3c89": 4
|
||||
},
|
||||
"packages/subagent/tool-subagent/tests/harness.ts": {
|
||||
"1a2098d6ff345b66d13f9822385af407ac03acdabeb607c2bd18a75987c410a5": 1
|
||||
},
|
||||
"packages/subagent/tool-subagent/tests/model-selection.spec.ts": {
|
||||
"9a362ded6cf17e795dc9cb0c88a672eb17c03d90ae87987f53dbb3a3f6bcc467": 1
|
||||
},
|
||||
@@ -1920,10 +1891,7 @@
|
||||
"e770821c1f637aca0a346d5f6e6f0873ca4d7e3b070b557cddf773c160ec599a": 1
|
||||
},
|
||||
"packages/subagent/tool-subagent/tests/tool-subagent.spec.ts": {
|
||||
"65e1dff6ee6fedd8c89582d5197e791126c328ff859ce4509a69619afbc4c63f": 1,
|
||||
"719322ba287998bcfb906150f393012229c1ec3e4b59bd106469319ba402a09a": 1,
|
||||
"a5bde0aa0b7d2193204df547d0a9b7e9e116afead259c1914dc93486a8af4f0a": 1,
|
||||
"b6277d8ca00f4653c46b62f27a1743fdbb00465b2aaa4687ea0cb4119adee4e3": 1
|
||||
"719322ba287998bcfb906150f393012229c1ec3e4b59bd106469319ba402a09a": 1
|
||||
},
|
||||
"packages/subprocess/subprocess-local/tests/linux-scope.spec.ts": {
|
||||
"8497f236582c842e59edf49dce625d59d16a828048731d7c2b9625bb1270f32f": 1
|
||||
@@ -2162,9 +2130,6 @@
|
||||
"packages/workflow/tool-workflow/tests/tool-workflow.spec.ts": {
|
||||
"061944ebda38f8da144d88cfa668e6336efc68c33441a9fe3215d8c001124526": 2
|
||||
},
|
||||
"packages/workflow/workflow-ptc/tests/setup.ts": {
|
||||
"eedb5ebd5377314cae568128ab901b78a063216d34f322aa8ad8a33d9e14df54": 1
|
||||
},
|
||||
"packages/workflow/workflow-ptc/tests/workflow-ptc.spec.ts": {
|
||||
"3ddf655a4c4522404be3c2758f0511c7605887c72d9d91cb4ad521433cbdb0b4": 1,
|
||||
"f382d714ca5cc4bc76a5e36d3e1e99c535590cce3ca1b04e5374315e9d8f51df": 1
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { getEventListeners } from 'node:events'
|
||||
import { Context } from '@deepseek-ai/cordis'
|
||||
import { agentEvents, type Agent, type PreStepDecision } from '@deepseek-ai/dsh-agent'
|
||||
import { createUserMessage } from '@deepseek-ai/dsh-llm'
|
||||
import { createToolResultMessage, createUserMessage, ToolCallId } from '@deepseek-ai/dsh-llm'
|
||||
import SessionStore, { SessionId } from '@deepseek-ai/dsh-session'
|
||||
import { WorkflowRunId } from '@deepseek-ai/dsh-workflow'
|
||||
import AgentLoop from '@deepseek-ai/dsh-agent-loop'
|
||||
@@ -10,6 +10,7 @@ import SubagentRuntime from '@deepseek-ai/dsh-subagent'
|
||||
import * as spawn from '@deepseek-ai/dsh-subagent-spawn-in-process'
|
||||
import { MockAdapter, textResponse } from '../packages/core/agent-loop/tests/mock-adapter.ts'
|
||||
import type {} from '@deepseek-ai/dsh-tool-workflow/types'
|
||||
import type {} from '@deepseek-ai/dsh-session-log-deepseek'
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest'
|
||||
// @ts-expect-error Scenario plugins are runtime JavaScript without declaration artifacts.
|
||||
import * as fixtureModule from './fixtures/python-snapshot-workflow-order.mjs'
|
||||
@@ -17,17 +18,17 @@ import * as fixtureModule from './fixtures/python-snapshot-workflow-order.mjs'
|
||||
const config = { parentSessionId: 'advanced-parent', prompt: 'workflow child prompt' }
|
||||
const fixture = fixtureModule as unknown as {
|
||||
name: string
|
||||
apply(ctx: Context, config: { parentSessionId: string; prompt: string }): void
|
||||
apply(ctx: Context, config: { parentSessionId: string; prompt: string; direct?: { prompt: string; callId: string } }): void
|
||||
}
|
||||
const cleanups: (() => Promise<unknown>)[] = []
|
||||
afterEach(async () => { for (const cleanup of cleanups.splice(0).reverse()) await cleanup() })
|
||||
|
||||
async function harness() {
|
||||
async function harness(direct = false) {
|
||||
const ctx = new Context()
|
||||
const store = ctx.plugin(SessionStore)
|
||||
await store
|
||||
cleanups.push(() => store.dispose())
|
||||
const fiber = ctx.plugin(fixture, config)
|
||||
const fiber = ctx.plugin(fixture, { ...config, ...direct ? { direct: { prompt: config.prompt, callId: 'direct-child' } } : {} })
|
||||
await fiber
|
||||
cleanups.push(() => fiber.dispose())
|
||||
const parent = ctx.sessions.create(SessionId(config.parentSessionId))
|
||||
@@ -49,7 +50,7 @@ async function harness() {
|
||||
}
|
||||
|
||||
describe('advanced Python snapshot workflow ordering', () => {
|
||||
it('blocks a real spawned child before its descriptor and first model request', async () => {
|
||||
it('blocks a real activated child before its first model request', async () => {
|
||||
const ctx = new Context()
|
||||
const entered = Promise.withResolvers<Agent>()
|
||||
const order: string[] = []
|
||||
@@ -66,7 +67,7 @@ describe('advanced Python snapshot workflow ordering', () => {
|
||||
return next()
|
||||
})
|
||||
inner.on('session/event', (_session, event) => {
|
||||
if (event.type === 'tool-workflow/agent-start' || event.type === 'subagent/descriptor') order.push(event.type)
|
||||
if (event.type === 'subagent/catalog' || event.type === 'tool-workflow/agent-start') order.push(event.type)
|
||||
})
|
||||
await inner.plugin(fixture, config)
|
||||
},
|
||||
@@ -75,20 +76,21 @@ describe('advanced Python snapshot workflow ordering', () => {
|
||||
await assembly
|
||||
ctx.llm.registerAdapter(['mock'], adapter)
|
||||
const parent = await ctx.agentLoop.create(SessionId(config.parentSessionId), { provider: 'mock', model: 'mock' })
|
||||
const run = await ctx.subagents.start('spawn', {
|
||||
parent, prompt: [{ type: 'text', text: config.prompt }], signal: new AbortController().signal,
|
||||
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,
|
||||
})
|
||||
cleanups.push(() => run.dispose())
|
||||
const child = await entered.promise
|
||||
expect(child.id).toBe(run.id)
|
||||
expect(child.id).toBe(run.childId)
|
||||
expect(adapter.requests).toHaveLength(0)
|
||||
expect(child.session.snapshotEvents().some(event => event.type === 'subagent/descriptor')).toBe(false)
|
||||
expect(child.session.snapshotEvents().some(event => event.type === 'subagent/descriptor')).toBe(true)
|
||||
parent.session.append('tool-workflow/agent-start', {
|
||||
runId: WorkflowRunId('run'), seq: 1, label: 'workflow-child', childId: child.id,
|
||||
})
|
||||
expect((await run.result).output).toEqual([{ type: 'text', text: 'child complete' }])
|
||||
expect(adapter.requests).toHaveLength(1)
|
||||
expect(order).toEqual(['tool-workflow/agent-start', 'subagent/descriptor'])
|
||||
expect(order).toEqual(['subagent/catalog', 'tool-workflow/agent-start'])
|
||||
})
|
||||
|
||||
it('holds the child until the exact parent records the exact member', async () => {
|
||||
@@ -107,6 +109,27 @@ describe('advanced Python snapshot workflow ordering', () => {
|
||||
expect(getEventListeners(h.controller.signal, 'abort')).toHaveLength(0)
|
||||
})
|
||||
|
||||
it('waits for the parent to consume the direct start and acknowledge its request log', async () => {
|
||||
const h = await harness(true)
|
||||
const pending = h.step()
|
||||
h.parent.append('turn/start', { turn: 1 })
|
||||
h.parent.append('step/start', { turn: 1, step: 1 })
|
||||
const callId = ToolCallId('direct-child')
|
||||
h.parent.append('tool/call', { turn: 1, step: 1, callId, name: 'subagent', arguments: '{}' })
|
||||
const result = h.parent.append('tool/result', {
|
||||
turn: 1, step: 1,
|
||||
message: createToolResultMessage({ callId, content: [{ type: 'text', text: 'started subagent child' }], isError: false }),
|
||||
}, { surfaceOp: 'append' })
|
||||
h.parent.append('step/start', { turn: 1, step: 2 })
|
||||
await Promise.resolve()
|
||||
expect(h.next).not.toHaveBeenCalled()
|
||||
h.parent.append('session-log-deepseek/delivery-accepted', {
|
||||
sessionId: h.parent.id, sessionFormatVersion: h.parent.header.version, throughSeq: result.seq,
|
||||
})
|
||||
expect(await pending).toBe(h.decision)
|
||||
expect(h.next).toHaveBeenCalledOnce()
|
||||
})
|
||||
|
||||
it('retains a start recorded before the child reaches its first step', async () => {
|
||||
const h = await harness()
|
||||
h.start()
|
||||
|
||||
@@ -39,6 +39,18 @@ describe('recorded-session corpus policy', () => {
|
||||
.toEqual({ currentRoles: 8, baselineRoles: 8, retainedRoles: 3, retainedScenarios: 3 })
|
||||
})
|
||||
|
||||
it.each([
|
||||
[SESSION_FORMAT_VERSION, 3],
|
||||
[3, SESSION_FORMAT_VERSION],
|
||||
])('counts baseline and current roles independently for parent v%i and child v%i', (parent, child) => {
|
||||
expect(assertSnapshotCorpusPolicy([
|
||||
current,
|
||||
{ key: 'sdk/mixed', selectedVersions: [parent, child] },
|
||||
completeV0,
|
||||
...adjacent,
|
||||
])).toEqual({ currentRoles: 9, baselineRoles: 1, retainedRoles: 3, retainedScenarios: 3 })
|
||||
})
|
||||
|
||||
it('rejects refreshing away every direct V3 migration input', () => {
|
||||
expect(() => assertSnapshotCorpusPolicy([current, completeV0, ...adjacent]))
|
||||
.toThrow('Session corpus lacks v3 coverage: adjacent-migration')
|
||||
@@ -85,15 +97,18 @@ describe('recorded-session corpus policy', () => {
|
||||
])).toThrow('session/v0: v0 retained coverage must be multi-hop, packed-row, retry-failure, shipped-profile')
|
||||
})
|
||||
|
||||
it('rejects absent roles, undeclared history, and mixed selected generations', () => {
|
||||
it('rejects absent roles, undeclared history, and mixed explicitly retained generations', () => {
|
||||
expect(() => assertSnapshotCorpusPolicy([{ key: 'session/empty', selectedVersions: [] }]))
|
||||
.toThrow('session/empty: scenario owns no selected Session role')
|
||||
expect(() => assertSnapshotCorpusPolicy([{ key: 'session/old', selectedVersions: [1] }]))
|
||||
.toThrow(`session/old: selected Session generation v1 must be retained baseline v3 or current v${SESSION_FORMAT_VERSION}`)
|
||||
expect(() => assertSnapshotCorpusPolicy([{ ...completeV0, selectedVersions: [0, SESSION_FORMAT_VERSION] }]))
|
||||
.toThrow(`session/v0: selected Session generation v${SESSION_FORMAT_VERSION} does not match expected v0`)
|
||||
expect(() => assertSnapshotCorpusPolicy([{ ...baseline, selectedVersions: [3, SESSION_FORMAT_VERSION] }]))
|
||||
.toThrow(`session/v3: selected Session generation v${SESSION_FORMAT_VERSION} does not match expected v3`)
|
||||
})
|
||||
|
||||
it.each([2, SESSION_FORMAT_VERSION + 1])('rejects unsupported child generation %s', (version) => {
|
||||
expect(() => assertSnapshotCorpusPolicy([{ key: 'sdk/invalid-child', selectedVersions: [3, version] }]))
|
||||
.toThrow(`sdk/invalid-child: selected Session generation v${version} must be retained baseline v3 or current v${SESSION_FORMAT_VERSION}`)
|
||||
})
|
||||
|
||||
it('rejects future unpinned generations', () => {
|
||||
|
||||
@@ -32,7 +32,7 @@ const REQUIRED_V0_COVERAGE = new Set([
|
||||
const REQUIRED_ADJACENT_COVERAGE = new Set(['adjacent-migration'])
|
||||
|
||||
/**
|
||||
* Require a baseline/current majority and direct coverage of each historical migration source.
|
||||
* Count baseline/current generations per role, require their majority, and preserve direct historical migration coverage.
|
||||
*
|
||||
* @param scenarios - Every owning top-level recorded-session scenario.
|
||||
* @returns Accepted current, baseline, and explicitly retained role counts.
|
||||
@@ -47,29 +47,30 @@ export function assertSnapshotCorpusPolicy(
|
||||
const coverageByVersion = new Map<number, Set<string>>()
|
||||
|
||||
for (const scenario of scenarios) {
|
||||
const selectedVersion = scenario.selectedVersions[0]
|
||||
if (selectedVersion === undefined) {
|
||||
if (scenario.selectedVersions.length === 0) {
|
||||
throw new Error(`${scenario.key}: scenario owns no selected Session role`)
|
||||
}
|
||||
const expectedVersion = scenario.retained?.version ?? selectedVersion
|
||||
if (scenario.retained === undefined) {
|
||||
for (const version of scenario.selectedVersions) {
|
||||
if (version === RETAINED_BASELINE_VERSION) {
|
||||
baselineRoles += 1
|
||||
} else if (version === SESSION_FORMAT_VERSION) {
|
||||
currentRoles += 1
|
||||
} else {
|
||||
throw new Error(
|
||||
`${scenario.key}: selected Session generation v${version} must be retained baseline v${RETAINED_BASELINE_VERSION} or current v${SESSION_FORMAT_VERSION}`,
|
||||
)
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
const expectedVersion = scenario.retained.version
|
||||
const mismatched = scenario.selectedVersions.find(version => version !== expectedVersion)
|
||||
if (mismatched !== undefined) {
|
||||
throw new Error(
|
||||
`${scenario.key}: selected Session generation v${mismatched} does not match expected v${expectedVersion}`,
|
||||
)
|
||||
}
|
||||
if (scenario.retained === undefined) {
|
||||
if (selectedVersion === RETAINED_BASELINE_VERSION) {
|
||||
baselineRoles += scenario.selectedVersions.length
|
||||
} else if (selectedVersion === SESSION_FORMAT_VERSION) {
|
||||
currentRoles += scenario.selectedVersions.length
|
||||
} else {
|
||||
throw new Error(
|
||||
`${scenario.key}: selected Session generation v${selectedVersion} must be retained baseline v${RETAINED_BASELINE_VERSION} or current v${SESSION_FORMAT_VERSION}`,
|
||||
)
|
||||
}
|
||||
continue
|
||||
}
|
||||
const retiredTools = scenario.retained.coverage.length === 1 && scenario.retained.coverage[0] === 'retired-tools'
|
||||
if (!Number.isSafeInteger(scenario.retained.version)
|
||||
|| scenario.retained.version < 0 || scenario.retained.version > SESSION_FORMAT_VERSION
|
||||
|
||||
@@ -25,7 +25,7 @@ from pathlib import Path
|
||||
from typing import TYPE_CHECKING, Callable
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from deepseek_harness import RunResult
|
||||
from deepseek_harness import Notification, RunResult
|
||||
|
||||
|
||||
EXPECTED_TEXT = "runtime smoke ok"
|
||||
@@ -261,7 +261,6 @@ def write_advanced_profile_patch(root: Path, name: str, sessions: Path) -> Path:
|
||||
"config": {
|
||||
"provider": "spawn",
|
||||
"toolName": "subagent",
|
||||
"backgroundMode": "one-shot",
|
||||
},
|
||||
},
|
||||
{"insert": [
|
||||
@@ -295,6 +294,7 @@ class MockModelHandler(BaseHTTPRequestHandler):
|
||||
"""Return deterministic text, worker, and orchestration completions."""
|
||||
|
||||
requests: list[dict[str, object]] = []
|
||||
direct_child_finished = threading.Event()
|
||||
|
||||
def do_POST(self) -> None:
|
||||
if self.path != "/v1/messages":
|
||||
@@ -306,8 +306,13 @@ class MockModelHandler(BaseHTTPRequestHandler):
|
||||
self.send_response(200)
|
||||
self.send_header("content-type", "text/event-stream")
|
||||
self.end_headers()
|
||||
opening = message_start()
|
||||
self.wfile.write(f"event: {opening['type']}\ndata: {json.dumps(opening)}\n\n".encode())
|
||||
self.wfile.flush()
|
||||
chunks = completion_chunks(body)
|
||||
for chunk in chunks:
|
||||
if chunks[0] != opening:
|
||||
raise AssertionError("model fixture response must begin with message_start")
|
||||
for chunk in chunks[1:]:
|
||||
self.wfile.write(f"event: {chunk['type']}\ndata: {json.dumps(chunk)}\n\n".encode())
|
||||
self.wfile.flush()
|
||||
|
||||
@@ -643,8 +648,10 @@ def advanced_tool_followup(
|
||||
},
|
||||
)
|
||||
if call_id == "advanced-direct-child" and tool_name == "subagent":
|
||||
if "DIRECT_CHILD_OK" not in tool_text:
|
||||
raise AssertionError(f"subagent returned no expected child value: {tool_text}")
|
||||
if not tool_text.startswith("started subagent "):
|
||||
raise AssertionError(f"subagent returned no child identity: {tool_text}")
|
||||
if not MockModelHandler.direct_child_finished.wait(timeout=30):
|
||||
raise AssertionError("direct child did not emit its terminal SDK notification")
|
||||
assert_advertised_tool(body, "workflow")
|
||||
return tool_call_chunks(
|
||||
"advanced-workflow",
|
||||
@@ -1532,6 +1539,7 @@ def smoke_sdk_snapshot(base_url: str, executable: Path, update_snapshots: bool)
|
||||
Path(__file__).resolve().parent / "fixtures/python-snapshot-workflow-order.mjs"
|
||||
).as_uri(), "config": {
|
||||
"parentSessionId": SNAPSHOT_SESSION_ID, "prompt": SNAPSHOT_WORKFLOW_CHILD_PROMPT,
|
||||
"direct": {"prompt": SNAPSHOT_DIRECT_CHILD_PROMPT, "callId": "advanced-direct-child"},
|
||||
}},
|
||||
{"id": "snapshot-message-feedback", "name": "@deepseek-ai/dsh-message-feedback",
|
||||
"config": {"maxNoteBytes": 1024}},
|
||||
@@ -1560,9 +1568,18 @@ def smoke_sdk_snapshot(base_url: str, executable: Path, update_snapshots: bool)
|
||||
base_url=base_url,
|
||||
request_timeout_seconds=60,
|
||||
) as harness:
|
||||
result = harness.run(SNAPSHOT_PROMPT, session_id=SNAPSHOT_SESSION_ID)
|
||||
MockModelHandler.direct_child_finished.clear()
|
||||
|
||||
assert result.final_response == SNAPSHOT_FINAL_TEXT, result.final_response
|
||||
def observe_child(notification: "Notification") -> None:
|
||||
if (notification.method == "subagent.finished"
|
||||
and message_text(notification.payload.get("lastAssistantMessage")) == "DIRECT_CHILD_OK"):
|
||||
MockModelHandler.direct_child_finished.set()
|
||||
|
||||
result = harness.run(
|
||||
SNAPSHOT_PROMPT, session_id=SNAPSHOT_SESSION_ID, on_notification=observe_child,
|
||||
)
|
||||
|
||||
assert result.final_response == SNAPSHOT_FINAL_TEXT, (result.final_response, harness.client._runtime_diagnostics())
|
||||
offloads = [event for event in result.events if event.get("type") == "image/offload"]
|
||||
if len(offloads) != 1 or "surfaceOp" in offloads[0]:
|
||||
raise AssertionError(f"advanced snapshot expected one standalone image offload: {offloads}")
|
||||
|
||||
@@ -1127,7 +1127,7 @@
|
||||
"version": 0,
|
||||
"childId": "{{child-1}}",
|
||||
"childCreatedAt": 0,
|
||||
"mode": "one-shot",
|
||||
"mode": "continuable",
|
||||
"label": "Check direct child"
|
||||
}
|
||||
},
|
||||
@@ -1148,7 +1148,7 @@
|
||||
"content": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "DIRECT_CHILD_OK"
|
||||
"text": "started subagent {{child-1}}"
|
||||
}
|
||||
],
|
||||
"isError": false,
|
||||
@@ -1189,9 +1189,44 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "assistant/message",
|
||||
"type": "agent/inbox/spliced",
|
||||
"seq": 50,
|
||||
"time": 0,
|
||||
"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": "assistant/message",
|
||||
"seq": 51,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"turn": 1,
|
||||
"step": 5,
|
||||
@@ -1314,7 +1349,7 @@
|
||||
},
|
||||
{
|
||||
"type": "tool/call",
|
||||
"seq": 51,
|
||||
"seq": 52,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"turn": 1,
|
||||
@@ -1326,7 +1361,7 @@
|
||||
},
|
||||
{
|
||||
"type": "tool-workflow/run-start",
|
||||
"seq": 52,
|
||||
"seq": 53,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"runId": "{{workflow-run}}",
|
||||
@@ -1335,18 +1370,19 @@
|
||||
},
|
||||
{
|
||||
"type": "subagent/catalog",
|
||||
"seq": 53,
|
||||
"seq": 54,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"version": 0,
|
||||
"childId": "{{child-2}}",
|
||||
"childCreatedAt": 0,
|
||||
"mode": "one-shot"
|
||||
"mode": "continuable",
|
||||
"label": "advanced-exe-snapshot child 1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "tool-workflow/agent-start",
|
||||
"seq": 54,
|
||||
"seq": 55,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"runId": "{{workflow-run}}",
|
||||
@@ -1358,7 +1394,7 @@
|
||||
},
|
||||
{
|
||||
"type": "tool-workflow/agent-end",
|
||||
"seq": 55,
|
||||
"seq": 56,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"runId": "{{workflow-run}}",
|
||||
@@ -1368,7 +1404,7 @@
|
||||
},
|
||||
{
|
||||
"type": "tool-workflow/run-end",
|
||||
"seq": 56,
|
||||
"seq": 57,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"runId": "{{workflow-run}}",
|
||||
@@ -1377,7 +1413,7 @@
|
||||
},
|
||||
{
|
||||
"type": "tool/result",
|
||||
"seq": 57,
|
||||
"seq": 58,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"turn": 1,
|
||||
@@ -1400,41 +1436,82 @@
|
||||
}
|
||||
},
|
||||
"sourceEventSeqs": [
|
||||
51
|
||||
52
|
||||
],
|
||||
"surfaceOp": "append"
|
||||
},
|
||||
{
|
||||
"type": "step/end",
|
||||
"seq": 58,
|
||||
"seq": 59,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"turn": 1,
|
||||
"step": 5
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "agent/inbox/spliced",
|
||||
"seq": 60,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"target": "next-step",
|
||||
"start": 0,
|
||||
"removedCount": 1,
|
||||
"inserted": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "step/start",
|
||||
"seq": 59,
|
||||
"seq": 61,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"turn": 1,
|
||||
"step": 6
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "user/message",
|
||||
"seq": 62,
|
||||
"time": 0,
|
||||
"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": "session-log-deepseek/delivery-accepted",
|
||||
"seq": 60,
|
||||
"seq": 63,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"sessionId": "{{parent}}",
|
||||
"sessionFormatVersion": 4,
|
||||
"throughSeq": 59
|
||||
"throughSeq": 62
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "assistant/message",
|
||||
"seq": 61,
|
||||
"seq": 64,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"turn": 1,
|
||||
@@ -1542,7 +1619,7 @@
|
||||
},
|
||||
{
|
||||
"type": "step/end",
|
||||
"seq": 62,
|
||||
"seq": 65,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"turn": 1,
|
||||
@@ -1551,7 +1628,7 @@
|
||||
},
|
||||
{
|
||||
"type": "command/run",
|
||||
"seq": 63,
|
||||
"seq": 66,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"commandId": "{{command:1}}",
|
||||
@@ -1563,7 +1640,7 @@
|
||||
},
|
||||
{
|
||||
"type": "feedback/record",
|
||||
"seq": 64,
|
||||
"seq": 67,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"text": "The session needs a clearer explanation."
|
||||
@@ -1571,7 +1648,7 @@
|
||||
},
|
||||
{
|
||||
"type": "command/done",
|
||||
"seq": 65,
|
||||
"seq": 68,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"commandId": "{{command:1}}",
|
||||
@@ -1581,7 +1658,7 @@
|
||||
},
|
||||
{
|
||||
"type": "feedback/record",
|
||||
"seq": 66,
|
||||
"seq": 69,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"category": "other"
|
||||
@@ -1589,7 +1666,7 @@
|
||||
},
|
||||
{
|
||||
"type": "feedback/message-put",
|
||||
"seq": 67,
|
||||
"seq": 70,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"sessionId": "{{parent}}",
|
||||
@@ -1606,7 +1683,7 @@
|
||||
},
|
||||
{
|
||||
"type": "feedback/message-put",
|
||||
"seq": 68,
|
||||
"seq": 71,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"sessionId": "{{parent}}",
|
||||
@@ -1622,7 +1699,7 @@
|
||||
},
|
||||
{
|
||||
"type": "feedback/message-delete",
|
||||
"seq": 69,
|
||||
"seq": 72,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"sessionId": "{{parent}}",
|
||||
@@ -1631,7 +1708,7 @@
|
||||
},
|
||||
{
|
||||
"type": "turn/end",
|
||||
"seq": 70,
|
||||
"seq": 73,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"turn": 1,
|
||||
@@ -3025,7 +3102,7 @@
|
||||
"sessionId": "{{child-1}}",
|
||||
"event": {
|
||||
"type": "agent/inbox/spliced",
|
||||
"seq": 3,
|
||||
"seq": 4,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"target": "next-step",
|
||||
@@ -3055,7 +3132,7 @@
|
||||
"sessionId": "{{child-1}}",
|
||||
"event": {
|
||||
"type": "agent/inbox/spliced",
|
||||
"seq": 4,
|
||||
"seq": 5,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"target": "next-turn",
|
||||
@@ -3092,7 +3169,7 @@
|
||||
"sessionId": "{{child-1}}",
|
||||
"event": {
|
||||
"type": "turn/start",
|
||||
"seq": 5,
|
||||
"seq": 6,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"turn": 1
|
||||
@@ -3106,7 +3183,7 @@
|
||||
"sessionId": "{{child-1}}",
|
||||
"event": {
|
||||
"type": "agent/inbox/spliced",
|
||||
"seq": 6,
|
||||
"seq": 7,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"target": "next-step",
|
||||
@@ -3123,7 +3200,7 @@
|
||||
"sessionId": "{{child-1}}",
|
||||
"event": {
|
||||
"type": "agent/inbox/spliced",
|
||||
"seq": 7,
|
||||
"seq": 8,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"target": "next-turn",
|
||||
@@ -3146,7 +3223,7 @@
|
||||
"version": 0,
|
||||
"childId": "{{child-1}}",
|
||||
"childCreatedAt": 0,
|
||||
"mode": "one-shot",
|
||||
"mode": "continuable",
|
||||
"label": "Check direct child"
|
||||
}
|
||||
}
|
||||
@@ -3155,16 +3232,80 @@
|
||||
{
|
||||
"method": "session.event",
|
||||
"payload": {
|
||||
"sessionId": "{{child-1}}",
|
||||
"sessionId": "{{parent}}",
|
||||
"event": {
|
||||
"type": "subagent/descriptor",
|
||||
"seq": 8,
|
||||
"type": "tool/result",
|
||||
"seq": 46,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"version": 3,
|
||||
"mode": "one-shot",
|
||||
"provider": "spawn",
|
||||
"label": "Check direct child"
|
||||
"turn": 1,
|
||||
"step": 4,
|
||||
"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": [
|
||||
44
|
||||
],
|
||||
"surfaceOp": "append"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"method": "session.event",
|
||||
"payload": {
|
||||
"sessionId": "{{parent}}",
|
||||
"event": {
|
||||
"type": "step/end",
|
||||
"seq": 47,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"turn": 1,
|
||||
"step": 4
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"method": "session.event",
|
||||
"payload": {
|
||||
"sessionId": "{{parent}}",
|
||||
"event": {
|
||||
"type": "step/start",
|
||||
"seq": 48,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"turn": 1,
|
||||
"step": 5
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"method": "session.event",
|
||||
"payload": {
|
||||
"sessionId": "{{parent}}",
|
||||
"event": {
|
||||
"type": "session-log-deepseek/delivery-accepted",
|
||||
"seq": 49,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"sessionId": "{{parent}}",
|
||||
"sessionFormatVersion": 4,
|
||||
"throughSeq": 48
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3549,6 +3690,47 @@
|
||||
"status": "idle"
|
||||
}
|
||||
},
|
||||
{
|
||||
"method": "session.event",
|
||||
"payload": {
|
||||
"sessionId": "{{parent}}",
|
||||
"event": {
|
||||
"type": "agent/inbox/spliced",
|
||||
"seq": 50,
|
||||
"time": 0,
|
||||
"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}}"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"method": "subagent.finished",
|
||||
"payload": {
|
||||
@@ -3566,94 +3748,13 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"method": "session.event",
|
||||
"payload": {
|
||||
"sessionId": "{{parent}}",
|
||||
"event": {
|
||||
"type": "tool/result",
|
||||
"seq": 46,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"turn": 1,
|
||||
"step": 4,
|
||||
"message": {
|
||||
"role": "tool",
|
||||
"source": {
|
||||
"kind": "tool",
|
||||
"callId": "advanced-direct-child"
|
||||
},
|
||||
"toolCallId": "advanced-direct-child",
|
||||
"content": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "DIRECT_CHILD_OK"
|
||||
}
|
||||
],
|
||||
"isError": false,
|
||||
"id": "{{messageId}}"
|
||||
}
|
||||
},
|
||||
"sourceEventSeqs": [
|
||||
44
|
||||
],
|
||||
"surfaceOp": "append"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"method": "session.event",
|
||||
"payload": {
|
||||
"sessionId": "{{parent}}",
|
||||
"event": {
|
||||
"type": "step/end",
|
||||
"seq": 47,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"turn": 1,
|
||||
"step": 4
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"method": "session.event",
|
||||
"payload": {
|
||||
"sessionId": "{{parent}}",
|
||||
"event": {
|
||||
"type": "step/start",
|
||||
"seq": 48,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"turn": 1,
|
||||
"step": 5
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"method": "session.event",
|
||||
"payload": {
|
||||
"sessionId": "{{parent}}",
|
||||
"event": {
|
||||
"type": "session-log-deepseek/delivery-accepted",
|
||||
"seq": 49,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"sessionId": "{{parent}}",
|
||||
"sessionFormatVersion": 4,
|
||||
"throughSeq": 48
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"method": "session.event",
|
||||
"payload": {
|
||||
"sessionId": "{{parent}}",
|
||||
"event": {
|
||||
"type": "assistant/message",
|
||||
"seq": 50,
|
||||
"seq": 51,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"turn": 1,
|
||||
@@ -3783,7 +3884,7 @@
|
||||
"sessionId": "{{parent}}",
|
||||
"event": {
|
||||
"type": "tool/call",
|
||||
"seq": 51,
|
||||
"seq": 52,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"turn": 1,
|
||||
@@ -3801,7 +3902,7 @@
|
||||
"sessionId": "{{parent}}",
|
||||
"event": {
|
||||
"type": "tool-workflow/run-start",
|
||||
"seq": 52,
|
||||
"seq": 53,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"runId": "{{workflow-run}}",
|
||||
@@ -3823,7 +3924,7 @@
|
||||
"sessionId": "{{child-2}}",
|
||||
"event": {
|
||||
"type": "agent/inbox/spliced",
|
||||
"seq": 3,
|
||||
"seq": 4,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"target": "next-step",
|
||||
@@ -3853,7 +3954,7 @@
|
||||
"sessionId": "{{child-2}}",
|
||||
"event": {
|
||||
"type": "agent/inbox/spliced",
|
||||
"seq": 4,
|
||||
"seq": 5,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"target": "next-turn",
|
||||
@@ -3890,7 +3991,7 @@
|
||||
"sessionId": "{{child-2}}",
|
||||
"event": {
|
||||
"type": "turn/start",
|
||||
"seq": 5,
|
||||
"seq": 6,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"turn": 1
|
||||
@@ -3904,7 +4005,7 @@
|
||||
"sessionId": "{{child-2}}",
|
||||
"event": {
|
||||
"type": "agent/inbox/spliced",
|
||||
"seq": 6,
|
||||
"seq": 7,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"target": "next-step",
|
||||
@@ -3921,7 +4022,7 @@
|
||||
"sessionId": "{{child-2}}",
|
||||
"event": {
|
||||
"type": "agent/inbox/spliced",
|
||||
"seq": 7,
|
||||
"seq": 8,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"target": "next-turn",
|
||||
@@ -3938,13 +4039,14 @@
|
||||
"sessionId": "{{parent}}",
|
||||
"event": {
|
||||
"type": "subagent/catalog",
|
||||
"seq": 53,
|
||||
"seq": 54,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"version": 0,
|
||||
"childId": "{{child-2}}",
|
||||
"childCreatedAt": 0,
|
||||
"mode": "one-shot"
|
||||
"mode": "continuable",
|
||||
"label": "advanced-exe-snapshot child 1"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3955,7 +4057,7 @@
|
||||
"sessionId": "{{parent}}",
|
||||
"event": {
|
||||
"type": "tool-workflow/agent-start",
|
||||
"seq": 54,
|
||||
"seq": 55,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"runId": "{{workflow-run}}",
|
||||
@@ -3967,22 +4069,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"method": "session.event",
|
||||
"payload": {
|
||||
"sessionId": "{{child-2}}",
|
||||
"event": {
|
||||
"type": "subagent/descriptor",
|
||||
"seq": 8,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"version": 3,
|
||||
"mode": "one-shot",
|
||||
"provider": "spawn"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"method": "session.event",
|
||||
"payload": {
|
||||
@@ -4386,7 +4472,7 @@
|
||||
"sessionId": "{{parent}}",
|
||||
"event": {
|
||||
"type": "tool-workflow/agent-end",
|
||||
"seq": 55,
|
||||
"seq": 56,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"runId": "{{workflow-run}}",
|
||||
@@ -4402,7 +4488,7 @@
|
||||
"sessionId": "{{parent}}",
|
||||
"event": {
|
||||
"type": "tool-workflow/run-end",
|
||||
"seq": 56,
|
||||
"seq": 57,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"runId": "{{workflow-run}}",
|
||||
@@ -4417,7 +4503,7 @@
|
||||
"sessionId": "{{parent}}",
|
||||
"event": {
|
||||
"type": "tool/result",
|
||||
"seq": 57,
|
||||
"seq": 58,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"turn": 1,
|
||||
@@ -4440,7 +4526,7 @@
|
||||
}
|
||||
},
|
||||
"sourceEventSeqs": [
|
||||
51
|
||||
52
|
||||
],
|
||||
"surfaceOp": "append"
|
||||
}
|
||||
@@ -4452,7 +4538,7 @@
|
||||
"sessionId": "{{parent}}",
|
||||
"event": {
|
||||
"type": "step/end",
|
||||
"seq": 58,
|
||||
"seq": 59,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"turn": 1,
|
||||
@@ -4461,13 +4547,30 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"method": "session.event",
|
||||
"payload": {
|
||||
"sessionId": "{{parent}}",
|
||||
"event": {
|
||||
"type": "agent/inbox/spliced",
|
||||
"seq": 60,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"target": "next-step",
|
||||
"start": 0,
|
||||
"removedCount": 1,
|
||||
"inserted": []
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"method": "session.event",
|
||||
"payload": {
|
||||
"sessionId": "{{parent}}",
|
||||
"event": {
|
||||
"type": "step/start",
|
||||
"seq": 59,
|
||||
"seq": 61,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"turn": 1,
|
||||
@@ -4476,18 +4579,54 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"method": "session.event",
|
||||
"payload": {
|
||||
"sessionId": "{{parent}}",
|
||||
"event": {
|
||||
"type": "user/message",
|
||||
"seq": 62,
|
||||
"time": 0,
|
||||
"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"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"method": "session.event",
|
||||
"payload": {
|
||||
"sessionId": "{{parent}}",
|
||||
"event": {
|
||||
"type": "session-log-deepseek/delivery-accepted",
|
||||
"seq": 60,
|
||||
"seq": 63,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"sessionId": "{{parent}}",
|
||||
"sessionFormatVersion": 4,
|
||||
"throughSeq": 59
|
||||
"throughSeq": 62
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4498,7 +4637,7 @@
|
||||
"sessionId": "{{parent}}",
|
||||
"event": {
|
||||
"type": "assistant/message",
|
||||
"seq": 61,
|
||||
"seq": 64,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"turn": 1,
|
||||
@@ -4612,7 +4751,7 @@
|
||||
"sessionId": "{{parent}}",
|
||||
"event": {
|
||||
"type": "step/end",
|
||||
"seq": 62,
|
||||
"seq": 65,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"turn": 1,
|
||||
@@ -4627,7 +4766,7 @@
|
||||
"sessionId": "{{parent}}",
|
||||
"event": {
|
||||
"type": "command/run",
|
||||
"seq": 63,
|
||||
"seq": 66,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"commandId": "{{command:1}}",
|
||||
@@ -4645,7 +4784,7 @@
|
||||
"sessionId": "{{parent}}",
|
||||
"event": {
|
||||
"type": "feedback/record",
|
||||
"seq": 64,
|
||||
"seq": 67,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"text": "The session needs a clearer explanation."
|
||||
@@ -4659,7 +4798,7 @@
|
||||
"sessionId": "{{parent}}",
|
||||
"event": {
|
||||
"type": "command/done",
|
||||
"seq": 65,
|
||||
"seq": 68,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"commandId": "{{command:1}}",
|
||||
@@ -4675,7 +4814,7 @@
|
||||
"sessionId": "{{parent}}",
|
||||
"event": {
|
||||
"type": "feedback/record",
|
||||
"seq": 66,
|
||||
"seq": 69,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"category": "other"
|
||||
@@ -4689,7 +4828,7 @@
|
||||
"sessionId": "{{parent}}",
|
||||
"event": {
|
||||
"type": "feedback/message-put",
|
||||
"seq": 67,
|
||||
"seq": 70,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"sessionId": "{{parent}}",
|
||||
@@ -4712,7 +4851,7 @@
|
||||
"sessionId": "{{parent}}",
|
||||
"event": {
|
||||
"type": "feedback/message-put",
|
||||
"seq": 68,
|
||||
"seq": 71,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"sessionId": "{{parent}}",
|
||||
@@ -4734,7 +4873,7 @@
|
||||
"sessionId": "{{parent}}",
|
||||
"event": {
|
||||
"type": "feedback/message-delete",
|
||||
"seq": 69,
|
||||
"seq": 72,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"sessionId": "{{parent}}",
|
||||
@@ -4749,7 +4888,7 @@
|
||||
"sessionId": "{{parent}}",
|
||||
"event": {
|
||||
"type": "turn/end",
|
||||
"seq": 70,
|
||||
"seq": 73,
|
||||
"time": 0,
|
||||
"data": {
|
||||
"turn": 1,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{"type":"session","version":4,"id":"{{child-1}}","createdAt":0,"cwd":"{{cwd}}","parentSession":"{{parent}}","isSeeded":false,"origin":"subagent","delegationDepth":1}
|
||||
{"type":"subagent/descriptor","data":{"version":3,"mode":"continuable","provider":"spawn","label":"Check direct child","agentProvider":"deepseek-official","agentModel":"smoke-model","agentReasoningEffort":"high"}}
|
||||
{"type":"sandbox/mode","data":{"mode":"danger-full-access","source":"delegation"}}
|
||||
{"type":"approval/policy","data":{"policy":"never","source":"delegation"}}
|
||||
{"type":"permission/preset","data":{"preset":"danger-full-access"}}
|
||||
@@ -7,7 +8,6 @@
|
||||
{"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":"subagent/descriptor","data":{"version":3,"mode":"one-shot","provider":"spawn","label":"Check direct child"}}
|
||||
{"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"}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{"type":"session","version":4,"id":"{{child-2}}","createdAt":0,"cwd":"{{cwd}}","parentSession":"{{parent}}","isSeeded":false,"origin":"subagent","delegationDepth":1}
|
||||
{"type":"subagent/descriptor","data":{"version":3,"mode":"continuable","provider":"spawn","label":"advanced-exe-snapshot child 1","agentProvider":"deepseek-official","agentModel":"smoke-model","agentReasoningEffort":"high"}}
|
||||
{"type":"sandbox/mode","data":{"mode":"danger-full-access","source":"delegation"}}
|
||||
{"type":"approval/policy","data":{"policy":"never","source":"delegation"}}
|
||||
{"type":"permission/preset","data":{"preset":"danger-full-access"}}
|
||||
@@ -7,7 +8,6 @@
|
||||
{"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":"subagent/descriptor","data":{"version":3,"mode":"one-shot","provider":"spawn"}}
|
||||
{"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"}
|
||||
|
||||
@@ -44,22 +44,25 @@
|
||||
{"type":"session-log-deepseek/delivery-accepted","data":{"sessionId":"{{parent}}","sessionFormatVersion":4,"throughSeq":41}}
|
||||
{"type":"assistant/message","data":{"turn":1,"step":4,"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":4,"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":"one-shot","label":"Check direct child"}}
|
||||
{"type":"tool/result","data":{"turn":1,"step":4,"message":{"role":"tool","source":{"kind":"tool","callId":"advanced-direct-child"},"toolCallId":"advanced-direct-child","content":[{"type":"text","text":"DIRECT_CHILD_OK"}],"isError":false,"id":"{{messageId}}"}},"sourceEventSeqs":[44],"surfaceOp":"append"}
|
||||
{"type":"subagent/catalog","data":{"version":0,"childId":"{{child-1}}","childCreatedAt":0,"mode":"continuable","label":"Check direct child"}}
|
||||
{"type":"tool/result","data":{"turn":1,"step":4,"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":[44],"surfaceOp":"append"}
|
||||
{"type":"step/end","data":{"turn":1,"step":4}}
|
||||
{"type":"step/start","data":{"turn":1,"step":5}}
|
||||
{"type":"session-log-deepseek/delivery-accepted","data":{"sessionId":"{{parent}}","sessionFormatVersion":4,"throughSeq":48}}
|
||||
{"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":"assistant/message","data":{"turn":1,"step":5,"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":5,"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"}}
|
||||
{"type":"subagent/catalog","data":{"version":0,"childId":"{{child-2}}","childCreatedAt":0,"mode":"one-shot"}}
|
||||
{"type":"subagent/catalog","data":{"version":0,"childId":"{{child-2}}","childCreatedAt":0,"mode":"continuable","label":"advanced-exe-snapshot child 1"}}
|
||||
{"type":"tool-workflow/agent-start","data":{"runId":"{{workflow-run}}","seq":1,"label":"workflow-child","phase":"Delegate","childId":"{{child-2}}"}}
|
||||
{"type":"tool-workflow/agent-end","data":{"runId":"{{workflow-run}}","seq":1,"outcome":"completed"}}
|
||||
{"type":"tool-workflow/run-end","data":{"runId":"{{workflow-run}}","stopReason":"completed"}}
|
||||
{"type":"tool/result","data":{"turn":1,"step":5,"message":{"role":"tool","source":{"kind":"tool","callId":"advanced-workflow"},"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,"id":"{{messageId}}"}},"sourceEventSeqs":[51],"surfaceOp":"append"}
|
||||
{"type":"tool/result","data":{"turn":1,"step":5,"message":{"role":"tool","source":{"kind":"tool","callId":"advanced-workflow"},"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,"id":"{{messageId}}"}},"sourceEventSeqs":[52],"surfaceOp":"append"}
|
||||
{"type":"step/end","data":{"turn":1,"step":5}}
|
||||
{"type":"agent/inbox/spliced","data":{"target":"next-step","start":0,"removedCount":1,"inserted":[]}}
|
||||
{"type":"step/start","data":{"turn":1,"step":6}}
|
||||
{"type":"session-log-deepseek/delivery-accepted","data":{"sessionId":"{{parent}}","sessionFormatVersion":4,"throughSeq":59}}
|
||||
{"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":"session-log-deepseek/delivery-accepted","data":{"sessionId":"{{parent}}","sessionFormatVersion":4,"throughSeq":62}}
|
||||
{"type":"assistant/message","data":{"turn":1,"step":6,"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":6}}
|
||||
{"type":"command/run","data":{"commandId":"{{command:1}}","name":"feedback","source":{"kind":"user"}}}
|
||||
|
||||
@@ -497,11 +497,6 @@
|
||||
"symbol": "PromptContext",
|
||||
"source": "packages/core/system-prompt/src/index.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/subsystems/system-prompt.md",
|
||||
"symbol": "AssembledContext",
|
||||
"source": "packages/core/system-prompt/src/index.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/subsystems/system-prompt.md",
|
||||
"symbol": "PromptSection",
|
||||
@@ -1586,16 +1581,6 @@
|
||||
"symbol": "SubagentInterruptAuthority",
|
||||
"source": "packages/subagent/subagent/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/subsystems/subagent.md",
|
||||
"symbol": "SubagentDescendantListEntry",
|
||||
"source": "packages/subagent/subagent/src/list-children.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/subsystems/subagent.md",
|
||||
"symbol": "ContinuableStart",
|
||||
"source": "packages/subagent/subagent/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/subsystems/subagent.md",
|
||||
"symbol": "ContinuableCreateRequest",
|
||||
@@ -2362,6 +2347,16 @@
|
||||
"symbol": "WorkspaceChanges",
|
||||
"source": "packages/deliverables/workspace-changes/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/subsystems/subagent.md",
|
||||
"symbol": "SubagentActivationSpec",
|
||||
"source": "packages/subagent/subagent/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/subsystems/subagent.md",
|
||||
"symbol": "SubagentActivation",
|
||||
"source": "packages/subagent/subagent/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/subsystems/product-telemetry.md",
|
||||
"symbol": "ProductTelemetryScalar",
|
||||
@@ -2372,6 +2367,16 @@
|
||||
"symbol": "ProductTelemetryRecord",
|
||||
"source": "packages/host/product-telemetry-otel/src/index.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/subsystems/subagent.md",
|
||||
"symbol": "SubagentDescendantListEntry",
|
||||
"source": "packages/subagent/subagent/src/list-children.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/subsystems/system-prompt.md",
|
||||
"symbol": "AssembledContext",
|
||||
"source": "packages/core/system-prompt/src/index.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/subsystems/worktrees.md",
|
||||
"symbol": "CreateWorktreeRequest",
|
||||
|
||||
Reference in new issue
Block a user