@sentry/junior 0.120.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.
Files changed (69) hide show
  1. package/dist/{agent-hooks-5DDPWRYO.js → agent-hooks-HJMLQLCB.js} +8 -8
  2. package/dist/api.js +14 -14
  3. package/dist/app.js +604 -997
  4. package/dist/{catalog-runtime-5KR5XBMJ.js → catalog-runtime-CYPZUYJD.js} +5 -5
  5. package/dist/chat/agent/index.d.ts +19 -2
  6. package/dist/chat/agent/request.d.ts +8 -8
  7. package/dist/chat/agent/resume.d.ts +1 -3
  8. package/dist/chat/agent/retry-usage.d.ts +8 -0
  9. package/dist/chat/agent/skills.d.ts +0 -9
  10. package/dist/chat/conversations/messages.d.ts +7 -0
  11. package/dist/chat/conversations/projection.d.ts +16 -0
  12. package/dist/chat/logging.d.ts +46 -13
  13. package/dist/chat/runtime/processing-reaction.d.ts +2 -4
  14. package/dist/chat/runtime/slack-runtime.d.ts +2 -2
  15. package/dist/chat/sandbox/noninteractive-command.d.ts +1 -0
  16. package/dist/chat/sandbox/session.d.ts +2 -2
  17. package/dist/chat/sandbox/snapshot/install.d.ts +2 -2
  18. package/dist/chat/sandbox/snapshot/resolve.d.ts +1 -0
  19. package/dist/chat/sandbox/workspace.d.ts +3 -1
  20. package/dist/chat/services/assistant-reply.d.ts +16 -0
  21. package/dist/chat/services/empty-output-continuation.d.ts +20 -0
  22. package/dist/chat/services/turn-failure-response.d.ts +1 -3
  23. package/dist/chat/services/turn-result.d.ts +0 -5
  24. package/dist/chat/services/turn-session-record.d.ts +0 -11
  25. package/dist/chat/tool-support/zod-tool.d.ts +11 -6
  26. package/dist/chat/tools/execute-tool.d.ts +1 -33
  27. package/dist/chat/tools/execution/tool-error-handler.d.ts +2 -2
  28. package/dist/chat/tools/query-conversation-events.d.ts +3 -1
  29. package/dist/chat/tools/sandbox/bash.d.ts +1 -33
  30. package/dist/chat/tools/sandbox/edit-file.d.ts +1 -33
  31. package/dist/chat/tools/sandbox/find-files.d.ts +1 -33
  32. package/dist/chat/tools/sandbox/grep.d.ts +1 -33
  33. package/dist/chat/tools/sandbox/list-dir.d.ts +1 -33
  34. package/dist/chat/tools/sandbox/read-file.d.ts +1 -33
  35. package/dist/chat/tools/sandbox/write-file.d.ts +1 -33
  36. package/dist/{chunk-VURFJVFH.js → chunk-2RFM7STT.js} +11 -22
  37. package/dist/{chunk-6WX5SPI3.js → chunk-32FPGM2V.js} +1 -1
  38. package/dist/{chunk-ALYQOZG7.js → chunk-4SPRQJOW.js} +37 -45
  39. package/dist/{chunk-QOQPPRFS.js → chunk-4TGVC42X.js} +133 -206
  40. package/dist/{chunk-Q3RL6A4E.js → chunk-BDE5RZVF.js} +2 -2
  41. package/dist/{chunk-DJDGDJDF.js → chunk-CYW4M6IV.js} +44 -23
  42. package/dist/{chunk-M243JMZH.js → chunk-DOB5RVMH.js} +2 -2
  43. package/dist/{chunk-EIVE6GL4.js → chunk-E5SRLZQV.js} +14 -29
  44. package/dist/{chunk-YEAOUT5W.js → chunk-GFTZKOTP.js} +1 -1
  45. package/dist/{chunk-XAI2HNAY.js → chunk-GN6WW7WR.js} +16 -31
  46. package/dist/{chunk-DMKHRBI6.js → chunk-ISQYBTPG.js} +3 -3
  47. package/dist/{chunk-DWOH57FW.js → chunk-L2KJ2GNL.js} +1 -1
  48. package/dist/{chunk-ERYVNWNE.js → chunk-NIJG6G6X.js} +621 -735
  49. package/dist/{chunk-JQWP6V22.js → chunk-NNGYYGX5.js} +2061 -2015
  50. package/dist/{chunk-GNNV3UTY.js → chunk-OKUZIXQM.js} +1 -1
  51. package/dist/{chunk-HDFGNU5E.js → chunk-S7G34U75.js} +3 -1
  52. package/dist/{chunk-ALAKN7LR.js → chunk-TZVZCIYL.js} +1 -1
  53. package/dist/{chunk-24B5R6OU.js → chunk-USKP7IM2.js} +49 -92
  54. package/dist/{chunk-BHXW734F.js → chunk-V6XPIKYA.js} +13 -19
  55. package/dist/{chunk-TKUCRFNX.js → chunk-VCC7XFX2.js} +22 -45
  56. package/dist/cli/chat.js +22 -22
  57. package/dist/cli/check.js +6 -6
  58. package/dist/cli/plugins.js +11 -11
  59. package/dist/cli/snapshot-create.js +9 -9
  60. package/dist/cli/upgrade.js +6 -6
  61. package/dist/{db-T4ZY64PB.js → db-E6O2DXUQ.js} +6 -6
  62. package/dist/instrumentation.js +3 -3
  63. package/dist/nitro.js +2 -2
  64. package/dist/{oauth-callback-server-LQ6DFI26.js → oauth-callback-server-6SCPUFFD.js} +15 -15
  65. package/dist/{runner-D3ZU6TJH.js → runner-JFKCMVVY.js} +62 -51
  66. package/dist/{skills-JMXY7LIY.js → skills-RD66F2OB.js} +6 -6
  67. package/dist/{validation-OEPJS7JX.js → validation-XVRNX6IU.js} +6 -6
  68. package/dist/version.js +1 -1
  69. 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-QOQPPRFS.js";
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-BHXW734F.js";
68
+ } from "./chunk-V6XPIKYA.js";
69
69
  import {
70
70
  GET,
71
71
  conversationVisibilityFromSlackChannelType,
72
72
  resolveSlackChannelTypeFromMessage,
73
73
  resolveSlackConversationContext
74
- } from "./chunk-Q3RL6A4E.js";
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-YEAOUT5W.js";
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-ERYVNWNE.js";
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-TKUCRFNX.js";
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-JQWP6V22.js";
193
+ } from "./chunk-NNGYYGX5.js";
192
194
  import "./chunk-HDSDCEPK.js";
