@sentry/junior 0.147.0 → 0.149.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 (162) hide show
  1. package/dist/{agent-hooks-HS6VGUPM.js → agent-hooks-CWMCXAMT.js} +10 -10
  2. package/dist/api/conversations/access.d.ts +2 -1
  3. package/dist/api/conversations/aggregate.d.ts +2 -0
  4. package/dist/api/conversations/archive.d.ts +3 -5
  5. package/dist/api/conversations/create.d.ts +6 -0
  6. package/dist/api/conversations/detail.d.ts +2 -296
  7. package/dist/api/conversations/event-list.d.ts +2 -193
  8. package/dist/api/conversations/list.d.ts +4 -66
  9. package/dist/api/conversations/pending-messages.d.ts +15 -0
  10. package/dist/api/conversations/routes.d.ts +1 -1
  11. package/dist/api/conversations/stats.d.ts +0 -54
  12. package/dist/api/http.d.ts +2 -6
  13. package/dist/api/locations/detail.d.ts +3 -111
  14. package/dist/api/locations/list.d.ts +0 -45
  15. package/dist/api/locations/query.d.ts +2 -1
  16. package/dist/api/locations/routes.d.ts +1 -1
  17. package/dist/api/people/list.d.ts +0 -29
  18. package/dist/api/people/profile.d.ts +2 -106
  19. package/dist/api/people/profile.query.d.ts +2 -1
  20. package/dist/api/people/routes.d.ts +1 -1
  21. package/dist/api/people/shared.d.ts +3 -1
  22. package/dist/api/people/spend.d.ts +3 -4
  23. package/dist/api/personal-tokens/routes.d.ts +2 -2
  24. package/dist/api/route.d.ts +8 -14
  25. package/dist/api/schema/conversation.d.ts +130 -25
  26. package/dist/api/schema/location.d.ts +4 -0
  27. package/dist/api/schema/person.d.ts +7 -3
  28. package/dist/api/schema.d.ts +2 -2
  29. package/dist/api/schema.js +13 -1
  30. package/dist/api/stats.d.ts +0 -13
  31. package/dist/api/validation.d.ts +7 -0
  32. package/dist/api/viewer.d.ts +3 -0
  33. package/dist/api.d.ts +2 -1
  34. package/dist/api.js +772 -561
  35. package/dist/app.js +241 -204
  36. package/dist/{catalog-runtime-M6ZTPYRV.js → catalog-runtime-GAW5APVI.js} +5 -5
  37. package/dist/chat/actor.d.ts +4 -1
  38. package/dist/chat/agent/request.d.ts +2 -0
  39. package/dist/chat/agent/resume.d.ts +1 -0
  40. package/dist/chat/agent-dispatch/context.d.ts +2 -2
  41. package/dist/chat/agent-invocations/types.d.ts +52 -0
  42. package/dist/chat/api-turns/work.d.ts +108 -0
  43. package/dist/chat/app/services.d.ts +1 -1
  44. package/dist/chat/conversations/history.d.ts +60 -0
  45. package/dist/chat/conversations/provenance.d.ts +6 -0
  46. package/dist/chat/conversations/sql/actor-identity.d.ts +19 -0
  47. package/dist/chat/conversations/store.d.ts +1 -1
  48. package/dist/chat/resource-events/store.d.ts +1 -1
  49. package/dist/chat/runtime/reply-executor.d.ts +1 -3
  50. package/dist/chat/runtime/slack-runtime.d.ts +2 -1
  51. package/dist/chat/runtime/turn-preparation.d.ts +9 -0
  52. package/dist/chat/scheduled-tasks/tool-support.d.ts +5 -5
  53. package/dist/chat/scheduled-tasks/tools/create.d.ts +2 -2
  54. package/dist/chat/scheduled-tasks/tools/delete.d.ts +2 -2
  55. package/dist/chat/scheduled-tasks/tools/list.d.ts +2 -2
  56. package/dist/chat/scheduled-tasks/tools/run-now.d.ts +2 -2
  57. package/dist/chat/scheduled-tasks/tools/update.d.ts +2 -2
  58. package/dist/chat/services/conversation-memory.d.ts +2 -2
  59. package/dist/chat/services/conversation-title.d.ts +20 -0
  60. package/dist/chat/slack/assistant-thread/title.d.ts +10 -27
  61. package/dist/chat/slack/channel.d.ts +26 -0
  62. package/dist/chat/slack/dispatch-turn.d.ts +1 -0
  63. package/dist/chat/slack/timestamp-param.d.ts +0 -2
  64. package/dist/chat/slack/{tools → tool-support}/canvas/api.d.ts +1 -1
  65. package/dist/chat/slack/{tools → tool-support}/canvas/context.d.ts +3 -0
  66. package/dist/chat/slack/tool-support/channel-access.d.ts +42 -0
  67. package/dist/chat/slack/tool-support/channel-target.d.ts +27 -0
  68. package/dist/chat/slack/tool-support/list/params.d.ts +5 -0
  69. package/dist/chat/slack/tools/canvas/create.d.ts +1 -1
  70. package/dist/chat/slack/tools/channel-join.d.ts +28 -0
  71. package/dist/chat/slack/tools/channel-list-messages.d.ts +3 -2
  72. package/dist/chat/slack/tools/conversation-message-search.d.ts +2 -8
  73. package/dist/chat/slack/tools/message-add-reaction.d.ts +1 -1
  74. package/dist/chat/slack/tools/public-search.d.ts +63 -2
  75. package/dist/chat/slack/tools/send-files.d.ts +1 -1
  76. package/dist/chat/slack/tools/thread-read.d.ts +2 -5
  77. package/dist/chat/source.d.ts +2 -0
  78. package/dist/chat/state/conversation.d.ts +1 -0
  79. package/dist/chat/task-execution/checkpoint.d.ts +1 -0
  80. package/dist/chat/task-execution/state.d.ts +9 -4
  81. package/dist/chat/task-execution/store.d.ts +3 -2
  82. package/dist/chat/task-execution/turn-cursor.d.ts +2 -0
  83. package/dist/chat/task-execution/worker.d.ts +2 -0
  84. package/dist/chat/tasks/read.d.ts +1 -1
  85. package/dist/chat/tools/search-conversation-events.d.ts +1 -1
  86. package/dist/chat/tools/types.d.ts +11 -4
  87. package/dist/chat/tools/watch-resource-events.d.ts +2 -2
  88. package/dist/{chunk-ORO3PWKQ.js → chunk-25SC7D6Z.js} +1 -1
  89. package/dist/{chunk-GGB4WL5A.js → chunk-2JHKISQA.js} +1 -1
  90. package/dist/{chunk-AVZDSUFB.js → chunk-2P6BQQER.js} +4 -4
  91. package/dist/{chunk-7NVJVJID.js → chunk-32CXPIZE.js} +2 -2
  92. package/dist/{chunk-YSB2N563.js → chunk-3GYZ5YEH.js} +121 -87
  93. package/dist/{chunk-DU74WPIC.js → chunk-3I2IVWSZ.js} +1 -1
  94. package/dist/{chunk-SVHUEHUL.js → chunk-3JVUWLR7.js} +65 -4
  95. package/dist/{chunk-RXBVB2JM.js → chunk-3KGQOKIG.js} +1 -1
  96. package/dist/{chunk-YBVXBNSV.js → chunk-4JGM7FXO.js} +2 -2
  97. package/dist/{chunk-AZ7EAOBE.js → chunk-4YKPOYRV.js} +5 -5
  98. package/dist/chunk-5CWUQXXT.js +806 -0
  99. package/dist/{chunk-DLDRQHG5.js → chunk-6G5RCDKP.js} +1 -1
  100. package/dist/{chunk-AYU72WO7.js → chunk-75DSYFFB.js} +47 -70
  101. package/dist/{chunk-YHDERDRO.js → chunk-7BH46NB4.js} +91 -75
  102. package/dist/{chunk-745QJ3ZA.js → chunk-ALJG73MQ.js} +2 -2
  103. package/dist/chunk-ART7P442.js +48 -0
  104. package/dist/{chunk-7A4M7PCV.js → chunk-DK5UT6PQ.js} +1180 -556
  105. package/dist/{chunk-AIOK6LAG.js → chunk-EW2LNCRL.js} +813 -44
  106. package/dist/chunk-EXW76GW4.js +36 -0
  107. package/dist/chunk-GDFF3BQG.js +136 -0
  108. package/dist/{chunk-7UQIVT6G.js → chunk-JMH3DBM5.js} +6 -6
  109. package/dist/{chunk-CWP5RHVX.js → chunk-JMSMXXED.js} +12 -5
  110. package/dist/{chunk-CSZINQJ2.js → chunk-MEMURSBG.js} +1 -1
  111. package/dist/{chunk-YNTTROJT.js → chunk-QIW7OQEO.js} +1 -1
  112. package/dist/{chunk-Q4R73I43.js → chunk-RB26T3LU.js} +58 -28
  113. package/dist/{chunk-4LJO56OJ.js → chunk-RLVZIWNQ.js} +1 -1
  114. package/dist/{chunk-SGZO7UWH.js → chunk-SWHPVZBV.js} +2 -2
  115. package/dist/{chunk-PVNG6UMU.js → chunk-TFBNBXPU.js} +22 -10
  116. package/dist/{chunk-EFLJ2WJH.js → chunk-URDCZAL4.js} +5 -5
  117. package/dist/{chunk-6H5SZR24.js → chunk-UZGASGOD.js} +300 -383
  118. package/dist/{chunk-YEO3VX72.js → chunk-W7UONUIZ.js} +8 -6
  119. package/dist/{chunk-JUUKWRBE.js → chunk-X22XXSZA.js} +1 -1
  120. package/dist/{chunk-X5UY34KT.js → chunk-XPYWWMHR.js} +8 -8
  121. package/dist/{chunk-IIOOO4FQ.js → chunk-ZYMYLLWG.js} +2 -1
  122. package/dist/cli/chat.js +32 -35
  123. package/dist/cli/check.js +6 -6
  124. package/dist/cli/plugins.js +13 -13
  125. package/dist/cli/snapshot-create.js +9 -9
  126. package/dist/cli/upgrade.js +7 -7
  127. package/dist/db/schema/agent-invocations.d.ts +20 -0
  128. package/dist/db/schema/conversation-annotations.d.ts +2 -2
  129. package/dist/db/schema.d.ts +22 -2
  130. package/dist/{db-CSEW4RII.js → db-JBZLRIVY.js} +7 -7
  131. package/dist/instrumentation.js +4 -4
  132. package/dist/nitro.js +2 -2
  133. package/dist/{oauth-callback-server-FUEV5HNG.js → oauth-callback-server-MNTVYY2I.js} +24 -26
  134. package/dist/{runner-U3XHSKSH.js → runner-K24BWZSZ.js} +23 -25
  135. package/dist/{skills-AKE6W3WI.js → skills-AU5ST2CG.js} +6 -6
  136. package/dist/spawn-6PO7CVJ6.js +38 -0
  137. package/dist/{validation-WDTAULUF.js → validation-IXGZCFRO.js} +6 -6
  138. package/dist/version.js +1 -1
  139. package/dist/{work-C3HAO2BG.js → work-WCZIYILX.js} +18 -19
  140. package/dist/{worker-LNP4YKBM.js → worker-U2AACBEY.js} +11 -10
  141. package/package.json +6 -5
  142. package/dist/api/health.d.ts +0 -7
  143. package/dist/api/personal-tokens/create.d.ts +0 -11
  144. package/dist/api/personal-tokens/list.d.ts +0 -12
  145. package/dist/api/personal-tokens/revoke.d.ts +0 -5
  146. package/dist/api/plugin-reports.d.ts +0 -63
  147. package/dist/api/plugins.d.ts +0 -8
  148. package/dist/api/runtime.d.ts +0 -24
  149. package/dist/api/skills.d.ts +0 -6
  150. package/dist/chat/slack/tools/channel-access.d.ts +0 -30
  151. package/dist/chunk-34GS7B6L.js +0 -115
  152. package/dist/chunk-AVOCH2Z5.js +0 -285
  153. package/dist/chunk-HLXXQI5Q.js +0 -238
  154. package/dist/chunk-K4SZAP3K.js +0 -67
  155. package/dist/chunk-R776HKPK.js +0 -81
  156. package/dist/chunk-Z4YU7Z4L.js +0 -76
  157. package/dist/spawn-B6GDTWJ4.js +0 -39
  158. /package/dist/chat/slack/{tools → tool-support}/canvas/markdown.d.ts +0 -0
  159. /package/dist/chat/slack/{tools → tool-support}/channel-capabilities.d.ts +0 -0
  160. /package/dist/chat/slack/{tools → tool-support}/context.d.ts +0 -0
  161. /package/dist/chat/slack/{tools → tool-support}/list/api.d.ts +0 -0
  162. /package/dist/chat/slack/{tools → tool-support}/slack-message-url.d.ts +0 -0
@@ -7,7 +7,7 @@ import {
7
7
  PluginCredentialFailureError,
8
8
  agentNameSchema,
9
9
  createPluginAuthOrchestration
10
- } from "./chunk-X5UY34KT.js";
10
+ } from "./chunk-XPYWWMHR.js";
11
11
  import {
12
12
  authorizationId
13
13
  } from "./chunk-DIB4GX3G.js";
@@ -32,7 +32,7 @@ import {
32
32
  searchSlackUsers,
33
33
  selectPluginGrant,
34
34
  toMcpProviderError
35
- } from "./chunk-Q4R73I43.js";
35
+ } from "./chunk-RB26T3LU.js";
36
36
  import {
37
37
  RetryableDeliveryError,
38
38
  actorFromRouting,
@@ -40,7 +40,7 @@ import {
40
40
  isAgentRunFeatureDisabled,
41
41
  surfaceFromRouting,
42
42
  toolInvocationDestination
43
- } from "./chunk-R776HKPK.js";
43
+ } from "./chunk-GDFF3BQG.js";
44
44
  import {
45
45
  addReactionToMessage,
46
46
  deliverOAuthAuthorization,
@@ -48,76 +48,7 @@ import {
48
48
  getSlackMessagePermalink,
49
49
  resolveBaseUrl,
50
50
  uploadFilesToConversation
51
- } from "./chunk-7UQIVT6G.js";
52
- import {
53
- estimateTextTokens,
54
- getAgentContextCompactionTriggerTokens,
55
- getAgentContextInputLimitTokens
56
- } from "./chunk-K4SZAP3K.js";
57
- import {
58
- AGENTS_REMOVAL_NOTICE,
59
- AGENTS_REPLACEMENT_NOTICE,
60
- CooperativeTurnYieldError,
61
- DEFAULT_SEARCH_COMMAND_TIMEOUT_MS,
62
- MAX_TEXT_CHARS,
63
- NO_REPLY_MARKER,
64
- RIPGREP_EXCLUDED_GLOBS,
65
- TURN_CONTEXT_TAG,
66
- TurnInputCommitLostError,
67
- TurnSliceLimitExceededError,
68
- abandonReplacedPendingAuth,
69
- buildAgentsInstructionsMessage,
70
- canReusePendingAuthLink,
71
- collectFiles,
72
- containsNoReplyMarker,
73
- continuableMessages,
74
- decideReply,
75
- extractAssistantText,
76
- findSingleRepositoryDirectory,
77
- findVisibleAgentsInstructions,
78
- getAssistantReplyText,
79
- getPiMessageRole,
80
- getRipgrepSearchLocation,
81
- getTerminalAssistantMessages,
82
- getUserMessageInstructionText,
83
- hasRuntimeTurnContext,
84
- historyItemFromPiMessage,
85
- isAssistantMessage,
86
- isContinuablePiBoundary,
87
- isMissingPathError,
88
- isNoReplyMarker,
89
- isToolResultError,
90
- isToolResultMessage,
91
- isTurnInputCommitLostError,
92
- loadConnectedMcpProviders,
93
- loadConversationProjection,
94
- loadTurnCheckpoint,
95
- loadTurnRoute,
96
- missingPathSearchResult,
97
- normalizeToLf,
98
- normalizeToolNameFromResult,
99
- openConversationProjection,
100
- piMessageFromHistoryItem,
101
- positiveInteger,
102
- recordAgentsInstructionsUpdated,
103
- recordAuthorizationRequested,
104
- recordGuardianActionReviewed,
105
- recordMcpProviderConnected,
106
- recordToolExecutionStarted,
107
- recordTurnRoute,
108
- renderAgentsInstructions,
109
- resolveRepositoryInstructions,
110
- resolveWorkspacePath,
111
- retainRuntimeTurnContext,
112
- sanitizeAssistantText,
113
- saveTurnCheckpoint,
114
- stripRuntimeTurnContext,
115
- trimTrailingAssistantMessages,
116
- truncateText
117
- } from "./chunk-AIOK6LAG.js";
118
- import {
119
- JUNIOR_THREAD_STATE_TTL_MS
120
- } from "./chunk-3Y47VMHV.js";
51
+ } from "./chunk-JMH3DBM5.js";
121
52
  import {
122
53
  parseSandboxEgressAuthRequiredSignal,
123
54
  parseSandboxEgressPermissionDeniedSignal,
@@ -146,7 +77,7 @@ import {
146
77
  runNonInteractiveCommand,
147
78
  throwSandboxOperationError,
148
79
  wrapSandboxSetupError
149
- } from "./chunk-AVZDSUFB.js";
80
+ } from "./chunk-2P6BQQER.js";
150
81
  import {
151
82
  SCHEDULED_TASK_SYSTEM_ACTOR,
152
83
  compactEventTask,
@@ -178,20 +109,83 @@ import {
178
109
  saveEventTask,
179
110
  scheduledTaskAttributes,
180
111
  writableEventTask
181
- } from "./chunk-EFLJ2WJH.js";
112
+ } from "./chunk-URDCZAL4.js";
182
113
  import {
183
114
  parseSlackThreadId
184
- } from "./chunk-CSZINQJ2.js";
115
+ } from "./chunk-MEMURSBG.js";
185
116
  import {
117
+ AGENTS_REMOVAL_NOTICE,
118
+ AGENTS_REPLACEMENT_NOTICE,
119
+ CooperativeTurnYieldError,
120
+ DEFAULT_SEARCH_COMMAND_TIMEOUT_MS,
121
+ MAX_TEXT_CHARS,
122
+ NO_REPLY_MARKER,
123
+ RIPGREP_EXCLUDED_GLOBS,
186
124
  SHORT_TITLE_MAX_LENGTH,
187
- generateShortTitle,
188
- resolveTaskTitle
189
- } from "./chunk-Z4YU7Z4L.js";
190
- import {
125
+ TURN_CONTEXT_TAG,
191
126
  ToolInputError,
127
+ TurnInputCommitLostError,
128
+ TurnSliceLimitExceededError,
129
+ abandonReplacedPendingAuth,
192
130
  addAgentTurnUsage,
193
- hasAgentTurnUsage
194
- } from "./chunk-HLXXQI5Q.js";
131
+ buildAgentsInstructionsMessage,
132
+ canReusePendingAuthLink,
133
+ collectFiles,
134
+ containsNoReplyMarker,
135
+ continuableMessages,
136
+ decideReply,
137
+ estimateTextTokens,
138
+ extractAssistantText,
139
+ findSingleRepositoryDirectory,
140
+ findVisibleAgentsInstructions,
141
+ generateShortTitle,
142
+ getAgentContextCompactionTriggerTokens,
143
+ getAgentContextInputLimitTokens,
144
+ getAssistantReplyText,
145
+ getPiMessageRole,
146
+ getRipgrepSearchLocation,
147
+ getTerminalAssistantMessages,
148
+ getUserMessageInstructionText,
149
+ hasAgentTurnUsage,
150
+ hasRuntimeTurnContext,
151
+ historyItemFromPiMessage,
152
+ isAssistantMessage,
153
+ isContinuablePiBoundary,
154
+ isMissingPathError,
155
+ isNoReplyMarker,
156
+ isToolResultError,
157
+ isToolResultMessage,
158
+ isTurnInputCommitLostError,
159
+ loadConnectedMcpProviders,
160
+ loadConversationProjection,
161
+ loadTurnCheckpoint,
162
+ loadTurnRoute,
163
+ missingPathSearchResult,
164
+ normalizeToLf,
165
+ normalizeToolNameFromResult,
166
+ openConversationProjection,
167
+ piMessageFromHistoryItem,
168
+ positiveInteger,
169
+ recordAgentsInstructionsUpdated,
170
+ recordAuthorizationRequested,
171
+ recordGuardianActionReviewed,
172
+ recordMcpProviderConnected,
173
+ recordToolExecutionStarted,
174
+ recordTurnRoute,
175
+ renderAgentsInstructions,
176
+ resolveRepositoryInstructions,
177
+ resolveTaskTitle,
178
+ resolveWorkspacePath,
179
+ retainRuntimeTurnContext,
180
+ sanitizeAssistantText,
181
+ saveTurnCheckpoint,
182
+ stripRuntimeTurnContext,
183
+ trimTrailingAssistantMessages,
184
+ truncateText
185
+ } from "./chunk-EW2LNCRL.js";
186
+ import {
187
+ JUNIOR_THREAD_STATE_TTL_MS
188
+ } from "./chunk-3Y47VMHV.js";
195
189
  import {
196
190
  privateTraceResultAttributes
197
191
  } from "./chunk-UIE3R5XU.js";
