@sentry/junior 0.195.0 → 0.197.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 (76) hide show
  1. package/dist/{agent-hooks-VIBTIP5F.js → agent-hooks-CM4EKXL4.js} +13 -13
  2. package/dist/api/schema.js +3 -3
  3. package/dist/api.js +27 -27
  4. package/dist/app.d.ts +8 -2
  5. package/dist/app.js +102 -108
  6. package/dist/{catalog-runtime-6E6SGRZK.js → catalog-runtime-V7WGCXUD.js} +4 -4
  7. package/dist/chat/agent/handoff.d.ts +73 -0
  8. package/dist/chat/config.d.ts +2 -2
  9. package/dist/chat/model-profile.d.ts +12 -0
  10. package/dist/chat/no-reply.d.ts +4 -3
  11. package/dist/chat/services/automated-turn-limit.d.ts +13 -34
  12. package/dist/chat/services/subscribed-decision.d.ts +8 -0
  13. package/dist/chat/slack/automated-turn-limit-notice.d.ts +2 -24
  14. package/dist/chat/tools/types.d.ts +6 -2
  15. package/dist/{chunk-RKA22BQP.js → chunk-2K26W3FP.js} +1 -1
  16. package/dist/{chunk-FE3Y4KES.js → chunk-4USCVHQZ.js} +5 -5
  17. package/dist/{chunk-PFQRZ4IB.js → chunk-4UUKQB6I.js} +7 -7
  18. package/dist/{chunk-KQ3GZGBK.js → chunk-5GWV7SXF.js} +4 -11
  19. package/dist/{chunk-7PQMTME5.js → chunk-5MMQLB2B.js} +1 -1
  20. package/dist/{chunk-FPQPYJEG.js → chunk-6ZQ3CE6X.js} +1 -1
  21. package/dist/{chunk-HUQOXMDG.js → chunk-7ENMKNEK.js} +1 -1
  22. package/dist/{chunk-GFBR4S3Q.js → chunk-7NL7T6VU.js} +1 -1
  23. package/dist/{chunk-I4RH3KLJ.js → chunk-BGSAFVJO.js} +1 -1
  24. package/dist/{chunk-3VWIZVK7.js → chunk-BQ7UXCUO.js} +1 -1
  25. package/dist/{chunk-LMYRF6HX.js → chunk-BR5GNDPS.js} +85 -14
  26. package/dist/{chunk-L62JVQPL.js → chunk-C2SOA3KN.js} +3 -3
  27. package/dist/{chunk-JBSCMOLR.js → chunk-DBTI74IT.js} +6 -6
  28. package/dist/{chunk-X3PBQ4BK.js → chunk-DHWYAHAM.js} +1 -1
  29. package/dist/{chunk-RQO67SAO.js → chunk-DLXA2ZXJ.js} +2 -2
  30. package/dist/{chunk-KBPWC7NU.js → chunk-EZKISZ3Q.js} +1 -1
  31. package/dist/{chunk-6TRQLG3D.js → chunk-FCONFDZ4.js} +1 -1
  32. package/dist/{chunk-7UED4D5T.js → chunk-H6QL3FWC.js} +22 -22
  33. package/dist/{chunk-5HNXLP4W.js → chunk-HDSDOBWN.js} +1 -1
  34. package/dist/{chunk-QKHZF2LH.js → chunk-IGSAXSM7.js} +4 -4
  35. package/dist/{chunk-HE5PZAPS.js → chunk-ITP4OR7R.js} +4 -4
  36. package/dist/{chunk-QPZL2IYT.js → chunk-LBN67OZR.js} +1 -1
  37. package/dist/{chunk-R5OHONYU.js → chunk-LIK4T6CF.js} +6 -6
  38. package/dist/{chunk-RZ3PBNDV.js → chunk-LMTHFU5D.js} +7 -7
  39. package/dist/{chunk-5NR7N567.js → chunk-MJCJ62RZ.js} +4 -4
  40. package/dist/{chunk-PYTRZZ4V.js → chunk-NANQFFGO.js} +8 -8
  41. package/dist/{chunk-D4YV5TDV.js → chunk-NHJU2ZAL.js} +11 -0
  42. package/dist/{chunk-FEZIEEOP.js → chunk-O2H7JUEQ.js} +289 -334
  43. package/dist/{chunk-CE2P6TOV.js → chunk-O5RG7SKU.js} +3 -3
  44. package/dist/{chunk-7MYXH5LA.js → chunk-PF3PEX4Q.js} +1 -1
  45. package/dist/{chunk-C4NY6L7S.js → chunk-PM5WSDAD.js} +1 -1
  46. package/dist/{chunk-KUQRWV53.js → chunk-S3DRTWPX.js} +5 -5
  47. package/dist/{chunk-N67T4VY3.js → chunk-SBCWV4E4.js} +9 -9
  48. package/dist/{chunk-6GGDBXXP.js → chunk-T5MK3RUT.js} +3 -3
  49. package/dist/{chunk-OQHO62VA.js → chunk-V2IX5Q5L.js} +8 -8
  50. package/dist/{chunk-DTYOEHUP.js → chunk-VAUPAX3O.js} +4 -4
  51. package/dist/{chunk-BJZML4RT.js → chunk-WOJG25QZ.js} +9 -9
  52. package/dist/{chunk-QHJZSYKY.js → chunk-XALIC5SY.js} +2 -2
  53. package/dist/{chunk-VYDIA2UL.js → chunk-YC5CFKME.js} +2 -6
  54. package/dist/{chunk-LM7UE523.js → chunk-YLBJBBYX.js} +3 -3
  55. package/dist/{chunk-R5BGW4WR.js → chunk-YZQLXALE.js} +2 -2
  56. package/dist/cli/chat.js +35 -35
  57. package/dist/cli/check.js +5 -5
  58. package/dist/cli/plugins.js +14 -14
  59. package/dist/cli/snapshot-create.js +10 -10
  60. package/dist/cli/upgrade.js +7 -7
  61. package/dist/{db-FVCSDROR.js → db-6BD722UD.js} +8 -8
  62. package/dist/instrumentation.js +3 -3
  63. package/dist/nitro.js +7 -7
  64. package/dist/{oauth-callback-server-Z5PXTDOE.js → oauth-callback-server-P6AIT27Q.js} +31 -31
  65. package/dist/{runner-RHYU7OGV.js → runner-OKPDZOJC.js} +23 -23
  66. package/dist/{skills-QTN73DN5.js → skills-CQFX3V2I.js} +5 -5
  67. package/dist/spawn-GA5MADGU.js +42 -0
  68. package/dist/task-runner-7IIYO44C.js +36 -0
  69. package/dist/{validation-2ZK7GA6N.js → validation-STB4W433.js} +5 -5
  70. package/dist/version.js +1 -1
  71. package/dist/{work-7IBKSZRU.js → work-B7N5TEYI.js} +23 -23
  72. package/dist/worker-W6PBEHCA.js +24 -0
  73. package/package.json +5 -5
  74. package/dist/spawn-R56OWLUH.js +0 -42
  75. package/dist/task-runner-LLJZTLRP.js +0 -36
  76. package/dist/worker-RNVQVCO7.js +0 -24
