nanocodex 0.6.4 → 0.6.6

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 (78) hide show
  1. package/README.md +37 -11
  2. package/browser/Agent.d.mts +1 -1
  3. package/browser/ElevenLabsPlayback.mjs +119 -0
  4. package/browser/InlineAgent.mjs +22 -17
  5. package/browser/Transport.d.mts +2 -0
  6. package/browser/Transport.mjs +3 -2
  7. package/browser/Voice.d.mts +4 -0
  8. package/browser/Voice.mjs +38 -6
  9. package/browser/VoiceSession.mjs +37 -7
  10. package/browser/host.d.mts +20 -4
  11. package/browser/host.mjs +156 -18
  12. package/cloud/types.d.mts +1 -0
  13. package/cloudflare/Agent.d.mts +14 -9
  14. package/cloudflare/Agent.mjs +354 -96
  15. package/cloudflare/agent-settings.d.mts +15 -0
  16. package/cloudflare/agent-settings.mjs +135 -0
  17. package/cloudflare/chat-reasoning.mjs +20 -0
  18. package/cloudflare/durable-placement.d.mts +12 -0
  19. package/cloudflare/durable-placement.mjs +82 -0
  20. package/cloudflare/egress.d.mts +31 -0
  21. package/cloudflare/gateway-binding-responses.mjs +84 -0
  22. package/cloudflare/gateway-responses.d.mts +48 -0
  23. package/cloudflare/gateway-responses.mjs +191 -0
  24. package/cloudflare/index.d.mts +6 -0
  25. package/cloudflare/index.mjs +3 -0
  26. package/cloudflare/managed-auth.d.mts +15 -0
  27. package/cloudflare/managed-auth.mjs +119 -0
  28. package/cloudflare/managed-live.d.mts +7 -0
  29. package/cloudflare/managed-live.mjs +56 -0
  30. package/cloudflare/prepared-connection.mjs +49 -0
  31. package/cloudflare/provider-stream.mjs +353 -0
  32. package/cloudflare/rpc.d.mts +9 -0
  33. package/cloudflare/rpc.mjs +14 -0
  34. package/cloudflare/tool-alias-diagnostic.mjs +21 -0
  35. package/cloudflare/tracing.d.mts +16 -0
  36. package/cloudflare/tracing.mjs +47 -0
  37. package/cloudflare/workers-ai-responses.d.mts +28 -0
  38. package/cloudflare/workers-ai-responses.mjs +399 -0
  39. package/host/Agent.d.mts +1 -1
  40. package/index.d.mts +2 -0
  41. package/internal.mjs +53 -14
  42. package/managed/Agent.d.mts +25 -69
  43. package/managed/Agent.mjs +82 -196
  44. package/managed/README.md +9 -38
  45. package/managed/Voice.mjs +5 -2
  46. package/managed/index.d.mts +0 -3
  47. package/managed/internal.mjs +2 -1
  48. package/node/Agent.d.mts +1 -1
  49. package/node/Agent.mjs +9 -3
  50. package/node/host.mjs +5 -0
  51. package/package.json +34 -2
  52. package/pkg-node/nanocodex.d.ts +22 -18
  53. package/pkg-node/nanocodex.js +118 -92
  54. package/pkg-node/nanocodex_bg.wasm.d.ts +6 -5
  55. package/pkg-web/.nanocodex-bindgen-stamp +7 -1
  56. package/pkg-web/nanocodex-build.json +1 -1
  57. package/pkg-web/nanocodex.d.ts +28 -23
  58. package/pkg-web/nanocodex.js +117 -91
  59. package/pkg-web/nanocodex_bg.js +117 -91
  60. package/pkg-web/nanocodex_bg.wasm +0 -0
  61. package/pkg-web/nanocodex_bg.wasm.d.ts +6 -5
  62. package/pkg-web/nanocodex_worker.js +1 -1
  63. package/runtime/before-compaction.mjs +49 -0
  64. package/runtime/code-evaluator.worker.mjs +2 -9
  65. package/runtime/managed-transport.mjs +10 -2
  66. package/runtime/mcp-runtime.mjs +20 -4
  67. package/runtime/quickjs-evaluator.mjs +11 -5
  68. package/runtime/response-controls.mjs +67 -4
  69. package/runtime/steer-receipt.mjs +13 -0
  70. package/runtime/subagent-routing.d.mts +38 -0
  71. package/runtime/subagent-routing.mjs +81 -0
  72. package/runtime/subagents.d.mts +1 -1
  73. package/runtime/tempo-provider.d.mts +1 -1
  74. package/runtime/tempo-provider.mjs +1 -1
  75. package/tools/environment.d.mts +17 -2
  76. package/tools/environment.mjs +30 -3
  77. package/tools/index.d.mts +2 -0
  78. package/types.d.mts +30 -2
@@ -41,61 +41,6 @@ export type ReadSessionResponse = Readonly<{
41
41
  turns: readonly SessionTurn[];
42
42
  citations: readonly HistoryCitation[];
43
43
  }>;
