nanocodex 0.6.6 → 0.6.7

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.
Files changed (114) hide show
  1. package/README.md +297 -6
  2. package/THIRD-PARTY-LICENSES +23 -0
  3. package/browser/Agent.d.mts +5 -4
  4. package/browser/Agent.mjs +4 -0
  5. package/browser/Claude.d.mts +4 -0
  6. package/browser/Claude.mjs +11 -0
  7. package/browser/InlineAgent.mjs +19 -1
  8. package/browser/host.d.mts +54 -1
  9. package/browser/host.mjs +65 -16
  10. package/browser/hostManagedWebSocket.mjs +11 -2
  11. package/browser/index.d.mts +3 -10
  12. package/browser/index.mjs +2 -5
  13. package/browser/socket-observations.mjs +203 -0
  14. package/cloud/Decorator.d.mts +2 -2
  15. package/cloud/Decorator.mjs +1 -0
  16. package/cloud/Dialog.d.mts +13 -0
  17. package/cloud/Dialog.mjs +52 -6
  18. package/cloud/Transport.d.mts +0 -11
  19. package/cloud/Transport.mjs +1 -386
  20. package/cloud/actions/account.d.mts +12 -0
  21. package/cloud/actions/account.mjs +11 -0
  22. package/cloud/actions/agent.mjs +3 -0
  23. package/cloud/actions/connection.d.mts +3 -1
  24. package/cloud/actions/connection.mjs +19 -4
  25. package/cloud/index.d.mts +1 -1
  26. package/cloud/index.mjs +1 -1
  27. package/cloud/internal.mjs +19 -4
  28. package/cloud/mercator.mjs +1 -1
  29. package/cloud/mock.d.mts +17 -0
  30. package/cloud/mock.mjs +392 -0
  31. package/cloud/types.d.mts +3 -1
  32. package/cloudflare/Agent.d.mts +2 -0
  33. package/cloudflare/Agent.mjs +88 -2
  34. package/cloudflare/agent-settings.d.mts +1 -1
  35. package/cloudflare/agent-settings.mjs +9 -0
  36. package/cloudflare/egress.mjs +9 -6
  37. package/cloudflare/gateway-responses.d.mts +1 -0
  38. package/cloudflare/gateway-responses.mjs +2 -2
  39. package/cloudflare/managed-auth.d.mts +4 -4
  40. package/cloudflare/managed-auth.mjs +14 -2
  41. package/cloudflare/managed-live.d.mts +6 -0
  42. package/cloudflare/managed-live.mjs +56 -1
  43. package/cloudflare/workers-ai-responses.d.mts +2 -1
  44. package/cloudflare/workers-ai-responses.mjs +80 -6
  45. package/host/Agent.d.mts +6 -2
  46. package/host/Claude.d.mts +3 -0
  47. package/host/Claude.mjs +2 -0
  48. package/host/index.d.mts +6 -10
  49. package/host/index.mjs +2 -5
  50. package/index.d.mts +11 -10
  51. package/index.mjs +0 -5
  52. package/internal.mjs +28 -3
  53. package/managed/Agent.d.mts +6 -2
  54. package/managed/Agent.mjs +39 -5
  55. package/managed/ManagedError.d.mts +3 -0
  56. package/node/Agent.d.mts +6 -2
  57. package/node/Agent.mjs +14 -1
  58. package/node/Claude.d.mts +4 -0
  59. package/node/Claude.mjs +24 -0
  60. package/node/host.mjs +18 -9
  61. package/node/index.d.mts +6 -10
  62. package/node/index.mjs +2 -5
  63. package/package.json +41 -9
  64. package/pkg-node/nanocodex.d.ts +109 -0
  65. package/pkg-node/nanocodex.js +818 -51
  66. package/pkg-node/nanocodex_bg.wasm.d.ts +47 -3
  67. package/pkg-web/.nanocodex-bindgen-stamp +3 -3
  68. package/pkg-web/nanocodex-build.json +1 -1
  69. package/pkg-web/nanocodex.d.ts +156 -3
  70. package/pkg-web/nanocodex.js +813 -51
  71. package/pkg-web/nanocodex_bg.js +813 -51
  72. package/pkg-web/nanocodex_bg.wasm +0 -0
  73. package/pkg-web/nanocodex_bg.wasm.d.ts +47 -3
  74. package/pkg-web/nanocodex_worker.js +1 -1
  75. package/runtime/claude-host.mjs +308 -0
  76. package/runtime/claude-subscription.mjs +79 -0
  77. package/runtime/claude.d.mts +112 -0
  78. package/runtime/claude.mjs +221 -0
  79. package/runtime/code-effect-identity.mjs +123 -0
  80. package/runtime/harnesses.mjs +51 -0
  81. package/runtime/mcp-payment.mjs +8 -0
  82. package/runtime/mcp-runtime.d.mts +10 -0
  83. package/runtime/mcp-runtime.mjs +242 -57
  84. package/runtime/mercator.mjs +2 -0
  85. package/runtime/quickjs-evaluator.mjs +23 -11
  86. package/runtime/response-controls.mjs +1 -6
  87. package/runtime/responses-http.mjs +4 -2
  88. package/runtime/retry-after.d.mts +10 -0
  89. package/runtime/retry-after.mjs +86 -0
  90. package/runtime/steer-receipt.mjs +8 -1
  91. package/runtime/subagent-routing.d.mts +4 -2
  92. package/runtime/subagent-routing.mjs +7 -2
  93. package/runtime/subagents.d.mts +8 -3
  94. package/services/account.d.mts +76 -0
  95. package/services/account.mjs +151 -0
  96. package/services/hosted.mjs +72 -0
  97. package/services/index.d.mts +95 -0
  98. package/services/index.mjs +192 -0
  99. package/services/scope.mjs +36 -0
  100. package/{runtime/tempo-provider.d.mts → tempo/index.d.mts} +20 -1
  101. package/{runtime/tempo-provider.mjs → tempo/index.mjs} +39 -3
  102. package/tools/browser/browserShell.mjs +18 -8
  103. package/tools/browser/python.worker.mjs +17 -1
  104. package/tools/browser/threadGit.mjs +12 -2
  105. package/tools/environment.d.mts +8 -0
  106. package/tools/environment.mjs +10 -0
  107. package/tools/index.d.mts +6 -3
  108. package/types.d.mts +104 -4
  109. package/worker/Claude.d.mts +4 -0
  110. package/worker/Claude.mjs +11 -0
  111. package/worker/ClaudeSubscription.d.mts +24 -0
  112. package/worker/ClaudeSubscription.mjs +9 -0
  113. package/worker/index.d.mts +9 -0
  114. package/worker/index.mjs +4 -0
