@yolk-sdk/codemode 0.1.0-canary.106 → 0.1.0-canary.108
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +20 -2
- package/dist/catalog.d.mts +69 -9
- package/dist/catalog.d.mts.map +1 -1
- package/dist/catalog.mjs +168 -19
- package/dist/catalog.mjs.map +1 -1
- package/dist/classifier-tool.mjs +1 -1
- package/dist/executor.d.mts +10 -1
- package/dist/executor.d.mts.map +1 -1
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +2 -2
- package/dist/node.d.mts.map +1 -1
- package/dist/node.mjs +12 -5
- package/dist/node.mjs.map +1 -1
- package/dist/search.d.mts +11 -6
- package/dist/search.d.mts.map +1 -1
- package/dist/search.mjs +23 -45
- package/dist/search.mjs.map +1 -1
- package/dist/tool.d.mts +9 -0
- package/dist/tool.d.mts.map +1 -1
- package/dist/tool.mjs +72 -21
- package/dist/tool.mjs.map +1 -1
- package/package.json +2 -2
- package/src/catalog.ts +241 -22
- package/src/executor.ts +6 -0
- package/src/index.ts +8 -1
- package/src/node.ts +22 -9
- package/src/search.ts +31 -85
- package/src/tool.ts +112 -16
package/dist/tool.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tool.mjs","names":[],"sources":["../src/tool.ts"],"sourcesContent":["import {\n Cause,\n Clock,\n Data,\n Duration,\n Effect,\n Exit,\n Fiber,\n FiberSet,\n Option,\n Predicate,\n Result,\n Scope\n} from 'effect'\nimport * as Schema from 'effect/Schema'\nimport { ToolError } from '@yolk-sdk/agent/loop'\nimport {\n boundNestedToolCallArgs,\n makeNestedToolCallRecorder,\n nestedToolCallMaxErrorChars,\n nestedToolCallRecordLimit,\n nestedToolCallResultFields,\n recordNestedToolCall,\n truncateCodePoints,\n contentPartText,\n ToolCall,\n ToolResult,\n type AgentUsage,\n type Content,\n type NestedToolCallInput,\n type NestedToolCallStatus\n} from '@yolk-sdk/agent/protocol'\nimport {\n makeTool,\n type NestedToolExecutor,\n type StagedCallReceipt,\n type ToolExecutionInput,\n type ToolLedgerAbandonedInput,\n type ToolLedgerEntry,\n type ToolPlanBuilder,\n type ToolPlanStageError,\n type ToolRegistration\n} from '@yolk-sdk/agent/tools'\nimport {\n codeModeCatalog,\n defaultCodeModeInlineBudget,\n describeCodeModeTool,\n findCodeModeTool,\n renderCodeModeDescription,\n type CodeModeCatalogTool\n} from './catalog.ts'\nimport type {\n CodeModeExecutionResult,\n CodeModeExecutor,\n CodeModeExecutorGlobal,\n CodeModeExecutorTool,\n CodeModeStore\n} from './executor.ts'\nimport {\n boundCodeModeSegments,\n codeModeResultSegments,\n codeModeSegmentsContent,\n defaultCodeModeMaxImageBytes,\n defaultCodeModeMaxImages\n} from './output.ts'\nimport { searchCodeModeTools } from './search.ts'\nimport type {\n CodeModeInterruptedCall,\n CodeModeInterruptedCalls,\n CodeModeInterruptedCallStatus,\n CodeModeStagedPlan,\n CodeModeStructuredContent\n} from './store.ts'\n\n/** Default name of the code mode tool. */\nexport const codeModeToolName = 'codemode'\n\n/** Host-owned limits of one script. */\nexport type CodeModeLimits = {\n /** Overall deadline including nested calls. Default 120000 (clamped by `deadline`). */\n readonly timeoutMs?: number\n /** VM heap cap. Default 64 MiB. */\n readonly memoryLimitBytes?: number\n /** Nested tool calls per script; further calls reject with an Error. Default 256. The\n * `nestedCalls` record keeps up to this many calls, at most `nestedToolCallMaxRecordedCalls`\n * (4096) whatever the limit (argument byte budgets still apply).\n */\n readonly maxNestedCalls?: number\n /** Model-visible result characters, cut head and tail with an omission marker. Default 40000. */\n readonly maxOutputChars?: number\n /** Images kept in the result, in order; later images are dropped with a note. Default 8. */\n readonly maxImages?: number\n /** Base64 characters of images kept in the result in total; an image that would exceed it is\n * dropped with a note. Default 4 MiB.\n */\n readonly maxImageBytes?: number\n}\n\nexport const defaultCodeModeLimits = {\n timeoutMs: 120_000,\n memoryLimitBytes: 64 * 1024 * 1024,\n maxNestedCalls: 256,\n maxOutputChars: 40_000,\n maxImages: defaultCodeModeMaxImages,\n maxImageBytes: defaultCodeModeMaxImageBytes\n} as const satisfies Required<CodeModeLimits>\n\n/** Kept between a host deadline and the script deadline, for result handling. */\nexport const codeModeDeadlineMarginMs = 5_000\n\n/** A deadline never clamps a script below this timeout. */\nexport const codeModeMinimumTimeoutMs = 1_000\n\nexport type MakeCodeModeToolOptions<Context> = {\n readonly executor: CodeModeExecutor\n /** Default `codemode`. */\n readonly name?: string\n /** Estimated tokens (four characters each) for listing nested tools. Default 3000. */\n readonly inlineBudget?: number\n readonly limits?: CodeModeLimits\n /**\n * Epoch milliseconds by which the tool call must end, for example the remaining function budget\n * of a Workflow step. The script timeout is clamped to it minus 5 s, but never below 1 s.\n */\n readonly deadline?: (context: Context) => number | undefined\n /**\n * The store scripts read with `load(key)`, usually rebuilt from the transcript with\n * `codeModeStoreFromToolResults`. Without it every script starts with an empty store.\n */\n readonly loadStore?: (context: Context) => Effect.Effect<CodeModeStore, ToolError>\n /**\n * Runs before each nested call executes, with the nested call (id `<toolCallId>/<seq>`) and the\n * host context. A failure rejects that call in the script with the message and records it as an\n * `error` without executing it. Host decorators around the `ToolExecutor` (outside\n * `ResolvedToolSet.execute`) never see nested calls: put per-call run-authority checks here or in\n * registration-level wrappers.\n */\n readonly beforeNestedCall?: (input: {\n readonly call: ToolCall\n readonly context: Context\n }) => Effect.Effect<void, string>\n /**\n * Runs after each nested call that `beforeNestedCall` admitted, with its outcome: `success` (a\n * result without `isError`), `failure` (an error result or an unexpected failure), or\n * `interrupted` (cancelled, for example still running when the script ended). `durationMs`\n * covers the execution; `result` is present when the call produced one. Calls rejected by\n * `beforeNestedCall` or the call limit never ran and are not reported. The script waits for the\n * hook before it sees the call settle. A failing hook (a failure, a defect, or a synchronous\n * throw) is logged and never changes the call's result.\n */\n readonly afterNestedCall?: (input: CodeModeAfterNestedCallInput<Context>) => Effect.Effect<void>\n /**\n * Staged tool plans (ADR 0005). When the resolution offers staging (`resolveTools(..., { plans,\n * interactionHost })` with a plan review tool), scripts get `stage(name, args)` for stageable\n * approval-gated tools. `false` turns it off for this tool; `maxCalls` may only lower the\n * resolution's cap.\n */\n readonly staging?: CodeModeStagingOptions | false\n}\n\nexport type CodeModeStagingOptions = {\n /** Staged calls per script; `stage` rejects past it. At most the resolution's `maxCalls`. */\n readonly maxCalls?: number\n}\n\n/** Outcome of a nested call for `afterNestedCall`. */\nexport type CodeModeNestedCallOutcome = 'success' | 'failure' | 'interrupted'\n\nexport type CodeModeAfterNestedCallInput<Context> = {\n readonly call: ToolCall\n readonly outcome: CodeModeNestedCallOutcome\n readonly durationMs: number\n readonly context: Context\n readonly result?: ToolResult\n}\n\nconst CodeModeParams = Schema.Struct({\n code: Schema.String.annotate({\n description:\n 'Raw JavaScript: the body of an async function. Top-level await and return work. Not a function declaration and not a module.'\n })\n})\n\ntype CodeModeParams = typeof CodeModeParams.Type\n\nclass CodeModeExecutorRejected extends Data.TaggedError('CodeModeExecutorRejected')<{\n readonly message: string\n}> {}\n\nconst errorMessage = (error: unknown) =>\n error instanceof Error ? error.message : Predicate.isString(error) ? error : 'unknown error'\n\nconst nestedText = (content: Content) =>\n Predicate.isString(content)\n ? content\n : content\n .map(contentPartText)\n .filter(text => text.length > 0)\n .join('\\n')\n\ntype NestedResolution =\n | { readonly ok: true; readonly value: unknown }\n | { readonly ok: false; readonly message: string }\n\n/** What a nested call resolves to: `structuredContent` for tools with an output schema (when\n * present), text content otherwise; error results reject with their text. A tool that declares an\n * output schema but returns no `structuredContent` resolves to its text: that is a tool bug, kept\n * readable rather than turned into a failure.\n */\nconst resolveNestedResult = (tool: CodeModeCatalogTool, result: ToolResult): NestedResolution => {\n const text = nestedText(result.content)\n\n if (result.isError === true) {\n return { ok: false, message: text.length > 0 ? text : `Tool ${tool.name} failed.` }\n }\n\n return tool.structured && result.structuredContent !== undefined\n ? { ok: true, value: result.structuredContent }\n : { ok: true, value: text }\n}\n\ntype CallOutcome = {\n status: NestedToolCallStatus\n durationMs: number\n error?: string\n usage?: AgentUsage\n}\n\ntype CallRecord = {\n readonly name: string\n readonly args: unknown\n readonly id: string\n outcome?: CallOutcome\n}\n\nconst recordedOutcome = (exit: Exit.Exit<ToolResult>, durationMs: number): CallOutcome => {\n if (Exit.isFailure(exit)) {\n return Cause.hasInterruptsOnly(exit.cause)\n ? { status: 'cancelled', durationMs }\n : { status: 'error', durationMs, error: 'The tool call failed unexpectedly.' }\n }\n\n const result = exit.value\n\n const outcome: CallOutcome =\n result.isError === true\n ? { status: 'error', durationMs, error: nestedText(result.content) }\n : { status: 'ok', durationMs }\n\n if (result.usage !== undefined) {\n outcome.usage = result.usage\n }\n\n return outcome\n}\n\nconst hookOutcome = (exit: Exit.Exit<ToolResult>): CodeModeNestedCallOutcome =>\n Exit.isSuccess(exit)\n ? exit.value.isError === true\n ? 'failure'\n : 'success'\n : Cause.hasInterruptsOnly(exit.cause)\n ? 'interrupted'\n : 'failure'\n\n/** What `describeNamespace(name)` resolves to. */\ntype CodeModeNamespaceDescription = {\n readonly name: string\n description?: string\n readonly tools: ReadonlyArray<{ readonly name: string; readonly description: string }>\n}\n\nconst SearchOptions = Schema.Struct({\n limit: Schema.optionalKey(Schema.Finite),\n namespace: Schema.optionalKey(Schema.String)\n})\n\nconst decodeSearchOptions = Schema.decodeUnknownOption(SearchOptions)\n\n/** `searchTools`, `describeTool`, and `describeNamespace` over every nested tool. */\nconst discoveryGlobals = (\n catalog: ReadonlyArray<CodeModeCatalogTool>\n): ReadonlyArray<CodeModeExecutorGlobal> => [\n {\n name: 'searchTools',\n spread: true,\n execute: args => {\n const [query, options] = Array.isArray(args) ? args : []\n\n return Promise.resolve(\n searchCodeModeTools(\n catalog,\n Predicate.isString(query) ? query : '',\n Option.getOrElse(decodeSearchOptions(options ?? {}), () => ({}))\n )\n )\n }\n },\n {\n name: 'describeTool',\n execute: name => {\n const tool = Predicate.isString(name) ? findCodeModeTool(catalog, name) : undefined\n\n return Promise.resolve(tool === undefined ? undefined : describeCodeModeTool(tool))\n }\n },\n {\n name: 'describeNamespace',\n execute: name => {\n const tools = catalog.filter(tool => tool.namespace === name)\n\n if (!Predicate.isString(name) || tools.length === 0) return Promise.resolve(undefined)\n\n const description = tools.find(\n tool => tool.namespaceDescription !== undefined\n )?.namespaceDescription\n\n const namespace: CodeModeNamespaceDescription = {\n name,\n tools: tools.map(tool => ({ name: tool.identifier, description: tool.description }))\n }\n\n if (description !== undefined) namespace.description = description\n\n return Promise.resolve(namespace)\n }\n }\n]\n\ntype StageFork = (\n effect: Effect.Effect<StagedCallReceipt, ToolPlanStageError>,\n options: { readonly signal: AbortSignal }\n) => Fiber.Fiber<StagedCallReceipt, ToolPlanStageError>\n\n/** `stage(name, args)`: records one call into the script's plan (never runs it). */\nconst stageGlobal = (plan: ToolPlanBuilder, fork: StageFork): CodeModeExecutorGlobal => ({\n name: 'stage',\n spread: true,\n signature: '(name: string, args: object): Promise<{ staged: true; key: string; index: number }>',\n description:\n 'Record a call of a stageable tool into this script’s plan for a person to review; it does not run.',\n execute: (args, { signal }) => {\n const [name, params] = Array.isArray(args) ? args : []\n\n if (signal.aborted) return Promise.reject(new Error('stage was cancelled.'))\n\n if (!Predicate.isString(name))\n return Promise.reject(new Error('stage: the first argument must be a tool name.'))\n\n return new Promise((resolve, reject) => {\n fork(plan.stage({ name, params: params === undefined ? {} : params }), {\n signal\n }).addObserver(exit => {\n if (Exit.isSuccess(exit)) {\n resolve(exit.value)\n\n return\n }\n\n const error = Cause.findErrorOption(exit.cause)\n\n reject(\n new Error(\n Option.isSome(error)\n ? `stage: ${error.value.message}`\n : Cause.hasInterruptsOnly(exit.cause)\n ? 'stage was cancelled.'\n : 'stage failed unexpectedly.'\n )\n )\n })\n })\n }\n})\n\nconst planNotice = (plan: CodeModeStagedPlan) =>\n `\\n\\nStaged ${plan.count} call${plan.count === 1 ? '' : 's'} as plan ${plan.id}; nothing was applied yet. To apply them, call ${plan.reviewToolName}(${JSON.stringify({ planId: plan.id, planDigest: plan.digest })}): a person reviews the plan and selects the calls to apply.`\n\nconst hasStoreWrites = (result: CodeModeExecutionResult) =>\n result.storeWrites !== undefined &&\n (Object.keys(result.storeWrites.set).length > 0 || result.storeWrites.delete.length > 0)\n\nconst timeoutFor = (limit: number, deadline: number | undefined, now: number) =>\n deadline === undefined\n ? limit\n : Math.max(codeModeMinimumTimeoutMs, Math.min(limit, deadline - now - codeModeDeadlineMarginMs))\n\n/**\n * Waits at most `codeModeDeadlineMarginMs` for an effect (interrupting nested fibers) to finish; it\n * keeps running detached when a nested call does not respond to interruption in time.\n */\nconst boundedWait = (effect: Effect.Effect<void>) =>\n Effect.forkDetach(effect).pipe(\n Effect.flatMap(fiber =>\n Fiber.await(fiber).pipe(\n Effect.timeoutOption(Duration.millis(codeModeDeadlineMarginMs)),\n Effect.interruptible\n )\n ),\n Effect.asVoid\n )\n\ntype RunInput<Context> = {\n readonly options: MakeCodeModeToolOptions<Context>\n readonly limits: Required<CodeModeLimits>\n readonly call: ToolCall\n readonly context: Context\n readonly code: string\n readonly nested: NestedToolExecutor\n}\n\nconst runScript = <Context>(input: RunInput<Context>): Effect.Effect<ToolResult, ToolError> =>\n Effect.gen(function* () {\n const { call, limits, nested, options } = input\n const store = options.loadStore === undefined ? {} : yield* options.loadStore(input.context)\n const startedAt = yield* Clock.currentTimeMillis\n const timeoutMs = timeoutFor(limits.timeoutMs, options.deadline?.(input.context), startedAt)\n const catalog = codeModeCatalog(nested.tools)\n\n return yield* Effect.acquireUseRelease(\n Scope.make(),\n scope =>\n Effect.gen(function* () {\n // Nested calls run as fibers of this tool call (same services, interrupted with it).\n const fibers = yield* FiberSet.make<ToolResult>().pipe(Scope.provide(scope))\n const runFork = yield* FiberSet.runtime(fibers)<never>()\n const records: Array<CallRecord> = []\n\n // Staged tool plans: only when the resolution offers staging and the host keeps it on.\n const stagingOptions = options.staging === false ? undefined : options.staging\n const staging = options.staging === false ? undefined : nested.staging\n const maxStagedCalls = stagingOptions?.maxCalls\n\n const plan =\n staging === undefined\n ? undefined\n : staging.begin(\n maxStagedCalls === undefined ? undefined : { maxCalls: maxStagedCalls }\n )\n\n const stageFibers = yield* FiberSet.make<StagedCallReceipt, ToolPlanStageError>().pipe(\n Scope.provide(scope)\n )\n\n const stageFork = yield* FiberSet.runtime(stageFibers)<never>()\n\n const callTool =\n (tool: CodeModeCatalogTool): CodeModeExecutorTool['execute'] =>\n (args, { signal }) => {\n if (signal.aborted) {\n return Promise.reject(new Error(`${tool.callLabel} was cancelled.`))\n }\n\n if (records.length >= limits.maxNestedCalls) {\n return Promise.reject(\n new Error(\n `${tool.callLabel}: Nested call limit reached: a script may make at most ${limits.maxNestedCalls} tool calls. Batch the work or return partial results.`\n )\n )\n }\n\n const params = args === undefined ? {} : args\n\n const record: CallRecord = {\n id: `${call.id}/${records.length + 1}`,\n name: tool.name,\n args: params\n }\n\n records.push(record)\n\n const nestedCall = ToolCall.make({ id: record.id, name: tool.name, params })\n const afterNestedCall = options.afterNestedCall\n\n const run =\n afterNestedCall === undefined\n ? nested.execute(nestedCall)\n : Effect.gen(function* () {\n const started = yield* Clock.currentTimeMillis\n\n // Host observability never changes the call: a hook failure, defects and\n // synchronous throws included, is only logged.\n const afterNestedCallSafely = (\n hookInput: CodeModeAfterNestedCallInput<Context>\n ) =>\n Effect.suspend(() => afterNestedCall(hookInput)).pipe(\n Effect.exit,\n Effect.flatMap(hookExit =>\n Exit.isSuccess(hookExit)\n ? Effect.void\n : Effect.logWarning(\n `Code mode afterNestedCall failed for ${nestedCall.id}; ignored: ${Cause.pretty(hookExit.cause)}`\n )\n )\n )\n\n return yield* nested.execute(nestedCall).pipe(\n Effect.onExit(exit =>\n Effect.flatMap(Clock.currentTimeMillis, finished =>\n afterNestedCallSafely(\n Exit.isSuccess(exit)\n ? {\n call: nestedCall,\n outcome: hookOutcome(exit),\n durationMs: finished - started,\n context: input.context,\n result: exit.value\n }\n : {\n call: nestedCall,\n outcome: hookOutcome(exit),\n durationMs: finished - started,\n context: input.context\n }\n )\n )\n )\n )\n })\n\n // The plan's ordering guardrail: once a script stages, only read tools run.\n const guarded =\n plan === undefined\n ? run\n : plan.admit(nestedCall).pipe(\n Effect.matchEffect({\n onFailure: error =>\n Effect.succeed(\n ToolResult.make({\n toolCallId: nestedCall.id,\n content: error.message,\n isError: true\n })\n ),\n onSuccess: () => run\n })\n )\n\n const admitted =\n options.beforeNestedCall === undefined\n ? guarded\n : options.beforeNestedCall({ call: nestedCall, context: input.context }).pipe(\n Effect.matchEffect({\n onFailure: message =>\n Effect.succeed(\n ToolResult.make({\n toolCallId: nestedCall.id,\n content: message,\n isError: true\n })\n ),\n onSuccess: () => guarded\n })\n )\n\n const execution = Effect.gen(function* () {\n const started = yield* Clock.currentTimeMillis\n\n return yield* admitted.pipe(\n Effect.onExit(exit =>\n Effect.map(Clock.currentTimeMillis, finished => {\n record.outcome = recordedOutcome(exit, finished - started)\n })\n )\n )\n })\n\n return new Promise((resolve, reject) => {\n runFork(execution, { signal }).addObserver(exit => {\n if (Exit.isFailure(exit)) {\n reject(\n new Error(\n Cause.hasInterruptsOnly(exit.cause)\n ? `${tool.callLabel} was cancelled.`\n : `${tool.callLabel} failed unexpectedly.`\n )\n )\n\n return\n }\n\n const resolution = resolveNestedResult(tool, exit.value)\n\n if (resolution.ok) {\n resolve(resolution.value)\n } else {\n // The script sees which call failed; the nested-call record keeps the raw text.\n reject(new Error(`${tool.callLabel}: ${resolution.message}`))\n }\n })\n })\n }\n\n const tools: ReadonlyArray<CodeModeExecutorTool> = catalog.map(tool => ({\n name: tool.name,\n description: tool.description,\n inputSchema: tool.inputSchema,\n outputSchema: tool.outputSchema,\n callLabel: tool.callLabel,\n execute: callTool(tool)\n }))\n\n const result = yield* Effect.tryPromise({\n try: signal =>\n options.executor.execute(input.code, {\n tools,\n globals:\n plan === undefined\n ? discoveryGlobals(catalog)\n : [...discoveryGlobals(catalog), stageGlobal(plan, stageFork)],\n timeoutMs,\n memoryLimitBytes: limits.memoryLimitBytes,\n store,\n signal\n }),\n catch: error => new CodeModeExecutorRejected({ message: errorMessage(error) })\n }).pipe(\n Effect.catch(error =>\n Effect.succeed<CodeModeExecutionResult>({\n ok: false,\n error: {\n kind: 'sandbox',\n message: `The code mode executor failed: ${error.message}`\n },\n output: []\n })\n ),\n // Backstop for executors that miss their own deadline: interrupting aborts the signal.\n Effect.timeoutOption(Duration.millis(timeoutMs + codeModeDeadlineMarginMs)),\n Effect.map(\n Option.getOrElse((): CodeModeExecutionResult => ({\n ok: false,\n error: {\n kind: 'timeout',\n message: `Execution timed out after ${timeoutMs} ms (the executor did not stop in time)`\n },\n output: []\n }))\n )\n )\n\n // Calls still running when the script ended are cancelled and recorded as such.\n yield* boundedWait(FiberSet.clear(fibers))\n yield* boundedWait(FiberSet.clear(stageFibers))\n\n const finishedAt = yield* Clock.currentTimeMillis\n\n // A successful script's staged calls are saved once as a plan; a failed script's are\n // discarded (a partial plan is never offered for review). Nothing staged ever ran.\n const stagedCount = plan === undefined ? 0 : (yield* plan.staged).length\n\n const saved =\n plan === undefined || stagedCount === 0 || !result.ok\n ? undefined\n : yield* plan.finish.pipe(Effect.result)\n\n const savedPlan: CodeModeStagedPlan | undefined =\n staging === undefined ||\n saved === undefined ||\n Result.isFailure(saved) ||\n saved.success === undefined\n ? undefined\n : {\n id: saved.success.id,\n digest: saved.success.digest,\n count: saved.success.calls.length,\n reviewToolName: staging.reviewToolName\n }\n\n const planFailure =\n saved !== undefined && Result.isFailure(saved) ? saved.failure : undefined\n\n const planFailed = planFailure !== undefined\n const plural = stagedCount === 1 ? '' : 's'\n\n const stagingNotice =\n stagedCount === 0\n ? undefined\n : !result.ok\n ? `\\n\\nThe script failed, so its ${stagedCount} staged call${plural} ${stagedCount === 1 ? 'was' : 'were'} discarded; nothing was applied.`\n : planFailure !== undefined\n ? `\\n\\nThe script staged ${stagedCount} call${plural}, but the plan could not be saved (${planFailure.message}); nothing was applied. Run the script again.`\n : savedPlan === undefined\n ? undefined\n : planNotice(savedPlan)\n\n const recorded = records.map((record): NestedToolCallInput => ({\n id: record.id,\n name: record.name,\n args: record.args,\n // A call interrupted before it started has no outcome.\n ...(record.outcome ?? { status: 'cancelled' })\n }))\n\n const recorder = recorded.reduce(\n recordNestedToolCall,\n makeNestedToolCallRecorder({ maxCalls: limits.maxNestedCalls })\n )\n\n const { nestedCalls, usage } = nestedToolCallResultFields(recorder)\n\n const segments = codeModeResultSegments({\n result,\n wallTimeMs: finishedAt - startedAt,\n calls: recorded,\n maxChars: limits.maxOutputChars,\n maxImages: limits.maxImages,\n maxImageBytes: limits.maxImageBytes\n })\n\n // The staging notice follows the bounded output so a cut never hides the plan.\n const content = codeModeSegmentsContent(\n stagingNotice === undefined\n ? segments\n : [...segments, { type: 'text', text: stagingNotice }]\n )\n\n const ok = result.ok && !planFailed\n\n const codemode: CodeModeStructuredContent['codemode'] =\n ok && result.storeWrites !== undefined && hasStoreWrites(result)\n ? { ok: true, storeWrites: result.storeWrites }\n : { ok }\n\n const structuredContent: CodeModeStructuredContent = {\n codemode: savedPlan === undefined ? codemode : { ...codemode, plan: savedPlan }\n }\n\n type ResultFields = {\n toolCallId: string\n content: Content\n isError?: boolean\n structuredContent: CodeModeStructuredContent\n nestedCalls: typeof nestedCalls\n usage?: AgentUsage\n }\n\n const fields: ResultFields = {\n toolCallId: call.id,\n content,\n structuredContent,\n nestedCalls\n }\n\n if (!ok) {\n fields.isError = true\n }\n\n if (usage !== undefined) {\n fields.usage = usage\n }\n\n return ToolResult.make(fields)\n }),\n (scope, exit) => boundedWait(Scope.close(scope, exit))\n )\n })\n\nconst argsPreviewChars = 200\n\nconst interruptedCallStatus = (entry: ToolLedgerEntry): CodeModeInterruptedCallStatus => {\n const outcome = entry.outcome\n\n if (outcome === undefined) return 'unknown'\n\n if (Predicate.isTagged(outcome, 'Failed')) return 'failed'\n\n return outcome.result.isError === true ? 'failed' : 'applied'\n}\n\nconst nestedEntryState = (entry: ToolLedgerEntry) => {\n const outcome = entry.outcome\n\n if (outcome === undefined) {\n return 'unknown: it started but never recorded a result, so it may have been applied'\n }\n\n if (Predicate.isTagged(outcome, 'Failed'))\n return `completed with an error: ${truncateCodePoints(outcome.error.message, nestedToolCallMaxErrorChars)}`\n\n return outcome.result.isError === true ? 'completed with an error result' : 'applied'\n}\n\n/** The bounded, wire-safe `interruptedCalls` of an abandoned execution's nested ledger entries:\n * the `nestedCalls` record bounds (`maxCalls`, 8 KiB of arguments per call, 32 KiB in total).\n */\nconst interruptedCalls = (\n nested: ReadonlyArray<ToolLedgerEntry>,\n maxCalls: number\n): CodeModeInterruptedCalls => {\n const counts = { applied: 0, failed: 0, unknown: 0 }\n const calls: Array<CodeModeInterruptedCall> = []\n let argsBytes = 0\n let complete = true\n\n for (const entry of nested) {\n const status = interruptedCallStatus(entry)\n\n counts[status]++\n\n if (calls.length >= maxCalls) {\n complete = false\n continue\n }\n\n const { args, bytes, truncated } = boundNestedToolCallArgs(entry.args, argsBytes)\n\n // `argsTruncated`: the ledger already cut the arguments when the call was claimed.\n if (truncated || entry.argsTruncated) complete = false\n\n argsBytes += bytes\n calls.push({ key: entry.key, toolName: entry.toolName, args, status })\n }\n\n return { calls, complete, counts }\n}\n\n/**\n * The result of a code mode call whose earlier execution was abandoned (claimed, lease expired,\n * no result recorded). The script never runs again: the result lists that execution's ledgered\n * nested calls as applied, failed, or unknown, and says they were not undone. The text is bounded\n * by `maxOutputChars`; `structuredContent.codemode.interruptedCalls` carries the same entries for\n * hosts, bounded like `nestedCalls`. When the ledger cannot list them, the result stays\n * interrupted, says they may have been applied, and sets `interruptedCallsUnavailable` instead.\n */\nconst abandonedScriptResult =\n (limits: Required<CodeModeLimits>) =>\n ({ call, nested }: ToolLedgerAbandonedInput): ToolResult => {\n const maxChars = limits.maxOutputChars\n\n const listing =\n nested === undefined\n ? 'Its ledgered nested tool calls could not be listed (the tool ledger is unavailable), so any of them may already have been applied.'\n : nested.length === 0\n ? 'No ledgered nested tool calls were recorded for that execution.'\n : [\n `Ledgered nested tool calls of that execution (they were not undone):`,\n ...nested.map(\n entry =>\n `- ${entry.key} ${entry.toolName} ${truncateCodePoints(entry.args, argsPreviewChars)}: ${nestedEntryState(entry)}`\n )\n ].join('\\n')\n\n const text = [\n `Script interrupted: an earlier execution of this ${call.name} call (${call.id}) started but never recorded a result. The script was not run again.`,\n listing,\n 'Calls the ledger policy skips (by default read-only calls) are not listed. Verify the state these calls affect before repeating any work.'\n ].join('\\n\\n')\n\n const structuredContent: CodeModeStructuredContent = {\n codemode:\n nested === undefined\n ? { ok: false, interrupted: true, interruptedCallsUnavailable: true }\n : {\n ok: false,\n interrupted: true,\n interruptedCalls: interruptedCalls(\n nested,\n nestedToolCallRecordLimit(limits.maxNestedCalls)\n )\n }\n }\n\n return ToolResult.make({\n toolCallId: call.id,\n content: codeModeSegmentsContent(boundCodeModeSegments([{ type: 'text', text }], maxChars)),\n isError: true,\n structuredContent\n })\n }\n\n/**\n * The code mode tool: one registration (default name `codemode`, input `{ code }`) whose scripts\n * call the other code-mode-callable tools of the same `resolveTools` resolution. Nested calls run\n * through the resolved execute path with the same host context (input decoding, enablement,\n * registration wrappers), get ids `<toolCallId>/<seq>`, and are recorded on the result's\n * `nestedCalls`. The resolved description lists the callable tools (see\n * `renderCodeModeDescription`).\n *\n * Access is `write`: scripts can call any write tool the resolution exposes to code mode; each\n * nested call keeps its own access metadata and host wrappers.\n *\n * With a tool ledger (`resolveTools(..., { ledger })`) the code mode call itself is ledgered, so a\n * re-executed call never runs its script again: a completed call returns its stored result, an\n * in-flight one is awaited, and an abandoned one returns an interrupted result listing its\n * ledgered nested calls. Nested write calls are ledgered under `<toolCallId>/<seq>`.\n */\nexport const makeCodeModeTool = <Context>(\n options: MakeCodeModeToolOptions<Context>\n): ToolRegistration<Context> => {\n const name = options.name ?? codeModeToolName\n const limits: Required<CodeModeLimits> = { ...defaultCodeModeLimits, ...options.limits }\n const inlineBudget = options.inlineBudget ?? defaultCodeModeInlineBudget\n const store = options.loadStore !== undefined\n\n return makeTool<Context, typeof CodeModeParams>({\n name,\n description: renderCodeModeDescription({ tools: [], inlineBudget, store }),\n parameters: CodeModeParams,\n access: 'write',\n nestedToolAccess: true,\n describe: ({ tools, staging }) =>\n renderCodeModeDescription(\n staging === undefined || options.staging === false\n ? { tools, inlineBudget, store }\n : { tools, inlineBudget, store, staging }\n ),\n abandonedResult: abandonedScriptResult(limits),\n execute: ({\n call,\n context,\n params,\n nested\n }: ToolExecutionInput<Context> & { readonly params: CodeModeParams }) =>\n nested === undefined\n ? Effect.fail(\n new ToolError({\n tool: name,\n cause: 'unavailable',\n message: `${name} requires nested tool access; resolve it with resolveTools.`\n })\n )\n : runScript({ options, limits, call, context, code: params.code, nested })\n })\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"tool.mjs","names":[],"sources":["../src/tool.ts"],"sourcesContent":["import {\n Cause,\n Clock,\n Data,\n Duration,\n Effect,\n Exit,\n Fiber,\n FiberSet,\n Option,\n Predicate,\n Result,\n Scope\n} from 'effect'\nimport * as Schema from 'effect/Schema'\nimport { ToolError } from '@yolk-sdk/agent/loop'\nimport {\n boundNestedToolCallArgs,\n makeNestedToolCallRecorder,\n nestedToolCallMaxErrorChars,\n nestedToolCallRecordLimit,\n nestedToolCallResultFields,\n recordNestedToolCall,\n truncateCodePoints,\n contentPartText,\n ToolCall,\n ToolResult,\n type AgentUsage,\n type Content,\n type NestedToolCallInput,\n type NestedToolCallStatus\n} from '@yolk-sdk/agent/protocol'\nimport { toCodemodeIdentifier } from '@earendil-works/pi-codemode/declarations'\nimport {\n makeTool,\n type NestedTool,\n type NestedToolExecutor,\n type NestedToolInstructor,\n type StagedCallReceipt,\n type ToolExecutionInput,\n type ToolLedgerAbandonedInput,\n type ToolLedgerEntry,\n type ToolPlanBuilder,\n type ToolPlanStageError,\n type ToolRegistration\n} from '@yolk-sdk/agent/tools'\nimport {\n codeModeCatalog,\n codeModeCatalogInstruction,\n codeModeStageOnlyCatalog,\n defaultCodeModeInlineBudget,\n describeCodeModeTool,\n findCodeModeTool,\n renderCodeModeDescription,\n renderCodeModeInstructedDescription,\n type CodeModeCatalogTool,\n type CodeModeStagingDescription\n} from './catalog.ts'\nimport type {\n CodeModeExecutionResult,\n CodeModeExecutor,\n CodeModeExecutorGlobal,\n CodeModeExecutorTool,\n CodeModeStore\n} from './executor.ts'\nimport {\n boundCodeModeSegments,\n codeModeResultSegments,\n codeModeSegmentsContent,\n defaultCodeModeMaxImageBytes,\n defaultCodeModeMaxImages\n} from './output.ts'\nimport { searchCodeModeTools } from './search.ts'\nimport type {\n CodeModeInterruptedCall,\n CodeModeInterruptedCalls,\n CodeModeInterruptedCallStatus,\n CodeModeStagedPlan,\n CodeModeStructuredContent\n} from './store.ts'\n\n/** Default name of the code mode tool. */\nexport const codeModeToolName = 'codemode'\n\n/** Host-owned limits of one script. */\nexport type CodeModeLimits = {\n /** Overall deadline including nested calls. Default 120000 (clamped by `deadline`). */\n readonly timeoutMs?: number\n /** VM heap cap. Default 64 MiB. */\n readonly memoryLimitBytes?: number\n /** Nested tool calls per script; further calls reject with an Error. Default 256. The\n * `nestedCalls` record keeps up to this many calls, at most `nestedToolCallMaxRecordedCalls`\n * (4096) whatever the limit (argument byte budgets still apply).\n */\n readonly maxNestedCalls?: number\n /** Model-visible result characters, cut head and tail with an omission marker. Default 40000. */\n readonly maxOutputChars?: number\n /** Images kept in the result, in order; later images are dropped with a note. Default 8. */\n readonly maxImages?: number\n /** Base64 characters of images kept in the result in total; an image that would exceed it is\n * dropped with a note. Default 4 MiB.\n */\n readonly maxImageBytes?: number\n}\n\nexport const defaultCodeModeLimits = {\n timeoutMs: 120_000,\n memoryLimitBytes: 64 * 1024 * 1024,\n maxNestedCalls: 256,\n maxOutputChars: 40_000,\n maxImages: defaultCodeModeMaxImages,\n maxImageBytes: defaultCodeModeMaxImageBytes\n} as const satisfies Required<CodeModeLimits>\n\n/** Kept between a host deadline and the script deadline, for result handling. */\nexport const codeModeDeadlineMarginMs = 5_000\n\n/** A deadline never clamps a script below this timeout. */\nexport const codeModeMinimumTimeoutMs = 1_000\n\nexport type MakeCodeModeToolOptions<Context> = {\n readonly executor: CodeModeExecutor\n /** Default `codemode`. */\n readonly name?: string\n /** Estimated tokens (four characters each) for listing nested tools. Default 3000. */\n readonly inlineBudget?: number\n readonly limits?: CodeModeLimits\n /**\n * Epoch milliseconds by which the tool call must end, for example the remaining function budget\n * of a Workflow step. The script timeout is clamped to it minus 5 s, but never below 1 s.\n */\n readonly deadline?: (context: Context) => number | undefined\n /**\n * The store scripts read with `load(key)`, usually rebuilt from the transcript with\n * `codeModeStoreFromToolResults`. Without it every script starts with an empty store.\n */\n readonly loadStore?: (context: Context) => Effect.Effect<CodeModeStore, ToolError>\n /**\n * Runs before each nested call executes, with the nested call (id `<toolCallId>/<seq>`) and the\n * host context. A failure rejects that call in the script with the message and records it as an\n * `error` without executing it. Host decorators around the `ToolExecutor` (outside\n * `ResolvedToolSet.execute`) never see nested calls: put per-call run-authority checks here or in\n * registration-level wrappers.\n */\n readonly beforeNestedCall?: (input: {\n readonly call: ToolCall\n readonly context: Context\n }) => Effect.Effect<void, string>\n /**\n * Runs after each nested call that `beforeNestedCall` admitted, with its outcome: `success` (a\n * result without `isError`), `failure` (an error result or an unexpected failure), or\n * `interrupted` (cancelled, for example still running when the script ended). `durationMs`\n * covers the execution; `result` is present when the call produced one. Calls rejected by\n * `beforeNestedCall` or the call limit never ran and are not reported. The script waits for the\n * hook before it sees the call settle. A failing hook (a failure, a defect, or a synchronous\n * throw) is logged and never changes the call's result.\n */\n readonly afterNestedCall?: (input: CodeModeAfterNestedCallInput<Context>) => Effect.Effect<void>\n /**\n * Staged tool plans (ADR 0005). When the resolution offers staging (`resolveTools(..., { plans,\n * interactionHost })` with a plan review tool), scripts get `stage(name, args)` for stageable\n * approval-gated tools. `false` turns it off for this tool; `maxCalls` may only lower the\n * resolution's cap.\n */\n readonly staging?: CodeModeStagingOptions | false\n /**\n * Where the nested tool catalog goes (ADR 0007). `description` (default): in this tool's\n * description, which changes with the catalog. `instructions`: the description stays invariant\n * and the catalog (every namespace with its tool count, listed tools within `inlineBudget`) is\n * the resolution's `instructions` entry; hosts pass `ResolvedToolSet.instructions` to\n * `RunConfig.instructions`, so a change is appended to the conversation instead of rewriting the\n * cached request prefix.\n */\n readonly catalog?: 'description' | 'instructions'\n}\n\nexport type CodeModeStagingOptions = {\n /** Staged calls per script; `stage` rejects past it. At most the resolution's `maxCalls`. */\n readonly maxCalls?: number\n}\n\n/** Outcome of a nested call for `afterNestedCall`. */\nexport type CodeModeNestedCallOutcome = 'success' | 'failure' | 'interrupted'\n\nexport type CodeModeAfterNestedCallInput<Context> = {\n readonly call: ToolCall\n readonly outcome: CodeModeNestedCallOutcome\n readonly durationMs: number\n readonly context: Context\n readonly result?: ToolResult\n}\n\nconst CodeModeParams = Schema.Struct({\n code: Schema.String.annotate({\n description:\n 'Raw JavaScript: the body of an async function. Top-level await and return work. Not a function declaration and not a module.'\n })\n})\n\ntype CodeModeParams = typeof CodeModeParams.Type\n\nclass CodeModeExecutorRejected extends Data.TaggedError('CodeModeExecutorRejected')<{\n readonly message: string\n}> {}\n\nconst errorMessage = (error: unknown) =>\n error instanceof Error ? error.message : Predicate.isString(error) ? error : 'unknown error'\n\nconst nestedText = (content: Content) =>\n Predicate.isString(content)\n ? content\n : content\n .map(contentPartText)\n .filter(text => text.length > 0)\n .join('\\n')\n\ntype NestedResolution =\n | { readonly ok: true; readonly value: unknown }\n | { readonly ok: false; readonly message: string }\n\n/** What a nested call resolves to: `structuredContent` for tools with an output schema (when\n * present), text content otherwise; error results reject with their text. A tool that declares an\n * output schema but returns no `structuredContent` resolves to its text: that is a tool bug, kept\n * readable rather than turned into a failure.\n */\nconst resolveNestedResult = (tool: CodeModeCatalogTool, result: ToolResult): NestedResolution => {\n const text = nestedText(result.content)\n\n if (result.isError === true) {\n return { ok: false, message: text.length > 0 ? text : `Tool ${tool.name} failed.` }\n }\n\n return tool.structured && result.structuredContent !== undefined\n ? { ok: true, value: result.structuredContent }\n : { ok: true, value: text }\n}\n\ntype CallOutcome = {\n status: NestedToolCallStatus\n durationMs: number\n error?: string\n usage?: AgentUsage\n}\n\ntype CallRecord = {\n readonly name: string\n readonly args: unknown\n readonly id: string\n outcome?: CallOutcome\n}\n\nconst recordedOutcome = (exit: Exit.Exit<ToolResult>, durationMs: number): CallOutcome => {\n if (Exit.isFailure(exit)) {\n return Cause.hasInterruptsOnly(exit.cause)\n ? { status: 'cancelled', durationMs }\n : { status: 'error', durationMs, error: 'The tool call failed unexpectedly.' }\n }\n\n const result = exit.value\n\n const outcome: CallOutcome =\n result.isError === true\n ? { status: 'error', durationMs, error: nestedText(result.content) }\n : { status: 'ok', durationMs }\n\n if (result.usage !== undefined) {\n outcome.usage = result.usage\n }\n\n return outcome\n}\n\nconst hookOutcome = (exit: Exit.Exit<ToolResult>): CodeModeNestedCallOutcome =>\n Exit.isSuccess(exit)\n ? exit.value.isError === true\n ? 'failure'\n : 'success'\n : Cause.hasInterruptsOnly(exit.cause)\n ? 'interrupted'\n : 'failure'\n\n/** What `describeNamespace(name)` resolves to. */\ntype CodeModeNamespaceDescription = {\n readonly name: string\n description?: string\n readonly tools: ReadonlyArray<{\n readonly name: string\n readonly description: string\n readonly stageOnly?: true\n }>\n}\n\nconst SearchOptions = Schema.Struct({\n limit: Schema.optionalKey(Schema.Finite),\n namespace: Schema.optionalKey(Schema.String)\n})\n\nconst decodeSearchOptions = Schema.decodeUnknownOption(SearchOptions)\n\n/** `searchTools`, `describeTool`, and `describeNamespace` over every nested tool and, while the\n * script may stage, every stage-only tool. */\nconst discoveryGlobals = (\n catalog: ReadonlyArray<CodeModeCatalogTool>\n): ReadonlyArray<CodeModeExecutorGlobal> => [\n {\n name: 'searchTools',\n spread: true,\n execute: args => {\n const [query, options] = Array.isArray(args) ? args : []\n\n return Promise.resolve(\n searchCodeModeTools(\n catalog,\n Predicate.isString(query) ? query : '',\n Option.getOrElse(decodeSearchOptions(options ?? {}), () => ({}))\n )\n )\n }\n },\n {\n name: 'describeTool',\n execute: name => {\n const tool = Predicate.isString(name) ? findCodeModeTool(catalog, name) : undefined\n\n return Promise.resolve(tool === undefined ? undefined : describeCodeModeTool(tool))\n }\n },\n {\n name: 'describeNamespace',\n execute: name => {\n const tools = catalog.filter(tool => tool.namespace === name)\n\n if (!Predicate.isString(name) || tools.length === 0) return Promise.resolve(undefined)\n\n const description = tools.find(\n tool => tool.namespaceDescription !== undefined\n )?.namespaceDescription\n\n const namespace: CodeModeNamespaceDescription = {\n name,\n tools: tools.map(tool =>\n tool.exposure === 'stage'\n ? { name: tool.name, description: tool.description, stageOnly: true }\n : { name: tool.identifier, description: tool.description }\n )\n }\n\n if (description !== undefined) namespace.description = description\n\n return Promise.resolve(namespace)\n }\n }\n]\n\ntype StageFork = (\n effect: Effect.Effect<StagedCallReceipt, ToolPlanStageError>,\n options: { readonly signal: AbortSignal }\n) => Fiber.Fiber<StagedCallReceipt, ToolPlanStageError>\n\n/**\n * `stage(name, args)`: records one call into the script's plan (never runs it). `name` is the raw\n * tool name or its catalog identifier (what `searchTools` returns for callable tools).\n */\nconst stageGlobal = (\n plan: ToolPlanBuilder,\n fork: StageFork,\n stageable: ReadonlyArray<NestedTool>\n): CodeModeExecutorGlobal => ({\n name: 'stage',\n spread: true,\n signature: '(name: string, args: object): Promise<{ staged: true; key: string; index: number }>',\n description:\n 'Record a call of a stageable tool into this script’s plan for a person to review; it does not run.',\n execute: (args, { signal }) => {\n const [name, params] = Array.isArray(args) ? args : []\n\n if (signal.aborted) return Promise.reject(new Error('stage was cancelled.'))\n\n if (!Predicate.isString(name))\n return Promise.reject(new Error('stage: the first argument must be a tool name.'))\n\n const toolName =\n stageable.find(({ def }) => def.name === name)?.def.name ??\n stageable.find(({ def }) => toCodemodeIdentifier(def.name) === name)?.def.name ??\n name\n\n return new Promise((resolve, reject) => {\n fork(plan.stage({ name: toolName, params: params === undefined ? {} : params }), {\n signal\n }).addObserver(exit => {\n if (Exit.isSuccess(exit)) {\n resolve(exit.value)\n\n return\n }\n\n const error = Cause.findErrorOption(exit.cause)\n\n reject(\n new Error(\n Option.isSome(error)\n ? `stage: ${error.value.message}`\n : Cause.hasInterruptsOnly(exit.cause)\n ? 'stage was cancelled.'\n : 'stage failed unexpectedly.'\n )\n )\n })\n })\n }\n})\n\n/** A direct call of a stage-only tool rejects with a hint to stage it (ADR 0007). */\nconst stageOnlyRejections = (stageOnly: ReadonlyArray<CodeModeCatalogTool>) =>\n stageOnly.flatMap(tool => {\n const message = `${tool.name} is approval-gated: record it with stage(${JSON.stringify(tool.name)}, args) instead of calling it.`\n\n return tool.identifier === tool.name\n ? [{ key: tool.name, message }]\n : [\n { key: tool.identifier, message },\n { key: tool.name, message }\n ]\n })\n\nconst planNotice = (plan: CodeModeStagedPlan) =>\n `\\n\\nStaged ${plan.count} call${plan.count === 1 ? '' : 's'} as plan ${plan.id}; nothing was applied yet. To apply them, call ${plan.reviewToolName}(${JSON.stringify({ planId: plan.id, planDigest: plan.digest })}): a person reviews the plan and selects the calls to apply.`\n\nconst hasStoreWrites = (result: CodeModeExecutionResult) =>\n result.storeWrites !== undefined &&\n (Object.keys(result.storeWrites.set).length > 0 || result.storeWrites.delete.length > 0)\n\nconst timeoutFor = (limit: number, deadline: number | undefined, now: number) =>\n deadline === undefined\n ? limit\n : Math.max(codeModeMinimumTimeoutMs, Math.min(limit, deadline - now - codeModeDeadlineMarginMs))\n\n/**\n * Waits at most `codeModeDeadlineMarginMs` for an effect (interrupting nested fibers) to finish; it\n * keeps running detached when a nested call does not respond to interruption in time.\n */\nconst boundedWait = (effect: Effect.Effect<void>) =>\n Effect.forkDetach(effect).pipe(\n Effect.flatMap(fiber =>\n Fiber.await(fiber).pipe(\n Effect.timeoutOption(Duration.millis(codeModeDeadlineMarginMs)),\n Effect.interruptible\n )\n ),\n Effect.asVoid\n )\n\ntype RunInput<Context> = {\n readonly options: MakeCodeModeToolOptions<Context>\n readonly limits: Required<CodeModeLimits>\n readonly call: ToolCall\n readonly context: Context\n readonly code: string\n readonly nested: NestedToolExecutor\n}\n\nconst runScript = <Context>(input: RunInput<Context>): Effect.Effect<ToolResult, ToolError> =>\n Effect.gen(function* () {\n const { call, limits, nested, options } = input\n const store = options.loadStore === undefined ? {} : yield* options.loadStore(input.context)\n const startedAt = yield* Clock.currentTimeMillis\n const timeoutMs = timeoutFor(limits.timeoutMs, options.deadline?.(input.context), startedAt)\n const catalog = codeModeCatalog(nested.tools)\n\n return yield* Effect.acquireUseRelease(\n Scope.make(),\n scope =>\n Effect.gen(function* () {\n // Nested calls run as fibers of this tool call (same services, interrupted with it).\n const fibers = yield* FiberSet.make<ToolResult>().pipe(Scope.provide(scope))\n const runFork = yield* FiberSet.runtime(fibers)<never>()\n const records: Array<CallRecord> = []\n\n // Staged tool plans: only when the resolution offers staging and the host keeps it on.\n const stagingOptions = options.staging === false ? undefined : options.staging\n const staging = options.staging === false ? undefined : nested.staging\n const maxStagedCalls = stagingOptions?.maxCalls\n\n const plan =\n staging === undefined\n ? undefined\n : staging.begin(\n maxStagedCalls === undefined ? undefined : { maxCalls: maxStagedCalls }\n )\n\n const stageFibers = yield* FiberSet.make<StagedCallReceipt, ToolPlanStageError>().pipe(\n Scope.provide(scope)\n )\n\n const stageFork = yield* FiberSet.runtime(stageFibers)<never>()\n\n const callTool =\n (tool: CodeModeCatalogTool): CodeModeExecutorTool['execute'] =>\n (args, { signal }) => {\n if (signal.aborted) {\n return Promise.reject(new Error(`${tool.callLabel} was cancelled.`))\n }\n\n if (records.length >= limits.maxNestedCalls) {\n return Promise.reject(\n new Error(\n `${tool.callLabel}: Nested call limit reached: a script may make at most ${limits.maxNestedCalls} tool calls. Batch the work or return partial results.`\n )\n )\n }\n\n const params = args === undefined ? {} : args\n\n const record: CallRecord = {\n id: `${call.id}/${records.length + 1}`,\n name: tool.name,\n args: params\n }\n\n records.push(record)\n\n const nestedCall = ToolCall.make({ id: record.id, name: tool.name, params })\n const afterNestedCall = options.afterNestedCall\n\n const run =\n afterNestedCall === undefined\n ? nested.execute(nestedCall)\n : Effect.gen(function* () {\n const started = yield* Clock.currentTimeMillis\n\n // Host observability never changes the call: a hook failure, defects and\n // synchronous throws included, is only logged.\n const afterNestedCallSafely = (\n hookInput: CodeModeAfterNestedCallInput<Context>\n ) =>\n Effect.suspend(() => afterNestedCall(hookInput)).pipe(\n Effect.exit,\n Effect.flatMap(hookExit =>\n Exit.isSuccess(hookExit)\n ? Effect.void\n : Effect.logWarning(\n `Code mode afterNestedCall failed for ${nestedCall.id}; ignored: ${Cause.pretty(hookExit.cause)}`\n )\n )\n )\n\n return yield* nested.execute(nestedCall).pipe(\n Effect.onExit(exit =>\n Effect.flatMap(Clock.currentTimeMillis, finished =>\n afterNestedCallSafely(\n Exit.isSuccess(exit)\n ? {\n call: nestedCall,\n outcome: hookOutcome(exit),\n durationMs: finished - started,\n context: input.context,\n result: exit.value\n }\n : {\n call: nestedCall,\n outcome: hookOutcome(exit),\n durationMs: finished - started,\n context: input.context\n }\n )\n )\n )\n )\n })\n\n // The plan's ordering guardrail: once a script stages, only read tools run.\n const guarded =\n plan === undefined\n ? run\n : plan.admit(nestedCall).pipe(\n Effect.matchEffect({\n onFailure: error =>\n Effect.succeed(\n ToolResult.make({\n toolCallId: nestedCall.id,\n content: error.message,\n isError: true\n })\n ),\n onSuccess: () => run\n })\n )\n\n const admitted =\n options.beforeNestedCall === undefined\n ? guarded\n : options.beforeNestedCall({ call: nestedCall, context: input.context }).pipe(\n Effect.matchEffect({\n onFailure: message =>\n Effect.succeed(\n ToolResult.make({\n toolCallId: nestedCall.id,\n content: message,\n isError: true\n })\n ),\n onSuccess: () => guarded\n })\n )\n\n const execution = Effect.gen(function* () {\n const started = yield* Clock.currentTimeMillis\n\n return yield* admitted.pipe(\n Effect.onExit(exit =>\n Effect.map(Clock.currentTimeMillis, finished => {\n record.outcome = recordedOutcome(exit, finished - started)\n })\n )\n )\n })\n\n return new Promise((resolve, reject) => {\n runFork(execution, { signal }).addObserver(exit => {\n if (Exit.isFailure(exit)) {\n reject(\n new Error(\n Cause.hasInterruptsOnly(exit.cause)\n ? `${tool.callLabel} was cancelled.`\n : `${tool.callLabel} failed unexpectedly.`\n )\n )\n\n return\n }\n\n const resolution = resolveNestedResult(tool, exit.value)\n\n if (resolution.ok) {\n resolve(resolution.value)\n } else {\n // The script sees which call failed; the nested-call record keeps the raw text.\n reject(new Error(`${tool.callLabel}: ${resolution.message}`))\n }\n })\n })\n }\n\n const tools: ReadonlyArray<CodeModeExecutorTool> = catalog.map(tool => ({\n name: tool.name,\n description: tool.description,\n inputSchema: tool.inputSchema,\n outputSchema: tool.outputSchema,\n callLabel: tool.callLabel,\n execute: callTool(tool)\n }))\n\n const stageOnly =\n plan === undefined || staging === undefined\n ? []\n : codeModeStageOnlyCatalog(staging.tools)\n\n const result = yield* Effect.tryPromise({\n try: signal =>\n options.executor.execute(input.code, {\n tools,\n globals:\n plan === undefined || staging === undefined\n ? discoveryGlobals(catalog)\n : [\n ...discoveryGlobals([...catalog, ...stageOnly]),\n stageGlobal(plan, stageFork, staging.tools)\n ],\n unbound: stageOnlyRejections(stageOnly),\n timeoutMs,\n memoryLimitBytes: limits.memoryLimitBytes,\n store,\n signal\n }),\n catch: error => new CodeModeExecutorRejected({ message: errorMessage(error) })\n }).pipe(\n Effect.catch(error =>\n Effect.succeed<CodeModeExecutionResult>({\n ok: false,\n error: {\n kind: 'sandbox',\n message: `The code mode executor failed: ${error.message}`\n },\n output: []\n })\n ),\n // Backstop for executors that miss their own deadline: interrupting aborts the signal.\n Effect.timeoutOption(Duration.millis(timeoutMs + codeModeDeadlineMarginMs)),\n Effect.map(\n Option.getOrElse((): CodeModeExecutionResult => ({\n ok: false,\n error: {\n kind: 'timeout',\n message: `Execution timed out after ${timeoutMs} ms (the executor did not stop in time)`\n },\n output: []\n }))\n )\n )\n\n // Calls still running when the script ended are cancelled and recorded as such.\n yield* boundedWait(FiberSet.clear(fibers))\n yield* boundedWait(FiberSet.clear(stageFibers))\n\n const finishedAt = yield* Clock.currentTimeMillis\n\n // A successful script's staged calls are saved once as a plan; a failed script's are\n // discarded (a partial plan is never offered for review). Nothing staged ever ran.\n const stagedCount = plan === undefined ? 0 : (yield* plan.staged).length\n\n const saved =\n plan === undefined || stagedCount === 0 || !result.ok\n ? undefined\n : yield* plan.finish.pipe(Effect.result)\n\n const savedPlan: CodeModeStagedPlan | undefined =\n staging === undefined ||\n saved === undefined ||\n Result.isFailure(saved) ||\n saved.success === undefined\n ? undefined\n : {\n id: saved.success.id,\n digest: saved.success.digest,\n count: saved.success.calls.length,\n reviewToolName: staging.reviewToolName\n }\n\n const planFailure =\n saved !== undefined && Result.isFailure(saved) ? saved.failure : undefined\n\n const planFailed = planFailure !== undefined\n const plural = stagedCount === 1 ? '' : 's'\n\n const stagingNotice =\n stagedCount === 0\n ? undefined\n : !result.ok\n ? `\\n\\nThe script failed, so its ${stagedCount} staged call${plural} ${stagedCount === 1 ? 'was' : 'were'} discarded; nothing was applied.`\n : planFailure !== undefined\n ? `\\n\\nThe script staged ${stagedCount} call${plural}, but the plan could not be saved (${planFailure.message}); nothing was applied. Run the script again.`\n : savedPlan === undefined\n ? undefined\n : planNotice(savedPlan)\n\n const recorded = records.map((record): NestedToolCallInput => ({\n id: record.id,\n name: record.name,\n args: record.args,\n // A call interrupted before it started has no outcome.\n ...(record.outcome ?? { status: 'cancelled' })\n }))\n\n const recorder = recorded.reduce(\n recordNestedToolCall,\n makeNestedToolCallRecorder({ maxCalls: limits.maxNestedCalls })\n )\n\n const { nestedCalls, usage } = nestedToolCallResultFields(recorder)\n\n const segments = codeModeResultSegments({\n result,\n wallTimeMs: finishedAt - startedAt,\n calls: recorded,\n maxChars: limits.maxOutputChars,\n maxImages: limits.maxImages,\n maxImageBytes: limits.maxImageBytes\n })\n\n // The staging notice follows the bounded output so a cut never hides the plan.\n const content = codeModeSegmentsContent(\n stagingNotice === undefined\n ? segments\n : [...segments, { type: 'text', text: stagingNotice }]\n )\n\n const ok = result.ok && !planFailed\n\n const codemode: CodeModeStructuredContent['codemode'] =\n ok && result.storeWrites !== undefined && hasStoreWrites(result)\n ? { ok: true, storeWrites: result.storeWrites }\n : { ok }\n\n const structuredContent: CodeModeStructuredContent = {\n codemode: savedPlan === undefined ? codemode : { ...codemode, plan: savedPlan }\n }\n\n type ResultFields = {\n toolCallId: string\n content: Content\n isError?: boolean\n structuredContent: CodeModeStructuredContent\n nestedCalls: typeof nestedCalls\n usage?: AgentUsage\n }\n\n const fields: ResultFields = {\n toolCallId: call.id,\n content,\n structuredContent,\n nestedCalls\n }\n\n if (!ok) {\n fields.isError = true\n }\n\n if (usage !== undefined) {\n fields.usage = usage\n }\n\n return ToolResult.make(fields)\n }),\n (scope, exit) => boundedWait(Scope.close(scope, exit))\n )\n })\n\nconst argsPreviewChars = 200\n\nconst interruptedCallStatus = (entry: ToolLedgerEntry): CodeModeInterruptedCallStatus => {\n const outcome = entry.outcome\n\n if (outcome === undefined) return 'unknown'\n\n if (Predicate.isTagged(outcome, 'Failed')) return 'failed'\n\n return outcome.result.isError === true ? 'failed' : 'applied'\n}\n\nconst nestedEntryState = (entry: ToolLedgerEntry) => {\n const outcome = entry.outcome\n\n if (outcome === undefined) {\n return 'unknown: it started but never recorded a result, so it may have been applied'\n }\n\n if (Predicate.isTagged(outcome, 'Failed'))\n return `completed with an error: ${truncateCodePoints(outcome.error.message, nestedToolCallMaxErrorChars)}`\n\n return outcome.result.isError === true ? 'completed with an error result' : 'applied'\n}\n\n/** The bounded, wire-safe `interruptedCalls` of an abandoned execution's nested ledger entries:\n * the `nestedCalls` record bounds (`maxCalls`, 8 KiB of arguments per call, 32 KiB in total).\n */\nconst interruptedCalls = (\n nested: ReadonlyArray<ToolLedgerEntry>,\n maxCalls: number\n): CodeModeInterruptedCalls => {\n const counts = { applied: 0, failed: 0, unknown: 0 }\n const calls: Array<CodeModeInterruptedCall> = []\n let argsBytes = 0\n let complete = true\n\n for (const entry of nested) {\n const status = interruptedCallStatus(entry)\n\n counts[status]++\n\n if (calls.length >= maxCalls) {\n complete = false\n continue\n }\n\n const { args, bytes, truncated } = boundNestedToolCallArgs(entry.args, argsBytes)\n\n // `argsTruncated`: the ledger already cut the arguments when the call was claimed.\n if (truncated || entry.argsTruncated) complete = false\n\n argsBytes += bytes\n calls.push({ key: entry.key, toolName: entry.toolName, args, status })\n }\n\n return { calls, complete, counts }\n}\n\n/**\n * The result of a code mode call whose earlier execution was abandoned (claimed, lease expired,\n * no result recorded). The script never runs again: the result lists that execution's ledgered\n * nested calls as applied, failed, or unknown, and says they were not undone. The text is bounded\n * by `maxOutputChars`; `structuredContent.codemode.interruptedCalls` carries the same entries for\n * hosts, bounded like `nestedCalls`. When the ledger cannot list them, the result stays\n * interrupted, says they may have been applied, and sets `interruptedCallsUnavailable` instead.\n */\nconst abandonedScriptResult =\n (limits: Required<CodeModeLimits>) =>\n ({ call, nested }: ToolLedgerAbandonedInput): ToolResult => {\n const maxChars = limits.maxOutputChars\n\n const listing =\n nested === undefined\n ? 'Its ledgered nested tool calls could not be listed (the tool ledger is unavailable), so any of them may already have been applied.'\n : nested.length === 0\n ? 'No ledgered nested tool calls were recorded for that execution.'\n : [\n `Ledgered nested tool calls of that execution (they were not undone):`,\n ...nested.map(\n entry =>\n `- ${entry.key} ${entry.toolName} ${truncateCodePoints(entry.args, argsPreviewChars)}: ${nestedEntryState(entry)}`\n )\n ].join('\\n')\n\n const text = [\n `Script interrupted: an earlier execution of this ${call.name} call (${call.id}) started but never recorded a result. The script was not run again.`,\n listing,\n 'Calls the ledger policy skips (by default read-only calls) are not listed. Verify the state these calls affect before repeating any work.'\n ].join('\\n\\n')\n\n const structuredContent: CodeModeStructuredContent = {\n codemode:\n nested === undefined\n ? { ok: false, interrupted: true, interruptedCallsUnavailable: true }\n : {\n ok: false,\n interrupted: true,\n interruptedCalls: interruptedCalls(\n nested,\n nestedToolCallRecordLimit(limits.maxNestedCalls)\n )\n }\n }\n\n return ToolResult.make({\n toolCallId: call.id,\n content: codeModeSegmentsContent(boundCodeModeSegments([{ type: 'text', text }], maxChars)),\n isError: true,\n structuredContent\n })\n }\n\n/**\n * The code mode tool: one registration (default name `codemode`, input `{ code }`) whose scripts\n * call the other code-mode-callable tools of the same `resolveTools` resolution. Nested calls run\n * through the resolved execute path with the same host context (input decoding, enablement,\n * registration wrappers), get ids `<toolCallId>/<seq>`, and are recorded on the result's\n * `nestedCalls`. The resolved description lists the callable tools (see\n * `renderCodeModeDescription`).\n *\n * Access is `write`: scripts can call any write tool the resolution exposes to code mode; each\n * nested call keeps its own access metadata and host wrappers.\n *\n * With a tool ledger (`resolveTools(..., { ledger })`) the code mode call itself is ledgered, so a\n * re-executed call never runs its script again: a completed call returns its stored result, an\n * in-flight one is awaited, and an abandoned one returns an interrupted result listing its\n * ledgered nested calls. Nested write calls are ledgered under `<toolCallId>/<seq>`.\n */\nexport const makeCodeModeTool = <Context>(\n options: MakeCodeModeToolOptions<Context>\n): ToolRegistration<Context> => {\n const name = options.name ?? codeModeToolName\n const limits: Required<CodeModeLimits> = { ...defaultCodeModeLimits, ...options.limits }\n const inlineBudget = options.inlineBudget ?? defaultCodeModeInlineBudget\n const store = options.loadStore !== undefined\n\n const instructed = options.catalog === 'instructions'\n\n // In description mode `stage()` is documented once a stageable tool exists (the description\n // lists them anyway). In instructions mode the description must not depend on tools: staging\n // availability alone decides.\n const offered = (staging: CodeModeStagingDescription | undefined) =>\n options.staging === false ||\n staging === undefined ||\n (!instructed && staging.tools.length === 0)\n ? undefined\n : staging\n\n const registration = makeTool<Context, typeof CodeModeParams>({\n name,\n description: instructed\n ? renderCodeModeInstructedDescription({ store })\n : renderCodeModeDescription({ tools: [], inlineBudget, store }),\n parameters: CodeModeParams,\n access: 'write',\n nestedToolAccess: true,\n describe: ({ tools, staging }) => {\n const offeredStaging = offered(staging)\n\n if (instructed)\n return renderCodeModeInstructedDescription(\n offeredStaging === undefined ? { store } : { store, staging: offeredStaging }\n )\n\n return renderCodeModeDescription(\n offeredStaging === undefined\n ? { tools, inlineBudget, store }\n : { tools, inlineBudget, store, staging: offeredStaging }\n )\n },\n\n abandonedResult: abandonedScriptResult(limits),\n execute: ({\n call,\n context,\n params,\n nested\n }: ToolExecutionInput<Context> & { readonly params: CodeModeParams }) =>\n nested === undefined\n ? Effect.fail(\n new ToolError({\n tool: name,\n cause: 'unavailable',\n message: `${name} requires nested tool access; resolve it with resolveTools.`\n })\n )\n : runScript({ options, limits, call, context, code: params.code, nested })\n })\n\n if (!instructed) return registration\n\n const instruct: NestedToolInstructor = ({ tools, staging }) => {\n const offeredStaging = offered(staging)\n\n return codeModeCatalogInstruction(\n offeredStaging === undefined\n ? { tools, inlineBudget }\n : { tools, inlineBudget, staging: offeredStaging }\n )\n }\n\n return { ...registration, instruct }\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yolk-sdk/codemode",
|
|
3
|
-
"version": "0.1.0-canary.
|
|
3
|
+
"version": "0.1.0-canary.108",
|
|
4
4
|
"description": "Code mode for Yolk agents: one tool that runs model-written JavaScript calling the host's resolved tools, plus a Node executor on a confined QuickJS sandbox.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
"dependencies": {
|
|
53
53
|
"@earendil-works/pi-codemode": "1.0.0",
|
|
54
54
|
"effect": "4.0.0",
|
|
55
|
-
"@yolk-sdk/agent": "^0.1.0-canary.
|
|
55
|
+
"@yolk-sdk/agent": "^0.1.0-canary.108"
|
|
56
56
|
},
|
|
57
57
|
"scripts": {
|
|
58
58
|
"build": "tsdown",
|
package/src/catalog.ts
CHANGED
|
@@ -1,18 +1,22 @@
|
|
|
1
|
-
import { Predicate } from 'effect'
|
|
1
|
+
import { Option, Predicate } from 'effect'
|
|
2
|
+
import * as Schema from 'effect/Schema'
|
|
2
3
|
import {
|
|
3
4
|
renderDeclarations,
|
|
4
5
|
renderToolOutputType,
|
|
5
6
|
renderToolSample,
|
|
7
|
+
schemaToType,
|
|
6
8
|
toCodemodeIdentifier
|
|
7
9
|
} from '@earendil-works/pi-codemode/declarations'
|
|
8
|
-
import { toolDiscovery } from '@yolk-sdk/agent/protocol'
|
|
10
|
+
import { isDeferredTool, toolDiscovery } from '@yolk-sdk/agent/protocol'
|
|
11
|
+
import type { RunInstruction } from '@yolk-sdk/agent/loop'
|
|
9
12
|
import type { NestedTool } from '@yolk-sdk/agent/tools'
|
|
10
13
|
import type { CodeModeJsonSchema } from './executor.ts'
|
|
11
14
|
|
|
12
15
|
/** How a nested tool reaches scripts: `all` tools are also direct model tools; `listed` and
|
|
13
|
-
* `search` are the two discovery modes of `codemode`-only tools
|
|
16
|
+
* `search` are the two discovery modes of `codemode`-only tools; `stage` marks a stage-only tool
|
|
17
|
+
* (ADR 0007): found by search, never callable, only passed to `stage()`.
|
|
14
18
|
*/
|
|
15
|
-
export type CodeModeToolExposure = 'all' | 'listed' | 'search'
|
|
19
|
+
export type CodeModeToolExposure = 'all' | 'listed' | 'search' | 'pinned' | 'stage'
|
|
16
20
|
|
|
17
21
|
/** One nested tool as scripts see it. */
|
|
18
22
|
export type CodeModeCatalogTool = {
|
|
@@ -78,7 +82,9 @@ export const codeModeCatalog = (
|
|
|
78
82
|
inputSchema: def.parameters,
|
|
79
83
|
outputSchema: def.outputSchema ?? textOutputSchema,
|
|
80
84
|
structured: def.outputSchema !== undefined,
|
|
81
|
-
|
|
85
|
+
// A deferred tool's declaration is not in model context until activated: scripts find it
|
|
86
|
+
// by search like a codemode-only `search` tool (ADR 0007).
|
|
87
|
+
exposure: toolDiscovery(def) ?? (isDeferredTool(def) ? 'search' : 'all')
|
|
82
88
|
}
|
|
83
89
|
|
|
84
90
|
return moduleDescription === undefined
|
|
@@ -87,6 +93,34 @@ export const codeModeCatalog = (
|
|
|
87
93
|
})
|
|
88
94
|
}
|
|
89
95
|
|
|
96
|
+
/**
|
|
97
|
+
* Catalog entries of the stage-only tools among the stageable ones (`callableBy: 'codemode'`,
|
|
98
|
+
* ADR 0007): searchable and describable, never callable (no `tools` binding), so their call label
|
|
99
|
+
* is the `stage()` call that records them.
|
|
100
|
+
*/
|
|
101
|
+
export const codeModeStageOnlyCatalog = (
|
|
102
|
+
stageable: ReadonlyArray<NestedTool>
|
|
103
|
+
): ReadonlyArray<CodeModeCatalogTool> =>
|
|
104
|
+
stageable.flatMap(({ def, moduleId, moduleDescription }) => {
|
|
105
|
+
if (def.callableBy !== 'codemode') return []
|
|
106
|
+
|
|
107
|
+
const tool: CodeModeCatalogTool = {
|
|
108
|
+
name: def.name,
|
|
109
|
+
identifier: toCodemodeIdentifier(def.name),
|
|
110
|
+
callLabel: `stage(${JSON.stringify(def.name)})`,
|
|
111
|
+
namespace: moduleId,
|
|
112
|
+
description: def.description,
|
|
113
|
+
inputSchema: def.parameters,
|
|
114
|
+
outputSchema: def.outputSchema ?? textOutputSchema,
|
|
115
|
+
structured: def.outputSchema !== undefined,
|
|
116
|
+
exposure: 'stage'
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return [
|
|
120
|
+
moduleDescription === undefined ? tool : { ...tool, namespaceDescription: moduleDescription }
|
|
121
|
+
]
|
|
122
|
+
})
|
|
123
|
+
|
|
90
124
|
/** Default inline budget of the nested tool listing, in estimated tokens. */
|
|
91
125
|
export const defaultCodeModeInlineBudget = 3000
|
|
92
126
|
|
|
@@ -162,9 +196,10 @@ export type CodeModeListing = {
|
|
|
162
196
|
|
|
163
197
|
/**
|
|
164
198
|
* Chooses the tools the description lists. `all` tools are always placed, outside the budget
|
|
165
|
-
* (the model already has their declarations)
|
|
166
|
-
* across namespaces: each round, every namespace still in play
|
|
167
|
-
* a namespace whose next tool does not fit drops out. `search`
|
|
199
|
+
* (the model already has their declarations), and so are `pinned` tools. `listed` tools fill
|
|
200
|
+
* `budget` estimated tokens fairly across namespaces: each round, every namespace still in play
|
|
201
|
+
* places its cheapest remaining tool; a namespace whose next tool does not fit drops out. `search`
|
|
202
|
+
* tools are never candidates.
|
|
168
203
|
*/
|
|
169
204
|
export const selectCodeModeListing = (
|
|
170
205
|
catalog: ReadonlyArray<CodeModeCatalogTool>,
|
|
@@ -181,7 +216,11 @@ export const selectCodeModeListing = (
|
|
|
181
216
|
])
|
|
182
217
|
)
|
|
183
218
|
|
|
184
|
-
const placed = new Set(
|
|
219
|
+
const placed = new Set(
|
|
220
|
+
catalog.flatMap((tool, index) =>
|
|
221
|
+
tool.exposure === 'all' || tool.exposure === 'pinned' ? [index] : []
|
|
222
|
+
)
|
|
223
|
+
)
|
|
185
224
|
|
|
186
225
|
const inPlay = new Set(queues.keys())
|
|
187
226
|
let remaining = budget
|
|
@@ -222,22 +261,39 @@ const intro = [
|
|
|
222
261
|
].join('\n')
|
|
223
262
|
|
|
224
263
|
const stageLine = (reviewToolName: string) =>
|
|
225
|
-
`- \`await stage(name, args)\`: record a call of a stageable tool for review instead of running it; resolves to \`{ staged: true, key, index }\`. Nothing runs until a person approves the plan through the \`${reviewToolName}\` tool, which the result tells you how to call. Once a script stages, it may only run read tools; a script that already ran a write tool cannot stage.`
|
|
264
|
+
`- \`await stage(name, args)\`: record a call of a stageable tool for review instead of running it; resolves to \`{ staged: true, key, index }\`. Nothing runs until a person approves the plan through the \`${reviewToolName}\` tool, which the result tells you how to call. Once a script stages, it may only run read tools; a script that already ran a write tool cannot stage. \`searchTools\` marks approval-gated tools \`stageOnly: true\`: they have no \`tools\` binding, only \`stage()\` them.`
|
|
226
265
|
|
|
227
266
|
/** Stageable tool names listed in the description; the rest are summarized with a count. */
|
|
228
267
|
const maxListedStageableTools = 20
|
|
229
268
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
269
|
+
/** Stageable tools the description names: `pinned` ones first, never stage-only `search` ones. */
|
|
270
|
+
type StageableNames = {
|
|
271
|
+
readonly pinned: ReadonlyArray<string>
|
|
272
|
+
readonly listed: ReadonlyArray<string>
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
const listedStageableNames = (staging: CodeModeStagingDescription): StageableNames => ({
|
|
276
|
+
pinned: staging.tools.flatMap(({ def }) => (toolDiscovery(def) === 'pinned' ? [def.name] : [])),
|
|
277
|
+
listed: staging.tools.flatMap(({ def }) => {
|
|
278
|
+
const discovery = toolDiscovery(def)
|
|
279
|
+
|
|
280
|
+
return discovery === 'search' || discovery === 'pinned' ? [] : [def.name]
|
|
281
|
+
})
|
|
282
|
+
})
|
|
283
|
+
|
|
284
|
+
const hasStageableNames = (names: StageableNames) =>
|
|
285
|
+
names.pinned.length > 0 || names.listed.length > 0
|
|
286
|
+
|
|
287
|
+
/** `pinned` names are always shown; the cap applies to the others. */
|
|
288
|
+
const stageableSection = (names: StageableNames) => {
|
|
289
|
+
const room = Math.max(0, maxListedStageableTools - names.pinned.length)
|
|
290
|
+
const hidden = names.listed.length - room
|
|
233
291
|
|
|
234
292
|
return [
|
|
235
293
|
'## Stageable tools',
|
|
236
294
|
'Approval-gated tools a script cannot call through `tools`, only stage with `await stage("<name>", args)` (arguments as for the tool of the same name):',
|
|
237
|
-
...names.slice(0,
|
|
238
|
-
...(hidden > 0
|
|
239
|
-
? [`- … and ${hidden} more (an unknown name rejects with the stageable tools).`]
|
|
240
|
-
: [])
|
|
295
|
+
...[...names.pinned, ...names.listed.slice(0, room)].map(name => `- \`${name}\``),
|
|
296
|
+
...(hidden > 0 ? [`- … and ${hidden} more (find them with \`searchTools\`).`] : [])
|
|
241
297
|
].join('\n')
|
|
242
298
|
}
|
|
243
299
|
|
|
@@ -287,7 +343,7 @@ export type CodeModeDescriptionInput = {
|
|
|
287
343
|
readonly inlineBudget?: number
|
|
288
344
|
/** Mention `store()`/`load()` persistence. */
|
|
289
345
|
readonly store?: boolean
|
|
290
|
-
/** Document `stage()` and list the stageable tools
|
|
346
|
+
/** Document `stage()` and list the stageable tools that are not stage-only `search` tools. */
|
|
291
347
|
readonly staging?: CodeModeStagingDescription
|
|
292
348
|
}
|
|
293
349
|
|
|
@@ -297,7 +353,8 @@ export type CodeModeDescriptionInput = {
|
|
|
297
353
|
* `all` tools get one line each outside the budget; `listed` tools are declared within the inline
|
|
298
354
|
* budget; `search` tools never contribute, so adding or removing them (even whole namespaces of
|
|
299
355
|
* them) leaves the text byte-identical. With `staging` (and stageable tools), `stage()` is listed
|
|
300
|
-
* with the globals and the stageable tool names follow the nested tools
|
|
356
|
+
* with the globals and the stageable tool names follow the nested tools; stage-only `search` tools
|
|
357
|
+
* are never named, so they leave the text byte-identical too once any stageable tool exists.
|
|
301
358
|
*/
|
|
302
359
|
export const renderCodeModeDescription = (input: CodeModeDescriptionInput): string => {
|
|
303
360
|
const catalog = codeModeCatalog(input.tools)
|
|
@@ -315,6 +372,9 @@ export const renderCodeModeDescription = (input: CodeModeDescriptionInput): stri
|
|
|
315
372
|
const staging =
|
|
316
373
|
input.staging !== undefined && input.staging.tools.length > 0 ? input.staging : undefined
|
|
317
374
|
|
|
375
|
+
const stageableNames =
|
|
376
|
+
staging === undefined ? { pinned: [], listed: [] } : listedStageableNames(staging)
|
|
377
|
+
|
|
318
378
|
return [
|
|
319
379
|
intro,
|
|
320
380
|
[
|
|
@@ -325,14 +385,23 @@ export const renderCodeModeDescription = (input: CodeModeDescriptionInput): stri
|
|
|
325
385
|
sections.length === 0
|
|
326
386
|
? 'Nested tools: none are listed here.'
|
|
327
387
|
: ['## Nested tools by namespace', ...sections].join('\n\n'),
|
|
328
|
-
...(
|
|
388
|
+
...(hasStageableNames(stageableNames) ? [stageableSection(stageableNames)] : []),
|
|
329
389
|
unlistedToolsLine
|
|
330
390
|
].join('\n\n')
|
|
331
391
|
}
|
|
332
392
|
|
|
333
|
-
/**
|
|
393
|
+
/**
|
|
394
|
+
* `describeTool` text: the description and TypeScript declaration of one tool. A stage-only tool
|
|
395
|
+
* has no `tools` binding, so it is described as the `stage()` call that records it.
|
|
396
|
+
*/
|
|
334
397
|
export const describeCodeModeTool = (tool: CodeModeCatalogTool): string =>
|
|
335
|
-
|
|
398
|
+
tool.exposure === 'stage'
|
|
399
|
+
? [
|
|
400
|
+
tool.description,
|
|
401
|
+
`Stage-only (approval-gated): record a call with \`await stage(${JSON.stringify(tool.name)}, args)\`; it runs only after a person approves the plan.`,
|
|
402
|
+
`args: ${schemaToType(tool.inputSchema)}`
|
|
403
|
+
].join('\n\n')
|
|
404
|
+
: renderToolSample(declarationTool(tool))
|
|
336
405
|
|
|
337
406
|
/** Finds a tool by identifier or raw name. */
|
|
338
407
|
export const findCodeModeTool = (
|
|
@@ -340,3 +409,153 @@ export const findCodeModeTool = (
|
|
|
340
409
|
name: string
|
|
341
410
|
): CodeModeCatalogTool | undefined =>
|
|
342
411
|
catalog.find(tool => tool.identifier === name) ?? catalog.find(tool => tool.name === name)
|
|
412
|
+
|
|
413
|
+
/** The instruction key of the code mode catalog (`catalog: 'instructions'`, ADR 0007). */
|
|
414
|
+
export const codeModeCatalogInstructionKey = 'yolk.codemode.catalog'
|
|
415
|
+
|
|
416
|
+
const CodeModeCatalogNamespace = Schema.Struct({
|
|
417
|
+
name: Schema.String,
|
|
418
|
+
description: Schema.optional(Schema.String),
|
|
419
|
+
/** Every tool scripts reach in the namespace: callable and stage-only. */
|
|
420
|
+
count: Schema.Number,
|
|
421
|
+
/** Tools the listing shows. */
|
|
422
|
+
shown: Schema.Number,
|
|
423
|
+
/** The rendered declarations and direct tool lines of the shown tools. */
|
|
424
|
+
listing: Schema.String
|
|
425
|
+
})
|
|
426
|
+
|
|
427
|
+
/** The catalog an instruction message records as its state (JSON, never model-visible). */
|
|
428
|
+
export const CodeModeCatalogSummary = Schema.Struct({
|
|
429
|
+
namespaces: Schema.Array(CodeModeCatalogNamespace),
|
|
430
|
+
/** Stageable tool names the catalog lists: `pinned` ones, then `listed` ones (never `search`). */
|
|
431
|
+
stageable: Schema.Struct({
|
|
432
|
+
pinned: Schema.Array(Schema.String),
|
|
433
|
+
listed: Schema.Array(Schema.String)
|
|
434
|
+
})
|
|
435
|
+
})
|
|
436
|
+
|
|
437
|
+
export type CodeModeCatalogSummary = typeof CodeModeCatalogSummary.Type
|
|
438
|
+
|
|
439
|
+
const decodeCatalogSummary = Schema.decodeUnknownOption(CodeModeCatalogSummary)
|
|
440
|
+
|
|
441
|
+
const encodeCatalogSummary = Schema.encodeOption(CodeModeCatalogSummary)
|
|
442
|
+
|
|
443
|
+
const namespaceListing = (tools: ReadonlyArray<CodeModeCatalogTool>) => {
|
|
444
|
+
const declared = tools.filter(tool => tool.exposure !== 'all')
|
|
445
|
+
const direct = tools.filter(tool => tool.exposure === 'all')
|
|
446
|
+
|
|
447
|
+
return [
|
|
448
|
+
...(declared.length > 0 ? ['```ts', declarationMembers(declared), '```'] : []),
|
|
449
|
+
...direct.map(directToolLine)
|
|
450
|
+
].join('\n')
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
/**
|
|
454
|
+
* The catalog of one resolution: every namespace with its tool count and description, and the
|
|
455
|
+
* listing of the tools the inline budget (or `pinned`) places. `search` and stage-only tools are
|
|
456
|
+
* counted, never listed.
|
|
457
|
+
*/
|
|
458
|
+
export const summarizeCodeModeCatalog = (input: {
|
|
459
|
+
readonly tools: ReadonlyArray<NestedTool>
|
|
460
|
+
readonly staging?: CodeModeStagingDescription
|
|
461
|
+
readonly inlineBudget?: number
|
|
462
|
+
}): CodeModeCatalogSummary => {
|
|
463
|
+
const catalog = codeModeCatalog(input.tools)
|
|
464
|
+
const stageOnly = input.staging === undefined ? [] : codeModeStageOnlyCatalog(input.staging.tools)
|
|
465
|
+
const listing = selectCodeModeListing(catalog, input.inlineBudget ?? defaultCodeModeInlineBudget)
|
|
466
|
+
const listedByNamespace = groupByNamespace(listing.listed.map(tool => ({ tool })))
|
|
467
|
+
|
|
468
|
+
const namespaces = [...groupByNamespace([...catalog, ...stageOnly].map(tool => ({ tool })))].map(
|
|
469
|
+
([name, items]) => {
|
|
470
|
+
const shown = (listedByNamespace.get(name) ?? []).map(item => item.tool)
|
|
471
|
+
|
|
472
|
+
const description = items.find(item => item.tool.namespaceDescription !== undefined)?.tool
|
|
473
|
+
.namespaceDescription
|
|
474
|
+
|
|
475
|
+
const namespace = {
|
|
476
|
+
name,
|
|
477
|
+
count: items.length,
|
|
478
|
+
shown: shown.length,
|
|
479
|
+
listing: namespaceListing(shown)
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
return description === undefined ? namespace : { ...namespace, description }
|
|
483
|
+
}
|
|
484
|
+
)
|
|
485
|
+
|
|
486
|
+
return {
|
|
487
|
+
namespaces,
|
|
488
|
+
stageable:
|
|
489
|
+
input.staging === undefined ? { pinned: [], listed: [] } : listedStageableNames(input.staging)
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
const namespaceLine = (namespace: CodeModeCatalogSummary['namespaces'][number]) => {
|
|
494
|
+
const count = namespace.count === 1 ? '1 tool' : `${namespace.count} tools`
|
|
495
|
+
|
|
496
|
+
const label =
|
|
497
|
+
namespace.shown === namespace.count
|
|
498
|
+
? count
|
|
499
|
+
: namespace.shown === 0
|
|
500
|
+
? `${count}, none listed`
|
|
501
|
+
: `${count}, ${namespace.shown} listed`
|
|
502
|
+
|
|
503
|
+
return `- \`${namespace.name}\` (${label})${namespace.description === undefined ? '' : `: ${namespace.description}`}`
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
/** The full catalog text of an instruction message. */
|
|
507
|
+
export const renderCodeModeCatalog = (summary: CodeModeCatalogSummary): string =>
|
|
508
|
+
[
|
|
509
|
+
'# Code mode catalog',
|
|
510
|
+
'Tools `codemode` scripts can call, by namespace; the latest catalog in the conversation replaces any earlier one. Call a listed tool as `await tools.<id>(args)`; find the others with `await searchTools(query, { namespace })` and read one with `await describeTool(name)` before calling it.',
|
|
511
|
+
['## Namespaces', ...summary.namespaces.map(namespaceLine)].join('\n'),
|
|
512
|
+
...summary.namespaces.flatMap(namespace =>
|
|
513
|
+
namespace.listing.length === 0 ? [] : [`### ${namespace.name}\n${namespace.listing}`]
|
|
514
|
+
),
|
|
515
|
+
...(hasStageableNames(summary.stageable) ? [stageableSection(summary.stageable)] : [])
|
|
516
|
+
].join('\n\n')
|
|
517
|
+
|
|
518
|
+
/**
|
|
519
|
+
* The host instruction of the code mode catalog: its state is the encoded summary, and its render
|
|
520
|
+
* the full catalog (every change renders it whole, see `RunInstruction.render`) or a removal notice.
|
|
521
|
+
*/
|
|
522
|
+
export const codeModeCatalogInstruction = (input: {
|
|
523
|
+
readonly tools: ReadonlyArray<NestedTool>
|
|
524
|
+
readonly staging?: CodeModeStagingDescription
|
|
525
|
+
readonly inlineBudget?: number
|
|
526
|
+
}): RunInstruction => {
|
|
527
|
+
const summary = summarizeCodeModeCatalog(input)
|
|
528
|
+
|
|
529
|
+
return {
|
|
530
|
+
key: codeModeCatalogInstructionKey,
|
|
531
|
+
state:
|
|
532
|
+
summary.namespaces.length === 0
|
|
533
|
+
? undefined
|
|
534
|
+
: Option.getOrUndefined(encodeCatalogSummary(summary)),
|
|
535
|
+
render: current => {
|
|
536
|
+
const decoded = current === undefined ? Option.none() : decodeCatalogSummary(current)
|
|
537
|
+
|
|
538
|
+
return Option.isNone(decoded)
|
|
539
|
+
? 'Code mode tools are no longer available: do not call previously listed code mode tools.'
|
|
540
|
+
: renderCodeModeCatalog(decoded.value)
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
/**
|
|
546
|
+
* The code mode description when the catalog is delivered as an instruction: invariant across
|
|
547
|
+
* catalog changes (only staging availability adds the `stage()` line).
|
|
548
|
+
*/
|
|
549
|
+
export const renderCodeModeInstructedDescription = (input: {
|
|
550
|
+
readonly store?: boolean
|
|
551
|
+
readonly staging?: CodeModeStagingDescription
|
|
552
|
+
}): string =>
|
|
553
|
+
[
|
|
554
|
+
intro,
|
|
555
|
+
[
|
|
556
|
+
'Globals:',
|
|
557
|
+
...globalLines(input.store === true),
|
|
558
|
+
...(input.staging === undefined ? [] : [stageLine(input.staging.reviewToolName)])
|
|
559
|
+
].join('\n'),
|
|
560
|
+
'The tools scripts can call are in the "Code mode catalog" (namespaces, tool counts and listed tools; the latest one applies). Find the others with `await searchTools(query, { namespace? })` and read one with `await describeTool(name)` before calling it.'
|
|
561
|
+
].join('\n\n')
|
package/src/executor.ts
CHANGED
|
@@ -101,6 +101,12 @@ export type CodeModeExecuteOptions = {
|
|
|
101
101
|
/** Values the script reads with `load(key)`. */
|
|
102
102
|
readonly store: CodeModeStore
|
|
103
103
|
readonly signal: AbortSignal
|
|
104
|
+
/**
|
|
105
|
+
* Keys of `tools` a script may reach for that have no binding (stage-only tools, ADR 0007), with
|
|
106
|
+
* the message their call rejects with, for example a hint to use `stage()`. Absent keys keep the
|
|
107
|
+
* engine's own behavior.
|
|
108
|
+
*/
|
|
109
|
+
readonly unbound?: ReadonlyArray<{ readonly key: string; readonly message: string }>
|
|
104
110
|
}
|
|
105
111
|
|
|
106
112
|
/**
|
package/src/index.ts
CHANGED
|
@@ -15,12 +15,19 @@ export type {
|
|
|
15
15
|
|
|
16
16
|
export {
|
|
17
17
|
codeModeCatalog,
|
|
18
|
+
codeModeCatalogInstruction,
|
|
19
|
+
codeModeCatalogInstructionKey,
|
|
20
|
+
CodeModeCatalogSummary,
|
|
21
|
+
codeModeStageOnlyCatalog,
|
|
18
22
|
defaultCodeModeInlineBudget,
|
|
19
23
|
describeCodeModeTool,
|
|
20
24
|
estimateCodeModeTokens,
|
|
21
25
|
findCodeModeTool,
|
|
26
|
+
renderCodeModeCatalog,
|
|
22
27
|
renderCodeModeDescription,
|
|
23
|
-
|
|
28
|
+
renderCodeModeInstructedDescription,
|
|
29
|
+
selectCodeModeListing,
|
|
30
|
+
summarizeCodeModeCatalog
|
|
24
31
|
} from './catalog.ts'
|
|
25
32
|
|
|
26
33
|
export type {
|