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
package/dist/ai-pi.js
ADDED
|
@@ -0,0 +1,904 @@
|
|
|
1
|
+
import { u as ambientToolScopeStorage } from "./internal-B2T3U4Rq.js";
|
|
2
|
+
import { t as boundedCache } from "./bounded-cache-CPHWF-8h.js";
|
|
3
|
+
import { c as pending, d as saveHarness, i as gatewayCredential, l as readHarness, n as deferred, r as defineHarness, t as Divergence } from "./ai-harness-BuLh_EYi.js";
|
|
4
|
+
import { createHash } from "node:crypto";
|
|
5
|
+
import { asSchema, jsonSchema } from "ai";
|
|
6
|
+
import { readFile, unlink } from "node:fs/promises";
|
|
7
|
+
import { tmpdir } from "node:os";
|
|
8
|
+
import { basename, dirname, join } from "node:path";
|
|
9
|
+
//#region src/ai-pi.ts
|
|
10
|
+
/**
|
|
11
|
+
* experimental-a2/ai/pi — `pi()`: the Pi harness for `agent({ harness })`.
|
|
12
|
+
* Server-only; Pi comes from the optional `@earendil-works/pi-coding-agent`
|
|
13
|
+
* and `@earendil-works/pi-ai` peer dependencies.
|
|
14
|
+
*/
|
|
15
|
+
/**
|
|
16
|
+
* The Pi harness. Every model call Pi makes is one durable step; A2 rebuilds
|
|
17
|
+
* Pi's session for each step from its latest snapshot and the log since.
|
|
18
|
+
*/
|
|
19
|
+
function pi(options) {
|
|
20
|
+
if (typeof options?.sandbox !== "function") throw new TypeError("pi() requires sandbox");
|
|
21
|
+
return defineHarness("pi", {
|
|
22
|
+
sandbox: options.sandbox,
|
|
23
|
+
tools: builtinTools,
|
|
24
|
+
step: (step) => runStep(step, async () => options.sandbox({
|
|
25
|
+
session: { id: step.sessionId },
|
|
26
|
+
signal: step.signal
|
|
27
|
+
}))
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
const SNAPSHOT_VERSION = 1;
|
|
31
|
+
const GATEWAY = "vercel-ai-gateway";
|
|
32
|
+
const MAX_TRANSFER_BYTES = 4194304;
|
|
33
|
+
const MAX_TRANSFER = "4 MiB";
|
|
34
|
+
const BUILTINS = [
|
|
35
|
+
"read",
|
|
36
|
+
"write",
|
|
37
|
+
"edit",
|
|
38
|
+
"bash"
|
|
39
|
+
];
|
|
40
|
+
function endStep(state) {
|
|
41
|
+
state.over = true;
|
|
42
|
+
state.stepOver.resolve();
|
|
43
|
+
}
|
|
44
|
+
const diverged = (message) => new Divergence("Pi", message);
|
|
45
|
+
async function runStep(step, sandbox) {
|
|
46
|
+
const modules = await loadPi();
|
|
47
|
+
const credential = gatewayCredential("pi");
|
|
48
|
+
const name = `${step.agentName}.a2.harness.pi`;
|
|
49
|
+
const { replayAll, ...first } = await readHarness({
|
|
50
|
+
step,
|
|
51
|
+
name,
|
|
52
|
+
parse: savedSession
|
|
53
|
+
});
|
|
54
|
+
let read = first;
|
|
55
|
+
let results = read.results;
|
|
56
|
+
const cwd = read.saved?.cwd ?? await sessionCwd({
|
|
57
|
+
key: cwdKey(step.agentName, step.sessionId),
|
|
58
|
+
sandbox
|
|
59
|
+
});
|
|
60
|
+
let completion = {};
|
|
61
|
+
let session;
|
|
62
|
+
let compacting = false;
|
|
63
|
+
let turn;
|
|
64
|
+
const fail = (state, error) => {
|
|
65
|
+
state.failure ??= error;
|
|
66
|
+
endStep(state);
|
|
67
|
+
session?.abort();
|
|
68
|
+
throw state.failure;
|
|
69
|
+
};
|
|
70
|
+
let controller;
|
|
71
|
+
let closed = false;
|
|
72
|
+
const emit = (chunk) => {
|
|
73
|
+
if (!closed) controller.enqueue(chunk);
|
|
74
|
+
};
|
|
75
|
+
const record = (data) => emit({
|
|
76
|
+
type: "data-a2-pi",
|
|
77
|
+
data,
|
|
78
|
+
transient: true
|
|
79
|
+
});
|
|
80
|
+
const answer = async (out, model, context, options) => {
|
|
81
|
+
const state = turn;
|
|
82
|
+
if (state.failure) throw state.failure;
|
|
83
|
+
if (state.over) {
|
|
84
|
+
replay(out, interrupted(model));
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
const kind = compacting ? "compaction" : "step";
|
|
88
|
+
const recorded = state.calls[state.next];
|
|
89
|
+
if (recorded !== void 0) {
|
|
90
|
+
if (recorded.kind !== kind) fail(state, diverged(`call ${state.next} is not a ${kind}`));
|
|
91
|
+
state.next++;
|
|
92
|
+
for (const text of recorded.steers) await session.steer(text);
|
|
93
|
+
replay(out, recorded.response);
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
if (state.plan.end === "stop") fail(state, diverged("Pi continued a turn the log ended"));
|
|
97
|
+
if (state.plan.end === "interrupted") {
|
|
98
|
+
replay(out, interrupted(model));
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
if (kind === "step" && state.liveStep) {
|
|
102
|
+
endStep(state);
|
|
103
|
+
replay(out, interrupted(model));
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
if (kind === "step") state.liveStep = true;
|
|
107
|
+
const upstream = realStream(model, context, {
|
|
108
|
+
...options,
|
|
109
|
+
headers: {
|
|
110
|
+
...options?.headers,
|
|
111
|
+
...gatewayHeaders(credential)
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
const live = kind === "step" ? liveStep() : void 0;
|
|
115
|
+
for await (const event of upstream) {
|
|
116
|
+
live?.(event);
|
|
117
|
+
if (event.type === "done" || event.type === "error") {
|
|
118
|
+
const response = event.type === "done" ? event.message : event.error;
|
|
119
|
+
if (kind === "compaction") state.compactions.push(response);
|
|
120
|
+
if (event.type === "error" && event.reason === "error") {
|
|
121
|
+
state.failure ??= new Error(response.errorMessage ?? "the Pi model call failed");
|
|
122
|
+
endStep(state);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
out.push(event);
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
/** Streams one live step response as UI chunks and records it. */
|
|
129
|
+
const liveStep = () => {
|
|
130
|
+
const ref = (index) => `pi:${step.requestIndex}:${index}`;
|
|
131
|
+
const started = /* @__PURE__ */ new Set();
|
|
132
|
+
return (event) => {
|
|
133
|
+
switch (event.type) {
|
|
134
|
+
case "start":
|
|
135
|
+
emit({ type: "start-step" });
|
|
136
|
+
break;
|
|
137
|
+
case "text_start":
|
|
138
|
+
emit({
|
|
139
|
+
type: "text-start",
|
|
140
|
+
id: ref(event.contentIndex)
|
|
141
|
+
});
|
|
142
|
+
break;
|
|
143
|
+
case "text_delta":
|
|
144
|
+
emit({
|
|
145
|
+
type: "text-delta",
|
|
146
|
+
id: ref(event.contentIndex),
|
|
147
|
+
delta: event.delta
|
|
148
|
+
});
|
|
149
|
+
break;
|
|
150
|
+
case "text_end":
|
|
151
|
+
emit({
|
|
152
|
+
type: "text-end",
|
|
153
|
+
id: ref(event.contentIndex)
|
|
154
|
+
});
|
|
155
|
+
break;
|
|
156
|
+
case "thinking_start":
|
|
157
|
+
emit({
|
|
158
|
+
type: "reasoning-start",
|
|
159
|
+
id: ref(event.contentIndex)
|
|
160
|
+
});
|
|
161
|
+
break;
|
|
162
|
+
case "thinking_delta":
|
|
163
|
+
emit({
|
|
164
|
+
type: "reasoning-delta",
|
|
165
|
+
id: ref(event.contentIndex),
|
|
166
|
+
delta: event.delta
|
|
167
|
+
});
|
|
168
|
+
break;
|
|
169
|
+
case "thinking_end":
|
|
170
|
+
emit({
|
|
171
|
+
type: "reasoning-end",
|
|
172
|
+
id: ref(event.contentIndex)
|
|
173
|
+
});
|
|
174
|
+
break;
|
|
175
|
+
case "toolcall_start":
|
|
176
|
+
case "toolcall_delta": {
|
|
177
|
+
const call = event.partial.content[event.contentIndex];
|
|
178
|
+
if (!call.id) break;
|
|
179
|
+
if (!started.has(call.id)) {
|
|
180
|
+
started.add(call.id);
|
|
181
|
+
emit({
|
|
182
|
+
type: "tool-input-start",
|
|
183
|
+
toolCallId: call.id,
|
|
184
|
+
toolName: call.name
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
if (event.type === "toolcall_delta") emit({
|
|
188
|
+
type: "tool-input-delta",
|
|
189
|
+
toolCallId: call.id,
|
|
190
|
+
inputTextDelta: event.delta
|
|
191
|
+
});
|
|
192
|
+
break;
|
|
193
|
+
}
|
|
194
|
+
case "done":
|
|
195
|
+
case "error": {
|
|
196
|
+
const response = event.type === "done" ? event.message : event.error;
|
|
197
|
+
if (event.type === "done") for (const block of response.content) {
|
|
198
|
+
if (block.type !== "toolCall") continue;
|
|
199
|
+
if (!started.has(block.id)) emit({
|
|
200
|
+
type: "tool-input-start",
|
|
201
|
+
toolCallId: block.id,
|
|
202
|
+
toolName: block.name
|
|
203
|
+
});
|
|
204
|
+
emit(response.stopReason === "length" ? {
|
|
205
|
+
type: "tool-input-error",
|
|
206
|
+
toolCallId: block.id,
|
|
207
|
+
toolName: block.name,
|
|
208
|
+
input: block.arguments,
|
|
209
|
+
errorText: "The response hit the output token limit before this call was complete."
|
|
210
|
+
} : {
|
|
211
|
+
type: "tool-input-available",
|
|
212
|
+
toolCallId: block.id,
|
|
213
|
+
toolName: block.name,
|
|
214
|
+
input: block.arguments
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
record({
|
|
218
|
+
call: "step",
|
|
219
|
+
response: storedResponse(response, ref)
|
|
220
|
+
});
|
|
221
|
+
completion = {
|
|
222
|
+
finishReason: finishReason(response.stopReason),
|
|
223
|
+
usage: modelUsage(response.usage)
|
|
224
|
+
};
|
|
225
|
+
break;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
};
|
|
229
|
+
};
|
|
230
|
+
const toolResult = async (toolCallId, signal, shape) => {
|
|
231
|
+
const state = turn;
|
|
232
|
+
if (state.failure) throw state.failure;
|
|
233
|
+
const result = results.get(toolCallId);
|
|
234
|
+
if (result !== void 0) return answeredTool(result, shape);
|
|
235
|
+
if (state.plan.end === "live" && state.liveStep) {
|
|
236
|
+
endStep(state);
|
|
237
|
+
return pending(signal);
|
|
238
|
+
}
|
|
239
|
+
if (state.plan.end === "interrupted") {
|
|
240
|
+
session.abort();
|
|
241
|
+
return pending(signal);
|
|
242
|
+
}
|
|
243
|
+
return fail(state, diverged(`no recorded result for ${toolCallId}`));
|
|
244
|
+
};
|
|
245
|
+
const runtime = await modules.agent.ModelRuntime.create({
|
|
246
|
+
credentials: new modules.ai.InMemoryCredentialStore(),
|
|
247
|
+
modelsPath: null,
|
|
248
|
+
allowModelNetwork: false,
|
|
249
|
+
refreshOnCreate: false
|
|
250
|
+
});
|
|
251
|
+
await runtime.setRuntimeApiKey(GATEWAY, credential.token);
|
|
252
|
+
const model = runtime.getModel(GATEWAY, step.model);
|
|
253
|
+
if (model === void 0) throw new Error(`Pi does not know the Gateway model ${step.model}`);
|
|
254
|
+
const realStream = runtime.streamSimple.bind(runtime);
|
|
255
|
+
runtime.streamSimple = (requested, context, options) => {
|
|
256
|
+
const out = modules.ai.createAssistantMessageEventStream();
|
|
257
|
+
answer(out, requested, context, options).catch((error) => {
|
|
258
|
+
const state = turn;
|
|
259
|
+
if (state) state.failure ??= asError(error);
|
|
260
|
+
replay(out, {
|
|
261
|
+
...interrupted(requested),
|
|
262
|
+
stopReason: "error"
|
|
263
|
+
});
|
|
264
|
+
});
|
|
265
|
+
return out;
|
|
266
|
+
};
|
|
267
|
+
const tools = await sessionTools(modules, step.tools, cwd, toolResult);
|
|
268
|
+
const createSession = async (from) => {
|
|
269
|
+
const sessionManager = from === void 0 ? modules.agent.SessionManager.inMemory(cwd, { id: sessionUuid(step.agentName, step.sessionId) }) : modules.agent.SessionManager.inMemory(cwd, { id: from.header.id }, [from.header, ...from.entries]);
|
|
270
|
+
const created = await modules.agent.createAgentSession({
|
|
271
|
+
cwd,
|
|
272
|
+
agentDir: join(tmpdir(), "a2-pi"),
|
|
273
|
+
model,
|
|
274
|
+
...thinkingLevel(step),
|
|
275
|
+
modelRuntime: runtime,
|
|
276
|
+
resourceLoader: resources(modules, step.instructions),
|
|
277
|
+
sessionManager,
|
|
278
|
+
settingsManager: modules.agent.SettingsManager.inMemory({
|
|
279
|
+
retry: {
|
|
280
|
+
enabled: false,
|
|
281
|
+
maxRetries: 0,
|
|
282
|
+
baseDelayMs: 0
|
|
283
|
+
},
|
|
284
|
+
cacheWarming: "off"
|
|
285
|
+
}),
|
|
286
|
+
customTools: tools,
|
|
287
|
+
tools: tools.map((definition) => definition.name)
|
|
288
|
+
});
|
|
289
|
+
created.session.subscribe((event) => {
|
|
290
|
+
if (event.type === "compaction_start") compacting = true;
|
|
291
|
+
if (event.type !== "compaction_end") return;
|
|
292
|
+
compacting = false;
|
|
293
|
+
const state = turn;
|
|
294
|
+
if (state === void 0 || state.over || state.compactions.length === 0) return;
|
|
295
|
+
for (const response of state.compactions.splice(0)) record({
|
|
296
|
+
call: "compaction",
|
|
297
|
+
response
|
|
298
|
+
});
|
|
299
|
+
emit({
|
|
300
|
+
type: "data-a2-compaction",
|
|
301
|
+
id: `pi-compaction:${step.requestIndex}:${compactions++}`,
|
|
302
|
+
data: compactionData(event)
|
|
303
|
+
});
|
|
304
|
+
});
|
|
305
|
+
return created.session;
|
|
306
|
+
};
|
|
307
|
+
let compactions = 0;
|
|
308
|
+
const saveSnapshot = async () => {
|
|
309
|
+
const manager = session.sessionManager;
|
|
310
|
+
const header = manager.getHeader();
|
|
311
|
+
const entries = manager.getEntries();
|
|
312
|
+
const last = entries.findLast((entry) => entry.type === "message" && entry.message.role === "assistant");
|
|
313
|
+
const stop = last?.type === "message" && last.message.role === "assistant" ? last.message.stopReason : void 0;
|
|
314
|
+
if (header === null || step.signal.aborted || stop === "aborted" || stop === "error") return;
|
|
315
|
+
await saveHarness({
|
|
316
|
+
step,
|
|
317
|
+
name,
|
|
318
|
+
state: {
|
|
319
|
+
version: SNAPSHOT_VERSION,
|
|
320
|
+
cwd,
|
|
321
|
+
header,
|
|
322
|
+
entries
|
|
323
|
+
}
|
|
324
|
+
});
|
|
325
|
+
};
|
|
326
|
+
const run = async () => {
|
|
327
|
+
emit({
|
|
328
|
+
type: "start",
|
|
329
|
+
messageId: step.responseMessageId
|
|
330
|
+
});
|
|
331
|
+
try {
|
|
332
|
+
session = await createSession(read.saved);
|
|
333
|
+
} catch (error) {
|
|
334
|
+
if (read.saved === void 0) throw error;
|
|
335
|
+
read = await replayAll();
|
|
336
|
+
results = read.results;
|
|
337
|
+
session = await createSession(void 0);
|
|
338
|
+
}
|
|
339
|
+
for (const plan of read.turns) {
|
|
340
|
+
if (step.signal.aborted) return;
|
|
341
|
+
const state = {
|
|
342
|
+
plan,
|
|
343
|
+
calls: recordedTurn(plan),
|
|
344
|
+
next: 0,
|
|
345
|
+
liveStep: false,
|
|
346
|
+
over: false,
|
|
347
|
+
stepOver: deferred(),
|
|
348
|
+
compactions: []
|
|
349
|
+
};
|
|
350
|
+
turn = state;
|
|
351
|
+
const running = session.prompt(plan.prompt, { expandPromptTemplates: false });
|
|
352
|
+
if (plan.end !== "live") {
|
|
353
|
+
await running.catch((error) => {
|
|
354
|
+
throw state.failure ?? error;
|
|
355
|
+
});
|
|
356
|
+
if (state.failure) throw state.failure;
|
|
357
|
+
continue;
|
|
358
|
+
}
|
|
359
|
+
const ended = await Promise.race([running.then(() => true), state.stepOver.promise.then(() => false)]).catch((error) => {
|
|
360
|
+
throw state.failure ?? error;
|
|
361
|
+
});
|
|
362
|
+
if (!ended) {
|
|
363
|
+
await session.abort();
|
|
364
|
+
await running.catch(() => {});
|
|
365
|
+
}
|
|
366
|
+
if (state.failure) throw state.failure;
|
|
367
|
+
if (ended) await saveSnapshot();
|
|
368
|
+
emit({ type: "finish-step" });
|
|
369
|
+
emit({
|
|
370
|
+
type: "finish",
|
|
371
|
+
...completion.finishReason === void 0 ? {} : { finishReason: completion.finishReason }
|
|
372
|
+
});
|
|
373
|
+
}
|
|
374
|
+
};
|
|
375
|
+
return {
|
|
376
|
+
stream: new ReadableStream({
|
|
377
|
+
start(streamController) {
|
|
378
|
+
controller = streamController;
|
|
379
|
+
const abort = () => void session?.abort();
|
|
380
|
+
step.signal.addEventListener("abort", abort, { once: true });
|
|
381
|
+
(async () => {
|
|
382
|
+
try {
|
|
383
|
+
await run();
|
|
384
|
+
closed = true;
|
|
385
|
+
streamController.close();
|
|
386
|
+
} catch (error) {
|
|
387
|
+
closed = true;
|
|
388
|
+
streamController.error(step.signal.aborted ? step.signal.reason : error);
|
|
389
|
+
} finally {
|
|
390
|
+
step.signal.removeEventListener("abort", abort);
|
|
391
|
+
session?.dispose();
|
|
392
|
+
}
|
|
393
|
+
})();
|
|
394
|
+
},
|
|
395
|
+
cancel() {
|
|
396
|
+
closed = true;
|
|
397
|
+
session?.abort();
|
|
398
|
+
}
|
|
399
|
+
}),
|
|
400
|
+
completion: () => completion
|
|
401
|
+
};
|
|
402
|
+
}
|
|
403
|
+
/** A turn's recorded model calls, in the order Pi made them. */
|
|
404
|
+
function recordedTurn(turn) {
|
|
405
|
+
const calls = [];
|
|
406
|
+
turn.generations.forEach((generation, index) => {
|
|
407
|
+
calls.push(...recordedCalls(generation));
|
|
408
|
+
const steers = turn.steers.get(index);
|
|
409
|
+
const last = calls.findLast((call) => call.kind === "step");
|
|
410
|
+
if (steers !== void 0 && last !== void 0) last.steers.push(...steers);
|
|
411
|
+
});
|
|
412
|
+
return calls;
|
|
413
|
+
}
|
|
414
|
+
function recordedCalls(generation) {
|
|
415
|
+
const texts = /* @__PURE__ */ new Map();
|
|
416
|
+
const inputs = /* @__PURE__ */ new Map();
|
|
417
|
+
const calls = [];
|
|
418
|
+
for (const chunk of generation.chunks) switch (chunk.type) {
|
|
419
|
+
case "text-delta":
|
|
420
|
+
case "reasoning-delta":
|
|
421
|
+
texts.set(chunk.id, (texts.get(chunk.id) ?? "") + chunk.delta);
|
|
422
|
+
break;
|
|
423
|
+
case "tool-input-available":
|
|
424
|
+
case "tool-input-error":
|
|
425
|
+
inputs.set(chunk.toolCallId, chunk.input);
|
|
426
|
+
break;
|
|
427
|
+
case "data-a2-pi": {
|
|
428
|
+
const record = chunk.data;
|
|
429
|
+
calls.push({
|
|
430
|
+
kind: record.call,
|
|
431
|
+
response: record.call === "step" ? restoredResponse(record.response, texts, inputs) : record.response,
|
|
432
|
+
steers: []
|
|
433
|
+
});
|
|
434
|
+
break;
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
if (!calls.some((call) => call.kind === "step")) throw diverged("a completed generation has no recorded response");
|
|
438
|
+
return calls;
|
|
439
|
+
}
|
|
440
|
+
function storedResponse(response, ref) {
|
|
441
|
+
return {
|
|
442
|
+
...response,
|
|
443
|
+
content: response.content.map((block, index) => {
|
|
444
|
+
switch (block.type) {
|
|
445
|
+
case "text": {
|
|
446
|
+
const { text: _, ...rest } = block;
|
|
447
|
+
return {
|
|
448
|
+
...rest,
|
|
449
|
+
ref: ref(index)
|
|
450
|
+
};
|
|
451
|
+
}
|
|
452
|
+
case "thinking": {
|
|
453
|
+
const { thinking: _, ...rest } = block;
|
|
454
|
+
return {
|
|
455
|
+
...rest,
|
|
456
|
+
ref: ref(index)
|
|
457
|
+
};
|
|
458
|
+
}
|
|
459
|
+
case "toolCall": {
|
|
460
|
+
const { arguments: _, ...rest } = block;
|
|
461
|
+
return rest;
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
})
|
|
465
|
+
};
|
|
466
|
+
}
|
|
467
|
+
function restoredResponse(stored, texts, inputs) {
|
|
468
|
+
return {
|
|
469
|
+
...stored,
|
|
470
|
+
content: stored.content.map((block) => {
|
|
471
|
+
if (block.type === "toolCall") return {
|
|
472
|
+
...block,
|
|
473
|
+
arguments: inputs.get(block.id) ?? {}
|
|
474
|
+
};
|
|
475
|
+
const { ref, ...rest } = block;
|
|
476
|
+
const text = texts.get(ref) ?? "";
|
|
477
|
+
return rest.type === "text" ? {
|
|
478
|
+
...rest,
|
|
479
|
+
text
|
|
480
|
+
} : {
|
|
481
|
+
...rest,
|
|
482
|
+
thinking: text
|
|
483
|
+
};
|
|
484
|
+
})
|
|
485
|
+
};
|
|
486
|
+
}
|
|
487
|
+
/** Hands a recorded response to Pi's loop. */
|
|
488
|
+
function replay(out, response) {
|
|
489
|
+
out.push({
|
|
490
|
+
type: "start",
|
|
491
|
+
partial: response
|
|
492
|
+
});
|
|
493
|
+
const reason = response.stopReason;
|
|
494
|
+
if (reason === "error" || reason === "aborted") out.push({
|
|
495
|
+
type: "error",
|
|
496
|
+
reason,
|
|
497
|
+
error: response
|
|
498
|
+
});
|
|
499
|
+
else if (reason === "pending") out.push({
|
|
500
|
+
type: "done",
|
|
501
|
+
reason: "stop",
|
|
502
|
+
message: response
|
|
503
|
+
});
|
|
504
|
+
else out.push({
|
|
505
|
+
type: "done",
|
|
506
|
+
reason,
|
|
507
|
+
message: response
|
|
508
|
+
});
|
|
509
|
+
}
|
|
510
|
+
/** The response of a model call that A2 interrupted. */
|
|
511
|
+
function interrupted(model) {
|
|
512
|
+
return {
|
|
513
|
+
role: "assistant",
|
|
514
|
+
content: [],
|
|
515
|
+
api: model.api,
|
|
516
|
+
provider: model.provider,
|
|
517
|
+
model: model.id,
|
|
518
|
+
usage: {
|
|
519
|
+
input: 0,
|
|
520
|
+
output: 0,
|
|
521
|
+
cacheRead: 0,
|
|
522
|
+
cacheWrite: 0,
|
|
523
|
+
totalTokens: 0,
|
|
524
|
+
cost: {
|
|
525
|
+
input: 0,
|
|
526
|
+
output: 0,
|
|
527
|
+
cacheRead: 0,
|
|
528
|
+
cacheWrite: 0,
|
|
529
|
+
total: 0
|
|
530
|
+
}
|
|
531
|
+
},
|
|
532
|
+
stopReason: "aborted",
|
|
533
|
+
timestamp: Date.now()
|
|
534
|
+
};
|
|
535
|
+
}
|
|
536
|
+
const builtinTools = Object.fromEntries(BUILTINS.map((name) => [name, {
|
|
537
|
+
inputSchema: jsonSchema(async () => builtinDefinition(await loadPi(), name, "/").parameters),
|
|
538
|
+
execute: (input, options) => runBuiltin(name, input, options)
|
|
539
|
+
}]));
|
|
540
|
+
/** Runs one of Pi's tools in the session's sandbox, as Pi's loop would. */
|
|
541
|
+
async function runBuiltin(name, input, options) {
|
|
542
|
+
const sandbox = options.experimental_sandbox;
|
|
543
|
+
if (sandbox === void 0) throw new Error(`pi() runs ${name} in the agent's sandbox`);
|
|
544
|
+
const modules = await loadPi();
|
|
545
|
+
const scope = ambientToolScopeStorage()?.getStore();
|
|
546
|
+
const event = (scope?.context)?.event;
|
|
547
|
+
const definition = builtinDefinition(modules, name, await sessionCwd({
|
|
548
|
+
key: scope?.contract !== void 0 && typeof event?.sessionId === "string" ? cwdKey(scope.contract.name, event.sessionId) : void 0,
|
|
549
|
+
sandbox: async () => sandbox
|
|
550
|
+
}), {
|
|
551
|
+
sandbox,
|
|
552
|
+
signal: options.abortSignal
|
|
553
|
+
});
|
|
554
|
+
const args = modules.ai.validateToolArguments(definition, {
|
|
555
|
+
type: "toolCall",
|
|
556
|
+
id: options.toolCallId,
|
|
557
|
+
name,
|
|
558
|
+
arguments: definition.prepareArguments?.(input) ?? input
|
|
559
|
+
});
|
|
560
|
+
let outputFile;
|
|
561
|
+
try {
|
|
562
|
+
const result = await definition.execute(options.toolCallId, args, options.abortSignal, (update) => {
|
|
563
|
+
outputFile ??= fullOutputPath(update);
|
|
564
|
+
}, void 0);
|
|
565
|
+
outputFile ??= fullOutputPath(result);
|
|
566
|
+
return await recordedResult(result, sandbox);
|
|
567
|
+
} catch (error) {
|
|
568
|
+
outputFile ??= namedOutputFile(error);
|
|
569
|
+
throw error;
|
|
570
|
+
} finally {
|
|
571
|
+
if (outputFile !== void 0) await unlink(outputFile).catch(() => {});
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
const fullOutputPath = (result) => {
|
|
575
|
+
const path = result.details?.fullOutputPath;
|
|
576
|
+
return typeof path === "string" ? path : void 0;
|
|
577
|
+
};
|
|
578
|
+
/** The full-output file an aborted or timed-out bash call's error names. */
|
|
579
|
+
function namedOutputFile(error) {
|
|
580
|
+
if (!(error instanceof Error)) return void 0;
|
|
581
|
+
const path = /Full output: ([^\n]+)\]\n\nCommand [^\n]+$/.exec(error.message)?.[1];
|
|
582
|
+
return path !== void 0 && dirname(path) === tmpdir() && /^pi-bash-[0-9a-f]{16}\.log$/.test(basename(path)) ? path : void 0;
|
|
583
|
+
}
|
|
584
|
+
/**
|
|
585
|
+
* What the log keeps of a result: what Pi's tool-result message holds. A
|
|
586
|
+
* long output's full text moves to the path its result names in the sandbox,
|
|
587
|
+
* where the model's `read` finds it.
|
|
588
|
+
*/
|
|
589
|
+
async function recordedResult(result, sandbox) {
|
|
590
|
+
const { structuredContent: _, ...kept } = result;
|
|
591
|
+
const details = kept.details;
|
|
592
|
+
const path = fullOutputPath(kept);
|
|
593
|
+
if (path !== void 0) try {
|
|
594
|
+
await sandbox.writeBinaryFile({
|
|
595
|
+
path,
|
|
596
|
+
content: await readFile(path)
|
|
597
|
+
});
|
|
598
|
+
} catch {}
|
|
599
|
+
if (details?.truncation === void 0) return kept;
|
|
600
|
+
const { content: __, ...truncation } = details.truncation;
|
|
601
|
+
return {
|
|
602
|
+
...kept,
|
|
603
|
+
details: {
|
|
604
|
+
...details,
|
|
605
|
+
truncation
|
|
606
|
+
}
|
|
607
|
+
};
|
|
608
|
+
}
|
|
609
|
+
const untyped = (definition) => definition;
|
|
610
|
+
function builtinDefinition(modules, name, cwd, call) {
|
|
611
|
+
const sandbox = call?.sandbox;
|
|
612
|
+
const files = call === void 0 ? void 0 : { operations: sandboxFiles(call) };
|
|
613
|
+
switch (name) {
|
|
614
|
+
case "read": return untyped(modules.agent.createReadToolDefinition(cwd, files));
|
|
615
|
+
case "write": return untyped(modules.agent.createWriteToolDefinition(cwd, files));
|
|
616
|
+
case "edit": return untyped(modules.agent.createEditToolDefinition(cwd, files));
|
|
617
|
+
case "bash": return untyped(modules.agent.createBashToolDefinition(cwd, {
|
|
618
|
+
exposeSessionEnvironment: false,
|
|
619
|
+
...sandbox === void 0 ? {} : { operations: sandboxBash(sandbox) }
|
|
620
|
+
}));
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
/**
|
|
624
|
+
* The tools Pi's session declares: Pi's own, and the agent's. Their results
|
|
625
|
+
* come from A2's log; A2's tool handlers ran them.
|
|
626
|
+
*/
|
|
627
|
+
async function sessionTools(modules, tools, cwd, result) {
|
|
628
|
+
return Promise.all(Object.entries(tools).filter(([, tool]) => tool.type !== "provider").map(async ([name, tool]) => {
|
|
629
|
+
const builtin = BUILTINS.find((candidate) => candidate === name && builtinTools[name] === tool);
|
|
630
|
+
if (builtin !== void 0) return Object.assign(builtinDefinition(modules, builtin, cwd), { execute: (toolCallId, _params, signal) => result(toolCallId, signal, "pi") });
|
|
631
|
+
return {
|
|
632
|
+
name,
|
|
633
|
+
label: name,
|
|
634
|
+
description: typeof tool.description === "string" ? tool.description : "",
|
|
635
|
+
parameters: await asSchema(tool.inputSchema).jsonSchema,
|
|
636
|
+
execute: (toolCallId, _params, signal) => result(toolCallId, signal, "tool")
|
|
637
|
+
};
|
|
638
|
+
}));
|
|
639
|
+
}
|
|
640
|
+
function answeredTool(result, shape) {
|
|
641
|
+
if (result.error !== void 0) throw new Error(result.error);
|
|
642
|
+
if (result.denied === true) throw new Error("The tool call was denied.");
|
|
643
|
+
if (shape === "pi") return result.output;
|
|
644
|
+
const output = result.output;
|
|
645
|
+
return {
|
|
646
|
+
content: [{
|
|
647
|
+
type: "text",
|
|
648
|
+
text: typeof output === "string" ? output : JSON.stringify(output) ?? ""
|
|
649
|
+
}],
|
|
650
|
+
details: void 0
|
|
651
|
+
};
|
|
652
|
+
}
|
|
653
|
+
/** Pi's file operations, against the sandbox. */
|
|
654
|
+
function sandboxFiles({ sandbox, signal }) {
|
|
655
|
+
const abortSignal = signal === void 0 ? {} : { abortSignal: signal };
|
|
656
|
+
let checked;
|
|
657
|
+
const readFile = async (path) => {
|
|
658
|
+
if (checked?.path === path) {
|
|
659
|
+
const { bytes } = checked;
|
|
660
|
+
checked = void 0;
|
|
661
|
+
return bytes;
|
|
662
|
+
}
|
|
663
|
+
const stream = await sandbox.readFile({
|
|
664
|
+
path,
|
|
665
|
+
...abortSignal
|
|
666
|
+
});
|
|
667
|
+
if (stream === null) throw Object.assign(/* @__PURE__ */ new Error(`ENOENT: no such file or directory, open '${path}'`), { code: "ENOENT" });
|
|
668
|
+
const chunks = [];
|
|
669
|
+
let size = 0;
|
|
670
|
+
for await (const chunk of stream) {
|
|
671
|
+
size += chunk.byteLength;
|
|
672
|
+
if (size > MAX_TRANSFER_BYTES) throw new Error(`${path} is larger than ${MAX_TRANSFER}. Read or change parts of it with bash, using sed, head, or grep.`);
|
|
673
|
+
chunks.push(chunk);
|
|
674
|
+
}
|
|
675
|
+
return Buffer.concat(chunks);
|
|
676
|
+
};
|
|
677
|
+
return {
|
|
678
|
+
readFile,
|
|
679
|
+
access: async (path) => {
|
|
680
|
+
checked = {
|
|
681
|
+
path,
|
|
682
|
+
bytes: await readFile(path)
|
|
683
|
+
};
|
|
684
|
+
},
|
|
685
|
+
writeFile: async (path, content) => {
|
|
686
|
+
checked = void 0;
|
|
687
|
+
await sandbox.writeTextFile({
|
|
688
|
+
path,
|
|
689
|
+
content,
|
|
690
|
+
...abortSignal
|
|
691
|
+
});
|
|
692
|
+
},
|
|
693
|
+
mkdir: async () => {}
|
|
694
|
+
};
|
|
695
|
+
}
|
|
696
|
+
/** Pi's shell operations, against the sandbox. */
|
|
697
|
+
const sandboxBash = (sandbox) => ({ exec: (command, cwd, { onData, signal, timeout }) => sandboxExec({
|
|
698
|
+
sandbox,
|
|
699
|
+
command,
|
|
700
|
+
workingDirectory: cwd,
|
|
701
|
+
onData,
|
|
702
|
+
signal,
|
|
703
|
+
timeout
|
|
704
|
+
}) });
|
|
705
|
+
/**
|
|
706
|
+
* Runs a command in the sandbox with Pi's error contract. At most
|
|
707
|
+
* `MAX_TRANSFER_BYTES` of its output reach the server; past that, the
|
|
708
|
+
* command is stopped.
|
|
709
|
+
*/
|
|
710
|
+
async function sandboxExec({ sandbox, command, workingDirectory, onData, signal, timeout }) {
|
|
711
|
+
if (signal?.aborted) throw new Error("aborted");
|
|
712
|
+
const stop = new AbortController();
|
|
713
|
+
const abort = () => stop.abort();
|
|
714
|
+
signal?.addEventListener("abort", abort, { once: true });
|
|
715
|
+
let timedOut = false;
|
|
716
|
+
const timer = timeout === void 0 || timeout <= 0 ? void 0 : setTimeout(() => {
|
|
717
|
+
timedOut = true;
|
|
718
|
+
stop.abort();
|
|
719
|
+
}, timeout * 1e3);
|
|
720
|
+
let size = 0;
|
|
721
|
+
let overflowed = false;
|
|
722
|
+
const overflow = (options) => new Error(`The command's output passed ${MAX_TRANSFER}, so it was stopped. Write long output to a file and read parts of it with head, tail, or grep.`, options);
|
|
723
|
+
let exitCode;
|
|
724
|
+
try {
|
|
725
|
+
const process = await sandbox.spawn({
|
|
726
|
+
command,
|
|
727
|
+
...workingDirectory === void 0 ? {} : { workingDirectory },
|
|
728
|
+
abortSignal: stop.signal
|
|
729
|
+
});
|
|
730
|
+
const pipe = async (stream) => {
|
|
731
|
+
for await (const chunk of process[stream]) {
|
|
732
|
+
size += chunk.byteLength;
|
|
733
|
+
if (size > MAX_TRANSFER_BYTES) {
|
|
734
|
+
overflowed = true;
|
|
735
|
+
stop.abort();
|
|
736
|
+
return;
|
|
737
|
+
}
|
|
738
|
+
onData(Buffer.from(chunk), stream);
|
|
739
|
+
}
|
|
740
|
+
};
|
|
741
|
+
const [exited] = await Promise.all([
|
|
742
|
+
process.wait(),
|
|
743
|
+
pipe("stdout"),
|
|
744
|
+
pipe("stderr")
|
|
745
|
+
]);
|
|
746
|
+
exitCode = exited.exitCode;
|
|
747
|
+
} catch (error) {
|
|
748
|
+
if (signal?.aborted) throw new Error("aborted", { cause: error });
|
|
749
|
+
if (timedOut) throw new Error(`timeout:${timeout}`, { cause: error });
|
|
750
|
+
if (overflowed) throw overflow({ cause: error });
|
|
751
|
+
throw error;
|
|
752
|
+
} finally {
|
|
753
|
+
clearTimeout(timer);
|
|
754
|
+
signal?.removeEventListener("abort", abort);
|
|
755
|
+
}
|
|
756
|
+
if (signal?.aborted) throw new Error("aborted");
|
|
757
|
+
if (timedOut) throw new Error(`timeout:${timeout}`);
|
|
758
|
+
if (overflowed) throw overflow();
|
|
759
|
+
return { exitCode };
|
|
760
|
+
}
|
|
761
|
+
const cwds = boundedCache({
|
|
762
|
+
maxEntries: 1e3,
|
|
763
|
+
maxBytes: 1e3
|
|
764
|
+
});
|
|
765
|
+
const cwdKey = (agentName, sessionId) => `${agentName}\u001f${sessionId}`;
|
|
766
|
+
/** A session's working directory in its sandbox: Pi's cwd. */
|
|
767
|
+
function sessionCwd({ key, sandbox }) {
|
|
768
|
+
const cached = key === void 0 ? void 0 : cwds.get(key);
|
|
769
|
+
if (cached !== void 0) return cached;
|
|
770
|
+
const cwd = sandbox().then(async (resolved) => {
|
|
771
|
+
const output = {
|
|
772
|
+
stdout: "",
|
|
773
|
+
stderr: ""
|
|
774
|
+
};
|
|
775
|
+
const decoder = {
|
|
776
|
+
stdout: new TextDecoder(),
|
|
777
|
+
stderr: new TextDecoder()
|
|
778
|
+
};
|
|
779
|
+
const { exitCode } = await sandboxExec({
|
|
780
|
+
sandbox: resolved,
|
|
781
|
+
command: "pwd",
|
|
782
|
+
onData: (data, stream) => {
|
|
783
|
+
output[stream] += decoder[stream].decode(data, { stream: true });
|
|
784
|
+
}
|
|
785
|
+
}).catch((error) => {
|
|
786
|
+
throw new Error("pi() could not read the sandbox's working directory", { cause: error });
|
|
787
|
+
});
|
|
788
|
+
const path = output.stdout.trim();
|
|
789
|
+
if (exitCode !== 0 || !path.startsWith("/")) throw new Error(`pi() could not read the sandbox's working directory: ${(output.stderr || output.stdout).slice(0, 1e3)}`);
|
|
790
|
+
return path;
|
|
791
|
+
});
|
|
792
|
+
if (key !== void 0) {
|
|
793
|
+
cwds.set(key, cwd, 1);
|
|
794
|
+
cwd.catch(() => cwds.delete(key));
|
|
795
|
+
}
|
|
796
|
+
return cwd;
|
|
797
|
+
}
|
|
798
|
+
let loading;
|
|
799
|
+
function loadPi() {
|
|
800
|
+
loading ??= Promise.all([import("@earendil-works/pi-coding-agent"), import("@earendil-works/pi-ai")]).then(([agent, ai]) => ({
|
|
801
|
+
agent,
|
|
802
|
+
ai
|
|
803
|
+
}), (error) => {
|
|
804
|
+
loading = void 0;
|
|
805
|
+
throw new Error("pi() needs @earendil-works/pi-coding-agent and @earendil-works/pi-ai: install them", { cause: error });
|
|
806
|
+
});
|
|
807
|
+
return loading;
|
|
808
|
+
}
|
|
809
|
+
/** Pi's resources: nothing discovered from disk, the agent's instructions appended. */
|
|
810
|
+
function resources(modules, instructions) {
|
|
811
|
+
const runtime = modules.agent.createExtensionRuntime();
|
|
812
|
+
return {
|
|
813
|
+
getExtensions: () => ({
|
|
814
|
+
extensions: [],
|
|
815
|
+
errors: [],
|
|
816
|
+
runtime
|
|
817
|
+
}),
|
|
818
|
+
getSkills: () => ({
|
|
819
|
+
skills: [],
|
|
820
|
+
diagnostics: []
|
|
821
|
+
}),
|
|
822
|
+
getPrompts: () => ({
|
|
823
|
+
prompts: [],
|
|
824
|
+
diagnostics: []
|
|
825
|
+
}),
|
|
826
|
+
getThemes: () => ({
|
|
827
|
+
themes: [],
|
|
828
|
+
diagnostics: []
|
|
829
|
+
}),
|
|
830
|
+
getAgentsFiles: () => ({ agentsFiles: [] }),
|
|
831
|
+
getSystemPrompt: () => void 0,
|
|
832
|
+
getSystemPromptSource: () => void 0,
|
|
833
|
+
getAppendSystemPrompt: () => instructions,
|
|
834
|
+
getAppendSystemPromptSources: () => [],
|
|
835
|
+
extendResources: () => {},
|
|
836
|
+
reload: async () => {}
|
|
837
|
+
};
|
|
838
|
+
}
|
|
839
|
+
function thinkingLevel(step) {
|
|
840
|
+
if (step.reasoning === void 0 || step.reasoning === "provider-default") return {};
|
|
841
|
+
return { thinkingLevel: step.reasoning === "none" ? "off" : step.reasoning };
|
|
842
|
+
}
|
|
843
|
+
function gatewayHeaders(credential) {
|
|
844
|
+
return {
|
|
845
|
+
authorization: `Bearer ${credential.token}`,
|
|
846
|
+
"ai-gateway-auth-method": credential.method
|
|
847
|
+
};
|
|
848
|
+
}
|
|
849
|
+
/** A stable Pi session ID for an A2 session, so Pi's cache affinity holds. */
|
|
850
|
+
function sessionUuid(agentName, sessionId) {
|
|
851
|
+
const hex = createHash("sha256").update(`${agentName}\u001f${sessionId}`).digest("hex");
|
|
852
|
+
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-8${hex.slice(13, 16)}-${hex.slice(16, 20)}-${hex.slice(20, 32)}`;
|
|
853
|
+
}
|
|
854
|
+
function savedSession(state) {
|
|
855
|
+
if (typeof state !== "object" || state === null) return void 0;
|
|
856
|
+
const { version, cwd, header, entries } = state;
|
|
857
|
+
if (version !== SNAPSHOT_VERSION || typeof cwd !== "string" || typeof header !== "object" || header === null || !Array.isArray(entries)) return void 0;
|
|
858
|
+
return {
|
|
859
|
+
cwd,
|
|
860
|
+
header,
|
|
861
|
+
entries
|
|
862
|
+
};
|
|
863
|
+
}
|
|
864
|
+
function compactionData(event) {
|
|
865
|
+
if (event.result === void 0) return { status: event.aborted ? "interrupted" : "failed" };
|
|
866
|
+
return {
|
|
867
|
+
status: "completed",
|
|
868
|
+
inputTokens: event.result.tokensBefore,
|
|
869
|
+
summary: event.result.summary,
|
|
870
|
+
...event.result.usage === void 0 ? {} : { usage: modelUsage(event.result.usage) }
|
|
871
|
+
};
|
|
872
|
+
}
|
|
873
|
+
function finishReason(reason) {
|
|
874
|
+
switch (reason) {
|
|
875
|
+
case "stop": return "stop";
|
|
876
|
+
case "length": return "length";
|
|
877
|
+
case "toolUse": return "tool-calls";
|
|
878
|
+
case "error": return "error";
|
|
879
|
+
default: return "other";
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
function modelUsage(usage) {
|
|
883
|
+
return {
|
|
884
|
+
inputTokens: usage.input + usage.cacheRead + usage.cacheWrite,
|
|
885
|
+
inputTokenDetails: {
|
|
886
|
+
noCacheTokens: usage.input,
|
|
887
|
+
cacheReadTokens: usage.cacheRead,
|
|
888
|
+
cacheWriteTokens: usage.cacheWrite
|
|
889
|
+
},
|
|
890
|
+
outputTokens: usage.output,
|
|
891
|
+
outputTokenDetails: {
|
|
892
|
+
textTokens: usage.reasoning === void 0 ? void 0 : usage.output - usage.reasoning,
|
|
893
|
+
reasoningTokens: usage.reasoning
|
|
894
|
+
},
|
|
895
|
+
totalTokens: usage.totalTokens
|
|
896
|
+
};
|
|
897
|
+
}
|
|
898
|
+
function asError(error) {
|
|
899
|
+
return error instanceof Error ? error : new Error(String(error));
|
|
900
|
+
}
|
|
901
|
+
//#endregion
|
|
902
|
+
export { pi };
|
|
903
|
+
|
|
904
|
+
//# sourceMappingURL=ai-pi.js.map
|