@sentry/junior 0.202.0 → 0.204.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/agent-CMyTD4DD.js +17509 -0
- package/dist/agent-hooks-Bbcp9e33.js +4040 -0
- package/dist/agent-runner-DNb1A49M.js +678 -0
- package/dist/api/schema.d.ts +2728 -2639
- package/dist/api/schema.js +4 -169
- package/dist/api.d.ts +23 -17
- package/dist/api.js +3881 -5326
- package/dist/app-B1TOqBI8.d.ts +246 -0
- package/dist/app.d.ts +4 -520
- package/dist/app.js +9522 -11875
- package/dist/auth-pause-state-DosEalIG.js +168 -0
- package/dist/catalog-runtime-BReos-D4.js +7 -0
- package/dist/checkpoint-CNrPyRJN.js +2450 -0
- package/dist/cli/chat.d.ts +20 -0
- package/dist/cli/chat.js +229 -336
- package/dist/cli/check.d.ts +8 -0
- package/dist/cli/check.js +513 -806
- package/dist/cli/env.d.ts +13 -0
- package/dist/cli/env.js +63 -12
- package/dist/cli/init.d.ts +3 -0
- package/dist/cli/init.js +90 -159
- package/dist/cli/main.d.ts +6 -0
- package/dist/cli/main.js +43 -64
- package/dist/cli/plugins.d.ts +13 -0
- package/dist/cli/plugins.js +148 -227
- package/dist/cli/run.d.ts +16 -0
- package/dist/cli/run.js +52 -8
- package/dist/cli/snapshot-create.d.ts +4 -0
- package/dist/cli/snapshot-create.js +71 -125
- package/dist/cli/upgrade.d.ts +15 -0
- package/dist/cli/upgrade.js +232 -335
- package/dist/config-nGCW1OTG.js +941 -0
- package/dist/context-B45_WqVX.js +298 -0
- package/dist/conversation-privacy-B4voFRSm.js +182 -0
- package/dist/conversation-work-C6y3T5Hx.js +51 -0
- package/dist/credential-sync-kXr1hvO1.js +2660 -0
- package/dist/db-BeyXKJOI.d.ts +5 -0
- package/dist/destinations-BeBiJeEM.js +29 -0
- package/dist/execution-stats-DWfSihcH.js +373 -0
- package/dist/executor-DcMSlI5c.js +721 -0
- package/dist/experimental-CIXf7Uye.js +46 -0
- package/dist/history-ClE4toxe.d.ts +32 -0
- package/dist/history-CqTuSAdI.js +441 -0
- package/dist/instrumentation.d.ts +3 -3
- package/dist/instrumentation.js +130 -176
- package/dist/job-queue-Drn_LXlM.js +63 -0
- package/dist/logging-BzAk1S_s.js +1210 -0
- package/dist/model-profile-CZmVoGdX.js +49 -0
- package/dist/nitro.d.ts +31 -36
- package/dist/nitro.js +275 -456
- package/dist/oauth-callback-server-JiftokvA.js +130 -0
- package/dist/oauth-relay-CdCnu80W.js +62 -0
- package/dist/package-discovery-7a9YqLhE.js +455 -0
- package/dist/pending-auth-CWmSdZVg.js +185 -0
- package/dist/plugin-module-BKGOXca2.js +81 -0
- package/dist/plugins-BI9A1vKA.d.ts +95 -0
- package/dist/plugins-egKWjgue.js +99 -0
- package/dist/private-trace-result-BLvBjy_e.js +16 -0
- package/dist/profile-CrT5nXbw.js +861 -0
- package/dist/registry-DH1xUo2N.js +1419 -0
- package/dist/reporting-schema-DosTN__Q.js +105 -0
- package/dist/resolve-CZHnfxaS.js +546 -0
- package/dist/rolldown-runtime-B4FMCO8f.js +28 -0
- package/dist/runner-CZV2sC46.js +392 -0
- package/dist/sandbox-egress-signals-bHZfmhbd.js +46 -0
- package/dist/schema-Rn8-XEaN.js +1140 -0
- package/dist/sentry-CD3SdJl-.js +26 -0
- package/dist/skills-CIcDQSgM.js +316 -0
- package/dist/spawn-DdkUFRmq.js +29 -0
- package/dist/stats-DTUbAb88.js +2634 -0
- package/dist/store-C5qxF2ac.d.ts +208 -0
- package/dist/task-queue-CJIR4RF7.js +136 -0
- package/dist/task-runner-D-O3A-Cp.js +639 -0
- package/dist/thread-state-CRc9jTJS.js +431 -0
- package/dist/turn-execution-BkinIdJF.js +47 -0
- package/dist/turn-lifecycle-DQV19VTW.js +54 -0
- package/dist/usage-D3EbSO7w.js +296 -0
- package/dist/usage-schema-CKaDkH_j.js +20 -0
- package/dist/validation-C2CR-OX3.js +40 -0
- package/dist/vercel-queue-DIkVcfK4.js +167 -0
- package/dist/vercel.d.ts +6 -6
- package/dist/vercel.js +14 -6
- package/dist/version-CnXZ614E.d.ts +5 -0
- package/dist/version-DxcWdoKa.js +8 -0
- package/dist/version.d.ts +2 -4
- package/dist/version.js +2 -6
- package/dist/web-authorization-RMBxAR_t.js +76 -0
- package/dist/web-input-3EPfpf55.js +494 -0
- package/dist/work-fGofI7Op.js +509 -0
- package/dist/worker-CsOLq-mR.js +625 -0
- package/package.json +9 -9
- package/dist/agent-hooks-UXITLYOK.js +0 -51
- package/dist/catalog-runtime-BG3JLLK2.js +0 -14
- package/dist/chunk-2LYHWMR4.js +0 -46
- package/dist/chunk-2MK3QBGA.js +0 -321
- package/dist/chunk-3PGQSKJV.js +0 -692
- package/dist/chunk-3UI3WFTX.js +0 -519
- package/dist/chunk-3Y47VMHV.js +0 -6
- package/dist/chunk-4UO6FK4G.js +0 -64
- package/dist/chunk-5NOPO64K.js +0 -198
- package/dist/chunk-5PKWYPFT.js +0 -2943
- package/dist/chunk-63NKKFJL.js +0 -1228
- package/dist/chunk-6AYH6252.js +0 -787
- package/dist/chunk-6Y5B5PEX.js +0 -80
- package/dist/chunk-7DHZP5KJ.js +0 -333
- package/dist/chunk-7H2VZTWN.js +0 -103
- package/dist/chunk-7NL7T6VU.js +0 -406
- package/dist/chunk-A76OIMYN.js +0 -102
- package/dist/chunk-AWMS3SPP.js +0 -58
- package/dist/chunk-B3LY5RCL.js +0 -229
- package/dist/chunk-BD2KT3OB.js +0 -36
- package/dist/chunk-BXCX5QKA.js +0 -2086
- package/dist/chunk-C6VVF43U.js +0 -81
- package/dist/chunk-CEMMLV5L.js +0 -98
- package/dist/chunk-CM5HNV2W.js +0 -131
- package/dist/chunk-DA2KGZZI.js +0 -13
- package/dist/chunk-DBVY6HLX.js +0 -64
- package/dist/chunk-DIB4GX3G.js +0 -8
- package/dist/chunk-DKIJ2RP5.js +0 -68
- package/dist/chunk-DL2KLKVH.js +0 -931
- package/dist/chunk-DWPN6SCH.js +0 -172
- package/dist/chunk-ELWOYVOY.js +0 -206
- package/dist/chunk-EWT7H65X.js +0 -506
- package/dist/chunk-FA6POGLH.js +0 -70
- package/dist/chunk-HMREVIO2.js +0 -135
- package/dist/chunk-IIWUT4FO.js +0 -820
- package/dist/chunk-JAL3CH4G.js +0 -27
- package/dist/chunk-JSJQEWBJ.js +0 -3295
- package/dist/chunk-KHKKHDLD.js +0 -387
- package/dist/chunk-LA6R4A7Q.js +0 -706
- package/dist/chunk-LOOU2DUR.js +0 -20428
- package/dist/chunk-M6FHWXE3.js +0 -700
- package/dist/chunk-M76HMB7M.js +0 -1856
- package/dist/chunk-MAGNK5J4.js +0 -1520
- package/dist/chunk-MTM5IFCY.js +0 -1190
- package/dist/chunk-N7TMSOZH.js +0 -235
- package/dist/chunk-NHJU2ZAL.js +0 -56
- package/dist/chunk-NSXKRGRT.js +0 -717
- package/dist/chunk-OWMPFXJA.js +0 -1764
- package/dist/chunk-PDO5BLNM.js +0 -22
- package/dist/chunk-PEQXYSSE.js +0 -45
- package/dist/chunk-PW4P4JYF.js +0 -1135
- package/dist/chunk-QB54UQUS.js +0 -320
- package/dist/chunk-QP3OGJXF.js +0 -105
- package/dist/chunk-RC745GGZ.js +0 -122
- package/dist/chunk-REOIRYPR.js +0 -37
- package/dist/chunk-SCOZRLZ4.js +0 -164
- package/dist/chunk-TXJSXBV5.js +0 -1433
- package/dist/chunk-TXKUKMVO.js +0 -417
- package/dist/chunk-U2GPUYAL.js +0 -3656
- package/dist/chunk-UIE3R5XU.js +0 -17
- package/dist/chunk-UXPG6ZIN.js +0 -84
- package/dist/chunk-VFUK3X5B.js +0 -50
- package/dist/chunk-WQSHDTTI.js +0 -1163
- package/dist/chunk-X6GDDWQH.js +0 -60
- package/dist/chunk-Y2CM7HXH.js +0 -111
- package/dist/chunk-Y7X25LFY.js +0 -15
- package/dist/chunk-YDUR3ZYW.js +0 -10
- package/dist/conversation-work-VIOQ5ZF3.js +0 -54
- package/dist/db-3L2MCVRI.js +0 -26
- package/dist/discovery-DWVDM7UK.js +0 -44
- package/dist/experimental-P5FZM4CU.js +0 -12
- package/dist/oauth-callback-server-YVV6C6T2.js +0 -243
- package/dist/oauth-relay-5XPVRPYB.js +0 -8
- package/dist/plugins-YTYJMMJF.js +0 -14
- package/dist/runner-LQ7RETDP.js +0 -522
- package/dist/sandbox-egress-signals-FIL7EGPR.js +0 -73
- package/dist/sentry-MB3NL3IR.js +0 -42
- package/dist/skills-XRWYEDCD.js +0 -27
- package/dist/spawn-RC6SJUAI.js +0 -42
- package/dist/task-runner-7DY7ARYH.js +0 -36
- package/dist/validation-7MNQGCJA.js +0 -17
- package/dist/work-COALHUCG.js +0 -51
- package/dist/worker-6MURZIPD.js +0 -24
|
@@ -0,0 +1,625 @@
|
|
|
1
|
+
import { t as __exportAll } from "./rolldown-runtime-B4FMCO8f.js";
|
|
2
|
+
import { P as isProviderRetryError, i as getChatConfig } from "./config-nGCW1OTG.js";
|
|
3
|
+
import { S as withLogContext, d as logInfo, f as logWarn, u as logException } from "./logging-BzAk1S_s.js";
|
|
4
|
+
import { At as releaseConversationWork, Bt as isInvalidConversationRecordError, Ct as ensureConversationWake, Mt as requestAnotherSlice, Ot as recordAttemptFailure, Pt as startConversationWork, St as drainConversationMailbox, Tt as getConversationWorkState, bt as countPendingConversationMessages, gt as clearConsumedConversationWake, ht as checkInConversationWork, kt as recordConversationRetry, pt as beginConversationResume, ut as ackMessages, vt as completeConversationStop, xt as deadLetterAttempt, yt as completeConversationWork, zt as isFinalAttempt } from "./stats-DTUbAb88.js";
|
|
5
|
+
import { c as ConversationQueueMessageRejectedError } from "./vercel-queue-DIkVcfK4.js";
|
|
6
|
+
import { a as getTurnRequestDeadline } from "./job-queue-Drn_LXlM.js";
|
|
7
|
+
//#region src/chat/task-execution/worker.ts
|
|
8
|
+
var worker_exports = /* @__PURE__ */ __exportAll({
|
|
9
|
+
CONVERSATION_WORK_DEFER_DELAY_MS: () => CONVERSATION_WORK_DEFER_DELAY_MS,
|
|
10
|
+
processConversationWork: () => processConversationWork
|
|
11
|
+
});
|
|
12
|
+
const CONVERSATION_WORK_DEFER_DELAY_MS = 15e3;
|
|
13
|
+
const CONVERSATION_STOP_CHECK_INTERVAL_MS = 500;
|
|
14
|
+
function now(options) {
|
|
15
|
+
return options.nowMs?.() ?? Date.now();
|
|
16
|
+
}
|
|
17
|
+
function selectContiguousTurnBatch(messages) {
|
|
18
|
+
const first = messages[0];
|
|
19
|
+
if (!first) return [];
|
|
20
|
+
const nextTurnIndex = messages.findIndex((message) => message.input.authorId !== first.input.authorId || message.source !== first.source);
|
|
21
|
+
return messages.slice(0, nextTurnIndex === -1 ? messages.length : nextTurnIndex);
|
|
22
|
+
}
|
|
23
|
+
/** Prioritize interrupts while keeping each attempt to one Actor and Source. */
|
|
24
|
+
function selectAttemptMessages(work) {
|
|
25
|
+
const messages = work.messages;
|
|
26
|
+
const interrupts = messages.filter((message) => message.delivery === "interrupt");
|
|
27
|
+
if (interrupts.length > 0) return selectContiguousTurnBatch(interrupts);
|
|
28
|
+
return work.execution.status === "paused" ? [] : selectContiguousTurnBatch(messages);
|
|
29
|
+
}
|
|
30
|
+
function nudgeIdempotencyKey(reason, conversationId, nowMs) {
|
|
31
|
+
return `${reason}:${conversationId}:${nowMs}`;
|
|
32
|
+
}
|
|
33
|
+
async function requestLostLeaseRecovery(args) {
|
|
34
|
+
const before = await getConversationWorkState({
|
|
35
|
+
conversationId: args.conversationId,
|
|
36
|
+
state: args.options.state
|
|
37
|
+
});
|
|
38
|
+
const retry = await recordConversationRetry({
|
|
39
|
+
conversationId: args.conversationId,
|
|
40
|
+
leaseToken: args.leaseToken,
|
|
41
|
+
conversationStore: args.options.conversationStore,
|
|
42
|
+
nowMs: args.nowMs,
|
|
43
|
+
state: args.options.state
|
|
44
|
+
});
|
|
45
|
+
if (retry === "lost_lease") return;
|
|
46
|
+
if (retry === "stopped") {
|
|
47
|
+
logException(/* @__PURE__ */ new Error("Conversation work stopped after repeated failed attempts"), "conversation.work.retry.exhausted", {
|
|
48
|
+
"app.run.id": before?.execution.runId ?? "unknown",
|
|
49
|
+
"app.worker.last_progress_at_ms": before?.execution.lastProgressAtMs ?? before?.createdAtMs ?? 0,
|
|
50
|
+
"app.worker.retry_count": (before?.execution.retryCount ?? 0) + 1
|
|
51
|
+
});
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
if (!await requestAnotherSlice({
|
|
55
|
+
conversationId: args.conversationId,
|
|
56
|
+
destination: args.destination,
|
|
57
|
+
leaseToken: args.leaseToken,
|
|
58
|
+
conversationStore: args.options.conversationStore,
|
|
59
|
+
nowMs: args.nowMs,
|
|
60
|
+
state: args.options.state
|
|
61
|
+
})) return;
|
|
62
|
+
if (!await releaseConversationWork({
|
|
63
|
+
conversationId: args.conversationId,
|
|
64
|
+
leaseToken: args.leaseToken,
|
|
65
|
+
conversationStore: args.options.conversationStore,
|
|
66
|
+
nowMs: args.nowMs,
|
|
67
|
+
state: args.options.state
|
|
68
|
+
})) return;
|
|
69
|
+
await ensureConversationWake({
|
|
70
|
+
conversationId: args.conversationId,
|
|
71
|
+
conversationStore: args.options.conversationStore,
|
|
72
|
+
idempotencyKey: nudgeIdempotencyKey("lost_lease", args.conversationId, args.nowMs),
|
|
73
|
+
nowMs: args.nowMs,
|
|
74
|
+
queue: args.options.queue,
|
|
75
|
+
replaceExistingWake: true,
|
|
76
|
+
state: args.options.state
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Record one failed delivery attempt and surface dead-lettered messages.
|
|
81
|
+
*
|
|
82
|
+
* Consumption is logged here so every dead-lettered message leaves a
|
|
83
|
+
* `conversation.work.dead_lettered` trail with its terminal attempt count.
|
|
84
|
+
*/
|
|
85
|
+
async function recordFailedDeliveryAttempt(args) {
|
|
86
|
+
const failure = await recordAttemptFailure({
|
|
87
|
+
conversationId: args.conversationId,
|
|
88
|
+
inboundMessageIds: args.messageIds,
|
|
89
|
+
leaseToken: args.leaseToken,
|
|
90
|
+
conversationStore: args.options.conversationStore,
|
|
91
|
+
nowMs: args.nowMs,
|
|
92
|
+
state: args.options.state
|
|
93
|
+
});
|
|
94
|
+
for (const message of failure.deadLetteredMessages) logWarn("conversation.work.dead_lettered", {
|
|
95
|
+
"app.conversation.source": message.source,
|
|
96
|
+
"app.inbound.attempt_count": message.attemptCount ?? 0,
|
|
97
|
+
"app.inbound.message_id": message.inboundMessageId,
|
|
98
|
+
"app.inbound.pending_count": failure.pendingCount
|
|
99
|
+
});
|
|
100
|
+
return failure;
|
|
101
|
+
}
|
|
102
|
+
/** True only when this attempt dead-lettered messages and left no further pending work. */
|
|
103
|
+
function isTerminalFailure(failure) {
|
|
104
|
+
return failure.status === "recorded" && failure.deadLetteredMessages.length > 0 && failure.pendingCount === 0;
|
|
105
|
+
}
|
|
106
|
+
function logRetryExhausted(args) {
|
|
107
|
+
logException(args.error, "conversation.work.retry.exhausted", {
|
|
108
|
+
"app.run.id": args.work.execution.runId ?? "unknown",
|
|
109
|
+
"app.worker.last_progress_at_ms": args.work.execution.lastProgressAtMs ?? args.work.createdAtMs,
|
|
110
|
+
"app.worker.retry_count": args.retryCount
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
function startLeaseCheckIn(args) {
|
|
114
|
+
const timer = setInterval(() => {
|
|
115
|
+
const nowMs = now(args.options);
|
|
116
|
+
checkInConversationWork({
|
|
117
|
+
conversationId: args.conversationId,
|
|
118
|
+
leaseToken: args.leaseToken,
|
|
119
|
+
conversationStore: args.options.conversationStore,
|
|
120
|
+
nowMs,
|
|
121
|
+
state: args.options.state
|
|
122
|
+
}).then((checkedIn) => {
|
|
123
|
+
if (!checkedIn) {
|
|
124
|
+
args.onLostLease();
|
|
125
|
+
logWarn("conversation.work.check_in.failed");
|
|
126
|
+
}
|
|
127
|
+
}, (error) => {
|
|
128
|
+
logException(error, "conversation.work.check_in.failed");
|
|
129
|
+
});
|
|
130
|
+
}, args.options.checkInIntervalMs ?? 15e3);
|
|
131
|
+
timer.unref?.();
|
|
132
|
+
return timer;
|
|
133
|
+
}
|
|
134
|
+
/** Poll shared state only when a worker adapter asks to observe remote stops. */
|
|
135
|
+
function createConversationStopSignal(args) {
|
|
136
|
+
const controller = new AbortController();
|
|
137
|
+
let checking = false;
|
|
138
|
+
let failureCaptured = false;
|
|
139
|
+
let listening = false;
|
|
140
|
+
let timer;
|
|
141
|
+
const requestStop = () => {
|
|
142
|
+
if (!controller.signal.aborted) controller.abort(/* @__PURE__ */ new Error("Conversation work stopped"));
|
|
143
|
+
};
|
|
144
|
+
if (args.initialStopRunId === args.runId) requestStop();
|
|
145
|
+
const check = async () => {
|
|
146
|
+
if (checking || controller.signal.aborted) return;
|
|
147
|
+
checking = true;
|
|
148
|
+
try {
|
|
149
|
+
if ((await getConversationWorkState({
|
|
150
|
+
conversationId: args.conversationId,
|
|
151
|
+
state: args.options.state
|
|
152
|
+
}))?.execution.stop?.runId === args.runId) requestStop();
|
|
153
|
+
} catch (error) {
|
|
154
|
+
if (!failureCaptured) {
|
|
155
|
+
failureCaptured = true;
|
|
156
|
+
logException(error, "conversation.work.stop_check.failed");
|
|
157
|
+
}
|
|
158
|
+
} finally {
|
|
159
|
+
checking = false;
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
return {
|
|
163
|
+
close() {
|
|
164
|
+
if (timer) clearInterval(timer);
|
|
165
|
+
},
|
|
166
|
+
isEnabled() {
|
|
167
|
+
return listening;
|
|
168
|
+
},
|
|
169
|
+
wasObserved() {
|
|
170
|
+
return listening && controller.signal.aborted;
|
|
171
|
+
},
|
|
172
|
+
signal() {
|
|
173
|
+
listening = true;
|
|
174
|
+
if (!timer && !controller.signal.aborted) {
|
|
175
|
+
timer = setInterval(() => void check(), CONVERSATION_STOP_CHECK_INTERVAL_MS);
|
|
176
|
+
timer.unref?.();
|
|
177
|
+
}
|
|
178
|
+
return controller.signal;
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
/** Process one queue wake-up for a conversation. */
|
|
183
|
+
async function processConversationWork(message, options) {
|
|
184
|
+
return withLogContext({ conversationId: message.conversationId }, () => processConversationWorkInContext(message, options));
|
|
185
|
+
}
|
|
186
|
+
async function processConversationWorkInContext(message, options) {
|
|
187
|
+
const conversationId = message.conversationId;
|
|
188
|
+
let initial;
|
|
189
|
+
try {
|
|
190
|
+
initial = await getConversationWorkState({
|
|
191
|
+
conversationId,
|
|
192
|
+
state: options.state
|
|
193
|
+
});
|
|
194
|
+
} catch (error) {
|
|
195
|
+
if (isInvalidConversationRecordError(error)) throw new ConversationQueueMessageRejectedError("invalid_record", `Conversation record failed validation for ${conversationId}`, { conversationId });
|
|
196
|
+
throw error;
|
|
197
|
+
}
|
|
198
|
+
if (!initial || countPendingConversationMessages(initial) === 0 && initial.execution.status === "idle" && !initial.execution.lease) {
|
|
199
|
+
if (initial) await clearConsumedConversationWake({
|
|
200
|
+
conversationId,
|
|
201
|
+
conversationStore: options.conversationStore,
|
|
202
|
+
nowMs: now(options),
|
|
203
|
+
state: options.state
|
|
204
|
+
});
|
|
205
|
+
return { status: "no_work" };
|
|
206
|
+
}
|
|
207
|
+
let destination = initial.destination;
|
|
208
|
+
if (!destination && options.conversationStore) {
|
|
209
|
+
const stored = await options.conversationStore.get({ conversationId });
|
|
210
|
+
if (stored && !stored.parentConversationId) destination = stored.destination;
|
|
211
|
+
}
|
|
212
|
+
const lease = await startConversationWork({
|
|
213
|
+
conversationId,
|
|
214
|
+
conversationStore: options.conversationStore,
|
|
215
|
+
nowMs: now(options),
|
|
216
|
+
state: options.state
|
|
217
|
+
});
|
|
218
|
+
if (lease.status === "no_work") {
|
|
219
|
+
await clearConsumedConversationWake({
|
|
220
|
+
conversationId,
|
|
221
|
+
conversationStore: options.conversationStore,
|
|
222
|
+
nowMs: now(options),
|
|
223
|
+
state: options.state
|
|
224
|
+
});
|
|
225
|
+
return { status: "no_work" };
|
|
226
|
+
}
|
|
227
|
+
if (lease.status === "active") {
|
|
228
|
+
const nudgeNowMs = now(options);
|
|
229
|
+
await ensureConversationWake({
|
|
230
|
+
conversationId,
|
|
231
|
+
conversationStore: options.conversationStore,
|
|
232
|
+
delayMs: CONVERSATION_WORK_DEFER_DELAY_MS,
|
|
233
|
+
idempotencyKey: nudgeIdempotencyKey("active", conversationId, nudgeNowMs),
|
|
234
|
+
nowMs: nudgeNowMs,
|
|
235
|
+
queue: options.queue,
|
|
236
|
+
replaceExistingWake: true,
|
|
237
|
+
state: options.state
|
|
238
|
+
});
|
|
239
|
+
logInfo("conversation.work.nudge.deferred", { "app.lease.expires_at_ms": lease.leaseExpiresAtMs });
|
|
240
|
+
return { status: "active" };
|
|
241
|
+
}
|
|
242
|
+
const startedAtMs = now(options);
|
|
243
|
+
const softYieldDeadlineMs = startedAtMs + (options.softYieldAfterMs ?? getChatConfig().conversationWorkSoftYieldAfterMs);
|
|
244
|
+
let attemptMessageIds = [];
|
|
245
|
+
let attemptSelectedMessageIds = /* @__PURE__ */ new Set();
|
|
246
|
+
let attemptStartMessageIds = /* @__PURE__ */ new Set();
|
|
247
|
+
let failedWithoutProgress = false;
|
|
248
|
+
let leaseLost = false;
|
|
249
|
+
const markLeaseLost = () => {
|
|
250
|
+
leaseLost = true;
|
|
251
|
+
};
|
|
252
|
+
const timer = startLeaseCheckIn({
|
|
253
|
+
conversationId,
|
|
254
|
+
leaseToken: lease.leaseToken,
|
|
255
|
+
onLostLease: markLeaseLost,
|
|
256
|
+
options
|
|
257
|
+
});
|
|
258
|
+
logInfo("conversation.work.lease.acquired", {
|
|
259
|
+
"app.lease.expires_at_ms": lease.leaseExpiresAtMs,
|
|
260
|
+
"app.worker.soft_yield_deadline_ms": softYieldDeadlineMs
|
|
261
|
+
});
|
|
262
|
+
const drain = (handle) => drainConversationMailbox({
|
|
263
|
+
conversationId,
|
|
264
|
+
leaseToken: lease.leaseToken,
|
|
265
|
+
conversationStore: options.conversationStore,
|
|
266
|
+
handle: async (messages) => {
|
|
267
|
+
const candidates = messages.filter((message) => message.delivery === "interrupt" && (attemptSelectedMessageIds.has(message.inboundMessageId) || !attemptStartMessageIds.has(message.inboundMessageId)));
|
|
268
|
+
if (candidates.length === 0) return [];
|
|
269
|
+
return await handle(candidates) ?? candidates.map((message) => message.inboundMessageId);
|
|
270
|
+
},
|
|
271
|
+
nowMs: now(options),
|
|
272
|
+
state: options.state
|
|
273
|
+
});
|
|
274
|
+
const requestDeadlineAtMs = getTurnRequestDeadline()?.deadlineAtMs;
|
|
275
|
+
const shouldYield = () => leaseLost || now(options) >= softYieldDeadlineMs || requestDeadlineAtMs !== void 0 && Date.now() >= requestDeadlineAtMs;
|
|
276
|
+
const checkIn = async () => {
|
|
277
|
+
const checkedIn = await checkInConversationWork({
|
|
278
|
+
conversationId,
|
|
279
|
+
leaseToken: lease.leaseToken,
|
|
280
|
+
conversationStore: options.conversationStore,
|
|
281
|
+
nowMs: now(options),
|
|
282
|
+
state: options.state
|
|
283
|
+
});
|
|
284
|
+
if (!checkedIn) markLeaseLost();
|
|
285
|
+
return checkedIn;
|
|
286
|
+
};
|
|
287
|
+
const yieldWork = async () => {
|
|
288
|
+
const yieldNowMs = now(options);
|
|
289
|
+
await ensureConversationWake({
|
|
290
|
+
conversationId,
|
|
291
|
+
conversationStore: options.conversationStore,
|
|
292
|
+
idempotencyKey: nudgeIdempotencyKey("yield", conversationId, yieldNowMs),
|
|
293
|
+
nowMs: yieldNowMs,
|
|
294
|
+
queue: options.queue,
|
|
295
|
+
replaceExistingWake: true,
|
|
296
|
+
state: options.state
|
|
297
|
+
});
|
|
298
|
+
if (!await releaseConversationWork({
|
|
299
|
+
conversationId,
|
|
300
|
+
leaseToken: lease.leaseToken,
|
|
301
|
+
conversationStore: options.conversationStore,
|
|
302
|
+
nowMs: yieldNowMs,
|
|
303
|
+
state: options.state
|
|
304
|
+
})) return { status: "lost_lease" };
|
|
305
|
+
logInfo("conversation.work.yielded", {
|
|
306
|
+
"app.worker.elapsed_ms": now(options) - startedAtMs,
|
|
307
|
+
"app.worker.soft_yield_deadline_ms": softYieldDeadlineMs
|
|
308
|
+
});
|
|
309
|
+
return { status: "yielded" };
|
|
310
|
+
};
|
|
311
|
+
try {
|
|
312
|
+
let hasRun = false;
|
|
313
|
+
let resumeIfStopped = false;
|
|
314
|
+
while (true) {
|
|
315
|
+
attemptMessageIds = [];
|
|
316
|
+
const leasedWork = await getConversationWorkState({
|
|
317
|
+
conversationId,
|
|
318
|
+
state: options.state
|
|
319
|
+
});
|
|
320
|
+
if (!leasedWork || leasedWork.lease?.leaseToken !== lease.leaseToken || leaseLost) {
|
|
321
|
+
markLeaseLost();
|
|
322
|
+
await requestLostLeaseRecovery({
|
|
323
|
+
conversationId,
|
|
324
|
+
destination,
|
|
325
|
+
leaseToken: lease.leaseToken,
|
|
326
|
+
nowMs: now(options),
|
|
327
|
+
options
|
|
328
|
+
});
|
|
329
|
+
return { status: "lost_lease" };
|
|
330
|
+
}
|
|
331
|
+
const resumePending = leasedWork.execution.status === "paused";
|
|
332
|
+
const attemptMessages = selectAttemptMessages(leasedWork);
|
|
333
|
+
const runId = leasedWork.execution.runId;
|
|
334
|
+
if (!runId) throw new Error(`Conversation run is missing for ${conversationId}`);
|
|
335
|
+
attemptStartMessageIds = new Set(leasedWork.messages.map((message) => message.inboundMessageId));
|
|
336
|
+
if (hasRun && shouldYield()) {
|
|
337
|
+
if (resumePending) return await yieldWork();
|
|
338
|
+
break;
|
|
339
|
+
}
|
|
340
|
+
if (resumePending && attemptMessages.length === 0) {
|
|
341
|
+
if (!await beginConversationResume({
|
|
342
|
+
conversationId,
|
|
343
|
+
leaseToken: lease.leaseToken,
|
|
344
|
+
conversationStore: options.conversationStore,
|
|
345
|
+
nowMs: now(options),
|
|
346
|
+
state: options.state
|
|
347
|
+
})) {
|
|
348
|
+
markLeaseLost();
|
|
349
|
+
await requestLostLeaseRecovery({
|
|
350
|
+
conversationId,
|
|
351
|
+
destination,
|
|
352
|
+
leaseToken: lease.leaseToken,
|
|
353
|
+
nowMs: now(options),
|
|
354
|
+
options
|
|
355
|
+
});
|
|
356
|
+
return { status: "lost_lease" };
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
attemptMessageIds = attemptMessages.map((message) => message.inboundMessageId);
|
|
360
|
+
attemptSelectedMessageIds = new Set(attemptMessageIds);
|
|
361
|
+
const ack = async () => {
|
|
362
|
+
if (!await ackMessages({
|
|
363
|
+
conversationId,
|
|
364
|
+
inboundMessageIds: attemptMessageIds,
|
|
365
|
+
leaseToken: lease.leaseToken,
|
|
366
|
+
conversationStore: options.conversationStore,
|
|
367
|
+
nowMs: now(options),
|
|
368
|
+
state: options.state
|
|
369
|
+
})) {
|
|
370
|
+
markLeaseLost();
|
|
371
|
+
throw new Error(`Conversation work lease lost before inbox ack for ${conversationId}`);
|
|
372
|
+
}
|
|
373
|
+
};
|
|
374
|
+
const stop = createConversationStopSignal({
|
|
375
|
+
conversationId,
|
|
376
|
+
initialStopRunId: leasedWork.execution.stop?.runId,
|
|
377
|
+
options,
|
|
378
|
+
runId
|
|
379
|
+
});
|
|
380
|
+
const workerContext = {
|
|
381
|
+
attempt: {
|
|
382
|
+
ack,
|
|
383
|
+
conversationId,
|
|
384
|
+
destination,
|
|
385
|
+
drain,
|
|
386
|
+
isFinalAttempt: attemptMessages.some((message) => isFinalAttempt(message)),
|
|
387
|
+
messages: attemptMessages
|
|
388
|
+
},
|
|
389
|
+
conversationId,
|
|
390
|
+
destination,
|
|
391
|
+
shouldYield,
|
|
392
|
+
stopSignal: stop.signal,
|
|
393
|
+
checkIn
|
|
394
|
+
};
|
|
395
|
+
let result;
|
|
396
|
+
try {
|
|
397
|
+
result = await options.run(workerContext);
|
|
398
|
+
} finally {
|
|
399
|
+
stop.close();
|
|
400
|
+
}
|
|
401
|
+
resumeIfStopped ||= stop.isEnabled();
|
|
402
|
+
hasRun = true;
|
|
403
|
+
if (result.status === "lost_lease") {
|
|
404
|
+
await requestLostLeaseRecovery({
|
|
405
|
+
conversationId,
|
|
406
|
+
destination,
|
|
407
|
+
leaseToken: lease.leaseToken,
|
|
408
|
+
nowMs: now(options),
|
|
409
|
+
options
|
|
410
|
+
});
|
|
411
|
+
return { status: "lost_lease" };
|
|
412
|
+
}
|
|
413
|
+
if (leaseLost) {
|
|
414
|
+
await requestLostLeaseRecovery({
|
|
415
|
+
conversationId,
|
|
416
|
+
destination,
|
|
417
|
+
leaseToken: lease.leaseToken,
|
|
418
|
+
nowMs: now(options),
|
|
419
|
+
options
|
|
420
|
+
});
|
|
421
|
+
return { status: "lost_lease" };
|
|
422
|
+
}
|
|
423
|
+
if (result.status === "completed" && stop.wasObserved()) {
|
|
424
|
+
if ((await completeConversationStop({
|
|
425
|
+
conversationId,
|
|
426
|
+
conversationStore: options.conversationStore,
|
|
427
|
+
leaseToken: lease.leaseToken,
|
|
428
|
+
nowMs: now(options),
|
|
429
|
+
runId,
|
|
430
|
+
state: options.state
|
|
431
|
+
})).status === "lost_lease") {
|
|
432
|
+
markLeaseLost();
|
|
433
|
+
await requestLostLeaseRecovery({
|
|
434
|
+
conversationId,
|
|
435
|
+
destination,
|
|
436
|
+
leaseToken: lease.leaseToken,
|
|
437
|
+
nowMs: now(options),
|
|
438
|
+
options
|
|
439
|
+
});
|
|
440
|
+
return { status: "lost_lease" };
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
if (result.status === "yielded") {
|
|
444
|
+
if (!await requestAnotherSlice({
|
|
445
|
+
conversationId,
|
|
446
|
+
destination,
|
|
447
|
+
leaseToken: lease.leaseToken,
|
|
448
|
+
conversationStore: options.conversationStore,
|
|
449
|
+
nowMs: now(options),
|
|
450
|
+
state: options.state
|
|
451
|
+
})) return { status: "lost_lease" };
|
|
452
|
+
return await yieldWork();
|
|
453
|
+
}
|
|
454
|
+
if (result.status === "deferred") {
|
|
455
|
+
const deferredNowMs = now(options);
|
|
456
|
+
if (!await releaseConversationWork({
|
|
457
|
+
conversationId,
|
|
458
|
+
leaseToken: lease.leaseToken,
|
|
459
|
+
conversationStore: options.conversationStore,
|
|
460
|
+
nowMs: deferredNowMs,
|
|
461
|
+
state: options.state
|
|
462
|
+
})) return { status: "lost_lease" };
|
|
463
|
+
return (await ensureConversationWake({
|
|
464
|
+
conversationId,
|
|
465
|
+
conversationStore: options.conversationStore,
|
|
466
|
+
idempotencyKey: nudgeIdempotencyKey("deferred", conversationId, deferredNowMs),
|
|
467
|
+
nowMs: deferredNowMs,
|
|
468
|
+
queue: options.queue,
|
|
469
|
+
state: options.state
|
|
470
|
+
})).status === "enqueued" ? { status: "pending_requeued" } : { status: "completed" };
|
|
471
|
+
}
|
|
472
|
+
if (attemptMessageIds.length > 0) {
|
|
473
|
+
const failure = await recordFailedDeliveryAttempt({
|
|
474
|
+
conversationId,
|
|
475
|
+
leaseToken: lease.leaseToken,
|
|
476
|
+
nowMs: now(options),
|
|
477
|
+
messageIds: attemptMessageIds,
|
|
478
|
+
options
|
|
479
|
+
});
|
|
480
|
+
if (isTerminalFailure(failure)) {
|
|
481
|
+
if (failure.retryCount >= 5) logRetryExhausted({
|
|
482
|
+
error: /* @__PURE__ */ new Error("Conversation work stopped after repeated failed attempts"),
|
|
483
|
+
retryCount: failure.retryCount,
|
|
484
|
+
work: leasedWork
|
|
485
|
+
});
|
|
486
|
+
await deadLetterAttempt({
|
|
487
|
+
conversationId,
|
|
488
|
+
leaseToken: lease.leaseToken,
|
|
489
|
+
conversationStore: options.conversationStore,
|
|
490
|
+
nowMs: now(options),
|
|
491
|
+
state: options.state
|
|
492
|
+
});
|
|
493
|
+
return { status: "failed" };
|
|
494
|
+
}
|
|
495
|
+
if (failure.status === "lost_lease") return { status: "lost_lease" };
|
|
496
|
+
if (failure.status === "recorded") {
|
|
497
|
+
failedWithoutProgress = true;
|
|
498
|
+
break;
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
if (result.status === "paused") break;
|
|
502
|
+
const next = await getConversationWorkState({
|
|
503
|
+
conversationId,
|
|
504
|
+
state: options.state
|
|
505
|
+
});
|
|
506
|
+
if (!next || next.lease?.leaseToken !== lease.leaseToken) return { status: "lost_lease" };
|
|
507
|
+
if (next.execution.status !== "paused" && countPendingConversationMessages(next) === 0) break;
|
|
508
|
+
}
|
|
509
|
+
const completion = await completeConversationWork({
|
|
510
|
+
conversationId,
|
|
511
|
+
leaseToken: lease.leaseToken,
|
|
512
|
+
madeProgress: !failedWithoutProgress,
|
|
513
|
+
resumeIfStopped,
|
|
514
|
+
conversationStore: options.conversationStore,
|
|
515
|
+
nowMs: now(options),
|
|
516
|
+
state: options.state
|
|
517
|
+
});
|
|
518
|
+
if (completion === "lost_lease") return { status: "lost_lease" };
|
|
519
|
+
if (completion === "pending") {
|
|
520
|
+
const nudgeNowMs = now(options);
|
|
521
|
+
return (await ensureConversationWake({
|
|
522
|
+
conversationId,
|
|
523
|
+
conversationStore: options.conversationStore,
|
|
524
|
+
idempotencyKey: nudgeIdempotencyKey("pending", conversationId, nudgeNowMs),
|
|
525
|
+
nowMs: nudgeNowMs,
|
|
526
|
+
queue: options.queue,
|
|
527
|
+
state: options.state
|
|
528
|
+
})).status === "enqueued" ? { status: "pending_requeued" } : { status: "completed" };
|
|
529
|
+
}
|
|
530
|
+
logInfo("conversation.work.completed", { "app.worker.elapsed_ms": now(options) - startedAtMs });
|
|
531
|
+
return { status: "completed" };
|
|
532
|
+
} catch (error) {
|
|
533
|
+
const errorNowMs = now(options);
|
|
534
|
+
let recoveryRecorded = false;
|
|
535
|
+
try {
|
|
536
|
+
const failure = attemptMessageIds.length > 0 ? await recordFailedDeliveryAttempt({
|
|
537
|
+
conversationId,
|
|
538
|
+
leaseToken: lease.leaseToken,
|
|
539
|
+
nowMs: errorNowMs,
|
|
540
|
+
messageIds: attemptMessageIds,
|
|
541
|
+
options
|
|
542
|
+
}) : void 0;
|
|
543
|
+
if (failure && isTerminalFailure(failure)) {
|
|
544
|
+
if (failure.retryCount >= 5) logRetryExhausted({
|
|
545
|
+
error,
|
|
546
|
+
retryCount: failure.retryCount,
|
|
547
|
+
work: initial
|
|
548
|
+
});
|
|
549
|
+
await deadLetterAttempt({
|
|
550
|
+
conversationId,
|
|
551
|
+
leaseToken: lease.leaseToken,
|
|
552
|
+
conversationStore: options.conversationStore,
|
|
553
|
+
nowMs: errorNowMs,
|
|
554
|
+
state: options.state
|
|
555
|
+
});
|
|
556
|
+
} else if (failure?.status === "recorded") {
|
|
557
|
+
await releaseConversationWork({
|
|
558
|
+
conversationId,
|
|
559
|
+
leaseToken: lease.leaseToken,
|
|
560
|
+
conversationStore: options.conversationStore,
|
|
561
|
+
nowMs: errorNowMs,
|
|
562
|
+
state: options.state
|
|
563
|
+
});
|
|
564
|
+
await ensureConversationWake({
|
|
565
|
+
conversationId,
|
|
566
|
+
conversationStore: options.conversationStore,
|
|
567
|
+
idempotencyKey: nudgeIdempotencyKey("error", conversationId, errorNowMs),
|
|
568
|
+
nowMs: errorNowMs,
|
|
569
|
+
queue: options.queue,
|
|
570
|
+
replaceExistingWake: true,
|
|
571
|
+
state: options.state
|
|
572
|
+
});
|
|
573
|
+
} else {
|
|
574
|
+
if ((failure ? failure.status : await recordConversationRetry({
|
|
575
|
+
conversationId,
|
|
576
|
+
leaseToken: lease.leaseToken,
|
|
577
|
+
conversationStore: options.conversationStore,
|
|
578
|
+
nowMs: errorNowMs,
|
|
579
|
+
state: options.state
|
|
580
|
+
})) === "stopped") {
|
|
581
|
+
logException(error, "conversation.work.retry.exhausted", {
|
|
582
|
+
"app.run.id": initial.execution.runId ?? "unknown",
|
|
583
|
+
"app.worker.last_progress_at_ms": initial.execution.lastProgressAtMs ?? initial.createdAtMs,
|
|
584
|
+
"app.worker.retry_count": failure?.retryCount ?? (initial.execution.retryCount ?? 0) + 1
|
|
585
|
+
});
|
|
586
|
+
recoveryRecorded = true;
|
|
587
|
+
return { status: "failed" };
|
|
588
|
+
}
|
|
589
|
+
if (await requestAnotherSlice({
|
|
590
|
+
conversationId,
|
|
591
|
+
destination,
|
|
592
|
+
leaseToken: lease.leaseToken,
|
|
593
|
+
conversationStore: options.conversationStore,
|
|
594
|
+
nowMs: errorNowMs,
|
|
595
|
+
state: options.state
|
|
596
|
+
})) await ensureConversationWake({
|
|
597
|
+
conversationId,
|
|
598
|
+
conversationStore: options.conversationStore,
|
|
599
|
+
idempotencyKey: nudgeIdempotencyKey("error", conversationId, errorNowMs),
|
|
600
|
+
nowMs: errorNowMs,
|
|
601
|
+
queue: options.queue,
|
|
602
|
+
replaceExistingWake: true,
|
|
603
|
+
state: options.state
|
|
604
|
+
});
|
|
605
|
+
await releaseConversationWork({
|
|
606
|
+
conversationId,
|
|
607
|
+
leaseToken: lease.leaseToken,
|
|
608
|
+
conversationStore: options.conversationStore,
|
|
609
|
+
nowMs: errorNowMs,
|
|
610
|
+
state: options.state
|
|
611
|
+
});
|
|
612
|
+
}
|
|
613
|
+
recoveryRecorded = true;
|
|
614
|
+
} catch (recoveryError) {
|
|
615
|
+
logException(recoveryError, "conversation.work.requeue.failed");
|
|
616
|
+
}
|
|
617
|
+
if (!isProviderRetryError(error)) logException(error, "conversation.work.failed", { "app.worker.elapsed_ms": now(options) - startedAtMs });
|
|
618
|
+
if (!recoveryRecorded) throw error;
|
|
619
|
+
return { status: "failed" };
|
|
620
|
+
} finally {
|
|
621
|
+
clearInterval(timer);
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
//#endregion
|
|
625
|
+
export { worker_exports as n, processConversationWork as t };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sentry/junior",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.204.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -85,7 +85,7 @@
|
|
|
85
85
|
"pg": "^8.16.3",
|
|
86
86
|
"yaml": "^2.9.0",
|
|
87
87
|
"zod": "^4.5.4",
|
|
88
|
-
"@sentry/junior-plugin-api": "0.
|
|
88
|
+
"@sentry/junior-plugin-api": "0.204.0"
|
|
89
89
|
},
|
|
90
90
|
"devDependencies": {
|
|
91
91
|
"@emnapi/core": "^1.10.0",
|
|
@@ -100,20 +100,20 @@
|
|
|
100
100
|
"msw": "^2.14.6",
|
|
101
101
|
"nitro": "3.0.260522-beta",
|
|
102
102
|
"oxlint": "^1.79.0",
|
|
103
|
-
"
|
|
103
|
+
"tsdown": "0.23.0",
|
|
104
104
|
"typescript": "^6.0.3",
|
|
105
105
|
"vercel": "^54.4.0",
|
|
106
106
|
"vitest": "^4.1.7",
|
|
107
|
-
"@sentry/junior-
|
|
108
|
-
"@sentry/junior-
|
|
109
|
-
"@sentry/junior-
|
|
107
|
+
"@sentry/junior-github": "0.204.0",
|
|
108
|
+
"@sentry/junior-memory": "0.204.0",
|
|
109
|
+
"@sentry/junior-testing": "0.0.0"
|
|
110
110
|
},
|
|
111
111
|
"scripts": {
|
|
112
|
-
"build": "
|
|
112
|
+
"build": "tsdown",
|
|
113
113
|
"acp:smoke": "pnpm exec tsx scripts/acp-smoke.ts",
|
|
114
114
|
"db:generate": "pnpm exec drizzle-kit generate --config drizzle.config.ts",
|
|
115
|
-
"lint": "oxlint --config .oxlintrc.json --deny-warnings src tests scripts bin
|
|
116
|
-
"lint:fix": "oxlint --config .oxlintrc.json --deny-warnings --fix src tests scripts bin
|
|
115
|
+
"lint": "oxlint --config .oxlintrc.json --deny-warnings src tests scripts bin tsdown.config.ts && depcruise --config .dependency-cruiser.mjs src/chat",
|
|
116
|
+
"lint:fix": "oxlint --config .oxlintrc.json --deny-warnings --fix src tests scripts bin tsdown.config.ts",
|
|
117
117
|
"test": "vitest run --maxWorkers=4",
|
|
118
118
|
"test:watch": "vitest",
|
|
119
119
|
"typecheck": "tsc --noEmit",
|