@sentry/junior 0.194.0 → 0.196.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 (97) hide show
  1. package/dist/{agent-hooks-IDX764FD.js → agent-hooks-IQLRR4RC.js} +13 -13
  2. package/dist/api/activity.d.ts +9 -1
  3. package/dist/api/code/overview.d.ts +12 -0
  4. package/dist/api/locations/detail.d.ts +8 -0
  5. package/dist/api/locations/list.d.ts +5 -0
  6. package/dist/api/people/code.d.ts +6 -0
  7. package/dist/api/people/shared.d.ts +9 -1
  8. package/dist/api/reporting-window.d.ts +39 -1
  9. package/dist/api/schema/code.d.ts +12 -0
  10. package/dist/api/schema/conversation.d.ts +25 -0
  11. package/dist/api/schema/location.d.ts +13 -0
  12. package/dist/api/schema/person.d.ts +14 -0
  13. package/dist/api/schema/task.d.ts +12 -0
  14. package/dist/api/schema.js +3 -3
  15. package/dist/api.js +244 -65
  16. package/dist/app.d.ts +8 -2
  17. package/dist/app.js +166 -70
  18. package/dist/{catalog-runtime-MICXEBLK.js → catalog-runtime-EM7V2F4I.js} +4 -4
  19. package/dist/chat/agent/handoff.d.ts +73 -0
  20. package/dist/chat/config.d.ts +3 -2
  21. package/dist/chat/event-tasks/store.d.ts +27 -17
  22. package/dist/chat/model-profile.d.ts +12 -0
  23. package/dist/chat/no-reply.d.ts +4 -3
  24. package/dist/chat/resource-events/notification.d.ts +2 -1
  25. package/dist/chat/services/automated-turn-limit.d.ts +110 -0
  26. package/dist/chat/services/subscribed-decision.d.ts +8 -0
  27. package/dist/chat/slack/automated-turn-limit-notice.d.ts +38 -0
  28. package/dist/chat/tasks/execution-stats.d.ts +2 -1
  29. package/dist/chat/tasks/read.d.ts +5 -2
  30. package/dist/chat/tools/types.d.ts +6 -2
  31. package/dist/{chunk-U76RWMFR.js → chunk-5XSEOJPC.js} +87 -14
  32. package/dist/{chunk-OEQOAONV.js → chunk-6LQNW2CJ.js} +7 -7
  33. package/dist/{chunk-GFBR4S3Q.js → chunk-7NL7T6VU.js} +1 -1
  34. package/dist/{chunk-CFGBKPWQ.js → chunk-7TUDDW3Y.js} +3 -3
  35. package/dist/{chunk-RFE7DCZK.js → chunk-B5WJBVHY.js} +2 -2
  36. package/dist/{chunk-S7GX44BU.js → chunk-BXIVMNLR.js} +1 -1
  37. package/dist/{chunk-QMEF4Z7R.js → chunk-CNI74T7U.js} +6 -6
  38. package/dist/{chunk-X76JVHQ4.js → chunk-CTS72G4B.js} +1 -1
  39. package/dist/{chunk-G35QGPO2.js → chunk-DFWJJOW3.js} +2 -6
  40. package/dist/{chunk-INTBMGNX.js → chunk-EEUFKVCD.js} +22 -22
  41. package/dist/{chunk-H4DCWLTR.js → chunk-EFNUCRB5.js} +8 -8
  42. package/dist/{chunk-V4HXV5MY.js → chunk-EQBHN4JK.js} +1 -1
  43. package/dist/{chunk-JOI3DHML.js → chunk-ES7KCEND.js} +1 -1
  44. package/dist/{chunk-WWXJUURQ.js → chunk-G6YRPYDC.js} +1 -1
  45. package/dist/{chunk-WMJS5DDV.js → chunk-GZ336GZ5.js} +148 -19
  46. package/dist/{chunk-IME5OW4Y.js → chunk-I7GEGEHZ.js} +1 -1
  47. package/dist/{chunk-2NL3Y47F.js → chunk-J357NJQ5.js} +5 -5
  48. package/dist/{chunk-53IEEFFP.js → chunk-KJGH3KWA.js} +3 -3
  49. package/dist/{chunk-ELCYG6UU.js → chunk-KZX7LORG.js} +3 -3
  50. package/dist/{chunk-YSLIB7ER.js → chunk-LBN67OZR.js} +83 -7
  51. package/dist/{chunk-73JEAQRT.js → chunk-LCAM2P7H.js} +4 -4
  52. package/dist/{chunk-VOB2DKO6.js → chunk-LGXI6PWX.js} +24 -13
  53. package/dist/{chunk-P2P6MUQ4.js → chunk-LSPUMNZK.js} +2 -2
  54. package/dist/{chunk-AWZS5SVX.js → chunk-N2MSPOGG.js} +8 -8
  55. package/dist/{chunk-CCTYK5RH.js → chunk-NE2UQMC7.js} +10 -7
  56. package/dist/{chunk-D4YV5TDV.js → chunk-NHJU2ZAL.js} +11 -0
  57. package/dist/{chunk-G4RARFNT.js → chunk-OVWAO5KY.js} +1194 -726
  58. package/dist/{chunk-XRDSML3K.js → chunk-P3LE2CSJ.js} +2 -2
  59. package/dist/{chunk-KNIAWMBU.js → chunk-PI6VXVBK.js} +1 -1
  60. package/dist/{chunk-XLWZRHX5.js → chunk-PIZJBSGZ.js} +23 -29
  61. package/dist/{chunk-GY5SEFVL.js → chunk-QAINJHI4.js} +5 -5
  62. package/dist/{chunk-XND3J6AT.js → chunk-RAWYH7TW.js} +1 -1
  63. package/dist/{chunk-ENWIHU54.js → chunk-RFNMWONZ.js} +6 -6
  64. package/dist/{chunk-E53C73ML.js → chunk-S74KAE77.js} +4 -4
  65. package/dist/{chunk-7ANA7PBB.js → chunk-SR5FS2JP.js} +3 -3
  66. package/dist/{chunk-F2KSURTJ.js → chunk-VKLQ2M7V.js} +9 -9
  67. package/dist/{chunk-NWWDFHXP.js → chunk-VRHZXY5X.js} +1 -1
  68. package/dist/{chunk-6H2MKVV5.js → chunk-VSOO3Y6M.js} +1 -1
  69. package/dist/{chunk-NPO24LAN.js → chunk-VVU7GHIZ.js} +9 -9
  70. package/dist/{chunk-IOGDKPVU.js → chunk-VXAQTJSR.js} +1 -1
  71. package/dist/{chunk-ADEACKAH.js → chunk-XNXKRWWI.js} +11 -4
  72. package/dist/cli/chat.js +35 -35
  73. package/dist/cli/check.js +5 -5
  74. package/dist/cli/plugins.js +14 -14
  75. package/dist/cli/snapshot-create.js +10 -10
  76. package/dist/cli/upgrade.js +7 -7
  77. package/dist/db/schema/event-tasks.d.ts +21 -0
  78. package/dist/db/schema.d.ts +19 -0
  79. package/dist/{db-3IEVINAY.js → db-YTM7ZXPK.js} +8 -8
  80. package/dist/instrumentation.js +3 -3
  81. package/dist/nitro.js +7 -7
  82. package/dist/{oauth-callback-server-N7L2BB4L.js → oauth-callback-server-OXW4KQLQ.js} +31 -31
  83. package/dist/{runner-PTTZV3WU.js → runner-RJY5RNJO.js} +23 -23
  84. package/dist/{skills-POMWS2RR.js → skills-WZV6NLFY.js} +5 -5
  85. package/dist/spawn-J7SQJLYF.js +42 -0
  86. package/dist/task-runner-SMUQ5464.js +36 -0
  87. package/dist/{validation-U6LOMXR7.js → validation-XS5ZHV3W.js} +5 -5
  88. package/dist/version.js +1 -1
  89. package/dist/{work-FHA3BS4L.js → work-LJMSDEY3.js} +23 -23
  90. package/dist/worker-NMFZ7A2N.js +24 -0
  91. package/migrations/0038_event_task_status.sql +5 -0
  92. package/migrations/meta/0038_snapshot.json +3323 -0
  93. package/migrations/meta/_journal.json +8 -1
  94. package/package.json +4 -4
  95. package/dist/spawn-R75MXXKE.js +0 -42
  96. package/dist/task-runner-OQLRHADB.js +0 -36
  97. package/dist/worker-GR4UNVVP.js +0 -24