package/dist/app.js CHANGED
@@ -1,15 +1,15 @@
1
1
  import {
2
2
  processConversationWork
3
- } from "./chunk-HE5PZAPS.js";
3
+ } from "./chunk-ITP4OR7R.js";
4
4
  import {
5
5
  bindSpawnAgent
6
- } from "./chunk-QHJZSYKY.js";
6
+ } from "./chunk-XALIC5SY.js";
7
7
  import {
8
8
  createAgentInvocationWorker,
9
9
  enqueueAgentInvocation,
10
10
  listPendingAgentInvocationMailboxAppends,
11
11
  resolveAgentInvocationId
12
- } from "./chunk-N67T4VY3.js";
12
+ } from "./chunk-SBCWV4E4.js";
13
13
  import {
14
14
  GET as GET2,
15
15
  GET2 as GET3,
@@ -37,12 +37,12 @@ import {
37
37
  startProcessingReaction,
38
38
  stripLeadingBotMention,
39
39
  wakePausedTurn
40
- } from "./chunk-7UED4D5T.js";
40
+ } from "./chunk-H6QL3FWC.js";
41
41
  import "./chunk-FA6POGLH.js";
42
42
  import {
43
43
  AgentRunError,
44
44
  executeTurn
45
- } from "./chunk-CE2P6TOV.js";
45
+ } from "./chunk-O5RG7SKU.js";
46
46
  import {
47
47
  buildDeliveredTurnStatePatch,
48
48
  completeAuthPauseTurn,
@@ -53,7 +53,7 @@ import {
53
53
  getTurnUserSlackMessageTs,
54
54
  persistAuthPauseTurnState,
55
55
  requireTurnFailureEventId
56
- } from "./chunk-KUQRWV53.js";
56
+ } from "./chunk-S3DRTWPX.js";
57
57
  import {
58
58
  claimDispatchMailboxAppend,
59
59
  confirmDispatchMailboxAppend,
@@ -73,11 +73,11 @@ import {
73
73
  processPluginTask,
74
74
  resolveTurnSessionRouting,
75
75
  scheduleSessionCompletedPluginTasks
76
- } from "./chunk-BJZML4RT.js";
76
+ } from "./chunk-WOJG25QZ.js";
77
77
  import {
78
78
  ConversationTurnLifecycleService,
79
79
  getTurnLifecycle
80
- } from "./chunk-7PQMTME5.js";
80
+ } from "./chunk-5MMQLB2B.js";
81
81
  import {
82
82
  coerceThreadConversationState,
83
83
  getLocationConfigurationService,
@@ -86,7 +86,7 @@ import {
86
86
  persistThreadRuntimeState,
87
87
  persistThreadState,
88
88
  persistThreadStateById
89
- } from "./chunk-FE3Y4KES.js";
89
+ } from "./chunk-4USCVHQZ.js";
90
90
  import {
91
91
  GET,
92
92
  appendAndEnqueueWebMessage,
@@ -101,24 +101,24 @@ import {
101
101
  resolveSlackChannelTypeFromMessage,
102
102
  resolveSlackConversationContext,
103
103
  webActorFromEmail
104
- } from "./chunk-PYTRZZ4V.js";
104
+ } from "./chunk-NANQFFGO.js";
105
105
  import {
106
106
  createWebAuthorization,
107
107
  deleteWebAuthorization
108
- } from "./chunk-5HNXLP4W.js";
108
+ } from "./chunk-HDSDOBWN.js";
109
109
  import {
110
110
  recordTaskExecution
111
- } from "./chunk-5NR7N567.js";
111
+ } from "./chunk-MJCJ62RZ.js";
112
112
  import "./chunk-RC745GGZ.js";