44
- export type MemoryKey = Readonly<{ id: number; version: number }>;
45
- export type MemoryRecord = Readonly<{
46
- key: MemoryKey;
47
- content: string;
48
- created_at_ms: number;
49
- updated_at_ms: number;
50
- last_scanned_at_ms: number | null;
51
- scan_count: number;
52
- last_used_at_ms: number | null;
53
- use_count: number;
54
- probation_until_ms: number | null;
55
- }>;
56
- export type MemoryCandidate = Readonly<{
57
- key: MemoryKey;
58
- preview: string;
59
- score: number;
60
- }>;
61
- export type MemoryScanOperation = Readonly<{
62
- operation: "scan";
63
- query: string;
64
- limit?: number | undefined;
65
- }>;
66
- export type MemoryReadOperation = Readonly<{ operation: "read"; keys: readonly MemoryKey[] }>;
67
- export type MemoryPutOperation = Readonly<{
68
- operation: "put";
69
- content: string;
70
- replace?: MemoryKey | undefined;
71
- }>;
72
- export type MemoryDeleteOperation = Readonly<{ operation: "delete"; key: MemoryKey }>;
73
- export type MemoryOperation =
74
- | MemoryScanOperation
75
- | MemoryReadOperation
76
- | MemoryPutOperation
77
- | MemoryDeleteOperation;
78
- export type MemoryScanResult = Readonly<{
79
- operation: "scan";
80
- abstained: boolean;
81
- candidates: readonly MemoryCandidate[];
82
- }>;
83
- export type MemoryReadResult = Readonly<{
84
- operation: "read";
85
- memories: readonly MemoryRecord[];
86
- }>;
87
- export type MemoryPutResult = Readonly<{
88
- operation: "put";
89
- memory: MemoryRecord;
90
- replaced: boolean;
91
- }>;
92
- export type MemoryDeleteResult = Readonly<{ operation: "delete"; key: MemoryKey }>;
93
- export type MemoryResult =
94
- | MemoryScanResult
95
- | MemoryReadResult
96
- | MemoryPutResult
97
- | MemoryDeleteResult;
98
-
99
44
  export type Organization = Readonly<{
100
45
  id: string;
101
46
  name: string | null;
@@ -195,8 +140,6 @@ export type CreateAndPromptOptions = CreateOptions & Readonly<{
195
140
  export type Capabilities = Readonly<{
196
141
  durable_turns: true;
197
142
  resumable_events: true;
198
- live_steer: true;
199
- live_cancel: true;
200
143
  workspace: "cloudflare-computer";
201
144
  /** Tools can target explicit sandbox and connected-user environments. */
202
145
  execution_environments: true;
@@ -227,11 +170,26 @@ export type State = Readonly<{
227
170
  }>;
228
171
  }>;
229
172
 
173
+ export type AgentPresentation = Readonly<{
174
+ revision: number;
175
+ status: "running" | "stopping" | "completed" | "cancelled" | "failed" | "idle";
176
+ activeTurnIds: readonly string[];
177
+ title?: string;
178
+ activity?: string;
179
+ activityTurnId?: string;
180
+ /** Bounded preview of the latest accepted user prompt. */
181
+ lastUserPrompt?: string;
182
+ lastUserMessageAt?: number;
183
+ updatedAt: number;
184
+ }>;
185
+
230
186
  export type Summary = Readonly<{
231
187
  title: string;
232
188
  createdAt: number;
233
189
  updatedAt: number;
234
190
  turnCount: number;
191
+ lastUserMessageAt?: number;
192
+ presentation?: AgentPresentation;
235
193
  }>;
236
194
 
237
195
  export type TurnState =
@@ -260,9 +218,11 @@ export type EventData = Readonly<{
260
218
  cursor: string;
261
219
  created_at: number;
262
220
  turn_id: string | null;
221
+ model_route?: { model: string; thinking: string; reasoning_mode: string; fast_mode: boolean; backend: string };
222
+ model_routing_automatic?: boolean;
263
223
  }> & (
264
224
  | { type: "agent_created"; agent_id: string; capabilities: Capabilities }
265
- | { type: "turn_accepted"; id: string; input: PromptInput; replayed: boolean }
225
+ | { type: "turn_accepted"; id: string; input: PromptInput; replayed: boolean; author?: "guest"; share_link_id?: string }
266
226
  | { type: "turn_cancelling"; id: string; error?: string; retry_at?: number }
267
227
  | CompletedEventData
268
228
  | { type: "turn_cancelled"; id: string }
@@ -369,7 +329,7 @@ export type CronTriggerConfig = Readonly<{
369
329
  cron: string;
370
330
  /** IANA time zone. Defaults to UTC. */
371
331
  timezone?: string | undefined;
372
- /** Text prompt submitted for each occurrence, at most 64 KiB. */
332
+ /** Text prompt submitted for each occurrence. */
373
333
  input: string;
374
334
  /** Defaults to true; false pauses future occurrences. */
375
335
  enabled?: boolean | undefined;
@@ -415,6 +375,9 @@ export type Agent = Readonly<{
415
375
  /** Account-owned list metadata, present on handles returned by `list()`. */
416
376
  summary?: Summary | undefined;
417
377
  turn: Readonly<{ prompt(options: PromptOptions): Turn }>;
378
+ /** Copy the latest committed model boundary into a separate durable agent.
379
+ * Reuse the same key to reconcile an uncertain response. */
380
+ fork(options: Readonly<{ idempotencyKey: string; signal?: AbortSignal | undefined }>): Promise<Agent>;
418
381
  settings: Readonly<{
419
382
  read(): Promise<CreateSettings>;
420
383
  update(patch: SettingsPatch): Promise<CreateSettings>;
@@ -424,6 +387,9 @@ export type Agent = Readonly<{
424
387
  get(id: string): Promise<CronTrigger>;
425
388
  /** Create or replace an account-owned schedule using a stable id. */
426
389
  put(id: string, config: CronTriggerConfig): Promise<CronTrigger>;
390
+ /** Update an existing schedule; omitted settings are preserved. */
391
+ update(id: string, patch: Partial<CronTriggerConfig>): Promise<CronTrigger>;
392
+ /** Stop future occurrences. Already dispatched runs are not cancelled. */
427
393
  delete(id: string): Promise<void>;
428
394
  }>;
429
395
  /** Reverse-tool endpoint with cookie/bearer transport retained in a private closure. */
@@ -451,16 +417,6 @@ export function remove(id: string, options?: Options): Promise<void>;
451
417
  export { remove as delete };
452
418
  export function findSessions(request: FindSessionsRequest, options?: Options): Promise<FindSessionsResponse>;
453
419
  export function readSession(request: ReadSessionRequest, options?: Options): Promise<ReadSessionResponse>;
454
- export type MemoryOptions = Options & Readonly<{ scope?: "team" | "personal" }>;
455
- /** List the authenticated account's hosted durable memory. */
456
- export function listMemories(options?: MemoryOptions): Promise<readonly MemoryRecord[]>;
457
- /** Compare-and-swap delete one hosted durable memory; deleting an absent id is idempotent. */
458
- export function deleteMemory(key: MemoryKey, options?: MemoryOptions): Promise<void>;
459
- export function memory(operation: MemoryScanOperation, options?: MemoryOptions): Promise<MemoryScanResult>;
460
- export function memory(operation: MemoryReadOperation, options?: MemoryOptions): Promise<MemoryReadResult>;
461
- export function memory(operation: MemoryPutOperation, options?: MemoryOptions): Promise<MemoryPutResult>;
462
- export function memory(operation: MemoryDeleteOperation, options?: MemoryOptions): Promise<MemoryDeleteResult>;
463
- export function memory(operation: MemoryOperation, options?: MemoryOptions): Promise<MemoryResult>;
464
420
  export function getOrganization(options?: Options): Promise<Organization>;
465
421
  export function updateOrganization(request: OrganizationUpdate, options?: Options): Promise<Organization>;
466
422
 
package/managed/Agent.mjs CHANGED
@@ -1,3 +1,4 @@
1
+ import { steerInputKey } from "../runtime/steer-receipt.mjs";
1
2
  import { requestOriginContext } from "../tools/environment.mjs";
2
3
  import { ManagedError } from "./ManagedError.mjs";
3
4
  import { registerManagedAgent } from "./internal.mjs";
@@ -28,13 +29,13 @@ const TURN_STATE_READ_TIMEOUT_MS = 2_000;
28
29
  const ALLOWED_OPTIONS = new Set(["apiKey", "baseUrl", "fetch", "toolsTransport", "requestOrigin"]);
29
30
  const CREATE_SETTINGS = new Set(["model", "thinking", "reasoningMode", "fastMode"]);
30
31
  const SETTINGS_PATCH = CREATE_SETTINGS;
31
- const MODELS = new Set(["gpt-5.6-sol", "gpt-5.6-terra", "gpt-5.6-luna", "gpt-6-astra"]);
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"]);
32
33
  const THINKING = new Set(["none", "low", "medium", "high", "xhigh", "max"]);
33
34
  const REASONING_MODES = new Set(["standard", "pro"]);
34
35
  const eventEncoder = new TextEncoder();
35
36
 
36
37
  /** Create a new managed agent owned by the authenticated account. */
37
- export async function create(options = {}) {
38
+ export async function create(options = {}, onCreated) {
38
39
  const { clientOptions, requestBody, creationKey } = managedCreateOptions(options);
39
40
  const client = managedClient(clientOptions);
40
41
  const idempotencyKey = creationKey ?? `managed-create:${globalThis.crypto.randomUUID()}`;
@@ -44,7 +45,12 @@ export async function create(options = {}) {
44
45
  idempotencyKey,
45
46
  requestBody,
46
47
  );
47
- return agentHandle(client, requiredString(receipt, "agent_id"));
48
+ const agent = agentHandle(client, requiredString(receipt, "agent_id"));
49
+ // Internal transport hook: preserve the public handle while sharing the POST
50
+ // identity receipt with the common Agent lifecycle. Never cache initial_state: an
51
+ // idempotent replay may return synthetic creation state for an existing agent.
52
+ onCreated?.(receipt);
53
+ return agent;
48
54
  }
49
55
 
50
56
  /** Create a managed agent and durably admit its first turn in one request. */
@@ -144,8 +150,11 @@ function managedCreateOptions(options) {
144
150
  || typeof settings.fastMode !== "boolean") {
145
151
  throw new TypeError("managed agent creation settings are invalid");
146
152
  }
147
- if (settings.model === "gpt-6-astra" && settings.thinking === "none") {
148
- throw new TypeError("GPT-6 Astra requires low, medium, high, xhigh, or max thinking");
153
+ if (["@cf/zai-org/glm-5.3", "kimi-k3", "mimo-v2.6-pro"].includes(settings.model) && ((settings.thinking !== undefined && !(settings.model === "kimi-k3" ? ["low", "high"] : ["low", "medium", "high"]).includes(settings.thinking)) || settings.reasoningMode === "pro")) {
154
+ throw new TypeError(`${settings.model === "@cf/zai-org/glm-5.3" ? "GLM-5.3" : settings.model} requires a supported thinking effort and standard reasoning mode`);
155
+ }
156
+ if (["gpt-6-astra", "gpt-6.1-sol"].includes(settings.model) && settings.thinking === "none") {
157
+ throw new TypeError("GPT-6 Astra and GPT-6.1 Sol require low, medium, high, xhigh, or max thinking");
149
158
  }
150
159
  if (settings.model === "gpt-6-astra" && settings.reasoningMode === "pro") {
151
160
  throw new TypeError("GPT-6 Astra does not support pro reasoning mode");
@@ -249,43 +258,6 @@ export async function readSession(request, options = {}) {
249
258
  return managedReadSessionResponse(body);
250
259
  }
251
260
 
252
- function memoryClient(options) {
253
- const { scope = "team", ...clientOptions } = options;
254
- if (scope !== "team" && scope !== "personal") throw new TypeError("memory scope must be team or personal");
255
- return { client: managedClient(clientOptions), scope };
256
- }
257
-
258
- /** List the authenticated account's hosted durable memory. */
259
- export async function listMemories(options = {}) {
260
- const { client, scope } = memoryClient(options);
261
- const body = await client.json(`/v1/memory${scope === "personal" ? "?scope=personal" : ""}`);
262
- if (!body || typeof body !== "object" || Array.isArray(body) || !Array.isArray(body.memories)) {
263
- throw new ManagedError("invalid_response", "managed memory list is malformed");
264
- }
265
- return Object.freeze(body.memories.map(managedMemoryRecord));
266
- }
267
-
268
- /** Compare-and-swap delete one account-owned hosted memory. */
269
- export async function deleteMemory(key, options = {}) {
270
- validateMemoryKey(key);
271
- const { client, scope } = memoryClient(options);
272
- await client.empty(
273
- `/v1/memory/${key.id}?version=${key.version}${scope === "personal" ? "&scope=personal" : ""}`,
274
- { method: "DELETE" },
275
- );
276
- }
277
-
278
- /** Run one atomic durable-memory operation in the authenticated account scope. */
279
- export async function memory(operation, options = {}) {
280
- validateMemoryOperation(operation);
281
- const { client, scope } = memoryClient(options);
282
- const body = await client.json("/v1/memory", {
283
- method: "POST",
284
- body: JSON.stringify(scope === "personal" ? { ...operation, scope } : operation),
285
- });
286
- return managedMemoryResponse(body, operation.operation);
287
- }
288
-
289
261
  /** Read metadata for the authenticated account's organization. */
290
262
  export async function getOrganization(options = {}) {
291
263
  return managedOrganization(await managedClient(options).json("/v1/organization"));
@@ -313,6 +285,22 @@ function agentHandle(client, id, summary, retainedEventStream) {
313
285
  id,
314
286
  ...(summary === undefined ? {} : { summary }),
315
287
  events,
288
+ fork: async (options) => {
289
+ if (!options || typeof options !== "object" || Array.isArray(options)
290
+ || Object.keys(options).some(key => !["idempotencyKey", "signal"].includes(key))
291
+ || typeof options.idempotencyKey !== "string"
292
+ || !IDEMPOTENCY_KEY.test(options.idempotencyKey)) {
293
+ throw new TypeError("managed fork requires a valid idempotency key");
294
+ }
295
+ if (options.signal !== undefined && !(options.signal instanceof AbortSignal))
296
+ throw new TypeError("managed fork signal must be an AbortSignal");
297
+ const receipt = await retryCreateMutation(client, `${agentPath(id)}/forks`,
298
+ options.idempotencyKey, undefined, options.signal);
299
+ const childId = requiredString(receipt, "agent_id");
300
+ if (!SESSION_ID.test(childId) || childId === id || receipt.parent_agent_id !== id)
301
+ throw new ManagedError("invalid_response", "managed fork response did not identify a separate child");
302
+ return agentHandle(client, childId);
303
+ },
316
304
  // Activation is explicit: passive event/history readers never warm a model.
317
305
  prepare: (options = {}) => client.json(`${agentPath(id)}/prepare`, {
318
306
  method: "POST", ...(options.signal === undefined ? {} : { signal: options.signal }),
@@ -362,6 +350,9 @@ function agentHandle(client, id, summary, retainedEventStream) {
362
350
  put: async (triggerId, config) => managedCronTrigger(await client.json(cronTriggerPath(id, triggerId), {
363
351
  method: "PUT", body: cronTriggerBody(config),
364
352
  })),
353
+ update: async (triggerId, patch) => managedCronTrigger(await client.json(cronTriggerPath(id, triggerId), {
354
+ method: "PATCH", body: cronTriggerPatch(patch),
355
+ })),
365
356
  delete: async (triggerId) => { await client.empty(cronTriggerPath(id, triggerId), { method: "DELETE" }); },
366
357
  }),
367
358
  toolsTarget: () => client.toolsTarget(id),
@@ -398,6 +389,14 @@ function cronTriggerBody(config) {
398
389
  return JSON.stringify(config);
399
390
  }
400
391
 
392
+ function cronTriggerPatch(patch) {
393
+ if (!patch || typeof patch !== "object" || Array.isArray(patch)) throw new TypeError("invalid cron trigger patch");
394
+ const defined = Object.fromEntries(Object.entries(patch).filter(([, value]) => value !== undefined));
395
+ if (!Object.keys(defined).length) throw new TypeError("cron trigger patch must contain a setting");
396
+ cronTriggerBody({ cron: "0 9 * * *", input: "validation", ...defined });
397
+ return JSON.stringify(defined);
398
+ }
399
+
401
400
  function managedCronTrigger(value) {
402
401
  if (!value || typeof value !== "object" || Array.isArray(value)
403
402
  || typeof value.id !== "string" || !/^[A-Za-z0-9_-]{1,64}$/.test(value.id)
@@ -430,8 +429,11 @@ function managedSettingsPatch(patch) {
430
429
  || (Object.hasOwn(patch, "fastMode") && typeof patch.fastMode !== "boolean")) {
431
430
  throw new TypeError("managed agent settings patch is invalid");
432
431
  }
433
- if (patch.model === "gpt-6-astra" && patch.thinking === "none") {
434
- throw new TypeError("GPT-6 Astra requires low, medium, high, xhigh, or max thinking");
432
+ if (["@cf/zai-org/glm-5.3", "kimi-k3", "mimo-v2.6-pro"].includes(patch.model) && ((patch.thinking !== undefined && !(patch.model === "kimi-k3" ? ["low", "high"] : ["low", "medium", "high"]).includes(patch.thinking)) || patch.reasoningMode === "pro")) {
433
+ throw new TypeError(`${patch.model === "@cf/zai-org/glm-5.3" ? "GLM-5.3" : patch.model} requires a supported thinking effort and standard reasoning mode`);
434
+ }
435
+ if (["gpt-6-astra", "gpt-6.1-sol"].includes(patch.model) && patch.thinking === "none") {
436
+ throw new TypeError("GPT-6 Astra and GPT-6.1 Sol require low, medium, high, xhigh, or max thinking");
435
437
  }
436
438
  if (patch.model === "gpt-6-astra" && patch.reasoningMode === "pro") {
437
439
  throw new TypeError("GPT-6 Astra does not support pro reasoning mode");
@@ -448,8 +450,10 @@ function managedSettings(value) {
448
450
  if (!value || typeof value !== "object" || Array.isArray(value)
449
451
  || !MODELS.has(value.model) || !THINKING.has(value.thinking)
450
452
  || !REASONING_MODES.has(value.reasoning_mode) || typeof value.fast_mode !== "boolean"
451
- || (value.model === "gpt-6-astra" && value.thinking === "none")
452
- || (value.model === "gpt-6-astra" && value.reasoning_mode === "pro")) {
453
+ || (["@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
+ || (["gpt-6-astra", "gpt-6.1-sol"].includes(value.model) && value.thinking === "none")
455
+ || (value.model === "gpt-6-astra" && value.reasoning_mode === "pro")
456
+ ) {
453
457
  throw new ManagedError("invalid_response", "managed agent settings are malformed");
454
458
  }
455
459
  return Object.freeze({
@@ -509,9 +513,22 @@ function managedSummary(value) {
509
513
  createdAt: value.created_at,
510
514
  updatedAt: value.updated_at,
511
515
  turnCount: value.turn_count,
516
+ lastUserMessageAt: nonnegativeNumber(value.last_user_message_at) ? value.last_user_message_at : value.turn_count > 0 ? value.updated_at : 0,
517
+ ...(validPresentation(value.presentation) ? { presentation: Object.freeze({ ...value.presentation, activeTurnIds: Object.freeze([...value.presentation.activeTurnIds]) }) } : {}),
512
518
  });
513
519
  }
514
520
 
521
+ function validPresentation(value) {
522
+ return value && typeof value === "object" && !Array.isArray(value)
523
+ && Number.isSafeInteger(value.revision) && value.revision > 0
524
+ && ["running", "stopping", "completed", "cancelled", "failed", "idle"].includes(value.status)
525
+ && Array.isArray(value.activeTurnIds) && value.activeTurnIds.every(id => typeof id === "string")
526
+ && nonnegativeNumber(value.updatedAt)
527
+ && (value.activity === undefined || typeof value.activity === "string")
528
+ && (value.activityTurnId === undefined || typeof value.activityTurnId === "string")
529
+ && (value.lastUserPrompt === undefined || typeof value.lastUserPrompt === "string");
530
+ }
531
+
515
532
  function nonnegativeNumber(value) {
516
533
  return typeof value === "number" && Number.isFinite(value) && value >= 0;
517
534
  }
@@ -590,11 +607,24 @@ function managedTurn(client, agentId, eventStream, options, acceptedSubmission)
590
607
  // Cancellation deliberately bypasses this queue.
591
608
  const steering = steeringTail.then(async () => {
592
609
  const accepted = await submission;
593
- return client.json(`${turnPath(agentId, requiredString(accepted, "turn_id"))}/steer`, {
594
- method: "POST",
595
- body,
596
- signal,
597
- });
610
+ const turnId = requiredString(accepted, "turn_id");
611
+ try {
612
+ return await client.json(`${turnPath(agentId, turnId)}/steer`, { method: "POST", body, signal });
613
+ } catch (error) {
614
+ if (typeof messageId === "string" && TURN_ID.test(messageId) && !(error?.status >= 400 && error?.status < 500)) {
615
+ // Capability discovery is read-only. Missing/legacy receipts never authorize resending.
616
+ const receipt = await client.json(`${turnPath(agentId, turnId)}/steer-receipt?message_id=${encodeURIComponent(messageId)}`, { signal }).catch(() => undefined);
617
+ if (
618
+ receipt?.protocol === 1 && receipt.turn_id === turnId &&
619
+ receipt.message_id === messageId && receipt.state === "accepted" &&
620
+ typeof receipt.input_key === "string" &&
621
+ receipt.input_key === await steerInputKey(JSON.parse(body).input).catch(() => undefined)
622
+ ) {
623
+ return { turn_id: turnId, state: "steering" };
624
+ }
625
+ }
626
+ throw error;
627
+ }
598
628
  });
599
629
  steeringTail = steering.then(() => {}, () => {});
600
630
  return steering;
@@ -935,94 +965,6 @@ function managedCitations(value) {
935
965
  }));
936
966
  }
937
967
 
938
- function managedMemoryResponse(value, expectedOperation) {
939
- if (!value || typeof value !== "object" || Array.isArray(value)
940
- || value.operation !== expectedOperation) {
941
- throw new ManagedError("invalid_response", "managed memory response is malformed");
942
- }
943
- if (value.operation === "scan") {
944
- if (typeof value.abstained !== "boolean" || !Array.isArray(value.candidates)
945
- || value.abstained !== (value.candidates.length === 0)) {
946
- throw new ManagedError("invalid_response", "managed memory scan response is malformed");
947
- }
948
- return Object.freeze({
949
- operation: "scan",
950
- abstained: value.abstained,
951
- candidates: Object.freeze(value.candidates.map(managedMemoryCandidate)),
952
- });
953
- }
954
- if (value.operation === "read") {
955
- if (!Array.isArray(value.memories)) {
956
- throw new ManagedError("invalid_response", "managed memory read response is malformed");
957
- }
958
- return Object.freeze({
959
- operation: "read",
960
- memories: Object.freeze(value.memories.map(managedMemoryRecord)),
961
- });
962
- }
963
- if (value.operation === "put") {
964
- if (typeof value.replaced !== "boolean") {
965
- throw new ManagedError("invalid_response", "managed memory put response is malformed");
966
- }
967
- return Object.freeze({
968
- operation: "put",
969
- memory: managedMemoryRecord(value.memory),
970
- replaced: value.replaced,
971
- });
972
- }
973
- if (value.operation === "delete") {
974
- return Object.freeze({ operation: "delete", key: managedMemoryKey(value.key) });
975
- }
976
- throw new ManagedError("invalid_response", "managed memory response is malformed");
977
- }
978
-
979
- function managedMemoryCandidate(value) {
980
- if (!value || typeof value !== "object" || Array.isArray(value)
981
- || typeof value.preview !== "string"
982
- || UTF8.encode(value.preview).byteLength > 64
983
- || typeof value.score !== "number" || !Number.isFinite(value.score) || value.score <= 0) {
984
- throw new ManagedError("invalid_response", "managed memory candidate is malformed");
985
- }
986
- return Object.freeze({
987
- key: managedMemoryKey(value.key),
988
- preview: value.preview,
989
- score: value.score,
990
- });
991
- }
992
-
993
- function managedMemoryRecord(value) {
994
- if (!value || typeof value !== "object" || Array.isArray(value)
995
- || typeof value.content !== "string" || !value.content.trim()
996
- || !nonnegativeSafeInteger(value.created_at_ms)
997
- || !nonnegativeSafeInteger(value.updated_at_ms)
998
- || !nullableNonnegativeSafeInteger(value.last_scanned_at_ms)
999
- || !nonnegativeSafeInteger(value.scan_count)
1000
- || !nullableNonnegativeSafeInteger(value.last_used_at_ms)
1001
- || !nonnegativeSafeInteger(value.use_count)
1002
- || !nullableNonnegativeSafeInteger(value.probation_until_ms)) {
1003
- throw new ManagedError("invalid_response", "managed memory record is malformed");
1004
- }
1005
- return Object.freeze({
1006
- key: managedMemoryKey(value.key),
1007
- content: value.content,
1008
- created_at_ms: value.created_at_ms,
1009
- updated_at_ms: value.updated_at_ms,
1010
- last_scanned_at_ms: value.last_scanned_at_ms,
1011
- scan_count: value.scan_count,
1012
- last_used_at_ms: value.last_used_at_ms,
1013
- use_count: value.use_count,
1014
- probation_until_ms: value.probation_until_ms,
1015
- });
1016
- }
1017
-
1018
- function managedMemoryKey(value) {
1019
- if (!value || typeof value !== "object" || Array.isArray(value)
1020
- || !positiveSafeInteger(value.id) || !positiveSafeInteger(value.version)) {
1021
- throw new ManagedError("invalid_response", "managed memory key is malformed");
1022
- }
1023
- return Object.freeze({ id: value.id, version: value.version });
1024
- }
1025
-
1026
968
  function managedOrganization(value) {
1027
969
  if (!value || typeof value !== "object" || Array.isArray(value)
1028
970
  || typeof value.id !== "string" || !UUID.test(value.id)
@@ -1553,7 +1495,7 @@ function managedClient(options) {
1553
1495
 
1554
1496
  const response = async (path, init = {}) => {
1555
1497
  const headers = new Headers();
1556
- if (path === "/v1/agents" || path.startsWith("/v1/agents/")) headers.set("x-nanocodex-client-context", JSON.stringify(requestOrigin));
1498
+ if (path === "/v1/agent-runs" || path === "/v1/agents" || path.startsWith("/v1/agents/")) headers.set("x-nanocodex-client-context", JSON.stringify(requestOrigin));
1557
1499
  if (init.body !== undefined) headers.set("content-type", "application/json");
1558
1500
  if (init.accept) headers.set("accept", init.accept);
1559
1501
  if (init.idempotencyKey) headers.set("idempotency-key", init.idempotencyKey);
@@ -1701,58 +1643,6 @@ function validateReadSessionRequest(request) {
1701
1643
  }
1702
1644
  }
1703
1645
 
1704
- function validateMemoryOperation(value) {
1705
- if (!value || typeof value !== "object" || Array.isArray(value)) {
1706
- throw new TypeError("managed memory operation must be an object");
1707
- }
1708
- if (typeof value.operation !== "string") {
1709
- throw new TypeError("managed memory operation is required");
1710
- }
1711
- if (value.operation === "scan") {
1712
- assertOnlyFields(value, ["operation", "query", "limit"], "managed memory scan");
1713
- if (typeof value.query !== "string" || !value.query.trim()) {
1714
- throw new TypeError("managed memory scan query must be a nonempty string");
1715
- }
1716
- if (value.limit !== undefined
1717
- && (!Number.isSafeInteger(value.limit) || value.limit < 1)) {
1718
- throw new TypeError("managed memory scan limit must be a positive safe integer");
1719
- }
1720
- return;
1721
- }
1722
- if (value.operation === "read") {
1723
- assertOnlyFields(value, ["operation", "keys"], "managed memory read");
1724
- if (!Array.isArray(value.keys) || value.keys.length === 0) {
1725
- throw new TypeError("managed memory read requires at least one key");
1726
- }
1727
- value.keys.forEach(validateMemoryKey);
1728
- return;
1729
- }
1730
- if (value.operation === "put") {
1731
- assertOnlyFields(value, ["operation", "content", "replace"], "managed memory put");
1732
- if (typeof value.content !== "string" || !value.content.trim()) {
1733
- throw new TypeError("managed memory content must be a nonempty string");
1734
- }
1735
- if (value.replace !== undefined) validateMemoryKey(value.replace);
1736
- return;
1737
- }
1738
- if (value.operation === "delete") {
1739
- assertOnlyFields(value, ["operation", "key"], "managed memory delete");
1740
- validateMemoryKey(value.key);
1741
- return;
1742
- }
1743
- throw new TypeError("managed memory operation must be scan, read, put, or delete");
1744
- }
1745
-
1746
- function validateMemoryKey(value) {
1747
- if (!value || typeof value !== "object" || Array.isArray(value)) {
1748
- throw new TypeError("managed memory key must be an object");
1749
- }
1750
- assertOnlyFields(value, ["id", "version"], "managed memory key");
1751
- if (!positiveSafeInteger(value.id) || !positiveSafeInteger(value.version)) {
1752
- throw new TypeError("managed memory key id and version must be positive safe integers");
1753
- }
1754
- }
1755
-
1756
1646
  function validateOrganizationUpdate(value) {
1757
1647
  if (!value || typeof value !== "object" || Array.isArray(value)) {
1758
1648
  throw new TypeError("managed organization update must be an object");
@@ -1777,10 +1667,6 @@ function nonnegativeSafeInteger(value) {
1777
1667
  return Number.isSafeInteger(value) && value >= 0;
1778
1668
  }
1779
1669
 
1780
- function nullableNonnegativeSafeInteger(value) {
1781
- return value === null || nonnegativeSafeInteger(value);
1782
- }
1783
-
1784
1670
  function requiredString(value, field) {
1785
1671
  const result = value?.[field];
1786
1672
  if (typeof result !== "string" || result.length === 0) {
package/managed/README.md CHANGED
@@ -93,39 +93,6 @@ const session = await Agent.readSession(
93
93
  Both methods derive the account scope from the cookie or API key; no scope or user
94
94
  identifier is accepted from the caller.
95
95
 
96
- Hosted durable memory is account-owned and independent from session history. The
97
- memory panel can list records and compare-and-swap delete one current key:
98
-
99
- ```js
100
- const memories = await Agent.listMemories({
101
- baseUrl: process.env.NANOCODEX_MANAGED_URL,
102
- apiKey: process.env.NANOCODEX_API_KEY,
103
- });
104
- await Agent.deleteMemory(memories[0].key, {
105
- baseUrl: process.env.NANOCODEX_MANAGED_URL,
106
- apiKey: process.env.NANOCODEX_API_KEY,
107
- });
108
- ```
109
-
110
- Managed agents access this same hosted store through their `memory` tool. It is
111
- never mirrored into a browser, TUI, or other local persistence layer.
112
-
113
- Account memory uses the same derived organization scope and supports scan, read,
114
- put, and delete operations. Scan before writing so the service can reject
115
- duplicates, and retain returned `{ id, version }` keys for compare-and-swap
116
- updates:
117
-
118
- ```js
119
- const scanned = await Agent.memory(
120
- { operation: "scan", query: "production deploy schedule" },
121
- { baseUrl: process.env.NANOCODEX_MANAGED_URL, apiKey: process.env.NANOCODEX_API_KEY },
122
- );
123
- const stored = await Agent.memory(
124
- { operation: "put", content: "Production deploys happen on Tuesdays." },
125
- { baseUrl: process.env.NANOCODEX_MANAGED_URL, apiKey: process.env.NANOCODEX_API_KEY },
126
- );
127
- ```
128
-
129
96
  Browser account owners can read and rename their organization:
130
97
 
131
98
  ```js
@@ -172,8 +139,9 @@ const morning = {
172
139
  await agent.triggers.put("morning-review", morning);
173
140
  await agent.triggers.list();
174
141
  await agent.triggers.get("morning-review");
175
- await agent.triggers.put("morning-review", { ...morning, enabled: false }); // pause
176
- await agent.triggers.put("morning-review", morning); // resume
142
+ await agent.triggers.update("morning-review", { enabled: false }); // pause
143
+ await agent.triggers.update("morning-review", { enabled: true }); // resume
144
+ await agent.triggers.update("morning-review", { cron: "30 9 * * *" }); // preserve other fields
177
145
  await agent.triggers.delete("morning-review");
178
146
  ```
179
147
 
@@ -182,6 +150,7 @@ await agent.triggers.delete("morning-review");
182
150
  | GET | `/v1/agents/:agent/triggers` | `{ data: [...] }` |
183
151
  | GET | `/v1/agents/:agent/triggers/:id` | Trigger or 404 |
184
152
  | PUT | `/v1/agents/:agent/triggers/:id` | Create (201) or replace (200) |
153
+ | PATCH | `/v1/agents/:agent/triggers/:id` | Partial update (200), or 404 |
185
154
  | DELETE | `/v1/agents/:agent/triggers/:id` | Idempotent deletion (204) |
186
155
 
187
156
  The managed-agent web composer also has a **Schedules** button. It opens the
@@ -194,10 +163,12 @@ PUT takes `{ cron, input, timezone?, enabled?, session_mode? }`. Expressions hav
194
163
  (minute, hour, day of month, month, day of week), supporting numeric values,
195
164
  lists, ranges, steps, and month/day names. The time zone defaults to UTC and
196
165
  accepts IANA names. Local times follow daylight saving transitions. Seconds,
197
- macros, and random `H` fields are rejected. Inputs are text up to 64 KiB;
198
- there can be at most 32 triggers per agent. IDs use 1–64 letters, digits,
166
+ macros, and random `H` fields are rejected. Inputs must be non-empty text.
167
+ IDs use 1–64 letters, digits,
199
168
  underscores, or hyphens. PUT is a full replacement; an identical retry preserves
200
- the next occurrence. A changed configuration starts from the next matching time.
169
+ the next occurrence. PATCH (SDK `triggers.update`) changes only supplied fields
170
+ and requires an existing schedule. A concurrent edit may return 409; refresh
171
+ before deciding which changes to retry. A changed configuration starts from the next matching time.
201
172
 
202
173
  `session_mode: "new"` (the default for new configurations) creates a fresh managed
203
174
  session for every occurrence. It copies the source agent's model settings at
package/managed/Voice.mjs CHANGED
@@ -13,6 +13,7 @@ export async function createManagedBrowserVoice(agent, voice, options = {}) {
13
13
  await initializeBrowserEngine(options);
14
14
  const raw = new ManagedBrowserVoice(voice);
15
15
  const voiceSessionId = uuidV7();
16
+ raw.bindSession(voiceSessionId);
16
17
  const startOperationId = crypto.randomUUID();
17
18
  const stopOperationId = crypto.randomUUID();
18
19
  const tailOperationId = crypto.randomUUID();
@@ -38,6 +39,7 @@ export async function createManagedBrowserVoice(agent, voice, options = {}) {
38
39
  if (typeof routed.turn_id !== "string") return [];
39
40
  activeTurnId = routed.turn_id;
40
41
  if (routed.route === "started") startedTurnId = routed.turn_id;
42
+ else if (startedTurnId !== activeTurnId) startedTurnId = undefined;
41
43
  const buffered = pendingEvents;
42
44
  pendingEvents = [];
43
45
  return buffered.map(observe).filter((effects) => effects !== undefined);
@@ -53,7 +55,7 @@ export async function createManagedBrowserVoice(agent, voice, options = {}) {
53
55
  dataChannelControl: true,
54
56
  async start() {
55
57
  const started = await startManagedRealtime(agent, voiceSessionId, startOperationId);
56
- raw.start(JSON.stringify(started.context));
58
+ return raw.start(JSON.stringify(started.context));
57
59
  },
58
60
  callBody(sdp) {
59
61
  const envelope = JSON.parse(raw.callBody(sdp, voiceSessionId));
@@ -136,7 +138,8 @@ export async function createManagedBrowserVoice(agent, voice, options = {}) {
136
138
  }
137
139
  let stopFailure;
138
140
  try {
139
- await stopManagedRealtime(agent, voiceSessionId, stopOperationId);
141
+ await stopManagedRealtime(agent, voiceSessionId, stopOperationId,
142
+ update.delegation?.trim() ? undefined : update.transcript);
140
143
  } catch (error) {
141
144
  stopFailure = error;
142
145
  }
@@ -14,9 +14,6 @@ export type {
14
14
  HistoryCitation as ManagedHistoryCitation,
15
15
  FindSessionsResponse as ManagedFindSessionsResponse,
16
16
  ReadSessionResponse as ManagedReadSessionResponse,
17
- MemoryKey as ManagedMemoryKey,
18
- MemoryRecord as ManagedMemoryRecord,
19
- MemoryResult as ManagedMemoryResult,
20
17
  Organization as ManagedOrganization,
21
18
  Turn as ManagedTurn,
22
19
  TurnResult as ManagedTurnResult,