@desplega.ai/agent-swarm 1.171.0 → 1.172.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/dist/{acp-adapter-1z9p3fvx.js → acp-adapter-fpecx1g1.js} +23 -5
- package/dist/{actions-rc2yjk8a.js → actions-pw87a1a8.js} +24 -25
- package/dist/{amp-adapter-ypyf5s1x.js → amp-adapter-8jem2bc0.js} +1 -1
- package/dist/{anthropic-messages-etkbxhhy.js → anthropic-messages-8ee2f22q.js} +55 -22
- package/dist/{app-04ab2s0z.js → app-wmf0m843.js} +7 -8
- package/dist/{assistant-vhmjd8wf.js → assistant-emp1e108.js} +26 -27
- package/dist/{azure-openai-responses-xap57x4b.js → azure-openai-responses-ap1gwxgr.js} +3 -3
- package/dist/{boot-reembed-3p0m259x.js → boot-reembed-1de4f4aj.js} +6 -7
- package/dist/{boot-reembed-wew3gwk8.js → boot-reembed-4ymn9atn.js} +6 -7
- package/dist/{boot-scrub-sweep-5ytwtq24.js → boot-scrub-sweep-adhk1s8c.js} +7 -8
- package/dist/{boot-seeds-s8p2xry8.js → boot-seeds-8q637aew.js} +9 -10
- package/dist/{claude-adapter-akrzg6jh.js → claude-adapter-t26cv8a4.js} +16 -17
- package/dist/{claude-managed-adapter-kqqqhp2q.js → claude-managed-adapter-xgxhqrt2.js} +2 -2
- package/dist/{claude-sdk-session-exq1jadx.js → claude-sdk-session-gsfsh662.js} +16 -17
- package/dist/{cli-n8ybnv1k.js → cli-04emfe2q.js} +1 -1
- package/dist/{cli-w0bwte75.js → cli-104zsxba.js} +2 -1
- package/dist/{cli-x6z90nq9.js → cli-19njxg5g.js} +2 -4
- package/dist/{cli-8mr695sb.js → cli-1hgat6pa.js} +5 -5
- package/dist/{cli-1qtgd1h1.js → cli-1m2xxxe2.js} +2 -2
- package/dist/{cli-231av4y6.js → cli-1x7da0de.js} +2 -4
- package/dist/{cli-x3fy2tgp.js → cli-388egyf6.js} +1 -1
- package/dist/{cli-j0w59p15.js → cli-4fnb6884.js} +4 -4
- package/dist/{cli-y1j1ggbf.js → cli-4nz8p1p5.js} +1372 -588
- package/dist/{cli-s88my3br.js → cli-5rv1j9ga.js} +6 -6
- package/dist/{cli-mn8qkd8v.js → cli-61625k3x.js} +1 -1
- package/dist/{cli-mzj3aqac.js → cli-6jb2rf77.js} +3 -3
- package/dist/{cli-ec7hrgqp.js → cli-8hzfb6xz.js} +8 -8
- package/dist/{cli-n805bb98.js → cli-8vs9trd4.js} +1 -1
- package/dist/{cli-xf713zz0.js → cli-9fxpr83v.js} +1 -1
- package/dist/{cli-q1vnz8wv.js → cli-9s17naqf.js} +1 -1
- package/dist/{cli-nkjwe4z7.js → cli-9xsmdpdt.js} +59 -54
- package/dist/{cli-6xxxb8qt.js → cli-aspcaef3.js} +5 -5
- package/dist/cli-bf7485pm.js +514 -0
- package/dist/{cli-s9y81s7j.js → cli-bt8k5h3s.js} +625 -150
- package/dist/{cli-jxsgrnmq.js → cli-c2pq2rjb.js} +2 -2
- package/dist/{cli-13xwcqvj.js → cli-c5km5znr.js} +2 -2
- package/dist/{cli-5y67yrt6.js → cli-cceskcvt.js} +112 -176
- package/dist/{cli-ph3145b7.js → cli-cf69s3ew.js} +4 -4
- package/dist/{cli-k2v5x13t.js → cli-d603qf86.js} +1 -1
- package/dist/{cli-r455yd58.js → cli-e0a9sk00.js} +1 -1
- package/dist/{cli-cgj4hc12.js → cli-eh1ytktb.js} +1872 -190
- package/dist/{cli-erbp6b4g.js → cli-esnpehf5.js} +7 -8
- package/dist/cli-ev91t6xp.js +148 -0
- package/dist/{cli-e2prcx5m.js → cli-fdvbjtbw.js} +235 -5
- package/dist/{cli-62bwwc2q.js → cli-g4f5pj26.js} +3 -3
- package/dist/{cli-tg8q0b5y.js → cli-g6t9texm.js} +5 -5
- package/dist/{cli-p2pjfq1r.js → cli-gj81e47t.js} +1 -1
- package/dist/{cli-srmej5tp.js → cli-gp1rtyvk.js} +2 -2
- package/dist/{cli-rb9m6rpe.js → cli-h6vtvt5c.js} +139 -1
- package/dist/cli-hd27d2md.js +1057 -0
- package/dist/{cli-hsk5gd15.js → cli-hwwvy36w.js} +3 -3
- package/dist/{cli-2rbt7at8.js → cli-hz99wfxp.js} +1 -1
- package/dist/cli-j1pmc07z.js +706 -0
- package/dist/{cli-01qndrvw.js → cli-j6kpj9az.js} +14 -8
- package/dist/{cli-2qbs2tvf.js → cli-k46r9hmk.js} +6 -6
- package/dist/{cli-wfvedq5w.js → cli-kb4ehzx5.js} +5 -5
- package/dist/{cli-agamgm0p.js → cli-mvzyxwg3.js} +1 -1
- package/dist/{cli-280gnz4v.js → cli-nv4zd0b9.js} +1 -1
- package/dist/{cli-rrbctenq.js → cli-nyhb1r6x.js} +3 -5
- package/dist/{cli-g7k7zbcz.js → cli-p7z6q2m6.js} +2 -1
- package/dist/{cli-yn0a417a.js → cli-phre6gz0.js} +22 -4
- package/dist/{cli-m4csbs67.js → cli-pj6x9qce.js} +1 -1
- package/dist/{cli-w0zx9ya1.js → cli-prrt9fa0.js} +4 -4
- package/dist/{cli-ctret352.js → cli-pstq9888.js} +33 -33
- package/dist/{cli-y195hv1a.js → cli-pxeeyc53.js} +1 -1
- package/dist/{cli-4bm25xtw.js → cli-qsztpfry.js} +1 -1
- package/dist/{cli-5t5my0h8.js → cli-qybkr9n3.js} +278 -1267
- package/dist/{cli-xezn9nkh.js → cli-r44t9wqc.js} +29 -472
- package/dist/{cli-x207g4w1.js → cli-r8smj8b8.js} +1 -1
- package/dist/{cli-bxqez1p0.js → cli-rcp5aqdw.js} +9 -39
- package/dist/{cli-vr5avem2.js → cli-smcrrmhy.js} +14 -18
- package/dist/{cli-0d2rqhvq.js → cli-tgpfnm6r.js} +15 -17
- package/dist/cli-vb8g5x02.js +549 -0
- package/dist/{cli-gt4hxb8p.js → cli-vv0qtfpx.js} +1 -2
- package/dist/{cli-95zs84qw.js → cli-wd0hz6ah.js} +671 -388
- package/dist/{cli-nkqjj42q.js → cli-x4m6feqt.js} +1 -1
- package/dist/{cli-x61srfpp.js → cli-xczpdp9e.js} +2 -2
- package/dist/cli-z1kd834t.js +36 -0
- package/dist/{cli-p8f7dqgt.js → cli-ztrv9y21.js} +48 -33
- package/dist/cli.js +24 -25
- package/dist/{cloudflare-workers-ai-system-one-sta45jf9.js → cloudflare-workers-ai-system-one-vnpyfr7e.js} +3 -3
- package/dist/{codex-adapter-9ag75whm.js → codex-adapter-m4tgdysy.js} +12 -12
- package/dist/{codex-hook-7ba15hjd.js → codex-hook-xch6z5wt.js} +2 -3
- package/dist/{codex-session-runner-9fp7y4cf.js → codex-session-runner-ymwzm6ea.js} +12 -12
- package/dist/{commands-d7eah0y9.js → commands-n0hshcvs.js} +6 -7
- package/dist/{controllers-connect-8t0k65x2.js → controllers-connect-y07cqcvy.js} +24 -25
- package/dist/{cursor-adapter-t4pr1m3c.js → cursor-adapter-q8pgv5qa.js} +2 -3
- package/dist/{db-sp39qv72.js → db-4w7tmt1g.js} +6 -7
- package/dist/{db-2rmd23wq.js → db-hkqvrdcb.js} +7 -8
- package/dist/{dispatcher-g7fsybsd.js → dispatcher-5sd69y14.js} +23 -24
- package/dist/{easter-egg-3d-66zxervv.js → easter-egg-3d-m8rvz40s.js} +4 -3
- package/dist/execute-107xtsp1.js +3745 -0
- package/dist/{google-generative-ai-pvhm0dq1.js → google-generative-ai-7pavrbv7.js} +3 -3
- package/dist/{google-vertex-tek9sqe2.js → google-vertex-3f1jk41f.js} +3 -3
- package/dist/{grok-adapter-1err1me7.js → grok-adapter-8m9yqzbq.js} +24 -6
- package/dist/{handlers-c7yggm37.js → handlers-s20xr10r.js} +26 -27
- package/dist/{heartbeat-dkm924fk.js → heartbeat-3k31q4g2.js} +25 -26
- package/dist/{hook-wgx4exw6.js → hook-gdn6a2fn.js} +13 -14
- package/dist/{hook-g8e7sfnf.js → hook-ygwerrdh.js} +14 -15
- package/dist/{http-1d23k928.js → http-jdteeg81.js} +53 -55
- package/dist/{index-y5vvczrs.js → index-6ksewd5y.js} +20 -18
- package/dist/{index-kkeftv5h.js → index-82rk11v7.js} +4 -4
- package/dist/{index-gm10tmye.js → index-e0snym4t.js} +23 -24
- package/dist/{index-vertaxs1.js → index-ffqzqgwb.js} +6 -7
- package/dist/{index-q7cj9bn2.js → index-tsszt7g9.js} +25 -26
- package/dist/{index-w097eg0x.js → index-vccv018g.js} +1 -1
- package/dist/{index-va4zbvzp.js → index-vm3bm9t7.js} +24 -25
- package/dist/{keepalive-1jc5e7ct.js → keepalive-jekc896t.js} +8 -9
- package/dist/{lead-yxw2f1sg.js → lead-4k9s9szx.js} +40 -41
- package/dist/{maintenance-h7w2m10y.js → maintenance-shazch4n.js} +6 -7
- package/dist/{mistral-conversations-rjxa9s1j.js → mistral-conversations-vjc1ggpd.js} +29 -15
- package/dist/{oauth-refresh-sweep-wh9ypd1b.js → oauth-refresh-sweep-5gb36kn8.js} +7 -8
- package/dist/{onboard-dfr9j762.js → onboard-ydrbnvww.js} +2 -2
- package/dist/{openai-codex-responses-pgvtddvv.js → openai-codex-responses-33qhkkdy.js} +7 -39
- package/dist/{openai-completions-44gb0zsk.js → openai-completions-s88xm3ns.js} +2 -2
- package/dist/{openai-decisions-vj9nkpf8.js → openai-decisions-fctnj8xd.js} +2 -2
- package/dist/{openai-responses-9dyzwzkr.js → openai-responses-7xd2f2e2.js} +3 -3
- package/dist/opencode-adapter-5zh0he20.js +55 -0
- package/dist/{otel-impl-and31m5s.js → otel-impl-6pf5cv86.js} +1 -1
- package/dist/{pi-mono-adapter-fx6k5wwh.js → pi-mono-adapter-ef9wwx9m.js} +25 -500
- package/dist/{pricing-refresh-23zr6j3w.js → pricing-refresh-s52nsa5q.js} +8 -9
- package/dist/{rbac-roles-wnx35x4x.js → rbac-roles-aq3msh4e.js} +6 -7
- package/dist/{rbac-roles-nc7k9d0t.js → rbac-roles-q5gb5e8v.js} +7 -8
- package/dist/{render-v2-81cg1y52.js → render-v2-pd1rg54q.js} +7 -8
- package/dist/{resume-d4b6j2n2.js → resume-gw4a2c6v.js} +14 -15
- package/dist/{runtime-h9ha1mq6.js → runtime-qfry88v2.js} +4 -7
- package/dist/{setup-g68r1w28.js → setup-jda1ksdh.js} +2 -2
- package/dist/{typesafe-system-one-e988vty7.js → typesafe-system-one-zzzcx4s3.js} +3 -3
- package/dist/{virtual-modules-rdjmcg7q.js → virtual-modules-b3ga2m65.js} +13 -12
- package/dist/{work-objects-0zzdnbt9.js → work-objects-tb7ycye8.js} +26 -27
- package/dist/{worker-p1g8qm6h.js → worker-njjccz5d.js} +40 -41
- package/openapi.json +1 -1
- package/package.json +7 -8
- package/src/be/scripts/extract-schema.ts +4 -0
- package/src/github/handlers.ts +61 -18
- package/src/github/types.ts +2 -1
- package/src/prompts/goal-reminder.ts +30 -0
- package/src/prompts/session-templates.ts +22 -0
- package/src/providers/acp-targets.ts +3 -4
- package/src/providers/amp-adapter.ts +1 -1
- package/src/providers/harness-version.ts +0 -1
- package/src/providers/opencode-adapter.ts +442 -723
- package/src/providers/opencode-swarm-plugin.ts +88 -0
- package/src/providers/pi-mono-extension.ts +58 -37
- package/src/scripts-runtime/executors/native.ts +6 -5
- package/src/scripts-runtime/executors/quickjs-bundle.ts +18 -11
- package/src/scripts-runtime/extract-args-schema.ts +22 -4
- package/src/scripts-runtime/import-allowlist.ts +1 -1
- package/src/scripts-runtime/zod-mini-bundle.ts +5 -0
- package/src/slack/render-v2.ts +2 -2
- package/src/tests/entrypoint-acp-target-binary.test.ts +19 -1
- package/src/tests/github-handlers-inline-comments.test.ts +131 -2
- package/src/tests/github-handlers-reactions.test.ts +9 -0
- package/src/tests/harness-version.test.ts +0 -20
- package/src/tests/opencode-adapter.test.ts +536 -1389
- package/src/tests/opencode-mcp-admission-sdk.test.ts +106 -0
- package/src/tests/opencode-swarm-plugin.test.ts +130 -0
- package/src/tests/pi-mono-extension.test.ts +59 -0
- package/src/tests/prompt-template-session.test.ts +4 -3
- package/src/tests/provider-registration.test.ts +3 -0
- package/src/tests/script-executor-quickjs.test.ts +17 -12
- package/src/tests/scripts-http.test.ts +24 -16
- package/src/tests/scripts-import-allowlist.test.ts +1 -1
- package/src/tests/scripts-runtime.test.ts +78 -40
- package/src/tests/seed-scripts.test.ts +13 -0
- package/src/tests/slack-render-v2.test.ts +4 -2
- package/src/tests/ui-logs-parser.test.ts +28 -0
- package/src/tools/script-upsert.ts +1 -1
- package/src/utils/memory-raters.ts +7 -3
- package/src/utils/openrouter-base-url.ts +0 -2
- package/templates/schedules/weekly-harness-upgrade-check/content.md +4 -6
- package/templates/skills/heartbeat-runbook/content.md +2 -0
- package/templates/skills/swarm-scripts/SKILL.md +1 -0
- package/templates/skills/swarm-scripts/content.md +1 -0
- package/dist/cli-5bfdm1ah.js +0 -462
- package/dist/cli-5c2j5yvx.js +0 -222
- package/dist/cli-rhy7z1rp.js +0 -532
- package/dist/cli-ywh2a87k.js +0 -2359
- package/dist/execute-h4jgevyw.js +0 -1387
- package/dist/opencode-adapter-f1zby94p.js +0 -34
- package/plugin/opencode-plugins/lib/memory-raters.ts +0 -7
- package/src/tests/opencode-plugin.test.ts +0 -580
- package/src/tests/providers/opencode-cost.test.ts +0 -74
package/dist/execute-h4jgevyw.js
DELETED
|
@@ -1,1387 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
Bm25Ranker,
|
|
3
|
-
CODEMODE_DOCS_PATH,
|
|
4
|
-
CODEMODE_STORE_ENTRY_TYPE,
|
|
5
|
-
DEFAULT_TOOL_SEARCH_LIMIT,
|
|
6
|
-
combineUsage,
|
|
7
|
-
createToolSearchDocument,
|
|
8
|
-
getCodemodeCallableTools,
|
|
9
|
-
parseCodemodeSource,
|
|
10
|
-
renderToolSample,
|
|
11
|
-
toCodemodeDeclaration,
|
|
12
|
-
toCodemodeIdentifier
|
|
13
|
-
} from "./cli-s9y81s7j.js";
|
|
14
|
-
import {
|
|
15
|
-
formatSize,
|
|
16
|
-
writeOutputFile
|
|
17
|
-
} from "./cli-rhy7z1rp.js";
|
|
18
|
-
import {
|
|
19
|
-
getCodemodeWorkerSpecifier,
|
|
20
|
-
getQuickJSWasmPath
|
|
21
|
-
} from "./cli-5t5my0h8.js";
|
|
22
|
-
import"./cli-5bfdm1ah.js";
|
|
23
|
-
import"./cli-0m7wct0d.js";
|
|
24
|
-
import"./cli-rb9m6rpe.js";
|
|
25
|
-
import"./cli-pqetb83f.js";
|
|
26
|
-
import"./cli-bc05qbss.js";
|
|
27
|
-
// node_modules/@earendil-works/pi-codemode/dist/runtime/host.js
|
|
28
|
-
import { Worker } from "node:worker_threads";
|
|
29
|
-
|
|
30
|
-
// node_modules/@earendil-works/pi-codemode/dist/wasm.js
|
|
31
|
-
import { readFile } from "node:fs/promises";
|
|
32
|
-
import { createRequire } from "node:module";
|
|
33
|
-
var modules = new Map;
|
|
34
|
-
function loadQuickJSWasm(path) {
|
|
35
|
-
const resolved = path ?? createRequire(import.meta.url).resolve("quickjs-wasi/quickjs.wasm");
|
|
36
|
-
let module = modules.get(resolved);
|
|
37
|
-
if (!module) {
|
|
38
|
-
const { WebAssembly } = globalThis;
|
|
39
|
-
module = readFile(resolved).then((bytes) => WebAssembly.compile(bytes)).catch((error) => {
|
|
40
|
-
modules.delete(resolved);
|
|
41
|
-
throw error;
|
|
42
|
-
});
|
|
43
|
-
modules.set(resolved, module);
|
|
44
|
-
}
|
|
45
|
-
return module;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
// node_modules/@earendil-works/pi-codemode/dist/runtime/protocol.js
|
|
49
|
-
function isWorkerToHostMessage(value) {
|
|
50
|
-
if (typeof value !== "object" || value === null)
|
|
51
|
-
return false;
|
|
52
|
-
const type = value.type;
|
|
53
|
-
return type === "call" || type === "output" || type === "done" || type === "crash";
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
// node_modules/@earendil-works/pi-codemode/dist/runtime/host.js
|
|
57
|
-
var DEFAULT_TIMEOUT_MS = 300000;
|
|
58
|
-
var IDENTIFIER = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
|
|
59
|
-
var RESERVED_GLOBALS = new Set([
|
|
60
|
-
"tools",
|
|
61
|
-
"ALL_TOOLS",
|
|
62
|
-
"console",
|
|
63
|
-
"text",
|
|
64
|
-
"image",
|
|
65
|
-
"exit",
|
|
66
|
-
"globalThis",
|
|
67
|
-
"store",
|
|
68
|
-
"load"
|
|
69
|
-
]);
|
|
70
|
-
function errorMessage(error) {
|
|
71
|
-
return error instanceof Error ? error.message : String(error);
|
|
72
|
-
}
|
|
73
|
-
function serializeStore(store) {
|
|
74
|
-
const serialized = {};
|
|
75
|
-
for (const [key, value] of Object.entries(store ?? {})) {
|
|
76
|
-
const json = JSON.stringify(value);
|
|
77
|
-
if (json !== undefined)
|
|
78
|
-
serialized[key] = json;
|
|
79
|
-
}
|
|
80
|
-
return serialized;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
class BridgeError extends Error {
|
|
84
|
-
}
|
|
85
|
-
function parseBridgeJson(json, what) {
|
|
86
|
-
try {
|
|
87
|
-
return JSON.parse(json);
|
|
88
|
-
} catch {
|
|
89
|
-
throw new BridgeError(`${what} is not valid JSON`);
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
function parseStoreWrites(json) {
|
|
93
|
-
const entries = parseBridgeJson(json, "store writes");
|
|
94
|
-
if (!Array.isArray(entries))
|
|
95
|
-
throw new BridgeError("store writes are not an array");
|
|
96
|
-
const writes = { set: {}, delete: [] };
|
|
97
|
-
for (const entry of entries) {
|
|
98
|
-
if (!Array.isArray(entry) || typeof entry[0] !== "string" || (entry.length === 2 ? typeof entry[1] !== "string" : entry.length !== 1)) {
|
|
99
|
-
throw new BridgeError("store writes contain a malformed entry");
|
|
100
|
-
}
|
|
101
|
-
const [key, value] = entry;
|
|
102
|
-
if (value === undefined)
|
|
103
|
-
writes.delete.push(key);
|
|
104
|
-
else
|
|
105
|
-
writes.set[key] = parseBridgeJson(value, `store value for ${JSON.stringify(key)}`);
|
|
106
|
-
}
|
|
107
|
-
return writes;
|
|
108
|
-
}
|
|
109
|
-
function parseScriptError(json) {
|
|
110
|
-
const parsed = parseBridgeJson(json, "script error");
|
|
111
|
-
if (typeof parsed !== "object" || parsed === null)
|
|
112
|
-
throw new BridgeError("script error is not an object");
|
|
113
|
-
const { name, message, stack } = parsed;
|
|
114
|
-
if (typeof message !== "string" || name !== undefined && typeof name !== "string" || stack !== undefined && typeof stack !== "string") {
|
|
115
|
-
throw new BridgeError("script error is malformed");
|
|
116
|
-
}
|
|
117
|
-
return { kind: "script", name, message, stack };
|
|
118
|
-
}
|
|
119
|
-
function defaultWorkerUrl() {
|
|
120
|
-
return new URL(import.meta.url.endsWith(".ts") ? "./worker.ts" : "./worker.js", import.meta.url);
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
class Execution {
|
|
124
|
-
promise;
|
|
125
|
-
resolveResult;
|
|
126
|
-
worker;
|
|
127
|
-
interrupt = new SharedArrayBuffer(4);
|
|
128
|
-
tools;
|
|
129
|
-
globals;
|
|
130
|
-
signal;
|
|
131
|
-
timer;
|
|
132
|
-
output = [];
|
|
133
|
-
calls = [];
|
|
134
|
-
pending = new Map;
|
|
135
|
-
finished = false;
|
|
136
|
-
constructor(options) {
|
|
137
|
-
this.promise = new Promise((resolve) => {
|
|
138
|
-
this.resolveResult = resolve;
|
|
139
|
-
});
|
|
140
|
-
this.tools = options.tools;
|
|
141
|
-
this.globals = options.globals;
|
|
142
|
-
this.signal = options.signal;
|
|
143
|
-
if (Number.isFinite(options.timeoutMs)) {
|
|
144
|
-
this.timer = setTimeout(() => {
|
|
145
|
-
this.finish({ kind: "timeout", message: `Execution timed out after ${options.timeoutMs} ms` });
|
|
146
|
-
}, options.timeoutMs);
|
|
147
|
-
}
|
|
148
|
-
if (options.signal) {
|
|
149
|
-
if (options.signal.aborted) {
|
|
150
|
-
this.onAbort();
|
|
151
|
-
} else {
|
|
152
|
-
options.signal.addEventListener("abort", this.onAbort, { once: true });
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
options.wasm.then((wasm) => this.start(options, wasm), (error) => {
|
|
156
|
-
this.finish({ kind: "sandbox", message: `Failed to load QuickJS: ${errorMessage(error)}` });
|
|
157
|
-
});
|
|
158
|
-
}
|
|
159
|
-
abort(message) {
|
|
160
|
-
this.finish({ kind: "aborted", message });
|
|
161
|
-
return this.promise;
|
|
162
|
-
}
|
|
163
|
-
start(options, wasm) {
|
|
164
|
-
if (this.finished)
|
|
165
|
-
return;
|
|
166
|
-
const workerData = {
|
|
167
|
-
code: options.code,
|
|
168
|
-
tools: [...options.tools.values()].map((tool) => ({
|
|
169
|
-
name: tool.name,
|
|
170
|
-
jsName: toCodemodeIdentifier(tool.name),
|
|
171
|
-
description: tool.description ?? ""
|
|
172
|
-
})),
|
|
173
|
-
globals: [...options.globals.values()].map((global) => ({
|
|
174
|
-
name: global.name,
|
|
175
|
-
spread: global.spread === true
|
|
176
|
-
})),
|
|
177
|
-
wasm,
|
|
178
|
-
memoryLimitBytes: options.memoryLimitBytes,
|
|
179
|
-
store: options.store,
|
|
180
|
-
interrupt: this.interrupt
|
|
181
|
-
};
|
|
182
|
-
let worker;
|
|
183
|
-
try {
|
|
184
|
-
worker = new Worker(options.workerUrl, { workerData });
|
|
185
|
-
} catch (error) {
|
|
186
|
-
this.finish({ kind: "sandbox", message: `Failed to start worker: ${errorMessage(error)}` });
|
|
187
|
-
return;
|
|
188
|
-
}
|
|
189
|
-
this.worker = worker;
|
|
190
|
-
worker.on("message", (message) => {
|
|
191
|
-
try {
|
|
192
|
-
this.handleMessage(message);
|
|
193
|
-
} catch (error) {
|
|
194
|
-
this.finish({
|
|
195
|
-
kind: "sandbox",
|
|
196
|
-
message: error instanceof BridgeError ? `Sandbox bridge broken: ${error.message}. The script may have modified built-ins such as a prototype's toJSON.` : `Sandbox host failed: ${errorMessage(error)}`
|
|
197
|
-
});
|
|
198
|
-
}
|
|
199
|
-
});
|
|
200
|
-
worker.on("error", (error) => {
|
|
201
|
-
this.finish({
|
|
202
|
-
kind: "sandbox",
|
|
203
|
-
name: error instanceof Error ? error.name : undefined,
|
|
204
|
-
message: errorMessage(error)
|
|
205
|
-
});
|
|
206
|
-
});
|
|
207
|
-
worker.on("exit", (code) => {
|
|
208
|
-
this.finish({ kind: "sandbox", message: `Worker exited with code ${code} before the script settled` });
|
|
209
|
-
});
|
|
210
|
-
}
|
|
211
|
-
onAbort = () => {
|
|
212
|
-
const reason = this.signal?.reason;
|
|
213
|
-
this.finish({ kind: "aborted", message: reason instanceof Error ? reason.message : "Execution aborted" });
|
|
214
|
-
};
|
|
215
|
-
post(message) {
|
|
216
|
-
this.worker?.postMessage(message);
|
|
217
|
-
}
|
|
218
|
-
handleMessage(message) {
|
|
219
|
-
if (this.finished)
|
|
220
|
-
return;
|
|
221
|
-
if (!isWorkerToHostMessage(message))
|
|
222
|
-
throw new BridgeError("unknown message from the worker");
|
|
223
|
-
switch (message.type) {
|
|
224
|
-
case "output":
|
|
225
|
-
this.output.push(message.item);
|
|
226
|
-
break;
|
|
227
|
-
case "call":
|
|
228
|
-
if (this.pending.has(message.id))
|
|
229
|
-
throw new BridgeError(`duplicate call id ${message.id}`);
|
|
230
|
-
this.handleCall(message);
|
|
231
|
-
break;
|
|
232
|
-
case "done":
|
|
233
|
-
this.handleDone(message);
|
|
234
|
-
break;
|
|
235
|
-
case "crash":
|
|
236
|
-
this.finish({ kind: "sandbox", message: message.message });
|
|
237
|
-
break;
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
handleDone(message) {
|
|
241
|
-
if (!message.ok) {
|
|
242
|
-
this.finish(parseScriptError(message.error));
|
|
243
|
-
return;
|
|
244
|
-
}
|
|
245
|
-
const value = message.value === undefined ? undefined : parseBridgeJson(message.value, "return value");
|
|
246
|
-
this.finish(undefined, value, parseStoreWrites(message.writes));
|
|
247
|
-
}
|
|
248
|
-
async handleCall(message) {
|
|
249
|
-
const { id, name } = message;
|
|
250
|
-
const isTool = message.target === "tool";
|
|
251
|
-
const record = isTool ? { name, status: "cancelled", durationMs: 0 } : undefined;
|
|
252
|
-
if (record)
|
|
253
|
-
this.calls.push(record);
|
|
254
|
-
const pending = { record, startedAt: performance.now(), controller: new AbortController };
|
|
255
|
-
this.pending.set(id, pending);
|
|
256
|
-
let status;
|
|
257
|
-
let reply;
|
|
258
|
-
try {
|
|
259
|
-
const tool = (isTool ? this.tools : this.globals).get(name);
|
|
260
|
-
if (!tool)
|
|
261
|
-
throw new Error(`Unknown ${isTool ? "tool" : "global"} "${name}"`);
|
|
262
|
-
const args = message.args === undefined ? undefined : JSON.parse(message.args);
|
|
263
|
-
const value = await tool.execute(args, { signal: pending.controller.signal });
|
|
264
|
-
reply = { type: "result", id, ok: true, payload: value === undefined ? undefined : JSON.stringify(value) };
|
|
265
|
-
status = "ok";
|
|
266
|
-
} catch (error) {
|
|
267
|
-
reply = { type: "result", id, ok: false, payload: errorMessage(error) };
|
|
268
|
-
status = "error";
|
|
269
|
-
}
|
|
270
|
-
if (!this.pending.delete(id))
|
|
271
|
-
return;
|
|
272
|
-
if (record) {
|
|
273
|
-
record.status = status;
|
|
274
|
-
record.durationMs = performance.now() - pending.startedAt;
|
|
275
|
-
}
|
|
276
|
-
this.post(reply);
|
|
277
|
-
}
|
|
278
|
-
finish(error, value, writes) {
|
|
279
|
-
if (this.finished)
|
|
280
|
-
return;
|
|
281
|
-
this.finished = true;
|
|
282
|
-
clearTimeout(this.timer);
|
|
283
|
-
this.signal?.removeEventListener("abort", this.onAbort);
|
|
284
|
-
const now = performance.now();
|
|
285
|
-
for (const pending of this.pending.values()) {
|
|
286
|
-
if (pending.record)
|
|
287
|
-
pending.record.durationMs = now - pending.startedAt;
|
|
288
|
-
pending.controller.abort();
|
|
289
|
-
}
|
|
290
|
-
this.pending.clear();
|
|
291
|
-
const result = error ? { ok: false, error, output: this.output, calls: this.calls } : {
|
|
292
|
-
ok: true,
|
|
293
|
-
value,
|
|
294
|
-
output: this.output,
|
|
295
|
-
calls: this.calls,
|
|
296
|
-
storeWrites: writes ?? { set: {}, delete: [] }
|
|
297
|
-
};
|
|
298
|
-
if (!this.worker) {
|
|
299
|
-
this.resolveResult(result);
|
|
300
|
-
return;
|
|
301
|
-
}
|
|
302
|
-
Atomics.store(new Int32Array(this.interrupt), 0, 1);
|
|
303
|
-
this.worker.terminate().catch(() => {
|
|
304
|
-
return;
|
|
305
|
-
}).then(() => this.resolveResult(result));
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
class CodemodeSandbox {
|
|
310
|
-
toolsByName = new Map;
|
|
311
|
-
globalsByName = new Map;
|
|
312
|
-
timeoutMs;
|
|
313
|
-
memoryLimitBytes;
|
|
314
|
-
wasm;
|
|
315
|
-
workerUrl;
|
|
316
|
-
running = new Set;
|
|
317
|
-
closed = false;
|
|
318
|
-
constructor(options = {}) {
|
|
319
|
-
this.timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
320
|
-
this.memoryLimitBytes = options.memoryLimitBytes;
|
|
321
|
-
this.wasm = options.wasm;
|
|
322
|
-
this.workerUrl = options.workerUrl ?? defaultWorkerUrl();
|
|
323
|
-
for (const tool of options.tools ?? [])
|
|
324
|
-
this.registerTool(tool);
|
|
325
|
-
const namespaces = new Set;
|
|
326
|
-
for (const global of options.globals ?? []) {
|
|
327
|
-
const parts = global.name.split(".");
|
|
328
|
-
if (parts.length > 2 || !parts.every((part) => IDENTIFIER.test(part)) || RESERVED_GLOBALS.has(parts[0])) {
|
|
329
|
-
throw new Error(`Invalid global name "${global.name}"`);
|
|
330
|
-
}
|
|
331
|
-
if (this.globalsByName.has(global.name))
|
|
332
|
-
throw new Error(`Global "${global.name}" is already registered`);
|
|
333
|
-
if (parts.length === 2)
|
|
334
|
-
namespaces.add(parts[0]);
|
|
335
|
-
this.globalsByName.set(global.name, global);
|
|
336
|
-
}
|
|
337
|
-
for (const name of namespaces) {
|
|
338
|
-
if (this.globalsByName.has(name))
|
|
339
|
-
throw new Error(`Global "${name}" conflicts with the namespace "${name}"`);
|
|
340
|
-
}
|
|
341
|
-
}
|
|
342
|
-
registerTool(tool) {
|
|
343
|
-
if (this.toolsByName.has(tool.name))
|
|
344
|
-
throw new Error(`Tool "${tool.name}" is already registered`);
|
|
345
|
-
this.toolsByName.set(tool.name, tool);
|
|
346
|
-
}
|
|
347
|
-
unregisterTool(name) {
|
|
348
|
-
return this.toolsByName.delete(name);
|
|
349
|
-
}
|
|
350
|
-
get tools() {
|
|
351
|
-
return [...this.toolsByName.values()];
|
|
352
|
-
}
|
|
353
|
-
get globals() {
|
|
354
|
-
return [...this.globalsByName.values()];
|
|
355
|
-
}
|
|
356
|
-
execute(code, options = {}) {
|
|
357
|
-
if (this.closed)
|
|
358
|
-
return Promise.reject(new Error("Sandbox is closed"));
|
|
359
|
-
const execution = new Execution({
|
|
360
|
-
code,
|
|
361
|
-
tools: new Map(this.toolsByName),
|
|
362
|
-
globals: this.globalsByName,
|
|
363
|
-
timeoutMs: options.timeoutMs ?? this.timeoutMs,
|
|
364
|
-
signal: options.signal,
|
|
365
|
-
memoryLimitBytes: this.memoryLimitBytes,
|
|
366
|
-
store: serializeStore(options.store),
|
|
367
|
-
wasm: this.wasm === undefined ? loadQuickJSWasm() : Promise.resolve(this.wasm),
|
|
368
|
-
workerUrl: this.workerUrl
|
|
369
|
-
});
|
|
370
|
-
this.running.add(execution);
|
|
371
|
-
return execution.promise.finally(() => this.running.delete(execution));
|
|
372
|
-
}
|
|
373
|
-
async close() {
|
|
374
|
-
this.closed = true;
|
|
375
|
-
await Promise.all([...this.running].map((execution) => execution.abort("Sandbox closed")));
|
|
376
|
-
}
|
|
377
|
-
}
|
|
378
|
-
// node_modules/@earendil-works/pi-codemode/dist/runtime/prelude-source.js
|
|
379
|
-
var MAX_STORE_VALUE_CHARS = 256 * 1024;
|
|
380
|
-
var MAX_STORE_TOTAL_CHARS = 1024 * 1024;
|
|
381
|
-
var MAX_OUTPUT_CHARS = 16 * 1024 * 1024;
|
|
382
|
-
var MAX_OUTPUT_ITEMS = 1e5;
|
|
383
|
-
var IMAGE_HELPER_EXPECTS = "image expects a non-empty image URL string, an object with image_url, or a raw MCP image block";
|
|
384
|
-
var PRELUDE_SOURCE = `(function (bridge, toolsJson, globalsJson, storeJson) {
|
|
385
|
-
"use strict";
|
|
386
|
-
|
|
387
|
-
// Freeze every object reachable from the built-in globals, plus the intrinsics that are only
|
|
388
|
-
// reachable from instances (iterator and generator prototypes, %TypedArray%).
|
|
389
|
-
//
|
|
390
|
-
// Freezing alone breaks ordinary code through the "override mistake": a data property that is
|
|
391
|
-
// read-only on a prototype cannot be assigned on an instance either, so
|
|
392
|
-
// this.name = "MyError" in an Error subclass would throw. Commonly overridden properties
|
|
393
|
-
// become accessors whose setter defines an own property on the instance instead.
|
|
394
|
-
(function lockdown() {
|
|
395
|
-
const OVERRIDABLE = new Set(["constructor", "name", "message", "toString", "toLocaleString", "valueOf", "toJSON"]);
|
|
396
|
-
const seen = new Set([globalThis]);
|
|
397
|
-
const queue = [];
|
|
398
|
-
const add = (value) => {
|
|
399
|
-
if ((typeof value === "object" && value !== null) || typeof value === "function") {
|
|
400
|
-
if (!seen.has(value)) {
|
|
401
|
-
seen.add(value);
|
|
402
|
-
queue.push(value);
|
|
403
|
-
}
|
|
404
|
-
}
|
|
405
|
-
};
|
|
406
|
-
|
|
407
|
-
// Accessors from an object literal have no own prototype object, unlike function expressions,
|
|
408
|
-
// whose prototype.constructor would be converted again without end.
|
|
409
|
-
function allowOverride(object, key, value, enumerable) {
|
|
410
|
-
const { get, set } = Object.getOwnPropertyDescriptor(
|
|
411
|
-
{
|
|
412
|
-
get accessor() {
|
|
413
|
-
return value;
|
|
414
|
-
},
|
|
415
|
-
set accessor(next) {
|
|
416
|
-
if (this === object) {
|
|
417
|
-
throw new TypeError("Cannot assign to read only property '" + String(key) + "' of a built-in");
|
|
418
|
-
}
|
|
419
|
-
if ((typeof this !== "object" || this === null) && typeof this !== "function") return;
|
|
420
|
-
Object.defineProperty(this, key, { value: next, writable: true, enumerable: true, configurable: true });
|
|
421
|
-
},
|
|
422
|
-
},
|
|
423
|
-
"accessor",
|
|
424
|
-
);
|
|
425
|
-
Object.defineProperty(object, key, { get, set, enumerable, configurable: false });
|
|
426
|
-
add(get);
|
|
427
|
-
add(set);
|
|
428
|
-
}
|
|
429
|
-
|
|
430
|
-
for (const key of Reflect.ownKeys(globalThis)) {
|
|
431
|
-
const descriptor = Object.getOwnPropertyDescriptor(globalThis, key);
|
|
432
|
-
add(descriptor.value);
|
|
433
|
-
add(descriptor.get);
|
|
434
|
-
add(descriptor.set);
|
|
435
|
-
if ("value" in descriptor && descriptor.configurable) {
|
|
436
|
-
Object.defineProperty(globalThis, key, { writable: false, configurable: false });
|
|
437
|
-
}
|
|
438
|
-
}
|
|
439
|
-
add(Object.getPrototypeOf(function* () {}));
|
|
440
|
-
add(Object.getPrototypeOf(async function () {}));
|
|
441
|
-
add(Object.getPrototypeOf(async function* () {}));
|
|
442
|
-
add(Object.getPrototypeOf(Int8Array));
|
|
443
|
-
add(Object.getPrototypeOf([][Symbol.iterator]()));
|
|
444
|
-
add(Object.getPrototypeOf(new Map()[Symbol.iterator]()));
|
|
445
|
-
add(Object.getPrototypeOf(new Set()[Symbol.iterator]()));
|
|
446
|
-
add(Object.getPrototypeOf(""[Symbol.iterator]()));
|
|
447
|
-
add(Object.getPrototypeOf(/a/[Symbol.matchAll]("")));
|
|
448
|
-
if (typeof Iterator === "function") {
|
|
449
|
-
if (typeof Iterator.prototype.map === "function") add(Object.getPrototypeOf([].values().map((x) => x)));
|
|
450
|
-
if (typeof Iterator.from === "function") add(Object.getPrototypeOf(Iterator.from({ next() {} })));
|
|
451
|
-
}
|
|
452
|
-
|
|
453
|
-
while (queue.length > 0) {
|
|
454
|
-
const object = queue.pop();
|
|
455
|
-
add(Object.getPrototypeOf(object));
|
|
456
|
-
const descriptors = Object.getOwnPropertyDescriptors(object);
|
|
457
|
-
for (const key of Reflect.ownKeys(descriptors)) {
|
|
458
|
-
const descriptor = descriptors[key];
|
|
459
|
-
if ("value" in descriptor) {
|
|
460
|
-
add(descriptor.value);
|
|
461
|
-
if (
|
|
462
|
-
descriptor.writable &&
|
|
463
|
-
descriptor.configurable &&
|
|
464
|
-
(object === Object.prototype || OVERRIDABLE.has(key))
|
|
465
|
-
) {
|
|
466
|
-
allowOverride(object, key, descriptor.value, descriptor.enumerable);
|
|
467
|
-
}
|
|
468
|
-
} else {
|
|
469
|
-
add(descriptor.get);
|
|
470
|
-
add(descriptor.set);
|
|
471
|
-
}
|
|
472
|
-
}
|
|
473
|
-
Object.freeze(object);
|
|
474
|
-
}
|
|
475
|
-
})();
|
|
476
|
-
const stringify = JSON.stringify;
|
|
477
|
-
const parse = JSON.parse;
|
|
478
|
-
const promiseThen = Promise.prototype.then;
|
|
479
|
-
const ErrorCtor = Error;
|
|
480
|
-
const TypeErrorCtor = TypeError;
|
|
481
|
-
const RangeErrorCtor = RangeError;
|
|
482
|
-
const pending = new Map();
|
|
483
|
-
let nextId = 1;
|
|
484
|
-
let finished = false;
|
|
485
|
-
// Thrown by exit() to unwind the script after it already reported success.
|
|
486
|
-
const EXIT = Object.freeze({});
|
|
487
|
-
|
|
488
|
-
function done(ok, payload, writes) {
|
|
489
|
-
if (finished) return;
|
|
490
|
-
finished = true;
|
|
491
|
-
bridge("done", ok, payload, writes);
|
|
492
|
-
}
|
|
493
|
-
|
|
494
|
-
function serialize(value) {
|
|
495
|
-
return value === undefined ? undefined : stringify(value);
|
|
496
|
-
}
|
|
497
|
-
|
|
498
|
-
// QuickJS stacks list frames only. Prefix "Name: message" like V8 so the
|
|
499
|
-
// text reads the same as a Node error, and drop this prelude's frames.
|
|
500
|
-
function errorText(error) {
|
|
501
|
-
const head = error.message ? error.name + ": " + error.message : String(error.name);
|
|
502
|
-
const frames =
|
|
503
|
-
typeof error.stack === "string"
|
|
504
|
-
? error.stack.split("\\n").filter((line) => line.trim() && !line.includes("codemode-prelude.js"))
|
|
505
|
-
: [];
|
|
506
|
-
return [head, ...frames].join("\\n");
|
|
507
|
-
}
|
|
508
|
-
|
|
509
|
-
function format(value) {
|
|
510
|
-
if (typeof value === "string") return value;
|
|
511
|
-
if (value instanceof ErrorCtor) return errorText(value);
|
|
512
|
-
try {
|
|
513
|
-
const json = stringify(value);
|
|
514
|
-
return json === undefined ? String(value) : json;
|
|
515
|
-
} catch {
|
|
516
|
-
return String(value);
|
|
517
|
-
}
|
|
518
|
-
}
|
|
519
|
-
|
|
520
|
-
// The host requires string fields. A script can set an error's name or message to anything, so
|
|
521
|
-
// they are coerced, and a value whose coercion throws gets a placeholder.
|
|
522
|
-
function describeError(error) {
|
|
523
|
-
try {
|
|
524
|
-
if (error instanceof ErrorCtor) {
|
|
525
|
-
return stringify({ name: String(error.name), message: String(error.message), stack: errorText(error) });
|
|
526
|
-
}
|
|
527
|
-
return stringify({ message: String(format(error)) });
|
|
528
|
-
} catch {
|
|
529
|
-
return stringify({ message: "The script threw a value that cannot be described" });
|
|
530
|
-
}
|
|
531
|
-
}
|
|
532
|
-
|
|
533
|
-
function caller(kind, name, spread) {
|
|
534
|
-
return (...args) =>
|
|
535
|
-
new Promise((resolve, reject) => {
|
|
536
|
-
let json;
|
|
537
|
-
try {
|
|
538
|
-
json = serialize(spread ? args : args[0]);
|
|
539
|
-
} catch (error) {
|
|
540
|
-
reject(error);
|
|
541
|
-
return;
|
|
542
|
-
}
|
|
543
|
-
const id = nextId++;
|
|
544
|
-
pending.set(id, { resolve, reject });
|
|
545
|
-
bridge(kind, id, name, json);
|
|
546
|
-
});
|
|
547
|
-
}
|
|
548
|
-
|
|
549
|
-
const tools = Object.create(null);
|
|
550
|
-
const allTools = [];
|
|
551
|
-
for (const { name, jsName, description } of parse(toolsJson)) {
|
|
552
|
-
const fn = caller("call", name);
|
|
553
|
-
// The first tool wins when two names normalize to the same identifier.
|
|
554
|
-
if (!(jsName in tools)) {
|
|
555
|
-
tools[jsName] = fn;
|
|
556
|
-
allTools.push(Object.freeze({ name: jsName, description }));
|
|
557
|
-
}
|
|
558
|
-
if (!(name in tools)) tools[name] = fn;
|
|
559
|
-
}
|
|
560
|
-
Object.freeze(tools);
|
|
561
|
-
Object.freeze(allTools);
|
|
562
|
-
|
|
563
|
-
// Reading a member that does not exist throws an error that names the close matches, instead of
|
|
564
|
-
// a later "not a function". \`in\` checks still work.
|
|
565
|
-
const comparable = (name) => name.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
566
|
-
function guard(target, label, names, hint) {
|
|
567
|
-
return new Proxy(target, {
|
|
568
|
-
get(object, property, receiver) {
|
|
569
|
-
if (typeof property !== "string" || property in object || property in Object.prototype || property === "then" || property === "toJSON") {
|
|
570
|
-
return Reflect.get(object, property, receiver);
|
|
571
|
-
}
|
|
572
|
-
const wanted = comparable(property);
|
|
573
|
-
const exact = names.filter((name) => comparable(name) === wanted);
|
|
574
|
-
const close = exact.length > 0 ? exact : names.filter((name) => wanted && (comparable(name).includes(wanted) || wanted.includes(comparable(name))));
|
|
575
|
-
let message = label + "." + property + " does not exist.";
|
|
576
|
-
if (close.length > 0) message += " Did you mean " + close.slice(0, 5).map((name) => label + "." + name).join(", ") + "?";
|
|
577
|
-
else if (names.length <= 20) message += " Available: " + names.join(", ") + ".";
|
|
578
|
-
if (hint) message += " " + hint;
|
|
579
|
-
message += ' Check for a member with "' + property + '" in ' + label + ".";
|
|
580
|
-
throw new TypeErrorCtor(message);
|
|
581
|
-
},
|
|
582
|
-
});
|
|
583
|
-
}
|
|
584
|
-
const toolsProxy = guard(
|
|
585
|
-
tools,
|
|
586
|
-
"tools",
|
|
587
|
-
allTools.map((tool) => tool.name),
|
|
588
|
-
"ALL_TOOLS lists every tool; searchTools(query) finds tools by topic.",
|
|
589
|
-
);
|
|
590
|
-
|
|
591
|
-
const namespaces = new Map();
|
|
592
|
-
for (const { name, spread } of parse(globalsJson)) {
|
|
593
|
-
const fn = caller("global", name, spread);
|
|
594
|
-
const dot = name.indexOf(".");
|
|
595
|
-
if (dot === -1) {
|
|
596
|
-
Object.defineProperty(globalThis, name, { value: fn, enumerable: true });
|
|
597
|
-
continue;
|
|
598
|
-
}
|
|
599
|
-
const namespace = name.slice(0, dot);
|
|
600
|
-
if (!namespaces.has(namespace)) namespaces.set(namespace, Object.create(null));
|
|
601
|
-
namespaces.get(namespace)[name.slice(dot + 1)] = fn;
|
|
602
|
-
}
|
|
603
|
-
for (const [namespace, members] of namespaces) {
|
|
604
|
-
Object.freeze(members);
|
|
605
|
-
const value = guard(members, namespace, Object.keys(members));
|
|
606
|
-
Object.defineProperty(globalThis, namespace, { value, enumerable: true });
|
|
607
|
-
}
|
|
608
|
-
|
|
609
|
-
// key -> JSON text. Sizes count key and JSON characters.
|
|
610
|
-
const stored = new Map(Object.entries(parse(storeJson)));
|
|
611
|
-
const writes = new Map();
|
|
612
|
-
let storedChars = 0;
|
|
613
|
-
for (const [key, json] of stored) storedChars += key.length + json.length;
|
|
614
|
-
|
|
615
|
-
const STORE_HINT =
|
|
616
|
-
"store() is for small state such as IDs or summaries. Show images with image(), keep large data in variables, or write it to a file with a tool.";
|
|
617
|
-
|
|
618
|
-
function checkKey(name, key) {
|
|
619
|
-
if (typeof key !== "string") throw new TypeError(name + "() key must be a string");
|
|
620
|
-
}
|
|
621
|
-
|
|
622
|
-
function store(key, value) {
|
|
623
|
-
checkKey("store", key);
|
|
624
|
-
const previous = stored.has(key) ? key.length + stored.get(key).length : 0;
|
|
625
|
-
if (value === undefined) {
|
|
626
|
-
stored.delete(key);
|
|
627
|
-
storedChars -= previous;
|
|
628
|
-
writes.set(key, undefined);
|
|
629
|
-
return;
|
|
630
|
-
}
|
|
631
|
-
let json;
|
|
632
|
-
try {
|
|
633
|
-
json = stringify(value);
|
|
634
|
-
} catch (error) {
|
|
635
|
-
throw new TypeError("store(" + stringify(key) + ") value is not JSON-serializable: " + format(error));
|
|
636
|
-
}
|
|
637
|
-
if (json === undefined) {
|
|
638
|
-
throw new TypeError("store(" + stringify(key) + ") value is not JSON-serializable");
|
|
639
|
-
}
|
|
640
|
-
if (json.length > ${MAX_STORE_VALUE_CHARS}) {
|
|
641
|
-
throw new RangeError(
|
|
642
|
-
"store(" + stringify(key) + ") value has " + json.length + " characters of JSON, more than the limit of ${MAX_STORE_VALUE_CHARS}. " +
|
|
643
|
-
STORE_HINT,
|
|
644
|
-
);
|
|
645
|
-
}
|
|
646
|
-
const next = storedChars - previous + key.length + json.length;
|
|
647
|
-
if (next > ${MAX_STORE_TOTAL_CHARS}) {
|
|
648
|
-
throw new RangeError(
|
|
649
|
-
"store is full: stored values would exceed ${MAX_STORE_TOTAL_CHARS} characters of JSON. Delete keys with store(key, undefined). " +
|
|
650
|
-
STORE_HINT,
|
|
651
|
-
);
|
|
652
|
-
}
|
|
653
|
-
stored.set(key, json);
|
|
654
|
-
storedChars = next;
|
|
655
|
-
writes.set(key, json);
|
|
656
|
-
}
|
|
657
|
-
|
|
658
|
-
function load(key) {
|
|
659
|
-
checkKey("load", key);
|
|
660
|
-
const json = stored.get(key);
|
|
661
|
-
return json === undefined ? undefined : parse(json);
|
|
662
|
-
}
|
|
663
|
-
|
|
664
|
-
function serializeWrites() {
|
|
665
|
-
const entries = [];
|
|
666
|
-
for (const [key, json] of writes) entries.push(json === undefined ? [key] : [key, json]);
|
|
667
|
-
return stringify(entries);
|
|
668
|
-
}
|
|
669
|
-
|
|
670
|
-
Object.defineProperty(globalThis, "store", { value: store, enumerable: true });
|
|
671
|
-
Object.defineProperty(globalThis, "load", { value: load, enumerable: true });
|
|
672
|
-
|
|
673
|
-
let outputChars = 0;
|
|
674
|
-
let outputItems = 0;
|
|
675
|
-
|
|
676
|
-
// Past the output limits the script fails: done() reports the error, so catching it does not
|
|
677
|
-
// resume output, and the host ends the script.
|
|
678
|
-
function output(kind, data, mimeType) {
|
|
679
|
-
if (finished) return;
|
|
680
|
-
outputChars += data.length;
|
|
681
|
-
outputItems++;
|
|
682
|
-
if (outputChars > ${MAX_OUTPUT_CHARS} || outputItems > ${MAX_OUTPUT_ITEMS}) {
|
|
683
|
-
const error = new RangeErrorCtor(
|
|
684
|
-
"script output exceeded the limit of ${MAX_OUTPUT_CHARS} characters or ${MAX_OUTPUT_ITEMS} text(), image(), and console calls. " +
|
|
685
|
-
"Print a summary instead, or write large data to a file with a tool.",
|
|
686
|
-
);
|
|
687
|
-
done(false, describeError(error));
|
|
688
|
-
throw error;
|
|
689
|
-
}
|
|
690
|
-
bridge("output", kind, data, mimeType);
|
|
691
|
-
}
|
|
692
|
-
|
|
693
|
-
// Primitives become their string form, everything else JSON.
|
|
694
|
-
function outputText(value) {
|
|
695
|
-
if (value === undefined || value === null || typeof value !== "object" && typeof value !== "function") {
|
|
696
|
-
return String(value);
|
|
697
|
-
}
|
|
698
|
-
const json = stringify(value);
|
|
699
|
-
return json === undefined ? String(value) : json;
|
|
700
|
-
}
|
|
701
|
-
|
|
702
|
-
function text(value) {
|
|
703
|
-
let rendered;
|
|
704
|
-
try {
|
|
705
|
-
rendered = outputText(value);
|
|
706
|
-
} catch (error) {
|
|
707
|
-
throw new TypeErrorCtor(error instanceof ErrorCtor ? error.message : String(error));
|
|
708
|
-
}
|
|
709
|
-
output("text", rendered);
|
|
710
|
-
}
|
|
711
|
-
|
|
712
|
-
function imageUrl(value) {
|
|
713
|
-
if (typeof value === "string") return value;
|
|
714
|
-
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
715
|
-
throw new TypeErrorCtor(${JSON.stringify(IMAGE_HELPER_EXPECTS)});
|
|
716
|
-
}
|
|
717
|
-
if (value.image_url !== undefined) {
|
|
718
|
-
if (typeof value.image_url !== "string") throw new TypeErrorCtor(${JSON.stringify(IMAGE_HELPER_EXPECTS)});
|
|
719
|
-
return value.image_url;
|
|
720
|
-
}
|
|
721
|
-
if (typeof value.type !== "string") throw new TypeErrorCtor(${JSON.stringify(IMAGE_HELPER_EXPECTS)});
|
|
722
|
-
if (value.type !== "image") {
|
|
723
|
-
throw new TypeErrorCtor('image only accepts MCP image blocks, got "' + value.type + '"');
|
|
724
|
-
}
|
|
725
|
-
if (typeof value.data !== "string" || value.data === "") throw new TypeErrorCtor("image expected MCP image data");
|
|
726
|
-
if (value.data.toLowerCase().startsWith("data:")) return value.data;
|
|
727
|
-
return "data:;base64," + value.data;
|
|
728
|
-
}
|
|
729
|
-
|
|
730
|
-
// Base64 of the signatures of the formats providers accept inline (PNG, JPEG except
|
|
731
|
-
// JPEG-LS, GIF, "RIFF....WEBP"). Signatures start at byte 0, so their encodings are prefixes.
|
|
732
|
-
const IMAGE_SIGNATURES = [
|
|
733
|
-
["image/png", /^iVBORw0KGg/],
|
|
734
|
-
["image/jpeg", /^[/]9j[/](?!9)/],
|
|
735
|
-
["image/gif", /^R0lGOD[dl]h/],
|
|
736
|
-
["image/webp", /^UklG.{8}RUJQ/],
|
|
737
|
-
];
|
|
738
|
-
|
|
739
|
-
function image(value) {
|
|
740
|
-
const url = imageUrl(value);
|
|
741
|
-
if (url === "") throw new TypeErrorCtor(${JSON.stringify(IMAGE_HELPER_EXPECTS)});
|
|
742
|
-
const colon = url.indexOf(":");
|
|
743
|
-
const scheme = colon === -1 ? "" : url.slice(0, colon).toLowerCase();
|
|
744
|
-
if (scheme === "http" || scheme === "https") {
|
|
745
|
-
throw new TypeErrorCtor("remote image URLs are not supported in tool outputs. Pass a base64 data URI instead");
|
|
746
|
-
}
|
|
747
|
-
const comma = url.indexOf(",");
|
|
748
|
-
const header = comma === -1 ? [] : url.slice(colon + 1, comma).split(";");
|
|
749
|
-
if (scheme !== "data" || comma === -1 || header.slice(1).every((part) => part.toLowerCase() !== "base64")) {
|
|
750
|
-
throw new TypeErrorCtor("invalid image output. Pass a base64 data URI instead");
|
|
751
|
-
}
|
|
752
|
-
// Providers reject the whole request on a bad image, and a persisted image block would be
|
|
753
|
-
// resent on every later turn. Line breaks from wrapped base64 are dropped. The declared type
|
|
754
|
-
// is ignored in favor of the detected one, as providers also reject mismatches.
|
|
755
|
-
const data = url.slice(comma + 1).replace(/\\s+/g, "");
|
|
756
|
-
if (data.length % 4 !== 0 || !/^[A-Za-z0-9+/]+={0,2}$/.test(data)) {
|
|
757
|
-
throw new TypeErrorCtor("invalid image output. The image data is not valid base64 (truncated or corrupted?)");
|
|
758
|
-
}
|
|
759
|
-
const head = data.slice(0, 16);
|
|
760
|
-
const signature = IMAGE_SIGNATURES.find(([, pattern]) => pattern.test(head));
|
|
761
|
-
if (!signature) {
|
|
762
|
-
throw new TypeErrorCtor("invalid image output. The image data is not a PNG, JPEG, GIF, or WebP image");
|
|
763
|
-
}
|
|
764
|
-
output("image", data, signature[0]);
|
|
765
|
-
}
|
|
766
|
-
|
|
767
|
-
function exit() {
|
|
768
|
-
let writesJson;
|
|
769
|
-
try {
|
|
770
|
-
writesJson = serializeWrites();
|
|
771
|
-
} catch (error) {
|
|
772
|
-
done(false, describeError(error));
|
|
773
|
-
throw EXIT;
|
|
774
|
-
}
|
|
775
|
-
done(true, undefined, writesJson);
|
|
776
|
-
throw EXIT;
|
|
777
|
-
}
|
|
778
|
-
|
|
779
|
-
const console = {};
|
|
780
|
-
for (const level of ["log", "info", "warn", "error", "debug"]) {
|
|
781
|
-
console[level] = (...args) => {
|
|
782
|
-
output("console", args.map(format).join(" "));
|
|
783
|
-
};
|
|
784
|
-
}
|
|
785
|
-
Object.freeze(console);
|
|
786
|
-
|
|
787
|
-
Object.defineProperty(globalThis, "tools", { value: toolsProxy, enumerable: true });
|
|
788
|
-
Object.defineProperty(globalThis, "ALL_TOOLS", { value: allTools, enumerable: true });
|
|
789
|
-
Object.defineProperty(globalThis, "console", { value: console, enumerable: true });
|
|
790
|
-
Object.defineProperty(globalThis, "text", { value: text, enumerable: true });
|
|
791
|
-
Object.defineProperty(globalThis, "image", { value: image, enumerable: true });
|
|
792
|
-
Object.defineProperty(globalThis, "exit", { value: exit, enumerable: true });
|
|
793
|
-
|
|
794
|
-
return {
|
|
795
|
-
settle(id, ok, payload) {
|
|
796
|
-
const entry = pending.get(id);
|
|
797
|
-
if (!entry) return;
|
|
798
|
-
pending.delete(id);
|
|
799
|
-
if (!ok) {
|
|
800
|
-
entry.reject(new ErrorCtor(payload));
|
|
801
|
-
return;
|
|
802
|
-
}
|
|
803
|
-
let value;
|
|
804
|
-
try {
|
|
805
|
-
value = payload === undefined ? undefined : parse(payload);
|
|
806
|
-
} catch (error) {
|
|
807
|
-
entry.reject(error);
|
|
808
|
-
return;
|
|
809
|
-
}
|
|
810
|
-
entry.resolve(value);
|
|
811
|
-
},
|
|
812
|
-
run(fn) {
|
|
813
|
-
let promise;
|
|
814
|
-
try {
|
|
815
|
-
promise = fn(toolsProxy, console);
|
|
816
|
-
} catch (error) {
|
|
817
|
-
done(false, describeError(error));
|
|
818
|
-
return;
|
|
819
|
-
}
|
|
820
|
-
promiseThen.call(
|
|
821
|
-
promise,
|
|
822
|
-
(value) => {
|
|
823
|
-
let json;
|
|
824
|
-
try {
|
|
825
|
-
json = serialize(value);
|
|
826
|
-
} catch (error) {
|
|
827
|
-
done(false, describeError(error));
|
|
828
|
-
return;
|
|
829
|
-
}
|
|
830
|
-
done(true, json, serializeWrites());
|
|
831
|
-
},
|
|
832
|
-
(error) => {
|
|
833
|
-
done(false, describeError(error));
|
|
834
|
-
},
|
|
835
|
-
);
|
|
836
|
-
},
|
|
837
|
-
stalled() {
|
|
838
|
-
if (finished || pending.size > 0) return false;
|
|
839
|
-
done(
|
|
840
|
-
false,
|
|
841
|
-
stringify({
|
|
842
|
-
name: "Error",
|
|
843
|
-
message:
|
|
844
|
-
"The script is waiting on a promise that can never settle: no tool call is pending, and timers do not exist here.",
|
|
845
|
-
}),
|
|
846
|
-
);
|
|
847
|
-
return true;
|
|
848
|
-
},
|
|
849
|
-
};
|
|
850
|
-
})`;
|
|
851
|
-
// node_modules/@earendil-works/pi-coding-agent/dist/extensions/codemode/execute.js
|
|
852
|
-
var ARGS_PREVIEW_CHARS = 200;
|
|
853
|
-
var ERROR_PREVIEW_CHARS = 500;
|
|
854
|
-
var MAX_CONCURRENT_MODEL_CALLS = 4;
|
|
855
|
-
var CODEMODE_MEMORY_LIMIT_BYTES = 256 * 1024 * 1024;
|
|
856
|
-
var MODEL_TYPES = new Set(["chat", "image", "classifier"]);
|
|
857
|
-
function truncateText(text, maxChars) {
|
|
858
|
-
return text.length > maxChars ? `${text.slice(0, maxChars - 3)}...` : text;
|
|
859
|
-
}
|
|
860
|
-
function previewArgs(args) {
|
|
861
|
-
if (args === undefined)
|
|
862
|
-
return "";
|
|
863
|
-
try {
|
|
864
|
-
return truncateText(JSON.stringify(args) ?? "", ARGS_PREVIEW_CHARS);
|
|
865
|
-
} catch {
|
|
866
|
-
return "";
|
|
867
|
-
}
|
|
868
|
-
}
|
|
869
|
-
function textOf(result) {
|
|
870
|
-
return (result.content ?? []).filter((block) => block.type === "text").map((block) => block.text).join(`
|
|
871
|
-
`);
|
|
872
|
-
}
|
|
873
|
-
function toModelType(value) {
|
|
874
|
-
if (typeof value === "string" && MODEL_TYPES.has(value))
|
|
875
|
-
return value;
|
|
876
|
-
throw new Error(`Unknown model type ${JSON.stringify(value)}. Use "chat", "image", or "classifier".`);
|
|
877
|
-
}
|
|
878
|
-
function toProvider(value) {
|
|
879
|
-
if (value === undefined || value === null)
|
|
880
|
-
return;
|
|
881
|
-
if (typeof value !== "string")
|
|
882
|
-
throw new Error("provider must be a string");
|
|
883
|
-
return value;
|
|
884
|
-
}
|
|
885
|
-
function toModelInfo(model) {
|
|
886
|
-
const info = { ...model };
|
|
887
|
-
delete info.headers;
|
|
888
|
-
return info;
|
|
889
|
-
}
|
|
890
|
-
function withArticle(word) {
|
|
891
|
-
return `${/^[aeiou]/.test(word) ? "an" : "a"} ${word}`;
|
|
892
|
-
}
|
|
893
|
-
function isRecord(value) {
|
|
894
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
895
|
-
}
|
|
896
|
-
function describeValue(value) {
|
|
897
|
-
if (value === undefined || value === null)
|
|
898
|
-
return String(value);
|
|
899
|
-
if (Array.isArray(value))
|
|
900
|
-
return value.length === 0 ? "an empty array" : "an array";
|
|
901
|
-
if (typeof value === "object") {
|
|
902
|
-
const keys = Object.keys(value);
|
|
903
|
-
if (keys.length === 0)
|
|
904
|
-
return "{}";
|
|
905
|
-
return `{ ${keys.slice(0, 6).join(", ")}${keys.length > 6 ? ", ..." : ""} }`;
|
|
906
|
-
}
|
|
907
|
-
return typeof value === "string" ? "a string" : `a ${typeof value}`;
|
|
908
|
-
}
|
|
909
|
-
var CLASSIFIER_CONTEXT_SHAPE = '{ state: { ... }, images?: [{ type: "image", data: <base64>, mimeType }], questions: { <id>: { type: "choice", instructions, criteria: { <label>: <meaning> } } | { type: "score", instructions, criteria: [<lowest level>, ..., <highest level>] } | { type: "bool", instructions, criteria: { true: <meaning>, false: <meaning> } } } }';
|
|
910
|
-
function checkClassifierContext(context) {
|
|
911
|
-
const fail = (problem) => new Error(`models.classify() ${problem}. Expected context: ${CLASSIFIER_CONTEXT_SHAPE}. See "Classify" in ${CODEMODE_DOCS_PATH}.`);
|
|
912
|
-
if (!isRecord(context))
|
|
913
|
-
throw fail(`expects a context object as its second argument, got ${describeValue(context)}`);
|
|
914
|
-
if (!isRecord(context.state))
|
|
915
|
-
throw fail(`context.state must be an object, got ${describeValue(context.state)}`);
|
|
916
|
-
const { images } = context;
|
|
917
|
-
if (images !== undefined) {
|
|
918
|
-
if (!Array.isArray(images))
|
|
919
|
-
throw fail(`context.images must be an array, got ${describeValue(images)}`);
|
|
920
|
-
images.forEach((image, index) => {
|
|
921
|
-
if (!isRecord(image) || image.type !== "image" || typeof image.data !== "string" || typeof image.mimeType !== "string") {
|
|
922
|
-
throw fail(`context.images[${index}] must be an image block, got ${describeValue(image)}`);
|
|
923
|
-
}
|
|
924
|
-
});
|
|
925
|
-
}
|
|
926
|
-
const { questions } = context;
|
|
927
|
-
if (!isRecord(questions) || Object.keys(questions).length === 0) {
|
|
928
|
-
throw fail(`context.questions must map question IDs to questions, got ${describeValue(questions)}`);
|
|
929
|
-
}
|
|
930
|
-
const isStrings = (values) => values.length > 0 && values.every((value) => typeof value === "string");
|
|
931
|
-
for (const [id, question] of Object.entries(questions)) {
|
|
932
|
-
const at = `context.questions.${id}`;
|
|
933
|
-
if (!isRecord(question))
|
|
934
|
-
throw fail(`${at} must be a question object, got ${describeValue(question)}`);
|
|
935
|
-
if (typeof question.instructions !== "string")
|
|
936
|
-
throw fail(`${at}.instructions must be a string`);
|
|
937
|
-
const { criteria } = question;
|
|
938
|
-
if (question.type === "choice") {
|
|
939
|
-
if (!isRecord(criteria) || !isStrings(Object.values(criteria))) {
|
|
940
|
-
throw fail(`${at} is a "choice" question, so criteria must map each label to its meaning`);
|
|
941
|
-
}
|
|
942
|
-
} else if (question.type === "score") {
|
|
943
|
-
if (!Array.isArray(criteria) || !isStrings(criteria)) {
|
|
944
|
-
throw fail(`${at} is a "score" question, so criteria must list the levels as strings, lowest first`);
|
|
945
|
-
}
|
|
946
|
-
} else if (question.type === "bool") {
|
|
947
|
-
if (!isRecord(criteria) || typeof criteria.true !== "string" || typeof criteria.false !== "string") {
|
|
948
|
-
throw fail(`${at} is a "bool" question, so criteria must be { true: string, false: string }`);
|
|
949
|
-
}
|
|
950
|
-
} else {
|
|
951
|
-
throw fail(`${at}.type must be "choice", "score", or "bool", got ${JSON.stringify(question.type)}`);
|
|
952
|
-
}
|
|
953
|
-
}
|
|
954
|
-
return context;
|
|
955
|
-
}
|
|
956
|
-
function checkImagesContext(context) {
|
|
957
|
-
const fail = (problem) => new Error(`models.generateImages() ${problem}. Expected context: { input: [{ type: "text", text: <prompt> }, ...optional { type: "image", data: <base64>, mimeType } references] }. See "Generate images" in ${CODEMODE_DOCS_PATH}.`);
|
|
958
|
-
if (!isRecord(context))
|
|
959
|
-
throw fail(`expects a context object as its second argument, got ${describeValue(context)}`);
|
|
960
|
-
const { input } = context;
|
|
961
|
-
if (!Array.isArray(input) || input.length === 0) {
|
|
962
|
-
throw fail(`context.input must be a non-empty array of blocks, got ${describeValue(input)}`);
|
|
963
|
-
}
|
|
964
|
-
input.forEach((block, index) => {
|
|
965
|
-
if (isRecord(block) && block.type === "text" && typeof block.text === "string")
|
|
966
|
-
return;
|
|
967
|
-
if (isRecord(block) && block.type === "image" && typeof block.data === "string" && typeof block.mimeType === "string") {
|
|
968
|
-
return;
|
|
969
|
-
}
|
|
970
|
-
throw fail(`context.input[${index}] must be a text or image block, got ${describeValue(block)}`);
|
|
971
|
-
});
|
|
972
|
-
return context;
|
|
973
|
-
}
|
|
974
|
-
function createLimiter(limit) {
|
|
975
|
-
let active = 0;
|
|
976
|
-
const waiting = [];
|
|
977
|
-
return async (run) => {
|
|
978
|
-
if (active >= limit)
|
|
979
|
-
await new Promise((resolve) => waiting.push(resolve));
|
|
980
|
-
active++;
|
|
981
|
-
try {
|
|
982
|
-
return await run();
|
|
983
|
-
} finally {
|
|
984
|
-
active--;
|
|
985
|
-
waiting.shift()?.();
|
|
986
|
-
}
|
|
987
|
-
};
|
|
988
|
-
}
|
|
989
|
-
function isStoreEntryData(data) {
|
|
990
|
-
if (typeof data !== "object" || data === null)
|
|
991
|
-
return false;
|
|
992
|
-
const { set, delete: deleted } = data;
|
|
993
|
-
return typeof set === "object" && set !== null && Array.isArray(deleted) && deleted.every((key) => typeof key === "string");
|
|
994
|
-
}
|
|
995
|
-
function readCodemodeStore(branch) {
|
|
996
|
-
const store = new Map;
|
|
997
|
-
for (const entry of branch) {
|
|
998
|
-
if (entry.type !== "custom" || entry.customType !== CODEMODE_STORE_ENTRY_TYPE || !isStoreEntryData(entry.data)) {
|
|
999
|
-
continue;
|
|
1000
|
-
}
|
|
1001
|
-
for (const key of entry.data.delete)
|
|
1002
|
-
store.delete(key);
|
|
1003
|
-
for (const [key, value] of Object.entries(entry.data.set))
|
|
1004
|
-
store.set(key, value);
|
|
1005
|
-
}
|
|
1006
|
-
return Object.fromEntries(store);
|
|
1007
|
-
}
|
|
1008
|
-
var DEFAULT_MAX_OUTPUT_TOKENS = 1e4;
|
|
1009
|
-
var CHARS_PER_TOKEN = 4;
|
|
1010
|
-
function valueText(value) {
|
|
1011
|
-
if (typeof value === "string")
|
|
1012
|
-
return value;
|
|
1013
|
-
return JSON.stringify(value) ?? String(value);
|
|
1014
|
-
}
|
|
1015
|
-
function formatOutput(output) {
|
|
1016
|
-
const total = output.filter((item) => item.type === "text" && !item.console).length;
|
|
1017
|
-
const items = [];
|
|
1018
|
-
const consoleLines = [];
|
|
1019
|
-
let index = 0;
|
|
1020
|
-
for (const item of output) {
|
|
1021
|
-
if (item.type === "image") {
|
|
1022
|
-
items.push(item);
|
|
1023
|
-
} else if (item.console) {
|
|
1024
|
-
consoleLines.push(item.text);
|
|
1025
|
-
} else {
|
|
1026
|
-
index++;
|
|
1027
|
-
items.push({ type: "text", text: total > 1 ? `==> text ${index}/${total} <==
|
|
1028
|
-
${item.text}` : item.text });
|
|
1029
|
-
}
|
|
1030
|
-
}
|
|
1031
|
-
if (consoleLines.length > 0) {
|
|
1032
|
-
items.push({ type: "text", text: `<console_output>
|
|
1033
|
-
${consoleLines.join(`
|
|
1034
|
-
`)}
|
|
1035
|
-
</console_output>` });
|
|
1036
|
-
}
|
|
1037
|
-
return items;
|
|
1038
|
-
}
|
|
1039
|
-
function joinAdjacentText(items) {
|
|
1040
|
-
const joined = [];
|
|
1041
|
-
for (const item of items) {
|
|
1042
|
-
const last = joined.at(-1);
|
|
1043
|
-
if (item.type === "text" && last?.type === "text") {
|
|
1044
|
-
const separator = last.text === "" || last.text.endsWith(`
|
|
1045
|
-
`) ? "" : `
|
|
1046
|
-
`;
|
|
1047
|
-
joined[joined.length - 1] = { type: "text", text: `${last.text}${separator}${item.text}` };
|
|
1048
|
-
} else {
|
|
1049
|
-
joined.push(item);
|
|
1050
|
-
}
|
|
1051
|
-
}
|
|
1052
|
-
return joined;
|
|
1053
|
-
}
|
|
1054
|
-
function formatCallSummary(calls) {
|
|
1055
|
-
if (calls.length === 0)
|
|
1056
|
-
return "No tool calls were made.";
|
|
1057
|
-
return `Tool calls made before the failure (they are not undone): ${calls.map((call) => `${call.name} (${call.status})`).join(", ")}`;
|
|
1058
|
-
}
|
|
1059
|
-
function formatError(result, calls) {
|
|
1060
|
-
const { error } = result;
|
|
1061
|
-
const head = error.kind === "script" ? error.stack ?? `${error.name ?? "Error"}: ${error.message}` : error.kind === "timeout" ? `Script timed out: ${error.message}` : error.kind === "aborted" ? `Script aborted: ${error.message}` : `Script sandbox failed: ${error.message}`;
|
|
1062
|
-
return `${head}
|
|
1063
|
-
|
|
1064
|
-
${formatCallSummary(calls)}`;
|
|
1065
|
-
}
|
|
1066
|
-
async function spillOutput(text) {
|
|
1067
|
-
try {
|
|
1068
|
-
return { path: await writeOutputFile("pi-codemode", ".txt", text) };
|
|
1069
|
-
} catch (error) {
|
|
1070
|
-
return { error: error instanceof Error ? error.message : String(error) };
|
|
1071
|
-
}
|
|
1072
|
-
}
|
|
1073
|
-
var IMAGE_EXTENSIONS = {
|
|
1074
|
-
"image/png": ".png",
|
|
1075
|
-
"image/jpeg": ".jpg",
|
|
1076
|
-
"image/gif": ".gif",
|
|
1077
|
-
"image/webp": ".webp"
|
|
1078
|
-
};
|
|
1079
|
-
async function saveImages(items) {
|
|
1080
|
-
const labels = new Map;
|
|
1081
|
-
const label = async ({ data, mimeType }) => {
|
|
1082
|
-
const bytes = Buffer.from(data, "base64");
|
|
1083
|
-
const kind = `${mimeType}, ${formatSize(bytes.length)}`;
|
|
1084
|
-
const extension = IMAGE_EXTENSIONS[mimeType];
|
|
1085
|
-
if (!extension)
|
|
1086
|
-
throw new Error(`No file extension for image type ${mimeType}`);
|
|
1087
|
-
try {
|
|
1088
|
-
const path = await writeOutputFile("pi-codemode", extension, bytes);
|
|
1089
|
-
return `[Image saved to ${path} (${kind})]`;
|
|
1090
|
-
} catch (error) {
|
|
1091
|
-
return `[Image (${kind}) could not be saved: ${error instanceof Error ? error.message : String(error)}]`;
|
|
1092
|
-
}
|
|
1093
|
-
};
|
|
1094
|
-
const result = await Promise.all(items.map(async (item) => {
|
|
1095
|
-
if (item.type !== "image")
|
|
1096
|
-
return [item];
|
|
1097
|
-
let pending = labels.get(item.data);
|
|
1098
|
-
if (!pending) {
|
|
1099
|
-
pending = label(item);
|
|
1100
|
-
labels.set(item.data, pending);
|
|
1101
|
-
}
|
|
1102
|
-
return [{ type: "text", text: await pending }, item];
|
|
1103
|
-
}));
|
|
1104
|
-
return result.flat();
|
|
1105
|
-
}
|
|
1106
|
-
async function truncateOutput(items, maxTokens) {
|
|
1107
|
-
const texts = items.filter((item) => item.type === "text").map((item) => item.text);
|
|
1108
|
-
const combined = texts.join(`
|
|
1109
|
-
`);
|
|
1110
|
-
const budget = maxTokens * CHARS_PER_TOKEN;
|
|
1111
|
-
if (texts.length === 0 || combined.length <= budget)
|
|
1112
|
-
return { items };
|
|
1113
|
-
const headChars = Math.floor(budget / 2);
|
|
1114
|
-
const tailChars = budget - headChars;
|
|
1115
|
-
const removed = combined.length - headChars - tailChars;
|
|
1116
|
-
const head = combined.slice(0, headChars);
|
|
1117
|
-
const tail = tailChars > 0 ? combined.slice(-tailChars) : "";
|
|
1118
|
-
let text = `Warning: truncated output (original token count: ${Math.ceil(combined.length / CHARS_PER_TOKEN)})
|
|
1119
|
-
Total output lines: ${combined.split(`
|
|
1120
|
-
`).length}
|
|
1121
|
-
|
|
1122
|
-
${head}…${Math.ceil(removed / CHARS_PER_TOKEN)} tokens truncated…${tail}`;
|
|
1123
|
-
const spilled = await spillOutput(combined);
|
|
1124
|
-
text += "path" in spilled ? `
|
|
1125
|
-
|
|
1126
|
-
[Full output: ${spilled.path} (read with offset/limit)]` : `
|
|
1127
|
-
|
|
1128
|
-
[Could not save the full output: ${spilled.error}]`;
|
|
1129
|
-
return {
|
|
1130
|
-
items: [{ type: "text", text }, ...items.filter((item) => item.type === "image")],
|
|
1131
|
-
..."path" in spilled ? { fullOutputPath: spilled.path } : {}
|
|
1132
|
-
};
|
|
1133
|
-
}
|
|
1134
|
-
function toScriptValue(tool, outcome) {
|
|
1135
|
-
const { result } = outcome;
|
|
1136
|
-
if (tool.outputSchema && result.structuredContent !== undefined)
|
|
1137
|
-
return result.structuredContent;
|
|
1138
|
-
const text = textOf(result);
|
|
1139
|
-
if (outcome.isError)
|
|
1140
|
-
throw new Error(text || `Tool "${tool.name}" failed`);
|
|
1141
|
-
return text;
|
|
1142
|
-
}
|
|
1143
|
-
async function executeCodemode(toolCallId, input, signal, onUpdate, ctx, options = {}) {
|
|
1144
|
-
const startedAt = performance.now();
|
|
1145
|
-
const { code, options: sourceOptions } = parseCodemodeSource(input.code);
|
|
1146
|
-
const calls = [];
|
|
1147
|
-
let modelUsage;
|
|
1148
|
-
let generatedImages = 0;
|
|
1149
|
-
const addGeneratedImages = (count) => {
|
|
1150
|
-
generatedImages += count;
|
|
1151
|
-
};
|
|
1152
|
-
const addModelUsage = (usage) => {
|
|
1153
|
-
modelUsage = modelUsage ? combineUsage(modelUsage, usage) : usage;
|
|
1154
|
-
};
|
|
1155
|
-
const snapshot = () => ({ calls: calls.map((call) => ({ ...call })) });
|
|
1156
|
-
const publish = () => onUpdate?.({ content: [], details: snapshot() });
|
|
1157
|
-
const callable = ctx ? getCodemodeCallableTools(ctx.tools) : [];
|
|
1158
|
-
const guidelines = options.getToolGuidelines?.();
|
|
1159
|
-
const samples = new Map(callable.map((tool) => [tool.name, renderToolSample(toCodemodeDeclaration(tool, guidelines?.get(tool.name)))]));
|
|
1160
|
-
const sandboxTools = callable.map((tool) => ({
|
|
1161
|
-
name: tool.name,
|
|
1162
|
-
description: samples.get(tool.name),
|
|
1163
|
-
execute: async (args, { signal: callSignal }) => {
|
|
1164
|
-
const record = {
|
|
1165
|
-
id: `${toolCallId}/?`,
|
|
1166
|
-
name: tool.name,
|
|
1167
|
-
args: previewArgs(args),
|
|
1168
|
-
status: "running"
|
|
1169
|
-
};
|
|
1170
|
-
calls.push(record);
|
|
1171
|
-
publish();
|
|
1172
|
-
const callStartedAt = performance.now();
|
|
1173
|
-
if (!ctx)
|
|
1174
|
-
throw new Error("Tool calls need a session");
|
|
1175
|
-
const outcome = await ctx.executeTool(tool.name, args, { signal: callSignal });
|
|
1176
|
-
record.id = outcome.toolCall.id;
|
|
1177
|
-
record.durationMs = performance.now() - callStartedAt;
|
|
1178
|
-
if (outcome.isError) {
|
|
1179
|
-
record.status = callSignal.aborted ? "cancelled" : "error";
|
|
1180
|
-
record.error = truncateText(textOf(outcome.result) || `Tool "${tool.name}" failed`, ERROR_PREVIEW_CHARS);
|
|
1181
|
-
} else {
|
|
1182
|
-
record.status = "ok";
|
|
1183
|
-
}
|
|
1184
|
-
publish();
|
|
1185
|
-
return toScriptValue(tool, outcome);
|
|
1186
|
-
}
|
|
1187
|
-
}));
|
|
1188
|
-
const sandbox = new CodemodeSandbox({
|
|
1189
|
-
tools: sandboxTools,
|
|
1190
|
-
globals: [
|
|
1191
|
-
...createDiscoveryGlobals(callable, samples, options),
|
|
1192
|
-
...options.models && ctx ? createModelGlobals(ctx.modelRegistry, toolCallId, calls, publish, addModelUsage, addGeneratedImages) : []
|
|
1193
|
-
],
|
|
1194
|
-
timeoutMs: sourceOptions.timeoutMs ?? Number.POSITIVE_INFINITY,
|
|
1195
|
-
memoryLimitBytes: CODEMODE_MEMORY_LIMIT_BYTES,
|
|
1196
|
-
wasm: loadQuickJSWasm(getQuickJSWasmPath()),
|
|
1197
|
-
workerUrl: getCodemodeWorkerSpecifier()
|
|
1198
|
-
});
|
|
1199
|
-
let result;
|
|
1200
|
-
try {
|
|
1201
|
-
const store = ctx ? readCodemodeStore(ctx.sessionManager.getBranch()) : {};
|
|
1202
|
-
result = await sandbox.execute(code, { signal, store });
|
|
1203
|
-
} finally {
|
|
1204
|
-
await sandbox.close();
|
|
1205
|
-
}
|
|
1206
|
-
for (const call of calls) {
|
|
1207
|
-
if (call.status === "running")
|
|
1208
|
-
call.status = "cancelled";
|
|
1209
|
-
}
|
|
1210
|
-
const scriptOutput = [...result.output];
|
|
1211
|
-
if (result.ok) {
|
|
1212
|
-
const { set, delete: deleted } = result.storeWrites;
|
|
1213
|
-
if (Object.keys(set).length > 0 || deleted.length > 0) {
|
|
1214
|
-
options.appendEntry?.(CODEMODE_STORE_ENTRY_TYPE, { set, delete: deleted });
|
|
1215
|
-
}
|
|
1216
|
-
if (result.value !== undefined)
|
|
1217
|
-
scriptOutput.push({ type: "text", text: valueText(result.value) });
|
|
1218
|
-
}
|
|
1219
|
-
const items = formatOutput(scriptOutput);
|
|
1220
|
-
if (!result.ok)
|
|
1221
|
-
items.push({ type: "text", text: `Script error:
|
|
1222
|
-
${formatError(result, calls)}` });
|
|
1223
|
-
if (generatedImages > 0 && !items.some((item) => item.type === "image")) {
|
|
1224
|
-
items.push({
|
|
1225
|
-
type: "text",
|
|
1226
|
-
text: `Note: models.generateImages() returned ${generatedImages} image${generatedImages === 1 ? "" : "s"} that the script did not show. Show each image block of result.output with image(block).`
|
|
1227
|
-
});
|
|
1228
|
-
}
|
|
1229
|
-
const truncated = await truncateOutput(joinAdjacentText(items), sourceOptions.maxOutputTokens ?? DEFAULT_MAX_OUTPUT_TOKENS);
|
|
1230
|
-
const output = joinAdjacentText(await saveImages(truncated.items));
|
|
1231
|
-
const wallTime = ((performance.now() - startedAt) / 1000).toFixed(1);
|
|
1232
|
-
const header = `${result.ok ? "Script completed" : "Script failed"}
|
|
1233
|
-
Wall time ${wallTime} seconds
|
|
1234
|
-
Output:
|
|
1235
|
-
`;
|
|
1236
|
-
const details = snapshot();
|
|
1237
|
-
if (truncated.fullOutputPath)
|
|
1238
|
-
details.fullOutputPath = truncated.fullOutputPath;
|
|
1239
|
-
return {
|
|
1240
|
-
content: [{ type: "text", text: header }, ...output],
|
|
1241
|
-
details,
|
|
1242
|
-
...modelUsage ? { usage: modelUsage } : {},
|
|
1243
|
-
...result.ok ? {} : { isError: true }
|
|
1244
|
-
};
|
|
1245
|
-
}
|
|
1246
|
-
function isNamespaceName(namespace, query) {
|
|
1247
|
-
const id = toCodemodeIdentifier(namespace);
|
|
1248
|
-
const queryId = toCodemodeIdentifier(query);
|
|
1249
|
-
const suffix = (name) => name.includes("__") ? name.slice(name.lastIndexOf("__") + 2) : undefined;
|
|
1250
|
-
return namespace === query || id === queryId || suffix(namespace) === query || suffix(id) === queryId;
|
|
1251
|
-
}
|
|
1252
|
-
function createDiscoveryGlobals(tools, samples, options) {
|
|
1253
|
-
const ranker = new Bm25Ranker;
|
|
1254
|
-
const entry = (name) => ({ name: toCodemodeIdentifier(name), description: samples.get(name) ?? "" });
|
|
1255
|
-
return [
|
|
1256
|
-
{
|
|
1257
|
-
name: "searchTools",
|
|
1258
|
-
spread: true,
|
|
1259
|
-
execute: (args) => {
|
|
1260
|
-
const [query, searchOptions] = args;
|
|
1261
|
-
if (typeof query !== "string")
|
|
1262
|
-
throw new Error("searchTools() expects a query string");
|
|
1263
|
-
const limit = searchOptions?.limit ?? DEFAULT_TOOL_SEARCH_LIMIT;
|
|
1264
|
-
if (typeof limit !== "number" || !Number.isInteger(limit) || limit <= 0) {
|
|
1265
|
-
throw new Error("searchTools() limit must be a positive integer");
|
|
1266
|
-
}
|
|
1267
|
-
const namespace = searchOptions?.namespace;
|
|
1268
|
-
if (namespace !== undefined && namespace !== null && typeof namespace !== "string") {
|
|
1269
|
-
throw new Error("searchTools() namespace must be a string");
|
|
1270
|
-
}
|
|
1271
|
-
const documents = tools.flatMap((tool) => {
|
|
1272
|
-
const toolNamespace = options.getToolNamespace?.(tool.name);
|
|
1273
|
-
if (namespace && (!toolNamespace || !isNamespaceName(toolNamespace.name, namespace)))
|
|
1274
|
-
return [];
|
|
1275
|
-
return [createToolSearchDocument(tool, toolNamespace)];
|
|
1276
|
-
});
|
|
1277
|
-
return ranker.rank(query, documents, limit).map((match) => entry(match.name));
|
|
1278
|
-
}
|
|
1279
|
-
},
|
|
1280
|
-
{
|
|
1281
|
-
name: "describeTool",
|
|
1282
|
-
spread: true,
|
|
1283
|
-
execute: (args) => {
|
|
1284
|
-
const [name] = args;
|
|
1285
|
-
if (typeof name !== "string")
|
|
1286
|
-
throw new Error("describeTool() expects a tool name");
|
|
1287
|
-
const tool = tools.find((candidate) => candidate.name === name || toCodemodeIdentifier(candidate.name) === name);
|
|
1288
|
-
return tool ? samples.get(tool.name) : undefined;
|
|
1289
|
-
}
|
|
1290
|
-
},
|
|
1291
|
-
{
|
|
1292
|
-
name: "describeNamespace",
|
|
1293
|
-
spread: true,
|
|
1294
|
-
execute: (args) => {
|
|
1295
|
-
const [name] = args;
|
|
1296
|
-
if (typeof name !== "string")
|
|
1297
|
-
throw new Error("describeNamespace() expects a namespace name");
|
|
1298
|
-
let namespace;
|
|
1299
|
-
const names = [];
|
|
1300
|
-
for (const tool of tools) {
|
|
1301
|
-
const toolNamespace = options.getToolNamespace?.(tool.name);
|
|
1302
|
-
if (!toolNamespace || !isNamespaceName(toolNamespace.name, name))
|
|
1303
|
-
continue;
|
|
1304
|
-
namespace ??= toolNamespace;
|
|
1305
|
-
names.push(toCodemodeIdentifier(tool.name));
|
|
1306
|
-
}
|
|
1307
|
-
if (!namespace)
|
|
1308
|
-
return;
|
|
1309
|
-
return {
|
|
1310
|
-
name: namespace.name,
|
|
1311
|
-
...namespace.description ? { description: namespace.description } : {},
|
|
1312
|
-
...namespace.instructions ? { instructions: namespace.instructions } : {},
|
|
1313
|
-
tools: names
|
|
1314
|
-
};
|
|
1315
|
-
}
|
|
1316
|
-
}
|
|
1317
|
-
];
|
|
1318
|
-
}
|
|
1319
|
-
function createModelGlobals(models, toolCallId, calls, publish, addUsage, addGeneratedImages) {
|
|
1320
|
-
const limit = createLimiter(MAX_CONCURRENT_MODEL_CALLS);
|
|
1321
|
-
let callCount = 0;
|
|
1322
|
-
const runModelCall = async (name, type, [model, context], checkContext, run) => {
|
|
1323
|
-
const listHint = `List the ${type} models you can use with models.getAvailableOfType("${type}").`;
|
|
1324
|
-
if (!isRecord(model) || typeof model.provider !== "string" || typeof model.id !== "string") {
|
|
1325
|
-
const undefinedHint = model === undefined || model === null ? " models.getModelOfType() returns undefined for an unknown provider or id." : "";
|
|
1326
|
-
throw new Error(`${name}() expects ${withArticle(type)} model as its first argument, got ${describeValue(model)}.${undefinedHint} ${listHint}`);
|
|
1327
|
-
}
|
|
1328
|
-
const { provider, id } = model;
|
|
1329
|
-
const ref = `${provider}/${id}`;
|
|
1330
|
-
const resolved = models.getModelOfType(type, provider, id);
|
|
1331
|
-
if (!resolved) {
|
|
1332
|
-
const actualType = [...MODEL_TYPES].find((other) => other !== type && models.getModelOfType(other, provider, id) !== undefined);
|
|
1333
|
-
throw new Error(actualType ? `"${ref}" is ${withArticle(actualType)} model, not ${withArticle(type)} model. ${listHint}` : `Unknown ${type} model "${ref}". ${listHint}`);
|
|
1334
|
-
}
|
|
1335
|
-
const checked = checkContext(context);
|
|
1336
|
-
const record = {
|
|
1337
|
-
id: `${toolCallId}/${name}/${++callCount}`,
|
|
1338
|
-
name,
|
|
1339
|
-
args: `${resolved.provider}/${resolved.id}`,
|
|
1340
|
-
status: "running"
|
|
1341
|
-
};
|
|
1342
|
-
calls.push(record);
|
|
1343
|
-
publish();
|
|
1344
|
-
const startedAt = performance.now();
|
|
1345
|
-
const result = await limit(() => run(resolved, checked));
|
|
1346
|
-
record.durationMs = performance.now() - startedAt;
|
|
1347
|
-
record.status = result.stopReason === "stop" ? "ok" : result.stopReason === "aborted" ? "cancelled" : "error";
|
|
1348
|
-
if (result.errorMessage)
|
|
1349
|
-
record.error = truncateText(result.errorMessage, ERROR_PREVIEW_CHARS);
|
|
1350
|
-
if (result.usage) {
|
|
1351
|
-
record.cost = result.usage.cost.total;
|
|
1352
|
-
addUsage(result.usage);
|
|
1353
|
-
}
|
|
1354
|
-
publish();
|
|
1355
|
-
return result;
|
|
1356
|
-
};
|
|
1357
|
-
const implementations = {
|
|
1358
|
-
"models.getModelsOfType": (args) => {
|
|
1359
|
-
const [type, provider] = args;
|
|
1360
|
-
return models.getModelsOfType(toModelType(type), toProvider(provider)).map(toModelInfo);
|
|
1361
|
-
},
|
|
1362
|
-
"models.getAvailableOfType": async (args, { signal }) => {
|
|
1363
|
-
const [type, provider] = args;
|
|
1364
|
-
const available = await models.getAvailableOfType(toModelType(type), toProvider(provider), { signal });
|
|
1365
|
-
return available.map(toModelInfo);
|
|
1366
|
-
},
|
|
1367
|
-
"models.getModelOfType": (args) => {
|
|
1368
|
-
const [type, provider, id] = args;
|
|
1369
|
-
if (typeof provider !== "string" || typeof id !== "string") {
|
|
1370
|
-
throw new Error(`models.getModelOfType(type, provider, id) expects three strings, got (${args.map(describeValue).join(", ")}). The provider and the id are separate arguments, for example models.getModelOfType("classifier", "typesafe", "jev-latest").`);
|
|
1371
|
-
}
|
|
1372
|
-
const model = models.getModelOfType(toModelType(type), provider, id);
|
|
1373
|
-
return model === undefined ? undefined : toModelInfo(model);
|
|
1374
|
-
},
|
|
1375
|
-
"models.classify": (args, { signal }) => runModelCall("models.classify", "classifier", args, checkClassifierContext, (resolved, context) => models.classify(resolved, context, { signal })),
|
|
1376
|
-
"models.generateImages": (args, { signal }) => runModelCall("models.generateImages", "image", args, checkImagesContext, async (resolved, context) => {
|
|
1377
|
-
const result = await models.generateImages(resolved, context, { signal });
|
|
1378
|
-
addGeneratedImages(result.output.filter((block) => block.type === "image").length);
|
|
1379
|
-
return result;
|
|
1380
|
-
})
|
|
1381
|
-
};
|
|
1382
|
-
return Object.entries(implementations).map(([name, execute]) => ({ name, spread: true, execute }));
|
|
1383
|
-
}
|
|
1384
|
-
export {
|
|
1385
|
-
executeCodemode,
|
|
1386
|
-
readCodemodeStore
|
|
1387
|
-
};
|