@stigmer/runner 3.1.12 → 3.1.14

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 (31) hide show
  1. package/dist/.build-fingerprint +1 -1
  2. package/dist/activities/execute-deep-agent/cas-capture-backend.d.ts +35 -15
  3. package/dist/activities/execute-deep-agent/cas-capture-backend.js +40 -12
  4. package/dist/activities/execute-deep-agent/cas-capture-backend.js.map +1 -1
  5. package/dist/activities/execute-deep-agent/deepagents-profiles.d.ts +16 -0
  6. package/dist/activities/execute-deep-agent/deepagents-profiles.js +31 -0
  7. package/dist/activities/execute-deep-agent/deepagents-profiles.js.map +1 -0
  8. package/dist/activities/execute-deep-agent/setup.js +12 -3
  9. package/dist/activities/execute-deep-agent/setup.js.map +1 -1
  10. package/dist/activities/execute-deep-agent/shell-env.d.ts +16 -0
  11. package/dist/activities/execute-deep-agent/shell-env.js +36 -0
  12. package/dist/activities/execute-deep-agent/shell-env.js.map +1 -0
  13. package/dist/activities/execute-deep-agent/subagent-transformer.d.ts +29 -6
  14. package/dist/activities/execute-deep-agent/subagent-transformer.js +73 -19
  15. package/dist/activities/execute-deep-agent/subagent-transformer.js.map +1 -1
  16. package/dist/runner-manager.js +2 -0
  17. package/dist/runner-manager.js.map +1 -1
  18. package/dist/runner.js +2 -0
  19. package/dist/runner.js.map +1 -1
  20. package/package.json +3 -3
  21. package/src/activities/execute-deep-agent/__tests__/cas-capture-backend.test.ts +134 -1
  22. package/src/activities/execute-deep-agent/__tests__/shell-env.test.ts +46 -0
  23. package/src/activities/execute-deep-agent/__tests__/shell-execute-gate.test.ts +291 -0
  24. package/src/activities/execute-deep-agent/__tests__/subagent-transformer.test.ts +7 -4
  25. package/src/activities/execute-deep-agent/cas-capture-backend.ts +69 -15
  26. package/src/activities/execute-deep-agent/deepagents-profiles.ts +35 -0
  27. package/src/activities/execute-deep-agent/setup.ts +12 -6
  28. package/src/activities/execute-deep-agent/shell-env.ts +42 -0
  29. package/src/activities/execute-deep-agent/subagent-transformer.ts +93 -20
  30. package/src/runner-manager.ts +5 -0
  31. package/src/runner.ts +5 -0
@@ -11,11 +11,15 @@
11
11
  * - Filter parent MCP tools: no reconnection overhead, stateless servers are the norm
12
12
  * - Prompt injection for skills: FilesystemBackend incompatible with native skills field
13
13
  * - Built-in explore/shell subagents use prompt-based tool restriction