@@ -1,24 +1,34 @@
1
1
  import { type ResourceEvent, type User } from "@sentry/junior-plugin-api";
2
2
  import type { JuniorDatabase } from "@/db/db";
3
+ import { type EventTaskStatus } from "@/db/schema/event-tasks";
3
4
  import { type EventTask } from "./types";
4
- /** Read one event task by id. */
5
- export declare function getEventTask(db: JuniorDatabase, id: string): Promise<EventTask | undefined>;
6
- /** Create one retry-stable event task or return its existing record. */
7
- export declare function createEventTask(db: JuniorDatabase, task: EventTask): Promise<EventTask>;
8
- /** Replace an existing event task. */
9
- export declare function saveEventTask(db: JuniorDatabase, task: EventTask): Promise<EventTask | undefined>;
10
- /** Delete one existing event task. */
11
- export declare function deleteEventTask(db: JuniorDatabase, id: string): Promise<EventTask | undefined>;
12
- /** List event tasks in one Slack workspace. */
13
- export declare function listEventTasksForTeam(db: JuniorDatabase, teamId: string): Promise<EventTask[]>;
5
+ /** Live event task plus retained SQL status for history after delete. */
6
+ export type StoredEventTask = EventTask & {
7
+ status: EventTaskStatus;
8
+ };
9
+ /** Read one event task by id, including deleted rows when present. */
10
+ export declare function getEventTask(db: JuniorDatabase, id: string): Promise<StoredEventTask | undefined>;
11
+ /** Create one retry-stable event task, or revive a deleted row with the new payload. */
12
+ export declare function createEventTask(db: JuniorDatabase, task: EventTask): Promise<StoredEventTask>;
13
+ /** Replace an existing non-deleted event task. */
14
+ export declare function saveEventTask(db: JuniorDatabase, task: EventTask): Promise<StoredEventTask | undefined>;
15
+ /** Mark one existing event task deleted while retaining the row for history. */
16
+ export declare function deleteEventTask(db: JuniorDatabase, id: string): Promise<StoredEventTask | undefined>;
17
+ /** List live event tasks in one Slack workspace. */
18
+ export declare function listEventTasksForTeam(db: JuniorDatabase, teamId: string): Promise<StoredEventTask[]>;
14
19
  /** Return whether one event task was created by a viewer-linked Slack identity. */
