@sentry/junior 0.144.0 → 0.146.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 (104) hide show
  1. package/dist/{agent-hooks-GCUTGKY3.js → agent-hooks-SLJADYE5.js} +10 -10
  2. package/dist/api/conversations/detail.d.ts +1 -0
  3. package/dist/api/schema/conversation.d.ts +2 -0
  4. package/dist/api/schema.js +1 -1
  5. package/dist/api.js +40 -22
  6. package/dist/app.js +400 -461
  7. package/dist/{catalog-runtime-DWVYKDC6.js → catalog-runtime-RQIMSDPR.js} +5 -5
  8. package/dist/chat/agent/request.d.ts +1 -1
  9. package/dist/chat/agent/resume.d.ts +6 -18
  10. package/dist/chat/agent/skills.d.ts +0 -1
  11. package/dist/chat/agent/tools.d.ts +1 -2
  12. package/dist/chat/agent-dispatch/types.d.ts +1 -1
  13. package/dist/chat/app/conversation-work.d.ts +24 -0
  14. package/dist/chat/conversations/message-search.d.ts +32 -0
  15. package/dist/chat/conversations/projection.d.ts +11 -3
  16. package/dist/chat/conversations/sql/message-search.d.ts +4 -0
  17. package/dist/chat/conversations/store.d.ts +1 -1
  18. package/dist/chat/conversations/tool-source.d.ts +5 -0
  19. package/dist/chat/db.d.ts +2 -2
  20. package/dist/chat/runtime/agent-run-outcome.d.ts +1 -1
  21. package/dist/chat/runtime/reply-executor.d.ts +5 -5
  22. package/dist/chat/runtime/slack-resume.d.ts +12 -4
  23. package/dist/chat/slack/message/attachments.d.ts +2 -0
  24. package/dist/chat/slack/message/blocks.d.ts +2 -0
  25. package/dist/chat/slack/message/content.d.ts +18 -0
  26. package/dist/chat/slack/message/identity.d.ts +7 -0
  27. package/dist/chat/slack/tools/{conversation-search.d.ts → conversation-message-search.d.ts} +21 -14
  28. package/dist/chat/slack/tools/user-lookup.d.ts +2 -1
  29. package/dist/chat/state/locks.d.ts +28 -0
  30. package/dist/chat/task-execution/checkpoint.d.ts +84 -0
  31. package/dist/chat/task-execution/paused-turn.d.ts +29 -0
  32. package/dist/chat/task-execution/queue-signing.d.ts +1 -0
  33. package/dist/chat/task-execution/queue.d.ts +7 -1
  34. package/dist/chat/task-execution/slack-work.d.ts +1 -1
  35. package/dist/chat/task-execution/state.d.ts +4 -4
  36. package/dist/chat/task-execution/store.d.ts +2 -2
  37. package/dist/chat/task-execution/turn-cursor-keys.d.ts +6 -0
  38. package/dist/chat/{state/turn-session.d.ts → task-execution/turn-cursor.d.ts} +52 -51
  39. package/dist/chat/task-execution/turn-wake.d.ts +29 -0
  40. package/dist/chat/tools/{query-conversation-events.d.ts → search-conversation-events.d.ts} +3 -3
  41. package/dist/chat/tools/types.d.ts +1 -1
  42. package/dist/{chunk-SBZP2J5Q.js → chunk-37EVZP66.js} +48 -35
  43. package/dist/{chunk-A67GZN73.js → chunk-37JJDS4H.js} +1 -1
  44. package/dist/{chunk-BBCWYXHB.js → chunk-47OSH2ZO.js} +7 -7
  45. package/dist/{chunk-4X5KM7Z6.js → chunk-AISNSNDV.js} +14 -14
  46. package/dist/{chunk-RIFJUJSI.js → chunk-B63BF443.js} +1 -1
  47. package/dist/{chunk-SSDTQDYQ.js → chunk-BLETMLRX.js} +566 -268
  48. package/dist/{chunk-I44JB7HQ.js → chunk-F7REGDKO.js} +1 -1
  49. package/dist/{chunk-DVO5VKYQ.js → chunk-FEKQHPVA.js} +5 -5
  50. package/dist/{chunk-SUK7NC5A.js → chunk-FGHADGIC.js} +2 -2
  51. package/dist/{chunk-7KAW7UF4.js → chunk-FXDDDSPL.js} +4 -4
  52. package/dist/{chunk-BEGGFVFR.js → chunk-GDQEU4I7.js} +414 -542
  53. package/dist/{chunk-UDQMY6RS.js → chunk-GYEKOX7N.js} +43 -1
  54. package/dist/{chunk-W7JFV5SB.js → chunk-H6U2U3EP.js} +14 -17
  55. package/dist/{chunk-WTX2RFJG.js → chunk-HQE6EJS5.js} +1 -1
  56. package/dist/{chunk-IUFM5JWV.js → chunk-JAL3CH4G.js} +3 -0
  57. package/dist/{chunk-E7HCWOHG.js → chunk-JLMVCNXL.js} +2 -1
  58. package/dist/{chunk-PELZ2ZSY.js → chunk-JUA6VU2Z.js} +33 -63
  59. package/dist/{chunk-SHEEZSLY.js → chunk-LVW7BLLT.js} +2 -2
  60. package/dist/{chunk-GKA74DRA.js → chunk-M6ZWP4RI.js} +1 -1
  61. package/dist/{chunk-HNO2E5WB.js → chunk-MCFIXAV5.js} +1 -1
  62. package/dist/{chunk-3GU4PWA3.js → chunk-MKRIZ4VG.js} +29 -30
  63. package/dist/{chunk-QUZJDAQQ.js → chunk-OFPYOC3M.js} +93 -90
  64. package/dist/{chunk-ZF3N3JD6.js → chunk-PB222GIA.js} +1 -1
  65. package/dist/{chunk-PKH6762I.js → chunk-PH4Q3A6W.js} +1 -1
  66. package/dist/{chunk-STFCL3K4.js → chunk-QG7OOKIN.js} +1 -1
  67. package/dist/{chunk-WSOLZJJS.js → chunk-QSXZ42KX.js} +3 -3
  68. package/dist/{chunk-44RJWAND.js → chunk-SCGVWUZX.js} +6 -6
  69. package/dist/{chunk-CNTWB2SM.js → chunk-T5GXFFLA.js} +1 -1
  70. package/dist/{chunk-LDWNYCZ6.js → chunk-TCEJ7XXM.js} +1 -1
  71. package/dist/{chunk-QE3TICD4.js → chunk-V6SRLKUB.js} +2 -2
  72. package/dist/{chunk-54JMYQYS.js → chunk-W65V465O.js} +2 -2
  73. package/dist/{chunk-KCBZSJRU.js → chunk-WR76IVA5.js} +5 -2
  74. package/dist/{chunk-ZZCTGAIT.js → chunk-X22KCSCO.js} +4 -4
  75. package/dist/{chunk-HD6BHR25.js → chunk-X2WMBTS3.js} +32 -34
  76. package/dist/{chunk-KQBXT5B7.js → chunk-ZDGHGY74.js} +6 -6
  77. package/dist/cli/chat.js +32 -32
  78. package/dist/cli/check.js +6 -6
  79. package/dist/cli/plugins.js +13 -13
  80. package/dist/cli/snapshot-create.js +9 -9
  81. package/dist/cli/upgrade.js +7 -7
  82. package/dist/{db-EFLHTBU6.js → db-WWOMXWTT.js} +9 -9
  83. package/dist/instrumentation.js +4 -4
  84. package/dist/nitro.js +4 -4
  85. package/dist/{oauth-callback-server-RO3IQKWM.js → oauth-callback-server-A6ZZX74U.js} +25 -25
  86. package/dist/{runner-LOWW7JKA.js → runner-KSGB2UAV.js} +19 -20
  87. package/dist/{skills-ZLMUG7AE.js → skills-26QPWKVS.js} +6 -6
  88. package/dist/spawn-5LAJXZ2P.js +39 -0
  89. package/dist/{validation-MAKEZNTS.js → validation-OPSRGFGH.js} +6 -6
  90. package/dist/version.js +1 -1
  91. package/dist/{work-5OAGC3I4.js → work-VMSW7STJ.js} +17 -16
  92. package/dist/worker-2JNFB7C7.js +24 -0
  93. package/package.json +5 -5
  94. package/dist/chat/agent/session.d.ts +0 -19
  95. package/dist/chat/conversations/search.d.ts +0 -24
  96. package/dist/chat/conversations/sql/search.d.ts +0 -4
  97. package/dist/chat/runtime/agent-continue-runner.d.ts +0 -37
  98. package/dist/chat/services/agent-continue.d.ts +0 -29
  99. package/dist/chat/services/turn-session-record.d.ts +0 -164
  100. package/dist/chat/slack/legacy-attachments.d.ts +0 -4
  101. package/dist/chat/slack/message.d.ts +0 -12
  102. package/dist/chat/state/turn-session-keys.d.ts +0 -6
  103. package/dist/spawn-R3LUV6RF.js +0 -38
  104. package/dist/worker-KTGGRTTP.js +0 -24
package/dist/app.js CHANGED
@@ -1,26 +1,25 @@
1
1
  import {
2
2
  processConversationWork
3
- } from "./chunk-4X5KM7Z6.js";
3
+ } from "./chunk-AISNSNDV.js";
4
4
  import {
5
5
  bindSpawnAgent
6
- } from "./chunk-GKA74DRA.js";
6
+ } from "./chunk-M6ZWP4RI.js";
7
7
  import {
8
8
  createAgentInvocationWorker,
9
9
  enqueueAgentInvocation,
10
10
  listPendingAgentInvocationMailboxAppends,
11
11
  routeAgentInvocationWork
12
- } from "./chunk-PELZ2ZSY.js";
12
+ } from "./chunk-JUA6VU2Z.js";
13
13
  import {
14
14
  GET as GET2,
15
15
  GET2 as GET3,
16
- ResumeTurnBusyError,
17
16
  buildAuthPauseResponse,
18
17
  buildSlackOutputMessage,
19
18
  createSlackAdapterAssistantStatusSession,
20
19
  getAssistantThreadContext,
21
- getAwaitingAgentContinueRequest,
22
20
  getChannelId,
23
21
  getMessageTs,
22
+ getPausedTurnRequest,
24
23
  getRunId,
25
24
  getSlackApiErrorCode,
26
25
  getSlackErrorObservabilityAttributes,
@@ -33,13 +32,13 @@ import {
33
32
  receiveLocalOAuthCredential,
34
33
  resumeSlackTurn,
35
34
  runWithTurnRequestDeadline,
36
- scheduleAgentContinue,
37
35
  sendSlackReply,
38
36
  shouldKeepProcessingReactionForToolInvocation,
39
37
  splitSlackReplyText,
40
38
  startSlackProcessingReaction,
41
- stripLeadingBotMention
42
- } from "./chunk-QUZJDAQQ.js";
39
+ stripLeadingBotMention,
40
+ wakePausedTurn
41
+ } from "./chunk-OFPYOC3M.js";
43
42
  import "./chunk-FA6POGLH.js";
