experimental-a2 0.18.0 → 0.19.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.
Files changed (152) hide show
  1. package/CHANGELOG.md +39 -0
  2. package/dist/{actor-Bjce9_5U.d.ts → actor-C7b-1arn.d.ts} +3 -3
  3. package/dist/{actor-Bjce9_5U.d.ts.map → actor-C7b-1arn.d.ts.map} +1 -1
  4. package/dist/actor-client.d.ts +1 -1
  5. package/dist/actor-react.d.ts +1 -1
  6. package/dist/actor-react.js +1 -1
  7. package/dist/actor.d.ts +1 -1
  8. package/dist/actor.js +2 -2
  9. package/dist/{ai-DrUaMyRt.d.ts → ai-Cz3hqVhk.d.ts} +34 -17
  10. package/dist/ai-Cz3hqVhk.d.ts.map +1 -0
  11. package/dist/ai-client.d.ts +1 -1
  12. package/dist/ai-client.js +2 -2
  13. package/dist/ai-control-CvLbW10B.js +46 -0
  14. package/dist/ai-control-CvLbW10B.js.map +1 -0
  15. package/dist/ai-fx.d.ts +1 -1
  16. package/dist/ai-fx.d.ts.map +1 -1
  17. package/dist/ai-fx.js +122 -44
  18. package/dist/ai-fx.js.map +1 -1
  19. package/dist/{ai-harness-BuLh_EYi.js → ai-harness-BuuC-gJ3.js} +79 -15
  20. package/dist/ai-harness-BuuC-gJ3.js.map +1 -0
  21. package/dist/{ai-harness-DvsPeJeB.d.ts → ai-harness-CV6rXbB3.d.ts} +2 -2
  22. package/dist/ai-harness-CV6rXbB3.d.ts.map +1 -0
  23. package/dist/{ai-BpOGm-zB.js → ai-mMiWul1T.js} +117 -78
  24. package/dist/ai-mMiWul1T.js.map +1 -0
  25. package/dist/ai-pi.d.ts +1 -1
  26. package/dist/ai-pi.d.ts.map +1 -1
  27. package/dist/ai-pi.js +114 -23
  28. package/dist/ai-pi.js.map +1 -1
  29. package/dist/ai-react.d.ts +1 -1
  30. package/dist/ai-react.js +2 -2
  31. package/dist/ai-server.d.ts +3 -3
  32. package/dist/ai-server.d.ts.map +1 -1
  33. package/dist/ai-server.js +127 -55
  34. package/dist/ai-server.js.map +1 -1
  35. package/dist/ai-tool-input-DmSqMUQi.js +40 -0
  36. package/dist/ai-tool-input-DmSqMUQi.js.map +1 -0
  37. package/dist/ai.d.ts +2 -2
  38. package/dist/ai.js +1 -1
  39. package/dist/{client-BReKqwZC.js → client-BhHLoNKF.js} +3 -3
  40. package/dist/{client-BReKqwZC.js.map → client-BhHLoNKF.js.map} +1 -1
  41. package/dist/client.js +1 -1
  42. package/dist/index.d.ts +2 -2
  43. package/dist/{internal-B2T3U4Rq.js → internal-CscSY7K_.js} +4 -2
  44. package/dist/{internal-B2T3U4Rq.js.map → internal-CscSY7K_.js.map} +1 -1
  45. package/dist/react.js +1 -1
  46. package/dist/scheduler-qstash.d.ts +2 -2
  47. package/dist/scheduler-qstash.js +3 -3
  48. package/dist/scheduler-qstash.js.map +1 -1
  49. package/dist/scheduler-vercel.d.ts +7 -7
  50. package/dist/scheduler-vercel.d.ts.map +1 -1
  51. package/dist/scheduler-vercel.js +10 -14
  52. package/dist/scheduler-vercel.js.map +1 -1
  53. package/dist/{server-Dw_k_7kO.js → server-D-Y4pqwu.js} +37 -11
  54. package/dist/server-D-Y4pqwu.js.map +1 -0
  55. package/dist/{server-En7cbi3v.d.ts → server-DYAATFuT.d.ts} +11 -5
  56. package/dist/server-DYAATFuT.d.ts.map +1 -0
  57. package/dist/server.d.ts +2 -2
  58. package/dist/server.js +1 -1
  59. package/dist/{session-clear-Bo-XTFEy.d.ts → session-clear-C-QwTXV1.d.ts} +2 -2
  60. package/dist/session-clear-C-QwTXV1.d.ts.map +1 -0
  61. package/dist/{store-Djdv0bnZ.d.ts → store-CyLUy86v.d.ts} +23 -4
  62. package/dist/store-CyLUy86v.d.ts.map +1 -0
  63. package/dist/store-N8PXxDAS.js.map +1 -1
  64. package/dist/store-memory.d.ts +1 -1
  65. package/dist/store-memory.js +25 -6
  66. package/dist/store-memory.js.map +1 -1
  67. package/dist/{store-polling-B5FcwJfo.js → store-polling-7WJV0YWP.js} +2 -2
  68. package/dist/{store-polling-B5FcwJfo.js.map → store-polling-7WJV0YWP.js.map} +1 -1
  69. package/dist/store-postgres.d.ts +1 -1
  70. package/dist/store-postgres.d.ts.map +1 -1
  71. package/dist/store-postgres.js +55 -25
  72. package/dist/store-postgres.js.map +1 -1
  73. package/dist/{store-presence-polling-BAwS8RWn.js → store-presence-polling-B1ixeqIS.js} +3 -3
  74. package/dist/{store-presence-polling-BAwS8RWn.js.map → store-presence-polling-B1ixeqIS.js.map} +1 -1
  75. package/dist/store-redis-http.d.ts +1 -1
  76. package/dist/store-redis-http.js +3 -3
  77. package/dist/{store-redis-notify-BRITti_o.js → store-redis-notify-CM0AfLY0.js} +88 -37
  78. package/dist/store-redis-notify-CM0AfLY0.js.map +1 -0
  79. package/dist/store-redis.d.ts +1 -1
  80. package/dist/store-redis.js +1 -1
  81. package/dist/store-sqlite.d.ts +1 -1
  82. package/dist/store-sqlite.js +35 -20
  83. package/dist/store-sqlite.js.map +1 -1
  84. package/dist/{wire-rSUglYo8.js → wire-BKxH1vbP.js} +2 -2
  85. package/dist/{wire-rSUglYo8.js.map → wire-BKxH1vbP.js.map} +1 -1
  86. package/docs/agents/03-conversations.mdx +27 -15
  87. package/docs/agents/04-harnesses.mdx +51 -5
  88. package/docs/contract/03-sessions.mdx +1 -1
  89. package/docs/index.mdx +3 -3
  90. package/docs/reference/02-server.mdx +12 -7
  91. package/docs/reference/04-ai.mdx +41 -23
  92. package/docs/reference/05-adapters.mdx +6 -6
  93. package/docs/server/01-handlers.mdx +1 -1
  94. package/docs/server/02-timers.mdx +1 -1
  95. package/docs/server/03-cancellation.mdx +3 -2
  96. package/docs/server/05-production.mdx +4 -3
  97. package/docs/server/06-guarantees.mdx +20 -11
  98. package/examples/playground/DEPLOY.md +11 -4
  99. package/examples/playground/app/a0/[appId]/page.tsx +2 -8
  100. package/examples/playground/app/a0/a0.css +244 -5
  101. package/examples/playground/app/a0/app-view.tsx +2 -1
  102. package/examples/playground/app/a0/attachments.ts +133 -0
  103. package/examples/playground/app/a0/chat.test.tsx +504 -0
  104. package/examples/playground/app/a0/chat.tsx +623 -63
  105. package/examples/playground/app/a0/context-menu.tsx +38 -11
  106. package/examples/playground/app/a0/devtools.tsx +2 -8
  107. package/examples/playground/app/a0/model.test.ts +28 -0
  108. package/examples/playground/app/a0/model.ts +30 -0
  109. package/examples/playground/app/a0/server.ts +13 -19
  110. package/examples/playground/app/a0/uploads/route.ts +29 -0
  111. package/examples/playground/app/agent/[agentId]/agent-client.tsx +2 -1
  112. package/examples/playground/app/agent/[agentId]/agent-queue.test.tsx +1 -0
  113. package/examples/playground/app/agent/model.test.ts +12 -3
  114. package/examples/playground/app/chat/[chatId]/agent-state.test.ts +1 -5
  115. package/examples/playground/next.config.ts +7 -4
  116. package/examples/playground/package.json +3 -1
  117. package/package.json +1 -1
  118. package/src/ai-agent.ts +14 -3
  119. package/src/ai-client-state.ts +21 -9
  120. package/src/ai-context-schema.ts +28 -4
  121. package/src/ai-context.ts +83 -3
  122. package/src/ai-control-server.ts +70 -43
  123. package/src/ai-control-state.ts +3 -4
  124. package/src/ai-control.ts +59 -10
  125. package/src/ai-coordinator.ts +7 -6
  126. package/src/ai-fx.ts +184 -67
  127. package/src/ai-harness.ts +153 -29
  128. package/src/ai-message-shape.ts +41 -0
  129. package/src/ai-pi.ts +161 -46
  130. package/src/ai-stored-state.ts +1 -3
  131. package/src/ai-tool-input.ts +45 -0
  132. package/src/ai.ts +116 -77
  133. package/src/internal.ts +6 -0
  134. package/src/libfx.d.ts +8 -1
  135. package/src/scheduler-qstash.ts +5 -5
  136. package/src/scheduler-vercel.ts +15 -27
  137. package/src/server.ts +58 -12
  138. package/src/store-memory.ts +50 -9
  139. package/src/store-postgres.ts +79 -28
  140. package/src/store-redis-core.ts +112 -31
  141. package/src/store-sqlite.ts +61 -28
  142. package/src/store.ts +22 -3
  143. package/dist/ai-BpOGm-zB.js.map +0 -1
  144. package/dist/ai-DrUaMyRt.d.ts.map +0 -1
  145. package/dist/ai-harness-BuLh_EYi.js.map +0 -1
  146. package/dist/ai-harness-DvsPeJeB.d.ts.map +0 -1
  147. package/dist/server-Dw_k_7kO.js.map +0 -1
  148. package/dist/server-En7cbi3v.d.ts.map +0 -1
  149. package/dist/session-clear-Bo-XTFEy.d.ts.map +0 -1
  150. package/dist/store-Djdv0bnZ.d.ts.map +0 -1
  151. package/dist/store-redis-notify-BRITti_o.js.map +0 -1
  152. package/examples/playground/app/a0/no-photon.cjs +0 -4
