@stigmer/runner 3.1.3 → 3.1.5

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 (114) hide show
  1. package/dist/.build-fingerprint +1 -1
  2. package/dist/activities/execute-cursor/attachment-resolver.js +1 -1
  3. package/dist/activities/execute-cursor/attachment-resolver.js.map +1 -1
  4. package/dist/activities/execute-cursor/capture-flow.d.ts +26 -15
  5. package/dist/activities/execute-cursor/capture-flow.js +56 -18
  6. package/dist/activities/execute-cursor/capture-flow.js.map +1 -1
  7. package/dist/activities/execute-cursor/command-provenance.d.ts +11 -25
  8. package/dist/activities/execute-cursor/command-provenance.js +25 -115
  9. package/dist/activities/execute-cursor/command-provenance.js.map +1 -1
  10. package/dist/activities/execute-cursor/index.js +297 -478
  11. package/dist/activities/execute-cursor/index.js.map +1 -1
  12. package/dist/activities/execute-cursor/skill-resolver.js +1 -1
  13. package/dist/activities/execute-cursor/skill-resolver.js.map +1 -1
  14. package/dist/activities/execute-cursor/todo-tracker.d.ts +6 -1
  15. package/dist/activities/execute-cursor/todo-tracker.js +15 -43
  16. package/dist/activities/execute-cursor/todo-tracker.js.map +1 -1
  17. package/dist/activities/execute-cursor/turn-stream.d.ts +141 -0
  18. package/dist/activities/execute-cursor/turn-stream.js +249 -0
  19. package/dist/activities/execute-cursor/turn-stream.js.map +1 -0
  20. package/dist/activities/execute-deep-agent/command-provenance.d.ts +61 -0
  21. package/dist/activities/execute-deep-agent/command-provenance.js +72 -0
  22. package/dist/activities/execute-deep-agent/command-provenance.js.map +1 -0
  23. package/dist/activities/execute-deep-agent/index.js +88 -20
  24. package/dist/activities/execute-deep-agent/index.js.map +1 -1
  25. package/dist/activities/execute-deep-agent/status-builder.js +8 -1
  26. package/dist/activities/execute-deep-agent/status-builder.js.map +1 -1
  27. package/dist/activities/execute-deep-agent/v3-status-builder.js +12 -1
  28. package/dist/activities/execute-deep-agent/v3-status-builder.js.map +1 -1
  29. package/dist/activities/execute-deep-agent/writeback-coordinator.js +5 -1
  30. package/dist/activities/execute-deep-agent/writeback-coordinator.js.map +1 -1
  31. package/dist/otel.js +10 -0
  32. package/dist/otel.js.map +1 -1
  33. package/dist/shared/filereview/cas-progress.d.ts +63 -0
  34. package/dist/shared/filereview/cas-progress.js +128 -0
  35. package/dist/shared/filereview/cas-progress.js.map +1 -0
  36. package/dist/shared/filereview/cas-substrate.d.ts +29 -0
  37. package/dist/shared/filereview/cas-substrate.js +46 -21
  38. package/dist/shared/filereview/cas-substrate.js.map +1 -1
  39. package/dist/shared/filereview/command-provenance.d.ts +93 -0
  40. package/dist/shared/filereview/command-provenance.js +132 -0
  41. package/dist/shared/filereview/command-provenance.js.map +1 -0
  42. package/dist/shared/filereview/git-substrate.d.ts +9 -3
  43. package/dist/shared/filereview/git-substrate.js +4 -0
  44. package/dist/shared/filereview/git-substrate.js.map +1 -1
  45. package/dist/shared/filereview/index.d.ts +4 -3
  46. package/dist/shared/filereview/index.js +3 -2
  47. package/dist/shared/filereview/index.js.map +1 -1
  48. package/dist/shared/filereview/progress.d.ts +105 -34
  49. package/dist/shared/filereview/progress.js +96 -34
  50. package/dist/shared/filereview/progress.js.map +1 -1
  51. package/dist/shared/plan-mode-prompt.d.ts +9 -0
  52. package/dist/shared/plan-mode-prompt.js +18 -0
  53. package/dist/shared/plan-mode-prompt.js.map +1 -1
  54. package/dist/shared/todos.d.ts +56 -0
  55. package/dist/shared/todos.js +98 -0
  56. package/dist/shared/todos.js.map +1 -0
  57. package/dist/shared/tool-row.d.ts +16 -0
  58. package/dist/shared/tool-row.js +31 -0
  59. package/dist/shared/tool-row.js.map +1 -1
  60. package/dist/shared/workspace/git-identity.d.ts +36 -0
  61. package/dist/shared/workspace/git-identity.js +39 -0
  62. package/dist/shared/workspace/git-identity.js.map +1 -0
  63. package/dist/shared/workspace/local-backend.d.ts +6 -2
  64. package/dist/shared/workspace/local-backend.js +6 -2
  65. package/dist/shared/workspace/local-backend.js.map +1 -1
  66. package/dist/shared/workspace/stigmer-link.d.ts +54 -0
  67. package/dist/shared/workspace/stigmer-link.js +92 -0
  68. package/dist/shared/workspace/stigmer-link.js.map +1 -0
  69. package/dist/shared/workspace/types.d.ts +6 -4
  70. package/package.json +2 -2
  71. package/src/__tests__/otel-turn-span.test.ts +61 -0
  72. package/src/activities/execute-cursor/__tests__/progress-substrate.test.ts +169 -0
  73. package/src/activities/execute-cursor/__tests__/turn-stream.test.ts +349 -0
  74. package/src/activities/execute-cursor/attachment-resolver.ts +1 -1
  75. package/src/activities/execute-cursor/capture-flow.ts +71 -25
  76. package/src/activities/execute-cursor/command-provenance.ts +25 -120
  77. package/src/activities/execute-cursor/index.ts +345 -505
  78. package/src/activities/execute-cursor/skill-resolver.ts +1 -1
  79. package/src/activities/execute-cursor/todo-tracker.ts +17 -59
  80. package/src/activities/execute-cursor/turn-stream.ts +418 -0
  81. package/src/activities/execute-deep-agent/__tests__/command-provenance.test.ts +252 -0
  82. package/src/activities/execute-deep-agent/__tests__/status-builder.test.ts +78 -0
  83. package/src/activities/execute-deep-agent/__tests__/v3-status-builder.test.ts +105 -1
  84. package/src/activities/execute-deep-agent/__tests__/writeback-coordinator.test.ts +30 -3
  85. package/src/activities/execute-deep-agent/command-provenance.ts +102 -0
  86. package/src/activities/execute-deep-agent/index.ts +107 -20
  87. package/src/activities/execute-deep-agent/status-builder.ts +9 -0
  88. package/src/activities/execute-deep-agent/v3-status-builder.ts +13 -0
  89. package/src/activities/execute-deep-agent/writeback-coordinator.ts +5 -1
  90. package/src/otel.ts +8 -0
  91. package/src/shared/__tests__/todos.test.ts +216 -0
  92. package/src/shared/filereview/__tests__/cas-progress.test.ts +228 -0
  93. package/src/shared/filereview/__tests__/cas-substrate.test.ts +66 -0
  94. package/src/shared/filereview/__tests__/command-provenance.test.ts +252 -0
  95. package/src/shared/filereview/__tests__/progress.test.ts +112 -10
  96. package/src/shared/filereview/cas-progress.ts +170 -0
  97. package/src/shared/filereview/cas-substrate.ts +69 -24
  98. package/src/shared/filereview/command-provenance.ts +180 -0
  99. package/src/shared/filereview/git-substrate.ts +13 -3
  100. package/src/shared/filereview/index.ts +15 -1
  101. package/src/shared/filereview/progress.ts +171 -47
  102. package/src/shared/plan-mode-prompt.ts +18 -0
  103. package/src/shared/todos.ts +126 -0
  104. package/src/shared/tool-row.ts +34 -0
  105. package/src/shared/workspace/__tests__/git-identity.test.ts +124 -0
  106. package/src/shared/workspace/__tests__/stigmer-link.test.ts +173 -0
  107. package/src/shared/workspace/git-identity.ts +41 -0
  108. package/src/shared/workspace/local-backend.ts +6 -2
  109. package/src/shared/workspace/stigmer-link.ts +97 -0
  110. package/src/shared/workspace/types.ts +6 -4
  111. package/dist/activities/execute-cursor/stigmer-link.d.ts +0 -35
  112. package/dist/activities/execute-cursor/stigmer-link.js +0 -73
  113. package/dist/activities/execute-cursor/stigmer-link.js.map +0 -1
  114. package/src/activities/execute-cursor/stigmer-link.ts +0 -78
