experimental-a2 0.12.0 → 0.14.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 +47 -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 +16 -8
- package/dist/ai-server.d.ts.map +1 -1
- package/dist/ai-server.js +1322 -475
- package/dist/ai-server.js.map +1 -1
- package/dist/ai.d.ts +2 -334
- package/dist/ai.js +838 -85
- 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 +361 -76
- package/docs/reference/01-api.mdx +159 -27
- package/examples/playground/app/agent/[agentId]/agent-client.tsx +145 -33
- package/examples/playground/app/agent/[agentId]/agent-queue.tsx +289 -0
- package/examples/playground/app/agent/[agentId]/compaction/route.ts +14 -0
- package/examples/playground/app/agent/[agentId]/compaction-event.tsx +38 -0
- package/examples/playground/app/agent/[agentId]/compaction-panel.tsx +294 -0
- package/examples/playground/app/agent/compaction-settings.test.ts +144 -0
- package/examples/playground/app/agent/compaction-settings.ts +49 -0
- package/examples/playground/app/agent/compaction-timeline.test.ts +337 -0
- package/examples/playground/app/agent/compaction-timeline.ts +198 -0
- package/examples/playground/app/agent/model.ts +56 -1
- package/examples/playground/app/agent/server.ts +9 -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 +333 -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-model-metadata.ts +108 -0
- package/src/ai-progress-batches.ts +68 -0
- package/src/ai-projector.ts +76 -15
- package/src/ai-sdk-step.ts +5 -2
- package/src/ai-server.ts +920 -638
- package/src/ai.ts +650 -110
- 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
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import type { UIMessage } from 'ai'
|
|
2
|
+
import type { AIState } from 'experimental-a2/ai'
|
|
3
|
+
import type { AgentEvent } from './model'
|
|
4
|
+
|
|
5
|
+
type Generation = Extract<AgentEvent, { type: 'ai.generation.started' }>
|
|
6
|
+
type CompactionEvent = Extract<
|
|
7
|
+
AgentEvent,
|
|
8
|
+
{ type: 'ai.compaction.requested' | 'ai.compaction.completed' }
|
|
9
|
+
>
|
|
10
|
+
|
|
11
|
+
export type CompactionEntry = {
|
|
12
|
+
type: 'compaction'
|
|
13
|
+
id: string
|
|
14
|
+
event: CompactionEvent
|
|
15
|
+
running: boolean
|
|
16
|
+
outcome: 'failed' | 'interrupted' | 'superseded' | undefined
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export type TimelineEntry =
|
|
20
|
+
| { type: 'message'; id: string; message: UIMessage; lastSegment: boolean }
|
|
21
|
+
| CompactionEntry
|
|
22
|
+
|
|
23
|
+
export function compactionTimeline({
|
|
24
|
+
state,
|
|
25
|
+
events,
|
|
26
|
+
}: {
|
|
27
|
+
state: AIState
|
|
28
|
+
events: readonly AgentEvent[]
|
|
29
|
+
}): TimelineEntry[] {
|
|
30
|
+
const generations = new Map<string, Generation>()
|
|
31
|
+
const requests = new Map<string, string>()
|
|
32
|
+
const stepCounts = new Map<string, number>()
|
|
33
|
+
const contentGenerations = new Set<string>()
|
|
34
|
+
const compactions = new Map<string, CompactionEvent>()
|
|
35
|
+
const roots = new Map<string, string>()
|
|
36
|
+
for (const event of events) {
|
|
37
|
+
switch (event.type) {
|
|
38
|
+
case 'ai.generation.requested':
|
|
39
|
+
requests.set(event.id, event.payload.reason)
|
|
40
|
+
break
|
|
41
|
+
case 'ai.generation.started': {
|
|
42
|
+
const { generationId, messageId, responseMessageId } = event.payload
|
|
43
|
+
generations.set(generationId, event)
|
|
44
|
+
if (messageId !== responseMessageId)
|
|
45
|
+
roots.set(responseMessageId, messageId)
|
|
46
|
+
break
|
|
47
|
+
}
|
|
48
|
+
case 'ai.generation.progress': {
|
|
49
|
+
const id = event.payload.generationId
|
|
50
|
+
const count = event.payload.chunks.filter(
|
|
51
|
+
(chunk) => chunk.type === 'start-step',
|
|
52
|
+
).length
|
|
53
|
+
stepCounts.set(id, (stepCounts.get(id) ?? 0) + count)
|
|
54
|
+
if (
|
|
55
|
+
event.payload.chunks.some(
|
|
56
|
+
(chunk) =>
|
|
57
|
+
![
|
|
58
|
+
'start',
|
|
59
|
+
'finish',
|
|
60
|
+
'start-step',
|
|
61
|
+
'finish-step',
|
|
62
|
+
'message-metadata',
|
|
63
|
+
'abort',
|
|
64
|
+
'error',
|
|
65
|
+
].includes(chunk.type),
|
|
66
|
+
)
|
|
67
|
+
)
|
|
68
|
+
contentGenerations.add(id)
|
|
69
|
+
break
|
|
70
|
+
}
|
|
71
|
+
case 'ai.compaction.requested':
|
|
72
|
+
case 'ai.compaction.completed':
|
|
73
|
+
compactions.set(event.payload.generationId, event)
|
|
74
|
+
break
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const contributions = new Map<string, Generation[]>()
|
|
79
|
+
for (const generation of generations.values()) {
|
|
80
|
+
const { generationId, responseMessageId, requestId } = generation.payload
|
|
81
|
+
const ownerId = state.responseGenerationIds[responseMessageId]
|
|
82
|
+
const owner = ownerId === undefined ? undefined : generations.get(ownerId)
|
|
83
|
+
if (
|
|
84
|
+
owner === undefined ||
|
|
85
|
+
generation.index > owner.index ||
|
|
86
|
+
state.terminalGenerations?.[generationId] === 'superseded'
|
|
87
|
+
)
|
|
88
|
+
continue
|
|
89
|
+
let steps = contributions.get(responseMessageId) ?? []
|
|
90
|
+
if (requests.get(requestId) === 'retry') steps = []
|
|
91
|
+
if (steps.at(-1)?.payload.requestId === requestId) steps.pop()
|
|
92
|
+
steps.push(generation)
|
|
93
|
+
contributions.set(responseMessageId, steps)
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const included = new Set(
|
|
97
|
+
[...contributions.values()].flatMap((steps) =>
|
|
98
|
+
steps.map((step) => step.payload.generationId),
|
|
99
|
+
),
|
|
100
|
+
)
|
|
101
|
+
const offsets = new Map<string, number>()
|
|
102
|
+
const counts = new Map<string, number>()
|
|
103
|
+
for (const { payload } of generations.values()) {
|
|
104
|
+
const count = counts.get(payload.responseMessageId) ?? 0
|
|
105
|
+
offsets.set(payload.generationId, count)
|
|
106
|
+
if (included.has(payload.generationId))
|
|
107
|
+
counts.set(
|
|
108
|
+
payload.responseMessageId,
|
|
109
|
+
count + (stepCounts.get(payload.generationId) ?? 0),
|
|
110
|
+
)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const markers = new Map<string, CompactionEntry[]>()
|
|
114
|
+
const orphaned: CompactionEntry[] = []
|
|
115
|
+
const messageIds = new Set(state.messages.map((message) => message.id))
|
|
116
|
+
for (const [generationId, event] of compactions) {
|
|
117
|
+
const terminal = state.terminalGenerations?.[generationId]
|
|
118
|
+
const entry: CompactionEntry = {
|
|
119
|
+
type: 'compaction',
|
|
120
|
+
id: `compaction:${generationId}`,
|
|
121
|
+
event,
|
|
122
|
+
running:
|
|
123
|
+
state.compaction?.generationId === generationId &&
|
|
124
|
+
state.compaction.status === 'running',
|
|
125
|
+
outcome: terminal === 'completed' ? undefined : terminal,
|
|
126
|
+
}
|
|
127
|
+
const responseId = generations.get(generationId)?.payload.responseMessageId
|
|
128
|
+
const anchor =
|
|
129
|
+
responseId !== undefined && messageIds.has(responseId)
|
|
130
|
+
? responseId
|
|
131
|
+
: responseId === undefined
|
|
132
|
+
? undefined
|
|
133
|
+
: roots.get(responseId)
|
|
134
|
+
if (anchor === undefined || !messageIds.has(anchor)) orphaned.push(entry)
|
|
135
|
+
else {
|
|
136
|
+
const entries = markers.get(anchor) ?? []
|
|
137
|
+
entries.push(entry)
|
|
138
|
+
markers.set(anchor, entries)
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const timeline: TimelineEntry[] = []
|
|
143
|
+
for (const message of state.messages) {
|
|
144
|
+
const entries = markers.get(message.id) ?? []
|
|
145
|
+
if (entries.length === 0) {
|
|
146
|
+
timeline.push({
|
|
147
|
+
type: 'message',
|
|
148
|
+
id: `${message.id}:0`,
|
|
149
|
+
message,
|
|
150
|
+
lastSegment: true,
|
|
151
|
+
})
|
|
152
|
+
continue
|
|
153
|
+
}
|
|
154
|
+
const steps = contributions.get(message.id) ?? []
|
|
155
|
+
const boundaries = message.parts.flatMap((part, index) =>
|
|
156
|
+
part.type === 'step-start' ? [index] : [],
|
|
157
|
+
)
|
|
158
|
+
const mapped =
|
|
159
|
+
boundaries[0] === 0 &&
|
|
160
|
+
boundaries.length === counts.get(message.id) &&
|
|
161
|
+
steps.every(
|
|
162
|
+
({ payload }) =>
|
|
163
|
+
!contentGenerations.has(payload.generationId) ||
|
|
164
|
+
(stepCounts.get(payload.generationId) ?? 0) > 0,
|
|
165
|
+
)
|
|
166
|
+
let offset = 0
|
|
167
|
+
const appendMessage = (end: number): void => {
|
|
168
|
+
if (end <= offset) return
|
|
169
|
+
timeline.push({
|
|
170
|
+
type: 'message',
|
|
171
|
+
id: `${message.id}:${offset}`,
|
|
172
|
+
message: { ...message, parts: message.parts.slice(offset, end) },
|
|
173
|
+
lastSegment: end === message.parts.length,
|
|
174
|
+
})
|
|
175
|
+
offset = end
|
|
176
|
+
}
|
|
177
|
+
for (const entry of entries) {
|
|
178
|
+
if (message.role === 'user') {
|
|
179
|
+
appendMessage(message.parts.length)
|
|
180
|
+
} else if (mapped) {
|
|
181
|
+
const stepIndex = offsets.get(entry.event.payload.generationId) ?? 0
|
|
182
|
+
appendMessage(boundaries[stepIndex] ?? message.parts.length)
|
|
183
|
+
}
|
|
184
|
+
timeline.push(entry)
|
|
185
|
+
}
|
|
186
|
+
if (message.parts.length === 0) {
|
|
187
|
+
timeline.push({
|
|
188
|
+
type: 'message',
|
|
189
|
+
id: message.id,
|
|
190
|
+
message,
|
|
191
|
+
lastSegment: true,
|
|
192
|
+
})
|
|
193
|
+
} else {
|
|
194
|
+
appendMessage(message.parts.length)
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
return [...timeline, ...orphaned]
|
|
198
|
+
}
|
|
@@ -1,3 +1,58 @@
|
|
|
1
1
|
import { agent } from 'experimental-a2/ai'
|
|
2
|
+
import type { ContractEvent } from 'experimental-a2'
|
|
3
|
+
import type { AutomaticCompaction } from 'experimental-a2/ai/server'
|
|
4
|
+
import { z } from 'zod'
|
|
2
5
|
|
|
3
|
-
export const
|
|
6
|
+
export const agentModel = 'openai/gpt-5.6-luna'
|
|
7
|
+
|
|
8
|
+
export const compactionSettingsSchema = z.discriminatedUnion('mode', [
|
|
9
|
+
z.strictObject({ mode: z.literal('automatic') }),
|
|
10
|
+
z.strictObject({
|
|
11
|
+
mode: z.literal('manual'),
|
|
12
|
+
thresholdTokens: z.number().int().min(1).max(Number.MAX_SAFE_INTEGER),
|
|
13
|
+
}),
|
|
14
|
+
z.strictObject({ mode: z.literal('disabled') }),
|
|
15
|
+
])
|
|
16
|
+
|
|
17
|
+
export type CompactionSettings = z.infer<typeof compactionSettingsSchema>
|
|
18
|
+
|
|
19
|
+
export const demoAgent = agent({
|
|
20
|
+
name: 'agent',
|
|
21
|
+
events: { 'compaction.settings.updated': compactionSettingsSchema },
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
export type AgentEvent = ContractEvent<typeof demoAgent.contract.events>
|
|
25
|
+
|
|
26
|
+
const automatic: CompactionSettings = { mode: 'automatic' }
|
|
27
|
+
|
|
28
|
+
export function latestCompactionSettings(history: readonly AgentEvent[]): {
|
|
29
|
+
settings: CompactionSettings
|
|
30
|
+
id: string
|
|
31
|
+
} {
|
|
32
|
+
const event = history.findLast(
|
|
33
|
+
(candidate) => candidate.type === 'compaction.settings.updated',
|
|
34
|
+
)
|
|
35
|
+
return event
|
|
36
|
+
? { settings: event.payload, id: event.id }
|
|
37
|
+
: { settings: automatic, id: 'default' }
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export const compactionSettings = demoAgent.contract
|
|
41
|
+
.reducer({
|
|
42
|
+
name: 'agent.compaction-settings.v1',
|
|
43
|
+
initialState: automatic,
|
|
44
|
+
stateSchema: compactionSettingsSchema,
|
|
45
|
+
})
|
|
46
|
+
.fold((state, event): CompactionSettings =>
|
|
47
|
+
event.type === 'compaction.settings.updated' ? event.payload : state,
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
export function compactionOptions(
|
|
51
|
+
settings: CompactionSettings,
|
|
52
|
+
): AutomaticCompaction | false {
|
|
53
|
+
return settings.mode === 'disabled'
|
|
54
|
+
? false
|
|
55
|
+
: settings.mode === 'manual'
|
|
56
|
+
? { thresholdTokens: settings.thresholdTokens }
|
|
57
|
+
: {}
|
|
58
|
+
}
|
|
@@ -8,7 +8,12 @@ import { createAgentServer } from 'experimental-a2/ai/server'
|
|
|
8
8
|
import { vercelQueues } from 'experimental-a2/scheduler-vercel'
|
|
9
9
|
import { z } from 'zod'
|
|
10
10
|
import { store } from '@/lib/store'
|
|
11
|
-
import {
|
|
11
|
+
import {
|
|
12
|
+
agentModel,
|
|
13
|
+
compactionOptions,
|
|
14
|
+
compactionSettings,
|
|
15
|
+
demoAgent,
|
|
16
|
+
} from './model'
|
|
12
17
|
|
|
13
18
|
const maxReminderDelaySeconds = 6 * 24 * 60 * 60
|
|
14
19
|
const bashTimeoutMs = 120_000
|
|
@@ -131,7 +136,9 @@ export const agentServer = createAgentServer({
|
|
|
131
136
|
...(store ? { store } : {}),
|
|
132
137
|
...(agentScheduler ? { scheduler: agentScheduler } : {}),
|
|
133
138
|
agent: demoAgent,
|
|
134
|
-
model:
|
|
139
|
+
model: agentModel,
|
|
140
|
+
compaction: async ({ session }) =>
|
|
141
|
+
compactionOptions((await session.state(compactionSettings)).state),
|
|
135
142
|
tools,
|
|
136
143
|
instructions: `You are an incident-response agent for the playground.
|
|
137
144
|
When responding to an incident, inspect checkout-api before taking action. Its
|
|
@@ -539,22 +539,12 @@ export function ChatClient({ chatId }: { chatId: string }): ReactNode {
|
|
|
539
539
|
}
|
|
540
540
|
|
|
541
541
|
const stopAgent = async (): Promise<void> => {
|
|
542
|
-
if (!
|
|
543
|
-
const target = state.ai.activeGeneration
|
|
544
|
-
? { generationId: state.ai.activeGeneration.generationId }
|
|
545
|
-
: runningTool
|
|
546
|
-
? { generationId: runningTool.generationId }
|
|
547
|
-
: state.ai.activeRequestId
|
|
548
|
-
? { requestId: state.ai.activeRequestId }
|
|
549
|
-
: null
|
|
550
|
-
if (target === null) return
|
|
542
|
+
if (!state.ai.active) return
|
|
551
543
|
setError(null)
|
|
552
544
|
try {
|
|
553
545
|
await push(
|
|
554
|
-
...inputs.
|
|
555
|
-
|
|
556
|
-
...target,
|
|
557
|
-
reason: 'a chat participant stopped the agent',
|
|
546
|
+
...inputs.stop({
|
|
547
|
+
turnId: state.ai.active.turnId,
|
|
558
548
|
lastSeenIndex: index,
|
|
559
549
|
}),
|
|
560
550
|
)
|
|
@@ -164,7 +164,7 @@ export const chatAgent = agent<ChatAgentMessage, typeof chatEvents>({
|
|
|
164
164
|
name: 'chat',
|
|
165
165
|
events: chatEvents,
|
|
166
166
|
messageSchema: chatMessageSchema,
|
|
167
|
-
reducerName: 'a2.playground.chat.ai.
|
|
167
|
+
reducerName: 'a2.playground.chat.ai.v3',
|
|
168
168
|
})
|
|
169
169
|
|
|
170
170
|
export const chat = chatAgent.contract
|
|
@@ -208,7 +208,7 @@ const initialChatState: ChatState = {
|
|
|
208
208
|
|
|
209
209
|
export const chatReducer = chat
|
|
210
210
|
.reducer({
|
|
211
|
-
name: 'a2.playground.chat.
|
|
211
|
+
name: 'a2.playground.chat.v3',
|
|
212
212
|
initialState: initialChatState,
|
|
213
213
|
})
|
|
214
214
|
.fold((state, event) => {
|
|
@@ -21,16 +21,13 @@ const tools = {
|
|
|
21
21
|
inputSchema: chatPostInputSchema,
|
|
22
22
|
execute: async ({ message }, { toolCallId }) => {
|
|
23
23
|
const context = handlerContext(chatAgent)
|
|
24
|
-
if (context.event.type !== 'ai.tool.called') {
|
|
25
|
-
throw new Error('The chat post tool must execute automatically')
|
|
26
|
-
}
|
|
27
24
|
context.signal.throwIfAborted()
|
|
28
25
|
await context.session.append('post', {
|
|
29
26
|
type: 'agent.message.posted',
|
|
30
27
|
payload: {
|
|
31
28
|
message,
|
|
32
|
-
generationId: context.event.payload.generationId,
|
|
33
|
-
responseMessageId: context.event.payload.messageId,
|
|
29
|
+
generationId: context.event.payload.call.generationId,
|
|
30
|
+
responseMessageId: context.event.payload.call.messageId,
|
|
34
31
|
toolCallId,
|
|
35
32
|
},
|
|
36
33
|
})
|
|
@@ -118,23 +115,33 @@ export const authorizeChatOperation = (
|
|
|
118
115
|
return false
|
|
119
116
|
}
|
|
120
117
|
return !(
|
|
121
|
-
event.type === 'ai.
|
|
122
|
-
event.
|
|
123
|
-
event.
|
|
124
|
-
event.
|
|
118
|
+
event.type === 'ai.control.requested' &&
|
|
119
|
+
typeof event.payload === 'object' &&
|
|
120
|
+
event.payload !== null &&
|
|
121
|
+
'action' in event.payload &&
|
|
122
|
+
typeof event.payload.action === 'string' &&
|
|
123
|
+
[
|
|
124
|
+
'approval',
|
|
125
|
+
'input',
|
|
126
|
+
'interrupt',
|
|
127
|
+
'stop',
|
|
128
|
+
'retry',
|
|
129
|
+
'pause',
|
|
130
|
+
'resume',
|
|
131
|
+
].includes(event.payload.action)
|
|
125
132
|
)
|
|
126
133
|
})
|
|
127
134
|
return rejected === undefined
|
|
128
135
|
}
|
|
129
136
|
|
|
130
|
-
const
|
|
131
|
-
if (
|
|
132
|
-
throw new Error('A2 AI handlers must include ai.tool.
|
|
137
|
+
const toolExecutionHandler = aiHandlers['ai.tool.execution.requested']
|
|
138
|
+
if (toolExecutionHandler === undefined) {
|
|
139
|
+
throw new Error('A2 AI handlers must include ai.tool.execution.requested')
|
|
133
140
|
}
|
|
134
|
-
const
|
|
135
|
-
typeof
|
|
136
|
-
? { handler:
|
|
137
|
-
: { ...
|
|
141
|
+
const serializedToolExecutionHandler =
|
|
142
|
+
typeof toolExecutionHandler === 'function'
|
|
143
|
+
? { handler: toolExecutionHandler, lane: 'a2.chat.agent-tools' }
|
|
144
|
+
: { ...toolExecutionHandler, lane: 'a2.chat.agent-tools' }
|
|
138
145
|
|
|
139
146
|
export const chatScheduler =
|
|
140
147
|
process.env.VERCEL || process.env.A2_QUEUES
|
|
@@ -147,7 +154,7 @@ export const chatServer = createServer({
|
|
|
147
154
|
contract: chatAgent.contract,
|
|
148
155
|
handlers: {
|
|
149
156
|
...aiHandlers,
|
|
150
|
-
'ai.tool.
|
|
157
|
+
'ai.tool.execution.requested': serializedToolExecutionHandler,
|
|
151
158
|
'message.sent': {
|
|
152
159
|
lane: 'a2.chat.ingress',
|
|
153
160
|
handler: async (context) => {
|