@sentry/junior 0.137.0 → 0.139.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 (103) hide show
  1. package/dist/{agent-hooks-MZOKEP3J.js → agent-hooks-MJXMD7JP.js} +11 -9
  2. package/dist/api/schema/task.d.ts +100 -1
  3. package/dist/api/schema.d.ts +2 -2
  4. package/dist/api/schema.js +9 -1
  5. package/dist/api.js +202 -55
  6. package/dist/app.d.ts +7 -0
  7. package/dist/app.js +601 -1356
  8. package/dist/{catalog-runtime-MD66YLFV.js → catalog-runtime-GCCFYPIK.js} +6 -6
  9. package/dist/chat/agent/request.d.ts +44 -4
  10. package/dist/chat/agent-dispatch/heartbeat.d.ts +5 -0
  11. package/dist/chat/agent-dispatch/types.d.ts +1 -1
  12. package/dist/chat/agent-invocations/errors.d.ts +8 -0
  13. package/dist/chat/agent-invocations/spawn.d.ts +12 -0
  14. package/dist/chat/agent-invocations/store.d.ts +44 -0
  15. package/dist/chat/agent-invocations/types.d.ts +449 -0
  16. package/dist/chat/agent-invocations/work.d.ts +31 -0
  17. package/dist/chat/config.d.ts +1 -0
  18. package/dist/chat/conversations/sql/store.d.ts +8 -0
  19. package/dist/chat/conversations/store.d.ts +7 -0
  20. package/dist/chat/credentials/context.d.ts +14 -0
  21. package/dist/chat/experimental.d.ts +16 -0
  22. package/dist/chat/resource-events/actor.d.ts +23 -0
  23. package/dist/chat/runtime/agent-runner.d.ts +2 -1
  24. package/dist/chat/services/auth-pause.d.ts +0 -1
  25. package/dist/chat/services/mcp-auth-orchestration.d.ts +3 -2
  26. package/dist/chat/services/plugin-auth-orchestration.d.ts +3 -2
  27. package/dist/chat/state/turn-session.d.ts +1 -5
  28. package/dist/chat/task-execution/state.d.ts +23 -11
  29. package/dist/chat/task-execution/store.d.ts +20 -11
  30. package/dist/chat/task-execution/worker.d.ts +2 -2
  31. package/dist/chat/tasks/execution-stats.d.ts +34 -3
  32. package/dist/chat/tasks/read.d.ts +3 -1
  33. package/dist/chat/tools/runtime/spawn-agent.d.ts +31 -0
  34. package/dist/chat/tools/types.d.ts +2 -0
  35. package/dist/{chunk-T6Z5S6PQ.js → chunk-2ECYPYKP.js} +1 -1
  36. package/dist/chunk-3T2JAGRH.js +115 -0
  37. package/dist/chunk-3Y47VMHV.js +6 -0
  38. package/dist/chunk-4V4QIYJH.js +41 -0
  39. package/dist/{chunk-SYCX4DOK.js → chunk-5GSZWRE2.js} +434 -65
  40. package/dist/chunk-5IDFPCON.js +46 -0
  41. package/dist/{chunk-36EG34YI.js → chunk-6OHTDR4F.js} +1 -1
  42. package/dist/chunk-6SU2DT27.js +441 -0
  43. package/dist/{chunk-UG6ZYPBI.js → chunk-75I3UBIL.js} +73 -60
  44. package/dist/chunk-AEQISMMU.js +711 -0
  45. package/dist/{chunk-5PNJMOY5.js → chunk-C2N6MNO4.js} +9 -2
  46. package/dist/{chunk-BZAMASJP.js → chunk-CXH25EL4.js} +2 -2
  47. package/dist/{chunk-K2NCQJKH.js → chunk-DW4PMHNW.js} +3 -3
  48. package/dist/chunk-EM225ITJ.js +56 -0
  49. package/dist/{chunk-UOXATPSI.js → chunk-G52UXKGO.js} +7 -5
  50. package/dist/chunk-GI52BW74.js +207 -0
  51. package/dist/{chunk-3Q342EJJ.js → chunk-GWQSZT4C.js} +2 -2
  52. package/dist/{chunk-EPBBYMNE.js → chunk-HUO3UTAQ.js} +1 -1
  53. package/dist/{chunk-WPPXIAFB.js → chunk-IIESAYDL.js} +17 -434
  54. package/dist/chunk-IUFM5JWV.js +24 -0
  55. package/dist/{chunk-UZQ4VY3M.js → chunk-J3S57AXY.js} +1 -1
  56. package/dist/{chunk-DQXAAVTR.js → chunk-J6EJQCHY.js} +46 -1471
  57. package/dist/{chunk-6ZK6FZIV.js → chunk-KCBZSJRU.js} +6 -27
  58. package/dist/chunk-MQ5RYATN.js +1523 -0
  59. package/dist/{chunk-5G34IEK2.js → chunk-MXTVUV3I.js} +2 -56
  60. package/dist/{chunk-YRPC6MJY.js → chunk-MXVD2BDS.js} +1 -1
  61. package/dist/{chunk-POBSY47Q.js → chunk-PJW6U46K.js} +337 -306
  62. package/dist/chunk-Q6QZTTLS.js +55 -0
  63. package/dist/{chunk-ZFS67WIR.js → chunk-QTBKPZDA.js} +57 -6
  64. package/dist/chunk-R776HKPK.js +81 -0
  65. package/dist/{chunk-77VDLF3Z.js → chunk-RUIXF3YQ.js} +10 -0
  66. package/dist/chunk-U27DSXKB.js +714 -0
  67. package/dist/{chunk-XZ7NPXAB.js → chunk-U7CMWQ7Z.js} +1 -1
  68. package/dist/{chunk-RB4NJKEX.js → chunk-UDBUS533.js} +125 -51
  69. package/dist/chunk-WQRX57EA.js +668 -0
  70. package/dist/chunk-WZH4JV7R.js +320 -0
  71. package/dist/{chunk-HCIVH3JS.js → chunk-YCTDA7BD.js} +28 -0
  72. package/dist/{chunk-YNFERGKP.js → chunk-YGZ2OLXL.js} +1 -1
  73. package/dist/{chunk-EWDSB24B.js → chunk-ZQCGQMF2.js} +16 -776
  74. package/dist/{chunk-KJ4HGRHN.js → chunk-ZYQSPQYR.js} +515 -601
  75. package/dist/cli/chat.js +68 -29
  76. package/dist/cli/check.js +7 -7
  77. package/dist/cli/plugins.js +15 -13
  78. package/dist/cli/snapshot-create.js +11 -10
  79. package/dist/cli/upgrade.js +8 -8
  80. package/dist/conversation-work-KATFHLG3.js +56 -0
  81. package/dist/db/schema/agent-invocations.d.ts +544 -0
  82. package/dist/db/schema/task-executions.d.ts +23 -2
  83. package/dist/db/schema.d.ts +563 -2
  84. package/dist/{db-66E7VDNC.js → db-UXPPGHDF.js} +7 -7
  85. package/dist/experimental-HH6OM6UH.js +13 -0
  86. package/dist/instrumentation.js +4 -4
  87. package/dist/nitro.js +4 -3
  88. package/dist/{oauth-callback-server-7QPD4Q7P.js → oauth-callback-server-4ZYMD5UA.js} +28 -19
  89. package/dist/{runner-L2BKJKOD.js → runner-MZSEONRZ.js} +32 -18
  90. package/dist/{sandbox-egress-signals-A5HWIQH4.js → sandbox-egress-signals-EKPAVWQL.js} +2 -2
  91. package/dist/{skills-HOQ2NSPW.js → skills-IMVPXSQ7.js} +7 -7
  92. package/dist/spawn-S5LSVOCE.js +38 -0
  93. package/dist/{validation-ENHI7S2C.js → validation-ZKCY2BVR.js} +7 -7
  94. package/dist/version.js +1 -1
  95. package/dist/work-GG3XDYRX.js +46 -0
  96. package/dist/worker-23AUELCJ.js +24 -0
  97. package/migrations/0021_first_warhawk.sql +35 -0
  98. package/migrations/0022_task_execution_status.sql +3 -0
  99. package/migrations/meta/0021_snapshot.json +2176 -0
  100. package/migrations/meta/0022_snapshot.json +2186 -0
  101. package/migrations/meta/_journal.json +15 -1
  102. package/package.json +4 -4
  103. package/dist/chunk-6QW2RRQT.js +0 -233