113
113
  import {
114
114
  createVercelPluginTaskCallback,
115
115
  queueCallback,
116
116
  registerVercelPluginTaskDevConsumer
117
- } from "./chunk-6GGDBXXP.js";
117
+ } from "./chunk-T5MK3RUT.js";
118
118
  import {
119
119
  validatePluginEgressCredentialHooks,
120
120
  validatePluginRegistrations
121
- } from "./chunk-3VWIZVK7.js";
121
+ } from "./chunk-BQ7UXCUO.js";
122
122
  import {
123
123
  defineJuniorPlugins,
124
124
  pluginCatalogConfigFromEnv,
@@ -129,7 +129,6 @@ import {
129
129
  RESOURCE_EVENT_MESSAGE_AUTHOR,
130
130
  RESOURCE_EVENT_SYSTEM_ACTOR,
131
131
  SANDBOX_EGRESS_PROXY_PATH,
132
- admitAutomatedTurn,
133
132
  buildSteeringPiMessage,
134
133
  clearSandboxEgressSignals,
135
134
  consumeSandboxEgressAuthRequiredSignal,
@@ -148,7 +147,6 @@ import {
148
147
  maybePostAutomatedTurnLimitNotice,
149
148
  normalizeSandboxEgressTracePropagationDomains,
150
149
  parseSandboxEgressCredentialToken,
151
- postAutomatedTurnLimitNoticeForDestination,
152
150
  processWorkspaceSnapshotJob,
153
151
  publicArtifactHeaders,
154
152
  readPublicArtifact,
@@ -162,11 +160,11 @@ import {
162
160
  shouldEmitDevAgentTrace,
163
161
  shouldPropagateSandboxEgressTrace,
164
162
  unpublishedArtifactHeaders
165
- } from "./chunk-FEZIEEOP.js";
163
+ } from "./chunk-O2H7JUEQ.js";
166
164
  import {
167
165
  AuthorizationFlowDisabledError,
168
166
  PluginCredentialFailureError
169
- } from "./chunk-JBSCMOLR.js";
167
+ } from "./chunk-DBTI74IT.js";
170
168
  import "./chunk-6Y5B5PEX.js";
171
169
  import "./chunk-DIB4GX3G.js";
172
170
  import {
@@ -174,14 +172,14 @@ import {
174
172
  deleteMcpAuthSessionsForUserProvider,
175
173
  deleteMcpServerSessionId,
176
174
  deleteMcpStoredOAuthCredentials
177
- } from "./chunk-OQHO62VA.js";
175
+ } from "./chunk-V2IX5Q5L.js";
178
176
  import {
179
177
  RetryableDeliveryError,
180
178
  createAgentRunner,
181
179
  formatProviderLabel,
182
180
  postSlackMessage,
183
181
  startOAuthFlow
184
- } from "./chunk-R5OHONYU.js";
182
+ } from "./chunk-LIK4T6CF.js";
185
183
  import {
186
184
  ConversationTurnBoundaryError,
187
185
  CooperativeTurnYieldError,
@@ -197,7 +195,7 @@ import {
197
195
  markTurnCompleted,
198
196
  markTurnFailed,
199
197
  startActiveTurn
200
- } from "./chunk-VYDIA2UL.js";
198
+ } from "./chunk-YC5CFKME.js";
201
199
  import {
202
200
  abandonTurnRecord,
203
201
  appendThreadContextMessages,
@@ -234,7 +232,7 @@ import {
234
232
  trimTrailingAssistantMessages,
235
233
  turnHasReply,
236
234
  upsertConversationMessage
237
- } from "./chunk-RZ3PBNDV.js";
235
+ } from "./chunk-LMTHFU5D.js";
238
236
  import {
239
237
  getExperimentalFeatures,
240
238
  isExperimentalFeatureEnabled,
@@ -254,10 +252,10 @@ import {
254
252
  scheduledRunId,
255
253
  scheduledRunSchema,
256
254
  withScheduledTaskLock
257
- } from "./chunk-DTYOEHUP.js";
255
+ } from "./chunk-VAUPAX3O.js";
258
256
  import {
259
257
  parseSlackThreadId
260
- } from "./chunk-6TRQLG3D.js";
258
+ } from "./chunk-FCONFDZ4.js";
261
259
  import {
262
260
  CONVERSATION_WORK_STALE_ENQUEUE_MS,
263
261
  appendAndEnqueueInboundMessage,
@@ -272,16 +270,16 @@ import {
272
270
  removeActiveConversation,
273
271
  requestConversationWork,
274
272
  stopConversationWork
275
- } from "./chunk-QKHZF2LH.js";
273
+ } from "./chunk-IGSAXSM7.js";
276
274
  import {
277
275
  buildDeterministicAssistantMessageId,
278
276
  buildDeterministicTurnId
279
277
  } from "./chunk-DA2KGZZI.js";
