@lamplitisles/dsh-nanocodex 0.1.0-beta.0
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/LICENSE +65 -0
- package/NOTICE +2 -0
- package/README.md +182 -0
- package/THIRD_PARTY_NOTICES.md +29 -0
- package/cordis.patch.yml +52 -0
- package/dist/index.d.ts +435 -0
- package/dist/index.js +3405 -0
- package/dist/standard-preset.d.ts +7 -0
- package/dist/standard-preset.js +25 -0
- package/engine-release.json +14 -0
- package/package.json +119 -0
- package/presets/standard/agent.cordis.yml +6 -0
- package/presets/standard/preset.yml +2 -0
- package/vendor/nanocodex/README.md +1344 -0
- package/vendor/nanocodex/actions/events.d.mts +10 -0
- package/vendor/nanocodex/actions/events.mjs +163 -0
- package/vendor/nanocodex/actions/index.d.mts +13 -0
- package/vendor/nanocodex/actions/index.mjs +35 -0
- package/vendor/nanocodex/actions/session.d.mts +66 -0
- package/vendor/nanocodex/actions/session.mjs +59 -0
- package/vendor/nanocodex/actions/turn.d.mts +59 -0
- package/vendor/nanocodex/actions/turn.mjs +37 -0
- package/vendor/nanocodex/actions/voice.d.mts +22 -0
- package/vendor/nanocodex/actions/voice.mjs +50 -0
- package/vendor/nanocodex/browser/Agent.d.mts +55 -0
- package/vendor/nanocodex/browser/Agent.mjs +11 -0
- package/vendor/nanocodex/browser/ChatGptSubscription.d.mts +7 -0
- package/vendor/nanocodex/browser/ChatGptSubscription.mjs +19 -0
- package/vendor/nanocodex/browser/InlineAgent.mjs +266 -0
- package/vendor/nanocodex/browser/Transport.d.mts +74 -0
- package/vendor/nanocodex/browser/Transport.mjs +61 -0
- package/vendor/nanocodex/browser/Voice.d.mts +71 -0
- package/vendor/nanocodex/browser/Voice.mjs +291 -0
- package/vendor/nanocodex/browser/VoiceSession.mjs +688 -0
- package/vendor/nanocodex/browser/WorkerAgent.d.mts +65 -0
- package/vendor/nanocodex/browser/WorkerAgent.mjs +1472 -0
- package/vendor/nanocodex/browser/agent.worker.mjs +3 -0
- package/vendor/nanocodex/browser/config.d.mts +36 -0
- package/vendor/nanocodex/browser/config.mjs +439 -0
- package/vendor/nanocodex/browser/engine.mjs +13 -0
- package/vendor/nanocodex/browser/harness.mjs +55 -0
- package/vendor/nanocodex/browser/host.d.mts +83 -0
- package/vendor/nanocodex/browser/host.mjs +555 -0
- package/vendor/nanocodex/browser/hostManagedWebSocket.d.mts +14 -0
- package/vendor/nanocodex/browser/hostManagedWebSocket.mjs +110 -0
- package/vendor/nanocodex/browser/index.d.mts +92 -0
- package/vendor/nanocodex/browser/index.mjs +23 -0
- package/vendor/nanocodex/browser/indexeddb-durability-store.mjs +197 -0
- package/vendor/nanocodex/browser/workspace.d.mts +16 -0
- package/vendor/nanocodex/browser/workspace.mjs +107 -0
- package/vendor/nanocodex/cloud/Client.d.mts +83 -0
- package/vendor/nanocodex/cloud/Client.mjs +231 -0
- package/vendor/nanocodex/cloud/Decorator.d.mts +31 -0
- package/vendor/nanocodex/cloud/Decorator.mjs +37 -0
- package/vendor/nanocodex/cloud/Dialog.d.mts +113 -0
- package/vendor/nanocodex/cloud/Dialog.mjs +366 -0
- package/vendor/nanocodex/cloud/Errors.d.mts +22 -0
- package/vendor/nanocodex/cloud/Errors.mjs +39 -0
- package/vendor/nanocodex/cloud/Principal.d.mts +26 -0
- package/vendor/nanocodex/cloud/Principal.mjs +88 -0
- package/vendor/nanocodex/cloud/RemoteProvider.mjs +88 -0
- package/vendor/nanocodex/cloud/Transport.d.mts +47 -0
- package/vendor/nanocodex/cloud/Transport.mjs +454 -0
- package/vendor/nanocodex/cloud/actions/account.d.mts +9 -0
- package/vendor/nanocodex/cloud/actions/account.mjs +19 -0
- package/vendor/nanocodex/cloud/actions/agent.d.mts +18 -0
- package/vendor/nanocodex/cloud/actions/agent.mjs +428 -0
- package/vendor/nanocodex/cloud/actions/connection.d.mts +133 -0
- package/vendor/nanocodex/cloud/actions/connection.mjs +580 -0
- package/vendor/nanocodex/cloud/actions/grant.d.mts +10 -0
- package/vendor/nanocodex/cloud/actions/grant.mjs +12 -0
- package/vendor/nanocodex/cloud/actions/index.d.mts +7 -0
- package/vendor/nanocodex/cloud/actions/index.mjs +7 -0
- package/vendor/nanocodex/cloud/actions/machineUsd.d.mts +23 -0
- package/vendor/nanocodex/cloud/actions/machineUsd.mjs +37 -0
- package/vendor/nanocodex/cloud/actions/model.d.mts +12 -0
- package/vendor/nanocodex/cloud/actions/model.mjs +50 -0
- package/vendor/nanocodex/cloud/actions/mpp.d.mts +28 -0
- package/vendor/nanocodex/cloud/actions/mpp.mjs +39 -0
- package/vendor/nanocodex/cloud/index.d.mts +30 -0
- package/vendor/nanocodex/cloud/index.mjs +9 -0
- package/vendor/nanocodex/cloud/internal.mjs +533 -0
- package/vendor/nanocodex/cloud/mercator.mjs +108 -0
- package/vendor/nanocodex/cloud/server/HostPrincipal.d.mts +34 -0
- package/vendor/nanocodex/cloud/server/HostPrincipal.mjs +202 -0
- package/vendor/nanocodex/cloud/server/index.d.mts +1 -0
- package/vendor/nanocodex/cloud/server/index.mjs +1 -0
- package/vendor/nanocodex/cloud/types.d.mts +211 -0
- package/vendor/nanocodex/cloudflare/Agent.d.mts +133 -0
- package/vendor/nanocodex/cloudflare/Agent.mjs +811 -0
- package/vendor/nanocodex/cloudflare/egress-subject.mjs +19 -0
- package/vendor/nanocodex/cloudflare/egress.d.mts +41 -0
- package/vendor/nanocodex/cloudflare/egress.mjs +119 -0
- package/vendor/nanocodex/cloudflare/event-socket.mjs +316 -0
- package/vendor/nanocodex/cloudflare/index.d.mts +7 -0
- package/vendor/nanocodex/cloudflare/index.mjs +6 -0
- package/vendor/nanocodex/host/Agent.d.mts +55 -0
- package/vendor/nanocodex/host/Agent.mjs +1 -0
- package/vendor/nanocodex/host/index.d.mts +82 -0
- package/vendor/nanocodex/host/index.mjs +10 -0
- package/vendor/nanocodex/index.d.mts +81 -0
- package/vendor/nanocodex/index.mjs +12 -0
- package/vendor/nanocodex/internal.mjs +1275 -0
- package/vendor/nanocodex/managed/Agent.d.mts +380 -0
- package/vendor/nanocodex/managed/Agent.mjs +1694 -0
- package/vendor/nanocodex/managed/ManagedError.d.mts +9 -0
- package/vendor/nanocodex/managed/ManagedError.mjs +8 -0
- package/vendor/nanocodex/managed/README.md +241 -0
- package/vendor/nanocodex/managed/Voice.mjs +202 -0
- package/vendor/nanocodex/managed/index.d.mts +20 -0
- package/vendor/nanocodex/managed/index.mjs +2 -0
- package/vendor/nanocodex/managed/internal.mjs +119 -0
- package/vendor/nanocodex/node/Agent.d.mts +50 -0
- package/vendor/nanocodex/node/Agent.mjs +203 -0
- package/vendor/nanocodex/node/ChatGptSubscription.d.mts +9 -0
- package/vendor/nanocodex/node/ChatGptSubscription.mjs +17 -0
- package/vendor/nanocodex/node/Transport.d.mts +44 -0
- package/vendor/nanocodex/node/Transport.mjs +46 -0
- package/vendor/nanocodex/node/host.mjs +731 -0
- package/vendor/nanocodex/node/index.d.mts +73 -0
- package/vendor/nanocodex/node/index.mjs +17 -0
- package/vendor/nanocodex/node/workspace.d.mts +10 -0
- package/vendor/nanocodex/node/workspace.mjs +198 -0
- package/vendor/nanocodex/package.json +200 -0
- package/vendor/nanocodex/pkg-node/nanocodex.d.ts +675 -0
- package/vendor/nanocodex/pkg-node/nanocodex.js +2628 -0
- package/vendor/nanocodex/pkg-node/nanocodex_bg.wasm.d.ts +105 -0
- package/vendor/nanocodex/pkg-node/package.json +1 -0
- package/vendor/nanocodex/pkg-web/.nanocodex-bindgen-stamp +6 -0
- package/vendor/nanocodex/pkg-web/nanocodex-build.json +1 -0
- package/vendor/nanocodex/pkg-web/nanocodex.d.ts +805 -0
- package/vendor/nanocodex/pkg-web/nanocodex.js +2714 -0
- package/vendor/nanocodex/pkg-web/nanocodex_bg.js +2615 -0
- package/vendor/nanocodex/pkg-web/nanocodex_bg.wasm +0 -0
- package/vendor/nanocodex/pkg-web/nanocodex_bg.wasm.d.ts +105 -0
- package/vendor/nanocodex/pkg-web/nanocodex_worker.js +9 -0
- package/vendor/nanocodex/pkg-web/package.json +1 -0
- package/vendor/nanocodex/runtime/chatgpt-subscription.mjs +212 -0
- package/vendor/nanocodex/runtime/cloudflare-durability-store.d.mts +26 -0
- package/vendor/nanocodex/runtime/cloudflare-durability-store.mjs +168 -0
- package/vendor/nanocodex/runtime/code-evaluator.worker.mjs +137 -0
- package/vendor/nanocodex/runtime/code-runtime.mjs +1 -0
- package/vendor/nanocodex/runtime/durability-store.d.mts +85 -0
- package/vendor/nanocodex/runtime/durability-store.mjs +631 -0
- package/vendor/nanocodex/runtime/durability.mjs +153 -0
- package/vendor/nanocodex/runtime/managed-transport.mjs +460 -0
- package/vendor/nanocodex/runtime/mcp-runtime.mjs +534 -0
- package/vendor/nanocodex/runtime/postgres-durability-store.d.mts +64 -0
- package/vendor/nanocodex/runtime/postgres-durability-store.mjs +532 -0
- package/vendor/nanocodex/runtime/quickjs-evaluator.d.mts +17 -0
- package/vendor/nanocodex/runtime/quickjs-evaluator.mjs +236 -0
- package/vendor/nanocodex/runtime/responses-transport.mjs +22 -0
- package/vendor/nanocodex/runtime/subagents.d.mts +107 -0
- package/vendor/nanocodex/runtime/subagents.mjs +54 -0
- package/vendor/nanocodex/runtime/subscription-store.d.mts +8 -0
- package/vendor/nanocodex/runtime/subscription-store.mjs +42 -0
- package/vendor/nanocodex/runtime/tempo-provider.d.mts +125 -0
- package/vendor/nanocodex/runtime/tempo-provider.mjs +264 -0
- package/vendor/nanocodex/runtime/tool-configuration.mjs +1 -0
- package/vendor/nanocodex/runtime/tool-router.mjs +1 -0
- package/vendor/nanocodex/runtime/utf8.mjs +1 -0
- package/vendor/nanocodex/runtime/worker-evaluator.mjs +162 -0
- package/vendor/nanocodex/runtime/workspace.d.mts +1 -0
- package/vendor/nanocodex/runtime/workspace.mjs +1 -0
- package/vendor/nanocodex/tools/Tools.d.mts +56 -0
- package/vendor/nanocodex/tools/Tools.mjs +136 -0
- package/vendor/nanocodex/tools/artifact.d.mts +1 -0
- package/vendor/nanocodex/tools/artifact.mjs +1 -0
- package/vendor/nanocodex/tools/attachment.mjs +1 -0
- package/vendor/nanocodex/tools/bash.d.mts +1 -0
- package/vendor/nanocodex/tools/bash.mjs +1 -0
- package/vendor/nanocodex/tools/browser/accountInfo.mjs +795 -0
- package/vendor/nanocodex/tools/browser/browserBuffer.mjs +18 -0
- package/vendor/nanocodex/tools/browser/browserCompiler.mjs +155 -0
- package/vendor/nanocodex/tools/browser/browserEgress.mjs +161 -0
- package/vendor/nanocodex/tools/browser/browserPython.mjs +133 -0
- package/vendor/nanocodex/tools/browser/browserShell.mjs +1075 -0
- package/vendor/nanocodex/tools/browser/browserSprintf.mjs +4 -0
- package/vendor/nanocodex/tools/browser/browserSsh.mjs +201 -0
- package/vendor/nanocodex/tools/browser/browserZlib.mjs +136 -0
- package/vendor/nanocodex/tools/browser/compiler.worker.mjs +160 -0
- package/vendor/nanocodex/tools/browser/devTunnelsSshBrowser.mjs +13 -0
- package/vendor/nanocodex/tools/browser/index.d.mts +209 -0
- package/vendor/nanocodex/tools/browser/index.mjs +174 -0
- package/vendor/nanocodex/tools/browser/opfsGit.mjs +248 -0
- package/vendor/nanocodex/tools/browser/python.worker.mjs +114 -0
- package/vendor/nanocodex/tools/browser/threadGit.mjs +197 -0
- package/vendor/nanocodex/tools/browser/unsupportedNodeRsa.mjs +5 -0
- package/vendor/nanocodex/tools/browser/workspace.mjs +83 -0
- package/vendor/nanocodex/tools/dataset.d.mts +1 -0
- package/vendor/nanocodex/tools/dataset.mjs +1 -0
- package/vendor/nanocodex/tools/datasetContract.mjs +1 -0
- package/vendor/nanocodex/tools/datasetEngine.mjs +1 -0
- package/vendor/nanocodex/tools/hostedCatalog.d.mts +1 -0
- package/vendor/nanocodex/tools/hostedCatalog.mjs +1 -0
- package/vendor/nanocodex/tools/index.d.mts +49 -0
- package/vendor/nanocodex/tools/index.mjs +19 -0
- package/vendor/nanocodex/tools/namedTool.mjs +1 -0
- package/vendor/nanocodex/tools/repository-workspace.d.mts +1 -0
- package/vendor/nanocodex/tools/repository-workspace.mjs +1 -0
- package/vendor/nanocodex/tools/ssh.d.mts +1 -0
- package/vendor/nanocodex/tools/ssh.mjs +1 -0
- package/vendor/nanocodex/tools/standard.mjs +1 -0
- package/vendor/nanocodex/tools/standardDescriptions.mjs +1 -0
- package/vendor/nanocodex/types.d.mts +663 -0
- package/vendor/nanocodex/wasm.d.mts +4 -0
- package/vendor/nanocodex/worker/ChatGptSubscription.d.mts +9 -0
- package/vendor/nanocodex/worker/ChatGptSubscription.mjs +29 -0
- package/vendor/nanocodex/worker/index.d.mts +18 -0
- package/vendor/nanocodex/worker/index.mjs +5 -0
- package/vendor/nanocodex-tools/README.md +65 -0
- package/vendor/nanocodex-tools/dist/hosted/app-tool-catalog.d.ts +7 -0
- package/vendor/nanocodex-tools/dist/hosted/app-tool-catalog.js +14 -0
- package/vendor/nanocodex-tools/dist/hosted/broker-core.d.ts +172 -0
- package/vendor/nanocodex-tools/dist/hosted/broker-core.js +1278 -0
- package/vendor/nanocodex-tools/dist/hosted/index.d.ts +3 -0
- package/vendor/nanocodex-tools/dist/hosted/index.js +3 -0
- package/vendor/nanocodex-tools/dist/hosted/protocol.d.ts +120 -0
- package/vendor/nanocodex-tools/dist/hosted/protocol.js +415 -0
- package/vendor/nanocodex-tools/dist/index.d.ts +19 -0
- package/vendor/nanocodex-tools/dist/index.js +14 -0
- package/vendor/nanocodex-tools/dist/memory.d.ts +204 -0
- package/vendor/nanocodex-tools/dist/memory.js +411 -0
- package/vendor/nanocodex-tools/dist/namespace.d.ts +87 -0
- package/vendor/nanocodex-tools/dist/namespace.js +338 -0
- package/vendor/nanocodex-tools/dist/repository-archive.d.ts +4 -0
- package/vendor/nanocodex-tools/dist/repository-archive.js +104 -0
- package/vendor/nanocodex-tools/dist/runtime.d.ts +30 -0
- package/vendor/nanocodex-tools/dist/runtime.js +37 -0
- package/vendor/nanocodex-tools/dist/session.d.ts +167 -0
- package/vendor/nanocodex-tools/dist/session.js +286 -0
- package/vendor/nanocodex-tools/dist/shell.d.ts +51 -0
- package/vendor/nanocodex-tools/dist/shell.js +553 -0
- package/vendor/nanocodex-tools/dist/ssh.d.ts +29 -0
- package/vendor/nanocodex-tools/dist/ssh.js +66 -0
- package/vendor/nanocodex-tools/dist/workspace.d.ts +45 -0
- package/vendor/nanocodex-tools/dist/workspace.js +237 -0
- package/vendor/nanocodex-tools/package.json +122 -0
- package/vendor/nanocodex-tools/runtime/code-runtime.mjs +817 -0
- package/vendor/nanocodex-tools/runtime/tool-configuration.mjs +50 -0
- package/vendor/nanocodex-tools/runtime/tool-router.mjs +730 -0
- package/vendor/nanocodex-tools/runtime/utf8.d.mts +2 -0
- package/vendor/nanocodex-tools/runtime/utf8.mjs +25 -0
- package/vendor/nanocodex-tools/tools/artifact.d.mts +69 -0
- package/vendor/nanocodex-tools/tools/artifact.mjs +270 -0
- package/vendor/nanocodex-tools/tools/attachment.mjs +603 -0
- package/vendor/nanocodex-tools/tools/bash.d.mts +90 -0
- package/vendor/nanocodex-tools/tools/bash.mjs +742 -0
- package/vendor/nanocodex-tools/tools/dataset.d.mts +9 -0
- package/vendor/nanocodex-tools/tools/dataset.mjs +47 -0
- package/vendor/nanocodex-tools/tools/datasetContract.mjs +54 -0
- package/vendor/nanocodex-tools/tools/datasetEngine.mjs +1167 -0
- package/vendor/nanocodex-tools/tools/execution-contract.d.mts +5 -0
- package/vendor/nanocodex-tools/tools/execution-contract.mjs +63 -0
- package/vendor/nanocodex-tools/tools/hostedCatalog.d.mts +20 -0
- package/vendor/nanocodex-tools/tools/hostedCatalog.mjs +67 -0
- package/vendor/nanocodex-tools/tools/hostedMachine.d.mts +10 -0
- package/vendor/nanocodex-tools/tools/hostedMachine.mjs +56 -0
- package/vendor/nanocodex-tools/tools/just-bash-browser.d.mts +1 -0
- package/vendor/nanocodex-tools/tools/just-bash-browser.mjs +1 -0
- package/vendor/nanocodex-tools/tools/namedTool.d.mts +3 -0
- package/vendor/nanocodex-tools/tools/namedTool.mjs +3 -0
- package/vendor/nanocodex-tools/tools/nodeProcess.d.mts +10 -0
- package/vendor/nanocodex-tools/tools/nodeProcess.mjs +314 -0
- package/vendor/nanocodex-tools/tools/processOutput.mjs +56 -0
- package/vendor/nanocodex-tools/tools/repository-workspace.d.mts +55 -0
- package/vendor/nanocodex-tools/tools/repository-workspace.mjs +397 -0
- package/vendor/nanocodex-tools/tools/ssh.d.mts +56 -0
- package/vendor/nanocodex-tools/tools/ssh.mjs +503 -0
- package/vendor/nanocodex-tools/tools/standard.mjs +458 -0
- package/vendor/nanocodex-tools/tools/standardDescriptions.mjs +3 -0
- package/vendor/nanocodex-tools/tools/types.d.mts +83 -0
- package/vendor/nanocodex-tools/tools/workspace.d.mts +25 -0
- package/vendor/nanocodex-tools/tools/workspace.mjs +282 -0
- package/vendor/nanocodex-tools/tools/x.d.mts +30 -0
- package/vendor/nanocodex-tools/tools/x.mjs +91 -0
|
@@ -0,0 +1,817 @@
|
|
|
1
|
+
import {
|
|
2
|
+
providerSource,
|
|
3
|
+
ToolRouter,
|
|
4
|
+
toolMapSource,
|
|
5
|
+
toolRouterBrand,
|
|
6
|
+
toolRouterRuntime,
|
|
7
|
+
} from "./tool-router.mjs";
|
|
8
|
+
|
|
9
|
+
const CANCELLATION_MESSAGE = "Code Mode execution was cancelled";
|
|
10
|
+
|
|
11
|
+
export function createCodeRuntime(toolConfiguration = {}, extras = {}) {
|
|
12
|
+
const activeExecutions = new Set();
|
|
13
|
+
const codeObservations = new Map();
|
|
14
|
+
const cells = new Map();
|
|
15
|
+
const turns = new Map();
|
|
16
|
+
const cellGeneration = globalThis.crypto.randomUUID();
|
|
17
|
+
let nextCellId = 1;
|
|
18
|
+
const stores = new Map();
|
|
19
|
+
const ownsRouter = !toolConfiguration?.[toolRouterBrand];
|
|
20
|
+
const router = !ownsRouter
|
|
21
|
+
? (toolConfiguration[toolRouterRuntime] ?? toolConfiguration)
|
|
22
|
+
: new ToolRouter();
|
|
23
|
+
let nextSourceId = 1;
|
|
24
|
+
let nextCallId = 1;
|
|
25
|
+
const toolByName = new Map();
|
|
26
|
+
const subagentBindingsBySession = new Map();
|
|
27
|
+
const subagentSessions = extras.subagentSessions;
|
|
28
|
+
|
|
29
|
+
function addTools(configuration = {}) {
|
|
30
|
+
const added = {};
|
|
31
|
+
for (const [name, tool] of Object.entries(configuration)) {
|
|
32
|
+
if (toolByName.has(name)) {
|
|
33
|
+
throw new Error(`tool is already configured: ${name}`);
|
|
34
|
+
}
|
|
35
|
+
added[name] = tool;
|
|
36
|
+
}
|
|
37
|
+
if (Object.keys(added).length) {
|
|
38
|
+
router.addSource(toolMapSource(`cloud:${String(nextSourceId++).padStart(8, "0")}`, added));
|
|
39
|
+
for (const [name, tool] of Object.entries(added)) toolByName.set(name, tool);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
if (!toolConfiguration?.[toolRouterBrand]) addTools(toolConfiguration);
|
|
43
|
+
|
|
44
|
+
function callableDefinitions() {
|
|
45
|
+
return router.definitions();
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function resolveTool(name) {
|
|
49
|
+
return router.resolve(name);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async function executeTool(name, encodedInput, sessionId = "default", callId = "tool", model = "unknown") {
|
|
53
|
+
let input;
|
|
54
|
+
try {
|
|
55
|
+
input = JSON.parse(encodedInput);
|
|
56
|
+
} catch (error) {
|
|
57
|
+
return encodeToolOutput(`invalid tool input: ${errorMessage(error)}`, false, null);
|
|
58
|
+
}
|
|
59
|
+
const controller = new AbortController();
|
|
60
|
+
const execution = { callId, controller, sessionId, turn: turns.get(sessionId) ?? 0 };
|
|
61
|
+
activeExecutions.add(execution);
|
|
62
|
+
try {
|
|
63
|
+
const tool = resolveTool(name);
|
|
64
|
+
if (!tool) return encodeToolOutput(`unknown application tool: ${name}`, false, null);
|
|
65
|
+
const result = await router.execute(name, input, {
|
|
66
|
+
sessionId,
|
|
67
|
+
parentCallId: "",
|
|
68
|
+
callId,
|
|
69
|
+
model,
|
|
70
|
+
signal: controller.signal,
|
|
71
|
+
subagent: subagentBindingsBySession.get(sessionId)?.descriptor,
|
|
72
|
+
});
|
|
73
|
+
return encodeToolOutput(
|
|
74
|
+
outputBody(result),
|
|
75
|
+
toolSucceeded(result),
|
|
76
|
+
structuredResult(result, `tool ${name} result`),
|
|
77
|
+
toolMetadata(result, `tool ${name} metadata`),
|
|
78
|
+
);
|
|
79
|
+
} catch (error) {
|
|
80
|
+
return encodeToolOutput(errorMessage(error), false, null);
|
|
81
|
+
} finally {
|
|
82
|
+
activeExecutions.delete(execution);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
async function executeCode(source, sessionId = "default", parentCallId = "exec", model = "unknown", observer, cell) {
|
|
87
|
+
if (typeof model === "function" && observer === undefined) {
|
|
88
|
+
observer = model;
|
|
89
|
+
model = "unknown";
|
|
90
|
+
}
|
|
91
|
+
const startedAt = performance.now();
|
|
92
|
+
const content = cell?.content ?? [];
|
|
93
|
+
const stored = stores.get(sessionId) || new Map();
|
|
94
|
+
stores.set(sessionId, stored);
|
|
95
|
+
const nestedCalls = [];
|
|
96
|
+
const controller = cell?.controller ?? new AbortController();
|
|
97
|
+
const execution = { callId: parentCallId, controller, sessionId, cell, turn: turns.get(sessionId) ?? 0 };
|
|
98
|
+
activeExecutions.add(execution);
|
|
99
|
+
let admission;
|
|
100
|
+
try {
|
|
101
|
+
admission = await router.admit(controller.signal);
|
|
102
|
+
} catch (error) {
|
|
103
|
+
activeExecutions.delete(execution);
|
|
104
|
+
return JSON.stringify({
|
|
105
|
+
output: `Script failed\nWall time ${wallTime(startedAt)} seconds\nOutput:\n${errorMessage(error)}`,
|
|
106
|
+
success: false,
|
|
107
|
+
nested_calls: nestedCalls,
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
const tools = Object.create(null);
|
|
111
|
+
const availableTools = [...admission.tools.values()];
|
|
112
|
+
const availableDefinitions = admission.definitions.map((definition) => definition.type === "tool_search"
|
|
113
|
+
? deepFreeze({
|
|
114
|
+
type: "function",
|
|
115
|
+
name: "tool_search",
|
|
116
|
+
description: definition.description,
|
|
117
|
+
strict: false,
|
|
118
|
+
parameters: jsonSnapshot(definition.parameters, "tool_search parameters"),
|
|
119
|
+
})
|
|
120
|
+
: definition);
|
|
121
|
+
const nestedInvocations = [];
|
|
122
|
+
const normalized = availableTools.map(({ name }) => normalizeIdentifier(name));
|
|
123
|
+
if (new Set(normalized).size !== normalized.length) {
|
|
124
|
+
admission.release();
|
|
125
|
+
activeExecutions.delete(execution);
|
|
126
|
+
return JSON.stringify({ output: "Script failed\nOutput:\nCode Mode tool names collide after normalization", success: false, nested_calls: [] });
|
|
127
|
+
}
|
|
128
|
+
for (const { name } of availableTools) {
|
|
129
|
+
const normalizedName = normalizeIdentifier(name);
|
|
130
|
+
tools[normalizedName] = (input) => {
|
|
131
|
+
const invocation = executeNestedTool(input);
|
|
132
|
+
// Attach a rejection handler immediately so a discarded guest Promise
|
|
133
|
+
// cannot become an unhandled rejection before the cell reaches its
|
|
134
|
+
// quiescence boundary.
|
|
135
|
+
nestedInvocations.push(invocation.then(() => undefined, () => undefined));
|
|
136
|
+
return invocation;
|
|
137
|
+
};
|
|
138
|
+
// Preserve existing SDK bracket access while advertising Codex's
|
|
139
|
+
// normalized identifiers to newly generated cells.
|
|
140
|
+
if (name !== normalizedName) tools[name] = tools[normalizedName];
|
|
141
|
+
|
|
142
|
+
async function executeNestedTool(input) {
|
|
143
|
+
const callId = `${parentCallId}/code-${nextCallId++}`;
|
|
144
|
+
const toolStartedAt = performance.now();
|
|
145
|
+
const startedAfterNs = Math.max(
|
|
146
|
+
0,
|
|
147
|
+
Math.round((toolStartedAt - startedAt) * 1_000_000),
|
|
148
|
+
);
|
|
149
|
+
const recordedInput = clone(input) ?? null;
|
|
150
|
+
const recordedCall = {
|
|
151
|
+
call_id: callId,
|
|
152
|
+
name,
|
|
153
|
+
input: recordedInput,
|
|
154
|
+
output: "",
|
|
155
|
+
structured_result: null,
|
|
156
|
+
success: false,
|
|
157
|
+
started_after_ns: startedAfterNs,
|
|
158
|
+
duration_ns: 0,
|
|
159
|
+
metadata: null,
|
|
160
|
+
};
|
|
161
|
+
// Rust records nested calls in invocation order even when parallel
|
|
162
|
+
// siblings finish out of order. Reserve the slot before dispatch.
|
|
163
|
+
nestedCalls.push(recordedCall);
|
|
164
|
+
observer?.({
|
|
165
|
+
type: "nested_call_started",
|
|
166
|
+
call_id: callId,
|
|
167
|
+
name,
|
|
168
|
+
input: recordedInput,
|
|
169
|
+
});
|
|
170
|
+
let result;
|
|
171
|
+
try {
|
|
172
|
+
controller.signal.throwIfAborted();
|
|
173
|
+
result = await admission.invoke(name, input, {
|
|
174
|
+
sessionId,
|
|
175
|
+
parentCallId,
|
|
176
|
+
callId,
|
|
177
|
+
model,
|
|
178
|
+
signal: controller.signal,
|
|
179
|
+
subagent: subagentBindingsBySession.get(sessionId)?.descriptor,
|
|
180
|
+
});
|
|
181
|
+
} catch (error) {
|
|
182
|
+
const message = errorMessage(error);
|
|
183
|
+
Object.assign(recordedCall, {
|
|
184
|
+
output: message,
|
|
185
|
+
structured_result: message,
|
|
186
|
+
success: false,
|
|
187
|
+
duration_ns: elapsedNs(toolStartedAt),
|
|
188
|
+
});
|
|
189
|
+
observer?.({ type: "nested_call_completed", call: recordedCall });
|
|
190
|
+
throw error;
|
|
191
|
+
}
|
|
192
|
+
let structured;
|
|
193
|
+
let output;
|
|
194
|
+
let metadata;
|
|
195
|
+
let success;
|
|
196
|
+
try {
|
|
197
|
+
structured = structuredResult(result, `tool ${name} result`);
|
|
198
|
+
output = outputBody(result);
|
|
199
|
+
metadata = toolMetadata(result, `tool ${name} metadata`);
|
|
200
|
+
success = toolSucceeded(result);
|
|
201
|
+
} catch (error) {
|
|
202
|
+
const message = errorMessage(error);
|
|
203
|
+
Object.assign(recordedCall, {
|
|
204
|
+
output: message,
|
|
205
|
+
structured_result: message,
|
|
206
|
+
success: false,
|
|
207
|
+
duration_ns: elapsedNs(toolStartedAt),
|
|
208
|
+
});
|
|
209
|
+
observer?.({ type: "nested_call_completed", call: recordedCall });
|
|
210
|
+
throw error;
|
|
211
|
+
}
|
|
212
|
+
Object.assign(recordedCall, {
|
|
213
|
+
output,
|
|
214
|
+
structured_result: structured,
|
|
215
|
+
success,
|
|
216
|
+
duration_ns: elapsedNs(toolStartedAt),
|
|
217
|
+
metadata,
|
|
218
|
+
});
|
|
219
|
+
observer?.({ type: "nested_call_completed", call: recordedCall });
|
|
220
|
+
if (!success) throw toolValue(result);
|
|
221
|
+
return toolValue(result);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
Object.freeze(tools);
|
|
225
|
+
const EXIT = Symbol("exit");
|
|
226
|
+
|
|
227
|
+
function text(value) {
|
|
228
|
+
controller.signal.throwIfAborted();
|
|
229
|
+
content.push({ type: "input_text", text: stringify(value) });
|
|
230
|
+
}
|
|
231
|
+
function image(value, detail) {
|
|
232
|
+
controller.signal.throwIfAborted();
|
|
233
|
+
const url = typeof value === "string" ? value
|
|
234
|
+
: value?.type === "image" ? `data:${value.mimeType};base64,${value.data}`
|
|
235
|
+
: value?.image_url;
|
|
236
|
+
if (typeof url !== "string" || !url.startsWith("data:image/")) {
|
|
237
|
+
throw new TypeError("image() requires a base64 data URL or MCP image block");
|
|
238
|
+
}
|
|
239
|
+
const selected = detail ?? value?._meta?.["codex/imageDetail"] ?? value?.detail ?? "auto";
|
|
240
|
+
if (!["auto", "low", "high", "original"].includes(selected)) throw new TypeError("invalid image detail");
|
|
241
|
+
content.push({ type: "input_image", image_url: url, detail: selected });
|
|
242
|
+
}
|
|
243
|
+
function audio(value) {
|
|
244
|
+
controller.signal.throwIfAborted();
|
|
245
|
+
const url = typeof value === "string" ? value
|
|
246
|
+
: value?.type === "audio" ? `data:${value.mimeType};base64,${value.data}`
|
|
247
|
+
: value?.audio_url;
|
|
248
|
+
if (typeof url !== "string" || !url.startsWith("data:audio/")) {
|
|
249
|
+
throw new TypeError("audio() requires a base64 data URL or MCP audio block");
|
|
250
|
+
}
|
|
251
|
+
content.push({ type: "input_audio", audio_url: url });
|
|
252
|
+
}
|
|
253
|
+
function generatedImage(result) {
|
|
254
|
+
if (!result || typeof result !== "object" || typeof result.image_url !== "string") {
|
|
255
|
+
throw new TypeError("generatedImage() requires an image generation result");
|
|
256
|
+
}
|
|
257
|
+
image(result.image_url, "high");
|
|
258
|
+
if (typeof result.output_hint === "string" && result.output_hint) text(result.output_hint);
|
|
259
|
+
}
|
|
260
|
+
function notify(value) {
|
|
261
|
+
controller.signal.throwIfAborted();
|
|
262
|
+
if (cell) {
|
|
263
|
+
cell.notifications.push({ call_id: parentCallId, text: stringify(value) });
|
|
264
|
+
cell.wake?.();
|
|
265
|
+
} else text(value);
|
|
266
|
+
}
|
|
267
|
+
function yield_control() {
|
|
268
|
+
if (cell) { cell.yieldRequested = true; cell.wake?.(); }
|
|
269
|
+
}
|
|
270
|
+
const timers = new Map();
|
|
271
|
+
let nextTimer = 1;
|
|
272
|
+
function schedule(callback, delay = 0) {
|
|
273
|
+
controller.signal.throwIfAborted();
|
|
274
|
+
const id = nextTimer++;
|
|
275
|
+
const timer = setTimeout(() => {
|
|
276
|
+
timers.delete(id);
|
|
277
|
+
if (!controller.signal.aborted) {
|
|
278
|
+
try { Promise.resolve(callback()).catch((error) => controller.abort(error)); }
|
|
279
|
+
catch (error) { controller.abort(error); }
|
|
280
|
+
}
|
|
281
|
+
}, delay);
|
|
282
|
+
timers.set(id, timer);
|
|
283
|
+
return id;
|
|
284
|
+
}
|
|
285
|
+
function unschedule(id) {
|
|
286
|
+
clearTimeout(timers.get(id));
|
|
287
|
+
timers.delete(id);
|
|
288
|
+
}
|
|
289
|
+
function store(key, value) {
|
|
290
|
+
controller.signal.throwIfAborted();
|
|
291
|
+
if (typeof key !== "string") throw new TypeError("store key must be a string");
|
|
292
|
+
stored.set(key, clone(value));
|
|
293
|
+
}
|
|
294
|
+
function load(key) {
|
|
295
|
+
return stored.has(key) ? clone(stored.get(key)) : undefined;
|
|
296
|
+
}
|
|
297
|
+
function exit() {
|
|
298
|
+
throw EXIT;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
try {
|
|
302
|
+
try {
|
|
303
|
+
await abortableEvaluation((async () => {
|
|
304
|
+
try {
|
|
305
|
+
await (extras.evaluate || evaluateNative)(source, {
|
|
306
|
+
tools,
|
|
307
|
+
toolDefinitions: availableDefinitions,
|
|
308
|
+
text,
|
|
309
|
+
image,
|
|
310
|
+
audio,
|
|
311
|
+
notify,
|
|
312
|
+
yield_control,
|
|
313
|
+
setTimeout: schedule,
|
|
314
|
+
clearTimeout: unschedule,
|
|
315
|
+
generatedImage,
|
|
316
|
+
store,
|
|
317
|
+
load,
|
|
318
|
+
exit,
|
|
319
|
+
require: extras.require,
|
|
320
|
+
console: extras.console || console,
|
|
321
|
+
signal: controller.signal,
|
|
322
|
+
storedEntries: [...stored],
|
|
323
|
+
});
|
|
324
|
+
} finally {
|
|
325
|
+
// A guest may discard a tool Promise or call exit(). The cell still
|
|
326
|
+
// owns that work: do not report completion or drop its cancellation
|
|
327
|
+
// controller until every invocation reaches a terminal boundary.
|
|
328
|
+
await Promise.allSettled(nestedInvocations);
|
|
329
|
+
}
|
|
330
|
+
})(), controller.signal);
|
|
331
|
+
} catch (error) {
|
|
332
|
+
if (error !== EXIT) throw error;
|
|
333
|
+
}
|
|
334
|
+
return JSON.stringify({
|
|
335
|
+
output: withStatus("Script completed", startedAt, content),
|
|
336
|
+
success: true,
|
|
337
|
+
nested_calls: nestedCalls,
|
|
338
|
+
});
|
|
339
|
+
} catch (error) {
|
|
340
|
+
return JSON.stringify({
|
|
341
|
+
output: `Script failed\nWall time ${wallTime(startedAt)} seconds\nOutput:\n${errorMessage(error)}`,
|
|
342
|
+
success: false,
|
|
343
|
+
nested_calls: nestedCalls,
|
|
344
|
+
});
|
|
345
|
+
} finally {
|
|
346
|
+
for (const timer of timers.values()) clearTimeout(timer);
|
|
347
|
+
admission.release();
|
|
348
|
+
activeExecutions.delete(execution);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
function executeCodeObserved(source, sessionId = "default", parentCallId = "exec", model = "unknown") {
|
|
353
|
+
return observeOperation(sessionId, parentCallId, (observation) => {
|
|
354
|
+
const options = parseExec(source);
|
|
355
|
+
const cell = {
|
|
356
|
+
id: `${cellGeneration}:${nextCellId++}`, sessionId, controller: new AbortController(),
|
|
357
|
+
content: [], updates: [], completedCalls: [], notifications: [], turn: turns.get(sessionId) ?? 0,
|
|
358
|
+
budget: options.max_output_tokens ?? 10_000, result: undefined, observing: false,
|
|
359
|
+
};
|
|
360
|
+
cells.set(cell.id, cell);
|
|
361
|
+
cell.completion = executeCode(options.source, sessionId, parentCallId, model, (update) => {
|
|
362
|
+
// Keep queued completions immutable; the invocation record is mutable
|
|
363
|
+
// until the nested call finishes. Original call IDs survive every wait.
|
|
364
|
+
const encoded = JSON.stringify(update);
|
|
365
|
+
if (cell.observation) cell.observation.push(encoded);
|
|
366
|
+
else cell.updates.push(encoded);
|
|
367
|
+
if (update.type === "nested_call_completed") cell.completedCalls.push(update.call);
|
|
368
|
+
}, cell).then((result) => {
|
|
369
|
+
const completed = JSON.parse(result);
|
|
370
|
+
if (!completed.success && typeof completed.output === "string") {
|
|
371
|
+
cell.content.push({ type: "input_text", text: completed.output.split("Output:\n").slice(1).join("Output:\n") || completed.output });
|
|
372
|
+
}
|
|
373
|
+
cell.result = { success: completed.success };
|
|
374
|
+
cell.wake?.();
|
|
375
|
+
}, (error) => {
|
|
376
|
+
cell.content.push({ type: "input_text", text: errorMessage(error) });
|
|
377
|
+
cell.result = { success: false };
|
|
378
|
+
cell.wake?.();
|
|
379
|
+
});
|
|
380
|
+
return observeCell(cell, observation, options.yield_time_ms ?? 10_000, cell.budget);
|
|
381
|
+
});
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
function waitCodeObserved(input, sessionId = "default", callId = "wait") {
|
|
385
|
+
return observeOperation(sessionId, callId, (observation) => {
|
|
386
|
+
const options = parseCellOptions(JSON.parse(input), ["cell_id", "yield_time_ms", "max_tokens", "terminate"]);
|
|
387
|
+
if (typeof options.cell_id !== "string") throw new TypeError("wait requires a string cell_id");
|
|
388
|
+
if (options.terminate !== undefined && typeof options.terminate !== "boolean") throw new TypeError("terminate must be boolean");
|
|
389
|
+
const cell = cells.get(options.cell_id);
|
|
390
|
+
if (!cell || cell.sessionId !== sessionId) throw new Error(`exec cell ${options.cell_id} not found`);
|
|
391
|
+
if (cell.observing) throw new Error(`exec cell ${cell.id} already has an active observer`);
|
|
392
|
+
cell.turn = turns.get(sessionId) ?? 0;
|
|
393
|
+
if (options.terminate) {
|
|
394
|
+
cell.terminated = true;
|
|
395
|
+
cell.controller.abort(new Error(CANCELLATION_MESSAGE));
|
|
396
|
+
}
|
|
397
|
+
return observeCell(cell, observation, options.yield_time_ms ?? 10_000, options.max_tokens ?? cell.budget);
|
|
398
|
+
});
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
function observeOperation(sessionId, callId, operation) {
|
|
402
|
+
const key = codeObservationKey(sessionId, callId);
|
|
403
|
+
const observation = createCodeObservation(sessionId, turns.get(sessionId) ?? 0);
|
|
404
|
+
codeObservations.get(key)?.close();
|
|
405
|
+
codeObservations.set(key, observation);
|
|
406
|
+
return Promise.resolve().then(() => operation(observation)).catch((error) => JSON.stringify({
|
|
407
|
+
output: `Script failed\nOutput:\n${errorMessage(error)}`, success: false, nested_calls: [],
|
|
408
|
+
})).finally(() => observation.close());
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
async function observeCell(cell, observation, yieldTime, budget) {
|
|
412
|
+
const startedAt = performance.now();
|
|
413
|
+
cell.observing = true;
|
|
414
|
+
cell.observation = observation;
|
|
415
|
+
for (const update of cell.updates.splice(0)) observation.push(update);
|
|
416
|
+
let timer;
|
|
417
|
+
try {
|
|
418
|
+
if (cell.terminated) await cell.completion;
|
|
419
|
+
else if (!cell.result && !cell.yieldRequested && cell.notifications.length === 0) {
|
|
420
|
+
await new Promise((resolve) => {
|
|
421
|
+
cell.wake = resolve;
|
|
422
|
+
// JS timer APIs overflow past this boundary; clamp instead of
|
|
423
|
+
// accidentally turning a large valid duration into a 1 ms wait.
|
|
424
|
+
timer = setTimeout(resolve, Math.min(yieldTime, 2_147_483_647));
|
|
425
|
+
});
|
|
426
|
+
}
|
|
427
|
+
cell.yieldRequested = false;
|
|
428
|
+
const result = cell.result;
|
|
429
|
+
const status = cell.terminated ? "Script terminated"
|
|
430
|
+
: result ? (result.success ? "Script completed" : "Script failed")
|
|
431
|
+
: `Script running with cell ID ${cell.id}`;
|
|
432
|
+
const output = withStatus(status, startedAt, cell.content.splice(0));
|
|
433
|
+
if (result) cells.delete(cell.id);
|
|
434
|
+
let limited = limitCodeOutput(output, budget);
|
|
435
|
+
if (result?.success === false && Array.isArray(limited) && limited.every((item) => item.type === "input_text")) {
|
|
436
|
+
limited = limited.map((item) => item.text).join("");
|
|
437
|
+
}
|
|
438
|
+
return JSON.stringify({
|
|
439
|
+
output: limited,
|
|
440
|
+
success: cell.terminated || (result?.success ?? true),
|
|
441
|
+
nested_calls: cell.completedCalls.splice(0),
|
|
442
|
+
notifications: cell.notifications.splice(0),
|
|
443
|
+
});
|
|
444
|
+
} finally {
|
|
445
|
+
clearTimeout(timer);
|
|
446
|
+
cell.wake = undefined;
|
|
447
|
+
cell.observation = undefined;
|
|
448
|
+
cell.observing = false;
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
async function nextCodeUpdate(sessionId, parentCallId) {
|
|
453
|
+
const key = codeObservationKey(sessionId, parentCallId);
|
|
454
|
+
const observation = codeObservations.get(key);
|
|
455
|
+
if (!observation) throw new Error(`unknown Code Mode observation: ${parentCallId}`);
|
|
456
|
+
const update = await observation.next();
|
|
457
|
+
if (update === null && codeObservations.get(key) === observation) {
|
|
458
|
+
codeObservations.delete(key);
|
|
459
|
+
}
|
|
460
|
+
return update;
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
function closeCodeObservations(sessionId, turn) {
|
|
464
|
+
for (const [key, observation] of codeObservations) {
|
|
465
|
+
if (sessionId !== undefined && observation.sessionId !== sessionId) continue;
|
|
466
|
+
if (turn !== undefined && observation.turn !== turn) continue;
|
|
467
|
+
codeObservations.delete(key);
|
|
468
|
+
observation.close();
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
function cancel(sessionId, turn) {
|
|
473
|
+
for (const execution of activeExecutions) {
|
|
474
|
+
if ((sessionId === undefined || execution.sessionId === sessionId)
|
|
475
|
+
&& (turn === undefined || (execution.cell?.turn ?? execution.turn) === turn)) {
|
|
476
|
+
execution.controller.abort(new Error(CANCELLATION_MESSAGE));
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
for (const [id, cell] of cells) {
|
|
480
|
+
if ((sessionId === undefined || cell.sessionId === sessionId)
|
|
481
|
+
&& (turn === undefined || cell.turn === turn)) cells.delete(id);
|
|
482
|
+
}
|
|
483
|
+
closeCodeObservations(sessionId, turn);
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
function releaseSession(sessionId) {
|
|
487
|
+
cancel(sessionId);
|
|
488
|
+
const binding = subagentBindingsBySession.get(sessionId);
|
|
489
|
+
if (binding !== undefined) {
|
|
490
|
+
subagentSessions?.release?.(sessionId, binding.hostContextRef);
|
|
491
|
+
}
|
|
492
|
+
turns.delete(sessionId);
|
|
493
|
+
stores.delete(sessionId);
|
|
494
|
+
subagentBindingsBySession.delete(sessionId);
|
|
495
|
+
router.releaseSession(sessionId);
|
|
496
|
+
closeCodeObservations(sessionId);
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
function reset() {
|
|
500
|
+
for (const execution of activeExecutions) {
|
|
501
|
+
execution.controller.abort(new Error(CANCELLATION_MESSAGE));
|
|
502
|
+
}
|
|
503
|
+
cells.clear();
|
|
504
|
+
turns.clear();
|
|
505
|
+
stores.clear();
|
|
506
|
+
subagentBindingsBySession.clear();
|
|
507
|
+
closeCodeObservations();
|
|
508
|
+
return ownsRouter ? router.reset() : undefined;
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
return Object.freeze({
|
|
512
|
+
addTools,
|
|
513
|
+
addProvider(provider, options = {}) {
|
|
514
|
+
if (!provider || typeof provider.definitions !== "function" || typeof provider.resolve !== "function") {
|
|
515
|
+
throw new TypeError("a Code Mode tool provider requires definitions() and resolve(name)");
|
|
516
|
+
}
|
|
517
|
+
const sourceId = options.id ?? provider.sourceId ?? `provider:${String(nextSourceId++).padStart(8, "0")}`;
|
|
518
|
+
router.addSource(providerSource(
|
|
519
|
+
sourceId,
|
|
520
|
+
provider,
|
|
521
|
+
options,
|
|
522
|
+
));
|
|
523
|
+
return sourceId;
|
|
524
|
+
},
|
|
525
|
+
validateProviderDefinitions(sourceId, candidateDefinitions, options = {}) {
|
|
526
|
+
const definitions = jsonSnapshot(candidateDefinitions, `tool source ${sourceId} candidate definitions`);
|
|
527
|
+
return router.validateSource({
|
|
528
|
+
id: sourceId,
|
|
529
|
+
kind: options.kind ?? "attached",
|
|
530
|
+
mode: options.mode ?? "attached-over-cloud",
|
|
531
|
+
deferred: options.deferred ?? true,
|
|
532
|
+
definitions: () => definitions,
|
|
533
|
+
resolve: (name) => ({ name, parallelSafe: false, handler() {} }),
|
|
534
|
+
});
|
|
535
|
+
},
|
|
536
|
+
router,
|
|
537
|
+
executeCode,
|
|
538
|
+
executeCodeObserved,
|
|
539
|
+
waitCodeObserved,
|
|
540
|
+
executeTool,
|
|
541
|
+
bindSubagentSession(sessionId, context, hostContextRef) {
|
|
542
|
+
if (hostContextRef !== undefined
|
|
543
|
+
&& (typeof hostContextRef !== "string" || hostContextRef.length === 0)) {
|
|
544
|
+
throw new TypeError("subagent host context ref must be a non-empty string when supplied");
|
|
545
|
+
}
|
|
546
|
+
const descriptor = Object.freeze({
|
|
547
|
+
agentId: context.agentId,
|
|
548
|
+
parentAgentId: context.parentAgentId,
|
|
549
|
+
sessionId: context.sessionId,
|
|
550
|
+
role: context.role,
|
|
551
|
+
task: context.task,
|
|
552
|
+
});
|
|
553
|
+
const existing = subagentBindingsBySession.get(sessionId);
|
|
554
|
+
if (sameSubagentBinding(existing, descriptor, hostContextRef)) return;
|
|
555
|
+
subagentSessions?.bind?.(sessionId, descriptor, hostContextRef);
|
|
556
|
+
subagentBindingsBySession.set(sessionId, Object.freeze({
|
|
557
|
+
descriptor,
|
|
558
|
+
hostContextRef,
|
|
559
|
+
}));
|
|
560
|
+
},
|
|
561
|
+
nextCodeUpdate,
|
|
562
|
+
beginTurn(sessionId) { turns.set(sessionId, (turns.get(sessionId) ?? 0) + 1); },
|
|
563
|
+
cancelTurn(sessionId) { cancel(sessionId, turns.get(sessionId) ?? 0); },
|
|
564
|
+
cancel,
|
|
565
|
+
toolDefinitions: () => JSON.stringify(callableDefinitions()),
|
|
566
|
+
releaseSession,
|
|
567
|
+
reset,
|
|
568
|
+
});
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
function sameSubagentBinding(binding, descriptor, hostContextRef) {
|
|
572
|
+
const left = binding?.descriptor;
|
|
573
|
+
return left !== undefined
|
|
574
|
+
&& left.agentId === descriptor.agentId
|
|
575
|
+
&& left.parentAgentId === descriptor.parentAgentId
|
|
576
|
+
&& left.sessionId === descriptor.sessionId
|
|
577
|
+
&& left.role === descriptor.role
|
|
578
|
+
&& left.task === descriptor.task
|
|
579
|
+
&& binding.hostContextRef === hostContextRef;
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
async function evaluateNative(source, environment) {
|
|
583
|
+
const AsyncFunction = Object.getPrototypeOf(async function () {}).constructor;
|
|
584
|
+
const script = new AsyncFunction(
|
|
585
|
+
"tools",
|
|
586
|
+
"ALL_TOOLS",
|
|
587
|
+
"text",
|
|
588
|
+
"image",
|
|
589
|
+
"generatedImage",
|
|
590
|
+
"audio",
|
|
591
|
+
"notify",
|
|
592
|
+
"yield_control",
|
|
593
|
+
"setTimeout",
|
|
594
|
+
"clearTimeout",
|
|
595
|
+
"store",
|
|
596
|
+
"load",
|
|
597
|
+
"exit",
|
|
598
|
+
"require",
|
|
599
|
+
"console",
|
|
600
|
+
source,
|
|
601
|
+
);
|
|
602
|
+
await script(
|
|
603
|
+
environment.tools,
|
|
604
|
+
environment.toolDefinitions,
|
|
605
|
+
environment.text,
|
|
606
|
+
environment.image,
|
|
607
|
+
environment.generatedImage,
|
|
608
|
+
environment.audio,
|
|
609
|
+
environment.notify,
|
|
610
|
+
environment.yield_control,
|
|
611
|
+
environment.setTimeout,
|
|
612
|
+
environment.clearTimeout,
|
|
613
|
+
environment.store,
|
|
614
|
+
environment.load,
|
|
615
|
+
environment.exit,
|
|
616
|
+
environment.require,
|
|
617
|
+
environment.console,
|
|
618
|
+
);
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
function encodeToolOutput(output, success, structuredResult, metadata = null) {
|
|
622
|
+
return JSON.stringify({
|
|
623
|
+
output,
|
|
624
|
+
success,
|
|
625
|
+
structured_result: structuredResult,
|
|
626
|
+
metadata,
|
|
627
|
+
process_trace: null,
|
|
628
|
+
});
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
function outputBody(value) {
|
|
632
|
+
if (isToolResult(value)) return outputBody(value.output);
|
|
633
|
+
if (Array.isArray(value) && value.every((item) => item?.type === "input_text" || item?.type === "input_image")) {
|
|
634
|
+
return clone(value);
|
|
635
|
+
}
|
|
636
|
+
return stringify(value);
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
function stringify(value) {
|
|
640
|
+
if (typeof value === "string") return value;
|
|
641
|
+
if (value === undefined) return "undefined";
|
|
642
|
+
try {
|
|
643
|
+
return JSON.stringify(value);
|
|
644
|
+
} catch {
|
|
645
|
+
return String(value);
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
function clone(value) {
|
|
650
|
+
if (typeof globalThis.structuredClone === "function") return structuredClone(value);
|
|
651
|
+
return value === undefined ? undefined : JSON.parse(JSON.stringify(value));
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
function jsonSnapshot(value, label) {
|
|
655
|
+
try {
|
|
656
|
+
return JSON.parse(JSON.stringify(value));
|
|
657
|
+
} catch (error) {
|
|
658
|
+
throw new TypeError(`${label} must be JSON-serializable`, { cause: error });
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
function structuredResult(value, label) {
|
|
663
|
+
if (isToolResult(value)) return jsonSnapshot(value.structuredResult, label);
|
|
664
|
+
return value === undefined ? null : jsonSnapshot(value, label);
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
function toolMetadata(value, label) {
|
|
668
|
+
if (!isToolResult(value) || value.metadata == null) return null;
|
|
669
|
+
return jsonSnapshot(value.metadata, label);
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
function toolSucceeded(value) {
|
|
673
|
+
return !isToolResult(value) || value.success;
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
function toolValue(value) {
|
|
677
|
+
return isToolResult(value) ? value.value : value;
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
const TOOL_RESULT = Symbol.for("nanocodex.toolResult");
|
|
681
|
+
|
|
682
|
+
export function toolResult(output, structuredResult = output, options = {}) {
|
|
683
|
+
const success = options.success ?? true;
|
|
684
|
+
if (typeof success !== "boolean") throw new TypeError("tool result success must be boolean");
|
|
685
|
+
const value = Object.prototype.hasOwnProperty.call(options, "value")
|
|
686
|
+
? options.value
|
|
687
|
+
: output;
|
|
688
|
+
return Object.freeze({
|
|
689
|
+
[TOOL_RESULT]: true,
|
|
690
|
+
metadata: options.metadata ?? null,
|
|
691
|
+
output,
|
|
692
|
+
structuredResult,
|
|
693
|
+
success,
|
|
694
|
+
value,
|
|
695
|
+
});
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
function isToolResult(value) {
|
|
699
|
+
return Boolean(value?.[TOOL_RESULT]);
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
function deepFreeze(value) {
|
|
703
|
+
if (!value || typeof value !== "object" || Object.isFrozen(value)) return value;
|
|
704
|
+
for (const child of Object.values(value)) deepFreeze(child);
|
|
705
|
+
return Object.freeze(value);
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
function errorMessage(error) {
|
|
709
|
+
if (error && (error.stack || error.message)) return error.stack || error.message;
|
|
710
|
+
return String(error);
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
function elapsedNs(startedAt) {
|
|
714
|
+
return Math.max(0, Math.round((performance.now() - startedAt) * 1_000_000));
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
function wallTime(startedAt) {
|
|
718
|
+
return ((performance.now() - startedAt) / 1_000).toFixed(1);
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
function withStatus(status, startedAt, content) {
|
|
722
|
+
const heading = `${status}\nWall time ${wallTime(startedAt)} seconds\nOutput:\n`;
|
|
723
|
+
if (!content.length) return heading;
|
|
724
|
+
return [{ type: "input_text", text: heading }, ...content];
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
function abortableEvaluation(evaluation, signal) {
|
|
728
|
+
if (signal.aborted) return Promise.reject(signal.reason);
|
|
729
|
+
return new Promise((resolve, reject) => {
|
|
730
|
+
const onAbort = () => reject(signal.reason ?? new Error(CANCELLATION_MESSAGE));
|
|
731
|
+
const settle = (callback, value) => {
|
|
732
|
+
signal.removeEventListener("abort", onAbort);
|
|
733
|
+
callback(value);
|
|
734
|
+
};
|
|
735
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
736
|
+
Promise.resolve(evaluation).then(
|
|
737
|
+
(value) => settle(resolve, value),
|
|
738
|
+
(error) => settle(reject, error),
|
|
739
|
+
);
|
|
740
|
+
});
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
function codeObservationKey(sessionId, callId) {
|
|
744
|
+
return JSON.stringify([sessionId, callId]);
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
function createCodeObservation(sessionId, turn) {
|
|
748
|
+
const queued = [];
|
|
749
|
+
const waiters = [];
|
|
750
|
+
let closed = false;
|
|
751
|
+
return Object.freeze({
|
|
752
|
+
sessionId,
|
|
753
|
+
turn,
|
|
754
|
+
push(update) {
|
|
755
|
+
if (closed) return;
|
|
756
|
+
const resolve = waiters.shift();
|
|
757
|
+
if (resolve) resolve(update);
|
|
758
|
+
else queued.push(update);
|
|
759
|
+
},
|
|
760
|
+
close() {
|
|
761
|
+
if (closed) return;
|
|
762
|
+
closed = true;
|
|
763
|
+
while (waiters.length) waiters.shift()(null);
|
|
764
|
+
},
|
|
765
|
+
next() {
|
|
766
|
+
if (queued.length) return Promise.resolve(queued.shift());
|
|
767
|
+
if (closed) return Promise.resolve(null);
|
|
768
|
+
return new Promise((resolve) => waiters.push(resolve));
|
|
769
|
+
},
|
|
770
|
+
});
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
function normalizeIdentifier(name) {
|
|
774
|
+
return [...name].map((character, index) => (index === 0 ? /[A-Za-z_$]/ : /[A-Za-z0-9_$]/).test(character) ? character : "_").join("") || "_";
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
function parseExec(source) {
|
|
778
|
+
const [line] = source.split(/\r?\n/, 1);
|
|
779
|
+
if (!line.trimStart().startsWith("// @exec:")) return { source };
|
|
780
|
+
const rest = source.slice(line.length).replace(/^\r?\n/, "");
|
|
781
|
+
if (!rest) throw new TypeError("exec pragma must be followed by JavaScript source on subsequent lines");
|
|
782
|
+
return { ...parseCellOptions(JSON.parse(line.trimStart().slice("// @exec:".length)), ["yield_time_ms", "max_output_tokens"]), source: rest };
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
function parseCellOptions(value, allowed) {
|
|
786
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) throw new TypeError("cell options must be a JSON object");
|
|
787
|
+
for (const [key, field] of Object.entries(value)) {
|
|
788
|
+
if (!allowed.includes(key)) throw new TypeError(`unknown cell option: ${key}`);
|
|
789
|
+
if (key !== "cell_id" && key !== "terminate" && (!Number.isSafeInteger(field) || field < 0)) {
|
|
790
|
+
throw new TypeError(`${key} must be a non-negative safe integer`);
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
return value;
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
function limitCodeOutput(output, budget) {
|
|
797
|
+
let remaining = budget * 4;
|
|
798
|
+
const limit = (text) => {
|
|
799
|
+
const bytes = new TextEncoder().encode(text);
|
|
800
|
+
if (bytes.length <= remaining) { remaining -= bytes.length; return text; }
|
|
801
|
+
const half = Math.floor(remaining / 2);
|
|
802
|
+
remaining = 0;
|
|
803
|
+
// Decode complete code points only, including for non-ASCII tool output.
|
|
804
|
+
let end = half;
|
|
805
|
+
let start = bytes.length - half;
|
|
806
|
+
while (end > 0 && (bytes[end] & 0xc0) === 0x80) end--;
|
|
807
|
+
while (start < bytes.length && (bytes[start] & 0xc0) === 0x80) start++;
|
|
808
|
+
const decoder = new TextDecoder();
|
|
809
|
+
return `${decoder.decode(bytes.subarray(0, end))}…output truncated…${decoder.decode(bytes.subarray(start))}`;
|
|
810
|
+
};
|
|
811
|
+
if (typeof output === "string") {
|
|
812
|
+
const split = output.indexOf("Output:\n");
|
|
813
|
+
return split < 0 ? output : output.slice(0, split + 8) + limit(output.slice(split + 8));
|
|
814
|
+
}
|
|
815
|
+
return output.map((item, index) =>
|
|
816
|
+
item.type === "input_text" ? { ...item, text: index === 0 ? item.text : limit(item.text) } : item);
|
|
817
|
+
}
|