@sentry/junior 0.71.3 → 0.73.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/bin/junior.mjs +22 -10
  2. package/dist/api-reference.d.ts +2 -0
  3. package/dist/app.d.ts +12 -3
  4. package/dist/app.js +2522 -15560
  5. package/dist/chat/agent-dispatch/heartbeat.d.ts +0 -6
  6. package/dist/chat/agent-dispatch/runner.d.ts +2 -0
  7. package/dist/chat/agent-dispatch/store.d.ts +2 -2
  8. package/dist/chat/agent-dispatch/types.d.ts +6 -3
  9. package/dist/chat/agent-dispatch/validation.d.ts +2 -3
  10. package/dist/chat/app/production.d.ts +8 -1
  11. package/dist/chat/app/services.d.ts +7 -0
  12. package/dist/chat/destination.d.ts +3 -1
  13. package/dist/chat/logging.d.ts +3 -0
  14. package/dist/chat/mcp/errors.d.ts +3 -0
  15. package/dist/chat/mcp/tool-manager.d.ts +5 -1
  16. package/dist/chat/oauth-flow.d.ts +1 -1
  17. package/dist/chat/plugins/agent-hooks.d.ts +3 -2
  18. package/dist/chat/plugins/registry.d.ts +2 -0
  19. package/dist/chat/plugins/types.d.ts +2 -0
  20. package/dist/chat/prompt.d.ts +4 -1
  21. package/dist/chat/requester.d.ts +67 -0
  22. package/dist/chat/respond.d.ts +8 -8
  23. package/dist/chat/runtime/agent-continue-runner.d.ts +25 -0
  24. package/dist/chat/runtime/reply-executor.d.ts +7 -5
  25. package/dist/chat/runtime/slack-resume.d.ts +3 -3
  26. package/dist/chat/runtime/slack-runtime.d.ts +13 -3
  27. package/dist/chat/runtime/turn.d.ts +17 -3
  28. package/dist/chat/sandbox/egress-credentials.d.ts +5 -2
  29. package/dist/chat/sandbox/egress-policy.d.ts +5 -1
  30. package/dist/chat/sandbox/egress-proxy.d.ts +2 -0
  31. package/dist/chat/sandbox/egress-schemas.d.ts +4 -0
  32. package/dist/chat/sandbox/egress-session.d.ts +3 -1
  33. package/dist/chat/sandbox/egress-tracing.d.ts +7 -0
  34. package/dist/chat/sandbox/sandbox.d.ts +2 -0
  35. package/dist/chat/sandbox/session.d.ts +3 -2
  36. package/dist/chat/services/agent-continue.d.ts +27 -0
  37. package/dist/chat/services/auth-pause-response.d.ts +1 -1
  38. package/dist/chat/services/auth-pause.d.ts +2 -1
  39. package/dist/chat/services/mcp-auth-orchestration.d.ts +1 -1
  40. package/dist/chat/services/message-actor-identity.d.ts +12 -4
  41. package/dist/chat/services/plugin-auth-orchestration.d.ts +9 -8
  42. package/dist/chat/services/turn-result.d.ts +3 -0
  43. package/dist/chat/services/turn-session-record.d.ts +10 -7
  44. package/dist/chat/slack/user.d.ts +4 -4
  45. package/dist/chat/state/adapter.d.ts +2 -0
  46. package/dist/chat/state/conversation-details.d.ts +4 -3
  47. package/dist/chat/state/session-log.d.ts +43 -0
  48. package/dist/chat/state/turn-session.d.ts +7 -10
  49. package/dist/chat/task-execution/slack-work.d.ts +5 -5
  50. package/dist/chat/task-execution/store.d.ts +83 -48
  51. package/dist/chat/task-execution/worker.d.ts +3 -3
  52. package/dist/chat/tools/definition.d.ts +3 -0
  53. package/dist/chat/tools/execution/tool-error-handler.d.ts +2 -1
  54. package/dist/chat/tools/slack/canvas-tools.d.ts +3 -2
  55. package/dist/chat/tools/slack/channel-list-messages.d.ts +2 -2
  56. package/dist/chat/tools/slack/channel-post-message.d.ts +3 -2
  57. package/dist/chat/tools/slack/context.d.ts +15 -2
  58. package/dist/chat/tools/slack/message-add-reaction.d.ts +3 -2
  59. package/dist/chat/tools/slack/thread-read.d.ts +2 -2
  60. package/dist/chat/tools/types.d.ts +20 -23
  61. package/dist/{chunk-BBXYXOJW.js → chunk-3BYAPS6B.js} +48 -529
  62. package/dist/{chunk-UXG6TU2U.js → chunk-7Q5YOUUT.js} +16 -93
  63. package/dist/chunk-AL5T52ZD.js +1119 -0
  64. package/dist/chunk-CYUI7JU5.js +195 -0
  65. package/dist/{chunk-R62YWUNO.js → chunk-DIMX5F3T.js} +10 -28
  66. package/dist/chunk-G3E7SCME.js +28 -0
  67. package/dist/chunk-KVZL5NZS.js +519 -0
  68. package/dist/chunk-M4FLLXXD.js +212 -0
  69. package/dist/chunk-OQSYYOLM.js +12787 -0
  70. package/dist/{chunk-GT67ZWZQ.js → chunk-OR6NQJ5E.js} +5 -3
  71. package/dist/{chunk-B5HKWWQB.js → chunk-RY6AL5C7.js} +8 -6
  72. package/dist/chunk-SJHUF3DP.js +43 -0
  73. package/dist/{chunk-XE2VFQQN.js → chunk-UOTZ3EEQ.js} +1 -1
  74. package/dist/{chunk-HOGQL2H6.js → chunk-UZVHXZ7V.js} +1357 -1486
  75. package/dist/{chunk-76YMBKW7.js → chunk-V4VYUY4A.js} +27 -14
  76. package/dist/{chunk-JS4HURDT.js → chunk-WS2EG3GW.js} +224 -224
  77. package/dist/chunk-ZDA2HYX5.js +275 -0
  78. package/dist/cli/chat.js +205 -0
  79. package/dist/cli/check.js +6 -5
  80. package/dist/cli/run.js +18 -2
  81. package/dist/cli/snapshot-warmup.js +11 -8
  82. package/dist/cli/upgrade.js +599 -0
  83. package/dist/deployment.d.ts +4 -0
  84. package/dist/handlers/agent-dispatch.d.ts +6 -1
  85. package/dist/handlers/mcp-oauth-callback.d.ts +6 -1
  86. package/dist/handlers/oauth-callback.d.ts +6 -1
  87. package/dist/handlers/sandbox-egress-proxy.d.ts +2 -0
  88. package/dist/handlers/webhooks.d.ts +4 -2
  89. package/dist/instrumentation.js +17 -2
  90. package/dist/nitro.d.ts +1 -1
  91. package/dist/nitro.js +10 -10
  92. package/dist/plugins.d.ts +1 -1
  93. package/dist/reporting/conversations.d.ts +116 -0
  94. package/dist/reporting.d.ts +24 -129
  95. package/dist/reporting.js +320 -166
  96. package/dist/runner-LMAM4OGD.js +259 -0
  97. package/package.json +3 -3
  98. package/dist/chat/runtime/timeout-resume-runner.d.ts +0 -19
  99. package/dist/chat/services/requester-identity.d.ts +0 -19
  100. package/dist/chat/services/timeout-resume.d.ts +0 -23
  101. package/dist/chunk-6YY4Q3D4.js +0 -12
  102. package/dist/chunk-Z3YD6NHK.js +0 -12
  103. package/dist/handlers/turn-resume.d.ts +0 -4