@@ -115,3 +115,19 @@ export declare function isToolCallRowHidden(tc: ToolCall): boolean;
115
115
  * none; Cursor clones them in) — the snapshot reflects whatever is present.
116
116
  */
117
117
  export declare function collectSubAgentToolCallIds(subAgents: readonly SubAgentExecution[]): Set<string>;
118
+ /**
119
+ * Collect the ids of tool-call rows that have already SETTLED (reached a terminal
120
+ * state: completed, failed, or skipped) in a transcript.
121
+ *
122
+ * The deep-agent turn-boundary provenance derivation (DD-28) snapshots this
123
+ * BEFORE a turn's stream to scope "this turn's tool calls" by identity: a call
124
+ * whose id is absent from the snapshot is this-turn's — either freshly streamed,
125
+ * or a prior gate that was WAITING_APPROVAL before the stream and executes now on
126
+ * resume (StatusBuilder updates the seeded row in place, so it keeps its id).
127
+ * Unlike the Cursor harness, the deep-agent cannot scope positionally — an
128
+ * approved command executes at its SEEDED position — so identity is the only
129
+ * correct scope, mirroring {@link collectSubAgentToolCallIds}. Terminal (not just
130
+ * completed) so a PRIOR turn's failed/skipped non-shell call is excluded and
131
+ * cannot spuriously disqualify this turn.
132
+ */
133
+ export declare function collectSettledToolCallIds(messages: readonly AgentMessage[]): Set<string>;
@@ -173,4 +173,35 @@ export function collectSubAgentToolCallIds(subAgents) {
173
173
  }
174
174
  return ids;
175
175
  }
176
+ /** Terminal tool-call statuses — a row that has finished (however it finished). */
177
+ const TERMINAL_TOOL_CALL_STATUSES = new Set([
178
+ ToolCallStatus.TOOL_CALL_COMPLETED,
179
+ ToolCallStatus.TOOL_CALL_FAILED,
180
+ ToolCallStatus.TOOL_CALL_SKIPPED,
181
+ ]);
182
+ /**
183
+ * Collect the ids of tool-call rows that have already SETTLED (reached a terminal
184
+ * state: completed, failed, or skipped) in a transcript.
185
+ *
186
+ * The deep-agent turn-boundary provenance derivation (DD-28) snapshots this
187
+ * BEFORE a turn's stream to scope "this turn's tool calls" by identity: a call
188
+ * whose id is absent from the snapshot is this-turn's — either freshly streamed,
189
+ * or a prior gate that was WAITING_APPROVAL before the stream and executes now on
190
+ * resume (StatusBuilder updates the seeded row in place, so it keeps its id).
191
+ * Unlike the Cursor harness, the deep-agent cannot scope positionally — an
192
+ * approved command executes at its SEEDED position — so identity is the only
193
+ * correct scope, mirroring {@link collectSubAgentToolCallIds}. Terminal (not just
194
+ * completed) so a PRIOR turn's failed/skipped non-shell call is excluded and
195
+ * cannot spuriously disqualify this turn.
196
+ */
197
+ export function collectSettledToolCallIds(messages) {
198
+ const ids = new Set();
199
+ for (const msg of messages) {
200
+ for (const tc of msg.toolCalls) {
201
+ if (TERMINAL_TOOL_CALL_STATUSES.has(tc.status))
202
+ ids.add(tc.id);
203
+ }
204
+ }
205
+ return ids;
206
+ }
176
207
  //# sourceMappingURL=tool-row.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"tool-row.js","sourceRoot":"","sources":["../../src/shared/tool-row.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"tool-row.js","sourceRoot":"","sources":["../../src/shared/tool-row.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * The public git identity for commits the agent authors on the user's behalf.
