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,101 @@
|
|
|
1
|
+
import { expect, it, vi } from 'vitest'
|
|
2
|
+
import { agent } from 'experimental-a2/ai/server'
|
|
3
|
+
import { memory } from 'experimental-a2/store-memory'
|
|
4
|
+
import { inputs } from 'experimental-a2/ai'
|
|
5
|
+
import { demoAgent } from './model'
|
|
6
|
+
|
|
7
|
+
const message = { id: 'message', role: 'user' as const, parts: [] }
|
|
8
|
+
|
|
9
|
+
const post = (events: unknown[]): Request =>
|
|
10
|
+
new Request('http://test/events?sessionId=one', {
|
|
11
|
+
method: 'POST',
|
|
12
|
+
headers: { 'content-type': 'application/json' },
|
|
13
|
+
body: JSON.stringify({ sessionId: 'one', events }),
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
const server = () => {
|
|
17
|
+
let generations = 0
|
|
18
|
+
const agentServer = agent({
|
|
19
|
+
contract: demoAgent,
|
|
20
|
+
store: memory(),
|
|
21
|
+
model: 'test/model',
|
|
22
|
+
harness: {
|
|
23
|
+
type: 'ai-sdk',
|
|
24
|
+
generate: () => {
|
|
25
|
+
generations += 1
|
|
26
|
+
return new ReadableStream({ start: (controller) => controller.close() })
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
})
|
|
30
|
+
return { agentServer, generations: () => generations }
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
it('accepts compaction settings from the browser without starting a turn', async () => {
|
|
34
|
+
const { agentServer, generations } = server()
|
|
35
|
+
const response = await agentServer.fetch(
|
|
36
|
+
post([
|
|
37
|
+
{
|
|
38
|
+
type: 'compaction.settings.updated',
|
|
39
|
+
payload: { mode: 'manual', thresholdTokens: 1 },
|
|
40
|
+
},
|
|
41
|
+
]),
|
|
42
|
+
)
|
|
43
|
+
expect(response.status).toBe(200)
|
|
44
|
+
await agentServer.drain('one')
|
|
45
|
+
const { state } = await agentServer.session('one').state()
|
|
46
|
+
expect(state.compactionSettings).toEqual({
|
|
47
|
+
mode: 'manual',
|
|
48
|
+
thresholdTokens: 1,
|
|
49
|
+
})
|
|
50
|
+
expect(generations()).toBe(0)
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
it.each([0, -1, 1.5, Number.MAX_SAFE_INTEGER + 1, '1', null])(
|
|
54
|
+
'rejects invalid threshold %s',
|
|
55
|
+
async (thresholdTokens) => {
|
|
56
|
+
const { agentServer } = server()
|
|
57
|
+
const response = await agentServer.fetch(
|
|
58
|
+
post([
|
|
59
|
+
{
|
|
60
|
+
type: 'compaction.settings.updated',
|
|
61
|
+
payload: { mode: 'manual', thresholdTokens },
|
|
62
|
+
},
|
|
63
|
+
]),
|
|
64
|
+
)
|
|
65
|
+
expect(response.status).toBe(400)
|
|
66
|
+
expect(await agentServer.session('one').history()).toEqual([])
|
|
67
|
+
},
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
it('keeps lifecycle events server-only beside public controls', async () => {
|
|
71
|
+
const { agentServer } = server()
|
|
72
|
+
const rejected = await agentServer.fetch(
|
|
73
|
+
post([
|
|
74
|
+
...inputs.message(message),
|
|
75
|
+
{ type: 'ai.session.closed', payload: {} },
|
|
76
|
+
]),
|
|
77
|
+
)
|
|
78
|
+
expect(rejected.status).toBe(400)
|
|
79
|
+
expect(await agentServer.session('one').history()).toEqual([])
|
|
80
|
+
const accepted = await agentServer.fetch(post(inputs.message(message)))
|
|
81
|
+
expect(accepted.status).toBe(200)
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
it('forwards both HTTP and WebSocket entry requests to the agent', async () => {
|
|
85
|
+
const fetch = vi
|
|
86
|
+
.fn<(request: Request) => Promise<Response>>()
|
|
87
|
+
.mockResolvedValue(new Response(null))
|
|
88
|
+
vi.doMock('./server', () => ({ agentServer: { fetch } }))
|
|
89
|
+
try {
|
|
90
|
+
const route = await import('./events/route')
|
|
91
|
+
const read = new Request('http://test/events', {
|
|
92
|
+
headers: { upgrade: 'websocket' },
|
|
93
|
+
})
|
|
94
|
+
const write = new Request('http://test/events', { method: 'POST' })
|
|
95
|
+
await route.GET(read)
|
|
96
|
+
await route.POST(write)
|
|
97
|
+
expect(fetch.mock.calls).toEqual([[read], [write]])
|
|
98
|
+
} finally {
|
|
99
|
+
vi.doUnmock('./server')
|
|
100
|
+
}
|
|
101
|
+
})
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// @vitest-environment jsdom
|
|
2
|
+
import { cleanup, fireEvent, render, screen } from '@testing-library/react'
|
|
3
|
+
import { afterEach, expect, it, vi } from 'vitest'
|
|
4
|
+
import type { ModelChoice } from './model'
|
|
5
|
+
import { ModelInput } from './model-input'
|
|
6
|
+
|
|
7
|
+
afterEach(cleanup)
|
|
8
|
+
|
|
9
|
+
const renderInput = (value: ModelChoice) => {
|
|
10
|
+
const onChange = vi.fn<(settings: ModelChoice) => void>()
|
|
11
|
+
const view = render(
|
|
12
|
+
<ModelInput value={value} onChange={onChange} disabled={false} />,
|
|
13
|
+
)
|
|
14
|
+
return {
|
|
15
|
+
onChange,
|
|
16
|
+
model: screen.getByRole('textbox') as HTMLInputElement,
|
|
17
|
+
update: (next: ModelChoice) =>
|
|
18
|
+
view.rerender(
|
|
19
|
+
<ModelInput value={next} onChange={onChange} disabled={false} />,
|
|
20
|
+
),
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
it('commits a typed model on blur or Enter, not per keystroke', () => {
|
|
25
|
+
const { onChange, model, update } = renderInput({ reasoning: 'high' })
|
|
26
|
+
fireEvent.change(model, { target: { value: 'openai/gpt' } })
|
|
27
|
+
fireEvent.change(model, { target: { value: ' provider/model ' } })
|
|
28
|
+
expect(onChange).not.toHaveBeenCalled()
|
|
29
|
+
expect(model.value).toBe(' provider/model ')
|
|
30
|
+
fireEvent.blur(model)
|
|
31
|
+
expect(onChange).toHaveBeenCalledExactlyOnceWith({
|
|
32
|
+
reasoning: 'high',
|
|
33
|
+
model: 'provider/model',
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
update({ reasoning: 'high', model: 'provider/model' })
|
|
37
|
+
expect(model.value).toBe('provider/model')
|
|
38
|
+
fireEvent.change(model, { target: { value: '' } })
|
|
39
|
+
fireEvent.keyDown(model, { key: 'Enter' })
|
|
40
|
+
expect(onChange).toHaveBeenLastCalledWith({ reasoning: 'high' })
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
it('commits reasoning and speed immediately and omits defaults', () => {
|
|
44
|
+
const { onChange } = renderInput({
|
|
45
|
+
model: 'provider/model',
|
|
46
|
+
reasoning: 'low',
|
|
47
|
+
})
|
|
48
|
+
fireEvent.change(screen.getByTitle('Thinking level'), {
|
|
49
|
+
target: { value: 'provider-default' },
|
|
50
|
+
})
|
|
51
|
+
expect(onChange).toHaveBeenLastCalledWith({ model: 'provider/model' })
|
|
52
|
+
fireEvent.click(screen.getByRole('button', { name: 'Prefer fast' }))
|
|
53
|
+
expect(onChange).toHaveBeenLastCalledWith({
|
|
54
|
+
model: 'provider/model',
|
|
55
|
+
reasoning: 'low',
|
|
56
|
+
speed: 'fast',
|
|
57
|
+
})
|
|
58
|
+
})
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { useState, type ReactNode } from 'react'
|
|
4
|
+
import { reasoningLevels, type ReasoningLevel } from 'experimental-a2/ai'
|
|
5
|
+
import { agentModel, type ModelChoice } from './model'
|
|
6
|
+
|
|
7
|
+
const choose = <K extends keyof ModelChoice>(
|
|
8
|
+
settings: ModelChoice,
|
|
9
|
+
key: K,
|
|
10
|
+
value: ModelChoice[K] | undefined,
|
|
11
|
+
): ModelChoice => {
|
|
12
|
+
const { [key]: _previous, ...rest } = settings
|
|
13
|
+
return value === undefined ? rest : { ...rest, [key]: value }
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function ModelInput({
|
|
17
|
+
value,
|
|
18
|
+
onChange,
|
|
19
|
+
disabled,
|
|
20
|
+
}: {
|
|
21
|
+
value: ModelChoice
|
|
22
|
+
onChange: (settings: ModelChoice) => void
|
|
23
|
+
disabled: boolean
|
|
24
|
+
}): ReactNode {
|
|
25
|
+
// A choice is a durable session event, so a typed model id commits on
|
|
26
|
+
// blur or Enter rather than once per keystroke.
|
|
27
|
+
const [draft, setDraft] = useState<string | null>(null)
|
|
28
|
+
const model = draft ?? value.model ?? ''
|
|
29
|
+
const commit = (): void => {
|
|
30
|
+
if (draft === null) return
|
|
31
|
+
setDraft(null)
|
|
32
|
+
const next = draft.trim() || undefined
|
|
33
|
+
if (next !== value.model) onChange(choose(value, 'model', next))
|
|
34
|
+
}
|
|
35
|
+
return (
|
|
36
|
+
<div className="agent-model-controls">
|
|
37
|
+
<label className="agent-model-choice">
|
|
38
|
+
<span className="sr-only">Model</span>
|
|
39
|
+
<input
|
|
40
|
+
type="text"
|
|
41
|
+
value={model}
|
|
42
|
+
placeholder={agentModel}
|
|
43
|
+
size={Math.max(model.length || agentModel.length, 12)}
|
|
44
|
+
onChange={(event) => setDraft(event.target.value)}
|
|
45
|
+
onBlur={commit}
|
|
46
|
+
onKeyDown={(event) => {
|
|
47
|
+
if (event.key !== 'Enter' || event.nativeEvent.isComposing) return
|
|
48
|
+
event.preventDefault()
|
|
49
|
+
commit()
|
|
50
|
+
}}
|
|
51
|
+
disabled={disabled}
|
|
52
|
+
autoComplete="off"
|
|
53
|
+
autoCorrect="off"
|
|
54
|
+
autoCapitalize="none"
|
|
55
|
+
spellCheck={false}
|
|
56
|
+
/>
|
|
57
|
+
</label>
|
|
58
|
+
<label className="agent-thinking-choice">
|
|
59
|
+
<span className="sr-only">Thinking level</span>
|
|
60
|
+
<select
|
|
61
|
+
title="Thinking level"
|
|
62
|
+
value={value.reasoning ?? 'provider-default'}
|
|
63
|
+
onChange={(event) => {
|
|
64
|
+
const level = event.target.value as ReasoningLevel
|
|
65
|
+
onChange(
|
|
66
|
+
choose(
|
|
67
|
+
value,
|
|
68
|
+
'reasoning',
|
|
69
|
+
level === 'provider-default' ? undefined : level,
|
|
70
|
+
),
|
|
71
|
+
)
|
|
72
|
+
}}
|
|
73
|
+
disabled={disabled}
|
|
74
|
+
>
|
|
75
|
+
{reasoningLevels.map((level) => (
|
|
76
|
+
<option key={level} value={level}>
|
|
77
|
+
{level === 'provider-default'
|
|
78
|
+
? 'Default'
|
|
79
|
+
: level === 'xhigh'
|
|
80
|
+
? 'Extra high'
|
|
81
|
+
: level[0]!.toUpperCase() + level.slice(1)}
|
|
82
|
+
</option>
|
|
83
|
+
))}
|
|
84
|
+
</select>
|
|
85
|
+
</label>
|
|
86
|
+
<button
|
|
87
|
+
type="button"
|
|
88
|
+
className="agent-fast-toggle"
|
|
89
|
+
aria-label="Prefer fast"
|
|
90
|
+
aria-pressed={value.speed === 'fast'}
|
|
91
|
+
title="Prefer fast: uses the faster tier when available. May cost more; otherwise runs at standard speed."
|
|
92
|
+
onClick={() =>
|
|
93
|
+
onChange(
|
|
94
|
+
choose(value, 'speed', value.speed === 'fast' ? undefined : 'fast'),
|
|
95
|
+
)
|
|
96
|
+
}
|
|
97
|
+
disabled={disabled}
|
|
98
|
+
>
|
|
99
|
+
<svg
|
|
100
|
+
width="18"
|
|
101
|
+
height="18"
|
|
102
|
+
viewBox="0 0 24 24"
|
|
103
|
+
fill={value.speed === 'fast' ? 'currentColor' : 'none'}
|
|
104
|
+
stroke="currentColor"
|
|
105
|
+
strokeWidth="1.7"
|
|
106
|
+
strokeLinecap="round"
|
|
107
|
+
strokeLinejoin="round"
|
|
108
|
+
aria-hidden="true"
|
|
109
|
+
>
|
|
110
|
+
<path d="M13 2 3 14h9l-1 8 10-12h-9l1-8Z" />
|
|
111
|
+
</svg>
|
|
112
|
+
</button>
|
|
113
|
+
</div>
|
|
114
|
+
)
|
|
115
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { expect, it } from 'vitest'
|
|
2
|
+
import {
|
|
3
|
+
agentModel,
|
|
4
|
+
compactionOptions,
|
|
5
|
+
demoAgent,
|
|
6
|
+
modelSettings,
|
|
7
|
+
type AgentEvent,
|
|
8
|
+
} from './model'
|
|
9
|
+
|
|
10
|
+
const event = <T extends AgentEvent['type']>(
|
|
11
|
+
index: number,
|
|
12
|
+
type: T,
|
|
13
|
+
payload: Extract<AgentEvent, { type: T }>['payload'],
|
|
14
|
+
): AgentEvent =>
|
|
15
|
+
({
|
|
16
|
+
id: `event-${index}`,
|
|
17
|
+
sessionId: 's',
|
|
18
|
+
index,
|
|
19
|
+
createdAt: new Date(0),
|
|
20
|
+
type,
|
|
21
|
+
payload,
|
|
22
|
+
}) as AgentEvent
|
|
23
|
+
|
|
24
|
+
it('folds compaction settings beside the AI state', () => {
|
|
25
|
+
const { reducer } = demoAgent
|
|
26
|
+
let state = reducer.fold(
|
|
27
|
+
reducer.initialState,
|
|
28
|
+
event(1, 'compaction.settings.updated', { mode: 'disabled' }),
|
|
29
|
+
)
|
|
30
|
+
state = reducer.fold(
|
|
31
|
+
state,
|
|
32
|
+
event(2, 'ai.message.created', {
|
|
33
|
+
message: { id: 'user', role: 'user', parts: [] },
|
|
34
|
+
}),
|
|
35
|
+
)
|
|
36
|
+
expect(state.compactionSettings).toEqual({ mode: 'disabled' })
|
|
37
|
+
expect(state.compactionSettingsId).toBe('event-1')
|
|
38
|
+
expect(state.messages.map((message) => message.id)).toEqual(['user'])
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
it.each([
|
|
42
|
+
[{ mode: 'automatic' } as const, {}],
|
|
43
|
+
[{ mode: 'manual', thresholdTokens: 1 } as const, { thresholdTokens: 1 }],
|
|
44
|
+
[{ mode: 'disabled' } as const, false],
|
|
45
|
+
])('maps %j to compaction options', (settings, options) => {
|
|
46
|
+
const state = demoAgent.reducer.fold(
|
|
47
|
+
demoAgent.reducer.initialState,
|
|
48
|
+
event(1, 'compaction.settings.updated', settings),
|
|
49
|
+
)
|
|
50
|
+
expect(compactionOptions(state.compactionSettings)).toEqual(options)
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
it('folds the model choice and fills in the default model', () => {
|
|
54
|
+
const { reducer } = demoAgent
|
|
55
|
+
const state = reducer.fold(
|
|
56
|
+
reducer.initialState,
|
|
57
|
+
event(1, 'model.choice.updated', { reasoning: 'high', speed: 'fast' }),
|
|
58
|
+
)
|
|
59
|
+
expect(reducer.initialState.modelChoice).toEqual({})
|
|
60
|
+
expect(modelSettings(state.modelChoice)).toEqual({
|
|
61
|
+
model: agentModel,
|
|
62
|
+
reasoning: 'high',
|
|
63
|
+
speed: 'fast',
|
|
64
|
+
})
|
|
65
|
+
expect(
|
|
66
|
+
modelSettings(
|
|
67
|
+
reducer.fold(
|
|
68
|
+
state,
|
|
69
|
+
event(2, 'model.choice.updated', { model: 'openai/gpt-5.6-terra' }),
|
|
70
|
+
).modelChoice,
|
|
71
|
+
),
|
|
72
|
+
).toEqual({ model: 'openai/gpt-5.6-terra' })
|
|
73
|
+
})
|
|
@@ -1,6 +1,13 @@
|
|
|
1
|
-
import { agent } from 'experimental-a2/ai'
|
|
2
1
|
import type { ContractEvent } from 'experimental-a2'
|
|
3
|
-
import
|
|
2
|
+
import {
|
|
3
|
+
agentContract,
|
|
4
|
+
reasoningLevels,
|
|
5
|
+
type ReasoningLevel,
|
|
6
|
+
} from 'experimental-a2/ai'
|
|
7
|
+
import type {
|
|
8
|
+
AutomaticCompaction,
|
|
9
|
+
ModelSettings,
|
|
10
|
+
} from 'experimental-a2/ai/server'
|
|
4
11
|
import { z } from 'zod'
|
|
5
12
|
|
|
6
13
|
export const agentModel = 'openai/gpt-5.6-luna'
|
|
@@ -16,36 +23,61 @@ export const compactionSettingsSchema = z.discriminatedUnion('mode', [
|
|
|
16
23
|
|
|
17
24
|
export type CompactionSettings = z.infer<typeof compactionSettingsSchema>
|
|
18
25
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
26
|
+
/** The picker's choice; an omitted field keeps the agent's default. */
|
|
27
|
+
export const modelChoiceSchema = z.strictObject({
|
|
28
|
+
model: z.string().trim().min(1).optional(),
|
|
29
|
+
reasoning: z
|
|
30
|
+
.string()
|
|
31
|
+
.refine((value): value is ReasoningLevel =>
|
|
32
|
+
reasoningLevels.includes(value as ReasoningLevel),
|
|
33
|
+
)
|
|
34
|
+
.optional(),
|
|
35
|
+
speed: z.enum(['fast', 'standard']).optional(),
|
|
22
36
|
})
|
|
23
37
|
|
|
24
|
-
export type
|
|
38
|
+
export type ModelChoice = z.infer<typeof modelChoiceSchema>
|
|
25
39
|
|
|
26
40
|
const automatic: CompactionSettings = { mode: 'automatic' }
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
? { settings: event.payload, id: event.id }
|
|
37
|
-
: { settings: automatic, id: 'default' }
|
|
41
|
+
const noChoice: ModelChoice = {}
|
|
42
|
+
const settingsState: {
|
|
43
|
+
compactionSettings: CompactionSettings
|
|
44
|
+
compactionSettingsId: string
|
|
45
|
+
modelChoice: ModelChoice
|
|
46
|
+
} = {
|
|
47
|
+
compactionSettings: automatic,
|
|
48
|
+
compactionSettingsId: 'default',
|
|
49
|
+
modelChoice: noChoice,
|
|
38
50
|
}
|
|
39
51
|
|
|
40
|
-
export const
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
52
|
+
export const demoAgent = agentContract({
|
|
53
|
+
name: 'agent',
|
|
54
|
+
events: {
|
|
55
|
+
'compaction.settings.updated': compactionSettingsSchema,
|
|
56
|
+
'model.choice.updated': modelChoiceSchema,
|
|
57
|
+
},
|
|
58
|
+
reducer: {
|
|
59
|
+
name: 'a2.playground.agent.v4',
|
|
60
|
+
initialState: settingsState,
|
|
61
|
+
fold: (state, event) =>
|
|
62
|
+
event.type === 'compaction.settings.updated'
|
|
63
|
+
? {
|
|
64
|
+
...state,
|
|
65
|
+
compactionSettings: event.payload,
|
|
66
|
+
compactionSettingsId: event.id,
|
|
67
|
+
}
|
|
68
|
+
: event.type === 'model.choice.updated'
|
|
69
|
+
? { ...state, modelChoice: event.payload }
|
|
70
|
+
: state,
|
|
71
|
+
},
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
export type AgentEvent = ContractEvent<typeof demoAgent.events>
|
|
75
|
+
export type AgentState = typeof demoAgent.reducer.initialState
|
|
76
|
+
|
|
77
|
+
export const modelSettings = (choice: ModelChoice): ModelSettings => ({
|
|
78
|
+
...choice,
|
|
79
|
+
model: choice.model ?? agentModel,
|
|
80
|
+
})
|
|
49
81
|
|
|
50
82
|
export function compactionOptions(
|
|
51
83
|
settings: CompactionSettings,
|
|
@@ -4,11 +4,12 @@ import type {
|
|
|
4
4
|
KeyboardEvent as ReactKeyboardEvent,
|
|
5
5
|
ReactNode,
|
|
6
6
|
} from 'react'
|
|
7
|
-
import { useState } from 'react'
|
|
7
|
+
import { useId, useState } from 'react'
|
|
8
8
|
import { useRouter } from 'next/navigation'
|
|
9
|
-
import type { UIMessage } from 'ai'
|
|
10
9
|
import { inputs } from 'experimental-a2/ai'
|
|
10
|
+
import type { ModelChoice } from './model'
|
|
11
11
|
import { agentClient } from './session'
|
|
12
|
+
import { ModelInput } from './model-input'
|
|
12
13
|
|
|
13
14
|
const REMINDER_PROMPT =
|
|
14
15
|
'Set a reminder for yourself in 37 seconds to check checkout-api again.'
|
|
@@ -33,6 +34,8 @@ export function NewAgentSession({
|
|
|
33
34
|
remindersEnabled: boolean
|
|
34
35
|
}): ReactNode {
|
|
35
36
|
const router = useRouter()
|
|
37
|
+
const promptId = useId()
|
|
38
|
+
const [choice, setChoice] = useState<ModelChoice>({})
|
|
36
39
|
const [prompt, setPrompt] = useState(
|
|
37
40
|
remindersEnabled ? REMINDER_PROMPT : INCIDENT_PROMPT,
|
|
38
41
|
)
|
|
@@ -46,17 +49,21 @@ export function NewAgentSession({
|
|
|
46
49
|
if (!text || submitting) return
|
|
47
50
|
|
|
48
51
|
const agentId = crypto.randomUUID().slice(0, 8)
|
|
49
|
-
const message: UIMessage = {
|
|
50
|
-
id: crypto.randomUUID(),
|
|
51
|
-
role: 'user',
|
|
52
|
-
parts: [{ type: 'text', text }],
|
|
53
|
-
}
|
|
54
52
|
|
|
55
53
|
setPrompt('')
|
|
56
54
|
setSubmitting(true)
|
|
57
55
|
setError(null)
|
|
58
56
|
try {
|
|
59
|
-
await agentClient.session(agentId).push(
|
|
57
|
+
await agentClient.session(agentId).push(
|
|
58
|
+
...(Object.keys(choice).length === 0
|
|
59
|
+
? []
|
|
60
|
+
: [{ type: 'model.choice.updated' as const, payload: choice }]),
|
|
61
|
+
...inputs.message({
|
|
62
|
+
id: crypto.randomUUID(),
|
|
63
|
+
role: 'user',
|
|
64
|
+
parts: [{ type: 'text', text }],
|
|
65
|
+
}),
|
|
66
|
+
)
|
|
60
67
|
router.push(`/agent/${agentId}`)
|
|
61
68
|
} catch (err) {
|
|
62
69
|
setPrompt((current) => (current === '' ? submittedPrompt : current))
|
|
@@ -75,17 +82,28 @@ export function NewAgentSession({
|
|
|
75
82
|
</p>
|
|
76
83
|
</div>
|
|
77
84
|
<form className="agent-form" onSubmit={(event) => void start(event)}>
|
|
78
|
-
<
|
|
79
|
-
Message
|
|
80
|
-
<
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
85
|
+
<div className="agent-message-field">
|
|
86
|
+
<label htmlFor={promptId}>Message</label>
|
|
87
|
+
<div className="agent-composer">
|
|
88
|
+
<textarea
|
|
89
|
+
id={promptId}
|
|
90
|
+
autoFocus
|
|
91
|
+
rows={3}
|
|
92
|
+
placeholder="Message the agent…"
|
|
93
|
+
value={prompt}
|
|
94
|
+
onChange={(event) => setPrompt(event.target.value)}
|
|
95
|
+
onKeyDown={submitOnEnter}
|
|
96
|
+
disabled={submitting}
|
|
97
|
+
/>
|
|
98
|
+
<div className="agent-composer-toolbar">
|
|
99
|
+
<ModelInput
|
|
100
|
+
value={choice}
|
|
101
|
+
onChange={setChoice}
|
|
102
|
+
disabled={submitting}
|
|
103
|
+
/>
|
|
104
|
+
</div>
|
|
105
|
+
</div>
|
|
106
|
+
</div>
|
|
89
107
|
<div className="agent-form-actions">
|
|
90
108
|
<button type="submit" disabled={submitting || !prompt.trim()}>
|
|
91
109
|
{submitting ? 'Opening…' : 'Send to agent'}
|
|
@@ -15,10 +15,10 @@ export default function AgentPage(): ReactNode {
|
|
|
15
15
|
<NewAgentSession remindersEnabled={agentScheduler !== undefined} />
|
|
16
16
|
<p className="hint">
|
|
17
17
|
Each durable generation runs one AI SDK <code>streamText</code> step
|
|
18
|
-
with
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
18
|
+
with your chosen model through Vercel AI Gateway. Local development
|
|
19
|
+
needs Gateway credentials and a linked Vercel project for Sandbox;
|
|
20
|
+
Vercel deployments use their OIDC token. Reminder delivery also needs
|
|
21
|
+
the configured scheduler.
|
|
22
22
|
</p>
|
|
23
23
|
</article>
|
|
24
24
|
)
|
|
@@ -4,16 +4,13 @@ import { createHash } from 'node:crypto'
|
|
|
4
4
|
import { Sandbox } from '@vercel/sandbox'
|
|
5
5
|
import { tool } from 'ai'
|
|
6
6
|
import { handlerContext, inputs } from 'experimental-a2/ai'
|
|
7
|
-
import {
|
|
7
|
+
import { agent } from 'experimental-a2/ai/server'
|
|
8
8
|
import { vercelQueues } from 'experimental-a2/scheduler-vercel'
|
|
9
|
+
import { otel } from 'experimental-a2/otel'
|
|
9
10
|
import { z } from 'zod'
|
|
10
11
|
import { store } from '@/lib/store'
|
|
11
|
-
import {
|
|
12
|
-
|
|
13
|
-
compactionOptions,
|
|
14
|
-
compactionSettings,
|
|
15
|
-
demoAgent,
|
|
16
|
-
} from './model'
|
|
12
|
+
import { modelTelemetry, traceMetadata } from './telemetry'
|
|
13
|
+
import { compactionOptions, demoAgent, modelSettings } from './model'
|
|
17
14
|
|
|
18
15
|
const maxReminderDelaySeconds = 6 * 24 * 60 * 60
|
|
19
16
|
const bashTimeoutMs = 120_000
|
|
@@ -132,13 +129,23 @@ export const agentScheduler =
|
|
|
132
129
|
? vercelQueues({ topic: 'a2-agent' })
|
|
133
130
|
: undefined
|
|
134
131
|
|
|
135
|
-
|
|
132
|
+
const telemetry = otel()
|
|
133
|
+
|
|
134
|
+
export const agentServer = agent({
|
|
135
|
+
contract: demoAgent,
|
|
136
136
|
...(store ? { store } : {}),
|
|
137
137
|
...(agentScheduler ? { scheduler: agentScheduler } : {}),
|
|
138
|
-
|
|
139
|
-
model:
|
|
140
|
-
|
|
141
|
-
|
|
138
|
+
telemetry,
|
|
139
|
+
model: ({ state }) => modelSettings(state.modelChoice),
|
|
140
|
+
harness: {
|
|
141
|
+
type: 'ai-sdk',
|
|
142
|
+
messageMetadata: traceMetadata,
|
|
143
|
+
compaction: ({ state }) => compactionOptions(state.compactionSettings),
|
|
144
|
+
generation: () => ({
|
|
145
|
+
telemetry: modelTelemetry(),
|
|
146
|
+
toolApproval: { restartService: 'user-approval' },
|
|
147
|
+
}),
|
|
148
|
+
},
|
|
142
149
|
tools,
|
|
143
150
|
instructions: `You are an incident-response agent for the playground.
|
|
144
151
|
When responding to an incident, inspect checkout-api before taking action. Its
|
|
@@ -154,7 +161,4 @@ it again.
|
|
|
154
161
|
|
|
155
162
|
When the operator asks you to run a shell command, call bash. Make one bash call
|
|
156
163
|
at a time and wait for its result before calling it again. Be concise.`,
|
|
157
|
-
generation: {
|
|
158
|
-
toolApproval: { restartService: 'user-approval' },
|
|
159
|
-
},
|
|
160
164
|
})
|
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
import { createClient } from 'experimental-a2/client'
|
|
3
|
-
import {
|
|
4
|
-
import { demoAgent } from './model'
|
|
3
|
+
import type { UseSessionResult } from 'experimental-a2/react'
|
|
4
|
+
import { demoAgent, type AgentState } from './model'
|
|
5
5
|
|
|
6
|
+
/** Shared by the first push and the session page, so one live session serves both. */
|
|
6
7
|
export const agentClient = createClient({
|
|
7
|
-
|
|
8
|
+
contract: demoAgent,
|
|
8
9
|
api: '/agent/events',
|
|
9
10
|
})
|
|
10
11
|
|
|
11
|
-
export
|
|
12
|
-
client: agentClient,
|
|
13
|
-
})
|
|
12
|
+
export type AgentSession = UseSessionResult<typeof demoAgent.events, AgentState>
|