@@ -2,7 +2,8 @@ import { a as reducer, o as assertSyncSchema, s as validateSync } from "./reduce
2
2
  import { t as contract } from "./contract-BYKASlCV.js";
3
3
  import { t as idempotentId } from "./idempotent-id-BRJVeylj.js";
4
4
  import { i as withReducerRuntime } from "./reducer-runtime-DQwz_J59.js";
5
- import { u as ambientToolScopeStorage } from "./internal-B2T3U4Rq.js";
5
+ import { u as ambientToolScopeStorage } from "./internal-CscSY7K_.js";
6
+ import { a as unexplainedAdmission, i as steersOn, r as steeringEnd, t as STEERED_REASON } from "./ai-control-CvLbW10B.js";
6
7
  //#region src/equal-json.ts
7
8
  const sameValue = (left, right) => {
8
9
  if (Object.is(left, right)) return true;
@@ -1318,10 +1319,10 @@ function projectAIClientState(state) {
1318
1319
  const admit = () => {
1319
1320
  if (turnId !== null || paused || items.length === 0) return;
1320
1321
  let count = 0;
1321
- for (const item of items) {
1322
+ for (const [index, item] of items.entries()) {
1322
1323
  count += 1;
1323
1324
  show(item.message);
1324
- if (item.generate) {
1325
+ if (item.generate && !steersOn(item, items.slice(index + 1))) {
1325
1326
  turnId = item.id;
1326
1327
  starting = true;
1327
1328
  suspended = false;
@@ -1338,14 +1339,22 @@ function projectAIClientState(state) {
1338
1339
  const item = {
1339
1340
  ...command.action === "steer" ? { afterStepOf: command.turnId } : {},
1340
1341
  id: command.message.id,
1342
+ role: command.message.role,
1341
1343
  message: command.message,
1342
1344
  generate: command.message.role === "user" && (command.action === "steer" || command.generate !== false)
1343
1345
  };
1344
- items = command.action === "steer" ? [item, ...items] : [...items, item];
1346
+ if (command.action === "steer") {
1347
+ const at = steeringEnd(items, command.turnId);
1348
+ items = [
1349
+ ...items.slice(0, at),
1350
+ item,
1351
+ ...items.slice(at)
1352
+ ];
1353
+ } else items = [...items, item];
1345
1354
  break;
1346
1355
  }
1347
1356
  case "edit":
1348
- items = items.map((item) => item.id === command.message.id && item.message.role === command.message.role ? {
1357
+ items = items.map((item) => item.id === command.message.id && item.role === command.message.role ? {
1349
1358
  ...item,
1350
1359
  message: command.message
1351
1360
  } : item);
@@ -1363,16 +1372,18 @@ function projectAIClientState(state) {
1363
1372
  case "send-now": {
1364
1373
  if (turnId !== command.turnId) break;
1365
1374
  const selected = items.find((item) => item.id === command.inputId);
1366
- if (!selected || selected.message.role !== "user") break;
1375
+ if (!selected || selected.role !== "user") break;
1376
+ const { afterStepOf: _, ...promoted } = selected;
1367
1377
  items = items.map((item) => item.id === selected.id ? {
1368
- ...item,
1378
+ ...promoted,
1369
1379
  generate: true,
1370
1380
  ...turnId === null ? {} : { afterStepOf: turnId }
1371
1381
  } : item);
1382
+ const others = items.filter((item) => item.id !== selected.id);
1372
1383
  items = moveItem({
1373
1384
  items,
1374
1385
  inputId: command.inputId,
1375
- beforeId: items[0]?.id ?? null
1386
+ beforeId: others[turnId === null ? 0 : steeringEnd(others, turnId)]?.id ?? null
1376
1387
  });
1377
1388
  break;
1378
1389
  }
@@ -1533,29 +1544,31 @@ const controlCommit = (options) => {
1533
1544
  };
1534
1545
  };
1535
1546
  //#endregion
1536
- //#region src/ai-control.ts
1537
- const initialControlState = () => ({
1538
- pendingStops: {},
1539
- inbox: {
1540
- paused: false,
1541
- items: []
1542
- },
1543
- active: null,
1544
- coordinator: {
1545
- closed: false,
1546
- queued: []
1547
- }
1548
- });
1547
+ //#region src/ai-message-shape.ts
1548
+ const isRecord$1 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
1549
+ const isJSONCompatible = (value, seen = /* @__PURE__ */ new Set()) => {
1550
+ if (value === null || typeof value === "string" || typeof value === "boolean") return true;
1551
+ if (typeof value === "number") return Number.isFinite(value);
1552
+ if (typeof value === "undefined") return true;
1553
+ if (typeof value !== "object") return false;
1554
+ if (seen.has(value)) return false;
1555
+ seen.add(value);
1556
+ const valid = Array.isArray(value) ? value.every((item) => isJSONCompatible(item, seen)) : Object.getPrototypeOf(value) === Object.prototype && Object.values(value).every((item) => isJSONCompatible(item, seen));
1557
+ seen.delete(value);
1558
+ return valid;
1559
+ };
1560
+ /** Whether a value has an AI SDK `UIMessage`'s shape. */
1561
+ const isUIMessage = (value) => isRecord$1(value) && typeof value["id"] === "string" && (value["role"] === "user" || value["role"] === "assistant" || value["role"] === "system") && Array.isArray(value["parts"]) && value["parts"].every((part) => isRecord$1(part) && typeof part["type"] === "string") && isJSONCompatible(value);
1549
1562
  //#endregion
1550
1563
  //#region src/ai-id.ts
1551
1564
  const AI_EVENT_ID_PREFIX = "a2.ai:";
1552
1565
  const aiEventId = async (...parts) => `${AI_EVENT_ID_PREFIX}${await idempotentId(...parts)}`;
1553
1566
  //#endregion
1554
1567
  //#region src/ai-stored-state.ts
1555
- const isRecord$1 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
1556
- const hasKeys = (value, { required, optional = [] }) => isRecord$1(value) && required.every((key) => Object.hasOwn(value, key)) && Object.keys(value).every((key) => required.includes(key) || optional.includes(key));
1568
+ const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
1569
+ const hasKeys = (value, { required, optional = [] }) => isRecord(value) && required.every((key) => Object.hasOwn(value, key)) && Object.keys(value).every((key) => required.includes(key) || optional.includes(key));
1557
1570
  const reference = (value) => {
1558
- if (!isRecord$1(value) || !Object.hasOwn(value, "$a2Value")) return void 0;
1571
+ if (!isRecord(value) || !Object.hasOwn(value, "$a2Value")) return void 0;
1559
1572
  const id = value["$a2Value"];
1560
1573
  if (typeof id !== "number" || !Number.isSafeInteger(id) || id < 0 || Object.keys(value).length !== 1) throw new TypeError("invalid AI payload reference");
1561
1574
  return id;
@@ -1574,13 +1587,13 @@ const payloadFields = /* @__PURE__ */ new Set([
1574
1587
  const keyFor = (value) => value === void 0 ? "undefined" : JSON.stringify(value);
1575
1588
  const restore = (current, undo) => {
1576
1589
  if (undo === void 0) return current;
1577
- if (!isRecord$1(undo)) throw new TypeError("invalid AI recovery change");
1590
+ if (!isRecord(undo)) throw new TypeError("invalid AI recovery change");
1578
1591
  if ("value" in undo) {
1579
1592
  if (!hasKeys(undo, { required: ["value"] })) throw new TypeError("invalid AI replacement recovery");
1580
1593
  return undo.value;
1581
1594
  }
1582
1595
  if ("length" in undo) {
1583
- if (!hasKeys(undo, { required: ["length", "items"] }) || !Number.isSafeInteger(undo.length) || undo.length < 0 || undo.length > 4294967295 || !isRecord$1(undo.items) || !Array.isArray(current)) throw new TypeError("invalid AI array recovery");
1596
+ if (!hasKeys(undo, { required: ["length", "items"] }) || !Number.isSafeInteger(undo.length) || undo.length < 0 || undo.length > 4294967295 || !isRecord(undo.items) || !Array.isArray(current)) throw new TypeError("invalid AI array recovery");
1584
1597
  const items = Object.entries(undo.items);
1585
1598
  let added = 0;
1586
1599
  for (const [key, patch] of items) {
@@ -1594,7 +1607,7 @@ const restore = (current, undo) => {
1594
1607
  for (const [index, patch] of items) result[Number(index)] = restore(current[Number(index)] ?? null, patch);
1595
1608
  return result;
1596
1609
  }
1597
- if (!hasKeys(undo, { required: ["fields", "remove"] }) || !isRecord$1(undo.fields) || !Array.isArray(undo.remove) || Object.keys(undo.remove).length !== undo.remove.length || Array.from(undo.remove).some((key) => typeof key !== "string") || new Set(undo.remove).size !== undo.remove.length || !isRecord$1(current) || reference(current) !== void 0) throw new TypeError("invalid AI object recovery");
1610
+ if (!hasKeys(undo, { required: ["fields", "remove"] }) || !isRecord(undo.fields) || !Array.isArray(undo.remove) || Object.keys(undo.remove).length !== undo.remove.length || Array.from(undo.remove).some((key) => typeof key !== "string") || new Set(undo.remove).size !== undo.remove.length || !isRecord(current) || reference(current) !== void 0) throw new TypeError("invalid AI object recovery");
1598
1611
  const result = { ...current };
1599
1612
  for (const key of undo.remove) delete result[key];
1600
1613
  for (const [key, patch] of Object.entries(undo.fields)) {
@@ -1622,7 +1635,7 @@ const difference = (current, before) => {
1622
1635
  items
1623
1636
  };
1624
1637
  }
1625
- if (isRecord$1(current) && isRecord$1(before)) {
1638
+ if (isRecord(current) && isRecord(before)) {
1626
1639
  const fields = {};
1627
1640
  const remove = Object.keys(current).filter((key) => !Object.hasOwn(before, key));
1628
1641
  for (const [key, value] of Object.entries(before)) {
@@ -1661,7 +1674,7 @@ const storeState = (state) => {
1661
1674
  return { $a2Value: id };
1662
1675
  };
1663
1676
  const pack = (value, field) => {
1664
- if (value === void 0 || typeof value === "string" && value.length > 80 || field !== void 0 && payloadFields.has(field) && !(field === "input" && isRecord$1(value) && "message" in value) || isRecord$1(value) && Object.hasOwn(value, "$a2Value")) return intern(value);
1677
+ if (value === void 0 || typeof value === "string" && value.length > 80 || field !== void 0 && payloadFields.has(field) || isRecord(value) && Object.hasOwn(value, "$a2Value")) return intern(value);
1665
1678
  if (value === null || typeof value === "string" || typeof value === "boolean" || typeof value === "number") return value;
1666
1679
  if (typeof value !== "object") throw new TypeError("AI state must contain JSON values");
1667
1680
  const cached = packed.get(value);
@@ -1698,7 +1711,7 @@ const loadState = (stored) => {
1698
1711
  if (Object.keys(value).length !== value.length) throw new TypeError("invalid AI stored array");
1699
1712
  return Array.from(value, unpack);
1700
1713
  }
1701
- if (isRecord$1(value)) return Object.fromEntries(Object.entries(value).map(([key, item]) => [key, unpack(item)]));
1714
+ if (isRecord(value)) return Object.fromEntries(Object.entries(value).map(([key, item]) => [key, unpack(item)]));
1702
1715
  if (value === null || typeof value === "boolean" || typeof value === "string" || typeof value === "number" && Number.isFinite(value)) return value;
1703
1716
  throw new TypeError("invalid AI stored value");
1704
1717
  };
@@ -1708,7 +1721,7 @@ const loadState = (stored) => {
1708
1721
  if (!hasKeys(saved, {
1709
1722
  required: ["fields"],
1710
1723
  optional: ["tools"]
1711
- }) || !isRecord$1(saved.fields) || "tools" in saved && saved.tools === void 0) throw new TypeError("invalid AI projection recovery");
1724
+ }) || !isRecord(saved.fields) || "tools" in saved && saved.tools === void 0) throw new TypeError("invalid AI projection recovery");
1712
1725
  const projection = unpack(saved.fields);
1713
1726
  if (projection.base !== void 0) {
1714
1727
  assertMessageBase(projection.base);
@@ -1735,7 +1748,7 @@ const withAIStorage = (source) => withReducerRuntime({
1735
1748
  "fields",
1736
1749
  "projection",
1737
1750
  "values"
1738
- ] }) || value["version"] !== 3 || !Array.isArray(value["values"]) || !isRecord$1(value["fields"]) || value["projection"] !== null && !isRecord$1(value["projection"])) throw new TypeError("invalid AI stored state");
1751
+ ] }) || value["version"] !== 3 || !Array.isArray(value["values"]) || !isRecord(value["fields"]) || value["projection"] !== null && !isRecord(value["projection"])) throw new TypeError("invalid AI stored state");
1739
1752
  const state = value;
1740
1753
  if (Object.keys(state.values).length !== state.values.length) throw new TypeError("invalid AI payload table");
1741
1754
  for (const payload of state.values) if (!hasKeys(payload, {
@@ -1824,52 +1837,36 @@ const schema = (label, parse) => ({ "~standard": {
1824
1837
  }
1825
1838
  }
1826
1839
  } });
1827
- const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
1828
- const isJSONCompatible = (value, seen = /* @__PURE__ */ new Set()) => {
1829
- if (value === null || typeof value === "string" || typeof value === "boolean") return true;
1830
- if (typeof value === "number") return Number.isFinite(value);
1831
- if (typeof value === "undefined") return true;
1832
- if (typeof value !== "object") return false;
1833
- if (seen.has(value)) return false;
1834
- seen.add(value);
1835
- const valid = Array.isArray(value) ? value.every((item) => isJSONCompatible(item, seen)) : Object.getPrototypeOf(value) === Object.prototype && Object.values(value).every((item) => isJSONCompatible(item, seen));
1836
- seen.delete(value);
1837
- return valid;
1838
- };
1839
1840
  const jsonObject = (label, check) => schema(label, (value) => {
1840
- if (!isRecord(value) || !isJSONCompatible(value) || !check(value)) return issue(`invalid ${label}`);
1841
+ if (!isRecord$1(value) || !isJSONCompatible(value) || !check(value)) return issue(`invalid ${label}`);
1841
1842
  return { value };
1842
1843
  });
1843
1844
  const stringField = (value, key) => typeof value[key] === "string" && value[key].length > 0;
1844
1845
  const optionalIndexField = (value, key) => value[key] === void 0 || typeof value[key] === "number" && Number.isSafeInteger(value[key]) && value[key] >= 0;
1845
1846
  const optionalStringField = (value, key) => value[key] === void 0 || typeof value[key] === "string";
1846
- const messageValue = (value) => isRecord(value) && typeof value["id"] === "string" && (value["role"] === "user" || value["role"] === "assistant" || value["role"] === "system") && Array.isArray(value["parts"]) && value["parts"].every((part) => isRecord(part) && typeof part["type"] === "string") && isJSONCompatible(value);
1847
1847
  const validateMessage = (value, messageSchema) => {
1848
- if (!messageValue(value)) return issue("invalid AI SDK UIMessage");
1848
+ if (!isUIMessage(value)) return issue("invalid AI SDK UIMessage");
1849
1849
  if (!messageSchema) return { value };
1850
1850
  return validateSync(messageSchema, value, "the AI message");
1851
1851
  };
1852
- const messagePayloadSchema = (messageSchema) => schema("ai.message.created", (value) => {
1853
- if (!isRecord(value)) return issue("invalid ai.message.created payload");
1854
- const message = validateMessage(value["message"], messageSchema);
1855
- if (message.issues) return { issues: message.issues };
1856
- const generate = value["generate"];
1857
- if (generate !== void 0 && typeof generate !== "boolean") return issue("invalid ai.message.created generate flag");
1852
+ const messageCreatedSchema = schema("ai.message.created", (value) => {
1853
+ if (!isRecord$1(value) || Object.keys(value).some((key) => key !== "messageId" && key !== "role" && key !== "generate") || !stringField(value, "messageId") || value["role"] !== "user" && value["role"] !== "assistant" && value["role"] !== "system" || value["generate"] !== void 0 && value["generate"] !== false) return issue("invalid ai.message.created payload");
1858
1854
  return { value: {
1859
- message: message.value,
1860
- ...generate === false ? { generate: false } : {}
1855
+ messageId: value["messageId"],
1856
+ role: value["role"],
1857
+ ...value["generate"] === false ? { generate: false } : {}
1861
1858
  } };
1862
1859
  });
1863
1860
  const progressPayloadSchema = schema("ai.generation.progress", (value) => {
1864
- if (!isRecord(value) || !isJSONCompatible(value) || typeof value["startIndex"] !== "number" || !optionalIndexField(value, "startIndex") || !Array.isArray(value["chunks"]) || !value["chunks"].every((chunk) => isRecord(chunk) && typeof chunk["type"] === "string")) return issue("invalid ai.generation.progress payload");
1861
+ if (!isRecord$1(value) || !isJSONCompatible(value) || typeof value["startIndex"] !== "number" || !optionalIndexField(value, "startIndex") || !Array.isArray(value["chunks"]) || !value["chunks"].every((chunk) => isRecord$1(chunk) && typeof chunk["type"] === "string")) return issue("invalid ai.generation.progress payload");
1865
1862
  return { value };
1866
1863
  });
1867
1864
  const completedPayloadSchema = schema("ai.generation.completed", (value) => {
1868
- if (!isRecord(value) || !isJSONCompatible(value) || !stringField(value, "requestId") || !stringField(value, "messageId") || !stringField(value, "generationId") || !stringField(value, "responseMessageId") || !optionalIndexField(value, "inputTokenEstimate") || !optionalStringField(value, "finishReason")) return issue("invalid ai.generation.completed payload");
1865
+ if (!isRecord$1(value) || !isJSONCompatible(value) || !stringField(value, "requestId") || !stringField(value, "messageId") || !stringField(value, "generationId") || !stringField(value, "responseMessageId") || !optionalIndexField(value, "inputTokenEstimate") || !optionalStringField(value, "finishReason")) return issue("invalid ai.generation.completed payload");
1869
1866
  return { value };
1870
1867
  });
1871
1868
  const compactionPayloadSchema = (messageSchema) => schema("ai.compaction.completed", (value) => {
1872
- if (!isRecord(value) || !isJSONCompatible(value) || !stringField(value, "generationId") || !stringField(value, "throughMessageId") || !optionalIndexField(value, "throughIndex") || !optionalStringField(value, "summary") || !Array.isArray(value["messages"]) || value["retainedMessageIds"] !== void 0 && (!Array.isArray(value["retainedMessageIds"]) || !value["retainedMessageIds"].every((candidate) => typeof candidate === "string"))) return issue("invalid ai.compaction.completed payload");
1869
+ if (!isRecord$1(value) || !isJSONCompatible(value) || !stringField(value, "generationId") || !stringField(value, "throughMessageId") || !optionalIndexField(value, "throughIndex") || !optionalStringField(value, "summary") || !Array.isArray(value["messages"]) || value["retainedMessageIds"] !== void 0 && (!Array.isArray(value["retainedMessageIds"]) || !value["retainedMessageIds"].every((candidate) => typeof candidate === "string"))) return issue("invalid ai.compaction.completed payload");
1873
1870
  const messages = [];
1874
1871
  for (const candidate of value["messages"]) {
1875
1872
  const result = validateMessage(candidate, messageSchema);
@@ -1891,7 +1888,7 @@ function createEvents(options) {
1891
1888
  if (messageSchema) assertSyncSchema(messageSchema, "the AI message schema");
1892
1889
  const definition = {
1893
1890
  "ai.control.requested": schema("ai.control.requested", (value) => {
1894
- if (!isRecord(value) || !isJSONCompatible(value)) return issue("invalid AI command");
1891
+ if (!isRecord$1(value) || !isJSONCompatible(value)) return issue("invalid AI command");
1895
1892
  const action = value["action"];
1896
1893
  const forwarded = {
1897
1894
  approval: ["ai.approval.responded", "response"],
@@ -1905,6 +1902,7 @@ function createEvents(options) {
1905
1902
  const [type, key] = forwarded[action];
1906
1903
  const payload = validateSync(definition[type], value[key], "AI command");
1907
1904
  if (payload.issues) return { issues: payload.issues };
1905
+ if (action === "interrupt" && isRecord$1(payload.value) && payload.value["reason"] === "steered after step") return issue(`the interruption reason "${STEERED_REASON}" is reserved for steering`);
1908
1906
  return { value: {
1909
1907
  action,
1910
1908
  [key]: payload.value
@@ -1923,10 +1921,10 @@ function createEvents(options) {
1923
1921
  if (action === "remove" && stringField(value, "inputId") || action === "send-now" && stringField(value, "inputId") && (value["turnId"] === null || stringField(value, "turnId")) && optionalIndexField(value, "lastSeenIndex") || action === "move" && stringField(value, "inputId") && (value["beforeId"] === null || stringField(value, "beforeId")) || action === "stop" && stringField(value, "turnId") && optionalIndexField(value, "lastSeenIndex") || action === "pause" && (value["when"] === "now" || value["when"] === "after-turn") || action === "resume") return { value };
1924
1922
  return issue("invalid AI command");
1925
1923
  }),
1926
- "ai.control.committed": jsonObject("ai.control.committed", (value) => Array.isArray(value["changes"]) && isRecord(value["view"])),
1924
+ "ai.control.committed": jsonObject("ai.control.committed", (value) => Array.isArray(value["changes"]) && isRecord$1(value["view"])),
1927
1925
  "ai.control.decided": jsonObject("ai.control.decided", (value) => stringField(value, "commandId") && (value["outcome"] === "applied" || value["outcome"] === "rejected")),
1928
1926
  "ai.work.reported": schema("ai.work.reported", (value) => {
1929
- if (!isRecord(value) || !isJSONCompatible(value) || !stringField(value, "workId") || !stringField(value, "turnId") || typeof value["version"] !== "number" || !optionalIndexField(value, "version") || typeof value["attempt"] !== "number" || !Number.isSafeInteger(value["attempt"]) || value["attempt"] < 1 || typeof value["settled"] !== "boolean" || value["kind"] !== "model" && value["kind"] !== "tool" || !Array.isArray(value["events"])) return issue("invalid AI work report");
1927
+ if (!isRecord$1(value) || !isJSONCompatible(value) || !stringField(value, "workId") || !stringField(value, "turnId") || typeof value["version"] !== "number" || !optionalIndexField(value, "version") || typeof value["attempt"] !== "number" || !Number.isSafeInteger(value["attempt"]) || value["attempt"] < 1 || typeof value["settled"] !== "boolean" || value["kind"] !== "model" && value["kind"] !== "tool" || !Array.isArray(value["events"])) return issue("invalid AI work report");
1930
1928
  const allowed = /* @__PURE__ */ new Set([
1931
1929
  "ai.generation.started",
1932
1930
  "ai.generation.completed",
@@ -1941,7 +1939,7 @@ function createEvents(options) {
1941
1939
  ]);
1942
1940
  const events = [];
1943
1941
  for (const event of value["events"]) {
1944
- if (!isRecord(event) || !stringField(event, "id") || typeof event["type"] !== "string" || !allowed.has(event["type"]) || value["kind"] === "tool" && event["type"] !== "ai.tool.result") return issue("invalid AI work fact");
1942
+ if (!isRecord$1(event) || !stringField(event, "id") || typeof event["type"] !== "string" || !allowed.has(event["type"]) || value["kind"] === "tool" && event["type"] !== "ai.tool.result") return issue("invalid AI work fact");
1945
1943
  const parsed = validateSync(definition[event["type"]], event["payload"], "AI work fact");
1946
1944
  if (parsed.issues) return { issues: parsed.issues };
1947
1945
  events.push({
@@ -1955,11 +1953,11 @@ function createEvents(options) {
1955
1953
  events
1956
1954
  } };
1957
1955
  }),
1958
- "ai.tool.execution.requested": jsonObject("ai.tool.execution.requested", (value) => stringField(value, "turnId") && optionalIndexField(value, "version") && isRecord(value["call"]) && isRecord(value["generation"])),
1956
+ "ai.tool.execution.requested": jsonObject("ai.tool.execution.requested", (value) => stringField(value, "turnId") && optionalIndexField(value, "version") && isRecord$1(value["call"]) && isRecord$1(value["generation"])),
1959
1957
  "ai.generation.paused": jsonObject("ai.generation.paused", (value) => stringField(value, "generationId") && stringField(value, "messageId")),
1960
1958
  "ai.session.created": jsonObject("ai.session.created", () => true),
1961
1959
  "ai.session.closed": jsonObject("ai.session.closed", (value) => optionalStringField(value, "reason")),
1962
- "ai.message.created": messagePayloadSchema(messageSchema),
1960
+ "ai.message.created": messageCreatedSchema,
1963
1961
  "ai.message.completed": jsonObject("ai.message.completed", (value) => stringField(value, "messageId")),
1964
1962
  "ai.message.interrupted": jsonObject("ai.message.interrupted", (value) => stringField(value, "messageId") && (stringField(value, "generationId") && value["requestId"] === void 0 || stringField(value, "requestId") && value["generationId"] === void 0) && optionalStringField(value, "reason") && (value["lastSeenIndex"] === void 0 || typeof value["lastSeenIndex"] === "number" && Number.isInteger(value["lastSeenIndex"]) && value["lastSeenIndex"] >= 0)),
1965
1963
  "ai.generation.requested": jsonObject("ai.generation.requested", (value) => stringField(value, "messageId") && stringField(value, "responseMessageId") && optionalStringField(value, "sourceGenerationId") && (value["reason"] !== "tool" || stringField(value, "sourceGenerationId")) && (value["reason"] === "message" || value["reason"] === "tool" || value["reason"] === "input" || value["reason"] === "retry" || value["reason"] === "resume")),
@@ -1977,13 +1975,16 @@ function createEvents(options) {
1977
1975
  "ai.compaction.requested": jsonObject("ai.compaction.requested", (value) => stringField(value, "generationId") && stringField(value, "throughMessageId") && optionalIndexField(value, "throughIndex") && optionalIndexField(value, "inputTokenEstimate") && optionalIndexField(value, "inputTokens") && optionalIndexField(value, "thresholdTokens")),
1978
1976
  "ai.compaction.completed": compactionPayloadSchema(messageSchema),
1979
1977
  "ai.model.metadata.requested": jsonObject("ai.model.metadata.requested", (value) => stringField(value, "modelId")),
1980
- "ai.model.metadata.resolved": jsonObject("ai.model.metadata.resolved", (value) => stringField(value, "modelId") && (value["limits"] === null || isRecord(value["limits"]) && typeof value["limits"]["contextWindow"] === "number" && Number.isSafeInteger(value["limits"]["contextWindow"]) && value["limits"]["contextWindow"] > 0 && typeof value["limits"]["maxOutputTokens"] === "number" && Number.isSafeInteger(value["limits"]["maxOutputTokens"]) && value["limits"]["maxOutputTokens"] > 0))
1978
+ "ai.model.metadata.resolved": jsonObject("ai.model.metadata.resolved", (value) => stringField(value, "modelId") && (value["limits"] === null || isRecord$1(value["limits"]) && typeof value["limits"]["contextWindow"] === "number" && Number.isSafeInteger(value["limits"]["contextWindow"]) && value["limits"]["contextWindow"] > 0 && typeof value["limits"]["maxOutputTokens"] === "number" && Number.isSafeInteger(value["limits"]["maxOutputTokens"]) && value["limits"]["maxOutputTokens"] > 0))
1981
1979
  };
1982
1980
  return definition;
1983
1981
  }
1984
1982
  const events = createEvents();
1985
1983
  const initialState = () => ({
1986
- inbox: initialControlState().inbox,
1984
+ inbox: {
1985
+ paused: false,
1986
+ items: []
1987
+ },
1987
1988
  pendingQueueCommands: [],
1988
1989
  active: null,
1989
1990
  starting: false,
@@ -2045,6 +2046,18 @@ function reduceAIState(state, event) {
2045
2046
  after: reduceAIEvent(state, event)
2046
2047
  }));
2047
2048
  }
2049
+ /**
2050
+ * An input's content, while it is queued or about to enter the transcript:
2051
+ * its inbox item's, or its pending send's or steer's, whose command id is the
2052
+ * input's id.
2053
+ */
2054
+ function inputContent(state, id) {
2055
+ const queued = state.inbox.items.find((item) => item.id === id)?.message;
2056
+ if (queued !== void 0) return queued;
2057
+ const command = state.pendingQueueCommands?.find((item) => item.commandId === id)?.command;
2058
+ if (command?.action === "send" || command?.action === "steer") return command.message;
2059
+ throw unexplainedAdmission(id);
2060
+ }
2048
2061
  function reduceAIEvent(state, event) {
2049
2062
  if (state.status === "closed") return state;
2050
2063
  switch (event.type) {
@@ -2064,18 +2077,35 @@ function reduceAIEvent(state, event) {
2064
2077
  }
2065
2078
  case "ai.control.committed": {
2066
2079
  const commit = event.payload;
2067
- const visible = applyControlChanges({
2068
- state: {
2069
- inbox: state.inbox,
2070
- active: state.active
2071
- },
2072
- changes: commit.changes.filter((change) => change.path[0] === "inbox" || change.path[0] === "active")
2073
- });
2080
+ const changes = commit.changes.filter((change) => change.path[0] === "inbox" || change.path[0] === "active");
2081
+ let { inbox, active } = state;
2082
+ if (changes.length > 0) {
2083
+ const visible = applyControlChanges({
2084
+ state: {
2085
+ inbox: {
2086
+ ...inbox,
2087
+ items: inbox.items.map(({ message: _, ...input }) => input)
2088
+ },
2089
+ active
2090
+ },
2091
+ changes
2092
+ });
2093
+ const queued = new Map(inbox.items.map((item) => [item.id, item]));
2094
+ inbox = {
2095
+ ...visible.inbox,
2096
+ items: visible.inbox.items.map((input) => ({
2097
+ ...input,
2098
+ message: queued.get(input.id)?.message ?? inputContent(state, input.id)
2099
+ }))
2100
+ };
2101
+ active = visible.active;
2102
+ }
2074
2103
  const { phase } = commit.view;
2075
2104
  const status = commit.view.closed ? "closed" : phase === "paused" || phase === "pausing" || commit.view.paused && phase === null ? "paused" : phase === "failed" ? "failed" : phase === "starting" || phase === "generating" ? "generating" : phase ? "waiting" : "idle";
2076
2105
  return {
2077
2106
  ...state,
2078
- ...visible,
2107
+ inbox,
2108
+ active,
2079
2109
  status,
2080
2110
  activeRequestId: commit.view.requestId,
2081
2111
  activeRequestReason: commit.view.requestReason,
@@ -2088,9 +2118,17 @@ function reduceAIEvent(state, event) {
2088
2118
  if (receipt.work) return state;
2089
2119
  const pending = state.pendingQueueCommands ?? [];
2090
2120
  const settled = pending.find((item) => item.commandId === receipt.commandId);
2121
+ const edited = receipt.outcome === "applied" && settled?.command.action === "edit" ? settled.command.message : void 0;
2091
2122
  return {
2092
2123
  ...state,
2093
2124
  receipt,
2125
+ ...edited === void 0 ? {} : { inbox: {
2126
+ ...state.inbox,
2127
+ items: state.inbox.items.map((item) => item.id === edited.id ? {
2128
+ ...item,
2129
+ message: edited
2130
+ } : item)
2131
+ } },
2094
2132
  ...settled === void 0 ? {} : { pendingQueueCommands: pending.filter((item) => item.commandId !== receipt.commandId) }
2095
2133
  };
2096
2134
  }
@@ -2172,7 +2210,8 @@ function reduceAIEvent(state, event) {
2172
2210
  activeProjection: null
2173
2211
  };
2174
2212
  case "ai.message.created": {
2175
- const { message } = event.payload;
2213
+ const { messageId } = event.payload;
2214
+ const message = inputContent(state, messageId);
2176
2215
  return {
2177
2216
  ...state,
2178
2217
  messages: upsertMessage(state.messages, message),
@@ -2804,7 +2843,7 @@ function handlerContext(contract) {
2804
2843
  return scope.context;
2805
2844
  }
2806
2845
  /** The built-in AI reducer's name, and so its checkpoint identity. */
2807
- const AI_STATE_REDUCER = "a2.ai.state.v26";
2846
+ const AI_STATE_REDUCER = "a2.ai.state.v27";
2808
2847
  /**
2809
2848
  * The built-in AI protocol plus application events, as a core contract whose
2810
2849
  * reducer folds `AIState`. Isomorphic: share it when a client needs
@@ -2851,6 +2890,6 @@ function agentContract(options) {
2851
2890
  return shared;
2852
2891
  }
2853
2892
  //#endregion
2854
- export { interruptUIMessage as A, reduceToolActivity as C, flattenProgressBatches as D, upsertResponse as E, assertMessageBase as M, restoreMessageBase as N, progressBatches as O, projectedMessage as S, upsertMessage as T, applyControlChanges as _, events as a, findMessage as b, reasoningLevels as c, appSlice as d, isAgentContract as f, initialControlState as g, aiEventId as h, deriveUIMessages as i, pauseUIMessage as j, progressOwnedBy as k, reduceAIState as l, AI_EVENT_ID_PREFIX as m, agentContract as n, handlerContext as o, withAIStorage as p, createEvents as r, inputs as s, AI_STATE_REDUCER as t, appReducerOf as u, controlCommit as v, retainMessageBase as w, isCompactionPart as x, advanceProjection as y };
2893
+ export { interruptUIMessage as A, reduceToolActivity as C, flattenProgressBatches as D, upsertResponse as E, assertMessageBase as M, restoreMessageBase as N, progressBatches as O, projectedMessage as S, upsertMessage as T, applyControlChanges as _, events as a, findMessage as b, reasoningLevels as c, appSlice as d, isAgentContract as f, isUIMessage as g, aiEventId as h, deriveUIMessages as i, pauseUIMessage as j, progressOwnedBy as k, reduceAIState as l, AI_EVENT_ID_PREFIX as m, agentContract as n, handlerContext as o, withAIStorage as p, createEvents as r, inputs as s, AI_STATE_REDUCER as t, appReducerOf as u, controlCommit as v, retainMessageBase as w, isCompactionPart as x, advanceProjection as y };
2855
2894
 
2856
- //# sourceMappingURL=ai-BpOGm-zB.js.map
2895
+ //# sourceMappingURL=ai-mMiWul1T.js.map