@@ -204,7 +198,7 @@ import {
204
198
  getSlackToolContext,
205
199
  readActorIdentity,
206
200
  resolveChannelCapabilities
207
- } from "./chunk-YHDERDRO.js";
201
+ } from "./chunk-7BH46NB4.js";
208
202
  import {
209
203
  SlackActionError,
210
204
  downloadPrivateSlackFile,
@@ -213,7 +207,7 @@ import {
213
207
  isConversationScopedChannel,
214
208
  normalizeSlackConversationId,
215
209
  withSlackRetries
216
- } from "./chunk-SGZO7UWH.js";
210
+ } from "./chunk-SWHPVZBV.js";
217
211
  import {
218
212
  KNOWN_CONVERSATION_EVENT_TYPES,
219
213
  contextProvenance,
@@ -225,10 +219,10 @@ import {
225
219
  instructionActors,
226
220
  instructionProvenanceFor,
227
221
  sameActorIdentity
228
- } from "./chunk-YSB2N563.js";
222
+ } from "./chunk-3GYZ5YEH.js";
229
223
  import {
230
224
  juniorIdentities
231
- } from "./chunk-DU74WPIC.js";
225
+ } from "./chunk-3I2IVWSZ.js";
232
226
  import {
233
227
  SANDBOX_DATA_ROOT,
234
228
  SANDBOX_SKILLS_ROOT,
@@ -240,7 +234,7 @@ import {
240
234
  } from "./chunk-EM225ITJ.js";
241
235
  import {
242
236
  getStateAdapter
243
- } from "./chunk-ORO3PWKQ.js";
237
+ } from "./chunk-25SC7D6Z.js";
244
238
  import {
245
239
  ACTIVE_TURN_COMPACTION_SUMMARY_PREFIX,
246
240
  COMPACTION_SUMMARY_PREFIX,
@@ -273,7 +267,7 @@ import {
273
267
  resolveGatewayCredential,
274
268
  resolveGatewayModel,
275
269
  unwrapCurrentInstruction
276
- } from "./chunk-7NVJVJID.js";
270
+ } from "./chunk-32CXPIZE.js";
277
271
  import {
278
272
  canExposeConversationPayload,
279
273
  resolveConversationPrivacy,
@@ -283,16 +277,16 @@ import {
283
277
  toGenAiMessagesTraceAttributes,
284
278
  toGenAiPayloadMetadata,
285
279
  toGenAiPayloadTraceAttributes
286
- } from "./chunk-RXBVB2JM.js";
280
+ } from "./chunk-3KGQOKIG.js";
287
281
  import {
288
282
  discoverSkills,
289
283
  findSkillByName,
290
284
  loadSkillsByName,
291
285
  parseSkillInvocation
292
- } from "./chunk-YBVXBNSV.js";
286
+ } from "./chunk-4JGM7FXO.js";
293
287
  import {
294
288
  pluginCatalogRuntime
295
- } from "./chunk-JUUKWRBE.js";
289
+ } from "./chunk-X22XXSZA.js";
296
290
  import {
297
291
  CredentialUnavailableError,
298
292
  OAuthProviderError,
@@ -300,7 +294,7 @@ import {
300
294
  getOAuthProviderErrorAttributes,
301
295
  resolveAuthTokenPlaceholder,
302
296
  resolvePluginCommandEnv
303
- } from "./chunk-GGB4WL5A.js";
297
+ } from "./chunk-2JHKISQA.js";
304
298
  import {
305
299
  credentialUserSubjectId
306
300
  } from "./chunk-RUIXF3YQ.js";
