nanocodex 0.5.0 → 0.6.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/README.md +944 -78
- package/actions/events.mjs +14 -25
- package/actions/index.d.mts +1 -0
- package/actions/index.mjs +11 -1
- package/actions/session.d.mts +29 -1
- package/actions/session.mjs +22 -0
- package/actions/turn.d.mts +15 -6
- package/actions/turn.mjs +10 -0
- package/actions/voice.d.mts +26 -0
- package/actions/voice.mjs +54 -0
- package/browser/Agent.d.mts +41 -24
- package/browser/Agent.mjs +7 -84
- package/browser/ChatGptSubscription.d.mts +7 -0
- package/browser/ChatGptSubscription.mjs +19 -0
- package/browser/InlineAgent.mjs +267 -0
- package/browser/Transport.d.mts +80 -0
- package/browser/Transport.mjs +63 -0
- package/browser/Voice.d.mts +86 -0
- package/browser/Voice.mjs +316 -0
- package/browser/VoiceSession.mjs +763 -0
- package/browser/WorkerAgent.d.mts +65 -0
- package/browser/WorkerAgent.mjs +1467 -0
- package/browser/agent.worker.mjs +3 -0
- package/browser/config.d.mts +36 -0
- package/browser/config.mjs +439 -0
- package/browser/engine.mjs +13 -0
- package/browser/harness.mjs +55 -0
- package/browser/host.d.mts +33 -5
- package/browser/host.mjs +320 -26
- package/browser/hostManagedWebSocket.d.mts +14 -0
- package/browser/hostManagedWebSocket.mjs +110 -0
- package/browser/index.d.mts +64 -1
- package/browser/index.mjs +22 -1
- package/browser/indexeddb-durability-store.mjs +210 -0
- package/browser/workspace.d.mts +16 -0
- package/browser/workspace.mjs +107 -0
- package/cloud/Client.d.mts +83 -0
- package/cloud/Client.mjs +231 -0
- package/cloud/Decorator.d.mts +31 -0
- package/cloud/Decorator.mjs +37 -0
- package/cloud/Dialog.d.mts +113 -0
- package/cloud/Dialog.mjs +366 -0
- package/cloud/Errors.d.mts +22 -0
- package/cloud/Errors.mjs +39 -0
- package/cloud/Principal.d.mts +26 -0
- package/cloud/Principal.mjs +88 -0
- package/cloud/RemoteProvider.mjs +88 -0
- package/cloud/Transport.d.mts +47 -0
- package/cloud/Transport.mjs +454 -0
- package/cloud/actions/account.d.mts +9 -0
- package/cloud/actions/account.mjs +19 -0
- package/cloud/actions/agent.d.mts +18 -0
- package/cloud/actions/agent.mjs +429 -0
- package/cloud/actions/connection.d.mts +133 -0
- package/cloud/actions/connection.mjs +580 -0
- package/cloud/actions/grant.d.mts +10 -0
- package/cloud/actions/grant.mjs +12 -0
- package/cloud/actions/index.d.mts +7 -0
- package/cloud/actions/index.mjs +7 -0
- package/cloud/actions/machineUsd.d.mts +23 -0
- package/cloud/actions/machineUsd.mjs +37 -0
- package/cloud/actions/model.d.mts +12 -0
- package/cloud/actions/model.mjs +50 -0
- package/cloud/actions/mpp.d.mts +28 -0
- package/cloud/actions/mpp.mjs +39 -0
- package/cloud/index.d.mts +30 -0
- package/cloud/index.mjs +9 -0
- package/cloud/internal.mjs +533 -0
- package/cloud/mercator.mjs +108 -0
- package/cloud/server/HostPrincipal.d.mts +34 -0
- package/cloud/server/HostPrincipal.mjs +202 -0
- package/cloud/server/index.d.mts +1 -0
- package/cloud/server/index.mjs +1 -0
- package/cloud/types.d.mts +212 -0
- package/cloudflare/Agent.d.mts +136 -0
- package/cloudflare/Agent.mjs +784 -0
- package/cloudflare/egress-subject.mjs +19 -0
- package/cloudflare/egress.d.mts +41 -0
- package/cloudflare/egress.mjs +119 -0
- package/cloudflare/event-socket.mjs +316 -0
- package/cloudflare/index.d.mts +7 -0
- package/cloudflare/index.mjs +6 -0
- package/host/Agent.d.mts +55 -0
- package/host/Agent.mjs +1 -0
- package/host/index.d.mts +71 -0
- package/host/index.mjs +10 -0
- package/index.d.mts +46 -0
- package/index.mjs +11 -0
- package/internal.mjs +673 -61
- package/managed/Agent.d.mts +459 -0
- package/managed/Agent.mjs +1824 -0
- package/managed/ManagedError.d.mts +9 -0
- package/managed/ManagedError.mjs +8 -0
- package/managed/README.md +244 -0
- package/managed/Voice.mjs +209 -0
- package/managed/index.d.mts +22 -0
- package/managed/index.mjs +2 -0
- package/managed/internal.mjs +119 -0
- package/node/Agent.d.mts +35 -8
- package/node/Agent.mjs +116 -25
- package/node/ChatGptSubscription.d.mts +9 -0
- package/node/ChatGptSubscription.mjs +17 -0
- package/node/Transport.d.mts +41 -0
- package/node/Transport.mjs +46 -0
- package/node/host.mjs +99 -17
- package/node/index.d.mts +45 -1
- package/node/index.mjs +16 -1
- package/node/workspace.d.mts +10 -0
- package/node/workspace.mjs +198 -0
- package/package.json +145 -11
- package/pkg-node/nanocodex.d.ts +523 -11
- package/pkg-node/nanocodex.js +2038 -179
- package/pkg-node/nanocodex_bg.wasm.d.ts +100 -29
- package/pkg-web/.nanocodex-bindgen-stamp +6 -0
- package/pkg-web/nanocodex-build.json +1 -0
- package/pkg-web/nanocodex.d.ts +623 -40
- package/pkg-web/nanocodex.js +2031 -178
- package/pkg-web/nanocodex_bg.js +2667 -0
- package/pkg-web/nanocodex_bg.wasm +0 -0
- package/pkg-web/nanocodex_bg.wasm.d.ts +100 -29
- package/pkg-web/nanocodex_worker.js +9 -0
- package/runtime/chatgpt-subscription.mjs +212 -0
- package/runtime/cloudflare-durability-store.d.mts +26 -0
- package/runtime/cloudflare-durability-store.mjs +14 -0
- package/runtime/code-evaluator.worker.mjs +137 -0
- package/runtime/code-runtime.mjs +1 -250
- package/runtime/durability-store.d.mts +85 -0
- package/runtime/durability-store.mjs +752 -0
- package/runtime/durability.mjs +174 -0
- package/runtime/managed-transport.mjs +461 -0
- package/runtime/mcp-runtime.mjs +534 -0
- package/runtime/postgres-durability-store.d.mts +64 -0
- package/runtime/postgres-durability-store.mjs +581 -0
- package/runtime/quickjs-evaluator.d.mts +17 -0
- package/runtime/quickjs-evaluator.mjs +236 -0
- package/runtime/response-controls.mjs +42 -0
- package/runtime/response-lanes.mjs +72 -0
- package/runtime/responses-transport.mjs +22 -0
- package/runtime/subagents.d.mts +107 -0
- package/runtime/subagents.mjs +54 -0
- package/runtime/subscription-store.d.mts +8 -0
- package/runtime/subscription-store.mjs +42 -0
- package/runtime/tempo-provider.d.mts +125 -0
- package/runtime/tempo-provider.mjs +264 -0
- package/runtime/tool-configuration.mjs +1 -0
- package/runtime/tool-router.mjs +1 -0
- package/runtime/utf8.mjs +1 -0
- package/runtime/worker-evaluator.mjs +162 -0
- package/runtime/workspace.d.mts +1 -0
- package/runtime/workspace.mjs +1 -0
- package/tools/Tools.d.mts +56 -0
- package/tools/Tools.mjs +136 -0
- package/tools/artifact.d.mts +1 -0
- package/tools/artifact.mjs +1 -0
- package/tools/attachment.mjs +1 -0
- package/tools/bash.d.mts +1 -0
- package/tools/bash.mjs +1 -0
- package/tools/browser/accountInfo.mjs +795 -0
- package/tools/browser/browserBuffer.mjs +18 -0
- package/tools/browser/browserCompiler.mjs +155 -0
- package/tools/browser/browserEgress.mjs +161 -0
- package/tools/browser/browserPython.mjs +133 -0
- package/tools/browser/browserShell.mjs +1075 -0
- package/tools/browser/browserSprintf.mjs +4 -0
- package/tools/browser/browserSsh.mjs +201 -0
- package/tools/browser/browserZlib.mjs +136 -0
- package/tools/browser/compiler.worker.mjs +160 -0
- package/tools/browser/devTunnelsSshBrowser.mjs +13 -0
- package/tools/browser/index.d.mts +209 -0
- package/tools/browser/index.mjs +174 -0
- package/tools/browser/opfsGit.mjs +248 -0
- package/tools/browser/python.worker.mjs +114 -0
- package/tools/browser/threadGit.mjs +197 -0
- package/tools/browser/unsupportedNodeRsa.mjs +5 -0
- package/tools/browser/workspace.mjs +83 -0
- package/tools/dataset.d.mts +1 -0
- package/tools/dataset.mjs +1 -0
- package/tools/datasetContract.mjs +1 -0
- package/tools/datasetEngine.mjs +1 -0
- package/tools/hostedCatalog.d.mts +1 -0
- package/tools/hostedCatalog.mjs +1 -0
- package/tools/index.d.mts +49 -0
- package/tools/index.mjs +19 -0
- package/tools/namedTool.mjs +1 -0
- package/tools/repository-workspace.d.mts +1 -0
- package/tools/repository-workspace.mjs +1 -0
- package/tools/ssh.d.mts +1 -0
- package/tools/ssh.mjs +1 -0
- package/tools/standard.mjs +1 -0
- package/tools/standardDescriptions.mjs +1 -0
- package/types.d.mts +431 -16
- package/worker/ChatGptSubscription.d.mts +9 -0
- package/worker/ChatGptSubscription.mjs +29 -0
- package/worker/index.d.mts +18 -0
- package/worker/index.mjs +5 -0
- package/pkg-node/nanocodex_bg.wasm +0 -0
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
const DEFAULT_MEMORY_LIMIT_BYTES = 64 * 1024 * 1024;
|
|
2
|
+
const DEFAULT_STACK_LIMIT_BYTES = 512 * 1024;
|
|
3
|
+
const DEFAULT_INTERRUPT_CYCLES = 1_000_000;
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Builds a Code Mode evaluator for runtimes that reject eval/new Function.
|
|
7
|
+
* Pass an asyncified QuickJS WASM module from quickjs-emscripten-core.
|
|
8
|
+
*/
|
|
9
|
+
export function createQuickJsEvaluator(quickJs, options = {}) {
|
|
10
|
+
if (!quickJs || typeof quickJs.newContext !== "function") {
|
|
11
|
+
throw new TypeError("quickJs must be an asyncified QuickJS WASM module");
|
|
12
|
+
}
|
|
13
|
+
let queue = Promise.resolve();
|
|
14
|
+
|
|
15
|
+
return (source, environment) => {
|
|
16
|
+
const evaluation = queue.then(() => evaluate(quickJs, source, environment, options));
|
|
17
|
+
queue = evaluation.catch(() => {});
|
|
18
|
+
return evaluation;
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
async function evaluate(quickJs, source, environment, options) {
|
|
23
|
+
environment.signal?.throwIfAborted();
|
|
24
|
+
const vm = quickJs.newContext();
|
|
25
|
+
const runtime = vm.runtime;
|
|
26
|
+
runtime.setMemoryLimit(options.memoryLimitBytes ?? DEFAULT_MEMORY_LIMIT_BYTES);
|
|
27
|
+
runtime.setMaxStackSize(options.stackLimitBytes ?? DEFAULT_STACK_LIMIT_BYTES);
|
|
28
|
+
let interruptCycles = 0;
|
|
29
|
+
const maxInterruptCycles = options.maxInterruptCycles ?? DEFAULT_INTERRUPT_CYCLES;
|
|
30
|
+
let aborted = environment.signal?.aborted === true;
|
|
31
|
+
const onAbort = () => { aborted = true; };
|
|
32
|
+
environment.signal?.addEventListener("abort", onAbort, { once: true });
|
|
33
|
+
runtime.setInterruptHandler(() => aborted || ++interruptCycles > maxInterruptCycles);
|
|
34
|
+
let closed = false;
|
|
35
|
+
|
|
36
|
+
try {
|
|
37
|
+
expose(vm, "__nanocodex_call_tool", (nameHandle, inputHandle) => {
|
|
38
|
+
const name = vm.getString(nameHandle);
|
|
39
|
+
const input = vm.getString(inputHandle);
|
|
40
|
+
const deferred = vm.newPromise();
|
|
41
|
+
invokeTool(environment, name, input).then((encoded) => {
|
|
42
|
+
if (closed) return;
|
|
43
|
+
vm.newString(encoded).consume(deferred.resolve);
|
|
44
|
+
runtime.executePendingJobs().unwrap();
|
|
45
|
+
});
|
|
46
|
+
return deferred.handle;
|
|
47
|
+
});
|
|
48
|
+
expose(vm, "__nanocodex_emit", (kindHandle, payloadHandle) => {
|
|
49
|
+
const kind = vm.getString(kindHandle);
|
|
50
|
+
const payload = JSON.parse(vm.getString(payloadHandle));
|
|
51
|
+
if (kind === "text") environment.text(payload);
|
|
52
|
+
else if (kind === "image") environment.image(payload.value, payload.detail);
|
|
53
|
+
else if (kind === "audio") environment.audio(payload);
|
|
54
|
+
else if (kind === "notify") environment.notify(payload);
|
|
55
|
+
else if (kind === "yield_control") environment.yield_control();
|
|
56
|
+
else if (kind === "generatedImage") environment.generatedImage(payload);
|
|
57
|
+
else throw new Error(`unknown Code Mode output kind: ${kind}`);
|
|
58
|
+
});
|
|
59
|
+
expose(vm, "__nanocodex_sleep", (delayHandle) => {
|
|
60
|
+
const deferred = vm.newPromise();
|
|
61
|
+
environment.setTimeout(() => {
|
|
62
|
+
if (closed) return;
|
|
63
|
+
deferred.resolve();
|
|
64
|
+
runtime.executePendingJobs().unwrap();
|
|
65
|
+
}, vm.getNumber(delayHandle));
|
|
66
|
+
return deferred.handle;
|
|
67
|
+
});
|
|
68
|
+
expose(vm, "__nanocodex_store", (keyHandle, valueHandle) => {
|
|
69
|
+
const key = vm.getString(keyHandle);
|
|
70
|
+
const envelope = JSON.parse(vm.getString(valueHandle));
|
|
71
|
+
environment.store(key, envelope.value);
|
|
72
|
+
});
|
|
73
|
+
expose(vm, "__nanocodex_load", (keyHandle) => {
|
|
74
|
+
const value = environment.load(vm.getString(keyHandle));
|
|
75
|
+
return vm.newString(JSON.stringify({ value }));
|
|
76
|
+
});
|
|
77
|
+
expose(vm, "__nanocodex_log", (levelHandle, valuesHandle) => {
|
|
78
|
+
const level = vm.getString(levelHandle);
|
|
79
|
+
const values = JSON.parse(vm.getString(valuesHandle));
|
|
80
|
+
const logger = typeof environment.console?.[level] === "function"
|
|
81
|
+
? environment.console[level]
|
|
82
|
+
: environment.console?.log;
|
|
83
|
+
logger?.(...values);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
const setup = guestSource(
|
|
87
|
+
source,
|
|
88
|
+
Object.keys(environment.tools),
|
|
89
|
+
environment.toolDefinitions,
|
|
90
|
+
);
|
|
91
|
+
const started = await vm.evalCodeAsync(setup, "nanocodex-code-mode.js");
|
|
92
|
+
const promise = unwrap(vm, started);
|
|
93
|
+
try {
|
|
94
|
+
const settling = vm.resolvePromise(promise);
|
|
95
|
+
runtime.executePendingJobs().unwrap();
|
|
96
|
+
const settled = await abortable(settling, environment.signal);
|
|
97
|
+
unwrap(vm, settled).dispose();
|
|
98
|
+
} finally {
|
|
99
|
+
promise.dispose();
|
|
100
|
+
}
|
|
101
|
+
} finally {
|
|
102
|
+
closed = true;
|
|
103
|
+
environment.signal?.removeEventListener("abort", onAbort);
|
|
104
|
+
vm.dispose();
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function abortable(promise, signal) {
|
|
109
|
+
if (!signal) return promise;
|
|
110
|
+
if (signal.aborted) return Promise.reject(signal.reason);
|
|
111
|
+
return new Promise((resolve, reject) => {
|
|
112
|
+
const onAbort = () => reject(signal.reason ?? new Error("Code Mode execution was cancelled"));
|
|
113
|
+
const settle = (callback, value) => {
|
|
114
|
+
signal.removeEventListener("abort", onAbort);
|
|
115
|
+
callback(value);
|
|
116
|
+
};
|
|
117
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
118
|
+
Promise.resolve(promise).then(
|
|
119
|
+
(value) => settle(resolve, value),
|
|
120
|
+
(error) => settle(reject, error),
|
|
121
|
+
);
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function expose(vm, name, handler) {
|
|
126
|
+
vm.newFunction(name, handler).consume((handle) => vm.setProp(vm.global, name, handle));
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function exposeAsync(vm, name, handler) {
|
|
130
|
+
if (typeof vm.newAsyncifiedFunction !== "function") {
|
|
131
|
+
throw new TypeError("QuickJS module must use an asyncify variant");
|
|
132
|
+
}
|
|
133
|
+
vm.newAsyncifiedFunction(name, handler).consume((handle) => vm.setProp(vm.global, name, handle));
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function unwrap(vm, result) {
|
|
137
|
+
if (result.error) {
|
|
138
|
+
const dumped = vm.dump(result.error);
|
|
139
|
+
result.error.dispose();
|
|
140
|
+
throw new Error(formatQuickJsError(dumped));
|
|
141
|
+
}
|
|
142
|
+
return result.value;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function formatQuickJsError(error) {
|
|
146
|
+
if (typeof error === "string") return error;
|
|
147
|
+
if (error && typeof error === "object") {
|
|
148
|
+
if (typeof error.stack === "string") {
|
|
149
|
+
return typeof error.message === "string" && !error.stack.includes(error.message)
|
|
150
|
+
? `${error.message}\n${error.stack}`
|
|
151
|
+
: error.stack;
|
|
152
|
+
}
|
|
153
|
+
if (typeof error.message === "string") return error.message;
|
|
154
|
+
}
|
|
155
|
+
try {
|
|
156
|
+
return JSON.stringify(error);
|
|
157
|
+
} catch {
|
|
158
|
+
return String(error);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function guestSource(source, toolNames, toolDefinitions) {
|
|
163
|
+
return `
|
|
164
|
+
const __nanocodex_exit = Symbol("exit");
|
|
165
|
+
const __nanocodex_stringify = (value) => {
|
|
166
|
+
if (typeof value === "string") return value;
|
|
167
|
+
if (value === undefined) return "undefined";
|
|
168
|
+
try { return JSON.stringify(value); } catch { return String(value); }
|
|
169
|
+
};
|
|
170
|
+
const __nanocodex_decode = (encoded) => {
|
|
171
|
+
const result = JSON.parse(encoded);
|
|
172
|
+
if (!result.ok) throw new Error(result.error);
|
|
173
|
+
return result.value;
|
|
174
|
+
};
|
|
175
|
+
const tools = Object.freeze(Object.fromEntries(
|
|
176
|
+
${JSON.stringify(toolNames)}.map((name) => [name, (input) =>
|
|
177
|
+
__nanocodex_call_tool(name, JSON.stringify(input ?? null)).then(__nanocodex_decode)])
|
|
178
|
+
));
|
|
179
|
+
const ALL_TOOLS = Object.freeze(${JSON.stringify(toolDefinitions)});
|
|
180
|
+
const text = (value) => __nanocodex_emit("text", JSON.stringify(__nanocodex_stringify(value)));
|
|
181
|
+
const image = (value, detail) =>
|
|
182
|
+
__nanocodex_emit("image", JSON.stringify({ value, detail }));
|
|
183
|
+
const audio = (value) => __nanocodex_emit("audio", JSON.stringify(value));
|
|
184
|
+
const notify = (value) => __nanocodex_emit("notify", JSON.stringify(__nanocodex_stringify(value)));
|
|
185
|
+
const yield_control = () => __nanocodex_emit("yield_control", "null");
|
|
186
|
+
const __nanocodex_timers = new Map();
|
|
187
|
+
let __nanocodex_next_timer = 1;
|
|
188
|
+
const setTimeout = (callback, delay = 0) => {
|
|
189
|
+
const id = __nanocodex_next_timer++;
|
|
190
|
+
__nanocodex_timers.set(id, callback);
|
|
191
|
+
__nanocodex_sleep(Number(delay) || 0).then(() => {
|
|
192
|
+
const callback = __nanocodex_timers.get(id);
|
|
193
|
+
__nanocodex_timers.delete(id);
|
|
194
|
+
callback?.();
|
|
195
|
+
});
|
|
196
|
+
return id;
|
|
197
|
+
};
|
|
198
|
+
const clearTimeout = (id) => __nanocodex_timers.delete(id);
|
|
199
|
+
const generatedImage = (value) =>
|
|
200
|
+
__nanocodex_emit("generatedImage", JSON.stringify(value));
|
|
201
|
+
const store = (key, value) => {
|
|
202
|
+
if (typeof key !== "string") throw new TypeError("store key must be a string");
|
|
203
|
+
__nanocodex_store(key, JSON.stringify({ value }));
|
|
204
|
+
};
|
|
205
|
+
const load = (key) => JSON.parse(__nanocodex_load(key)).value;
|
|
206
|
+
const exit = () => { throw __nanocodex_exit; };
|
|
207
|
+
const require = undefined;
|
|
208
|
+
const console = Object.freeze(Object.fromEntries(
|
|
209
|
+
["debug", "info", "log", "warn", "error"].map((level) => [level, (...values) =>
|
|
210
|
+
__nanocodex_log(level, JSON.stringify(values.map(__nanocodex_stringify)))])
|
|
211
|
+
));
|
|
212
|
+
(async () => {
|
|
213
|
+
try {
|
|
214
|
+
${source}
|
|
215
|
+
} catch (error) {
|
|
216
|
+
if (error !== __nanocodex_exit) throw error;
|
|
217
|
+
}
|
|
218
|
+
})()
|
|
219
|
+
`;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
function errorMessage(error) {
|
|
223
|
+
if (error && (error.stack || error.message)) return error.stack || error.message;
|
|
224
|
+
return String(error);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
async function invokeTool(environment, name, encodedInput) {
|
|
228
|
+
try {
|
|
229
|
+
const tool = environment.tools[name];
|
|
230
|
+
if (typeof tool !== "function") throw new Error(`unknown application tool: ${name}`);
|
|
231
|
+
const value = await tool(JSON.parse(encodedInput));
|
|
232
|
+
return JSON.stringify({ ok: true, value });
|
|
233
|
+
} catch (error) {
|
|
234
|
+
return JSON.stringify({ ok: false, error: errorMessage(error) });
|
|
235
|
+
}
|
|
236
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/** Apply stable managed response policy at the provider wire boundary, including replay. */
|
|
2
|
+
export function responseControlsSocket(socket, controls = {}) {
|
|
3
|
+
if (controls.promptCacheKey !== undefined && (typeof controls.promptCacheKey !== "string" || controls.promptCacheKey.length === 0 || controls.promptCacheKey.length > 64)) {
|
|
4
|
+
throw new TypeError("invalid prompt cache key");
|
|
5
|
+
}
|
|
6
|
+
if (controls.promptCache !== undefined && !["implicit", "explicit"].includes(controls.promptCache)) {
|
|
7
|
+
throw new TypeError("invalid prompt cache mode");
|
|
8
|
+
}
|
|
9
|
+
if (controls.outputSchema !== undefined && (!controls.outputSchema || typeof controls.outputSchema !== "object" || Array.isArray(controls.outputSchema))) {
|
|
10
|
+
throw new TypeError("output schema must be an object");
|
|
11
|
+
}
|
|
12
|
+
return new Proxy({}, {
|
|
13
|
+
get(_target, property) {
|
|
14
|
+
const target = socket;
|
|
15
|
+
if (property === "send") return (data, ...args) => {
|
|
16
|
+
const body = typeof data === "string" ? JSON.parse(data) : undefined;
|
|
17
|
+
if (body?.type === "response.create") {
|
|
18
|
+
if (controls.promptCacheKey !== undefined) body.prompt_cache_key = controls.promptCacheKey;
|
|
19
|
+
if (controls.outputSchema !== undefined) body.text = {
|
|
20
|
+
...body.text, format: { type: "json_schema", name: "managed_output", strict: true, schema: controls.outputSchema },
|
|
21
|
+
};
|
|
22
|
+
if (controls.promptCache !== undefined) {
|
|
23
|
+
body.prompt_cache_options = { mode: controls.promptCache, ttl: "30m" };
|
|
24
|
+
if (controls.promptCache === "explicit") {
|
|
25
|
+
// Cache the developer context without marking changing user input.
|
|
26
|
+
// Continuations with no prefix intentionally do not write new cache entries.
|
|
27
|
+
const developers = (Array.isArray(body.input) ? body.input : []).filter(item => item.role === "developer");
|
|
28
|
+
const text = developers.flatMap(item => Array.isArray(item.content) ? item.content : [])
|
|
29
|
+
.filter(part => part.type === "input_text").at(-1);
|
|
30
|
+
if (text) text.prompt_cache_breakpoint = { mode: "explicit" };
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return target.send(JSON.stringify(body), ...args);
|
|
34
|
+
}
|
|
35
|
+
return target.send(data, ...args);
|
|
36
|
+
};
|
|
37
|
+
const value = Reflect.get(target, property, target);
|
|
38
|
+
return typeof value === "function" ? value.bind(target) : value;
|
|
39
|
+
},
|
|
40
|
+
set(_target, property, value) { return Reflect.set(socket, property, value, socket); },
|
|
41
|
+
});
|
|
42
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/** Multiplex an already authenticated Responses WebSocket. The caller owns its lifetime. */
|
|
2
|
+
export function multiplex(socket) {
|
|
3
|
+
if (!socket || typeof socket.addEventListener !== "function" || typeof socket.send !== "function") throw new TypeError("multiplex requires a WebSocket");
|
|
4
|
+
const lanes = new Map();
|
|
5
|
+
const used = new Set();
|
|
6
|
+
const broadcast = event => { for (const lane of lanes.values()) lane.dispatchEvent(event()); };
|
|
7
|
+
const onMessage = event => {
|
|
8
|
+
try {
|
|
9
|
+
const data = typeof event.data === "string" ? event.data
|
|
10
|
+
: new TextDecoder().decode(event.data instanceof ArrayBuffer ? event.data : new Uint8Array(event.data.buffer, event.data.byteOffset, event.data.byteLength));
|
|
11
|
+
const message = JSON.parse(data);
|
|
12
|
+
if (message.stream_id !== undefined) lanes.get(message.stream_id)?.dispatchEvent(new MessageEvent("message", { data }));
|
|
13
|
+
else if (message.type === "error") broadcast(() => new MessageEvent("message", { data }));
|
|
14
|
+
} catch { broadcast(() => new Event("error")); }
|
|
15
|
+
};
|
|
16
|
+
const onOpen = () => broadcast(() => new Event("open"));
|
|
17
|
+
const onError = () => broadcast(() => new Event("error"));
|
|
18
|
+
const onClose = event => { for (const lane of [...lanes.values()]) lane.close(event.code, event.reason); };
|
|
19
|
+
socket.addEventListener("message", onMessage);
|
|
20
|
+
socket.addEventListener("open", onOpen);
|
|
21
|
+
socket.addEventListener("error", onError);
|
|
22
|
+
socket.addEventListener("close", onClose);
|
|
23
|
+
return Object.freeze({
|
|
24
|
+
lane(id) {
|
|
25
|
+
if (typeof id !== "string" || !/^[A-Za-z0-9_.-]{1,256}$/.test(id)) throw new TypeError("invalid Responses stream ID");
|
|
26
|
+
if (lanes.has(id)) return lanes.get(id);
|
|
27
|
+
if (!used.has(id) && used.size >= 32) throw new RangeError("a Responses socket supports 32 distinct named streams");
|
|
28
|
+
if (socket.readyState > 1) throw new Error("Responses socket is closed");
|
|
29
|
+
used.add(id);
|
|
30
|
+
let closed = false;
|
|
31
|
+
const lane = new EventTarget();
|
|
32
|
+
for (const type of ["open", "message", "error", "close"]) {
|
|
33
|
+
let handler = null;
|
|
34
|
+
Object.defineProperty(lane, `on${type}`, { configurable: true, enumerable: true,
|
|
35
|
+
get: () => handler,
|
|
36
|
+
set(value) { if (handler) lane.removeEventListener(type, handler); handler = typeof value === "function" ? value : null; if (handler) lane.addEventListener(type, handler); },
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
Object.defineProperties(lane, {
|
|
40
|
+
url: { get: () => socket.url },
|
|
41
|
+
protocol: { get: () => socket.protocol ?? "" },
|
|
42
|
+
extensions: { get: () => socket.extensions ?? "" },
|
|
43
|
+
CONNECTING: { value: 0 }, OPEN: { value: 1 }, CLOSING: { value: 2 }, CLOSED: { value: 3 },
|
|
44
|
+
readyState: { get: () => closed ? 3 : socket.readyState },
|
|
45
|
+
bufferedAmount: { get: () => socket.bufferedAmount },
|
|
46
|
+
binaryType: { get: () => socket.binaryType, set: value => { socket.binaryType = value; } },
|
|
47
|
+
send: { value: data => {
|
|
48
|
+
if (closed || socket.readyState !== 1) throw new Error("Responses lane is not open");
|
|
49
|
+
const body = JSON.parse(data);
|
|
50
|
+
if (body.type !== "response.create") throw new TypeError("multiplexed lanes accept response.create only");
|
|
51
|
+
if (body.stream_id !== undefined && body.stream_id !== id) throw new TypeError("request belongs to a different lane");
|
|
52
|
+
delete body.stream; delete body.background;
|
|
53
|
+
// Lineage is caller-owned; never substitute the most recent response on another lane.
|
|
54
|
+
socket.send(JSON.stringify({ ...body, stream_id: id }));
|
|
55
|
+
} },
|
|
56
|
+
close: { value: (code = 1000, reason = "lane released") => {
|
|
57
|
+
if (closed) return; closed = true; lanes.delete(id);
|
|
58
|
+
const event = new Event("close"); Object.assign(event, { code, reason, wasClean: code === 1000 });
|
|
59
|
+
lane.dispatchEvent(event);
|
|
60
|
+
} },
|
|
61
|
+
});
|
|
62
|
+
lanes.set(id, lane);
|
|
63
|
+
return lane;
|
|
64
|
+
},
|
|
65
|
+
close(code = 1000, reason = "Responses pool closed") {
|
|
66
|
+
for (const lane of [...lanes.values()]) lane.close(code, reason);
|
|
67
|
+
socket.removeEventListener("message", onMessage); socket.removeEventListener("open", onOpen);
|
|
68
|
+
socket.removeEventListener("error", onError); socket.removeEventListener("close", onClose);
|
|
69
|
+
socket.close(code, reason);
|
|
70
|
+
},
|
|
71
|
+
});
|
|
72
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
const responsesTransports = new WeakMap();
|
|
2
|
+
|
|
3
|
+
export function createResponsesTransport(setup) {
|
|
4
|
+
const transport = Object.freeze({});
|
|
5
|
+
responsesTransports.set(transport, Object.freeze({ ...setup }));
|
|
6
|
+
return transport;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function resolveResponsesTransport(transport) {
|
|
10
|
+
const setup = responsesTransports.get(transport);
|
|
11
|
+
if (setup === undefined) {
|
|
12
|
+
throw new TypeError("Agent.create requires a Responses transport");
|
|
13
|
+
}
|
|
14
|
+
return setup;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function nonEmpty(value, label) {
|
|
18
|
+
if (typeof value !== "string" || !value.trim()) {
|
|
19
|
+
throw new TypeError(`${label} must be a non-empty string`);
|
|
20
|
+
}
|
|
21
|
+
return value;
|
|
22
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import type { DefaultAgent, Thinking } from "../types.mjs";
|
|
2
|
+
|
|
3
|
+
// Adapter-specific extend() signatures do not change subagent ownership.
|
|
4
|
+
type SubagentOwner = Omit<DefaultAgent, "extend">;
|
|
5
|
+
|
|
6
|
+
declare const subagentToolBrand: unique symbol;
|
|
7
|
+
|
|
8
|
+
/** Opaque selector for the Rust-owned subagent tool set. */
|
|
9
|
+
export type Tool = Readonly<{
|
|
10
|
+
[subagentToolBrand]: true;
|
|
11
|
+
}>;
|
|
12
|
+
|
|
13
|
+
export type Subagents = readonly [Tool];
|
|
14
|
+
|
|
15
|
+
export interface Options {
|
|
16
|
+
/** Optional finite concurrency limit. Omit for unlimited active turns. */
|
|
17
|
+
maxConcurrency?: number | undefined;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export type AgentId = number;
|
|
21
|
+
export type AgentStatus =
|
|
22
|
+
| Readonly<{ state: "pending" | "running" | "interrupted" | "closing" | "closed" }>
|
|
23
|
+
| Readonly<{ state: "completed"; output: unknown }>
|
|
24
|
+
| Readonly<{ state: "failed"; error: string }>;
|
|
25
|
+
export type AgentSummary = Readonly<{
|
|
26
|
+
agent_id: AgentId;
|
|
27
|
+
role: string;
|
|
28
|
+
task: string;
|
|
29
|
+
parent_agent_id: AgentId | null;
|
|
30
|
+
status: AgentStatus;
|
|
31
|
+
last_output?: unknown;
|
|
32
|
+
}>;
|
|
33
|
+
export type JsonSchema = boolean | Readonly<Record<string, unknown>>;
|
|
34
|
+
export type SpawnOptions = Readonly<{
|
|
35
|
+
role: string;
|
|
36
|
+
task: string;
|
|
37
|
+
model?: "sol" | "terra" | "luna" | "astra" | undefined;
|
|
38
|
+
thinking?: Thinking | undefined;
|
|
39
|
+
outputSchema: JsonSchema;
|
|
40
|
+
}>;
|
|
41
|
+
export type BatchSpawnOptions = Readonly<{
|
|
42
|
+
role: string;
|
|
43
|
+
task: string;
|
|
44
|
+
outputSchema: JsonSchema;
|
|
45
|
+
}>;
|
|
46
|
+
export type SpawnReport = Readonly<{
|
|
47
|
+
agent_id: AgentId;
|
|
48
|
+
role: string;
|
|
49
|
+
status: Readonly<{ state: "running" }>;
|
|
50
|
+
}>;
|
|
51
|
+
export type WaitOptions = Readonly<{
|
|
52
|
+
agentIds: readonly AgentId[];
|
|
53
|
+
timeoutMs?: number | undefined;
|
|
54
|
+
}>;
|
|
55
|
+
export type WaitReport = Readonly<{
|
|
56
|
+
agents: readonly AgentSummary[];
|
|
57
|
+
timed_out: boolean;
|
|
58
|
+
}>;
|
|
59
|
+
export type LifecycleReport = Readonly<{ agents: readonly AgentSummary[] }>;
|
|
60
|
+
export type DirectoryEntry = AgentSummary & Readonly<{
|
|
61
|
+
can_message: boolean;
|
|
62
|
+
can_manage: boolean;
|
|
63
|
+
}>;
|
|
64
|
+
export type DirectoryOptions = Readonly<{
|
|
65
|
+
includeCompleted?: boolean | undefined;
|
|
66
|
+
includeSelf?: boolean | undefined;
|
|
67
|
+
}>;
|
|
68
|
+
export type DirectoryReport = Readonly<{ agents: readonly DirectoryEntry[] }>;
|
|
69
|
+
export type MessagePriority = "deferred" | "urgent";
|
|
70
|
+
export type MessagePurpose = "delegate" | "coordinate" | "finding" | "question" | "reply";
|
|
71
|
+
export type MessageSender =
|
|
72
|
+
| Readonly<{ kind: "root" }>
|
|
73
|
+
| Readonly<{ kind: "agent"; agent_id: AgentId }>;
|
|
74
|
+
export type SendOptions = Readonly<{
|
|
75
|
+
agentId: AgentId;
|
|
76
|
+
message: string;
|
|
77
|
+
priority?: MessagePriority | undefined;
|
|
78
|
+
purpose?: MessagePurpose | undefined;
|
|
79
|
+
inReplyTo?: number | undefined;
|
|
80
|
+
}>;
|
|
81
|
+
export type MessageReceipt = Readonly<{
|
|
82
|
+
message_id: number;
|
|
83
|
+
thread_id: number;
|
|
84
|
+
from: MessageSender;
|
|
85
|
+
to_agent_id: AgentId;
|
|
86
|
+
disposition: "started" | "queued" | "steered";
|
|
87
|
+
}>;
|
|
88
|
+
|
|
89
|
+
/** Returns a spreadable Rust-backed tool extension for an Agent's tools array. */
|
|
90
|
+
export function create(options?: Options): Subagents;
|
|
91
|
+
/** Directly invokes the canonical Rust spawn_agent handler. */
|
|
92
|
+
export function spawn(agent: SubagentOwner, options: SpawnOptions): Promise<SpawnReport>;
|
|
93
|
+
/** Atomically reserves and starts an ordered batch of canonical Rust subagents. */
|
|
94
|
+
export function spawnMany(
|
|
95
|
+
agent: SubagentOwner,
|
|
96
|
+
options: readonly BatchSpawnOptions[],
|
|
97
|
+
): Promise<readonly SpawnReport[]>;
|
|
98
|
+
/** Directly invokes the canonical Rust wait_agent handler. */
|
|
99
|
+
export function wait(agent: SubagentOwner, options: WaitOptions): Promise<WaitReport>;
|
|
100
|
+
/** Directly invokes the canonical Rust list_agents handler. */
|
|
101
|
+
export function list(agent: SubagentOwner, options?: DirectoryOptions): Promise<DirectoryReport>;
|
|
102
|
+
/** Directly invokes the canonical Rust send_agent_message handler. */
|
|
103
|
+
export function send(agent: SubagentOwner, options: SendOptions): Promise<MessageReceipt>;
|
|
104
|
+
/** Directly invokes the canonical Rust interrupt_agent handler. */
|
|
105
|
+
export function interrupt(agent: SubagentOwner, agentId: AgentId): Promise<LifecycleReport>;
|
|
106
|
+
/** Directly invokes the canonical Rust close_agent handler. */
|
|
107
|
+
export function close(agent: SubagentOwner, agentId: AgentId): Promise<LifecycleReport>;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import {
|
|
2
|
+
closeSubagent,
|
|
3
|
+
interruptSubagent,
|
|
4
|
+
listSubagents,
|
|
5
|
+
sendSubagentMessage,
|
|
6
|
+
spawnSubagent,
|
|
7
|
+
spawnSubagents,
|
|
8
|
+
waitSubagents,
|
|
9
|
+
} from "../internal.mjs";
|
|
10
|
+
import {
|
|
11
|
+
defaultSubagentMaxConcurrency,
|
|
12
|
+
subagentsBrand,
|
|
13
|
+
} from "./tool-configuration.mjs";
|
|
14
|
+
|
|
15
|
+
export function create(options = {}) {
|
|
16
|
+
if (!options || typeof options !== "object" || Array.isArray(options)) {
|
|
17
|
+
throw new TypeError("Subagents.create options must be an object");
|
|
18
|
+
}
|
|
19
|
+
const maxConcurrency = options.maxConcurrency ?? defaultSubagentMaxConcurrency;
|
|
20
|
+
if (maxConcurrency !== undefined && (!Number.isSafeInteger(maxConcurrency) || maxConcurrency < 1)) {
|
|
21
|
+
throw new TypeError("subagents maxConcurrency must be a positive safe integer");
|
|
22
|
+
}
|
|
23
|
+
return Object.freeze([Object.freeze({
|
|
24
|
+
[subagentsBrand]: Object.freeze({ maxConcurrency }),
|
|
25
|
+
})]);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function spawn(agent, options) {
|
|
29
|
+
return spawnSubagent(agent, options);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function spawnMany(agent, options) {
|
|
33
|
+
return spawnSubagents(agent, options);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function wait(agent, options) {
|
|
37
|
+
return waitSubagents(agent, options);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function list(agent, options) {
|
|
41
|
+
return listSubagents(agent, options);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function send(agent, options) {
|
|
45
|
+
return sendSubagentMessage(agent, options);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function interrupt(agent, agentId) {
|
|
49
|
+
return interruptSubagent(agent, agentId);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function close(agent, agentId) {
|
|
53
|
+
return closeSubagent(agent, agentId);
|
|
54
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export declare function subscriptionRevision(
|
|
2
|
+
value: string | bigint,
|
|
3
|
+
): import("../types.mjs").SubscriptionRevision;
|
|
4
|
+
|
|
5
|
+
export declare function createMemoryChatGptSubscriptionStore(
|
|
6
|
+
id: string,
|
|
7
|
+
initial?: import("../types.mjs").SubscriptionStoredValue,
|
|
8
|
+
): import("../types.mjs").MemoryChatGptSubscriptionStore;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
const MAX_REVISION = 18_446_744_073_709_551_615n;
|
|
2
|
+
|
|
3
|
+
export function subscriptionRevision(value) {
|
|
4
|
+
const revision = String(value);
|
|
5
|
+
if (!/^(0|[1-9][0-9]*)$/.test(revision) || BigInt(revision) > MAX_REVISION) {
|
|
6
|
+
throw new TypeError("subscription revision must be an unsigned 64-bit decimal string");
|
|
7
|
+
}
|
|
8
|
+
return revision;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function createMemoryChatGptSubscriptionStore(id, initial) {
|
|
12
|
+
if (typeof id !== "string" || !id.trim()) {
|
|
13
|
+
throw new TypeError("subscription ID must be a non-empty string");
|
|
14
|
+
}
|
|
15
|
+
let stored = Object.freeze({
|
|
16
|
+
revision: subscriptionRevision(initial?.revision ?? 0n),
|
|
17
|
+
...(initial?.payload === undefined ? {} : { payload: initial.payload }),
|
|
18
|
+
});
|
|
19
|
+
const select = (selected) => {
|
|
20
|
+
if (selected !== id) throw new Error(`unknown ChatGPT subscription: ${selected}`);
|
|
21
|
+
};
|
|
22
|
+
return Object.freeze({
|
|
23
|
+
id,
|
|
24
|
+
load(selected) {
|
|
25
|
+
select(selected);
|
|
26
|
+
return stored;
|
|
27
|
+
},
|
|
28
|
+
compareAndSwap(selected, request) {
|
|
29
|
+
select(selected);
|
|
30
|
+
const expectedRevision = subscriptionRevision(request.expectedRevision);
|
|
31
|
+
if (expectedRevision !== stored.revision) {
|
|
32
|
+
return { status: "conflict", actualRevision: stored.revision };
|
|
33
|
+
}
|
|
34
|
+
const revision = subscriptionRevision(BigInt(stored.revision) + 1n);
|
|
35
|
+
stored = Object.freeze({ revision, payload: request.payload });
|
|
36
|
+
return { status: "committed", revision };
|
|
37
|
+
},
|
|
38
|
+
snapshot() {
|
|
39
|
+
return stored;
|
|
40
|
+
},
|
|
41
|
+
});
|
|
42
|
+
}
|