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
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import { contract } from 'experimental-a2'
|
|
1
2
|
import type { FinishReason, LanguageModelUsage, UIMessage } from 'ai'
|
|
2
|
-
import {
|
|
3
|
+
import { agentContract } from 'experimental-a2/ai'
|
|
3
4
|
import { z } from 'zod'
|
|
4
5
|
|
|
5
6
|
export type ChatAgentStep = {
|
|
@@ -147,28 +148,12 @@ const chatMessageSchema = z.custom<ChatAgentMessage>(
|
|
|
147
148
|
'invalid chat UI message',
|
|
148
149
|
)
|
|
149
150
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
text: z.string().min(1).max(2000),
|
|
154
|
-
}),
|
|
155
|
-
typed: z.object({ author: z.string().min(1).max(40) }),
|
|
156
|
-
'agent.ambient.changed': z.object({
|
|
157
|
-
enabled: z.boolean(),
|
|
158
|
-
author: z.string().min(1).max(40),
|
|
159
|
-
}),
|
|
160
|
-
'agent.message.posted': agentPostEventSchema,
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
export const chatAgent = agent<ChatAgentMessage, typeof chatEvents>({
|
|
164
|
-
name: 'chat',
|
|
165
|
-
events: chatEvents,
|
|
151
|
+
/** The agent's own session: the conversation it sees, one per room. */
|
|
152
|
+
export const chatAgent = agentContract<ChatAgentMessage>({
|
|
153
|
+
name: 'chat-agent',
|
|
166
154
|
messageSchema: chatMessageSchema,
|
|
167
|
-
reducerName: 'a2.playground.chat.ai.v3',
|
|
168
155
|
})
|
|
169
156
|
|
|
170
|
-
export const chat = chatAgent.contract
|
|
171
|
-
|
|
172
157
|
export type ChatMessage = {
|
|
173
158
|
id: string
|
|
174
159
|
index: number
|
|
@@ -187,7 +172,6 @@ export type AgentPost = {
|
|
|
187
172
|
}
|
|
188
173
|
|
|
189
174
|
export type ChatState = {
|
|
190
|
-
ai: AIStoredState
|
|
191
175
|
messages: ChatMessage[]
|
|
192
176
|
posts: AgentPost[]
|
|
193
177
|
lastTypingAt: Record<string, string>
|
|
@@ -199,124 +183,94 @@ export type ChatState = {
|
|
|
199
183
|
}
|
|
200
184
|
|
|
201
185
|
const initialChatState: ChatState = {
|
|
202
|
-
ai: chatAgent.reducer.initialState,
|
|
203
186
|
messages: [],
|
|
204
187
|
posts: [],
|
|
205
188
|
lastTypingAt: {},
|
|
206
189
|
ambient: { enabled: false, changedBy: null, changedAt: null },
|
|
207
190
|
}
|
|
208
191
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
192
|
+
/**
|
|
193
|
+
* The room: an ordinary A2 session. The agent runs in its own session, and its
|
|
194
|
+
* `post` tool appends here, so the room's log orders its messages among
|
|
195
|
+
* everyone else's.
|
|
196
|
+
*/
|
|
197
|
+
export const chat = contract({
|
|
198
|
+
name: 'chat-room',
|
|
199
|
+
events: {
|
|
200
|
+
'message.sent': z.object({
|
|
201
|
+
author: z.string().min(1).max(40),
|
|
202
|
+
text: z.string().min(1).max(2000),
|
|
203
|
+
}),
|
|
204
|
+
typed: z.object({ author: z.string().min(1).max(40) }),
|
|
205
|
+
'agent.ambient.changed': z.object({
|
|
206
|
+
enabled: z.boolean(),
|
|
207
|
+
author: z.string().min(1).max(40),
|
|
208
|
+
}),
|
|
209
|
+
'agent.message.posted': agentPostEventSchema,
|
|
210
|
+
},
|
|
211
|
+
reducer: {
|
|
212
|
+
name: 'a2.playground.chat-room.v1',
|
|
212
213
|
initialState: initialChatState,
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
const stillTyping = { ...base.lastTypingAt }
|
|
233
|
-
delete stillTyping[event.payload.author]
|
|
234
|
-
return {
|
|
235
|
-
...base,
|
|
236
|
-
messages: [
|
|
237
|
-
...base.messages,
|
|
238
|
-
{
|
|
239
|
-
id: event.id,
|
|
240
|
-
index: event.index,
|
|
241
|
-
author: event.payload.author,
|
|
242
|
-
text: event.payload.text,
|
|
243
|
-
at: event.createdAt.toISOString(),
|
|
244
|
-
},
|
|
245
|
-
],
|
|
246
|
-
lastTypingAt: stillTyping,
|
|
214
|
+
fold: (state, event) => {
|
|
215
|
+
switch (event.type) {
|
|
216
|
+
case 'message.sent': {
|
|
217
|
+
const stillTyping = { ...state.lastTypingAt }
|
|
218
|
+
delete stillTyping[event.payload.author]
|
|
219
|
+
return {
|
|
220
|
+
...state,
|
|
221
|
+
messages: [
|
|
222
|
+
...state.messages,
|
|
223
|
+
{
|
|
224
|
+
id: event.id,
|
|
225
|
+
index: event.index,
|
|
226
|
+
author: event.payload.author,
|
|
227
|
+
text: event.payload.text,
|
|
228
|
+
at: event.createdAt.toISOString(),
|
|
229
|
+
},
|
|
230
|
+
],
|
|
231
|
+
lastTypingAt: stillTyping,
|
|
232
|
+
}
|
|
247
233
|
}
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
[event.payload.author]: event.createdAt.toISOString(),
|
|
255
|
-
},
|
|
256
|
-
}
|
|
257
|
-
case 'agent.ambient.changed':
|
|
258
|
-
return {
|
|
259
|
-
...base,
|
|
260
|
-
ambient: {
|
|
261
|
-
enabled: event.payload.enabled,
|
|
262
|
-
changedBy: event.payload.author,
|
|
263
|
-
changedAt: event.createdAt.toISOString(),
|
|
264
|
-
},
|
|
265
|
-
}
|
|
266
|
-
case 'agent.message.posted': {
|
|
267
|
-
const payload = event.payload
|
|
268
|
-
const running = previousAI.tools.some(
|
|
269
|
-
(activity) =>
|
|
270
|
-
activity.generationId === payload.generationId &&
|
|
271
|
-
activity.messageId === payload.responseMessageId &&
|
|
272
|
-
activity.toolCallId === payload.toolCallId &&
|
|
273
|
-
activity.toolName === 'post' &&
|
|
274
|
-
activity.status === 'running',
|
|
275
|
-
)
|
|
276
|
-
if (!running) return base
|
|
277
|
-
return {
|
|
278
|
-
...base,
|
|
279
|
-
posts: [
|
|
280
|
-
...base.posts,
|
|
281
|
-
{
|
|
282
|
-
generationId: payload.generationId,
|
|
283
|
-
responseMessageId: payload.responseMessageId,
|
|
284
|
-
toolCallId: payload.toolCallId,
|
|
285
|
-
index: event.index,
|
|
286
|
-
text: payload.message,
|
|
287
|
-
at: event.createdAt.toISOString(),
|
|
234
|
+
case 'typed':
|
|
235
|
+
return {
|
|
236
|
+
...state,
|
|
237
|
+
lastTypingAt: {
|
|
238
|
+
...state.lastTypingAt,
|
|
239
|
+
[event.payload.author]: event.createdAt.toISOString(),
|
|
288
240
|
},
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
241
|
+
}
|
|
242
|
+
case 'agent.ambient.changed':
|
|
243
|
+
return {
|
|
244
|
+
...state,
|
|
245
|
+
ambient: {
|
|
246
|
+
enabled: event.payload.enabled,
|
|
247
|
+
changedBy: event.payload.author,
|
|
248
|
+
changedAt: event.createdAt.toISOString(),
|
|
249
|
+
},
|
|
250
|
+
}
|
|
251
|
+
case 'agent.message.posted':
|
|
252
|
+
return {
|
|
253
|
+
...state,
|
|
254
|
+
posts: [
|
|
255
|
+
...state.posts,
|
|
256
|
+
{
|
|
257
|
+
generationId: event.payload.generationId,
|
|
258
|
+
responseMessageId: event.payload.responseMessageId,
|
|
259
|
+
toolCallId: event.payload.toolCallId,
|
|
260
|
+
index: event.index,
|
|
261
|
+
text: event.payload.message,
|
|
262
|
+
at: event.createdAt.toISOString(),
|
|
263
|
+
},
|
|
264
|
+
],
|
|
265
|
+
}
|
|
266
|
+
default:
|
|
267
|
+
return state
|
|
305
268
|
}
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
269
|
+
},
|
|
270
|
+
},
|
|
271
|
+
})
|
|
272
|
+
export const chatReducer = chat.reducer
|
|
310
273
|
|
|
311
274
|
const agentMention = /(?:^|\s)@agent(?![\w-])/i
|
|
312
275
|
|
|
313
276
|
export const mentionsAgent = (text: string): boolean => agentMention.test(text)
|
|
314
|
-
|
|
315
|
-
export type ChatViewState = Omit<ChatState, 'ai'> & {
|
|
316
|
-
ai: AIState<ChatAgentMessage>
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
export const viewChatState = (state: ChatState): ChatViewState => ({
|
|
320
|
-
...state,
|
|
321
|
-
ai: chatAgent.reducer.view(state.ai),
|
|
322
|
-
})
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { chatScheduler, chatServer } from '../server'
|
|
1
|
+
import { chatAgentServer, chatScheduler, chatServer } from '../server'
|
|
2
2
|
|
|
3
3
|
export const POST =
|
|
4
|
-
chatScheduler?.handler(chatServer) ??
|
|
4
|
+
chatScheduler?.handler(chatServer, chatAgentServer) ??
|
|
5
5
|
(() => new Response('chat scheduler is not configured', { status: 503 }))
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import type { UIMessageChunk } from 'ai'
|
|
2
|
+
import { expect, it, vi } from 'vitest'
|
|
3
|
+
|
|
4
|
+
vi.mock('server-only', () => ({}))
|
|
5
|
+
vi.mock('@/lib/store', async () => {
|
|
6
|
+
const { memory } = await import('experimental-a2/store-memory')
|
|
7
|
+
return { store: memory() }
|
|
8
|
+
})
|
|
9
|
+
|
|
10
|
+
// The chat's real agent, with a scripted model: two `post` calls, then done.
|
|
11
|
+
const { prompts, step } = vi.hoisted(() => ({
|
|
12
|
+
prompts: [] as string[],
|
|
13
|
+
step: (chunks: UIMessageChunk[]) =>
|
|
14
|
+
new ReadableStream<UIMessageChunk>({
|
|
15
|
+
start(controller) {
|
|
16
|
+
for (const chunk of chunks) controller.enqueue(chunk)
|
|
17
|
+
controller.close()
|
|
18
|
+
},
|
|
19
|
+
}),
|
|
20
|
+
}))
|
|
21
|
+
vi.mock('experimental-a2/ai/server', async (importActual) => {
|
|
22
|
+
const actual =
|
|
23
|
+
await importActual<typeof import('experimental-a2/ai/server')>()
|
|
24
|
+
return {
|
|
25
|
+
...actual,
|
|
26
|
+
agent: (options: Parameters<typeof actual.agent>[0]) =>
|
|
27
|
+
actual.agent({
|
|
28
|
+
...options,
|
|
29
|
+
harness: {
|
|
30
|
+
...(options.harness?.type === 'ai-sdk' ? options.harness : {}),
|
|
31
|
+
type: 'ai-sdk',
|
|
32
|
+
generate: ({
|
|
33
|
+
event,
|
|
34
|
+
messages,
|
|
35
|
+
}: {
|
|
36
|
+
event: { payload: { reason: string } }
|
|
37
|
+
messages: { role: string; id: string }[]
|
|
38
|
+
}) => {
|
|
39
|
+
prompts.push(
|
|
40
|
+
messages.map((message) => `${message.role}:${message.id}`).join(),
|
|
41
|
+
)
|
|
42
|
+
return step(
|
|
43
|
+
event.payload.reason === 'message'
|
|
44
|
+
? [
|
|
45
|
+
{ type: 'start' },
|
|
46
|
+
...['Hello', 'there'].map(
|
|
47
|
+
(message, call): UIMessageChunk => ({
|
|
48
|
+
type: 'tool-input-available',
|
|
49
|
+
toolCallId: `call-${call}`,
|
|
50
|
+
toolName: 'post',
|
|
51
|
+
input: { message },
|
|
52
|
+
}),
|
|
53
|
+
),
|
|
54
|
+
{ type: 'finish', finishReason: 'tool-calls' },
|
|
55
|
+
]
|
|
56
|
+
: [{ type: 'start' }, { type: 'finish', finishReason: 'stop' }],
|
|
57
|
+
)
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
} as never),
|
|
61
|
+
}
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
it('forwards room messages to the agent, whose posts land in the room', async () => {
|
|
65
|
+
const { chatAgentServer, chatServer } = await import('./server')
|
|
66
|
+
const room = chatServer.session('room')
|
|
67
|
+
const agent = chatAgentServer.session('room')
|
|
68
|
+
await room.append({
|
|
69
|
+
type: 'message.sent',
|
|
70
|
+
payload: { author: 'alice', text: 'just chatting' },
|
|
71
|
+
})
|
|
72
|
+
await room.append({
|
|
73
|
+
type: 'message.sent',
|
|
74
|
+
payload: { author: 'bob', text: '@agent say hi' },
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
// Without a scheduler, appends run in background drains.
|
|
78
|
+
await vi.waitFor(
|
|
79
|
+
async () => {
|
|
80
|
+
expect((await room.state()).state.posts).toHaveLength(2)
|
|
81
|
+
expect((await agent.state()).state.status).toBe('idle')
|
|
82
|
+
},
|
|
83
|
+
{ timeout: 5_000 },
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
const { state } = await room.state()
|
|
87
|
+
expect(state.messages.map((message) => message.author)).toEqual([
|
|
88
|
+
'alice',
|
|
89
|
+
'bob',
|
|
90
|
+
])
|
|
91
|
+
expect(state.posts.map((post) => post.text)).toEqual(['Hello', 'there'])
|
|
92
|
+
expect(state.posts[0]!.index).toBeGreaterThan(state.messages[1]!.index)
|
|
93
|
+
// The passive message reached the agent as context without a reply of its
|
|
94
|
+
// own; only the mention generated, and it saw both.
|
|
95
|
+
expect(prompts).toHaveLength(2)
|
|
96
|
+
expect(
|
|
97
|
+
prompts[0]!.split(',').filter((id) => id.startsWith('user:')),
|
|
98
|
+
).toHaveLength(2)
|
|
99
|
+
const messages = (await agent.state()).state.messages
|
|
100
|
+
expect(messages.map((message) => message.role)).toEqual([
|
|
101
|
+
'user',
|
|
102
|
+
'user',
|
|
103
|
+
'assistant',
|
|
104
|
+
])
|
|
105
|
+
|
|
106
|
+
// Replaying the post tool's append converges on the stored event.
|
|
107
|
+
const [post] = (await room.history()).filter(
|
|
108
|
+
(event) => event.type === 'agent.message.posted',
|
|
109
|
+
)
|
|
110
|
+
await room.append({ type: post!.type, id: post!.id, payload: post!.payload })
|
|
111
|
+
expect((await room.state()).state.posts).toHaveLength(2)
|
|
112
|
+
})
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import 'server-only'
|
|
2
2
|
import { tool } from 'ai'
|
|
3
|
+
import { idempotentId } from 'experimental-a2'
|
|
3
4
|
import { handlerContext, inputs } from 'experimental-a2/ai'
|
|
4
|
-
import {
|
|
5
|
+
import { agent } from 'experimental-a2/ai/server'
|
|
5
6
|
import { vercelQueues } from 'experimental-a2/scheduler-vercel'
|
|
6
7
|
import { createServer, type A2Operation } from 'experimental-a2/server'
|
|
7
8
|
import { store } from '@/lib/store'
|
|
8
9
|
import {
|
|
10
|
+
chat,
|
|
9
11
|
chatAgent,
|
|
10
12
|
chatPostInputSchema,
|
|
11
13
|
mentionsAgent,
|
|
@@ -22,12 +24,16 @@ const tools = {
|
|
|
22
24
|
execute: async ({ message }, { toolCallId }) => {
|
|
23
25
|
const context = handlerContext(chatAgent)
|
|
24
26
|
context.signal.throwIfAborted()
|
|
25
|
-
|
|
27
|
+
const { generationId, messageId } = context.event.payload.call
|
|
28
|
+
// The agent's session shares the room's id. The explicit id makes a
|
|
29
|
+
// retried tool converge on one post.
|
|
30
|
+
await chatServer.session(context.session.id).append({
|
|
26
31
|
type: 'agent.message.posted',
|
|
32
|
+
id: await idempotentId('chat.post', generationId, toolCallId),
|
|
27
33
|
payload: {
|
|
28
34
|
message,
|
|
29
|
-
generationId
|
|
30
|
-
responseMessageId:
|
|
35
|
+
generationId,
|
|
36
|
+
responseMessageId: messageId,
|
|
31
37
|
toolCallId,
|
|
32
38
|
},
|
|
33
39
|
})
|
|
@@ -36,139 +42,88 @@ const tools = {
|
|
|
36
42
|
}),
|
|
37
43
|
}
|
|
38
44
|
|
|
39
|
-
const
|
|
40
|
-
|
|
45
|
+
export const chatScheduler =
|
|
46
|
+
process.env.VERCEL || process.env.A2_QUEUES
|
|
47
|
+
? vercelQueues({ topic: 'a2-chat' })
|
|
48
|
+
: undefined
|
|
49
|
+
|
|
50
|
+
export const chatAgentServer = agent({
|
|
51
|
+
...(store ? { store } : {}),
|
|
52
|
+
...(chatScheduler ? { scheduler: chatScheduler } : {}),
|
|
53
|
+
contract: chatAgent,
|
|
41
54
|
model: 'openai/gpt-5.4',
|
|
42
55
|
tools,
|
|
56
|
+
// Posts become chat bubbles, so one agent's posts must land in call order.
|
|
57
|
+
toolLane: 'a2.chat.agent-tools',
|
|
43
58
|
instructions: `You are "agent", a participant in a multiplayer chat.
|
|
44
59
|
The latest participant message invited you either by mentioning @agent or by enabling the room's ambient mode. Messages are formatted as "name: message"; earlier passive messages are useful conversation context.
|
|
45
60
|
Sound like a sharp, relaxed person in the chat, not a customer-support bot. Match the room's language and energy. Keep messages specific and conversational; avoid canned acknowledgements, assistant-y disclaimers, and explanations of your own mechanics.
|
|
46
61
|
Only contribute when you have something useful, relevant, or friendly to add. It is fine to stay silent or stop whenever you have nothing else to say.
|
|
47
62
|
To speak, call post once for each visible chat bubble. Never communicate through ordinary assistant text because it is private and is not shown in the room.
|
|
48
63
|
Use as many post calls as the moment naturally calls for. Several short messages are welcome when they create better rhythm, separate distinct thoughts, or fulfill a request for a sequence. A single message is fine too. Do not spam. After post results, continue only when another message genuinely improves the conversation.`,
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
64
|
+
harness: {
|
|
65
|
+
type: 'ai-sdk',
|
|
66
|
+
generation: {
|
|
67
|
+
maxOutputTokens: 500,
|
|
68
|
+
temperature: 0.4,
|
|
69
|
+
},
|
|
70
|
+
messageMetadata: (context) => {
|
|
71
|
+
const previous = context.messages.find(
|
|
72
|
+
(message) => message.id === context.event.payload.responseMessageId,
|
|
73
|
+
)?.metadata
|
|
74
|
+
const trigger = context.messages.find(
|
|
75
|
+
(message) => message.id === context.event.payload.messageId,
|
|
76
|
+
)?.metadata
|
|
77
|
+
const steps = previous?.kind === 'agent' ? previous.steps : []
|
|
78
|
+
const mode: ChatAgentMode | undefined =
|
|
79
|
+
previous?.kind === 'agent'
|
|
80
|
+
? previous.mode
|
|
81
|
+
: trigger?.kind === 'participant' && trigger.mode !== 'passive'
|
|
82
|
+
? trigger.mode
|
|
83
|
+
: undefined
|
|
84
|
+
if (mode === undefined) return undefined
|
|
85
|
+
const metadata = {
|
|
86
|
+
kind: 'agent' as const,
|
|
87
|
+
author: 'agent' as const,
|
|
88
|
+
mode,
|
|
89
|
+
steps,
|
|
90
|
+
}
|
|
91
|
+
if (context.part.type === 'start') return metadata
|
|
92
|
+
if (context.part.type !== 'finish') return undefined
|
|
93
|
+
return {
|
|
94
|
+
...metadata,
|
|
95
|
+
steps: [
|
|
96
|
+
...steps,
|
|
97
|
+
{
|
|
98
|
+
generationId: context.generationId,
|
|
99
|
+
model: context.model,
|
|
100
|
+
finishReason: context.part.finishReason,
|
|
101
|
+
...(context.part.rawFinishReason === undefined
|
|
102
|
+
? {}
|
|
103
|
+
: { rawFinishReason: context.part.rawFinishReason }),
|
|
104
|
+
usage: context.part.totalUsage,
|
|
105
|
+
completedAt: new Date().toISOString(),
|
|
106
|
+
},
|
|
107
|
+
],
|
|
108
|
+
}
|
|
109
|
+
},
|
|
52
110
|
},
|
|
53
111
|
progress: {
|
|
54
112
|
maxChunks: 4,
|
|
55
113
|
maxDelayMs: 30,
|
|
56
114
|
},
|
|
57
|
-
messageMetadata: (context) => {
|
|
58
|
-
const previous = context.messages.find(
|
|
59
|
-
(message) => message.id === context.responseMessageId,
|
|
60
|
-
)?.metadata
|
|
61
|
-
const trigger = context.messages.find(
|
|
62
|
-
(message) => message.id === context.request.payload.messageId,
|
|
63
|
-
)?.metadata
|
|
64
|
-
const steps = previous?.kind === 'agent' ? previous.steps : []
|
|
65
|
-
const mode: ChatAgentMode | undefined =
|
|
66
|
-
previous?.kind === 'agent'
|
|
67
|
-
? previous.mode
|
|
68
|
-
: trigger?.kind === 'participant' && trigger.mode !== 'passive'
|
|
69
|
-
? trigger.mode
|
|
70
|
-
: undefined
|
|
71
|
-
if (mode === undefined) return undefined
|
|
72
|
-
const metadata = {
|
|
73
|
-
kind: 'agent' as const,
|
|
74
|
-
author: 'agent' as const,
|
|
75
|
-
mode,
|
|
76
|
-
steps,
|
|
77
|
-
}
|
|
78
|
-
if (context.part.type === 'start') return metadata
|
|
79
|
-
if (context.part.type !== 'finish') return undefined
|
|
80
|
-
return {
|
|
81
|
-
...metadata,
|
|
82
|
-
steps: [
|
|
83
|
-
...steps,
|
|
84
|
-
{
|
|
85
|
-
generationId: context.generationId,
|
|
86
|
-
model:
|
|
87
|
-
typeof context.model === 'string'
|
|
88
|
-
? context.model
|
|
89
|
-
: context.model.modelId,
|
|
90
|
-
finishReason: context.part.finishReason,
|
|
91
|
-
...(context.part.rawFinishReason === undefined
|
|
92
|
-
? {}
|
|
93
|
-
: { rawFinishReason: context.part.rawFinishReason }),
|
|
94
|
-
usage: context.part.totalUsage,
|
|
95
|
-
completedAt: new Date().toISOString(),
|
|
96
|
-
},
|
|
97
|
-
],
|
|
98
|
-
}
|
|
99
|
-
},
|
|
100
115
|
})
|
|
101
116
|
|
|
102
|
-
|
|
103
|
-
operation: A2Operation<(typeof chatAgent.contract)['events']>,
|
|
104
|
-
): boolean => {
|
|
105
|
-
if (operation.type !== 'push') return true
|
|
106
|
-
const serverOnly = operation.events.find(
|
|
107
|
-
(event) =>
|
|
108
|
-
event.type === 'ai.message.created' ||
|
|
109
|
-
event.type === 'agent.message.posted',
|
|
110
|
-
)
|
|
111
|
-
if (serverOnly !== undefined) return false
|
|
112
|
-
const rejected = operation.events.find((event) => {
|
|
113
|
-
if (
|
|
114
|
-
event.type === 'message.sent' ||
|
|
115
|
-
event.type === 'typed' ||
|
|
116
|
-
event.type === 'agent.ambient.changed'
|
|
117
|
-
) {
|
|
118
|
-
return false
|
|
119
|
-
}
|
|
120
|
-
return !(
|
|
121
|
-
event.type === 'ai.control.requested' &&
|
|
122
|
-
typeof event.payload === 'object' &&
|
|
123
|
-
event.payload !== null &&
|
|
124
|
-
'action' in event.payload &&
|
|
125
|
-
typeof event.payload.action === 'string' &&
|
|
126
|
-
[
|
|
127
|
-
'approval',
|
|
128
|
-
'input',
|
|
129
|
-
'interrupt',
|
|
130
|
-
'stop',
|
|
131
|
-
'retry',
|
|
132
|
-
'pause',
|
|
133
|
-
'resume',
|
|
134
|
-
].includes(event.payload.action)
|
|
135
|
-
)
|
|
136
|
-
})
|
|
137
|
-
return rejected === undefined
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
const toolExecutionHandler = aiHandlers['ai.tool.execution.requested']
|
|
141
|
-
if (toolExecutionHandler === undefined) {
|
|
142
|
-
throw new Error('A2 AI handlers must include ai.tool.execution.requested')
|
|
143
|
-
}
|
|
144
|
-
const serializedToolExecutionHandler =
|
|
145
|
-
typeof toolExecutionHandler === 'function'
|
|
146
|
-
? { handler: toolExecutionHandler, lane: 'a2.chat.agent-tools' }
|
|
147
|
-
: { ...toolExecutionHandler, lane: 'a2.chat.agent-tools' }
|
|
148
|
-
|
|
149
|
-
export const chatScheduler =
|
|
150
|
-
process.env.VERCEL || process.env.A2_QUEUES
|
|
151
|
-
? vercelQueues({ topic: 'a2-chat' })
|
|
152
|
-
: undefined
|
|
153
|
-
|
|
117
|
+
/** The room. Each message goes on to the agent's session, marked for whether it should answer. */
|
|
154
118
|
export const chatServer = createServer({
|
|
155
119
|
...(store ? { store } : {}),
|
|
156
120
|
...(chatScheduler ? { scheduler: chatScheduler } : {}),
|
|
157
|
-
contract:
|
|
121
|
+
contract: chat,
|
|
158
122
|
handlers: {
|
|
159
|
-
...aiHandlers,
|
|
160
|
-
'ai.tool.execution.requested': serializedToolExecutionHandler,
|
|
161
123
|
'message.sent': {
|
|
162
124
|
lane: 'a2.chat.ingress',
|
|
163
125
|
handler: async (context) => {
|
|
164
|
-
|
|
165
|
-
for (const event of await context.session.history()) {
|
|
166
|
-
if (event.index > context.event.index) break
|
|
167
|
-
if (event.type === 'agent.ambient.changed') {
|
|
168
|
-
ambient = event.payload.enabled
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
|
|
126
|
+
const ambient = (await context.session.state()).state.ambient.enabled
|
|
172
127
|
const { author, text } = context.event.payload
|
|
173
128
|
const mode: ChatParticipantMode = ambient
|
|
174
129
|
? 'ambient'
|
|
@@ -176,7 +131,9 @@ export const chatServer = createServer({
|
|
|
176
131
|
? 'mention'
|
|
177
132
|
: 'passive'
|
|
178
133
|
const message: ChatAgentMessage = {
|
|
179
|
-
|
|
134
|
+
// Event ids are unique across a store's sessions, so the forward
|
|
135
|
+
// derives its own; a retried forward converges on the same id.
|
|
136
|
+
id: await idempotentId('chat.message', context.event.id),
|
|
180
137
|
role: 'user',
|
|
181
138
|
metadata: {
|
|
182
139
|
kind: 'participant',
|
|
@@ -187,8 +144,47 @@ export const chatServer = createServer({
|
|
|
187
144
|
},
|
|
188
145
|
parts: [{ type: 'text', text: `${author}: ${text}` }],
|
|
189
146
|
}
|
|
190
|
-
|
|
147
|
+
await chatAgentServer
|
|
148
|
+
.session(context.session.id)
|
|
149
|
+
.append(...inputs.message(message, { generate: mode !== 'passive' }))
|
|
191
150
|
},
|
|
192
151
|
},
|
|
193
152
|
},
|
|
194
153
|
})
|
|
154
|
+
|
|
155
|
+
/** Room pushes: people's messages, typing, and ambient mode. Posts are the agent's. */
|
|
156
|
+
export const authorizeChatOperation = (
|
|
157
|
+
operation: A2Operation<(typeof chat)['events']>,
|
|
158
|
+
): boolean =>
|
|
159
|
+
operation.type !== 'push' ||
|
|
160
|
+
operation.events.every(
|
|
161
|
+
(event) =>
|
|
162
|
+
event.type === 'message.sent' ||
|
|
163
|
+
event.type === 'typed' ||
|
|
164
|
+
event.type === 'agent.ambient.changed',
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
const agentControls = new Set([
|
|
168
|
+
'approval',
|
|
169
|
+
'input',
|
|
170
|
+
'interrupt',
|
|
171
|
+
'stop',
|
|
172
|
+
'retry',
|
|
173
|
+
'pause',
|
|
174
|
+
'resume',
|
|
175
|
+
])
|
|
176
|
+
|
|
177
|
+
/** Agent pushes: controls only. Messages reach the agent through the room. */
|
|
178
|
+
export const authorizeChatAgentOperation = (
|
|
179
|
+
operation: A2Operation<(typeof chatAgent)['events']>,
|
|
180
|
+
): boolean =>
|
|
181
|
+
operation.type !== 'push' ||
|
|
182
|
+
operation.events.every(
|
|
183
|
+
(event) =>
|
|
184
|
+
event.type === 'ai.control.requested' &&
|
|
185
|
+
typeof event.payload === 'object' &&
|
|
186
|
+
event.payload !== null &&
|
|
187
|
+
'action' in event.payload &&
|
|
188
|
+
typeof event.payload.action === 'string' &&
|
|
189
|
+
agentControls.has(event.payload.action),
|
|
190
|
+
)
|