15
20
  export declare function eventTaskBelongsToUser(task: EventTask, user: User): boolean;
16
- /** List a bounded newest-first page of event tasks created by one user. */
17
- export declare function listEventTasksCreatedBy(db: JuniorDatabase, user: User, limit: number): Promise<EventTask[]>;
18
- /** List event tasks whose current Slack destination is public. */
19
- export declare function listPublicEventTasksForTeams(db: JuniorDatabase, teamIds: string[], limit: number): Promise<EventTask[]>;
21
+ /** List a bounded newest-first page of live event tasks created by one user. */
22
+ export declare function listEventTasksCreatedBy(db: JuniorDatabase, user: User, limit: number): Promise<StoredEventTask[]>;
20
23
  /**
21
- * Collect match keys from event tasks for these identifiers and event types.
24
+ * List deleted event tasks created by one user, newest first.
25
+ * Used only to keep historical runs after the live task list drops the row.
26
+ */
27
+ export declare function listDeletedEventTasksCreatedBy(db: JuniorDatabase, user: User, limit: number): Promise<StoredEventTask[]>;
28
+ /** List live event tasks whose current Slack destination is public. */
29
+ export declare function listPublicEventTasksForTeams(db: JuniorDatabase, teamIds: string[], limit: number): Promise<StoredEventTask[]>;
30
+ /**
31
+ * Collect match keys from live event tasks for these identifiers and event types.
22
32
  * Does not evaluate match values — only reports keys that filters use.
23
33
  */
