@sentry/junior 0.89.0 → 0.91.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 (159) hide show
  1. package/dist/{agent-hooks-RIJ5TSWP.js → agent-hooks-MXAF7RQL.js} +10 -10
  2. package/dist/api/people/list.js +8 -8
  3. package/dist/api/people/profile.js +8 -8
  4. package/dist/api-reference.d.ts +1 -1
  5. package/dist/app.js +797 -745
  6. package/dist/{catalog-runtime-MW7XERSU.js → catalog-runtime-DXE6NXHT.js} +5 -5
  7. package/dist/chat/agent/index.d.ts +4 -0
  8. package/dist/chat/agent/prompt.d.ts +78 -0
  9. package/dist/chat/{respond.d.ts → agent/request.d.ts} +106 -55
  10. package/dist/chat/agent/resume.d.ts +65 -0
  11. package/dist/chat/agent/sandbox.d.ts +15 -0
  12. package/dist/chat/agent/session.d.ts +20 -0
  13. package/dist/chat/agent/skills.d.ts +26 -0
  14. package/dist/chat/agent/tools.d.ts +78 -0
  15. package/dist/chat/agent-dispatch/runner.d.ts +3 -5
  16. package/dist/chat/app/production.d.ts +3 -1
  17. package/dist/chat/app/services.d.ts +1 -4
  18. package/dist/chat/current-instruction.d.ts +5 -3
  19. package/dist/chat/logging.d.ts +2 -0
  20. package/dist/chat/mcp/tool-manager.d.ts +1 -6
  21. package/dist/chat/oauth-authorization-message.d.ts +6 -0
  22. package/dist/chat/pi/messages.d.ts +18 -2
  23. package/dist/chat/pi/transcript.d.ts +31 -0
  24. package/dist/chat/plugins/agent-hooks.d.ts +2 -2
  25. package/dist/chat/prompt.d.ts +1 -1
  26. package/dist/chat/runtime/agent-continue-runner.d.ts +5 -5
  27. package/dist/chat/runtime/agent-run-outcome.d.ts +21 -0
  28. package/dist/chat/runtime/agent-runner.d.ts +11 -0
  29. package/dist/chat/runtime/delivered-turn-state.d.ts +2 -2
  30. package/dist/chat/runtime/generated-artifacts.d.ts +16 -0
  31. package/dist/chat/runtime/reply-executor.d.ts +2 -2
  32. package/dist/chat/runtime/slack-resume.d.ts +21 -12
  33. package/dist/chat/runtime/turn.d.ts +5 -39
  34. package/dist/chat/services/reply-delivery-plan.d.ts +0 -8
  35. package/dist/chat/services/turn-failure-response.d.ts +4 -4
  36. package/dist/chat/services/turn-result.d.ts +4 -7
  37. package/dist/chat/slack/channel.d.ts +9 -5
  38. package/dist/chat/slack/client.d.ts +3 -1
  39. package/dist/chat/slack/context.d.ts +17 -5
  40. package/dist/chat/slack/id-param.d.ts +25 -0
  41. package/dist/chat/slack/ids.d.ts +18 -4
  42. package/dist/chat/slack/mrkdwn.d.ts +6 -0
  43. package/dist/chat/slack/outbound.d.ts +16 -0
  44. package/dist/chat/slack/output.d.ts +3 -3
  45. package/dist/chat/slack/reply.d.ts +4 -7
  46. package/dist/chat/slack/timestamp-param.d.ts +25 -0
  47. package/dist/chat/slack/tools/canvas/context.d.ts +18 -0
  48. package/dist/chat/slack/tools/canvas/create.d.ts +4 -0
  49. package/dist/chat/slack/tools/canvas/edit.d.ts +3 -0
  50. package/dist/chat/slack/tools/canvas/read.d.ts +6 -0
  51. package/dist/chat/slack/tools/canvas/write.d.ts +3 -0
  52. package/dist/chat/{tools/slack → slack/tools}/channel-access.d.ts +4 -3
  53. package/dist/chat/{tools → slack/tools}/channel-capabilities.d.ts +2 -0
  54. package/dist/chat/slack/tools/channel-list-messages.d.ts +3 -0
  55. package/dist/chat/{tools/slack → slack/tools}/context.d.ts +5 -4
  56. package/dist/chat/slack/tools/list/add-items.d.ts +3 -0
  57. package/dist/chat/{tools/slack/lists.d.ts → slack/tools/list/api.d.ts} +2 -1
  58. package/dist/chat/slack/tools/list/create.d.ts +3 -0
  59. package/dist/chat/slack/tools/list/get-items.d.ts +3 -0
  60. package/dist/chat/slack/tools/list/update-item.d.ts +3 -0
  61. package/dist/chat/slack/tools/message-add-reaction.d.ts +4 -0
  62. package/dist/chat/slack/tools/send-message.d.ts +11 -0
  63. package/dist/chat/{tools/slack → slack/tools}/slack-message-url.d.ts +5 -5
  64. package/dist/chat/slack/tools/thread-read.d.ts +6 -0
  65. package/dist/chat/slack/tools/user-lookup.d.ts +2 -0
  66. package/dist/chat/slack/users.d.ts +2 -1
  67. package/dist/chat/{services → slack}/vision-context.d.ts +4 -2
  68. package/dist/chat/state/session-log.d.ts +1 -1
  69. package/dist/chat/tool-exposure.d.ts +10 -0
  70. package/dist/chat/tool-support/catalog-tool-call.d.ts +10 -0
  71. package/dist/chat/{tools/execution → tool-support}/normalize-result.d.ts +4 -1
  72. package/dist/chat/{tools/agent-tools.d.ts → tool-support/pi-tool-adapter.d.ts} +3 -9
  73. package/dist/chat/tool-support/schema-summary.d.ts +2 -0
  74. package/dist/chat/{tools → tool-support}/skill/mcp-tool-summary.d.ts +0 -6
  75. package/dist/chat/tool-support/structured-result.d.ts +73 -0
  76. package/dist/chat/tool-support/text-range-result.d.ts +58 -0
  77. package/dist/chat/tool-support/tool-execution-report.d.ts +7 -0
  78. package/dist/chat/tool-support/zod-tool.d.ts +27 -0
  79. package/dist/chat/tools/advisor/tool.d.ts +9 -18
  80. package/dist/chat/tools/definition.d.ts +56 -9
  81. package/dist/chat/tools/execute-tool.d.ts +3 -0
  82. package/dist/chat/tools/index.d.ts +3 -2
  83. package/dist/chat/tools/resource-events.d.ts +3 -13
  84. package/dist/chat/tools/runtime/report-progress.d.ts +1 -3
  85. package/dist/chat/tools/sandbox/bash.d.ts +1 -4
  86. package/dist/chat/tools/sandbox/edit-file.d.ts +25 -7
  87. package/dist/chat/tools/sandbox/file-uploads.d.ts +30 -0
  88. package/dist/chat/tools/sandbox/file-utils.d.ts +10 -1
  89. package/dist/chat/tools/sandbox/find-files.d.ts +12 -11
  90. package/dist/chat/tools/sandbox/grep.d.ts +18 -12
  91. package/dist/chat/tools/sandbox/list-dir.d.ts +15 -7
  92. package/dist/chat/tools/sandbox/read-file.d.ts +2 -31
  93. package/dist/chat/tools/sandbox/write-file.d.ts +1 -4
  94. package/dist/chat/tools/search-tools.d.ts +4 -0
  95. package/dist/chat/tools/skill/call-mcp-tool.d.ts +1 -4
  96. package/dist/chat/tools/skill/load-skill.d.ts +4 -5
  97. package/dist/chat/tools/skill/search-mcp-tools.d.ts +1 -5
  98. package/dist/chat/tools/system-time.d.ts +1 -1
  99. package/dist/chat/tools/types.d.ts +15 -5
  100. package/dist/chat/tools/web/fetch-content.d.ts +4 -2
  101. package/dist/chat/tools/web/fetch-tool.d.ts +4 -4
  102. package/dist/chat/tools/web/image-generate.d.ts +4 -3
  103. package/dist/chat/tools/web/search.d.ts +1 -4
  104. package/dist/{chunk-4IXHIV37.js → chunk-2S7M37HY.js} +6 -8
  105. package/dist/{chunk-L7OHKDOX.js → chunk-3MPOMK3K.js} +58 -7
  106. package/dist/{chunk-V6XDCS3X.js → chunk-5GBUZI6M.js} +4 -4
  107. package/dist/{chunk-7ES37H6U.js → chunk-AN2437E3.js} +4 -4
  108. package/dist/chunk-AOISHLSF.js +747 -0
  109. package/dist/{chunk-XS6NLQPA.js → chunk-DACFZ5CI.js} +474 -33
  110. package/dist/chunk-GCWD2VHP.js +1216 -0
  111. package/dist/chunk-GHGPTPBL.js +53 -0
  112. package/dist/{chunk-S35CPNPT.js → chunk-HXL2ZV74.js} +40 -216
  113. package/dist/{chunk-PBV4ZIVM.js → chunk-IRRMABQD.js} +1 -1
  114. package/dist/{chunk-2DPZRS3B.js → chunk-IX76WFJV.js} +8 -8
  115. package/dist/{chunk-66NX7MNW.js → chunk-KC42JAAQ.js} +1 -1
  116. package/dist/chunk-LFLTJQVR.js +90 -0
  117. package/dist/{chunk-IATAYWFU.js → chunk-NW7AXDA5.js} +2 -2
  118. package/dist/{chunk-F22AM3OW.js → chunk-NYWPISSO.js} +1 -1
  119. package/dist/chunk-OB42YVAE.js +16 -0
  120. package/dist/chunk-ONFOWI2D.js +955 -0
  121. package/dist/chunk-PIB4EOW6.js +1093 -0
  122. package/dist/{chunk-6UPQ5GTJ.js → chunk-QCKGI74V.js} +7 -7
  123. package/dist/{chunk-YPAE5RH3.js → chunk-RS6ANWVT.js} +1 -1
  124. package/dist/{chunk-C5NCV3OB.js → chunk-TYUXD3FN.js} +1 -1
  125. package/dist/{chunk-KBFQXJL4.js → chunk-XP7F4LYB.js} +5 -7
  126. package/dist/{chunk-NYJLQI42.js → chunk-ZGDTBSQ7.js} +11191 -11891
  127. package/dist/{chunk-VMBZFPZM.js → chunk-ZUK7BL63.js} +187 -9
  128. package/dist/cli/chat.js +55 -40
  129. package/dist/cli/check.js +6 -6
  130. package/dist/cli/plugins.js +16 -16
  131. package/dist/cli/snapshot-warmup.js +9 -9
  132. package/dist/cli/upgrade.js +21 -21
  133. package/dist/db-CXI7PR5U.js +18 -0
  134. package/dist/handlers/agent-dispatch.d.ts +3 -3
  135. package/dist/handlers/mcp-oauth-callback.d.ts +3 -3
  136. package/dist/handlers/oauth-callback.d.ts +3 -3
  137. package/dist/instrumentation.js +6 -5
  138. package/dist/nitro.js +3 -7
  139. package/dist/reporting.js +23 -25
  140. package/dist/{runner-BCRF2FCI.js → runner-FKL3RAHA.js} +89 -77
  141. package/dist/{validation-FMJO3L5P.js → validation-I7GD2YWS.js} +6 -6
  142. package/package.json +5 -5
  143. package/dist/chat/respond-helpers.d.ts +0 -82
  144. package/dist/chat/services/attachment-claims.d.ts +0 -2
  145. package/dist/chat/services/channel-intent.d.ts +0 -2
  146. package/dist/chat/tools/sandbox/attach-file.d.ts +0 -7
  147. package/dist/chat/tools/slack/canvas-tools.d.ts +0 -30
  148. package/dist/chat/tools/slack/channel-list-messages.d.ts +0 -9
  149. package/dist/chat/tools/slack/channel-post-message.d.ts +0 -5
  150. package/dist/chat/tools/slack/list-tools.d.ts +0 -21
  151. package/dist/chat/tools/slack/message-add-reaction.d.ts +0 -5
  152. package/dist/chat/tools/slack/thread-read.d.ts +0 -12
  153. package/dist/chat/tools/slack/user-lookup.d.ts +0 -8
  154. package/dist/chunk-AHLSXMOU.js +0 -506
  155. package/dist/chunk-FTMXFBDC.js +0 -49
  156. package/dist/chunk-KBSLCOGE.js +0 -2226
  157. package/dist/chunk-KQKIA4CU.js +0 -186
  158. package/dist/db-37HOGLI7.js +0 -18
  159. /package/dist/chat/{tools/slack/canvases.d.ts → slack/tools/canvas/api.d.ts} +0 -0