44
43
  import {
45
44
  CONVERSATION_WORK_STALE_ENQUEUE_MS,
@@ -51,7 +50,7 @@ import {
51
50
  isInvalidConversationRecordError,
52
51
  listActiveConversationIds,
53
52
  removeActiveConversation
54
- } from "./chunk-HD6BHR25.js";
53
+ } from "./chunk-X2WMBTS3.js";
55
54
  import {
56
55
  buildConversationContext,
57
56
  buildDeliveredTurnStatePatch,
@@ -90,8 +89,9 @@ import {
90
89
  requireTurnFailureEventId,
91
90
  resolveTurnSessionRouting,
92
91
  scheduleSessionCompletedPluginTasks,
92
+ turnHasReply,
93
93
  upsertConversationMessage
94
- } from "./chunk-W7JFV5SB.js";
94
+ } from "./chunk-H6U2U3EP.js";
95
95
  import {
96
96
  ConversationTurnLifecycleService
97
97
  } from "./chunk-Q6QZTTLS.js";
@@ -100,21 +100,21 @@ import {
100
100
  conversationVisibilityFromSlackChannelType,
101
101
  resolveSlackChannelTypeFromMessage,
102
102
  resolveSlackConversationContext
103
- } from "./chunk-SHEEZSLY.js";
103
+ } from "./chunk-LVW7BLLT.js";
104
104
  import "./chunk-4CODA7P5.js";
105
105
  import {
106
106
  recordTaskExecution
107
- } from "./chunk-WSOLZJJS.js";
107
+ } from "./chunk-QSXZ42KX.js";
108
108
  import "./chunk-UTL3MHPC.js";
109
109
  import {
110
110
  getVercelConversationWorkQueue,
111
111
  resolveConversationWorkQueueTopic,
112
112
  verifyConversationQueueMessage
113
- } from "./chunk-KCBZSJRU.js";
113
+ } from "./chunk-WR76IVA5.js";
114
114
  import {
115
115
  conversationQueueMessageSchema,
116
116
  isConversationQueueMessageRejectedError
117
- } from "./chunk-IUFM5JWV.js";
117
+ } from "./chunk-JAL3CH4G.js";
118
118
  import {
119
119
  PLUGIN_TASK_QUEUE_TOPIC,
120
120
  createVercelQueueClient,
@@ -123,7 +123,7 @@ import {
123
123
  import {
124
124
  validatePluginEgressCredentialHooks,
125
125
  validatePluginRegistrations
126
- } from "./chunk-LDWNYCZ6.js";
126
+ } from "./chunk-TCEJ7XXM.js";
127
127
  import {
128
128
  defineJuniorPlugins,
129
129
  pluginCatalogConfigFromEnv,
@@ -132,7 +132,6 @@ import {
132
132
  } from "./chunk-FIANEE7Z.js";
133
133
  import {
134
134
  SANDBOX_EGRESS_PROXY_PATH,
135
- appendSlackLegacyAttachmentText,
136
135
  buildSteeringPiMessage,
137
136
  cancelSubscriptions,
138
137
  clearSandboxEgressSignals,
@@ -149,25 +148,26 @@ import {
149
148
  listThreadReplies,
150
149
  normalizeSandboxEgressTracePropagationDomains,
151
150
  parseSandboxEgressCredentialToken,
152
- renderSlackLegacyAttachmentText,
151
+ renderAttachmentText,
152
+ renderBlockText,
153
153
  resolveDestinationVisibility,
154
154
  resolveSandboxEgressProviderForHost,
155
155
  sanitizeScheduledTaskPrincipal,
156
156
  setConfigDefaults,
157
157
  shouldEmitDevAgentTrace,
158
158
  shouldPropagateSandboxEgressTrace
159
- } from "./chunk-SSDTQDYQ.js";
159
+ } from "./chunk-BLETMLRX.js";
160
160
  import {
161
161
  AuthorizationFlowDisabledError,
162
162
  PluginCredentialFailureError
163
- } from "./chunk-DVO5VKYQ.js";
163
+ } from "./chunk-FEKQHPVA.js";
164
164
  import "./chunk-DIB4GX3G.js";
165
165
  import {
166
166
  createUserTokenStore,
167
167
  deleteMcpAuthSessionsForUserProvider,
168
168
  deleteMcpServerSessionId,
169
169
  deleteMcpStoredOAuthCredentials
170
- } from "./chunk-BBCWYXHB.js";
170
+ } from "./chunk-47OSH2ZO.js";
171
171
  import {
172
172
  RetryableDeliveryError
173
173
  } from "./chunk-R776HKPK.js";
@@ -175,16 +175,16 @@ import {
175
175
  formatProviderLabel,
176
176
  postSlackMessage,
177
177
  startOAuthFlow
178
- } from "./chunk-KQBXT5B7.js";
178
+ } from "./chunk-ZDGHGY74.js";
179
179
  import {
180
180
  estimateTextTokens
181
- } from "./chunk-CNTWB2SM.js";
181
+ } from "./chunk-T5GXFFLA.js";
182
182
  import {
183
183
  ConversationTurnBoundaryError,
184
184
  CooperativeTurnYieldError,
185
185
  TurnInputCommitLostError,
186
186
  TurnInputDeferredError,
187
- abandonAgentTurnSessionRecord,
187
+ abandonTurnRecord,
188
188
  buildDeterministicAssistantMessageId,
189
189
  buildDeterministicTurnId,
190
190
  clearPendingAuth,
@@ -192,24 +192,26 @@ import {
192
192
  coerceThreadConversationState,
193
193
  commitAcceptedReply,
194
194
  commitMessages,
195
- completeDeliveredTurn,
196
- failAgentTurnSessionRecord,
197
- getAgentTurnSessionRecord,
198
- getAgentTurnSessionRecordForResume,
195
+ completeTurnRecord,
196
+ failTurnRecord,
199
197
  getAssistantReplyText,
200
198
  getChannelConfigurationService,
201
199
  getChannelConfigurationServiceById,
202
200
  getConversationTurnBoundaryError,
203
201
  getPersistedSandboxState,
204
202
  getPersistedThreadState,
203
+ getTurnRecord,
204
+ getTurnRecordForResume,
205
205
  hydrateConversationMessages,
206
206
  isCooperativeTurnYieldError,
207
207
  isTurnInputCommitLostError,
208
208
  isTurnInputDeferredError,
209
- listAgentTurnSessionSummariesForConversation,
209
+ listTurnSummaries,
210
210
  loadConversationProjection,
211
211
  loadProjection,
212
+ loadTurnCheckpoint,
212
213
  markTurnClosed,
214
+ markTurnCompleted,
213
215
  markTurnFailed,
214
216
  mergeArtifactsState,
215
217
  persistConversationMessageSummaries,
@@ -218,12 +220,12 @@ import {
218
220
  persistThreadState,
219
221
  persistThreadStateById,
220
222
  persistWithRetry,
221
- persistYieldSessionRecord,
222
- recordAgentTurnSessionSummary,
223
+ recordTurnSummary,
224
+ saveTurnCheckpoint,
223
225
  startActiveTurn,
224
226
  stripRuntimeTurnContext,
225
227
  trimTrailingAssistantMessages
226
- } from "./chunk-BEGGFVFR.js";
228
+ } from "./chunk-GDQEU4I7.js";
227
229
  import "./chunk-3Y47VMHV.js";
228
230
  import "./chunk-HUO3UTAQ.js";
229
231
  import {
@@ -233,17 +235,17 @@ import {
233
235
  import {
234
236
  getSandboxResourceConfig,
235
237
  setSandboxResourceConfig
236
- } from "./chunk-7KAW7UF4.js";
238
+ } from "./chunk-FXDDDSPL.js";
237
239
  import {
238
240
  createSchedulerSqlStore,
239
241
  findMatchingEventTasks,
240
242
  logScheduledTaskRunSkipped,
241
243
  scheduledTaskRunAttributes
242
- } from "./chunk-ZZCTGAIT.js";
244
+ } from "./chunk-X22KCSCO.js";
243
245
  import {
244
246
  parseSlackThreadId
245
- } from "./chunk-PKH6762I.js";
246
- import "./chunk-54JMYQYS.js";
247
+ } from "./chunk-PH4Q3A6W.js";
248
+ import "./chunk-W65V465O.js";
247
249
  import "./chunk-HLXXQI5Q.js";
248
250
  import "./chunk-PDO5BLNM.js";
249
251
  import "./chunk-UIE3R5XU.js";
@@ -263,7 +265,7 @@ import {
263
265
  verifyEventTaskCredentialSubject,
264
266
  verifyScheduledTaskCredentialSubject,
265
267
  verifySlackDirectCredentialSubject
266
- } from "./chunk-44RJWAND.js";
268
+ } from "./chunk-SCGVWUZX.js";
267
269
  import {
268
270
  SlackActionError,
269
271
  downloadPrivateSlackFile,
@@ -273,7 +275,7 @@ import {
273
275
  normalizeSlackConversationId,
274
276
  runWithSlackInstallationToken,
275
277
  runWithWorkspaceTeamId
276
- } from "./chunk-QE3TICD4.js";
278
+ } from "./chunk-V6SRLKUB.js";
277
279
  import {
278
280
  contextProvenance,
279
281
  createSlackDestination,
@@ -285,20 +287,19 @@ import {
285
287
  requireSlackDestination,
286
288
  withConversationEventLock,
287
289
  withConversationMutationLock
288
- } from "./chunk-SBZP2J5Q.js";
290
+ } from "./chunk-37EVZP66.js";
289
291
  import {
290
292
  juniorAgentBindings,
291
293
  juniorAgentInvocations,
292
294
  juniorConversationEvents,
293
295
  juniorConversations
294
- } from "./chunk-HNO2E5WB.js";
295
- import {
296
- sleep
297
- } from "./chunk-EM225ITJ.js";
296
+ } from "./chunk-MCFIXAV5.js";
297
+ import "./chunk-EM225ITJ.js";
298
298
  import {
299
299
  acquireActiveLock,
300
- getStateAdapter
301
- } from "./chunk-UDQMY6RS.js";
300
+ getStateAdapter,
301
+ withActiveLock
302
+ } from "./chunk-GYEKOX7N.js";
302
303
  import {
303
304
  STANDARD_MODEL_PROFILE,
304
305
  botConfig,
@@ -316,16 +317,16 @@ import {
316
317
  modelIdForProfile,
317
318
  setSlackReactionConfig,
318
319
  standardModelId
319
- } from "./chunk-3GU4PWA3.js";
320
+ } from "./chunk-MKRIZ4VG.js";
320
321
  import {
321
322
  juniorDestinations
322
323
  } from "./chunk-VFUK3X5B.js";
323
- import "./chunk-STFCL3K4.js";
324
- import "./chunk-SUK7NC5A.js";
324
+ import "./chunk-QG7OOKIN.js";
325
+ import "./chunk-FGHADGIC.js";
325
326
  import {
326
327
  pluginCatalogRuntime
327
- } from "./chunk-ZF3N3JD6.js";
328
- import "./chunk-A67GZN73.js";
328
+ } from "./chunk-PB222GIA.js";
329
+ import "./chunk-37JJDS4H.js";
329
330
  import {
330
331
  credentialContextForActor
331
332
  } from "./chunk-RUIXF3YQ.js";
@@ -357,7 +358,7 @@ import {
357
358
  withContext,
358
359
  withLogContext,
359
360
  withSpan
360
- } from "./chunk-RIFJUJSI.js";
361
+ } from "./chunk-B63BF443.js";
361
362
  import {
362
363
  continueTrace
363
364
  } from "./chunk-6GWA276C.js";
