experimental-a2 0.20.0 → 0.21.1
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 +34 -0
- package/README.md +1 -1
- package/dist/{actor-C7b-1arn.d.ts → actor-HnGmrOZZ.d.ts} +5 -5
- package/dist/{actor-C7b-1arn.d.ts.map → actor-HnGmrOZZ.d.ts.map} +1 -1
- package/dist/actor-client.d.ts +1 -1
- package/dist/actor-client.js +2 -2
- package/dist/actor-react.d.ts +3 -3
- package/dist/actor-react.js +5 -5
- package/dist/{actor-shared-t7w8xIKP.js → actor-shared-DFET5q16.js} +3 -3
- package/dist/{actor-shared-t7w8xIKP.js.map → actor-shared-DFET5q16.js.map} +1 -1
- package/dist/actor.d.ts +1 -1
- package/dist/actor.js +54 -43
- package/dist/actor.js.map +1 -1
- package/dist/{ai-Cz3hqVhk.d.ts → ai-CvEnqVfo.d.ts} +8 -8
- package/dist/{ai-Cz3hqVhk.d.ts.map → ai-CvEnqVfo.d.ts.map} +1 -1
- package/dist/{ai-mMiWul1T.js → ai-CzE672Q3.js} +9 -9
- package/dist/ai-CzE672Q3.js.map +1 -0
- package/dist/ai-client.d.ts +2 -2
- package/dist/ai-client.js +2 -2
- package/dist/ai-fx.d.ts +1 -1
- package/dist/ai-fx.js +3 -6
- package/dist/ai-fx.js.map +1 -1
- package/dist/{ai-harness-CV6rXbB3.d.ts → ai-harness-Bfk-Lycn.d.ts} +2 -2
- package/dist/{ai-harness-CV6rXbB3.d.ts.map → ai-harness-Bfk-Lycn.d.ts.map} +1 -1
- package/dist/{ai-harness-Clbg5ei7.js → ai-harness-dQeJaR1t.js} +7 -3
- package/dist/ai-harness-dQeJaR1t.js.map +1 -0
- package/dist/ai-pi.d.ts +1 -1
- package/dist/ai-pi.js +3 -3
- package/dist/ai-pi.js.map +1 -1
- package/dist/ai-react.d.ts +5 -5
- package/dist/ai-react.js +2 -2
- package/dist/ai-server.d.ts +14 -17
- package/dist/ai-server.d.ts.map +1 -1
- package/dist/ai-server.js +80 -73
- package/dist/ai-server.js.map +1 -1
- package/dist/ai.d.ts +2 -2
- package/dist/ai.js +2 -2
- package/dist/{client-BhHLoNKF.js → client-9IeTsiAs.js} +5 -5
- package/dist/{client-BhHLoNKF.js.map → client-9IeTsiAs.js.map} +1 -1
- package/dist/{client-h_DKFAjj.d.ts → client-CUMbS_38.d.ts} +3 -3
- package/dist/{client-h_DKFAjj.d.ts.map → client-CUMbS_38.d.ts.map} +1 -1
- package/dist/client.d.ts +3 -3
- package/dist/client.js +1 -1
- package/dist/{contract-BYKASlCV.js → contract-CWukt01d.js} +2 -2
- package/dist/contract-CWukt01d.js.map +1 -0
- package/dist/index.d.ts +5 -5
- package/dist/index.js +3 -3
- package/dist/{initial-snapshot-Bx-c0pDP.d.ts → initial-snapshot-D3Lve43o.d.ts} +4 -4
- package/dist/initial-snapshot-D3Lve43o.d.ts.map +1 -0
- package/dist/{internal-CscSY7K_.js → internal-Bc4PvfHk.js} +10 -3
- package/dist/internal-Bc4PvfHk.js.map +1 -0
- package/dist/otel.d.ts +1 -1
- package/dist/otel.js +1 -1
- package/dist/otel.js.map +1 -1
- package/dist/{react-BBXgHWFc.d.ts → react-BoHvC3v9.d.ts} +4 -4
- package/dist/{react-BBXgHWFc.d.ts.map → react-BoHvC3v9.d.ts.map} +1 -1
- package/dist/react.d.ts +1 -1
- package/dist/react.js +1 -1
- package/dist/{reducer-CqA59Q6U.d.ts → reducer-CYPHsSmd.d.ts} +2 -2
- package/dist/{reducer-CqA59Q6U.d.ts.map → reducer-CYPHsSmd.d.ts.map} +1 -1
- package/dist/{reducer-BAXc8JyF.js → reducer-CjmvZSAb.js} +2 -2
- package/dist/{reducer-BAXc8JyF.js.map → reducer-CjmvZSAb.js.map} +1 -1
- package/dist/runtime-consumer-BWmht60z.js +12 -0
- package/dist/runtime-consumer-BWmht60z.js.map +1 -0
- package/dist/scheduler-qstash.d.ts +2 -2
- package/dist/scheduler-qstash.js +3 -3
- package/dist/scheduler-task-X1SToDjP.js.map +1 -1
- package/dist/scheduler-vercel.d.ts +2 -2
- package/dist/scheduler-vercel.js +3 -3
- package/dist/scheduler-vercel.js.map +1 -1
- package/dist/{server-DYAATFuT.d.ts → server-BshFZ0eL.d.ts} +108 -41
- package/dist/server-BshFZ0eL.d.ts.map +1 -0
- package/dist/{server-qyb5aJJG.js → server-DvIB8kj5.js} +387 -113
- package/dist/server-DvIB8kj5.js.map +1 -0
- package/dist/server.browser.js +1 -1
- package/dist/server.browser.js.map +1 -1
- package/dist/server.d.ts +4 -4
- package/dist/server.js +2 -2
- package/dist/{session-clear-C-QwTXV1.d.ts → session-clear-Bk_29eEw.d.ts} +2 -2
- package/dist/session-clear-Bk_29eEw.d.ts.map +1 -0
- package/dist/{session-clear-DGI4jTQ5.js → session-clear-wHd5GCpZ.js} +5 -4
- package/dist/session-clear-wHd5GCpZ.js.map +1 -0
- package/dist/store-N8PXxDAS.js.map +1 -1
- package/dist/store-Z2qjw50n.d.ts +523 -0
- package/dist/store-Z2qjw50n.d.ts.map +1 -0
- package/dist/store-codec-DwrWlgQx.js +65 -0
- package/dist/store-codec-DwrWlgQx.js.map +1 -0
- package/dist/store-memory.d.ts +1 -1
- package/dist/store-memory.d.ts.map +1 -1
- package/dist/store-memory.js +393 -210
- package/dist/store-memory.js.map +1 -1
- package/dist/{store-polling-7WJV0YWP.js → store-polling-DYNyxCev.js} +2 -2
- package/dist/{store-polling-7WJV0YWP.js.map → store-polling-DYNyxCev.js.map} +1 -1
- package/dist/store-postgres.d.ts +1 -1
- package/dist/store-postgres.d.ts.map +1 -1
- package/dist/store-postgres.js +1586 -566
- package/dist/store-postgres.js.map +1 -1
- package/dist/{store-presence-polling-B1ixeqIS.js → store-presence-polling-CzQ0wdtM.js} +3 -3
- package/dist/{store-presence-polling-B1ixeqIS.js.map → store-presence-polling-CzQ0wdtM.js.map} +1 -1
- package/dist/store-redis-http.d.ts +1 -1
- package/dist/store-redis-http.js +4 -4
- package/dist/store-redis-notify-IiVdlYHc.js +2919 -0
- package/dist/store-redis-notify-IiVdlYHc.js.map +1 -0
- package/dist/store-redis.d.ts +1 -1
- package/dist/store-redis.js +2 -2
- package/dist/store-sqlite.d.ts +1 -1
- package/dist/store-sqlite.d.ts.map +1 -1
- package/dist/store-sqlite.js +517 -287
- package/dist/store-sqlite.js.map +1 -1
- package/dist/{telemetry-Cyfqo6Qt.d.ts → telemetry-CWwsh-Gx.d.ts} +3 -3
- package/dist/telemetry-CWwsh-Gx.d.ts.map +1 -0
- package/dist/{wire-BKxH1vbP.js → wire-CZGqNZRx.js} +5 -4
- package/dist/wire-CZGqNZRx.js.map +1 -0
- package/docs/01-quickstart.mdx +21 -15
- package/docs/02-examples.mdx +1 -1
- package/docs/actors/01-introduction.mdx +8 -0
- package/docs/actors/02-concurrency.mdx +1 -1
- package/docs/agents/01-introduction.mdx +1 -1
- package/docs/agents/02-tools.mdx +12 -11
- package/docs/agents/03-conversations.mdx +30 -20
- package/docs/agents/04-harnesses.mdx +7 -3
- package/docs/client/03-presence.mdx +18 -14
- package/docs/contract/01-events.mdx +2 -2
- package/docs/contract/02-state.mdx +18 -14
- package/docs/contract/03-sessions.mdx +32 -25
- package/docs/index.mdx +29 -24
- package/docs/reference/01-core.mdx +4 -4
- package/docs/reference/02-server.mdx +245 -190
- package/docs/reference/04-ai.mdx +20 -19
- package/docs/reference/05-telemetry.mdx +27 -13
- package/docs/reference/06-errors.mdx +24 -18
- package/docs/reference/meta.ts +1 -1
- package/docs/schedulers/01-overview.mdx +8 -8
- package/docs/schedulers/02-vercel-queues.mdx +2 -2
- package/docs/schedulers/03-qstash.mdx +4 -4
- package/docs/server/01-consumers.mdx +415 -0
- package/docs/server/02-timers.mdx +31 -21
- package/docs/server/03-cancellation.mdx +52 -44
- package/docs/server/04-routes.mdx +4 -1
- package/docs/server/05-sql-projections.mdx +391 -0
- package/docs/server/06-guarantees.mdx +55 -48
- package/docs/stores/01-overview.mdx +3 -3
- package/docs/stores/02-redis.mdx +11 -3
- package/docs/stores/03-redis-http.mdx +2 -2
- package/docs/stores/04-postgres.mdx +65 -14
- package/docs/stores/05-sqlite.mdx +5 -3
- package/docs/upgrades/01-overview.mdx +30 -0
- package/docs/upgrades/79-0-21.mdx +165 -0
- package/docs/upgrades/80-0-20.mdx +43 -0
- package/docs/upgrades/81-0-19.mdx +33 -0
- package/docs/upgrades/82-0-18.mdx +33 -0
- package/docs/upgrades/83-0-17.mdx +69 -0
- package/docs/upgrades/meta.ts +3 -0
- package/examples/playground/DEPLOY.md +39 -7
- package/examples/playground/app/a0/chat.test.tsx +2 -2
- package/examples/playground/app/a0/sandbox-log.ts +17 -15
- package/examples/playground/app/a0/server.ts +4 -4
- package/examples/playground/app/a2slack/a2slack.css +1067 -0
- package/examples/playground/app/a2slack/c/[channel]/page.tsx +20 -0
- package/examples/playground/app/a2slack/consumers/agent.ts +63 -0
- package/examples/playground/app/a2slack/consumers/feed.ts +164 -0
- package/examples/playground/app/a2slack/consumers/notify.ts +116 -0
- package/examples/playground/app/a2slack/consumers/search.ts +37 -0
- package/examples/playground/app/a2slack/consumers/triage.ts +31 -0
- package/examples/playground/app/a2slack/consumers/welcome.ts +25 -0
- package/examples/playground/app/a2slack/db/a2.ts +206 -0
- package/examples/playground/app/a2slack/db/index.ts +73 -0
- package/examples/playground/app/a2slack/db/migrations/0000_extensions.sql +4 -0
- package/examples/playground/app/a2slack/db/migrations/0001_workspace.sql +74 -0
- package/examples/playground/app/a2slack/db/migrations/0002_a2.sql +215 -0
- package/examples/playground/app/a2slack/db/migrations/0003_a2_batch.sql +67 -0
- package/examples/playground/app/a2slack/db/migrations/meta/0000_snapshot.json +18 -0
- package/examples/playground/app/a2slack/db/migrations/meta/0001_snapshot.json +500 -0
- package/examples/playground/app/a2slack/db/migrations/meta/0002_snapshot.json +1159 -0
- package/examples/playground/app/a2slack/db/migrations/meta/0003_snapshot.json +1159 -0
- package/examples/playground/app/a2slack/db/migrations/meta/_journal.json +34 -0
- package/examples/playground/app/a2slack/db/queries.ts +263 -0
- package/examples/playground/app/a2slack/db/schema.ts +145 -0
- package/examples/playground/app/a2slack/drills/a2slack.drill.ts +708 -0
- package/examples/playground/app/a2slack/drills/harness.ts +244 -0
- package/examples/playground/app/a2slack/events/route.ts +36 -0
- package/examples/playground/app/a2slack/identity.ts +44 -0
- package/examples/playground/app/a2slack/inbox/route.ts +8 -0
- package/examples/playground/app/a2slack/lag/data/route.ts +8 -0
- package/examples/playground/app/a2slack/lag/page.tsx +101 -0
- package/examples/playground/app/a2slack/lag.ts +73 -0
- package/examples/playground/app/a2slack/layout.tsx +43 -0
- package/examples/playground/app/a2slack/messages/route.ts +17 -0
- package/examples/playground/app/a2slack/model.test.ts +208 -0
- package/examples/playground/app/a2slack/model.ts +369 -0
- package/examples/playground/app/a2slack/models.test.ts +72 -0
- package/examples/playground/app/a2slack/page.tsx +10 -0
- package/examples/playground/app/a2slack/scheduler/route.ts +5 -0
- package/examples/playground/app/a2slack/search/route.ts +40 -0
- package/examples/playground/app/a2slack/search-index.ts +108 -0
- package/examples/playground/app/a2slack/server.test.ts +315 -0
- package/examples/playground/app/a2slack/server.ts +55 -0
- package/examples/playground/app/a2slack/sessions.ts +50 -0
- package/examples/playground/app/a2slack/thread/route.ts +8 -0
- package/examples/playground/app/a2slack/ui/ChannelView.tsx +87 -0
- package/examples/playground/app/a2slack/ui/Composer.tsx +117 -0
- package/examples/playground/app/a2slack/ui/DotsShadow.tsx +42 -0
- package/examples/playground/app/a2slack/ui/Feed.tsx +157 -0
- package/examples/playground/app/a2slack/ui/InboxContext.tsx +63 -0
- package/examples/playground/app/a2slack/ui/InboxView.tsx +82 -0
- package/examples/playground/app/a2slack/ui/Menu.tsx +128 -0
- package/examples/playground/app/a2slack/ui/MessageRow.tsx +314 -0
- package/examples/playground/app/a2slack/ui/NameGate.tsx +35 -0
- package/examples/playground/app/a2slack/ui/SearchContext.tsx +146 -0
- package/examples/playground/app/a2slack/ui/SearchResults.tsx +97 -0
- package/examples/playground/app/a2slack/ui/Shell.tsx +34 -0
- package/examples/playground/app/a2slack/ui/Sidebar.tsx +199 -0
- package/examples/playground/app/a2slack/ui/ThreadPanel.tsx +134 -0
- package/examples/playground/app/a2slack/ui/useInbox.ts +71 -0
- package/examples/playground/app/agent/server.ts +3 -3
- package/examples/playground/app/chat/[chatId]/chat-client.tsx +1 -1
- package/examples/playground/app/chat/server.ts +45 -35
- package/examples/playground/app/components/demo-icons.tsx +7 -0
- package/examples/playground/app/components/demos.tsx +7 -0
- package/examples/playground/app/orders/[orderId]/order-client.tsx +2 -2
- package/examples/playground/app/orders/page.tsx +1 -1
- package/examples/playground/app/orders/server.ts +49 -30
- package/examples/playground/app/recovery/[recoveryId]/recovery-client.tsx +14 -15
- package/examples/playground/app/recovery/page.tsx +4 -4
- package/examples/playground/app/recovery/server.ts +23 -32
- package/examples/playground/drizzle.config.ts +14 -0
- package/examples/playground/next.config.ts +6 -1
- package/examples/playground/package.json +12 -1
- package/examples/playground/scripts/migrate.mjs +31 -0
- package/examples/playground/vercel.json +9 -0
- package/examples/playground/vitest.drills.config.ts +32 -0
- package/package.json +5 -2
- package/sql/postgres/drizzle-schema.ts +206 -0
- package/sql/postgres/schema.sql +206 -67
- package/src/actor.ts +101 -71
- package/src/ai-agent.ts +149 -111
- package/src/ai-control-server.ts +8 -8
- package/src/ai-fx.ts +4 -6
- package/src/ai-harness.ts +10 -2
- package/src/ai-pi.ts +2 -2
- package/src/ai-server.ts +2 -2
- package/src/ai.ts +7 -7
- package/src/contract.ts +1 -1
- package/src/deterministic-id.ts +26 -3
- package/src/errors.ts +8 -5
- package/src/idempotent-replay.ts +0 -1
- package/src/index.ts +1 -1
- package/src/internal.ts +15 -5
- package/src/otel.ts +1 -1
- package/src/postgres-schema.ts +98 -57
- package/src/push-envelope.ts +20 -0
- package/src/runtime-consumer.ts +10 -0
- package/src/scheduler-task.ts +2 -2
- package/src/scheduler-vercel.ts +1 -1
- package/src/server.browser.ts +1 -1
- package/src/server.ts +774 -207
- package/src/session-socket.ts +1 -1
- package/src/store-codec.ts +66 -0
- package/src/store-memory.ts +581 -255
- package/src/store-postgres.ts +1984 -825
- package/src/store-redis-core.ts +1624 -1152
- package/src/store-redis-notify.ts +2 -0
- package/src/store-sqlite.ts +891 -486
- package/src/store.ts +227 -97
- package/src/telemetry.ts +2 -1
- package/src/wire.ts +1 -0
- package/dist/ai-harness-Clbg5ei7.js.map +0 -1
- package/dist/ai-mMiWul1T.js.map +0 -1
- package/dist/contract-BYKASlCV.js.map +0 -1
- package/dist/idempotent-replay-JdgMQRRm.js +0 -21
- package/dist/idempotent-replay-JdgMQRRm.js.map +0 -1
- package/dist/initial-snapshot-Bx-c0pDP.d.ts.map +0 -1
- package/dist/internal-CscSY7K_.js.map +0 -1
- package/dist/server-DYAATFuT.d.ts.map +0 -1
- package/dist/server-qyb5aJJG.js.map +0 -1
- package/dist/session-clear-C-QwTXV1.d.ts.map +0 -1
- package/dist/session-clear-DGI4jTQ5.js.map +0 -1
- package/dist/store-CyLUy86v.d.ts +0 -404
- package/dist/store-CyLUy86v.d.ts.map +0 -1
- package/dist/store-codec-DTG0Ftek.js +0 -10
- package/dist/store-codec-DTG0Ftek.js.map +0 -1
- package/dist/store-redis-notify-CM0AfLY0.js +0 -2544
- package/dist/store-redis-notify-CM0AfLY0.js.map +0 -1
- package/dist/telemetry-Cyfqo6Qt.d.ts.map +0 -1
- package/dist/wire-BKxH1vbP.js.map +0 -1
- package/docs/server/01-handlers.mdx +0 -227
package/src/ai-agent.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The server-only implementation of the experimental-a2/ai protocol:
|
|
3
|
-
*
|
|
2
|
+
* The server-only implementation of the experimental-a2/ai protocol: its
|
|
3
|
+
* consumers and `agent()`, its assembly as an ordinary A2 server.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
// oxlint-disable no-await-in-loop -- stream chunks and durable appends are
|
|
@@ -79,10 +79,12 @@ import {
|
|
|
79
79
|
installAmbientToolScopeStorage,
|
|
80
80
|
type AmbientToolScope,
|
|
81
81
|
} from './internal.ts'
|
|
82
|
+
import { runtimeConsumer } from './runtime-consumer.ts'
|
|
82
83
|
import {
|
|
83
84
|
createServer,
|
|
84
85
|
type A2Server,
|
|
85
|
-
type
|
|
86
|
+
type Consumer,
|
|
87
|
+
type ConsumerContext,
|
|
86
88
|
type ServerOptions,
|
|
87
89
|
} from './server.ts'
|
|
88
90
|
import {
|
|
@@ -191,7 +193,7 @@ export type AgentResolverContext<
|
|
|
191
193
|
* in the checkpoint the generation already reads, so it costs no I/O.
|
|
192
194
|
*/
|
|
193
195
|
state: X
|
|
194
|
-
session: Pick<
|
|
196
|
+
session: Pick<ConsumerContext<D>['session'], 'state'>
|
|
195
197
|
signal: AbortSignal
|
|
196
198
|
}
|
|
197
199
|
|
|
@@ -239,7 +241,7 @@ export type CompactionOptions<
|
|
|
239
241
|
|
|
240
242
|
export type AgentGenerationSettings<T extends ToolSet> = AISDKStepSettings<T>
|
|
241
243
|
|
|
242
|
-
export type
|
|
244
|
+
export type ConsumersOptions<
|
|
243
245
|
M extends UIMessage,
|
|
244
246
|
D extends AIEventDefs<M> & EventDefs,
|
|
245
247
|
T extends ToolSet = ToolSet,
|
|
@@ -344,27 +346,24 @@ type AgentServerOptions<
|
|
|
344
346
|
T extends ToolSet,
|
|
345
347
|
X extends object = Record<never, never>,
|
|
346
348
|
H extends string = string,
|
|
349
|
+
C extends readonly unknown[] = readonly Consumer<D>[],
|
|
347
350
|
> = AgentModelOptions<M, D, T, X, H> &
|
|
348
|
-
Omit<
|
|
349
|
-
ServerOptions<D, Record<never, never>, Reducer<D, S>>,
|
|
350
|
-
'contract' | 'handlers'
|
|
351
|
-
> & {
|
|
352
|
-
/** Handlers for application events. */
|
|
353
|
-
handlers?: ServerOptions<D, Record<never, never>, Reducer<D, S>>['handlers']
|
|
354
|
-
}
|
|
351
|
+
Omit<ServerOptions<D, Record<never, never>, Reducer<D, S>, C>, 'contract'>
|
|
355
352
|
|
|
356
353
|
export type AgentOptions<
|
|
357
354
|
M extends UIMessage,
|
|
358
355
|
E extends EventDefs,
|
|
359
356
|
T extends ToolSet,
|
|
360
357
|
H extends string = string,
|
|
358
|
+
C extends readonly unknown[] = readonly Consumer<AIEventDefs<M> & E>[],
|
|
361
359
|
> = AgentServerOptions<
|
|
362
360
|
NoInfer<M>,
|
|
363
361
|
AIEventDefs<NoInfer<M>> & E,
|
|
364
362
|
AIState<NoInfer<M>>,
|
|
365
363
|
T,
|
|
366
364
|
Record<never, never>,
|
|
367
|
-
H
|
|
365
|
+
H,
|
|
366
|
+
C
|
|
368
367
|
> & {
|
|
369
368
|
/** Identity: prefixes storage keys and queue messages. */
|
|
370
369
|
name: string
|
|
@@ -384,7 +383,8 @@ export type AgentFromContractOptions<
|
|
|
384
383
|
Name extends string = string,
|
|
385
384
|
X extends object = Record<never, never>,
|
|
386
385
|
H extends string = string,
|
|
387
|
-
|
|
386
|
+
C extends readonly unknown[] = readonly Consumer<D>[],
|
|
387
|
+
> = AgentServerOptions<AIMessageOf<D>, D, S, T, X, H, C> & {
|
|
388
388
|
/**
|
|
389
389
|
* A shared `agentContract()`. Its application state reaches every resolver
|
|
390
390
|
* as `state`; a fully custom contract is core A2, served by `createServer()`.
|
|
@@ -480,8 +480,8 @@ const toolScopeStorage = installAmbientToolScopeStorage(
|
|
|
480
480
|
() => new AsyncLocalStorage<AmbientToolScope>(),
|
|
481
481
|
)
|
|
482
482
|
|
|
483
|
-
/** Run `fn` with `ctx` as the ambient tool
|
|
484
|
-
export function
|
|
483
|
+
/** Run `fn` with `ctx` as the ambient tool consumer context, for direct tool tests. */
|
|
484
|
+
export function runWithConsumerContext<D extends AgentToolEventDefs, R>(
|
|
485
485
|
ctx: AgentToolContext<D>,
|
|
486
486
|
fn: () => R,
|
|
487
487
|
): R {
|
|
@@ -683,7 +683,7 @@ async function* consumeGeneration(options: {
|
|
|
683
683
|
const reader = options.source.stream.getReader()
|
|
684
684
|
// The next read starts before a batch is yielded, and the consumer may
|
|
685
685
|
// take a while to ask again: a stream that fails meanwhile must not leave
|
|
686
|
-
// that read rejected with
|
|
686
|
+
// that read rejected with nothing to catch it, which ends the process.
|
|
687
687
|
const readAhead = (): Promise<StreamRead<UIMessageChunk>> => {
|
|
688
688
|
const read = reader.read()
|
|
689
689
|
read.catch(() => {})
|
|
@@ -1232,14 +1232,12 @@ const assertNoLoopOptions = (options: { harness?: { type: string } }): void => {
|
|
|
1232
1232
|
}
|
|
1233
1233
|
}
|
|
1234
1234
|
|
|
1235
|
-
export function
|
|
1235
|
+
export function createConsumers<
|
|
1236
1236
|
M extends UIMessage,
|
|
1237
1237
|
D extends AIEventDefs<M> & EventDefs,
|
|
1238
1238
|
T extends ToolSet = ToolSet,
|
|
1239
1239
|
X extends object = Record<never, never>,
|
|
1240
|
-
>(
|
|
1241
|
-
options: HandlersOptions<M, D, T, X>,
|
|
1242
|
-
): NonNullable<ServerOptions<D>['handlers']> {
|
|
1240
|
+
>(options: ConsumersOptions<M, D, T, X>): Consumer<D>[] {
|
|
1243
1241
|
if (options.model === undefined) {
|
|
1244
1242
|
throw new TypeError('agent requires model')
|
|
1245
1243
|
}
|
|
@@ -1349,7 +1347,7 @@ export function createHandlers<
|
|
|
1349
1347
|
contract: options.contract,
|
|
1350
1348
|
})
|
|
1351
1349
|
const readContext = async (
|
|
1352
|
-
session:
|
|
1350
|
+
session: ConsumerContext<D>['session'],
|
|
1353
1351
|
through: number | 'latest',
|
|
1354
1352
|
) => {
|
|
1355
1353
|
const snapshot = await session.state(contextReducer, { through })
|
|
@@ -1390,7 +1388,7 @@ export function createHandlers<
|
|
|
1390
1388
|
) => unknown
|
|
1391
1389
|
}
|
|
1392
1390
|
|
|
1393
|
-
type
|
|
1391
|
+
type ToolConsumerContext = ConsumerContext<D, 'ai.tool.execution.requested'>
|
|
1394
1392
|
|
|
1395
1393
|
const resultEvent = async (
|
|
1396
1394
|
call: ToolCalledPayload,
|
|
@@ -1435,7 +1433,7 @@ export function createHandlers<
|
|
|
1435
1433
|
}) as AppendInput<D>
|
|
1436
1434
|
|
|
1437
1435
|
const promptMessages = (input: {
|
|
1438
|
-
ctx:
|
|
1436
|
+
ctx: ToolConsumerContext
|
|
1439
1437
|
call: ToolCalledPayload
|
|
1440
1438
|
generation: GenerationStartedPayload
|
|
1441
1439
|
}): Promise<ModelMessage[]> => {
|
|
@@ -1485,7 +1483,7 @@ export function createHandlers<
|
|
|
1485
1483
|
}
|
|
1486
1484
|
|
|
1487
1485
|
const toolExecutionFailure = async (
|
|
1488
|
-
ctx:
|
|
1486
|
+
ctx: ToolConsumerContext,
|
|
1489
1487
|
call: ToolCalledPayload,
|
|
1490
1488
|
error: unknown,
|
|
1491
1489
|
): Promise<AppendInput<D> | void> => {
|
|
@@ -1498,7 +1496,7 @@ export function createHandlers<
|
|
|
1498
1496
|
}
|
|
1499
1497
|
|
|
1500
1498
|
const runToolExecution = async (
|
|
1501
|
-
ctx:
|
|
1499
|
+
ctx: ToolConsumerContext,
|
|
1502
1500
|
call: ToolCalledPayload,
|
|
1503
1501
|
execute: NonNullable<RuntimeTool['execute']>,
|
|
1504
1502
|
messages: ModelMessage[],
|
|
@@ -1575,7 +1573,7 @@ export function createHandlers<
|
|
|
1575
1573
|
}
|
|
1576
1574
|
|
|
1577
1575
|
const executeTool = async (input: {
|
|
1578
|
-
ctx:
|
|
1576
|
+
ctx: ToolConsumerContext
|
|
1579
1577
|
call: ToolCalledPayload
|
|
1580
1578
|
generation: GenerationStartedPayload
|
|
1581
1579
|
}): Promise<AppendInput<D> | void> => {
|
|
@@ -1629,7 +1627,7 @@ export function createHandlers<
|
|
|
1629
1627
|
}
|
|
1630
1628
|
|
|
1631
1629
|
const handleToolExecution = async (
|
|
1632
|
-
ctx:
|
|
1630
|
+
ctx: ToolConsumerContext,
|
|
1633
1631
|
): Promise<AppendInput<D>> => {
|
|
1634
1632
|
try {
|
|
1635
1633
|
const state = (
|
|
@@ -1666,7 +1664,7 @@ export function createHandlers<
|
|
|
1666
1664
|
}
|
|
1667
1665
|
|
|
1668
1666
|
const generationHandler = async (
|
|
1669
|
-
ctx:
|
|
1667
|
+
ctx: ConsumerContext<D, 'ai.generation.requested'>,
|
|
1670
1668
|
): Promise<void | AppendInput<D> | readonly AppendInput<D>[]> => {
|
|
1671
1669
|
if (checkAbort(ctx.signal)) return
|
|
1672
1670
|
const requestId = ctx.event.id
|
|
@@ -2315,23 +2313,41 @@ export function createHandlers<
|
|
|
2315
2313
|
}
|
|
2316
2314
|
}
|
|
2317
2315
|
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
}
|
|
2333
|
-
|
|
2316
|
+
const contract = options.contract as unknown as Contract<
|
|
2317
|
+
D,
|
|
2318
|
+
Record<never, never>,
|
|
2319
|
+
undefined
|
|
2320
|
+
>
|
|
2321
|
+
const { toolLane } = options
|
|
2322
|
+
type Of<K extends string> = ContractEvent<D, K & keyof D & string>
|
|
2323
|
+
type Generation = Of<'ai.generation.requested'>
|
|
2324
|
+
type ToolCall = Of<'ai.tool.execution.requested'>
|
|
2325
|
+
return [
|
|
2326
|
+
runtimeConsumer({
|
|
2327
|
+
contract,
|
|
2328
|
+
name: 'a2.ai.metadata',
|
|
2329
|
+
types: ['ai.model.metadata.requested'],
|
|
2330
|
+
}).run(async (ctx) => {
|
|
2331
|
+
const limits = await readModelLimits({
|
|
2332
|
+
modelId: ctx.event.payload.modelId,
|
|
2333
|
+
signal: ctx.signal,
|
|
2334
|
+
})
|
|
2335
|
+
ctx.signal.throwIfAborted()
|
|
2336
|
+
return {
|
|
2337
|
+
type: 'ai.model.metadata.resolved',
|
|
2338
|
+
id: await aiEventId('a2.ai.model.metadata.resolved', ctx.event.id),
|
|
2339
|
+
payload: { modelId: ctx.event.payload.modelId, limits },
|
|
2340
|
+
} as AppendInput<D>
|
|
2341
|
+
}),
|
|
2342
|
+
// One lane for every control event: commands, work reports, and closes
|
|
2343
|
+
// apply one at a time. Its lane function also rejects a bad command at
|
|
2344
|
+
// append, before anything is written.
|
|
2345
|
+
runtimeConsumer({
|
|
2346
|
+
contract,
|
|
2347
|
+
name: 'a2.ai.control',
|
|
2348
|
+
types: ['ai.control.requested', 'ai.work.reported', 'ai.session.closed'],
|
|
2334
2349
|
lane: ({ event }) => {
|
|
2350
|
+
if (event.type !== 'ai.control.requested') return 'a2.ai.control'
|
|
2335
2351
|
const command = event.payload as ControlCommand<M>
|
|
2336
2352
|
if (event.id === '')
|
|
2337
2353
|
throw new A2Error(
|
|
@@ -2353,17 +2369,25 @@ export function createHandlers<
|
|
|
2353
2369
|
)
|
|
2354
2370
|
return 'a2.ai.control'
|
|
2355
2371
|
},
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
lane: 'a2.ai.model',
|
|
2372
|
+
}).run(controlHandler as never),
|
|
2373
|
+
runtimeConsumer({
|
|
2374
|
+
contract,
|
|
2375
|
+
name: 'a2.ai.model',
|
|
2376
|
+
types: ['ai.generation.requested'],
|
|
2377
|
+
lane: () => 'a2.ai.model',
|
|
2378
|
+
// The contract is generic here, so the map's keys widen to string and
|
|
2379
|
+
// each predicate names its own event types.
|
|
2362
2380
|
abortOn: {
|
|
2363
|
-
'ai.control.requested': (
|
|
2381
|
+
'ai.control.requested': (
|
|
2382
|
+
event: Of<'ai.control.requested'>,
|
|
2383
|
+
trigger: Generation,
|
|
2384
|
+
) =>
|
|
2364
2385
|
event.payload.action === 'stop' &&
|
|
2365
2386
|
event.payload.turnId === trigger.payload.control?.turnId,
|
|
2366
|
-
'ai.control.committed': (
|
|
2387
|
+
'ai.control.committed': (
|
|
2388
|
+
event: Of<'ai.control.committed'>,
|
|
2389
|
+
trigger: Generation,
|
|
2390
|
+
) => {
|
|
2367
2391
|
const active = (event.payload as ControlCommit).view
|
|
2368
2392
|
return (
|
|
2369
2393
|
active?.turnId !== trigger.payload.control?.turnId ||
|
|
@@ -2371,39 +2395,50 @@ export function createHandlers<
|
|
|
2371
2395
|
)
|
|
2372
2396
|
},
|
|
2373
2397
|
'ai.session.closed': true,
|
|
2374
|
-
},
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2398
|
+
} as never,
|
|
2399
|
+
}).run((ctx) =>
|
|
2400
|
+
telemetry.span(
|
|
2401
|
+
'a2.ai.generate',
|
|
2402
|
+
{
|
|
2403
|
+
'a2.contract': options.contract.name,
|
|
2404
|
+
'a2.session_id': ctx.event.sessionId,
|
|
2405
|
+
'a2.ai.generate.request_id': ctx.event.id,
|
|
2406
|
+
'a2.ai.generate.attempt': ctx.attempt,
|
|
2407
|
+
},
|
|
2408
|
+
async () => {
|
|
2409
|
+
try {
|
|
2410
|
+
return control.settled({
|
|
2411
|
+
ctx,
|
|
2412
|
+
events: await generationHandler(ctx),
|
|
2413
|
+
})
|
|
2414
|
+
} catch (error) {
|
|
2415
|
+
if (control.cancelled({ error, signal: ctx.signal }))
|
|
2416
|
+
return control.settled({ ctx, events: undefined })
|
|
2417
|
+
throw error
|
|
2418
|
+
}
|
|
2419
|
+
},
|
|
2420
|
+
),
|
|
2421
|
+
),
|
|
2422
|
+
runtimeConsumer({
|
|
2423
|
+
contract,
|
|
2424
|
+
name: 'a2.ai.tools',
|
|
2425
|
+
types: ['ai.tool.execution.requested'],
|
|
2426
|
+
...(toolLane === undefined ? {} : { lane: () => toolLane }),
|
|
2400
2427
|
abortOn: {
|
|
2401
|
-
'ai.control.requested': (
|
|
2428
|
+
'ai.control.requested': (
|
|
2429
|
+
event: Of<'ai.control.requested'>,
|
|
2430
|
+
trigger: ToolCall,
|
|
2431
|
+
) =>
|
|
2402
2432
|
event.payload.action === 'stop' &&
|
|
2403
2433
|
event.payload.turnId === trigger.payload.turnId,
|
|
2404
|
-
'ai.generation.failed': (
|
|
2405
|
-
event
|
|
2406
|
-
|
|
2434
|
+
'ai.generation.failed': (
|
|
2435
|
+
event: Of<'ai.generation.failed'>,
|
|
2436
|
+
trigger: ToolCall,
|
|
2437
|
+
) => event.payload.generationId === trigger.payload.call.generationId,
|
|
2438
|
+
'ai.control.committed': (
|
|
2439
|
+
event: Of<'ai.control.committed'>,
|
|
2440
|
+
trigger: ToolCall,
|
|
2441
|
+
) => {
|
|
2407
2442
|
const active = (event.payload as ControlCommit).view
|
|
2408
2443
|
return (
|
|
2409
2444
|
active?.turnId !== trigger.payload.turnId ||
|
|
@@ -2411,26 +2446,26 @@ export function createHandlers<
|
|
|
2411
2446
|
)
|
|
2412
2447
|
},
|
|
2413
2448
|
'ai.session.closed': true,
|
|
2414
|
-
},
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
2449
|
+
} as never,
|
|
2450
|
+
}).run((ctx) =>
|
|
2451
|
+
telemetry.span(
|
|
2452
|
+
'a2.ai.tool',
|
|
2453
|
+
{
|
|
2454
|
+
'a2.contract': options.contract.name,
|
|
2455
|
+
'a2.session_id': ctx.event.sessionId,
|
|
2456
|
+
'a2.ai.tool.request_id': ctx.event.payload.call.requestId,
|
|
2457
|
+
'a2.ai.tool.call_id': ctx.event.payload.call.toolCallId,
|
|
2458
|
+
'a2.ai.tool.name': ctx.event.payload.call.toolName,
|
|
2459
|
+
'a2.ai.tool.attempt': ctx.attempt,
|
|
2460
|
+
},
|
|
2461
|
+
() => handleToolExecution(ctx),
|
|
2462
|
+
),
|
|
2463
|
+
),
|
|
2464
|
+
] as Consumer<D>[]
|
|
2430
2465
|
}
|
|
2431
2466
|
|
|
2432
2467
|
/**
|
|
2433
|
-
* Serve an AI agent: the built-in protocol, its
|
|
2468
|
+
* Serve an AI agent: the built-in protocol, its consumers, and A2's store,
|
|
2434
2469
|
* scheduler, and telemetry, as one ordinary A2 server. Pass `name` for the
|
|
2435
2470
|
* built-in protocol, or a shared `agentContract()` when clients fold
|
|
2436
2471
|
* application state.
|
|
@@ -2440,8 +2475,9 @@ export function agent<
|
|
|
2440
2475
|
M extends UIMessage = UIMessage,
|
|
2441
2476
|
E extends EventDefs = Record<never, never>,
|
|
2442
2477
|
H extends string = never,
|
|
2478
|
+
const C extends readonly unknown[] = readonly Consumer<AIEventDefs<M> & E>[],
|
|
2443
2479
|
>(
|
|
2444
|
-
options: AgentOptions<M, E, T, H>,
|
|
2480
|
+
options: AgentOptions<M, E, T, H, C>,
|
|
2445
2481
|
): Agent<AIEventDefs<M> & E, AIState<M>, typeof AI_STATE_REDUCER>
|
|
2446
2482
|
export function agent<
|
|
2447
2483
|
D extends AIEventDefs<AIMessageOf<D>> & EventDefs,
|
|
@@ -2450,7 +2486,8 @@ export function agent<
|
|
|
2450
2486
|
Name extends string = string,
|
|
2451
2487
|
X extends object = Record<never, never>,
|
|
2452
2488
|
H extends string = never,
|
|
2453
|
-
|
|
2489
|
+
const C extends readonly unknown[] = readonly Consumer<D>[],
|
|
2490
|
+
>(options: AgentFromContractOptions<D, S, T, Name, X, H, C>): Agent<D, S, Name>
|
|
2454
2491
|
export function agent(
|
|
2455
2492
|
options:
|
|
2456
2493
|
| AgentOptions<UIMessage, EventDefs, ToolSet>
|
|
@@ -2458,6 +2495,12 @@ export function agent(
|
|
|
2458
2495
|
): Agent<AIEventDefs & EventDefs, AIState> {
|
|
2459
2496
|
type D = AIEventDefs & EventDefs
|
|
2460
2497
|
assertNoLoopOptions(options)
|
|
2498
|
+
if (
|
|
2499
|
+
options.toolLane !== undefined &&
|
|
2500
|
+
(typeof options.toolLane !== 'string' || options.toolLane.length === 0)
|
|
2501
|
+
) {
|
|
2502
|
+
throw new TypeError('toolLane must be a non-empty string')
|
|
2503
|
+
}
|
|
2461
2504
|
const {
|
|
2462
2505
|
model,
|
|
2463
2506
|
tools,
|
|
@@ -2467,13 +2510,12 @@ export function agent(
|
|
|
2467
2510
|
toolRecovery,
|
|
2468
2511
|
progress,
|
|
2469
2512
|
toolLane,
|
|
2470
|
-
handlers,
|
|
2471
2513
|
...rest
|
|
2472
2514
|
} = options
|
|
2473
2515
|
let contract: Contract<D, Record<never, never>, Reducer<D, AIState>>
|
|
2474
2516
|
let serverOptions: Omit<
|
|
2475
2517
|
ServerOptions<D, Record<never, never>, Reducer<D, AIState>>,
|
|
2476
|
-
'contract'
|
|
2518
|
+
'contract'
|
|
2477
2519
|
>
|
|
2478
2520
|
if ('contract' in rest) {
|
|
2479
2521
|
const { contract: shared, ...remaining } = rest
|
|
@@ -2493,7 +2535,7 @@ export function agent(
|
|
|
2493
2535
|
serverOptions = remaining
|
|
2494
2536
|
}
|
|
2495
2537
|
const states = harnessStates()
|
|
2496
|
-
const builtIns =
|
|
2538
|
+
const builtIns = createConsumers<UIMessage, D>({
|
|
2497
2539
|
contract,
|
|
2498
2540
|
model,
|
|
2499
2541
|
harnessStates: states,
|
|
@@ -2507,15 +2549,11 @@ export function agent(
|
|
|
2507
2549
|
...(toolRecovery === undefined ? {} : { toolRecovery }),
|
|
2508
2550
|
...(progress === undefined ? {} : { progress }),
|
|
2509
2551
|
...(toolLane === undefined ? {} : { toolLane }),
|
|
2510
|
-
} as
|
|
2511
|
-
for (const type of Object.keys(handlers ?? {})) {
|
|
2512
|
-
if (Object.hasOwn(builtIns, type))
|
|
2513
|
-
throw new TypeError(`agent handles '${type}' itself`)
|
|
2514
|
-
}
|
|
2552
|
+
} as ConsumersOptions<UIMessage, D>)
|
|
2515
2553
|
const server = createServer({
|
|
2516
2554
|
...serverOptions,
|
|
2517
2555
|
contract,
|
|
2518
|
-
|
|
2556
|
+
consumers: [...builtIns, ...(serverOptions.consumers ?? [])],
|
|
2519
2557
|
})
|
|
2520
2558
|
registerHarnessStates(server, states)
|
|
2521
2559
|
const applicationEvents = 'contract' in rest
|
package/src/ai-control-server.ts
CHANGED
|
@@ -31,7 +31,7 @@ import type {
|
|
|
31
31
|
ContractEvent,
|
|
32
32
|
EventDefs,
|
|
33
33
|
} from './contract.ts'
|
|
34
|
-
import type {
|
|
34
|
+
import type { ConsumerContext } from './server.ts'
|
|
35
35
|
import type { Reducer } from './reducer.ts'
|
|
36
36
|
import type { AICoordinatorState, CoordinatorCall } from './ai-coordinator.ts'
|
|
37
37
|
import { validateSync } from './validate.ts'
|
|
@@ -56,18 +56,18 @@ export type AIControlRuntime<
|
|
|
56
56
|
D extends AIEventDefs<M> & EventDefs,
|
|
57
57
|
> = {
|
|
58
58
|
handler(
|
|
59
|
-
ctx:
|
|
59
|
+
ctx: ConsumerContext<
|
|
60
60
|
D,
|
|
61
61
|
'ai.control.requested' | 'ai.work.reported' | 'ai.session.closed'
|
|
62
62
|
>,
|
|
63
63
|
): Promise<AppendInput<D>[]>
|
|
64
64
|
append(options: {
|
|
65
|
-
ctx:
|
|
65
|
+
ctx: ConsumerContext<D>
|
|
66
66
|
name: string
|
|
67
67
|
events: AppendInput<D>[]
|
|
68
68
|
}): Promise<ContractEvent<D>[]>
|
|
69
69
|
settled(options: {
|
|
70
|
-
ctx:
|
|
70
|
+
ctx: ConsumerContext<D>
|
|
71
71
|
events: void | AppendInput<D> | readonly AppendInput<D>[]
|
|
72
72
|
}): Promise<AppendInput<D>>
|
|
73
73
|
cancelled(options: { error: unknown; signal: AbortSignal }): boolean
|
|
@@ -117,7 +117,7 @@ export const createControlRuntime = <
|
|
|
117
117
|
const coordinator = aiCoordinatorReducer(options.contract)
|
|
118
118
|
|
|
119
119
|
const handler = async (
|
|
120
|
-
ctx:
|
|
120
|
+
ctx: ConsumerContext<
|
|
121
121
|
D,
|
|
122
122
|
'ai.control.requested' | 'ai.work.reported' | 'ai.session.closed'
|
|
123
123
|
>,
|
|
@@ -761,7 +761,7 @@ export const createControlRuntime = <
|
|
|
761
761
|
settled,
|
|
762
762
|
name,
|
|
763
763
|
}: {
|
|
764
|
-
ctx:
|
|
764
|
+
ctx: ConsumerContext<D>
|
|
765
765
|
events: readonly AppendInput<D>[]
|
|
766
766
|
settled: boolean
|
|
767
767
|
name: string
|
|
@@ -828,7 +828,7 @@ export const createControlRuntime = <
|
|
|
828
828
|
name,
|
|
829
829
|
events,
|
|
830
830
|
}: {
|
|
831
|
-
ctx:
|
|
831
|
+
ctx: ConsumerContext<D>
|
|
832
832
|
name: string
|
|
833
833
|
events: AppendInput<D>[]
|
|
834
834
|
}): Promise<ContractEvent<D>[]> => {
|
|
@@ -896,7 +896,7 @@ export const createControlRuntime = <
|
|
|
896
896
|
ctx,
|
|
897
897
|
events,
|
|
898
898
|
}: {
|
|
899
|
-
ctx:
|
|
899
|
+
ctx: ConsumerContext<D>
|
|
900
900
|
events: void | AppendInput<D> | readonly AppendInput<D>[]
|
|
901
901
|
}): Promise<AppendInput<D>> =>
|
|
902
902
|
report({
|
package/src/ai-fx.ts
CHANGED
|
@@ -175,7 +175,7 @@ async function runStep(step: HarnessStep): Promise<HarnessGeneration> {
|
|
|
175
175
|
if (outcome.result.stopReason === 'end_turn' && !step.signal.aborted)
|
|
176
176
|
await saveCheckpoint()
|
|
177
177
|
} else {
|
|
178
|
-
// FX now waits on tools that A2's tool
|
|
178
|
+
// FX now waits on tools that A2's tool consumer run.
|
|
179
179
|
fxTurn.cancel()
|
|
180
180
|
}
|
|
181
181
|
controller.enqueue({ type: 'finish-step' })
|
|
@@ -417,14 +417,12 @@ async function turnPlan(
|
|
|
417
417
|
): Promise<TurnPlan> {
|
|
418
418
|
const calls: RecordedCall[] = []
|
|
419
419
|
const steers = new Map<number, string[]>()
|
|
420
|
-
const steer = (at: number, inputs: HarnessInput[] | undefined) => {
|
|
421
|
-
if (inputs !== undefined) steers.set(Math.max(0, at), inputs.map(steerText))
|
|
422
|
-
}
|
|
423
|
-
steer(0, turn.generations.length === 0 ? turn.steers.get(0) : undefined)
|
|
424
420
|
turn.generations.forEach((generation, index) => {
|
|
425
421
|
calls.push(...recordedCalls(generation.chunks, generation.completed))
|
|
422
|
+
const inputs = turn.steers.get(index)
|
|
426
423
|
// A generation's step is its last call.
|
|
427
|
-
|
|
424
|
+
if (inputs !== undefined)
|
|
425
|
+
steers.set(calls.length - 1, inputs.map(steerText))
|
|
428
426
|
})
|
|
429
427
|
const images = await harnessImages({
|
|
430
428
|
harness: 'fx()',
|
package/src/ai-harness.ts
CHANGED
|
@@ -323,6 +323,7 @@ export type HarnessInput = { text: string; files: FileUIPart[] }
|
|
|
323
323
|
|
|
324
324
|
/** One harness turn to rebuild: its prompt, completed generations, and end. */
|
|
325
325
|
export type HarnessTurn = {
|
|
326
|
+
/** With any steering that entered before the turn's first completed generation. */
|
|
326
327
|
prompt: HarnessInput
|
|
327
328
|
generations: RecordedGeneration[]
|
|
328
329
|
/** Steering that entered after the generation at that index. */
|
|
@@ -441,8 +442,15 @@ export function harnessTurns(
|
|
|
441
442
|
files: response.prompt.flatMap((prompt) => prompt.files),
|
|
442
443
|
}
|
|
443
444
|
let turn = turns.at(-1)
|
|
444
|
-
if (steered && turn
|
|
445
|
-
|
|
445
|
+
if (steered && turn?.generations.length === 0) {
|
|
446
|
+
// Nothing answered the prompt yet, so the steering joins it, as messages
|
|
447
|
+
// queued before a response join its prompt.
|
|
448
|
+
turn.prompt = {
|
|
449
|
+
text: `${turn.prompt.text}\n\n${input.text}`,
|
|
450
|
+
files: [...turn.prompt.files, ...input.files],
|
|
451
|
+
}
|
|
452
|
+
} else if (steered && turn !== undefined) {
|
|
453
|
+
const at = turn.generations.length - 1
|
|
446
454
|
turn.steers.set(at, [...(turn.steers.get(at) ?? []), input])
|
|
447
455
|
} else {
|
|
448
456
|
turn = { prompt: input, generations: [], steers: new Map(), end: 'live' }
|
package/src/ai-pi.ts
CHANGED
|
@@ -436,7 +436,7 @@ async function runStep(
|
|
|
436
436
|
const result = results.get(toolCallId)
|
|
437
437
|
if (result !== undefined) return answeredTool(result, shape)
|
|
438
438
|
if (state.plan.end === 'live' && state.liveStep) {
|
|
439
|
-
// Pi now waits on tools that A2's tool
|
|
439
|
+
// Pi now waits on tools that A2's tool consumer run.
|
|
440
440
|
endStep(state)
|
|
441
441
|
return pending(signal)
|
|
442
442
|
}
|
|
@@ -962,7 +962,7 @@ function builtinDefinition(
|
|
|
962
962
|
|
|
963
963
|
/**
|
|
964
964
|
* The tools Pi's session declares: Pi's own, and the agent's. Their results
|
|
965
|
-
* come from A2's log; A2's tool
|
|
965
|
+
* come from A2's log; A2's tool consumer ran them.
|
|
966
966
|
*/
|
|
967
967
|
async function sessionTools(
|
|
968
968
|
modules: PiModules,
|
package/src/ai-server.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* experimental-a2/ai/server — `agent()`: the experimental-a2/ai protocol,
|
|
3
|
-
* its model and tool
|
|
3
|
+
* its model and tool consumers, and A2's store, scheduler, and telemetry,
|
|
4
4
|
* assembled as one ordinary A2 server.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
export { agent,
|
|
7
|
+
export { agent, runWithConsumerContext } from './ai-agent.ts'
|
|
8
8
|
export type {
|
|
9
9
|
AISDKHarness,
|
|
10
10
|
Agent,
|