280
- import "./chunk-R5BGW4WR.js";
278
+ import "./chunk-YZQLXALE.js";
281
279
  import {
282
280
  getSandboxResourceConfig,
283
281
  setSandboxResourceConfig
284
- } from "./chunk-L62JVQPL.js";
282
+ } from "./chunk-C2SOA3KN.js";
285
283
  import "./chunk-PDO5BLNM.js";
286
284
  import "./chunk-UIE3R5XU.js";
287
285
  import {
@@ -290,7 +288,7 @@ import {
290
288
  } from "./chunk-X6GDDWQH.js";
291
289
  import {
292
290
  getTurnRequestDeadline
293
- } from "./chunk-I4RH3KLJ.js";
291
+ } from "./chunk-BGSAFVJO.js";
294
292
  import {
295
293
  getVercelConversationWorkQueue,
296
294
  resolveConversationWorkQueueTopic,
@@ -326,7 +324,7 @@ import {
326
324
  verifyEventTaskCredentialSubject,
327
325
  verifyScheduledTaskCredentialSubject,
328
326
  verifySlackDirectCredentialSubject
329
- } from "./chunk-PFQRZ4IB.js";
327
+ } from "./chunk-4UUKQB6I.js";
330
328
  import "./chunk-3Y47VMHV.js";
331
329
  import {
332
330
  createSlackDestination,
@@ -338,7 +336,7 @@ import {
338
336
  requireSlackDestination,
339
337
  withConversationEventLock,
340
338
  withConversationMutationLock
341
- } from "./chunk-KQ3GZGBK.js";
339
+ } from "./chunk-5GWV7SXF.js";
342
340
  import {
343
341
  juniorAgentBindings,
344
342
  juniorAgentInvocations,
@@ -346,16 +344,16 @@ import {
346
344
  juniorConversations,
347
345
  juniorSchedulerRuns,
348
346
  juniorSchedulerTasks
349
- } from "./chunk-FPQPYJEG.js";
350
- import "./chunk-LM7UE523.js";
347
+ } from "./chunk-6ZQ3CE6X.js";
348
+ import "./chunk-YLBJBBYX.js";
351
349
  import {
352
350
  acquireActiveLock,
353
351
  getStateAdapter,
354
352
  withActiveLock
355
- } from "./chunk-7MYXH5LA.js";
353
+ } from "./chunk-PF3PEX4Q.js";
356
354
  import {
357
355
  instructionProvenanceFor
358
- } from "./chunk-GFBR4S3Q.js";
356
+ } from "./chunk-7NL7T6VU.js";
359
357
  import {
360
358
  juniorDestinations
361
359
  } from "./chunk-VFUK3X5B.js";
@@ -374,17 +372,17 @@ import {
374
372
  isProviderRetryError,
375
373
  setProfiles,
376
374
  setSlackReactionConfig
377
- } from "./chunk-LMYRF6HX.js";
375
+ } from "./chunk-BR5GNDPS.js";
378
376
  import {
379
377
  defaultModelId,
380
378
  modelIdForProfile
381
- } from "./chunk-D4YV5TDV.js";
382
- import "./chunk-KBPWC7NU.js";
383
- import "./chunk-RQO67SAO.js";
379
+ } from "./chunk-NHJU2ZAL.js";
380
+ import "./chunk-EZKISZ3Q.js";
381
+ import "./chunk-DLXA2ZXJ.js";
384
382
  import {
385
383
  pluginCatalogRuntime
386
- } from "./chunk-RKA22BQP.js";
387
- import "./chunk-X3PBQ4BK.js";
384
+ } from "./chunk-2K26W3FP.js";
385
+ import "./chunk-DHWYAHAM.js";
388
386
  import {
389
387
  credentialContextForActor
390
388
  } from "./chunk-CEMMLV5L.js";
@@ -420,13 +418,13 @@ import {
420
418
  withContext,
421
419
  withLogContext,
422
420
  withSpan
423
- } from "./chunk-HUQOXMDG.js";
421
+ } from "./chunk-7ENMKNEK.js";
424
422
  import {
425
423
  continueTrace
426
424
  } from "./chunk-PEQXYSSE.js";
