experimental-a2 0.15.0 → 0.16.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 +73 -0
- package/dist/actor-client.d.ts +1 -1
- package/dist/{actor-BfQSE0KC.d.ts → actor-ohPC-81x.d.ts} +5 -5
- package/dist/{actor-BfQSE0KC.d.ts.map → actor-ohPC-81x.d.ts.map} +1 -1
- package/dist/actor-react.d.ts +3 -3
- package/dist/actor.d.ts +1 -1
- package/dist/actor.js +1 -1
- package/dist/ai-CrLYwNEx.js +2431 -0
- package/dist/ai-CrLYwNEx.js.map +1 -0
- package/dist/{ai-Cai-lCbj.d.ts → ai-HJ9fHfYI.d.ts} +72 -13
- package/dist/ai-HJ9fHfYI.d.ts.map +1 -0
- package/dist/ai-server.d.ts +4 -5
- package/dist/ai-server.d.ts.map +1 -1
- package/dist/ai-server.js +755 -290
- package/dist/ai-server.js.map +1 -1
- package/dist/ai.d.ts +2 -2
- package/dist/ai.js +1 -1999
- package/dist/client-BYzHjkwU.js.map +1 -1
- package/dist/{client-BAEABRZB.d.ts → client-CzyacQpJ.d.ts} +9 -9
- package/dist/client-CzyacQpJ.d.ts.map +1 -0
- package/dist/client.d.ts +1 -1
- package/dist/idempotent-id-BRJVeylj.js +13 -0
- package/dist/idempotent-id-BRJVeylj.js.map +1 -0
- package/dist/index.d.ts +3 -3
- package/dist/index.js +1 -12
- package/dist/otel.d.ts +1 -1
- package/dist/react.d.ts +13 -13
- package/dist/react.d.ts.map +1 -1
- package/dist/react.js.map +1 -1
- package/dist/reducer-DEMjEY_O.js.map +1 -1
- package/dist/{reducer-BcS9VDKC.d.ts → reducer-otzHjuJj.d.ts} +3 -3
- package/dist/{reducer-BcS9VDKC.d.ts.map → reducer-otzHjuJj.d.ts.map} +1 -1
- package/dist/scheduler-qstash.d.ts +2 -2
- package/dist/scheduler-qstash.js +1 -1
- package/dist/scheduler-vercel.d.ts +2 -2
- package/dist/scheduler-vercel.js +1 -1
- package/dist/{server-CjJSGcF7.js → server-BD5ckxZb.js} +39 -26
- package/dist/server-BD5ckxZb.js.map +1 -0
- package/dist/{server-Bp5Nd1pF.d.ts → server-DbD7IJVK.d.ts} +5 -5
- package/dist/{server-Bp5Nd1pF.d.ts.map → server-DbD7IJVK.d.ts.map} +1 -1
- package/dist/server.d.ts +3 -3
- package/dist/server.js +1 -1
- package/dist/{store-D_yhNdPz.d.ts → store-DyZM6fS5.d.ts} +3 -2
- package/dist/{store-D_yhNdPz.d.ts.map → store-DyZM6fS5.d.ts.map} +1 -1
- package/dist/store-N8PXxDAS.js.map +1 -1
- package/dist/store-memory.d.ts +1 -1
- package/dist/store-postgres.d.ts +1 -1
- package/dist/store-redis-http.d.ts +1 -1
- package/dist/store-redis-http.js +1 -1
- package/dist/{store-redis-notify-D2EI6gwX.js → store-redis-notify-BVLUfI3j.js} +14 -13
- package/dist/store-redis-notify-BVLUfI3j.js.map +1 -0
- package/dist/store-redis.d.ts +1 -1
- package/dist/store-redis.js +1 -1
- package/dist/store-sqlite.d.ts +1 -1
- package/dist/{telemetry-CpeclqB2.d.ts → telemetry-B5jzpy6y.d.ts} +2 -2
- package/dist/telemetry-B5jzpy6y.d.ts.map +1 -0
- package/docs/concepts/04-state.mdx +9 -4
- package/docs/guides/06-ai-agents.mdx +134 -43
- package/docs/reference/01-api.mdx +104 -27
- package/examples/playground/app/agent/[agentId]/agent-client.tsx +20 -28
- package/examples/playground/app/agent/[agentId]/agent-queue.test.tsx +123 -0
- package/examples/playground/app/agent/[agentId]/agent-queue.tsx +37 -43
- package/examples/playground/app/agent/[agentId]/compaction-panel.tsx +44 -24
- package/examples/playground/app/agent/compaction-timeline.test.ts +47 -7
- package/examples/playground/app/agent/compaction-timeline.ts +13 -3
- package/examples/playground/app/chat/[chatId]/chat-client.tsx +1 -5
- package/examples/playground/app/chat/[chatId]/session.ts +17 -2
- package/examples/playground/app/chat/model.test.ts +4 -4
- package/examples/playground/app/chat/model.ts +18 -6
- package/examples/playground/app/chat/server.ts +6 -3
- package/examples/playground/package.json +3 -1
- package/package.json +1 -1
- package/src/ai-client-state.ts +0 -2
- package/src/ai-context-schema.ts +296 -0
- package/src/ai-context.ts +680 -0
- package/src/ai-control-server.ts +160 -131
- package/src/ai-control-state.ts +6 -0
- package/src/ai-control.ts +6 -7
- package/src/ai-coordinator.ts +92 -38
- package/src/ai-id.ts +6 -0
- package/src/ai-input-tokens.ts +75 -0
- package/src/ai-message-projection.ts +348 -0
- package/src/ai-server.ts +232 -334
- package/src/ai-stored-state.ts +484 -0
- package/src/ai.ts +252 -418
- package/src/client.ts +21 -15
- package/src/react.ts +25 -19
- package/src/reducer.ts +2 -1
- package/src/server.ts +73 -56
- package/src/store-redis-core.ts +15 -17
- package/src/store.ts +1 -0
- package/src/telemetry.ts +2 -1
- package/dist/ai-Cai-lCbj.d.ts.map +0 -1
- package/dist/ai-control-CcD4hh3y.js +0 -119
- package/dist/ai-control-CcD4hh3y.js.map +0 -1
- package/dist/ai.js.map +0 -1
- package/dist/client-BAEABRZB.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/server-CjJSGcF7.js.map +0 -1
- package/dist/store-redis-notify-D2EI6gwX.js.map +0 -1
- package/dist/telemetry-CpeclqB2.d.ts.map +0 -1
package/src/ai-server.ts
CHANGED
|
@@ -9,10 +9,20 @@
|
|
|
9
9
|
// ordered; parallel consumption would corrupt progress sequence and state.
|
|
10
10
|
|
|
11
11
|
import { createControlRuntime, ControlCancelled } from './ai-control-server.ts'
|
|
12
|
-
import
|
|
12
|
+
import {
|
|
13
|
+
createContextReducer,
|
|
14
|
+
contextForAttempt,
|
|
15
|
+
generationBase,
|
|
16
|
+
type AIContextState,
|
|
17
|
+
type AIModelContext,
|
|
18
|
+
} from './ai-context.ts'
|
|
19
|
+
import type { Reducer } from './reducer.ts'
|
|
20
|
+
import { aiStateReducer } from './ai-stored-state.ts'
|
|
21
|
+
import { aiEventId, AI_EVENT_ID_PREFIX } from './ai-id.ts'
|
|
22
|
+
import type { ControlCommit, ControlCommand } from './ai-control.ts'
|
|
13
23
|
import { AsyncLocalStorage } from 'node:async_hooks'
|
|
14
24
|
import { createHash } from 'node:crypto'
|
|
15
|
-
import {
|
|
25
|
+
import { convertToModelMessages, isToolUIPart } from 'ai'
|
|
16
26
|
import type {
|
|
17
27
|
FinishReason,
|
|
18
28
|
Instructions,
|
|
@@ -26,14 +36,15 @@ import type {
|
|
|
26
36
|
} from 'ai'
|
|
27
37
|
import { generateAISDKStep, type AISDKStepSettings } from './ai-sdk-step.ts'
|
|
28
38
|
import { gatewayModelId, readModelLimits } from './ai-model-metadata.ts'
|
|
39
|
+
import { estimateInputTokens, measuredInputTokens } from './ai-input-tokens.ts'
|
|
29
40
|
import type {
|
|
30
41
|
AIEventDefs,
|
|
31
|
-
AIState,
|
|
32
42
|
AgentDefinition,
|
|
33
43
|
AgentToolContext,
|
|
34
44
|
AgentToolEventDefs,
|
|
35
45
|
ApprovalRequestedPayload,
|
|
36
46
|
CompactionCompletedPayload,
|
|
47
|
+
CompactionRequestedPayload,
|
|
37
48
|
GenerationCompletedPayload,
|
|
38
49
|
GenerationFailedPayload,
|
|
39
50
|
GenerationProgressPayload,
|
|
@@ -42,10 +53,7 @@ import type {
|
|
|
42
53
|
ToolCalledPayload,
|
|
43
54
|
ToolResultPayload,
|
|
44
55
|
} from './ai.ts'
|
|
45
|
-
import {
|
|
46
|
-
aiCoordinatorReducer,
|
|
47
|
-
type AICoordinatorState,
|
|
48
|
-
} from './ai-coordinator.ts'
|
|
56
|
+
import type { AICoordinatorState } from './ai-coordinator.ts'
|
|
49
57
|
import type { AppendInput, ContractEvent, EventDefs } from './contract.ts'
|
|
50
58
|
import { A2Error } from './errors.ts'
|
|
51
59
|
import {
|
|
@@ -116,7 +124,6 @@ export type AgentGenerateContext<
|
|
|
116
124
|
responseMessageId: string
|
|
117
125
|
messages: M[]
|
|
118
126
|
modelMessages: ModelMessage[]
|
|
119
|
-
state: AIState<M>
|
|
120
127
|
session: Pick<HandlerContext<D>['session'], 'state'>
|
|
121
128
|
signal: AbortSignal
|
|
122
129
|
model: LanguageModel
|
|
@@ -149,7 +156,7 @@ export type AgentResolverContext<
|
|
|
149
156
|
D extends AIEventDefs<M> & EventDefs,
|
|
150
157
|
> = {
|
|
151
158
|
event: ContractEvent<D, 'ai.generation.requested'>
|
|
152
|
-
|
|
159
|
+
messages: M[]
|
|
153
160
|
session: Pick<HandlerContext<D>['session'], 'state'>
|
|
154
161
|
signal: AbortSignal
|
|
155
162
|
}
|
|
@@ -472,13 +479,12 @@ const foldAIEvents = <
|
|
|
472
479
|
M extends UIMessage,
|
|
473
480
|
D extends AIEventDefs<M> & EventDefs,
|
|
474
481
|
>(options: {
|
|
475
|
-
|
|
476
|
-
state:
|
|
482
|
+
reducer: Reducer<D, AIContextState<M>>
|
|
483
|
+
state: AIContextState<M>
|
|
477
484
|
events: ContractEvent<D>[]
|
|
478
|
-
}):
|
|
485
|
+
}): AIContextState<M> => {
|
|
479
486
|
let state = options.state
|
|
480
|
-
for (const event of options.events)
|
|
481
|
-
state = options.agent.reducer.fold(state, event)
|
|
487
|
+
for (const event of options.events) state = options.reducer.fold(state, event)
|
|
482
488
|
return state
|
|
483
489
|
}
|
|
484
490
|
|
|
@@ -539,7 +545,10 @@ const summarize = async <
|
|
|
539
545
|
...options.context.modelMessages,
|
|
540
546
|
{ role: 'user', content: instruction },
|
|
541
547
|
],
|
|
542
|
-
responseMessageId:
|
|
548
|
+
responseMessageId: await aiEventId(
|
|
549
|
+
'a2.ai.summary',
|
|
550
|
+
options.context.generationId,
|
|
551
|
+
),
|
|
543
552
|
},
|
|
544
553
|
messageMetadata: undefined,
|
|
545
554
|
})
|
|
@@ -573,98 +582,26 @@ const summarize = async <
|
|
|
573
582
|
}
|
|
574
583
|
|
|
575
584
|
const activeCompaction = <M extends UIMessage>(
|
|
576
|
-
state:
|
|
577
|
-
):
|
|
585
|
+
state: AIModelContext<M>,
|
|
586
|
+
): AIModelContext<M>['compaction'] => {
|
|
578
587
|
const compaction = state.compaction
|
|
579
|
-
return compaction?.status === 'completed'
|
|
580
|
-
compaction.messages !== undefined &&
|
|
581
|
-
state.messages.some((message) => message.id === compaction.throughMessageId)
|
|
582
|
-
? compaction
|
|
583
|
-
: null
|
|
588
|
+
return compaction?.status === 'completed' ? compaction : null
|
|
584
589
|
}
|
|
585
590
|
|
|
586
|
-
const contextMessages =
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
session: HandlerContext<D>['session']
|
|
592
|
-
snapshot: { state: AIState<M>; index: number }
|
|
591
|
+
const contextMessages = <M extends UIMessage>({
|
|
592
|
+
state,
|
|
593
|
+
coordinator,
|
|
594
|
+
}: {
|
|
595
|
+
state: AIModelContext<M>
|
|
593
596
|
coordinator: AICoordinatorState
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
const { agent, session, snapshot } = options
|
|
598
|
-
const appended = options.appended ?? []
|
|
599
|
-
const state = foldAIEvents({ agent, events: appended, state: snapshot.state })
|
|
600
|
-
const compaction = activeCompaction(state)
|
|
601
|
-
const queued = new Set(
|
|
602
|
-
options.coordinator.queued.map((item) => item.messageId),
|
|
603
|
-
)
|
|
604
|
-
const visible = (messages: M[]): M[] =>
|
|
605
|
-
messages.filter((message) => !queued.has(message.id))
|
|
606
|
-
if (compaction === null) return visible(state.messages)
|
|
607
|
-
const retained = new Set(compaction.retainedMessageIds ?? [])
|
|
608
|
-
const throughIndex = compaction.throughIndex
|
|
609
|
-
if (throughIndex !== undefined) {
|
|
610
|
-
const [prefix, priorCoordinator, tail] = await Promise.all([
|
|
611
|
-
throughIndex === snapshot.index
|
|
612
|
-
? Promise.resolve(snapshot)
|
|
613
|
-
: session.state(agent.reducer, { through: throughIndex }),
|
|
614
|
-
session.state(aiCoordinatorReducer(agent.contract), {
|
|
615
|
-
through: throughIndex,
|
|
616
|
-
}),
|
|
617
|
-
throughIndex >= snapshot.index
|
|
618
|
-
? Promise.resolve([])
|
|
619
|
-
: session.history({ gte: throughIndex + 1, lte: snapshot.index }),
|
|
620
|
-
])
|
|
621
|
-
for (const item of priorCoordinator.state.queued)
|
|
622
|
-
retained.add(item.messageId)
|
|
623
|
-
const events =
|
|
624
|
-
options.excluded === undefined
|
|
625
|
-
? tail
|
|
626
|
-
: withoutGenerationLifecycle(tail, options.excluded)
|
|
627
|
-
const context = foldAIEvents({
|
|
628
|
-
agent,
|
|
629
|
-
events: [...events, ...appended],
|
|
630
|
-
state: {
|
|
631
|
-
...prefix.state,
|
|
632
|
-
messages: [
|
|
633
|
-
...compaction.messages!,
|
|
634
|
-
...prefix.state.messages.filter((message) =>
|
|
635
|
-
retained.has(message.id),
|
|
636
|
-
),
|
|
637
|
-
],
|
|
638
|
-
activeProjection: null,
|
|
639
|
-
},
|
|
640
|
-
})
|
|
641
|
-
const positions = new Map<string, number>()
|
|
642
|
-
for (const message of [...compaction.messages!, ...state.messages]) {
|
|
643
|
-
if (!positions.has(message.id)) positions.set(message.id, positions.size)
|
|
644
|
-
}
|
|
645
|
-
return visible(
|
|
646
|
-
context.messages.toSorted(
|
|
647
|
-
(left, right) =>
|
|
648
|
-
(positions.get(left.id) ?? positions.size) -
|
|
649
|
-
(positions.get(right.id) ?? positions.size),
|
|
650
|
-
),
|
|
651
|
-
)
|
|
652
|
-
}
|
|
653
|
-
const boundary = state.messages.findIndex(
|
|
654
|
-
(message) => message.id === compaction.throughMessageId,
|
|
655
|
-
)
|
|
656
|
-
return visible([
|
|
657
|
-
...compaction.messages!,
|
|
658
|
-
...state.messages
|
|
659
|
-
.slice(0, boundary + 1)
|
|
660
|
-
.filter((message) => retained.has(message.id)),
|
|
661
|
-
...state.messages.slice(boundary + 1),
|
|
662
|
-
])
|
|
597
|
+
}): M[] => {
|
|
598
|
+
const queued = new Set(coordinator.queued.map((item) => item.messageId))
|
|
599
|
+
return state.messages.filter((message) => !queued.has(message.id))
|
|
663
600
|
}
|
|
664
601
|
|
|
665
602
|
const modelContext = async <M extends UIMessage>(options: {
|
|
666
603
|
messages: M[]
|
|
667
|
-
state:
|
|
604
|
+
state: AIModelContext<M>
|
|
668
605
|
tools: ToolSet
|
|
669
606
|
}): Promise<ModelMessage[]> => {
|
|
670
607
|
const messages = await convertToModelMessages(options.messages, {
|
|
@@ -676,45 +613,6 @@ const modelContext = async <M extends UIMessage>(options: {
|
|
|
676
613
|
: [{ role: 'user', content: summary }, ...messages]
|
|
677
614
|
}
|
|
678
615
|
|
|
679
|
-
const estimateInputTokens = async (options: {
|
|
680
|
-
messages: ModelMessage[]
|
|
681
|
-
instructions: Instructions | undefined
|
|
682
|
-
tools: ToolSet
|
|
683
|
-
}): Promise<number> => {
|
|
684
|
-
const tools = await Promise.all(
|
|
685
|
-
Object.entries(options.tools).map(async ([name, tool]) => ({
|
|
686
|
-
name,
|
|
687
|
-
description: tool.description,
|
|
688
|
-
inputSchema: await asSchema(tool.inputSchema).jsonSchema,
|
|
689
|
-
})),
|
|
690
|
-
)
|
|
691
|
-
return Math.ceil(
|
|
692
|
-
new TextEncoder().encode(
|
|
693
|
-
JSON.stringify({
|
|
694
|
-
messages: options.messages,
|
|
695
|
-
instructions: options.instructions,
|
|
696
|
-
tools,
|
|
697
|
-
}),
|
|
698
|
-
).byteLength / 4,
|
|
699
|
-
)
|
|
700
|
-
}
|
|
701
|
-
|
|
702
|
-
const measuredInputTokens = (options: {
|
|
703
|
-
estimate: number
|
|
704
|
-
model: string
|
|
705
|
-
calibration: AICoordinatorState['calibration']
|
|
706
|
-
}): number => {
|
|
707
|
-
const previous = options.calibration
|
|
708
|
-
return previous !== undefined &&
|
|
709
|
-
previous.model === options.model &&
|
|
710
|
-
options.estimate >= previous.estimate
|
|
711
|
-
? Math.max(
|
|
712
|
-
options.estimate,
|
|
713
|
-
Math.ceil(previous.inputTokens + options.estimate - previous.estimate),
|
|
714
|
-
)
|
|
715
|
-
: options.estimate
|
|
716
|
-
}
|
|
717
|
-
|
|
718
616
|
type ToolCallClassification = 'automatic' | 'approval' | 'unknown'
|
|
719
617
|
|
|
720
618
|
type PendingToolCall = {
|
|
@@ -722,12 +620,16 @@ type PendingToolCall = {
|
|
|
722
620
|
classification: ToolCallClassification
|
|
723
621
|
}
|
|
724
622
|
|
|
725
|
-
const toolCalledEvent = <D extends EventDefs>(
|
|
623
|
+
const toolCalledEvent = async <D extends EventDefs>(
|
|
726
624
|
payload: ToolCalledPayload,
|
|
727
|
-
): AppendInput<D
|
|
625
|
+
): Promise<AppendInput<D>> =>
|
|
728
626
|
({
|
|
729
627
|
type: 'ai.tool.called',
|
|
730
|
-
id:
|
|
628
|
+
id: await aiEventId(
|
|
629
|
+
'a2.ai.tool.called',
|
|
630
|
+
payload.generationId,
|
|
631
|
+
payload.toolCallId,
|
|
632
|
+
),
|
|
731
633
|
payload,
|
|
732
634
|
}) as AppendInput<D>
|
|
733
635
|
|
|
@@ -757,7 +659,10 @@ const approvalClassification = <T extends ToolSet>(
|
|
|
757
659
|
return tool?.needsApproval === true ? 'approval' : 'automatic'
|
|
758
660
|
}
|
|
759
661
|
|
|
760
|
-
const lifecycleEvents = <
|
|
662
|
+
const lifecycleEvents = async <
|
|
663
|
+
D extends EventDefs,
|
|
664
|
+
T extends ToolSet,
|
|
665
|
+
>(options: {
|
|
761
666
|
requestId: string
|
|
762
667
|
messageId: string
|
|
763
668
|
generationId: string
|
|
@@ -767,7 +672,7 @@ const lifecycleEvents = <D extends EventDefs, T extends ToolSet>(options: {
|
|
|
767
672
|
tools: T
|
|
768
673
|
generation: AgentGenerationSettings<T>
|
|
769
674
|
custom: boolean
|
|
770
|
-
}): { events: AppendInput<D>[]; pending: PendingToolCall[] } => {
|
|
675
|
+
}): Promise<{ events: AppendInput<D>[]; pending: PendingToolCall[] }> => {
|
|
771
676
|
const result: AppendInput<D>[] = []
|
|
772
677
|
const pending = [...options.pending]
|
|
773
678
|
const take = (toolCallId: string): PendingToolCall | undefined => {
|
|
@@ -777,12 +682,18 @@ const lifecycleEvents = <D extends EventDefs, T extends ToolSet>(options: {
|
|
|
777
682
|
if (index === -1) return undefined
|
|
778
683
|
return pending.splice(index, 1)[0]
|
|
779
684
|
}
|
|
780
|
-
const flush = (toolCallId: string): void => {
|
|
685
|
+
const flush = async (toolCallId: string): Promise<void> => {
|
|
781
686
|
const pendingCall = take(toolCallId)
|
|
782
|
-
if (pendingCall) result.push(toolCalledEvent<D>(pendingCall.call))
|
|
687
|
+
if (pendingCall) result.push(await toolCalledEvent<D>(pendingCall.call))
|
|
783
688
|
}
|
|
784
689
|
for (const [chunkIndex, chunk] of options.chunks.entries()) {
|
|
785
|
-
const resultId =
|
|
690
|
+
const resultId = (): Promise<string> =>
|
|
691
|
+
aiEventId(
|
|
692
|
+
'a2.ai.tool.stream-result',
|
|
693
|
+
options.generationId,
|
|
694
|
+
String(options.sequence),
|
|
695
|
+
String(chunkIndex),
|
|
696
|
+
)
|
|
786
697
|
if (chunk.type === 'tool-input-available') {
|
|
787
698
|
const payload: ToolCalledPayload = {
|
|
788
699
|
requestId: options.requestId,
|
|
@@ -816,7 +727,7 @@ const lifecycleEvents = <D extends EventDefs, T extends ToolSet>(options: {
|
|
|
816
727
|
chunk.toolName,
|
|
817
728
|
)
|
|
818
729
|
if (classification === 'automatic') {
|
|
819
|
-
result.push(toolCalledEvent<D>(payload))
|
|
730
|
+
result.push(await toolCalledEvent<D>(payload))
|
|
820
731
|
} else {
|
|
821
732
|
pending.push({ call: payload, classification })
|
|
822
733
|
}
|
|
@@ -850,13 +761,13 @@ const lifecycleEvents = <D extends EventDefs, T extends ToolSet>(options: {
|
|
|
850
761
|
input: chunk.input,
|
|
851
762
|
}
|
|
852
763
|
take(chunk.toolCallId)
|
|
853
|
-
result.push(toolCalledEvent<D>(called), {
|
|
764
|
+
result.push(await toolCalledEvent<D>(called), {
|
|
854
765
|
type: 'ai.tool.result',
|
|
855
|
-
id: resultId,
|
|
766
|
+
id: await resultId(),
|
|
856
767
|
payload,
|
|
857
768
|
} as AppendInput<D>)
|
|
858
769
|
} else if (chunk.type === 'tool-output-available') {
|
|
859
|
-
flush(chunk.toolCallId)
|
|
770
|
+
await flush(chunk.toolCallId)
|
|
860
771
|
const payload: ToolResultPayload = {
|
|
861
772
|
requestId: options.requestId,
|
|
862
773
|
messageId: options.messageId,
|
|
@@ -880,11 +791,11 @@ const lifecycleEvents = <D extends EventDefs, T extends ToolSet>(options: {
|
|
|
880
791
|
}
|
|
881
792
|
result.push({
|
|
882
793
|
type: 'ai.tool.result',
|
|
883
|
-
id: resultId,
|
|
794
|
+
id: await resultId(),
|
|
884
795
|
payload,
|
|
885
796
|
} as AppendInput<D>)
|
|
886
797
|
} else if (chunk.type === 'tool-output-error') {
|
|
887
|
-
flush(chunk.toolCallId)
|
|
798
|
+
await flush(chunk.toolCallId)
|
|
888
799
|
const payload: ToolResultPayload = {
|
|
889
800
|
requestId: options.requestId,
|
|
890
801
|
messageId: options.messageId,
|
|
@@ -905,11 +816,11 @@ const lifecycleEvents = <D extends EventDefs, T extends ToolSet>(options: {
|
|
|
905
816
|
}
|
|
906
817
|
result.push({
|
|
907
818
|
type: 'ai.tool.result',
|
|
908
|
-
id: resultId,
|
|
819
|
+
id: await resultId(),
|
|
909
820
|
payload,
|
|
910
821
|
} as AppendInput<D>)
|
|
911
822
|
} else if (chunk.type === 'tool-output-denied') {
|
|
912
|
-
flush(chunk.toolCallId)
|
|
823
|
+
await flush(chunk.toolCallId)
|
|
913
824
|
const payload: ToolResultPayload = {
|
|
914
825
|
requestId: options.requestId,
|
|
915
826
|
messageId: options.messageId,
|
|
@@ -920,12 +831,13 @@ const lifecycleEvents = <D extends EventDefs, T extends ToolSet>(options: {
|
|
|
920
831
|
}
|
|
921
832
|
result.push({
|
|
922
833
|
type: 'ai.tool.result',
|
|
923
|
-
id: resultId,
|
|
834
|
+
id: await resultId(),
|
|
924
835
|
payload,
|
|
925
836
|
} as AppendInput<D>)
|
|
926
837
|
} else if (chunk.type === 'tool-approval-request') {
|
|
927
|
-
flush(chunk.toolCallId)
|
|
838
|
+
await flush(chunk.toolCallId)
|
|
928
839
|
const payload: ApprovalRequestedPayload = {
|
|
840
|
+
requestId: options.requestId,
|
|
929
841
|
messageId: options.messageId,
|
|
930
842
|
generationId: options.generationId,
|
|
931
843
|
approvalId: chunk.approvalId,
|
|
@@ -939,14 +851,23 @@ const lifecycleEvents = <D extends EventDefs, T extends ToolSet>(options: {
|
|
|
939
851
|
}
|
|
940
852
|
result.push({
|
|
941
853
|
type: 'ai.approval.requested',
|
|
942
|
-
id:
|
|
854
|
+
id: await aiEventId(
|
|
855
|
+
'a2.ai.approval.requested',
|
|
856
|
+
options.generationId,
|
|
857
|
+
chunk.approvalId,
|
|
858
|
+
),
|
|
943
859
|
payload,
|
|
944
860
|
} as AppendInput<D>)
|
|
945
861
|
} else if (chunk.type === 'tool-approval-response') {
|
|
946
862
|
result.push({
|
|
947
863
|
type: 'ai.approval.responded',
|
|
948
|
-
id:
|
|
864
|
+
id: await aiEventId(
|
|
865
|
+
'a2.ai.approval.automatic-response',
|
|
866
|
+
options.generationId,
|
|
867
|
+
chunk.approvalId,
|
|
868
|
+
),
|
|
949
869
|
payload: {
|
|
870
|
+
requestId: options.requestId,
|
|
950
871
|
messageId: options.messageId,
|
|
951
872
|
generationId: options.generationId,
|
|
952
873
|
approvalId: chunk.approvalId,
|
|
@@ -959,73 +880,6 @@ const lifecycleEvents = <D extends EventDefs, T extends ToolSet>(options: {
|
|
|
959
880
|
return { events: result, pending }
|
|
960
881
|
}
|
|
961
882
|
|
|
962
|
-
function withoutGenerationLifecycle<D extends EventDefs>(
|
|
963
|
-
history: ContractEvent<D>[],
|
|
964
|
-
generationIds: ReadonlySet<string>,
|
|
965
|
-
): ContractEvent<D>[] {
|
|
966
|
-
if (generationIds.size === 0) return history
|
|
967
|
-
const approvalKeys = new Set<string>()
|
|
968
|
-
const inputKeys = new Set<string>()
|
|
969
|
-
for (const event of history) {
|
|
970
|
-
const payload = event.payload as {
|
|
971
|
-
generationId?: unknown
|
|
972
|
-
messageId?: unknown
|
|
973
|
-
approvalId?: unknown
|
|
974
|
-
inputId?: unknown
|
|
975
|
-
}
|
|
976
|
-
if (
|
|
977
|
-
typeof payload.generationId !== 'string' ||
|
|
978
|
-
!generationIds.has(payload.generationId) ||
|
|
979
|
-
typeof payload.messageId !== 'string'
|
|
980
|
-
) {
|
|
981
|
-
continue
|
|
982
|
-
}
|
|
983
|
-
if (
|
|
984
|
-
event.type === 'ai.approval.requested' &&
|
|
985
|
-
typeof payload.approvalId === 'string'
|
|
986
|
-
) {
|
|
987
|
-
approvalKeys.add(`${payload.messageId}\u001f${payload.approvalId}`)
|
|
988
|
-
}
|
|
989
|
-
if (
|
|
990
|
-
event.type === 'ai.input.requested' &&
|
|
991
|
-
typeof payload.inputId === 'string'
|
|
992
|
-
) {
|
|
993
|
-
inputKeys.add(`${payload.messageId}\u001f${payload.inputId}`)
|
|
994
|
-
}
|
|
995
|
-
}
|
|
996
|
-
return history.filter((event) => {
|
|
997
|
-
const payload = event.payload as {
|
|
998
|
-
generationId?: unknown
|
|
999
|
-
messageId?: unknown
|
|
1000
|
-
approvalId?: unknown
|
|
1001
|
-
inputId?: unknown
|
|
1002
|
-
}
|
|
1003
|
-
if (
|
|
1004
|
-
typeof payload.generationId === 'string' &&
|
|
1005
|
-
generationIds.has(payload.generationId)
|
|
1006
|
-
) {
|
|
1007
|
-
return false
|
|
1008
|
-
}
|
|
1009
|
-
if (
|
|
1010
|
-
event.type === 'ai.approval.responded' &&
|
|
1011
|
-
typeof payload.messageId === 'string' &&
|
|
1012
|
-
typeof payload.approvalId === 'string'
|
|
1013
|
-
) {
|
|
1014
|
-
return !approvalKeys.has(
|
|
1015
|
-
`${payload.messageId}\u001f${payload.approvalId}`,
|
|
1016
|
-
)
|
|
1017
|
-
}
|
|
1018
|
-
if (
|
|
1019
|
-
event.type === 'ai.input.responded' &&
|
|
1020
|
-
typeof payload.messageId === 'string' &&
|
|
1021
|
-
typeof payload.inputId === 'string'
|
|
1022
|
-
) {
|
|
1023
|
-
return !inputKeys.has(`${payload.messageId}\u001f${payload.inputId}`)
|
|
1024
|
-
}
|
|
1025
|
-
return true
|
|
1026
|
-
})
|
|
1027
|
-
}
|
|
1028
|
-
|
|
1029
883
|
const validateCompaction = <
|
|
1030
884
|
M extends UIMessage,
|
|
1031
885
|
D extends AIEventDefs<M> & EventDefs,
|
|
@@ -1135,6 +989,14 @@ export function createHandlers<
|
|
|
1135
989
|
const maxSteps = options.maxSteps ?? Number.POSITIVE_INFINITY
|
|
1136
990
|
const control = createControlRuntime({ agent: options.agent })
|
|
1137
991
|
const coordinator = control.coordinator
|
|
992
|
+
const contextReducer = createContextReducer({ agent: options.agent })
|
|
993
|
+
const readContext = async (
|
|
994
|
+
session: HandlerContext<D>['session'],
|
|
995
|
+
through: number | 'latest',
|
|
996
|
+
) => {
|
|
997
|
+
const snapshot = await session.state(contextReducer, { through })
|
|
998
|
+
return { state: contextReducer.view(snapshot.state), index: snapshot.index }
|
|
999
|
+
}
|
|
1138
1000
|
const promptCache = new Map<string, Promise<ModelMessage[]>>()
|
|
1139
1001
|
|
|
1140
1002
|
type RuntimeTool = {
|
|
@@ -1150,7 +1012,7 @@ export function createHandlers<
|
|
|
1150
1012
|
|
|
1151
1013
|
type ToolHandlerContext = HandlerContext<D, 'ai.tool.execution.requested'>
|
|
1152
1014
|
|
|
1153
|
-
const resultEvent = (
|
|
1015
|
+
const resultEvent = async (
|
|
1154
1016
|
call: ToolCalledPayload,
|
|
1155
1017
|
suffix: string,
|
|
1156
1018
|
result: {
|
|
@@ -1159,10 +1021,15 @@ export function createHandlers<
|
|
|
1159
1021
|
denied?: boolean
|
|
1160
1022
|
preliminary?: boolean
|
|
1161
1023
|
},
|
|
1162
|
-
): AppendInput<D
|
|
1024
|
+
): Promise<AppendInput<D>> =>
|
|
1163
1025
|
({
|
|
1164
1026
|
type: 'ai.tool.result',
|
|
1165
|
-
id:
|
|
1027
|
+
id: await aiEventId(
|
|
1028
|
+
'a2.ai.tool.result',
|
|
1029
|
+
call.generationId,
|
|
1030
|
+
call.toolCallId,
|
|
1031
|
+
suffix,
|
|
1032
|
+
),
|
|
1166
1033
|
payload: {
|
|
1167
1034
|
requestId: call.requestId,
|
|
1168
1035
|
messageId: call.messageId,
|
|
@@ -1200,32 +1067,20 @@ export function createHandlers<
|
|
|
1200
1067
|
const frontier = owner.promptThroughIndex
|
|
1201
1068
|
if (frontier === undefined)
|
|
1202
1069
|
throw new TypeError('AI work requires a prompt checkpoint')
|
|
1203
|
-
const
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
const appended = tail.filter(
|
|
1209
|
-
(event) =>
|
|
1210
|
-
(event.type === 'ai.generation.started' ||
|
|
1211
|
-
event.type === 'ai.compaction.requested' ||
|
|
1212
|
-
event.type === 'ai.compaction.completed') &&
|
|
1213
|
-
(event.payload as { generationId: string }).generationId ===
|
|
1214
|
-
call.generationId,
|
|
1070
|
+
const snapshot = await readContext(ctx.session, ctx.event.index)
|
|
1071
|
+
if (
|
|
1072
|
+
snapshot.state.generation?.owner.generationId !== owner.generationId ||
|
|
1073
|
+
snapshot.state.generation.owner.requestId !== owner.requestId ||
|
|
1074
|
+
snapshot.state.activeProjection?.generationId !== owner.generationId
|
|
1215
1075
|
)
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
})
|
|
1076
|
+
throw new TypeError(
|
|
1077
|
+
'Tool prompt checkpoint does not own its generation',
|
|
1078
|
+
)
|
|
1079
|
+
const state = generationBase(snapshot.state)
|
|
1221
1080
|
return modelContext({
|
|
1222
|
-
messages:
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
snapshot,
|
|
1226
|
-
coordinator: atPrompt.state,
|
|
1227
|
-
appended,
|
|
1228
|
-
}),
|
|
1081
|
+
messages: state.messages.filter(
|
|
1082
|
+
(message) => state.messageIndexes[message.id]! <= frontier,
|
|
1083
|
+
),
|
|
1229
1084
|
state,
|
|
1230
1085
|
tools,
|
|
1231
1086
|
})
|
|
@@ -1237,11 +1092,11 @@ export function createHandlers<
|
|
|
1237
1092
|
return computation
|
|
1238
1093
|
}
|
|
1239
1094
|
|
|
1240
|
-
const toolExecutionFailure = (
|
|
1095
|
+
const toolExecutionFailure = async (
|
|
1241
1096
|
ctx: ToolHandlerContext,
|
|
1242
1097
|
call: ToolCalledPayload,
|
|
1243
1098
|
error: unknown,
|
|
1244
|
-
): AppendInput<D> | void => {
|
|
1099
|
+
): Promise<AppendInput<D> | void> => {
|
|
1245
1100
|
const schedulerFailure = consumeSchedulerSendFailure(error)
|
|
1246
1101
|
if (checkAbort(ctx.signal)) return
|
|
1247
1102
|
if (schedulerFailure === 'retryable') throw error
|
|
@@ -1297,7 +1152,7 @@ export function createHandlers<
|
|
|
1297
1152
|
ctx,
|
|
1298
1153
|
name: `tool:${call.toolCallId}:preliminary:${sequence}`,
|
|
1299
1154
|
events: [
|
|
1300
|
-
resultEvent(
|
|
1155
|
+
await resultEvent(
|
|
1301
1156
|
call,
|
|
1302
1157
|
`${ctx.event.id}:execution:${ctx.attempt}:${sequence}:preliminary`,
|
|
1303
1158
|
{
|
|
@@ -1393,12 +1248,12 @@ export function createHandlers<
|
|
|
1393
1248
|
if (checkAbort(ctx.signal)) return
|
|
1394
1249
|
const requestId = ctx.event.id
|
|
1395
1250
|
const request = ctx.event.payload
|
|
1396
|
-
const snapshot = await ctx.session
|
|
1397
|
-
through: 'latest',
|
|
1398
|
-
})
|
|
1251
|
+
const snapshot = await readContext(ctx.session, 'latest')
|
|
1399
1252
|
if (
|
|
1400
|
-
snapshot.state.
|
|
1401
|
-
snapshot.state.
|
|
1253
|
+
snapshot.state.closed ||
|
|
1254
|
+
snapshot.state.authority?.requestId !== requestId ||
|
|
1255
|
+
snapshot.state.authority?.phase === 'paused' ||
|
|
1256
|
+
snapshot.state.authority?.phase === 'pausing'
|
|
1402
1257
|
)
|
|
1403
1258
|
return
|
|
1404
1259
|
const coordinatorState = (
|
|
@@ -1423,54 +1278,29 @@ export function createHandlers<
|
|
|
1423
1278
|
return
|
|
1424
1279
|
if (current !== undefined && current.attempt >= ctx.attempt) return
|
|
1425
1280
|
const attempt = ctx.attempt
|
|
1426
|
-
const generationId =
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
(
|
|
1430
|
-
|
|
1431
|
-
|
|
1281
|
+
const generationId = await aiEventId(
|
|
1282
|
+
'a2.ai.generation',
|
|
1283
|
+
requestId,
|
|
1284
|
+
String(attempt),
|
|
1285
|
+
)
|
|
1286
|
+
const responseMessageId = request.responseMessageId
|
|
1432
1287
|
const responseStepCount = response.stepCount
|
|
1433
1288
|
if (
|
|
1434
1289
|
request.reason === 'tool' &&
|
|
1435
|
-
(
|
|
1436
|
-
|
|
1290
|
+
(request.sourceGenerationId !==
|
|
1291
|
+
response.source?.generation.generationId ||
|
|
1437
1292
|
response.responseMessageId !== request.messageId ||
|
|
1438
1293
|
response.responseMessageId !== request.responseMessageId)
|
|
1439
1294
|
)
|
|
1440
1295
|
return
|
|
1441
1296
|
const previous = current
|
|
1442
|
-
const
|
|
1297
|
+
const state =
|
|
1443
1298
|
previous === undefined
|
|
1444
|
-
?
|
|
1445
|
-
:
|
|
1446
|
-
if (request.reason === 'retry' && response.source?.failed) {
|
|
1447
|
-
replaced.push({
|
|
1448
|
-
generation: response.source.generation,
|
|
1449
|
-
frontier: response.source.promptThroughIndex,
|
|
1450
|
-
})
|
|
1451
|
-
}
|
|
1452
|
-
const replacedGenerationIds = new Set(
|
|
1453
|
-
replaced.map(({ generation: owner }) => owner.generationId),
|
|
1454
|
-
)
|
|
1455
|
-
let state = snapshot.state
|
|
1456
|
-
if (replaced.length > 0) {
|
|
1457
|
-
const through = Math.min(...replaced.map(({ frontier }) => frontier))
|
|
1458
|
-
const baseline = await ctx.session.state(options.agent.reducer, {
|
|
1459
|
-
through,
|
|
1460
|
-
})
|
|
1461
|
-
const tail = await ctx.session.history({
|
|
1462
|
-
gte: through + 1,
|
|
1463
|
-
lte: snapshot.index,
|
|
1464
|
-
})
|
|
1465
|
-
state = foldAIEvents({
|
|
1466
|
-
agent: options.agent,
|
|
1467
|
-
events: withoutGenerationLifecycle(tail, replacedGenerationIds),
|
|
1468
|
-
state: baseline.state,
|
|
1469
|
-
})
|
|
1470
|
-
}
|
|
1299
|
+
? snapshot.state
|
|
1300
|
+
: contextForAttempt(snapshot.state)
|
|
1471
1301
|
const resolverContext: AgentResolverContext<M, D> = {
|
|
1472
1302
|
event: ctx.event,
|
|
1473
|
-
state,
|
|
1303
|
+
messages: contextMessages({ state, coordinator: coordinatorState }),
|
|
1474
1304
|
session: {
|
|
1475
1305
|
state: (reducer, readOptions) =>
|
|
1476
1306
|
ctx.session.state(reducer, {
|
|
@@ -1509,7 +1339,7 @@ export function createHandlers<
|
|
|
1509
1339
|
if (source === undefined) return
|
|
1510
1340
|
return {
|
|
1511
1341
|
type: 'ai.generation.failed',
|
|
1512
|
-
id:
|
|
1342
|
+
id: await aiEventId('a2.ai.step-limit', requestId),
|
|
1513
1343
|
payload: {
|
|
1514
1344
|
requestId: source.requestId,
|
|
1515
1345
|
messageId: source.messageId,
|
|
@@ -1548,7 +1378,12 @@ export function createHandlers<
|
|
|
1548
1378
|
) {
|
|
1549
1379
|
startEvents.push({
|
|
1550
1380
|
type: 'ai.model.metadata.requested',
|
|
1551
|
-
id:
|
|
1381
|
+
id: await aiEventId(
|
|
1382
|
+
'a2.ai.model.metadata',
|
|
1383
|
+
options.agent.contract.name,
|
|
1384
|
+
ctx.event.sessionId,
|
|
1385
|
+
catalogModelId,
|
|
1386
|
+
),
|
|
1552
1387
|
payload: { modelId: catalogModelId },
|
|
1553
1388
|
} as AppendInput<D>)
|
|
1554
1389
|
}
|
|
@@ -1564,7 +1399,7 @@ export function createHandlers<
|
|
|
1564
1399
|
}
|
|
1565
1400
|
startEvents.push({
|
|
1566
1401
|
type: 'ai.generation.failed',
|
|
1567
|
-
id:
|
|
1402
|
+
id: await aiEventId('a2.ai.superseded', payload.generationId),
|
|
1568
1403
|
payload: superseded,
|
|
1569
1404
|
} as AppendInput<D>)
|
|
1570
1405
|
}
|
|
@@ -1583,12 +1418,9 @@ export function createHandlers<
|
|
|
1583
1418
|
if (checkAbort(ctx.signal)) return
|
|
1584
1419
|
|
|
1585
1420
|
const promptCoordinatorState = coordinatorState
|
|
1586
|
-
const messages =
|
|
1587
|
-
|
|
1588
|
-
session: ctx.session,
|
|
1589
|
-
snapshot: { state, index: snapshot.index },
|
|
1421
|
+
const messages = contextMessages({
|
|
1422
|
+
state,
|
|
1590
1423
|
coordinator: promptCoordinatorState,
|
|
1591
|
-
excluded: replacedGenerationIds,
|
|
1592
1424
|
})
|
|
1593
1425
|
let generationMessages = messages
|
|
1594
1426
|
let modelMessages = await modelContext({ messages, state, tools })
|
|
@@ -1599,7 +1431,6 @@ export function createHandlers<
|
|
|
1599
1431
|
responseMessageId,
|
|
1600
1432
|
messages,
|
|
1601
1433
|
modelMessages,
|
|
1602
|
-
state,
|
|
1603
1434
|
session: resolverContext.session,
|
|
1604
1435
|
signal: ctx.signal,
|
|
1605
1436
|
model: resolvedModel,
|
|
@@ -1624,8 +1455,24 @@ export function createHandlers<
|
|
|
1624
1455
|
: undefined
|
|
1625
1456
|
let inputTokenEstimate: number | undefined
|
|
1626
1457
|
let compacted = false
|
|
1627
|
-
|
|
1628
|
-
|
|
1458
|
+
let compactionEstimate:
|
|
1459
|
+
| Pick<
|
|
1460
|
+
CompactionRequestedPayload,
|
|
1461
|
+
'inputTokenEstimate' | 'inputTokens' | 'thresholdTokens'
|
|
1462
|
+
>
|
|
1463
|
+
| undefined
|
|
1464
|
+
const hasPendingTools = messages.some((message) =>
|
|
1465
|
+
message.parts.some(
|
|
1466
|
+
(part) =>
|
|
1467
|
+
isToolUIPart(part) &&
|
|
1468
|
+
!(
|
|
1469
|
+
part.state === 'output-error' ||
|
|
1470
|
+
part.state === 'output-denied' ||
|
|
1471
|
+
(part.state === 'output-available' && part.preliminary !== true)
|
|
1472
|
+
),
|
|
1473
|
+
),
|
|
1474
|
+
)
|
|
1475
|
+
if (policy && !hasPendingTools) {
|
|
1629
1476
|
const compactionContext = {
|
|
1630
1477
|
...resolverContext,
|
|
1631
1478
|
messages,
|
|
@@ -1665,10 +1512,24 @@ export function createHandlers<
|
|
|
1665
1512
|
)
|
|
1666
1513
|
}
|
|
1667
1514
|
shouldCompact = threshold !== undefined && inputTokens >= threshold
|
|
1515
|
+
if (threshold !== undefined && shouldCompact) {
|
|
1516
|
+
compactionEstimate = {
|
|
1517
|
+
inputTokenEstimate,
|
|
1518
|
+
inputTokens,
|
|
1519
|
+
thresholdTokens: threshold,
|
|
1520
|
+
}
|
|
1521
|
+
}
|
|
1668
1522
|
} else {
|
|
1669
1523
|
shouldCompact = false
|
|
1670
1524
|
}
|
|
1671
1525
|
if (checkAbort(ctx.signal)) return
|
|
1526
|
+
if (shouldCompact) {
|
|
1527
|
+
const latest = (
|
|
1528
|
+
await ctx.session.state(coordinator, { through: 'latest' })
|
|
1529
|
+
).state
|
|
1530
|
+
shouldCompact = Object.keys(latest.unsettledTools ?? {}).length === 0
|
|
1531
|
+
}
|
|
1532
|
+
if (checkAbort(ctx.signal)) return
|
|
1672
1533
|
if (shouldCompact) {
|
|
1673
1534
|
const throughMessageId =
|
|
1674
1535
|
state.messages.findLast(
|
|
@@ -1685,8 +1546,16 @@ export function createHandlers<
|
|
|
1685
1546
|
events: [
|
|
1686
1547
|
{
|
|
1687
1548
|
type: 'ai.compaction.requested',
|
|
1688
|
-
id:
|
|
1689
|
-
|
|
1549
|
+
id: await aiEventId(
|
|
1550
|
+
'a2.ai.compaction.requested',
|
|
1551
|
+
generationId,
|
|
1552
|
+
),
|
|
1553
|
+
payload: {
|
|
1554
|
+
generationId,
|
|
1555
|
+
throughMessageId,
|
|
1556
|
+
throughIndex,
|
|
1557
|
+
...compactionEstimate,
|
|
1558
|
+
},
|
|
1690
1559
|
} as AppendInput<D>,
|
|
1691
1560
|
],
|
|
1692
1561
|
})),
|
|
@@ -1722,7 +1591,10 @@ export function createHandlers<
|
|
|
1722
1591
|
events: [
|
|
1723
1592
|
{
|
|
1724
1593
|
type: 'ai.compaction.completed',
|
|
1725
|
-
id:
|
|
1594
|
+
id: await aiEventId(
|
|
1595
|
+
'a2.ai.compaction.completed',
|
|
1596
|
+
generationId,
|
|
1597
|
+
),
|
|
1726
1598
|
payload: completed,
|
|
1727
1599
|
} as AppendInput<D>,
|
|
1728
1600
|
],
|
|
@@ -1739,17 +1611,14 @@ export function createHandlers<
|
|
|
1739
1611
|
}
|
|
1740
1612
|
|
|
1741
1613
|
const currentState = foldAIEvents({
|
|
1742
|
-
|
|
1614
|
+
reducer: contextReducer[aiStateReducer],
|
|
1743
1615
|
events: generationEvents,
|
|
1744
|
-
state
|
|
1616
|
+
state,
|
|
1745
1617
|
})
|
|
1746
1618
|
if (request.reason === 'retry' || previous !== undefined) {
|
|
1747
|
-
generationMessages =
|
|
1748
|
-
|
|
1749
|
-
session: ctx.session,
|
|
1750
|
-
snapshot,
|
|
1619
|
+
generationMessages = contextMessages({
|
|
1620
|
+
state: currentState,
|
|
1751
1621
|
coordinator: promptCoordinatorState,
|
|
1752
|
-
appended: generationEvents,
|
|
1753
1622
|
})
|
|
1754
1623
|
}
|
|
1755
1624
|
if (
|
|
@@ -1785,7 +1654,6 @@ export function createHandlers<
|
|
|
1785
1654
|
responseMessageId,
|
|
1786
1655
|
messages: generationMessages,
|
|
1787
1656
|
modelMessages,
|
|
1788
|
-
state: currentState,
|
|
1789
1657
|
session: resolverContext.session,
|
|
1790
1658
|
signal: ctx.signal,
|
|
1791
1659
|
model: resolvedModel,
|
|
@@ -1827,7 +1695,7 @@ export function createHandlers<
|
|
|
1827
1695
|
sequence,
|
|
1828
1696
|
chunks: update.chunks,
|
|
1829
1697
|
}
|
|
1830
|
-
const lifecycle = lifecycleEvents<D, T>({
|
|
1698
|
+
const lifecycle = await lifecycleEvents<D, T>({
|
|
1831
1699
|
requestId,
|
|
1832
1700
|
messageId: responseMessageId,
|
|
1833
1701
|
generationId,
|
|
@@ -1845,7 +1713,11 @@ export function createHandlers<
|
|
|
1845
1713
|
events: [
|
|
1846
1714
|
{
|
|
1847
1715
|
type: 'ai.generation.progress',
|
|
1848
|
-
id:
|
|
1716
|
+
id: await aiEventId(
|
|
1717
|
+
'a2.ai.progress',
|
|
1718
|
+
generationId,
|
|
1719
|
+
String(sequence),
|
|
1720
|
+
),
|
|
1849
1721
|
payload: progress,
|
|
1850
1722
|
} as AppendInput<D>,
|
|
1851
1723
|
...lifecycle.events,
|
|
@@ -1868,7 +1740,7 @@ export function createHandlers<
|
|
|
1868
1740
|
}
|
|
1869
1741
|
const completionEvent = {
|
|
1870
1742
|
type: 'ai.generation.completed',
|
|
1871
|
-
id:
|
|
1743
|
+
id: await aiEventId('a2.ai.completed', generationId),
|
|
1872
1744
|
payload: completed,
|
|
1873
1745
|
} as AppendInput<D>
|
|
1874
1746
|
const unresolved = options.generate
|
|
@@ -1881,14 +1753,16 @@ export function createHandlers<
|
|
|
1881
1753
|
`generation ended with unresolved tool authorization for '${unresolved[0]!.call.toolName}'`,
|
|
1882
1754
|
)
|
|
1883
1755
|
}
|
|
1884
|
-
const pendingEvents =
|
|
1885
|
-
|
|
1886
|
-
(
|
|
1887
|
-
pending
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1756
|
+
const pendingEvents = await Promise.all(
|
|
1757
|
+
pendingToolCalls
|
|
1758
|
+
.filter(
|
|
1759
|
+
(pending) =>
|
|
1760
|
+
pending.classification === 'automatic' ||
|
|
1761
|
+
(options.generate === undefined &&
|
|
1762
|
+
pending.classification === 'unknown'),
|
|
1763
|
+
)
|
|
1764
|
+
.map((pending) => toolCalledEvent<D>(pending.call)),
|
|
1765
|
+
)
|
|
1892
1766
|
if (finish.finishReason === 'tool-calls') {
|
|
1893
1767
|
return [...pendingEvents, completionEvent]
|
|
1894
1768
|
}
|
|
@@ -1897,7 +1771,7 @@ export function createHandlers<
|
|
|
1897
1771
|
completionEvent,
|
|
1898
1772
|
{
|
|
1899
1773
|
type: 'ai.message.completed',
|
|
1900
|
-
id:
|
|
1774
|
+
id: await aiEventId('a2.ai.message.completed', generationId),
|
|
1901
1775
|
payload: {
|
|
1902
1776
|
messageId: responseMessageId,
|
|
1903
1777
|
} satisfies MessageCompletedPayload,
|
|
@@ -1920,7 +1794,7 @@ export function createHandlers<
|
|
|
1920
1794
|
}
|
|
1921
1795
|
return {
|
|
1922
1796
|
type: 'ai.generation.failed',
|
|
1923
|
-
id:
|
|
1797
|
+
id: await aiEventId('a2.ai.failed', generationId),
|
|
1924
1798
|
payload: failed,
|
|
1925
1799
|
} as AppendInput<D>
|
|
1926
1800
|
}
|
|
@@ -1943,12 +1817,36 @@ export function createHandlers<
|
|
|
1943
1817
|
ctx.signal.throwIfAborted()
|
|
1944
1818
|
return {
|
|
1945
1819
|
type: 'ai.model.metadata.resolved',
|
|
1946
|
-
id:
|
|
1820
|
+
id: await aiEventId('a2.ai.model.metadata.resolved', ctx.event.id),
|
|
1947
1821
|
payload: { modelId: ctx.event.payload.modelId, limits },
|
|
1948
1822
|
} as AppendInput<D>
|
|
1949
1823
|
},
|
|
1950
1824
|
},
|
|
1951
|
-
'ai.control.requested': {
|
|
1825
|
+
'ai.control.requested': {
|
|
1826
|
+
lane: ({ event }) => {
|
|
1827
|
+
const command = event.payload as ControlCommand<M>
|
|
1828
|
+
if (event.id === '')
|
|
1829
|
+
throw new A2Error(
|
|
1830
|
+
'INVALID_PAYLOAD',
|
|
1831
|
+
'AI command IDs must be nonempty',
|
|
1832
|
+
)
|
|
1833
|
+
if (event.id?.startsWith(AI_EVENT_ID_PREFIX))
|
|
1834
|
+
throw new A2Error(
|
|
1835
|
+
'INVALID_PAYLOAD',
|
|
1836
|
+
`AI command IDs cannot use the reserved '${AI_EVENT_ID_PREFIX}' prefix`,
|
|
1837
|
+
)
|
|
1838
|
+
if (
|
|
1839
|
+
(command.action === 'send' || command.action === 'steer') &&
|
|
1840
|
+
event.id !== command.message.id
|
|
1841
|
+
)
|
|
1842
|
+
throw new A2Error(
|
|
1843
|
+
'INVALID_PAYLOAD',
|
|
1844
|
+
'AI input event ID must equal message.id',
|
|
1845
|
+
)
|
|
1846
|
+
return 'a2.ai.control'
|
|
1847
|
+
},
|
|
1848
|
+
handler: control.handler,
|
|
1849
|
+
},
|
|
1952
1850
|
'ai.work.reported': { lane: 'a2.ai.control', handler: control.handler },
|
|
1953
1851
|
'ai.message.completed': {
|
|
1954
1852
|
handler: async (ctx) => {
|