3
+ *
4
+ * Write-back commits (branch/commit/push/PR — see WriteBackCoordinator) land
5
+ * on real branches in the user's repository, so they carry a dedicated,
6
+ * recognizable agent identity in every mode — local and cloud alike. This is
7
+ * deliberate: a commit authored by the agent should never be silently
8
+ * attributed to whatever identity happens to live in the host's `~/.gitconfig`
9
+ * (local), and the cloud sandbox has no identity at all, which makes a bare
10
+ * `git commit` fail with "Author identity unknown".
11
+ *
12
+ * Distinct from the *internal* snapshot identity in
13
+ * `../filereview/git-substrate.ts` (`stigmer-runner <runner@stigmer.local>`):
14
+ * those commit objects are never on a branch and never pushed — they exist
15
+ * only to pin trees against GC. This identity is the public-facing one that
16
+ * appears in `git log`, on GitHub, and in pull requests.
17
+ */
18
+ export declare const AGENT_GIT_AUTHOR_NAME = "Stigmer Agent";
19
+ export declare const AGENT_GIT_AUTHOR_EMAIL = "noreply@stigmer.ai";
20
+ /**
21
+ * `git -c` config flags that pin the agent identity for a single command.
22
+ *
23
+ * Per-command `-c` flags (rather than repo-local or global `git config`) keep
24
+ * the identity deterministic in every environment without mutating the user's
25
+ * repository config — the same never-mutate reasoning documented on
26
+ * `SNAPSHOT_IDENTITY_ENV` in `git-substrate.ts`. Env-var injection is not an
27
+ * option here: `WorkspaceBackend.execute()` takes no env parameter, and
28
+ * widening that contract for one caller would be a larger change than the
29
+ * problem deserves.
30
+ */
31
+ export declare const AGENT_GIT_IDENTITY_FLAGS = "-c user.name='Stigmer Agent' -c user.email='noreply@stigmer.ai'";
32
+ /**
33
+ * Build a `git commit` command that is always authored by the agent identity,
34
+ * regardless of what identity (if any) the surrounding environment provides.
35
+ */
36
+ export declare function gitCommitAsAgent(commitMsg: string): string;
@@ -0,0 +1,39 @@
1
+ /**
2
+ * The public git identity for commits the agent authors on the user's behalf.
3
+ *
4
+ * Write-back commits (branch/commit/push/PR — see WriteBackCoordinator) land
5
+ * on real branches in the user's repository, so they carry a dedicated,
6
+ * recognizable agent identity in every mode — local and cloud alike. This is
7
+ * deliberate: a commit authored by the agent should never be silently
8
+ * attributed to whatever identity happens to live in the host's `~/.gitconfig`
9
+ * (local), and the cloud sandbox has no identity at all, which makes a bare
10
+ * `git commit` fail with "Author identity unknown".
11
+ *
12
+ * Distinct from the *internal* snapshot identity in
13
+ * `../filereview/git-substrate.ts` (`stigmer-runner <runner@stigmer.local>`):
14
+ * those commit objects are never on a branch and never pushed — they exist
15
+ * only to pin trees against GC. This identity is the public-facing one that
16
+ * appears in `git log`, on GitHub, and in pull requests.
17
+ */
18
+ export const AGENT_GIT_AUTHOR_NAME = "Stigmer Agent";
19
+ export const AGENT_GIT_AUTHOR_EMAIL = "noreply@stigmer.ai";
20
+ /**
21
+ * `git -c` config flags that pin the agent identity for a single command.
22
+ *
23
+ * Per-command `-c` flags (rather than repo-local or global `git config`) keep
24
+ * the identity deterministic in every environment without mutating the user's
25
+ * repository config — the same never-mutate reasoning documented on
26
+ * `SNAPSHOT_IDENTITY_ENV` in `git-substrate.ts`. Env-var injection is not an
27
+ * option here: `WorkspaceBackend.execute()` takes no env parameter, and
28
+ * widening that contract for one caller would be a larger change than the
29
+ * problem deserves.
30
+ */
31
+ export const AGENT_GIT_IDENTITY_FLAGS = `-c user.name='${AGENT_GIT_AUTHOR_NAME}' -c user.email='${AGENT_GIT_AUTHOR_EMAIL}'`;
32
+ /**
33
+ * Build a `git commit` command that is always authored by the agent identity,
34
+ * regardless of what identity (if any) the surrounding environment provides.
35
+ */
36
+ export function gitCommitAsAgent(commitMsg) {
37
+ return `git ${AGENT_GIT_IDENTITY_FLAGS} commit -m "${commitMsg}"`;
38
+ }
39
+ //# sourceMappingURL=git-identity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"git-identity.js","sourceRoot":"","sources":["../../../src/shared/workspace/git-identity.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,eAAe,CAAC;AACrD,MAAM,CAAC,MAAM,sBAAsB,GAAG,oBAAoB,CAAC;AAE3D;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,wBAAwB,GACnC,iBAAiB,qBAAqB,oBAAoB,sBAAsB,GAAG,CAAC;AAEtF;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAAC,SAAiB;IAChD,OAAO,OAAO,wBAAwB,eAAe,SAAS,GAAG,CAAC;AACpE,CAAC"}
@@ -6,8 +6,12 @@
6
6
  * sandbox) backend will be added in Phase 3.