@@ -0,0 +1,31 @@
1
+ import type { StateAdapter } from "chat";
2
+ import type { ConversationStore } from "@/chat/conversations/store";
3
+ import type { AgentRunner } from "@/chat/runtime/agent-runner";
4
+ import { type InboundMessage } from "@/chat/task-execution/store";
5
+ import type { ConversationWorkQueue } from "@/chat/task-execution/queue";
6
+ import type { ConversationWorkerContext, ConversationWorkerResult } from "@/chat/task-execution/worker";
7
+ import type { AgentInvocation, CreateAgentInvocationInput } from "./types";
8
+ type EnqueueOptions = {
9
+ conversationStore?: ConversationStore;
10
+ nowMs?: number;
11
+ queue: ConversationWorkQueue;
12
+ state?: StateAdapter;
13
+ };
14
+ /** Build destinationless mailbox work that references one durable invocation. */
15
+ export declare function buildAgentInvocationInboundMessage(invocation: AgentInvocation, nowMs?: number): InboundMessage;
16
+ /** Append one invocation to its child mailbox and send the normal queue wake. */
17
+ export declare function enqueueAgentInvocation(invocation: AgentInvocation, options: EnqueueOptions): Promise<void>;
18
+ /** Create or replay an invocation, then durably schedule its child work. */
19
+ export declare function createAndEnqueueAgentInvocation(input: CreateAgentInvocationInput, options: EnqueueOptions): Promise<AgentInvocation>;
20
+ /** Resolve invocation execution from mailbox input or durable active state. */
21
+ export declare function resolveAgentInvocationId(context: ConversationWorkerContext): Promise<string | undefined>;
22
+ /** Build the invocation consumer that advances work through the shared runner. */
23
+ export declare function createAgentInvocationWorker(options: {
24
+ agentRunner: AgentRunner;
25
+ }): (context: ConversationWorkerContext, invocationId: string) => Promise<ConversationWorkerResult>;
26
+ /** Route invocation-backed work before falling through to existing consumers. */
27
+ export declare function routeAgentInvocationWork(options: {
28
+ fallbackWorker: (context: ConversationWorkerContext) => Promise<ConversationWorkerResult>;
29
+ invocationWorker: (context: ConversationWorkerContext, invocationId: string) => Promise<ConversationWorkerResult>;
30
+ }): (context: ConversationWorkerContext) => Promise<ConversationWorkerResult>;
31
+ export {};
@@ -29,6 +29,7 @@ export type SqlDriver = "neon" | "postgres";
29
29
  export interface ChatConfig {
30
30
  bot: BotConfig;
31
31
  functionMaxDurationSeconds: number;
32
+ conversationWorkEnabled: boolean;
32
33
  conversationWorkSoftYieldAfterMs: number;
33
34
  sql: {
34
35
  databaseUrl: string;
@@ -5,11 +5,19 @@ import type { JuniorSqlDatabase } from "@/db/db";
5
5
  import type { Conversation, ConversationExecution, ConversationSource, ConversationStore } from "../store";
6
6
  import type { AgentTurnUsage } from "@/chat/usage";
7
7
  import { type ProviderConversationBinding, type ProviderConversationReference } from "./bindings";
8
+ /** Serialize one conversation's durable mutations inside a SQL transaction. */
9
+ export declare function withConversationMutationLock<T>(executor: JuniorSqlDatabase, conversationId: string, callback: () => Promise<T>): Promise<T>;
8
10
  export declare class SqlStore implements ConversationStore {
9
11
  private readonly executor;
10
12
  constructor(executor: JuniorSqlDatabase);
11
13
  getConversationIdByProviderConversation(args: ProviderConversationReference): Promise<string | undefined>;
12
14
  bindProviderConversation(args: ProviderConversationBinding): Promise<void>;
15
+ createChild(args: {
16
+ childConversationId: string;
17
+ parentConversationId: string;
18
+ nowMs?: number;
19
+ source?: ConversationSource;
20
+ }): Promise<void>;
13
21
  get(args: {
14
22
  conversationId: string;
15
23
  }): Promise<Conversation | undefined>;
@@ -49,6 +49,13 @@ export interface Conversation {
49
49
  }
50
50
  /** Persist and read durable conversation metadata for reporting surfaces. */
51
51
  export interface ConversationStore {
52
+ /** Create one destinationless child with immutable parent lineage. */
53
+ createChild(args: {
54
+ childConversationId: string;
55
+ parentConversationId: string;
56
+ nowMs?: number;
57
+ source?: ConversationSource;
58
+ }): Promise<void>;
52
59
  get(args: {
53
60
  conversationId: string;
54
61
  }): Promise<Conversation | undefined>;
@@ -1,4 +1,5 @@
1
1
  import { z } from "zod";
2
+ import { type Actor } from "@/chat/actor";
2
3
  declare const credentialSubjectBindingSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
3
4
  type: z.ZodLiteral<"slack-direct-conversation">;
4
5
  teamId: z.ZodString;
@@ -100,6 +101,19 @@ export type CredentialSubjectBinding = z.output<typeof credentialSubjectBindingS
100
101
  export type CredentialSystemActor = z.output<typeof credentialSystemActorSchema>;
101
102
  export type CredentialSubject = z.output<typeof credentialSubjectSchema>;
102
103
  export type CredentialContext = z.output<typeof credentialContextSchema>;
104
+ /**
105
+ * Build credential authority from the run actor.
106
+ *
107
+ * `actor` is the rich execution principal. This projects the credential stub
108
+ * tools/auth need from it:
109
+ * - user actor → `{ actor: { type: "user", userId } }`
110
+ * - system actor → `{ actor: systemActor, subject? }`
111
+ *
112
+ * They only diverge when a system run carries an explicit delegated `subject`
113
+ * (scheduled/event-task creator credentials). Callers should set `actor` first,
114
+ * then derive this — do not hand-build parallel shapes.
115
+ */
116
+ export declare function credentialContextForActor(actor: Actor, subject?: CredentialSubject): CredentialContext;
103
117
  /** Return the user whose OAuth token may satisfy this credential request. */
104
118
  export declare function credentialUserSubjectId(context: CredentialContext): string | undefined;
105
119
  /** Parse an untrusted credential context payload from sandbox egress state. */
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Known unstable product features that must be opted into explicitly.
3
+ * Add new keys here as features graduate from private experiments; remove them
4
+ * once they become stable defaults.
5
+ */
6
+ export declare const EXPERIMENTAL_FEATURES: readonly ["subagents"];
7
+ /** One known experimental feature name. */
8
+ export type ExperimentalFeature = (typeof EXPERIMENTAL_FEATURES)[number];
9
+ /** App-level opt-ins for unstable product features. */
10
+ export type ExperimentalFeaturesConfig = Readonly<Partial<Record<ExperimentalFeature, boolean>>>;
11
+ /** Replace app-level experimental opt-ins and return the previous setting. */
12
+ export declare function setExperimentalFeatures(config?: ExperimentalFeaturesConfig): ExperimentalFeaturesConfig;
13
+ /** Return a copy of the app-level experimental opt-ins configured by createApp(). */
14
+ export declare function getExperimentalFeatures(): ExperimentalFeaturesConfig;
15
+ /** Return whether one experimental feature is enabled via createApp({ experimental }). */
16
+ export declare function isExperimentalFeatureEnabled(feature: ExperimentalFeature): boolean;
@@ -0,0 +1,23 @@
1
+ /** Synthetic Slack author id stamped on resource-event mailbox messages. */
2
+ export declare const RESOURCE_EVENT_SLACK_AUTHOR_ID = "UJRNEVENT";
3
+ /** System execution actor for every resource-event turn. */
4
+ export declare const RESOURCE_EVENT_SYSTEM_ACTOR: {
5
+ readonly platform: "system";
6
+ readonly name: "resource-event";
7
+ };
8
+ /** Whether a durable conversation message is a resource-event turn input. */
9
+ export declare function isResourceEventConversationMessage(message: {
10
+ author?: {
11
+ userId?: string;
12
+ };
13
+ meta?: {
14
+ eventType?: string;
15
+ };
16
+ }): boolean;
17
+ /**
18
+ * Whether a Slack Message payload is a synthetic resource-event notification.
19
+ * Checks the raw `event_type` marker stamped at mailbox serialization.
20
+ */
21
+ export declare function isResourceEventSlackMessage(message: {
22
+ raw?: unknown;
23
+ }): boolean;
@@ -1,4 +1,4 @@
1
- import type { AgentRunRequest } from "@/chat/agent/request";
1
+ import { type AgentRunRequest, type SpawnAgent } from "@/chat/agent/request";
2
2
  import type { AgentRunOutcome } from "@/chat/runtime/agent-run-outcome";
3
3
  import type { SandboxEgressTracePropagationConfig } from "@/chat/sandbox/egress/tracing";
4
4
  /** Run one agent-run slice behind runtime-owned orchestration boundaries. */
@@ -7,5 +7,6 @@ export interface AgentRunner {
7
7
  }
8
8
  /** Adapt the Pi-facing agent-run executor behind the runtime-owned runner seam. */
9
9
  export declare function createAgentRunner(run: AgentRunner["run"], options?: {
10
+ bindSpawnAgent?: (request: AgentRunRequest) => SpawnAgent | undefined;
10
11
  tracePropagation?: SandboxEgressTracePropagationConfig;
11
12
  }): AgentRunner;
@@ -1,6 +1,5 @@
1
1
  export type AuthorizationPauseKind = "mcp" | "plugin";
2
2
  export type AuthorizationPauseDisposition = "link_already_sent" | "link_sent";
3
- export type AuthorizationFlowMode = "interactive" | "disabled";
4
3
  /**
5
4
  * Runtime-owned signal that the current turn must park until the user
6
5
  * completes an external authorization step.
@@ -9,7 +9,7 @@
9
9
  import type { OAuthClientProvider } from "@modelcontextprotocol/sdk/client/auth.js";
10
10
  import type { Destination, Source } from "@sentry/junior-plugin-api";
11
11
  import { type OAuthAuthorization } from "@/chat/oauth-authorization";
12
- import { AuthorizationPauseError, type AuthorizationFlowMode } from "@/chat/services/auth-pause";
12
+ import { AuthorizationPauseError } from "@/chat/services/auth-pause";
13
13
  import type { ThreadArtifactsState } from "@/chat/state/artifacts";
14
14
  import type { ConversationPendingAuthState } from "@/chat/state/conversation";
15
15
  import type { PluginDefinition } from "@/chat/plugins/types";
@@ -32,7 +32,8 @@ export interface McpAuthOrchestrationInput {
32
32
  getArtifactState: () => ThreadArtifactsState | undefined;
33
33
  getMergedArtifactState: () => ThreadArtifactsState;
34
34
  recordPendingAuth?: (pendingAuth: ConversationPendingAuthState | undefined) => void | Promise<void>;
35
- authorizationFlowMode?: AuthorizationFlowMode;
35
+ /** When false, required auth hard-fails instead of pausing for an OAuth link. */
36
+ interactiveAuthEnabled?: boolean;
36
37
  authorization?: OAuthAuthorization;
37
38
  }
38
39
  export interface McpAuthOrchestration {
@@ -12,7 +12,7 @@
12
12
  */
13
13
  import type { Destination, Source } from "@sentry/junior-plugin-api";
14
14
  import type { UserTokenStore } from "@/chat/credentials/user-token-store";
15
- import { AuthorizationPauseError, type AuthorizationFlowMode } from "@/chat/services/auth-pause";
15
+ import { AuthorizationPauseError } from "@/chat/services/auth-pause";
16
16
  import type { ConversationPendingAuthState } from "@/chat/state/conversation";
17
17
  import { type OAuthAuthorization } from "@/chat/oauth-authorization";
18
18
  export declare class PluginAuthorizationPauseError extends AuthorizationPauseError {
@@ -34,7 +34,8 @@ export interface PluginAuthOrchestrationInput {
34
34
  userMessage?: string;
35
35
  pendingAuth?: ConversationPendingAuthState;
36
36
  recordPendingAuth?: (pendingAuth: ConversationPendingAuthState) => void | Promise<void>;
37
- authorizationFlowMode?: AuthorizationFlowMode;
37
+ /** When false, required auth hard-fails instead of pausing for an OAuth link. */
38
+ interactiveAuthEnabled?: boolean;
38
39
  userTokenStore?: UserTokenStore;
39
40
  authorization?: OAuthAuthorization;
40
41
  }
@@ -26,12 +26,10 @@ export interface AgentTurnSessionRecord {
26
26
  conversationId: string;
27
27
  cumulativeDurationMs: number;
28
28
  cumulativeUsage?: AgentTurnUsage;
29
- destination?: Destination;
30
29
  dispatchId?: string;
31
30
  dispatchOutcome?: AgentDispatchOutcome;
32
31
  /** Provider-owned identifier returned after visible delivery is accepted. */
33
32
  resultMessageId?: string;
34
- source?: Source;
35
33
  errorMessage?: string;
36
34
  lastProgressAtMs: number;
37
35
  loadedSkillNames?: string[];
@@ -44,11 +42,9 @@ export interface AgentTurnSessionRecord {
44
42
  * All distinct actors annotated on this run's committed instruction-authority
45
43
  * messages, in first-seen order. Persisted as an attribution handle so a
46
44
  * summary-only handoff cannot erase them. It never grants authority or
47
- * replaces the singular execution actor.
45
+ * replaces the singular execution actor, which is rebuilt at resume.
48
46
  */
49
47
  actors: Actor[];
50
- /** The single actor this run executes as (credential binding, auth flows). */
51
- actor?: Actor;
52
48
  resumeReason?: AgentTurnResumeReason;
53
49
  resumedFromSliceId?: number;
54
50
  sessionId: string;
@@ -13,6 +13,7 @@ export declare const CONVERSATION_WORK_LEASE_TTL_MS = 90000;
13
13
  export declare const CONVERSATION_WORK_CHECK_IN_INTERVAL_MS = 15000;
14
14
  export declare const CONVERSATION_WORK_STALE_ENQUEUE_MS = 60000;
15
15
  export declare const CONVERSATION_WORK_MAX_DELIVERY_ATTEMPTS = 5;
16
+ export declare const CONVERSATION_WORK_MAX_RETRIES = 5;
16
17
  declare const inboundMessageSourceSchema: z.ZodEnum<{
17
18
  slack: "slack";
18
19
  plugin: "plugin";
@@ -46,14 +47,14 @@ export declare const inboundMessageSchema: z.ZodObject<{
46
47
  defer: "defer";
47
48
  interrupt: "interrupt";
48
49
  }>;
49
- destination: z.ZodDiscriminatedUnion<[z.ZodObject<{
50
+ destination: z.ZodOptional<z.ZodDiscriminatedUnion<[z.ZodObject<{
50
51
  platform: z.ZodLiteral<"slack">;
51
52
  teamId: z.ZodString;
52
53
  channelId: z.ZodString;
53
54
  }, z.core.$strict>, z.ZodObject<{
54
55
  platform: z.ZodLiteral<"local">;
55
56
  conversationId: z.ZodString;
56
- }, z.core.$strict>], "platform">;
57
+ }, z.core.$strict>], "platform">>;
57
58
  inboundMessageId: z.ZodString;
58
59
  injectedAtMs: z.ZodOptional<z.ZodNumber>;
59
60
  input: z.ZodObject<{
@@ -84,6 +85,8 @@ export interface ConversationExecution {
84
85
  inboundMessageIds: string[];
85
86
  lastCheckpointAtMs?: number;
86
87
  lastEnqueuedAtMs?: number;
88
+ lastProgressAtMs?: number;
89
+ retryCount?: number;
87
90
  lease?: Lease;
88
91
  pendingCount: number;
89
92
  pendingMessages: InboundMessage[];
@@ -163,7 +166,7 @@ export declare function appendInboundMessage(args: {
163
166
  /** Mark a conversation runnable when there is no new mailbox message. */
164
167
  export declare function requestConversationWork(args: {
165
168
  conversationId: string;
166
- destination: Destination;
169
+ destination?: Destination;
167
170
  nowMs?: number;
168
171
  state?: StateAdapter;
169
172
  }): Promise<RequestConversationWorkResult>;
@@ -251,7 +254,7 @@ export declare function ackMessages(args: {
251
254
  /** Mark the leased conversation as needing another queue-delivered slice. */
252
255
  export declare function requestConversationContinuation(args: {
253
256
  conversationId: string;
254
- destination: Destination;
257
+ destination?: Destination;
255
258
  leaseToken: string;
256
259
  nowMs?: number;
257
260
  state?: StateAdapter;
@@ -270,25 +273,34 @@ export declare function releaseConversationWork(args: {
270
273
  nowMs?: number;
271
274
  state?: StateAdapter;
272
275
  }): Promise<boolean>;
276
+ /** Count one failed execution attempt and stop the conversation when retries are exhausted. */
277
+ export declare function recordConversationRetry(args: {
278
+ conversationId: string;
279
+ leaseToken: string;
280
+ nowMs?: number;
281
+ state?: StateAdapter;
282
+ }): Promise<"lost_lease" | "recorded" | "stopped">;
273
283
  /** Finish a leased conversation and report whether runnable work remains. */
274
284
  export declare function completeConversationWork(args: {
275
285
  conversationId: string;
276
286
  leaseToken: string;
287
+ madeProgress?: boolean;
277
288
  nowMs?: number;
278
289
  state?: StateAdapter;
279
290
  }): Promise<"completed" | "lost_lease" | "pending">;
280
- /** Failure outcome: `lost_lease` (another owner took over), `recorded` (attempt counted), or `skipped` (durable progress was made). */
291
+ /** Failure outcome: `lost_lease` (another owner took over), `recorded` (retry counted), or `skipped` (durable progress was made). */
281
292
  export interface AttemptFailure {
282
293
  pendingCount: number;
283
294
  deadLetteredMessages: InboundMessage[];
295
+ retryCount: number;
284
296
  status: "lost_lease" | "recorded" | "skipped";
285
297
  }
286
298
  /**
287
- * Record one failed delivery attempt for the pending messages a run attempted,
288
- * dead-lettering messages that reach the retry limit so a deterministic
289
- * failure cannot requeue forever.
299
+ * Record one failed execution attempt for pending messages, dead-lettering
300
+ * the conversation when its retry budget or the legacy message delivery limit
301
+ * is exhausted.
290
302
  *
291
- * Attempts are counted only when the run made no durable progress: if any
303
+ * Retries are counted only when the run made no durable progress: if any
292
304
  * attempted message left the mailbox, the remaining pending entries may be
293
305
  * deliberate deferrals and are left untouched. Consumed message ids stay in
294
306
  * `inboundMessageIds` so source retries remain duplicates.
@@ -307,12 +319,12 @@ export declare function deadLetterAttempt(args: {
307
319
  nowMs?: number;
308
320
  state?: StateAdapter;
309
321
  }): Promise<"failed" | "lost_lease" | "pending">;
310
- /** Clear an expired durable lease so a later worker can resume safely. */
322
+ /** Recover an expired lease, consuming the retry budget under the mutation lock. */
311
323
  export declare function clearExpiredConversationLease(args: {
312
324
  conversationId: string;
313
325
  nowMs?: number;
314
326
  state?: StateAdapter;
315
- }): Promise<boolean>;
327
+ }): Promise<"not_expired" | "requeued" | "stopped">;
316
328
  /** Delete one conversation mailbox and all of its index entries. */
317
329
  export declare function deleteConversationState(args: {
318
330
  conversationId: string;
@@ -2,7 +2,7 @@ import type { StateAdapter } from "chat";
2
2
  import type { ConversationStore } from "@/chat/conversations/store";
3
3
  import type { ConversationWorkQueue } from "./queue";
4
4
  import * as workState from "./state";
5
- export { CONVERSATION_ACTIVE_INDEX_KEY, CONVERSATION_BY_ACTIVITY_INDEX_KEY, CONVERSATION_WORK_CHECK_IN_INTERVAL_MS, CONVERSATION_WORK_LEASE_TTL_MS, CONVERSATION_WORK_MAX_DELIVERY_ATTEMPTS, CONVERSATION_WORK_STALE_ENQUEUE_MS, isFinalAttempt, isInvalidConversationRecordError, type AgentInput, type AttemptFailure, type AppendAndEnqueueInboundMessageResult, type AppendInboundMessageResult, type Conversation, type ConversationExecution, type ConversationWorkLease, type ConversationWorkState, type ExecutionStatus, type InboundMessage, type InboundMessageDelivery, type Lease, type RequestConversationWorkResult, type Source, type StartConversationWorkAcquired, type StartConversationWorkActive, type StartConversationWorkNoWork, type StartConversationWorkResult, } from "@/chat/task-execution/state";
5
+ export { CONVERSATION_ACTIVE_INDEX_KEY, CONVERSATION_BY_ACTIVITY_INDEX_KEY, CONVERSATION_WORK_CHECK_IN_INTERVAL_MS, CONVERSATION_WORK_LEASE_TTL_MS, CONVERSATION_WORK_MAX_DELIVERY_ATTEMPTS, CONVERSATION_WORK_MAX_RETRIES, CONVERSATION_WORK_STALE_ENQUEUE_MS, isFinalAttempt, isInvalidConversationRecordError, type AgentInput, type AttemptFailure, type AppendAndEnqueueInboundMessageResult, type AppendInboundMessageResult, type Conversation, type ConversationExecution, type ConversationWorkLease, type ConversationWorkState, type ExecutionStatus, type InboundMessage, type InboundMessageDelivery, type Lease, type RequestConversationWorkResult, type Source, type StartConversationWorkAcquired, type StartConversationWorkActive, type StartConversationWorkNoWork, type StartConversationWorkResult, } from "@/chat/task-execution/state";
6
6
  import type { AppendAndEnqueueInboundMessageResult, Conversation, InboundMessage } from "@/chat/task-execution/state";
7
7
  export type EnsureConversationWakeResult = {
8
8
  queueMessageId?: string;
@@ -20,6 +20,14 @@ export declare function getConversationWorkState(args: {
20
20
  conversationId: string;
21
21
  state?: StateAdapter;
22
22
  }): Promise<workState.ConversationWorkState | undefined>;
23
+ /** Count one failed conversation execution and persist its terminal retry state. */
24
+ export declare function recordConversationRetry(args: {
25
+ conversationId: string;
26
+ conversationStore?: ConversationStore;
27
+ leaseToken: string;
28
+ nowMs?: number;
29
+ state?: StateAdapter;
30
+ }): Promise<"stopped" | "lost_lease" | "recorded">;
23
31
  /** Count mailbox messages that have not yet reached the conversation event log. */
24
32
  export declare function countPendingConversationMessages(conversation: Conversation): number;
25
33
  /** Return whether a conversation has pending or resumable execution work. */
@@ -98,14 +106,6 @@ export declare function drainConversationMailbox(args: Parameters<typeof workSta
98
106
  conversationId: string;
99
107
  createdAtMs: number;
100
108
  delivery: "defer" | "interrupt";
101
- destination: {
102
- platform: "slack";
103
- teamId: string;
104
- channelId: string;
105
- } | {
106
- platform: "local";
107
- conversationId: string;
108
- };
109
109
  inboundMessageId: string;
110
110
  input: {
111
111
  text: string;
@@ -116,6 +116,14 @@ export declare function drainConversationMailbox(args: Parameters<typeof workSta
116
116
  receivedAtMs: number;
117
117
  source: "slack" | "plugin" | "local" | "api" | "internal" | "resource_event" | "scheduler";
118
118
  attemptCount?: number | undefined;
119
+ destination?: {
120
+ platform: "slack";
121
+ teamId: string;
122
+ channelId: string;
123
+ } | {
124
+ platform: "local";
125
+ conversationId: string;
126
+ } | undefined;
119
127
  injectedAtMs?: number | undefined;
120
128
  }[]>;
121
129
  /** Acknowledge leased mailbox entries after the handler accepts responsibility. */
@@ -156,6 +164,7 @@ export declare function releaseConversationWork(args: {
156
164
  export declare function completeConversationWork(args: {
157
165
  conversationId: string;
158
166
  leaseToken: string;
167
+ madeProgress?: boolean;
159
168
  conversationStore?: ConversationStore;
160
169
  nowMs?: number;
161
170
  state?: StateAdapter;
@@ -177,13 +186,13 @@ export declare function deadLetterAttempt(args: {
177
186
  nowMs?: number;
178
187
  state?: StateAdapter;
179
188
  }): Promise<"pending" | "failed" | "lost_lease">;
180
- /** Clear an expired durable lease so a later worker can resume safely. */
189
+ /** Recover an expired lease and persist the resulting retry state. */
181
190
  export declare function clearExpiredConversationLease(args: {
182
191
  conversationId: string;
183
192
  conversationStore?: ConversationStore;
184
193
  nowMs?: number;
185
194
  state?: StateAdapter;
186
- }): Promise<boolean>;
195
+ }): Promise<"stopped" | "not_expired" | "requeued">;
187
196
  /** Remove one conversation from the active index after it is missing or idle. */
188
197
  export declare function removeActiveConversation(args: {
189
198
  conversationId: string;
@@ -8,13 +8,13 @@ export interface ConversationWorkerContext {
8
8
  attempt: InboxAttempt;
9
9
  checkIn(): Promise<boolean>;
10
10
  conversationId: string;
11
- destination: Destination;
11
+ destination?: Destination;
12
12
  shouldYield(): boolean;
13
13
  }
14
14
  export interface InboxAttempt {
15
15
  ack(): Promise<void>;
16
16
  conversationId: string;
17
- destination: Destination;
17
+ destination?: Destination;
18
18
  drain(handle: (messages: InboundMessage[]) => Promise<readonly string[] | void>): Promise<InboundMessage[]>;
19
19
  isFinalAttempt: boolean;
20
20
  messages: InboundMessage[];
@@ -1,4 +1,6 @@
1
+ import { type TaskExecutionStatus } from "@/db/schema/task-executions";
1
2
  export declare const TASK_EXECUTION_TYPES: readonly ["scheduled", "event"];
3
+ export declare const TASK_EXECUTION_STATUSES: readonly ["blocked", "completed", "failed"];
2
4
  export type TaskExecutionType = (typeof TASK_EXECUTION_TYPES)[number];
3
5
  export type TaskExecutionDay = {
4
6
  date: string;
@@ -11,18 +13,47 @@ export type TaskExecutionSummary = {
11
13
  runsLast7Days: number;
12
14
  totalRuns: number;
13
15
  };
14
- /** Record one successful task execution and its durable conversation. */
16
+ export type TaskExecutionRecord = {
17
+ conversationId?: string;
18
+ executedAt: string;
19
+ executionId: string;
20
+ status: TaskExecutionStatus;
21
+ title?: string;
22
+ };
23
+ export type TaskExecutionStatusDay = {
24
+ blocked: number;
25
+ completed: number;
26
+ date: string;
27
+ failed: number;
28
+ };
29
+ /** Record one terminal task execution and its durable conversation when present. */
15
30
  export declare function recordTaskExecution(type: TaskExecutionType, taskId: string, options: {
16
- conversationId: string;
31
+ conversationId?: string;
17
32
  executionId: string;
18
33
  namespace?: string;
19
34
  nowMs?: number;
35
+ status?: TaskExecutionStatus;
20
36
  }): Promise<void>;
21
37
  /** Load usage analytics for all tasks of one type and namespace. */
22
38
  export declare function readTaskExecutionSummaries(type: TaskExecutionType, namespace: string, options?: {
23
39
  nowMs?: number;
24
40
  }): Promise<Map<string, TaskExecutionSummary>>;
25
- /** Load a fixed trailing window of successful executions stacked by task type. */
41
+ /** Load a fixed trailing window of completed executions stacked by task type. */
26
42
  export declare function readTaskExecutionDays(dayCount?: number, options?: {
27
43
  nowMs?: number;
28
44
  }): Promise<TaskExecutionDay[]>;
45
+ /** Load newest-first executions for one task, with conversation titles when present. */
46
+ export declare function readTaskExecutions(args: {
47
+ kind: TaskExecutionType;
48
+ limit: number;
49
+ namespace?: string;
50
+ taskId: string;
51
+ }): Promise<TaskExecutionRecord[]>;
52
+ /** Load a fixed trailing window of terminal executions for one task by status. */
53
+ export declare function readTaskExecutionStatusDays(args: {
54
+ dayCount?: number;
55
+ kind: TaskExecutionType;
56
+ namespace?: string;
57
+ nowMs?: number;
58
+ taskId: string;
59
+ }): Promise<TaskExecutionStatusDay[]>;
@@ -1,9 +1,11 @@
1
1
  import type { User } from "@sentry/junior-plugin-api";
2
- import type { TaskList } from "@/api/schema/task";
2
+ import type { TaskExecutionList, TaskList } from "@/api/schema/task";
3
3
  /** Read viewer-owned and public-workspace tasks as one bounded newest-first projection. */
4
4
  export declare function readViewerTasks(user: User): Promise<TaskList>;
5
5
  export declare class ViewerTaskNotFoundError extends Error {
6
6
  constructor();
7
7
  }
8
+ /** Read one viewer-visible task and its newest terminal executions. */
9
+ export declare function readViewerTaskExecutions(user: User, kind: "scheduled" | "event", id: string): Promise<TaskExecutionList>;
8
10
  /** Delete one viewer-owned scheduled or event task. */
9
11
  export declare function deleteViewerTask(user: User, kind: "scheduled" | "event", id: string): Promise<void>;
@@ -0,0 +1,31 @@
1
+ import type { ToolRuntimeContext } from "@/chat/tools/types";
2
+ export declare const SPAWN_AGENT_TOOL_NAME = "spawnAgent";
3
+ /** Create the asynchronous child-agent tool for one parent run. */
4
+ export declare function createSpawnAgentTool(spawnAgent: NonNullable<ToolRuntimeContext["spawnAgent"]>): Omit<import("../definition").AnyToolDefinition, "inputSchema" | "outputSchema" | "prepareArguments" | "execute"> & {
5
+ inputSchema: import("../definition").JsonSchemaObject;
6
+ outputSchema: import("../definition").JsonSchemaObject;
7
+ prepareArguments(args: unknown): {
8
+ task: string;
9
+ name?: string | null | undefined;
10
+ reasoning_level?: "low" | "medium" | "high" | "xhigh" | "none" | null | undefined;
11
+ };
12
+ execute?: ((input: unknown, options: import("../definition").ToolExecuteOptions) => {
13
+ [x: string]: unknown;
14
+ invocation_id: string;
15
+ target?: string | undefined;
16
+ truncated?: boolean | undefined;
17
+ continuation?: {
18
+ arguments: Record<string, unknown>;
19
+ reason?: string | undefined;
20
+ } | undefined;
21
+ } | Promise<{
22
+ [x: string]: unknown;
23
+ invocation_id: string;
24
+ target?: string | undefined;
25
+ truncated?: boolean | undefined;
26
+ continuation?: {
27
+ arguments: Record<string, unknown>;
28
+ reason?: string | undefined;
29
+ } | undefined;
30
+ }>) | undefined;
31
+ };
@@ -11,6 +11,7 @@ import type { LocalActor, Actor, SlackActor } from "@/chat/actor";
11
11
  import type { SlackActionToken } from "@/chat/slack/action-token";
12
12
  import type { ModelProfile } from "@/chat/model-profile";
13
13
  import type { GeneratedArtifactFileRef } from "@/chat/tools/sandbox/file-uploads";
14
+ import type { SpawnAgent } from "@/chat/agent/request";
14
15
  interface HandoffControl {
15
16
  /** Non-empty catalog of configured targets. */
16
17
  profiles: readonly [ModelProfile, ...ModelProfile[]];
@@ -55,6 +56,7 @@ export interface ToolHooks {
55
56
  }
56
57
  interface BaseToolRuntimeContext {
57
58
  handoff?: HandoffControl;
59
+ spawnAgent?: SpawnAgent;
58
60
  /**
59
61
  * Opaque Junior conversation/session identity for this turn.
60
62
  * Interactive Slack turns use `slack:{channelId}:{threadTs}`.
@@ -9,7 +9,7 @@ import {
9
9
  } from "./chunk-6GWA276C.js";
10
10
  import {
11
11
  getDeploymentTelemetryAttributes
12
- } from "./chunk-5PNJMOY5.js";
12
+ } from "./chunk-C2N6MNO4.js";
13
13
 
14
14
  // src/chat/logging.ts
15
15
  import path from "path";