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/dist/ai-server.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import { C as
|
|
1
|
+
import { i as A2Error } from "./session-clear-DGI4jTQ5.js";
|
|
2
|
+
import { a as reducer, s as validateSync } from "./reducer-BAXc8JyF.js";
|
|
3
|
+
import { m as installAmbientToolScopeStorage, p as consumeSchedulerSendFailure } from "./internal-CbVvEwad.js";
|
|
4
|
+
import { a as CONSOLE_TELEMETRY, l as boundedCache, o as setServerFetchHooks, t as createServer } from "./server-Rj64fNvX.js";
|
|
5
|
+
import { A as interruptUIMessage, C as reduceToolActivity, D as flattenProgressBatches, E as upsertResponse, M as assertMessageBase, N as restoreMessageBase, O as progressBatches, S as projectedMessage, T as upsertMessage, _ as applyControlChanges, a as events, b as findMessage, d as appSlice, f as isAgentContract, g as initialControlState, h as aiEventId, j as pauseUIMessage, k as progressOwnedBy, m as AI_EVENT_ID_PREFIX, n as agentContract, p as withAIStorage, u as appReducerOf, v as controlCommit, w as retainMessageBase, y as advanceProjection } from "./ai-UrWfKeuv.js";
|
|
6
6
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
7
7
|
import { createHash } from "node:crypto";
|
|
8
8
|
import { asSchema, convertToModelMessages, isToolUIPart, stepCountIs, streamText, toUIMessageStream } from "ai";
|
|
@@ -118,7 +118,7 @@ const foldCoordinator = (state, event) => {
|
|
|
118
118
|
}));
|
|
119
119
|
const inputTokens = completion.usage?.inputTokens;
|
|
120
120
|
const estimate = completion.inputTokenEstimate;
|
|
121
|
-
return inputTokens !== void 0 && Number.isFinite(inputTokens) && inputTokens >= 0 && estimate !== void 0 ? {
|
|
121
|
+
return inputTokens !== void 0 && Number.isFinite(inputTokens) && inputTokens >= 0 && estimate !== void 0 && state.response.generation.model !== void 0 ? {
|
|
122
122
|
...uncalibrated,
|
|
123
123
|
calibration: {
|
|
124
124
|
model: state.response.generation.model,
|
|
@@ -218,7 +218,8 @@ const foldCoordinator = (state, event) => {
|
|
|
218
218
|
default: return state;
|
|
219
219
|
}
|
|
220
220
|
};
|
|
221
|
-
const aiCoordinatorReducer = (contract) =>
|
|
221
|
+
const aiCoordinatorReducer = (contract) => reducer({
|
|
222
|
+
contract,
|
|
222
223
|
name: "a2.ai.coordinator.v6",
|
|
223
224
|
initialState: {
|
|
224
225
|
closed: false,
|
|
@@ -238,16 +239,41 @@ var ControlCancelled = class extends Error {};
|
|
|
238
239
|
const callReadyForModel = (call) => call.terminal || call.call.providerExecuted === true && !call.call.supportsDeferredResults && call.response !== void 0;
|
|
239
240
|
const approvalBlocksExecution = (call) => call.approval !== void 0 && call.response?.approved !== true;
|
|
240
241
|
const createControlRuntime = (options) => {
|
|
241
|
-
const
|
|
242
|
-
|
|
242
|
+
const telemetry = options.telemetry ?? CONSOLE_TELEMETRY;
|
|
243
|
+
const reducer$2 = reducer({
|
|
244
|
+
contract: options.contract,
|
|
245
|
+
name: "a2.ai.control.v5",
|
|
243
246
|
initialState: initialControlState()
|
|
244
|
-
}).fold((state, event) =>
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
247
|
+
}).fold((state, event) => {
|
|
248
|
+
if (event.type === "ai.control.requested") {
|
|
249
|
+
const command = event.payload;
|
|
250
|
+
if (command.action === "stop") return {
|
|
251
|
+
...state,
|
|
252
|
+
pendingStops: {
|
|
253
|
+
...state.pendingStops,
|
|
254
|
+
[event.id]: command.turnId
|
|
255
|
+
}
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
if (event.type === "ai.control.decided") {
|
|
259
|
+
const receipt = event.payload;
|
|
260
|
+
if (Object.hasOwn(state.pendingStops, receipt.commandId)) {
|
|
261
|
+
const pendingStops = { ...state.pendingStops };
|
|
262
|
+
delete pendingStops[receipt.commandId];
|
|
263
|
+
return {
|
|
264
|
+
...state,
|
|
265
|
+
pendingStops
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
return event.type === "ai.control.committed" ? applyControlChanges({
|
|
270
|
+
state,
|
|
271
|
+
changes: event.payload.changes
|
|
272
|
+
}) : state;
|
|
273
|
+
});
|
|
274
|
+
const coordinator = aiCoordinatorReducer(options.contract);
|
|
249
275
|
const handler = async (ctx) => {
|
|
250
|
-
const snapshot = await ctx.session.state(reducer, { through: "latest" });
|
|
276
|
+
const snapshot = await ctx.session.state(reducer$2, { through: "latest" });
|
|
251
277
|
const before = structuredClone(snapshot.state);
|
|
252
278
|
const moves = [];
|
|
253
279
|
const state = structuredClone(snapshot.state);
|
|
@@ -479,7 +505,7 @@ const createControlRuntime = (options) => {
|
|
|
479
505
|
responseMessageId: response.responseMessageId,
|
|
480
506
|
reason: "retry"
|
|
481
507
|
},
|
|
482
|
-
id: await aiEventId("a2.ai.request.retry", options.
|
|
508
|
+
id: await aiEventId("a2.ai.request.retry", options.contract.name, ctx.event.sessionId, command.request.retryId)
|
|
483
509
|
});
|
|
484
510
|
} else reason = "stale-turn";
|
|
485
511
|
break;
|
|
@@ -611,7 +637,7 @@ const createControlRuntime = (options) => {
|
|
|
611
637
|
message: item.message,
|
|
612
638
|
generate: item.generate
|
|
613
639
|
},
|
|
614
|
-
id: await aiEventId("a2.ai.admitted", options.
|
|
640
|
+
id: await aiEventId("a2.ai.admitted", options.contract.name, ctx.event.sessionId, item.id)
|
|
615
641
|
});
|
|
616
642
|
if (!item.generate) continue;
|
|
617
643
|
state.active = {
|
|
@@ -622,7 +648,7 @@ const createControlRuntime = (options) => {
|
|
|
622
648
|
suspended: false,
|
|
623
649
|
model: null
|
|
624
650
|
};
|
|
625
|
-
const requestId = await aiEventId("a2.ai.request.message", options.
|
|
651
|
+
const requestId = await aiEventId("a2.ai.request.message", options.contract.name, ctx.event.sessionId, item.id);
|
|
626
652
|
requestModel({
|
|
627
653
|
request: {
|
|
628
654
|
messageId: item.id,
|
|
@@ -662,7 +688,7 @@ const createControlRuntime = (options) => {
|
|
|
662
688
|
const payload = ctx.event.payload;
|
|
663
689
|
const { turnId, version } = "turnId" in payload ? payload : payload.control;
|
|
664
690
|
for (const event of events) {
|
|
665
|
-
const schema = options.
|
|
691
|
+
const schema = options.contract.events[event.type];
|
|
666
692
|
const result = validateSync(schema, event.payload, `AI worker ${event.type}`);
|
|
667
693
|
if (result.issues) throw new TypeError(result.issues.map((issue) => issue.message).join("; "));
|
|
668
694
|
}
|
|
@@ -696,32 +722,39 @@ const createControlRuntime = (options) => {
|
|
|
696
722
|
});
|
|
697
723
|
const persisted = await ctx.session.append(name, ...progress, input);
|
|
698
724
|
const index = persisted.at(-1).index;
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
725
|
+
return telemetry.span("a2.ai.ack", {
|
|
726
|
+
"a2.contract": options.contract.name,
|
|
727
|
+
"a2.session_id": ctx.event.sessionId,
|
|
728
|
+
"a2.ai.ack.request_id": ctx.event.id,
|
|
729
|
+
"a2.ai.ack.report_id": input.id
|
|
730
|
+
}, async () => {
|
|
731
|
+
const stream = ctx.session.stream({ startAfter: index })[Symbol.asyncIterator]();
|
|
732
|
+
const canonical = [];
|
|
733
|
+
const wanted = new Set(lifecycle.map((event) => event.id));
|
|
734
|
+
let rejectAbort;
|
|
735
|
+
const aborted = new Promise((_, reject) => {
|
|
736
|
+
rejectAbort = reject;
|
|
737
|
+
});
|
|
738
|
+
const onAbort = () => {
|
|
739
|
+
rejectAbort(ctx.signal.reason);
|
|
740
|
+
};
|
|
741
|
+
ctx.signal.addEventListener("abort", onAbort, { once: true });
|
|
742
|
+
try {
|
|
743
|
+
ctx.signal.throwIfAborted();
|
|
744
|
+
for (;;) {
|
|
745
|
+
const item = await Promise.race([stream.next(), aborted]);
|
|
746
|
+
if (item.done) throw new Error("AI control acknowledgement stream ended");
|
|
747
|
+
if (wanted.has(item.value.id)) canonical.push(item.value);
|
|
748
|
+
if (item.value.type === "ai.control.decided" && item.value.payload.commandId === input.id) {
|
|
749
|
+
if (item.value.payload.outcome === "rejected") throw new ControlCancelled("stale AI work");
|
|
750
|
+
return [...persisted.filter((event) => event.type !== "ai.work.reported"), ...canonical];
|
|
751
|
+
}
|
|
719
752
|
}
|
|
753
|
+
} finally {
|
|
754
|
+
ctx.signal.removeEventListener("abort", onAbort);
|
|
755
|
+
await stream.return?.();
|
|
720
756
|
}
|
|
721
|
-
}
|
|
722
|
-
ctx.signal.removeEventListener("abort", onAbort);
|
|
723
|
-
await stream.return?.();
|
|
724
|
-
}
|
|
757
|
+
});
|
|
725
758
|
};
|
|
726
759
|
const settled = ({ ctx, events }) => report({
|
|
727
760
|
ctx,
|
|
@@ -738,7 +771,7 @@ const createControlRuntime = (options) => {
|
|
|
738
771
|
append,
|
|
739
772
|
settled,
|
|
740
773
|
cancelled,
|
|
741
|
-
reducer,
|
|
774
|
+
reducer: reducer$2,
|
|
742
775
|
coordinator
|
|
743
776
|
};
|
|
744
777
|
};
|
|
@@ -779,19 +812,29 @@ const progress = (value) => {
|
|
|
779
812
|
return count === value["length"];
|
|
780
813
|
};
|
|
781
814
|
const indexedEvents = (value, types) => Array.isArray(value) && value.every((event) => record(event) && index(event["index"]) && types.includes(event["type"]) && matches(events[event["type"]], event["payload"]));
|
|
815
|
+
const restorableBase = (options) => {
|
|
816
|
+
assertMessageBase(options.base);
|
|
817
|
+
return isMessage(restoreMessageBase({
|
|
818
|
+
current: options.current,
|
|
819
|
+
base: options.base
|
|
820
|
+
}));
|
|
821
|
+
};
|
|
782
822
|
const projection = (value, messages) => {
|
|
783
823
|
if (!record(value) || Object.keys(value).some((key) => ![
|
|
784
824
|
"generationId",
|
|
785
825
|
"responseMessageId",
|
|
786
826
|
"promptThroughIndex",
|
|
787
827
|
"completionIndex",
|
|
788
|
-
"
|
|
828
|
+
"base",
|
|
789
829
|
"baseCompaction",
|
|
790
830
|
"batches",
|
|
791
831
|
"toolEvents",
|
|
792
832
|
"approvalEvents",
|
|
793
833
|
"cursor"
|
|
794
|
-
].includes(key)) || !id(value["generationId"]) || !id(value["responseMessageId"]) || !index(value["promptThroughIndex"]) || value["completionIndex"] !== void 0 && !index(value["completionIndex"]) || value["
|
|
834
|
+
].includes(key)) || !id(value["generationId"]) || !id(value["responseMessageId"]) || !index(value["promptThroughIndex"]) || value["completionIndex"] !== void 0 && !index(value["completionIndex"]) || value["base"] !== void 0 && !restorableBase({
|
|
835
|
+
base: value["base"],
|
|
836
|
+
current: messages.find((message) => message.id === value["responseMessageId"])
|
|
837
|
+
}) || value["baseCompaction"] !== void 0 && !compaction(value["baseCompaction"]) || !progress(value["batches"]) || !indexedEvents(value["toolEvents"], ["ai.tool.called", "ai.tool.result"]) || !indexedEvents(value["approvalEvents"], ["ai.approval.requested", "ai.approval.responded"])) return false;
|
|
795
838
|
const cursor = value["cursor"];
|
|
796
839
|
if (cursor === void 0 || cursor === null) return true;
|
|
797
840
|
if (!record(cursor) || !index(cursor["throughIndex"]) || !record(cursor["stream"])) return false;
|
|
@@ -825,7 +868,7 @@ const authority = (value) => value === null || record(value) && Object.keys(valu
|
|
|
825
868
|
"pausing",
|
|
826
869
|
"paused"
|
|
827
870
|
].includes(String(value["phase"])));
|
|
828
|
-
const contextSchema = () => ({ "~standard": {
|
|
871
|
+
const contextSchema = (app) => ({ "~standard": {
|
|
829
872
|
version: 1,
|
|
830
873
|
vendor: "a2-context",
|
|
831
874
|
validate(value) {
|
|
@@ -838,7 +881,8 @@ const contextSchema = () => ({ "~standard": {
|
|
|
838
881
|
"authority",
|
|
839
882
|
"generation",
|
|
840
883
|
"activeProjection",
|
|
841
|
-
"messageIndexes"
|
|
884
|
+
"messageIndexes",
|
|
885
|
+
...app === void 0 ? [] : ["app"]
|
|
842
886
|
];
|
|
843
887
|
try {
|
|
844
888
|
if (!record(value) || keys.some((key) => !Object.hasOwn(value, key)) || Object.keys(value).some((key) => !keys.includes(key))) return invalid;
|
|
@@ -850,7 +894,7 @@ const contextSchema = () => ({ "~standard": {
|
|
|
850
894
|
modelId,
|
|
851
895
|
limits: entry["limits"]
|
|
852
896
|
}))) || !indexes(value["messageIndexes"], messages)) return invalid;
|
|
853
|
-
if (generation !== null && (!record(generation) || !matches(events["ai.generation.started"], generation["owner"]) || ![
|
|
897
|
+
if (generation !== null && (!record(generation) || !matches(events["ai.generation.started"], generation["owner"]) || !index(generation["startIndex"]) || ![
|
|
854
898
|
"streaming",
|
|
855
899
|
"completed",
|
|
856
900
|
"paused",
|
|
@@ -861,6 +905,7 @@ const contextSchema = () => ({ "~standard": {
|
|
|
861
905
|
if (current !== null && (!projection(current, messages) || !record(current) || current["generationId"] !== owner?.["generationId"] || current["responseMessageId"] !== owner?.["responseMessageId"])) return invalid;
|
|
862
906
|
if (generation !== null && generation["phase"] !== "superseded" && current === null) return invalid;
|
|
863
907
|
if (value["closed"] && (generation !== null || current !== null)) return invalid;
|
|
908
|
+
if (app !== void 0 && !record(value["app"])) return invalid;
|
|
864
909
|
return { value };
|
|
865
910
|
} catch {
|
|
866
911
|
return invalid;
|
|
@@ -888,14 +933,24 @@ const replaceResponse = ({ state, message, index }) => {
|
|
|
888
933
|
}
|
|
889
934
|
};
|
|
890
935
|
};
|
|
891
|
-
const calledTools = (projection) => /* @__PURE__ */ new Set([...
|
|
936
|
+
const calledTools = ({ projection, current }) => /* @__PURE__ */ new Set([...restoreMessageBase({
|
|
937
|
+
current,
|
|
938
|
+
base: projection.base
|
|
939
|
+
})?.parts.flatMap((part) => "toolCallId" in part ? [part.toolCallId] : []) ?? [], ...projection.toolEvents.filter((event) => event.type === "ai.tool.called").map((event) => event.payload.toolCallId)]);
|
|
892
940
|
const renderProjection = ({ state, index }) => {
|
|
893
941
|
const projection = state.activeProjection;
|
|
894
|
-
|
|
895
|
-
|
|
942
|
+
const current = findMessage(state.messages, projection.responseMessageId);
|
|
943
|
+
let message = projectedMessage({
|
|
944
|
+
projection,
|
|
945
|
+
current
|
|
946
|
+
});
|
|
947
|
+
if (projection.base === void 0 && projection.batches.length === 0 && message.parts.length === 0) return state;
|
|
896
948
|
if (state.generation?.phase === "paused") message = pauseUIMessage({
|
|
897
949
|
message,
|
|
898
|
-
toolCallIds: calledTools(
|
|
950
|
+
toolCallIds: calledTools({
|
|
951
|
+
projection,
|
|
952
|
+
current
|
|
953
|
+
})
|
|
899
954
|
});
|
|
900
955
|
if (state.generation?.phase === "failed") message = interruptUIMessage(message, `Generation failed before tool completion: ${state.generation.error}`);
|
|
901
956
|
return replaceResponse({
|
|
@@ -913,9 +968,13 @@ const renderProjection = ({ state, index }) => {
|
|
|
913
968
|
const generationBase = (state) => {
|
|
914
969
|
const projection = state.activeProjection;
|
|
915
970
|
if (!projection) return state;
|
|
971
|
+
const base = restoreMessageBase({
|
|
972
|
+
current: findMessage(state.messages, projection.responseMessageId),
|
|
973
|
+
base: projection.base
|
|
974
|
+
});
|
|
916
975
|
return {
|
|
917
976
|
...state,
|
|
918
|
-
messages:
|
|
977
|
+
messages: base === void 0 ? state.messages.filter((message) => message.id !== projection.responseMessageId) : upsertMessage(state.messages, base),
|
|
919
978
|
compaction: projection.baseCompaction ?? null
|
|
920
979
|
};
|
|
921
980
|
};
|
|
@@ -934,22 +993,37 @@ const endGeneration = (state) => ({
|
|
|
934
993
|
activeProjection: null
|
|
935
994
|
});
|
|
936
995
|
const ownsGeneration = (state, owner) => state.generation?.owner.generationId === owner.generationId && state.generation.owner.requestId === owner.requestId && state.generation.phase !== "superseded";
|
|
937
|
-
const createContextReducer = ({
|
|
938
|
-
const
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
996
|
+
const createContextReducer = ({ contract }) => {
|
|
997
|
+
const app = appReducerOf(contract);
|
|
998
|
+
const initialState = {
|
|
999
|
+
messages: [],
|
|
1000
|
+
compaction: null,
|
|
1001
|
+
modelMetadata: {},
|
|
1002
|
+
closed: false,
|
|
1003
|
+
authority: null,
|
|
1004
|
+
generation: null,
|
|
1005
|
+
activeProjection: null,
|
|
1006
|
+
messageIndexes: {},
|
|
1007
|
+
...app === void 0 ? {} : { app: app.initialState }
|
|
1008
|
+
};
|
|
1009
|
+
const reducer$1 = reducer({
|
|
1010
|
+
contract,
|
|
1011
|
+
name: app === void 0 ? "a2.ai.context.v6" : `${app.name}+a2.ai.context.v6`,
|
|
1012
|
+
initialState,
|
|
1013
|
+
stateSchema: contextSchema(app)
|
|
1014
|
+
}).fold((state, event) => {
|
|
1015
|
+
const after = retainMessageBase({
|
|
1016
|
+
before: state,
|
|
1017
|
+
after: reduceContext(state, event)
|
|
1018
|
+
});
|
|
1019
|
+
if (app === void 0) return after;
|
|
1020
|
+
const next = app.fold(state.app, event);
|
|
1021
|
+
return next === state.app ? after : {
|
|
1022
|
+
...after,
|
|
1023
|
+
app: appSlice(next)
|
|
1024
|
+
};
|
|
1025
|
+
});
|
|
1026
|
+
return withAIStorage(reducer$1);
|
|
953
1027
|
};
|
|
954
1028
|
const reduceContext = (state, event) => {
|
|
955
1029
|
if (state.closed) return state;
|
|
@@ -1006,6 +1080,7 @@ const reduceContext = (state, event) => {
|
|
|
1006
1080
|
...base,
|
|
1007
1081
|
generation: {
|
|
1008
1082
|
owner,
|
|
1083
|
+
startIndex: event.index,
|
|
1009
1084
|
phase: "streaming",
|
|
1010
1085
|
...previous?.owner.responseMessageId === owner.responseMessageId && previous.responseIndex !== void 0 ? { responseIndex: previous.responseIndex } : {}
|
|
1011
1086
|
},
|
|
@@ -1013,7 +1088,7 @@ const reduceContext = (state, event) => {
|
|
|
1013
1088
|
generationId: owner.generationId,
|
|
1014
1089
|
responseMessageId: owner.responseMessageId,
|
|
1015
1090
|
promptThroughIndex: owner.promptThroughIndex ?? event.index - 1,
|
|
1016
|
-
...baseMessage === void 0 ? {} : { baseMessage },
|
|
1091
|
+
...baseMessage === void 0 ? {} : { base: { partCount: baseMessage.parts.length } },
|
|
1017
1092
|
baseCompaction: base.compaction,
|
|
1018
1093
|
batches: progressBatches(),
|
|
1019
1094
|
toolEvents: [],
|
|
@@ -1023,7 +1098,12 @@ const reduceContext = (state, event) => {
|
|
|
1023
1098
|
}
|
|
1024
1099
|
case "ai.generation.progress": {
|
|
1025
1100
|
const payload = event.payload;
|
|
1026
|
-
|
|
1101
|
+
const generation = state.generation;
|
|
1102
|
+
if (generation === null || generation.phase !== "streaming" || !progressOwnedBy({
|
|
1103
|
+
payload,
|
|
1104
|
+
owner: generation.owner,
|
|
1105
|
+
startIndex: generation.startIndex
|
|
1106
|
+
}) || !state.activeProjection) return state;
|
|
1027
1107
|
const projection = state.activeProjection;
|
|
1028
1108
|
const progress = advanceProjection({
|
|
1029
1109
|
projection,
|
|
@@ -1102,9 +1182,12 @@ const reduceContext = (state, event) => {
|
|
|
1102
1182
|
approvalEvents: projection.approvalEvents.filter((item) => item.index <= cutoff)
|
|
1103
1183
|
};
|
|
1104
1184
|
let next = generationBase(state);
|
|
1105
|
-
if (retained.
|
|
1185
|
+
if (retained.base !== void 0 || retained.batches.length > 0) next = replaceResponse({
|
|
1106
1186
|
state: next,
|
|
1107
|
-
message: interruptUIMessage(projectedMessage(
|
|
1187
|
+
message: interruptUIMessage(projectedMessage({
|
|
1188
|
+
projection: retained,
|
|
1189
|
+
current: findMessage(state.messages, retained.responseMessageId)
|
|
1190
|
+
})),
|
|
1108
1191
|
index: event.index
|
|
1109
1192
|
});
|
|
1110
1193
|
return endGeneration(next);
|
|
@@ -1116,7 +1199,10 @@ const reduceContext = (state, event) => {
|
|
|
1116
1199
|
case "ai.generation.paused": {
|
|
1117
1200
|
const { generationId } = event.payload;
|
|
1118
1201
|
if (state.generation?.owner.generationId !== generationId || state.generation.phase === "failed" || state.generation.phase === "superseded" || !state.activeProjection) return state;
|
|
1119
|
-
const toolCallIds = calledTools(
|
|
1202
|
+
const toolCallIds = calledTools({
|
|
1203
|
+
projection: state.activeProjection,
|
|
1204
|
+
current: findMessage(state.messages, state.activeProjection.responseMessageId)
|
|
1205
|
+
});
|
|
1120
1206
|
return renderProjection({
|
|
1121
1207
|
state: {
|
|
1122
1208
|
...state,
|
|
@@ -1219,9 +1305,9 @@ const reduceContext = (state, event) => {
|
|
|
1219
1305
|
...projection,
|
|
1220
1306
|
cursor: null
|
|
1221
1307
|
};
|
|
1222
|
-
delete activeProjection.
|
|
1308
|
+
delete activeProjection.base;
|
|
1223
1309
|
const baseMessage = messages.find((message) => message.id === projection.responseMessageId);
|
|
1224
|
-
if (baseMessage !== void 0) activeProjection.
|
|
1310
|
+
if (baseMessage !== void 0) activeProjection.base = { partCount: baseMessage.parts.length };
|
|
1225
1311
|
const compaction = {
|
|
1226
1312
|
status: "completed",
|
|
1227
1313
|
generationId: payload.generationId,
|
|
@@ -1444,23 +1530,24 @@ const measuredInputTokens = (options) => {
|
|
|
1444
1530
|
return previous !== void 0 && previous.model === options.model && options.estimate >= previous.estimate ? Math.ceil(previous.inputTokens + options.estimate - previous.estimate) : options.estimate;
|
|
1445
1531
|
};
|
|
1446
1532
|
//#endregion
|
|
1447
|
-
//#region src/ai-
|
|
1533
|
+
//#region src/ai-agent.ts
|
|
1448
1534
|
/**
|
|
1449
|
-
*
|
|
1450
|
-
*
|
|
1451
|
-
* `createHandlers()` exposes the ordinary A2 handler table;
|
|
1452
|
-
* `createAgentServer()` is its batteries-included assembly with createServer.
|
|
1535
|
+
* The server-only implementation of the experimental-a2/ai protocol: the
|
|
1536
|
+
* handler table and `agent()`, its assembly as an ordinary A2 server.
|
|
1453
1537
|
*/
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1538
|
+
const isAgentIngressEvent = (event) => {
|
|
1539
|
+
if (event.type !== "ai.control.requested") return false;
|
|
1540
|
+
const command = event.payload;
|
|
1541
|
+
if (typeof command !== "object" || command === null) return false;
|
|
1542
|
+
const action = command.action;
|
|
1543
|
+
if (action === "request-input" || action === "tool-result") return false;
|
|
1544
|
+
if (action === "send" || action === "edit" || action === "steer") return command.message?.role === "user";
|
|
1545
|
+
return true;
|
|
1546
|
+
};
|
|
1547
|
+
const validateAgentIngress = ({ context, applicationEvents }) => {
|
|
1548
|
+
const rejected = context.events.find((event) => Object.hasOwn(events, event.type) ? !isAgentIngressEvent(event) : !applicationEvents);
|
|
1462
1549
|
if (rejected !== void 0) throw new A2Error("INVALID_PAYLOAD", `event '${rejected.type}' is server-only for an AI agent`);
|
|
1463
|
-
}
|
|
1550
|
+
};
|
|
1464
1551
|
const checkAbort = (signal) => {
|
|
1465
1552
|
if (!signal.aborted) return false;
|
|
1466
1553
|
const reason = signal.reason;
|
|
@@ -1478,6 +1565,22 @@ const resolve = async (value, context) => {
|
|
|
1478
1565
|
checkAbort(context.signal);
|
|
1479
1566
|
return result;
|
|
1480
1567
|
};
|
|
1568
|
+
const withModelSettings = (options) => {
|
|
1569
|
+
const { generation, settings } = options;
|
|
1570
|
+
if (settings.reasoning === void 0 && settings.speed === void 0) return generation;
|
|
1571
|
+
const { speed: _speed, ...gateway } = generation.providerOptions?.["gateway"] ?? {};
|
|
1572
|
+
return {
|
|
1573
|
+
...generation,
|
|
1574
|
+
...settings.reasoning === void 0 ? {} : { reasoning: settings.reasoning },
|
|
1575
|
+
...settings.speed === void 0 ? {} : { providerOptions: {
|
|
1576
|
+
...generation.providerOptions,
|
|
1577
|
+
gateway: settings.speed === "fast" ? {
|
|
1578
|
+
...gateway,
|
|
1579
|
+
speed: "fast"
|
|
1580
|
+
} : gateway
|
|
1581
|
+
} }
|
|
1582
|
+
};
|
|
1583
|
+
};
|
|
1481
1584
|
const modelName = (model) => {
|
|
1482
1585
|
if (typeof model === "string") return model;
|
|
1483
1586
|
if ("modelId" in model && typeof model.modelId === "string") return model.modelId;
|
|
@@ -1489,7 +1592,8 @@ const toolScopeStorage = installAmbientToolScopeStorage(() => new AsyncLocalStor
|
|
|
1489
1592
|
function runWithHandlerContext(ctx, fn) {
|
|
1490
1593
|
return toolScopeStorage.run({ context: ctx }, fn);
|
|
1491
1594
|
}
|
|
1492
|
-
|
|
1595
|
+
/** Sessions whose latest step prompt one process keeps for that step's tools. */
|
|
1596
|
+
const PROMPT_SESSIONS = 64;
|
|
1493
1597
|
const asyncIterator = (value) => {
|
|
1494
1598
|
if (typeof value !== "object" || value === null) return void 0;
|
|
1495
1599
|
const factory = Reflect.get(value, Symbol.asyncIterator);
|
|
@@ -1497,6 +1601,49 @@ const asyncIterator = (value) => {
|
|
|
1497
1601
|
return factory.call(value);
|
|
1498
1602
|
};
|
|
1499
1603
|
const isBoundaryChunk = (chunk) => chunk !== void 0 && (chunk.type === "text-end" || chunk.type === "reasoning-end" || chunk.type === "tool-input-available" || chunk.type === "tool-input-error" || chunk.type === "tool-output-available" || chunk.type === "tool-output-error" || chunk.type === "tool-output-denied" || chunk.type === "tool-approval-request" || chunk.type === "finish" || chunk.type === "abort" || chunk.type === "error");
|
|
1604
|
+
const DELTA_KEYS = {
|
|
1605
|
+
"text-delta": /* @__PURE__ */ new Set([
|
|
1606
|
+
"type",
|
|
1607
|
+
"id",
|
|
1608
|
+
"delta",
|
|
1609
|
+
"providerMetadata"
|
|
1610
|
+
]),
|
|
1611
|
+
"reasoning-delta": /* @__PURE__ */ new Set([
|
|
1612
|
+
"type",
|
|
1613
|
+
"id",
|
|
1614
|
+
"delta",
|
|
1615
|
+
"providerMetadata"
|
|
1616
|
+
]),
|
|
1617
|
+
"tool-input-delta": /* @__PURE__ */ new Set([
|
|
1618
|
+
"type",
|
|
1619
|
+
"toolCallId",
|
|
1620
|
+
"inputTextDelta"
|
|
1621
|
+
])
|
|
1622
|
+
};
|
|
1623
|
+
const isDeltaChunk = (chunk) => Object.hasOwn(DELTA_KEYS, chunk.type) && Object.keys(chunk).every((key) => DELTA_KEYS[chunk.type].has(key)) && typeof (chunk.type === "tool-input-delta" ? chunk.inputTextDelta : chunk.delta) === "string";
|
|
1624
|
+
const joinDeltas = (left, right) => {
|
|
1625
|
+
if (left.type === "tool-input-delta") return right.type === "tool-input-delta" && right.toolCallId === left.toolCallId ? {
|
|
1626
|
+
...left,
|
|
1627
|
+
inputTextDelta: left.inputTextDelta + right.inputTextDelta
|
|
1628
|
+
} : void 0;
|
|
1629
|
+
if (right.type === "tool-input-delta" || right.type !== left.type || right.id !== left.id) return void 0;
|
|
1630
|
+
const providerMetadata = right.providerMetadata !== void 0 ? right.providerMetadata : left.providerMetadata;
|
|
1631
|
+
return {
|
|
1632
|
+
...left,
|
|
1633
|
+
delta: left.delta + right.delta,
|
|
1634
|
+
...providerMetadata === void 0 ? {} : { providerMetadata }
|
|
1635
|
+
};
|
|
1636
|
+
};
|
|
1637
|
+
const mergeDeltas = (chunks) => {
|
|
1638
|
+
const merged = [];
|
|
1639
|
+
for (const chunk of chunks) {
|
|
1640
|
+
const previous = merged.at(-1);
|
|
1641
|
+
const joined = previous !== void 0 && isDeltaChunk(previous) && isDeltaChunk(chunk) ? joinDeltas(previous, chunk) : void 0;
|
|
1642
|
+
if (joined === void 0) merged.push(chunk);
|
|
1643
|
+
else merged[merged.length - 1] = joined;
|
|
1644
|
+
}
|
|
1645
|
+
return merged;
|
|
1646
|
+
};
|
|
1500
1647
|
const nextOrTimer = async (next, delayMs) => {
|
|
1501
1648
|
let timer;
|
|
1502
1649
|
try {
|
|
@@ -1518,7 +1665,7 @@ async function generateWithAISDK(options) {
|
|
|
1518
1665
|
...context.generation,
|
|
1519
1666
|
headers: Object.keys(headers).some((key) => key.toLowerCase() === "x-session-affinity") ? headers : {
|
|
1520
1667
|
...headers,
|
|
1521
|
-
"x-session-affinity": createHash("sha256").update(JSON.stringify([agentName, context.
|
|
1668
|
+
"x-session-affinity": createHash("sha256").update(JSON.stringify([agentName, context.event.sessionId])).digest("hex")
|
|
1522
1669
|
},
|
|
1523
1670
|
providerOptions: {
|
|
1524
1671
|
...context.generation.providerOptions,
|
|
@@ -1533,7 +1680,7 @@ async function generateWithAISDK(options) {
|
|
|
1533
1680
|
tools: context.tools,
|
|
1534
1681
|
messages: context.messages,
|
|
1535
1682
|
modelMessages: context.modelMessages,
|
|
1536
|
-
responseMessageId: context.responseMessageId,
|
|
1683
|
+
responseMessageId: context.event.payload.responseMessageId,
|
|
1537
1684
|
abortSignal: context.signal,
|
|
1538
1685
|
...context.instructions === void 0 ? {} : { instructions: context.instructions },
|
|
1539
1686
|
settings: generation,
|
|
@@ -1552,9 +1699,13 @@ async function* consumeGeneration(options) {
|
|
|
1552
1699
|
const pending = [];
|
|
1553
1700
|
let streamedFinishReason;
|
|
1554
1701
|
let streamedError;
|
|
1555
|
-
const maxChunks = options.progress?.maxChunks ??
|
|
1556
|
-
const maxDelayMs = options.progress?.maxDelayMs ??
|
|
1702
|
+
const maxChunks = options.progress?.maxChunks ?? 128;
|
|
1703
|
+
const maxDelayMs = options.progress?.maxDelayMs ?? 0;
|
|
1557
1704
|
let lastFlush = Date.now();
|
|
1705
|
+
const batch = () => ({
|
|
1706
|
+
type: "progress",
|
|
1707
|
+
chunks: mergeDeltas(pending.splice(0))
|
|
1708
|
+
});
|
|
1558
1709
|
const reader = options.source.stream.getReader();
|
|
1559
1710
|
let pendingChunk = reader.read();
|
|
1560
1711
|
try {
|
|
@@ -1564,10 +1715,7 @@ async function* consumeGeneration(options) {
|
|
|
1564
1715
|
const remaining = Math.max(0, maxDelayMs - (Date.now() - lastFlush));
|
|
1565
1716
|
const outcome = await nextOrTimer(pendingChunk, remaining);
|
|
1566
1717
|
if (outcome.type === "timer") {
|
|
1567
|
-
yield
|
|
1568
|
-
type: "progress",
|
|
1569
|
-
chunks: pending.splice(0)
|
|
1570
|
-
};
|
|
1718
|
+
yield batch();
|
|
1571
1719
|
lastFlush = Date.now();
|
|
1572
1720
|
continue;
|
|
1573
1721
|
}
|
|
@@ -1580,18 +1728,12 @@ async function* consumeGeneration(options) {
|
|
|
1580
1728
|
if (chunk.type === "error") streamedError = chunk.errorText;
|
|
1581
1729
|
pendingChunk = reader.read();
|
|
1582
1730
|
if (pending.length >= maxChunks || isBoundaryChunk(pending.at(-1))) {
|
|
1583
|
-
yield
|
|
1584
|
-
type: "progress",
|
|
1585
|
-
chunks: pending.splice(0)
|
|
1586
|
-
};
|
|
1731
|
+
yield batch();
|
|
1587
1732
|
lastFlush = Date.now();
|
|
1588
1733
|
}
|
|
1589
1734
|
}
|
|
1590
1735
|
} catch (error) {
|
|
1591
|
-
if (pending.length > 0) yield
|
|
1592
|
-
type: "progress",
|
|
1593
|
-
chunks: pending.splice(0)
|
|
1594
|
-
};
|
|
1736
|
+
if (pending.length > 0) yield batch();
|
|
1595
1737
|
throw error;
|
|
1596
1738
|
} finally {
|
|
1597
1739
|
pendingChunk.catch(() => {});
|
|
@@ -1605,16 +1747,10 @@ async function* consumeGeneration(options) {
|
|
|
1605
1747
|
try {
|
|
1606
1748
|
completion = options.source.completion?.();
|
|
1607
1749
|
} catch (error) {
|
|
1608
|
-
if (pending.length > 0) yield
|
|
1609
|
-
type: "progress",
|
|
1610
|
-
chunks: pending.splice(0)
|
|
1611
|
-
};
|
|
1750
|
+
if (pending.length > 0) yield batch();
|
|
1612
1751
|
throw error;
|
|
1613
1752
|
}
|
|
1614
|
-
if (pending.length > 0) yield
|
|
1615
|
-
type: "progress",
|
|
1616
|
-
chunks: pending.splice(0)
|
|
1617
|
-
};
|
|
1753
|
+
if (pending.length > 0) yield batch();
|
|
1618
1754
|
if (streamedError !== void 0) throw new Error(streamedError);
|
|
1619
1755
|
const finishReason = completion?.finishReason ?? streamedFinishReason;
|
|
1620
1756
|
yield {
|
|
@@ -1665,8 +1801,7 @@ const summarize = async (options) => {
|
|
|
1665
1801
|
modelMessages: [...options.context.modelMessages, {
|
|
1666
1802
|
role: "user",
|
|
1667
1803
|
content: instruction
|
|
1668
|
-
}]
|
|
1669
|
-
responseMessageId: await aiEventId("a2.ai.summary", options.context.generationId)
|
|
1804
|
+
}]
|
|
1670
1805
|
},
|
|
1671
1806
|
messageMetadata: void 0
|
|
1672
1807
|
});
|
|
@@ -1893,12 +2028,8 @@ const validateCompaction = (options) => {
|
|
|
1893
2028
|
}
|
|
1894
2029
|
return compaction;
|
|
1895
2030
|
};
|
|
1896
|
-
/**
|
|
1897
|
-
* Build the ordinary A2 handler table for the built-in agent protocol.
|
|
1898
|
-
* Application handlers can be spread beside this table.
|
|
1899
|
-
*/
|
|
1900
2031
|
function createHandlers(options) {
|
|
1901
|
-
if (options.model === void 0) throw new TypeError("
|
|
2032
|
+
if (options.model === void 0) throw new TypeError("agent requires model");
|
|
1902
2033
|
if (options.progress?.maxChunks !== void 0 && (!Number.isInteger(options.progress.maxChunks) || options.progress.maxChunks < 1)) throw new TypeError("progress.maxChunks must be a positive integer");
|
|
1903
2034
|
if (options.progress?.maxDelayMs !== void 0 && (!Number.isFinite(options.progress.maxDelayMs) || options.progress.maxDelayMs < 0)) throw new TypeError("progress.maxDelayMs must be a non-negative number");
|
|
1904
2035
|
if (options.maxSteps !== void 0 && options.maxSteps !== Number.POSITIVE_INFINITY && (!Number.isInteger(options.maxSteps) || options.maxSteps < 1)) throw new TypeError("maxSteps must be a positive integer or Infinity");
|
|
@@ -1906,23 +2037,39 @@ function createHandlers(options) {
|
|
|
1906
2037
|
const staticCompaction = typeof configuredCompaction === "function" ? void 0 : validateCompaction({
|
|
1907
2038
|
compaction: configuredCompaction,
|
|
1908
2039
|
explicit: options.compaction !== void 0,
|
|
1909
|
-
generation: options.generation,
|
|
2040
|
+
generation: typeof options.generation === "function" ? void 0 : options.generation,
|
|
1910
2041
|
tools: options.tools
|
|
1911
2042
|
});
|
|
1912
2043
|
const tools = options.tools ?? {};
|
|
1913
|
-
const
|
|
2044
|
+
const telemetry = options.telemetry ?? CONSOLE_TELEMETRY;
|
|
1914
2045
|
const maxSteps = options.maxSteps ?? Number.POSITIVE_INFINITY;
|
|
1915
|
-
const control = createControlRuntime({
|
|
2046
|
+
const control = createControlRuntime({
|
|
2047
|
+
contract: options.contract,
|
|
2048
|
+
telemetry
|
|
2049
|
+
});
|
|
2050
|
+
const controlHandler = (ctx) => telemetry.span("a2.ai.control", {
|
|
2051
|
+
"a2.contract": options.contract.name,
|
|
2052
|
+
"a2.session_id": ctx.event.sessionId,
|
|
2053
|
+
"a2.ai.control.type": ctx.event.type,
|
|
2054
|
+
"a2.ai.control.id": ctx.event.id,
|
|
2055
|
+
"a2.ai.control.attempt": ctx.attempt
|
|
2056
|
+
}, () => control.handler(ctx));
|
|
1916
2057
|
const coordinator = control.coordinator;
|
|
1917
|
-
const contextReducer = createContextReducer({
|
|
2058
|
+
const contextReducer = createContextReducer({ contract: options.contract });
|
|
1918
2059
|
const readContext = async (session, through) => {
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
2060
|
+
return await session.state(contextReducer, { through });
|
|
2061
|
+
};
|
|
2062
|
+
const prompts = boundedCache({
|
|
2063
|
+
maxEntries: PROMPT_SESSIONS,
|
|
2064
|
+
maxBytes: PROMPT_SESSIONS
|
|
2065
|
+
});
|
|
2066
|
+
const cachePrompt = ({ sessionId, generationId, messages }) => prompts.set(sessionId, {
|
|
2067
|
+
generationId,
|
|
2068
|
+
messages
|
|
2069
|
+
}, 1);
|
|
2070
|
+
const forgetPrompt = (sessionId, generationId) => {
|
|
2071
|
+
if (prompts.get(sessionId)?.generationId === generationId) prompts.delete(sessionId);
|
|
1924
2072
|
};
|
|
1925
|
-
const promptCache = /* @__PURE__ */ new Map();
|
|
1926
2073
|
const resultEvent = async (call, suffix, result) => ({
|
|
1927
2074
|
type: "ai.tool.result",
|
|
1928
2075
|
id: await aiEventId("a2.ai.tool.result", call.generationId, call.toolCallId, suffix),
|
|
@@ -1945,25 +2092,32 @@ function createHandlers(options) {
|
|
|
1945
2092
|
});
|
|
1946
2093
|
const promptMessages = (input) => {
|
|
1947
2094
|
const { ctx, call, generation: owner } = input;
|
|
1948
|
-
const
|
|
1949
|
-
const cached =
|
|
1950
|
-
if (cached) return cached;
|
|
2095
|
+
const sessionId = ctx.event.sessionId;
|
|
2096
|
+
const cached = prompts.get(sessionId);
|
|
2097
|
+
if (cached?.generationId === call.generationId) return cached.messages;
|
|
1951
2098
|
const computation = (async () => {
|
|
1952
2099
|
const frontier = owner.promptThroughIndex;
|
|
1953
2100
|
if (frontier === void 0) throw new TypeError("AI work requires a prompt checkpoint");
|
|
1954
2101
|
const snapshot = await readContext(ctx.session, ctx.event.index);
|
|
1955
2102
|
if (snapshot.state.generation?.owner.generationId !== owner.generationId || snapshot.state.generation.owner.requestId !== owner.requestId || snapshot.state.activeProjection?.generationId !== owner.generationId) throw new TypeError("Tool prompt checkpoint does not own its generation");
|
|
1956
2103
|
const state = generationBase(snapshot.state);
|
|
1957
|
-
return
|
|
2104
|
+
return telemetry.span("a2.ai.context", {
|
|
2105
|
+
"a2.contract": options.contract.name,
|
|
2106
|
+
"a2.session_id": ctx.event.sessionId,
|
|
2107
|
+
"a2.ai.context.request_id": call.requestId,
|
|
2108
|
+
"a2.ai.context.phase": "tool"
|
|
2109
|
+
}, () => modelContext({
|
|
1958
2110
|
messages: state.messages.filter((message) => state.messageIndexes[message.id] <= frontier),
|
|
1959
2111
|
state,
|
|
1960
2112
|
tools
|
|
1961
|
-
});
|
|
2113
|
+
}));
|
|
1962
2114
|
})();
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
2115
|
+
cachePrompt({
|
|
2116
|
+
sessionId,
|
|
2117
|
+
generationId: call.generationId,
|
|
2118
|
+
messages: computation
|
|
1966
2119
|
});
|
|
2120
|
+
computation.catch(() => forgetPrompt(sessionId, call.generationId));
|
|
1967
2121
|
return computation;
|
|
1968
2122
|
};
|
|
1969
2123
|
const toolExecutionFailure = async (ctx, call, error) => {
|
|
@@ -2032,7 +2186,7 @@ function createHandlers(options) {
|
|
|
2032
2186
|
const messages = await promptMessages(input);
|
|
2033
2187
|
if (checkAbort(ctx.signal)) return;
|
|
2034
2188
|
const scope = {
|
|
2035
|
-
contract: options.
|
|
2189
|
+
contract: options.contract,
|
|
2036
2190
|
context: ctx
|
|
2037
2191
|
};
|
|
2038
2192
|
return toolScopeStorage.run(scope, () => runToolExecution(ctx, call, execute, messages));
|
|
@@ -2042,7 +2196,7 @@ function createHandlers(options) {
|
|
|
2042
2196
|
const state = (await ctx.session.state(control.reducer, { through: "latest" })).state;
|
|
2043
2197
|
const request = ctx.event.payload;
|
|
2044
2198
|
const call = state.coordinator.response?.calls.find((candidate) => candidate.work?.id === ctx.event.id);
|
|
2045
|
-
if (state.active?.turnId !== request.turnId || state.active.suspended || state.active.version !== request.version || state.coordinator.response?.failure !== void 0 || !call || call.terminal || call.work?.settled) return control.settled({
|
|
2199
|
+
if (state.active?.turnId !== request.turnId || Object.values(state.pendingStops).includes(request.turnId) || state.active.suspended || state.active.version !== request.version || state.coordinator.response?.failure !== void 0 || !call || call.terminal || call.work?.settled) return control.settled({
|
|
2046
2200
|
ctx,
|
|
2047
2201
|
events: void 0
|
|
2048
2202
|
});
|
|
@@ -2071,7 +2225,9 @@ function createHandlers(options) {
|
|
|
2071
2225
|
const request = ctx.event.payload;
|
|
2072
2226
|
const snapshot = await readContext(ctx.session, "latest");
|
|
2073
2227
|
if (snapshot.state.closed || snapshot.state.authority?.requestId !== requestId || snapshot.state.authority?.phase === "paused" || snapshot.state.authority?.phase === "pausing") return;
|
|
2074
|
-
const
|
|
2228
|
+
const controlState = (await ctx.session.state(control.reducer, { through: snapshot.index })).state;
|
|
2229
|
+
if (request.control !== void 0 && Object.values(controlState.pendingStops).includes(request.control.turnId)) return;
|
|
2230
|
+
const coordinatorState = controlState.coordinator;
|
|
2075
2231
|
if (coordinatorState.closed || coordinatorState.response?.activeRequestId !== requestId) return;
|
|
2076
2232
|
const response = coordinatorState.response;
|
|
2077
2233
|
const current = response.generation?.requestId === requestId ? response.generation : void 0;
|
|
@@ -2090,109 +2246,124 @@ function createHandlers(options) {
|
|
|
2090
2246
|
state,
|
|
2091
2247
|
coordinator: coordinatorState
|
|
2092
2248
|
}),
|
|
2249
|
+
state: snapshot.state.app ?? {},
|
|
2093
2250
|
session: { state: (reducer, readOptions) => ctx.session.state(reducer, {
|
|
2094
2251
|
...readOptions,
|
|
2095
2252
|
through: readOptions?.through ?? snapshot.index
|
|
2096
2253
|
}) },
|
|
2097
2254
|
signal: ctx.signal
|
|
2098
2255
|
};
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
if (request.reason === "tool" && responseStepCount >= maxSteps) {
|
|
2114
|
-
const source = response.source?.generation;
|
|
2115
|
-
if (source === void 0) return;
|
|
2116
|
-
return {
|
|
2117
|
-
type: "ai.generation.failed",
|
|
2118
|
-
id: await aiEventId("a2.ai.step-limit", requestId),
|
|
2119
|
-
payload: {
|
|
2120
|
-
requestId: source.requestId,
|
|
2121
|
-
messageId: source.messageId,
|
|
2122
|
-
generationId: source.generationId,
|
|
2123
|
-
responseMessageId,
|
|
2124
|
-
error: `agent exceeded the ${maxSteps}-step limit`,
|
|
2125
|
-
stepLimit: true
|
|
2126
|
-
}
|
|
2127
|
-
};
|
|
2128
|
-
}
|
|
2129
|
-
const started = {
|
|
2130
|
-
requestId,
|
|
2131
|
-
messageId: request.messageId,
|
|
2132
|
-
generationId,
|
|
2133
|
-
responseMessageId,
|
|
2134
|
-
attempt,
|
|
2135
|
-
model: modelName(resolvedModel),
|
|
2136
|
-
promptThroughIndex: snapshot.index
|
|
2256
|
+
if (request.reason === "tool" && responseStepCount >= maxSteps) {
|
|
2257
|
+
const source = response.source?.generation;
|
|
2258
|
+
if (source === void 0) return;
|
|
2259
|
+
return {
|
|
2260
|
+
type: "ai.generation.failed",
|
|
2261
|
+
id: await aiEventId("a2.ai.step-limit", requestId),
|
|
2262
|
+
payload: {
|
|
2263
|
+
requestId: source.requestId,
|
|
2264
|
+
messageId: source.messageId,
|
|
2265
|
+
generationId: source.generationId,
|
|
2266
|
+
responseMessageId,
|
|
2267
|
+
error: `agent exceeded the ${maxSteps}-step limit`,
|
|
2268
|
+
stepLimit: true
|
|
2269
|
+
}
|
|
2137
2270
|
};
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
const
|
|
2141
|
-
|
|
2142
|
-
const startEvents = [];
|
|
2143
|
-
if (discoversMetadata && !Object.hasOwn(state.modelMetadata, catalogModelId)) startEvents.push({
|
|
2271
|
+
}
|
|
2272
|
+
const startEvents = async (started, metadataModelId) => {
|
|
2273
|
+
const events = [];
|
|
2274
|
+
if (metadataModelId !== void 0) events.push({
|
|
2144
2275
|
type: "ai.model.metadata.requested",
|
|
2145
|
-
id: await aiEventId("a2.ai.model.metadata", options.
|
|
2146
|
-
payload: { modelId:
|
|
2276
|
+
id: await aiEventId("a2.ai.model.metadata", options.contract.name, ctx.event.sessionId, metadataModelId),
|
|
2277
|
+
payload: { modelId: metadataModelId }
|
|
2147
2278
|
});
|
|
2148
2279
|
if (previous) {
|
|
2149
|
-
const payload = previous;
|
|
2150
2280
|
const superseded = {
|
|
2151
2281
|
requestId,
|
|
2152
|
-
messageId:
|
|
2153
|
-
generationId:
|
|
2154
|
-
responseMessageId:
|
|
2282
|
+
messageId: previous.messageId,
|
|
2283
|
+
generationId: previous.generationId,
|
|
2284
|
+
responseMessageId: previous.responseMessageId,
|
|
2155
2285
|
error: "generation attempt was superseded after an incomplete run",
|
|
2156
2286
|
superseded: true
|
|
2157
2287
|
};
|
|
2158
|
-
|
|
2288
|
+
events.push({
|
|
2159
2289
|
type: "ai.generation.failed",
|
|
2160
|
-
id: await aiEventId("a2.ai.superseded",
|
|
2290
|
+
id: await aiEventId("a2.ai.superseded", previous.generationId),
|
|
2161
2291
|
payload: superseded
|
|
2162
2292
|
});
|
|
2163
2293
|
}
|
|
2164
|
-
|
|
2294
|
+
events.push({
|
|
2165
2295
|
type: "ai.generation.started",
|
|
2166
2296
|
id: generationId,
|
|
2167
2297
|
payload: started
|
|
2168
2298
|
});
|
|
2299
|
+
return events;
|
|
2300
|
+
};
|
|
2301
|
+
const startedPayload = (model) => ({
|
|
2302
|
+
requestId,
|
|
2303
|
+
messageId: request.messageId,
|
|
2304
|
+
generationId,
|
|
2305
|
+
responseMessageId,
|
|
2306
|
+
attempt,
|
|
2307
|
+
...model === void 0 ? {} : { model: modelName(model) },
|
|
2308
|
+
promptThroughIndex: snapshot.index
|
|
2309
|
+
});
|
|
2310
|
+
let resolvedModel;
|
|
2311
|
+
let resolving = true;
|
|
2312
|
+
let generationStarted = false;
|
|
2313
|
+
try {
|
|
2314
|
+
const choice = await resolve(options.model, resolverContext);
|
|
2315
|
+
if (choice === void 0) throw new TypeError("the model resolver returned undefined");
|
|
2316
|
+
const settings = typeof choice === "string" || "specificationVersion" in choice ? { model: choice } : choice;
|
|
2317
|
+
const model = settings.model;
|
|
2318
|
+
if (typeof model !== "string" && (typeof model !== "object" || model === null)) throw new TypeError("the model resolver must return a model");
|
|
2319
|
+
resolvedModel = model;
|
|
2320
|
+
if (checkAbort(ctx.signal)) return;
|
|
2321
|
+
const resolvedInstructions = options.instructions === void 0 ? void 0 : await resolve(options.instructions, resolverContext);
|
|
2322
|
+
if (checkAbort(ctx.signal)) return;
|
|
2323
|
+
const resolvedGeneration = await resolve(options.generation ?? {}, resolverContext);
|
|
2324
|
+
if (checkAbort(ctx.signal)) return;
|
|
2325
|
+
if (resolvedGeneration === null || typeof resolvedGeneration !== "object" || Array.isArray(resolvedGeneration)) throw new TypeError("the generation resolver must return settings");
|
|
2326
|
+
const generation = withModelSettings({
|
|
2327
|
+
generation: resolvedGeneration,
|
|
2328
|
+
settings
|
|
2329
|
+
});
|
|
2330
|
+
const compaction = typeof configuredCompaction === "function" || typeof options.generation === "function" ? validateCompaction({
|
|
2331
|
+
compaction: typeof configuredCompaction === "function" ? await resolve(configuredCompaction, resolverContext) : staticCompaction,
|
|
2332
|
+
explicit: options.compaction !== void 0,
|
|
2333
|
+
generation,
|
|
2334
|
+
tools: options.tools
|
|
2335
|
+
}) : staticCompaction;
|
|
2336
|
+
if (checkAbort(ctx.signal)) return;
|
|
2337
|
+
resolving = false;
|
|
2338
|
+
const catalogModelId = gatewayModelId(model);
|
|
2339
|
+
const gatewayOptions = generation.providerOptions?.["gateway"];
|
|
2340
|
+
const usesFallbackModels = Array.isArray(gatewayOptions?.["models"]) && gatewayOptions["models"].length > 0;
|
|
2341
|
+
const discoversMetadata = compaction !== false && !("shouldCompact" in compaction) && compaction.thresholdTokens === void 0 && !usesFallbackModels && catalogModelId !== void 0;
|
|
2169
2342
|
const generationEvents = await control.append({
|
|
2170
2343
|
ctx,
|
|
2171
2344
|
name: "generation-start",
|
|
2172
|
-
events: startEvents
|
|
2345
|
+
events: await startEvents(startedPayload(model), discoversMetadata && !Object.hasOwn(state.modelMetadata, catalogModelId) ? catalogModelId : void 0)
|
|
2173
2346
|
});
|
|
2347
|
+
const startIndex = generationEvents.find((event) => event.id === generationId).index;
|
|
2174
2348
|
generationStarted = true;
|
|
2175
2349
|
if (checkAbort(ctx.signal)) return;
|
|
2176
2350
|
const promptCoordinatorState = coordinatorState;
|
|
2177
|
-
const messages =
|
|
2178
|
-
state,
|
|
2179
|
-
coordinator: promptCoordinatorState
|
|
2180
|
-
});
|
|
2351
|
+
const { messages } = resolverContext;
|
|
2181
2352
|
let generationMessages = messages;
|
|
2182
|
-
let modelMessages = await
|
|
2353
|
+
let modelMessages = await telemetry.span("a2.ai.context", {
|
|
2354
|
+
"a2.contract": options.contract.name,
|
|
2355
|
+
"a2.session_id": ctx.event.sessionId,
|
|
2356
|
+
"a2.ai.context.request_id": requestId,
|
|
2357
|
+
"a2.ai.context.phase": "generation"
|
|
2358
|
+
}, () => modelContext({
|
|
2183
2359
|
messages,
|
|
2184
2360
|
state,
|
|
2185
2361
|
tools
|
|
2186
|
-
});
|
|
2362
|
+
}));
|
|
2187
2363
|
const baseContext = {
|
|
2188
|
-
|
|
2189
|
-
requestId,
|
|
2364
|
+
...resolverContext,
|
|
2190
2365
|
generationId,
|
|
2191
|
-
responseMessageId,
|
|
2192
|
-
messages,
|
|
2193
2366
|
modelMessages,
|
|
2194
|
-
session: resolverContext.session,
|
|
2195
|
-
signal: ctx.signal,
|
|
2196
2367
|
model: resolvedModel,
|
|
2197
2368
|
tools,
|
|
2198
2369
|
...resolvedInstructions === void 0 ? {} : { instructions: resolvedInstructions },
|
|
@@ -2257,14 +2428,20 @@ function createHandlers(options) {
|
|
|
2257
2428
|
}
|
|
2258
2429
|
}]
|
|
2259
2430
|
}));
|
|
2260
|
-
const result =
|
|
2431
|
+
const result = await telemetry.span("a2.ai.compaction", {
|
|
2432
|
+
"a2.contract": options.contract.name,
|
|
2433
|
+
"a2.session_id": ctx.event.sessionId,
|
|
2434
|
+
"a2.ai.compaction.request_id": requestId,
|
|
2435
|
+
"a2.ai.compaction.generation_id": generationId,
|
|
2436
|
+
"a2.ai.compaction.policy": "shouldCompact" in policy ? "custom" : "automatic"
|
|
2437
|
+
}, async () => !("shouldCompact" in policy) ? {
|
|
2261
2438
|
messages: [],
|
|
2262
2439
|
...await summarize({
|
|
2263
|
-
agentName: options.
|
|
2440
|
+
agentName: options.contract.name,
|
|
2264
2441
|
context: baseContext,
|
|
2265
2442
|
instructions: policy.instructions
|
|
2266
2443
|
})
|
|
2267
|
-
} : { messages: await policy.compact(compactionContext) };
|
|
2444
|
+
} : { messages: await policy.compact(compactionContext) });
|
|
2268
2445
|
if (checkAbort(ctx.signal)) return;
|
|
2269
2446
|
const completed = {
|
|
2270
2447
|
generationId,
|
|
@@ -2288,7 +2465,7 @@ function createHandlers(options) {
|
|
|
2288
2465
|
}
|
|
2289
2466
|
}
|
|
2290
2467
|
const currentState = foldAIEvents({
|
|
2291
|
-
reducer: contextReducer
|
|
2468
|
+
reducer: contextReducer,
|
|
2292
2469
|
events: generationEvents,
|
|
2293
2470
|
state
|
|
2294
2471
|
});
|
|
@@ -2296,12 +2473,21 @@ function createHandlers(options) {
|
|
|
2296
2473
|
state: currentState,
|
|
2297
2474
|
coordinator: promptCoordinatorState
|
|
2298
2475
|
});
|
|
2299
|
-
if (policy && "shouldCompact" in policy || generationMessages !== messages || activeCompaction(currentState)?.summary !== activeCompaction(state)?.summary) modelMessages = await
|
|
2476
|
+
if (policy && "shouldCompact" in policy || generationMessages !== messages || activeCompaction(currentState)?.summary !== activeCompaction(state)?.summary) modelMessages = await telemetry.span("a2.ai.context", {
|
|
2477
|
+
"a2.contract": options.contract.name,
|
|
2478
|
+
"a2.session_id": ctx.event.sessionId,
|
|
2479
|
+
"a2.ai.context.request_id": requestId,
|
|
2480
|
+
"a2.ai.context.phase": "refresh"
|
|
2481
|
+
}, () => modelContext({
|
|
2300
2482
|
messages: generationMessages,
|
|
2301
2483
|
state: currentState,
|
|
2302
2484
|
tools
|
|
2485
|
+
}));
|
|
2486
|
+
cachePrompt({
|
|
2487
|
+
sessionId: ctx.event.sessionId,
|
|
2488
|
+
generationId,
|
|
2489
|
+
messages: Promise.resolve(modelMessages)
|
|
2303
2490
|
});
|
|
2304
|
-
promptCache.set(promptCacheKey(ctx.event.sessionId, generationId), Promise.resolve(modelMessages));
|
|
2305
2491
|
if (tracksInput && (inputTokenEstimate === void 0 || compacted)) inputTokenEstimate = await estimateInputTokens({
|
|
2306
2492
|
messages: modelMessages,
|
|
2307
2493
|
instructions: resolvedInstructions,
|
|
@@ -2310,23 +2496,14 @@ function createHandlers(options) {
|
|
|
2310
2496
|
let pendingToolCalls = [];
|
|
2311
2497
|
if (checkAbort(ctx.signal)) return;
|
|
2312
2498
|
const generateContext = {
|
|
2313
|
-
|
|
2314
|
-
requestId,
|
|
2315
|
-
generationId,
|
|
2316
|
-
responseMessageId,
|
|
2499
|
+
...baseContext,
|
|
2317
2500
|
messages: generationMessages,
|
|
2318
|
-
modelMessages
|
|
2319
|
-
session: resolverContext.session,
|
|
2320
|
-
signal: ctx.signal,
|
|
2321
|
-
model: resolvedModel,
|
|
2322
|
-
tools,
|
|
2323
|
-
...resolvedInstructions === void 0 ? {} : { instructions: resolvedInstructions },
|
|
2324
|
-
generation: options.generation ?? {}
|
|
2501
|
+
modelMessages
|
|
2325
2502
|
};
|
|
2326
2503
|
const custom = options.generate !== void 0;
|
|
2327
2504
|
const updates = consumeGeneration({
|
|
2328
2505
|
source: options.generate === void 0 ? await generateWithAISDK({
|
|
2329
|
-
agentName: options.
|
|
2506
|
+
agentName: options.contract.name,
|
|
2330
2507
|
context: generateContext,
|
|
2331
2508
|
messageMetadata: options.messageMetadata
|
|
2332
2509
|
}) : { stream: await options.generate(generateContext) },
|
|
@@ -2341,11 +2518,7 @@ function createHandlers(options) {
|
|
|
2341
2518
|
continue;
|
|
2342
2519
|
}
|
|
2343
2520
|
const progress = {
|
|
2344
|
-
|
|
2345
|
-
messageId: request.messageId,
|
|
2346
|
-
generationId,
|
|
2347
|
-
responseMessageId,
|
|
2348
|
-
sequence,
|
|
2521
|
+
startIndex,
|
|
2349
2522
|
chunks: update.chunks
|
|
2350
2523
|
};
|
|
2351
2524
|
const lifecycle = await lifecycleEvents({
|
|
@@ -2390,6 +2563,7 @@ function createHandlers(options) {
|
|
|
2390
2563
|
if (unresolved.length > 0) throw new Error(`generation ended with unresolved tool authorization for '${unresolved[0].call.toolName}'`);
|
|
2391
2564
|
const pendingEvents = await Promise.all(pendingToolCalls.filter((pending) => pending.classification === "automatic" || options.generate === void 0 && pending.classification === "unknown").map((pending) => toolCalledEvent(pending.call)));
|
|
2392
2565
|
if (finish.finishReason === "tool-calls") return [...pendingEvents, completionEvent];
|
|
2566
|
+
forgetPrompt(ctx.event.sessionId, generationId);
|
|
2393
2567
|
return [
|
|
2394
2568
|
...pendingEvents,
|
|
2395
2569
|
completionEvent,
|
|
@@ -2406,7 +2580,13 @@ function createHandlers(options) {
|
|
|
2406
2580
|
throw error;
|
|
2407
2581
|
}
|
|
2408
2582
|
if (checkAbort(ctx.signal)) return;
|
|
2409
|
-
if (!generationStarted) throw error;
|
|
2583
|
+
if (!generationStarted && !resolving) throw error;
|
|
2584
|
+
if (!generationStarted) await control.append({
|
|
2585
|
+
ctx,
|
|
2586
|
+
name: "generation-start",
|
|
2587
|
+
events: await startEvents(startedPayload(resolvedModel))
|
|
2588
|
+
});
|
|
2589
|
+
forgetPrompt(ctx.event.sessionId, generationId);
|
|
2410
2590
|
const failed = {
|
|
2411
2591
|
requestId,
|
|
2412
2592
|
messageId: request.messageId,
|
|
@@ -2421,10 +2601,6 @@ function createHandlers(options) {
|
|
|
2421
2601
|
};
|
|
2422
2602
|
}
|
|
2423
2603
|
};
|
|
2424
|
-
const clearPromptCache = (sessionId) => {
|
|
2425
|
-
const prefix = `${sessionId}\u001f`;
|
|
2426
|
-
for (const key of promptCache.keys()) if (key.startsWith(prefix)) promptCache.delete(key);
|
|
2427
|
-
};
|
|
2428
2604
|
return {
|
|
2429
2605
|
"ai.model.metadata.requested": { handler: async (ctx) => {
|
|
2430
2606
|
const limits = await readModelLimits({
|
|
@@ -2449,39 +2625,32 @@ function createHandlers(options) {
|
|
|
2449
2625
|
if ((command.action === "send" || command.action === "steer") && event.id !== command.message.id) throw new A2Error("INVALID_PAYLOAD", "AI input event ID must equal message.id");
|
|
2450
2626
|
return "a2.ai.control";
|
|
2451
2627
|
},
|
|
2452
|
-
handler:
|
|
2628
|
+
handler: controlHandler
|
|
2453
2629
|
},
|
|
2454
2630
|
"ai.work.reported": {
|
|
2455
2631
|
lane: "a2.ai.control",
|
|
2456
|
-
handler:
|
|
2632
|
+
handler: controlHandler
|
|
2457
2633
|
},
|
|
2458
|
-
"ai.message.completed": { handler: async (ctx) => {
|
|
2459
|
-
clearPromptCache(ctx.event.sessionId);
|
|
2460
|
-
} },
|
|
2461
|
-
"ai.message.interrupted": { handler: async (ctx) => {
|
|
2462
|
-
clearPromptCache(ctx.event.sessionId);
|
|
2463
|
-
} },
|
|
2464
2634
|
"ai.session.closed": {
|
|
2465
2635
|
lane: "a2.ai.control",
|
|
2466
|
-
handler:
|
|
2467
|
-
clearPromptCache(ctx.event.sessionId);
|
|
2468
|
-
return control.handler(ctx);
|
|
2469
|
-
}
|
|
2636
|
+
handler: controlHandler
|
|
2470
2637
|
},
|
|
2471
|
-
"ai.generation.failed": { handler: (ctx) => {
|
|
2472
|
-
promptCache.delete(promptCacheKey(ctx.event.sessionId, ctx.event.payload.generationId));
|
|
2473
|
-
return Promise.resolve();
|
|
2474
|
-
} },
|
|
2475
2638
|
"ai.generation.requested": {
|
|
2476
2639
|
lane: "a2.ai.model",
|
|
2477
2640
|
abortOn: {
|
|
2641
|
+
"ai.control.requested": (event, trigger) => event.payload.action === "stop" && event.payload.turnId === trigger.payload.control?.turnId,
|
|
2478
2642
|
"ai.control.committed": (event, trigger) => {
|
|
2479
2643
|
const active = event.payload.view;
|
|
2480
2644
|
return active?.turnId !== trigger.payload.control?.turnId || active?.version !== trigger.payload.control?.version;
|
|
2481
2645
|
},
|
|
2482
2646
|
"ai.session.closed": true
|
|
2483
2647
|
},
|
|
2484
|
-
handler:
|
|
2648
|
+
handler: (ctx) => telemetry.span("a2.ai.generate", {
|
|
2649
|
+
"a2.contract": options.contract.name,
|
|
2650
|
+
"a2.session_id": ctx.event.sessionId,
|
|
2651
|
+
"a2.ai.generate.request_id": ctx.event.id,
|
|
2652
|
+
"a2.ai.generate.attempt": ctx.attempt
|
|
2653
|
+
}, async () => {
|
|
2485
2654
|
try {
|
|
2486
2655
|
return control.settled({
|
|
2487
2656
|
ctx,
|
|
@@ -2497,10 +2666,12 @@ function createHandlers(options) {
|
|
|
2497
2666
|
});
|
|
2498
2667
|
throw error;
|
|
2499
2668
|
}
|
|
2500
|
-
}
|
|
2669
|
+
})
|
|
2501
2670
|
},
|
|
2502
2671
|
"ai.tool.execution.requested": {
|
|
2672
|
+
...options.toolLane === void 0 ? {} : { lane: options.toolLane },
|
|
2503
2673
|
abortOn: {
|
|
2674
|
+
"ai.control.requested": (event, trigger) => event.payload.action === "stop" && event.payload.turnId === trigger.payload.turnId,
|
|
2504
2675
|
"ai.generation.failed": (event, trigger) => event.payload.generationId === trigger.payload.call.generationId,
|
|
2505
2676
|
"ai.control.committed": (event, trigger) => {
|
|
2506
2677
|
const active = event.payload.view;
|
|
@@ -2508,16 +2679,40 @@ function createHandlers(options) {
|
|
|
2508
2679
|
},
|
|
2509
2680
|
"ai.session.closed": true
|
|
2510
2681
|
},
|
|
2511
|
-
handler:
|
|
2682
|
+
handler: (ctx) => telemetry.span("a2.ai.tool", {
|
|
2683
|
+
"a2.contract": options.contract.name,
|
|
2684
|
+
"a2.session_id": ctx.event.sessionId,
|
|
2685
|
+
"a2.ai.tool.request_id": ctx.event.payload.call.requestId,
|
|
2686
|
+
"a2.ai.tool.call_id": ctx.event.payload.call.toolCallId,
|
|
2687
|
+
"a2.ai.tool.name": ctx.event.payload.call.toolName,
|
|
2688
|
+
"a2.ai.tool.attempt": ctx.attempt
|
|
2689
|
+
}, () => handleToolExecution(ctx))
|
|
2512
2690
|
}
|
|
2513
2691
|
};
|
|
2514
2692
|
}
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
|
|
2693
|
+
function agent(options) {
|
|
2694
|
+
const { model, tools, instructions, generation, maxSteps, messageMetadata, generate, compaction, progress, toolLane, handlers, ...rest } = options;
|
|
2695
|
+
let contract;
|
|
2696
|
+
let serverOptions;
|
|
2697
|
+
if ("contract" in rest) {
|
|
2698
|
+
const { contract: shared, ...remaining } = rest;
|
|
2699
|
+
if ("name" in remaining) throw new TypeError("agent takes either name or contract, not both");
|
|
2700
|
+
if (!isAgentContract(shared)) throw new TypeError("agent({ contract }) takes an agentContract()");
|
|
2701
|
+
contract = shared;
|
|
2702
|
+
serverOptions = remaining;
|
|
2703
|
+
} else {
|
|
2704
|
+
const { name, events, messageSchema, ...remaining } = rest;
|
|
2705
|
+
contract = agentContract({
|
|
2706
|
+
name,
|
|
2707
|
+
...events === void 0 ? {} : { events },
|
|
2708
|
+
...messageSchema === void 0 ? {} : { messageSchema }
|
|
2709
|
+
});
|
|
2710
|
+
serverOptions = remaining;
|
|
2711
|
+
}
|
|
2518
2712
|
const builtIns = createHandlers({
|
|
2519
|
-
|
|
2713
|
+
contract,
|
|
2520
2714
|
model,
|
|
2715
|
+
...serverOptions.telemetry === void 0 ? {} : { telemetry: serverOptions.telemetry },
|
|
2521
2716
|
...tools === void 0 ? {} : { tools },
|
|
2522
2717
|
...instructions === void 0 ? {} : { instructions },
|
|
2523
2718
|
...generation === void 0 ? {} : { generation },
|
|
@@ -2525,20 +2720,26 @@ function createAgentServer(options) {
|
|
|
2525
2720
|
...messageMetadata === void 0 ? {} : { messageMetadata },
|
|
2526
2721
|
...generate === void 0 ? {} : { generate },
|
|
2527
2722
|
...compaction === void 0 ? {} : { compaction },
|
|
2528
|
-
...progress === void 0 ? {} : { progress }
|
|
2723
|
+
...progress === void 0 ? {} : { progress },
|
|
2724
|
+
...toolLane === void 0 ? {} : { toolLane }
|
|
2529
2725
|
});
|
|
2726
|
+
for (const type of Object.keys(handlers ?? {})) if (Object.hasOwn(builtIns, type)) throw new TypeError(`agent handles '${type}' itself`);
|
|
2530
2727
|
const server = createServer({
|
|
2531
2728
|
...serverOptions,
|
|
2532
|
-
contract
|
|
2729
|
+
contract,
|
|
2533
2730
|
handlers: {
|
|
2534
2731
|
...builtIns,
|
|
2535
2732
|
...handlers
|
|
2536
2733
|
}
|
|
2537
2734
|
});
|
|
2538
|
-
|
|
2735
|
+
const applicationEvents = "contract" in rest;
|
|
2736
|
+
setServerFetchHooks(server, { validateIngress: (context) => validateAgentIngress({
|
|
2737
|
+
context,
|
|
2738
|
+
applicationEvents
|
|
2739
|
+
}) });
|
|
2539
2740
|
return server;
|
|
2540
2741
|
}
|
|
2541
2742
|
//#endregion
|
|
2542
|
-
export {
|
|
2743
|
+
export { agent, runWithHandlerContext };
|
|
2543
2744
|
|
|
2544
2745
|
//# sourceMappingURL=ai-server.js.map
|