experimental-a2 0.13.0 → 0.14.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 +43 -0
- package/dist/{actor-DJi3RsNu.d.ts → actor-BfQSE0KC.d.ts} +4 -4
- package/dist/{actor-DJi3RsNu.d.ts.map → actor-BfQSE0KC.d.ts.map} +1 -1
- package/dist/actor-client.d.ts +1 -1
- package/dist/actor-client.js +1 -1
- package/dist/actor-react.d.ts +3 -3
- package/dist/actor-react.js +2 -2
- package/dist/{actor-shared-DI7J5upy.js → actor-shared-B5tJfzt-.js} +2 -2
- package/dist/{actor-shared-DI7J5upy.js.map → actor-shared-B5tJfzt-.js.map} +1 -1
- package/dist/actor.d.ts +1 -1
- package/dist/actor.js +3 -3
- package/dist/ai-Cai-lCbj.d.ts +580 -0
- package/dist/ai-Cai-lCbj.d.ts.map +1 -0
- package/dist/ai-control-CcD4hh3y.js +119 -0
- package/dist/ai-control-CcD4hh3y.js.map +1 -0
- package/dist/ai-server.d.ts +6 -6
- package/dist/ai-server.d.ts.map +1 -1
- package/dist/ai-server.js +1053 -513
- package/dist/ai-server.js.map +1 -1
- package/dist/ai.d.ts +2 -365
- package/dist/ai.js +801 -80
- package/dist/ai.js.map +1 -1
- package/dist/{client-P_NNNRM-.d.ts → client-BAEABRZB.d.ts} +2 -2
- package/dist/{client-P_NNNRM-.d.ts.map → client-BAEABRZB.d.ts.map} +1 -1
- package/dist/{client-Bf6uSEAk.js → client-BYzHjkwU.js} +21 -6
- package/dist/client-BYzHjkwU.js.map +1 -0
- package/dist/client.d.ts +1 -1
- package/dist/client.js +1 -1
- package/dist/{contract-48bUMgcL.js → contract-CKRg_E4q.js} +3 -26
- package/dist/contract-CKRg_E4q.js.map +1 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/react.d.ts +2 -2
- package/dist/react.js +1 -1
- package/dist/{reducer-DJKWm3cp.d.ts → reducer-BcS9VDKC.d.ts} +4 -1
- package/dist/{reducer-DJKWm3cp.d.ts.map → reducer-BcS9VDKC.d.ts.map} +1 -1
- package/dist/reducer-DEMjEY_O.js +29 -0
- package/dist/reducer-DEMjEY_O.js.map +1 -0
- 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-DjZZa1wr.d.ts → server-Bp5Nd1pF.d.ts} +3 -3
- package/dist/{server-DjZZa1wr.d.ts.map → server-Bp5Nd1pF.d.ts.map} +1 -1
- package/dist/{server-BeNADlCI.js → server-CjJSGcF7.js} +4 -3
- package/dist/server-CjJSGcF7.js.map +1 -0
- package/dist/server.d.ts +3 -3
- package/dist/server.js +1 -1
- package/dist/{store-DtDOWLSn.d.ts → store-D_yhNdPz.d.ts} +7 -2
- package/dist/{store-DtDOWLSn.d.ts.map → store-D_yhNdPz.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-postgres.js +19 -0
- package/dist/store-postgres.js.map +1 -1
- package/dist/store-redis-http.d.ts +1 -1
- package/dist/store-redis-http.js +1 -1
- package/dist/{store-redis-notify-BUCyXOn0.js → store-redis-notify-D2EI6gwX.js} +27 -2
- package/dist/store-redis-notify-D2EI6gwX.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/docs/guides/06-ai-agents.mdx +287 -65
- package/docs/reference/01-api.mdx +122 -27
- package/examples/playground/app/agent/[agentId]/agent-client.tsx +118 -21
- package/examples/playground/app/agent/[agentId]/agent-queue.tsx +289 -0
- package/examples/playground/app/agent/compaction-settings.test.ts +22 -6
- package/examples/playground/app/agent/model.ts +11 -2
- package/examples/playground/app/agent/server.ts +8 -2
- package/examples/playground/app/chat/[chatId]/chat-client.tsx +3 -13
- package/examples/playground/app/chat/model.ts +2 -2
- package/examples/playground/app/chat/server.ts +24 -17
- package/examples/playground/app/globals.css +179 -0
- package/examples/playground/package.json +1 -1
- package/package.json +1 -1
- package/src/ai-client-state.ts +185 -0
- package/src/ai-control-server.ts +829 -0
- package/src/ai-control-state.ts +152 -0
- package/src/ai-control.ts +139 -0
- package/src/ai-coordinator.ts +99 -32
- package/src/ai-progress-batches.ts +68 -0
- package/src/ai-projector.ts +76 -15
- package/src/ai-sdk-step.ts +0 -1
- package/src/ai-server.ts +429 -619
- package/src/ai.ts +553 -108
- package/src/client.ts +31 -9
- package/src/licenses/Apache-2.0.txt +55 -0
- package/src/parse-partial-json.ts +441 -0
- package/src/reducer.ts +6 -0
- package/src/server.ts +8 -4
- package/src/store-postgres.ts +27 -0
- package/src/store-redis-core.ts +53 -1
- package/src/store-redis-notify.ts +1 -0
- package/src/store.ts +6 -0
- package/dist/ai.d.ts.map +0 -1
- package/dist/client-Bf6uSEAk.js.map +0 -1
- package/dist/contract-48bUMgcL.js.map +0 -1
- package/dist/server-BeNADlCI.js.map +0 -1
- package/dist/store-redis-notify-BUCyXOn0.js.map +0 -1
package/src/ai-server.ts
CHANGED
|
@@ -8,7 +8,10 @@
|
|
|
8
8
|
// oxlint-disable no-await-in-loop -- stream chunks and durable appends are
|
|
9
9
|
// ordered; parallel consumption would corrupt progress sequence and state.
|
|
10
10
|
|
|
11
|
+
import { createControlRuntime, ControlCancelled } from './ai-control-server.ts'
|
|
12
|
+
import type { ControlCommit } from './ai-control.ts'
|
|
11
13
|
import { AsyncLocalStorage } from 'node:async_hooks'
|
|
14
|
+
import { createHash } from 'node:crypto'
|
|
12
15
|
import { asSchema, convertToModelMessages } from 'ai'
|
|
13
16
|
import type {
|
|
14
17
|
FinishReason,
|
|
@@ -34,17 +37,13 @@ import type {
|
|
|
34
37
|
GenerationCompletedPayload,
|
|
35
38
|
GenerationFailedPayload,
|
|
36
39
|
GenerationProgressPayload,
|
|
37
|
-
GenerationRequestedPayload,
|
|
38
40
|
GenerationStartedPayload,
|
|
39
41
|
MessageCompletedPayload,
|
|
40
|
-
MessageCreatedPayload,
|
|
41
|
-
MessageInterruptedPayload,
|
|
42
42
|
ToolCalledPayload,
|
|
43
43
|
ToolResultPayload,
|
|
44
44
|
} from './ai.ts'
|
|
45
45
|
import {
|
|
46
46
|
aiCoordinatorReducer,
|
|
47
|
-
continuationReady,
|
|
48
47
|
type AICoordinatorState,
|
|
49
48
|
} from './ai-coordinator.ts'
|
|
50
49
|
import type { AppendInput, ContractEvent, EventDefs } from './contract.ts'
|
|
@@ -67,24 +66,23 @@ import {
|
|
|
67
66
|
|
|
68
67
|
function validateAgentIngress(context: ServerIngressContext): void {
|
|
69
68
|
const rejected = context.events.find((event) => {
|
|
70
|
-
if (event.type !== 'ai.
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
69
|
+
if (event.type !== 'ai.control.requested') return true
|
|
70
|
+
const command = event.payload as Record<string, unknown>
|
|
71
|
+
if (
|
|
72
|
+
command['action'] === 'request-input' ||
|
|
73
|
+
command['action'] === 'tool-result'
|
|
74
|
+
)
|
|
75
|
+
return true
|
|
76
|
+
if (
|
|
77
|
+
command['action'] === 'send' ||
|
|
78
|
+
command['action'] === 'edit' ||
|
|
79
|
+
command['action'] === 'steer'
|
|
80
|
+
) {
|
|
81
|
+
return (
|
|
82
|
+
(command['message'] as { role?: unknown } | undefined)?.role !== 'user'
|
|
76
83
|
)
|
|
77
84
|
}
|
|
78
|
-
|
|
79
|
-
return (
|
|
80
|
-
typeof payload !== 'object' ||
|
|
81
|
-
payload === null ||
|
|
82
|
-
!('message' in payload) ||
|
|
83
|
-
typeof payload.message !== 'object' ||
|
|
84
|
-
payload.message === null ||
|
|
85
|
-
!('role' in payload.message) ||
|
|
86
|
-
payload.message.role !== 'user'
|
|
87
|
-
)
|
|
85
|
+
return false
|
|
88
86
|
})
|
|
89
87
|
if (rejected !== undefined) {
|
|
90
88
|
throw new A2Error(
|
|
@@ -119,7 +117,7 @@ export type AgentGenerateContext<
|
|
|
119
117
|
messages: M[]
|
|
120
118
|
modelMessages: ModelMessage[]
|
|
121
119
|
state: AIState<M>
|
|
122
|
-
|
|
120
|
+
session: Pick<HandlerContext<D>['session'], 'state'>
|
|
123
121
|
signal: AbortSignal
|
|
124
122
|
model: LanguageModel
|
|
125
123
|
tools: T
|
|
@@ -152,7 +150,7 @@ export type AgentResolverContext<
|
|
|
152
150
|
> = {
|
|
153
151
|
event: ContractEvent<D, 'ai.generation.requested'>
|
|
154
152
|
state: AIState<M>
|
|
155
|
-
|
|
153
|
+
session: Pick<HandlerContext<D>['session'], 'state'>
|
|
156
154
|
signal: AbortSignal
|
|
157
155
|
}
|
|
158
156
|
|
|
@@ -214,9 +212,7 @@ export type CreateHandlersOptions<
|
|
|
214
212
|
* The custom stream owns its metadata chunks.
|
|
215
213
|
*/
|
|
216
214
|
generate?: AgentGenerate<M, D, T>
|
|
217
|
-
compaction?:
|
|
218
|
-
| CompactionOptions<M, D>
|
|
219
|
-
| ((context: AgentResolverContext<M, D>) => CompactionOptions<M, D>)
|
|
215
|
+
compaction?: Resolvable<CompactionOptions<M, D>, AgentResolverContext<M, D>>
|
|
220
216
|
/** Progress is durably flushed at either limit, whichever is reached first. */
|
|
221
217
|
progress?: { maxChunks?: number; maxDelayMs?: number }
|
|
222
218
|
}
|
|
@@ -341,10 +337,38 @@ async function generateWithAISDK<
|
|
|
341
337
|
M extends UIMessage,
|
|
342
338
|
D extends AIEventDefs<M> & EventDefs,
|
|
343
339
|
T extends ToolSet,
|
|
344
|
-
>(
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
340
|
+
>(options: {
|
|
341
|
+
agentName: string
|
|
342
|
+
context: AgentGenerateContext<M, D, T>
|
|
343
|
+
messageMetadata: AgentMessageMetadata<M, D, T> | undefined
|
|
344
|
+
}): Promise<GenerationSource> {
|
|
345
|
+
const { agentName, context, messageMetadata } = options
|
|
346
|
+
const headers = context.generation.headers ?? {}
|
|
347
|
+
const generation =
|
|
348
|
+
gatewayModelId(context.model) === undefined
|
|
349
|
+
? context.generation
|
|
350
|
+
: {
|
|
351
|
+
...context.generation,
|
|
352
|
+
headers: Object.keys(headers).some(
|
|
353
|
+
(key) => key.toLowerCase() === 'x-session-affinity',
|
|
354
|
+
)
|
|
355
|
+
? headers
|
|
356
|
+
: {
|
|
357
|
+
...headers,
|
|
358
|
+
'x-session-affinity': createHash('sha256')
|
|
359
|
+
.update(
|
|
360
|
+
JSON.stringify([agentName, context.request.sessionId]),
|
|
361
|
+
)
|
|
362
|
+
.digest('hex'),
|
|
363
|
+
},
|
|
364
|
+
providerOptions: {
|
|
365
|
+
...context.generation.providerOptions,
|
|
366
|
+
gateway: {
|
|
367
|
+
caching: 'auto',
|
|
368
|
+
...context.generation.providerOptions?.['gateway'],
|
|
369
|
+
},
|
|
370
|
+
},
|
|
371
|
+
}
|
|
348
372
|
const step = await generateAISDKStep<M, T>({
|
|
349
373
|
model: context.model,
|
|
350
374
|
tools: context.tools,
|
|
@@ -355,11 +379,12 @@ async function generateWithAISDK<
|
|
|
355
379
|
...(context.instructions === undefined
|
|
356
380
|
? {}
|
|
357
381
|
: { instructions: context.instructions }),
|
|
358
|
-
settings:
|
|
382
|
+
settings: generation,
|
|
359
383
|
...(messageMetadata === undefined
|
|
360
384
|
? {}
|
|
361
385
|
: {
|
|
362
|
-
messageMetadata: ({ part }) =>
|
|
386
|
+
messageMetadata: ({ part }) =>
|
|
387
|
+
messageMetadata({ ...context, generation, part }),
|
|
363
388
|
}),
|
|
364
389
|
})
|
|
365
390
|
return {
|
|
@@ -443,12 +468,17 @@ async function* consumeGeneration(options: {
|
|
|
443
468
|
}
|
|
444
469
|
}
|
|
445
470
|
|
|
446
|
-
const
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
471
|
+
const foldAIEvents = <
|
|
472
|
+
M extends UIMessage,
|
|
473
|
+
D extends AIEventDefs<M> & EventDefs,
|
|
474
|
+
>(options: {
|
|
475
|
+
agent: AgentDefinition<M, D>
|
|
476
|
+
state: AIState<M>
|
|
477
|
+
events: ContractEvent<D>[]
|
|
478
|
+
}): AIState<M> => {
|
|
479
|
+
let state = options.state
|
|
480
|
+
for (const event of options.events)
|
|
481
|
+
state = options.agent.reducer.fold(state, event)
|
|
452
482
|
return state
|
|
453
483
|
}
|
|
454
484
|
|
|
@@ -457,6 +487,7 @@ const summarize = async <
|
|
|
457
487
|
D extends AIEventDefs<M> & EventDefs,
|
|
458
488
|
T extends ToolSet,
|
|
459
489
|
>(options: {
|
|
490
|
+
agentName: string
|
|
460
491
|
context: AgentGenerateContext<M, D, T>
|
|
461
492
|
instructions: string | undefined
|
|
462
493
|
}): Promise<{ summary: string; usage?: LanguageModelUsage }> => {
|
|
@@ -498,8 +529,9 @@ const summarize = async <
|
|
|
498
529
|
return [name, definition]
|
|
499
530
|
}),
|
|
500
531
|
) as T
|
|
501
|
-
const source = await generateWithAISDK(
|
|
502
|
-
|
|
532
|
+
const source = await generateWithAISDK({
|
|
533
|
+
agentName: options.agentName,
|
|
534
|
+
context: {
|
|
503
535
|
...options.context,
|
|
504
536
|
generation,
|
|
505
537
|
tools,
|
|
@@ -509,8 +541,8 @@ const summarize = async <
|
|
|
509
541
|
],
|
|
510
542
|
responseMessageId: `${options.context.generationId}:summary`,
|
|
511
543
|
},
|
|
512
|
-
undefined,
|
|
513
|
-
)
|
|
544
|
+
messageMetadata: undefined,
|
|
545
|
+
})
|
|
514
546
|
let summary = ''
|
|
515
547
|
let calledTool = false
|
|
516
548
|
let finish: AgentGenerationFinish | undefined
|
|
@@ -551,71 +583,83 @@ const activeCompaction = <M extends UIMessage>(
|
|
|
551
583
|
: null
|
|
552
584
|
}
|
|
553
585
|
|
|
554
|
-
const contextMessages = <
|
|
586
|
+
const contextMessages = async <
|
|
555
587
|
M extends UIMessage,
|
|
556
588
|
D extends AIEventDefs<M> & EventDefs,
|
|
557
589
|
>(options: {
|
|
558
590
|
agent: AgentDefinition<M, D>
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
591
|
+
session: HandlerContext<D>['session']
|
|
592
|
+
snapshot: { state: AIState<M>; index: number }
|
|
593
|
+
coordinator: AICoordinatorState
|
|
594
|
+
appended?: ContractEvent<D>[]
|
|
595
|
+
excluded?: ReadonlySet<string>
|
|
596
|
+
}): Promise<M[]> => {
|
|
597
|
+
const { agent, session, snapshot } = options
|
|
598
|
+
const appended = options.appended ?? []
|
|
599
|
+
const state = foldAIEvents({ agent, events: appended, state: snapshot.state })
|
|
563
600
|
const compaction = activeCompaction(state)
|
|
564
|
-
|
|
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)
|
|
565
607
|
const retained = new Set(compaction.retainedMessageIds ?? [])
|
|
566
|
-
const
|
|
567
|
-
(event) =>
|
|
568
|
-
event.type === 'ai.generation.started' &&
|
|
569
|
-
(event.payload as GenerationStartedPayload).generationId ===
|
|
570
|
-
compaction.generationId,
|
|
571
|
-
)?.index
|
|
572
|
-
const throughIndex =
|
|
573
|
-
compaction.throughIndex ??
|
|
574
|
-
(startedIndex === undefined ? undefined : startedIndex - 1)
|
|
608
|
+
const throughIndex = compaction.throughIndex
|
|
575
609
|
if (throughIndex !== undefined) {
|
|
576
|
-
const prefix =
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
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)
|
|
591
644
|
}
|
|
592
|
-
return
|
|
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
|
+
)
|
|
593
652
|
}
|
|
594
653
|
const boundary = state.messages.findIndex(
|
|
595
654
|
(message) => message.id === compaction.throughMessageId,
|
|
596
655
|
)
|
|
597
|
-
return [
|
|
656
|
+
return visible([
|
|
598
657
|
...compaction.messages!,
|
|
599
658
|
...state.messages
|
|
600
659
|
.slice(0, boundary + 1)
|
|
601
660
|
.filter((message) => retained.has(message.id)),
|
|
602
661
|
...state.messages.slice(boundary + 1),
|
|
603
|
-
]
|
|
604
|
-
}
|
|
605
|
-
|
|
606
|
-
const activeContextMessages = <
|
|
607
|
-
M extends UIMessage,
|
|
608
|
-
D extends AIEventDefs<M> & EventDefs,
|
|
609
|
-
>(options: {
|
|
610
|
-
agent: AgentDefinition<M, D>
|
|
611
|
-
state: AIState<M>
|
|
612
|
-
history: ContractEvent<D>[]
|
|
613
|
-
coordinator: AICoordinatorState
|
|
614
|
-
}): M[] => {
|
|
615
|
-
const queued = new Set(
|
|
616
|
-
options.coordinator.queued.map((item) => item.messageId),
|
|
617
|
-
)
|
|
618
|
-
return contextMessages(options).filter((message) => !queued.has(message.id))
|
|
662
|
+
])
|
|
619
663
|
}
|
|
620
664
|
|
|
621
665
|
const modelContext = async <M extends UIMessage>(options: {
|
|
@@ -655,51 +699,20 @@ const estimateInputTokens = async (options: {
|
|
|
655
699
|
)
|
|
656
700
|
}
|
|
657
701
|
|
|
658
|
-
const measuredInputTokens =
|
|
702
|
+
const measuredInputTokens = (options: {
|
|
659
703
|
estimate: number
|
|
660
704
|
model: string
|
|
661
|
-
|
|
705
|
+
calibration: AICoordinatorState['calibration']
|
|
662
706
|
}): number => {
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
(candidate) =>
|
|
673
|
-
candidate.type === 'ai.generation.started' &&
|
|
674
|
-
(candidate.payload as GenerationStartedPayload).generationId ===
|
|
675
|
-
completed.generationId,
|
|
676
|
-
)
|
|
677
|
-
const input = completed.usage?.inputTokens
|
|
678
|
-
const estimate = completed.inputTokenEstimate
|
|
679
|
-
if (
|
|
680
|
-
(started?.payload as GenerationStartedPayload | undefined)?.model !==
|
|
681
|
-
options.model
|
|
682
|
-
)
|
|
683
|
-
break
|
|
684
|
-
if (
|
|
685
|
-
input === undefined ||
|
|
686
|
-
estimate === undefined ||
|
|
687
|
-
!Number.isFinite(input) ||
|
|
688
|
-
input < 0 ||
|
|
689
|
-
options.estimate < estimate
|
|
690
|
-
)
|
|
691
|
-
break
|
|
692
|
-
return Math.max(
|
|
693
|
-
options.estimate,
|
|
694
|
-
Math.ceil(input + options.estimate - estimate),
|
|
695
|
-
)
|
|
696
|
-
}
|
|
697
|
-
return options.estimate
|
|
698
|
-
}
|
|
699
|
-
|
|
700
|
-
const generationRequestId = (generationId: string): string | undefined => {
|
|
701
|
-
const markerIndex = generationId.lastIndexOf(':generation:')
|
|
702
|
-
return markerIndex === -1 ? undefined : generationId.slice(0, markerIndex)
|
|
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
|
|
703
716
|
}
|
|
704
717
|
|
|
705
718
|
type ToolCallClassification = 'automatic' | 'approval' | 'unknown'
|
|
@@ -946,36 +959,6 @@ const lifecycleEvents = <D extends EventDefs, T extends ToolSet>(options: {
|
|
|
946
959
|
return { events: result, pending }
|
|
947
960
|
}
|
|
948
961
|
|
|
949
|
-
function queuedMessagesAt<D extends EventDefs>(
|
|
950
|
-
history: ContractEvent<D>[],
|
|
951
|
-
): AICoordinatorState['queued'] {
|
|
952
|
-
const queued: AICoordinatorState['queued'] = []
|
|
953
|
-
for (const event of history) {
|
|
954
|
-
if (event.type === 'ai.message.created') {
|
|
955
|
-
const payload = event.payload as MessageCreatedPayload<UIMessage>
|
|
956
|
-
if (payload.message.role !== 'user') continue
|
|
957
|
-
const duplicate = queued.findIndex(
|
|
958
|
-
(item) => item.messageId === payload.message.id,
|
|
959
|
-
)
|
|
960
|
-
if (duplicate !== -1) queued.splice(duplicate, 1)
|
|
961
|
-
queued.push({
|
|
962
|
-
index: event.index,
|
|
963
|
-
messageId: payload.message.id,
|
|
964
|
-
generate: payload.generate !== false,
|
|
965
|
-
})
|
|
966
|
-
continue
|
|
967
|
-
}
|
|
968
|
-
if (event.type !== 'ai.generation.requested') continue
|
|
969
|
-
const request = event.payload as GenerationRequestedPayload
|
|
970
|
-
if (request.reason !== 'message') continue
|
|
971
|
-
const requested = queued.findIndex(
|
|
972
|
-
(item) => item.messageId === request.messageId,
|
|
973
|
-
)
|
|
974
|
-
if (requested !== -1) queued.splice(0, requested + 1)
|
|
975
|
-
}
|
|
976
|
-
return queued
|
|
977
|
-
}
|
|
978
|
-
|
|
979
962
|
function withoutGenerationLifecycle<D extends EventDefs>(
|
|
980
963
|
history: ContractEvent<D>[],
|
|
981
964
|
generationIds: ReadonlySet<string>,
|
|
@@ -1062,7 +1045,7 @@ const validateCompaction = <
|
|
|
1062
1045
|
}
|
|
1063
1046
|
if (compaction !== false && 'then' in compaction) {
|
|
1064
1047
|
void Promise.resolve(compaction).catch(() => {})
|
|
1065
|
-
throw new TypeError('compaction options must
|
|
1048
|
+
throw new TypeError('compaction options must be a policy or a resolver')
|
|
1066
1049
|
}
|
|
1067
1050
|
if (compaction !== false && !('shouldCompact' in compaction)) {
|
|
1068
1051
|
if (
|
|
@@ -1150,89 +1133,10 @@ export function createHandlers<
|
|
|
1150
1133
|
const tools = options.tools ?? ({} as T)
|
|
1151
1134
|
const generation: AgentGenerationSettings<T> = options.generation ?? {}
|
|
1152
1135
|
const maxSteps = options.maxSteps ?? Number.POSITIVE_INFINITY
|
|
1153
|
-
const
|
|
1136
|
+
const control = createControlRuntime({ agent: options.agent })
|
|
1137
|
+
const coordinator = control.coordinator
|
|
1154
1138
|
const promptCache = new Map<string, Promise<ModelMessage[]>>()
|
|
1155
1139
|
|
|
1156
|
-
const coordinatorStateAt = (
|
|
1157
|
-
history: ContractEvent<D>[],
|
|
1158
|
-
frontier = Number.POSITIVE_INFINITY,
|
|
1159
|
-
): AICoordinatorState => {
|
|
1160
|
-
let state = coordinator.initialState
|
|
1161
|
-
for (const event of history) {
|
|
1162
|
-
if (event.index >= frontier) break
|
|
1163
|
-
state = coordinator.fold(state, event)
|
|
1164
|
-
}
|
|
1165
|
-
return state
|
|
1166
|
-
}
|
|
1167
|
-
|
|
1168
|
-
const requestForNextMessage = (
|
|
1169
|
-
state: AICoordinatorState,
|
|
1170
|
-
): AppendInput<D> | undefined => {
|
|
1171
|
-
if (state.closed || state.response !== undefined) return undefined
|
|
1172
|
-
const next = state.queued.find((item) => item.generate !== false)
|
|
1173
|
-
if (next === undefined) return undefined
|
|
1174
|
-
return {
|
|
1175
|
-
type: 'ai.generation.requested',
|
|
1176
|
-
id: `ai.generate:message:${next.messageId}`,
|
|
1177
|
-
payload: { messageId: next.messageId, reason: 'message' },
|
|
1178
|
-
} as AppendInput<D>
|
|
1179
|
-
}
|
|
1180
|
-
|
|
1181
|
-
const scheduleNext = async (
|
|
1182
|
-
ctx: Pick<HandlerContext<D>, 'session'>,
|
|
1183
|
-
): Promise<AppendInput<D> | void> =>
|
|
1184
|
-
requestForNextMessage(
|
|
1185
|
-
(await ctx.session.state(coordinator, { through: 'latest' })).state,
|
|
1186
|
-
)
|
|
1187
|
-
|
|
1188
|
-
const continueIfReady = async (
|
|
1189
|
-
ctx: Pick<HandlerContext<D>, 'session'>,
|
|
1190
|
-
generationId: string,
|
|
1191
|
-
): Promise<void> => {
|
|
1192
|
-
const state = (await ctx.session.state(coordinator, { through: 'latest' }))
|
|
1193
|
-
.state
|
|
1194
|
-
const response = state.response
|
|
1195
|
-
if (response?.generation?.generationId !== generationId) {
|
|
1196
|
-
return
|
|
1197
|
-
}
|
|
1198
|
-
const input = response.inputResponse
|
|
1199
|
-
const inputReady =
|
|
1200
|
-
response.completion?.generationId === generationId &&
|
|
1201
|
-
response.failure === undefined &&
|
|
1202
|
-
input !== undefined &&
|
|
1203
|
-
response.inputs.length === 0 &&
|
|
1204
|
-
response.calls.every(
|
|
1205
|
-
(call) =>
|
|
1206
|
-
call.terminal ||
|
|
1207
|
-
(call.call.providerExecuted === true &&
|
|
1208
|
-
call.call.supportsDeferredResults !== true &&
|
|
1209
|
-
call.approval !== undefined &&
|
|
1210
|
-
call.response !== undefined),
|
|
1211
|
-
)
|
|
1212
|
-
if (inputReady) {
|
|
1213
|
-
await ctx.session.append('continue-after-input', {
|
|
1214
|
-
type: 'ai.generation.requested',
|
|
1215
|
-
id: `ai.generate:input:${encodeURIComponent(response.responseMessageId)}:${encodeURIComponent(input.generationId)}:${encodeURIComponent(input.inputId)}`,
|
|
1216
|
-
payload: {
|
|
1217
|
-
messageId: response.responseMessageId,
|
|
1218
|
-
responseMessageId: response.responseMessageId,
|
|
1219
|
-
reason: 'input',
|
|
1220
|
-
},
|
|
1221
|
-
} as AppendInput<D>)
|
|
1222
|
-
return
|
|
1223
|
-
}
|
|
1224
|
-
if (!continuationReady(state)) return
|
|
1225
|
-
await ctx.session.append('continue-after-tools', {
|
|
1226
|
-
type: 'ai.generation.requested',
|
|
1227
|
-
id: `ai.generate:tools:${generationId}`,
|
|
1228
|
-
payload: {
|
|
1229
|
-
messageId: response.responseMessageId,
|
|
1230
|
-
responseMessageId: response.responseMessageId,
|
|
1231
|
-
reason: 'tool',
|
|
1232
|
-
},
|
|
1233
|
-
} as AppendInput<D>)
|
|
1234
|
-
}
|
|
1235
|
-
|
|
1236
1140
|
type RuntimeTool = {
|
|
1237
1141
|
execute?: (
|
|
1238
1142
|
input: unknown,
|
|
@@ -1244,9 +1148,7 @@ export function createHandlers<
|
|
|
1244
1148
|
) => unknown
|
|
1245
1149
|
}
|
|
1246
1150
|
|
|
1247
|
-
type ToolHandlerContext =
|
|
1248
|
-
| HandlerContext<D, 'ai.tool.called'>
|
|
1249
|
-
| HandlerContext<D, 'ai.approval.responded'>
|
|
1151
|
+
type ToolHandlerContext = HandlerContext<D, 'ai.tool.execution.requested'>
|
|
1250
1152
|
|
|
1251
1153
|
const resultEvent = (
|
|
1252
1154
|
call: ToolCalledPayload,
|
|
@@ -1285,43 +1187,44 @@ export function createHandlers<
|
|
|
1285
1187
|
},
|
|
1286
1188
|
}) as AppendInput<D>
|
|
1287
1189
|
|
|
1288
|
-
const promptMessages = (
|
|
1289
|
-
|
|
1290
|
-
call: ToolCalledPayload
|
|
1291
|
-
|
|
1292
|
-
): Promise<ModelMessage[]> => {
|
|
1293
|
-
const
|
|
1190
|
+
const promptMessages = (input: {
|
|
1191
|
+
ctx: ToolHandlerContext
|
|
1192
|
+
call: ToolCalledPayload
|
|
1193
|
+
generation: GenerationStartedPayload
|
|
1194
|
+
}): Promise<ModelMessage[]> => {
|
|
1195
|
+
const { ctx, call, generation: owner } = input
|
|
1196
|
+
const key = promptCacheKey(ctx.event.sessionId, call.generationId)
|
|
1294
1197
|
const cached = promptCache.get(key)
|
|
1295
1198
|
if (cached) return cached
|
|
1296
1199
|
const computation = (async () => {
|
|
1297
|
-
const
|
|
1298
|
-
|
|
1299
|
-
(
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
const frontier =
|
|
1307
|
-
startedPayload?.promptThroughIndex ??
|
|
1308
|
-
(started?.index ?? Number.POSITIVE_INFINITY) - 1
|
|
1309
|
-
const promptHistory = history.filter(
|
|
1200
|
+
const frontier = owner.promptThroughIndex
|
|
1201
|
+
if (frontier === undefined)
|
|
1202
|
+
throw new TypeError('AI work requires a prompt checkpoint')
|
|
1203
|
+
const [snapshot, atPrompt, tail] = await Promise.all([
|
|
1204
|
+
ctx.session.state(options.agent.reducer, { through: frontier }),
|
|
1205
|
+
ctx.session.state(coordinator, { through: frontier }),
|
|
1206
|
+
ctx.session.history({ gte: frontier + 1, lte: ctx.event.index }),
|
|
1207
|
+
])
|
|
1208
|
+
const appended = tail.filter(
|
|
1310
1209
|
(event) =>
|
|
1311
|
-
event.
|
|
1312
|
-
((event.type === 'ai.generation.started' ||
|
|
1210
|
+
(event.type === 'ai.generation.started' ||
|
|
1313
1211
|
event.type === 'ai.compaction.requested' ||
|
|
1314
1212
|
event.type === 'ai.compaction.completed') &&
|
|
1315
|
-
|
|
1316
|
-
|
|
1213
|
+
(event.payload as { generationId: string }).generationId ===
|
|
1214
|
+
call.generationId,
|
|
1317
1215
|
)
|
|
1318
|
-
const state =
|
|
1216
|
+
const state = foldAIEvents({
|
|
1217
|
+
agent: options.agent,
|
|
1218
|
+
events: appended,
|
|
1219
|
+
state: snapshot.state,
|
|
1220
|
+
})
|
|
1319
1221
|
return modelContext({
|
|
1320
|
-
messages:
|
|
1222
|
+
messages: await contextMessages({
|
|
1321
1223
|
agent: options.agent,
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
coordinator:
|
|
1224
|
+
session: ctx.session,
|
|
1225
|
+
snapshot,
|
|
1226
|
+
coordinator: atPrompt.state,
|
|
1227
|
+
appended,
|
|
1325
1228
|
}),
|
|
1326
1229
|
state,
|
|
1327
1230
|
tools,
|
|
@@ -1342,7 +1245,7 @@ export function createHandlers<
|
|
|
1342
1245
|
const schedulerFailure = consumeSchedulerSendFailure(error)
|
|
1343
1246
|
if (checkAbort(ctx.signal)) return
|
|
1344
1247
|
if (schedulerFailure === 'retryable') throw error
|
|
1345
|
-
return resultEvent(call,
|
|
1248
|
+
return resultEvent(call, `${ctx.event.id}:execution:error`, {
|
|
1346
1249
|
error: errorMessage(error),
|
|
1347
1250
|
})
|
|
1348
1251
|
}
|
|
@@ -1370,7 +1273,7 @@ export function createHandlers<
|
|
|
1370
1273
|
return toolExecutionFailure(ctx, call, error)
|
|
1371
1274
|
}
|
|
1372
1275
|
if (iterator === undefined) {
|
|
1373
|
-
return resultEvent(call,
|
|
1276
|
+
return resultEvent(call, `${ctx.event.id}:execution:0`, { output })
|
|
1374
1277
|
}
|
|
1375
1278
|
let last: unknown
|
|
1376
1279
|
let sequence = 0
|
|
@@ -1383,23 +1286,27 @@ export function createHandlers<
|
|
|
1383
1286
|
} catch (error) {
|
|
1384
1287
|
return toolExecutionFailure(ctx, call, error)
|
|
1385
1288
|
}
|
|
1386
|
-
if (checkAbort(ctx.signal)) return
|
|
1387
1289
|
if (result.done) {
|
|
1290
|
+
if (ctx.signal.reason instanceof A2Error) throw ctx.signal.reason
|
|
1388
1291
|
done = true
|
|
1389
1292
|
break
|
|
1390
1293
|
}
|
|
1294
|
+
if (checkAbort(ctx.signal)) return
|
|
1391
1295
|
last = result.value
|
|
1392
|
-
await
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1296
|
+
await control.append({
|
|
1297
|
+
ctx,
|
|
1298
|
+
name: `tool:${call.toolCallId}:preliminary:${sequence}`,
|
|
1299
|
+
events: [
|
|
1300
|
+
resultEvent(
|
|
1301
|
+
call,
|
|
1302
|
+
`${ctx.event.id}:execution:${ctx.attempt}:${sequence}:preliminary`,
|
|
1303
|
+
{
|
|
1304
|
+
output: result.value,
|
|
1305
|
+
preliminary: true,
|
|
1306
|
+
},
|
|
1307
|
+
),
|
|
1308
|
+
],
|
|
1309
|
+
})
|
|
1403
1310
|
sequence += 1
|
|
1404
1311
|
}
|
|
1405
1312
|
} finally {
|
|
@@ -1413,27 +1320,25 @@ export function createHandlers<
|
|
|
1413
1320
|
}
|
|
1414
1321
|
return resultEvent(
|
|
1415
1322
|
call,
|
|
1416
|
-
|
|
1323
|
+
`${ctx.event.id}:execution:${sequence}:final`,
|
|
1417
1324
|
sequence === 0 ? {} : { output: last },
|
|
1418
1325
|
)
|
|
1419
1326
|
}
|
|
1420
1327
|
|
|
1421
|
-
const executeTool = async (
|
|
1422
|
-
ctx: ToolHandlerContext
|
|
1423
|
-
call: ToolCalledPayload
|
|
1424
|
-
|
|
1328
|
+
const executeTool = async (input: {
|
|
1329
|
+
ctx: ToolHandlerContext
|
|
1330
|
+
call: ToolCalledPayload
|
|
1331
|
+
generation: GenerationStartedPayload
|
|
1332
|
+
}): Promise<AppendInput<D> | void> => {
|
|
1333
|
+
const { ctx, call } = input
|
|
1425
1334
|
const tool = tools[call.toolName] as RuntimeTool | undefined
|
|
1426
1335
|
const execute = tool?.execute
|
|
1427
1336
|
if (execute === undefined) {
|
|
1428
|
-
return resultEvent(call,
|
|
1337
|
+
return resultEvent(call, `${ctx.event.id}:execution:error`, {
|
|
1429
1338
|
error: `Tool '${call.toolName}' has no server executor`,
|
|
1430
1339
|
})
|
|
1431
1340
|
}
|
|
1432
|
-
const messages = await promptMessages(
|
|
1433
|
-
ctx.event.sessionId,
|
|
1434
|
-
call,
|
|
1435
|
-
ctx.session.history,
|
|
1436
|
-
)
|
|
1341
|
+
const messages = await promptMessages(input)
|
|
1437
1342
|
if (checkAbort(ctx.signal)) return
|
|
1438
1343
|
const scope: AmbientToolScope = {
|
|
1439
1344
|
contract: options.agent.contract,
|
|
@@ -1446,100 +1351,77 @@ export function createHandlers<
|
|
|
1446
1351
|
)
|
|
1447
1352
|
}
|
|
1448
1353
|
|
|
1449
|
-
const
|
|
1450
|
-
ctx:
|
|
1451
|
-
): Promise<AppendInput<D
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
candidate.approval?.approvalId === ctx.event.payload.approvalId &&
|
|
1483
|
-
candidate.approval.messageId === ctx.event.payload.messageId &&
|
|
1484
|
-
candidate.approval.generationId === ctx.event.payload.generationId &&
|
|
1485
|
-
candidate.responseIndex === ctx.event.index,
|
|
1486
|
-
)
|
|
1487
|
-
if (
|
|
1488
|
-
current === undefined ||
|
|
1489
|
-
response?.generation?.generationId !== current.call.generationId ||
|
|
1490
|
-
response.failure !== undefined ||
|
|
1491
|
-
current.terminal
|
|
1492
|
-
) {
|
|
1493
|
-
return
|
|
1494
|
-
}
|
|
1495
|
-
if (current.call.providerExecuted === true) {
|
|
1496
|
-
await continueIfReady(ctx, current.call.generationId)
|
|
1497
|
-
return
|
|
1498
|
-
}
|
|
1499
|
-
if (!ctx.event.payload.approved) {
|
|
1500
|
-
return resultEvent(current.call, 'execution:denied', { denied: true })
|
|
1354
|
+
const handleToolExecution = async (
|
|
1355
|
+
ctx: ToolHandlerContext,
|
|
1356
|
+
): Promise<AppendInput<D>> => {
|
|
1357
|
+
try {
|
|
1358
|
+
const state = (
|
|
1359
|
+
await ctx.session.state(control.reducer, { through: 'latest' })
|
|
1360
|
+
).state
|
|
1361
|
+
const request = ctx.event.payload
|
|
1362
|
+
const call = state.coordinator.response?.calls.find(
|
|
1363
|
+
(candidate) => candidate.work?.id === ctx.event.id,
|
|
1364
|
+
)
|
|
1365
|
+
if (
|
|
1366
|
+
state.active?.turnId !== request.turnId ||
|
|
1367
|
+
state.active.suspended ||
|
|
1368
|
+
state.active.version !== request.version ||
|
|
1369
|
+
state.coordinator.response?.failure !== undefined ||
|
|
1370
|
+
!call ||
|
|
1371
|
+
call.terminal ||
|
|
1372
|
+
call.work?.settled
|
|
1373
|
+
)
|
|
1374
|
+
return control.settled({ ctx, events: undefined })
|
|
1375
|
+
return control.settled({
|
|
1376
|
+
ctx,
|
|
1377
|
+
events: await executeTool({
|
|
1378
|
+
ctx,
|
|
1379
|
+
call: request.call,
|
|
1380
|
+
generation: request.generation,
|
|
1381
|
+
}),
|
|
1382
|
+
})
|
|
1383
|
+
} catch (error) {
|
|
1384
|
+
if (control.cancelled({ error, signal: ctx.signal }))
|
|
1385
|
+
return control.settled({ ctx, events: undefined })
|
|
1386
|
+
throw error
|
|
1501
1387
|
}
|
|
1502
|
-
return executeTool(ctx, current.call)
|
|
1503
1388
|
}
|
|
1504
1389
|
|
|
1505
1390
|
const generationHandler = async (
|
|
1506
1391
|
ctx: HandlerContext<D, 'ai.generation.requested'>,
|
|
1507
1392
|
): Promise<void | AppendInput<D> | readonly AppendInput<D>[]> => {
|
|
1508
|
-
|
|
1393
|
+
if (checkAbort(ctx.signal)) return
|
|
1509
1394
|
const requestId = ctx.event.id
|
|
1510
1395
|
const request = ctx.event.payload
|
|
1396
|
+
const snapshot = await ctx.session.state(options.agent.reducer, {
|
|
1397
|
+
through: 'latest',
|
|
1398
|
+
})
|
|
1399
|
+
if (
|
|
1400
|
+
snapshot.state.active?.phase === 'paused' ||
|
|
1401
|
+
snapshot.state.active?.phase === 'pausing'
|
|
1402
|
+
)
|
|
1403
|
+
return
|
|
1511
1404
|
const coordinatorState = (
|
|
1512
|
-
await ctx.session.state(coordinator, { through:
|
|
1405
|
+
await ctx.session.state(coordinator, { through: snapshot.index })
|
|
1513
1406
|
).state
|
|
1514
1407
|
if (
|
|
1515
1408
|
coordinatorState.closed ||
|
|
1516
1409
|
coordinatorState.response?.activeRequestId !== requestId
|
|
1517
|
-
) {
|
|
1518
|
-
return
|
|
1519
|
-
}
|
|
1520
|
-
const terminal = history.some((event) => {
|
|
1521
|
-
if (event.type === 'ai.generation.completed') {
|
|
1522
|
-
return (
|
|
1523
|
-
(event.payload as GenerationCompletedPayload).requestId === requestId
|
|
1524
|
-
)
|
|
1525
|
-
}
|
|
1526
|
-
if (event.type !== 'ai.generation.failed') return false
|
|
1527
|
-
const payload = event.payload as GenerationFailedPayload
|
|
1528
|
-
return payload.requestId === requestId && payload.superseded !== true
|
|
1529
|
-
})
|
|
1530
|
-
if (terminal) return
|
|
1531
|
-
|
|
1532
|
-
const previousStarts = history.filter(
|
|
1533
|
-
(event) =>
|
|
1534
|
-
event.type === 'ai.generation.started' &&
|
|
1535
|
-
(event.payload as GenerationStartedPayload).requestId === requestId,
|
|
1536
1410
|
)
|
|
1537
|
-
|
|
1538
|
-
|
|
1411
|
+
return
|
|
1412
|
+
const response = coordinatorState.response
|
|
1413
|
+
const current =
|
|
1414
|
+
response.generation?.requestId === requestId
|
|
1415
|
+
? response.generation
|
|
1416
|
+
: undefined
|
|
1417
|
+
if (
|
|
1418
|
+
current !== undefined &&
|
|
1419
|
+
(response.completion !== undefined ||
|
|
1420
|
+
(response.failure !== undefined &&
|
|
1421
|
+
response.failure.superseded !== true))
|
|
1539
1422
|
)
|
|
1540
|
-
if (priorAttempts.some((priorAttempt) => priorAttempt >= ctx.attempt)) {
|
|
1541
1423
|
return
|
|
1542
|
-
|
|
1424
|
+
if (current !== undefined && current.attempt >= ctx.attempt) return
|
|
1543
1425
|
const attempt = ctx.attempt
|
|
1544
1426
|
const generationId = `${requestId}:generation:${attempt}`
|
|
1545
1427
|
const responseMessageId =
|
|
@@ -1547,65 +1429,55 @@ export function createHandlers<
|
|
|
1547
1429
|
(request.reason === 'message'
|
|
1548
1430
|
? `${request.messageId}:assistant`
|
|
1549
1431
|
: request.messageId)
|
|
1550
|
-
|
|
1551
|
-
const responseStepCount = history.filter(
|
|
1552
|
-
(event) =>
|
|
1553
|
-
event.type === 'ai.generation.completed' &&
|
|
1554
|
-
(event.payload as GenerationCompletedPayload).responseMessageId ===
|
|
1555
|
-
responseMessageId,
|
|
1556
|
-
).length
|
|
1557
|
-
|
|
1558
|
-
const sourceCoordinatorState = coordinatorStateAt(history, ctx.event.index)
|
|
1559
|
-
if (request.reason === 'tool') {
|
|
1560
|
-
const prefix = 'ai.generate:tools:'
|
|
1561
|
-
if (!requestId.startsWith(prefix)) return
|
|
1562
|
-
const sourceGenerationId = requestId.slice(prefix.length)
|
|
1563
|
-
if (
|
|
1564
|
-
!continuationReady(sourceCoordinatorState) ||
|
|
1565
|
-
sourceCoordinatorState.response?.generation?.generationId !==
|
|
1566
|
-
sourceGenerationId ||
|
|
1567
|
-
sourceCoordinatorState.response.responseMessageId !==
|
|
1568
|
-
request.messageId ||
|
|
1569
|
-
sourceCoordinatorState.response.responseMessageId !==
|
|
1570
|
-
request.responseMessageId
|
|
1571
|
-
) {
|
|
1572
|
-
return
|
|
1573
|
-
}
|
|
1574
|
-
}
|
|
1575
|
-
|
|
1576
|
-
const previous = previousStarts
|
|
1577
|
-
.filter(
|
|
1578
|
-
(event) =>
|
|
1579
|
-
(event.payload as GenerationStartedPayload).attempt < attempt,
|
|
1580
|
-
)
|
|
1581
|
-
.toSorted(
|
|
1582
|
-
(left, right) =>
|
|
1583
|
-
(right.payload as GenerationStartedPayload).attempt -
|
|
1584
|
-
(left.payload as GenerationStartedPayload).attempt,
|
|
1585
|
-
)[0]
|
|
1586
|
-
|
|
1587
|
-
const incompleteId = previous
|
|
1588
|
-
? (previous.payload as GenerationStartedPayload).generationId
|
|
1589
|
-
: undefined
|
|
1590
|
-
const replacedGenerationIds = new Set<string>()
|
|
1591
|
-
if (incompleteId !== undefined) replacedGenerationIds.add(incompleteId)
|
|
1432
|
+
const responseStepCount = response.stepCount
|
|
1592
1433
|
if (
|
|
1593
|
-
request.reason === '
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1434
|
+
request.reason === 'tool' &&
|
|
1435
|
+
(requestId !==
|
|
1436
|
+
`ai.generate:tools:${response.source?.generation.generationId}` ||
|
|
1437
|
+
response.responseMessageId !== request.messageId ||
|
|
1438
|
+
response.responseMessageId !== request.responseMessageId)
|
|
1439
|
+
)
|
|
1440
|
+
return
|
|
1441
|
+
const previous = current
|
|
1442
|
+
const replaced =
|
|
1443
|
+
previous === undefined
|
|
1444
|
+
? []
|
|
1445
|
+
: [{ generation: previous, frontier: response.promptThroughIndex! }]
|
|
1446
|
+
if (request.reason === 'retry' && response.source?.failed) {
|
|
1447
|
+
replaced.push({
|
|
1448
|
+
generation: response.source.generation,
|
|
1449
|
+
frontier: response.source.promptThroughIndex,
|
|
1450
|
+
})
|
|
1599
1451
|
}
|
|
1600
|
-
const
|
|
1601
|
-
|
|
1602
|
-
replacedGenerationIds,
|
|
1452
|
+
const replacedGenerationIds = new Set(
|
|
1453
|
+
replaced.map(({ generation: owner }) => owner.generationId),
|
|
1603
1454
|
)
|
|
1604
|
-
|
|
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
|
+
}
|
|
1605
1471
|
const resolverContext: AgentResolverContext<M, D> = {
|
|
1606
1472
|
event: ctx.event,
|
|
1607
1473
|
state,
|
|
1608
|
-
|
|
1474
|
+
session: {
|
|
1475
|
+
state: (reducer, readOptions) =>
|
|
1476
|
+
ctx.session.state(reducer, {
|
|
1477
|
+
...readOptions,
|
|
1478
|
+
through: readOptions?.through ?? snapshot.index,
|
|
1479
|
+
}),
|
|
1480
|
+
},
|
|
1609
1481
|
signal: ctx.signal,
|
|
1610
1482
|
}
|
|
1611
1483
|
|
|
@@ -1624,7 +1496,7 @@ export function createHandlers<
|
|
|
1624
1496
|
const compaction =
|
|
1625
1497
|
typeof configuredCompaction === 'function'
|
|
1626
1498
|
? validateCompaction({
|
|
1627
|
-
compaction: configuredCompaction
|
|
1499
|
+
compaction: await resolve(configuredCompaction, resolverContext),
|
|
1628
1500
|
explicit: true,
|
|
1629
1501
|
generation: options.generation,
|
|
1630
1502
|
tools: options.tools,
|
|
@@ -1633,7 +1505,7 @@ export function createHandlers<
|
|
|
1633
1505
|
if (checkAbort(ctx.signal)) return
|
|
1634
1506
|
|
|
1635
1507
|
if (request.reason === 'tool' && responseStepCount >= maxSteps) {
|
|
1636
|
-
const source =
|
|
1508
|
+
const source = response.source?.generation
|
|
1637
1509
|
if (source === undefined) return
|
|
1638
1510
|
return {
|
|
1639
1511
|
type: 'ai.generation.failed',
|
|
@@ -1656,7 +1528,7 @@ export function createHandlers<
|
|
|
1656
1528
|
responseMessageId,
|
|
1657
1529
|
attempt,
|
|
1658
1530
|
model: modelName(resolvedModel),
|
|
1659
|
-
promptThroughIndex:
|
|
1531
|
+
promptThroughIndex: snapshot.index,
|
|
1660
1532
|
}
|
|
1661
1533
|
const catalogModelId = gatewayModelId(resolvedModel)
|
|
1662
1534
|
const gatewayOptions = options.generation?.providerOptions?.['gateway']
|
|
@@ -1681,7 +1553,7 @@ export function createHandlers<
|
|
|
1681
1553
|
} as AppendInput<D>)
|
|
1682
1554
|
}
|
|
1683
1555
|
if (previous) {
|
|
1684
|
-
const payload = previous
|
|
1556
|
+
const payload = previous
|
|
1685
1557
|
const superseded: GenerationFailedPayload = {
|
|
1686
1558
|
requestId,
|
|
1687
1559
|
messageId: payload.messageId,
|
|
@@ -1701,33 +1573,24 @@ export function createHandlers<
|
|
|
1701
1573
|
id: generationId,
|
|
1702
1574
|
payload: started,
|
|
1703
1575
|
} as AppendInput<D>)
|
|
1704
|
-
const
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1576
|
+
const generationEvents = await control.append({
|
|
1577
|
+
ctx,
|
|
1578
|
+
name: 'generation-start',
|
|
1579
|
+
events: startEvents,
|
|
1580
|
+
})
|
|
1708
1581
|
generationStarted = true
|
|
1709
1582
|
|
|
1710
|
-
|
|
1711
|
-
await ctx.session.state(coordinator, { through: 'latest' })
|
|
1712
|
-
).state
|
|
1713
|
-
if (
|
|
1714
|
-
startedCoordinatorState.response?.activeRequestId !== requestId ||
|
|
1715
|
-
startedCoordinatorState.response.generation?.generationId !==
|
|
1716
|
-
generationId
|
|
1717
|
-
) {
|
|
1718
|
-
return
|
|
1719
|
-
}
|
|
1583
|
+
if (checkAbort(ctx.signal)) return
|
|
1720
1584
|
|
|
1721
|
-
const promptCoordinatorState =
|
|
1722
|
-
|
|
1723
|
-
queued: queuedMessagesAt(history),
|
|
1724
|
-
}
|
|
1725
|
-
const messages = activeContextMessages({
|
|
1585
|
+
const promptCoordinatorState = coordinatorState
|
|
1586
|
+
const messages = await contextMessages({
|
|
1726
1587
|
agent: options.agent,
|
|
1727
|
-
|
|
1728
|
-
|
|
1588
|
+
session: ctx.session,
|
|
1589
|
+
snapshot: { state, index: snapshot.index },
|
|
1729
1590
|
coordinator: promptCoordinatorState,
|
|
1591
|
+
excluded: replacedGenerationIds,
|
|
1730
1592
|
})
|
|
1593
|
+
let generationMessages = messages
|
|
1731
1594
|
let modelMessages = await modelContext({ messages, state, tools })
|
|
1732
1595
|
const baseContext: AgentGenerateContext<M, D, T> = {
|
|
1733
1596
|
request: ctx.event,
|
|
@@ -1737,7 +1600,7 @@ export function createHandlers<
|
|
|
1737
1600
|
messages,
|
|
1738
1601
|
modelMessages,
|
|
1739
1602
|
state,
|
|
1740
|
-
|
|
1603
|
+
session: resolverContext.session,
|
|
1741
1604
|
signal: ctx.signal,
|
|
1742
1605
|
model: resolvedModel,
|
|
1743
1606
|
tools,
|
|
@@ -1761,9 +1624,7 @@ export function createHandlers<
|
|
|
1761
1624
|
: undefined
|
|
1762
1625
|
let inputTokenEstimate: number | undefined
|
|
1763
1626
|
let compacted = false
|
|
1764
|
-
const canCompact =
|
|
1765
|
-
sourceCoordinatorState.response?.calls.every((call) => call.terminal) ??
|
|
1766
|
-
true
|
|
1627
|
+
const canCompact = response.source?.canCompact ?? true
|
|
1767
1628
|
if (policy && canCompact) {
|
|
1768
1629
|
const compactionContext = {
|
|
1769
1630
|
...resolverContext,
|
|
@@ -1782,7 +1643,7 @@ export function createHandlers<
|
|
|
1782
1643
|
const inputTokens = measuredInputTokens({
|
|
1783
1644
|
estimate: inputTokenEstimate,
|
|
1784
1645
|
model: modelName(resolvedModel),
|
|
1785
|
-
|
|
1646
|
+
calibration: coordinatorState.calibration,
|
|
1786
1647
|
})
|
|
1787
1648
|
const threshold =
|
|
1788
1649
|
policy.thresholdTokens ??
|
|
@@ -1816,18 +1677,25 @@ export function createHandlers<
|
|
|
1816
1677
|
(queued) => queued.messageId === message.id,
|
|
1817
1678
|
),
|
|
1818
1679
|
)?.id ?? request.messageId
|
|
1819
|
-
const throughIndex =
|
|
1820
|
-
|
|
1821
|
-
...(await
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1680
|
+
const throughIndex = snapshot.index
|
|
1681
|
+
generationEvents.push(
|
|
1682
|
+
...(await control.append({
|
|
1683
|
+
ctx,
|
|
1684
|
+
name: 'compaction-requested',
|
|
1685
|
+
events: [
|
|
1686
|
+
{
|
|
1687
|
+
type: 'ai.compaction.requested',
|
|
1688
|
+
id: `${generationId}:compaction:requested`,
|
|
1689
|
+
payload: { generationId, throughMessageId, throughIndex },
|
|
1690
|
+
} as AppendInput<D>,
|
|
1691
|
+
],
|
|
1825
1692
|
})),
|
|
1826
1693
|
)
|
|
1827
1694
|
const result = !('shouldCompact' in policy)
|
|
1828
1695
|
? {
|
|
1829
1696
|
messages: [] as M[],
|
|
1830
1697
|
...(await summarize({
|
|
1698
|
+
agentName: options.agent.contract.name,
|
|
1831
1699
|
context: baseContext,
|
|
1832
1700
|
instructions: policy.instructions,
|
|
1833
1701
|
})),
|
|
@@ -1847,30 +1715,55 @@ export function createHandlers<
|
|
|
1847
1715
|
),
|
|
1848
1716
|
}),
|
|
1849
1717
|
}
|
|
1850
|
-
|
|
1851
|
-
...(await
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1718
|
+
generationEvents.push(
|
|
1719
|
+
...(await control.append({
|
|
1720
|
+
ctx,
|
|
1721
|
+
name: 'compaction-completed',
|
|
1722
|
+
events: [
|
|
1723
|
+
{
|
|
1724
|
+
type: 'ai.compaction.completed',
|
|
1725
|
+
id: `${generationId}:compaction:completed`,
|
|
1726
|
+
payload: completed,
|
|
1727
|
+
} as AppendInput<D>,
|
|
1728
|
+
],
|
|
1855
1729
|
})),
|
|
1856
1730
|
)
|
|
1731
|
+
const queued = new Set(
|
|
1732
|
+
promptCoordinatorState.queued.map((item) => item.messageId),
|
|
1733
|
+
)
|
|
1734
|
+
generationMessages = result.messages.filter(
|
|
1735
|
+
(message) => !queued.has(message.id),
|
|
1736
|
+
)
|
|
1857
1737
|
compacted = true
|
|
1858
1738
|
}
|
|
1859
1739
|
}
|
|
1860
1740
|
|
|
1861
|
-
const currentState =
|
|
1862
|
-
const currentCoordinatorState = coordinatorStateAt(generationHistory)
|
|
1863
|
-
const generationMessages = activeContextMessages({
|
|
1741
|
+
const currentState = foldAIEvents({
|
|
1864
1742
|
agent: options.agent,
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
coordinator: currentCoordinatorState,
|
|
1868
|
-
})
|
|
1869
|
-
modelMessages = await modelContext({
|
|
1870
|
-
messages: generationMessages,
|
|
1871
|
-
state: currentState,
|
|
1872
|
-
tools,
|
|
1743
|
+
events: generationEvents,
|
|
1744
|
+
state: snapshot.state,
|
|
1873
1745
|
})
|
|
1746
|
+
if (request.reason === 'retry' || previous !== undefined) {
|
|
1747
|
+
generationMessages = await contextMessages({
|
|
1748
|
+
agent: options.agent,
|
|
1749
|
+
session: ctx.session,
|
|
1750
|
+
snapshot,
|
|
1751
|
+
coordinator: promptCoordinatorState,
|
|
1752
|
+
appended: generationEvents,
|
|
1753
|
+
})
|
|
1754
|
+
}
|
|
1755
|
+
if (
|
|
1756
|
+
(policy && 'shouldCompact' in policy) ||
|
|
1757
|
+
generationMessages !== messages ||
|
|
1758
|
+
activeCompaction(currentState)?.summary !==
|
|
1759
|
+
activeCompaction(state)?.summary
|
|
1760
|
+
) {
|
|
1761
|
+
modelMessages = await modelContext({
|
|
1762
|
+
messages: generationMessages,
|
|
1763
|
+
state: currentState,
|
|
1764
|
+
tools,
|
|
1765
|
+
})
|
|
1766
|
+
}
|
|
1874
1767
|
promptCache.set(
|
|
1875
1768
|
promptCacheKey(ctx.event.sessionId, generationId),
|
|
1876
1769
|
Promise.resolve(modelMessages),
|
|
@@ -1893,7 +1786,7 @@ export function createHandlers<
|
|
|
1893
1786
|
messages: generationMessages,
|
|
1894
1787
|
modelMessages,
|
|
1895
1788
|
state: currentState,
|
|
1896
|
-
|
|
1789
|
+
session: resolverContext.session,
|
|
1897
1790
|
signal: ctx.signal,
|
|
1898
1791
|
model: resolvedModel,
|
|
1899
1792
|
tools,
|
|
@@ -1905,7 +1798,11 @@ export function createHandlers<
|
|
|
1905
1798
|
const custom = options.generate !== undefined
|
|
1906
1799
|
const source =
|
|
1907
1800
|
options.generate === undefined
|
|
1908
|
-
? await generateWithAISDK(
|
|
1801
|
+
? await generateWithAISDK({
|
|
1802
|
+
agentName: options.agent.contract.name,
|
|
1803
|
+
context: generateContext,
|
|
1804
|
+
messageMetadata: options.messageMetadata,
|
|
1805
|
+
})
|
|
1909
1806
|
: { stream: await options.generate(generateContext) }
|
|
1910
1807
|
const updates = consumeGeneration({
|
|
1911
1808
|
source,
|
|
@@ -1942,15 +1839,18 @@ export function createHandlers<
|
|
|
1942
1839
|
custom,
|
|
1943
1840
|
})
|
|
1944
1841
|
pendingToolCalls = lifecycle.pending
|
|
1945
|
-
await
|
|
1946
|
-
|
|
1947
|
-
{
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1842
|
+
await control.append({
|
|
1843
|
+
ctx,
|
|
1844
|
+
name: `generation-progress:${sequence}`,
|
|
1845
|
+
events: [
|
|
1846
|
+
{
|
|
1847
|
+
type: 'ai.generation.progress',
|
|
1848
|
+
id: `${generationId}:progress:${sequence}`,
|
|
1849
|
+
payload: progress,
|
|
1850
|
+
} as AppendInput<D>,
|
|
1851
|
+
...lifecycle.events,
|
|
1852
|
+
],
|
|
1853
|
+
})
|
|
1954
1854
|
sequence += 1
|
|
1955
1855
|
}
|
|
1956
1856
|
|
|
@@ -2004,22 +1904,12 @@ export function createHandlers<
|
|
|
2004
1904
|
} as AppendInput<D>,
|
|
2005
1905
|
]
|
|
2006
1906
|
} catch (error) {
|
|
1907
|
+
if (error instanceof ControlCancelled) throw error
|
|
2007
1908
|
if (error instanceof A2Error) {
|
|
2008
1909
|
checkAbort(ctx.signal)
|
|
2009
1910
|
throw error
|
|
2010
1911
|
}
|
|
2011
|
-
if (checkAbort(ctx.signal))
|
|
2012
|
-
const interrupted: MessageInterruptedPayload = {
|
|
2013
|
-
messageId: responseMessageId,
|
|
2014
|
-
generationId,
|
|
2015
|
-
reason: 'aborted',
|
|
2016
|
-
}
|
|
2017
|
-
return {
|
|
2018
|
-
type: 'ai.message.interrupted',
|
|
2019
|
-
id: `${generationId}:interrupted`,
|
|
2020
|
-
payload: interrupted,
|
|
2021
|
-
} as AppendInput<D>
|
|
2022
|
-
}
|
|
1912
|
+
if (checkAbort(ctx.signal)) return
|
|
2023
1913
|
if (!generationStarted) throw error
|
|
2024
1914
|
const failed: GenerationFailedPayload = {
|
|
2025
1915
|
requestId,
|
|
@@ -2036,59 +1926,6 @@ export function createHandlers<
|
|
|
2036
1926
|
}
|
|
2037
1927
|
}
|
|
2038
1928
|
|
|
2039
|
-
const handleMessageCreated = async (
|
|
2040
|
-
ctx: HandlerContext<D, 'ai.message.created'>,
|
|
2041
|
-
): Promise<AppendInput<D> | void> => {
|
|
2042
|
-
if (
|
|
2043
|
-
ctx.event.payload.message.role !== 'user' ||
|
|
2044
|
-
ctx.event.payload.generate === false
|
|
2045
|
-
) {
|
|
2046
|
-
return
|
|
2047
|
-
}
|
|
2048
|
-
return scheduleNext(ctx)
|
|
2049
|
-
}
|
|
2050
|
-
|
|
2051
|
-
const handleRetry = async (
|
|
2052
|
-
ctx: HandlerContext<D, 'ai.retry.requested'>,
|
|
2053
|
-
): Promise<AppendInput<D> | void> => {
|
|
2054
|
-
const state = (await ctx.session.state(coordinator, { through: 'latest' }))
|
|
2055
|
-
.state
|
|
2056
|
-
const response = state.response
|
|
2057
|
-
if (
|
|
2058
|
-
response?.status !== 'failed' ||
|
|
2059
|
-
response.rootMessageId !== ctx.event.payload.messageId ||
|
|
2060
|
-
response.responseMessageId !== ctx.event.payload.responseMessageId
|
|
2061
|
-
) {
|
|
2062
|
-
return
|
|
2063
|
-
}
|
|
2064
|
-
return {
|
|
2065
|
-
type: 'ai.generation.requested',
|
|
2066
|
-
id: `ai.generate:retry:${ctx.event.payload.retryId}`,
|
|
2067
|
-
payload: {
|
|
2068
|
-
messageId: response.rootMessageId,
|
|
2069
|
-
responseMessageId: response.responseMessageId,
|
|
2070
|
-
reason: 'retry',
|
|
2071
|
-
},
|
|
2072
|
-
} as AppendInput<D>
|
|
2073
|
-
}
|
|
2074
|
-
|
|
2075
|
-
const handleInputResponse = async (
|
|
2076
|
-
ctx: HandlerContext<D, 'ai.input.responded'>,
|
|
2077
|
-
): Promise<void> => {
|
|
2078
|
-
const state = (await ctx.session.state(coordinator, { through: 'latest' }))
|
|
2079
|
-
.state
|
|
2080
|
-
const response = state.response
|
|
2081
|
-
if (
|
|
2082
|
-
response?.responseMessageId !== ctx.event.payload.messageId ||
|
|
2083
|
-
response.inputResponse?.index !== ctx.event.index ||
|
|
2084
|
-
response.inputResponse.generationId !== ctx.event.payload.generationId ||
|
|
2085
|
-
response.inputResponse.inputId !== ctx.event.payload.inputId
|
|
2086
|
-
) {
|
|
2087
|
-
return
|
|
2088
|
-
}
|
|
2089
|
-
await continueIfReady(ctx, ctx.event.payload.generationId)
|
|
2090
|
-
}
|
|
2091
|
-
|
|
2092
1929
|
const clearPromptCache = (sessionId: string): void => {
|
|
2093
1930
|
const prefix = `${sessionId}\u001f`
|
|
2094
1931
|
for (const key of promptCache.keys()) {
|
|
@@ -2096,13 +1933,6 @@ export function createHandlers<
|
|
|
2096
1933
|
}
|
|
2097
1934
|
}
|
|
2098
1935
|
|
|
2099
|
-
const handleResponseEnded = async (
|
|
2100
|
-
ctx: HandlerContext<D, 'ai.message.completed' | 'ai.message.interrupted'>,
|
|
2101
|
-
): Promise<AppendInput<D> | void> => {
|
|
2102
|
-
clearPromptCache(ctx.event.sessionId)
|
|
2103
|
-
return scheduleNext(ctx)
|
|
2104
|
-
}
|
|
2105
|
-
|
|
2106
1936
|
return {
|
|
2107
1937
|
'ai.model.metadata.requested': {
|
|
2108
1938
|
handler: async (ctx) => {
|
|
@@ -2118,24 +1948,23 @@ export function createHandlers<
|
|
|
2118
1948
|
} as AppendInput<D>
|
|
2119
1949
|
},
|
|
2120
1950
|
},
|
|
2121
|
-
'ai.
|
|
2122
|
-
'ai.
|
|
2123
|
-
'ai.input.responded': {
|
|
2124
|
-
lane: 'a2.ai.turn',
|
|
2125
|
-
handler: handleInputResponse,
|
|
2126
|
-
},
|
|
1951
|
+
'ai.control.requested': { lane: 'a2.ai.control', handler: control.handler },
|
|
1952
|
+
'ai.work.reported': { lane: 'a2.ai.control', handler: control.handler },
|
|
2127
1953
|
'ai.message.completed': {
|
|
2128
|
-
|
|
2129
|
-
|
|
1954
|
+
handler: async (ctx) => {
|
|
1955
|
+
clearPromptCache(ctx.event.sessionId)
|
|
1956
|
+
},
|
|
2130
1957
|
},
|
|
2131
1958
|
'ai.message.interrupted': {
|
|
2132
|
-
|
|
2133
|
-
|
|
1959
|
+
handler: async (ctx) => {
|
|
1960
|
+
clearPromptCache(ctx.event.sessionId)
|
|
1961
|
+
},
|
|
2134
1962
|
},
|
|
2135
1963
|
'ai.session.closed': {
|
|
2136
|
-
|
|
1964
|
+
lane: 'a2.ai.control',
|
|
1965
|
+
handler: async (ctx) => {
|
|
2137
1966
|
clearPromptCache(ctx.event.sessionId)
|
|
2138
|
-
return
|
|
1967
|
+
return control.handler(ctx)
|
|
2139
1968
|
},
|
|
2140
1969
|
},
|
|
2141
1970
|
'ai.generation.failed': {
|
|
@@ -2147,60 +1976,41 @@ export function createHandlers<
|
|
|
2147
1976
|
},
|
|
2148
1977
|
},
|
|
2149
1978
|
'ai.generation.requested': {
|
|
2150
|
-
lane: 'a2.ai.
|
|
1979
|
+
lane: 'a2.ai.model',
|
|
2151
1980
|
abortOn: {
|
|
2152
|
-
'ai.
|
|
2153
|
-
const
|
|
2154
|
-
trigger.payload.responseMessageId ??
|
|
2155
|
-
(trigger.payload.reason === 'message'
|
|
2156
|
-
? `${trigger.payload.messageId}:assistant`
|
|
2157
|
-
: trigger.payload.messageId)
|
|
1981
|
+
'ai.control.committed': (event, trigger) => {
|
|
1982
|
+
const active = (event.payload as ControlCommit).view
|
|
2158
1983
|
return (
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
event.payload.generationId ===
|
|
2162
|
-
`${trigger.id}:generation:${context.attempt}`)
|
|
1984
|
+
active?.turnId !== trigger.payload.control?.turnId ||
|
|
1985
|
+
active?.version !== trigger.payload.control?.version
|
|
2163
1986
|
)
|
|
2164
1987
|
},
|
|
2165
1988
|
'ai.session.closed': true,
|
|
2166
1989
|
},
|
|
2167
|
-
handler:
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
'ai.generation.failed': (event, trigger) =>
|
|
2176
|
-
event.payload.generationId === trigger.payload.generationId,
|
|
2177
|
-
'ai.message.interrupted': (event, trigger) =>
|
|
2178
|
-
event.payload.messageId === trigger.payload.messageId &&
|
|
2179
|
-
(event.payload.requestId === trigger.payload.requestId ||
|
|
2180
|
-
event.payload.generationId === trigger.payload.generationId),
|
|
2181
|
-
'ai.session.closed': true,
|
|
1990
|
+
handler: async (ctx) => {
|
|
1991
|
+
try {
|
|
1992
|
+
return control.settled({ ctx, events: await generationHandler(ctx) })
|
|
1993
|
+
} catch (error) {
|
|
1994
|
+
if (control.cancelled({ error, signal: ctx.signal }))
|
|
1995
|
+
return control.settled({ ctx, events: undefined })
|
|
1996
|
+
throw error
|
|
1997
|
+
}
|
|
2182
1998
|
},
|
|
2183
|
-
handler: handleToolCall,
|
|
2184
1999
|
},
|
|
2185
|
-
'ai.
|
|
2000
|
+
'ai.tool.execution.requested': {
|
|
2186
2001
|
abortOn: {
|
|
2187
2002
|
'ai.generation.failed': (event, trigger) =>
|
|
2188
|
-
event.payload.
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2003
|
+
event.payload.generationId === trigger.payload.call.generationId,
|
|
2004
|
+
'ai.control.committed': (event, trigger) => {
|
|
2005
|
+
const active = (event.payload as ControlCommit).view
|
|
2006
|
+
return (
|
|
2007
|
+
active?.turnId !== trigger.payload.turnId ||
|
|
2008
|
+
active?.version !== trigger.payload.version
|
|
2009
|
+
)
|
|
2010
|
+
},
|
|
2195
2011
|
'ai.session.closed': true,
|
|
2196
2012
|
},
|
|
2197
|
-
handler:
|
|
2198
|
-
},
|
|
2199
|
-
'ai.tool.result': {
|
|
2200
|
-
handler: async (ctx) => {
|
|
2201
|
-
if (ctx.event.payload.preliminary === true) return
|
|
2202
|
-
await continueIfReady(ctx, ctx.event.payload.generationId)
|
|
2203
|
-
},
|
|
2013
|
+
handler: handleToolExecution,
|
|
2204
2014
|
},
|
|
2205
2015
|
} as NonNullable<ServerOptions<D>['handlers']>
|
|
2206
2016
|
}
|