experimental-a2 0.16.0 → 0.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +360 -0
- package/dist/{actor-ohPC-81x.d.ts → actor-DBbe5xeE.d.ts} +21 -11
- package/dist/actor-DBbe5xeE.d.ts.map +1 -0
- package/dist/actor-client.d.ts +2 -5
- package/dist/actor-client.d.ts.map +1 -1
- package/dist/actor-client.js +13 -4
- package/dist/actor-client.js.map +1 -1
- package/dist/actor-react.d.ts +5 -3
- package/dist/actor-react.d.ts.map +1 -1
- package/dist/actor-react.js +20 -10
- package/dist/actor-react.js.map +1 -1
- package/dist/{actor-shared-B5tJfzt-.js → actor-shared-t7w8xIKP.js} +12 -7
- package/dist/actor-shared-t7w8xIKP.js.map +1 -0
- package/dist/actor.d.ts +1 -1
- package/dist/actor.js +42 -23
- package/dist/actor.js.map +1 -1
- package/dist/{ai-CrLYwNEx.js → ai-UrWfKeuv.js} +966 -541
- package/dist/ai-UrWfKeuv.js.map +1 -0
- package/dist/ai-client.d.ts +27 -0
- package/dist/ai-client.d.ts.map +1 -0
- package/dist/ai-client.js +29 -0
- package/dist/ai-client.js.map +1 -0
- package/dist/ai-react.d.ts +96 -0
- package/dist/ai-react.d.ts.map +1 -0
- package/dist/ai-react.js +142 -0
- package/dist/ai-react.js.map +1 -0
- package/dist/ai-server.browser.js +2 -2
- package/dist/ai-server.browser.js.map +1 -1
- package/dist/ai-server.d.ts +80 -36
- package/dist/ai-server.d.ts.map +1 -1
- package/dist/ai-server.js +466 -265
- package/dist/ai-server.js.map +1 -1
- package/dist/{ai-HJ9fHfYI.d.ts → ai-vpGtcaCB.d.ts} +115 -101
- package/dist/ai-vpGtcaCB.d.ts.map +1 -0
- package/dist/ai.d.ts +2 -2
- package/dist/ai.js +2 -2
- package/dist/{client-BYzHjkwU.js → client-COaiaDub.js} +447 -80
- package/dist/client-COaiaDub.js.map +1 -0
- package/dist/{client-CzyacQpJ.d.ts → client-qRnRUzBS.d.ts} +14 -13
- package/dist/client-qRnRUzBS.d.ts.map +1 -0
- package/dist/client.d.ts +4 -2
- package/dist/client.js +1 -1
- package/dist/{contract-CKRg_E4q.js → contract-BYKASlCV.js} +12 -21
- package/dist/contract-BYKASlCV.js.map +1 -0
- package/dist/{idempotent-replay-DVOlyYbx.js → idempotent-replay-JdgMQRRm.js} +2 -2
- package/dist/{idempotent-replay-DVOlyYbx.js.map → idempotent-replay-JdgMQRRm.js.map} +1 -1
- package/dist/index.d.ts +6 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -3
- package/dist/{errors-DvhSXnxk.d.ts → initial-snapshot-BrLso5x_.d.ts} +7 -3
- package/dist/initial-snapshot-BrLso5x_.d.ts.map +1 -0
- package/dist/{internal-Dq2qYxou.js → internal-CbVvEwad.js} +23 -2
- package/dist/internal-CbVvEwad.js.map +1 -0
- package/dist/otel.d.ts +1 -1
- package/dist/react-BbJ-g6Uu.d.ts +86 -0
- package/dist/react-BbJ-g6Uu.d.ts.map +1 -0
- package/dist/react.d.ts +2 -122
- package/dist/react.js +107 -100
- package/dist/react.js.map +1 -1
- package/dist/reducer-BAXc8JyF.js +87 -0
- package/dist/reducer-BAXc8JyF.js.map +1 -0
- package/dist/{reducer-otzHjuJj.d.ts → reducer-BYu6DT7h.d.ts} +45 -29
- package/dist/reducer-BYu6DT7h.d.ts.map +1 -0
- package/dist/reducer-runtime-DQwz_J59.js +28 -0
- package/dist/reducer-runtime-DQwz_J59.js.map +1 -0
- package/dist/scheduler-qstash.d.ts +3 -3
- package/dist/scheduler-qstash.d.ts.map +1 -1
- package/dist/scheduler-qstash.js +33 -10
- package/dist/scheduler-qstash.js.map +1 -1
- package/dist/{scheduler-task-BpzhPnRS.js → scheduler-task-X1SToDjP.js} +18 -3
- package/dist/scheduler-task-X1SToDjP.js.map +1 -0
- package/dist/scheduler-vercel.d.ts +4 -9
- package/dist/scheduler-vercel.d.ts.map +1 -1
- package/dist/scheduler-vercel.js +25 -44
- package/dist/scheduler-vercel.js.map +1 -1
- package/dist/{server-DbD7IJVK.d.ts → server-CGM8WzRG.d.ts} +87 -26
- package/dist/server-CGM8WzRG.d.ts.map +1 -0
- package/dist/{server-BD5ckxZb.js → server-Rj64fNvX.js} +854 -202
- package/dist/server-Rj64fNvX.js.map +1 -0
- package/dist/server.d.ts +4 -4
- package/dist/server.js +2 -2
- package/dist/{errors-DCk6ch5n.js → session-clear-DGI4jTQ5.js} +17 -2
- package/dist/session-clear-DGI4jTQ5.js.map +1 -0
- package/dist/session-clear-Dshpuhff.d.ts +12 -0
- package/dist/session-clear-Dshpuhff.d.ts.map +1 -0
- package/dist/{store-DyZM6fS5.d.ts → store-Gpkq7WBo.d.ts} +44 -3
- package/dist/store-Gpkq7WBo.d.ts.map +1 -0
- package/dist/store-N8PXxDAS.js.map +1 -1
- package/dist/store-memory.d.ts +1 -1
- package/dist/store-memory.d.ts.map +1 -1
- package/dist/store-memory.js +182 -69
- package/dist/store-memory.js.map +1 -1
- package/dist/{store-polling-CmxUbV93.js → store-polling-4_y8lETm.js} +21 -4
- package/dist/store-polling-4_y8lETm.js.map +1 -0
- package/dist/store-postgres.d.ts +1 -1
- package/dist/store-postgres.d.ts.map +1 -1
- package/dist/store-postgres.js +399 -108
- package/dist/store-postgres.js.map +1 -1
- package/dist/{store-presence-polling-C7-XZyW9.js → store-presence-polling-Q1C-k2dd.js} +3 -3
- package/dist/{store-presence-polling-C7-XZyW9.js.map → store-presence-polling-Q1C-k2dd.js.map} +1 -1
- package/dist/store-redis-http.d.ts +1 -1
- package/dist/store-redis-http.d.ts.map +1 -1
- package/dist/store-redis-http.js +293 -11
- package/dist/store-redis-http.js.map +1 -1
- package/dist/store-redis-notify-tqWNnMRu.js +2374 -0
- package/dist/store-redis-notify-tqWNnMRu.js.map +1 -0
- package/dist/store-redis.d.ts +1 -1
- package/dist/store-redis.d.ts.map +1 -1
- package/dist/store-redis.js +11 -11
- package/dist/store-redis.js.map +1 -1
- package/dist/store-sqlite.d.ts +1 -1
- package/dist/store-sqlite.d.ts.map +1 -1
- package/dist/store-sqlite.js +166 -32
- package/dist/store-sqlite.js.map +1 -1
- package/dist/stream-buffer-Cx7AAPGq.js +23 -0
- package/dist/stream-buffer-Cx7AAPGq.js.map +1 -0
- package/dist/{telemetry-B5jzpy6y.d.ts → telemetry-cZH-cibj.d.ts} +2 -2
- package/dist/telemetry-cZH-cibj.d.ts.map +1 -0
- package/dist/{wire-BO5wWCb1.js → wire-DjfHOvdW.js} +132 -10
- package/dist/wire-DjfHOvdW.js.map +1 -0
- package/docs/01-quickstart.mdx +2 -2
- package/docs/actors/01-introduction.mdx +4 -0
- package/docs/actors/04-routes.mdx +14 -1
- package/docs/concepts/01-contracts.mdx +95 -0
- package/docs/concepts/04-state.mdx +3 -3
- package/docs/guides/01-timers.mdx +22 -14
- package/docs/guides/02-cancellation.mdx +1 -1
- package/docs/guides/03-react.mdx +268 -328
- package/docs/guides/04-local-first.mdx +32 -73
- package/docs/guides/05-production.mdx +40 -27
- package/docs/guides/06-ai-agents.mdx +680 -580
- package/docs/guides/09-presence.mdx +3 -7
- package/docs/guides/10-transports.mdx +5 -5
- package/docs/index.mdx +10 -18
- package/docs/reference/01-api.mdx +662 -195
- package/docs/reference/02-errors.mdx +2 -1
- package/examples/playground/DEPLOY.md +52 -6
- package/examples/playground/app/a0/[appId]/loading.tsx +16 -0
- package/examples/playground/app/a0/[appId]/page.tsx +48 -0
- package/examples/playground/app/a0/a0.css +818 -0
- package/examples/playground/app/a0/app-list.tsx +211 -0
- package/examples/playground/app/a0/app-view.tsx +55 -0
- package/examples/playground/app/a0/apps/[appId]/preview/route.ts +27 -0
- package/examples/playground/app/a0/apps/[appId]/route.ts +23 -0
- package/examples/playground/app/a0/apps.test.ts +116 -0
- package/examples/playground/app/a0/apps.ts +87 -0
- package/examples/playground/app/a0/chat.tsx +364 -0
- package/examples/playground/app/a0/columns.tsx +109 -0
- package/examples/playground/app/a0/context-menu.tsx +116 -0
- package/examples/playground/app/a0/cookies.ts +46 -0
- package/examples/playground/app/a0/crash-on-request.ts +72 -0
- package/examples/playground/app/a0/crashes/route.ts +3 -0
- package/examples/playground/app/a0/crashes.test.ts +33 -0
- package/examples/playground/app/a0/crashes.ts +28 -0
- package/examples/playground/app/a0/devtools.tsx +164 -0
- package/examples/playground/app/a0/events/route.ts +6 -0
- package/examples/playground/app/a0/flags.ts +30 -0
- package/examples/playground/app/a0/layout.tsx +38 -0
- package/examples/playground/app/a0/list/route.ts +10 -0
- package/examples/playground/app/a0/model.test.ts +56 -0
- package/examples/playground/app/a0/model.ts +58 -0
- package/examples/playground/app/a0/page.tsx +14 -0
- package/examples/playground/app/a0/preview-pane.tsx +145 -0
- package/examples/playground/app/a0/preview.test.ts +53 -0
- package/examples/playground/app/a0/preview.ts +26 -0
- package/examples/playground/app/a0/sandbox-log.ts +69 -0
- package/examples/playground/app/a0/sandbox.ts +191 -0
- package/examples/playground/app/a0/sandboxes/scheduler/route.ts +6 -0
- package/examples/playground/app/a0/scheduler/route.ts +7 -0
- package/examples/playground/app/a0/server.ts +158 -0
- package/examples/playground/app/a0/session.ts +47 -0
- package/examples/playground/app/a0/shell.tsx +150 -0
- package/examples/playground/app/a0/short/events/route.ts +9 -0
- package/examples/playground/app/a0/splitter.tsx +120 -0
- package/examples/playground/app/agent/[agentId]/agent-client.tsx +144 -174
- package/examples/playground/app/agent/[agentId]/command-receipt.test.tsx +65 -0
- package/examples/playground/app/agent/[agentId]/command-receipt.tsx +27 -0
- package/examples/playground/app/agent/[agentId]/compaction-event.tsx +25 -24
- package/examples/playground/app/agent/[agentId]/compaction-panel.test.tsx +119 -0
- package/examples/playground/app/agent/[agentId]/compaction-panel.tsx +208 -165
- package/examples/playground/app/agent/[agentId]/page.tsx +2 -16
- package/examples/playground/app/agent/events/route.ts +5 -2
- package/examples/playground/app/agent/ingress.test.ts +98 -0
- package/examples/playground/app/agent/model-input.test.tsx +58 -0
- package/examples/playground/app/agent/model-input.tsx +115 -0
- package/examples/playground/app/agent/model.test.ts +73 -0
- package/examples/playground/app/agent/model.ts +58 -26
- package/examples/playground/app/agent/new-agent-session.tsx +37 -19
- package/examples/playground/app/agent/page.tsx +4 -4
- package/examples/playground/app/agent/server.ts +15 -14
- package/examples/playground/app/agent/session.ts +5 -6
- package/examples/playground/app/agent/telemetry.test.ts +168 -0
- package/examples/playground/app/agent/telemetry.ts +30 -0
- package/examples/playground/app/agent/trace.test.ts +23 -0
- package/examples/playground/app/agent/trace.ts +18 -0
- package/examples/playground/app/canvas/[canvasId]/canvas-client.tsx +11 -11
- package/examples/playground/app/canvas/[canvasId]/page.tsx +1 -2
- package/examples/playground/app/canvas/[canvasId]/session.ts +2 -2
- package/examples/playground/app/canvas/model.ts +53 -53
- package/examples/playground/app/chat/[chatId]/agent-state.test.ts +176 -0
- package/examples/playground/app/chat/[chatId]/agent-stream-drawer.tsx +16 -1
- package/examples/playground/app/chat/[chatId]/chat-client.tsx +55 -77
- package/examples/playground/app/chat/[chatId]/chat-view.ts +37 -3
- package/examples/playground/app/chat/[chatId]/page.tsx +7 -15
- package/examples/playground/app/chat/[chatId]/session.ts +2 -19
- package/examples/playground/app/chat/agent/route.ts +9 -0
- package/examples/playground/app/chat/model.test.ts +46 -119
- package/examples/playground/app/chat/model.ts +82 -128
- package/examples/playground/app/chat/scheduler/route.ts +2 -2
- package/examples/playground/app/chat/server.test.ts +108 -0
- package/examples/playground/app/chat/server.ts +69 -73
- package/examples/playground/app/components/activity-feed.tsx +30 -1
- package/examples/playground/app/components/activity-history.test.tsx +35 -0
- package/examples/playground/app/components/demo-icons.tsx +89 -0
- package/examples/playground/app/components/demos.tsx +33 -0
- package/examples/playground/app/components/fast-link.test.tsx +58 -0
- package/examples/playground/app/components/fast-link.tsx +55 -0
- package/examples/playground/app/components/site-header.tsx +96 -0
- package/examples/playground/app/counter/counter-client.tsx +3 -3
- package/examples/playground/app/counter/model.test.ts +3 -3
- package/examples/playground/app/counter/model.ts +11 -21
- package/examples/playground/app/counter/page.tsx +2 -15
- package/examples/playground/app/counter/session.ts +2 -2
- package/examples/playground/app/documents/[documentId]/code-editor.tsx +12 -1
- package/examples/playground/app/documents/[documentId]/document-client.tsx +10 -11
- package/examples/playground/app/documents/[documentId]/page.tsx +1 -2
- package/examples/playground/app/documents/[documentId]/session.ts +2 -2
- package/examples/playground/app/documents/model.ts +27 -27
- package/examples/playground/app/globals.css +420 -207
- package/examples/playground/app/initial-state.test.tsx +54 -0
- package/examples/playground/app/layout.tsx +7 -23
- package/examples/playground/app/orders/[orderId]/page.tsx +1 -2
- package/examples/playground/app/orders/[orderId]/session.ts +2 -2
- package/examples/playground/app/orders/model.ts +49 -49
- package/examples/playground/app/page.tsx +13 -102
- package/examples/playground/app/recovery/[recoveryId]/page.tsx +1 -2
- package/examples/playground/app/recovery/[recoveryId]/session.ts +2 -2
- package/examples/playground/app/recovery/model.ts +30 -30
- package/examples/playground/instrumentation.ts +5 -0
- package/examples/playground/package.json +10 -2
- package/examples/playground/scripts/a0-template.ts +77 -0
- package/examples/playground/vercel.json +18 -0
- package/package.json +3 -1
- package/src/actor-client.ts +16 -4
- package/src/actor-react.ts +25 -11
- package/src/actor-shared.ts +21 -6
- package/src/actor.ts +76 -31
- package/src/ai-agent-actions.ts +219 -0
- package/src/ai-agent.ts +2343 -0
- package/src/ai-app-state.ts +91 -0
- package/src/ai-client-state.ts +39 -2
- package/src/ai-client.ts +62 -0
- package/src/ai-context-schema.ts +25 -5
- package/src/ai-context.ts +90 -40
- package/src/ai-control-server.ts +94 -55
- package/src/ai-control.ts +2 -0
- package/src/ai-coordinator.ts +23 -22
- package/src/ai-message-base.ts +209 -0
- package/src/ai-message-projection.ts +382 -58
- package/src/ai-progress-batches.ts +26 -0
- package/src/ai-projector.ts +95 -14
- package/src/ai-react.ts +146 -0
- package/src/ai-server.browser.ts +2 -2
- package/src/ai-server.ts +20 -1951
- package/src/ai-stored-state.ts +48 -163
- package/src/ai.ts +343 -276
- package/src/bootstrap-stream.ts +72 -0
- package/src/bounded-cache.ts +55 -0
- package/src/client-render-session.ts +49 -0
- package/src/client.ts +688 -151
- package/src/contract.ts +92 -42
- package/src/deterministic-id.ts +37 -0
- package/src/equal-json.ts +32 -0
- package/src/errors.ts +13 -1
- package/src/index.ts +5 -1
- package/src/initial-snapshot.ts +39 -0
- package/src/internal.ts +45 -0
- package/src/postgres-notification-scope.ts +14 -3
- package/src/react.ts +171 -206
- package/src/redis-http-reply.ts +256 -0
- package/src/redis-http-stream.ts +73 -0
- package/src/redis-http-subscriptions.ts +13 -5
- package/src/reducer-runtime.ts +52 -0
- package/src/reducer.ts +92 -10
- package/src/scheduler-qstash.ts +49 -20
- package/src/scheduler-task.ts +34 -2
- package/src/scheduler-vercel.ts +32 -84
- package/src/server-fetch.ts +139 -35
- package/src/server.ts +957 -237
- package/src/session-clear.ts +23 -0
- package/src/session-socket.ts +154 -49
- package/src/sse.ts +71 -4
- package/src/store-memory.ts +220 -78
- package/src/store-polling.ts +18 -4
- package/src/store-postgres.ts +461 -114
- package/src/store-redis-core.ts +1260 -434
- package/src/store-redis-http.ts +18 -2
- package/src/store-redis-notify.ts +260 -85
- package/src/store-redis.ts +19 -9
- package/src/store-sqlite.ts +259 -45
- package/src/store.ts +46 -1
- package/src/stream-buffer.ts +43 -0
- package/src/telemetry.ts +13 -1
- package/src/wire.ts +241 -13
- package/dist/actor-ohPC-81x.d.ts.map +0 -1
- package/dist/actor-shared-B5tJfzt-.js.map +0 -1
- package/dist/ai-CrLYwNEx.js.map +0 -1
- package/dist/ai-HJ9fHfYI.d.ts.map +0 -1
- package/dist/client-BYzHjkwU.js.map +0 -1
- package/dist/client-CzyacQpJ.d.ts.map +0 -1
- package/dist/contract-CKRg_E4q.js.map +0 -1
- package/dist/errors-DCk6ch5n.js.map +0 -1
- package/dist/errors-DvhSXnxk.d.ts.map +0 -1
- package/dist/internal-Dq2qYxou.js.map +0 -1
- package/dist/react.d.ts.map +0 -1
- package/dist/reducer-DEMjEY_O.js +0 -29
- package/dist/reducer-DEMjEY_O.js.map +0 -1
- package/dist/reducer-otzHjuJj.d.ts.map +0 -1
- package/dist/scheduler-task-BpzhPnRS.js.map +0 -1
- package/dist/server-BD5ckxZb.js.map +0 -1
- package/dist/server-DbD7IJVK.d.ts.map +0 -1
- package/dist/store-DyZM6fS5.d.ts.map +0 -1
- package/dist/store-polling-CmxUbV93.js.map +0 -1
- package/dist/store-redis-notify-BVLUfI3j.js +0 -1583
- package/dist/store-redis-notify-BVLUfI3j.js.map +0 -1
- package/dist/telemetry-B5jzpy6y.d.ts.map +0 -1
- package/dist/validate-XKT4FSNn.js +0 -30
- package/dist/validate-XKT4FSNn.js.map +0 -1
- package/dist/wire-BO5wWCb1.js.map +0 -1
- package/examples/playground/app/agent/[agentId]/compaction/route.ts +0 -14
- package/examples/playground/app/agent/compaction-settings.test.ts +0 -144
- package/examples/playground/app/agent/compaction-settings.ts +0 -49
- package/examples/playground/app/agent/compaction-timeline.test.ts +0 -377
- package/examples/playground/app/agent/compaction-timeline.ts +0 -208
package/src/ai.ts
CHANGED
|
@@ -1,14 +1,17 @@
|
|
|
1
|
+
import type { MessageBase } from './ai-message-base.ts'
|
|
2
|
+
import { reducer as makeReducer } from './reducer.ts'
|
|
1
3
|
/**
|
|
2
|
-
* experimental-a2/ai —
|
|
4
|
+
* experimental-a2/ai — the isomorphic AI protocol.
|
|
3
5
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
6
|
+
* Event schemas, the `AIState` reducer, `agentContract()` for extending
|
|
7
|
+
* both, and pure builders for the events applications append. Model calls
|
|
8
|
+
* and tool implementations stay in experimental-a2/ai/server.
|
|
7
9
|
*/
|
|
8
10
|
|
|
9
11
|
import type {
|
|
10
12
|
FinishReason,
|
|
11
13
|
JSONValue,
|
|
14
|
+
LanguageModelCallOptions,
|
|
12
15
|
LanguageModelUsage,
|
|
13
16
|
UIMessage,
|
|
14
17
|
UIMessageChunk,
|
|
@@ -38,31 +41,55 @@ export type {
|
|
|
38
41
|
InboxItem,
|
|
39
42
|
} from './ai-control.ts'
|
|
40
43
|
import { contract as createContract } from './contract.ts'
|
|
41
|
-
import type { Contract, EventDefs } from './contract.ts'
|
|
44
|
+
import type { Contract, ContractEvent, EventDefs } from './contract.ts'
|
|
42
45
|
import {
|
|
43
46
|
interruptUIMessage,
|
|
44
47
|
pauseUIMessage,
|
|
45
48
|
projectUIMessage,
|
|
49
|
+
projectUIMessageStream,
|
|
50
|
+
hasToolCall,
|
|
51
|
+
type ProjectionChunk,
|
|
46
52
|
type UIMessageCursor,
|
|
47
53
|
} from './ai-projector.ts'
|
|
48
54
|
import { ambientToolScopeStorage } from './internal.ts'
|
|
49
55
|
import {
|
|
50
56
|
upsertMessage,
|
|
57
|
+
retainMessageBase,
|
|
58
|
+
findMessage,
|
|
51
59
|
upsertResponse,
|
|
52
60
|
answerApproval,
|
|
53
61
|
approvalRequestedChunk,
|
|
54
62
|
toolResultChunk,
|
|
63
|
+
toolCalledChunk,
|
|
55
64
|
advanceProjection,
|
|
65
|
+
projectedTools,
|
|
66
|
+
restoreGenerationBase,
|
|
67
|
+
stopGenerationTools,
|
|
68
|
+
interruptGenerationProjection,
|
|
56
69
|
projectedMessage,
|
|
70
|
+
projectedMessageStream,
|
|
57
71
|
reduceToolActivity,
|
|
72
|
+
addCompactionPart,
|
|
73
|
+
isCompactionPart,
|
|
74
|
+
settleAbandonedCompaction,
|
|
75
|
+
settleCompactionPart,
|
|
58
76
|
} from './ai-message-projection.ts'
|
|
59
|
-
import {
|
|
60
|
-
import {
|
|
61
|
-
|
|
77
|
+
import type { Reducer } from './reducer.ts'
|
|
78
|
+
import { withReducerRuntime } from './reducer-runtime.ts'
|
|
79
|
+
import { withAIStorage } from './ai-stored-state.ts'
|
|
80
|
+
import {
|
|
81
|
+
appSlice,
|
|
82
|
+
isAIStateKey,
|
|
83
|
+
registerAgentReducer,
|
|
84
|
+
withAppSlice,
|
|
85
|
+
type AppReducer,
|
|
86
|
+
} from './ai-app-state.ts'
|
|
62
87
|
import {
|
|
63
88
|
flattenProgressBatches,
|
|
64
89
|
progressBatches,
|
|
90
|
+
progressOwnedBy,
|
|
65
91
|
type ProgressBatches,
|
|
92
|
+
type StoredProgressPayload,
|
|
66
93
|
} from './ai-progress-batches.ts'
|
|
67
94
|
import type { HandlerContext } from './server.ts'
|
|
68
95
|
import type { StandardSchemaV1 } from './standard-schema.ts'
|
|
@@ -73,6 +100,43 @@ export type AgentStatus =
|
|
|
73
100
|
|
|
74
101
|
export type GenerationReason = 'message' | 'tool' | 'input' | 'retry' | 'resume'
|
|
75
102
|
|
|
103
|
+
export type ReasoningLevel = NonNullable<LanguageModelCallOptions['reasoning']>
|
|
104
|
+
|
|
105
|
+
export const reasoningLevels: readonly ReasoningLevel[] = [
|
|
106
|
+
'provider-default',
|
|
107
|
+
'none',
|
|
108
|
+
'minimal',
|
|
109
|
+
'low',
|
|
110
|
+
'medium',
|
|
111
|
+
'high',
|
|
112
|
+
'xhigh',
|
|
113
|
+
]
|
|
114
|
+
|
|
115
|
+
export type CompactionPartData = {
|
|
116
|
+
status: 'running' | 'completed' | 'failed' | 'interrupted'
|
|
117
|
+
inputTokens?: number
|
|
118
|
+
thresholdTokens?: number
|
|
119
|
+
summary?: string
|
|
120
|
+
usage?: LanguageModelUsage
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/** The transcript part A2 adds to a response where its context was compacted. */
|
|
124
|
+
export type CompactionPart = {
|
|
125
|
+
type: 'data-a2-compaction'
|
|
126
|
+
id: string
|
|
127
|
+
data: CompactionPartData
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export { isCompactionPart }
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* A transcript message: `M` plus the compaction part A2 adds, so
|
|
134
|
+
* `isCompactionPart` narrows even when `M` declares its own data parts.
|
|
135
|
+
*/
|
|
136
|
+
export type TranscriptMessage<M extends UIMessage> = Omit<M, 'parts'> & {
|
|
137
|
+
parts: Array<M['parts'][number] | CompactionPart>
|
|
138
|
+
}
|
|
139
|
+
|
|
76
140
|
export type SessionCreatedPayload = { metadata?: JSONValue }
|
|
77
141
|
export type SessionClosedPayload = { reason?: string }
|
|
78
142
|
export type MessageCreatedPayload<M extends UIMessage> = {
|
|
@@ -111,15 +175,13 @@ export type GenerationStartedPayload = {
|
|
|
111
175
|
generationId: string
|
|
112
176
|
responseMessageId: string
|
|
113
177
|
attempt: number
|
|
114
|
-
model
|
|
178
|
+
/** Absent when the attempt failed before its model resolved. */
|
|
179
|
+
model?: string
|
|
115
180
|
promptThroughIndex?: number
|
|
116
181
|
}
|
|
117
182
|
export type GenerationProgressPayload = {
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
generationId: string
|
|
121
|
-
responseMessageId: string
|
|
122
|
-
sequence: number
|
|
183
|
+
/** Log index of the generation's `ai.generation.started`. */
|
|
184
|
+
startIndex: number
|
|
123
185
|
chunks: UIMessageChunk[]
|
|
124
186
|
}
|
|
125
187
|
export type GenerationCompletedPayload = {
|
|
@@ -271,17 +333,20 @@ export type AIEventDefs<M extends UIMessage = UIMessage> = {
|
|
|
271
333
|
'ai.model.metadata.resolved': StandardSchemaV1<ModelMetadataResolvedPayload>
|
|
272
334
|
}
|
|
273
335
|
|
|
274
|
-
export type ActiveGeneration = GenerationStartedPayload
|
|
336
|
+
export type ActiveGeneration = GenerationStartedPayload & {
|
|
337
|
+
/** Log index of this generation's `ai.generation.started`. */
|
|
338
|
+
startIndex: number
|
|
339
|
+
}
|
|
275
340
|
|
|
276
|
-
export type ActiveGenerationProjection
|
|
341
|
+
export type ActiveGenerationProjection = {
|
|
277
342
|
generationId: string
|
|
278
343
|
responseMessageId: string
|
|
279
344
|
/** Log index of the completed model step while its response remains interruptible. */
|
|
280
345
|
completionIndex?: number
|
|
281
|
-
|
|
346
|
+
base?: MessageBase
|
|
282
347
|
promptThroughIndex?: number
|
|
283
348
|
cursor?: { throughIndex: number; stream: UIMessageCursor } | null
|
|
284
|
-
baseCompaction?: CompactionState
|
|
349
|
+
baseCompaction?: CompactionState | null
|
|
285
350
|
batches: ProgressBatches
|
|
286
351
|
baseTools: ToolActivity[]
|
|
287
352
|
toolEvents: Array<
|
|
@@ -311,28 +376,20 @@ export type ToolActivity = {
|
|
|
311
376
|
generationId: string
|
|
312
377
|
toolCallId: string
|
|
313
378
|
toolName?: string
|
|
314
|
-
input?: unknown
|
|
315
|
-
rawInput?: unknown
|
|
316
|
-
output?: unknown
|
|
317
379
|
error?: string
|
|
318
380
|
preliminary?: boolean
|
|
319
381
|
phase?: 'input' | 'execution'
|
|
320
382
|
dynamic?: boolean
|
|
321
383
|
providerExecuted?: boolean
|
|
322
|
-
callProviderMetadata?: unknown
|
|
323
|
-
resultProviderMetadata?: unknown
|
|
324
|
-
toolMetadata?: unknown
|
|
325
384
|
title?: string
|
|
326
385
|
status: 'running' | 'completed' | 'failed' | 'denied'
|
|
327
386
|
}
|
|
328
387
|
|
|
329
|
-
export type CompactionState
|
|
388
|
+
export type CompactionState = {
|
|
330
389
|
status: 'running' | 'completed'
|
|
331
390
|
generationId: string
|
|
332
391
|
throughMessageId: string
|
|
333
392
|
throughIndex?: number
|
|
334
|
-
messages?: M[]
|
|
335
|
-
retainedMessageIds?: string[]
|
|
336
393
|
summary?: string
|
|
337
394
|
usage?: LanguageModelUsage
|
|
338
395
|
}
|
|
@@ -350,14 +407,14 @@ export type AIState<M extends UIMessage = UIMessage> = {
|
|
|
350
407
|
receipt: ControlReceipt | null
|
|
351
408
|
pausedGenerationIds?: Record<string, true>
|
|
352
409
|
session: { created: boolean; metadata?: JSONValue }
|
|
353
|
-
messages: M[]
|
|
410
|
+
messages: TranscriptMessage<M>[]
|
|
354
411
|
status: AgentStatus
|
|
355
412
|
activeGeneration: ActiveGeneration | null
|
|
356
413
|
activeRequestId: string | null
|
|
357
414
|
activeRequestReason: GenerationReason | null
|
|
358
415
|
activeRequestSourceGenerationId: string | null
|
|
359
416
|
activeResponseMessageId: string | null
|
|
360
|
-
activeProjection: ActiveGenerationProjection
|
|
417
|
+
activeProjection: ActiveGenerationProjection | null
|
|
361
418
|
responseOwners: Record<
|
|
362
419
|
string,
|
|
363
420
|
Pick<GenerationStartedPayload, 'generationId' | 'requestId' | 'attempt'>
|
|
@@ -370,19 +427,29 @@ export type AIState<M extends UIMessage = UIMessage> = {
|
|
|
370
427
|
pendingApprovals: PendingApproval[]
|
|
371
428
|
pendingInputs: PendingInput[]
|
|
372
429
|
tools: ToolActivity[]
|
|
373
|
-
compaction: CompactionState
|
|
430
|
+
compaction: CompactionState | null
|
|
374
431
|
modelMetadata: Record<string, ModelMetadataState>
|
|
375
432
|
usage: GenerationUsage[]
|
|
376
433
|
error: string | null
|
|
377
434
|
}
|
|
378
435
|
|
|
379
|
-
|
|
436
|
+
/** The `UIMessage` type a contract's `ai.message.created` schema carries. */
|
|
437
|
+
export type AIMessageOf<D> = D extends {
|
|
438
|
+
readonly 'ai.message.created': infer S extends StandardSchemaV1
|
|
439
|
+
}
|
|
440
|
+
? StandardSchemaV1.InferOutput<S> extends MessageCreatedPayload<
|
|
441
|
+
infer M extends UIMessage
|
|
442
|
+
>
|
|
443
|
+
? M
|
|
444
|
+
: UIMessage
|
|
445
|
+
: UIMessage
|
|
446
|
+
|
|
447
|
+
/** What `agentContract()` returns: the AI protocol, with `X` as application state. */
|
|
448
|
+
export type AgentContract<
|
|
380
449
|
M extends UIMessage = UIMessage,
|
|
381
450
|
D extends EventDefs = AIEventDefs<M>,
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
readonly reducer: AIStoredReducer<D, AIState<M>>
|
|
385
|
-
}
|
|
451
|
+
X extends object = Record<never, never>,
|
|
452
|
+
> = Contract<D, Record<never, never>, AgentReducer<M, D, X>>
|
|
386
453
|
|
|
387
454
|
export type AgentToolEventDefs = EventDefs &
|
|
388
455
|
Pick<AIEventDefs, 'ai.tool.execution.requested'>
|
|
@@ -393,24 +460,50 @@ export type AgentToolContext<D extends AgentToolEventDefs> = HandlerContext<
|
|
|
393
460
|
'ai.tool.execution.requested'
|
|
394
461
|
>
|
|
395
462
|
|
|
396
|
-
|
|
463
|
+
/** Application state folded after the AI protocol, on the server and in clients. */
|
|
464
|
+
/** Rejects application state keys that would shadow `AIState`'s. */
|
|
465
|
+
type OwnKeys<X> = { [K in Extract<keyof X, keyof AIState>]: never }
|
|
466
|
+
/** Rejects a stateless application reducer, whose name would not be built in. */
|
|
467
|
+
type NonEmpty<X> = keyof X extends never ? never : unknown
|
|
468
|
+
|
|
469
|
+
/**
|
|
470
|
+
* The reducer `agentContract()` builds. Without application state it carries
|
|
471
|
+
* the built-in name, which is what lets the contract-free client fold it.
|
|
472
|
+
* `~appState` is type-only, absent at runtime: it carries the application
|
|
473
|
+
* state's type to `agent()`, and marks the reducer as `agentContract()`'s.
|
|
474
|
+
*/
|
|
475
|
+
export type AgentReducer<
|
|
476
|
+
M extends UIMessage,
|
|
477
|
+
D extends EventDefs,
|
|
478
|
+
X extends object,
|
|
479
|
+
> = (keyof X extends never
|
|
480
|
+
? Reducer<D, AIState<M>> & { readonly name: typeof AI_STATE_REDUCER }
|
|
481
|
+
: Reducer<D, AIState<M> & X>) & { readonly '~appState': X }
|
|
482
|
+
|
|
483
|
+
export type AgentContractOptions<
|
|
484
|
+
M extends UIMessage,
|
|
485
|
+
E extends EventDefs,
|
|
486
|
+
X extends object,
|
|
487
|
+
> = {
|
|
397
488
|
name: string
|
|
398
489
|
events?: E
|
|
399
490
|
/** Optional stricter schema for custom UIMessage metadata/data/tool types. */
|
|
400
491
|
messageSchema?: StandardSchemaV1<M>
|
|
401
|
-
/**
|
|
402
|
-
|
|
492
|
+
/**
|
|
493
|
+
* Application state, folded from events beside `AIState` and merged into
|
|
494
|
+
* it. `fold` sees only this state, never the transcript.
|
|
495
|
+
*/
|
|
496
|
+
reducer?: {
|
|
497
|
+
/** Snapshot identity of the application state. Rename it when `fold` changes. */
|
|
498
|
+
name: string
|
|
499
|
+
initialState: X & OwnKeys<X> & NonEmpty<X>
|
|
500
|
+
fold: (
|
|
501
|
+
state: NoInfer<X>,
|
|
502
|
+
event: ContractEvent<AIEventDefs<M> & NoInfer<E>>,
|
|
503
|
+
) => NoInfer<X>
|
|
504
|
+
}
|
|
403
505
|
}
|
|
404
506
|
|
|
405
|
-
type AIMessageFrom<D extends EventDefs> =
|
|
406
|
-
D extends Record<'ai.message.created', infer S extends StandardSchemaV1>
|
|
407
|
-
? StandardSchemaV1.InferOutput<S> extends MessageCreatedPayload<
|
|
408
|
-
infer M extends UIMessage
|
|
409
|
-
>
|
|
410
|
-
? M
|
|
411
|
-
: UIMessage
|
|
412
|
-
: UIMessage
|
|
413
|
-
|
|
414
507
|
type SchemaResult<T> = StandardSchemaV1.Result<T>
|
|
415
508
|
|
|
416
509
|
const issue = <T>(message: string): SchemaResult<T> => ({
|
|
@@ -536,13 +629,8 @@ const progressPayloadSchema: StandardSchemaV1<GenerationProgressPayload> =
|
|
|
536
629
|
if (
|
|
537
630
|
!isRecord(value) ||
|
|
538
631
|
!isJSONCompatible(value) ||
|
|
539
|
-
|
|
540
|
-
!
|
|
541
|
-
!stringField(value, 'generationId') ||
|
|
542
|
-
!stringField(value, 'responseMessageId') ||
|
|
543
|
-
typeof value['sequence'] !== 'number' ||
|
|
544
|
-
!Number.isInteger(value['sequence']) ||
|
|
545
|
-
value['sequence'] < 0 ||
|
|
632
|
+
typeof value['startIndex'] !== 'number' ||
|
|
633
|
+
!optionalIndexField(value, 'startIndex') ||
|
|
546
634
|
!Array.isArray(value['chunks']) ||
|
|
547
635
|
!value['chunks'].every(
|
|
548
636
|
(chunk) => isRecord(chunk) && typeof chunk['type'] === 'string',
|
|
@@ -818,7 +906,7 @@ export function createEvents<M extends UIMessage = UIMessage>(options?: {
|
|
|
818
906
|
typeof value['attempt'] === 'number' &&
|
|
819
907
|
Number.isInteger(value['attempt']) &&
|
|
820
908
|
value['attempt'] > 0 &&
|
|
821
|
-
stringField(value, 'model') &&
|
|
909
|
+
(value['model'] === undefined || stringField(value, 'model')) &&
|
|
822
910
|
optionalIndexField(value, 'promptThroughIndex'),
|
|
823
911
|
),
|
|
824
912
|
'ai.generation.progress': progressPayloadSchema,
|
|
@@ -975,13 +1063,18 @@ const initialState = <M extends UIMessage>(): AIState<M> => ({
|
|
|
975
1063
|
const projectMessageChunks = <M extends UIMessage>(
|
|
976
1064
|
messages: M[],
|
|
977
1065
|
messageId: string,
|
|
978
|
-
chunks:
|
|
979
|
-
): M[] =>
|
|
980
|
-
messages
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
1066
|
+
chunks: ProjectionChunk[],
|
|
1067
|
+
): M[] => {
|
|
1068
|
+
const base = findMessage(messages, messageId)
|
|
1069
|
+
return upsertMessage(
|
|
1070
|
+
messages,
|
|
1071
|
+
projectUIMessage<M>({
|
|
1072
|
+
messageId,
|
|
1073
|
+
...(base === undefined ? {} : { base }),
|
|
1074
|
+
chunks,
|
|
1075
|
+
}),
|
|
984
1076
|
)
|
|
1077
|
+
}
|
|
985
1078
|
|
|
986
1079
|
const waitingStatus = <M extends UIMessage>(state: AIState<M>): AgentStatus => {
|
|
987
1080
|
if (state.pendingApprovals.length > 0 || state.pendingInputs.length > 0) {
|
|
@@ -1014,9 +1107,15 @@ const isStaleGeneration = <M extends UIMessage>(
|
|
|
1014
1107
|
|
|
1015
1108
|
const projectedResponse = <M extends UIMessage>(options: {
|
|
1016
1109
|
state: AIState<M>
|
|
1017
|
-
projection: ActiveGenerationProjection
|
|
1018
|
-
}): M => {
|
|
1019
|
-
const message = projectedMessage(
|
|
1110
|
+
projection: ActiveGenerationProjection
|
|
1111
|
+
}): TranscriptMessage<M> => {
|
|
1112
|
+
const message = projectedMessage({
|
|
1113
|
+
projection: options.projection,
|
|
1114
|
+
current: findMessage(
|
|
1115
|
+
options.state.messages,
|
|
1116
|
+
options.projection.responseMessageId,
|
|
1117
|
+
),
|
|
1118
|
+
})
|
|
1020
1119
|
return options.state.pausedGenerationIds?.[options.projection.generationId]
|
|
1021
1120
|
? pauseUIMessage({
|
|
1022
1121
|
message,
|
|
@@ -1027,127 +1126,6 @@ const projectedResponse = <M extends UIMessage>(options: {
|
|
|
1027
1126
|
: message
|
|
1028
1127
|
}
|
|
1029
1128
|
|
|
1030
|
-
const projectedTools = <M extends UIMessage>(
|
|
1031
|
-
projection: ActiveGenerationProjection<M>,
|
|
1032
|
-
): ToolActivity[] =>
|
|
1033
|
-
projection.toolEvents.reduce(
|
|
1034
|
-
(tools, event) => reduceToolActivity(tools, event),
|
|
1035
|
-
projection.baseTools,
|
|
1036
|
-
)
|
|
1037
|
-
|
|
1038
|
-
const restoreGenerationBase = <M extends UIMessage>(
|
|
1039
|
-
state: AIState<M>,
|
|
1040
|
-
generationId: string,
|
|
1041
|
-
responseMessageId: string,
|
|
1042
|
-
reason: 'interrupted' | 'superseded' | 'failed',
|
|
1043
|
-
): AIState<M> => {
|
|
1044
|
-
const projection =
|
|
1045
|
-
state.activeProjection?.generationId === generationId
|
|
1046
|
-
? state.activeProjection
|
|
1047
|
-
: null
|
|
1048
|
-
return {
|
|
1049
|
-
...state,
|
|
1050
|
-
messages:
|
|
1051
|
-
projection?.baseMessage === undefined
|
|
1052
|
-
? state.messages.filter((message) => message.id !== responseMessageId)
|
|
1053
|
-
: upsertMessage(state.messages, projection.baseMessage),
|
|
1054
|
-
tools:
|
|
1055
|
-
projection === null
|
|
1056
|
-
? state.tools.filter(
|
|
1057
|
-
(activity) => activity.generationId !== generationId,
|
|
1058
|
-
)
|
|
1059
|
-
: projection.baseTools,
|
|
1060
|
-
pendingApprovals: state.pendingApprovals.filter(
|
|
1061
|
-
(approval) => approval.generationId !== generationId,
|
|
1062
|
-
),
|
|
1063
|
-
pendingInputs: state.pendingInputs.filter(
|
|
1064
|
-
(input) => input.generationId !== generationId,
|
|
1065
|
-
),
|
|
1066
|
-
activeGeneration: null,
|
|
1067
|
-
activeProjection: null,
|
|
1068
|
-
terminalGenerations: {
|
|
1069
|
-
...state.terminalGenerations,
|
|
1070
|
-
[generationId]: reason,
|
|
1071
|
-
},
|
|
1072
|
-
compaction:
|
|
1073
|
-
projection === null
|
|
1074
|
-
? state.compaction?.generationId === generationId
|
|
1075
|
-
? null
|
|
1076
|
-
: state.compaction
|
|
1077
|
-
: (projection.baseCompaction ?? null),
|
|
1078
|
-
}
|
|
1079
|
-
}
|
|
1080
|
-
|
|
1081
|
-
const stopGenerationTools = (
|
|
1082
|
-
tools: ToolActivity[],
|
|
1083
|
-
generationId: string | undefined,
|
|
1084
|
-
responseMessageId: string,
|
|
1085
|
-
error: string,
|
|
1086
|
-
): ToolActivity[] =>
|
|
1087
|
-
tools.map((activity) =>
|
|
1088
|
-
activity.status === 'running' &&
|
|
1089
|
-
(generationId === undefined
|
|
1090
|
-
? activity.messageId === responseMessageId
|
|
1091
|
-
: activity.generationId === generationId)
|
|
1092
|
-
? { ...activity, status: 'failed', error, preliminary: false }
|
|
1093
|
-
: activity,
|
|
1094
|
-
)
|
|
1095
|
-
|
|
1096
|
-
const interruptGenerationProjection = <M extends UIMessage>(
|
|
1097
|
-
state: AIState<M>,
|
|
1098
|
-
generationId: string,
|
|
1099
|
-
responseMessageId: string,
|
|
1100
|
-
cutoff: number,
|
|
1101
|
-
): AIState<M> | null => {
|
|
1102
|
-
const projection =
|
|
1103
|
-
state.activeProjection?.generationId === generationId
|
|
1104
|
-
? state.activeProjection
|
|
1105
|
-
: null
|
|
1106
|
-
if (projection === null) return null
|
|
1107
|
-
const retained: ActiveGenerationProjection<M> = {
|
|
1108
|
-
...projection,
|
|
1109
|
-
batches: progressBatches(
|
|
1110
|
-
flattenProgressBatches(projection.batches).filter(
|
|
1111
|
-
(batch) => batch.index <= cutoff,
|
|
1112
|
-
),
|
|
1113
|
-
),
|
|
1114
|
-
toolEvents: projection.toolEvents.filter(
|
|
1115
|
-
(toolEvent) => toolEvent.index <= cutoff,
|
|
1116
|
-
),
|
|
1117
|
-
approvalEvents: projection.approvalEvents.filter(
|
|
1118
|
-
(approvalEvent) => approvalEvent.index <= cutoff,
|
|
1119
|
-
),
|
|
1120
|
-
}
|
|
1121
|
-
const shouldProject =
|
|
1122
|
-
retained.baseMessage !== undefined || retained.batches.length > 0
|
|
1123
|
-
const restored = restoreGenerationBase(
|
|
1124
|
-
state,
|
|
1125
|
-
generationId,
|
|
1126
|
-
responseMessageId,
|
|
1127
|
-
'interrupted',
|
|
1128
|
-
)
|
|
1129
|
-
return {
|
|
1130
|
-
...restored,
|
|
1131
|
-
messages: shouldProject
|
|
1132
|
-
? upsertMessage(
|
|
1133
|
-
state.messages,
|
|
1134
|
-
interruptUIMessage(projectedMessage(retained)),
|
|
1135
|
-
)
|
|
1136
|
-
: restored.messages,
|
|
1137
|
-
tools: stopGenerationTools(
|
|
1138
|
-
projectedTools(retained),
|
|
1139
|
-
generationId,
|
|
1140
|
-
responseMessageId,
|
|
1141
|
-
'Tool execution was interrupted.',
|
|
1142
|
-
),
|
|
1143
|
-
usage:
|
|
1144
|
-
projection.completionIndex === undefined ||
|
|
1145
|
-
projection.completionIndex <= cutoff
|
|
1146
|
-
? restored.usage
|
|
1147
|
-
: restored.usage.filter((entry) => entry.generationId !== generationId),
|
|
1148
|
-
}
|
|
1149
|
-
}
|
|
1150
|
-
|
|
1151
1129
|
const terminalGenerationReason = <M extends UIMessage>(
|
|
1152
1130
|
state: AIState<M>,
|
|
1153
1131
|
generationId: string,
|
|
@@ -1165,6 +1143,19 @@ const terminalGenerationReason = <M extends UIMessage>(
|
|
|
1165
1143
|
export function reduceAIState<M extends UIMessage>(
|
|
1166
1144
|
state: AIState<M>,
|
|
1167
1145
|
event: { type: string; payload: unknown; index: number; id?: string },
|
|
1146
|
+
): AIState<M> {
|
|
1147
|
+
return settleAbandonedCompaction(
|
|
1148
|
+
state,
|
|
1149
|
+
retainMessageBase({
|
|
1150
|
+
before: state,
|
|
1151
|
+
after: reduceAIEvent(state, event),
|
|
1152
|
+
}),
|
|
1153
|
+
)
|
|
1154
|
+
}
|
|
1155
|
+
|
|
1156
|
+
function reduceAIEvent<M extends UIMessage>(
|
|
1157
|
+
state: AIState<M>,
|
|
1158
|
+
event: { type: string; payload: unknown; index: number; id?: string },
|
|
1168
1159
|
): AIState<M> {
|
|
1169
1160
|
if (state.status === 'closed') return state
|
|
1170
1161
|
switch (event.type) {
|
|
@@ -1488,13 +1479,14 @@ export function reduceAIState<M extends UIMessage>(
|
|
|
1488
1479
|
'superseded',
|
|
1489
1480
|
)
|
|
1490
1481
|
: state
|
|
1491
|
-
const baseMessage =
|
|
1492
|
-
|
|
1482
|
+
const baseMessage = findMessage(
|
|
1483
|
+
baseState.messages,
|
|
1484
|
+
payload.responseMessageId,
|
|
1493
1485
|
)
|
|
1494
1486
|
return {
|
|
1495
1487
|
...baseState,
|
|
1496
1488
|
status: 'generating',
|
|
1497
|
-
activeGeneration: payload,
|
|
1489
|
+
activeGeneration: { ...payload, startIndex: event.index },
|
|
1498
1490
|
activeResponseMessageId: null,
|
|
1499
1491
|
responseOwners: {
|
|
1500
1492
|
...baseState.responseOwners,
|
|
@@ -1508,7 +1500,9 @@ export function reduceAIState<M extends UIMessage>(
|
|
|
1508
1500
|
generationId: payload.generationId,
|
|
1509
1501
|
responseMessageId: payload.responseMessageId,
|
|
1510
1502
|
promptThroughIndex: payload.promptThroughIndex ?? event.index - 1,
|
|
1511
|
-
...(baseMessage === undefined
|
|
1503
|
+
...(baseMessage === undefined
|
|
1504
|
+
? {}
|
|
1505
|
+
: { base: { partCount: baseMessage.parts.length } }),
|
|
1512
1506
|
baseCompaction: baseState.compaction,
|
|
1513
1507
|
batches: progressBatches(),
|
|
1514
1508
|
baseTools: baseState.tools,
|
|
@@ -1519,30 +1513,29 @@ export function reduceAIState<M extends UIMessage>(
|
|
|
1519
1513
|
}
|
|
1520
1514
|
}
|
|
1521
1515
|
case 'ai.generation.progress': {
|
|
1522
|
-
const payload = event.payload as
|
|
1523
|
-
|
|
1516
|
+
const payload = event.payload as StoredProgressPayload
|
|
1517
|
+
const generation = state.activeGeneration
|
|
1518
|
+
const activeProjection = state.activeProjection
|
|
1524
1519
|
if (
|
|
1520
|
+
generation === null ||
|
|
1521
|
+
!progressOwnedBy({
|
|
1522
|
+
payload,
|
|
1523
|
+
owner: generation,
|
|
1524
|
+
startIndex: generation.startIndex,
|
|
1525
|
+
}) ||
|
|
1526
|
+
state.pausedGenerationIds?.[generation.generationId] ||
|
|
1525
1527
|
terminalGenerationReason(
|
|
1526
1528
|
state,
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
) !== undefined
|
|
1530
|
-
|
|
1531
|
-
return state
|
|
1532
|
-
}
|
|
1533
|
-
const activeProjection = state.activeProjection
|
|
1534
|
-
if (
|
|
1535
|
-
state.activeGeneration?.generationId !== payload.generationId ||
|
|
1536
|
-
state.activeGeneration.requestId !== payload.requestId ||
|
|
1537
|
-
activeProjection?.generationId !== payload.generationId
|
|
1529
|
+
generation.generationId,
|
|
1530
|
+
generation.requestId,
|
|
1531
|
+
) !== undefined ||
|
|
1532
|
+
activeProjection?.generationId !== generation.generationId
|
|
1538
1533
|
) {
|
|
1539
1534
|
return state
|
|
1540
1535
|
}
|
|
1541
1536
|
const progress = advanceProjection({
|
|
1542
1537
|
projection: activeProjection,
|
|
1543
|
-
base: state.messages.
|
|
1544
|
-
(message) => message.id === activeProjection.responseMessageId,
|
|
1545
|
-
),
|
|
1538
|
+
base: findMessage(state.messages, activeProjection.responseMessageId),
|
|
1546
1539
|
chunks: payload.chunks,
|
|
1547
1540
|
index: event.index,
|
|
1548
1541
|
})
|
|
@@ -1551,7 +1544,7 @@ export function reduceAIState<M extends UIMessage>(
|
|
|
1551
1544
|
messages: upsertResponse({
|
|
1552
1545
|
messages: state.messages,
|
|
1553
1546
|
message: progress.message,
|
|
1554
|
-
inputId:
|
|
1547
|
+
inputId: generation.messageId,
|
|
1555
1548
|
}),
|
|
1556
1549
|
activeProjection: {
|
|
1557
1550
|
...activeProjection,
|
|
@@ -1638,14 +1631,20 @@ export function reduceAIState<M extends UIMessage>(
|
|
|
1638
1631
|
: null
|
|
1639
1632
|
const shouldProject =
|
|
1640
1633
|
projection !== null &&
|
|
1641
|
-
(projection.
|
|
1634
|
+
(projection.base !== undefined || projection.batches.length > 0)
|
|
1642
1635
|
return {
|
|
1643
1636
|
...state,
|
|
1644
1637
|
messages: shouldProject
|
|
1645
1638
|
? upsertMessage(
|
|
1646
1639
|
state.messages,
|
|
1647
1640
|
interruptUIMessage(
|
|
1648
|
-
projectedMessage(
|
|
1641
|
+
projectedMessage({
|
|
1642
|
+
projection,
|
|
1643
|
+
current: findMessage(
|
|
1644
|
+
state.messages,
|
|
1645
|
+
projection.responseMessageId,
|
|
1646
|
+
),
|
|
1647
|
+
}),
|
|
1649
1648
|
`Generation failed before tool completion: ${payload.error}`,
|
|
1650
1649
|
),
|
|
1651
1650
|
)
|
|
@@ -1949,6 +1948,9 @@ export function reduceAIState<M extends UIMessage>(
|
|
|
1949
1948
|
if (projection?.generationId !== payload.generationId) {
|
|
1950
1949
|
const next = {
|
|
1951
1950
|
...state,
|
|
1951
|
+
messages: projectMessageChunks(state.messages, payload.messageId, [
|
|
1952
|
+
toolCalledChunk(payload),
|
|
1953
|
+
]),
|
|
1952
1954
|
tools: reduceToolActivity(state.tools, {
|
|
1953
1955
|
type: 'ai.tool.called',
|
|
1954
1956
|
payload,
|
|
@@ -1956,16 +1958,51 @@ export function reduceAIState<M extends UIMessage>(
|
|
|
1956
1958
|
}
|
|
1957
1959
|
return { ...next, status: lifecycleStatus(state, next) }
|
|
1958
1960
|
}
|
|
1959
|
-
const activeProjection: ActiveGenerationProjection
|
|
1961
|
+
const activeProjection: ActiveGenerationProjection = {
|
|
1960
1962
|
...projection,
|
|
1963
|
+
cursor: null,
|
|
1961
1964
|
toolEvents: [
|
|
1962
1965
|
...projection.toolEvents,
|
|
1963
1966
|
{ index: event.index, type: 'ai.tool.called', payload },
|
|
1964
1967
|
],
|
|
1965
1968
|
}
|
|
1969
|
+
const base = findMessage(state.messages, payload.messageId)
|
|
1970
|
+
const paused = state.pausedGenerationIds?.[payload.generationId]
|
|
1971
|
+
const cursor = projection.cursor
|
|
1972
|
+
const incremental =
|
|
1973
|
+
base && cursor && !paused && event.index > cursor.throughIndex
|
|
1974
|
+
const projected = incremental
|
|
1975
|
+
? {
|
|
1976
|
+
...(hasToolCall({ message: base, call: payload })
|
|
1977
|
+
? { message: base, cursor: cursor.stream }
|
|
1978
|
+
: projectUIMessageStream({
|
|
1979
|
+
messageId: payload.messageId,
|
|
1980
|
+
base,
|
|
1981
|
+
cursor: cursor.stream,
|
|
1982
|
+
chunks: [toolCalledChunk(payload)],
|
|
1983
|
+
})),
|
|
1984
|
+
throughIndex: event.index,
|
|
1985
|
+
}
|
|
1986
|
+
: projectedMessageStream({
|
|
1987
|
+
projection: activeProjection,
|
|
1988
|
+
current: base,
|
|
1989
|
+
})
|
|
1990
|
+
activeProjection.cursor = paused
|
|
1991
|
+
? null
|
|
1992
|
+
: { throughIndex: projected.throughIndex, stream: projected.cursor }
|
|
1966
1993
|
const next = {
|
|
1967
1994
|
...state,
|
|
1968
1995
|
activeProjection,
|
|
1996
|
+
messages: upsertResponse({
|
|
1997
|
+
messages: state.messages,
|
|
1998
|
+
message: paused
|
|
1999
|
+
? projectedResponse({ state, projection: activeProjection })
|
|
2000
|
+
: projected.message,
|
|
2001
|
+
inputId:
|
|
2002
|
+
state.activeGeneration?.messageId ??
|
|
2003
|
+
state.active?.input.id ??
|
|
2004
|
+
payload.messageId,
|
|
2005
|
+
}),
|
|
1969
2006
|
tools: projectedTools(activeProjection),
|
|
1970
2007
|
}
|
|
1971
2008
|
return { ...next, status: lifecycleStatus(state, next) }
|
|
@@ -2020,7 +2057,7 @@ export function reduceAIState<M extends UIMessage>(
|
|
|
2020
2057
|
}
|
|
2021
2058
|
return { ...next, status: lifecycleStatus(state, next) }
|
|
2022
2059
|
}
|
|
2023
|
-
const activeProjection: ActiveGenerationProjection
|
|
2060
|
+
const activeProjection: ActiveGenerationProjection = {
|
|
2024
2061
|
...projection,
|
|
2025
2062
|
cursor: null,
|
|
2026
2063
|
toolEvents: [
|
|
@@ -2253,10 +2290,10 @@ export function reduceAIState<M extends UIMessage>(
|
|
|
2253
2290
|
if (state.activeGeneration?.generationId !== payload.generationId) {
|
|
2254
2291
|
return state
|
|
2255
2292
|
}
|
|
2256
|
-
return
|
|
2257
|
-
...state,
|
|
2258
|
-
|
|
2259
|
-
|
|
2293
|
+
return addCompactionPart(
|
|
2294
|
+
{ ...state, compaction: { status: 'running', ...payload } },
|
|
2295
|
+
payload,
|
|
2296
|
+
)
|
|
2260
2297
|
}
|
|
2261
2298
|
case 'ai.compaction.completed': {
|
|
2262
2299
|
const payload = event.payload as CompactionCompletedPayload<M>
|
|
@@ -2267,20 +2304,41 @@ export function reduceAIState<M extends UIMessage>(
|
|
|
2267
2304
|
) {
|
|
2268
2305
|
return state
|
|
2269
2306
|
}
|
|
2270
|
-
const
|
|
2307
|
+
const {
|
|
2308
|
+
messages: _messages,
|
|
2309
|
+
retainedMessageIds: _retained,
|
|
2310
|
+
...summary
|
|
2311
|
+
} = payload
|
|
2312
|
+
const compaction: CompactionState = {
|
|
2271
2313
|
status: 'completed',
|
|
2272
|
-
...
|
|
2314
|
+
...summary,
|
|
2273
2315
|
throughIndex:
|
|
2274
2316
|
payload.throughIndex ?? state.activeProjection!.promptThroughIndex!,
|
|
2275
2317
|
}
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2318
|
+
const projection = state.activeProjection
|
|
2319
|
+
return settleCompactionPart({
|
|
2320
|
+
state: {
|
|
2321
|
+
...state,
|
|
2322
|
+
compaction,
|
|
2323
|
+
activeProjection: projection && {
|
|
2324
|
+
...projection,
|
|
2325
|
+
baseCompaction: compaction,
|
|
2326
|
+
// A new base identity records the completed part as the step's
|
|
2327
|
+
// starting point, so a later cutoff cannot revert it to running.
|
|
2328
|
+
...(projection.base === undefined
|
|
2329
|
+
? {}
|
|
2330
|
+
: { base: { ...projection.base } }),
|
|
2331
|
+
},
|
|
2282
2332
|
},
|
|
2283
|
-
|
|
2333
|
+
generationId: payload.generationId,
|
|
2334
|
+
data: {
|
|
2335
|
+
status: 'completed',
|
|
2336
|
+
...(summary.summary === undefined
|
|
2337
|
+
? {}
|
|
2338
|
+
: { summary: summary.summary }),
|
|
2339
|
+
...(summary.usage === undefined ? {} : { usage: summary.usage }),
|
|
2340
|
+
},
|
|
2341
|
+
})
|
|
2284
2342
|
}
|
|
2285
2343
|
default:
|
|
2286
2344
|
return state
|
|
@@ -2290,35 +2348,12 @@ export function reduceAIState<M extends UIMessage>(
|
|
|
2290
2348
|
/** Derive AI SDK UI messages directly from an A2 event history. */
|
|
2291
2349
|
export function deriveUIMessages<M extends UIMessage = UIMessage>(
|
|
2292
2350
|
history: readonly { type: string; payload: unknown; index: number }[],
|
|
2293
|
-
): M[] {
|
|
2351
|
+
): TranscriptMessage<M>[] {
|
|
2294
2352
|
let state = initialState<M>()
|
|
2295
2353
|
for (const event of history) state = reduceAIState(state, event)
|
|
2296
2354
|
return state.messages
|
|
2297
2355
|
}
|
|
2298
2356
|
|
|
2299
|
-
/** Build the standard AI state projection for any contract containing the protocol. */
|
|
2300
|
-
export function createReducer<D extends EventDefs>(options: {
|
|
2301
|
-
contract: Contract<D>
|
|
2302
|
-
name?: string
|
|
2303
|
-
fold?: Reducer<D, AIState<AIMessageFrom<D>>>['fold']
|
|
2304
|
-
stateSchema?: StandardSchemaV1<unknown, AIState<AIMessageFrom<D>>>
|
|
2305
|
-
}): AIStoredReducer<D, AIState<AIMessageFrom<D>>> {
|
|
2306
|
-
type M = AIMessageFrom<D>
|
|
2307
|
-
const reducer = options.contract
|
|
2308
|
-
.reducer({
|
|
2309
|
-
name: options.name ?? 'a2.ai.state.v21',
|
|
2310
|
-
initialState: initialState<M>(),
|
|
2311
|
-
...(options.stateSchema === undefined
|
|
2312
|
-
? {}
|
|
2313
|
-
: { stateSchema: options.stateSchema }),
|
|
2314
|
-
})
|
|
2315
|
-
.fold(options.fold ?? ((state, event) => reduceAIState(state, event)))
|
|
2316
|
-
return storedAIReducer({
|
|
2317
|
-
...reducer,
|
|
2318
|
-
[clientStateProjection]: projectAIClientState,
|
|
2319
|
-
})
|
|
2320
|
-
}
|
|
2321
|
-
|
|
2322
2357
|
type CommandInput<C> = { type: 'ai.control.requested'; payload: C; id?: string }
|
|
2323
2358
|
type ControlInput<
|
|
2324
2359
|
M extends UIMessage,
|
|
@@ -2455,22 +2490,24 @@ export const inputs: AIInputs = {
|
|
|
2455
2490
|
* Read the durable handler context ambient to the current AI tool
|
|
2456
2491
|
* execution. A2 runs each local tool's `execute` inside this scope, so
|
|
2457
2492
|
* plain AI SDK `tool()` definitions reach their `event`, `attempt`,
|
|
2458
|
-
* `session`, and `signal` without an A2-specific wrapper.
|
|
2459
|
-
* contract
|
|
2460
|
-
* the executing
|
|
2493
|
+
* `session`, and `signal` without an A2-specific wrapper. Pass the
|
|
2494
|
+
* agent's contract to type application events; it is verified against
|
|
2495
|
+
* the executing agent.
|
|
2461
2496
|
*/
|
|
2462
|
-
export function handlerContext<D extends AgentToolEventDefs>(
|
|
2463
|
-
|
|
2497
|
+
export function handlerContext<D extends AgentToolEventDefs = AIEventDefs>(
|
|
2498
|
+
contract?: Contract<D>,
|
|
2464
2499
|
): AgentToolContext<D> {
|
|
2465
|
-
const contract =
|
|
2466
|
-
'contract' in agentOrContract ? agentOrContract.contract : agentOrContract
|
|
2467
2500
|
const scope = ambientToolScopeStorage()?.getStore()
|
|
2468
2501
|
if (scope === undefined) {
|
|
2469
2502
|
throw new TypeError(
|
|
2470
|
-
|
|
2503
|
+
"handlerContext() requires an ambient A2 tool execution: call it inside a local tool's execute, or wrap direct tool tests with runWithHandlerContext",
|
|
2471
2504
|
)
|
|
2472
2505
|
}
|
|
2473
|
-
if (
|
|
2506
|
+
if (
|
|
2507
|
+
contract !== undefined &&
|
|
2508
|
+
scope.contract !== undefined &&
|
|
2509
|
+
scope.contract.name !== contract.name
|
|
2510
|
+
) {
|
|
2474
2511
|
throw new TypeError(
|
|
2475
2512
|
`handlerContext('${contract.name}') was called during a tool execution for agent '${scope.contract.name}'`,
|
|
2476
2513
|
)
|
|
@@ -2478,14 +2515,23 @@ export function handlerContext<D extends AgentToolEventDefs>(
|
|
|
2478
2515
|
return scope.context as AgentToolContext<D>
|
|
2479
2516
|
}
|
|
2480
2517
|
|
|
2518
|
+
/** The built-in AI reducer's name, and so its checkpoint identity. */
|
|
2519
|
+
export const AI_STATE_REDUCER = 'a2.ai.state.v26'
|
|
2520
|
+
|
|
2481
2521
|
/**
|
|
2482
|
-
*
|
|
2483
|
-
* reducer
|
|
2522
|
+
* The built-in AI protocol plus application events, as a core contract whose
|
|
2523
|
+
* reducer folds `AIState`. Isomorphic: share it when a client needs
|
|
2524
|
+
* application events or extended state; otherwise `agent({ name })` on the
|
|
2525
|
+
* server builds one and clients use the built-in protocol.
|
|
2484
2526
|
*/
|
|
2485
|
-
export function
|
|
2527
|
+
export function agentContract<
|
|
2486
2528
|
M extends UIMessage = UIMessage,
|
|
2487
2529
|
E extends EventDefs = Record<never, never>,
|
|
2488
|
-
|
|
2530
|
+
X extends object = Record<never, never>,
|
|
2531
|
+
>(
|
|
2532
|
+
options: AgentContractOptions<M, E, X>,
|
|
2533
|
+
): AgentContract<M, AIEventDefs<M> & E, X> {
|
|
2534
|
+
type D = AIEventDefs<M> & E
|
|
2489
2535
|
const builtIns = createEvents(
|
|
2490
2536
|
options.messageSchema === undefined
|
|
2491
2537
|
? undefined
|
|
@@ -2499,25 +2545,46 @@ export function agent<
|
|
|
2499
2545
|
)
|
|
2500
2546
|
}
|
|
2501
2547
|
}
|
|
2502
|
-
const
|
|
2548
|
+
const vocabulary = createContract({
|
|
2503
2549
|
name: options.name,
|
|
2504
|
-
events: { ...builtIns, ...extensions },
|
|
2550
|
+
events: { ...builtIns, ...extensions } as D,
|
|
2505
2551
|
})
|
|
2506
|
-
const
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
.fold((state, event) => reduceAIState(state, event))
|
|
2512
|
-
|
|
2513
|
-
const definition = {
|
|
2514
|
-
contract: agentContract,
|
|
2515
|
-
reducer: storedAIReducer({
|
|
2516
|
-
...reducer,
|
|
2517
|
-
[clientStateProjection]: projectAIClientState<M>,
|
|
2518
|
-
}),
|
|
2552
|
+
const app = options.reducer as AppReducer<D, X> | undefined
|
|
2553
|
+
const aiState = initialState<M>()
|
|
2554
|
+
for (const key of Object.keys(app?.initialState ?? {})) {
|
|
2555
|
+
if (isAIStateKey(key))
|
|
2556
|
+
throw new TypeError(`agent state '${key}' conflicts with AIState`)
|
|
2519
2557
|
}
|
|
2520
|
-
|
|
2558
|
+
const reducer = makeReducer({
|
|
2559
|
+
contract: vocabulary,
|
|
2560
|
+
// Application checkpoints embed AI state, so they advance with it.
|
|
2561
|
+
name:
|
|
2562
|
+
app === undefined ? AI_STATE_REDUCER : `${app.name}+${AI_STATE_REDUCER}`,
|
|
2563
|
+
initialState: { ...aiState, ...app?.initialState } as AIState<M> & X,
|
|
2564
|
+
}).fold((state, event) => {
|
|
2565
|
+
const next = reduceAIState(state, event) as AIState<M> & X
|
|
2566
|
+
if (app === undefined) return next
|
|
2567
|
+
const before = appSlice<X>(state)
|
|
2568
|
+
const after = app.fold(before, event as ContractEvent<D>)
|
|
2569
|
+
return after === before ? next : withAppSlice(next, after)
|
|
2570
|
+
})
|
|
2571
|
+
const shared = createContract({
|
|
2572
|
+
name: options.name,
|
|
2573
|
+
events: vocabulary.events,
|
|
2574
|
+
reducer: withAIStorage(
|
|
2575
|
+
withReducerRuntime({
|
|
2576
|
+
reducer,
|
|
2577
|
+
project: projectAIClientState<M> as unknown as (
|
|
2578
|
+
state: AIState<M> & X,
|
|
2579
|
+
) => AIState<M> & X,
|
|
2580
|
+
}),
|
|
2581
|
+
),
|
|
2582
|
+
})
|
|
2583
|
+
registerAgentReducer(
|
|
2584
|
+
shared.reducer,
|
|
2585
|
+
app as unknown as AppReducer<EventDefs, object> | undefined,
|
|
2586
|
+
)
|
|
2587
|
+
return shared as unknown as AgentContract<M, AIEventDefs<M> & E, X>
|
|
2521
2588
|
}
|
|
2522
2589
|
|
|
2523
2590
|
export type {
|