7
7
  *
8
8
  * When `platformDir` is provided, paths under `.stigmer/` are
9
- * transparently routed to the platform directory. The agent sees a
10
- * unified namespace; the workspace directory is never polluted.
9
+ * transparently routed to the platform directory, keeping platform files
10
+ * out of the workspace tree. This routing serves the RUNNER's own I/O
11
+ * (skill/attachment materialization, `execute` command rewriting) — the
12
+ * agent's file tools do not go through this backend. Agent-visible reads
13
+ * of `.stigmer/…` reach the same physical files through the per-turn
14
+ * workspace symlink instead (see shared/workspace/stigmer-link.ts).
11
15
  */
12
16
  import type { WorkspaceBackend } from "./types.js";
13
17
  export declare class LocalWorkspaceBackend implements WorkspaceBackend {
@@ -6,8 +6,12 @@
6
6
  * sandbox) backend will be added in Phase 3.
7
7
  *
8
8
  * When `platformDir` is provided, paths under `.stigmer/` are
9
- * transparently routed to the platform directory. The agent sees a
10
- * unified namespace; the workspace directory is never polluted.
9
+ * transparently routed to the platform directory, keeping platform files
10
+ * out of the workspace tree. This routing serves the RUNNER's own I/O
11
+ * (skill/attachment materialization, `execute` command rewriting) — the
12
+ * agent's file tools do not go through this backend. Agent-visible reads
13
+ * of `.stigmer/…` reach the same physical files through the per-turn
14
+ * workspace symlink instead (see shared/workspace/stigmer-link.ts).
11
15
  */
12
16
  import { execFile } from "node:child_process";
13
17
  import { readFile, writeFile, access, mkdir } from "node:fs/promises";
