@sentry/junior 0.242.0 → 0.244.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/dist/{admin-C54kvHVC.js → admin-CF9lQg0M.js} +1 -1
- package/dist/{agent-hooks-BVttjZvb.js → agent-hooks-Cj_rSeWd.js} +22 -12
- package/dist/agent-invocations-BO6ddZax.js +177 -0
- package/dist/{agent-runner-BTgnxJ7g.js → agent-runner-DtEtbaOb.js} +646 -70
- package/dist/api/schema.d.ts +67 -1
- package/dist/api/schema.js +4 -4
- package/dist/api.js +53 -22
- package/dist/{app-MEd7wbF0.d.ts → app-DRLh-8zs.d.ts} +5 -0
- package/dist/app.d.ts +1 -1
- package/dist/app.js +41 -24
- package/dist/{briefs-CKUsv9F1.js → briefs-BMzTiGJO.js} +4 -4
- package/dist/{catalog-runtime-C2YkfMZz.js → catalog-runtime-CpxhLwZG.js} +1 -1
- package/dist/cli/chat.js +12 -12
- package/dist/cli/check.js +3 -3
- package/dist/cli/main.js +2 -2
- package/dist/cli/plugins.js +4 -4
- package/dist/cli/snapshot-create.js +3 -3
- package/dist/cli/upgrade.js +3 -3
- package/dist/{client-D51G-Jdg.js → client-BVaGEb1-.js} +2 -2
- package/dist/{config-C6Uu3Re9.js → config-q9xXVN_5.js} +7 -2
- package/dist/{conversation-DwD-vquD.js → conversation-BxvgH64Q.js} +5 -1
- package/dist/{conversation-privacy-CzL47nXs.js → conversation-privacy-Dyh_63Ee.js} +1 -1
- package/dist/{credential-sync-DvDaE-Uj.js → credential-sync-Bsn-6m16.js} +14 -14
- package/dist/{executor-CGUukwus.js → executor-DA7eHhmC.js} +5 -124
- package/dist/{history-BLTUgFYO.js → history-Nte1oVed.js} +4 -0
- package/dist/{images-DSxZt5vk.js → images-CvMreau5.js} +3 -3
- package/dist/instrumentation.js +2 -2
- package/dist/{job-queue-hLylM9cy.js → job-queue-mVyF9jgv.js} +1 -1
- package/dist/{logging-BdZ_mK3q.js → logging-z7_w-KN_.js} +1 -1
- package/dist/nitro.d.ts +1 -1
- package/dist/nitro.js +3 -3
- package/dist/{oauth-authorization-BokpeHv-.js → oauth-authorization-Db1yGVvG.js} +1 -1
- package/dist/{oauth-callback-server-C3BOSmT_.js → oauth-callback-server-_MfOtSxS.js} +2 -2
- package/dist/{pending-auth-DOzL4D24.js → pending-auth-OndKPW4M.js} +2 -2
- package/dist/{profile-2gPzzMib.js → profile-yeKVNECl.js} +4 -4
- package/dist/{prompt-CMU-B4FA.js → prompt-CX36ZlbV.js} +1 -1
- package/dist/{registry-XJkXDNK2.js → registry-DvzzL8-v.js} +1 -1
- package/dist/{resolve-DhcdtDt6.js → resolve-4u5gEU04.js} +2 -2
- package/dist/{runner-CGyrnIPm.js → runner-Y6A6pww4.js} +10 -10
- package/dist/{sandbox-egress-signals-D6F3OZJP.js → sandbox-egress-signals-D7AOkuoe.js} +1 -1
- package/dist/{schema-Cabm4jcj.js → schema-Dyyi9oRc.js} +35 -3
- package/dist/{skills-C6PqcBPU.js → skills-DFduSvuO.js} +2 -2
- package/dist/{spawn-DL9x3T1o.js → spawn-DYg7jhhR.js} +7 -5
- package/dist/{task-queue-vZnBGu9B.js → task-queue-BQ0vnumm.js} +3 -3
- package/dist/{task-runner-C-c0t5F5.js → task-runner-D4jvUpFV.js} +12 -11
- package/dist/{tasks-EqCtmUJ2.js → tasks-DBZAgTx7.js} +49 -17
- package/dist/{thread-state-B8IKs_J8.js → thread-state-hj70P2Cp.js} +7 -7
- package/dist/{turn-failure-response-BS7OO1BE.js → turn-failure-response-S2pwtR8b.js} +9 -9
- package/dist/{turn-lifecycle-Cxrt8qMD.js → turn-lifecycle-CNFOMAJq.js} +1 -1
- package/dist/{turn-session-routing-BlGt2AUb.js → turn-session-routing-1eTgv6ns.js} +1 -1
- package/dist/{validation-C8hci6pj.js → validation-DcsBjzdi.js} +1 -1
- package/dist/{version-CjnHQ8aY.js → version-DVCHJYcY.js} +1 -1
- package/dist/version.js +1 -1
- package/dist/{viewer-BOgEy5zC.js → viewer-BBnCMR1g.js} +6 -8
- package/dist/{web-authorization-DSMV5tMK.js → web-authorization-CTltRLsB.js} +6 -6
- package/dist/{web-input-TD5P9jdo.js → web-input-CIsRGF07.js} +227 -31
- package/dist/{work-D39OAwP1.js → work-fje9VXgQ.js} +111 -225
- package/dist/{worker-CWiLgCWH.js → worker-Byd2Et9r.js} +5 -5
- package/migrations/0050_agent_invocation_parent_mailbox.sql +4 -0
- package/migrations/0051_agent_invocation_parent_tool_call.sql +1 -0
- package/migrations/0052_agent_invocation_title.sql +1 -0
- package/migrations/meta/0050_snapshot.json +3753 -0
- package/migrations/meta/0051_snapshot.json +3759 -0
- package/migrations/meta/0052_snapshot.json +3765 -0
- package/migrations/meta/_journal.json +22 -1
- package/package.json +5 -5
- package/dist/destinations-BeBiJeEM.js +0 -29
|
@@ -1,204 +1,11 @@
|
|
|
1
1
|
import { t as __exportAll } from "./rolldown-runtime-B4FMCO8f.js";
|
|
2
|
-
import { G as
|
|
3
|
-
import { c as getConversationEventStore,
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import { n as getPersistedSandboxState, o as persistThreadStateById, r as getPersistedThreadState } from "./thread-state-B8IKs_J8.js";
|
|
2
|
+
import { $ as getLatestAgentInvocationForConversation, G as completeAgentInvocation, H as stopChildAgentsOf, J as getActiveAgentInvocationForConversation, K as createAgentInvocation, Q as getAgentInvocationTurnId, U as recordSubagentEnded, W as recordSubagentStarted, X as getAgentInvocationMessageId, Y as getAgentInvocation, _n as AuthorizationFlowDisabledError, bt as appendAndEnqueueInboundMessage, ct as reportReplacedAgentInvocation, et as isTerminalAgentInvocation, it as markAgentInvocationMailboxAppended, m as PluginCredentialFailureError, ot as markAgentInvocationPaused, st as markAgentInvocationRunning } from "./agent-runner-DtEtbaOb.js";
|
|
3
|
+
import { c as getConversationEventStore, u as getConversationStore } from "./viewer-BBnCMR1g.js";
|
|
4
|
+
import { K as saveTurnCheckpoint, Ot as openConversationProjection, X as getTurnRecord, Y as failTurnRecord, ot as getTerminalAssistantMessages, q as abandonTurnRecord } from "./tasks-DBZAgTx7.js";
|
|
5
|
+
import { d as getConversationTurnBoundaryError, l as TurnInputCommitLostError, p as isTurnInputCommitLostError, r as getAssistantReplyText } from "./oauth-authorization-Db1yGVvG.js";
|
|
6
|
+
import { t as ConversationTurnLifecycleService } from "./turn-lifecycle-CNFOMAJq.js";
|
|
7
|
+
import { n as getPersistedSandboxState, o as persistThreadStateById, r as getPersistedThreadState } from "./thread-state-hj70P2Cp.js";
|
|
9
8
|
import { z } from "zod";
|
|
10
|
-
import { createHash } from "node:crypto";
|
|
11
|
-
import { and, asc, count, eq, inArray } from "drizzle-orm";
|
|
12
|
-
//#region src/chat/agent-invocations/store.ts
|
|
13
|
-
const CREATE_LOCK_PREFIX = "junior:agent_invocation:create";
|
|
14
|
-
const TERMINAL_AGENT_INVOCATION_STATUSES = [
|
|
15
|
-
"blocked",
|
|
16
|
-
"completed",
|
|
17
|
-
"failed"
|
|
18
|
-
];
|
|
19
|
-
const NON_TERMINAL_AGENT_INVOCATION_STATUSES = [
|
|
20
|
-
"pending",
|
|
21
|
-
"running",
|
|
22
|
-
"awaiting_resume"
|
|
23
|
-
];
|
|
24
|
-
function stableId(prefix, ...parts) {
|
|
25
|
-
return `${prefix}:${createHash("sha256").update(parts.join("\0")).digest("hex").slice(0, 32)}`;
|
|
26
|
-
}
|
|
27
|
-
/** Return the stable identity for one retry-safe delegated task. */
|
|
28
|
-
function getAgentInvocationId(parentConversationId, idempotencyKey) {
|
|
29
|
-
return stableId("agent-invocation", parentConversationId, idempotencyKey);
|
|
30
|
-
}
|
|
31
|
-
/** Return the stable child identity for one invocation without a named binding. */
|
|
32
|
-
function getUnnamedAgentConversationId(invocationId) {
|
|
33
|
-
return stableId("agent", invocationId);
|
|
34
|
-
}
|
|
35
|
-
/** Return the stable child identity for one named binding. */
|
|
36
|
-
function getNamedAgentConversationId(parentConversationId, name) {
|
|
37
|
-
return stableId("agent", parentConversationId, name);
|
|
38
|
-
}
|
|
39
|
-
/** Return the stable turn identity advanced by one agent invocation. */
|
|
40
|
-
function getAgentInvocationTurnId(invocationId) {
|
|
41
|
-
return `agent-invocation:${invocationId}`;
|
|
42
|
-
}
|
|
43
|
-
/** Return the stable mailbox identity for one agent invocation. */
|
|
44
|
-
function getAgentInvocationMessageId(invocationId) {
|
|
45
|
-
return `agent-invocation:${invocationId}:input`;
|
|
46
|
-
}
|
|
47
|
-
function bindingFromRow(row) {
|
|
48
|
-
return agentBindingSchema.parse({
|
|
49
|
-
childConversationId: row.childConversationId,
|
|
50
|
-
name: row.name,
|
|
51
|
-
parentConversationId: row.parentConversationId
|
|
52
|
-
});
|
|
53
|
-
}
|
|
54
|
-
function invocationFromRow(row) {
|
|
55
|
-
return agentInvocationSchema.parse({
|
|
56
|
-
actor: row.actor,
|
|
57
|
-
...row.agentName ? { agentName: row.agentName } : void 0,
|
|
58
|
-
childConversationId: row.childConversationId,
|
|
59
|
-
createdAtMs: row.createdAt.getTime(),
|
|
60
|
-
...row.credentialContext ? { credentialContext: row.credentialContext } : void 0,
|
|
61
|
-
destination: row.destination,
|
|
62
|
-
...row.errorMessage !== null ? { errorMessage: row.errorMessage } : void 0,
|
|
63
|
-
input: row.input,
|
|
64
|
-
invocationId: row.invocationId,
|
|
65
|
-
mailboxStatus: row.mailboxStatus,
|
|
66
|
-
parentConversationId: row.parentConversationId,
|
|
67
|
-
...row.reasoningLevel ? { reasoningLevel: row.reasoningLevel } : void 0,
|
|
68
|
-
...row.result !== null ? { result: row.result } : void 0,
|
|
69
|
-
source: row.source,
|
|
70
|
-
status: row.status,
|
|
71
|
-
...row.terminalAt ? { terminalAtMs: row.terminalAt.getTime() } : void 0,
|
|
72
|
-
updatedAtMs: row.updatedAt.getTime()
|
|
73
|
-
});
|
|
74
|
-
}
|
|
75
|
-
/** Require every durable creation input to match before replaying one key. */
|
|
76
|
-
function sameCreateInput(invocation, input) {
|
|
77
|
-
return invocation.parentConversationId === input.parentConversationId && invocation.agentName === input.agentName && invocation.input === input.input && invocation.reasoningLevel === input.reasoningLevel && JSON.stringify(invocation.actor) === JSON.stringify(input.actor) && JSON.stringify(invocation.credentialContext) === JSON.stringify(input.credentialContext) && JSON.stringify(invocation.destination) === JSON.stringify(input.destination);
|
|
78
|
-
}
|
|
79
|
-
/** Read one named child binding in its parent-agent scope. */
|
|
80
|
-
async function getAgentBinding(args) {
|
|
81
|
-
const rows = await getSqlExecutor().db().select().from(juniorAgentBindings).where(and(eq(juniorAgentBindings.parentConversationId, args.parentConversationId), eq(juniorAgentBindings.name, args.name)));
|
|
82
|
-
return rows[0] ? bindingFromRow(rows[0]) : void 0;
|
|
83
|
-
}
|
|
84
|
-
/** Read one durable agent invocation. */
|
|
85
|
-
async function getAgentInvocation(invocationId) {
|
|
86
|
-
const rows = await getSqlExecutor().db().select().from(juniorAgentInvocations).where(eq(juniorAgentInvocations.invocationId, invocationId));
|
|
87
|
-
return rows[0] ? invocationFromRow(rows[0]) : void 0;
|
|
88
|
-
}
|
|
89
|
-
/** Resolve the single invocation that may resume for one child conversation. */
|
|
90
|
-
async function getActiveAgentInvocationForConversation(childConversationId) {
|
|
91
|
-
const rows = await getSqlExecutor().db().select().from(juniorAgentInvocations).where(and(eq(juniorAgentInvocations.childConversationId, childConversationId), inArray(juniorAgentInvocations.status, ["running", "awaiting_resume"]))).orderBy(asc(juniorAgentInvocations.createdAt)).limit(2);
|
|
92
|
-
if (rows.length > 1) throw new Error(`Child conversation ${childConversationId} has multiple active agent invocations`);
|
|
93
|
-
return rows[0] ? invocationFromRow(rows[0]) : void 0;
|
|
94
|
-
}
|
|
95
|
-
async function getNonTerminalAgentInvocationForConversation(childConversationId) {
|
|
96
|
-
const rows = await getSqlExecutor().db().select().from(juniorAgentInvocations).where(and(eq(juniorAgentInvocations.childConversationId, childConversationId), inArray(juniorAgentInvocations.status, NON_TERMINAL_AGENT_INVOCATION_STATUSES))).orderBy(asc(juniorAgentInvocations.createdAt)).limit(1);
|
|
97
|
-
return rows[0] ? invocationFromRow(rows[0]) : void 0;
|
|
98
|
-
}
|
|
99
|
-
async function countNonTerminalAgentInvocationsForParent(parentConversationId) {
|
|
100
|
-
return (await getSqlExecutor().db().select({ value: count() }).from(juniorAgentInvocations).where(and(eq(juniorAgentInvocations.parentConversationId, parentConversationId), inArray(juniorAgentInvocations.status, NON_TERMINAL_AGENT_INVOCATION_STATUSES))))[0]?.value ?? 0;
|
|
101
|
-
}
|
|
102
|
-
/**
|
|
103
|
-
* Create or replay one invocation, reusing named child conversations and
|
|
104
|
-
* keeping unnamed child identities scoped to the invocation.
|
|
105
|
-
*/
|
|
106
|
-
async function createAgentInvocation(rawInput, nowMs = Date.now()) {
|
|
107
|
-
const input = createAgentInvocationSchema.parse(rawInput);
|
|
108
|
-
const invocationId = getAgentInvocationId(input.parentConversationId, input.idempotencyKey);
|
|
109
|
-
const childConversationId = input.agentName ? getNamedAgentConversationId(input.parentConversationId, input.agentName) : getUnnamedAgentConversationId(invocationId);
|
|
110
|
-
const lockName = `${CREATE_LOCK_PREFIX}:${input.parentConversationId}`;
|
|
111
|
-
return await getSqlExecutor().withLock(lockName, async () => {
|
|
112
|
-
const existing = await getAgentInvocation(invocationId);
|
|
113
|
-
if (existing) {
|
|
114
|
-
if (!sameCreateInput(existing, input)) throw new Error(`Agent invocation idempotency key was reused with different input for ${invocationId}`);
|
|
115
|
-
return existing;
|
|
116
|
-
}
|
|
117
|
-
if (await countNonTerminalAgentInvocationsForParent(input.parentConversationId) >= 8) throw new AgentInvocationLimitError(8);
|
|
118
|
-
await getConversationStore().createChild({
|
|
119
|
-
childConversationId,
|
|
120
|
-
parentConversationId: input.parentConversationId,
|
|
121
|
-
nowMs,
|
|
122
|
-
source: "internal"
|
|
123
|
-
});
|
|
124
|
-
if (input.agentName) {
|
|
125
|
-
await getSqlExecutor().db().insert(juniorAgentBindings).values({
|
|
126
|
-
childConversationId,
|
|
127
|
-
name: input.agentName,
|
|
128
|
-
parentConversationId: input.parentConversationId
|
|
129
|
-
}).onConflictDoNothing();
|
|
130
|
-
const binding = await getAgentBinding({
|
|
131
|
-
name: input.agentName,
|
|
132
|
-
parentConversationId: input.parentConversationId
|
|
133
|
-
});
|
|
134
|
-
if (!binding || binding.childConversationId !== childConversationId) throw new Error(`Named agent binding did not resolve to ${childConversationId}`);
|
|
135
|
-
if (await getNonTerminalAgentInvocationForConversation(childConversationId)) throw new AgentInvocationBusyError(input.agentName);
|
|
136
|
-
}
|
|
137
|
-
await getSqlExecutor().db().insert(juniorAgentInvocations).values({
|
|
138
|
-
invocationId,
|
|
139
|
-
parentConversationId: input.parentConversationId,
|
|
140
|
-
childConversationId,
|
|
141
|
-
agentName: input.agentName ?? null,
|
|
142
|
-
input: input.input,
|
|
143
|
-
actor: input.actor,
|
|
144
|
-
credentialContext: input.credentialContext ?? null,
|
|
145
|
-
source: { kind: "agent_invocation" },
|
|
146
|
-
destination: input.destination,
|
|
147
|
-
reasoningLevel: input.reasoningLevel ?? null,
|
|
148
|
-
status: "pending",
|
|
149
|
-
mailboxStatus: "pending",
|
|
150
|
-
createdAt: new Date(nowMs),
|
|
151
|
-
updatedAt: new Date(nowMs),
|
|
152
|
-
terminalAt: null
|
|
153
|
-
}).onConflictDoNothing();
|
|
154
|
-
const invocation = await getAgentInvocation(invocationId);
|
|
155
|
-
if (!invocation || !sameCreateInput(invocation, input)) throw new Error(`Agent invocation creation raced for ${invocationId}`);
|
|
156
|
-
return invocation;
|
|
157
|
-
});
|
|
158
|
-
}
|
|
159
|
-
/** List bounded invocation records whose mailbox append still needs repair. */
|
|
160
|
-
async function listPendingAgentInvocationMailboxAppends(limit = 100) {
|
|
161
|
-
return (await getSqlExecutor().db().select().from(juniorAgentInvocations).where(eq(juniorAgentInvocations.mailboxStatus, "pending")).orderBy(asc(juniorAgentInvocations.createdAt)).limit(limit)).map(invocationFromRow);
|
|
162
|
-
}
|
|
163
|
-
/** Record that the invocation's idempotent mailbox append completed. */
|
|
164
|
-
async function markAgentInvocationMailboxAppended(invocationId, nowMs = Date.now()) {
|
|
165
|
-
await getSqlExecutor().db().update(juniorAgentInvocations).set({
|
|
166
|
-
mailboxStatus: "appended",
|
|
167
|
-
updatedAt: new Date(nowMs)
|
|
168
|
-
}).where(eq(juniorAgentInvocations.invocationId, invocationId));
|
|
169
|
-
}
|
|
170
|
-
/** Mark one non-terminal invocation as actively executing. */
|
|
171
|
-
async function markAgentInvocationRunning(invocationId, nowMs = Date.now()) {
|
|
172
|
-
await getSqlExecutor().db().update(juniorAgentInvocations).set({
|
|
173
|
-
status: "running",
|
|
174
|
-
updatedAt: new Date(nowMs)
|
|
175
|
-
}).where(and(eq(juniorAgentInvocations.invocationId, invocationId), inArray(juniorAgentInvocations.status, NON_TERMINAL_AGENT_INVOCATION_STATUSES)));
|
|
176
|
-
return await getAgentInvocation(invocationId);
|
|
177
|
-
}
|
|
178
|
-
/** Mark one invocation as waiting for another shared execution slice. */
|
|
179
|
-
async function markAgentInvocationAwaitingResume(invocationId, nowMs = Date.now()) {
|
|
180
|
-
await getSqlExecutor().db().update(juniorAgentInvocations).set({
|
|
181
|
-
status: "awaiting_resume",
|
|
182
|
-
updatedAt: new Date(nowMs)
|
|
183
|
-
}).where(and(eq(juniorAgentInvocations.invocationId, invocationId), inArray(juniorAgentInvocations.status, NON_TERMINAL_AGENT_INVOCATION_STATUSES)));
|
|
184
|
-
}
|
|
185
|
-
/** Persist one terminal invocation result without overwriting an earlier result. */
|
|
186
|
-
async function completeAgentInvocation(args) {
|
|
187
|
-
const nowMs = args.nowMs ?? Date.now();
|
|
188
|
-
await getSqlExecutor().db().update(juniorAgentInvocations).set({
|
|
189
|
-
status: args.status,
|
|
190
|
-
result: args.status === "completed" ? args.result : null,
|
|
191
|
-
errorMessage: args.status === "completed" ? null : args.errorMessage,
|
|
192
|
-
terminalAt: new Date(nowMs),
|
|
193
|
-
updatedAt: new Date(nowMs)
|
|
194
|
-
}).where(and(eq(juniorAgentInvocations.invocationId, args.invocationId), inArray(juniorAgentInvocations.status, NON_TERMINAL_AGENT_INVOCATION_STATUSES)));
|
|
195
|
-
return await getAgentInvocation(args.invocationId);
|
|
196
|
-
}
|
|
197
|
-
/** Return whether an agent invocation has finished. */
|
|
198
|
-
function isTerminalAgentInvocation(invocation) {
|
|
199
|
-
return TERMINAL_AGENT_INVOCATION_STATUSES.includes(invocation.status);
|
|
200
|
-
}
|
|
201
|
-
//#endregion
|
|
202
9
|
//#region src/chat/runtime/turn-execution.ts
|
|
203
10
|
/** An error thrown while the agent advances a Run. */
|
|
204
11
|
var AgentRunError = class extends Error {
|
|
@@ -275,7 +82,8 @@ function buildAgentInvocationInboundMessage(invocation, nowMs = Date.now()) {
|
|
|
275
82
|
}
|
|
276
83
|
/** Append one invocation to its child mailbox and send the normal queue wake. */
|
|
277
84
|
async function enqueueAgentInvocation(invocation, options) {
|
|
278
|
-
if (invocation.mailboxStatus === "appended") return;
|
|
85
|
+
if (invocation.mailboxStatus === "appended" || isTerminalAgentInvocation(invocation)) return;
|
|
86
|
+
await recordSubagentStarted(invocation);
|
|
279
87
|
const nowMs = options.nowMs ?? Date.now();
|
|
280
88
|
await appendAndEnqueueInboundMessage({
|
|
281
89
|
message: buildAgentInvocationInboundMessage(invocation, nowMs),
|
|
@@ -310,7 +118,10 @@ async function resolveAgentInvocationId(context) {
|
|
|
310
118
|
const mailboxInvocationId = invocationIdFromMessages(context.attempt.messages);
|
|
311
119
|
if (mailboxInvocationId) return mailboxInvocationId;
|
|
312
120
|
if (context.attempt.messages.length > 0) return;
|
|
313
|
-
|
|
121
|
+
const active = await getActiveAgentInvocationForConversation(context.conversationId);
|
|
122
|
+
if (active) return active.invocationId;
|
|
123
|
+
const latest = await getLatestAgentInvocationForConversation(context.conversationId);
|
|
124
|
+
return latest?.status === "stopped" ? latest.invocationId : void 0;
|
|
314
125
|
}
|
|
315
126
|
/** Project the terminal invocation into the idempotent turn lifecycle. */
|
|
316
127
|
async function persistTerminalLifecycle(invocation) {
|
|
@@ -324,7 +135,17 @@ async function persistTerminalLifecycle(invocation) {
|
|
|
324
135
|
...common,
|
|
325
136
|
outcome: invocation.result?.trim() ? "success" : "no_reply"
|
|
326
137
|
});
|
|
327
|
-
else if (invocation.status === "
|
|
138
|
+
else if (invocation.status === "stopped") {
|
|
139
|
+
await abandonTurnRecord({
|
|
140
|
+
conversationId: common.conversationId,
|
|
141
|
+
errorMessage: "Turn cancelled",
|
|
142
|
+
turnId: common.turnId
|
|
143
|
+
});
|
|
144
|
+
await lifecycle.complete({
|
|
145
|
+
...common,
|
|
146
|
+
outcome: "cancelled"
|
|
147
|
+
});
|
|
148
|
+
} else if (invocation.status === "blocked" || invocation.status === "failed") await lifecycle.fail({
|
|
328
149
|
...common,
|
|
329
150
|
failureCode: "model_execution_failed"
|
|
330
151
|
});
|
|
@@ -393,20 +214,58 @@ async function saveAgentInvocationResult(args) {
|
|
|
393
214
|
}
|
|
394
215
|
});
|
|
395
216
|
if (!terminal || !isTerminalAgentInvocation(terminal)) throw new Error(`Agent invocation did not finish for ${args.invocation.invocationId}`);
|
|
396
|
-
|
|
217
|
+
if (terminal.status === "completed") return {
|
|
397
218
|
finishedAtMs: terminal.terminalAtMs,
|
|
398
219
|
outcome: terminal.result.trim() ? "success" : "no_reply"
|
|
399
|
-
}
|
|
220
|
+
};
|
|
221
|
+
if (terminal.status === "stopped") return {
|
|
222
|
+
finishedAtMs: terminal.terminalAtMs,
|
|
223
|
+
outcome: "cancelled"
|
|
224
|
+
};
|
|
225
|
+
return {
|
|
400
226
|
finishedAtMs: terminal.terminalAtMs,
|
|
401
227
|
failureCode: "model_execution_failed",
|
|
402
228
|
outcome: "failed"
|
|
403
229
|
};
|
|
404
230
|
}
|
|
405
|
-
/**
|
|
406
|
-
|
|
231
|
+
/**
|
|
232
|
+
* Read how many parents a child Conversation has and the root Conversation's
|
|
233
|
+
* Location. A child of the root has depth 1.
|
|
234
|
+
*/
|
|
235
|
+
async function readChildDepthAndRootLocation(conversationId) {
|
|
236
|
+
const conversationStore = getConversationStore();
|
|
237
|
+
let depth = 0;
|
|
238
|
+
let conversation = await conversationStore.get({ conversationId });
|
|
239
|
+
while (conversation?.parentConversationId) {
|
|
240
|
+
depth += 1;
|
|
241
|
+
conversation = await conversationStore.get({ conversationId: conversation.parentConversationId });
|
|
242
|
+
}
|
|
243
|
+
return {
|
|
244
|
+
depth,
|
|
245
|
+
location: conversation?.location
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Build the invocation consumer that advances work through the shared runner.
|
|
250
|
+
* `enqueueResult` sends each terminal result to the parent mailbox. Omit it
|
|
251
|
+
* only where the parent has no mailbox, such as the local CLI.
|
|
252
|
+
* `maxAgentDepth` is the number of child agent levels that can start their
|
|
253
|
+
* own child agents. The default, 0, lets only the root start child agents.
|
|
254
|
+
*/
|
|
255
|
+
function createAgentInvocationWorker(agentRunner, options = {}) {
|
|
256
|
+
const maxAgentDepth = options.maxAgentDepth ?? 0;
|
|
407
257
|
return async (context, invocationId) => {
|
|
408
258
|
let invocation = await getAgentInvocation(invocationId);
|
|
409
259
|
if (!invocation) throw new Error(`Agent invocation is missing for ${invocationId}`);
|
|
260
|
+
const report = async (terminal) => {
|
|
261
|
+
await recordSubagentEnded(terminal);
|
|
262
|
+
await options.enqueueResult?.(terminal);
|
|
263
|
+
};
|
|
264
|
+
const finish = async (terminal) => {
|
|
265
|
+
if (!terminal || !isTerminalAgentInvocation(terminal)) return;
|
|
266
|
+
await persistTerminalLifecycle(terminal);
|
|
267
|
+
await report(terminal);
|
|
268
|
+
};
|
|
410
269
|
let acknowledged = context.attempt.messages.length === 0;
|
|
411
270
|
const acknowledge = async () => {
|
|
412
271
|
if (acknowledged) return;
|
|
@@ -417,26 +276,43 @@ function createAgentInvocationWorker(agentRunner) {
|
|
|
417
276
|
}
|
|
418
277
|
acknowledged = true;
|
|
419
278
|
};
|
|
279
|
+
const stopSignal = context.stopSignal?.();
|
|
280
|
+
const finishStopped = async () => {
|
|
281
|
+
await finish(await completeAgentInvocation({
|
|
282
|
+
invocationId,
|
|
283
|
+
errorMessage: "Agent invocation was stopped",
|
|
284
|
+
status: "stopped"
|
|
285
|
+
}));
|
|
286
|
+
await stopChildAgentsOf(context.conversationId);
|
|
287
|
+
await acknowledge();
|
|
288
|
+
return { status: "completed" };
|
|
289
|
+
};
|
|
420
290
|
const turnId = getAgentInvocationTurnId(invocation.invocationId);
|
|
421
291
|
const lifecycle = new ConversationTurnLifecycleService(getConversationEventStore());
|
|
422
292
|
let sandboxRef;
|
|
423
293
|
let history;
|
|
424
294
|
let location;
|
|
295
|
+
let canSpawn = false;
|
|
425
296
|
try {
|
|
426
297
|
if (invocation.childConversationId !== context.conversationId) throw new Error(`Agent invocation ${invocationId} belongs to ${invocation.childConversationId}, not ${context.conversationId}`);
|
|
427
298
|
if (context.destination) throw new Error(`Agent invocation conversation ${context.conversationId} must not own a provider destination`);
|
|
428
299
|
if (isTerminalAgentInvocation(invocation)) {
|
|
429
|
-
await
|
|
300
|
+
await finish(invocation);
|
|
430
301
|
await acknowledge();
|
|
431
302
|
return { status: "completed" };
|
|
432
303
|
}
|
|
433
304
|
const projected = await projectTerminalTurn(invocation);
|
|
434
305
|
if (projected && isTerminalAgentInvocation(projected)) {
|
|
435
|
-
await
|
|
306
|
+
await finish(projected);
|
|
436
307
|
await acknowledge();
|
|
437
308
|
return { status: "completed" };
|
|
438
309
|
}
|
|
439
310
|
invocation = await markAgentInvocationRunning(invocation.invocationId) ?? invocation;
|
|
311
|
+
if (isTerminalAgentInvocation(invocation)) {
|
|
312
|
+
await finish(invocation);
|
|
313
|
+
await acknowledge();
|
|
314
|
+
return { status: "completed" };
|
|
315
|
+
}
|
|
440
316
|
await lifecycle.start({
|
|
441
317
|
conversationId: invocation.childConversationId,
|
|
442
318
|
createdAtMs: invocation.createdAtMs,
|
|
@@ -444,19 +320,23 @@ function createAgentInvocationWorker(agentRunner) {
|
|
|
444
320
|
surface: "internal",
|
|
445
321
|
turnId
|
|
446
322
|
});
|
|
447
|
-
const [persisted, projection] = await Promise.all([
|
|
323
|
+
const [persisted, projection, tree] = await Promise.all([
|
|
324
|
+
getPersistedThreadState(invocation.childConversationId),
|
|
325
|
+
openConversationProjection({ conversationId: invocation.childConversationId }),
|
|
326
|
+
readChildDepthAndRootLocation(invocation.childConversationId)
|
|
327
|
+
]);
|
|
448
328
|
sandboxRef = getPersistedSandboxState(persisted);
|
|
449
329
|
history = projection.messages;
|
|
450
|
-
location =
|
|
330
|
+
location = tree.location;
|
|
331
|
+
canSpawn = tree.depth <= maxAgentDepth;
|
|
451
332
|
} catch (error) {
|
|
452
333
|
if (isInvocationInputCommitLost(error)) return { status: "lost_lease" };
|
|
453
334
|
if (!context.attempt.isFinalAttempt) throw error;
|
|
454
|
-
|
|
335
|
+
await finish(await completeAgentInvocation({
|
|
455
336
|
invocationId: invocation.invocationId,
|
|
456
337
|
errorMessage: error instanceof Error ? error.message : "Agent invocation failed",
|
|
457
338
|
status: "failed"
|
|
458
|
-
});
|
|
459
|
-
if (terminal) await persistTerminalLifecycle(terminal);
|
|
339
|
+
}));
|
|
460
340
|
await acknowledge();
|
|
461
341
|
return { status: "completed" };
|
|
462
342
|
}
|
|
@@ -474,7 +354,8 @@ function createAgentInvocationWorker(agentRunner) {
|
|
|
474
354
|
source: invocation.source,
|
|
475
355
|
...location ? { location } : void 0,
|
|
476
356
|
surface: "internal",
|
|
477
|
-
|
|
357
|
+
...stopSignal ? { signal: stopSignal } : void 0,
|
|
358
|
+
disabledFeatures: canSpawn ? ["handoff", "interactive-auth"] : [
|
|
478
359
|
"handoff",
|
|
479
360
|
"interactive-auth",
|
|
480
361
|
"subagents"
|
|
@@ -499,41 +380,46 @@ function createAgentInvocationWorker(agentRunner) {
|
|
|
499
380
|
if (!(error instanceof AgentRunError)) throw error;
|
|
500
381
|
const runError = error.cause;
|
|
501
382
|
if (isInvocationInputCommitLost(runError)) return { status: "lost_lease" };
|
|
383
|
+
if (stopSignal?.aborted) return await finishStopped();
|
|
502
384
|
const blocking = blockingInvocationError(runError);
|
|
503
385
|
if (blocking) {
|
|
504
|
-
|
|
386
|
+
await finish(await completeAgentInvocation({
|
|
505
387
|
invocationId: invocation.invocationId,
|
|
506
388
|
errorMessage: blocking.message,
|
|
507
389
|
status: "blocked"
|
|
508
|
-
});
|
|
509
|
-
if (terminal) await persistTerminalLifecycle(terminal);
|
|
390
|
+
}));
|
|
510
391
|
await acknowledge();
|
|
511
392
|
return { status: "completed" };
|
|
512
393
|
}
|
|
513
394
|
if (!context.attempt.isFinalAttempt) throw runError;
|
|
514
|
-
|
|
395
|
+
await finish(await completeAgentInvocation({
|
|
515
396
|
invocationId: invocation.invocationId,
|
|
516
397
|
errorMessage: runError instanceof Error ? runError.message : "Agent invocation failed",
|
|
517
398
|
status: "failed"
|
|
518
|
-
});
|
|
519
|
-
if (terminal) await persistTerminalLifecycle(terminal);
|
|
399
|
+
}));
|
|
520
400
|
await acknowledge();
|
|
521
401
|
return { status: "completed" };
|
|
522
402
|
}
|
|
403
|
+
if (outcome.status !== "completed" && stopSignal?.aborted) return await finishStopped();
|
|
523
404
|
if (outcome.status === "suspended") {
|
|
524
|
-
await
|
|
405
|
+
await markAgentInvocationPaused(invocation.invocationId);
|
|
525
406
|
return { status: "yielded" };
|
|
526
407
|
}
|
|
527
408
|
if (outcome.status === "awaiting_auth") {
|
|
528
|
-
|
|
409
|
+
await finish(await completeAgentInvocation({
|
|
529
410
|
invocationId: invocation.invocationId,
|
|
530
411
|
errorMessage: `Agent invocation requires ${outcome.providerDisplayName} authorization`,
|
|
531
412
|
status: "blocked"
|
|
532
|
-
});
|
|
533
|
-
if (terminal) await persistTerminalLifecycle(terminal);
|
|
413
|
+
}));
|
|
534
414
|
await acknowledge();
|
|
535
415
|
return { status: "completed" };
|
|
536
416
|
}
|
|
417
|
+
let terminal = await getAgentInvocation(invocation.invocationId);
|
|
418
|
+
if (stopSignal?.aborted) {
|
|
419
|
+
if (terminal && isTerminalAgentInvocation(terminal) && terminal.parentMailboxStatus === "replaced") terminal = await reportReplacedAgentInvocation(terminal.invocationId);
|
|
420
|
+
await stopChildAgentsOf(context.conversationId);
|
|
421
|
+
}
|
|
422
|
+
if (terminal && isTerminalAgentInvocation(terminal)) await report(terminal);
|
|
537
423
|
await acknowledge();
|
|
538
424
|
return { status: "completed" };
|
|
539
425
|
};
|
|
@@ -546,4 +432,4 @@ function routeAgentInvocationWork(options) {
|
|
|
546
432
|
};
|
|
547
433
|
}
|
|
548
434
|
//#endregion
|
|
549
|
-
export { work_exports as a,
|
|
435
|
+
export { work_exports as a, resolveAgentInvocationId as i, createAndEnqueueAgentInvocation as n, AgentRunError as o, enqueueAgentInvocation as r, executeTurn as s, createAgentInvocationWorker as t };
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { t as __exportAll } from "./rolldown-runtime-B4FMCO8f.js";
|
|
2
|
-
import { At as
|
|
3
|
-
import { v as isProviderRetryError } from "./client-
|
|
4
|
-
import { C as withLogContext, d as logException, f as logInfo, p as logWarn } from "./logging-
|
|
5
|
-
import { i as getChatConfig } from "./config-
|
|
6
|
-
import { f as getTurnRequestDeadline, m as ConversationQueueMessageRejectedError } from "./job-queue-
|
|
2
|
+
import { At as drainConversationMailbox, Bt as requestAnotherSlice, Ct as checkInConversationWork, Dt as completeConversationWork, Et as completeConversationStop, Ht as startConversationWork, It as recordAttemptFailure, Jt as isFinalAttempt, Kt as hasConversationStop, Lt as recordConversationRetry, Nt as getConversationWorkState, Ot as countPendingConversationMessages, Rt as releaseConversationWork, Yt as isInvalidConversationRecordError, jt as ensureConversationWake, kt as deadLetterAttempt, vt as ackMessages, wt as clearConsumedConversationWake, xt as beginConversationResume } from "./agent-runner-DtEtbaOb.js";
|
|
3
|
+
import { v as isProviderRetryError } from "./client-BVaGEb1-.js";
|
|
4
|
+
import { C as withLogContext, d as logException, f as logInfo, p as logWarn } from "./logging-z7_w-KN_.js";
|
|
5
|
+
import { i as getChatConfig } from "./config-q9xXVN_5.js";
|
|
6
|
+
import { f as getTurnRequestDeadline, m as ConversationQueueMessageRejectedError } from "./job-queue-mVyF9jgv.js";
|
|
7
7
|
//#region src/chat/task-execution/worker.ts
|
|
8
8
|
var worker_exports = /* @__PURE__ */ __exportAll({
|
|
9
9
|
CONVERSATION_WORK_DEFER_DELAY_MS: () => CONVERSATION_WORK_DEFER_DELAY_MS,
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
ALTER TABLE "junior_agent_invocations" ADD COLUMN "parent_mailbox_status" text;--> statement-breakpoint
|
|
2
|
+
CREATE INDEX "junior_agent_invocations_parent_mailbox_idx" ON "junior_agent_invocations" USING btree ("parent_mailbox_status","terminal_at");--> statement-breakpoint
|
|
3
|
+
UPDATE "junior_agent_invocations" SET "status" = 'paused' WHERE "status" = 'awaiting_resume';--> statement-breakpoint
|
|
4
|
+
UPDATE "junior_agent_invocations" SET "parent_mailbox_status" = 'appended' WHERE "status" IN ('blocked', 'completed', 'failed') AND "parent_mailbox_status" IS NULL;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
ALTER TABLE "junior_agent_invocations" ADD COLUMN "parent_tool_call_id" text;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
ALTER TABLE "junior_agent_invocations" ADD COLUMN "title" text;
|