@sentry/junior 0.121.0 → 0.122.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-hooks-CQDGDBCP.js → agent-hooks-HJMLQLCB.js} +8 -8
- package/dist/api.js +14 -14
- package/dist/app.js +604 -997
- package/dist/{catalog-runtime-AD3YNTTM.js → catalog-runtime-CYPZUYJD.js} +5 -5
- package/dist/chat/agent/index.d.ts +19 -2
- package/dist/chat/agent/request.d.ts +8 -8
- package/dist/chat/agent/resume.d.ts +1 -3
- package/dist/chat/agent/retry-usage.d.ts +8 -0
- package/dist/chat/agent/skills.d.ts +0 -9
- package/dist/chat/conversations/messages.d.ts +7 -0
- package/dist/chat/conversations/projection.d.ts +16 -0
- package/dist/chat/logging.d.ts +46 -13
- package/dist/chat/runtime/processing-reaction.d.ts +2 -4
- package/dist/chat/runtime/slack-runtime.d.ts +2 -2
- package/dist/chat/services/assistant-reply.d.ts +16 -0
- package/dist/chat/services/empty-output-continuation.d.ts +20 -0
- package/dist/chat/services/turn-failure-response.d.ts +1 -3
- package/dist/chat/services/turn-result.d.ts +0 -5
- package/dist/chat/services/turn-session-record.d.ts +0 -11
- package/dist/chat/tool-support/zod-tool.d.ts +11 -6
- package/dist/chat/tools/execute-tool.d.ts +1 -33
- package/dist/chat/tools/execution/tool-error-handler.d.ts +2 -2
- package/dist/chat/tools/query-conversation-events.d.ts +3 -1
- package/dist/chat/tools/sandbox/bash.d.ts +1 -33
- package/dist/chat/tools/sandbox/edit-file.d.ts +1 -33
- package/dist/chat/tools/sandbox/find-files.d.ts +1 -33
- package/dist/chat/tools/sandbox/grep.d.ts +1 -33
- package/dist/chat/tools/sandbox/list-dir.d.ts +1 -33
- package/dist/chat/tools/sandbox/read-file.d.ts +1 -33
- package/dist/chat/tools/sandbox/write-file.d.ts +1 -33
- package/dist/{chunk-IDXLBKPS.js → chunk-2RFM7STT.js} +11 -22
- package/dist/{chunk-XDRGYDED.js → chunk-32FPGM2V.js} +1 -1
- package/dist/{chunk-JZ47AFSH.js → chunk-4SPRQJOW.js} +37 -45
- package/dist/{chunk-KLUMVJRQ.js → chunk-4TGVC42X.js} +133 -206
- package/dist/{chunk-UXQ5XSA2.js → chunk-BDE5RZVF.js} +2 -2
- package/dist/{chunk-ITMEXX5H.js → chunk-CYW4M6IV.js} +4 -4
- package/dist/{chunk-254MFDOF.js → chunk-DOB5RVMH.js} +2 -2
- package/dist/{chunk-TRECK7RV.js → chunk-E5SRLZQV.js} +14 -29
- package/dist/{chunk-LE77HHF3.js → chunk-GFTZKOTP.js} +1 -1
- package/dist/{chunk-ET2SDCNV.js → chunk-GN6WW7WR.js} +16 -31
- package/dist/{chunk-D2EMOH3N.js → chunk-ISQYBTPG.js} +3 -3
- package/dist/{chunk-IK4QSS5H.js → chunk-L2KJ2GNL.js} +1 -1
- package/dist/{chunk-VWBKDPMQ.js → chunk-NIJG6G6X.js} +472 -684
- package/dist/{chunk-Z2XXPUZG.js → chunk-NNGYYGX5.js} +2061 -2015
- package/dist/{chunk-WUP7NWMP.js → chunk-OKUZIXQM.js} +1 -1
- package/dist/{chunk-HDFGNU5E.js → chunk-S7G34U75.js} +3 -1
- package/dist/{chunk-UURFXX66.js → chunk-TZVZCIYL.js} +1 -1
- package/dist/{chunk-7HYV7KFM.js → chunk-USKP7IM2.js} +49 -92
- package/dist/{chunk-JYDIBXFY.js → chunk-V6XPIKYA.js} +13 -19
- package/dist/{chunk-3DBMANUI.js → chunk-VCC7XFX2.js} +22 -45
- package/dist/cli/chat.js +22 -22
- package/dist/cli/check.js +6 -6
- package/dist/cli/plugins.js +11 -11
- package/dist/cli/snapshot-create.js +9 -9
- package/dist/cli/upgrade.js +6 -6
- package/dist/{db-7HQT2SXQ.js → db-E6O2DXUQ.js} +6 -6
- package/dist/instrumentation.js +3 -3
- package/dist/nitro.js +2 -2
- package/dist/{oauth-callback-server-VHTUCNHK.js → oauth-callback-server-6SCPUFFD.js} +15 -15
- package/dist/{runner-3ONPZRED.js → runner-JFKCMVVY.js} +62 -51
- package/dist/{skills-WGAYGGPA.js → skills-RD66F2OB.js} +6 -6
- package/dist/{validation-ZIWNBG4D.js → validation-XVRNX6IU.js} +6 -6
- package/dist/version.js +1 -1
- package/package.json +8 -8
package/dist/app.js
CHANGED
|
@@ -50,7 +50,7 @@ import {
|
|
|
50
50
|
startConversationWork,
|
|
51
51
|
startSlackProcessingReaction,
|
|
52
52
|
stripLeadingBotMention
|
|
53
|
-
} from "./chunk-
|
|
53
|
+
} from "./chunk-4TGVC42X.js";
|
|
54
54
|
import "./chunk-FA6POGLH.js";
|
|
55
55
|
import {
|
|
56
56
|
ConversationTurnLifecycleService,
|
|
@@ -65,13 +65,13 @@ import {
|
|
|
65
65
|
processPluginTask,
|
|
66
66
|
requireTurnFailureEventId,
|
|
67
67
|
scheduleSessionCompletedPluginTasks
|
|
68
|
-
} from "./chunk-
|
|
68
|
+
} from "./chunk-V6XPIKYA.js";
|
|
69
69
|
import {
|
|
70
70
|
GET,
|
|
71
71
|
conversationVisibilityFromSlackChannelType,
|
|
72
72
|
resolveSlackChannelTypeFromMessage,
|
|
73
73
|
resolveSlackConversationContext
|
|
74
|
-
} from "./chunk-
|
|
74
|
+
} from "./chunk-BDE5RZVF.js";
|
|
75
75
|
import "./chunk-4CODA7P5.js";
|
|
76
76
|
import "./chunk-PE44LDDQ.js";
|
|
77
77
|
import {
|
|
@@ -90,7 +90,7 @@ import {
|
|
|
90
90
|
import {
|
|
91
91
|
validatePluginEgressCredentialHooks,
|
|
92
92
|
validatePluginRegistrations
|
|
93
|
-
} from "./chunk-
|
|
93
|
+
} from "./chunk-GFTZKOTP.js";
|
|
94
94
|
import {
|
|
95
95
|
defineJuniorPlugins,
|
|
96
96
|
pluginCatalogConfigFromEnv,
|
|
@@ -123,7 +123,7 @@ import {
|
|
|
123
123
|
setConfigDefaults,
|
|
124
124
|
shouldEmitDevAgentTrace,
|
|
125
125
|
shouldPropagateSandboxEgressTrace
|
|
126
|
-
} from "./chunk-
|
|
126
|
+
} from "./chunk-NIJG6G6X.js";
|
|
127
127
|
import "./chunk-DIB4GX3G.js";
|
|
128
128
|
import {
|
|
129
129
|
RetryableDeliveryError,
|
|
@@ -134,7 +134,7 @@ import {
|
|
|
134
134
|
formatProviderLabel,
|
|
135
135
|
postSlackMessage,
|
|
136
136
|
startOAuthFlow
|
|
137
|
-
} from "./chunk-
|
|
137
|
+
} from "./chunk-VCC7XFX2.js";
|
|
138
138
|
import {
|
|
139
139
|
ConversationTurnBoundaryError,
|
|
140
140
|
CooperativeTurnYieldError,
|
|
@@ -148,12 +148,14 @@ import {
|
|
|
148
148
|
clearPendingAuth,
|
|
149
149
|
coerceThreadArtifactsState,
|
|
150
150
|
coerceThreadConversationState,
|
|
151
|
+
commitAcceptedReply,
|
|
151
152
|
commitMessages,
|
|
152
153
|
completeDeliveredTurn,
|
|
153
154
|
createConversationMemoryService,
|
|
154
155
|
failAgentTurnSessionRecord,
|
|
155
156
|
getAgentTurnSessionRecord,
|
|
156
157
|
getAgentTurnSessionRecordForResume,
|
|
158
|
+
getAssistantReplyText,
|
|
157
159
|
getChannelConfigurationService,
|
|
158
160
|
getChannelConfigurationServiceById,
|
|
159
161
|
getConversationMessageSlackTs,
|
|
@@ -188,10 +190,10 @@ import {
|
|
|
188
190
|
trimTrailingAssistantMessages,
|
|
189
191
|
updateConversationStats,
|
|
190
192
|
upsertConversationMessage
|
|
191
|
-
} from "./chunk-
|
|
193
|
+
} from "./chunk-NNGYYGX5.js";
|
|
192
194
|
import "./chunk-HDSDCEPK.js";
|
|
193
|
-
import "./chunk-
|
|
194
|
-
import "./chunk-
|
|
195
|
+
import "./chunk-CYW4M6IV.js";
|
|
196
|
+
import "./chunk-32FPGM2V.js";
|
|
195
197
|
import "./chunk-MU6HHZEN.js";
|
|
196
198
|
import "./chunk-PDO5BLNM.js";
|
|
197
199
|
import "./chunk-UIE3R5XU.js";
|
|
@@ -216,7 +218,7 @@ import {
|
|
|
216
218
|
validatePlugins,
|
|
217
219
|
verifyScheduledTaskCredentialSubject,
|
|
218
220
|
verifySlackDirectCredentialSubject
|
|
219
|
-
} from "./chunk-
|
|
221
|
+
} from "./chunk-USKP7IM2.js";
|
|
220
222
|
import {
|
|
221
223
|
contextProvenance,
|
|
222
224
|
createSlackDestination,
|
|
@@ -227,7 +229,7 @@ import {
|
|
|
227
229
|
instructionProvenanceFor,
|
|
228
230
|
requireSlackDestination,
|
|
229
231
|
withConversationEventLock
|
|
230
|
-
} from "./chunk-
|
|
232
|
+
} from "./chunk-DOB5RVMH.js";
|
|
231
233
|
import {
|
|
232
234
|
juniorConversationEvents,
|
|
233
235
|
juniorConversations
|
|
@@ -236,7 +238,7 @@ import {
|
|
|
236
238
|
acquireActiveLock,
|
|
237
239
|
getStateAdapter,
|
|
238
240
|
sleep
|
|
239
|
-
} from "./chunk-
|
|
241
|
+
} from "./chunk-TZVZCIYL.js";
|
|
240
242
|
import {
|
|
241
243
|
STANDARD_MODEL_PROFILE,
|
|
242
244
|
botConfig,
|
|
@@ -254,18 +256,18 @@ import {
|
|
|
254
256
|
modelIdForProfile,
|
|
255
257
|
setSlackReactionConfig,
|
|
256
258
|
standardModelId
|
|
257
|
-
} from "./chunk-
|
|
259
|
+
} from "./chunk-2RFM7STT.js";
|
|
258
260
|
import {
|
|
259
261
|
juniorDestinations
|
|
260
262
|
} from "./chunk-VFUK3X5B.js";
|
|
261
263
|
import {
|
|
262
264
|
parseSlackThreadId
|
|
263
|
-
} from "./chunk-
|
|
264
|
-
import "./chunk-
|
|
265
|
+
} from "./chunk-S7G34U75.js";
|
|
266
|
+
import "./chunk-E5SRLZQV.js";
|
|
265
267
|
import {
|
|
266
268
|
pluginCatalogRuntime
|
|
267
|
-
} from "./chunk-
|
|
268
|
-
import "./chunk-
|
|
269
|
+
} from "./chunk-OKUZIXQM.js";
|
|
270
|
+
import "./chunk-GN6WW7WR.js";
|
|
269
271
|
import {
|
|
270
272
|
credentialSubjectSchema
|
|
271
273
|
} from "./chunk-W3U3YQYI.js";
|
|
@@ -290,8 +292,9 @@ import {
|
|
|
290
292
|
setSpanStatus,
|
|
291
293
|
setTags,
|
|
292
294
|
withContext,
|
|
295
|
+
withLogContext,
|
|
293
296
|
withSpan
|
|
294
|
-
} from "./chunk-
|
|
297
|
+
} from "./chunk-4SPRQJOW.js";
|
|
295
298
|
import {
|
|
296
299
|
toOptionalString
|
|
297
300
|
} from "./chunk-OB42YVAE.js";
|
|
@@ -306,10 +309,10 @@ import "./chunk-OOUNJNMR.js";
|
|
|
306
309
|
import "./chunk-H4HBKISO.js";
|
|
307
310
|
import {
|
|
308
311
|
JUNIOR_PLUGIN_TASK_CALLBACK_ROUTE
|
|
309
|
-
} from "./chunk-
|
|
312
|
+
} from "./chunk-L2KJ2GNL.js";
|
|
310
313
|
import {
|
|
311
314
|
JUNIOR_VERSION
|
|
312
|
-
} from "./chunk-
|
|
315
|
+
} from "./chunk-ISQYBTPG.js";
|
|
313
316
|
import "./chunk-MLKGABMK.js";
|
|
314
317
|
|
|
315
318
|
// src/app.ts
|
|
@@ -1109,13 +1112,9 @@ async function POST(request, waitUntil, options) {
|
|
|
1109
1112
|
queue: options.conversationWorkQueue
|
|
1110
1113
|
});
|
|
1111
1114
|
})().catch((error) => {
|
|
1112
|
-
logException(
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
{},
|
|
1116
|
-
{ "app.dispatch.id": payload.id },
|
|
1117
|
-
"Legacy dispatch callback failed"
|
|
1118
|
-
);
|
|
1115
|
+
logException(error, "agent.dispatch.callback.failed", {
|
|
1116
|
+
"app.dispatch.id": payload.id
|
|
1117
|
+
});
|
|
1119
1118
|
})
|
|
1120
1119
|
);
|
|
1121
1120
|
return new Response("Accepted", { status: 202 });
|
|
@@ -1141,107 +1140,87 @@ async function recoverConversationWork(args) {
|
|
|
1141
1140
|
state: args.state
|
|
1142
1141
|
});
|
|
1143
1142
|
for (const conversationId of ids) {
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
state: args.state
|
|
1149
|
-
});
|
|
1150
|
-
} catch (error) {
|
|
1151
|
-
logException(
|
|
1152
|
-
error,
|
|
1153
|
-
"conversation_work_recovery_failed",
|
|
1154
|
-
{ conversationId },
|
|
1155
|
-
{},
|
|
1156
|
-
"Conversation work heartbeat recovery failed"
|
|
1157
|
-
);
|
|
1158
|
-
if (isInvalidConversationRecordError(error)) {
|
|
1159
|
-
await removeActiveConversation({
|
|
1160
|
-
conversationId,
|
|
1161
|
-
state: args.state
|
|
1162
|
-
});
|
|
1163
|
-
}
|
|
1164
|
-
continue;
|
|
1165
|
-
}
|
|
1166
|
-
try {
|
|
1167
|
-
if (!work) {
|
|
1168
|
-
await removeActiveConversation({
|
|
1169
|
-
conversationId,
|
|
1170
|
-
state: args.state
|
|
1171
|
-
});
|
|
1172
|
-
continue;
|
|
1173
|
-
}
|
|
1174
|
-
if (work.execution.status === "idle") {
|
|
1175
|
-
await removeActiveConversation({
|
|
1143
|
+
await withLogContext({ conversationId }, async () => {
|
|
1144
|
+
let work;
|
|
1145
|
+
try {
|
|
1146
|
+
work = await getConversationWorkState({
|
|
1176
1147
|
conversationId,
|
|
1177
1148
|
state: args.state
|
|
1178
1149
|
});
|
|
1179
|
-
|
|
1150
|
+
} catch (error) {
|
|
1151
|
+
logException(error, "conversation.work.recovery.failed");
|
|
1152
|
+
if (isInvalidConversationRecordError(error)) {
|
|
1153
|
+
await removeActiveConversation({
|
|
1154
|
+
conversationId,
|
|
1155
|
+
state: args.state
|
|
1156
|
+
});
|
|
1157
|
+
}
|
|
1158
|
+
return;
|
|
1180
1159
|
}
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1160
|
+
try {
|
|
1161
|
+
if (!work) {
|
|
1162
|
+
await removeActiveConversation({
|
|
1163
|
+
conversationId,
|
|
1164
|
+
state: args.state
|
|
1165
|
+
});
|
|
1166
|
+
return;
|
|
1167
|
+
}
|
|
1168
|
+
if (work.execution.status === "idle") {
|
|
1169
|
+
await removeActiveConversation({
|
|
1170
|
+
conversationId,
|
|
1171
|
+
state: args.state
|
|
1172
|
+
});
|
|
1173
|
+
return;
|
|
1174
|
+
}
|
|
1175
|
+
if (work.execution.lease && work.execution.lease.expiresAtMs <= args.nowMs) {
|
|
1176
|
+
const cleared = await clearExpiredConversationLease({
|
|
1177
|
+
conversationId,
|
|
1178
|
+
nowMs: args.nowMs,
|
|
1179
|
+
state: args.state
|
|
1180
|
+
});
|
|
1181
|
+
if (!cleared) {
|
|
1182
|
+
return;
|
|
1183
|
+
}
|
|
1184
|
+
const wake2 = await ensureConversationWake({
|
|
1185
|
+
conversationId,
|
|
1186
|
+
idempotencyKey: heartbeatIdempotencyKey(
|
|
1187
|
+
"lease",
|
|
1188
|
+
conversationId,
|
|
1189
|
+
args.nowMs
|
|
1190
|
+
),
|
|
1191
|
+
nowMs: args.nowMs,
|
|
1192
|
+
queue: args.queue,
|
|
1193
|
+
replaceExistingWake: true,
|
|
1194
|
+
state: args.state
|
|
1195
|
+
});
|
|
1196
|
+
if (wake2.status === "enqueued") {
|
|
1197
|
+
result.expiredLeaseCount += 1;
|
|
1198
|
+
logInfo("conversation.work.lease_expired.requeued");
|
|
1199
|
+
}
|
|
1200
|
+
return;
|
|
1201
|
+
}
|
|
1202
|
+
if (work.execution.lease || !hasRunnableConversationWork(work)) {
|
|
1203
|
+
return;
|
|
1189
1204
|
}
|
|
1190
|
-
const
|
|
1205
|
+
const wake = await ensureConversationWake({
|
|
1191
1206
|
conversationId,
|
|
1192
1207
|
idempotencyKey: heartbeatIdempotencyKey(
|
|
1193
|
-
"
|
|
1208
|
+
"pending",
|
|
1194
1209
|
conversationId,
|
|
1195
1210
|
args.nowMs
|
|
1196
1211
|
),
|
|
1197
1212
|
nowMs: args.nowMs,
|
|
1198
1213
|
queue: args.queue,
|
|
1199
|
-
replaceExistingWake: true,
|
|
1200
1214
|
state: args.state
|
|
1201
1215
|
});
|
|
1202
|
-
if (
|
|
1203
|
-
result.
|
|
1204
|
-
logInfo(
|
|
1205
|
-
"conversation_work_lease_expired_requeued",
|
|
1206
|
-
{ conversationId },
|
|
1207
|
-
{},
|
|
1208
|
-
"Heartbeat requeued expired conversation work lease"
|
|
1209
|
-
);
|
|
1216
|
+
if (wake.status === "enqueued") {
|
|
1217
|
+
result.pendingCount += 1;
|
|
1218
|
+
logInfo("conversation.work.pending.requeued");
|
|
1210
1219
|
}
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
if (work.execution.lease || !hasRunnableConversationWork(work)) {
|
|
1214
|
-
continue;
|
|
1215
|
-
}
|
|
1216
|
-
const wake = await ensureConversationWake({
|
|
1217
|
-
conversationId,
|
|
1218
|
-
idempotencyKey: heartbeatIdempotencyKey(
|
|
1219
|
-
"pending",
|
|
1220
|
-
conversationId,
|
|
1221
|
-
args.nowMs
|
|
1222
|
-
),
|
|
1223
|
-
nowMs: args.nowMs,
|
|
1224
|
-
queue: args.queue,
|
|
1225
|
-
state: args.state
|
|
1226
|
-
});
|
|
1227
|
-
if (wake.status === "enqueued") {
|
|
1228
|
-
result.pendingCount += 1;
|
|
1229
|
-
logInfo(
|
|
1230
|
-
"conversation_work_pending_requeued",
|
|
1231
|
-
{ conversationId },
|
|
1232
|
-
{},
|
|
1233
|
-
"Heartbeat requeued pending conversation work"
|
|
1234
|
-
);
|
|
1220
|
+
} catch (error) {
|
|
1221
|
+
logException(error, "conversation.work.recovery.failed");
|
|
1235
1222
|
}
|
|
1236
|
-
}
|
|
1237
|
-
logException(
|
|
1238
|
-
error,
|
|
1239
|
-
"conversation_work_recovery_failed",
|
|
1240
|
-
{ conversationId },
|
|
1241
|
-
{},
|
|
1242
|
-
"Conversation work heartbeat recovery failed"
|
|
1243
|
-
);
|
|
1244
|
-
}
|
|
1223
|
+
});
|
|
1245
1224
|
}
|
|
1246
1225
|
return result;
|
|
1247
1226
|
}
|
|
@@ -1494,24 +1473,15 @@ async function runPluginHeartbeats(args) {
|
|
|
1494
1473
|
PLUGIN_HEARTBEAT_TIMEOUT_MS
|
|
1495
1474
|
);
|
|
1496
1475
|
if (typeof result?.dispatchCount === "number" && result.dispatchCount > 0) {
|
|
1497
|
-
logInfo(
|
|
1498
|
-
"
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
"app.dispatch.count": result.dispatchCount,
|
|
1502
|
-
"app.plugin.name": pluginName
|
|
1503
|
-
},
|
|
1504
|
-
"Plugin heartbeat dispatched agent work"
|
|
1505
|
-
);
|
|
1476
|
+
logInfo("plugin.heartbeat.dispatched", {
|
|
1477
|
+
"app.dispatch.count": result.dispatchCount,
|
|
1478
|
+
"app.plugin.name": pluginName
|
|
1479
|
+
});
|
|
1506
1480
|
}
|
|
1507
1481
|
} catch (error) {
|
|
1508
|
-
logException(
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
{},
|
|
1512
|
-
{ "app.plugin.name": pluginName },
|
|
1513
|
-
"Plugin heartbeat failed"
|
|
1514
|
-
);
|
|
1482
|
+
logException(error, "plugin.heartbeat.failed", {
|
|
1483
|
+
"app.plugin.name": pluginName
|
|
1484
|
+
});
|
|
1515
1485
|
}
|
|
1516
1486
|
}
|
|
1517
1487
|
}
|
|
@@ -1540,13 +1510,9 @@ async function recoverPendingDispatchMailboxAppends(args) {
|
|
|
1540
1510
|
queue: args.conversationWorkQueue
|
|
1541
1511
|
});
|
|
1542
1512
|
} catch (error) {
|
|
1543
|
-
logException(
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
{},
|
|
1547
|
-
{ "app.dispatch.id": id },
|
|
1548
|
-
"Pending dispatch mailbox append recovery failed"
|
|
1549
|
-
);
|
|
1513
|
+
logException(error, "dispatch.mailbox.append_recovery.failed", {
|
|
1514
|
+
"app.dispatch.id": id
|
|
1515
|
+
});
|
|
1550
1516
|
}
|
|
1551
1517
|
}
|
|
1552
1518
|
}
|
|
@@ -1593,13 +1559,9 @@ async function GET4(request, waitUntil, options = {}) {
|
|
|
1593
1559
|
conversationWorkQueue: options.conversationWorkQueue,
|
|
1594
1560
|
nowMs
|
|
1595
1561
|
}).catch((error) => {
|
|
1596
|
-
logException(
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
{},
|
|
1600
|
-
{ "app.heartbeat.now_ms": nowMs },
|
|
1601
|
-
"Heartbeat failed"
|
|
1602
|
-
);
|
|
1562
|
+
logException(error, "heartbeat.failed", {
|
|
1563
|
+
"app.heartbeat.now_ms": nowMs
|
|
1564
|
+
});
|
|
1603
1565
|
})
|
|
1604
1566
|
);
|
|
1605
1567
|
return new Response("Accepted", { status: 202 });
|
|
@@ -1820,42 +1782,33 @@ async function runRetentionPurge(executor, args) {
|
|
|
1820
1782
|
let failed = 0;
|
|
1821
1783
|
let conversations = 0;
|
|
1822
1784
|
for (const root of roots) {
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1785
|
+
await withLogContext({ conversationId: root.conversationId }, async () => {
|
|
1786
|
+
try {
|
|
1787
|
+
const result = await purgeConversationTree(executor, {
|
|
1788
|
+
rootConversationId: root.conversationId,
|
|
1789
|
+
nowMs: args.nowMs,
|
|
1790
|
+
retention: {
|
|
1791
|
+
publicWindowMs: CONTENT_RETENTION_MS.public,
|
|
1792
|
+
privateWindowMs: CONTENT_RETENTION_MS.private
|
|
1793
|
+
}
|
|
1794
|
+
});
|
|
1795
|
+
if (result.purged) {
|
|
1796
|
+
purged += 1;
|
|
1797
|
+
conversations += result.conversations;
|
|
1830
1798
|
}
|
|
1831
|
-
})
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
conversations += result.conversations;
|
|
1799
|
+
} catch (error) {
|
|
1800
|
+
failed += 1;
|
|
1801
|
+
logException(error, "retention.purge.tree.failed");
|
|
1835
1802
|
}
|
|
1836
|
-
}
|
|
1837
|
-
failed += 1;
|
|
1838
|
-
logException(
|
|
1839
|
-
error,
|
|
1840
|
-
"retention_purge_tree_failed",
|
|
1841
|
-
{ conversationId: root.conversationId },
|
|
1842
|
-
{},
|
|
1843
|
-
"Retention purge failed for one conversation tree"
|
|
1844
|
-
);
|
|
1845
|
-
}
|
|
1803
|
+
});
|
|
1846
1804
|
}
|
|
1847
|
-
logInfo(
|
|
1848
|
-
"
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
"app.retention.conversations": conversations,
|
|
1855
|
-
"app.retention.duration_ms": Date.now() - startedAtMs
|
|
1856
|
-
},
|
|
1857
|
-
"Retention purge batch completed"
|
|
1858
|
-
);
|
|
1805
|
+
logInfo("retention.purge.completed", {
|
|
1806
|
+
"app.retention.scanned": roots.length,
|
|
1807
|
+
"app.retention.purged": purged,
|
|
1808
|
+
"app.retention.failed": failed,
|
|
1809
|
+
"app.retention.conversations": conversations,
|
|
1810
|
+
"app.retention.duration_ms": Date.now() - startedAtMs
|
|
1811
|
+
});
|
|
1859
1812
|
return { scanned: roots.length, purged, failed, conversations };
|
|
1860
1813
|
}
|
|
1861
1814
|
|
|
@@ -1886,13 +1839,7 @@ async function GET5(request) {
|
|
|
1886
1839
|
});
|
|
1887
1840
|
return Response.json(result, { status: 200 });
|
|
1888
1841
|
} catch (error) {
|
|
1889
|
-
logException(
|
|
1890
|
-
error,
|
|
1891
|
-
"retention_run_failed",
|
|
1892
|
-
{},
|
|
1893
|
-
{},
|
|
1894
|
-
"Retention purge run failed"
|
|
1895
|
-
);
|
|
1842
|
+
logException(error, "retention.run.failed");
|
|
1896
1843
|
return new Response("Retention purge failed", { status: 500 });
|
|
1897
1844
|
}
|
|
1898
1845
|
}
|
|
@@ -2127,27 +2074,17 @@ async function proxySandboxEgressRequest(request, deps = {}) {
|
|
|
2127
2074
|
oidcToken
|
|
2128
2075
|
);
|
|
2129
2076
|
} catch (error) {
|
|
2130
|
-
logWarn(
|
|
2131
|
-
"
|
|
2132
|
-
|
|
2133
|
-
{
|
|
2134
|
-
"app.sandbox.oidc_error": error instanceof Error ? error.message : String(error)
|
|
2135
|
-
},
|
|
2136
|
-
"Sandbox egress OIDC verification failed"
|
|
2137
|
-
);
|
|
2077
|
+
logWarn("sandbox.egress.oidc_verification.failed", {
|
|
2078
|
+
"app.sandbox.oidc_error": error instanceof Error ? error.message : String(error)
|
|
2079
|
+
});
|
|
2138
2080
|
return jsonError("Invalid Vercel Sandbox OIDC token", 401);
|
|
2139
2081
|
}
|
|
2140
2082
|
const activeEgressId = sandboxIdFromPayload(oidcPayload);
|
|
2141
2083
|
if (!activeEgressId) {
|
|
2142
|
-
logWarn(
|
|
2143
|
-
"
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
"http.request.method": request.method,
|
|
2147
|
-
"url.path": redactedProxyPath(new URL(request.url).pathname)
|
|
2148
|
-
},
|
|
2149
|
-
"Sandbox egress OIDC payload did not include a VM session id"
|
|
2150
|
-
);
|
|
2084
|
+
logWarn("sandbox.egress.oidc_session.missing", {
|
|
2085
|
+
"http.request.method": request.method,
|
|
2086
|
+
"url.path": redactedProxyPath(new URL(request.url).pathname)
|
|
2087
|
+
});
|
|
2151
2088
|
return jsonError(
|
|
2152
2089
|
"Vercel Sandbox OIDC token did not include sandbox_id",
|
|
2153
2090
|
401
|
|
@@ -2155,62 +2092,47 @@ async function proxySandboxEgressRequest(request, deps = {}) {
|
|
|
2155
2092
|
}
|
|
2156
2093
|
const upstreamResult = buildUpstreamUrl(request);
|
|
2157
2094
|
if (!upstreamResult.ok) {
|
|
2158
|
-
logWarn(
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
}),
|
|
2168
|
-
...routingAttributes(request)
|
|
2169
|
-
},
|
|
2170
|
-
"Sandbox egress forwarded request had invalid upstream routing headers"
|
|
2171
|
-
);
|
|
2095
|
+
logWarn("sandbox.egress.upstream_url.invalid", {
|
|
2096
|
+
...egressAttributes({
|
|
2097
|
+
egressId: activeEgressId,
|
|
2098
|
+
method: request.method,
|
|
2099
|
+
path: redactedProxyPath(new URL(request.url).pathname),
|
|
2100
|
+
status: 400
|
|
2101
|
+
}),
|
|
2102
|
+
...routingAttributes(request)
|
|
2103
|
+
});
|
|
2172
2104
|
return jsonError(upstreamResult.error, 400);
|
|
2173
2105
|
}
|
|
2174
2106
|
const upstreamUrl = upstreamResult.url;
|
|
2175
2107
|
const provider = resolveSandboxEgressProviderForHost(upstreamUrl.hostname);
|
|
2176
2108
|
if (!provider) {
|
|
2177
|
-
logWarn(
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
}),
|
|
2188
|
-
...routingAttributes(request, upstreamUrl)
|
|
2189
|
-
},
|
|
2190
|
-
"Sandbox egress forwarded host is not owned by any credential provider"
|
|
2191
|
-
);
|
|
2109
|
+
logWarn("sandbox.egress.provider.unresolved", {
|
|
2110
|
+
...egressAttributes({
|
|
2111
|
+
egressId: activeEgressId,
|
|
2112
|
+
host: upstreamUrl.hostname,
|
|
2113
|
+
method: request.method,
|
|
2114
|
+
path: upstreamUrl.pathname,
|
|
2115
|
+
status: 403
|
|
2116
|
+
}),
|
|
2117
|
+
...routingAttributes(request, upstreamUrl)
|
|
2118
|
+
});
|
|
2192
2119
|
return jsonError("No provider owns forwarded host", 403);
|
|
2193
2120
|
}
|
|
2194
2121
|
const credentialContext = parseSandboxEgressCredentialToken(
|
|
2195
2122
|
credentialTokenFromRequest(request)
|
|
2196
2123
|
);
|
|
2197
2124
|
if (!credentialContext || credentialContext.egressId !== activeEgressId) {
|
|
2198
|
-
logWarn(
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
}),
|
|
2210
|
-
...routingAttributes(request, upstreamUrl)
|
|
2211
|
-
},
|
|
2212
|
-
"Sandbox egress request did not include a valid credential context for the VM session"
|
|
2213
|
-
);
|
|
2125
|
+
logWarn("sandbox.egress.credential_context.unauthorized", {
|
|
2126
|
+
...egressAttributes({
|
|
2127
|
+
egressId: activeEgressId,
|
|
2128
|
+
host: upstreamUrl.hostname,
|
|
2129
|
+
method: request.method,
|
|
2130
|
+
path: upstreamUrl.pathname,
|
|
2131
|
+
provider,
|
|
2132
|
+
status: 403
|
|
2133
|
+
}),
|
|
2134
|
+
...routingAttributes(request, upstreamUrl)
|
|
2135
|
+
});
|
|
2214
2136
|
return jsonError(
|
|
2215
2137
|
"Sandbox egress credential context is not authorized",
|
|
2216
2138
|
403
|
|
@@ -2526,16 +2448,11 @@ async function lookupSlackUser(teamId, userId) {
|
|
|
2526
2448
|
}
|
|
2527
2449
|
);
|
|
2528
2450
|
if (!response.ok) {
|
|
2529
|
-
logWarn(
|
|
2530
|
-
"
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
"app.slack.team_id": teamId,
|
|
2535
|
-
"http.response.status_code": response.status
|
|
2536
|
-
},
|
|
2537
|
-
"Slack user lookup request failed"
|
|
2538
|
-
);
|
|
2451
|
+
logWarn("slack.user.lookup.failed", {
|
|
2452
|
+
"enduser.id": userId,
|
|
2453
|
+
"app.slack.team_id": teamId,
|
|
2454
|
+
"http.response.status_code": response.status
|
|
2455
|
+
});
|
|
2539
2456
|
return null;
|
|
2540
2457
|
}
|
|
2541
2458
|
const parsed = slackUserInfoSchema.safeParse(await response.json());
|
|
@@ -2553,16 +2470,11 @@ async function lookupSlackUser(teamId, userId) {
|
|
|
2553
2470
|
writeToCache(teamId, userId, result);
|
|
2554
2471
|
return result;
|
|
2555
2472
|
} catch (error) {
|
|
2556
|
-
logWarn(
|
|
2557
|
-
"
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
"app.slack.team_id": teamId,
|
|
2562
|
-
"exception.message": error instanceof Error ? error.message : String(error)
|
|
2563
|
-
},
|
|
2564
|
-
"Slack user lookup failed with exception"
|
|
2565
|
-
);
|
|
2473
|
+
logWarn("slack.user.lookup.failed", {
|
|
2474
|
+
"enduser.id": userId,
|
|
2475
|
+
"app.slack.team_id": teamId,
|
|
2476
|
+
"exception.message": error instanceof Error ? error.message : String(error)
|
|
2477
|
+
});
|
|
2566
2478
|
return null;
|
|
2567
2479
|
}
|
|
2568
2480
|
}
|
|
@@ -3343,12 +3255,9 @@ async function handleUnlink(event, actorId, provider) {
|
|
|
3343
3255
|
}
|
|
3344
3256
|
const tokenStore = createUserTokenStore();
|
|
3345
3257
|
await tokenStore.delete(actorId, provider);
|
|
3346
|
-
logInfo(
|
|
3347
|
-
"
|
|
3348
|
-
|
|
3349
|
-
{ "app.credential.provider": provider },
|
|
3350
|
-
`Unlinked ${formatProviderLabel(provider)} account via ${getCommandName()} slash command`
|
|
3351
|
-
);
|
|
3258
|
+
logInfo("slash_command.credential.unlinked", {
|
|
3259
|
+
"app.credential.provider": provider
|
|
3260
|
+
});
|
|
3352
3261
|
await postEphemeral(
|
|
3353
3262
|
event,
|
|
3354
3263
|
`Your ${formatProviderLabel(provider)} account has been unlinked.`
|
|
@@ -3390,6 +3299,18 @@ async function unlinkProvider(userId, provider, userTokenStore) {
|
|
|
3390
3299
|
}
|
|
3391
3300
|
|
|
3392
3301
|
// src/chat/ingress/slack-webhook.ts
|
|
3302
|
+
function slackEventLogContext(event) {
|
|
3303
|
+
const channelId = event?.channel?.trim() || void 0;
|
|
3304
|
+
const threadTs = event?.thread_ts?.trim() || event?.ts?.trim() || void 0;
|
|
3305
|
+
const conversationId = channelId && threadTs ? `slack:${channelId}:${threadTs}` : void 0;
|
|
3306
|
+
return {
|
|
3307
|
+
platform: "slack",
|
|
3308
|
+
conversationId,
|
|
3309
|
+
messageConversationId: conversationId,
|
|
3310
|
+
destinationName: channelId,
|
|
3311
|
+
userId: event?.user?.trim() || void 0
|
|
3312
|
+
};
|
|
3313
|
+
}
|
|
3393
3314
|
var IGNORED_MESSAGE_SUBTYPES = /* @__PURE__ */ new Set([
|
|
3394
3315
|
"message_changed",
|
|
3395
3316
|
"message_deleted",
|
|
@@ -3559,17 +3480,17 @@ async function handleSlackEvent(args) {
|
|
|
3559
3480
|
const installation = installationFromEnvelope(args.body);
|
|
3560
3481
|
const receivedAtMs = Date.now();
|
|
3561
3482
|
async function publishAppHomeViewBestEffort(userId) {
|
|
3562
|
-
|
|
3563
|
-
|
|
3564
|
-
|
|
3565
|
-
|
|
3566
|
-
|
|
3567
|
-
|
|
3568
|
-
|
|
3569
|
-
|
|
3570
|
-
|
|
3571
|
-
}
|
|
3572
|
-
}
|
|
3483
|
+
await withLogContext({ platform: "slack", userId }, async () => {
|
|
3484
|
+
try {
|
|
3485
|
+
await publishAppHomeView(
|
|
3486
|
+
getSlackClient(),
|
|
3487
|
+
userId,
|
|
3488
|
+
getUserTokenStore(args.services)
|
|
3489
|
+
);
|
|
3490
|
+
} catch (error) {
|
|
3491
|
+
logException(error, "slack.app_home.publish.failed");
|
|
3492
|
+
}
|
|
3493
|
+
});
|
|
3573
3494
|
}
|
|
3574
3495
|
await runWithWorkspaceTeamId(
|
|
3575
3496
|
installation.teamId,
|
|
@@ -3722,22 +3643,16 @@ async function handleInteractivePayload(args) {
|
|
|
3722
3643
|
try {
|
|
3723
3644
|
await unlinkProvider(userId, provider, args.userTokenStore);
|
|
3724
3645
|
} catch (error) {
|
|
3725
|
-
logException(
|
|
3726
|
-
|
|
3727
|
-
|
|
3728
|
-
{ userId },
|
|
3729
|
-
{ "app.credential.provider": provider }
|
|
3730
|
-
);
|
|
3646
|
+
logException(error, "app_home.disconnect_unlink.failed", {
|
|
3647
|
+
"app.credential.provider": provider
|
|
3648
|
+
});
|
|
3731
3649
|
}
|
|
3732
3650
|
try {
|
|
3733
3651
|
await publishAppHomeView(getSlackClient(), userId, args.userTokenStore);
|
|
3734
3652
|
} catch (error) {
|
|
3735
|
-
logException(
|
|
3736
|
-
|
|
3737
|
-
|
|
3738
|
-
{ userId },
|
|
3739
|
-
{ "app.credential.provider": provider }
|
|
3740
|
-
);
|
|
3653
|
+
logException(error, "app_home.disconnect_publish.failed", {
|
|
3654
|
+
"app.credential.provider": provider
|
|
3655
|
+
});
|
|
3741
3656
|
}
|
|
3742
3657
|
}
|
|
3743
3658
|
);
|
|
@@ -3764,23 +3679,27 @@ async function handleSlackForm(args) {
|
|
|
3764
3679
|
const installation2 = installationFromForm(params);
|
|
3765
3680
|
enqueue(
|
|
3766
3681
|
args.waitUntil,
|
|
3767
|
-
|
|
3768
|
-
|
|
3769
|
-
|
|
3770
|
-
|
|
3771
|
-
|
|
3772
|
-
|
|
3773
|
-
|
|
3682
|
+
withLogContext(
|
|
3683
|
+
{
|
|
3684
|
+
platform: "slack",
|
|
3685
|
+
userId: params.get("user_id")?.trim() || void 0
|
|
3686
|
+
},
|
|
3687
|
+
() => runWithWorkspaceTeamId(
|
|
3688
|
+
installation2.teamId,
|
|
3689
|
+
() => runWithSlackInstallation({
|
|
3774
3690
|
adapter,
|
|
3775
|
-
|
|
3776
|
-
state
|
|
3691
|
+
installation: installation2,
|
|
3692
|
+
state,
|
|
3693
|
+
task: () => handleSlashCommandForm({
|
|
3694
|
+
adapter,
|
|
3695
|
+
params,
|
|
3696
|
+
state
|
|
3697
|
+
})
|
|
3777
3698
|
})
|
|
3699
|
+
).catch((error) => {
|
|
3700
|
+
logException(error, "slash_command.failed");
|
|
3778
3701
|
})
|
|
3779
|
-
)
|
|
3780
|
-
logException(error, "slash_command_failed", {
|
|
3781
|
-
userId: params.get("user_id") ?? void 0
|
|
3782
|
-
});
|
|
3783
|
-
})
|
|
3702
|
+
)
|
|
3784
3703
|
);
|
|
3785
3704
|
return new Response("", { status: 200 });
|
|
3786
3705
|
}
|
|
@@ -3795,22 +3714,26 @@ async function handleSlackForm(args) {
|
|
|
3795
3714
|
const installation = installationFromInteractive(payload);
|
|
3796
3715
|
enqueue(
|
|
3797
3716
|
args.waitUntil,
|
|
3798
|
-
|
|
3799
|
-
|
|
3800
|
-
|
|
3801
|
-
|
|
3802
|
-
|
|
3803
|
-
|
|
3804
|
-
|
|
3805
|
-
|
|
3806
|
-
|
|
3717
|
+
withLogContext(
|
|
3718
|
+
{
|
|
3719
|
+
platform: "slack",
|
|
3720
|
+
userId: payload.user?.id?.trim() || void 0
|
|
3721
|
+
},
|
|
3722
|
+
() => runWithWorkspaceTeamId(
|
|
3723
|
+
installation.teamId,
|
|
3724
|
+
() => runWithSlackInstallation({
|
|
3725
|
+
adapter,
|
|
3726
|
+
installation,
|
|
3727
|
+
state,
|
|
3728
|
+
task: () => handleInteractivePayload({
|
|
3729
|
+
payload,
|
|
3730
|
+
userTokenStore: getUserTokenStore(args.services)
|
|
3731
|
+
})
|
|
3807
3732
|
})
|
|
3733
|
+
).catch((error) => {
|
|
3734
|
+
logException(error, "slack.interactive.payload.failed");
|
|
3808
3735
|
})
|
|
3809
|
-
)
|
|
3810
|
-
logException(error, "slack_interactive_payload_failed", {
|
|
3811
|
-
userId: payload.user?.id?.trim() || void 0
|
|
3812
|
-
});
|
|
3813
|
-
})
|
|
3736
|
+
)
|
|
3814
3737
|
);
|
|
3815
3738
|
return new Response("", { status: 200 });
|
|
3816
3739
|
}
|
|
@@ -3837,27 +3760,42 @@ async function handleSlackWebhook(args) {
|
|
|
3837
3760
|
return Response.json({ challenge });
|
|
3838
3761
|
}
|
|
3839
3762
|
if (parsed.type === "event_callback") {
|
|
3840
|
-
const
|
|
3841
|
-
body: parsed,
|
|
3842
|
-
services: args.services
|
|
3843
|
-
});
|
|
3763
|
+
const eventLogContext = slackEventLogContext(parsed.event);
|
|
3844
3764
|
if (shouldPersistBeforeAck(parsed)) {
|
|
3845
|
-
|
|
3846
|
-
|
|
3847
|
-
|
|
3848
|
-
|
|
3849
|
-
|
|
3850
|
-
|
|
3851
|
-
|
|
3765
|
+
const failureResponse = await withLogContext(
|
|
3766
|
+
eventLogContext,
|
|
3767
|
+
async () => {
|
|
3768
|
+
try {
|
|
3769
|
+
await handleSlackEvent({
|
|
3770
|
+
body: parsed,
|
|
3771
|
+
services: args.services
|
|
3772
|
+
});
|
|
3773
|
+
} catch (error) {
|
|
3774
|
+
if (error instanceof SlackEventPersistenceError) {
|
|
3775
|
+
logException(error.cause, "slack.event.persist.failed");
|
|
3776
|
+
} else {
|
|
3777
|
+
logException(error, "slack.event.routing.failed");
|
|
3778
|
+
}
|
|
3779
|
+
return new Response("Slack event handling failed", { status: 503 });
|
|
3780
|
+
}
|
|
3781
|
+
return void 0;
|
|
3852
3782
|
}
|
|
3853
|
-
|
|
3783
|
+
);
|
|
3784
|
+
if (failureResponse) {
|
|
3785
|
+
return failureResponse;
|
|
3854
3786
|
}
|
|
3855
3787
|
} else {
|
|
3856
3788
|
enqueue(
|
|
3857
3789
|
args.waitUntil,
|
|
3858
|
-
|
|
3859
|
-
|
|
3860
|
-
|
|
3790
|
+
withLogContext(
|
|
3791
|
+
eventLogContext,
|
|
3792
|
+
() => handleSlackEvent({
|
|
3793
|
+
body: parsed,
|
|
3794
|
+
services: args.services
|
|
3795
|
+
}).catch((error) => {
|
|
3796
|
+
logException(error, "slack.event.enqueue.failed");
|
|
3797
|
+
})
|
|
3798
|
+
)
|
|
3861
3799
|
);
|
|
3862
3800
|
}
|
|
3863
3801
|
}
|
|
@@ -3887,17 +3825,12 @@ async function handleWebhookRequest(request, platform, run) {
|
|
|
3887
3825
|
} catch {
|
|
3888
3826
|
responseBodySnippet = void 0;
|
|
3889
3827
|
}
|
|
3890
|
-
logWarn(
|
|
3891
|
-
"
|
|
3892
|
-
|
|
3893
|
-
|
|
3894
|
-
|
|
3895
|
-
|
|
3896
|
-
"http.request.header.x_slack_request_timestamp": request.headers.get("x-slack-request-timestamp") ?? void 0,
|
|
3897
|
-
...responseBodySnippet ? { "app.webhook.response_body": responseBodySnippet } : {}
|
|
3898
|
-
},
|
|
3899
|
-
`Webhook ${platform} returned ${response.status}`
|
|
3900
|
-
);
|
|
3828
|
+
logWarn("webhook.response.unsuccessful", {
|
|
3829
|
+
"http.response.status_code": response.status,
|
|
3830
|
+
"http.request.header.x_slack_signature": request.headers.get("x-slack-signature") ?? void 0,
|
|
3831
|
+
"http.request.header.x_slack_request_timestamp": request.headers.get("x-slack-request-timestamp") ?? void 0,
|
|
3832
|
+
...responseBodySnippet ? { "app.webhook.response_body": responseBodySnippet } : {}
|
|
3833
|
+
});
|
|
3901
3834
|
}
|
|
3902
3835
|
setSpanAttributes({
|
|
3903
3836
|
"http.response.status_code": response.status
|
|
@@ -3915,7 +3848,7 @@ async function handleWebhookRequest(request, platform, run) {
|
|
|
3915
3848
|
}
|
|
3916
3849
|
);
|
|
3917
3850
|
} catch (error) {
|
|
3918
|
-
logException(error, "
|
|
3851
|
+
logException(error, "webhook.handler.failed");
|
|
3919
3852
|
throw error;
|
|
3920
3853
|
}
|
|
3921
3854
|
});
|
|
@@ -4017,17 +3950,12 @@ async function recordFailedDeliveryAttempt(args) {
|
|
|
4017
3950
|
state: args.options.state
|
|
4018
3951
|
});
|
|
4019
3952
|
for (const message of failure.deadLetteredMessages) {
|
|
4020
|
-
logWarn(
|
|
4021
|
-
"
|
|
4022
|
-
|
|
4023
|
-
|
|
4024
|
-
|
|
4025
|
-
|
|
4026
|
-
"app.inbound.message_id": message.inboundMessageId,
|
|
4027
|
-
"app.inbound.pending_count": failure.pendingCount
|
|
4028
|
-
},
|
|
4029
|
-
"Conversation work message consumed after exceeding the delivery attempt limit"
|
|
4030
|
-
);
|
|
3953
|
+
logWarn("conversation.work.dead_lettered", {
|
|
3954
|
+
"app.conversation.source": message.source,
|
|
3955
|
+
"app.inbound.attempt_count": message.attemptCount ?? 0,
|
|
3956
|
+
"app.inbound.message_id": message.inboundMessageId,
|
|
3957
|
+
"app.inbound.pending_count": failure.pendingCount
|
|
3958
|
+
});
|
|
4031
3959
|
}
|
|
4032
3960
|
return failure;
|
|
4033
3961
|
}
|
|
@@ -4047,22 +3975,11 @@ function startLeaseCheckIn(args) {
|
|
|
4047
3975
|
(checkedIn) => {
|
|
4048
3976
|
if (!checkedIn) {
|
|
4049
3977
|
args.onLostLease();
|
|
4050
|
-
logWarn(
|
|
4051
|
-
"conversation_work_check_in_failed",
|
|
4052
|
-
{ conversationId: args.conversationId },
|
|
4053
|
-
{},
|
|
4054
|
-
"Conversation work check-in lost its lease"
|
|
4055
|
-
);
|
|
3978
|
+
logWarn("conversation.work.check_in.failed");
|
|
4056
3979
|
}
|
|
4057
3980
|
},
|
|
4058
3981
|
(error) => {
|
|
4059
|
-
logException(
|
|
4060
|
-
error,
|
|
4061
|
-
"conversation_work_check_in_failed",
|
|
4062
|
-
{ conversationId: args.conversationId },
|
|
4063
|
-
{},
|
|
4064
|
-
"Conversation work check-in failed"
|
|
4065
|
-
);
|
|
3982
|
+
logException(error, "conversation.work.check_in.failed");
|
|
4066
3983
|
}
|
|
4067
3984
|
);
|
|
4068
3985
|
}, args.options.checkInIntervalMs ?? CONVERSATION_WORK_CHECK_IN_INTERVAL_MS);
|
|
@@ -4070,6 +3987,12 @@ function startLeaseCheckIn(args) {
|
|
|
4070
3987
|
return timer;
|
|
4071
3988
|
}
|
|
4072
3989
|
async function processConversationWork(message, options) {
|
|
3990
|
+
return withLogContext(
|
|
3991
|
+
{ conversationId: message.conversationId },
|
|
3992
|
+
() => processConversationWorkInContext(message, options)
|
|
3993
|
+
);
|
|
3994
|
+
}
|
|
3995
|
+
async function processConversationWorkInContext(message, options) {
|
|
4073
3996
|
const conversationId = message.conversationId;
|
|
4074
3997
|
let initial;
|
|
4075
3998
|
try {
|
|
@@ -4133,14 +4056,9 @@ async function processConversationWork(message, options) {
|
|
|
4133
4056
|
replaceExistingWake: true,
|
|
4134
4057
|
state: options.state
|
|
4135
4058
|
});
|
|
4136
|
-
logInfo(
|
|
4137
|
-
"
|
|
4138
|
-
|
|
4139
|
-
{
|
|
4140
|
-
"app.lease.expires_at_ms": lease.leaseExpiresAtMs
|
|
4141
|
-
},
|
|
4142
|
-
"Conversation work nudge deferred for active lease"
|
|
4143
|
-
);
|
|
4059
|
+
logInfo("conversation.work.nudge.deferred", {
|
|
4060
|
+
"app.lease.expires_at_ms": lease.leaseExpiresAtMs
|
|
4061
|
+
});
|
|
4144
4062
|
return { status: "active" };
|
|
4145
4063
|
}
|
|
4146
4064
|
const startedAtMs = now(options);
|
|
@@ -4158,15 +4076,10 @@ async function processConversationWork(message, options) {
|
|
|
4158
4076
|
onLostLease: markLeaseLost,
|
|
4159
4077
|
options
|
|
4160
4078
|
});
|
|
4161
|
-
logInfo(
|
|
4162
|
-
"
|
|
4163
|
-
|
|
4164
|
-
|
|
4165
|
-
"app.lease.expires_at_ms": lease.leaseExpiresAtMs,
|
|
4166
|
-
"app.worker.soft_yield_deadline_ms": softYieldDeadlineMs
|
|
4167
|
-
},
|
|
4168
|
-
"Conversation work lease acquired"
|
|
4169
|
-
);
|
|
4079
|
+
logInfo("conversation.work.lease.acquired", {
|
|
4080
|
+
"app.lease.expires_at_ms": lease.leaseExpiresAtMs,
|
|
4081
|
+
"app.worker.soft_yield_deadline_ms": softYieldDeadlineMs
|
|
4082
|
+
});
|
|
4170
4083
|
const drain = (handle) => drainConversationMailbox({
|
|
4171
4084
|
conversationId,
|
|
4172
4085
|
leaseToken: lease.leaseToken,
|
|
@@ -4218,15 +4131,10 @@ async function processConversationWork(message, options) {
|
|
|
4218
4131
|
if (!released) {
|
|
4219
4132
|
return { status: "lost_lease" };
|
|
4220
4133
|
}
|
|
4221
|
-
logInfo(
|
|
4222
|
-
"
|
|
4223
|
-
|
|
4224
|
-
|
|
4225
|
-
"app.worker.elapsed_ms": now(options) - startedAtMs,
|
|
4226
|
-
"app.worker.soft_yield_deadline_ms": softYieldDeadlineMs
|
|
4227
|
-
},
|
|
4228
|
-
"Conversation work yielded cooperatively"
|
|
4229
|
-
);
|
|
4134
|
+
logInfo("conversation.work.yielded", {
|
|
4135
|
+
"app.worker.elapsed_ms": now(options) - startedAtMs,
|
|
4136
|
+
"app.worker.soft_yield_deadline_ms": softYieldDeadlineMs
|
|
4137
|
+
});
|
|
4230
4138
|
return { status: "yielded" };
|
|
4231
4139
|
};
|
|
4232
4140
|
try {
|
|
@@ -4439,14 +4347,9 @@ async function processConversationWork(message, options) {
|
|
|
4439
4347
|
});
|
|
4440
4348
|
return wake.status === "enqueued" ? { status: "pending_requeued" } : { status: "completed" };
|
|
4441
4349
|
}
|
|
4442
|
-
logInfo(
|
|
4443
|
-
"
|
|
4444
|
-
|
|
4445
|
-
{
|
|
4446
|
-
"app.worker.elapsed_ms": now(options) - startedAtMs
|
|
4447
|
-
},
|
|
4448
|
-
"Conversation work completed"
|
|
4449
|
-
);
|
|
4350
|
+
logInfo("conversation.work.completed", {
|
|
4351
|
+
"app.worker.elapsed_ms": now(options) - startedAtMs
|
|
4352
|
+
});
|
|
4450
4353
|
return { status: "completed" };
|
|
4451
4354
|
} catch (error) {
|
|
4452
4355
|
const errorNowMs = now(options);
|
|
@@ -4501,24 +4404,12 @@ async function processConversationWork(message, options) {
|
|
|
4501
4404
|
}
|
|
4502
4405
|
recoveryRecorded = true;
|
|
4503
4406
|
} catch (recoveryError) {
|
|
4504
|
-
logException(
|
|
4505
|
-
recoveryError,
|
|
4506
|
-
"conversation_work_requeue_failed",
|
|
4507
|
-
{ conversationId },
|
|
4508
|
-
{},
|
|
4509
|
-
"Conversation work recovery failed after runner error"
|
|
4510
|
-
);
|
|
4407
|
+
logException(recoveryError, "conversation.work.requeue.failed");
|
|
4511
4408
|
}
|
|
4512
4409
|
if (!isProviderRetryError(error)) {
|
|
4513
|
-
logException(
|
|
4514
|
-
|
|
4515
|
-
|
|
4516
|
-
{ conversationId },
|
|
4517
|
-
{
|
|
4518
|
-
"app.worker.elapsed_ms": now(options) - startedAtMs
|
|
4519
|
-
},
|
|
4520
|
-
"Conversation work failed"
|
|
4521
|
-
);
|
|
4410
|
+
logException(error, "conversation.work.failed", {
|
|
4411
|
+
"app.worker.elapsed_ms": now(options) - startedAtMs
|
|
4412
|
+
});
|
|
4522
4413
|
}
|
|
4523
4414
|
if (!recoveryRecorded) {
|
|
4524
4415
|
throw error;
|
|
@@ -4580,18 +4471,15 @@ async function handleConversationQueueMessage(message, metadata, options) {
|
|
|
4580
4471
|
}
|
|
4581
4472
|
}
|
|
4582
4473
|
function logConversationQueueMessageRejected(reason, metadata, context = {}) {
|
|
4583
|
-
|
|
4584
|
-
"
|
|
4585
|
-
context.conversationId ? { conversationId: context.conversationId } : {},
|
|
4586
|
-
{
|
|
4474
|
+
withLogContext({ conversationId: context.conversationId }, () => {
|
|
4475
|
+
logWarn("conversation.queue.message.rejected", {
|
|
4587
4476
|
"app.queue.consumer_group": metadata.consumerGroup,
|
|
4588
4477
|
"app.queue.delivery_count": metadata.deliveryCount,
|
|
4589
4478
|
"app.queue.message_id": metadata.messageId,
|
|
4590
4479
|
"app.queue.reject_reason": reason,
|
|
4591
4480
|
"app.queue.topic_name": metadata.topicName
|
|
4592
|
-
}
|
|
4593
|
-
|
|
4594
|
-
);
|
|
4481
|
+
});
|
|
4482
|
+
});
|
|
4595
4483
|
}
|
|
4596
4484
|
function handleConversationQueueRetry(error, metadata) {
|
|
4597
4485
|
if (!isConversationQueueMessageRejectedError(error)) {
|
|
@@ -4633,18 +4521,13 @@ import {
|
|
|
4633
4521
|
var PLUGIN_TASK_DEV_CONSUMER_GROUP = "junior_plugin_tasks_dev";
|
|
4634
4522
|
var PLUGIN_TASK_MAX_DELIVERIES = 5;
|
|
4635
4523
|
function logPluginTaskQueueMessageRejected(reason, metadata) {
|
|
4636
|
-
logWarn(
|
|
4637
|
-
"
|
|
4638
|
-
|
|
4639
|
-
|
|
4640
|
-
|
|
4641
|
-
|
|
4642
|
-
|
|
4643
|
-
"app.queue.reject_reason": reason,
|
|
4644
|
-
"app.queue.topic_name": metadata.topicName
|
|
4645
|
-
},
|
|
4646
|
-
"Plugin task queue message rejected without retry"
|
|
4647
|
-
);
|
|
4524
|
+
logWarn("plugin.task.queue_message.rejected", {
|
|
4525
|
+
"app.queue.consumer_group": metadata.consumerGroup,
|
|
4526
|
+
"app.queue.delivery_count": metadata.deliveryCount,
|
|
4527
|
+
"app.queue.message_id": metadata.messageId,
|
|
4528
|
+
"app.queue.reject_reason": reason,
|
|
4529
|
+
"app.queue.topic_name": metadata.topicName
|
|
4530
|
+
});
|
|
4648
4531
|
}
|
|
4649
4532
|
async function handlePluginTaskQueueMessage(message, metadata) {
|
|
4650
4533
|
const verification = verifyPluginTaskQueueMessage(message);
|
|
@@ -5168,8 +5051,7 @@ function createSlackTurnRuntime(deps) {
|
|
|
5168
5051
|
const started = await startSlackProcessingReaction({
|
|
5169
5052
|
thread,
|
|
5170
5053
|
message: targetMessage,
|
|
5171
|
-
logException: deps.logException
|
|
5172
|
-
logContext: context
|
|
5054
|
+
logException: deps.logException
|
|
5173
5055
|
});
|
|
5174
5056
|
processingReactions.push(started);
|
|
5175
5057
|
if (reactionKey) {
|
|
@@ -5185,17 +5067,11 @@ function createSlackTurnRuntime(deps) {
|
|
|
5185
5067
|
try {
|
|
5186
5068
|
await args.thread.post(buildTurnFailureResponse(args.eventId));
|
|
5187
5069
|
} catch (postError) {
|
|
5188
|
-
deps.logException(
|
|
5189
|
-
|
|
5190
|
-
args.
|
|
5191
|
-
|
|
5192
|
-
|
|
5193
|
-
"app.slack.reply_stage": "error_fallback_post",
|
|
5194
|
-
"app.error.original_event_id": args.eventId,
|
|
5195
|
-
...getSlackErrorObservabilityAttributes(postError)
|
|
5196
|
-
},
|
|
5197
|
-
args.postFailureBody
|
|
5198
|
-
);
|
|
5070
|
+
deps.logException(postError, args.postFailureEventName, {
|
|
5071
|
+
"app.slack.reply_stage": "error_fallback_post",
|
|
5072
|
+
"app.error.original_event_id": args.eventId,
|
|
5073
|
+
...getSlackErrorObservabilityAttributes(postError)
|
|
5074
|
+
});
|
|
5199
5075
|
throw postError;
|
|
5200
5076
|
}
|
|
5201
5077
|
};
|
|
@@ -5233,8 +5109,7 @@ function createSlackTurnRuntime(deps) {
|
|
|
5233
5109
|
};
|
|
5234
5110
|
};
|
|
5235
5111
|
const logSkippedSubscribedDecision = (args) => {
|
|
5236
|
-
|
|
5237
|
-
"subscribed_message_reply_skipped",
|
|
5112
|
+
withLogContext(
|
|
5238
5113
|
logContext({
|
|
5239
5114
|
threadId: args.context.threadId,
|
|
5240
5115
|
actorId: args.context.actorId,
|
|
@@ -5242,10 +5117,11 @@ function createSlackTurnRuntime(deps) {
|
|
|
5242
5117
|
channelId: args.context.channelId,
|
|
5243
5118
|
runId: args.context.runId
|
|
5244
5119
|
}),
|
|
5245
|
-
{
|
|
5246
|
-
"
|
|
5247
|
-
|
|
5248
|
-
|
|
5120
|
+
() => {
|
|
5121
|
+
deps.logWarn("subscribed_message.reply.skipped", {
|
|
5122
|
+
"app.decision.reason": args.decision.reason
|
|
5123
|
+
});
|
|
5124
|
+
}
|
|
5249
5125
|
);
|
|
5250
5126
|
};
|
|
5251
5127
|
const skipSubscribedMessage = async (args) => {
|
|
@@ -5337,13 +5213,9 @@ function createSlackTurnRuntime(deps) {
|
|
|
5337
5213
|
});
|
|
5338
5214
|
}
|
|
5339
5215
|
} catch (error) {
|
|
5340
|
-
|
|
5341
|
-
error,
|
|
5342
|
-
|
|
5343
|
-
args.context,
|
|
5344
|
-
{},
|
|
5345
|
-
"Failed to reapply skipped steering message records"
|
|
5346
|
-
);
|
|
5216
|
+
withLogContext(args.context, () => {
|
|
5217
|
+
deps.logException(error, "agent.turn.steering_flush.failed");
|
|
5218
|
+
});
|
|
5347
5219
|
}
|
|
5348
5220
|
};
|
|
5349
5221
|
return {
|
|
@@ -5442,50 +5314,40 @@ function createSlackTurnRuntime(deps) {
|
|
|
5442
5314
|
});
|
|
5443
5315
|
});
|
|
5444
5316
|
} catch (error) {
|
|
5445
|
-
const
|
|
5446
|
-
const failureCause = classifiedFailure?.cause ?? error;
|
|
5447
|
-
if (shouldRethrowTurnControlError(failureCause)) {
|
|
5448
|
-
throw failureCause;
|
|
5449
|
-
}
|
|
5450
|
-
const errorContext = logContext({
|
|
5317
|
+
const failureLogContext = logContext({
|
|
5451
5318
|
threadId: deps.getThreadId(thread, message),
|
|
5452
5319
|
actorId: message.author.userId,
|
|
5453
5320
|
actorUserName: actorUserName(message),
|
|
5454
5321
|
channelId: deps.getChannelId(thread, message),
|
|
5455
5322
|
runId: deps.getRunId(thread, message)
|
|
5456
5323
|
});
|
|
5324
|
+
const classifiedFailure = getConversationTurnBoundaryError(error);
|
|
5325
|
+
const failureCause = classifiedFailure?.cause ?? error;
|
|
5326
|
+
if (shouldRethrowTurnControlError(failureCause)) {
|
|
5327
|
+
throw failureCause;
|
|
5328
|
+
}
|
|
5457
5329
|
if (failureCause instanceof AuthorizationFlowDisabledError) {
|
|
5458
5330
|
return;
|
|
5459
5331
|
}
|
|
5460
5332
|
const failureCode = classifiedFailure?.failureCode ?? "agent_run_failed";
|
|
5461
5333
|
if (failureCause instanceof SlackActionError && failureCause.code === "read_only_channel") {
|
|
5462
|
-
|
|
5463
|
-
"
|
|
5464
|
-
|
|
5465
|
-
|
|
5466
|
-
|
|
5467
|
-
actorUserName: actorUserName(message),
|
|
5468
|
-
channelId: deps.getChannelId(thread, message),
|
|
5469
|
-
runId: deps.getRunId(thread, message)
|
|
5470
|
-
}),
|
|
5471
|
-
{},
|
|
5472
|
-
"Skipping reply to read-only channel"
|
|
5473
|
-
);
|
|
5334
|
+
withLogContext(failureLogContext, () => {
|
|
5335
|
+
deps.logWarn("mention.handler.skipped", {
|
|
5336
|
+
"app.decision.reason": "read_only_channel"
|
|
5337
|
+
});
|
|
5338
|
+
});
|
|
5474
5339
|
return;
|
|
5475
5340
|
}
|
|
5476
|
-
const eventId = classifiedFailure?.eventId ??
|
|
5477
|
-
|
|
5478
|
-
"
|
|
5479
|
-
errorContext,
|
|
5480
|
-
{},
|
|
5481
|
-
"onNewMention failed"
|
|
5341
|
+
const eventId = classifiedFailure?.eventId ?? withLogContext(
|
|
5342
|
+
failureLogContext,
|
|
5343
|
+
() => deps.logException(failureCause, "mention.handler.failed")
|
|
5482
5344
|
);
|
|
5483
5345
|
if (!acked && hooks.isFinalAttempt === false) {
|
|
5484
5346
|
return;
|
|
5485
5347
|
}
|
|
5486
5348
|
if (!eventId) {
|
|
5487
5349
|
throw new Error(
|
|
5488
|
-
"Sentry did not return an event ID for
|
|
5350
|
+
"Sentry did not return an event ID for mention.handler.failed"
|
|
5489
5351
|
);
|
|
5490
5352
|
}
|
|
5491
5353
|
let lifecycleError;
|
|
@@ -5502,10 +5364,8 @@ function createSlackTurnRuntime(deps) {
|
|
|
5502
5364
|
await hooks.beforeFirstResponsePost?.();
|
|
5503
5365
|
await postFallbackErrorReplyWithLogging({
|
|
5504
5366
|
thread,
|
|
5505
|
-
errorContext,
|
|
5506
5367
|
eventId,
|
|
5507
|
-
postFailureEventName: "
|
|
5508
|
-
postFailureBody: "Failed to post fallback error reply for mention handler"
|
|
5368
|
+
postFailureEventName: "mention.handler.failure_reply_post.failed"
|
|
5509
5369
|
});
|
|
5510
5370
|
if (lifecycleError) throw lifecycleError;
|
|
5511
5371
|
} finally {
|
|
@@ -5720,50 +5580,43 @@ function createSlackTurnRuntime(deps) {
|
|
|
5720
5580
|
});
|
|
5721
5581
|
});
|
|
5722
5582
|
} catch (error) {
|
|
5723
|
-
const
|
|
5724
|
-
const failureCause = classifiedFailure?.cause ?? error;
|
|
5725
|
-
if (shouldRethrowTurnControlError(failureCause)) {
|
|
5726
|
-
throw failureCause;
|
|
5727
|
-
}
|
|
5728
|
-
const errorContext = logContext({
|
|
5583
|
+
const failureLogContext = logContext({
|
|
5729
5584
|
threadId: deps.getThreadId(thread, message),
|
|
5730
5585
|
actorId: message.author.userId,
|
|
5731
5586
|
actorUserName: actorUserName(message),
|
|
5732
5587
|
channelId: deps.getChannelId(thread, message),
|
|
5733
5588
|
runId: deps.getRunId(thread, message)
|
|
5734
5589
|
});
|
|
5590
|
+
const classifiedFailure = getConversationTurnBoundaryError(error);
|
|
5591
|
+
const failureCause = classifiedFailure?.cause ?? error;
|
|
5592
|
+
if (shouldRethrowTurnControlError(failureCause)) {
|
|
5593
|
+
throw failureCause;
|
|
5594
|
+
}
|
|
5735
5595
|
if (failureCause instanceof AuthorizationFlowDisabledError) {
|
|
5736
5596
|
return;
|
|
5737
5597
|
}
|
|
5738
5598
|
const failureCode = classifiedFailure?.failureCode ?? "agent_run_failed";
|
|
5739
5599
|
if (failureCause instanceof SlackActionError && failureCause.code === "read_only_channel") {
|
|
5740
|
-
|
|
5741
|
-
"
|
|
5742
|
-
|
|
5743
|
-
|
|
5744
|
-
|
|
5745
|
-
actorUserName: actorUserName(message),
|
|
5746
|
-
channelId: deps.getChannelId(thread, message),
|
|
5747
|
-
runId: deps.getRunId(thread, message)
|
|
5748
|
-
}),
|
|
5749
|
-
{},
|
|
5750
|
-
"Skipping reply to read-only channel"
|
|
5751
|
-
);
|
|
5600
|
+
withLogContext(failureLogContext, () => {
|
|
5601
|
+
deps.logWarn("subscribed_message.handler.skipped", {
|
|
5602
|
+
"app.decision.reason": "read_only_channel"
|
|
5603
|
+
});
|
|
5604
|
+
});
|
|
5752
5605
|
return;
|
|
5753
5606
|
}
|
|
5754
|
-
const eventId = classifiedFailure?.eventId ??
|
|
5755
|
-
|
|
5756
|
-
|
|
5757
|
-
|
|
5758
|
-
|
|
5759
|
-
|
|
5607
|
+
const eventId = classifiedFailure?.eventId ?? withLogContext(
|
|
5608
|
+
failureLogContext,
|
|
5609
|
+
() => deps.logException(
|
|
5610
|
+
failureCause,
|
|
5611
|
+
"subscribed_message.handler.failed"
|
|
5612
|
+
)
|
|
5760
5613
|
);
|
|
5761
5614
|
if (!acked && hooks.isFinalAttempt === false) {
|
|
5762
5615
|
return;
|
|
5763
5616
|
}
|
|
5764
5617
|
if (!eventId) {
|
|
5765
5618
|
throw new Error(
|
|
5766
|
-
"Sentry did not return an event ID for
|
|
5619
|
+
"Sentry did not return an event ID for subscribed_message.handler.failed"
|
|
5767
5620
|
);
|
|
5768
5621
|
}
|
|
5769
5622
|
let lifecycleError;
|
|
@@ -5780,10 +5633,8 @@ function createSlackTurnRuntime(deps) {
|
|
|
5780
5633
|
await hooks.beforeFirstResponsePost?.();
|
|
5781
5634
|
await postFallbackErrorReplyWithLogging({
|
|
5782
5635
|
thread,
|
|
5783
|
-
errorContext,
|
|
5784
5636
|
eventId,
|
|
5785
|
-
postFailureEventName: "
|
|
5786
|
-
postFailureBody: "Failed to post fallback error reply for subscribed message handler"
|
|
5637
|
+
postFailureEventName: "subscribed_message.handler.failure_reply_post.failed"
|
|
5787
5638
|
});
|
|
5788
5639
|
if (lifecycleError) throw lifecycleError;
|
|
5789
5640
|
} finally {
|
|
@@ -5806,52 +5657,46 @@ function createSlackTurnRuntime(deps) {
|
|
|
5806
5657
|
}
|
|
5807
5658
|
},
|
|
5808
5659
|
async handleAssistantThreadStarted(event) {
|
|
5809
|
-
|
|
5810
|
-
|
|
5660
|
+
await withLogContext(
|
|
5661
|
+
logContext({
|
|
5811
5662
|
threadId: event.threadId,
|
|
5812
|
-
|
|
5813
|
-
|
|
5814
|
-
|
|
5815
|
-
|
|
5816
|
-
|
|
5817
|
-
|
|
5818
|
-
|
|
5819
|
-
|
|
5820
|
-
|
|
5821
|
-
|
|
5822
|
-
|
|
5823
|
-
|
|
5824
|
-
|
|
5825
|
-
|
|
5826
|
-
|
|
5827
|
-
|
|
5828
|
-
"onAssistantThreadStarted failed"
|
|
5829
|
-
);
|
|
5830
|
-
}
|
|
5663
|
+
actorId: event.userId,
|
|
5664
|
+
channelId: event.channelId
|
|
5665
|
+
}),
|
|
5666
|
+
async () => {
|
|
5667
|
+
try {
|
|
5668
|
+
await deps.initializeAssistantThread({
|
|
5669
|
+
threadId: event.threadId,
|
|
5670
|
+
channelId: event.channelId,
|
|
5671
|
+
threadTs: event.threadTs,
|
|
5672
|
+
sourceChannelId: event.context?.channelId
|
|
5673
|
+
});
|
|
5674
|
+
} catch (error) {
|
|
5675
|
+
deps.logException(error, "assistant.thread.initialization.failed");
|
|
5676
|
+
}
|
|
5677
|
+
}
|
|
5678
|
+
);
|
|
5831
5679
|
},
|
|
5832
5680
|
async handleAssistantContextChanged(event) {
|
|
5833
|
-
|
|
5834
|
-
|
|
5681
|
+
await withLogContext(
|
|
5682
|
+
logContext({
|
|
5835
5683
|
threadId: event.threadId,
|
|
5836
|
-
|
|
5837
|
-
|
|
5838
|
-
|
|
5839
|
-
|
|
5840
|
-
|
|
5841
|
-
|
|
5842
|
-
|
|
5843
|
-
|
|
5844
|
-
|
|
5845
|
-
|
|
5846
|
-
|
|
5847
|
-
|
|
5848
|
-
|
|
5849
|
-
|
|
5850
|
-
|
|
5851
|
-
|
|
5852
|
-
"onAssistantContextChanged failed"
|
|
5853
|
-
);
|
|
5854
|
-
}
|
|
5684
|
+
actorId: event.userId,
|
|
5685
|
+
channelId: event.channelId
|
|
5686
|
+
}),
|
|
5687
|
+
async () => {
|
|
5688
|
+
try {
|
|
5689
|
+
await deps.refreshAssistantThreadContext({
|
|
5690
|
+
threadId: event.threadId,
|
|
5691
|
+
channelId: event.channelId,
|
|
5692
|
+
threadTs: event.threadTs,
|
|
5693
|
+
sourceChannelId: event.context?.channelId
|
|
5694
|
+
});
|
|
5695
|
+
} catch (error) {
|
|
5696
|
+
deps.logException(error, "assistant.context.refresh.failed");
|
|
5697
|
+
}
|
|
5698
|
+
}
|
|
5699
|
+
);
|
|
5855
5700
|
}
|
|
5856
5701
|
};
|
|
5857
5702
|
}
|
|
@@ -5864,22 +5709,10 @@ function createSubscribedReplyPolicy(deps) {
|
|
|
5864
5709
|
modelId: botConfig.fastModelId,
|
|
5865
5710
|
input: args,
|
|
5866
5711
|
completeObject: deps.completeObject,
|
|
5867
|
-
logClassifierFailure: (error
|
|
5868
|
-
logWarn(
|
|
5869
|
-
"
|
|
5870
|
-
|
|
5871
|
-
messageConversationId: input.context.threadId,
|
|
5872
|
-
userId: input.context.actorId,
|
|
5873
|
-
destinationName: input.context.channelId,
|
|
5874
|
-
runId: input.context.runId,
|
|
5875
|
-
assistantUserName: botConfig.userName,
|
|
5876
|
-
modelId: botConfig.fastModelId
|
|
5877
|
-
},
|
|
5878
|
-
{
|
|
5879
|
-
"exception.message": error instanceof Error ? error.message : String(error)
|
|
5880
|
-
},
|
|
5881
|
-
"Subscribed-message classifier failed; skipping reply"
|
|
5882
|
-
);
|
|
5712
|
+
logClassifierFailure: (error) => {
|
|
5713
|
+
logWarn("subscribed_message.classifier.failed", {
|
|
5714
|
+
"exception.message": error instanceof Error ? error.message : String(error)
|
|
5715
|
+
});
|
|
5883
5716
|
}
|
|
5884
5717
|
});
|
|
5885
5718
|
const reason = decision.reasonDetail ? `${decision.reason}:${decision.reasonDetail}` : decision.reason;
|
|
@@ -5928,6 +5761,18 @@ function buildImageAttachmentPromptText(args) {
|
|
|
5928
5761
|
"</image-attachment>"
|
|
5929
5762
|
].join("\n");
|
|
5930
5763
|
}
|
|
5764
|
+
function buildImageAttachmentFailurePromptText(args) {
|
|
5765
|
+
return [
|
|
5766
|
+
"<image-attachment>",
|
|
5767
|
+
`filename: ${args.filename ?? "unnamed"}`,
|
|
5768
|
+
`media_type: ${args.mediaType}`,
|
|
5769
|
+
"<analysis-error>",
|
|
5770
|
+
args.message,
|
|
5771
|
+
"The image contents are unavailable.",
|
|
5772
|
+
"</analysis-error>",
|
|
5773
|
+
"</image-attachment>"
|
|
5774
|
+
].join("\n");
|
|
5775
|
+
}
|
|
5931
5776
|
async function summarizeImageWithVision(args) {
|
|
5932
5777
|
const visionModelId = botConfig.visionModelId;
|
|
5933
5778
|
if (!visionModelId) {
|
|
@@ -6076,22 +5921,10 @@ async function resolveUserAttachmentsWithDeps(attachments, context, deps) {
|
|
|
6076
5921
|
}
|
|
6077
5922
|
if (!data) continue;
|
|
6078
5923
|
if (data.byteLength > MAX_USER_ATTACHMENT_BYTES) {
|
|
6079
|
-
logWarn(
|
|
6080
|
-
"
|
|
6081
|
-
|
|
6082
|
-
|
|
6083
|
-
userId: context.actorId,
|
|
6084
|
-
destinationName: context.channelId,
|
|
6085
|
-
runId: context.runId,
|
|
6086
|
-
assistantUserName: botConfig.userName,
|
|
6087
|
-
modelId: standardModelId(botConfig)
|
|
6088
|
-
},
|
|
6089
|
-
{
|
|
6090
|
-
"file.size": data.byteLength,
|
|
6091
|
-
"app.file.mime_type": mediaType
|
|
6092
|
-
},
|
|
6093
|
-
"Skipping user attachment that exceeds size limit"
|
|
6094
|
-
);
|
|
5924
|
+
logWarn("attachment.size_limit.skipped", {
|
|
5925
|
+
"file.size": data.byteLength,
|
|
5926
|
+
"app.file.mime_type": mediaType
|
|
5927
|
+
});
|
|
6095
5928
|
continue;
|
|
6096
5929
|
}
|
|
6097
5930
|
resolvedAttachment.data = data;
|
|
@@ -6101,41 +5934,26 @@ async function resolveUserAttachmentsWithDeps(attachments, context, deps) {
|
|
|
6101
5934
|
const attachmentError = error instanceof ImageAttachmentProcessingError ? error : createImageAttachmentProcessingError({
|
|
6102
5935
|
filename: attachment.name
|
|
6103
5936
|
});
|
|
6104
|
-
logWarn(
|
|
6105
|
-
"image_attachment_processing_failed",
|
|
6106
|
-
{
|
|
6107
|
-
messageConversationId: context.threadId,
|
|
6108
|
-
userId: context.actorId,
|
|
6109
|
-
destinationName: context.channelId,
|
|
6110
|
-
runId: context.runId,
|
|
6111
|
-
assistantUserName: botConfig.userName,
|
|
6112
|
-
modelId: botConfig.visionModelId ?? standardModelId(botConfig)
|
|
6113
|
-
},
|
|
6114
|
-
{
|
|
6115
|
-
"exception.message": error instanceof Error ? error.message : String(error),
|
|
6116
|
-
"app.file.mime_type": mediaType,
|
|
6117
|
-
...attachment.name ? { "file.name": attachment.name } : {}
|
|
6118
|
-
},
|
|
6119
|
-
"Image attachment processing failed"
|
|
6120
|
-
);
|
|
6121
|
-
throw attachmentError;
|
|
6122
|
-
}
|
|
6123
|
-
logWarn(
|
|
6124
|
-
"attachment_resolution_failed",
|
|
6125
|
-
{
|
|
6126
|
-
messageConversationId: context.threadId,
|
|
6127
|
-
userId: context.actorId,
|
|
6128
|
-
destinationName: context.channelId,
|
|
6129
|
-
runId: context.runId,
|
|
6130
|
-
assistantUserName: botConfig.userName,
|
|
6131
|
-
modelId: standardModelId(botConfig)
|
|
6132
|
-
},
|
|
6133
|
-
{
|
|
5937
|
+
logWarn("image.attachment.processing.failed", {
|
|
6134
5938
|
"exception.message": error instanceof Error ? error.message : String(error),
|
|
6135
|
-
"app.file.mime_type": mediaType
|
|
6136
|
-
|
|
6137
|
-
|
|
6138
|
-
|
|
5939
|
+
"app.file.mime_type": mediaType,
|
|
5940
|
+
...attachment.name ? { "file.name": attachment.name } : {}
|
|
5941
|
+
});
|
|
5942
|
+
results.push({
|
|
5943
|
+
mediaType,
|
|
5944
|
+
filename: attachment.name,
|
|
5945
|
+
promptText: buildImageAttachmentFailurePromptText({
|
|
5946
|
+
filename: attachment.name,
|
|
5947
|
+
mediaType,
|
|
5948
|
+
message: attachmentError.message
|
|
5949
|
+
})
|
|
5950
|
+
});
|
|
5951
|
+
continue;
|
|
5952
|
+
}
|
|
5953
|
+
logWarn("attachment.resolution.failed", {
|
|
5954
|
+
"exception.message": error instanceof Error ? error.message : String(error),
|
|
5955
|
+
"app.file.mime_type": mediaType
|
|
5956
|
+
});
|
|
6139
5957
|
}
|
|
6140
5958
|
}
|
|
6141
5959
|
return results;
|
|
@@ -6170,23 +5988,11 @@ async function summarizeConversationImage(args, deps) {
|
|
|
6170
5988
|
}
|
|
6171
5989
|
return truncateVisionSummary(summary);
|
|
6172
5990
|
} catch (error) {
|
|
6173
|
-
logWarn(
|
|
6174
|
-
"
|
|
6175
|
-
|
|
6176
|
-
|
|
6177
|
-
|
|
6178
|
-
destinationName: args.context.channelId,
|
|
6179
|
-
runId: args.context.runId,
|
|
6180
|
-
assistantUserName: botConfig.userName,
|
|
6181
|
-
modelId: visionModelId
|
|
6182
|
-
},
|
|
6183
|
-
{
|
|
6184
|
-
"exception.message": error instanceof Error ? error.message : String(error),
|
|
6185
|
-
"app.file.id": args.fileId,
|
|
6186
|
-
"app.file.mime_type": args.mimeType
|
|
6187
|
-
},
|
|
6188
|
-
"Image analysis failed while hydrating conversation context"
|
|
6189
|
-
);
|
|
5991
|
+
logWarn("conversation.image.vision.failed", {
|
|
5992
|
+
"exception.message": error instanceof Error ? error.message : String(error),
|
|
5993
|
+
"app.file.id": args.fileId,
|
|
5994
|
+
"app.file.mime_type": args.mimeType
|
|
5995
|
+
});
|
|
6190
5996
|
return void 0;
|
|
6191
5997
|
}
|
|
6192
5998
|
}
|
|
@@ -6226,21 +6032,9 @@ async function hydrateConversationVisionContextWithDeps(conversation, context, d
|
|
|
6226
6032
|
targetMessageTs: [...messagesByTs.keys()]
|
|
6227
6033
|
});
|
|
6228
6034
|
} catch (error) {
|
|
6229
|
-
logWarn(
|
|
6230
|
-
"
|
|
6231
|
-
|
|
6232
|
-
messageConversationId: context.threadId,
|
|
6233
|
-
userId: context.actorId,
|
|
6234
|
-
destinationName: context.channelId,
|
|
6235
|
-
runId: context.runId,
|
|
6236
|
-
assistantUserName: botConfig.userName,
|
|
6237
|
-
modelId: standardModelId(botConfig)
|
|
6238
|
-
},
|
|
6239
|
-
{
|
|
6240
|
-
"exception.message": error instanceof Error ? error.message : String(error)
|
|
6241
|
-
},
|
|
6242
|
-
"Failed to fetch thread replies for image context hydration"
|
|
6243
|
-
);
|
|
6035
|
+
logWarn("conversation.image.replies_fetch.failed", {
|
|
6036
|
+
"exception.message": error instanceof Error ? error.message : String(error)
|
|
6037
|
+
});
|
|
6244
6038
|
return;
|
|
6245
6039
|
}
|
|
6246
6040
|
let cacheHits = 0;
|
|
@@ -6293,23 +6087,11 @@ async function hydrateConversationVisionContextWithDeps(conversation, context, d
|
|
|
6293
6087
|
const mimeType = toOptionalString(file.mimetype) ?? "application/octet-stream";
|
|
6294
6088
|
const fileSize = typeof file.size === "number" && Number.isFinite(file.size) ? file.size : void 0;
|
|
6295
6089
|
if (fileSize && fileSize > MAX_USER_ATTACHMENT_BYTES) {
|
|
6296
|
-
logWarn(
|
|
6297
|
-
"
|
|
6298
|
-
|
|
6299
|
-
|
|
6300
|
-
|
|
6301
|
-
destinationName: context.channelId,
|
|
6302
|
-
runId: context.runId,
|
|
6303
|
-
assistantUserName: botConfig.userName,
|
|
6304
|
-
modelId: standardModelId(botConfig)
|
|
6305
|
-
},
|
|
6306
|
-
{
|
|
6307
|
-
"app.file.id": fileId,
|
|
6308
|
-
"file.size": fileSize,
|
|
6309
|
-
"app.file.mime_type": mimeType
|
|
6310
|
-
},
|
|
6311
|
-
"Skipping thread image that exceeds size limit"
|
|
6312
|
-
);
|
|
6090
|
+
logWarn("conversation.image.size_limit.skipped", {
|
|
6091
|
+
"app.file.id": fileId,
|
|
6092
|
+
"file.size": fileSize,
|
|
6093
|
+
"app.file.mime_type": mimeType
|
|
6094
|
+
});
|
|
6313
6095
|
continue;
|
|
6314
6096
|
}
|
|
6315
6097
|
const downloadUrl = toOptionalString(file.url_private_download) ?? toOptionalString(file.url_private);
|
|
@@ -6320,43 +6102,19 @@ async function hydrateConversationVisionContextWithDeps(conversation, context, d
|
|
|
6320
6102
|
try {
|
|
6321
6103
|
imageData = await deps.downloadFile(downloadUrl);
|
|
6322
6104
|
} catch (error) {
|
|
6323
|
-
logWarn(
|
|
6324
|
-
"
|
|
6325
|
-
|
|
6326
|
-
|
|
6327
|
-
|
|
6328
|
-
destinationName: context.channelId,
|
|
6329
|
-
runId: context.runId,
|
|
6330
|
-
assistantUserName: botConfig.userName,
|
|
6331
|
-
modelId: standardModelId(botConfig)
|
|
6332
|
-
},
|
|
6333
|
-
{
|
|
6334
|
-
"exception.message": error instanceof Error ? error.message : String(error),
|
|
6335
|
-
"app.file.id": fileId,
|
|
6336
|
-
"app.file.mime_type": mimeType
|
|
6337
|
-
},
|
|
6338
|
-
"Failed to download thread image for context hydration"
|
|
6339
|
-
);
|
|
6105
|
+
logWarn("conversation.image.download.failed", {
|
|
6106
|
+
"exception.message": error instanceof Error ? error.message : String(error),
|
|
6107
|
+
"app.file.id": fileId,
|
|
6108
|
+
"app.file.mime_type": mimeType
|
|
6109
|
+
});
|
|
6340
6110
|
continue;
|
|
6341
6111
|
}
|
|
6342
6112
|
if (imageData.byteLength > MAX_USER_ATTACHMENT_BYTES) {
|
|
6343
|
-
logWarn(
|
|
6344
|
-
"
|
|
6345
|
-
|
|
6346
|
-
|
|
6347
|
-
|
|
6348
|
-
destinationName: context.channelId,
|
|
6349
|
-
runId: context.runId,
|
|
6350
|
-
assistantUserName: botConfig.userName,
|
|
6351
|
-
modelId: standardModelId(botConfig)
|
|
6352
|
-
},
|
|
6353
|
-
{
|
|
6354
|
-
"app.file.id": fileId,
|
|
6355
|
-
"file.size": imageData.byteLength,
|
|
6356
|
-
"app.file.mime_type": mimeType
|
|
6357
|
-
},
|
|
6358
|
-
"Skipping downloaded thread image that exceeds size limit"
|
|
6359
|
-
);
|
|
6113
|
+
logWarn("conversation.image.size_limit.skipped", {
|
|
6114
|
+
"app.file.id": fileId,
|
|
6115
|
+
"file.size": imageData.byteLength,
|
|
6116
|
+
"app.file.mime_type": mimeType
|
|
6117
|
+
});
|
|
6360
6118
|
continue;
|
|
6361
6119
|
}
|
|
6362
6120
|
const summary = await summarizeConversationImage(
|
|
@@ -6383,24 +6141,12 @@ async function hydrateConversationVisionContextWithDeps(conversation, context, d
|
|
|
6383
6141
|
updateConversationStats(conversation);
|
|
6384
6142
|
}
|
|
6385
6143
|
if (cacheHits > 0 || cacheMisses > 0 || analyzed > 0 || hydratedMessageIds.size > 0) {
|
|
6386
|
-
logInfo(
|
|
6387
|
-
"
|
|
6388
|
-
|
|
6389
|
-
|
|
6390
|
-
|
|
6391
|
-
|
|
6392
|
-
runId: context.runId,
|
|
6393
|
-
assistantUserName: botConfig.userName,
|
|
6394
|
-
modelId: standardModelId(botConfig)
|
|
6395
|
-
},
|
|
6396
|
-
{
|
|
6397
|
-
"app.conversation_image.cache_hits": cacheHits,
|
|
6398
|
-
"app.conversation_image.cache_misses": cacheMisses,
|
|
6399
|
-
"app.conversation_image.analyzed": analyzed,
|
|
6400
|
-
"app.conversation_image.messages_hydrated": hydratedMessageIds.size
|
|
6401
|
-
},
|
|
6402
|
-
"Hydrated conversation image context"
|
|
6403
|
-
);
|
|
6144
|
+
logInfo("conversation.image.context.hydrated", {
|
|
6145
|
+
"app.conversation_image.cache_hits": cacheHits,
|
|
6146
|
+
"app.conversation_image.cache_misses": cacheMisses,
|
|
6147
|
+
"app.conversation_image.analyzed": analyzed,
|
|
6148
|
+
"app.conversation_image.messages_hydrated": hydratedMessageIds.size
|
|
6149
|
+
});
|
|
6404
6150
|
}
|
|
6405
6151
|
if (!conversation.vision.backfillCompletedAtMs) {
|
|
6406
6152
|
conversation.vision.backfillCompletedAtMs = Date.now();
|
|
@@ -6508,21 +6254,9 @@ function maybeUpdateAssistantTitle(args) {
|
|
|
6508
6254
|
try {
|
|
6509
6255
|
title = await args.generateThreadTitle(titleSourceMessage.text);
|
|
6510
6256
|
} catch (error) {
|
|
6511
|
-
logWarn(
|
|
6512
|
-
"
|
|
6513
|
-
|
|
6514
|
-
messageConversationId: args.threadId,
|
|
6515
|
-
userId: args.actorId,
|
|
6516
|
-
destinationName: args.channelId,
|
|
6517
|
-
runId: args.runId,
|
|
6518
|
-
assistantUserName: args.assistantUserName,
|
|
6519
|
-
modelId: args.modelId
|
|
6520
|
-
},
|
|
6521
|
-
{
|
|
6522
|
-
"exception.message": error instanceof Error ? error.message : String(error)
|
|
6523
|
-
},
|
|
6524
|
-
"Thread title generation failed"
|
|
6525
|
-
);
|
|
6257
|
+
logWarn("thread.title.generation.failed", {
|
|
6258
|
+
"exception.message": error instanceof Error ? error.message : String(error)
|
|
6259
|
+
});
|
|
6526
6260
|
return void 0;
|
|
6527
6261
|
}
|
|
6528
6262
|
if (isDm) {
|
|
@@ -6543,34 +6277,13 @@ function maybeUpdateAssistantTitle(args) {
|
|
|
6543
6277
|
if (isSlackTitlePermissionError(error)) {
|
|
6544
6278
|
setSpanAttributes(assistantTitleErrorAttributes);
|
|
6545
6279
|
logError(
|
|
6546
|
-
"
|
|
6547
|
-
|
|
6548
|
-
messageConversationId: args.threadId,
|
|
6549
|
-
userId: args.actorId,
|
|
6550
|
-
destinationName: args.channelId,
|
|
6551
|
-
runId: args.runId,
|
|
6552
|
-
assistantUserName: args.assistantUserName,
|
|
6553
|
-
modelId: args.modelId
|
|
6554
|
-
},
|
|
6555
|
-
assistantTitleErrorAttributes,
|
|
6556
|
-
"Skipping Slack thread title update due to permission error"
|
|
6280
|
+
"thread.title.generation_permission.denied",
|
|
6281
|
+
assistantTitleErrorAttributes
|
|
6557
6282
|
);
|
|
6558
6283
|
} else {
|
|
6559
|
-
logWarn(
|
|
6560
|
-
"
|
|
6561
|
-
|
|
6562
|
-
messageConversationId: args.threadId,
|
|
6563
|
-
userId: args.actorId,
|
|
6564
|
-
destinationName: args.channelId,
|
|
6565
|
-
runId: args.runId,
|
|
6566
|
-
assistantUserName: args.assistantUserName,
|
|
6567
|
-
modelId: args.modelId
|
|
6568
|
-
},
|
|
6569
|
-
{
|
|
6570
|
-
"exception.message": error instanceof Error ? error.message : String(error)
|
|
6571
|
-
},
|
|
6572
|
-
"Slack thread title update failed"
|
|
6573
|
-
);
|
|
6284
|
+
logWarn("thread.title.slack_update.failed", {
|
|
6285
|
+
"exception.message": error instanceof Error ? error.message : String(error)
|
|
6286
|
+
});
|
|
6574
6287
|
}
|
|
6575
6288
|
}
|
|
6576
6289
|
}
|
|
@@ -6855,15 +6568,6 @@ function createReplyToThread(deps) {
|
|
|
6855
6568
|
});
|
|
6856
6569
|
const slackMessageTs = getSlackMessageTs(message);
|
|
6857
6570
|
const turnId = options.execution?.turnId ?? buildDeterministicTurnId(message.id);
|
|
6858
|
-
const turnTraceContext = {
|
|
6859
|
-
conversationId,
|
|
6860
|
-
messageConversationId: threadId,
|
|
6861
|
-
userId: message.author.userId,
|
|
6862
|
-
destinationName: channelId,
|
|
6863
|
-
runId,
|
|
6864
|
-
assistantUserName: botConfig.userName,
|
|
6865
|
-
modelId: standardModelId(botConfig)
|
|
6866
|
-
};
|
|
6867
6571
|
let beforeFirstResponsePostCalled = false;
|
|
6868
6572
|
const beforeFirstResponsePost = async () => {
|
|
6869
6573
|
if (beforeFirstResponsePostCalled) {
|
|
@@ -6894,17 +6598,11 @@ function createReplyToThread(deps) {
|
|
|
6894
6598
|
await thread.post(buildSlackOutputMessage(text));
|
|
6895
6599
|
}
|
|
6896
6600
|
} catch (error) {
|
|
6897
|
-
logException(
|
|
6898
|
-
|
|
6899
|
-
"
|
|
6900
|
-
|
|
6901
|
-
|
|
6902
|
-
"app.slack.reply_stage": "thread_reply_auth_pause_notice",
|
|
6903
|
-
...messageTs ? { "messaging.message.id": messageTs } : {},
|
|
6904
|
-
...getSlackErrorObservabilityAttributes(error)
|
|
6905
|
-
},
|
|
6906
|
-
"Failed to post auth pause notice"
|
|
6907
|
-
);
|
|
6601
|
+
logException(error, "slack.auth_pause.notice_post.failed", {
|
|
6602
|
+
"app.slack.reply_stage": "thread_reply_auth_pause_notice",
|
|
6603
|
+
...messageTs ? { "messaging.message.id": messageTs } : {},
|
|
6604
|
+
...getSlackErrorObservabilityAttributes(error)
|
|
6605
|
+
});
|
|
6908
6606
|
}
|
|
6909
6607
|
};
|
|
6910
6608
|
let activeTurnId = preparedState.conversation.processing.activeTurnId;
|
|
@@ -7027,17 +6725,11 @@ function createReplyToThread(deps) {
|
|
|
7027
6725
|
try {
|
|
7028
6726
|
await deps.services.scheduleAgentContinue(resumeRequest);
|
|
7029
6727
|
} catch (error) {
|
|
7030
|
-
logException(
|
|
7031
|
-
|
|
7032
|
-
"
|
|
7033
|
-
|
|
7034
|
-
|
|
7035
|
-
"app.ai.resume_session_version": resumeRequest.expectedVersion,
|
|
7036
|
-
"app.ai.resume_session_id": resumeRequest.sessionId,
|
|
7037
|
-
...messageTs ? { "messaging.message.id": messageTs } : {}
|
|
7038
|
-
},
|
|
7039
|
-
"Failed to reschedule active agent continuation"
|
|
7040
|
-
);
|
|
6728
|
+
logException(error, "agent.continue.schedule.failed", {
|
|
6729
|
+
"app.ai.resume_session_version": resumeRequest.expectedVersion,
|
|
6730
|
+
"app.ai.resume_session_id": resumeRequest.sessionId,
|
|
6731
|
+
...messageTs ? { "messaging.message.id": messageTs } : {}
|
|
6732
|
+
});
|
|
7041
6733
|
throw error;
|
|
7042
6734
|
}
|
|
7043
6735
|
await persistThreadState(thread, {
|
|
@@ -7157,28 +6849,19 @@ function createReplyToThread(deps) {
|
|
|
7157
6849
|
source,
|
|
7158
6850
|
traceId: getActiveTraceId()
|
|
7159
6851
|
}).catch((error) => {
|
|
7160
|
-
logException(
|
|
7161
|
-
|
|
7162
|
-
|
|
7163
|
-
turnTraceContext,
|
|
7164
|
-
{ "app.agent.turn.state": "running" },
|
|
7165
|
-
"Failed to record running turn summary"
|
|
7166
|
-
);
|
|
6852
|
+
logException(error, "agent.turn.summary_record.failed", {
|
|
6853
|
+
"app.agent.turn.state": "running"
|
|
6854
|
+
});
|
|
7167
6855
|
});
|
|
7168
6856
|
}
|
|
7169
6857
|
setTags({
|
|
7170
6858
|
conversationId
|
|
7171
6859
|
});
|
|
7172
6860
|
if (shouldEmitDevAgentTrace()) {
|
|
7173
|
-
logInfo(
|
|
7174
|
-
"
|
|
7175
|
-
|
|
7176
|
-
|
|
7177
|
-
"app.message.id": message.id,
|
|
7178
|
-
...messageTs ? { "messaging.message.id": messageTs } : {}
|
|
7179
|
-
},
|
|
7180
|
-
"Agent turn started"
|
|
7181
|
-
);
|
|
6861
|
+
logInfo("agent.turn.started", {
|
|
6862
|
+
"app.message.id": message.id,
|
|
6863
|
+
...messageTs ? { "messaging.message.id": messageTs } : {}
|
|
6864
|
+
});
|
|
7182
6865
|
}
|
|
7183
6866
|
await persistThreadState(thread, {
|
|
7184
6867
|
conversation: preparedState.conversation
|
|
@@ -7259,8 +6942,10 @@ function createReplyToThread(deps) {
|
|
|
7259
6942
|
await options.onTurnCompleted?.();
|
|
7260
6943
|
turnCompletionNotified = true;
|
|
7261
6944
|
};
|
|
7262
|
-
const deliverAssistantMessage = async (
|
|
7263
|
-
|
|
6945
|
+
const deliverAssistantMessage = async (reply, terminalDispatchOutcome) => {
|
|
6946
|
+
const agentMessage = typeof reply === "string" ? void 0 : reply;
|
|
6947
|
+
const text = typeof reply === "string" ? reply : getAssistantReplyText(reply);
|
|
6948
|
+
if (!text?.trim()) {
|
|
7264
6949
|
return;
|
|
7265
6950
|
}
|
|
7266
6951
|
boundaryFailureCode = "delivery_failed";
|
|
@@ -7271,29 +6956,23 @@ function createReplyToThread(deps) {
|
|
|
7271
6956
|
slackTs = await sendSlackReply({
|
|
7272
6957
|
channelId,
|
|
7273
6958
|
conversationId,
|
|
7274
|
-
text
|
|
6959
|
+
text,
|
|
7275
6960
|
...threadTs ? { threadTs } : {}
|
|
7276
6961
|
});
|
|
7277
6962
|
} else {
|
|
7278
|
-
for (const
|
|
7279
|
-
slackTs = (await thread.post(buildSlackOutputMessage(
|
|
6963
|
+
for (const part of splitSlackReplyText(text)) {
|
|
6964
|
+
slackTs = (await thread.post(buildSlackOutputMessage(part))).id;
|
|
7280
6965
|
}
|
|
7281
6966
|
}
|
|
7282
6967
|
} catch (error) {
|
|
7283
6968
|
if (isRetryableSlackPostError(error)) {
|
|
7284
6969
|
throw new RetryableDeliveryError(error);
|
|
7285
6970
|
}
|
|
7286
|
-
const eventId = logException(
|
|
7287
|
-
|
|
7288
|
-
"
|
|
7289
|
-
|
|
7290
|
-
|
|
7291
|
-
"app.slack.reply_stage": "thread_reply",
|
|
7292
|
-
...messageTs ? { "messaging.message.id": messageTs } : {},
|
|
7293
|
-
...getSlackErrorObservabilityAttributes(error)
|
|
7294
|
-
},
|
|
7295
|
-
"Failed to post Slack thread reply"
|
|
7296
|
-
);
|
|
6971
|
+
const eventId = logException(error, "slack.thread.post.failed", {
|
|
6972
|
+
"app.slack.reply_stage": "thread_reply",
|
|
6973
|
+
...messageTs ? { "messaging.message.id": messageTs } : {},
|
|
6974
|
+
...getSlackErrorObservabilityAttributes(error)
|
|
6975
|
+
});
|
|
7297
6976
|
throw new ConversationTurnBoundaryError({
|
|
7298
6977
|
cause: error,
|
|
7299
6978
|
...eventId ? { eventId } : {},
|
|
@@ -7306,7 +6985,7 @@ function createReplyToThread(deps) {
|
|
|
7306
6985
|
const recordedMessageId = recordDeliveredAssistantMessage({
|
|
7307
6986
|
conversation: preparedState.conversation,
|
|
7308
6987
|
sessionId: turnId,
|
|
7309
|
-
text
|
|
6988
|
+
text,
|
|
7310
6989
|
userMessageId: preparedState.userMessageId
|
|
7311
6990
|
});
|
|
7312
6991
|
if (slackTs) {
|
|
@@ -7318,7 +6997,12 @@ function createReplyToThread(deps) {
|
|
|
7318
6997
|
}
|
|
7319
6998
|
try {
|
|
7320
6999
|
await persistWithRetry(
|
|
7321
|
-
() =>
|
|
7000
|
+
() => agentMessage && conversationId ? commitAcceptedReply({
|
|
7001
|
+
agentMessage,
|
|
7002
|
+
conversation: preparedState.conversation,
|
|
7003
|
+
conversationMessageId: recordedMessageId,
|
|
7004
|
+
conversationId
|
|
7005
|
+
}) : persistConversationMessages({
|
|
7322
7006
|
conversation: preparedState.conversation,
|
|
7323
7007
|
conversationId
|
|
7324
7008
|
})
|
|
@@ -7326,12 +7010,10 @@ function createReplyToThread(deps) {
|
|
|
7326
7010
|
} catch (error) {
|
|
7327
7011
|
logException(
|
|
7328
7012
|
new Error("Accepted assistant message persistence failed"),
|
|
7329
|
-
"
|
|
7330
|
-
turnTraceContext,
|
|
7013
|
+
"slack.assistant.message_post_delivery_persist.failed",
|
|
7331
7014
|
{
|
|
7332
7015
|
"error.type": error instanceof Error ? error.name : typeof error
|
|
7333
|
-
}
|
|
7334
|
-
"Failed to persist an accepted Slack assistant message"
|
|
7016
|
+
}
|
|
7335
7017
|
);
|
|
7336
7018
|
}
|
|
7337
7019
|
if (slackTs && options.execution?.dispatch?.id) {
|
|
@@ -7354,13 +7036,7 @@ function createReplyToThread(deps) {
|
|
|
7354
7036
|
})
|
|
7355
7037
|
);
|
|
7356
7038
|
} catch (error) {
|
|
7357
|
-
logException(
|
|
7358
|
-
error,
|
|
7359
|
-
"agent_turn_delivery_receipt_persist_failed",
|
|
7360
|
-
turnTraceContext,
|
|
7361
|
-
{},
|
|
7362
|
-
"Failed to persist accepted turn delivery receipt"
|
|
7363
|
-
);
|
|
7039
|
+
logException(error, "agent.turn.delivery_receipt_persist.failed");
|
|
7364
7040
|
}
|
|
7365
7041
|
}
|
|
7366
7042
|
boundaryFailureCode = "agent_run_failed";
|
|
@@ -7454,13 +7130,7 @@ function createReplyToThread(deps) {
|
|
|
7454
7130
|
title: titleUpdateResult.title
|
|
7455
7131
|
});
|
|
7456
7132
|
} catch (error) {
|
|
7457
|
-
logException(
|
|
7458
|
-
error,
|
|
7459
|
-
"conversation_title_persist_failed",
|
|
7460
|
-
turnTraceContext,
|
|
7461
|
-
{},
|
|
7462
|
-
"Failed to persist generated conversation title"
|
|
7463
|
-
);
|
|
7133
|
+
logException(error, "conversation.title.persist.failed");
|
|
7464
7134
|
}
|
|
7465
7135
|
}
|
|
7466
7136
|
try {
|
|
@@ -7468,22 +7138,10 @@ function createReplyToThread(deps) {
|
|
|
7468
7138
|
artifacts: latestArtifacts
|
|
7469
7139
|
});
|
|
7470
7140
|
} catch (error) {
|
|
7471
|
-
logException(
|
|
7472
|
-
error,
|
|
7473
|
-
"assistant_title_artifact_persist_failed",
|
|
7474
|
-
turnTraceContext,
|
|
7475
|
-
{},
|
|
7476
|
-
"Failed to persist async assistant title artifact state"
|
|
7477
|
-
);
|
|
7141
|
+
logException(error, "assistant.title.artifact_persist.failed");
|
|
7478
7142
|
}
|
|
7479
7143
|
}).catch((error) => {
|
|
7480
|
-
logException(
|
|
7481
|
-
error,
|
|
7482
|
-
"assistant_title_task_failed",
|
|
7483
|
-
turnTraceContext,
|
|
7484
|
-
{},
|
|
7485
|
-
"Async assistant title task failed"
|
|
7486
|
-
);
|
|
7144
|
+
logException(error, "assistant.title.task.failed");
|
|
7487
7145
|
});
|
|
7488
7146
|
const toolChannelId = preparedState.artifacts.assistantContextChannelId ?? channelId;
|
|
7489
7147
|
const activeInstructionAuthorId = actor?.userId ?? parseActorUserId(message.author.userId);
|
|
@@ -7548,9 +7206,7 @@ function createReplyToThread(deps) {
|
|
|
7548
7206
|
onStatus: (nextStatus) => status.update(nextStatus),
|
|
7549
7207
|
onToolInvocation: options.onToolInvocation
|
|
7550
7208
|
},
|
|
7551
|
-
delivery:
|
|
7552
|
-
onAssistantMessage: deliverAssistantMessage
|
|
7553
|
-
},
|
|
7209
|
+
delivery: deliverAssistantMessage,
|
|
7554
7210
|
durability: {
|
|
7555
7211
|
onInputCommitted: options.ack,
|
|
7556
7212
|
drainSteeringMessages,
|
|
@@ -7585,13 +7241,11 @@ function createReplyToThread(deps) {
|
|
|
7585
7241
|
new Error(
|
|
7586
7242
|
`Subscribed Slack turn requires ${outcome.providerDisplayName} authorization`
|
|
7587
7243
|
),
|
|
7588
|
-
"
|
|
7589
|
-
|
|
7590
|
-
{ "app.ai.failure_code": "agent_run_failed" },
|
|
7591
|
-
"Subscribed Slack turn could not continue without user authorization"
|
|
7244
|
+
"subscribed_message.authorization.required",
|
|
7245
|
+
{ "app.ai.failure_code": "agent_run_failed" }
|
|
7592
7246
|
);
|
|
7593
7247
|
const text = `I could not act on this subscribed event because ${outcome.providerDisplayName} needs user authorization. Ask me in this thread to connect ${outcome.providerDisplayName} before retrying.`;
|
|
7594
|
-
await deliverAssistantMessage(
|
|
7248
|
+
await deliverAssistantMessage(text);
|
|
7595
7249
|
markTurnClosed({
|
|
7596
7250
|
conversation: preparedState.conversation,
|
|
7597
7251
|
nowMs: Date.now(),
|
|
@@ -7650,16 +7304,10 @@ function createReplyToThread(deps) {
|
|
|
7650
7304
|
});
|
|
7651
7305
|
shouldPersistFailureState = false;
|
|
7652
7306
|
} catch (scheduleError) {
|
|
7653
|
-
logException(
|
|
7654
|
-
|
|
7655
|
-
"
|
|
7656
|
-
|
|
7657
|
-
{
|
|
7658
|
-
...messageTs ? { "messaging.message.id": messageTs } : {},
|
|
7659
|
-
"app.ai.resume_session_version": outcome.resumeVersion
|
|
7660
|
-
},
|
|
7661
|
-
"Failed to schedule agent continuation"
|
|
7662
|
-
);
|
|
7307
|
+
logException(scheduleError, "agent.continue.schedule.failed", {
|
|
7308
|
+
...messageTs ? { "messaging.message.id": messageTs } : {},
|
|
7309
|
+
"app.ai.resume_session_version": outcome.resumeVersion
|
|
7310
|
+
});
|
|
7663
7311
|
shouldPersistFailureState = true;
|
|
7664
7312
|
agentContinueScheduleError = scheduleError;
|
|
7665
7313
|
throw scheduleError;
|
|
@@ -7667,25 +7315,17 @@ function createReplyToThread(deps) {
|
|
|
7667
7315
|
return;
|
|
7668
7316
|
}
|
|
7669
7317
|
let reply = outcome.result;
|
|
7670
|
-
|
|
7671
|
-
messageConversationId: threadId,
|
|
7672
|
-
userId: message.author.userId,
|
|
7673
|
-
destinationName: channelId,
|
|
7674
|
-
runId,
|
|
7675
|
-
assistantUserName: botConfig.userName,
|
|
7676
|
-
modelId: reply.diagnostics.modelId
|
|
7677
|
-
};
|
|
7318
|
+
setTags({ modelId: reply.diagnostics.modelId });
|
|
7678
7319
|
const diagnosticsAttributes = getAgentTurnDiagnosticsAttributes(reply);
|
|
7679
7320
|
setSpanAttributes(diagnosticsAttributes);
|
|
7680
7321
|
if (reply.diagnostics.outcome !== "success") {
|
|
7681
7322
|
const finalized = finalizeFailedTurnReplyWithEvent({
|
|
7682
7323
|
reply,
|
|
7683
|
-
logException
|
|
7684
|
-
context: diagnosticsContext
|
|
7324
|
+
logException
|
|
7685
7325
|
});
|
|
7686
7326
|
reply = finalized.reply;
|
|
7687
7327
|
finalizedFailureEventId = finalized.eventId;
|
|
7688
|
-
await deliverAssistantMessage(
|
|
7328
|
+
await deliverAssistantMessage(reply.text);
|
|
7689
7329
|
}
|
|
7690
7330
|
const turnResult = reply.diagnostics.outcome === "success" ? { outcome: "completed" } : {
|
|
7691
7331
|
errorMessage: reply.diagnostics.errorMessage ?? `Agent turn ended with ${reply.diagnostics.outcome}.`,
|
|
@@ -7767,10 +7407,8 @@ function createReplyToThread(deps) {
|
|
|
7767
7407
|
} catch (commitError) {
|
|
7768
7408
|
const persistenceEventId = logException(
|
|
7769
7409
|
commitError,
|
|
7770
|
-
"
|
|
7771
|
-
|
|
7772
|
-
messageTs ? { "messaging.message.id": messageTs } : {},
|
|
7773
|
-
"Post-delivery turn state persistence failed after Slack accepted the reply"
|
|
7410
|
+
"slack.reply.post_delivery_commit.failed",
|
|
7411
|
+
messageTs ? { "messaging.message.id": messageTs } : {}
|
|
7774
7412
|
);
|
|
7775
7413
|
if (conversationId && !lifecycleTerminalized) {
|
|
7776
7414
|
await deps.services.turnLifecycle.fail({
|
|
@@ -7806,16 +7444,11 @@ function createReplyToThread(deps) {
|
|
|
7806
7444
|
lifecycleTerminalized = true;
|
|
7807
7445
|
}
|
|
7808
7446
|
if (shouldEmitDevAgentTrace()) {
|
|
7809
|
-
logInfo(
|
|
7810
|
-
"
|
|
7811
|
-
|
|
7812
|
-
|
|
7813
|
-
|
|
7814
|
-
"app.ai.tool_call_count": reply.diagnostics.toolCalls.length,
|
|
7815
|
-
"app.ai.tool_error_results": reply.diagnostics.toolErrorCount
|
|
7816
|
-
},
|
|
7817
|
-
"Agent turn completed"
|
|
7818
|
-
);
|
|
7447
|
+
logInfo("agent.turn.completed", {
|
|
7448
|
+
"app.ai.outcome": reply.diagnostics.outcome,
|
|
7449
|
+
"app.ai.tool_call_count": reply.diagnostics.toolCalls.length,
|
|
7450
|
+
"app.ai.tool_error_results": reply.diagnostics.toolErrorCount
|
|
7451
|
+
});
|
|
7819
7452
|
}
|
|
7820
7453
|
await notifyTurnCompleted();
|
|
7821
7454
|
if (reply.diagnostics.outcome === "success" && conversationId) {
|
|
@@ -7827,10 +7460,7 @@ function createReplyToThread(deps) {
|
|
|
7827
7460
|
} catch (error) {
|
|
7828
7461
|
logException(
|
|
7829
7462
|
error,
|
|
7830
|
-
"
|
|
7831
|
-
turnTraceContext,
|
|
7832
|
-
{},
|
|
7833
|
-
"Plugin session.completed task scheduling failed"
|
|
7463
|
+
"plugin.session.completed_task_schedule.failed"
|
|
7834
7464
|
);
|
|
7835
7465
|
}
|
|
7836
7466
|
}
|
|
@@ -7839,20 +7469,16 @@ function createReplyToThread(deps) {
|
|
|
7839
7469
|
shouldPersistFailureState = false;
|
|
7840
7470
|
logException(
|
|
7841
7471
|
error,
|
|
7842
|
-
"
|
|
7843
|
-
|
|
7844
|
-
messageTs ? { "messaging.message.id": messageTs } : {},
|
|
7845
|
-
"Post-delivery turn work failed after Slack accepted the reply"
|
|
7472
|
+
"slack.reply.post_delivery_commit.failed",
|
|
7473
|
+
messageTs ? { "messaging.message.id": messageTs } : {}
|
|
7846
7474
|
);
|
|
7847
7475
|
try {
|
|
7848
7476
|
await notifyTurnCompleted();
|
|
7849
7477
|
} catch (completionError) {
|
|
7850
7478
|
logException(
|
|
7851
7479
|
completionError,
|
|
7852
|
-
"
|
|
7853
|
-
|
|
7854
|
-
messageTs ? { "messaging.message.id": messageTs } : {},
|
|
7855
|
-
"Post-delivery turn completion callback failed after Slack accepted the reply"
|
|
7480
|
+
"slack.reply.post_delivery_completion_callback.failed",
|
|
7481
|
+
messageTs ? { "messaging.message.id": messageTs } : {}
|
|
7856
7482
|
);
|
|
7857
7483
|
}
|
|
7858
7484
|
return;
|
|
@@ -7875,13 +7501,9 @@ function createReplyToThread(deps) {
|
|
|
7875
7501
|
terminalDispatchFailureOutcome = "blocked";
|
|
7876
7502
|
}
|
|
7877
7503
|
const failureCode = classifiedFailure?.failureCode ?? boundaryFailureCode;
|
|
7878
|
-
const failureEventId = classifiedFailure?.eventId ?? logException(
|
|
7879
|
-
|
|
7880
|
-
|
|
7881
|
-
turnTraceContext,
|
|
7882
|
-
{ "app.ai.failure_code": failureCode },
|
|
7883
|
-
"Slack turn failed at its reply execution boundary"
|
|
7884
|
-
);
|
|
7504
|
+
const failureEventId = classifiedFailure?.eventId ?? logException(failureCause, "slack.turn.execution.failed", {
|
|
7505
|
+
"app.ai.failure_code": failureCode
|
|
7506
|
+
});
|
|
7885
7507
|
const createdCanvasUrl = getCurrentTurnCanvasUrl({
|
|
7886
7508
|
before: preparedState.artifacts,
|
|
7887
7509
|
after: latestArtifacts
|
|
@@ -7890,10 +7512,7 @@ function createReplyToThread(deps) {
|
|
|
7890
7512
|
const dispatchOutcome = terminalDispatchFailureOutcome ?? "failed";
|
|
7891
7513
|
const errorMessage = failureCause instanceof Error ? failureCause.message : "Agent turn failed after creating a canvas";
|
|
7892
7514
|
const recoveryText = buildCanvasRecoveryReply(createdCanvasUrl);
|
|
7893
|
-
await deliverAssistantMessage(
|
|
7894
|
-
{ text: recoveryText },
|
|
7895
|
-
dispatchOutcome
|
|
7896
|
-
);
|
|
7515
|
+
await deliverAssistantMessage(recoveryText, dispatchOutcome);
|
|
7897
7516
|
if (conversationId) {
|
|
7898
7517
|
const sessionRecord = await getAgentTurnSessionRecord(
|
|
7899
7518
|
conversationId,
|
|
@@ -7988,10 +7607,7 @@ function createReplyToThread(deps) {
|
|
|
7988
7607
|
} catch (recordError) {
|
|
7989
7608
|
logException(
|
|
7990
7609
|
recordError,
|
|
7991
|
-
"
|
|
7992
|
-
turnTraceContext,
|
|
7993
|
-
{},
|
|
7994
|
-
"Failed to mark failed turn session record"
|
|
7610
|
+
"agent.turn.failed_session_record_persist.failed"
|
|
7995
7611
|
);
|
|
7996
7612
|
}
|
|
7997
7613
|
}
|
|
@@ -7999,12 +7615,7 @@ function createReplyToThread(deps) {
|
|
|
7999
7615
|
conversation: preparedState.conversation
|
|
8000
7616
|
});
|
|
8001
7617
|
if (shouldEmitDevAgentTrace()) {
|
|
8002
|
-
logWarn(
|
|
8003
|
-
"agent_turn_failed",
|
|
8004
|
-
turnTraceContext,
|
|
8005
|
-
{},
|
|
8006
|
-
"Agent turn failed"
|
|
8007
|
-
);
|
|
7618
|
+
logWarn("agent.turn.failed");
|
|
8008
7619
|
}
|
|
8009
7620
|
}
|
|
8010
7621
|
await status.clear();
|
|
@@ -8553,13 +8164,11 @@ async function failSessionRecordBestEffort(args) {
|
|
|
8553
8164
|
} catch (error) {
|
|
8554
8165
|
logException(
|
|
8555
8166
|
error,
|
|
8556
|
-
"
|
|
8557
|
-
{},
|
|
8167
|
+
"agent.continue.session_record.failure_persistence.failed",
|
|
8558
8168
|
{
|
|
8559
8169
|
"app.ai.conversation_id": args.sessionRecord.conversationId,
|
|
8560
8170
|
"app.ai.session_id": args.sessionRecord.sessionId
|
|
8561
|
-
}
|
|
8562
|
-
"Failed to mark paused agent run session record failed"
|
|
8171
|
+
}
|
|
8563
8172
|
);
|
|
8564
8173
|
}
|
|
8565
8174
|
}
|
|
@@ -8599,13 +8208,11 @@ async function failContinuationStartup(args) {
|
|
|
8599
8208
|
});
|
|
8600
8209
|
logException(
|
|
8601
8210
|
persistError,
|
|
8602
|
-
"
|
|
8603
|
-
{},
|
|
8211
|
+
"agent.continue.startup_failure_persist.failed",
|
|
8604
8212
|
{
|
|
8605
8213
|
"app.ai.conversation_id": args.sessionRecord.conversationId,
|
|
8606
8214
|
"app.ai.session_id": args.sessionRecord.sessionId
|
|
8607
|
-
}
|
|
8608
|
-
"Failed to persist paused agent run startup failure"
|
|
8215
|
+
}
|
|
8609
8216
|
);
|
|
8610
8217
|
}
|
|
8611
8218
|
}
|
|
@@ -8657,6 +8264,12 @@ async function failUnresumableContinuation(args) {
|
|
|
8657
8264
|
}
|
|
8658
8265
|
}
|
|
8659
8266
|
async function continueSlackAgentRun(payload, options, runOptions = {}) {
|
|
8267
|
+
return withLogContext(
|
|
8268
|
+
{ conversationId: payload.conversationId },
|
|
8269
|
+
() => continueSlackAgentRunInContext(payload, options, runOptions)
|
|
8270
|
+
);
|
|
8271
|
+
}
|
|
8272
|
+
async function continueSlackAgentRunInContext(payload, options, runOptions) {
|
|
8660
8273
|
const thread = parseSlackThreadId(payload.conversationId);
|
|
8661
8274
|
const destination = requireSlackDestination(
|
|
8662
8275
|
payload.destination,
|
|
@@ -8845,15 +8458,10 @@ async function continueSlackAgentRun(payload, options, runOptions = {}) {
|
|
|
8845
8458
|
threadStateId: payload.conversationId
|
|
8846
8459
|
});
|
|
8847
8460
|
await recordDispatchOutcome("blocked");
|
|
8848
|
-
logWarn(
|
|
8849
|
-
"
|
|
8850
|
-
|
|
8851
|
-
|
|
8852
|
-
"app.ai.conversation_id": payload.conversationId,
|
|
8853
|
-
"app.ai.session_id": payload.sessionId
|
|
8854
|
-
},
|
|
8855
|
-
"Continued agent run parked for auth"
|
|
8856
|
-
);
|
|
8461
|
+
logWarn("agent.continue.reparked_for_auth", {
|
|
8462
|
+
"app.ai.conversation_id": payload.conversationId,
|
|
8463
|
+
"app.ai.session_id": payload.sessionId
|
|
8464
|
+
});
|
|
8857
8465
|
},
|
|
8858
8466
|
onSuspend: async (resumeVersion) => {
|
|
8859
8467
|
await scheduleAgentContinue2({
|
|
@@ -8914,17 +8522,11 @@ async function failStrandedSessionWithFallback(args) {
|
|
|
8914
8522
|
updateConversationStats
|
|
8915
8523
|
});
|
|
8916
8524
|
await persistThreadStateById(args.conversationId, { conversation });
|
|
8917
|
-
const eventName = "
|
|
8918
|
-
const eventId = logException(
|
|
8919
|
-
|
|
8920
|
-
|
|
8921
|
-
|
|
8922
|
-
{
|
|
8923
|
-
"app.ai.conversation_id": args.conversationId,
|
|
8924
|
-
"app.ai.session_id": args.sessionRecord.sessionId
|
|
8925
|
-
},
|
|
8926
|
-
"Stranded running agent session terminally failed"
|
|
8927
|
-
);
|
|
8525
|
+
const eventName = "agent.turn.stranded_session.failed";
|
|
8526
|
+
const eventId = logException(new Error(args.errorMessage), eventName, {
|
|
8527
|
+
"app.ai.conversation_id": args.conversationId,
|
|
8528
|
+
"app.ai.session_id": args.sessionRecord.sessionId
|
|
8529
|
+
});
|
|
8928
8530
|
const thread = parseSlackThreadId(args.conversationId);
|
|
8929
8531
|
const channelId = thread?.channelId ?? requireSlackDestination(
|
|
8930
8532
|
args.sessionRecord.destination,
|
|
@@ -8967,7 +8569,6 @@ async function recoverStrandedRunningSession(args) {
|
|
|
8967
8569
|
source: sessionRecord.source,
|
|
8968
8570
|
messages: sessionRecord.piMessages,
|
|
8969
8571
|
errorMessage: "Recovered running session after hard worker death",
|
|
8970
|
-
logContext: {},
|
|
8971
8572
|
modelId,
|
|
8972
8573
|
actor: sessionRecord.actor,
|
|
8973
8574
|
surface: sessionRecord.surface
|
|
@@ -9008,6 +8609,16 @@ async function recoverStrandedRunningSession(args) {
|
|
|
9008
8609
|
return false;
|
|
9009
8610
|
}
|
|
9010
8611
|
async function resumeAwaitingSlackContinuation(conversationId, options, runOptions = {}) {
|
|
8612
|
+
return withLogContext(
|
|
8613
|
+
{ conversationId },
|
|
8614
|
+
() => resumeAwaitingSlackContinuationInContext(
|
|
8615
|
+
conversationId,
|
|
8616
|
+
options,
|
|
8617
|
+
runOptions
|
|
8618
|
+
)
|
|
8619
|
+
);
|
|
8620
|
+
}
|
|
8621
|
+
async function resumeAwaitingSlackContinuationInContext(conversationId, options, runOptions) {
|
|
9011
8622
|
const summaries = await listAgentTurnSessionSummariesForConversation(conversationId);
|
|
9012
8623
|
const newest = summaries[0];
|
|
9013
8624
|
if (newest?.state === "running") {
|
|
@@ -9047,6 +8658,12 @@ async function resumeAwaitingSlackContinuation(conversationId, options, runOptio
|
|
|
9047
8658
|
return false;
|
|
9048
8659
|
}
|
|
9049
8660
|
async function continueSlackAgentRunWithLockRetry(payload, options, runOptions = {}) {
|
|
8661
|
+
return withLogContext(
|
|
8662
|
+
{ conversationId: payload.conversationId },
|
|
8663
|
+
() => continueSlackAgentRunWithLockRetryInContext(payload, options, runOptions)
|
|
8664
|
+
);
|
|
8665
|
+
}
|
|
8666
|
+
async function continueSlackAgentRunWithLockRetryInContext(payload, options, runOptions) {
|
|
9050
8667
|
const scheduleAgentContinue2 = options.scheduleAgentContinue ?? scheduleAgentContinue;
|
|
9051
8668
|
for (const [attempt, delayMs] of [
|
|
9052
8669
|
...AGENT_CONTINUE_LOCK_RETRY_DELAYS_MS,
|
|
@@ -9059,30 +8676,20 @@ async function continueSlackAgentRunWithLockRetry(payload, options, runOptions =
|
|
|
9059
8676
|
throw error;
|
|
9060
8677
|
}
|
|
9061
8678
|
if (typeof delayMs !== "number") {
|
|
9062
|
-
logWarn(
|
|
9063
|
-
"
|
|
9064
|
-
|
|
9065
|
-
|
|
9066
|
-
|
|
9067
|
-
"app.ai.session_id": payload.sessionId,
|
|
9068
|
-
"app.ai.resume_lock_retry_count": attempt
|
|
9069
|
-
},
|
|
9070
|
-
"Rescheduling agent continuation because another run still owns the thread lock"
|
|
9071
|
-
);
|
|
8679
|
+
logWarn("agent.continue.lock.busy", {
|
|
8680
|
+
"app.ai.conversation_id": payload.conversationId,
|
|
8681
|
+
"app.ai.session_id": payload.sessionId,
|
|
8682
|
+
"app.ai.resume_lock_retry_count": attempt
|
|
8683
|
+
});
|
|
9072
8684
|
await scheduleAgentContinue2(payload);
|
|
9073
8685
|
return true;
|
|
9074
8686
|
}
|
|
9075
|
-
logWarn(
|
|
9076
|
-
"
|
|
9077
|
-
|
|
9078
|
-
|
|
9079
|
-
|
|
9080
|
-
|
|
9081
|
-
"app.ai.resume_lock_retry_attempt": attempt + 1,
|
|
9082
|
-
"app.ai.resume_lock_retry_delay_ms": delayMs
|
|
9083
|
-
},
|
|
9084
|
-
"Agent continuation lock was busy; retrying"
|
|
9085
|
-
);
|
|
8687
|
+
logWarn("agent.continue.lock.retrying", {
|
|
8688
|
+
"app.ai.conversation_id": payload.conversationId,
|
|
8689
|
+
"app.ai.session_id": payload.sessionId,
|
|
8690
|
+
"app.ai.resume_lock_retry_attempt": attempt + 1,
|
|
8691
|
+
"app.ai.resume_lock_retry_delay_ms": delayMs
|
|
8692
|
+
});
|
|
9086
8693
|
await sleep(delayMs);
|
|
9087
8694
|
}
|
|
9088
8695
|
}
|
|
@@ -9647,7 +9254,7 @@ async function createApp(options) {
|
|
|
9647
9254
|
});
|
|
9648
9255
|
const app = new Hono();
|
|
9649
9256
|
app.onError((err, c) => {
|
|
9650
|
-
logException(err, "
|
|
9257
|
+
logException(err, "route.unhandled.exception");
|
|
9651
9258
|
return c.text("Internal Server Error", 500);
|
|
9652
9259
|
});
|
|
9653
9260
|
app.use("*", async (c, next) => {
|