@@ -1 +1 @@
1
- {"version":3,"file":"local-backend.js","sourceRoot":"","sources":["../../../src/shared/workspace/local-backend.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACtE,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,OAAO,EAAY,MAAM,WAAW,CAAC;AAEhE,OAAO,EACL,oBAAoB,EACpB,sBAAsB,EACtB,wBAAwB,GACzB,MAAM,qBAAqB,CAAC;AAE7B,MAAM,OAAO,qBAAqB;IACvB,OAAO,CAAS;IAChB,WAAW,CAAU;IAE9B,YAAY,OAAe,EAAE,WAAoB;QAC/C,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;QACvB,IAAI,CAAC,WAAW,GAAG,WAAW,CAAC;IACjC,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,OAAe,EAAE,OAA0B;QACvD,MAAM,GAAG,GAAG,OAAO,EAAE,GAAG;YACtB,CAAC,CAAC,CAAC,UAAU,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC;YAC3E,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC;QAEjB,IAAI,eAAe,GAAG,OAAO,CAAC;QAC9B,MAAM,GAAG,GAAuC,IAAI,CAAC,WAAW;YAC9D,CAAC,CAAC,EAAE,GAAG,OAAO,CAAC,GAA6B,EAAE,CAAC,wBAAwB,CAAC,EAAE,IAAI,CAAC,WAAW,EAAE;YAC5F,CAAC,CAAC,SAAS,CAAC;QAEd,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;YACrB,eAAe,GAAG,sBAAsB,CAAC,OAAO,CAAC,CAAC;QACpD,CAAC;QAED,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACrC,QAAQ,CACN,IAAI,EACJ,CAAC,IAAI,EAAE,eAAe,CAAC,EACvB,EAAE,GAAG,EAAE,SAAS,EAAE,EAAE,GAAG,IAAI,GAAG,IAAI,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAC7D,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE;gBACtB,IAAI,GAAG,EAAE,CAAC;oBACR,MAAM,CAAC,IAAI,KAAK,CAAC,mBAAmB,OAAO,KAAK,MAAM,IAAI,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;gBAC5E,CAAC;qBAAM,CAAC;oBACN,OAAO,CAAC,MAAM,CAAC,CAAC;gBAClB,CAAC;YACH,CAAC,CACF,CAAC;QACJ,CAAC,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,QAAQ,CAAC,IAAY;QACzB,MAAM,IAAI,GAAG,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;QACpC,OAAO,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IACjC,CAAC;IAED,KAAK,CAAC,SAAS,CAAC,IAAY,EAAE,OAAe;QAC3C,MAAM,IAAI,GAAG,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;QACpC,MAAM,IAAI,CAAC,eAAe,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QACvC,MAAM,SAAS,CAAC,IAAI,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;IAC1C,CAAC;IAED,KAAK,CAAC,eAAe,CAAC,IAAY,EAAE,OAAe;QACjD,MAAM,IAAI,GAAG,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;QACpC,MAAM,IAAI,CAAC,eAAe,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QACvC,MAAM,SAAS,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IACjC,CAAC;IAEO,KAAK,CAAC,eAAe,CAAC,YAAoB,EAAE,YAAoB;QACtE,IAAI,IAAI,CAAC,WAAW,IAAI,CAAC,UAAU,CAAC,YAAY,CAAC,EAAE,CAAC;YAClD,MAAM,EAAE,UAAU,EAAE,GAAG,oBAAoB,CAAC,YAAY,CAAC,CAAC;YAC1D,IAAI,UAAU,EAAE,CAAC;gBACf,MAAM,SAAS,GAAG,IAAI,CAAC,YAAY,EAAE,IAAI,CAAC,CAAC;gBAC3C,MAAM,KAAK,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YAC9C,CAAC;QACH,CAAC;IACH,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,IAAY;QACvB,MAAM,IAAI,GAAG,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;QACpC,IAAI,CAAC;YACH,MAAM,MAAM,CAAC,IAAI,CAAC,CAAC;YACnB,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IAED;;;;;;;;;OASG;IACK,WAAW,CAAC,IAAY;QAC9B,IAAI,UAAU,CAAC,IAAI,CAAC;YAAE,OAAO,IAAI,CAAC;QAElC,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;YACrB,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,GAAG,oBAAoB,CAAC,IAAI,CAAC,CAAC;YAC7D,IAAI,UAAU,EAAE,CAAC;gBACf,MAAM,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,WAAW,EAAE,SAAS,CAAC,CAAC;gBACtD,MAAM,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;gBACrD,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,kBAAkB,GAAG,GAAG,CAAC,IAAI,QAAQ,KAAK,kBAAkB,EAAE,CAAC;oBACtF,MAAM,IAAI,KAAK,CACb,6BAA6B,IAAI,uCAAuC,CACzE,CAAC;gBACJ,CAAC;gBACD,OAAO,QAAQ,CAAC;YAClB,CAAC;QACH,CAAC;QAED,OAAO,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IAClC,CAAC;CACF;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,wBAAwB,CAC5C,OAAe,EACf,WAAoB;IAEpB,MAAM,KAAK,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC1C,OAAO,IAAI,qBAAqB,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC;AACzD,CAAC"}
1
+ {"version":3,"file":"local-backend.js","sourceRoot":"","sources":["../../../src/shared/workspace/local-backend.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,54 @@
1
+ /**
2
+ * The workspace `.stigmer` symlink — the bridge from the session's workspace
3
+ * to its platform-managed directory (~/.stigmer/sessions/{id}/platform/, see
4
+ * platform-dir.ts), shared by BOTH harnesses.
5
+ *
6
+ * Platform-injected content (skills, attachment inputs, the approved plan)
7
+ * physically lives in the platform dir, outside the workspace, so a real repo
8
+ * is never polluted with Stigmer files (issue #173). But the agent reads from
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
12
+ * `.stigmer/…` paths those agents are prompted with (skill locations,
13
+ * `.stigmer/inputs/…` attachments) actually resolve.
14
+ *
15
+ * Lifecycle contract (same for both harnesses):
16
+ * - Created per turn, under the workspace turn lock — the link is a
17
+ * working-tree mutation, and re-pointing it while another session's turn is
18
+ * running on a shared tree would redirect that turn's reads mid-flight.
19
+ * Cursor creates it in its skill/attachment resolvers (which run after lock
20
+ * acquisition); the native harness creates it right after acquiring the
21
+ * lock (execute-deep-agent/index.ts).
22
+ * - Removed at turn end ({@link removeStigmerSymlink} in the activity's
23
+ * cleanup), so a real repo is left untouched once the turn ends; a
24
+ * multi-turn session recreates the link on the next turn.
25
+ * - {@link ensureStigmerSymlink} is idempotent, so multiple populaters may
26
+ * run in any order within a turn.
27
+ *
28
+ * OWNERSHIP SHARP EDGE: the platform owns the `.stigmer` name inside a
29
+ * workspace. {@link ensureStigmerSymlink} REPLACES a real (non-symlink)
30
+ * `.stigmer` directory it finds there — deliberate, so a directory left
31
+ * behind by an older runner can never shadow the platform mount. (A user's
32
+ * own `.stigmer` content is unreachable anyway: the runner's
33
+ * `LocalWorkspaceBackend` routes every `.stigmer/…` path to the platform
34
+ * dir.) Turn-end removal is the conservative half: it only ever removes a
35
+ * SYMLINK.
36
+ */
37
+ /** The workspace-visible name of the platform namespace. */
38
+ export declare const STIGMER_LOCAL_STATE_DIR = ".stigmer";
39
+ /**
40
+ * Ensure the workspace `.stigmer` symlink points to the platform dir.
41
+ * Idempotent: an existing correct link is kept; a stale link (or a non-link
42
+ * left behind by an older runner) is replaced.
43
+ */
44
+ export declare function ensureStigmerSymlink(workspaceDir: string, platformDir: string): Promise<void>;
45
+ /**
46
+ * Remove the workspace `.stigmer` symlink created by
47
+ * {@link ensureStigmerSymlink}.
48
+ *
49
+ * Called in the activity's cleanup so attaching a real repo leaves no Stigmer
50
+ * symlink behind once the turn ends (issue #173). Only ever removes a
51
+ * SYMLINK — a real `.stigmer` directory (which would be the user's own, not
52
+ * ours) is left untouched. Best-effort.
53
+ */
54
+ export declare function removeStigmerSymlink(workspaceDir: string): Promise<void>;
@@ -0,0 +1,92 @@
1
+ /**
2
+ * The workspace `.stigmer` symlink — the bridge from the session's workspace
3
+ * to its platform-managed directory (~/.stigmer/sessions/{id}/platform/, see
4
+ * platform-dir.ts), shared by BOTH harnesses.
5
+ *
6
+ * Platform-injected content (skills, attachment inputs, the approved plan)
7
+ * physically lives in the platform dir, outside the workspace, so a real repo
8
+ * is never polluted with Stigmer files (issue #173). But the agent reads from
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
12
+ * `.stigmer/…` paths those agents are prompted with (skill locations,
13
+ * `.stigmer/inputs/…` attachments) actually resolve.
14
+ *
15
+ * Lifecycle contract (same for both harnesses):
16
+ * - Created per turn, under the workspace turn lock — the link is a
17
+ * working-tree mutation, and re-pointing it while another session's turn is
18
+ * running on a shared tree would redirect that turn's reads mid-flight.
19
+ * Cursor creates it in its skill/attachment resolvers (which run after lock
20
+ * acquisition); the native harness creates it right after acquiring the
21
+ * lock (execute-deep-agent/index.ts).
22
+ * - Removed at turn end ({@link removeStigmerSymlink} in the activity's
23
+ * cleanup), so a real repo is left untouched once the turn ends; a
24
+ * multi-turn session recreates the link on the next turn.
25
+ * - {@link ensureStigmerSymlink} is idempotent, so multiple populaters may
26
+ * run in any order within a turn.
27
+ *
28
+ * OWNERSHIP SHARP EDGE: the platform owns the `.stigmer` name inside a
29
+ * workspace. {@link ensureStigmerSymlink} REPLACES a real (non-symlink)
30
+ * `.stigmer` directory it finds there — deliberate, so a directory left
31
+ * behind by an older runner can never shadow the platform mount. (A user's
32
+ * own `.stigmer` content is unreachable anyway: the runner's
33
+ * `LocalWorkspaceBackend` routes every `.stigmer/…` path to the platform
34
+ * dir.) Turn-end removal is the conservative half: it only ever removes a
35
+ * SYMLINK.
36
+ */
37
+ import { symlink, readlink, unlink, rm, lstat } from "node:fs/promises";
38
+ import { join } from "node:path";
39
+ /** The workspace-visible name of the platform namespace. */
40
+ export const STIGMER_LOCAL_STATE_DIR = ".stigmer";
41
+ /**
42
+ * Ensure the workspace `.stigmer` symlink points to the platform dir.
43
+ * Idempotent: an existing correct link is kept; a stale link (or a non-link
44
+ * left behind by an older runner) is replaced.
45
+ */
46
+ export async function ensureStigmerSymlink(workspaceDir, platformDir) {
47
+ const linkPath = join(workspaceDir, STIGMER_LOCAL_STATE_DIR);
48
+ try {
49
+ const existing = await readlink(linkPath);
50
+ if (existing === platformDir)
51
+ return;
52
+ await unlink(linkPath);
53
+ }
54
+ catch (err) {
55
+ if (err.code === "ENOENT") {
56
+ // No existing symlink
57
+ }
58
+ else if (err.code === "EINVAL") {
59
+ // Exists but is not a symlink — remove the directory
60
+ await rm(linkPath, { recursive: true, force: true });
61
+ }
62
+ else {
63
+ throw err;
64
+ }
65
+ }
66
+ await symlink(platformDir, linkPath, "dir");
67
+ }
68
+ /**
69
+ * Remove the workspace `.stigmer` symlink created by
70
+ * {@link ensureStigmerSymlink}.
71
+ *
72
+ * Called in the activity's cleanup so attaching a real repo leaves no Stigmer
73
+ * symlink behind once the turn ends (issue #173). Only ever removes a
74
+ * SYMLINK — a real `.stigmer` directory (which would be the user's own, not
75
+ * ours) is left untouched. Best-effort.
76
+ */
77
+ export async function removeStigmerSymlink(workspaceDir) {
78
+ const linkPath = join(workspaceDir, STIGMER_LOCAL_STATE_DIR);
79
+ try {
80
+ const stat = await lstat(linkPath);
81
+ if (stat.isSymbolicLink()) {
82
+ await unlink(linkPath);
83
+ }
84
+ }
85
+ catch (err) {
86
+ if (err?.code !== "ENOENT") {
87
+ console.warn(`removeStigmerSymlink: failed to remove ${linkPath} (non-fatal): ` +
88
+ `${err instanceof Error ? err.message : err}`);
89
+ }
90
+ }
91
+ }
92
+ //# sourceMappingURL=stigmer-link.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stigmer-link.js","sourceRoot":"","sources":["../../../src/shared/workspace/stigmer-link.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AAEH,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACxE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,4DAA4D;AAC5D,MAAM,CAAC,MAAM,uBAAuB,GAAG,UAAU,CAAC;AAElD;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,YAAoB,EACpB,WAAmB;IAEnB,MAAM,QAAQ,GAAG,IAAI,CAAC,YAAY,EAAE,uBAAuB,CAAC,CAAC;IAE7D,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,MAAM,QAAQ,CAAC,QAAQ,CAAC,CAAC;QAC1C,IAAI,QAAQ,KAAK,WAAW;YAAE,OAAO;QACrC,MAAM,MAAM,CAAC,QAAQ,CAAC,CAAC;IACzB,CAAC;IAAC,OAAO,GAAQ,EAAE,CAAC;QAClB,IAAI,GAAG,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YAC1B,sBAAsB;QACxB,CAAC;aAAM,IAAI,GAAG,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YACjC,qDAAqD;YACrD,MAAM,EAAE,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QACvD,CAAC;aAAM,CAAC;YACN,MAAM,GAAG,CAAC;QACZ,CAAC;IACH,CAAC;IAED,MAAM,OAAO,CAAC,WAAW,EAAE,QAAQ,EAAE,KAAK,CAAC,CAAC;AAC9C,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CAAC,YAAoB;IAC7D,MAAM,QAAQ,GAAG,IAAI,CAAC,YAAY,EAAE,uBAAuB,CAAC,CAAC;IAC7D,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,MAAM,KAAK,CAAC,QAAQ,CAAC,CAAC;QACnC,IAAI,IAAI,CAAC,cAAc,EAAE,EAAE,CAAC;YAC1B,MAAM,MAAM,CAAC,QAAQ,CAAC,CAAC;QACzB,CAAC;IACH,CAAC;IAAC,OAAO,GAAQ,EAAE,CAAC;QAClB,IAAI,GAAG,EAAE,IAAI,KAAK,QAAQ,EAAE,CAAC;YAC3B,OAAO,CAAC,IAAI,CACV,0CAA0C,QAAQ,gBAAgB;gBAClE,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,EAAE,CAC9C,CAAC;QACJ,CAAC;IACH,CAAC;AACH,CAAC"}
@@ -40,10 +40,12 @@ export declare class WorkspaceProvisionError extends Error {
40
40
  * commands route through the Daytona sandbox proxy.
41
41
  *
42
42
  * When `platformDir` is set, paths under `.stigmer/` are transparently
43
- * routed to the platform directory instead of the workspace root. This
44
- * keeps platform files (skills, inputs) physically separate from the
45
- * user's workspace while presenting a unified `.stigmer/` namespace to
46
- * the agent.
43
+ * routed to the platform directory instead of the workspace root, keeping
44
+ * platform files (skills, inputs) physically separate from the user's
45
+ * workspace. The routing covers the RUNNER's own reads/writes through
46
+ * this interface; the agent's file tools resolve against the workspace
47
+ * root and see the same files through the per-turn `.stigmer` symlink
48
+ * (see stigmer-link.ts).
47
49
  */
48
50
  export interface WorkspaceBackend {
49
51
  readonly rootDir: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stigmer/runner",
3
- "version": "3.1.3",
3
+ "version": "3.1.5",
4
4
  "description": "Embeddable Temporal worker for the Stigmer AI agent platform — handles agent execution, workflow orchestration, and MCP server management",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -85,7 +85,7 @@
85
85
  "@opentelemetry/sdk-metrics": "^2.0.0",
86
86
  "@opentelemetry/sdk-trace-base": "^2.0.0",
87
87
  "@opentelemetry/sdk-trace-node": "^2.0.0",
88
- "@stigmer/protos": "3.1.3",
88
+ "@stigmer/protos": "3.1.5",
89
89
  "@temporalio/activity": "^1.11.0",
90
90
  "@temporalio/client": "^1.11.0",
91
91
  "@temporalio/common": "^1.11.0",
@@ -0,0 +1,61 @@
1
+ import { describe, it, expect, vi, beforeEach } from "vitest";
2
+
3
+ // Spy span/tracer, hoisted so the vi.mock factory (which runs before imports)
4
+ // can close over them without a temporal-dead-zone error.
5
+ const { spanEnd, spanSetAttribute, startSpan } = vi.hoisted(() => {
6
+ const spanEnd = vi.fn();
7
+ const spanSetAttribute = vi.fn();
8
+ const startSpan = vi.fn(() => ({ end: spanEnd, setAttribute: spanSetAttribute }));
9
+ return { spanEnd, spanSetAttribute, startSpan };
10
+ });
11
+
12
+ vi.mock("@opentelemetry/api", () => ({
13
+ trace: { getTracer: () => ({ startSpan }) },
14
+ }));
15
+
16
+ import {
17
+ startCursorTurnSpan,
18
+ ATTR_LLM_INPUT_TOKENS,
19
+ ATTR_LLM_OUTPUT_TOKENS,
20
+ } from "../otel.js";
21
+
22
+ const spanAttrs = { model: "gpt-x", mode: "local", sessionId: "sess-1" };
23
+
24
+ describe("startCursorTurnSpan", () => {
25
+ beforeEach(() => {
26
+ spanEnd.mockClear();
27
+ spanSetAttribute.mockClear();
28
+ startSpan.mockClear();
29
+ });
30
+
31
+ it("records the input and output token attributes", async () => {
32
+ const span = await startCursorTurnSpan(spanAttrs);
33
+ span.setTokens(1200, 340);
34
+ expect(spanSetAttribute).toHaveBeenCalledWith(ATTR_LLM_INPUT_TOKENS, 1200);
35
+ expect(spanSetAttribute).toHaveBeenCalledWith(ATTR_LLM_OUTPUT_TOKENS, 340);
36
+ });
37
+
38
+ it("omits zero-valued token attributes (no count = no attribute)", async () => {
39
+ const span = await startCursorTurnSpan(spanAttrs);
40
+ span.setTokens(0, 0);
41
+ expect(spanSetAttribute).not.toHaveBeenCalled();
42
+ });
43
+
44
+ it("ends the span exactly once even when end() is called repeatedly", async () => {
45
+ // finishTurnTelemetry is invoked from the activity's finally on every exit
46
+ // path; a second end() (e.g. a defensive double-invoke) must be a no-op so
47
+ // OTel never warns about ending an already-ended span.
48
+ const span = await startCursorTurnSpan(spanAttrs);
49
+ span.end();
50
+ span.end();
51
+ span.end();
52
+ expect(spanEnd).toHaveBeenCalledTimes(1);
53
+ });
54
+
55
+ it("ignores setTokens after the span has ended (no late mutation)", async () => {
56
+ const span = await startCursorTurnSpan(spanAttrs);
57
+ span.end();
58
+ span.setTokens(1200, 340);
59
+ expect(spanSetAttribute).not.toHaveBeenCalled();
60
+ });
61
+ });
@@ -0,0 +1,169 @@
1
+ /**
2
+ * Unit tests for the Cursor mid-run progress substrate selection (DD-33) —
3
+ * `buildCursorProgressSubstrate`. This is the Cursor adapter's responsibility:
4
+ * pick git / non-git CAS / hybrid for the turn's workspace shape (the CAS + git
5
+ * capture behaviors themselves are covered in shared/filereview/__tests__).
6
+ *
7
+ * The chosen substrate is driven against a real temp workspace to prove WHICH
8
+ * kind was built: a git-only substrate surfaces the tracked change; a hybrid adds
9
+ * the (empty here) CAS slice; a non-git substrate reads the (empty) hook sidecar
10
+ * without touching git.
11
+ */
12
+
13
+ import { execFile } from "node:child_process";
14
+ import { mkdtemp, mkdir, rm, writeFile } from "node:fs/promises";
15
+ import { tmpdir } from "node:os";
16
+ import { join } from "node:path";
17
+ import { promisify } from "node:util";
18
+ import { afterEach, beforeEach, describe, expect, it } from "vitest";
19
+ import { buildCursorProgressSubstrate } from "../capture-flow.js";
20
+ import { snapshotBaseline, dropCaptureRefs } from "../../../shared/filereview/git-substrate.js";
21
+ import { makeInMemoryArtifactStorage } from "../../../__test-utils__/fake-artifact-storage.js";
22
+
23
+ const execFileAsync = promisify(execFile);
24
+ const EXEC_ID = "exec-cursor-progress";
25
+
26
+ let repo: string;
27
+ let hitlDir: string;
28
+
29
+ async function git(args: string[]): Promise<void> {
30
+ await execFileAsync("git", args, { cwd: repo });
31
+ }
32
+
33
+ async function write(rel: string, content: string): Promise<void> {
34
+ await mkdir(join(repo, rel, ".."), { recursive: true });
35
+ await writeFile(join(repo, rel), content, "utf-8");
36
+ }
37
+
38
+ beforeEach(async () => {
39
+ repo = await mkdtemp(join(tmpdir(), "cursor-progress-"));
40
+ hitlDir = await mkdtemp(join(tmpdir(), "cursor-progress-hitl-"));
41
+ await git(["init", "-q"]);
42
+ await git(["config", "user.email", "t@t.local"]);
43
+ await git(["config", "user.name", "t"]);
44
+ await write("src/main.ts", "export const x = 1;\n");
45
+ await git(["add", "-A"]);
46
+ await git(["commit", "-q", "-m", "initial"]);
47
+ });
48
+
49
+ afterEach(async () => {
50
+ await dropCaptureRefs(repo, EXEC_ID).catch(() => {});
51
+ await rm(repo, { recursive: true, force: true });
52
+ await rm(hitlDir, { recursive: true, force: true });
53
+ });
54
+
55
+ describe("buildCursorProgressSubstrate — selection", () => {
56
+ it("returns undefined outside capture mode", () => {
57
+ const sub = buildCursorProgressSubstrate({
58
+ captureMode: false,
59
+ gitWorkspace: true,
60
+ workspaceRoot: repo,
61
+ baselineTree: "sha",
62
+ executionId: EXEC_ID,
63
+ hitlDir,
64
+ storage: undefined,
65
+ });
66
+ expect(sub).toBeUndefined();
67
+ });
68
+
69
+ it("returns undefined with no workspace root", () => {
70
+ const sub = buildCursorProgressSubstrate({
71
+ captureMode: true,
72
+ gitWorkspace: true,
73
+ workspaceRoot: undefined,
74
+ baselineTree: "sha",
75
+ executionId: EXEC_ID,
76
+ hitlDir,
77
+ storage: undefined,
78
+ });
79
+ expect(sub).toBeUndefined();
80
+ });
81
+
82
+ it("returns undefined for a git tree with no baseline tree yet", () => {
83
+ const sub = buildCursorProgressSubstrate({
84
+ captureMode: true,
85
+ gitWorkspace: true,
86
+ workspaceRoot: repo,
87
+ baselineTree: undefined,
88
+ executionId: EXEC_ID,
89
+ hitlDir,
90
+ storage: undefined,
91
+ });
92
+ expect(sub).toBeUndefined();
93
+ });
94
+
95
+ it("returns undefined for a non-git workspace with no storage/sidecar", () => {
96
+ const sub = buildCursorProgressSubstrate({
97
+ captureMode: true,
98
+ gitWorkspace: false,
99
+ workspaceRoot: repo,
100
+ baselineTree: undefined,
101
+ executionId: EXEC_ID,
102
+ hitlDir: undefined,
103
+ storage: undefined,
104
+ });
105
+ expect(sub).toBeUndefined();
106
+ });
107
+
108
+ it("builds a git-only substrate (no storage) that surfaces the tracked change", async () => {
109
+ const baselineTree = await snapshotBaseline(repo, EXEC_ID);
110
+ const sub = buildCursorProgressSubstrate({
111
+ captureMode: true,
112
+ gitWorkspace: true,
113
+ workspaceRoot: repo,
114
+ baselineTree,
115
+ executionId: EXEC_ID,
116
+ hitlDir,
117
+ storage: undefined,
118
+ });
119
+ expect(sub).toBeDefined();
120
+
121
+ await write("src/main.ts", "export const x = 2;\nexport const y = 3;\n");
122
+ const { delta, changed } = await sub!.capture();
123
+ expect(changed).toBe(true);
124
+ expect(delta.entries.some((e) => e.pathAfter === "src/main.ts")).toBe(true);
125
+ });
126
+
127
+ it("builds a hybrid substrate (git + storage) surfacing the git slice + empty CAS", async () => {
128
+ const baselineTree = await snapshotBaseline(repo, EXEC_ID);
129
+ const { storage } = makeInMemoryArtifactStorage();
130
+ const sub = buildCursorProgressSubstrate({
131
+ captureMode: true,
132
+ gitWorkspace: true,
133
+ workspaceRoot: repo,
134
+ baselineTree,
135
+ executionId: EXEC_ID,
136
+ hitlDir,
137
+ storage,
138
+ });
139
+ expect(sub).toBeDefined();
140
+
141
+ await write("src/main.ts", "export const x = 9;\n");
142
+ const { delta } = await sub!.capture();
143
+ // The git slice contributes the tracked change; the CAS sidecar is empty, so
144
+ // it adds nothing — total equals the git entries.
145
+ expect(delta.entries.some((e) => e.pathAfter === "src/main.ts")).toBe(true);
146
+ expect(delta.totalFilesChanged).toBe(delta.entries.length);
147
+ });
148
+
149
+ it("builds a non-git CAS substrate that reads the (empty) sidecar without git", async () => {
150
+ const { storage } = makeInMemoryArtifactStorage();
151
+ const sub = buildCursorProgressSubstrate({
152
+ captureMode: true,
153
+ gitWorkspace: false,
154
+ workspaceRoot: repo,
155
+ baselineTree: undefined,
156
+ executionId: EXEC_ID,
157
+ hitlDir,
158
+ storage,
159
+ });
160
+ expect(sub).toBeDefined();
161
+
162
+ // Even with a git change on disk, the non-git substrate ignores git entirely;
163
+ // the sidecar is empty, so it reports zero changed files.
164
+ await write("src/main.ts", "export const x = 42;\n");
165
+ const { delta } = await sub!.capture();
166
+ expect(delta.totalFilesChanged).toBe(0);
167
+ expect(delta.entries).toHaveLength(0);
168
+ });
169
+ });