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.ts
CHANGED
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
export type { PendingQueueCommand } from './ai-client-state.ts'
|
|
22
22
|
import { applyControlChanges } from './ai-control-state.ts'
|
|
23
23
|
import { initialControlState } from './ai-control.ts'
|
|
24
|
+
import { AI_EVENT_ID_PREFIX } from './ai-id.ts'
|
|
24
25
|
import type {
|
|
25
26
|
ControlCommit,
|
|
26
27
|
ActiveTurn,
|
|
@@ -42,13 +43,23 @@ import {
|
|
|
42
43
|
interruptUIMessage,
|
|
43
44
|
pauseUIMessage,
|
|
44
45
|
projectUIMessage,
|
|
45
|
-
projectUIMessageStream,
|
|
46
46
|
type UIMessageCursor,
|
|
47
47
|
} from './ai-projector.ts'
|
|
48
48
|
import { ambientToolScopeStorage } from './internal.ts'
|
|
49
|
+
import {
|
|
50
|
+
upsertMessage,
|
|
51
|
+
upsertResponse,
|
|
52
|
+
answerApproval,
|
|
53
|
+
approvalRequestedChunk,
|
|
54
|
+
toolResultChunk,
|
|
55
|
+
advanceProjection,
|
|
56
|
+
projectedMessage,
|
|
57
|
+
reduceToolActivity,
|
|
58
|
+
} from './ai-message-projection.ts'
|
|
49
59
|
import { clientStateProjection, type Reducer } from './reducer.ts'
|
|
60
|
+
import { storedAIReducer, type AIStoredReducer } from './ai-stored-state.ts'
|
|
61
|
+
export type { AIStoredState, AIStoredReducer } from './ai-stored-state.ts'
|
|
50
62
|
import {
|
|
51
|
-
appendProgressBatch,
|
|
52
63
|
flattenProgressBatches,
|
|
53
64
|
progressBatches,
|
|
54
65
|
type ProgressBatches,
|
|
@@ -85,7 +96,8 @@ export type GenerationRequestedPayload = {
|
|
|
85
96
|
control?: { turnId: string; version: number }
|
|
86
97
|
messageId: string
|
|
87
98
|
reason: GenerationReason
|
|
88
|
-
responseMessageId
|
|
99
|
+
responseMessageId: string
|
|
100
|
+
sourceGenerationId?: string
|
|
89
101
|
}
|
|
90
102
|
export type RetryGenerationOptions = {
|
|
91
103
|
messageId: string
|
|
@@ -162,6 +174,7 @@ export type ToolResultPayload = {
|
|
|
162
174
|
toolMetadata?: unknown
|
|
163
175
|
}
|
|
164
176
|
export type ApprovalRequestedPayload = {
|
|
177
|
+
requestId: string
|
|
165
178
|
messageId: string
|
|
166
179
|
generationId: string
|
|
167
180
|
approvalId: string
|
|
@@ -170,6 +183,7 @@ export type ApprovalRequestedPayload = {
|
|
|
170
183
|
signature?: string
|
|
171
184
|
}
|
|
172
185
|
export type ApprovalRespondedPayload = {
|
|
186
|
+
requestId: string
|
|
173
187
|
messageId: string
|
|
174
188
|
generationId: string
|
|
175
189
|
approvalId: string
|
|
@@ -177,6 +191,7 @@ export type ApprovalRespondedPayload = {
|
|
|
177
191
|
reason?: string
|
|
178
192
|
}
|
|
179
193
|
export type InputRequestedPayload = {
|
|
194
|
+
requestId: string
|
|
180
195
|
messageId: string
|
|
181
196
|
generationId: string
|
|
182
197
|
inputId: string
|
|
@@ -184,6 +199,7 @@ export type InputRequestedPayload = {
|
|
|
184
199
|
question?: JSONValue
|
|
185
200
|
}
|
|
186
201
|
export type InputRespondedPayload = {
|
|
202
|
+
requestId: string
|
|
187
203
|
messageId: string
|
|
188
204
|
generationId: string
|
|
189
205
|
inputId: string
|
|
@@ -208,6 +224,9 @@ export type CompactionRequestedPayload = {
|
|
|
208
224
|
generationId: string
|
|
209
225
|
throughMessageId: string
|
|
210
226
|
throughIndex?: number
|
|
227
|
+
inputTokenEstimate?: number
|
|
228
|
+
inputTokens?: number
|
|
229
|
+
thresholdTokens?: number
|
|
211
230
|
}
|
|
212
231
|
export type CompactionCompletedPayload<M extends UIMessage> = {
|
|
213
232
|
generationId: string
|
|
@@ -335,9 +354,14 @@ export type AIState<M extends UIMessage = UIMessage> = {
|
|
|
335
354
|
status: AgentStatus
|
|
336
355
|
activeGeneration: ActiveGeneration | null
|
|
337
356
|
activeRequestId: string | null
|
|
357
|
+
activeRequestReason: GenerationReason | null
|
|
358
|
+
activeRequestSourceGenerationId: string | null
|
|
338
359
|
activeResponseMessageId: string | null
|
|
339
360
|
activeProjection: ActiveGenerationProjection<M> | null
|
|
340
|
-
|
|
361
|
+
responseOwners: Record<
|
|
362
|
+
string,
|
|
363
|
+
Pick<GenerationStartedPayload, 'generationId' | 'requestId' | 'attempt'>
|
|
364
|
+
>
|
|
341
365
|
terminalRequestIds?: Record<string, true>
|
|
342
366
|
terminalGenerations?: Record<
|
|
343
367
|
string,
|
|
@@ -357,7 +381,7 @@ export type AgentDefinition<
|
|
|
357
381
|
D extends EventDefs = AIEventDefs<M>,
|
|
358
382
|
> = {
|
|
359
383
|
readonly contract: Contract<D>
|
|
360
|
-
readonly reducer:
|
|
384
|
+
readonly reducer: AIStoredReducer<D, AIState<M>>
|
|
361
385
|
}
|
|
362
386
|
|
|
363
387
|
export type AgentToolEventDefs = EventDefs &
|
|
@@ -625,9 +649,14 @@ export function createEvents<M extends UIMessage = UIMessage>(options?: {
|
|
|
625
649
|
const parsed = validateMessage(value['message'], messageSchema)
|
|
626
650
|
if (parsed.issues) return parsed
|
|
627
651
|
if (
|
|
628
|
-
(action === '
|
|
629
|
-
|
|
630
|
-
|
|
652
|
+
(action === 'send' || action === 'steer') &&
|
|
653
|
+
(parsed.value.id.length === 0 ||
|
|
654
|
+
parsed.value.id.startsWith(AI_EVENT_ID_PREFIX))
|
|
655
|
+
)
|
|
656
|
+
return issue(
|
|
657
|
+
'AI input IDs must be nonempty and outside the reserved internal prefix',
|
|
658
|
+
)
|
|
659
|
+
if (
|
|
631
660
|
(action === 'steer' &&
|
|
632
661
|
(!stringField(value, 'turnId') ||
|
|
633
662
|
!optionalIndexField(value, 'lastSeenIndex'))) ||
|
|
@@ -762,7 +791,10 @@ export function createEvents<M extends UIMessage = UIMessage>(options?: {
|
|
|
762
791
|
'ai.generation.requested',
|
|
763
792
|
(value) =>
|
|
764
793
|
stringField(value, 'messageId') &&
|
|
765
|
-
|
|
794
|
+
stringField(value, 'responseMessageId') &&
|
|
795
|
+
optionalStringField(value, 'sourceGenerationId') &&
|
|
796
|
+
(value['reason'] !== 'tool' ||
|
|
797
|
+
stringField(value, 'sourceGenerationId')) &&
|
|
766
798
|
(value['reason'] === 'message' ||
|
|
767
799
|
value['reason'] === 'tool' ||
|
|
768
800
|
value['reason'] === 'input' ||
|
|
@@ -844,6 +876,7 @@ export function createEvents<M extends UIMessage = UIMessage>(options?: {
|
|
|
844
876
|
'ai.approval.requested': jsonObject(
|
|
845
877
|
'ai.approval.requested',
|
|
846
878
|
(value) =>
|
|
879
|
+
stringField(value, 'requestId') &&
|
|
847
880
|
stringField(value, 'messageId') &&
|
|
848
881
|
stringField(value, 'generationId') &&
|
|
849
882
|
stringField(value, 'approvalId') &&
|
|
@@ -855,6 +888,7 @@ export function createEvents<M extends UIMessage = UIMessage>(options?: {
|
|
|
855
888
|
'ai.approval.responded': jsonObject(
|
|
856
889
|
'ai.approval.responded',
|
|
857
890
|
(value) =>
|
|
891
|
+
stringField(value, 'requestId') &&
|
|
858
892
|
stringField(value, 'messageId') &&
|
|
859
893
|
stringField(value, 'generationId') &&
|
|
860
894
|
stringField(value, 'approvalId') &&
|
|
@@ -864,6 +898,7 @@ export function createEvents<M extends UIMessage = UIMessage>(options?: {
|
|
|
864
898
|
'ai.input.requested': jsonObject(
|
|
865
899
|
'ai.input.requested',
|
|
866
900
|
(value) =>
|
|
901
|
+
stringField(value, 'requestId') &&
|
|
867
902
|
stringField(value, 'messageId') &&
|
|
868
903
|
stringField(value, 'generationId') &&
|
|
869
904
|
stringField(value, 'inputId') &&
|
|
@@ -872,6 +907,7 @@ export function createEvents<M extends UIMessage = UIMessage>(options?: {
|
|
|
872
907
|
'ai.input.responded': jsonObject(
|
|
873
908
|
'ai.input.responded',
|
|
874
909
|
(value) =>
|
|
910
|
+
stringField(value, 'requestId') &&
|
|
875
911
|
stringField(value, 'messageId') &&
|
|
876
912
|
stringField(value, 'generationId') &&
|
|
877
913
|
stringField(value, 'inputId') &&
|
|
@@ -882,7 +918,10 @@ export function createEvents<M extends UIMessage = UIMessage>(options?: {
|
|
|
882
918
|
(value) =>
|
|
883
919
|
stringField(value, 'generationId') &&
|
|
884
920
|
stringField(value, 'throughMessageId') &&
|
|
885
|
-
optionalIndexField(value, 'throughIndex')
|
|
921
|
+
optionalIndexField(value, 'throughIndex') &&
|
|
922
|
+
optionalIndexField(value, 'inputTokenEstimate') &&
|
|
923
|
+
optionalIndexField(value, 'inputTokens') &&
|
|
924
|
+
optionalIndexField(value, 'thresholdTokens'),
|
|
886
925
|
),
|
|
887
926
|
'ai.compaction.completed': compactionPayloadSchema(messageSchema),
|
|
888
927
|
'ai.model.metadata.requested': jsonObject(
|
|
@@ -919,9 +958,11 @@ const initialState = <M extends UIMessage>(): AIState<M> => ({
|
|
|
919
958
|
status: 'idle',
|
|
920
959
|
activeGeneration: null,
|
|
921
960
|
activeRequestId: null,
|
|
961
|
+
activeRequestReason: null,
|
|
962
|
+
activeRequestSourceGenerationId: null,
|
|
922
963
|
activeResponseMessageId: null,
|
|
923
964
|
activeProjection: null,
|
|
924
|
-
|
|
965
|
+
responseOwners: {},
|
|
925
966
|
pendingApprovals: [],
|
|
926
967
|
pendingInputs: [],
|
|
927
968
|
tools: [],
|
|
@@ -931,62 +972,6 @@ const initialState = <M extends UIMessage>(): AIState<M> => ({
|
|
|
931
972
|
error: null,
|
|
932
973
|
})
|
|
933
974
|
|
|
934
|
-
const upsertMessage = <M extends UIMessage>(messages: M[], message: M): M[] => {
|
|
935
|
-
const index = messages.findIndex((candidate) => candidate.id === message.id)
|
|
936
|
-
if (index === -1) return [...messages, message]
|
|
937
|
-
const next = [...messages]
|
|
938
|
-
next[index] = message
|
|
939
|
-
return next
|
|
940
|
-
}
|
|
941
|
-
|
|
942
|
-
const upsertResponse = <M extends UIMessage>(options: {
|
|
943
|
-
messages: M[]
|
|
944
|
-
message: M
|
|
945
|
-
inputId: string
|
|
946
|
-
}): M[] => {
|
|
947
|
-
const { messages, message, inputId } = options
|
|
948
|
-
if (messages.some((item) => item.id === message.id))
|
|
949
|
-
return upsertMessage(messages, message)
|
|
950
|
-
const inputIndex = messages.findIndex((item) => item.id === inputId)
|
|
951
|
-
if (inputIndex === -1) return [...messages, message]
|
|
952
|
-
return [
|
|
953
|
-
...messages.slice(0, inputIndex + 1),
|
|
954
|
-
message,
|
|
955
|
-
...messages.slice(inputIndex + 1),
|
|
956
|
-
]
|
|
957
|
-
}
|
|
958
|
-
|
|
959
|
-
const answerApproval = <M extends UIMessage>(
|
|
960
|
-
messages: M[],
|
|
961
|
-
response: ApprovalRespondedPayload,
|
|
962
|
-
): M[] =>
|
|
963
|
-
messages.map((message) => {
|
|
964
|
-
if (message.id !== response.messageId) return message
|
|
965
|
-
let changed = false
|
|
966
|
-
const parts = message.parts.map((part) => {
|
|
967
|
-
const candidate = part as unknown
|
|
968
|
-
if (
|
|
969
|
-
!isRecord(candidate) ||
|
|
970
|
-
candidate['state'] !== 'approval-requested' ||
|
|
971
|
-
!isRecord(candidate['approval']) ||
|
|
972
|
-
candidate['approval']['id'] !== response.approvalId
|
|
973
|
-
) {
|
|
974
|
-
return part
|
|
975
|
-
}
|
|
976
|
-
changed = true
|
|
977
|
-
return {
|
|
978
|
-
...part,
|
|
979
|
-
state: 'approval-responded',
|
|
980
|
-
approval: {
|
|
981
|
-
...candidate['approval'],
|
|
982
|
-
approved: response.approved,
|
|
983
|
-
...(response.reason === undefined ? {} : { reason: response.reason }),
|
|
984
|
-
},
|
|
985
|
-
} as typeof part
|
|
986
|
-
})
|
|
987
|
-
return changed ? ({ ...message, parts } as M) : message
|
|
988
|
-
})
|
|
989
|
-
|
|
990
975
|
const projectMessageChunks = <M extends UIMessage>(
|
|
991
976
|
messages: M[],
|
|
992
977
|
messageId: string,
|
|
@@ -998,90 +983,6 @@ const projectMessageChunks = <M extends UIMessage>(
|
|
|
998
983
|
: message,
|
|
999
984
|
)
|
|
1000
985
|
|
|
1001
|
-
const approvalRequestedChunk = (
|
|
1002
|
-
payload: ApprovalRequestedPayload,
|
|
1003
|
-
): UIMessageChunk => ({
|
|
1004
|
-
type: 'tool-approval-request',
|
|
1005
|
-
approvalId: payload.approvalId,
|
|
1006
|
-
toolCallId: payload.toolCallId,
|
|
1007
|
-
...(payload.isAutomatic === undefined
|
|
1008
|
-
? {}
|
|
1009
|
-
: { isAutomatic: payload.isAutomatic }),
|
|
1010
|
-
...(payload.signature === undefined ? {} : { signature: payload.signature }),
|
|
1011
|
-
})
|
|
1012
|
-
|
|
1013
|
-
const approvalRespondedChunk = (
|
|
1014
|
-
payload: ApprovalRespondedPayload,
|
|
1015
|
-
): UIMessageChunk => ({
|
|
1016
|
-
type: 'tool-approval-response',
|
|
1017
|
-
approvalId: payload.approvalId,
|
|
1018
|
-
approved: payload.approved,
|
|
1019
|
-
...(payload.reason === undefined ? {} : { reason: payload.reason }),
|
|
1020
|
-
})
|
|
1021
|
-
|
|
1022
|
-
const toolResultChunk = (payload: ToolResultPayload): UIMessageChunk => {
|
|
1023
|
-
if (payload.phase === 'input') {
|
|
1024
|
-
return {
|
|
1025
|
-
type: 'tool-input-error',
|
|
1026
|
-
toolCallId: payload.toolCallId,
|
|
1027
|
-
toolName: payload.toolName ?? 'unknown',
|
|
1028
|
-
input: payload.rawInput ?? payload.input,
|
|
1029
|
-
errorText: payload.error ?? 'Invalid tool input',
|
|
1030
|
-
...(payload.dynamic === undefined ? {} : { dynamic: payload.dynamic }),
|
|
1031
|
-
...(payload.providerExecuted === undefined
|
|
1032
|
-
? {}
|
|
1033
|
-
: { providerExecuted: payload.providerExecuted }),
|
|
1034
|
-
...(payload.providerMetadata === undefined
|
|
1035
|
-
? {}
|
|
1036
|
-
: { providerMetadata: payload.providerMetadata }),
|
|
1037
|
-
...(payload.toolMetadata === undefined
|
|
1038
|
-
? {}
|
|
1039
|
-
: { toolMetadata: payload.toolMetadata }),
|
|
1040
|
-
} as UIMessageChunk
|
|
1041
|
-
}
|
|
1042
|
-
if (payload.denied === true) {
|
|
1043
|
-
return {
|
|
1044
|
-
type: 'tool-output-denied',
|
|
1045
|
-
toolCallId: payload.toolCallId,
|
|
1046
|
-
} as UIMessageChunk
|
|
1047
|
-
}
|
|
1048
|
-
if (payload.error !== undefined) {
|
|
1049
|
-
return {
|
|
1050
|
-
type: 'tool-output-error',
|
|
1051
|
-
toolCallId: payload.toolCallId,
|
|
1052
|
-
errorText: payload.error,
|
|
1053
|
-
...(payload.dynamic === undefined ? {} : { dynamic: payload.dynamic }),
|
|
1054
|
-
...(payload.providerExecuted === undefined
|
|
1055
|
-
? {}
|
|
1056
|
-
: { providerExecuted: payload.providerExecuted }),
|
|
1057
|
-
...(payload.providerMetadata === undefined
|
|
1058
|
-
? {}
|
|
1059
|
-
: { providerMetadata: payload.providerMetadata }),
|
|
1060
|
-
...(payload.toolMetadata === undefined
|
|
1061
|
-
? {}
|
|
1062
|
-
: { toolMetadata: payload.toolMetadata }),
|
|
1063
|
-
} as UIMessageChunk
|
|
1064
|
-
}
|
|
1065
|
-
return {
|
|
1066
|
-
type: 'tool-output-available',
|
|
1067
|
-
toolCallId: payload.toolCallId,
|
|
1068
|
-
output: payload.output,
|
|
1069
|
-
...(payload.preliminary === undefined
|
|
1070
|
-
? {}
|
|
1071
|
-
: { preliminary: payload.preliminary }),
|
|
1072
|
-
...(payload.dynamic === undefined ? {} : { dynamic: payload.dynamic }),
|
|
1073
|
-
...(payload.providerExecuted === undefined
|
|
1074
|
-
? {}
|
|
1075
|
-
: { providerExecuted: payload.providerExecuted }),
|
|
1076
|
-
...(payload.providerMetadata === undefined
|
|
1077
|
-
? {}
|
|
1078
|
-
: { providerMetadata: payload.providerMetadata }),
|
|
1079
|
-
...(payload.toolMetadata === undefined
|
|
1080
|
-
? {}
|
|
1081
|
-
: { toolMetadata: payload.toolMetadata }),
|
|
1082
|
-
} as UIMessageChunk
|
|
1083
|
-
}
|
|
1084
|
-
|
|
1085
986
|
const waitingStatus = <M extends UIMessage>(state: AIState<M>): AgentStatus => {
|
|
1086
987
|
if (state.pendingApprovals.length > 0 || state.pendingInputs.length > 0) {
|
|
1087
988
|
return 'waiting'
|
|
@@ -1101,49 +1002,16 @@ const lifecycleStatus = <M extends UIMessage>(
|
|
|
1101
1002
|
const isStaleGeneration = <M extends UIMessage>(
|
|
1102
1003
|
state: AIState<M>,
|
|
1103
1004
|
messageId: string,
|
|
1104
|
-
generationId: string,
|
|
1005
|
+
owner: { generationId: string; requestId: string },
|
|
1105
1006
|
): boolean => {
|
|
1106
|
-
const current = state.
|
|
1107
|
-
return
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
): { message: M; cursor: UIMessageCursor } => {
|
|
1113
|
-
const projectionEvents = flattenProgressBatches(projection.batches)
|
|
1114
|
-
for (const event of projection.toolEvents) {
|
|
1115
|
-
if (event.type === 'ai.tool.result') {
|
|
1116
|
-
projectionEvents.push({
|
|
1117
|
-
index: event.index,
|
|
1118
|
-
chunks: [toolResultChunk(event.payload)],
|
|
1119
|
-
})
|
|
1120
|
-
}
|
|
1121
|
-
}
|
|
1122
|
-
for (const event of projection.approvalEvents) {
|
|
1123
|
-
projectionEvents.push({
|
|
1124
|
-
index: event.index,
|
|
1125
|
-
chunks: [
|
|
1126
|
-
event.type === 'ai.approval.requested'
|
|
1127
|
-
? approvalRequestedChunk(event.payload)
|
|
1128
|
-
: approvalRespondedChunk(event.payload),
|
|
1129
|
-
],
|
|
1130
|
-
})
|
|
1131
|
-
}
|
|
1132
|
-
return projectUIMessageStream({
|
|
1133
|
-
messageId: projection.responseMessageId,
|
|
1134
|
-
...(projection.baseMessage === undefined
|
|
1135
|
-
? {}
|
|
1136
|
-
: { base: projection.baseMessage }),
|
|
1137
|
-
chunks: projectionEvents
|
|
1138
|
-
.toSorted((left, right) => left.index - right.index)
|
|
1139
|
-
.flatMap((event) => event.chunks),
|
|
1140
|
-
})
|
|
1007
|
+
const current = state.responseOwners[messageId]
|
|
1008
|
+
return (
|
|
1009
|
+
current !== undefined &&
|
|
1010
|
+
(current.generationId !== owner.generationId ||
|
|
1011
|
+
current.requestId !== owner.requestId)
|
|
1012
|
+
)
|
|
1141
1013
|
}
|
|
1142
1014
|
|
|
1143
|
-
const projectedMessage = <M extends UIMessage>(
|
|
1144
|
-
projection: ActiveGenerationProjection<M>,
|
|
1145
|
-
): M => projectedMessageStream(projection).message
|
|
1146
|
-
|
|
1147
1015
|
const projectedResponse = <M extends UIMessage>(options: {
|
|
1148
1016
|
state: AIState<M>
|
|
1149
1017
|
projection: ActiveGenerationProjection<M>
|
|
@@ -1159,86 +1027,6 @@ const projectedResponse = <M extends UIMessage>(options: {
|
|
|
1159
1027
|
: message
|
|
1160
1028
|
}
|
|
1161
1029
|
|
|
1162
|
-
const reduceToolActivity = (
|
|
1163
|
-
tools: ToolActivity[],
|
|
1164
|
-
event:
|
|
1165
|
-
| { type: 'ai.tool.called'; payload: ToolCalledPayload }
|
|
1166
|
-
| { type: 'ai.tool.result'; payload: ToolResultPayload },
|
|
1167
|
-
): ToolActivity[] => {
|
|
1168
|
-
if (event.type === 'ai.tool.called') {
|
|
1169
|
-
const payload = event.payload
|
|
1170
|
-
const { providerMetadata, ...called } = payload
|
|
1171
|
-
const activity: ToolActivity = {
|
|
1172
|
-
...called,
|
|
1173
|
-
...(providerMetadata === undefined
|
|
1174
|
-
? {}
|
|
1175
|
-
: { callProviderMetadata: providerMetadata }),
|
|
1176
|
-
status: 'running',
|
|
1177
|
-
}
|
|
1178
|
-
return [
|
|
1179
|
-
...tools.filter(
|
|
1180
|
-
(candidate) =>
|
|
1181
|
-
candidate.generationId !== payload.generationId ||
|
|
1182
|
-
candidate.toolCallId !== payload.toolCallId,
|
|
1183
|
-
),
|
|
1184
|
-
activity,
|
|
1185
|
-
]
|
|
1186
|
-
}
|
|
1187
|
-
|
|
1188
|
-
const payload = event.payload
|
|
1189
|
-
const existing = tools.find(
|
|
1190
|
-
(candidate) =>
|
|
1191
|
-
candidate.generationId === payload.generationId &&
|
|
1192
|
-
candidate.toolCallId === payload.toolCallId,
|
|
1193
|
-
)
|
|
1194
|
-
const toolName = payload.toolName ?? existing?.toolName
|
|
1195
|
-
const input = payload.input === undefined ? existing?.input : payload.input
|
|
1196
|
-
const dynamic = payload.dynamic ?? existing?.dynamic
|
|
1197
|
-
const providerExecuted =
|
|
1198
|
-
payload.providerExecuted ?? existing?.providerExecuted
|
|
1199
|
-
const toolMetadata = payload.toolMetadata ?? existing?.toolMetadata
|
|
1200
|
-
const resultProviderMetadata =
|
|
1201
|
-
payload.providerMetadata ?? existing?.resultProviderMetadata
|
|
1202
|
-
const activity: ToolActivity = {
|
|
1203
|
-
requestId: payload.requestId,
|
|
1204
|
-
messageId: payload.messageId,
|
|
1205
|
-
generationId: payload.generationId,
|
|
1206
|
-
toolCallId: payload.toolCallId,
|
|
1207
|
-
...(toolName === undefined ? {} : { toolName }),
|
|
1208
|
-
...(input === undefined ? {} : { input }),
|
|
1209
|
-
...(payload.rawInput === undefined ? {} : { rawInput: payload.rawInput }),
|
|
1210
|
-
...(payload.output === undefined ? {} : { output: payload.output }),
|
|
1211
|
-
...(payload.error === undefined ? {} : { error: payload.error }),
|
|
1212
|
-
...(payload.preliminary === undefined
|
|
1213
|
-
? {}
|
|
1214
|
-
: { preliminary: payload.preliminary }),
|
|
1215
|
-
...(payload.phase === undefined ? {} : { phase: payload.phase }),
|
|
1216
|
-
...(dynamic === undefined ? {} : { dynamic }),
|
|
1217
|
-
...(providerExecuted === undefined ? {} : { providerExecuted }),
|
|
1218
|
-
...(existing?.callProviderMetadata === undefined
|
|
1219
|
-
? {}
|
|
1220
|
-
: { callProviderMetadata: existing.callProviderMetadata }),
|
|
1221
|
-
...(resultProviderMetadata === undefined ? {} : { resultProviderMetadata }),
|
|
1222
|
-
...(toolMetadata === undefined ? {} : { toolMetadata }),
|
|
1223
|
-
...(existing?.title === undefined ? {} : { title: existing.title }),
|
|
1224
|
-
status: payload.denied
|
|
1225
|
-
? 'denied'
|
|
1226
|
-
: payload.error
|
|
1227
|
-
? 'failed'
|
|
1228
|
-
: payload.preliminary
|
|
1229
|
-
? 'running'
|
|
1230
|
-
: 'completed',
|
|
1231
|
-
}
|
|
1232
|
-
return [
|
|
1233
|
-
...tools.filter(
|
|
1234
|
-
(candidate) =>
|
|
1235
|
-
candidate.generationId !== payload.generationId ||
|
|
1236
|
-
candidate.toolCallId !== payload.toolCallId,
|
|
1237
|
-
),
|
|
1238
|
-
activity,
|
|
1239
|
-
]
|
|
1240
|
-
}
|
|
1241
|
-
|
|
1242
1030
|
const projectedTools = <M extends UIMessage>(
|
|
1243
1031
|
projection: ActiveGenerationProjection<M>,
|
|
1244
1032
|
): ToolActivity[] =>
|
|
@@ -1251,7 +1039,7 @@ const restoreGenerationBase = <M extends UIMessage>(
|
|
|
1251
1039
|
state: AIState<M>,
|
|
1252
1040
|
generationId: string,
|
|
1253
1041
|
responseMessageId: string,
|
|
1254
|
-
reason: 'interrupted' | 'superseded',
|
|
1042
|
+
reason: 'interrupted' | 'superseded' | 'failed',
|
|
1255
1043
|
): AIState<M> => {
|
|
1256
1044
|
const projection =
|
|
1257
1045
|
state.activeProjection?.generationId === generationId
|
|
@@ -1360,19 +1148,13 @@ const interruptGenerationProjection = <M extends UIMessage>(
|
|
|
1360
1148
|
}
|
|
1361
1149
|
}
|
|
1362
1150
|
|
|
1363
|
-
const generationRequestId = (generationId: string): string | undefined => {
|
|
1364
|
-
const marker = ':generation:'
|
|
1365
|
-
const markerIndex = generationId.lastIndexOf(marker)
|
|
1366
|
-
return markerIndex === -1 ? undefined : generationId.slice(0, markerIndex)
|
|
1367
|
-
}
|
|
1368
|
-
|
|
1369
1151
|
const terminalGenerationReason = <M extends UIMessage>(
|
|
1370
1152
|
state: AIState<M>,
|
|
1371
1153
|
generationId: string,
|
|
1154
|
+
requestId?: string,
|
|
1372
1155
|
): 'completed' | 'failed' | 'interrupted' | 'superseded' | undefined => {
|
|
1373
1156
|
const terminal = state.terminalGenerations?.[generationId]
|
|
1374
1157
|
if (terminal !== undefined) return terminal
|
|
1375
|
-
const requestId = generationRequestId(generationId)
|
|
1376
1158
|
return requestId !== undefined &&
|
|
1377
1159
|
state.terminalRequestIds?.[requestId] === true
|
|
1378
1160
|
? 'interrupted'
|
|
@@ -1429,6 +1211,8 @@ export function reduceAIState<M extends UIMessage>(
|
|
|
1429
1211
|
...visible,
|
|
1430
1212
|
status,
|
|
1431
1213
|
activeRequestId: commit.view.requestId,
|
|
1214
|
+
activeRequestReason: commit.view.requestReason,
|
|
1215
|
+
activeRequestSourceGenerationId: commit.view.sourceGenerationId,
|
|
1432
1216
|
activeResponseMessageId: commit.view.responseMessageId,
|
|
1433
1217
|
}
|
|
1434
1218
|
}
|
|
@@ -1548,6 +1332,8 @@ export function reduceAIState<M extends UIMessage>(
|
|
|
1548
1332
|
: state.compaction,
|
|
1549
1333
|
activeGeneration: null,
|
|
1550
1334
|
activeRequestId: null,
|
|
1335
|
+
activeRequestReason: null,
|
|
1336
|
+
activeRequestSourceGenerationId: null,
|
|
1551
1337
|
activeResponseMessageId: null,
|
|
1552
1338
|
activeProjection: null,
|
|
1553
1339
|
}
|
|
@@ -1564,17 +1350,20 @@ export function reduceAIState<M extends UIMessage>(
|
|
|
1564
1350
|
}
|
|
1565
1351
|
case 'ai.message.completed': {
|
|
1566
1352
|
const { messageId } = event.payload as MessageCompletedPayload
|
|
1567
|
-
const
|
|
1353
|
+
const owner = state.responseOwners[messageId]
|
|
1354
|
+
const generationId = owner?.generationId
|
|
1355
|
+
const requestId = owner?.requestId
|
|
1568
1356
|
if (
|
|
1569
1357
|
generationId === undefined ||
|
|
1570
|
-
terminalGenerationReason(state, generationId) !== undefined
|
|
1358
|
+
terminalGenerationReason(state, generationId, requestId) !== undefined
|
|
1571
1359
|
) {
|
|
1572
1360
|
return state
|
|
1573
1361
|
}
|
|
1574
|
-
const requestId = generationRequestId(generationId)
|
|
1575
1362
|
return {
|
|
1576
1363
|
...state,
|
|
1577
1364
|
activeRequestId: null,
|
|
1365
|
+
activeRequestReason: null,
|
|
1366
|
+
activeRequestSourceGenerationId: null,
|
|
1578
1367
|
activeResponseMessageId: null,
|
|
1579
1368
|
activeProjection:
|
|
1580
1369
|
state.activeProjection?.generationId === generationId
|
|
@@ -1597,18 +1386,10 @@ export function reduceAIState<M extends UIMessage>(
|
|
|
1597
1386
|
case 'ai.generation.requested': {
|
|
1598
1387
|
const payload = event.payload as GenerationRequestedPayload
|
|
1599
1388
|
const schedulesToolContinuation = payload.reason === 'tool'
|
|
1600
|
-
const responseMessageId =
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
: payload.messageId)
|
|
1605
|
-
const ownerGenerationId =
|
|
1606
|
-
state.responseGenerationIds[responseMessageId] ??
|
|
1607
|
-
state.activeGeneration?.generationId
|
|
1608
|
-
const ownerRequestId =
|
|
1609
|
-
ownerGenerationId === undefined
|
|
1610
|
-
? undefined
|
|
1611
|
-
: generationRequestId(ownerGenerationId)
|
|
1389
|
+
const responseMessageId = payload.responseMessageId
|
|
1390
|
+
const owner = state.responseOwners[responseMessageId]
|
|
1391
|
+
const ownerGenerationId = owner?.generationId
|
|
1392
|
+
const ownerRequestId = owner?.requestId
|
|
1612
1393
|
const requestId = (event as { id?: string }).id
|
|
1613
1394
|
if (
|
|
1614
1395
|
requestId !== undefined &&
|
|
@@ -1621,30 +1402,35 @@ export function reduceAIState<M extends UIMessage>(
|
|
|
1621
1402
|
payload.reason === 'message' ||
|
|
1622
1403
|
(payload.reason === 'tool'
|
|
1623
1404
|
? ownerGenerationId !== undefined &&
|
|
1624
|
-
|
|
1405
|
+
payload.sourceGenerationId === ownerGenerationId &&
|
|
1625
1406
|
state.activeRequestId === ownerRequestId &&
|
|
1626
1407
|
state.status !== 'failed'
|
|
1627
1408
|
: payload.reason === 'retry'
|
|
1628
1409
|
? state.status === 'failed'
|
|
1629
1410
|
: ownerGenerationId !== undefined &&
|
|
1630
|
-
|
|
1631
|
-
`ai.generate:input:${encodeURIComponent(responseMessageId)}:${encodeURIComponent(ownerGenerationId)}:`,
|
|
1632
|
-
) === true &&
|
|
1411
|
+
payload.sourceGenerationId === ownerGenerationId &&
|
|
1633
1412
|
state.activeRequestId === ownerRequestId &&
|
|
1634
1413
|
state.status !== 'failed')
|
|
1635
1414
|
if (!acceptsRequest) return state
|
|
1415
|
+
const base =
|
|
1416
|
+
payload.reason === 'retry' &&
|
|
1417
|
+
state.activeProjection &&
|
|
1418
|
+
state.activeProjection.completionIndex === undefined
|
|
1419
|
+
? restoreGenerationBase(
|
|
1420
|
+
state,
|
|
1421
|
+
state.activeProjection.generationId,
|
|
1422
|
+
responseMessageId,
|
|
1423
|
+
'failed',
|
|
1424
|
+
)
|
|
1425
|
+
: state
|
|
1636
1426
|
return {
|
|
1637
|
-
...
|
|
1638
|
-
messages:
|
|
1639
|
-
payload.reason === 'retry' && payload.responseMessageId !== undefined
|
|
1640
|
-
? state.messages.filter(
|
|
1641
|
-
(message) => message.id !== payload.responseMessageId,
|
|
1642
|
-
)
|
|
1643
|
-
: state.messages,
|
|
1427
|
+
...base,
|
|
1644
1428
|
activeProjection:
|
|
1645
1429
|
payload.reason === 'retry' ? null : state.activeProjection,
|
|
1646
1430
|
status: schedulesToolContinuation ? state.status : 'generating',
|
|
1647
1431
|
activeRequestId: requestId ?? null,
|
|
1432
|
+
activeRequestReason: payload.reason,
|
|
1433
|
+
activeRequestSourceGenerationId: payload.sourceGenerationId ?? null,
|
|
1648
1434
|
activeResponseMessageId: responseMessageId,
|
|
1649
1435
|
error: schedulesToolContinuation ? state.error : null,
|
|
1650
1436
|
}
|
|
@@ -1653,7 +1439,11 @@ export function reduceAIState<M extends UIMessage>(
|
|
|
1653
1439
|
const payload = event.payload as GenerationStartedPayload
|
|
1654
1440
|
if (
|
|
1655
1441
|
state.terminalRequestIds?.[payload.requestId] === true ||
|
|
1656
|
-
terminalGenerationReason(
|
|
1442
|
+
terminalGenerationReason(
|
|
1443
|
+
state,
|
|
1444
|
+
payload.generationId,
|
|
1445
|
+
payload.requestId,
|
|
1446
|
+
) !== undefined
|
|
1657
1447
|
) {
|
|
1658
1448
|
return state
|
|
1659
1449
|
}
|
|
@@ -1663,31 +1453,26 @@ export function reduceAIState<M extends UIMessage>(
|
|
|
1663
1453
|
) {
|
|
1664
1454
|
return state
|
|
1665
1455
|
}
|
|
1666
|
-
const currentOwner =
|
|
1667
|
-
state.responseGenerationIds[payload.responseMessageId]
|
|
1456
|
+
const currentOwner = state.responseOwners[payload.responseMessageId]
|
|
1668
1457
|
const currentTerminal =
|
|
1669
1458
|
currentOwner === undefined
|
|
1670
1459
|
? undefined
|
|
1671
|
-
: terminalGenerationReason(
|
|
1460
|
+
: terminalGenerationReason(
|
|
1461
|
+
state,
|
|
1462
|
+
currentOwner.generationId,
|
|
1463
|
+
currentOwner.requestId,
|
|
1464
|
+
)
|
|
1672
1465
|
if (
|
|
1673
1466
|
currentTerminal === 'completed' ||
|
|
1674
1467
|
currentTerminal === 'interrupted'
|
|
1675
1468
|
) {
|
|
1676
1469
|
return state
|
|
1677
1470
|
}
|
|
1678
|
-
const marker = ':generation:'
|
|
1679
|
-
const currentAttempt = currentOwner?.startsWith(
|
|
1680
|
-
`${payload.requestId}${marker}`,
|
|
1681
|
-
)
|
|
1682
|
-
? Number(currentOwner.slice(`${payload.requestId}${marker}`.length))
|
|
1683
|
-
: undefined
|
|
1684
1471
|
if (
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
) {
|
|
1472
|
+
currentOwner?.requestId === payload.requestId &&
|
|
1473
|
+
currentOwner.attempt >= payload.attempt
|
|
1474
|
+
)
|
|
1689
1475
|
return state
|
|
1690
|
-
}
|
|
1691
1476
|
const activeGeneration = state.activeGeneration
|
|
1692
1477
|
const replacesActiveAttempt =
|
|
1693
1478
|
activeGeneration !== null &&
|
|
@@ -1711,9 +1496,13 @@ export function reduceAIState<M extends UIMessage>(
|
|
|
1711
1496
|
status: 'generating',
|
|
1712
1497
|
activeGeneration: payload,
|
|
1713
1498
|
activeResponseMessageId: null,
|
|
1714
|
-
|
|
1715
|
-
...baseState.
|
|
1716
|
-
[payload.responseMessageId]:
|
|
1499
|
+
responseOwners: {
|
|
1500
|
+
...baseState.responseOwners,
|
|
1501
|
+
[payload.responseMessageId]: {
|
|
1502
|
+
generationId: payload.generationId,
|
|
1503
|
+
requestId: payload.requestId,
|
|
1504
|
+
attempt: payload.attempt,
|
|
1505
|
+
},
|
|
1717
1506
|
},
|
|
1718
1507
|
activeProjection: {
|
|
1719
1508
|
generationId: payload.generationId,
|
|
@@ -1732,83 +1521,61 @@ export function reduceAIState<M extends UIMessage>(
|
|
|
1732
1521
|
case 'ai.generation.progress': {
|
|
1733
1522
|
const payload = event.payload as GenerationProgressPayload
|
|
1734
1523
|
if (state.pausedGenerationIds?.[payload.generationId]) return state
|
|
1735
|
-
if (
|
|
1524
|
+
if (
|
|
1525
|
+
terminalGenerationReason(
|
|
1526
|
+
state,
|
|
1527
|
+
payload.generationId,
|
|
1528
|
+
payload.requestId,
|
|
1529
|
+
) !== undefined
|
|
1530
|
+
) {
|
|
1736
1531
|
return state
|
|
1737
1532
|
}
|
|
1738
1533
|
const activeProjection = state.activeProjection
|
|
1739
1534
|
if (
|
|
1740
1535
|
state.activeGeneration?.generationId !== payload.generationId ||
|
|
1536
|
+
state.activeGeneration.requestId !== payload.requestId ||
|
|
1741
1537
|
activeProjection?.generationId !== payload.generationId
|
|
1742
1538
|
) {
|
|
1743
1539
|
return state
|
|
1744
1540
|
}
|
|
1745
|
-
const
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
})
|
|
1754
|
-
: progressBatches(
|
|
1755
|
-
[
|
|
1756
|
-
...flattenProgressBatches(activeProjection.batches).filter(
|
|
1757
|
-
(batch) => batch.index !== event.index,
|
|
1758
|
-
),
|
|
1759
|
-
{ index: event.index, chunks: payload.chunks },
|
|
1760
|
-
].toSorted((left, right) => left.index - right.index),
|
|
1761
|
-
),
|
|
1762
|
-
}
|
|
1763
|
-
const base = state.messages.find(
|
|
1764
|
-
(message) => message.id === projection.responseMessageId,
|
|
1765
|
-
)
|
|
1766
|
-
const incremental =
|
|
1767
|
-
base !== undefined &&
|
|
1768
|
-
activeProjection.cursor != null &&
|
|
1769
|
-
event.index > activeProjection.cursor.throughIndex
|
|
1770
|
-
const projected = incremental
|
|
1771
|
-
? projectUIMessageStream({
|
|
1772
|
-
messageId: projection.responseMessageId,
|
|
1773
|
-
base,
|
|
1774
|
-
cursor: activeProjection.cursor!.stream,
|
|
1775
|
-
chunks: payload.chunks,
|
|
1776
|
-
})
|
|
1777
|
-
: projectedMessageStream(projection)
|
|
1778
|
-
projection.cursor = {
|
|
1779
|
-
throughIndex: incremental
|
|
1780
|
-
? event.index
|
|
1781
|
-
: Math.max(
|
|
1782
|
-
event.index,
|
|
1783
|
-
projection.batches.tail.at(-1)?.index ?? 0,
|
|
1784
|
-
projection.toolEvents.reduce(
|
|
1785
|
-
(index, entry) => Math.max(index, entry.index),
|
|
1786
|
-
0,
|
|
1787
|
-
),
|
|
1788
|
-
projection.approvalEvents.reduce(
|
|
1789
|
-
(index, entry) => Math.max(index, entry.index),
|
|
1790
|
-
0,
|
|
1791
|
-
),
|
|
1792
|
-
),
|
|
1793
|
-
stream: projected.cursor,
|
|
1794
|
-
}
|
|
1541
|
+
const progress = advanceProjection({
|
|
1542
|
+
projection: activeProjection,
|
|
1543
|
+
base: state.messages.find(
|
|
1544
|
+
(message) => message.id === activeProjection.responseMessageId,
|
|
1545
|
+
),
|
|
1546
|
+
chunks: payload.chunks,
|
|
1547
|
+
index: event.index,
|
|
1548
|
+
})
|
|
1795
1549
|
return {
|
|
1796
1550
|
...state,
|
|
1797
1551
|
messages: upsertResponse({
|
|
1798
1552
|
messages: state.messages,
|
|
1799
|
-
message:
|
|
1553
|
+
message: progress.message,
|
|
1800
1554
|
inputId: state.activeGeneration.messageId,
|
|
1801
1555
|
}),
|
|
1802
|
-
activeProjection:
|
|
1556
|
+
activeProjection: {
|
|
1557
|
+
...activeProjection,
|
|
1558
|
+
batches: progress.batches,
|
|
1559
|
+
cursor: progress.cursor,
|
|
1560
|
+
},
|
|
1803
1561
|
status: 'generating',
|
|
1804
1562
|
}
|
|
1805
1563
|
}
|
|
1806
1564
|
case 'ai.generation.completed': {
|
|
1807
1565
|
const payload = event.payload as GenerationCompletedPayload
|
|
1808
|
-
if (
|
|
1566
|
+
if (
|
|
1567
|
+
terminalGenerationReason(
|
|
1568
|
+
state,
|
|
1569
|
+
payload.generationId,
|
|
1570
|
+
payload.requestId,
|
|
1571
|
+
) !== undefined
|
|
1572
|
+
) {
|
|
1809
1573
|
return state
|
|
1810
1574
|
}
|
|
1811
|
-
if (
|
|
1575
|
+
if (
|
|
1576
|
+
state.activeGeneration?.generationId !== payload.generationId ||
|
|
1577
|
+
state.activeGeneration.requestId !== payload.requestId
|
|
1578
|
+
) {
|
|
1812
1579
|
return state
|
|
1813
1580
|
}
|
|
1814
1581
|
const usage = payload.usage
|
|
@@ -1832,17 +1599,26 @@ export function reduceAIState<M extends UIMessage>(
|
|
|
1832
1599
|
}
|
|
1833
1600
|
case 'ai.generation.failed': {
|
|
1834
1601
|
const payload = event.payload as GenerationFailedPayload
|
|
1835
|
-
if (
|
|
1602
|
+
if (
|
|
1603
|
+
terminalGenerationReason(
|
|
1604
|
+
state,
|
|
1605
|
+
payload.generationId,
|
|
1606
|
+
payload.requestId,
|
|
1607
|
+
) !== undefined
|
|
1608
|
+
) {
|
|
1836
1609
|
return state
|
|
1837
1610
|
}
|
|
1838
1611
|
if (
|
|
1839
|
-
state.activeGeneration?.generationId !== payload.generationId
|
|
1612
|
+
(state.activeGeneration?.generationId !== payload.generationId ||
|
|
1613
|
+
state.activeGeneration.requestId !== payload.requestId) &&
|
|
1840
1614
|
!(
|
|
1841
1615
|
payload.stepLimit === true &&
|
|
1842
|
-
state.
|
|
1616
|
+
state.activeRequestReason === 'tool' &&
|
|
1617
|
+
state.responseOwners[payload.responseMessageId]?.requestId ===
|
|
1618
|
+
payload.requestId &&
|
|
1619
|
+
state.responseOwners[payload.responseMessageId]?.generationId ===
|
|
1843
1620
|
payload.generationId &&
|
|
1844
|
-
state.
|
|
1845
|
-
`ai.generate:tools:${payload.generationId}` &&
|
|
1621
|
+
state.activeRequestSourceGenerationId === payload.generationId &&
|
|
1846
1622
|
state.activeResponseMessageId === payload.responseMessageId
|
|
1847
1623
|
)
|
|
1848
1624
|
) {
|
|
@@ -1912,9 +1688,13 @@ export function reduceAIState<M extends UIMessage>(
|
|
|
1912
1688
|
}
|
|
1913
1689
|
case 'ai.message.interrupted': {
|
|
1914
1690
|
const payload = event.payload as MessageInterruptedPayload
|
|
1915
|
-
const
|
|
1691
|
+
const owner = state.responseOwners[payload.messageId]
|
|
1692
|
+
const currentGenerationId = owner?.generationId
|
|
1916
1693
|
const targetRequestId =
|
|
1917
|
-
payload.requestId ??
|
|
1694
|
+
payload.requestId ??
|
|
1695
|
+
(owner?.generationId === payload.generationId
|
|
1696
|
+
? owner?.requestId
|
|
1697
|
+
: undefined)
|
|
1918
1698
|
const ownsRequestPhase =
|
|
1919
1699
|
payload.requestId !== undefined &&
|
|
1920
1700
|
state.activeGeneration === null &&
|
|
@@ -1925,7 +1705,7 @@ export function reduceAIState<M extends UIMessage>(
|
|
|
1925
1705
|
!ownsRequestPhase &&
|
|
1926
1706
|
(payload.generationId !== undefined
|
|
1927
1707
|
? currentGenerationId !== payload.generationId
|
|
1928
|
-
:
|
|
1708
|
+
: owner?.requestId !== payload.requestId)
|
|
1929
1709
|
) {
|
|
1930
1710
|
return state
|
|
1931
1711
|
}
|
|
@@ -1940,7 +1720,11 @@ export function reduceAIState<M extends UIMessage>(
|
|
|
1940
1720
|
const currentTerminal =
|
|
1941
1721
|
currentGenerationId === undefined
|
|
1942
1722
|
? undefined
|
|
1943
|
-
: terminalGenerationReason(
|
|
1723
|
+
: terminalGenerationReason(
|
|
1724
|
+
state,
|
|
1725
|
+
currentGenerationId,
|
|
1726
|
+
owner?.requestId,
|
|
1727
|
+
)
|
|
1944
1728
|
if (
|
|
1945
1729
|
currentTerminal === 'completed' ||
|
|
1946
1730
|
currentTerminal === 'interrupted' ||
|
|
@@ -1970,6 +1754,8 @@ export function reduceAIState<M extends UIMessage>(
|
|
|
1970
1754
|
...baseState,
|
|
1971
1755
|
status: 'idle',
|
|
1972
1756
|
activeRequestId: null,
|
|
1757
|
+
activeRequestReason: null,
|
|
1758
|
+
activeRequestSourceGenerationId: null,
|
|
1973
1759
|
activeResponseMessageId: null,
|
|
1974
1760
|
activeProjection: null,
|
|
1975
1761
|
terminalRequestIds: {
|
|
@@ -1990,7 +1776,7 @@ export function reduceAIState<M extends UIMessage>(
|
|
|
1990
1776
|
const targetGenerationId =
|
|
1991
1777
|
payload.generationId ??
|
|
1992
1778
|
(currentGenerationId !== undefined &&
|
|
1993
|
-
|
|
1779
|
+
owner?.requestId === payload.requestId
|
|
1994
1780
|
? currentGenerationId
|
|
1995
1781
|
: undefined)
|
|
1996
1782
|
if (targetGenerationId === undefined) return state
|
|
@@ -2019,7 +1805,8 @@ export function reduceAIState<M extends UIMessage>(
|
|
|
2019
1805
|
currentGenerationId === targetGenerationId &&
|
|
2020
1806
|
(state.activeRequestId === null ||
|
|
2021
1807
|
state.activeRequestId === targetRequestId ||
|
|
2022
|
-
state.
|
|
1808
|
+
(state.activeRequestReason === 'tool' &&
|
|
1809
|
+
state.activeRequestSourceGenerationId === targetGenerationId))
|
|
2023
1810
|
const resolvesCurrentGeneration =
|
|
2024
1811
|
currentGenerationId === targetGenerationId &&
|
|
2025
1812
|
currentTerminal === undefined
|
|
@@ -2041,6 +1828,8 @@ export function reduceAIState<M extends UIMessage>(
|
|
|
2041
1828
|
const next = {
|
|
2042
1829
|
...baseState,
|
|
2043
1830
|
activeRequestId: null,
|
|
1831
|
+
activeRequestReason: null,
|
|
1832
|
+
activeRequestSourceGenerationId: null,
|
|
2044
1833
|
activeResponseMessageId: null,
|
|
2045
1834
|
activeProjection: null,
|
|
2046
1835
|
messages:
|
|
@@ -2114,6 +1903,8 @@ export function reduceAIState<M extends UIMessage>(
|
|
|
2114
1903
|
const next = {
|
|
2115
1904
|
...baseState,
|
|
2116
1905
|
activeRequestId: null,
|
|
1906
|
+
activeRequestReason: null,
|
|
1907
|
+
activeRequestSourceGenerationId: null,
|
|
2117
1908
|
activeResponseMessageId: null,
|
|
2118
1909
|
terminalRequestIds: {
|
|
2119
1910
|
...baseState.terminalRequestIds,
|
|
@@ -2135,8 +1926,12 @@ export function reduceAIState<M extends UIMessage>(
|
|
|
2135
1926
|
const payload = event.payload as ToolCalledPayload
|
|
2136
1927
|
const projection = state.activeProjection
|
|
2137
1928
|
if (
|
|
2138
|
-
isStaleGeneration(state, payload.messageId, payload
|
|
2139
|
-
terminalGenerationReason(
|
|
1929
|
+
isStaleGeneration(state, payload.messageId, payload) ||
|
|
1930
|
+
terminalGenerationReason(
|
|
1931
|
+
state,
|
|
1932
|
+
payload.generationId,
|
|
1933
|
+
payload.requestId,
|
|
1934
|
+
) !== undefined
|
|
2140
1935
|
) {
|
|
2141
1936
|
return state
|
|
2142
1937
|
}
|
|
@@ -2181,6 +1976,7 @@ export function reduceAIState<M extends UIMessage>(
|
|
|
2181
1976
|
const terminalReason = terminalGenerationReason(
|
|
2182
1977
|
state,
|
|
2183
1978
|
payload.generationId,
|
|
1979
|
+
payload.requestId,
|
|
2184
1980
|
)
|
|
2185
1981
|
const completesFailedTool =
|
|
2186
1982
|
terminalReason === 'failed' &&
|
|
@@ -2194,7 +1990,7 @@ export function reduceAIState<M extends UIMessage>(
|
|
|
2194
1990
|
activity.status === 'running',
|
|
2195
1991
|
)
|
|
2196
1992
|
if (
|
|
2197
|
-
isStaleGeneration(state, payload.messageId, payload
|
|
1993
|
+
isStaleGeneration(state, payload.messageId, payload) ||
|
|
2198
1994
|
(terminalReason !== undefined && !completesFailedTool)
|
|
2199
1995
|
) {
|
|
2200
1996
|
return state
|
|
@@ -2266,8 +2062,12 @@ export function reduceAIState<M extends UIMessage>(
|
|
|
2266
2062
|
const payload = event.payload as ApprovalRequestedPayload
|
|
2267
2063
|
const projection = state.activeProjection
|
|
2268
2064
|
if (
|
|
2269
|
-
isStaleGeneration(state, payload.messageId, payload
|
|
2270
|
-
terminalGenerationReason(
|
|
2065
|
+
isStaleGeneration(state, payload.messageId, payload) ||
|
|
2066
|
+
terminalGenerationReason(
|
|
2067
|
+
state,
|
|
2068
|
+
payload.generationId,
|
|
2069
|
+
payload.requestId,
|
|
2070
|
+
) !== undefined
|
|
2271
2071
|
) {
|
|
2272
2072
|
return state
|
|
2273
2073
|
}
|
|
@@ -2329,8 +2129,12 @@ export function reduceAIState<M extends UIMessage>(
|
|
|
2329
2129
|
case 'ai.approval.responded': {
|
|
2330
2130
|
const payload = event.payload as ApprovalRespondedPayload
|
|
2331
2131
|
if (
|
|
2332
|
-
isStaleGeneration(state, payload.messageId, payload
|
|
2333
|
-
terminalGenerationReason(
|
|
2132
|
+
isStaleGeneration(state, payload.messageId, payload) ||
|
|
2133
|
+
terminalGenerationReason(
|
|
2134
|
+
state,
|
|
2135
|
+
payload.generationId,
|
|
2136
|
+
payload.requestId,
|
|
2137
|
+
) !== undefined
|
|
2334
2138
|
) {
|
|
2335
2139
|
return state
|
|
2336
2140
|
}
|
|
@@ -2398,8 +2202,12 @@ export function reduceAIState<M extends UIMessage>(
|
|
|
2398
2202
|
case 'ai.input.requested': {
|
|
2399
2203
|
const payload = event.payload as InputRequestedPayload
|
|
2400
2204
|
if (
|
|
2401
|
-
isStaleGeneration(state, payload.messageId, payload
|
|
2402
|
-
terminalGenerationReason(
|
|
2205
|
+
isStaleGeneration(state, payload.messageId, payload) ||
|
|
2206
|
+
terminalGenerationReason(
|
|
2207
|
+
state,
|
|
2208
|
+
payload.generationId,
|
|
2209
|
+
payload.requestId,
|
|
2210
|
+
) !== undefined
|
|
2403
2211
|
) {
|
|
2404
2212
|
return state
|
|
2405
2213
|
}
|
|
@@ -2420,8 +2228,12 @@ export function reduceAIState<M extends UIMessage>(
|
|
|
2420
2228
|
case 'ai.input.responded': {
|
|
2421
2229
|
const payload = event.payload as InputRespondedPayload
|
|
2422
2230
|
if (
|
|
2423
|
-
isStaleGeneration(state, payload.messageId, payload
|
|
2424
|
-
terminalGenerationReason(
|
|
2231
|
+
isStaleGeneration(state, payload.messageId, payload) ||
|
|
2232
|
+
terminalGenerationReason(
|
|
2233
|
+
state,
|
|
2234
|
+
payload.generationId,
|
|
2235
|
+
payload.requestId,
|
|
2236
|
+
) !== undefined
|
|
2425
2237
|
) {
|
|
2426
2238
|
return state
|
|
2427
2239
|
}
|
|
@@ -2455,13 +2267,18 @@ export function reduceAIState<M extends UIMessage>(
|
|
|
2455
2267
|
) {
|
|
2456
2268
|
return state
|
|
2457
2269
|
}
|
|
2270
|
+
const compaction: CompactionState<M> = {
|
|
2271
|
+
status: 'completed',
|
|
2272
|
+
...payload,
|
|
2273
|
+
throughIndex:
|
|
2274
|
+
payload.throughIndex ?? state.activeProjection!.promptThroughIndex!,
|
|
2275
|
+
}
|
|
2458
2276
|
return {
|
|
2459
2277
|
...state,
|
|
2460
|
-
compaction
|
|
2461
|
-
|
|
2462
|
-
...
|
|
2463
|
-
|
|
2464
|
-
payload.throughIndex ?? state.activeProjection!.promptThroughIndex!,
|
|
2278
|
+
compaction,
|
|
2279
|
+
activeProjection: state.activeProjection && {
|
|
2280
|
+
...state.activeProjection,
|
|
2281
|
+
baseCompaction: compaction,
|
|
2465
2282
|
},
|
|
2466
2283
|
}
|
|
2467
2284
|
}
|
|
@@ -2483,22 +2300,31 @@ export function deriveUIMessages<M extends UIMessage = UIMessage>(
|
|
|
2483
2300
|
export function createReducer<D extends EventDefs>(options: {
|
|
2484
2301
|
contract: Contract<D>
|
|
2485
2302
|
name?: string
|
|
2486
|
-
|
|
2303
|
+
fold?: Reducer<D, AIState<AIMessageFrom<D>>>['fold']
|
|
2304
|
+
stateSchema?: StandardSchemaV1<unknown, AIState<AIMessageFrom<D>>>
|
|
2305
|
+
}): AIStoredReducer<D, AIState<AIMessageFrom<D>>> {
|
|
2487
2306
|
type M = AIMessageFrom<D>
|
|
2488
2307
|
const reducer = options.contract
|
|
2489
2308
|
.reducer({
|
|
2490
|
-
name: options.name ?? 'a2.ai.state.
|
|
2309
|
+
name: options.name ?? 'a2.ai.state.v21',
|
|
2491
2310
|
initialState: initialState<M>(),
|
|
2311
|
+
...(options.stateSchema === undefined
|
|
2312
|
+
? {}
|
|
2313
|
+
: { stateSchema: options.stateSchema }),
|
|
2492
2314
|
})
|
|
2493
|
-
.fold((state, event) => reduceAIState(state, event))
|
|
2494
|
-
return {
|
|
2315
|
+
.fold(options.fold ?? ((state, event) => reduceAIState(state, event)))
|
|
2316
|
+
return storedAIReducer({
|
|
2317
|
+
...reducer,
|
|
2318
|
+
[clientStateProjection]: projectAIClientState,
|
|
2319
|
+
})
|
|
2495
2320
|
}
|
|
2496
2321
|
|
|
2497
2322
|
type CommandInput<C> = { type: 'ai.control.requested'; payload: C; id?: string }
|
|
2498
2323
|
type ControlInput<
|
|
2499
2324
|
M extends UIMessage,
|
|
2500
2325
|
A extends ControlCommand<M>['action'],
|
|
2501
|
-
> = CommandInput<Extract<ControlCommand<M>, { action: A }>>
|
|
2326
|
+
> = CommandInput<Extract<ControlCommand<M>, { action: A }>> &
|
|
2327
|
+
(A extends 'send' | 'steer' ? { id: string } : {})
|
|
2502
2328
|
|
|
2503
2329
|
export type AIInputs = {
|
|
2504
2330
|
message<M extends UIMessage>(
|
|
@@ -2515,7 +2341,6 @@ export type AIInputs = {
|
|
|
2515
2341
|
}): ControlInput<UIMessage, 'send-now'>[]
|
|
2516
2342
|
edit<M extends UIMessage>(options: {
|
|
2517
2343
|
message: M
|
|
2518
|
-
expectedRevision: number
|
|
2519
2344
|
}): ControlInput<M, 'edit'>[]
|
|
2520
2345
|
move(options: {
|
|
2521
2346
|
inputId: string
|
|
@@ -2560,7 +2385,7 @@ const messageInput = <M extends UIMessage>(
|
|
|
2560
2385
|
): ControlInput<M, 'send'>[] => [
|
|
2561
2386
|
{
|
|
2562
2387
|
type: 'ai.control.requested',
|
|
2563
|
-
id:
|
|
2388
|
+
id: message.id,
|
|
2564
2389
|
payload: {
|
|
2565
2390
|
action: 'send',
|
|
2566
2391
|
message,
|
|
@@ -2579,7 +2404,13 @@ export const inputs: AIInputs = {
|
|
|
2579
2404
|
move: (options) => commandInput({ action: 'move', ...options }),
|
|
2580
2405
|
},
|
|
2581
2406
|
stop: (options) => commandInput({ action: 'stop', ...options }),
|
|
2582
|
-
steer: (options) =>
|
|
2407
|
+
steer: (options) => [
|
|
2408
|
+
{
|
|
2409
|
+
type: 'ai.control.requested',
|
|
2410
|
+
id: options.message.id,
|
|
2411
|
+
payload: { action: 'steer', ...options },
|
|
2412
|
+
},
|
|
2413
|
+
],
|
|
2583
2414
|
pause: (options) => commandInput({ action: 'pause', ...options }),
|
|
2584
2415
|
resume: () => commandInput({ action: 'resume' }),
|
|
2585
2416
|
message<M extends UIMessage>(message: M, options?: { generate?: boolean }) {
|
|
@@ -2591,19 +2422,19 @@ export const inputs: AIInputs = {
|
|
|
2591
2422
|
approval: (response) => [
|
|
2592
2423
|
{
|
|
2593
2424
|
...commandInput({ action: 'approval', response })[0]!,
|
|
2594
|
-
id: `ai.approval:${encodeURIComponent(response.messageId)}:${encodeURIComponent(response.generationId)}:${encodeURIComponent(response.approvalId)}:response`,
|
|
2425
|
+
id: `ai.approval:${encodeURIComponent(response.requestId)}:${encodeURIComponent(response.messageId)}:${encodeURIComponent(response.generationId)}:${encodeURIComponent(response.approvalId)}:response`,
|
|
2595
2426
|
},
|
|
2596
2427
|
],
|
|
2597
2428
|
input: (response) => [
|
|
2598
2429
|
{
|
|
2599
2430
|
...commandInput({ action: 'input', response })[0]!,
|
|
2600
|
-
id: `ai.input:${encodeURIComponent(response.messageId)}:${encodeURIComponent(response.generationId)}:${encodeURIComponent(response.inputId)}:response`,
|
|
2431
|
+
id: `ai.input:${encodeURIComponent(response.requestId)}:${encodeURIComponent(response.messageId)}:${encodeURIComponent(response.generationId)}:${encodeURIComponent(response.inputId)}:response`,
|
|
2601
2432
|
},
|
|
2602
2433
|
],
|
|
2603
2434
|
requestInput: (request) => [
|
|
2604
2435
|
{
|
|
2605
2436
|
...commandInput({ action: 'request-input', request })[0]!,
|
|
2606
|
-
id: `ai.input:${encodeURIComponent(request.messageId)}:${encodeURIComponent(request.generationId)}:${encodeURIComponent(request.inputId)}:request`,
|
|
2437
|
+
id: `ai.input:${encodeURIComponent(request.requestId)}:${encodeURIComponent(request.messageId)}:${encodeURIComponent(request.generationId)}:${encodeURIComponent(request.inputId)}:request`,
|
|
2607
2438
|
},
|
|
2608
2439
|
],
|
|
2609
2440
|
retry: (request) => [
|
|
@@ -2674,14 +2505,17 @@ export function agent<
|
|
|
2674
2505
|
})
|
|
2675
2506
|
const reducer = agentContract
|
|
2676
2507
|
.reducer({
|
|
2677
|
-
name: options.reducerName ?? 'a2.ai.state.
|
|
2508
|
+
name: options.reducerName ?? 'a2.ai.state.v21',
|
|
2678
2509
|
initialState: initialState<M>(),
|
|
2679
2510
|
})
|
|
2680
2511
|
.fold((state, event) => reduceAIState(state, event))
|
|
2681
2512
|
|
|
2682
2513
|
const definition = {
|
|
2683
2514
|
contract: agentContract,
|
|
2684
|
-
reducer: {
|
|
2515
|
+
reducer: storedAIReducer({
|
|
2516
|
+
...reducer,
|
|
2517
|
+
[clientStateProjection]: projectAIClientState<M>,
|
|
2518
|
+
}),
|
|
2685
2519
|
}
|
|
2686
2520
|
return definition as unknown as AgentDefinition<M, AIEventDefs<M> & E>
|
|
2687
2521
|
}
|