@@ -330,7 +324,7 @@ import {
330
324
  summarizeMessageText,
331
325
  withLogContext,
332
326
  withSpan
333
- } from "./chunk-4LJO56OJ.js";
327
+ } from "./chunk-RLVZIWNQ.js";
334
328
  import {
335
329
  startInactiveSpan,
336
330
  withActiveSpan
@@ -1903,19 +1897,22 @@ function buildRuntimeSection(params) {
1903
1897
  return renderTagBlock("runtime", lines.join("\n"));
1904
1898
  }
1905
1899
  function formatSourceLines(source) {
1906
- if (source.platform === "local") {
1907
- return [
1908
- "- source.platform: local",
1909
- `- source.conversation_id: ${escapeXml(source.conversationId)}`
1910
- ];
1900
+ switch (source.platform) {
1901
+ case "web":
1902
+ case "local":
1903
+ return [
1904
+ `- source.platform: ${source.platform}`,
1905
+ `- source.conversation_id: ${escapeXml(source.conversationId)}`
1906
+ ];
1907
+ case "slack":
1908
+ return [
1909
+ "- source.platform: slack",
1910
+ `- source.team_id: ${escapeXml(source.teamId)}`,
1911
+ `- source.channel_id: ${escapeXml(source.channelId)}`,
1912
+ ...source.messageTs ? [`- source.message_ts: ${escapeXml(source.messageTs)}`] : [],
1913
+ ...source.threadTs ? [`- source.thread_ts: ${escapeXml(source.threadTs)}`] : []
1914
+ ];
1911
1915
  }
1912
- return [
1913
- "- source.platform: slack",
1914
- `- source.team_id: ${escapeXml(source.teamId)}`,
1915
- `- source.channel_id: ${escapeXml(source.channelId)}`,
1916
- ...source.messageTs ? [`- source.message_ts: ${escapeXml(source.messageTs)}`] : [],
1917
- ...source.threadTs ? [`- source.thread_ts: ${escapeXml(source.threadTs)}`] : []
1918
- ];
1919
1916
  }
1920
1917
  function formatDestinationLines(destination) {
1921
1918
  if (destination.platform === "local") {
@@ -2059,7 +2056,8 @@ var STATIC_SYSTEM_PROMPTS = {
2059
2056
  slack: buildStaticSystemPrompt("slack")
2060
2057
  };
2061
2058
  function buildSystemPrompt(params) {
2062
- return STATIC_SYSTEM_PROMPTS[params.source.platform];
2059
+ const platform = params.source.platform === "slack" ? "slack" : "local";
2060
+ return STATIC_SYSTEM_PROMPTS[platform];
2063
2061
  }
2064
2062
  function buildTurnContextPrompt(params) {
2065
2063
  const includeSessionContext = params.includeSessionContext ?? true;
@@ -9638,94 +9636,382 @@ async function listThreadReplies(input) {
9638
9636
  }
9639
9637
  return replies.slice(0, targetLimit);
9640
9638
  }
9641
-
9642
- // src/chat/slack/timestamp-param.ts
9643
- import { z as z35 } from "zod";
9644
- function slackTimestampParam(description) {
9645
- return z35.preprocess(
9646
- (value) => typeof value === "number" && Number.isFinite(value) ? String(value) : value,
9647
- z35.string().min(1)
9648
- ).describe(description);
9649
- }
9650
- function optionalSlackTimestampParam(description) {
9651
- return slackTimestampParam(description).optional();
9652
- }
9653
- function parseSlackTimestampParam(field, value) {
9654
- if (!value) {
9655
- return { ok: true, value: void 0 };
9656
- }
9657
- const timestamp = parseSlackMessageTs(String(value));
9658
- if (timestamp) {
9659
- return { ok: true, value: timestamp };
9639
+ async function getConversationInfo(channelId) {
9640
+ const client = getSlackClient();
9641
+ const response = await withSlackRetries(
9642
+ () => client.conversations.info({
9643
+ channel: channelId
9644
+ }),
9645
+ 3,
9646
+ { action: "conversations.info", idempotent: true }
9647
+ );
9648
+ const channel = response.channel;
9649
+ if (!channel || typeof channel !== "object") {
9650
+ throw new Error(`Slack conversations.info returned no channel for ${channelId}`);
9660
9651
  }
9652
+ const record2 = channel;
9661
9653
  return {
9662
- ok: false,
9663
- error: `Invalid \`${field}\` Slack timestamp. Use a numeric Slack ts like \`1712345678.123456\`.`
9654
+ id: channelId,
9655
+ ...typeof record2.name === "string" && record2.name ? { name: record2.name } : {},
9656
+ isChannel: record2.is_channel === true,
9657
+ isPrivate: record2.is_private === true,
9658
+ isIm: record2.is_im === true,
9659
+ isMpim: record2.is_mpim === true,
9660
+ ...typeof record2.is_member === "boolean" ? { isMember: record2.is_member } : {}
9664
9661
  };
9665
9662
  }
9666
- function parseRequiredSlackTimestampParam(field, value) {
9667
- const timestamp = parseSlackMessageTs(String(value));
9668
- if (timestamp) {
9669
- return { ok: true, value: timestamp };
9663
+ async function joinPublicChannel(channelId) {
9664
+ const client = getSlackClient();
9665
+ await withSlackRetries(
9666
+ () => client.conversations.join({
9667
+ channel: channelId
9668
+ }),
9669
+ 3,
9670
+ { action: "conversations.join", idempotent: true }
9671
+ );
9672
+ }
9673
+ async function listPublicChannels(input = {}) {
9674
+ const client = getSlackClient();
9675
+ const targetLimit = Math.max(1, Math.min(input.limit ?? 1e3, 1e3));
9676
+ const maxPages = Math.max(1, Math.min(input.maxPages ?? 10, 20));
9677
+ const channels = [];
9678
+ let cursor;
9679
+ let pages = 0;
9680
+ while (channels.length < targetLimit && pages < maxPages) {
9681
+ pages += 1;
9682
+ const pageLimit = Math.max(1, Math.min(200, targetLimit - channels.length));
9683
+ const response = await withSlackRetries(
9684
+ () => client.conversations.list({
9685
+ types: "public_channel",
9686
+ exclude_archived: true,
9687
+ limit: pageLimit,
9688
+ cursor
9689
+ }),
9690
+ 3,
9691
+ { action: "conversations.list", idempotent: true }
9692
+ );
9693
+ const batch = response.channels ?? [];
9694
+ for (const item of batch) {
9695
+ if (typeof item.id !== "string" || !item.id) {
9696
+ continue;
9697
+ }
9698
+ if (item.is_private === true) {
9699
+ continue;
9700
+ }
9701
+ channels.push({
9702
+ id: item.id,
9703
+ ...typeof item.name === "string" && item.name ? { name: item.name } : {},
9704
+ ...typeof item.is_member === "boolean" ? { isMember: item.is_member } : {},
9705
+ ...typeof item.is_archived === "boolean" ? { isArchived: item.is_archived } : {}
9706
+ });
9707
+ }
9708
+ cursor = response.response_metadata?.next_cursor || void 0;
9709
+ if (!cursor) {
9710
+ break;
9711
+ }
9670
9712
  }
9671
- return {
9672
- ok: false,
9673
- error: `Invalid \`${field}\` Slack timestamp. Use a numeric Slack ts like \`1712345678.123456\`.`
9674
- };
9713
+ return channels.slice(0, targetLimit);
9675
9714
  }
9676
-
9677
- // src/chat/slack/tools/channel-list-messages.ts
9678
- import { z as z36 } from "zod";
9679
- var booleanInput2 = (description) => z36.preprocess(
9680
- (value) => value === "true" ? true : value === "false" ? false : value,
9681
- z36.boolean()
9682
- ).describe(description);
9683
- function normalizeRangeTimestamp(field, value, targetChannelId) {
9684
- if (value === void 0) {
9685
- return { ok: true, value: void 0 };
9715
+ async function resolvePublicChannelByName(channelName) {
9716
+ const normalized = channelName.trim().replace(/^#/, "").toLowerCase();
9717
+ if (!normalized) {
9718
+ return void 0;
9686
9719
  }
9687
- const trimmed = String(value).trim();
9688
- if (!trimmed) {
9689
- return parseSlackTimestampParam(field, value);
9720
+ const channels = await listPublicChannels();
9721
+ return channels.find(
9722
+ (channel) => channel.name?.trim().toLowerCase() === normalized
9723
+ );
9724
+ }
9725
+
9726
+ // src/chat/slack/tool-support/channel-access.ts
9727
+ var DENIED_PRIVATE = "Cannot read this Slack conversation because it is private. Only the current conversation or public channels are readable.";
9728
+ var DENIED_NOT_FOUND = "Cannot read this Slack conversation because the channel was not found or the bot cannot see it.";
9729
+ var DENIED_NOT_IN_CHANNEL = "Cannot read this Slack conversation because the bot is not in the channel.";
9730
+ var DENIED_MISSING_SCOPE = "Cannot verify Slack channel access because this installation is missing channel read scopes.";
9731
+ function deny(reason, error) {
9732
+ return { allowed: false, reason, error };
9733
+ }
9734
+ async function checkSlackChannelReadAccess(args) {
9735
+ const currentChannels = args.currentChannelIds.filter(
9736
+ (channelId) => Boolean(channelId)
9737
+ );
9738
+ if (currentChannels.includes(args.targetChannelId)) {
9739
+ return { allowed: true, isMember: true };
9690
9740
  }
9691
- const timestamp = parseSlackTimestampParam(field, trimmed);
9692
- if (timestamp.ok && timestamp.value) {
9693
- return timestamp;
9741
+ const visibility = await getConversationStore().getDestinationVisibility({
9742
+ provider: "slack",
9743
+ providerTenantId: args.teamId,
9744
+ providerDestinationId: args.targetChannelId
9745
+ });
9746
+ if (visibility === "private") {
9747
+ return deny("private", DENIED_PRIVATE);
9694
9748
  }
9695
- const threadId = parseSlackThreadId(trimmed);
9696
- const threadTimestamp = threadId ? parseSlackTimestampParam(field, threadId.threadTs) : void 0;
9697
- if (threadId && threadTimestamp?.ok && threadTimestamp.value) {
9698
- if (threadId.channelId === targetChannelId) {
9699
- return threadTimestamp;
9749
+ try {
9750
+ const info = await getConversationInfo(args.targetChannelId);
9751
+ const isPublicChannel = info.isChannel && !info.isPrivate && !info.isIm && !info.isMpim;
9752
+ if (!isPublicChannel) {
9753
+ return deny("private", DENIED_PRIVATE);
9754
+ }
9755
+ return {
9756
+ allowed: true,
9757
+ isPublic: true,
9758
+ ...info.name ? { channelName: info.name } : {},
9759
+ ...typeof info.isMember === "boolean" ? { isMember: info.isMember } : {}
9760
+ };
9761
+ } catch (error) {
9762
+ if (error instanceof SlackActionError) {
9763
+ if (error.code === "not_found") {
9764
+ return deny("not_found", DENIED_NOT_FOUND);
9765
+ }
9766
+ if (error.code === "not_in_channel") {
9767
+ return deny("not_in_channel", DENIED_NOT_IN_CHANNEL);
9768
+ }
9769
+ if (error.code === "missing_scope") {
9770
+ return deny("missing_scope", DENIED_MISSING_SCOPE);
9771
+ }
9700
9772
  }
9773
+ throw error;
9701
9774
  }
9702
- return timestamp;
9703
9775
  }
9704
- function createSlackChannelListMessagesTool(context) {
9705
- return zodTool({
9706
- description: "List channel messages from Slack history in the active channel context. Use when the user asks for recent or historical channel context outside this thread. Do not use for live monitoring or when current thread context already answers the question.",
9707
- annotations: {
9708
- destructiveHint: false,
9709
- idempotentHint: true,
9710
- openWorldHint: true,
9711
- readOnlyHint: true
9712
- },
9713
- inputSchema: z36.object({
9714
- limit: z36.coerce.number().int().min(1).max(1e3).describe("Maximum number of messages to return across pages.").optional(),
9715
- cursor: z36.string().min(1).describe("Optional cursor to continue from a prior call.").optional(),
9716
- oldest: optionalSlackTimestampParam(
9717
- "Optional oldest message timestamp (Slack ts) for range filtering."
9718
- ),
9719
- latest: optionalSlackTimestampParam(
9720
- "Optional latest message timestamp (Slack ts) for range filtering."
9721
- ),
9722
- inclusive: booleanInput2(
9723
- "Whether oldest/latest bounds should be inclusive."
9724
- ).optional(),
9725
- max_pages: z36.coerce.number().int().min(1).max(10).describe("Maximum number of API pages to traverse in a single call.").optional()
9726
- }),
9727
- outputSchema: juniorToolOutputSchema,
9776
+ async function joinPublicChannelForRead(args) {
9777
+ try {
9778
+ await joinPublicChannel(args.targetChannelId);
9779
+ return {
9780
+ ok: true,
9781
+ ...args.channelName ? { channelName: args.channelName } : {}
9782
+ };
9783
+ } catch (error) {
9784
+ if (error instanceof SlackActionError) {
9785
+ if (error.code === "missing_scope") {
9786
+ return {
9787
+ ok: false,
9788
+ reason: "missing_scope",
9789
+ error: "Cannot join this public Slack channel because this installation is missing the `channels:join` scope."
9790
+ };
9791
+ }
9792
+ if (error.code === "not_found") {
9793
+ return {
9794
+ ok: false,
9795
+ reason: "not_found",
9796
+ error: DENIED_NOT_FOUND
9797
+ };
9798
+ }
9799
+ if (error.apiError === "method_not_supported_for_channel_type" || error.apiError === "is_archived" || error.apiError === "cant_invite_self") {
9800
+ return {
9801
+ ok: false,
9802
+ reason: "private",
9803
+ error: DENIED_PRIVATE
9804
+ };
9805
+ }
9806
+ return {
9807
+ ok: false,
9808
+ reason: "not_in_channel",
9809
+ error: "Could not join this public Slack channel. Ask an admin to add the bot, then retry."
9810
+ };
9811
+ }
9812
+ throw error;
9813
+ }
9814
+ }
9815
+
9816
+ // src/chat/slack/tool-support/channel-target.ts
9817
+ import { z as z35 } from "zod";
9818
+ var slackChannelRefParam = z35.string().trim().min(1).max(80).describe("Slack channel ID or public channel name.");
9819
+ async function resolveSlackChannelRef(input) {
9820
+ const trimmed = input.value.trim();
9821
+ if (!trimmed) {
9822
+ throw new ToolInputError(
9823
+ `Invalid \`${input.field}\`. Provide a Slack channel id like \`C123\` or a public channel name like \`#proj-foo\`.`
9824
+ );
9825
+ }
9826
+ const channelId = parseSlackChannelId(trimmed);
9827
+ if (channelId) {
9828
+ return {
9829
+ channelId,
9830
+ resolvedFromName: false
9831
+ };
9832
+ }
9833
+ const match = await resolvePublicChannelByName(trimmed);
9834
+ if (!match) {
9835
+ throw new ToolInputError(
9836
+ `No public Slack channel named \`${trimmed}\` was found. Use an exact public channel name or a channel id.`
9837
+ );
9838
+ }
9839
+ return {
9840
+ channelId: match.id,
9841
+ ...match.name ? { channelName: match.name } : {},
9842
+ resolvedFromName: true
9843
+ };
9844
+ }
9845
+ async function resolveOptionalSlackChannelRef(input) {
9846
+ if (input.value !== void 0) {
9847
+ return resolveSlackChannelRef({
9848
+ field: input.field ?? "channel_id",
9849
+ value: input.value
9850
+ });
9851
+ }
9852
+ if (input.defaultChannelId) {
9853
+ return {
9854
+ channelId: input.defaultChannelId,
9855
+ resolvedFromName: false
9856
+ };
9857
+ }
9858
+ throw new ToolInputError(
9859
+ "Provide `channel_id`, or use this tool in an active Slack channel context."
9860
+ );
9861
+ }
9862
+
9863
+ // src/chat/slack/tools/channel-join.ts
9864
+ import { z as z36 } from "zod";
9865
+ function createSlackChannelJoinTool(context) {
9866
+ return zodTool({
9867
+ description: "Join a public Slack channel.",
9868
+ annotations: {
9869
+ destructiveHint: false,
9870
+ idempotentHint: false,
9871
+ openWorldHint: true,
9872
+ readOnlyHint: false
9873
+ },
9874
+ inputSchema: z36.object({
9875
+ channel_id: slackChannelRefParam
9876
+ }),
9877
+ outputSchema: juniorToolOutputSchema,
9878
+ execute: async ({ channel_id }) => {
9879
+ const target = await resolveSlackChannelRef({
9880
+ field: "channel_id",
9881
+ value: channel_id
9882
+ });
9883
+ const access = await checkSlackChannelReadAccess({
9884
+ currentChannelIds: [
9885
+ context.destinationChannelId,
9886
+ context.sourceChannelId
9887
+ ],
9888
+ targetChannelId: target.channelId,
9889
+ teamId: context.teamId
9890
+ });
9891
+ if (!access.allowed) {
9892
+ throw new ToolInputError(access.error);
9893
+ }
9894
+ if (access.isMember === true) {
9895
+ return {
9896
+ channel_id: target.channelId,
9897
+ ...access.channelName || target.channelName ? { channel_name: access.channelName ?? target.channelName } : {},
9898
+ joined: false,
9899
+ already_member: true
9900
+ };
9901
+ }
9902
+ try {
9903
+ await joinPublicChannel(target.channelId);
9904
+ } catch (error) {
9905
+ if (error instanceof SlackActionError) {
9906
+ if (error.code === "missing_scope") {
9907
+ throw new ToolInputError(
9908
+ "Cannot join this public Slack channel because this installation is missing the `channels:join` scope.",
9909
+ { cause: error }
9910
+ );
9911
+ }
9912
+ throw new ToolInputError(
9913
+ "Could not join this public Slack channel. Confirm it is public and retry, or ask an admin to add the bot.",
9914
+ { cause: error }
9915
+ );
9916
+ }
9917
+ throw error;
9918
+ }
9919
+ return {
9920
+ channel_id: target.channelId,
9921
+ ...access.channelName || target.channelName ? { channel_name: access.channelName ?? target.channelName } : {},
9922
+ joined: true,
9923
+ already_member: false
9924
+ };
9925
+ }
9926
+ });
9927
+ }
9928
+
9929
+ // src/chat/slack/timestamp-param.ts
9930
+ import { z as z37 } from "zod";
9931
+ function slackTimestampParam(description) {
9932
+ return z37.preprocess(
9933
+ (value) => typeof value === "number" && Number.isFinite(value) ? String(value) : value,
9934
+ z37.string().min(1)
9935
+ ).describe(description);
9936
+ }
9937
+ function parseSlackTimestampParam(field, value) {
9938
+ if (!value) {
9939
+ return { ok: true, value: void 0 };
9940
+ }
9941
+ const timestamp = parseSlackMessageTs(String(value));
9942
+ if (timestamp) {
9943
+ return { ok: true, value: timestamp };
9944
+ }
9945
+ return {
9946
+ ok: false,
9947
+ error: `Invalid \`${field}\` Slack timestamp. Use a numeric Slack ts like \`1712345678.123456\`.`
9948
+ };
9949
+ }
9950
+ function parseRequiredSlackTimestampParam(field, value) {
9951
+ const timestamp = parseSlackMessageTs(String(value));
9952
+ if (timestamp) {
9953
+ return { ok: true, value: timestamp };
9954
+ }
9955
+ return {
9956
+ ok: false,
9957
+ error: `Invalid \`${field}\` Slack timestamp. Use a numeric Slack ts like \`1712345678.123456\`.`
9958
+ };
9959
+ }
9960
+
9961
+ // src/chat/slack/tools/channel-list-messages.ts
9962
+ import { z as z38 } from "zod";
9963
+ var booleanInput2 = (description) => z38.preprocess(
9964
+ (value) => value === "true" ? true : value === "false" ? false : value,
9965
+ z38.boolean()
9966
+ ).describe(description);
9967
+ function normalizeRangeTimestamp(field, value, targetChannelId) {
9968
+ if (value === void 0) {
9969
+ return { ok: true, value: void 0 };
9970
+ }
9971
+ const trimmed = String(value).trim();
9972
+ if (!trimmed) {
9973
+ return parseSlackTimestampParam(field, value);
9974
+ }
9975
+ const timestamp = parseSlackTimestampParam(field, trimmed);
9976
+ if (timestamp.ok && timestamp.value) {
9977
+ return timestamp;
9978
+ }
9979
+ const threadId = parseSlackThreadId(trimmed);
9980
+ const threadTimestamp = threadId ? parseSlackTimestampParam(field, threadId.threadTs) : void 0;
9981
+ if (threadId && threadTimestamp?.ok && threadTimestamp.value) {
9982
+ if (threadId.channelId === targetChannelId) {
9983
+ return threadTimestamp;
9984
+ }
9985
+ }
9986
+ return timestamp;
9987
+ }
9988
+ function createSlackChannelListMessagesTool(context) {
9989
+ return zodTool({
9990
+ description: "List messages from Slack channel history. Defaults to the active channel. Pass `channel_id` to read another public channel.",
9991
+ annotations: {
9992
+ destructiveHint: false,
9993
+ idempotentHint: true,
9994
+ openWorldHint: true,
9995
+ readOnlyHint: true
9996
+ },
9997
+ inputSchema: z38.object({
9998
+ channel_id: slackChannelRefParam.optional(),
9999
+ limit: z38.coerce.number().int().min(1).max(1e3).describe("Maximum number of messages to return across pages.").optional(),
10000
+ cursor: z38.string().min(1).describe("Optional cursor to continue from a prior call.").optional(),
10001
+ oldest: slackTimestampParam(
10002
+ "Oldest message timestamp (Slack ts) for range filtering."
10003
+ ).optional(),
10004
+ latest: slackTimestampParam(
10005
+ "Latest message timestamp (Slack ts) for range filtering."
10006
+ ).optional(),
10007
+ inclusive: booleanInput2(
10008
+ "Whether oldest/latest bounds should be inclusive."
10009
+ ).optional(),
10010
+ max_pages: z38.coerce.number().int().min(1).max(10).describe("Maximum number of API pages to traverse in a single call.").optional()
10011
+ }),
10012
+ outputSchema: juniorToolOutputSchema,
9728
10013
  execute: async ({
10014
+ channel_id,
9729
10015
  limit,
9730
10016
  cursor,
9731
10017
  oldest,
@@ -9733,9 +10019,22 @@ function createSlackChannelListMessagesTool(context) {
9733
10019
  inclusive,
9734
10020
  max_pages
9735
10021
  }) => {
9736
- const targetChannelId = context.destinationChannelId;
9737
- if (!targetChannelId) {
9738
- throw new ToolInputError("No active Slack destination is available.");
10022
+ const target = await resolveOptionalSlackChannelRef({
10023
+ field: "channel_id",
10024
+ value: channel_id,
10025
+ defaultChannelId: context.destinationChannelId
10026
+ });
10027
+ const targetChannelId = target.channelId;
10028
+ const access = await checkSlackChannelReadAccess({
10029
+ currentChannelIds: [
10030
+ context.destinationChannelId,
10031
+ context.sourceChannelId
10032
+ ],
10033
+ targetChannelId,
10034
+ teamId: context.teamId
10035
+ });
10036
+ if (!access.allowed) {
10037
+ throw new ToolInputError(access.error);
9739
10038
  }
9740
10039
  const normalizedOldest = normalizeRangeTimestamp(
9741
10040
  "oldest",
@@ -9753,39 +10052,84 @@ function createSlackChannelListMessagesTool(context) {
9753
10052
  if (!normalizedLatest.ok) {
9754
10053
  throw new ToolInputError(normalizedLatest.error);
9755
10054
  }
10055
+ const readHistory = async () => listChannelMessages({
10056
+ channelId: targetChannelId,
10057
+ limit: limit ?? 100,
10058
+ cursor,
10059
+ oldest: normalizedOldest.value,
10060
+ latest: normalizedLatest.value,
10061
+ inclusive,
10062
+ maxPages: max_pages
10063
+ });
9756
10064
  let result;
10065
+ let joined = false;
10066
+ let channelName = access.channelName ?? target.channelName;
10067
+ let readError;
9757
10068
  try {
9758
- result = await listChannelMessages({
9759
- channelId: targetChannelId,
9760
- limit: limit ?? 100,
9761
- cursor,
9762
- oldest: normalizedOldest.value,
9763
- latest: normalizedLatest.value,
9764
- inclusive,
9765
- maxPages: max_pages
9766
- });
10069
+ result = await readHistory();
9767
10070
  } catch (error) {
10071
+ readError = error;
10072
+ const canJoin = error instanceof SlackActionError && error.code === "not_in_channel" && access.isMember !== true;
10073
+ if (canJoin) {
10074
+ const joinResult = await joinPublicChannelForRead({
10075
+ channelName,
10076
+ targetChannelId
10077
+ });
10078
+ if (!joinResult.ok) {
10079
+ throw new ToolInputError(joinResult.error);
10080
+ }
10081
+ joined = true;
10082
+ channelName = joinResult.channelName ?? channelName;
10083
+ try {
10084
+ result = await readHistory();
10085
+ readError = void 0;
10086
+ } catch (retryError) {
10087
+ readError = retryError;
10088
+ }
10089
+ }
10090
+ }
10091
+ if (!result) {
10092
+ const error = readError;
9768
10093
  if (error instanceof SlackActionError && error.apiError === "invalid_cursor") {
9769
10094
  throw new ToolInputError(
9770
10095
  "The supplied Slack history cursor is no longer valid. Retry the lookup without `cursor` to start from the newest page again.",
9771
10096
  { cause: error }
9772
10097
  );
9773
10098
  }
10099
+ if (error instanceof SlackActionError) {
10100
+ if (error.code === "not_in_channel") {
10101
+ throw new ToolInputError(
10102
+ "Could not read this Slack channel history because the bot is not in the channel.",
10103
+ { cause: error }
10104
+ );
10105
+ }
10106
+ if (error.code === "missing_scope") {
10107
+ throw new ToolInputError(
10108
+ "Could not read this Slack channel history because this installation is missing history scopes.",
10109
+ { cause: error }
10110
+ );
10111
+ }
10112
+ throw new ToolInputError(
10113
+ "Could not read this Slack channel history.",
10114
+ { cause: error }
10115
+ );
10116
+ }
9774
10117
  throw error;
9775
10118
  }
9776
- const summary = {
10119
+ return {
9777
10120
  channel_id: targetChannelId,
10121
+ ...channelName ? { channel_name: channelName } : {},
10122
+ ...joined ? { joined_channel: true } : {},
9778
10123
  count: result.messages.length,
9779
10124
  messages: result.messages,
9780
10125
  ...result.nextCursor ? { next_cursor: result.nextCursor } : {}
9781
10126
  };
9782
- return summary;
9783
10127
  }
9784
10128
  });
9785
10129
  }
9786
10130
 
9787
10131
  // src/chat/slack/tools/conversation-message-search.ts
9788
- import { z as z37 } from "zod";
10132
+ import { z as z39 } from "zod";
9789
10133
 
9790
10134
  // src/chat/conversations/tool-source.ts
9791
10135
  var CONVERSATIONS_TOOL_SOURCE = {
@@ -9796,35 +10140,32 @@ var CONVERSATIONS_TOOL_SOURCE = {
9796
10140
  // src/chat/slack/tools/conversation-message-search.ts
9797
10141
  var DEFAULT_LIMIT = 5;
9798
10142
  var conversationMessageSearchOutputSchema = juniorToolOutputSchema.extend({
9799
- channel_id: z37.string().min(1).optional(),
9800
- count: z37.number().int().nonnegative(),
9801
- query: z37.string().optional(),
9802
- matches: z37.array(
9803
- z37.object({
9804
- channel_id: z37.string().min(1),
9805
- channel_name: z37.string().min(1).optional(),
9806
- conversation_id: z37.string().min(1),
9807
- excerpt: z37.string(),
9808
- message_id: z37.string().min(1),
9809
- message_role: z37.enum(["assistant", "user"]),
9810
- message_timestamp: z37.string().datetime(),
9811
- permalink: z37.string().url().optional(),
9812
- thread_ts: z37.string().min(1)
10143
+ channel_id: z39.string().min(1).optional(),
10144
+ count: z39.number().int().nonnegative(),
10145
+ query: z39.string().optional(),
10146
+ matches: z39.array(
10147
+ z39.object({
10148
+ channel_id: z39.string().min(1),
10149
+ channel_name: z39.string().min(1).optional(),
10150
+ conversation_id: z39.string().min(1),
10151
+ excerpt: z39.string(),
10152
+ message_id: z39.string().min(1),
10153
+ message_role: z39.enum(["assistant", "user"]),
10154
+ message_timestamp: z39.string().datetime(),
10155
+ permalink: z39.string().url().optional(),
10156
+ thread_ts: z39.string().min(1)
9813
10157
  }).strict()
9814
10158
  )
9815
10159
  });
9816
- function resolveSearchFilters(input) {
10160
+ async function resolveSearchFilters(input) {
9817
10161
  const query = input.query?.trim() || void 0;
9818
10162
  let channelId;
9819
- if (input.channel_id != null) {
9820
- const parsed = parseRequiredSlackChannelIdParam(
9821
- "channel_id",
9822
- input.channel_id
9823
- );
9824
- if (!parsed.ok) {
9825
- throw new ToolInputError(parsed.error);
9826
- }
9827
- channelId = parsed.value;
10163
+ if (input.channel_id != null && input.channel_id.trim() !== "") {
10164
+ const target = await resolveSlackChannelRef({
10165
+ field: "channel_id",
10166
+ value: input.channel_id
10167
+ });
10168
+ channelId = target.channelId;
9828
10169
  }
9829
10170
  if (!query && !channelId) {
9830
10171
  throw new ToolInputError(
@@ -9836,9 +10177,9 @@ function resolveSearchFilters(input) {
9836
10177
  ...query ? { query } : {}
9837
10178
  };
9838
10179
  }
9839
- function createSlackConversationMessageSearchTool(scope, currentConversationId, deps = {}) {
10180
+ function createSlackConversationMessageSearchTool(scope, currentConversationId) {
9840
10181
  return zodTool({
9841
- description: "Search retained user and assistant messages in public Junior conversations across the current Slack workspace. Excludes the current conversation.",
10182
+ description: "Search retained user and assistant messages from public conversations in this Slack workspace. Excludes the current conversation. Not live Slack workspace search.",
9842
10183
  exposure: "deferred",
9843
10184
  source: CONVERSATIONS_TOOL_SOURCE,
9844
10185
  annotations: {
@@ -9847,24 +10188,21 @@ function createSlackConversationMessageSearchTool(scope, currentConversationId,
9847
10188
  openWorldHint: false,
9848
10189
  readOnlyHint: true
9849
10190
  },
9850
- inputSchema: z37.object({
9851
- channel_id: slackChannelIdParam(
9852
- "Destination Slack channel ID; no active-channel default."
9853
- ).nullable().optional(),
9854
- query: z37.string().trim().min(1).max(200).nullable().describe("Words or phrase to match in message text.").optional(),
9855
- limit: z37.number().int().min(1).max(10).describe(`Maximum matches. Default ${DEFAULT_LIMIT}; max 10.`).nullable().optional()
10191
+ inputSchema: z39.object({
10192
+ channel_id: slackChannelRefParam.nullable().optional(),
10193
+ query: z39.string().trim().min(1).max(200).nullable().describe("Words or phrase to match in message text.").optional(),
10194
+ limit: z39.number().int().min(1).max(10).describe(`Maximum matches. Default ${DEFAULT_LIMIT}; max 10.`).nullable().optional()
9856
10195
  }).strict(),
9857
10196
  outputSchema: conversationMessageSearchOutputSchema,
9858
10197
  execute: async (input) => {
9859
- const filters = resolveSearchFilters(input);
9860
- const store = deps.store ?? getConversationMessageSearchStore();
10198
+ const filters = await resolveSearchFilters(input);
10199
+ const store = getConversationMessageSearchStore();
9861
10200
  const matches = await store.search({
9862
10201
  currentConversationId,
9863
10202
  filters,
9864
10203
  limit: input.limit ?? DEFAULT_LIMIT,
9865
10204
  scope
9866
10205
  });
9867
- const getPermalink = deps.getPermalink ?? getSlackMessagePermalink;
9868
10206
  const matchesOutput = await Promise.all(
9869
10207
  matches.map(async (match) => {
9870
10208
  const reference = parseSlackThreadId(match.conversationId);
@@ -9873,7 +10211,7 @@ function createSlackConversationMessageSearchTool(scope, currentConversationId,
9873
10211
  "Stored Slack conversation search returned an invalid destination"
9874
10212
  );
9875
10213
  }
9876
- const permalink = await getPermalink({
10214
+ const permalink = await getSlackMessagePermalink({
9877
10215
  channelId: reference.channelId,
9878
10216
  messageTs: reference.threadTs
9879
10217
  });
@@ -9901,35 +10239,238 @@ function createSlackConversationMessageSearchTool(scope, currentConversationId,
9901
10239
  }
9902
10240
 
9903
10241
  // src/chat/slack/tools/public-search.ts
9904
- import { z as z38 } from "zod";
10242
+ import { z as z40 } from "zod";
9905
10243
  var DEFAULT_LIMIT2 = 10;
9906
- var optionalTimestampSchema = z38.preprocess(
9907
- (value) => typeof value === "string" && value.trim() === "" ? void 0 : value,
9908
- z38.coerce.number().int().nonnegative().describe("Optional Unix timestamp bound.").optional()
9909
- );
9910
- var searchMessageSchema = z38.object({
9911
- author_name: z38.string().optional(),
9912
- author_user_id: z38.string().optional(),
9913
- channel_id: z38.string().min(1),
9914
- channel_name: z38.string().optional(),
9915
- message_ts: z38.string().min(1),
9916
- content: z38.string(),
9917
- is_author_bot: z38.boolean().optional(),
9918
- permalink: z38.string().url()
10244
+ var DEFAULT_CONTENT_TYPES = ["messages"];
10245
+ var CONTENT_TYPES = ["messages", "files", "channels", "users"];
10246
+ var unixTimestampParam = z40.coerce.number().int().nonnegative();
10247
+ function optionalUnixTimestampParam(description) {
10248
+ return z40.preprocess(
10249
+ (value) => typeof value === "string" && value.trim() === "" ? void 0 : value,
10250
+ unixTimestampParam.optional()
10251
+ ).describe(description);
10252
+ }
10253
+ function normalizeOptionalUnixTimestamp(value) {
10254
+ if (value === void 0 || value === null) {
10255
+ return void 0;
10256
+ }
10257
+ if (typeof value === "string" && value.trim() === "") {
10258
+ return void 0;
10259
+ }
10260
+ return unixTimestampParam.parse(value);
10261
+ }
10262
+ var searchMessageSchema = z40.object({
10263
+ author_name: z40.string().min(1).optional().describe("Author display name."),
10264
+ author_user_id: z40.string().min(1).optional().describe("Author user ID."),
10265
+ channel_id: z40.string().min(1).describe("Channel ID."),
10266
+ channel_name: z40.string().min(1).optional().describe("Channel name."),
10267
+ message_ts: z40.string().min(1).describe("Message timestamp."),
10268
+ content: z40.string().describe("Message text."),
10269
+ is_author_bot: z40.boolean().optional().describe("Whether the author is a bot."),
10270
+ permalink: z40.string().url().describe("Message permalink.")
10271
+ });
10272
+ var searchFileSchema = z40.object({
10273
+ file_id: z40.string().min(1).describe("File ID."),
10274
+ title: z40.string().min(1).optional().describe("File title."),
10275
+ name: z40.string().min(1).optional().describe("File name."),
10276
+ filetype: z40.string().min(1).optional().describe("File type."),
10277
+ user_id: z40.string().min(1).optional().describe("Uploader user ID."),
10278
+ user_name: z40.string().min(1).optional().describe("Uploader user name."),
10279
+ channel_id: z40.string().min(1).optional().describe("Channel ID."),
10280
+ channel_name: z40.string().min(1).optional().describe("Channel name."),
10281
+ permalink: z40.string().url().optional().describe("File permalink."),
10282
+ content: z40.string().min(1).optional().describe("File preview or content snippet.")
10283
+ });
10284
+ var searchChannelSchema = z40.object({
10285
+ channel_id: z40.string().min(1).describe("Channel ID."),
10286
+ channel_name: z40.string().min(1).optional().describe("Channel name."),
10287
+ is_private: z40.boolean().optional().describe("Whether the channel is private."),
10288
+ is_member: z40.boolean().optional().describe("Whether the bot is a member."),
10289
+ topic: z40.string().min(1).optional().describe("Channel topic."),
10290
+ purpose: z40.string().min(1).optional().describe("Channel purpose."),
10291
+ permalink: z40.string().url().optional().describe("Channel permalink.")
10292
+ });
10293
+ var searchUserSchema = z40.object({
10294
+ user_id: z40.string().min(1).describe("User ID."),
10295
+ user_name: z40.string().min(1).optional().describe("Username."),
10296
+ real_name: z40.string().min(1).optional().describe("Real name."),
10297
+ display_name: z40.string().min(1).optional().describe("Display name."),
10298
+ title: z40.string().min(1).optional().describe("Profile title."),
10299
+ permalink: z40.string().url().optional().describe("Profile permalink.")
9919
10300
  });
9920
10301
  var publicSearchOutputSchema = juniorToolOutputSchema.extend({
9921
- query: z38.string(),
9922
- count: z38.number().int().nonnegative(),
9923
- messages: z38.array(searchMessageSchema),
9924
- next_cursor: z38.string().optional()
10302
+ query: z40.string().describe("Search query."),
10303
+ content_types: z40.array(z40.enum(CONTENT_TYPES)).describe("Content types included in the search."),
10304
+ count: z40.number().int().nonnegative().describe("Total matched results."),
10305
+ messages: z40.array(searchMessageSchema).describe("Matched messages."),
10306
+ files: z40.array(searchFileSchema).describe("Matched files."),
10307
+ channels: z40.array(searchChannelSchema).describe("Matched channels."),
10308
+ users: z40.array(searchUserSchema).describe("Matched users."),
10309
+ next_cursor: z40.string().min(1).optional().describe("Cursor for the next result page.")
9925
10310
  });
9926
- function normalizeMessage(value) {
9927
- const parsed = searchMessageSchema.safeParse(value);
9928
- return parsed.success ? parsed.data : void 0;
10311
+ var nonEmptyString = z40.string().trim().min(1);
10312
+ var optionalWireString = z40.preprocess((value) => {
10313
+ if (typeof value !== "string") {
10314
+ return void 0;
10315
+ }
10316
+ const trimmed = value.trim();
10317
+ return trimmed === "" ? void 0 : trimmed;
10318
+ }, nonEmptyString.optional());
10319
+ var optionalWireUrl = z40.preprocess((value) => {
10320
+ if (typeof value !== "string") {
10321
+ return void 0;
10322
+ }
10323
+ const trimmed = value.trim();
10324
+ if (!trimmed) {
10325
+ return void 0;
10326
+ }
10327
+ try {
10328
+ new URL(trimmed);
10329
+ return trimmed;
10330
+ } catch {
10331
+ return void 0;
10332
+ }
10333
+ }, z40.string().url().optional());
10334
+ var optionalWireBoolean = z40.preprocess(
10335
+ (value) => typeof value === "boolean" ? value : void 0,
10336
+ z40.boolean().optional()
10337
+ );
10338
+ var slackSearchMessageWireSchema = z40.object({
10339
+ author_name: optionalWireString,
10340
+ author_user_id: optionalWireString,
10341
+ channel_id: nonEmptyString,
10342
+ channel_name: optionalWireString,
10343
+ message_ts: nonEmptyString,
10344
+ content: z40.string(),
10345
+ is_author_bot: optionalWireBoolean,
10346
+ permalink: z40.string().url()
10347
+ }).transform(
10348
+ (value) => ({
10349
+ channel_id: value.channel_id,
10350
+ message_ts: value.message_ts,
10351
+ content: value.content,
10352
+ permalink: value.permalink,
10353
+ ...value.author_name ? { author_name: value.author_name } : {},
10354
+ ...value.author_user_id ? { author_user_id: value.author_user_id } : {},
10355
+ ...value.channel_name ? { channel_name: value.channel_name } : {},
10356
+ ...value.is_author_bot !== void 0 ? { is_author_bot: value.is_author_bot } : {}
10357
+ })
10358
+ );
10359
+ var slackSearchFileWireSchema = z40.object({
10360
+ id: optionalWireString,
10361
+ file_id: optionalWireString,
10362
+ title: optionalWireString,
10363
+ name: optionalWireString,
10364
+ filetype: optionalWireString,
10365
+ file_type: optionalWireString,
10366
+ user: optionalWireString,
10367
+ user_id: optionalWireString,
10368
+ username: optionalWireString,
10369
+ user_name: optionalWireString,
10370
+ channel_id: optionalWireString,
10371
+ channel_name: optionalWireString,
10372
+ permalink: optionalWireUrl,
10373
+ content: optionalWireString,
10374
+ preview: optionalWireString
10375
+ }).transform((value) => {
10376
+ const fileId = value.file_id ?? value.id;
10377
+ if (!fileId) {
10378
+ return void 0;
10379
+ }
10380
+ return {
10381
+ file_id: fileId,
10382
+ ...value.title ? { title: value.title } : {},
10383
+ ...value.name ? { name: value.name } : {},
10384
+ ...value.filetype ?? value.file_type ? { filetype: value.filetype ?? value.file_type } : {},
10385
+ ...value.user_id ?? value.user ? { user_id: value.user_id ?? value.user } : {},
10386
+ ...value.user_name ?? value.username ? { user_name: value.user_name ?? value.username } : {},
10387
+ ...value.channel_id ? { channel_id: value.channel_id } : {},
10388
+ ...value.channel_name ? { channel_name: value.channel_name } : {},
10389
+ ...value.permalink ? { permalink: value.permalink } : {},
10390
+ ...value.content ?? value.preview ? { content: value.content ?? value.preview } : {}
10391
+ };
10392
+ });
10393
+ var slackSearchChannelWireSchema = z40.object({
10394
+ id: optionalWireString,
10395
+ channel_id: optionalWireString,
10396
+ name: optionalWireString,
10397
+ channel_name: optionalWireString,
10398
+ is_private: optionalWireBoolean,
10399
+ is_member: optionalWireBoolean,
10400
+ topic: optionalWireString,
10401
+ purpose: optionalWireString,
10402
+ permalink: optionalWireUrl
10403
+ }).transform((value) => {
10404
+ const channelId = value.channel_id ?? value.id;
10405
+ if (!channelId) {
10406
+ return void 0;
10407
+ }
10408
+ return {
10409
+ channel_id: channelId,
10410
+ ...value.channel_name ?? value.name ? { channel_name: value.channel_name ?? value.name } : {},
10411
+ ...value.is_private !== void 0 ? { is_private: value.is_private } : {},
10412
+ ...value.is_member !== void 0 ? { is_member: value.is_member } : {},
10413
+ ...value.topic ? { topic: value.topic } : {},
10414
+ ...value.purpose ? { purpose: value.purpose } : {},
10415
+ ...value.permalink ? { permalink: value.permalink } : {}
10416
+ };
10417
+ });
10418
+ var slackSearchUserWireSchema = z40.object({
10419
+ id: optionalWireString,
10420
+ user_id: optionalWireString,
10421
+ name: optionalWireString,
10422
+ username: optionalWireString,
10423
+ user_name: optionalWireString,
10424
+ real_name: optionalWireString,
10425
+ display_name: optionalWireString,
10426
+ title: optionalWireString,
10427
+ permalink: optionalWireUrl
10428
+ }).transform((value) => {
10429
+ const userId = value.user_id ?? value.id;
10430
+ if (!userId) {
10431
+ return void 0;
10432
+ }
10433
+ return {
10434
+ user_id: userId,
10435
+ ...value.user_name ?? value.name ?? value.username ? { user_name: value.user_name ?? value.name ?? value.username } : {},
10436
+ ...value.real_name ? { real_name: value.real_name } : {},
10437
+ ...value.display_name ? { display_name: value.display_name } : {},
10438
+ ...value.title ? { title: value.title } : {},
10439
+ ...value.permalink ? { permalink: value.permalink } : {}
10440
+ };
10441
+ });
10442
+ var slackSearchResponseSchema = z40.object({
10443
+ results: z40.object({
10444
+ messages: z40.array(z40.unknown()).default([]),
10445
+ files: z40.array(z40.unknown()).default([]),
10446
+ channels: z40.array(z40.unknown()).default([]),
10447
+ users: z40.array(z40.unknown()).default([]),
10448
+ // Slack often returns "" on the final page; omit rather than fail the parse.
10449
+ next_cursor: optionalWireString
10450
+ }).default({
10451
+ messages: [],
10452
+ files: [],
10453
+ channels: [],
10454
+ users: []
10455
+ })
10456
+ });
10457
+ function parseItems(values, schema) {
10458
+ const items = [];
10459
+ for (const value of values) {
10460
+ const parsed = schema.safeParse(value);
10461
+ if (parsed.success && parsed.data !== void 0) {
10462
+ items.push(parsed.data);
10463
+ }
10464
+ }
10465
+ return items;
9929
10466
  }
9930
10467
  function explicitSearchError(error) {
9931
10468
  if (error.code === "missing_scope") {
9932
- return "Public Slack search is unavailable because this installation is missing the `search:read.public` scope.";
10469
+ const needed = error.needed?.trim();
10470
+ if (needed) {
10471
+ return `Public Slack search is unavailable because this installation is missing the \`${needed}\` scope.`;
10472
+ }
10473
+ return "Public Slack search is unavailable because this installation is missing a required search scope (`search:read.public`, and `search:read.files` / `search:read.users` when those content types are requested).";
9933
10474
  }
9934
10475
  if (error.code === "feature_unavailable") {
9935
10476
  return "Public Slack search is not available for this Slack workspace or app installation.";
@@ -9939,33 +10480,33 @@ function explicitSearchError(error) {
9939
10480
  }
9940
10481
  return void 0;
9941
10482
  }
10483
+ function normalizeContentTypes(contentTypes) {
10484
+ const selected = contentTypes?.length ? contentTypes : [...DEFAULT_CONTENT_TYPES];
10485
+ return [...new Set(selected)];
10486
+ }
9942
10487
  function createSlackPublicSearchTool(actionToken) {
9943
10488
  return zodTool({
9944
- description: "Search public Slack channel messages across the current workspace. Use when the user asks about company activity, announcements, public mentions, or context outside the active channel. Search only when requested or clearly needed, prefer focused keywords and time bounds, and cite returned permalinks. This never searches private channels or DMs.",
10489
+ description: "Search live public Slack content across the workspace. Defaults to messages; optionally include files, channels, or users. Private channels and DMs are never searched.",
9945
10490
  annotations: {
9946
10491
  destructiveHint: false,
9947
10492
  idempotentHint: true,
9948
10493
  openWorldHint: true,
9949
10494
  readOnlyHint: true
9950
10495
  },
9951
- inputSchema: z38.object({
9952
- query: z38.string().trim().min(1).max(500).describe(
9953
- "A focused Slack search query, including Slack search filters when useful."
9954
- ),
9955
- after: optionalTimestampSchema.describe(
9956
- "Optional Unix timestamp lower bound."
9957
- ),
9958
- before: optionalTimestampSchema.describe(
9959
- "Optional Unix timestamp upper bound."
9960
- ),
9961
- cursor: z38.string().min(1).describe("Cursor for the next result page.").optional(),
9962
- limit: z38.coerce.number().int().min(1).max(20).describe("Maximum results to return; Slack allows at most 20.").optional(),
9963
- sort: z38.enum(["score", "timestamp"]).describe("Rank by relevance or timestamp.").optional(),
9964
- sort_dir: z38.enum(["asc", "desc"]).describe("Sort direction.").optional()
10496
+ inputSchema: z40.object({
10497
+ query: z40.string().trim().min(1).max(500).describe("Slack search query."),
10498
+ content_types: z40.array(z40.enum(CONTENT_TYPES)).min(1).max(4).describe("Content types to include. Defaults to messages.").optional(),
10499
+ after: optionalUnixTimestampParam("Unix timestamp lower bound."),
10500
+ before: optionalUnixTimestampParam("Unix timestamp upper bound."),
10501
+ cursor: z40.string().min(1).describe("Cursor for the next result page.").optional(),
10502
+ limit: z40.coerce.number().int().min(1).max(20).describe("Maximum results to return; Slack allows at most 20.").optional(),
10503
+ sort: z40.enum(["score", "timestamp"]).describe("Rank by relevance or timestamp.").optional(),
10504
+ sort_dir: z40.enum(["asc", "desc"]).describe("Sort direction.").optional()
9965
10505
  }),
9966
10506
  outputSchema: publicSearchOutputSchema,
9967
10507
  execute: async ({
9968
10508
  query,
10509
+ content_types,
9969
10510
  after,
9970
10511
  before,
9971
10512
  cursor,
@@ -9973,36 +10514,63 @@ function createSlackPublicSearchTool(actionToken) {
9973
10514
  sort,
9974
10515
  sort_dir
9975
10516
  }) => {
10517
+ if (!actionToken) {
10518
+ throw new ToolInputError(
10519
+ "Public Slack search needs a fresh interactive mention or DM. Slack only issues the short-lived action token on those turns, so resumes and scheduled runs cannot search the workspace. Ask again in a new message, or use channel history / thread read when you already know the channel."
10520
+ );
10521
+ }
9976
10522
  try {
9977
- const normalizedAfter = optionalTimestampSchema.parse(after);
9978
- const normalizedBefore = optionalTimestampSchema.parse(before);
9979
- const response = await withSlackRetries(
9980
- () => getSlackClient().apiCall("assistant.search.context", {
9981
- action_token: actionToken,
9982
- query,
9983
- channel_types: ["public_channel"],
9984
- content_types: ["messages"],
9985
- include_bots: true,
9986
- limit: limit ?? DEFAULT_LIMIT2,
9987
- ...normalizedAfter !== void 0 ? { after: normalizedAfter } : {},
9988
- ...normalizedBefore !== void 0 ? { before: normalizedBefore } : {},
9989
- ...cursor ? { cursor } : {},
9990
- ...sort ? { sort } : {},
9991
- ...sort_dir ? { sort_dir } : {}
9992
- }),
9993
- 3,
9994
- {
9995
- action: "assistant.search.context",
9996
- idempotent: true
9997
- }
10523
+ const normalizedContentTypes = normalizeContentTypes(content_types);
10524
+ const normalizedAfter = normalizeOptionalUnixTimestamp(after);
10525
+ const normalizedBefore = normalizeOptionalUnixTimestamp(before);
10526
+ const response = slackSearchResponseSchema.parse(
10527
+ await withSlackRetries(
10528
+ () => getSlackClient().apiCall("assistant.search.context", {
10529
+ action_token: actionToken,
10530
+ query,
10531
+ channel_types: ["public_channel"],
10532
+ content_types: normalizedContentTypes,
10533
+ include_bots: true,
10534
+ limit: limit ?? DEFAULT_LIMIT2,
10535
+ ...normalizedAfter !== void 0 ? { after: normalizedAfter } : {},
10536
+ ...normalizedBefore !== void 0 ? { before: normalizedBefore } : {},
10537
+ ...cursor ? { cursor } : {},
10538
+ ...sort ? { sort } : {},
10539
+ ...sort_dir ? { sort_dir } : {}
10540
+ }),
10541
+ 3,
10542
+ {
10543
+ action: "assistant.search.context",
10544
+ idempotent: true
10545
+ }
10546
+ )
10547
+ );
10548
+ const messages = parseItems(
10549
+ response.results.messages,
10550
+ slackSearchMessageWireSchema
10551
+ );
10552
+ const files = parseItems(
10553
+ response.results.files,
10554
+ slackSearchFileWireSchema
9998
10555
  );
9999
- const messages = (response.results?.messages ?? []).map(normalizeMessage).filter((message) => Boolean(message));
10000
- const nextCursor = response.results?.next_cursor;
10556
+ const channels = parseItems(
10557
+ response.results.channels,
10558
+ slackSearchChannelWireSchema
10559
+ );
10560
+ const users = parseItems(
10561
+ response.results.users,
10562
+ slackSearchUserWireSchema
10563
+ );
10564
+ const count = messages.length + files.length + channels.length + users.length;
10001
10565
  return {
10002
10566
  query,
10003
- count: messages.length,
10567
+ content_types: normalizedContentTypes,
10568
+ count,
10004
10569
  messages,
10005
- ...typeof nextCursor === "string" && nextCursor ? { next_cursor: nextCursor } : {}
10570
+ files,
10571
+ channels,
10572
+ users,
10573
+ ...response.results.next_cursor ? { next_cursor: response.results.next_cursor } : {}
10006
10574
  };
10007
10575
  } catch (error) {
10008
10576
  if (error instanceof SlackActionError) {
@@ -10018,7 +10586,7 @@ function createSlackPublicSearchTool(actionToken) {
10018
10586
  }
10019
10587
 
10020
10588
  // src/chat/slack/tools/message-add-reaction.ts
10021
- import { z as z39 } from "zod";
10589
+ import { z as z41 } from "zod";
10022
10590
 
10023
10591
  // src/chat/tools/idempotency.ts
10024
10592
  function stableSerialize(value) {
@@ -10048,9 +10616,9 @@ function createSlackMessageAddReactionTool(context, state) {
10048
10616
  openWorldHint: true,
10049
10617
  readOnlyHint: false
10050
10618
  },
10051
- description: "Add an emoji reaction to the current inbound Slack message. Use for explicit reaction requests and for non-text acknowledgements on the current message when no thread reply is needed. Provide a Slack emoji alias name (for example `thumbsup`, `white_check_mark`, or `thumbsup::skin-tone-6`), not a unicode emoji glyph. The target message is injected by runtime context; do not use this for arbitrary historical messages.",
10052
- inputSchema: z39.object({
10053
- emoji: z39.string().min(1).max(64).describe(
10619
+ description: "Add an emoji reaction to the current inbound Slack message.",
10620
+ inputSchema: z41.object({
10621
+ emoji: z41.string().min(1).max(64).describe(
10054
10622
  "Slack emoji alias name to react with (for example `thumbsup`, `white_check_mark`, or `thumbsup::skin-tone-6`). Optional surrounding colons are allowed."
10055
10623
  )
10056
10624
  }),
@@ -10099,14 +10667,14 @@ function createSlackMessageAddReactionTool(context, state) {
10099
10667
 
10100
10668
  // src/chat/slack/tools/send-files.ts
10101
10669
  import { createHash as createHash4 } from "crypto";
10102
- import { z as z40 } from "zod";
10670
+ import { z as z42 } from "zod";
10103
10671
  var sendFilesResultSchema = juniorToolOutputSchema.extend({
10104
- target: z40.string().min(1),
10105
- channel_id: z40.string().min(1),
10106
- deduplicated: z40.boolean().optional(),
10107
- file_count: z40.number().int().nonnegative(),
10108
- file_ids: z40.array(z40.string().min(1)).optional(),
10109
- thread_ts: z40.string().min(1)
10672
+ target: z42.string().min(1),
10673
+ channel_id: z42.string().min(1),
10674
+ deduplicated: z42.boolean().optional(),
10675
+ file_count: z42.number().int().nonnegative(),
10676
+ file_ids: z42.array(z42.string().min(1)).optional(),
10677
+ thread_ts: z42.string().min(1)
10110
10678
  });
10111
10679
  function normalizeFiles(files) {
10112
10680
  return files.map((file) => ({
@@ -10135,9 +10703,9 @@ function createSendFilesTool(context, state, materializeFile) {
10135
10703
  openWorldHint: true,
10136
10704
  readOnlyHint: false
10137
10705
  },
10138
- description: "Send one or more sandbox files into the active Slack conversation. Use when the user asks to attach, send, or share files here, in this conversation, or in this thread. Do not use for ordinary assistant text, top-level channel posts, other named channels, inline @mentions, or pinging mentioned users.",
10139
- inputSchema: z40.object({
10140
- files: z40.array(sandboxFileReferenceSchema).min(1).describe(
10706
+ description: "Send one or more sandbox files into the active Slack conversation.",
10707
+ inputSchema: z42.object({
10708
+ files: z42.array(sandboxFileReferenceSchema).min(1).describe(
10141
10709
  "One or more existing sandbox files. Objects returned by imageGenerate or webFetch can be passed unchanged."
10142
10710
  )
10143
10711
  }),
@@ -10223,7 +10791,7 @@ function extractCanvasId(input) {
10223
10791
  }
10224
10792
  }
10225
10793
 
10226
- // src/chat/slack/tools/canvas/markdown.ts
10794
+ // src/chat/slack/tool-support/canvas/markdown.ts
10227
10795
  function getListType(marker) {
10228
10796
  return /^\d/.test(marker) ? "numbered" : "bullet";
10229
10797
  }
@@ -10372,7 +10940,7 @@ function normalizeCanvasMarkdown(markdown) {
10372
10940
  };
10373
10941
  }
10374
10942
 
10375
- // src/chat/slack/tools/canvas/api.ts
10943
+ // src/chat/slack/tool-support/canvas/api.ts
10376
10944
  async function createCanvas(input) {
10377
10945
  const client = getSlackClient();
10378
10946
  const normalizedChannelId = normalizeSlackConversationId(input.channelId);
@@ -10530,7 +11098,7 @@ async function readCanvas(canvasIdOrUrl) {
10530
11098
  }
10531
11099
 
10532
11100
  // src/chat/slack/tools/canvas/create.ts
10533
- import { z as z41 } from "zod";
11101
+ import { z as z43 } from "zod";
10534
11102
  function createSlackCanvasCreateTool(context, state) {
10535
11103
  return zodTool({
10536
11104
  annotations: {
@@ -10539,10 +11107,10 @@ function createSlackCanvasCreateTool(context, state) {
10539
11107
  openWorldHint: true,
10540
11108
  readOnlyHint: false
10541
11109
  },
10542
- description: "Create a Slack canvas for long-form output in the active assistant context channel. Use when the answer is better as a reusable document than a thread reply: long-form research, timelines, bios/profiles, structured notes, plans, comparisons, or anything likely to exceed one compact Slack reply. After creating it, reply with one or two short sentences plus the canvas link; do not recap the canvas contents. Do not use for short answers that fit cleanly in one normal thread reply.",
10543
- inputSchema: z41.object({
10544
- title: z41.string().min(1).max(160).describe("Canvas title."),
10545
- markdown: z41.string().min(1).describe("Canvas markdown body content.")
11110
+ description: "Create a Slack canvas in the active channel.",
11111
+ inputSchema: z43.object({
11112
+ title: z43.string().min(1).max(160).describe("Canvas title."),
11113
+ markdown: z43.string().min(1).describe("Canvas markdown body content.")
10546
11114
  }),
10547
11115
  outputSchema: juniorToolOutputSchema,
10548
11116
  execute: async ({ title, markdown }) => {
@@ -10585,7 +11153,9 @@ function createSlackCanvasCreateTool(context, state) {
10585
11153
  });
10586
11154
  }
10587
11155
 
10588
- // src/chat/slack/tools/canvas/context.ts
11156
+ // src/chat/slack/tool-support/canvas/context.ts
11157
+ import { z as z44 } from "zod";
11158
+ var slackCanvasRefParam = z44.string().min(1).describe("Slack canvas/file ID or canvas/docs URL.");
10589
11159
  function resolveCanvasTarget(canvas) {
10590
11160
  const canvasId = extractCanvasId(canvas);
10591
11161
  if (!canvasId) {
@@ -10598,26 +11168,26 @@ function resolveCanvasTarget(canvas) {
10598
11168
  }
10599
11169
 
10600
11170
  // src/chat/slack/tools/canvas/edit.ts
10601
- import { z as z42 } from "zod";
11171
+ import { z as z45 } from "zod";
10602
11172
  function prepareCanvasEditArguments(input) {
10603
11173
  return prepareTextReplacementArguments(input);
10604
11174
  }
10605
- var editReplacementSchema2 = z42.object({
10606
- oldText: z42.string().min(1).describe(
11175
+ var editReplacementSchema2 = z45.object({
11176
+ oldText: z45.string().min(1).describe(
10607
11177
  "Exact Canvas markdown to replace. It must be unique in the current Canvas body and must not overlap another edit."
10608
11178
  ),
10609
- newText: z42.string().describe("Replacement Canvas markdown for this edit.")
11179
+ newText: z45.string().describe("Replacement Canvas markdown for this edit.")
10610
11180
  });
10611
11181
  var slackCanvasEditOutputSchema = juniorToolOutputSchema.extend({
10612
- canvas_id: z42.string().optional(),
10613
- title: z42.string().optional(),
10614
- permalink: z42.string().optional(),
10615
- diff: z42.string().optional(),
10616
- first_changed_line: z42.number().int().positive().optional(),
10617
- replacements: z42.number().int().nonnegative().optional(),
10618
- normalized_heading_count: z42.number().int().nonnegative().optional(),
10619
- summary: z42.string().optional(),
10620
- deduplicated: z42.boolean().optional()
11182
+ canvas_id: z45.string().optional(),
11183
+ title: z45.string().optional(),
11184
+ permalink: z45.string().optional(),
11185
+ diff: z45.string().optional(),
11186
+ first_changed_line: z45.number().int().positive().optional(),
11187
+ replacements: z45.number().int().nonnegative().optional(),
11188
+ normalized_heading_count: z45.number().int().nonnegative().optional(),
11189
+ summary: z45.string().optional(),
11190
+ deduplicated: z45.boolean().optional()
10621
11191
  }).strict();
10622
11192
  function createSlackCanvasEditTool(state) {
10623
11193
  return zodTool({
@@ -10627,12 +11197,12 @@ function createSlackCanvasEditTool(state) {
10627
11197
  openWorldHint: true,
10628
11198
  readOnlyHint: false
10629
11199
  },
10630
- description: "Edit one Slack canvas with exact markdown replacements. Use for precise changes to existing Canvas content; prefer this over slackCanvasWrite for targeted changes. Each oldText must match exactly, be unique, and not overlap another edit. Returns a diff. Multiple changes to the same canvas: use one edits[] call.",
11200
+ description: "Edit a Slack canvas with exact markdown replacements. Returns a diff.",
10631
11201
  prepareArguments: prepareCanvasEditArguments,
10632
11202
  executionMode: "sequential",
10633
- inputSchema: z42.object({
10634
- canvas: z42.string().min(1).describe("Canvas/file ID (e.g. `F0ABCDEF`) or Slack canvas/docs URL."),
10635
- edits: z42.array(editReplacementSchema2).min(1).describe(
11203
+ inputSchema: z45.object({
11204
+ canvas: slackCanvasRefParam,
11205
+ edits: z45.array(editReplacementSchema2).min(1).describe(
10636
11206
  "Exact replacements matched against the current Canvas body, not incrementally."
10637
11207
  )
10638
11208
  }),
@@ -10685,34 +11255,32 @@ function createSlackCanvasEditTool(state) {
10685
11255
  }
10686
11256
 
10687
11257
  // src/chat/slack/tools/canvas/read.ts
10688
- import { z as z43 } from "zod";
11258
+ import { z as z46 } from "zod";
10689
11259
  var slackCanvasReadOutputSchema = juniorToolOutputSchema.extend({
10690
- canvas_id: z43.string().optional(),
10691
- title: z43.string().optional(),
10692
- permalink: z43.string().optional(),
10693
- mimetype: z43.string().optional(),
10694
- filetype: z43.string().optional(),
10695
- original_byte_length: z43.number().int().nonnegative().optional(),
10696
- content: z43.string().optional(),
10697
- start_line: z43.number().int().positive().optional(),
10698
- end_line: z43.number().int().nonnegative().optional(),
10699
- total_lines: z43.number().int().nonnegative().optional()
11260
+ canvas_id: z46.string().optional(),
11261
+ title: z46.string().optional(),
11262
+ permalink: z46.string().optional(),
11263
+ mimetype: z46.string().optional(),
11264
+ filetype: z46.string().optional(),
11265
+ original_byte_length: z46.number().int().nonnegative().optional(),
11266
+ content: z46.string().optional(),
11267
+ start_line: z46.number().int().positive().optional(),
11268
+ end_line: z46.number().int().nonnegative().optional(),
11269
+ total_lines: z46.number().int().nonnegative().optional()
10700
11270
  }).strict();
10701
11271
  function createSlackCanvasReadTool() {
10702
11272
  return zodTool({
10703
- description: "Read a bounded line range from a Slack canvas as markdown. Use when you need exact Canvas contents to verify facts or make edits safely. Do not use for generic web pages \u2014 use webFetch for those.",
11273
+ description: "Read a bounded line range from a Slack canvas as markdown.",
10704
11274
  annotations: {
10705
11275
  destructiveHint: false,
10706
11276
  idempotentHint: true,
10707
11277
  openWorldHint: true,
10708
11278
  readOnlyHint: true
10709
11279
  },
10710
- inputSchema: z43.object({
10711
- canvas: z43.string().min(1).describe(
10712
- "Canvas/file ID (e.g. `F0ABCDEF`) or Slack canvas/docs URL (e.g. `https://team.slack.com/docs/T.../F...`)."
10713
- ),
10714
- offset: z43.coerce.number().int().min(1).describe("1-indexed line number to start reading from.").optional(),
10715
- limit: z43.coerce.number().int().min(1).describe("Maximum number of lines to read. Defaults to 1000.").optional()
11280
+ inputSchema: z46.object({
11281
+ canvas: slackCanvasRefParam,
11282
+ offset: z46.coerce.number().int().min(1).describe("1-indexed line number to start reading from.").optional(),
11283
+ limit: z46.coerce.number().int().min(1).describe("Maximum number of lines to read. Defaults to 1000.").optional()
10716
11284
  }),
10717
11285
  outputSchema: slackCanvasReadOutputSchema,
10718
11286
  execute: async ({ canvas, offset, limit }) => {
@@ -10748,7 +11316,7 @@ function createSlackCanvasReadTool() {
10748
11316
  }
10749
11317
 
10750
11318
  // src/chat/slack/tools/canvas/write.ts
10751
- import { z as z44 } from "zod";
11319
+ import { z as z47 } from "zod";
10752
11320
  function createSlackCanvasWriteTool(state) {
10753
11321
  return zodTool({
10754
11322
  annotations: {
@@ -10757,11 +11325,11 @@ function createSlackCanvasWriteTool(state) {
10757
11325
  openWorldHint: true,
10758
11326
  readOnlyHint: false
10759
11327
  },
10760
- description: "Write UTF-8 markdown content to a Slack canvas. Use for deliberate full-Canvas replacement after validation; use slackCanvasEdit for targeted changes to existing canvas content.",
11328
+ description: "Replace the full markdown body of a Slack canvas.",
10761
11329
  executionMode: "sequential",
10762
- inputSchema: z44.object({
10763
- canvas: z44.string().min(1).describe("Canvas/file ID (e.g. `F0ABCDEF`) or Slack canvas/docs URL."),
10764
- content: z44.string().describe("UTF-8 markdown content to write.")
11330
+ inputSchema: z47.object({
11331
+ canvas: slackCanvasRefParam,
11332
+ content: z47.string().describe("UTF-8 markdown content to write.")
10765
11333
  }),
10766
11334
  outputSchema: juniorToolOutputSchema,
10767
11335
  execute: async ({ canvas, content }) => {
@@ -10795,7 +11363,7 @@ function createSlackCanvasWriteTool(state) {
10795
11363
  });
10796
11364
  }
10797
11365
 
10798
- // src/chat/slack/tools/list/api.ts
11366
+ // src/chat/slack/tool-support/list/api.ts
10799
11367
  function normalizeKey(value) {
10800
11368
  return value.toLowerCase().replace(/[^a-z0-9]+/g, "_");
10801
11369
  }
@@ -10982,8 +11550,13 @@ async function updateListItem(input) {
10982
11550
  );
10983
11551
  }
10984
11552
 
11553
+ // src/chat/slack/tool-support/list/params.ts
11554
+ import { z as z48 } from "zod";
11555
+ var slackListIdParam = z48.string().min(1).describe("Slack list ID.");
11556
+ var slackListItemIdParam = z48.string().min(1).describe("Slack list item ID.");
11557
+
10985
11558
  // src/chat/slack/tools/list/add-items.ts
10986
- import { z as z45 } from "zod";
11559
+ import { z as z49 } from "zod";
10987
11560
  function createSlackListAddItemsTool(state) {
10988
11561
  return zodTool({
10989
11562
  annotations: {
@@ -10992,14 +11565,14 @@ function createSlackListAddItemsTool(state) {
10992
11565
  openWorldHint: true,
10993
11566
  readOnlyHint: false
10994
11567
  },
10995
- description: "Add tasks to a Slack list. Use the list_id from the list create result or conversation history.",
10996
- inputSchema: z45.object({
10997
- list_id: z45.string().min(1).describe("ID of the Slack list to update."),
10998
- items: z45.array(z45.string().min(1)).min(1).max(25).describe("List item titles to create."),
11568
+ description: "Add tasks to a Slack list.",
11569
+ inputSchema: z49.object({
11570
+ list_id: slackListIdParam,
11571
+ items: z49.array(z49.string().min(1)).min(1).max(25).describe("List item titles to create."),
10999
11572
  assignee_user_id: slackUserIdParam(
11000
11573
  "Optional Slack user ID assigned to all created items."
11001
11574
  ).optional(),
11002
- due_date: z45.string().regex(/^\d{4}-\d{2}-\d{2}$/).describe("Optional due date in YYYY-MM-DD format.").optional()
11575
+ due_date: z49.string().regex(/^\d{4}-\d{2}-\d{2}$/).describe("Optional due date in YYYY-MM-DD format.").optional()
11003
11576
  }),
11004
11577
  outputSchema: juniorToolOutputSchema,
11005
11578
  execute: async ({ list_id, items, assignee_user_id, due_date }) => {
@@ -11038,7 +11611,7 @@ function createSlackListAddItemsTool(state) {
11038
11611
  }
11039
11612
 
11040
11613
  // src/chat/slack/tools/list/create.ts
11041
- import { z as z46 } from "zod";
11614
+ import { z as z50 } from "zod";
11042
11615
  function createSlackListCreateTool(state) {
11043
11616
  return zodTool({
11044
11617
  annotations: {
@@ -11047,9 +11620,9 @@ function createSlackListCreateTool(state) {
11047
11620
  openWorldHint: true,
11048
11621
  readOnlyHint: false
11049
11622
  },
11050
- description: "Create a Slack todo list for action tracking. Use when the user needs structured tasks with ownership/completion tracking. Do not use for one-off notes without task management needs.",
11051
- inputSchema: z46.object({
11052
- name: z46.string().min(1).max(160).describe("Name for the new Slack list.")
11623
+ description: "Create a Slack todo list.",
11624
+ inputSchema: z50.object({
11625
+ name: z50.string().min(1).max(160).describe("Name for the new Slack list.")
11053
11626
  }),
11054
11627
  outputSchema: juniorToolOutputSchema,
11055
11628
  execute: async ({ name }) => {
@@ -11074,19 +11647,19 @@ function createSlackListCreateTool(state) {
11074
11647
  }
11075
11648
 
11076
11649
  // src/chat/slack/tools/list/get-items.ts
11077
- import { z as z47 } from "zod";
11650
+ import { z as z51 } from "zod";
11078
11651
  function createSlackListGetItemsTool() {
11079
11652
  return zodTool({
11080
- description: "Read items from a Slack list. Use the list_id from a prior tool result or conversation history.",
11653
+ description: "Read items from a Slack list.",
11081
11654
  annotations: {
11082
11655
  destructiveHint: false,
11083
11656
  idempotentHint: true,
11084
11657
  openWorldHint: true,
11085
11658
  readOnlyHint: true
11086
11659
  },
11087
- inputSchema: z47.object({
11088
- list_id: z47.string().min(1).describe("ID of the Slack list to read."),
11089
- limit: z47.coerce.number().int().min(1).max(200).describe("Maximum number of list items to return.").optional()
11660
+ inputSchema: z51.object({
11661
+ list_id: slackListIdParam,
11662
+ limit: z51.coerce.number().int().min(1).max(200).describe("Maximum number of list items to return.").optional()
11090
11663
  }),
11091
11664
  outputSchema: juniorToolOutputSchema,
11092
11665
  execute: async ({ list_id, limit }) => {
@@ -11101,23 +11674,23 @@ function createSlackListGetItemsTool() {
11101
11674
  }
11102
11675
 
11103
11676
  // src/chat/slack/tools/list/update-item.ts
11104
- import { z as z48 } from "zod";
11105
- var booleanInput3 = (description) => z48.preprocess(
11677
+ import { z as z52 } from "zod";
11678
+ var booleanInput3 = (description) => z52.preprocess(
11106
11679
  (value) => value === "true" ? true : value === "false" ? false : value,
11107
- z48.boolean()
11680
+ z52.boolean()
11108
11681
  ).describe(description);
11109
- var updateListItemInputSchema = z48.union([
11110
- z48.object({
11111
- list_id: z48.string().min(1).describe("ID of the Slack list to update."),
11112
- item_id: z48.string().min(1).describe("ID of the Slack list item to update."),
11682
+ var updateListItemInputSchema = z52.union([
11683
+ z52.object({
11684
+ list_id: slackListIdParam,
11685
+ item_id: slackListItemIdParam,
11113
11686
  completed: booleanInput3("Optional completion status update."),
11114
- title: z48.string().min(1).describe("Optional new item title.").optional()
11687
+ title: z52.string().min(1).describe("Optional new item title.").optional()
11115
11688
  }),
11116
- z48.object({
11117
- list_id: z48.string().min(1).describe("ID of the Slack list to update."),
11118
- item_id: z48.string().min(1).describe("ID of the Slack list item to update."),
11689
+ z52.object({
11690
+ list_id: slackListIdParam,
11691
+ item_id: slackListItemIdParam,
11119
11692
  completed: booleanInput3("Optional completion status update.").optional(),
11120
- title: z48.string().min(1).describe("Optional new item title.")
11693
+ title: z52.string().min(1).describe("Optional new item title.")
11121
11694
  })
11122
11695
  ]);
11123
11696
  function createSlackListUpdateItemTool(state) {
@@ -11128,7 +11701,7 @@ function createSlackListUpdateItemTool(state) {
11128
11701
  openWorldHint: true,
11129
11702
  readOnlyHint: false
11130
11703
  },
11131
- description: "Update an item in a Slack list. Use list_id and item_id from prior tool results or conversation history.",
11704
+ description: "Update an item in a Slack list.",
11132
11705
  inputSchema: updateListItemInputSchema,
11133
11706
  outputSchema: juniorToolOutputSchema,
11134
11707
  execute: async ({ list_id, item_id, completed, title }) => {
@@ -11164,33 +11737,10 @@ function createSlackListUpdateItemTool(state) {
11164
11737
  });
11165
11738
  }
11166
11739
 
11167
- // src/chat/slack/tools/channel-access.ts
11168
- async function checkSlackChannelReadAccess(args) {
11169
- const currentChannels = args.currentChannelIds.filter(
11170
- (channelId) => Boolean(channelId)
11171
- );
11172
- if (currentChannels.includes(args.targetChannelId)) {
11173
- return { allowed: true };
11174
- }
11175
- const store = args.store ?? getConversationStore();
11176
- const visibility = await store.getDestinationVisibility({
11177
- provider: "slack",
11178
- providerTenantId: args.teamId,
11179
- providerDestinationId: args.targetChannelId
11180
- });
11181
- if (visibility === "public") {
11182
- return { allowed: true };
11183
- }
11184
- return {
11185
- allowed: false,
11186
- error: "Cannot read this Slack conversation: only the current conversation or public channels Junior has seen in this workspace are readable."
11187
- };
11188
- }
11189
-
11190
11740
  // src/chat/slack/tools/thread-read.ts
11191
- import { z as z49 } from "zod";
11741
+ import { z as z53 } from "zod";
11192
11742
 
11193
- // src/chat/slack/tools/slack-message-url.ts
11743
+ // src/chat/slack/tool-support/slack-message-url.ts
11194
11744
  var SLACK_HOST_PATTERN = /^[a-z0-9-]+\.slack(?:-gov)?\.com$/;
11195
11745
  var ARCHIVE_PATH_PATTERN = /^\/archives\/([CDG][A-Z0-9]+)\/p(\d{10})(\d{6})$/;
11196
11746
  function pTimestampToTs(seconds, micros) {
@@ -11445,27 +11995,23 @@ function truncateMessages(messages, maxChars) {
11445
11995
  }
11446
11996
  return { messages: kept, omitted: messages.length - kept.length };
11447
11997
  }
11448
- function createSlackThreadReadTool(context, deps = {}) {
11998
+ function createSlackThreadReadTool(context) {
11449
11999
  return zodTool({
11450
- description: "Read a Slack thread from a shared Slack message archive URL or explicit channel + timestamp. Use when the user shares a Slack message link (https://*.slack.com/archives/...) and you need the referenced message and its thread context. Only the current conversation and public channels Junior has seen in this workspace are readable.",
12000
+ description: "Read a Slack thread from a shared archive URL or explicit channel + timestamp. Works for the current conversation and public channels.",
11451
12001
  annotations: {
11452
12002
  destructiveHint: false,
11453
12003
  idempotentHint: true,
11454
12004
  openWorldHint: true,
11455
12005
  readOnlyHint: true
11456
12006
  },
11457
- inputSchema: z49.object({
11458
- url: z49.string().min(1).describe(
11459
- "Slack message archive URL, e.g. https://workspace.slack.com/archives/C123/p1700000000123456"
11460
- ).optional(),
11461
- channel_id: slackChannelIdParam(
11462
- "Slack channel/conversation ID (e.g. C123). Use with `ts` as an alternative to `url`."
11463
- ).optional(),
12007
+ inputSchema: z53.object({
12008
+ url: z53.string().min(1).describe("Slack message archive URL.").optional(),
12009
+ channel_id: slackChannelRefParam.optional(),
11464
12010
  ts: slackTimestampParam(
11465
- "Slack message timestamp (e.g. 1700000000.123456). May be the thread root or any message in the thread."
12011
+ "Slack message timestamp. May be the thread root or any message in the thread."
11466
12012
  ).optional(),
11467
- limit: z49.coerce.number().int().min(1).max(1e3).describe("Maximum number of thread messages to fetch.").optional(),
11468
- max_pages: z49.coerce.number().int().min(1).max(10).describe("Maximum number of Slack API pages to traverse.").optional()
12013
+ limit: z53.coerce.number().int().min(1).max(1e3).describe("Maximum number of thread messages to fetch.").optional(),
12014
+ max_pages: z53.coerce.number().int().min(1).max(10).describe("Maximum number of Slack API pages to traverse.").optional()
11469
12015
  }),
11470
12016
  outputSchema: juniorToolOutputSchema,
11471
12017
  execute: async ({ url, channel_id, ts, limit, max_pages }) => {
@@ -11485,14 +12031,11 @@ function createSlackThreadReadTool(context, deps = {}) {
11485
12031
  if (!parsedTs.ok) {
11486
12032
  throw new ToolInputError(parsedTs.error);
11487
12033
  }
11488
- const parsedChannelId = parseRequiredSlackChannelIdParam(
11489
- "channel_id",
11490
- channel_id
11491
- );
11492
- if (!parsedChannelId.ok) {
11493
- throw new ToolInputError(parsedChannelId.error);
11494
- }
11495
- channelId = parsedChannelId.value;
12034
+ const target = await resolveSlackChannelRef({
12035
+ field: "channel_id",
12036
+ value: channel_id
12037
+ });
12038
+ channelId = target.channelId;
11496
12039
  messageTs = parsedTs.value;
11497
12040
  } else {
11498
12041
  throw new ToolInputError(
@@ -11504,7 +12047,6 @@ function createSlackThreadReadTool(context, deps = {}) {
11504
12047
  context.destinationChannelId,
11505
12048
  context.sourceChannelId
11506
12049
  ],
11507
- store: deps.visibilityStore,
11508
12050
  targetChannelId: channelId,
11509
12051
  teamId: context.teamId
11510
12052
  });
@@ -11512,20 +12054,57 @@ function createSlackThreadReadTool(context, deps = {}) {
11512
12054
  throw new ToolInputError(access.error);
11513
12055
  }
11514
12056
  const lookupTs = threadTs ?? messageTs;
12057
+ const readReplies = async () => listThreadReplies({
12058
+ channelId,
12059
+ threadTs: lookupTs,
12060
+ limit: limit ?? 1e3,
12061
+ maxPages: max_pages
12062
+ });
11515
12063
  let replies;
12064
+ let joined = false;
12065
+ let channelName = access.channelName;
12066
+ let readError;
11516
12067
  try {
11517
- replies = await listThreadReplies({
11518
- channelId,
11519
- threadTs: lookupTs,
11520
- limit: limit ?? 1e3,
11521
- maxPages: max_pages
11522
- });
12068
+ replies = await readReplies();
11523
12069
  } catch (error) {
12070
+ readError = error;
12071
+ const canJoin = error instanceof SlackActionError && error.code === "not_in_channel" && access.isMember !== true;
12072
+ if (canJoin) {
12073
+ const joinResult = await joinPublicChannelForRead({
12074
+ channelName,
12075
+ targetChannelId: channelId
12076
+ });
12077
+ if (!joinResult.ok) {
12078
+ throw new ToolInputError(joinResult.error);
12079
+ }
12080
+ joined = true;
12081
+ channelName = joinResult.channelName ?? channelName;
12082
+ try {
12083
+ replies = await readReplies();
12084
+ readError = void 0;
12085
+ } catch (retryError) {
12086
+ readError = retryError;
12087
+ }
12088
+ }
12089
+ }
12090
+ if (!replies) {
12091
+ const error = readError;
11524
12092
  if (error instanceof SlackActionError) {
11525
- throw new ToolInputError(
11526
- "Could not read this Slack thread. The bot may not be in the channel or may lack history scopes.",
11527
- { cause: error }
11528
- );
12093
+ if (error.code === "not_in_channel") {
12094
+ throw new ToolInputError(
12095
+ "Could not read this Slack thread because the bot is not in the channel.",
12096
+ { cause: error }
12097
+ );
12098
+ }
12099
+ if (error.code === "missing_scope") {
12100
+ throw new ToolInputError(
12101
+ "Could not read this Slack thread because this installation is missing history scopes.",
12102
+ { cause: error }
12103
+ );
12104
+ }
12105
+ throw new ToolInputError("Could not read this Slack thread.", {
12106
+ cause: error
12107
+ });
11529
12108
  }
11530
12109
  throw error;
11531
12110
  }
@@ -11541,6 +12120,8 @@ function createSlackThreadReadTool(context, deps = {}) {
11541
12120
  );
11542
12121
  return {
11543
12122
  channel_id: channelId,
12123
+ ...channelName ? { channel_name: channelName } : {},
12124
+ ...joined ? { joined_channel: true } : {},
11544
12125
  target_message_ts: messageTs,
11545
12126
  thread_ts: resolvedThreadTs,
11546
12127
  count: messages.length,
@@ -11556,7 +12137,7 @@ function createSlackThreadReadTool(context, deps = {}) {
11556
12137
  // src/chat/tools/user-lookup.ts
11557
12138
  import { and, eq, or, sql } from "drizzle-orm";
11558
12139
  import { alias } from "drizzle-orm/pg-core";
11559
- import { z as z50 } from "zod";
12140
+ import { z as z54 } from "zod";
11560
12141
  var linkedSlackIdentity = alias(juniorIdentities, "user_lookup_slack_identity");
11561
12142
  function explicitUserLookupError(error) {
11562
12143
  if (error.apiError === "user_not_found" || error.code === "not_found") {
@@ -11705,9 +12286,9 @@ function createUserLookupTool(teamId, providers) {
11705
12286
  openWorldHint: true,
11706
12287
  readOnlyHint: true
11707
12288
  },
11708
- inputSchema: z50.object({
11709
- provider: z50.enum(providers).describe("Enabled identity provider to query."),
11710
- query: z50.string().trim().min(1).describe("Provider subject ID, handle, Slack email, or Slack name.")
12289
+ inputSchema: z54.object({
12290
+ provider: z54.enum(providers).describe("Enabled identity provider to query."),
12291
+ query: z54.string().trim().min(1).describe("Provider subject ID, handle, Slack email, or Slack name.")
11711
12292
  }).strict(),
11712
12293
  outputSchema: juniorToolOutputSchema,
11713
12294
  execute: async ({ provider, query }) => {
@@ -11727,18 +12308,18 @@ function createUserLookupTool(teamId, providers) {
11727
12308
  }
11728
12309
 
11729
12310
  // src/chat/tools/system-time.ts
11730
- import { z as z51 } from "zod";
11731
- var systemTimeInputSchema = z51.object({
11732
- timezone: z51.string().trim().min(1).optional().describe(
12311
+ import { z as z55 } from "zod";
12312
+ var systemTimeInputSchema = z55.object({
12313
+ timezone: z55.string().trim().min(1).optional().describe(
11733
12314
  "Optional IANA timezone, for example America/Los_Angeles. When set, iso_local and timezone_offset_minutes are for that zone."
11734
12315
  )
11735
12316
  });
11736
12317
  var systemTimeOutputSchema = juniorToolOutputSchema.extend({
11737
- unix_ms: z51.number(),
11738
- iso_utc: z51.string(),
11739
- iso_local: z51.string(),
11740
- timezone: z51.string().nullable(),
11741
- timezone_offset_minutes: z51.number()
12318
+ unix_ms: z55.number(),
12319
+ iso_utc: z55.string(),
12320
+ iso_local: z55.string(),
12321
+ timezone: z55.string().nullable(),
12322
+ timezone_offset_minutes: z55.number()
11742
12323
  });
11743
12324
  function createSystemTimeTool() {
11744
12325
  return zodTool({
@@ -11818,27 +12399,27 @@ function getTimeZoneOffsetMinutes(timestampMs, timezone) {
11818
12399
  }
11819
12400
 
11820
12401
  // src/chat/tools/search-conversation-events.ts
11821
- import { z as z52 } from "zod";
12402
+ import { z as z56 } from "zod";
11822
12403
  var DEFAULT_LIMIT3 = 25;
11823
12404
  var MAX_LIMIT = 100;
11824
12405
  var MAX_EVENT_DATA_BYTES = 4e3;
11825
12406
  var MAX_EVENTS_JSON_BYTES = 2e4;
11826
12407
  var MAX_IDENTITY_CHARS = 256;
11827
12408
  var textEncoder = new TextEncoder();
11828
- var knownEventTypeSchema = z52.enum(KNOWN_CONVERSATION_EVENT_TYPES);
11829
- var projectedEventSchema = z52.object({
11830
- seq: z52.number().int().nonnegative().describe("Stable event position for pagination."),
11831
- history_version: z52.number().int().nonnegative().describe("Agent history generation after replacement."),
11832
- created_at: z52.iso.datetime().describe("When the event was recorded."),
11833
- idempotency_key: z52.string().min(1).optional().describe("Retry-stable event identity, when assigned."),
11834
- data: z52.record(z52.string(), z52.unknown()).describe("Bounded event data.")
12409
+ var knownEventTypeSchema = z56.enum(KNOWN_CONVERSATION_EVENT_TYPES);
12410
+ var projectedEventSchema = z56.object({
12411
+ seq: z56.number().int().nonnegative().describe("Stable event position for pagination."),
12412
+ history_version: z56.number().int().nonnegative().describe("Agent history generation after replacement."),
12413
+ created_at: z56.iso.datetime().describe("When the event was recorded."),
12414
+ idempotency_key: z56.string().min(1).optional().describe("Retry-stable event identity, when assigned."),
12415
+ data: z56.record(z56.string(), z56.unknown()).describe("Bounded event data.")
11835
12416
  }).strict();
11836
12417
  var searchConversationEventsOutputSchema = juniorToolOutputSchema.extend({
11837
- conversation_id: z52.string().min(1),
11838
- events: z52.array(projectedEventSchema),
11839
- has_older: z52.boolean().describe("Whether matching older events exist."),
11840
- has_newer: z52.boolean().describe("Whether matching newer events exist."),
11841
- omitted_event_count: z52.number().int().nonnegative().optional().describe("Events omitted by the response byte limit.")
12418
+ conversation_id: z56.string().min(1),
12419
+ events: z56.array(projectedEventSchema),
12420
+ has_older: z56.boolean().describe("Whether matching older events exist."),
12421
+ has_newer: z56.boolean().describe("Whether matching newer events exist."),
12422
+ omitted_event_count: z56.number().int().nonnegative().optional().describe("Events omitted by the response byte limit.")
11842
12423
  });
11843
12424
  function createSearchConversationEventsTool(context) {
11844
12425
  return zodTool({
@@ -11851,16 +12432,16 @@ function createSearchConversationEventsTool(context) {
11851
12432
  openWorldHint: false,
11852
12433
  readOnlyHint: true
11853
12434
  },
11854
- inputSchema: z52.object({
11855
- conversation_id: z52.string().trim().min(1).nullable().describe(
12435
+ inputSchema: z56.object({
12436
+ conversation_id: z56.string().trim().min(1).nullable().describe(
11856
12437
  "Conversation ID to search. Omit or null for the current conversation."
11857
12438
  ).optional(),
11858
- after_seq: z52.number().int().nonnegative().nullable().describe("Exclusive event sequence lower bound; returns ascending.").optional(),
11859
- before_seq: z52.number().int().nonnegative().nullable().describe("Exclusive event sequence upper bound.").optional(),
11860
- limit: z52.number().int().min(1).max(MAX_LIMIT).nullable().describe(
12439
+ after_seq: z56.number().int().nonnegative().nullable().describe("Exclusive event sequence lower bound; returns ascending.").optional(),
12440
+ before_seq: z56.number().int().nonnegative().nullable().describe("Exclusive event sequence upper bound.").optional(),
12441
+ limit: z56.number().int().min(1).max(MAX_LIMIT).nullable().describe(
11861
12442
  `Maximum events. Default ${DEFAULT_LIMIT3}; max ${MAX_LIMIT}.`
11862
12443
  ).optional(),
11863
- types: z52.array(knownEventTypeSchema).min(1).nullable().describe("Event types to include; omit for all types.").optional()
12444
+ types: z56.array(knownEventTypeSchema).min(1).nullable().describe("Event types to include; omit for all types.").optional()
11864
12445
  }).strict(),
11865
12446
  outputSchema: searchConversationEventsOutputSchema,
11866
12447
  execute: async (input) => {
@@ -12106,10 +12687,10 @@ function stripReplacementHistoryField(value) {
12106
12687
  }
12107
12688
 
12108
12689
  // src/chat/tools/handoff/tool.ts
12109
- import { z as z53 } from "zod";
12690
+ import { z as z57 } from "zod";
12110
12691
  var HANDOFF_TOOL_NAME = "handoff";
12111
12692
  function createHandoffTool(handoff) {
12112
- const profileSchema = z53.enum(handoff.profiles);
12693
+ const profileSchema = z57.enum(handoff.profiles);
12113
12694
  const handoffResultSchema = juniorToolOutputSchema.extend({
12114
12695
  model_profile: modelProfileSchema
12115
12696
  });
@@ -12123,7 +12704,7 @@ function createHandoffTool(handoff) {
12123
12704
  },
12124
12705
  description: `Switch to another execution profile and continue the same task. Profiles: ${profileNames}. Call this as the only tool.`,
12125
12706
  executionMode: "sequential",
12126
- inputSchema: z53.object({
12707
+ inputSchema: z57.object({
12127
12708
  profile: profileSchema.describe("Target execution profile")
12128
12709
  }).strict(),
12129
12710
  outputSchema: handoffResultSchema,
@@ -12144,7 +12725,7 @@ function createHandoffTool(handoff) {
12144
12725
  }
12145
12726
 
12146
12727
  // src/chat/tools/web/fetch-tool.ts
12147
- import { z as z54 } from "zod";
12728
+ import { z as z58 } from "zod";
12148
12729
 
12149
12730
  // src/chat/tools/web/constants.ts
12150
12731
  var USER_AGENT = "junior-bot/0.1";
@@ -12587,9 +13168,9 @@ function createWebFetchTool(hooks, options = {}) {
12587
13168
  openWorldHint: true,
12588
13169
  readOnlyHint: true
12589
13170
  },
12590
- inputSchema: z54.object({
12591
- url: z54.string().min(1).describe("HTTP(S) URL to fetch."),
12592
- max_chars: z54.coerce.number().int().min(500).max(MAX_FETCH_CHARS).describe("Optional maximum number of extracted characters to return.").optional()
13171
+ inputSchema: z58.object({
13172
+ url: z58.string().min(1).describe("HTTP(S) URL to fetch."),
13173
+ max_chars: z58.coerce.number().int().min(500).max(MAX_FETCH_CHARS).describe("Optional maximum number of extracted characters to return.").optional()
12593
13174
  }),
12594
13175
  outputSchema: juniorToolOutputSchema,
12595
13176
  execute: async ({ url, max_chars }) => {
@@ -12634,7 +13215,7 @@ function createWebFetchTool(hooks, options = {}) {
12634
13215
  }
12635
13216
 
12636
13217
  // src/chat/tools/web/search.ts
12637
- import { z as z55 } from "zod";
13218
+ import { z as z59 } from "zod";
12638
13219
  import { generateText } from "ai";
12639
13220
  import { createGatewayProvider } from "@ai-sdk/gateway";
12640
13221
  var SEARCH_TIMEOUT_MS = 6e4;
@@ -12676,9 +13257,9 @@ function createWebSearchTool(modelId, override) {
12676
13257
  openWorldHint: true,
12677
13258
  readOnlyHint: true
12678
13259
  },
12679
- inputSchema: z55.object({
12680
- query: z55.string().min(1).max(500).describe("Search query."),
12681
- max_results: z55.coerce.number().int().min(1).max(MAX_RESULTS2).describe("Max results to return.").optional()
13260
+ inputSchema: z59.object({
13261
+ query: z59.string().min(1).max(500).describe("Search query."),
13262
+ max_results: z59.coerce.number().int().min(1).max(MAX_RESULTS2).describe("Max results to return.").optional()
12682
13263
  }),
12683
13264
  outputSchema: juniorToolOutputSchema,
12684
13265
  execute: async ({ query, max_results }) => {
@@ -12720,7 +13301,7 @@ function createWebSearchTool(modelId, override) {
12720
13301
  }
12721
13302
 
12722
13303
  // src/chat/tools/sandbox/write-file.ts
12723
- import { z as z56 } from "zod";
13304
+ import { z as z60 } from "zod";
12724
13305
  function createWriteFileTool() {
12725
13306
  return zodTool({
12726
13307
  annotations: {
@@ -12731,9 +13312,9 @@ function createWriteFileTool() {
12731
13312
  },
12732
13313
  description: "Write UTF-8 content to a file in the sandbox workspace. Use for intentional file creation or deliberate full-file replacement after validation; use editFile instead for targeted changes to existing files. Do not use for exploratory analysis-only turns.",
12733
13314
  executionMode: "sequential",
12734
- inputSchema: z56.object({
12735
- path: z56.string().min(1).describe("Path to write in the sandbox workspace."),
12736
- content: z56.string().describe("UTF-8 file content to write.")
13315
+ inputSchema: z60.object({
13316
+ path: z60.string().min(1).describe("Path to write in the sandbox workspace."),
13317
+ content: z60.string().describe("UTF-8 file content to write.")
12737
13318
  }),
12738
13319
  outputSchema: juniorToolOutputSchema,
12739
13320
  execute: async () => {
@@ -12825,6 +13406,7 @@ function createTools(availableSkills, hooks = {}, context, options = {}) {
12825
13406
  tools.slackCanvasEdit = createSlackCanvasEditTool(state);
12826
13407
  tools.slackCanvasWrite = createSlackCanvasWriteTool(state);
12827
13408
  tools.slackThreadRead = createSlackThreadReadTool(slackContext);
13409
+ tools.slackChannelJoin = createSlackChannelJoinTool(slackContext);
12828
13410
  if (context.conversationId && slackContext.source.visibility === "public") {
12829
13411
  tools.searchConversationMessages = createSlackConversationMessageSearchTool(
12830
13412
  {
@@ -12835,7 +13417,7 @@ function createTools(availableSkills, hooks = {}, context, options = {}) {
12835
13417
  context.conversationId
12836
13418
  );
12837
13419
  }
12838
- if (context.source.platform === "slack" && context.slackActionToken) {
13420
+ if (context.source.platform === "slack") {
12839
13421
  tools.slackPublicSearch = createSlackPublicSearchTool(
12840
13422
  context.slackActionToken
12841
13423
  );
@@ -12870,9 +13452,7 @@ function createTools(availableSkills, hooks = {}, context, options = {}) {
12870
13452
  (input) => readSandboxFileUpload(context.workspace, input)
12871
13453
  );
12872
13454
  }
12873
- if (outputCapabilities?.canPostToChannel) {
12874
- tools.slackChannelListMessages = createSlackChannelListMessagesTool(slackContext);
12875
- }
13455
+ tools.slackChannelListMessages = createSlackChannelListMessagesTool(slackContext);
12876
13456
  if (rawChannelCapabilities.canAddReactions) {
12877
13457
  tools.addReaction = createSlackMessageAddReactionTool(
12878
13458
  slackContext,
@@ -13431,7 +14011,7 @@ function handleToolExecutionError(error, toolName, toolCallId, shouldTrace, conv
13431
14011
  }
13432
14012
 
13433
14013
  // src/chat/tools/execute-tool.ts
13434
- import { z as z57 } from "zod";
14014
+ import { z as z61 } from "zod";
13435
14015
  var EXECUTE_TOOL_NAME = "executeTool";
13436
14016
  function createExecuteToolTool() {
13437
14017
  return zodTool({
@@ -13443,11 +14023,11 @@ function createExecuteToolTool() {
13443
14023
  },
13444
14024
  description: "Execute any catalog tool by exact tool_name from searchTools or a tool-result execution hint. Put tool-specific parameters inside arguments.",
13445
14025
  executionMode: "sequential",
13446
- inputSchema: z57.object({
13447
- tool_name: z57.string().min(1).describe(
14026
+ inputSchema: z61.object({
14027
+ tool_name: z61.string().min(1).describe(
13448
14028
  "Exact catalog tool_name returned by searchTools or a tool-result execution hint."
13449
14029
  ),
13450
- arguments: z57.record(z57.string(), z57.unknown()).describe(
14030
+ arguments: z61.record(z61.string(), z61.unknown()).describe(
13451
14031
  'Arguments matching the selected catalog tool schema, for example { "query": "..." }.'
13452
14032
  ).optional()
13453
14033
  }).strict(),
@@ -13487,7 +14067,7 @@ function planToolExposure(tools) {
13487
14067
  }
13488
14068
 
13489
14069
  // src/chat/tools/search-tools.ts
13490
- import { z as z58 } from "zod";
14070
+ import { z as z62 } from "zod";
13491
14071
  var SEARCH_TOOLS_NAME = "searchTools";
13492
14072
  var DEFAULT_MAX_RESULTS2 = 5;
13493
14073
  var MAX_RESULTS3 = 20;
@@ -13532,29 +14112,29 @@ var SEARCH_STOP_WORDS = /* @__PURE__ */ new Set([
13532
14112
  "you",
13533
14113
  "your"
13534
14114
  ]);
13535
- var searchToolsSourceSchema = z58.object({
13536
- id: z58.string(),
13537
- description: z58.string()
14115
+ var searchToolsSourceSchema = z62.object({
14116
+ id: z62.string(),
14117
+ description: z62.string()
13538
14118
  }).strict();
13539
- var searchToolsToolSchema = z58.object({
13540
- tool_name: z58.string(),
13541
- description: z58.string(),
13542
- exposure: z58.enum(["direct", "deferred", "modelOnly", "hidden"]),
13543
- source: z58.string().optional(),
13544
- input_schema: z58.unknown(),
13545
- call_notes: z58.array(z58.string()),
13546
- annotations: z58.record(z58.string(), z58.unknown())
14119
+ var searchToolsToolSchema = z62.object({
14120
+ tool_name: z62.string(),
14121
+ description: z62.string(),
14122
+ exposure: z62.enum(["direct", "deferred", "modelOnly", "hidden"]),
14123
+ source: z62.string().optional(),
14124
+ input_schema: z62.unknown(),
14125
+ call_notes: z62.array(z62.string()),
14126
+ annotations: z62.record(z62.string(), z62.unknown())
13547
14127
  }).strict();
13548
14128
  var searchToolsOutputSchema = juniorToolOutputSchema.extend({
13549
- query: z58.string().nullable(),
13550
- source: z58.string().nullable(),
13551
- sources: z58.array(searchToolsSourceSchema),
13552
- total_catalog_tools: z58.number().int().nonnegative(),
13553
- total_eligible_tools: z58.number().int().nonnegative(),
13554
- total_matches: z58.number().int().nonnegative(),
13555
- returned_tools: z58.number().int().nonnegative(),
13556
- execution_tool: z58.literal("executeTool"),
13557
- tools: z58.array(searchToolsToolSchema)
14129
+ query: z62.string().nullable(),
14130
+ source: z62.string().nullable(),
14131
+ sources: z62.array(searchToolsSourceSchema),
14132
+ total_catalog_tools: z62.number().int().nonnegative(),
14133
+ total_eligible_tools: z62.number().int().nonnegative(),
14134
+ total_matches: z62.number().int().nonnegative(),
14135
+ returned_tools: z62.number().int().nonnegative(),
14136
+ execution_tool: z62.literal("executeTool"),
14137
+ tools: z62.array(searchToolsToolSchema)
13558
14138
  }).strict();
13559
14139
  function terms(value) {
13560
14140
  const chunks = value.match(/[\p{L}\p{N}]+/gu) ?? [];
@@ -13765,14 +14345,14 @@ function createSearchToolsTool(catalogTools) {
13765
14345
  openWorldHint: false,
13766
14346
  readOnlyHint: true
13767
14347
  },
13768
- inputSchema: z58.object({
13769
- query: z58.string().nullable().describe(
14348
+ inputSchema: z62.object({
14349
+ query: z62.string().nullable().describe(
13770
14350
  "Optional search terms describing the tool, owner, action, or arguments needed. Empty string lists catalog tools."
13771
14351
  ).optional(),
13772
- source: z58.string().nullable().describe(
14352
+ source: z62.string().nullable().describe(
13773
14353
  "Optional source id to search within, such as a plugin source returned in sources."
13774
14354
  ).optional(),
13775
- max_results: z58.number().int().min(1).max(MAX_RESULTS3).nullable().describe("Maximum matching catalog tool descriptors to return.").optional()
14355
+ max_results: z62.number().int().min(1).max(MAX_RESULTS3).nullable().describe("Maximum matching catalog tool descriptors to return.").optional()
13776
14356
  }).strict(),
13777
14357
  outputSchema: searchToolsOutputSchema,
13778
14358
  privateTraceResult: (result) => ({
@@ -15014,7 +15594,7 @@ function createPluginEgress(deps) {
15014
15594
  }
15015
15595
 
15016
15596
  // src/chat/services/guardian-action-review.ts
15017
- import { z as z59 } from "zod";
15597
+ import { z as z63 } from "zod";
15018
15598
 
15019
15599
  // src/chat/services/guardian-action-policy.ts
15020
15600
  var GUARDIAN_ACTION_POLICY = `You are judging one planned Junior agent action.
@@ -15082,17 +15662,17 @@ Assess the exact action's intrinsic risk and whether the user's intent authorize
15082
15662
  - Keep the reason to one concise, specific sentence grounded in the main risk or authorization fact. For ask, name the exact action, target, and material side effects the user must confirm.`;
15083
15663
 
15084
15664
  // src/chat/services/guardian-action-review.ts
15085
- var guardianDecisionSchema = z59.object({
15086
- decision: z59.enum(["allow", "ask", "deny"]),
15087
- reason: z59.string().trim().min(1).max(500),
15088
- riskLevel: z59.enum(["low", "medium", "high", "critical"]),
15089
- userAuthorization: z59.enum(["high", "medium", "low", "unknown"])
15665
+ var guardianDecisionSchema = z63.object({
15666
+ decision: z63.enum(["allow", "ask", "deny"]),
15667
+ reason: z63.string().trim().min(1).max(500),
15668
+ riskLevel: z63.enum(["low", "medium", "high", "critical"]),
15669
+ userAuthorization: z63.enum(["high", "medium", "low", "unknown"])
15090
15670
  }).strict();
15091
15671
  var GUARDIAN_REVIEW_TIMEOUT_MS = 6e4;
15092
15672
  var GUARDIAN_REVIEW_MAX_TOKENS = 2e3;
15093
15673
  var MAX_PROPOSAL_CHARS = 192e3;
15094
15674
  function guardianPrompt(proposal) {
15095
- const proposalJson = JSON.stringify(z59.json().parse(proposal), null, 2);
15675
+ const proposalJson = JSON.stringify(z63.json().parse(proposal), null, 2);
15096
15676
  if (proposalJson.length > MAX_PROPOSAL_CHARS) {
15097
15677
  throw new Error(
15098
15678
  `Guardian action proposal exceeds ${MAX_PROPOSAL_CHARS} characters.`
@@ -15337,6 +15917,38 @@ async function tryRecordSkillLoadStat(skill) {
15337
15917
  });
15338
15918
  }
15339
15919
  }
15920
+ function resolveToolRuntimeRoute(args) {
15921
+ const destination = toolInvocationDestination(args.routing);
15922
+ switch (args.routing.source.platform) {
15923
+ case "slack": {
15924
+ if (destination.platform !== "slack") {
15925
+ throw new TypeError("Slack tool runtime requires a Slack destination");
15926
+ }
15927
+ return {
15928
+ destination,
15929
+ actor: args.actor?.platform === "slack" ? args.actor : void 0,
15930
+ source: args.routing.source,
15931
+ slackActionToken: args.routing.slackActionToken
15932
+ };
15933
+ }
15934
+ case "local": {
15935
+ if (destination.platform !== "local") {
15936
+ throw new TypeError("Local tool runtime requires a local destination");
15937
+ }
15938
+ return {
15939
+ destination,
15940
+ actor: args.actor?.platform === "local" ? args.actor : void 0,
15941
+ source: args.routing.source
15942
+ };
15943
+ }
15944
+ case "web":
15945
+ return {
15946
+ destination,
15947
+ actor: args.actor?.platform === "web" ? args.actor : void 0,
15948
+ source: args.routing.source
15949
+ };
15950
+ }
15951
+ }
15340
15952
  async function wireAgentTools(args) {
15341
15953
  const runSource = args.routing.source;
15342
15954
  const credentialUserId = args.routing.credentialContext ? credentialUserSubjectId(args.routing.credentialContext) : void 0;
@@ -15453,36 +16065,20 @@ async function wireAgentTools(args) {
15453
16065
  ...args.durability.spawnAgent ? { spawnAgent: args.durability.spawnAgent } : {},
15454
16066
  ...args.requestHandoff ? { handoff: args.requestHandoff } : {}
15455
16067
  };
15456
- const toolDestination = toolInvocationDestination(args.routing);
15457
- let toolRuntimeContext;
15458
- if (runSource.platform === "slack") {
15459
- if (toolDestination.platform !== "slack") {
15460
- throw new TypeError("Slack tool runtime requires a Slack destination");
15461
- }
15462
- toolRuntimeContext = {
15463
- ...commonToolRuntimeContext,
15464
- destination: toolDestination,
15465
- actor: args.currentActor?.platform === "slack" ? args.currentActor : void 0,
15466
- source: runSource,
15467
- slackActionToken: args.routing.slackActionToken
15468
- };
15469
- } else {
15470
- if (toolDestination.platform !== "local") {
15471
- throw new TypeError("Local tool runtime requires a local destination");
15472
- }
15473
- toolRuntimeContext = {
15474
- ...commonToolRuntimeContext,
15475
- destination: toolDestination,
15476
- actor: args.currentActor?.platform === "local" ? args.currentActor : void 0,
15477
- source: runSource
15478
- };
15479
- }
16068
+ const toolRoute = resolveToolRuntimeRoute({
16069
+ actor: args.currentActor,
16070
+ routing: args.routing
16071
+ });
16072
+ const toolRuntimeContext = {
16073
+ ...commonToolRuntimeContext,
16074
+ ...toolRoute
16075
+ };
15480
16076
  const actionReview = createToolActionReview({
15481
16077
  context: {
15482
16078
  actor: args.currentActor,
15483
16079
  conversationId: args.conversationId,
15484
16080
  credentialContext: args.routing.credentialContext,
15485
- destination: toolDestination,
16081
+ destination: toolRoute.destination,
15486
16082
  source: runSource,
15487
16083
  userIntent: args.currentUserIntent,
15488
16084
  evidence: () => buildToolActionEvidence(args.currentAgentMessages())
@@ -15688,6 +16284,7 @@ function createResumeState(args) {
15688
16284
  destination: args.destination,
15689
16285
  destinationVisibility: args.destinationVisibility,
15690
16286
  dispatchId: args.dispatchId,
16287
+ publishExternally: args.publishExternally,
15691
16288
  source: args.runSource,
15692
16289
  actor: args.actor,
15693
16290
  surface: args.surface
@@ -16496,6 +17093,12 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy, run
16496
17093
  let connectedMcpProviders = /* @__PURE__ */ new Set();
16497
17094
  let turnUsage;
16498
17095
  let priorPhaseUsage;
17096
+ let agent;
17097
+ let acceptedToolFreeAssistant = false;
17098
+ const generatedFiles = [];
17099
+ const toolCalls = [];
17100
+ let shouldTrace = false;
17101
+ let getSandboxRef;
16499
17102
  const configuredReasoningLevel = policy.reasoningLevel ?? botConfig.reasoningLevel;
16500
17103
  let turnRoute = configuredReasoningLevel ? configuredTurnRoute(
16501
17104
  STANDARD_MODEL_PROFILE,
@@ -16534,7 +17137,7 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy, run
16534
17137
  activeModelProfile = projection.modelProfile;
16535
17138
  activeModelId = modelIdForProfile(botConfig, activeModelProfile);
16536
17139
  let durableModelProfile = projection.modelProfile;
16537
- const shouldTrace = shouldEmitDevAgentTrace();
17140
+ shouldTrace = shouldEmitDevAgentTrace();
16538
17141
  const spanContext = { modelId: activeModelId };
16539
17142
  const availableSkills = await discoverRunSkills({
16540
17143
  skillDirs: policy.skillDirs
@@ -16591,6 +17194,7 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy, run
16591
17194
  ...routing.dispatch?.id ? { dispatchId: routing.dispatch.id } : {},
16592
17195
  durability,
16593
17196
  recordActiveMcpProviders,
17197
+ publishExternally: checkpoint.record?.publishExternally ?? routing.publishExternally ?? false,
16594
17198
  actor,
16595
17199
  runSource,
16596
17200
  conversationId,
@@ -16716,9 +17320,6 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy, run
16716
17320
  }
16717
17321
  activeModelProfile = routedModelProfile;
16718
17322
  activeModelId = routedModelId;
16719
- const generatedFiles = [];
16720
- const toolCalls = [];
16721
- let agent;
16722
17323
  let pendingPiHookError;
16723
17324
  let pendingHandoff;
16724
17325
  const currentAgentMessages = () => agent ? [...agent.state.messages] : [];
@@ -16857,6 +17458,7 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy, run
16857
17458
  });
16858
17459
  mcpToolManager = wiring.mcpToolManager;
16859
17460
  closeTools = wiring.close;
17461
+ getSandboxRef = () => wiring.getSandboxRef();
16860
17462
  const initialRepositoryInstructions = wiring.getSandboxRef() ? await wiring.captureRepositoryInstructions() : void 0;
16861
17463
  const recordAgentsTransition = async (instructions) => {
16862
17464
  try {
@@ -17038,6 +17640,7 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy, run
17038
17640
  }
17039
17641
  try {
17040
17642
  await delivery(message);
17643
+ acceptedToolFreeAssistant = true;
17041
17644
  } catch (error) {
17042
17645
  assistantMessageDeliveryError = new AssistantMessageDeliveryError(
17043
17646
  error
@@ -17521,6 +18124,27 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy, run
17521
18124
  if (resume && runError instanceof AuthorizationPauseError) {
17522
18125
  return await resume.parkForAuth(runError, turnUsage);
17523
18126
  }
18127
+ if (resume?.timedOut && acceptedToolFreeAssistant && agent && turnRoute) {
18128
+ await recordActiveMcpProviders();
18129
+ const piMessages = [...agent.state.messages];
18130
+ return {
18131
+ status: "completed",
18132
+ result: buildTurnResult({
18133
+ newMessages: piMessages.slice(resume.beforeMessageCount),
18134
+ userInput,
18135
+ toolCalls,
18136
+ sandboxRef: getSandboxRef?.() ?? lastKnownSandboxRef,
18137
+ piMessages,
18138
+ durationMs: Date.now() - replyStartedAtMs,
18139
+ generatedFileCount: generatedFiles.length,
18140
+ shouldTrace,
18141
+ usage: turnUsage,
18142
+ executionProfile: turnRoute,
18143
+ assistantUserName: botConfig.userName,
18144
+ modelId: activeModelId
18145
+ })
18146
+ };
18147
+ }
17524
18148
  if (resume) {
17525
18149
  const outcome = await resume.translateSuspension({
17526
18150
  currentUsage: turnUsage,