package/dist/app.js CHANGED
@@ -1,81 +1,23 @@
1
1
  import {
2
- AuthorizationFlowDisabledError,
3
- PluginCredentialFailureError,
4
- SANDBOX_EGRESS_PROXY_PATH,
5
- TurnInputCommitLostError,
6
- TurnInputDeferredError,
7
- addReactionToMessage,
8
- appendSlackLegacyAttachmentText,
9
- applyPendingAuthUpdate,
10
2
  buildConversationContext,
11
3
  buildDeliveredTurnStatePatch,
12
- buildDeterministicTurnId,
13
- buildSteeringPiMessage,
14
- clearPendingAuth,
15
- coerceThreadArtifactsState,
16
- completeDeliveredTurn,
17
4
  createConversationMemoryService,
18
- createUserTokenStore,
19
- deleteMcpAuthSession,
20
- deliverResourceEventSubscription,
21
5
  estimateTextTokens,
22
- executeCredentialedEgressRequest,
23
6
  finalizeFailedTurnReply,
24
- finalizeMcpAuthorization,
25
- findMatchingResourceEventSubscriptions,
26
- formatProviderLabel,
27
- generateAssistantReply,
28
7
  generateConversationId,
29
8
  getAgentContextCompactionTriggerTokens,
30
9
  getAgentTurnDiagnosticsAttributes,
31
- getChannelConfigurationService,
32
- getChannelConfigurationServiceById,
33
- getConfigDefaults,
34
10
  getConversationMessageSlackTs,
35
- getConversationPendingAuth,
36
- getMcpStoredOAuthCredentials,
37
- getPersistedSandboxState,
38
- getPersistedThreadState,
39
11
  getThreadTitleSourceMessage,
40
- isAuthResumeRetryableTurnError,
41
- isCooperativeTurnYieldError,
42
12
  isHumanConversationMessage,
43
- isPendingAuthLatestRequest,
44
- isRetryableTurnError,
45
- isTurnInputCommitLostError,
46
- isTurnInputDeferredError,
47
- listThreadReplies,
48
13
  markConversationMessage,
49
- markTurnClosed,
50
- markTurnFailed,
51
- mergeArtifactsState,
52
14
  normalizeConversationText,
53
- normalizeSandboxEgressTracePropagationDomains,
54
- parseOAuthStatePayload,
55
- parseSandboxEgressCredentialToken,
56
- persistCompletedSessionRecord,
57
- persistThreadState,
58
- persistThreadStateById,
59
- persistYieldSessionRecord,
60
- postSlackMessage,
61
15
  processPluginTask,
62
- removeReactionFromMessage,
63
- renderSlackLegacyAttachmentText,
64
16
  requireTurnFailureEventId,
65
- resolveBaseUrl,
66
- resolvePluginOAuthAccount,
67
- resolveSandboxEgressProviderForHost,
68
17
  scheduleSessionCompletedPluginTasks,
69
- setConfigDefaults,
70
- shouldEmitDevAgentTrace,
71
- shouldPropagateSandboxEgressTrace,
72
- startActiveTurn,
73
- startOAuthFlow,
74
- unlinkProvider,
75
18
  updateConversationStats,
76
- uploadFilesToThread,
77
19
  upsertConversationMessage
78
- } from "./chunk-NYJLQI42.js";
20
+ } from "./chunk-AOISHLSF.js";
79
21
  import {
80
22
  CONVERSATION_WORK_CHECK_IN_INTERVAL_MS,
81
23
  CONVERSATION_WORK_STALE_ENQUEUE_MS,
@@ -99,17 +41,12 @@ import {
99
41
  requestConversationContinuation,
100
42
  requestConversationWork,
101
43
  startConversationWork
102
- } from "./chunk-2DPZRS3B.js";
103
- import {
104
- JUNIOR_THREAD_STATE_TTL_MS,
105
- coerceThreadConversationState
106
- } from "./chunk-66NX7MNW.js";
107
- import "./chunk-V6XDCS3X.js";
44
+ } from "./chunk-IX76WFJV.js";
108
45
  import {
109
46
  getVercelConversationWorkQueue,
110
47
  resolveConversationWorkQueueTopic,
111
48
  verifyConversationQueueMessage
112
- } from "./chunk-F22AM3OW.js";
49
+ } from "./chunk-NYWPISSO.js";
113
50
  import {
114
51
  PLUGIN_TASK_QUEUE_TOPIC,
115
52
  createVercelQueueClient,
@@ -123,25 +60,11 @@ import {
123
60
  resolveSlackChannelTypeFromMessage,
124
61
  resolveSlackConversationContext,
125
62
  setConversationTitle
126
- } from "./chunk-6UPQ5GTJ.js";
127
- import {
128
- abandonAgentTurnSessionRecord,
129
- buildSlackOutputMessage,
130
- commitMessages,
131
- failAgentTurnSessionRecord,
132
- getAgentTurnSessionRecord,
133
- listAgentTurnSessionSummariesForConversation,
134
- loadProjection,
135
- normalizeSlackStatusText,
136
- recordAgentTurnSessionSummary,
137
- recordAuthorizationCompleted,
138
- splitSlackReplyText,
139
- truncateStatusText
140
- } from "./chunk-KBSLCOGE.js";
63
+ } from "./chunk-QCKGI74V.js";
141
64
  import {
142
65
  validatePluginEgressCredentialHooks,
143
66
  validatePluginRegistrations
144
- } from "./chunk-C5NCV3OB.js";
67
+ } from "./chunk-TYUXD3FN.js";
145
68
  import {
146
69
  defineJuniorPlugins,
147
70
  pluginCatalogConfigFromEnv,
@@ -149,51 +72,128 @@ import {
149
72
  pluginRuntimeRegistrationsFromPluginSet
150
73
  } from "./chunk-SG5WAA7H.js";
151
74
  import {
75
+ AuthorizationFlowDisabledError,
76
+ PluginCredentialFailureError,
77
+ SANDBOX_EGRESS_PROXY_PATH,
78
+ addReactionToMessage,
79
+ appendSlackLegacyAttachmentText,
80
+ buildSteeringPiMessage,
81
+ createAgentRunner,
82
+ createUserTokenStore,
83
+ deleteMcpAuthSession,
84
+ deliverResourceEventSubscription,
85
+ executeAgentRun,
86
+ executeCredentialedEgressRequest,
87
+ finalizeMcpAuthorization,
88
+ findMatchingResourceEventSubscriptions,
89
+ formatProviderLabel,
90
+ getConfigDefaults,
91
+ getMcpStoredOAuthCredentials,
92
+ listThreadReplies,
93
+ normalizeSandboxEgressTracePropagationDomains,
94
+ parseOAuthStatePayload,
95
+ parseSandboxEgressCredentialToken,
96
+ postSlackMessage,
97
+ removeReactionFromMessage,
98
+ renderSlackLegacyAttachmentText,
99
+ resolveBaseUrl,
100
+ resolvePluginOAuthAccount,
101
+ resolveSandboxEgressProviderForHost,
102
+ setConfigDefaults,
103
+ shouldEmitDevAgentTrace,
104
+ shouldPropagateSandboxEgressTrace,
105
+ startOAuthFlow,
106
+ unlinkProvider
107
+ } from "./chunk-ZGDTBSQ7.js";
108
+ import {
109
+ CooperativeTurnYieldError,
110
+ TurnInputCommitLostError,
111
+ TurnInputDeferredError,
112
+ applyPendingAuthUpdate,
113
+ buildDeterministicTurnId,
114
+ clearPendingAuth,
115
+ coerceThreadArtifactsState,
116
+ completeDeliveredTurn,
117
+ getChannelConfigurationService,
118
+ getChannelConfigurationServiceById,
119
+ getConversationPendingAuth,
120
+ getPersistedSandboxState,
121
+ getPersistedThreadState,
122
+ isCooperativeTurnYieldError,
123
+ isPendingAuthLatestRequest,
124
+ isTurnInputCommitLostError,
125
+ isTurnInputDeferredError,
126
+ markTurnClosed,
127
+ markTurnFailed,
128
+ mergeArtifactsState,
129
+ persistCompletedSessionRecord,
130
+ persistThreadState,
131
+ persistThreadStateById,
132
+ persistYieldSessionRecord,
133
+ startActiveTurn
134
+ } from "./chunk-ONFOWI2D.js";
135
+ import {
136
+ JUNIOR_THREAD_STATE_TTL_MS,
137
+ coerceThreadConversationState
138
+ } from "./chunk-KC42JAAQ.js";
139
+ import "./chunk-5GBUZI6M.js";
140
+ import {
141
+ buildSlackOutputMessage,
142
+ escapeSlackMrkdwnText,
143
+ formatSlackLink,
144
+ normalizeSlackStatusText,
145
+ splitSlackReplyText,
146
+ truncateStatusText,
147
+ unwrapCurrentInstruction
148
+ } from "./chunk-PIB4EOW6.js";
149
+ import {
150
+ abandonAgentTurnSessionRecord,
151
+ commitMessages,
152
+ escapeXml,
153
+ failAgentTurnSessionRecord,
154
+ getAgentTurnSessionRecord,
155
+ listAgentTurnSessionSummariesForConversation,
156
+ loadProjection,
157
+ recordAgentTurnSessionSummary,
158
+ recordAuthorizationCompleted
159
+ } from "./chunk-GCWD2VHP.js";
160
+ import {
161
+ SlackActionError,
152
162
  bindSlackDirectCredentialSubject,
163
+ downloadPrivateSlackFile,
164
+ getHeaderString,
153
165
  getPluginApiRoutes,
154
166
  getPluginRoutes,
155
167
  getPluginSlackConversationLink,
156
168
  getPlugins,
157
- parseSlackMessageTs,
169
+ getSlackClient,
170
+ getWorkspaceTeamId,
171
+ isDmChannel,
172
+ normalizeSlackConversationId,
173
+ runWithSlackInstallationToken,
174
+ runWithWorkspaceTeamId,
158
175
  setPlugins,
159
176
  validatePlugins,
160
177
  verifySlackDirectCredentialSubject
161
- } from "./chunk-XS6NLQPA.js";
178
+ } from "./chunk-DACFZ5CI.js";
162
179
  import {
163
180
  createPluginLogger,
164
181
  createPluginState
165
- } from "./chunk-IATAYWFU.js";
182
+ } from "./chunk-NW7AXDA5.js";
166
183
  import "./chunk-G3E7SCME.js";
167
184
  import {
168
185
  acquireActiveLock,
169
186
  getStateAdapter
170
- } from "./chunk-YPAE5RH3.js";
187
+ } from "./chunk-RS6ANWVT.js";
171
188
  import {
172
189
  getConversationStore,
173
190
  getDb
174
- } from "./chunk-4IXHIV37.js";
175
- import {
176
- SlackActionError,
177
- createSlackDestination,
178
- destinationKey,
179
- downloadPrivateSlackFile,
180
- getHeaderString,
181
- getSlackClient,
182
- getWorkspaceTeamId,
183
- isDmChannel,
184
- normalizeSlackConversationId,
185
- parseDestination,
186
- requireSlackDestination,
187
- runWithSlackInstallationToken,
188
- runWithWorkspaceTeamId,
189
- sameDestination
190
- } from "./chunk-AHLSXMOU.js";
191
+ } from "./chunk-2S7M37HY.js";
191
192
  import {
192
193
  FUNCTION_TIMEOUT_BUFFER_SECONDS,
193
194
  botConfig,
194
195
  completeObject,
195
196
  completeText,
196
- escapeXml,
197
197
  getChatConfig,
198
198
  getRuntimeMetadata,
199
199
  getSlackBotToken,
@@ -205,57 +205,66 @@ import {
205
205
  normalizeSlackEmojiName,
206
206
  setSlackReactionConfig,
207
207
  stripRuntimeTurnContext,
208
- trimTrailingAssistantMessages,
209
- unwrapCurrentInstruction
210
- } from "./chunk-S35CPNPT.js";
208
+ trimTrailingAssistantMessages
209
+ } from "./chunk-HXL2ZV74.js";
211
210
  import {
212
211
  parseSlackThreadId,
212
+ readSlackRawMessageContext,
213
213
  resolveSlackChannelIdFromMessage,
214
214
  resolveSlackChannelIdFromThreadId
215
- } from "./chunk-L7OHKDOX.js";
215
+ } from "./chunk-3MPOMK3K.js";
216
+ import {
217
+ createSlackDestination,
218
+ destinationKey,
219
+ parseDestination,
220
+ requireSlackDestination,
221
+ sameDestination
222
+ } from "./chunk-GHGPTPBL.js";
216
223
  import {
217
224
  discoverSkills
218
- } from "./chunk-7ES37H6U.js";
225
+ } from "./chunk-AN2437E3.js";
219
226
  import {
220
227
  buildOAuthTokenRequest,
221
228
  hasRequiredOAuthScope,
222
229
  parseOAuthTokenResponse,
223
230
  pluginCatalogRuntime
224
- } from "./chunk-KBFQXJL4.js";
225
- import {
226
- createRequester,
227
- createSlackRequester,
228
- createSlackResumeRequester,
229
- isActorUserId,
230
- parseActorUserId,
231
- toStoredSlackRequester
232
- } from "./chunk-KQKIA4CU.js";
233
- import {
234
- homeDir
235
- } from "./chunk-6APU57E6.js";
231
+ } from "./chunk-XP7F4LYB.js";
236
232
  import {
237
233
  buildTurnFailureResponse,
238
234
  createChatSdkLogger,
239
235
  createRequestContext,
236
+ createRequester,
237
+ createSlackRequester,
238
+ createSlackResumeRequester,
240
239
  getActiveTraceId,
240
+ isActorUserId,
241
241
  logError,
242
242
  logException,
243
243
  logInfo,
244
244
  logWarn,
245
+ parseActorUserId,
245
246
  setSentryUser,
246
247
  setSpanAttributes,
247
248
  setSpanStatus,
248
249
  setTags,
250
+ toStoredSlackRequester,
249
251
  withContext,
250
252
  withSpan
251
- } from "./chunk-VMBZFPZM.js";
253
+ } from "./chunk-ZUK7BL63.js";
252
254
  import {
253
- JUNIOR_PLUGIN_TASK_CALLBACK_ROUTE,
254
255
  toOptionalString
255
- } from "./chunk-FTMXFBDC.js";
256
+ } from "./chunk-OB42YVAE.js";
256
257
  import {
257
258
  continueTrace
258
259
  } from "./chunk-3DA7X2U3.js";
260
+ import {
261
+ homeDir
262
+ } from "./chunk-6APU57E6.js";
263
+ import {
264
+ JUNIOR_PLUGIN_TASK_CALLBACK_ROUTE,
265
+ parseSlackChannelId,
266
+ parseSlackMessageTs
267
+ } from "./chunk-LFLTJQVR.js";
259
268
  import "./chunk-MLKGABMK.js";
260
269
 
261
270
  // src/app.ts
@@ -313,16 +322,7 @@ function getDashboardConversationLink(conversationId) {
313
322
  return `${baseURL}${path2}`;
314
323
  }
315
324
 
316
- // src/chat/slack/reply.ts
317
- import { Buffer as Buffer2 } from "buffer";
318
-
319
325
  // src/chat/slack/footer.ts
320
- function escapeSlackMrkdwn(text) {
321
- return text.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;");
322
- }
323
- function escapeSlackLinkUrl(url) {
324
- return url.replaceAll("&", "&amp;").replaceAll("<", "%3C").replaceAll(">", "%3E");
325
- }
326
326
  function buildSlackReplyFooter(args) {
327
327
  const items = [];
328
328
  const conversationId = args.conversationId?.trim();
@@ -354,7 +354,7 @@ function buildSlackReplyBlocks(text, footer) {
354
354
  type: "context",
355
355
  elements: footer.items.map((item) => ({
356
356
  type: "mrkdwn",
357
- text: item.url ? `*${escapeSlackMrkdwn(item.label)}:* <${escapeSlackLinkUrl(item.url)}|${escapeSlackMrkdwn(item.value)}>` : `*${escapeSlackMrkdwn(item.label)}:* ${escapeSlackMrkdwn(item.value)}`
357
+ text: item.url ? `*${escapeSlackMrkdwnText(item.label)}:* ${formatSlackLink(item.url, item.value)}` : `*${escapeSlackMrkdwnText(item.label)}:* ${escapeSlackMrkdwnText(item.value)}`
358
358
  }))
359
359
  });
