nanocodex 0.1.1 → 0.3.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/browser/host.mjs CHANGED
@@ -3,25 +3,30 @@ import { createCodeRuntime } from "../runtime/code-runtime.mjs";
3
3
  const DEFAULT_MAX_QUEUED_MESSAGES = 4_096;
4
4
  const DEFAULT_MAX_QUEUED_BYTES = 32 * 1024 * 1024;
5
5
  const DEFAULT_MAX_BUFFERED_SEND_BYTES = 16 * 1024 * 1024;
6
+ const WEBSOCKET_OPEN = 1;
6
7
 
7
8
  export function createBrowserHost(options = {}) {
8
- const WebSocketImpl = options.WebSocketImpl || globalThis.WebSocket;
9
- if (!WebSocketImpl) throw new Error("WebSocket is unavailable in this runtime");
9
+ const WebSocketImpl = options.WebSocketImpl ?? globalThis.WebSocket;
10
+ const createWebSocket = options.createWebSocket
11
+ ?? (WebSocketImpl && ((endpoint) => new WebSocketImpl(endpoint)));
12
+ if (!options.mpp && !createWebSocket) {
13
+ throw new Error("WebSocket is unavailable in this runtime");
14
+ }
10
15
  const connections = new Map();
11
16
  const code = createCodeRuntime(options.tools);
12
17
  const onEvent = options.onEvent || (() => {});
13
- const createWebSocket = options.createWebSocket || ((endpoint) => new WebSocketImpl(endpoint));
14
18
  const maxQueuedMessages = options.maxQueuedMessages ?? DEFAULT_MAX_QUEUED_MESSAGES;
15
19
  const maxQueuedBytes = options.maxQueuedBytes ?? DEFAULT_MAX_QUEUED_BYTES;
16
20
  const maxBufferedSendBytes = options.maxBufferedSendBytes ?? DEFAULT_MAX_BUFFERED_SEND_BYTES;
17
21
  const encoder = new TextEncoder();
18
22
  let nextHandle = 1;
19
23
 
20
- function connect(endpoint, _apiKey, sessionId) {
24
+ function connect(endpoint, _apiKey, sessionId, metadata = {}) {
25
+ if (options.mpp) return connectMpp(endpoint);
21
26
  return new Promise((resolve, reject) => {
22
27
  let settled = false;
23
28
  const handle = nextHandle++;
24
- const socket = createWebSocket(endpoint, sessionId);
29
+ const socket = createWebSocket(endpoint, sessionId, metadata);
25
30
  const connection = {
26
31
  socket,
27
32
  queue: [],
@@ -59,9 +64,44 @@ export function createBrowserHost(options = {}) {
59
64
  });
60
65
  }
61
66
 
67
+ async function connectMpp(endpoint) {
68
+ if (typeof options.mpp.ws !== "function") {
69
+ throw new TypeError("mpp must provide ws(endpoint)");
70
+ }
71
+ const socket = await options.mpp.ws(endpoint);
72
+ if (!socket || typeof socket.addEventListener !== "function") {
73
+ throw new TypeError("mpp.ws(endpoint) must return a WebSocket");
74
+ }
75
+ const handle = nextHandle++;
76
+ const connection = {
77
+ socket,
78
+ queue: [],
79
+ queuedBytes: 0,
80
+ waiter: undefined,
81
+ intentionallyClosed: false,
82
+ overflowed: false,
83
+ managed: true,
84
+ };
85
+ connections.set(handle, connection);
86
+ socket.addEventListener("message", (event) => {
87
+ enqueue(connection, typeof event.data === "string"
88
+ ? { kind: "text", text: event.data }
89
+ : { kind: "binary" });
90
+ });
91
+ socket.addEventListener("close", (event) => {
92
+ if (!connection.intentionallyClosed && !connection.overflowed) {
93
+ enqueue(connection, { kind: "closed", detail: `with code ${event.code ?? 1000}` });
94
+ }
95
+ });
96
+ socket.addEventListener("error", () => {
97
+ enqueue(connection, { kind: "error", detail: "MPP WebSocket connection failed" });
98
+ });
99
+ return JSON.stringify({ handle, status: 101, reasoning_included: false });
100
+ }
101
+
62
102
  function send(handle, message) {
63
103
  const connection = connections.get(handle);
64
- if (!connection || connection.socket.readyState !== WebSocketImpl.OPEN) {
104
+ if (!connection || connection.socket.readyState !== WEBSOCKET_OPEN) {
65
105
  return Promise.resolve(JSON.stringify({
66
106
  ok: false,
67
107
  reconnectable: true,
@@ -69,6 +109,10 @@ export function createBrowserHost(options = {}) {
69
109
  }));
70
110
  }
71
111
  try {
112
+ if (connection.managed) {
113
+ connection.socket.send(JSON.stringify({ mpp: "message", data: message }));
114
+ return Promise.resolve(JSON.stringify({ ok: true }));
115
+ }
72
116
  const frameBytes = encoder.encode(message).byteLength;
73
117
  if (frameBytes > maxBufferedSendBytes
74
118
  || connection.socket.bufferedAmount + frameBytes > maxBufferedSendBytes) {
@@ -83,7 +127,7 @@ export function createBrowserHost(options = {}) {
83
127
  } catch (error) {
84
128
  return Promise.resolve(JSON.stringify({
85
129
  ok: false,
86
- reconnectable: connection.socket.readyState !== WebSocketImpl.OPEN,
130
+ reconnectable: connection.socket.readyState !== WEBSOCKET_OPEN,
87
131
  error: error instanceof Error ? error.message : String(error),
88
132
  }));
89
133
  }
@@ -1,13 +1,18 @@
1
1
  export { Actions } from "../index.mjs";
2
2
  export type {
3
3
  AgentEvent,
4
+ CostStatus,
5
+ EstimatedUsdCost,
4
6
  PromptInput,
5
7
  PromptItem,
6
8
  ReasoningMode,
9
+ SessionSnapshot,
7
10
  Thinking,
8
11
  Tool,
9
12
  ToolContext,
10
13
  ToolMap,
11
14
  Turn,
15
+ TurnResult,
16
+ TurnUsage,
12
17
  } from "../types.mjs";
13
18
  export * as Agent from "./Agent.mjs";
package/index.d.mts CHANGED
@@ -4,16 +4,21 @@ export type {
4
4
  AgentActions,
5
5
  AgentEvent,
6
6
  AgentOptions,
7
+ CostStatus,
7
8
  DefaultAgent,
8
9
  EventWatcher,
10
+ EstimatedUsdCost,
9
11
  ForkOptions,
10
12
  PromptInput,
11
13
  PromptItem,
12
14
  ReasoningMode,
15
+ SessionSnapshot,
13
16
  Thinking,
14
17
  Tool,
15
18
  ToolContext,
16
19
  ToolMap,
17
20
  Turn,
21
+ TurnResult,
22
+ TurnUsage,
18
23
  WatchEventsOptions,
19
24
  } from "./types.mjs";
package/internal.mjs CHANGED
@@ -1,5 +1,6 @@
1
- const AGENT_STATE = Symbol("nanocodex.agent");
2
- const TURN_STATE = Symbol("nanocodex.turn");
1
+ const agentStates = new WeakMap();
2
+ const turnStates = new WeakMap();
3
+ const resultStates = new WeakMap();
3
4
  const hostSessions = new Map();
4
5
  const hostConnections = new Map();
5
6
  let activeHost;
@@ -41,10 +42,20 @@ export function prompt(agent, options) {
41
42
 
42
43
  export function getTurnResult(turn) {
43
44
  const state = turnState(turn);
44
- state.result ||= Promise.resolve().then(() => state.raw.result());
45
+ state.result ||= Promise.resolve()
46
+ .then(() => state.raw.result())
47
+ .then(createTurnResult);
45
48
  return state.result;
46
49
  }
47
50
 
51
+ export function getTurnSnapshot(result) {
52
+ return resultState(result).snapshot();
53
+ }
54
+
55
+ export function getTurnUsage(result) {
56
+ return resultState(result).usage();
57
+ }
58
+
48
59
  export function steer(turn, options) {
49
60
  const state = turnState(turn);
50
61
  const input = actionInput(options);
@@ -62,7 +73,7 @@ export async function fork(agent, options) {
62
73
  const at = options?.at;
63
74
  const raw = at === undefined
64
75
  ? await state.raw.fork()
65
- : await state.raw.forkFrom(turnState(at).raw);
76
+ : await state.raw.forkFrom(resultState(at).raw);
66
77
  return createAgent(raw, state.runtime);
67
78
  }
68
79
 
@@ -71,7 +82,31 @@ export async function spawn(agent) {
71
82
  return createAgent(await state.raw.spawn(), state.runtime);
72
83
  }
73
84
 
74
- export function subscribeAgentEvents(agent, listener, options = {}) {
85
+ export function setThinking(agent, thinking) {
86
+ return agentState(agent).raw.setThinking(thinking);
87
+ }
88
+
89
+ export function setFastMode(agent, enabled) {
90
+ return agentState(agent).raw.setFastMode(enabled);
91
+ }
92
+
93
+ export function compact(agent) {
94
+ return agentState(agent).raw.compact();
95
+ }
96
+
97
+ export async function shutdown(agent) {
98
+ const state = knownAgentState(agent);
99
+ if (state.shutdownPromise) return state.shutdownPromise;
100
+ if (state.disposed) throw new Error("the Nanocodex agent has been disposed");
101
+ if (typeof state.raw.shutdown !== "function") {
102
+ throw new Error("this Nanocodex runtime does not expose graceful shutdown");
103
+ }
104
+ state.disposed = true;
105
+ state.shutdownPromise = joinAgentShutdown(state);
106
+ return state.shutdownPromise;
107
+ }
108
+
109
+ export function subscribeAgentEvents(agent, listener, options = {}, onRelease) {
75
110
  const state = agentState(agent);
76
111
  if (typeof state.runtime.subscribe !== "function") {
77
112
  throw new Error("this Nanocodex runtime does not expose agent events");
@@ -79,11 +114,25 @@ export function subscribeAgentEvents(agent, listener, options = {}) {
79
114
  if (typeof listener !== "function") {
80
115
  throw new TypeError("watchAgentEvents requires a listener");
81
116
  }
82
- return state.runtime.subscribe((event) => {
117
+ const unsubscribe = state.runtime.subscribe((event, encodedLength) => {
83
118
  if (options.includeAllSessions || !event?.request_id || event.request_id === agent.sessionId) {
84
- listener(event);
119
+ listener(event, encodedLength);
85
120
  }
86
121
  });
122
+ let active = true;
123
+ const subscription = {
124
+ close(notify) {
125
+ if (!active) return;
126
+ active = false;
127
+ state.subscriptions.delete(subscription);
128
+ const errors = [];
129
+ runCleanup(errors, () => unsubscribe?.());
130
+ if (notify) runCleanup(errors, () => onRelease?.());
131
+ throwCleanupErrors(errors);
132
+ },
133
+ };
134
+ state.subscriptions.add(subscription);
135
+ return () => subscription.close(false);
87
136
  }
88
137
 
89
138
  export function toWasmConfig(options = {}) {
@@ -94,10 +143,13 @@ export function toWasmConfig(options = {}) {
94
143
  const config = { api_key: apiKey };
95
144
  copy(config, "thinking", options.thinking);
96
145
  copy(config, "reasoning_mode", options.reasoningMode);
146
+ copy(config, "fast_mode", options.fastMode);
97
147
  copy(config, "websocket_url", options.websocketUrl);
98
148
  copy(config, "api_base_url", options.apiBaseUrl);
99
149
  copy(config, "instructions", options.instructions);
100
150
  copy(config, "session_id", options.sessionId);
151
+ copy(config, "workspace", options.workspace);
152
+ copy(config, "resume", options.resume);
101
153
  return config;
102
154
  }
103
155
 
@@ -105,8 +157,10 @@ export function createEventChannel() {
105
157
  const listeners = new Set();
106
158
  return Object.freeze({
107
159
  emit(eventJson) {
160
+ if (!listeners.size) return;
108
161
  const event = typeof eventJson === "string" ? JSON.parse(eventJson) : eventJson;
109
- for (const listener of listeners) listener(event);
162
+ const encodedLength = typeof eventJson === "string" ? eventJson.length : undefined;
163
+ for (const listener of listeners) listener(event, encodedLength);
110
164
  },
111
165
  subscribe(listener) {
112
166
  listeners.add(listener);
@@ -136,9 +190,18 @@ export function releaseHostSession(host, sessionId) {
136
190
  }
137
191
 
138
192
  const hostBridge = Object.freeze({
139
- async connect(endpoint, apiKey, sessionId) {
193
+ async connect(endpoint, apiKey, accountId, fedramp, sessionId, turnState) {
140
194
  const host = requiredSessionHost(sessionId);
141
- const result = JSON.parse(await host.connect(endpoint, apiKey, sessionId));
195
+ let result;
196
+ try {
197
+ result = JSON.parse(await host.connect(endpoint, apiKey, sessionId, {
198
+ accountId: accountId ?? undefined,
199
+ fedramp,
200
+ turnState: turnState ?? undefined,
201
+ }));
202
+ } catch (error) {
203
+ throw JSON.stringify(connectFailure(error));
204
+ }
142
205
  const handle = nextHostConnection++;
143
206
  hostConnections.set(handle, { host, handle: result.handle });
144
207
  hostSessions.set(sessionId, host);
@@ -162,8 +225,12 @@ const hostBridge = Object.freeze({
162
225
  hostConnections.delete(handle);
163
226
  connection.host.close(connection.handle);
164
227
  },
165
- sleep(milliseconds) {
166
- return new Promise((resolve) => setTimeout(resolve, milliseconds));
228
+ sleep(sessionId, milliseconds) {
229
+ const host = requiredSessionHost(sessionId);
230
+ if (typeof host.sleep !== "function") {
231
+ throw new TypeError("the selected Nanocodex host must define sleep(milliseconds)");
232
+ }
233
+ return host.sleep(milliseconds);
167
234
  },
168
235
  executeCode(source, sessionId, callId) {
169
236
  return requiredSessionHost(sessionId).executeCode(source, sessionId, callId);
@@ -188,6 +255,9 @@ function createAgent(raw, runtime) {
188
255
  raw,
189
256
  runtime,
190
257
  disposed: false,
258
+ released: false,
259
+ shutdownPromise: undefined,
260
+ subscriptions: new Set(),
191
261
  sessionId: raw.sessionId,
192
262
  uid: `agent-${nextAgentUid++}`,
193
263
  };
@@ -220,14 +290,12 @@ function agentView(state, extensions) {
220
290
  return agentView(state, deepMerge(extensions, extension));
221
291
  },
222
292
  dispose() {
223
- if (state.disposed) return;
224
- state.disposed = true;
225
- state.runtime.release?.(state.raw);
226
- state.runtime.dispose(state.raw);
293
+ if (state.shutdownPromise) return;
294
+ releaseAgentState(state);
227
295
  },
228
296
  };
229
297
  agent = Object.assign(base, extensions);
230
- Object.defineProperty(agent, AGENT_STATE, { value: state });
298
+ agentStates.set(agent, state);
231
299
  return agent;
232
300
  }
233
301
 
@@ -237,11 +305,87 @@ function requiredSessionHost(sessionId) {
237
305
  return host;
238
306
  }
239
307
 
308
+ function releaseAgentState(state) {
309
+ if (state.released) return;
310
+ state.disposed = true;
311
+ state.released = true;
312
+ const errors = [];
313
+ for (const subscription of [...state.subscriptions]) {
314
+ runCleanup(errors, () => subscription.close(true));
315
+ }
316
+ runCleanup(errors, () => state.runtime.release?.(state.raw));
317
+ runCleanup(errors, () => state.runtime.dispose(state.raw));
318
+ throwCleanupErrors(errors);
319
+ }
320
+
321
+ async function joinAgentShutdown(state) {
322
+ await Promise.resolve();
323
+ let shutdownFailed = false;
324
+ let shutdownError;
325
+ try {
326
+ await state.raw.shutdown();
327
+ } catch (error) {
328
+ shutdownFailed = true;
329
+ shutdownError = error;
330
+ }
331
+
332
+ let cleanupFailed = false;
333
+ let cleanupError;
334
+ try {
335
+ releaseAgentState(state);
336
+ } catch (error) {
337
+ cleanupFailed = true;
338
+ cleanupError = error;
339
+ }
340
+
341
+ if (shutdownFailed && cleanupFailed) {
342
+ const cleanupErrors = cleanupError instanceof AggregateError
343
+ ? cleanupError.errors
344
+ : [cleanupError];
345
+ throw new AggregateError(
346
+ [shutdownError, ...cleanupErrors],
347
+ "Nanocodex driver shutdown and resource release both failed",
348
+ );
349
+ }
350
+ if (shutdownFailed) throw shutdownError;
351
+ if (cleanupFailed) throw cleanupError;
352
+ }
353
+
240
354
  function requiredActiveHost() {
241
355
  if (!activeHost) throw new Error("no Nanocodex host is active");
242
356
  return activeHost;
243
357
  }
244
358
 
359
+ function connectFailure(error) {
360
+ if (typeof error === "string") {
361
+ try {
362
+ const encoded = JSON.parse(error);
363
+ if (encoded?.kind === "handshake_rejected" || encoded?.kind === "transport") {
364
+ return encoded;
365
+ }
366
+ } catch {}
367
+ }
368
+ const status = Number(error?.status);
369
+ if (Number.isInteger(status) && status >= 100 && status <= 599) {
370
+ const retryAfter = Number(error?.retryAfter);
371
+ return {
372
+ kind: "handshake_rejected",
373
+ status,
374
+ body: typeof error?.body === "string" ? error.body : errorDetail(error),
375
+ ...(Number.isFinite(retryAfter) && retryAfter >= 0 ? { retry_after: retryAfter } : {}),
376
+ };
377
+ }
378
+ return {
379
+ kind: "transport",
380
+ detail: errorDetail(error),
381
+ reconnectable: true,
382
+ };
383
+ }
384
+
385
+ function errorDetail(error) {
386
+ return error && (error.stack || error.message) || String(error);
387
+ }
388
+
245
389
  function createTurn(raw, agent) {
246
390
  if (!raw || typeof raw.result !== "function") {
247
391
  throw new TypeError("the runtime returned an invalid Nanocodex turn handle");
@@ -258,24 +402,67 @@ function createTurn(raw, agent) {
258
402
  state.raw.free();
259
403
  },
260
404
  };
261
- Object.defineProperty(turn, TURN_STATE, { value: state });
405
+ turnStates.set(turn, state);
262
406
  return Object.freeze(turn);
263
407
  }
264
408
 
409
+ function createTurnResult(raw) {
410
+ if (
411
+ !raw
412
+ || typeof raw.finalMessage !== "string"
413
+ || typeof raw.snapshot !== "function"
414
+ || typeof raw.usage !== "function"
415
+ ) {
416
+ raw?.free?.();
417
+ throw new TypeError("the runtime returned an invalid Nanocodex turn result");
418
+ }
419
+ const state = {
420
+ raw,
421
+ snapshotValue: undefined,
422
+ usageValue: undefined,
423
+ snapshot() {
424
+ state.snapshotValue ||= freezeJson(JSON.parse(raw.snapshot()));
425
+ return state.snapshotValue;
426
+ },
427
+ usage() {
428
+ state.usageValue ||= freezeJson(JSON.parse(raw.usage()));
429
+ return state.usageValue;
430
+ },
431
+ };
432
+ const result = {
433
+ finalMessage: raw.finalMessage,
434
+ get snapshot() { return state.snapshot(); },
435
+ get usage() { return state.usage(); },
436
+ };
437
+ resultStates.set(result, state);
438
+ return Object.freeze(result);
439
+ }
440
+
265
441
  function agentState(agent) {
266
- const state = agent?.[AGENT_STATE];
267
- if (!state) throw new TypeError("expected a Nanocodex agent");
442
+ const state = knownAgentState(agent);
268
443
  if (state.disposed) throw new Error("the Nanocodex agent has been disposed");
269
444
  return state;
270
445
  }
271
446
 
447
+ function knownAgentState(agent) {
448
+ const state = agentStates.get(agent);
449
+ if (!state) throw new TypeError("expected a Nanocodex agent");
450
+ return state;
451
+ }
452
+
272
453
  function turnState(turn) {
273
- const state = turn?.[TURN_STATE];
454
+ const state = turnStates.get(turn);
274
455
  if (!state) throw new TypeError("expected a Nanocodex turn");
275
456
  if (state.disposed) throw new Error("the Nanocodex turn has been disposed");
276
457
  return state;
277
458
  }
278
459
 
460
+ function resultState(result) {
461
+ const state = resultStates.get(result);
462
+ if (!state) throw new TypeError("expected a completed Nanocodex turn result");
463
+ return state;
464
+ }
465
+
279
466
  function actionInput(options) {
280
467
  const input = options?.input;
281
468
  if (typeof input !== "string" && !Array.isArray(input)) {
@@ -301,3 +488,32 @@ function isObject(value) {
301
488
  function copy(target, key, value) {
302
489
  if (value !== undefined) target[key] = value;
303
490
  }
491
+
492
+ function runCleanup(errors, cleanup) {
493
+ try {
494
+ cleanup();
495
+ } catch (error) {
496
+ errors.push(error);
497
+ }
498
+ }
499
+
500
+ function throwCleanupErrors(errors) {
501
+ if (errors.length === 1) throw errors[0];
502
+ if (errors.length > 1) {
503
+ throw new AggregateError(errors, "multiple Nanocodex resources failed to release");
504
+ }
505
+ }
506
+
507
+ function freezeJson(value) {
508
+ if (!value || typeof value !== "object") return value;
509
+ const pending = [value];
510
+ while (pending.length) {
511
+ const current = pending.pop();
512
+ if (Object.isFrozen(current)) continue;
513
+ for (const child of Object.values(current)) {
514
+ if (child && typeof child === "object" && !Object.isFrozen(child)) pending.push(child);
515
+ }
516
+ Object.freeze(current);
517
+ }
518
+ return value;
519
+ }
package/node/Agent.d.mts CHANGED
@@ -1,6 +1,7 @@
1
1
  import type {
2
2
  AgentOptions,
3
3
  DefaultAgent,
4
+ MppSession,
4
5
  ToolMap,
5
6
  } from "../types.mjs";
6
7
 
@@ -9,14 +10,9 @@ export type Agent = DefaultAgent;
9
10
  /** Creates a Node-hosted Rust/WASM Agent. */
10
11
  export function create(options: create.Options): Promise<create.ReturnType>;
11
12
  export declare namespace create {
12
- type Options = AgentOptions & {
13
+ type Options = AgentOptions & ({ apiKey: string; mpp?: never } | { apiKey?: never; mpp: MppSession }) & {
13
14
  apiBaseUrl?: string | undefined;
14
- apiKey: string;
15
- connectTimeoutMs?: number | undefined;
16
- maxFrameBytes?: number | undefined;
17
- maxQueuedBytes?: number | undefined;
18
- maxQueuedMessages?: number | undefined;
19
- sendTimeoutMs?: number | undefined;
15
+ module?: unknown;
20
16
  tools?: ToolMap | undefined;
21
17
  websocketUrl?: string | undefined;
22
18
  };
package/node/Agent.mjs CHANGED
@@ -1,4 +1,5 @@
1
1
  import { createRequire } from "node:module";
2
+ import initWeb, { Nanocodex as WebNanocodex } from "../pkg-web/nanocodex.js";
2
3
 
3
4
  import { agentActions } from "../actions/index.mjs";
4
5
  import {
@@ -12,32 +13,51 @@ import {
12
13
  } from "../internal.mjs";
13
14
  import { createNodeHost } from "./host.mjs";
14
15
 
15
- const require = createRequire(import.meta.url);
16
- const { Nanocodex } = require("../pkg-node/nanocodex.js");
16
+ let initializedWeb;
17
+ let NodeNanocodex;
17
18
 
18
19
  export function create(options = {}) {
19
20
  const {
20
21
  thinking,
21
22
  reasoningMode,
23
+ fastMode,
22
24
  instructions,
23
25
  sessionId,
26
+ workspace,
27
+ resume,
24
28
  apiKey,
29
+ mpp,
25
30
  websocketUrl,
26
31
  apiBaseUrl,
27
- ...hostOptions
32
+ module,
33
+ tools,
28
34
  } = options;
29
35
  const events = createEventChannel();
30
- const host = createNodeHost({ ...hostOptions, onEvent: events.emit });
36
+ if (mpp !== undefined && apiKey !== undefined) {
37
+ throw new TypeError("apiKey and mpp are mutually exclusive");
38
+ }
39
+ const host = createNodeHost({
40
+ mpp,
41
+ onEvent: events.emit,
42
+ tools,
43
+ workspace: workspace ?? resume?.workspace,
44
+ });
31
45
  activateHost(host);
32
46
  const runtime = defineRuntime({
33
47
  key: "node-wasm",
34
48
  name: "Nanocodex Node WASM",
35
49
  type: "node",
36
- create(config) {
50
+ async create(config) {
51
+ activateHost(host);
52
+ const Nanocodex = module === undefined
53
+ ? loadNodeNanocodex()
54
+ : await loadWebNanocodex(module);
37
55
  activateHost(host);
38
56
  return new Nanocodex(JSON.stringify(toWasmConfig({
39
- apiKey,
40
- websocketUrl,
57
+ apiKey: apiKey ?? (mpp === undefined ? undefined : "mpp-managed"),
58
+ websocketUrl: websocketUrl ?? (mpp === undefined
59
+ ? undefined
60
+ : "wss://openai.mpp.tempo.xyz/v1/responses"),
41
61
  apiBaseUrl,
42
62
  ...config,
43
63
  })));
@@ -47,5 +67,25 @@ export function create(options = {}) {
47
67
  release: (raw) => releaseHostSession(host, raw.sessionId),
48
68
  decorate: (agent) => agent.extend(agentActions()),
49
69
  });
50
- return createAgentClient(runtime, { thinking, reasoningMode, instructions, sessionId });
70
+ return createAgentClient(runtime, {
71
+ thinking,
72
+ reasoningMode,
73
+ fastMode,
74
+ instructions,
75
+ sessionId,
76
+ workspace,
77
+ resume,
78
+ });
79
+ }
80
+
81
+ function loadNodeNanocodex() {
82
+ const require = createRequire(import.meta.url);
83
+ NodeNanocodex ||= require("../pkg-node/nanocodex.js").Nanocodex;
84
+ return NodeNanocodex;
85
+ }
86
+
87
+ async function loadWebNanocodex(module) {
88
+ initializedWeb ||= initWeb({ module_or_path: module });
89
+ await initializedWeb;
90
+ return WebNanocodex;
51
91
  }