@sentry/junior 0.69.0 → 0.71.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/app.js +513 -65
- package/dist/chat/plugins/credential-hooks.d.ts +19 -0
- package/dist/chat/sandbox/egress-credentials.d.ts +1 -0
- package/dist/chat/sandbox/egress-schemas.d.ts +1 -1
- package/dist/chat/state/conversation-details.d.ts +46 -0
- package/dist/chat/state/turn-session.d.ts +0 -3
- package/dist/chat/task-execution/store.d.ts +23 -0
- package/dist/chat/task-execution/worker.d.ts +1 -1
- package/dist/{chunk-N3MORKTH.js → chunk-HOGQL2H6.js} +123 -12
- package/dist/cli/init.js +18 -1
- package/dist/reporting.d.ts +6 -2
- package/dist/reporting.js +62 -21
- package/package.json +3 -3
package/dist/app.js
CHANGED
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
getAgentPlugins,
|
|
20
20
|
getAgentTurnSessionRecord,
|
|
21
21
|
getInterruptionMarker,
|
|
22
|
+
initConversationContext,
|
|
22
23
|
listAgentTurnSessionSummaries,
|
|
23
24
|
listAgentTurnSessionSummariesForConversation,
|
|
24
25
|
loadConnectedMcpProviders,
|
|
@@ -32,12 +33,13 @@ import {
|
|
|
32
33
|
resolveSlackChannelTypeFromMessage,
|
|
33
34
|
resolveSlackConversationContext,
|
|
34
35
|
setAgentPlugins,
|
|
36
|
+
setConversationTitle,
|
|
35
37
|
splitSlackReplyText,
|
|
36
38
|
truncateStatusText,
|
|
37
39
|
upsertAgentTurnSessionRecord,
|
|
38
40
|
validateAgentPlugins,
|
|
39
41
|
verifySlackDirectCredentialSubject
|
|
40
|
-
} from "./chunk-
|
|
42
|
+
} from "./chunk-HOGQL2H6.js";
|
|
41
43
|
import {
|
|
42
44
|
discoverSkills,
|
|
43
45
|
findSkillByName,
|
|
@@ -6545,6 +6547,7 @@ import {
|
|
|
6545
6547
|
agentPluginProviderAccountSchema
|
|
6546
6548
|
} from "@sentry/junior-plugin-api";
|
|
6547
6549
|
var finiteNumberSchema = z.number().refine(Number.isFinite);
|
|
6550
|
+
var httpStatusSchema = z.number().int().min(100).max(599);
|
|
6548
6551
|
var providerNameSchema = z.string().regex(/^[a-z][a-z0-9-]*$/);
|
|
6549
6552
|
var sandboxEgressGrantSchema = agentPluginGrantSchema;
|
|
6550
6553
|
var sandboxEgressCredentialContextSchema = z.object({
|
|
@@ -6584,7 +6587,7 @@ var sandboxEgressPermissionDeniedSignalSchema = z.object({
|
|
|
6584
6587
|
provider: providerNameSchema,
|
|
6585
6588
|
source: z.literal("upstream"),
|
|
6586
6589
|
sso: z.string().optional(),
|
|
6587
|
-
status:
|
|
6590
|
+
status: httpStatusSchema,
|
|
6588
6591
|
upstreamHost: z.string().min(1),
|
|
6589
6592
|
upstreamPath: z.string().min(1),
|
|
6590
6593
|
createdAtMs: finiteNumberSchema
|
|
@@ -8482,6 +8485,10 @@ function stringField(record, key) {
|
|
|
8482
8485
|
const value = record[key];
|
|
8483
8486
|
return typeof value === "string" ? value : "";
|
|
8484
8487
|
}
|
|
8488
|
+
function numberField(record, key) {
|
|
8489
|
+
const value = record[key];
|
|
8490
|
+
return typeof value === "number" && Number.isFinite(value) ? value : 0;
|
|
8491
|
+
}
|
|
8485
8492
|
function stringListField(record, key) {
|
|
8486
8493
|
const value = record[key];
|
|
8487
8494
|
return Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
|
|
@@ -8502,14 +8509,15 @@ function upstreamPermissionDeniedText(value) {
|
|
|
8502
8509
|
return void 0;
|
|
8503
8510
|
}
|
|
8504
8511
|
const signal = value.permission_denied;
|
|
8505
|
-
if (signal.source !== "upstream"
|
|
8512
|
+
if (signal.source !== "upstream") {
|
|
8506
8513
|
return void 0;
|
|
8507
8514
|
}
|
|
8508
8515
|
const provider = stringField(signal, "provider");
|
|
8509
8516
|
const message = stringField(signal, "message");
|
|
8510
8517
|
const upstreamHost = stringField(signal, "upstreamHost");
|
|
8511
8518
|
const upstreamPath2 = stringField(signal, "upstreamPath");
|
|
8512
|
-
|
|
8519
|
+
const status = numberField(signal, "status");
|
|
8520
|
+
if (!provider || !message || !upstreamHost || !upstreamPath2 || !status) {
|
|
8513
8521
|
return void 0;
|
|
8514
8522
|
}
|
|
8515
8523
|
const grant = isRecord3(signal.grant) ? signal.grant : {};
|
|
@@ -8535,7 +8543,7 @@ function upstreamPermissionDeniedText(value) {
|
|
|
8535
8543
|
...grantRequirements.map((item) => `- ${item}`)
|
|
8536
8544
|
] : [],
|
|
8537
8545
|
`Upstream: ${upstreamHost}${upstreamPath2}`,
|
|
8538
|
-
|
|
8546
|
+
`Status: ${status}`,
|
|
8539
8547
|
...acceptedPermissions ? [`Accepted provider permissions: ${acceptedPermissions}`] : [],
|
|
8540
8548
|
...sso ? [`Provider SSO: ${sso}`] : [],
|
|
8541
8549
|
...command ? [`Command: ${command}`] : [],
|
|
@@ -8953,12 +8961,41 @@ async function selectPluginGrant(input) {
|
|
|
8953
8961
|
plugin: { name: plugin.name },
|
|
8954
8962
|
log: createAgentPluginLogger(plugin.name),
|
|
8955
8963
|
request: {
|
|
8964
|
+
...input.bodyText !== void 0 ? { bodyText: input.bodyText } : {},
|
|
8956
8965
|
method: input.method,
|
|
8957
8966
|
url: input.upstreamUrl.toString()
|
|
8958
8967
|
}
|
|
8959
8968
|
});
|
|
8960
8969
|
return result === void 0 ? void 0 : parseGrant(result, plugin.name);
|
|
8961
8970
|
}
|
|
8971
|
+
async function onPluginEgressResponse(input) {
|
|
8972
|
+
const plugin = agentPluginFor(input.provider);
|
|
8973
|
+
const hook = plugin?.hooks?.onEgressResponse;
|
|
8974
|
+
if (!plugin || !hook) {
|
|
8975
|
+
return {};
|
|
8976
|
+
}
|
|
8977
|
+
let permissionDenied;
|
|
8978
|
+
await hook({
|
|
8979
|
+
plugin: { name: plugin.name },
|
|
8980
|
+
log: createAgentPluginLogger(plugin.name),
|
|
8981
|
+
grant: input.grant,
|
|
8982
|
+
permissionDenied(message) {
|
|
8983
|
+
const trimmed = message.trim();
|
|
8984
|
+
if (!trimmed) {
|
|
8985
|
+
throw new Error(
|
|
8986
|
+
`Plugin "${plugin.name}" onEgressResponse permissionDenied message is empty`
|
|
8987
|
+
);
|
|
8988
|
+
}
|
|
8989
|
+
permissionDenied = { message: trimmed };
|
|
8990
|
+
},
|
|
8991
|
+
request: {
|
|
8992
|
+
method: input.method,
|
|
8993
|
+
url: input.upstreamUrl.toString()
|
|
8994
|
+
},
|
|
8995
|
+
response: input.response
|
|
8996
|
+
});
|
|
8997
|
+
return permissionDenied ? { permissionDenied } : {};
|
|
8998
|
+
}
|
|
8962
8999
|
function hasEgressCredentialHooks(provider) {
|
|
8963
9000
|
const hooks = agentPluginFor(provider)?.hooks;
|
|
8964
9001
|
return Boolean(hooks?.grantForEgress || hooks?.issueCredential);
|
|
@@ -13914,6 +13951,7 @@ var CONVERSATION_WORK_MUTATION_RETRY_MS = 25;
|
|
|
13914
13951
|
var CONVERSATION_WORK_LEASE_TTL_MS = 9e4;
|
|
13915
13952
|
var CONVERSATION_WORK_CHECK_IN_INTERVAL_MS = 15e3;
|
|
13916
13953
|
var CONVERSATION_WORK_STALE_ENQUEUE_MS = 6e4;
|
|
13954
|
+
var CONVERSATION_WORK_MAX_CONSECUTIVE_FAILURES = 5;
|
|
13917
13955
|
function duplicateInboundNudgeIdempotencyKey(message, nowMs) {
|
|
13918
13956
|
return `duplicate:${message.conversationId}:${message.inboundMessageId}:${nowMs}`;
|
|
13919
13957
|
}
|
|
@@ -14035,14 +14073,18 @@ function normalizeWorkState(conversationId, value) {
|
|
|
14035
14073
|
messages,
|
|
14036
14074
|
needsRun: value.needsRun === true,
|
|
14037
14075
|
updatedAtMs,
|
|
14076
|
+
consecutiveFailureCount: toOptionalNumber(value.consecutiveFailureCount) ?? 0,
|
|
14038
14077
|
lastEnqueuedAtMs: toOptionalNumber(value.lastEnqueuedAtMs),
|
|
14039
|
-
|
|
14078
|
+
lastFailureAtMs: toOptionalNumber(value.lastFailureAtMs),
|
|
14079
|
+
lease: normalizeLease(value.lease),
|
|
14080
|
+
terminallyFailedAtMs: toOptionalNumber(value.terminallyFailedAtMs)
|
|
14040
14081
|
};
|
|
14041
14082
|
}
|
|
14042
14083
|
function emptyWorkState(args) {
|
|
14043
14084
|
return {
|
|
14044
14085
|
schemaVersion: CONVERSATION_WORK_SCHEMA_VERSION,
|
|
14045
14086
|
conversationId: args.conversationId,
|
|
14087
|
+
consecutiveFailureCount: 0,
|
|
14046
14088
|
destination: args.destination,
|
|
14047
14089
|
messages: [],
|
|
14048
14090
|
needsRun: false,
|
|
@@ -14056,6 +14098,9 @@ function pendingMessages(state) {
|
|
|
14056
14098
|
return state.messages.filter((message) => message.injectedAtMs === void 0).sort(compareMessages);
|
|
14057
14099
|
}
|
|
14058
14100
|
function shouldKeepIndexed(state) {
|
|
14101
|
+
if (state.terminallyFailedAtMs !== void 0) {
|
|
14102
|
+
return false;
|
|
14103
|
+
}
|
|
14059
14104
|
return state.needsRun || Boolean(state.lease) || pendingMessages(state).length > 0;
|
|
14060
14105
|
}
|
|
14061
14106
|
async function getConnectedState(stateAdapter) {
|
|
@@ -14180,6 +14225,9 @@ async function writeWorkState(state, work) {
|
|
|
14180
14225
|
}
|
|
14181
14226
|
}
|
|
14182
14227
|
function hasRunnableWork(state) {
|
|
14228
|
+
if (state.terminallyFailedAtMs !== void 0) {
|
|
14229
|
+
return false;
|
|
14230
|
+
}
|
|
14183
14231
|
return state.needsRun || pendingMessages(state).length > 0;
|
|
14184
14232
|
}
|
|
14185
14233
|
function assertSameConversationDestination(args) {
|
|
@@ -14229,8 +14277,11 @@ async function appendInboundMessage(args) {
|
|
|
14229
14277
|
}
|
|
14230
14278
|
const next = {
|
|
14231
14279
|
...current,
|
|
14280
|
+
consecutiveFailureCount: 0,
|
|
14281
|
+
lastFailureAtMs: void 0,
|
|
14232
14282
|
messages: [...current.messages, args.message].sort(compareMessages),
|
|
14233
14283
|
needsRun: true,
|
|
14284
|
+
terminallyFailedAtMs: void 0,
|
|
14234
14285
|
updatedAtMs: nowMs
|
|
14235
14286
|
};
|
|
14236
14287
|
await writeWorkState(state, next);
|
|
@@ -14398,6 +14449,8 @@ async function drainConversationMailbox(args) {
|
|
|
14398
14449
|
);
|
|
14399
14450
|
await writeWorkState(state, {
|
|
14400
14451
|
...current,
|
|
14452
|
+
consecutiveFailureCount: 0,
|
|
14453
|
+
lastFailureAtMs: void 0,
|
|
14401
14454
|
messages,
|
|
14402
14455
|
needsRun: hasPending,
|
|
14403
14456
|
updatedAtMs: nowMs
|
|
@@ -14429,6 +14482,8 @@ async function markConversationMessagesInjected(args) {
|
|
|
14429
14482
|
}
|
|
14430
14483
|
await writeWorkState(state, {
|
|
14431
14484
|
...current,
|
|
14485
|
+
consecutiveFailureCount: 0,
|
|
14486
|
+
lastFailureAtMs: void 0,
|
|
14432
14487
|
messages,
|
|
14433
14488
|
updatedAtMs: nowMs
|
|
14434
14489
|
});
|
|
@@ -14481,6 +14536,8 @@ async function completeConversationWork(args) {
|
|
|
14481
14536
|
const hasRunnableWork2 = current.needsRun || hasPending;
|
|
14482
14537
|
await writeWorkState(state, {
|
|
14483
14538
|
...current,
|
|
14539
|
+
consecutiveFailureCount: 0,
|
|
14540
|
+
lastFailureAtMs: void 0,
|
|
14484
14541
|
lease: void 0,
|
|
14485
14542
|
needsRun: hasRunnableWork2,
|
|
14486
14543
|
updatedAtMs: nowMs
|
|
@@ -14504,6 +14561,53 @@ async function clearExpiredConversationLease(args) {
|
|
|
14504
14561
|
return true;
|
|
14505
14562
|
});
|
|
14506
14563
|
}
|
|
14564
|
+
async function recordConversationWorkFailure(args) {
|
|
14565
|
+
const nowMs = args.nowMs ?? now();
|
|
14566
|
+
return await withConversationMutation(args, async (state) => {
|
|
14567
|
+
const current = await readWorkState(state, args.conversationId);
|
|
14568
|
+
if (!current) {
|
|
14569
|
+
return {
|
|
14570
|
+
abandoned: false,
|
|
14571
|
+
consecutiveFailureCount: 0,
|
|
14572
|
+
releasedLease: false
|
|
14573
|
+
};
|
|
14574
|
+
}
|
|
14575
|
+
const consecutiveFailureCount = current.consecutiveFailureCount + 1;
|
|
14576
|
+
const abandoned = consecutiveFailureCount >= CONVERSATION_WORK_MAX_CONSECUTIVE_FAILURES;
|
|
14577
|
+
if (!abandoned) {
|
|
14578
|
+
await writeWorkState(state, {
|
|
14579
|
+
...current,
|
|
14580
|
+
consecutiveFailureCount,
|
|
14581
|
+
lastFailureAtMs: nowMs,
|
|
14582
|
+
updatedAtMs: nowMs
|
|
14583
|
+
});
|
|
14584
|
+
return {
|
|
14585
|
+
abandoned: false,
|
|
14586
|
+
consecutiveFailureCount,
|
|
14587
|
+
releasedLease: false
|
|
14588
|
+
};
|
|
14589
|
+
}
|
|
14590
|
+
const releasedLease = Boolean(current.lease);
|
|
14591
|
+
const drainedMessages = current.messages.filter(
|
|
14592
|
+
(message) => message.injectedAtMs !== void 0
|
|
14593
|
+
);
|
|
14594
|
+
await writeWorkState(state, {
|
|
14595
|
+
...current,
|
|
14596
|
+
consecutiveFailureCount,
|
|
14597
|
+
lastFailureAtMs: nowMs,
|
|
14598
|
+
lease: void 0,
|
|
14599
|
+
messages: drainedMessages,
|
|
14600
|
+
needsRun: false,
|
|
14601
|
+
terminallyFailedAtMs: nowMs,
|
|
14602
|
+
updatedAtMs: nowMs
|
|
14603
|
+
});
|
|
14604
|
+
return {
|
|
14605
|
+
abandoned: true,
|
|
14606
|
+
consecutiveFailureCount,
|
|
14607
|
+
releasedLease
|
|
14608
|
+
};
|
|
14609
|
+
});
|
|
14610
|
+
}
|
|
14507
14611
|
async function listConversationWorkIds(args = {}) {
|
|
14508
14612
|
const state = await getConnectedState(args.state);
|
|
14509
14613
|
const ids = uniqueStrings(await state.get(indexKey()) ?? []);
|
|
@@ -17426,6 +17530,7 @@ async function selectSandboxEgressGrant(input) {
|
|
|
17426
17530
|
return defaultGrantForProvider(input);
|
|
17427
17531
|
}
|
|
17428
17532
|
const pluginGrant = await selectPluginGrant({
|
|
17533
|
+
...input.bodyText !== void 0 ? { bodyText: input.bodyText } : {},
|
|
17429
17534
|
provider: input.provider,
|
|
17430
17535
|
method: input.method,
|
|
17431
17536
|
upstreamUrl: input.upstreamUrl
|
|
@@ -17587,6 +17692,7 @@ async function verifyVercelSandboxOidcToken(token) {
|
|
|
17587
17692
|
}
|
|
17588
17693
|
|
|
17589
17694
|
// src/chat/sandbox/egress-proxy.ts
|
|
17695
|
+
import { EgressAuthRequired } from "@sentry/junior-plugin-api";
|
|
17590
17696
|
var OIDC_TOKEN_HEADER = "vercel-sandbox-oidc-token";
|
|
17591
17697
|
var FORWARDED_HOST_HEADER = "vercel-forwarded-host";
|
|
17592
17698
|
var FORWARDED_SCHEME_HEADER = "vercel-forwarded-scheme";
|
|
@@ -17616,6 +17722,8 @@ var DECODED_RESPONSE_HEADERS = /* @__PURE__ */ new Set([
|
|
|
17616
17722
|
]);
|
|
17617
17723
|
var UPSTREAM_TOKEN_REJECTION_STATUS = 401;
|
|
17618
17724
|
var UPSTREAM_PERMISSION_REJECTION_STATUS = 403;
|
|
17725
|
+
var GRANT_SELECTION_BODY_TEXT_LIMIT_BYTES = 64 * 1024;
|
|
17726
|
+
var RESPONSE_BODY_TEXT_LIMIT_BYTES = 64 * 1024;
|
|
17619
17727
|
function jsonError(message, status) {
|
|
17620
17728
|
return Response.json({ error: message }, { status });
|
|
17621
17729
|
}
|
|
@@ -17714,6 +17822,9 @@ function githubPermissionHeaders(upstream) {
|
|
|
17714
17822
|
function permissionDeniedMessage(provider, grant) {
|
|
17715
17823
|
return `${provider} returned HTTP 403 after Junior injected the ${grant.name} grant. Junior forwarded the request; this is not a local runtime block.`;
|
|
17716
17824
|
}
|
|
17825
|
+
function isEgressAuthRequired(error) {
|
|
17826
|
+
return error instanceof EgressAuthRequired || error instanceof Error && error.name === "EgressAuthRequired";
|
|
17827
|
+
}
|
|
17717
17828
|
function logSandboxEgressUpstreamRequest(input) {
|
|
17718
17829
|
if (!shouldLogSandboxEgressInfo()) {
|
|
17719
17830
|
return;
|
|
@@ -17822,6 +17933,78 @@ async function requestBodyBytes(request) {
|
|
|
17822
17933
|
}
|
|
17823
17934
|
return await request.arrayBuffer();
|
|
17824
17935
|
}
|
|
17936
|
+
function isGrantSelectionBodyVisible(input) {
|
|
17937
|
+
return input.provider === "github" && input.upstreamUrl.hostname.toLowerCase() === "api.github.com" && input.upstreamUrl.pathname.toLowerCase().endsWith("/graphql");
|
|
17938
|
+
}
|
|
17939
|
+
function requestBodyText(body) {
|
|
17940
|
+
if (body === void 0 || body.byteLength > GRANT_SELECTION_BODY_TEXT_LIMIT_BYTES) {
|
|
17941
|
+
return void 0;
|
|
17942
|
+
}
|
|
17943
|
+
return new TextDecoder().decode(body);
|
|
17944
|
+
}
|
|
17945
|
+
function responseContentLength(upstream) {
|
|
17946
|
+
const raw = upstream.headers.get("content-length");
|
|
17947
|
+
if (!raw) {
|
|
17948
|
+
return void 0;
|
|
17949
|
+
}
|
|
17950
|
+
const parsed = Number(raw);
|
|
17951
|
+
return Number.isSafeInteger(parsed) && parsed >= 0 ? parsed : void 0;
|
|
17952
|
+
}
|
|
17953
|
+
async function responseTextWithinLimit(upstream, maxBytes) {
|
|
17954
|
+
const limit = Math.min(
|
|
17955
|
+
Math.max(0, Math.floor(maxBytes)),
|
|
17956
|
+
RESPONSE_BODY_TEXT_LIMIT_BYTES
|
|
17957
|
+
);
|
|
17958
|
+
if (limit <= 0) {
|
|
17959
|
+
return void 0;
|
|
17960
|
+
}
|
|
17961
|
+
const contentLength = responseContentLength(upstream);
|
|
17962
|
+
if (contentLength !== void 0 && contentLength > limit) {
|
|
17963
|
+
return void 0;
|
|
17964
|
+
}
|
|
17965
|
+
let clone;
|
|
17966
|
+
try {
|
|
17967
|
+
clone = upstream.clone();
|
|
17968
|
+
} catch {
|
|
17969
|
+
return void 0;
|
|
17970
|
+
}
|
|
17971
|
+
const body = clone.body;
|
|
17972
|
+
if (!body) {
|
|
17973
|
+
return "";
|
|
17974
|
+
}
|
|
17975
|
+
const reader = body.getReader();
|
|
17976
|
+
const chunks = [];
|
|
17977
|
+
let bytes = 0;
|
|
17978
|
+
try {
|
|
17979
|
+
while (true) {
|
|
17980
|
+
const { done, value } = await reader.read();
|
|
17981
|
+
if (done) {
|
|
17982
|
+
break;
|
|
17983
|
+
}
|
|
17984
|
+
if (!value) {
|
|
17985
|
+
continue;
|
|
17986
|
+
}
|
|
17987
|
+
bytes += value.byteLength;
|
|
17988
|
+
if (bytes > limit) {
|
|
17989
|
+
await reader.cancel().catch(() => void 0);
|
|
17990
|
+
return void 0;
|
|
17991
|
+
}
|
|
17992
|
+
chunks.push(value);
|
|
17993
|
+
}
|
|
17994
|
+
} catch {
|
|
17995
|
+
await reader.cancel().catch(() => void 0);
|
|
17996
|
+
return void 0;
|
|
17997
|
+
} finally {
|
|
17998
|
+
reader.releaseLock();
|
|
17999
|
+
}
|
|
18000
|
+
const combined = new Uint8Array(bytes);
|
|
18001
|
+
let offset = 0;
|
|
18002
|
+
for (const chunk of chunks) {
|
|
18003
|
+
combined.set(chunk, offset);
|
|
18004
|
+
offset += chunk.byteLength;
|
|
18005
|
+
}
|
|
18006
|
+
return new TextDecoder().decode(combined);
|
|
18007
|
+
}
|
|
17825
18008
|
function requestHeaders(request, lease, upstreamHost) {
|
|
17826
18009
|
const headers = new Headers();
|
|
17827
18010
|
request.headers.forEach((value, key) => {
|
|
@@ -17956,7 +18139,14 @@ async function proxySandboxEgressRequest(request, deps = {}) {
|
|
|
17956
18139
|
403
|
|
17957
18140
|
);
|
|
17958
18141
|
}
|
|
18142
|
+
let body;
|
|
18143
|
+
let bodyRead = false;
|
|
18144
|
+
if (isGrantSelectionBodyVisible({ provider, upstreamUrl })) {
|
|
18145
|
+
body = await requestBodyBytes(request);
|
|
18146
|
+
bodyRead = true;
|
|
18147
|
+
}
|
|
17959
18148
|
const grantSelection = await selectSandboxEgressGrant({
|
|
18149
|
+
bodyText: requestBodyText(body),
|
|
17960
18150
|
provider,
|
|
17961
18151
|
method: request.method,
|
|
17962
18152
|
upstreamUrl
|
|
@@ -18067,7 +18257,9 @@ async function proxySandboxEgressRequest(request, deps = {}) {
|
|
|
18067
18257
|
}
|
|
18068
18258
|
const fetchImpl = deps.fetch ?? fetch;
|
|
18069
18259
|
const headers = requestHeaders(request, lease, upstreamUrl.hostname);
|
|
18070
|
-
|
|
18260
|
+
if (!bodyRead) {
|
|
18261
|
+
body = await requestBodyBytes(request);
|
|
18262
|
+
}
|
|
18071
18263
|
const intercepted = await deps.interceptHttp?.({
|
|
18072
18264
|
provider,
|
|
18073
18265
|
request: new Request(upstreamUrl, {
|
|
@@ -18086,6 +18278,93 @@ async function proxySandboxEgressRequest(request, deps = {}) {
|
|
|
18086
18278
|
...body !== void 0 ? { body } : {},
|
|
18087
18279
|
redirect: "manual"
|
|
18088
18280
|
});
|
|
18281
|
+
try {
|
|
18282
|
+
const effects = await onPluginEgressResponse({
|
|
18283
|
+
provider,
|
|
18284
|
+
grant: lease.grant,
|
|
18285
|
+
method: request.method,
|
|
18286
|
+
upstreamUrl,
|
|
18287
|
+
response: {
|
|
18288
|
+
headers: new Headers(upstream.headers),
|
|
18289
|
+
readText: async (maxBytes) => await responseTextWithinLimit(upstream, maxBytes),
|
|
18290
|
+
status: upstream.status
|
|
18291
|
+
}
|
|
18292
|
+
});
|
|
18293
|
+
if (effects.permissionDenied) {
|
|
18294
|
+
await setSandboxEgressPermissionDeniedSignal(credentialContext, {
|
|
18295
|
+
provider,
|
|
18296
|
+
grant: lease.grant,
|
|
18297
|
+
...lease.account ? { account: lease.account } : {},
|
|
18298
|
+
message: effects.permissionDenied.message,
|
|
18299
|
+
source: "upstream",
|
|
18300
|
+
status: upstream.status,
|
|
18301
|
+
upstreamHost: upstreamUrl.hostname,
|
|
18302
|
+
upstreamPath: displayedUpstreamPath(upstreamUrl),
|
|
18303
|
+
...provider === "github" ? githubPermissionHeaders(upstream) : {}
|
|
18304
|
+
});
|
|
18305
|
+
logWarn(
|
|
18306
|
+
"sandbox_egress_upstream_permission_classified",
|
|
18307
|
+
{},
|
|
18308
|
+
{
|
|
18309
|
+
...egressAttributes({
|
|
18310
|
+
egressId: activeEgressId,
|
|
18311
|
+
grantAccess: lease.grant.access,
|
|
18312
|
+
grantName: lease.grant.name,
|
|
18313
|
+
grantReason: lease.grant.reason,
|
|
18314
|
+
host: upstreamUrl.hostname,
|
|
18315
|
+
method: request.method,
|
|
18316
|
+
path: upstreamUrl.pathname,
|
|
18317
|
+
provider,
|
|
18318
|
+
status: upstream.status
|
|
18319
|
+
}),
|
|
18320
|
+
...routingAttributes(request, upstreamUrl),
|
|
18321
|
+
...upstreamPermissionAttributes(provider, upstream)
|
|
18322
|
+
},
|
|
18323
|
+
"Sandbox egress plugin classified upstream response as permission denied"
|
|
18324
|
+
);
|
|
18325
|
+
}
|
|
18326
|
+
} catch (error) {
|
|
18327
|
+
if (!isEgressAuthRequired(error)) {
|
|
18328
|
+
throw error;
|
|
18329
|
+
}
|
|
18330
|
+
await clearSandboxEgressCredentialLease(
|
|
18331
|
+
provider,
|
|
18332
|
+
lease.grant.name,
|
|
18333
|
+
credentialContext
|
|
18334
|
+
);
|
|
18335
|
+
await setSandboxEgressAuthRequiredSignal(credentialContext, {
|
|
18336
|
+
provider,
|
|
18337
|
+
grant: lease.grant,
|
|
18338
|
+
...error.authorization ?? lease.authorization ? { authorization: error.authorization ?? lease.authorization } : {},
|
|
18339
|
+
message: error.message
|
|
18340
|
+
});
|
|
18341
|
+
logWarn(
|
|
18342
|
+
"sandbox_egress_upstream_auth_required_classified",
|
|
18343
|
+
{},
|
|
18344
|
+
{
|
|
18345
|
+
...egressAttributes({
|
|
18346
|
+
egressId: activeEgressId,
|
|
18347
|
+
grantAccess: lease.grant.access,
|
|
18348
|
+
grantName: lease.grant.name,
|
|
18349
|
+
grantReason: lease.grant.reason,
|
|
18350
|
+
host: upstreamUrl.hostname,
|
|
18351
|
+
method: request.method,
|
|
18352
|
+
path: upstreamUrl.pathname,
|
|
18353
|
+
provider,
|
|
18354
|
+
status: upstream.status
|
|
18355
|
+
}),
|
|
18356
|
+
...routingAttributes(request, upstreamUrl),
|
|
18357
|
+
...upstreamPermissionAttributes(provider, upstream)
|
|
18358
|
+
},
|
|
18359
|
+
"Sandbox egress plugin classified upstream response as auth required"
|
|
18360
|
+
);
|
|
18361
|
+
await upstream.body?.cancel().catch(() => void 0);
|
|
18362
|
+
return authRequiredResponse({
|
|
18363
|
+
provider,
|
|
18364
|
+
grant: lease.grant,
|
|
18365
|
+
message: error.message
|
|
18366
|
+
});
|
|
18367
|
+
}
|
|
18089
18368
|
logSandboxEgressUpstreamRequest({
|
|
18090
18369
|
egressId: activeEgressId,
|
|
18091
18370
|
grantAccess: lease.grant.access,
|
|
@@ -19034,6 +19313,7 @@ function createSlackTurnRuntime(deps) {
|
|
|
19034
19313
|
text: args.text
|
|
19035
19314
|
});
|
|
19036
19315
|
}
|
|
19316
|
+
await args.onInputCommitted?.();
|
|
19037
19317
|
};
|
|
19038
19318
|
return {
|
|
19039
19319
|
async handleNewMention(thread, message, hooks) {
|
|
@@ -19222,6 +19502,7 @@ function createSlackTurnRuntime(deps) {
|
|
|
19222
19502
|
message,
|
|
19223
19503
|
decision: { shouldReply: false, reason },
|
|
19224
19504
|
context: threadContext,
|
|
19505
|
+
onInputCommitted: hooks.onInputCommitted,
|
|
19225
19506
|
text: combinedText
|
|
19226
19507
|
});
|
|
19227
19508
|
return;
|
|
@@ -19258,6 +19539,7 @@ function createSlackTurnRuntime(deps) {
|
|
|
19258
19539
|
message,
|
|
19259
19540
|
decision,
|
|
19260
19541
|
context: threadContext,
|
|
19542
|
+
onInputCommitted: hooks.onInputCommitted,
|
|
19261
19543
|
preparedState,
|
|
19262
19544
|
text: combinedText
|
|
19263
19545
|
});
|
|
@@ -19269,6 +19551,7 @@ function createSlackTurnRuntime(deps) {
|
|
|
19269
19551
|
message,
|
|
19270
19552
|
decision,
|
|
19271
19553
|
context: threadContext,
|
|
19554
|
+
onInputCommitted: hooks.onInputCommitted,
|
|
19272
19555
|
preparedState,
|
|
19273
19556
|
text: combinedText
|
|
19274
19557
|
});
|
|
@@ -20694,12 +20977,13 @@ function createReplyToThread(deps) {
|
|
|
20694
20977
|
updateConversationStats
|
|
20695
20978
|
});
|
|
20696
20979
|
if (conversationId) {
|
|
20980
|
+
const turnStartedAtMs = message.metadata.dateSent.getTime();
|
|
20697
20981
|
void recordAgentTurnSessionSummary({
|
|
20698
20982
|
channelName,
|
|
20699
20983
|
conversationId,
|
|
20700
20984
|
sessionId: turnId,
|
|
20701
20985
|
sliceId: 1,
|
|
20702
|
-
startedAtMs:
|
|
20986
|
+
startedAtMs: turnStartedAtMs,
|
|
20703
20987
|
state: "running",
|
|
20704
20988
|
surface: "slack",
|
|
20705
20989
|
requester,
|
|
@@ -20710,12 +20994,41 @@ function createReplyToThread(deps) {
|
|
|
20710
20994
|
error,
|
|
20711
20995
|
"agent_turn_summary_record_failed",
|
|
20712
20996
|
turnTraceContext,
|
|
20713
|
-
{
|
|
20714
|
-
"app.agent.turn.state": "running"
|
|
20715
|
-
},
|
|
20997
|
+
{ "app.agent.turn.state": "running" },
|
|
20716
20998
|
"Failed to record running turn summary"
|
|
20717
20999
|
);
|
|
20718
21000
|
});
|
|
21001
|
+
void initConversationContext(conversationId, {
|
|
21002
|
+
channelName,
|
|
21003
|
+
originSurface: "slack",
|
|
21004
|
+
originRequester: requester,
|
|
21005
|
+
startedAtMs: turnStartedAtMs
|
|
21006
|
+
}).catch((error) => {
|
|
21007
|
+
logException(
|
|
21008
|
+
error,
|
|
21009
|
+
"conversation_details_context_init_failed",
|
|
21010
|
+
turnTraceContext,
|
|
21011
|
+
{ "app.agent.turn.state": "running" },
|
|
21012
|
+
"Failed to init conversation context at turn start"
|
|
21013
|
+
);
|
|
21014
|
+
});
|
|
21015
|
+
const existingAssistantTitle = preparedState.artifacts.assistantTitle?.trim();
|
|
21016
|
+
if (existingAssistantTitle) {
|
|
21017
|
+
void setConversationTitle(conversationId, {
|
|
21018
|
+
displayTitle: existingAssistantTitle,
|
|
21019
|
+
...preparedState.artifacts.assistantTitleSourceMessageId ? {
|
|
21020
|
+
titleSourceMessageId: preparedState.artifacts.assistantTitleSourceMessageId
|
|
21021
|
+
} : {}
|
|
21022
|
+
}).catch((error) => {
|
|
21023
|
+
logException(
|
|
21024
|
+
error,
|
|
21025
|
+
"conversation_details_title_refresh_failed",
|
|
21026
|
+
turnTraceContext,
|
|
21027
|
+
{ "app.agent.turn.state": "running" },
|
|
21028
|
+
"Failed to refresh conversation title from artifacts"
|
|
21029
|
+
);
|
|
21030
|
+
});
|
|
21031
|
+
}
|
|
20719
21032
|
}
|
|
20720
21033
|
setTags({
|
|
20721
21034
|
conversationId
|
|
@@ -20791,6 +21104,7 @@ function createReplyToThread(deps) {
|
|
|
20791
21104
|
let persistedAtLeastOnce = false;
|
|
20792
21105
|
let shouldPersistFailureState = true;
|
|
20793
21106
|
let latestArtifacts = preparedState.artifacts;
|
|
21107
|
+
let assistantTitleArtifacts = {};
|
|
20794
21108
|
try {
|
|
20795
21109
|
const loadedPiMessages = await loadPiMessagesForTurn({
|
|
20796
21110
|
conversationId,
|
|
@@ -20833,6 +21147,54 @@ function createReplyToThread(deps) {
|
|
|
20833
21147
|
runId,
|
|
20834
21148
|
threadId
|
|
20835
21149
|
});
|
|
21150
|
+
void assistantTitleTask.then(async (titleUpdateResult) => {
|
|
21151
|
+
if (!titleUpdateResult) return;
|
|
21152
|
+
assistantTitleArtifacts = {
|
|
21153
|
+
assistantTitleSourceMessageId: titleUpdateResult.sourceMessageId,
|
|
21154
|
+
...titleUpdateResult.title ? { assistantTitle: titleUpdateResult.title } : {}
|
|
21155
|
+
};
|
|
21156
|
+
latestArtifacts = {
|
|
21157
|
+
...latestArtifacts,
|
|
21158
|
+
...assistantTitleArtifacts
|
|
21159
|
+
};
|
|
21160
|
+
if (conversationId && titleUpdateResult.title) {
|
|
21161
|
+
try {
|
|
21162
|
+
await setConversationTitle(conversationId, {
|
|
21163
|
+
displayTitle: titleUpdateResult.title,
|
|
21164
|
+
titleSourceMessageId: titleUpdateResult.sourceMessageId
|
|
21165
|
+
});
|
|
21166
|
+
} catch (error) {
|
|
21167
|
+
logException(
|
|
21168
|
+
error,
|
|
21169
|
+
"conversation_details_title_set_failed",
|
|
21170
|
+
turnTraceContext,
|
|
21171
|
+
{},
|
|
21172
|
+
"Failed to set conversation title in details record"
|
|
21173
|
+
);
|
|
21174
|
+
}
|
|
21175
|
+
}
|
|
21176
|
+
try {
|
|
21177
|
+
await persistThreadState(thread, {
|
|
21178
|
+
artifacts: latestArtifacts
|
|
21179
|
+
});
|
|
21180
|
+
} catch (error) {
|
|
21181
|
+
logException(
|
|
21182
|
+
error,
|
|
21183
|
+
"assistant_title_artifact_persist_failed",
|
|
21184
|
+
turnTraceContext,
|
|
21185
|
+
{},
|
|
21186
|
+
"Failed to persist async assistant title artifact state"
|
|
21187
|
+
);
|
|
21188
|
+
}
|
|
21189
|
+
}).catch((error) => {
|
|
21190
|
+
logException(
|
|
21191
|
+
error,
|
|
21192
|
+
"assistant_title_task_failed",
|
|
21193
|
+
turnTraceContext,
|
|
21194
|
+
{},
|
|
21195
|
+
"Async assistant title task failed"
|
|
21196
|
+
);
|
|
21197
|
+
});
|
|
20836
21198
|
const toolChannelId = preparedState.artifacts.assistantContextChannelId ?? channelId;
|
|
20837
21199
|
const resolveSteeringMessages = async (queuedMessages) => {
|
|
20838
21200
|
return await Promise.all(
|
|
@@ -20911,8 +21273,13 @@ function createReplyToThread(deps) {
|
|
|
20911
21273
|
});
|
|
20912
21274
|
},
|
|
20913
21275
|
onArtifactStateUpdated: async (artifacts) => {
|
|
20914
|
-
latestArtifacts =
|
|
20915
|
-
|
|
21276
|
+
latestArtifacts = {
|
|
21277
|
+
...artifacts,
|
|
21278
|
+
...assistantTitleArtifacts
|
|
21279
|
+
};
|
|
21280
|
+
await persistThreadState(thread, {
|
|
21281
|
+
artifacts: latestArtifacts
|
|
21282
|
+
});
|
|
20916
21283
|
},
|
|
20917
21284
|
onAuthPending: async (pendingAuth) => {
|
|
20918
21285
|
await applyPendingAuthUpdate({
|
|
@@ -21013,24 +21380,26 @@ function createReplyToThread(deps) {
|
|
|
21013
21380
|
await sent.delete();
|
|
21014
21381
|
}
|
|
21015
21382
|
}
|
|
21016
|
-
const titleUpdateResult = await assistantTitleTask;
|
|
21017
|
-
if (titleUpdateResult) {
|
|
21018
|
-
artifactStatePatch.assistantTitleSourceMessageId = titleUpdateResult.sourceMessageId;
|
|
21019
|
-
if (titleUpdateResult.title) {
|
|
21020
|
-
artifactStatePatch.assistantTitle = titleUpdateResult.title;
|
|
21021
|
-
}
|
|
21022
|
-
}
|
|
21023
21383
|
const completedState = buildDeliveredTurnStatePatch({
|
|
21024
|
-
artifactStatePatch
|
|
21025
|
-
|
|
21384
|
+
artifactStatePatch: {
|
|
21385
|
+
...artifactStatePatch,
|
|
21386
|
+
...assistantTitleArtifacts
|
|
21387
|
+
},
|
|
21388
|
+
artifacts: latestArtifacts,
|
|
21026
21389
|
conversation: preparedState.conversation,
|
|
21027
21390
|
reply,
|
|
21028
21391
|
sessionId: turnId,
|
|
21029
21392
|
userMessageId: preparedState.userMessageId
|
|
21030
21393
|
});
|
|
21394
|
+
if (completedState.artifacts) {
|
|
21395
|
+
latestArtifacts = completedState.artifacts;
|
|
21396
|
+
}
|
|
21031
21397
|
await persistThreadState(thread, {
|
|
21032
21398
|
...completedState
|
|
21033
21399
|
});
|
|
21400
|
+
if (completedState.artifacts && (assistantTitleArtifacts.assistantTitle !== void 0 || assistantTitleArtifacts.assistantTitleSourceMessageId !== void 0) && (completedState.artifacts.assistantTitle !== assistantTitleArtifacts.assistantTitle || completedState.artifacts.assistantTitleSourceMessageId !== assistantTitleArtifacts.assistantTitleSourceMessageId)) {
|
|
21401
|
+
await persistThreadState(thread, { artifacts: latestArtifacts });
|
|
21402
|
+
}
|
|
21034
21403
|
if (conversationId) {
|
|
21035
21404
|
await recordAgentTurnSessionSummary({
|
|
21036
21405
|
channelName,
|
|
@@ -21041,7 +21410,6 @@ function createReplyToThread(deps) {
|
|
|
21041
21410
|
sliceId: 1,
|
|
21042
21411
|
startedAtMs: message.metadata.dateSent.getTime(),
|
|
21043
21412
|
state: "completed",
|
|
21044
|
-
conversationTitle: titleUpdateResult?.title,
|
|
21045
21413
|
requester,
|
|
21046
21414
|
destination,
|
|
21047
21415
|
traceId: getActiveTraceId()
|
|
@@ -22998,24 +23366,50 @@ async function processConversationWork(message, options) {
|
|
|
22998
23366
|
);
|
|
22999
23367
|
}
|
|
23000
23368
|
const destination = initial.destination;
|
|
23001
|
-
|
|
23002
|
-
|
|
23003
|
-
|
|
23004
|
-
|
|
23005
|
-
|
|
23369
|
+
let lease;
|
|
23370
|
+
try {
|
|
23371
|
+
lease = await startConversationWork({
|
|
23372
|
+
conversationId,
|
|
23373
|
+
nowMs: now2(options),
|
|
23374
|
+
state: options.state
|
|
23375
|
+
});
|
|
23376
|
+
} catch (error) {
|
|
23377
|
+
logException(
|
|
23378
|
+
error,
|
|
23379
|
+
"conversation_work_lease_acquire_failed",
|
|
23380
|
+
{ conversationId },
|
|
23381
|
+
{},
|
|
23382
|
+
"Conversation work lease acquisition failed; heartbeat will recover"
|
|
23383
|
+
);
|
|
23384
|
+
return { status: "no_work" };
|
|
23385
|
+
}
|
|
23006
23386
|
if (lease.status === "no_work") {
|
|
23007
23387
|
return { status: "no_work" };
|
|
23008
23388
|
}
|
|
23009
23389
|
if (lease.status === "active") {
|
|
23010
23390
|
const nudgeNowMs = now2(options);
|
|
23011
|
-
|
|
23012
|
-
|
|
23013
|
-
|
|
23014
|
-
|
|
23015
|
-
|
|
23016
|
-
|
|
23017
|
-
|
|
23018
|
-
|
|
23391
|
+
try {
|
|
23392
|
+
await sendWakeNudge({
|
|
23393
|
+
conversationId,
|
|
23394
|
+
destination,
|
|
23395
|
+
delayMs: CONVERSATION_WORK_DEFER_DELAY_MS,
|
|
23396
|
+
idempotencyKey: nudgeIdempotencyKey(
|
|
23397
|
+
"active",
|
|
23398
|
+
conversationId,
|
|
23399
|
+
nudgeNowMs
|
|
23400
|
+
),
|
|
23401
|
+
nowMs: nudgeNowMs,
|
|
23402
|
+
options
|
|
23403
|
+
});
|
|
23404
|
+
} catch (error) {
|
|
23405
|
+
logException(
|
|
23406
|
+
error,
|
|
23407
|
+
"conversation_work_active_nudge_failed",
|
|
23408
|
+
{ conversationId },
|
|
23409
|
+
{},
|
|
23410
|
+
"Conversation work active-lease nudge failed; heartbeat will recover"
|
|
23411
|
+
);
|
|
23412
|
+
}
|
|
23019
23413
|
logInfo(
|
|
23020
23414
|
"conversation_work_nudge_deferred_for_active_lease",
|
|
23021
23415
|
{ conversationId },
|
|
@@ -23169,35 +23563,97 @@ async function processConversationWork(message, options) {
|
|
|
23169
23563
|
return { status: "completed" };
|
|
23170
23564
|
} catch (error) {
|
|
23171
23565
|
const errorNowMs = now2(options);
|
|
23566
|
+
let failure2;
|
|
23172
23567
|
try {
|
|
23173
|
-
|
|
23568
|
+
failure2 = await recordConversationWorkFailure({
|
|
23174
23569
|
conversationId,
|
|
23175
|
-
destination,
|
|
23176
|
-
leaseToken: lease.leaseToken,
|
|
23177
23570
|
nowMs: errorNowMs,
|
|
23178
23571
|
state: options.state
|
|
23179
23572
|
});
|
|
23180
|
-
|
|
23181
|
-
|
|
23573
|
+
} catch (recordError) {
|
|
23574
|
+
logException(
|
|
23575
|
+
recordError,
|
|
23576
|
+
"conversation_work_failure_record_failed",
|
|
23577
|
+
{ conversationId },
|
|
23578
|
+
{},
|
|
23579
|
+
"Conversation work failure counter update failed"
|
|
23580
|
+
);
|
|
23581
|
+
}
|
|
23582
|
+
if (!isProviderRetryError(error)) {
|
|
23583
|
+
logException(
|
|
23584
|
+
error,
|
|
23585
|
+
"conversation_work_failed",
|
|
23586
|
+
{ conversationId },
|
|
23587
|
+
{
|
|
23588
|
+
"app.worker.consecutive_failure_count": failure2?.consecutiveFailureCount ?? null,
|
|
23589
|
+
"app.worker.elapsed_ms": now2(options) - startedAtMs
|
|
23590
|
+
},
|
|
23591
|
+
"Conversation work failed"
|
|
23592
|
+
);
|
|
23593
|
+
}
|
|
23594
|
+
if (failure2?.abandoned) {
|
|
23595
|
+
logWarn(
|
|
23596
|
+
"conversation_work_abandoned",
|
|
23597
|
+
{ conversationId },
|
|
23598
|
+
{
|
|
23599
|
+
"app.worker.consecutive_failure_count": failure2.consecutiveFailureCount,
|
|
23600
|
+
"app.worker.max_consecutive_failures": CONVERSATION_WORK_MAX_CONSECUTIVE_FAILURES
|
|
23601
|
+
},
|
|
23602
|
+
"Conversation work abandoned after repeated failures; stopping retries"
|
|
23603
|
+
);
|
|
23604
|
+
if (!failure2.releasedLease) {
|
|
23605
|
+
try {
|
|
23606
|
+
await releaseConversationWork({
|
|
23607
|
+
conversationId,
|
|
23608
|
+
leaseToken: lease.leaseToken,
|
|
23609
|
+
nowMs: errorNowMs,
|
|
23610
|
+
state: options.state
|
|
23611
|
+
});
|
|
23612
|
+
} catch (releaseError) {
|
|
23613
|
+
logException(
|
|
23614
|
+
releaseError,
|
|
23615
|
+
"conversation_work_release_failed",
|
|
23616
|
+
{ conversationId },
|
|
23617
|
+
{},
|
|
23618
|
+
"Conversation work release failed after abandoning"
|
|
23619
|
+
);
|
|
23620
|
+
}
|
|
23621
|
+
}
|
|
23622
|
+
return { status: "abandoned" };
|
|
23623
|
+
}
|
|
23624
|
+
let requeueSucceeded = false;
|
|
23625
|
+
if (failure2) {
|
|
23626
|
+
try {
|
|
23627
|
+
const continuationMarked = await requestConversationContinuation({
|
|
23182
23628
|
conversationId,
|
|
23183
23629
|
destination,
|
|
23184
|
-
|
|
23185
|
-
"error",
|
|
23186
|
-
conversationId,
|
|
23187
|
-
errorNowMs
|
|
23188
|
-
),
|
|
23630
|
+
leaseToken: lease.leaseToken,
|
|
23189
23631
|
nowMs: errorNowMs,
|
|
23190
|
-
options
|
|
23632
|
+
state: options.state
|
|
23191
23633
|
});
|
|
23634
|
+
if (continuationMarked) {
|
|
23635
|
+
await sendWakeNudge({
|
|
23636
|
+
conversationId,
|
|
23637
|
+
destination,
|
|
23638
|
+
idempotencyKey: nudgeIdempotencyKey(
|
|
23639
|
+
"error",
|
|
23640
|
+
conversationId,
|
|
23641
|
+
errorNowMs
|
|
23642
|
+
),
|
|
23643
|
+
nowMs: errorNowMs,
|
|
23644
|
+
options
|
|
23645
|
+
});
|
|
23646
|
+
requeueSucceeded = true;
|
|
23647
|
+
}
|
|
23648
|
+
} catch (requeueError) {
|
|
23649
|
+
logException(
|
|
23650
|
+
requeueError,
|
|
23651
|
+
"conversation_work_requeue_failed",
|
|
23652
|
+
{ conversationId },
|
|
23653
|
+
{},
|
|
23654
|
+
"Conversation work requeue failed after runner error"
|
|
23655
|
+
);
|
|
23192
23656
|
}
|
|
23193
|
-
} catch (requeueError) {
|
|
23194
|
-
logException(
|
|
23195
|
-
requeueError,
|
|
23196
|
-
"conversation_work_requeue_failed",
|
|
23197
|
-
{ conversationId },
|
|
23198
|
-
{},
|
|
23199
|
-
"Conversation work requeue failed after runner error"
|
|
23200
|
-
);
|
|
23201
23657
|
}
|
|
23202
23658
|
try {
|
|
23203
23659
|
await releaseConversationWork({
|
|
@@ -23215,16 +23671,8 @@ async function processConversationWork(message, options) {
|
|
|
23215
23671
|
"Conversation work release failed after runner error"
|
|
23216
23672
|
);
|
|
23217
23673
|
}
|
|
23218
|
-
if (
|
|
23219
|
-
|
|
23220
|
-
error,
|
|
23221
|
-
"conversation_work_failed",
|
|
23222
|
-
{ conversationId },
|
|
23223
|
-
{
|
|
23224
|
-
"app.worker.elapsed_ms": now2(options) - startedAtMs
|
|
23225
|
-
},
|
|
23226
|
-
"Conversation work failed"
|
|
23227
|
-
);
|
|
23674
|
+
if (requeueSucceeded) {
|
|
23675
|
+
return { status: "pending_requeued" };
|
|
23228
23676
|
}
|
|
23229
23677
|
throw error;
|
|
23230
23678
|
} finally {
|