@@ -1,10 +1,4 @@
1
1
  import type { ConversationWorkQueue } from "@/chat/task-execution/queue";
2
- /** Re-drive stale turn continuations whose internal callback vanished. */
3
- export declare function recoverStaleTimeoutResumes(args: {
4
- conversationWorkQueue?: ConversationWorkQueue;
5
- limit?: number;
6
- nowMs: number;
7
- }): Promise<number>;
8
2
  /** Re-drive stale core dispatches before invoking plugin heartbeat hooks. */
9
3
  export declare function recoverStaleDispatches(args: {
10
4
  limit?: number;
@@ -1,9 +1,11 @@
1
1
  import { generateAssistantReply as generateAssistantReplyImpl } from "@/chat/respond";
2
+ import type { SandboxEgressTracePropagationConfig } from "@/chat/sandbox/egress-tracing";
2
3
  import { scheduleDispatchCallback } from "./signing";
3
4
  import type { DispatchCallback } from "./types";
4
5
  export interface AgentDispatchRunnerDeps {
5
6
  generateAssistantReply?: typeof generateAssistantReplyImpl;
6
7
  scheduleCallback?: typeof scheduleDispatchCallback;
8
+ tracePropagation?: SandboxEgressTracePropagationConfig;
7
9
  }
8
10
  /** Run one serverless slice for a core-owned agent dispatch. */
9
11
  export declare function runAgentDispatchSlice(callback: DispatchCallback, deps?: AgentDispatchRunnerDeps): Promise<void>;
@@ -1,12 +1,12 @@
1
1
  import type { StateAdapter } from "chat";
2
- import { type Destination } from "@sentry/junior-plugin-api";
2
+ import { type SlackDestination } from "@sentry/junior-plugin-api";
3
3
  import type { BoundDispatchOptions, DispatchCreateResult, DispatchProjection, DispatchRecord, DispatchStatus } from "./types";
4
4
  /** Keep dispatch persistence keys consistent across callback and recovery paths. */
5
5
  export declare function getDispatchStorageKey(id: string): string;
6
6
  /** Parse persisted dispatch records before recovery, callbacks, or projections use them. */
7
7
  export declare function parseDispatchRecord(value: unknown): DispatchRecord | undefined;
8
8
  /** Map a dispatch destination to the lock key that serializes Slack delivery. */
9
- export declare function getDispatchDestinationLockId(destination: Destination): string;
9
+ export declare function getDispatchDestinationLockId(destination: SlackDestination): string;
10
10
  /** Return the isolated persisted conversation key for one dispatch run. */
11
11
  export declare function getDispatchConversationId(dispatch: Pick<DispatchRecord, "id">): string;
12
12
  /** Give dispatch slices stable turn ids for resumability and trace correlation. */
@@ -1,7 +1,10 @@
1
- import type { Destination, DispatchOptions } from "@sentry/junior-plugin-api";
1
+ import type { DispatchOptions, SlackDestination } from "@sentry/junior-plugin-api";
2
2
  import type { CredentialSubject, CredentialSystemActor } from "@/chat/credentials/context";
3
3
  export type DispatchStatus = "pending" | "running" | "awaiting_resume" | "completed" | "failed" | "blocked";
4
- export interface BoundDispatchOptions extends Omit<DispatchOptions, "credentialSubject"> {
4
+ export type SlackDispatchOptions = Omit<DispatchOptions, "destination"> & {
5
+ destination: SlackDestination;
6
+ };
7
+ export interface BoundDispatchOptions extends Omit<SlackDispatchOptions, "credentialSubject"> {
5
8
  credentialSubject?: CredentialSubject;
6
9
  }
7
10
  export interface DispatchRecord {
@@ -9,7 +12,7 @@ export interface DispatchRecord {
9
12
  attempt: number;
10
13
  createdAtMs: number;
11
14
  credentialSubject?: CredentialSubject;
12
- destination: Destination;
15
+ destination: SlackDestination;
13
16
  errorMessage?: string;
14
17
  id: string;
15
18
  idempotencyKey: string;
@@ -1,6 +1,5 @@
1
- import { type DispatchOptions } from "@sentry/junior-plugin-api";
2
- import type { BoundDispatchOptions } from "./types";
1
+ import type { BoundDispatchOptions, SlackDispatchOptions } from "./types";
3
2
  /** Validate plugin-provided dispatch options before core persists them. */
4
- export declare function validateDispatchOptions(options: unknown): asserts options is DispatchOptions;
3
+ export declare function validateDispatchOptions(options: unknown): asserts options is SlackDispatchOptions;
5
4
  /** Verify runtime-owned access requirements for delegated dispatch credentials. */
6
5
  export declare function verifyDispatchCredentialSubjectAccess(options: BoundDispatchOptions): Promise<void>;
@@ -2,11 +2,18 @@ import type { SlackAdapter } from "@chat-adapter/slack";
2
2
  import { createSlackRuntime } from "@/chat/app/factory";
3
3
  import type { SlackWebhookServices } from "@/chat/ingress/slack-webhook";
4
4
  import type { VercelConversationWorkCallbackOptions } from "@/chat/task-execution/vercel-callback";
5
+ import type { JuniorRuntimeServiceOverrides } from "@/chat/app/services";
5
6
  /** Return the lazily initialized production Slack adapter. */
6
7
  export declare function getProductionSlackAdapter(): SlackAdapter;
7
8
  /** Return the lazily initialized production Slack runtime. */
8
9
  export declare function getProductionSlackRuntime(): ReturnType<typeof createSlackRuntime>;
10
+ /** Create production-backed services for Slack webhook ingress. */
11
+ export declare function createProductionSlackWebhookServices(options?: {
12
+ services?: JuniorRuntimeServiceOverrides;
13
+ }): SlackWebhookServices;
9
14
  /** Return production services for Slack webhook ingress. */
10
15
  export declare function getProductionSlackWebhookServices(): SlackWebhookServices;
11
16
  /** Return the production queue callback options for conversation work. */
12
- export declare function getProductionConversationWorkOptions(): VercelConversationWorkCallbackOptions;
17
+ export declare function createProductionConversationWorkOptions(options?: {
18
+ services?: JuniorRuntimeServiceOverrides;
19
+ }): VercelConversationWorkCallbackOptions;
@@ -1,3 +1,5 @@
1
+ import { generateAssistantReply as generateAssistantReplyImpl } from "@/chat/respond";
2
+ import type { SandboxEgressTracePropagationConfig } from "@/chat/sandbox/egress-tracing";
1
3
  import { type ConversationMemoryDeps, type ConversationMemoryService } from "@/chat/services/conversation-memory";
2
4
  import { type ContextCompactor, type ContextCompactorDeps } from "@/chat/services/context-compaction";
3
5
  import { type SubscribedReplyPolicy, type SubscribedReplyPolicyDeps } from "@/chat/services/subscribed-reply-policy";
@@ -15,6 +17,11 @@ export interface JuniorRuntimeServiceOverrides {
15
17
  contextCompactor?: Partial<ContextCompactorDeps>;
16
18
  replyExecutor?: Partial<Omit<ReplyExecutorServices, "generateThreadTitle">>;
17
19
  subscribedReplyPolicy?: Partial<SubscribedReplyPolicyDeps>;
20
+ sandbox?: {
21
+ tracePropagation?: SandboxEgressTracePropagationConfig;
22
+ };
18
23
  visionContext?: Partial<VisionContextDeps>;
19
24
  }
25
+ /** Apply app-owned sandbox egress trace config unless a turn overrides it. */
26
+ export declare function withSandboxTracePropagation(generateReply: typeof generateAssistantReplyImpl, tracePropagation?: SandboxEgressTracePropagationConfig): typeof generateAssistantReplyImpl;
20
27
  export declare function createJuniorRuntimeServices(overrides?: JuniorRuntimeServiceOverrides): JuniorRuntimeServices;
@@ -1,4 +1,4 @@
1
- import { type Destination } from "@sentry/junior-plugin-api";
1
+ import { type Destination, type SlackDestination } from "@sentry/junior-plugin-api";
2
2
  /** Build Junior's canonical destination from Slack workspace and channel ids. */
3
3
  export declare function createSlackDestination(input: {
4
4
  channelId: string | undefined;
@@ -6,6 +6,8 @@ export declare function createSlackDestination(input: {
6
6
  }): Destination | undefined;
7
7
  /** Parse and validate a serialized destination that crossed a runtime boundary. */
8
8
  export declare function parseDestination(value: unknown): Destination | undefined;
9
+ /** Require a Slack destination at a Slack-only runtime boundary. */
10
+ export declare function requireSlackDestination(destination: Destination | undefined, action: string): SlackDestination;
9
11
  /** Compare two destinations without relying on object identity. */
10
12
  export declare function sameDestination(left: Destination, right: Destination): boolean;
11
13
  /** Return the lock/index-safe storage key for a destination. */
@@ -31,6 +31,7 @@ export interface LogContext {
31
31
  urlFull?: string;
32
32
  userAgent?: string;
33
33
  }
34
+ export type TracePropagationHeaders = Partial<Record<"baggage" | "sentry-trace" | "traceparent", string>>;
34
35
  interface SentryUserIdentity {
35
36
  id: string | number;
36
37
  email?: string;
@@ -77,6 +78,8 @@ export declare function createRequestContext(request: Request, context?: Partial
77
78
  export declare function withContext<T>(context: LogContext, callback: () => Promise<T>): Promise<T>;
78
79
  /** Run a callback within a Sentry span and scoped log context. */
79
80
  export declare function withSpan<T>(name: string, op: string, context: LogContext, callback: () => Promise<T>, attributes?: Record<string, unknown>): Promise<T>;
81
+ /** Return Sentry-standard trace propagation headers for the active span. */
82
+ export declare function getTracePropagationHeaders(): TracePropagationHeaders;
80
83
  /** Set attributes on the currently active Sentry span. */
81
84
  export declare function setSpanAttributes(attributes: Record<string, unknown>): void;
82
85
  /** Set the status of the currently active Sentry span. */
@@ -1,3 +1,4 @@
1
+ import type { ConversationPrivacy } from "@/chat/conversation-privacy";
1
2
  /** Thrown when an MCP failure should be returned as a model-visible tool error. */
2
3
  export declare class McpToolError extends Error {
3
4
  constructor(message: string);
@@ -6,3 +7,5 @@ export declare class McpToolError extends Error {
6
7
  export declare function getMcpAwareErrorType(error: unknown, fallback: string): string;
7
8
  /** Return the display-safe error message for MCP-aware tool failures. */
8
9
  export declare function getMcpAwareErrorMessage(error: unknown): string;
10
+ /** Return an error message safe for logs and span attributes. */
11
+ export declare function getMcpAwareTelemetryMessage(error: unknown, privacy: ConversationPrivacy | undefined): string;
@@ -8,6 +8,7 @@
8
8
  */
9
9
  import type { ImageContent, TextContent } from "@earendil-works/pi-ai";
10
10
  import type { OAuthClientProvider } from "@modelcontextprotocol/sdk/client/auth.js";
11
+ import { type ConversationPrivacy } from "@/chat/conversation-privacy";
11
12
  import type { SkillMetadata } from "@/chat/skills";
12
13
  import type { PluginDefinition } from "@/chat/plugins/types";
13
14
  import { McpAuthorizationRequiredError, type PluginMcpToolCallResult } from "./client";
@@ -36,7 +37,10 @@ export interface ManagedMcpToolDescriptor {
36
37
  }
37
38
  type ActiveMcpSkill = Pick<SkillMetadata, "name" | "pluginProvider">;
38
39
  export interface ManagedMcpTool extends ManagedMcpToolDescriptor {
39
- execute: (args: Record<string, unknown>) => Promise<ManagedMcpToolResult>;
40
+ execute: (args: Record<string, unknown>, options?: {
41
+ conversationPrivacy?: ConversationPrivacy;
42
+ toolCallId?: string;
43
+ }) => Promise<ManagedMcpToolResult>;
40
44
  }
41
45
  export declare class McpToolManager {
42
46
  private readonly options;
@@ -27,7 +27,7 @@ type OAuthFlowInput = {
27
27
  };
28
28
  /** Parse OAuth callback state that was persisted before a provider redirect. */
29
29
  export declare function parseOAuthStatePayload(value: unknown): OAuthStatePayload | undefined;
30
- /** Capitalize the first letter of a provider name for display. */
30
+ /** Return the manifest-owned display label for a provider. */
31
31
  export declare function formatProviderLabel(provider: string): string;
32
32
  /** Resolve the public base URL from environment variables (JUNIOR_BASE_URL or Vercel). */
33
33
  export declare function resolveBaseUrl(): string | undefined;
@@ -1,7 +1,8 @@
1
- import type { AgentPluginRequester, AgentPluginRoute, PluginOperationalReport, SlackConversationLink, JuniorPluginRegistration } from "@sentry/junior-plugin-api";
1
+ import type { AgentPluginRoute, PluginOperationalReport, SlackConversationLink, JuniorPluginRegistration } from "@sentry/junior-plugin-api";
2
2
  import type { ToolDefinition } from "@/chat/tools/definition";
3
3
  import type { ToolRuntimeContext } from "@/chat/tools/types";
4
4
  import type { SandboxInstance } from "@/chat/sandbox/workspace";
5
+ import type { Requester } from "@/chat/requester";
5
6
  /** Signal that a plugin intentionally denied a tool execution. */
6
7
  export declare class AgentPluginHookDeniedError extends Error {
7
8
  constructor(message: string);
@@ -37,5 +38,5 @@ export declare function getAgentPluginSlackConversationLink(conversationId: stri
37
38
  export declare function getAgentPluginOperationalReports(nowMs?: number): Promise<PluginOperationalReport[]>;
38
39
  /** Create one runner over runtime hook plugins registered by the app. */
39
40
  export declare function createAgentPluginHookRunner(input?: {
40
- requester?: AgentPluginRequester;
41
+ requester?: Requester;
41
42
  }): AgentPluginHookRunner;
@@ -17,6 +17,8 @@ export declare function getPluginOAuthConfig(provider: string): OAuthProviderCon
17
17
  export declare function getPluginSkillRoots(): string[];
18
18
  export declare function getPluginForSkillPath(skillPath: string): PluginDefinition | undefined;
19
19
  export declare function getPluginDefinition(provider: string): PluginDefinition | undefined;
20
+ /** Return the human-facing provider label from the plugin manifest. */
21
+ export declare function getPluginDisplayName(provider: string): string | undefined;
20
22
  export declare function isPluginProvider(provider: string): boolean;
21
23
  export declare function isPluginCapability(capability: string): boolean;
22
24
  export declare function isPluginConfigKey(key: string): boolean;
@@ -61,6 +61,7 @@ export interface PluginEnvVarDeclaration {
61
61
  }
62
62
  export interface PluginManifest {
63
63
  name: string;
64
+ displayName: string;
64
65
  description: string;
65
66
  capabilities: string[];
66
67
  configKeys: string[];
@@ -106,6 +107,7 @@ type PluginCredentialConfig = PluginCredentialConfigBase & {
106
107
  };
107
108
  /** Install-level changes applied to one plugin manifest before validation. */
108
109
  export interface PluginManifestConfig {
110
+ displayName?: string;
109
111
  description?: string;
110
112
  capabilities?: string[];
111
113
  configKeys?: string[];
@@ -2,6 +2,7 @@ import type { SlackConversationContext } from "@/chat/slack/conversation-context
2
2
  import type { ThreadArtifactsState } from "@/chat/state/artifacts";
3
3
  import type { SkillMetadata, SkillInvocation } from "@/chat/skills";
4
4
  import type { ActiveMcpCatalogSummary } from "@/chat/tools/skill/mcp-tool-summary";
5
+ import type { Source } from "@sentry/junior-plugin-api";
5
6
  export declare const JUNIOR_PERSONALITY: string;
6
7
  export declare const JUNIOR_WORLD: string | null;
7
8
  interface ToolPromptContext {
@@ -28,7 +29,9 @@ type TurnContextPromptInput = {
28
29
  configuration?: Record<string, unknown>;
29
30
  };
30
31
  /** Return byte-stable platform instructions shared by every conversation and turn. */
31
- export declare function buildSystemPrompt(): string;
32
+ export declare function buildSystemPrompt(params: {
33
+ source: Source;
34
+ }): string;
32
35
  /** Build volatile runtime context that belongs in the user turn, not the system prompt. */
33
36
  export declare function buildTurnContextPrompt(params: TurnContextPromptInput): string | null;
34
37
  export {};
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Canonical requester identity.
3
+ *
4
+ * Runtime requesters are platform-scoped actors. Stored Slack requester parsing
5
+ * remains explicit so legacy durable records can resume without repairing
6
+ * malformed team or user ids.
7
+ */
8
+ import { z } from "zod";
9
+ export declare const storedSlackRequesterSchema: z.ZodObject<{
10
+ email: z.ZodOptional<z.ZodString>;
11
+ fullName: z.ZodOptional<z.ZodString>;
12
+ platform: z.ZodOptional<z.ZodLiteral<"slack">>;
13
+ slackUserId: z.ZodOptional<z.ZodString>;
14
+ slackUserName: z.ZodOptional<z.ZodString>;
15
+ teamId: z.ZodOptional<z.ZodString>;
16
+ }, z.core.$strict>;
17
+ interface BaseRequester {
18
+ email?: string;
19
+ fullName?: string;
20
+ userId: string;
21
+ userName?: string;
22
+ }
23
+ export interface SlackRequester extends BaseRequester {
24
+ platform: "slack";
25
+ teamId: string;
26
+ }
27
+ export interface LocalRequester extends BaseRequester {
28
+ platform: "local";
29
+ }
30
+ export type Requester = SlackRequester | LocalRequester;
31
+ export interface SlackRequesterProfile {
32
+ email?: string;
33
+ fullName?: string;
34
+ userName?: string;
35
+ }
36
+ export type StoredSlackRequester = z.output<typeof storedSlackRequesterSchema>;
37
+ interface RequesterInput {
38
+ email?: string;
39
+ fullName?: string;
40
+ platform?: Requester["platform"];
41
+ teamId?: string;
42
+ userId?: string;
43
+ userName?: string;
44
+ }
45
+ /** Keep actor ids exact at platform boundaries before they enter owned state. */
46
+ export declare function parseActorUserId(value: unknown): string | undefined;
47
+ /** Assert persisted actor ids without read-side repair. */
48
+ export declare function isActorUserId(value: string | undefined): value is string;
49
+ /** Build Junior's canonical platform requester from exact actor ids and profile data. */
50
+ export declare function createRequester(input: RequesterInput | undefined, context: {
51
+ platform?: Requester["platform"];
52
+ teamId?: string;
53
+ userId?: string;
54
+ }): Requester | undefined;
55
+ /** Build Junior's canonical requester from Slack profile data. */
56
+ export declare function createSlackRequester(teamId: string, userId: string, profile: SlackRequesterProfile | null | undefined): SlackRequester;
57
+ /** Parse a serialized Slack requester that crossed a runtime boundary. */
58
+ export declare function parseStoredSlackRequester(value: unknown): StoredSlackRequester | undefined;
59
+ /** Convert a runtime Slack requester into its durable session shape. */
60
+ export declare function toStoredSlackRequester(requester: SlackRequester): StoredSlackRequester;
61
+ /** Rebuild a runtime requester from durable Slack requester state. */
62
+ export declare function createRequesterFromStoredSlackRequester(args: {
63
+ requester?: StoredSlackRequester;
64
+ teamId: string;
65
+ userId: string;
66
+ }): SlackRequester;
67
+ export {};
@@ -5,24 +5,21 @@ import type { ConversationPendingAuthState } from "@/chat/state/conversation";
5
5
  import type { ImageGenerateToolDeps, WebFetchToolDeps, WebSearchToolDeps } from "@/chat/tools/types";
6
6
  import type { PiMessage } from "@/chat/pi/messages";
7
7
  import { type SandboxAcquiredState } from "@/chat/sandbox/sandbox";
8
+ import type { SandboxEgressTracePropagationConfig } from "@/chat/sandbox/egress-tracing";
8
9
  import type { AssistantStatusSpec } from "@/chat/slack/assistant-thread/status";
9
10
  import type { SlackConversationContext } from "@/chat/slack/conversation-context";
10
11
  import { type AssistantReply, type AgentTurnDiagnostics } from "@/chat/services/turn-result";
11
12
  import type { AgentTurnSurface } from "@/chat/state/turn-session";
12
13
  import type { CredentialContext } from "@/chat/credentials/context";
14
+ import { type Requester } from "@/chat/requester";
13
15
  import { type AuthorizationFlowMode } from "@/chat/services/auth-pause";
14
16
  export type { AssistantReply, AgentTurnDiagnostics };
15
17
  export interface ReplyRequestContext {
16
18
  skillDirs?: string[];
17
19
  credentialContext?: CredentialContext;
18
- requester?: {
19
- userId?: string;
20
- userName?: string;
21
- fullName?: string;
22
- email?: string;
23
- };
20
+ requester?: Requester;
24
21
  slackConversation?: SlackConversationContext;
25
- destination?: Destination;
22
+ destination: Destination;
26
23
  surface?: AgentTurnSurface;
27
24
  correlation?: {
28
25
  conversationId?: string;
@@ -53,6 +50,8 @@ export interface ReplyRequestContext {
53
50
  sandbox?: {
54
51
  sandboxId?: string;
55
52
  sandboxDependencyProfileHash?: string;
53
+ /** Per-turn override for app-owned sandbox egress trace propagation. */
54
+ tracePropagation?: SandboxEgressTracePropagationConfig;
56
55
  };
57
56
  onSandboxAcquired?: (sandbox: SandboxAcquiredState) => void | Promise<void>;
58
57
  onArtifactStateUpdated?: (artifactState: ThreadArtifactsState) => void | Promise<void>;
@@ -74,6 +73,7 @@ export interface ReplyRequestContext {
74
73
  params: Record<string, unknown>;
75
74
  }) => void;
76
75
  }
76
+ export type AssistantReplyRequestContext = ReplyRequestContext;
77
77
  export interface ReplyRequestAttachment {
78
78
  data?: Buffer;
79
79
  mediaType: string;
@@ -87,4 +87,4 @@ export interface ReplySteeringMessage {
87
87
  userAttachments?: ReplyRequestAttachment[];
88
88
  }
89
89
  /** Run a full agent turn: discover skills, execute tools, and return the assistant reply. */
90
- export declare function generateAssistantReply(messageText: string, context?: ReplyRequestContext): Promise<AssistantReply>;
90
+ export declare function generateAssistantReply(messageText: string, context: AssistantReplyRequestContext): Promise<AssistantReply>;
@@ -0,0 +1,25 @@
1
+ import { resumeSlackTurn } from "@/chat/runtime/slack-resume";
2
+ import { type AgentContinueRequest } from "@/chat/services/agent-continue";
3
+ import type { generateAssistantReply } from "@/chat/respond";
4
+ /** Runtime ports for agent continuation scheduling. */
5
+ export interface AgentContinueRunnerOptions {
6
+ generateReply?: typeof generateAssistantReply;
7
+ resumeTurn?: typeof resumeSlackTurn;
8
+ scheduleAgentContinue?: (request: AgentContinueRequest) => Promise<void>;
9
+ }
10
+ /**
11
+ * Continue one paused Slack agent run from durable conversation state.
12
+ *
13
+ * Returns false when the session became stale before generation began.
14
+ */
15
+ export declare function continueSlackAgentRun(payload: AgentContinueRequest, options?: AgentContinueRunnerOptions): Promise<boolean>;
16
+ /** Resume the first valid paused Slack session for an idle conversation. */
17
+ export declare function resumeAwaitingSlackContinuation(conversationId: string, options?: AgentContinueRunnerOptions): Promise<boolean>;
18
+ /**
19
+ * Retry agent continuation when the normal Slack thread lock is briefly busy.
20
+ *
21
+ * Returns false when the session became stale before generation began. A busy
22
+ * lock that is rescheduled still returns true because runnable work remains
23
+ * durable.
24
+ */
25
+ export declare function continueSlackAgentRunWithLockRetry(payload: AgentContinueRequest, options?: AgentContinueRunnerOptions): Promise<boolean>;
@@ -15,17 +15,17 @@ import { type PrepareTurnStateInput, type QueuedTurnMessage, type TurnToolInvoca
15
15
  import { type ConversationMemoryService } from "@/chat/services/conversation-memory";
16
16
  import type { ContextCompactor } from "@/chat/services/context-compaction";
17
17
  import { lookupSlackUser } from "@/chat/slack/user";
18
- import type { TurnContinuationRequest } from "@/chat/services/timeout-resume";
18
+ import type { AgentContinueRequest } from "@/chat/services/agent-continue";
19
19
  export interface ReplyExecutorServices {
20
20
  contextCompactor: ContextCompactor;
21
21
  generateAssistantReply: typeof generateAssistantReplyImpl;
22
22
  generateThreadTitle: ConversationMemoryService["generateThreadTitle"];
23
- getAwaitingTurnContinuationRequest: (args: {
23
+ getAwaitingAgentContinueRequest: (args: {
24
24
  conversationId: string;
25
25
  sessionId: string;
26
- }) => Promise<TurnContinuationRequest | undefined>;
26
+ }) => Promise<AgentContinueRequest | undefined>;
27
27
  lookupSlackUser: typeof lookupSlackUser;
28
- scheduleTurnTimeoutResume: (request: TurnContinuationRequest) => Promise<void>;
28
+ scheduleAgentContinue: (request: AgentContinueRequest) => Promise<void>;
29
29
  }
30
30
  interface ReplyExecutorDeps {
31
31
  getSlackAdapter: () => SlackAdapter;
@@ -56,7 +56,9 @@ export declare function createReplyToThread(deps: ReplyExecutorDeps): (thread: T
56
56
  onTurnStatePersisted?: () => Promise<void>;
57
57
  preparedState?: PreparedTurnState;
58
58
  queuedMessages?: QueuedTurnMessage[];
59
- drainSteeringMessages?: (inject: (messages: QueuedTurnMessage[]) => Promise<void>) => Promise<QueuedTurnMessage[]>;
59
+ drainSteeringMessages?: (inject: (messages: QueuedTurnMessage[]) => Promise<void>, context?: {
60
+ conversationContext?: string;
61
+ }) => Promise<QueuedTurnMessage[]>;
60
62
  shouldYield?: () => boolean;
61
63
  }) => Promise<void>;
62
64
  export {};
@@ -1,4 +1,4 @@
1
- import { generateAssistantReply, type AssistantReply, type ReplyRequestContext } from "@/chat/respond";
1
+ import { generateAssistantReply, type AssistantReply, type AssistantReplyRequestContext } from "@/chat/respond";
2
2
  /** Error raised when another worker already owns the resume lock. */
3
3
  export declare class ResumeTurnBusyError extends Error {
4
4
  constructor(lockKey: string);
@@ -8,7 +8,7 @@ interface ResumeSlackTurnArgs {
8
8
  channelId: string;
9
9
  threadTs: string;
10
10
  messageTs?: string;
11
- replyContext?: ReplyRequestContext;
11
+ replyContext?: AssistantReplyRequestContext;
12
12
  lockKey?: string;
13
13
  initialText?: string;
14
14
  generateReply?: typeof generateAssistantReply;
@@ -35,7 +35,7 @@ export declare function resumeAuthorizedRequest(args: {
35
35
  threadTs: string;
36
36
  messageTs?: string;
37
37
  connectedText: string;
38
- replyContext?: ReplyRequestContext;
38
+ replyContext?: AssistantReplyRequestContext;
39
39
  lockKey?: string;
40
40
  generateReply?: typeof generateAssistantReply;
41
41
  onSuccess?: (reply: AssistantReply) => Promise<void>;
@@ -28,6 +28,9 @@ export interface ReplyHooks {
28
28
  onTurnStatePersisted?: () => Promise<void>;
29
29
  shouldYield?: () => boolean;
30
30
  }
31
+ interface SteeringDrainContext {
32
+ conversationContext?: string;
33
+ }
31
34
  export interface SlackTurnOptions extends ReplyHooks {
32
35
  destination: Destination;
33
36
  }
@@ -53,7 +56,13 @@ export interface SlackTurnRuntimeDependencies<TPreparedState> {
53
56
  logWarn: (eventName: string, context?: Record<string, unknown>, attributes?: Record<string, unknown>, body?: string) => void;
54
57
  modelId: string;
55
58
  now: () => number;
56
- recordSkippedSubscribedMessage: (args: {
59
+ recordSkippedSteeringMessage: (args: {
60
+ decision: SubscribedReplyDecision;
61
+ message: Message;
62
+ text: TurnMessageText;
63
+ thread: Thread;
64
+ }) => Promise<void>;
65
+ recordSkippedSubscribedTurn: (args: {
57
66
  completedAtMs: number;
58
67
  decision: SubscribedReplyDecision;
59
68
  message: Message;
@@ -64,7 +73,7 @@ export interface SlackTurnRuntimeDependencies<TPreparedState> {
64
73
  completedAtMs: number;
65
74
  decision: SubscribedReplyDecision;
66
75
  message: Message;
67
- preparedState?: TPreparedState;
76
+ preparedState: TPreparedState;
68
77
  thread: Thread;
69
78
  }) => Promise<void>;
70
79
  persistPreparedState: (args: {
@@ -82,7 +91,7 @@ export interface SlackTurnRuntimeDependencies<TPreparedState> {
82
91
  onTurnStatePersisted?: () => Promise<void>;
83
92
  preparedState?: TPreparedState;
84
93
  queuedMessages?: QueuedTurnMessage[];
85
- drainSteeringMessages?: (inject: (messages: QueuedTurnMessage[]) => Promise<void>) => Promise<QueuedTurnMessage[]>;
94
+ drainSteeringMessages?: (inject: (messages: QueuedTurnMessage[]) => Promise<void>, context?: SteeringDrainContext) => Promise<QueuedTurnMessage[]>;
86
95
  shouldYield?: () => boolean;
87
96
  }) => Promise<void>;
88
97
  decideSubscribedReply: SubscribedReplyPolicy;
@@ -100,3 +109,4 @@ export interface SlackTurnRuntime<_TPreparedState, TAssistantEvent extends Assis
100
109
  }
101
110
  /** Build the Slack event runtime that routes mentions and subscribed messages. */
102
111
  export declare function createSlackTurnRuntime<TPreparedState, TAssistantEvent extends AssistantLifecycleEvent = AssistantLifecycleEvent>(deps: SlackTurnRuntimeDependencies<TPreparedState>): SlackTurnRuntime<TPreparedState, TAssistantEvent>;
112
+ export {};
@@ -3,12 +3,15 @@ import type { AuthorizationPauseDisposition, AuthorizationPauseKind } from "@/ch
3
3
  import type { TurnThinkingSelection } from "@/chat/services/turn-thinking-level";
4
4
  import type { AgentTurnUsage } from "@/chat/usage";
5
5
  export { buildDeterministicTurnId } from "@/chat/state/turn-id";
6
- export type RetryableTurnReason = "mcp_auth_resume" | "plugin_auth_resume" | "turn_timeout_resume";
6
+ export type RetryableTurnReason = "mcp_auth_resume" | "plugin_auth_resume" | "agent_continue";
7
+ /** Auth-pause reasons require a known provider before a resume can be parked. */
8
+ export type AuthResumeRetryableTurnReason = Extract<RetryableTurnReason, "mcp_auth_resume" | "plugin_auth_resume">;
7
9
  export interface RetryableTurnMetadata {
8
10
  authDisposition?: AuthorizationPauseDisposition;
9
11
  authDurationMs?: number;
10
12
  authKind?: AuthorizationPauseKind;
11
13
  authProvider?: string;
14
+ authProviderDisplayName?: string;
12
15
  authThinkingLevel?: TurnThinkingSelection["thinkingLevel"];
13
16
  authUsage?: AgentTurnUsage;
14
17
  version?: number;
@@ -16,14 +19,25 @@ export interface RetryableTurnMetadata {
16
19
  sessionId?: string;
17
20
  sliceId?: number;
18
21
  }
19
- /** Error indicating the turn can be retried (timeout or auth pause). */
22
+ export interface AuthResumeRetryableTurnMetadata extends RetryableTurnMetadata {
23
+ authProvider: string;
24
+ authProviderDisplayName: string;
25
+ }
26
+ export type AuthResumeRetryableTurnError = RetryableTurnError & {
27
+ readonly reason: AuthResumeRetryableTurnReason;
28
+ readonly metadata: AuthResumeRetryableTurnMetadata;
29
+ };
30
+ /** Error indicating an agent run can continue later after timeout or auth pause. */
20
31
  export declare class RetryableTurnError extends Error {
21
32
  readonly code = "retryable_turn";
22
33
  readonly metadata?: RetryableTurnMetadata;
23
34
  readonly reason: RetryableTurnReason;
24
- constructor(reason: RetryableTurnReason, message: string, metadata?: RetryableTurnMetadata);
35
+ constructor(reason: AuthResumeRetryableTurnReason, message: string, metadata: AuthResumeRetryableTurnMetadata);
36
+ constructor(reason: "agent_continue", message: string, metadata?: RetryableTurnMetadata);
25
37
  }
26
38
  export declare function isRetryableTurnError(error: unknown, reason?: RetryableTurnReason): error is RetryableTurnError;
39
+ /** Return whether a retryable turn is waiting for provider authorization. */
40
+ export declare function isAuthResumeRetryableTurnError(error: unknown): error is AuthResumeRetryableTurnError;
27
41
  /** Error indicating the turn paused voluntarily at a safe continuation boundary. */
28
42
  export declare class CooperativeTurnYieldError extends Error {
29
43
  readonly code = "cooperative_turn_yield";
@@ -7,14 +7,17 @@ export type SandboxEgressGrantSelection = {
7
7
  grant: AgentPluginGrant;
8
8
  source: "broker";
9
9
  };
10
- /** Signals that a plugin selected a grant but needs user authorization before issuing headers. */
11
- export declare class SandboxEgressCredentialNeededError extends Error {
10
+ export type SandboxEgressCredentialErrorKind = "auth_required" | "unavailable";
11
+ /** Signals that egress selected a grant but could not issue credential headers. */
12
+ export declare class SandboxEgressCredentialError extends Error {
12
13
  readonly authorization?: AgentPluginAuthorization;
13
14
  readonly grant: AgentPluginGrant;
15
+ readonly kind: SandboxEgressCredentialErrorKind;
14
16
  readonly provider: string;
15
17
  constructor(input: {
16
18
  authorization?: AgentPluginAuthorization;
17
19
  grant: AgentPluginGrant;
20
+ kind: SandboxEgressCredentialErrorKind;
18
21
  message: string;
19
22
  provider: string;
20
23
  });
@@ -1,11 +1,15 @@
1
1
  import type { NetworkPolicy } from "@vercel/sandbox";
2
+ import type { TracePropagationHeaders } from "@/chat/logging";
3
+ import { type SandboxEgressTracePropagationConfig } from "@/chat/sandbox/egress-tracing";
2
4
  /** Return whether an outbound host is covered by a sandbox egress domain rule. */
3
5
  export declare function matchesSandboxEgressDomain(host: string, domain: string): boolean;
4
6
  /** Resolve the plugin provider responsible for an outbound sandbox host. */
5
7
  export declare function resolveSandboxEgressProviderForHost(host: string): string | undefined;
6
- /** Build the policy that forwards provider requests back to Junior for credentials. */
8
+ /** Build the policy that forwards credentials and configured trace headers. */
7
9
  export declare function buildSandboxEgressNetworkPolicy(input?: {
8
10
  credentialToken?: string;
11
+ traceConfig?: SandboxEgressTracePropagationConfig;
12
+ traceHeaders?: TracePropagationHeaders;
9
13
  }): NetworkPolicy;
10
14
  /** Resolve non-secret command environment values for registered sandbox providers. */
11
15
  export declare function resolveSandboxCommandEnvironment(): Promise<Record<string, string>>;
@@ -1,3 +1,4 @@
1
+ import { type SandboxEgressTracePropagationConfig } from "@/chat/sandbox/egress-tracing";
1
2
  import type { JWTPayload } from "jose";
2
3
  /** Intercepts a credential-injected sandbox HTTP request before live forwarding. */
3
4
  export type SandboxEgressHttpInterceptor = (input: {
@@ -8,6 +9,7 @@ export type SandboxEgressHttpInterceptor = (input: {
8
9
  interface ProxyDeps {
9
10
  fetch?: typeof fetch;
10
11
  interceptHttp?: SandboxEgressHttpInterceptor;
12
+ tracePropagation?: SandboxEgressTracePropagationConfig;
11
13
  verifyOidc?: (token: string) => Promise<JWTPayload>;
12
14
  }
13
15
  /** Return whether a request appears to be from the Vercel Sandbox egress proxy. */
@@ -68,6 +68,10 @@ export declare const sandboxEgressAuthRequiredSignalSchema: z.ZodObject<{
68
68
  reason: z.ZodOptional<z.ZodString>;
69
69
  requirements: z.ZodOptional<z.ZodArray<z.ZodString>>;
70
70
  }, z.core.$strict>;
71
+ kind: z.ZodDefault<z.ZodEnum<{
72
+ unavailable: "unavailable";
73
+ auth_required: "auth_required";
74
+ }>>;
71
75
  provider: z.ZodString;
72
76
  message: z.ZodOptional<z.ZodString>;
73
77
  createdAtMs: z.ZodNumber;
@@ -17,7 +17,9 @@ export declare function getSandboxEgressCredentialLease(provider: string, grantN
17
17
  /** Clear a cached egress credential lease after the provider rejects its headers. */
18
18
  export declare function clearSandboxEgressCredentialLease(provider: string, grantName: string, context: SandboxEgressCredentialContext): Promise<void>;
19
19
  /** Record that host-side sandbox egress returned an auth-required response. */
20
- export declare function setSandboxEgressAuthRequiredSignal(context: SandboxEgressCredentialContext, signal: Omit<SandboxEgressAuthRequiredSignal, "createdAtMs">): Promise<void>;
20
+ export declare function setSandboxEgressAuthRequiredSignal(context: SandboxEgressCredentialContext, signal: Omit<SandboxEgressAuthRequiredSignal, "createdAtMs" | "kind"> & {
21
+ kind?: SandboxEgressAuthRequiredSignal["kind"];
22
+ }): Promise<void>;
21
23
  /** Record that host-side sandbox egress saw an upstream permission denial. */
22
24
  export declare function setSandboxEgressPermissionDeniedSignal(context: SandboxEgressCredentialContext, signal: Omit<SandboxEgressPermissionDeniedSignal, "createdAtMs">): Promise<void>;
23
25
  /** Remove any pending host-side sandbox egress signals for a command. */