package/host/index.mjs CHANGED
@@ -1,10 +1,7 @@
1
1
  export * as Actions from "../actions/index.mjs";
2
2
  export { createQuickJsEvaluator } from "../runtime/quickjs-evaluator.mjs";
3
- export {
4
- createTempoProvider,
5
- createTempoProviderFromAccounts,
6
- DEFAULT_MERCATOR_MCP_URL,
7
- } from "../runtime/tempo-provider.mjs";
8
3
  export * as Agent from "./Agent.mjs";
9
4
  export * as Subagents from "../runtime/subagents.mjs";
10
5
  export * as Transport from "../browser/Transport.mjs";
6
+
7
+ export * as Claude from "./Claude.mjs";
package/index.d.mts CHANGED
@@ -8,16 +8,6 @@ export { createTools } from "./tools/Tools.mjs";
8
8
  export type { Tools, AttachmentClient, AttachmentTarget, AttachmentTransport } from "./tools/Tools.mjs";
9
9
  export type { HostedMachine } from "./tools/hostedCatalog.mjs";
10
10
  export type { AsyncQuickJsModule, QuickJsEvaluatorOptions } from "./runtime/quickjs-evaluator.mjs";
11
- export {
12
- createTempoProvider,
13
- createTempoProviderFromAccounts,
14
- DEFAULT_MERCATOR_MCP_URL,
15
- } from "./runtime/tempo-provider.mjs";
16
- export type {
17
- AccountsTempoProviderOptions,
18
- AccountsWallet,
19
- TempoProvider,
20
- } from "./runtime/tempo-provider.mjs";
21
11
  export type {
22
12
  Agent,
23
13
  AgentActions,
@@ -35,6 +25,9 @@ export type {
35
25
  ChatGptSubscriptionStore,
36
26
  CostStatus,
37
27
  CodeEvaluator,
28
+ CodeEffectContext,
29
+ CodeEffectReceipt,
30
+ CodeEffectJournal,
38
31
  CodeEvaluatorEnvironment,
39
32
  DefaultAgent,
40
33
  EventWatcher,
@@ -43,6 +36,8 @@ export type {
43
36
  ForkOptions,
44
37
  McpClient,
45
38
  McpPayment,
39
+ PaidMcpPayment,
40
+ McpPrivateResultPolicy,
46
41
  McpServer,
47
42
  McpServers,
48
43
  McpTool,
@@ -70,3 +65,9 @@ export type {
70
65
  TurnUsage,
71
66
  WatchEventsOptions,
72
67
  } from "./types.mjs";
68
+
69
+ export type {
70
+ Options as ClaudeSubscriptionOptions,
71
+ Subscription as ClaudeSubscriptionHandle,
72
+ Status as ClaudeSubscriptionStatus,
73
+ } from "./worker/ClaudeSubscription.mjs";
package/index.mjs CHANGED
@@ -5,8 +5,3 @@ export {
5
5
  } from "./runtime/subscription-store.mjs";
6
6
  export { createQuickJsEvaluator } from "./runtime/quickjs-evaluator.mjs";
7
7
  export { createTools } from "./tools/Tools.mjs";
8
- export {
9
- createTempoProvider,
10
- createTempoProviderFromAccounts,
11
- DEFAULT_MERCATOR_MCP_URL,
12
- } from "./runtime/tempo-provider.mjs";
package/internal.mjs CHANGED
@@ -110,6 +110,14 @@ export async function routePrompt(agent, options) {
110
110
  return raw === undefined ? undefined : createTurn(raw, agent);
111
111
  }
112
112
 
113
+ /** Internal host lifecycle identity; accepted() retains its public durable-only contract. */
114
+ export function getTurnHostId(turn) {
115
+ const state = turnState(turn);
116
+ return typeof state.raw.hostTurnId === "function"
117
+ ? Promise.resolve(state.raw.hostTurnId())
118
+ : awaitTurnAcceptance(turn);
119
+ }
120
+
113
121
  export function getTurnResult(turn) {
114
122
  const state = turnState(turn);
115
123
  if (!state.result) {
@@ -321,6 +329,7 @@ export function toWasmConfig(options = {}) {
321
329
  copy(config, "thinking", options.thinking);
322
330
  copy(config, "reasoning_mode", options.reasoningMode);
323
331
  copy(config, "fast_mode", options.fastMode);
332
+ copy(config, "instant_tool_steering", options.instantToolSteering);
324
333
  copy(config, "stateless_http", options.stateless);
325
334
  copy(config, "subagent_routing", options.subagentRouting);
326
335
  copy(config, "websocket_warmup", options.websocketWarmup);
@@ -330,6 +339,7 @@ export function toWasmConfig(options = {}) {
330
339
  copy(config, "instructions", options.instructions);
331
340
  copy(config, "additional_instructions", options.additionalInstructions);
332
341
  copy(config, "session_id", options.sessionId);
342
+ copy(config, "prompt_cache_key", options.promptCacheKey);
333
343
  copy(config, "workspace", options.workspace);
334
344
  if (options.executionEnvironment !== undefined) {
335
345
  const environment = options.executionEnvironment;
@@ -351,6 +361,7 @@ export function toWasmConfig(options = {}) {
351
361
  copy(config, "durability_host_id", options.durabilityHostId);
352
362
  copy(config, "terminal_receipt_retention", options.terminalReceiptRetention);
353
363
  copy(config, "subagents", options.subagents);
364
+ copy(config, "claude_harness", options.claudeHarness);
354
365
  copy(config, "host_definition_id", options.hostDefinitionId);
355
366
  copy(config, "before_compaction", options.beforeCompaction);
356
367
  return config;
@@ -463,6 +474,12 @@ export function releaseDefinitionHost(id) {
463
474
  }
464
475
 
465
476
  const hostBridge = Object.freeze({
477
+ claudeAuth(hostDefinitionId) {
478
+ return requiredDefinitionHost(hostDefinitionId).claudeAuthHeaders();
479
+ },
480
+ executeClaudeTool(hostDefinitionId, name, input, sessionId, callId, model, turnId, localDefinitions, executeLocalTool) {
481
+ return requiredDefinitionHost(hostDefinitionId).executeClaudeTool(name, input, sessionId, callId, model, turnId, localDefinitions, executeLocalTool);
482
+ },
466
483
  httpOpen(endpoint, apiKey, accountId, fedramp, sessionId, threadId, turnState, body) {
467
484
  const host = requiredSessionHost(threadId);
468
485
  if (typeof host.httpOpen !== "function") {
@@ -592,8 +609,8 @@ const hostBridge = Object.freeze({
592
609
  host.releaseSession(sessionId);
593
610
  releaseHostSession(host, sessionId);
594
611
  },
595
- executeCode(source, sessionId, callId, model, turnId) {
596
- return requiredSessionHost(sessionId).executeCode(source, sessionId, callId, model, turnId);
612
+ executeCode(source, sessionId, callId, model, turnId, localDefinitions, executeLocalTool) {
613
+ return requiredSessionHost(sessionId).executeCode(source, sessionId, callId, model, turnId, localDefinitions, executeLocalTool);
597
614
  },
598
615
  waitCode(input, sessionId, callId) {
599
616
  return requiredSessionHost(sessionId).waitCode(input, sessionId, callId);
@@ -604,6 +621,12 @@ const hostBridge = Object.freeze({
604
621
  executeTool(name, input, sessionId, callId, model, turnId) {
605
622
  return requiredSessionHost(sessionId).executeTool(name, input, sessionId, callId, model, turnId);
606
623
  },
624
+ preemptCode(sessionId, callId) {
625
+ return hostSessions.get(sessionId)?.preemptCode?.(sessionId, callId) ?? false;
626
+ },
627
+ preemptCodeTurn(sessionId) {
628
+ return hostSessions.get(sessionId)?.preemptCodeTurn?.(sessionId) ?? 0;
629
+ },
607
630
  beginCodeTurn(sessionId) {
608
631
  hostSessions.get(sessionId)?.beginCodeTurn?.(sessionId);
609
632
  },
@@ -1021,7 +1044,9 @@ function connectFailure(error) {
1021
1044
  kind: "handshake_rejected",
1022
1045
  status,
1023
1046
  body: typeof error?.body === "string" ? error.body : errorDetail(error),
1024
- ...(Number.isFinite(retryAfter) && retryAfter >= 0 ? { retry_after: retryAfter } : {}),
1047
+ ...(error?.retryAfter !== undefined && Number.isFinite(retryAfter) && retryAfter >= 0 ? { retry_after: retryAfter } : {}),
1048
+ ...(Number.isSafeInteger(error?.retry_after_deadline_ms) && error.retry_after_deadline_ms >= 0
1049
+ ? { retry_after_deadline_ms: error.retry_after_deadline_ms } : {}),
1025
1050
  };
1026
1051
  }
1027
1052
  return {
@@ -73,7 +73,7 @@ export type Options = Readonly<{
73
73
  }>;
74
74
 
75
75
  export type CreateSettings = Readonly<{
76
- model: Model;
76
+ model: Model | "claude-sonnet-4-6" | "claude-opus-4-6" | "claude-sonnet-5-5" | "claude-opus-5-5";
77
77
  thinking: Thinking;
78
78
  reasoningMode: ReasoningMode;
79
79
  fastMode: boolean;
@@ -89,6 +89,8 @@ export type Environment = Readonly<{
89
89
  network?: NetworkPolicy;
90
90
  }>;
91
91
  export type Configuration = Readonly<{
92
+ /** Yield active exec/wait observers on accepted steering; default false. */
93
+ instant_tool_steering?: boolean;
92
94
  /** Pin this session to one connected ChatGPT account; disables automatic account failover. */
93
95
  chatgpt_account_id?: string;
94
96
  settings?: Readonly<{ model: CreateSettings["model"]; thinking: CreateSettings["thinking"]; reasoning_mode: "standard" | "pro"; fast_mode: boolean }>;
@@ -163,7 +165,7 @@ export type State = Readonly<{
163
165
  latest_event_cursor: string;
164
166
  stream_error: string | null;
165
167
  settings: Readonly<{
166
- model: Model;
168
+ model: Model | "claude-sonnet-4-6" | "claude-opus-4-6" | "claude-sonnet-5-5" | "claude-opus-5-5";
167
169
  thinking: Thinking;
168
170
  reasoning_mode: ReasoningMode;
169
171
  fast_mode: boolean;
@@ -357,6 +359,8 @@ export type CronTrigger = Readonly<{
357
359
  export type Agent = Readonly<{
358
360
  /** Start bounded background runtime/socket preparation for an active conversation.
359
361
  * Resolves on acceptance, not provider readiness. Never required before prompt(). */
362
+ /** Idle manual compaction; uncertain failures are never automatically retried. */
363
+ compact(options?: Readonly<{ signal?: AbortSignal }>): Promise<void>;
360
364
  prepare(options?: Readonly<{ signal?: AbortSignal }>): Promise<void>;
361
365
  requiredActions: Readonly<{
362
366
  list(): Promise<{ data: readonly Readonly<{ call_id: string; session_id: string; source_call_id: string; name: string; input: unknown; deadline_at: number }>[] }>;
package/managed/Agent.mjs CHANGED
@@ -1,3 +1,4 @@
1
+ import { attachRetryAfterAdvice, retryAfterAdvice, retryAfterRemaining } from "../runtime/retry-after.mjs";
1
2
  import { steerInputKey } from "../runtime/steer-receipt.mjs";
2
3
  import { requestOriginContext } from "../tools/environment.mjs";
3
4
  import { ManagedError } from "./ManagedError.mjs";
@@ -17,6 +18,14 @@ const TERMINAL_TYPES = new Set([
17
18
  "turn_cancelled",
18
19
  "turn_failed",
19
20
  ]);
21
+ // Typed terminal failures override an otherwise retryable HTTP status.
22
+ const TERMINAL_RETRY_CODES = new Set([
23
+ "context_length_exceeded", "insufficient_quota", "usage_not_included",
24
+ "cyber_policy", "misalignment_policy_violation", "invalid_prompt", "bio_policy",
25
+ ]);
26
+ // Private event-reconnect veto; does not alter public code precedence or the
27
+ // independent creation/mutation retry policy.
28
+ const terminalResponseErrors = new WeakSet();
20
29
  const TERMINAL_CACHE_CAPACITY = 256;
21
30
  const TERMINAL_CACHE_BYTES = 8 * 1024 * 1024;
22
31
  const SUBSCRIBER_QUEUE_CAPACITY = 4_096;
@@ -29,7 +38,7 @@ const TURN_STATE_READ_TIMEOUT_MS = 2_000;
29
38
  const ALLOWED_OPTIONS = new Set(["apiKey", "baseUrl", "fetch", "toolsTransport", "requestOrigin"]);
30
39
  const CREATE_SETTINGS = new Set(["model", "thinking", "reasoningMode", "fastMode"]);
31
40
  const SETTINGS_PATCH = CREATE_SETTINGS;
32
- const MODELS = new Set(["gpt-6.1-sol", "gpt-6-luna", "gpt-6-astra", "@cf/zai-org/glm-5.3", "kimi-k3", "mimo-v2.6-pro"]);
41
+ const MODELS = new Set(["gpt-6.1-sol", "gpt-6-luna", "gpt-6-astra", "@cf/zai-org/glm-5.3", "kimi-k3", "mimo-v2.6-pro", "claude-sonnet-4-6", "claude-opus-4-6", "claude-sonnet-5-5", "claude-opus-5-5"]);
33
42
  const THINKING = new Set(["none", "low", "medium", "high", "xhigh", "max"]);
34
43
  const REASONING_MODES = new Set(["standard", "pro"]);
35
44
  const eventEncoder = new TextEncoder();
@@ -159,6 +168,7 @@ function managedCreateOptions(options) {
159
168
  if (settings.model === "gpt-6-astra" && settings.reasoningMode === "pro") {
160
169
  throw new TypeError("GPT-6 Astra does not support pro reasoning mode");
161
170
  }
171
+ if (settings.model.startsWith("claude-") && (!["low", "medium", "high"].includes(settings.thinking) || settings.reasoningMode !== "standard" || settings.fastMode)) throw new TypeError("unsupported Claude settings");
162
172
  return {
163
173
  clientOptions,
164
174
  creationKey,
@@ -302,6 +312,9 @@ function agentHandle(client, id, summary, retainedEventStream) {
302
312
  return agentHandle(client, childId);
303
313
  },
304
314
  // Activation is explicit: passive event/history readers never warm a model.
315
+ compact: (options = {}) => client.json(`${agentPath(id)}/compact`, {
316
+ method: "POST", signal: options.signal,
317
+ }).then(() => undefined),
305
318
  prepare: (options = {}) => client.json(`${agentPath(id)}/prepare`, {
306
319
  method: "POST", ...(options.signal === undefined ? {} : { signal: options.signal }),
307
320
  }).then(() => undefined),
@@ -452,6 +465,7 @@ function managedSettings(value) {
452
465
  || !REASONING_MODES.has(value.reasoning_mode) || typeof value.fast_mode !== "boolean"
453
466
  || (["@cf/zai-org/glm-5.3", "kimi-k3", "mimo-v2.6-pro"].includes(value.model) && (!(value.model === "kimi-k3" ? ["low", "high"] : ["low", "medium", "high"]).includes(value.thinking) || value.reasoning_mode === "pro"))
454
467
  || (["gpt-6-astra", "gpt-6.1-sol"].includes(value.model) && value.thinking === "none")
468
+ || (value.model.startsWith("claude-") && (!["low", "medium", "high"].includes(value.thinking) || value.reasoning_mode !== "standard" || value.fast_mode))
455
469
  || (value.model === "gpt-6-astra" && value.reasoning_mode === "pro")
456
470
  ) {
457
471
  throw new ManagedError("invalid_response", "managed agent settings are malformed");
@@ -1323,8 +1337,8 @@ async function* readEvents(client, agentId, initialCursor, signal, onControlCurs
1323
1337
  }
1324
1338
  if (!response.ok) {
1325
1339
  const error = await responseError(response);
1326
- if (response.status !== 429 && response.status < 500) throw error;
1327
- await delay(reconnectDelay, signal);
1340
+ if ((terminalResponseErrors.has(error) || TERMINAL_RETRY_CODES.has(error.code)) || (response.status !== 429 && response.status < 500)) throw error;
1341
+ await waitForRetryAdvice(error, reconnectDelay, signal);
1328
1342
  continue;
1329
1343
  }
1330
1344
  if (!response.body) throw new ManagedError("invalid_response", "managed event stream has no body");
@@ -1376,6 +1390,16 @@ async function* readEvents(client, agentId, initialCursor, signal, onControlCurs
1376
1390
  }
1377
1391
  }
1378
1392
 
1393
+ async function waitForRetryAdvice(advice, fallback, signal) {
1394
+ let remaining = retryAfterRemaining(advice);
1395
+ if (remaining === undefined) return delay(fallback, signal);
1396
+ while (remaining > 0) {
1397
+ // Timers coerce larger delays to ~1ms; chunking preserves the original deadline.
1398
+ await delay(Math.min(remaining, 2_147_483_647), signal);
1399
+ remaining = retryAfterRemaining(advice);
1400
+ }
1401
+ }
1402
+
1379
1403
  function encodedBytes(value) {
1380
1404
  const encoded = typeof value === "string" ? value : JSON.stringify(value);
1381
1405
  return eventEncoder.encode(encoded).byteLength;
@@ -1569,11 +1593,21 @@ function managedClient(options) {
1569
1593
  }
1570
1594
 
1571
1595
  async function responseError(response) {
1596
+ const advice = retryAfterAdvice(response.headers.get("retry-after"));
1572
1597
  let body;
1573
1598
  try { body = await response.json(); } catch { body = undefined; }
1574
- const code = typeof body?.error === "string" ? body.error : `http_${response.status}`;
1599
+ const code = typeof body?.error === "string" ? body.error
1600
+ : typeof body?.error?.code === "string" ? body.error.code
1601
+ : typeof body?.code === "string" ? body.code
1602
+ : typeof body?.error?.type === "string" ? body.error.type : `http_${response.status}`;
1575
1603
  const message = typeof body?.message === "string" ? body.message : `managed request failed (${response.status})`;
1576
- return new ManagedError(code, message, { status: response.status });
1604
+ const error = attachRetryAfterAdvice(new ManagedError(code, message, { status: response.status }), advice);
1605
+ const discriminators = [body?.error, body?.code, body?.type,
1606
+ body?.error?.code, body?.error?.type, body?.response?.error?.code, body?.response?.error?.type];
1607
+ if (discriminators.some((value) => typeof value === "string" && TERMINAL_RETRY_CODES.has(value))) {
1608
+ terminalResponseErrors.add(error);
1609
+ }
1610
+ return error;
1577
1611
  }
1578
1612
 
1579
1613
  function validateOptions(options) {
@@ -1,6 +1,9 @@
1
1
  export declare class ManagedError extends Error {
2
2
  readonly code: string;
3
3
  readonly status: number | undefined;
4
+ /** Server advice captured before consuming a rejected response body. */
5
+ readonly retry_after?: number;
6
+ readonly retry_after_deadline_ms?: number;
4
7
  constructor(
5
8
  code: string,
6
9
  message: string,
package/node/Agent.d.mts CHANGED
@@ -2,6 +2,7 @@ import type {
2
2
  AgentLifecycle,
3
3
  AgentOptions,
4
4
  CodeEvaluator,
5
+ CodeEffectJournal,
5
6
  DefaultAgent,
6
7
  DurabilityStore,
7
8
  McpServers,
@@ -14,10 +15,11 @@ import type { Tools } from "../tools/Tools.mjs";
14
15
 
15
16
  export type Agent = DefaultAgent;
16
17
  type ToolExposureOptions =
17
- | { mcp?: false | undefined; toolMode?: "code" | "direct" | undefined }
18
- | { mcp: McpServers; toolMode?: "code" | undefined };
18
+ | { mcp?: false | undefined; toolMode?: "code" | "code-only" | "direct" | undefined }
19
+ | { mcp: McpServers; toolMode?: "code" | "code-only" | undefined };
19
20
 
20
21
  /** Creates a Node-hosted Rust/WASM Agent. */
22
+ export function create(options: import('../runtime/claude.mjs').Options & { harness: 'claude' }): Promise<import('../runtime/claude.mjs').Agent>;
21
23
  export function create(options: create.ManagedOptions): Promise<AgentLifecycle>;
22
24
  export function create(options: create.Options): Promise<create.ReturnType>;
23
25
  export declare namespace create {
@@ -27,6 +29,8 @@ export declare namespace create {
27
29
  }>;
28
30
  type Options = AgentOptions & ToolExposureOptions & {
29
31
  codeEvaluator?: CodeEvaluator | undefined;
32
+ /** Opt-in durable direct-tool and nested Code Mode receipts for safe cold recovery. */
33
+ codeEffectJournal?: CodeEffectJournal | undefined;
30
34
  /** Caller-owned rooted filesystem mounted through standard workspace tools. */
31
35
  filesystem?: Workspace | undefined;
32
36
  module?: unknown;
package/node/Agent.mjs CHANGED
@@ -1,3 +1,5 @@
1
+ import { prepareHarnesses } from '../runtime/harnesses.mjs';
2
+ import { create as createClaude } from './Claude.mjs';
1
3
  import { createRequire } from "node:module";
2
4
  import initWeb, { Nanocodex as WebNanocodex } from "../pkg-web/nanocodex.js";
3
5
 
@@ -29,12 +31,15 @@ let initializedWeb;
29
31
  let NodeNanocodex;
30
32
 
31
33
  export function create(options = {}) {
34
+ if (options.harness === 'claude') return createClaude(options);
35
+ if (options.harness !== undefined && options.harness !== 'codex') throw new TypeError('unsupported harness family');
32
36
  if (managedTransportOptions(options?.transport)) return createManagedAgent(options);
33
37
  const {
34
38
  model,
35
39
  thinking,
36
40
  reasoningMode,
37
41
  fastMode,
42
+ instantToolSteering,
38
43
  rawApiEvents,
39
44
  instructions,
40
45
  additionalInstructions,
@@ -51,6 +56,7 @@ export function create(options = {}) {
51
56
  toolMode,
52
57
  mcp,
53
58
  codeEvaluator,
59
+ codeEffectJournal,
54
60
  } = options;
55
61
  const stableSessionId = sessionId ?? createSessionId();
56
62
  const {
@@ -63,6 +69,8 @@ export function create(options = {}) {
63
69
  } = resolveResponsesTransport(transport);
64
70
  const { tools: hostTools, subagents: subagentConfig } = resolveTools(tools);
65
71
  const events = createEventChannel();
72
+ // Host lifecycle observers need terminal events even without a public watcher.
73
+ events.subscribe(() => {});
66
74
  if (filesystem && workspace !== undefined && workspace !== filesystem.root) {
67
75
  throw new TypeError("workspace must match filesystem.root when both are provided");
68
76
  }
@@ -80,8 +88,10 @@ export function create(options = {}) {
80
88
  toolMode,
81
89
  workspace: workspace ?? filesystem?.root ?? resume?.workspace,
82
90
  codeEvaluator,
83
- onDispose: () => releaseDefinitionHost(hostDefinitionId),
91
+ codeEffectJournal,
92
+ onDispose: () => { releaseDefinitionHost(hostDefinitionId); void harnesses?.close(); },
84
93
  });
94
+ let harnesses;
85
95
  let durabilityOwner;
86
96
  let creationStarted = false;
87
97
  hostDefinitionId = registerDefinitionHost(host);
@@ -100,6 +110,7 @@ export function create(options = {}) {
100
110
  durabilityId,
101
111
  );
102
112
  }
113
+ harnesses = await prepareHarnesses(options.harnesses, events.emit);
103
114
  activateHost(host);
104
115
  await host.ready();
105
116
  const Nanocodex = module === undefined
@@ -116,6 +127,7 @@ export function create(options = {}) {
116
127
  apiBaseUrl,
117
128
  websocketWarmup,
118
129
  subagents: subagentConfig,
130
+ claudeHarness: harnesses?.claude,
119
131
  hostDefinitionId,
120
132
  beforeCompaction: beforeCompaction !== undefined,
121
133
  ...config,
@@ -162,6 +174,7 @@ export function create(options = {}) {
162
174
  thinking,
163
175
  reasoningMode,
164
176
  fastMode,
177
+ instantToolSteering,
165
178
  rawApiEvents,
166
179
  instructions,
167
180
  additionalInstructions,
@@ -0,0 +1,4 @@
1
+ import type { Agent, Options } from '../runtime/claude.mjs';
2
+ export type { Agent, Auth, Options, NativeToolResult, Result, Tool, ToolContent, ToolResult, Turn } from '../runtime/claude.mjs';
3
+ /** Creates the actual Claude Rust/WASM backend with explicit host-owned authentication and tools. */
4
+ export function create(options: Options): Promise<Agent>;
@@ -0,0 +1,24 @@
1
+ import { createRequire } from 'node:module';
2
+ import { createClaude } from '../runtime/claude.mjs';
3
+ import { createNodeHost } from './host.mjs';
4
+ import { createBrowserHost } from '../browser/host.mjs';
5
+
6
+ function createCodexHost(options) {
7
+ // Function-backed Web API transports retain their own callbacks and evaluator.
8
+ if (options.hostAuth || options.createResponse || options.createWebSocket || options.WebSocketImpl) {
9
+ if ((options.toolMode === 'code' || options.toolMode === 'code-only') && typeof globalThis.Worker !== 'function' && options.codeEvaluator === undefined) throw new TypeError('host-managed Codex Code Mode requires an explicit codeEvaluator');
10
+ return createBrowserHost(options);
11
+ }
12
+ return createNodeHost(options);
13
+ }
14
+
15
+ /** Explicit Claude Messages runtime; no authentication or tools are inferred. */
16
+ export function create(options) {
17
+ return createClaude(options, async (module) => {
18
+ if (module === undefined) return createRequire(import.meta.url)('../pkg-node/nanocodex.js').Nanoclaude;
19
+ const wasm = await import('../pkg-web/nanocodex.js');
20
+ const { initializeBrowserEngine } = await import('../browser/engine.mjs');
21
+ await initializeBrowserEngine({ module });
22
+ return wasm.Nanoclaude;
23
+ }, 'node', { createCodexHost });
24
+ }
package/node/host.mjs CHANGED
@@ -1,3 +1,6 @@
1
+ import { retryAfterAdvice } from "../runtime/retry-after.mjs";
2
+
3
+ import { createCodeEffectIdentity } from "../runtime/code-effect-identity.mjs";
1
4
  import { createBeforeCompaction } from "../runtime/before-compaction.mjs";
2
5
  import { createResponsesHttp, responsesHttpHeaders } from "../runtime/responses-http.mjs";
3
6
  import { Console } from "node:console";
@@ -26,8 +29,8 @@ const MPP_CLIENT_PROTOCOL_ERROR_CLOSE_CODE = 3008;
26
29
  export function createNodeHost(options = {}) {
27
30
  const preservation = createBeforeCompaction(options.beforeCompaction);
28
31
  const toolMode = options.toolMode ?? "code";
29
- if (toolMode !== "code" && toolMode !== "direct") {
30
- throw new TypeError("toolMode must be code or direct");
32
+ if (toolMode !== "code" && toolMode !== "code-only" && toolMode !== "direct") {
33
+ throw new TypeError("toolMode must be code, code-only or direct");
31
34
  }
32
35
  const toolsRouter = options.tools?.[toolRouterBrand]
33
36
  ? options.tools[toolRouterRuntime]
@@ -39,7 +42,7 @@ export function createNodeHost(options = {}) {
39
42
  if (toolsMcp && options.mcpServers) {
40
43
  throw new TypeError("MCP is already configured in Tools");
41
44
  }
42
- if ((toolsMcp || options.mcpServers) && toolMode !== "code") {
45
+ if ((toolsMcp || options.mcpServers) && toolMode === "direct") {
43
46
  throw new TypeError("remote MCP requires Code Mode");
44
47
  }
45
48
  const toolsLifecycle = options.tools?.[toolRuntimeLifecycle];
@@ -51,10 +54,13 @@ export function createNodeHost(options = {}) {
51
54
  body, signal, redirect: "error" });
52
55
  });
53
56
  const connections = new Map();
57
+ const effectIdentity = createCodeEffectIdentity(options.codeEffectJournal);
54
58
  const code = createCodeRuntime(options.tools, {
55
59
  require: createRequire(resolve(options.workspace ?? process.cwd(), ".nanocodex-code-mode.cjs")),
56
60
  console: new Console({ stdout: process.stderr, stderr: process.stderr }),
57
61
  evaluate: options.codeEvaluator,
62
+ effectJournal: options.codeEffectJournal,
63
+ effectIdentity: options.codeEffectJournal ? effectIdentity.resolve : undefined,
58
64
  });
59
65
  const filesystem = options.filesystem
60
66
  ? import("../runtime/workspace.mjs")
@@ -118,6 +124,7 @@ export function createNodeHost(options = {}) {
118
124
  socket.on("unexpected-response", (_request, response) => {
119
125
  if (settled) return;
120
126
  settled = true;
127
+ const advice = retryAfterAdvice(header(response.headers, "retry-after"));
121
128
  response.setEncoding("utf8");
122
129
  const chunks = [];
123
130
  response.on("data", (chunk) => chunks.push(chunk));
@@ -125,8 +132,8 @@ export function createNodeHost(options = {}) {
125
132
  const error = new Error(`WebSocket handshake was rejected with HTTP ${response.statusCode}`);
126
133
  error.status = response.statusCode;
127
134
  error.body = chunks.length ? chunks.join("") : "empty response body";
128
- const retryAfter = Number(header(response.headers, "retry-after"));
129
- if (Number.isFinite(retryAfter) && retryAfter >= 0) error.retryAfter = retryAfter;
135
+ Object.assign(error, advice);
136
+ if (advice.retry_after !== undefined) error.retryAfter = advice.retry_after;
130
137
  reject(error);
131
138
  });
132
139
  });
@@ -301,7 +308,7 @@ export function createNodeHost(options = {}) {
301
308
  http.dispose();
302
309
  disposal = Promise.resolve().then(() => settleCleanup([
303
310
  ...[...connections.keys()].map((handle) => () => close(handle)),
304
- () => code.reset(),
311
+ () => { effectIdentity.reset(); return code.reset(); },
305
312
  () => mcpInstalled,
306
313
  () => toolsLifecycle?.close(),
307
314
  () => options.onDispose?.(),
@@ -335,15 +342,17 @@ export function createNodeHost(options = {}) {
335
342
  waitCode: code.waitCodeObserved,
336
343
  beginCodeTurn: code.beginTurn,
337
344
  cancelCodeTurn: code.cancelTurn,
345
+ preemptCode: code.preempt,
346
+ preemptCodeTurn: code.preemptTurn,
338
347
  nextCodeUpdate: code.nextCodeUpdate,
339
348
  executeTool: code.executeTool,
340
349
  bindSubagentSession: code.bindSubagentSession,
341
350
  cancelCode: code.cancel,
342
351
  toolMode: () => toolMode,
343
352
  toolDefinitions: code.toolDefinitions,
344
- releaseSession: code.releaseSession,
345
- emitEvent: onEvent,
346
- reset: code.reset,
353
+ releaseSession: (sessionId) => { effectIdentity.release(sessionId); return code.releaseSession(sessionId); },
354
+ emitEvent: (event, ...args) => { code.observeEvent(event); effectIdentity.observe(event); return onEvent(event, ...args); },
355
+ reset: () => { effectIdentity.reset(); return code.reset(); },
347
356
  dispose,
348
357
  });
349
358
  }
package/node/index.d.mts CHANGED
@@ -4,16 +4,6 @@ export {
4
4
  subscriptionRevision,
5
5
  } from "../runtime/subscription-store.mjs";
6
6
  export { createQuickJsEvaluator } from "../runtime/quickjs-evaluator.mjs";
7
- export {
8
- createTempoProvider,
9
- createTempoProviderFromAccounts,
10
- DEFAULT_MERCATOR_MCP_URL,
11
- } from "../runtime/tempo-provider.mjs";
12
- export type {
13
- AccountsTempoProviderOptions,
14
- AccountsWallet,
15
- TempoProvider,
16
- } from "../runtime/tempo-provider.mjs";
17
7
  export type {
18
8
  AgentEvent,
19
9
  AgentLifecycle,
@@ -27,6 +17,9 @@ export type {
27
17
  CostStatus,
28
18
  CodeEvaluator,
29
19
  CodeEvaluatorEnvironment,
20
+ CodeEffectContext,
21
+ CodeEffectReceipt,
22
+ CodeEffectJournal,
30
23
  EstimatedUsdCost,
31
24
  PromptInput,
32
25
  PromptItem,
@@ -45,6 +38,7 @@ export type {
45
38
  TurnResult,
46
39
  TurnUsage,
47
40
  McpPayment,
41
+ PaidMcpPayment,
48
42
  McpServer,
49
43
  McpServers,
50
44
  MemoryChatGptSubscriptionStore,
@@ -60,3 +54,5 @@ export * as Subagents from "../runtime/subagents.mjs";
60
54
  export * as Transport from "./Transport.mjs";
61
55
  export * as Workspace from "./workspace.mjs";
62
56
  export * as Tools from "../tools/index.mjs";
57
+
58
+ export * as Claude from "./Claude.mjs";
package/node/index.mjs CHANGED
@@ -4,14 +4,11 @@ export {
4
4
  subscriptionRevision,
5
5
  } from "../runtime/subscription-store.mjs";
6
6
  export { createQuickJsEvaluator } from "../runtime/quickjs-evaluator.mjs";
7
- export {
8
- createTempoProvider,
9
- createTempoProviderFromAccounts,
10
- DEFAULT_MERCATOR_MCP_URL,
11
- } from "../runtime/tempo-provider.mjs";
12
7
  export * as Agent from "./Agent.mjs";
13
8
  export * as ChatGptSubscription from "./ChatGptSubscription.mjs";
14
9
  export * as Subagents from "../runtime/subagents.mjs";
15
10
  export * as Transport from "./Transport.mjs";
16
11
  export * as Workspace from "./workspace.mjs";
17
12
  export * as Tools from "../tools/index.mjs";
13
+
14
+ export * as Claude from "./Claude.mjs";