@stigmer/runner 3.32.0 → 3.34.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 (53) hide show
  1. package/dist/.build-fingerprint +1 -1
  2. package/dist/activities/execute-deep-agent/cas-capture-backend.d.ts +13 -6
  3. package/dist/activities/execute-deep-agent/cas-capture-backend.js +21 -5
  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 +21 -7
  6. package/dist/activities/execute-deep-agent/deepagents-profiles.js +24 -9
  7. package/dist/activities/execute-deep-agent/deepagents-profiles.js.map +1 -1
  8. package/dist/activities/execute-deep-agent/platform-route.d.ts +75 -0
  9. package/dist/activities/execute-deep-agent/platform-route.js +107 -0
  10. package/dist/activities/execute-deep-agent/platform-route.js.map +1 -0
  11. package/dist/activities/execute-deep-agent/subagent-transformer.d.ts +2 -0
  12. package/dist/activities/execute-deep-agent/subagent-transformer.js +29 -5
  13. package/dist/activities/execute-deep-agent/subagent-transformer.js.map +1 -1
  14. package/dist/activities/execute-deep-agent/subagent-wiring.d.ts +5 -7
  15. package/dist/activities/execute-deep-agent/subagent-wiring.js +5 -7
  16. package/dist/activities/execute-deep-agent/subagent-wiring.js.map +1 -1
  17. package/dist/activities/execute-deep-agent/translator.d.ts +6 -2
  18. package/dist/activities/execute-deep-agent/translator.js +6 -2
  19. package/dist/activities/execute-deep-agent/translator.js.map +1 -1
  20. package/dist/activities/execute-deep-agent/turn-setup.js +14 -3
  21. package/dist/activities/execute-deep-agent/turn-setup.js.map +1 -1
  22. package/dist/middleware/index.d.ts +7 -3
  23. package/dist/middleware/index.js +7 -3
  24. package/dist/middleware/index.js.map +1 -1
  25. package/dist/shared/workspace/stigmer-link.d.ts +15 -3
  26. package/dist/shared/workspace/stigmer-link.js +22 -3
  27. package/dist/shared/workspace/stigmer-link.js.map +1 -1
  28. package/dist/shared/workspace/types.d.ts +4 -3
  29. package/package.json +15 -9
  30. package/src/activities/execute-deep-agent/__tests__/builtin-tool-surface.test.ts +107 -0
  31. package/src/activities/execute-deep-agent/__tests__/cas-capture-backend.test.ts +14 -0
  32. package/src/activities/execute-deep-agent/__tests__/hermetic/fixtures/paused-session.deepagents-1.10.2.json +592 -0
  33. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/sub-agent-delegation.status.json +2 -2
  34. package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/tool-call.status.json +1 -1
  35. package/src/activities/execute-deep-agent/__tests__/hermetic/plain-turn.test.ts +10 -13
  36. package/src/activities/execute-deep-agent/__tests__/hermetic/resume-across-engine-upgrade.test.ts +374 -0
  37. package/src/activities/execute-deep-agent/__tests__/hermetic/sub-agent-delegation.test.ts +7 -0
  38. package/src/activities/execute-deep-agent/__tests__/hermetic/tool-call.test.ts +6 -0
  39. package/src/activities/execute-deep-agent/__tests__/plan-mode-path-normalization.test.ts +16 -8
  40. package/src/activities/execute-deep-agent/__tests__/platform-route.test.ts +148 -0
  41. package/src/activities/execute-deep-agent/__tests__/subagent-transformer.test.ts +24 -0
  42. package/src/activities/execute-deep-agent/__tests__/summarization-verification.test.ts +16 -9
  43. package/src/activities/execute-deep-agent/cas-capture-backend.ts +25 -6
  44. package/src/activities/execute-deep-agent/deepagents-profiles.ts +25 -9
  45. package/src/activities/execute-deep-agent/platform-route.ts +134 -0
  46. package/src/activities/execute-deep-agent/subagent-transformer.ts +40 -6
  47. package/src/activities/execute-deep-agent/subagent-wiring.ts +5 -7
  48. package/src/activities/execute-deep-agent/translator.ts +6 -2
  49. package/src/activities/execute-deep-agent/turn-setup.ts +17 -3
  50. package/src/middleware/index.ts +7 -3
  51. package/src/shared/workspace/__tests__/stigmer-link.test.ts +41 -18
  52. package/src/shared/workspace/stigmer-link.ts +22 -3
  53. package/src/shared/workspace/types.ts +4 -3