14
+ * - Built-in general-purpose replaces deepagents' auto-injected one (which carries
15
+ * no approval gate — see deepagents-profiles.ts for the suppression half)
16
+ * - Sub-agent backends are shell-capable outside plan mode (issue #248), mirroring
17
+ * the parent's backend selection in setup.ts
14
18
  * - Invalid configurations are logged and skipped (graceful degradation)
15
19
  * - Empty subagent list returns null (no subagents configured)
16
20
  */
17
21
 
18
- import { createDeepAgent, FilesystemBackend } from "deepagents";
22
+ import { createDeepAgent, FilesystemBackend, LocalShellBackend, DEFAULT_GENERAL_PURPOSE_DESCRIPTION, DEFAULT_SUBAGENT_PROMPT } from "deepagents";
19
23
  import type { CompiledSubAgent } from "deepagents";
20
24
  import type { StructuredTool } from "@langchain/core/tools";
21
25
  import type { RunnableConfig } from "@langchain/core/runnables";
@@ -26,7 +30,7 @@ import type { SubAgent, McpServerUsage } from "@stigmer/protos/ai/stigmer/agenti
26
30
  import type { WorkspaceBackend } from "../../shared/workspace/types.js";
27
31
  import type { StigmerClient } from "../../client/stigmer-client.js";
28
32
  import type { ApprovalGateConfig } from "../../middleware/approval-gate.js";
29
- import { CasCaptureFilesystemBackend } from "./cas-capture-backend.js";
33
+ import { createCasCaptureBackend } from "./cas-capture-backend.js";
30
34
  import type { CasCaptureObserver } from "./cas-capture-observer.js";
31
35
  import type { CostCapMiddleware, StigmerMiddleware } from "../../middleware/index.js";
32
36
  import { createThinkTool } from "../../middleware/index.js";
@@ -44,7 +48,11 @@ import {
44
48
  // Built-in subagent types and prompts
45
49
  // =========================================================================
46
50
 
47
- export const BUILTIN_SUBAGENT_TYPES: ReadonlySet<string> = new Set(["explore", "shell"]);
51
+ export const BUILTIN_SUBAGENT_TYPES: ReadonlySet<string> = new Set([
52
+ "explore",
53
+ "shell",
54
+ "general-purpose",
55
+ ]);
48
56
 
49
57
  const EXPLORE_SYSTEM_PROMPT = `\
50
58
  You are an exploration specialist. Your ONLY job is to explore codebases \
@@ -159,6 +167,13 @@ export interface SubagentTransformOptions {
159
167
  * model name string is passed to deepagents directly.
160
168
  */
161
169
  readonly modelFactory?: (modelName: string) => Promise<BaseChatModel>;
170
+ /**
171
+ * Per-execution env for the sub-agent `execute` tool, snapshotted in setup
172
+ * (see shell-env.ts). Presence is the single switch for shell capability:
173
+ * undefined (plan mode) compiles sub-agents onto non-shell filesystem
174
+ * backends, exactly like the parent's backend selection.
175
+ */
176
+ readonly shellEnv?: Record<string, string>;
162
177
  }
163
178
 
164
179
  // =========================================================================
@@ -166,18 +181,28 @@ export interface SubagentTransformOptions {
166
181
  // =========================================================================
167
182
 
168
183
  /**
169
- * Create built-in explore and shell subagent specifications.
184
+ * Create built-in explore, shell, and general-purpose subagent specifications.
170
185
  *
171
- * Built-in subagents receive:
172
- * - The full deepagents built-in tool set (from FilesystemBackend) restricted via prompt
186
+ * explore and shell receive:
187
+ * - The full deepagents built-in tool set restricted via prompt
173
188
  * - Purpose-built system prompts with explicit scope boundaries
174
189
  * - No skills, no MCP tools, no parent prompt inheritance
175
190
  *
191
+ * general-purpose is the gated replacement for the sub-agent deepagents would
192
+ * otherwise auto-inject WITHOUT our approval gate (suppressed process-wide in
193
+ * deepagents-profiles.ts; supplying our own under the same name is the
194
+ * documented override path). It uses deepagents' own description/prompt so the
195
+ * model's delegation behavior is unchanged, and receives the parent's MCP tools
196
+ * for capability parity with the injected original. Conscious simplification:
197
+ * the parent's skills prompt is NOT inherited — a sub-agent needing skills
198
+ * should be declared explicitly in the Agent spec.
199
+ *
176
200
  * Returns an empty array if no workspace is configured (subagents need
177
201
  * workspace tools to be useful).
178
202
  */
179
203
  export function createBuiltinSubagents(
180
204
  hasWorkspace: boolean,
205
+ parentMcpTools: readonly StructuredTool[] = [],
181
206
  ): TransformedSubagent[] {
182
207
  if (!hasWorkspace) {
183
208
  return [];
@@ -185,7 +210,17 @@ export function createBuiltinSubagents(
185
210
 
186
211
  const result: TransformedSubagent[] = [];
187
212
 
188
- for (const subagentType of ["explore", "shell"] as const) {
213
+ for (const subagentType of ["explore", "shell", "general-purpose"] as const) {
214
+ if (subagentType === "general-purpose") {
215
+ result.push({
216
+ name: "general-purpose",
217
+ description: DEFAULT_GENERAL_PURPOSE_DESCRIPTION,
218
+ systemPrompt: DEFAULT_SUBAGENT_PROMPT + RESPONSE_RULES,
219
+ tools: [...parentMcpTools],
220
+ });
221
+ continue;
222
+ }
223
+
189
224
  const prompt = BUILTIN_PROMPTS.get(subagentType);
190
225
  const description = BUILTIN_DESCRIPTIONS.get(subagentType);
191
226
  if (!prompt || !description) continue;
@@ -408,11 +443,51 @@ export function resolveSubagentSkillPrompt(
408
443
  // Compilation pipeline
409
444
  // =========================================================================
410
445
 
446
+ /**
447
+ * Select a sub-agent's deepagents backend, mirroring the parent's selection in
448
+ * setup.ts along two independent axes:
449
+ *
450
+ * - CAS observation (capture mode): when a shared observer is supplied, the
451
+ * backend records pre-turn bytes of CAS-owned paths so the sub-agent's
452
+ * gitignored writes fold into the parent turn's change set (DD-19). Without
453
+ * one, writes stay on the classic gitignored deny-gate.
454
+ * - Shell capability (issue #248): when `shellEnv` is present the backend
455
+ * implements deepagents' sandbox protocol so the `execute` tool exists
456
+ * (approval-gated by the sub-agent's own middleware). Absent (plan mode),
457
+ * the backend is filesystem-only — read-only by construction, matching the
458
+ * parent.
459
+ */
460
+ async function buildSubagentBackend(opts: {
461
+ readonly workspaceRootDir: string;
462
+ readonly casObserver?: CasCaptureObserver;
463
+ readonly shellEnv?: Record<string, string>;
464
+ }): Promise<FilesystemBackend> {
465
+ if (opts.casObserver) {
466
+ return createCasCaptureBackend({
467
+ rootDir: opts.workspaceRootDir,
468
+ observer: opts.casObserver,
469
+ shellEnv: opts.shellEnv,
470
+ });
471
+ }
472
+
473
+ if (opts.shellEnv === undefined) {
474
+ return new FilesystemBackend({ rootDir: opts.workspaceRootDir });
475
+ }
476
+
477
+ const shellBackend = new LocalShellBackend({
478
+ rootDir: opts.workspaceRootDir,
479
+ env: opts.shellEnv,
480
+ });
481
+ await shellBackend.initialize();
482
+ return shellBackend;
483
+ }
484
+
411
485
  /**
412
486
  * Compile transformed subagent specifications into CompiledSubAgent instances.
413
487
  *
414
488
  * Each subagent gets:
415
- * - Its own agent graph (via createDeepAgent with FilesystemBackend sharing the workspace)
489
+ * - Its own agent graph sharing the parent's workspace root (shell-capable
490
+ * outside plan mode; see {@link buildSubagentBackend})
416
491
  * - Per-subagent middleware (loop detection, budget, truncation, cost cap view)
417
492
  * - Concurrency gating via shared SubAgentGate
418
493
  */
@@ -426,6 +501,8 @@ export async function compileSubagents(
426
501
  /** Shared CAS observer (capture mode only); see {@link SubagentTransformOptions.casObserver}. */
427
502
  readonly casObserver?: CasCaptureObserver;
428
503
  readonly modelFactory?: (modelName: string) => Promise<BaseChatModel>;
504
+ /** Shell env for `execute`; see {@link SubagentTransformOptions.shellEnv}. */
505
+ readonly shellEnv?: Record<string, string>;
429
506
  },
430
507
  ): Promise<CompiledSubAgent[]> {
431
508
  if (transformed.length === 0) return [];
@@ -453,15 +530,7 @@ export async function compileSubagents(
453
530
  // no-proxy paths).
454
531
  const model = opts.modelFactory ? await opts.modelFactory(modelName) : modelName;
455
532
 
456
- // Capture mode: a CAS-observing backend wired to the shared observer, so
457
- // the sub-agent's gitignored writes are captured (same class the parent
458
- // uses). Otherwise the plain backend + classic gitignored deny-gate.
459
- const backend = opts.casObserver
460
- ? new CasCaptureFilesystemBackend(
461
- { rootDir: opts.workspaceRootDir },
462
- { observer: opts.casObserver },
463
- )
464
- : new FilesystemBackend({ rootDir: opts.workspaceRootDir });
533
+ const backend = await buildSubagentBackend(opts);
465
534
 
466
535
  const agentGraph = await createDeepAgent({
467
536
  model,
@@ -469,7 +538,6 @@ export async function compileSubagents(
469
538
  tools: spec.tools.length > 0 ? spec.tools : undefined,
470
539
  middleware: middleware as unknown[],
471
540
  backend,
472
- generalPurposeAgent: false,
473
541
  } as Parameters<typeof createDeepAgent>[0]);
474
542
 
475
543
  const gatedRunnable = gate.wrapRunnable(
@@ -507,7 +575,7 @@ export async function compileSubagents(
507
575
  * Transform proto SubAgents and compile into CompiledSubAgent instances.
508
576
  *
509
577
  * This is the main entry point called from setup.ts. It orchestrates:
510
- * 1. Built-in subagent creation (explore + shell)
578
+ * 1. Built-in subagent creation (explore, shell, general-purpose)
511
579
  * 2. Per-subagent transformation (proto → TransformedSubagent)
512
580
  * 3. MCP tool filtering per subagent
513
581
  * 4. Skill resolution and prompt injection (Session 2)
@@ -531,6 +599,7 @@ export async function transformAndCompileSubagents(
531
599
  parentHasNativeThinking,
532
600
  costCap,
533
601
  modelFactory,
602
+ shellEnv,
534
603
  } = options;
535
604
 
536
605
  if (subAgents.length === 0 && !workspaceBackend.rootDir) {
@@ -543,7 +612,10 @@ export async function transformAndCompileSubagents(
543
612
  );
544
613
 
545
614
  // Step 1: Create built-in subagents
546
- const builtins = createBuiltinSubagents(!!workspaceBackend.rootDir);
615
+ const builtins = createBuiltinSubagents(
616
+ !!workspaceBackend.rootDir,
617
+ parentMcpTools,
618
+ );
547
619
 
548
620
  // Step 1b: Batch fetch all skills referenced by subagents
549
621
  const allSkillRefs = collectAllSkillRefs(subAgents);
@@ -645,6 +717,7 @@ export async function transformAndCompileSubagents(
645
717
  workspaceRootDir: workspaceBackend.rootDir,
646
718
  casObserver,
647
719
  modelFactory,
720
+ shellEnv,
648
721
  });
649
722
 
650
723
  if (compiled.length === 0) {
@@ -195,6 +195,11 @@ export async function createStigmerRunnerManager(
195
195
  ): Promise<StigmerRunnerManager> {
196
196
  validateManagerOptions(options);
197
197
 
198
+ const { registerStigmerDeepagentsProfiles } = await import(
199
+ "./activities/execute-deep-agent/deepagents-profiles.js"
200
+ );
201
+ registerStigmerDeepagentsProfiles();
202
+
198
203
  // `tokenRef` holds the control-plane token (the host's durable credential,
199
204
  // e.g. the desktop's Auth0 token) and is shared with activity clients via
200
205
  // config.stigmerTokenRef. The proxy credential (x-stigmer-auth) is managed
package/src/runner.ts CHANGED
@@ -136,6 +136,11 @@ export async function createStigmerRunner(
136
136
  ): Promise<StigmerRunner> {
137
137
  validateOptions(options);
138
138
 
139
+ const { registerStigmerDeepagentsProfiles } = await import(
140
+ "./activities/execute-deep-agent/deepagents-profiles.js"
141
+ );
142
+ registerStigmerDeepagentsProfiles();
143
+
139
144
  const baseConfig = mapOptionsToConfig(options);
140
145
 
141
146
  // Install the Cursor SDK interceptors BEFORE resolving Temporal coordinates.