427
425
  import {
428
426
  JUNIOR_VERSION
429
- } from "./chunk-C4NY6L7S.js";
427
+ } from "./chunk-PM5WSDAD.js";
430
428
 
431
429
  // src/app.ts
432
430
  import { createRequire } from "module";
@@ -4448,7 +4446,7 @@ Assess conversation floor ownership and whether the latest message continues wor
4448
4446
  - should_reply=false when the latest message is plainly human-to-human, even if it discusses the same project.
4449
4447
 
4450
4448
  # Unsubscribe Policy
4451
- - should_unsubscribe=true only when the latest message clearly tells ${assistantName} to stop watching, replying, participating, or to leave the thread.
4449
+ - should_unsubscribe=true when the latest message clearly tells ${assistantName} to stop or leave the thread.
4452
4450
  - If should_unsubscribe=true, should_reply must be false.
4453
4451
  - Ordinary disagreement, correction, or "not yet" is not an unsubscribe.
4454
4452
 
@@ -4476,14 +4474,9 @@ var RECENT_ASSISTANT_ENTRY_LIMIT = 40;
4476
4474
  var LEADING_SLACK_MENTION_RE = /^\s*<@([A-Z0-9]+)(?:\|([^>]+))?>[\s,:-]*/i;
4477
4475
  var LEADING_NAMED_MENTION_RE = /^\s*@([a-z0-9._-]+)\b[\s,:-]*/i;
4478
4476
  var TRANSCRIPT_MESSAGE_LINE_RE = /^\[(assistant|user)\]\s+([^:]+):\s+([\s\S]+)$/i;
4479
- var FORCED_THREAD_OPTOUT_RE = /^!stop(?:\s|$)/i;
4480
- var THREAD_OPTOUT_PATTERNS = [
4481
- /\bstop (?:watching|replying|participating)\b/i,
4482
- /\bstay out\b/i,
4483
- /\bdon['’]t (?:reply|participate|watch)\b/i,
4484
- /\bunsubscribe\b/i,
4485
- /\bleave (?:this )?thread\b/i
4486
- ];
4477
+ var BANG_STOP_RE = /(?:^|\s)!stop(?=\s|$|[.!?,;:])/i;
4478
+ var LEADING_ADDRESS_RE = /^(?:(?:<@[^>]+>|@[\w.-]+)\s*[,:\-–—]?\s*)+/i;
4479
+ var BARE_STOP_RE = /^stop(?:\s*[.!…]+)?$/i;
4487
4480
  var ACKNOWLEDGMENT_ONLY_RE = /^(?:thanks(?: you)?|thank you|thx|ty|got it|sounds good|sgtm|lgtm|ok(?:ay)?|cool|nice|perfect|awesome|great|makes sense|understood|roger|yep|yup|kk|on it|will do)(?:[.!]+)?$/i;
4488
4481
  var DIRECTED_FOLLOW_UP_CUE_RE = /\b(?:you said|you just said|your last response|your last answer|what did you just say|what do you mean|what did you mean|explain(?: that| this| it| more)?|clarify(?: that| this| it)?|expand(?: on)?(?: that| this| it)?|elaborate(?: on)?(?: that| this| it)?|say more)\b/i;
4489
4482
  var TERSE_CLARIFICATION_RE = /^(?:which one|which ones|why|how so|what do you mean|what did you mean|say more|explain that|clarify that|expand on that|elaborate on that)\??$/i;
@@ -4518,13 +4511,35 @@ function detectLeadingOtherPartyAddress(rawText, text, botUserName) {
4518
4511
  }
4519
4512
  return `named_mention:${directedName}`;
4520
4513
  }