@@ -40,6 +40,7 @@ import type { BaseCheckpointSaver } from "@langchain/langgraph-checkpoint";
40
40
  import type { DynamicStructuredTool } from "@langchain/core/tools";
41
41
  import type { Command } from "@langchain/langgraph";
42
42
  import { createDeepAgent } from "deepagents";
43
+ import { todoListMiddleware } from "langchain";
43
44
  import { InteractionMode } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
44
45
 
45
46
  import type { Config } from "../../config.js";
@@ -71,6 +72,7 @@ import { resolveRecursionLimit, UNBOUNDED_ADVISORY_RECURSION_LIMIT } from "../..
71
72
  import { jsonSchemaToZod } from "../../shared/json-schema-to-zod.js";
72
73
  import { CasCaptureObserver } from "./cas-capture-observer.js";
73
74
  import { createCasCaptureBackend } from "./cas-capture-backend.js";
75
+ import { mountPlatformRoute } from "./platform-route.js";
74
76
  import { resolveResumeInput, type GraphStateSnapshot } from "./hitl.js";
75
77
  import { buildEnhancedSystemPrompt, composeUserMessage, renderSkillsSection } from "./prompt-builder.js";
76
78
  import { buildShellEnv } from "./shell-env.js";
@@ -454,8 +456,14 @@ export async function buildEngine(
454
456
  // File capture point: the CAS-observing backend — git-tracked edits flow to
455
457
  // disk (the boundary's git diff is authoritative) and the shared observer
456
458
  // records the pre-turn bytes of every CAS-owned path. Gate-independent, so
457
- // it holds even under the global bypass.
458
- const fileBackend = await createCasCaptureBackend({ rootDir: primaryDir, observer: workspace.casObserver, shellEnv });
459
+ // it holds even under the global bypass. Platform content (skills, inputs,
460
+ // the approved plan) is mounted over it read-only at `.stigmer/`
461
+ // (`platform-route.ts`): the virtual-rooted backend refuses to follow the
462
+ // workspace link out of the root.
463
+ const fileBackend = await mountPlatformRoute(
464
+ await createCasCaptureBackend({ rootDir: primaryDir, observer: workspace.casObserver, shellEnv }),
465
+ { workspaceDir: primaryDir, platformDir: workspace.backend.platformDir },
466
+ );
459
467
  const graph = await createDeepAgent({
460
468
  model,
461
469
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -463,8 +471,14 @@ export async function buildEngine(
463
471
  backend: fileBackend,
464
472
  systemPrompt,
465
473
  tools: graphTools,
474
+ // The to-do list rides ahead of Stigmer's stack: deepagents stopped
475
+ // installing it by default (1.12), and the product reads it — the parent's
476
+ // `write_todos` is what `status.todos` projects and what plan mode's
477
+ // build progress is instructed through (`shared/implement-plan-prompt.ts`).
478
+ // The framework's own middleware, with its default texts. Parent only: a
479
+ // sub-agent's list never reaches the execution (`harness/transcript/builder.ts`).
466
480
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
467
- middleware: middleware as any,
481
+ middleware: [todoListMiddleware(), ...middleware] as any,
468
482
  subagents: compiledSubagents ?? undefined,
469
483
  ...(responseFormat ? { responseFormat } : {}),
470
484
  ...(planModePermissions ? { permissions: planModePermissions } : {}),
@@ -4,9 +4,9 @@
4
4
  * Assembles the ordered middleware array from a MiddlewareStackConfig. The
5
5
  * order is load-bearing and reads top to bottom:
6
6
  *
7
- * 0. Path normalization (conditional: only on permission-rule-bearing
8
- * graphs — FIRST, so every downstream middleware observes canonical
9
- * workspace-absolute paths)
7
+ * 0. Path normalization (whenever configured, which every graph does
8
+ * since issue #754 — FIRST, so every downstream middleware observes
9
+ * canonical workspace-absolute paths)
10
10
  * 1. Loop detection (always)
11
11
  * 2. Execution budget (always — advises at ~80% of the tool-round budget
12
12
  * that LangGraph's `recursionLimit` enforces)
@@ -25,6 +25,10 @@
25
25
  * graceful-stop middleware and the cost cap's tool block were second stops
26
26
  * inside the graph that the runtime could not see).
27
27
  *
28
+ * Ahead of this stack the parent graph adds the framework's to-do list
29
+ * middleware (`execute-deep-agent/turn-setup.ts`): it is langchain's, not
30
+ * Stigmer's, and only the parent carries it.
31
+ *
28
32
  * The sub-agent stack is `execute-deep-agent/subagent-wiring.ts`'s: the same
29
33
  * order over the parent's shared instances where one exists (the advisory's
30
34
  * running total), fresh ones otherwise.
@@ -2,25 +2,29 @@ import { describe, it, expect, beforeEach, afterEach } from "vitest";
2
2
  import { mkdtemp, mkdir, rm, writeFile, readFile, lstat, readlink } from "node:fs/promises";
3
3
  import { tmpdir } from "node:os";
4
4
  import { join } from "node:path";
5
- import { FilesystemBackend } from "deepagents";
6
5
 
7
6
  import {
8
7
  ensureStigmerSymlink,
9
8
  removeStigmerSymlink,
9
+ stigmerSymlinkPointsAt,
10
10
  STIGMER_LOCAL_STATE_DIR,
11
11
  } from "../stigmer-link.js";
12
12
  import { LocalWorkspaceBackend } from "../local-backend.js";
13
13
 
14
14
  /**
15
15
  * The `.stigmer` symlink is the bridge that makes platform-mounted content
16
- * (approved plan, skills, attachment inputs) readable by the agent's file
17
- * tools in BOTH harnesses. The lifecycle tests pin the ownership contract
18
- * (ensure replaces, remove is symlink-only); the convergence tests pin the
19
- * end-to-end physics the native harness depends on: content written through
20
- * the platform-routing LocalWorkspaceBackend must be readable at the same
21
- * `.stigmer/…` path through a stock deepagents FilesystemBackend rooted at
22
- * the workspace — which is exactly what broke when the native harness had no
23
- * symlink (the "plan file doesn't exist" bug).
16
+ * (approved plan, skills, attachment inputs) readable at `.stigmer/…` by
17
+ * whatever reads from the workspace: the Cursor SDK and shell commands on
18
+ * both harnesses. The lifecycle tests pin the ownership contract (ensure
19
+ * replaces, remove is symlink-only); the convergence tests pin the physics:
20
+ * content written through the platform-routing LocalWorkspaceBackend is
21
+ * readable at the same `.stigmer/…` path from inside the workspace once the
22
+ * link exists, and not before (the "plan file doesn't exist" bug).
23
+ *
24
+ * The native harness's FILE tools do not read through the link — their
25
+ * virtual-rooted backend refuses a path whose real location leaves the
26
+ * workspace — so their side of the convergence is pinned on the production
27
+ * backends in `activities/execute-deep-agent/__tests__/platform-route.test.ts`.
24
28
  */
25
29
  describe("stigmer-link", () => {
26
30
  let workspaceDir: string;
@@ -104,16 +108,35 @@ describe("stigmer-link", () => {
104
108
  });
105
109
  });
106
110
 
107
- // ── Write/read convergence (the native-harness bug, pinned) ─────────
111
+ describe("stigmerSymlinkPointsAt", () => {
112
+ it("is true only while the link points at this platform dir", async () => {
113
+ expect(await stigmerSymlinkPointsAt(workspaceDir, platformDir), "no link yet").toBe(false);
114
+ await ensureStigmerSymlink(workspaceDir, platformDir);
115
+ expect(await stigmerSymlinkPointsAt(workspaceDir, platformDir)).toBe(true);
116
+ expect(await stigmerSymlinkPointsAt(workspaceDir, join(platformDir, "elsewhere")), "another dir").toBe(false);
117
+ await removeStigmerSymlink(workspaceDir);
118
+ expect(await stigmerSymlinkPointsAt(workspaceDir, platformDir), "removed at turn end").toBe(false);
119
+ });
120
+
121
+ it("is false for a real .stigmer directory", async () => {
122
+ await mkdir(linkPath());
123
+ expect(await stigmerSymlinkPointsAt(workspaceDir, platformDir)).toBe(false);
124
+ });
125
+ });
126
+
127
+ // ── Write/read convergence (the link, pinned) ─────────
108
128
 
109
129
  describe("platform write / agent read convergence", () => {
110
- /** Read through the agent's actual file-tool backend. */
111
- function agentRead(path: string): Promise<{ content?: string; error?: string }> {
112
- const agentBackend = new FilesystemBackend({ rootDir: workspaceDir });
113
- return agentBackend.read(path) as Promise<{ content?: string; error?: string }>;
130
+ /** Read the way a process in the workspace does (the shell, the Cursor SDK): a path relative to its CWD. */
131
+ async function agentRead(path: string): Promise<{ content?: string; error?: string }> {
132
+ try {
133
+ return { content: await readFile(join(workspaceDir, path), "utf8") };
134
+ } catch (err) {
135
+ return { error: err instanceof Error ? err.message : String(err) };
136
+ }
114
137
  }
115
138
 
116
- it("an approved plan mounted under .stigmer/inputs is readable by the deepagents backend once the link exists", async () => {
139
+ it("an approved plan mounted under .stigmer/inputs is readable from the workspace once the link exists", async () => {
117
140
  const planFileName = "notes_ab12cd34.plan.md";
118
141
  const planText = "# The Approved Plan\n\nStep 1: do the thing.\n";
119
142
 
@@ -125,9 +148,9 @@ describe("stigmer-link", () => {
125
148
  const workspaceBackend = new LocalWorkspaceBackend(workspaceDir, platformDir);
126
149
  await workspaceBackend.writeFile(`.stigmer/inputs/${planFileName}`, planText);
127
150
 
128
- // Without the symlink the agent cannot see the plan — the bug this
129
- // module fixes for the native harness. Pinned so the link stays
130
- // load-bearing in reviewers' and agents' mental models.
151
+ // Without the symlink nothing in the workspace can see the plan — the
152
+ // bug this module fixes. Pinned so the link stays load-bearing in
153
+ // reviewers' and agents' mental models.
131
154
  const before = await agentRead(`.stigmer/inputs/${planFileName}`);
132
155
  expect(before.content ?? "").not.toContain("The Approved Plan");
133
156
 
@@ -7,10 +7,14 @@
7
7
  * physically lives in the platform dir, outside the workspace, so a real repo
8
8
  * is never polluted with Stigmer files (issue #173). But the agent reads from
9
9
  * the workspace: the Cursor SDK resolves paths against the workspace CWD, and
10
- * the native (deepagents) harness's file tools resolve against a
11
- * `FilesystemBackend` rooted at the workspace. The symlink is what makes the
10
+ * shell commands on both harnesses run there. The symlink is what makes the
12
11
  * `.stigmer/…` paths those agents are prompted with (skill locations,
13
- * `.stigmer/inputs/…` attachments) actually resolve.
12
+ * `.stigmer/inputs/…` attachments) actually resolve. The native harness's
13
+ * FILE tools are the exception: their virtual-rooted backend refuses a path
14
+ * whose real location leaves the workspace, so they read the platform dir
15
+ * through a read-only `.stigmer/` route instead
16
+ * (`activities/execute-deep-agent/platform-route.ts`), mounted on exactly
17
+ * the turns this link exists ({@link stigmerSymlinkPointsAt}).
14
18
  *
15
19
  * Lifecycle contract (same for both harnesses):
16
20
  * - Created per turn, under the workspace turn lock — the link is a
@@ -70,6 +74,21 @@ export async function ensureStigmerSymlink(
70
74
  await symlink(platformDir, linkPath, "dir");
71
75
  }
72
76
 
77
+ /**
78
+ * Whether the workspace's `.stigmer` is, right now, the link
79
+ * {@link ensureStigmerSymlink} makes to `platformDir`. A turn that mounts no
80
+ * skill and no attachment creates no link, so this is also "does this turn
81
+ * expose platform content at all" — the native harness mounts its read-only
82
+ * `.stigmer/` route on exactly the turns where the link would resolve.
83
+ */
84
+ export async function stigmerSymlinkPointsAt(workspaceDir: string, platformDir: string): Promise<boolean> {
85
+ try {
86
+ return (await readlink(join(workspaceDir, STIGMER_LOCAL_STATE_DIR))) === platformDir;
87
+ } catch {
88
+ return false;
89
+ }
90
+ }
91
+
73
92
  /**
74
93
  * Remove the workspace `.stigmer` symlink created by
75
94
  * {@link ensureStigmerSymlink}.
@@ -54,9 +54,10 @@ export class WorkspaceProvisionError extends Error {
54
54
  * routed to the platform directory instead of the workspace root, keeping
55
55
  * platform files (skills, inputs) physically separate from the user's
56
56
  * workspace. The routing covers the RUNNER's own reads/writes through
57
- * this interface; the agent's file tools resolve against the workspace
58
- * root and see the same files through the per-turn `.stigmer` symlink
59
- * (see stigmer-link.ts).
57
+ * this interface; the agent sees the same files through the per-turn
58
+ * `.stigmer` symlink (see stigmer-link.ts), and the native file tools
59
+ * through their read-only `.stigmer/` route over the same dir
60
+ * (`activities/execute-deep-agent/platform-route.ts`).
60
61
  */
61
62
  export interface WorkspaceBackend {
62
63
  readonly rootDir: string;