@sentry/junior 0.162.0 → 0.163.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 (90) hide show
  1. package/README.md +2 -0
  2. package/dist/{agent-hooks-TL2SGC5W.js → agent-hooks-6IX5CV7G.js} +10 -10
  3. package/dist/api/acp/route.d.ts +13 -0
  4. package/dist/api.js +204 -403
  5. package/dist/app.d.ts +2 -2
  6. package/dist/app.js +480 -38
  7. package/dist/{catalog-runtime-IGSMIADJ.js → catalog-runtime-NEPZNTOM.js} +5 -5
  8. package/dist/chat/agent/sandbox.d.ts +4 -0
  9. package/dist/chat/api-turns/cancellation.d.ts +27 -0
  10. package/dist/chat/api-turns/routing.d.ts +31 -0
  11. package/dist/chat/api-turns/work.d.ts +12 -29
  12. package/dist/chat/app/conversation-work.d.ts +6 -1
  13. package/dist/chat/app/production.d.ts +2 -2
  14. package/dist/chat/conversations/history.d.ts +3 -0
  15. package/dist/chat/conversations/turn-lifecycle.d.ts +1 -1
  16. package/dist/chat/experimental.d.ts +1 -1
  17. package/dist/chat/plugins/agent-hooks.d.ts +4 -0
  18. package/dist/chat/repository-instructions.d.ts +1 -1
  19. package/dist/chat/sandbox/paths.d.ts +1 -0
  20. package/dist/chat/sandbox/prepare-workspace.d.ts +11 -0
  21. package/dist/chat/sandbox/ref.d.ts +1 -0
  22. package/dist/chat/sandbox/sandbox.d.ts +4 -0
  23. package/dist/chat/sandbox/session.d.ts +4 -0
  24. package/dist/chat/sandbox/snapshot/profile.d.ts +4 -3
  25. package/dist/chat/sandbox/snapshot/resolve.d.ts +4 -0
  26. package/dist/chat/sandbox/workspace.d.ts +4 -0
  27. package/dist/chat/tools/types.d.ts +5 -0
  28. package/dist/chat/workspaces/checkout-path.d.ts +4 -0
  29. package/dist/chat/workspaces/store.d.ts +8 -0
  30. package/dist/chat/workspaces/tools.d.ts +4 -0
  31. package/dist/chat/workspaces/types.d.ts +13 -0
  32. package/dist/{chunk-BPZQWAZJ.js → chunk-2FBPN2Y4.js} +4 -1
  33. package/dist/{chunk-H2KW7AZC.js → chunk-2HBJQEBE.js} +1 -1
  34. package/dist/{chunk-O6P7DZ6O.js → chunk-3OZNFCPR.js} +6 -6
  35. package/dist/{chunk-7DTPFFQX.js → chunk-3T7RJVDJ.js} +1 -1
  36. package/dist/{chunk-FEWT5KPP.js → chunk-4P2XNVTO.js} +2 -2
  37. package/dist/{chunk-CZ6EHOU5.js → chunk-ATWHPSMF.js} +40 -2
  38. package/dist/{chunk-MMBMDSRK.js → chunk-BLZUFHM7.js} +1 -1
  39. package/dist/chunk-BVREIWBD.js +131 -0
  40. package/dist/{chunk-OPNJRQDP.js → chunk-EKGJVUVS.js} +9 -6
  41. package/dist/{chunk-4WYGQ47U.js → chunk-FEIHINKR.js} +5 -5
  42. package/dist/{chunk-JJ3VXFDI.js → chunk-GCLHNRMU.js} +1 -1
  43. package/dist/{chunk-WHAJ3QVY.js → chunk-ICJ3KCA2.js} +1 -1
  44. package/dist/{chunk-5NEBOHRG.js → chunk-IIT23D3M.js} +4 -4
  45. package/dist/{chunk-EHQLTK3X.js → chunk-IX4XI42N.js} +376 -128
  46. package/dist/{chunk-DDQ43S5Q.js → chunk-JI5EPI3O.js} +4 -4
  47. package/dist/{chunk-C3KUKA27.js → chunk-KD3ROG6P.js} +17 -17
  48. package/dist/{chunk-JRVURR3H.js → chunk-MWVQ35U6.js} +543 -164
  49. package/dist/{chunk-V7QL4QMW.js → chunk-MXFL7LNZ.js} +12 -7
  50. package/dist/{chunk-RDV4BQRZ.js → chunk-NL53P7PS.js} +1 -1
  51. package/dist/{chunk-EBEHJPFF.js → chunk-O4IK4O7C.js} +1 -1
  52. package/dist/{chunk-KQSO4DJ4.js → chunk-O5ZZL7IR.js} +9 -9
  53. package/dist/{chunk-FESUWAYU.js → chunk-RK7KPI6G.js} +1 -1
  54. package/dist/{chunk-OBFRMQBY.js → chunk-RTPJRLOP.js} +9 -9
  55. package/dist/{chunk-HW4BW3PC.js → chunk-SR6WRBJW.js} +4 -4
  56. package/dist/{chunk-6BJFIWEV.js → chunk-THS4CKN4.js} +46 -87
  57. package/dist/{chunk-DZC3F2UI.js → chunk-TPDNZCZU.js} +1 -1
  58. package/dist/{chunk-Y75ZDQB5.js → chunk-TPT6EF7K.js} +1 -1
  59. package/dist/{chunk-5IDFPCON.js → chunk-VRS6WDK5.js} +1 -1
  60. package/dist/{chunk-AJ7J2HWY.js → chunk-WPGYWDJR.js} +6 -6
  61. package/dist/{chunk-K7IV6UV3.js → chunk-XY2IVIAY.js} +5 -5
  62. package/dist/{chunk-MNCEGXEH.js → chunk-YAUQXZVT.js} +2 -2
  63. package/dist/{chunk-THXMSSFZ.js → chunk-ZDKTZG3G.js} +1 -1
  64. package/dist/{chunk-QHCIZLIX.js → chunk-ZF6IC27P.js} +87 -10
  65. package/dist/cli/chat.js +32 -32
  66. package/dist/cli/check.js +6 -6
  67. package/dist/cli/plugins.js +13 -13
  68. package/dist/cli/snapshot-create.js +10 -10
  69. package/dist/cli/upgrade.js +7 -7
  70. package/dist/db/schema/workspaces.d.ts +169 -0
  71. package/dist/db/schema.d.ts +169 -1
  72. package/dist/{db-66XZ4HHQ.js → db-6VBMHVW2.js} +7 -7
  73. package/dist/{experimental-HH6OM6UH.js → experimental-XVDOSRBS.js} +1 -1
  74. package/dist/instrumentation.js +4 -4
  75. package/dist/nitro.js +2 -2
  76. package/dist/{oauth-callback-server-FMLKDUUF.js → oauth-callback-server-5NCCUIM2.js} +24 -24
  77. package/dist/{runner-YZONUMNY.js → runner-6ONEB6TQ.js} +13 -13
  78. package/dist/{skills-3UDJJXSO.js → skills-Y5R36UH4.js} +6 -6
  79. package/dist/spawn-RFMBQ6B3.js +37 -0
  80. package/dist/{validation-3HTDBONC.js → validation-HLHEQDEK.js} +6 -6
  81. package/dist/version.js +1 -1
  82. package/dist/{work-O4YTIS54.js → work-VIEFYTFG.js} +18 -18
  83. package/dist/worker-MRSKPKQZ.js +24 -0
  84. package/migrations/0029_dizzy_barracuda.sql +19 -0
  85. package/migrations/meta/0029_snapshot.json +2709 -0
  86. package/migrations/meta/_journal.json +8 -1
  87. package/package.json +8 -5
  88. package/dist/chunk-EM225ITJ.js +0 -56
  89. package/dist/spawn-TBPVA5S2.js +0 -37
  90. package/dist/worker-WHURUEYX.js +0 -24