4521
- function isForcedThreadOptOutCommand(rawText, text) {
4522
- return FORCED_THREAD_OPTOUT_RE.test(rawText.trim()) || FORCED_THREAD_OPTOUT_RE.test(text.trim());
4514
+ function hasBangStop(rawText, text) {
4515
+ return BANG_STOP_RE.test(rawText.trim()) || BANG_STOP_RE.test(text.trim());
4523
4516
  }
4524
- function isThreadOptOutInstruction(rawText, text) {
4525
- return THREAD_OPTOUT_PATTERNS.some(
4526
- (pattern) => pattern.test(rawText) || pattern.test(text)
4527
- );
4517
+ function isBareStop(rawText, text) {
4518
+ return [rawText, text].some((value) => {
4519
+ const candidate = value.trim().replace(LEADING_ADDRESS_RE, "").trim();
4520
+ return candidate.length > 0 && BARE_STOP_RE.test(candidate);
4521
+ });
4522
+ }
4523
+ function getThreadStopDecision(args) {
4524
+ const text = args.text.trim();
4525
+ const rawText = args.rawText.trim();
4526
+ if (hasBangStop(rawText, text)) {
4527
+ return {
4528
+ shouldReply: false,
4529
+ shouldUnsubscribe: true,
4530
+ reason: "thread_opt_out" /* ThreadOptOut */,
4531
+ reasonDetail: "!stop"
4532
+ };
4533
+ }
4534
+ if (isBareStop(rawText, text)) {
4535
+ return {
4536
+ shouldReply: false,
4537
+ shouldUnsubscribe: true,
4538
+ reason: "thread_opt_out" /* ThreadOptOut */,
4539
+ reasonDetail: "stop"
4540
+ };
4541
+ }
4542
+ return void 0;
4528
4543
  }
4529
4544
  function isAcknowledgmentOnly(text) {
4530
4545
  return ACKNOWLEDGMENT_ONLY_RE.test(text.trim());
@@ -4700,13 +4715,9 @@ function getSubscribedReplyPreflightDecision(args) {
4700
4715
  async function decideSubscribedThreadReply(args) {
4701
4716
  const text = args.input.text.trim();
4702
4717
  const rawText = args.input.rawText.trim();
4703
- if (isForcedThreadOptOutCommand(rawText, text)) {
4704
- return {
4705
- shouldReply: false,
4706
- shouldUnsubscribe: true,
4707
- reason: "thread_opt_out" /* ThreadOptOut */,
4708
- reasonDetail: "forced !stop command"
4709
- };
4718
+ const stopDecision = getThreadStopDecision({ rawText, text });
4719
+ if (stopDecision) {
4720
+ return stopDecision;
4710
4721
  }
4711
4722
  const preflightDecision = getSubscribedReplyPreflightDecision({
4712
4723
  botUserName: args.botUserName,
@@ -4721,14 +4732,6 @@ async function decideSubscribedThreadReply(args) {
4721
4732
  return { shouldReply: false, reason: "empty_message" /* EmptyMessage */ };
4722
4733
  }
4723
4734
  if (args.input.isExplicitMention) {
4724
- if (isThreadOptOutInstruction(rawText, text)) {
4725
- return {
4726
- shouldReply: false,
4727
- shouldUnsubscribe: true,
4728
- reason: "thread_opt_out" /* ThreadOptOut */,
4729
- reasonDetail: "explicit stop instruction"
4730
- };
4731
- }
4732
4735
  return {
4733
4736
  shouldReply: true,
4734
4737
  reason: "explicit_mention" /* ExplicitMention */
@@ -5406,6 +5409,33 @@ function createSlackTurnRuntime(deps) {
5406
5409
  });
5407
5410
  const combinedText = combineTurnText(queuedMessages, currentText);
5408
5411
  const turnIsExplicitMention = Boolean(message.isMention) || queuedMessages.some((queued) => queued.explicitMention);
5412
+ const stopDecision = [...queuedMessages, currentText].map(
5413
+ (entry) => getThreadStopDecision({
5414
+ rawText: entry.rawText,
5415
+ text: entry.userText
5416
+ })
5417
+ ).find((decision2) => decision2?.shouldUnsubscribe);
5418
+ if (stopDecision) {
5419
+ const decision2 = {
5420
+ shouldReply: false,
5421
+ shouldUnsubscribe: true,
5422
+ reason: stopDecision.reasonDetail ? `${stopDecision.reason}:${stopDecision.reasonDetail}` : stopDecision.reason
5423
+ };
5424
+ await maybeHandleThreadOptOutDecision({
5425
+ thread,
5426
+ decision: decision2,
5427
+ beforeFirstResponsePost: hooks.beforeFirstResponsePost
5428
+ });
5429
+ await skipSubscribedMessage({
5430
+ thread,
5431
+ message,
5432
+ decision: decision2,
5433
+ context: threadContext,
5434
+ ack: hooks.ack,
5435
+ text: combinedText
5436
+ });
5437
+ return;
5438
+ }
5409
5439
  const preflightDecision = getSubscribedReplyPreflightDecision({
5410
5440
  botUserName: deps.assistantUserName,
5411
5441
  rawText: combinedText.rawText,
@@ -7258,14 +7288,12 @@ function createSlackTurn(deps) {
7258
7288
  const automatedTurnLimit = await recordFinishedTurnForAutomatedLimit(
7259
7289
  {
7260
7290
  conversationId,
7261
- destination,
7262
7291
  maxTurns: botConfig.maxConsecutiveAutomatedTurns,
7263
7292
  source
7264
7293
  }
7265
7294
  );
7266
7295
  await maybePostAutomatedTurnLimitNotice({
7267
7296
  conversationId,
7268
- destination,
7269
7297
  maxTurns: botConfig.maxConsecutiveAutomatedTurns,
7270
7298
  threadTs,
7271
7299
  update: automatedTurnLimit
@@ -9162,13 +9190,11 @@ function createConversationTurnWorker(agentRunner, deliverMessage) {
9162
9190
  }
9163
9191
  const automatedTurnLimit = await recordFinishedTurnForAutomatedLimit({
9164
9192
  conversationId: context.conversationId,
9165
- destination,
9166
9193
  maxTurns: botConfig.maxConsecutiveAutomatedTurns,
9167
9194
  source
9168
9195
  });
9169
9196
  await maybePostAutomatedTurnLimitNotice({
9170
9197
  conversationId: context.conversationId,
9171
- destination,
9172
9198
  maxTurns: botConfig.maxConsecutiveAutomatedTurns,
9173
9199
  threadTs: conversationLocation?.provider === "slack" ? conversationLocation.threadTs : void 0,
9174
9200
  update: automatedTurnLimit
@@ -9555,40 +9581,8 @@ async function ingestEventTasks(input, options) {
9555
9581
  const tasks = await findMatchingEventTasks(db, event, options.teamId);
9556
9582
  let dispatched = 0;
9557
9583
  const errors = [];
9558
- const maxTurns = botConfig.maxConsecutiveAutomatedTurns;
9559
- const noticedDestinations = /* @__PURE__ */ new Set();
9560
9584
  for (const task of tasks) {
9561
9585
  try {
9562
- const decision = await admitAutomatedTurn({
9563
- maxTurns,
9564
- nowMs,
9565
- scope: { kind: "destination", destination: task.destination }
9566
- });
9567
- if (decision.status === "paused") {
9568
- const destinationId = `${task.destination.teamId}:${task.destination.channelId}`;
9569
- logInfo("event_tasks.automated_turn_limit.paused", {
9570
- "app.automated_turn_limit.consecutive": decision.consecutiveAutomatedTurns,
9571
- "app.automated_turn_limit.max": maxTurns,
9572
- "app.event_task.id": task.id,
9573
- "app.resource_event.event_type": event.eventType,
9574
- "app.resource_event.namespace": event.namespace,
9575
- "app.slack.channel_id": task.destination.channelId,
9576
- "app.slack.team_id": task.destination.teamId
9577
- });
9578
- if (decision.shouldPostNotice && !noticedDestinations.has(destinationId)) {
9579
- const posted = await postAutomatedTurnLimitNoticeForDestination({
9580
- destination: task.destination,
9581
- maxTurns,
9582
- nowMs,
9583
- resumeIn: "channel",
9584
- scope: { kind: "destination", destination: task.destination }
9585
- });
9586
- if (posted) {
9587
- noticedDestinations.add(destinationId);
9588
- }
9589
- }
9590
- continue;
9591
- }
9592
9586
  const idempotencyKey = eventTaskDispatchKey(
9593
9587
  task.id,
9594
9588
  event.namespace,
@@ -1,14 +1,14 @@
1
1
  import {
2
2
  pluginCatalogRuntime
3
- } from "./chunk-RKA22BQP.js";
4
- import "./chunk-X3PBQ4BK.js";
3
+ } from "./chunk-2K26W3FP.js";
4
+ import "./chunk-DHWYAHAM.js";
5
5
  import "./chunk-CEMMLV5L.js";
6
6
  import "./chunk-N7TMSOZH.js";
7
7
  import "./chunk-DWPN6SCH.js";
8
8
  import "./chunk-TXKUKMVO.js";
9
- import "./chunk-HUQOXMDG.js";
9
+ import "./chunk-7ENMKNEK.js";
10
10
  import "./chunk-PEQXYSSE.js";
11
- import "./chunk-C4NY6L7S.js";
11
+ import "./chunk-PM5WSDAD.js";
12
12
  export {
13
13
  pluginCatalogRuntime
14
14
  };
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Model-profile handoff helpers for one agent run slice.
3
+ *
4
+ * Owns the durable handoff commit, Pi apply shape, tool control, and the few
5
+ * loop checks that only care about handoff tool calls.
6
+ */
7
+ import { type Agent, type AgentLoopTurnUpdate, type AgentTool } from "@earendil-works/pi-agent-core";
8
+ import type { ResumeState } from "@/chat/agent/resume";
9
+ import { type ModelProfile } from "@/chat/model-profile";
10
+ import { resolveGatewayModel } from "@/chat/pi/client";
11
+ import type { PiMessage } from "@/chat/pi/messages";
12
+ import { type CompactContextArgs } from "@/chat/services/context-compaction";
13
+ import { type TurnRoute } from "@/chat/services/turn-router";
14
+ import type { ToolRuntimeContext } from "@/chat/tools/types";
15
+ import { type AgentTurnUsage } from "@/chat/usage";
16
+ /** Staged handoff waiting for the next Pi turn boundary. */
17
+ export type PendingHandoff = {
18
+ messages: PiMessage[];
19
+ model: ReturnType<typeof resolveGatewayModel>;
20
+ modelId: string;
21
+ modelProfile: ModelProfile;
22
+ phaseUsage?: AgentTurnUsage;
23
+ thinkingLevel: NonNullable<AgentLoopTurnUpdate["thinkingLevel"]>;
24
+ turnRoute: TurnRoute;
25
+ };
26
+ /** Return the exclusive tool name when one is present among the calls. */
27
+ export declare function exclusiveToolName(toolCalls: ReadonlyArray<{
28
+ name: string;
29
+ }>): string | undefined;
30
+ /** Build the tool control for every configured profile except the active one. */
31
+ export declare function handoffControl(args: {
32
+ activeProfile: ModelProfile;
33
+ enabled: boolean;
34
+ execute: NonNullable<ToolRuntimeContext["handoff"]>["execute"];
35
+ }): ToolRuntimeContext["handoff"];
36
+ /**
37
+ * Rebind the handoff tool description and schema for the active profile.
38
+ * Other tools stay as wired.
39
+ */
40
+ export declare function toolsForHandoffProfile(args: {
41
+ activeProfile: ModelProfile;
42
+ agentTools: AgentTool[];
43
+ enabled: boolean;
44
+ execute: NonNullable<ToolRuntimeContext["handoff"]>["execute"];
45
+ }): AgentTool[];
46
+ /**
47
+ * Commit the durable handoff epoch and return the staged Pi swap.
48
+ * No-op when the requested profile is already active.
49
+ */
50
+ export declare function commitHandoff(args: {
51
+ activeModelProfile: ModelProfile;
52
+ beforeMessageCount: number;
53
+ conversationContext?: string;
54
+ conversationId: string;
55
+ metadata?: CompactContextArgs["metadata"];
56
+ onStatus?: (status: {
57
+ text: string;
58
+ }) => void | Promise<void>;
59
+ profile: ModelProfile;
60
+ runtimeContextSourceMessages?: PiMessage[];
61
+ signal?: AbortSignal;
62
+ sourceMessages: PiMessage[];
63
+ triggeringToolCallId?: string;
64
+ turnRoute: TurnRoute;
65
+ }): Promise<PendingHandoff | undefined>;
66
+ /** Apply a committed handoff to Pi and reset its durable resume baseline. */
67
+ export declare function applyHandoff(args: {
68
+ agent: Agent;
69
+ baseInstructions: string;
70
+ pending: PendingHandoff;
71
+ resume: ResumeState;
72
+ tools: AgentTool[];
73
+ }): AgentLoopTurnUpdate;
@@ -1,5 +1,5 @@
1
1
  import { type TurnReasoningLevel } from "@/chat/reasoning-level";
2
- import { type ModelProfileConfig, type ModelProfile } from "@/chat/model-profile";
2
+ import { type ModelProfileConfig, type ModelProfile, type ModelProfileInput } from "@/chat/model-profile";
3
3
  export declare const DEFAULT_SQL_STATEMENT_TIMEOUT_MS = 30000;
4
4
  /**
5
5
  * Buffer between the Vercel function timeout and the agent turn timeout so
@@ -77,7 +77,7 @@ export interface SlackReactionConfig {
77
77
  processingReactionEmoji: string;
78
78
  }
79
79
  /** Apply profiles from createApp(). */
80
- export declare function setProfiles(profiles: Readonly<Record<string, string>> | undefined, defaultProfile: string | undefined): void;
80
+ export declare function setProfiles(profiles: Readonly<Record<string, ModelProfileInput>> | undefined, defaultProfile: string | undefined): void;
81
81
  /** Return the current Slack reaction emoji config. */
82
82
  export declare function getSlackReactionConfig(): SlackReactionConfig;
83
83
  /** Apply Slack reaction emoji overrides from createApp() options, validating names. */
@@ -7,9 +7,21 @@ export declare const modelProfileSchema: z.ZodString;
7
7
  export type ModelProfile = z.output<typeof modelProfileSchema>;
8
8
  /** Runtime configuration for one named model profile. */
9
9
  export interface ModelProfileConfig {
10
+ /**
11
+ * Tells models which tasks fit this profile.
12
+ * Name concrete tasks. Add use and avoid cases when helpful.
13
+ * Do not use model product names as the selection rule.
14
+ */
15
+ description?: string;
10
16
  modelId: string;
11
17
  reasoningLevel?: TurnReasoningLevel;
12
18
  }
19
+ /** App-level profile input: a model id string or a full profile config. */
20
+ export type ModelProfileInput = string | ModelProfileConfig;
21
+ /** Format one profile and its task-fit description. */
22
+ export declare function formatModelProfile(profile: ModelProfile, description?: string): string;
23
+ /** Format configured profiles as one list item per line. */
24
+ export declare function formatModelProfiles(profiles: Readonly<Record<string, ModelProfileConfig>>, profileNames: readonly ModelProfile[]): string;
13
25
  /** Identify durable profile bindings that the current host cannot resolve. */
14
26
  export declare class ModelProfileNotConfiguredError extends Error {
15
27
  constructor(profile: ModelProfile);
@@ -1,5 +1,6 @@
1
1
  export declare const NO_REPLY_MARKER = "[[NO_REPLY]]";
2
- /** Detect the reserved marker for intentionally completing without thread text. */
2
+ /**
3
+ * True when one assistant message ends with the silence marker.
4
+ * Mid-sentence mentions do not match. Detection is per message only.
5
+ */
3
6
  export declare function isNoReplyMarker(text: string): boolean;
4
- /** Detect marker leaks before publication strips or rejects them. */
5
- export declare function containsNoReplyMarker(text: string): boolean;