experimental-a2 0.15.0 → 0.16.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 +81 -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-Cai-lCbj.d.ts → ai-CrEf6p_W.d.ts} +73 -14
- package/dist/ai-CrEf6p_W.d.ts.map +1 -0
- package/dist/ai-DgOBltJ_.js +2451 -0
- package/dist/ai-DgOBltJ_.js.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 +788 -296
- 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 +149 -48
- package/docs/reference/01-api.mdx +110 -28
- 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 +36 -4
- package/src/ai-context-schema.ts +296 -0
- package/src/ai-context.ts +680 -0
- package/src/ai-control-server.ts +183 -135
- package/src/ai-control-state.ts +6 -0
- package/src/ai-control.ts +8 -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 +474 -0
- package/src/ai-server.ts +247 -336
- package/src/ai-stored-state.ts +484 -0
- package/src/ai.ts +255 -538
- 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
|
{
|
|
@@ -1364,6 +1219,7 @@ export function createHandlers<
|
|
|
1364
1219
|
)
|
|
1365
1220
|
if (
|
|
1366
1221
|
state.active?.turnId !== request.turnId ||
|
|
1222
|
+
Object.values(state.pendingStops).includes(request.turnId) ||
|
|
1367
1223
|
state.active.suspended ||
|
|
1368
1224
|
state.active.version !== request.version ||
|
|
1369
1225
|
state.coordinator.response?.failure !== undefined ||
|
|
@@ -1393,17 +1249,23 @@ export function createHandlers<
|
|
|
1393
1249
|
if (checkAbort(ctx.signal)) return
|
|
1394
1250
|
const requestId = ctx.event.id
|
|
1395
1251
|
const request = ctx.event.payload
|
|
1396
|
-
const snapshot = await ctx.session
|
|
1397
|
-
through: 'latest',
|
|
1398
|
-
})
|
|
1252
|
+
const snapshot = await readContext(ctx.session, 'latest')
|
|
1399
1253
|
if (
|
|
1400
|
-
snapshot.state.
|
|
1401
|
-
snapshot.state.
|
|
1254
|
+
snapshot.state.closed ||
|
|
1255
|
+
snapshot.state.authority?.requestId !== requestId ||
|
|
1256
|
+
snapshot.state.authority?.phase === 'paused' ||
|
|
1257
|
+
snapshot.state.authority?.phase === 'pausing'
|
|
1402
1258
|
)
|
|
1403
1259
|
return
|
|
1404
|
-
const
|
|
1405
|
-
await ctx.session.state(
|
|
1260
|
+
const controlState = (
|
|
1261
|
+
await ctx.session.state(control.reducer, { through: snapshot.index })
|
|
1406
1262
|
).state
|
|
1263
|
+
if (
|
|
1264
|
+
request.control !== undefined &&
|
|
1265
|
+
Object.values(controlState.pendingStops).includes(request.control.turnId)
|
|
1266
|
+
)
|
|
1267
|
+
return
|
|
1268
|
+
const coordinatorState = controlState.coordinator
|
|
1407
1269
|
if (
|
|
1408
1270
|
coordinatorState.closed ||
|
|
1409
1271
|
coordinatorState.response?.activeRequestId !== requestId
|
|
@@ -1423,54 +1285,29 @@ export function createHandlers<
|
|
|
1423
1285
|
return
|
|
1424
1286
|
if (current !== undefined && current.attempt >= ctx.attempt) return
|
|
1425
1287
|
const attempt = ctx.attempt
|
|
1426
|
-
const generationId =
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
(
|
|
1430
|
-
|
|
1431
|
-
|
|
1288
|
+
const generationId = await aiEventId(
|
|
1289
|
+
'a2.ai.generation',
|
|
1290
|
+
requestId,
|
|
1291
|
+
String(attempt),
|
|
1292
|
+
)
|
|
1293
|
+
const responseMessageId = request.responseMessageId
|
|
1432
1294
|
const responseStepCount = response.stepCount
|
|
1433
1295
|
if (
|
|
1434
1296
|
request.reason === 'tool' &&
|
|
1435
|
-
(
|
|
1436
|
-
|
|
1297
|
+
(request.sourceGenerationId !==
|
|
1298
|
+
response.source?.generation.generationId ||
|
|
1437
1299
|
response.responseMessageId !== request.messageId ||
|
|
1438
1300
|
response.responseMessageId !== request.responseMessageId)
|
|
1439
1301
|
)
|
|
1440
1302
|
return
|
|
1441
1303
|
const previous = current
|
|
1442
|
-
const
|
|
1304
|
+
const state =
|
|
1443
1305
|
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
|
-
}
|
|
1306
|
+
? snapshot.state
|
|
1307
|
+
: contextForAttempt(snapshot.state)
|
|
1471
1308
|
const resolverContext: AgentResolverContext<M, D> = {
|
|
1472
1309
|
event: ctx.event,
|
|
1473
|
-
state,
|
|
1310
|
+
messages: contextMessages({ state, coordinator: coordinatorState }),
|
|
1474
1311
|
session: {
|
|
1475
1312
|
state: (reducer, readOptions) =>
|
|
1476
1313
|
ctx.session.state(reducer, {
|
|
@@ -1509,7 +1346,7 @@ export function createHandlers<
|
|
|
1509
1346
|
if (source === undefined) return
|
|
1510
1347
|
return {
|
|
1511
1348
|
type: 'ai.generation.failed',
|
|
1512
|
-
id:
|
|
1349
|
+
id: await aiEventId('a2.ai.step-limit', requestId),
|
|
1513
1350
|
payload: {
|
|
1514
1351
|
requestId: source.requestId,
|
|
1515
1352
|
messageId: source.messageId,
|
|
@@ -1548,7 +1385,12 @@ export function createHandlers<
|
|
|
1548
1385
|
) {
|
|
1549
1386
|
startEvents.push({
|
|
1550
1387
|
type: 'ai.model.metadata.requested',
|
|
1551
|
-
id:
|
|
1388
|
+
id: await aiEventId(
|
|
1389
|
+
'a2.ai.model.metadata',
|
|
1390
|
+
options.agent.contract.name,
|
|
1391
|
+
ctx.event.sessionId,
|
|
1392
|
+
catalogModelId,
|
|
1393
|
+
),
|
|
1552
1394
|
payload: { modelId: catalogModelId },
|
|
1553
1395
|
} as AppendInput<D>)
|
|
1554
1396
|
}
|
|
@@ -1564,7 +1406,7 @@ export function createHandlers<
|
|
|
1564
1406
|
}
|
|
1565
1407
|
startEvents.push({
|
|
1566
1408
|
type: 'ai.generation.failed',
|
|
1567
|
-
id:
|
|
1409
|
+
id: await aiEventId('a2.ai.superseded', payload.generationId),
|
|
1568
1410
|
payload: superseded,
|
|
1569
1411
|
} as AppendInput<D>)
|
|
1570
1412
|
}
|
|
@@ -1583,12 +1425,9 @@ export function createHandlers<
|
|
|
1583
1425
|
if (checkAbort(ctx.signal)) return
|
|
1584
1426
|
|
|
1585
1427
|
const promptCoordinatorState = coordinatorState
|
|
1586
|
-
const messages =
|
|
1587
|
-
|
|
1588
|
-
session: ctx.session,
|
|
1589
|
-
snapshot: { state, index: snapshot.index },
|
|
1428
|
+
const messages = contextMessages({
|
|
1429
|
+
state,
|
|
1590
1430
|
coordinator: promptCoordinatorState,
|
|
1591
|
-
excluded: replacedGenerationIds,
|
|
1592
1431
|
})
|
|
1593
1432
|
let generationMessages = messages
|
|
1594
1433
|
let modelMessages = await modelContext({ messages, state, tools })
|
|
@@ -1599,7 +1438,6 @@ export function createHandlers<
|
|
|
1599
1438
|
responseMessageId,
|
|
1600
1439
|
messages,
|
|
1601
1440
|
modelMessages,
|
|
1602
|
-
state,
|
|
1603
1441
|
session: resolverContext.session,
|
|
1604
1442
|
signal: ctx.signal,
|
|
1605
1443
|
model: resolvedModel,
|
|
@@ -1624,8 +1462,24 @@ export function createHandlers<
|
|
|
1624
1462
|
: undefined
|
|
1625
1463
|
let inputTokenEstimate: number | undefined
|
|
1626
1464
|
let compacted = false
|
|
1627
|
-
|
|
1628
|
-
|
|
1465
|
+
let compactionEstimate:
|
|
1466
|
+
| Pick<
|
|
1467
|
+
CompactionRequestedPayload,
|
|
1468
|
+
'inputTokenEstimate' | 'inputTokens' | 'thresholdTokens'
|
|
1469
|
+
>
|
|
1470
|
+
| undefined
|
|
1471
|
+
const hasPendingTools = messages.some((message) =>
|
|
1472
|
+
message.parts.some(
|
|
1473
|
+
(part) =>
|
|
1474
|
+
isToolUIPart(part) &&
|
|
1475
|
+
!(
|
|
1476
|
+
part.state === 'output-error' ||
|
|
1477
|
+
part.state === 'output-denied' ||
|
|
1478
|
+
(part.state === 'output-available' && part.preliminary !== true)
|
|
1479
|
+
),
|
|
1480
|
+
),
|
|
1481
|
+
)
|
|
1482
|
+
if (policy && !hasPendingTools) {
|
|
1629
1483
|
const compactionContext = {
|
|
1630
1484
|
...resolverContext,
|
|
1631
1485
|
messages,
|
|
@@ -1665,10 +1519,24 @@ export function createHandlers<
|
|
|
1665
1519
|
)
|
|
1666
1520
|
}
|
|
1667
1521
|
shouldCompact = threshold !== undefined && inputTokens >= threshold
|
|
1522
|
+
if (threshold !== undefined && shouldCompact) {
|
|
1523
|
+
compactionEstimate = {
|
|
1524
|
+
inputTokenEstimate,
|
|
1525
|
+
inputTokens,
|
|
1526
|
+
thresholdTokens: threshold,
|
|
1527
|
+
}
|
|
1528
|
+
}
|
|
1668
1529
|
} else {
|
|
1669
1530
|
shouldCompact = false
|
|
1670
1531
|
}
|
|
1671
1532
|
if (checkAbort(ctx.signal)) return
|
|
1533
|
+
if (shouldCompact) {
|
|
1534
|
+
const latest = (
|
|
1535
|
+
await ctx.session.state(coordinator, { through: 'latest' })
|
|
1536
|
+
).state
|
|
1537
|
+
shouldCompact = Object.keys(latest.unsettledTools ?? {}).length === 0
|
|
1538
|
+
}
|
|
1539
|
+
if (checkAbort(ctx.signal)) return
|
|
1672
1540
|
if (shouldCompact) {
|
|
1673
1541
|
const throughMessageId =
|
|
1674
1542
|
state.messages.findLast(
|
|
@@ -1685,8 +1553,16 @@ export function createHandlers<
|
|
|
1685
1553
|
events: [
|
|
1686
1554
|
{
|
|
1687
1555
|
type: 'ai.compaction.requested',
|
|
1688
|
-
id:
|
|
1689
|
-
|
|
1556
|
+
id: await aiEventId(
|
|
1557
|
+
'a2.ai.compaction.requested',
|
|
1558
|
+
generationId,
|
|
1559
|
+
),
|
|
1560
|
+
payload: {
|
|
1561
|
+
generationId,
|
|
1562
|
+
throughMessageId,
|
|
1563
|
+
throughIndex,
|
|
1564
|
+
...compactionEstimate,
|
|
1565
|
+
},
|
|
1690
1566
|
} as AppendInput<D>,
|
|
1691
1567
|
],
|
|
1692
1568
|
})),
|
|
@@ -1722,7 +1598,10 @@ export function createHandlers<
|
|
|
1722
1598
|
events: [
|
|
1723
1599
|
{
|
|
1724
1600
|
type: 'ai.compaction.completed',
|
|
1725
|
-
id:
|
|
1601
|
+
id: await aiEventId(
|
|
1602
|
+
'a2.ai.compaction.completed',
|
|
1603
|
+
generationId,
|
|
1604
|
+
),
|
|
1726
1605
|
payload: completed,
|
|
1727
1606
|
} as AppendInput<D>,
|
|
1728
1607
|
],
|
|
@@ -1739,17 +1618,14 @@ export function createHandlers<
|
|
|
1739
1618
|
}
|
|
1740
1619
|
|
|
1741
1620
|
const currentState = foldAIEvents({
|
|
1742
|
-
|
|
1621
|
+
reducer: contextReducer[aiStateReducer],
|
|
1743
1622
|
events: generationEvents,
|
|
1744
|
-
state
|
|
1623
|
+
state,
|
|
1745
1624
|
})
|
|
1746
1625
|
if (request.reason === 'retry' || previous !== undefined) {
|
|
1747
|
-
generationMessages =
|
|
1748
|
-
|
|
1749
|
-
session: ctx.session,
|
|
1750
|
-
snapshot,
|
|
1626
|
+
generationMessages = contextMessages({
|
|
1627
|
+
state: currentState,
|
|
1751
1628
|
coordinator: promptCoordinatorState,
|
|
1752
|
-
appended: generationEvents,
|
|
1753
1629
|
})
|
|
1754
1630
|
}
|
|
1755
1631
|
if (
|
|
@@ -1785,7 +1661,6 @@ export function createHandlers<
|
|
|
1785
1661
|
responseMessageId,
|
|
1786
1662
|
messages: generationMessages,
|
|
1787
1663
|
modelMessages,
|
|
1788
|
-
state: currentState,
|
|
1789
1664
|
session: resolverContext.session,
|
|
1790
1665
|
signal: ctx.signal,
|
|
1791
1666
|
model: resolvedModel,
|
|
@@ -1827,7 +1702,7 @@ export function createHandlers<
|
|
|
1827
1702
|
sequence,
|
|
1828
1703
|
chunks: update.chunks,
|
|
1829
1704
|
}
|
|
1830
|
-
const lifecycle = lifecycleEvents<D, T>({
|
|
1705
|
+
const lifecycle = await lifecycleEvents<D, T>({
|
|
1831
1706
|
requestId,
|
|
1832
1707
|
messageId: responseMessageId,
|
|
1833
1708
|
generationId,
|
|
@@ -1845,7 +1720,11 @@ export function createHandlers<
|
|
|
1845
1720
|
events: [
|
|
1846
1721
|
{
|
|
1847
1722
|
type: 'ai.generation.progress',
|
|
1848
|
-
id:
|
|
1723
|
+
id: await aiEventId(
|
|
1724
|
+
'a2.ai.progress',
|
|
1725
|
+
generationId,
|
|
1726
|
+
String(sequence),
|
|
1727
|
+
),
|
|
1849
1728
|
payload: progress,
|
|
1850
1729
|
} as AppendInput<D>,
|
|
1851
1730
|
...lifecycle.events,
|
|
@@ -1868,7 +1747,7 @@ export function createHandlers<
|
|
|
1868
1747
|
}
|
|
1869
1748
|
const completionEvent = {
|
|
1870
1749
|
type: 'ai.generation.completed',
|
|
1871
|
-
id:
|
|
1750
|
+
id: await aiEventId('a2.ai.completed', generationId),
|
|
1872
1751
|
payload: completed,
|
|
1873
1752
|
} as AppendInput<D>
|
|
1874
1753
|
const unresolved = options.generate
|
|
@@ -1881,14 +1760,16 @@ export function createHandlers<
|
|
|
1881
1760
|
`generation ended with unresolved tool authorization for '${unresolved[0]!.call.toolName}'`,
|
|
1882
1761
|
)
|
|
1883
1762
|
}
|
|
1884
|
-
const pendingEvents =
|
|
1885
|
-
|
|
1886
|
-
(
|
|
1887
|
-
pending
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1763
|
+
const pendingEvents = await Promise.all(
|
|
1764
|
+
pendingToolCalls
|
|
1765
|
+
.filter(
|
|
1766
|
+
(pending) =>
|
|
1767
|
+
pending.classification === 'automatic' ||
|
|
1768
|
+
(options.generate === undefined &&
|
|
1769
|
+
pending.classification === 'unknown'),
|
|
1770
|
+
)
|
|
1771
|
+
.map((pending) => toolCalledEvent<D>(pending.call)),
|
|
1772
|
+
)
|
|
1892
1773
|
if (finish.finishReason === 'tool-calls') {
|
|
1893
1774
|
return [...pendingEvents, completionEvent]
|
|
1894
1775
|
}
|
|
@@ -1897,7 +1778,7 @@ export function createHandlers<
|
|
|
1897
1778
|
completionEvent,
|
|
1898
1779
|
{
|
|
1899
1780
|
type: 'ai.message.completed',
|
|
1900
|
-
id:
|
|
1781
|
+
id: await aiEventId('a2.ai.message.completed', generationId),
|
|
1901
1782
|
payload: {
|
|
1902
1783
|
messageId: responseMessageId,
|
|
1903
1784
|
} satisfies MessageCompletedPayload,
|
|
@@ -1920,7 +1801,7 @@ export function createHandlers<
|
|
|
1920
1801
|
}
|
|
1921
1802
|
return {
|
|
1922
1803
|
type: 'ai.generation.failed',
|
|
1923
|
-
id:
|
|
1804
|
+
id: await aiEventId('a2.ai.failed', generationId),
|
|
1924
1805
|
payload: failed,
|
|
1925
1806
|
} as AppendInput<D>
|
|
1926
1807
|
}
|
|
@@ -1943,12 +1824,36 @@ export function createHandlers<
|
|
|
1943
1824
|
ctx.signal.throwIfAborted()
|
|
1944
1825
|
return {
|
|
1945
1826
|
type: 'ai.model.metadata.resolved',
|
|
1946
|
-
id:
|
|
1827
|
+
id: await aiEventId('a2.ai.model.metadata.resolved', ctx.event.id),
|
|
1947
1828
|
payload: { modelId: ctx.event.payload.modelId, limits },
|
|
1948
1829
|
} as AppendInput<D>
|
|
1949
1830
|
},
|
|
1950
1831
|
},
|
|
1951
|
-
'ai.control.requested': {
|
|
1832
|
+
'ai.control.requested': {
|
|
1833
|
+
lane: ({ event }) => {
|
|
1834
|
+
const command = event.payload as ControlCommand<M>
|
|
1835
|
+
if (event.id === '')
|
|
1836
|
+
throw new A2Error(
|
|
1837
|
+
'INVALID_PAYLOAD',
|
|
1838
|
+
'AI command IDs must be nonempty',
|
|
1839
|
+
)
|
|
1840
|
+
if (event.id?.startsWith(AI_EVENT_ID_PREFIX))
|
|
1841
|
+
throw new A2Error(
|
|
1842
|
+
'INVALID_PAYLOAD',
|
|
1843
|
+
`AI command IDs cannot use the reserved '${AI_EVENT_ID_PREFIX}' prefix`,
|
|
1844
|
+
)
|
|
1845
|
+
if (
|
|
1846
|
+
(command.action === 'send' || command.action === 'steer') &&
|
|
1847
|
+
event.id !== command.message.id
|
|
1848
|
+
)
|
|
1849
|
+
throw new A2Error(
|
|
1850
|
+
'INVALID_PAYLOAD',
|
|
1851
|
+
'AI input event ID must equal message.id',
|
|
1852
|
+
)
|
|
1853
|
+
return 'a2.ai.control'
|
|
1854
|
+
},
|
|
1855
|
+
handler: control.handler,
|
|
1856
|
+
},
|
|
1952
1857
|
'ai.work.reported': { lane: 'a2.ai.control', handler: control.handler },
|
|
1953
1858
|
'ai.message.completed': {
|
|
1954
1859
|
handler: async (ctx) => {
|
|
@@ -1978,6 +1883,9 @@ export function createHandlers<
|
|
|
1978
1883
|
'ai.generation.requested': {
|
|
1979
1884
|
lane: 'a2.ai.model',
|
|
1980
1885
|
abortOn: {
|
|
1886
|
+
'ai.control.requested': (event, trigger) =>
|
|
1887
|
+
event.payload.action === 'stop' &&
|
|
1888
|
+
event.payload.turnId === trigger.payload.control?.turnId,
|
|
1981
1889
|
'ai.control.committed': (event, trigger) => {
|
|
1982
1890
|
const active = (event.payload as ControlCommit).view
|
|
1983
1891
|
return (
|
|
@@ -1999,6 +1907,9 @@ export function createHandlers<
|
|
|
1999
1907
|
},
|
|
2000
1908
|
'ai.tool.execution.requested': {
|
|
2001
1909
|
abortOn: {
|
|
1910
|
+
'ai.control.requested': (event, trigger) =>
|
|
1911
|
+
event.payload.action === 'stop' &&
|
|
1912
|
+
event.payload.turnId === trigger.payload.turnId,
|
|
2002
1913
|
'ai.generation.failed': (event, trigger) =>
|
|
2003
1914
|
event.payload.generationId === trigger.payload.call.generationId,
|
|
2004
1915
|
'ai.control.committed': (event, trigger) => {
|