193
- import "./chunk-DJDGDJDF.js";
194
- import "./chunk-6WX5SPI3.js";
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-24B5R6OU.js";
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-M243JMZH.js";
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-ALAKN7LR.js";
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-VURFJVFH.js";
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-HDFGNU5E.js";
264
- import "./chunk-EIVE6GL4.js";
265
+ } from "./chunk-S7G34U75.js";
266
+ import "./chunk-E5SRLZQV.js";
265
267
  import {
266
268
  pluginCatalogRuntime
267
- } from "./chunk-GNNV3UTY.js";
268
- import "./chunk-XAI2HNAY.js";
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-ALYQOZG7.js";
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-DWOH57FW.js";
312
+ } from "./chunk-L2KJ2GNL.js";
310
313
  import {
311
314
  JUNIOR_VERSION
312
- } from "./chunk-DMKHRBI6.js";
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
- error,
1114
- "agent_dispatch_callback_failed",
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
- let work;
1145
- try {
1146
- work = await getConversationWorkState({
1147
- conversationId,
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
- continue;
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
- if (work.execution.lease && work.execution.lease.expiresAtMs <= args.nowMs) {
1182
- const cleared = await clearExpiredConversationLease({
1183
- conversationId,
1184
- nowMs: args.nowMs,
1185
- state: args.state
1186
- });
1187
- if (!cleared) {
1188
- continue;
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 wake2 = await ensureConversationWake({
1205
+ const wake = await ensureConversationWake({
1191
1206
  conversationId,
1192
1207
  idempotencyKey: heartbeatIdempotencyKey(
1193
- "lease",
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 (wake2.status === "enqueued") {
1203
- result.expiredLeaseCount += 1;
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
- continue;
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
- } catch (error) {
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
- "plugin_heartbeat_dispatched",
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
- error,
1510
- "plugin_heartbeat_failed",
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
- error,
1545
- "dispatch_mailbox_append_recovery_failed",
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
- error,
1598
- "heartbeat_failed",
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
- try {
1824
- const result = await purgeConversationTree(executor, {
1825
- rootConversationId: root.conversationId,
1826
- nowMs: args.nowMs,
1827
- retention: {
1828
- publicWindowMs: CONTENT_RETENTION_MS.public,
1829
- privateWindowMs: CONTENT_RETENTION_MS.private
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
- if (result.purged) {
1833
- purged += 1;
1834
- conversations += result.conversations;
1799
+ } catch (error) {
1800
+ failed += 1;
1801
+ logException(error, "retention.purge.tree.failed");
1835
1802
  }
1836
- } catch (error) {
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
- "retention_purge_completed",
1849
- {},
1850
- {
1851
- "app.retention.scanned": roots.length,
1852
- "app.retention.purged": purged,
1853
- "app.retention.failed": failed,
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
- "sandbox_egress_oidc_verification_failed",
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
- "sandbox_egress_oidc_session_missing",
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
- "sandbox_egress_upstream_url_invalid",
2160
- {},
2161
- {
2162
- ...egressAttributes({
2163
- egressId: activeEgressId,
2164
- method: request.method,
2165
- path: redactedProxyPath(new URL(request.url).pathname),
2166
- status: 400
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
- "sandbox_egress_provider_unresolved",
2179
- {},
2180
- {
2181
- ...egressAttributes({
2182
- egressId: activeEgressId,
2183
- host: upstreamUrl.hostname,
2184
- method: request.method,
2185
- path: upstreamUrl.pathname,
2186
- status: 403
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
- "sandbox_egress_credential_context_unauthorized",
2200
- {},
2201
- {
2202
- ...egressAttributes({
2203
- egressId: activeEgressId,
2204
- host: upstreamUrl.hostname,
2205
- method: request.method,
2206
- path: upstreamUrl.pathname,
2207
- provider,
2208
- status: 403
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
- "slack_user_lookup_failed",
2531
- {},
2532
- {
2533
- "enduser.id": userId,
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
- "slack_user_lookup_failed",
2558
- {},
2559
- {
2560
- "enduser.id": userId,
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
- "slash_command_unlink",
3348
- { userId: actorId },
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
- try {
3563
- await publishAppHomeView(
3564
- getSlackClient(),
3565
- userId,
3566
- getUserTokenStore(args.services)
3567
- );
3568
- } catch (error) {
3569
- logException(error, "slack_app_home_publish_failed", {
3570
- userId
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
- error,
3727
- "app_home_disconnect_unlink_failed",
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
- error,
3737
- "app_home_disconnect_publish_failed",
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
- runWithWorkspaceTeamId(
3768
- installation2.teamId,
3769
- () => runWithSlackInstallation({
3770
- adapter,
3771
- installation: installation2,
3772
- state,
3773
- task: () => handleSlashCommandForm({
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
- params,
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
- ).catch((error) => {
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
- runWithWorkspaceTeamId(
3799
- installation.teamId,
3800
- () => runWithSlackInstallation({
3801
- adapter,
3802
- installation,
3803
- state,
3804
- task: () => handleInteractivePayload({
3805
- payload,
3806
- userTokenStore: getUserTokenStore(args.services)
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
- ).catch((error) => {
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 eventTask = handleSlackEvent({
3841
- body: parsed,
3842
- services: args.services
3843
- });
3763
+ const eventLogContext = slackEventLogContext(parsed.event);
3844
3764
  if (shouldPersistBeforeAck(parsed)) {
3845
- try {
3846
- await eventTask;
3847
- } catch (error) {
3848
- if (error instanceof SlackEventPersistenceError) {
3849
- logException(error.cause, "slack_event_persist_failed");
3850
- } else {
3851
- logException(error, "slack_event_routing_failed");
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
- return new Response("Slack event handling failed", { status: 503 });
3783
+ );
3784
+ if (failureResponse) {
3785
+ return failureResponse;
3854
3786
  }
3855
3787
  } else {
3856
3788
  enqueue(
3857
3789
  args.waitUntil,
3858
- eventTask.catch((error) => {
3859
- logException(error, "slack_event_enqueue_failed");
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
- "webhook_non_success_response",
3892
- {},
3893
- {
3894
- "http.response.status_code": response.status,
3895
- "http.request.header.x_slack_signature": request.headers.get("x-slack-signature") ?? void 0,
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, "webhook_handler_failed");
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
- "conversation_work_dead_lettered",
4022
- { conversationId: args.conversationId },
4023
- {
4024
- "app.conversation.source": message.source,
4025
- "app.inbound.attempt_count": message.attemptCount ?? 0,
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
- "conversation_work_nudge_deferred_for_active_lease",
4138
- { conversationId },
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
- "conversation_work_lease_acquired",
4163
- { conversationId },
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
- "conversation_work_cooperative_yield",
4223
- { conversationId },
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
- "conversation_work_completed",
4444
- { conversationId },
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
- error,
4515
- "conversation_work_failed",
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
- logWarn(
4584
- "conversation_queue_message_rejected",
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
- "Conversation queue message rejected without retry"
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
- "plugin_task_queue_message_rejected",
4638
- {},
4639
- {
4640
- "app.queue.consumer_group": metadata.consumerGroup,
4641
- "app.queue.delivery_count": metadata.deliveryCount,
4642
- "app.queue.message_id": metadata.messageId,
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
- postError,
5190
- args.postFailureEventName,
5191
- args.errorContext,
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
- deps.logWarn(
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
- "app.decision.reason": args.decision.reason
5247
- },
5248
- "Skipping subscribed message reply"
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
- deps.logException(
5341
- error,
5342
- "skipped_steering_flush_failed",
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 classifiedFailure = getConversationTurnBoundaryError(error);
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
- deps.logWarn(
5463
- "mention_handler_read_only_channel",
5464
- logContext({
5465
- threadId: deps.getThreadId(thread, message),
5466
- actorId: message.author.userId,
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 ?? deps.logException(
5477
- failureCause,
5478
- "mention_handler_failed",
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 mention_handler_failed"
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: "mention_handler_failure_reply_post_failed",
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 classifiedFailure = getConversationTurnBoundaryError(error);
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
- deps.logWarn(
5741
- "subscribed_message_handler_read_only_channel",
5742
- logContext({
5743
- threadId: deps.getThreadId(thread, message),
5744
- actorId: message.author.userId,
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 ?? deps.logException(
5755
- failureCause,
5756
- "subscribed_message_handler_failed",
5757
- errorContext,
5758
- {},
5759
- "onSubscribedMessage failed"
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 subscribed_message_handler_failed"
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: "subscribed_message_handler_failure_reply_post_failed",
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
- try {
5810
- await deps.initializeAssistantThread({
5660
+ await withLogContext(
5661
+ logContext({
5811
5662
  threadId: event.threadId,
5812
- channelId: event.channelId,
5813
- threadTs: event.threadTs,
5814
- sourceChannelId: event.context?.channelId
5815
- });
5816
- } catch (error) {
5817
- deps.logException(
5818
- error,
5819
- "assistant_thread_started_handler_failed",
5820
- {
5821
- messageConversationId: event.threadId,
5822
- userId: event.userId,
5823
- destinationName: event.channelId,
5824
- assistantUserName: deps.assistantUserName,
5825
- modelId: deps.modelId
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
- try {
5834
- await deps.refreshAssistantThreadContext({
5681
+ await withLogContext(
5682
+ logContext({
5835
5683
  threadId: event.threadId,
5836
- channelId: event.channelId,
5837
- threadTs: event.threadTs,
5838
- sourceChannelId: event.context?.channelId
5839
- });
5840
- } catch (error) {
5841
- deps.logException(
5842
- error,
5843
- "assistant_context_changed_handler_failed",
5844
- {
5845
- messageConversationId: event.threadId,
5846
- userId: event.userId,
5847
- destinationName: event.channelId,
5848
- assistantUserName: deps.assistantUserName,
5849
- modelId: deps.modelId
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, input) => {
5868
- logWarn(
5869
- "subscribed_message_classifier_failed",
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
- "attachment_skipped_size_limit",
6081
- {
6082
- messageConversationId: context.threadId,
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
- "Failed to resolve user attachment"
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
- "conversation_image_vision_failed",
6175
- {
6176
- messageConversationId: args.context.threadId,
6177
- userId: args.context.actorId,
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
- "conversation_image_replies_fetch_failed",
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
- "conversation_image_skipped_size_limit",
6298
- {
6299
- messageConversationId: context.threadId,
6300
- userId: context.actorId,
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
- "conversation_image_download_failed",
6325
- {
6326
- messageConversationId: context.threadId,
6327
- userId: context.actorId,
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
- "conversation_image_skipped_size_limit",
6345
- {
6346
- messageConversationId: context.threadId,
6347
- userId: context.actorId,
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
- "conversation_image_context_hydrated",
6388
- {
6389
- messageConversationId: context.threadId,
6390
- userId: context.actorId,
6391
- destinationName: context.channelId,
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
- "thread_title_generation_failed",
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
- "thread_title_generation_permission_denied",
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
- "thread_title_slack_update_failed",
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
- error,
6899
- "slack_auth_pause_notice_post_failed",
6900
- turnTraceContext,
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
- error,
7032
- "agent_continue_schedule_failed",
7033
- turnTraceContext,
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
- error,
7162
- "agent_turn_summary_record_failed",
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
- "agent_turn_started",
7175
- turnTraceContext,
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 (assistantMessage, terminalDispatchOutcome) => {
7263
- if (!assistantMessage.text.trim()) {
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: assistantMessage.text,
6959
+ text,
7275
6960
  ...threadTs ? { threadTs } : {}
7276
6961
  });
7277
6962
  } else {
7278
- for (const text of splitSlackReplyText(assistantMessage.text)) {
7279
- slackTs = (await thread.post(buildSlackOutputMessage(text))).id;
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
- error,
7288
- "slack_thread_post_failed",
7289
- turnTraceContext,
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: assistantMessage.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
- () => persistConversationMessages({
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
- "slack_assistant_message_post_delivery_persist_failed",
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
- "slack_subscribed_turn_authorization_required",
7589
- turnTraceContext,
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({ text });
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
- scheduleError,
7655
- "agent_continue_schedule_failed",
7656
- turnTraceContext,
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
- const diagnosticsContext = {
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({ text: reply.text });
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
- "slack_reply_post_delivery_commit_failed",
7771
- turnTraceContext,
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
- "agent_turn_completed",
7811
- turnTraceContext,
7812
- {
7813
- "app.ai.outcome": reply.diagnostics.outcome,
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
- "plugin_session_completed_task_schedule_failed",
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
- "slack_reply_post_delivery_commit_failed",
7843
- turnTraceContext,
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
- "slack_reply_post_delivery_completion_callback_failed",
7853
- turnTraceContext,
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
- failureCause,
7880
- "slack_turn_execution_failed",
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
- "agent_turn_failed_session_record_persist_failed",
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
- "agent_continue_session_record_fail_persist_failed",
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
- "agent_continue_startup_failure_persist_failed",
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
- "agent_continue_reparked_for_auth",
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 = "agent_turn_stranded_session_failed";
8918
- const eventId = logException(
8919
- new Error(args.errorMessage),
8920
- eventName,
8921
- { conversationId: args.conversationId },
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
- "agent_continue_lock_busy",
9064
- {},
9065
- {
9066
- "app.ai.conversation_id": payload.conversationId,
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
- "agent_continue_lock_busy_retrying",
9077
- {},
9078
- {
9079
- "app.ai.conversation_id": payload.conversationId,
9080
- "app.ai.session_id": payload.sessionId,
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, "unhandled_route_error");
9257
+ logException(err, "route.unhandled.exception");
9651
9258
  return c.text("Internal Server Error", 500);
9652
9259
  });
9653
9260
  app.use("*", async (c, next) => {