360
360
  }
@@ -362,17 +362,12 @@ function buildSlackReplyBlocks(text, footer) {
362
362
  }
363
363
 
364
364
  // src/chat/slack/reply.ts
365
- function resolveReplyDelivery(reply) {
366
- const replyHasFiles = Boolean(reply.files && reply.files.length > 0);
365
+ function shouldPostThreadReply(reply) {
367
366
  const deliveryPlan = reply.deliveryPlan ?? {
368
367
  mode: reply.deliveryMode ?? "thread",
369
- postThreadText: (reply.deliveryMode ?? "thread") !== "channel_only",
370
- attachFiles: replyHasFiles ? "inline" : "none"
371
- };
372
- return {
373
- shouldPostThreadReply: deliveryPlan.postThreadText,
374
- attachFiles: replyHasFiles && deliveryPlan.attachFiles !== "none" ? "inline" : "none"
368
+ postThreadText: (reply.deliveryMode ?? "thread") !== "channel_only"
375
369
  };
370
+ return deliveryPlan.postThreadText;
376
371
  }
377
372
  function buildReplyText(text) {
378
373
  const message = buildSlackOutputMessage(text);
@@ -388,28 +383,9 @@ function buildTextPosts(args) {
388
383
  const chunks = splitSlackReplyText(args.text);
389
384
  return chunks.map((chunk, index) => ({
390
385
  text: chunk,
391
- ...index === 0 && args.firstFiles ? { files: args.firstFiles } : {},
392
386
  stage: index === 0 ? args.firstStage ?? "thread_reply" : "thread_reply_continuation"
393
387
  }));
394
388
  }
395
- async function normalizeFileUploads(files) {
396
- return await Promise.all(
397
- files.map(async (file) => {
398
- let data;
399
- if (Buffer2.isBuffer(file.data)) {
400
- data = file.data;
401
- } else if (file.data instanceof ArrayBuffer) {
402
- data = Buffer2.from(file.data);
403
- } else {
404
- data = Buffer2.from(await file.data.arrayBuffer());
405
- }
406
- return {
407
- data,
408
- filename: file.filename
409
- };
410
- })
411
- );
412
- }
413
389
  function findLastTextPostIndex(posts) {
414
390
  for (let index = posts.length - 1; index >= 0; index -= 1) {
415
391
  if (posts[index]?.text.trim().length) {
@@ -418,56 +394,25 @@ function findLastTextPostIndex(posts) {
418
394
  }
419
395
  return -1;
420
396
  }
421
- async function uploadReplyFiles(args) {
422
- try {
423
- await uploadFilesToThread({
424
- channelId: args.channelId,
425
- threadTs: args.threadTs,
426
- files: await normalizeFileUploads(args.files)
427
- });
428
- } catch (error) {
429
- if (args.failureMode === "strict") {
430
- throw error;
431
- }
432
- }
433
- }
434
397
  function planSlackReplyPosts(args) {
435
- const replyFiles = args.reply.files && args.reply.files.length > 0 ? args.reply.files : void 0;
436
- const { shouldPostThreadReply, attachFiles } = resolveReplyDelivery(
437
- args.reply
438
- );
439
398
  const posts = [];
440
- const textPosts = shouldPostThreadReply ? buildTextPosts({
441
- text: args.reply.text,
442
- firstFiles: attachFiles === "inline" ? replyFiles : void 0
399
+ const textPosts = shouldPostThreadReply(args.reply) ? buildTextPosts({
400
+ text: args.reply.text
443
401
  }) : [];
444
402
  posts.push(...textPosts);
445
- if (attachFiles === "inline" && replyFiles && textPosts.length === 0) {
446
- posts.push({
447
- files: replyFiles,
448
- stage: "thread_reply",
449
- text: ""
450
- });
451
- } else if (shouldPostThreadReply && textPosts.length === 0) {
403
+ if (shouldPostThreadReply(args.reply) && textPosts.length === 0) {
452
404
  posts.push({
453
405
  text: buildReplyText(args.reply.text),
454
406
  stage: "thread_reply"
455
407
  });
456
408
  }
457
- if (attachFiles === "followup" && replyFiles) {
458
- posts.push({
459
- files: replyFiles,
460
- stage: "thread_reply_files_followup",
461
- text: ""
462
- });
463
- }
464
409
  return posts;
465
410
  }
466
411
  async function postSlackApiReplyPosts(args) {
467
412
  const lastTextPostIndex = findLastTextPostIndex(args.posts);
468
413
  let lastPostedMessageTs;
469
414
  for (const [index, post] of args.posts.entries()) {
470
- const hasVisibleDelivery = post.text.trim().length > 0 || post.files?.length;
415
+ const hasVisibleDelivery = post.text.trim().length > 0;
471
416
  if (hasVisibleDelivery) {
472
417
  await args.beforePost?.();
473
418
  }
@@ -485,18 +430,7 @@ async function postSlackApiReplyPosts(args) {
485
430
  messageTs = response.ts;
486
431
  lastPostedMessageTs = response.ts;
487
432
  }
488
- if (!post.files?.length) {
489
- continue;
490
- }
491
- if (!args.threadTs && !lastPostedMessageTs) {
492
- throw new Error("Slack file delivery requires a posted message thread");
493
- }
494
- await uploadReplyFiles({
495
- channelId: args.channelId,
496
- failureMode: args.fileUploadFailureMode ?? "best_effort",
497
- threadTs: args.threadTs ?? lastPostedMessageTs,
498
- files: post.files
499
- });
433
+ continue;
500
434
  } catch (error) {
501
435
  await args.onPostError?.({
502
436
  error,
@@ -892,13 +826,19 @@ function getAssistantMessageId(dispatch) {
892
826
  function buildDispatchConversationText(dispatch) {
893
827
  return `[dispatched task] ${dispatch.input}`;
894
828
  }
829
+ function shouldPostDispatchReplyText(reply) {
830
+ return reply.deliveryPlan?.postThreadText ?? (reply.deliveryMode ?? "thread") !== "channel_only";
831
+ }
895
832
  function ensureVisibleDeliveryText(reply) {
896
- if (reply.text.trim().length > 0 || !reply.files?.length) {
833
+ if (!shouldPostDispatchReplyText(reply)) {
834
+ return reply;
835
+ }
836
+ if (reply.text.trim().length > 0) {
897
837
  return reply;
898
838
  }
899
839
  return {
900
840
  ...reply,
901
- text: "Generated files are attached."
841
+ text: "The task completed without a visible response."
902
842
  };
903
843
  }
904
844
  function upsertDispatchUserMessage(args) {
@@ -954,8 +894,7 @@ function canClaimDispatch(record, nowMs) {
954
894
  }
955
895
  return true;
956
896
  }
957
- async function runAgentDispatchSlice(callback, deps = {}) {
958
- const generateAssistantReply2 = deps.generateAssistantReply ?? generateAssistantReply;
897
+ async function runAgentDispatchSlice(callback, deps) {
959
898
  const scheduleCallback = deps.scheduleCallback ?? scheduleDispatchCallback;
960
899
  const scheduleCompletedTasks = deps.scheduleSessionCompletedPluginTasks ?? scheduleSessionCompletedPluginTasks;
961
900
  const nowMs = Date.now();
@@ -1051,61 +990,91 @@ async function runAgentDispatchSlice(callback, deps = {}) {
1051
990
  const conversationContext = buildConversationContext(conversation, {
1052
991
  excludeMessageId: userMessageId
1053
992
  });
1054
- let reply = await generateAssistantReply2(dispatch.input, {
1055
- authorizationFlowMode: "disabled",
1056
- credentialContext: {
1057
- actor: dispatch.actor,
1058
- ...dispatch.credentialSubject ? { subject: dispatch.credentialSubject } : {}
993
+ const outcome = await deps.agentRunner.run({
994
+ input: {
995
+ messageText: dispatch.input,
996
+ conversationContext,
997
+ piMessages: conversation.piMessages
1059
998
  },
1060
- configuration,
1061
- channelConfiguration,
1062
- conversationContext,
1063
- artifactState: artifacts,
1064
- piMessages: conversation.piMessages,
1065
- destination: dispatch.destination,
1066
- source: dispatch.source,
1067
- dispatch: {
1068
- actor: dispatch.actor,
1069
- metadata: dispatch.metadata,
1070
- plugin: dispatch.plugin
1071
- },
1072
- correlation: {
1073
- conversationId,
1074
- threadId: conversationId,
1075
- turnId,
1076
- runId: dispatch.id,
1077
- channelId: dispatch.destination.channelId,
1078
- teamId: dispatch.destination.teamId
999
+ routing: {
1000
+ credentialContext: {
1001
+ actor: dispatch.actor,
1002
+ ...dispatch.credentialSubject ? { subject: dispatch.credentialSubject } : {}
1003
+ },
1004
+ destination: dispatch.destination,
1005
+ source: dispatch.source,
1006
+ dispatch: {
1007
+ actor: dispatch.actor,
1008
+ metadata: dispatch.metadata,
1009
+ plugin: dispatch.plugin
1010
+ },
1011
+ correlation: {
1012
+ conversationId,
1013
+ threadId: conversationId,
1014
+ turnId,
1015
+ runId: dispatch.id,
1016
+ channelId: dispatch.destination.channelId,
1017
+ teamId: dispatch.destination.teamId
1018
+ },
1019
+ surface: "api",
1020
+ toolChannelId: dispatch.destination.channelId
1079
1021
  },
1080
- surface: "api",
1081
- toolChannelId: dispatch.destination.channelId,
1082
- sandbox: {
1083
- sandboxId,
1084
- sandboxDependencyProfileHash,
1085
- tracePropagation: deps.tracePropagation
1022
+ policy: {
1023
+ authorizationFlowMode: "disabled",
1024
+ configuration,
1025
+ channelConfiguration
1086
1026
  },
1087
- onSandboxAcquired: async (sandbox) => {
1088
- sandboxId = sandbox.sandboxId;
1089
- sandboxDependencyProfileHash = sandbox.sandboxDependencyProfileHash;
1090
- await persistRuntimePatch({
1091
- threadId: conversationId,
1092
- conversation,
1093
- artifacts,
1027
+ state: {
1028
+ artifactState: artifacts,
1029
+ sandbox: {
1094
1030
  sandboxId,
1095
1031
  sandboxDependencyProfileHash
1096
- });
1032
+ }
1097
1033
  },
1098
- onArtifactStateUpdated: async (nextArtifacts2) => {
1099
- artifacts = nextArtifacts2;
1100
- await persistRuntimePatch({
1101
- threadId: conversationId,
1102
- conversation,
1103
- artifacts,
1104
- sandboxId,
1105
- sandboxDependencyProfileHash
1106
- });
1034
+ durability: {
1035
+ onSandboxAcquired: async (sandbox) => {
1036
+ sandboxId = sandbox.sandboxId;
1037
+ sandboxDependencyProfileHash = sandbox.sandboxDependencyProfileHash;
1038
+ await persistRuntimePatch({
1039
+ threadId: conversationId,
1040
+ conversation,
1041
+ artifacts,
1042
+ sandboxId,
1043
+ sandboxDependencyProfileHash
1044
+ });
1045
+ },
1046
+ onArtifactStateUpdated: async (nextArtifacts2) => {
1047
+ artifacts = nextArtifacts2;
1048
+ await persistRuntimePatch({
1049
+ threadId: conversationId,
1050
+ conversation,
1051
+ artifacts,
1052
+ sandboxId,
1053
+ sandboxDependencyProfileHash
1054
+ });
1055
+ }
1107
1056
  }
1108
1057
  });
1058
+ if (outcome.status === "awaiting_auth") {
1059
+ await markDispatch({
1060
+ dispatch,
1061
+ status: "blocked",
1062
+ errorMessage: "Dispatch requires authorization from an interactive user turn."
1063
+ });
1064
+ return;
1065
+ }
1066
+ if (outcome.status === "suspended") {
1067
+ const awaiting = await markDispatch({
1068
+ dispatch,
1069
+ status: "awaiting_resume"
1070
+ });
1071
+ await scheduleCallback({
1072
+ id: awaiting.id,
1073
+ expectedVersion: awaiting.version
1074
+ });
1075
+ return;
1076
+ }
1077
+ let reply = outcome.result;
1109
1078
  const failure = reply.diagnostics.outcome === "success" ? void 0 : reply.diagnostics.errorMessage ?? `Agent turn ended with ${reply.diagnostics.outcome}.`;
1110
1079
  if (failure) {
1111
1080
  reply = finalizeFailedTurnReply({
@@ -1123,8 +1092,7 @@ async function runAgentDispatchSlice(callback, deps = {}) {
1123
1092
  posts: planSlackReplyPosts({ reply: deliveryReply }),
1124
1093
  footer: buildSlackReplyFooter({
1125
1094
  conversationId
1126
- }),
1127
- fileUploadFailureMode: "strict"
1095
+ })
1128
1096
  });
1129
1097
  markConversationMessage(conversation, userMessageId, {
1130
1098
  replied: true,
@@ -1224,28 +1192,6 @@ async function runAgentDispatchSlice(callback, deps = {}) {
1224
1192
  });
1225
1193
  return;
1226
1194
  }
1227
- if (isRetryableTurnError(error, "mcp_auth_resume") || isRetryableTurnError(error, "plugin_auth_resume")) {
1228
- await markDispatch({
1229
- dispatch,
1230
- status: "blocked",
1231
- errorMessage: "Dispatch requires authorization from an interactive user turn."
1232
- });
1233
- return;
1234
- }
1235
- if (isRetryableTurnError(error, "agent_continue")) {
1236
- const version = error.metadata?.version;
1237
- if (typeof version === "number") {
1238
- const awaiting = await markDispatch({
1239
- dispatch,
1240
- status: "awaiting_resume"
1241
- });
1242
- await scheduleCallback({
1243
- id: awaiting.id,
1244
- expectedVersion: awaiting.version
1245
- });
1246
- return;
1247
- }
1248
- }
1249
1195
  logException(
1250
1196
  error,
1251
1197
  "agent_dispatch_run_failed",
@@ -1267,14 +1213,14 @@ async function runAgentDispatchSlice(callback, deps = {}) {
1267
1213
  }
1268
1214
 
1269
1215
  // src/handlers/agent-dispatch.ts
1270
- async function POST(request, waitUntil, options = {}) {
1216
+ async function POST(request, waitUntil, options) {
1271
1217
  const payload = await verifyDispatchCallbackRequest(request);
1272
1218
  if (!payload) {
1273
1219
  return new Response("Unauthorized", { status: 401 });
1274
1220
  }
1275
1221
  waitUntil(
1276
1222
  () => runAgentDispatchSlice(payload, {
1277
- tracePropagation: options.tracePropagation
1223
+ agentRunner: options.agentRunner
1278
1224
  }).catch((error) => {
1279
1225
  logException(
1280
1226
  error,
@@ -3529,6 +3475,12 @@ function isSlackTitlePermissionError(error) {
3529
3475
  function escapeRegExp(value) {
3530
3476
  return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
3531
3477
  }
3478
+ function readObjectField(value, key) {
3479
+ return value[key];
3480
+ }
3481
+ function readOptionalStringField(value, key) {
3482
+ return toOptionalString(readObjectField(value, key));
3483
+ }
3532
3484
  function stripLeadingBotMention(text, options = {}) {
3533
3485
  if (!text.trim()) return text;
3534
3486
  let next = text;
@@ -3560,7 +3512,7 @@ function getThreadId(thread, _message) {
3560
3512
  return toOptionalString(thread.id);
3561
3513
  }
3562
3514
  function getRunId(thread, message) {
3563
- return toOptionalString(thread.runId) ?? toOptionalString(message.runId);
3515
+ return readOptionalStringField(thread, "runId") ?? readOptionalStringField(message, "runId");
3564
3516
  }
3565
3517
  function getChannelId(thread, message) {
3566
3518
  return resolveSlackChannelIdFromThreadId(toOptionalString(thread.id)) ?? normalizeSlackConversationId(toOptionalString(thread.channelId)) ?? resolveSlackChannelIdFromMessage(message);
@@ -3569,18 +3521,16 @@ function getThreadTs(threadId) {
3569
3521
  return parseSlackThreadId(threadId)?.threadTs;
3570
3522
  }
3571
3523
  function getAssistantThreadContext(message) {
3572
- const raw = message.raw;
3573
- const rawRecord = raw && typeof raw === "object" ? raw : void 0;
3574
- const channelId = toOptionalString(rawRecord?.channel);
3524
+ const rawContext = readSlackRawMessageContext(message);
3525
+ const channelId = rawContext?.channelId;
3575
3526
  if (channelId) {
3576
- const rawThreadTs = toOptionalString(rawRecord?.thread_ts);
3577
- const threadTs = isDmChannel(channelId) ? rawThreadTs : rawThreadTs ?? toOptionalString(rawRecord?.ts);
3527
+ const threadTs = isDmChannel(channelId) ? rawContext.threadTs : rawContext.threadTs ?? rawContext.messageTs;
3578
3528
  if (threadTs) {
3579
3529
  return { channelId, threadTs };
3580
3530
  }
3581
3531
  }
3582
3532
  const parsedThreadId = parseSlackThreadId(
3583
- toOptionalString(message.threadId)
3533
+ readOptionalStringField(message, "threadId")
3584
3534
  );
3585
3535
  if (!parsedThreadId || isDmChannel(parsedThreadId.channelId)) {
3586
3536
  return void 0;
@@ -3588,22 +3538,16 @@ function getAssistantThreadContext(message) {
3588
3538
  return parsedThreadId;
3589
3539
  }
3590
3540
  function getMessageTs(message) {
3591
- const directTs = toOptionalString(
3592
- message.ts
3593
- );
3541
+ const directTs = readOptionalStringField(message, "ts");
3594
3542
  const parsedDirectTs = parseSlackMessageTs(directTs);
3595
3543
  if (parsedDirectTs) {
3596
3544
  return parsedDirectTs;
3597
3545
  }
3598
- const raw = message.raw;
3599
- if (!raw || typeof raw !== "object") {
3546
+ const rawContext = readSlackRawMessageContext(message);
3547
+ if (!rawContext) {
3600
3548
  return void 0;
3601
3549
  }
3602
- const rawRecord = raw;
3603
- const candidates = [
3604
- rawRecord.ts,
3605
- rawRecord.message?.ts
3606
- ];
3550
+ const candidates = [rawContext.messageTs, rawContext.nestedMessageTs];
3607
3551
  for (const candidate of candidates) {
3608
3552
  const ts = parseSlackMessageTs(candidate);
3609
3553
  if (ts) {
@@ -3623,7 +3567,7 @@ function isProcessingReactionEmoji(value) {
3623
3567
  return typeof value === "string" && normalizeSlackEmojiName(value) === getChatConfig().slack.processingReactionEmoji;
3624
3568
  }
3625
3569
  function shouldKeepProcessingReactionForToolInvocation(input) {
3626
- return input.toolName === "slackMessageAddReaction" && isProcessingReactionEmoji(input.params.emoji);
3570
+ return input.toolName === "addReaction" && isProcessingReactionEmoji(input.params.emoji);
3627
3571
  }
3628
3572
  async function startSlackProcessingReaction(args) {
3629
3573
  if (args.message.author.isMe) {
@@ -3824,19 +3768,20 @@ function getDefaultLockKey(channelId, threadTs) {
3824
3768
  return `slack:${channelId}:${threadTs}`;
3825
3769
  }
3826
3770
  function getResumeLogContext(args, lockKey) {
3771
+ const routing = args.replyContext?.routing;
3827
3772
  return {
3828
- conversationId: args.replyContext?.correlation?.conversationId ?? lockKey,
3829
- slackThreadId: args.replyContext?.correlation?.threadId ?? lockKey,
3830
- slackUserId: args.replyContext?.requester?.userId ?? args.replyContext?.correlation?.requesterId,
3831
- slackUserName: args.replyContext?.requester?.userName,
3773
+ conversationId: routing?.correlation?.conversationId ?? lockKey,
3774
+ slackThreadId: routing?.correlation?.threadId ?? lockKey,
3775
+ slackUserId: routing?.requester?.userId ?? routing?.correlation?.requesterId,
3776
+ slackUserName: routing?.requester?.userName,
3832
3777
  slackChannelId: args.channelId,
3833
- runId: args.replyContext?.correlation?.runId,
3778
+ runId: routing?.correlation?.runId,
3834
3779
  assistantUserName: botConfig.userName,
3835
3780
  modelId: botConfig.modelId
3836
3781
  };
3837
3782
  }
3838
3783
  function getResumeConversationId(args, lockKey) {
3839
- return args.replyContext?.correlation?.conversationId ?? lockKey;
3784
+ return args.replyContext?.routing.correlation?.conversationId ?? lockKey;
3840
3785
  }
3841
3786
  async function postResumeFailureReply(args) {
3842
3787
  try {
@@ -3881,42 +3826,58 @@ function createResumeReplyContext(args, statusSession) {
3881
3826
  if (!replyContext) {
3882
3827
  throw new TypeError("Slack resume requires a reply context");
3883
3828
  }
3884
- if (!replyContext.source) {
3829
+ if (!replyContext.routing.source) {
3885
3830
  throw new TypeError("Slack resume requires a reply context source");
3886
3831
  }
3887
- const source = replyContext.source;
3888
- if (replyContext.destination.platform !== "slack") {
3832
+ const source = replyContext.routing.source;
3833
+ if (replyContext.routing.destination.platform !== "slack") {
3889
3834
  throw new TypeError("Slack resume requires a Slack destination");
3890
3835
  }
3891
3836
  const requestDeadline = getTurnRequestDeadline();
3892
3837
  const threadId = args.lockKey ?? getDefaultLockKey(args.channelId, args.threadTs);
3893
- const persistedChannelConfiguration = replyContext.channelConfiguration ?? (replyContext.configuration ? createReadOnlyConfigService(replyContext.configuration) : void 0);
3838
+ const persistedChannelConfiguration = replyContext.policy?.channelConfiguration ?? (replyContext.policy?.configuration ? createReadOnlyConfigService(replyContext.policy.configuration) : void 0);
3894
3839
  return {
3895
- ...replyContext,
3896
- source,
3897
- turnDeadlineAtMs: replyContext.turnDeadlineAtMs ?? requestDeadline?.deadlineAtMs,
3898
- correlation: {
3899
- ...replyContext.correlation,
3900
- threadId: replyContext.correlation?.threadId ?? threadId,
3901
- channelId: replyContext.correlation?.channelId ?? args.channelId,
3902
- threadTs: replyContext.correlation?.threadTs ?? args.threadTs,
3903
- requesterId: replyContext.correlation?.requesterId ?? replyContext.requester?.userId
3840
+ input: {
3841
+ ...replyContext.input ?? {},
3842
+ messageText: args.messageText
3904
3843
  },
3905
- channelConfiguration: persistedChannelConfiguration,
3906
- onSandboxAcquired: async (sandbox) => {
3907
- await persistThreadStateById(threadId, {
3908
- sandboxId: sandbox.sandboxId,
3909
- sandboxDependencyProfileHash: sandbox.sandboxDependencyProfileHash
3910
- });
3911
- await replyContext.onSandboxAcquired?.(sandbox);
3844
+ routing: {
3845
+ ...replyContext.routing,
3846
+ source,
3847
+ correlation: {
3848
+ ...replyContext.routing.correlation,
3849
+ threadId: replyContext.routing.correlation?.threadId ?? threadId,
3850
+ channelId: replyContext.routing.correlation?.channelId ?? args.channelId,
3851
+ threadTs: replyContext.routing.correlation?.threadTs ?? args.threadTs,
3852
+ requesterId: replyContext.routing.correlation?.requesterId ?? replyContext.routing.requester?.userId
3853
+ }
3854
+ },
3855
+ policy: {
3856
+ ...replyContext.policy,
3857
+ turnDeadlineAtMs: replyContext.policy?.turnDeadlineAtMs ?? requestDeadline?.deadlineAtMs,
3858
+ channelConfiguration: persistedChannelConfiguration
3912
3859
  },
3913
- onArtifactStateUpdated: async (artifacts) => {
3914
- await persistThreadStateById(threadId, { artifacts });
3915
- await replyContext.onArtifactStateUpdated?.(artifacts);
3860
+ state: replyContext.state,
3861
+ observers: {
3862
+ ...replyContext.observers,
3863
+ onStatus: async (nextStatus) => {
3864
+ statusSession.update(nextStatus);
3865
+ await replyContext.observers?.onStatus?.(nextStatus);
3866
+ }
3916
3867
  },
3917
- onStatus: async (nextStatus) => {
3918
- statusSession.update(nextStatus);
3919
- await replyContext.onStatus?.(nextStatus);
3868
+ durability: {
3869
+ ...replyContext.durability,
3870
+ onSandboxAcquired: async (sandbox) => {
3871
+ await persistThreadStateById(threadId, {
3872
+ sandboxId: sandbox.sandboxId,
3873
+ sandboxDependencyProfileHash: sandbox.sandboxDependencyProfileHash
3874
+ });
3875
+ await replyContext.durability?.onSandboxAcquired?.(sandbox);
3876
+ },
3877
+ onArtifactStateUpdated: async (artifacts) => {
3878
+ await persistThreadStateById(threadId, { artifacts });
3879
+ await replyContext.durability?.onArtifactStateUpdated?.(artifacts);
3880
+ }
3920
3881
  }
3921
3882
  };
3922
3883
  }
@@ -3948,16 +3909,18 @@ async function resumeSlackTurn(args) {
3948
3909
  if (preparedArgs) {
3949
3910
  runArgs = { ...args, ...preparedArgs };
3950
3911
  }
3951
- if (!runArgs.replyContext?.requester?.userId) {
3952
- throw new Error("Resumed turn requires replyContext.requester.userId");
3912
+ if (!runArgs.replyContext?.routing.requester?.userId) {
3913
+ throw new Error(
3914
+ "Resumed turn requires replyContext.routing.requester.userId"
3915
+ );
3953
3916
  }
3954
- const credentialContext = runArgs.replyContext.credentialContext;
3917
+ const credentialContext = runArgs.replyContext.routing.credentialContext;
3955
3918
  if (!credentialContext) {
3956
3919
  throw new Error("Resumed turn requires replyContext.credentialContext");
3957
3920
  }
3958
- if (credentialContext.actor.type !== "user" || credentialContext.actor.userId !== runArgs.replyContext.requester.userId) {
3921
+ if (credentialContext.actor.type !== "user" || credentialContext.actor.userId !== runArgs.replyContext.routing.requester.userId) {
3959
3922
  throw new Error(
3960
- "Resumed turn credential actor must match replyContext.requester.userId"
3923
+ "Resumed turn credential actor must match replyContext.routing.requester.userId"
3961
3924
  );
3962
3925
  }
3963
3926
  if (runArgs.messageTs) {
@@ -3976,87 +3939,119 @@ async function resumeSlackTurn(args) {
3976
3939
  );
3977
3940
  }
3978
3941
  status.start();
3979
- const generateReply = runArgs.generateReply ?? generateAssistantReply;
3980
3942
  const replyContext = createResumeReplyContext(runArgs, status);
3981
- const replyPromise = generateReply(runArgs.messageText, replyContext);
3943
+ const replyPromise = runArgs.agentRunner.run(replyContext);
3982
3944
  const replyTimeoutMs = resolveReplyTimeoutMs(runArgs.replyTimeoutMs);
3983
- let reply = typeof replyTimeoutMs === "number" ? await Promise.race([
3945
+ const outcome = typeof replyTimeoutMs === "number" ? await Promise.race([
3984
3946
  replyPromise,
3985
3947
  new Promise(
3986
3948
  (_, reject) => setTimeout(
3987
3949
  () => reject(
3988
3950
  new Error(
3989
- `generateAssistantReply timed out after ${replyTimeoutMs}ms`
3951
+ `executeAgentRun timed out after ${replyTimeoutMs}ms`
3990
3952
  )
3991
3953
  ),
3992
3954
  replyTimeoutMs
3993
3955
  )
3994
3956
  )
3995
3957
  ]) : await replyPromise;
3996
- reply = finalizeFailedTurnReply({
3997
- reply,
3998
- logException,
3999
- context: getResumeLogContext(runArgs, lockKey)
4000
- });
4001
- await status.stop();
4002
- const footer = buildSlackReplyFooter({
4003
- conversationId: getResumeConversationId(runArgs, lockKey)
4004
- });
4005
- await postSlackApiReplyPosts({
4006
- channelId: runArgs.channelId,
4007
- threadTs: runArgs.threadTs,
4008
- posts: planSlackReplyPosts({ reply }),
4009
- fileUploadFailureMode: "best_effort",
4010
- footer
4011
- });
4012
- finalReplyDelivered = true;
4013
- if (replyContext.correlation?.conversationId && replyContext.correlation.turnId && reply.piMessages?.length) {
4014
- await persistCompletedSessionRecord({
4015
- conversationId: replyContext.correlation.conversationId,
4016
- sessionId: replyContext.correlation.turnId,
4017
- allMessages: reply.piMessages,
4018
- currentDurationMs: reply.diagnostics.durationMs,
4019
- currentUsage: reply.diagnostics.usage,
4020
- destination: replyContext.destination,
4021
- source: replyContext.source,
4022
- requester: replyContext.requester,
4023
- surface: "slack",
4024
- logContext: {
4025
- threadId: replyContext.correlation.threadId,
4026
- requesterId: replyContext.requester?.userId,
4027
- channelId: runArgs.channelId,
4028
- runId: replyContext.correlation.runId,
4029
- assistantUserName: botConfig.userName,
4030
- modelId: reply.diagnostics.modelId
4031
- }
4032
- });
4033
- }
4034
- await runArgs.onSuccess?.(reply);
4035
- if (reply.diagnostics.outcome === "success" && replyContext.correlation?.conversationId && replyContext.correlation.turnId) {
4036
- try {
4037
- const params = {
4038
- conversationId: replyContext.correlation.conversationId,
4039
- sessionId: replyContext.correlation.turnId
3958
+ if (outcome.status !== "completed") {
3959
+ await status.stop();
3960
+ const onAuthPause = runArgs.onAuthPause;
3961
+ const onTimeoutPause = runArgs.onTimeoutPause;
3962
+ if (outcome.status === "awaiting_auth" && onAuthPause) {
3963
+ deferredPauseKind = "auth";
3964
+ deferredAuthInfo = {
3965
+ providerDisplayName: outcome.providerDisplayName,
3966
+ requesterId: runArgs.replyContext?.routing.requester?.userId
4040
3967
  };
4041
- if (runArgs.scheduleSessionCompletedPluginTasks) {
4042
- await runArgs.scheduleSessionCompletedPluginTasks(params);
4043
- } else {
4044
- await scheduleSessionCompletedPluginTasks(params);
3968
+ deferredPauseHandler = async () => {
3969
+ await onAuthPause({
3970
+ providerDisplayName: outcome.providerDisplayName
3971
+ });
3972
+ };
3973
+ } else if (outcome.status === "suspended" && onTimeoutPause) {
3974
+ deferredPauseKind = "timeout";
3975
+ deferredPauseHandler = async () => {
3976
+ await onTimeoutPause({ resumeVersion: outcome.resumeVersion });
3977
+ };
3978
+ } else {
3979
+ deferredFailureHandler = async () => {
3980
+ await handleResumeFailure({
3981
+ body: "Failed to resume Slack turn",
3982
+ error: new Error(
3983
+ `Resumed run ended ${outcome.status} without a pause handler`
3984
+ ),
3985
+ eventName: "slack_resume_turn_failed",
3986
+ lockKey,
3987
+ resumeArgs: runArgs
3988
+ });
3989
+ };
3990
+ }
3991
+ } else {
3992
+ let reply = outcome.result;
3993
+ reply = finalizeFailedTurnReply({
3994
+ reply,
3995
+ logException,
3996
+ context: getResumeLogContext(runArgs, lockKey)
3997
+ });
3998
+ await status.stop();
3999
+ const footer = buildSlackReplyFooter({
4000
+ conversationId: getResumeConversationId(runArgs, lockKey)
4001
+ });
4002
+ await postSlackApiReplyPosts({
4003
+ channelId: runArgs.channelId,
4004
+ threadTs: runArgs.threadTs,
4005
+ posts: planSlackReplyPosts({ reply }),
4006
+ footer
4007
+ });
4008
+ finalReplyDelivered = true;
4009
+ if (replyContext.routing.correlation?.conversationId && replyContext.routing.correlation.turnId && reply.piMessages?.length) {
4010
+ await persistCompletedSessionRecord({
4011
+ conversationId: replyContext.routing.correlation.conversationId,
4012
+ sessionId: replyContext.routing.correlation.turnId,
4013
+ allMessages: reply.piMessages,
4014
+ currentDurationMs: reply.diagnostics.durationMs,
4015
+ currentUsage: reply.diagnostics.usage,
4016
+ destination: replyContext.routing.destination,
4017
+ source: replyContext.routing.source,
4018
+ requester: replyContext.routing.requester,
4019
+ surface: "slack",
4020
+ logContext: {
4021
+ threadId: replyContext.routing.correlation.threadId,
4022
+ requesterId: replyContext.routing.requester?.userId,
4023
+ channelId: runArgs.channelId,
4024
+ runId: replyContext.routing.correlation.runId,
4025
+ assistantUserName: botConfig.userName,
4026
+ modelId: reply.diagnostics.modelId
4027
+ }
4028
+ });
4029
+ }
4030
+ await runArgs.onSuccess?.(reply);
4031
+ if (reply.diagnostics.outcome === "success" && replyContext.routing.correlation?.conversationId && replyContext.routing.correlation.turnId) {
4032
+ try {
4033
+ const params = {
4034
+ conversationId: replyContext.routing.correlation.conversationId,
4035
+ sessionId: replyContext.routing.correlation.turnId
4036
+ };
4037
+ if (runArgs.scheduleSessionCompletedPluginTasks) {
4038
+ await runArgs.scheduleSessionCompletedPluginTasks(params);
4039
+ } else {
4040
+ await scheduleSessionCompletedPluginTasks(params);
4041
+ }
4042
+ } catch (scheduleError) {
4043
+ logException(
4044
+ scheduleError,
4045
+ "plugin_session_completed_task_schedule_failed",
4046
+ getResumeLogContext(runArgs, lockKey),
4047
+ {},
4048
+ "Plugin session.completed task scheduling failed"
4049
+ );
4045
4050
  }
4046
- } catch (scheduleError) {
4047
- logException(
4048
- scheduleError,
4049
- "plugin_session_completed_task_schedule_failed",
4050
- getResumeLogContext(runArgs, lockKey),
4051
- {},
4052
- "Plugin session.completed task scheduling failed"
4053
- );
4054
4051
  }
4055
4052
  }
4056
4053
  } catch (error) {
4057
4054
  await status.stop();
4058
- const onAuthPause = runArgs.onAuthPause;
4059
- const onTimeoutPause = runArgs.onTimeoutPause;
4060
4055
  if (finalReplyDelivered) {
4061
4056
  postDeliveryCommitError = error;
4062
4057
  try {
@@ -4070,21 +4065,6 @@ async function resumeSlackTurn(args) {
4070
4065
  "Failed to terminalize resumed turn after post-delivery commit failure"
4071
4066
  );
4072
4067
  }
4073
- } else if (isAuthResumeRetryableTurnError(error) && onAuthPause) {
4074
- deferredPauseKind = "auth";
4075
- deferredAuthInfo = {
4076
- providerDisplayName: error.metadata.authProviderDisplayName,
4077
- // The try body validates requester.userId; catch scope does not retain that narrowing.
4078
- requesterId: runArgs.replyContext?.requester?.userId
4079
- };
4080
- deferredPauseHandler = async () => {
4081
- await onAuthPause(error);
4082
- };
4083
- } else if (isRetryableTurnError(error, "agent_continue") && onTimeoutPause) {
4084
- deferredPauseKind = "timeout";
4085
- deferredPauseHandler = async () => {
4086
- await onTimeoutPause(error);
4087
- };
4088
4068
  } else {
4089
4069
  deferredFailureHandler = async () => {
4090
4070
  await handleResumeFailure({
@@ -4157,7 +4137,7 @@ async function resumeAuthorizedRequest(args) {
4157
4137
  replyContext: args.replyContext,
4158
4138
  lockKey: args.lockKey,
4159
4139
  initialText: args.connectedText,
4160
- generateReply: args.generateReply,
4140
+ agentRunner: args.agentRunner,
4161
4141
  onSuccess: args.onSuccess,
4162
4142
  onFailure: args.onFailure,
4163
4143
  onAuthPause: args.onAuthPause,
@@ -4354,7 +4334,7 @@ async function persistFailedReplyState(channelId, threadTs, sessionId, expectedV
4354
4334
  });
4355
4335
  }
4356
4336
  async function resumeAuthorizedMcpTurn(args) {
4357
- const { authSession, generateReply, provider } = args;
4337
+ const { authSession, agentRunner, provider } = args;
4358
4338
  if (!authSession.channelId || !authSession.destination || !authSession.threadTs) {
4359
4339
  return;
4360
4340
  }
@@ -4397,7 +4377,7 @@ async function resumeAuthorizedMcpTurn(args) {
4397
4377
  messageTs: getTurnUserSlackMessageTs(userMessage2),
4398
4378
  lockKey: threadId,
4399
4379
  connectedText: "",
4400
- generateReply,
4380
+ agentRunner,
4401
4381
  beforeStart: async () => {
4402
4382
  const lockedState = await getPersistedThreadState(threadId);
4403
4383
  const lockedConversation = coerceThreadConversationState(lockedState);
@@ -4492,38 +4472,48 @@ async function resumeAuthorizedMcpTurn(args) {
4492
4472
  messageText: lockedUserMessage.text,
4493
4473
  messageTs: lockedMessageTs,
4494
4474
  replyContext: {
4495
- credentialContext: {
4496
- actor: { type: "user", userId: requester.userId }
4497
- },
4498
- requester,
4499
- destination,
4500
- source: lockedSessionRecord.source,
4501
- correlation: {
4502
- conversationId: authSession.conversationId,
4503
- turnId: lockedSessionId,
4504
- channelId: authSession.channelId,
4505
- threadTs: authSession.threadTs,
4506
- requesterId: requester.userId
4475
+ input: {
4476
+ conversationContext: lockedConversationContext,
4477
+ piMessages: lockedConversation.piMessages,
4478
+ ...getTurnUserReplyAttachmentContext(lockedUserMessage)
4507
4479
  },
4508
- toolChannelId: authSession.toolChannelId ?? lockedArtifacts.assistantContextChannelId ?? authSession.channelId,
4509
- conversationContext: lockedConversationContext,
4510
- artifactState: lockedArtifacts,
4511
- piMessages: lockedConversation.piMessages,
4512
- configuration: authSession.configuration,
4513
- pendingAuth: lockedPendingAuth,
4514
- channelConfiguration: lockedChannelConfiguration,
4515
- sandbox: getPersistedSandboxState(lockedState),
4516
- recordPendingAuth: async (nextPendingAuth) => {
4517
- await applyPendingAuthUpdate({
4518
- conversation: lockedConversation,
4480
+ routing: {
4481
+ credentialContext: {
4482
+ actor: { type: "user", userId: requester.userId }
4483
+ },
4484
+ requester,
4485
+ destination,
4486
+ source: lockedSessionRecord.source,
4487
+ correlation: {
4519
4488
  conversationId: authSession.conversationId,
4520
- nextPendingAuth
4521
- });
4522
- await persistThreadStateById(threadId, {
4523
- conversation: lockedConversation
4524
- });
4489
+ turnId: lockedSessionId,
4490
+ channelId: authSession.channelId,
4491
+ threadTs: authSession.threadTs,
4492
+ requesterId: requester.userId
4493
+ },
4494
+ toolChannelId: authSession.toolChannelId ?? lockedArtifacts.assistantContextChannelId ?? authSession.channelId
4495
+ },
4496
+ policy: {
4497
+ configuration: authSession.configuration,
4498
+ channelConfiguration: lockedChannelConfiguration
4499
+ },
4500
+ state: {
4501
+ artifactState: lockedArtifacts,
4502
+ pendingAuth: lockedPendingAuth,
4503
+ sandbox: getPersistedSandboxState(lockedState)
4525
4504
  },
4526
- ...getTurnUserReplyAttachmentContext(lockedUserMessage)
4505
+ durability: {
4506
+ recordPendingAuth: async (nextPendingAuth) => {
4507
+ await applyPendingAuthUpdate({
4508
+ conversation: lockedConversation,
4509
+ conversationId: authSession.conversationId,
4510
+ nextPendingAuth
4511
+ });
4512
+ await persistThreadStateById(threadId, {
4513
+ conversation: lockedConversation
4514
+ });
4515
+ }
4516
+ }
4527
4517
  },
4528
4518
  onSuccess: async (reply) => {
4529
4519
  await persistCompletedReplyState(
@@ -4559,7 +4549,7 @@ async function resumeAuthorizedMcpTurn(args) {
4559
4549
  );
4560
4550
  }
4561
4551
  },
4562
- onAuthPause: async (error) => {
4552
+ onAuthPause: async () => {
4563
4553
  await persistAuthPauseTurnState({
4564
4554
  sessionId: lockedSessionId,
4565
4555
  threadStateId: threadId
@@ -4567,35 +4557,23 @@ async function resumeAuthorizedMcpTurn(args) {
4567
4557
  logWarn(
4568
4558
  "mcp_oauth_callback_resume_reparked_for_auth",
4569
4559
  {},
4570
- {
4571
- "app.credential.provider": provider,
4572
- ...isRetryableTurnError(error) ? { "app.ai.retryable_reason": error.reason } : {}
4573
- },
4560
+ { "app.credential.provider": provider },
4574
4561
  "Resumed MCP turn requested another authorization flow"
4575
4562
  );
4576
4563
  },
4577
- onTimeoutPause: async (error) => {
4578
- if (!isRetryableTurnError(error, "agent_continue")) {
4579
- throw error;
4580
- }
4581
- const version = error.metadata?.version;
4582
- if (typeof version !== "number") {
4583
- throw new Error(
4584
- "MCP OAuth agent continuation did not include a session record version"
4585
- );
4586
- }
4564
+ onTimeoutPause: async ({ resumeVersion }) => {
4587
4565
  await scheduleAgentContinue({
4588
4566
  conversationId: authSession.conversationId,
4589
4567
  destination,
4590
4568
  sessionId: lockedSessionId,
4591
- expectedVersion: version
4569
+ expectedVersion: resumeVersion
4592
4570
  });
4593
4571
  }
4594
4572
  };
4595
4573
  }
4596
4574
  });
4597
4575
  }
4598
- async function GET3(request, provider, waitUntil, options = {}) {
4576
+ async function GET3(request, provider, waitUntil, options) {
4599
4577
  const url = new URL(request.url);
4600
4578
  const state = url.searchParams.get("state")?.trim();
4601
4579
  const code = url.searchParams.get("code")?.trim();
@@ -4625,7 +4603,7 @@ async function GET3(request, provider, waitUntil, options = {}) {
4625
4603
  waitUntil(
4626
4604
  () => resumeAuthorizedMcpTurn({
4627
4605
  authSession,
4628
- generateReply: options.generateReply,
4606
+ agentRunner: options.agentRunner,
4629
4607
  provider
4630
4608
  })
4631
4609
  );
@@ -4927,7 +4905,7 @@ async function resumeOAuthSessionRecordTurn(stored, options) {
4927
4905
  messageTs: getTurnUserSlackMessageTs(userMessage2),
4928
4906
  lockKey: stored.resumeConversationId,
4929
4907
  initialText: "",
4930
- generateReply: options.generateReply,
4908
+ agentRunner: options.agentRunner,
4931
4909
  beforeStart: async () => {
4932
4910
  const lockedState = await getPersistedThreadState(
4933
4911
  stored.resumeConversationId
@@ -5025,40 +5003,50 @@ async function resumeOAuthSessionRecordTurn(stored, options) {
5025
5003
  messageText: lockedPendingAuth ? lockedUserMessage.text : stored.pendingMessage ?? lockedUserMessage.text,
5026
5004
  messageTs: lockedMessageTs,
5027
5005
  replyContext: {
5028
- credentialContext: {
5029
- actor: {
5030
- type: "user",
5031
- userId: requester.userId
5032
- }
5006
+ input: {
5007
+ conversationContext: lockedConversationContext,
5008
+ piMessages: lockedConversation.piMessages,
5009
+ ...getTurnUserReplyAttachmentContext(lockedUserMessage)
5033
5010
  },
5034
- requester,
5035
- destination,
5036
- source: lockedSessionRecord.source,
5037
- correlation: {
5038
- conversationId: stored.resumeConversationId,
5039
- turnId: lockedSessionId,
5040
- channelId: stored.channelId,
5041
- threadTs: stored.threadTs,
5042
- requesterId: requester.userId
5043
- },
5044
- toolChannelId: lockedArtifacts.assistantContextChannelId ?? stored.channelId,
5045
- artifactState: lockedArtifacts,
5046
- pendingAuth: lockedPendingAuth,
5047
- conversationContext: lockedConversationContext,
5048
- channelConfiguration: lockedChannelConfiguration,
5049
- piMessages: lockedConversation.piMessages,
5050
- sandbox: getPersistedSandboxState(lockedState),
5051
- recordPendingAuth: async (nextPendingAuth) => {
5052
- await applyPendingAuthUpdate({
5053
- conversation: lockedConversation,
5011
+ routing: {
5012
+ credentialContext: {
5013
+ actor: {
5014
+ type: "user",
5015
+ userId: requester.userId
5016
+ }
5017
+ },
5018
+ requester,
5019
+ destination,
5020
+ source: lockedSessionRecord.source,
5021
+ correlation: {
5054
5022
  conversationId: stored.resumeConversationId,
5055
- nextPendingAuth
5056
- });
5057
- await persistThreadStateById(stored.resumeConversationId, {
5058
- conversation: lockedConversation
5059
- });
5023
+ turnId: lockedSessionId,
5024
+ channelId: stored.channelId,
5025
+ threadTs: stored.threadTs,
5026
+ requesterId: requester.userId
5027
+ },
5028
+ toolChannelId: lockedArtifacts.assistantContextChannelId ?? stored.channelId
5060
5029
  },
5061
- ...getTurnUserReplyAttachmentContext(lockedUserMessage)
5030
+ policy: {
5031
+ channelConfiguration: lockedChannelConfiguration
5032
+ },
5033
+ state: {
5034
+ artifactState: lockedArtifacts,
5035
+ pendingAuth: lockedPendingAuth,
5036
+ sandbox: getPersistedSandboxState(lockedState)
5037
+ },
5038
+ durability: {
5039
+ recordPendingAuth: async (nextPendingAuth) => {
5040
+ await applyPendingAuthUpdate({
5041
+ conversation: lockedConversation,
5042
+ conversationId: stored.resumeConversationId,
5043
+ nextPendingAuth
5044
+ });
5045
+ await persistThreadStateById(stored.resumeConversationId, {
5046
+ conversation: lockedConversation
5047
+ });
5048
+ }
5049
+ }
5062
5050
  },
5063
5051
  onSuccess: async (reply) => {
5064
5052
  logInfo(
@@ -5098,21 +5086,12 @@ async function resumeOAuthSessionRecordTurn(stored, options) {
5098
5086
  threadStateId: stored.resumeConversationId
5099
5087
  });
5100
5088
  },
5101
- onTimeoutPause: async (error) => {
5102
- if (!isRetryableTurnError(error, "agent_continue")) {
5103
- throw error;
5104
- }
5105
- const version = error.metadata?.version;
5106
- if (typeof version !== "number") {
5107
- throw new Error(
5108
- "OAuth agent continuation did not include a session record version"
5109
- );
5110
- }
5089
+ onTimeoutPause: async ({ resumeVersion }) => {
5111
5090
  await scheduleAgentContinue({
5112
5091
  conversationId: stored.resumeConversationId,
5113
5092
  destination,
5114
5093
  sessionId: lockedSessionId,
5115
- expectedVersion: version
5094
+ expectedVersion: resumeVersion
5116
5095
  });
5117
5096
  }
5118
5097
  };
@@ -5148,23 +5127,29 @@ async function resumePendingOAuthMessage(stored, options) {
5148
5127
  threadTs: stored.threadTs,
5149
5128
  messageTs,
5150
5129
  connectedText: "",
5151
- generateReply: options.generateReply,
5130
+ agentRunner: options.agentRunner,
5152
5131
  replyContext: {
5153
- credentialContext: {
5154
- actor: { type: "user", userId: stored.userId }
5132
+ input: {
5133
+ conversationContext,
5134
+ piMessages: conversation.piMessages
5155
5135
  },
5156
- requester,
5157
- destination: stored.destination,
5158
- source,
5159
- correlation: {
5160
- conversationId: threadId,
5161
- channelId: stored.channelId,
5162
- threadTs: stored.threadTs,
5163
- requesterId: stored.userId
5136
+ routing: {
5137
+ credentialContext: {
5138
+ actor: { type: "user", userId: stored.userId }
5139
+ },
5140
+ requester,
5141
+ destination: stored.destination,
5142
+ source,
5143
+ correlation: {
5144
+ conversationId: threadId,
5145
+ channelId: stored.channelId,
5146
+ threadTs: stored.threadTs,
5147
+ requesterId: stored.userId
5148
+ }
5164
5149
  },
5165
- conversationContext,
5166
- piMessages: conversation.piMessages,
5167
- configuration: stored.configuration
5150
+ policy: {
5151
+ configuration: stored.configuration
5152
+ }
5168
5153
  },
5169
5154
  onSuccess: async (reply) => {
5170
5155
  logInfo(
@@ -5180,7 +5165,7 @@ async function resumePendingOAuthMessage(stored, options) {
5180
5165
  }
5181
5166
  });
5182
5167
  }
5183
- async function GET4(request, provider, waitUntil, options = {}) {
5168
+ async function GET4(request, provider, waitUntil, options) {
5184
5169
  const providerConfig = pluginCatalogRuntime.getOAuthConfig(provider);
5185
5170
  if (!providerConfig) {
5186
5171
  return htmlErrorResponse(
@@ -7532,7 +7517,7 @@ function getQueuedMessages(context, options) {
7532
7517
  stripLeadingSlackMentionToken: options.explicitMention || Boolean(message.isMention)
7533
7518
  });
7534
7519
  return {
7535
- explicitMention: options.explicitMention || Boolean(message.isMention),
7520
+ explicitMention: Boolean(message.isMention),
7536
7521
  message,
7537
7522
  rawText: appendSlackLegacyAttachmentText(message.text, message.raw),
7538
7523
  userText: appendSlackLegacyAttachmentText(stripped, message.raw)
@@ -7908,16 +7893,6 @@ function createSlackTurnRuntime(deps) {
7908
7893
  channelId: deps.getChannelId(thread, message),
7909
7894
  runId: deps.getRunId(thread, message)
7910
7895
  });
7911
- if (isRetryableTurnError(error, "mcp_auth_resume") || isRetryableTurnError(error, "plugin_auth_resume")) {
7912
- deps.logException(
7913
- error,
7914
- "mention_handler_auth_pause",
7915
- errorContext,
7916
- { "app.ai.retryable_reason": error.reason },
7917
- "onNewMention parked turn for auth resume"
7918
- );
7919
- return;
7920
- }
7921
7896
  if (error instanceof AuthorizationFlowDisabledError) {
7922
7897
  return;
7923
7898
  }
@@ -8178,16 +8153,6 @@ function createSlackTurnRuntime(deps) {
8178
8153
  channelId: deps.getChannelId(thread, message),
8179
8154
  runId: deps.getRunId(thread, message)
8180
8155
  });
8181
- if (isRetryableTurnError(error, "mcp_auth_resume") || isRetryableTurnError(error, "plugin_auth_resume")) {
8182
- deps.logException(
8183
- error,
8184
- "subscribed_message_handler_auth_pause",
8185
- errorContext,
8186
- { "app.ai.retryable_reason": error.reason },
8187
- "onSubscribedMessage parked turn for auth resume"
8188
- );
8189
- return;
8190
- }
8191
8156
  if (error instanceof AuthorizationFlowDisabledError) {
8192
8157
  return;
8193
8158
  }
@@ -8601,7 +8566,7 @@ function createSubscribedReplyPolicy(deps) {
8601
8566
  };
8602
8567
  }
8603
8568
 
8604
- // src/chat/services/vision-context.ts
8569
+ // src/chat/slack/vision-context.ts
8605
8570
  var MAX_USER_ATTACHMENTS = 3;
8606
8571
  var MAX_USER_ATTACHMENT_BYTES = 5 * 1024 * 1024;
8607
8572
  var MAX_MESSAGE_IMAGE_ATTACHMENTS = 3;
@@ -8907,6 +8872,14 @@ async function hydrateConversationVisionContextWithDeps(conversation, context, d
8907
8872
  if (!context.channelId || !context.threadTs) {
8908
8873
  return;
8909
8874
  }
8875
+ const channelId = parseSlackChannelId(context.channelId);
8876
+ if (!channelId) {
8877
+ return;
8878
+ }
8879
+ const threadTs = parseSlackMessageTs(context.threadTs);
8880
+ if (!threadTs) {
8881
+ return;
8882
+ }
8910
8883
  const messagesByTs = /* @__PURE__ */ new Map();
8911
8884
  for (const message of conversation.messages) {
8912
8885
  if (!isHumanConversationMessage(message)) continue;
@@ -8921,8 +8894,8 @@ async function hydrateConversationVisionContextWithDeps(conversation, context, d
8921
8894
  let replies;
8922
8895
  try {
8923
8896
  replies = await deps.listThreadReplies({
8924
- channelId: context.channelId,
8925
- threadTs: context.threadTs,
8897
+ channelId,
8898
+ threadTs,
8926
8899
  limit: 1e3,
8927
8900
  maxPages: 10,
8928
8901
  targetMessageTs: [...messagesByTs.keys()]
@@ -9120,15 +9093,6 @@ function createVisionContextService(deps) {
9120
9093
  }
9121
9094
 
9122
9095
  // src/chat/app/services.ts
9123
- function withSandboxTracePropagation(generateReply, tracePropagation) {
9124
- return async (messageText2, context) => await generateReply(messageText2, {
9125
- ...context,
9126
- sandbox: {
9127
- ...context?.sandbox,
9128
- tracePropagation: context?.sandbox?.tracePropagation ?? tracePropagation
9129
- }
9130
- });
9131
- }
9132
9096
  function createJuniorRuntimeServices(overrides = {}) {
9133
9097
  const conversationMemory = createConversationMemoryService({
9134
9098
  completeText: overrides.conversationMemory?.completeText ?? completeText
@@ -9147,10 +9111,9 @@ function createJuniorRuntimeServices(overrides = {}) {
9147
9111
  contextCompactor,
9148
9112
  replyExecutor: {
9149
9113
  contextCompactor: overrides.replyExecutor?.contextCompactor ?? contextCompactor,
9150
- generateAssistantReply: overrides.replyExecutor?.generateAssistantReply ?? withSandboxTracePropagation(
9151
- generateAssistantReply,
9152
- overrides.sandbox?.tracePropagation
9153
- ),
9114
+ agentRunner: overrides.replyExecutor?.agentRunner ?? createAgentRunner(executeAgentRun, {
9115
+ tracePropagation: overrides.sandbox?.tracePropagation
9116
+ }),
9154
9117
  getAwaitingAgentContinueRequest: overrides.replyExecutor?.getAwaitingAgentContinueRequest ?? getAwaitingAgentContinueRequest,
9155
9118
  lookupSlackUser: overrides.replyExecutor?.lookupSlackUser ?? lookupSlackUser,
9156
9119
  scheduleAgentContinue: overrides.replyExecutor?.scheduleAgentContinue ?? scheduleAgentContinue,
@@ -9317,6 +9280,56 @@ function parkedInputKey(message) {
9317
9280
  const text = first && typeof first === "object" && "text" in first ? String(first.text ?? "") : "";
9318
9281
  return `${message.timestamp}:${text}`;
9319
9282
  }
9283
+ function renderRecentThreadMessages(conversationContext, messages) {
9284
+ const passiveMessages = messages.filter((queued) => {
9285
+ if (queued.explicitMention) {
9286
+ return false;
9287
+ }
9288
+ const slackTs = queuedInstructionActor(queued)?.slackTs;
9289
+ return !slackTs || !conversationContext?.includes(`slack_ts="${slackTs}"`);
9290
+ });
9291
+ if (passiveMessages.length === 0) {
9292
+ return void 0;
9293
+ }
9294
+ const lines = ["<recent-thread-messages>"];
9295
+ for (const queued of passiveMessages) {
9296
+ const actor = queuedInstructionActor(queued);
9297
+ const attrs = [
9298
+ actor?.authorId ? `author_id="${escapeXml(actor.authorId)}"` : void 0,
9299
+ actor?.authorName ? `author_name="${escapeXml(actor.authorName)}"` : void 0,
9300
+ actor?.slackTs ? `slack_ts="${escapeXml(actor.slackTs)}"` : void 0
9301
+ ].filter((attr) => Boolean(attr)).join(" ");
9302
+ lines.push(
9303
+ attrs ? ` <message ${attrs}>` : " <message>",
9304
+ escapeXml(queued.userText),
9305
+ " </message>"
9306
+ );
9307
+ }
9308
+ lines.push("</recent-thread-messages>");
9309
+ return lines.join("\n");
9310
+ }
9311
+ function appendRecentThreadMessagesToContext(conversationContext, messages, options) {
9312
+ const recentThreadMessages = renderRecentThreadMessages(
9313
+ conversationContext,
9314
+ messages
9315
+ );
9316
+ const contextParts = [
9317
+ options?.includeConversationContext === false ? void 0 : conversationContext?.trim(),
9318
+ recentThreadMessages
9319
+ ].filter((part) => Boolean(part));
9320
+ return contextParts.length > 0 ? contextParts.join("\n\n") : void 0;
9321
+ }
9322
+ function queuedInstructionActor(queued) {
9323
+ const actor = getMessageActorIdentity(queued.message);
9324
+ const authorId = actor?.userId ?? parseActorUserId(queued.message.author.userId);
9325
+ const authorName = actor?.fullName ?? actor?.userName;
9326
+ const slackTs = getSlackMessageTs(queued.message);
9327
+ return {
9328
+ ...authorId ? { authorId } : {},
9329
+ ...authorName ? { authorName } : {},
9330
+ ...slackTs ? { slackTs } : {}
9331
+ };
9332
+ }
9320
9333
  function isResourceEventMessage(message) {
9321
9334
  const raw = message.raw && typeof message.raw === "object" ? message.raw : void 0;
9322
9335
  return raw?.event_type === "resource_event";
@@ -9440,10 +9453,6 @@ function createReplyToThread(deps) {
9440
9453
  message.raw
9441
9454
  )
9442
9455
  };
9443
- const effectiveUserText = combineTurnText(
9444
- options.queuedMessages ?? [],
9445
- currentText
9446
- ).userText;
9447
9456
  await Promise.all(
9448
9457
  (options.queuedMessages ?? []).filter((queued) => !isResourceEventMessage(queued.message)).map(
9449
9458
  (queued) => ensureSlackMessageActorIdentity(
@@ -9453,6 +9462,7 @@ function createReplyToThread(deps) {
9453
9462
  )
9454
9463
  )
9455
9464
  );
9465
+ const effectiveUserText = currentText.userText;
9456
9466
  const credentialContext = resourceEventCredentialContext(message) ?? {
9457
9467
  actor: { type: "user", userId: message.author.userId }
9458
9468
  };
@@ -9544,6 +9554,7 @@ function createReplyToThread(deps) {
9544
9554
  queuedMessages.map(async (queued) => {
9545
9555
  const attachments = queued.message.attachments;
9546
9556
  return {
9557
+ actor: queuedInstructionActor(queued),
9547
9558
  text: queued.userText,
9548
9559
  timestampMs: queued.message.metadata.dateSent.getTime(),
9549
9560
  omittedImageAttachmentCount: !isVisionEnabled() && hasPotentialImageAttachment(attachments) ? countPotentialImageAttachments(attachments) : 0,
@@ -9864,7 +9875,8 @@ function createReplyToThread(deps) {
9864
9875
  let finalReplyDelivered = false;
9865
9876
  let latestArtifacts = preparedState.artifacts;
9866
9877
  let assistantTitleArtifacts = {};
9867
- const hasVisibleSlackDelivery = (post) => post.text.trim().length > 0 || Boolean(post.files?.length);
9878
+ let agentContinueScheduleError;
9879
+ const hasVisibleSlackDelivery = (post) => post.text.trim().length > 0;
9868
9880
  try {
9869
9881
  const loadedPiMessages = await loadPiMessagesForTurn({
9870
9882
  conversationId,
@@ -9893,6 +9905,18 @@ function createReplyToThread(deps) {
9893
9905
  });
9894
9906
  }
9895
9907
  }
9908
+ const hasDurablePromptHistory = Boolean(piMessages?.length);
9909
+ const queuedInstructionPiMessages = (await resolveSteeringMessages(
9910
+ (options.queuedMessages ?? []).filter(
9911
+ (queued) => queued.explicitMention
9912
+ )
9913
+ )).map(buildSteeringPiMessage);
9914
+ if (queuedInstructionPiMessages.length > 0) {
9915
+ piMessages = [
9916
+ ...piMessages ?? [],
9917
+ ...queuedInstructionPiMessages
9918
+ ];
9919
+ }
9896
9920
  status.start();
9897
9921
  const assistantTitleTask = maybeUpdateAssistantTitle({
9898
9922
  assistantThreadContext,
@@ -9956,6 +9980,12 @@ function createReplyToThread(deps) {
9956
9980
  );
9957
9981
  });
9958
9982
  const toolChannelId = preparedState.artifacts.assistantContextChannelId ?? channelId;
9983
+ const activeInstructionAuthorId = requester?.userId ?? parseActorUserId(message.author.userId);
9984
+ const activeInstructionAuthorName = requester?.fullName ?? requester?.userName;
9985
+ const promptConversationContext = appendRecentThreadMessagesToContext(
9986
+ preparedState.conversationContext,
9987
+ options.queuedMessages ?? []
9988
+ );
9959
9989
  const drainSteeringMessages = options.drainSteeringMessages ? async (accept) => {
9960
9990
  let acceptedMessages;
9961
9991
  const drained = await options.drainSteeringMessages(
@@ -9967,26 +9997,28 @@ function createReplyToThread(deps) {
9967
9997
  );
9968
9998
  return acceptedMessages ?? await resolveSteeringMessages(drained);
9969
9999
  } : void 0;
9970
- let reply = await deps.services.generateAssistantReply(
9971
- effectiveUserText,
9972
- {
9973
- credentialContext,
9974
- requester,
9975
- conversationContext: preparedState.conversationContext,
9976
- artifactState: preparedState.artifacts,
10000
+ const outcome = await deps.services.agentRunner.run({
10001
+ input: {
10002
+ actor: {
10003
+ ...activeInstructionAuthorId ? { authorId: activeInstructionAuthorId } : {},
10004
+ ...activeInstructionAuthorName ? { authorName: activeInstructionAuthorName } : {},
10005
+ ...slackMessageTs ? { slackTs: slackMessageTs } : {}
10006
+ },
10007
+ includeConversationContextWithPiMessages: hasDurablePromptHistory,
10008
+ messageText: effectiveUserText,
10009
+ conversationContext: promptConversationContext,
9977
10010
  piMessages,
9978
- pendingAuth: preparedState.conversation.processing.pendingAuth,
9979
- configuration: preparedState.configuration,
9980
- channelConfiguration: preparedState.channelConfiguration,
9981
10011
  inboundAttachmentCount: turnAttachments.length,
9982
10012
  omittedImageAttachmentCount,
9983
- userAttachments,
10013
+ userAttachments
10014
+ },
10015
+ routing: {
10016
+ credentialContext,
10017
+ requester,
9984
10018
  slackConversation,
9985
10019
  source,
9986
10020
  destination,
9987
10021
  surface: "slack",
9988
- authorizationFlowMode: message.author.isBot === true ? "disabled" : void 0,
9989
- turnDeadlineAtMs: getTurnRequestDeadline()?.deadlineAtMs,
9990
10022
  correlation: {
9991
10023
  conversationId,
9992
10024
  threadId,
@@ -9999,11 +10031,30 @@ function createReplyToThread(deps) {
9999
10031
  channelName,
10000
10032
  requesterId: slackRequesterId
10001
10033
  },
10002
- toolChannelId,
10034
+ toolChannelId
10035
+ },
10036
+ policy: {
10037
+ configuration: preparedState.configuration,
10038
+ channelConfiguration: preparedState.channelConfiguration,
10039
+ authorizationFlowMode: message.author.isBot === true ? "disabled" : void 0,
10040
+ turnDeadlineAtMs: getTurnRequestDeadline()?.deadlineAtMs
10041
+ },
10042
+ state: {
10043
+ artifactState: preparedState.artifacts,
10044
+ pendingAuth: preparedState.conversation.processing.pendingAuth,
10003
10045
  sandbox: {
10004
10046
  sandboxId: preparedState.sandboxId,
10005
10047
  sandboxDependencyProfileHash: preparedState.sandboxDependencyProfileHash
10006
- },
10048
+ }
10049
+ },
10050
+ observers: {
10051
+ onStatus: (nextStatus) => status.update(nextStatus),
10052
+ onToolInvocation: options.onToolInvocation
10053
+ },
10054
+ durability: {
10055
+ onInputCommitted: options.ack,
10056
+ drainSteeringMessages,
10057
+ shouldYield: options.shouldYield,
10007
10058
  onSandboxAcquired: async (sandbox) => {
10008
10059
  await persistThreadState(thread, {
10009
10060
  sandboxId: sandbox.sandboxId,
@@ -10028,14 +10079,98 @@ function createReplyToThread(deps) {
10028
10079
  await persistThreadState(thread, {
10029
10080
  conversation: preparedState.conversation
10030
10081
  });
10031
- },
10032
- onStatus: (nextStatus) => status.update(nextStatus),
10033
- onToolInvocation: options.onToolInvocation,
10034
- onInputCommitted: options.ack,
10035
- drainSteeringMessages,
10036
- shouldYield: options.shouldYield
10082
+ }
10037
10083
  }
10038
- );
10084
+ });
10085
+ if (outcome.status === "awaiting_auth") {
10086
+ if (!requester) {
10087
+ 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.`;
10088
+ await postThreadReply(
10089
+ buildSlackOutputMessage(text),
10090
+ "thread_reply"
10091
+ );
10092
+ markConversationMessage(
10093
+ preparedState.conversation,
10094
+ preparedState.userMessageId,
10095
+ {
10096
+ replied: true,
10097
+ skippedReason: void 0
10098
+ }
10099
+ );
10100
+ upsertConversationMessage(preparedState.conversation, {
10101
+ id: generateConversationId("assistant"),
10102
+ role: "assistant",
10103
+ text: normalizeConversationText(text),
10104
+ createdAtMs: Date.now(),
10105
+ author: {
10106
+ userName: botConfig.userName,
10107
+ isBot: true
10108
+ },
10109
+ meta: {
10110
+ replied: true
10111
+ }
10112
+ });
10113
+ markTurnClosed({
10114
+ conversation: preparedState.conversation,
10115
+ nowMs: Date.now(),
10116
+ sessionId: turnId,
10117
+ updateConversationStats
10118
+ });
10119
+ await persistThreadState(thread, {
10120
+ conversation: preparedState.conversation
10121
+ });
10122
+ persistedAtLeastOnce = true;
10123
+ shouldPersistFailureState = false;
10124
+ return;
10125
+ }
10126
+ await postAuthPauseNotice(outcome.providerDisplayName);
10127
+ completeAuthPauseTurn({
10128
+ conversation: preparedState.conversation,
10129
+ sessionId: turnId
10130
+ });
10131
+ await persistThreadState(thread, {
10132
+ conversation: preparedState.conversation
10133
+ });
10134
+ persistedAtLeastOnce = true;
10135
+ shouldPersistFailureState = false;
10136
+ return;
10137
+ }
10138
+ if (outcome.status === "suspended") {
10139
+ if (options.shouldYield?.()) {
10140
+ shouldPersistFailureState = false;
10141
+ throw new CooperativeTurnYieldError();
10142
+ }
10143
+ if (!destination || !conversationId) {
10144
+ throw new Error(
10145
+ "Agent continuation requires a destination and conversation id"
10146
+ );
10147
+ }
10148
+ try {
10149
+ await deps.services.scheduleAgentContinue({
10150
+ conversationId,
10151
+ destination,
10152
+ sessionId: turnId,
10153
+ expectedVersion: outcome.resumeVersion
10154
+ });
10155
+ shouldPersistFailureState = false;
10156
+ } catch (scheduleError) {
10157
+ logException(
10158
+ scheduleError,
10159
+ "agent_continue_schedule_failed",
10160
+ turnTraceContext,
10161
+ {
10162
+ ...messageTs ? { "messaging.message.id": messageTs } : {},
10163
+ "app.ai.resume_session_version": outcome.resumeVersion
10164
+ },
10165
+ "Failed to schedule agent continuation"
10166
+ );
10167
+ shouldPersistFailureState = true;
10168
+ agentContinueScheduleError = scheduleError;
10169
+ throw scheduleError;
10170
+ }
10171
+ return;
10172
+ }
10173
+ let reply = outcome.result;
10039
10174
  const diagnosticsContext = {
10040
10175
  slackThreadId: threadId,
10041
10176
  slackUserId: message.author.userId,
@@ -10081,7 +10216,6 @@ function createReplyToThread(deps) {
10081
10216
  channelId: slackChannelId,
10082
10217
  threadTs: slackThreadTs,
10083
10218
  posts: plannedPosts,
10084
- fileUploadFailureMode: "strict",
10085
10219
  footer: replyFooter,
10086
10220
  onPostError: ({ error, messageTs: messageTs2, stage }) => {
10087
10221
  logException(
@@ -10103,7 +10237,7 @@ function createReplyToThread(deps) {
10103
10237
  continue;
10104
10238
  }
10105
10239
  await postThreadReply(
10106
- buildSlackOutputMessage(post.text, post.files),
10240
+ buildSlackOutputMessage(post.text),
10107
10241
  post.stage
10108
10242
  );
10109
10243
  }
@@ -10229,99 +10363,13 @@ function createReplyToThread(deps) {
10229
10363
  );
10230
10364
  return;
10231
10365
  }
10232
- if (isCooperativeTurnYieldError(error)) {
10366
+ if (error instanceof CooperativeTurnYieldError) {
10233
10367
  shouldPersistFailureState = false;
10234
10368
  throw error;
10235
10369
  }
10236
- if (isAuthResumeRetryableTurnError(error)) {
10237
- if (!requester) {
10238
- const text = `I could not act on this subscribed event because ${error.metadata.authProviderDisplayName} needs user authorization. Ask me in this thread to connect ${error.metadata.authProviderDisplayName} before retrying.`;
10239
- await postThreadReply(
10240
- buildSlackOutputMessage(text),
10241
- "thread_reply"
10242
- );
10243
- markConversationMessage(
10244
- preparedState.conversation,
10245
- preparedState.userMessageId,
10246
- {
10247
- replied: true,
10248
- skippedReason: void 0
10249
- }
10250
- );
10251
- upsertConversationMessage(preparedState.conversation, {
10252
- id: generateConversationId("assistant"),
10253
- role: "assistant",
10254
- text: normalizeConversationText(text),
10255
- createdAtMs: Date.now(),
10256
- author: {
10257
- userName: botConfig.userName,
10258
- isBot: true
10259
- },
10260
- meta: {
10261
- replied: true
10262
- }
10263
- });
10264
- markTurnClosed({
10265
- conversation: preparedState.conversation,
10266
- nowMs: Date.now(),
10267
- sessionId: turnId,
10268
- updateConversationStats
10269
- });
10270
- await persistThreadState(thread, {
10271
- conversation: preparedState.conversation
10272
- });
10273
- persistedAtLeastOnce = true;
10274
- shouldPersistFailureState = false;
10275
- return;
10276
- }
10277
- await postAuthPauseNotice(error.metadata.authProviderDisplayName);
10278
- completeAuthPauseTurn({
10279
- conversation: preparedState.conversation,
10280
- sessionId: error.metadata?.sessionId ?? turnId
10281
- });
10282
- await persistThreadState(thread, {
10283
- conversation: preparedState.conversation
10284
- });
10285
- persistedAtLeastOnce = true;
10286
- shouldPersistFailureState = false;
10287
- return;
10288
- }
10289
- if (isRetryableTurnError(error, "agent_continue")) {
10290
- const conversationIdForResume = error.metadata?.conversationId;
10291
- const sessionIdForResume = error.metadata?.sessionId;
10292
- const version = error.metadata?.version;
10293
- if (conversationIdForResume && sessionIdForResume && typeof version === "number" && destination) {
10294
- try {
10295
- await deps.services.scheduleAgentContinue({
10296
- conversationId: conversationIdForResume,
10297
- destination,
10298
- sessionId: sessionIdForResume,
10299
- expectedVersion: version
10300
- });
10301
- shouldPersistFailureState = false;
10302
- } catch (scheduleError) {
10303
- logException(
10304
- scheduleError,
10305
- "agent_continue_schedule_failed",
10306
- turnTraceContext,
10307
- {
10308
- ...messageTs ? { "messaging.message.id": messageTs } : {},
10309
- "app.ai.resume_session_version": version
10310
- },
10311
- "Failed to schedule agent continuation"
10312
- );
10313
- shouldPersistFailureState = true;
10314
- throw scheduleError;
10315
- }
10316
- return;
10317
- } else {
10318
- logWarn(
10319
- "agent_continue_metadata_missing",
10320
- turnTraceContext,
10321
- messageTs ? { "messaging.message.id": messageTs } : {},
10322
- "Agent continuation could not be scheduled because retry metadata was incomplete"
10323
- );
10324
- }
10370
+ if (error === agentContinueScheduleError) {
10371
+ shouldPersistFailureState = true;
10372
+ throw error;
10325
10373
  }
10326
10374
  shouldPersistFailureState = true;
10327
10375
  const createdCanvasUrl = getCurrentTurnCanvasUrl({
@@ -10962,7 +11010,7 @@ async function failUnresumableContinuation(args) {
10962
11010
  errorMessage: args.errorMessage
10963
11011
  });
10964
11012
  }
10965
- async function continueSlackAgentRun(payload, options = {}) {
11013
+ async function continueSlackAgentRun(payload, options) {
10966
11014
  const thread = parseSlackThreadId(payload.conversationId);
10967
11015
  if (!thread) {
10968
11016
  throw new Error(
@@ -10976,7 +11024,7 @@ async function continueSlackAgentRun(payload, options = {}) {
10976
11024
  channelId: thread.channelId,
10977
11025
  threadTs: thread.threadTs,
10978
11026
  lockKey: payload.conversationId,
10979
- generateReply: options.generateReply,
11027
+ agentRunner: options.agentRunner,
10980
11028
  scheduleSessionCompletedPluginTasks: options.scheduleSessionCompletedPluginTasks,
10981
11029
  beforeStart: async () => {
10982
11030
  let sessionRecord;
@@ -11041,40 +11089,50 @@ async function continueSlackAgentRun(payload, options = {}) {
11041
11089
  messageText: userMessage2.text,
11042
11090
  messageTs: getTurnUserSlackMessageTs(userMessage2),
11043
11091
  replyContext: {
11044
- credentialContext: {
11045
- actor: {
11046
- type: "user",
11047
- userId: requester.userId
11048
- }
11049
- },
11050
- requester,
11051
- destination: payload.destination,
11052
- source: activeSessionRecord.source,
11053
- correlation: {
11054
- conversationId: payload.conversationId,
11055
- turnId: payload.sessionId,
11056
- channelId: thread.channelId,
11057
- threadTs: thread.threadTs,
11058
- requesterId: requester.userId
11092
+ input: {
11093
+ conversationContext,
11094
+ piMessages: conversation.piMessages,
11095
+ ...getTurnUserReplyAttachmentContext(userMessage2)
11059
11096
  },
11060
- toolChannelId: artifacts.assistantContextChannelId ?? thread.channelId,
11061
- artifactState: artifacts,
11062
- pendingAuth: conversation.processing.pendingAuth,
11063
- conversationContext,
11064
- channelConfiguration,
11065
- piMessages: conversation.piMessages,
11066
- sandbox,
11067
- recordPendingAuth: async (nextPendingAuth) => {
11068
- await applyPendingAuthUpdate({
11069
- conversation,
11097
+ routing: {
11098
+ credentialContext: {
11099
+ actor: {
11100
+ type: "user",
11101
+ userId: requester.userId
11102
+ }
11103
+ },
11104
+ requester,
11105
+ destination: payload.destination,
11106
+ source: activeSessionRecord.source,
11107
+ correlation: {
11070
11108
  conversationId: payload.conversationId,
11071
- nextPendingAuth
11072
- });
11073
- await persistThreadStateById(payload.conversationId, {
11074
- conversation
11075
- });
11109
+ turnId: payload.sessionId,
11110
+ channelId: thread.channelId,
11111
+ threadTs: thread.threadTs,
11112
+ requesterId: requester.userId
11113
+ },
11114
+ toolChannelId: artifacts.assistantContextChannelId ?? thread.channelId
11115
+ },
11116
+ policy: {
11117
+ channelConfiguration
11076
11118
  },
11077
- ...getTurnUserReplyAttachmentContext(userMessage2)
11119
+ state: {
11120
+ artifactState: artifacts,
11121
+ pendingAuth: conversation.processing.pendingAuth,
11122
+ sandbox
11123
+ },
11124
+ durability: {
11125
+ recordPendingAuth: async (nextPendingAuth) => {
11126
+ await applyPendingAuthUpdate({
11127
+ conversation,
11128
+ conversationId: payload.conversationId,
11129
+ nextPendingAuth
11130
+ });
11131
+ await persistThreadStateById(payload.conversationId, {
11132
+ conversation
11133
+ });
11134
+ }
11135
+ }
11078
11136
  },
11079
11137
  onSuccess: async (reply) => {
11080
11138
  await persistCompletedReplyState2({
@@ -11108,21 +11166,12 @@ async function continueSlackAgentRun(payload, options = {}) {
11108
11166
  "Continued agent run parked for auth"
11109
11167
  );
11110
11168
  },
11111
- onTimeoutPause: async (error) => {
11112
- if (!isRetryableTurnError(error, "agent_continue")) {
11113
- throw error;
11114
- }
11115
- const version = error.metadata?.version;
11116
- if (typeof version !== "number") {
11117
- throw new Error(
11118
- "Agent continuation did not include a session record version"
11119
- );
11120
- }
11169
+ onTimeoutPause: async ({ resumeVersion }) => {
11121
11170
  await scheduleAgentContinue2({
11122
11171
  conversationId: payload.conversationId,
11123
11172
  destination: payload.destination,
11124
11173
  sessionId: payload.sessionId,
11125
- expectedVersion: version
11174
+ expectedVersion: resumeVersion
11126
11175
  });
11127
11176
  }
11128
11177
  };
@@ -11240,7 +11289,7 @@ async function recoverStrandedRunningSession(args) {
11240
11289
  });
11241
11290
  return false;
11242
11291
  }
11243
- async function resumeAwaitingSlackContinuation(conversationId, options = {}) {
11292
+ async function resumeAwaitingSlackContinuation(conversationId, options) {
11244
11293
  const summaries = await listAgentTurnSessionSummariesForConversation(conversationId);
11245
11294
  const newest = summaries[0];
11246
11295
  if (newest?.state === "running") {
@@ -11278,7 +11327,7 @@ async function resumeAwaitingSlackContinuation(conversationId, options = {}) {
11278
11327
  }
11279
11328
  return false;
11280
11329
  }
11281
- async function continueSlackAgentRunWithLockRetry(payload, options = {}) {
11330
+ async function continueSlackAgentRunWithLockRetry(payload, options) {
11282
11331
  const scheduleAgentContinue2 = options.scheduleAgentContinue ?? scheduleAgentContinue;
11283
11332
  for (const [attempt, delayMs] of [
11284
11333
  ...AGENT_CONTINUE_LOCK_RETRY_DELAYS_MS,
@@ -11362,9 +11411,17 @@ function createProductionSlackWebhookServices(options) {
11362
11411
  }
11363
11412
  function createProductionConversationWorkOptions(options) {
11364
11413
  const conversationStore = getProductionConversationStore();
11414
+ const { agentRunner } = options;
11415
+ const services = {
11416
+ ...options.services,
11417
+ replyExecutor: {
11418
+ ...options.services?.replyExecutor,
11419
+ agentRunner
11420
+ }
11421
+ };
11365
11422
  const runtime = createSlackRuntime({
11366
11423
  getSlackAdapter: getProductionSlackAdapter,
11367
- services: options?.services
11424
+ services
11368
11425
  });
11369
11426
  return {
11370
11427
  conversationStore,
@@ -11373,11 +11430,8 @@ function createProductionConversationWorkOptions(options) {
11373
11430
  getSlackAdapter: getProductionSlackAdapter,
11374
11431
  conversationStore,
11375
11432
  resumeAwaitingContinuation: async (conversationId) => await resumeAwaitingSlackContinuation(conversationId, {
11376
- generateReply: withSandboxTracePropagation(
11377
- generateAssistantReply,
11378
- options?.services?.sandbox?.tracePropagation
11379
- ),
11380
- scheduleSessionCompletedPluginTasks: options?.services?.replyExecutor?.scheduleSessionCompletedPluginTasks
11433
+ agentRunner,
11434
+ scheduleSessionCompletedPluginTasks: services.replyExecutor?.scheduleSessionCompletedPluginTasks
11381
11435
  }),
11382
11436
  runtime
11383
11437
  })
@@ -11699,18 +11753,17 @@ async function createApp(options) {
11699
11753
  throw error;
11700
11754
  }
11701
11755
  const waitUntil = options?.waitUntil ?? await defaultWaitUntil();
11756
+ const tracePropagation = { domains: sandboxEgressTracePropagationDomains };
11757
+ const agentRunner = createAgentRunner(executeAgentRun, {
11758
+ tracePropagation
11759
+ });
11702
11760
  const runtimeServiceOverrides = {
11703
- sandbox: {
11704
- tracePropagation: { domains: sandboxEgressTracePropagationDomains }
11705
- }
11761
+ replyExecutor: { agentRunner },
11762
+ sandbox: { tracePropagation }
11706
11763
  };
11707
11764
  const slackWebhookServices = createProductionSlackWebhookServices({
11708
11765
  services: runtimeServiceOverrides
11709
11766
  });
11710
- const generateReplyWithTracePropagation = withSandboxTracePropagation(
11711
- generateAssistantReply,
11712
- runtimeServiceOverrides.sandbox.tracePropagation
11713
- );
11714
11767
  const app = new Hono();
11715
11768
  app.onError((err, c) => {
11716
11769
  logException(err, "unhandled_route_error");
@@ -11736,24 +11789,23 @@ async function createApp(options) {
11736
11789
  app.get("/health", () => GET());
11737
11790
  app.get("/api/oauth/callback/mcp/:provider", (c) => {
11738
11791
  return GET3(c.req.raw, c.req.param("provider"), waitUntil, {
11739
- generateReply: generateReplyWithTracePropagation
11792
+ agentRunner
11740
11793
  });
11741
11794
  });
11742
11795
  app.get("/api/oauth/callback/:provider", (c) => {
11743
11796
  return GET4(c.req.raw, c.req.param("provider"), waitUntil, {
11744
- generateReply: generateReplyWithTracePropagation
11797
+ agentRunner
11745
11798
  });
11746
11799
  });
11747
11800
  app.post("/api/internal/agent-dispatch", (c) => {
11748
- return POST(c.req.raw, waitUntil, {
11749
- tracePropagation: { domains: sandboxEgressTracePropagationDomains }
11750
- });
11801
+ return POST(c.req.raw, waitUntil, { agentRunner });
11751
11802
  });
11752
11803
  let agentContinuePOST;
11753
11804
  let pluginTaskPOST;
11754
11805
  let conversationWorkOptions;
11755
11806
  const getConversationWorkOptions = () => {
11756
11807
  conversationWorkOptions ??= options?.conversationWork ?? createProductionConversationWorkOptions({
11808
+ agentRunner,
11757
11809
  services: runtimeServiceOverrides
11758
11810
  });
11759
11811
  return conversationWorkOptions;