Files
deepseek-harness/packages/core/tool-executor-ptc/src/ptc.ts
T

758 lines
37 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* PTC mode `run_code` transport. Programs call the registry's agent-visible
* tools through nested executions scheduled under the native concurrency
* contract; each sub-dispatch is logged for reconstruction, while only the
* outer curated result enters model history.
* @module
*/
import type { PtcDispatchLog } from './runtime-types.ts'
import { symbols, type Context } from '@deepseek-ai/cordis'
import { brandString } from '@deepseek-ai/dsh-brand'
import { createUserMessage, HarnessError } from '@deepseek-ai/dsh-llm'
import type { ContentBlock, ToolCallId, ToolSchema } from '@deepseek-ai/dsh-llm'
import type { PtcBindingFunction, PtcRunResult, PtcRunSandbox, PtcRuntime } from '@deepseek-ai/dsh-ptc-runtime'
import { approveEscalation, ESCALATION_TARGETS, validateEscalationArgs } from '@deepseek-ai/dsh-sandbox'
import type { SandboxExecutionPolicy } from '@deepseek-ai/dsh-sandbox'
import type {} from '@deepseek-ai/dsh-user-approval'
import type {} from '@deepseek-ai/dsh-working-directory'
import type {} from '@deepseek-ai/dsh-sandbox-policy'
import { deepFreeze, snapshotJsonValue, type JsonValue } from '@deepseek-ai/dsh-util-values'
import { defineTool, parameterSchemaSpecToJsonSchema, RUN_CODE_NAME, TOOL_ABORTED } from '@deepseek-ai/dsh-tools'
import type { ToolExecutionPipeline } from '@deepseek-ai/dsh-tools/pipeline'
import { toolErrorMessage } from '@deepseek-ai/dsh-tools/pipeline'
import { scopeTarget } from '@deepseek-ai/dsh-scope'
import { PtcCalls } from './ptc-calls.ts'
import { CodeRunFailedError } from './errors.ts'
import type { ToolDefinition, ToolExecutionResult, ToolRunContext } from '@deepseek-ai/dsh-tools'
import type {} from './types.ts'
/** The model-facing name of the PTC mode tool. */
export { RUN_CODE_NAME }
/** Language-specific tool and code-parameter descriptions. */
interface RunCodeFlavor {
/** The tool `description` the model sees for this language. */
readonly description: string
/** The `code` parameter's description for this language. */
readonly codeDescription: string
}
/** Keep tool-call rules here; the runtime owns evaluation syntax and state retention. */
function runCodeFlavor(language: string, execution = {
programDescription: 'the program to execute',
outputInstruction: 'Curate the program output.',
codeDescription: `The ${language} program to execute, following this tool's execution instructions.`,
}): RunCodeFlavor {
return {
description: `Execute a ${language} program against the available tools. Takes two required arguments: `
+ `\`code\`, ${execution.programDescription}, and \`description\`, a short summary of what the program does. `
+ 'Call tools as `await tools.name(args)` per the declarations in the system prompt. '
+ `${execution.outputInstruction} Image-bearing subtool results are attached after the run.`,
codeDescription: execution.codeDescription,
}
}
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.',
codeDescription: 'The program: the body of an async TypeScript function.',
})
const PYTHON_FLAVOR = runCodeFlavor('Python')
/** Languages with both a transport schema and an SDK renderer. */
export type PtcSdkLanguage = 'typescript' | 'python'
type SupportedPtcRuntime = PtcRuntime & { readonly language: PtcSdkLanguage }
const RUN_CODE_FLAVORS = {
typescript: TYPESCRIPT_FLAVOR,
python: PYTHON_FLAVOR,
} satisfies Record<PtcSdkLanguage, RunCodeFlavor>
/**
* Read the active runtime, rejecting missing providers and unsupported languages.
* @param ctx - executor context containing the runtime provider.
* @returns the runtime with a supported SDK language.
*/
export function requirePtcRuntime(ctx: Context): SupportedPtcRuntime {
const runtime = ctx.get('ptcRuntime')
if (runtime === undefined) {
throw new Error('dsh-tools: mode "ptc" requires a PTC runtime — load a ctx.ptcRuntime implementation (e.g. @deepseek-ai/dsh-ptc-runtime-node) or load @deepseek-ai/dsh-tool-executor-native')
}
if (!Object.hasOwn(RUN_CODE_FLAVORS, runtime.language)) {
const known = Object.keys(RUN_CODE_FLAVORS).map(name => JSON.stringify(name)).join(', ')
throw new Error(`dsh-tools: no SDK renderer registered for runtime language ${JSON.stringify(runtime.language)} (known: ${known})`)
}
return runtime as SupportedPtcRuntime
}
/**
* The `description` parameter's model-facing description: language-independent
* (the UI label contract is the same for every runtime), shared between the
* static spec and the language-aware `parameters` getter so the two emissions
* can never drift.
*/
const RUN_CODE_DESCRIPTION_PARAM_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".'
const RUN_CODE_CONTROLS = {
timeoutMs: { type: 'number', description: 'Positive elapsed-time budget in milliseconds, capped by the deployment maximum.' },
sandbox_permissions: { type: 'string', enum: [...ESCALATION_TARGETS], description: 'Wider sandbox mode for this complete program execution; requires justification and approval.' },
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.' },
} as const
function controlParameters(runtime: PtcRuntime) {
return {
...runtime.timeout === undefined ? {} : {
timeoutMs: { ...RUN_CODE_CONTROLS.timeoutMs,
description: `Positive elapsed-time budget in milliseconds, including nested tool and approval waits. Default ${runtime.timeout.defaultMs}; capped at ${runtime.timeout.maxMs}. Zero does not disable the deadline.` },
},
...runtime.sandboxMode === undefined ? {} : {
sandbox_permissions: RUN_CODE_CONTROLS.sandbox_permissions,
justification: RUN_CODE_CONTROLS.justification,
},
}
}
function escalationGuidance(mode: PtcRuntime['sandboxMode']): string {
return mode === undefined ? ''
: ' 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.'
}
/** Match schema text to the runtime language and program syntax. */
function resolveFlavor(runtime: SupportedPtcRuntime): RunCodeFlavor {
return runtime.language === 'typescript' && runtime.programFormat === 'function-body'
? TYPESCRIPT_FUNCTION_FLAVOR : RUN_CODE_FLAVORS[runtime.language]
}
/**
* Snapshot one binding call's argument as lossless JSON, then snapshot that
* detached value again so dispatch and logging stay independent without
* reintroducing structured-clone's platform-specific nesting limit.
*/
function jsonNormalizeArgs(value: unknown): { dispatched: unknown; logged: unknown } {
let snapshot: JsonValue | undefined
try {
snapshot = snapshotJsonValue(value) as JsonValue | undefined
} catch (error: unknown) {
throw new Error(`tool arguments must be lossless JSON: ${error instanceof Error ? error.message : String(error)}`)
}
if (snapshot === undefined) {
throw new Error('tool arguments must be lossless JSON (call the tool with an arguments object, e.g. `{}`)')
}
const logged = snapshotJsonValue(snapshot)
/* v8 ignore next -- snapshot is already a detached lossless JSON value. */
if (logged === undefined) {
throw new Error('tool arguments could not be detached for durable logging')
}
return { dispatched: snapshot, logged }
}
/** Two-space JSON presentation, matching the existing shallow `run_code` text contract. */
const JSON_INDENT = ' '
/**
* ECMAScript caps `JSON.stringify`'s `space` string at ten characters. The
* renderer also caps TOTAL indentation there, compacting deeper subtrees, so
* formatted output remains linear in the canonical JSON size.
*/
const MAX_JSON_INDENT_CHARS = 10
/** A pending fragment in the iterative JSON presentation traversal. */
type JsonRenderTask =
| { kind: 'text'; text: string }
| { kind: 'value'; value: JsonValue; depth: number; compact: boolean }
/** Render one non-string JSON root without recursive traversal or unbounded indentation growth. */
function renderJsonValue(value: Exclude<JsonValue, string>): string {
const chunks: string[] = []
const tasks: JsonRenderTask[] = [{ kind: 'value', value, depth: 0, compact: false }]
for (let task = tasks.pop(); task !== undefined; task = tasks.pop()) {
if (task.kind === 'text') {
chunks.push(task.text)
continue
}
const current = task.value
if (current === null || typeof current === 'boolean' || typeof current === 'number') {
chunks.push(String(current))
continue
}
if (typeof current === 'string') {
chunks.push(JSON.stringify(current))
continue
}
const compact = task.compact || (task.depth + 1) * JSON_INDENT.length > MAX_JSON_INDENT_CHARS
const childDepth = task.depth + 1
if (Array.isArray(current)) {
chunks.push('[')
if (current.length === 0) {
chunks.push(']')
continue
}
tasks.push({ kind: 'text', text: compact ? ']' : `\n${JSON_INDENT.repeat(task.depth)}]` })
for (let index = current.length - 1; index >= 0; index--) {
const item = current[index]
/* v8 ignore next -- canonical JsonValue arrays are dense. */
if (item === undefined) throw new Error('cannot render a sparse JSON array')
tasks.push({ kind: 'value', value: item, depth: childDepth, compact })
tasks.push({
kind: 'text',
text: compact
? index === 0 ? '' : ','
: `${index === 0 ? '\n' : ',\n'}${JSON_INDENT.repeat(childDepth)}`,
})
}
continue
}
const keys = Object.keys(current)
chunks.push('{')
if (keys.length === 0) {
chunks.push('}')
continue
}
tasks.push({ kind: 'text', text: compact ? '}' : `\n${JSON_INDENT.repeat(task.depth)}}` })
for (let index = keys.length - 1; index >= 0; index--) {
const key = keys[index]
/* v8 ignore next -- the loop is bounded by the captured key count. */
if (key === undefined) throw new Error('cannot render a missing JSON object key')
const item = current[key]
/* v8 ignore next -- canonical JsonValue records contain no undefined properties. */
if (item === undefined) throw new Error('cannot render an undefined JSON object property')
tasks.push({ kind: 'value', value: item, depth: childDepth, compact })
tasks.push({
kind: 'text',
text: compact
? `${index === 0 ? '' : ','}${JSON.stringify(key)}:`
: `${index === 0 ? '\n' : ',\n'}${JSON_INDENT.repeat(childDepth)}${JSON.stringify(key)}: `,
})
}
}
return chunks.join('')
}
/** Render one present program completion value for the model-facing result text. */
function renderValue(value: JsonValue): string {
return typeof value === 'string' ? value : renderJsonValue(value)
}
/** Canonical value returned by the outer PTC mode transport. */
type RunCodeOutput = { logs: string[]; result?: JsonValue; sandbox?: PtcRunSandbox }
/** Unowned programs do not require Session directory state. */
async function resolveWorkingDirectory(ctx: Context, exec: ToolRunContext): Promise<string | undefined> {
if (exec.agent === undefined) return undefined
const directories = ctx.get('workingDirectory')
if (directories === undefined) throw new Error('dsh-tools: run_code with an Agent requires workingDirectory')
return directories.ensure(exec.agent, exec.signal)
}
/** Resolve standing authority for a runtime that enforces file policy. */
function resolveSandboxPolicy(ctx: Context, exec: ToolRunContext): SandboxExecutionPolicy {
const policy = ctx.get('sandboxPolicy')
if (policy === undefined) throw new Error('dsh-tools: confined PTC runtime requires sandboxPolicy')
return policy.resolve(exec.agent === undefined ? {} : { session: exec.agent.session })
}
/**
* Build the reserved `run_code` transport outside registered capabilities.
* @param ctx - executor context owning policy listeners and active program cleanup.
* @param executor - shared invocation pipeline for nested calls.
* @param maxParallel - maximum overlapping concurrency-safe subcalls.
* @returns the transport definition with runtime-dependent schema text.
*/
export function createRunCodeTool(
ctx: Context,
executor: ToolExecutionPipeline,
maxParallel: number,
): ToolDefinition {
// Retain services for log settlement after the executor context becomes inactive.
const tools = ctx.tools
const logger = ctx.logger
/** A failed log-content listener preserves the settled content and cannot fail the dispatch. */
async function shapeDispatchLog(dispatch: PtcDispatchLog): Promise<ContentBlock[]> {
try {
return await ctx.waterfall(
scopeTarget(tools, dispatch.agent), 'tools/ptc-dispatch-log', dispatch,
() => Promise.resolve(dispatch.content),
)
} catch (error: unknown) {
logger.warn(`tools: ptc-dispatch-log listener failed for ${dispatch.name}: ${toolErrorMessage(error)}; logging the original settled content`)
return dispatch.content
}
}
const stateOwners = new WeakMap<object, WeakSet<PtcRuntime>>()
const calls = new PtcCalls(ctx)
const definition = defineTool({
name: RUN_CODE_NAME,
// The description and `code` parameter description are placeholders here:
// the language-aware getters installed below replace both, resolving the
// loaded runtime's flavor at schema-emission time so the schema the MODEL
// sees matches the SDK section's language. Argument VALIDATION still keys
// off this static spec (defineTool closes over it), which is language-
// independent (one required string `code`).
description: TYPESCRIPT_FLAVOR.description,
parameters: {
code: { type: 'string', required: true, description: TYPESCRIPT_FLAVOR.codeDescription },
description: {
type: 'string',
required: true,
description: RUN_CODE_DESCRIPTION_PARAM_DESCRIPTION,
},
...RUN_CODE_CONTROLS,
},
output: {
schema: {
type: 'object',
additionalProperties: false,
properties: {
logs: { type: 'array', required: true, items: { type: 'string' } },
result: { type: 'json' },
sandbox: {
type: 'object',
additionalProperties: false,
properties: {
mode: { type: 'string', required: true, enum: ['read-only', 'workspace-write', 'danger-full-access'] },
denied: { type: 'boolean', required: true },
enforcement: { type: 'string', enum: ['full', 'partial'] },
},
},
},
},
render: (_args, value) => {
const rendered = value.result === undefined ? '' : renderValue(value.result)
const parts = [value.logs.join('\n'), rendered].filter(part => part.length > 0)
if (value.sandbox?.enforcement === 'partial') parts.push('File sandbox enforcement is partial on this host.')
if (value.sandbox?.denied) parts.push(`The ${value.sandbox.mode} file sandbox denied an operation.${escalationGuidance(ctx.get('ptcRuntime')?.sandboxMode)}`)
return [{ type: 'text', text: parts.length > 0 ? parts.join('\n') : '(run_code completed with no output)' }]
},
},
async execute(args, exec): Promise<RunCodeOutput> {
if (args.description.trim().length === 0) {
throw new Error('invalid description: expected a non-empty string')
}
const runtime = requirePtcRuntime(ctx)
const run = async (callSignal: AbortSignal): Promise<RunCodeOutput> => {
validateEscalationArgs(args.sandbox_permissions, args.justification)
if (args.timeoutMs !== undefined && runtime.timeout === undefined) {
throw new Error('timeoutMs is not available for this PTC runtime')
}
if (args.timeoutMs !== undefined && (!Number.isFinite(args.timeoutMs) || args.timeoutMs <= 0)) {
throw new Error('invalid timeoutMs: expected a positive finite number')
}
const standingPolicy = runtime.sandboxMode === undefined ? undefined : resolveSandboxPolicy(ctx, exec)
let policy = standingPolicy
if (args.sandbox_permissions !== undefined && args.justification !== undefined) {
if (standingPolicy === undefined) throw new Error('sandbox_permissions is not available for this PTC runtime')
const approvedMode = await approveEscalation({
requestedMode: args.sandbox_permissions,
justification: args.justification,
effectiveMode: standingPolicy.mode,
subject: 'program',
}, {
approver: ctx.get('approval'), agent: exec.agent, callId: exec.callId,
toolName: RUN_CODE_NAME, signal: callSignal,
})
policy = { ...standingPolicy, mode: approvedMode }
}
const cwd = await resolveWorkingDirectory(ctx, { ...exec, signal: callSignal })
callSignal.throwIfAborted()
// The run-scoped abort: follows the outer signal in, and fires when the
// run settles for ANY reason, so an in-flight sub-dispatch is aborted
// (its executor kills on this signal) instead of orphaned, and
// queued-unstarted dispatches are abandoned.
const runController = new AbortController()
const onOuterAbort = (): void => { runController.abort(callSignal.reason) }
callSignal.addEventListener('abort', onOuterAbort, { once: true })
let dispatches = 0
// The per-run scheduler uses the registry's staged interface and follows
// the same concurrency rules as the native loop. It also follows the
// native loop's SEQUENCING: every ordered stage (the dispatch-start
// append, prepare = pre-execute/guards, complete = post-execute,
// context deferral, the settle append) runs inside ONE driver lane, so
// ordered policy stages never overlap each other and only the
// around-dispatch/body stage runs concurrently. Starts are strictly
// submission-ordered; results commit in submission order through the
// head-of-line cursor. Consecutive parallel-classified calls overlap up
// to maxParallel; an exclusive call waits for the pool to drain, runs
// alone, and holds its barrier until its COMMIT (post-execute included)
// completes, exactly like a native exclusive group. Classification is
// re-read via executionMode() immediately before each start (a registry
// mutation while queued can flip a call exclusive), matching the native
// scheduler's lazy reclassification.
interface PendingDispatch {
/** Ordered stage: append the start event, await prepare (pre-execute/guards), launch the body into `flight`. */
start(): Promise<void>
classify(): 'parallel' | 'exclusive'
abandon(): void
/** Ordered stage: post-execute + context deferral + settle event, in submission order. */
commit(): Promise<void>
/** The launched around-dispatch/body stage; resolved until start() replaces it. */
flight: Promise<void>
/** True once the dispatch stage parked its outcome; the commit cursor waits on it. */
settled: boolean
/** The classification this entry started under; an exclusive holds its barrier through commit(). */
mode?: 'parallel' | 'exclusive'
}
const pendingQueue: PendingDispatch[] = []
const inFlight = new Set<Promise<void>>()
/** Tracked settle-event side work (log-content listener + append), drained at run settlement. */
const logWork = new Set<Promise<void>>()
const commitQueue: PendingDispatch[] = []
let exclusiveActive = false
let driving = false
let driverRun: Promise<void> = Promise.resolve()
let wake: (() => void) | undefined
const wakeup = (): void => {
const release = wake
wake = undefined
release?.()
}
/**
* The single ordered lane. Each pass commits the head-of-line settled
* dispatch (ordered post-execute), then starts the next queued entry if
* its slot is free (ordered pre-execute), and otherwise sleeps until a
* body settles or a new submission arrives. One run reaching the
* empty-queues/empty-pool state is quiescence.
*/
const drive = (): Promise<void> => {
if (driving) return driverRun
driving = true
driverRun = (async () => {
try {
for (;;) {
// Create the wakeup promise before inspecting state so a settle or submission arriving
// between the checks and the await below cannot be lost.
const signal = new Promise<void>((resolve) => { wake = resolve })
const commitHead = commitQueue[0]
if (commitHead !== undefined && commitHead.settled) {
commitQueue.shift()
await commitHead.commit()
// The barrier covers post-execute: later starts wait for the
// exclusive call's full pipeline, as under the native loop.
if (commitHead.mode === 'exclusive') exclusiveActive = false
continue
}
const head = pendingQueue[0]
if (head !== undefined) {
if (runController.signal.aborted) {
pendingQueue.shift()
head.abandon()
continue
}
// Reclassify at start time (fail-closed on registry changes).
const mode = head.classify()
const capacity = !exclusiveActive
&& (mode === 'exclusive' ? inFlight.size === 0 : inFlight.size < maxParallel)
if (capacity) {
if (mode === 'exclusive') exclusiveActive = true
head.mode = mode
pendingQueue.shift()
// Joined before start() so the commit cursor sees submission
// order; nothing commits it until `settled` flips.
commitQueue.push(head)
await head.start()
const flight: Promise<void> = head.flight.finally(() => {
inFlight.delete(flight)
wakeup()
})
inFlight.add(flight)
continue
}
}
if (pendingQueue.length === 0 && commitQueue.length === 0 && inFlight.size === 0) return
await signal
}
} finally {
driving = false
wake = undefined
}
})()
return driverRun
}
/** Every dispatch settled AND committed; nothing can start (the run is aborted at call time). */
const drainDispatches = async (): Promise<void> => {
// The abort already fired: the driver abandons queued-unstarted
// entries, awaits the live pool, and drains the ordered commit lane —
// including a commit already in progress when the program returned.
await drive()
// Every settle event is appended inside the open run_code turn
// (tasks self-remove on settlement).
while (logWork.size > 0) await Promise.allSettled([...logWork])
}
// Read through a call, not a bare property: the abort state genuinely
// changes across awaits, and a direct `.aborted` re-check after one
// would be narrowed away by control flow analysis.
const runOver = (): boolean => runController.signal.aborted
const binding = (schema: ToolSchema): PtcBindingFunction => async (rawArgs: unknown): Promise<JsonValue> => {
const { name } = schema
if (runOver()) {
throw new Error(`run_code run is over (${String(runController.signal.reason)}); ${name} not dispatched`)
}
const normalized = jsonNormalizeArgs(rawArgs)
const n = ++dispatches
const subCallId = brandString<ToolCallId>(`${String(exec.callId)}:ptc:${n}`)
const input = {
callId: subCallId,
rootCallId: exec.rootCallId,
name,
schema,
arguments: normalized.dispatched,
...exec.agent ? { agent: exec.agent } : {},
...exec.toolSet !== undefined ? { toolSet: exec.toolSet } : {},
parent: exec.token,
signal: runController.signal,
}
type DispatchOutcome = { isError: true; message: string } | { isError: false; value: JsonValue }
const outcome = await new Promise<DispatchOutcome>((resolve, reject) => {
// Set by the dispatch stage (or start() for a pre-settled result): what commit() finalizes in submission order.
let parked:
| { kind: 'post-result' | 'final-result'; exec: ToolRunContext; result: ToolExecutionResult }
| undefined
const settle = (result: ToolExecutionResult): void => {
// The program gets its value NOW: the log-content listener (for
// example, a spill backend) must never delay the binding or occupy
// a dispatch slot. The event append is tracked side work; the run's
// settlement drains logWork so every settle event is still appended
// inside the open turn (shapeDispatchLog is contained, so this
// chain cannot reject).
resolve(result.isError
? { isError: true, message: result.error.message }
: { isError: false, value: result.value })
const agent = exec.agent
if (agent === undefined) return
const task: Promise<void> = (async () => {
// The listener may replace the durable copy with a preview and
// locator; the program's value and model-visible result are
// untouched.
const logged = await shapeDispatchLog({
exec, agent, subCallId, name, isError: result.isError,
// The registry deep-froze this projection at result
// finalization; append snapshots the final copy again, so
// the log stays detached.
content: result.content,
})
agent.session.append('tool/ptc-dispatch', {
rootCallId: exec.rootCallId,
parentCallId: exec.callId,
subCallId,
name,
// The SIBLING parse of the dispatched value: byte-identical JSON,
// but a separate object — a tool mutating its args cannot desync
// this record from what it actually received.
arguments: normalized.logged,
isError: result.isError,
...result.error?.info === undefined ? {} : { error: result.error.info },
content: logged,
...result.meta === undefined ? {} : { meta: result.meta },
})
})().finally(() => { logWork.delete(task) })
logWork.add(task)
}
pendingQueue.push({
flight: Promise.resolve(),
settled: false,
// Re-read per driver pass against the same agent view the SDK
// declared; fail-closed exclusive when undeclared/invalid.
classify: () => executor.executionMode(input).kind,
abandon: () => {
reject(new Error(`run_code run is over (${String(runController.signal.reason)}); ${name} tool call abandoned`))
},
async start(): Promise<void> {
exec.agent?.session.append('tool/ptc-dispatch-start', {
rootCallId: exec.rootCallId,
parentCallId: exec.callId,
subCallId,
name,
arguments: normalized.logged,
})
// Ordered prepare runs INSIDE the driver lane: the next entry's
// pre-execute waits for this resolution, as under the native
// scheduler. Only the launched body below overlaps.
const prepared = await executor.prepare(input)
if (prepared.kind === 'dispatch') {
this.flight = executor.dispatch(prepared.exec).then((dispatchOutcome) => {
parked = { kind: dispatchOutcome.kind, exec: prepared.exec, result: dispatchOutcome.result }
this.settled = true
})
return
}
parked = { kind: prepared.kind, exec: prepared.exec, result: prepared.result }
this.settled = true
},
async commit(): Promise<void> {
/* v8 ignore next -- commit() runs only after `settled` flipped, which set parked. */
if (parked === undefined) return
const result = await executor.complete(parked.exec, parked)
if (!result.isError && result.content.some(block => block.type === 'image')) {
exec.deferContext(createUserMessage({
content: result.content,
source: { kind: 'ptc-mode' },
}))
}
for (const context of result.additionalContexts ?? []) {
exec.deferContext(context)
}
// The composite forwards `additionalContexts` above and
// `concludesTurn` here from the nested result. Only a successful
// nested result can carry the terminal marker
// (ToolExecutionFailure types it never), so a policy-converted
// failure cannot stop the turn through a recovering program.
if (result.concludesTurn) exec.concludeTurn()
settle(result)
// Backpressure on pending event-append tasks: each task retains
// a full result while a slow backend stores it, so the pool cap
// bounds their count. Beyond the cap, the
// ordered lane waits, so later sub-calls cannot start and
// pending I/O/memory cannot grow without bound.
while (logWork.size > maxParallel) await Promise.race(logWork)
},
})
wakeup()
void drive()
})
// A budget expiry or outer cancel that occurs while this call was in
// flight already aborted the dispatch; stop the program now rather
// than hand it a result from a run that is over.
if (runOver()) {
throw new Error(`run_code run is over (${String(runController.signal.reason)}); ${name} result discarded`)
}
// The worker turns a binding rejection into ToolCallError and adds
// only the binding name. Native content and internal error metadata
// stay outside the program-facing failure contract.
if (outcome.isError) throw new Error(outcome.message)
return outcome.value
}
// Null-prototype + defineProperty, mirroring the worker-side namespace
// build: a registered tool named `__proto__` must become an ordinary
// own key (a plain-object assignment would hit the prototype setter,
// silently dropping the binding), and the runtime host resolves
// binding names as own properties only.
const functions: Record<string, PtcBindingFunction> = Object.create(null) as Record<string, PtcBindingFunction>
// Enumerate the CALLING AGENT's visible set (scoped tools join,
// restricted globals vanish) — the same view the SDK section declared,
// so a program can bind exactly what its prompt promised; sub-dispatch
// re-resolves per call through the same view (exec.agent threads down).
for (const schema of tools.schemas(exec.agent, exec.toolSet)) {
Object.defineProperty(functions, schema.name, { enumerable: true, value: binding(deepFreeze(schema)) })
}
try {
let result: PtcRunResult
try {
if (exec.agent !== undefined) {
const agent = exec.agent
let runtimes = stateOwners.get(agent)
if (runtimes === undefined) stateOwners.set(agent, runtimes = new WeakSet())
// Cordis can wrap service returns repeatedly; cleanup belongs to the original provider.
let provider: PtcRuntime = runtime
for (;;) {
const original = (provider as PtcRuntime & { [symbols.original]?: PtcRuntime })[symbols.original]
if (original === undefined) break
provider = original
}
if (!runtimes.has(provider)) {
agent.ctx.effect(() => () => runtime.release(agent), 'PTC retained environment')
runtimes.add(provider)
}
}
result = await runtime.run(runtime.resolve({
program: args.code,
...exec.agent === undefined || policy?.mode !== standingPolicy?.mode ? {} : { stateOwner: exec.agent },
bindings: [{
global: 'tools',
functions,
errorClass: { name: 'ToolCallError', memberNameProperty: 'toolName' },
}],
signal: runController.signal,
...cwd !== undefined ? { cwd } : {},
...policy !== undefined ? { sandboxPolicy: policy } : {},
...args.timeoutMs !== undefined ? { timeoutMs: args.timeoutMs } : {},
}))
} finally {
// Abort sub-dispatches and drain every in-flight dispatch before
// closing the turn (queued-unstarted ones are abandoned unlogged).
// Binding failures remain observable through their individual promises.
runController.abort('run_code settled')
await drainDispatches()
}
if (result.error) {
if (result.error.kind === 'abort' && exec.signal.aborted) {
const error = new HarnessError('tool call aborted', TOOL_ABORTED)
error.name = 'AbortError'
throw error
}
const logsText = result.logs.length > 0 ? `\nCaptured output:\n${result.logs.join('\n')}` : ''
const sandboxText = result.sandbox === undefined ? ''
: `\nFile sandbox: ${result.sandbox.mode}${result.sandbox.enforcement === undefined ? '' : `; enforcement: ${result.sandbox.enforcement}`}${result.sandbox.denied ? '; operation denied' : ''}.`
throw new CodeRunFailedError(`code run failed (${result.error.kind}): ${result.error.message}${logsText}${sandboxText}${result.sandbox?.denied ? escalationGuidance(runtime.sandboxMode) : ''}`)
}
return {
logs: result.logs,
...result.sandbox === undefined ? {} : { sandbox: result.sandbox },
...result.value !== undefined ? { result: result.value } : {},
}
} finally {
callSignal.removeEventListener('abort', onOuterAbort)
}
}
return runtime.sandboxMode === undefined || exec.agent === undefined ? run(exec.signal)
: calls.run(exec.agent.session.id, exec.signal, () => resolveSandboxPolicy(ctx, exec).mode, run)
},
// The model-authored description is the call's always-visible UI label
// (the bash `description` precedent); the program itself rides rawInput.
presentCall: args => ({
card: 'generic',
title: args.description,
kind: 'execute',
rawInput: args.code,
}),
// Deliberately no presentResult: the generic card fallback keeps this
// title and reads durable result content without duplicating a large raw
// result into the host view payload.
})
// Read current runtime instructions and controls whenever the schema is projected.
Object.defineProperty(definition, 'description', {
enumerable: true,
get: () => {
const runtime = requirePtcRuntime(ctx)
const instructions = runtime.executionInstructions
return resolveFlavor(runtime).description
+ (instructions ? ` ${instructions}` : '')
+ " Each program starts in the Session's current directory. Running programs keep their initial directory."
+ escalationGuidance(runtime.sandboxMode)
},
})
Object.defineProperty(definition, 'parameters', {
enumerable: true,
// Recompile through the same spec→schema projection defineTool used, so
// the emitted schema always matches the validated specification.
get: () => {
const runtime = requirePtcRuntime(ctx)
return parameterSchemaSpecToJsonSchema({
code: { type: 'string', required: true, description: resolveFlavor(runtime).codeDescription },
description: { type: 'string', required: true, description: RUN_CODE_DESCRIPTION_PARAM_DESCRIPTION },
...controlParameters(runtime),
})
},
})
return definition
}