diff --git a/apps/cli/tests/optional-bundle-transitions.e2e.ts b/apps/cli/tests/optional-bundle-transitions.e2e.ts index c63adb0823..4f514fc82d 100644 --- a/apps/cli/tests/optional-bundle-transitions.e2e.ts +++ b/apps/cli/tests/optional-bundle-transitions.e2e.ts @@ -189,7 +189,7 @@ describe.skipIf(!existsSync(join(root, 'apps/cli/lib/bin.js')))('Official option expect(names(minimal.agent)).toContain('session_search') await turn(coded.agent, 'Read this session lineage through run_code.', [ toolCallResponse('ptc-search', 'run_code', { - code: 'console.log(await tools.session_trace({}));', + code: 'Harness.inspect(await tools.session_trace({}));', description: 'Read the current session lineage', }), textResponse('PTC_SEARCH_DONE'), ]) @@ -340,7 +340,7 @@ describe.skipIf(!existsSync(join(root, 'apps/cli/lib/bin.js')))('Official option if (coded === undefined) throw new Error('the PTC Agent was not created') await turn(coded, 'Read this session lineage and load the badge through one program.', [ toolCallResponse('combined-program', 'run_code', { - code: 'console.log(await tools.session_trace({})); console.log(await tools.skill({ name: "dsh-badge" }));', + code: 'Harness.inspect(await tools.session_trace({})); Harness.inspect(await tools.skill({ name: "dsh-badge" }));', description: 'Use optional session search and badge together', }), textResponse('COMBINED_DONE'), ]) diff --git a/apps/cli/tests/profiles/headless/tests/ptc.e2e.ts b/apps/cli/tests/profiles/headless/tests/ptc.e2e.ts index 0eb7a187b0..f7dee875ae 100644 --- a/apps/cli/tests/profiles/headless/tests/ptc.e2e.ts +++ b/apps/cli/tests/profiles/headless/tests/ptc.e2e.ts @@ -38,7 +38,7 @@ import * as CordisInspectProviders from '@deepseek-ai/dsh-tool-cordis/host' */ const PERSONA = 'You are a coding agent. You work by writing TypeScript programs for run_code: ' - + 'batch related tool work into one program and print or return ONLY the findings that matter.' + + 'batch related tool work into one program and inspect ONLY the findings that matter.' const WORKSPACE_PROBE = 'dragonfruit-8675309' let ctx: Context | undefined @@ -106,14 +106,17 @@ function runCode( }) } -/** Read the optional completion from a successful canonical `run_code` value. */ +/** Read a JSON value explicitly inspected by the program. */ function completion(result: ToolExecutionResult): unknown { if (result.isError) { throw new Error(result.content.filter(block => block.type === 'text').map(block => block.text).join('\n')) } const value = result.value if (typeof value !== 'object' || value === null || Array.isArray(value)) throw new Error('invalid run_code result') - return value.result + if (!Array.isArray(value.logs)) throw new Error('run_code result has no captured output') + const text = value.logs.at(-1) + if (typeof text !== 'string') throw new Error('run_code did not inspect a JSON value') + return JSON.parse(text) } async function mountRuntime(harness: Context): Promise { @@ -184,7 +187,7 @@ describe('PTC mode typed values: keyless real-process contracts', () => { exposesInfo: 'info' in error, }; } - return { length: large.length, failure }; + Harness.inspect(JSON.stringify({ length: large.length, failure })); `)) expect(value).toEqual({ @@ -211,12 +214,12 @@ describe('PTC mode typed values: keyless real-process contracts', () => { description: 'Run completion marker in background', run_in_background: true, }); - return started.jobId; + Harness.inspect(JSON.stringify(started.jobId)); `)) expect(jobId).toMatch(/^bash-[0-9a-f]{16}$/) const polled = completion(await runCode(ctx, ` - return await tools.job_output({ job_id: ${JSON.stringify(jobId)}, wait: true, timeout_ms: 5000 }); + Harness.inspect(JSON.stringify(await tools.job_output({ job_id: ${JSON.stringify(jobId)}, wait: true, timeout_ms: 5000 }))); `)) if (typeof polled !== 'object' || polled === null || Array.isArray(polled)) throw new Error('invalid job_output completion') const taskOutput = polled as Record @@ -231,7 +234,7 @@ describe('PTC mode typed values: keyless real-process contracts', () => { const pre = new AbortController() pre.abort('pre-aborted') const preResult = await runCode(ctx, ` - return await tools.bash({ command: 'sleep 10', description: 'Must never start', run_in_background: true }); + Harness.inspect(JSON.stringify(await tools.bash({ command: 'sleep 10', description: 'Must never start', run_in_background: true }))); `, pre.signal) expect(preResult.isError).toBe(true) expect(ctx.jobs.list()).toEqual([]) @@ -246,7 +249,7 @@ describe('PTC mode typed values: keyless real-process contracts', () => { const afterPublication = new AbortController() const running = runCode(ctx, ` const started = await tools.bash({ command: 'sleep 10', description: 'Wait for explicit task kill', run_in_background: true }); - console.log(started.jobId); + Harness.inspect(started.jobId); await new Promise(() => {}); `, afterPublication.signal) await Promise.race([ @@ -265,11 +268,11 @@ describe('PTC mode typed values: keyless real-process contracts', () => { expect(ctx.jobs.list()[0]).toMatchObject({ id: job!.id, status: 'running' }) const killed = completion(await runCode(ctx, ` - return await tools.job_kill({ job_id: ${JSON.stringify(job!.id)}, reason: 'test owns cancellation' }); + Harness.inspect(JSON.stringify(await tools.job_kill({ job_id: ${JSON.stringify(job!.id)}, reason: 'test owns cancellation' }))); `)) expect(killed).toMatchObject({ outcome: 'cancellation-requested', job: { id: job!.id } }) const settled = completion(await runCode(ctx, ` - return await tools.job_output({ job_id: ${JSON.stringify(job!.id)}, wait: true, timeout_ms: 5000 }); + Harness.inspect(JSON.stringify(await tools.job_output({ job_id: ${JSON.stringify(job!.id)}, wait: true, timeout_ms: 5000 }))); `)) expect(settled).toMatchObject({ job: { id: job!.id, status: 'killed' } }) }) @@ -280,7 +283,7 @@ describe('PTC mode typed values: keyless real-process contracts', () => { const controller = new AbortController() const startedAt = Date.now() const pending = runCode(ctx, ` - return await tools.bash({ command: 'sleep 10', description: 'Run cancellable foreground command' }); + Harness.inspect(JSON.stringify(await tools.bash({ command: 'sleep 10', description: 'Run cancellable foreground command' }))); `, controller.signal) setTimeout(() => { controller.abort('stop-foreground') }, 200) const result = await pending @@ -306,7 +309,7 @@ describe('PTC mode typed values: keyless real-process contracts', () => { const inspected = await tools.cordis_inspect_query({ platform: provider.platform, provider: provider.id, method: provider.methods[0].name, }); - return { provider: provider.id, names: inspected.data.tools.map(tool => tool.name) }; + Harness.inspect(JSON.stringify({ provider: provider.id, names: inspected.data.tools.map(tool => tool.name) })); `, testToolSignal, agent)) expect(value).toEqual({ provider: 'Tool', names: ['cordis_inspect_list', 'cordis_inspect_query'] }) @@ -335,7 +338,7 @@ describe.skipIf(!process.env.DEEPSEEK_API_KEY)('PTC mode: real model writes a pr type: 'text', text: 'Using one run_code program: run `echo alpha-7` with the bash tool, run `echo beta-9` with the bash tool, ' + 'then write both outputs joined by a plus sign into combined.txt (bash heredoc or redirect), ' - + 'and return only the joined string.', + + 'and inspect the joined string. Reply with that string only.', }], source: { kind: 'user' } })) await waitForIdle(ctx, agent) const events: readonly SessionEvent[] = agent.session.snapshotEvents() diff --git a/apps/web/tests/official-subagent-bundle.e2e.ts b/apps/web/tests/official-subagent-bundle.e2e.ts index 8dad28b2a1..3f978e9faa 100644 --- a/apps/web/tests/official-subagent-bundle.e2e.ts +++ b/apps/web/tests/official-subagent-bundle.e2e.ts @@ -97,7 +97,7 @@ describe.skipIf(MODE === 'record')('web snapshot: Official native subagent bundl scaffold = await launchWebScaffold({ developerTools: true, profile: { packages: [{ dir: PACKAGE }] }, extraOverlayPath: [fileURLToPath(new URL('./pin-browse-picker.overlay.yml', import.meta.url)), overlay], - replayFixture: FIXTURE, compareReplaySession: true, + replayFixture: FIXTURE, replayOverride: join(DIR, 'replay.override.json'), compareReplaySession: true, }) expect((await scaffold.ctx.pluginManager.listBundles()).find(bundle => bundle.name === BUNDLE)) .toMatchObject({ official: true, availability: 'profile', enabled: false }) @@ -179,7 +179,7 @@ describe.skipIf(MODE === 'record')('web snapshot: Official native subagent bundl it.skipIf(MODE !== 'replay')('keeps the owned fixture artifacts closed', async () => { await assertFixtureInventory(DIR, [ - 'session.v4.jsonl', 'system-prompt.expected.md', 'tool-schemas.expected.json', 'ui.expected.md', 'workspace.expected', + 'session.v4.jsonl', 'system-prompt.expected.md', 'tool-schemas.expected.json', 'ui.expected.md', 'workspace.expected', 'replay.override.json', ]) }) }) diff --git a/apps/web/tests/present.e2e.ts b/apps/web/tests/present.e2e.ts index c8d4abc172..1952868a76 100644 --- a/apps/web/tests/present.e2e.ts +++ b/apps/web/tests/present.e2e.ts @@ -57,7 +57,7 @@ fs.appendFileSync(${JSON.stringify(openLog)}, JSON.stringify({ path, action, con openInAppEnvironment: createLaunchEnvironmentSnapshot([{ source: 'process', values: { SSH_CONNECTION: '10.0.0.2 55000 10.0.0.9 22' } }]), extraOverlayPath: fileURLToPath(new URL('./present.overlay.yml', import.meta.url)), agentPresets: { default: 'ptc' }, compareReplaySession: true, - ...(MODE === 'record' ? {} : { replayFixture: FIXTURE }), + ...(MODE === 'record' ? {} : { replayFixture: FIXTURE, replayOverride: fileURLToPath(new URL('../../../snapshots/web/present/replay.override.json', import.meta.url)) }), }) // File associations belong to the desktop rather than the recorded Session. const controller = scaffold.ctx.get('sessionController') diff --git a/apps/web/tests/ptc-escalation.e2e.ts b/apps/web/tests/ptc-escalation.e2e.ts index 3f3a74a65a..c6aa2d0496 100644 --- a/apps/web/tests/ptc-escalation.e2e.ts +++ b/apps/web/tests/ptc-escalation.e2e.ts @@ -34,7 +34,7 @@ describe('web e2e: PTC program sandbox escalation', () => { scaffold = await launchWebScaffold({ agentPresets: { default: 'ptc' }, compareReplaySession: true, - ...(MODE === 'record' ? {} : { replayFixture: FIXTURE, paceMs: 15 }), + ...(MODE === 'record' ? {} : { replayFixture: FIXTURE, replayOverride: join(SNAPSHOT_DIR, 'replay.override.json'), paceMs: 15 }), }) scaffold.ctx.on('session/event', (_session, event: SessionEvent) => { events.push(event) }) browser = await chromium.launch() @@ -90,6 +90,6 @@ describe('web e2e: PTC program sandbox escalation', () => { }, 300_000) it.skipIf(MODE === 'record')('keeps the fixture inventory closed', async () => { - await assertFixtureInventory(SNAPSHOT_DIR, ['session.v3.jsonl', 'approval.expected.md', 'workspace.expected']) + await assertFixtureInventory(SNAPSHOT_DIR, ['session.v3.jsonl', 'approval.expected.md', 'workspace.expected', 'replay.override.json']) }) }) diff --git a/apps/web/tests/ptc-round.e2e.ts b/apps/web/tests/ptc-round.e2e.ts index 1a14c2fedc..9e0f325b9f 100644 --- a/apps/web/tests/ptc-round.e2e.ts +++ b/apps/web/tests/ptc-round.e2e.ts @@ -37,7 +37,7 @@ describe('web e2e: PTC mode round renders nested sub-calls', () => { scaffold = await launchWebScaffold({ agentPresets: { default: 'ptc' }, compareReplaySession: true, - ...(MODE === 'record' ? {} : { replayFixture: FIXTURE, paceMs: 15 }), + ...(MODE === 'record' ? {} : { replayFixture: FIXTURE, replayOverride: fileURLToPath(new URL('../../../snapshots/web/ptc-round/replay.override.json', import.meta.url)), paceMs: 15 }), }) scaffold.ctx.on('session/event', (_session, event: SessionEvent) => { sessionEvents.push(event) }) browser = await chromium.launch() diff --git a/apps/web/tests/standalone-string-editor.e2e.ts b/apps/web/tests/standalone-string-editor.e2e.ts index a04dc6e96c..77332a14cd 100644 --- a/apps/web/tests/standalone-string-editor.e2e.ts +++ b/apps/web/tests/standalone-string-editor.e2e.ts @@ -30,7 +30,7 @@ describe('web snapshot: standalone string editor through PTC', () => { ], profile: { packages: [] }, compareReplaySession: true, - ...(MODE === 'record' ? {} : { replayFixture: FIXTURE }), + ...(MODE === 'record' ? {} : { replayFixture: FIXTURE, replayOverride: join(DIR, 'replay.override.json') }), }) expect(scaffold.ctx.tools.schemas().some(tool => tool.name === 'str_replace_editor')).toBe(true) browser = await chromium.launch() @@ -82,7 +82,7 @@ describe('web snapshot: standalone string editor through PTC', () => { it.skipIf(MODE !== 'replay')('keeps the owned fixture artifacts closed', async () => { await assertFixtureInventory(DIR, [ - 'session.v4.jsonl', 'system-prompt.expected.md', 'tool-schemas.expected.json', 'ui.expected.md', 'workspace.expected', + 'session.v4.jsonl', 'system-prompt.expected.md', 'tool-schemas.expected.json', 'ui.expected.md', 'workspace.expected', 'replay.override.json', ]) }) }) diff --git a/docs/config-catalog.md b/docs/config-catalog.md index d6284aee3c..eb6f427b42 100644 --- a/docs/config-catalog.md +++ b/docs/config-catalog.md @@ -2855,6 +2855,8 @@ export interface Config { executionMode?: 'per-call' | 'persistent-in-memory' | 'vm-script' /** Retain a Script process and context per stateOwner; only supported with executionMode: vm-script. */ scriptPersistence?: boolean + /** Expose console with log, info, warn, error and debug using Harness.inspect output; otherwise omit the global. */ + enableConsole?: boolean /** Default elapsed deadline, including nested tool and approval waits. */ timeoutMs?: number /** Maximum numeric elapsed budget accepted by resolve. */ diff --git a/docs/config-catalog.zh.md b/docs/config-catalog.zh.md index 70e75f8142..09f0f83ee9 100644 --- a/docs/config-catalog.zh.md +++ b/docs/config-catalog.zh.md @@ -2857,6 +2857,8 @@ export interface Config { executionMode?: 'per-call' | 'persistent-in-memory' | 'vm-script' /** Retain a Script process and context per stateOwner; only supported with executionMode: vm-script. */ scriptPersistence?: boolean + /** Expose console with log, info, warn, error and debug using Harness.inspect output; otherwise omit the global. */ + enableConsole?: boolean /** Default elapsed deadline, including nested tool and approval waits. */ timeoutMs?: number /** Maximum numeric elapsed budget accepted by resolve. */ diff --git a/docs/subsystems/ptc-runtime.md b/docs/subsystems/ptc-runtime.md index b1820bc264..57aa64eaa4 100644 --- a/docs/subsystems/ptc-runtime.md +++ b/docs/subsystems/ptc-runtime.md @@ -26,6 +26,8 @@ interface PtcRunRequest { * becomes {@link PtcRunResult.value}. */ program: string + /** Discard the completion without JSON validation when true; omission preserves the completion value. */ + discardCompletion?: boolean /** * Object-identity owner for optional in-memory state. Omission requests a fresh * environment. Consumers must await runtime.release(owner) when the owner ends. diff --git a/docs/subsystems/ptc-runtime.zh.md b/docs/subsystems/ptc-runtime.zh.md index 9aee80d258..016a072fc5 100644 --- a/docs/subsystems/ptc-runtime.zh.md +++ b/docs/subsystems/ptc-runtime.zh.md @@ -26,6 +26,8 @@ interface PtcRunRequest { * becomes {@link PtcRunResult.value}. */ program: string + /** Discard the completion without JSON validation when true; omission preserves the completion value. */ + discardCompletion?: boolean /** * Object-identity owner for optional in-memory state. Omission requests a fresh * environment. Consumers must await runtime.release(owner) when the owner ends. diff --git a/docs/tool-catalog.i18n.yaml b/docs/tool-catalog.i18n.yaml index 54f6107f2b..4c4cdfe9fe 100644 --- a/docs/tool-catalog.i18n.yaml +++ b/docs/tool-catalog.i18n.yaml @@ -72,8 +72,8 @@ en: 8c8643ef46fd075f zh: 8c8643ef46fd075f /tool-schema-catalog/deepseek-ai-dsh-tool-executor-ptc/run-code: - en: 7917e16cbc2c366f - zh: c39eb0420544f04a + en: b2a44345d772f1a8 + zh: e6c94bdb4a69176a /tool-schema-catalog/deepseek-ai-dsh-plan-mode: en: b500b530845e26a9 zh: b500b530845e26a9 diff --git a/docs/tool-catalog.md b/docs/tool-catalog.md index 16c42b6393..711947f73f 100644 --- a/docs/tool-catalog.md +++ b/docs/tool-catalog.md @@ -623,7 +623,7 @@ ask_user_question keeps the original blocking behavior by default; set `mode: ti ### `run_code` -Execute a TypeScript program against the available tools. Takes two required arguments: `code`, the BODY of an async function (erasable syntax only; top-level `await` and `return` work), and `description`, a short summary of what the program does. Call tools as `await tools.name(args)` per the declarations in the system prompt. Only what you print or return is program output — curate it. Image-bearing subtool results are attached after the run. The program is the body of an async TypeScript function; only erasable syntax is accepted, with no enum or namespaces. Type annotations are advisory. Top-level await and return are supported. Emit output with console.log(...) or return a lossless JSON value. Only printed values and the returned value become program output. Each call runs in a fresh Node process. Node APIs are available through await import(...). Relative paths use the supplied working directory; process.env starts empty. Direct filesystem access follows this execution's sandbox policy. Each program starts in the Session's current directory. Running programs keep their initial directory. A sandbox escalation approves this complete program for one execution only. Nested tools retain their own policies and approvals. Request wider access only after evidence of a denial. Earlier effects may already have completed: inspect them before explicitly retrying. Programs are never replayed automatically. +Execute a TypeScript program against the available tools. Takes two required arguments: `code`, the BODY of an async function (erasable syntax only; top-level `await` and `return` work), and `description`, a short summary of what the program does. Call tools as `await tools.name(args)` per the declarations in the system prompt. Show selected results with Harness.inspect(...values). Return values are not displayed. Image-bearing subtool results are attached after the run. The program is the body of an async TypeScript function; only erasable syntax is accepted, with no enum or namespaces. Type annotations are advisory. Top-level await and return are supported. Console is unavailable. Use Harness.inspect(...values) for output. Each call runs in a fresh Node process. Node APIs are available through await import(...). Relative paths use the supplied working directory; process.env starts empty. Direct filesystem access follows this execution's sandbox policy. Each program starts in the Session's current directory. Running programs keep their initial directory. A sandbox escalation approves this complete program for one execution only. Nested tools retain their own policies and approvals. Request wider access only after evidence of a denial. Earlier effects may already have completed: inspect them before explicitly retrying. Programs are never replayed automatically. ```json { diff --git a/docs/tool-catalog.zh.md b/docs/tool-catalog.zh.md index bf9a8255bb..0d76272a66 100644 --- a/docs/tool-catalog.zh.md +++ b/docs/tool-catalog.zh.md @@ -627,7 +627,7 @@ ask_user_question 默认保持原有阻塞行为;设置 `mode: timed` 后才 ### `run_code` -针对可用工具执行 TypeScript 程序。接受两个必填参数:`code`,异步函数的函数体(仅限可擦除语法,支持顶层 `await` 和 `return`);以及 `description`,简要说明程序做什么。请按系统提示词中的声明,以 `await tools.name(args)` 调用工具。只有打印或返回的内容构成程序输出,请筛选这些内容。含图片的子工具结果会在运行结束后附加。程序是异步 TypeScript 函数的函数体,只接受可擦除语法,不支持 enum 或 namespace。类型标注只作提示。支持顶层 await 和 return。使用 console.log(...) 或返回无损 JSON 值来输出。只有打印值和返回值成为程序输出。每次调用在新的 Node 进程中运行,可通过 await import(...) 使用 Node API。相对路径以传入的工作目录为准;process.env 初始为空。直接文件系统访问遵循本次执行的沙箱策略。每个程序从 Session 的当前目录启动,运行中保留初始目录。沙箱提权只为本次完整程序执行授权。嵌套工具保留自身策略与审批。只有在获得拒绝证据后才请求更宽访问。先前效果可能已经发生,请在显式重试前检查。程序不会自动重放。 +针对可用工具执行 TypeScript 程序。接受两个必填参数:`code`,异步函数的函数体(仅限可擦除语法,支持顶层 `await` 和 `return`);以及 `description`,简要说明程序做什么。请按系统提示词中的声明,以 `await tools.name(args)` 调用工具。使用 Harness.inspect(...values) 展示选定的结果,返回值不会展示。含图片的子工具结果会在运行结束后附加。程序是异步 TypeScript 函数的函数体,只接受可擦除语法,不支持 enum 或 namespace。类型标注只作提示。支持顶层 await 和 return。Console 不可用。使用 Harness.inspect(...values) 输出。每次调用在新的 Node 进程中运行,可通过 await import(...) 使用 Node API。相对路径以传入的工作目录为准;process.env 初始为空。直接文件系统访问遵循本次执行的沙箱策略。每个程序从 Session 的当前目录启动,运行中保留初始目录。沙箱提权只为本次完整程序执行授权。嵌套工具保留自身策略与审批。只有在获得拒绝证据后才请求更宽访问。先前效果可能已经发生,请在显式重试前检查。程序不会自动重放。 ```json { diff --git a/docs/upgrade-guide/v0.2.2-rc.1/ptc-inspect-output/guide.i18n.yaml b/docs/upgrade-guide/v0.2.2-rc.1/ptc-inspect-output/guide.i18n.yaml new file mode 100644 index 0000000000..a6b3196461 --- /dev/null +++ b/docs/upgrade-guide/v0.2.2-rc.1/ptc-inspect-output/guide.i18n.yaml @@ -0,0 +1,16 @@ +# Bilingual-pair consistency record for guide.md (docs/i18n/README.md): per heading +# section, a hash of its English and Chinese blocks outside code blocks and generated regions. +# After editing either side, bring the other along and re-record with: +# pnpm run verify-translation-pairing --write docs/upgrade-guide/v0.2.2-rc.1/ptc-inspect-output/guide.md +/: + en: b250221af15bffe8 + zh: fa304d6a52d0de50 +/explicit-output-in-typescript-ptc-programs: + en: 064ec0081b5c2946 + zh: 4b67a513ea949220 +/explicit-output-in-typescript-ptc-programs/change: + en: 8dfa94207a557f3a + zh: f4260133406fdcae +/explicit-output-in-typescript-ptc-programs/migration: + en: da0cbb3c426c7a01 + zh: 1f37522e621b7901 diff --git a/docs/upgrade-guide/v0.2.2-rc.1/ptc-inspect-output/guide.md b/docs/upgrade-guide/v0.2.2-rc.1/ptc-inspect-output/guide.md new file mode 100644 index 0000000000..ff930ca24b --- /dev/null +++ b/docs/upgrade-guide/v0.2.2-rc.1/ptc-inspect-output/guide.md @@ -0,0 +1,28 @@ +--- +kind: upgrade-guide +description: "TypeScript PTC programs use Harness.inspect for visible output and start without a console global." +--- + +# Explicit output in TypeScript PTC programs + +English | [中文](guide.zh.md) + +## Change + +TypeScript `run_code` programs display selected values through `Harness.inspect(...values)`. Function return values and final expressions previously appeared in the result; they are now omitted. Each inspected argument is rendered independently, so strings containing `%s` or `%d` remain literal. + +The Node runtime starts without a console global by default. Reading `console` raises `ReferenceError`, and `typeof console` returns `"undefined"`. Its `enableConsole: true` configuration exposes `log`, `info`, `warn`, `error` and `debug` through the same inspection function. + +## Migration + +1. Update programs that print with console or rely on completion values to explicitly inspect the values they need: + + ```js + const result = await tools.bash({ command: 'pwd', description: 'Show current directory' }) + Harness.inspect(result) + ``` + +2. Replace format strings with separate arguments or template strings. For example, use `Harness.inspect('count:', count)` instead of `console.log('count: %d', count)`. +3. Update CUA bridge callbacks to `text => Harness.inspect(text)`. +4. For integrations that require console methods, set `enableConsole: true` on the `@deepseek-ai/dsh-ptc-runtime-node` provider row. Direct runtime consumers can set `discardCompletion: true` to select the same explicit-output behavior. +5. Run an updated program and verify that inspected values appear once and its final expression adds no output. diff --git a/docs/upgrade-guide/v0.2.2-rc.1/ptc-inspect-output/guide.zh.md b/docs/upgrade-guide/v0.2.2-rc.1/ptc-inspect-output/guide.zh.md new file mode 100644 index 0000000000..3dd47bafd8 --- /dev/null +++ b/docs/upgrade-guide/v0.2.2-rc.1/ptc-inspect-output/guide.zh.md @@ -0,0 +1,28 @@ +--- +kind: upgrade-guide +description: "TypeScript PTC 程序通过 Harness.inspect 展示输出,并默认不提供 console 全局变量。" +--- + +# TypeScript PTC 程序的显式输出 + +[English](guide.md) | 中文 + +## 变更 + +TypeScript `run_code` 程序通过 `Harness.inspect(...values)` 展示选定的值。此前函数返回值和末尾表达式的值会进入结果,现在会被省略。各个 inspect 参数独立渲染,因此包含 `%s` 或 `%d` 的字符串保持原样。 + +Node 运行时默认启动时没有 console 全局变量。读取 `console` 会抛出 `ReferenceError`,`typeof console` 返回 `"undefined"`。设置 `enableConsole: true` 后,`log`、`info`、`warn`、`error` 与 `debug` 通过同一 inspection 函数输出。 + +## 迁移 + +1. 将使用 console 输出或依赖完成值的程序,改为显式 inspect 所需的值: + + ```js + const result = await tools.bash({ command: 'pwd', description: 'Show current directory' }) + Harness.inspect(result) + ``` + +2. 将格式字符串改为独立参数或模板字符串。例如,使用 `Harness.inspect('count:', count)` 代替 `console.log('count: %d', count)`。 +3. 将 CUA 桥接回调更新为 `text => Harness.inspect(text)`。 +4. 对于需要 console 方法的集成,在 `@deepseek-ai/dsh-ptc-runtime-node` 提供方配置行中设置 `enableConsole: true`。直接调用运行时的消费方可设置 `discardCompletion: true`,选择相同的显式输出行为。 +5. 运行更新后的程序,确认 inspect 的值只出现一次,且末尾表达式不额外输出。 diff --git a/packages/core/tool-executor-ptc/README.i18n.yaml b/packages/core/tool-executor-ptc/README.i18n.yaml index 67e5cc206e..1bef34beae 100644 --- a/packages/core/tool-executor-ptc/README.i18n.yaml +++ b/packages/core/tool-executor-ptc/README.i18n.yaml @@ -33,8 +33,8 @@ en: eaefd7303f22977d zh: 9c425f9501128224 /deepseek-ai-dsh-tool-executor-ptc/model-experience/ptc-mode-schema-and-system-prompt/what-the-model-sees: - en: 3c4ee15c38a82543 - zh: c032ede27425f917 + en: 68fef21f726658da + zh: 4fd1e1f5340ef68b /deepseek-ai-dsh-tool-executor-ptc/model-experience/ptc-mode-schema-and-system-prompt/what-the-model-sees/typescript-ptc-mode-sdk-instructions-with-bash: en: 3a8df15e4ae32f9e zh: fc4a800ba363e835 diff --git a/packages/core/tool-executor-ptc/README.md b/packages/core/tool-executor-ptc/README.md index 786e94a845..323545a8b5 100644 --- a/packages/core/tool-executor-ptc/README.md +++ b/packages/core/tool-executor-ptc/README.md @@ -91,7 +91,7 @@ Custom host integrations retain their `PtcExecutor` instance and call `reset(age #### What the model sees -PTC mode exposes the generated [`run_code` schema](../../../docs/tool-catalog.md#deepseek-aidsh-tool-executor-ptc), the SDK instructions below, and the generated exact SDK block for the loaded runtime's language. Both runtime languages list `code` before `description`; this is presentation order, not an enforced JSON member order. REPL and Script runtimes share the generic tool description and SDK instructions; their `executionInstructions` supply syntax and state rules. The TypeScript bash example returns a value for function-body runtimes, and prints output for REPL and Script runtimes. The instructions identify generated declarations as program-only bindings. When the current `bash` parameter schema accepts the example arguments, they also show a complete `run_code` call around `tools.bash(...)`. The `tools:sdk` section uses first-party order 5000 and disables prompt-variable interpolation, preserving literal `{{…}}` text in tool descriptions and schemas for both runtime languages. Under `ptc`, the prompt also carries the `tools:ptc-only` rule earlier in the first-party order, so the model reads which tools it may call before it reads what each one is for. Exhausting the tool-call allowance returns a failed `run_code` result with the limit, rejected call number, termination notice, and possible infinite-loop warning. Runtime cleanup diagnostics and captured output remain visible. +PTC mode exposes the generated [`run_code` schema](../../../docs/tool-catalog.md#deepseek-aidsh-tool-executor-ptc), the SDK instructions below, and the generated exact SDK block for the loaded runtime's language. Both runtime languages list `code` before `description`; this is presentation order, not an enforced JSON member order. REPL and Script runtimes share the generic tool description and SDK instructions; their `executionInstructions` supply syntax and state rules. The TypeScript SDK declares `Harness.inspect(...values)` and uses it in the bash example for every execution mode. The instructions identify generated declarations as program-only bindings. When the current `bash` parameter schema accepts the example arguments, they also show a complete `run_code` call around `tools.bash(...)`. The `tools:sdk` section uses first-party order 5000 and disables prompt-variable interpolation, preserving literal `{{…}}` text in tool descriptions and schemas for both runtime languages. Under `ptc`, the prompt also carries the `tools:ptc-only` rule earlier in the first-party order, so the model reads which tools it may call before it reads what each one is for. Exhausting the tool-call allowance returns a failed `run_code` result with the limit, rejected call number, termination notice, and possible infinite-loop warning. Runtime cleanup diagnostics and captured output remain visible. TypeScript programs show selected values with `Harness.inspect(...values)`. Each argument is rendered independently; strings are literal. The executor omits function returns and final expression values. The Node provider omits the console global by default; its `enableConsole` option exposes five methods through the same output function. ##### TypeScript PTC mode SDK instructions with bash @@ -100,14 +100,14 @@ PTC mode exposes the generated [`run_code` schema](../../../docs/tool-catalog.md `run_code` takes two required arguments: `code`, the body of an async TypeScript function (erasable syntax only — no `enum` or namespaces; type annotations are advisory, the code runs type-stripped), and `description`, a short summary of what the program does. The declarations below are SDK bindings for this program. A declaration does not make its name a directly callable tool; only names supplied as separate tool schemas may be called directly. When no separate `bash` schema is supplied, invoke a declared `bash` binding inside `run_code`: -`run_code({ code: "return await tools.bash({ description: 'Show current directory', command: 'pwd' })", description: "Show current directory" })` +`run_code({ code: "Harness.inspect(await tools.bash({ description: 'Show current directory', command: 'pwd' }))", description: "Show current directory" })` Inside the program: - Call tools as `await tools.name(args)` — quoted access for exotic names: `tools["my-tool"](args)`. Every call resolves to the tool's typed canonical JSON value. Tool arguments must be lossless JSON. - A FAILED tool call rejects with `ToolCallError`, whose `toolName` identifies the failed tool and whose `message` is human-readable — `try/catch` it to handle and continue. - Independent read-only calls MAY overlap under `Promise.all` (safe calls run concurrently; mutating calls run alone, in submission order). Sequence dependent work with `await`. -- Emit results with `return` and/or `console.log(...)`. Only what you print or return is program output. A successful tool result containing an image is attached after the run so you can inspect it on the next step; every other intermediate result stays out of the conversation, so extract just what you need. +- Show selected results with `Harness.inspect(...values)`. Return values and final expressions are not displayed. A successful tool result containing an image is attached after the run so you can inspect it on the next step; every other intermediate result stays out of the conversation, so extract just what you need. Program-only SDK bindings: ``` diff --git a/packages/core/tool-executor-ptc/README.zh.md b/packages/core/tool-executor-ptc/README.zh.md index 182070a6d6..f3b94acaa7 100644 --- a/packages/core/tool-executor-ptc/README.zh.md +++ b/packages/core/tool-executor-ptc/README.zh.md @@ -91,7 +91,7 @@ PTC 执行器公开保留的 `run_code` 传输,以及按所加载运行时语 #### 模型看到什么 -PTC mode 会公开生成的 [`run_code` schema](../../../docs/tool-catalog.zh.md#deepseek-aidsh-tool-executor-ptc)、下方 SDK 说明,以及按所加载运行时语言生成的精确 SDK 块。两种运行时语言都将 `code` 列在 `description` 前;这是呈现顺序,不强制 JSON 成员顺序。REPL 与 Script 运行时共用通用工具描述和 SDK 说明;语法与状态规则由各自的 `executionInstructions` 提供。TypeScript bash 示例对函数体运行时返回值,对 REPL 和 Script 运行时打印输出。说明将生成声明明确标为只能在程序内使用的绑定。当当前 `bash` 参数 schema 接受示例参数时,说明还会给出以 `run_code` 包住 `tools.bash(...)` 的完整调用。`tools:sdk` 段使用 first-party 顺序 5000,并关闭提示词变量插值,使两种运行时语言都原样保留工具描述和 schema 中的 `{{…}}` 文本。在 `ptc` 下,提示词还会带上处于更早 first-party 顺序的 `tools:ptc-only` 规则,让模型先读到「可以调用哪些工具」再读「每个工具做什么」。 工具调用额度耗尽时,`run_code` 返回失败结果,说明额度、被拒绝的调用序号、程序已终止,以及可能存在死循环。运行时清理诊断和已捕获的输出仍然可见。 +PTC mode 会公开生成的 [`run_code` schema](../../../docs/tool-catalog.zh.md#deepseek-aidsh-tool-executor-ptc)、下方 SDK 说明,以及按所加载运行时语言生成的精确 SDK 块。两种运行时语言都将 `code` 列在 `description` 前;这是呈现顺序,不强制 JSON 成员顺序。REPL 与 Script 运行时共用通用工具描述和 SDK 说明;语法与状态规则由各自的 `executionInstructions` 提供。TypeScript SDK 声明 `Harness.inspect(...values)`,并在所有执行模式的 bash 示例中使用它。说明将生成声明明确标为只能在程序内使用的绑定。当当前 `bash` 参数 schema 接受示例参数时,说明还会给出以 `run_code` 包住 `tools.bash(...)` 的完整调用。`tools:sdk` 段使用 first-party 顺序 5000,并关闭提示词变量插值,使两种运行时语言都原样保留工具描述和 schema 中的 `{{…}}` 文本。在 `ptc` 下,提示词还会带上处于更早 first-party 顺序的 `tools:ptc-only` 规则,让模型先读到「可以调用哪些工具」再读「每个工具做什么」。 工具调用额度耗尽时,`run_code` 返回失败结果,说明额度、被拒绝的调用序号、程序已终止,以及可能存在死循环。运行时清理诊断和已捕获的输出仍然可见。 TypeScript 程序通过 `Harness.inspect(...values)` 展示选定的值。每个参数独立渲染,字符串保持原样。执行器省略函数返回值和末尾表达式的值。Node 提供方默认不提供 console 全局变量;其 `enableConsole` 选项通过同一输出函数提供五个 console 方法。 ##### 带 bash 的 TypeScript PTC mode SDK 说明 @@ -100,14 +100,14 @@ PTC mode 会公开生成的 [`run_code` schema](../../../docs/tool-catalog.zh.md `run_code` takes two required arguments: `code`, the body of an async TypeScript function (erasable syntax only — no `enum` or namespaces; type annotations are advisory, the code runs type-stripped), and `description`, a short summary of what the program does. The declarations below are SDK bindings for this program. A declaration does not make its name a directly callable tool; only names supplied as separate tool schemas may be called directly. When no separate `bash` schema is supplied, invoke a declared `bash` binding inside `run_code`: -`run_code({ code: "return await tools.bash({ description: 'Show current directory', command: 'pwd' })", description: "Show current directory" })` +`run_code({ code: "Harness.inspect(await tools.bash({ description: 'Show current directory', command: 'pwd' }))", description: "Show current directory" })` Inside the program: - Call tools as `await tools.name(args)` — quoted access for exotic names: `tools["my-tool"](args)`. Every call resolves to the tool's typed canonical JSON value. Tool arguments must be lossless JSON. - A FAILED tool call rejects with `ToolCallError`, whose `toolName` identifies the failed tool and whose `message` is human-readable — `try/catch` it to handle and continue. - Independent read-only calls MAY overlap under `Promise.all` (safe calls run concurrently; mutating calls run alone, in submission order). Sequence dependent work with `await`. -- Emit results with `return` and/or `console.log(...)`. Only what you print or return is program output. A successful tool result containing an image is attached after the run so you can inspect it on the next step; every other intermediate result stays out of the conversation, so extract just what you need. +- Show selected results with `Harness.inspect(...values)`. Return values and final expressions are not displayed. A successful tool result containing an image is attached after the run so you can inspect it on the next step; every other intermediate result stays out of the conversation, so extract just what you need. Program-only SDK bindings: ``` diff --git a/packages/core/tool-executor-ptc/src/ptc.ts b/packages/core/tool-executor-ptc/src/ptc.ts index f509c261e5..9eab6f164f 100644 --- a/packages/core/tool-executor-ptc/src/ptc.ts +++ b/packages/core/tool-executor-ptc/src/ptc.ts @@ -58,7 +58,7 @@ function runCodeFlavor(language: string, execution = { const TYPESCRIPT_FLAVOR = runCodeFlavor('TypeScript') const TYPESCRIPT_FUNCTION_FLAVOR = runCodeFlavor('TypeScript', { programDescription: 'the BODY of an async function (erasable syntax only; top-level `await` and `return` work)', - outputInstruction: 'Only what you print or return is program output — curate it.', + outputInstruction: 'Show selected results with Harness.inspect(...values). Return values are not displayed.', codeDescription: 'The program: the body of an async TypeScript function.', }) const PYTHON_FLAVOR = runCodeFlavor('Python') @@ -684,6 +684,7 @@ export function createRunCodeTool( } const spec = runtime.resolve({ program: args.code, + ...runtime.language === 'typescript' ? { discardCompletion: true } : {}, ...exec.agent === undefined || policy?.mode !== standingPolicy?.mode ? {} : { stateOwner: exec.agent }, bindings: [{ global: 'tools', @@ -725,7 +726,7 @@ export function createRunCodeTool( return { logs: result.logs, ...result.sandbox === undefined ? {} : { sandbox: result.sandbox }, - ...result.value !== undefined ? { result: result.value } : {}, + ...runtime.language !== 'typescript' && result.value !== undefined ? { result: result.value } : {}, } } finally { callSignal.removeEventListener('abort', onOuterAbort) diff --git a/packages/core/tool-executor-ptc/src/ts-types.ts b/packages/core/tool-executor-ptc/src/ts-types.ts index 8026c847d2..e60ff15bfa 100644 --- a/packages/core/tool-executor-ptc/src/ts-types.ts +++ b/packages/core/tool-executor-ptc/src/ts-types.ts @@ -258,16 +258,13 @@ function renderSdkInstructions(programFormat: PtcProgramFormat): string { } /** Render tool execution and output instructions for the selected execution syntax. */ -function renderSdkProgramInstructions(programFormat: PtcProgramFormat): string { - const outputInstruction = programFormat === 'function-body' - ? 'Emit results with `return` and/or `console.log(...)`. Only what you print or return is program output.' - : 'Only values emitted as program output enter the conversation.' +function renderSdkProgramInstructions(): string { return `Inside the program: - Call tools as \`await tools.name(args)\` — quoted access for exotic names: \`tools["my-tool"](args)\`. Every call resolves to the tool's typed canonical JSON value. Tool arguments must be lossless JSON. - A FAILED tool call rejects with \`ToolCallError\`, whose \`toolName\` identifies the failed tool and whose \`message\` is human-readable — \`try/catch\` it to handle and continue. - Independent read-only calls MAY overlap under \`Promise.all\` (safe calls run concurrently; mutating calls run alone, in submission order). Sequence dependent work with \`await\`. -- ${outputInstruction} A successful tool result containing an image is attached after the run so you can inspect it on the next step; every other intermediate result stays out of the conversation, so extract just what you need. +- Show selected results with \`Harness.inspect(...values)\`. Return values and final expressions are not displayed. A successful tool result containing an image is attached after the run so you can inspect it on the next step; every other intermediate result stays out of the conversation, so extract just what you need. Program-only SDK bindings:` } @@ -280,7 +277,7 @@ function acceptsExampleString(schema: JsonSchemaNode | undefined, value: string) } /** Render the bash example only when its literal arguments satisfy the current parameter schema. */ -function renderBashExample(schemas: ToolSdkSchema[], programFormat: PtcProgramFormat): string { +function renderBashExample(schemas: ToolSdkSchema[]): string { const bash = schemas.find(schema => schema.name === 'bash') if (bash === undefined) return '' const parameters = bash.parameters as JsonSchemaNode @@ -291,9 +288,7 @@ function renderBashExample(schemas: ToolSdkSchema[], programFormat: PtcProgramFo const needsDescription = required.includes('description') if (needsDescription && !acceptsExampleString(parameters.properties?.description, 'Show current directory')) return '' const description = needsDescription ? "description: 'Show current directory', " : '' - const program = programFormat === 'function-body' - ? `return await tools.bash({ ${description}command: 'pwd' })` - : `console.log(await tools.bash({ ${description}command: 'pwd' }))` + const program = `Harness.inspect(await tools.bash({ ${description}command: 'pwd' }))` return ` When no separate \`bash\` schema is supplied, invoke a declared \`bash\` binding inside \`run_code\`:\n\n\`run_code({ code: "${program}", description: "Show current directory" })\`` } @@ -322,6 +317,7 @@ export function renderToolsSdk(schemas: readonly ToolSdkSchema[], programFormat: const argsMap = `interface ToolArgsMap {${argsMembers.length > 0 ? `\n${argsMembers.join('\n')}\n` : ''}}` const outputMap = `interface ToolOutputMap {${outputMembers.length > 0 ? `\n${outputMembers.join('\n')}\n` : ''}}` const declaration = [ + 'declare namespace Harness {\n function inspect(...values: unknown[]): void;\n}', argsMap, outputMap, 'type ToolName = keyof ToolOutputMap', @@ -330,6 +326,6 @@ export function renderToolsSdk(schemas: readonly ToolSdkSchema[], programFormat: ].join('\n\n') const jsonValue = 'type JsonValue = null | boolean | number | string | JsonValue[] | { [key: string]: JsonValue }' const instructions = renderSdkInstructions(programFormat) - const programInstructions = renderSdkProgramInstructions(programFormat) - return `${instructions}${renderBashExample(sorted, programFormat)}\n\n${programInstructions}\n\n\`\`\`ts\n${jsonValue}\n\n${declaration}\n\`\`\`` + const programInstructions = renderSdkProgramInstructions() + return `${instructions}${renderBashExample(sorted)}\n\n${programInstructions}\n\n\`\`\`ts\n${jsonValue}\n\n${declaration}\n\`\`\`` } diff --git a/packages/core/tool-executor-ptc/tests/ptc.spec.ts b/packages/core/tool-executor-ptc/tests/ptc.spec.ts index 02f9b64753..4aa604ba84 100644 --- a/packages/core/tool-executor-ptc/tests/ptc.spec.ts +++ b/packages/core/tool-executor-ptc/tests/ptc.spec.ts @@ -63,6 +63,11 @@ class FakeRuntime extends PtcRuntime { } } +/** Output from a program that explicitly prints a selected JSON value. */ +function printed(value: JsonValue): PtcRunResult { + return { logs: [typeof value === 'string' ? value : JSON.stringify(value, null, 2)] } +} + interface SetupOptions { mode?: 'native' | 'ptc' maxSubCallsPerMinute?: number @@ -188,7 +193,7 @@ it('logs detached frozen nested metadata without changing the binding value or l expect(value).toBe('original contents') metadata.cwd = '/later' metadata.path = '/later/other.txt' - return { logs: [], value } + return printed(value) } const result = await runCode(ctx, 'return await tools.location({})', { agent }) expect(result.isError).toBe(false) @@ -423,10 +428,7 @@ describe('mode-aware wire contribution', () => { expect(sdk).toContain('echo: {') expect(sdk).not.toContain('hidden:') - runtime.behavior = request => Promise.resolve({ - logs: [], - value: Object.keys(request.bindings[0]!.functions).sort().join(','), - }) + runtime.behavior = request => Promise.resolve(printed(Object.keys(request.bindings[0]!.functions).sort().join(','))) const result = await runCode(ctx, 'return Object.keys(tools)', { agent }) expect(result.isError).toBe(false) expect(result.content).toEqual([{ type: 'text', text: 'echo' }]) @@ -450,10 +452,7 @@ describe('mode-aware wire contribution', () => { expect(sdk).not.toContain('denied:') expect(sdk).toContain('kept: {') - runtime.behavior = request => Promise.resolve({ - logs: [], - value: Object.keys(request.bindings[0]!.functions).sort().join(','), - }) + runtime.behavior = request => Promise.resolve(printed(Object.keys(request.bindings[0]!.functions).sort().join(','))) const result = await runCode(ctx, 'return Object.keys(tools)', { agent }) expect(result.isError).toBe(false) expect(result.content).toEqual([{ type: 'text', text: 'kept' }]) @@ -519,15 +518,12 @@ describe('mode-aware wire contribution', () => { memberNameProperty: 'toolName', }) const functions = request.bindings[0]!.functions - return Promise.resolve({ - logs: [], - value: JSON.stringify({ - names: Object.keys(functions).sort(), - // Own-property AND prototype-chain reads both come back empty — - // there is no handle a program could re-enter run_code through. - runCode: String(functions[RUN_CODE_NAME]), - }), - }) + return Promise.resolve(printed(JSON.stringify({ + names: Object.keys(functions).sort(), + // Own-property AND prototype-chain reads both come back empty — + // there is no handle a program could re-enter run_code through. + runCode: String(functions[RUN_CODE_NAME]), + }))) } const result = await runCode(ctx, 'program') expect(result.isError).toBe(false) @@ -697,7 +693,7 @@ describe('the per-program tool-call limit', () => { expect(events.filter(event => event.type === 'tool/ptc-dispatch-start')).toHaveLength(2048) expect(events.filter(event => event.type === 'tool/ptc-dispatch')).toHaveLength(2048) - runtime.behavior = async request => ({ logs: [], value: await request.bindings[0]!.functions.echo!({ value: 'next run' }) }) + runtime.behavior = async request => (printed(await request.bindings[0]!.functions.echo!({ value: 'next run' }))) expect((await runCode(ctx, 'next program', { agent })).isError).toBe(false) expect(calls).toHaveLength(2049) }) @@ -885,13 +881,13 @@ describe('the sub-dispatch scheduler (native concurrency contract)', () => { // All three must be START-able without any completion (overlap proof). await expect.poll(() => gated.pending()).toBe(3) gated.releaseAll() - return { logs: [], value: (await all).map(String).join(',') } + return printed((await all).map(String).join(',')) } const result = await runCode(ctx, 'program', { agent }) expect(result.isError).toBe(false) expect(gated.peakLive()).toBe(3) if (result.isError) throw new Error('expected success') - expect(result.value).toMatchObject({ result: 'safe_read:a,safe_read:b,safe_read:c' }) + expect(result.value).toMatchObject({ logs: ['safe_read:a,safe_read:b,safe_read:c'] }) // One start per dispatch, paired with its settle by subCallId, starts in submission order. const starts = events.filter(event => event.type === 'tool/ptc-dispatch-start').map(event => event.data as { subCallId: string }) const settles = events.filter(event => event.type === 'tool/ptc-dispatch').map(event => event.data as { subCallId: string }) @@ -919,7 +915,7 @@ describe('the sub-dispatch scheduler (native concurrency contract)', () => { await expect.poll(() => safe.pending()).toBe(1) safe.releaseAll() await Promise.all([...reads, write, tail]) - return { logs: [], value: 'ordered' } + return printed('ordered') } const result = await runCode(ctx, 'program') expect(result.isError).toBe(false) @@ -941,9 +937,9 @@ describe('the sub-dispatch scheduler (native concurrency contract)', () => { await release.promise return next() }) - runtime.behavior = async request => ({ logs: [], value: await Promise.all( + runtime.behavior = async request => (printed(await Promise.all( [0, 1, 2, 3].map(value => request.bindings[0]!.functions.echo!({ value: String(value) })), - ) }) + ))) const running = runCode(ctx, 'program', { agent }) try { await blocked.promise @@ -967,7 +963,7 @@ describe('the sub-dispatch scheduler (native concurrency contract)', () => { await expect.poll(() => gated.pending()).toBe(2) gated.releaseAll() await all - return { logs: [], value: 'capped' } + return printed('capped') } const result = await runCode(ctx, 'program') expect(result.isError).toBe(false) @@ -997,12 +993,12 @@ describe('the sub-dispatch scheduler (native concurrency contract)', () => { dispose() const message = await request.bindings[0]!.functions.ephemeral!({}) .then(() => 'resolved', (error: unknown) => error instanceof Error ? error.message : String(error)) - return { logs: [], value: message } + return printed(message) } const result = await runCode(ctx, 'program') expect(result.isError).toBe(false) if (result.isError) throw new Error('expected success') - expect(result.value).toMatchObject({ result: 'unknown tool "ephemeral"' }) + expect(result.value).toMatchObject({ logs: ['unknown tool "ephemeral"'] }) expect(calls).toEqual([]) }) @@ -1032,7 +1028,7 @@ describe('the sub-dispatch scheduler (native concurrency contract)', () => { await expect.poll(() => gated.pending()).toBe(2) gated.releaseAll() await all - return { logs: [], value: 'ordered-prepare' } + return printed('ordered-prepare') } const result = await runCode(ctx, 'program') expect(result.isError).toBe(false) @@ -1072,7 +1068,7 @@ describe('the sub-dispatch scheduler (native concurrency contract)', () => { await expect.poll(() => reader.pending()).toBe(1) reader.releaseAll() await r - return { logs: [], value: 'barrier-through-commit' } + return printed('barrier-through-commit') } const result = await runCode(ctx, 'program') expect(result.isError).toBe(false) @@ -1098,7 +1094,7 @@ describe('the sub-dispatch scheduler (native concurrency contract)', () => { gated.release() await expect.poll(() => releasePost !== undefined).toBe(true) queueMicrotask(() => { releasePost!() }) - return { logs: [], value: 'returned-early' } + return printed('returned-early') } const result = await runCode(ctx, 'program', { agent }) expect(result.isError).toBe(false) @@ -1138,7 +1134,7 @@ describe('the sub-dispatch scheduler (native concurrency contract)', () => { gated.release() // releases a (FIFO gate) — invert: release twice reversed is not possible; gated.releaseAll() await all - return { logs: [], value: 'ordered-commit' } + return printed('ordered-commit') } const result = await runCode(ctx, 'program') expect(result.isError).toBe(false) @@ -1210,7 +1206,7 @@ describe('the run_code dispatch bridge', () => { await releaseFirst.promise } const value = await request.bindings[0]!.functions.probe!({ value: request.program }) - return { logs: [], value: JSON.stringify(value) } + return printed(JSON.stringify(value)) } const first = runCode(ctx, 'first') try { @@ -1237,12 +1233,12 @@ describe('the run_code dispatch bridge', () => { const first = await tools.echo!({ value: 'one' }) const second = await tools.echo!({ value: 'two' }) if (typeof first !== 'string' || typeof second !== 'string') throw new Error('echo returned a non-string') - return { logs: [`saw ${first}`], value: second } + return { logs: [`saw ${first}`, second] } } const result = await runCode(ctx, 'const …: string = …', { agent }) expect(result.isError).toBe(false) if (result.isError) throw new Error('expected run_code success') - expect(result.value).toEqual({ logs: ['saw echo:one'], result: 'echo:two' }) + expect(result.value).toEqual({ logs: ['saw echo:one', 'echo:two'] }) expect(result.content).toEqual([{ type: 'text', text: 'saw echo:one\necho:two' }]) expect(calls).toEqual([{ value: 'one' }, { value: 'two' }]) const dispatches = events.filter(event => event.type === 'tool/ptc-dispatch') @@ -1264,7 +1260,7 @@ describe('the run_code dispatch bridge', () => { registerEcho(ctx) runtime.behavior = async (request) => { await request.bindings[0]!.functions.echo!({ value: 'nested' }) - return { logs: [], value: 'done' } + return printed('done') } // Freeze the nested observer's parent correlation. If that were the live @@ -1303,7 +1299,7 @@ describe('the run_code dispatch bridge', () => { })) runtime.behavior = async (request) => { await request.bindings[0]!.functions.finalize!({}) - return { logs: [], value: 'program complete' } + return printed('program complete') } const concluded = await runCode(ctx, 'await tools.finalize({})') @@ -1318,7 +1314,7 @@ describe('the run_code dispatch bridge', () => { }) runtime.behavior = async (request) => { await request.bindings[0]!.functions.finalize!({}).catch(() => undefined) - return { logs: [], value: 'recovered' } + return printed('recovered') } const recovered = await runCode(ctx, 'await tools.finalize({}).catch(() => {})') veto() @@ -1352,7 +1348,7 @@ describe('the run_code dispatch bridge', () => { const tools = request.bindings[0]!.functions const values = await Promise.all([tools.probe!({ id: 'a' }), tools.probe!({ id: 'b' }), tools.probe!({ id: 'c' })]) if (!values.every(value => typeof value === 'string')) throw new Error('probe returned a non-string') - return { logs: [], value: values.join(',') } + return printed(values.join(',')) } const result = await runCode(ctx, 'program') expect(result.isError).toBe(false) @@ -1375,9 +1371,9 @@ describe('the run_code dispatch bridge', () => { runtime.behavior = async (request) => { try { await request.bindings[0]!.functions.fail!({}) - return { logs: [], value: 'unreachable' } + return printed('unreachable') } catch (error: unknown) { - return { logs: [], value: `caught: ${error instanceof Error ? error.message : String(error)}` } + return printed(`caught: ${error instanceof Error ? error.message : String(error)}`) } } const result = await runCode(ctx, 'program') @@ -1409,7 +1405,7 @@ describe('the run_code dispatch bridge', () => { firstLog.resolve(undefined) expect(await third).toBe('echo:three') secondLog.resolve(undefined) - return { logs: [], value: 'complete' } + return printed('complete') } try { const result = await runCode(ctx, 'program', { agent }) @@ -1428,7 +1424,7 @@ describe('the run_code dispatch bridge', () => { const { agent, events } = fakeAgent() runtime.behavior = async (request) => { const value = await request.bindings[0]!.functions.echo!({ value: 'x' }) - return { logs: [], value: value as string } + return printed(value) } const result = await runCode(ctx, 'program', { agent }) expect(result.isError).toBe(false) @@ -1452,12 +1448,12 @@ describe('the run_code dispatch bridge', () => { runtime.behavior = async (request) => { const message = await request.bindings[0]!.functions.echo!({ value: 'x' }) .then(() => 'resolved', (error: unknown) => error instanceof Error ? error.message : String(error)) - return { logs: [], value: message } + return printed(message) } const result = await runCode(ctx, 'program', { agent }) expect(result.isError).toBe(false) if (result.isError) throw new Error('expected success') - expect(result.value).toMatchObject({ result: 'gate exploded' }) + expect(result.value).toMatchObject({ logs: ['gate exploded'] }) // The pipeline failure is final: the body never ran and post-execute was // skipped, yet the settle event still carries the error outcome. expect(calls).toEqual([]) @@ -1488,9 +1484,9 @@ describe('the run_code dispatch bridge', () => { runtime.behavior = async (request) => { try { await request.bindings[0]!.functions.echo!({ value: 'x' }) - return { logs: [], value: 'unreachable' } + return printed('unreachable') } catch (error: unknown) { - return { logs: [], value: `denied: ${error instanceof Error ? error.message : String(error)}` } + return printed(`denied: ${error instanceof Error ? error.message : String(error)}`) } } const result = await runCode(ctx, 'program', { agent }) @@ -1539,7 +1535,7 @@ describe('the run_code dispatch bridge', () => { const { agent, events } = fakeAgent() runtime.behavior = async (request) => { await request.bindings[0]!.functions.echo!({ value: 'x' }) - return { logs: [], value: 'unreachable' } + return printed('unreachable') } const result = await runCode(ctx, 'await tools.echo({ value: "x" })', { agent }) @@ -1567,9 +1563,9 @@ describe('the run_code dispatch bridge', () => { runtime.behavior = async (request) => { try { await request.bindings[0]!.functions.echo!({ value: 'x' }) - return { logs: [], value: 'unreachable' } + return printed('unreachable') } catch (error: unknown) { - return { logs: [], value: error instanceof Error ? error.message : String(error) } + return printed(error instanceof Error ? error.message : String(error)) } } @@ -1593,9 +1589,9 @@ describe('the run_code dispatch bridge', () => { runtime.behavior = async (request) => { try { await request.bindings[0]!.functions.echo!({ value: 'x', big: 1n }) - return { logs: [], value: 'unreachable' } + return printed('unreachable') } catch (error: unknown) { - return { logs: [], value: error instanceof Error ? error.message : String(error) } + return printed(error instanceof Error ? error.message : String(error)) } } const result = await runCode(ctx, 'program', { agent }) @@ -1637,7 +1633,7 @@ describe('the run_code dispatch bridge', () => { runtime.behavior = async (request) => { await request.bindings[0]!.functions.echo!({ value: 'x' }) await request.bindings[0]!.functions.echo!({ value: 'y' }) - return { logs: [], value: 'done' } + return printed('done') } const result = await runCode(ctx, 'program') expect(result.isError).toBe(false) @@ -1674,7 +1670,7 @@ describe('the run_code dispatch bridge', () => { })) runtime.behavior = async (request) => { await request.bindings[0]!.functions.image_result!({}) - return { logs: [], value: 'done' } + return printed('done') } const result = await runCode(ctx, 'program') @@ -1712,7 +1708,7 @@ describe('the run_code dispatch bridge', () => { }) runtime.behavior = async (request) => { await request.bindings[0]!.functions.image_result!({}).catch(() => undefined) - return { logs: [], value: 'done' } + return printed('done') } const result = await runCode(ctx, 'program') @@ -1850,7 +1846,7 @@ describe('the run_code dispatch bridge', () => { const calls = registerEcho(ctx) runtime.behavior = async (request) => { await request.bindings[0]!.functions.echo!({ value: 'x' }) - return { logs: [], value: 'ok' } + return printed('ok') } const result = await runCode(ctx, 'program') expect(result.isError).toBe(false) @@ -1919,8 +1915,8 @@ describe('the run_code dispatch bridge', () => { it.each([ ['logs only', { logs: ['printed'] }, 'printed'], - ['result only', { logs: [], value: 'returned' }, 'returned'], - ['logs plus result', { logs: ['printed'], value: 'returned' }, 'printed\nreturned'], + ['completion only', { logs: [], value: 'returned' }, '(run_code completed with no output)'], + ['logs plus completion', { logs: ['printed'], value: 'returned' }, 'printed'], ['no output', { logs: [] }, '(run_code completed with no output)'], ] as [string, PtcRunResult, string][])('keeps %s in durable content without a result presenter', async (_name, output, text) => { const { ctx, runtime } = await setup({ mode: 'ptc' }) @@ -1991,7 +1987,7 @@ describe('the run_code dispatch bridge', () => { })) runtime.behavior = async (request) => { const value = await request.bindings[0]!.functions.mixed!({}) - return { logs: [], value } + return printed(value) } const result = await runCode(ctx, 'program', { agent }) expect(result.isError).toBe(false) @@ -2010,19 +2006,16 @@ describe('the run_code dispatch bridge', () => { runtime.behavior = async (request) => { const echo = request.bindings[0]!.functions.echo! const catchMessage = (promise: Promise) => promise.then(() => 'resolved', (error: unknown) => error instanceof Error ? error.message : String(error)) - return { - logs: [], - value: [ - // Root undefined must reject up front: the event log rejects it as - // data, and nothing may execute unlogged. - await catchMessage(echo(undefined)), - await catchMessage(echo(Object.defineProperty({}, 'bad', { enumerable: true, get() { throw 'raw-throw' } }))), - await catchMessage(echo(Object.defineProperty({}, 'bad', { enumerable: true, get() { throw new Error('error-throw') } }))), - await catchMessage(echo(new Date(0))), - // A bare function is a value JSON cannot represent at all. - await catchMessage(echo(() => 1)), - ].join(' | '), - } + return printed([ + // Root undefined must reject up front: the event log rejects it as + // data, and nothing may execute unlogged. + await catchMessage(echo(undefined)), + await catchMessage(echo(Object.defineProperty({}, 'bad', { enumerable: true, get() { throw 'raw-throw' } }))), + await catchMessage(echo(Object.defineProperty({}, 'bad', { enumerable: true, get() { throw new Error('error-throw') } }))), + await catchMessage(echo(new Date(0))), + // A bare function is a value JSON cannot represent at all. + await catchMessage(echo(() => 1)), + ].join(' | ')) } const result = await runCode(ctx, 'program', { agent }) const text = (result.content[0] as { text: string }).text @@ -2065,13 +2058,13 @@ describe('the run_code dispatch bridge', () => { let nested: JsonValue = 'leaf' for (let index = 0; index < depth; index++) nested = [nested] const value = await request.bindings[0]!.functions.deep_args!({ nested }) - return { logs: [], value } + return printed(value) } const result = await runCode(ctx, 'return tools.deep_args(...)', { agent }) expect(result.isError).toBe(false) - expect(result.isError ? undefined : result.value).toEqual({ logs: [], result: depth }) + expect(result.isError ? undefined : result.value).toEqual({ logs: [String(depth)] }) expect({ observedDepth, observedLeaf }).toEqual({ observedDepth: depth, observedLeaf: 'leaf' }) const dispatch = session.snapshotEvents().find(event => event.type === 'tool/ptc-dispatch') if (dispatch === undefined) throw new Error('expected a durable tool/ptc-dispatch event') @@ -2126,15 +2119,15 @@ describe('the run_code dispatch bridge', () => { const functions = request.bindings[0]!.functions expect(Object.getPrototypeOf(functions)).toBeNull() const value = await functions['__proto__']!({}) - return { logs: [], value } + return printed(value) } const result = await runCode(ctx, 'program') expect(result.isError).toBe(false) expect(result.content[0]).toEqual({ type: 'text', text: 'proto-tool-ok' }) }) - it('renders every non-string JSON root as pretty JSON while preserving strings raw', async () => { - const { ctx, runtime } = await setup({ mode: 'ptc' }) + it('renders Python completion values as pretty JSON while preserving strings raw', async () => { + const { ctx, runtime } = await setup({ mode: 'ptc', runtime: { language: 'python' } }) runtime.behavior = () => Promise.resolve({ logs: [], value: { n: 42, ok: true } }) expect((await runCode(ctx, 'object')).content[0]).toEqual({ type: 'text', text: '{\n "n": 42,\n "ok": true\n}' }) runtime.behavior = () => Promise.resolve({ logs: [], value: {} }) @@ -2156,8 +2149,8 @@ describe('the run_code dispatch bridge', () => { expect(absent.isError ? undefined : absent.value).toEqual({ logs: [] }) }) - it('renders deeply nested JSON without recursive traversal or quadratic indentation', async () => { - const { ctx, runtime } = await setup({ mode: 'ptc' }) + it('renders deeply nested Python completions without recursive traversal or quadratic indentation', async () => { + const { ctx, runtime } = await setup({ mode: 'ptc', runtime: { language: 'python' } }) let value: JsonValue = { emptyArray: [], emptyObject: {}, @@ -2183,7 +2176,7 @@ describe('the run_code dispatch bridge', () => { runtime.behavior = (request) => { // The fake honors the seam contract for an already-aborted signal. if (request.signal?.aborted) return Promise.resolve({ logs: [], error: { kind: 'abort' as const, message: String(request.signal.reason) } }) - return Promise.resolve({ logs: [], value: 'unreachable' }) + return Promise.resolve(printed('unreachable')) } const controller = new AbortController() controller.abort('too-late') @@ -2209,7 +2202,7 @@ describe('the run_code dispatch bridge', () => { controller.abort('cancelled-mid-run') const message = await request.bindings[0]!.functions.echo!({ value: 'x' }) .then(() => 'resolved', (error: unknown) => error instanceof Error ? error.message : String(error)) - return { logs: [], value: message } + return printed(message) } const result = await runCode(ctx, 'program', { signal: controller.signal }) expect(result.isError).toBe(true) @@ -2887,7 +2880,7 @@ describe('per-program execution controls', () => { expect(ctx.sandboxPolicy.resolve({ session: agent.session }).mode).toBe('read-only') const value = await request.bindings[0]!.functions.echo!({ value: 'nested' }) .then(() => 'unexpected nested grant', () => 'nested approval rejected') - return { logs: [], value } + return printed(value) } const resolver = vi.spyOn(runtime, 'resolve') const result = execute({ sandbox_permissions: 'workspace-write', justification: 'Write generated source', timeoutMs: 1234 }) @@ -2963,7 +2956,7 @@ it.each([ runtime.behavior = async (request) => { entered.resolve(undefined) await release.promise - return { logs: [], value: await request.bindings[0]!.functions.echo!({}) } + return printed(await request.bindings[0]!.functions.echo!({})) } const agent = { ctx, id: SessionId('unloading-ptc'), session: Session.create(SessionId('unloading-ptc')) } as Agent const input = { @@ -2978,7 +2971,7 @@ it.each([ await (unload === 'runtime' ? runtimeProvider : provider).dispose() await expect(ctx.tools.execute(input)).rejects.toThrow('selected tool executor is unavailable') release.resolve(undefined) - expect((await pending).results[0]?.result).toMatchObject({ isError: false, value: { logs: [], result: 'settled' } }) + expect((await pending).results[0]?.result).toMatchObject({ isError: false, value: { logs: ['settled'] } }) if (owned) expect(agent.session.snapshotEvents().filter(event => event.type === 'tool/ptc-dispatch')) .toMatchObject([{ data: { name: 'echo', isError: false, content: [{ type: 'text', text: 'settled' }] } }]) } finally { diff --git a/packages/core/tool-executor-ptc/tests/ts-types.spec.ts b/packages/core/tool-executor-ptc/tests/ts-types.spec.ts index 2ec79e974c..9cd18a4536 100644 --- a/packages/core/tool-executor-ptc/tests/ts-types.spec.ts +++ b/packages/core/tool-executor-ptc/tests/ts-types.spec.ts @@ -163,14 +163,14 @@ describe('renderToolsSdk', () => { const text = renderToolsSdk([bash]) expect(text).toContain('A declaration does not make its name a directly callable tool') expect(text).toContain('only names supplied as separate tool schemas may be called directly') - expect(text).toContain('`run_code({ code: "return await tools.bash({ description: \'Show current directory\', command: \'pwd\' })", description: "Show current directory" })`') + expect(text).toContain('`run_code({ code: "Harness.inspect(await tools.bash({ description: \'Show current directory\', command: \'pwd\' }))", description: "Show current directory" })`') expect(text).toContain('Program-only SDK bindings:') expect(text).not.toContain('The available tools:') }) it('keeps REPL output syntax distinct from function bodies', () => { const text = renderToolsSdk([bash], 'repl') - expect(text).toContain('run_code({ code: "console.log(await tools.bash(') + expect(text).toContain('run_code({ code: "Harness.inspect(await tools.bash(') expect(text).not.toContain('return await tools.bash(') expect(text).not.toContain('the body of an async TypeScript function') expect(text).not.toContain('Emit results with `return`') @@ -222,9 +222,9 @@ describe('renderToolsSdk', () => { expect(renderToolsSdk([constrained])).toContain('tools.bash({ command: \'pwd\' })') }) - it('uses a returned result in function-body mode', () => { + it('uses explicit inspection in function-body mode', () => { const text = renderToolsSdk([bash]) - expect(text).toContain('return await tools.bash(') + expect(text).toContain('Harness.inspect(await tools.bash(') expect(text).toContain('body of an async TypeScript function') expect(text).not.toContain('TypeScript REPL statements') }) diff --git a/packages/experimental/cua/README.i18n.yaml b/packages/experimental/cua/README.i18n.yaml index f4abb13c43..cfc79b42e6 100644 --- a/packages/experimental/cua/README.i18n.yaml +++ b/packages/experimental/cua/README.i18n.yaml @@ -15,8 +15,8 @@ en: 5db4d4fea25493b9 zh: 65ab64fc24eb0d09 /deepseek-ai-dsh-cua/use-this-package: - en: ba1f8e6ae51f69d7 - zh: 8f4fde9857bae319 + en: d04cf769cf0085ef + zh: 067bc76a22acda65 /deepseek-ai-dsh-cua/use-this-package/permission-controls: en: d92d5948a7941e8d zh: d43491ae6521b87e diff --git a/packages/experimental/cua/README.md b/packages/experimental/cua/README.md index c95e4ae8ec..5582f36bde 100644 --- a/packages/experimental/cua/README.md +++ b/packages/experimental/cua/README.md @@ -35,7 +35,7 @@ Use `cua.getApp(selector)` to select an application; it obtains consent, optiona Keyboard methods are available in the scopes a provider declares; the provider owns how input is delivered within each scope. A lost connection invalidates its handles and coordinates. Failed cleanup is reported before that connection is retired; select the application again and inspect fresh state, because earlier input may have partially executed. Screenshot image errors retain the failing stage and attachment error code. Invalid request arguments reject before dispatch with a readable method, parameter and requirement. -Observation methods emit and return their result. Use `void await app.getAccessibilityTree()` for one visible copy, or use `emit:false` and print the returned text once. `window.getScreenshot()` returns `CuaScreenshot` with `bytes`, `mimeType`, `pixelWidth`, `pixelHeight`, and `windowId`. The image sink receives the same object. Use its MIME type when selecting a file extension or declaring an image; the format can be PNG, JPEG, WebP, or GIF. Each screenshot enters the attachment pipeline once; `emit:false` only suppresses the object sink. +Observation methods emit and return their result. Connect PTC text output with `connectCua(() => tools.cua, text => Harness.inspect(text))`. Use `await app.getAccessibilityTree()` for one visible copy, or use `emit:false` and inspect the returned text once. `window.getScreenshot()` returns `CuaScreenshot` with `bytes`, `mimeType`, `pixelWidth`, `pixelHeight`, and `windowId`. The image sink receives the same object. Use its MIME type when selecting a file extension or declaring an image; the format can be PNG, JPEG, WebP, or GIF. Each screenshot enters the attachment pipeline once; `emit:false` only suppresses the object sink. `tools.cua` returns operation values directly. Screenshot values retain the encoded image Base64 and add a durable `attachment` reference for the tool renderer; model content contains coordinate metadata and one image, without the encoded bytes. Before capture, the tool requires an attachment store and a resolved model route declaring image input. Admission and storage failures reject the call. The object bridge preserves the screenshot bytes and validated metadata without uploading or emitting the same image again. diff --git a/packages/experimental/cua/README.zh.md b/packages/experimental/cua/README.zh.md index c1c5ed863c..b37aa3b126 100644 --- a/packages/experimental/cua/README.zh.md +++ b/packages/experimental/cua/README.zh.md @@ -35,7 +35,7 @@ kind: "package-reference" 键盘方法可在 provider 声明的作用域中使用;每个作用域内的输入投递由 provider 负责。连接丢失会使其句柄和坐标失效。清理失败会先报告,再释放该连接;应重新选择应用并读取新状态,因为先前的输入可能已部分执行。截图图像错误会保留失败阶段和附件错误码。请求参数校验失败时,在分派前返回包含方法、参数和要求的可读消息。 -观察方法会输出并返回结果。使用 `void await app.getAccessibilityTree()` 只展示一份内容,或使用 `emit:false` 后打印一次返回文本。`window.getScreenshot()` 返回包含 `bytes`、`mimeType`、`pixelWidth`、`pixelHeight` 和 `windowId` 的 `CuaScreenshot`,图片输出回调接收同一个对象。选择文件后缀或声明图片时使用其中的 MIME 类型;格式可能是 PNG、JPEG、WebP 或 GIF。每张截图只进入一次附件处理链;`emit:false` 只抑制对象层的输出。 +观察方法会输出并返回结果。通过 `connectCua(() => tools.cua, text => Harness.inspect(text))` 连接 PTC 文本输出。使用 `await app.getAccessibilityTree()` 展示一份内容,或使用 `emit:false` 后 inspect 一次返回文本。`window.getScreenshot()` 返回包含 `bytes`、`mimeType`、`pixelWidth`、`pixelHeight` 和 `windowId` 的 `CuaScreenshot`,图片输出回调接收同一个对象。选择文件后缀或声明图片时使用其中的 MIME 类型;格式可能是 PNG、JPEG、WebP 或 GIF。每张截图只进入一次附件处理链;`emit:false` 只抑制对象层的输出。 `tools.cua` 直接返回操作结果。截图结果保留图片 Base64,并增加供工具渲染使用的持久 `attachment` 引用;模型内容只包含坐标元数据和一张图片,不包含编码字节。截图前要求附件服务可用,并确认当前模型支持图片输入。图片接入或保存失败会拒绝调用。对象桥接保留截图字节和已校验的元数据,不重复上传或输出同一张图片。 diff --git a/packages/experimental/cua/src/guidance.ts b/packages/experimental/cua/src/guidance.ts index d8d7d46660..6ceefdc8ae 100644 --- a/packages/experimental/cua/src/guidance.ts +++ b/packages/experimental/cua/src/guidance.ts @@ -2,7 +2,7 @@ /** Async method catalog with consent and observation rules, grouped by observation, mouse, and keyboard. */ export const COMMON_GUIDANCE = `Use cua to observe and operate native applications. Always use cua.getCapabilities() to confirm whether a specific API is available on the current platform; its reported capabilities take precedence over availability statements in these instructions. All methods are async; observation options default to { emit: true }, and emit: false suppresses sink output only, leaving returned values unchanged. Actions report dispatch evidence, not verified completion. If sessionLocked is true or an operation reports SESSION_LOCKED, stop UI actions and ask the user to unlock the desktop session; null or unknown means verify a usable unlocked desktop session first. cua.getApp requests user consent; if access is denied or revoked, stop using that application until the user explicitly permits another request, and never bypass a refusal or switch tools to avoid it. Never replay uncertain input without a fresh observation. -In persistent PTC, use the host-provided bridge initialization once and retain the returned objects; cua comes from that bridge and is not a preinstalled global. Use void await for observations that emit, and never stringify screenshot image bytes. +In persistent PTC, use the host-provided bridge initialization once and retain the returned objects; cua comes from that bridge and is not a preinstalled global. Observations emit through Harness.inspect; never stringify screenshot image bytes. Signatures show resolved return values after await. ActionResult = { dispatched: boolean, foregroundChanged?: boolean | null }; foregroundChanged is omitted when not sampled and null when the sample is unavailable. JsonValue = string | number | boolean | null | JsonValue[] | { [key: string]: JsonValue }; numbers must be finite. App is the application object with readonly app.appHandle: string (opaque) and the methods listed below for app level. Window is the window object with readonly window.windowId: number and the methods listed below for window level. Neither object carries pid. diff --git a/packages/experimental/cua/src/index.ts b/packages/experimental/cua/src/index.ts index d88a958711..2a34fd278b 100644 --- a/packages/experimental/cua/src/index.ts +++ b/packages/experimental/cua/src/index.ts @@ -363,7 +363,7 @@ Persistent PTC initialization: \`\`\`ts var { connectCua } = await import(${JSON.stringify(new URL('../lib/bridge.js', import.meta.url).href)}); -var cua = connectCua(() => tools.cua, text => console.log(text)); +var cua = connectCua(() => tools.cua, text => Harness.inspect(text)); \`\`\`` }, } diff --git a/packages/experimental/cua/tests/agent-scopes.spec.ts b/packages/experimental/cua/tests/agent-scopes.spec.ts index 9404dd7af1..e36cdc0bf8 100644 --- a/packages/experimental/cua/tests/agent-scopes.spec.ts +++ b/packages/experimental/cua/tests/agent-scopes.spec.ts @@ -123,5 +123,5 @@ it.each([ const prompt = (await ctx.systemPrompt.assemble({ scope: owner.agent })).sections.find(section => section.name === 'computer-use:dsh-cua')?.text expect(prompt).toBeDefined() expect(prompt?.includes('Persistent PTC initialization:')).toBe(runtime.retainsState) - expect(prompt?.includes('var cua = connectCua(() => tools.cua, text => console.log(text));')).toBe(runtime.retainsState) + expect(prompt?.includes('var cua = connectCua(() => tools.cua, text => Harness.inspect(text));')).toBe(runtime.retainsState) }) diff --git a/packages/extensions/tool-cordis/src/api-catalog.ts b/packages/extensions/tool-cordis/src/api-catalog.ts index d5b6ba6109..77238eed59 100644 --- a/packages/extensions/tool-cordis/src/api-catalog.ts +++ b/packages/extensions/tool-cordis/src/api-catalog.ts @@ -8095,7 +8095,7 @@ export const TYPE_API: readonly TypeApiEntry[] = [ }, { name: 'PtcRunRequest', - declaration: 'export interface PtcRunRequest {\n program: string;\n stateOwner?: object;\n bindings: PtcBindingNamespace[];\n cwd?: string;\n timeoutMs?: number | null;\n sandboxPolicy?: SandboxExecutionPolicy;\n signal?: AbortSignal;\n}', + declaration: 'export interface PtcRunRequest {\n program: string;\n discardCompletion?: boolean;\n stateOwner?: object;\n bindings: PtcBindingNamespace[];\n cwd?: string;\n timeoutMs?: number | null;\n sandboxPolicy?: SandboxExecutionPolicy;\n signal?: AbortSignal;\n}', }, { name: 'PtcRunResult', diff --git a/packages/ptc-runtime/ptc-runtime-node/README.i18n.yaml b/packages/ptc-runtime/ptc-runtime-node/README.i18n.yaml index 1812034c2b..e29fce4128 100644 --- a/packages/ptc-runtime/ptc-runtime-node/README.i18n.yaml +++ b/packages/ptc-runtime/ptc-runtime-node/README.i18n.yaml @@ -18,20 +18,20 @@ en: 9c742ab3175c4f80 zh: 5fa7d1ce9c3920d2 /deepseek-ai-dsh-ptc-runtime-node/use-this-package/configuration: - en: 8d42f921a1fa8e5c - zh: 27b79ec4ec52b3fd + en: fb8fb38f2d633166 + zh: e9bc16998a05e027 /deepseek-ai-dsh-ptc-runtime-node/use-this-package/execution-and-results: - en: 2a9fa3292bf07355 - zh: db0b5684820915eb + en: 309dc144f53c960b + zh: 0e66d639768cc688 /deepseek-ai-dsh-ptc-runtime-node/use-this-package/in-memory-modes: - en: 41c54a052af70659 - zh: becb41300547089e + en: 014408adf339cc52 + zh: c353958fe45ae859 /deepseek-ai-dsh-ptc-runtime-node/use-this-package/deadlines-and-cancellation: en: d383ec7aeab8afec zh: 7c9c49e205fbbcd8 /deepseek-ai-dsh-ptc-runtime-node/use-this-package/failures: - en: 34a221d0f26b8e47 - zh: 2fd20ac9c9953c7f + en: 41c236b0eb12affb + zh: 7b89c6c6b58d28cf /deepseek-ai-dsh-ptc-runtime-node/understand-the-implementation: en: 50e6cd38e298da31 zh: c8b252b3acf59147 @@ -57,8 +57,8 @@ en: 40294c1c31b74824 zh: d91e7b78c0619c7e /deepseek-ai-dsh-ptc-runtime-node/known-limitations-and-deferred-work: - en: 7f688098214c7c72 - zh: de06cbba4da172c5 + en: cf5d486613e84d77 + zh: 5a6d13a07a97e672 /deepseek-ai-dsh-ptc-runtime-node/known-limitations-and-deferred-work/dev-note: en: 6373aba4e08f7205 zh: 2a8c33d1f2839df4 diff --git a/packages/ptc-runtime/ptc-runtime-node/README.md b/packages/ptc-runtime/ptc-runtime-node/README.md index 6f5d9f66ac..9e509070fb 100644 --- a/packages/ptc-runtime/ptc-runtime-node/README.md +++ b/packages/ptc-runtime/ptc-runtime-node/README.md @@ -35,6 +35,7 @@ Configure the provider row after its required services are available: - name: '@deepseek-ai/dsh-ptc-runtime-node' config: executionMode: per-call + enableConsole: false timeoutMs: 120000 maxTimeoutMs: 600000 maxOutputBytes: 67108864 @@ -49,6 +50,7 @@ Configure the provider row after its required services are available: |---|---|---| | `executionMode` | `per-call` | `per-call` runs async function bodies in fresh processes; `vm-script` evaluates Scripts; `persistent-in-memory` retains a confined REPL per owner | | `scriptPersistence` | `false` | With `executionMode: vm-script`, reuse a process and context per owner; `true` with another mode rejects at load | +| `enableConsole` | `false` | Expose `console.log`, `info`, `warn`, `error` and `debug` through `Harness.inspect`; otherwise omit the console global | | `timeoutMs` | `120,000` | Default elapsed execution deadline, including nested tool and approval waits | | `maxTimeoutMs` | `600,000` | Elapsed deadline ceiling applied by the resolver | | `maxOutputBytes` | `67,108,864` | Combined serialized logs and completion or diagnostic budget | @@ -67,9 +69,13 @@ Profile editors can import `resolvePersistentExecution` from `@deepseek-ai/dsh-p In `per-call` mode, programs are async function bodies: top-level `await` and `return` work, and only erasable TypeScript is accepted. Node globals remain available, and a function body may declare a local `process`, including `const process = await import("node:process")`. A successful call returns its lossless-JSON value as `result.value` and captured text as `result.logs`. `result.sandbox` reports the selected mode, observed denial and the backend's full or partial enforcement independently of the program outcome. +`Harness.inspect(...values)` renders each argument independently, joins them with spaces, and appends one captured log entry. Strings remain literal, including `%s` and `%d`. The console global is absent by default: direct access raises `ReferenceError`, while `typeof console` returns `"undefined"`. Set `enableConsole: true` to expose its five methods through the same inspection function. Programs may declare their own console variables. + +`run_code` sets `discardCompletion: true`: function return values and final expression values are omitted without JSON serialization, while inspected output remains visible. Direct runtime consumers receive the usual lossless-JSON completion unless they set this option. + Direct filesystem, network and subprocess operations remain Node operations, subject to the selected OS sandbox. Nested host bindings cross the control channel; PTC tool calls retain the registry's visibility, ordering, logging and approval rules. Running a program does not change the Session's standing policy or automatically replay it after a denial. -Set `executionMode: vm-script` to evaluate erasable TypeScript through `node:vm` Script. Top-level declarations and `await` work, for example `var fs = await import("node:fs/promises"); await fs.readdir(".")`. Top-level `return` and static `import`/`export` are not supported. The runtime awaits the final expression and returns its lossless JSON value; `console.log(...)` also emits output. Relative dynamic imports resolve from the supplied working directory. Script syntax is non-strict unless the program opts into strict mode; the context is not a security boundary, and Node access remains subject to the process sandbox. +Set `executionMode: vm-script` to evaluate erasable TypeScript through `node:vm` Script. Top-level declarations and `await` work, for example `var fs = await import("node:fs/promises"); Harness.inspect(await fs.readdir("."))`. Top-level `return` and static `import`/`export` are not supported. The runtime awaits the final expression; its lossless JSON value is returned unless `discardCompletion` is enabled. Relative dynamic imports resolve from the supplied working directory. Script syntax is non-strict unless the program opts into strict mode; the context is not a security boundary, and Node access remains subject to the process sandbox. `scriptPersistence: false` is the default: every call uses a fresh process and context, ignoring `stateOwner`. Set it to `true` to retain a context per owner under the lifecycle rules below. Top-level declarations, functions and object identities survive subsequent calls, including values initialized through top-level `await`. Function locals remain local. This option requires `executionMode: vm-script`; both settings accept the same syntax. @@ -93,7 +99,7 @@ The `persistent-in-memory` mode and Script mode with `scriptPersistence: true` r Save the patch and pass its path to `dsh headless --patch ` in a composition with PTC tool presentation. The `per-call` mode and Script mode with `scriptPersistence: false` retain no program memory between calls. Changing the configuration reloads the provider and releases its execution resources. Packaged single-executable hosts use their embedded carrier by default; the host sends the selected program format and retention flag with each evaluation, so the embedded `process.js` bootstrap serves both per-call and retained execution. Remote script deployments use a `node-script` launch with `launch.bootstrapPath` pointing to this package's built `persistent-process.js`. -In REPL mode, each live Agent keeps its own process and top-level variables, functions, and imported modules across `run_code` calls and turns. Use `await import(...)`, `console.log(...)`, and a final JSON expression; top-level `return` is not supported. Assignments to undeclared names create retained globals under Node REPL’s non-strict evaluation. `_` and `_error` are ordinary retained globals, without interactive REPL result setters. Node REPL declaration rules apply, including errors on redeclaring existing `let`, `const`, or `class` names; assign to a mutable binding or choose a new name. A leading `{ ... }` is a statement block and can contain top-level await and temporary declarations; parenthesize an object expression to return it. Retained functions and saved tool methods call the current evaluation's bindings. All tool calls must be awaited; callbacks from a completed evaluation cannot invoke tools in a later evaluation. +In REPL mode, each live Agent keeps its own process and top-level variables, functions, and imported modules across `run_code` calls and turns. Use `await import(...)` and `Harness.inspect(...)`; top-level `return` is not supported. Assignments to undeclared names create retained globals under Node REPL’s non-strict evaluation. `_` and `_error` are ordinary retained globals, without interactive REPL result setters. Node REPL declaration rules apply, including errors on redeclaring existing `let`, `const`, or `class` names; assign to a mutable binding or choose a new name. A leading `{ ... }` is a statement block and can contain top-level await and temporary declarations; parenthesize an object expression to return it. Retained functions and saved tool methods call the current evaluation's bindings. All tool calls must be awaited; callbacks from a completed evaluation cannot invoke tools in a later evaluation. Memory is best-effort and never saved to disk. Restart, Agent disposal, provider unload, protocol failure, output overflow, or process exit clears it. Missing variables raise normal reference errors; reacquire handles and recreate declarations. Exceptions in the active evaluation settle once and preserve earlier mutations. In REPL mode, exceptions from callbacks belonging to a completed evaluation are not reported in a later result. In Script mode, uncaught callback exceptions can terminate the retained process; await asynchronous work before completing an evaluation. The Session still records programs, tool dispatches and visible results; restoring a Session or forking an Agent starts with empty memory. Each evaluation resets the process working directory to its resolved cwd; program calls to `process.chdir()` do not persist across evaluations. Direct Node access follows the process launch policy. A retained owner cannot change mode, workspace root, or Session identity until its environment is released. Additional writable directories are fixed when the process starts: later grants and revocations apply only after release and recreation. @@ -109,7 +115,7 @@ Omitting `timeoutMs` uses the configured elapsed default; numeric requests are v ### Failures -Program parse errors and thrown exceptions are `exception`; deadline expiry is `timeout`; cancellation is `abort`; unparsable or oversized control traffic and invalid program messages are `protocol`; unavailable confinement is `sandbox-unavailable`; early process exit, lost control transport or failed managed cleanup is `worker-exit`. Control I/O failures retain the transport diagnostic alongside the observed process exit status. The substrate-independent failure name remains `worker-exit` for process providers. Lossy completions are `invalid-output`, and an oversized outer result is `output-limit`, retaining the fitting log prefix. Invalid or unsupported options and calls after disposal reject as caller misuse. +Program parse errors and thrown exceptions are `exception`; deadline expiry is `timeout`; cancellation is `abort`; unparsable or oversized control traffic and invalid program messages are `protocol`; unavailable confinement is `sandbox-unavailable`; early process exit, lost control transport or failed managed cleanup is `worker-exit`. Control I/O failures retain the transport diagnostic alongside the observed process exit status. The substrate-independent failure name remains `worker-exit` for process providers. When completion capture is enabled, lossy values are `invalid-output`, and an oversized outer result is `output-limit`, retaining the fitting log prefix. Invalid or unsupported options and calls after disposal reject as caller misuse. ----- @@ -226,7 +232,7 @@ These limits qualify the execution guarantees and retained output. - **Output caps reject rather than retain every byte** — spill can preserve only the bounded result delivered by this provider. - **Bindings are bounded at transport admission** — control limits do not bound the memory a host binding allocates while producing its result. - **Preinstalled bootstraps require a compatible protocol revision** — readiness rejects a missing or mismatched revision before dispatching code. Upgrade `launch.bootstrapPath` with the host; the runtime does not install remote bootstrap updates. -- **The console shim has five methods** — `log`, `info`, `warn`, `error` and `debug`. +- **The enabled console shim has five methods** — `log`, `info`, `warn`, `error` and `debug`. ### Dev Note diff --git a/packages/ptc-runtime/ptc-runtime-node/README.zh.md b/packages/ptc-runtime/ptc-runtime-node/README.zh.md index 111837701b..e4610db544 100644 --- a/packages/ptc-runtime/ptc-runtime-node/README.zh.md +++ b/packages/ptc-runtime/ptc-runtime-node/README.zh.md @@ -35,6 +35,7 @@ kind: "package-reference" - name: '@deepseek-ai/dsh-ptc-runtime-node' config: executionMode: per-call + enableConsole: false timeoutMs: 120000 maxTimeoutMs: 600000 maxOutputBytes: 67108864 @@ -49,6 +50,7 @@ kind: "package-reference" |---|---|---| | `executionMode` | `per-call` | `per-call` 在全新进程中运行异步函数体;`vm-script` 求值 Script;`persistent-in-memory` 为每个所有者保留受限 REPL | | `scriptPersistence` | `false` | 在 `executionMode: vm-script` 下,为每个所有者复用进程与 context;其他模式下设为 `true` 会在加载时拒绝 | +| `enableConsole` | `false` | 通过 `Harness.inspect` 提供 `console.log`、`info`、`warn`、`error` 与 `debug`;否则不提供 console 全局变量 | | `timeoutMs` | `120,000` | 默认经过时间截止,包括嵌套工具与审批等待 | | `maxTimeoutMs` | `600,000` | 解析器应用的经过时间截止上限 | | `maxOutputBytes` | `67,108,864` | 序列化日志与完成值或诊断的合计预算 | @@ -67,9 +69,13 @@ profile 编辑器可从 `@deepseek-ai/dsh-ptc-runtime-node/configuration` 导入 `per-call` 模式中的程序是异步函数体:支持顶层 `await` 与 `return`,且只接受可擦除 TypeScript。Node 全局变量仍然可用,函数体可声明局部 `process`,例如 `const process = await import("node:process")`。成功调用以 `result.value` 返回无损 JSON 值,以 `result.logs` 返回捕获文本。`result.sandbox` 独立于程序结果报告所选模式、观察到的拒绝,以及后端完整或部分的强制能力。 +`Harness.inspect(...values)` 独立渲染各个参数,以空格连接后追加一条捕获日志。字符串保持原样,包括 `%s` 与 `%d`。默认没有 console 全局变量:直接访问会抛出 `ReferenceError`,而 `typeof console` 返回 `"undefined"`。设置 `enableConsole: true` 可通过同一个 inspection 函数提供五个 console 方法。程序也可以自行声明 console 变量。 + +`run_code` 设置 `discardCompletion: true`:函数返回值和末尾表达式的值不经过 JSON 序列化,也不出现在结果中,显式 inspect 的输出仍然可见。直接调用运行时的消费方在未设置此选项时,仍可取得原有的无损 JSON 完成值。 + 直接文件系统、网络与子进程操作仍是 Node 操作,受所选 OS 沙箱约束。嵌套 Host 绑定通过控制通道调用;PTC 工具调用保留注册表的可见性、排序、日志和审批规则。运行程序不会改变 Session 的常设策略,也不会在拒绝后自动重放程序。 -设置 `executionMode: vm-script`,即可通过 `node:vm` Script 求值可擦除 TypeScript。支持顶层声明和 `await`,例如 `var fs = await import("node:fs/promises"); await fs.readdir(".")`。不支持顶层 `return` 和静态 `import`/`export`。运行时等待末尾表达式,并返回其无损 JSON 值;`console.log(...)` 也会产生输出。相对动态导入以传入的工作目录为基准解析。除非程序显式启用严格模式,否则 Script 使用非严格语法;context 不是安全边界,Node 访问仍受进程沙箱约束。 +设置 `executionMode: vm-script`,即可通过 `node:vm` Script 求值可擦除 TypeScript。支持顶层声明和 `await`,例如 `var fs = await import("node:fs/promises"); Harness.inspect(await fs.readdir("."))`。不支持顶层 `return` 和静态 `import`/`export`。运行时等待末尾表达式;未启用 `discardCompletion` 时,返回其无损 JSON 值。相对动态导入以传入的工作目录为基准解析。除非程序显式启用严格模式,否则 Script 使用非严格语法;context 不是安全边界,Node 访问仍受进程沙箱约束。 `scriptPersistence: false` 是默认值:每次调用都使用全新进程和 context,忽略 `stateOwner`。设为 `true` 后,运行时按下述生命周期规则为每个所有者保留 context。顶层声明、函数与对象身份可供后续调用使用,包括通过顶层 `await` 初始化的值。函数局部变量仍只在函数内有效。该选项要求 `executionMode: vm-script`;两种设置接受相同语法。 @@ -93,7 +99,7 @@ profile 编辑器可从 `@deepseek-ai/dsh-ptc-runtime-node/configuration` 导入 保存补丁,在使用 PTC 工具展示的配置中通过 `dsh headless --patch ` 加载。`per-call` 模式和 `scriptPersistence: false` 的 Script 模式不跨调用保留程序内存。修改配置会重载提供方并释放其执行资源。单文件打包宿主默认使用内嵌载体;宿主在每次执行中发送所选的程序格式和保留标志,因此内嵌的 `process.js` bootstrap 同时服务单次执行和保留执行。远端脚本部署使用 `node-script` 启动方式,并将 `launch.bootstrapPath` 指向本包构建出的 `persistent-process.js`。 -在 REPL 模式下,每个存活 Agent 保留独立进程,跨 `run_code` 调用和轮次保留顶层变量、函数及导入模块。使用 `await import(...)`、`console.log(...)` 和末尾 JSON 表达式;不支持顶层 `return`。Node REPL 使用非严格模式,给未声明的名称赋值会创建保留的全局变量。`_` 和 `_error` 是普通的保留全局变量,不使用交互式 REPL 的结果设置器。声明遵循 Node REPL 规则,包括重复声明已有 `let`、`const` 或 `class` 名称时报错;应给可变绑定赋值或选择新名称。开头的 `{ ... }` 按语句块执行,可以包含顶层 await 和临时声明;需要返回对象表达式时,用圆括号包裹。保留的函数和工具方法引用调用当前执行的绑定。必须等待所有工具调用;已完成执行的回调不能在后续执行中调用工具。 +在 REPL 模式下,每个存活 Agent 保留独立进程,跨 `run_code` 调用和轮次保留顶层变量、函数及导入模块。使用 `await import(...)` 和 `Harness.inspect(...)`;不支持顶层 `return`。Node REPL 使用非严格模式,给未声明的名称赋值会创建保留的全局变量。`_` 和 `_error` 是普通的保留全局变量,不使用交互式 REPL 的结果设置器。声明遵循 Node REPL 规则,包括重复声明已有 `let`、`const` 或 `class` 名称时报错;应给可变绑定赋值或选择新名称。开头的 `{ ... }` 按语句块执行,可以包含顶层 await 和临时声明;需要返回对象表达式时,用圆括号包裹。保留的函数和工具方法引用调用当前执行的绑定。必须等待所有工具调用;已完成执行的回调不能在后续执行中调用工具。 内存按尽力保留处理,不保存到磁盘。重启、Agent 销毁、提供方卸载、协议失败、输出超限或进程退出都会清除内存。缺失变量产生普通引用错误,模型需要重新获取句柄和创建声明。当前执行中的异常只结算一次,并保留此前发生的修改。在 REPL 模式下,已完成执行的回调抛出的异常不会在后续结果中报告。在 Script 模式下,未捕获的回调异常可能终止保留进程;结束执行前应等待异步工作完成。Session 仍记录程序、工具分派和可见结果;恢复 Session 或派生 Agent 时从空内存开始。每次执行都会将进程工作目录重置为本次解析的 cwd;程序中的 `process.chdir()` 不跨执行保留。直接 Node 访问遵循进程启动时的文件策略。保留状态的所有者必须先释放环境,才能更改权限模式、工作区根目录或 Session 身份。额外可写目录在进程启动时固定:之后的授权和撤销仅在释放并重新创建环境后生效。 @@ -109,7 +115,7 @@ PTC 消费方按 [dsh-tool-executor-ptc](../../core/tool-executor-ptc/README.zh. ### 失败 -程序解析错误与抛出异常为 `exception`;截止到期为 `timeout`;取消为 `abort`;无法解析或超量的控制通信以及无效的程序消息为 `protocol`;约束不可用为 `sandbox-unavailable`;进程提前退出、控制通道丢失或受管清理失败为 `worker-exit`。控制通道 I/O 失败时,在观测到的进程退出状态之外保留通道诊断原因。进程提供方保留与执行基底无关的失败名 `worker-exit`。有损完成值为 `invalid-output`,外层结果超限为 `output-limit`,并保留能容纳的日志前缀。无效或不支持的选项,以及资源释放后的调用,以调用方误用拒绝。 +程序解析错误与抛出异常为 `exception`;截止到期为 `timeout`;取消为 `abort`;无法解析或超量的控制通信以及无效的程序消息为 `protocol`;约束不可用为 `sandbox-unavailable`;进程提前退出、控制通道丢失或受管清理失败为 `worker-exit`。控制通道 I/O 失败时,在观测到的进程退出状态之外保留通道诊断原因。进程提供方保留与执行基底无关的失败名 `worker-exit`。捕获完成值时,有损值为 `invalid-output`,外层结果超限为 `output-limit`,并保留能容纳的日志前缀。无效或不支持的选项,以及资源释放后的调用,以调用方误用拒绝。 ----- @@ -226,7 +232,7 @@ AsyncFunction 和 Node REPL 共用同步 `registerHooks` 和以 symbol 为键的 - **输出上限拒绝超量内容,而不保留每个字节**——溢出只能保存本提供方交付的有界结果。 - **绑定在传输接纳时受限**——控制限制不约束 Host 绑定生成结果期间分配的内存。 - **预装 bootstrap 需要兼容的协议版本**——就绪握手会在派发代码前拒绝缺失或不匹配的版本。请将 `launch.bootstrapPath` 与宿主一同升级;运行时不会安装远端 bootstrap 更新。 -- **console shim 有五个方法**——`log`、`info`、`warn`、`error` 与 `debug`。 +- **启用后的 console shim 有五个方法**——`log`、`info`、`warn`、`error` 与 `debug`。 ### 开发备注 diff --git a/packages/ptc-runtime/ptc-runtime-node/src/bootstrap.ts b/packages/ptc-runtime/ptc-runtime-node/src/bootstrap.ts index 40ddbf3900..9123077d87 100644 --- a/packages/ptc-runtime/ptc-runtime-node/src/bootstrap.ts +++ b/packages/ptc-runtime/ptc-runtime-node/src/bootstrap.ts @@ -103,11 +103,12 @@ const CONSOLE_LEVELS = ['log', 'info', 'warn', 'error', 'debug'] as const * @returns the five-method console object handed to the program. */ export function makeConsoleShim(logs: Pick): Record<(typeof CONSOLE_LEVELS)[number], (...args: unknown[]) => void> { - const render = (args: unknown[]): string => - args.map(arg => typeof arg === 'string' ? arg : inspect(arg, INSPECT_OPTIONS)).join(' ') + const output = (...args: unknown[]): void => { + logs.push(args.map(arg => typeof arg === 'string' ? arg : inspect(arg, INSPECT_OPTIONS)).join(' ')) + } const shim = Object.create(null) as Record<(typeof CONSOLE_LEVELS)[number], (...args: unknown[]) => void> for (const level of CONSOLE_LEVELS) { - shim[level] = (...args: unknown[]) => { logs.push(render(args)) } + shim[level] = output } return shim } diff --git a/packages/ptc-runtime/ptc-runtime-node/src/config.ts b/packages/ptc-runtime/ptc-runtime-node/src/config.ts index aceeb45b8a..b4a894c895 100644 --- a/packages/ptc-runtime/ptc-runtime-node/src/config.ts +++ b/packages/ptc-runtime/ptc-runtime-node/src/config.ts @@ -11,6 +11,8 @@ export interface Config { executionMode?: 'per-call' | 'persistent-in-memory' | 'vm-script' /** Retain a Script process and context per stateOwner; only supported with executionMode: vm-script. */ scriptPersistence?: boolean + /** Expose console with log, info, warn, error and debug using Harness.inspect output; otherwise omit the global. */ + enableConsole?: boolean /** Default elapsed deadline, including nested tool and approval waits. */ timeoutMs?: number /** Maximum numeric elapsed budget accepted by resolve. */ @@ -35,6 +37,7 @@ export type ResolvedConfig = Required export const NodeConfig: z = z.object({ executionMode: z.union([z.const('per-call'), z.const('persistent-in-memory'), z.const('vm-script')]).default('per-call'), scriptPersistence: z.boolean().default(false), + enableConsole: z.boolean().default(false), timeoutMs: z.number().default(120_000), maxTimeoutMs: z.number().default(600_000), maxOutputBytes: z.number().default(67_108_864), diff --git a/packages/ptc-runtime/ptc-runtime-node/src/evaluators/vm-script.ts b/packages/ptc-runtime/ptc-runtime-node/src/evaluators/vm-script.ts index 187604c1ca..cf63f6999a 100644 --- a/packages/ptc-runtime/ptc-runtime-node/src/evaluators/vm-script.ts +++ b/packages/ptc-runtime/ptc-runtime-node/src/evaluators/vm-script.ts @@ -33,8 +33,9 @@ function createScriptContext(): Context { const globals = Object.create(null) as Record const context = createContext(globals) const realm = new Script('globalThis').runInContext(context) as Record + Reflect.deleteProperty(realm, 'console') for (const key of Reflect.ownKeys(globalThis)) { - if (key === 'global' || Object.hasOwn(realm, key)) continue + if (key === 'global' || key === 'console' || Object.hasOwn(realm, key)) continue Object.defineProperty(globals, key, Object.getOwnPropertyDescriptor(globalThis, key) as PropertyDescriptor) } globals.global = realm diff --git a/packages/ptc-runtime/ptc-runtime-node/src/execution.ts b/packages/ptc-runtime/ptc-runtime-node/src/execution.ts index b4162212dc..830ba88a30 100644 --- a/packages/ptc-runtime/ptc-runtime-node/src/execution.ts +++ b/packages/ptc-runtime/ptc-runtime-node/src/execution.ts @@ -130,11 +130,13 @@ const STRIP_SUFFIX = '\n}' const RETAINED_STATE_INSTRUCTIONS = 'Timeout and cancellation results say whether retained variables were preserved or cleared. Restart, sandbox changes, output overflow, or process failure clears them. Recreate missing variables and resources; conversation history does not restore state. Earlier mutations and tool effects are not rolled back. Approved sandbox escalations use a fresh environment and discard its variables afterward. Do not redeclare existing let, const, or class names; reuse mutable bindings or choose new names. Await tools and cleanup before returning; unfinished tools reject. After completion, timeout, or cancellation, new tool calls are rejected and captured output is discarded; background work may still change state. Await child-process completion before returning; raw output belongs to the active evaluation.' function resolveMode(config: ResolvedConfig): ExecutionMode { + const outputInstructions = (config.enableConsole ? '' : 'Console is unavailable. ') + + 'Use Harness.inspect(...values) for output.' switch (config.executionMode) { case 'per-call': return { format: 'function-body', retainsState: false, - executionInstructions: 'The program is the body of an async TypeScript function; only erasable syntax is accepted, with no enum or namespaces. Type annotations are advisory. Top-level await and return are supported. Emit output with console.log(...) or return a lossless JSON value. Only printed values and the returned value become program output. Each call runs in a fresh Node process. Node APIs are available through await import(...). Relative paths use the supplied working directory; process.env starts empty. Direct filesystem access follows this execution\'s sandbox policy.', + executionInstructions: 'The program is the body of an async TypeScript function; only erasable syntax is accepted, with no enum or namespaces. Type annotations are advisory. Top-level await and return are supported. ' + outputInstructions + ' Each call runs in a fresh Node process. Node APIs are available through await import(...). Relative paths use the supplied working directory; process.env starts empty. Direct filesystem access follows this execution\'s sandbox policy.', stripTypes(program) { const stripped = stripTypeScriptTypes(STRIP_PREFIX + program + STRIP_SUFFIX) return stripped.slice(STRIP_PREFIX.length, stripped.length - STRIP_SUFFIX.length) @@ -143,7 +145,7 @@ function resolveMode(config: ResolvedConfig): ExecutionMode { case 'persistent-in-memory': return { format: 'repl', retainsState: true, - executionInstructions: 'The program contains TypeScript REPL statements; only erasable syntax is accepted, with no enum or namespaces. Type annotations are advisory. Top-level declarations and await are supported; top-level return is not supported. Emit output with console.log(...) or a final lossless JSON expression. Only printed values and that final expression become program output. Variables, functions, and imported modules remain in memory for this Agent across calls. Use await import(...) for modules. Direct filesystem access follows the environment\'s launch-time sandbox policy; process.env starts empty. ' + RETAINED_STATE_INSTRUCTIONS, + executionInstructions: 'The program contains TypeScript REPL statements; only erasable syntax is accepted, with no enum or namespaces. Type annotations are advisory. Top-level declarations and await are supported; top-level return is not supported. ' + outputInstructions + ' Variables, functions, and imported modules remain in memory for this Agent across calls. Use await import(...) for modules. Direct filesystem access follows the environment\'s launch-time sandbox policy; process.env starts empty. ' + RETAINED_STATE_INSTRUCTIONS, stripTypes: stripTypeScriptTypes, } case 'vm-script': { @@ -161,7 +163,7 @@ function resolveMode(config: ResolvedConfig): ExecutionMode { return { format: 'script', retainsState: config.scriptPersistence, - executionInstructions: 'The program contains TypeScript script statements; only erasable syntax is accepted, with no enum or namespaces. Type annotations are advisory. Top-level declarations and await are supported; top-level return is not supported. Emit output with console.log(...) or a final lossless JSON expression. The final expression is awaited if it is a Promise. Only printed values and that final expression become program output. ' + lifetime + ' Use await import(...) for modules. Relative paths use the supplied working directory. ' + filePolicy + ' ' + asynchronousFailure + ' Values from bindings and imports can belong to another realm; use Array.isArray for arrays and the supplied binding error classes for failures, because instanceof Array or Error may be false.' + recovery, + executionInstructions: 'The program contains TypeScript script statements; only erasable syntax is accepted, with no enum or namespaces. Type annotations are advisory. Top-level declarations and await are supported; top-level return is not supported. ' + outputInstructions + ' The final expression is awaited if it is a Promise. ' + lifetime + ' Use await import(...) for modules. Relative paths use the supplied working directory. ' + filePolicy + ' ' + asynchronousFailure + ' Values from bindings and imports can belong to another realm; use Array.isArray for arrays and the supplied binding error classes for failures, because instanceof Array or Error may be false.' + recovery, stripTypes: stripTypeScriptTypes, } } diff --git a/packages/ptc-runtime/ptc-runtime-node/src/process.ts b/packages/ptc-runtime/ptc-runtime-node/src/process.ts index b4b95dc950..a6b9ff97c3 100644 --- a/packages/ptc-runtime/ptc-runtime-node/src/process.ts +++ b/packages/ptc-runtime/ptc-runtime-node/src/process.ts @@ -112,9 +112,19 @@ export async function runNodeMain( throw error }) : checkActive(result) }) - evaluator.globals.console = makeConsoleShim(logSink) // Context access must not reserve a function-body parameter named process. Object.defineProperty(evaluator.globals, 'process', { configurable: true, writable: true, value: processState }) + const console = makeConsoleShim(logSink) + Object.defineProperty(evaluator.globals, 'Harness', { + configurable: true, enumerable: true, value: Object.freeze({ inspect: console.log }), + }) + const programGlobals = processState === process ? [evaluator.globals, globalThis] : [evaluator.globals] + for (const globals of programGlobals) { + if (data.enableConsole) Object.defineProperty(globals, 'console', { + configurable: true, enumerable: true, writable: true, value: console, + }) + else Reflect.deleteProperty(globals, 'console') + } } for (const namespace of data.namespaces) { const descriptor = namespace.errorClass @@ -136,7 +146,8 @@ export async function runNodeMain( evaluator.evaluate(data.code, (result) => { if (!evaluation.active) return if (evaluation.revoked) { evaluation.finish({}); return } - evaluation.finish(result.ok ? prepareCompletion(result.value, logs.remainingOutputBytes(), data.maxOutputBytes) + evaluation.finish(result.ok + ? data.discardCompletion ? {} : prepareCompletion(result.value, logs.remainingOutputBytes(), data.maxOutputBytes) : prepareException(result.error, logs.remainingOutputBytes(), data.maxOutputBytes)) }) } catch (error: unknown) { diff --git a/packages/ptc-runtime/ptc-runtime-node/src/protocol.ts b/packages/ptc-runtime/ptc-runtime-node/src/protocol.ts index 5224ed9509..b4f36b162c 100644 --- a/packages/ptc-runtime/ptc-runtime-node/src/protocol.ts +++ b/packages/ptc-runtime/ptc-runtime-node/src/protocol.ts @@ -13,6 +13,10 @@ export interface ProgramBootData { programFormat?: PtcProgramFormat /** Whether the host will reuse this process after the evaluation. */ retainsState: boolean + /** Allow the five console methods to emit inspected output; omission disables console access. */ + enableConsole?: boolean + /** Await successful completion without serializing its value. */ + discardCompletion?: boolean /** Globals and callable names injected into the program. */ namespaces: { global: string diff --git a/packages/ptc-runtime/ptc-runtime-node/src/worker.ts b/packages/ptc-runtime/ptc-runtime-node/src/worker.ts index d350e27a0a..a75d224394 100644 --- a/packages/ptc-runtime/ptc-runtime-node/src/worker.ts +++ b/packages/ptc-runtime/ptc-runtime-node/src/worker.ts @@ -137,6 +137,8 @@ export class ProgramWorker { data: { code: '', programFormat: this.mode.format, retainsState: this.retainsState, maxOutputBytes: this.config.maxOutputBytes, + enableConsole: this.config.enableConsole, + discardCompletion: spec.discardCompletion === true, namespaces: [...bindings.values()].map(binding => ({ global: binding.global, names: Object.keys(binding.functions), ...binding.errorClass ? { errorClass: binding.errorClass } : {}, })), diff --git a/packages/ptc-runtime/ptc-runtime-node/tests/built-lib.e2e.ts b/packages/ptc-runtime/ptc-runtime-node/tests/built-lib.e2e.ts index f61fd60741..5c033c9dfc 100644 --- a/packages/ptc-runtime/ptc-runtime-node/tests/built-lib.e2e.ts +++ b/packages/ptc-runtime/ptc-runtime-node/tests/built-lib.e2e.ts @@ -34,7 +34,7 @@ describe.skipIf(!built)('built lib real load path (plain node)', () => { }) it.each(['per-call', 'vm-script'] as const)('runs TypeScript and bindings through published exports in %s mode', async (executionMode) => { - const program = 'const doubled: number = await tools.double({ n: 21 }); console.log("halfway", doubled); let failure; try { await tools.fail({}) } catch (error) { failure = { typed: error instanceof ToolCallError, name: error.name, toolName: error.toolName, message: error.message } }; const env = Object.keys((await import("node:process")).env); ' + (executionMode === 'per-call' ? 'return { doubled, failure, env };' : '({ doubled, failure, env })') + const program = 'const doubled: number = await tools.double({ n: 21 }); Harness.inspect("halfway", doubled); let failure; try { await tools.fail({}) } catch (error) { failure = { typed: error instanceof ToolCallError, name: error.name, toolName: error.toolName, message: error.message } }; const env = Object.keys((await import("node:process")).env); ' + (executionMode === 'per-call' ? 'return { doubled, failure, env };' : '({ doubled, failure, env })') const script = ` const { Context } = await import('@deepseek-ai/cordis') const { NodePtcRuntime } = await import('@deepseek-ai/dsh-ptc-runtime-node') diff --git a/packages/ptc-runtime/ptc-runtime-node/tests/output.spec.ts b/packages/ptc-runtime/ptc-runtime-node/tests/output.spec.ts new file mode 100644 index 0000000000..5b97545342 --- /dev/null +++ b/packages/ptc-runtime/ptc-runtime-node/tests/output.spec.ts @@ -0,0 +1,87 @@ +import { Context } from '@deepseek-ai/cordis' +import { describe, expect, it } from 'vitest' +import { mountRuntime } from './setup.ts' + +const modes = [ + { executionMode: 'per-call', scriptPersistence: false }, + { executionMode: 'persistent-in-memory', scriptPersistence: false }, + { executionMode: 'vm-script', scriptPersistence: false }, + { executionMode: 'vm-script', scriptPersistence: true }, +] as const + +describe.each(modes)('program output in $executionMode with retention $scriptPersistence', (mode) => { + async function setup(enableConsole = false) { + const { runtime } = await mountRuntime(new Context(), enableConsole ? { ...mode, enableConsole: true } : mode) + const stateOwner = {} + const run = (program: string, discardCompletion = true) => runtime.run(runtime.resolve({ + program, bindings: [], stateOwner, discardCompletion, + })) + const completion = (expression: string) => mode.executionMode === 'per-call' ? `return ${expression}` : expression + return { runtime, run, completion } + } + + it('renders each inspected argument independently and preserves format placeholders', async () => { + const { run } = await setup() + const result = await run('Harness.inspect("%s %d %j %%", "text", 7, { n: 1 }, [2, 3]);') + expect(result.error).toBeUndefined() + expect(result.logs).toEqual(['%s %d %j %% text 7 { n: 1 } [ 2, 3 ]']) + expect(result.value).toBeUndefined() + }) + + it('discards completion values without reading their getters or requiring lossless JSON', async () => { + const { run, completion } = await setup() + const object = await run(completion('({ get value() { throw new Error("completion getter ran") } })')) + expect(object.error).toBeUndefined() + expect(object.logs).toEqual([]) + expect(object.value).toBeUndefined() + const bigint = await run(completion('42n')) + expect(bigint.error).toBeUndefined() + expect(bigint.value).toBeUndefined() + const captured = await run(`Harness.inspect("selected"); ${completion('"hidden"')}`) + expect(captured.logs).toEqual(['selected']) + expect(captured.value).toBeUndefined() + }) + + it('omits the console global and preserves ordinary undefined-variable behavior', async () => { + const { runtime, run } = await setup() + const caught = await run(` + Harness.inspect(typeof console, globalThis.console, Object.hasOwn(globalThis, 'console')); + for (const read of [() => console, () => console.log]) { + try { read() } catch (error) { Harness.inspect(error.name, error.message) } + } + `) + expect(caught.error).toBeUndefined() + expect(caught.logs).toEqual(['undefined undefined false', ...Array(2).fill('ReferenceError console is not defined')]) + const failed = await run('void console') + expect(failed.error?.kind).toBe('exception') + expect(failed.error?.message).toContain('ReferenceError: console is not defined') + expect(runtime.executionInstructions).toContain('Console is unavailable. Use Harness.inspect(...values) for output.') + const local = await run('{ const console = { value: 42 }; Harness.inspect(console.value) }') + expect(local.error).toBeUndefined() + expect(local.logs).toEqual(['42']) + if (runtime.retainsState) { + expect((await run('var console = { value: 17 }')).error).toBeUndefined() + expect((await run('Harness.inspect(console.value)')).logs).toEqual(['17']) + } + }) + + it('routes all enabled console methods through inspect with independent arguments', async () => { + const { runtime, run } = await setup(true) + const result = await run(` + Harness.inspect("selected", { n: 1 }); + for (const method of ['log', 'info', 'warn', 'error', 'debug']) { + Harness.inspect(console[method] === Harness.inspect); + console[method]("%s", method); + } + `) + expect(result.error).toBeUndefined() + expect(result.logs).toEqual(['selected { n: 1 }', 'true', '%s log', 'true', '%s info', 'true', '%s warn', 'true', '%s error', 'true', '%s debug']) + expect(runtime.executionInstructions).toContain('Use Harness.inspect(...values) for output.') + expect(runtime.executionInstructions).not.toContain('console') + }) + + it('preserves completion values for direct runtime consumers that request them', async () => { + const { run, completion } = await setup() + expect((await run(completion('42'), false)).value).toBe(42) + }) +}) diff --git a/packages/ptc-runtime/ptc-runtime-node/tests/persistent-built.e2e.ts b/packages/ptc-runtime/ptc-runtime-node/tests/persistent-built.e2e.ts index 56d7546240..3b69797f08 100644 --- a/packages/ptc-runtime/ptc-runtime-node/tests/persistent-built.e2e.ts +++ b/packages/ptc-runtime/ptc-runtime-node/tests/persistent-built.e2e.ts @@ -22,7 +22,7 @@ describe.skipIf(!built)('persistent PTC published exports', () => { const stateOwner = {} const run = (program, timeoutMs, bindings = []) => ctx.ptcRuntime.run(ctx.ptcRuntime.resolve({program, timeoutMs, bindings, stateOwner})) const first = await run('let count: number = 40; const path = await import("node:path");') - const second = await run('count += 2; console.log(path.basename("/a/b")); count') + const second = await run('count += 2; Harness.inspect(path.basename("/a/b")); count') const interrupted = await run('try { await tools.wait({}) } catch {} finally { count++ }', 200, [{ global: 'tools', functions: { wait: () => new Promise(() => {}) } }]) const preserved = await run('count') await ctx.ptcRuntime.release(stateOwner) diff --git a/packages/ptc-runtime/ptc-runtime-node/tests/persistent-process.spec.ts b/packages/ptc-runtime/ptc-runtime-node/tests/persistent-process.spec.ts index 299f34a652..cc606c7819 100644 --- a/packages/ptc-runtime/ptc-runtime-node/tests/persistent-process.spec.ts +++ b/packages/ptc-runtime/ptc-runtime-node/tests/persistent-process.spec.ts @@ -64,7 +64,7 @@ it('keeps an environment while capturing each evaluation and binding replies sep const h = await setup(diagnosticBudget) const declarations = [{ global: 'tools', names: ['echo'], errorClass: { name: 'ToolCallError', memberNameProperty: 'toolName' } }] expect(await h.run('let count = 1; const saved = tools.echo;', declarations)).toEqual({}) - const result = await h.run('count++; process.stdout.write("raw"); console.log("value", count); await saved({})', declarations) + const result = await h.run('count++; process.stdout.write("raw"); Harness.inspect("value", count); await saved({})', declarations) expect(decodePtcJsonWire(result.value)).toBe(42) expect(h.seen.filter(item => item.type === 'log').map(item => item.text)).toEqual(['raw', 'value 2']) expect(h.state.env).toEqual({}) @@ -108,7 +108,7 @@ it('contains directory failures and output overflow in the active evaluation', a expect(failed).toMatchObject({ error: { kind: 'exception' } }) expect((failed.error as { message: string }).message).toContain('directory missing') const h = await setup() - await h.run('console.log("x".repeat(100))', [], 20) + await h.run('Harness.inspect("x".repeat(100))', [], 20) expect(h.seen.some(item => item.type === 'output-limit')).toBe(true) }) @@ -121,7 +121,7 @@ it('closes the process channel when a terminal result cannot fit its write budge it('ignores output and rejects new tool calls from a completed evaluation', async () => { const h = await setup() const declarations = [{ global: 'tools', names: ['echo'] }] - await h.run('let finish; let old; const trigger = new Promise(resolve => { finish = resolve }); void trigger.then(async () => { console.log("late"); try { await tools.echo({}) } catch (error) { old = error.message } });', declarations) + await h.run('let finish; let old; const trigger = new Promise(resolve => { finish = resolve }); void trigger.then(async () => { Harness.inspect("late"); try { await tools.echo({}) } catch (error) { old = error.message } });', declarations) const result = await h.run('finish(); await new Promise(resolve => setImmediate(resolve)); old', declarations) expect(decodePtcJsonWire(result.value)).toBe('PTC evaluation ended') expect(h.seen.some(item => item.type === 'log' || item.type === 'call')).toBe(false) @@ -194,7 +194,7 @@ it.each(['repl', 'script'] as const)('keeps run pending until an arbitrary user const started = Promise.withResolvers() // A host-controlled Promise models asynchronous work outside the managed binding wait. Object.defineProperty(h.state, 'wait', { value: () => { started.resolve(undefined); return resume.promise } }) - const first = h.run('var mutation = 0; var serialized = 0; await process.wait(); mutation++; console.log("late"); ({ get value() { serialized++; return 1 } })') + const first = h.run('var mutation = 0; var serialized = 0; await process.wait(); mutation++; Harness.inspect("late"); ({ get value() { serialized++; return 1 } })') await started.promise const settled = vi.fn() void first.then(settled) diff --git a/packages/ptc-runtime/ptc-runtime-node/tests/persistent.spec.ts b/packages/ptc-runtime/ptc-runtime-node/tests/persistent.spec.ts index 68cd9a6afb..610e804b2a 100644 --- a/packages/ptc-runtime/ptc-runtime-node/tests/persistent.spec.ts +++ b/packages/ptc-runtime/ptc-runtime-node/tests/persistent.spec.ts @@ -75,7 +75,7 @@ describe.each([ it('retains declarations, closures, object identity, and imports across evaluations', async () => { const { run, cwd } = await setup() expect((await run('const box: { count: number } = { count: 1 }; let alias = box; function increment() { return ++box.count }; const path = await import("node:path");')).error).toBeUndefined() - expect(await run('console.log("second call"); ({ count: increment(), same: box === alias, dir: path.basename(process.cwd()), env: Object.keys(process.env) })')).toMatchObject({ logs: ['second call'], value: { count: 2, same: true, dir: cwd.split(/[\\/]/).at(-1), env: [] } }) + expect(await run('Harness.inspect("second call"); ({ count: increment(), same: box === alias, dir: path.basename(process.cwd()), env: Object.keys(process.env) })')).toMatchObject({ logs: ['second call'], value: { count: 2, same: true, dir: cwd.split(/[\\/]/).at(-1), env: [] } }) }) @@ -87,14 +87,14 @@ describe.each([ expect((await run('await query()', { bindings: bindings(second) })).value).toBe('new') expect(first).toHaveBeenCalledTimes(1) expect(second).toHaveBeenCalledTimes(1) - expect((await run('let failure; try { await query() } catch (error) { console.log(error.toolName); failure = "unavailable" }; failure')).value).toBe('unavailable') + expect((await run('let failure; try { await query() } catch (error) { Harness.inspect(error.toolName); failure = "unavailable" }; failure')).value).toBe('unavailable') expect(second).toHaveBeenCalledTimes(1) }) it('runs top-level await blocks with reusable local declarations and keeps outer state', async () => { const { run } = await setup() await run('let total = 0;') - const code = '{ const amount = await tools.value({}); total += amount; console.log(amount); }' + const code = '{ const amount = await tools.value({}); total += amount; Harness.inspect(amount); }' expect(await run(code, { bindings: bindings(async () => 2) })).toMatchObject({ logs: ['2'] }) expect(await run(code, { bindings: bindings(async () => 3) })).toMatchObject({ logs: ['3'] }) expect(await run('({ total, local: typeof amount })')).toMatchObject({ logs: [], value: { total: 5, local: 'undefined' } }) @@ -229,7 +229,7 @@ describe.each([ const controller = new AbortController() const entered = Promise.withResolvers() await run('var kept = { count: 1 }; var alias = kept; var effects = [];') - const pending = run('kept.count++; console.log("before stop"); await tools.value({})', { + const pending = run('kept.count++; Harness.inspect("before stop"); await tools.value({})', { timeoutMs: action === 'timeout' ? 200 : null, signal: controller.signal, bindings: bindings(() => { entered.resolve(undefined); return new Promise(() => {}) }), }) @@ -252,7 +252,7 @@ describe.each([ const { run, cwd } = await setup({ stopGraceMs: 50 }) await run('var kept = 42; var fs = await import("node:fs");') const controller = new AbortController() - const pending = run(`${prefix} console.log("before stop"); fs.writeFileSync("entered-loop", "ready"); while (true) {}`, { + const pending = run(`${prefix} Harness.inspect("before stop"); fs.writeFileSync("entered-loop", "ready"); while (true) {}`, { timeoutMs: action === 'timeout' ? 1000 : null, signal: controller.signal, }) try { @@ -388,7 +388,7 @@ describe.each([ it('bounds output and clears state after an overflow', async () => { const { run } = await setup({ maxOutputBytes: 100 }) - expect((await run('let retained = true; console.log("x".repeat(1000))')).error?.kind).toBe('output-limit') + expect((await run('let retained = true; Harness.inspect("x".repeat(1000))')).error?.kind).toBe('output-limit') expect((await run('typeof retained')).value).toBe('undefined') }) }) diff --git a/packages/ptc-runtime/ptc-runtime-node/tests/process-main.spec.ts b/packages/ptc-runtime/ptc-runtime-node/tests/process-main.spec.ts index d5da47ff09..f9d190f57c 100644 --- a/packages/ptc-runtime/ptc-runtime-node/tests/process-main.spec.ts +++ b/packages/ptc-runtime/ptc-runtime-node/tests/process-main.spec.ts @@ -49,7 +49,7 @@ it('clears the environment and returns a binding result after captured output', const args = decodePtcJsonWire(wire) as { n: number } return encodePtcJsonWire(args.n * 2) } - const result = await h.run('const doubled = await tools.echo({ n: 21 }); console.log("got", doubled); return { doubled };') + const result = await h.run('const doubled = await tools.echo({ n: 21 }); Harness.inspect("got", doubled); return { doubled };') expect(decodePtcJsonWire(result.value)).toEqual({ doubled: 42 }) expect(h.logs).toEqual(['got 42']) expect(h.state.env).toEqual({}) @@ -66,7 +66,7 @@ it('keeps unfinished temporary calls pending and suppresses later logs while awa const release = Promise.withResolvers>() h.methods.invoke = () => release.promise try { - expect(decodePtcJsonWire((await h.run('void tools.echo({}).catch(() => {}); void tools.echo({}).catch(() => console.log("late rejection")); setImmediate(() => console.log("late timer")); return 42;')).value)).toBe(42) + expect(decodePtcJsonWire((await h.run('void tools.echo({}).catch(() => {}); void tools.echo({}).catch(() => Harness.inspect("late rejection")); setImmediate(() => Harness.inspect("late timer")); return 42;')).value)).toBe(42) await nextTurn() expect(h.child.destroyed).toBe(false) release.resolve(encodePtcJsonWire(42)) @@ -99,7 +99,7 @@ it.each([4, 64])('reports exhausted output accounting during the run with a %s-b const h = await setup() const limited = vi.fn() h.methods.outputLimit = limited - expect(await h.run('console.log("x".repeat(100)); return null', maxOutputBytes)) + expect(await h.run('Harness.inspect("x".repeat(100)); return null', maxOutputBytes)) .toEqual({ error: { kind: 'output-limit', message: `outer output exceeded ${maxOutputBytes} bytes` } }) if (maxOutputBytes === 4) expect(h.logs).toEqual([]) await vi.waitFor(() => { expect(limited).toHaveBeenCalledOnce() }) @@ -138,7 +138,7 @@ it('sends no binding calls or output notifications after returning its result', const limited = vi.fn() h.methods.invoke = invoked h.methods.outputLimit = limited - const result = await h.run('setImmediate(() => { void tools.echo({}).catch(() => {}); console.log("x".repeat(2000)) }); return 1', 64) + const result = await h.run('setImmediate(() => { void tools.echo({}).catch(() => {}); Harness.inspect("x".repeat(2000)) }); return 1', 64) expect(decodePtcJsonWire(result.value)).toBe(1) await nextTurn() await nextTurn() @@ -167,7 +167,7 @@ it('accepts host shutdown before the first evaluation', async () => { }) it.each([ - 'console.log("x".repeat(2000));', + 'Harness.inspect("x".repeat(2000));', 'return "x".repeat(2000)', ])('closes on control output overflow: %s', async (code) => { const h = await setup(1024) diff --git a/packages/ptc-runtime/ptc-runtime-node/tests/runtime.spec.ts b/packages/ptc-runtime/ptc-runtime-node/tests/runtime.spec.ts index 5a9e42e506..9cf9458003 100644 --- a/packages/ptc-runtime/ptc-runtime-node/tests/runtime.spec.ts +++ b/packages/ptc-runtime/ptc-runtime-node/tests/runtime.spec.ts @@ -48,7 +48,7 @@ describe('Node program process', () => { it('runs erasable TypeScript in the resolved directory with an empty environment', async () => { const { run, cwd } = await setup() - const result = await run({ program: 'const n: number = 6; console.log("ready"); return { n: n * 7, cwd: process.cwd(), env: { ...process.env } };', bindings: [] }) + const result = await run({ program: 'const n: number = 6; Harness.inspect("ready"); return { n: n * 7, cwd: process.cwd(), env: { ...process.env } };', bindings: [] }) expect(result.error).toBeUndefined() expect(result.value).toEqual({ n: 42, cwd, env: {} }) expect(result.logs).toEqual(['ready']) @@ -140,7 +140,7 @@ describe('Node program process', () => { it('enforces the combined output cap', async () => { const { run } = await setup({ maxOutputBytes: 80 }) - const result = await run({ program: 'console.log("x".repeat(500)); return 42;', bindings: [] }) + const result = await run({ program: 'Harness.inspect("x".repeat(500)); return 42;', bindings: [] }) expect(result.error?.kind).toBe('output-limit') const bytes = Buffer.byteLength(JSON.stringify(result.logs)) + Buffer.byteLength(JSON.stringify(result.error?.message)) expect(bytes).toBeLessThanOrEqual(80) @@ -149,7 +149,7 @@ describe('Node program process', () => { it('uses the default deadline and caps explicit requests', async () => { const { runtime } = await setup() expect(runtime.timeout).toEqual({ defaultMs: 120_000, maxMs: 600_000 }) - expect(runtime.executionInstructions).toBe('The program is the body of an async TypeScript function; only erasable syntax is accepted, with no enum or namespaces. Type annotations are advisory. Top-level await and return are supported. Emit output with console.log(...) or return a lossless JSON value. Only printed values and the returned value become program output. Each call runs in a fresh Node process. Node APIs are available through await import(...). Relative paths use the supplied working directory; process.env starts empty. Direct filesystem access follows this execution\'s sandbox policy.') + expect(runtime.executionInstructions).toBe('The program is the body of an async TypeScript function; only erasable syntax is accepted, with no enum or namespaces. Type annotations are advisory. Top-level await and return are supported. Console is unavailable. Use Harness.inspect(...values) for output. Each call runs in a fresh Node process. Node APIs are available through await import(...). Relative paths use the supplied working directory; process.env starts empty. Direct filesystem access follows this execution\'s sandbox policy.') expect(runtime.resolve({ program: '', bindings: [] }).timeoutMs).toBe(120_000) expect(runtime.resolve({ program: '', bindings: [], timeoutMs: 900_000 }).timeoutMs).toBe(600_000) expect(runtime.resolve({ program: '', bindings: [], timeoutMs: null }).timeoutMs).toBeNull() @@ -168,11 +168,11 @@ describe('Node program process', () => { expect((await run({ program, bindings: [] })).error?.kind).toBe('timeout') }) - it('retains console output when a non-yielding program is canceled', async () => { + it('retains inspected output when a non-yielding program is canceled', async () => { const { run, cwd } = await setup() const controller = new AbortController() const pending = run({ - program: 'const fs = await import("node:fs"); console.log("before hot loop"); await tools.enter({}); fs.writeFileSync("entered-loop", "ready"); for (;;) {}', + program: 'const fs = await import("node:fs"); Harness.inspect("before hot loop"); await tools.enter({}); fs.writeFileSync("entered-loop", "ready"); for (;;) {}', signal: controller.signal, timeoutMs: null, bindings: bindings({ enter: async () => null }), }) @@ -322,7 +322,7 @@ describe('Node program process', () => { it('preserves empty console entries and bounds native output overflow', async () => { const { run } = await setup({ maxOutputBytes: 100 }) - const lines = await run({ program: 'console.log("a"); console.log(""); console.log("b")', bindings: [] }) + const lines = await run({ program: 'Harness.inspect("a"); Harness.inspect(""); Harness.inspect("b")', bindings: [] }) expect(lines.logs).toEqual(['a', '', 'b']) const overflow = await run({ program: 'const fs=await import("node:fs"); fs.writeSync(1,"HEAD-"+"x".repeat(100000));', bindings: [] }) expect(overflow.error?.kind).toBe('output-limit') diff --git a/packages/ptc-runtime/ptc-runtime/README.i18n.yaml b/packages/ptc-runtime/ptc-runtime/README.i18n.yaml index bce60e0d33..75c619d9a6 100644 --- a/packages/ptc-runtime/ptc-runtime/README.i18n.yaml +++ b/packages/ptc-runtime/ptc-runtime/README.i18n.yaml @@ -18,14 +18,14 @@ en: 346f62bbfd0e6d9d zh: bf86b2d7a26e8193 /deepseek-ai-dsh-ptc-runtime/use-this-package/run-a-program: - en: aa58e5977dc6c33b - zh: cfa416436582b1cc + en: 7703e18ca431e362 + zh: 6240c5fcb5125baa /deepseek-ai-dsh-ptc-runtime/use-this-package/choose-a-backend: en: fc0d515f0c3664f3 zh: 0a1b646b5cf92232 /deepseek-ai-dsh-ptc-runtime/use-this-package/name-your-bindings-portably: - en: 4e46c1672701b8df - zh: 0353c5855c9ff6be + en: 4d94356353cacc7d + zh: ea16d83f31a432ed /deepseek-ai-dsh-ptc-runtime/use-this-package/what-can-go-wrong: en: 44f9fdf2c8e2cacc zh: 99dd555a49075f88 @@ -42,8 +42,8 @@ en: 855cb8366e3bd293 zh: da6d7a0ce51f97fc /deepseek-ai-dsh-ptc-runtime/understand-the-implementation/portable-identifiers: - en: 01c82c6e73180979 - zh: e29b066f9f82c8b4 + en: fb90e052246e83b1 + zh: 64560b6532793f9c /deepseek-ai-dsh-ptc-runtime/understand-the-implementation/source-map: en: 85b65ceb9be0c49f zh: 52df7f06d4e49d6e diff --git a/packages/ptc-runtime/ptc-runtime/README.md b/packages/ptc-runtime/ptc-runtime/README.md index eb9a2e3c6d..a91630c90b 100644 --- a/packages/ptc-runtime/ptc-runtime/README.md +++ b/packages/ptc-runtime/ptc-runtime/README.md @@ -31,6 +31,8 @@ Choose this package when you compose a deployment that executes model-written pr Call `resolve(request)` followed by `run(spec)` with a program and binding namespaces. Resolution validates cwd, timeout and sandbox choices and applies provider defaults. `programFormat: PtcProgramFormat` selects an async function body with top-level `await` and `return`, or REPL/Script statements with top-level declarations, `await`, and a final expression. The Node Script provider lowers top-level await and awaits the completion value; its [declaration limitations](../ptc-runtime-node/README.md#known-limitations-and-deferred-work) apply to transformed programs. Lossless-JSON completion becomes `result.value`, text becomes `result.logs`, and failures become `result.error`. +Set `discardCompletion: true` to omit the successful completion without JSON validation or serialization; captured logs and failures are still returned. The TypeScript tool executor selects this option for explicit program output, while workflow consumers retain completion values. + ```text const spec = ctx.ptcRuntime.resolve({ program: 'return await tools.add({ a: 1, b: 2 })', @@ -48,7 +50,7 @@ For the optional [Node in-memory mode](../ptc-runtime-node/README.md#persistent- ### Name your bindings portably -Binding-global and error-class names are language-portable: they must match `[A-Za-z_][A-Za-z0-9_]*`, avoid every portable target language's reserved words, and avoid backend-owned slots, so one namespace list is valid against every backend. A name like `$tools`, `lambda`, or `console` fails the run before it starts; the exact exclusion sets are part of the seam contract. +Binding-global and error-class names are language-portable: they must match `[A-Za-z_][A-Za-z0-9_]*`, avoid every portable target language's reserved words, and avoid backend-owned slots, so one namespace list is valid against every backend. A name like `$tools`, `lambda`, `console`, or `Harness` fails the run before it starts; the exact exclusion sets are part of the seam contract. ### What can go wrong @@ -84,7 +86,7 @@ The exhaustive semantics live in the [PTC runtime subsystem reference](../../../ ### Portable identifiers -Binding-global and error-class names are language-portable: they must match the identifier subset `[A-Za-z_][A-Za-z0-9_]*` (no JS-only `$`) and clear the seam-exported exclusion sets, so one `bindings` list is valid against every backend. The package exports the contract every backend enforces — `PORTABLE_RESERVED_WORDS` (ECMAScript ∪ Python reserved words), `RESERVED_BINDING_GLOBALS` (backend-owned globals such as `console` and `__dsh_main__`), `RESERVED_ERROR_MEMBERS` and `DUNDER_MEMBER` (error-member exclusions) — so a name like `$tools`, `lambda`, or `__dsh_main__` makes `run()` reject as seam misuse on any backend. See `src/index.ts` for the exact sets. +Binding-global and error-class names are language-portable: they must match the identifier subset `[A-Za-z_][A-Za-z0-9_]*` (no JS-only `$`) and clear the seam-exported exclusion sets, so one `bindings` list is valid against every backend. The package exports the contract every backend enforces — `PORTABLE_RESERVED_WORDS` (ECMAScript ∪ Python reserved words), `RESERVED_BINDING_GLOBALS` (backend-owned globals such as `console`, `Harness` and `__dsh_main__`), `RESERVED_ERROR_MEMBERS` and `DUNDER_MEMBER` (error-member exclusions) — so a name like `$tools`, `lambda`, or `__dsh_main__` makes `run()` reject as seam misuse on any backend. See `src/index.ts` for the exact sets. ### Source map diff --git a/packages/ptc-runtime/ptc-runtime/README.zh.md b/packages/ptc-runtime/ptc-runtime/README.zh.md index 0fef2d6b77..a8cd4faf37 100644 --- a/packages/ptc-runtime/ptc-runtime/README.zh.md +++ b/packages/ptc-runtime/ptc-runtime/README.zh.md @@ -31,6 +31,8 @@ kind: "package-reference" 传入程序和绑定命名空间,依次调用 `resolve(request)` 和 `run(spec)`。解析阶段验证 cwd、超时与沙盒选项,并应用提供方默认值。`programFormat: PtcProgramFormat` 选择支持顶层 `await`、`return` 的异步函数体,或支持顶层声明、`await` 和末尾表达式的 REPL/Script 语句。Node Script 提供方转换顶层 await 并等待完成值;其[声明限制](../ptc-runtime-node/README.zh.md#known-limitations-and-deferred-work)适用于转换后的程序。无损 JSON 完成值进入 `result.value`,文本进入 `result.logs`,失败进入 `result.error`。 +设置 `discardCompletion: true` 可跳过成功完成值的 JSON 验证与序列化,并从结果中省略该值;捕获日志与失败信息仍会返回。TypeScript 工具执行器通过此选项提供显式程序输出,workflow 消费方则继续保留完成值。 + ```text const spec = ctx.ptcRuntime.resolve({ program: 'return await tools.add({ a: 1, b: 2 })', @@ -48,7 +50,7 @@ const result = await ctx.ptcRuntime.run(spec) ### 可移植地命名绑定 -binding-global 与 error-class 名称是语言可移植的:必须匹配 `[A-Za-z_][A-Za-z0-9_]*`,避开每个可移植目标语言的保留字,并避开后端拥有的槽位,因此同一份命名空间列表对每个后端都有效。`$tools`、`lambda` 或 `console` 之类的名称会在运行开始前失败;确切的排除集是 seam 约定的一部分。 +binding-global 与 error-class 名称是语言可移植的:必须匹配 `[A-Za-z_][A-Za-z0-9_]*`,避开每个可移植目标语言的保留字,并避开后端拥有的槽位,因此同一份命名空间列表对每个后端都有效。`$tools`、`lambda`、`console` 或 `Harness` 之类的名称会在运行开始前失败;确切的排除集是 seam 约定的一部分。 ### 可能出什么问题 @@ -84,7 +86,7 @@ binding-global 与 error-class 名称是语言可移植的:必须匹配 `[A-Za ### 可移植标识符 -binding-global 与 error-class 名称是语言可移植的:必须匹配标识符子集 `[A-Za-z_][A-Za-z0-9_]*`(不含 JS 专有的 `$`)并通过 seam 导出的排除集,因此同一份 `bindings` 列表对每个后端都有效。本包导出每个后端都执行的约定——`PORTABLE_RESERVED_WORDS`(ECMAScript ∪ Python 保留字)、`RESERVED_BINDING_GLOBALS`(如 `console`、`__dsh_main__` 等后端拥有的 global)、`RESERVED_ERROR_MEMBERS` 与 `DUNDER_MEMBER`(error-member 排除)——因此 `$tools`、`lambda` 或 `__dsh_main__` 之类的名称会让 `run()` 在任何后端上作为 seam 误用而 reject。确切集合见 `src/index.ts`。 +binding-global 与 error-class 名称是语言可移植的:必须匹配标识符子集 `[A-Za-z_][A-Za-z0-9_]*`(不含 JS 专有的 `$`)并通过 seam 导出的排除集,因此同一份 `bindings` 列表对每个后端都有效。本包导出每个后端都执行的约定——`PORTABLE_RESERVED_WORDS`(ECMAScript ∪ Python 保留字)、`RESERVED_BINDING_GLOBALS`(如 `console`、`Harness`、`__dsh_main__` 等后端拥有的 global)、`RESERVED_ERROR_MEMBERS` 与 `DUNDER_MEMBER`(error-member 排除)——因此 `$tools`、`lambda` 或 `__dsh_main__` 之类的名称会让 `run()` 在任何后端上作为 seam 误用而 reject。确切集合见 `src/index.ts`。 ### 源码地图 diff --git a/packages/ptc-runtime/ptc-runtime/src/index.ts b/packages/ptc-runtime/ptc-runtime/src/index.ts index 2fa080fcde..6a7442993e 100644 --- a/packages/ptc-runtime/ptc-runtime/src/index.ts +++ b/packages/ptc-runtime/ptc-runtime/src/index.ts @@ -23,7 +23,7 @@ export type { /** * Binding globals EVERY backend refuses because SOME backend owns the slot in - * the program's namespace: `console` (Node's log capture), and + * the program's namespace: `console` and `Harness` (Node's output bindings), and * `__dsh_main__`/`__builtins__`/`__name__` (the Python backend's bootstrap * wrapper and seeded module globals), * and `__debug__`. One shared set — rather than each backend refusing only its @@ -41,7 +41,7 @@ export type { * Python backend, which is exactly the split the shared set exists to prevent. */ export const RESERVED_BINDING_GLOBALS: ReadonlySet = new Set([ - 'console', + 'console', 'Harness', '__dsh_main__', '__builtins__', '__name__', '__debug__', ]) diff --git a/packages/ptc-runtime/ptc-runtime/src/types.ts b/packages/ptc-runtime/ptc-runtime/src/types.ts index 64af15c5fe..734f2ae4d6 100644 --- a/packages/ptc-runtime/ptc-runtime/src/types.ts +++ b/packages/ptc-runtime/ptc-runtime/src/types.ts @@ -84,6 +84,8 @@ export interface PtcRunRequest { * becomes {@link PtcRunResult.value}. */ program: string + /** Discard the completion without JSON validation when true; omission preserves the completion value. */ + discardCompletion?: boolean /** * Object-identity owner for optional in-memory state. Omission requests a fresh * environment. Consumers must await runtime.release(owner) when the owner ends. diff --git a/packages/spill/spill-policy/tests/spill-policy.spec.ts b/packages/spill/spill-policy/tests/spill-policy.spec.ts index 11b5f0adb4..b4fcda56cf 100644 --- a/packages/spill/spill-policy/tests/spill-policy.spec.ts +++ b/packages/spill/spill-policy/tests/spill-policy.spec.ts @@ -240,7 +240,7 @@ describe('outer PTC mode failure capture', () => { callId: ToolCallId('code-output-limit'), name: 'run_code', arguments: { - code: 'console.log("HEAD-" + "x".repeat(300)); console.log("TAIL-" + "y".repeat(300)); return "unreachable";', + code: 'Harness.inspect("HEAD-" + "x".repeat(300)); Harness.inspect("TAIL-" + "y".repeat(300)); return "unreachable";', description: 'Print oversized head and tail lines', }, agent: agent as never, @@ -294,11 +294,11 @@ describe('the durable dispatch-log arm', () => { it('bounds the tool/ptc-dispatch copy of an oversized sub-result while the program value stays whole', async () => { const { result, events, spill } = await runCodeWith( - 'const blocks = await tools.huge_read({});\nreturn blocks[0].text.length', 200) + 'const blocks = await tools.huge_read({});\nHarness.inspect(blocks[0].text.length)', 200) expect(result.isError).toBe(false) if (result.isError) throw new Error('expected success') // The program received the COMPLETE text (length 2000), untouched by spill. - expect(result.value).toMatchObject({ result: 2_000 }) + expect(result.value).toMatchObject({ logs: ['2000'] }) // The durable settle event carries the bounded projection + locator. const settle = events.find(event => event.type === 'tool/ptc-dispatch') expect(settle).toBeDefined() @@ -370,7 +370,7 @@ describe('the durable dispatch-log arm', () => { name: 'run_code', arguments: { // Both program values remain available while the durable spill is blocked. - code: 'const big = await tools.huge_read({});\nconst small = await tools.small_read({});\nreturn big[0].text.length + small[0].text.length', + code: 'const big = await tools.huge_read({});\nconst small = await tools.small_read({});\nHarness.inspect(big[0].text.length + small[0].text.length)', description: 'Prove log shaping is off the program path', }, agent: agent as never, @@ -385,7 +385,7 @@ describe('the durable dispatch-log arm', () => { const result = await runPromise expect(result.isError).toBe(false) if (result.isError) throw new Error('expected success') - expect(result.value).toMatchObject({ result: 2_004 }) + expect(result.value).toMatchObject({ logs: ['2004'] }) expect(events.filter(event => event.type === 'tool/ptc-dispatch')).toHaveLength(2) } finally { saveGate.resolve(undefined) diff --git a/scripts/smoke-python-runtime.py b/scripts/smoke-python-runtime.py index 92611f23bc..6b5a7ebd4c 100644 --- a/scripts/smoke-python-runtime.py +++ b/scripts/smoke-python-runtime.py @@ -508,7 +508,7 @@ def completion_chunks(body: dict[str, object]) -> list[dict[str, object]]: raise AssertionError("PTC mode must advertise only run_code") return tool_call_chunks( "advanced-code", "run_code", - {"code": "return await tools.snapshot_double({ value: 21 })", + {"code": "Harness.inspect(await tools.snapshot_double({ value: 21 }))", "description": "Call the configured Plugin tool"}, ) assert_advertised_tool(body, "snapshot_double") @@ -692,7 +692,7 @@ def advanced_tool_followup( if "42" not in tool_text: raise AssertionError(f"run_code returned no configured-tool value: {tool_text}") return tool_call_chunks("advanced-denied-ptc", "run_code", { - "code": "try { await tools.snapshot_double({ value: -1 }) } catch (error) { return error.message }", + "code": "try { await tools.snapshot_double({ value: -1 }) } catch (error) { Harness.inspect(error.message) }", "description": "Catch a structured inner tool denial", }) if call_id == "advanced-denied-ptc" and tool_name == "run_code": diff --git a/scripts/snapshots/python-sdk-single-exe/advanced-ptc/session.v4.jsonl b/scripts/snapshots/python-sdk-single-exe/advanced-ptc/session.v4.jsonl index ecdd1eb24b..e18dfe643b 100644 --- a/scripts/snapshots/python-sdk-single-exe/advanced-ptc/session.v4.jsonl +++ b/scripts/snapshots/python-sdk-single-exe/advanced-ptc/session.v4.jsonl @@ -20,8 +20,8 @@ {"type":"image/offload","data":{"targets":[{"seq":13,"imageIndexes":[0]}]}} {"type":"request/header","data":{"header":{"config":{"provider":"deepseek-official","model":"smoke-model","maxTokens":256000,"reasoningEffort":"high"},"adapterDefaults":{"reasoningEffort":true,"maxTokens":true},"tools":["run_code"]},"reason":"series"}} {"type":"session-log-deepseek/delivery-accepted","data":{"sessionId":"{{parent}}","sessionFormatVersion":4,"throughSeq":19}} -{"type":"assistant/message","data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"tool-call","id":"advanced-code","name":"run_code","arguments":"{\"code\": \"return await tools.snapshot_double({ value: 21 })\", \"description\": \"Call the configured Plugin tool\"}"}],"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-code","name":"run_code","args":[""]},{"type":"tool-call-chunks","time0":0,"index":0,"dt":[],"id":"advanced-code","args":["{\"code\": \"return await tools.snapshot_double({ value: 21 })\", \"description\": \"Call the configured Plugin tool\"}"]},{"type":"chunk","time":0,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"advanced-code","name":"run_code","arguments":"{\"code\": \"return await tools.snapshot_double({ value: 21 })\", \"description\": \"Call the configured Plugin tool\"}"}}},{"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":1,"callId":"advanced-code","name":"run_code","arguments":"{\"code\": \"return await tools.snapshot_double({ value: 21 })\", \"description\": \"Call the configured Plugin tool\"}"}} +{"type":"assistant/message","data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"tool-call","id":"advanced-code","name":"run_code","arguments":"{\"code\": \"Harness.inspect(await tools.snapshot_double({ value: 21 }))\", \"description\": \"Call the configured Plugin tool\"}"}],"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-code","name":"run_code","args":[""]},{"type":"tool-call-chunks","time0":0,"index":0,"dt":[],"id":"advanced-code","args":["{\"code\": \"Harness.inspect(await tools.snapshot_double({ value: 21 }))\", \"description\": \"Call the configured Plugin tool\"}"]},{"type":"chunk","time":0,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"advanced-code","name":"run_code","arguments":"{\"code\": \"Harness.inspect(await tools.snapshot_double({ value: 21 }))\", \"description\": \"Call the configured Plugin tool\"}"}}},{"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":1,"callId":"advanced-code","name":"run_code","arguments":"{\"code\": \"Harness.inspect(await tools.snapshot_double({ value: 21 }))\", \"description\": \"Call the configured Plugin tool\"}"}} {"type":"tool/ptc-dispatch-start","data":{"rootCallId":"advanced-code","parentCallId":"advanced-code","subCallId":"advanced-code:ptc:1","name":"snapshot_double","arguments":{"value":21}}} {"type":"job/registered","data":{"jobId":"{{id:1}}","owner":"{{parent}}","kind":"snapshot","label":"snapshot_double","startedAt":0,"toolCall":{"callId":"advanced-code:ptc:1","rootCallId":"advanced-code"}}} {"type":"job/settled","data":{"jobId":"{{id:1}}","owner":"{{parent}}","kind":"snapshot","label":"snapshot_double","startedAt":0,"toolCall":{"callId":"advanced-code:ptc:1","rootCallId":"advanced-code"},"finishedAt":0,"status":"completed","cause":"producer","producerReported":true}} @@ -30,8 +30,8 @@ {"type":"step/end","data":{"turn":1,"step":1}} {"type":"step/start","data":{"turn":1,"step":2}} {"type":"session-log-deepseek/delivery-accepted","data":{"sessionId":"{{parent}}","sessionFormatVersion":4,"throughSeq":29}} -{"type":"assistant/message","data":{"turn":1,"step":2,"message":{"role":"assistant","content":[{"type":"tool-call","id":"advanced-denied-ptc","name":"run_code","arguments":"{\"code\": \"try { await tools.snapshot_double({ value: -1 }) } catch (error) { return error.message }\", \"description\": \"Catch a structured inner tool denial\"}"}],"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-denied-ptc","name":"run_code","args":[""]},{"type":"tool-call-chunks","time0":0,"index":0,"dt":[],"id":"advanced-denied-ptc","args":["{\"code\": \"try { await tools.snapshot_double({ value: -1 }) } catch (error) { return error.message }\", \"description\": \"Catch a structured inner tool denial\"}"]},{"type":"chunk","time":0,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"advanced-denied-ptc","name":"run_code","arguments":"{\"code\": \"try { await tools.snapshot_double({ value: -1 }) } catch (error) { return error.message }\", \"description\": \"Catch a structured inner tool denial\"}"}}},{"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":2,"callId":"advanced-denied-ptc","name":"run_code","arguments":"{\"code\": \"try { await tools.snapshot_double({ value: -1 }) } catch (error) { return error.message }\", \"description\": \"Catch a structured inner tool denial\"}"}} +{"type":"assistant/message","data":{"turn":1,"step":2,"message":{"role":"assistant","content":[{"type":"tool-call","id":"advanced-denied-ptc","name":"run_code","arguments":"{\"code\": \"try { await tools.snapshot_double({ value: -1 }) } catch (error) { Harness.inspect(error.message) }\", \"description\": \"Catch a structured inner tool denial\"}"}],"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-denied-ptc","name":"run_code","args":[""]},{"type":"tool-call-chunks","time0":0,"index":0,"dt":[],"id":"advanced-denied-ptc","args":["{\"code\": \"try { await tools.snapshot_double({ value: -1 }) } catch (error) { Harness.inspect(error.message) }\", \"description\": \"Catch a structured inner tool denial\"}"]},{"type":"chunk","time":0,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"advanced-denied-ptc","name":"run_code","arguments":"{\"code\": \"try { await tools.snapshot_double({ value: -1 }) } catch (error) { Harness.inspect(error.message) }\", \"description\": \"Catch a structured inner tool denial\"}"}}},{"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":2,"callId":"advanced-denied-ptc","name":"run_code","arguments":"{\"code\": \"try { await tools.snapshot_double({ value: -1 }) } catch (error) { Harness.inspect(error.message) }\", \"description\": \"Catch a structured inner tool denial\"}"}} {"type":"tool/ptc-dispatch-start","data":{"rootCallId":"advanced-denied-ptc","parentCallId":"advanced-denied-ptc","subCallId":"advanced-denied-ptc:ptc:1","name":"snapshot_double","arguments":{"value":-1}}} {"type":"tool/ptc-dispatch","data":{"rootCallId":"advanced-denied-ptc","parentCallId":"advanced-denied-ptc","subCallId":"advanced-denied-ptc:ptc:1","name":"snapshot_double","arguments":{"value":-1},"isError":true,"error":{"name":"AutoReviewDeniedError","code":"AUTO_REVIEW_DENIED","reason":" transport raw\r\nreason "},"content":[{"type":"text","text":"Error: Auto review rejected tool \"snapshot_double\"; its body was not executed"}]}} {"type":"tool/result","data":{"turn":1,"step":2,"message":{"role":"tool","source":{"kind":"tool","callId":"advanced-denied-ptc"},"toolCallId":"advanced-denied-ptc","content":[{"type":"text","text":"Auto review rejected tool \"snapshot_double\"; its body was not executed"}],"isError":false,"id":"{{messageId}}"}},"sourceEventSeqs":[32],"surfaceOp":"append"} diff --git a/snapshots/sdk/desktop-ptc-context/system-prompt.expected.md b/snapshots/sdk/desktop-ptc-context/system-prompt.expected.md index 98d3bca7f1..8d3e254ea4 100644 --- a/snapshots/sdk/desktop-ptc-context/system-prompt.expected.md +++ b/snapshots/sdk/desktop-ptc-context/system-prompt.expected.md @@ -40,20 +40,24 @@ Bash, formatters, code generators, and scripts can overwrite concurrent edits wi `run_code` takes two required arguments: `code`, the body of an async TypeScript function (erasable syntax only — no `enum` or namespaces; type annotations are advisory, the code runs type-stripped), and `description`, a short summary of what the program does. The declarations below are SDK bindings for this program. A declaration does not make its name a directly callable tool; only names supplied as separate tool schemas may be called directly. When no separate `bash` schema is supplied, invoke a declared `bash` binding inside `run_code`: -`run_code({ code: "return await tools.bash({ description: 'Show current directory', command: 'pwd' })", description: "Show current directory" })` +`run_code({ code: "Harness.inspect(await tools.bash({ description: 'Show current directory', command: 'pwd' }))", description: "Show current directory" })` Inside the program: - Call tools as `await tools.name(args)` — quoted access for exotic names: `tools["my-tool"](args)`. Every call resolves to the tool's typed canonical JSON value. Tool arguments must be lossless JSON. - A FAILED tool call rejects with `ToolCallError`, whose `toolName` identifies the failed tool and whose `message` is human-readable — `try/catch` it to handle and continue. - Independent read-only calls MAY overlap under `Promise.all` (safe calls run concurrently; mutating calls run alone, in submission order). Sequence dependent work with `await`. -- Emit results with `return` and/or `console.log(...)`. Only what you print or return is program output. A successful tool result containing an image is attached after the run so you can inspect it on the next step; every other intermediate result stays out of the conversation, so extract just what you need. +- Show selected results with `Harness.inspect(...values)`. Return values and final expressions are not displayed. A successful tool result containing an image is attached after the run so you can inspect it on the next step; every other intermediate result stays out of the conversation, so extract just what you need. Program-only SDK bindings: ```ts type JsonValue = null | boolean | number | string | JsonValue[] | { [key: string]: JsonValue } +declare namespace Harness { + function inspect(...values: unknown[]): void; +} + interface ToolArgsMap { /** Create, update, delete, or move UTF-8 text files with one patch; prefer it over multiple edit calls when a change spans several files or separated regions. */ apply_patch: { diff --git a/snapshots/sdk/ptc-turn/notifications.expected.jsonl b/snapshots/sdk/ptc-turn/notifications.expected.jsonl index 7a7fcc7265..266557159a 100644 --- a/snapshots/sdk/ptc-turn/notifications.expected.jsonl +++ b/snapshots/sdk/ptc-turn/notifications.expected.jsonl @@ -10,8 +10,8 @@ {"method":"session.event","params":{"sessionId":"{{session:1}}","event":{"type":"request/header","seq":11,"time":0,"data":{"header":{"config":{"provider":"deepseek-official","model":"deepseek-v4-flash"},"tools":"{{tools}}"},"reason":"initial"}}}} {"method":"session.event","params":{"sessionId":"{{session:1}}","event":{"type":"request/context","seq":12,"time":0,"data":{"provider":"deepseek-official","model":"deepseek-v4-flash"}}}} {"method":"session.event","params":{"sessionId":"{{session:1}}","event":{"type":"session/title","seq":13,"time":0,"data":{"title":"Using ONE run_code program: call","messageSeqs":[8],"source":{"kind":"fallback"}}}}} -{"method":"session.event","params":{"sessionId":"{{session:1}}","event":{"type":"assistant/message","seq":14,"time":0,"data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"reasoning","text":"The user wants me to write a single run_code program that:\n1. Calls bash tool twice: `echo CODE_ONE` and `echo CODE_TWO`\n2. console.log exactly `captured output`\n3. Return the two outputs joined with a plus sign\n\nLet me write this."},{"type":"tool-call","id":"call_00_UiQPVqoELyzBZCY5pm1z7875","name":"run_code","arguments":"{\"code\": \"\\nconst out1 = await tools.bash({command: \\\"echo CODE_ONE\\\", description: \\\"Print CODE_ONE\\\"});\\nconst out2 = await tools.bash({command: \\\"echo CODE_TWO\\\", description: \\\"Print CODE_TWO\\\"});\\nconsole.log(\\\"captured output\\\");\\nconst text1 = out1.stdout.text.trim();\\nconst text2 = out2.stdout.text.trim();\\nreturn text1 + \\\"+\\\" + text2;\\n\", \"description\": \"Run two echo commands and join outputs\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:5}}"},"usage":{"inputTokens":6152,"outputTokens":214,"cacheReadTokens":0,"reasoningTokens":60},"stream":[{"type":"chunk","time":0,"chunk":{"type":"block-start","index":0,"blockType":"reasoning"}},{"type":"reasoning-chunks","time0":0,"index":0,"dt":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"texts":["The"," user"," wants"," me"," to"," write"," a"," single"," run","_code"," program"," that",":\n","1","."," Calls"," bash"," tool"," twice",":"," `","echo"," CODE","_","ONE","`"," and"," `","echo"," CODE","_T","WO","`\n","2","."," console",".log"," exactly"," `","capt","ured"," output","`\n","3","."," Return"," the"," two"," outputs"," joined"," with"," a"," plus"," sign","\n\n","Let"," me"," write"," this","."]},{"type":"chunk","time":0,"chunk":{"type":"block-start","index":1,"blockType":"tool-call"}},{"type":"tool-call-chunks","time0":0,"index":1,"dt":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"id":"call_00_UiQPVqoELyzBZCY5pm1z7875","name":"run_code","args":["","{","\"","code","\"",": ","\"","\\n","const"," out","1"," ="," await"," tools",".b","ash","({","command",":"," \\\"","echo"," CODE","_","ONE","\\\","," description",":"," \\\"","Print"," CODE","_","ONE","\\\"","});\\n","const"," out","2"," ="," await"," tools",".b","ash","({","command",":"," \\\"","echo"," CODE","_T","WO","\\\","," description",":"," \\\"","Print"," CODE","_T","WO","\\\"","});\\n","console",".log","(\\\"","capt","ured"," output","\\\");\\n","const"," text","1"," ="," out","1",".stdout",".text",".trim","();\\n","const"," text","2"," ="," out","2",".stdout",".text",".trim","();\\n","return"," text","1"," +"," \\\"+","\\\""," +"," text","2",";\\n","\"",", ","\"","description","\"",": ","\"","Run"," two"," echo"," commands"," and"," join"," outputs","\"","}"]},{"type":"chunk","time":0,"chunk":{"type":"block-end","index":0,"block":{"type":"reasoning","text":"The user wants me to write a single run_code program that:\n1. Calls bash tool twice: `echo CODE_ONE` and `echo CODE_TWO`\n2. console.log exactly `captured output`\n3. Return the two outputs joined with a plus sign\n\nLet me write this."}}},{"type":"chunk","time":0,"chunk":{"type":"block-end","index":1,"block":{"type":"tool-call","id":"call_00_UiQPVqoELyzBZCY5pm1z7875","name":"run_code","arguments":"{\"code\": \"\\nconst out1 = await tools.bash({command: \\\"echo CODE_ONE\\\", description: \\\"Print CODE_ONE\\\"});\\nconst out2 = await tools.bash({command: \\\"echo CODE_TWO\\\", description: \\\"Print CODE_TWO\\\"});\\nconsole.log(\\\"captured output\\\");\\nconst text1 = out1.stdout.text.trim();\\nconst text2 = out2.stdout.text.trim();\\nreturn text1 + \\\"+\\\" + text2;\\n\", \"description\": \"Run two echo commands and join outputs\"}"}}},{"type":"chunk","time":0,"chunk":{"type":"usage","usage":{"inputTokens":6152,"outputTokens":214,"cacheReadTokens":0,"reasoningTokens":60}}},{"type":"chunk","time":0,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"}}} -{"method":"session.event","params":{"sessionId":"{{session:1}}","event":{"type":"tool/call","seq":15,"time":0,"data":{"turn":1,"step":1,"callId":"call_00_UiQPVqoELyzBZCY5pm1z7875","name":"run_code","arguments":"{\"code\": \"\\nconst out1 = await tools.bash({command: \\\"echo CODE_ONE\\\", description: \\\"Print CODE_ONE\\\"});\\nconst out2 = await tools.bash({command: \\\"echo CODE_TWO\\\", description: \\\"Print CODE_TWO\\\"});\\nconsole.log(\\\"captured output\\\");\\nconst text1 = out1.stdout.text.trim();\\nconst text2 = out2.stdout.text.trim();\\nreturn text1 + \\\"+\\\" + text2;\\n\", \"description\": \"Run two echo commands and join outputs\"}"}}}} +{"method":"session.event","params":{"sessionId":"{{session:1}}","event":{"type":"assistant/message","seq":14,"time":0,"data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"reasoning","text":"The user wants me to write a single run_code program that:\n1. Calls bash tool twice: `echo CODE_ONE` and `echo CODE_TWO`\n2. console.log exactly `captured output`\n3. Return the two outputs joined with a plus sign\n\nLet me write this."},{"type":"tool-call","id":"call_00_UiQPVqoELyzBZCY5pm1z7875","name":"run_code","arguments":"{\"code\":\"\\nconst out1 = await tools.bash({command: \\\"echo CODE_ONE\\\", description: \\\"Print CODE_ONE\\\"});\\nconst out2 = await tools.bash({command: \\\"echo CODE_TWO\\\", description: \\\"Print CODE_TWO\\\"});\\nHarness.inspect(\\\"captured output\\\");\\nconst text1 = out1.stdout.text.trim();\\nconst text2 = out2.stdout.text.trim();\\nHarness.inspect(text1 + \\\"+\\\" + text2); return;\\n\",\"description\":\"Run two echo commands and join outputs\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:5}}"},"usage":{"inputTokens":6152,"outputTokens":214,"cacheReadTokens":0,"reasoningTokens":60},"stream":[{"type":"chunk","time":0,"chunk":{"type":"block-start","index":0,"blockType":"reasoning"}},{"type":"chunk","time":0,"chunk":{"type":"block-start","index":1,"blockType":"tool-call"}},{"type":"chunk","time":0,"chunk":{"type":"block-end","index":0,"block":{"type":"reasoning","text":"The user wants me to write a single run_code program that:\n1. Calls bash tool twice: `echo CODE_ONE` and `echo CODE_TWO`\n2. console.log exactly `captured output`\n3. Return the two outputs joined with a plus sign\n\nLet me write this."}}},{"type":"chunk","time":0,"chunk":{"type":"block-end","index":1,"block":{"type":"tool-call","id":"call_00_UiQPVqoELyzBZCY5pm1z7875","name":"run_code","arguments":"{\"code\":\"\\nconst out1 = await tools.bash({command: \\\"echo CODE_ONE\\\", description: \\\"Print CODE_ONE\\\"});\\nconst out2 = await tools.bash({command: \\\"echo CODE_TWO\\\", description: \\\"Print CODE_TWO\\\"});\\nHarness.inspect(\\\"captured output\\\");\\nconst text1 = out1.stdout.text.trim();\\nconst text2 = out2.stdout.text.trim();\\nHarness.inspect(text1 + \\\"+\\\" + text2); return;\\n\",\"description\":\"Run two echo commands and join outputs\"}"}}},{"type":"chunk","time":0,"chunk":{"type":"usage","usage":{"inputTokens":6152,"outputTokens":214,"cacheReadTokens":0,"reasoningTokens":60}}},{"type":"chunk","time":0,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"}}} +{"method":"session.event","params":{"sessionId":"{{session:1}}","event":{"type":"tool/call","seq":15,"time":0,"data":{"turn":1,"step":1,"callId":"call_00_UiQPVqoELyzBZCY5pm1z7875","name":"run_code","arguments":"{\"code\":\"\\nconst out1 = await tools.bash({command: \\\"echo CODE_ONE\\\", description: \\\"Print CODE_ONE\\\"});\\nconst out2 = await tools.bash({command: \\\"echo CODE_TWO\\\", description: \\\"Print CODE_TWO\\\"});\\nHarness.inspect(\\\"captured output\\\");\\nconst text1 = out1.stdout.text.trim();\\nconst text2 = out2.stdout.text.trim();\\nHarness.inspect(text1 + \\\"+\\\" + text2); return;\\n\",\"description\":\"Run two echo commands and join outputs\"}"}}}} {"method":"session.event","params":{"sessionId":"{{session:1}}","event":{"type":"tool/ptc-dispatch-start","seq":16,"time":0,"data":{"rootCallId":"call_00_UiQPVqoELyzBZCY5pm1z7875","parentCallId":"call_00_UiQPVqoELyzBZCY5pm1z7875","subCallId":"call_00_UiQPVqoELyzBZCY5pm1z7875:ptc:1","name":"bash","arguments":{"command":"echo CODE_ONE","description":"Print CODE_ONE"}}}}} {"method":"session.event","params":{"sessionId":"{{session:1}}","event":{"type":"job/registered","seq":17,"time":0,"data":{"jobId":"{{id:1}}","owner":"{{session:1}}","kind":"bash","label":"echo CODE_ONE","startedAt":0,"toolCall":{"callId":"call_00_UiQPVqoELyzBZCY5pm1z7875:ptc:1","rootCallId":"call_00_UiQPVqoELyzBZCY5pm1z7875"}}}}} {"method":"session.event","params":{"sessionId":"{{session:1}}","event":{"type":"job/settled","seq":18,"time":0,"data":{"jobId":"{{id:1}}","owner":"{{session:1}}","kind":"bash","label":"echo CODE_ONE","startedAt":0,"toolCall":{"callId":"call_00_UiQPVqoELyzBZCY5pm1z7875:ptc:1","rootCallId":"call_00_UiQPVqoELyzBZCY5pm1z7875"},"finishedAt":0,"status":"completed","cause":"producer","producerReported":true,"detail":"exit code: 0"}}}} @@ -23,7 +23,7 @@ {"method":"session.event","params":{"sessionId":"{{session:1}}","event":{"type":"tool/result","seq":24,"time":0,"data":{"turn":1,"step":1,"message":{"role":"tool","source":{"kind":"tool","callId":"call_00_UiQPVqoELyzBZCY5pm1z7875"},"toolCallId":"call_00_UiQPVqoELyzBZCY5pm1z7875","content":[{"type":"text","text":"captured output\nCODE_ONE+CODE_TWO"}],"isError":false,"id":"{{message:6}}"}},"sourceEventSeqs":[15],"surfaceOp":"append"}}} {"method":"session.event","params":{"sessionId":"{{session:1}}","event":{"type":"step/end","seq":25,"time":0,"data":{"turn":1,"step":1}}}} {"method":"session.event","params":{"sessionId":"{{session:1}}","event":{"type":"step/start","seq":26,"time":0,"data":{"turn":1,"step":2}}}} -{"method":"session.event","params":{"sessionId":"{{session:1}}","event":{"type":"assistant/message","seq":27,"time":0,"data":{"turn":1,"step":2,"message":{"role":"assistant","content":[{"type":"reasoning","text":"The program ran successfully. The console.log output \"captured output\" appeared, and the return value is \"CODE_ONE+CODE_TWO\". The user asked me to reply with that joined string only."},{"type":"text","text":"CODE_ONE+CODE_TWO"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:7}}"},"usage":{"inputTokens":117,"outputTokens":50,"cacheReadTokens":6272,"reasoningTokens":42},"stream":[{"type":"chunk","time":0,"chunk":{"type":"block-start","index":0,"blockType":"reasoning"}},{"type":"reasoning-chunks","time0":0,"index":0,"dt":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"texts":["The"," program"," ran"," successfully","."," The"," console",".log"," output"," \"","capt","ured"," output","\""," appeared",","," and"," the"," return"," value"," is"," \"","CODE","_","ONE","+","CODE","_T","WO","\"."," The"," user"," asked"," me"," to"," reply"," with"," that"," joined"," string"," only","."]},{"type":"chunk","time":0,"chunk":{"type":"block-start","index":1,"blockType":"text"}},{"type":"text-chunks","time0":0,"index":1,"dt":[0,0,0,0,0,0],"texts":["CODE","_","ONE","+","CODE","_T","WO"]},{"type":"chunk","time":0,"chunk":{"type":"block-end","index":0,"block":{"type":"reasoning","text":"The program ran successfully. The console.log output \"captured output\" appeared, and the return value is \"CODE_ONE+CODE_TWO\". The user asked me to reply with that joined string only."}}},{"type":"chunk","time":0,"chunk":{"type":"block-end","index":1,"block":{"type":"text","text":"CODE_ONE+CODE_TWO"}}},{"type":"chunk","time":0,"chunk":{"type":"usage","usage":{"inputTokens":117,"outputTokens":50,"cacheReadTokens":6272,"reasoningTokens":42}}},{"type":"chunk","time":0,"chunk":{"type":"finish","reason":{"kind":"stop"}}}]},"surfaceOp":"append"}}} +{"method":"session.event","params":{"sessionId":"{{session:1}}","event":{"type":"assistant/message","seq":27,"time":0,"data":{"turn":1,"step":2,"message":{"role":"assistant","content":[{"type":"reasoning","text":"The program ran successfully. The console.log output \"captured output\" appeared, and the return value is \"CODE_ONE+CODE_TWO\". The user asked me to reply with that joined string only."},{"type":"text","text":"CODE_ONE+CODE_TWO"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:7}}"},"usage":{"inputTokens":117,"outputTokens":50,"cacheReadTokens":6272,"reasoningTokens":42},"stream":[{"type":"chunk","time":0,"chunk":{"type":"block-start","index":0,"blockType":"reasoning"}},{"type":"chunk","time":0,"chunk":{"type":"block-start","index":1,"blockType":"text"}},{"type":"chunk","time":0,"chunk":{"type":"block-end","index":0,"block":{"type":"reasoning","text":"The program ran successfully. The console.log output \"captured output\" appeared, and the return value is \"CODE_ONE+CODE_TWO\". The user asked me to reply with that joined string only."}}},{"type":"chunk","time":0,"chunk":{"type":"block-end","index":1,"block":{"type":"text","text":"CODE_ONE+CODE_TWO"}}},{"type":"chunk","time":0,"chunk":{"type":"usage","usage":{"inputTokens":117,"outputTokens":50,"cacheReadTokens":6272,"reasoningTokens":42}}},{"type":"chunk","time":0,"chunk":{"type":"finish","reason":{"kind":"stop"}}}]},"surfaceOp":"append"}}} {"method":"session.event","params":{"sessionId":"{{session:1}}","event":{"type":"step/end","seq":28,"time":0,"data":{"turn":1,"step":2}}}} {"method":"session.event","params":{"sessionId":"{{session:1}}","event":{"type":"turn/end","seq":29,"time":0,"data":{"turn":1,"reason":{"kind":"completed"}}}}} {"method":"session.status","params":{"sessionId":"{{session:1}}","status":"idle"}} diff --git a/snapshots/sdk/ptc-turn/replay.override.json b/snapshots/sdk/ptc-turn/replay.override.json new file mode 100644 index 0000000000..6e19849e93 --- /dev/null +++ b/snapshots/sdk/ptc-turn/replay.override.json @@ -0,0 +1,24 @@ +[ + { + "kind": "chunks", + "chunks": [ + {"type":"block-start","index":0,"blockType":"reasoning"}, + {"type":"block-start","index":1,"blockType":"tool-call"}, + {"type":"block-end","index":0,"block":{"type":"reasoning","text":"The user wants me to write a single run_code program that:\n1. Calls bash tool twice: `echo CODE_ONE` and `echo CODE_TWO`\n2. console.log exactly `captured output`\n3. Return the two outputs joined with a plus sign\n\nLet me write this."}}, + {"type":"block-end","index":1,"block":{"type":"tool-call","id":"call_00_UiQPVqoELyzBZCY5pm1z7875","name":"run_code","arguments":"{\"code\":\"\\nconst out1 = await tools.bash({command: \\\"echo CODE_ONE\\\", description: \\\"Print CODE_ONE\\\"});\\nconst out2 = await tools.bash({command: \\\"echo CODE_TWO\\\", description: \\\"Print CODE_TWO\\\"});\\nHarness.inspect(\\\"captured output\\\");\\nconst text1 = out1.stdout.text.trim();\\nconst text2 = out2.stdout.text.trim();\\nHarness.inspect(text1 + \\\"+\\\" + text2); return;\\n\",\"description\":\"Run two echo commands and join outputs\"}"}}, + {"type":"usage","usage":{"inputTokens":6152,"outputTokens":214,"cacheReadTokens":0,"reasoningTokens":60}}, + {"type":"finish","reason":{"kind":"tool-calls"}} + ] + }, + { + "kind": "chunks", + "chunks": [ + {"type":"block-start","index":0,"blockType":"reasoning"}, + {"type":"block-start","index":1,"blockType":"text"}, + {"type":"block-end","index":0,"block":{"type":"reasoning","text":"The program ran successfully. The console.log output \"captured output\" appeared, and the return value is \"CODE_ONE+CODE_TWO\". The user asked me to reply with that joined string only."}}, + {"type":"block-end","index":1,"block":{"type":"text","text":"CODE_ONE+CODE_TWO"}}, + {"type":"usage","usage":{"inputTokens":117,"outputTokens":50,"cacheReadTokens":6272,"reasoningTokens":42}}, + {"type":"finish","reason":{"kind":"stop"}} + ] + } +] diff --git a/snapshots/sdk/ptc-turn/session.v4.jsonl b/snapshots/sdk/ptc-turn/session.v4.jsonl index 5ba70ecd36..097883006d 100644 --- a/snapshots/sdk/ptc-turn/session.v4.jsonl +++ b/snapshots/sdk/ptc-turn/session.v4.jsonl @@ -13,8 +13,8 @@ {"type":"request/header","data":{"header":{"config":{"provider":"deepseek-official","model":"deepseek-v4-flash"},"tools":"{{tools}}"},"reason":"initial"}} {"type":"request/context","data":{"provider":"deepseek-official","model":"deepseek-v4-flash"}} {"type":"session/title","data":{"title":"Using ONE run_code program: call","messageSeqs":[8],"source":{"kind":"fallback"}}} -{"type":"assistant/message","data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"reasoning","text":"The user wants me to write a single run_code program that:\n1. Calls bash tool twice: `echo CODE_ONE` and `echo CODE_TWO`\n2. console.log exactly `captured output`\n3. Return the two outputs joined with a plus sign\n\nLet me write this."},{"type":"tool-call","id":"call_00_UiQPVqoELyzBZCY5pm1z7875","name":"run_code","arguments":"{\"code\": \"\\nconst out1 = await tools.bash({command: \\\"echo CODE_ONE\\\", description: \\\"Print CODE_ONE\\\"});\\nconst out2 = await tools.bash({command: \\\"echo CODE_TWO\\\", description: \\\"Print CODE_TWO\\\"});\\nconsole.log(\\\"captured output\\\");\\nconst text1 = out1.stdout.text.trim();\\nconst text2 = out2.stdout.text.trim();\\nreturn text1 + \\\"+\\\" + text2;\\n\", \"description\": \"Run two echo commands and join outputs\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:5}}"},"usage":{"inputTokens":6152,"outputTokens":214,"cacheReadTokens":0,"reasoningTokens":60},"stream":[{"type":"chunk","time":1793796117112,"chunk":{"type":"block-start","index":0,"blockType":"reasoning"}},{"type":"reasoning-chunks","time0":1793796117112,"index":0,"dt":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"texts":["The"," user"," wants"," me"," to"," write"," a"," single"," run","_code"," program"," that",":\n","1","."," Calls"," bash"," tool"," twice",":"," `","echo"," CODE","_","ONE","`"," and"," `","echo"," CODE","_T","WO","`\n","2","."," console",".log"," exactly"," `","capt","ured"," output","`\n","3","."," Return"," the"," two"," outputs"," joined"," with"," a"," plus"," sign","\n\n","Let"," me"," write"," this","."]},{"type":"chunk","time":1793796117113,"chunk":{"type":"block-start","index":1,"blockType":"tool-call"}},{"type":"tool-call-chunks","time0":1793796117113,"index":1,"dt":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"id":"call_00_UiQPVqoELyzBZCY5pm1z7875","name":"run_code","args":["","{","\"","code","\"",": ","\"","\\n","const"," out","1"," ="," await"," tools",".b","ash","({","command",":"," \\\"","echo"," CODE","_","ONE","\\\","," description",":"," \\\"","Print"," CODE","_","ONE","\\\"","});\\n","const"," out","2"," ="," await"," tools",".b","ash","({","command",":"," \\\"","echo"," CODE","_T","WO","\\\","," description",":"," \\\"","Print"," CODE","_T","WO","\\\"","});\\n","console",".log","(\\\"","capt","ured"," output","\\\");\\n","const"," text","1"," ="," out","1",".stdout",".text",".trim","();\\n","const"," text","2"," ="," out","2",".stdout",".text",".trim","();\\n","return"," text","1"," +"," \\\"+","\\\""," +"," text","2",";\\n","\"",", ","\"","description","\"",": ","\"","Run"," two"," echo"," commands"," and"," join"," outputs","\"","}"]},{"type":"chunk","time":1793796117113,"chunk":{"type":"block-end","index":0,"block":{"type":"reasoning","text":"The user wants me to write a single run_code program that:\n1. Calls bash tool twice: `echo CODE_ONE` and `echo CODE_TWO`\n2. console.log exactly `captured output`\n3. Return the two outputs joined with a plus sign\n\nLet me write this."}}},{"type":"chunk","time":1793796117113,"chunk":{"type":"block-end","index":1,"block":{"type":"tool-call","id":"call_00_UiQPVqoELyzBZCY5pm1z7875","name":"run_code","arguments":"{\"code\": \"\\nconst out1 = await tools.bash({command: \\\"echo CODE_ONE\\\", description: \\\"Print CODE_ONE\\\"});\\nconst out2 = await tools.bash({command: \\\"echo CODE_TWO\\\", description: \\\"Print CODE_TWO\\\"});\\nconsole.log(\\\"captured output\\\");\\nconst text1 = out1.stdout.text.trim();\\nconst text2 = out2.stdout.text.trim();\\nreturn text1 + \\\"+\\\" + text2;\\n\", \"description\": \"Run two echo commands and join outputs\"}"}}},{"type":"chunk","time":1793796117113,"chunk":{"type":"usage","usage":{"inputTokens":6152,"outputTokens":214,"cacheReadTokens":0,"reasoningTokens":60}}},{"type":"chunk","time":1793796117113,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"} -{"type":"tool/call","data":{"turn":1,"step":1,"callId":"call_00_UiQPVqoELyzBZCY5pm1z7875","name":"run_code","arguments":"{\"code\": \"\\nconst out1 = await tools.bash({command: \\\"echo CODE_ONE\\\", description: \\\"Print CODE_ONE\\\"});\\nconst out2 = await tools.bash({command: \\\"echo CODE_TWO\\\", description: \\\"Print CODE_TWO\\\"});\\nconsole.log(\\\"captured output\\\");\\nconst text1 = out1.stdout.text.trim();\\nconst text2 = out2.stdout.text.trim();\\nreturn text1 + \\\"+\\\" + text2;\\n\", \"description\": \"Run two echo commands and join outputs\"}"}} +{"type":"assistant/message","data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"reasoning","text":"The user wants me to write a single run_code program that:\n1. Calls bash tool twice: `echo CODE_ONE` and `echo CODE_TWO`\n2. console.log exactly `captured output`\n3. Return the two outputs joined with a plus sign\n\nLet me write this."},{"type":"tool-call","id":"call_00_UiQPVqoELyzBZCY5pm1z7875","name":"run_code","arguments":"{\"code\":\"\\nconst out1 = await tools.bash({command: \\\"echo CODE_ONE\\\", description: \\\"Print CODE_ONE\\\"});\\nconst out2 = await tools.bash({command: \\\"echo CODE_TWO\\\", description: \\\"Print CODE_TWO\\\"});\\nHarness.inspect(\\\"captured output\\\");\\nconst text1 = out1.stdout.text.trim();\\nconst text2 = out2.stdout.text.trim();\\nHarness.inspect(text1 + \\\"+\\\" + text2); return;\\n\",\"description\":\"Run two echo commands and join outputs\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:5}}"},"usage":{"inputTokens":6152,"outputTokens":214,"cacheReadTokens":0,"reasoningTokens":60},"stream":[{"type":"chunk","time":1794195953521,"chunk":{"type":"block-start","index":0,"blockType":"reasoning"}},{"type":"chunk","time":1794195953521,"chunk":{"type":"block-start","index":1,"blockType":"tool-call"}},{"type":"chunk","time":1794195953521,"chunk":{"type":"block-end","index":0,"block":{"type":"reasoning","text":"The user wants me to write a single run_code program that:\n1. Calls bash tool twice: `echo CODE_ONE` and `echo CODE_TWO`\n2. console.log exactly `captured output`\n3. Return the two outputs joined with a plus sign\n\nLet me write this."}}},{"type":"chunk","time":1794195953521,"chunk":{"type":"block-end","index":1,"block":{"type":"tool-call","id":"call_00_UiQPVqoELyzBZCY5pm1z7875","name":"run_code","arguments":"{\"code\":\"\\nconst out1 = await tools.bash({command: \\\"echo CODE_ONE\\\", description: \\\"Print CODE_ONE\\\"});\\nconst out2 = await tools.bash({command: \\\"echo CODE_TWO\\\", description: \\\"Print CODE_TWO\\\"});\\nHarness.inspect(\\\"captured output\\\");\\nconst text1 = out1.stdout.text.trim();\\nconst text2 = out2.stdout.text.trim();\\nHarness.inspect(text1 + \\\"+\\\" + text2); return;\\n\",\"description\":\"Run two echo commands and join outputs\"}"}}},{"type":"chunk","time":1794195953521,"chunk":{"type":"usage","usage":{"inputTokens":6152,"outputTokens":214,"cacheReadTokens":0,"reasoningTokens":60}}},{"type":"chunk","time":1794195953521,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"} +{"type":"tool/call","data":{"turn":1,"step":1,"callId":"call_00_UiQPVqoELyzBZCY5pm1z7875","name":"run_code","arguments":"{\"code\":\"\\nconst out1 = await tools.bash({command: \\\"echo CODE_ONE\\\", description: \\\"Print CODE_ONE\\\"});\\nconst out2 = await tools.bash({command: \\\"echo CODE_TWO\\\", description: \\\"Print CODE_TWO\\\"});\\nHarness.inspect(\\\"captured output\\\");\\nconst text1 = out1.stdout.text.trim();\\nconst text2 = out2.stdout.text.trim();\\nHarness.inspect(text1 + \\\"+\\\" + text2); return;\\n\",\"description\":\"Run two echo commands and join outputs\"}"}} {"type":"tool/ptc-dispatch-start","data":{"rootCallId":"call_00_UiQPVqoELyzBZCY5pm1z7875","parentCallId":"call_00_UiQPVqoELyzBZCY5pm1z7875","subCallId":"call_00_UiQPVqoELyzBZCY5pm1z7875:ptc:1","name":"bash","arguments":{"command":"echo CODE_ONE","description":"Print CODE_ONE"}}} {"type":"job/registered","data":{"jobId":"{{id:1}}","owner":"{{session:1}}","kind":"bash","label":"echo CODE_ONE","startedAt":1793796117164,"toolCall":{"callId":"call_00_UiQPVqoELyzBZCY5pm1z7875:ptc:1","rootCallId":"call_00_UiQPVqoELyzBZCY5pm1z7875"}}} {"type":"job/settled","data":{"jobId":"{{id:1}}","owner":"{{session:1}}","kind":"bash","label":"echo CODE_ONE","startedAt":1793796117164,"toolCall":{"callId":"call_00_UiQPVqoELyzBZCY5pm1z7875:ptc:1","rootCallId":"call_00_UiQPVqoELyzBZCY5pm1z7875"},"finishedAt":1793796117166,"status":"completed","cause":"producer","producerReported":true,"detail":"exit code: 0"}} @@ -26,6 +26,6 @@ {"type":"tool/result","data":{"turn":1,"step":1,"message":{"role":"tool","source":{"kind":"tool","callId":"call_00_UiQPVqoELyzBZCY5pm1z7875"},"toolCallId":"call_00_UiQPVqoELyzBZCY5pm1z7875","content":[{"type":"text","text":"captured output\nCODE_ONE+CODE_TWO"}],"isError":false,"id":"{{message:6}}"}},"sourceEventSeqs":[15],"surfaceOp":"append"} {"type":"step/end","data":{"turn":1,"step":1}} {"type":"step/start","data":{"turn":1,"step":2}} -{"type":"assistant/message","data":{"turn":1,"step":2,"message":{"role":"assistant","content":[{"type":"reasoning","text":"The program ran successfully. The console.log output \"captured output\" appeared, and the return value is \"CODE_ONE+CODE_TWO\". The user asked me to reply with that joined string only."},{"type":"text","text":"CODE_ONE+CODE_TWO"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:7}}"},"usage":{"inputTokens":117,"outputTokens":50,"cacheReadTokens":6272,"reasoningTokens":42},"stream":[{"type":"chunk","time":1793796117200,"chunk":{"type":"block-start","index":0,"blockType":"reasoning"}},{"type":"reasoning-chunks","time0":1793796117200,"index":0,"dt":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"texts":["The"," program"," ran"," successfully","."," The"," console",".log"," output"," \"","capt","ured"," output","\""," appeared",","," and"," the"," return"," value"," is"," \"","CODE","_","ONE","+","CODE","_T","WO","\"."," The"," user"," asked"," me"," to"," reply"," with"," that"," joined"," string"," only","."]},{"type":"chunk","time":1793796117200,"chunk":{"type":"block-start","index":1,"blockType":"text"}},{"type":"text-chunks","time0":1793796117200,"index":1,"dt":[0,0,0,0,0,0],"texts":["CODE","_","ONE","+","CODE","_T","WO"]},{"type":"chunk","time":1793796117200,"chunk":{"type":"block-end","index":0,"block":{"type":"reasoning","text":"The program ran successfully. The console.log output \"captured output\" appeared, and the return value is \"CODE_ONE+CODE_TWO\". The user asked me to reply with that joined string only."}}},{"type":"chunk","time":1793796117200,"chunk":{"type":"block-end","index":1,"block":{"type":"text","text":"CODE_ONE+CODE_TWO"}}},{"type":"chunk","time":1793796117200,"chunk":{"type":"usage","usage":{"inputTokens":117,"outputTokens":50,"cacheReadTokens":6272,"reasoningTokens":42}}},{"type":"chunk","time":1793796117200,"chunk":{"type":"finish","reason":{"kind":"stop"}}}]},"surfaceOp":"append"} +{"type":"assistant/message","data":{"turn":1,"step":2,"message":{"role":"assistant","content":[{"type":"reasoning","text":"The program ran successfully. The console.log output \"captured output\" appeared, and the return value is \"CODE_ONE+CODE_TWO\". The user asked me to reply with that joined string only."},{"type":"text","text":"CODE_ONE+CODE_TWO"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:7}}"},"usage":{"inputTokens":117,"outputTokens":50,"cacheReadTokens":6272,"reasoningTokens":42},"stream":[{"type":"chunk","time":1794195953778,"chunk":{"type":"block-start","index":0,"blockType":"reasoning"}},{"type":"chunk","time":1794195953778,"chunk":{"type":"block-start","index":1,"blockType":"text"}},{"type":"chunk","time":1794195953778,"chunk":{"type":"block-end","index":0,"block":{"type":"reasoning","text":"The program ran successfully. The console.log output \"captured output\" appeared, and the return value is \"CODE_ONE+CODE_TWO\". The user asked me to reply with that joined string only."}}},{"type":"chunk","time":1794195953778,"chunk":{"type":"block-end","index":1,"block":{"type":"text","text":"CODE_ONE+CODE_TWO"}}},{"type":"chunk","time":1794195953778,"chunk":{"type":"usage","usage":{"inputTokens":117,"outputTokens":50,"cacheReadTokens":6272,"reasoningTokens":42}}},{"type":"chunk","time":1794195953778,"chunk":{"type":"finish","reason":{"kind":"stop"}}}]},"surfaceOp":"append"} {"type":"step/end","data":{"turn":1,"step":2}} {"type":"turn/end","data":{"turn":1,"reason":{"kind":"completed"}}} diff --git a/snapshots/sdk/ptc-turn/snapshot.yml b/snapshots/sdk/ptc-turn/snapshot.yml index e5a31c881e..e5239beba4 100644 --- a/snapshots/sdk/ptc-turn/snapshot.yml +++ b/snapshots/sdk/ptc-turn/snapshot.yml @@ -5,3 +5,5 @@ composition: ptc recording: live header: class: ptc +replay: + override: true diff --git a/snapshots/sdk/tool-error-details/notifications.expected.jsonl b/snapshots/sdk/tool-error-details/notifications.expected.jsonl index d96af775ab..33022b5ad5 100644 --- a/snapshots/sdk/tool-error-details/notifications.expected.jsonl +++ b/snapshots/sdk/tool-error-details/notifications.expected.jsonl @@ -10,7 +10,7 @@ {"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"request/header","seq":11,"time":0,"data":{"header":{"config":{"provider":"deepseek-official","model":"deepseek-v4-flash"},"tools":["apply_patch","bash","create_goal","edit","get_goal","glob","grep","job_kill","job_list","job_output","list_agents","read","read_image","send_message","skill","subagent","subagent_fork","todo_write","update_goal","wait_agent","web_fetch","web_search","workflow","working_directory","write"]},"reason":"initial"}}}} {"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"request/context","seq":12,"time":0,"data":{"provider":"deepseek-official","model":"deepseek-v4-flash"}}}} {"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"session/title","seq":13,"time":0,"data":{"title":"Exercise a native denial and","messageSeqs":[8],"source":{"kind":"fallback"}}}}} -{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"assistant/message","seq":14,"time":0,"data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"tool-call","id":"denied-native","name":"bash","arguments":"{\"command\":\"echo AUTO_DENIED\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{sessionId}}"},"stream":[{"type":"chunk","time":0,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}},{"type":"tool-call-chunks","time0":0,"index":0,"dt":[],"id":"denied-native","name":"bash","args":["{\"command\":\"echo AUTO_DENIED\"}"]},{"type":"chunk","time":0,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"denied-native","name":"bash","arguments":"{\"command\":\"echo AUTO_DENIED\"}"}}},{"type":"chunk","time":0,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"}}} +{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"assistant/message","seq":14,"time":0,"data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"tool-call","id":"denied-native","name":"bash","arguments":"{\"command\":\"echo AUTO_DENIED\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{sessionId}}"},"stream":[{"type":"chunk","time":0,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}},{"type":"chunk","time":0,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"denied-native","name":"bash","arguments":"{\"command\":\"echo AUTO_DENIED\"}"}}},{"type":"chunk","time":0,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"}}} {"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"tool/call","seq":15,"time":0,"data":{"turn":1,"step":1,"callId":"denied-native","name":"bash","arguments":"{\"command\":\"echo AUTO_DENIED\"}"}}}} {"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"tool/result","seq":16,"time":0,"data":{"turn":1,"step":1,"message":{"role":"tool","source":{"kind":"tool","callId":"denied-native"},"toolCallId":"denied-native","content":[{"type":"text","text":"Error: Auto review rejected tool \"bash\"; its body was not executed"}],"isError":true,"id":"{{sessionId}}"},"error":{"name":"AutoReviewDeniedError","code":"AUTO_REVIEW_DENIED","reason":" transport raw\r\nreason "}},"sourceEventSeqs":[15],"surfaceOp":"append"}}} {"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"step/end","seq":17,"time":0,"data":{"turn":1,"step":1}}}} @@ -18,14 +18,14 @@ {"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"system/message","seq":19,"time":0,"data":{"turn":1,"step":2,"message":{"role":"system","content":[{"type":"text","text":"{{system}}"}],"source":{"kind":"system-prompt"},"id":"{{sessionId}}"}},"sourceEventSeqs":[7],"surfaceOp":{"op":"replace","startSeq":7,"endSeq":7}}}} {"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"request/header","seq":20,"time":0,"data":{"header":{"config":{"provider":"deepseek-official","model":"deepseek-v4-flash"},"tools":["run_code"]},"reason":"change","startsSeries":true}}}} {"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"developer/message","seq":21,"time":0,"data":{"turn":1,"step":2,"message":{"source":{"kind":"tool-registry"},"content":[{"type":"tool-addition","toolName":"run_code"},{"type":"tool-removal","toolName":"apply_patch"},{"type":"tool-removal","toolName":"bash"},{"type":"tool-removal","toolName":"create_goal"},{"type":"tool-removal","toolName":"edit"},{"type":"tool-removal","toolName":"get_goal"},{"type":"tool-removal","toolName":"glob"},{"type":"tool-removal","toolName":"grep"},{"type":"tool-removal","toolName":"job_kill"},{"type":"tool-removal","toolName":"job_list"},{"type":"tool-removal","toolName":"job_output"},{"type":"tool-removal","toolName":"list_agents"},{"type":"tool-removal","toolName":"read"},{"type":"tool-removal","toolName":"read_image"},{"type":"tool-removal","toolName":"send_message"},{"type":"tool-removal","toolName":"skill"},{"type":"tool-removal","toolName":"subagent"},{"type":"tool-removal","toolName":"subagent_fork"},{"type":"tool-removal","toolName":"todo_write"},{"type":"tool-removal","toolName":"update_goal"},{"type":"tool-removal","toolName":"wait_agent"},{"type":"tool-removal","toolName":"web_fetch"},{"type":"tool-removal","toolName":"web_search"},{"type":"tool-removal","toolName":"workflow"},{"type":"tool-removal","toolName":"working_directory"},{"type":"tool-removal","toolName":"write"}],"role":"developer","id":"{{sessionId}}"},"headerSeq":20},"surfaceOp":"append"}}} -{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"assistant/message","seq":22,"time":0,"data":{"turn":1,"step":2,"message":{"role":"assistant","content":[{"type":"tool-call","id":"denied-code","name":"run_code","arguments":"{\"code\":\"try { await tools.bash({command: \\\"echo AUTO_DENIED\\\"}) } catch (error) { return error.message }\",\"description\":\"Catch one denied inner call\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{sessionId}}"},"stream":[{"type":"chunk","time":0,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}},{"type":"tool-call-chunks","time0":0,"index":0,"dt":[],"id":"denied-code","name":"run_code","args":["{\"code\":\"try { await tools.bash({command: \\\"echo AUTO_DENIED\\\"}) } catch (error) { return error.message }\",\"description\":\"Catch one denied inner call\"}"]},{"type":"chunk","time":0,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"denied-code","name":"run_code","arguments":"{\"code\":\"try { await tools.bash({command: \\\"echo AUTO_DENIED\\\"}) } catch (error) { return error.message }\",\"description\":\"Catch one denied inner call\"}"}}},{"type":"chunk","time":0,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"}}} -{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"tool/call","seq":23,"time":0,"data":{"turn":1,"step":2,"callId":"denied-code","name":"run_code","arguments":"{\"code\":\"try { await tools.bash({command: \\\"echo AUTO_DENIED\\\"}) } catch (error) { return error.message }\",\"description\":\"Catch one denied inner call\"}"}}}} +{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"assistant/message","seq":22,"time":0,"data":{"turn":1,"step":2,"message":{"role":"assistant","content":[{"type":"tool-call","id":"denied-code","name":"run_code","arguments":"{\"code\":\"try { await tools.bash({command: \\\"echo AUTO_DENIED\\\"}) } catch (error) { Harness.inspect(error.message); return; }\",\"description\":\"Catch one denied inner call\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{sessionId}}"},"stream":[{"type":"chunk","time":0,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}},{"type":"chunk","time":0,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"denied-code","name":"run_code","arguments":"{\"code\":\"try { await tools.bash({command: \\\"echo AUTO_DENIED\\\"}) } catch (error) { Harness.inspect(error.message); return; }\",\"description\":\"Catch one denied inner call\"}"}}},{"type":"chunk","time":0,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"}}} +{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"tool/call","seq":23,"time":0,"data":{"turn":1,"step":2,"callId":"denied-code","name":"run_code","arguments":"{\"code\":\"try { await tools.bash({command: \\\"echo AUTO_DENIED\\\"}) } catch (error) { Harness.inspect(error.message); return; }\",\"description\":\"Catch one denied inner call\"}"}}}} {"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"tool/ptc-dispatch-start","seq":24,"time":0,"data":{"rootCallId":"denied-code","parentCallId":"denied-code","subCallId":"denied-code:ptc:1","name":"bash","arguments":{"command":"echo AUTO_DENIED"}}}}} {"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"tool/ptc-dispatch","seq":25,"time":0,"data":{"rootCallId":"denied-code","parentCallId":"denied-code","subCallId":"denied-code:ptc:1","name":"bash","arguments":{"command":"echo AUTO_DENIED"},"isError":true,"error":{"name":"AutoReviewDeniedError","code":"AUTO_REVIEW_DENIED","reason":" transport raw\r\nreason "},"content":[{"type":"text","text":"Error: Auto review rejected tool \"bash\"; its body was not executed"}]}}}} {"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"tool/result","seq":26,"time":0,"data":{"turn":1,"step":2,"message":{"role":"tool","source":{"kind":"tool","callId":"denied-code"},"toolCallId":"denied-code","content":[{"type":"text","text":"Auto review rejected tool \"bash\"; its body was not executed"}],"isError":false,"id":"{{sessionId}}"}},"sourceEventSeqs":[23],"surfaceOp":"append"}}} {"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"step/end","seq":27,"time":0,"data":{"turn":1,"step":2}}}} {"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"step/start","seq":28,"time":0,"data":{"turn":1,"step":3}}}} -{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"assistant/message","seq":29,"time":0,"data":{"turn":1,"step":3,"message":{"role":"assistant","content":[{"type":"text","text":"ERROR_DETAILS_OK"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{sessionId}}"},"stream":[{"type":"chunk","time":0,"chunk":{"type":"block-start","index":0,"blockType":"text"}},{"type":"text-chunks","time0":0,"index":0,"dt":[],"texts":["ERROR_DETAILS_OK"]},{"type":"chunk","time":0,"chunk":{"type":"block-end","index":0,"block":{"type":"text","text":"ERROR_DETAILS_OK"}}},{"type":"chunk","time":0,"chunk":{"type":"finish","reason":{"kind":"stop"}}}]},"surfaceOp":"append"}}} +{"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"assistant/message","seq":29,"time":0,"data":{"turn":1,"step":3,"message":{"role":"assistant","content":[{"type":"text","text":"ERROR_DETAILS_OK"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{sessionId}}"},"stream":[{"type":"chunk","time":0,"chunk":{"type":"block-start","index":0,"blockType":"text"}},{"type":"chunk","time":0,"chunk":{"type":"block-end","index":0,"block":{"type":"text","text":"ERROR_DETAILS_OK"}}},{"type":"chunk","time":0,"chunk":{"type":"finish","reason":{"kind":"stop"}}}]},"surfaceOp":"append"}}} {"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"step/end","seq":30,"time":0,"data":{"turn":1,"step":3}}}} {"method":"session.event","params":{"sessionId":"{{sessionId}}","event":{"type":"turn/end","seq":31,"time":0,"data":{"turn":1,"reason":{"kind":"completed"}}}}} {"method":"session.status","params":{"sessionId":"{{sessionId}}","status":"idle"}} diff --git a/snapshots/sdk/tool-error-details/replay.override.json b/snapshots/sdk/tool-error-details/replay.override.json new file mode 100644 index 0000000000..f397710a49 --- /dev/null +++ b/snapshots/sdk/tool-error-details/replay.override.json @@ -0,0 +1,26 @@ +[ + { + "kind": "chunks", + "chunks": [ + {"type":"block-start","index":0,"blockType":"tool-call"}, + {"type":"block-end","index":0,"block":{"type":"tool-call","id":"denied-native","name":"bash","arguments":"{\"command\":\"echo AUTO_DENIED\"}"}}, + {"type":"finish","reason":{"kind":"tool-calls"}} + ] + }, + { + "kind": "chunks", + "chunks": [ + {"type":"block-start","index":0,"blockType":"tool-call"}, + {"type":"block-end","index":0,"block":{"type":"tool-call","id":"denied-code","name":"run_code","arguments":"{\"code\":\"try { await tools.bash({command: \\\"echo AUTO_DENIED\\\"}) } catch (error) { Harness.inspect(error.message); return; }\",\"description\":\"Catch one denied inner call\"}"}}, + {"type":"finish","reason":{"kind":"tool-calls"}} + ] + }, + { + "kind": "chunks", + "chunks": [ + {"type":"block-start","index":0,"blockType":"text"}, + {"type":"block-end","index":0,"block":{"type":"text","text":"ERROR_DETAILS_OK"}}, + {"type":"finish","reason":{"kind":"stop"}} + ] + } +] diff --git a/snapshots/sdk/tool-error-details/session.v4.jsonl b/snapshots/sdk/tool-error-details/session.v4.jsonl index 0095f09f67..cbe5cbf82a 100644 --- a/snapshots/sdk/tool-error-details/session.v4.jsonl +++ b/snapshots/sdk/tool-error-details/session.v4.jsonl @@ -13,7 +13,7 @@ {"type":"request/header","data":{"header":{"config":{"provider":"deepseek-official","model":"deepseek-v4-flash"},"tools":["apply_patch","bash","create_goal","edit","get_goal","glob","grep","job_kill","job_list","job_output","list_agents","read","read_image","send_message","skill","subagent","subagent_fork","todo_write","update_goal","wait_agent","web_fetch","web_search","workflow","working_directory","write"]},"reason":"initial"}} {"type":"request/context","data":{"provider":"deepseek-official","model":"deepseek-v4-flash"}} {"type":"session/title","data":{"title":"Exercise a native denial and","messageSeqs":[8],"source":{"kind":"fallback"}}} -{"type":"assistant/message","data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"tool-call","id":"denied-native","name":"bash","arguments":"{\"command\":\"echo AUTO_DENIED\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:5}}"},"stream":[{"type":"chunk","time":1793796132469,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}},{"type":"tool-call-chunks","time0":1793796132470,"index":0,"dt":[],"id":"denied-native","name":"bash","args":["{\"command\":\"echo AUTO_DENIED\"}"]},{"type":"chunk","time":1793796132470,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"denied-native","name":"bash","arguments":"{\"command\":\"echo AUTO_DENIED\"}"}}},{"type":"chunk","time":1793796132470,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"} +{"type":"assistant/message","data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"tool-call","id":"denied-native","name":"bash","arguments":"{\"command\":\"echo AUTO_DENIED\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:5}}"},"stream":[{"type":"chunk","time":1794195955152,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}},{"type":"chunk","time":1794195955152,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"denied-native","name":"bash","arguments":"{\"command\":\"echo AUTO_DENIED\"}"}}},{"type":"chunk","time":1794195955152,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"} {"type":"tool/call","data":{"turn":1,"step":1,"callId":"denied-native","name":"bash","arguments":"{\"command\":\"echo AUTO_DENIED\"}"}} {"type":"tool/result","data":{"turn":1,"step":1,"message":{"role":"tool","source":{"kind":"tool","callId":"denied-native"},"toolCallId":"denied-native","content":[{"type":"text","text":"Error: Auto review rejected tool \"bash\"; its body was not executed"}],"isError":true,"id":"{{message:6}}"},"error":{"name":"AutoReviewDeniedError","code":"AUTO_REVIEW_DENIED","reason":" transport raw\r\nreason "}},"sourceEventSeqs":[15],"surfaceOp":"append"} {"type":"step/end","data":{"turn":1,"step":1}} @@ -21,13 +21,13 @@ {"type":"system/message","data":{"turn":1,"step":2,"message":{"role":"system","content":[{"type":"text","text":"{{system}}"}],"source":{"kind":"system-prompt"},"id":"{{message:7}}"}},"sourceEventSeqs":[7],"surfaceOp":{"op":"replace","startSeq":7,"endSeq":7}} {"type":"request/header","data":{"header":{"config":{"provider":"deepseek-official","model":"deepseek-v4-flash"},"tools":["run_code"]},"reason":"change","startsSeries":true}} {"type":"developer/message","data":{"turn":1,"step":2,"message":{"source":{"kind":"tool-registry"},"content":[{"type":"tool-addition","toolName":"run_code"},{"type":"tool-removal","toolName":"apply_patch"},{"type":"tool-removal","toolName":"bash"},{"type":"tool-removal","toolName":"create_goal"},{"type":"tool-removal","toolName":"edit"},{"type":"tool-removal","toolName":"get_goal"},{"type":"tool-removal","toolName":"glob"},{"type":"tool-removal","toolName":"grep"},{"type":"tool-removal","toolName":"job_kill"},{"type":"tool-removal","toolName":"job_list"},{"type":"tool-removal","toolName":"job_output"},{"type":"tool-removal","toolName":"list_agents"},{"type":"tool-removal","toolName":"read"},{"type":"tool-removal","toolName":"read_image"},{"type":"tool-removal","toolName":"send_message"},{"type":"tool-removal","toolName":"skill"},{"type":"tool-removal","toolName":"subagent"},{"type":"tool-removal","toolName":"subagent_fork"},{"type":"tool-removal","toolName":"todo_write"},{"type":"tool-removal","toolName":"update_goal"},{"type":"tool-removal","toolName":"wait_agent"},{"type":"tool-removal","toolName":"web_fetch"},{"type":"tool-removal","toolName":"web_search"},{"type":"tool-removal","toolName":"workflow"},{"type":"tool-removal","toolName":"working_directory"},{"type":"tool-removal","toolName":"write"}],"role":"developer","id":"{{message:8}}"},"headerSeq":20},"surfaceOp":"append"} -{"type":"assistant/message","data":{"turn":1,"step":2,"message":{"role":"assistant","content":[{"type":"tool-call","id":"denied-code","name":"run_code","arguments":"{\"code\":\"try { await tools.bash({command: \\\"echo AUTO_DENIED\\\"}) } catch (error) { return error.message }\",\"description\":\"Catch one denied inner call\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:9}}"},"stream":[{"type":"chunk","time":1793796132486,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}},{"type":"tool-call-chunks","time0":1793796132486,"index":0,"dt":[],"id":"denied-code","name":"run_code","args":["{\"code\":\"try { await tools.bash({command: \\\"echo AUTO_DENIED\\\"}) } catch (error) { return error.message }\",\"description\":\"Catch one denied inner call\"}"]},{"type":"chunk","time":1793796132486,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"denied-code","name":"run_code","arguments":"{\"code\":\"try { await tools.bash({command: \\\"echo AUTO_DENIED\\\"}) } catch (error) { return error.message }\",\"description\":\"Catch one denied inner call\"}"}}},{"type":"chunk","time":1793796132486,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"} -{"type":"tool/call","data":{"turn":1,"step":2,"callId":"denied-code","name":"run_code","arguments":"{\"code\":\"try { await tools.bash({command: \\\"echo AUTO_DENIED\\\"}) } catch (error) { return error.message }\",\"description\":\"Catch one denied inner call\"}"}} +{"type":"assistant/message","data":{"turn":1,"step":2,"message":{"role":"assistant","content":[{"type":"tool-call","id":"denied-code","name":"run_code","arguments":"{\"code\":\"try { await tools.bash({command: \\\"echo AUTO_DENIED\\\"}) } catch (error) { Harness.inspect(error.message); return; }\",\"description\":\"Catch one denied inner call\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:9}}"},"stream":[{"type":"chunk","time":1794195955183,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}},{"type":"chunk","time":1794195955183,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"denied-code","name":"run_code","arguments":"{\"code\":\"try { await tools.bash({command: \\\"echo AUTO_DENIED\\\"}) } catch (error) { Harness.inspect(error.message); return; }\",\"description\":\"Catch one denied inner call\"}"}}},{"type":"chunk","time":1794195955183,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"} +{"type":"tool/call","data":{"turn":1,"step":2,"callId":"denied-code","name":"run_code","arguments":"{\"code\":\"try { await tools.bash({command: \\\"echo AUTO_DENIED\\\"}) } catch (error) { Harness.inspect(error.message); return; }\",\"description\":\"Catch one denied inner call\"}"}} {"type":"tool/ptc-dispatch-start","data":{"rootCallId":"denied-code","parentCallId":"denied-code","subCallId":"denied-code:ptc:1","name":"bash","arguments":{"command":"echo AUTO_DENIED"}}} {"type":"tool/ptc-dispatch","data":{"rootCallId":"denied-code","parentCallId":"denied-code","subCallId":"denied-code:ptc:1","name":"bash","arguments":{"command":"echo AUTO_DENIED"},"isError":true,"error":{"name":"AutoReviewDeniedError","code":"AUTO_REVIEW_DENIED","reason":" transport raw\r\nreason "},"content":[{"type":"text","text":"Error: Auto review rejected tool \"bash\"; its body was not executed"}]}} {"type":"tool/result","data":{"turn":1,"step":2,"message":{"role":"tool","source":{"kind":"tool","callId":"denied-code"},"toolCallId":"denied-code","content":[{"type":"text","text":"Auto review rejected tool \"bash\"; its body was not executed"}],"isError":false,"id":"{{message:10}}"}},"sourceEventSeqs":[23],"surfaceOp":"append"} {"type":"step/end","data":{"turn":1,"step":2}} {"type":"step/start","data":{"turn":1,"step":3}} -{"type":"assistant/message","data":{"turn":1,"step":3,"message":{"role":"assistant","content":[{"type":"text","text":"ERROR_DETAILS_OK"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:11}}"},"stream":[{"type":"chunk","time":1793796132574,"chunk":{"type":"block-start","index":0,"blockType":"text"}},{"type":"text-chunks","time0":1793796132574,"index":0,"dt":[],"texts":["ERROR_DETAILS_OK"]},{"type":"chunk","time":1793796132574,"chunk":{"type":"block-end","index":0,"block":{"type":"text","text":"ERROR_DETAILS_OK"}}},{"type":"chunk","time":1793796132574,"chunk":{"type":"finish","reason":{"kind":"stop"}}}]},"surfaceOp":"append"} +{"type":"assistant/message","data":{"turn":1,"step":3,"message":{"role":"assistant","content":[{"type":"text","text":"ERROR_DETAILS_OK"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:11}}"},"stream":[{"type":"chunk","time":1794195955350,"chunk":{"type":"block-start","index":0,"blockType":"text"}},{"type":"chunk","time":1794195955350,"chunk":{"type":"block-end","index":0,"block":{"type":"text","text":"ERROR_DETAILS_OK"}}},{"type":"chunk","time":1794195955350,"chunk":{"type":"finish","reason":{"kind":"stop"}}}]},"surfaceOp":"append"} {"type":"step/end","data":{"turn":1,"step":3}} {"type":"turn/end","data":{"turn":1,"reason":{"kind":"completed"}}} diff --git a/snapshots/sdk/tool-error-details/snapshot.yml b/snapshots/sdk/tool-error-details/snapshot.yml index c05d82c9f1..96260a4c2b 100644 --- a/snapshots/sdk/tool-error-details/snapshot.yml +++ b/snapshots/sdk/tool-error-details/snapshot.yml @@ -8,3 +8,5 @@ header: pin: true changes: 1 promptChanges: 1 +replay: + override: true diff --git a/snapshots/sdk/tool-error-details/system-prompt.expected.md b/snapshots/sdk/tool-error-details/system-prompt.expected.md index ccfe4750ea..6960f23e6a 100644 --- a/snapshots/sdk/tool-error-details/system-prompt.expected.md +++ b/snapshots/sdk/tool-error-details/system-prompt.expected.md @@ -76,20 +76,24 @@ Bash, formatters, code generators, and scripts can overwrite concurrent edits wi `run_code` takes two required arguments: `code`, the body of an async TypeScript function (erasable syntax only — no `enum` or namespaces; type annotations are advisory, the code runs type-stripped), and `description`, a short summary of what the program does. The declarations below are SDK bindings for this program. A declaration does not make its name a directly callable tool; only names supplied as separate tool schemas may be called directly. When no separate `bash` schema is supplied, invoke a declared `bash` binding inside `run_code`: -`run_code({ code: "return await tools.bash({ description: 'Show current directory', command: 'pwd' })", description: "Show current directory" })` +`run_code({ code: "Harness.inspect(await tools.bash({ description: 'Show current directory', command: 'pwd' }))", description: "Show current directory" })` Inside the program: - Call tools as `await tools.name(args)` — quoted access for exotic names: `tools["my-tool"](args)`. Every call resolves to the tool's typed canonical JSON value. Tool arguments must be lossless JSON. - A FAILED tool call rejects with `ToolCallError`, whose `toolName` identifies the failed tool and whose `message` is human-readable — `try/catch` it to handle and continue. - Independent read-only calls MAY overlap under `Promise.all` (safe calls run concurrently; mutating calls run alone, in submission order). Sequence dependent work with `await`. -- Emit results with `return` and/or `console.log(...)`. Only what you print or return is program output. A successful tool result containing an image is attached after the run so you can inspect it on the next step; every other intermediate result stays out of the conversation, so extract just what you need. +- Show selected results with `Harness.inspect(...values)`. Return values and final expressions are not displayed. A successful tool result containing an image is attached after the run so you can inspect it on the next step; every other intermediate result stays out of the conversation, so extract just what you need. Program-only SDK bindings: ```ts type JsonValue = null | boolean | number | string | JsonValue[] | { [key: string]: JsonValue } +declare namespace Harness { + function inspect(...values: unknown[]): void; +} + interface ToolArgsMap { /** Create, update, delete, or move UTF-8 text files with one patch; prefer it over multiple edit calls when a change spans several files or separated regions. */ apply_patch: { diff --git a/snapshots/sdk/tool-error-details/tool-schemas.expected.json b/snapshots/sdk/tool-error-details/tool-schemas.expected.json index bf3d9439fe..954b204296 100644 --- a/snapshots/sdk/tool-error-details/tool-schemas.expected.json +++ b/snapshots/sdk/tool-error-details/tool-schemas.expected.json @@ -643,7 +643,7 @@ [ { "name": "run_code", - "description": "Execute a TypeScript program against the available tools. Takes two required arguments: `code`, the BODY of an async function (erasable syntax only; top-level `await` and `return` work), and `description`, a short summary of what the program does. Call tools as `await tools.name(args)` per the declarations in the system prompt. Only what you print or return is program output — curate it. Image-bearing subtool results are attached after the run. The program is the body of an async TypeScript function; only erasable syntax is accepted, with no enum or namespaces. Type annotations are advisory. Top-level await and return are supported. Emit output with console.log(...) or return a lossless JSON value. Only printed values and the returned value become program output. Each call runs in a fresh Node process. Node APIs are available through await import(...). Relative paths use the supplied working directory; process.env starts empty. Direct filesystem access follows this execution's sandbox policy. Each program starts in the Session's current directory. Running programs keep their initial directory. A sandbox escalation approves this complete program for one execution only. Nested tools retain their own policies and approvals. Request wider access only after evidence of a denial. Earlier effects may already have completed: inspect them before explicitly retrying. Programs are never replayed automatically.", + "description": "Execute a TypeScript program against the available tools. Takes two required arguments: `code`, the BODY of an async function (erasable syntax only; top-level `await` and `return` work), and `description`, a short summary of what the program does. Call tools as `await tools.name(args)` per the declarations in the system prompt. Show selected results with Harness.inspect(...values). Return values are not displayed. Image-bearing subtool results are attached after the run. The program is the body of an async TypeScript function; only erasable syntax is accepted, with no enum or namespaces. Type annotations are advisory. Top-level await and return are supported. Console is unavailable. Use Harness.inspect(...values) for output. Each call runs in a fresh Node process. Node APIs are available through await import(...). Relative paths use the supplied working directory; process.env starts empty. Direct filesystem access follows this execution's sandbox policy. Each program starts in the Session's current directory. Running programs keep their initial directory. A sandbox escalation approves this complete program for one execution only. Nested tools retain their own policies and approvals. Request wider access only after evidence of a denial. Earlier effects may already have completed: inspect them before explicitly retrying. Programs are never replayed automatically.", "parameters": { "type": "object", "properties": { diff --git a/snapshots/session/advanced-toolchain/replay.override.json b/snapshots/session/advanced-toolchain/replay.override.json new file mode 100644 index 0000000000..0c6203e3a7 --- /dev/null +++ b/snapshots/session/advanced-toolchain/replay.override.json @@ -0,0 +1,56 @@ +[ + { + "kind": "chunks", + "chunks": [ + {"type":"block-start","index":0,"blockType":"tool-call"}, + {"type":"block-end","index":0,"block":{"type":"tool-call","id":"advanced-inspect-before","name":"cordis_inspect_list","arguments":"{}"}}, + {"type":"usage","usage":{"inputTokens":3,"outputTokens":3}}, + {"type":"finish","reason":{"kind":"tool-calls"}} + ] + }, + { + "kind": "chunks", + "chunks": [ + {"type":"block-start","index":0,"blockType":"tool-call"}, + {"type":"block-end","index":0,"block":{"type":"tool-call","id":"advanced-code","name":"run_code","arguments":"{\"code\":\"const inspected = await tools.cordis_inspect_list({}); Harness.inspect(JSON.stringify(inspected.providers.map(provider => provider.id), null, 2)); return;\",\"description\":\"Discover runtime APIs through PTC\"}"}}, + {"type":"usage","usage":{"inputTokens":3,"outputTokens":3}}, + {"type":"finish","reason":{"kind":"tool-calls"}} + ] + }, + { + "kind": "chunks", + "chunks": [ + {"type":"block-start","index":0,"blockType":"tool-call"}, + {"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.\",\"run_in_background\":false}"}}, + {"type":"usage","usage":{"inputTokens":3,"outputTokens":3}}, + {"type":"finish","reason":{"kind":"tool-calls"}} + ] + }, + { + "kind": "chunks", + "chunks": [ + {"type":"block-start","index":0,"blockType":"tool-call"}, + {"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-headless-snapshot\",\"description\":\"exercise one workflow child through the headless agent\"}}"}}, + {"type":"usage","usage":{"inputTokens":3,"outputTokens":3}}, + {"type":"finish","reason":{"kind":"tool-calls"}} + ] + }, + { + "kind": "chunks", + "chunks": [ + {"type":"block-start","index":0,"blockType":"tool-call"}, + {"type":"block-end","index":0,"block":{"type":"tool-call","id":"advanced-inspect-after","name":"cordis_inspect_list","arguments":"{}"}}, + {"type":"usage","usage":{"inputTokens":3,"outputTokens":3}}, + {"type":"finish","reason":{"kind":"tool-calls"}} + ] + }, + { + "kind": "chunks", + "chunks": [ + {"type":"block-start","index":0,"blockType":"text"}, + {"type":"block-end","index":0,"block":{"type":"text","text":"ADVANCED_HEADLESS_OK"}}, + {"type":"usage","usage":{"inputTokens":3,"outputTokens":3}}, + {"type":"finish","reason":{"kind":"stop"}} + ] + } +] diff --git a/snapshots/session/advanced-toolchain/session.v4.jsonl b/snapshots/session/advanced-toolchain/session.v4.jsonl index c1a09eb618..b22bb4cc18 100644 --- a/snapshots/session/advanced-toolchain/session.v4.jsonl +++ b/snapshots/session/advanced-toolchain/session.v4.jsonl @@ -12,7 +12,7 @@ {"type":"request/header","data":{"header":{"config":{"provider":"deepseek-official","model":"deepseek-v4-flash"},"tools":["apply_patch","bash","cordis_inspect_list","cordis_inspect_query","create_goal","edit","get_goal","glob","grep","job_kill","job_list","job_output","list_agents","read","read_image","send_message","skill","subagent","subagent_fork","todo_write","update_goal","wait_agent","web_fetch","web_search","workflow","working_directory","write"]},"reason":"initial"}} {"type":"request/context","data":{"provider":"deepseek-official","model":"deepseek-v4-flash"}} {"type":"session/title","data":{"title":"Run this advanced flow exactly","messageSeqs":[8],"source":{"kind":"fallback"}}} -{"type":"assistant/message","data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"tool-call","id":"advanced-inspect-before","name":"cordis_inspect_list","arguments":"{}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:4}}"},"usage":{"inputTokens":3,"outputTokens":3},"stream":[{"type":"chunk","time":1791315715021,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}},{"type":"tool-call-chunks","time0":1791315715022,"index":0,"dt":[],"id":"advanced-inspect-before","name":"cordis_inspect_list","args":["{}"]},{"type":"chunk","time":1791315715022,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"advanced-inspect-before","name":"cordis_inspect_list","arguments":"{}"}}},{"type":"chunk","time":1791315715022,"chunk":{"type":"usage","usage":{"inputTokens":3,"outputTokens":3}}},{"type":"chunk","time":1791315715022,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"} +{"type":"assistant/message","data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"tool-call","id":"advanced-inspect-before","name":"cordis_inspect_list","arguments":"{}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:4}}"},"usage":{"inputTokens":3,"outputTokens":3},"stream":[{"type":"chunk","time":1794196083589,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}},{"type":"chunk","time":1794196083589,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"advanced-inspect-before","name":"cordis_inspect_list","arguments":"{}"}}},{"type":"chunk","time":1794196083589,"chunk":{"type":"usage","usage":{"inputTokens":3,"outputTokens":3}}},{"type":"chunk","time":1794196083589,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"} {"type":"tool/call","data":{"turn":1,"step":1,"callId":"advanced-inspect-before","name":"cordis_inspect_list","arguments":"{}"}} {"type":"tool/result","data":{"turn":1,"step":1,"message":{"role":"tool","source":{"kind":"tool","callId":"advanced-inspect-before"},"toolCallId":"advanced-inspect-before","content":[{"type":"text","text":"{\n \"providers\": [\n {\n \"platform\": \"host\",\n \"id\": \"Service\",\n \"description\": \"Progressive Host Service discovery: compact capability/signature directory, then one exact coding contract.\",\n \"methods\": [\n {\n \"name\": \"listService\",\n \"description\": \"Progressive Host Service discovery: compact capability/signature directory, then one exact coding contract.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"service\": {\n \"type\": \"string\",\n \"description\": \"Exact Service key. Omit it for the compact Service and method-signature directory.\"\n }\n },\n \"additionalProperties\": false\n },\n \"outputSchema\": {\n \"description\": \"Compact Service directory, or one exact Service contract with only its referenced type declarations.\"\n }\n }\n ]\n },\n {\n \"platform\": \"host\",\n \"id\": \"Event\",\n \"description\": \"Progressive Host Event discovery: compact listener directory, then one exact event contract.\",\n \"methods\": [\n {\n \"name\": \"listEvents\",\n \"description\": \"Progressive Host Event discovery: compact listener directory, then one exact event contract.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"event\": {\n \"type\": \"string\",\n \"description\": \"Exact Event name. Omit it for the compact Event and listener-signature directory.\"\n }\n },\n \"additionalProperties\": false\n },\n \"outputSchema\": {\n \"description\": \"Compact Event directory, or one exact Event contract with only its referenced type declarations.\"\n }\n }\n ]\n },\n {\n \"platform\": \"host\",\n \"id\": \"Config\",\n \"description\": \"Progressive live plugin Config discovery: paged entry directory with schema status, then one entry's exact JSON Schema.\",\n \"methods\": [\n {\n \"name\": \"listConfigs\",\n \"description\": \"Progressive live plugin Config discovery: paged entry directory with schema status, then one entry's exact JSON Schema.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"entry\": {\n \"type\": \"string\",\n \"description\": \"Exact Loader entry id from the directory; returns that entry's projected Config schema.\"\n },\n \"name\": {\n \"type\": \"string\",\n \"description\": \"Exact plugin package name; limits the directory to its entries.\"\n },\n \"offset\": {\n \"type\": \"number\",\n \"description\": \"Zero-based directory offset; defaults to 0.\"\n },\n \"limit\": {\n \"type\": \"number\",\n \"description\": \"Directory page size, from 1 to 100; defaults to 25.\"\n }\n },\n \"additionalProperties\": false\n },\n \"outputSchema\": {\n \"description\": \"One directory page of live entries with patch ids, Config status, total, and nextOffset, or one entry's projected JSON Schema with shared definitions, omission acceptance, and projection limitations.\"\n }\n }\n ]\n },\n {\n \"platform\": \"host\",\n \"id\": \"Tool\",\n \"description\": \"Tools visible to the requesting Agent, including scoped and dynamic registrations.\",\n \"methods\": [\n {\n \"name\": \"listTools\",\n \"description\": \"Return every Tool schema currently callable by this Agent.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {},\n \"additionalProperties\": false\n },\n \"outputSchema\": {\n \"description\": \"JSON data owned by this inspect provider.\"\n }\n }\n ]\n }\n ]\n}"}],"isError":false,"id":"{{message:5}}"}},"sourceEventSeqs":[14],"surfaceOp":"append"} {"type":"step/end","data":{"turn":1,"step":1}} @@ -20,8 +20,8 @@ {"type":"system/message","data":{"turn":1,"step":2,"message":{"role":"system","content":[{"type":"text","text":"{{system}}"}],"source":{"kind":"system-prompt"},"id":"{{message:6}}"}},"sourceEventSeqs":[7],"surfaceOp":{"op":"replace","startSeq":7,"endSeq":7}} {"type":"request/header","data":{"header":{"config":{"provider":"deepseek-official","model":"deepseek-v4-flash"},"tools":["run_code"]},"reason":"change","startsSeries":true}} {"type":"developer/message","data":{"turn":1,"step":2,"message":{"source":{"kind":"tool-registry"},"content":[{"type":"tool-addition","toolName":"run_code"},{"type":"tool-removal","toolName":"apply_patch"},{"type":"tool-removal","toolName":"bash"},{"type":"tool-removal","toolName":"cordis_inspect_list"},{"type":"tool-removal","toolName":"cordis_inspect_query"},{"type":"tool-removal","toolName":"create_goal"},{"type":"tool-removal","toolName":"edit"},{"type":"tool-removal","toolName":"get_goal"},{"type":"tool-removal","toolName":"glob"},{"type":"tool-removal","toolName":"grep"},{"type":"tool-removal","toolName":"job_kill"},{"type":"tool-removal","toolName":"job_list"},{"type":"tool-removal","toolName":"job_output"},{"type":"tool-removal","toolName":"list_agents"},{"type":"tool-removal","toolName":"read"},{"type":"tool-removal","toolName":"read_image"},{"type":"tool-removal","toolName":"send_message"},{"type":"tool-removal","toolName":"skill"},{"type":"tool-removal","toolName":"subagent"},{"type":"tool-removal","toolName":"subagent_fork"},{"type":"tool-removal","toolName":"todo_write"},{"type":"tool-removal","toolName":"update_goal"},{"type":"tool-removal","toolName":"wait_agent"},{"type":"tool-removal","toolName":"web_fetch"},{"type":"tool-removal","toolName":"web_search"},{"type":"tool-removal","toolName":"workflow"},{"type":"tool-removal","toolName":"working_directory"},{"type":"tool-removal","toolName":"write"}],"role":"developer","id":"{{message:7}}"},"headerSeq":19},"surfaceOp":"append"} -{"type":"assistant/message","data":{"turn":1,"step":2,"message":{"role":"assistant","content":[{"type":"tool-call","id":"advanced-code","name":"run_code","arguments":"{\"code\":\"const inspected = await tools.cordis_inspect_list({}); return inspected.providers.map(provider => provider.id);\",\"description\":\"Discover runtime APIs through PTC\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:8}}"},"usage":{"inputTokens":3,"outputTokens":3},"stream":[{"type":"chunk","time":1791315715055,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}},{"type":"tool-call-chunks","time0":1791315715055,"index":0,"dt":[],"id":"advanced-code","name":"run_code","args":["{\"code\":\"const inspected = await tools.cordis_inspect_list({}); return inspected.providers.map(provider => provider.id);\",\"description\":\"Discover runtime APIs through PTC\"}"]},{"type":"chunk","time":1791315715055,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"advanced-code","name":"run_code","arguments":"{\"code\":\"const inspected = await tools.cordis_inspect_list({}); return inspected.providers.map(provider => provider.id);\",\"description\":\"Discover runtime APIs through PTC\"}"}}},{"type":"chunk","time":1791315715055,"chunk":{"type":"usage","usage":{"inputTokens":3,"outputTokens":3}}},{"type":"chunk","time":1791315715055,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"} -{"type":"tool/call","data":{"turn":1,"step":2,"callId":"advanced-code","name":"run_code","arguments":"{\"code\":\"const inspected = await tools.cordis_inspect_list({}); return inspected.providers.map(provider => provider.id);\",\"description\":\"Discover runtime APIs through PTC\"}"}} +{"type":"assistant/message","data":{"turn":1,"step":2,"message":{"role":"assistant","content":[{"type":"tool-call","id":"advanced-code","name":"run_code","arguments":"{\"code\":\"const inspected = await tools.cordis_inspect_list({}); Harness.inspect(JSON.stringify(inspected.providers.map(provider => provider.id), null, 2)); return;\",\"description\":\"Discover runtime APIs through PTC\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:8}}"},"usage":{"inputTokens":3,"outputTokens":3},"stream":[{"type":"chunk","time":1794196083630,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}},{"type":"chunk","time":1794196083630,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"advanced-code","name":"run_code","arguments":"{\"code\":\"const inspected = await tools.cordis_inspect_list({}); Harness.inspect(JSON.stringify(inspected.providers.map(provider => provider.id), null, 2)); return;\",\"description\":\"Discover runtime APIs through PTC\"}"}}},{"type":"chunk","time":1794196083630,"chunk":{"type":"usage","usage":{"inputTokens":3,"outputTokens":3}}},{"type":"chunk","time":1794196083630,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"} +{"type":"tool/call","data":{"turn":1,"step":2,"callId":"advanced-code","name":"run_code","arguments":"{\"code\":\"const inspected = await tools.cordis_inspect_list({}); Harness.inspect(JSON.stringify(inspected.providers.map(provider => provider.id), null, 2)); return;\",\"description\":\"Discover runtime APIs through PTC\"}"}} {"type":"tool/ptc-dispatch-start","data":{"rootCallId":"advanced-code","parentCallId":"advanced-code","subCallId":"advanced-code:ptc:1","name":"cordis_inspect_list","arguments":{}}} {"type":"tool/ptc-dispatch","data":{"rootCallId":"advanced-code","parentCallId":"advanced-code","subCallId":"advanced-code:ptc:1","name":"cordis_inspect_list","arguments":{},"isError":false,"content":[{"type":"text","text":"{\n \"providers\": [\n {\n \"platform\": \"host\",\n \"id\": \"Service\",\n \"description\": \"Progressive Host Service discovery: compact capability/signature directory, then one exact coding contract.\",\n \"methods\": [\n {\n \"name\": \"listService\",\n \"description\": \"Progressive Host Service discovery: compact capability/signature directory, then one exact coding contract.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"service\": {\n \"type\": \"string\",\n \"description\": \"Exact Service key. Omit it for the compact Service and method-signature directory.\"\n }\n },\n \"additionalProperties\": false\n },\n \"outputSchema\": {\n \"description\": \"Compact Service directory, or one exact Service contract with only its referenced type declarations.\"\n }\n }\n ]\n },\n {\n \"platform\": \"host\",\n \"id\": \"Event\",\n \"description\": \"Progressive Host Event discovery: compact listener directory, then one exact event contract.\",\n \"methods\": [\n {\n \"name\": \"listEvents\",\n \"description\": \"Progressive Host Event discovery: compact listener directory, then one exact event contract.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"event\": {\n \"type\": \"string\",\n \"description\": \"Exact Event name. Omit it for the compact Event and listener-signature directory.\"\n }\n },\n \"additionalProperties\": false\n },\n \"outputSchema\": {\n \"description\": \"Compact Event directory, or one exact Event contract with only its referenced type declarations.\"\n }\n }\n ]\n },\n {\n \"platform\": \"host\",\n \"id\": \"Config\",\n \"description\": \"Progressive live plugin Config discovery: paged entry directory with schema status, then one entry's exact JSON Schema.\",\n \"methods\": [\n {\n \"name\": \"listConfigs\",\n \"description\": \"Progressive live plugin Config discovery: paged entry directory with schema status, then one entry's exact JSON Schema.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"entry\": {\n \"type\": \"string\",\n \"description\": \"Exact Loader entry id from the directory; returns that entry's projected Config schema.\"\n },\n \"name\": {\n \"type\": \"string\",\n \"description\": \"Exact plugin package name; limits the directory to its entries.\"\n },\n \"offset\": {\n \"type\": \"number\",\n \"description\": \"Zero-based directory offset; defaults to 0.\"\n },\n \"limit\": {\n \"type\": \"number\",\n \"description\": \"Directory page size, from 1 to 100; defaults to 25.\"\n }\n },\n \"additionalProperties\": false\n },\n \"outputSchema\": {\n \"description\": \"One directory page of live entries with patch ids, Config status, total, and nextOffset, or one entry's projected JSON Schema with shared definitions, omission acceptance, and projection limitations.\"\n }\n }\n ]\n },\n {\n \"platform\": \"host\",\n \"id\": \"Tool\",\n \"description\": \"Tools visible to the requesting Agent, including scoped and dynamic registrations.\",\n \"methods\": [\n {\n \"name\": \"listTools\",\n \"description\": \"Return every Tool schema currently callable by this Agent.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {},\n \"additionalProperties\": false\n },\n \"outputSchema\": {\n \"description\": \"JSON data owned by this inspect provider.\"\n }\n }\n ]\n }\n ]\n}"}]}} {"type":"tool/result","data":{"turn":1,"step":2,"message":{"role":"tool","source":{"kind":"tool","callId":"advanced-code"},"toolCallId":"advanced-code","content":[{"type":"text","text":"[\n \"Service\",\n \"Event\",\n \"Config\",\n \"Tool\"\n]"}],"isError":false,"id":"{{message:9}}"}},"sourceEventSeqs":[22],"surfaceOp":"append"} @@ -30,7 +30,7 @@ {"type":"system/message","data":{"turn":1,"step":3,"message":{"role":"system","content":[{"type":"text","text":"{{system}}"}],"source":{"kind":"system-prompt"},"id":"{{message:10}}"}},"sourceEventSeqs":[18],"surfaceOp":{"op":"replace","startSeq":18,"endSeq":18}} {"type":"request/header","data":{"header":{"config":{"provider":"deepseek-official","model":"deepseek-v4-flash"},"tools":["apply_patch","bash","cordis_inspect_list","cordis_inspect_query","create_goal","edit","get_goal","glob","grep","job_kill","job_list","job_output","list_agents","read","read_image","send_message","skill","subagent","subagent_fork","todo_write","update_goal","wait_agent","web_fetch","web_search","workflow","working_directory","write"]},"reason":"change","startsSeries":true}} {"type":"developer/message","data":{"turn":1,"step":3,"message":{"source":{"kind":"tool-registry"},"content":[{"type":"tool-addition","toolName":"apply_patch"},{"type":"tool-addition","toolName":"bash"},{"type":"tool-addition","toolName":"cordis_inspect_list"},{"type":"tool-addition","toolName":"cordis_inspect_query"},{"type":"tool-addition","toolName":"create_goal"},{"type":"tool-addition","toolName":"edit"},{"type":"tool-addition","toolName":"get_goal"},{"type":"tool-addition","toolName":"glob"},{"type":"tool-addition","toolName":"grep"},{"type":"tool-addition","toolName":"job_kill"},{"type":"tool-addition","toolName":"job_list"},{"type":"tool-addition","toolName":"job_output"},{"type":"tool-addition","toolName":"list_agents"},{"type":"tool-addition","toolName":"read"},{"type":"tool-addition","toolName":"read_image"},{"type":"tool-addition","toolName":"send_message"},{"type":"tool-addition","toolName":"skill"},{"type":"tool-addition","toolName":"subagent"},{"type":"tool-addition","toolName":"subagent_fork"},{"type":"tool-addition","toolName":"todo_write"},{"type":"tool-addition","toolName":"update_goal"},{"type":"tool-addition","toolName":"wait_agent"},{"type":"tool-addition","toolName":"web_fetch"},{"type":"tool-addition","toolName":"web_search"},{"type":"tool-addition","toolName":"workflow"},{"type":"tool-addition","toolName":"working_directory"},{"type":"tool-addition","toolName":"write"},{"type":"tool-removal","toolName":"run_code"}],"role":"developer","id":"{{message:11}}"},"headerSeq":29},"surfaceOp":"append"} -{"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.\",\"run_in_background\":false}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:12}}"},"usage":{"inputTokens":3,"outputTokens":3},"stream":[{"type":"chunk","time":1791315715200,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}},{"type":"tool-call-chunks","time0":1791315715200,"index":0,"dt":[],"id":"advanced-direct-child","name":"subagent","args":["{\"description\":\"Check direct child\",\"prompt\":\"Reply with exactly DIRECT_CHILD_OK and nothing else.\",\"run_in_background\":false}"]},{"type":"chunk","time":1791315715200,"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.\",\"run_in_background\":false}"}}},{"type":"chunk","time":1791315715200,"chunk":{"type":"usage","usage":{"inputTokens":3,"outputTokens":3}}},{"type":"chunk","time":1791315715200,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"} +{"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.\",\"run_in_background\":false}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:12}}"},"usage":{"inputTokens":3,"outputTokens":3},"stream":[{"type":"chunk","time":1794196083856,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}},{"type":"chunk","time":1794196083856,"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.\",\"run_in_background\":false}"}}},{"type":"chunk","time":1794196083856,"chunk":{"type":"usage","usage":{"inputTokens":3,"outputTokens":3}}},{"type":"chunk","time":1794196083856,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"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.\",\"run_in_background\":false}"}} {"type":"subagent/catalog","data":{"version":3,"childId":"{{session:2}}","childCreatedAt":1791315715221,"mode":"continuable","label":"Check direct child","name":"agent-1"}} {"type":"agent/inbox/spliced","data":{"target":"next-step","start":0,"inserted":[{"content":[{"type":"text","text":"Background subagent agent-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 agent-1 finished and will do no further work unless you send it more.","stopReason":"completed","senderSessionId":"{{session:2}}"},"role":"user","id":"{{message:13}}"}]}} @@ -39,7 +39,7 @@ {"type":"agent/inbox/spliced","data":{"target":"next-step","start":0,"removedCount":1,"inserted":[]}} {"type":"step/start","data":{"turn":1,"step":4}} {"type":"user/message","data":{"content":[{"type":"text","text":"Background subagent agent-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 agent-1 finished and will do no further work unless you send it more.","stopReason":"completed","senderSessionId":"{{session:2}}"},"role":"user","id":"{{message:13}}"},"surfaceOp":"append"} -{"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-headless-snapshot\",\"description\":\"exercise one workflow child through the headless agent\"}}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:15}}"},"usage":{"inputTokens":3,"outputTokens":3},"stream":[{"type":"chunk","time":1791315715326,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}},{"type":"tool-call-chunks","time0":1791315715326,"index":0,"dt":[],"id":"advanced-workflow","name":"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-headless-snapshot\",\"description\":\"exercise one workflow child through the headless agent\"}}"]},{"type":"chunk","time":1791315715326,"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-headless-snapshot\",\"description\":\"exercise one workflow child through the headless agent\"}}"}}},{"type":"chunk","time":1791315715326,"chunk":{"type":"usage","usage":{"inputTokens":3,"outputTokens":3}}},{"type":"chunk","time":1791315715326,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"} +{"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-headless-snapshot\",\"description\":\"exercise one workflow child through the headless agent\"}}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:15}}"},"usage":{"inputTokens":3,"outputTokens":3},"stream":[{"type":"chunk","time":1794196083993,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}},{"type":"chunk","time":1794196083993,"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-headless-snapshot\",\"description\":\"exercise one workflow child through the headless agent\"}}"}}},{"type":"chunk","time":1794196083993,"chunk":{"type":"usage","usage":{"inputTokens":3,"outputTokens":3}}},{"type":"chunk","time":1794196083993,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"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-headless-snapshot\",\"description\":\"exercise one workflow child through the headless agent\"}}"}} {"type":"tool-workflow/run-start","data":{"runId":"{{workflow:1}}","name":"advanced-headless-snapshot"}} {"type":"subagent/catalog","data":{"version":3,"childId":"{{session:3}}","childCreatedAt":1791315715414,"mode":"continuable","label":"advanced-headless-snapshot child 1","name":"agent-2"}} @@ -49,11 +49,11 @@ {"type":"tool/result","data":{"turn":1,"step":4,"message":{"role":"tool","source":{"kind":"tool","callId":"advanced-workflow"},"toolCallId":"advanced-workflow","content":[{"type":"text","text":"workflow \"advanced-headless-snapshot\" completed (1 agent).\nReturn value:\n{\n \"reply\": \"WORKFLOW_CHILD_OK\"\n}"}],"isError":false,"id":"{{message:16}}"}},"sourceEventSeqs":[41],"surfaceOp":"append"} {"type":"step/end","data":{"turn":1,"step":4}} {"type":"step/start","data":{"turn":1,"step":5}} -{"type":"assistant/message","data":{"turn":1,"step":5,"message":{"role":"assistant","content":[{"type":"tool-call","id":"advanced-inspect-after","name":"cordis_inspect_list","arguments":"{}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:17}}"},"usage":{"inputTokens":3,"outputTokens":3},"stream":[{"type":"chunk","time":1791315715551,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}},{"type":"tool-call-chunks","time0":1791315715551,"index":0,"dt":[],"id":"advanced-inspect-after","name":"cordis_inspect_list","args":["{}"]},{"type":"chunk","time":1791315715551,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"advanced-inspect-after","name":"cordis_inspect_list","arguments":"{}"}}},{"type":"chunk","time":1791315715551,"chunk":{"type":"usage","usage":{"inputTokens":3,"outputTokens":3}}},{"type":"chunk","time":1791315715551,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"} +{"type":"assistant/message","data":{"turn":1,"step":5,"message":{"role":"assistant","content":[{"type":"tool-call","id":"advanced-inspect-after","name":"cordis_inspect_list","arguments":"{}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:17}}"},"usage":{"inputTokens":3,"outputTokens":3},"stream":[{"type":"chunk","time":1794196084225,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}},{"type":"chunk","time":1794196084225,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"advanced-inspect-after","name":"cordis_inspect_list","arguments":"{}"}}},{"type":"chunk","time":1794196084225,"chunk":{"type":"usage","usage":{"inputTokens":3,"outputTokens":3}}},{"type":"chunk","time":1794196084225,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"} {"type":"tool/call","data":{"turn":1,"step":5,"callId":"advanced-inspect-after","name":"cordis_inspect_list","arguments":"{}"}} {"type":"tool/result","data":{"turn":1,"step":5,"message":{"role":"tool","source":{"kind":"tool","callId":"advanced-inspect-after"},"toolCallId":"advanced-inspect-after","content":[{"type":"text","text":"{\n \"providers\": [\n {\n \"platform\": \"host\",\n \"id\": \"Service\",\n \"description\": \"Progressive Host Service discovery: compact capability/signature directory, then one exact coding contract.\",\n \"methods\": [\n {\n \"name\": \"listService\",\n \"description\": \"Progressive Host Service discovery: compact capability/signature directory, then one exact coding contract.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"service\": {\n \"type\": \"string\",\n \"description\": \"Exact Service key. Omit it for the compact Service and method-signature directory.\"\n }\n },\n \"additionalProperties\": false\n },\n \"outputSchema\": {\n \"description\": \"Compact Service directory, or one exact Service contract with only its referenced type declarations.\"\n }\n }\n ]\n },\n {\n \"platform\": \"host\",\n \"id\": \"Event\",\n \"description\": \"Progressive Host Event discovery: compact listener directory, then one exact event contract.\",\n \"methods\": [\n {\n \"name\": \"listEvents\",\n \"description\": \"Progressive Host Event discovery: compact listener directory, then one exact event contract.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"event\": {\n \"type\": \"string\",\n \"description\": \"Exact Event name. Omit it for the compact Event and listener-signature directory.\"\n }\n },\n \"additionalProperties\": false\n },\n \"outputSchema\": {\n \"description\": \"Compact Event directory, or one exact Event contract with only its referenced type declarations.\"\n }\n }\n ]\n },\n {\n \"platform\": \"host\",\n \"id\": \"Config\",\n \"description\": \"Progressive live plugin Config discovery: paged entry directory with schema status, then one entry's exact JSON Schema.\",\n \"methods\": [\n {\n \"name\": \"listConfigs\",\n \"description\": \"Progressive live plugin Config discovery: paged entry directory with schema status, then one entry's exact JSON Schema.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"entry\": {\n \"type\": \"string\",\n \"description\": \"Exact Loader entry id from the directory; returns that entry's projected Config schema.\"\n },\n \"name\": {\n \"type\": \"string\",\n \"description\": \"Exact plugin package name; limits the directory to its entries.\"\n },\n \"offset\": {\n \"type\": \"number\",\n \"description\": \"Zero-based directory offset; defaults to 0.\"\n },\n \"limit\": {\n \"type\": \"number\",\n \"description\": \"Directory page size, from 1 to 100; defaults to 25.\"\n }\n },\n \"additionalProperties\": false\n },\n \"outputSchema\": {\n \"description\": \"One directory page of live entries with patch ids, Config status, total, and nextOffset, or one entry's projected JSON Schema with shared definitions, omission acceptance, and projection limitations.\"\n }\n }\n ]\n },\n {\n \"platform\": \"host\",\n \"id\": \"Tool\",\n \"description\": \"Tools visible to the requesting Agent, including scoped and dynamic registrations.\",\n \"methods\": [\n {\n \"name\": \"listTools\",\n \"description\": \"Return every Tool schema currently callable by this Agent.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {},\n \"additionalProperties\": false\n },\n \"outputSchema\": {\n \"description\": \"JSON data owned by this inspect provider.\"\n }\n }\n ]\n }\n ]\n}"}],"isError":false,"id":"{{message:18}}"}},"sourceEventSeqs":[51],"surfaceOp":"append"} {"type":"step/end","data":{"turn":1,"step":5}} {"type":"step/start","data":{"turn":1,"step":6}} -{"type":"assistant/message","data":{"turn":1,"step":6,"message":{"role":"assistant","content":[{"type":"text","text":"ADVANCED_HEADLESS_OK"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:19}}"},"usage":{"inputTokens":3,"outputTokens":3},"stream":[{"type":"chunk","time":1791315715575,"chunk":{"type":"block-start","index":0,"blockType":"text"}},{"type":"text-chunks","time0":1791315715575,"index":0,"dt":[],"texts":["ADVANCED_HEADLESS_OK"]},{"type":"chunk","time":1791315715575,"chunk":{"type":"block-end","index":0,"block":{"type":"text","text":"ADVANCED_HEADLESS_OK"}}},{"type":"chunk","time":1791315715575,"chunk":{"type":"usage","usage":{"inputTokens":3,"outputTokens":3}}},{"type":"chunk","time":1791315715575,"chunk":{"type":"finish","reason":{"kind":"stop"}}}]},"surfaceOp":"append"} +{"type":"assistant/message","data":{"turn":1,"step":6,"message":{"role":"assistant","content":[{"type":"text","text":"ADVANCED_HEADLESS_OK"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:19}}"},"usage":{"inputTokens":3,"outputTokens":3},"stream":[{"type":"chunk","time":1794196084265,"chunk":{"type":"block-start","index":0,"blockType":"text"}},{"type":"chunk","time":1794196084265,"chunk":{"type":"block-end","index":0,"block":{"type":"text","text":"ADVANCED_HEADLESS_OK"}}},{"type":"chunk","time":1794196084265,"chunk":{"type":"usage","usage":{"inputTokens":3,"outputTokens":3}}},{"type":"chunk","time":1794196084265,"chunk":{"type":"finish","reason":{"kind":"stop"}}}]},"surfaceOp":"append"} {"type":"step/end","data":{"turn":1,"step":6}} {"type":"turn/end","data":{"turn":1,"reason":{"kind":"completed"}}} diff --git a/snapshots/session/advanced-toolchain/snapshot.yml b/snapshots/session/advanced-toolchain/snapshot.yml index bbcdd6f562..d2cde865ef 100644 --- a/snapshots/session/advanced-toolchain/snapshot.yml +++ b/snapshots/session/advanced-toolchain/snapshot.yml @@ -9,3 +9,5 @@ header: changes: 2 promptChanges: 2 childSystemPrompts: [1, 2] +replay: + override: true diff --git a/snapshots/session/advanced-toolchain/system-prompt.expected.md b/snapshots/session/advanced-toolchain/system-prompt.expected.md index 2761bf68ad..953930ed31 100644 --- a/snapshots/session/advanced-toolchain/system-prompt.expected.md +++ b/snapshots/session/advanced-toolchain/system-prompt.expected.md @@ -76,20 +76,24 @@ Bash, formatters, code generators, and scripts can overwrite concurrent edits wi `run_code` takes two required arguments: `code`, the body of an async TypeScript function (erasable syntax only — no `enum` or namespaces; type annotations are advisory, the code runs type-stripped), and `description`, a short summary of what the program does. The declarations below are SDK bindings for this program. A declaration does not make its name a directly callable tool; only names supplied as separate tool schemas may be called directly. When no separate `bash` schema is supplied, invoke a declared `bash` binding inside `run_code`: -`run_code({ code: "return await tools.bash({ description: 'Show current directory', command: 'pwd' })", description: "Show current directory" })` +`run_code({ code: "Harness.inspect(await tools.bash({ description: 'Show current directory', command: 'pwd' }))", description: "Show current directory" })` Inside the program: - Call tools as `await tools.name(args)` — quoted access for exotic names: `tools["my-tool"](args)`. Every call resolves to the tool's typed canonical JSON value. Tool arguments must be lossless JSON. - A FAILED tool call rejects with `ToolCallError`, whose `toolName` identifies the failed tool and whose `message` is human-readable — `try/catch` it to handle and continue. - Independent read-only calls MAY overlap under `Promise.all` (safe calls run concurrently; mutating calls run alone, in submission order). Sequence dependent work with `await`. -- Emit results with `return` and/or `console.log(...)`. Only what you print or return is program output. A successful tool result containing an image is attached after the run so you can inspect it on the next step; every other intermediate result stays out of the conversation, so extract just what you need. +- Show selected results with `Harness.inspect(...values)`. Return values and final expressions are not displayed. A successful tool result containing an image is attached after the run so you can inspect it on the next step; every other intermediate result stays out of the conversation, so extract just what you need. Program-only SDK bindings: ```ts type JsonValue = null | boolean | number | string | JsonValue[] | { [key: string]: JsonValue } +declare namespace Harness { + function inspect(...values: unknown[]): void; +} + interface ToolArgsMap { /** Create, update, delete, or move UTF-8 text files with one patch; prefer it over multiple edit calls when a change spans several files or separated regions. */ apply_patch: { diff --git a/snapshots/session/advanced-toolchain/tool-schemas.expected.json b/snapshots/session/advanced-toolchain/tool-schemas.expected.json index 60c0d74948..543a28c9f3 100644 --- a/snapshots/session/advanced-toolchain/tool-schemas.expected.json +++ b/snapshots/session/advanced-toolchain/tool-schemas.expected.json @@ -684,7 +684,7 @@ [ { "name": "run_code", - "description": "Execute a TypeScript program against the available tools. Takes two required arguments: `code`, the BODY of an async function (erasable syntax only; top-level `await` and `return` work), and `description`, a short summary of what the program does. Call tools as `await tools.name(args)` per the declarations in the system prompt. Only what you print or return is program output — curate it. Image-bearing subtool results are attached after the run. The program is the body of an async TypeScript function; only erasable syntax is accepted, with no enum or namespaces. Type annotations are advisory. Top-level await and return are supported. Emit output with console.log(...) or return a lossless JSON value. Only printed values and the returned value become program output. Each call runs in a fresh Node process. Node APIs are available through await import(...). Relative paths use the supplied working directory; process.env starts empty. Direct filesystem access follows this execution's sandbox policy. Each program starts in the Session's current directory. Running programs keep their initial directory. A sandbox escalation approves this complete program for one execution only. Nested tools retain their own policies and approvals. Request wider access only after evidence of a denial. Earlier effects may already have completed: inspect them before explicitly retrying. Programs are never replayed automatically.", + "description": "Execute a TypeScript program against the available tools. Takes two required arguments: `code`, the BODY of an async function (erasable syntax only; top-level `await` and `return` work), and `description`, a short summary of what the program does. Call tools as `await tools.name(args)` per the declarations in the system prompt. Show selected results with Harness.inspect(...values). Return values are not displayed. Image-bearing subtool results are attached after the run. The program is the body of an async TypeScript function; only erasable syntax is accepted, with no enum or namespaces. Type annotations are advisory. Top-level await and return are supported. Console is unavailable. Use Harness.inspect(...values) for output. Each call runs in a fresh Node process. Node APIs are available through await import(...). Relative paths use the supplied working directory; process.env starts empty. Direct filesystem access follows this execution's sandbox policy. Each program starts in the Session's current directory. Running programs keep their initial directory. A sandbox escalation approves this complete program for one execution only. Nested tools retain their own policies and approvals. Request wider access only after evidence of a denial. Earlier effects may already have completed: inspect them before explicitly retrying. Programs are never replayed automatically.", "parameters": { "type": "object", "properties": { diff --git a/snapshots/session/both-mode-turn/replay.override.json b/snapshots/session/both-mode-turn/replay.override.json new file mode 100644 index 0000000000..85c4fad56d --- /dev/null +++ b/snapshots/session/both-mode-turn/replay.override.json @@ -0,0 +1,24 @@ +[ + { + "kind": "chunks", + "chunks": [ + {"type":"block-start","index":0,"blockType":"reasoning"}, + {"type":"block-start","index":1,"blockType":"tool-call"}, + {"type":"block-end","index":0,"block":{"type":"reasoning","text":"The user wants me to call the run_code tool with a TypeScript program that runs `echo BOTH_OK` via `tools.bash` and returns its output."}}, + {"type":"block-end","index":1,"block":{"type":"tool-call","id":"call_00_Era4M5eh79bvNOIey5q90401","name":"run_code","arguments":"{\"code\":\"const result = await tools.bash({ command: \\\"echo BOTH_OK\\\", description: \\\"Print BOTH_OK\\\" });\\nHarness.inspect(result.stdout.text); return;\",\"description\":\"Run echo BOTH_OK via tools.bash\"}"}}, + {"type":"usage","usage":{"inputTokens":10400,"outputTokens":130,"cacheReadTokens":0,"reasoningTokens":34}}, + {"type":"finish","reason":{"kind":"tool-calls"}} + ] + }, + { + "kind": "chunks", + "chunks": [ + {"type":"block-start","index":0,"blockType":"reasoning"}, + {"type":"block-start","index":1,"blockType":"text"}, + {"type":"block-end","index":0,"block":{"type":"reasoning","text":"The output is \"BOTH_OK\" (with a trailing newline, but that's fine). The user asked me to reply with that output only."}}, + {"type":"block-end","index":1,"block":{"type":"text","text":"BOTH_OK"}}, + {"type":"usage","usage":{"inputTokens":50,"outputTokens":35,"cacheReadTokens":10496,"reasoningTokens":31}}, + {"type":"finish","reason":{"kind":"stop"}} + ] + } +] diff --git a/snapshots/session/both-mode-turn/session.v4.jsonl b/snapshots/session/both-mode-turn/session.v4.jsonl index edaf6b9531..95d4a8b4da 100644 --- a/snapshots/session/both-mode-turn/session.v4.jsonl +++ b/snapshots/session/both-mode-turn/session.v4.jsonl @@ -12,8 +12,8 @@ {"type":"request/header","data":{"header":{"config":{"provider":"deepseek-official","model":"deepseek-v4-flash"},"tools":"{{tools}}"},"reason":"initial"}} {"type":"request/context","data":{"provider":"deepseek-official","model":"deepseek-v4-flash"}} {"type":"session/title","data":{"title":"Call the run_code tool (NOT","messageSeqs":[8],"source":{"kind":"fallback"}}} -{"type":"assistant/message","data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"reasoning","text":"The user wants me to call the run_code tool with a TypeScript program that runs `echo BOTH_OK` via `tools.bash` and returns its output."},{"type":"tool-call","id":"call_00_Era4M5eh79bvNOIey5q90401","name":"run_code","arguments":"{\"code\": \"const result = await tools.bash({ command: \\\"echo BOTH_OK\\\", description: \\\"Print BOTH_OK\\\" });\\nreturn result.stdout.text;\", \"description\": \"Run echo BOTH_OK via tools.bash\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:4}}"},"usage":{"inputTokens":10400,"outputTokens":130,"cacheReadTokens":0,"reasoningTokens":34},"stream":[{"type":"chunk","time":1791427798733,"chunk":{"type":"block-start","index":0,"blockType":"reasoning"}},{"type":"reasoning-chunks","time0":1791427798733,"index":0,"dt":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"texts":["The"," user"," wants"," me"," to"," call"," the"," run","_code"," tool"," with"," a"," Type","Script"," program"," that"," runs"," `","echo"," B","OTH","_OK","`"," via"," `","tools",".b","ash","`"," and"," returns"," its"," output","."]},{"type":"chunk","time":1791427798733,"chunk":{"type":"block-start","index":1,"blockType":"tool-call"}},{"type":"tool-call-chunks","time0":1791427798733,"index":1,"dt":[0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"id":"call_00_Era4M5eh79bvNOIey5q90401","name":"run_code","args":["","{","\"","code","\"",": ","\"","const"," result"," ="," await"," tools",".b","ash","({"," command",":"," \\\"","echo"," B","OTH","_OK","\\\","," description",":"," \\\"","Print"," B","OTH","_OK","\\\""," });\\n","return"," result",".stdout",".text",";","\"",", ","\"","description","\"",": ","\"","Run"," echo"," B","OTH","_OK"," via"," tools",".b","ash","\"","}"]},{"type":"chunk","time":1791427798734,"chunk":{"type":"block-end","index":0,"block":{"type":"reasoning","text":"The user wants me to call the run_code tool with a TypeScript program that runs `echo BOTH_OK` via `tools.bash` and returns its output."}}},{"type":"chunk","time":1791427798734,"chunk":{"type":"block-end","index":1,"block":{"type":"tool-call","id":"call_00_Era4M5eh79bvNOIey5q90401","name":"run_code","arguments":"{\"code\": \"const result = await tools.bash({ command: \\\"echo BOTH_OK\\\", description: \\\"Print BOTH_OK\\\" });\\nreturn result.stdout.text;\", \"description\": \"Run echo BOTH_OK via tools.bash\"}"}}},{"type":"chunk","time":1791427798734,"chunk":{"type":"usage","usage":{"inputTokens":10400,"outputTokens":130,"cacheReadTokens":0,"reasoningTokens":34}}},{"type":"chunk","time":1791427798734,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"} -{"type":"tool/call","data":{"turn":1,"step":1,"callId":"call_00_Era4M5eh79bvNOIey5q90401","name":"run_code","arguments":"{\"code\": \"const result = await tools.bash({ command: \\\"echo BOTH_OK\\\", description: \\\"Print BOTH_OK\\\" });\\nreturn result.stdout.text;\", \"description\": \"Run echo BOTH_OK via tools.bash\"}"}} +{"type":"assistant/message","data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"reasoning","text":"The user wants me to call the run_code tool with a TypeScript program that runs `echo BOTH_OK` via `tools.bash` and returns its output."},{"type":"tool-call","id":"call_00_Era4M5eh79bvNOIey5q90401","name":"run_code","arguments":"{\"code\":\"const result = await tools.bash({ command: \\\"echo BOTH_OK\\\", description: \\\"Print BOTH_OK\\\" });\\nHarness.inspect(result.stdout.text); return;\",\"description\":\"Run echo BOTH_OK via tools.bash\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:4}}"},"usage":{"inputTokens":10400,"outputTokens":130,"cacheReadTokens":0,"reasoningTokens":34},"stream":[{"type":"chunk","time":1794195771161,"chunk":{"type":"block-start","index":0,"blockType":"reasoning"}},{"type":"chunk","time":1794195771161,"chunk":{"type":"block-start","index":1,"blockType":"tool-call"}},{"type":"chunk","time":1794195771161,"chunk":{"type":"block-end","index":0,"block":{"type":"reasoning","text":"The user wants me to call the run_code tool with a TypeScript program that runs `echo BOTH_OK` via `tools.bash` and returns its output."}}},{"type":"chunk","time":1794195771161,"chunk":{"type":"block-end","index":1,"block":{"type":"tool-call","id":"call_00_Era4M5eh79bvNOIey5q90401","name":"run_code","arguments":"{\"code\":\"const result = await tools.bash({ command: \\\"echo BOTH_OK\\\", description: \\\"Print BOTH_OK\\\" });\\nHarness.inspect(result.stdout.text); return;\",\"description\":\"Run echo BOTH_OK via tools.bash\"}"}}},{"type":"chunk","time":1794195771161,"chunk":{"type":"usage","usage":{"inputTokens":10400,"outputTokens":130,"cacheReadTokens":0,"reasoningTokens":34}}},{"type":"chunk","time":1794195771161,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"} +{"type":"tool/call","data":{"turn":1,"step":1,"callId":"call_00_Era4M5eh79bvNOIey5q90401","name":"run_code","arguments":"{\"code\":\"const result = await tools.bash({ command: \\\"echo BOTH_OK\\\", description: \\\"Print BOTH_OK\\\" });\\nHarness.inspect(result.stdout.text); return;\",\"description\":\"Run echo BOTH_OK via tools.bash\"}"}} {"type":"tool/ptc-dispatch-start","data":{"rootCallId":"call_00_Era4M5eh79bvNOIey5q90401","parentCallId":"call_00_Era4M5eh79bvNOIey5q90401","subCallId":"call_00_Era4M5eh79bvNOIey5q90401:ptc:1","name":"bash","arguments":{"command":"echo BOTH_OK","description":"Print BOTH_OK"}}} {"type":"job/registered","data":{"jobId":"{{id:1}}","owner":"{{session:1}}","kind":"bash","label":"echo BOTH_OK","startedAt":1791427798824,"toolCall":{"callId":"call_00_Era4M5eh79bvNOIey5q90401:ptc:1","rootCallId":"call_00_Era4M5eh79bvNOIey5q90401"}}} {"type":"job/settled","data":{"jobId":"{{id:1}}","owner":"{{session:1}}","kind":"bash","label":"echo BOTH_OK","startedAt":1791427798824,"toolCall":{"callId":"call_00_Era4M5eh79bvNOIey5q90401:ptc:1","rootCallId":"call_00_Era4M5eh79bvNOIey5q90401"},"finishedAt":1791427798827,"status":"completed","cause":"producer","producerReported":true,"detail":"exit code: 0"}} @@ -21,6 +21,6 @@ {"type":"tool/result","data":{"turn":1,"step":1,"message":{"role":"tool","source":{"kind":"tool","callId":"call_00_Era4M5eh79bvNOIey5q90401"},"toolCallId":"call_00_Era4M5eh79bvNOIey5q90401","content":[{"type":"text","text":"BOTH_OK\n"}],"isError":false,"id":"{{message:5}}"}},"sourceEventSeqs":[14],"surfaceOp":"append"} {"type":"step/end","data":{"turn":1,"step":1}} {"type":"step/start","data":{"turn":1,"step":2}} -{"type":"assistant/message","data":{"turn":1,"step":2,"message":{"role":"assistant","content":[{"type":"reasoning","text":"The output is \"BOTH_OK\" (with a trailing newline, but that's fine). The user asked me to reply with that output only."},{"type":"text","text":"BOTH_OK"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:6}}"},"usage":{"inputTokens":50,"outputTokens":35,"cacheReadTokens":10496,"reasoningTokens":31},"stream":[{"type":"chunk","time":1791427798862,"chunk":{"type":"block-start","index":0,"blockType":"reasoning"}},{"type":"reasoning-chunks","time0":1791427798862,"index":0,"dt":[0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"texts":["The"," output"," is"," \"","B","OTH","_OK","\""," (","with"," a"," trailing"," new","line",","," but"," that","'s"," fine",")."," The"," user"," asked"," me"," to"," reply"," with"," that"," output"," only","."]},{"type":"chunk","time":1791427798863,"chunk":{"type":"block-start","index":1,"blockType":"text"}},{"type":"text-chunks","time0":1791427798863,"index":1,"dt":[0,0],"texts":["B","OTH","_OK"]},{"type":"chunk","time":1791427798863,"chunk":{"type":"block-end","index":0,"block":{"type":"reasoning","text":"The output is \"BOTH_OK\" (with a trailing newline, but that's fine). The user asked me to reply with that output only."}}},{"type":"chunk","time":1791427798863,"chunk":{"type":"block-end","index":1,"block":{"type":"text","text":"BOTH_OK"}}},{"type":"chunk","time":1791427798863,"chunk":{"type":"usage","usage":{"inputTokens":50,"outputTokens":35,"cacheReadTokens":10496,"reasoningTokens":31}}},{"type":"chunk","time":1791427798863,"chunk":{"type":"finish","reason":{"kind":"stop"}}}]},"surfaceOp":"append"} +{"type":"assistant/message","data":{"turn":1,"step":2,"message":{"role":"assistant","content":[{"type":"reasoning","text":"The output is \"BOTH_OK\" (with a trailing newline, but that's fine). The user asked me to reply with that output only."},{"type":"text","text":"BOTH_OK"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:6}}"},"usage":{"inputTokens":50,"outputTokens":35,"cacheReadTokens":10496,"reasoningTokens":31},"stream":[{"type":"chunk","time":1794195771355,"chunk":{"type":"block-start","index":0,"blockType":"reasoning"}},{"type":"chunk","time":1794195771355,"chunk":{"type":"block-start","index":1,"blockType":"text"}},{"type":"chunk","time":1794195771355,"chunk":{"type":"block-end","index":0,"block":{"type":"reasoning","text":"The output is \"BOTH_OK\" (with a trailing newline, but that's fine). The user asked me to reply with that output only."}}},{"type":"chunk","time":1794195771355,"chunk":{"type":"block-end","index":1,"block":{"type":"text","text":"BOTH_OK"}}},{"type":"chunk","time":1794195771355,"chunk":{"type":"usage","usage":{"inputTokens":50,"outputTokens":35,"cacheReadTokens":10496,"reasoningTokens":31}}},{"type":"chunk","time":1794195771355,"chunk":{"type":"finish","reason":{"kind":"stop"}}}]},"surfaceOp":"append"} {"type":"step/end","data":{"turn":1,"step":2}} {"type":"turn/end","data":{"turn":1,"reason":{"kind":"completed"}}} diff --git a/snapshots/session/both-mode-turn/snapshot.yml b/snapshots/session/both-mode-turn/snapshot.yml index f367d2ec00..b109f721f4 100644 --- a/snapshots/session/both-mode-turn/snapshot.yml +++ b/snapshots/session/both-mode-turn/snapshot.yml @@ -7,3 +7,5 @@ header: class: ptc-shell pin: true toolSchemasSource: ptc-turn +replay: + override: true diff --git a/snapshots/session/both-mode-turn/system-prompt.expected.md b/snapshots/session/both-mode-turn/system-prompt.expected.md index 3cc2d5e4c5..0a1ddc32c3 100644 --- a/snapshots/session/both-mode-turn/system-prompt.expected.md +++ b/snapshots/session/both-mode-turn/system-prompt.expected.md @@ -39,20 +39,24 @@ Bash, formatters, code generators, and scripts can overwrite concurrent edits wi `run_code` takes two required arguments: `code`, the body of an async TypeScript function (erasable syntax only — no `enum` or namespaces; type annotations are advisory, the code runs type-stripped), and `description`, a short summary of what the program does. The declarations below are SDK bindings for this program. A declaration does not make its name a directly callable tool; only names supplied as separate tool schemas may be called directly. When no separate `bash` schema is supplied, invoke a declared `bash` binding inside `run_code`: -`run_code({ code: "return await tools.bash({ description: 'Show current directory', command: 'pwd' })", description: "Show current directory" })` +`run_code({ code: "Harness.inspect(await tools.bash({ description: 'Show current directory', command: 'pwd' }))", description: "Show current directory" })` Inside the program: - Call tools as `await tools.name(args)` — quoted access for exotic names: `tools["my-tool"](args)`. Every call resolves to the tool's typed canonical JSON value. Tool arguments must be lossless JSON. - A FAILED tool call rejects with `ToolCallError`, whose `toolName` identifies the failed tool and whose `message` is human-readable — `try/catch` it to handle and continue. - Independent read-only calls MAY overlap under `Promise.all` (safe calls run concurrently; mutating calls run alone, in submission order). Sequence dependent work with `await`. -- Emit results with `return` and/or `console.log(...)`. Only what you print or return is program output. A successful tool result containing an image is attached after the run so you can inspect it on the next step; every other intermediate result stays out of the conversation, so extract just what you need. +- Show selected results with `Harness.inspect(...values)`. Return values and final expressions are not displayed. A successful tool result containing an image is attached after the run so you can inspect it on the next step; every other intermediate result stays out of the conversation, so extract just what you need. Program-only SDK bindings: ```ts type JsonValue = null | boolean | number | string | JsonValue[] | { [key: string]: JsonValue } +declare namespace Harness { + function inspect(...values: unknown[]): void; +} + interface ToolArgsMap { /** Create, update, delete, or move UTF-8 text files with one patch; prefer it over multiple edit calls when a change spans several files or separated regions. */ apply_patch: { diff --git a/snapshots/session/cua-ptc/replay.override.json b/snapshots/session/cua-ptc/replay.override.json index 63e4009990..58547ff176 100644 --- a/snapshots/session/cua-ptc/replay.override.json +++ b/snapshots/session/cua-ptc/replay.override.json @@ -14,7 +14,7 @@ "type": "tool-call", "id": "cua-ptc-native-result", "name": "run_code", - "arguments": "{\"code\":\"const { Buffer } = await import('node:buffer');\\nconst app = await tools.cua({ method: 'getApp', params: { selector: 'test.fixture' } });\\nconst state = await tools.cua({ method: 'getState', params: { appHandle: app.appHandle, windowId: 7 } });\\nconst shot = await tools.cua({ method: 'screenshot', params: { appHandle: app.appHandle, windowId: 7 } });\\nconst bytes = Buffer.from(shot.data, 'base64');\\nreturn { appHandle: app.appHandle, hasIncrement: state.tree.includes('Increment'), pngBytes: bytes.length, signature: bytes.subarray(0, 8).toString('hex'), imageWidth: shot.attachment.width, imageHeight: shot.attachment.height };\",\"description\":\"Read native CUA values and receive one screenshot\"}" + "arguments": "{\"code\":\"const { Buffer } = await import('node:buffer');\\nconst app = await tools.cua({ method: 'getApp', params: { selector: 'test.fixture' } });\\nconst state = await tools.cua({ method: 'getState', params: { appHandle: app.appHandle, windowId: 7 } });\\nconst shot = await tools.cua({ method: 'screenshot', params: { appHandle: app.appHandle, windowId: 7 } });\\nconst bytes = Buffer.from(shot.data, 'base64');\\nHarness.inspect({ appHandle: app.appHandle, hasIncrement: state.tree.includes('Increment'), pngBytes: bytes.length, signature: bytes.subarray(0, 8).toString('hex'), imageWidth: shot.attachment.width, imageHeight: shot.attachment.height }); return;\",\"description\":\"Read native CUA values and receive one screenshot\"}" } }, { diff --git a/snapshots/session/cua-ptc/session.v4.jsonl b/snapshots/session/cua-ptc/session.v4.jsonl index 87791413b2..b6f56725e7 100644 --- a/snapshots/session/cua-ptc/session.v4.jsonl +++ b/snapshots/session/cua-ptc/session.v4.jsonl @@ -12,15 +12,15 @@ {"type":"request/header","data":{"header":{"config":{"provider":"deepseek-official","model":"deepseek-v4-flash-vision-exp"},"tools":"{{tools}}"},"reason":"initial"}} {"type":"request/context","data":{"provider":"deepseek-official","model":"deepseek-v4-flash-vision-exp"}} {"type":"session/title","data":{"title":"Use tools.cua from Node PTC","messageSeqs":[8],"source":{"kind":"fallback"}}} -{"type":"assistant/message","data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"tool-call","id":"cua-ptc-native-result","name":"run_code","arguments":"{\"code\":\"const { Buffer } = await import('node:buffer');\\nconst app = await tools.cua({ method: 'getApp', params: { selector: 'test.fixture' } });\\nconst state = await tools.cua({ method: 'getState', params: { appHandle: app.appHandle, windowId: 7 } });\\nconst shot = await tools.cua({ method: 'screenshot', params: { appHandle: app.appHandle, windowId: 7 } });\\nconst bytes = Buffer.from(shot.data, 'base64');\\nreturn { appHandle: app.appHandle, hasIncrement: state.tree.includes('Increment'), pngBytes: bytes.length, signature: bytes.subarray(0, 8).toString('hex'), imageWidth: shot.attachment.width, imageHeight: shot.attachment.height };\",\"description\":\"Read native CUA values and receive one screenshot\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash-vision-exp"},"id":"{{message:4}}"},"usage":{"inputTokens":3,"outputTokens":3},"stream":[{"type":"chunk","time":1791864698727,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}},{"type":"chunk","time":1791864698727,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"cua-ptc-native-result","name":"run_code","arguments":"{\"code\":\"const { Buffer } = await import('node:buffer');\\nconst app = await tools.cua({ method: 'getApp', params: { selector: 'test.fixture' } });\\nconst state = await tools.cua({ method: 'getState', params: { appHandle: app.appHandle, windowId: 7 } });\\nconst shot = await tools.cua({ method: 'screenshot', params: { appHandle: app.appHandle, windowId: 7 } });\\nconst bytes = Buffer.from(shot.data, 'base64');\\nreturn { appHandle: app.appHandle, hasIncrement: state.tree.includes('Increment'), pngBytes: bytes.length, signature: bytes.subarray(0, 8).toString('hex'), imageWidth: shot.attachment.width, imageHeight: shot.attachment.height };\",\"description\":\"Read native CUA values and receive one screenshot\"}"}}},{"type":"chunk","time":1791864698727,"chunk":{"type":"usage","usage":{"inputTokens":3,"outputTokens":3}}},{"type":"chunk","time":1791864698727,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"} -{"type":"tool/call","data":{"turn":1,"step":1,"callId":"cua-ptc-native-result","name":"run_code","arguments":"{\"code\":\"const { Buffer } = await import('node:buffer');\\nconst app = await tools.cua({ method: 'getApp', params: { selector: 'test.fixture' } });\\nconst state = await tools.cua({ method: 'getState', params: { appHandle: app.appHandle, windowId: 7 } });\\nconst shot = await tools.cua({ method: 'screenshot', params: { appHandle: app.appHandle, windowId: 7 } });\\nconst bytes = Buffer.from(shot.data, 'base64');\\nreturn { appHandle: app.appHandle, hasIncrement: state.tree.includes('Increment'), pngBytes: bytes.length, signature: bytes.subarray(0, 8).toString('hex'), imageWidth: shot.attachment.width, imageHeight: shot.attachment.height };\",\"description\":\"Read native CUA values and receive one screenshot\"}"}} +{"type":"assistant/message","data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"tool-call","id":"cua-ptc-native-result","name":"run_code","arguments":"{\"code\":\"const { Buffer } = await import('node:buffer');\\nconst app = await tools.cua({ method: 'getApp', params: { selector: 'test.fixture' } });\\nconst state = await tools.cua({ method: 'getState', params: { appHandle: app.appHandle, windowId: 7 } });\\nconst shot = await tools.cua({ method: 'screenshot', params: { appHandle: app.appHandle, windowId: 7 } });\\nconst bytes = Buffer.from(shot.data, 'base64');\\nHarness.inspect({ appHandle: app.appHandle, hasIncrement: state.tree.includes('Increment'), pngBytes: bytes.length, signature: bytes.subarray(0, 8).toString('hex'), imageWidth: shot.attachment.width, imageHeight: shot.attachment.height }); return;\",\"description\":\"Read native CUA values and receive one screenshot\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash-vision-exp"},"id":"{{message:4}}"},"usage":{"inputTokens":3,"outputTokens":3},"stream":[{"type":"chunk","time":1791864698727,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}},{"type":"chunk","time":1791864698727,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"cua-ptc-native-result","name":"run_code","arguments":"{\"code\":\"const { Buffer } = await import('node:buffer');\\nconst app = await tools.cua({ method: 'getApp', params: { selector: 'test.fixture' } });\\nconst state = await tools.cua({ method: 'getState', params: { appHandle: app.appHandle, windowId: 7 } });\\nconst shot = await tools.cua({ method: 'screenshot', params: { appHandle: app.appHandle, windowId: 7 } });\\nconst bytes = Buffer.from(shot.data, 'base64');\\nHarness.inspect({ appHandle: app.appHandle, hasIncrement: state.tree.includes('Increment'), pngBytes: bytes.length, signature: bytes.subarray(0, 8).toString('hex'), imageWidth: shot.attachment.width, imageHeight: shot.attachment.height }); return;\",\"description\":\"Read native CUA values and receive one screenshot\"}"}}},{"type":"chunk","time":1791864698727,"chunk":{"type":"usage","usage":{"inputTokens":3,"outputTokens":3}}},{"type":"chunk","time":1791864698727,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"} +{"type":"tool/call","data":{"turn":1,"step":1,"callId":"cua-ptc-native-result","name":"run_code","arguments":"{\"code\":\"const { Buffer } = await import('node:buffer');\\nconst app = await tools.cua({ method: 'getApp', params: { selector: 'test.fixture' } });\\nconst state = await tools.cua({ method: 'getState', params: { appHandle: app.appHandle, windowId: 7 } });\\nconst shot = await tools.cua({ method: 'screenshot', params: { appHandle: app.appHandle, windowId: 7 } });\\nconst bytes = Buffer.from(shot.data, 'base64');\\nHarness.inspect({ appHandle: app.appHandle, hasIncrement: state.tree.includes('Increment'), pngBytes: bytes.length, signature: bytes.subarray(0, 8).toString('hex'), imageWidth: shot.attachment.width, imageHeight: shot.attachment.height }); return;\",\"description\":\"Read native CUA values and receive one screenshot\"}"}} {"type":"tool/ptc-dispatch-start","data":{"rootCallId":"cua-ptc-native-result","parentCallId":"cua-ptc-native-result","subCallId":"cua-ptc-native-result:ptc:1","name":"cua","arguments":{"method":"getApp","params":{"selector":"test.fixture"}}}} {"type":"tool/ptc-dispatch","data":{"rootCallId":"cua-ptc-native-result","parentCallId":"cua-ptc-native-result","subCallId":"cua-ptc-native-result:ptc:1","name":"cua","arguments":{"method":"getApp","params":{"selector":"test.fixture"}},"isError":false,"content":[{"type":"text","text":"{\"appHandle\":\"fixture-app\",\"name\":\"Fixture\",\"bundleId\":\"test.fixture\",\"running\":true}"}]}} {"type":"tool/ptc-dispatch-start","data":{"rootCallId":"cua-ptc-native-result","parentCallId":"cua-ptc-native-result","subCallId":"cua-ptc-native-result:ptc:2","name":"cua","arguments":{"method":"getState","params":{"appHandle":"fixture-app","windowId":7}}}} {"type":"tool/ptc-dispatch","data":{"rootCallId":"cua-ptc-native-result","parentCallId":"cua-ptc-native-result","subCallId":"cua-ptc-native-result:ptc:2","name":"cua","arguments":{"method":"getState","params":{"appHandle":"fixture-app","windowId":7}},"isError":false,"content":[{"type":"text","text":"{\"appHandle\":\"fixture-app\",\"windowId\":7,\"snapshotId\":\"fixture-snapshot-1\",\"tree\":\"AX scope=window offsets=UTF-16\\n- [0] AXButton \\\"Increment\\\"\\n- [1] AXTextField valueWritable=true selectionWritable=true value=\\\"\\\" placeholder=\\\"Fixture text\\\"\\n- AXStaticText \\\"Count: 0\\\"\\n- [2] AXMenuBarItem \\\"File\\\"\\nMenu contents omitted; request includeMenus=true to expand.\",\"windows\":[{\"windowId\":7,\"appName\":\"Fixture\",\"title\":\"CUA fixture\",\"bounds\":{\"x\":40,\"y\":60,\"width\":320,\"height\":200},\"isOnScreen\":true,\"onCurrentSpace\":true}],\"truncated\":false}"}]}} {"type":"tool/ptc-dispatch-start","data":{"rootCallId":"cua-ptc-native-result","parentCallId":"cua-ptc-native-result","subCallId":"cua-ptc-native-result:ptc:3","name":"cua","arguments":{"method":"screenshot","params":{"appHandle":"fixture-app","windowId":7}}}} {"type":"tool/ptc-dispatch","data":{"rootCallId":"cua-ptc-native-result","parentCallId":"cua-ptc-native-result","subCallId":"cua-ptc-native-result:ptc:3","name":"cua","arguments":{"method":"screenshot","params":{"appHandle":"fixture-app","windowId":7}},"isError":false,"content":[{"type":"text","text":"{\"pixelWidth\":1,\"pixelHeight\":1,\"windowId\":7,\"bounds\":{\"x\":40,\"y\":60,\"width\":320,\"height\":200},\"coordinateSpace\":\"screenshot-pixels\"}"},{"type":"image","attachment":{"attachmentId":"sha256:999f1d1527ee7e79266f16add5430fff76b1225d742464a5b1ff1f02971bb8ee","mediaType":"image/png","width":1,"height":1,"bytes":90}}]}} -{"type":"tool/result","data":{"turn":1,"step":1,"message":{"role":"tool","source":{"kind":"tool","callId":"cua-ptc-native-result"},"toolCallId":"cua-ptc-native-result","content":[{"type":"text","text":"{\n \"appHandle\": \"fixture-app\",\n \"hasIncrement\": true,\n \"pngBytes\": 90,\n \"signature\": \"89504e470d0a1a0a\",\n \"imageWidth\": 1,\n \"imageHeight\": 1\n}"}],"isError":false,"id":"{{message:5}}"}},"sourceEventSeqs":[14],"surfaceOp":"append"} +{"type":"tool/result","data":{"turn":1,"step":1,"message":{"role":"tool","source":{"kind":"tool","callId":"cua-ptc-native-result"},"toolCallId":"cua-ptc-native-result","content":[{"type":"text","text":"{\n appHandle: 'fixture-app',\n hasIncrement: true,\n pngBytes: 90,\n signature: '89504e470d0a1a0a',\n imageWidth: 1,\n imageHeight: 1\n}"}],"isError":false,"id":"{{message:5}}"}},"sourceEventSeqs":[14],"surfaceOp":"append"} {"type":"agent/inbox/spliced","data":{"target":"next-step","start":0,"inserted":[{"content":[{"type":"text","text":"{\"pixelWidth\":1,\"pixelHeight\":1,\"windowId\":7,\"bounds\":{\"x\":40,\"y\":60,\"width\":320,\"height\":200},\"coordinateSpace\":\"screenshot-pixels\"}"},{"type":"image","attachment":{"attachmentId":"sha256:999f1d1527ee7e79266f16add5430fff76b1225d742464a5b1ff1f02971bb8ee","mediaType":"image/png","width":1,"height":1,"bytes":90}}],"source":{"kind":"ptc-mode"},"role":"user","id":"{{message:6}}"}]}} {"type":"step/end","data":{"turn":1,"step":1}} {"type":"agent/inbox/spliced","data":{"target":"next-step","start":0,"removedCount":1,"inserted":[]}} diff --git a/snapshots/session/cua-ptc/system-prompt.expected.md b/snapshots/session/cua-ptc/system-prompt.expected.md index 43f68b4c34..928043734c 100644 --- a/snapshots/session/cua-ptc/system-prompt.expected.md +++ b/snapshots/session/cua-ptc/system-prompt.expected.md @@ -39,7 +39,7 @@ macOS: accessibility tree operations are available at app or window level. Simul Use cua to observe and operate native applications. Always use cua.getCapabilities() to confirm whether a specific API is available on the current platform; its reported capabilities take precedence over availability statements in these instructions. All methods are async; observation options default to { emit: true }, and emit: false suppresses sink output only, leaving returned values unchanged. Actions report dispatch evidence, not verified completion. If sessionLocked is true or an operation reports SESSION_LOCKED, stop UI actions and ask the user to unlock the desktop session; null or unknown means verify a usable unlocked desktop session first. cua.getApp requests user consent; if access is denied or revoked, stop using that application until the user explicitly permits another request, and never bypass a refusal or switch tools to avoid it. Never replay uncertain input without a fresh observation. -In persistent PTC, use the host-provided bridge initialization once and retain the returned objects; cua comes from that bridge and is not a preinstalled global. Use void await for observations that emit, and never stringify screenshot image bytes. +In persistent PTC, use the host-provided bridge initialization once and retain the returned objects; cua comes from that bridge and is not a preinstalled global. Observations emit through Harness.inspect; never stringify screenshot image bytes. Signatures show resolved return values after await. ActionResult = { dispatched: boolean, foregroundChanged?: boolean | null }; foregroundChanged is omitted when not sampled and null when the sample is unavailable. JsonValue = string | number | boolean | null | JsonValue[] | { [key: string]: JsonValue }; numbers must be finite. App is the application object with readonly app.appHandle: string (opaque) and the methods listed below for app level. Window is the window object with readonly window.windowId: number and the methods listed below for window level. Neither object carries pid. @@ -91,20 +91,24 @@ Maximum click holdMs: 5000 ms. `run_code` takes two required arguments: `code`, the body of an async TypeScript function (erasable syntax only — no `enum` or namespaces; type annotations are advisory, the code runs type-stripped), and `description`, a short summary of what the program does. The declarations below are SDK bindings for this program. A declaration does not make its name a directly callable tool; only names supplied as separate tool schemas may be called directly. When no separate `bash` schema is supplied, invoke a declared `bash` binding inside `run_code`: -`run_code({ code: "return await tools.bash({ description: 'Show current directory', command: 'pwd' })", description: "Show current directory" })` +`run_code({ code: "Harness.inspect(await tools.bash({ description: 'Show current directory', command: 'pwd' }))", description: "Show current directory" })` Inside the program: - Call tools as `await tools.name(args)` — quoted access for exotic names: `tools["my-tool"](args)`. Every call resolves to the tool's typed canonical JSON value. Tool arguments must be lossless JSON. - A FAILED tool call rejects with `ToolCallError`, whose `toolName` identifies the failed tool and whose `message` is human-readable — `try/catch` it to handle and continue. - Independent read-only calls MAY overlap under `Promise.all` (safe calls run concurrently; mutating calls run alone, in submission order). Sequence dependent work with `await`. -- Emit results with `return` and/or `console.log(...)`. Only what you print or return is program output. A successful tool result containing an image is attached after the run so you can inspect it on the next step; every other intermediate result stays out of the conversation, so extract just what you need. +- Show selected results with `Harness.inspect(...values)`. Return values and final expressions are not displayed. A successful tool result containing an image is attached after the run so you can inspect it on the next step; every other intermediate result stays out of the conversation, so extract just what you need. Program-only SDK bindings: ```ts type JsonValue = null | boolean | number | string | JsonValue[] | { [key: string]: JsonValue } +declare namespace Harness { + function inspect(...values: unknown[]): void; +} + interface ToolArgsMap { /** Create, update, delete, or move UTF-8 text files with one patch; prefer it over multiple edit calls when a change spans several files or separated regions. */ apply_patch: { diff --git a/snapshots/session/mcp-empty-ptc/replay.override.json b/snapshots/session/mcp-empty-ptc/replay.override.json new file mode 100644 index 0000000000..d73d3fdcc2 --- /dev/null +++ b/snapshots/session/mcp-empty-ptc/replay.override.json @@ -0,0 +1,20 @@ +[ + { + "kind": "chunks", + "chunks": [ + {"type":"block-start","index":0,"blockType":"tool-call"}, + {"type":"block-end","index":0,"block":{"type":"tool-call","id":"call_00_ET_nCgG71pOO3KB4MYELUyr5350","name":"run_code","arguments":"{\"code\":\"const a = await tools.bash({ command: 'echo CODE_ONE', description: 'Echo CODE_ONE' });\\nconst b = await tools.bash({ command: 'echo CODE_TWO', description: 'Echo CODE_TWO' });\\nconst out1 = a.kind === 'foreground' ? a.stdout.text.trim() : '';\\nconst out2 = b.kind === 'foreground' ? b.stdout.text.trim() : '';\\nHarness.inspect('captured output');\\nHarness.inspect(out1 + '+' + out2); return;\",\"description\":\"Run two bash echoes and join outputs\"}"}}, + {"type":"usage","usage":{"inputTokens":8689,"outputTokens":166,"totalTokens":8855,"cacheReadTokens":0,"reasoningTokens":0}}, + {"type":"finish","reason":{"kind":"tool-calls"}} + ] + }, + { + "kind": "chunks", + "chunks": [ + {"type":"block-start","index":0,"blockType":"text"}, + {"type":"block-end","index":0,"block":{"type":"text","text":"CODE_ONE+CODE_TWO"}}, + {"type":"usage","usage":{"inputTokens":174,"outputTokens":8,"totalTokens":8886,"cacheReadTokens":8704,"reasoningTokens":0}}, + {"type":"finish","reason":{"kind":"stop"}} + ] + } +] diff --git a/snapshots/session/mcp-empty-ptc/session.v4.jsonl b/snapshots/session/mcp-empty-ptc/session.v4.jsonl index 3a7bb8752d..cc9df69222 100644 --- a/snapshots/session/mcp-empty-ptc/session.v4.jsonl +++ b/snapshots/session/mcp-empty-ptc/session.v4.jsonl @@ -12,8 +12,8 @@ {"type":"request/header","data":{"header":{"config":{"provider":"deepseek-official","model":"deepseek-v4-flash","maxTokens":256000,"reasoningEffort":"max"},"adapterDefaults":{"reasoningEffort":true,"maxTokens":true},"tools":"{{tools}}"},"reason":"initial"}} {"type":"request/context","data":{"provider":"deepseek-official","model":"deepseek-v4-flash","contextWindow":1000000}} {"type":"session/title","data":{"title":"Using ONE run_code program: call","messageSeqs":[8],"source":{"kind":"fallback"}}} -{"type":"assistant/message","data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"tool-call","id":"call_00_ET_nCgG71pOO3KB4MYELUyr5350","name":"run_code","arguments":"{\"code\": \"const a = await tools.bash({ command: 'echo CODE_ONE', description: 'Echo CODE_ONE' });\\nconst b = await tools.bash({ command: 'echo CODE_TWO', description: 'Echo CODE_TWO' });\\nconst out1 = a.kind === 'foreground' ? a.stdout.text.trim() : '';\\nconst out2 = b.kind === 'foreground' ? b.stdout.text.trim() : '';\\nconsole.log('captured output');\\nreturn out1 + '+' + out2;\", \"description\": \"Run two bash echoes and join outputs\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:4}}"},"usage":{"inputTokens":8689,"outputTokens":166,"totalTokens":8855,"cacheReadTokens":0,"reasoningTokens":0},"stream":[{"type":"chunk","time":1791427845652,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}},{"type":"tool-call-chunks","time0":1791427845652,"index":0,"dt":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"id":"call_00_ET_nCgG71pOO3KB4MYELUyr5350","name":"run_code","args":["","{","\"","code","\"",": ","\"","const"," a"," ="," await"," tools",".b","ash","({"," command",":"," '","echo"," CODE","_","ONE","',"," description",":"," '","E","cho"," CODE","_","ONE","'"," });\\n","const"," b"," ="," await"," tools",".b","ash","({"," command",":"," '","echo"," CODE","_T","WO","',"," description",":"," '","E","cho"," CODE","_T","WO","'"," });\\n","const"," out","1"," ="," a",".k","ind"," ==="," '","fore","ground","'"," ?"," a",".stdout",".text",".trim","()"," :"," '';\\n","const"," out","2"," ="," b",".k","ind"," ==="," '","fore","ground","'"," ?"," b",".stdout",".text",".trim","()"," :"," '';\\n","console",".log","('","capt","ured"," output","');\\n","return"," out","1"," +"," '+'"," +"," out","2",";","\"",", ","\"","description","\"",": ","\"","Run"," two"," bash"," echoes"," and"," join"," outputs","\"","}"]},{"type":"chunk","time":1791427845652,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"call_00_ET_nCgG71pOO3KB4MYELUyr5350","name":"run_code","arguments":"{\"code\": \"const a = await tools.bash({ command: 'echo CODE_ONE', description: 'Echo CODE_ONE' });\\nconst b = await tools.bash({ command: 'echo CODE_TWO', description: 'Echo CODE_TWO' });\\nconst out1 = a.kind === 'foreground' ? a.stdout.text.trim() : '';\\nconst out2 = b.kind === 'foreground' ? b.stdout.text.trim() : '';\\nconsole.log('captured output');\\nreturn out1 + '+' + out2;\", \"description\": \"Run two bash echoes and join outputs\"}"}}},{"type":"chunk","time":1791427845652,"chunk":{"type":"usage","usage":{"inputTokens":8689,"outputTokens":166,"totalTokens":8855,"cacheReadTokens":0,"reasoningTokens":0}}},{"type":"chunk","time":1791427845652,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"} -{"type":"tool/call","data":{"turn":1,"step":1,"callId":"call_00_ET_nCgG71pOO3KB4MYELUyr5350","name":"run_code","arguments":"{\"code\": \"const a = await tools.bash({ command: 'echo CODE_ONE', description: 'Echo CODE_ONE' });\\nconst b = await tools.bash({ command: 'echo CODE_TWO', description: 'Echo CODE_TWO' });\\nconst out1 = a.kind === 'foreground' ? a.stdout.text.trim() : '';\\nconst out2 = b.kind === 'foreground' ? b.stdout.text.trim() : '';\\nconsole.log('captured output');\\nreturn out1 + '+' + out2;\", \"description\": \"Run two bash echoes and join outputs\"}"}} +{"type":"assistant/message","data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"tool-call","id":"call_00_ET_nCgG71pOO3KB4MYELUyr5350","name":"run_code","arguments":"{\"code\":\"const a = await tools.bash({ command: 'echo CODE_ONE', description: 'Echo CODE_ONE' });\\nconst b = await tools.bash({ command: 'echo CODE_TWO', description: 'Echo CODE_TWO' });\\nconst out1 = a.kind === 'foreground' ? a.stdout.text.trim() : '';\\nconst out2 = b.kind === 'foreground' ? b.stdout.text.trim() : '';\\nHarness.inspect('captured output');\\nHarness.inspect(out1 + '+' + out2); return;\",\"description\":\"Run two bash echoes and join outputs\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:4}}"},"usage":{"inputTokens":8689,"outputTokens":166,"totalTokens":8855,"cacheReadTokens":0,"reasoningTokens":0},"stream":[{"type":"chunk","time":1794195773902,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}},{"type":"chunk","time":1794195773902,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"call_00_ET_nCgG71pOO3KB4MYELUyr5350","name":"run_code","arguments":"{\"code\":\"const a = await tools.bash({ command: 'echo CODE_ONE', description: 'Echo CODE_ONE' });\\nconst b = await tools.bash({ command: 'echo CODE_TWO', description: 'Echo CODE_TWO' });\\nconst out1 = a.kind === 'foreground' ? a.stdout.text.trim() : '';\\nconst out2 = b.kind === 'foreground' ? b.stdout.text.trim() : '';\\nHarness.inspect('captured output');\\nHarness.inspect(out1 + '+' + out2); return;\",\"description\":\"Run two bash echoes and join outputs\"}"}}},{"type":"chunk","time":1794195773903,"chunk":{"type":"usage","usage":{"inputTokens":8689,"outputTokens":166,"totalTokens":8855,"cacheReadTokens":0,"reasoningTokens":0}}},{"type":"chunk","time":1794195773903,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"} +{"type":"tool/call","data":{"turn":1,"step":1,"callId":"call_00_ET_nCgG71pOO3KB4MYELUyr5350","name":"run_code","arguments":"{\"code\":\"const a = await tools.bash({ command: 'echo CODE_ONE', description: 'Echo CODE_ONE' });\\nconst b = await tools.bash({ command: 'echo CODE_TWO', description: 'Echo CODE_TWO' });\\nconst out1 = a.kind === 'foreground' ? a.stdout.text.trim() : '';\\nconst out2 = b.kind === 'foreground' ? b.stdout.text.trim() : '';\\nHarness.inspect('captured output');\\nHarness.inspect(out1 + '+' + out2); return;\",\"description\":\"Run two bash echoes and join outputs\"}"}} {"type":"tool/ptc-dispatch-start","data":{"rootCallId":"call_00_ET_nCgG71pOO3KB4MYELUyr5350","parentCallId":"call_00_ET_nCgG71pOO3KB4MYELUyr5350","subCallId":"call_00_ET_nCgG71pOO3KB4MYELUyr5350:ptc:1","name":"bash","arguments":{"command":"echo CODE_ONE","description":"Echo CODE_ONE"}}} {"type":"job/registered","data":{"jobId":"{{id:1}}","owner":"{{session:1}}","kind":"bash","label":"echo CODE_ONE","startedAt":1791427845736,"toolCall":{"callId":"call_00_ET_nCgG71pOO3KB4MYELUyr5350:ptc:1","rootCallId":"call_00_ET_nCgG71pOO3KB4MYELUyr5350"}}} {"type":"job/settled","data":{"jobId":"{{id:1}}","owner":"{{session:1}}","kind":"bash","label":"echo CODE_ONE","startedAt":1791427845736,"toolCall":{"callId":"call_00_ET_nCgG71pOO3KB4MYELUyr5350:ptc:1","rootCallId":"call_00_ET_nCgG71pOO3KB4MYELUyr5350"},"finishedAt":1791427845738,"status":"completed","cause":"producer","producerReported":true,"detail":"exit code: 0"}} @@ -25,6 +25,6 @@ {"type":"tool/result","data":{"turn":1,"step":1,"message":{"role":"tool","source":{"kind":"tool","callId":"call_00_ET_nCgG71pOO3KB4MYELUyr5350"},"toolCallId":"call_00_ET_nCgG71pOO3KB4MYELUyr5350","content":[{"type":"text","text":"captured output\nCODE_ONE+CODE_TWO"}],"isError":false,"id":"{{message:5}}"}},"sourceEventSeqs":[14],"surfaceOp":"append"} {"type":"step/end","data":{"turn":1,"step":1}} {"type":"step/start","data":{"turn":1,"step":2}} -{"type":"assistant/message","data":{"turn":1,"step":2,"message":{"role":"assistant","content":[{"type":"text","text":"CODE_ONE+CODE_TWO"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:6}}"},"usage":{"inputTokens":174,"outputTokens":8,"totalTokens":8886,"cacheReadTokens":8704,"reasoningTokens":0},"stream":[{"type":"chunk","time":1791427845777,"chunk":{"type":"block-start","index":0,"blockType":"text"}},{"type":"text-chunks","time0":1791427845777,"index":0,"dt":[0,0,0,0,0,0],"texts":["CODE","_","ONE","+","CODE","_T","WO"]},{"type":"chunk","time":1791427845777,"chunk":{"type":"block-end","index":0,"block":{"type":"text","text":"CODE_ONE+CODE_TWO"}}},{"type":"chunk","time":1791427845777,"chunk":{"type":"usage","usage":{"inputTokens":174,"outputTokens":8,"totalTokens":8886,"cacheReadTokens":8704,"reasoningTokens":0}}},{"type":"chunk","time":1791427845777,"chunk":{"type":"finish","reason":{"kind":"stop"}}}]},"surfaceOp":"append"} +{"type":"assistant/message","data":{"turn":1,"step":2,"message":{"role":"assistant","content":[{"type":"text","text":"CODE_ONE+CODE_TWO"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:6}}"},"usage":{"inputTokens":174,"outputTokens":8,"totalTokens":8886,"cacheReadTokens":8704,"reasoningTokens":0},"stream":[{"type":"chunk","time":1794195774161,"chunk":{"type":"block-start","index":0,"blockType":"text"}},{"type":"chunk","time":1794195774161,"chunk":{"type":"block-end","index":0,"block":{"type":"text","text":"CODE_ONE+CODE_TWO"}}},{"type":"chunk","time":1794195774162,"chunk":{"type":"usage","usage":{"inputTokens":174,"outputTokens":8,"totalTokens":8886,"cacheReadTokens":8704,"reasoningTokens":0}}},{"type":"chunk","time":1794195774162,"chunk":{"type":"finish","reason":{"kind":"stop"}}}]},"surfaceOp":"append"} {"type":"step/end","data":{"turn":1,"step":2}} {"type":"turn/end","data":{"turn":1,"reason":{"kind":"completed"}}} diff --git a/snapshots/session/mcp-empty-ptc/snapshot.yml b/snapshots/session/mcp-empty-ptc/snapshot.yml index 1f7c3645b1..26becd2a74 100644 --- a/snapshots/session/mcp-empty-ptc/snapshot.yml +++ b/snapshots/session/mcp-empty-ptc/snapshot.yml @@ -8,3 +8,5 @@ header: pin: true systemPromptSource: ptc-turn toolSchemasSource: ptc-turn +replay: + override: true diff --git a/snapshots/session/mcp-resources-ptc/replay.override.json b/snapshots/session/mcp-resources-ptc/replay.override.json new file mode 100644 index 0000000000..c2518d7a6f --- /dev/null +++ b/snapshots/session/mcp-resources-ptc/replay.override.json @@ -0,0 +1,24 @@ +[ + { + "kind": "chunks", + "chunks": [ + {"type":"block-start","index":0,"blockType":"text"}, + {"type":"block-start","index":1,"blockType":"tool-call"}, + {"type":"block-end","index":0,"block":{"type":"text","text":"I'll query the catalog server's resources and templates, then read the three memo resources."}}, + {"type":"block-end","index":1,"block":{"type":"tool-call","id":"call_00_ET_jWtEEO7HWPCVcCNJHI8s6062","name":"run_code","arguments":"{\"code\":\"\\nconst [resources, templates] = await Promise.all([\\n tools.list_mcp_resources({ server: \\\"catalog\\\" }),\\n tools.list_mcp_resource_templates({ server: \\\"catalog\\\" }),\\n]);\\n\\nconst text = await tools.read_mcp_resource({ server: \\\"catalog\\\", uri: \\\"memo://text\\\" });\\nconst binary = await tools.read_mcp_resource({ server: \\\"catalog\\\", uri: \\\"memo://binary\\\" });\\nconst greeting = await tools.read_mcp_resource({ server: \\\"catalog\\\", uri: \\\"memo://greeting/reader\\\" });\\n\\n// Inspect binary contents without exposing blob data.\\nconst contents = (binary as any)?.contents;\\nconst first = Array.isArray(contents) ? contents[0] : undefined;\\nconst binaryAvailable = typeof first?.blob === \\\"string\\\" && first.blob.length > 0;\\n\\nHarness.inspect(JSON.stringify({\\n resources,\\n templates,\\n text,\\n greeting,\\n binarySummary: {\\n uri: first?.uri,\\n mimeType: first?.mimeType,\\n blobIsNonemptyString: binaryAvailable,\\n },\\n binaryAvailable,\\n}, null, 2)); return;\\n\",\"description\":\"List catalog MCP resources and read three memos\"}"}}, + {"type":"usage","usage":{"inputTokens":8736,"outputTokens":314,"totalTokens":9050,"cacheReadTokens":0,"reasoningTokens":0}}, + {"type":"finish","reason":{"kind":"tool-calls"}} + ] + }, + { + "kind": "chunks", + "chunks": [ + {"type":"block-start","index":0,"blockType":"reasoning"}, + {"type":"block-start","index":1,"blockType":"text"}, + {"type":"block-end","index":0,"block":{"type":"reasoning","text":"The task is done. Report results concisely and reply MCP_RESOURCES_PTC_DONE.\n\nNote: blob data not printed. Return the text and template results."}}, + {"type":"block-end","index":1,"block":{"type":"text","text":"Catalog server results:\n\n**Resources** (`memo://text`, `memo://binary`):\n- `memo://text` — \"Deterministic text memo.\" (text/plain)\n- `memo://binary` — \"Binary content for programmatic callers.\" (application/octet-stream)\n\n**Resource templates**:\n- `greeting` → `memo://greeting/{name}` — \"A greeting for the named reader.\" (text/plain)\n\n**Reads**:\n- `memo://text`: `MCP resource text with {{braces}} intact.` (braces kept literal)\n- `memo://greeting/reader`: `Hello, reader.`\n- `memo://binary`: blob verified in-program as a nonempty string; `binaryAvailable: true`. Blob data was neither printed nor returned.\n\nMCP_RESOURCES_PTC_DONE"}}, + {"type":"usage","usage":{"inputTokens":659,"outputTokens":221,"totalTokens":9840,"cacheReadTokens":8960,"reasoningTokens":35}}, + {"type":"finish","reason":{"kind":"stop"}} + ] + } +] diff --git a/snapshots/session/mcp-resources-ptc/session.v4.jsonl b/snapshots/session/mcp-resources-ptc/session.v4.jsonl index 73cb45d7fc..f387029483 100644 --- a/snapshots/session/mcp-resources-ptc/session.v4.jsonl +++ b/snapshots/session/mcp-resources-ptc/session.v4.jsonl @@ -12,8 +12,8 @@ {"type":"request/header","data":{"header":{"config":{"provider":"deepseek-official","model":"deepseek-v4-flash","maxTokens":256000,"reasoningEffort":"max"},"adapterDefaults":{"reasoningEffort":true,"maxTokens":true},"tools":"{{tools}}"},"reason":"initial"}} {"type":"request/context","data":{"provider":"deepseek-official","model":"deepseek-v4-flash","contextWindow":1000000}} {"type":"session/title","data":{"title":"Use run_code to call tools.list_mcp_reso","messageSeqs":[8],"source":{"kind":"fallback"}}} -{"type":"assistant/message","data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"text","text":"I'll query the catalog server's resources and templates, then read the three memo resources."},{"type":"tool-call","id":"call_00_ET_jWtEEO7HWPCVcCNJHI8s6062","name":"run_code","arguments":"{\"code\": \"\\nconst [resources, templates] = await Promise.all([\\n tools.list_mcp_resources({ server: \\\"catalog\\\" }),\\n tools.list_mcp_resource_templates({ server: \\\"catalog\\\" }),\\n]);\\n\\nconst text = await tools.read_mcp_resource({ server: \\\"catalog\\\", uri: \\\"memo://text\\\" });\\nconst binary = await tools.read_mcp_resource({ server: \\\"catalog\\\", uri: \\\"memo://binary\\\" });\\nconst greeting = await tools.read_mcp_resource({ server: \\\"catalog\\\", uri: \\\"memo://greeting/reader\\\" });\\n\\n// Inspect binary contents without exposing blob data.\\nconst contents = (binary as any)?.contents;\\nconst first = Array.isArray(contents) ? contents[0] : undefined;\\nconst binaryAvailable = typeof first?.blob === \\\"string\\\" && first.blob.length > 0;\\n\\nreturn {\\n resources,\\n templates,\\n text,\\n greeting,\\n binarySummary: {\\n uri: first?.uri,\\n mimeType: first?.mimeType,\\n blobIsNonemptyString: binaryAvailable,\\n },\\n binaryAvailable,\\n};\\n\", \"description\": \"List catalog MCP resources and read three memos\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:4}}"},"usage":{"inputTokens":8736,"outputTokens":314,"totalTokens":9050,"cacheReadTokens":0,"reasoningTokens":0},"stream":[{"type":"chunk","time":1789200549995,"chunk":{"type":"block-start","index":0,"blockType":"text"}},{"type":"text-chunks","time0":1789200549995,"index":0,"dt":[14,0,18,0,7,13,0,0,0,25,0,0,12,0,0,13,0],"texts":["I","'ll"," query"," the"," catalog"," server","'s"," resources"," and"," templates",","," then"," read"," the"," three"," memo"," resources","."]},{"type":"chunk","time":1789200550120,"chunk":{"type":"block-start","index":1,"blockType":"tool-call"}},{"type":"tool-call-chunks","time0":1789200550120,"index":1,"dt":[15,1,0,0,0,13,0,15,0,10,0,0,0,0,0,73,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,0,0,0,0,0,11,0,0,0,0,20,0,0,0,0,0,10,1,0,0,0,0,12,1,0,0,0,0,9,0,0,0,0,0,22,0,0,0,0,0,14,0,0,0,0,0,15,0,0,0,0,0,17,1,0,0,0,0,12,1,0,0,0,0,13,0,0,0,0,0,13,0,0,0,0,0,15,0,0,0,0,0,12,0,18,0,10,0,0,14,16,0,0,0,0,14,15,0,0,0,12,0,0,0,0,0,16,0,0,0,0,10,0,0,0,0,1,13,0,0,0,0,0,15,0,0,0,0,0,12,0,0,0,0,0,18,0,0,0,0,0,10,0,0,0,0,15,0,0,0,0,0,12,0,1,12,0,0,0,13,0,0,0,16,0,11,0,0,0,0,0,14,1,0,0,0,0,13,0,0,0,0,18,10,1,0,0,14,0,0,0,0,0,12,0,0,0,15,0,15,0,0,1,11,0,13,19,0,0,17,0,0,7,0,19],"id":"call_00_ET_jWtEEO7HWPCVcCNJHI8s6062","name":"run_code","args":["","{","\"","code","\"",": ","\"","\\n","const"," [","resources",","," templates","]"," ="," await"," Promise",".all","([\\n"," "," tools",".list","_m","cp","_res","ources","({"," server",":"," \\\"","c","atalog","\\\""," }),\\n"," "," tools",".list","_m","cp","_resource","_t","emplates","({"," server",":"," \\\"","c","atalog","\\\""," }),\\n","]);\\n\\n","const"," text"," ="," await"," tools",".read","_m","cp","_resource","({"," server",":"," \\\"","c","atalog","\\\","," uri",":"," \\\"","m","emo","://","text","\\\""," });\\n","const"," binary"," ="," await"," tools",".read","_m","cp","_resource","({"," server",":"," \\\"","c","atalog","\\\","," uri",":"," \\\"","m","emo","://","binary","\\\""," });\\n","const"," greeting"," ="," await"," tools",".read","_m","cp","_resource","({"," server",":"," \\\"","c","atalog","\\\","," uri",":"," \\\"","m","emo","://","gre","eting","/","reader","\\\""," });\\n\\n","//"," Insp","ect"," binary"," contents"," without"," exposing"," blob"," data",".\\n","const"," contents"," ="," (","binary"," as"," any",")","?.","contents",";\\n","const"," first"," ="," Array",".is","Array","(","contents",")"," ?"," contents","[","0","]"," :"," undefined",";\\n","const"," binary","Available"," ="," typeof"," first","?.","bl","ob"," ==="," \\\"","string","\\\""," &&"," first",".bl","ob",".length"," >"," ","0",";\\n\\n","return"," {\\n"," "," resources",",\\n"," "," templates",",\\n"," "," text",",\\n"," "," greeting",",\\n"," "," binary","Summary",":"," {\\n"," "," uri",":"," first","?.","uri",",\\n"," "," m","ime","Type",":"," first","?.","m","ime","Type",",\\n"," "," blob","Is","Non","empty","String",":"," binary","Available",",\\n"," "," },\\n"," "," binary","Available",",\\n","};\\n","\"",", ","\"","description","\"",": ","\"","List"," catalog"," M","CP"," resources"," and"," read"," three"," mem","os","\"","}"]},{"type":"chunk","time":1789200550951,"chunk":{"type":"block-end","index":0,"block":{"type":"text","text":"I'll query the catalog server's resources and templates, then read the three memo resources."}}},{"type":"chunk","time":1789200550952,"chunk":{"type":"block-end","index":1,"block":{"type":"tool-call","id":"call_00_ET_jWtEEO7HWPCVcCNJHI8s6062","name":"run_code","arguments":"{\"code\": \"\\nconst [resources, templates] = await Promise.all([\\n tools.list_mcp_resources({ server: \\\"catalog\\\" }),\\n tools.list_mcp_resource_templates({ server: \\\"catalog\\\" }),\\n]);\\n\\nconst text = await tools.read_mcp_resource({ server: \\\"catalog\\\", uri: \\\"memo://text\\\" });\\nconst binary = await tools.read_mcp_resource({ server: \\\"catalog\\\", uri: \\\"memo://binary\\\" });\\nconst greeting = await tools.read_mcp_resource({ server: \\\"catalog\\\", uri: \\\"memo://greeting/reader\\\" });\\n\\n// Inspect binary contents without exposing blob data.\\nconst contents = (binary as any)?.contents;\\nconst first = Array.isArray(contents) ? contents[0] : undefined;\\nconst binaryAvailable = typeof first?.blob === \\\"string\\\" && first.blob.length > 0;\\n\\nreturn {\\n resources,\\n templates,\\n text,\\n greeting,\\n binarySummary: {\\n uri: first?.uri,\\n mimeType: first?.mimeType,\\n blobIsNonemptyString: binaryAvailable,\\n },\\n binaryAvailable,\\n};\\n\", \"description\": \"List catalog MCP resources and read three memos\"}"}}},{"type":"chunk","time":1789200550952,"chunk":{"type":"usage","usage":{"inputTokens":8736,"outputTokens":314,"totalTokens":9050,"cacheReadTokens":0,"reasoningTokens":0}}},{"type":"chunk","time":1789200550952,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"} -{"type":"tool/call","data":{"turn":1,"step":1,"callId":"call_00_ET_jWtEEO7HWPCVcCNJHI8s6062","name":"run_code","arguments":"{\"code\": \"\\nconst [resources, templates] = await Promise.all([\\n tools.list_mcp_resources({ server: \\\"catalog\\\" }),\\n tools.list_mcp_resource_templates({ server: \\\"catalog\\\" }),\\n]);\\n\\nconst text = await tools.read_mcp_resource({ server: \\\"catalog\\\", uri: \\\"memo://text\\\" });\\nconst binary = await tools.read_mcp_resource({ server: \\\"catalog\\\", uri: \\\"memo://binary\\\" });\\nconst greeting = await tools.read_mcp_resource({ server: \\\"catalog\\\", uri: \\\"memo://greeting/reader\\\" });\\n\\n// Inspect binary contents without exposing blob data.\\nconst contents = (binary as any)?.contents;\\nconst first = Array.isArray(contents) ? contents[0] : undefined;\\nconst binaryAvailable = typeof first?.blob === \\\"string\\\" && first.blob.length > 0;\\n\\nreturn {\\n resources,\\n templates,\\n text,\\n greeting,\\n binarySummary: {\\n uri: first?.uri,\\n mimeType: first?.mimeType,\\n blobIsNonemptyString: binaryAvailable,\\n },\\n binaryAvailable,\\n};\\n\", \"description\": \"List catalog MCP resources and read three memos\"}"}} +{"type":"assistant/message","data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"text","text":"I'll query the catalog server's resources and templates, then read the three memo resources."},{"type":"tool-call","id":"call_00_ET_jWtEEO7HWPCVcCNJHI8s6062","name":"run_code","arguments":"{\"code\":\"\\nconst [resources, templates] = await Promise.all([\\n tools.list_mcp_resources({ server: \\\"catalog\\\" }),\\n tools.list_mcp_resource_templates({ server: \\\"catalog\\\" }),\\n]);\\n\\nconst text = await tools.read_mcp_resource({ server: \\\"catalog\\\", uri: \\\"memo://text\\\" });\\nconst binary = await tools.read_mcp_resource({ server: \\\"catalog\\\", uri: \\\"memo://binary\\\" });\\nconst greeting = await tools.read_mcp_resource({ server: \\\"catalog\\\", uri: \\\"memo://greeting/reader\\\" });\\n\\n// Inspect binary contents without exposing blob data.\\nconst contents = (binary as any)?.contents;\\nconst first = Array.isArray(contents) ? contents[0] : undefined;\\nconst binaryAvailable = typeof first?.blob === \\\"string\\\" && first.blob.length > 0;\\n\\nHarness.inspect(JSON.stringify({\\n resources,\\n templates,\\n text,\\n greeting,\\n binarySummary: {\\n uri: first?.uri,\\n mimeType: first?.mimeType,\\n blobIsNonemptyString: binaryAvailable,\\n },\\n binaryAvailable,\\n}, null, 2)); return;\\n\",\"description\":\"List catalog MCP resources and read three memos\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:4}}"},"usage":{"inputTokens":8736,"outputTokens":314,"totalTokens":9050,"cacheReadTokens":0,"reasoningTokens":0},"stream":[{"type":"chunk","time":1794196089002,"chunk":{"type":"block-start","index":0,"blockType":"text"}},{"type":"chunk","time":1794196089002,"chunk":{"type":"block-start","index":1,"blockType":"tool-call"}},{"type":"chunk","time":1794196089002,"chunk":{"type":"block-end","index":0,"block":{"type":"text","text":"I'll query the catalog server's resources and templates, then read the three memo resources."}}},{"type":"chunk","time":1794196089002,"chunk":{"type":"block-end","index":1,"block":{"type":"tool-call","id":"call_00_ET_jWtEEO7HWPCVcCNJHI8s6062","name":"run_code","arguments":"{\"code\":\"\\nconst [resources, templates] = await Promise.all([\\n tools.list_mcp_resources({ server: \\\"catalog\\\" }),\\n tools.list_mcp_resource_templates({ server: \\\"catalog\\\" }),\\n]);\\n\\nconst text = await tools.read_mcp_resource({ server: \\\"catalog\\\", uri: \\\"memo://text\\\" });\\nconst binary = await tools.read_mcp_resource({ server: \\\"catalog\\\", uri: \\\"memo://binary\\\" });\\nconst greeting = await tools.read_mcp_resource({ server: \\\"catalog\\\", uri: \\\"memo://greeting/reader\\\" });\\n\\n// Inspect binary contents without exposing blob data.\\nconst contents = (binary as any)?.contents;\\nconst first = Array.isArray(contents) ? contents[0] : undefined;\\nconst binaryAvailable = typeof first?.blob === \\\"string\\\" && first.blob.length > 0;\\n\\nHarness.inspect(JSON.stringify({\\n resources,\\n templates,\\n text,\\n greeting,\\n binarySummary: {\\n uri: first?.uri,\\n mimeType: first?.mimeType,\\n blobIsNonemptyString: binaryAvailable,\\n },\\n binaryAvailable,\\n}, null, 2)); return;\\n\",\"description\":\"List catalog MCP resources and read three memos\"}"}}},{"type":"chunk","time":1794196089002,"chunk":{"type":"usage","usage":{"inputTokens":8736,"outputTokens":314,"totalTokens":9050,"cacheReadTokens":0,"reasoningTokens":0}}},{"type":"chunk","time":1794196089002,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"} +{"type":"tool/call","data":{"turn":1,"step":1,"callId":"call_00_ET_jWtEEO7HWPCVcCNJHI8s6062","name":"run_code","arguments":"{\"code\":\"\\nconst [resources, templates] = await Promise.all([\\n tools.list_mcp_resources({ server: \\\"catalog\\\" }),\\n tools.list_mcp_resource_templates({ server: \\\"catalog\\\" }),\\n]);\\n\\nconst text = await tools.read_mcp_resource({ server: \\\"catalog\\\", uri: \\\"memo://text\\\" });\\nconst binary = await tools.read_mcp_resource({ server: \\\"catalog\\\", uri: \\\"memo://binary\\\" });\\nconst greeting = await tools.read_mcp_resource({ server: \\\"catalog\\\", uri: \\\"memo://greeting/reader\\\" });\\n\\n// Inspect binary contents without exposing blob data.\\nconst contents = (binary as any)?.contents;\\nconst first = Array.isArray(contents) ? contents[0] : undefined;\\nconst binaryAvailable = typeof first?.blob === \\\"string\\\" && first.blob.length > 0;\\n\\nHarness.inspect(JSON.stringify({\\n resources,\\n templates,\\n text,\\n greeting,\\n binarySummary: {\\n uri: first?.uri,\\n mimeType: first?.mimeType,\\n blobIsNonemptyString: binaryAvailable,\\n },\\n binaryAvailable,\\n}, null, 2)); return;\\n\",\"description\":\"List catalog MCP resources and read three memos\"}"}} {"type":"tool/ptc-dispatch-start","data":{"rootCallId":"call_00_ET_jWtEEO7HWPCVcCNJHI8s6062","parentCallId":"call_00_ET_jWtEEO7HWPCVcCNJHI8s6062","subCallId":"call_00_ET_jWtEEO7HWPCVcCNJHI8s6062:ptc:1","name":"list_mcp_resources","arguments":{"server":"catalog"}}} {"type":"tool/ptc-dispatch-start","data":{"rootCallId":"call_00_ET_jWtEEO7HWPCVcCNJHI8s6062","parentCallId":"call_00_ET_jWtEEO7HWPCVcCNJHI8s6062","subCallId":"call_00_ET_jWtEEO7HWPCVcCNJHI8s6062:ptc:2","name":"list_mcp_resource_templates","arguments":{"server":"catalog"}}} {"type":"tool/ptc-dispatch","data":{"rootCallId":"call_00_ET_jWtEEO7HWPCVcCNJHI8s6062","parentCallId":"call_00_ET_jWtEEO7HWPCVcCNJHI8s6062","subCallId":"call_00_ET_jWtEEO7HWPCVcCNJHI8s6062:ptc:1","name":"list_mcp_resources","arguments":{"server":"catalog"},"isError":false,"content":[{"type":"text","text":"MCP server: catalog\n{\"_meta\":{\"io.modelcontextprotocol/serverInfo\":{\"name\":\"snapshot-resources\",\"version\":\"1.0.0\"}},\"ttlMs\":0,\"cacheScope\":\"private\",\"resources\":[{\"name\":\"memo\",\"uri\":\"memo://text\",\"description\":\"Deterministic text memo.\",\"mimeType\":\"text/plain\"},{\"name\":\"binary\",\"uri\":\"memo://binary\",\"description\":\"Binary content for programmatic callers.\",\"mimeType\":\"application/octet-stream\"}]}"}]}} @@ -27,6 +27,6 @@ {"type":"tool/result","data":{"turn":1,"step":1,"message":{"role":"tool","source":{"kind":"tool","callId":"call_00_ET_jWtEEO7HWPCVcCNJHI8s6062"},"toolCallId":"call_00_ET_jWtEEO7HWPCVcCNJHI8s6062","content":[{"type":"text","text":"{\n \"resources\": {\n \"_meta\": {\n \"io.modelcontextprotocol/serverInfo\": {\n \"name\": \"snapshot-resources\",\n \"version\": \"1.0.0\"\n }\n },\n \"ttlMs\": 0,\n \"cacheScope\": \"private\",\n \"resources\": [\n {\n \"name\": \"memo\",\n \"uri\": \"memo://text\",\n \"description\": \"Deterministic text memo.\",\n \"mimeType\": \"text/plain\"\n },\n {\n \"name\": \"binary\",\n \"uri\": \"memo://binary\",\n \"description\": \"Binary content for programmatic callers.\",\n \"mimeType\": \"application/octet-stream\"\n }\n ]\n },\n \"templates\": {\n \"_meta\": {\n \"io.modelcontextprotocol/serverInfo\": {\n \"name\": \"snapshot-resources\",\n \"version\": \"1.0.0\"\n }\n },\n \"ttlMs\": 0,\n \"cacheScope\": \"private\",\n \"resourceTemplates\": [\n {\n \"name\": \"greeting\",\n \"uriTemplate\": \"memo://greeting/{name}\",\n \"description\": \"A greeting for the named reader.\",\n \"mimeType\": \"text/plain\"\n }\n ]\n },\n \"text\": {\n \"_meta\": {\n \"io.modelcontextprotocol/serverInfo\": {\n \"name\": \"snapshot-resources\",\n \"version\": \"1.0.0\"\n }\n },\n \"ttlMs\": 0,\n \"cacheScope\": \"private\",\n \"contents\": [\n {\n \"uri\": \"memo://text\",\n \"mimeType\": \"text/plain\",\n \"text\": \"MCP resource text with {{braces}} intact.\"\n }\n ]\n },\n \"greeting\": {\n \"_meta\": {\n \"io.modelcontextprotocol/serverInfo\": {\n \"name\": \"snapshot-resources\",\n \"version\": \"1.0.0\"\n }\n },\n \"ttlMs\": 0,\n \"cacheScope\": \"private\",\n \"contents\": [\n {\n \"uri\": \"memo://greeting/reader\",\n \"mimeType\": \"text/plain\",\n \"text\": \"Hello, reader.\"\n }\n ]\n },\n \"binarySummary\": {\n \"uri\": \"memo://binary\",\n \"mimeType\": \"application/octet-stream\",\n \"blobIsNonemptyString\": true\n },\n \"binaryAvailable\": true\n}"}],"isError":false,"id":"{{message:5}}"}},"sourceEventSeqs":[14],"surfaceOp":"append"} {"type":"step/end","data":{"turn":1,"step":1}} {"type":"step/start","data":{"turn":1,"step":2}} -{"type":"assistant/message","data":{"turn":1,"step":2,"message":{"role":"assistant","content":[{"type":"reasoning","text":"The task is done. Report results concisely and reply MCP_RESOURCES_PTC_DONE.\n\nNote: blob data not printed. Return the text and template results."},{"type":"text","text":"Catalog server results:\n\n**Resources** (`memo://text`, `memo://binary`):\n- `memo://text` — \"Deterministic text memo.\" (text/plain)\n- `memo://binary` — \"Binary content for programmatic callers.\" (application/octet-stream)\n\n**Resource templates**:\n- `greeting` → `memo://greeting/{name}` — \"A greeting for the named reader.\" (text/plain)\n\n**Reads**:\n- `memo://text`: `MCP resource text with {{braces}} intact.` (braces kept literal)\n- `memo://greeting/reader`: `Hello, reader.`\n- `memo://binary`: blob verified in-program as a nonempty string; `binaryAvailable: true`. Blob data was neither printed nor returned.\n\nMCP_RESOURCES_PTC_DONE"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:6}}"},"usage":{"inputTokens":659,"outputTokens":221,"totalTokens":9840,"cacheReadTokens":8960,"reasoningTokens":35},"stream":[{"type":"chunk","time":1789200551813,"chunk":{"type":"block-start","index":0,"blockType":"reasoning"}},{"type":"reasoning-chunks","time0":1789200551813,"index":0,"dt":[64,16,0,0,0,1,8,15,0,0,11,0,0,14,1,0,17,0,0,1,0,0,9,0,16,1,0,0,0,22,18,0,0,0],"texts":["The"," task"," is"," done","."," Report"," results"," conc","is","ely"," and"," reply"," M","CP","_RES","OURCES","_P","TC","_D","ONE",".\n\n","Note",":"," blob"," data"," not"," printed","."," Return"," the"," text"," and"," template"," results","."]},{"type":"chunk","time":1789200552028,"chunk":{"type":"block-start","index":1,"blockType":"text"}},{"type":"text-chunks","time0":1789200552028,"index":1,"dt":[7,10,11,0,0,16,9,0,0,15,0,12,0,0,0,26,0,0,0,1,0,0,0,0,0,12,0,0,1,8,1,0,0,13,0,0,1,0,122,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,8,0,0,0,0,0,0,0,0,0,0,0,25,1,0,0,0,9,0,0,0,0,1,11,0,0,0,0,9,0,19,0,0,0,6,0,0,0,1,0,12,0,0,0,20,0,0,0,0,0,7,0,0,0,0,0,11,0,0,0,0,0,17,0,0,0,0,0,14,15,18,11,0,0,0,0,22,0,0,0,0,0,0,0,0,0,15,0,0,10,1,0,19,1,0,0,11,0,0,0,0],"texts":["Catalog"," server"," results",":\n\n","**","Resources","**"," (`","m","emo","://","text","`,"," `","m","emo","://","binary","`","):\n","-"," `","m","emo","://","text","`"," —"," \"","Determ","in","istic"," text"," memo",".\""," (","text","/","plain",")\n","-"," `","m","emo","://","binary","`"," —"," \"","Binary"," content"," for"," program","matic"," call","ers",".\""," (","application","/","oct","et","-stream",")\n\n","**","Resource"," templates","**",":\n","-"," `","gre","eting","`"," →"," `","m","emo","://","gre","eting","/{","name","}`"," —"," \"","A"," greeting"," for"," the"," named"," reader",".\""," (","text","/","plain",")\n\n","**","Read","s","**",":\n","-"," `","m","emo","://","text","`:"," `","M","CP"," resource"," text"," with"," {{","bra","ces","}}"," intact",".","`"," (","bra","ces"," kept"," literal",")\n","-"," `","m","emo","://","gre","eting","/","reader","`:"," `","Hello",","," reader",".","`\n","-"," `","m","emo","://","binary","`:"," blob"," verified"," in","-program"," as"," a"," non","empty"," string",";"," `","binary","Available",":"," true","`."," Bl","ob"," data"," was"," neither"," printed"," nor"," returned",".\n\n","M","CP","_RES","OURCES","_P","TC","_D","ONE"]},{"type":"chunk","time":1789200552589,"chunk":{"type":"block-end","index":0,"block":{"type":"reasoning","text":"The task is done. Report results concisely and reply MCP_RESOURCES_PTC_DONE.\n\nNote: blob data not printed. Return the text and template results."}}},{"type":"chunk","time":1789200552589,"chunk":{"type":"block-end","index":1,"block":{"type":"text","text":"Catalog server results:\n\n**Resources** (`memo://text`, `memo://binary`):\n- `memo://text` — \"Deterministic text memo.\" (text/plain)\n- `memo://binary` — \"Binary content for programmatic callers.\" (application/octet-stream)\n\n**Resource templates**:\n- `greeting` → `memo://greeting/{name}` — \"A greeting for the named reader.\" (text/plain)\n\n**Reads**:\n- `memo://text`: `MCP resource text with {{braces}} intact.` (braces kept literal)\n- `memo://greeting/reader`: `Hello, reader.`\n- `memo://binary`: blob verified in-program as a nonempty string; `binaryAvailable: true`. Blob data was neither printed nor returned.\n\nMCP_RESOURCES_PTC_DONE"}}},{"type":"chunk","time":1789200552589,"chunk":{"type":"usage","usage":{"inputTokens":659,"outputTokens":221,"totalTokens":9840,"cacheReadTokens":8960,"reasoningTokens":35}}},{"type":"chunk","time":1789200552589,"chunk":{"type":"finish","reason":{"kind":"stop"}}}]},"surfaceOp":"append"} +{"type":"assistant/message","data":{"turn":1,"step":2,"message":{"role":"assistant","content":[{"type":"reasoning","text":"The task is done. Report results concisely and reply MCP_RESOURCES_PTC_DONE.\n\nNote: blob data not printed. Return the text and template results."},{"type":"text","text":"Catalog server results:\n\n**Resources** (`memo://text`, `memo://binary`):\n- `memo://text` — \"Deterministic text memo.\" (text/plain)\n- `memo://binary` — \"Binary content for programmatic callers.\" (application/octet-stream)\n\n**Resource templates**:\n- `greeting` → `memo://greeting/{name}` — \"A greeting for the named reader.\" (text/plain)\n\n**Reads**:\n- `memo://text`: `MCP resource text with {{braces}} intact.` (braces kept literal)\n- `memo://greeting/reader`: `Hello, reader.`\n- `memo://binary`: blob verified in-program as a nonempty string; `binaryAvailable: true`. Blob data was neither printed nor returned.\n\nMCP_RESOURCES_PTC_DONE"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:6}}"},"usage":{"inputTokens":659,"outputTokens":221,"totalTokens":9840,"cacheReadTokens":8960,"reasoningTokens":35},"stream":[{"type":"chunk","time":1794196089269,"chunk":{"type":"block-start","index":0,"blockType":"reasoning"}},{"type":"chunk","time":1794196089269,"chunk":{"type":"block-start","index":1,"blockType":"text"}},{"type":"chunk","time":1794196089269,"chunk":{"type":"block-end","index":0,"block":{"type":"reasoning","text":"The task is done. Report results concisely and reply MCP_RESOURCES_PTC_DONE.\n\nNote: blob data not printed. Return the text and template results."}}},{"type":"chunk","time":1794196089269,"chunk":{"type":"block-end","index":1,"block":{"type":"text","text":"Catalog server results:\n\n**Resources** (`memo://text`, `memo://binary`):\n- `memo://text` — \"Deterministic text memo.\" (text/plain)\n- `memo://binary` — \"Binary content for programmatic callers.\" (application/octet-stream)\n\n**Resource templates**:\n- `greeting` → `memo://greeting/{name}` — \"A greeting for the named reader.\" (text/plain)\n\n**Reads**:\n- `memo://text`: `MCP resource text with {{braces}} intact.` (braces kept literal)\n- `memo://greeting/reader`: `Hello, reader.`\n- `memo://binary`: blob verified in-program as a nonempty string; `binaryAvailable: true`. Blob data was neither printed nor returned.\n\nMCP_RESOURCES_PTC_DONE"}}},{"type":"chunk","time":1794196089269,"chunk":{"type":"usage","usage":{"inputTokens":659,"outputTokens":221,"totalTokens":9840,"cacheReadTokens":8960,"reasoningTokens":35}}},{"type":"chunk","time":1794196089269,"chunk":{"type":"finish","reason":{"kind":"stop"}}}]},"surfaceOp":"append"} {"type":"step/end","data":{"turn":1,"step":2}} {"type":"turn/end","data":{"turn":1,"reason":{"kind":"completed"}}} diff --git a/snapshots/session/mcp-resources-ptc/snapshot.yml b/snapshots/session/mcp-resources-ptc/snapshot.yml index 0df34bff2c..568344bef3 100644 --- a/snapshots/session/mcp-resources-ptc/snapshot.yml +++ b/snapshots/session/mcp-resources-ptc/snapshot.yml @@ -7,3 +7,5 @@ header: class: mcp-resources-ptc pin: true toolSchemasSource: ptc-turn +replay: + override: true diff --git a/snapshots/session/mcp-resources-ptc/system-prompt.expected.md b/snapshots/session/mcp-resources-ptc/system-prompt.expected.md index 4e7f0f4ab6..40d2b77f16 100644 --- a/snapshots/session/mcp-resources-ptc/system-prompt.expected.md +++ b/snapshots/session/mcp-resources-ptc/system-prompt.expected.md @@ -47,20 +47,24 @@ MCP_RESOURCE_INSTRUCTION: keep {{braces}} literal. Read resources from the catal `run_code` takes two required arguments: `code`, the body of an async TypeScript function (erasable syntax only — no `enum` or namespaces; type annotations are advisory, the code runs type-stripped), and `description`, a short summary of what the program does. The declarations below are SDK bindings for this program. A declaration does not make its name a directly callable tool; only names supplied as separate tool schemas may be called directly. When no separate `bash` schema is supplied, invoke a declared `bash` binding inside `run_code`: -`run_code({ code: "return await tools.bash({ description: 'Show current directory', command: 'pwd' })", description: "Show current directory" })` +`run_code({ code: "Harness.inspect(await tools.bash({ description: 'Show current directory', command: 'pwd' }))", description: "Show current directory" })` Inside the program: - Call tools as `await tools.name(args)` — quoted access for exotic names: `tools["my-tool"](args)`. Every call resolves to the tool's typed canonical JSON value. Tool arguments must be lossless JSON. - A FAILED tool call rejects with `ToolCallError`, whose `toolName` identifies the failed tool and whose `message` is human-readable — `try/catch` it to handle and continue. - Independent read-only calls MAY overlap under `Promise.all` (safe calls run concurrently; mutating calls run alone, in submission order). Sequence dependent work with `await`. -- Emit results with `return` and/or `console.log(...)`. Only what you print or return is program output. A successful tool result containing an image is attached after the run so you can inspect it on the next step; every other intermediate result stays out of the conversation, so extract just what you need. +- Show selected results with `Harness.inspect(...values)`. Return values and final expressions are not displayed. A successful tool result containing an image is attached after the run so you can inspect it on the next step; every other intermediate result stays out of the conversation, so extract just what you need. Program-only SDK bindings: ```ts type JsonValue = null | boolean | number | string | JsonValue[] | { [key: string]: JsonValue } +declare namespace Harness { + function inspect(...values: unknown[]): void; +} + interface ToolArgsMap { /** Create, update, delete, or move UTF-8 text files with one patch; prefer it over multiple edit calls when a change spans several files or separated regions. */ apply_patch: { diff --git a/snapshots/session/office-progressive-ptc/replay.override.json b/snapshots/session/office-progressive-ptc/replay.override.json new file mode 100644 index 0000000000..a8950e518f --- /dev/null +++ b/snapshots/session/office-progressive-ptc/replay.override.json @@ -0,0 +1,27 @@ +[ + { + "kind": "chunks", + "chunks": [ + {"type":"block-start","index":0,"blockType":"tool-call"}, + {"type":"block-end","index":0,"block":{"type":"tool-call","id":"call_office_skill","name":"run_code","arguments":"{\"code\":\"Harness.inspect(await tools.skill({\\\"name\\\":\\\"office\\\"}));\",\"description\":\"Load task instructions\"}"}}, + {"type":"finish","reason":{"kind":"tool-calls"}} + ] + }, + { + "kind": "chunks", + "chunks": [ + {"type":"block-start","index":0,"blockType":"tool-call"}, + {"type":"block-end","index":0,"block":{"type":"tool-call","id":"call_office_2","name":"run_code","arguments":"{\"code\":\"Harness.inspect(await tools.skill({\\\"name\\\":\\\"office-docx\\\"}));\",\"description\":\"Load task instructions\"}"}}, + {"type":"finish","reason":{"kind":"tool-calls"}} + ] + }, + { + "kind": "chunks", + "chunks": [ + {"type":"block-start","index":0,"blockType":"text"}, + {"type":"block-end","index":0,"block":{"type":"text","text":"DONE"}}, + {"type":"usage","usage":{"inputTokens":12,"outputTokens":2,"cacheReadTokens":10}}, + {"type":"finish","reason":{"kind":"stop"}} + ] + } +] diff --git a/snapshots/session/office-progressive-ptc/session.v4.jsonl b/snapshots/session/office-progressive-ptc/session.v4.jsonl index aaad022fd4..268a378143 100644 --- a/snapshots/session/office-progressive-ptc/session.v4.jsonl +++ b/snapshots/session/office-progressive-ptc/session.v4.jsonl @@ -13,8 +13,8 @@ {"type":"request/header","data":{"header":{"config":{"provider":"deepseek-official","model":"deepseek-v4-flash"},"tools":"{{tools}}"},"reason":"initial"}} {"type":"request/context","data":{"provider":"deepseek-official","model":"deepseek-v4-flash"}} {"type":"session/title","data":{"title":"Load the office router, then","messageSeqs":[8],"source":{"kind":"fallback"}}} -{"type":"assistant/message","data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"tool-call","id":"call_office_skill","name":"run_code","arguments":"{\"code\":\"console.log(await tools.skill({\\\"name\\\":\\\"office\\\"}));\",\"description\":\"Load task instructions\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:5}}"},"stream":[{"type":"chunk","time":1793628045463,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}},{"type":"tool-call-chunks","time0":1793628045463,"index":0,"dt":[],"id":"call_office_skill","name":"run_code","args":["{\"code\":\"console.log(await tools.skill({\\\"name\\\":\\\"office\\\"}));\",\"description\":\"Load task instructions\"}"]},{"type":"chunk","time":1793628045463,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"call_office_skill","name":"run_code","arguments":"{\"code\":\"console.log(await tools.skill({\\\"name\\\":\\\"office\\\"}));\",\"description\":\"Load task instructions\"}"}}},{"type":"chunk","time":1793628045463,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"} -{"type":"tool/call","data":{"turn":1,"step":1,"callId":"call_office_skill","name":"run_code","arguments":"{\"code\":\"console.log(await tools.skill({\\\"name\\\":\\\"office\\\"}));\",\"description\":\"Load task instructions\"}"}} +{"type":"assistant/message","data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"tool-call","id":"call_office_skill","name":"run_code","arguments":"{\"code\":\"Harness.inspect(await tools.skill({\\\"name\\\":\\\"office\\\"}));\",\"description\":\"Load task instructions\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:5}}"},"stream":[{"type":"chunk","time":1794195776174,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}},{"type":"chunk","time":1794195776174,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"call_office_skill","name":"run_code","arguments":"{\"code\":\"Harness.inspect(await tools.skill({\\\"name\\\":\\\"office\\\"}));\",\"description\":\"Load task instructions\"}"}}},{"type":"chunk","time":1794195776174,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"} +{"type":"tool/call","data":{"turn":1,"step":1,"callId":"call_office_skill","name":"run_code","arguments":"{\"code\":\"Harness.inspect(await tools.skill({\\\"name\\\":\\\"office\\\"}));\",\"description\":\"Load task instructions\"}"}} {"type":"tool/ptc-dispatch-start","data":{"rootCallId":"call_office_skill","parentCallId":"call_office_skill","subCallId":"call_office_skill:ptc:1","name":"skill","arguments":{"name":"office"}}} {"type":"tool/ptc-dispatch","data":{"rootCallId":"call_office_skill","parentCallId":"call_office_skill","subCallId":"call_office_skill:ptc:1","name":"skill","arguments":{"name":"office"},"isError":false,"content":[{"type":"text","text":"\n\nBase directory for this skill: {{cwd}}/office-skills/office\nResolve relative paths mentioned by this skill against the base directory before using them. Load referenced resources only as needed.\n\n\n\n# Office task routing\n\nLoad `office-docx`, `office-pptx`, or `office-xlsx` to create, read, or edit; operations alone need no format skill.\n\nIf `office_render`, `office_convert`, or `office_recalculate` is not in your tools, load `office` with `skill`; they appear next step.\n\n- `office_render`: Office or PDF images, visual checks, and appearance or slide checks required by the format skill; skip it when asked.\n- `office_recalculate`: formulas, with `format: \"xlsx\"` or `\"ods\"`.\n- `office_convert` with `format`; Office inputs only, not PDFs. For PDF export alone, check the copy is nonempty and starts with `%PDF-`, then deliver it without rendering.\n\nCopy temporary outputs to the destination, keeping existing files unless asked to replace them. Reuse loaded skills.\n\n"}]}} {"type":"tool/result","data":{"turn":1,"step":1,"message":{"role":"tool","source":{"kind":"tool","callId":"call_office_skill"},"toolCallId":"call_office_skill","content":[{"type":"text","text":"{\n name: 'office',\n provider: 'dsh-office',\n resourceBase: {\n kind: 'directory',\n path: '{{cwd}}/office-skills/office'\n },\n content: '# Office task routing\\n' +\n '\\n' +\n 'Load `office-docx`, `office-pptx`, or `office-xlsx` to create, read, or edit; operations alone need no format skill.\\n' +\n '\\n' +\n 'If `office_render`, `office_convert`, or `office_recalculate` is not in your tools, load `office` with `skill`; they appear next step.\\n' +\n '\\n' +\n '- `office_render`: Office or PDF images, visual checks, and appearance or slide checks required by the format skill; skip it when asked.\\n' +\n '- `office_recalculate`: formulas, with `format: \"xlsx\"` or `\"ods\"`.\\n' +\n '- `office_convert` with `format`; Office inputs only, not PDFs. For PDF export alone, check the copy is nonempty and starts with `%PDF-`, then deliver it without rendering.\\n' +\n '\\n' +\n 'Copy temporary outputs to the destination, keeping existing files unless asked to replace them. Reuse loaded skills.'\n}"}],"isError":false,"id":"{{message:6}}"}},"sourceEventSeqs":[15],"surfaceOp":"append"} @@ -22,13 +22,13 @@ {"type":"step/start","data":{"turn":1,"step":2}} {"type":"system/message","data":{"turn":1,"step":2,"message":{"role":"system","content":[{"type":"text","text":"{{system}}"}],"source":{"kind":"system-prompt"},"id":"{{message:7}}"}},"sourceEventSeqs":[7],"surfaceOp":{"op":"replace","startSeq":7,"endSeq":7}} {"type":"request/header","data":{"header":{"config":{"provider":"deepseek-official","model":"deepseek-v4-flash"},"tools":"{{tools}}"},"reason":"series"}} -{"type":"assistant/message","data":{"turn":1,"step":2,"message":{"role":"assistant","content":[{"type":"tool-call","id":"call_office_2","name":"run_code","arguments":"{\"code\":\"console.log(await tools.skill({\\\"name\\\":\\\"office-docx\\\"}));\",\"description\":\"Load task instructions\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:8}}"},"stream":[{"type":"chunk","time":1793628046138,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}},{"type":"tool-call-chunks","time0":1793628046138,"index":0,"dt":[],"id":"call_office_2","name":"run_code","args":["{\"code\":\"console.log(await tools.skill({\\\"name\\\":\\\"office-docx\\\"}));\",\"description\":\"Load task instructions\"}"]},{"type":"chunk","time":1793628046138,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"call_office_2","name":"run_code","arguments":"{\"code\":\"console.log(await tools.skill({\\\"name\\\":\\\"office-docx\\\"}));\",\"description\":\"Load task instructions\"}"}}},{"type":"chunk","time":1793628046138,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"} -{"type":"tool/call","data":{"turn":1,"step":2,"callId":"call_office_2","name":"run_code","arguments":"{\"code\":\"console.log(await tools.skill({\\\"name\\\":\\\"office-docx\\\"}));\",\"description\":\"Load task instructions\"}"}} +{"type":"assistant/message","data":{"turn":1,"step":2,"message":{"role":"assistant","content":[{"type":"tool-call","id":"call_office_2","name":"run_code","arguments":"{\"code\":\"Harness.inspect(await tools.skill({\\\"name\\\":\\\"office-docx\\\"}));\",\"description\":\"Load task instructions\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:8}}"},"stream":[{"type":"chunk","time":1794195777197,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}},{"type":"chunk","time":1794195777197,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"call_office_2","name":"run_code","arguments":"{\"code\":\"Harness.inspect(await tools.skill({\\\"name\\\":\\\"office-docx\\\"}));\",\"description\":\"Load task instructions\"}"}}},{"type":"chunk","time":1794195777197,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"} +{"type":"tool/call","data":{"turn":1,"step":2,"callId":"call_office_2","name":"run_code","arguments":"{\"code\":\"Harness.inspect(await tools.skill({\\\"name\\\":\\\"office-docx\\\"}));\",\"description\":\"Load task instructions\"}"}} {"type":"tool/ptc-dispatch-start","data":{"rootCallId":"call_office_2","parentCallId":"call_office_2","subCallId":"call_office_2:ptc:1","name":"skill","arguments":{"name":"office-docx"}}} {"type":"tool/ptc-dispatch","data":{"rootCallId":"call_office_2","parentCallId":"call_office_2","subCallId":"call_office_2:ptc:1","name":"skill","arguments":{"name":"office-docx"},"isError":false,"content":[{"type":"text","text":"\n\nBase directory for this skill: {{cwd}}/office-skills/office-docx\nResolve relative paths mentioned by this skill against the base directory before using them. Load referenced resources only as needed.\n\n\n\n# Word documents\n\nUse `python-docx` for DOCX creation and ordinary edits unless an explicit user or applicable AGENTS.md requirement names another library. Run Python as described in the Python environment section below.\n\nKeep scripts, intermediate files, and final documents in the workspace; runtime and skill resources are read-only. Use the requested language and preserve the document's design unless a redesign is requested.\n\nName Python scripts for their task, such as `edit_docx.py`; avoid module names such as `inspect.py`, `docx.py`, or `zipfile.py` that shadow imported libraries.\n\n## Create and edit\n\nIf `office_render`, `office_convert`, or `office_recalculate` is not in your tools, load `office` with `skill`; they appear next step.\n\npython-docx cannot read legacy `.doc` files. Use `office_convert` with `format: \"docx\"`, copy the result into the workspace, then edit the copy.\n\nFor text edits, locate the target by its text or position and inspect only its runs and formatting. A title can be an ordinary paragraph without a Title style or document-property title. Update the relevant runs; assigning paragraph `.text` destroys run formatting. Save a new file unless an in-place edit is requested. For ordinary text edits, do not unzip the document, enumerate ZIP members, dump raw XML, or inspect core properties, headers, or footers unrelated to the target. Investigate these only for a concrete failure, an ambiguous target, an unsupported feature, or an explicit user requirement. Rebuilding a document can lose unsupported features.\n\nUse paragraph styles for headings and body text. Size tables for the section that contains them, and account for merged cells and nested tables. Chinese, Japanese, and Korean text may need an explicit `w:eastAsia` font assignment in addition to `run.font.name`; font names alone do not establish glyph availability or rendered appearance.\n\nFor a new document, use the selected Python executable:\n\n```python\nfrom docx import Document\n\ndocument = Document()\ndocument.add_heading(\"Project report\", level=0)\ndocument.add_paragraph(\"Summary\", style=\"Heading 1\")\ndocument.add_paragraph(\"The requested findings go here.\")\ndocument.save(\"report.docx\")\n```\n\npython-docx does not paginate or render documents. Do not represent ordinary replacement, colored text, or comments as tracked changes. When real revisions or unsupported OOXML features matter, preserve their package parts and verify the requested operation rather than silently discarding them. When writing package parts directly, create a new ZIP and copy each unchanged member once; appending a replacement member duplicates its name and corrupts the package.\n\n## Check and deliver\n\nReopen the saved document with python-docx and verify the requested content, related formatting, and unchanged content that must be preserved. These checks complete routine validation; deliver the file once they pass. ZIP integrity, package inventories, and metadata matter only for a concrete failure.\n\nCall `office_render` on the changed page when the user asks for images or visual inspection, or when styling, font color, spacing, sizing, or text alignment is the requested edit; a request to skip rendering takes precedence. New documents and content edits, with incidental basic formatting, are checked with python-docx alone. For PDF output, call `office_convert` with `format: \"pdf\"`, then copy the returned temporary file to the destination.\n\nA fully transparent or uniformly blank image of populated content means the renderer failed, which ends visual checks for the task and leaves the source as it is. When content and structure checks pass, state the visual status only as \"Content and structure checks passed; visual inspection was not completed.\" or \"内容与结构检查通过;视觉检查未完成。\".\n\nCall `present({\"files\":[{\"path\":\"report.docx\"}]})` with the actual final DOCX path. It exposes the current file without copying or preserving its bytes; keep it in place and omit temporary QA reports unless requested. If `present` is unavailable, deliver the final workspace path with the session's supported method.\n\n## Python environment\n\nFollow an explicit user or applicable AGENTS.md requirement for a Python environment. Otherwise call `load_workspace_dependencies` and run scripts with its Python executable and bundled libraries. Do not install packages or search for a system Python for the default workflow. If the tool is unavailable, use an already configured environment and report a missing dependency only when it prevents the requested operation.\n\n"}]}} {"type":"tool/result","data":{"turn":1,"step":2,"message":{"role":"tool","source":{"kind":"tool","callId":"call_office_2"},"toolCallId":"call_office_2","content":[{"type":"text","text":"{\n name: 'office-docx',\n provider: 'dsh-office',\n resourceBase: {\n kind: 'directory',\n path: '{{cwd}}/office-skills/office-docx'\n },\n content: '# Word documents\\n' +\n '\\n' +\n 'Use `python-docx` for DOCX creation and ordinary edits unless an explicit user or applicable AGENTS.md requirement names another library. Run Python as described in the Python environment section below.\\n' +\n '\\n' +\n \"Keep scripts, intermediate files, and final documents in the workspace; runtime and skill resources are read-only. Use the requested language and preserve the document's design unless a redesign is requested.\\n\" +\n '\\n' +\n 'Name Python scripts for their task, such as `edit_docx.py`; avoid module names such as `inspect.py`, `docx.py`, or `zipfile.py` that shadow imported libraries.\\n' +\n '\\n' +\n '## Create and edit\\n' +\n '\\n' +\n 'If `office_render`, `office_convert`, or `office_recalculate` is not in your tools, load `office` with `skill`; they appear next step.\\n' +\n '\\n' +\n 'python-docx cannot read legacy `.doc` files. Use `office_convert` with `format: \"docx\"`, copy the result into the workspace, then edit the copy.\\n' +\n '\\n' +\n 'For text edits, locate the target by its text or position and inspect only its runs and formatting. A title can be an ordinary paragraph without a Title style or document-property title. Update the relevant runs; assigning paragraph `.text` destroys run formatting. Save a new file unless an in-place edit is requested. For ordinary text edits, do not unzip the document, enumerate ZIP members, dump raw XML, or inspect core properties, headers, or footers unrelated to the target. Investigate these only for a concrete failure, an ambiguous target, an unsupported feature, or an explicit user requirement. Rebuilding a document can lose unsupported features.\\n' +\n '\\n' +\n 'Use paragraph styles for headings and body text. Size tables for the section that contains them, and account for merged cells and nested tables. Chinese, Japanese, and Korean text may need an explicit `w:eastAsia` font assignment in addition to `run.font.name`; font names alone do not establish glyph availability or rendered appearance.\\n' +\n '\\n' +\n 'For a new document, use the selected Python executable:\\n' +\n '\\n' +\n '```python\\n' +\n 'from docx import Document\\n' +\n '\\n' +\n 'document = Document()\\n' +\n 'document.add_heading(\"Project report\", level=0)\\n' +\n 'document.add_paragraph(\"Summary\", style=\"Heading 1\")\\n' +\n 'document.add_paragraph(\"The requested findings go here.\")\\n' +\n 'document.save(\"report.docx\")\\n' +\n '```\\n' +\n '\\n' +\n 'python-docx does not paginate or render documents. Do not represent ordinary replacement, colored text, or comments as tracked changes. When real revisions or unsupported OOXML features matter, preserve their package parts and verify the requested operation rather than silently discarding them. When writing package parts directly, create a new ZIP and copy each unchanged member once; appending a replacement member duplicates its name and corrupts the package.\\n' +\n '\\n' +\n '## Check and deliver\\n' +\n '\\n' +\n 'Reopen the saved document with python-docx and verify the requested content, related formatting, and unchanged content that must be preserved. These checks complete routine validation; deliver the file once they pass. ZIP integrity, package inventories, and metadata matter only for a concrete failure.\\n' +\n '\\n' +\n 'Call `office_render` on the changed page when the user asks for images or visual inspection, or when styling, font color, spacing, sizing, or text alignment is the requested edit; a request to skip rendering takes precedence. New documents and content edits, with incidental basic formatting, are checked with python-docx alone. For PDF output, call `office_convert` with `format: \"pdf\"`, then copy the returned temporary file to the destination.\\n' +\n '\\n' +\n 'A fully transparent or uniformly blank image of populated content means the renderer failed, which ends visual checks for the task and leaves the source as it is. When content and structure checks pass, state the visual status only as \"Content and structure checks passed; visual inspection was not completed.\" or \"内容与结构检查通过;视觉检查未完成。\".\\n' +\n '\\n' +\n 'Call `present({\"files\":[{\"path\":\"report.docx\"}]})` with the actual final DOCX path. It exposes the current file without copying or preserving its bytes; keep it in place and omit temporary QA reports unless requested. If `present` is unavailable, deliver the final workspace path with the session\\'s supported method.\\n' +\n '\\n' +\n '## Python environment\\n' +\n '\\n' +\n 'Follow an explicit user or applicable AGENTS.md requirement for a Python environment. Otherwise call `load_workspace_dependencies` and run scripts with its Python executable and bundled libraries. Do not install packages or search for a system Python for the default workflow. If the tool is unavailable, use an already configured environment and report a missing dependency only when it prevents the requested operation.'\n}"}],"isError":false,"id":"{{message:9}}"}},"sourceEventSeqs":[24],"surfaceOp":"append"} {"type":"step/end","data":{"turn":1,"step":2}} {"type":"step/start","data":{"turn":1,"step":3}} -{"type":"assistant/message","data":{"turn":1,"step":3,"message":{"role":"assistant","content":[{"type":"text","text":"DONE"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:10}}"},"usage":{"inputTokens":12,"outputTokens":2,"cacheReadTokens":10},"stream":[{"type":"chunk","time":1793628046253,"chunk":{"type":"block-start","index":0,"blockType":"text"}},{"type":"text-chunks","time0":1793628046253,"index":0,"dt":[],"texts":["DONE"]},{"type":"chunk","time":1793628046253,"chunk":{"type":"block-end","index":0,"block":{"type":"text","text":"DONE"}}},{"type":"chunk","time":1793628046253,"chunk":{"type":"usage","usage":{"inputTokens":12,"outputTokens":2,"cacheReadTokens":10}}},{"type":"chunk","time":1793628046253,"chunk":{"type":"finish","reason":{"kind":"stop"}}}]},"surfaceOp":"append"} +{"type":"assistant/message","data":{"turn":1,"step":3,"message":{"role":"assistant","content":[{"type":"text","text":"DONE"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:10}}"},"usage":{"inputTokens":12,"outputTokens":2,"cacheReadTokens":10},"stream":[{"type":"chunk","time":1794195777349,"chunk":{"type":"block-start","index":0,"blockType":"text"}},{"type":"chunk","time":1794195777349,"chunk":{"type":"block-end","index":0,"block":{"type":"text","text":"DONE"}}},{"type":"chunk","time":1794195777349,"chunk":{"type":"usage","usage":{"inputTokens":12,"outputTokens":2,"cacheReadTokens":10}}},{"type":"chunk","time":1794195777349,"chunk":{"type":"finish","reason":{"kind":"stop"}}}]},"surfaceOp":"append"} {"type":"step/end","data":{"turn":1,"step":3}} {"type":"turn/end","data":{"turn":1,"reason":{"kind":"completed"}}} diff --git a/snapshots/session/office-progressive-ptc/snapshot.yml b/snapshots/session/office-progressive-ptc/snapshot.yml index 0563ee7396..af226cdead 100644 --- a/snapshots/session/office-progressive-ptc/snapshot.yml +++ b/snapshots/session/office-progressive-ptc/snapshot.yml @@ -11,3 +11,5 @@ header: promptChanges: 1 workspace: setup: office-retired-ptc +replay: + override: true diff --git a/snapshots/session/office-progressive-ptc/system-prompt.expected.md b/snapshots/session/office-progressive-ptc/system-prompt.expected.md index f93624c469..ceb5a25629 100644 --- a/snapshots/session/office-progressive-ptc/system-prompt.expected.md +++ b/snapshots/session/office-progressive-ptc/system-prompt.expected.md @@ -42,13 +42,17 @@ Inside the program: - Call tools as `await tools.name(args)` — quoted access for exotic names: `tools["my-tool"](args)`. Every call resolves to the tool's typed canonical JSON value. Tool arguments must be lossless JSON. - A FAILED tool call rejects with `ToolCallError`, whose `toolName` identifies the failed tool and whose `message` is human-readable — `try/catch` it to handle and continue. - Independent read-only calls MAY overlap under `Promise.all` (safe calls run concurrently; mutating calls run alone, in submission order). Sequence dependent work with `await`. -- Emit results with `return` and/or `console.log(...)`. Only what you print or return is program output. A successful tool result containing an image is attached after the run so you can inspect it on the next step; every other intermediate result stays out of the conversation, so extract just what you need. +- Show selected results with `Harness.inspect(...values)`. Return values and final expressions are not displayed. A successful tool result containing an image is attached after the run so you can inspect it on the next step; every other intermediate result stays out of the conversation, so extract just what you need. Program-only SDK bindings: ```ts type JsonValue = null | boolean | number | string | JsonValue[] | { [key: string]: JsonValue } +declare namespace Harness { + function inspect(...values: unknown[]): void; +} + interface ToolArgsMap { /** Create, update, delete, or move UTF-8 text files with one patch; prefer it over multiple edit calls when a change spans several files or separated regions. */ apply_patch: { @@ -566,13 +570,17 @@ Inside the program: - Call tools as `await tools.name(args)` — quoted access for exotic names: `tools["my-tool"](args)`. Every call resolves to the tool's typed canonical JSON value. Tool arguments must be lossless JSON. - A FAILED tool call rejects with `ToolCallError`, whose `toolName` identifies the failed tool and whose `message` is human-readable — `try/catch` it to handle and continue. - Independent read-only calls MAY overlap under `Promise.all` (safe calls run concurrently; mutating calls run alone, in submission order). Sequence dependent work with `await`. -- Emit results with `return` and/or `console.log(...)`. Only what you print or return is program output. A successful tool result containing an image is attached after the run so you can inspect it on the next step; every other intermediate result stays out of the conversation, so extract just what you need. +- Show selected results with `Harness.inspect(...values)`. Return values and final expressions are not displayed. A successful tool result containing an image is attached after the run so you can inspect it on the next step; every other intermediate result stays out of the conversation, so extract just what you need. Program-only SDK bindings: ```ts type JsonValue = null | boolean | number | string | JsonValue[] | { [key: string]: JsonValue } +declare namespace Harness { + function inspect(...values: unknown[]): void; +} + interface ToolArgsMap { /** Create, update, delete, or move UTF-8 text files with one patch; prefer it over multiple edit calls when a change spans several files or separated regions. */ apply_patch: { diff --git a/snapshots/session/office-progressive-ptc/tool-schemas.expected.json b/snapshots/session/office-progressive-ptc/tool-schemas.expected.json index 90ac75c304..ae11cd9398 100644 --- a/snapshots/session/office-progressive-ptc/tool-schemas.expected.json +++ b/snapshots/session/office-progressive-ptc/tool-schemas.expected.json @@ -2,7 +2,7 @@ "initial": [ { "name": "run_code", - "description": "Execute a TypeScript program against the available tools. Takes two required arguments: `code`, the BODY of an async function (erasable syntax only; top-level `await` and `return` work), and `description`, a short summary of what the program does. Call tools as `await tools.name(args)` per the declarations in the system prompt. Only what you print or return is program output — curate it. Image-bearing subtool results are attached after the run. The program is the body of an async TypeScript function; only erasable syntax is accepted, with no enum or namespaces. Type annotations are advisory. Top-level await and return are supported. Emit output with console.log(...) or return a lossless JSON value. Only printed values and the returned value become program output. Each call runs in a fresh Node process. Node APIs are available through await import(...). Relative paths use the supplied working directory; process.env starts empty. Direct filesystem access follows this execution's sandbox policy. Each program starts in the Session's current directory. Running programs keep their initial directory. A sandbox escalation approves this complete program for one execution only. Nested tools retain their own policies and approvals. Request wider access only after evidence of a denial. Earlier effects may already have completed: inspect them before explicitly retrying. Programs are never replayed automatically.", + "description": "Execute a TypeScript program against the available tools. Takes two required arguments: `code`, the BODY of an async function (erasable syntax only; top-level `await` and `return` work), and `description`, a short summary of what the program does. Call tools as `await tools.name(args)` per the declarations in the system prompt. Show selected results with Harness.inspect(...values). Return values are not displayed. Image-bearing subtool results are attached after the run. The program is the body of an async TypeScript function; only erasable syntax is accepted, with no enum or namespaces. Type annotations are advisory. Top-level await and return are supported. Console is unavailable. Use Harness.inspect(...values) for output. Each call runs in a fresh Node process. Node APIs are available through await import(...). Relative paths use the supplied working directory; process.env starts empty. Direct filesystem access follows this execution's sandbox policy. Each program starts in the Session's current directory. Running programs keep their initial directory. A sandbox escalation approves this complete program for one execution only. Nested tools retain their own policies and approvals. Request wider access only after evidence of a denial. Earlier effects may already have completed: inspect them before explicitly retrying. Programs are never replayed automatically.", "parameters": { "type": "object", "properties": { @@ -42,7 +42,7 @@ [ { "name": "run_code", - "description": "Execute a TypeScript program against the available tools. Takes two required arguments: `code`, the BODY of an async function (erasable syntax only; top-level `await` and `return` work), and `description`, a short summary of what the program does. Call tools as `await tools.name(args)` per the declarations in the system prompt. Only what you print or return is program output — curate it. Image-bearing subtool results are attached after the run. The program is the body of an async TypeScript function; only erasable syntax is accepted, with no enum or namespaces. Type annotations are advisory. Top-level await and return are supported. Emit output with console.log(...) or return a lossless JSON value. Only printed values and the returned value become program output. Each call runs in a fresh Node process. Node APIs are available through await import(...). Relative paths use the supplied working directory; process.env starts empty. Direct filesystem access follows this execution's sandbox policy. Each program starts in the Session's current directory. Running programs keep their initial directory. A sandbox escalation approves this complete program for one execution only. Nested tools retain their own policies and approvals. Request wider access only after evidence of a denial. Earlier effects may already have completed: inspect them before explicitly retrying. Programs are never replayed automatically.", + "description": "Execute a TypeScript program against the available tools. Takes two required arguments: `code`, the BODY of an async function (erasable syntax only; top-level `await` and `return` work), and `description`, a short summary of what the program does. Call tools as `await tools.name(args)` per the declarations in the system prompt. Show selected results with Harness.inspect(...values). Return values are not displayed. Image-bearing subtool results are attached after the run. The program is the body of an async TypeScript function; only erasable syntax is accepted, with no enum or namespaces. Type annotations are advisory. Top-level await and return are supported. Console is unavailable. Use Harness.inspect(...values) for output. Each call runs in a fresh Node process. Node APIs are available through await import(...). Relative paths use the supplied working directory; process.env starts empty. Direct filesystem access follows this execution's sandbox policy. Each program starts in the Session's current directory. Running programs keep their initial directory. A sandbox escalation approves this complete program for one execution only. Nested tools retain their own policies and approvals. Request wider access only after evidence of a denial. Earlier effects may already have completed: inspect them before explicitly retrying. Programs are never replayed automatically.", "parameters": { "type": "object", "properties": { diff --git a/snapshots/session/office-render-tools-ptc/replay.override.json b/snapshots/session/office-render-tools-ptc/replay.override.json new file mode 100644 index 0000000000..05f2af4e43 --- /dev/null +++ b/snapshots/session/office-render-tools-ptc/replay.override.json @@ -0,0 +1,43 @@ +[ + { + "kind": "chunks", + "chunks": [ + {"type":"block-start","index":0,"blockType":"tool-call"}, + {"type":"block-end","index":0,"block":{"type":"tool-call","id":"call_office_skill","name":"run_code","arguments":"{\"code\":\"Harness.inspect(await tools.skill({\\\"name\\\":\\\"office-xlsx\\\"}));\",\"description\":\"Use the Office workflow\"}"}}, + {"type":"finish","reason":{"kind":"tool-calls"}} + ] + }, + { + "kind": "chunks", + "chunks": [ + {"type":"block-start","index":0,"blockType":"tool-call"}, + {"type":"block-end","index":0,"block":{"type":"tool-call","id":"call_office_2","name":"run_code","arguments":"{\"code\":\"Harness.inspect(await tools.office_tools({\\\"action\\\":\\\"load\\\"}));\",\"description\":\"Use the Office workflow\"}"}}, + {"type":"finish","reason":{"kind":"tool-calls"}} + ] + }, + { + "kind": "chunks", + "chunks": [ + {"type":"block-start","index":0,"blockType":"tool-call"}, + {"type":"block-end","index":0,"block":{"type":"tool-call","id":"call_office_3","name":"run_code","arguments":"{\"code\":\"Harness.inspect(await tools.render_document({\\\"input\\\":\\\"report.docx\\\",\\\"outputDirectory\\\":\\\"preview\\\",\\\"pages\\\":[1]}));\",\"description\":\"Use the Office workflow\"}"}}, + {"type":"finish","reason":{"kind":"tool-calls"}} + ] + }, + { + "kind": "chunks", + "chunks": [ + {"type":"block-start","index":0,"blockType":"tool-call"}, + {"type":"block-end","index":0,"block":{"type":"tool-call","id":"call_office_4","name":"run_code","arguments":"{\"code\":\"Harness.inspect(await tools.office_tools({\\\"action\\\":\\\"remove\\\"}));\",\"description\":\"Use the Office workflow\"}"}}, + {"type":"finish","reason":{"kind":"tool-calls"}} + ] + }, + { + "kind": "chunks", + "chunks": [ + {"type":"block-start","index":0,"blockType":"text"}, + {"type":"block-end","index":0,"block":{"type":"text","text":"DONE"}}, + {"type":"usage","usage":{"inputTokens":12,"outputTokens":2,"cacheReadTokens":10}}, + {"type":"finish","reason":{"kind":"stop"}} + ] + } +] diff --git a/snapshots/session/office-render-tools-ptc/snapshot.yml b/snapshots/session/office-render-tools-ptc/snapshot.yml index 2113f42163..d456828e44 100644 --- a/snapshots/session/office-render-tools-ptc/snapshot.yml +++ b/snapshots/session/office-render-tools-ptc/snapshot.yml @@ -12,3 +12,5 @@ header: class: office-render-tools-ptc workspace: setup: office-retired-ptc +replay: + override: true diff --git a/snapshots/session/office-render-tools-ptc/writer.expected.jsonl b/snapshots/session/office-render-tools-ptc/writer.expected.jsonl index 2ad549a672..84ada87cb6 100644 --- a/snapshots/session/office-render-tools-ptc/writer.expected.jsonl +++ b/snapshots/session/office-render-tools-ptc/writer.expected.jsonl @@ -14,8 +14,8 @@ {"type":"request/header","data":{"header":{"config":{"provider":"deepseek-official","model":"deepseek-v4-flash"},"tools":"{{tools}}"},"reason":"initial"}} {"type":"request/context","data":{"provider":"deepseek-official","model":"deepseek-v4-flash"}} {"type":"session/title","data":{"title":"/office-docx Load the Office rendering","messageSeqs":[8],"source":{"kind":"fallback"}}} -{"type":"assistant/message","data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"tool-call","id":"call_office_skill","name":"run_code","arguments":"{\"code\":\"console.log(await tools.skill({\\\"name\\\":\\\"office-xlsx\\\"}));\",\"description\":\"Use the Office workflow\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:6}}"},"stream":[{"type":"chunk","time":1793793370810,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}},{"type":"tool-call-chunks","time0":1793793370811,"index":0,"dt":[],"id":"call_office_skill","name":"run_code","args":["{\"code\":\"console.log(await tools.skill({\\\"name\\\":\\\"office-xlsx\\\"}));\",\"description\":\"Use the Office workflow\"}"]},{"type":"chunk","time":1793793370811,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"call_office_skill","name":"run_code","arguments":"{\"code\":\"console.log(await tools.skill({\\\"name\\\":\\\"office-xlsx\\\"}));\",\"description\":\"Use the Office workflow\"}"}}},{"type":"chunk","time":1793793370811,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"} -{"type":"tool/call","data":{"turn":1,"step":1,"callId":"call_office_skill","name":"run_code","arguments":"{\"code\":\"console.log(await tools.skill({\\\"name\\\":\\\"office-xlsx\\\"}));\",\"description\":\"Use the Office workflow\"}"}} +{"type":"assistant/message","data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"tool-call","id":"call_office_skill","name":"run_code","arguments":"{\"code\":\"Harness.inspect(await tools.skill({\\\"name\\\":\\\"office-xlsx\\\"}));\",\"description\":\"Use the Office workflow\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:6}}"},"stream":[{"type":"chunk","time":1794195899120,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}},{"type":"chunk","time":1794195899121,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"call_office_skill","name":"run_code","arguments":"{\"code\":\"Harness.inspect(await tools.skill({\\\"name\\\":\\\"office-xlsx\\\"}));\",\"description\":\"Use the Office workflow\"}"}}},{"type":"chunk","time":1794195899121,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"} +{"type":"tool/call","data":{"turn":1,"step":1,"callId":"call_office_skill","name":"run_code","arguments":"{\"code\":\"Harness.inspect(await tools.skill({\\\"name\\\":\\\"office-xlsx\\\"}));\",\"description\":\"Use the Office workflow\"}"}} {"type":"tool/ptc-dispatch-start","data":{"rootCallId":"call_office_skill","parentCallId":"call_office_skill","subCallId":"call_office_skill:ptc:1","name":"skill","arguments":{"name":"office-xlsx"}}} {"type":"tool/ptc-dispatch","data":{"rootCallId":"call_office_skill","parentCallId":"call_office_skill","subCallId":"call_office_skill:ptc:1","name":"skill","arguments":{"name":"office-xlsx"},"isError":false,"content":[{"type":"text","text":"\n\nBase directory for this skill: {{cwd}}/office-skills/office-xlsx\nResolve relative paths mentioned by this skill against the base directory before using them. Load referenced resources only as needed.\n\n\n\n# Excel workbooks\n\nUse `openpyxl` and `pandas` unless an explicit user or applicable AGENTS.md requirement names another library. Run Python as described in the Python environment section below.\n\nKeep scripts, working files, and outputs in the task workspace. The runtime and skill directory are shared read-only resources. Save to a new workbook unless the user requests an in-place edit.\n\nName Python scripts for their task, such as `edit_xlsx.py`; avoid module names such as `inspect.py`, `openpyxl.py`, or `pandas.py` that shadow imported libraries.\n\n## Read and modify\n\nIf `office_render`, `office_convert`, or `office_recalculate` is not in your tools, load `office` with `skill`; they appear next step.\n\nopenpyxl cannot read legacy `.xls` files. Use `office_convert` with `format: \"xlsx\"`, copy the result into the workspace, then edit the copy.\n\nUse `openpyxl` for existing XLSX workbooks and targeted cell or style changes. Load with `data_only=False` when formulas must survive. Inspect sheet names, the affected cell types, formulas, styles, merged ranges, and referenced ranges before editing. Check the saved file by reopening it, including unchanged content that the request requires preserving.\n\nUse pandas for data analysis and transformations. A DataFrame is not the workbook: exporting it over an existing file can lose sheets, formulas, charts, and formatting. Write analysis results back to the intended ranges with openpyxl. `XlsxWriter` can create new workbooks, but cannot read or modify existing ones.\n\nFor a new workbook, the selected Python environment can write editable values, styles, and formulas directly:\n\n```python\nfrom openpyxl import Workbook\nfrom openpyxl.styles import Font\n\nworkbook = Workbook()\nsheet = workbook.active\nsheet.title = \"Revenue\"\nfor row in [(\"Quarter\", \"Revenue\"), (\"Q1\", 12), (\"Q2\", 18), (\"Total\", \"=SUM(B2:B3)\")]:\n sheet.append(row)\nfor cell in sheet[1]:\n cell.font = Font(bold=True)\nsheet.column_dimensions[\"A\"].width = 18\nsheet.column_dimensions[\"B\"].width = 18\nworkbook.save(\"report.xlsx\")\n```\n\nPreserve numbers, dates, booleans, and identifiers as the intended cell types; formatting is not a type conversion. For modifications, load the existing file with `openpyxl.load_workbook(\"input.xlsx\", data_only=False)`, change the requested ranges, and save a separate result. Avoid a DataFrame round trip when workbook features must survive.\n\nWriting a formula does not calculate its result. openpyxl and XlsxWriter do not evaluate formulas; `data_only=True` returns cached values that may be absent or stale. When calculated results must be refreshed, call `office_recalculate` with `format: \"xlsx\"`, and copy the returned temporary workbook to a new file in the workspace. Open the copy with openpyxl using both `data_only=False` and `data_only=True` to check that formulas survive and cached values match the expected results. If recalculation fails, preserve the original workbook and report that calculated values were not refreshed. Do not replace requested formulas with constants or report stale or absent cached values as newly calculated results.\n\nDo not rename `.xls`, `.xlsb`, encrypted files, or macro-enabled files to `.xlsx`. They need an appropriate supported operation. `keep_vba=True` can preserve VBA package content in an XLSM workflow, but does not execute or edit macros. Preserve the original and verify each workbook feature the task depends on.\n\n## Check and deliver\n\nReopen the saved workbook with openpyxl and verify the requested sheet names, values, types, formulas, styles, and totals against the source data, including unchanged content that must be preserved. These checks do not calculate formulas or judge workbook appearance.\n\nCall `office_render` when the user asks for images or visual inspection, or when the task targets appearance, such as cell fill colors, text alignment, spacing, or sizing; a request to skip rendering takes precedence. Do not render new workbooks or data or formula edits merely to verify accompanying bold headers or number formats; check these with openpyxl alone.\n\nRender worksheets by `sheet` and the affected `range`; worksheet numbers are not PDF page numbers. Earlier images of an unchanged source remain current. A fully transparent or uniformly blank image of populated content means the renderer failed, which ends visual checks for the task and leaves the source as it is. When content and structure checks pass, state the visual status only as \"Content and structure checks passed; visual inspection was not completed.\" or \"内容与结构检查通过;视觉检查未完成。\".\n\nRecalculate when refreshed calculated values are needed. For PDF output, call `office_convert` with `format: \"pdf\"`, then copy the returned temporary file to the destination. Deliver after the requested data checks pass and the required operations finish.\n\nCall `present({\"files\":[{\"path\":\"report.xlsx\"}]})` with the actual final workbook path. It exposes the current source file without copying or preserving its bytes, so keep that file in place and omit intermediate scripts and reports unless requested. If `present` is unavailable, use the session's supported file delivery method.\n\n## Python environment\n\nFollow an explicit user or applicable AGENTS.md requirement for a Python environment. Otherwise call `load_workspace_dependencies` and run scripts with its Python executable and bundled libraries. Do not install packages or search for a system Python for the default workflow. If the tool is unavailable, use an already configured environment and report a missing dependency only when it prevents the requested operation.\n\n"}]}} {"type":"tool/result","data":{"turn":1,"step":1,"message":{"role":"tool","source":{"kind":"tool","callId":"call_office_skill"},"toolCallId":"call_office_skill","content":[{"type":"text","text":"{\n name: 'office-xlsx',\n provider: 'dsh-office',\n resourceBase: {\n kind: 'directory',\n path: '{{cwd}}/office-skills/office-xlsx'\n },\n content: '# Excel workbooks\\n' +\n '\\n' +\n 'Use `openpyxl` and `pandas` unless an explicit user or applicable AGENTS.md requirement names another library. Run Python as described in the Python environment section below.\\n' +\n '\\n' +\n 'Keep scripts, working files, and outputs in the task workspace. The runtime and skill directory are shared read-only resources. Save to a new workbook unless the user requests an in-place edit.\\n' +\n '\\n' +\n 'Name Python scripts for their task, such as `edit_xlsx.py`; avoid module names such as `inspect.py`, `openpyxl.py`, or `pandas.py` that shadow imported libraries.\\n' +\n '\\n' +\n '## Read and modify\\n' +\n '\\n' +\n 'If `office_render`, `office_convert`, or `office_recalculate` is not in your tools, load `office` with `skill`; they appear next step.\\n' +\n '\\n' +\n 'openpyxl cannot read legacy `.xls` files. Use `office_convert` with `format: \"xlsx\"`, copy the result into the workspace, then edit the copy.\\n' +\n '\\n' +\n 'Use `openpyxl` for existing XLSX workbooks and targeted cell or style changes. Load with `data_only=False` when formulas must survive. Inspect sheet names, the affected cell types, formulas, styles, merged ranges, and referenced ranges before editing. Check the saved file by reopening it, including unchanged content that the request requires preserving.\\n' +\n '\\n' +\n 'Use pandas for data analysis and transformations. A DataFrame is not the workbook: exporting it over an existing file can lose sheets, formulas, charts, and formatting. Write analysis results back to the intended ranges with openpyxl. `XlsxWriter` can create new workbooks, but cannot read or modify existing ones.\\n' +\n '\\n' +\n 'For a new workbook, the selected Python environment can write editable values, styles, and formulas directly:\\n' +\n '\\n' +\n '```python\\n' +\n 'from openpyxl import Workbook\\n' +\n 'from openpyxl.styles import Font\\n' +\n '\\n' +\n 'workbook = Workbook()\\n' +\n 'sheet = workbook.active\\n' +\n 'sheet.title = \"Revenue\"\\n' +\n 'for row in [(\"Quarter\", \"Revenue\"), (\"Q1\", 12), (\"Q2\", 18), (\"Total\", \"=SUM(B2:B3)\")]:\\n' +\n ' sheet.append(row)\\n' +\n 'for cell in sheet[1]:\\n' +\n ' cell.font = Font(bold=True)\\n' +\n 'sheet.column_dimensions[\"A\"].width = 18\\n' +\n 'sheet.column_dimensions[\"B\"].width = 18\\n' +\n 'workbook.save(\"report.xlsx\")\\n' +\n '```\\n' +\n '\\n' +\n 'Preserve numbers, dates, booleans, and identifiers as the intended cell types; formatting is not a type conversion. For modifications, load the existing file with `openpyxl.load_workbook(\"input.xlsx\", data_only=False)`, change the requested ranges, and save a separate result. Avoid a DataFrame round trip when workbook features must survive.\\n' +\n '\\n' +\n 'Writing a formula does not calculate its result. openpyxl and XlsxWriter do not evaluate formulas; `data_only=True` returns cached values that may be absent or stale. When calculated results must be refreshed, call `office_recalculate` with `format: \"xlsx\"`, and copy the returned temporary workbook to a new file in the workspace. Open the copy with openpyxl using both `data_only=False` and `data_only=True` to check that formulas survive and cached values match the expected results. If recalculation fails, preserve the original workbook and report that calculated values were not refreshed. Do not replace requested formulas with constants or report stale or absent cached values as newly calculated results.\\n' +\n '\\n' +\n 'Do not rename `.xls`, `.xlsb`, encrypted files, or macro-enabled files to `.xlsx`. They need an appropriate supported operation. `keep_vba=True` can preserve VBA package content in an XLSM workflow, but does not execute or edit macros. Preserve the original and verify each workbook feature the task depends on.\\n' +\n '\\n' +\n '## Check and deliver\\n' +\n '\\n' +\n 'Reopen the saved workbook with openpyxl and verify the requested sheet names, values, types, formulas, styles, and totals against the source data, including unchanged content that must be preserved. These checks do not calculate formulas or judge workbook appearance.\\n' +\n '\\n' +\n 'Call `office_render` when the user asks for images or visual inspection, or when the task targets appearance, such as cell fill colors, text alignment, spacing, or sizing; a request to skip rendering takes precedence. Do not render new workbooks or data or formula edits merely to verify accompanying bold headers or number formats; check these with openpyxl alone.\\n' +\n '\\n' +\n 'Render worksheets by `sheet` and the affected `range`; worksheet numbers are not PDF page numbers. Earlier images of an unchanged source remain current. A fully transparent or uniformly blank image of populated content means the renderer failed, which ends visual checks for the task and leaves the source as it is. When content and structure checks pass, state the visual status only as \"Content and structure checks passed; visual inspection was not completed.\" or \"内容与结构检查通过;视觉检查未完成。\".\\n' +\n '\\n' +\n 'Recalculate when refreshed calculated values are needed. For PDF output, call `office_convert` with `format: \"pdf\"`, then copy the returned temporary file to the destination. Deliver after the requested data checks pass and the required operations finish.\\n' +\n '\\n' +\n 'Call `present({\"files\":[{\"path\":\"report.xlsx\"}]})` with the actual final workbook path. It exposes the current source file without copying or preserving its bytes, so keep that file in place and omit intermediate scripts and reports unless requested. If `present` is unavailable, use the session\\'s supported file delivery method.\\n' +\n '\\n' +\n '## Python environment\\n' +\n '\\n' +\n 'Follow an explicit user or applicable AGENTS.md requirement for a Python environment. Otherwise call `load_workspace_dependencies` and run scripts with its Python executable and bundled libraries. Do not install packages or search for a system Python for the default workflow. If the tool is unavailable, use an already configured environment and report a missing dependency only when it prevents the requested operation.'\n}"}],"isError":false,"id":"{{message:7}}"}},"sourceEventSeqs":[16],"surfaceOp":"append"} @@ -23,21 +23,21 @@ {"type":"step/start","data":{"turn":1,"step":2}} {"type":"system/message","data":{"turn":1,"step":2,"message":{"role":"system","content":[{"type":"text","text":"{{system}}"}],"source":{"kind":"system-prompt"},"id":"{{message:8}}"}},"sourceEventSeqs":[7],"surfaceOp":{"op":"replace","startSeq":7,"endSeq":7}} {"type":"request/header","data":{"header":{"config":{"provider":"deepseek-official","model":"deepseek-v4-flash"},"tools":"{{tools}}"},"reason":"series"}} -{"type":"assistant/message","data":{"turn":1,"step":2,"message":{"role":"assistant","content":[{"type":"tool-call","id":"call_office_2","name":"run_code","arguments":"{\"code\":\"console.log(await tools.office_tools({\\\"action\\\":\\\"load\\\"}));\",\"description\":\"Use the Office workflow\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:9}}"},"stream":[{"type":"chunk","time":1793793371286,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}},{"type":"tool-call-chunks","time0":1793793371286,"index":0,"dt":[],"id":"call_office_2","name":"run_code","args":["{\"code\":\"console.log(await tools.office_tools({\\\"action\\\":\\\"load\\\"}));\",\"description\":\"Use the Office workflow\"}"]},{"type":"chunk","time":1793793371286,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"call_office_2","name":"run_code","arguments":"{\"code\":\"console.log(await tools.office_tools({\\\"action\\\":\\\"load\\\"}));\",\"description\":\"Use the Office workflow\"}"}}},{"type":"chunk","time":1793793371286,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"} -{"type":"tool/call","data":{"turn":1,"step":2,"callId":"call_office_2","name":"run_code","arguments":"{\"code\":\"console.log(await tools.office_tools({\\\"action\\\":\\\"load\\\"}));\",\"description\":\"Use the Office workflow\"}"}} +{"type":"assistant/message","data":{"turn":1,"step":2,"message":{"role":"assistant","content":[{"type":"tool-call","id":"call_office_2","name":"run_code","arguments":"{\"code\":\"Harness.inspect(await tools.office_tools({\\\"action\\\":\\\"load\\\"}));\",\"description\":\"Use the Office workflow\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:9}}"},"stream":[{"type":"chunk","time":1794195899655,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}},{"type":"chunk","time":1794195899655,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"call_office_2","name":"run_code","arguments":"{\"code\":\"Harness.inspect(await tools.office_tools({\\\"action\\\":\\\"load\\\"}));\",\"description\":\"Use the Office workflow\"}"}}},{"type":"chunk","time":1794195899655,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"} +{"type":"tool/call","data":{"turn":1,"step":2,"callId":"call_office_2","name":"run_code","arguments":"{\"code\":\"Harness.inspect(await tools.office_tools({\\\"action\\\":\\\"load\\\"}));\",\"description\":\"Use the Office workflow\"}"}} {"type":"tool/result","data":{"turn":1,"step":2,"message":{"role":"tool","source":{"kind":"tool","callId":"call_office_2"},"toolCallId":"call_office_2","content":[{"type":"text","text":"Error: code run failed (exception): TypeError: tools.office_tools is not a function\nFile sandbox: danger-full-access."}],"isError":true,"id":"{{message:10}}"},"error":{"name":"CodeRunFailedError","code":"CODE_RUN_FAILED"}},"sourceEventSeqs":[25],"surfaceOp":"append"} {"type":"step/end","data":{"turn":1,"step":2}} {"type":"step/start","data":{"turn":1,"step":3}} -{"type":"assistant/message","data":{"turn":1,"step":3,"message":{"role":"assistant","content":[{"type":"tool-call","id":"call_office_3","name":"run_code","arguments":"{\"code\":\"console.log(await tools.render_document({\\\"input\\\":\\\"report.docx\\\",\\\"outputDirectory\\\":\\\"preview\\\",\\\"pages\\\":[1]}));\",\"description\":\"Use the Office workflow\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:11}}"},"stream":[{"type":"chunk","time":1793793371432,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}},{"type":"tool-call-chunks","time0":1793793371432,"index":0,"dt":[],"id":"call_office_3","name":"run_code","args":["{\"code\":\"console.log(await tools.render_document({\\\"input\\\":\\\"report.docx\\\",\\\"outputDirectory\\\":\\\"preview\\\",\\\"pages\\\":[1]}));\",\"description\":\"Use the Office workflow\"}"]},{"type":"chunk","time":1793793371432,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"call_office_3","name":"run_code","arguments":"{\"code\":\"console.log(await tools.render_document({\\\"input\\\":\\\"report.docx\\\",\\\"outputDirectory\\\":\\\"preview\\\",\\\"pages\\\":[1]}));\",\"description\":\"Use the Office workflow\"}"}}},{"type":"chunk","time":1793793371432,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"} -{"type":"tool/call","data":{"turn":1,"step":3,"callId":"call_office_3","name":"run_code","arguments":"{\"code\":\"console.log(await tools.render_document({\\\"input\\\":\\\"report.docx\\\",\\\"outputDirectory\\\":\\\"preview\\\",\\\"pages\\\":[1]}));\",\"description\":\"Use the Office workflow\"}"}} +{"type":"assistant/message","data":{"turn":1,"step":3,"message":{"role":"assistant","content":[{"type":"tool-call","id":"call_office_3","name":"run_code","arguments":"{\"code\":\"Harness.inspect(await tools.render_document({\\\"input\\\":\\\"report.docx\\\",\\\"outputDirectory\\\":\\\"preview\\\",\\\"pages\\\":[1]}));\",\"description\":\"Use the Office workflow\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:11}}"},"stream":[{"type":"chunk","time":1794195899791,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}},{"type":"chunk","time":1794195899791,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"call_office_3","name":"run_code","arguments":"{\"code\":\"Harness.inspect(await tools.render_document({\\\"input\\\":\\\"report.docx\\\",\\\"outputDirectory\\\":\\\"preview\\\",\\\"pages\\\":[1]}));\",\"description\":\"Use the Office workflow\"}"}}},{"type":"chunk","time":1794195899791,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"} +{"type":"tool/call","data":{"turn":1,"step":3,"callId":"call_office_3","name":"run_code","arguments":"{\"code\":\"Harness.inspect(await tools.render_document({\\\"input\\\":\\\"report.docx\\\",\\\"outputDirectory\\\":\\\"preview\\\",\\\"pages\\\":[1]}));\",\"description\":\"Use the Office workflow\"}"}} {"type":"tool/result","data":{"turn":1,"step":3,"message":{"role":"tool","source":{"kind":"tool","callId":"call_office_3"},"toolCallId":"call_office_3","content":[{"type":"text","text":"Error: code run failed (exception): TypeError: tools.render_document is not a function\nFile sandbox: danger-full-access."}],"isError":true,"id":"{{message:12}}"},"error":{"name":"CodeRunFailedError","code":"CODE_RUN_FAILED"}},"sourceEventSeqs":[30],"surfaceOp":"append"} {"type":"step/end","data":{"turn":1,"step":3}} {"type":"step/start","data":{"turn":1,"step":4}} -{"type":"assistant/message","data":{"turn":1,"step":4,"message":{"role":"assistant","content":[{"type":"tool-call","id":"call_office_4","name":"run_code","arguments":"{\"code\":\"console.log(await tools.office_tools({\\\"action\\\":\\\"remove\\\"}));\",\"description\":\"Use the Office workflow\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:13}}"},"stream":[{"type":"chunk","time":1793793371628,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}},{"type":"tool-call-chunks","time0":1793793371628,"index":0,"dt":[],"id":"call_office_4","name":"run_code","args":["{\"code\":\"console.log(await tools.office_tools({\\\"action\\\":\\\"remove\\\"}));\",\"description\":\"Use the Office workflow\"}"]},{"type":"chunk","time":1793793371628,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"call_office_4","name":"run_code","arguments":"{\"code\":\"console.log(await tools.office_tools({\\\"action\\\":\\\"remove\\\"}));\",\"description\":\"Use the Office workflow\"}"}}},{"type":"chunk","time":1793793371628,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"} -{"type":"tool/call","data":{"turn":1,"step":4,"callId":"call_office_4","name":"run_code","arguments":"{\"code\":\"console.log(await tools.office_tools({\\\"action\\\":\\\"remove\\\"}));\",\"description\":\"Use the Office workflow\"}"}} +{"type":"assistant/message","data":{"turn":1,"step":4,"message":{"role":"assistant","content":[{"type":"tool-call","id":"call_office_4","name":"run_code","arguments":"{\"code\":\"Harness.inspect(await tools.office_tools({\\\"action\\\":\\\"remove\\\"}));\",\"description\":\"Use the Office workflow\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:13}}"},"stream":[{"type":"chunk","time":1794195899928,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}},{"type":"chunk","time":1794195899928,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"call_office_4","name":"run_code","arguments":"{\"code\":\"Harness.inspect(await tools.office_tools({\\\"action\\\":\\\"remove\\\"}));\",\"description\":\"Use the Office workflow\"}"}}},{"type":"chunk","time":1794195899928,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"} +{"type":"tool/call","data":{"turn":1,"step":4,"callId":"call_office_4","name":"run_code","arguments":"{\"code\":\"Harness.inspect(await tools.office_tools({\\\"action\\\":\\\"remove\\\"}));\",\"description\":\"Use the Office workflow\"}"}} {"type":"tool/result","data":{"turn":1,"step":4,"message":{"role":"tool","source":{"kind":"tool","callId":"call_office_4"},"toolCallId":"call_office_4","content":[{"type":"text","text":"Error: code run failed (exception): TypeError: tools.office_tools is not a function\nFile sandbox: danger-full-access."}],"isError":true,"id":"{{message:14}}"},"error":{"name":"CodeRunFailedError","code":"CODE_RUN_FAILED"}},"sourceEventSeqs":[35],"surfaceOp":"append"} {"type":"step/end","data":{"turn":1,"step":4}} {"type":"step/start","data":{"turn":1,"step":5}} -{"type":"assistant/message","data":{"turn":1,"step":5,"message":{"role":"assistant","content":[{"type":"text","text":"DONE"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:15}}"},"usage":{"inputTokens":12,"outputTokens":2,"cacheReadTokens":10},"stream":[{"type":"chunk","time":1793793371771,"chunk":{"type":"block-start","index":0,"blockType":"text"}},{"type":"text-chunks","time0":1793793371772,"index":0,"dt":[],"texts":["DONE"]},{"type":"chunk","time":1793793371772,"chunk":{"type":"block-end","index":0,"block":{"type":"text","text":"DONE"}}},{"type":"chunk","time":1793793371772,"chunk":{"type":"usage","usage":{"inputTokens":12,"outputTokens":2,"cacheReadTokens":10}}},{"type":"chunk","time":1793793371772,"chunk":{"type":"finish","reason":{"kind":"stop"}}}]},"surfaceOp":"append"} +{"type":"assistant/message","data":{"turn":1,"step":5,"message":{"role":"assistant","content":[{"type":"text","text":"DONE"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:15}}"},"usage":{"inputTokens":12,"outputTokens":2,"cacheReadTokens":10},"stream":[{"type":"chunk","time":1794195900075,"chunk":{"type":"block-start","index":0,"blockType":"text"}},{"type":"chunk","time":1794195900075,"chunk":{"type":"block-end","index":0,"block":{"type":"text","text":"DONE"}}},{"type":"chunk","time":1794195900075,"chunk":{"type":"usage","usage":{"inputTokens":12,"outputTokens":2,"cacheReadTokens":10}}},{"type":"chunk","time":1794195900075,"chunk":{"type":"finish","reason":{"kind":"stop"}}}]},"surfaceOp":"append"} {"type":"step/end","data":{"turn":1,"step":5}} {"type":"turn/end","data":{"turn":1,"reason":{"kind":"completed"}}} diff --git a/snapshots/session/ptc-console-enabled/runtime.cordis.yml b/snapshots/session/ptc-console-enabled/runtime.cordis.yml new file mode 100644 index 0000000000..49e3d082c5 --- /dev/null +++ b/snapshots/session/ptc-console-enabled/runtime.cordis.yml @@ -0,0 +1,3 @@ +- id: ptc-runtime + config: + enableConsole: true diff --git a/snapshots/session/ptc-console-enabled/session.v4.jsonl b/snapshots/session/ptc-console-enabled/session.v4.jsonl new file mode 100644 index 0000000000..f596acf92d --- /dev/null +++ b/snapshots/session/ptc-console-enabled/session.v4.jsonl @@ -0,0 +1,22 @@ +{"type":"session","version":4,"id":"{{session:1}}","createdAt":1785014439563,"cwd":"{{cwd}}","isSeeded":false,"delegationDepth":0} +{"type":"permission/preset","data":{"preset":"danger-full-access"}} +{"type":"sandbox/mode","data":{"mode":"danger-full-access"}} +{"type":"approval/policy","data":{"policy":"never"}} +{"type":"agent/inbox/spliced","data":{"target":"next-turn","start":0,"inserted":[{"content":[{"type":"text","text":"Inspect literal strings and selected values with Harness.inspect, then reply INSPECT_DONE."}],"source":{"kind":"user"},"role":"user","id":"{{message:1}}"}]}} +{"type":"turn/start","data":{"turn":1}} +{"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":"{{message:2}}"}},"surfaceOp":"append"} +{"type":"user/message","data":{"content":[{"type":"text","text":"Inspect literal strings and selected values with Harness.inspect, then reply INSPECT_DONE."}],"source":{"kind":"user"},"role":"user","id":"{{message:1}}"},"surfaceOp":"append"} +{"type":"user/message","data":{"content":[{"type":"text","text":"\nThe following information describes your current session:\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 name is \"lead\".\n\nTo message other agents, use send_message({ agent: \"\", message: \"...\" }).\nUse list_agents({}) to find your collaborator agents and their names.\n"}],"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":"subagent:identity","text":"You are the root agent. Your name is \"lead\".\n\nTo message other agents, use send_message({ agent: \"\", message: \"...\" }).\nUse list_agents({}) to find your collaborator agents and their names."}]},"role":"user","id":"{{message:3}}"},"surfaceOp":"append"} +{"type":"request/header","data":{"header":{"config":{"provider":"deepseek-official","model":"deepseek-v4-flash"},"tools":"{{tools}}"},"reason":"initial"}} +{"type":"request/context","data":{"provider":"deepseek-official","model":"deepseek-v4-flash"}} +{"type":"session/title","data":{"title":"Inspect literal strings and selected","messageSeqs":[8],"source":{"kind":"fallback"}}} +{"type":"assistant/message","data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"tool-call","id":"call_00_UiQPVqoELyzBZCY5pm1z7875","name":"run_code","arguments":"{\"code\":\"Harness.inspect(\\\"selected\\\", { visible: 42 }); for (const method of [\\\"log\\\", \\\"info\\\", \\\"warn\\\", \\\"error\\\", \\\"debug\\\"]) console[method](\\\"%s\\\", method); return 42n;\",\"description\":\"Inspect selected program values\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:4}}"},"usage":{"inputTokens":10,"outputTokens":5},"stream":[{"type":"chunk","time":1794193200000,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}},{"type":"chunk","time":1794193200001,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"call_00_UiQPVqoELyzBZCY5pm1z7875","name":"run_code","arguments":"{\"code\":\"Harness.inspect(\\\"selected\\\", { visible: 42 }); for (const method of [\\\"log\\\", \\\"info\\\", \\\"warn\\\", \\\"error\\\", \\\"debug\\\"]) console[method](\\\"%s\\\", method); return 42n;\",\"description\":\"Inspect selected program values\"}"}}},{"type":"chunk","time":1794193200002,"chunk":{"type":"usage","usage":{"inputTokens":10,"outputTokens":5}}},{"type":"chunk","time":1794193200003,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"} +{"type":"tool/call","data":{"turn":1,"step":1,"callId":"call_00_UiQPVqoELyzBZCY5pm1z7875","name":"run_code","arguments":"{\"code\":\"Harness.inspect(\\\"selected\\\", { visible: 42 }); for (const method of [\\\"log\\\", \\\"info\\\", \\\"warn\\\", \\\"error\\\", \\\"debug\\\"]) console[method](\\\"%s\\\", method); return 42n;\",\"description\":\"Inspect selected program values\"}"}} +{"type":"tool/result","data":{"turn":1,"step":1,"message":{"role":"tool","source":{"kind":"tool","callId":"call_00_UiQPVqoELyzBZCY5pm1z7875"},"toolCallId":"call_00_UiQPVqoELyzBZCY5pm1z7875","content":[{"type":"text","text":"selected { visible: 42 }\n%s log\n%s info\n%s warn\n%s error\n%s debug"}],"isError":false,"id":"{{message:5}}"}},"sourceEventSeqs":[14],"surfaceOp":"append"} +{"type":"step/end","data":{"turn":1,"step":1}} +{"type":"step/start","data":{"turn":1,"step":2}} +{"type":"assistant/message","data":{"turn":1,"step":2,"message":{"role":"assistant","content":[{"type":"text","text":"INSPECT_DONE"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:6}}"},"usage":{"inputTokens":10,"outputTokens":5},"stream":[{"type":"chunk","time":1794193200000,"chunk":{"type":"block-start","index":0,"blockType":"text"}},{"type":"chunk","time":1794193200001,"chunk":{"type":"block-end","index":0,"block":{"type":"text","text":"INSPECT_DONE"}}},{"type":"chunk","time":1794193200002,"chunk":{"type":"usage","usage":{"inputTokens":10,"outputTokens":5}}},{"type":"chunk","time":1794193200003,"chunk":{"type":"finish","reason":{"kind":"stop"}}}]},"surfaceOp":"append"} +{"type":"step/end","data":{"turn":1,"step":2}} +{"type":"turn/end","data":{"turn":1,"reason":{"kind":"completed"}}} diff --git a/snapshots/session/ptc-console-enabled/snapshot.yml b/snapshots/session/ptc-console-enabled/snapshot.yml new file mode 100644 index 0000000000..e5f0cb1265 --- /dev/null +++ b/snapshots/session/ptc-console-enabled/snapshot.yml @@ -0,0 +1,9 @@ +version: 1 +scenario: ptc-console-enabled +profile: headless +composition: ptc +recording: authored +header: + class: ptc-console-enabled + pin: true + systemPromptSource: ptc-turn diff --git a/snapshots/session/ptc-console-enabled/tool-schemas.expected.json b/snapshots/session/ptc-console-enabled/tool-schemas.expected.json new file mode 100644 index 0000000000..97cce643a6 --- /dev/null +++ b/snapshots/session/ptc-console-enabled/tool-schemas.expected.json @@ -0,0 +1,42 @@ +{ + "initial": [ + { + "name": "run_code", + "description": "Execute a TypeScript program against the available tools. Takes two required arguments: `code`, the BODY of an async function (erasable syntax only; top-level `await` and `return` work), and `description`, a short summary of what the program does. Call tools as `await tools.name(args)` per the declarations in the system prompt. Show selected results with Harness.inspect(...values). Return values are not displayed. Image-bearing subtool results are attached after the run. The program is the body of an async TypeScript function; only erasable syntax is accepted, with no enum or namespaces. Type annotations are advisory. Top-level await and return are supported. Use Harness.inspect(...values) for output. Each call runs in a fresh Node process. Node APIs are available through await import(...). Relative paths use the supplied working directory; process.env starts empty. Direct filesystem access follows this execution's sandbox policy. Each program starts in the Session's current directory. Running programs keep their initial directory. A sandbox escalation approves this complete program for one execution only. Nested tools retain their own policies and approvals. Request wider access only after evidence of a denial. Earlier effects may already have completed: inspect them before explicitly retrying. Programs are never replayed automatically.", + "parameters": { + "type": "object", + "properties": { + "code": { + "type": "string", + "description": "The program: the body of an async TypeScript function." + }, + "description": { + "type": "string", + "description": "Clear, concise description of what this program does in active voice, 5-10 words (shown in the UI). Examples: \"Count TODO markers across packages\"; \"Read failing test and its fixture\"; \"Rename config key in every cordis.yml\"." + }, + "timeoutMs": { + "type": "number", + "description": "Positive elapsed-time budget in milliseconds, including nested tool and approval waits. Default 120000; capped at 600000. Zero does not disable the deadline." + }, + "sandbox_permissions": { + "type": "string", + "description": "Wider sandbox mode for this complete program execution; requires justification and approval.", + "enum": [ + "workspace-write", + "danger-full-access" + ] + }, + "justification": { + "type": "string", + "description": "Reason this complete program needs wider access, shown to the user for approval. Use the language of the user’s current request." + } + }, + "required": [ + "code", + "description" + ] + } + } + ], + "changes": [] +} diff --git a/snapshots/session/ptc-inspect-output/session.v4.jsonl b/snapshots/session/ptc-inspect-output/session.v4.jsonl new file mode 100644 index 0000000000..1fa40f78c1 --- /dev/null +++ b/snapshots/session/ptc-inspect-output/session.v4.jsonl @@ -0,0 +1,22 @@ +{"type":"session","version":4,"id":"{{session:1}}","createdAt":1785014439563,"cwd":"{{cwd}}","isSeeded":false,"delegationDepth":0} +{"type":"permission/preset","data":{"preset":"danger-full-access"}} +{"type":"sandbox/mode","data":{"mode":"danger-full-access"}} +{"type":"approval/policy","data":{"policy":"never"}} +{"type":"agent/inbox/spliced","data":{"target":"next-turn","start":0,"inserted":[{"content":[{"type":"text","text":"Inspect literal strings and selected values with Harness.inspect, then reply INSPECT_DONE."}],"source":{"kind":"user"},"role":"user","id":"{{message:1}}"}]}} +{"type":"turn/start","data":{"turn":1}} +{"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":"{{message:2}}"}},"surfaceOp":"append"} +{"type":"user/message","data":{"content":[{"type":"text","text":"Inspect literal strings and selected values with Harness.inspect, then reply INSPECT_DONE."}],"source":{"kind":"user"},"role":"user","id":"{{message:1}}"},"surfaceOp":"append"} +{"type":"user/message","data":{"content":[{"type":"text","text":"\nThe following information describes your current session:\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 name is \"lead\".\n\nTo message other agents, use send_message({ agent: \"\", message: \"...\" }).\nUse list_agents({}) to find your collaborator agents and their names.\n"}],"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":"subagent:identity","text":"You are the root agent. Your name is \"lead\".\n\nTo message other agents, use send_message({ agent: \"\", message: \"...\" }).\nUse list_agents({}) to find your collaborator agents and their names."}]},"role":"user","id":"{{message:3}}"},"surfaceOp":"append"} +{"type":"request/header","data":{"header":{"config":{"provider":"deepseek-official","model":"deepseek-v4-flash"},"tools":"{{tools}}"},"reason":"initial"}} +{"type":"request/context","data":{"provider":"deepseek-official","model":"deepseek-v4-flash"}} +{"type":"session/title","data":{"title":"Inspect literal strings and selected","messageSeqs":[8],"source":{"kind":"fallback"}}} +{"type":"assistant/message","data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"tool-call","id":"call_00_UiQPVqoELyzBZCY5pm1z7875","name":"run_code","arguments":"{\"code\":\"Harness.inspect(\\\"%s\\\", \\\"literal\\\", { visible: 42 }); Harness.inspect(typeof console, Object.hasOwn(globalThis, \\\"console\\\")); try { void console } catch (error) { Harness.inspect(error.name) }; { const console = { value: 17 }; Harness.inspect(console.value) }; return { get hidden() { throw new Error(\\\"completion getter ran\\\") } };\",\"description\":\"Inspect selected program values\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:4}}"},"usage":{"inputTokens":10,"outputTokens":5},"stream":[{"type":"chunk","time":1794193200000,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}},{"type":"chunk","time":1794193200001,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"call_00_UiQPVqoELyzBZCY5pm1z7875","name":"run_code","arguments":"{\"code\":\"Harness.inspect(\\\"%s\\\", \\\"literal\\\", { visible: 42 }); Harness.inspect(typeof console, Object.hasOwn(globalThis, \\\"console\\\")); try { void console } catch (error) { Harness.inspect(error.name) }; { const console = { value: 17 }; Harness.inspect(console.value) }; return { get hidden() { throw new Error(\\\"completion getter ran\\\") } };\",\"description\":\"Inspect selected program values\"}"}}},{"type":"chunk","time":1794193200002,"chunk":{"type":"usage","usage":{"inputTokens":10,"outputTokens":5}}},{"type":"chunk","time":1794193200003,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"} +{"type":"tool/call","data":{"turn":1,"step":1,"callId":"call_00_UiQPVqoELyzBZCY5pm1z7875","name":"run_code","arguments":"{\"code\":\"Harness.inspect(\\\"%s\\\", \\\"literal\\\", { visible: 42 }); Harness.inspect(typeof console, Object.hasOwn(globalThis, \\\"console\\\")); try { void console } catch (error) { Harness.inspect(error.name) }; { const console = { value: 17 }; Harness.inspect(console.value) }; return { get hidden() { throw new Error(\\\"completion getter ran\\\") } };\",\"description\":\"Inspect selected program values\"}"}} +{"type":"tool/result","data":{"turn":1,"step":1,"message":{"role":"tool","source":{"kind":"tool","callId":"call_00_UiQPVqoELyzBZCY5pm1z7875"},"toolCallId":"call_00_UiQPVqoELyzBZCY5pm1z7875","content":[{"type":"text","text":"%s literal { visible: 42 }\nundefined false\nReferenceError\n17"}],"isError":false,"id":"{{message:5}}"}},"sourceEventSeqs":[14],"surfaceOp":"append"} +{"type":"step/end","data":{"turn":1,"step":1}} +{"type":"step/start","data":{"turn":1,"step":2}} +{"type":"assistant/message","data":{"turn":1,"step":2,"message":{"role":"assistant","content":[{"type":"text","text":"INSPECT_DONE"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:6}}"},"usage":{"inputTokens":10,"outputTokens":5},"stream":[{"type":"chunk","time":1794193200000,"chunk":{"type":"block-start","index":0,"blockType":"text"}},{"type":"chunk","time":1794193200001,"chunk":{"type":"block-end","index":0,"block":{"type":"text","text":"INSPECT_DONE"}}},{"type":"chunk","time":1794193200002,"chunk":{"type":"usage","usage":{"inputTokens":10,"outputTokens":5}}},{"type":"chunk","time":1794193200003,"chunk":{"type":"finish","reason":{"kind":"stop"}}}]},"surfaceOp":"append"} +{"type":"step/end","data":{"turn":1,"step":2}} +{"type":"turn/end","data":{"turn":1,"reason":{"kind":"completed"}}} diff --git a/snapshots/session/ptc-inspect-output/snapshot.yml b/snapshots/session/ptc-inspect-output/snapshot.yml new file mode 100644 index 0000000000..77d1c4306f --- /dev/null +++ b/snapshots/session/ptc-inspect-output/snapshot.yml @@ -0,0 +1,7 @@ +version: 1 +scenario: ptc-inspect-output +profile: headless +composition: ptc +recording: authored +header: + class: ptc diff --git a/snapshots/session/ptc-node-read-only/replay.override.json b/snapshots/session/ptc-node-read-only/replay.override.json index 7dfa005bcc..b86c31e1ba 100644 --- a/snapshots/session/ptc-node-read-only/replay.override.json +++ b/snapshots/session/ptc-node-read-only/replay.override.json @@ -12,7 +12,7 @@ "index": 0, "id": "call_node_policy", "name": "run_code", - "argumentsDelta": "{\"code\":\"const fs = await import('node:fs/promises'); let denied = false; try { await fs.writeFile('ptc-denied.txt', 'forbidden') } catch (error) { denied = ['EPERM', 'EACCES', 'EROFS'].includes(error.code) }; const shell = await tools.bash({ command: 'pwd', description: 'Report the current workspace directory' }); return { denied, emptyEnvironment: Object.keys(process.env).length === 0, sameWorkspace: process.cwd() === shell.stdout.text.trim() };\",\"description\":\"Verify Node file policy and execution context\"}" + "argumentsDelta": "{\"code\":\"const fs = await import('node:fs/promises'); let denied = false; try { await fs.writeFile('ptc-denied.txt', 'forbidden') } catch (error) { denied = ['EPERM', 'EACCES', 'EROFS'].includes(error.code) }; const shell = await tools.bash({ command: 'pwd', description: 'Report the current workspace directory' }); Harness.inspect({ denied, emptyEnvironment: Object.keys(process.env).length === 0, sameWorkspace: process.cwd() === shell.stdout.text.trim() }); return;\",\"description\":\"Verify Node file policy and execution context\"}" }, { "type": "block-end", @@ -21,7 +21,7 @@ "type": "tool-call", "id": "call_node_policy", "name": "run_code", - "arguments": "{\"code\":\"const fs = await import('node:fs/promises'); let denied = false; try { await fs.writeFile('ptc-denied.txt', 'forbidden') } catch (error) { denied = ['EPERM', 'EACCES', 'EROFS'].includes(error.code) }; const shell = await tools.bash({ command: 'pwd', description: 'Report the current workspace directory' }); return { denied, emptyEnvironment: Object.keys(process.env).length === 0, sameWorkspace: process.cwd() === shell.stdout.text.trim() };\",\"description\":\"Verify Node file policy and execution context\"}" + "arguments": "{\"code\":\"const fs = await import('node:fs/promises'); let denied = false; try { await fs.writeFile('ptc-denied.txt', 'forbidden') } catch (error) { denied = ['EPERM', 'EACCES', 'EROFS'].includes(error.code) }; const shell = await tools.bash({ command: 'pwd', description: 'Report the current workspace directory' }); Harness.inspect({ denied, emptyEnvironment: Object.keys(process.env).length === 0, sameWorkspace: process.cwd() === shell.stdout.text.trim() }); return;\",\"description\":\"Verify Node file policy and execution context\"}" } }, { diff --git a/snapshots/session/ptc-node-read-only/session.v4.jsonl b/snapshots/session/ptc-node-read-only/session.v4.jsonl index f12275b690..6bfbba8d01 100644 --- a/snapshots/session/ptc-node-read-only/session.v4.jsonl +++ b/snapshots/session/ptc-node-read-only/session.v4.jsonl @@ -12,13 +12,13 @@ {"type":"request/header","data":{"header":{"config":{"provider":"deepseek-official","model":"deepseek-v4-flash"},"tools":"{{tools}}"},"reason":"initial"}} {"type":"request/context","data":{"provider":"deepseek-official","model":"deepseek-v4-flash"}} {"type":"session/title","data":{"title":"Use one run_code program to","messageSeqs":[8],"source":{"kind":"fallback"}}} -{"type":"assistant/message","data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"tool-call","id":"call_node_policy","name":"run_code","arguments":"{\"code\":\"const fs = await import('node:fs/promises'); let denied = false; try { await fs.writeFile('ptc-denied.txt', 'forbidden') } catch (error) { denied = ['EPERM', 'EACCES', 'EROFS'].includes(error.code) }; const shell = await tools.bash({ command: 'pwd', description: 'Report the current workspace directory' }); return { denied, emptyEnvironment: Object.keys(process.env).length === 0, sameWorkspace: process.cwd() === shell.stdout.text.trim() };\",\"description\":\"Verify Node file policy and execution context\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:4}}"},"usage":{"inputTokens":10,"outputTokens":5},"stream":[{"type":"chunk","time":1791427867010,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}},{"type":"tool-call-chunks","time0":1791427867010,"index":0,"dt":[],"id":"call_node_policy","name":"run_code","args":["{\"code\":\"const fs = await import('node:fs/promises'); let denied = false; try { await fs.writeFile('ptc-denied.txt', 'forbidden') } catch (error) { denied = ['EPERM', 'EACCES', 'EROFS'].includes(error.code) }; const shell = await tools.bash({ command: 'pwd', description: 'Report the current workspace directory' }); return { denied, emptyEnvironment: Object.keys(process.env).length === 0, sameWorkspace: process.cwd() === shell.stdout.text.trim() };\",\"description\":\"Verify Node file policy and execution context\"}"]},{"type":"chunk","time":1791427867010,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"call_node_policy","name":"run_code","arguments":"{\"code\":\"const fs = await import('node:fs/promises'); let denied = false; try { await fs.writeFile('ptc-denied.txt', 'forbidden') } catch (error) { denied = ['EPERM', 'EACCES', 'EROFS'].includes(error.code) }; const shell = await tools.bash({ command: 'pwd', description: 'Report the current workspace directory' }); return { denied, emptyEnvironment: Object.keys(process.env).length === 0, sameWorkspace: process.cwd() === shell.stdout.text.trim() };\",\"description\":\"Verify Node file policy and execution context\"}"}}},{"type":"chunk","time":1791427867010,"chunk":{"type":"usage","usage":{"inputTokens":10,"outputTokens":5}}},{"type":"chunk","time":1791427867010,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"} -{"type":"tool/call","data":{"turn":1,"step":1,"callId":"call_node_policy","name":"run_code","arguments":"{\"code\":\"const fs = await import('node:fs/promises'); let denied = false; try { await fs.writeFile('ptc-denied.txt', 'forbidden') } catch (error) { denied = ['EPERM', 'EACCES', 'EROFS'].includes(error.code) }; const shell = await tools.bash({ command: 'pwd', description: 'Report the current workspace directory' }); return { denied, emptyEnvironment: Object.keys(process.env).length === 0, sameWorkspace: process.cwd() === shell.stdout.text.trim() };\",\"description\":\"Verify Node file policy and execution context\"}"}} +{"type":"assistant/message","data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"tool-call","id":"call_node_policy","name":"run_code","arguments":"{\"code\":\"const fs = await import('node:fs/promises'); let denied = false; try { await fs.writeFile('ptc-denied.txt', 'forbidden') } catch (error) { denied = ['EPERM', 'EACCES', 'EROFS'].includes(error.code) }; const shell = await tools.bash({ command: 'pwd', description: 'Report the current workspace directory' }); Harness.inspect({ denied, emptyEnvironment: Object.keys(process.env).length === 0, sameWorkspace: process.cwd() === shell.stdout.text.trim() }); return;\",\"description\":\"Verify Node file policy and execution context\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:4}}"},"usage":{"inputTokens":10,"outputTokens":5},"stream":[{"type":"chunk","time":1791427867010,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}},{"type":"tool-call-chunks","time0":1791427867010,"index":0,"dt":[],"id":"call_node_policy","name":"run_code","args":["{\"code\":\"const fs = await import('node:fs/promises'); let denied = false; try { await fs.writeFile('ptc-denied.txt', 'forbidden') } catch (error) { denied = ['EPERM', 'EACCES', 'EROFS'].includes(error.code) }; const shell = await tools.bash({ command: 'pwd', description: 'Report the current workspace directory' }); Harness.inspect({ denied, emptyEnvironment: Object.keys(process.env).length === 0, sameWorkspace: process.cwd() === shell.stdout.text.trim() }); return;\",\"description\":\"Verify Node file policy and execution context\"}"]},{"type":"chunk","time":1791427867010,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"call_node_policy","name":"run_code","arguments":"{\"code\":\"const fs = await import('node:fs/promises'); let denied = false; try { await fs.writeFile('ptc-denied.txt', 'forbidden') } catch (error) { denied = ['EPERM', 'EACCES', 'EROFS'].includes(error.code) }; const shell = await tools.bash({ command: 'pwd', description: 'Report the current workspace directory' }); Harness.inspect({ denied, emptyEnvironment: Object.keys(process.env).length === 0, sameWorkspace: process.cwd() === shell.stdout.text.trim() }); return;\",\"description\":\"Verify Node file policy and execution context\"}"}}},{"type":"chunk","time":1791427867010,"chunk":{"type":"usage","usage":{"inputTokens":10,"outputTokens":5}}},{"type":"chunk","time":1791427867010,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"} +{"type":"tool/call","data":{"turn":1,"step":1,"callId":"call_node_policy","name":"run_code","arguments":"{\"code\":\"const fs = await import('node:fs/promises'); let denied = false; try { await fs.writeFile('ptc-denied.txt', 'forbidden') } catch (error) { denied = ['EPERM', 'EACCES', 'EROFS'].includes(error.code) }; const shell = await tools.bash({ command: 'pwd', description: 'Report the current workspace directory' }); Harness.inspect({ denied, emptyEnvironment: Object.keys(process.env).length === 0, sameWorkspace: process.cwd() === shell.stdout.text.trim() }); return;\",\"description\":\"Verify Node file policy and execution context\"}"}} {"type":"tool/ptc-dispatch-start","data":{"rootCallId":"call_node_policy","parentCallId":"call_node_policy","subCallId":"call_node_policy:ptc:1","name":"bash","arguments":{"command":"pwd","description":"Report the current workspace directory"}}} {"type":"job/registered","data":{"jobId":"{{id:1}}","owner":"{{session:1}}","kind":"bash","label":"pwd","startedAt":1791427867102,"toolCall":{"callId":"call_node_policy:ptc:1","rootCallId":"call_node_policy"}}} {"type":"job/settled","data":{"jobId":"{{id:1}}","owner":"{{session:1}}","kind":"bash","label":"pwd","startedAt":1791427867102,"toolCall":{"callId":"call_node_policy:ptc:1","rootCallId":"call_node_policy"},"finishedAt":1791427867112,"status":"completed","cause":"producer","producerReported":true,"detail":"exit code: 0"}} {"type":"tool/ptc-dispatch","data":{"rootCallId":"call_node_policy","parentCallId":"call_node_policy","subCallId":"call_node_policy:ptc:1","name":"bash","arguments":{"command":"pwd","description":"Report the current workspace directory"},"isError":false,"content":[{"type":"text","text":"{{cwd}}\n"}],"meta":{"cwd":"{{cwd}}"}}} -{"type":"tool/result","data":{"turn":1,"step":1,"message":{"role":"tool","source":{"kind":"tool","callId":"call_node_policy"},"toolCallId":"call_node_policy","content":[{"type":"text","text":"{\n \"denied\": true,\n \"emptyEnvironment\": true,\n \"sameWorkspace\": true\n}"}],"isError":false,"id":"{{message:5}}"}},"sourceEventSeqs":[14],"surfaceOp":"append"} +{"type":"tool/result","data":{"turn":1,"step":1,"message":{"role":"tool","source":{"kind":"tool","callId":"call_node_policy"},"toolCallId":"call_node_policy","content":[{"type":"text","text":"{ denied: true, emptyEnvironment: true, sameWorkspace: true }"}],"isError":false,"id":"{{message:5}}"}},"sourceEventSeqs":[14],"surfaceOp":"append"} {"type":"step/end","data":{"turn":1,"step":1}} {"type":"step/start","data":{"turn":1,"step":2}} {"type":"assistant/message","data":{"turn":1,"step":2,"message":{"role":"assistant","content":[{"type":"text","text":"Node policy and execution context verified."}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:6}}"},"usage":{"inputTokens":10,"outputTokens":2},"stream":[{"type":"chunk","time":1791427867146,"chunk":{"type":"block-start","index":0,"blockType":"text"}},{"type":"text-chunks","time0":1791427867146,"index":0,"dt":[],"texts":["Node policy and execution context verified."]},{"type":"chunk","time":1791427867146,"chunk":{"type":"block-end","index":0,"block":{"type":"text","text":"Node policy and execution context verified."}}},{"type":"chunk","time":1791427867146,"chunk":{"type":"usage","usage":{"inputTokens":10,"outputTokens":2}}},{"type":"chunk","time":1791427867146,"chunk":{"type":"finish","reason":{"kind":"stop"}}}]},"surfaceOp":"append"} diff --git a/snapshots/session/ptc-node-workspace/replay.override.json b/snapshots/session/ptc-node-workspace/replay.override.json index a7b600373e..168589e34c 100644 --- a/snapshots/session/ptc-node-workspace/replay.override.json +++ b/snapshots/session/ptc-node-workspace/replay.override.json @@ -12,7 +12,7 @@ "index": 0, "id": "call_node_policy", "name": "run_code", - "argumentsDelta": "{\"code\":\"const fs = await import('node:fs/promises'); await fs.writeFile('ptc-created.txt', 'created by sandboxed Node\\\\n'); const written = await fs.readFile('ptc-created.txt', 'utf8') === 'created by sandboxed Node\\\\n'; const shell = await tools.bash({ command: 'pwd', description: 'Report the current workspace directory' }); return { written, emptyEnvironment: Object.keys(process.env).length === 0, sameWorkspace: process.cwd() === shell.stdout.text.trim() };\",\"description\":\"Verify Node file policy and execution context\"}" + "argumentsDelta": "{\"code\":\"const fs = await import('node:fs/promises'); await fs.writeFile('ptc-created.txt', 'created by sandboxed Node\\\\n'); const written = await fs.readFile('ptc-created.txt', 'utf8') === 'created by sandboxed Node\\\\n'; const shell = await tools.bash({ command: 'pwd', description: 'Report the current workspace directory' }); Harness.inspect({ written, emptyEnvironment: Object.keys(process.env).length === 0, sameWorkspace: process.cwd() === shell.stdout.text.trim() }); return;\",\"description\":\"Verify Node file policy and execution context\"}" }, { "type": "block-end", @@ -21,7 +21,7 @@ "type": "tool-call", "id": "call_node_policy", "name": "run_code", - "arguments": "{\"code\":\"const fs = await import('node:fs/promises'); await fs.writeFile('ptc-created.txt', 'created by sandboxed Node\\\\n'); const written = await fs.readFile('ptc-created.txt', 'utf8') === 'created by sandboxed Node\\\\n'; const shell = await tools.bash({ command: 'pwd', description: 'Report the current workspace directory' }); return { written, emptyEnvironment: Object.keys(process.env).length === 0, sameWorkspace: process.cwd() === shell.stdout.text.trim() };\",\"description\":\"Verify Node file policy and execution context\"}" + "arguments": "{\"code\":\"const fs = await import('node:fs/promises'); await fs.writeFile('ptc-created.txt', 'created by sandboxed Node\\\\n'); const written = await fs.readFile('ptc-created.txt', 'utf8') === 'created by sandboxed Node\\\\n'; const shell = await tools.bash({ command: 'pwd', description: 'Report the current workspace directory' }); Harness.inspect({ written, emptyEnvironment: Object.keys(process.env).length === 0, sameWorkspace: process.cwd() === shell.stdout.text.trim() }); return;\",\"description\":\"Verify Node file policy and execution context\"}" } }, { diff --git a/snapshots/session/ptc-node-workspace/session.v4.jsonl b/snapshots/session/ptc-node-workspace/session.v4.jsonl index 74e62e7196..68e6cafd49 100644 --- a/snapshots/session/ptc-node-workspace/session.v4.jsonl +++ b/snapshots/session/ptc-node-workspace/session.v4.jsonl @@ -12,13 +12,13 @@ {"type":"request/header","data":{"header":{"config":{"provider":"deepseek-official","model":"deepseek-v4-flash"},"tools":"{{tools}}"},"reason":"initial"}} {"type":"request/context","data":{"provider":"deepseek-official","model":"deepseek-v4-flash"}} {"type":"session/title","data":{"title":"Use one run_code program to","messageSeqs":[8],"source":{"kind":"fallback"}}} -{"type":"assistant/message","data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"tool-call","id":"call_node_policy","name":"run_code","arguments":"{\"code\":\"const fs = await import('node:fs/promises'); await fs.writeFile('ptc-created.txt', 'created by sandboxed Node\\\\n'); const written = await fs.readFile('ptc-created.txt', 'utf8') === 'created by sandboxed Node\\\\n'; const shell = await tools.bash({ command: 'pwd', description: 'Report the current workspace directory' }); return { written, emptyEnvironment: Object.keys(process.env).length === 0, sameWorkspace: process.cwd() === shell.stdout.text.trim() };\",\"description\":\"Verify Node file policy and execution context\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:4}}"},"usage":{"inputTokens":10,"outputTokens":5},"stream":[{"type":"chunk","time":1791427867917,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}},{"type":"tool-call-chunks","time0":1791427867917,"index":0,"dt":[],"id":"call_node_policy","name":"run_code","args":["{\"code\":\"const fs = await import('node:fs/promises'); await fs.writeFile('ptc-created.txt', 'created by sandboxed Node\\\\n'); const written = await fs.readFile('ptc-created.txt', 'utf8') === 'created by sandboxed Node\\\\n'; const shell = await tools.bash({ command: 'pwd', description: 'Report the current workspace directory' }); return { written, emptyEnvironment: Object.keys(process.env).length === 0, sameWorkspace: process.cwd() === shell.stdout.text.trim() };\",\"description\":\"Verify Node file policy and execution context\"}"]},{"type":"chunk","time":1791427867917,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"call_node_policy","name":"run_code","arguments":"{\"code\":\"const fs = await import('node:fs/promises'); await fs.writeFile('ptc-created.txt', 'created by sandboxed Node\\\\n'); const written = await fs.readFile('ptc-created.txt', 'utf8') === 'created by sandboxed Node\\\\n'; const shell = await tools.bash({ command: 'pwd', description: 'Report the current workspace directory' }); return { written, emptyEnvironment: Object.keys(process.env).length === 0, sameWorkspace: process.cwd() === shell.stdout.text.trim() };\",\"description\":\"Verify Node file policy and execution context\"}"}}},{"type":"chunk","time":1791427867917,"chunk":{"type":"usage","usage":{"inputTokens":10,"outputTokens":5}}},{"type":"chunk","time":1791427867917,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"} -{"type":"tool/call","data":{"turn":1,"step":1,"callId":"call_node_policy","name":"run_code","arguments":"{\"code\":\"const fs = await import('node:fs/promises'); await fs.writeFile('ptc-created.txt', 'created by sandboxed Node\\\\n'); const written = await fs.readFile('ptc-created.txt', 'utf8') === 'created by sandboxed Node\\\\n'; const shell = await tools.bash({ command: 'pwd', description: 'Report the current workspace directory' }); return { written, emptyEnvironment: Object.keys(process.env).length === 0, sameWorkspace: process.cwd() === shell.stdout.text.trim() };\",\"description\":\"Verify Node file policy and execution context\"}"}} +{"type":"assistant/message","data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"tool-call","id":"call_node_policy","name":"run_code","arguments":"{\"code\":\"const fs = await import('node:fs/promises'); await fs.writeFile('ptc-created.txt', 'created by sandboxed Node\\\\n'); const written = await fs.readFile('ptc-created.txt', 'utf8') === 'created by sandboxed Node\\\\n'; const shell = await tools.bash({ command: 'pwd', description: 'Report the current workspace directory' }); Harness.inspect({ written, emptyEnvironment: Object.keys(process.env).length === 0, sameWorkspace: process.cwd() === shell.stdout.text.trim() }); return;\",\"description\":\"Verify Node file policy and execution context\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:4}}"},"usage":{"inputTokens":10,"outputTokens":5},"stream":[{"type":"chunk","time":1791427867917,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}},{"type":"tool-call-chunks","time0":1791427867917,"index":0,"dt":[],"id":"call_node_policy","name":"run_code","args":["{\"code\":\"const fs = await import('node:fs/promises'); await fs.writeFile('ptc-created.txt', 'created by sandboxed Node\\\\n'); const written = await fs.readFile('ptc-created.txt', 'utf8') === 'created by sandboxed Node\\\\n'; const shell = await tools.bash({ command: 'pwd', description: 'Report the current workspace directory' }); Harness.inspect({ written, emptyEnvironment: Object.keys(process.env).length === 0, sameWorkspace: process.cwd() === shell.stdout.text.trim() }); return;\",\"description\":\"Verify Node file policy and execution context\"}"]},{"type":"chunk","time":1791427867917,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"call_node_policy","name":"run_code","arguments":"{\"code\":\"const fs = await import('node:fs/promises'); await fs.writeFile('ptc-created.txt', 'created by sandboxed Node\\\\n'); const written = await fs.readFile('ptc-created.txt', 'utf8') === 'created by sandboxed Node\\\\n'; const shell = await tools.bash({ command: 'pwd', description: 'Report the current workspace directory' }); Harness.inspect({ written, emptyEnvironment: Object.keys(process.env).length === 0, sameWorkspace: process.cwd() === shell.stdout.text.trim() }); return;\",\"description\":\"Verify Node file policy and execution context\"}"}}},{"type":"chunk","time":1791427867917,"chunk":{"type":"usage","usage":{"inputTokens":10,"outputTokens":5}}},{"type":"chunk","time":1791427867917,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"} +{"type":"tool/call","data":{"turn":1,"step":1,"callId":"call_node_policy","name":"run_code","arguments":"{\"code\":\"const fs = await import('node:fs/promises'); await fs.writeFile('ptc-created.txt', 'created by sandboxed Node\\\\n'); const written = await fs.readFile('ptc-created.txt', 'utf8') === 'created by sandboxed Node\\\\n'; const shell = await tools.bash({ command: 'pwd', description: 'Report the current workspace directory' }); Harness.inspect({ written, emptyEnvironment: Object.keys(process.env).length === 0, sameWorkspace: process.cwd() === shell.stdout.text.trim() }); return;\",\"description\":\"Verify Node file policy and execution context\"}"}} {"type":"tool/ptc-dispatch-start","data":{"rootCallId":"call_node_policy","parentCallId":"call_node_policy","subCallId":"call_node_policy:ptc:1","name":"bash","arguments":{"command":"pwd","description":"Report the current workspace directory"}}} {"type":"job/registered","data":{"jobId":"{{id:1}}","owner":"{{session:1}}","kind":"bash","label":"pwd","startedAt":1791427868009,"toolCall":{"callId":"call_node_policy:ptc:1","rootCallId":"call_node_policy"}}} {"type":"job/settled","data":{"jobId":"{{id:1}}","owner":"{{session:1}}","kind":"bash","label":"pwd","startedAt":1791427868009,"toolCall":{"callId":"call_node_policy:ptc:1","rootCallId":"call_node_policy"},"finishedAt":1791427868019,"status":"completed","cause":"producer","producerReported":true,"detail":"exit code: 0"}} {"type":"tool/ptc-dispatch","data":{"rootCallId":"call_node_policy","parentCallId":"call_node_policy","subCallId":"call_node_policy:ptc:1","name":"bash","arguments":{"command":"pwd","description":"Report the current workspace directory"},"isError":false,"content":[{"type":"text","text":"{{cwd}}\n"}],"meta":{"cwd":"{{cwd}}"}}} -{"type":"tool/result","data":{"turn":1,"step":1,"message":{"role":"tool","source":{"kind":"tool","callId":"call_node_policy"},"toolCallId":"call_node_policy","content":[{"type":"text","text":"{\n \"written\": true,\n \"emptyEnvironment\": true,\n \"sameWorkspace\": true\n}"}],"isError":false,"id":"{{message:5}}"}},"sourceEventSeqs":[14],"surfaceOp":"append"} +{"type":"tool/result","data":{"turn":1,"step":1,"message":{"role":"tool","source":{"kind":"tool","callId":"call_node_policy"},"toolCallId":"call_node_policy","content":[{"type":"text","text":"{ written: true, emptyEnvironment: true, sameWorkspace: true }"}],"isError":false,"id":"{{message:5}}"}},"sourceEventSeqs":[14],"surfaceOp":"append"} {"type":"step/end","data":{"turn":1,"step":1}} {"type":"step/start","data":{"turn":1,"step":2}} {"type":"assistant/message","data":{"turn":1,"step":2,"message":{"role":"assistant","content":[{"type":"text","text":"Node policy and execution context verified."}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:6}}"},"usage":{"inputTokens":10,"outputTokens":2},"stream":[{"type":"chunk","time":1791427868053,"chunk":{"type":"block-start","index":0,"blockType":"text"}},{"type":"text-chunks","time0":1791427868053,"index":0,"dt":[],"texts":["Node policy and execution context verified."]},{"type":"chunk","time":1791427868053,"chunk":{"type":"block-end","index":0,"block":{"type":"text","text":"Node policy and execution context verified."}}},{"type":"chunk","time":1791427868053,"chunk":{"type":"usage","usage":{"inputTokens":10,"outputTokens":2}}},{"type":"chunk","time":1791427868053,"chunk":{"type":"finish","reason":{"kind":"stop"}}}]},"surfaceOp":"append"} diff --git a/snapshots/session/ptc-persistent/replay.override.json b/snapshots/session/ptc-persistent/replay.override.json index b5ffcdfdce..685941fa1b 100644 --- a/snapshots/session/ptc-persistent/replay.override.json +++ b/snapshots/session/ptc-persistent/replay.override.json @@ -52,7 +52,7 @@ "index": 0, "id": "call_reuse", "name": "run_code", - "argumentsDelta": "{\"code\":\"const childProcess = await import(\\\"node:child_process\\\"); childProcess.execFileSync(process.execPath, [\\\"-e\\\", \\\"process.stdout.write(\\\\\\\"raw child output\\\\\\\")\\\"], { stdio: \\\"inherit\\\" }); await fs.writeFile(\\\"persistent-count.txt\\\", String(increment()) + \\\"\\\\n\\\"); const result = await report({command:\\\"pwd\\\",description:\\\"Read current working directory\\\"}); ({ value: counter.value, same: alias === counter, sameDirectory: process.cwd() === result.stdout.text.trim() })\",\"description\":\"Reuse retained objects and current tool bindings\"}" + "argumentsDelta": "{\"code\":\"const childProcess = await import(\\\"node:child_process\\\"); childProcess.execFileSync(process.execPath, [\\\"-e\\\", \\\"process.stdout.write(\\\\\\\"raw child output\\\\\\\")\\\"], { stdio: \\\"inherit\\\" }); await fs.writeFile(\\\"persistent-count.txt\\\", String(increment()) + \\\"\\\\n\\\"); const result = await report({command:\\\"pwd\\\",description:\\\"Read current working directory\\\"}); Harness.inspect(JSON.stringify(({ value: counter.value, same: alias === counter, sameDirectory: process.cwd() === result.stdout.text.trim() }), null, 2))\",\"description\":\"Reuse retained objects and current tool bindings\"}" }, { "type": "block-end", @@ -61,7 +61,7 @@ "type": "tool-call", "id": "call_reuse", "name": "run_code", - "arguments": "{\"code\":\"const childProcess = await import(\\\"node:child_process\\\"); childProcess.execFileSync(process.execPath, [\\\"-e\\\", \\\"process.stdout.write(\\\\\\\"raw child output\\\\\\\")\\\"], { stdio: \\\"inherit\\\" }); await fs.writeFile(\\\"persistent-count.txt\\\", String(increment()) + \\\"\\\\n\\\"); const result = await report({command:\\\"pwd\\\",description:\\\"Read current working directory\\\"}); ({ value: counter.value, same: alias === counter, sameDirectory: process.cwd() === result.stdout.text.trim() })\",\"description\":\"Reuse retained objects and current tool bindings\"}" + "arguments": "{\"code\":\"const childProcess = await import(\\\"node:child_process\\\"); childProcess.execFileSync(process.execPath, [\\\"-e\\\", \\\"process.stdout.write(\\\\\\\"raw child output\\\\\\\")\\\"], { stdio: \\\"inherit\\\" }); await fs.writeFile(\\\"persistent-count.txt\\\", String(increment()) + \\\"\\\\n\\\"); const result = await report({command:\\\"pwd\\\",description:\\\"Read current working directory\\\"}); Harness.inspect(JSON.stringify(({ value: counter.value, same: alias === counter, sameDirectory: process.cwd() === result.stdout.text.trim() }), null, 2))\",\"description\":\"Reuse retained objects and current tool bindings\"}" } }, { @@ -132,7 +132,7 @@ "index": 0, "id": "call_recover", "name": "run_code", - "argumentsDelta": "{\"code\":\"let missing; try { counter.value } catch (error) { missing = error.name === \\\"ReferenceError\\\" }; const fs2 = await import(\\\"node:fs/promises\\\"); let recovered = { value: 0 }; await fs2.appendFile(\\\"persistent-count.txt\\\", \\\"recreated\\\\n\\\"); ({ missing, recreated: recovered.value })\",\"description\":\"Recreate variables after execution process exits\"}" + "argumentsDelta": "{\"code\":\"let missing; try { counter.value } catch (error) { missing = error.name === \\\"ReferenceError\\\" }; const fs2 = await import(\\\"node:fs/promises\\\"); let recovered = { value: 0 }; await fs2.appendFile(\\\"persistent-count.txt\\\", \\\"recreated\\\\n\\\"); Harness.inspect(JSON.stringify(({ missing, recreated: recovered.value }), null, 2))\",\"description\":\"Recreate variables after execution process exits\"}" }, { "type": "block-end", @@ -141,7 +141,7 @@ "type": "tool-call", "id": "call_recover", "name": "run_code", - "arguments": "{\"code\":\"let missing; try { counter.value } catch (error) { missing = error.name === \\\"ReferenceError\\\" }; const fs2 = await import(\\\"node:fs/promises\\\"); let recovered = { value: 0 }; await fs2.appendFile(\\\"persistent-count.txt\\\", \\\"recreated\\\\n\\\"); ({ missing, recreated: recovered.value })\",\"description\":\"Recreate variables after execution process exits\"}" + "arguments": "{\"code\":\"let missing; try { counter.value } catch (error) { missing = error.name === \\\"ReferenceError\\\" }; const fs2 = await import(\\\"node:fs/promises\\\"); let recovered = { value: 0 }; await fs2.appendFile(\\\"persistent-count.txt\\\", \\\"recreated\\\\n\\\"); Harness.inspect(JSON.stringify(({ missing, recreated: recovered.value }), null, 2))\",\"description\":\"Recreate variables after execution process exits\"}" } }, { diff --git a/snapshots/session/ptc-persistent/session.v4.jsonl b/snapshots/session/ptc-persistent/session.v4.jsonl index 2e43d72bad..3df2690c7c 100644 --- a/snapshots/session/ptc-persistent/session.v4.jsonl +++ b/snapshots/session/ptc-persistent/session.v4.jsonl @@ -17,8 +17,8 @@ {"type":"tool/result","data":{"turn":1,"step":1,"message":{"role":"tool","source":{"kind":"tool","callId":"call_retain"},"toolCallId":"call_retain","content":[{"type":"text","text":"(run_code completed with no output)"}],"isError":false,"id":"{{message:5}}"}},"sourceEventSeqs":[14],"surfaceOp":"append"} {"type":"step/end","data":{"turn":1,"step":1}} {"type":"step/start","data":{"turn":1,"step":2}} -{"type":"assistant/message","data":{"turn":1,"step":2,"message":{"role":"assistant","content":[{"type":"tool-call","id":"call_reuse","name":"run_code","arguments":"{\"code\":\"const childProcess = await import(\\\"node:child_process\\\"); childProcess.execFileSync(process.execPath, [\\\"-e\\\", \\\"process.stdout.write(\\\\\\\"raw child output\\\\\\\")\\\"], { stdio: \\\"inherit\\\" }); await fs.writeFile(\\\"persistent-count.txt\\\", String(increment()) + \\\"\\\\n\\\"); const result = await report({command:\\\"pwd\\\",description:\\\"Read current working directory\\\"}); ({ value: counter.value, same: alias === counter, sameDirectory: process.cwd() === result.stdout.text.trim() })\",\"description\":\"Reuse retained objects and current tool bindings\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:6}}"},"usage":{"inputTokens":10,"outputTokens":5},"stream":[{"type":"chunk","time":1791781634061,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}},{"type":"tool-call-chunks","time0":1791781634061,"index":0,"dt":[],"id":"call_reuse","name":"run_code","args":["{\"code\":\"const childProcess = await import(\\\"node:child_process\\\"); childProcess.execFileSync(process.execPath, [\\\"-e\\\", \\\"process.stdout.write(\\\\\\\"raw child output\\\\\\\")\\\"], { stdio: \\\"inherit\\\" }); await fs.writeFile(\\\"persistent-count.txt\\\", String(increment()) + \\\"\\\\n\\\"); const result = await report({command:\\\"pwd\\\",description:\\\"Read current working directory\\\"}); ({ value: counter.value, same: alias === counter, sameDirectory: process.cwd() === result.stdout.text.trim() })\",\"description\":\"Reuse retained objects and current tool bindings\"}"]},{"type":"chunk","time":1791781634061,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"call_reuse","name":"run_code","arguments":"{\"code\":\"const childProcess = await import(\\\"node:child_process\\\"); childProcess.execFileSync(process.execPath, [\\\"-e\\\", \\\"process.stdout.write(\\\\\\\"raw child output\\\\\\\")\\\"], { stdio: \\\"inherit\\\" }); await fs.writeFile(\\\"persistent-count.txt\\\", String(increment()) + \\\"\\\\n\\\"); const result = await report({command:\\\"pwd\\\",description:\\\"Read current working directory\\\"}); ({ value: counter.value, same: alias === counter, sameDirectory: process.cwd() === result.stdout.text.trim() })\",\"description\":\"Reuse retained objects and current tool bindings\"}"}}},{"type":"chunk","time":1791781634061,"chunk":{"type":"usage","usage":{"inputTokens":10,"outputTokens":5}}},{"type":"chunk","time":1791781634061,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"} -{"type":"tool/call","data":{"turn":1,"step":2,"callId":"call_reuse","name":"run_code","arguments":"{\"code\":\"const childProcess = await import(\\\"node:child_process\\\"); childProcess.execFileSync(process.execPath, [\\\"-e\\\", \\\"process.stdout.write(\\\\\\\"raw child output\\\\\\\")\\\"], { stdio: \\\"inherit\\\" }); await fs.writeFile(\\\"persistent-count.txt\\\", String(increment()) + \\\"\\\\n\\\"); const result = await report({command:\\\"pwd\\\",description:\\\"Read current working directory\\\"}); ({ value: counter.value, same: alias === counter, sameDirectory: process.cwd() === result.stdout.text.trim() })\",\"description\":\"Reuse retained objects and current tool bindings\"}"}} +{"type":"assistant/message","data":{"turn":1,"step":2,"message":{"role":"assistant","content":[{"type":"tool-call","id":"call_reuse","name":"run_code","arguments":"{\"code\":\"const childProcess = await import(\\\"node:child_process\\\"); childProcess.execFileSync(process.execPath, [\\\"-e\\\", \\\"process.stdout.write(\\\\\\\"raw child output\\\\\\\")\\\"], { stdio: \\\"inherit\\\" }); await fs.writeFile(\\\"persistent-count.txt\\\", String(increment()) + \\\"\\\\n\\\"); const result = await report({command:\\\"pwd\\\",description:\\\"Read current working directory\\\"}); Harness.inspect(JSON.stringify(({ value: counter.value, same: alias === counter, sameDirectory: process.cwd() === result.stdout.text.trim() }), null, 2))\",\"description\":\"Reuse retained objects and current tool bindings\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:6}}"},"usage":{"inputTokens":10,"outputTokens":5},"stream":[{"type":"chunk","time":1791781634061,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}},{"type":"tool-call-chunks","time0":1791781634061,"index":0,"dt":[],"id":"call_reuse","name":"run_code","args":["{\"code\":\"const childProcess = await import(\\\"node:child_process\\\"); childProcess.execFileSync(process.execPath, [\\\"-e\\\", \\\"process.stdout.write(\\\\\\\"raw child output\\\\\\\")\\\"], { stdio: \\\"inherit\\\" }); await fs.writeFile(\\\"persistent-count.txt\\\", String(increment()) + \\\"\\\\n\\\"); const result = await report({command:\\\"pwd\\\",description:\\\"Read current working directory\\\"}); Harness.inspect(JSON.stringify(({ value: counter.value, same: alias === counter, sameDirectory: process.cwd() === result.stdout.text.trim() }), null, 2))\",\"description\":\"Reuse retained objects and current tool bindings\"}"]},{"type":"chunk","time":1791781634061,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"call_reuse","name":"run_code","arguments":"{\"code\":\"const childProcess = await import(\\\"node:child_process\\\"); childProcess.execFileSync(process.execPath, [\\\"-e\\\", \\\"process.stdout.write(\\\\\\\"raw child output\\\\\\\")\\\"], { stdio: \\\"inherit\\\" }); await fs.writeFile(\\\"persistent-count.txt\\\", String(increment()) + \\\"\\\\n\\\"); const result = await report({command:\\\"pwd\\\",description:\\\"Read current working directory\\\"}); Harness.inspect(JSON.stringify(({ value: counter.value, same: alias === counter, sameDirectory: process.cwd() === result.stdout.text.trim() }), null, 2))\",\"description\":\"Reuse retained objects and current tool bindings\"}"}}},{"type":"chunk","time":1791781634061,"chunk":{"type":"usage","usage":{"inputTokens":10,"outputTokens":5}}},{"type":"chunk","time":1791781634061,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"} +{"type":"tool/call","data":{"turn":1,"step":2,"callId":"call_reuse","name":"run_code","arguments":"{\"code\":\"const childProcess = await import(\\\"node:child_process\\\"); childProcess.execFileSync(process.execPath, [\\\"-e\\\", \\\"process.stdout.write(\\\\\\\"raw child output\\\\\\\")\\\"], { stdio: \\\"inherit\\\" }); await fs.writeFile(\\\"persistent-count.txt\\\", String(increment()) + \\\"\\\\n\\\"); const result = await report({command:\\\"pwd\\\",description:\\\"Read current working directory\\\"}); Harness.inspect(JSON.stringify(({ value: counter.value, same: alias === counter, sameDirectory: process.cwd() === result.stdout.text.trim() }), null, 2))\",\"description\":\"Reuse retained objects and current tool bindings\"}"}} {"type":"tool/ptc-dispatch-start","data":{"rootCallId":"call_reuse","parentCallId":"call_reuse","subCallId":"call_reuse:ptc:1","name":"bash","arguments":{"command":"pwd","description":"Read current working directory"}}} {"type":"job/registered","data":{"jobId":"{{id:1}}","owner":"{{session:1}}","kind":"bash","label":"pwd","startedAt":1791781634097,"toolCall":{"callId":"call_reuse:ptc:1","rootCallId":"call_reuse"}}} {"type":"job/settled","data":{"jobId":"{{id:1}}","owner":"{{session:1}}","kind":"bash","label":"pwd","startedAt":1791781634097,"toolCall":{"callId":"call_reuse:ptc:1","rootCallId":"call_reuse"},"finishedAt":1791781634106,"status":"completed","cause":"producer","producerReported":true,"detail":"exit code: 0"}} @@ -31,8 +31,8 @@ {"type":"tool/result","data":{"turn":1,"step":3,"message":{"role":"tool","source":{"kind":"tool","callId":"call_exit"},"toolCallId":"call_exit","content":[{"type":"text","text":"Error: code run failed (worker-exit): Node process exited (7, signal null); retained variables were cleared\nFile sandbox: workspace-write; enforcement: full."}],"isError":true,"id":"{{message:9}}"},"error":{"name":"CodeRunFailedError","code":"CODE_RUN_FAILED"}},"sourceEventSeqs":[28],"surfaceOp":"append"} {"type":"step/end","data":{"turn":1,"step":3}} {"type":"step/start","data":{"turn":1,"step":4}} -{"type":"assistant/message","data":{"turn":1,"step":4,"message":{"role":"assistant","content":[{"type":"tool-call","id":"call_recover","name":"run_code","arguments":"{\"code\":\"let missing; try { counter.value } catch (error) { missing = error.name === \\\"ReferenceError\\\" }; const fs2 = await import(\\\"node:fs/promises\\\"); let recovered = { value: 0 }; await fs2.appendFile(\\\"persistent-count.txt\\\", \\\"recreated\\\\n\\\"); ({ missing, recreated: recovered.value })\",\"description\":\"Recreate variables after execution process exits\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:10}}"},"usage":{"inputTokens":10,"outputTokens":5},"stream":[{"type":"chunk","time":1791781634155,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}},{"type":"tool-call-chunks","time0":1791781634155,"index":0,"dt":[],"id":"call_recover","name":"run_code","args":["{\"code\":\"let missing; try { counter.value } catch (error) { missing = error.name === \\\"ReferenceError\\\" }; const fs2 = await import(\\\"node:fs/promises\\\"); let recovered = { value: 0 }; await fs2.appendFile(\\\"persistent-count.txt\\\", \\\"recreated\\\\n\\\"); ({ missing, recreated: recovered.value })\",\"description\":\"Recreate variables after execution process exits\"}"]},{"type":"chunk","time":1791781634155,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"call_recover","name":"run_code","arguments":"{\"code\":\"let missing; try { counter.value } catch (error) { missing = error.name === \\\"ReferenceError\\\" }; const fs2 = await import(\\\"node:fs/promises\\\"); let recovered = { value: 0 }; await fs2.appendFile(\\\"persistent-count.txt\\\", \\\"recreated\\\\n\\\"); ({ missing, recreated: recovered.value })\",\"description\":\"Recreate variables after execution process exits\"}"}}},{"type":"chunk","time":1791781634155,"chunk":{"type":"usage","usage":{"inputTokens":10,"outputTokens":5}}},{"type":"chunk","time":1791781634155,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"} -{"type":"tool/call","data":{"turn":1,"step":4,"callId":"call_recover","name":"run_code","arguments":"{\"code\":\"let missing; try { counter.value } catch (error) { missing = error.name === \\\"ReferenceError\\\" }; const fs2 = await import(\\\"node:fs/promises\\\"); let recovered = { value: 0 }; await fs2.appendFile(\\\"persistent-count.txt\\\", \\\"recreated\\\\n\\\"); ({ missing, recreated: recovered.value })\",\"description\":\"Recreate variables after execution process exits\"}"}} +{"type":"assistant/message","data":{"turn":1,"step":4,"message":{"role":"assistant","content":[{"type":"tool-call","id":"call_recover","name":"run_code","arguments":"{\"code\":\"let missing; try { counter.value } catch (error) { missing = error.name === \\\"ReferenceError\\\" }; const fs2 = await import(\\\"node:fs/promises\\\"); let recovered = { value: 0 }; await fs2.appendFile(\\\"persistent-count.txt\\\", \\\"recreated\\\\n\\\"); Harness.inspect(JSON.stringify(({ missing, recreated: recovered.value }), null, 2))\",\"description\":\"Recreate variables after execution process exits\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:10}}"},"usage":{"inputTokens":10,"outputTokens":5},"stream":[{"type":"chunk","time":1791781634155,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}},{"type":"tool-call-chunks","time0":1791781634155,"index":0,"dt":[],"id":"call_recover","name":"run_code","args":["{\"code\":\"let missing; try { counter.value } catch (error) { missing = error.name === \\\"ReferenceError\\\" }; const fs2 = await import(\\\"node:fs/promises\\\"); let recovered = { value: 0 }; await fs2.appendFile(\\\"persistent-count.txt\\\", \\\"recreated\\\\n\\\"); Harness.inspect(JSON.stringify(({ missing, recreated: recovered.value }), null, 2))\",\"description\":\"Recreate variables after execution process exits\"}"]},{"type":"chunk","time":1791781634155,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"call_recover","name":"run_code","arguments":"{\"code\":\"let missing; try { counter.value } catch (error) { missing = error.name === \\\"ReferenceError\\\" }; const fs2 = await import(\\\"node:fs/promises\\\"); let recovered = { value: 0 }; await fs2.appendFile(\\\"persistent-count.txt\\\", \\\"recreated\\\\n\\\"); Harness.inspect(JSON.stringify(({ missing, recreated: recovered.value }), null, 2))\",\"description\":\"Recreate variables after execution process exits\"}"}}},{"type":"chunk","time":1791781634155,"chunk":{"type":"usage","usage":{"inputTokens":10,"outputTokens":5}}},{"type":"chunk","time":1791781634155,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"} +{"type":"tool/call","data":{"turn":1,"step":4,"callId":"call_recover","name":"run_code","arguments":"{\"code\":\"let missing; try { counter.value } catch (error) { missing = error.name === \\\"ReferenceError\\\" }; const fs2 = await import(\\\"node:fs/promises\\\"); let recovered = { value: 0 }; await fs2.appendFile(\\\"persistent-count.txt\\\", \\\"recreated\\\\n\\\"); Harness.inspect(JSON.stringify(({ missing, recreated: recovered.value }), null, 2))\",\"description\":\"Recreate variables after execution process exits\"}"}} {"type":"tool/result","data":{"turn":1,"step":4,"message":{"role":"tool","source":{"kind":"tool","callId":"call_recover"},"toolCallId":"call_recover","content":[{"type":"text","text":"{\n \"missing\": true,\n \"recreated\": 0\n}"}],"isError":false,"id":"{{message:11}}"}},"sourceEventSeqs":[33],"surfaceOp":"append"} {"type":"step/end","data":{"turn":1,"step":4}} {"type":"step/start","data":{"turn":1,"step":5}} diff --git a/snapshots/session/ptc-persistent/system-prompt.expected.md b/snapshots/session/ptc-persistent/system-prompt.expected.md index 89e23ec4fa..d047f84512 100644 --- a/snapshots/session/ptc-persistent/system-prompt.expected.md +++ b/snapshots/session/ptc-persistent/system-prompt.expected.md @@ -39,20 +39,24 @@ Bash, formatters, code generators, and scripts can overwrite concurrent edits wi `run_code` takes two required arguments: `code`, the program to execute, and `description`, a short summary of what the program does. The declarations below are SDK bindings for this program. A declaration does not make its name a directly callable tool; only names supplied as separate tool schemas may be called directly. When no separate `bash` schema is supplied, invoke a declared `bash` binding inside `run_code`: -`run_code({ code: "console.log(await tools.bash({ description: 'Show current directory', command: 'pwd' }))", description: "Show current directory" })` +`run_code({ code: "Harness.inspect(await tools.bash({ description: 'Show current directory', command: 'pwd' }))", description: "Show current directory" })` Inside the program: - Call tools as `await tools.name(args)` — quoted access for exotic names: `tools["my-tool"](args)`. Every call resolves to the tool's typed canonical JSON value. Tool arguments must be lossless JSON. - A FAILED tool call rejects with `ToolCallError`, whose `toolName` identifies the failed tool and whose `message` is human-readable — `try/catch` it to handle and continue. - Independent read-only calls MAY overlap under `Promise.all` (safe calls run concurrently; mutating calls run alone, in submission order). Sequence dependent work with `await`. -- Only values emitted as program output enter the conversation. A successful tool result containing an image is attached after the run so you can inspect it on the next step; every other intermediate result stays out of the conversation, so extract just what you need. +- Show selected results with `Harness.inspect(...values)`. Return values and final expressions are not displayed. A successful tool result containing an image is attached after the run so you can inspect it on the next step; every other intermediate result stays out of the conversation, so extract just what you need. Program-only SDK bindings: ```ts type JsonValue = null | boolean | number | string | JsonValue[] | { [key: string]: JsonValue } +declare namespace Harness { + function inspect(...values: unknown[]): void; +} + interface ToolArgsMap { /** Create, update, delete, or move UTF-8 text files with one patch; prefer it over multiple edit calls when a change spans several files or separated regions. */ apply_patch: { diff --git a/snapshots/session/ptc-persistent/tool-schemas.expected.json b/snapshots/session/ptc-persistent/tool-schemas.expected.json index d28cfa0acf..8c438feb8e 100644 --- a/snapshots/session/ptc-persistent/tool-schemas.expected.json +++ b/snapshots/session/ptc-persistent/tool-schemas.expected.json @@ -2,7 +2,7 @@ "initial": [ { "name": "run_code", - "description": "Execute a TypeScript program against the available tools. Takes two required arguments: `code`, the program to execute, and `description`, a short summary of what the program does. Call tools as `await tools.name(args)` per the declarations in the system prompt. Curate the program output. Image-bearing subtool results are attached after the run. The program contains TypeScript REPL statements; only erasable syntax is accepted, with no enum or namespaces. Type annotations are advisory. Top-level declarations and await are supported; top-level return is not supported. Emit output with console.log(...) or a final lossless JSON expression. Only printed values and that final expression become program output. Variables, functions, and imported modules remain in memory for this Agent across calls. Use await import(...) for modules. Direct filesystem access follows the environment's launch-time sandbox policy; process.env starts empty. Timeout and cancellation results say whether retained variables were preserved or cleared. Restart, sandbox changes, output overflow, or process failure clears them. Recreate missing variables and resources; conversation history does not restore state. Earlier mutations and tool effects are not rolled back. Approved sandbox escalations use a fresh environment and discard its variables afterward. Do not redeclare existing let, const, or class names; reuse mutable bindings or choose new names. Await tools and cleanup before returning; unfinished tools reject. After completion, timeout, or cancellation, new tool calls are rejected and captured output is discarded; background work may still change state. Await child-process completion before returning; raw output belongs to the active evaluation. Each program starts in the Session's current directory. Running programs keep their initial directory. A sandbox escalation approves this complete program for one execution only. Nested tools retain their own policies and approvals. Request wider access only after evidence of a denial. Earlier effects may already have completed: inspect them before explicitly retrying. Programs are never replayed automatically.", + "description": "Execute a TypeScript program against the available tools. Takes two required arguments: `code`, the program to execute, and `description`, a short summary of what the program does. Call tools as `await tools.name(args)` per the declarations in the system prompt. Curate the program output. Image-bearing subtool results are attached after the run. The program contains TypeScript REPL statements; only erasable syntax is accepted, with no enum or namespaces. Type annotations are advisory. Top-level declarations and await are supported; top-level return is not supported. Console is unavailable. Use Harness.inspect(...values) for output. Variables, functions, and imported modules remain in memory for this Agent across calls. Use await import(...) for modules. Direct filesystem access follows the environment's launch-time sandbox policy; process.env starts empty. Timeout and cancellation results say whether retained variables were preserved or cleared. Restart, sandbox changes, output overflow, or process failure clears them. Recreate missing variables and resources; conversation history does not restore state. Earlier mutations and tool effects are not rolled back. Approved sandbox escalations use a fresh environment and discard its variables afterward. Do not redeclare existing let, const, or class names; reuse mutable bindings or choose new names. Await tools and cleanup before returning; unfinished tools reject. After completion, timeout, or cancellation, new tool calls are rejected and captured output is discarded; background work may still change state. Await child-process completion before returning; raw output belongs to the active evaluation. Each program starts in the Session's current directory. Running programs keep their initial directory. A sandbox escalation approves this complete program for one execution only. Nested tools retain their own policies and approvals. Request wider access only after evidence of a denial. Earlier effects may already have completed: inspect them before explicitly retrying. Programs are never replayed automatically.", "parameters": { "type": "object", "properties": { diff --git a/snapshots/session/ptc-policy-change/replay.override.json b/snapshots/session/ptc-policy-change/replay.override.json index 80914aceaa..5f7a00c867 100644 --- a/snapshots/session/ptc-policy-change/replay.override.json +++ b/snapshots/session/ptc-policy-change/replay.override.json @@ -12,7 +12,7 @@ "index": 0, "id": "call_wait", "name": "run_code", - "argumentsDelta": "{\"description\":\"Wait for an external file policy change\",\"code\":\"await tools.wait_for_mode_change({}); await new Promise(() => {});\"}" + "argumentsDelta": "{\"description\":\"Wait for an external file policy change\",\"code\":\"await tools.wait_for_mode_change({}); Harness.inspect(await new Promise(() => {}));\"}" }, { "type": "block-end", @@ -21,7 +21,7 @@ "type": "tool-call", "id": "call_wait", "name": "run_code", - "arguments": "{\"description\":\"Wait for an external file policy change\",\"code\":\"await tools.wait_for_mode_change({}); await new Promise(() => {});\"}" + "arguments": "{\"description\":\"Wait for an external file policy change\",\"code\":\"await tools.wait_for_mode_change({}); Harness.inspect(await new Promise(() => {}));\"}" } }, { @@ -52,7 +52,7 @@ "index": 0, "id": "call_check", "name": "run_code", - "argumentsDelta": "{\"description\":\"Verify the new file policy denies writes\",\"code\":\"const fileSystem = await import(\\\"node:fs/promises\\\"); let denied = false; try { await fileSystem.writeFile(\\\"denied.txt\\\", \\\"unexpected\\\") } catch (error) { denied = [\\\"EPERM\\\", \\\"EACCES\\\", \\\"EROFS\\\"].includes(error.code) }; console.log(JSON.stringify({ denied }));\"}" + "argumentsDelta": "{\"description\":\"Verify the new file policy denies writes\",\"code\":\"const fileSystem = await import(\\\"node:fs/promises\\\"); let denied = false; try { await fileSystem.writeFile(\\\"denied.txt\\\", \\\"unexpected\\\") } catch (error) { denied = [\\\"EPERM\\\", \\\"EACCES\\\", \\\"EROFS\\\"].includes(error.code) }; Harness.inspect(JSON.stringify({ denied }));\"}" }, { "type": "block-end", @@ -61,7 +61,7 @@ "type": "tool-call", "id": "call_check", "name": "run_code", - "arguments": "{\"description\":\"Verify the new file policy denies writes\",\"code\":\"const fileSystem = await import(\\\"node:fs/promises\\\"); let denied = false; try { await fileSystem.writeFile(\\\"denied.txt\\\", \\\"unexpected\\\") } catch (error) { denied = [\\\"EPERM\\\", \\\"EACCES\\\", \\\"EROFS\\\"].includes(error.code) }; console.log(JSON.stringify({ denied }));\"}" + "arguments": "{\"description\":\"Verify the new file policy denies writes\",\"code\":\"const fileSystem = await import(\\\"node:fs/promises\\\"); let denied = false; try { await fileSystem.writeFile(\\\"denied.txt\\\", \\\"unexpected\\\") } catch (error) { denied = [\\\"EPERM\\\", \\\"EACCES\\\", \\\"EROFS\\\"].includes(error.code) }; Harness.inspect(JSON.stringify({ denied }));\"}" } }, { diff --git a/snapshots/session/ptc-policy-change/session.v4.jsonl b/snapshots/session/ptc-policy-change/session.v4.jsonl index fcbaac7771..d65c06949c 100644 --- a/snapshots/session/ptc-policy-change/session.v4.jsonl +++ b/snapshots/session/ptc-policy-change/session.v4.jsonl @@ -12,8 +12,8 @@ {"type":"request/header","data":{"header":{"config":{"provider":"deepseek-official","model":"deepseek-v4-flash"},"tools":"{{tools}}"},"reason":"initial"}} {"type":"request/context","data":{"provider":"deepseek-official","model":"deepseek-v4-flash"}} {"type":"session/title","data":{"title":"Retain a counter, imported module","messageSeqs":[8],"source":{"kind":"fallback"}}} -{"type":"assistant/message","data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"tool-call","id":"call_wait","name":"run_code","arguments":"{\"description\":\"Wait for an external file policy change\",\"code\":\"await tools.wait_for_mode_change({}); await new Promise(() => {});\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:4}}"},"usage":{"inputTokens":10,"outputTokens":5},"stream":[{"type":"chunk","time":1791529537983,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}},{"type":"tool-call-chunks","time0":1791529537983,"index":0,"dt":[],"id":"call_wait","name":"run_code","args":["{\"description\":\"Wait for an external file policy change\",\"code\":\"await tools.wait_for_mode_change({}); await new Promise(() => {});\"}"]},{"type":"chunk","time":1791529537983,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"call_wait","name":"run_code","arguments":"{\"description\":\"Wait for an external file policy change\",\"code\":\"await tools.wait_for_mode_change({}); await new Promise(() => {});\"}"}}},{"type":"chunk","time":1791529537983,"chunk":{"type":"usage","usage":{"inputTokens":10,"outputTokens":5}}},{"type":"chunk","time":1791529537983,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"} -{"type":"tool/call","data":{"turn":1,"step":1,"callId":"call_wait","name":"run_code","arguments":"{\"description\":\"Wait for an external file policy change\",\"code\":\"await tools.wait_for_mode_change({}); await new Promise(() => {});\"}"}} +{"type":"assistant/message","data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"tool-call","id":"call_wait","name":"run_code","arguments":"{\"description\":\"Wait for an external file policy change\",\"code\":\"await tools.wait_for_mode_change({}); Harness.inspect(await new Promise(() => {}));\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:4}}"},"usage":{"inputTokens":10,"outputTokens":5},"stream":[{"type":"chunk","time":1791529537983,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}},{"type":"tool-call-chunks","time0":1791529537983,"index":0,"dt":[],"id":"call_wait","name":"run_code","args":["{\"description\":\"Wait for an external file policy change\",\"code\":\"await tools.wait_for_mode_change({}); Harness.inspect(await new Promise(() => {}));\"}"]},{"type":"chunk","time":1791529537983,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"call_wait","name":"run_code","arguments":"{\"description\":\"Wait for an external file policy change\",\"code\":\"await tools.wait_for_mode_change({}); Harness.inspect(await new Promise(() => {}));\"}"}}},{"type":"chunk","time":1791529537983,"chunk":{"type":"usage","usage":{"inputTokens":10,"outputTokens":5}}},{"type":"chunk","time":1791529537983,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"} +{"type":"tool/call","data":{"turn":1,"step":1,"callId":"call_wait","name":"run_code","arguments":"{\"description\":\"Wait for an external file policy change\",\"code\":\"await tools.wait_for_mode_change({}); Harness.inspect(await new Promise(() => {}));\"}"}} {"type":"tool/ptc-dispatch-start","data":{"rootCallId":"call_wait","parentCallId":"call_wait","subCallId":"call_wait:ptc:1","name":"wait_for_mode_change","arguments":{}}} {"type":"sandbox/mode","data":{"mode":"read-only"}} {"type":"tool/ptc-dispatch","data":{"rootCallId":"call_wait","parentCallId":"call_wait","subCallId":"call_wait:ptc:1","name":"wait_for_mode_change","arguments":{},"isError":true,"error":{"name":"AbortError","code":"ABORTED"},"content":[{"type":"text","text":"Error: tool call aborted"}]}} @@ -21,8 +21,8 @@ {"type":"step/end","data":{"turn":1,"step":1}} {"type":"step/start","data":{"turn":1,"step":2}} {"type":"user/message","data":{"content":[{"type":"text","text":"\nThe following information describes your current session. It replaces earlier versions of this context, including any retained in conversation summaries.\n\nCurrent working directory: \"{{cwd}}\".\n\nCurrent DSH file policy: read-only. Any available operation enforced by the DSH file sandbox cannot modify files in the standing mode. Do not refuse a required modification from this policy alone: try an available tool normally and follow any denial and escalation guidance it returns.\n\nApproval prompts are enabled in this session: operations that require approval may ask through the configured answerers; without an available answerer, the request fails closed.\n\nYou are the root agent. Your name is \"lead\".\n\nTo message other agents, use send_message({ agent: \"\", message: \"...\" }).\nUse list_agents({}) to find your collaborator agents and their names.\n"}],"source":{"kind":"runtime-context","form":"snapshot","sections":[{"name":"working-directory:current","text":"Current working directory: \"{{cwd}}\"."},{"name":"sandbox:policy","text":"Current DSH file policy: read-only. Any available operation enforced by the DSH file sandbox cannot modify files in the standing mode. Do not refuse a required modification from this policy alone: try an available tool normally and follow any denial and escalation guidance it returns."},{"name":"approval:policy","text":"Approval prompts are enabled in this session: operations that require approval may ask through the configured answerers; without an available answerer, the request fails closed."},{"name":"subagent:identity","text":"You are the root agent. Your name is \"lead\".\n\nTo message other agents, use send_message({ agent: \"\", message: \"...\" }).\nUse list_agents({}) to find your collaborator agents and their names."}]},"role":"user","id":"{{message:6}}"},"surfaceOp":"append"} -{"type":"assistant/message","data":{"turn":1,"step":2,"message":{"role":"assistant","content":[{"type":"tool-call","id":"call_check","name":"run_code","arguments":"{\"description\":\"Verify the new file policy denies writes\",\"code\":\"const fileSystem = await import(\\\"node:fs/promises\\\"); let denied = false; try { await fileSystem.writeFile(\\\"denied.txt\\\", \\\"unexpected\\\") } catch (error) { denied = [\\\"EPERM\\\", \\\"EACCES\\\", \\\"EROFS\\\"].includes(error.code) }; console.log(JSON.stringify({ denied }));\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:7}}"},"usage":{"inputTokens":10,"outputTokens":5},"stream":[{"type":"chunk","time":1791529538136,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}},{"type":"tool-call-chunks","time0":1791529538136,"index":0,"dt":[],"id":"call_check","name":"run_code","args":["{\"description\":\"Verify the new file policy denies writes\",\"code\":\"const fileSystem = await import(\\\"node:fs/promises\\\"); let denied = false; try { await fileSystem.writeFile(\\\"denied.txt\\\", \\\"unexpected\\\") } catch (error) { denied = [\\\"EPERM\\\", \\\"EACCES\\\", \\\"EROFS\\\"].includes(error.code) }; console.log(JSON.stringify({ denied }));\"}"]},{"type":"chunk","time":1791529538136,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"call_check","name":"run_code","arguments":"{\"description\":\"Verify the new file policy denies writes\",\"code\":\"const fileSystem = await import(\\\"node:fs/promises\\\"); let denied = false; try { await fileSystem.writeFile(\\\"denied.txt\\\", \\\"unexpected\\\") } catch (error) { denied = [\\\"EPERM\\\", \\\"EACCES\\\", \\\"EROFS\\\"].includes(error.code) }; console.log(JSON.stringify({ denied }));\"}"}}},{"type":"chunk","time":1791529538136,"chunk":{"type":"usage","usage":{"inputTokens":10,"outputTokens":5}}},{"type":"chunk","time":1791529538136,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"} -{"type":"tool/call","data":{"turn":1,"step":2,"callId":"call_check","name":"run_code","arguments":"{\"description\":\"Verify the new file policy denies writes\",\"code\":\"const fileSystem = await import(\\\"node:fs/promises\\\"); let denied = false; try { await fileSystem.writeFile(\\\"denied.txt\\\", \\\"unexpected\\\") } catch (error) { denied = [\\\"EPERM\\\", \\\"EACCES\\\", \\\"EROFS\\\"].includes(error.code) }; console.log(JSON.stringify({ denied }));\"}"}} +{"type":"assistant/message","data":{"turn":1,"step":2,"message":{"role":"assistant","content":[{"type":"tool-call","id":"call_check","name":"run_code","arguments":"{\"description\":\"Verify the new file policy denies writes\",\"code\":\"const fileSystem = await import(\\\"node:fs/promises\\\"); let denied = false; try { await fileSystem.writeFile(\\\"denied.txt\\\", \\\"unexpected\\\") } catch (error) { denied = [\\\"EPERM\\\", \\\"EACCES\\\", \\\"EROFS\\\"].includes(error.code) }; Harness.inspect(JSON.stringify({ denied }));\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:7}}"},"usage":{"inputTokens":10,"outputTokens":5},"stream":[{"type":"chunk","time":1791529538136,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}},{"type":"tool-call-chunks","time0":1791529538136,"index":0,"dt":[],"id":"call_check","name":"run_code","args":["{\"description\":\"Verify the new file policy denies writes\",\"code\":\"const fileSystem = await import(\\\"node:fs/promises\\\"); let denied = false; try { await fileSystem.writeFile(\\\"denied.txt\\\", \\\"unexpected\\\") } catch (error) { denied = [\\\"EPERM\\\", \\\"EACCES\\\", \\\"EROFS\\\"].includes(error.code) }; Harness.inspect(JSON.stringify({ denied }));\"}"]},{"type":"chunk","time":1791529538136,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"call_check","name":"run_code","arguments":"{\"description\":\"Verify the new file policy denies writes\",\"code\":\"const fileSystem = await import(\\\"node:fs/promises\\\"); let denied = false; try { await fileSystem.writeFile(\\\"denied.txt\\\", \\\"unexpected\\\") } catch (error) { denied = [\\\"EPERM\\\", \\\"EACCES\\\", \\\"EROFS\\\"].includes(error.code) }; Harness.inspect(JSON.stringify({ denied }));\"}"}}},{"type":"chunk","time":1791529538136,"chunk":{"type":"usage","usage":{"inputTokens":10,"outputTokens":5}}},{"type":"chunk","time":1791529538136,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"} +{"type":"tool/call","data":{"turn":1,"step":2,"callId":"call_check","name":"run_code","arguments":"{\"description\":\"Verify the new file policy denies writes\",\"code\":\"const fileSystem = await import(\\\"node:fs/promises\\\"); let denied = false; try { await fileSystem.writeFile(\\\"denied.txt\\\", \\\"unexpected\\\") } catch (error) { denied = [\\\"EPERM\\\", \\\"EACCES\\\", \\\"EROFS\\\"].includes(error.code) }; Harness.inspect(JSON.stringify({ denied }));\"}"}} {"type":"tool/result","data":{"turn":1,"step":2,"message":{"role":"tool","source":{"kind":"tool","callId":"call_check"},"toolCallId":"call_check","content":[{"type":"text","text":"{\"denied\":true}"}],"isError":false,"id":"{{message:8}}"}},"sourceEventSeqs":[23],"surfaceOp":"append"} {"type":"step/end","data":{"turn":1,"step":2}} {"type":"step/start","data":{"turn":1,"step":3}} diff --git a/snapshots/session/ptc-policy-change/system-prompt.expected.md b/snapshots/session/ptc-policy-change/system-prompt.expected.md index ca761dccb8..a413c8a800 100644 --- a/snapshots/session/ptc-policy-change/system-prompt.expected.md +++ b/snapshots/session/ptc-policy-change/system-prompt.expected.md @@ -39,20 +39,24 @@ Bash, formatters, code generators, and scripts can overwrite concurrent edits wi `run_code` takes two required arguments: `code`, the body of an async TypeScript function (erasable syntax only — no `enum` or namespaces; type annotations are advisory, the code runs type-stripped), and `description`, a short summary of what the program does. The declarations below are SDK bindings for this program. A declaration does not make its name a directly callable tool; only names supplied as separate tool schemas may be called directly. When no separate `bash` schema is supplied, invoke a declared `bash` binding inside `run_code`: -`run_code({ code: "return await tools.bash({ description: 'Show current directory', command: 'pwd' })", description: "Show current directory" })` +`run_code({ code: "Harness.inspect(await tools.bash({ description: 'Show current directory', command: 'pwd' }))", description: "Show current directory" })` Inside the program: - Call tools as `await tools.name(args)` — quoted access for exotic names: `tools["my-tool"](args)`. Every call resolves to the tool's typed canonical JSON value. Tool arguments must be lossless JSON. - A FAILED tool call rejects with `ToolCallError`, whose `toolName` identifies the failed tool and whose `message` is human-readable — `try/catch` it to handle and continue. - Independent read-only calls MAY overlap under `Promise.all` (safe calls run concurrently; mutating calls run alone, in submission order). Sequence dependent work with `await`. -- Emit results with `return` and/or `console.log(...)`. Only what you print or return is program output. A successful tool result containing an image is attached after the run so you can inspect it on the next step; every other intermediate result stays out of the conversation, so extract just what you need. +- Show selected results with `Harness.inspect(...values)`. Return values and final expressions are not displayed. A successful tool result containing an image is attached after the run so you can inspect it on the next step; every other intermediate result stays out of the conversation, so extract just what you need. Program-only SDK bindings: ```ts type JsonValue = null | boolean | number | string | JsonValue[] | { [key: string]: JsonValue } +declare namespace Harness { + function inspect(...values: unknown[]): void; +} + interface ToolArgsMap { /** Create, update, delete, or move UTF-8 text files with one patch; prefer it over multiple edit calls when a change spans several files or separated regions. */ apply_patch: { diff --git a/snapshots/session/ptc-read-image-attachment-path/replay.override.json b/snapshots/session/ptc-read-image-attachment-path/replay.override.json index c1b0ca939f..02512a19e7 100644 --- a/snapshots/session/ptc-read-image-attachment-path/replay.override.json +++ b/snapshots/session/ptc-read-image-attachment-path/replay.override.json @@ -3,7 +3,7 @@ "kind": "chunks", "chunks": [ { "type": "block-start", "index": 0, "blockType": "tool-call" }, - { "type": "block-end", "index": 0, "block": { "type": "tool-call", "id": "code-image-source", "name": "run_code", "arguments": "{\"code\":\"return await tools.read_image({ file_path: 'red.png' })\",\"description\":\"Read the source image\"}" } }, + { "type": "block-end", "index": 0, "block": { "type": "tool-call", "id": "code-image-source", "name": "run_code", "arguments": "{\"code\":\"Harness.inspect(await tools.read_image({ file_path: 'red.png' })); return;\",\"description\":\"Read the source image\"}" } }, { "type": "usage", "usage": { "inputTokens": 3, "outputTokens": 3 } }, { "type": "finish", "reason": { "kind": "tool-calls" } } ] diff --git a/snapshots/session/ptc-read-image-attachment-path/session.v4.jsonl b/snapshots/session/ptc-read-image-attachment-path/session.v4.jsonl index edb1142639..68f132e766 100644 --- a/snapshots/session/ptc-read-image-attachment-path/session.v4.jsonl +++ b/snapshots/session/ptc-read-image-attachment-path/session.v4.jsonl @@ -12,11 +12,11 @@ {"type":"request/header","data":{"header":{"config":{"provider":"deepseek-official","model":"deepseek-v4-flash-vision-exp"},"tools":"{{tools}}"},"reason":"initial"}} {"type":"request/context","data":{"provider":"deepseek-official","model":"deepseek-v4-flash-vision-exp"}} {"type":"session/title","data":{"title":"Using run_code, call read_image on","messageSeqs":[8],"source":{"kind":"fallback"}}} -{"type":"assistant/message","data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"tool-call","id":"code-image-source","name":"run_code","arguments":"{\"code\":\"return await tools.read_image({ file_path: 'red.png' })\",\"description\":\"Read the source image\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash-vision-exp"},"id":"{{message:4}}"},"usage":{"inputTokens":3,"outputTokens":3},"stream":[{"type":"chunk","time":1788699406766,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}},{"type":"chunk","time":1788699406766,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"code-image-source","name":"run_code","arguments":"{\"code\":\"return await tools.read_image({ file_path: 'red.png' })\",\"description\":\"Read the source image\"}"}}},{"type":"chunk","time":1788699406766,"chunk":{"type":"usage","usage":{"inputTokens":3,"outputTokens":3}}},{"type":"chunk","time":1788699406766,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"} -{"type":"tool/call","data":{"turn":1,"step":1,"callId":"code-image-source","name":"run_code","arguments":"{\"code\":\"return await tools.read_image({ file_path: 'red.png' })\",\"description\":\"Read the source image\"}"}} +{"type":"assistant/message","data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"tool-call","id":"code-image-source","name":"run_code","arguments":"{\"code\":\"Harness.inspect(await tools.read_image({ file_path: 'red.png' })); return;\",\"description\":\"Read the source image\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash-vision-exp"},"id":"{{message:4}}"},"usage":{"inputTokens":3,"outputTokens":3},"stream":[{"type":"chunk","time":1788699406766,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}},{"type":"chunk","time":1788699406766,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"code-image-source","name":"run_code","arguments":"{\"code\":\"Harness.inspect(await tools.read_image({ file_path: 'red.png' })); return;\",\"description\":\"Read the source image\"}"}}},{"type":"chunk","time":1788699406766,"chunk":{"type":"usage","usage":{"inputTokens":3,"outputTokens":3}}},{"type":"chunk","time":1788699406766,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"} +{"type":"tool/call","data":{"turn":1,"step":1,"callId":"code-image-source","name":"run_code","arguments":"{\"code\":\"Harness.inspect(await tools.read_image({ file_path: 'red.png' })); return;\",\"description\":\"Read the source image\"}"}} {"type":"tool/ptc-dispatch-start","data":{"rootCallId":"code-image-source","parentCallId":"code-image-source","subCallId":"code-image-source:ptc:1","name":"read_image","arguments":{"file_path":"red.png"}}} {"type":"tool/ptc-dispatch","data":{"rootCallId":"code-image-source","parentCallId":"code-image-source","subCallId":"code-image-source:ptc:1","name":"read_image","arguments":{"file_path":"red.png"},"isError":false,"content":[{"type":"text","text":"{{cwd}}/red.png\nimage\n\nimage/png image, 1x1 px, 69 bytes\n"},{"type":"image","attachment":{"attachmentId":"sha256:b1ff9c8ea3a780bad09b346c423d2d0e46815926879b18e841d928376a946640","mediaType":"image/png","bytes":69,"width":1,"height":1,"name":"red.png"}}],"meta":{"path":"{{cwd}}/red.png"}}} -{"type":"tool/result","data":{"turn":1,"step":1,"message":{"role":"tool","source":{"kind":"tool","callId":"code-image-source"},"toolCallId":"code-image-source","content":[{"type":"text","text":"{\n \"path\": \"{{cwd}}/red.png\",\n \"image\": {\n \"attachmentId\": \"sha256:b1ff9c8ea3a780bad09b346c423d2d0e46815926879b18e841d928376a946640\",\n \"mediaType\": \"image/png\",\n \"bytes\": 69,\n \"width\": 1,\n \"height\": 1,\n \"name\": \"red.png\"\n }\n}"}],"isError":false,"id":"{{message:5}}"}},"sourceEventSeqs":[14],"surfaceOp":"append"} +{"type":"tool/result","data":{"turn":1,"step":1,"message":{"role":"tool","source":{"kind":"tool","callId":"code-image-source"},"toolCallId":"code-image-source","content":[{"type":"text","text":"{\n path: '{{cwd}}/red.png',\n image: {\n attachmentId: 'sha256:b1ff9c8ea3a780bad09b346c423d2d0e46815926879b18e841d928376a946640',\n mediaType: 'image/png',\n bytes: 69,\n width: 1,\n height: 1,\n name: 'red.png'\n }\n}"}],"isError":false,"id":"{{message:5}}"}},"sourceEventSeqs":[14],"surfaceOp":"append"} {"type":"agent/inbox/spliced","data":{"target":"next-step","start":0,"inserted":[{"content":[{"type":"text","text":"{{cwd}}/red.png\nimage\n\nimage/png image, 1x1 px, 69 bytes\n"},{"type":"image","attachment":{"attachmentId":"sha256:b1ff9c8ea3a780bad09b346c423d2d0e46815926879b18e841d928376a946640","mediaType":"image/png","bytes":69,"width":1,"height":1,"name":"red.png"}}],"source":{"kind":"ptc-mode"},"role":"user","id":"{{message:6}}"}]}} {"type":"step/end","data":{"turn":1,"step":1}} {"type":"agent/inbox/spliced","data":{"target":"next-step","start":0,"removedCount":1,"inserted":[]}} @@ -26,7 +26,7 @@ {"type":"tool/call","data":{"turn":1,"step":2,"callId":"code-image-object","name":"run_code","arguments":"{\"code\":\"return await tools.read_image({ file_path: '{{cwd}}/.dsh/attachments/v1/objects/b1/b1ff9c8ea3a780bad09b346c423d2d0e46815926879b18e841d928376a946640' })\",\"description\":\"Read the normalized attachment object directly\"}"}} {"type":"tool/ptc-dispatch-start","data":{"rootCallId":"code-image-object","parentCallId":"code-image-object","subCallId":"code-image-object:ptc:1","name":"read_image","arguments":{"file_path":"{{cwd}}/.dsh/attachments/v1/objects/b1/b1ff9c8ea3a780bad09b346c423d2d0e46815926879b18e841d928376a946640"}}} {"type":"tool/ptc-dispatch","data":{"rootCallId":"code-image-object","parentCallId":"code-image-object","subCallId":"code-image-object:ptc:1","name":"read_image","arguments":{"file_path":"{{cwd}}/.dsh/attachments/v1/objects/b1/b1ff9c8ea3a780bad09b346c423d2d0e46815926879b18e841d928376a946640"},"isError":false,"content":[{"type":"text","text":"{{cwd}}/.dsh/attachments/v1/objects/b1/b1ff9c8ea3a780bad09b346c423d2d0e46815926879b18e841d928376a946640\nimage\n\nimage/png image, 1x1 px, 69 bytes\n"},{"type":"image","attachment":{"attachmentId":"sha256:b1ff9c8ea3a780bad09b346c423d2d0e46815926879b18e841d928376a946640","mediaType":"image/png","bytes":69,"width":1,"height":1,"name":"b1ff9c8ea3a780bad09b346c423d2d0e46815926879b18e841d928376a946640"}}],"meta":{"path":"{{cwd}}/.dsh/attachments/v1/objects/b1/b1ff9c8ea3a780bad09b346c423d2d0e46815926879b18e841d928376a946640"}}} -{"type":"tool/result","data":{"turn":1,"step":2,"message":{"role":"tool","source":{"kind":"tool","callId":"code-image-object"},"toolCallId":"code-image-object","content":[{"type":"text","text":"{\n \"path\": \"{{cwd}}/.dsh/attachments/v1/objects/b1/b1ff9c8ea3a780bad09b346c423d2d0e46815926879b18e841d928376a946640\",\n \"image\": {\n \"attachmentId\": \"sha256:b1ff9c8ea3a780bad09b346c423d2d0e46815926879b18e841d928376a946640\",\n \"mediaType\": \"image/png\",\n \"bytes\": 69,\n \"width\": 1,\n \"height\": 1,\n \"name\": \"b1ff9c8ea3a780bad09b346c423d2d0e46815926879b18e841d928376a946640\"\n }\n}"}],"isError":false,"id":"{{message:8}}"}},"sourceEventSeqs":[24],"surfaceOp":"append"} +{"type":"tool/result","data":{"turn":1,"step":2,"message":{"role":"tool","source":{"kind":"tool","callId":"code-image-object"},"toolCallId":"code-image-object","content":[{"type":"text","text":"(run_code completed with no output)"}],"isError":false,"id":"{{message:8}}"}},"sourceEventSeqs":[24],"surfaceOp":"append"} {"type":"agent/inbox/spliced","data":{"target":"next-step","start":0,"inserted":[{"content":[{"type":"text","text":"{{cwd}}/.dsh/attachments/v1/objects/b1/b1ff9c8ea3a780bad09b346c423d2d0e46815926879b18e841d928376a946640\nimage\n\nimage/png image, 1x1 px, 69 bytes\n"},{"type":"image","attachment":{"attachmentId":"sha256:b1ff9c8ea3a780bad09b346c423d2d0e46815926879b18e841d928376a946640","mediaType":"image/png","bytes":69,"width":1,"height":1,"name":"b1ff9c8ea3a780bad09b346c423d2d0e46815926879b18e841d928376a946640"}}],"source":{"kind":"ptc-mode"},"role":"user","id":"{{message:9}}"}]}} {"type":"step/end","data":{"turn":1,"step":2}} {"type":"agent/inbox/spliced","data":{"target":"next-step","start":0,"removedCount":1,"inserted":[]}} diff --git a/snapshots/session/ptc-read-image/replay.override.json b/snapshots/session/ptc-read-image/replay.override.json new file mode 100644 index 0000000000..261d10b1bc --- /dev/null +++ b/snapshots/session/ptc-read-image/replay.override.json @@ -0,0 +1,20 @@ +[ + { + "kind": "chunks", + "chunks": [ + {"type":"block-start","index":0,"blockType":"tool-call"}, + {"type":"block-end","index":0,"block":{"type":"tool-call","id":"code-image-call","name":"run_code","arguments":"{\"code\":\"const bytes = [137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,1,0,0,0,1,8,2,0,0,0,144,119,83,222,0,0,0,12,73,68,65,84,120,156,99,248,207,192,0,0,3,1,1,0,201,254,146,239,0,0,0,0,73,69,78,68,174,66,96,130];\\nawait tools.bash({ command: \\\"node -e \\\\\\\"require('node:fs').writeFileSync('red.png',Buffer.from([137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,1,0,0,0,1,8,2,0,0,0,144,119,83,222,0,0,0,12,73,68,65,84,120,156,99,248,207,192,0,0,3,1,1,0,201,254,146,239,0,0,0,0,73,69,78,68,174,66,96,130]));\\\\\\\"\\\", description: \\\"Create a one pixel PNG\\\" });\\nconst image = await tools.read_image({ file_path: \\\"red.png\\\" });\\nHarness.inspect(image.path); return;\",\"description\":\"Create and inspect one image\"}"}}, + {"type":"usage","usage":{"inputTokens":3,"outputTokens":3}}, + {"type":"finish","reason":{"kind":"tool-calls"}} + ] + }, + { + "kind": "chunks", + "chunks": [ + {"type":"block-start","index":0,"blockType":"text"}, + {"type":"block-end","index":0,"block":{"type":"text","text":"DONE"}}, + {"type":"usage","usage":{"inputTokens":3,"outputTokens":3}}, + {"type":"finish","reason":{"kind":"stop"}} + ] + } +] diff --git a/snapshots/session/ptc-read-image/session.v4.jsonl b/snapshots/session/ptc-read-image/session.v4.jsonl index d6263f7b91..0376685701 100644 --- a/snapshots/session/ptc-read-image/session.v4.jsonl +++ b/snapshots/session/ptc-read-image/session.v4.jsonl @@ -12,8 +12,8 @@ {"type":"request/header","data":{"header":{"config":{"provider":"deepseek-official","model":"deepseek-v4-flash-vision-exp"},"tools":"{{tools}}"},"reason":"initial"}} {"type":"request/context","data":{"provider":"deepseek-official","model":"deepseek-v4-flash-vision-exp"}} {"type":"session/title","data":{"title":"Using ONE run_code program, create","messageSeqs":[8],"source":{"kind":"fallback"}}} -{"type":"assistant/message","data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"tool-call","id":"code-image-call","name":"run_code","arguments":"{\"code\":\"const bytes = [137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,1,0,0,0,1,8,2,0,0,0,144,119,83,222,0,0,0,12,73,68,65,84,120,156,99,248,207,192,0,0,3,1,1,0,201,254,146,239,0,0,0,0,73,69,78,68,174,66,96,130];\\nawait tools.bash({ command: \\\"node -e \\\\\\\"require('node:fs').writeFileSync('red.png',Buffer.from([137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,1,0,0,0,1,8,2,0,0,0,144,119,83,222,0,0,0,12,73,68,65,84,120,156,99,248,207,192,0,0,3,1,1,0,201,254,146,239,0,0,0,0,73,69,78,68,174,66,96,130]));\\\\\\\"\\\", description: \\\"Create a one pixel PNG\\\" });\\nconst image = await tools.read_image({ file_path: \\\"red.png\\\" });\\nreturn image.path;\",\"description\":\"Create and inspect one image\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash-vision-exp"},"id":"{{message:4}}"},"usage":{"inputTokens":3,"outputTokens":3},"stream":[{"type":"chunk","time":1791427871134,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}},{"type":"chunk","time":1791427871134,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"code-image-call","name":"run_code","arguments":"{\"code\":\"const bytes = [137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,1,0,0,0,1,8,2,0,0,0,144,119,83,222,0,0,0,12,73,68,65,84,120,156,99,248,207,192,0,0,3,1,1,0,201,254,146,239,0,0,0,0,73,69,78,68,174,66,96,130];\\nawait tools.bash({ command: \\\"node -e \\\\\\\"require('node:fs').writeFileSync('red.png',Buffer.from([137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,1,0,0,0,1,8,2,0,0,0,144,119,83,222,0,0,0,12,73,68,65,84,120,156,99,248,207,192,0,0,3,1,1,0,201,254,146,239,0,0,0,0,73,69,78,68,174,66,96,130]));\\\\\\\"\\\", description: \\\"Create a one pixel PNG\\\" });\\nconst image = await tools.read_image({ file_path: \\\"red.png\\\" });\\nreturn image.path;\",\"description\":\"Create and inspect one image\"}"}}},{"type":"chunk","time":1791427871135,"chunk":{"type":"usage","usage":{"inputTokens":3,"outputTokens":3}}},{"type":"chunk","time":1791427871135,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"} -{"type":"tool/call","data":{"turn":1,"step":1,"callId":"code-image-call","name":"run_code","arguments":"{\"code\":\"const bytes = [137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,1,0,0,0,1,8,2,0,0,0,144,119,83,222,0,0,0,12,73,68,65,84,120,156,99,248,207,192,0,0,3,1,1,0,201,254,146,239,0,0,0,0,73,69,78,68,174,66,96,130];\\nawait tools.bash({ command: \\\"node -e \\\\\\\"require('node:fs').writeFileSync('red.png',Buffer.from([137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,1,0,0,0,1,8,2,0,0,0,144,119,83,222,0,0,0,12,73,68,65,84,120,156,99,248,207,192,0,0,3,1,1,0,201,254,146,239,0,0,0,0,73,69,78,68,174,66,96,130]));\\\\\\\"\\\", description: \\\"Create a one pixel PNG\\\" });\\nconst image = await tools.read_image({ file_path: \\\"red.png\\\" });\\nreturn image.path;\",\"description\":\"Create and inspect one image\"}"}} +{"type":"assistant/message","data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"tool-call","id":"code-image-call","name":"run_code","arguments":"{\"code\":\"const bytes = [137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,1,0,0,0,1,8,2,0,0,0,144,119,83,222,0,0,0,12,73,68,65,84,120,156,99,248,207,192,0,0,3,1,1,0,201,254,146,239,0,0,0,0,73,69,78,68,174,66,96,130];\\nawait tools.bash({ command: \\\"node -e \\\\\\\"require('node:fs').writeFileSync('red.png',Buffer.from([137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,1,0,0,0,1,8,2,0,0,0,144,119,83,222,0,0,0,12,73,68,65,84,120,156,99,248,207,192,0,0,3,1,1,0,201,254,146,239,0,0,0,0,73,69,78,68,174,66,96,130]));\\\\\\\"\\\", description: \\\"Create a one pixel PNG\\\" });\\nconst image = await tools.read_image({ file_path: \\\"red.png\\\" });\\nHarness.inspect(image.path); return;\",\"description\":\"Create and inspect one image\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash-vision-exp"},"id":"{{message:4}}"},"usage":{"inputTokens":3,"outputTokens":3},"stream":[{"type":"chunk","time":1791427871134,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}},{"type":"chunk","time":1791427871134,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"code-image-call","name":"run_code","arguments":"{\"code\":\"const bytes = [137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,1,0,0,0,1,8,2,0,0,0,144,119,83,222,0,0,0,12,73,68,65,84,120,156,99,248,207,192,0,0,3,1,1,0,201,254,146,239,0,0,0,0,73,69,78,68,174,66,96,130];\\nawait tools.bash({ command: \\\"node -e \\\\\\\"require('node:fs').writeFileSync('red.png',Buffer.from([137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,1,0,0,0,1,8,2,0,0,0,144,119,83,222,0,0,0,12,73,68,65,84,120,156,99,248,207,192,0,0,3,1,1,0,201,254,146,239,0,0,0,0,73,69,78,68,174,66,96,130]));\\\\\\\"\\\", description: \\\"Create a one pixel PNG\\\" });\\nconst image = await tools.read_image({ file_path: \\\"red.png\\\" });\\nHarness.inspect(image.path); return;\",\"description\":\"Create and inspect one image\"}"}}},{"type":"chunk","time":1791427871135,"chunk":{"type":"usage","usage":{"inputTokens":3,"outputTokens":3}}},{"type":"chunk","time":1791427871135,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"} +{"type":"tool/call","data":{"turn":1,"step":1,"callId":"code-image-call","name":"run_code","arguments":"{\"code\":\"const bytes = [137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,1,0,0,0,1,8,2,0,0,0,144,119,83,222,0,0,0,12,73,68,65,84,120,156,99,248,207,192,0,0,3,1,1,0,201,254,146,239,0,0,0,0,73,69,78,68,174,66,96,130];\\nawait tools.bash({ command: \\\"node -e \\\\\\\"require('node:fs').writeFileSync('red.png',Buffer.from([137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,1,0,0,0,1,8,2,0,0,0,144,119,83,222,0,0,0,12,73,68,65,84,120,156,99,248,207,192,0,0,3,1,1,0,201,254,146,239,0,0,0,0,73,69,78,68,174,66,96,130]));\\\\\\\"\\\", description: \\\"Create a one pixel PNG\\\" });\\nconst image = await tools.read_image({ file_path: \\\"red.png\\\" });\\nHarness.inspect(image.path); return;\",\"description\":\"Create and inspect one image\"}"}} {"type":"tool/ptc-dispatch-start","data":{"rootCallId":"code-image-call","parentCallId":"code-image-call","subCallId":"code-image-call:ptc:1","name":"bash","arguments":{"command":"node -e \"require('node:fs').writeFileSync('red.png',Buffer.from([137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,1,0,0,0,1,8,2,0,0,0,144,119,83,222,0,0,0,12,73,68,65,84,120,156,99,248,207,192,0,0,3,1,1,0,201,254,146,239,0,0,0,0,73,69,78,68,174,66,96,130]));\"","description":"Create a one pixel PNG"}}} {"type":"job/registered","data":{"jobId":"{{id:1}}","owner":"{{session:1}}","kind":"bash","label":"node -e \"require('node:fs').writeFileSync('red.png',Buffer.from([137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,1,0,0,0,1,8,2,0,0,0,144,119,83,222,0,0,0,12,73,68,65,84,120,156,99,248,207,192,0,0,3,1,1,0,201,254,146,239,0,0,0,0,73,69,78,68,174,66,96,130]));\"","startedAt":1791427871232,"toolCall":{"callId":"code-image-call:ptc:1","rootCallId":"code-image-call"}}} {"type":"job/settled","data":{"jobId":"{{id:1}}","owner":"{{session:1}}","kind":"bash","label":"node -e \"require('node:fs').writeFileSync('red.png',Buffer.from([137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,1,0,0,0,1,8,2,0,0,0,144,119,83,222,0,0,0,12,73,68,65,84,120,156,99,248,207,192,0,0,3,1,1,0,201,254,146,239,0,0,0,0,73,69,78,68,174,66,96,130]));\"","startedAt":1791427871232,"toolCall":{"callId":"code-image-call:ptc:1","rootCallId":"code-image-call"},"finishedAt":1791427871263,"status":"completed","cause":"producer","producerReported":true,"detail":"exit code: 0"}} diff --git a/snapshots/session/ptc-read-image/snapshot.yml b/snapshots/session/ptc-read-image/snapshot.yml index e7a2fa6085..e55acda323 100644 --- a/snapshots/session/ptc-read-image/snapshot.yml +++ b/snapshots/session/ptc-read-image/snapshot.yml @@ -10,3 +10,5 @@ header: platform: posix workspace: final: true +replay: + override: true diff --git a/snapshots/session/ptc-read-image/system-prompt.expected.md b/snapshots/session/ptc-read-image/system-prompt.expected.md index 08651e72a0..625babeef3 100644 --- a/snapshots/session/ptc-read-image/system-prompt.expected.md +++ b/snapshots/session/ptc-read-image/system-prompt.expected.md @@ -39,20 +39,24 @@ Bash, formatters, code generators, and scripts can overwrite concurrent edits wi `run_code` takes two required arguments: `code`, the body of an async TypeScript function (erasable syntax only — no `enum` or namespaces; type annotations are advisory, the code runs type-stripped), and `description`, a short summary of what the program does. The declarations below are SDK bindings for this program. A declaration does not make its name a directly callable tool; only names supplied as separate tool schemas may be called directly. When no separate `bash` schema is supplied, invoke a declared `bash` binding inside `run_code`: -`run_code({ code: "return await tools.bash({ description: 'Show current directory', command: 'pwd' })", description: "Show current directory" })` +`run_code({ code: "Harness.inspect(await tools.bash({ description: 'Show current directory', command: 'pwd' }))", description: "Show current directory" })` Inside the program: - Call tools as `await tools.name(args)` — quoted access for exotic names: `tools["my-tool"](args)`. Every call resolves to the tool's typed canonical JSON value. Tool arguments must be lossless JSON. - A FAILED tool call rejects with `ToolCallError`, whose `toolName` identifies the failed tool and whose `message` is human-readable — `try/catch` it to handle and continue. - Independent read-only calls MAY overlap under `Promise.all` (safe calls run concurrently; mutating calls run alone, in submission order). Sequence dependent work with `await`. -- Emit results with `return` and/or `console.log(...)`. Only what you print or return is program output. A successful tool result containing an image is attached after the run so you can inspect it on the next step; every other intermediate result stays out of the conversation, so extract just what you need. +- Show selected results with `Harness.inspect(...values)`. Return values and final expressions are not displayed. A successful tool result containing an image is attached after the run so you can inspect it on the next step; every other intermediate result stays out of the conversation, so extract just what you need. Program-only SDK bindings: ```ts type JsonValue = null | boolean | number | string | JsonValue[] | { [key: string]: JsonValue } +declare namespace Harness { + function inspect(...values: unknown[]): void; +} + interface ToolArgsMap { /** Create, update, delete, or move UTF-8 text files with one patch; prefer it over multiple edit calls when a change spans several files or separated regions. */ apply_patch: { diff --git a/snapshots/session/ptc-reset/replay.override.json b/snapshots/session/ptc-reset/replay.override.json index 02068decda..f105135224 100644 --- a/snapshots/session/ptc-reset/replay.override.json +++ b/snapshots/session/ptc-reset/replay.override.json @@ -12,7 +12,7 @@ "index": 0, "id": "call_keep", "name": "run_code", - "argumentsDelta": "{\"code\":\"var kept = { value: 42 }; var lateEffects = []; await tools.bash({ command: \\\"echo 42 > kept.txt\\\", description: \\\"Write the retained value to a file\\\" }); void tools.bash({ command: \\\"echo should-not-run\\\", description: \\\"Leave a tool pending until the program ends\\\" }).then(() => lateEffects.push(\\\"then\\\")).catch(() => lateEffects.push(\\\"catch\\\")).finally(() => lateEffects.push(\\\"finally\\\")); kept.value\",\"description\":\"Retain an object and leave a tool pending\"}" + "argumentsDelta": "{\"code\":\"var kept = { value: 42 }; var lateEffects = []; await tools.bash({ command: \\\"echo 42 > kept.txt\\\", description: \\\"Write the retained value to a file\\\" }); void tools.bash({ command: \\\"echo should-not-run\\\", description: \\\"Leave a tool pending until the program ends\\\" }).then(() => lateEffects.push(\\\"then\\\")).catch(() => lateEffects.push(\\\"catch\\\")).finally(() => lateEffects.push(\\\"finally\\\")); Harness.inspect(kept.value)\",\"description\":\"Retain an object and leave a tool pending\"}" }, { "type": "block-end", @@ -21,7 +21,7 @@ "type": "tool-call", "id": "call_keep", "name": "run_code", - "arguments": "{\"code\":\"var kept = { value: 42 }; var lateEffects = []; await tools.bash({ command: \\\"echo 42 > kept.txt\\\", description: \\\"Write the retained value to a file\\\" }); void tools.bash({ command: \\\"echo should-not-run\\\", description: \\\"Leave a tool pending until the program ends\\\" }).then(() => lateEffects.push(\\\"then\\\")).catch(() => lateEffects.push(\\\"catch\\\")).finally(() => lateEffects.push(\\\"finally\\\")); kept.value\",\"description\":\"Retain an object and leave a tool pending\"}" + "arguments": "{\"code\":\"var kept = { value: 42 }; var lateEffects = []; await tools.bash({ command: \\\"echo 42 > kept.txt\\\", description: \\\"Write the retained value to a file\\\" }); void tools.bash({ command: \\\"echo should-not-run\\\", description: \\\"Leave a tool pending until the program ends\\\" }).then(() => lateEffects.push(\\\"then\\\")).catch(() => lateEffects.push(\\\"catch\\\")).finally(() => lateEffects.push(\\\"finally\\\")); Harness.inspect(kept.value)\",\"description\":\"Retain an object and leave a tool pending\"}" } }, { @@ -52,7 +52,7 @@ "index": 0, "id": "call_isolated", "name": "run_code", - "argumentsDelta": "{\"code\":\"({ retained: kept.value, lateEffects })\",\"description\":\"Inspect retained state after a late tool failure\"}" + "argumentsDelta": "{\"code\":\"Harness.inspect(JSON.stringify(({ retained: kept.value, lateEffects }), null, 2))\",\"description\":\"Inspect retained state after a late tool failure\"}" }, { "type": "block-end", @@ -61,7 +61,7 @@ "type": "tool-call", "id": "call_isolated", "name": "run_code", - "arguments": "{\"code\":\"({ retained: kept.value, lateEffects })\",\"description\":\"Inspect retained state after a late tool failure\"}" + "arguments": "{\"code\":\"Harness.inspect(JSON.stringify(({ retained: kept.value, lateEffects }), null, 2))\",\"description\":\"Inspect retained state after a late tool failure\"}" } }, { @@ -92,7 +92,7 @@ "index": 0, "id": "call_blocked", "name": "run_code", - "argumentsDelta": "{\"code\":\"kept.value\",\"description\":\"Check that failed reset blocks code until host recovery\"}" + "argumentsDelta": "{\"code\":\"Harness.inspect(kept.value)\",\"description\":\"Check that failed reset blocks code until host recovery\"}" }, { "type": "block-end", @@ -101,7 +101,7 @@ "type": "tool-call", "id": "call_blocked", "name": "run_code", - "arguments": "{\"code\":\"kept.value\",\"description\":\"Check that failed reset blocks code until host recovery\"}" + "arguments": "{\"code\":\"Harness.inspect(kept.value)\",\"description\":\"Check that failed reset blocks code until host recovery\"}" } }, { @@ -132,7 +132,7 @@ "index": 0, "id": "call_check", "name": "run_code", - "argumentsDelta": "{\"code\":\"({ retained: typeof kept, file: (await tools.bash({ command: \\\"cat kept.txt\\\", description: \\\"Read the saved value after reset\\\" })).stdout.text.trim() })\",\"description\":\"Check cleared variables and completed file effects\"}" + "argumentsDelta": "{\"code\":\"Harness.inspect(JSON.stringify(({ retained: typeof kept, file: (await tools.bash({ command: \\\"cat kept.txt\\\", description: \\\"Read the saved value after reset\\\" })).stdout.text.trim() }), null, 2))\",\"description\":\"Check cleared variables and completed file effects\"}" }, { "type": "block-end", @@ -141,7 +141,7 @@ "type": "tool-call", "id": "call_check", "name": "run_code", - "arguments": "{\"code\":\"({ retained: typeof kept, file: (await tools.bash({ command: \\\"cat kept.txt\\\", description: \\\"Read the saved value after reset\\\" })).stdout.text.trim() })\",\"description\":\"Check cleared variables and completed file effects\"}" + "arguments": "{\"code\":\"Harness.inspect(JSON.stringify(({ retained: typeof kept, file: (await tools.bash({ command: \\\"cat kept.txt\\\", description: \\\"Read the saved value after reset\\\" })).stdout.text.trim() }), null, 2))\",\"description\":\"Check cleared variables and completed file effects\"}" } }, { diff --git a/snapshots/session/ptc-reset/session.v4.jsonl b/snapshots/session/ptc-reset/session.v4.jsonl index 4f764d7f45..c211ae2041 100644 --- a/snapshots/session/ptc-reset/session.v4.jsonl +++ b/snapshots/session/ptc-reset/session.v4.jsonl @@ -12,8 +12,8 @@ {"type":"request/header","data":{"header":{"config":{"provider":"deepseek-official","model":"deepseek-v4-flash"},"tools":"{{tools}}"},"reason":"initial"}} {"type":"request/context","data":{"provider":"deepseek-official","model":"deepseek-v4-flash"}} {"type":"session/title","data":{"title":"Retain a value and write","messageSeqs":[8],"source":{"kind":"fallback"}}} -{"type":"assistant/message","data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"tool-call","id":"call_keep","name":"run_code","arguments":"{\"code\":\"var kept = { value: 42 }; var lateEffects = []; await tools.bash({ command: \\\"echo 42 > kept.txt\\\", description: \\\"Write the retained value to a file\\\" }); void tools.bash({ command: \\\"echo should-not-run\\\", description: \\\"Leave a tool pending until the program ends\\\" }).then(() => lateEffects.push(\\\"then\\\")).catch(() => lateEffects.push(\\\"catch\\\")).finally(() => lateEffects.push(\\\"finally\\\")); kept.value\",\"description\":\"Retain an object and leave a tool pending\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:4}}"},"usage":{"inputTokens":10,"outputTokens":5},"stream":[{"type":"chunk","time":1793281642180,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}},{"type":"tool-call-chunks","time0":1793281642180,"index":0,"dt":[],"id":"call_keep","name":"run_code","args":["{\"code\":\"var kept = { value: 42 }; var lateEffects = []; await tools.bash({ command: \\\"echo 42 > kept.txt\\\", description: \\\"Write the retained value to a file\\\" }); void tools.bash({ command: \\\"echo should-not-run\\\", description: \\\"Leave a tool pending until the program ends\\\" }).then(() => lateEffects.push(\\\"then\\\")).catch(() => lateEffects.push(\\\"catch\\\")).finally(() => lateEffects.push(\\\"finally\\\")); kept.value\",\"description\":\"Retain an object and leave a tool pending\"}"]},{"type":"chunk","time":1793281642180,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"call_keep","name":"run_code","arguments":"{\"code\":\"var kept = { value: 42 }; var lateEffects = []; await tools.bash({ command: \\\"echo 42 > kept.txt\\\", description: \\\"Write the retained value to a file\\\" }); void tools.bash({ command: \\\"echo should-not-run\\\", description: \\\"Leave a tool pending until the program ends\\\" }).then(() => lateEffects.push(\\\"then\\\")).catch(() => lateEffects.push(\\\"catch\\\")).finally(() => lateEffects.push(\\\"finally\\\")); kept.value\",\"description\":\"Retain an object and leave a tool pending\"}"}}},{"type":"chunk","time":1793281642180,"chunk":{"type":"usage","usage":{"inputTokens":10,"outputTokens":5}}},{"type":"chunk","time":1793281642180,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"} -{"type":"tool/call","data":{"turn":1,"step":1,"callId":"call_keep","name":"run_code","arguments":"{\"code\":\"var kept = { value: 42 }; var lateEffects = []; await tools.bash({ command: \\\"echo 42 > kept.txt\\\", description: \\\"Write the retained value to a file\\\" }); void tools.bash({ command: \\\"echo should-not-run\\\", description: \\\"Leave a tool pending until the program ends\\\" }).then(() => lateEffects.push(\\\"then\\\")).catch(() => lateEffects.push(\\\"catch\\\")).finally(() => lateEffects.push(\\\"finally\\\")); kept.value\",\"description\":\"Retain an object and leave a tool pending\"}"}} +{"type":"assistant/message","data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"tool-call","id":"call_keep","name":"run_code","arguments":"{\"code\":\"var kept = { value: 42 }; var lateEffects = []; await tools.bash({ command: \\\"echo 42 > kept.txt\\\", description: \\\"Write the retained value to a file\\\" }); void tools.bash({ command: \\\"echo should-not-run\\\", description: \\\"Leave a tool pending until the program ends\\\" }).then(() => lateEffects.push(\\\"then\\\")).catch(() => lateEffects.push(\\\"catch\\\")).finally(() => lateEffects.push(\\\"finally\\\")); Harness.inspect(kept.value)\",\"description\":\"Retain an object and leave a tool pending\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:4}}"},"usage":{"inputTokens":10,"outputTokens":5},"stream":[{"type":"chunk","time":1793281642180,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}},{"type":"tool-call-chunks","time0":1793281642180,"index":0,"dt":[],"id":"call_keep","name":"run_code","args":["{\"code\":\"var kept = { value: 42 }; var lateEffects = []; await tools.bash({ command: \\\"echo 42 > kept.txt\\\", description: \\\"Write the retained value to a file\\\" }); void tools.bash({ command: \\\"echo should-not-run\\\", description: \\\"Leave a tool pending until the program ends\\\" }).then(() => lateEffects.push(\\\"then\\\")).catch(() => lateEffects.push(\\\"catch\\\")).finally(() => lateEffects.push(\\\"finally\\\")); Harness.inspect(kept.value)\",\"description\":\"Retain an object and leave a tool pending\"}"]},{"type":"chunk","time":1793281642180,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"call_keep","name":"run_code","arguments":"{\"code\":\"var kept = { value: 42 }; var lateEffects = []; await tools.bash({ command: \\\"echo 42 > kept.txt\\\", description: \\\"Write the retained value to a file\\\" }); void tools.bash({ command: \\\"echo should-not-run\\\", description: \\\"Leave a tool pending until the program ends\\\" }).then(() => lateEffects.push(\\\"then\\\")).catch(() => lateEffects.push(\\\"catch\\\")).finally(() => lateEffects.push(\\\"finally\\\")); Harness.inspect(kept.value)\",\"description\":\"Retain an object and leave a tool pending\"}"}}},{"type":"chunk","time":1793281642180,"chunk":{"type":"usage","usage":{"inputTokens":10,"outputTokens":5}}},{"type":"chunk","time":1793281642180,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"} +{"type":"tool/call","data":{"turn":1,"step":1,"callId":"call_keep","name":"run_code","arguments":"{\"code\":\"var kept = { value: 42 }; var lateEffects = []; await tools.bash({ command: \\\"echo 42 > kept.txt\\\", description: \\\"Write the retained value to a file\\\" }); void tools.bash({ command: \\\"echo should-not-run\\\", description: \\\"Leave a tool pending until the program ends\\\" }).then(() => lateEffects.push(\\\"then\\\")).catch(() => lateEffects.push(\\\"catch\\\")).finally(() => lateEffects.push(\\\"finally\\\")); Harness.inspect(kept.value)\",\"description\":\"Retain an object and leave a tool pending\"}"}} {"type":"tool/ptc-dispatch-start","data":{"rootCallId":"call_keep","parentCallId":"call_keep","subCallId":"call_keep:ptc:1","name":"bash","arguments":{"command":"echo 42 > kept.txt","description":"Write the retained value to a file"}}} {"type":"job/registered","data":{"jobId":"{{id:1}}","owner":"{{session:1}}","kind":"bash","label":"echo 42 > kept.txt","startedAt":1793281642358,"toolCall":{"callId":"call_keep:ptc:1","rootCallId":"call_keep"}}} {"type":"job/settled","data":{"jobId":"{{id:1}}","owner":"{{session:1}}","kind":"bash","label":"echo 42 > kept.txt","startedAt":1793281642358,"toolCall":{"callId":"call_keep:ptc:1","rootCallId":"call_keep"},"finishedAt":1793281642423,"status":"completed","cause":"producer","producerReported":true,"detail":"exit code: 0"}} @@ -23,21 +23,21 @@ {"type":"tool/result","data":{"turn":1,"step":1,"message":{"role":"tool","source":{"kind":"tool","callId":"call_keep"},"toolCallId":"call_keep","content":[{"type":"text","text":"42"}],"isError":false,"id":"{{message:5}}"}},"sourceEventSeqs":[14],"surfaceOp":"append"} {"type":"step/end","data":{"turn":1,"step":1}} {"type":"step/start","data":{"turn":1,"step":2}} -{"type":"assistant/message","data":{"turn":1,"step":2,"message":{"role":"assistant","content":[{"type":"tool-call","id":"call_isolated","name":"run_code","arguments":"{\"code\":\"({ retained: kept.value, lateEffects })\",\"description\":\"Inspect retained state after a late tool failure\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:6}}"},"usage":{"inputTokens":10,"outputTokens":5},"stream":[{"type":"chunk","time":1793281642440,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}},{"type":"tool-call-chunks","time0":1793281642441,"index":0,"dt":[],"id":"call_isolated","name":"run_code","args":["{\"code\":\"({ retained: kept.value, lateEffects })\",\"description\":\"Inspect retained state after a late tool failure\"}"]},{"type":"chunk","time":1793281642441,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"call_isolated","name":"run_code","arguments":"{\"code\":\"({ retained: kept.value, lateEffects })\",\"description\":\"Inspect retained state after a late tool failure\"}"}}},{"type":"chunk","time":1793281642441,"chunk":{"type":"usage","usage":{"inputTokens":10,"outputTokens":5}}},{"type":"chunk","time":1793281642441,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"} -{"type":"tool/call","data":{"turn":1,"step":2,"callId":"call_isolated","name":"run_code","arguments":"{\"code\":\"({ retained: kept.value, lateEffects })\",\"description\":\"Inspect retained state after a late tool failure\"}"}} +{"type":"assistant/message","data":{"turn":1,"step":2,"message":{"role":"assistant","content":[{"type":"tool-call","id":"call_isolated","name":"run_code","arguments":"{\"code\":\"Harness.inspect(JSON.stringify(({ retained: kept.value, lateEffects }), null, 2))\",\"description\":\"Inspect retained state after a late tool failure\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:6}}"},"usage":{"inputTokens":10,"outputTokens":5},"stream":[{"type":"chunk","time":1793281642440,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}},{"type":"tool-call-chunks","time0":1793281642441,"index":0,"dt":[],"id":"call_isolated","name":"run_code","args":["{\"code\":\"Harness.inspect(JSON.stringify(({ retained: kept.value, lateEffects }), null, 2))\",\"description\":\"Inspect retained state after a late tool failure\"}"]},{"type":"chunk","time":1793281642441,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"call_isolated","name":"run_code","arguments":"{\"code\":\"Harness.inspect(JSON.stringify(({ retained: kept.value, lateEffects }), null, 2))\",\"description\":\"Inspect retained state after a late tool failure\"}"}}},{"type":"chunk","time":1793281642441,"chunk":{"type":"usage","usage":{"inputTokens":10,"outputTokens":5}}},{"type":"chunk","time":1793281642441,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"} +{"type":"tool/call","data":{"turn":1,"step":2,"callId":"call_isolated","name":"run_code","arguments":"{\"code\":\"Harness.inspect(JSON.stringify(({ retained: kept.value, lateEffects }), null, 2))\",\"description\":\"Inspect retained state after a late tool failure\"}"}} {"type":"tool/result","data":{"turn":1,"step":2,"message":{"role":"tool","source":{"kind":"tool","callId":"call_isolated"},"toolCallId":"call_isolated","content":[{"type":"text","text":"{\n \"retained\": 42,\n \"lateEffects\": [\n \"catch\",\n \"finally\"\n ]\n}"}],"isError":false,"id":"{{message:7}}"}},"sourceEventSeqs":[25],"surfaceOp":"append"} {"type":"step/end","data":{"turn":1,"step":2}} {"type":"step/start","data":{"turn":1,"step":3}} -{"type":"assistant/message","data":{"turn":1,"step":3,"message":{"role":"assistant","content":[{"type":"tool-call","id":"call_blocked","name":"run_code","arguments":"{\"code\":\"kept.value\",\"description\":\"Check that failed reset blocks code until host recovery\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:8}}"},"usage":{"inputTokens":10,"outputTokens":5},"stream":[{"type":"chunk","time":1793281642459,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}},{"type":"tool-call-chunks","time0":1793281642459,"index":0,"dt":[],"id":"call_blocked","name":"run_code","args":["{\"code\":\"kept.value\",\"description\":\"Check that failed reset blocks code until host recovery\"}"]},{"type":"chunk","time":1793281642459,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"call_blocked","name":"run_code","arguments":"{\"code\":\"kept.value\",\"description\":\"Check that failed reset blocks code until host recovery\"}"}}},{"type":"chunk","time":1793281642459,"chunk":{"type":"usage","usage":{"inputTokens":10,"outputTokens":5}}},{"type":"chunk","time":1793281642459,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"} -{"type":"tool/call","data":{"turn":1,"step":3,"callId":"call_blocked","name":"run_code","arguments":"{\"code\":\"kept.value\",\"description\":\"Check that failed reset blocks code until host recovery\"}"}} +{"type":"assistant/message","data":{"turn":1,"step":3,"message":{"role":"assistant","content":[{"type":"tool-call","id":"call_blocked","name":"run_code","arguments":"{\"code\":\"Harness.inspect(kept.value)\",\"description\":\"Check that failed reset blocks code until host recovery\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:8}}"},"usage":{"inputTokens":10,"outputTokens":5},"stream":[{"type":"chunk","time":1793281642459,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}},{"type":"tool-call-chunks","time0":1793281642459,"index":0,"dt":[],"id":"call_blocked","name":"run_code","args":["{\"code\":\"Harness.inspect(kept.value)\",\"description\":\"Check that failed reset blocks code until host recovery\"}"]},{"type":"chunk","time":1793281642459,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"call_blocked","name":"run_code","arguments":"{\"code\":\"Harness.inspect(kept.value)\",\"description\":\"Check that failed reset blocks code until host recovery\"}"}}},{"type":"chunk","time":1793281642459,"chunk":{"type":"usage","usage":{"inputTokens":10,"outputTokens":5}}},{"type":"chunk","time":1793281642459,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"} +{"type":"tool/call","data":{"turn":1,"step":3,"callId":"call_blocked","name":"run_code","arguments":"{\"code\":\"Harness.inspect(kept.value)\",\"description\":\"Check that failed reset blocks code until host recovery\"}"}} {"type":"agent/inbox/spliced","data":{"target":"next-step","start":0,"inserted":[{"content":[{"type":"text","text":"Program environment reset; retained variables cleared. Completed tool effects remain."}],"source":{"kind":"ptc-mode"},"role":"user","id":"{{message:9}}"}]}} {"type":"tool/result","data":{"turn":1,"step":3,"message":{"role":"tool","source":{"kind":"tool","callId":"call_blocked"},"toolCallId":"call_blocked","content":[{"type":"text","text":"Error: Program environment unavailable: fixture process cleanup failed. Ask the host to reset it before running more code."}],"isError":true,"id":"{{message:10}}"},"error":{"name":"CodeRunFailedError","code":"CODE_RUN_FAILED"}},"sourceEventSeqs":[30],"surfaceOp":"append"} {"type":"step/end","data":{"turn":1,"step":3}} {"type":"agent/inbox/spliced","data":{"target":"next-step","start":0,"removedCount":1,"inserted":[]}} {"type":"step/start","data":{"turn":1,"step":4}} {"type":"user/message","data":{"content":[{"type":"text","text":"Program environment reset; retained variables cleared. Completed tool effects remain."}],"source":{"kind":"ptc-mode"},"role":"user","id":"{{message:9}}"},"surfaceOp":"append"} -{"type":"assistant/message","data":{"turn":1,"step":4,"message":{"role":"assistant","content":[{"type":"tool-call","id":"call_check","name":"run_code","arguments":"{\"code\":\"({ retained: typeof kept, file: (await tools.bash({ command: \\\"cat kept.txt\\\", description: \\\"Read the saved value after reset\\\" })).stdout.text.trim() })\",\"description\":\"Check cleared variables and completed file effects\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:11}}"},"usage":{"inputTokens":10,"outputTokens":5},"stream":[{"type":"chunk","time":1793281642493,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}},{"type":"tool-call-chunks","time0":1793281642493,"index":0,"dt":[],"id":"call_check","name":"run_code","args":["{\"code\":\"({ retained: typeof kept, file: (await tools.bash({ command: \\\"cat kept.txt\\\", description: \\\"Read the saved value after reset\\\" })).stdout.text.trim() })\",\"description\":\"Check cleared variables and completed file effects\"}"]},{"type":"chunk","time":1793281642493,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"call_check","name":"run_code","arguments":"{\"code\":\"({ retained: typeof kept, file: (await tools.bash({ command: \\\"cat kept.txt\\\", description: \\\"Read the saved value after reset\\\" })).stdout.text.trim() })\",\"description\":\"Check cleared variables and completed file effects\"}"}}},{"type":"chunk","time":1793281642493,"chunk":{"type":"usage","usage":{"inputTokens":10,"outputTokens":5}}},{"type":"chunk","time":1793281642493,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"} -{"type":"tool/call","data":{"turn":1,"step":4,"callId":"call_check","name":"run_code","arguments":"{\"code\":\"({ retained: typeof kept, file: (await tools.bash({ command: \\\"cat kept.txt\\\", description: \\\"Read the saved value after reset\\\" })).stdout.text.trim() })\",\"description\":\"Check cleared variables and completed file effects\"}"}} +{"type":"assistant/message","data":{"turn":1,"step":4,"message":{"role":"assistant","content":[{"type":"tool-call","id":"call_check","name":"run_code","arguments":"{\"code\":\"Harness.inspect(JSON.stringify(({ retained: typeof kept, file: (await tools.bash({ command: \\\"cat kept.txt\\\", description: \\\"Read the saved value after reset\\\" })).stdout.text.trim() }), null, 2))\",\"description\":\"Check cleared variables and completed file effects\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:11}}"},"usage":{"inputTokens":10,"outputTokens":5},"stream":[{"type":"chunk","time":1793281642493,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}},{"type":"tool-call-chunks","time0":1793281642493,"index":0,"dt":[],"id":"call_check","name":"run_code","args":["{\"code\":\"Harness.inspect(JSON.stringify(({ retained: typeof kept, file: (await tools.bash({ command: \\\"cat kept.txt\\\", description: \\\"Read the saved value after reset\\\" })).stdout.text.trim() }), null, 2))\",\"description\":\"Check cleared variables and completed file effects\"}"]},{"type":"chunk","time":1793281642493,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"call_check","name":"run_code","arguments":"{\"code\":\"Harness.inspect(JSON.stringify(({ retained: typeof kept, file: (await tools.bash({ command: \\\"cat kept.txt\\\", description: \\\"Read the saved value after reset\\\" })).stdout.text.trim() }), null, 2))\",\"description\":\"Check cleared variables and completed file effects\"}"}}},{"type":"chunk","time":1793281642493,"chunk":{"type":"usage","usage":{"inputTokens":10,"outputTokens":5}}},{"type":"chunk","time":1793281642493,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"} +{"type":"tool/call","data":{"turn":1,"step":4,"callId":"call_check","name":"run_code","arguments":"{\"code\":\"Harness.inspect(JSON.stringify(({ retained: typeof kept, file: (await tools.bash({ command: \\\"cat kept.txt\\\", description: \\\"Read the saved value after reset\\\" })).stdout.text.trim() }), null, 2))\",\"description\":\"Check cleared variables and completed file effects\"}"}} {"type":"tool/ptc-dispatch-start","data":{"rootCallId":"call_check","parentCallId":"call_check","subCallId":"call_check:ptc:1","name":"bash","arguments":{"command":"cat kept.txt","description":"Read the saved value after reset"}}} {"type":"job/registered","data":{"jobId":"{{id:2}}","owner":"{{session:1}}","kind":"bash","label":"cat kept.txt","startedAt":1793281642627,"toolCall":{"callId":"call_check:ptc:1","rootCallId":"call_check"}}} {"type":"job/settled","data":{"jobId":"{{id:2}}","owner":"{{session:1}}","kind":"bash","label":"cat kept.txt","startedAt":1793281642627,"toolCall":{"callId":"call_check:ptc:1","rootCallId":"call_check"},"finishedAt":1793281642685,"status":"completed","cause":"producer","producerReported":true,"detail":"exit code: 0"}} diff --git a/snapshots/session/ptc-reset/tool-schemas.expected.json b/snapshots/session/ptc-reset/tool-schemas.expected.json index c3e65ba191..8cfc964066 100644 --- a/snapshots/session/ptc-reset/tool-schemas.expected.json +++ b/snapshots/session/ptc-reset/tool-schemas.expected.json @@ -2,7 +2,7 @@ "initial": [ { "name": "run_code", - "description": "Execute a TypeScript program against the available tools. Takes two required arguments: `code`, the program to execute, and `description`, a short summary of what the program does. Call tools as `await tools.name(args)` per the declarations in the system prompt. Curate the program output. Image-bearing subtool results are attached after the run. The program contains TypeScript script statements; only erasable syntax is accepted, with no enum or namespaces. Type annotations are advisory. Top-level declarations and await are supported; top-level return is not supported. Emit output with console.log(...) or a final lossless JSON expression. The final expression is awaited if it is a Promise. Only printed values and that final expression become program output. Variables, functions, and imported modules remain in memory for this Agent across calls. Use await import(...) for modules. Relative paths use the supplied working directory. Direct filesystem access follows the environment's launch-time sandbox policy; process.env starts empty. Await asynchronous work: an uncaught callback exception or unhandled rejection terminates the process and clears its variables. Values from bindings and imports can belong to another realm; use Array.isArray for arrays and the supplied binding error classes for failures, because instanceof Array or Error may be false. Timeout and cancellation results say whether retained variables were preserved or cleared. Restart, sandbox changes, output overflow, or process failure clears them. Recreate missing variables and resources; conversation history does not restore state. Earlier mutations and tool effects are not rolled back. Approved sandbox escalations use a fresh environment and discard its variables afterward. Do not redeclare existing let, const, or class names; reuse mutable bindings or choose new names. Await tools and cleanup before returning; unfinished tools reject. After completion, timeout, or cancellation, new tool calls are rejected and captured output is discarded; background work may still change state. Await child-process completion before returning; raw output belongs to the active evaluation. If a native top-level let, const, or class initializer throws, its name may remain permanently uninitialized; use a new name or restart the environment. Each program starts in the Session's current directory. Running programs keep their initial directory. A sandbox escalation approves this complete program for one execution only. Nested tools retain their own policies and approvals. Request wider access only after evidence of a denial. Earlier effects may already have completed: inspect them before explicitly retrying. Programs are never replayed automatically.", + "description": "Execute a TypeScript program against the available tools. Takes two required arguments: `code`, the program to execute, and `description`, a short summary of what the program does. Call tools as `await tools.name(args)` per the declarations in the system prompt. Curate the program output. Image-bearing subtool results are attached after the run. The program contains TypeScript script statements; only erasable syntax is accepted, with no enum or namespaces. Type annotations are advisory. Top-level declarations and await are supported; top-level return is not supported. Console is unavailable. Use Harness.inspect(...values) for output. The final expression is awaited if it is a Promise. Variables, functions, and imported modules remain in memory for this Agent across calls. Use await import(...) for modules. Relative paths use the supplied working directory. Direct filesystem access follows the environment's launch-time sandbox policy; process.env starts empty. Await asynchronous work: an uncaught callback exception or unhandled rejection terminates the process and clears its variables. Values from bindings and imports can belong to another realm; use Array.isArray for arrays and the supplied binding error classes for failures, because instanceof Array or Error may be false. Timeout and cancellation results say whether retained variables were preserved or cleared. Restart, sandbox changes, output overflow, or process failure clears them. Recreate missing variables and resources; conversation history does not restore state. Earlier mutations and tool effects are not rolled back. Approved sandbox escalations use a fresh environment and discard its variables afterward. Do not redeclare existing let, const, or class names; reuse mutable bindings or choose new names. Await tools and cleanup before returning; unfinished tools reject. After completion, timeout, or cancellation, new tool calls are rejected and captured output is discarded; background work may still change state. Await child-process completion before returning; raw output belongs to the active evaluation. If a native top-level let, const, or class initializer throws, its name may remain permanently uninitialized; use a new name or restart the environment. Each program starts in the Session's current directory. Running programs keep their initial directory. A sandbox escalation approves this complete program for one execution only. Nested tools retain their own policies and approvals. Request wider access only after evidence of a denial. Earlier effects may already have completed: inspect them before explicitly retrying. Programs are never replayed automatically.", "parameters": { "type": "object", "properties": { diff --git a/snapshots/session/ptc-script-fresh/replay.override.json b/snapshots/session/ptc-script-fresh/replay.override.json new file mode 100644 index 0000000000..62b2177a1e --- /dev/null +++ b/snapshots/session/ptc-script-fresh/replay.override.json @@ -0,0 +1,47 @@ +[ + { + "kind": "chunks", + "chunks": [ + {"type":"block-start","index":0,"blockType":"tool-call"}, + {"type":"block-end","index":0,"block":{"type":"tool-call","id":"script_declare","name":"run_code","arguments":"{\"code\":\"let counter: number = 1; const alias = { value: counter }; function increment() { return ++counter }; class Box { [await Promise.resolve(\\\"read\\\")]() { return counter } };\",\"description\":\"Declare Script state with computed await\"}"}}, + {"type":"usage","usage":{"inputTokens":10,"outputTokens":5}}, + {"type":"finish","reason":{"kind":"tool-calls"}} + ] + }, + { + "kind": "chunks", + "chunks": [ + {"type":"block-start","index":0,"blockType":"tool-call"}, + {"type":"block-end","index":0,"block":{"type":"tool-call","id":"script_fresh","name":"run_code","arguments":"{\"code\":\"for (var{answer} of [{answer:42}]) { await 0 }; Harness.inspect(JSON.stringify(({ previous: typeof counter, answer: await(Promise.resolve(answer)) }), null, 2))\",\"description\":\"Verify compact var patterns and fresh Script state\"}"}}, + {"type":"usage","usage":{"inputTokens":10,"outputTokens":5}}, + {"type":"finish","reason":{"kind":"tool-calls"}} + ] + }, + { + "kind": "chunks", + "chunks": [ + {"type":"block-start","index":0,"blockType":"tool-call"}, + {"type":"block-end","index":0,"block":{"type":"tool-call","id":"script_binding","name":"run_code","arguments":"{\"code\":\"const result = await tools.bash({command:\\\"pwd\\\",description:\\\"Read working directory\\\"}); Harness.inspect(result.stdout.text.trim() === process.cwd())\",\"description\":\"Call a host binding from a fresh Script\"}"}}, + {"type":"usage","usage":{"inputTokens":10,"outputTokens":5}}, + {"type":"finish","reason":{"kind":"tool-calls"}} + ] + }, + { + "kind": "chunks", + "chunks": [ + {"type":"block-start","index":0,"blockType":"tool-call"}, + {"type":"block-end","index":0,"block":{"type":"tool-call","id":"script_native_syntax","name":"run_code","arguments":"{\"code\":\"let duplicate; let duplicate;\",\"description\":\"Inspect native Script syntax diagnostics\"}"}}, + {"type":"usage","usage":{"inputTokens":10,"outputTokens":5}}, + {"type":"finish","reason":{"kind":"tool-calls"}} + ] + }, + { + "kind": "chunks", + "chunks": [ + {"type":"block-start","index":0,"blockType":"text"}, + {"type":"block-end","index":0,"block":{"type":"text","text":"Verified Script completion, compact var declarations, native syntax diagnostics, host bindings, and fresh state between calls."}}, + {"type":"usage","usage":{"inputTokens":10,"outputTokens":2}}, + {"type":"finish","reason":{"kind":"stop"}} + ] + } +] diff --git a/snapshots/session/ptc-script-fresh/session.v4.jsonl b/snapshots/session/ptc-script-fresh/session.v4.jsonl index 9239f9b1dc..f7599c384a 100644 --- a/snapshots/session/ptc-script-fresh/session.v4.jsonl +++ b/snapshots/session/ptc-script-fresh/session.v4.jsonl @@ -17,13 +17,13 @@ {"type":"tool/result","data":{"turn":1,"step":1,"message":{"role":"tool","source":{"kind":"tool","callId":"script_declare"},"toolCallId":"script_declare","content":[{"type":"text","text":"(run_code completed with no output)"}],"isError":false,"id":"{{message:5}}"}},"sourceEventSeqs":[14],"surfaceOp":"append"} {"type":"step/end","data":{"turn":1,"step":1}} {"type":"step/start","data":{"turn":1,"step":2}} -{"type":"assistant/message","data":{"turn":1,"step":2,"message":{"role":"assistant","content":[{"type":"tool-call","id":"script_fresh","name":"run_code","arguments":"{\"code\":\"for (var{answer} of [{answer:42}]) { await 0 }; ({ previous: typeof counter, answer: await(Promise.resolve(answer)) })\",\"description\":\"Verify compact var patterns and fresh Script state\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:6}}"},"usage":{"inputTokens":10,"outputTokens":5},"stream":[{"type":"chunk","time":1792550593515,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}},{"type":"chunk","time":1792550593515,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"script_fresh","name":"run_code","arguments":"{\"code\":\"for (var{answer} of [{answer:42}]) { await 0 }; ({ previous: typeof counter, answer: await(Promise.resolve(answer)) })\",\"description\":\"Verify compact var patterns and fresh Script state\"}"}}},{"type":"chunk","time":1792550593515,"chunk":{"type":"usage","usage":{"inputTokens":10,"outputTokens":5}}},{"type":"chunk","time":1792550593515,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"} -{"type":"tool/call","data":{"turn":1,"step":2,"callId":"script_fresh","name":"run_code","arguments":"{\"code\":\"for (var{answer} of [{answer:42}]) { await 0 }; ({ previous: typeof counter, answer: await(Promise.resolve(answer)) })\",\"description\":\"Verify compact var patterns and fresh Script state\"}"}} +{"type":"assistant/message","data":{"turn":1,"step":2,"message":{"role":"assistant","content":[{"type":"tool-call","id":"script_fresh","name":"run_code","arguments":"{\"code\":\"for (var{answer} of [{answer:42}]) { await 0 }; Harness.inspect(JSON.stringify(({ previous: typeof counter, answer: await(Promise.resolve(answer)) }), null, 2))\",\"description\":\"Verify compact var patterns and fresh Script state\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:6}}"},"usage":{"inputTokens":10,"outputTokens":5},"stream":[{"type":"chunk","time":1792550593515,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}},{"type":"chunk","time":1792550593515,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"script_fresh","name":"run_code","arguments":"{\"code\":\"for (var{answer} of [{answer:42}]) { await 0 }; Harness.inspect(JSON.stringify(({ previous: typeof counter, answer: await(Promise.resolve(answer)) }), null, 2))\",\"description\":\"Verify compact var patterns and fresh Script state\"}"}}},{"type":"chunk","time":1792550593515,"chunk":{"type":"usage","usage":{"inputTokens":10,"outputTokens":5}}},{"type":"chunk","time":1792550593515,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"} +{"type":"tool/call","data":{"turn":1,"step":2,"callId":"script_fresh","name":"run_code","arguments":"{\"code\":\"for (var{answer} of [{answer:42}]) { await 0 }; Harness.inspect(JSON.stringify(({ previous: typeof counter, answer: await(Promise.resolve(answer)) }), null, 2))\",\"description\":\"Verify compact var patterns and fresh Script state\"}"}} {"type":"tool/result","data":{"turn":1,"step":2,"message":{"role":"tool","source":{"kind":"tool","callId":"script_fresh"},"toolCallId":"script_fresh","content":[{"type":"text","text":"{\n \"previous\": \"undefined\",\n \"answer\": 42\n}"}],"isError":false,"id":"{{message:7}}"}},"sourceEventSeqs":[19],"surfaceOp":"append"} {"type":"step/end","data":{"turn":1,"step":2}} {"type":"step/start","data":{"turn":1,"step":3}} -{"type":"assistant/message","data":{"turn":1,"step":3,"message":{"role":"assistant","content":[{"type":"tool-call","id":"script_binding","name":"run_code","arguments":"{\"code\":\"const result = await tools.bash({command:\\\"pwd\\\",description:\\\"Read working directory\\\"}); result.stdout.text.trim() === process.cwd()\",\"description\":\"Call a host binding from a fresh Script\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:8}}"},"usage":{"inputTokens":10,"outputTokens":5},"stream":[{"type":"chunk","time":1792550593707,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}},{"type":"chunk","time":1792550593707,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"script_binding","name":"run_code","arguments":"{\"code\":\"const result = await tools.bash({command:\\\"pwd\\\",description:\\\"Read working directory\\\"}); result.stdout.text.trim() === process.cwd()\",\"description\":\"Call a host binding from a fresh Script\"}"}}},{"type":"chunk","time":1792550593707,"chunk":{"type":"usage","usage":{"inputTokens":10,"outputTokens":5}}},{"type":"chunk","time":1792550593707,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"} -{"type":"tool/call","data":{"turn":1,"step":3,"callId":"script_binding","name":"run_code","arguments":"{\"code\":\"const result = await tools.bash({command:\\\"pwd\\\",description:\\\"Read working directory\\\"}); result.stdout.text.trim() === process.cwd()\",\"description\":\"Call a host binding from a fresh Script\"}"}} +{"type":"assistant/message","data":{"turn":1,"step":3,"message":{"role":"assistant","content":[{"type":"tool-call","id":"script_binding","name":"run_code","arguments":"{\"code\":\"const result = await tools.bash({command:\\\"pwd\\\",description:\\\"Read working directory\\\"}); Harness.inspect(result.stdout.text.trim() === process.cwd())\",\"description\":\"Call a host binding from a fresh Script\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:8}}"},"usage":{"inputTokens":10,"outputTokens":5},"stream":[{"type":"chunk","time":1792550593707,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}},{"type":"chunk","time":1792550593707,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"script_binding","name":"run_code","arguments":"{\"code\":\"const result = await tools.bash({command:\\\"pwd\\\",description:\\\"Read working directory\\\"}); Harness.inspect(result.stdout.text.trim() === process.cwd())\",\"description\":\"Call a host binding from a fresh Script\"}"}}},{"type":"chunk","time":1792550593707,"chunk":{"type":"usage","usage":{"inputTokens":10,"outputTokens":5}}},{"type":"chunk","time":1792550593707,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"} +{"type":"tool/call","data":{"turn":1,"step":3,"callId":"script_binding","name":"run_code","arguments":"{\"code\":\"const result = await tools.bash({command:\\\"pwd\\\",description:\\\"Read working directory\\\"}); Harness.inspect(result.stdout.text.trim() === process.cwd())\",\"description\":\"Call a host binding from a fresh Script\"}"}} {"type":"tool/ptc-dispatch-start","data":{"rootCallId":"script_binding","parentCallId":"script_binding","subCallId":"script_binding:ptc:1","name":"bash","arguments":{"command":"pwd","description":"Read working directory"}}} {"type":"job/registered","data":{"jobId":"{{id:1}}","owner":"{{session:1}}","kind":"bash","label":"pwd","startedAt":1792550593834,"toolCall":{"callId":"script_binding:ptc:1","rootCallId":"script_binding"}}} {"type":"job/settled","data":{"jobId":"{{id:1}}","owner":"{{session:1}}","kind":"bash","label":"pwd","startedAt":1792550593834,"toolCall":{"callId":"script_binding:ptc:1","rootCallId":"script_binding"},"finishedAt":1792550594058,"status":"completed","cause":"producer","producerReported":true,"detail":"exit code: 0"}} @@ -36,6 +36,6 @@ {"type":"tool/result","data":{"turn":1,"step":4,"message":{"role":"tool","source":{"kind":"tool","callId":"script_native_syntax"},"toolCallId":"script_native_syntax","content":[{"type":"text","text":"Error: code run failed (exception): {{cwd}}/ptc-program.js:1\nlet duplicate; let duplicate;\n ^\n\nSyntaxError: Identifier 'duplicate' has already been declared\nFile sandbox: workspace-write; enforcement: full."}],"isError":true,"id":"{{message:11}}"},"error":{"name":"CodeRunFailedError","code":"CODE_RUN_FAILED"}},"sourceEventSeqs":[33],"surfaceOp":"append"} {"type":"step/end","data":{"turn":1,"step":4}} {"type":"step/start","data":{"turn":1,"step":5}} -{"type":"assistant/message","data":{"turn":1,"step":5,"message":{"role":"assistant","content":[{"type":"text","text":"Verified Script completion, compact var declarations, native syntax diagnostics, host bindings, and fresh state between calls."}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:12}}"},"usage":{"inputTokens":10,"outputTokens":2},"stream":[{"type":"chunk","time":1792550594139,"chunk":{"type":"block-start","index":0,"blockType":"text"}},{"type":"text-chunks","time0":1792550594139,"index":0,"dt":[],"texts":["Verified Script completion, compact var declarations, native syntax diagnostics, host bindings, and fresh state between calls."]},{"type":"chunk","time":1792550594139,"chunk":{"type":"block-end","index":0,"block":{"type":"text","text":"Verified Script completion, compact var declarations, native syntax diagnostics, host bindings, and fresh state between calls."}}},{"type":"chunk","time":1792550594139,"chunk":{"type":"usage","usage":{"inputTokens":10,"outputTokens":2}}},{"type":"chunk","time":1792550594139,"chunk":{"type":"finish","reason":{"kind":"stop"}}}]},"surfaceOp":"append"} +{"type":"assistant/message","data":{"turn":1,"step":5,"message":{"role":"assistant","content":[{"type":"text","text":"Verified Script completion, compact var declarations, native syntax diagnostics, host bindings, and fresh state between calls."}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:12}}"},"usage":{"inputTokens":10,"outputTokens":2},"stream":[{"type":"chunk","time":1794195791940,"chunk":{"type":"block-start","index":0,"blockType":"text"}},{"type":"chunk","time":1794195791940,"chunk":{"type":"block-end","index":0,"block":{"type":"text","text":"Verified Script completion, compact var declarations, native syntax diagnostics, host bindings, and fresh state between calls."}}},{"type":"chunk","time":1794195791940,"chunk":{"type":"usage","usage":{"inputTokens":10,"outputTokens":2}}},{"type":"chunk","time":1794195791940,"chunk":{"type":"finish","reason":{"kind":"stop"}}}]},"surfaceOp":"append"} {"type":"step/end","data":{"turn":1,"step":5}} {"type":"turn/end","data":{"turn":1,"reason":{"kind":"completed"}}} diff --git a/snapshots/session/ptc-script-fresh/snapshot.yml b/snapshots/session/ptc-script-fresh/snapshot.yml index 530b02af9f..855f81539d 100644 --- a/snapshots/session/ptc-script-fresh/snapshot.yml +++ b/snapshots/session/ptc-script-fresh/snapshot.yml @@ -8,3 +8,5 @@ header: pin: true systemPromptSource: ptc-persistent permission: workspace-write +replay: + override: true diff --git a/snapshots/session/ptc-script-fresh/tool-schemas.expected.json b/snapshots/session/ptc-script-fresh/tool-schemas.expected.json index 4fbcb83764..87e12cfad4 100644 --- a/snapshots/session/ptc-script-fresh/tool-schemas.expected.json +++ b/snapshots/session/ptc-script-fresh/tool-schemas.expected.json @@ -2,7 +2,7 @@ "initial": [ { "name": "run_code", - "description": "Execute a TypeScript program against the available tools. Takes two required arguments: `code`, the program to execute, and `description`, a short summary of what the program does. Call tools as `await tools.name(args)` per the declarations in the system prompt. Curate the program output. Image-bearing subtool results are attached after the run. The program contains TypeScript script statements; only erasable syntax is accepted, with no enum or namespaces. Type annotations are advisory. Top-level declarations and await are supported; top-level return is not supported. Emit output with console.log(...) or a final lossless JSON expression. The final expression is awaited if it is a Promise. Only printed values and that final expression become program output. Each call runs in a fresh Node process. Use await import(...) for modules. Relative paths use the supplied working directory. Direct filesystem access follows this execution's sandbox policy; process.env starts empty. Await asynchronous work: an uncaught callback exception or unhandled rejection terminates the process. Values from bindings and imports can belong to another realm; use Array.isArray for arrays and the supplied binding error classes for failures, because instanceof Array or Error may be false. Each program starts in the Session's current directory. Running programs keep their initial directory. A sandbox escalation approves this complete program for one execution only. Nested tools retain their own policies and approvals. Request wider access only after evidence of a denial. Earlier effects may already have completed: inspect them before explicitly retrying. Programs are never replayed automatically.", + "description": "Execute a TypeScript program against the available tools. Takes two required arguments: `code`, the program to execute, and `description`, a short summary of what the program does. Call tools as `await tools.name(args)` per the declarations in the system prompt. Curate the program output. Image-bearing subtool results are attached after the run. The program contains TypeScript script statements; only erasable syntax is accepted, with no enum or namespaces. Type annotations are advisory. Top-level declarations and await are supported; top-level return is not supported. Console is unavailable. Use Harness.inspect(...values) for output. The final expression is awaited if it is a Promise. Each call runs in a fresh Node process. Use await import(...) for modules. Relative paths use the supplied working directory. Direct filesystem access follows this execution's sandbox policy; process.env starts empty. Await asynchronous work: an uncaught callback exception or unhandled rejection terminates the process. Values from bindings and imports can belong to another realm; use Array.isArray for arrays and the supplied binding error classes for failures, because instanceof Array or Error may be false. Each program starts in the Session's current directory. Running programs keep their initial directory. A sandbox escalation approves this complete program for one execution only. Nested tools retain their own policies and approvals. Request wider access only after evidence of a denial. Earlier effects may already have completed: inspect them before explicitly retrying. Programs are never replayed automatically.", "parameters": { "type": "object", "properties": { diff --git a/snapshots/session/ptc-script-imports/replay.override.json b/snapshots/session/ptc-script-imports/replay.override.json index 0aa49b216e..371e9b329f 100644 --- a/snapshots/session/ptc-script-imports/replay.override.json +++ b/snapshots/session/ptc-script-imports/replay.override.json @@ -14,7 +14,7 @@ "type": "tool-call", "id": "esm_static", "name": "run_code", - "arguments": "{\"code\":\"var dependency = await import(\\\"./entry.mjs\\\"); dependency.first()\",\"description\":\"Import ESM and its runner object\"}" + "arguments": "{\"code\":\"var dependency = await import(\\\"./entry.mjs\\\"); Harness.inspect(dependency.first())\",\"description\":\"Import ESM and its runner object\"}" } }, { @@ -47,7 +47,7 @@ "type": "tool-call", "id": "esm_dynamic", "name": "run_code", - "arguments": "{\"code\":\"await dependency.later()\",\"description\":\"Invoke a retained function with a dynamic import\"}" + "arguments": "{\"code\":\"Harness.inspect(await dependency.later())\",\"description\":\"Invoke a retained function with a dynamic import\"}" } }, { @@ -80,7 +80,7 @@ "type": "tool-call", "id": "esm_denied", "name": "run_code", - "arguments": "{\"code\":\"await dependency.denied().catch(error => error.message)\",\"description\":\"Catch a rejected dependency import\"}" + "arguments": "{\"code\":\"Harness.inspect(await dependency.denied().catch(error => error.message))\",\"description\":\"Catch a rejected dependency import\"}" } }, { diff --git a/snapshots/session/ptc-script-imports/session.v4.jsonl b/snapshots/session/ptc-script-imports/session.v4.jsonl index 7acca2ed1a..031ad43a7f 100644 --- a/snapshots/session/ptc-script-imports/session.v4.jsonl +++ b/snapshots/session/ptc-script-imports/session.v4.jsonl @@ -12,18 +12,18 @@ {"type":"request/header","data":{"header":{"config":{"provider":"deepseek-official","model":"deepseek-v4-flash"},"tools":"{{tools}}"},"reason":"initial"}} {"type":"request/context","data":{"provider":"deepseek-official","model":"deepseek-v4-flash"}} {"type":"session/title","data":{"title":"Use Script run_code to import","messageSeqs":[8],"source":{"kind":"fallback"}}} -{"type":"assistant/message","data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"tool-call","id":"esm_static","name":"run_code","arguments":"{\"code\":\"var dependency = await import(\\\"./entry.mjs\\\"); dependency.first()\",\"description\":\"Import ESM and its runner object\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:4}}"},"usage":{"inputTokens":10,"outputTokens":5},"stream":[{"type":"chunk","time":1792561944612,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}},{"type":"chunk","time":1792561944612,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"esm_static","name":"run_code","arguments":"{\"code\":\"var dependency = await import(\\\"./entry.mjs\\\"); dependency.first()\",\"description\":\"Import ESM and its runner object\"}"}}},{"type":"chunk","time":1792561944612,"chunk":{"type":"usage","usage":{"inputTokens":10,"outputTokens":5}}},{"type":"chunk","time":1792561944612,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"} -{"type":"tool/call","data":{"turn":1,"step":1,"callId":"esm_static","name":"run_code","arguments":"{\"code\":\"var dependency = await import(\\\"./entry.mjs\\\"); dependency.first()\",\"description\":\"Import ESM and its runner object\"}"}} -{"type":"tool/result","data":{"turn":1,"step":1,"message":{"role":"tool","source":{"kind":"tool","callId":"esm_static"},"toolCallId":"esm_static","content":[{"type":"text","text":"[\n 1,\n true\n]"}],"isError":false,"id":"{{message:5}}"}},"sourceEventSeqs":[14],"surfaceOp":"append"} +{"type":"assistant/message","data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"tool-call","id":"esm_static","name":"run_code","arguments":"{\"code\":\"var dependency = await import(\\\"./entry.mjs\\\"); Harness.inspect(dependency.first())\",\"description\":\"Import ESM and its runner object\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:4}}"},"usage":{"inputTokens":10,"outputTokens":5},"stream":[{"type":"chunk","time":1792561944612,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}},{"type":"chunk","time":1792561944612,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"esm_static","name":"run_code","arguments":"{\"code\":\"var dependency = await import(\\\"./entry.mjs\\\"); Harness.inspect(dependency.first())\",\"description\":\"Import ESM and its runner object\"}"}}},{"type":"chunk","time":1792561944612,"chunk":{"type":"usage","usage":{"inputTokens":10,"outputTokens":5}}},{"type":"chunk","time":1792561944612,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"} +{"type":"tool/call","data":{"turn":1,"step":1,"callId":"esm_static","name":"run_code","arguments":"{\"code\":\"var dependency = await import(\\\"./entry.mjs\\\"); Harness.inspect(dependency.first())\",\"description\":\"Import ESM and its runner object\"}"}} +{"type":"tool/result","data":{"turn":1,"step":1,"message":{"role":"tool","source":{"kind":"tool","callId":"esm_static"},"toolCallId":"esm_static","content":[{"type":"text","text":"[ 1, true ]"}],"isError":false,"id":"{{message:5}}"}},"sourceEventSeqs":[14],"surfaceOp":"append"} {"type":"step/end","data":{"turn":1,"step":1}} {"type":"step/start","data":{"turn":1,"step":2}} -{"type":"assistant/message","data":{"turn":1,"step":2,"message":{"role":"assistant","content":[{"type":"tool-call","id":"esm_dynamic","name":"run_code","arguments":"{\"code\":\"await dependency.later()\",\"description\":\"Invoke a retained function with a dynamic import\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:6}}"},"usage":{"inputTokens":10,"outputTokens":5},"stream":[{"type":"chunk","time":1792561944793,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}},{"type":"chunk","time":1792561944793,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"esm_dynamic","name":"run_code","arguments":"{\"code\":\"await dependency.later()\",\"description\":\"Invoke a retained function with a dynamic import\"}"}}},{"type":"chunk","time":1792561944793,"chunk":{"type":"usage","usage":{"inputTokens":10,"outputTokens":5}}},{"type":"chunk","time":1792561944793,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"} -{"type":"tool/call","data":{"turn":1,"step":2,"callId":"esm_dynamic","name":"run_code","arguments":"{\"code\":\"await dependency.later()\",\"description\":\"Invoke a retained function with a dynamic import\"}"}} -{"type":"tool/result","data":{"turn":1,"step":2,"message":{"role":"tool","source":{"kind":"tool","callId":"esm_dynamic"},"toolCallId":"esm_dynamic","content":[{"type":"text","text":"[\n true,\n 2\n]"}],"isError":false,"id":"{{message:7}}"}},"sourceEventSeqs":[19],"surfaceOp":"append"} +{"type":"assistant/message","data":{"turn":1,"step":2,"message":{"role":"assistant","content":[{"type":"tool-call","id":"esm_dynamic","name":"run_code","arguments":"{\"code\":\"Harness.inspect(await dependency.later())\",\"description\":\"Invoke a retained function with a dynamic import\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:6}}"},"usage":{"inputTokens":10,"outputTokens":5},"stream":[{"type":"chunk","time":1792561944793,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}},{"type":"chunk","time":1792561944793,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"esm_dynamic","name":"run_code","arguments":"{\"code\":\"Harness.inspect(await dependency.later())\",\"description\":\"Invoke a retained function with a dynamic import\"}"}}},{"type":"chunk","time":1792561944793,"chunk":{"type":"usage","usage":{"inputTokens":10,"outputTokens":5}}},{"type":"chunk","time":1792561944793,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"} +{"type":"tool/call","data":{"turn":1,"step":2,"callId":"esm_dynamic","name":"run_code","arguments":"{\"code\":\"Harness.inspect(await dependency.later())\",\"description\":\"Invoke a retained function with a dynamic import\"}"}} +{"type":"tool/result","data":{"turn":1,"step":2,"message":{"role":"tool","source":{"kind":"tool","callId":"esm_dynamic"},"toolCallId":"esm_dynamic","content":[{"type":"text","text":"[ true, 2 ]"}],"isError":false,"id":"{{message:7}}"}},"sourceEventSeqs":[19],"surfaceOp":"append"} {"type":"step/end","data":{"turn":1,"step":2}} {"type":"step/start","data":{"turn":1,"step":3}} -{"type":"assistant/message","data":{"turn":1,"step":3,"message":{"role":"assistant","content":[{"type":"tool-call","id":"esm_denied","name":"run_code","arguments":"{\"code\":\"await dependency.denied().catch(error => error.message)\",\"description\":\"Catch a rejected dependency import\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:8}}"},"usage":{"inputTokens":10,"outputTokens":5},"stream":[{"type":"chunk","time":1792561944811,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}},{"type":"chunk","time":1792561944811,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"esm_denied","name":"run_code","arguments":"{\"code\":\"await dependency.denied().catch(error => error.message)\",\"description\":\"Catch a rejected dependency import\"}"}}},{"type":"chunk","time":1792561944811,"chunk":{"type":"usage","usage":{"inputTokens":10,"outputTokens":5}}},{"type":"chunk","time":1792561944811,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"} -{"type":"tool/call","data":{"turn":1,"step":3,"callId":"esm_denied","name":"run_code","arguments":"{\"code\":\"await dependency.denied().catch(error => error.message)\",\"description\":\"Catch a rejected dependency import\"}"}} +{"type":"assistant/message","data":{"turn":1,"step":3,"message":{"role":"assistant","content":[{"type":"tool-call","id":"esm_denied","name":"run_code","arguments":"{\"code\":\"Harness.inspect(await dependency.denied().catch(error => error.message))\",\"description\":\"Catch a rejected dependency import\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:8}}"},"usage":{"inputTokens":10,"outputTokens":5},"stream":[{"type":"chunk","time":1792561944811,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}},{"type":"chunk","time":1792561944811,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"esm_denied","name":"run_code","arguments":"{\"code\":\"Harness.inspect(await dependency.denied().catch(error => error.message))\",\"description\":\"Catch a rejected dependency import\"}"}}},{"type":"chunk","time":1792561944811,"chunk":{"type":"usage","usage":{"inputTokens":10,"outputTokens":5}}},{"type":"chunk","time":1792561944811,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"} +{"type":"tool/call","data":{"turn":1,"step":3,"callId":"esm_denied","name":"run_code","arguments":"{\"code\":\"Harness.inspect(await dependency.denied().catch(error => error.message))\",\"description\":\"Catch a rejected dependency import\"}"}} {"type":"tool/result","data":{"turn":1,"step":3,"message":{"role":"tool","source":{"kind":"tool","callId":"esm_denied"},"toolCallId":"esm_denied","content":[{"type":"text","text":"Dependency import denied by runner"}],"isError":false,"id":"{{message:9}}"}},"sourceEventSeqs":[24],"surfaceOp":"append"} {"type":"step/end","data":{"turn":1,"step":3}} {"type":"step/start","data":{"turn":1,"step":4}} diff --git a/snapshots/session/ptc-script-retained/replay.override.json b/snapshots/session/ptc-script-retained/replay.override.json new file mode 100644 index 0000000000..4c333e77fb --- /dev/null +++ b/snapshots/session/ptc-script-retained/replay.override.json @@ -0,0 +1,74 @@ +[ + { + "kind": "chunks", + "chunks": [ + {"type":"block-start","index":0,"blockType":"tool-call"}, + {"type":"block-end","index":0,"block":{"type":"tool-call","id":"script_declare","name":"run_code","arguments":"{\"code\":\"let counter: number = 1; const alias = { value: counter }; function increment() { return ++counter }; class Box { [await Promise.resolve(\\\"read\\\")]() { return counter } };\",\"description\":\"Declare Script state with computed await\"}"}}, + {"type":"usage","usage":{"inputTokens":10,"outputTokens":5}}, + {"type":"finish","reason":{"kind":"tool-calls"}} + ] + }, + { + "kind": "chunks", + "chunks": [ + {"type":"block-start","index":0,"blockType":"tool-call"}, + {"type":"block-end","index":0,"block":{"type":"tool-call","id":"script_reuse","name":"run_code","arguments":"{\"code\":\"counter++; Harness.inspect(\\\"retained\\\", new Box().read()); Harness.inspect(JSON.stringify(({ count: increment(), alias: alias.value, answer: await(Promise.resolve(42)) }), null, 2))\",\"description\":\"Reuse retained Script declarations\"}"}}, + {"type":"usage","usage":{"inputTokens":10,"outputTokens":5}}, + {"type":"finish","reason":{"kind":"tool-calls"}} + ] + }, + { + "kind": "chunks", + "chunks": [ + {"type":"block-start","index":0,"blockType":"tool-call"}, + {"type":"block-end","index":0,"block":{"type":"tool-call","id":"script_binding","name":"run_code","arguments":"{\"code\":\"const cwdResult = await tools.bash({command:\\\"pwd\\\",description:\\\"Read working directory\\\"}); Harness.inspect(JSON.stringify(({ sameDirectory: cwdResult.stdout.text.trim() === process.cwd(), count: counter }), null, 2))\",\"description\":\"Call a host binding from Script\"}"}}, + {"type":"usage","usage":{"inputTokens":10,"outputTokens":5}}, + {"type":"finish","reason":{"kind":"tool-calls"}} + ] + }, + { + "kind": "chunks", + "chunks": [ + {"type":"block-start","index":0,"blockType":"tool-call"}, + {"type":"block-end","index":0,"block":{"type":"tool-call","id":"script_temporary_exit","name":"run_code","arguments":"{\"code\":\"process.exit(7)\",\"description\":\"End a separate temporary Script process\",\"sandbox_permissions\":\"danger-full-access\",\"justification\":\"Approve a separate temporary process to check that its exit leaves retained variables intact.\"}"}}, + {"type":"usage","usage":{"inputTokens":10,"outputTokens":5}}, + {"type":"finish","reason":{"kind":"tool-calls"}} + ] + }, + { + "kind": "chunks", + "chunks": [ + {"type":"block-start","index":0,"blockType":"tool-call"}, + {"type":"block-end","index":0,"block":{"type":"tool-call","id":"script_after_temporary","name":"run_code","arguments":"{\"code\":\"Harness.inspect(JSON.stringify(({ count: counter, fromClass: new Box().read() }), null, 2))\",\"description\":\"Verify retained Script state after temporary exit\"}"}}, + {"type":"usage","usage":{"inputTokens":10,"outputTokens":5}}, + {"type":"finish","reason":{"kind":"tool-calls"}} + ] + }, + { + "kind": "chunks", + "chunks": [ + {"type":"block-start","index":0,"blockType":"tool-call"}, + {"type":"block-end","index":0,"block":{"type":"tool-call","id":"script_exit","name":"run_code","arguments":"{\"code\":\"process.exit(7)\",\"description\":\"End the Script process\"}"}}, + {"type":"usage","usage":{"inputTokens":10,"outputTokens":5}}, + {"type":"finish","reason":{"kind":"tool-calls"}} + ] + }, + { + "kind": "chunks", + "chunks": [ + {"type":"block-start","index":0,"blockType":"tool-call"}, + {"type":"block-end","index":0,"block":{"type":"tool-call","id":"script_recover","name":"run_code","arguments":"{\"code\":\"Harness.inspect(JSON.stringify(({ previous: typeof counter, recreated: await Promise.resolve(4) }), null, 2))\",\"description\":\"Verify Script state was cleared\"}"}}, + {"type":"usage","usage":{"inputTokens":10,"outputTokens":5}}, + {"type":"finish","reason":{"kind":"tool-calls"}} + ] + }, + { + "kind": "chunks", + "chunks": [ + {"type":"block-start","index":0,"blockType":"text"}, + {"type":"block-end","index":0,"block":{"type":"text","text":"Verified Script completion, host bindings, and state retention and recovery."}}, + {"type":"usage","usage":{"inputTokens":10,"outputTokens":2}}, + {"type":"finish","reason":{"kind":"stop"}} + ] + } +] diff --git a/snapshots/session/ptc-script-retained/session.v4.jsonl b/snapshots/session/ptc-script-retained/session.v4.jsonl index 30f762eae2..1d2ac0b178 100644 --- a/snapshots/session/ptc-script-retained/session.v4.jsonl +++ b/snapshots/session/ptc-script-retained/session.v4.jsonl @@ -17,13 +17,13 @@ {"type":"tool/result","data":{"turn":1,"step":1,"message":{"role":"tool","source":{"kind":"tool","callId":"script_declare"},"toolCallId":"script_declare","content":[{"type":"text","text":"(run_code completed with no output)"}],"isError":false,"id":"{{message:5}}"}},"sourceEventSeqs":[14],"surfaceOp":"append"} {"type":"step/end","data":{"turn":1,"step":1}} {"type":"step/start","data":{"turn":1,"step":2}} -{"type":"assistant/message","data":{"turn":1,"step":2,"message":{"role":"assistant","content":[{"type":"tool-call","id":"script_reuse","name":"run_code","arguments":"{\"code\":\"counter++; console.log(\\\"retained\\\", new Box().read()); ({ count: increment(), alias: alias.value, answer: await(Promise.resolve(42)) })\",\"description\":\"Reuse retained Script declarations\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:6}}"},"usage":{"inputTokens":10,"outputTokens":5},"stream":[{"type":"chunk","time":1792571730877,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}},{"type":"chunk","time":1792571730878,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"script_reuse","name":"run_code","arguments":"{\"code\":\"counter++; console.log(\\\"retained\\\", new Box().read()); ({ count: increment(), alias: alias.value, answer: await(Promise.resolve(42)) })\",\"description\":\"Reuse retained Script declarations\"}"}}},{"type":"chunk","time":1792571730878,"chunk":{"type":"usage","usage":{"inputTokens":10,"outputTokens":5}}},{"type":"chunk","time":1792571730878,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"} -{"type":"tool/call","data":{"turn":1,"step":2,"callId":"script_reuse","name":"run_code","arguments":"{\"code\":\"counter++; console.log(\\\"retained\\\", new Box().read()); ({ count: increment(), alias: alias.value, answer: await(Promise.resolve(42)) })\",\"description\":\"Reuse retained Script declarations\"}"}} +{"type":"assistant/message","data":{"turn":1,"step":2,"message":{"role":"assistant","content":[{"type":"tool-call","id":"script_reuse","name":"run_code","arguments":"{\"code\":\"counter++; Harness.inspect(\\\"retained\\\", new Box().read()); Harness.inspect(JSON.stringify(({ count: increment(), alias: alias.value, answer: await(Promise.resolve(42)) }), null, 2))\",\"description\":\"Reuse retained Script declarations\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:6}}"},"usage":{"inputTokens":10,"outputTokens":5},"stream":[{"type":"chunk","time":1792571730877,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}},{"type":"chunk","time":1792571730878,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"script_reuse","name":"run_code","arguments":"{\"code\":\"counter++; Harness.inspect(\\\"retained\\\", new Box().read()); Harness.inspect(JSON.stringify(({ count: increment(), alias: alias.value, answer: await(Promise.resolve(42)) }), null, 2))\",\"description\":\"Reuse retained Script declarations\"}"}}},{"type":"chunk","time":1792571730878,"chunk":{"type":"usage","usage":{"inputTokens":10,"outputTokens":5}}},{"type":"chunk","time":1792571730878,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"} +{"type":"tool/call","data":{"turn":1,"step":2,"callId":"script_reuse","name":"run_code","arguments":"{\"code\":\"counter++; Harness.inspect(\\\"retained\\\", new Box().read()); Harness.inspect(JSON.stringify(({ count: increment(), alias: alias.value, answer: await(Promise.resolve(42)) }), null, 2))\",\"description\":\"Reuse retained Script declarations\"}"}} {"type":"tool/result","data":{"turn":1,"step":2,"message":{"role":"tool","source":{"kind":"tool","callId":"script_reuse"},"toolCallId":"script_reuse","content":[{"type":"text","text":"retained 2\n{\n \"count\": 3,\n \"alias\": 1,\n \"answer\": 42\n}"}],"isError":false,"id":"{{message:7}}"}},"sourceEventSeqs":[19],"surfaceOp":"append"} {"type":"step/end","data":{"turn":1,"step":2}} {"type":"step/start","data":{"turn":1,"step":3}} -{"type":"assistant/message","data":{"turn":1,"step":3,"message":{"role":"assistant","content":[{"type":"tool-call","id":"script_binding","name":"run_code","arguments":"{\"code\":\"const cwdResult = await tools.bash({command:\\\"pwd\\\",description:\\\"Read working directory\\\"}); ({ sameDirectory: cwdResult.stdout.text.trim() === process.cwd(), count: counter })\",\"description\":\"Call a host binding from Script\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:8}}"},"usage":{"inputTokens":10,"outputTokens":5},"stream":[{"type":"chunk","time":1792571730917,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}},{"type":"chunk","time":1792571730917,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"script_binding","name":"run_code","arguments":"{\"code\":\"const cwdResult = await tools.bash({command:\\\"pwd\\\",description:\\\"Read working directory\\\"}); ({ sameDirectory: cwdResult.stdout.text.trim() === process.cwd(), count: counter })\",\"description\":\"Call a host binding from Script\"}"}}},{"type":"chunk","time":1792571730917,"chunk":{"type":"usage","usage":{"inputTokens":10,"outputTokens":5}}},{"type":"chunk","time":1792571730917,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"} -{"type":"tool/call","data":{"turn":1,"step":3,"callId":"script_binding","name":"run_code","arguments":"{\"code\":\"const cwdResult = await tools.bash({command:\\\"pwd\\\",description:\\\"Read working directory\\\"}); ({ sameDirectory: cwdResult.stdout.text.trim() === process.cwd(), count: counter })\",\"description\":\"Call a host binding from Script\"}"}} +{"type":"assistant/message","data":{"turn":1,"step":3,"message":{"role":"assistant","content":[{"type":"tool-call","id":"script_binding","name":"run_code","arguments":"{\"code\":\"const cwdResult = await tools.bash({command:\\\"pwd\\\",description:\\\"Read working directory\\\"}); Harness.inspect(JSON.stringify(({ sameDirectory: cwdResult.stdout.text.trim() === process.cwd(), count: counter }), null, 2))\",\"description\":\"Call a host binding from Script\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:8}}"},"usage":{"inputTokens":10,"outputTokens":5},"stream":[{"type":"chunk","time":1792571730917,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}},{"type":"chunk","time":1792571730917,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"script_binding","name":"run_code","arguments":"{\"code\":\"const cwdResult = await tools.bash({command:\\\"pwd\\\",description:\\\"Read working directory\\\"}); Harness.inspect(JSON.stringify(({ sameDirectory: cwdResult.stdout.text.trim() === process.cwd(), count: counter }), null, 2))\",\"description\":\"Call a host binding from Script\"}"}}},{"type":"chunk","time":1792571730917,"chunk":{"type":"usage","usage":{"inputTokens":10,"outputTokens":5}}},{"type":"chunk","time":1792571730917,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"} +{"type":"tool/call","data":{"turn":1,"step":3,"callId":"script_binding","name":"run_code","arguments":"{\"code\":\"const cwdResult = await tools.bash({command:\\\"pwd\\\",description:\\\"Read working directory\\\"}); Harness.inspect(JSON.stringify(({ sameDirectory: cwdResult.stdout.text.trim() === process.cwd(), count: counter }), null, 2))\",\"description\":\"Call a host binding from Script\"}"}} {"type":"tool/ptc-dispatch-start","data":{"rootCallId":"script_binding","parentCallId":"script_binding","subCallId":"script_binding:ptc:1","name":"bash","arguments":{"command":"pwd","description":"Read working directory"}}} {"type":"job/registered","data":{"jobId":"{{id:1}}","owner":"{{session:1}}","kind":"bash","label":"pwd","startedAt":1792571730931,"toolCall":{"callId":"script_binding:ptc:1","rootCallId":"script_binding"}}} {"type":"job/settled","data":{"jobId":"{{id:1}}","owner":"{{session:1}}","kind":"bash","label":"pwd","startedAt":1792571730931,"toolCall":{"callId":"script_binding:ptc:1","rootCallId":"script_binding"},"finishedAt":1792571731026,"status":"completed","cause":"producer","producerReported":true,"detail":"exit code: 0"}} @@ -38,8 +38,8 @@ {"type":"tool/result","data":{"turn":1,"step":4,"message":{"role":"tool","source":{"kind":"tool","callId":"script_temporary_exit"},"toolCallId":"script_temporary_exit","content":[{"type":"text","text":"Error: code run failed (worker-exit): Node process exited (7, signal null)\nFile sandbox: danger-full-access."}],"isError":true,"id":"{{message:11}}"},"error":{"name":"CodeRunFailedError","code":"CODE_RUN_FAILED"}},"sourceEventSeqs":[33],"surfaceOp":"append"} {"type":"step/end","data":{"turn":1,"step":4}} {"type":"step/start","data":{"turn":1,"step":5}} -{"type":"assistant/message","data":{"turn":1,"step":5,"message":{"role":"assistant","content":[{"type":"tool-call","id":"script_after_temporary","name":"run_code","arguments":"{\"code\":\"({ count: counter, fromClass: new Box().read() })\",\"description\":\"Verify retained Script state after temporary exit\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:12}}"},"usage":{"inputTokens":10,"outputTokens":5},"stream":[{"type":"chunk","time":1792571731213,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}},{"type":"chunk","time":1792571731213,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"script_after_temporary","name":"run_code","arguments":"{\"code\":\"({ count: counter, fromClass: new Box().read() })\",\"description\":\"Verify retained Script state after temporary exit\"}"}}},{"type":"chunk","time":1792571731213,"chunk":{"type":"usage","usage":{"inputTokens":10,"outputTokens":5}}},{"type":"chunk","time":1792571731213,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"} -{"type":"tool/call","data":{"turn":1,"step":5,"callId":"script_after_temporary","name":"run_code","arguments":"{\"code\":\"({ count: counter, fromClass: new Box().read() })\",\"description\":\"Verify retained Script state after temporary exit\"}"}} +{"type":"assistant/message","data":{"turn":1,"step":5,"message":{"role":"assistant","content":[{"type":"tool-call","id":"script_after_temporary","name":"run_code","arguments":"{\"code\":\"Harness.inspect(JSON.stringify(({ count: counter, fromClass: new Box().read() }), null, 2))\",\"description\":\"Verify retained Script state after temporary exit\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:12}}"},"usage":{"inputTokens":10,"outputTokens":5},"stream":[{"type":"chunk","time":1792571731213,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}},{"type":"chunk","time":1792571731213,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"script_after_temporary","name":"run_code","arguments":"{\"code\":\"Harness.inspect(JSON.stringify(({ count: counter, fromClass: new Box().read() }), null, 2))\",\"description\":\"Verify retained Script state after temporary exit\"}"}}},{"type":"chunk","time":1792571731213,"chunk":{"type":"usage","usage":{"inputTokens":10,"outputTokens":5}}},{"type":"chunk","time":1792571731213,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"} +{"type":"tool/call","data":{"turn":1,"step":5,"callId":"script_after_temporary","name":"run_code","arguments":"{\"code\":\"Harness.inspect(JSON.stringify(({ count: counter, fromClass: new Box().read() }), null, 2))\",\"description\":\"Verify retained Script state after temporary exit\"}"}} {"type":"tool/result","data":{"turn":1,"step":5,"message":{"role":"tool","source":{"kind":"tool","callId":"script_after_temporary"},"toolCallId":"script_after_temporary","content":[{"type":"text","text":"{\n \"count\": 3,\n \"fromClass\": 3\n}"}],"isError":false,"id":"{{message:13}}"}},"sourceEventSeqs":[40],"surfaceOp":"append"} {"type":"step/end","data":{"turn":1,"step":5}} {"type":"step/start","data":{"turn":1,"step":6}} @@ -48,11 +48,11 @@ {"type":"tool/result","data":{"turn":1,"step":6,"message":{"role":"tool","source":{"kind":"tool","callId":"script_exit"},"toolCallId":"script_exit","content":[{"type":"text","text":"Error: code run failed (worker-exit): Node process exited (7, signal null); retained variables were cleared\nFile sandbox: workspace-write; enforcement: full."}],"isError":true,"id":"{{message:15}}"},"error":{"name":"CodeRunFailedError","code":"CODE_RUN_FAILED"}},"sourceEventSeqs":[45],"surfaceOp":"append"} {"type":"step/end","data":{"turn":1,"step":6}} {"type":"step/start","data":{"turn":1,"step":7}} -{"type":"assistant/message","data":{"turn":1,"step":7,"message":{"role":"assistant","content":[{"type":"tool-call","id":"script_recover","name":"run_code","arguments":"{\"code\":\"({ previous: typeof counter, recreated: await Promise.resolve(4) })\",\"description\":\"Verify Script state was cleared\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:16}}"},"usage":{"inputTokens":10,"outputTokens":5},"stream":[{"type":"chunk","time":1792571731278,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}},{"type":"chunk","time":1792571731278,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"script_recover","name":"run_code","arguments":"{\"code\":\"({ previous: typeof counter, recreated: await Promise.resolve(4) })\",\"description\":\"Verify Script state was cleared\"}"}}},{"type":"chunk","time":1792571731278,"chunk":{"type":"usage","usage":{"inputTokens":10,"outputTokens":5}}},{"type":"chunk","time":1792571731278,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"} -{"type":"tool/call","data":{"turn":1,"step":7,"callId":"script_recover","name":"run_code","arguments":"{\"code\":\"({ previous: typeof counter, recreated: await Promise.resolve(4) })\",\"description\":\"Verify Script state was cleared\"}"}} +{"type":"assistant/message","data":{"turn":1,"step":7,"message":{"role":"assistant","content":[{"type":"tool-call","id":"script_recover","name":"run_code","arguments":"{\"code\":\"Harness.inspect(JSON.stringify(({ previous: typeof counter, recreated: await Promise.resolve(4) }), null, 2))\",\"description\":\"Verify Script state was cleared\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:16}}"},"usage":{"inputTokens":10,"outputTokens":5},"stream":[{"type":"chunk","time":1792571731278,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}},{"type":"chunk","time":1792571731278,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"script_recover","name":"run_code","arguments":"{\"code\":\"Harness.inspect(JSON.stringify(({ previous: typeof counter, recreated: await Promise.resolve(4) }), null, 2))\",\"description\":\"Verify Script state was cleared\"}"}}},{"type":"chunk","time":1792571731278,"chunk":{"type":"usage","usage":{"inputTokens":10,"outputTokens":5}}},{"type":"chunk","time":1792571731278,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"} +{"type":"tool/call","data":{"turn":1,"step":7,"callId":"script_recover","name":"run_code","arguments":"{\"code\":\"Harness.inspect(JSON.stringify(({ previous: typeof counter, recreated: await Promise.resolve(4) }), null, 2))\",\"description\":\"Verify Script state was cleared\"}"}} {"type":"tool/result","data":{"turn":1,"step":7,"message":{"role":"tool","source":{"kind":"tool","callId":"script_recover"},"toolCallId":"script_recover","content":[{"type":"text","text":"{\n \"previous\": \"undefined\",\n \"recreated\": 4\n}"}],"isError":false,"id":"{{message:17}}"}},"sourceEventSeqs":[50],"surfaceOp":"append"} {"type":"step/end","data":{"turn":1,"step":7}} {"type":"step/start","data":{"turn":1,"step":8}} -{"type":"assistant/message","data":{"turn":1,"step":8,"message":{"role":"assistant","content":[{"type":"text","text":"Verified Script completion, host bindings, and state retention and recovery."}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:18}}"},"usage":{"inputTokens":10,"outputTokens":2},"stream":[{"type":"chunk","time":1792571731484,"chunk":{"type":"block-start","index":0,"blockType":"text"}},{"type":"text-chunks","time0":1792571731484,"index":0,"dt":[],"texts":["Verified Script completion, host bindings, and state retention and recovery."]},{"type":"chunk","time":1792571731484,"chunk":{"type":"block-end","index":0,"block":{"type":"text","text":"Verified Script completion, host bindings, and state retention and recovery."}}},{"type":"chunk","time":1792571731484,"chunk":{"type":"usage","usage":{"inputTokens":10,"outputTokens":2}}},{"type":"chunk","time":1792571731484,"chunk":{"type":"finish","reason":{"kind":"stop"}}}]},"surfaceOp":"append"} +{"type":"assistant/message","data":{"turn":1,"step":8,"message":{"role":"assistant","content":[{"type":"text","text":"Verified Script completion, host bindings, and state retention and recovery."}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:18}}"},"usage":{"inputTokens":10,"outputTokens":2},"stream":[{"type":"chunk","time":1794195794578,"chunk":{"type":"block-start","index":0,"blockType":"text"}},{"type":"chunk","time":1794195794578,"chunk":{"type":"block-end","index":0,"block":{"type":"text","text":"Verified Script completion, host bindings, and state retention and recovery."}}},{"type":"chunk","time":1794195794578,"chunk":{"type":"usage","usage":{"inputTokens":10,"outputTokens":2}}},{"type":"chunk","time":1794195794578,"chunk":{"type":"finish","reason":{"kind":"stop"}}}]},"surfaceOp":"append"} {"type":"step/end","data":{"turn":1,"step":8}} {"type":"turn/end","data":{"turn":1,"reason":{"kind":"completed"}}} diff --git a/snapshots/session/ptc-script-retained/snapshot.yml b/snapshots/session/ptc-script-retained/snapshot.yml index 3324ec5ec3..525d219525 100644 --- a/snapshots/session/ptc-script-retained/snapshot.yml +++ b/snapshots/session/ptc-script-retained/snapshot.yml @@ -10,3 +10,5 @@ header: permission: workspace-write workspace: setup: cua-script-guidance +replay: + override: true diff --git a/snapshots/session/ptc-script-retained/system-prompt.expected.md b/snapshots/session/ptc-script-retained/system-prompt.expected.md index 2a6fe82a6e..662e9182ee 100644 --- a/snapshots/session/ptc-script-retained/system-prompt.expected.md +++ b/snapshots/session/ptc-script-retained/system-prompt.expected.md @@ -39,7 +39,7 @@ macOS: accessibility tree operations are available at app or window level. Simul Use cua to observe and operate native applications. Always use cua.getCapabilities() to confirm whether a specific API is available on the current platform; its reported capabilities take precedence over availability statements in these instructions. All methods are async; observation options default to { emit: true }, and emit: false suppresses sink output only, leaving returned values unchanged. Actions report dispatch evidence, not verified completion. If sessionLocked is true or an operation reports SESSION_LOCKED, stop UI actions and ask the user to unlock the desktop session; null or unknown means verify a usable unlocked desktop session first. cua.getApp requests user consent; if access is denied or revoked, stop using that application until the user explicitly permits another request, and never bypass a refusal or switch tools to avoid it. Never replay uncertain input without a fresh observation. -In persistent PTC, use the host-provided bridge initialization once and retain the returned objects; cua comes from that bridge and is not a preinstalled global. Use void await for observations that emit, and never stringify screenshot image bytes. +In persistent PTC, use the host-provided bridge initialization once and retain the returned objects; cua comes from that bridge and is not a preinstalled global. Observations emit through Harness.inspect; never stringify screenshot image bytes. Signatures show resolved return values after await. ActionResult = { dispatched: boolean, foregroundChanged?: boolean | null }; foregroundChanged is omitted when not sampled and null when the sample is unavailable. JsonValue = string | number | boolean | null | JsonValue[] | { [key: string]: JsonValue }; numbers must be finite. App is the application object with readonly app.appHandle: string (opaque) and the methods listed below for app level. Window is the window object with readonly window.windowId: number and the methods listed below for window level. Neither object carries pid. @@ -91,27 +91,31 @@ Persistent PTC initialization: ```ts var { connectCua } = await import("file://{{cwd}}/cua-runtime/lib/bridge.js"); -var cua = connectCua(() => tools.cua, text => console.log(text)); +var cua = connectCua(() => tools.cua, text => Harness.inspect(text)); ``` ## Writing code for run_code `run_code` takes two required arguments: `code`, the program to execute, and `description`, a short summary of what the program does. The declarations below are SDK bindings for this program. A declaration does not make its name a directly callable tool; only names supplied as separate tool schemas may be called directly. When no separate `bash` schema is supplied, invoke a declared `bash` binding inside `run_code`: -`run_code({ code: "console.log(await tools.bash({ description: 'Show current directory', command: 'pwd' }))", description: "Show current directory" })` +`run_code({ code: "Harness.inspect(await tools.bash({ description: 'Show current directory', command: 'pwd' }))", description: "Show current directory" })` Inside the program: - Call tools as `await tools.name(args)` — quoted access for exotic names: `tools["my-tool"](args)`. Every call resolves to the tool's typed canonical JSON value. Tool arguments must be lossless JSON. - A FAILED tool call rejects with `ToolCallError`, whose `toolName` identifies the failed tool and whose `message` is human-readable — `try/catch` it to handle and continue. - Independent read-only calls MAY overlap under `Promise.all` (safe calls run concurrently; mutating calls run alone, in submission order). Sequence dependent work with `await`. -- Only values emitted as program output enter the conversation. A successful tool result containing an image is attached after the run so you can inspect it on the next step; every other intermediate result stays out of the conversation, so extract just what you need. +- Show selected results with `Harness.inspect(...values)`. Return values and final expressions are not displayed. A successful tool result containing an image is attached after the run so you can inspect it on the next step; every other intermediate result stays out of the conversation, so extract just what you need. Program-only SDK bindings: ```ts type JsonValue = null | boolean | number | string | JsonValue[] | { [key: string]: JsonValue } +declare namespace Harness { + function inspect(...values: unknown[]): void; +} + interface ToolArgsMap { /** Create, update, delete, or move UTF-8 text files with one patch; prefer it over multiple edit calls when a change spans several files or separated regions. */ apply_patch: { diff --git a/snapshots/session/ptc-timeout-fallback/replay.override.json b/snapshots/session/ptc-timeout-fallback/replay.override.json new file mode 100644 index 0000000000..8b51e2f2b4 --- /dev/null +++ b/snapshots/session/ptc-timeout-fallback/replay.override.json @@ -0,0 +1,38 @@ +[ + { + "kind": "chunks", + "chunks": [ + {"type":"block-start","index":0,"blockType":"tool-call"}, + {"type":"block-end","index":0,"block":{"type":"tool-call","id":"call_keep","name":"run_code","arguments":"{\"code\":\"var kept = 42; Harness.inspect(kept)\",\"description\":\"Retain a value before execution times out\"}"}}, + {"type":"usage","usage":{"inputTokens":10,"outputTokens":5}}, + {"type":"finish","reason":{"kind":"tool-calls"}} + ] + }, + { + "kind": "chunks", + "chunks": [ + {"type":"block-start","index":0,"blockType":"tool-call"}, + {"type":"block-end","index":0,"block":{"type":"tool-call","id":"call_timeout","name":"run_code","arguments":"{\"code\":\"Harness.inspect(\\\"output before timeout\\\"); while (true) {}\",\"description\":\"Print partial output before an infinite loop\"}"}}, + {"type":"usage","usage":{"inputTokens":10,"outputTokens":5}}, + {"type":"finish","reason":{"kind":"tool-calls"}} + ] + }, + { + "kind": "chunks", + "chunks": [ + {"type":"block-start","index":0,"blockType":"tool-call"}, + {"type":"block-end","index":0,"block":{"type":"tool-call","id":"call_check","name":"run_code","arguments":"{\"code\":\"Harness.inspect(JSON.stringify(({ retained: typeof kept }), null, 2))\",\"description\":\"Check retained variables after execution times out\"}"}}, + {"type":"usage","usage":{"inputTokens":10,"outputTokens":5}}, + {"type":"finish","reason":{"kind":"tool-calls"}} + ] + }, + { + "kind": "chunks", + "chunks": [ + {"type":"block-start","index":0,"blockType":"text"}, + {"type":"block-end","index":0,"block":{"type":"text","text":"The execution timed out after printing output. The runtime was terminated and retained variables were cleared."}}, + {"type":"usage","usage":{"inputTokens":10,"outputTokens":5}}, + {"type":"finish","reason":{"kind":"stop"}} + ] + } +] diff --git a/snapshots/session/ptc-timeout-fallback/session.v4.jsonl b/snapshots/session/ptc-timeout-fallback/session.v4.jsonl index beea9a233d..e4723242d4 100644 --- a/snapshots/session/ptc-timeout-fallback/session.v4.jsonl +++ b/snapshots/session/ptc-timeout-fallback/session.v4.jsonl @@ -12,21 +12,21 @@ {"type":"request/header","data":{"header":{"config":{"provider":"deepseek-official","model":"deepseek-v4-flash"},"tools":"{{tools}}"},"reason":"initial"}} {"type":"request/context","data":{"provider":"deepseek-official","model":"deepseek-v4-flash"}} {"type":"session/title","data":{"title":"Retain a value, print output","messageSeqs":[8],"source":{"kind":"fallback"}}} -{"type":"assistant/message","data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"tool-call","id":"call_keep","name":"run_code","arguments":"{\"code\":\"var kept = 42; kept\",\"description\":\"Retain a value before execution times out\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:4}}"},"usage":{"inputTokens":10,"outputTokens":5},"stream":[{"type":"chunk","time":1793255395656,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}},{"type":"tool-call-chunks","time0":1793255395656,"index":0,"dt":[],"id":"call_keep","name":"run_code","args":["{\"code\":\"var kept = 42; kept\",\"description\":\"Retain a value before execution times out\"}"]},{"type":"chunk","time":1793255395656,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"call_keep","name":"run_code","arguments":"{\"code\":\"var kept = 42; kept\",\"description\":\"Retain a value before execution times out\"}"}}},{"type":"chunk","time":1793255395656,"chunk":{"type":"usage","usage":{"inputTokens":10,"outputTokens":5}}},{"type":"chunk","time":1793255395656,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"} -{"type":"tool/call","data":{"turn":1,"step":1,"callId":"call_keep","name":"run_code","arguments":"{\"code\":\"var kept = 42; kept\",\"description\":\"Retain a value before execution times out\"}"}} +{"type":"assistant/message","data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"tool-call","id":"call_keep","name":"run_code","arguments":"{\"code\":\"var kept = 42; Harness.inspect(kept)\",\"description\":\"Retain a value before execution times out\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:4}}"},"usage":{"inputTokens":10,"outputTokens":5},"stream":[{"type":"chunk","time":1794195808568,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}},{"type":"chunk","time":1794195808568,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"call_keep","name":"run_code","arguments":"{\"code\":\"var kept = 42; Harness.inspect(kept)\",\"description\":\"Retain a value before execution times out\"}"}}},{"type":"chunk","time":1794195808568,"chunk":{"type":"usage","usage":{"inputTokens":10,"outputTokens":5}}},{"type":"chunk","time":1794195808568,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"} +{"type":"tool/call","data":{"turn":1,"step":1,"callId":"call_keep","name":"run_code","arguments":"{\"code\":\"var kept = 42; Harness.inspect(kept)\",\"description\":\"Retain a value before execution times out\"}"}} {"type":"tool/result","data":{"turn":1,"step":1,"message":{"role":"tool","source":{"kind":"tool","callId":"call_keep"},"toolCallId":"call_keep","content":[{"type":"text","text":"42"}],"isError":false,"id":"{{message:5}}"}},"sourceEventSeqs":[14],"surfaceOp":"append"} {"type":"step/end","data":{"turn":1,"step":1}} {"type":"step/start","data":{"turn":1,"step":2}} -{"type":"assistant/message","data":{"turn":1,"step":2,"message":{"role":"assistant","content":[{"type":"tool-call","id":"call_timeout","name":"run_code","arguments":"{\"code\":\"console.log(\\\"output before timeout\\\"); while (true) {}\",\"description\":\"Print partial output before an infinite loop\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:6}}"},"usage":{"inputTokens":10,"outputTokens":5},"stream":[{"type":"chunk","time":1793255395835,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}},{"type":"tool-call-chunks","time0":1793255395835,"index":0,"dt":[],"id":"call_timeout","name":"run_code","args":["{\"code\":\"console.log(\\\"output before timeout\\\"); while (true) {}\",\"description\":\"Print partial output before an infinite loop\"}"]},{"type":"chunk","time":1793255395835,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"call_timeout","name":"run_code","arguments":"{\"code\":\"console.log(\\\"output before timeout\\\"); while (true) {}\",\"description\":\"Print partial output before an infinite loop\"}"}}},{"type":"chunk","time":1793255395835,"chunk":{"type":"usage","usage":{"inputTokens":10,"outputTokens":5}}},{"type":"chunk","time":1793255395835,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"} -{"type":"tool/call","data":{"turn":1,"step":2,"callId":"call_timeout","name":"run_code","arguments":"{\"code\":\"console.log(\\\"output before timeout\\\"); while (true) {}\",\"description\":\"Print partial output before an infinite loop\"}"}} +{"type":"assistant/message","data":{"turn":1,"step":2,"message":{"role":"assistant","content":[{"type":"tool-call","id":"call_timeout","name":"run_code","arguments":"{\"code\":\"Harness.inspect(\\\"output before timeout\\\"); while (true) {}\",\"description\":\"Print partial output before an infinite loop\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:6}}"},"usage":{"inputTokens":10,"outputTokens":5},"stream":[{"type":"chunk","time":1794195808820,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}},{"type":"chunk","time":1794195808820,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"call_timeout","name":"run_code","arguments":"{\"code\":\"Harness.inspect(\\\"output before timeout\\\"); while (true) {}\",\"description\":\"Print partial output before an infinite loop\"}"}}},{"type":"chunk","time":1794195808820,"chunk":{"type":"usage","usage":{"inputTokens":10,"outputTokens":5}}},{"type":"chunk","time":1794195808820,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"} +{"type":"tool/call","data":{"turn":1,"step":2,"callId":"call_timeout","name":"run_code","arguments":"{\"code\":\"Harness.inspect(\\\"output before timeout\\\"); while (true) {}\",\"description\":\"Print partial output before an infinite loop\"}"}} {"type":"tool/result","data":{"turn":1,"step":2,"message":{"role":"tool","source":{"kind":"tool","callId":"call_timeout"},"toolCallId":"call_timeout","content":[{"type":"text","text":"Error: code run failed (timeout): execution timed out during execution (10000ms); retained variables were cleared\nCaptured output:\noutput before timeout\nFile sandbox: workspace-write; enforcement: full."}],"isError":true,"id":"{{message:7}}"},"error":{"name":"CodeRunFailedError","code":"CODE_RUN_FAILED"}},"sourceEventSeqs":[19],"surfaceOp":"append"} {"type":"step/end","data":{"turn":1,"step":2}} {"type":"step/start","data":{"turn":1,"step":3}} -{"type":"assistant/message","data":{"turn":1,"step":3,"message":{"role":"assistant","content":[{"type":"tool-call","id":"call_check","name":"run_code","arguments":"{\"code\":\"({ retained: typeof kept })\",\"description\":\"Check retained variables after execution times out\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:8}}"},"usage":{"inputTokens":10,"outputTokens":5},"stream":[{"type":"chunk","time":1793255405869,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}},{"type":"tool-call-chunks","time0":1793255405869,"index":0,"dt":[],"id":"call_check","name":"run_code","args":["{\"code\":\"({ retained: typeof kept })\",\"description\":\"Check retained variables after execution times out\"}"]},{"type":"chunk","time":1793255405869,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"call_check","name":"run_code","arguments":"{\"code\":\"({ retained: typeof kept })\",\"description\":\"Check retained variables after execution times out\"}"}}},{"type":"chunk","time":1793255405869,"chunk":{"type":"usage","usage":{"inputTokens":10,"outputTokens":5}}},{"type":"chunk","time":1793255405869,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"} -{"type":"tool/call","data":{"turn":1,"step":3,"callId":"call_check","name":"run_code","arguments":"{\"code\":\"({ retained: typeof kept })\",\"description\":\"Check retained variables after execution times out\"}"}} +{"type":"assistant/message","data":{"turn":1,"step":3,"message":{"role":"assistant","content":[{"type":"tool-call","id":"call_check","name":"run_code","arguments":"{\"code\":\"Harness.inspect(JSON.stringify(({ retained: typeof kept }), null, 2))\",\"description\":\"Check retained variables after execution times out\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:8}}"},"usage":{"inputTokens":10,"outputTokens":5},"stream":[{"type":"chunk","time":1794195819868,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}},{"type":"chunk","time":1794195819868,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"call_check","name":"run_code","arguments":"{\"code\":\"Harness.inspect(JSON.stringify(({ retained: typeof kept }), null, 2))\",\"description\":\"Check retained variables after execution times out\"}"}}},{"type":"chunk","time":1794195819869,"chunk":{"type":"usage","usage":{"inputTokens":10,"outputTokens":5}}},{"type":"chunk","time":1794195819869,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"} +{"type":"tool/call","data":{"turn":1,"step":3,"callId":"call_check","name":"run_code","arguments":"{\"code\":\"Harness.inspect(JSON.stringify(({ retained: typeof kept }), null, 2))\",\"description\":\"Check retained variables after execution times out\"}"}} {"type":"tool/result","data":{"turn":1,"step":3,"message":{"role":"tool","source":{"kind":"tool","callId":"call_check"},"toolCallId":"call_check","content":[{"type":"text","text":"{\n \"retained\": \"undefined\"\n}"}],"isError":false,"id":"{{message:9}}"}},"sourceEventSeqs":[24],"surfaceOp":"append"} {"type":"step/end","data":{"turn":1,"step":3}} {"type":"step/start","data":{"turn":1,"step":4}} -{"type":"assistant/message","data":{"turn":1,"step":4,"message":{"role":"assistant","content":[{"type":"text","text":"The execution timed out after printing output. The runtime was terminated and retained variables were cleared."}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:10}}"},"usage":{"inputTokens":10,"outputTokens":5},"stream":[{"type":"chunk","time":1793255406006,"chunk":{"type":"block-start","index":0,"blockType":"text"}},{"type":"text-chunks","time0":1793255406006,"index":0,"dt":[],"texts":["The execution timed out after printing output. The runtime was terminated and retained variables were cleared."]},{"type":"chunk","time":1793255406006,"chunk":{"type":"block-end","index":0,"block":{"type":"text","text":"The execution timed out after printing output. The runtime was terminated and retained variables were cleared."}}},{"type":"chunk","time":1793255406006,"chunk":{"type":"usage","usage":{"inputTokens":10,"outputTokens":5}}},{"type":"chunk","time":1793255406006,"chunk":{"type":"finish","reason":{"kind":"stop"}}}]},"surfaceOp":"append"} +{"type":"assistant/message","data":{"turn":1,"step":4,"message":{"role":"assistant","content":[{"type":"text","text":"The execution timed out after printing output. The runtime was terminated and retained variables were cleared."}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:10}}"},"usage":{"inputTokens":10,"outputTokens":5},"stream":[{"type":"chunk","time":1794195820049,"chunk":{"type":"block-start","index":0,"blockType":"text"}},{"type":"chunk","time":1794195820049,"chunk":{"type":"block-end","index":0,"block":{"type":"text","text":"The execution timed out after printing output. The runtime was terminated and retained variables were cleared."}}},{"type":"chunk","time":1794195820049,"chunk":{"type":"usage","usage":{"inputTokens":10,"outputTokens":5}}},{"type":"chunk","time":1794195820049,"chunk":{"type":"finish","reason":{"kind":"stop"}}}]},"surfaceOp":"append"} {"type":"step/end","data":{"turn":1,"step":4}} {"type":"turn/end","data":{"turn":1,"reason":{"kind":"completed"}}} diff --git a/snapshots/session/ptc-timeout-fallback/snapshot.yml b/snapshots/session/ptc-timeout-fallback/snapshot.yml index 4b40a5d803..f9d5773f77 100644 --- a/snapshots/session/ptc-timeout-fallback/snapshot.yml +++ b/snapshots/session/ptc-timeout-fallback/snapshot.yml @@ -8,3 +8,5 @@ header: pin: true systemPromptSource: ptc-persistent permission: workspace-write +replay: + override: true diff --git a/snapshots/session/ptc-timeout-fallback/tool-schemas.expected.json b/snapshots/session/ptc-timeout-fallback/tool-schemas.expected.json index fdaea15490..f4387d5f43 100644 --- a/snapshots/session/ptc-timeout-fallback/tool-schemas.expected.json +++ b/snapshots/session/ptc-timeout-fallback/tool-schemas.expected.json @@ -2,7 +2,7 @@ "initial": [ { "name": "run_code", - "description": "Execute a TypeScript program against the available tools. Takes two required arguments: `code`, the program to execute, and `description`, a short summary of what the program does. Call tools as `await tools.name(args)` per the declarations in the system prompt. Curate the program output. Image-bearing subtool results are attached after the run. The program contains TypeScript script statements; only erasable syntax is accepted, with no enum or namespaces. Type annotations are advisory. Top-level declarations and await are supported; top-level return is not supported. Emit output with console.log(...) or a final lossless JSON expression. The final expression is awaited if it is a Promise. Only printed values and that final expression become program output. Variables, functions, and imported modules remain in memory for this Agent across calls. Use await import(...) for modules. Relative paths use the supplied working directory. Direct filesystem access follows the environment's launch-time sandbox policy; process.env starts empty. Await asynchronous work: an uncaught callback exception or unhandled rejection terminates the process and clears its variables. Values from bindings and imports can belong to another realm; use Array.isArray for arrays and the supplied binding error classes for failures, because instanceof Array or Error may be false. Timeout and cancellation results say whether retained variables were preserved or cleared. Restart, sandbox changes, output overflow, or process failure clears them. Recreate missing variables and resources; conversation history does not restore state. Earlier mutations and tool effects are not rolled back. Approved sandbox escalations use a fresh environment and discard its variables afterward. Do not redeclare existing let, const, or class names; reuse mutable bindings or choose new names. Await tools and cleanup before returning; unfinished tools reject. After completion, timeout, or cancellation, new tool calls are rejected and captured output is discarded; background work may still change state. Await child-process completion before returning; raw output belongs to the active evaluation. If a native top-level let, const, or class initializer throws, its name may remain permanently uninitialized; use a new name or restart the environment. Each program starts in the Session's current directory. Running programs keep their initial directory. A sandbox escalation approves this complete program for one execution only. Nested tools retain their own policies and approvals. Request wider access only after evidence of a denial. Earlier effects may already have completed: inspect them before explicitly retrying. Programs are never replayed automatically.", + "description": "Execute a TypeScript program against the available tools. Takes two required arguments: `code`, the program to execute, and `description`, a short summary of what the program does. Call tools as `await tools.name(args)` per the declarations in the system prompt. Curate the program output. Image-bearing subtool results are attached after the run. The program contains TypeScript script statements; only erasable syntax is accepted, with no enum or namespaces. Type annotations are advisory. Top-level declarations and await are supported; top-level return is not supported. Console is unavailable. Use Harness.inspect(...values) for output. The final expression is awaited if it is a Promise. Variables, functions, and imported modules remain in memory for this Agent across calls. Use await import(...) for modules. Relative paths use the supplied working directory. Direct filesystem access follows the environment's launch-time sandbox policy; process.env starts empty. Await asynchronous work: an uncaught callback exception or unhandled rejection terminates the process and clears its variables. Values from bindings and imports can belong to another realm; use Array.isArray for arrays and the supplied binding error classes for failures, because instanceof Array or Error may be false. Timeout and cancellation results say whether retained variables were preserved or cleared. Restart, sandbox changes, output overflow, or process failure clears them. Recreate missing variables and resources; conversation history does not restore state. Earlier mutations and tool effects are not rolled back. Approved sandbox escalations use a fresh environment and discard its variables afterward. Do not redeclare existing let, const, or class names; reuse mutable bindings or choose new names. Await tools and cleanup before returning; unfinished tools reject. After completion, timeout, or cancellation, new tool calls are rejected and captured output is discarded; background work may still change state. Await child-process completion before returning; raw output belongs to the active evaluation. If a native top-level let, const, or class initializer throws, its name may remain permanently uninitialized; use a new name or restart the environment. Each program starts in the Session's current directory. Running programs keep their initial directory. A sandbox escalation approves this complete program for one execution only. Nested tools retain their own policies and approvals. Request wider access only after evidence of a denial. Earlier effects may already have completed: inspect them before explicitly retrying. Programs are never replayed automatically.", "parameters": { "type": "object", "properties": { diff --git a/snapshots/session/ptc-timeout/replay.override.json b/snapshots/session/ptc-timeout/replay.override.json new file mode 100644 index 0000000000..3b74f68eb7 --- /dev/null +++ b/snapshots/session/ptc-timeout/replay.override.json @@ -0,0 +1,38 @@ +[ + { + "kind": "chunks", + "chunks": [ + {"type":"block-start","index":0,"blockType":"tool-call"}, + {"type":"block-end","index":0,"block":{"type":"tool-call","id":"call_keep","name":"run_code","arguments":"{\"code\":\"var kept = 42; Harness.inspect(kept)\",\"description\":\"Retain a value before execution times out\"}"}}, + {"type":"usage","usage":{"inputTokens":10,"outputTokens":5}}, + {"type":"finish","reason":{"kind":"tool-calls"}} + ] + }, + { + "kind": "chunks", + "chunks": [ + {"type":"block-start","index":0,"blockType":"tool-call"}, + {"type":"block-end","index":0,"block":{"type":"tool-call","id":"call_timeout","name":"run_code","arguments":"{\"code\":\"Harness.inspect(\\\"output before timeout\\\"); try { await tools.bash({ description: \\\"Wait for cancellation\\\", command: \\\"echo should-not-run\\\" }) } catch { kept++ } finally { kept++ }\",\"description\":\"Print partial output before an asynchronous wait\"}"}}, + {"type":"usage","usage":{"inputTokens":10,"outputTokens":5}}, + {"type":"finish","reason":{"kind":"tool-calls"}} + ] + }, + { + "kind": "chunks", + "chunks": [ + {"type":"block-start","index":0,"blockType":"tool-call"}, + {"type":"block-end","index":0,"block":{"type":"tool-call","id":"call_check","name":"run_code","arguments":"{\"code\":\"Harness.inspect(JSON.stringify(({ retained: kept }), null, 2))\",\"description\":\"Check retained variables after execution times out\"}"}}, + {"type":"usage","usage":{"inputTokens":10,"outputTokens":5}}, + {"type":"finish","reason":{"kind":"tool-calls"}} + ] + }, + { + "kind": "chunks", + "chunks": [ + {"type":"block-start","index":0,"blockType":"text"}, + {"type":"block-end","index":0,"block":{"type":"text","text":"The execution timed out after printing output. The retained value is 44 after catch and finally handled tool cancellation."}}, + {"type":"usage","usage":{"inputTokens":10,"outputTokens":5}}, + {"type":"finish","reason":{"kind":"stop"}} + ] + } +] diff --git a/snapshots/session/ptc-timeout/session.v4.jsonl b/snapshots/session/ptc-timeout/session.v4.jsonl index 6fc5258948..20c1903eab 100644 --- a/snapshots/session/ptc-timeout/session.v4.jsonl +++ b/snapshots/session/ptc-timeout/session.v4.jsonl @@ -12,23 +12,23 @@ {"type":"request/header","data":{"header":{"config":{"provider":"deepseek-official","model":"deepseek-v4-flash"},"tools":"{{tools}}"},"reason":"initial"}} {"type":"request/context","data":{"provider":"deepseek-official","model":"deepseek-v4-flash"}} {"type":"session/title","data":{"title":"Retain a value, print output","messageSeqs":[8],"source":{"kind":"fallback"}}} -{"type":"assistant/message","data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"tool-call","id":"call_keep","name":"run_code","arguments":"{\"code\":\"var kept = 42; kept\",\"description\":\"Retain a value before execution times out\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:4}}"},"usage":{"inputTokens":10,"outputTokens":5},"stream":[{"type":"chunk","time":1793265704277,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}},{"type":"tool-call-chunks","time0":1793265704277,"index":0,"dt":[],"id":"call_keep","name":"run_code","args":["{\"code\":\"var kept = 42; kept\",\"description\":\"Retain a value before execution times out\"}"]},{"type":"chunk","time":1793265704277,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"call_keep","name":"run_code","arguments":"{\"code\":\"var kept = 42; kept\",\"description\":\"Retain a value before execution times out\"}"}}},{"type":"chunk","time":1793265704277,"chunk":{"type":"usage","usage":{"inputTokens":10,"outputTokens":5}}},{"type":"chunk","time":1793265704277,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"} -{"type":"tool/call","data":{"turn":1,"step":1,"callId":"call_keep","name":"run_code","arguments":"{\"code\":\"var kept = 42; kept\",\"description\":\"Retain a value before execution times out\"}"}} +{"type":"assistant/message","data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"tool-call","id":"call_keep","name":"run_code","arguments":"{\"code\":\"var kept = 42; Harness.inspect(kept)\",\"description\":\"Retain a value before execution times out\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:4}}"},"usage":{"inputTokens":10,"outputTokens":5},"stream":[{"type":"chunk","time":1794195795602,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}},{"type":"chunk","time":1794195795602,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"call_keep","name":"run_code","arguments":"{\"code\":\"var kept = 42; Harness.inspect(kept)\",\"description\":\"Retain a value before execution times out\"}"}}},{"type":"chunk","time":1794195795602,"chunk":{"type":"usage","usage":{"inputTokens":10,"outputTokens":5}}},{"type":"chunk","time":1794195795602,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"} +{"type":"tool/call","data":{"turn":1,"step":1,"callId":"call_keep","name":"run_code","arguments":"{\"code\":\"var kept = 42; Harness.inspect(kept)\",\"description\":\"Retain a value before execution times out\"}"}} {"type":"tool/result","data":{"turn":1,"step":1,"message":{"role":"tool","source":{"kind":"tool","callId":"call_keep"},"toolCallId":"call_keep","content":[{"type":"text","text":"42"}],"isError":false,"id":"{{message:5}}"}},"sourceEventSeqs":[14],"surfaceOp":"append"} {"type":"step/end","data":{"turn":1,"step":1}} {"type":"step/start","data":{"turn":1,"step":2}} -{"type":"assistant/message","data":{"turn":1,"step":2,"message":{"role":"assistant","content":[{"type":"tool-call","id":"call_timeout","name":"run_code","arguments":"{\"code\":\"console.log(\\\"output before timeout\\\"); try { await tools.bash({ description: \\\"Wait for cancellation\\\", command: \\\"echo should-not-run\\\" }) } catch { kept++ } finally { kept++ }\",\"description\":\"Print partial output before an asynchronous wait\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:6}}"},"usage":{"inputTokens":10,"outputTokens":5},"stream":[{"type":"chunk","time":1793265704504,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}},{"type":"tool-call-chunks","time0":1793265704505,"index":0,"dt":[],"id":"call_timeout","name":"run_code","args":["{\"code\":\"console.log(\\\"output before timeout\\\"); try { await tools.bash({ description: \\\"Wait for cancellation\\\", command: \\\"echo should-not-run\\\" }) } catch { kept++ } finally { kept++ }\",\"description\":\"Print partial output before an asynchronous wait\"}"]},{"type":"chunk","time":1793265704505,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"call_timeout","name":"run_code","arguments":"{\"code\":\"console.log(\\\"output before timeout\\\"); try { await tools.bash({ description: \\\"Wait for cancellation\\\", command: \\\"echo should-not-run\\\" }) } catch { kept++ } finally { kept++ }\",\"description\":\"Print partial output before an asynchronous wait\"}"}}},{"type":"chunk","time":1793265704505,"chunk":{"type":"usage","usage":{"inputTokens":10,"outputTokens":5}}},{"type":"chunk","time":1793265704505,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"} -{"type":"tool/call","data":{"turn":1,"step":2,"callId":"call_timeout","name":"run_code","arguments":"{\"code\":\"console.log(\\\"output before timeout\\\"); try { await tools.bash({ description: \\\"Wait for cancellation\\\", command: \\\"echo should-not-run\\\" }) } catch { kept++ } finally { kept++ }\",\"description\":\"Print partial output before an asynchronous wait\"}"}} +{"type":"assistant/message","data":{"turn":1,"step":2,"message":{"role":"assistant","content":[{"type":"tool-call","id":"call_timeout","name":"run_code","arguments":"{\"code\":\"Harness.inspect(\\\"output before timeout\\\"); try { await tools.bash({ description: \\\"Wait for cancellation\\\", command: \\\"echo should-not-run\\\" }) } catch { kept++ } finally { kept++ }\",\"description\":\"Print partial output before an asynchronous wait\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:6}}"},"usage":{"inputTokens":10,"outputTokens":5},"stream":[{"type":"chunk","time":1794195795813,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}},{"type":"chunk","time":1794195795813,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"call_timeout","name":"run_code","arguments":"{\"code\":\"Harness.inspect(\\\"output before timeout\\\"); try { await tools.bash({ description: \\\"Wait for cancellation\\\", command: \\\"echo should-not-run\\\" }) } catch { kept++ } finally { kept++ }\",\"description\":\"Print partial output before an asynchronous wait\"}"}}},{"type":"chunk","time":1794195795813,"chunk":{"type":"usage","usage":{"inputTokens":10,"outputTokens":5}}},{"type":"chunk","time":1794195795813,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"} +{"type":"tool/call","data":{"turn":1,"step":2,"callId":"call_timeout","name":"run_code","arguments":"{\"code\":\"Harness.inspect(\\\"output before timeout\\\"); try { await tools.bash({ description: \\\"Wait for cancellation\\\", command: \\\"echo should-not-run\\\" }) } catch { kept++ } finally { kept++ }\",\"description\":\"Print partial output before an asynchronous wait\"}"}} {"type":"tool/ptc-dispatch-start","data":{"rootCallId":"call_timeout","parentCallId":"call_timeout","subCallId":"call_timeout:ptc:1","name":"bash","arguments":{"description":"Wait for cancellation","command":"echo should-not-run"}}} {"type":"tool/ptc-dispatch","data":{"rootCallId":"call_timeout","parentCallId":"call_timeout","subCallId":"call_timeout:ptc:1","name":"bash","arguments":{"description":"Wait for cancellation","command":"echo should-not-run"},"isError":true,"error":{"name":"AbortError","code":"ABORTED_BEFORE_DISPATCH"},"content":[{"type":"text","text":"Error: tool call aborted before dispatch"}]}} {"type":"tool/result","data":{"turn":1,"step":2,"message":{"role":"tool","source":{"kind":"tool","callId":"call_timeout"},"toolCallId":"call_timeout","content":[{"type":"text","text":"Error: code run failed (timeout): execution timed out during execution (10000ms); retained variables were preserved\nCaptured output:\noutput before timeout\nFile sandbox: workspace-write; enforcement: full."}],"isError":true,"id":"{{message:7}}"},"error":{"name":"CodeRunFailedError","code":"CODE_RUN_FAILED"}},"sourceEventSeqs":[19],"surfaceOp":"append"} {"type":"step/end","data":{"turn":1,"step":2}} {"type":"step/start","data":{"turn":1,"step":3}} -{"type":"assistant/message","data":{"turn":1,"step":3,"message":{"role":"assistant","content":[{"type":"tool-call","id":"call_check","name":"run_code","arguments":"{\"code\":\"({ retained: kept })\",\"description\":\"Check retained variables after execution times out\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:8}}"},"usage":{"inputTokens":10,"outputTokens":5},"stream":[{"type":"chunk","time":1793265714532,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}},{"type":"tool-call-chunks","time0":1793265714532,"index":0,"dt":[],"id":"call_check","name":"run_code","args":["{\"code\":\"({ retained: kept })\",\"description\":\"Check retained variables after execution times out\"}"]},{"type":"chunk","time":1793265714532,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"call_check","name":"run_code","arguments":"{\"code\":\"({ retained: kept })\",\"description\":\"Check retained variables after execution times out\"}"}}},{"type":"chunk","time":1793265714532,"chunk":{"type":"usage","usage":{"inputTokens":10,"outputTokens":5}}},{"type":"chunk","time":1793265714532,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"} -{"type":"tool/call","data":{"turn":1,"step":3,"callId":"call_check","name":"run_code","arguments":"{\"code\":\"({ retained: kept })\",\"description\":\"Check retained variables after execution times out\"}"}} +{"type":"assistant/message","data":{"turn":1,"step":3,"message":{"role":"assistant","content":[{"type":"tool-call","id":"call_check","name":"run_code","arguments":"{\"code\":\"Harness.inspect(JSON.stringify(({ retained: kept }), null, 2))\",\"description\":\"Check retained variables after execution times out\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:8}}"},"usage":{"inputTokens":10,"outputTokens":5},"stream":[{"type":"chunk","time":1794195805887,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}},{"type":"chunk","time":1794195805887,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"call_check","name":"run_code","arguments":"{\"code\":\"Harness.inspect(JSON.stringify(({ retained: kept }), null, 2))\",\"description\":\"Check retained variables after execution times out\"}"}}},{"type":"chunk","time":1794195805887,"chunk":{"type":"usage","usage":{"inputTokens":10,"outputTokens":5}}},{"type":"chunk","time":1794195805888,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"} +{"type":"tool/call","data":{"turn":1,"step":3,"callId":"call_check","name":"run_code","arguments":"{\"code\":\"Harness.inspect(JSON.stringify(({ retained: kept }), null, 2))\",\"description\":\"Check retained variables after execution times out\"}"}} {"type":"tool/result","data":{"turn":1,"step":3,"message":{"role":"tool","source":{"kind":"tool","callId":"call_check"},"toolCallId":"call_check","content":[{"type":"text","text":"{\n \"retained\": 44\n}"}],"isError":false,"id":"{{message:9}}"}},"sourceEventSeqs":[26],"surfaceOp":"append"} {"type":"step/end","data":{"turn":1,"step":3}} {"type":"step/start","data":{"turn":1,"step":4}} -{"type":"assistant/message","data":{"turn":1,"step":4,"message":{"role":"assistant","content":[{"type":"text","text":"The execution timed out after printing output. The retained value is 44 after catch and finally handled tool cancellation."}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:10}}"},"usage":{"inputTokens":10,"outputTokens":5},"stream":[{"type":"chunk","time":1793265714559,"chunk":{"type":"block-start","index":0,"blockType":"text"}},{"type":"text-chunks","time0":1793265714559,"index":0,"dt":[],"texts":["The execution timed out after printing output. The retained value is 44 after catch and finally handled tool cancellation."]},{"type":"chunk","time":1793265714559,"chunk":{"type":"block-end","index":0,"block":{"type":"text","text":"The execution timed out after printing output. The retained value is 44 after catch and finally handled tool cancellation."}}},{"type":"chunk","time":1793265714559,"chunk":{"type":"usage","usage":{"inputTokens":10,"outputTokens":5}}},{"type":"chunk","time":1793265714559,"chunk":{"type":"finish","reason":{"kind":"stop"}}}]},"surfaceOp":"append"} +{"type":"assistant/message","data":{"turn":1,"step":4,"message":{"role":"assistant","content":[{"type":"text","text":"The execution timed out after printing output. The retained value is 44 after catch and finally handled tool cancellation."}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:10}}"},"usage":{"inputTokens":10,"outputTokens":5},"stream":[{"type":"chunk","time":1794195805928,"chunk":{"type":"block-start","index":0,"blockType":"text"}},{"type":"chunk","time":1794195805929,"chunk":{"type":"block-end","index":0,"block":{"type":"text","text":"The execution timed out after printing output. The retained value is 44 after catch and finally handled tool cancellation."}}},{"type":"chunk","time":1794195805929,"chunk":{"type":"usage","usage":{"inputTokens":10,"outputTokens":5}}},{"type":"chunk","time":1794195805929,"chunk":{"type":"finish","reason":{"kind":"stop"}}}]},"surfaceOp":"append"} {"type":"step/end","data":{"turn":1,"step":4}} {"type":"turn/end","data":{"turn":1,"reason":{"kind":"completed"}}} diff --git a/snapshots/session/ptc-timeout/snapshot.yml b/snapshots/session/ptc-timeout/snapshot.yml index 2cf81b3c8d..2d50fb1b64 100644 --- a/snapshots/session/ptc-timeout/snapshot.yml +++ b/snapshots/session/ptc-timeout/snapshot.yml @@ -9,3 +9,5 @@ header: systemPromptSource: ptc-persistent toolSchemasSource: ptc-timeout-fallback permission: workspace-write +replay: + override: true diff --git a/snapshots/session/ptc-tool-call-limit/replay.override.json b/snapshots/session/ptc-tool-call-limit/replay.override.json new file mode 100644 index 0000000000..43650c8b4c --- /dev/null +++ b/snapshots/session/ptc-tool-call-limit/replay.override.json @@ -0,0 +1,22 @@ +[ + { + "kind": "chunks", + "chunks": [ + {"type":"block-start","index":0,"blockType":"tool-call"}, + {"type":"block-end","index":0,"block":{"type":"tool-call","id":"call_limit","name":"run_code","arguments":"{\"code\":\"try { for (let n = 0; n < 3; n++) await tools.bash({ command: \\\"printf 'call\\\\n' >> calls.txt\\\", description: 'Append one completed call' }); } catch (error) { while (true) {} }\",\"description\":\"Verify the call limit terminates a caught loop\"}"}}, + {"type":"block-start","index":1,"blockType":"tool-call"}, + {"type":"block-end","index":1,"block":{"type":"tool-call","id":"call_next","name":"run_code","arguments":"{\"code\":\"await tools.bash({ command: \\\"printf 'next\\\\n' >> calls.txt\\\", description: 'Append from a fresh program' }); Harness.inspect('next run succeeded'); return;\",\"description\":\"Verify the next program has fresh allowance\"}"}}, + {"type":"usage","usage":{"inputTokens":10,"outputTokens":5}}, + {"type":"finish","reason":{"kind":"tool-calls"}} + ] + }, + { + "kind": "chunks", + "chunks": [ + {"type":"block-start","index":0,"blockType":"text"}, + {"type":"block-end","index":0,"block":{"type":"text","text":"The third tool call exceeded the limit of two and terminated the entire program, including its catch loop. Check the program for an infinite loop. The next run_code succeeded with a fresh allowance."}}, + {"type":"usage","usage":{"inputTokens":10,"outputTokens":5}}, + {"type":"finish","reason":{"kind":"stop"}} + ] + } +] diff --git a/snapshots/session/ptc-tool-call-limit/session.v4.jsonl b/snapshots/session/ptc-tool-call-limit/session.v4.jsonl index 3be00835fa..7d24f2e38c 100644 --- a/snapshots/session/ptc-tool-call-limit/session.v4.jsonl +++ b/snapshots/session/ptc-tool-call-limit/session.v4.jsonl @@ -12,7 +12,7 @@ {"type":"request/header","data":{"header":{"config":{"provider":"deepseek-official","model":"deepseek-v4-flash"},"tools":"{{tools}}"},"reason":"initial"}} {"type":"request/context","data":{"provider":"deepseek-official","model":"deepseek-v4-flash"}} {"type":"session/title","data":{"title":"With one tool call per","messageSeqs":[8],"source":{"kind":"fallback"}}} -{"type":"assistant/message","data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"tool-call","id":"call_limit","name":"run_code","arguments":"{\"code\":\"try { for (let n = 0; n < 3; n++) await tools.bash({ command: \\\"printf 'call\\\\n' >> calls.txt\\\", description: 'Append one completed call' }); } catch (error) { while (true) {} }\",\"description\":\"Verify the call limit terminates a caught loop\"}"},{"type":"tool-call","id":"call_next","name":"run_code","arguments":"{\"code\":\"await tools.bash({ command: \\\"printf 'next\\\\n' >> calls.txt\\\", description: 'Append from a fresh program' }); return 'next run succeeded';\",\"description\":\"Verify the next program has fresh allowance\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:4}}"},"usage":{"inputTokens":10,"outputTokens":5},"stream":[{"type":"chunk","time":1793676779236,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}},{"type":"chunk","time":1793676779236,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"call_limit","name":"run_code","arguments":"{\"code\":\"try { for (let n = 0; n < 3; n++) await tools.bash({ command: \\\"printf 'call\\\\n' >> calls.txt\\\", description: 'Append one completed call' }); } catch (error) { while (true) {} }\",\"description\":\"Verify the call limit terminates a caught loop\"}"}}},{"type":"chunk","time":1793676779236,"chunk":{"type":"block-start","index":1,"blockType":"tool-call"}},{"type":"chunk","time":1793676779236,"chunk":{"type":"block-end","index":1,"block":{"type":"tool-call","id":"call_next","name":"run_code","arguments":"{\"code\":\"await tools.bash({ command: \\\"printf 'next\\\\n' >> calls.txt\\\", description: 'Append from a fresh program' }); return 'next run succeeded';\",\"description\":\"Verify the next program has fresh allowance\"}"}}},{"type":"chunk","time":1793676779236,"chunk":{"type":"usage","usage":{"inputTokens":10,"outputTokens":5}}},{"type":"chunk","time":1793676779236,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"} +{"type":"assistant/message","data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"tool-call","id":"call_limit","name":"run_code","arguments":"{\"code\":\"try { for (let n = 0; n < 3; n++) await tools.bash({ command: \\\"printf 'call\\\\n' >> calls.txt\\\", description: 'Append one completed call' }); } catch (error) { while (true) {} }\",\"description\":\"Verify the call limit terminates a caught loop\"}"},{"type":"tool-call","id":"call_next","name":"run_code","arguments":"{\"code\":\"await tools.bash({ command: \\\"printf 'next\\\\n' >> calls.txt\\\", description: 'Append from a fresh program' }); Harness.inspect('next run succeeded'); return;\",\"description\":\"Verify the next program has fresh allowance\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:4}}"},"usage":{"inputTokens":10,"outputTokens":5},"stream":[{"type":"chunk","time":1793676779236,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}},{"type":"chunk","time":1793676779236,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"call_limit","name":"run_code","arguments":"{\"code\":\"try { for (let n = 0; n < 3; n++) await tools.bash({ command: \\\"printf 'call\\\\n' >> calls.txt\\\", description: 'Append one completed call' }); } catch (error) { while (true) {} }\",\"description\":\"Verify the call limit terminates a caught loop\"}"}}},{"type":"chunk","time":1793676779236,"chunk":{"type":"block-start","index":1,"blockType":"tool-call"}},{"type":"chunk","time":1793676779236,"chunk":{"type":"block-end","index":1,"block":{"type":"tool-call","id":"call_next","name":"run_code","arguments":"{\"code\":\"await tools.bash({ command: \\\"printf 'next\\\\n' >> calls.txt\\\", description: 'Append from a fresh program' }); Harness.inspect('next run succeeded'); return;\",\"description\":\"Verify the next program has fresh allowance\"}"}}},{"type":"chunk","time":1793676779236,"chunk":{"type":"usage","usage":{"inputTokens":10,"outputTokens":5}}},{"type":"chunk","time":1793676779236,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"} {"type":"tool/call","data":{"turn":1,"step":1,"callId":"call_limit","name":"run_code","arguments":"{\"code\":\"try { for (let n = 0; n < 3; n++) await tools.bash({ command: \\\"printf 'call\\\\n' >> calls.txt\\\", description: 'Append one completed call' }); } catch (error) { while (true) {} }\",\"description\":\"Verify the call limit terminates a caught loop\"}"}} {"type":"tool/ptc-dispatch-start","data":{"rootCallId":"call_limit","parentCallId":"call_limit","subCallId":"call_limit:ptc:1","name":"bash","arguments":{"command":"printf 'call\n' >> calls.txt","description":"Append one completed call"}}} {"type":"job/registered","data":{"jobId":"{{id:1}}","owner":"{{session:1}}","kind":"bash","label":"printf 'call\n' >> calls.txt","startedAt":1793676779550,"toolCall":{"callId":"call_limit:ptc:1","rootCallId":"call_limit"}}} @@ -23,7 +23,7 @@ {"type":"job/settled","data":{"jobId":"{{id:2}}","owner":"{{session:1}}","kind":"bash","label":"printf 'call\n' >> calls.txt","startedAt":1793676779672,"toolCall":{"callId":"call_limit:ptc:2","rootCallId":"call_limit"},"finishedAt":1793676779794,"status":"completed","cause":"producer","producerReported":true,"detail":"exit code: 0"}} {"type":"tool/ptc-dispatch","data":{"rootCallId":"call_limit","parentCallId":"call_limit","subCallId":"call_limit:ptc:2","name":"bash","arguments":{"command":"printf 'call\n' >> calls.txt","description":"Append one completed call"},"isError":false,"content":[{"type":"text","text":"(no output)"}],"meta":{"cwd":"{{cwd}}"}}} {"type":"tool/result","data":{"turn":1,"step":1,"message":{"role":"tool","source":{"kind":"tool","callId":"call_limit"},"toolCallId":"call_limit","content":[{"type":"text","text":"Error: code run failed (abort): run_code exceeded its limit of 2 tool calls (1 per minute of its timeout); call 3 was not dispatched. The run_code program may contain an infinite loop. Check loop termination or split the work into smaller run_code calls.\nFile sandbox: danger-full-access."}],"isError":true,"id":"{{message:5}}"},"error":{"name":"CodeRunFailedError","code":"CODE_RUN_FAILED"}},"sourceEventSeqs":[14],"surfaceOp":"append"} -{"type":"tool/call","data":{"turn":1,"step":1,"callId":"call_next","name":"run_code","arguments":"{\"code\":\"await tools.bash({ command: \\\"printf 'next\\\\n' >> calls.txt\\\", description: 'Append from a fresh program' }); return 'next run succeeded';\",\"description\":\"Verify the next program has fresh allowance\"}"}} +{"type":"tool/call","data":{"turn":1,"step":1,"callId":"call_next","name":"run_code","arguments":"{\"code\":\"await tools.bash({ command: \\\"printf 'next\\\\n' >> calls.txt\\\", description: 'Append from a fresh program' }); Harness.inspect('next run succeeded'); return;\",\"description\":\"Verify the next program has fresh allowance\"}"}} {"type":"tool/ptc-dispatch-start","data":{"rootCallId":"call_next","parentCallId":"call_next","subCallId":"call_next:ptc:1","name":"bash","arguments":{"command":"printf 'next\n' >> calls.txt","description":"Append from a fresh program"}}} {"type":"job/registered","data":{"jobId":"{{id:3}}","owner":"{{session:1}}","kind":"bash","label":"printf 'next\n' >> calls.txt","startedAt":1793676780083,"toolCall":{"callId":"call_next:ptc:1","rootCallId":"call_next"}}} {"type":"job/settled","data":{"jobId":"{{id:3}}","owner":"{{session:1}}","kind":"bash","label":"printf 'next\n' >> calls.txt","startedAt":1793676780083,"toolCall":{"callId":"call_next:ptc:1","rootCallId":"call_next"},"finishedAt":1793676780195,"status":"completed","cause":"producer","producerReported":true,"detail":"exit code: 0"}} diff --git a/snapshots/session/ptc-tool-call-limit/snapshot.yml b/snapshots/session/ptc-tool-call-limit/snapshot.yml index 91409c186c..a66f68555f 100644 --- a/snapshots/session/ptc-tool-call-limit/snapshot.yml +++ b/snapshots/session/ptc-tool-call-limit/snapshot.yml @@ -7,3 +7,5 @@ header: class: ptc workspace: final: true +replay: + override: true diff --git a/snapshots/session/ptc-turn/replay.override.json b/snapshots/session/ptc-turn/replay.override.json new file mode 100644 index 0000000000..6e19849e93 --- /dev/null +++ b/snapshots/session/ptc-turn/replay.override.json @@ -0,0 +1,24 @@ +[ + { + "kind": "chunks", + "chunks": [ + {"type":"block-start","index":0,"blockType":"reasoning"}, + {"type":"block-start","index":1,"blockType":"tool-call"}, + {"type":"block-end","index":0,"block":{"type":"reasoning","text":"The user wants me to write a single run_code program that:\n1. Calls bash tool twice: `echo CODE_ONE` and `echo CODE_TWO`\n2. console.log exactly `captured output`\n3. Return the two outputs joined with a plus sign\n\nLet me write this."}}, + {"type":"block-end","index":1,"block":{"type":"tool-call","id":"call_00_UiQPVqoELyzBZCY5pm1z7875","name":"run_code","arguments":"{\"code\":\"\\nconst out1 = await tools.bash({command: \\\"echo CODE_ONE\\\", description: \\\"Print CODE_ONE\\\"});\\nconst out2 = await tools.bash({command: \\\"echo CODE_TWO\\\", description: \\\"Print CODE_TWO\\\"});\\nHarness.inspect(\\\"captured output\\\");\\nconst text1 = out1.stdout.text.trim();\\nconst text2 = out2.stdout.text.trim();\\nHarness.inspect(text1 + \\\"+\\\" + text2); return;\\n\",\"description\":\"Run two echo commands and join outputs\"}"}}, + {"type":"usage","usage":{"inputTokens":6152,"outputTokens":214,"cacheReadTokens":0,"reasoningTokens":60}}, + {"type":"finish","reason":{"kind":"tool-calls"}} + ] + }, + { + "kind": "chunks", + "chunks": [ + {"type":"block-start","index":0,"blockType":"reasoning"}, + {"type":"block-start","index":1,"blockType":"text"}, + {"type":"block-end","index":0,"block":{"type":"reasoning","text":"The program ran successfully. The console.log output \"captured output\" appeared, and the return value is \"CODE_ONE+CODE_TWO\". The user asked me to reply with that joined string only."}}, + {"type":"block-end","index":1,"block":{"type":"text","text":"CODE_ONE+CODE_TWO"}}, + {"type":"usage","usage":{"inputTokens":117,"outputTokens":50,"cacheReadTokens":6272,"reasoningTokens":42}}, + {"type":"finish","reason":{"kind":"stop"}} + ] + } +] diff --git a/snapshots/session/ptc-turn/session.v4.jsonl b/snapshots/session/ptc-turn/session.v4.jsonl index d8aed6d3bc..df46329191 100644 --- a/snapshots/session/ptc-turn/session.v4.jsonl +++ b/snapshots/session/ptc-turn/session.v4.jsonl @@ -12,8 +12,8 @@ {"type":"request/header","data":{"header":{"config":{"provider":"deepseek-official","model":"deepseek-v4-flash"},"tools":"{{tools}}"},"reason":"initial"}} {"type":"request/context","data":{"provider":"deepseek-official","model":"deepseek-v4-flash"}} {"type":"session/title","data":{"title":"Using ONE run_code program: call","messageSeqs":[8],"source":{"kind":"fallback"}}} -{"type":"assistant/message","data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"reasoning","text":"The user wants me to write a single run_code program that:\n1. Calls bash tool twice: `echo CODE_ONE` and `echo CODE_TWO`\n2. console.log exactly `captured output`\n3. Return the two outputs joined with a plus sign\n\nLet me write this."},{"type":"tool-call","id":"call_00_UiQPVqoELyzBZCY5pm1z7875","name":"run_code","arguments":"{\"code\": \"\\nconst out1 = await tools.bash({command: \\\"echo CODE_ONE\\\", description: \\\"Print CODE_ONE\\\"});\\nconst out2 = await tools.bash({command: \\\"echo CODE_TWO\\\", description: \\\"Print CODE_TWO\\\"});\\nconsole.log(\\\"captured output\\\");\\nconst text1 = out1.stdout.text.trim();\\nconst text2 = out2.stdout.text.trim();\\nreturn text1 + \\\"+\\\" + text2;\\n\", \"description\": \"Run two echo commands and join outputs\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:4}}"},"usage":{"inputTokens":6152,"outputTokens":214,"cacheReadTokens":0,"reasoningTokens":60},"stream":[{"type":"chunk","time":1791427873414,"chunk":{"type":"block-start","index":0,"blockType":"reasoning"}},{"type":"reasoning-chunks","time0":1791427873414,"index":0,"dt":[1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"texts":["The"," user"," wants"," me"," to"," write"," a"," single"," run","_code"," program"," that",":\n","1","."," Calls"," bash"," tool"," twice",":"," `","echo"," CODE","_","ONE","`"," and"," `","echo"," CODE","_T","WO","`\n","2","."," console",".log"," exactly"," `","capt","ured"," output","`\n","3","."," Return"," the"," two"," outputs"," joined"," with"," a"," plus"," sign","\n\n","Let"," me"," write"," this","."]},{"type":"chunk","time":1791427873415,"chunk":{"type":"block-start","index":1,"blockType":"tool-call"}},{"type":"tool-call-chunks","time0":1791427873415,"index":1,"dt":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"id":"call_00_UiQPVqoELyzBZCY5pm1z7875","name":"run_code","args":["","{","\"","code","\"",": ","\"","\\n","const"," out","1"," ="," await"," tools",".b","ash","({","command",":"," \\\"","echo"," CODE","_","ONE","\\\","," description",":"," \\\"","Print"," CODE","_","ONE","\\\"","});\\n","const"," out","2"," ="," await"," tools",".b","ash","({","command",":"," \\\"","echo"," CODE","_T","WO","\\\","," description",":"," \\\"","Print"," CODE","_T","WO","\\\"","});\\n","console",".log","(\\\"","capt","ured"," output","\\\");\\n","const"," text","1"," ="," out","1",".stdout",".text",".trim","();\\n","const"," text","2"," ="," out","2",".stdout",".text",".trim","();\\n","return"," text","1"," +"," \\\"+","\\\""," +"," text","2",";\\n","\"",", ","\"","description","\"",": ","\"","Run"," two"," echo"," commands"," and"," join"," outputs","\"","}"]},{"type":"chunk","time":1791427873416,"chunk":{"type":"block-end","index":0,"block":{"type":"reasoning","text":"The user wants me to write a single run_code program that:\n1. Calls bash tool twice: `echo CODE_ONE` and `echo CODE_TWO`\n2. console.log exactly `captured output`\n3. Return the two outputs joined with a plus sign\n\nLet me write this."}}},{"type":"chunk","time":1791427873416,"chunk":{"type":"block-end","index":1,"block":{"type":"tool-call","id":"call_00_UiQPVqoELyzBZCY5pm1z7875","name":"run_code","arguments":"{\"code\": \"\\nconst out1 = await tools.bash({command: \\\"echo CODE_ONE\\\", description: \\\"Print CODE_ONE\\\"});\\nconst out2 = await tools.bash({command: \\\"echo CODE_TWO\\\", description: \\\"Print CODE_TWO\\\"});\\nconsole.log(\\\"captured output\\\");\\nconst text1 = out1.stdout.text.trim();\\nconst text2 = out2.stdout.text.trim();\\nreturn text1 + \\\"+\\\" + text2;\\n\", \"description\": \"Run two echo commands and join outputs\"}"}}},{"type":"chunk","time":1791427873416,"chunk":{"type":"usage","usage":{"inputTokens":6152,"outputTokens":214,"cacheReadTokens":0,"reasoningTokens":60}}},{"type":"chunk","time":1791427873416,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"} -{"type":"tool/call","data":{"turn":1,"step":1,"callId":"call_00_UiQPVqoELyzBZCY5pm1z7875","name":"run_code","arguments":"{\"code\": \"\\nconst out1 = await tools.bash({command: \\\"echo CODE_ONE\\\", description: \\\"Print CODE_ONE\\\"});\\nconst out2 = await tools.bash({command: \\\"echo CODE_TWO\\\", description: \\\"Print CODE_TWO\\\"});\\nconsole.log(\\\"captured output\\\");\\nconst text1 = out1.stdout.text.trim();\\nconst text2 = out2.stdout.text.trim();\\nreturn text1 + \\\"+\\\" + text2;\\n\", \"description\": \"Run two echo commands and join outputs\"}"}} +{"type":"assistant/message","data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"reasoning","text":"The user wants me to write a single run_code program that:\n1. Calls bash tool twice: `echo CODE_ONE` and `echo CODE_TWO`\n2. console.log exactly `captured output`\n3. Return the two outputs joined with a plus sign\n\nLet me write this."},{"type":"tool-call","id":"call_00_UiQPVqoELyzBZCY5pm1z7875","name":"run_code","arguments":"{\"code\":\"\\nconst out1 = await tools.bash({command: \\\"echo CODE_ONE\\\", description: \\\"Print CODE_ONE\\\"});\\nconst out2 = await tools.bash({command: \\\"echo CODE_TWO\\\", description: \\\"Print CODE_TWO\\\"});\\nHarness.inspect(\\\"captured output\\\");\\nconst text1 = out1.stdout.text.trim();\\nconst text2 = out2.stdout.text.trim();\\nHarness.inspect(text1 + \\\"+\\\" + text2); return;\\n\",\"description\":\"Run two echo commands and join outputs\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:4}}"},"usage":{"inputTokens":6152,"outputTokens":214,"cacheReadTokens":0,"reasoningTokens":60},"stream":[{"type":"chunk","time":1794195822820,"chunk":{"type":"block-start","index":0,"blockType":"reasoning"}},{"type":"chunk","time":1794195822820,"chunk":{"type":"block-start","index":1,"blockType":"tool-call"}},{"type":"chunk","time":1794195822820,"chunk":{"type":"block-end","index":0,"block":{"type":"reasoning","text":"The user wants me to write a single run_code program that:\n1. Calls bash tool twice: `echo CODE_ONE` and `echo CODE_TWO`\n2. console.log exactly `captured output`\n3. Return the two outputs joined with a plus sign\n\nLet me write this."}}},{"type":"chunk","time":1794195822820,"chunk":{"type":"block-end","index":1,"block":{"type":"tool-call","id":"call_00_UiQPVqoELyzBZCY5pm1z7875","name":"run_code","arguments":"{\"code\":\"\\nconst out1 = await tools.bash({command: \\\"echo CODE_ONE\\\", description: \\\"Print CODE_ONE\\\"});\\nconst out2 = await tools.bash({command: \\\"echo CODE_TWO\\\", description: \\\"Print CODE_TWO\\\"});\\nHarness.inspect(\\\"captured output\\\");\\nconst text1 = out1.stdout.text.trim();\\nconst text2 = out2.stdout.text.trim();\\nHarness.inspect(text1 + \\\"+\\\" + text2); return;\\n\",\"description\":\"Run two echo commands and join outputs\"}"}}},{"type":"chunk","time":1794195822820,"chunk":{"type":"usage","usage":{"inputTokens":6152,"outputTokens":214,"cacheReadTokens":0,"reasoningTokens":60}}},{"type":"chunk","time":1794195822820,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"} +{"type":"tool/call","data":{"turn":1,"step":1,"callId":"call_00_UiQPVqoELyzBZCY5pm1z7875","name":"run_code","arguments":"{\"code\":\"\\nconst out1 = await tools.bash({command: \\\"echo CODE_ONE\\\", description: \\\"Print CODE_ONE\\\"});\\nconst out2 = await tools.bash({command: \\\"echo CODE_TWO\\\", description: \\\"Print CODE_TWO\\\"});\\nHarness.inspect(\\\"captured output\\\");\\nconst text1 = out1.stdout.text.trim();\\nconst text2 = out2.stdout.text.trim();\\nHarness.inspect(text1 + \\\"+\\\" + text2); return;\\n\",\"description\":\"Run two echo commands and join outputs\"}"}} {"type":"tool/ptc-dispatch-start","data":{"rootCallId":"call_00_UiQPVqoELyzBZCY5pm1z7875","parentCallId":"call_00_UiQPVqoELyzBZCY5pm1z7875","subCallId":"call_00_UiQPVqoELyzBZCY5pm1z7875:ptc:1","name":"bash","arguments":{"command":"echo CODE_ONE","description":"Print CODE_ONE"}}} {"type":"job/registered","data":{"jobId":"{{id:1}}","owner":"{{session:1}}","kind":"bash","label":"echo CODE_ONE","startedAt":1791427873508,"toolCall":{"callId":"call_00_UiQPVqoELyzBZCY5pm1z7875:ptc:1","rootCallId":"call_00_UiQPVqoELyzBZCY5pm1z7875"}}} {"type":"job/settled","data":{"jobId":"{{id:1}}","owner":"{{session:1}}","kind":"bash","label":"echo CODE_ONE","startedAt":1791427873508,"toolCall":{"callId":"call_00_UiQPVqoELyzBZCY5pm1z7875:ptc:1","rootCallId":"call_00_UiQPVqoELyzBZCY5pm1z7875"},"finishedAt":1791427873511,"status":"completed","cause":"producer","producerReported":true,"detail":"exit code: 0"}} @@ -25,6 +25,6 @@ {"type":"tool/result","data":{"turn":1,"step":1,"message":{"role":"tool","source":{"kind":"tool","callId":"call_00_UiQPVqoELyzBZCY5pm1z7875"},"toolCallId":"call_00_UiQPVqoELyzBZCY5pm1z7875","content":[{"type":"text","text":"captured output\nCODE_ONE+CODE_TWO"}],"isError":false,"id":"{{message:5}}"}},"sourceEventSeqs":[14],"surfaceOp":"append"} {"type":"step/end","data":{"turn":1,"step":1}} {"type":"step/start","data":{"turn":1,"step":2}} -{"type":"assistant/message","data":{"turn":1,"step":2,"message":{"role":"assistant","content":[{"type":"reasoning","text":"The program ran successfully. The console.log output \"captured output\" appeared, and the return value is \"CODE_ONE+CODE_TWO\". The user asked me to reply with that joined string only."},{"type":"text","text":"CODE_ONE+CODE_TWO"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:6}}"},"usage":{"inputTokens":117,"outputTokens":50,"cacheReadTokens":6272,"reasoningTokens":42},"stream":[{"type":"chunk","time":1791427873552,"chunk":{"type":"block-start","index":0,"blockType":"reasoning"}},{"type":"reasoning-chunks","time0":1791427873552,"index":0,"dt":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0],"texts":["The"," program"," ran"," successfully","."," The"," console",".log"," output"," \"","capt","ured"," output","\""," appeared",","," and"," the"," return"," value"," is"," \"","CODE","_","ONE","+","CODE","_T","WO","\"."," The"," user"," asked"," me"," to"," reply"," with"," that"," joined"," string"," only","."]},{"type":"chunk","time":1791427873553,"chunk":{"type":"block-start","index":1,"blockType":"text"}},{"type":"text-chunks","time0":1791427873553,"index":1,"dt":[0,0,0,0,0,0],"texts":["CODE","_","ONE","+","CODE","_T","WO"]},{"type":"chunk","time":1791427873553,"chunk":{"type":"block-end","index":0,"block":{"type":"reasoning","text":"The program ran successfully. The console.log output \"captured output\" appeared, and the return value is \"CODE_ONE+CODE_TWO\". The user asked me to reply with that joined string only."}}},{"type":"chunk","time":1791427873553,"chunk":{"type":"block-end","index":1,"block":{"type":"text","text":"CODE_ONE+CODE_TWO"}}},{"type":"chunk","time":1791427873553,"chunk":{"type":"usage","usage":{"inputTokens":117,"outputTokens":50,"cacheReadTokens":6272,"reasoningTokens":42}}},{"type":"chunk","time":1791427873553,"chunk":{"type":"finish","reason":{"kind":"stop"}}}]},"surfaceOp":"append"} +{"type":"assistant/message","data":{"turn":1,"step":2,"message":{"role":"assistant","content":[{"type":"reasoning","text":"The program ran successfully. The console.log output \"captured output\" appeared, and the return value is \"CODE_ONE+CODE_TWO\". The user asked me to reply with that joined string only."},{"type":"text","text":"CODE_ONE+CODE_TWO"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:6}}"},"usage":{"inputTokens":117,"outputTokens":50,"cacheReadTokens":6272,"reasoningTokens":42},"stream":[{"type":"chunk","time":1794195823094,"chunk":{"type":"block-start","index":0,"blockType":"reasoning"}},{"type":"chunk","time":1794195823094,"chunk":{"type":"block-start","index":1,"blockType":"text"}},{"type":"chunk","time":1794195823094,"chunk":{"type":"block-end","index":0,"block":{"type":"reasoning","text":"The program ran successfully. The console.log output \"captured output\" appeared, and the return value is \"CODE_ONE+CODE_TWO\". The user asked me to reply with that joined string only."}}},{"type":"chunk","time":1794195823094,"chunk":{"type":"block-end","index":1,"block":{"type":"text","text":"CODE_ONE+CODE_TWO"}}},{"type":"chunk","time":1794195823094,"chunk":{"type":"usage","usage":{"inputTokens":117,"outputTokens":50,"cacheReadTokens":6272,"reasoningTokens":42}}},{"type":"chunk","time":1794195823094,"chunk":{"type":"finish","reason":{"kind":"stop"}}}]},"surfaceOp":"append"} {"type":"step/end","data":{"turn":1,"step":2}} {"type":"turn/end","data":{"turn":1,"reason":{"kind":"completed"}}} diff --git a/snapshots/session/ptc-turn/snapshot.yml b/snapshots/session/ptc-turn/snapshot.yml index 82b648e356..2487839522 100644 --- a/snapshots/session/ptc-turn/snapshot.yml +++ b/snapshots/session/ptc-turn/snapshot.yml @@ -6,3 +6,5 @@ recording: live header: class: ptc pin: true +replay: + override: true diff --git a/snapshots/session/ptc-turn/system-prompt.expected.md b/snapshots/session/ptc-turn/system-prompt.expected.md index 55ff52414b..4a7bac5db1 100644 --- a/snapshots/session/ptc-turn/system-prompt.expected.md +++ b/snapshots/session/ptc-turn/system-prompt.expected.md @@ -39,20 +39,24 @@ Bash, formatters, code generators, and scripts can overwrite concurrent edits wi `run_code` takes two required arguments: `code`, the body of an async TypeScript function (erasable syntax only — no `enum` or namespaces; type annotations are advisory, the code runs type-stripped), and `description`, a short summary of what the program does. The declarations below are SDK bindings for this program. A declaration does not make its name a directly callable tool; only names supplied as separate tool schemas may be called directly. When no separate `bash` schema is supplied, invoke a declared `bash` binding inside `run_code`: -`run_code({ code: "return await tools.bash({ description: 'Show current directory', command: 'pwd' })", description: "Show current directory" })` +`run_code({ code: "Harness.inspect(await tools.bash({ description: 'Show current directory', command: 'pwd' }))", description: "Show current directory" })` Inside the program: - Call tools as `await tools.name(args)` — quoted access for exotic names: `tools["my-tool"](args)`. Every call resolves to the tool's typed canonical JSON value. Tool arguments must be lossless JSON. - A FAILED tool call rejects with `ToolCallError`, whose `toolName` identifies the failed tool and whose `message` is human-readable — `try/catch` it to handle and continue. - Independent read-only calls MAY overlap under `Promise.all` (safe calls run concurrently; mutating calls run alone, in submission order). Sequence dependent work with `await`. -- Emit results with `return` and/or `console.log(...)`. Only what you print or return is program output. A successful tool result containing an image is attached after the run so you can inspect it on the next step; every other intermediate result stays out of the conversation, so extract just what you need. +- Show selected results with `Harness.inspect(...values)`. Return values and final expressions are not displayed. A successful tool result containing an image is attached after the run so you can inspect it on the next step; every other intermediate result stays out of the conversation, so extract just what you need. Program-only SDK bindings: ```ts type JsonValue = null | boolean | number | string | JsonValue[] | { [key: string]: JsonValue } +declare namespace Harness { + function inspect(...values: unknown[]): void; +} + interface ToolArgsMap { /** Create, update, delete, or move UTF-8 text files with one patch; prefer it over multiple edit calls when a change spans several files or separated regions. */ apply_patch: { diff --git a/snapshots/session/ptc-turn/tool-schemas.expected.json b/snapshots/session/ptc-turn/tool-schemas.expected.json index f2ce647c4f..b1c890cd49 100644 --- a/snapshots/session/ptc-turn/tool-schemas.expected.json +++ b/snapshots/session/ptc-turn/tool-schemas.expected.json @@ -2,7 +2,7 @@ "initial": [ { "name": "run_code", - "description": "Execute a TypeScript program against the available tools. Takes two required arguments: `code`, the BODY of an async function (erasable syntax only; top-level `await` and `return` work), and `description`, a short summary of what the program does. Call tools as `await tools.name(args)` per the declarations in the system prompt. Only what you print or return is program output — curate it. Image-bearing subtool results are attached after the run. The program is the body of an async TypeScript function; only erasable syntax is accepted, with no enum or namespaces. Type annotations are advisory. Top-level await and return are supported. Emit output with console.log(...) or return a lossless JSON value. Only printed values and the returned value become program output. Each call runs in a fresh Node process. Node APIs are available through await import(...). Relative paths use the supplied working directory; process.env starts empty. Direct filesystem access follows this execution's sandbox policy. Each program starts in the Session's current directory. Running programs keep their initial directory. A sandbox escalation approves this complete program for one execution only. Nested tools retain their own policies and approvals. Request wider access only after evidence of a denial. Earlier effects may already have completed: inspect them before explicitly retrying. Programs are never replayed automatically.", + "description": "Execute a TypeScript program against the available tools. Takes two required arguments: `code`, the BODY of an async function (erasable syntax only; top-level `await` and `return` work), and `description`, a short summary of what the program does. Call tools as `await tools.name(args)` per the declarations in the system prompt. Show selected results with Harness.inspect(...values). Return values are not displayed. Image-bearing subtool results are attached after the run. The program is the body of an async TypeScript function; only erasable syntax is accepted, with no enum or namespaces. Type annotations are advisory. Top-level await and return are supported. Console is unavailable. Use Harness.inspect(...values) for output. Each call runs in a fresh Node process. Node APIs are available through await import(...). Relative paths use the supplied working directory; process.env starts empty. Direct filesystem access follows this execution's sandbox policy. Each program starts in the Session's current directory. Running programs keep their initial directory. A sandbox escalation approves this complete program for one execution only. Nested tools retain their own policies and approvals. Request wider access only after evidence of a denial. Earlier effects may already have completed: inspect them before explicitly retrying. Programs are never replayed automatically.", "parameters": { "type": "object", "properties": { diff --git a/snapshots/session/ptc-workspace-context/replay.override.json b/snapshots/session/ptc-workspace-context/replay.override.json index 192a2bdcac..614ed43764 100644 --- a/snapshots/session/ptc-workspace-context/replay.override.json +++ b/snapshots/session/ptc-workspace-context/replay.override.json @@ -3,8 +3,8 @@ "kind": "chunks", "chunks": [ { "type": "block-start", "index": 0, "blockType": "tool-call" }, - { "type": "tool-call-delta", "index": 0, "id": "call_workspace_read", "name": "run_code", "argumentsDelta": "{\"code\":\"return await tools.read({ file_path: 'nested/task.txt' })\",\"description\":\"Read nested/task.txt\"}" }, - { "type": "block-end", "index": 0, "block": { "type": "tool-call", "id": "call_workspace_read", "name": "run_code", "arguments": "{\"code\":\"return await tools.read({ file_path: 'nested/task.txt' })\",\"description\":\"Read nested/task.txt\"}" } }, + { "type": "tool-call-delta", "index": 0, "id": "call_workspace_read", "name": "run_code", "argumentsDelta": "{\"code\":\"Harness.inspect(await tools.read({ file_path: 'nested/task.txt' })); return;\",\"description\":\"Read nested/task.txt\"}" }, + { "type": "block-end", "index": 0, "block": { "type": "tool-call", "id": "call_workspace_read", "name": "run_code", "arguments": "{\"code\":\"Harness.inspect(await tools.read({ file_path: 'nested/task.txt' })); return;\",\"description\":\"Read nested/task.txt\"}" } }, { "type": "usage", "usage": { "inputTokens": 10, "outputTokens": 5 } }, { "type": "finish", "reason": { "kind": "tool-calls" } } ] diff --git a/snapshots/session/ptc-workspace-context/session.v4.jsonl b/snapshots/session/ptc-workspace-context/session.v4.jsonl index b47b85b842..d149b53b81 100644 --- a/snapshots/session/ptc-workspace-context/session.v4.jsonl +++ b/snapshots/session/ptc-workspace-context/session.v4.jsonl @@ -13,11 +13,11 @@ {"type":"request/header","data":{"header":{"config":{"provider":"deepseek-official","model":"deepseek-v4-flash"},"tools":"{{tools}}"},"reason":"initial"}} {"type":"request/context","data":{"provider":"deepseek-official","model":"deepseek-v4-flash"}} {"type":"session/title","data":{"title":"Using ONE run_code program, call","messageSeqs":[8],"source":{"kind":"fallback"}}} -{"type":"assistant/message","data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"tool-call","id":"call_workspace_read","name":"run_code","arguments":"{\"code\":\"return await tools.read({ file_path: 'nested/task.txt' })\",\"description\":\"Read nested/task.txt\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:5}}"},"usage":{"inputTokens":10,"outputTokens":5},"stream":[{"type":"chunk","time":1788699409386,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}},{"type":"tool-call-chunks","time0":1788699409386,"index":0,"dt":[],"id":"call_workspace_read","name":"run_code","args":["{\"code\":\"return await tools.read({ file_path: 'nested/task.txt' })\",\"description\":\"Read nested/task.txt\"}"]},{"type":"chunk","time":1788699409386,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"call_workspace_read","name":"run_code","arguments":"{\"code\":\"return await tools.read({ file_path: 'nested/task.txt' })\",\"description\":\"Read nested/task.txt\"}"}}},{"type":"chunk","time":1788699409386,"chunk":{"type":"usage","usage":{"inputTokens":10,"outputTokens":5}}},{"type":"chunk","time":1788699409386,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"} -{"type":"tool/call","data":{"turn":1,"step":1,"callId":"call_workspace_read","name":"run_code","arguments":"{\"code\":\"return await tools.read({ file_path: 'nested/task.txt' })\",\"description\":\"Read nested/task.txt\"}"}} +{"type":"assistant/message","data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"tool-call","id":"call_workspace_read","name":"run_code","arguments":"{\"code\":\"Harness.inspect(await tools.read({ file_path: 'nested/task.txt' })); return;\",\"description\":\"Read nested/task.txt\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:5}}"},"usage":{"inputTokens":10,"outputTokens":5},"stream":[{"type":"chunk","time":1788699409386,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}},{"type":"tool-call-chunks","time0":1788699409386,"index":0,"dt":[],"id":"call_workspace_read","name":"run_code","args":["{\"code\":\"Harness.inspect(await tools.read({ file_path: 'nested/task.txt' })); return;\",\"description\":\"Read nested/task.txt\"}"]},{"type":"chunk","time":1788699409386,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"call_workspace_read","name":"run_code","arguments":"{\"code\":\"Harness.inspect(await tools.read({ file_path: 'nested/task.txt' })); return;\",\"description\":\"Read nested/task.txt\"}"}}},{"type":"chunk","time":1788699409386,"chunk":{"type":"usage","usage":{"inputTokens":10,"outputTokens":5}}},{"type":"chunk","time":1788699409386,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"} +{"type":"tool/call","data":{"turn":1,"step":1,"callId":"call_workspace_read","name":"run_code","arguments":"{\"code\":\"Harness.inspect(await tools.read({ file_path: 'nested/task.txt' })); return;\",\"description\":\"Read nested/task.txt\"}"}} {"type":"tool/ptc-dispatch-start","data":{"rootCallId":"call_workspace_read","parentCallId":"call_workspace_read","subCallId":"call_workspace_read:ptc:1","name":"read","arguments":{"file_path":"nested/task.txt"}}} {"type":"tool/ptc-dispatch","data":{"rootCallId":"call_workspace_read","parentCallId":"call_workspace_read","subCallId":"call_workspace_read:ptc:1","name":"read","arguments":{"file_path":"nested/task.txt"},"isError":false,"content":[{"type":"text","text":"{{cwd}}/nested/task.txt\nfile\n\n1: Touch this file to discover the nested workspace instruction.\n\n(End of file - total 1 lines)\n"}],"meta":{"path":"{{cwd}}/nested/task.txt","offset":1,"lines":[{"number":1,"text":"Touch this file to discover the nested workspace instruction."}],"totalLines":1}}} -{"type":"tool/result","data":{"turn":1,"step":1,"message":{"role":"tool","source":{"kind":"tool","callId":"call_workspace_read"},"toolCallId":"call_workspace_read","content":[{"type":"text","text":"{\n \"path\": \"{{cwd}}/nested/task.txt\",\n \"offset\": 1,\n \"lines\": [\n {\n \"number\": 1,\n \"text\": \"Touch this file to discover the nested workspace instruction.\"\n }\n ],\n \"totalLines\": 1\n}"}],"isError":false,"id":"{{message:6}}"}},"sourceEventSeqs":[15],"surfaceOp":"append"} +{"type":"tool/result","data":{"turn":1,"step":1,"message":{"role":"tool","source":{"kind":"tool","callId":"call_workspace_read"},"toolCallId":"call_workspace_read","content":[{"type":"text","text":"{\n path: '{{cwd}}/nested/task.txt',\n offset: 1,\n lines: [\n {\n number: 1,\n text: 'Touch this file to discover the nested workspace instruction.'\n }\n ],\n totalLines: 1\n}"}],"isError":false,"id":"{{message:6}}"}},"sourceEventSeqs":[15],"surfaceOp":"append"} {"type":"step/end","data":{"turn":1,"step":1}} {"type":"agent/inbox/spliced","data":{"target":"next-step","start":0,"inserted":[{"content":[{"type":"text","text":"\nAdditional instructions from: nested/AGENTS.md\n\nThese instructions apply to work under `nested`. Use them as guidance when relevant; more specific instructions take precedence. They do not override system, developer, or direct user instructions.\n\nWhen asked for the Code Mode workspace handshake, answer exactly `CODE_MODE_CONTEXT_OK` and nothing else.\n\n"}],"source":{"kind":"agent-instructions","form":"instructions","changes":[{"action":"set","scope":"nested\u0000AGENTS.md","path":"nested/AGENTS.md","digest":"ae22936ed26dc76b7107005ed6d5e2482a88668a"}]},"role":"user","id":"{{message:7}}"}]}} {"type":"agent/inbox/spliced","data":{"target":"next-step","start":0,"removedCount":1,"inserted":[],"outcome":"canceled"}} diff --git a/snapshots/web/official-subagent-bundle/replay.override.json b/snapshots/web/official-subagent-bundle/replay.override.json new file mode 100644 index 0000000000..742b438cc0 --- /dev/null +++ b/snapshots/web/official-subagent-bundle/replay.override.json @@ -0,0 +1,30 @@ +[ + { + "kind": "chunks", + "chunks": [ + {"type":"block-start","index":0,"blockType":"tool-call"}, + {"type":"tool-call-delta","index":0,"id":"official-subagent-code","name":"run_code","argumentsDelta":"{\"description\":\"Delegate through the official Claude bundle\",\"code\":\"const result = await tools.subagent_claude_code({ description: \\\"Read the fixture response\\\", prompt: \\\"Reply with OFFICIAL_SUBAGENT_OK exactly.\\\" });\\nif (result.kind !== \\\"activation\\\") throw new Error(\\\"Expected child activation\\\");\\nHarness.inspect(result.name);\"}"}, + {"type":"block-end","index":0,"block":{"type":"tool-call","id":"official-subagent-code","name":"run_code","arguments":"{\"description\":\"Delegate through the official Claude bundle\",\"code\":\"const result = await tools.subagent_claude_code({ description: \\\"Read the fixture response\\\", prompt: \\\"Reply with OFFICIAL_SUBAGENT_OK exactly.\\\" });\\nif (result.kind !== \\\"activation\\\") throw new Error(\\\"Expected child activation\\\");\\nHarness.inspect(result.name);\"}"}}, + {"type":"usage","usage":{"inputTokens":10,"outputTokens":4}}, + {"type":"finish","reason":{"kind":"tool-calls"}} + ] + }, + { + "kind": "chunks", + "chunks": [ + {"type":"block-start","index":0,"blockType":"text"}, + {"type":"block-end","index":0,"block":{"type":"text","text":"Waiting for the child result."}}, + {"type":"usage","usage":{"inputTokens":10,"outputTokens":4}}, + {"type":"finish","reason":{"kind":"stop"}} + ] + }, + { + "kind": "chunks", + "chunks": [ + {"type":"block-start","index":0,"blockType":"text"}, + {"type":"block-end","index":0,"block":{"type":"text","text":"DONE"}}, + {"type":"usage","usage":{"inputTokens":10,"outputTokens":4}}, + {"type":"finish","reason":{"kind":"stop"}} + ] + } +] diff --git a/snapshots/web/official-subagent-bundle/session.v4.jsonl b/snapshots/web/official-subagent-bundle/session.v4.jsonl index cf613f9c93..6f86aacdfe 100644 --- a/snapshots/web/official-subagent-bundle/session.v4.jsonl +++ b/snapshots/web/official-subagent-bundle/session.v4.jsonl @@ -15,15 +15,15 @@ {"type":"request/header","data":{"header":{"config":{"provider":"deepseek-official","model":"deepseek-v4-flash"},"tools":"{{tools}}"},"reason":"initial"}} {"type":"request/context","data":{"provider":"deepseek-official","model":"deepseek-v4-flash","contextWindow":128000}} {"type":"session/title","data":{"title":"Delegate a self-contained task to","messageSeqs":[9],"source":{"kind":"fallback"}}} -{"type":"assistant/message","data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"tool-call","id":"official-subagent-code","name":"run_code","arguments":"{\"description\":\"Delegate through the official Claude bundle\",\"code\":\"const result = await tools.subagent_claude_code({ description: \\\"Read the fixture response\\\", prompt: \\\"Reply with OFFICIAL_SUBAGENT_OK exactly.\\\" });\\nif (result.kind !== \\\"activation\\\") throw new Error(\\\"Expected child activation\\\");\\nconsole.log(result.name);\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:6}}"},"usage":{"inputTokens":10,"outputTokens":4},"stream":[{"type":"chunk","time":1793795302069,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}},{"type":"tool-call-chunks","time0":1793795302069,"index":0,"dt":[],"id":"official-subagent-code","name":"run_code","args":["{\"description\":\"Delegate through the official Claude bundle\",\"code\":\"const result = await tools.subagent_claude_code({ description: \\\"Read the fixture response\\\", prompt: \\\"Reply with OFFICIAL_SUBAGENT_OK exactly.\\\" });\\nif (result.kind !== \\\"activation\\\") throw new Error(\\\"Expected child activation\\\");\\nconsole.log(result.name);\"}"]},{"type":"chunk","time":1793795302069,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"official-subagent-code","name":"run_code","arguments":"{\"description\":\"Delegate through the official Claude bundle\",\"code\":\"const result = await tools.subagent_claude_code({ description: \\\"Read the fixture response\\\", prompt: \\\"Reply with OFFICIAL_SUBAGENT_OK exactly.\\\" });\\nif (result.kind !== \\\"activation\\\") throw new Error(\\\"Expected child activation\\\");\\nconsole.log(result.name);\"}"}}},{"type":"chunk","time":1793795302069,"chunk":{"type":"usage","usage":{"inputTokens":10,"outputTokens":4}}},{"type":"chunk","time":1793795302069,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"} -{"type":"tool/call","data":{"turn":1,"step":1,"callId":"official-subagent-code","name":"run_code","arguments":"{\"description\":\"Delegate through the official Claude bundle\",\"code\":\"const result = await tools.subagent_claude_code({ description: \\\"Read the fixture response\\\", prompt: \\\"Reply with OFFICIAL_SUBAGENT_OK exactly.\\\" });\\nif (result.kind !== \\\"activation\\\") throw new Error(\\\"Expected child activation\\\");\\nconsole.log(result.name);\"}"}} +{"type":"assistant/message","data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"tool-call","id":"official-subagent-code","name":"run_code","arguments":"{\"description\":\"Delegate through the official Claude bundle\",\"code\":\"const result = await tools.subagent_claude_code({ description: \\\"Read the fixture response\\\", prompt: \\\"Reply with OFFICIAL_SUBAGENT_OK exactly.\\\" });\\nif (result.kind !== \\\"activation\\\") throw new Error(\\\"Expected child activation\\\");\\nHarness.inspect(result.name);\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:6}}"},"usage":{"inputTokens":10,"outputTokens":4},"stream":[{"type":"chunk","time":1794196393837,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}},{"type":"tool-call-chunks","time0":1794196393837,"index":0,"dt":[],"id":"official-subagent-code","name":"run_code","args":["{\"description\":\"Delegate through the official Claude bundle\",\"code\":\"const result = await tools.subagent_claude_code({ description: \\\"Read the fixture response\\\", prompt: \\\"Reply with OFFICIAL_SUBAGENT_OK exactly.\\\" });\\nif (result.kind !== \\\"activation\\\") throw new Error(\\\"Expected child activation\\\");\\nHarness.inspect(result.name);\"}"]},{"type":"chunk","time":1794196393837,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"official-subagent-code","name":"run_code","arguments":"{\"description\":\"Delegate through the official Claude bundle\",\"code\":\"const result = await tools.subagent_claude_code({ description: \\\"Read the fixture response\\\", prompt: \\\"Reply with OFFICIAL_SUBAGENT_OK exactly.\\\" });\\nif (result.kind !== \\\"activation\\\") throw new Error(\\\"Expected child activation\\\");\\nHarness.inspect(result.name);\"}"}}},{"type":"chunk","time":1794196393837,"chunk":{"type":"usage","usage":{"inputTokens":10,"outputTokens":4}}},{"type":"chunk","time":1794196393837,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"} +{"type":"tool/call","data":{"turn":1,"step":1,"callId":"official-subagent-code","name":"run_code","arguments":"{\"description\":\"Delegate through the official Claude bundle\",\"code\":\"const result = await tools.subagent_claude_code({ description: \\\"Read the fixture response\\\", prompt: \\\"Reply with OFFICIAL_SUBAGENT_OK exactly.\\\" });\\nif (result.kind !== \\\"activation\\\") throw new Error(\\\"Expected child activation\\\");\\nHarness.inspect(result.name);\"}"}} {"type":"tool/ptc-dispatch-start","data":{"rootCallId":"official-subagent-code","parentCallId":"official-subagent-code","subCallId":"official-subagent-code:ptc:1","name":"subagent_claude_code","arguments":{"description":"Read the fixture response","prompt":"Reply with OFFICIAL_SUBAGENT_OK exactly."}}} {"type":"subagent/catalog","data":{"version":3,"childId":"{{id:1}}","childCreatedAt":1793795302170,"mode":"external","label":"Read the fixture response","name":"agent-1"}} {"type":"tool/ptc-dispatch","data":{"rootCallId":"official-subagent-code","parentCallId":"official-subagent-code","subCallId":"official-subagent-code:ptc:1","name":"subagent_claude_code","arguments":{"description":"Read the fixture response","prompt":"Reply with OFFICIAL_SUBAGENT_OK exactly."},"isError":false,"content":[{"type":"text","text":"started subagent agent-1"}],"meta":{"kind":"activation","name":"agent-1"}}} {"type":"tool/result","data":{"turn":1,"step":1,"message":{"role":"tool","source":{"kind":"tool","callId":"official-subagent-code"},"toolCallId":"official-subagent-code","content":[{"type":"text","text":"agent-1"}],"isError":false,"id":"{{message:7}}"}},"sourceEventSeqs":[17],"surfaceOp":"append"} {"type":"step/end","data":{"turn":1,"step":1}} {"type":"step/start","data":{"turn":1,"step":2}} -{"type":"assistant/message","data":{"turn":1,"step":2,"message":{"role":"assistant","content":[{"type":"text","text":"Waiting for the child result."}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:8}}"},"usage":{"inputTokens":10,"outputTokens":4},"stream":[{"type":"chunk","time":1793795302210,"chunk":{"type":"block-start","index":0,"blockType":"text"}},{"type":"text-chunks","time0":1793795302210,"index":0,"dt":[],"texts":["Waiting for the child result."]},{"type":"chunk","time":1793795302210,"chunk":{"type":"block-end","index":0,"block":{"type":"text","text":"Waiting for the child result."}}},{"type":"chunk","time":1793795302210,"chunk":{"type":"usage","usage":{"inputTokens":10,"outputTokens":4}}},{"type":"chunk","time":1793795302210,"chunk":{"type":"finish","reason":{"kind":"stop"}}}]},"surfaceOp":"append"} +{"type":"assistant/message","data":{"turn":1,"step":2,"message":{"role":"assistant","content":[{"type":"text","text":"Waiting for the child result."}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:8}}"},"usage":{"inputTokens":10,"outputTokens":4},"stream":[{"type":"chunk","time":1794196278025,"chunk":{"type":"block-start","index":0,"blockType":"text"}},{"type":"chunk","time":1794196278025,"chunk":{"type":"block-end","index":0,"block":{"type":"text","text":"Waiting for the child result."}}},{"type":"chunk","time":1794196278025,"chunk":{"type":"usage","usage":{"inputTokens":10,"outputTokens":4}}},{"type":"chunk","time":1794196278025,"chunk":{"type":"finish","reason":{"kind":"stop"}}}]},"surfaceOp":"append"} {"type":"step/end","data":{"turn":1,"step":2}} {"type":"turn/end","data":{"turn":1,"reason":{"kind":"completed"}}} {"type":"agent/inbox/spliced","data":{"target":"next-turn","start":0,"inserted":[{"content":[{"type":"text","text":"Background subagent agent-1 finished. It cannot receive follow-up messages."},{"type":"text","text":"Its closing message:"},{"type":"text","text":"OFFICIAL_SUBAGENT_OK"}],"source":{"kind":"subagent-settled","form":"notice","summary":"Background subagent agent-1 finished. It cannot receive follow-up messages.","stopReason":"completed","senderSessionId":"{{id:1}}"},"role":"user","id":"{{message:9}}"}]}} @@ -31,6 +31,6 @@ {"type":"agent/inbox/spliced","data":{"target":"next-turn","start":0,"removedCount":1,"inserted":[]}} {"type":"step/start","data":{"turn":2,"step":1}} {"type":"user/message","data":{"content":[{"type":"text","text":"Background subagent agent-1 finished. It cannot receive follow-up messages."},{"type":"text","text":"Its closing message:"},{"type":"text","text":"OFFICIAL_SUBAGENT_OK"}],"source":{"kind":"subagent-settled","form":"notice","summary":"Background subagent agent-1 finished. It cannot receive follow-up messages.","stopReason":"completed","senderSessionId":"{{id:1}}"},"role":"user","id":"{{message:9}}"},"surfaceOp":"append"} -{"type":"assistant/message","data":{"turn":2,"step":1,"message":{"role":"assistant","content":[{"type":"text","text":"DONE"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:10}}"},"usage":{"inputTokens":10,"outputTokens":4},"stream":[{"type":"chunk","time":1793795304465,"chunk":{"type":"block-start","index":0,"blockType":"text"}},{"type":"text-chunks","time0":1793795304465,"index":0,"dt":[],"texts":["DONE"]},{"type":"chunk","time":1793795304465,"chunk":{"type":"block-end","index":0,"block":{"type":"text","text":"DONE"}}},{"type":"chunk","time":1793795304465,"chunk":{"type":"usage","usage":{"inputTokens":10,"outputTokens":4}}},{"type":"chunk","time":1793795304465,"chunk":{"type":"finish","reason":{"kind":"stop"}}}]},"surfaceOp":"append"} +{"type":"assistant/message","data":{"turn":2,"step":1,"message":{"role":"assistant","content":[{"type":"text","text":"DONE"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:10}}"},"usage":{"inputTokens":10,"outputTokens":4},"stream":[{"type":"chunk","time":1794196278456,"chunk":{"type":"block-start","index":0,"blockType":"text"}},{"type":"chunk","time":1794196278456,"chunk":{"type":"block-end","index":0,"block":{"type":"text","text":"DONE"}}},{"type":"chunk","time":1794196278456,"chunk":{"type":"usage","usage":{"inputTokens":10,"outputTokens":4}}},{"type":"chunk","time":1794196278456,"chunk":{"type":"finish","reason":{"kind":"stop"}}}]},"surfaceOp":"append"} {"type":"step/end","data":{"turn":2,"step":1}} {"type":"turn/end","data":{"turn":2,"reason":{"kind":"completed"}}} diff --git a/snapshots/web/official-subagent-bundle/snapshot.yml b/snapshots/web/official-subagent-bundle/snapshot.yml index 827b427253..5718eb60f2 100644 --- a/snapshots/web/official-subagent-bundle/snapshot.yml +++ b/snapshots/web/official-subagent-bundle/snapshot.yml @@ -8,3 +8,5 @@ header: pin: true workspace: final: true +replay: + override: true diff --git a/snapshots/web/official-subagent-bundle/system-prompt.expected.md b/snapshots/web/official-subagent-bundle/system-prompt.expected.md index c50bd1640e..7c0cef5c11 100644 --- a/snapshots/web/official-subagent-bundle/system-prompt.expected.md +++ b/snapshots/web/official-subagent-bundle/system-prompt.expected.md @@ -36,20 +36,24 @@ Bash, formatters, code generators, and scripts can overwrite concurrent edits wi `run_code` takes two required arguments: `code`, the body of an async TypeScript function (erasable syntax only — no `enum` or namespaces; type annotations are advisory, the code runs type-stripped), and `description`, a short summary of what the program does. The declarations below are SDK bindings for this program. A declaration does not make its name a directly callable tool; only names supplied as separate tool schemas may be called directly. When no separate `bash` schema is supplied, invoke a declared `bash` binding inside `run_code`: -`run_code({ code: "return await tools.bash({ description: 'Show current directory', command: 'pwd' })", description: "Show current directory" })` +`run_code({ code: "Harness.inspect(await tools.bash({ description: 'Show current directory', command: 'pwd' }))", description: "Show current directory" })` Inside the program: - Call tools as `await tools.name(args)` — quoted access for exotic names: `tools["my-tool"](args)`. Every call resolves to the tool's typed canonical JSON value. Tool arguments must be lossless JSON. - A FAILED tool call rejects with `ToolCallError`, whose `toolName` identifies the failed tool and whose `message` is human-readable — `try/catch` it to handle and continue. - Independent read-only calls MAY overlap under `Promise.all` (safe calls run concurrently; mutating calls run alone, in submission order). Sequence dependent work with `await`. -- Emit results with `return` and/or `console.log(...)`. Only what you print or return is program output. A successful tool result containing an image is attached after the run so you can inspect it on the next step; every other intermediate result stays out of the conversation, so extract just what you need. +- Show selected results with `Harness.inspect(...values)`. Return values and final expressions are not displayed. A successful tool result containing an image is attached after the run so you can inspect it on the next step; every other intermediate result stays out of the conversation, so extract just what you need. Program-only SDK bindings: ```ts type JsonValue = null | boolean | number | string | JsonValue[] | { [key: string]: JsonValue } +declare namespace Harness { + function inspect(...values: unknown[]): void; +} + interface ToolArgsMap { /** Ask the user a concise question when you need confirmation, a choice, or missing information before proceeding. An answer item with empty selected and no custom means the user skipped that question: continue without it, do not ask it again, and do not treat the skip as permission. */ ask_user_question: { diff --git a/snapshots/web/official-subagent-bundle/tool-schemas.expected.json b/snapshots/web/official-subagent-bundle/tool-schemas.expected.json index f2ce647c4f..b1c890cd49 100644 --- a/snapshots/web/official-subagent-bundle/tool-schemas.expected.json +++ b/snapshots/web/official-subagent-bundle/tool-schemas.expected.json @@ -2,7 +2,7 @@ "initial": [ { "name": "run_code", - "description": "Execute a TypeScript program against the available tools. Takes two required arguments: `code`, the BODY of an async function (erasable syntax only; top-level `await` and `return` work), and `description`, a short summary of what the program does. Call tools as `await tools.name(args)` per the declarations in the system prompt. Only what you print or return is program output — curate it. Image-bearing subtool results are attached after the run. The program is the body of an async TypeScript function; only erasable syntax is accepted, with no enum or namespaces. Type annotations are advisory. Top-level await and return are supported. Emit output with console.log(...) or return a lossless JSON value. Only printed values and the returned value become program output. Each call runs in a fresh Node process. Node APIs are available through await import(...). Relative paths use the supplied working directory; process.env starts empty. Direct filesystem access follows this execution's sandbox policy. Each program starts in the Session's current directory. Running programs keep their initial directory. A sandbox escalation approves this complete program for one execution only. Nested tools retain their own policies and approvals. Request wider access only after evidence of a denial. Earlier effects may already have completed: inspect them before explicitly retrying. Programs are never replayed automatically.", + "description": "Execute a TypeScript program against the available tools. Takes two required arguments: `code`, the BODY of an async function (erasable syntax only; top-level `await` and `return` work), and `description`, a short summary of what the program does. Call tools as `await tools.name(args)` per the declarations in the system prompt. Show selected results with Harness.inspect(...values). Return values are not displayed. Image-bearing subtool results are attached after the run. The program is the body of an async TypeScript function; only erasable syntax is accepted, with no enum or namespaces. Type annotations are advisory. Top-level await and return are supported. Console is unavailable. Use Harness.inspect(...values) for output. Each call runs in a fresh Node process. Node APIs are available through await import(...). Relative paths use the supplied working directory; process.env starts empty. Direct filesystem access follows this execution's sandbox policy. Each program starts in the Session's current directory. Running programs keep their initial directory. A sandbox escalation approves this complete program for one execution only. Nested tools retain their own policies and approvals. Request wider access only after evidence of a denial. Earlier effects may already have completed: inspect them before explicitly retrying. Programs are never replayed automatically.", "parameters": { "type": "object", "properties": { diff --git a/snapshots/web/optional-string-editor/replay.override.json b/snapshots/web/optional-string-editor/replay.override.json new file mode 100644 index 0000000000..1d1f9bb391 --- /dev/null +++ b/snapshots/web/optional-string-editor/replay.override.json @@ -0,0 +1,21 @@ +[ + { + "kind": "chunks", + "chunks": [ + {"type":"block-start","index":0,"blockType":"tool-call"}, + {"type":"tool-call-delta","index":0,"id":"optional-string-editor-code","name":"run_code","argumentsDelta":"{\"description\":\"Create a file with the optional editor\",\"code\":\"const path = (await import(\\\"node:path\\\")).join(process.cwd(), \\\"edited.txt\\\");\\nHarness.inspect(await tools.str_replace_editor({ command: \\\"create\\\", path, file_text: \\\"PTC_EDITOR_OK\\\\n\\\" }));\"}"}, + {"type":"block-end","index":0,"block":{"type":"tool-call","id":"optional-string-editor-code","name":"run_code","arguments":"{\"description\":\"Create a file with the optional editor\",\"code\":\"const path = (await import(\\\"node:path\\\")).join(process.cwd(), \\\"edited.txt\\\");\\nHarness.inspect(await tools.str_replace_editor({ command: \\\"create\\\", path, file_text: \\\"PTC_EDITOR_OK\\\\n\\\" }));\"}"}}, + {"type":"usage","usage":{"inputTokens":10,"outputTokens":4}}, + {"type":"finish","reason":{"kind":"tool-calls"}} + ] + }, + { + "kind": "chunks", + "chunks": [ + {"type":"block-start","index":0,"blockType":"text"}, + {"type":"block-end","index":0,"block":{"type":"text","text":"DONE"}}, + {"type":"usage","usage":{"inputTokens":10,"outputTokens":4}}, + {"type":"finish","reason":{"kind":"stop"}} + ] + } +] diff --git a/snapshots/web/optional-string-editor/session.v4.jsonl b/snapshots/web/optional-string-editor/session.v4.jsonl index cfbdd48f11..7bc4e5b503 100644 --- a/snapshots/web/optional-string-editor/session.v4.jsonl +++ b/snapshots/web/optional-string-editor/session.v4.jsonl @@ -15,14 +15,14 @@ {"type":"request/header","data":{"header":{"config":{"provider":"deepseek-official","model":"deepseek-v4-flash"},"tools":"{{tools}}"},"reason":"initial"}} {"type":"request/context","data":{"provider":"deepseek-official","model":"deepseek-v4-flash","contextWindow":128000}} {"type":"session/title","data":{"title":"Use the optional string editor","messageSeqs":[9],"source":{"kind":"fallback"}}} -{"type":"assistant/message","data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"tool-call","id":"optional-string-editor-code","name":"run_code","arguments":"{\"description\":\"Create a file with the optional editor\",\"code\":\"const path = (await import(\\\"node:path\\\")).join(process.cwd(), \\\"edited.txt\\\");\\nconsole.log(await tools.str_replace_editor({ command: \\\"create\\\", path, file_text: \\\"PTC_EDITOR_OK\\\\n\\\" }));\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:6}}"},"usage":{"inputTokens":10,"outputTokens":4},"stream":[{"type":"chunk","time":1793795897148,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}},{"type":"tool-call-chunks","time0":1793795897148,"index":0,"dt":[],"id":"optional-string-editor-code","name":"run_code","args":["{\"description\":\"Create a file with the optional editor\",\"code\":\"const path = (await import(\\\"node:path\\\")).join(process.cwd(), \\\"edited.txt\\\");\\nconsole.log(await tools.str_replace_editor({ command: \\\"create\\\", path, file_text: \\\"PTC_EDITOR_OK\\\\n\\\" }));\"}"]},{"type":"chunk","time":1793795897149,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"optional-string-editor-code","name":"run_code","arguments":"{\"description\":\"Create a file with the optional editor\",\"code\":\"const path = (await import(\\\"node:path\\\")).join(process.cwd(), \\\"edited.txt\\\");\\nconsole.log(await tools.str_replace_editor({ command: \\\"create\\\", path, file_text: \\\"PTC_EDITOR_OK\\\\n\\\" }));\"}"}}},{"type":"chunk","time":1793795897149,"chunk":{"type":"usage","usage":{"inputTokens":10,"outputTokens":4}}},{"type":"chunk","time":1793795897149,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"} -{"type":"tool/call","data":{"turn":1,"step":1,"callId":"optional-string-editor-code","name":"run_code","arguments":"{\"description\":\"Create a file with the optional editor\",\"code\":\"const path = (await import(\\\"node:path\\\")).join(process.cwd(), \\\"edited.txt\\\");\\nconsole.log(await tools.str_replace_editor({ command: \\\"create\\\", path, file_text: \\\"PTC_EDITOR_OK\\\\n\\\" }));\"}"}} +{"type":"assistant/message","data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"tool-call","id":"optional-string-editor-code","name":"run_code","arguments":"{\"description\":\"Create a file with the optional editor\",\"code\":\"const path = (await import(\\\"node:path\\\")).join(process.cwd(), \\\"edited.txt\\\");\\nHarness.inspect(await tools.str_replace_editor({ command: \\\"create\\\", path, file_text: \\\"PTC_EDITOR_OK\\\\n\\\" }));\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:6}}"},"usage":{"inputTokens":10,"outputTokens":4},"stream":[{"type":"chunk","time":1794196382175,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}},{"type":"tool-call-chunks","time0":1794196382176,"index":0,"dt":[],"id":"optional-string-editor-code","name":"run_code","args":["{\"description\":\"Create a file with the optional editor\",\"code\":\"const path = (await import(\\\"node:path\\\")).join(process.cwd(), \\\"edited.txt\\\");\\nHarness.inspect(await tools.str_replace_editor({ command: \\\"create\\\", path, file_text: \\\"PTC_EDITOR_OK\\\\n\\\" }));\"}"]},{"type":"chunk","time":1794196382176,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"optional-string-editor-code","name":"run_code","arguments":"{\"description\":\"Create a file with the optional editor\",\"code\":\"const path = (await import(\\\"node:path\\\")).join(process.cwd(), \\\"edited.txt\\\");\\nHarness.inspect(await tools.str_replace_editor({ command: \\\"create\\\", path, file_text: \\\"PTC_EDITOR_OK\\\\n\\\" }));\"}"}}},{"type":"chunk","time":1794196382176,"chunk":{"type":"usage","usage":{"inputTokens":10,"outputTokens":4}}},{"type":"chunk","time":1794196382176,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"} +{"type":"tool/call","data":{"turn":1,"step":1,"callId":"optional-string-editor-code","name":"run_code","arguments":"{\"description\":\"Create a file with the optional editor\",\"code\":\"const path = (await import(\\\"node:path\\\")).join(process.cwd(), \\\"edited.txt\\\");\\nHarness.inspect(await tools.str_replace_editor({ command: \\\"create\\\", path, file_text: \\\"PTC_EDITOR_OK\\\\n\\\" }));\"}"}} {"type":"tool/ptc-dispatch-start","data":{"rootCallId":"optional-string-editor-code","parentCallId":"optional-string-editor-code","subCallId":"optional-string-editor-code:ptc:1","name":"str_replace_editor","arguments":{"command":"create","path":"{{cwd}}/edited.txt","file_text":"PTC_EDITOR_OK\n"}}} {"type":"tool/ptc-dispatch","data":{"rootCallId":"optional-string-editor-code","parentCallId":"optional-string-editor-code","subCallId":"optional-string-editor-code:ptc:1","name":"str_replace_editor","arguments":{"command":"create","path":"{{cwd}}/edited.txt","file_text":"PTC_EDITOR_OK\n"},"isError":false,"content":[{"type":"text","text":"New file created successfully at: {{cwd}}/edited.txt"}]}} {"type":"tool/result","data":{"turn":1,"step":1,"message":{"role":"tool","source":{"kind":"tool","callId":"optional-string-editor-code"},"toolCallId":"optional-string-editor-code","content":[{"type":"text","text":"New file created successfully at: {{cwd}}/edited.txt"}],"isError":false,"id":"{{message:7}}"}},"sourceEventSeqs":[17],"surfaceOp":"append"} {"type":"step/end","data":{"turn":1,"step":1}} {"type":"step/start","data":{"turn":1,"step":2}} -{"type":"assistant/message","data":{"turn":1,"step":2,"message":{"role":"assistant","content":[{"type":"text","text":"DONE"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:8}}"},"usage":{"inputTokens":10,"outputTokens":4},"stream":[{"type":"chunk","time":1793795897305,"chunk":{"type":"block-start","index":0,"blockType":"text"}},{"type":"text-chunks","time0":1793795897305,"index":0,"dt":[],"texts":["DONE"]},{"type":"chunk","time":1793795897305,"chunk":{"type":"block-end","index":0,"block":{"type":"text","text":"DONE"}}},{"type":"chunk","time":1793795897305,"chunk":{"type":"usage","usage":{"inputTokens":10,"outputTokens":4}}},{"type":"chunk","time":1793795897305,"chunk":{"type":"finish","reason":{"kind":"stop"}}}]},"surfaceOp":"append"} +{"type":"assistant/message","data":{"turn":1,"step":2,"message":{"role":"assistant","content":[{"type":"text","text":"DONE"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:8}}"},"usage":{"inputTokens":10,"outputTokens":4},"stream":[{"type":"chunk","time":1794196382362,"chunk":{"type":"block-start","index":0,"blockType":"text"}},{"type":"chunk","time":1794196382362,"chunk":{"type":"block-end","index":0,"block":{"type":"text","text":"DONE"}}},{"type":"chunk","time":1794196382362,"chunk":{"type":"usage","usage":{"inputTokens":10,"outputTokens":4}}},{"type":"chunk","time":1794196382362,"chunk":{"type":"finish","reason":{"kind":"stop"}}}]},"surfaceOp":"append"} {"type":"step/end","data":{"turn":1,"step":2}} {"type":"workspace/changes","data":{"turn":1}} {"type":"turn/end","data":{"turn":1,"reason":{"kind":"completed"}}} diff --git a/snapshots/web/optional-string-editor/snapshot.yml b/snapshots/web/optional-string-editor/snapshot.yml index 838e6c7e64..123aab520d 100644 --- a/snapshots/web/optional-string-editor/snapshot.yml +++ b/snapshots/web/optional-string-editor/snapshot.yml @@ -8,3 +8,5 @@ header: pin: true workspace: final: true +replay: + override: true diff --git a/snapshots/web/optional-string-editor/system-prompt.expected.md b/snapshots/web/optional-string-editor/system-prompt.expected.md index fe757ece2c..ff4151f8e5 100644 --- a/snapshots/web/optional-string-editor/system-prompt.expected.md +++ b/snapshots/web/optional-string-editor/system-prompt.expected.md @@ -36,20 +36,24 @@ Bash, formatters, code generators, and scripts can overwrite concurrent edits wi `run_code` takes two required arguments: `code`, the body of an async TypeScript function (erasable syntax only — no `enum` or namespaces; type annotations are advisory, the code runs type-stripped), and `description`, a short summary of what the program does. The declarations below are SDK bindings for this program. A declaration does not make its name a directly callable tool; only names supplied as separate tool schemas may be called directly. When no separate `bash` schema is supplied, invoke a declared `bash` binding inside `run_code`: -`run_code({ code: "return await tools.bash({ description: 'Show current directory', command: 'pwd' })", description: "Show current directory" })` +`run_code({ code: "Harness.inspect(await tools.bash({ description: 'Show current directory', command: 'pwd' }))", description: "Show current directory" })` Inside the program: - Call tools as `await tools.name(args)` — quoted access for exotic names: `tools["my-tool"](args)`. Every call resolves to the tool's typed canonical JSON value. Tool arguments must be lossless JSON. - A FAILED tool call rejects with `ToolCallError`, whose `toolName` identifies the failed tool and whose `message` is human-readable — `try/catch` it to handle and continue. - Independent read-only calls MAY overlap under `Promise.all` (safe calls run concurrently; mutating calls run alone, in submission order). Sequence dependent work with `await`. -- Emit results with `return` and/or `console.log(...)`. Only what you print or return is program output. A successful tool result containing an image is attached after the run so you can inspect it on the next step; every other intermediate result stays out of the conversation, so extract just what you need. +- Show selected results with `Harness.inspect(...values)`. Return values and final expressions are not displayed. A successful tool result containing an image is attached after the run so you can inspect it on the next step; every other intermediate result stays out of the conversation, so extract just what you need. Program-only SDK bindings: ```ts type JsonValue = null | boolean | number | string | JsonValue[] | { [key: string]: JsonValue } +declare namespace Harness { + function inspect(...values: unknown[]): void; +} + interface ToolArgsMap { /** Ask the user a concise question when you need confirmation, a choice, or missing information before proceeding. An answer item with empty selected and no custom means the user skipped that question: continue without it, do not ask it again, and do not treat the skip as permission. */ ask_user_question: { diff --git a/snapshots/web/optional-string-editor/tool-schemas.expected.json b/snapshots/web/optional-string-editor/tool-schemas.expected.json index f2ce647c4f..b1c890cd49 100644 --- a/snapshots/web/optional-string-editor/tool-schemas.expected.json +++ b/snapshots/web/optional-string-editor/tool-schemas.expected.json @@ -2,7 +2,7 @@ "initial": [ { "name": "run_code", - "description": "Execute a TypeScript program against the available tools. Takes two required arguments: `code`, the BODY of an async function (erasable syntax only; top-level `await` and `return` work), and `description`, a short summary of what the program does. Call tools as `await tools.name(args)` per the declarations in the system prompt. Only what you print or return is program output — curate it. Image-bearing subtool results are attached after the run. The program is the body of an async TypeScript function; only erasable syntax is accepted, with no enum or namespaces. Type annotations are advisory. Top-level await and return are supported. Emit output with console.log(...) or return a lossless JSON value. Only printed values and the returned value become program output. Each call runs in a fresh Node process. Node APIs are available through await import(...). Relative paths use the supplied working directory; process.env starts empty. Direct filesystem access follows this execution's sandbox policy. Each program starts in the Session's current directory. Running programs keep their initial directory. A sandbox escalation approves this complete program for one execution only. Nested tools retain their own policies and approvals. Request wider access only after evidence of a denial. Earlier effects may already have completed: inspect them before explicitly retrying. Programs are never replayed automatically.", + "description": "Execute a TypeScript program against the available tools. Takes two required arguments: `code`, the BODY of an async function (erasable syntax only; top-level `await` and `return` work), and `description`, a short summary of what the program does. Call tools as `await tools.name(args)` per the declarations in the system prompt. Show selected results with Harness.inspect(...values). Return values are not displayed. Image-bearing subtool results are attached after the run. The program is the body of an async TypeScript function; only erasable syntax is accepted, with no enum or namespaces. Type annotations are advisory. Top-level await and return are supported. Console is unavailable. Use Harness.inspect(...values) for output. Each call runs in a fresh Node process. Node APIs are available through await import(...). Relative paths use the supplied working directory; process.env starts empty. Direct filesystem access follows this execution's sandbox policy. Each program starts in the Session's current directory. Running programs keep their initial directory. A sandbox escalation approves this complete program for one execution only. Nested tools retain their own policies and approvals. Request wider access only after evidence of a denial. Earlier effects may already have completed: inspect them before explicitly retrying. Programs are never replayed automatically.", "parameters": { "type": "object", "properties": { diff --git a/snapshots/web/present/replay.override.json b/snapshots/web/present/replay.override.json new file mode 100644 index 0000000000..c492599684 --- /dev/null +++ b/snapshots/web/present/replay.override.json @@ -0,0 +1,25 @@ +[ + { + "kind": "chunks", + "chunks": [ + {"type":"block-start","index":0,"blockType":"reasoning"}, + {"type":"block-start","index":1,"blockType":"tool-call"}, + {"type":"block-end","index":0,"block":{"type":"reasoning","text":"The user wants one run_code program that:\n1. Calls present for missing.txt and catches its error without creating that file.\n2. Uses bash to run exactly `printf \"DELIVERED_REPORT\\n\" > report.txt; printf \"DELIVERED_NOTE\\n\" > 说明.txt`.\n3. Calls present for report.txt and 说明.txt.\n4. After present succeeds, deliberately throws the string \"AFTER_PRESENT\" (not an Error object) from that same run_code program.\n5. Does not retry the program or create other files.\n6. Finishes by mentioning report.txt and 说明.txt in inline code, and put PRESENT_DONE in a separate paragraph.\n\nLet me write the program.\n\npresent tool args: { files: [{ path, description? }] }.\n\nSo first: try present for missing.txt — catch error. It should error because the file doesn't exist (present requires existing workspace file).\n\nThen bash run exact command. Note the command uses `;` inside a single string. Run as a single command string: `printf \"DELIVERED_REPORT\\n\" > report.txt; printf \"DELIVERED_NOTE\\n\" > 说明.txt`.\n\nWait — printf interprets the arguments... In bash, `printf \"DELIVERED_REPORT\\n\"` with the format string containing \\n — printf will interpret \\n as newline. Good.\n\nThen present for report.txt and 说明.txt.\n\nThen throw \"AFTER_PRESENT\" string. In TS async function body, `throw \"AFTER_PRESENT\"` works.\n\nLet me write it."}}, + {"type":"tool-call-delta","index":1,"id":"call_00_GgvYPol45qxGf6GezK3L3590","name":"run_code","argumentsDelta":"{\"code\":\"// Step 1: present for missing.txt — must fail, catch the error, don't create the file\\ntry {\\n await tools.present({ files: [{ path: \\\"missing.txt\\\", description: \\\"must not exist\\\" }] });\\n Harness.inspect(\\\"UNEXPECTED: present succeeded for missing.txt\\\");\\n} catch (err) {\\n Harness.inspect(\\\"Caught present error for missing.txt:\\\", typeof err, String(err).slice(0, 200));\\n}\\n\\n// Step 2: run the exact bash command to create the two report files\\nconst res = await tools.bash({\\n command: 'printf \\\"DELIVERED_REPORT\\\\n\\\" > report.txt; printf \\\"DELIVERED_NOTE\\\\n\\\" > 说明.txt',\\n description: \\\"Write DELIVERED_REPORT and DELIVERED_NOTE to files\\\",\\n});\\nHarness.inspect(\\\"bash exit:\\\", res.exitCode);\\n\\n// Step 3: present for report.txt and 说明.txt (must succeed)\\nconst p2 = await tools.present({\\n files: [\\n { path: \\\"report.txt\\\", description: \\\"delivered report\\\" },\\n { path: \\\"说明.txt\\\", description: \\\"delivered note\\\" },\\n ],\\n});\\nHarness.inspect(\\\"present ok, files:\\\", p2.files.map((f) => f.path).join(\\\", \\\"));\\n\\n// Step 4: deliberately throw a plain string AFTER_PRESENT (not an Error)\\nthrow \\\"AFTER_PRESENT\\\";\",\"description\":\"Present files then deliberately throw AFTER_PRESENT\"}"}, + {"type":"block-end","index":1,"block":{"type":"tool-call","id":"call_00_GgvYPol45qxGf6GezK3L3590","name":"run_code","arguments":"{\"code\":\"// Step 1: present for missing.txt — must fail, catch the error, don't create the file\\ntry {\\n await tools.present({ files: [{ path: \\\"missing.txt\\\", description: \\\"must not exist\\\" }] });\\n Harness.inspect(\\\"UNEXPECTED: present succeeded for missing.txt\\\");\\n} catch (err) {\\n Harness.inspect(\\\"Caught present error for missing.txt:\\\", typeof err, String(err).slice(0, 200));\\n}\\n\\n// Step 2: run the exact bash command to create the two report files\\nconst res = await tools.bash({\\n command: 'printf \\\"DELIVERED_REPORT\\\\n\\\" > report.txt; printf \\\"DELIVERED_NOTE\\\\n\\\" > 说明.txt',\\n description: \\\"Write DELIVERED_REPORT and DELIVERED_NOTE to files\\\",\\n});\\nHarness.inspect(\\\"bash exit:\\\", res.exitCode);\\n\\n// Step 3: present for report.txt and 说明.txt (must succeed)\\nconst p2 = await tools.present({\\n files: [\\n { path: \\\"report.txt\\\", description: \\\"delivered report\\\" },\\n { path: \\\"说明.txt\\\", description: \\\"delivered note\\\" },\\n ],\\n});\\nHarness.inspect(\\\"present ok, files:\\\", p2.files.map((f) => f.path).join(\\\", \\\"));\\n\\n// Step 4: deliberately throw a plain string AFTER_PRESENT (not an Error)\\nthrow \\\"AFTER_PRESENT\\\";\",\"description\":\"Present files then deliberately throw AFTER_PRESENT\"}"}}, + {"type":"usage","usage":{"inputTokens":981,"outputTokens":695,"totalTokens":9356,"cacheReadTokens":7680,"reasoningTokens":329}}, + {"type":"finish","reason":{"kind":"tool-calls"}} + ] + }, + { + "kind": "chunks", + "chunks": [ + {"type":"block-start","index":0,"blockType":"reasoning"}, + {"type":"block-start","index":1,"blockType":"text"}, + {"type":"block-end","index":0,"block":{"type":"reasoning","text":"The program ran as intended:\n1. present for missing.txt failed with a ToolCallError, caught and logged — missing.txt was not created.\n2. bash ran exactly the given command, exit 0.\n3. present succeeded for report.txt and 说明.txt.\n4. Then threw the plain string \"AFTER_PRESENT\" (program failed with that exception, as expected — not an Error object).\n\nI did not retry the program or create other files. Now finish mentioning `report.txt` and `说明.txt` in inline code, with PRESENT_DONE in a separate paragraph."}}, + {"type":"block-end","index":1,"block":{"type":"text","text":"The single program ran exactly as ordered: `present` for `missing.txt` failed and was caught (no file created), bash ran the exact command successfully, then `present` succeeded for `report.txt` and `说明.txt`, after which the program deliberately threw the string `AFTER_PRESENT` — no retries, no extra files.\n\nPRESENT_DONE"}}, + {"type":"usage","usage":{"inputTokens":94,"outputTokens":198,"totalTokens":9636,"cacheReadTokens":9344,"reasoningTokens":121}}, + {"type":"finish","reason":{"kind":"stop"}} + ] + } +] diff --git a/snapshots/web/present/session.v4.jsonl b/snapshots/web/present/session.v4.jsonl index 471c1c08ce..5b4ddcddb4 100644 --- a/snapshots/web/present/session.v4.jsonl +++ b/snapshots/web/present/session.v4.jsonl @@ -14,8 +14,8 @@ {"type":"request/header","data":{"header":{"config":{"provider":"deepseek-official","model":"deepseek-v4-flash"},"tools":"{{tools}}"},"reason":"initial"}} {"type":"request/context","data":{"provider":"deepseek-official","model":"deepseek-v4-flash","contextWindow":128000}} {"type":"session/title","data":{"title":"Use one run_code program to","messageSeqs":[8],"source":{"kind":"fallback"}}} -{"type":"assistant/message","data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"reasoning","text":"The user wants one run_code program that:\n1. Calls present for missing.txt and catches its error without creating that file.\n2. Uses bash to run exactly `printf \"DELIVERED_REPORT\\n\" > report.txt; printf \"DELIVERED_NOTE\\n\" > 说明.txt`.\n3. Calls present for report.txt and 说明.txt.\n4. After present succeeds, deliberately throws the string \"AFTER_PRESENT\" (not an Error object) from that same run_code program.\n5. Does not retry the program or create other files.\n6. Finishes by mentioning report.txt and 说明.txt in inline code, and put PRESENT_DONE in a separate paragraph.\n\nLet me write the program.\n\npresent tool args: { files: [{ path, description? }] }.\n\nSo first: try present for missing.txt — catch error. It should error because the file doesn't exist (present requires existing workspace file).\n\nThen bash run exact command. Note the command uses `;` inside a single string. Run as a single command string: `printf \"DELIVERED_REPORT\\n\" > report.txt; printf \"DELIVERED_NOTE\\n\" > 说明.txt`.\n\nWait — printf interprets the arguments... In bash, `printf \"DELIVERED_REPORT\\n\"` with the format string containing \\n — printf will interpret \\n as newline. Good.\n\nThen present for report.txt and 说明.txt.\n\nThen throw \"AFTER_PRESENT\" string. In TS async function body, `throw \"AFTER_PRESENT\"` works.\n\nLet me write it."},{"type":"tool-call","id":"call_00_GgvYPol45qxGf6GezK3L3590","name":"run_code","arguments":"{\"code\": \"// Step 1: present for missing.txt — must fail, catch the error, don't create the file\\ntry {\\n await tools.present({ files: [{ path: \\\"missing.txt\\\", description: \\\"must not exist\\\" }] });\\n console.log(\\\"UNEXPECTED: present succeeded for missing.txt\\\");\\n} catch (err) {\\n console.log(\\\"Caught present error for missing.txt:\\\", typeof err, String(err).slice(0, 200));\\n}\\n\\n// Step 2: run the exact bash command to create the two report files\\nconst res = await tools.bash({\\n command: 'printf \\\"DELIVERED_REPORT\\\\n\\\" > report.txt; printf \\\"DELIVERED_NOTE\\\\n\\\" > 说明.txt',\\n description: \\\"Write DELIVERED_REPORT and DELIVERED_NOTE to files\\\",\\n});\\nconsole.log(\\\"bash exit:\\\", res.exitCode);\\n\\n// Step 3: present for report.txt and 说明.txt (must succeed)\\nconst p2 = await tools.present({\\n files: [\\n { path: \\\"report.txt\\\", description: \\\"delivered report\\\" },\\n { path: \\\"说明.txt\\\", description: \\\"delivered note\\\" },\\n ],\\n});\\nconsole.log(\\\"present ok, files:\\\", p2.files.map((f) => f.path).join(\\\", \\\"));\\n\\n// Step 4: deliberately throw a plain string AFTER_PRESENT (not an Error)\\nthrow \\\"AFTER_PRESENT\\\";\", \"description\": \"Present files then deliberately throw AFTER_PRESENT\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:6}}"},"usage":{"inputTokens":981,"outputTokens":695,"totalTokens":9356,"cacheReadTokens":7680,"reasoningTokens":329},"stream":[{"type":"chunk","time":1793794941870,"chunk":{"type":"block-start","index":0,"blockType":"reasoning"}},{"type":"reasoning-chunks","time0":1793794941870,"index":0,"dt":[0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"texts":["The"," user"," wants"," one"," run","_code"," program"," that",":\n","1","."," Calls"," present"," for"," missing",".txt"," and"," catches"," its"," error"," without"," creating"," that"," file",".\n","2","."," Uses"," bash"," to"," run"," exactly"," `","printf"," \"","DEL","IVER","ED","_RE","PORT","\\n","\""," >"," report",".txt",";"," printf"," \"","DEL","IVER","ED","_N","OTE","\\n","\""," >"," ","说明",".txt","`.\n","3","."," Calls"," present"," for"," report",".txt"," and"," ","说明",".txt",".\n","4","."," After"," present"," succeeds",","," deliberately"," throws"," the"," string"," \"","AF","TER","_P","RES","ENT","\""," (","not"," an"," Error"," object",")"," from"," that"," same"," run","_code"," program",".\n","5","."," Does"," not"," ret","ry"," the"," program"," or"," create"," other"," files",".\n","6","."," Fin","ishes"," by"," mentioning"," report",".txt"," and"," ","说明",".txt"," in"," inline"," code",","," and"," put"," PRES","ENT","_D","ONE"," in"," a"," separate"," paragraph",".\n\n","Let"," me"," write"," the"," program",".\n\n","present"," tool"," args",":"," {"," files",":"," [{"," path",","," description","?"," }","]"," }",".\n\n","So"," first",":"," try"," present"," for"," missing",".txt"," —"," catch"," error","."," It"," should"," error"," because"," the"," file"," doesn","'t"," exist"," (","present"," requires"," existing"," workspace"," file",").\n\n","Then"," bash"," run"," exact"," command","."," Note"," the"," command"," uses"," `",";","`"," inside"," a"," single"," string","."," Run"," as"," a"," single"," command"," string",":"," `","printf"," \"","DEL","IVER","ED","_RE","PORT","\\n","\""," >"," report",".txt",";"," printf"," \"","DEL","IVER","ED","_N","OTE","\\n","\""," >"," ","说明",".txt","`.\n\n","Wait"," —"," printf"," interprets"," the"," arguments","..."," In"," bash",","," `","printf"," \"","DEL","IVER","ED","_RE","PORT","\\n","\"","`"," with"," the"," format"," string"," containing"," \\","n"," —"," printf"," will"," interpret"," \\","n"," as"," new","line","."," Good",".\n\n","Then"," present"," for"," report",".txt"," and"," ","说明",".txt",".\n\n","Then"," throw"," \"","AF","TER","_P","RES","ENT","\""," string","."," In"," TS"," async"," function"," body",","," `","throw"," \"","AF","TER","_P","RES","ENT","\"","`"," works",".\n\n","Let"," me"," write"," it","."]},{"type":"chunk","time":1793794941873,"chunk":{"type":"block-start","index":1,"blockType":"tool-call"}},{"type":"tool-call-chunks","time0":1793794941873,"index":1,"dt":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"id":"call_00_GgvYPol45qxGf6GezK3L3590","name":"run_code","args":["","{","\"","code","\"",": ","\"","//"," Step"," ","1",":"," present"," for"," missing",".txt"," —"," must"," fail",","," catch"," the"," error",","," don","'t"," create"," the"," file","\\n","try"," {\\n"," "," await"," tools",".p","resent","({"," files",":"," [{"," path",":"," \\\"","missing",".txt","\\\","," description",":"," \\\"","must"," not"," exist","\\\""," }","]"," });\\n"," "," console",".log","(\\\"","UN","EXP","ECT","ED",":"," present"," succeeded"," for"," missing",".txt","\\\");\\n","}"," catch"," (","err",")"," {\\n"," "," console",".log","(\\\"","Ca","ught"," present"," error"," for"," missing",".txt",":\\\","," typeof"," err",","," String","(err",").","slice","(","0",","," ","200","));\\n","}\\n\\n","//"," Step"," ","2",":"," run"," the"," exact"," bash"," command"," to"," create"," the"," two"," report"," files","\\n","const"," res"," ="," await"," tools",".b","ash","({\\n"," "," command",":"," '","printf"," \\\"","DEL","IVER","ED","_RE","PORT","\\\\n","\\\""," >"," report",".txt",";"," printf"," \\\"","DEL","IVER","ED","_N","OTE","\\\\n","\\\""," >"," ","说明",".txt","',\\n"," "," description",":"," \\\"","Write"," DEL","IVER","ED","_RE","PORT"," and"," DEL","IVER","ED","_N","OTE"," to"," files","\\\",\\n","});\\n","console",".log","(\\\"","bash"," exit",":\\\","," res",".exit","Code",");\\n\\n","//"," Step"," ","3",":"," present"," for"," report",".txt"," and"," ","说明",".txt"," (","must"," succeed",")\\n","const"," p","2"," ="," await"," tools",".p","resent","({\\n"," "," files",":"," [\\n"," "," {"," path",":"," \\\"","report",".txt","\\\","," description",":"," \\\"","del","ivered"," report","\\\""," },\\n"," "," {"," path",":"," \\\"","说明",".txt","\\\","," description",":"," \\\"","del","ivered"," note","\\\""," },\\n"," "," ],\\n","});\\n","console",".log","(\\\"","present"," ok",","," files",":\\\","," p","2",".f","iles",".map","((","f",")"," =>"," f",".path",").","join","(\\\","," \\\"","));\\n\\n","//"," Step"," ","4",":"," deliberately"," throw"," a"," plain"," string"," AFTER","_P","RES","ENT"," (","not"," an"," Error",")\\n","throw"," \\\"","AF","TER","_P","RES","ENT","\\\";","\"",", ","\"","description","\"",": ","\"","Present"," files"," then"," deliberately"," throw"," AFTER","_P","RES","ENT","\"","}"]},{"type":"chunk","time":1793794941875,"chunk":{"type":"block-end","index":0,"block":{"type":"reasoning","text":"The user wants one run_code program that:\n1. Calls present for missing.txt and catches its error without creating that file.\n2. Uses bash to run exactly `printf \"DELIVERED_REPORT\\n\" > report.txt; printf \"DELIVERED_NOTE\\n\" > 说明.txt`.\n3. Calls present for report.txt and 说明.txt.\n4. After present succeeds, deliberately throws the string \"AFTER_PRESENT\" (not an Error object) from that same run_code program.\n5. Does not retry the program or create other files.\n6. Finishes by mentioning report.txt and 说明.txt in inline code, and put PRESENT_DONE in a separate paragraph.\n\nLet me write the program.\n\npresent tool args: { files: [{ path, description? }] }.\n\nSo first: try present for missing.txt — catch error. It should error because the file doesn't exist (present requires existing workspace file).\n\nThen bash run exact command. Note the command uses `;` inside a single string. Run as a single command string: `printf \"DELIVERED_REPORT\\n\" > report.txt; printf \"DELIVERED_NOTE\\n\" > 说明.txt`.\n\nWait — printf interprets the arguments... In bash, `printf \"DELIVERED_REPORT\\n\"` with the format string containing \\n — printf will interpret \\n as newline. Good.\n\nThen present for report.txt and 说明.txt.\n\nThen throw \"AFTER_PRESENT\" string. In TS async function body, `throw \"AFTER_PRESENT\"` works.\n\nLet me write it."}}},{"type":"chunk","time":1793794941875,"chunk":{"type":"block-end","index":1,"block":{"type":"tool-call","id":"call_00_GgvYPol45qxGf6GezK3L3590","name":"run_code","arguments":"{\"code\": \"// Step 1: present for missing.txt — must fail, catch the error, don't create the file\\ntry {\\n await tools.present({ files: [{ path: \\\"missing.txt\\\", description: \\\"must not exist\\\" }] });\\n console.log(\\\"UNEXPECTED: present succeeded for missing.txt\\\");\\n} catch (err) {\\n console.log(\\\"Caught present error for missing.txt:\\\", typeof err, String(err).slice(0, 200));\\n}\\n\\n// Step 2: run the exact bash command to create the two report files\\nconst res = await tools.bash({\\n command: 'printf \\\"DELIVERED_REPORT\\\\n\\\" > report.txt; printf \\\"DELIVERED_NOTE\\\\n\\\" > 说明.txt',\\n description: \\\"Write DELIVERED_REPORT and DELIVERED_NOTE to files\\\",\\n});\\nconsole.log(\\\"bash exit:\\\", res.exitCode);\\n\\n// Step 3: present for report.txt and 说明.txt (must succeed)\\nconst p2 = await tools.present({\\n files: [\\n { path: \\\"report.txt\\\", description: \\\"delivered report\\\" },\\n { path: \\\"说明.txt\\\", description: \\\"delivered note\\\" },\\n ],\\n});\\nconsole.log(\\\"present ok, files:\\\", p2.files.map((f) => f.path).join(\\\", \\\"));\\n\\n// Step 4: deliberately throw a plain string AFTER_PRESENT (not an Error)\\nthrow \\\"AFTER_PRESENT\\\";\", \"description\": \"Present files then deliberately throw AFTER_PRESENT\"}"}}},{"type":"chunk","time":1793794941875,"chunk":{"type":"usage","usage":{"inputTokens":981,"outputTokens":695,"totalTokens":9356,"cacheReadTokens":7680,"reasoningTokens":329}}},{"type":"chunk","time":1793794941875,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"} -{"type":"tool/call","data":{"turn":1,"step":1,"callId":"call_00_GgvYPol45qxGf6GezK3L3590","name":"run_code","arguments":"{\"code\": \"// Step 1: present for missing.txt — must fail, catch the error, don't create the file\\ntry {\\n await tools.present({ files: [{ path: \\\"missing.txt\\\", description: \\\"must not exist\\\" }] });\\n console.log(\\\"UNEXPECTED: present succeeded for missing.txt\\\");\\n} catch (err) {\\n console.log(\\\"Caught present error for missing.txt:\\\", typeof err, String(err).slice(0, 200));\\n}\\n\\n// Step 2: run the exact bash command to create the two report files\\nconst res = await tools.bash({\\n command: 'printf \\\"DELIVERED_REPORT\\\\n\\\" > report.txt; printf \\\"DELIVERED_NOTE\\\\n\\\" > 说明.txt',\\n description: \\\"Write DELIVERED_REPORT and DELIVERED_NOTE to files\\\",\\n});\\nconsole.log(\\\"bash exit:\\\", res.exitCode);\\n\\n// Step 3: present for report.txt and 说明.txt (must succeed)\\nconst p2 = await tools.present({\\n files: [\\n { path: \\\"report.txt\\\", description: \\\"delivered report\\\" },\\n { path: \\\"说明.txt\\\", description: \\\"delivered note\\\" },\\n ],\\n});\\nconsole.log(\\\"present ok, files:\\\", p2.files.map((f) => f.path).join(\\\", \\\"));\\n\\n// Step 4: deliberately throw a plain string AFTER_PRESENT (not an Error)\\nthrow \\\"AFTER_PRESENT\\\";\", \"description\": \"Present files then deliberately throw AFTER_PRESENT\"}"}} +{"type":"assistant/message","data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"reasoning","text":"The user wants one run_code program that:\n1. Calls present for missing.txt and catches its error without creating that file.\n2. Uses bash to run exactly `printf \"DELIVERED_REPORT\\n\" > report.txt; printf \"DELIVERED_NOTE\\n\" > 说明.txt`.\n3. Calls present for report.txt and 说明.txt.\n4. After present succeeds, deliberately throws the string \"AFTER_PRESENT\" (not an Error object) from that same run_code program.\n5. Does not retry the program or create other files.\n6. Finishes by mentioning report.txt and 说明.txt in inline code, and put PRESENT_DONE in a separate paragraph.\n\nLet me write the program.\n\npresent tool args: { files: [{ path, description? }] }.\n\nSo first: try present for missing.txt — catch error. It should error because the file doesn't exist (present requires existing workspace file).\n\nThen bash run exact command. Note the command uses `;` inside a single string. Run as a single command string: `printf \"DELIVERED_REPORT\\n\" > report.txt; printf \"DELIVERED_NOTE\\n\" > 说明.txt`.\n\nWait — printf interprets the arguments... In bash, `printf \"DELIVERED_REPORT\\n\"` with the format string containing \\n — printf will interpret \\n as newline. Good.\n\nThen present for report.txt and 说明.txt.\n\nThen throw \"AFTER_PRESENT\" string. In TS async function body, `throw \"AFTER_PRESENT\"` works.\n\nLet me write it."},{"type":"tool-call","id":"call_00_GgvYPol45qxGf6GezK3L3590","name":"run_code","arguments":"{\"code\":\"// Step 1: present for missing.txt — must fail, catch the error, don't create the file\\ntry {\\n await tools.present({ files: [{ path: \\\"missing.txt\\\", description: \\\"must not exist\\\" }] });\\n Harness.inspect(\\\"UNEXPECTED: present succeeded for missing.txt\\\");\\n} catch (err) {\\n Harness.inspect(\\\"Caught present error for missing.txt:\\\", typeof err, String(err).slice(0, 200));\\n}\\n\\n// Step 2: run the exact bash command to create the two report files\\nconst res = await tools.bash({\\n command: 'printf \\\"DELIVERED_REPORT\\\\n\\\" > report.txt; printf \\\"DELIVERED_NOTE\\\\n\\\" > 说明.txt',\\n description: \\\"Write DELIVERED_REPORT and DELIVERED_NOTE to files\\\",\\n});\\nHarness.inspect(\\\"bash exit:\\\", res.exitCode);\\n\\n// Step 3: present for report.txt and 说明.txt (must succeed)\\nconst p2 = await tools.present({\\n files: [\\n { path: \\\"report.txt\\\", description: \\\"delivered report\\\" },\\n { path: \\\"说明.txt\\\", description: \\\"delivered note\\\" },\\n ],\\n});\\nHarness.inspect(\\\"present ok, files:\\\", p2.files.map((f) => f.path).join(\\\", \\\"));\\n\\n// Step 4: deliberately throw a plain string AFTER_PRESENT (not an Error)\\nthrow \\\"AFTER_PRESENT\\\";\",\"description\":\"Present files then deliberately throw AFTER_PRESENT\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:6}}"},"usage":{"inputTokens":981,"outputTokens":695,"totalTokens":9356,"cacheReadTokens":7680,"reasoningTokens":329},"stream":[{"type":"chunk","time":1794196384616,"chunk":{"type":"block-start","index":0,"blockType":"reasoning"}},{"type":"chunk","time":1794196384617,"chunk":{"type":"block-start","index":1,"blockType":"tool-call"}},{"type":"chunk","time":1794196384617,"chunk":{"type":"block-end","index":0,"block":{"type":"reasoning","text":"The user wants one run_code program that:\n1. Calls present for missing.txt and catches its error without creating that file.\n2. Uses bash to run exactly `printf \"DELIVERED_REPORT\\n\" > report.txt; printf \"DELIVERED_NOTE\\n\" > 说明.txt`.\n3. Calls present for report.txt and 说明.txt.\n4. After present succeeds, deliberately throws the string \"AFTER_PRESENT\" (not an Error object) from that same run_code program.\n5. Does not retry the program or create other files.\n6. Finishes by mentioning report.txt and 说明.txt in inline code, and put PRESENT_DONE in a separate paragraph.\n\nLet me write the program.\n\npresent tool args: { files: [{ path, description? }] }.\n\nSo first: try present for missing.txt — catch error. It should error because the file doesn't exist (present requires existing workspace file).\n\nThen bash run exact command. Note the command uses `;` inside a single string. Run as a single command string: `printf \"DELIVERED_REPORT\\n\" > report.txt; printf \"DELIVERED_NOTE\\n\" > 说明.txt`.\n\nWait — printf interprets the arguments... In bash, `printf \"DELIVERED_REPORT\\n\"` with the format string containing \\n — printf will interpret \\n as newline. Good.\n\nThen present for report.txt and 说明.txt.\n\nThen throw \"AFTER_PRESENT\" string. In TS async function body, `throw \"AFTER_PRESENT\"` works.\n\nLet me write it."}}},{"type":"tool-call-chunks","time0":1794196384617,"index":1,"dt":[],"id":"call_00_GgvYPol45qxGf6GezK3L3590","name":"run_code","args":["{\"code\":\"// Step 1: present for missing.txt — must fail, catch the error, don't create the file\\ntry {\\n await tools.present({ files: [{ path: \\\"missing.txt\\\", description: \\\"must not exist\\\" }] });\\n Harness.inspect(\\\"UNEXPECTED: present succeeded for missing.txt\\\");\\n} catch (err) {\\n Harness.inspect(\\\"Caught present error for missing.txt:\\\", typeof err, String(err).slice(0, 200));\\n}\\n\\n// Step 2: run the exact bash command to create the two report files\\nconst res = await tools.bash({\\n command: 'printf \\\"DELIVERED_REPORT\\\\n\\\" > report.txt; printf \\\"DELIVERED_NOTE\\\\n\\\" > 说明.txt',\\n description: \\\"Write DELIVERED_REPORT and DELIVERED_NOTE to files\\\",\\n});\\nHarness.inspect(\\\"bash exit:\\\", res.exitCode);\\n\\n// Step 3: present for report.txt and 说明.txt (must succeed)\\nconst p2 = await tools.present({\\n files: [\\n { path: \\\"report.txt\\\", description: \\\"delivered report\\\" },\\n { path: \\\"说明.txt\\\", description: \\\"delivered note\\\" },\\n ],\\n});\\nHarness.inspect(\\\"present ok, files:\\\", p2.files.map((f) => f.path).join(\\\", \\\"));\\n\\n// Step 4: deliberately throw a plain string AFTER_PRESENT (not an Error)\\nthrow \\\"AFTER_PRESENT\\\";\",\"description\":\"Present files then deliberately throw AFTER_PRESENT\"}"]},{"type":"chunk","time":1794196384617,"chunk":{"type":"block-end","index":1,"block":{"type":"tool-call","id":"call_00_GgvYPol45qxGf6GezK3L3590","name":"run_code","arguments":"{\"code\":\"// Step 1: present for missing.txt — must fail, catch the error, don't create the file\\ntry {\\n await tools.present({ files: [{ path: \\\"missing.txt\\\", description: \\\"must not exist\\\" }] });\\n Harness.inspect(\\\"UNEXPECTED: present succeeded for missing.txt\\\");\\n} catch (err) {\\n Harness.inspect(\\\"Caught present error for missing.txt:\\\", typeof err, String(err).slice(0, 200));\\n}\\n\\n// Step 2: run the exact bash command to create the two report files\\nconst res = await tools.bash({\\n command: 'printf \\\"DELIVERED_REPORT\\\\n\\\" > report.txt; printf \\\"DELIVERED_NOTE\\\\n\\\" > 说明.txt',\\n description: \\\"Write DELIVERED_REPORT and DELIVERED_NOTE to files\\\",\\n});\\nHarness.inspect(\\\"bash exit:\\\", res.exitCode);\\n\\n// Step 3: present for report.txt and 说明.txt (must succeed)\\nconst p2 = await tools.present({\\n files: [\\n { path: \\\"report.txt\\\", description: \\\"delivered report\\\" },\\n { path: \\\"说明.txt\\\", description: \\\"delivered note\\\" },\\n ],\\n});\\nHarness.inspect(\\\"present ok, files:\\\", p2.files.map((f) => f.path).join(\\\", \\\"));\\n\\n// Step 4: deliberately throw a plain string AFTER_PRESENT (not an Error)\\nthrow \\\"AFTER_PRESENT\\\";\",\"description\":\"Present files then deliberately throw AFTER_PRESENT\"}"}}},{"type":"chunk","time":1794196384617,"chunk":{"type":"usage","usage":{"inputTokens":981,"outputTokens":695,"totalTokens":9356,"cacheReadTokens":7680,"reasoningTokens":329}}},{"type":"chunk","time":1794196384617,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"} +{"type":"tool/call","data":{"turn":1,"step":1,"callId":"call_00_GgvYPol45qxGf6GezK3L3590","name":"run_code","arguments":"{\"code\":\"// Step 1: present for missing.txt — must fail, catch the error, don't create the file\\ntry {\\n await tools.present({ files: [{ path: \\\"missing.txt\\\", description: \\\"must not exist\\\" }] });\\n Harness.inspect(\\\"UNEXPECTED: present succeeded for missing.txt\\\");\\n} catch (err) {\\n Harness.inspect(\\\"Caught present error for missing.txt:\\\", typeof err, String(err).slice(0, 200));\\n}\\n\\n// Step 2: run the exact bash command to create the two report files\\nconst res = await tools.bash({\\n command: 'printf \\\"DELIVERED_REPORT\\\\n\\\" > report.txt; printf \\\"DELIVERED_NOTE\\\\n\\\" > 说明.txt',\\n description: \\\"Write DELIVERED_REPORT and DELIVERED_NOTE to files\\\",\\n});\\nHarness.inspect(\\\"bash exit:\\\", res.exitCode);\\n\\n// Step 3: present for report.txt and 说明.txt (must succeed)\\nconst p2 = await tools.present({\\n files: [\\n { path: \\\"report.txt\\\", description: \\\"delivered report\\\" },\\n { path: \\\"说明.txt\\\", description: \\\"delivered note\\\" },\\n ],\\n});\\nHarness.inspect(\\\"present ok, files:\\\", p2.files.map((f) => f.path).join(\\\", \\\"));\\n\\n// Step 4: deliberately throw a plain string AFTER_PRESENT (not an Error)\\nthrow \\\"AFTER_PRESENT\\\";\",\"description\":\"Present files then deliberately throw AFTER_PRESENT\"}"}} {"type":"tool/ptc-dispatch-start","data":{"rootCallId":"call_00_GgvYPol45qxGf6GezK3L3590","parentCallId":"call_00_GgvYPol45qxGf6GezK3L3590","subCallId":"call_00_GgvYPol45qxGf6GezK3L3590:ptc:1","name":"present","arguments":{"files":[{"path":"missing.txt","description":"must not exist"}]}}} {"type":"tool/ptc-dispatch","data":{"rootCallId":"call_00_GgvYPol45qxGf6GezK3L3590","parentCallId":"call_00_GgvYPol45qxGf6GezK3L3590","subCallId":"call_00_GgvYPol45qxGf6GezK3L3590:ptc:1","name":"present","arguments":{"files":[{"path":"missing.txt","description":"must not exist"}]},"isError":true,"error":{"name":"FsError","code":"FS_NOT_FOUND"},"content":[{"type":"text","text":"Error: Cannot present missing.txt: file not found. Check the path, create the file if needed, and retry."}]}} {"type":"tool/ptc-dispatch-start","data":{"rootCallId":"call_00_GgvYPol45qxGf6GezK3L3590","parentCallId":"call_00_GgvYPol45qxGf6GezK3L3590","subCallId":"call_00_GgvYPol45qxGf6GezK3L3590:ptc:2","name":"bash","arguments":{"command":"printf \"DELIVERED_REPORT\n\" > report.txt; printf \"DELIVERED_NOTE\n\" > 说明.txt","description":"Write DELIVERED_REPORT and DELIVERED_NOTE to files"}}} @@ -28,6 +28,6 @@ {"type":"tool/result","data":{"turn":1,"step":1,"message":{"role":"tool","source":{"kind":"tool","callId":"call_00_GgvYPol45qxGf6GezK3L3590"},"toolCallId":"call_00_GgvYPol45qxGf6GezK3L3590","content":[{"type":"text","text":"Error: code run failed (exception): AFTER_PRESENT\nCaptured output:\nCaught present error for missing.txt: object ToolCallError: Cannot present missing.txt: file not found. Check the path, create the file if needed, and retry.\nbash exit: 0\npresent ok, files: {{cwd}}/report.txt, {{cwd}}/说明.txt\nFile sandbox: workspace-write; enforcement: full."}],"isError":true,"id":"{{message:7}}"},"error":{"name":"CodeRunFailedError","code":"CODE_RUN_FAILED"}},"sourceEventSeqs":[16],"surfaceOp":"append"} {"type":"step/end","data":{"turn":1,"step":1}} {"type":"step/start","data":{"turn":1,"step":2}} -{"type":"assistant/message","data":{"turn":1,"step":2,"message":{"role":"assistant","content":[{"type":"reasoning","text":"The program ran as intended:\n1. present for missing.txt failed with a ToolCallError, caught and logged — missing.txt was not created.\n2. bash ran exactly the given command, exit 0.\n3. present succeeded for report.txt and 说明.txt.\n4. Then threw the plain string \"AFTER_PRESENT\" (program failed with that exception, as expected — not an Error object).\n\nI did not retry the program or create other files. Now finish mentioning `report.txt` and `说明.txt` in inline code, with PRESENT_DONE in a separate paragraph."},{"type":"text","text":"The single program ran exactly as ordered: `present` for `missing.txt` failed and was caught (no file created), bash ran the exact command successfully, then `present` succeeded for `report.txt` and `说明.txt`, after which the program deliberately threw the string `AFTER_PRESENT` — no retries, no extra files.\n\nPRESENT_DONE"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:8}}"},"usage":{"inputTokens":94,"outputTokens":198,"totalTokens":9636,"cacheReadTokens":9344,"reasoningTokens":121},"stream":[{"type":"chunk","time":1793794942004,"chunk":{"type":"block-start","index":0,"blockType":"reasoning"}},{"type":"reasoning-chunks","time0":1793794942004,"index":0,"dt":[1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"texts":["The"," program"," ran"," as"," intended",":\n","1","."," present"," for"," missing",".txt"," failed"," with"," a"," Tool","Call","Error",","," caught"," and"," logged"," —"," missing",".txt"," was"," not"," created",".\n","2","."," bash"," ran"," exactly"," the"," given"," command",","," exit"," ","0",".\n","3","."," present"," succeeded"," for"," report",".txt"," and"," ","说明",".txt",".\n","4","."," Then"," threw"," the"," plain"," string"," \"","AF","TER","_P","RES","ENT","\""," (","program"," failed"," with"," that"," exception",","," as"," expected"," —"," not"," an"," Error"," object",").\n\n","I"," did"," not"," ret","ry"," the"," program"," or"," create"," other"," files","."," Now"," finish"," mentioning"," `","report",".txt","`"," and"," `","说明",".txt","`"," in"," inline"," code",","," with"," PRES","ENT","_D","ONE"," in"," a"," separate"," paragraph","."]},{"type":"chunk","time":1793794942005,"chunk":{"type":"block-start","index":1,"blockType":"text"}},{"type":"text-chunks","time0":1793794942005,"index":1,"dt":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"texts":["The"," single"," program"," ran"," exactly"," as"," ordered",":"," `","present","`"," for"," `","missing",".txt","`"," failed"," and"," was"," caught"," (","no"," file"," created","),"," bash"," ran"," the"," exact"," command"," successfully",","," then"," `","present","`"," succeeded"," for"," `","report",".txt","`"," and"," `","说明",".txt","`,"," after"," which"," the"," program"," deliberately"," threw"," the"," string"," `","AF","TER","_P","RES","ENT","`"," —"," no"," ret","ries",","," no"," extra"," files",".\n\n","PR","ES","ENT","_D","ONE"]},{"type":"chunk","time":1793794942005,"chunk":{"type":"block-end","index":0,"block":{"type":"reasoning","text":"The program ran as intended:\n1. present for missing.txt failed with a ToolCallError, caught and logged — missing.txt was not created.\n2. bash ran exactly the given command, exit 0.\n3. present succeeded for report.txt and 说明.txt.\n4. Then threw the plain string \"AFTER_PRESENT\" (program failed with that exception, as expected — not an Error object).\n\nI did not retry the program or create other files. Now finish mentioning `report.txt` and `说明.txt` in inline code, with PRESENT_DONE in a separate paragraph."}}},{"type":"chunk","time":1793794942005,"chunk":{"type":"block-end","index":1,"block":{"type":"text","text":"The single program ran exactly as ordered: `present` for `missing.txt` failed and was caught (no file created), bash ran the exact command successfully, then `present` succeeded for `report.txt` and `说明.txt`, after which the program deliberately threw the string `AFTER_PRESENT` — no retries, no extra files.\n\nPRESENT_DONE"}}},{"type":"chunk","time":1793794942006,"chunk":{"type":"usage","usage":{"inputTokens":94,"outputTokens":198,"totalTokens":9636,"cacheReadTokens":9344,"reasoningTokens":121}}},{"type":"chunk","time":1793794942006,"chunk":{"type":"finish","reason":{"kind":"stop"}}}]},"surfaceOp":"append"} +{"type":"assistant/message","data":{"turn":1,"step":2,"message":{"role":"assistant","content":[{"type":"reasoning","text":"The program ran as intended:\n1. present for missing.txt failed with a ToolCallError, caught and logged — missing.txt was not created.\n2. bash ran exactly the given command, exit 0.\n3. present succeeded for report.txt and 说明.txt.\n4. Then threw the plain string \"AFTER_PRESENT\" (program failed with that exception, as expected — not an Error object).\n\nI did not retry the program or create other files. Now finish mentioning `report.txt` and `说明.txt` in inline code, with PRESENT_DONE in a separate paragraph."},{"type":"text","text":"The single program ran exactly as ordered: `present` for `missing.txt` failed and was caught (no file created), bash ran the exact command successfully, then `present` succeeded for `report.txt` and `说明.txt`, after which the program deliberately threw the string `AFTER_PRESENT` — no retries, no extra files.\n\nPRESENT_DONE"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:8}}"},"usage":{"inputTokens":94,"outputTokens":198,"totalTokens":9636,"cacheReadTokens":9344,"reasoningTokens":121},"stream":[{"type":"chunk","time":1794196384868,"chunk":{"type":"block-start","index":0,"blockType":"reasoning"}},{"type":"chunk","time":1794196384868,"chunk":{"type":"block-start","index":1,"blockType":"text"}},{"type":"chunk","time":1794196384869,"chunk":{"type":"block-end","index":0,"block":{"type":"reasoning","text":"The program ran as intended:\n1. present for missing.txt failed with a ToolCallError, caught and logged — missing.txt was not created.\n2. bash ran exactly the given command, exit 0.\n3. present succeeded for report.txt and 说明.txt.\n4. Then threw the plain string \"AFTER_PRESENT\" (program failed with that exception, as expected — not an Error object).\n\nI did not retry the program or create other files. Now finish mentioning `report.txt` and `说明.txt` in inline code, with PRESENT_DONE in a separate paragraph."}}},{"type":"chunk","time":1794196384869,"chunk":{"type":"block-end","index":1,"block":{"type":"text","text":"The single program ran exactly as ordered: `present` for `missing.txt` failed and was caught (no file created), bash ran the exact command successfully, then `present` succeeded for `report.txt` and `说明.txt`, after which the program deliberately threw the string `AFTER_PRESENT` — no retries, no extra files.\n\nPRESENT_DONE"}}},{"type":"chunk","time":1794196384869,"chunk":{"type":"usage","usage":{"inputTokens":94,"outputTokens":198,"totalTokens":9636,"cacheReadTokens":9344,"reasoningTokens":121}}},{"type":"chunk","time":1794196384869,"chunk":{"type":"finish","reason":{"kind":"stop"}}}]},"surfaceOp":"append"} {"type":"step/end","data":{"turn":1,"step":2}} {"type":"turn/end","data":{"turn":1,"reason":{"kind":"completed"}}} diff --git a/snapshots/web/present/snapshot.yml b/snapshots/web/present/snapshot.yml index 67b8b12df0..aff13e40c6 100644 --- a/snapshots/web/present/snapshot.yml +++ b/snapshots/web/present/snapshot.yml @@ -7,3 +7,5 @@ header: class: web-ptc workspace: final: true +replay: + override: true diff --git a/snapshots/web/present/ui.expected.md b/snapshots/web/present/ui.expected.md index 26a7e07dd0..c11a0ebd34 100644 --- a/snapshots/web/present/ui.expected.md +++ b/snapshots/web/present/ui.expected.md @@ -58,6 +58,6 @@ - 'button "Access mode, current: Workspace Write"': Workspace Write - button "Select model, current DeepSeek-V4-Flash": DeepSeek-V4-Flash - button "Send message" [disabled] -- button "1 turns 2 steps · {{throughput}} tok/s": 1 turns 2 steps{{throughput}} tok/s +- button "1 turns 2 steps" - button "19K tok · Cache hit 94%": 19K tokCache hit 94% - button "8% of context used": 8% diff --git a/snapshots/web/ptc-escalation-approved/replay.override.json b/snapshots/web/ptc-escalation-approved/replay.override.json new file mode 100644 index 0000000000..d5750638c7 --- /dev/null +++ b/snapshots/web/ptc-escalation-approved/replay.override.json @@ -0,0 +1,37 @@ +[ + { + "kind": "chunks", + "chunks": [ + {"type":"block-start","index":0,"blockType":"reasoning"}, + {"type":"block-start","index":1,"blockType":"tool-call"}, + {"type":"block-end","index":0,"block":{"type":"reasoning","text":"The user wants me to use run_code with direct Node filesystem access to create approved.txt. First attempt read-only without escalation, catch only EPERM/EACCES/EROFS and return \"EXPECTED_SANDBOX_DENIAL\". Then if denied, retry with workspace-write escalation and justification.\n\nLet me do the first attempt."}}, + {"type":"tool-call-delta","index":1,"id":"call_00_QymLSBlefCVT2cty9W385328","name":"run_code","argumentsDelta":"{\"code\":\"const fs = await import(\\\"node:fs/promises\\\");\\ntry {\\n await fs.writeFile(\\\"approved.txt\\\", \\\"approved\\\\n\\\");\\n Harness.inspect(\\\"WROTE_OK\\\"); return;\\n} catch (err) {\\n const code = err && err.code;\\n if (code === \\\"EPERM\\\" || code === \\\"EACCES\\\" || code === \\\"EROFS\\\") {\\n Harness.inspect(\\\"EXPECTED_SANDBOX_DENIAL\\\"); return;\\n }\\n throw err;\\n}\",\"description\":\"Attempt approved.txt write under read-only sandbox\",\"timeoutMs\":120000}"}, + {"type":"block-end","index":1,"block":{"type":"tool-call","id":"call_00_QymLSBlefCVT2cty9W385328","name":"run_code","arguments":"{\"code\":\"const fs = await import(\\\"node:fs/promises\\\");\\ntry {\\n await fs.writeFile(\\\"approved.txt\\\", \\\"approved\\\\n\\\");\\n Harness.inspect(\\\"WROTE_OK\\\"); return;\\n} catch (err) {\\n const code = err && err.code;\\n if (code === \\\"EPERM\\\" || code === \\\"EACCES\\\" || code === \\\"EROFS\\\") {\\n Harness.inspect(\\\"EXPECTED_SANDBOX_DENIAL\\\"); return;\\n }\\n throw err;\\n}\",\"description\":\"Attempt approved.txt write under read-only sandbox\",\"timeoutMs\":120000}"}}, + {"type":"usage","usage":{"inputTokens":1316,"outputTokens":249,"totalTokens":9117,"cacheReadTokens":7552,"reasoningTokens":72}}, + {"type":"finish","reason":{"kind":"tool-calls"}} + ] + }, + { + "kind": "chunks", + "chunks": [ + {"type":"block-start","index":0,"blockType":"reasoning"}, + {"type":"block-start","index":1,"blockType":"tool-call"}, + {"type":"block-end","index":0,"block":{"type":"reasoning","text":"The sandbox denied it. Now retry with escalation. Note that run_code supports sandbox_permissions and justification. Let me retry the exact same program with workspace-write."}}, + {"type":"tool-call-delta","index":1,"id":"call_00_3pIoFojggyHgQeOzCCph3114","name":"run_code","argumentsDelta":"{\"code\":\"const fs = await import(\\\"node:fs/promises\\\");\\ntry {\\n await fs.writeFile(\\\"approved.txt\\\", \\\"approved\\\\n\\\");\\n Harness.inspect(\\\"WROTE_OK\\\"); return;\\n} catch (err) {\\n const code = err && err.code;\\n if (code === \\\"EPERM\\\" || code === \\\"EACCES\\\" || code === \\\"EROFS\\\") {\\n Harness.inspect(\\\"EXPECTED_SANDBOX_DENIAL\\\"); return;\\n }\\n throw err;\\n}\",\"description\":\"Retry approved.txt write with workspace-write escalation\",\"sandbox_permissions\":\"workspace-write\",\"justification\":\"Create the file requested by the user\",\"timeoutMs\":120000}"}, + {"type":"block-end","index":1,"block":{"type":"tool-call","id":"call_00_3pIoFojggyHgQeOzCCph3114","name":"run_code","arguments":"{\"code\":\"const fs = await import(\\\"node:fs/promises\\\");\\ntry {\\n await fs.writeFile(\\\"approved.txt\\\", \\\"approved\\\\n\\\");\\n Harness.inspect(\\\"WROTE_OK\\\"); return;\\n} catch (err) {\\n const code = err && err.code;\\n if (code === \\\"EPERM\\\" || code === \\\"EACCES\\\" || code === \\\"EROFS\\\") {\\n Harness.inspect(\\\"EXPECTED_SANDBOX_DENIAL\\\"); return;\\n }\\n throw err;\\n}\",\"description\":\"Retry approved.txt write with workspace-write escalation\",\"sandbox_permissions\":\"workspace-write\",\"justification\":\"Create the file requested by the user\",\"timeoutMs\":120000}"}}, + {"type":"usage","usage":{"inputTokens":179,"outputTokens":260,"totalTokens":9399,"cacheReadTokens":8960,"reasoningTokens":37}}, + {"type":"finish","reason":{"kind":"tool-calls"}} + ] + }, + { + "kind": "chunks", + "chunks": [ + {"type":"block-start","index":0,"blockType":"reasoning"}, + {"type":"block-start","index":1,"blockType":"text"}, + {"type":"block-end","index":0,"block":{"type":"reasoning","text":"The file was written. Let me verify content? The user said \"After the file is written, reply DONE and stop.\" So reply DONE. Maybe verify quickly—but instructions say reply DONE and stop. I'll do a minimal verification read? The user said stop after. I think just reply DONE. But should I verify? The write returned OK. I'll reply DONE."}}, + {"type":"block-end","index":1,"block":{"type":"text","text":"DONE"}}, + {"type":"usage","usage":{"inputTokens":199,"outputTokens":84,"totalTokens":9499,"cacheReadTokens":9216,"reasoningTokens":81}}, + {"type":"finish","reason":{"kind":"stop"}} + ] + } +] diff --git a/snapshots/web/ptc-escalation-approved/session.v4.jsonl b/snapshots/web/ptc-escalation-approved/session.v4.jsonl index 8bffbf9bd2..f859273a70 100644 --- a/snapshots/web/ptc-escalation-approved/session.v4.jsonl +++ b/snapshots/web/ptc-escalation-approved/session.v4.jsonl @@ -18,18 +18,18 @@ {"type":"request/header","data":{"header":{"config":{"provider":"deepseek-official","model":"deepseek-v4-flash"},"tools":"{{tools}}"},"reason":"initial"}} {"type":"request/context","data":{"provider":"deepseek-official","model":"deepseek-v4-flash","contextWindow":128000}} {"type":"session/title","data":{"title":"Use run_code with timeoutMs 120000","messageSeqs":[12],"source":{"kind":"fallback"}}} -{"type":"assistant/message","data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"reasoning","text":"The user wants me to use run_code with direct Node filesystem access to create approved.txt. First attempt read-only without escalation, catch only EPERM/EACCES/EROFS and return \"EXPECTED_SANDBOX_DENIAL\". Then if denied, retry with workspace-write escalation and justification.\n\nLet me do the first attempt."},{"type":"tool-call","id":"call_00_QymLSBlefCVT2cty9W385328","name":"run_code","arguments":"{\"code\": \"const fs = await import(\\\"node:fs/promises\\\");\\ntry {\\n await fs.writeFile(\\\"approved.txt\\\", \\\"approved\\\\n\\\");\\n return \\\"WROTE_OK\\\";\\n} catch (err) {\\n const code = err && err.code;\\n if (code === \\\"EPERM\\\" || code === \\\"EACCES\\\" || code === \\\"EROFS\\\") {\\n return \\\"EXPECTED_SANDBOX_DENIAL\\\";\\n }\\n throw err;\\n}\", \"description\": \"Attempt approved.txt write under read-only sandbox\", \"timeoutMs\": 120000}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:6}}"},"usage":{"inputTokens":1316,"outputTokens":249,"totalTokens":9117,"cacheReadTokens":7552,"reasoningTokens":72},"stream":[{"type":"chunk","time":1793795820931,"chunk":{"type":"block-start","index":0,"blockType":"reasoning"}},{"type":"reasoning-chunks","time0":1793795820946,"index":0,"dt":[17,17,16,16,15,17,15,17,16,17,16,18,17,18,16,18,15,18,18,16,17,18,17,17,15,18,18,18,16,18,16,18,16,16,16,19,17,18,17,15,17,15,16,18,18,16,17,16,17,16,15,18,18,16,18,18,79,16,16,18,17,17,16,18,17,17,16,18,16,17,17],"texts":["The"," user"," wants"," me"," to"," use"," run","_code"," with"," direct"," Node"," files","ystem"," access"," to"," create"," approved",".txt","."," First"," attempt"," read","-only"," without"," escalation",","," catch"," only"," E","PER","M","/E","ACC","ES","/","ERO","FS"," and"," return"," \"","EXP","ECT","ED","_S","AN","DB","OX","_D","EN","IAL","\"."," Then"," if"," denied",","," ret","ry"," with"," workspace","-w","rite"," escalation"," and"," justification",".\n\n","Let"," me"," do"," the"," first"," attempt","."]},{"type":"chunk","time":1793795822219,"chunk":{"type":"block-start","index":1,"blockType":"tool-call"}},{"type":"tool-call-chunks","time0":1793795822236,"index":1,"dt":[16,17,15,17,17,15,18,16,18,17,17,17,16,17,17,16,18,16,17,17,17,15,16,15,18,18,17,17,16,15,17,17,16,16,18,17,17,17,17,17,16,16,20,17,17,17,17,17,18,16,17,17,16,18,17,17,16,16,15,16,18,18,17,16,17,18,17,17,18,16,16,17,17,16,18,18,16,17,17,17,17,18,15,17,17,17,18,17,15,18,18,16,17,17,16,18,17,17,18,18,18,18,18,16,17,18,16,18,17,15,20,16,15,17,17,14,16,17,15,17,17,17,17,15,17,16,18,16,17,18,17],"id":"call_00_QymLSBlefCVT2cty9W385328","name":"run_code","args":["","{","\"","code","\"",": ","\"","const"," fs"," ="," await"," import","(\\\"","node",":","fs","/p","rom","ises","\\\");\\n","try"," {\\n"," "," await"," fs",".write","File","(\\\"","approved",".txt","\\\","," \\\"","approved","\\\\n","\\\");\\n"," "," return"," \\\"","W","RO","TE","_OK","\\\";\\n","}"," catch"," (","err",")"," {\\n"," "," const"," code"," ="," err"," &&"," err",".code",";\\n"," "," if"," (","code"," ==="," \\\"","EP","ER","M","\\\""," ||"," code"," ==="," \\\"","E","ACC","ES","\\\""," ||"," code"," ==="," \\\"","ERO","FS","\\\")"," {\\n"," "," return"," \\\"","EXP","ECT","ED","_S","AN","DB","OX","_D","EN","IAL","\\\";\\n"," "," }\\n"," "," throw"," err",";\\n","}","\"",", ","\"","description","\"",": ","\"","Attempt"," approved",".txt"," write"," under"," read","-only"," sand","box","\"",", ","\"","time","out","Ms","\"",": ","120","000","}"]},{"type":"chunk","time":1793795824459,"chunk":{"type":"block-end","index":0,"block":{"type":"reasoning","text":"The user wants me to use run_code with direct Node filesystem access to create approved.txt. First attempt read-only without escalation, catch only EPERM/EACCES/EROFS and return \"EXPECTED_SANDBOX_DENIAL\". Then if denied, retry with workspace-write escalation and justification.\n\nLet me do the first attempt."}}},{"type":"chunk","time":1793795824476,"chunk":{"type":"block-end","index":1,"block":{"type":"tool-call","id":"call_00_QymLSBlefCVT2cty9W385328","name":"run_code","arguments":"{\"code\": \"const fs = await import(\\\"node:fs/promises\\\");\\ntry {\\n await fs.writeFile(\\\"approved.txt\\\", \\\"approved\\\\n\\\");\\n return \\\"WROTE_OK\\\";\\n} catch (err) {\\n const code = err && err.code;\\n if (code === \\\"EPERM\\\" || code === \\\"EACCES\\\" || code === \\\"EROFS\\\") {\\n return \\\"EXPECTED_SANDBOX_DENIAL\\\";\\n }\\n throw err;\\n}\", \"description\": \"Attempt approved.txt write under read-only sandbox\", \"timeoutMs\": 120000}"}}},{"type":"chunk","time":1793795824492,"chunk":{"type":"usage","usage":{"inputTokens":1316,"outputTokens":249,"totalTokens":9117,"cacheReadTokens":7552,"reasoningTokens":72}}},{"type":"chunk","time":1793795824509,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"} -{"type":"tool/call","data":{"turn":1,"step":1,"callId":"call_00_QymLSBlefCVT2cty9W385328","name":"run_code","arguments":"{\"code\": \"const fs = await import(\\\"node:fs/promises\\\");\\ntry {\\n await fs.writeFile(\\\"approved.txt\\\", \\\"approved\\\\n\\\");\\n return \\\"WROTE_OK\\\";\\n} catch (err) {\\n const code = err && err.code;\\n if (code === \\\"EPERM\\\" || code === \\\"EACCES\\\" || code === \\\"EROFS\\\") {\\n return \\\"EXPECTED_SANDBOX_DENIAL\\\";\\n }\\n throw err;\\n}\", \"description\": \"Attempt approved.txt write under read-only sandbox\", \"timeoutMs\": 120000}"}} +{"type":"assistant/message","data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"reasoning","text":"The user wants me to use run_code with direct Node filesystem access to create approved.txt. First attempt read-only without escalation, catch only EPERM/EACCES/EROFS and return \"EXPECTED_SANDBOX_DENIAL\". Then if denied, retry with workspace-write escalation and justification.\n\nLet me do the first attempt."},{"type":"tool-call","id":"call_00_QymLSBlefCVT2cty9W385328","name":"run_code","arguments":"{\"code\":\"const fs = await import(\\\"node:fs/promises\\\");\\ntry {\\n await fs.writeFile(\\\"approved.txt\\\", \\\"approved\\\\n\\\");\\n Harness.inspect(\\\"WROTE_OK\\\"); return;\\n} catch (err) {\\n const code = err && err.code;\\n if (code === \\\"EPERM\\\" || code === \\\"EACCES\\\" || code === \\\"EROFS\\\") {\\n Harness.inspect(\\\"EXPECTED_SANDBOX_DENIAL\\\"); return;\\n }\\n throw err;\\n}\",\"description\":\"Attempt approved.txt write under read-only sandbox\",\"timeoutMs\":120000}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:6}}"},"usage":{"inputTokens":1316,"outputTokens":249,"totalTokens":9117,"cacheReadTokens":7552,"reasoningTokens":72},"stream":[{"type":"chunk","time":1794196398075,"chunk":{"type":"block-start","index":0,"blockType":"reasoning"}},{"type":"chunk","time":1794196398091,"chunk":{"type":"block-start","index":1,"blockType":"tool-call"}},{"type":"chunk","time":1794196398106,"chunk":{"type":"block-end","index":0,"block":{"type":"reasoning","text":"The user wants me to use run_code with direct Node filesystem access to create approved.txt. First attempt read-only without escalation, catch only EPERM/EACCES/EROFS and return \"EXPECTED_SANDBOX_DENIAL\". Then if denied, retry with workspace-write escalation and justification.\n\nLet me do the first attempt."}}},{"type":"tool-call-chunks","time0":1794196398122,"index":1,"dt":[],"id":"call_00_QymLSBlefCVT2cty9W385328","name":"run_code","args":["{\"code\":\"const fs = await import(\\\"node:fs/promises\\\");\\ntry {\\n await fs.writeFile(\\\"approved.txt\\\", \\\"approved\\\\n\\\");\\n Harness.inspect(\\\"WROTE_OK\\\"); return;\\n} catch (err) {\\n const code = err && err.code;\\n if (code === \\\"EPERM\\\" || code === \\\"EACCES\\\" || code === \\\"EROFS\\\") {\\n Harness.inspect(\\\"EXPECTED_SANDBOX_DENIAL\\\"); return;\\n }\\n throw err;\\n}\",\"description\":\"Attempt approved.txt write under read-only sandbox\",\"timeoutMs\":120000}"]},{"type":"chunk","time":1794196398139,"chunk":{"type":"block-end","index":1,"block":{"type":"tool-call","id":"call_00_QymLSBlefCVT2cty9W385328","name":"run_code","arguments":"{\"code\":\"const fs = await import(\\\"node:fs/promises\\\");\\ntry {\\n await fs.writeFile(\\\"approved.txt\\\", \\\"approved\\\\n\\\");\\n Harness.inspect(\\\"WROTE_OK\\\"); return;\\n} catch (err) {\\n const code = err && err.code;\\n if (code === \\\"EPERM\\\" || code === \\\"EACCES\\\" || code === \\\"EROFS\\\") {\\n Harness.inspect(\\\"EXPECTED_SANDBOX_DENIAL\\\"); return;\\n }\\n throw err;\\n}\",\"description\":\"Attempt approved.txt write under read-only sandbox\",\"timeoutMs\":120000}"}}},{"type":"chunk","time":1794196398155,"chunk":{"type":"usage","usage":{"inputTokens":1316,"outputTokens":249,"totalTokens":9117,"cacheReadTokens":7552,"reasoningTokens":72}}},{"type":"chunk","time":1794196398171,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"} +{"type":"tool/call","data":{"turn":1,"step":1,"callId":"call_00_QymLSBlefCVT2cty9W385328","name":"run_code","arguments":"{\"code\":\"const fs = await import(\\\"node:fs/promises\\\");\\ntry {\\n await fs.writeFile(\\\"approved.txt\\\", \\\"approved\\\\n\\\");\\n Harness.inspect(\\\"WROTE_OK\\\"); return;\\n} catch (err) {\\n const code = err && err.code;\\n if (code === \\\"EPERM\\\" || code === \\\"EACCES\\\" || code === \\\"EROFS\\\") {\\n Harness.inspect(\\\"EXPECTED_SANDBOX_DENIAL\\\"); return;\\n }\\n throw err;\\n}\",\"description\":\"Attempt approved.txt write under read-only sandbox\",\"timeoutMs\":120000}"}} {"type":"tool/result","data":{"turn":1,"step":1,"message":{"role":"tool","source":{"kind":"tool","callId":"call_00_QymLSBlefCVT2cty9W385328"},"toolCallId":"call_00_QymLSBlefCVT2cty9W385328","content":[{"type":"text","text":"EXPECTED_SANDBOX_DENIAL"}],"isError":false,"id":"{{message:7}}"}},"sourceEventSeqs":[20],"surfaceOp":"append"} {"type":"step/end","data":{"turn":1,"step":1}} {"type":"step/start","data":{"turn":1,"step":2}} -{"type":"assistant/message","data":{"turn":1,"step":2,"message":{"role":"assistant","content":[{"type":"reasoning","text":"The sandbox denied it. Now retry with escalation. Note that run_code supports sandbox_permissions and justification. Let me retry the exact same program with workspace-write."},{"type":"tool-call","id":"call_00_3pIoFojggyHgQeOzCCph3114","name":"run_code","arguments":"{\"code\": \"const fs = await import(\\\"node:fs/promises\\\");\\ntry {\\n await fs.writeFile(\\\"approved.txt\\\", \\\"approved\\\\n\\\");\\n return \\\"WROTE_OK\\\";\\n} catch (err) {\\n const code = err && err.code;\\n if (code === \\\"EPERM\\\" || code === \\\"EACCES\\\" || code === \\\"EROFS\\\") {\\n return \\\"EXPECTED_SANDBOX_DENIAL\\\";\\n }\\n throw err;\\n}\", \"description\": \"Retry approved.txt write with workspace-write escalation\", \"sandbox_permissions\": \"workspace-write\", \"justification\": \"Create the file requested by the user\", \"timeoutMs\": 120000}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:8}}"},"usage":{"inputTokens":179,"outputTokens":260,"totalTokens":9399,"cacheReadTokens":8960,"reasoningTokens":37},"stream":[{"type":"chunk","time":1793795824652,"chunk":{"type":"block-start","index":0,"blockType":"reasoning"}},{"type":"reasoning-chunks","time0":1793795824669,"index":0,"dt":[15,15,16,17,16,18,17,16,16,18,18,17,17,17,15,16,17,17,17,17,17,17,16,17,19,16,16,17,17,16,15,15,17,17,17,17],"texts":["The"," sand","box"," denied"," it","."," Now"," ret","ry"," with"," escalation","."," Note"," that"," run","_code"," supports"," sand","box","_per","missions"," and"," justification","."," Let"," me"," ret","ry"," the"," exact"," same"," program"," with"," workspace","-w","rite","."]},{"type":"chunk","time":1793795825284,"chunk":{"type":"block-start","index":1,"blockType":"tool-call"}},{"type":"tool-call-chunks","time0":1793795825302,"index":1,"dt":[17,17,16,16,17,16,17,18,16,18,17,16,16,16,17,18,18,18,17,17,16,18,16,17,15,14,18,17,15,19,19,17,17,18,16,17,17,21,17,16,17,15,17,16,16,16,16,22,18,17,17,17,17,17,17,16,16,16,18,17,15,16,18,15,18,17,17,17,18,17,16,18,17,16,16,17,17,16,17,16,17,17,17,18,15,15,18,17,17,17,16,16,17,17,15,16,17,17,16,17,18,17,17,15,17,16,18,19,18,16,19,17,17,16,16,17,17,16,18,17,16,17,15,18,17,15,18,16,16,15,18,15,18,17,17,17,18,18,16,17,18,17,15,17,16,16,18,17,17,17,17,17,17,16,17,16,18,17,17,18,17],"id":"call_00_3pIoFojggyHgQeOzCCph3114","name":"run_code","args":["","{","\"","code","\"",": ","\"","const"," fs"," ="," await"," import","(\\\"","node",":","fs","/p","rom","ises","\\\");\\n","try"," {\\n"," "," await"," fs",".write","File","(\\\"","approved",".txt","\\\","," \\\"","approved","\\\\n","\\\");\\n"," "," return"," \\\"","W","RO","TE","_OK","\\\";\\n","}"," catch"," (","err",")"," {\\n"," "," const"," code"," ="," err"," &&"," err",".code",";\\n"," "," if"," (","code"," ==="," \\\"","EP","ER","M","\\\""," ||"," code"," ==="," \\\"","E","ACC","ES","\\\""," ||"," code"," ==="," \\\"","ERO","FS","\\\")"," {\\n"," "," return"," \\\"","EXP","ECT","ED","_S","AN","DB","OX","_D","EN","IAL","\\\";\\n"," "," }\\n"," "," throw"," err",";\\n","}","\"",", ","\"","description","\"",": ","\"","Ret","ry"," approved",".txt"," write"," with"," workspace","-w","rite"," escalation","\"",", ","\"","sand","box","_per","missions","\"",": ","\"","works","pace","-w","rite","\"",", ","\"","just","ification","\"",": ","\"","Create"," the"," file"," requested"," by"," the"," user","\"",", ","\"","time","out","Ms","\"",": ","120","000","}"]},{"type":"chunk","time":1793795828030,"chunk":{"type":"block-end","index":0,"block":{"type":"reasoning","text":"The sandbox denied it. Now retry with escalation. Note that run_code supports sandbox_permissions and justification. Let me retry the exact same program with workspace-write."}}},{"type":"chunk","time":1793795828045,"chunk":{"type":"block-end","index":1,"block":{"type":"tool-call","id":"call_00_3pIoFojggyHgQeOzCCph3114","name":"run_code","arguments":"{\"code\": \"const fs = await import(\\\"node:fs/promises\\\");\\ntry {\\n await fs.writeFile(\\\"approved.txt\\\", \\\"approved\\\\n\\\");\\n return \\\"WROTE_OK\\\";\\n} catch (err) {\\n const code = err && err.code;\\n if (code === \\\"EPERM\\\" || code === \\\"EACCES\\\" || code === \\\"EROFS\\\") {\\n return \\\"EXPECTED_SANDBOX_DENIAL\\\";\\n }\\n throw err;\\n}\", \"description\": \"Retry approved.txt write with workspace-write escalation\", \"sandbox_permissions\": \"workspace-write\", \"justification\": \"Create the file requested by the user\", \"timeoutMs\": 120000}"}}},{"type":"chunk","time":1793795828063,"chunk":{"type":"usage","usage":{"inputTokens":179,"outputTokens":260,"totalTokens":9399,"cacheReadTokens":8960,"reasoningTokens":37}}},{"type":"chunk","time":1793795828081,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"} -{"type":"tool/call","data":{"turn":1,"step":2,"callId":"call_00_3pIoFojggyHgQeOzCCph3114","name":"run_code","arguments":"{\"code\": \"const fs = await import(\\\"node:fs/promises\\\");\\ntry {\\n await fs.writeFile(\\\"approved.txt\\\", \\\"approved\\\\n\\\");\\n return \\\"WROTE_OK\\\";\\n} catch (err) {\\n const code = err && err.code;\\n if (code === \\\"EPERM\\\" || code === \\\"EACCES\\\" || code === \\\"EROFS\\\") {\\n return \\\"EXPECTED_SANDBOX_DENIAL\\\";\\n }\\n throw err;\\n}\", \"description\": \"Retry approved.txt write with workspace-write escalation\", \"sandbox_permissions\": \"workspace-write\", \"justification\": \"Create the file requested by the user\", \"timeoutMs\": 120000}"}} +{"type":"assistant/message","data":{"turn":1,"step":2,"message":{"role":"assistant","content":[{"type":"reasoning","text":"The sandbox denied it. Now retry with escalation. Note that run_code supports sandbox_permissions and justification. Let me retry the exact same program with workspace-write."},{"type":"tool-call","id":"call_00_3pIoFojggyHgQeOzCCph3114","name":"run_code","arguments":"{\"code\":\"const fs = await import(\\\"node:fs/promises\\\");\\ntry {\\n await fs.writeFile(\\\"approved.txt\\\", \\\"approved\\\\n\\\");\\n Harness.inspect(\\\"WROTE_OK\\\"); return;\\n} catch (err) {\\n const code = err && err.code;\\n if (code === \\\"EPERM\\\" || code === \\\"EACCES\\\" || code === \\\"EROFS\\\") {\\n Harness.inspect(\\\"EXPECTED_SANDBOX_DENIAL\\\"); return;\\n }\\n throw err;\\n}\",\"description\":\"Retry approved.txt write with workspace-write escalation\",\"sandbox_permissions\":\"workspace-write\",\"justification\":\"Create the file requested by the user\",\"timeoutMs\":120000}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:8}}"},"usage":{"inputTokens":179,"outputTokens":260,"totalTokens":9399,"cacheReadTokens":8960,"reasoningTokens":37},"stream":[{"type":"chunk","time":1794196398353,"chunk":{"type":"block-start","index":0,"blockType":"reasoning"}},{"type":"chunk","time":1794196398368,"chunk":{"type":"block-start","index":1,"blockType":"tool-call"}},{"type":"chunk","time":1794196398385,"chunk":{"type":"block-end","index":0,"block":{"type":"reasoning","text":"The sandbox denied it. Now retry with escalation. Note that run_code supports sandbox_permissions and justification. Let me retry the exact same program with workspace-write."}}},{"type":"tool-call-chunks","time0":1794196398401,"index":1,"dt":[],"id":"call_00_3pIoFojggyHgQeOzCCph3114","name":"run_code","args":["{\"code\":\"const fs = await import(\\\"node:fs/promises\\\");\\ntry {\\n await fs.writeFile(\\\"approved.txt\\\", \\\"approved\\\\n\\\");\\n Harness.inspect(\\\"WROTE_OK\\\"); return;\\n} catch (err) {\\n const code = err && err.code;\\n if (code === \\\"EPERM\\\" || code === \\\"EACCES\\\" || code === \\\"EROFS\\\") {\\n Harness.inspect(\\\"EXPECTED_SANDBOX_DENIAL\\\"); return;\\n }\\n throw err;\\n}\",\"description\":\"Retry approved.txt write with workspace-write escalation\",\"sandbox_permissions\":\"workspace-write\",\"justification\":\"Create the file requested by the user\",\"timeoutMs\":120000}"]},{"type":"chunk","time":1794196398417,"chunk":{"type":"block-end","index":1,"block":{"type":"tool-call","id":"call_00_3pIoFojggyHgQeOzCCph3114","name":"run_code","arguments":"{\"code\":\"const fs = await import(\\\"node:fs/promises\\\");\\ntry {\\n await fs.writeFile(\\\"approved.txt\\\", \\\"approved\\\\n\\\");\\n Harness.inspect(\\\"WROTE_OK\\\"); return;\\n} catch (err) {\\n const code = err && err.code;\\n if (code === \\\"EPERM\\\" || code === \\\"EACCES\\\" || code === \\\"EROFS\\\") {\\n Harness.inspect(\\\"EXPECTED_SANDBOX_DENIAL\\\"); return;\\n }\\n throw err;\\n}\",\"description\":\"Retry approved.txt write with workspace-write escalation\",\"sandbox_permissions\":\"workspace-write\",\"justification\":\"Create the file requested by the user\",\"timeoutMs\":120000}"}}},{"type":"chunk","time":1794196398433,"chunk":{"type":"usage","usage":{"inputTokens":179,"outputTokens":260,"totalTokens":9399,"cacheReadTokens":8960,"reasoningTokens":37}}},{"type":"chunk","time":1794196398449,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"} +{"type":"tool/call","data":{"turn":1,"step":2,"callId":"call_00_3pIoFojggyHgQeOzCCph3114","name":"run_code","arguments":"{\"code\":\"const fs = await import(\\\"node:fs/promises\\\");\\ntry {\\n await fs.writeFile(\\\"approved.txt\\\", \\\"approved\\\\n\\\");\\n Harness.inspect(\\\"WROTE_OK\\\"); return;\\n} catch (err) {\\n const code = err && err.code;\\n if (code === \\\"EPERM\\\" || code === \\\"EACCES\\\" || code === \\\"EROFS\\\") {\\n Harness.inspect(\\\"EXPECTED_SANDBOX_DENIAL\\\"); return;\\n }\\n throw err;\\n}\",\"description\":\"Retry approved.txt write with workspace-write escalation\",\"sandbox_permissions\":\"workspace-write\",\"justification\":\"Create the file requested by the user\",\"timeoutMs\":120000}"}} {"type":"approval/asked","data":{"id":"{{approval:1}}","toolName":"run_code","callId":"call_00_3pIoFojggyHgQeOzCCph3114","reason":"escalate sandbox to workspace-write: Create the file requested by the user"}} {"type":"approval/decided","data":{"id":"{{approval:1}}","outcome":"allowed-once"}} {"type":"tool/result","data":{"turn":1,"step":2,"message":{"role":"tool","source":{"kind":"tool","callId":"call_00_3pIoFojggyHgQeOzCCph3114"},"toolCallId":"call_00_3pIoFojggyHgQeOzCCph3114","content":[{"type":"text","text":"WROTE_OK"}],"isError":false,"id":"{{message:9}}"}},"sourceEventSeqs":[25],"surfaceOp":"append"} {"type":"step/end","data":{"turn":1,"step":2}} {"type":"step/start","data":{"turn":1,"step":3}} -{"type":"assistant/message","data":{"turn":1,"step":3,"message":{"role":"assistant","content":[{"type":"reasoning","text":"The file was written. Let me verify content? The user said \"After the file is written, reply DONE and stop.\" So reply DONE. Maybe verify quickly—but instructions say reply DONE and stop. I'll do a minimal verification read? The user said stop after. I think just reply DONE. But should I verify? The write returned OK. I'll reply DONE."},{"type":"text","text":"DONE"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:10}}"},"usage":{"inputTokens":199,"outputTokens":84,"totalTokens":9499,"cacheReadTokens":9216,"reasoningTokens":81},"stream":[{"type":"chunk","time":1793795828411,"chunk":{"type":"block-start","index":0,"blockType":"reasoning"}},{"type":"reasoning-chunks","time0":1793795828427,"index":0,"dt":[18,18,17,17,18,16,18,16,17,18,17,17,17,16,18,17,16,17,18,18,15,15,16,18,16,17,15,18,17,17,17,17,17,17,16,18,17,17,17,15,18,18,16,16,18,18,15,17,18,16,18,17,17,17,16,17,16,18,16,17,17,17,17,18,15,15,17,17,16,17,17,17,17,17,17,16,17,16,17,17],"texts":["The"," file"," was"," written","."," Let"," me"," verify"," content","?"," The"," user"," said"," \"","After"," the"," file"," is"," written",","," reply"," D","ONE"," and"," stop",".\""," So"," reply"," D","ONE","."," Maybe"," verify"," quickly","—","but"," instructions"," say"," reply"," D","ONE"," and"," stop","."," I","'ll"," do"," a"," minimal"," verification"," read","?"," The"," user"," said"," stop"," after","."," I"," think"," just"," reply"," D","ONE","."," But"," should"," I"," verify","?"," The"," write"," returned"," OK","."," I","'ll"," reply"," D","ONE","."]},{"type":"chunk","time":1793795829791,"chunk":{"type":"block-start","index":1,"blockType":"text"}},{"type":"text-chunks","time0":1793795829808,"index":1,"dt":[17],"texts":["D","ONE"]},{"type":"chunk","time":1793795829843,"chunk":{"type":"block-end","index":0,"block":{"type":"reasoning","text":"The file was written. Let me verify content? The user said \"After the file is written, reply DONE and stop.\" So reply DONE. Maybe verify quickly—but instructions say reply DONE and stop. I'll do a minimal verification read? The user said stop after. I think just reply DONE. But should I verify? The write returned OK. I'll reply DONE."}}},{"type":"chunk","time":1793795829860,"chunk":{"type":"block-end","index":1,"block":{"type":"text","text":"DONE"}}},{"type":"chunk","time":1793795829877,"chunk":{"type":"usage","usage":{"inputTokens":199,"outputTokens":84,"totalTokens":9499,"cacheReadTokens":9216,"reasoningTokens":81}}},{"type":"chunk","time":1793795829894,"chunk":{"type":"finish","reason":{"kind":"stop"}}}]},"surfaceOp":"append"} +{"type":"assistant/message","data":{"turn":1,"step":3,"message":{"role":"assistant","content":[{"type":"reasoning","text":"The file was written. Let me verify content? The user said \"After the file is written, reply DONE and stop.\" So reply DONE. Maybe verify quickly—but instructions say reply DONE and stop. I'll do a minimal verification read? The user said stop after. I think just reply DONE. But should I verify? The write returned OK. I'll reply DONE."},{"type":"text","text":"DONE"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:10}}"},"usage":{"inputTokens":199,"outputTokens":84,"totalTokens":9499,"cacheReadTokens":9216,"reasoningTokens":81},"stream":[{"type":"chunk","time":1794196274565,"chunk":{"type":"block-start","index":0,"blockType":"reasoning"}},{"type":"chunk","time":1794196274637,"chunk":{"type":"block-start","index":1,"blockType":"text"}},{"type":"chunk","time":1794196274653,"chunk":{"type":"block-end","index":0,"block":{"type":"reasoning","text":"The file was written. Let me verify content? The user said \"After the file is written, reply DONE and stop.\" So reply DONE. Maybe verify quickly—but instructions say reply DONE and stop. I'll do a minimal verification read? The user said stop after. I think just reply DONE. But should I verify? The write returned OK. I'll reply DONE."}}},{"type":"chunk","time":1794196274669,"chunk":{"type":"block-end","index":1,"block":{"type":"text","text":"DONE"}}},{"type":"chunk","time":1794196274686,"chunk":{"type":"usage","usage":{"inputTokens":199,"outputTokens":84,"totalTokens":9499,"cacheReadTokens":9216,"reasoningTokens":81}}},{"type":"chunk","time":1794196274702,"chunk":{"type":"finish","reason":{"kind":"stop"}}}]},"surfaceOp":"append"} {"type":"step/end","data":{"turn":1,"step":3}} {"type":"turn/end","data":{"turn":1,"reason":{"kind":"completed"}}} diff --git a/snapshots/web/ptc-escalation-approved/snapshot.yml b/snapshots/web/ptc-escalation-approved/snapshot.yml index d55fb10c45..e419a3c49b 100644 --- a/snapshots/web/ptc-escalation-approved/snapshot.yml +++ b/snapshots/web/ptc-escalation-approved/snapshot.yml @@ -7,3 +7,5 @@ header: class: web-ptc workspace: final: true +replay: + override: true diff --git a/snapshots/web/ptc-round/code.expected.md b/snapshots/web/ptc-round/code.expected.md index 1eb120612b..756f27a68d 100644 --- a/snapshots/web/ptc-round/code.expected.md +++ b/snapshots/web/ptc-round/code.expected.md @@ -3,4 +3,4 @@ - button "Wrap lines" [pressed] - button "Original JSON" - button "Copy code" - - code: "const bashResult = await tools.bash({ command: \"echo CODE_ROUND_OK\", description: \"Echo CODE_ROUND_OK\" }); let readError = null; try { await tools.read({ file_path: \"missing.txt\" }); } catch (e) { readError = { toolName: e.toolName, message: e.message }; } return { bash: bashResult.stdout.text.trim(), readError };" + - code: "const bashResult = await tools.bash({ command: \"echo CODE_ROUND_OK\", description: \"Echo CODE_ROUND_OK\" }); let readError = null; try { await tools.read({ file_path: \"missing.txt\" }); } catch (e) { readError = { toolName: e.toolName, message: e.message }; } Harness.inspect({ bash: bashResult.stdout.text.trim(), readError }); return;" diff --git a/snapshots/web/ptc-round/preparing.expected.md b/snapshots/web/ptc-round/preparing.expected.md index 2e7dfd5f31..3c203a3fc2 100644 --- a/snapshots/web/ptc-round/preparing.expected.md +++ b/snapshots/web/ptc-round/preparing.expected.md @@ -2,4 +2,4 @@ - button "Context injection skill-catalog" - button "Context injection time-context" - 'button "Think The user wants me to write a single `run_code` program that:"' -- text: Preparing tool call Code +- text: Preparing tool call Code Run bash echo and catch missing file read diff --git a/snapshots/web/ptc-round/replay.override.json b/snapshots/web/ptc-round/replay.override.json new file mode 100644 index 0000000000..562ef8ef13 --- /dev/null +++ b/snapshots/web/ptc-round/replay.override.json @@ -0,0 +1,25 @@ +[ + { + "kind": "chunks", + "chunks": [ + {"type":"block-start","index":0,"blockType":"reasoning"}, + {"type":"block-start","index":1,"blockType":"tool-call"}, + {"type":"block-end","index":0,"block":{"type":"reasoning","text":"The user wants me to write a single `run_code` program that:\n1. Runs bash to echo \"CODE_ROUND_OK\"\n2. Tries to read a file \"missing.txt\" and catches the error\n3. Returns an object with both outcomes\n4. They also want me to reply \"DONE\" and stop after\n\nLet me write this program."}}, + {"type":"tool-call-delta","index":1,"id":"call_00_6VNoF1gDSerTBKoCfYSH3765","name":"run_code","argumentsDelta":"{\"description\":\"Run bash echo and catch missing file read\",\"code\":\"\\nconst bashResult = await tools.bash({\\n command: \\\"echo CODE_ROUND_OK\\\",\\n description: \\\"Echo CODE_ROUND_OK\\\"\\n});\\n\\nlet readError = null;\\ntry {\\n await tools.read({ file_path: \\\"missing.txt\\\" });\\n} catch (e) {\\n readError = {\\n toolName: e.toolName,\\n message: e.message\\n };\\n}\\n\\nHarness.inspect({ bash: bashResult.stdout.text.trim(), readError }); return;\\n\"}"}, + {"type":"block-end","index":1,"block":{"type":"tool-call","id":"call_00_6VNoF1gDSerTBKoCfYSH3765","name":"run_code","arguments":"{\"description\":\"Run bash echo and catch missing file read\",\"code\":\"\\nconst bashResult = await tools.bash({\\n command: \\\"echo CODE_ROUND_OK\\\",\\n description: \\\"Echo CODE_ROUND_OK\\\"\\n});\\n\\nlet readError = null;\\ntry {\\n await tools.read({ file_path: \\\"missing.txt\\\" });\\n} catch (e) {\\n readError = {\\n toolName: e.toolName,\\n message: e.message\\n };\\n}\\n\\nHarness.inspect({ bash: bashResult.stdout.text.trim(), readError }); return;\\n\"}"}}, + {"type":"usage","usage":{"inputTokens":8082,"outputTokens":235,"cacheReadTokens":384,"reasoningTokens":75}}, + {"type":"finish","reason":{"kind":"tool-calls"}} + ] + }, + { + "kind": "chunks", + "chunks": [ + {"type":"block-start","index":0,"blockType":"reasoning"}, + {"type":"block-start","index":1,"blockType":"text"}, + {"type":"block-end","index":0,"block":{"type":"reasoning","text":"The program ran successfully. Let me now reply DONE as instructed."}}, + {"type":"block-end","index":1,"block":{"type":"text","text":"DONE"}}, + {"type":"usage","usage":{"inputTokens":196,"outputTokens":17,"cacheReadTokens":8576,"reasoningTokens":14}}, + {"type":"finish","reason":{"kind":"stop"}} + ] + } +] diff --git a/snapshots/web/ptc-round/session.v4.jsonl b/snapshots/web/ptc-round/session.v4.jsonl index 48d82ef490..1c72208796 100644 --- a/snapshots/web/ptc-round/session.v4.jsonl +++ b/snapshots/web/ptc-round/session.v4.jsonl @@ -14,17 +14,17 @@ {"type":"request/header","data":{"header":{"config":{"provider":"deepseek-official","model":"deepseek-v4-flash"},"tools":"{{tools}}"},"reason":"initial"}} {"type":"request/context","data":{"provider":"deepseek-official","model":"deepseek-v4-flash","contextWindow":128000}} {"type":"session/title","data":{"title":"Using ONE run_code program: run","messageSeqs":[8],"source":{"kind":"fallback"}}} -{"type":"assistant/message","data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"reasoning","text":"The user wants me to write a single `run_code` program that:\n1. Runs bash to echo \"CODE_ROUND_OK\"\n2. Tries to read a file \"missing.txt\" and catches the error\n3. Returns an object with both outcomes\n4. They also want me to reply \"DONE\" and stop after\n\nLet me write this program."},{"type":"tool-call","id":"call_00_6VNoF1gDSerTBKoCfYSH3765","name":"run_code","arguments":"{\"description\": \"Run bash echo and catch missing file read\", \"code\": \"\\nconst bashResult = await tools.bash({\\n command: \\\"echo CODE_ROUND_OK\\\",\\n description: \\\"Echo CODE_ROUND_OK\\\"\\n});\\n\\nlet readError = null;\\ntry {\\n await tools.read({ file_path: \\\"missing.txt\\\" });\\n} catch (e) {\\n readError = {\\n toolName: e.toolName,\\n message: e.message\\n };\\n}\\n\\nreturn { bash: bashResult.stdout.text.trim(), readError };\\n\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:6}}"},"usage":{"inputTokens":8082,"outputTokens":235,"cacheReadTokens":384,"reasoningTokens":75},"stream":[{"type":"chunk","time":1793795105739,"chunk":{"type":"block-start","index":0,"blockType":"reasoning"}},{"type":"reasoning-chunks","time0":1793795105755,"index":0,"dt":[18,16,15,18,17,17,17,17,15,16,18,18,17,17,15,17,17,18,16,17,17,17,18,16,15,17,16,19,17,16,18,17,16,18,18,17,18,17,16,17,17,17,17,18,14,18,18,18,17,17,16,16,17,17,17,17,16,77,18,17,17,17,17,17,16,18,15,18,18,18,17,16,17,16],"texts":["The"," user"," wants"," me"," to"," write"," a"," single"," `","run","_code","`"," program"," that",":\n","1","."," Runs"," bash"," to"," echo"," \"","CODE","_RO","UND","_OK","\"\n","2","."," T","ries"," to"," read"," a"," file"," \"","missing",".txt","\""," and"," catches"," the"," error","\n","3","."," Returns"," an"," object"," with"," both"," outcomes","\n","4","."," They"," also"," want"," me"," to"," reply"," \"","D","ONE","\""," and"," stop"," after","\n\n","Let"," me"," write"," this"," program","."]},{"type":"chunk","time":1793795107091,"chunk":{"type":"block-start","index":1,"blockType":"tool-call"}},{"type":"tool-call-chunks","time0":1793795107112,"index":1,"dt":[566,14,17,17,16,16,17,17,18,17,16,17,17,18,17,17,17,17,17,16,17,17,17,17,17,15,17,17,16,18,17,18,16,17,17,17,17,17,17,17,17,16,17,17,16,17,17,17,17,17,18,16,17,16,18,17,16,16,17,17,17,17,17,15,15,17,16,17,17,16,17,17,17,16,16,17,17,15,18,17,15,17,18,16,16,18,16,17,17,17,15,17,16,18,18,17,17,17,18,17,17,17,18,17,17,16,16,17,17,16,18,17,15,17,18,17,18,17],"id":"call_00_6VNoF1gDSerTBKoCfYSH3765","name":"run_code","args":["","{","\"","description","\"",": ","\"","Run"," bash"," echo"," and"," catch"," missing"," file"," read","\"",", ","\"","code","\"",": ","\"","\\n","const"," bash","Result"," ="," await"," tools",".b","ash","({\\n"," "," command",":"," \\\"","echo"," CODE","_RO","UND","_OK","\\\",\\n"," "," description",":"," \\\"","E","cho"," CODE","_RO","UND","_OK","\\\"\\n","});\\n\\n","let"," read","Error"," ="," null",";\\n","try"," {\\n"," "," await"," tools",".read","({"," file","_path",":"," \\\"","missing",".txt","\\\""," });\\n","}"," catch"," (","e",")"," {\\n"," "," read","Error"," ="," {\\n"," "," tool","Name",":"," e",".t","ool","Name",",\\n"," "," message",":"," e",".message","\\n"," "," };\\n","}\\n\\n","return"," {"," bash",":"," bash","Result",".stdout",".text",".trim","(),"," read","Error"," };\\n","\"","}"]},{"type":"chunk","time":1793795109659,"chunk":{"type":"block-end","index":0,"block":{"type":"reasoning","text":"The user wants me to write a single `run_code` program that:\n1. Runs bash to echo \"CODE_ROUND_OK\"\n2. Tries to read a file \"missing.txt\" and catches the error\n3. Returns an object with both outcomes\n4. They also want me to reply \"DONE\" and stop after\n\nLet me write this program."}}},{"type":"chunk","time":1793795109676,"chunk":{"type":"block-end","index":1,"block":{"type":"tool-call","id":"call_00_6VNoF1gDSerTBKoCfYSH3765","name":"run_code","arguments":"{\"description\": \"Run bash echo and catch missing file read\", \"code\": \"\\nconst bashResult = await tools.bash({\\n command: \\\"echo CODE_ROUND_OK\\\",\\n description: \\\"Echo CODE_ROUND_OK\\\"\\n});\\n\\nlet readError = null;\\ntry {\\n await tools.read({ file_path: \\\"missing.txt\\\" });\\n} catch (e) {\\n readError = {\\n toolName: e.toolName,\\n message: e.message\\n };\\n}\\n\\nreturn { bash: bashResult.stdout.text.trim(), readError };\\n\"}"}}},{"type":"chunk","time":1793795109693,"chunk":{"type":"usage","usage":{"inputTokens":8082,"outputTokens":235,"cacheReadTokens":384,"reasoningTokens":75}}},{"type":"chunk","time":1793795109710,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"} -{"type":"tool/call","data":{"turn":1,"step":1,"callId":"call_00_6VNoF1gDSerTBKoCfYSH3765","name":"run_code","arguments":"{\"description\": \"Run bash echo and catch missing file read\", \"code\": \"\\nconst bashResult = await tools.bash({\\n command: \\\"echo CODE_ROUND_OK\\\",\\n description: \\\"Echo CODE_ROUND_OK\\\"\\n});\\n\\nlet readError = null;\\ntry {\\n await tools.read({ file_path: \\\"missing.txt\\\" });\\n} catch (e) {\\n readError = {\\n toolName: e.toolName,\\n message: e.message\\n };\\n}\\n\\nreturn { bash: bashResult.stdout.text.trim(), readError };\\n\"}"}} +{"type":"assistant/message","data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"reasoning","text":"The user wants me to write a single `run_code` program that:\n1. Runs bash to echo \"CODE_ROUND_OK\"\n2. Tries to read a file \"missing.txt\" and catches the error\n3. Returns an object with both outcomes\n4. They also want me to reply \"DONE\" and stop after\n\nLet me write this program."},{"type":"tool-call","id":"call_00_6VNoF1gDSerTBKoCfYSH3765","name":"run_code","arguments":"{\"description\":\"Run bash echo and catch missing file read\",\"code\":\"\\nconst bashResult = await tools.bash({\\n command: \\\"echo CODE_ROUND_OK\\\",\\n description: \\\"Echo CODE_ROUND_OK\\\"\\n});\\n\\nlet readError = null;\\ntry {\\n await tools.read({ file_path: \\\"missing.txt\\\" });\\n} catch (e) {\\n readError = {\\n toolName: e.toolName,\\n message: e.message\\n };\\n}\\n\\nHarness.inspect({ bash: bashResult.stdout.text.trim(), readError }); return;\\n\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:6}}"},"usage":{"inputTokens":8082,"outputTokens":235,"cacheReadTokens":384,"reasoningTokens":75},"stream":[{"type":"chunk","time":1794196377860,"chunk":{"type":"block-start","index":0,"blockType":"reasoning"}},{"type":"chunk","time":1794196377876,"chunk":{"type":"block-start","index":1,"blockType":"tool-call"}},{"type":"chunk","time":1794196377892,"chunk":{"type":"block-end","index":0,"block":{"type":"reasoning","text":"The user wants me to write a single `run_code` program that:\n1. Runs bash to echo \"CODE_ROUND_OK\"\n2. Tries to read a file \"missing.txt\" and catches the error\n3. Returns an object with both outcomes\n4. They also want me to reply \"DONE\" and stop after\n\nLet me write this program."}}},{"type":"tool-call-chunks","time0":1794196377909,"index":1,"dt":[],"id":"call_00_6VNoF1gDSerTBKoCfYSH3765","name":"run_code","args":["{\"description\":\"Run bash echo and catch missing file read\",\"code\":\"\\nconst bashResult = await tools.bash({\\n command: \\\"echo CODE_ROUND_OK\\\",\\n description: \\\"Echo CODE_ROUND_OK\\\"\\n});\\n\\nlet readError = null;\\ntry {\\n await tools.read({ file_path: \\\"missing.txt\\\" });\\n} catch (e) {\\n readError = {\\n toolName: e.toolName,\\n message: e.message\\n };\\n}\\n\\nHarness.inspect({ bash: bashResult.stdout.text.trim(), readError }); return;\\n\"}"]},{"type":"chunk","time":1794196378184,"chunk":{"type":"block-end","index":1,"block":{"type":"tool-call","id":"call_00_6VNoF1gDSerTBKoCfYSH3765","name":"run_code","arguments":"{\"description\":\"Run bash echo and catch missing file read\",\"code\":\"\\nconst bashResult = await tools.bash({\\n command: \\\"echo CODE_ROUND_OK\\\",\\n description: \\\"Echo CODE_ROUND_OK\\\"\\n});\\n\\nlet readError = null;\\ntry {\\n await tools.read({ file_path: \\\"missing.txt\\\" });\\n} catch (e) {\\n readError = {\\n toolName: e.toolName,\\n message: e.message\\n };\\n}\\n\\nHarness.inspect({ bash: bashResult.stdout.text.trim(), readError }); return;\\n\"}"}}},{"type":"chunk","time":1794196378199,"chunk":{"type":"usage","usage":{"inputTokens":8082,"outputTokens":235,"cacheReadTokens":384,"reasoningTokens":75}}},{"type":"chunk","time":1794196378215,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"} +{"type":"tool/call","data":{"turn":1,"step":1,"callId":"call_00_6VNoF1gDSerTBKoCfYSH3765","name":"run_code","arguments":"{\"description\":\"Run bash echo and catch missing file read\",\"code\":\"\\nconst bashResult = await tools.bash({\\n command: \\\"echo CODE_ROUND_OK\\\",\\n description: \\\"Echo CODE_ROUND_OK\\\"\\n});\\n\\nlet readError = null;\\ntry {\\n await tools.read({ file_path: \\\"missing.txt\\\" });\\n} catch (e) {\\n readError = {\\n toolName: e.toolName,\\n message: e.message\\n };\\n}\\n\\nHarness.inspect({ bash: bashResult.stdout.text.trim(), readError }); return;\\n\"}"}} {"type":"tool/ptc-dispatch-start","data":{"rootCallId":"call_00_6VNoF1gDSerTBKoCfYSH3765","parentCallId":"call_00_6VNoF1gDSerTBKoCfYSH3765","subCallId":"call_00_6VNoF1gDSerTBKoCfYSH3765:ptc:1","name":"bash","arguments":{"command":"echo CODE_ROUND_OK","description":"Echo CODE_ROUND_OK"}}} {"type":"job/registered","data":{"jobId":"{{id:1}}","owner":"{{session:1}}","kind":"bash","label":"echo CODE_ROUND_OK","startedAt":1793795109798,"toolCall":{"callId":"call_00_6VNoF1gDSerTBKoCfYSH3765:ptc:1","rootCallId":"call_00_6VNoF1gDSerTBKoCfYSH3765"}}} {"type":"job/settled","data":{"jobId":"{{id:1}}","owner":"{{session:1}}","kind":"bash","label":"echo CODE_ROUND_OK","startedAt":1793795109798,"toolCall":{"callId":"call_00_6VNoF1gDSerTBKoCfYSH3765:ptc:1","rootCallId":"call_00_6VNoF1gDSerTBKoCfYSH3765"},"finishedAt":1793795109808,"status":"completed","cause":"producer","producerReported":true,"detail":"exit code: 0"}} {"type":"tool/ptc-dispatch","data":{"rootCallId":"call_00_6VNoF1gDSerTBKoCfYSH3765","parentCallId":"call_00_6VNoF1gDSerTBKoCfYSH3765","subCallId":"call_00_6VNoF1gDSerTBKoCfYSH3765:ptc:1","name":"bash","arguments":{"command":"echo CODE_ROUND_OK","description":"Echo CODE_ROUND_OK"},"isError":false,"content":[{"type":"text","text":"CODE_ROUND_OK\n"}],"meta":{"cwd":"{{cwd}}"}}} {"type":"tool/ptc-dispatch-start","data":{"rootCallId":"call_00_6VNoF1gDSerTBKoCfYSH3765","parentCallId":"call_00_6VNoF1gDSerTBKoCfYSH3765","subCallId":"call_00_6VNoF1gDSerTBKoCfYSH3765:ptc:2","name":"read","arguments":{"file_path":"missing.txt"}}} {"type":"tool/ptc-dispatch","data":{"rootCallId":"call_00_6VNoF1gDSerTBKoCfYSH3765","parentCallId":"call_00_6VNoF1gDSerTBKoCfYSH3765","subCallId":"call_00_6VNoF1gDSerTBKoCfYSH3765:ptc:2","name":"read","arguments":{"file_path":"missing.txt"},"isError":true,"error":{"name":"FsError","code":"FS_NOT_FOUND"},"content":[{"type":"text","text":"Error: cannot read \"{{cwd}}/missing.txt\": not found"}]}} -{"type":"tool/result","data":{"turn":1,"step":1,"message":{"role":"tool","source":{"kind":"tool","callId":"call_00_6VNoF1gDSerTBKoCfYSH3765"},"toolCallId":"call_00_6VNoF1gDSerTBKoCfYSH3765","content":[{"type":"text","text":"{\n \"bash\": \"CODE_ROUND_OK\",\n \"readError\": {\n \"toolName\": \"read\",\n \"message\": \"cannot read \\\"{{cwd}}/missing.txt\\\": not found\"\n }\n}"}],"isError":false,"id":"{{message:7}}"}},"sourceEventSeqs":[16],"surfaceOp":"append"} +{"type":"tool/result","data":{"turn":1,"step":1,"message":{"role":"tool","source":{"kind":"tool","callId":"call_00_6VNoF1gDSerTBKoCfYSH3765"},"toolCallId":"call_00_6VNoF1gDSerTBKoCfYSH3765","content":[{"type":"text","text":"{\n bash: 'CODE_ROUND_OK',\n readError: {\n toolName: 'read',\n message: 'cannot read \"{{cwd}}/missing.txt\": not found'\n }\n}"}],"isError":false,"id":"{{message:7}}"}},"sourceEventSeqs":[16],"surfaceOp":"append"} {"type":"step/end","data":{"turn":1,"step":1}} {"type":"step/start","data":{"turn":1,"step":2}} -{"type":"assistant/message","data":{"turn":1,"step":2,"message":{"role":"assistant","content":[{"type":"reasoning","text":"The program ran successfully. Let me now reply DONE as instructed."},{"type":"text","text":"DONE"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:8}}"},"usage":{"inputTokens":196,"outputTokens":17,"cacheReadTokens":8576,"reasoningTokens":14},"stream":[{"type":"chunk","time":1793795109867,"chunk":{"type":"block-start","index":0,"blockType":"reasoning"}},{"type":"reasoning-chunks","time0":1793795109884,"index":0,"dt":[17,17,18,15,17,16,17,17,16,17,16,16,17],"texts":["The"," program"," ran"," successfully","."," Let"," me"," now"," reply"," D","ONE"," as"," instructed","."]},{"type":"chunk","time":1793795110117,"chunk":{"type":"block-start","index":1,"blockType":"text"}},{"type":"text-chunks","time0":1793795110137,"index":1,"dt":[15],"texts":["D","ONE"]},{"type":"chunk","time":1793795110167,"chunk":{"type":"block-end","index":0,"block":{"type":"reasoning","text":"The program ran successfully. Let me now reply DONE as instructed."}}},{"type":"chunk","time":1793795110184,"chunk":{"type":"block-end","index":1,"block":{"type":"text","text":"DONE"}}},{"type":"chunk","time":1793795110201,"chunk":{"type":"usage","usage":{"inputTokens":196,"outputTokens":17,"cacheReadTokens":8576,"reasoningTokens":14}}},{"type":"chunk","time":1793795110223,"chunk":{"type":"finish","reason":{"kind":"stop"}}}]},"surfaceOp":"append"} +{"type":"assistant/message","data":{"turn":1,"step":2,"message":{"role":"assistant","content":[{"type":"reasoning","text":"The program ran successfully. Let me now reply DONE as instructed."},{"type":"text","text":"DONE"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:8}}"},"usage":{"inputTokens":196,"outputTokens":17,"cacheReadTokens":8576,"reasoningTokens":14},"stream":[{"type":"chunk","time":1794196240638,"chunk":{"type":"block-start","index":0,"blockType":"reasoning"}},{"type":"chunk","time":1794196240655,"chunk":{"type":"block-start","index":1,"blockType":"text"}},{"type":"chunk","time":1794196240671,"chunk":{"type":"block-end","index":0,"block":{"type":"reasoning","text":"The program ran successfully. Let me now reply DONE as instructed."}}},{"type":"chunk","time":1794196240687,"chunk":{"type":"block-end","index":1,"block":{"type":"text","text":"DONE"}}},{"type":"chunk","time":1794196240703,"chunk":{"type":"usage","usage":{"inputTokens":196,"outputTokens":17,"cacheReadTokens":8576,"reasoningTokens":14}}},{"type":"chunk","time":1794196240719,"chunk":{"type":"finish","reason":{"kind":"stop"}}}]},"surfaceOp":"append"} {"type":"step/end","data":{"turn":1,"step":2}} {"type":"turn/end","data":{"turn":1,"reason":{"kind":"completed"}}} diff --git a/snapshots/web/ptc-round/snapshot.yml b/snapshots/web/ptc-round/snapshot.yml index 01875305c8..c38ff2d117 100644 --- a/snapshots/web/ptc-round/snapshot.yml +++ b/snapshots/web/ptc-round/snapshot.yml @@ -6,3 +6,5 @@ recording: live header: class: web-ptc pin: true +replay: + override: true diff --git a/snapshots/web/ptc-round/system-prompt.expected.md b/snapshots/web/ptc-round/system-prompt.expected.md index 9e8a34b7e5..c40718cdad 100644 --- a/snapshots/web/ptc-round/system-prompt.expected.md +++ b/snapshots/web/ptc-round/system-prompt.expected.md @@ -36,20 +36,24 @@ Bash, formatters, code generators, and scripts can overwrite concurrent edits wi `run_code` takes two required arguments: `code`, the body of an async TypeScript function (erasable syntax only — no `enum` or namespaces; type annotations are advisory, the code runs type-stripped), and `description`, a short summary of what the program does. The declarations below are SDK bindings for this program. A declaration does not make its name a directly callable tool; only names supplied as separate tool schemas may be called directly. When no separate `bash` schema is supplied, invoke a declared `bash` binding inside `run_code`: -`run_code({ code: "return await tools.bash({ description: 'Show current directory', command: 'pwd' })", description: "Show current directory" })` +`run_code({ code: "Harness.inspect(await tools.bash({ description: 'Show current directory', command: 'pwd' }))", description: "Show current directory" })` Inside the program: - Call tools as `await tools.name(args)` — quoted access for exotic names: `tools["my-tool"](args)`. Every call resolves to the tool's typed canonical JSON value. Tool arguments must be lossless JSON. - A FAILED tool call rejects with `ToolCallError`, whose `toolName` identifies the failed tool and whose `message` is human-readable — `try/catch` it to handle and continue. - Independent read-only calls MAY overlap under `Promise.all` (safe calls run concurrently; mutating calls run alone, in submission order). Sequence dependent work with `await`. -- Emit results with `return` and/or `console.log(...)`. Only what you print or return is program output. A successful tool result containing an image is attached after the run so you can inspect it on the next step; every other intermediate result stays out of the conversation, so extract just what you need. +- Show selected results with `Harness.inspect(...values)`. Return values and final expressions are not displayed. A successful tool result containing an image is attached after the run so you can inspect it on the next step; every other intermediate result stays out of the conversation, so extract just what you need. Program-only SDK bindings: ```ts type JsonValue = null | boolean | number | string | JsonValue[] | { [key: string]: JsonValue } +declare namespace Harness { + function inspect(...values: unknown[]): void; +} + interface ToolArgsMap { /** Ask the user a concise question when you need confirmation, a choice, or missing information before proceeding. An answer item with empty selected and no custom means the user skipped that question: continue without it, do not ask it again, and do not treat the skip as permission. */ ask_user_question: { diff --git a/snapshots/web/ptc-round/tool-schemas.expected.json b/snapshots/web/ptc-round/tool-schemas.expected.json index f2ce647c4f..b1c890cd49 100644 --- a/snapshots/web/ptc-round/tool-schemas.expected.json +++ b/snapshots/web/ptc-round/tool-schemas.expected.json @@ -2,7 +2,7 @@ "initial": [ { "name": "run_code", - "description": "Execute a TypeScript program against the available tools. Takes two required arguments: `code`, the BODY of an async function (erasable syntax only; top-level `await` and `return` work), and `description`, a short summary of what the program does. Call tools as `await tools.name(args)` per the declarations in the system prompt. Only what you print or return is program output — curate it. Image-bearing subtool results are attached after the run. The program is the body of an async TypeScript function; only erasable syntax is accepted, with no enum or namespaces. Type annotations are advisory. Top-level await and return are supported. Emit output with console.log(...) or return a lossless JSON value. Only printed values and the returned value become program output. Each call runs in a fresh Node process. Node APIs are available through await import(...). Relative paths use the supplied working directory; process.env starts empty. Direct filesystem access follows this execution's sandbox policy. Each program starts in the Session's current directory. Running programs keep their initial directory. A sandbox escalation approves this complete program for one execution only. Nested tools retain their own policies and approvals. Request wider access only after evidence of a denial. Earlier effects may already have completed: inspect them before explicitly retrying. Programs are never replayed automatically.", + "description": "Execute a TypeScript program against the available tools. Takes two required arguments: `code`, the BODY of an async function (erasable syntax only; top-level `await` and `return` work), and `description`, a short summary of what the program does. Call tools as `await tools.name(args)` per the declarations in the system prompt. Show selected results with Harness.inspect(...values). Return values are not displayed. Image-bearing subtool results are attached after the run. The program is the body of an async TypeScript function; only erasable syntax is accepted, with no enum or namespaces. Type annotations are advisory. Top-level await and return are supported. Console is unavailable. Use Harness.inspect(...values) for output. Each call runs in a fresh Node process. Node APIs are available through await import(...). Relative paths use the supplied working directory; process.env starts empty. Direct filesystem access follows this execution's sandbox policy. Each program starts in the Session's current directory. Running programs keep their initial directory. A sandbox escalation approves this complete program for one execution only. Nested tools retain their own policies and approvals. Request wider access only after evidence of a denial. Earlier effects may already have completed: inspect them before explicitly retrying. Programs are never replayed automatically.", "parameters": { "type": "object", "properties": { diff --git a/snapshots/web/ptc-round/trajectory.expected.md b/snapshots/web/ptc-round/trajectory.expected.md index ff90616e70..fb9f40ca25 100644 --- a/snapshots/web/ptc-round/trajectory.expected.md +++ b/snapshots/web/ptc-round/trajectory.expected.md @@ -3,14 +3,8 @@ - text: typescript - button "Wrap lines" - button "Copy code" -- code: "const bashResult = await tools.bash({ command: \"echo CODE_ROUND_OK\", description: \"Echo CODE_ROUND_OK\" }); let readError = null; try { await tools.read({ file_path: \"missing.txt\" }); } catch (e) { readError = { toolName: e.toolName, message: e.message }; } return { bash: bashResult.stdout.text.trim(), readError };" +- code: "const bashResult = await tools.bash({ command: \"echo CODE_ROUND_OK\", description: \"Echo CODE_ROUND_OK\" }); let readError = null; try { await tools.read({ file_path: \"missing.txt\" }); } catch (e) { readError = { toolName: e.toolName, message: e.message }; } Harness.inspect({ bash: bashResult.stdout.text.trim(), readError }); return;" - heading "Output Copy output" [level=3]: - button "Output" - button "Copy output" -- text: "{" -- tree "Result JSON": - - treeitem "bash:\"CODE_ROUND_OK\"," - - 'treeitem "Expand readError:{toolName: \"read\", message: \"cannot read \\\"{{cwd}}/workspace/missing.txt\\\": not found\"}"': - - button "Expand" - - text: "readError:{toolName: \"read\", message: \"cannot read \\\"{{cwd}}/workspace/missing.txt\\\": not found\"}" -- text: "}" +- text: "{ bash: 'CODE_ROUND_OK', readError: { toolName: 'read', message: 'cannot read \"{{cwd}}/workspace/missing.txt\": not found' } }"