@@ -365,10 +366,10 @@ import "./chunk-OOUNJNMR.js";
365
366
  import "./chunk-H4HBKISO.js";
366
367
  import {
367
368
  JUNIOR_PLUGIN_TASK_CALLBACK_ROUTE
368
- } from "./chunk-WTX2RFJG.js";
369
+ } from "./chunk-HQE6EJS5.js";
369
370
  import {
370
371
  JUNIOR_VERSION
371
- } from "./chunk-I44JB7HQ.js";
372
+ } from "./chunk-F7REGDKO.js";
372
373
  import "./chunk-MLKGABMK.js";
373
374
 
374
375
  // src/app.ts
@@ -533,12 +534,10 @@ async function resolveAgentDispatchId(context) {
533
534
  if (context.attempt.messages.length > 0) {
534
535
  return void 0;
535
536
  }
536
- const summaries = await listAgentTurnSessionSummariesForConversation(
537
- context.conversationId
538
- );
537
+ const summaries = await listTurnSummaries(context.conversationId);
539
538
  const activeDispatchIds = new Set(
540
539
  summaries.filter(
541
- (summary) => (summary.state === "awaiting_resume" || summary.state === "running") && Boolean(summary.dispatchId)
540
+ (summary) => (summary.state === "paused" || summary.state === "running") && Boolean(summary.dispatchId)
542
541
  ).map((summary) => summary.dispatchId).filter((id) => Boolean(id))
543
542
  );
544
543
  if (activeDispatchIds.size > 1) {
@@ -585,8 +584,10 @@ function createAgentDispatchWorkRouter(options) {
585
584
  async function readDispatchTurnResult(dispatch2) {
586
585
  const conversationId = getDispatchConversationId(dispatch2);
587
586
  const turnId = getDispatchTurnId(dispatch2.id);
588
- const storedSession = await getAgentTurnSessionRecord(conversationId, turnId);
589
- const summary = (await listAgentTurnSessionSummariesForConversation(conversationId)).find((candidate) => candidate.sessionId === turnId);
587
+ const storedSession = await getTurnRecord(conversationId, turnId);
588
+ const summary = (await listTurnSummaries(conversationId)).find(
589
+ (candidate) => candidate.turnId === turnId
590
+ );
590
591
  const session = storedSession ?? summary;
591
592
  const dispatchOutcome = summary?.dispatchOutcome ?? storedSession?.dispatchOutcome;
592
593
  const resultMessageTs = summary?.resultMessageId ?? storedSession?.resultMessageId;
@@ -607,7 +608,7 @@ async function readDispatchTurnResult(dispatch2) {
607
608
  if (!session) {
608
609
  return {};
609
610
  }
610
- if (session.state === "awaiting_resume") {
611
+ if (session.state === "paused") {
611
612
  return { hasResumableRun: true, outcome: "awaiting_resume" };
612
613
  }
613
614
  if (session.state === "running") {
@@ -652,15 +653,15 @@ function getDispatchBlockingError(error) {
652
653
  async function persistBlockedDispatchTurn(dispatch2, error) {
653
654
  const conversationId = getDispatchConversationId(dispatch2);
654
655
  const sessionId = getDispatchTurnId(dispatch2.id);
655
- const session = await getAgentTurnSessionRecord(conversationId, sessionId);
656
- await recordAgentTurnSessionSummary({
656
+ const session = await getTurnRecord(conversationId, sessionId);
657
+ await recordTurnSummary({
657
658
  actor: dispatch2.actor,
658
659
  conversationId,
659
660
  destination: dispatch2.destination,
660
661
  destinationVisibility: dispatch2.destinationVisibility,
661
662
  dispatchId: dispatch2.id,
662
663
  dispatchOutcome: "blocked",
663
- sessionId,
664
+ turnId: sessionId,
664
665
  sliceId: session?.sliceId ?? 1,
665
666
  source: dispatch2.source,
666
667
  state: "failed",
@@ -880,7 +881,9 @@ async function recoverConversationWork(args) {
880
881
  }
881
882
  if (recovery === "stopped") {
882
883
  logException(
883
- new Error("Conversation work stopped after repeated failed attempts"),
884
+ new Error(
885
+ "Conversation work stopped after repeated failed attempts"
886
+ ),
884
887
  "conversation.work.retry.exhausted",
885
888
  {
886
889
  "app.run.id": work.execution.runId ?? "unknown",
@@ -3098,14 +3101,14 @@ function createSlackConversationWorker(options) {
3098
3101
  if (records.length === 0) {
3099
3102
  const destination = requireSlackDestination(
3100
3103
  context.destination,
3101
- "Slack continuation recovery"
3104
+ "Slack paused-turn recovery"
3102
3105
  );
3103
3106
  await runWithSlackInstallation({
3104
3107
  adapter,
3105
3108
  installation: { teamId: destination.teamId },
3106
3109
  state,
3107
3110
  task: async () => {
3108
- await options.resumeAwaitingContinuation(context.conversationId, {
3111
+ await options.runNextPausedTurn(context.conversationId, {
3109
3112
  shouldYield: context.shouldYield
3110
3113
  });
3111
3114
  }
@@ -4646,23 +4649,38 @@ async function decideSubscribedThreadReply(args) {
4646
4649
  }
4647
4650
  }
4648
4651
 
4649
- // src/chat/slack/message.ts
4652
+ // src/chat/slack/message/content.ts
4650
4653
  import { stringifyMarkdown } from "chat";
4651
- function getSlackMessageTs(message) {
4652
- const idTs = parseSlackMessageTs(message.id);
4653
- if (idTs) {
4654
- return idTs;
4655
- }
4656
- if (message.raw && typeof message.raw === "object") {
4657
- return parseSlackMessageTs(message.raw.ts);
4658
- }
4659
- return void 0;
4654
+ function combineText(text, attachmentText) {
4655
+ const topLevelText = text.trim();
4656
+ if (!attachmentText) return topLevelText;
4657
+ if (!topLevelText) return attachmentText;
4658
+ return `${topLevelText}
4659
+ ${attachmentText}`;
4660
+ }
4661
+ function replaceTopLevelText(content, text) {
4662
+ return combineText(text, content.attachmentText);
4663
+ }
4664
+ function readRawBlocks(raw) {
4665
+ if (!raw || typeof raw !== "object") return void 0;
4666
+ return raw.blocks;
4660
4667
  }
4661
- function getSlackMessageText(message) {
4668
+ function renderTopLevelText(message) {
4662
4669
  if (message.formatted.children.length > 0) {
4663
4670
  return stringifyMarkdown(message.formatted).trim();
4664
4671
  }
4665
- return message.text.trim();
4672
+ const text = message.text.trim();
4673
+ return text || renderBlockText(readRawBlocks(message.raw));
4674
+ }
4675
+ function parseContent(message) {
4676
+ const topLevelText = renderTopLevelText(message);
4677
+ const attachmentText = renderAttachmentText(message.raw);
4678
+ return {
4679
+ attachmentText,
4680
+ hasAttachments: message.attachments.length > 0 || attachmentText.length > 0,
4681
+ text: combineText(topLevelText, attachmentText),
4682
+ topLevelText
4683
+ };
4666
4684
  }
4667
4685
 
4668
4686
  // src/chat/runtime/turn-input.ts
@@ -4692,9 +4710,9 @@ function shouldRethrowTurnControlError(error) {
4692
4710
  }
4693
4711
  function getQueuedMessages(context, options) {
4694
4712
  return (context?.skipped ?? []).map((message) => {
4695
- const messageText = getSlackMessageText(message);
4713
+ const content = parseContent(message);
4696
4714
  const stripped = options.stripLeadingBotMention(
4697
- stripLeadingSteeringOverride(messageText),
4715
+ stripLeadingSteeringOverride(content.topLevelText),
4698
4716
  {
4699
4717
  stripLeadingSlackMentionToken: options.explicitMention || Boolean(message.isMention)
4700
4718
  }
@@ -4702,8 +4720,8 @@ function getQueuedMessages(context, options) {
4702
4720
  return {
4703
4721
  explicitMention: Boolean(message.isMention),
4704
4722
  message,
4705
- rawText: appendSlackLegacyAttachmentText(messageText, message.raw),
4706
- userText: appendSlackLegacyAttachmentText(stripped, message.raw)
4723
+ rawText: content.text,
4724
+ userText: replaceTopLevelText(content, stripped)
4707
4725
  };
4708
4726
  });
4709
4727
  }
@@ -4844,23 +4862,22 @@ function createSlackTurnRuntime(deps) {
4844
4862
  channelId: deps.getChannelId(thread, message),
4845
4863
  runId: deps.getRunId(thread, message)
4846
4864
  };
4847
- const legacyAttachmentText = renderSlackLegacyAttachmentText(message.raw);
4848
- const messageText = getSlackMessageText(message);
4865
+ const content = parseContent(message);
4849
4866
  const strippedUserText = deps.stripLeadingBotMention(
4850
- stripLeadingSteeringOverride(messageText),
4867
+ stripLeadingSteeringOverride(content.topLevelText),
4851
4868
  {
4852
4869
  stripLeadingSlackMentionToken: Boolean(message.isMention)
4853
4870
  }
4854
4871
  );
4855
4872
  const text = {
4856
- rawText: appendSlackLegacyAttachmentText(messageText, message.raw),
4857
- userText: appendSlackLegacyAttachmentText(strippedUserText, message.raw)
4873
+ rawText: content.text,
4874
+ userText: replaceTopLevelText(content, strippedUserText)
4858
4875
  };
4859
4876
  const decision = await deps.decideSubscribedReply({
4860
4877
  rawText: text.rawText,
4861
4878
  text: text.userText,
4862
4879
  conversationContext,
4863
- hasAttachments: message.attachments.length > 0 || legacyAttachmentText !== "",
4880
+ hasAttachments: content.hasAttachments,
4864
4881
  isExplicitMention: true,
4865
4882
  context
4866
4883
  });
@@ -5184,22 +5201,16 @@ function createSlackTurnRuntime(deps) {
5184
5201
  runId
5185
5202
  });
5186
5203
  await deps.withSpan("chat.turn", "chat.turn", turnContext, async () => {
5187
- const legacyAttachmentText = renderSlackLegacyAttachmentText(
5188
- message.raw
5189
- );
5190
- const messageText = getSlackMessageText(message);
5204
+ const content = parseContent(message);
5191
5205
  const strippedUserText = deps.stripLeadingBotMention(
5192
- stripLeadingSteeringOverride(messageText),
5206
+ stripLeadingSteeringOverride(content.topLevelText),
5193
5207
  {
5194
5208
  stripLeadingSlackMentionToken: Boolean(message.isMention)
5195
5209
  }
5196
5210
  );
5197
5211
  const currentText = {
5198
- rawText: appendSlackLegacyAttachmentText(messageText, message.raw),
5199
- userText: appendSlackLegacyAttachmentText(
5200
- strippedUserText,
5201
- message.raw
5202
- )
5212
+ rawText: content.text,
5213
+ userText: replaceTopLevelText(content, strippedUserText)
5203
5214
  };
5204
5215
  const threadContext = {
5205
5216
  threadId,
@@ -5247,9 +5258,9 @@ function createSlackTurnRuntime(deps) {
5247
5258
  rawText: combinedText.rawText,
5248
5259
  text: combinedText.userText,
5249
5260
  conversationContext: deps.getPreparedConversationContext(preparedState),
5250
- hasAttachments: message.attachments.length > 0 || queuedMessages.some(
5261
+ hasAttachments: content.hasAttachments || queuedMessages.some(
5251
5262
  (queued) => queued.message.attachments.length > 0
5252
- ) || legacyAttachmentText !== "",
5263
+ ),
5253
5264
  isExplicitMention: turnIsExplicitMention,
5254
5265
  context: threadContext
5255
5266
  });
@@ -5945,9 +5956,9 @@ function createJuniorRuntimeServices(overrides = {}) {
5945
5956
  replyExecutor: {
5946
5957
  contextCompactor: overrides.replyExecutor?.contextCompactor ?? contextCompactor,
5947
5958
  agentRunner,
5948
- getAwaitingAgentContinueRequest: overrides.replyExecutor?.getAwaitingAgentContinueRequest ?? getAwaitingAgentContinueRequest,
5959
+ getPausedTurnRequest: overrides.replyExecutor?.getPausedTurnRequest ?? getPausedTurnRequest,
5949
5960
  lookupSlackUser: overrides.replyExecutor?.lookupSlackUser ?? lookupSlackUser,
5950
- scheduleAgentContinue: overrides.replyExecutor?.scheduleAgentContinue ?? scheduleAgentContinue,
5961
+ wakePausedTurn: overrides.replyExecutor?.wakePausedTurn ?? wakePausedTurn,
5951
5962
  scheduleSessionCompletedPluginTasks: overrides.replyExecutor?.scheduleSessionCompletedPluginTasks ?? (async (params) => {
5952
5963
  await scheduleSessionCompletedPluginTasks(params);
5953
5964
  }),
@@ -5964,6 +5975,18 @@ function createJuniorRuntimeServices(overrides = {}) {
5964
5975
  // src/chat/runtime/reply-executor.ts
5965
5976
  import { createSlackSource as createSlackSource2 } from "@sentry/junior-plugin-api";
5966
5977
 
5978
+ // src/chat/slack/message/identity.ts
5979
+ function getMessageTimestamp(message) {
5980
+ const idTs = parseSlackMessageTs(message.id);
5981
+ if (idTs) {
5982
+ return idTs;
5983
+ }
5984
+ if (message.raw && typeof message.raw === "object") {
5985
+ return parseSlackMessageTs(message.raw.ts);
5986
+ }
5987
+ return void 0;
5988
+ }
5989
+
5967
5990
  // src/chat/slack/action-token.ts
5968
5991
  import { z as z6 } from "zod";
5969
5992
  var slackActionTokenSchema = z6.string().trim().min(1).brand();
@@ -6125,7 +6148,7 @@ function queuedInstructionActor(queued) {
6125
6148
  const actor = getMessageActorIdentity(queued.message);
6126
6149
  const authorId = actor?.userId ?? parseActorUserId(queued.message.author.userId);
6127
6150
  const authorName = actor?.fullName ?? actor?.userName;
6128
- const slackTs = getSlackMessageTs(queued.message);
6151
+ const slackTs = getMessageTimestamp(queued.message);
6129
6152
  return {
6130
6153
  ...authorId ? { authorId } : {},
6131
6154
  ...authorName ? { authorName } : {},
@@ -6183,7 +6206,7 @@ async function loadPiMessagesForTurn(args) {
6183
6206
  return {};
6184
6207
  }
6185
6208
  if (args.activeTurnId) {
6186
- const sessionRecord = await getAgentTurnSessionRecord(
6209
+ const sessionRecord = await getTurnRecord(
6187
6210
  args.conversationId,
6188
6211
  args.activeTurnId
6189
6212
  );
@@ -6259,20 +6282,17 @@ function createReplyToThread(deps) {
6259
6282
  modelId: standardModelId(botConfig)
6260
6283
  },
6261
6284
  async () => {
6262
- const messageText = getSlackMessageText(message);
6285
+ const content = parseContent(message);
6263
6286
  const strippedUserText = stripLeadingBotMention(
6264
- stripLeadingSteeringOverride(messageText),
6287
+ stripLeadingSteeringOverride(content.topLevelText),
6265
6288
  {
6266
6289
  botUserId: deps.getSlackAdapter().botUserId,
6267
6290
  stripLeadingSlackMentionToken: options.explicitMention || Boolean(message.isMention)
6268
6291
  }
6269
6292
  );
6270
6293
  const currentText = {
6271
- rawText: appendSlackLegacyAttachmentText(messageText, message.raw),
6272
- userText: appendSlackLegacyAttachmentText(
6273
- strippedUserText,
6274
- message.raw
6275
- )
6294
+ rawText: content.text,
6295
+ userText: replaceTopLevelText(content, strippedUserText)
6276
6296
  };
6277
6297
  await Promise.all(
6278
6298
  (options.queuedMessages ?? []).filter((queued) => !isResourceEventSlackMessage(queued.message)).map(
@@ -6324,7 +6344,7 @@ function createReplyToThread(deps) {
6324
6344
  },
6325
6345
  skipBackfill: options.skipBackfill
6326
6346
  });
6327
- const slackMessageTs = getSlackMessageTs(message);
6347
+ const slackMessageTs = getMessageTimestamp(message);
6328
6348
  const turnId = options.execution?.turnId ?? buildDeterministicTurnId(message.id);
6329
6349
  let beforeFirstResponsePostCalled = false;
6330
6350
  const beforeFirstResponsePost = async () => {
@@ -6383,7 +6403,7 @@ function createReplyToThread(deps) {
6383
6403
  channelId,
6384
6404
  runId,
6385
6405
  conversation: preparedState.conversation,
6386
- messageTs: getSlackMessageTs(queued.message)
6406
+ messageTs: getMessageTimestamp(queued.message)
6387
6407
  }
6388
6408
  )
6389
6409
  };
@@ -6473,20 +6493,20 @@ function createReplyToThread(deps) {
6473
6493
  return;
6474
6494
  }
6475
6495
  if (conversationId && activeTurnId) {
6476
- const resumeRequest = await deps.services.getAwaitingAgentContinueRequest({
6496
+ const pausedTurn = await deps.services.getPausedTurnRequest({
6477
6497
  conversationId,
6478
- sessionId: activeTurnId
6498
+ turnId: activeTurnId
6479
6499
  });
6480
- if (resumeRequest) {
6481
- if (!await appendParkedTurnInput(resumeRequest.sessionId)) {
6500
+ if (pausedTurn) {
6501
+ if (!await appendParkedTurnInput(pausedTurn.turnId)) {
6482
6502
  throw new TurnInputDeferredError();
6483
6503
  }
6484
6504
  try {
6485
- await deps.services.scheduleAgentContinue(resumeRequest);
6505
+ await deps.services.wakePausedTurn(pausedTurn);
6486
6506
  } catch (error) {
6487
6507
  logException(error, "agent.continue.schedule.failed", {
6488
- "app.ai.resume_session_version": resumeRequest.expectedVersion,
6489
- "app.ai.resume_session_id": resumeRequest.sessionId,
6508
+ "app.ai.resume_session_version": pausedTurn.expectedVersion,
6509
+ "app.ai.resume_session_id": pausedTurn.turnId,
6490
6510
  ...messageTs ? { "messaging.message.id": messageTs } : {}
6491
6511
  });
6492
6512
  throw error;
@@ -6498,15 +6518,15 @@ function createReplyToThread(deps) {
6498
6518
  await options.ack?.();
6499
6519
  return;
6500
6520
  }
6501
- const sessionRecord = await getAgentTurnSessionRecord(
6521
+ const sessionRecord = await getTurnRecord(
6502
6522
  conversationId,
6503
6523
  activeTurnId
6504
6524
  );
6505
- if (sessionRecord?.state === "awaiting_resume") {
6525
+ if (sessionRecord?.state === "paused") {
6506
6526
  if (sessionRecord.resumeReason === "auth") {
6507
- await abandonAgentTurnSessionRecord({
6527
+ await abandonTurnRecord({
6508
6528
  conversationId,
6509
- sessionId: activeTurnId,
6529
+ turnId: activeTurnId,
6510
6530
  errorMessage: "Auth-parked session superseded by a new user message"
6511
6531
  });
6512
6532
  markTurnClosed({
@@ -6516,11 +6536,11 @@ function createReplyToThread(deps) {
6516
6536
  });
6517
6537
  activeTurnId = void 0;
6518
6538
  } else {
6519
- await failAgentTurnSessionRecord({
6539
+ await failTurnRecord({
6520
6540
  conversationId,
6521
6541
  expectedVersion: sessionRecord.version,
6522
- sessionId: activeTurnId,
6523
- errorMessage: "Awaiting agent continuation metadata could not be materialized"
6542
+ turnId: activeTurnId,
6543
+ errorMessage: "Awaiting paused-turn metadata could not be materialized"
6524
6544
  });
6525
6545
  markTurnFailed({
6526
6546
  conversation: preparedState.conversation,
@@ -6590,10 +6610,10 @@ function createReplyToThread(deps) {
6590
6610
  }
6591
6611
  if (conversationId) {
6592
6612
  const turnStartedAtMs = message.metadata.dateSent.getTime();
6593
- void recordAgentTurnSessionSummary({
6613
+ void recordTurnSummary({
6594
6614
  channelName,
6595
6615
  conversationId,
6596
- sessionId: turnId,
6616
+ turnId,
6597
6617
  sliceId: 1,
6598
6618
  startedAtMs: turnStartedAtMs,
6599
6619
  state: "running",
@@ -6669,7 +6689,7 @@ function createReplyToThread(deps) {
6669
6689
  if (!dispatchId) {
6670
6690
  return;
6671
6691
  }
6672
- await recordAgentTurnSessionSummary({
6692
+ await recordTurnSummary({
6673
6693
  channelName,
6674
6694
  conversationId,
6675
6695
  destination,
@@ -6677,7 +6697,7 @@ function createReplyToThread(deps) {
6677
6697
  dispatchId,
6678
6698
  dispatchOutcome,
6679
6699
  ...acceptedDeliveryId ? { resultMessageId: acceptedDeliveryId } : {},
6680
- sessionId: turnId,
6700
+ turnId,
6681
6701
  sliceId: 1,
6682
6702
  source,
6683
6703
  startedAtMs: message.metadata.dateSent.getTime(),
@@ -6687,7 +6707,7 @@ function createReplyToThread(deps) {
6687
6707
  };
6688
6708
  let latestArtifacts = preparedState.artifacts;
6689
6709
  let assistantTitleArtifacts = {};
6690
- let agentContinueScheduleError;
6710
+ let turnWakeError;
6691
6711
  let boundaryFailureCode = "agent_run_failed";
6692
6712
  let finalizedFailureEventId;
6693
6713
  let terminalDispatchFailureOutcome;
@@ -6789,14 +6809,14 @@ function createReplyToThread(deps) {
6789
6809
  if (slackTs && options.execution?.dispatch?.id) {
6790
6810
  try {
6791
6811
  await persistWithRetry(
6792
- () => recordAgentTurnSessionSummary({
6812
+ () => recordTurnSummary({
6793
6813
  channelName,
6794
6814
  conversationId,
6795
6815
  destination,
6796
6816
  destinationVisibility,
6797
6817
  dispatchId: options.execution?.dispatch?.id,
6798
6818
  resultMessageId: slackTs,
6799
- sessionId: turnId,
6819
+ turnId,
6800
6820
  sliceId: 1,
6801
6821
  source,
6802
6822
  startedAtMs: message.metadata.dateSent.getTime(),
@@ -7063,14 +7083,14 @@ function createReplyToThread(deps) {
7063
7083
  }
7064
7084
  if (!destination || !conversationId) {
7065
7085
  throw new Error(
7066
- "Agent continuation requires a destination and conversation id"
7086
+ "Paused turn requires a destination and conversation id"
7067
7087
  );
7068
7088
  }
7069
7089
  try {
7070
- await deps.services.scheduleAgentContinue({
7090
+ await deps.services.wakePausedTurn({
7071
7091
  conversationId,
7072
7092
  destination,
7073
- sessionId: turnId,
7093
+ turnId,
7074
7094
  expectedVersion: outcome.resumeVersion
7075
7095
  });
7076
7096
  shouldPersistFailureState = false;
@@ -7080,7 +7100,7 @@ function createReplyToThread(deps) {
7080
7100
  "app.ai.resume_session_version": outcome.resumeVersion
7081
7101
  });
7082
7102
  shouldPersistFailureState = true;
7083
- agentContinueScheduleError = scheduleError;
7103
+ turnWakeError = scheduleError;
7084
7104
  throw scheduleError;
7085
7105
  }
7086
7106
  return;
@@ -7122,33 +7142,32 @@ function createReplyToThread(deps) {
7122
7142
  }
7123
7143
  try {
7124
7144
  if (conversationId && reply.piMessages?.length) {
7125
- await completeDeliveredTurn({
7145
+ await saveTurnCheckpoint({
7146
+ mode: "completed",
7126
7147
  channelName,
7127
7148
  conversationId,
7149
+ turnId,
7128
7150
  durationMs: reply.diagnostics.durationMs,
7129
7151
  usage: reply.diagnostics.usage,
7130
- reasoningLevel: reply.diagnostics.reasoningLevel,
7131
7152
  destination,
7132
7153
  destinationVisibility,
7133
7154
  source,
7134
- sessionId: turnId,
7135
7155
  sliceId: 1,
7136
7156
  dispatchOutcome: reply.diagnostics.outcome === "success" ? "completed" : "failed",
7137
7157
  ...options.execution?.dispatch && turnResult.errorMessage ? { errorMessage: turnResult.errorMessage } : {},
7138
7158
  ...acceptedDeliveryId ? { resultMessageId: acceptedDeliveryId } : {},
7139
7159
  messages: reply.piMessages,
7140
- modelId: reply.diagnostics.modelId,
7141
7160
  actor: executionActor,
7142
7161
  surface: options.execution?.surface ?? "slack",
7143
7162
  dispatchId: options.execution?.dispatch?.id
7144
7163
  });
7145
7164
  } else if (conversationId) {
7146
- await recordAgentTurnSessionSummary({
7165
+ await recordTurnSummary({
7147
7166
  channelName,
7148
7167
  conversationId,
7149
7168
  cumulativeDurationMs: reply.diagnostics.durationMs,
7150
7169
  cumulativeUsage: reply.diagnostics.usage,
7151
- sessionId: turnId,
7170
+ turnId,
7152
7171
  sliceId: 1,
7153
7172
  startedAtMs: message.metadata.dateSent.getTime(),
7154
7173
  state: "completed",
@@ -7262,7 +7281,7 @@ function createReplyToThread(deps) {
7262
7281
  shouldPersistFailureState = false;
7263
7282
  throw error;
7264
7283
  }
7265
- if (error === agentContinueScheduleError) {
7284
+ if (error === turnWakeError) {
7266
7285
  shouldPersistFailureState = true;
7267
7286
  }
7268
7287
  shouldPersistFailureState = true;
@@ -7285,15 +7304,12 @@ function createReplyToThread(deps) {
7285
7304
  const recoveryText = buildCanvasRecoveryReply(createdCanvasUrl);
7286
7305
  await deliverAssistantMessage(recoveryText, dispatchOutcome);
7287
7306
  if (conversationId) {
7288
- const sessionRecord = await getAgentTurnSessionRecord(
7289
- conversationId,
7290
- turnId
7291
- );
7307
+ const sessionRecord = await getTurnRecord(conversationId, turnId);
7292
7308
  if (sessionRecord) {
7293
- await failAgentTurnSessionRecord({
7309
+ await failTurnRecord({
7294
7310
  conversationId,
7295
7311
  expectedVersion: sessionRecord.version,
7296
- sessionId: turnId,
7312
+ turnId,
7297
7313
  errorMessage
7298
7314
  });
7299
7315
  }
@@ -7343,10 +7359,10 @@ function createReplyToThread(deps) {
7343
7359
  });
7344
7360
  if (conversationId) {
7345
7361
  try {
7346
- await recordAgentTurnSessionSummary({
7362
+ await recordTurnSummary({
7347
7363
  channelName,
7348
7364
  conversationId,
7349
- sessionId: turnId,
7365
+ turnId,
7350
7366
  sliceId: 1,
7351
7367
  startedAtMs: message.metadata.dateSent.getTime(),
7352
7368
  state: "failed",
@@ -7361,15 +7377,15 @@ function createReplyToThread(deps) {
7361
7377
  } : {},
7362
7378
  traceId: getActiveTraceId()
7363
7379
  });
7364
- const sessionRecord = await getAgentTurnSessionRecord(
7380
+ const sessionRecord = await getTurnRecord(
7365
7381
  conversationId,
7366
7382
  turnId
7367
7383
  );
7368
7384
  if (sessionRecord) {
7369
- await failAgentTurnSessionRecord({
7385
+ await failTurnRecord({
7370
7386
  conversationId,
7371
7387
  expectedVersion: sessionRecord.version,
7372
- sessionId: turnId,
7388
+ turnId,
7373
7389
  errorMessage: "Agent turn failed before assistant output handling completed"
7374
7390
  });
7375
7391
  }
@@ -7441,7 +7457,7 @@ function resolveMessageText(args) {
7441
7457
  }
7442
7458
  function toConversationMessage(args) {
7443
7459
  const actor = getMessageActorIdentity(args.entry);
7444
- const slackTs = getSlackMessageTs(args.entry);
7460
+ const slackTs = getMessageTimestamp(args.entry);
7445
7461
  const messageHasPotentialImageAttachment = hasPotentialImageAttachment(
7446
7462
  args.entry.attachments
7447
7463
  );
@@ -7476,9 +7492,7 @@ function hasPendingImageHydration(conversation) {
7476
7492
  );
7477
7493
  }
7478
7494
  function getBackfillText(entry) {
7479
- const text = normalizeConversationText(
7480
- appendSlackLegacyAttachmentText(getSlackMessageText(entry), entry.raw)
7481
- );
7495
+ const text = normalizeConversationText(parseContent(entry).text);
7482
7496
  return text || void 0;
7483
7497
  }
7484
7498
  async function seedConversationBackfill(thread, conversation, currentTurn) {
@@ -7871,39 +7885,33 @@ function createJuniorSlackAdapter(config) {
7871
7885
  return createSlackAdapter(config);
7872
7886
  }
7873
7887
 
7874
- // src/chat/runtime/agent-continue-runner.ts
7875
- var AGENT_CONTINUE_LOCK_RETRY_DELAYS_MS = [250, 1e3, 2e3];
7888
+ // src/chat/task-execution/paused-turn.ts
7876
7889
  async function persistCompletedReplyState(args) {
7877
- const currentState = await getPersistedThreadState(
7878
- args.sessionRecord.conversationId
7879
- );
7890
+ const currentState = await getPersistedThreadState(args.turn.conversationId);
7880
7891
  const conversation = coerceThreadConversationState(currentState);
7881
7892
  await hydrateConversationMessages({
7882
7893
  conversation,
7883
- conversationId: args.sessionRecord.conversationId
7894
+ conversationId: args.turn.conversationId
7884
7895
  });
7885
7896
  const artifacts = coerceThreadArtifactsState(currentState);
7886
- const userMessage = getTurnUserMessage(
7887
- conversation,
7888
- args.sessionRecord.sessionId
7889
- );
7897
+ const userMessage = getTurnUserMessage(conversation, args.turn.turnId);
7890
7898
  const statePatch = buildDeliveredTurnStatePatch({
7891
7899
  artifacts,
7892
7900
  conversation,
7893
7901
  reply: args.reply,
7894
- sessionId: args.sessionRecord.sessionId,
7902
+ sessionId: args.turn.turnId,
7895
7903
  userMessageId: userMessage?.id
7896
7904
  });
7897
- await persistThreadStateById(args.sessionRecord.conversationId, {
7905
+ await persistThreadStateById(args.turn.conversationId, {
7898
7906
  ...statePatch
7899
7907
  });
7900
7908
  }
7901
- async function failSessionRecordBestEffort(args) {
7909
+ async function failTurnBestEffort(args) {
7902
7910
  try {
7903
- await failAgentTurnSessionRecord({
7904
- conversationId: args.sessionRecord.conversationId,
7905
- expectedVersion: args.sessionRecord.version,
7906
- sessionId: args.sessionRecord.sessionId,
7911
+ await failTurnRecord({
7912
+ conversationId: args.turn.conversationId,
7913
+ expectedVersion: args.turn.version,
7914
+ turnId: args.turn.turnId,
7907
7915
  errorMessage: args.errorMessage
7908
7916
  });
7909
7917
  } catch (error) {
@@ -7911,51 +7919,49 @@ async function failSessionRecordBestEffort(args) {
7911
7919
  error,
7912
7920
  "agent.continue.session_record.failure_persistence.failed",
7913
7921
  {
7914
- "app.ai.conversation_id": args.sessionRecord.conversationId,
7915
- "app.ai.session_id": args.sessionRecord.sessionId
7922
+ "app.ai.conversation_id": args.turn.conversationId,
7923
+ "app.ai.session_id": args.turn.turnId
7916
7924
  }
7917
7925
  );
7918
7926
  }
7919
7927
  }
7920
- async function persistFailedReplyState(sessionRecord, errorMessage = "Paused agent run failed while continuing") {
7921
- const currentState = await getPersistedThreadState(
7922
- sessionRecord.conversationId
7923
- );
7928
+ async function persistFailedReplyState(turn, errorMessage = "Paused agent run failed while continuing") {
7929
+ const currentState = await getPersistedThreadState(turn.conversationId);
7924
7930
  const conversation = coerceThreadConversationState(currentState);
7925
7931
  await hydrateConversationMessages({
7926
7932
  conversation,
7927
- conversationId: sessionRecord.conversationId
7933
+ conversationId: turn.conversationId
7928
7934
  });
7929
- clearPendingAuth(conversation, sessionRecord.sessionId);
7935
+ clearPendingAuth(conversation, turn.turnId);
7930
7936
  markTurnFailed({
7931
7937
  conversation,
7932
7938
  nowMs: Date.now(),
7933
- sessionId: sessionRecord.sessionId,
7934
- userMessageId: getTurnUserMessage(conversation, sessionRecord.sessionId)?.id,
7939
+ sessionId: turn.turnId,
7940
+ userMessageId: getTurnUserMessage(conversation, turn.turnId)?.id,
7935
7941
  markConversationMessage
7936
7942
  });
7937
- await failSessionRecordBestEffort({
7938
- sessionRecord,
7943
+ await failTurnBestEffort({
7944
+ turn,
7939
7945
  errorMessage
7940
7946
  });
7941
- await persistThreadStateById(sessionRecord.conversationId, {
7947
+ await persistThreadStateById(turn.conversationId, {
7942
7948
  conversation
7943
7949
  });
7944
7950
  }
7945
- async function failContinuationStartup(args) {
7951
+ async function failPausedTurnStart(args) {
7946
7952
  try {
7947
- await persistFailedReplyState(args.sessionRecord, args.errorMessage);
7953
+ await persistFailedReplyState(args.turn, args.errorMessage);
7948
7954
  } catch (persistError) {
7949
- await failSessionRecordBestEffort({
7950
- sessionRecord: args.sessionRecord,
7951
- errorMessage: "Paused agent run failed while preparing continuation"
7955
+ await failTurnBestEffort({
7956
+ turn: args.turn,
7957
+ errorMessage: "Paused turn failed before it could run"
7952
7958
  });
7953
7959
  logException(
7954
7960
  persistError,
7955
7961
  "agent.continue.startup_failure_persist.failed",
7956
7962
  {
7957
- "app.ai.conversation_id": args.sessionRecord.conversationId,
7958
- "app.ai.session_id": args.sessionRecord.sessionId
7963
+ "app.ai.conversation_id": args.turn.conversationId,
7964
+ "app.ai.session_id": args.turn.turnId
7959
7965
  }
7960
7966
  );
7961
7967
  }
@@ -8012,74 +8018,78 @@ async function resolveResumeExecutionIdentity(args) {
8012
8018
  credentialContext: credentialContextForActor(actor)
8013
8019
  };
8014
8020
  }
8015
- function isContinuationResume(summary) {
8016
- return summary.state === "awaiting_resume" && (summary.resumeReason === "timeout" || summary.resumeReason === "yield" || summary.resumeReason === "retry");
8021
+ function isPausedTurn(summary) {
8022
+ return summary.state === "paused" && (summary.resumeReason === "timeout" || summary.resumeReason === "yield" || summary.resumeReason === "retry");
8017
8023
  }
8018
- async function failUnresumableContinuation(args) {
8019
- await failAgentTurnSessionRecord({
8024
+ async function failPausedTurn(args) {
8025
+ const failed = await failTurnRecord({
8020
8026
  conversationId: args.conversationId,
8021
8027
  expectedVersion: args.expectedVersion ?? args.summary.version,
8022
- sessionId: args.summary.sessionId,
8028
+ turnId: args.summary.turnId,
8023
8029
  errorMessage: args.errorMessage
8024
8030
  });
8025
- if (args.summary.dispatchId) {
8031
+ if (!failed) return;
8032
+ if (failed.dispatchId) {
8026
8033
  try {
8027
8034
  const routing = await resolveTurnSessionRouting({
8028
8035
  conversationId: args.conversationId
8029
8036
  });
8030
- await recordAgentTurnSessionSummary({
8037
+ await recordTurnSummary({
8031
8038
  conversationId: args.conversationId,
8032
8039
  destination: routing.destination,
8033
- dispatchId: args.summary.dispatchId,
8040
+ dispatchId: failed.dispatchId,
8034
8041
  dispatchOutcome: "failed",
8035
- sessionId: args.summary.sessionId,
8036
- sliceId: args.summary.sliceId,
8042
+ turnId: failed.turnId,
8043
+ sliceId: failed.sliceId,
8037
8044
  source: routing.source,
8038
8045
  state: "failed",
8039
- surface: args.summary.surface
8046
+ surface: failed.surface
8040
8047
  });
8041
8048
  } catch (error) {
8042
8049
  logException(error, "agent.continue.dispatch_failure_summary.failed", {
8043
8050
  "app.ai.conversation_id": args.conversationId,
8044
- "app.ai.session_id": args.summary.sessionId
8051
+ "app.ai.session_id": args.summary.turnId
8045
8052
  });
8046
8053
  }
8047
8054
  }
8048
8055
  }
8049
- async function continueSlackAgentRun(payload, options, runOptions = {}) {
8056
+ async function runPausedTurn(payload, options, runOptions = {}) {
8050
8057
  return withLogContext(
8051
8058
  { conversationId: payload.conversationId },
8052
- () => continueSlackAgentRunInContext(payload, options, runOptions)
8059
+ () => runPausedTurnInContext(payload, options, runOptions)
8053
8060
  );
8054
8061
  }
8055
- async function continueSlackAgentRunInContext(payload, options, runOptions) {
8062
+ async function runPausedTurnInContext(payload, options, runOptions) {
8056
8063
  const thread = parseSlackThreadId(payload.conversationId);
8057
8064
  const destination = requireSlackDestination(
8058
8065
  payload.destination,
8059
- "Agent continuation"
8066
+ "Paused turn"
8060
8067
  );
8061
- const scheduleAgentContinue2 = options.scheduleAgentContinue ?? scheduleAgentContinue;
8068
+ const wakePausedTurn2 = options.wakePausedTurn ?? wakePausedTurn;
8062
8069
  const resumeTurn = options.resumeTurn ?? resumeSlackTurn;
8063
8070
  return await resumeTurn({
8064
8071
  messageText: "",
8065
8072
  conversationId: payload.conversationId,
8066
- turnId: payload.sessionId,
8073
+ turnId: payload.turnId,
8067
8074
  channelId: thread?.channelId ?? destination.channelId,
8068
8075
  ...thread?.threadTs ? { threadTs: thread.threadTs } : {},
8069
8076
  lockKey: payload.conversationId,
8077
+ // Queue continue runs under the conversation work lease already.
8078
+ ownsConversationLease: true,
8070
8079
  agentRunner: options.agentRunner,
8071
8080
  scheduleSessionCompletedPluginTasks: options.scheduleSessionCompletedPluginTasks,
8072
8081
  beforeStart: async () => {
8073
- let sessionRecord;
8082
+ let turn;
8074
8083
  try {
8075
- sessionRecord = await getAgentTurnSessionRecord(
8076
- payload.conversationId,
8077
- payload.sessionId
8078
- );
8079
- if (!sessionRecord || sessionRecord.state !== "awaiting_resume" || sessionRecord.resumeReason !== "timeout" && sessionRecord.resumeReason !== "yield" && sessionRecord.resumeReason !== "retry" || sessionRecord.version !== payload.expectedVersion) {
8084
+ const checkpoint = await loadTurnCheckpoint({
8085
+ conversationId: payload.conversationId,
8086
+ turnId: payload.turnId
8087
+ });
8088
+ turn = checkpoint.record;
8089
+ if (!turn || turn.state !== "paused" || turn.resumeReason !== "timeout" && turn.resumeReason !== "yield" && turn.resumeReason !== "retry" || turn.version !== payload.expectedVersion) {
8080
8090
  return false;
8081
8091
  }
8082
- const activeSessionRecord = sessionRecord;
8092
+ const activeTurn = turn;
8083
8093
  const currentState = await getPersistedThreadState(
8084
8094
  payload.conversationId
8085
8095
  );
@@ -8089,20 +8099,20 @@ async function continueSlackAgentRunInContext(payload, options, runOptions) {
8089
8099
  conversationId: payload.conversationId
8090
8100
  });
8091
8101
  const artifacts = coerceThreadArtifactsState(currentState);
8092
- const dispatchId = activeSessionRecord.dispatchId ?? options.routingContext?.dispatch?.id;
8102
+ const dispatchId = activeTurn.dispatchId ?? options.routingContext?.dispatch?.id;
8093
8103
  const dispatchUserMessage = dispatchId ? conversation.messages.find(
8094
8104
  (message) => message.role === "user" && options.inputMessageIds?.includes(message.id)
8095
8105
  ) : void 0;
8096
- const userMessage = getTurnUserMessage(conversation, payload.sessionId) ?? dispatchUserMessage;
8106
+ const userMessage = getTurnUserMessage(conversation, payload.turnId) ?? dispatchUserMessage;
8097
8107
  if (!userMessage) {
8098
- await failStrandedSessionWithFallback({
8108
+ await failStrandedTurnWithFallback({
8099
8109
  conversationId: payload.conversationId,
8100
- errorMessage: `Unable to locate the persisted user message for agent continuation session "${payload.sessionId}"`,
8101
- sessionRecord: activeSessionRecord
8110
+ errorMessage: `Unable to locate the persisted user message for paused turn "${payload.turnId}"`,
8111
+ turn: activeTurn
8102
8112
  });
8103
8113
  return false;
8104
8114
  }
8105
- if (conversation.processing.activeTurnId !== payload.sessionId) {
8115
+ if (conversation.processing.activeTurnId !== payload.turnId) {
8106
8116
  return false;
8107
8117
  }
8108
8118
  const identity = await resolveResumeExecutionIdentity({
@@ -8112,10 +8122,10 @@ async function continueSlackAgentRunInContext(payload, options, runOptions) {
8112
8122
  userMessage
8113
8123
  });
8114
8124
  if (!identity) {
8115
- await failStrandedSessionWithFallback({
8125
+ await failStrandedTurnWithFallback({
8116
8126
  conversationId: payload.conversationId,
8117
- errorMessage: "Unable to rebuild Slack actor for continuation",
8118
- sessionRecord: activeSessionRecord
8127
+ errorMessage: "Unable to rebuild the Slack actor for the paused turn",
8128
+ turn: activeTurn
8119
8129
  });
8120
8130
  return false;
8121
8131
  }
@@ -8132,22 +8142,20 @@ async function continueSlackAgentRunInContext(payload, options, runOptions) {
8132
8142
  });
8133
8143
  const source = routing.source;
8134
8144
  const routingDestination = routing.destination;
8135
- const turnMessages = activeSessionRecord.turnStartMessageIndex === void 0 ? [] : activeSessionRecord.piMessages.slice(
8136
- activeSessionRecord.turnStartMessageIndex
8137
- );
8145
+ const turnMessages = activeTurn.turnStartMessageIndex === void 0 ? [] : activeTurn.piMessages.slice(activeTurn.turnStartMessageIndex);
8138
8146
  const recordDispatchOutcome = async (dispatchOutcome) => {
8139
8147
  const dispatchId2 = options.routingContext?.dispatch?.id;
8140
8148
  if (!dispatchId2) {
8141
8149
  return;
8142
8150
  }
8143
- await recordAgentTurnSessionSummary({
8151
+ await recordTurnSummary({
8144
8152
  conversationId: payload.conversationId,
8145
8153
  destination: routingDestination,
8146
8154
  destinationVisibility: options.routingContext?.destinationVisibility,
8147
8155
  dispatchId: dispatchId2,
8148
8156
  dispatchOutcome,
8149
- sessionId: payload.sessionId,
8150
- sliceId: activeSessionRecord.sliceId,
8157
+ turnId: payload.turnId,
8158
+ sliceId: activeTurn.sliceId,
8151
8159
  source,
8152
8160
  state: "failed",
8153
8161
  surface: options.routingContext?.surface ?? "slack"
@@ -8155,7 +8163,7 @@ async function continueSlackAgentRunInContext(payload, options, runOptions) {
8155
8163
  };
8156
8164
  return {
8157
8165
  messageText: userMessage.text,
8158
- sliceId: activeSessionRecord.sliceId,
8166
+ sliceId: activeTurn.sliceId,
8159
8167
  messageTs: getTurnUserSlackMessageTs(userMessage),
8160
8168
  inputMessageIds: [userMessage.id],
8161
8169
  initialStatus: latestReportedProgress(turnMessages),
@@ -8166,7 +8174,7 @@ async function continueSlackAgentRunInContext(payload, options, runOptions) {
8166
8174
  // exact dispatch session so unrelated conversation input cannot
8167
8175
  // gain system authority. Interactive turns retain their merged
8168
8176
  // projection so queued steering remains visible.
8169
- piMessages: dispatchId ? activeSessionRecord.piMessages : await loadProjection({
8177
+ piMessages: dispatchId ? activeTurn.piMessages : await loadProjection({
8170
8178
  conversationId: payload.conversationId
8171
8179
  }),
8172
8180
  ...getTurnUserReplyAttachmentContext(userMessage)
@@ -8199,51 +8207,51 @@ async function continueSlackAgentRunInContext(payload, options, runOptions) {
8199
8207
  },
8200
8208
  commitResult: async (reply) => {
8201
8209
  await persistCompletedReplyState({
8202
- sessionRecord: activeSessionRecord,
8210
+ turn: activeTurn,
8203
8211
  reply
8204
8212
  });
8205
8213
  },
8206
8214
  onFailure: async (error) => {
8207
8215
  await persistFailedReplyState(
8208
- activeSessionRecord,
8216
+ activeTurn,
8209
8217
  error instanceof Error ? error.message : String(error)
8210
8218
  );
8211
8219
  await recordDispatchOutcome("failed");
8212
8220
  },
8213
8221
  onPostDeliveryCommitFailure: async () => {
8214
- await failAgentTurnSessionRecord({
8215
- conversationId: activeSessionRecord.conversationId,
8216
- expectedVersion: activeSessionRecord.version,
8217
- sessionId: activeSessionRecord.sessionId,
8222
+ await failTurnRecord({
8223
+ conversationId: activeTurn.conversationId,
8224
+ expectedVersion: activeTurn.version,
8225
+ turnId: activeTurn.turnId,
8218
8226
  errorMessage: "Continued agent reply was delivered but completion state did not persist"
8219
8227
  });
8220
8228
  await recordDispatchOutcome("failed");
8221
8229
  },
8222
8230
  onAuthPause: async () => {
8223
8231
  await persistAuthPauseTurnState({
8224
- sessionId: payload.sessionId,
8232
+ sessionId: payload.turnId,
8225
8233
  threadStateId: payload.conversationId
8226
8234
  });
8227
8235
  await recordDispatchOutcome("blocked");
8228
8236
  logWarn("agent.continue.reparked_for_auth", {
8229
8237
  "app.ai.conversation_id": payload.conversationId,
8230
- "app.ai.session_id": payload.sessionId
8238
+ "app.ai.session_id": payload.turnId
8231
8239
  });
8232
8240
  },
8233
8241
  onSuspend: async (resumeVersion) => {
8234
- await scheduleAgentContinue2({
8242
+ await wakePausedTurn2({
8235
8243
  conversationId: payload.conversationId,
8236
8244
  destination: payload.destination,
8237
- sessionId: payload.sessionId,
8245
+ turnId: payload.turnId,
8238
8246
  expectedVersion: resumeVersion
8239
8247
  });
8240
8248
  }
8241
8249
  };
8242
8250
  } catch (error) {
8243
- if (sessionRecord) {
8244
- await failContinuationStartup({
8251
+ if (turn) {
8252
+ await failPausedTurnStart({
8245
8253
  errorMessage: error instanceof Error ? error.message : String(error),
8246
- sessionRecord
8254
+ turn
8247
8255
  });
8248
8256
  }
8249
8257
  throw error;
@@ -8251,13 +8259,14 @@ async function continueSlackAgentRunInContext(payload, options, runOptions) {
8251
8259
  }
8252
8260
  });
8253
8261
  }
8254
- async function failStrandedSessionWithFallback(args) {
8255
- await failAgentTurnSessionRecord({
8262
+ async function failStrandedTurnWithFallback(args) {
8263
+ const failed = await failTurnRecord({
8256
8264
  conversationId: args.conversationId,
8257
- expectedVersion: args.sessionRecord.version,
8258
- sessionId: args.sessionRecord.sessionId,
8265
+ expectedVersion: args.turn.version,
8266
+ turnId: args.turn.turnId,
8259
8267
  errorMessage: args.errorMessage
8260
8268
  });
8269
+ if (!failed) return false;
8261
8270
  const currentState = await getPersistedThreadState(args.conversationId);
8262
8271
  const conversation = coerceThreadConversationState(currentState);
8263
8272
  await hydrateConversationMessages({
@@ -8267,18 +8276,15 @@ async function failStrandedSessionWithFallback(args) {
8267
8276
  markTurnFailed({
8268
8277
  conversation,
8269
8278
  nowMs: Date.now(),
8270
- sessionId: args.sessionRecord.sessionId,
8271
- userMessageId: getTurnUserMessage(
8272
- conversation,
8273
- args.sessionRecord.sessionId
8274
- )?.id,
8279
+ sessionId: failed.turnId,
8280
+ userMessageId: getTurnUserMessage(conversation, failed.turnId)?.id,
8275
8281
  markConversationMessage
8276
8282
  });
8277
8283
  await persistThreadStateById(args.conversationId, { conversation });
8278
8284
  const eventName = "agent.turn.stranded_session.failed";
8279
8285
  const eventId = logException(new Error(args.errorMessage), eventName, {
8280
8286
  "app.ai.conversation_id": args.conversationId,
8281
- "app.ai.session_id": args.sessionRecord.sessionId
8287
+ "app.ai.session_id": failed.turnId
8282
8288
  });
8283
8289
  let routing;
8284
8290
  try {
@@ -8288,25 +8294,25 @@ async function failStrandedSessionWithFallback(args) {
8288
8294
  } catch (error) {
8289
8295
  logException(error, "agent.turn.stranded_session.routing_unavailable", {
8290
8296
  "app.ai.conversation_id": args.conversationId,
8291
- "app.ai.session_id": args.sessionRecord.sessionId
8297
+ "app.ai.session_id": failed.turnId
8292
8298
  });
8293
- return;
8299
+ return true;
8294
8300
  }
8295
- if (args.sessionRecord.dispatchId) {
8296
- await recordAgentTurnSessionSummary({
8301
+ if (failed.dispatchId) {
8302
+ await recordTurnSummary({
8297
8303
  conversationId: args.conversationId,
8298
8304
  destination: routing.destination,
8299
- dispatchId: args.sessionRecord.dispatchId,
8305
+ dispatchId: failed.dispatchId,
8300
8306
  dispatchOutcome: "failed",
8301
- sessionId: args.sessionRecord.sessionId,
8302
- sliceId: args.sessionRecord.sliceId,
8307
+ turnId: failed.turnId,
8308
+ sliceId: failed.sliceId,
8303
8309
  source: routing.source,
8304
8310
  state: "failed",
8305
- surface: args.sessionRecord.surface
8311
+ surface: failed.surface
8306
8312
  });
8307
8313
  }
8308
8314
  const thread = parseSlackThreadId(args.conversationId);
8309
- const channelId = thread?.channelId ?? requireSlackDestination(routing.destination, "Stranded agent continuation").channelId;
8315
+ const channelId = thread?.channelId ?? requireSlackDestination(routing.destination, "Stranded paused turn").channelId;
8310
8316
  await postSlackMessage({
8311
8317
  channelId,
8312
8318
  ...thread?.threadTs ? { threadTs: thread.threadTs } : {},
@@ -8314,173 +8320,154 @@ async function failStrandedSessionWithFallback(args) {
8314
8320
  requireTurnFailureEventId(eventId, eventName)
8315
8321
  )
8316
8322
  });
8323
+ return true;
8317
8324
  }
8318
- async function recoverStrandedRunningSession(args) {
8319
- const stateAdapter = getStateAdapter();
8320
- await stateAdapter.connect();
8321
- const probe = await acquireActiveLock(stateAdapter, args.conversationId);
8322
- if (!probe) {
8323
- return false;
8324
- }
8325
- await stateAdapter.releaseLock(probe);
8326
- const sessionRecord = await getAgentTurnSessionRecordForResume(
8327
- args.conversationId,
8328
- args.summary.sessionId
8329
- );
8330
- if (!sessionRecord || sessionRecord.state !== "running") {
8331
- return false;
8332
- }
8333
- const recoveryProjection = await loadConversationProjection({
8334
- conversationId: args.conversationId
8335
- });
8336
- const modelProfile = recoveryProjection.modelProfile;
8337
- const modelId = modelIdForProfile(botConfig, modelProfile);
8338
- let routing;
8339
- try {
8340
- routing = await resolveTurnSessionRouting({
8341
- conversationId: args.conversationId
8342
- });
8343
- } catch (error) {
8344
- await failStrandedSessionWithFallback({
8345
- conversationId: args.conversationId,
8346
- errorMessage: error instanceof Error ? error.message : String(error),
8347
- sessionRecord
8348
- });
8349
- return false;
8350
- }
8351
- const parked = await persistYieldSessionRecord({
8352
- channelName: sessionRecord.channelName,
8353
- conversationId: args.conversationId,
8354
- sessionId: sessionRecord.sessionId,
8355
- currentSliceId: sessionRecord.sliceId,
8356
- destination: routing.destination,
8357
- source: routing.source,
8358
- messages: sessionRecord.piMessages,
8359
- errorMessage: "Recovered running session after hard worker death",
8360
- modelId,
8361
- surface: sessionRecord.surface
8362
- });
8363
- if (!parked) {
8364
- await failStrandedSessionWithFallback({
8365
- conversationId: args.conversationId,
8366
- errorMessage: "Stranded running session had no resumable boundary after worker death",
8367
- sessionRecord
8368
- });
8369
- return false;
8370
- }
8371
- const request = await getAwaitingAgentContinueRequest({
8372
- conversationId: args.conversationId,
8373
- sessionId: sessionRecord.sessionId
8374
- });
8375
- if (!request) {
8376
- await failStrandedSessionWithFallback({
8377
- conversationId: args.conversationId,
8378
- errorMessage: "Stranded running session could not materialize continuation metadata",
8379
- sessionRecord: parked
8380
- });
8381
- return false;
8382
- }
8383
- if (await continueSlackAgentRunWithLockRetry(
8384
- request,
8385
- args.options,
8386
- args.runOptions
8387
- )) {
8388
- return true;
8389
- }
8390
- await failUnresumableContinuation({
8391
- conversationId: args.conversationId,
8392
- expectedVersion: request.expectedVersion,
8393
- summary: args.summary,
8394
- errorMessage: "Awaiting agent continuation was stale before it could run"
8395
- });
8396
- return false;
8397
- }
8398
- async function resumeAwaitingSlackContinuation(conversationId, options, runOptions = {}) {
8325
+ async function runNextPausedTurn(conversationId, options, runOptions = {}) {
8399
8326
  return withLogContext(
8400
8327
  { conversationId },
8401
- () => resumeAwaitingSlackContinuationInContext(
8402
- conversationId,
8403
- options,
8404
- runOptions
8405
- )
8328
+ () => runNextPausedTurnInContext(conversationId, options, runOptions)
8406
8329
  );
8407
8330
  }
8408
- async function resumeAwaitingSlackContinuationInContext(conversationId, options, runOptions) {
8409
- const summaries = await listAgentTurnSessionSummariesForConversation(conversationId);
8331
+ async function runNextPausedTurnInContext(conversationId, options, runOptions) {
8332
+ const summaries = await listTurnSummaries(conversationId);
8333
+ const persisted = await getPersistedThreadState(conversationId);
8334
+ const conversation = coerceThreadConversationState(persisted);
8335
+ await hydrateConversationMessages({ conversation, conversationId });
8410
8336
  const newest = summaries[0];
8411
- if (newest?.state === "running") {
8412
- return await recoverStrandedRunningSession({
8413
- conversationId,
8414
- options,
8415
- runOptions,
8416
- summary: newest
8337
+ const running = newest ? await getTurnRecord(conversationId, newest.turnId) : void 0;
8338
+ if (running?.state === "running") {
8339
+ if (turnHasReply(conversation, running.turnId)) {
8340
+ const acceptedReply = [...conversation.messages].reverse().find(
8341
+ (message) => message.role === "assistant" && message.id.startsWith(`${running.turnId}:assistant:`)
8342
+ );
8343
+ const completed = await completeTurnRecord({
8344
+ conversationId,
8345
+ expectedVersion: running.version,
8346
+ resultMessageId: acceptedReply?.meta?.slackTs,
8347
+ turnId: running.turnId
8348
+ });
8349
+ if (!completed) return false;
8350
+ markTurnCompleted({
8351
+ conversation,
8352
+ nowMs: Date.now(),
8353
+ sessionId: running.turnId
8354
+ });
8355
+ await persistThreadStateById(conversationId, { conversation });
8356
+ return false;
8357
+ }
8358
+ const state = getStateAdapter();
8359
+ await state.connect();
8360
+ await withActiveLock(state, conversationId, async () => {
8361
+ const record = await getTurnRecordForResume(
8362
+ conversationId,
8363
+ running.turnId
8364
+ );
8365
+ if (!record || record.state !== "running") return;
8366
+ await failStrandedTurnWithFallback({
8367
+ conversationId,
8368
+ errorMessage: "Turn lost its worker before reaching a safe boundary",
8369
+ turn: record
8370
+ });
8417
8371
  });
8372
+ return false;
8418
8373
  }
8419
8374
  for (const summary of summaries) {
8420
- if (!isContinuationResume(summary)) {
8375
+ if (!isPausedTurn(summary)) {
8421
8376
  continue;
8422
8377
  }
8423
- const request = await getAwaitingAgentContinueRequest({
8378
+ const request = await getPausedTurnRequest({
8424
8379
  conversationId,
8425
- sessionId: summary.sessionId
8380
+ turnId: summary.turnId
8426
8381
  });
8427
8382
  if (!request) {
8428
- await failUnresumableContinuation({
8383
+ await failPausedTurn({
8429
8384
  conversationId,
8430
8385
  summary,
8431
- errorMessage: "Awaiting agent continuation metadata could not be materialized"
8386
+ errorMessage: "Awaiting paused-turn metadata could not be materialized"
8432
8387
  });
8433
8388
  continue;
8434
8389
  }
8435
- if (await continueSlackAgentRunWithLockRetry(request, options, runOptions)) {
8390
+ if (await runPausedTurn(request, options, runOptions)) {
8436
8391
  return true;
8437
8392
  }
8438
- await failUnresumableContinuation({
8393
+ await failPausedTurn({
8439
8394
  conversationId,
8440
8395
  expectedVersion: request.expectedVersion,
8441
8396
  summary,
8442
- errorMessage: "Awaiting agent continuation was stale before it could run"
8397
+ errorMessage: "Awaiting paused turn was stale before it could run"
8443
8398
  });
8444
8399
  }
8445
8400
  return false;
8446
8401
  }
8447
- async function continueSlackAgentRunWithLockRetry(payload, options, runOptions = {}) {
8448
- return withLogContext(
8449
- { conversationId: payload.conversationId },
8450
- () => continueSlackAgentRunWithLockRetryInContext(payload, options, runOptions)
8451
- );
8452
- }
8453
- async function continueSlackAgentRunWithLockRetryInContext(payload, options, runOptions) {
8454
- const scheduleAgentContinue2 = options.scheduleAgentContinue ?? scheduleAgentContinue;
8455
- for (const [attempt, delayMs] of [
8456
- ...AGENT_CONTINUE_LOCK_RETRY_DELAYS_MS,
8457
- void 0
8458
- ].entries()) {
8459
- try {
8460
- return await continueSlackAgentRun(payload, options, runOptions);
8461
- } catch (error) {
8462
- if (!(error instanceof ResumeTurnBusyError)) {
8463
- throw error;
8464
- }
8465
- if (typeof delayMs !== "number") {
8466
- logWarn("agent.continue.lock.busy", {
8467
- "app.ai.conversation_id": payload.conversationId,
8468
- "app.ai.session_id": payload.sessionId,
8469
- "app.ai.resume_lock_retry_count": attempt
8470
- });
8471
- await scheduleAgentContinue2(payload);
8472
- return true;
8473
- }
8474
- logWarn("agent.continue.lock.retrying", {
8475
- "app.ai.conversation_id": payload.conversationId,
8476
- "app.ai.session_id": payload.sessionId,
8477
- "app.ai.resume_lock_retry_attempt": attempt + 1,
8478
- "app.ai.resume_lock_retry_delay_ms": delayMs
8479
- });
8480
- await sleep(delayMs);
8402
+
8403
+ // src/chat/app/conversation-work.ts
8404
+ function createConversationWork(options) {
8405
+ const services = {
8406
+ ...options.services,
8407
+ replyExecutor: {
8408
+ ...options.services?.replyExecutor,
8409
+ agentRunner: options.agentRunner,
8410
+ getPausedTurnRequest: options.services?.replyExecutor?.getPausedTurnRequest ?? (async (request) => await getPausedTurnRequest({
8411
+ ...request,
8412
+ conversationStore: options.conversationStore
8413
+ })),
8414
+ wakePausedTurn: options.services?.replyExecutor?.wakePausedTurn ?? (async (request) => await wakePausedTurn(request, {
8415
+ queue: options.queue,
8416
+ state: options.state
8417
+ }))
8481
8418
  }
8482
- }
8483
- return true;
8419
+ };
8420
+ const runtime = createSlackRuntime({
8421
+ getSlackAdapter: options.getSlackAdapter,
8422
+ services
8423
+ });
8424
+ const slackWorker = createSlackConversationWorker({
8425
+ getSlackAdapter: options.getSlackAdapter,
8426
+ conversationStore: options.conversationStore,
8427
+ runNextPausedTurn: async (conversationId, runOptions) => await runNextPausedTurn(
8428
+ conversationId,
8429
+ {
8430
+ agentRunner: options.agentRunner,
8431
+ scheduleSessionCompletedPluginTasks: services.replyExecutor?.scheduleSessionCompletedPluginTasks,
8432
+ wakePausedTurn: services.replyExecutor?.wakePausedTurn
8433
+ },
8434
+ runOptions
8435
+ ),
8436
+ runtime,
8437
+ state: options.state
8438
+ });
8439
+ const dispatchWorker = createAgentDispatchConversationWorker({
8440
+ resumeTurn: async (dispatch2, hooks) => {
8441
+ await runNextPausedTurn(
8442
+ getDispatchConversationId(dispatch2),
8443
+ {
8444
+ agentRunner: options.agentRunner,
8445
+ inputMessageIds: getDispatchInputMessageIds(dispatch2.id),
8446
+ routingContext: buildDispatchRoutingContext(dispatch2),
8447
+ scheduleSessionCompletedPluginTasks: services.replyExecutor?.scheduleSessionCompletedPluginTasks,
8448
+ wakePausedTurn: services.replyExecutor?.wakePausedTurn
8449
+ },
8450
+ { shouldYield: hooks.shouldYield }
8451
+ );
8452
+ },
8453
+ runTurn: runtime.runDispatchTurn
8454
+ });
8455
+ const providerWorker = createAgentDispatchWorkRouter({
8456
+ dispatchWorker,
8457
+ fallbackWorker: slackWorker
8458
+ });
8459
+ return {
8460
+ conversationStore: options.conversationStore,
8461
+ queue: options.queue,
8462
+ run: routeAgentInvocationWork({
8463
+ invocationWorker: createAgentInvocationWorker({
8464
+ agentRunner: options.agentRunner
8465
+ }),
8466
+ fallbackWorker: providerWorker
8467
+ }),
8468
+ runtime,
8469
+ state: options.state
8470
+ };
8484
8471
  }
8485
8472
 
8486
8473
  // src/chat/app/production.ts
@@ -8524,61 +8511,13 @@ function createProductionSlackWebhookServices(options) {
8524
8511
  }
8525
8512
  function createProductionConversationWorkOptions(options) {
8526
8513
  const conversationStore = getProductionConversationStore();
8527
- const { agentRunner } = options;
8528
- const services = {
8529
- ...options.services,
8530
- replyExecutor: {
8531
- ...options.services?.replyExecutor,
8532
- agentRunner
8533
- }
8534
- };
8535
- const runtime = createSlackRuntime({
8536
- getSlackAdapter: getProductionSlackAdapter,
8537
- services
8538
- });
8539
- const queue = getVercelConversationWorkQueue();
8540
- const slackWorker = createSlackConversationWorker({
8541
- getSlackAdapter: getProductionSlackAdapter,
8514
+ return createConversationWork({
8515
+ agentRunner: options.agentRunner,
8542
8516
  conversationStore,
8543
- resumeAwaitingContinuation: async (conversationId, runOptions) => await resumeAwaitingSlackContinuation(
8544
- conversationId,
8545
- {
8546
- agentRunner,
8547
- scheduleSessionCompletedPluginTasks: services.replyExecutor?.scheduleSessionCompletedPluginTasks
8548
- },
8549
- runOptions
8550
- ),
8551
- runtime
8552
- });
8553
- const dispatchWorker = createAgentDispatchConversationWorker({
8554
- resumeTurn: async (dispatch2, hooks) => {
8555
- await resumeAwaitingSlackContinuation(
8556
- getDispatchConversationId(dispatch2),
8557
- {
8558
- agentRunner,
8559
- inputMessageIds: getDispatchInputMessageIds(dispatch2.id),
8560
- routingContext: buildDispatchRoutingContext(dispatch2),
8561
- scheduleSessionCompletedPluginTasks: services.replyExecutor?.scheduleSessionCompletedPluginTasks
8562
- },
8563
- { shouldYield: hooks.shouldYield }
8564
- );
8565
- },
8566
- runTurn: runtime.runDispatchTurn
8567
- });
8568
- const providerWorker = createAgentDispatchWorkRouter({
8569
- dispatchWorker,
8570
- fallbackWorker: slackWorker
8517
+ getSlackAdapter: getProductionSlackAdapter,
8518
+ queue: getVercelConversationWorkQueue(),
8519
+ services: options.services
8571
8520
  });
8572
- return {
8573
- conversationStore,
8574
- queue,
8575
- run: routeAgentInvocationWork({
8576
- invocationWorker: createAgentInvocationWorker({
8577
- agentRunner
8578
- }),
8579
- fallbackWorker: providerWorker
8580
- })
8581
- };
8582
8521
  }
8583
8522
 
8584
8523
  // src/chat/resource-events/ingest.ts