experimental-a2 0.16.1 → 0.18.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 +373 -0
- package/README.md +1 -1
- package/dist/{actor-ohPC-81x.d.ts → actor-Bjce9_5U.d.ts} +21 -11
- package/dist/actor-Bjce9_5U.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-DgOBltJ_.js → ai-BpOGm-zB.js} +621 -216
- package/dist/ai-BpOGm-zB.js.map +1 -0
- package/dist/{ai-CrEf6p_W.d.ts → ai-DrUaMyRt.d.ts} +114 -100
- package/dist/ai-DrUaMyRt.d.ts.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-fx.browser.d.ts +1 -0
- package/dist/ai-fx.browser.js +6 -0
- package/dist/ai-fx.browser.js.map +1 -0
- package/dist/ai-fx.d.ts +10 -0
- package/dist/ai-fx.d.ts.map +1 -0
- package/dist/ai-fx.js +634 -0
- package/dist/ai-fx.js.map +1 -0
- package/dist/ai-harness-BuLh_EYi.js +273 -0
- package/dist/ai-harness-BuLh_EYi.js.map +1 -0
- package/dist/ai-harness-DvsPeJeB.d.ts +26 -0
- package/dist/ai-harness-DvsPeJeB.d.ts.map +1 -0
- package/dist/ai-pi.browser.d.ts +1 -0
- package/dist/ai-pi.browser.js +6 -0
- package/dist/ai-pi.browser.js.map +1 -0
- package/dist/ai-pi.d.ts +17 -0
- package/dist/ai-pi.d.ts.map +1 -0
- package/dist/ai-pi.js +904 -0
- package/dist/ai-pi.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 +109 -43
- package/dist/ai-server.d.ts.map +1 -1
- package/dist/ai-server.js +526 -283
- package/dist/ai-server.js.map +1 -1
- package/dist/ai.d.ts +2 -2
- package/dist/ai.js +2 -2
- package/dist/bounded-cache-CPHWF-8h.js +41 -0
- package/dist/bounded-cache-CPHWF-8h.js.map +1 -0
- package/dist/{client-BYzHjkwU.js → client-BReKqwZC.js} +447 -80
- package/dist/client-BReKqwZC.js.map +1 -0
- package/dist/{client-CzyacQpJ.d.ts → client-h_DKFAjj.d.ts} +14 -13
- package/dist/client-h_DKFAjj.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-Bx-c0pDP.d.ts} +7 -3
- package/dist/initial-snapshot-Bx-c0pDP.d.ts.map +1 -0
- package/dist/{internal-Dq2qYxou.js → internal-B2T3U4Rq.js} +29 -2
- package/dist/internal-B2T3U4Rq.js.map +1 -0
- package/dist/otel.d.ts +1 -1
- package/dist/react-BBXgHWFc.d.ts +86 -0
- package/dist/react-BBXgHWFc.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-CqA59Q6U.d.ts} +46 -30
- package/dist/reducer-CqA59Q6U.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-BD5ckxZb.js → server-Dw_k_7kO.js} +904 -219
- package/dist/server-Dw_k_7kO.js.map +1 -0
- package/dist/{server-DbD7IJVK.d.ts → server-En7cbi3v.d.ts} +116 -26
- package/dist/server-En7cbi3v.d.ts.map +1 -0
- package/dist/server.d.ts +4 -4
- package/dist/server.js +2 -2
- package/dist/session-clear-Bo-XTFEy.d.ts +12 -0
- package/dist/session-clear-Bo-XTFEy.d.ts.map +1 -0
- package/dist/{errors-DCk6ch5n.js → session-clear-DGI4jTQ5.js} +17 -2
- package/dist/session-clear-DGI4jTQ5.js.map +1 -0
- package/dist/{store-DyZM6fS5.d.ts → store-Djdv0bnZ.d.ts} +73 -3
- package/dist/store-Djdv0bnZ.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 +205 -66
- package/dist/store-memory.js.map +1 -1
- package/dist/{store-polling-CmxUbV93.js → store-polling-B5FcwJfo.js} +21 -4
- package/dist/store-polling-B5FcwJfo.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 +450 -108
- package/dist/store-postgres.js.map +1 -1
- package/dist/{store-presence-polling-C7-XZyW9.js → store-presence-polling-BAwS8RWn.js} +3 -3
- package/dist/{store-presence-polling-C7-XZyW9.js.map → store-presence-polling-BAwS8RWn.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-BRITti_o.js +2493 -0
- package/dist/store-redis-notify-BRITti_o.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 +197 -33
- 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-Cyfqo6Qt.d.ts} +2 -2
- package/dist/telemetry-Cyfqo6Qt.d.ts.map +1 -0
- package/dist/{wire-BO5wWCb1.js → wire-rSUglYo8.js} +132 -10
- package/dist/wire-rSUglYo8.js.map +1 -0
- package/docs/01-quickstart.mdx +50 -84
- package/docs/{guides/07-examples.mdx → 02-examples.mdx} +10 -20
- package/docs/actors/01-introduction.mdx +48 -82
- package/docs/actors/02-concurrency.mdx +43 -70
- package/docs/actors/03-timers.mdx +28 -42
- package/docs/actors/04-routes.mdx +110 -137
- package/docs/actors/meta.ts +1 -1
- package/docs/agents/01-introduction.mdx +257 -0
- package/docs/agents/02-tools.mdx +247 -0
- package/docs/agents/03-conversations.mdx +538 -0
- package/docs/agents/04-harnesses.mdx +136 -0
- package/docs/agents/meta.ts +1 -0
- package/docs/client/01-react.mdx +294 -0
- package/docs/client/02-transports.mdx +86 -0
- package/docs/client/03-presence.mdx +224 -0
- package/docs/client/meta.ts +1 -0
- package/docs/contract/01-events.mdx +57 -0
- package/docs/contract/02-state.mdx +142 -0
- package/docs/contract/03-sessions.mdx +175 -0
- package/docs/index.mdx +112 -156
- package/docs/reference/01-core.mdx +139 -0
- package/docs/reference/02-server.mdx +855 -0
- package/docs/reference/03-client.mdx +301 -0
- package/docs/reference/04-ai.mdx +722 -0
- package/docs/reference/05-adapters.mdx +351 -0
- package/docs/reference/06-errors.mdx +138 -0
- package/docs/reference/meta.ts +1 -1
- package/docs/server/01-handlers.mdx +200 -0
- package/docs/server/02-timers.mdx +130 -0
- package/docs/server/03-cancellation.mdx +141 -0
- package/docs/server/04-routes.mdx +243 -0
- package/docs/server/05-production.mdx +322 -0
- package/docs/server/06-guarantees.mdx +123 -0
- package/examples/playground/DEPLOY.md +53 -7
- package/examples/playground/app/a0/[appId]/loading.tsx +16 -0
- package/examples/playground/app/a0/[appId]/page.tsx +53 -0
- package/examples/playground/app/a0/a0.css +818 -0
- package/examples/playground/app/a0/app-list.tsx +218 -0
- package/examples/playground/app/a0/app-view.tsx +63 -0
- package/examples/playground/app/a0/apps/[appId]/preview/route.ts +27 -0
- package/examples/playground/app/a0/apps/[appId]/route.ts +28 -0
- package/examples/playground/app/a0/apps.test.ts +128 -0
- package/examples/playground/app/a0/apps.ts +102 -0
- package/examples/playground/app/a0/chat.tsx +410 -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/fx/events/route.ts +9 -0
- package/examples/playground/app/a0/fx/scheduler/route.ts +7 -0
- package/examples/playground/app/a0/fx/short/events/route.ts +11 -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 +73 -0
- package/examples/playground/app/a0/model.ts +95 -0
- package/examples/playground/app/a0/no-photon.cjs +4 -0
- package/examples/playground/app/a0/no-quickjs.cjs +3 -0
- package/examples/playground/app/a0/page.tsx +14 -0
- package/examples/playground/app/a0/pi/events/route.ts +9 -0
- package/examples/playground/app/a0/pi/scheduler/route.ts +7 -0
- package/examples/playground/app/a0/pi/short/events/route.ts +11 -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 +75 -0
- package/examples/playground/app/a0/sandbox-session.ts +152 -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 +213 -0
- package/examples/playground/app/a0/session.ts +59 -0
- package/examples/playground/app/a0/shell.tsx +158 -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 +101 -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 +19 -15
- package/examples/playground/app/agent/session.ts +5 -6
- package/examples/playground/app/agent/telemetry.test.ts +189 -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 +12 -12
- 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 +112 -0
- package/examples/playground/app/chat/server.ts +113 -117
- 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/next.config.ts +17 -5
- package/examples/playground/package.json +13 -2
- package/examples/playground/scripts/a0-template.ts +77 -0
- package/examples/playground/vercel.json +36 -0
- package/package.json +29 -3
- 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 +2517 -0
- package/src/ai-app-state.ts +91 -0
- package/src/ai-client-state.ts +6 -3
- 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 +92 -72
- package/src/ai-coordinator.ts +23 -22
- package/src/ai-fx.browser.ts +5 -0
- package/src/ai-fx.ts +833 -0
- package/src/ai-harness.ts +518 -0
- package/src/ai-message-base.ts +209 -0
- package/src/ai-message-projection.ts +262 -64
- package/src/ai-model-metadata.ts +11 -6
- package/src/ai-pi.browser.ts +5 -0
- package/src/ai-pi.ts +1263 -0
- 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-sdk-step.ts +2 -2
- package/src/ai-server.browser.ts +2 -2
- package/src/ai-server.ts +23 -1965
- package/src/ai-stored-state.ts +48 -163
- package/src/ai.ts +339 -155
- 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 +52 -0
- package/src/libfx.d.ts +34 -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 +93 -11
- 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 +1133 -255
- 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 +252 -75
- package/src/store-polling.ts +18 -4
- package/src/store-postgres.ts +541 -114
- package/src/store-redis-core.ts +1450 -493
- package/src/store-redis-http.ts +18 -2
- package/src/store-redis-notify.ts +262 -85
- package/src/store-redis.ts +19 -9
- package/src/store-sqlite.ts +319 -45
- package/src/store.ts +79 -1
- package/src/stream-buffer.ts +43 -0
- package/src/telemetry.ts +14 -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-CrEf6p_W.d.ts.map +0 -1
- package/dist/ai-DgOBltJ_.js.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/docs/concepts/01-contracts.mdx +0 -142
- package/docs/concepts/02-handlers.mdx +0 -284
- package/docs/concepts/03-durability.mdx +0 -314
- package/docs/concepts/04-state.mdx +0 -209
- package/docs/guides/01-timers.mdx +0 -185
- package/docs/guides/02-cancellation.mdx +0 -161
- package/docs/guides/03-react.mdx +0 -384
- package/docs/guides/04-local-first.mdx +0 -88
- package/docs/guides/05-production.mdx +0 -521
- package/docs/guides/06-ai-agents.mdx +0 -1318
- package/docs/guides/08-application-data.mdx +0 -113
- package/docs/guides/09-presence.mdx +0 -266
- package/docs/guides/10-transports.mdx +0 -221
- package/docs/reference/01-api.mdx +0 -1896
- package/docs/reference/02-errors.mdx +0 -149
- 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/docs/{concepts → contract}/meta.ts +0 -0
- /package/docs/{guides → server}/meta.ts +0 -0
|
@@ -0,0 +1,518 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
Experimental_SandboxSession,
|
|
3
|
+
FinishReason,
|
|
4
|
+
LanguageModelUsage,
|
|
5
|
+
ToolSet,
|
|
6
|
+
UIMessage,
|
|
7
|
+
UIMessageChunk,
|
|
8
|
+
} from 'ai'
|
|
9
|
+
import type {
|
|
10
|
+
GenerationCompletedPayload,
|
|
11
|
+
ReasoningLevel,
|
|
12
|
+
ToolResultPayload,
|
|
13
|
+
} from './ai.ts'
|
|
14
|
+
import { boundedCache, type BoundedCache } from './bounded-cache.ts'
|
|
15
|
+
import type { ContractEvent, EventDefs } from './contract.ts'
|
|
16
|
+
|
|
17
|
+
declare const harnessBrand: unique symbol
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* A coding agent's own loop for `agent({ harness })`, from `fx()` or `pi()`.
|
|
21
|
+
* `Tools` names the tools the harness brings, which `toolRecovery` may name.
|
|
22
|
+
*/
|
|
23
|
+
export type Harness<
|
|
24
|
+
Type extends 'fx' | 'pi' = 'fx' | 'pi',
|
|
25
|
+
Tools extends string = string,
|
|
26
|
+
> = {
|
|
27
|
+
readonly type: Type
|
|
28
|
+
readonly [harnessBrand]: { readonly tools: Tools }
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** What a harness's sandbox resolver receives for one step or tool execution. */
|
|
32
|
+
export type HarnessSandboxContext = {
|
|
33
|
+
session: { readonly id: string }
|
|
34
|
+
signal: AbortSignal
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** Resolves the AI SDK sandbox session for an agent session, the same one every time. */
|
|
38
|
+
export type HarnessSandbox = (
|
|
39
|
+
context: HarnessSandboxContext,
|
|
40
|
+
) => Experimental_SandboxSession | Promise<Experimental_SandboxSession>
|
|
41
|
+
|
|
42
|
+
/** What a harness step streams, as the default generator does. */
|
|
43
|
+
export type HarnessGeneration = {
|
|
44
|
+
stream: ReadableStream<UIMessageChunk>
|
|
45
|
+
completion: () => { finishReason?: FinishReason; usage?: LanguageModelUsage }
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** One stored snapshot and the log after it, read in one store operation. */
|
|
49
|
+
export type HarnessLogRead = {
|
|
50
|
+
snapshot: { index: number; state: unknown } | null
|
|
51
|
+
events: ContractEvent<EventDefs>[]
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** The session log as a harness step reads it, plus the state saved in it. */
|
|
55
|
+
export type HarnessLog = {
|
|
56
|
+
/** The latest saved state under `name` and the log after it; `fresh` reads it all. */
|
|
57
|
+
read(options: {
|
|
58
|
+
name: string
|
|
59
|
+
throughIndex: number
|
|
60
|
+
fresh?: boolean
|
|
61
|
+
}): Promise<HarnessLogRead>
|
|
62
|
+
write(options: { name: string; index: number; state: unknown }): Promise<void>
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** Everything one durable generation hands its harness. */
|
|
66
|
+
export type HarnessStep = {
|
|
67
|
+
agentName: string
|
|
68
|
+
sessionId: string
|
|
69
|
+
requestIndex: number
|
|
70
|
+
/** This attempt at the generation. */
|
|
71
|
+
generationId: string
|
|
72
|
+
responseMessageId: string
|
|
73
|
+
/** The log index this step's context was read through. */
|
|
74
|
+
throughIndex: number
|
|
75
|
+
/** The transcript through that index. */
|
|
76
|
+
messages: readonly UIMessage[]
|
|
77
|
+
model: string
|
|
78
|
+
reasoning?: ReasoningLevel
|
|
79
|
+
speed?: 'fast' | 'standard'
|
|
80
|
+
instructions: string[]
|
|
81
|
+
tools: ToolSet
|
|
82
|
+
log: HarnessLog
|
|
83
|
+
signal: AbortSignal
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/** The internal side of a harness value. */
|
|
87
|
+
export type HarnessDriver = {
|
|
88
|
+
/** The session's sandbox; every local tool execution receives it. */
|
|
89
|
+
readonly sandbox?: HarnessSandbox
|
|
90
|
+
/** Tools the harness brings; A2 executes them like the agent's own. */
|
|
91
|
+
readonly tools: ToolSet
|
|
92
|
+
step(step: HarnessStep): Promise<HarnessGeneration>
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// A global symbol, so harness values work across separately bundled copies.
|
|
96
|
+
const DRIVER = Symbol.for('experimental-a2.ai.harness')
|
|
97
|
+
|
|
98
|
+
export function defineHarness<
|
|
99
|
+
Type extends 'fx' | 'pi',
|
|
100
|
+
Tools extends string = never,
|
|
101
|
+
>(type: Type, driver: HarnessDriver): Harness<Type, Tools> {
|
|
102
|
+
const harness = { type }
|
|
103
|
+
Object.defineProperty(harness, DRIVER, { value: driver })
|
|
104
|
+
return Object.freeze(harness) as unknown as Harness<Type, Tools>
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export function harnessDriver(harness: unknown): HarnessDriver {
|
|
108
|
+
const driver =
|
|
109
|
+
typeof harness === 'object' && harness !== null
|
|
110
|
+
? (harness as { [DRIVER]?: HarnessDriver })[DRIVER]
|
|
111
|
+
: undefined
|
|
112
|
+
if (driver === undefined)
|
|
113
|
+
throw new TypeError(
|
|
114
|
+
"harness must be { type: 'ai-sdk' }, or come from fx() or pi()",
|
|
115
|
+
)
|
|
116
|
+
return driver
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// ------------------------------------------------------- saved state
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* What an agent's steps saved in this process at the end of a response, per
|
|
123
|
+
* session and harness. A cache: a cold process rebuilds from the whole log.
|
|
124
|
+
*/
|
|
125
|
+
export type HarnessStates = BoundedCache<
|
|
126
|
+
string,
|
|
127
|
+
{ index: number; state: unknown }
|
|
128
|
+
>
|
|
129
|
+
|
|
130
|
+
export const harnessStates = (): HarnessStates =>
|
|
131
|
+
boundedCache({ maxEntries: 1_000, maxBytes: 64 * 1024 * 1024 })
|
|
132
|
+
|
|
133
|
+
const savedKey = (sessionId: string, name: string): string =>
|
|
134
|
+
`${sessionId}\u0000${name}`
|
|
135
|
+
|
|
136
|
+
// Binary state counts its bytes; everything else its JSON text.
|
|
137
|
+
const sizeOf = (value: unknown): number => {
|
|
138
|
+
let binary = 0
|
|
139
|
+
const text = JSON.stringify(value, (_key, item: unknown) => {
|
|
140
|
+
if (!(item instanceof Uint8Array)) return item
|
|
141
|
+
binary += item.byteLength
|
|
142
|
+
return null
|
|
143
|
+
})
|
|
144
|
+
return binary + (text?.length ?? 0)
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* The session log as a harness step reads it: the state this process saved
|
|
149
|
+
* at the end of the last response, when it has one, and the log after it.
|
|
150
|
+
* Saved states are copied in and out, so a step cannot change one in place.
|
|
151
|
+
*/
|
|
152
|
+
export function harnessLog(options: {
|
|
153
|
+
states: HarnessStates
|
|
154
|
+
session: {
|
|
155
|
+
readonly id: string
|
|
156
|
+
history(bounds: {
|
|
157
|
+
gte?: number
|
|
158
|
+
lte?: number
|
|
159
|
+
}): Promise<ContractEvent<EventDefs>[]>
|
|
160
|
+
}
|
|
161
|
+
}): HarnessLog {
|
|
162
|
+
const { states, session } = options
|
|
163
|
+
return {
|
|
164
|
+
read: async ({ name, throughIndex, fresh }) => {
|
|
165
|
+
const entry = fresh ? undefined : states.get(savedKey(session.id, name))
|
|
166
|
+
const snapshot =
|
|
167
|
+
entry === undefined || entry.index > throughIndex
|
|
168
|
+
? null
|
|
169
|
+
: { index: entry.index, state: structuredClone(entry.state) }
|
|
170
|
+
const events = await session.history(
|
|
171
|
+
snapshot === null
|
|
172
|
+
? { lte: throughIndex }
|
|
173
|
+
: { gte: snapshot.index + 1, lte: throughIndex },
|
|
174
|
+
)
|
|
175
|
+
return { snapshot, events }
|
|
176
|
+
},
|
|
177
|
+
write: async ({ name, index, state }) => {
|
|
178
|
+
const copy = structuredClone(state)
|
|
179
|
+
states.set(
|
|
180
|
+
savedKey(session.id, name),
|
|
181
|
+
{ index, state: copy },
|
|
182
|
+
sizeOf(copy),
|
|
183
|
+
)
|
|
184
|
+
},
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const agentStates = new WeakMap<object, HarnessStates>()
|
|
189
|
+
|
|
190
|
+
/** Registers an agent server's saved harness states, for `savedHarnessState`. */
|
|
191
|
+
export function registerHarnessStates(
|
|
192
|
+
server: object,
|
|
193
|
+
states: HarnessStates,
|
|
194
|
+
): void {
|
|
195
|
+
agentStates.set(server, states)
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/** What an agent's process saved for one session's harness; tests read it. */
|
|
199
|
+
export function savedHarnessState(
|
|
200
|
+
server: object,
|
|
201
|
+
options: { sessionId: string; name: string },
|
|
202
|
+
): { index: number; state: unknown } | undefined {
|
|
203
|
+
return agentStates.get(server)?.get(savedKey(options.sessionId, options.name))
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/** A harness's state at the end of a response, from the attempt that ran it. */
|
|
207
|
+
type SavedEnvelope = {
|
|
208
|
+
generationId: string
|
|
209
|
+
responseMessageId: string
|
|
210
|
+
state: unknown
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
export type HarnessRead<S> = {
|
|
214
|
+
/** The state saved at the end of the last response, when it is usable. */
|
|
215
|
+
saved: S | undefined
|
|
216
|
+
turns: HarnessTurn[]
|
|
217
|
+
results: Map<string, ToolResultPayload>
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Reads a harness's saved state and the log since, as turns to replay. A
|
|
222
|
+
* snapshot counts only when the attempt that wrote it committed: a stopped,
|
|
223
|
+
* superseded, or still-running attempt can write one the log never kept.
|
|
224
|
+
* `replayAll` reads the whole log instead, for a state that fails to restore.
|
|
225
|
+
*/
|
|
226
|
+
export async function readHarness<S>({
|
|
227
|
+
step,
|
|
228
|
+
name,
|
|
229
|
+
parse,
|
|
230
|
+
}: {
|
|
231
|
+
step: HarnessStep
|
|
232
|
+
name: string
|
|
233
|
+
parse: (state: unknown) => S | undefined
|
|
234
|
+
}): Promise<HarnessRead<S> & { replayAll: () => Promise<HarnessRead<S>> }> {
|
|
235
|
+
const replayAll = async (): Promise<HarnessRead<S>> => {
|
|
236
|
+
const read = await step.log.read({
|
|
237
|
+
name,
|
|
238
|
+
throughIndex: step.throughIndex,
|
|
239
|
+
fresh: true,
|
|
240
|
+
})
|
|
241
|
+
return { saved: undefined, ...harnessTurns(step, read.events, undefined) }
|
|
242
|
+
}
|
|
243
|
+
const read = await step.log.read({ name, throughIndex: step.throughIndex })
|
|
244
|
+
if (read.snapshot === null)
|
|
245
|
+
return {
|
|
246
|
+
saved: undefined,
|
|
247
|
+
...harnessTurns(step, read.events, undefined),
|
|
248
|
+
replayAll,
|
|
249
|
+
}
|
|
250
|
+
const envelope = savedEnvelope(read.snapshot.state)
|
|
251
|
+
const saved = envelope === undefined ? undefined : parse(envelope.state)
|
|
252
|
+
const committed =
|
|
253
|
+
envelope !== undefined &&
|
|
254
|
+
read.events.some(
|
|
255
|
+
(event) =>
|
|
256
|
+
event.type === 'ai.generation.completed' &&
|
|
257
|
+
(event.payload as { generationId?: unknown }).generationId ===
|
|
258
|
+
envelope.generationId,
|
|
259
|
+
)
|
|
260
|
+
// A snapshot of this very response belongs to an earlier attempt at it.
|
|
261
|
+
if (
|
|
262
|
+
saved === undefined ||
|
|
263
|
+
!committed ||
|
|
264
|
+
envelope.responseMessageId === step.responseMessageId
|
|
265
|
+
)
|
|
266
|
+
return { ...(await replayAll()), replayAll }
|
|
267
|
+
return {
|
|
268
|
+
saved,
|
|
269
|
+
...harnessTurns(step, read.events, envelope.responseMessageId),
|
|
270
|
+
replayAll,
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/** Saves a harness's state at the end of a response; losing it costs a longer replay. */
|
|
275
|
+
export async function saveHarness({
|
|
276
|
+
step,
|
|
277
|
+
name,
|
|
278
|
+
state,
|
|
279
|
+
}: {
|
|
280
|
+
step: HarnessStep
|
|
281
|
+
name: string
|
|
282
|
+
state: unknown
|
|
283
|
+
}): Promise<void> {
|
|
284
|
+
const envelope: SavedEnvelope = {
|
|
285
|
+
generationId: step.generationId,
|
|
286
|
+
responseMessageId: step.responseMessageId,
|
|
287
|
+
state,
|
|
288
|
+
}
|
|
289
|
+
try {
|
|
290
|
+
await step.log.write({ name, index: step.requestIndex, state: envelope })
|
|
291
|
+
} catch {
|
|
292
|
+
// The snapshot is a cache.
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
function savedEnvelope(value: unknown): SavedEnvelope | undefined {
|
|
297
|
+
if (typeof value !== 'object' || value === null) return undefined
|
|
298
|
+
const { generationId, responseMessageId, state } = value as Record<
|
|
299
|
+
string,
|
|
300
|
+
unknown
|
|
301
|
+
>
|
|
302
|
+
return typeof generationId === 'string' &&
|
|
303
|
+
typeof responseMessageId === 'string'
|
|
304
|
+
? { generationId, responseMessageId, state }
|
|
305
|
+
: undefined
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// ------------------------------------------------- the log as harness turns
|
|
309
|
+
|
|
310
|
+
// A2's reason on `ai.message.interrupted` when a steer ends a response.
|
|
311
|
+
const STEERED = 'steered after step'
|
|
312
|
+
|
|
313
|
+
/** A completed generation as the log recorded it. */
|
|
314
|
+
export type RecordedGeneration = {
|
|
315
|
+
chunks: UIMessageChunk[]
|
|
316
|
+
completed: GenerationCompletedPayload
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/** One harness turn to rebuild: its prompt, completed generations, and end. */
|
|
320
|
+
export type HarnessTurn = {
|
|
321
|
+
prompt: string
|
|
322
|
+
generations: RecordedGeneration[]
|
|
323
|
+
/** Steering that entered after the generation at that index. */
|
|
324
|
+
steers: Map<number, string[]>
|
|
325
|
+
end: 'stop' | 'interrupted' | 'live'
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
/**
|
|
329
|
+
* Groups the log since a harness snapshot into harness turns. A response
|
|
330
|
+
* steered after a step continues its turn, with the next response's prompt as
|
|
331
|
+
* steering. `covered` is the response the snapshot already holds.
|
|
332
|
+
*/
|
|
333
|
+
export function harnessTurns(
|
|
334
|
+
step: HarnessStep,
|
|
335
|
+
events: readonly ContractEvent<EventDefs>[],
|
|
336
|
+
covered: string | undefined,
|
|
337
|
+
): { turns: HarnessTurn[]; results: Map<string, ToolResultPayload> } {
|
|
338
|
+
type Generation = {
|
|
339
|
+
id: string
|
|
340
|
+
chunks: UIMessageChunk[]
|
|
341
|
+
completed?: GenerationCompletedPayload
|
|
342
|
+
}
|
|
343
|
+
type Response = {
|
|
344
|
+
id: string
|
|
345
|
+
prompt: string[]
|
|
346
|
+
generations: Generation[]
|
|
347
|
+
end?: 'stop' | 'steered' | 'interrupted'
|
|
348
|
+
}
|
|
349
|
+
// Queued messages are admitted together, possibly before the snapshot.
|
|
350
|
+
const userText = new Map<string, string>(
|
|
351
|
+
step.messages
|
|
352
|
+
.filter((message) => message.role === 'user')
|
|
353
|
+
.map((message) => [message.id, textOf(message)]),
|
|
354
|
+
)
|
|
355
|
+
let passive: string[] = []
|
|
356
|
+
const responses = new Map<string, Response>()
|
|
357
|
+
const byStart = new Map<number, Generation>()
|
|
358
|
+
const byId = new Map<string, Generation>()
|
|
359
|
+
const results = new Map<string, ToolResultPayload>()
|
|
360
|
+
|
|
361
|
+
for (const event of events) {
|
|
362
|
+
const payload = event.payload as Record<string, unknown>
|
|
363
|
+
switch (event.type) {
|
|
364
|
+
case 'ai.message.created': {
|
|
365
|
+
const message = payload['message'] as UIMessage
|
|
366
|
+
if (message.role !== 'user') break
|
|
367
|
+
userText.set(message.id, textOf(message))
|
|
368
|
+
if (payload['generate'] === false) passive.push(message.id)
|
|
369
|
+
break
|
|
370
|
+
}
|
|
371
|
+
case 'ai.generation.requested': {
|
|
372
|
+
const id = payload['responseMessageId'] as string
|
|
373
|
+
if (id === covered || responses.has(id)) break
|
|
374
|
+
const messageId = payload['messageId'] as string
|
|
375
|
+
const ids = [...passive.filter((p) => p !== messageId), messageId]
|
|
376
|
+
passive = []
|
|
377
|
+
responses.set(id, {
|
|
378
|
+
id,
|
|
379
|
+
prompt: ids.flatMap((p) => userText.get(p) ?? []),
|
|
380
|
+
generations: [],
|
|
381
|
+
})
|
|
382
|
+
break
|
|
383
|
+
}
|
|
384
|
+
case 'ai.generation.started': {
|
|
385
|
+
const response = responses.get(payload['responseMessageId'] as string)
|
|
386
|
+
if (response === undefined) break
|
|
387
|
+
const generation: Generation = {
|
|
388
|
+
id: payload['generationId'] as string,
|
|
389
|
+
chunks: [],
|
|
390
|
+
}
|
|
391
|
+
response.generations.push(generation)
|
|
392
|
+
byStart.set(event.index, generation)
|
|
393
|
+
byId.set(generation.id, generation)
|
|
394
|
+
break
|
|
395
|
+
}
|
|
396
|
+
case 'ai.generation.progress':
|
|
397
|
+
byStart
|
|
398
|
+
.get(payload['startIndex'] as number)
|
|
399
|
+
?.chunks.push(...(payload['chunks'] as UIMessageChunk[]))
|
|
400
|
+
break
|
|
401
|
+
case 'ai.generation.completed': {
|
|
402
|
+
const generation = byId.get(payload['generationId'] as string)
|
|
403
|
+
if (generation)
|
|
404
|
+
generation.completed = event.payload as GenerationCompletedPayload
|
|
405
|
+
break
|
|
406
|
+
}
|
|
407
|
+
case 'ai.tool.result':
|
|
408
|
+
if (payload['preliminary'] !== true)
|
|
409
|
+
results.set(
|
|
410
|
+
payload['toolCallId'] as string,
|
|
411
|
+
event.payload as ToolResultPayload,
|
|
412
|
+
)
|
|
413
|
+
break
|
|
414
|
+
case 'ai.message.completed': {
|
|
415
|
+
const response = responses.get(payload['messageId'] as string)
|
|
416
|
+
if (response) response.end = 'stop'
|
|
417
|
+
break
|
|
418
|
+
}
|
|
419
|
+
case 'ai.message.interrupted': {
|
|
420
|
+
const response = responses.get(payload['messageId'] as string)
|
|
421
|
+
if (response)
|
|
422
|
+
response.end =
|
|
423
|
+
payload['reason'] === STEERED ? 'steered' : 'interrupted'
|
|
424
|
+
break
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
const turns: HarnessTurn[] = []
|
|
430
|
+
let steered = false
|
|
431
|
+
for (const response of responses.values()) {
|
|
432
|
+
const text = response.prompt.join('\n\n')
|
|
433
|
+
let turn = turns.at(-1)
|
|
434
|
+
if (steered && turn !== undefined) {
|
|
435
|
+
const at = Math.max(0, turn.generations.length - 1)
|
|
436
|
+
turn.steers.set(at, [...(turn.steers.get(at) ?? []), text])
|
|
437
|
+
} else {
|
|
438
|
+
turn = { prompt: text, generations: [], steers: new Map(), end: 'live' }
|
|
439
|
+
turns.push(turn)
|
|
440
|
+
}
|
|
441
|
+
for (const generation of response.generations)
|
|
442
|
+
if (generation.completed !== undefined)
|
|
443
|
+
turn.generations.push({
|
|
444
|
+
chunks: generation.chunks,
|
|
445
|
+
completed: generation.completed,
|
|
446
|
+
})
|
|
447
|
+
turn.end =
|
|
448
|
+
response.id === step.responseMessageId
|
|
449
|
+
? 'live'
|
|
450
|
+
: response.end === 'stop'
|
|
451
|
+
? 'stop'
|
|
452
|
+
: 'interrupted'
|
|
453
|
+
steered = response.end === 'steered'
|
|
454
|
+
}
|
|
455
|
+
return { turns, results }
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
function textOf(message: UIMessage): string {
|
|
459
|
+
return message.parts
|
|
460
|
+
.flatMap((part) => (part.type === 'text' ? [part.text] : []))
|
|
461
|
+
.join('\n')
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
// ------------------------------------------------------------- Gateway
|
|
465
|
+
|
|
466
|
+
export type GatewayCredential = { token: string; method: 'api-key' | 'oidc' }
|
|
467
|
+
|
|
468
|
+
/** Read per step: on Vercel the OIDC token rides on each function request. */
|
|
469
|
+
export function gatewayCredential(harness: string): GatewayCredential {
|
|
470
|
+
const key = process.env['AI_GATEWAY_API_KEY']
|
|
471
|
+
if (key) return { token: key, method: 'api-key' }
|
|
472
|
+
const oidc = requestOidcToken() ?? process.env['VERCEL_OIDC_TOKEN']
|
|
473
|
+
if (oidc) return { token: oidc, method: 'oidc' }
|
|
474
|
+
throw new Error(`${harness}() needs AI_GATEWAY_API_KEY or VERCEL_OIDC_TOKEN`)
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
// Where Vercel Functions keep the current request, as @vercel/oidc reads it.
|
|
478
|
+
const REQUEST_CONTEXT = Symbol.for('@vercel/request-context')
|
|
479
|
+
|
|
480
|
+
function requestOidcToken(): string | undefined {
|
|
481
|
+
const context = (
|
|
482
|
+
globalThis as {
|
|
483
|
+
[REQUEST_CONTEXT]?: { get?: () => { headers?: Record<string, unknown> } }
|
|
484
|
+
}
|
|
485
|
+
)[REQUEST_CONTEXT]
|
|
486
|
+
const token = context?.get?.()?.headers?.['x-vercel-oidc-token']
|
|
487
|
+
return typeof token === 'string' && token !== '' ? token : undefined
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
// ------------------------------------------------------------- helpers
|
|
491
|
+
|
|
492
|
+
export class Divergence extends Error {
|
|
493
|
+
constructor(harness: string, message: string) {
|
|
494
|
+
super(`${harness} replay diverged from the log: ${message}`)
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
export type Deferred = { promise: Promise<void>; resolve: () => void }
|
|
499
|
+
|
|
500
|
+
export function deferred(): Deferred {
|
|
501
|
+
let resolve!: () => void
|
|
502
|
+
const promise = new Promise<void>((done) => {
|
|
503
|
+
resolve = done
|
|
504
|
+
})
|
|
505
|
+
return { promise, resolve }
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
/** A result that never settles; the harness is stopped instead. */
|
|
509
|
+
export function pending(
|
|
510
|
+
signal: AbortSignal | null | undefined,
|
|
511
|
+
): Promise<never> {
|
|
512
|
+
return new Promise((_, reject) => {
|
|
513
|
+
if (signal?.aborted) reject(signal.reason)
|
|
514
|
+
signal?.addEventListener('abort', () => reject(signal.reason), {
|
|
515
|
+
once: true,
|
|
516
|
+
})
|
|
517
|
+
})
|
|
518
|
+
}
|