24
34
  export declare function collectEventTaskMatchKeys(db: JuniorDatabase, input: {
@@ -27,5 +37,5 @@ export declare function collectEventTaskMatchKeys(db: JuniorDatabase, input: {
27
37
  namespace: string;
28
38
  teamId: string;
29
39
  }): Promise<string[]>;
30
- /** Find every task matching one normalized resource event. */
31
- export declare function findMatchingEventTasks(db: JuniorDatabase, event: ResourceEvent, teamId: string): Promise<EventTask[]>;
40
+ /** Find every live task matching one normalized resource event. */
41
+ export declare function findMatchingEventTasks(db: JuniorDatabase, event: ResourceEvent, teamId: string): Promise<StoredEventTask[]>;
@@ -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;
@@ -43,7 +43,8 @@ export declare function createResourceEventInboundMessage(input: {
43
43
  * Enqueue a resource event as normal conversation mailbox input.
44
44
  *
45
45
  * The watch only names the conversation. Destination stays on the conversation
46
- * and is applied when the worker runs.
46
+ * and is applied when the worker runs. After too many automated Turns with no
47
+ * user Turn, later wakes stay quiet until a user message clears the pause.
47
48
  */
48
49
  export declare function enqueueResourceEventNotification(args: {
49
50
  event: ResourceEventNotification;
@@ -0,0 +1,110 @@
1
+ /**
2
+ * Stops event wakes after too many automated Turns with no user Turn.
3
+ *
4
+ * Resource-event watches count on the Conversation. Event tasks and other
5
+ * agent dispatches count on the Slack Destination. A finished user Turn clears
6
+ * every scope that Turn can name.
7
+ */
8
+ import type { Destination, Source } from "@sentry/junior-plugin-api";
9
+ import type { StateAdapter } from "chat";
10
+ import { z } from "zod";
11
+ /** Stored consecutive-turn limit for one Conversation or Destination. */
12
+ declare const automatedTurnLimitStateSchema: z.ZodObject<{
13
+ consecutiveAutomatedTurns: z.ZodNumber;
14
+ noticePostedAtMs: z.ZodOptional<z.ZodNumber>;
15
+ paused: z.ZodBoolean;
16
+ updatedAtMs: z.ZodNumber;
17
+ }, z.core.$strict>;
18
+ export type AutomatedTurnLimitState = z.output<typeof automatedTurnLimitStateSchema>;
19
+ export type AutomatedTurnLimitScope = {
20
+ kind: "conversation";
21
+ conversationId: string;
22
+ } | {
23
+ kind: "destination";
24
+ destination: Destination;
25
+ };
26
+ export type AutomatedTurnLimitDecision = {
27
+ status: "allow";
28
+ consecutiveAutomatedTurns: number;
29
+ } | {
30
+ status: "paused";
31
+ consecutiveAutomatedTurns: number;
32
+ shouldPostNotice: boolean;
33
+ };
34
+ export type AutomatedTurnLimitUpdate = {
35
+ consecutiveAutomatedTurns: number;
36
+ paused: boolean;
37
+ shouldPostNotice: boolean;
38
+ /** Where the pause notice should tell the user to reply. */
39
+ resumeIn: "thread" | "channel";
40
+ };
41
+ /** True when this Source starts an automated Turn rather than a user Turn. */
42
+ export declare function isAutomatedTurnSource(source: Source | undefined): boolean;
43
+ /**
44
+ * Scope that counts automated Turns for this Source.
45
+ * Resource-event watches stay on the Conversation. Dispatches use Destination.
46
+ */
47
+ export declare function automatedTurnLimitScopeForSource(args: {
48
+ conversationId: string;
49
+ destination?: Destination;
50
+ source?: Source;
51
+ }): AutomatedTurnLimitScope | undefined;
52
+ /** Plain notice when automatic updates stop until the user replies. */
53
+ export declare function buildAutomatedTurnLimitResponse(args: {
54
+ maxTurns: number;
55
+ resumeIn?: "thread" | "channel";
56
+ }): string;
57
+ /** Read the current consecutive-turn limit for one scope. */
58
+ export declare function getAutomatedTurnLimitState(args: {
59
+ nowMs?: number;
60
+ scope: AutomatedTurnLimitScope;
61
+ state?: StateAdapter;
62
+ }): Promise<AutomatedTurnLimitState>;
63
+ /**
64
+ * Allow one automated wake, or refuse once the consecutive-turn limit is full.
65
+ * If a prior pause never posted its notice, claim the notice slot so one caller
66
+ * posts it. Clear the claim if that post fails or is skipped.
67
+ */
68
+ export declare function admitAutomatedTurn(args: {
69
+ maxTurns: number;
70
+ nowMs?: number;
71
+ scope: AutomatedTurnLimitScope;
72
+ state?: StateAdapter;
73
+ }): Promise<AutomatedTurnLimitDecision>;
74
+ /** Count one finished automated Turn toward the consecutive-turn limit. */
75
+ export declare function countAutomatedTurn(args: {
76
+ maxTurns: number;
77
+ nowMs?: number;
78
+ scope: AutomatedTurnLimitScope;
79
+ state?: StateAdapter;
80
+ }): Promise<AutomatedTurnLimitUpdate>;
81
+ /**
82
+ * Drop a failed notice claim so a later paused wake can post the notice.
83
+ * Keeps the pause and consecutive count.
84
+ */
85
+ export declare function clearAutomatedTurnLimitNoticeClaim(args: {
86
+ nowMs?: number;
87
+ scope: AutomatedTurnLimitScope;
88
+ state?: StateAdapter;
89
+ }): Promise<void>;
90
+ /** Clear the consecutive automated-turn count after a user-owned Turn. */
91
+ export declare function resetAutomatedTurnLimit(args: {
92
+ nowMs?: number;
93
+ scope: AutomatedTurnLimitScope;
94
+ state?: StateAdapter;
95
+ }): Promise<void>;
96
+ /**
97
+ * Update consecutive automated-turn limits after one finished Turn.
98
+ *
99
+ * Automated Turns count one matching scope. User Turns clear Conversation and
100
+ * Destination scopes so later event wakes can run again.
101
+ */
102
+ export declare function recordFinishedTurnForAutomatedLimit(args: {
103
+ conversationId: string;
104
+ destination?: Destination;
105
+ maxTurns: number;
106
+ nowMs?: number;
107
+ source?: Source;
108
+ state?: StateAdapter;
109
+ }): Promise<AutomatedTurnLimitUpdate | undefined>;
110
+ export {};
@@ -37,6 +37,14 @@ declare const replyDecisionSchema: z.ZodObject<{
37
37
  confidence: z.ZodNumber;
38
38
  reason: z.ZodString;
39
39
  }, z.core.$strict>;
40
+ /**
41
+ * Return a stop decision for one message body.
42
+ * Deferred batches must call this on each body, not on joined text.
43
+ */
44
+ export declare function getThreadStopDecision(args: {
45
+ rawText: string;
46
+ text: string;
47
+ }): SubscribedDecisionResult | undefined;
40
48
  /** Fast heuristic check before the LLM classifier — skips messages directed at another party. */
41
49
  export declare function getSubscribedReplyPreflightDecision(args: {
42
50
  botUserName: string;
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Destination-visible pause notice for the consecutive automated-turn limit.
3
+ *
4
+ * Best-effort: a failed post must not fail the Turn or re-enable event wakes.
5
+ * The consecutive-turn limit claims the notice slot before this post. Clear that
6
+ * claim when the post fails or is skipped so a later paused wake can try again.
7
+ */
8
+ import type { Destination } from "@sentry/junior-plugin-api";
9
+ import { type AutomatedTurnLimitScope, type AutomatedTurnLimitUpdate } from "@/chat/services/automated-turn-limit";
10
+ /** Post the pause notice for one Conversation Location when known. */
11
+ export declare function postAutomatedTurnLimitNoticeForConversation(args: {
12
+ conversationId: string;
13
+ maxTurns: number;
14
+ nowMs?: number;
15
+ resumeIn?: "thread" | "channel";
16
+ scope?: AutomatedTurnLimitScope;
17
+ }): Promise<boolean>;
18
+ /** Post the pause notice for one Slack Destination. */
19
+ export declare function postAutomatedTurnLimitNoticeForDestination(args: {
20
+ destination: Destination;
21
+ maxTurns: number;
22
+ nowMs?: number;
23
+ resumeIn?: "thread" | "channel";
24
+ scope?: AutomatedTurnLimitScope;
25
+ threadTs?: string;
26
+ }): Promise<boolean>;
27
+ /**
28
+ * Post the pause notice after a finished Turn hits the limit.
29
+ * Prefer the Conversation Location so thread watches stay in-thread.
30
+ */
31
+ export declare function maybePostAutomatedTurnLimitNotice(args: {
32
+ conversationId: string;
33
+ destination?: Destination;
34
+ maxTurns: number;
35
+ nowMs?: number;
36
+ threadTs?: string;
37
+ update: AutomatedTurnLimitUpdate | undefined;
38
+ }): Promise<void>;
@@ -79,9 +79,10 @@ export declare function readTaskExecutions(args: {
79
79
  }): Promise<TaskExecutionRecord[]>;
80
80
  /** Load newest-first executions for the supplied viewer-visible tasks. */
81
81
  export declare function readTaskRuns(args: {
82
+ conversationIds?: string[];
82
83
  limit: number;
83
84
  namespace?: string;
84
- tasks: Array<{
85
+ tasks?: Array<{
85
86
  kind: TaskExecutionType;
86
87
  taskId: string;
87
88
  }>;
@@ -1,9 +1,12 @@
1
1
  import type { User } from "@sentry/junior-plugin-api";
2
2
  import type { ConversationSourceTask } from "@/api/schema/conversation";
3
3
  import type { TaskExecutionList, TaskList, TaskRunList } from "@/api/schema/task";
4
- /** Read viewer-owned and public-workspace tasks as one bounded newest-first projection. */
5
4
  export declare function readViewerTasks(user: User): Promise<TaskList>;
6
- /** Read newest runs across the viewer-visible scheduled and event tasks. */
5
+ /**
6
+ * Read newest runs across live viewer-visible tasks and deleted tasks the
7
+ * viewer owns. Deleted task rows stay available so history does not depend on
8
+ * conversation actor membership.
9
+ */
7
10
  export declare function readViewerTaskRuns(user: User): Promise<TaskRunList>;
8
11
  export declare class ViewerTaskNotFoundError extends Error {
9
12
  constructor();
@@ -13,9 +13,13 @@ import type { SpawnAgent } from "@/chat/agent/types";
13
13
  import type { AttachmentStorage } from "@/chat/attachments/storage";
14
14
  import type { Workspace } from "@/chat/workspaces/types";
15
15
  import type { ConversationPrivacy } from "@/chat/conversation-privacy";
16
+ interface HandoffProfile {
17
+ description?: string;
18
+ name: ModelProfile;
19
+ }
16
20
  interface HandoffControl {
17
- /** Non-empty catalog of configured targets. */
18
- profiles: readonly [ModelProfile, ...ModelProfile[]];
21
+ /** Non-empty list of configured profiles. */
22
+ profiles: readonly [HandoffProfile, ...HandoffProfile[]];
19
23
  execute: (profile: ModelProfile, options: {
20
24
  signal?: AbortSignal;
21
25
  toolCallId: string;
@@ -1,13 +1,14 @@
1
1
  import {
2
+ TURN_REASONING_LEVELS,
2
3
  modelProfileSchema,
3
4
  parseTurnReasoningLevel
4
- } from "./chunk-D4YV5TDV.js";
5
+ } from "./chunk-NHJU2ZAL.js";
5
6
  import {
6
7
  getCurrentConversationPrivacy,
7
8
  toCanonicalInputMessage,
8
9
  toCanonicalOutputMessage,
9
10
  toGenAiMessagesTraceAttributes
10
- } from "./chunk-JOI3DHML.js";
11
+ } from "./chunk-ES7KCEND.js";
11
12
  import {
12
13
  extractGenAiUsageAttributes,
13
14
  logException,
@@ -15,7 +16,7 @@ import {
15
16
  normalizeGenAiFinishReason,
16
17
  serializeGenAiAttribute,
17
18
  withSpan
18
- } from "./chunk-X76JVHQ4.js";
19
+ } from "./chunk-CTS72G4B.js";
19
20
 
20
21
  // src/chat/services/provider-error.ts
21
22
  var ProviderError = class extends Error {
@@ -675,6 +676,7 @@ var MIN_AGENT_TURN_TIMEOUT_MS = 10 * 1e3;
675
676
  var DEFAULT_AGENT_TURN_TIMEOUT_MS = 12 * 60 * 1e3;
676
677
  var MAX_SLICES_PER_TURN = 100;
677
678
  var MAX_TOOL_CALLS_PER_TURN = 150;
679
+ var MAX_CONSECUTIVE_AUTOMATED_TURNS = 10;
678
680
  var DEFAULT_FUNCTION_MAX_DURATION_SECONDS = 300;
679
681
  var DEFAULT_SLACK_SLASH_COMMAND = "/jr";
680
682
  var DEFAULT_PROCESSING_REACTION_EMOJI = "eyes";
@@ -806,33 +808,103 @@ function validateGatewayModelId(raw) {
806
808
  function validateEmbeddingModelId(raw) {
807
809
  return toOptionalTrimmed(raw);
808
810
  }
811
+ var DEFAULT_STANDARD_PROFILE_DESCRIPTION = "Use for default assistant work: lookups, explanations, ordinary tool use, short answers, and light investigation of one source. Avoid for implementation, debugging, multi-file changes, architecture decisions, or research across several systems.";
812
+ var DEFAULT_HANDOFF_PROFILE_DESCRIPTION = "Use for coding and difficult multi-step work: implementation, debugging, root-cause analysis, broad refactors, multi-file changes, architecture decisions, and research across several systems. Avoid for simple lookups, short answers, single-file reads, or ordinary tool use that the default profile can finish.";
813
+ function parseOptionalProfileDescription(rawDescription, configName, profile) {
814
+ if (rawDescription === void 0) {
815
+ return void 0;
816
+ }
817
+ if (typeof rawDescription !== "string") {
818
+ throw new Error(
819
+ `${configName}.${profile}.description must be a string when set`
820
+ );
821
+ }
822
+ const description = toOptionalTrimmed(rawDescription);
823
+ if (!description) {
824
+ throw new Error(
825
+ `${configName}.${profile}.description must not be empty when set`
826
+ );
827
+ }
828
+ return description;
829
+ }
830
+ function parseOptionalProfileReasoningLevel(rawReasoningLevel, configName, profile) {
831
+ if (rawReasoningLevel === void 0) {
832
+ return void 0;
833
+ }
834
+ try {
835
+ return parseTurnReasoningLevel(rawReasoningLevel);
836
+ } catch {
837
+ throw new Error(
838
+ `${configName}.${profile}.reasoningLevel must be one of ${TURN_REASONING_LEVELS.join(", ")}`
839
+ );
840
+ }
841
+ }
842
+ function parseProfileConfig(rawProfile, configName, profile) {
843
+ if (typeof rawProfile === "string") {
844
+ const modelId2 = validateGatewayModelId(rawProfile);
845
+ if (!modelId2) {
846
+ throw new Error(`${configName}.${profile} must not be empty`);
847
+ }
848
+ return { modelId: modelId2 };
849
+ }
850
+ if (!rawProfile || typeof rawProfile !== "object" || Array.isArray(rawProfile)) {
851
+ throw new Error(
852
+ `${configName}.${profile} must be a model id string or an object with modelId`
853
+ );
854
+ }
855
+ const rawModelId = rawProfile.modelId;
856
+ if (typeof rawModelId !== "string") {
857
+ throw new Error(
858
+ `${configName}.${profile}.modelId must be a model id string`
859
+ );
860
+ }
861
+ const modelId = validateGatewayModelId(rawModelId);
862
+ if (!modelId) {
863
+ throw new Error(`${configName}.${profile}.modelId must not be empty`);
864
+ }
865
+ const description = parseOptionalProfileDescription(
866
+ rawProfile.description,
867
+ configName,
868
+ profile
869
+ );
870
+ const reasoningLevel = parseOptionalProfileReasoningLevel(
871
+ rawProfile.reasoningLevel,
872
+ configName,
873
+ profile
874
+ );
875
+ return {
876
+ modelId,
877
+ ...description ? { description } : void 0,
878
+ ...reasoningLevel ? { reasoningLevel } : void 0
879
+ };
880
+ }
809
881
  function parseProfileMap(rawProfiles, configName) {
810
882
  if (!rawProfiles || typeof rawProfiles !== "object" || Array.isArray(rawProfiles)) {
811
883
  const objectType = configName === "AI_MODEL_PROFILES" ? "a JSON object" : "an object";
812
884
  throw new Error(`${configName} must be ${objectType}`);
813
885
  }
814
886
  const profiles = {};
815
- for (const [profile, rawModelId] of Object.entries(rawProfiles)) {
887
+ for (const [profile, rawProfile] of Object.entries(rawProfiles)) {
816
888
  if (!modelProfileSchema.safeParse(profile).success) {
817
889
  throw new Error(
818
890
  `${configName} profile "${profile}" must match ^[a-z][a-z0-9_-]*$`
819
891
  );
820
892
  }
821
- if (typeof rawModelId !== "string") {
822
- throw new Error(`${configName}.${profile} must be a model id string`);
823
- }
824
- const modelId = validateGatewayModelId(rawModelId);
825
- if (!modelId) {
826
- throw new Error(`${configName}.${profile} must not be empty`);
827
- }
828
- profiles[profile] = { modelId };
893
+ profiles[profile] = parseProfileConfig(rawProfile, configName, profile);
829
894
  }
830
895
  return profiles;
831
896
  }
832
897
  function parseProfiles(rawValue, standardModelId, handoffModelId) {
833
898
  const profiles = {
834
- standard: { modelId: standardModelId },
835
- handoff: { modelId: handoffModelId, reasoningLevel: "high" }
899
+ standard: {
900
+ modelId: standardModelId,
901
+ description: DEFAULT_STANDARD_PROFILE_DESCRIPTION
902
+ },
903
+ handoff: {
904
+ modelId: handoffModelId,
905
+ description: DEFAULT_HANDOFF_PROFILE_DESCRIPTION,
906
+ reasoningLevel: "high"
907
+ }
836
908
  };
837
909
  const trimmed = toOptionalTrimmed(rawValue);
838
910
  if (trimmed === void 0) {
@@ -904,6 +976,7 @@ function readBotConfig(env, functionMaxDurationSeconds) {
904
976
  visionModelId: validateGatewayModelId(env.AI_VISION_MODEL),
905
977
  maxSlicesPerTurn: MAX_SLICES_PER_TURN,
906
978
  maxToolCallsPerTurn: MAX_TOOL_CALLS_PER_TURN,
979
+ maxConsecutiveAutomatedTurns: MAX_CONSECUTIVE_AUTOMATED_TURNS,
907
980
  turnTimeoutMs: parseAgentTurnTimeoutMs(
908
981
  env.AGENT_TURN_TIMEOUT_MS,
909
982
  maxTurnTimeoutMs
@@ -7,7 +7,7 @@ import {
7
7
  authenticationUnlinkedEvent,
8
8
  fallbackShortTitle,
9
9
  generateShortTitle
10
- } from "./chunk-RFE7DCZK.js";
10
+ } from "./chunk-B5WJBVHY.js";
11
11
  import {
12
12
  bindProviderConversation,
13
13
  createSqlConversationEventStore,
@@ -16,31 +16,31 @@ import {
16
16
  getSqlExecutor,
17
17
  sanitizePostgresJson,
18
18
  withConversationEventLock
19
- } from "./chunk-ADEACKAH.js";
19
+ } from "./chunk-XNXKRWWI.js";
20
20
  import {
21
21
  SANDBOX_REPOS_ROOT,
22
22
  SANDBOX_WORKSPACE_ROOT,
23
23
  sleep
24
- } from "./chunk-7ANA7PBB.js";
24
+ } from "./chunk-SR5FS2JP.js";
25
25
  import {
26
26
  MUTATION_LOCK_TTL_MS,
27
27
  fenceLock,
28
28
  getStateAdapter,
29
29
  withLock
30
- } from "./chunk-V4HXV5MY.js";
30
+ } from "./chunk-EQBHN4JK.js";
31
31
  import {
32
32
  agentHistoryItemSchema,
33
33
  contextProvenance,
34
34
  instructionActors,
35
35
  instructionProvenanceFor
36
- } from "./chunk-GFBR4S3Q.js";
36
+ } from "./chunk-7NL7T6VU.js";
37
37
  import {
38
38
  botConfig,
39
39
  completeText,
40
40
  escapeXml,
41
41
  resolveGatewayModel,
42
42
  unwrapCurrentInstruction
43
- } from "./chunk-U76RWMFR.js";
43
+ } from "./chunk-5XSEOJPC.js";
44
44
  import {
45
45
  parseSlackMessageTs,
46
46
  toStoredSlackActor
@@ -51,7 +51,7 @@ import {
51
51
  logException,
52
52
  logWarn,
53
53
  setSpanAttributes
54
- } from "./chunk-X76JVHQ4.js";
54
+ } from "./chunk-CTS72G4B.js";
55
55
 
56
56
  // src/chat/turn-context-tag.ts
57
57
  var TURN_CONTEXT_TAG = "runtime-turn-context";
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  TURN_REASONING_LEVELS,
3
3
  modelProfileSchema
4
- } from "./chunk-D4YV5TDV.js";
4
+ } from "./chunk-NHJU2ZAL.js";
5
5
 
6
6
  // src/chat/conversations/provenance.ts
7
7
  import { actorSchema } from "@sentry/junior-plugin-api";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  runWithTurnRequestDeadline
3
- } from "./chunk-NWWDFHXP.js";
3
+ } from "./chunk-VRHZXY5X.js";
4
4
  import {
5
5
  QUEUE_SIGNATURE_MAX_AGE_MS,
6
6
  createVercelQueueClient,
@@ -9,7 +9,7 @@ import {
9
9
  } from "./chunk-7H2VZTWN.js";
10
10
  import {
11
11
  logWarn
12
- } from "./chunk-X76JVHQ4.js";
12
+ } from "./chunk-CTS72G4B.js";
13
13
 
14
14
  // src/chat/plugins/task-queue.ts
15
15
  import { createHash } from "crypto";
@@ -129,7 +129,7 @@ function pluginTaskCallback() {
129
129
  onRejected: logPluginTaskQueueMessageRejected,
130
130
  // Load the runner on first use so this module can send without a cycle.
131
131
  run: async (message) => {
132
- const { processPluginTask } = await import("./task-runner-OQLRHADB.js");
132
+ const { processPluginTask } = await import("./task-runner-SMUQ5464.js");
133
133
  await processPluginTask(message);
134
134
  },
135
135
  topic: PLUGIN_TASK_QUEUE_TOPIC,
@@ -3,10 +3,10 @@ import {
3
3
  } from "./chunk-PDO5BLNM.js";
4
4
  import {
5
5
  botConfig
6
- } from "./chunk-U76RWMFR.js";
6
+ } from "./chunk-5XSEOJPC.js";
7
7
  import {
8
8
  logWarn
9
- } from "./chunk-X76JVHQ4.js";
9
+ } from "./chunk-CTS72G4B.js";
10
10
 
11
11
  // src/chat/conversations/structured-events.ts
12
12
  import {
@@ -12,7 +12,7 @@ import {
12
12
  logInfo,
13
13
  logWarn,
14
14
  setSpanAttributes
15
- } from "./chunk-X76JVHQ4.js";
15
+ } from "./chunk-CTS72G4B.js";
16
16
 
17
17
  // src/chat/plugins/manifest.ts
18
18
  import { z } from "zod";
@@ -7,23 +7,23 @@ import {
7
7
  import {
8
8
  formatProviderLabel,
9
9
  startOAuthFlow
10
- } from "./chunk-ENWIHU54.js";
10
+ } from "./chunk-RFNMWONZ.js";
11
11
  import {
12
12
  abandonReplacedPendingAuth,
13
13
  canReusePendingAuthLink
14
- } from "./chunk-G35QGPO2.js";
14
+ } from "./chunk-DFWJJOW3.js";
15
15
  import {
16
16
  recordAuthorizationRequested
17
- } from "./chunk-OEQOAONV.js";
17
+ } from "./chunk-6LQNW2CJ.js";
18
18
  import {
19
19
  AGENT_INVOCATION_MAILBOX_STATUSES
20
- } from "./chunk-XLWZRHX5.js";
20
+ } from "./chunk-PIZJBSGZ.js";
21
21
  import {
22
22
  TURN_REASONING_LEVELS
23
- } from "./chunk-D4YV5TDV.js";
23
+ } from "./chunk-NHJU2ZAL.js";
24
24
  import {
25
25
  pluginCatalogRuntime
26
- } from "./chunk-WWXJUURQ.js";
26
+ } from "./chunk-G6YRPYDC.js";
27
27
  import {
28
28
  credentialContextSchema
29
29
  } from "./chunk-CEMMLV5L.js";
@@ -12,7 +12,7 @@ import {
12
12
  } from "./chunk-PEQXYSSE.js";
13
13
  import {
14
14
  JUNIOR_VERSION
15
- } from "./chunk-IME5OW4Y.js";
15
+ } from "./chunk-I7GEGEHZ.js";
16
16
 
17
17
  // src/deployment.ts
18
18
  var JUNIOR_HEARTBEAT_ROUTE = "/api/internal/heartbeat";
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  abandonTurnRecord,
3
3
  extractAssistantText
4
- } from "./chunk-OEQOAONV.js";
4
+ } from "./chunk-6LQNW2CJ.js";
5
5
  import {
6
6
  buildDeterministicTurnId
7
7
  } from "./chunk-DA2KGZZI.js";
@@ -83,10 +83,7 @@ function markTurnFailed(args) {
83
83
  // src/chat/no-reply.ts
84
84
  var NO_REPLY_MARKER = "[[NO_REPLY]]";
85
85
  function isNoReplyMarker(text) {
86
- return text.trim() === NO_REPLY_MARKER;
87
- }
88
- function containsNoReplyMarker(text) {
89
- return text.includes(NO_REPLY_MARKER);
86
+ return text.trimEnd().endsWith(NO_REPLY_MARKER);
90
87
  }
91
88
 
92
89
  // src/chat/services/assistant-reply.ts
@@ -198,7 +195,6 @@ export {
198
195
  markTurnFailed,
199
196
  NO_REPLY_MARKER,
200
197
  isNoReplyMarker,
201
- containsNoReplyMarker,
202
198
  sanitizeAssistantText,
203
199
  decideReply,
204
200
  getAssistantReplyText,