@@ -1,15 +1,15 @@
1
1
  import {
2
2
  pluginCatalogRuntime
3
- } from "./chunk-FESUWAYU.js";
4
- import "./chunk-EBEHJPFF.js";
3
+ } from "./chunk-RK7KPI6G.js";
4
+ import "./chunk-O4IK4O7C.js";
5
5
  import "./chunk-BDUENQD3.js";
6
6
  import "./chunk-V7YE4WCP.js";
7
- import "./chunk-Y75ZDQB5.js";
7
+ import "./chunk-TPT6EF7K.js";
8
8
  import "./chunk-6GWA276C.js";
9
9
  import "./chunk-OOUNJNMR.js";
10
10
  import "./chunk-H4HBKISO.js";
11
- import "./chunk-THXMSSFZ.js";
12
- import "./chunk-BPZQWAZJ.js";
11
+ import "./chunk-ZDKTZG3G.js";
12
+ import "./chunk-2FBPN2Y4.js";
13
13
  import "./chunk-MLKGABMK.js";
14
14
  export {
15
15
  pluginCatalogRuntime
@@ -18,9 +18,11 @@ import type { RepositoryInstructions } from "@/chat/repository-instructions";
18
18
  import { type SandboxTools } from "@/chat/sandbox/sandbox";
19
19
  import type { SandboxWorkspace } from "@/chat/sandbox/workspace";
20
20
  import type { Skill, SkillMetadata } from "@/chat/skills";
21
+ import type { Workspace } from "@/chat/workspaces/types";
21
22
  import type { GeneratedArtifactFileRef } from "@/chat/tools/sandbox/file-uploads";
22
23
  export interface AgentSandboxOptions {
23
24
  sandboxRef?: SandboxRef;
25
+ workspace?: Workspace;
24
26
  skills: SkillMetadata[];
25
27
  traceContext: LogContext;
26
28
  tracePropagation?: SandboxEgressTracePropagationConfig;
@@ -31,6 +33,7 @@ export interface AgentSandboxOptions {
31
33
  configurationValues: Record<string, unknown>;
32
34
  getActiveSkill(): Skill | null;
33
35
  prepareSandbox(workspace: SandboxWorkspace): void | Promise<void>;
36
+ prepareWorkspace?(workspace: SandboxWorkspace, recipe: Workspace, signal?: AbortSignal): Promise<void>;
34
37
  onSandboxRefChanged(sandboxRef: SandboxRef): void;
35
38
  persistSandboxRef?(sandboxRef: SandboxRef): void | Promise<void>;
36
39
  }
@@ -40,6 +43,7 @@ export interface AgentSandbox {
40
43
  readonly tools: SandboxTools;
41
44
  readonly workspace: SandboxWorkspace;
42
45
  sandboxRef(): SandboxRef | undefined;
46
+ switchWorkspace(workspace: Workspace, signal?: AbortSignal): Promise<void>;
43
47
  close(): void;
44
48
  writeGeneratedArtifacts(files: FileUpload[]): Promise<GeneratedArtifactFileRef[]>;
45
49
  }
@@ -0,0 +1,27 @@
1
+ /** App-scoped control for one active API Turn per Conversation. */
2
+ export interface ApiTurnCancellation {
3
+ begin(conversationId: string): AbortSignal | undefined;
4
+ cancel(conversationId: string): boolean;
5
+ disconnect(conversationId: string, signal: AbortSignal): void;
6
+ finish(conversationId: string, signal: AbortSignal): void;
7
+ park(conversationId: string, signal: AbortSignal): void;
8
+ signal(conversationId: string): AbortSignal | undefined;
9
+ }
10
+ /** Create in-process cancellation state for active API Turns. */
11
+ export declare function createApiTurnCancellation(): ApiTurnCancellation;
12
+ /** Close one cancelled API Turn without a failure reply or retry. */
13
+ export declare function completeCancelledApiTurn(args: {
14
+ acknowledge(): Promise<void>;
15
+ actorId: string;
16
+ cancellation?: ApiTurnCancellation;
17
+ conversation: ThreadConversationState;
18
+ conversationId: string;
19
+ lifecycle: ConversationTurnLifecycle;
20
+ sandboxRef?: SandboxRef;
21
+ signal?: AbortSignal;
22
+ turnId: string;
23
+ userMessageId: string;
24
+ }): Promise<void>;
25
+ import type { ConversationTurnLifecycle } from "@/chat/conversations/turn-lifecycle";
26
+ import type { SandboxRef } from "@/chat/sandbox/ref";
27
+ import type { ThreadConversationState } from "@/chat/state/conversation";
@@ -0,0 +1,31 @@
1
+ import { z } from "zod";
2
+ import type { InboundMessage } from "@/chat/task-execution/store";
3
+ import type { ConversationWorkerContext } from "@/chat/task-execution/worker";
4
+ declare const apiTurnMailboxMetadataSchema: z.ZodObject<{
5
+ authorEmail: z.ZodString;
6
+ authorFullName: z.ZodOptional<z.ZodString>;
7
+ authorUserId: z.ZodString;
8
+ authorUserName: z.ZodOptional<z.ZodString>;
9
+ kind: z.ZodLiteral<"api_turn">;
10
+ messageId: z.ZodString;
11
+ }, z.core.$strict>;
12
+ export type ApiTurnMailboxMetadata = z.output<typeof apiTurnMailboxMetadataSchema>;
13
+ /**
14
+ * Resolve API Turn work from mailbox metadata or an active checkpoint.
15
+ *
16
+ * Empty resume wakes after yield carry no mailbox rows. Use durable active
17
+ * Turn state so these wakes do not fall through to Slack.
18
+ */
19
+ export declare function resolveApiTurnWork(context: ConversationWorkerContext): Promise<{
20
+ kind: "mailbox";
21
+ batch: Array<{
22
+ message: InboundMessage;
23
+ metadata: ApiTurnMailboxMetadata;
24
+ }>;
25
+ } | {
26
+ kind: "resume";
27
+ turnId: string;
28
+ } | undefined>;
29
+ /** Return whether the leased attempt belongs to an API Turn. */
30
+ export declare function isApiTurnWork(context: ConversationWorkerContext): Promise<boolean>;
31
+ export {};
@@ -1,4 +1,3 @@
1
- import { z } from "zod";
2
1
  import type { StateAdapter } from "chat";
3
2
  import { type Destination } from "@sentry/junior-plugin-api";
4
3
  import type { ConversationPrivacy } from "@/chat/conversation-privacy";
@@ -9,15 +8,8 @@ import type { AgentRunner } from "@/chat/runtime/agent-runner";
9
8
  import { type InboundMessage } from "@/chat/task-execution/store";
10
9
  import type { ConversationWorkQueue } from "@/chat/task-execution/queue";
11
10
  import type { ConversationWorkerContext, ConversationWorkerResult } from "@/chat/task-execution/worker";
12
- declare const apiTurnMailboxMetadataSchema: z.ZodObject<{
13
- authorEmail: z.ZodString;
14
- authorFullName: z.ZodOptional<z.ZodString>;
15
- authorUserId: z.ZodString;
16
- authorUserName: z.ZodOptional<z.ZodString>;
17
- kind: z.ZodLiteral<"api_turn">;
18
- messageId: z.ZodString;
19
- }, z.core.$strict>;
20
- type ApiTurnMailboxMetadata = z.output<typeof apiTurnMailboxMetadataSchema>;
11
+ import { type ApiTurnCancellation } from "@/chat/api-turns/cancellation";
12
+ export { resolveApiTurnWork } from "@/chat/api-turns/routing";
21
13
  type EnqueueOptions = {
22
14
  conversationStore?: ConversationStore;
23
15
  nowMs?: number;
@@ -73,31 +65,23 @@ export declare function buildApiTurnInboundMessage(args: {
73
65
  messageId: string;
74
66
  nowMs?: number;
75
67
  }): InboundMessage;
68
+ /** Record web activity and materialize a new API Conversation root when needed. */
69
+ export declare function recordApiConversationActivity(args: {
70
+ actor: WebActor;
71
+ conversationId: string;
72
+ conversationStore?: ConversationStore;
73
+ nowMs: number;
74
+ /** Applied only when this call creates the conversation root. */
75
+ rootVisibility?: ConversationPrivacy;
76
+ }): Promise<Destination>;
76
77
  /** Create a dashboard root conversation and enqueue its first message. */
77
78
  export declare function createAndEnqueueApiConversation(input: CreateApiConversationInput, options: EnqueueOptions): Promise<ApiConversationMessageAccepted>;
78
79
  /** Append one API message to an existing conversation and wake the worker. */
79
80
  export declare function appendAndEnqueueApiConversationMessage(input: AppendApiConversationMessageInput, options: EnqueueOptions): Promise<ApiConversationMessageAccepted>;
80
- /**
81
- * Resolve API turn work from mailbox metadata or an active API turn checkpoint.
82
- *
83
- * Empty resume wakes after yield carry no mailbox rows; match agent-invocation
84
- * and look up durable active turn state instead of falling through to Slack.
85
- */
86
- export declare function resolveApiTurnWork(context: ConversationWorkerContext): Promise<{
87
- kind: "mailbox";
88
- batch: Array<{
89
- message: InboundMessage;
90
- metadata: ApiTurnMailboxMetadata;
91
- }>;
92
- } | {
93
- kind: "resume";
94
- turnId: string;
95
- } | undefined>;
96
- /** True when this leased attempt is API-authored root work. */
97
- export declare function isApiTurnWork(context: ConversationWorkerContext): Promise<boolean>;
98
81
  /** Build the mailbox consumer for API-authored root turns. */
99
82
  export declare function createApiTurnWorker(options: {
100
83
  agentRunner: AgentRunner;
84
+ cancellation?: ApiTurnCancellation;
101
85
  turnLifecycle?: ConversationTurnLifecycle;
102
86
  }): (context: ConversationWorkerContext) => Promise<ConversationWorkerResult>;
103
87
  /** Route API turn work before falling through to other consumers. */
@@ -105,4 +89,3 @@ export declare function routeApiTurnWork(options: {
105
89
  apiTurnWorker: (context: ConversationWorkerContext) => Promise<ConversationWorkerResult>;
106
90
  fallbackWorker: (context: ConversationWorkerContext) => Promise<ConversationWorkerResult>;
107
91
  }): (context: ConversationWorkerContext) => Promise<ConversationWorkerResult>;
108
- export {};
@@ -4,6 +4,7 @@ import type { AgentRunner } from "@/chat/runtime/agent-runner";
4
4
  import type { ConversationStore } from "@/chat/conversations/store";
5
5
  import type { ConversationWorkQueue } from "@/chat/task-execution/queue";
6
6
  import type { VercelConversationWorkCallbackOptions } from "@/chat/task-execution/vercel-callback";
7
+ import { type ApiTurnCancellation } from "@/chat/api-turns/cancellation";
7
8
  import { createSlackRuntime } from "./factory";
8
9
  import type { JuniorRuntimeServiceOverrides } from "./services";
9
10
  interface ConversationWorkOptions {
@@ -14,11 +15,15 @@ interface ConversationWorkOptions {
14
15
  services?: JuniorRuntimeServiceOverrides;
15
16
  state?: StateAdapter;
16
17
  }
18
+ export type ConversationWorkCallbackOptions = VercelConversationWorkCallbackOptions & {
19
+ /** App-scoped control required by the experimental ACP route. */
20
+ apiTurnCancellation?: ApiTurnCancellation;
21
+ };
17
22
  /**
18
23
  * Compose conversation work once for production and integration tests.
19
24
  * Environment-specific queue, state, Slack, and agent adapters stop here.
20
25
  */
21
- export declare function createConversationWork(options: ConversationWorkOptions): VercelConversationWorkCallbackOptions & {
26
+ export declare function createConversationWork(options: ConversationWorkOptions): ConversationWorkCallbackOptions & {
22
27
  runtime: ReturnType<typeof createSlackRuntime>;
23
28
  };
24
29
  export {};
@@ -2,9 +2,9 @@ import type { SlackAdapter } from "@chat-adapter/slack";
2
2
  import { createSlackRuntime } from "@/chat/app/factory";
3
3
  import type { AgentRunner } from "@/chat/runtime/agent-runner";
4
4
  import type { SlackWebhookServices } from "@/chat/ingress/slack-webhook";
5
- import type { VercelConversationWorkCallbackOptions } from "@/chat/task-execution/vercel-callback";
6
5
  import type { JuniorRuntimeServiceOverrides } from "@/chat/app/services";
7
6
  import type { ConversationStore } from "@/chat/conversations/store";
7
+ import { type ConversationWorkCallbackOptions } from "@/chat/app/conversation-work";
8
8
  /** Return the lazily initialized production Slack adapter. */
9
9
  export declare function getProductionSlackAdapter(): SlackAdapter;
10
10
  /** Return the lazily initialized production Slack runtime. */
@@ -21,4 +21,4 @@ export declare function getProductionSlackWebhookServices(): SlackWebhookService
21
21
  export declare function createProductionConversationWorkOptions(options: {
22
22
  agentRunner: AgentRunner;
23
23
  services?: JuniorRuntimeServiceOverrides;
24
- }): VercelConversationWorkCallbackOptions;
24
+ }): ConversationWorkCallbackOptions;
@@ -342,6 +342,7 @@ export declare const conversationEventDataSchema: z.ZodUnion<readonly [z.ZodUnio
342
342
  outcome: z.ZodEnum<{
343
343
  success: "success";
344
344
  no_reply: "no_reply";
345
+ cancelled: "cancelled";
345
346
  }>;
346
347
  }, z.core.$strict>, z.ZodObject<{
347
348
  type: z.ZodLiteral<"turn_failed">;
@@ -657,6 +658,7 @@ export declare const conversationEventSchema: z.ZodUnion<readonly [z.ZodObject<{
657
658
  outcome: z.ZodEnum<{
658
659
  success: "success";
659
660
  no_reply: "no_reply";
661
+ cancelled: "cancelled";
660
662
  }>;
661
663
  }, z.core.$strict>, z.ZodObject<{
662
664
  type: z.ZodLiteral<"turn_failed">;
@@ -995,6 +997,7 @@ export declare const newConversationEventSchema: z.ZodObject<{
995
997
  outcome: z.ZodEnum<{
996
998
  success: "success";
997
999
  no_reply: "no_reply";
1000
+ cancelled: "cancelled";
998
1001
  }>;
999
1002
  }, z.core.$strict>, z.ZodObject<{
1000
1003
  type: z.ZodLiteral<"turn_failed">;
@@ -11,7 +11,7 @@ export interface StartConversationTurnInput {
11
11
  export interface CompleteConversationTurnInput {
12
12
  conversationId: string;
13
13
  createdAtMs: number;
14
- outcome: "success" | "no_reply";
14
+ outcome: "success" | "no_reply" | "cancelled";
15
15
  turnId: string;
16
16
  }
17
17
  /** Privacy-safe inputs for failing one correlated conversation turn. */
@@ -3,7 +3,7 @@
3
3
  * Add new keys here as features graduate from private experiments; remove them
4
4
  * once they become stable defaults.
5
5
  */
6
- export declare const EXPERIMENTAL_FEATURES: readonly ["subagents"];
6
+ export declare const EXPERIMENTAL_FEATURES: readonly ["acp", "subagents"];
7
7
  /** One known experimental feature name. */
8
8
  export type ExperimentalFeature = (typeof EXPERIMENTAL_FEATURES)[number];
9
9
  /** App-level opt-ins for unstable product features. */
@@ -36,6 +36,10 @@ export interface PluginHookRunner {
36
36
  afterMcpTool(input: AfterMcpToolHookInput): Promise<void>;
37
37
  beforeToolExecute(input: ToolHookInput): Promise<ToolHookResult>;
38
38
  prepareSandbox(workspace: SandboxWorkspace): Promise<void>;
39
+ prepareWorkspace(workspace: SandboxWorkspace, repos: Array<{
40
+ provider: string;
41
+ repo: string;
42
+ }>, signal?: AbortSignal): Promise<void>;
39
43
  }
40
44
  /** Validate plugin identity before it can affect process-wide hooks. */
41
45
  export declare function validatePlugins(plugins: PluginRegistration[]): void;
@@ -20,7 +20,7 @@ export type VisibleAgentsInstructions = {
20
20
  directory?: string;
21
21
  text: string;
22
22
  };
23
- /** Return the only direct-child Git worktree, without guessing when ambiguous. */
23
+ /** Return the only Git worktree under repos/, without guessing when ambiguous. */
24
24
  export declare function findSingleRepositoryDirectory(fs: SandboxFileSystem): Promise<string | undefined>;
25
25
  /** Resolve the effective root-to-cwd AGENTS.md bundle for one selected directory. */
26
26
  export declare function resolveRepositoryInstructions(args: {
@@ -1,4 +1,5 @@
1
1
  export declare const SANDBOX_WORKSPACE_ROOT: string;
2
+ export declare const SANDBOX_REPOS_ROOT: string;
2
3
  export declare const SANDBOX_SKILLS_ROOT: string;
3
4
  export declare const SANDBOX_DATA_ROOT: string;
4
5
  export declare function sandboxSkillDir(skillName: string): string;
@@ -0,0 +1,11 @@
1
+ import type { SandboxSession } from "@/chat/sandbox/workspace";
2
+ import type { Workspace } from "@/chat/workspaces/types";
3
+ /** Prepare repositories and setup state before a Workspace snapshot is captured. */
4
+ export declare function prepareWorkspaceSnapshot(params: {
5
+ sandbox: SandboxSession;
6
+ workspace: Workspace;
7
+ signal?: AbortSignal;
8
+ applyNetworkPolicy(sandbox: SandboxSession): Promise<unknown>;
9
+ prepareRepositories?(sandbox: SandboxSession, workspace: Workspace, signal?: AbortSignal): Promise<void>;
10
+ removeCredentialRoute: boolean;
11
+ }): Promise<void>;
@@ -2,4 +2,5 @@
2
2
  export interface SandboxRef {
3
3
  id: string;
4
4
  profileHash?: string;
5
+ workspaceId?: string;
5
6
  }
@@ -6,6 +6,7 @@ import { type RepositoryInstructions } from "@/chat/repository-instructions";
6
6
  import type { SandboxRef } from "@/chat/sandbox/ref";
7
7
  import { type SandboxWorkspace } from "@/chat/sandbox/workspace";
8
8
  import type { SkillMetadata } from "@/chat/skills";
9
+ import type { Workspace } from "@/chat/workspaces/types";
9
10
  export interface SandboxToolCall {
10
11
  toolName: string;
11
12
  input: unknown;
@@ -23,10 +24,12 @@ export interface SandboxAccess {
23
24
  readonly tools: SandboxTools;
24
25
  readonly workspace: SandboxWorkspace;
25
26
  sandboxRef(): SandboxRef | undefined;
27
+ switchWorkspace(workspace: Workspace, signal?: AbortSignal): Promise<void>;
26
28
  close(): void;
27
29
  }
28
30
  export interface SandboxOptions {
29
31
  sandboxRef?: SandboxRef;
32
+ workspace?: Workspace;
30
33
  skills: SkillMetadata[];
31
34
  referenceFiles: string[];
32
35
  timeoutMs?: number;
@@ -35,6 +38,7 @@ export interface SandboxOptions {
35
38
  credentialEgress?: CredentialContext;
36
39
  egressSignals?: SandboxEgressSignalTransport;
37
40
  prepare?: (workspace: SandboxWorkspace) => void | Promise<void>;
41
+ prepareWorkspace?: (workspace: SandboxWorkspace, recipe: Workspace, signal?: AbortSignal) => Promise<void>;
38
42
  onSandboxRefChanged?: (sandboxRef: SandboxRef) => void | Promise<void>;
39
43
  }
40
44
  /** Create lazy run-scoped access to a conversation's durable sandbox. */
@@ -3,6 +3,7 @@ import { type LogContext, type TracePropagationHeaders } from "@/chat/logging";
3
3
  import { type SandboxFileSystem, type SandboxSession } from "@/chat/sandbox/workspace";
4
4
  import type { SkillMetadata } from "@/chat/skills";
5
5
  import type { SandboxRef } from "@/chat/sandbox/ref";
6
+ import type { Workspace } from "@/chat/workspaces/types";
6
7
  interface SandboxToolExecutors {
7
8
  sessionId: string;
8
9
  bash: (input: {
@@ -25,6 +26,7 @@ interface SandboxToolExecutors {
25
26
  }
26
27
  interface SandboxRuntime {
27
28
  sandboxRef(): SandboxRef | undefined;
29
+ switchWorkspace(workspace: Workspace, signal?: AbortSignal): Promise<void>;
28
30
  acquire(signal?: AbortSignal): Promise<SandboxSession>;
29
31
  tools(signal?: AbortSignal): Promise<SandboxToolExecutors>;
30
32
  refreshNetworkPolicy(traceHeaders?: TracePropagationHeaders): Promise<void>;
@@ -32,6 +34,7 @@ interface SandboxRuntime {
32
34
  }
33
35
  interface SandboxRuntimeOptions {
34
36
  sandboxRef?: SandboxRef;
37
+ workspace?: Workspace;
35
38
  skills: SkillMetadata[];
36
39
  referenceFiles: string[];
37
40
  timeoutMs?: number;
@@ -39,6 +42,7 @@ interface SandboxRuntimeOptions {
39
42
  commandEnv?: () => Promise<Record<string, string>>;
40
43
  createNetworkPolicy?: (egressId: string, traceHeaders?: TracePropagationHeaders) => NetworkPolicy | undefined;
41
44
  onSandboxPrepare?: (sandbox: SandboxSession) => void | Promise<void>;
45
+ onWorkspacePrepare?: (sandbox: SandboxSession, workspace: Workspace, signal?: AbortSignal) => Promise<void>;
42
46
  onSandboxRefChanged?: (sandboxRef: SandboxRef) => void | Promise<void>;
43
47
  }
44
48
  /** Own sandbox acquisition, preparation, and session-scoped tool caching. */
@@ -1,3 +1,4 @@
1
+ import type { Workspace } from "@/chat/workspaces/types";
1
2
  import type { PluginRuntimeDependency, PluginRuntimePostinstallCommand } from "@/chat/plugins/types";
2
3
  export type Profile = {
3
4
  hash: string;
@@ -6,9 +7,9 @@ export type Profile = {
6
7
  dependencies: PluginRuntimeDependency[];
7
8
  postinstall: PluginRuntimePostinstallCommand[];
8
9
  };
9
- /** Build the dependency profile that selects a reusable sandbox snapshot. */
10
- export declare function create(runtime: string): Profile | null;
10
+ /** Build the complete profile that selects one reusable sandbox snapshot. */
11
+ export declare function create(runtime: string, workspace?: Workspace): Profile | null;
11
12
  /** Return the current dependency profile hash without building its snapshot. */
12
- export declare function hash(runtime: string): string | undefined;
13
+ export declare function hash(runtime: string, workspace?: Workspace): string | undefined;
13
14
  /** Decide whether a cached snapshot has outlived a floating profile. */
14
15
  export declare function isStale(profile: Profile, createdAtMs: number): boolean;
@@ -1,3 +1,5 @@
1
+ import { type SandboxSession } from "@/chat/sandbox/workspace";
2
+ import type { Workspace } from "@/chat/workspaces/types";
1
3
  export type ResolveOutcome = "no_profile" | "cache_hit" | "cache_hit_after_lock_wait" | "rebuilt" | "forced_rebuild";
2
4
  export type RebuildReason = "cache_miss" | "floating_stale" | "force_rebuild" | "snapshot_missing";
3
5
  /** Result of resolving the reusable snapshot for one dependency profile. */
@@ -18,6 +20,8 @@ export declare function resolve(params: {
18
20
  staleSnapshotId?: string;
19
21
  onProgress?: (phase: ProgressPhase) => void | Promise<void>;
20
22
  signal?: AbortSignal;
23
+ workspace?: Workspace;
24
+ prepareWorkspace?: (sandbox: SandboxSession) => Promise<void>;
21
25
  }): Promise<Snapshot>;
22
26
  /** Identify provider errors that mean a referenced snapshot no longer exists. */
23
27
  export declare function isMissingError(error: unknown): boolean;
@@ -66,3 +66,7 @@ export interface SandboxSession extends SandboxWorkspace {
66
66
  export declare function createSandboxSession(sandbox: VercelSandbox, options?: {
67
67
  onUnavailable?: (sessionId: string) => void;
68
68
  }): SandboxSession;
69
+ /** Best-effort stop; ignore secondary stop failures during cleanup. */
70
+ export declare function stopSession(session: {
71
+ stop: () => Promise<unknown>;
72
+ } | null | undefined): Promise<void>;
@@ -11,6 +11,7 @@ import type { ModelProfile } from "@/chat/model-profile";
11
11
  import type { GeneratedArtifactFileRef } from "@/chat/tools/sandbox/file-uploads";
12
12
  import type { SpawnAgent } from "@/chat/agent/types";
13
13
  import type { AttachmentStorage } from "@/chat/attachments/storage";
14
+ import type { Workspace } from "@/chat/workspaces/types";
14
15
  interface HandoffControl {
15
16
  /** Non-empty catalog of configured targets. */
16
17
  profiles: readonly [ModelProfile, ...ModelProfile[]];
@@ -79,6 +80,10 @@ interface BaseToolRuntimeContext {
79
80
  egress: PluginEgress;
80
81
  mcpToolManager?: McpToolManager;
81
82
  workspace: SandboxWorkspace;
83
+ workspaces?: {
84
+ activeWorkspaceId(): string | undefined;
85
+ switch(workspace: Workspace, signal?: AbortSignal): Promise<void>;
86
+ };
82
87
  /** Report whether the model currently executing the turn accepts images. */
83
88
  supportsImageInput?: () => boolean;
84
89
  }
@@ -0,0 +1,4 @@
1
+ /** Derive the fixed sandbox checkout path for one repository, or undefined when invalid. */
2
+ export declare function tryWorkspaceRepoCheckoutPath(repo: string): string | undefined;
3
+ /** Derive the fixed sandbox checkout path for one repository. */
4
+ export declare function workspaceRepoCheckoutPath(repo: string): string;
@@ -0,0 +1,8 @@
1
+ import type { JuniorDatabase } from "@/db/db";
2
+ import type { Workspace } from "./types";
3
+ /** List Workspace recipes by stable name. */
4
+ export declare function listWorkspaces(db: JuniorDatabase): Promise<Workspace[]>;
5
+ /** Resolve one Workspace recipe by name. */
6
+ export declare function getWorkspaceByName(db: JuniorDatabase, name: string): Promise<Workspace | undefined>;
7
+ /** Resolve one Workspace recipe by id. */
8
+ export declare function getWorkspace(db: JuniorDatabase, id: string): Promise<Workspace | undefined>;
@@ -0,0 +1,4 @@
1
+ import type { ToolRegistry } from "@/chat/tools/definition";
2
+ import type { ToolRuntimeContext } from "@/chat/tools/types";
3
+ /** Build tools for listing and selecting registered workspaces. */
4
+ export declare function createWorkspaceTools(context: ToolRuntimeContext): ToolRegistry;
@@ -0,0 +1,13 @@
1
+ /** Provider-owned repository included in one Workspace recipe. */
2
+ export interface WorkspaceRepo {
3
+ provider: string;
4
+ repo: string;
5
+ isPrimary: boolean;
6
+ }
7
+ /** Named recipe used to prepare reusable sandbox contents. */
8
+ export interface Workspace {
9
+ id: string;
10
+ name: string;
11
+ setupScript: string;
12
+ repos: WorkspaceRepo[];
13
+ }
@@ -1,7 +1,7 @@
1
1
  // package.json
2
2
  var package_default = {
3
3
  name: "@sentry/junior",
4
- version: "0.162.0",
4
+ version: "0.163.0",
5
5
  private: false,
6
6
  publishConfig: {
7
7
  access: "public"
@@ -56,6 +56,7 @@ var package_default = {
56
56
  prepare: "pnpm run build",
57
57
  prepack: "pnpm run build",
58
58
  build: "tsup && tsc -p tsconfig.build.json --emitDeclarationOnly",
59
+ "acp:smoke": "pnpm exec tsx scripts/acp-smoke.ts",
59
60
  "db:generate": "pnpm exec drizzle-kit generate --config drizzle.config.ts",
60
61
  lint: "oxlint --config .oxlintrc.json --deny-warnings src tests scripts bin tsup.config.ts && depcruise --config .dependency-cruiser.mjs src/chat",
61
62
  "lint:fix": "oxlint --config .oxlintrc.json --deny-warnings --fix src tests scripts bin tsup.config.ts",
@@ -67,6 +68,7 @@ var package_default = {
67
68
  "test:coverage": "vitest run --maxWorkers=4 --coverage --reporter=default --reporter=junit --outputFile.junit=coverage/results.junit.xml"
68
69
  },
69
70
  dependencies: {
71
+ "@agentclientprotocol/sdk": "1.3.0",
70
72
  "@ai-sdk/gateway": "^3.0.119",
71
73
  "@chat-adapter/slack": "4.29.0",
72
74
  "@chat-adapter/state-memory": "4.29.0",
@@ -102,6 +104,7 @@ var package_default = {
102
104
  zod: "catalog:"
103
105
  },
104
106
  devDependencies: {
107
+ "@hono/node-server": "1.19.14",
105
108
  "@emnapi/core": "^1.10.0",
106
109
  "@emnapi/runtime": "^1.10.0",
107
110
  "@sentry/junior-github": "workspace:*",
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  createAndEnqueueAgentInvocation
3
- } from "./chunk-AJ7J2HWY.js";
3
+ } from "./chunk-WPGYWDJR.js";
4
4
  import {
5
5
  actorFromRun,
6
6
  toolInvocationDestination
@@ -5,19 +5,19 @@ import {
5
5
  isDmChannel,
6
6
  normalizeSlackConversationId,
7
7
  withSlackRetries
8
- } from "./chunk-QHCIZLIX.js";
8
+ } from "./chunk-ZF6IC27P.js";
9
9
  import {
10
10
  parseDestination
11
- } from "./chunk-DDQ43S5Q.js";
11
+ } from "./chunk-JI5EPI3O.js";
12
12
  import {
13
13
  getStateAdapter
14
- } from "./chunk-DZC3F2UI.js";
14
+ } from "./chunk-TPDNZCZU.js";
15
15
  import {
16
16
  normalizeSlackEmojiName
17
- } from "./chunk-MNCEGXEH.js";
17
+ } from "./chunk-YAUQXZVT.js";
18
18
  import {
19
19
  pluginCatalogRuntime
20
- } from "./chunk-FESUWAYU.js";
20
+ } from "./chunk-RK7KPI6G.js";
21
21
  import {
22
22
  parseSlackMessageTs,
23
23
  parseSlackUserId
@@ -26,7 +26,7 @@ import {
26
26
  isRecord,
27
27
  logInfo,
28
28
  logWarn
29
- } from "./chunk-Y75ZDQB5.js";
29
+ } from "./chunk-TPT6EF7K.js";
30
30
 
31
31
  // src/chat/slack/outbound.ts
32
32
  var MAX_SLACK_MESSAGE_TEXT_CHARS = 4e4;
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  logWarn
3
- } from "./chunk-Y75ZDQB5.js";
3
+ } from "./chunk-TPT6EF7K.js";
4
4
 
5
5
  // src/chat/conversation-privacy.ts
6
6
  import { AsyncLocalStorage } from "async_hooks";
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  pluginCatalogRuntime
3
- } from "./chunk-FESUWAYU.js";
3
+ } from "./chunk-RK7KPI6G.js";
4
4
  import {
5
5
  logWarn
6
- } from "./chunk-Y75ZDQB5.js";
6
+ } from "./chunk-TPT6EF7K.js";
7
7
  import {
8
8
  skillRoots
9
9
  } from "./chunk-H4HBKISO.js";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  DEFAULT_SQL_STATEMENT_TIMEOUT_MS
3
- } from "./chunk-MNCEGXEH.js";
3
+ } from "./chunk-YAUQXZVT.js";
4
4
  import {
5
5
  juniorDestinations,
6
6
  timestamptz
@@ -645,6 +645,40 @@ var juniorSchedulerRuns = pgTable16(
645
645
  ]
646
646
  );
647
647
 
648
+ // src/db/schema/workspaces.ts
649
+ import { sql as sql6 } from "drizzle-orm";
650
+ import {
651
+ boolean as boolean3,
652
+ pgTable as pgTable17,
653
+ primaryKey as primaryKey9,
654
+ text as text17,
655
+ uniqueIndex as uniqueIndex7
656
+ } from "drizzle-orm/pg-core";
657
+ var juniorWorkspaces = pgTable17(
658
+ "junior_workspaces",
659
+ {
660
+ id: text17("id").primaryKey(),
661
+ name: text17("name").notNull(),
662
+ setupScript: text17("setup_script").notNull().default(""),
663
+ createdAt: timestamptz("created_at").notNull(),
664
+ updatedAt: timestamptz("updated_at").notNull()
665
+ },
666
+ (table) => [uniqueIndex7("junior_workspaces_name_idx").on(table.name)]
667
+ );
668
+ var juniorWorkspaceRepos = pgTable17(
669
+ "junior_workspace_repos",
670
+ {
671
+ workspaceId: text17("workspace_id").notNull().references(() => juniorWorkspaces.id, { onDelete: "cascade" }),
672
+ provider: text17("provider").notNull(),
673
+ repo: text17("repo").notNull(),
674
+ isPrimary: boolean3("is_primary").notNull().default(false)
675
+ },
676
+ (table) => [
677
+ primaryKey9({ columns: [table.workspaceId, table.provider, table.repo] }),
678
+ uniqueIndex7("junior_workspace_repos_primary_idx").on(table.workspaceId).where(sql6`${table.isPrimary}`)
679
+ ]
680
+ );
681
+
648
682
  // src/db/schema.ts
649
683
  var juniorSqlSchema = {
650
684
  juniorArtifacts,
@@ -665,7 +699,9 @@ var juniorSqlSchema = {
665
699
  juniorTaskExecutions,
666
700
  juniorSchedulerRuns,
667
701
  juniorSchedulerTasks,
668
- juniorUsers
702
+ juniorUsers,
703
+ juniorWorkspaceRepos,
704
+ juniorWorkspaces
669
705
  };
670
706
 
671
707
  // src/db/neon.ts
@@ -937,5 +973,7 @@ export {
937
973
  juniorTaskExecutions,
938
974
  juniorSchedulerTasks,
939
975
  juniorSchedulerRuns,
976
+ juniorWorkspaces,
977
+ juniorWorkspaceRepos,
940
978
  createJuniorSqlExecutor
941
979
  };