@stigmer/runner 3.1.2 → 3.1.3

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 (77) hide show
  1. package/dist/.build-fingerprint +1 -1
  2. package/dist/activities/execute-cursor/attachment-resolver.d.ts +1 -1
  3. package/dist/activities/execute-cursor/attachment-resolver.js +1 -1
  4. package/dist/activities/execute-cursor/capture-flow.d.ts +18 -0
  5. package/dist/activities/execute-cursor/capture-flow.js +21 -0
  6. package/dist/activities/execute-cursor/capture-flow.js.map +1 -1
  7. package/dist/activities/execute-cursor/index.js +121 -139
  8. package/dist/activities/execute-cursor/index.js.map +1 -1
  9. package/dist/activities/execute-cursor/turn-boundary.d.ts +108 -0
  10. package/dist/activities/execute-cursor/turn-boundary.js +163 -0
  11. package/dist/activities/execute-cursor/turn-boundary.js.map +1 -0
  12. package/dist/activities/execute-deep-agent/index.js +30 -3
  13. package/dist/activities/execute-deep-agent/index.js.map +1 -1
  14. package/dist/activities/execute-deep-agent/streaming-v3.js +6 -1
  15. package/dist/activities/execute-deep-agent/streaming-v3.js.map +1 -1
  16. package/dist/activities/execute-deep-agent/streaming.d.ts +7 -0
  17. package/dist/activities/execute-deep-agent/streaming.js +5 -1
  18. package/dist/activities/execute-deep-agent/streaming.js.map +1 -1
  19. package/dist/shared/filereview/capture.d.ts +18 -1
  20. package/dist/shared/filereview/capture.js +105 -28
  21. package/dist/shared/filereview/capture.js.map +1 -1
  22. package/dist/shared/filereview/cas-substrate.d.ts +9 -0
  23. package/dist/shared/filereview/cas-substrate.js +9 -1
  24. package/dist/shared/filereview/cas-substrate.js.map +1 -1
  25. package/dist/shared/filereview/events.d.ts +16 -0
  26. package/dist/shared/filereview/events.js +37 -0
  27. package/dist/shared/filereview/events.js.map +1 -1
  28. package/dist/shared/filereview/git-substrate.d.ts +42 -0
  29. package/dist/shared/filereview/git-substrate.js +78 -0
  30. package/dist/shared/filereview/git-substrate.js.map +1 -1
  31. package/dist/shared/filereview/index.d.ts +3 -1
  32. package/dist/shared/filereview/index.js +3 -1
  33. package/dist/shared/filereview/index.js.map +1 -1
  34. package/dist/shared/filereview/line-counts.d.ts +48 -0
  35. package/dist/shared/filereview/line-counts.js +72 -0
  36. package/dist/shared/filereview/line-counts.js.map +1 -0
  37. package/dist/shared/filereview/progress.d.ts +96 -0
  38. package/dist/shared/filereview/progress.js +134 -0
  39. package/dist/shared/filereview/progress.js.map +1 -0
  40. package/dist/shared/implement-plan-prompt.d.ts +6 -4
  41. package/dist/shared/implement-plan-prompt.js +38 -6
  42. package/dist/shared/implement-plan-prompt.js.map +1 -1
  43. package/dist/shared/plan-artifact.d.ts +59 -11
  44. package/dist/shared/plan-artifact.js +144 -16
  45. package/dist/shared/plan-artifact.js.map +1 -1
  46. package/dist/shared/plan-mode-prompt.d.ts +6 -4
  47. package/dist/shared/plan-mode-prompt.js +10 -5
  48. package/dist/shared/plan-mode-prompt.js.map +1 -1
  49. package/package.json +3 -2
  50. package/src/activities/execute-cursor/__tests__/build-prompt.test.ts +7 -0
  51. package/src/activities/execute-cursor/__tests__/delta-enricher.test.ts +68 -0
  52. package/src/activities/execute-cursor/__tests__/turn-boundary.test.ts +280 -0
  53. package/src/activities/execute-cursor/attachment-resolver.ts +1 -1
  54. package/src/activities/execute-cursor/capture-flow.ts +32 -0
  55. package/src/activities/execute-cursor/index.ts +130 -153
  56. package/src/activities/execute-cursor/turn-boundary.ts +274 -0
  57. package/src/activities/execute-deep-agent/__tests__/prompt-builder.test.ts +11 -0
  58. package/src/activities/execute-deep-agent/index.ts +34 -3
  59. package/src/activities/execute-deep-agent/streaming-v3.ts +6 -1
  60. package/src/activities/execute-deep-agent/streaming.ts +12 -1
  61. package/src/shared/__tests__/implement-plan-prompt.test.ts +40 -2
  62. package/src/shared/__tests__/plan-artifact.test.ts +142 -9
  63. package/src/shared/filereview/__tests__/capture.test.ts +182 -1
  64. package/src/shared/filereview/__tests__/cas-substrate.test.ts +34 -0
  65. package/src/shared/filereview/__tests__/events.test.ts +105 -0
  66. package/src/shared/filereview/__tests__/line-counts.test.ts +73 -0
  67. package/src/shared/filereview/__tests__/progress.test.ts +236 -0
  68. package/src/shared/filereview/capture.ts +134 -30
  69. package/src/shared/filereview/cas-substrate.ts +17 -1
  70. package/src/shared/filereview/events.ts +50 -0
  71. package/src/shared/filereview/git-substrate.ts +108 -0
  72. package/src/shared/filereview/index.ts +22 -1
  73. package/src/shared/filereview/line-counts.ts +83 -0
  74. package/src/shared/filereview/progress.ts +186 -0
  75. package/src/shared/implement-plan-prompt.ts +39 -8
  76. package/src/shared/plan-artifact.ts +152 -16
  77. package/src/shared/plan-mode-prompt.ts +10 -5
@@ -1 +1 @@
1
- {"hash":"63dd2c451162293e","builtAt":"2026-07-04T19:34:42.064Z","fileCount":234}
1
+ {"hash":"21af7d0537f439ed","builtAt":"2026-07-05T14:32:49.617Z","fileCount":237}
@@ -17,7 +17,7 @@
17
17
  * Placement is always `inputs/{filename}` — the platform namespace this
18
18
  * harness can surface in the workspace. An attachment's `mountPath` is
19
19
  * honored by convention, not mechanism: the standard mounts (e.g. the
20
- * approved plan at `.stigmer/inputs/plan.md`) resolve to exactly this
20
+ * approved plan at `.stigmer/inputs/<slug>_<id>.plan.md`) resolve to exactly this
21
21
  * placement, and the prompt's `<input_files>` section plus any path-derived
22
22
  * directives are built from the RESOLVED paths, so prompt and filesystem can
23
23
  * never disagree.
@@ -17,7 +17,7 @@
17
17
  * Placement is always `inputs/{filename}` — the platform namespace this
18
18
  * harness can surface in the workspace. An attachment's `mountPath` is
19
19
  * honored by convention, not mechanism: the standard mounts (e.g. the
20
- * approved plan at `.stigmer/inputs/plan.md`) resolve to exactly this
20
+ * approved plan at `.stigmer/inputs/<slug>_<id>.plan.md`) resolve to exactly this
21
21
  * placement, and the prompt's `<input_files>` section plus any path-derived
22
22
  * directives are built from the RESOLVED paths, so prompt and filesystem can
23
23
  * never disagree.
@@ -21,6 +21,7 @@ import type { AgentMessage } from "@stigmer/protos/ai/stigmer/agentic/agentexecu
21
21
  import type { AgentExecutionStatus } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/api_pb";
22
22
  import type { FileChangeSet, TurnCommandProvenance } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/filereview_pb";
23
23
  import { type CaptureResumeResult } from "../../shared/filereview/capture.js";
24
+ import { type ProgressCaptureState } from "../../shared/filereview/progress.js";
24
25
  import type { GitSubstrateChange as GitCapturedChange } from "../../shared/filereview/git-substrate.js";
25
26
  import type { ArtifactStorage } from "../../shared/artifact-storage.js";
26
27
  export type { CaptureResumeResult };
@@ -100,6 +101,23 @@ export declare function captureTurnToLedger(opts: {
100
101
  */
101
102
  readonly commandProvenance?: TurnCommandProvenance;
102
103
  }): Promise<readonly GitCapturedChange[]>;
104
+ /**
105
+ * Mid-run: attach the live "N files changed so far" snapshot onto
106
+ * `status.file_change_progress` (DD-32), throttled by the floor + tree-sha
107
+ * short-circuit inside {@link captureFileChangeProgress}. The Cursor adapter binds
108
+ * `CURSOR_RUNNER_OWNED_PATHS` so the progress diff excludes the gate's own files —
109
+ * the SAME exclusion {@link captureTurnToLedger} uses for the turn-boundary
110
+ * candidate, so the live count and the reviewed set agree. Content-free and
111
+ * secret-safe; a no-op when nothing changed since the last capture.
112
+ */
113
+ export declare function captureProgressToStatus(opts: {
114
+ readonly status: AgentExecutionStatus;
115
+ readonly gitRoot: string;
116
+ readonly executionId: string;
117
+ readonly changeSetId: string;
118
+ readonly baselineTree: string;
119
+ readonly state: ProgressCaptureState;
120
+ }): Promise<void>;
103
121
  /**
104
122
  * Resume: reconcile the working tree to the change set's DECIDED decisions
105
123
  * (keep approved "after" bytes, snap rejected/undecided back to baseline,
@@ -26,6 +26,7 @@ import { readCasObservations } from "./cas-observations.js";
26
26
  import { contentDigest } from "../../shared/file-tools.js";
27
27
  import { isToolCallRowHidden, stampFileEditRow } from "../../shared/tool-row.js";
28
28
  import { applyCaptureDecisions as sharedApplyCaptureDecisions, captureBaselineToLedger as sharedCaptureBaselineToLedger, captureCandidateToLedger as sharedCaptureCandidateToLedger, } from "../../shared/filereview/capture.js";
29
+ import { captureFileChangeProgress, } from "../../shared/filereview/progress.js";
29
30
  import { hasCandidateCaptured } from "../../shared/filereview/events.js";
30
31
  import { partitionIgnoredPathsBySecret } from "../../shared/filereview/secret-paths.js";
31
32
  import { casBlobReader } from "../../shared/filereview/cas-substrate.js";
@@ -131,6 +132,26 @@ export async function captureTurnToLedger(opts) {
131
132
  }
132
133
  return changes;
133
134
  }
135
+ /**
136
+ * Mid-run: attach the live "N files changed so far" snapshot onto
137
+ * `status.file_change_progress` (DD-32), throttled by the floor + tree-sha
138
+ * short-circuit inside {@link captureFileChangeProgress}. The Cursor adapter binds
139
+ * `CURSOR_RUNNER_OWNED_PATHS` so the progress diff excludes the gate's own files —
140
+ * the SAME exclusion {@link captureTurnToLedger} uses for the turn-boundary
141
+ * candidate, so the live count and the reviewed set agree. Content-free and
142
+ * secret-safe; a no-op when nothing changed since the last capture.
143
+ */
144
+ export async function captureProgressToStatus(opts) {
145
+ await captureFileChangeProgress({
146
+ status: opts.status,
147
+ gitRoot: opts.gitRoot,
148
+ executionId: opts.executionId,
149
+ changeSetId: opts.changeSetId,
150
+ baselineTree: opts.baselineTree,
151
+ excludePaths: CURSOR_RUNNER_OWNED_PATHS,
152
+ state: opts.state,
153
+ });
154
+ }
134
155
  /**
135
156
  * Compose this turn's CAS captures from the sidecar the hook staged, mirroring
136
157
  * deep-agent's `buildCasTurnCaptures`: for each non-secret observed path, the
@@ -1 +1 @@
1
- {"version":3,"file":"capture-flow.js","sourceRoot":"","sources":["../../../src/activities/execute-cursor/capture-flow.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"capture-flow.js","sourceRoot":"","sources":["../../../src/activities/execute-cursor/capture-flow.ts"],"names":[],"mappings":"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"}
@@ -35,9 +35,9 @@ import { StigmerClient } from "../../client/stigmer-client.js";
35
35
  import { resolveAgent } from "./session-lifecycle.js";
36
36
  import { CursorMode } from "@stigmer/protos/ai/stigmer/agentic/session/v1/enum_pb";
37
37
  import { determineCursorMode, isCloudMode } from "./cursor-mode.js";
38
- import { MessageAccumulator, reconcileDeniedToolCalls, clearProvisionalPostDenialNarration, cancelInProgressSubAgentProtos, collapseRedundantToolCallTwins } from "./message-translator.js";
38
+ import { MessageAccumulator, cancelInProgressSubAgentProtos, collapseRedundantToolCallTwins } from "./message-translator.js";
39
39
  import { utcTimestamp, persistStatus, reportSetupProgress, slimStatus } from "../../shared/status.js";
40
- import { collectSubAgentToolCallIds, withholdSecretContentFromMessages } from "../../shared/tool-row.js";
40
+ import { withholdSecretContentFromMessages } from "../../shared/tool-row.js";
41
41
  import { startStallWatchdog, StallTimeoutError, formatStallFailure } from "../../shared/stall-watchdog.js";
42
42
  import { resolveUsableArtifactStorage, loadArtifactStorageConfig } from "../../shared/artifact-storage.js";
43
43
  import { publishPlanArtifact } from "../../shared/plan-artifact.js";
@@ -61,11 +61,12 @@ import { installHitlGate, removeHitlGate } from "./workspace-setup.js";
61
61
  import { ensureHitlDir } from "../../shared/workspace/platform-dir.js";
62
62
  import { acquireWorkspaceLock, WorkspaceLockCancelledError, WorkspaceLockTimeoutError, } from "../../shared/workspace/workspace-lock.js";
63
63
  import { LocalWorkspaceBackend } from "../../shared/workspace/local-backend.js";
64
- import { buildApprovalState, buildApprovalGrants, emitCursorGrantReceipts, primaryToken, readDenialLedger, reconstructAdjudicatedApprovals, watchDenialLedger } from "./approval-state.js";
65
- import { deriveTurnCommandProvenance } from "./command-provenance.js";
64
+ import { buildApprovalState, buildApprovalGrants, emitCursorGrantReceipts, readDenialLedger, reconstructAdjudicatedApprovals, watchDenialLedger } from "./approval-state.js";
66
65
  import { applyApprovedWholeFileWrites, excludeAppliedFromGrants } from "./exact-apply.js";
67
66
  import { isGitWorkTree } from "../../shared/filereview/git-substrate.js";
68
- import { captureBaselineToLedger, captureTurnToLedger, applyCaptureDecisions, deriveCaptureMode, } from "./capture-flow.js";
67
+ import { captureBaselineToLedger, captureProgressToStatus, applyCaptureDecisions, deriveCaptureMode, } from "./capture-flow.js";
68
+ import { runTurnBoundary } from "./turn-boundary.js";
69
+ import { newProgressCaptureState, } from "../../shared/filereview/progress.js";
69
70
  import { deriveExecutionFingerprintKey } from "../../shared/approval-fingerprint.js";
70
71
  import { getRunnerHitlMasterSecret } from "../../shared/fingerprint-secret.js";
71
72
  import { provisionCursorWorkspace } from "./workspace-provision.js";
@@ -82,11 +83,6 @@ import { createAgent, createCloudAgent } from "./session-lifecycle.js";
82
83
  import { setMaxListeners } from "node:events";
83
84
  import { startHeartbeat } from "../../shared/heartbeat.js";
84
85
  import { getShutdownSignalForQueue } from "../../runner-manager.js";
85
- // How long Phase 12 waits for the first-denial-stop's run.cancel() to settle
86
- // before reading the final denial ledger and capturing the turn's tree. Long
87
- // enough for the SDK's normal teardown, short enough that a wedged cancel
88
- // cannot noticeably delay the approval pause the user is already waiting on.
89
- const FIRST_DENIAL_CANCEL_TIMEOUT_MS = 5_000;
90
86
  /**
91
87
  * Creates the activity functions bound to the runner config.
92
88
  * Returned object is passed to Temporal Worker.create({ activities }).
@@ -254,6 +250,9 @@ turnSeq) {
254
250
  // Pre-turn baseline tree, pinned before the agent runs (capture mode only)
255
251
  // so the turn-end capture diffs against it and the tree restores exactly.
256
252
  let baselineTree;
253
+ // Per-turn state for mid-run live capture (DD-32): the last progress tree sha
254
+ // (short-circuit) + last capture time (floor), threaded across persists.
255
+ const progressState = newProgressCaptureState();
257
256
  // Deterministic id of the change set this turn may produce:
258
257
  // `${executionId}:${turnSeq}`. Minted from the workflow-threaded turn index
259
258
  // so it is stable across a Temporal retry (idempotent ledger authoring) and
@@ -893,6 +892,22 @@ turnSeq) {
893
892
  // accumulator tracked sub-agents in memory but they only reached the
894
893
  // status (and the subscriber stream) after the loop ended.
895
894
  status.subAgentExecutions = accumulator.subAgentExecutions;
895
+ // Mid-run live capture (DD-32): attach the "N files changed so far"
896
+ // snapshot onto status.file_change_progress, throttled internally by the
897
+ // floor + tree-sha short-circuit. Git capture mode only (a pinned baseline
898
+ // exists); shell + sub-agent + tool edits are all captured for free by the
899
+ // workspace-wide diff. Never authoritative — the turn-boundary candidate
900
+ // remains the reviewed diff.
901
+ if (captureMode && gitWorkspace && baselineTree && primaryWorkspaceDir) {
902
+ await captureProgressToStatus({
903
+ status,
904
+ gitRoot: primaryWorkspaceDir,
905
+ executionId,
906
+ changeSetId,
907
+ baselineTree,
908
+ state: progressState,
909
+ });
910
+ }
896
911
  const signal = await persist(status);
897
912
  deltaEnricher.markPersisted();
898
913
  todoTracker.markPersisted();
@@ -1060,134 +1075,80 @@ turnSeq) {
1060
1075
  console.log(`ExecuteCursor completed (platform stop): execution=${executionId}`);
1061
1076
  return slimStatus(status);
1062
1077
  }
1063
- // Phase 12: Surface tools the preToolUse hook gated (HITL).
1064
- //
1065
- // The hook records each denial to the ledger; we mark the corresponding tool
1066
- // calls WAITING_APPROVAL. The backend projects pending_approvals from that
1067
- // tool-call status (PendingApprovalComputer), so exactly like the native
1068
- // harness the approval surface is driven entirely by tool-call status. We
1069
- // deliberately do NOT set status.pendingApprovals here: any value would be
1070
- // discarded by the backend's recompute on the next updateStatus.
1071
- //
1072
- // Before reading the ledger, wait (timeboxed) for the first-denial-stop's
1073
- // run.cancel() to settle. run.cancel() races the SDK's auto-execution: until
1074
- // it lands, the agent process may still attempt a post-denial workaround
1075
- // whose hook denial would land AFTER a premature ledger read — the row then
1076
- // never collapses and renders as RUNNING forever (production case
1077
- // aex_01kwj07f7g23c3wp9sn8496z5g) — or a late tool could mutate the tree
1078
- // mid-capture. The timebox keeps a wedged cancel from hanging the pause;
1079
- // the Phase 12 trims below remain the backstop for that degraded case.
1080
- if (firstDenialDetected && denialCancelSettled) {
1081
- await Promise.race([
1082
- denialCancelSettled,
1083
- new Promise((resolve) => {
1084
- const timer = setTimeout(resolve, FIRST_DENIAL_CANCEL_TIMEOUT_MS);
1085
- timer.unref();
1086
- }),
1087
- ]);
1088
- }
1089
- const deniedLedger = await readDenialLedger(hitlDir ?? "");
1090
- // Capture mode: author the net change set to the file_review ledger as the
1091
- // CANDIDATE_CAPTURED event (projected server-side to a file_change_set
1092
- // AWAITING_REVIEW — the single review surface). The runner-owned gate files
1093
- // are excluded from the capture. The agent's edits are LEFT applied on the
1094
- // working tree (Cursor parity — the user reviews the real change; nothing is
1095
- // committed and the next turn is blocked until approval, and a reject snaps
1096
- // each file back on resume). Runs BEFORE the denial reconcile so a denied
1097
- // (gitignored) write stays on the deny-gate path while every flowed edit is
1098
- // captured to the ledger.
1099
- let capturedChangeCount = 0;
1100
- // `baselineTree !== undefined` means a baseline was authored this turn — the
1101
- // git tree sha for a git workspace, or "" (empty, but authored) for a non-git
1102
- // one. A plain truthiness check would wrongly skip the non-git capture.
1103
- if (captureMode && baselineTree !== undefined && primaryWorkspaceDir) {
1104
- const deniedTokens = new Set(deniedLedger.map((e) => e.token));
1105
- // Approved-command turn facts (DD-28): when every mutation-capable call
1106
- // this turn was a consented shell command, attach the provenance so the
1107
- // backend can verify the cited consent rows and auto-keep the set instead
1108
- // of arming a second gate. Fail-closed: any non-qualifying turn attaches
1109
- // nothing and reviews manually exactly as before.
1110
- const commandProvenance = deriveTurnCommandProvenance({
1111
- messages: status.messages,
1112
- turnStartIndex: turnStartMessageIndex,
1113
- deniedTokens,
1114
- grantTokenToConsentId: new Map((approvalGrants ?? []).map((g) => [
1115
- primaryToken(g.key, g.salient, g.contentDigest),
1116
- g.sourceToolCallId,
1117
- ])),
1118
- globalBypass,
1119
- });
1120
- if (commandProvenance) {
1121
- console.log(`ExecuteCursor capture: turn qualifies for approved-command auto-keep ` +
1122
- `(consent rows: ${commandProvenance.consentToolCallIds.join(",") || "(auto_approve_all)"}); ` +
1123
- `attaching provenance to candidate (execution=${executionId})`);
1124
- }
1125
- const captured = await captureTurnToLedger({
1126
- status,
1127
- gitRoot: primaryWorkspaceDir,
1128
- executionId,
1129
- changeSetId,
1130
- baselineTree,
1131
- messages: status.messages,
1132
- deniedTokens,
1133
- commandProvenance,
1134
- // Scope sub-agent row stamping to this turn: the seeded prior sub-agents
1135
- // (cloned in on resume) are the "before this turn" rows to skip.
1136
- priorSubAgentToolCallIds: collectSubAgentToolCallIds(seededSubAgents),
1137
- // The CAS half: read the sidecar the hook staged this turn and compose it
1138
- // into the change set. hitlDir + storage are present when captureIgnored
1139
- // was on (a git tree's gitignored writes, or ALL writes in a non-git
1140
- // workspace). In a git tree this composes with the git diff (HYBRID); in a
1141
- // non-git workspace it IS the whole change set (CAS-only).
1142
- hitlDir,
1143
- storage: artifactStorage,
1144
- gitWorkspace,
1145
- });
1146
- capturedChangeCount = captured.length;
1147
- if (capturedChangeCount > 0) {
1148
- console.log(`ExecuteCursor capture: ${capturedChangeCount} file change(s) authored to the ` +
1149
- `file_review ledger (change_set=${changeSetId}), working tree left applied ` +
1150
- `for review (execution=${executionId})`);
1151
- }
1152
- }
1153
- // The gate reads each denied file's pre-edit `before` from the workspace the
1154
- // runner is co-located with (local FS for OSS; the sandbox in cloud), so a
1155
- // whole-file rewrite gate renders a true before/after diff. The tool was
1156
- // DENIED, so disk still holds the old content. User files are never platform
1157
- // paths, so no platformDir routing is needed here.
1158
- const gateWorkspaceBackend = new LocalWorkspaceBackend(primaryWorkspaceDir);
1159
- const deniedToolCalls = await reconcileDeniedToolCalls(status.messages, deniedLedger, mergedPolicies, gateWorkspaceBackend);
1160
- // Observability: a synthesized placeholder (id `approval:*`) means a denial
1161
- // correlated to NO streamed tool call in either the exact or the normalized
1162
- // pass. After the normalized-path fallback this should be ~0; a non-zero rate
1163
- // is the early-warning signal of a NEW identity drift (the gate would then
1164
- // show "No preview available" with no diff). Logged, not thrown — the
1165
- // synthesized gate still safely surfaces the approval.
1166
- const synthesizedGateCount = deniedToolCalls.filter((tc) => tc.id.startsWith("approval:")).length;
1167
- if (synthesizedGateCount > 0) {
1168
- console.warn(`ExecuteCursor reconcile synthesized ${synthesizedGateCount} placeholder gate(s) ` +
1169
- `with no correlated stream call (execution=${executionId}); ` +
1170
- `possible hook/stream identity drift — gate(s) will lack a diff`);
1171
- }
1172
- if (deniedToolCalls.length > 0 || capturedChangeCount > 0) {
1173
- if (deniedToolCalls.length > 0) {
1174
- // Deterministic clean-pause: a turn that pauses for approval must read as
1175
- // the same shape the native harness produces — pre-tool text + the gated
1176
- // tool calls — never the model's provisional reaction to Cursor's deny
1177
- // (e.g. "blocked by a hook; enable it in your Cursor settings"). We blank
1178
- // that reaction in place (keeping the message count, so the finalize stays
1179
- // append-only) rather than removing it. See
1180
- // clearProvisionalPostDenialNarration for the full rationale.
1181
- const redactedNarration = clearProvisionalPostDenialNarration(status.messages, deniedToolCalls);
1182
- if (redactedNarration.length > 0) {
1183
- console.log(`ExecuteCursor redacted ${redactedNarration.length} provisional post-denial narration message(s) before pausing for approval`);
1184
- }
1185
- }
1078
+ // Phase 12: The turn boundary author this turn's change set to the
1079
+ // file_review ledger (CANDIDATE_CAPTURED) and overlay the hook's denials as
1080
+ // WAITING_APPROVAL gate rows. The full pipeline and its ordering rationale
1081
+ // live in turn-boundary.ts; this closure binds the turn's state so the
1082
+ // recovery retries below (which re-run the agent AFTER this primary call)
1083
+ // can re-enter the IDENTICAL pipeline a retry's edits must reach the
1084
+ // ledger or they silently escape review. `baselineTree` is read at call
1085
+ // time, so both entries see the baseline authored at turn start.
1086
+ const runBoundary = (denialSettled) => runTurnBoundary({
1087
+ status,
1088
+ executionId,
1089
+ changeSetId,
1090
+ hitlDir,
1091
+ captureMode,
1092
+ baselineTree,
1093
+ primaryWorkspaceDir,
1094
+ gitWorkspace,
1095
+ turnStartMessageIndex,
1096
+ approvalGrants,
1097
+ globalBypass,
1098
+ seededSubAgents,
1099
+ artifactStorage,
1100
+ mergedPolicies,
1101
+ denialCancelSettled: denialSettled,
1102
+ });
1103
+ // Pauses for review exactly like the native harness: the boundary mutated
1104
+ // the transcript in place; we flip the phase, persist, and RETURN to the
1105
+ // workflow, which waits for the approval/file-review signal and reinvokes.
1106
+ const enterApprovalPause = async (boundary) => {
1186
1107
  status.phase = ExecutionPhase.EXECUTION_WAITING_FOR_APPROVAL;
1187
1108
  await persist(status);
1188
- console.log(`ExecuteCursor returning WAITING_FOR_APPROVAL: ${deniedToolCalls.length} gated tool(s), ` +
1189
- `${capturedChangeCount} file card(s) pending`);
1109
+ console.log(`ExecuteCursor returning WAITING_FOR_APPROVAL: ${boundary.deniedToolCallCount} gated tool(s), ` +
1110
+ `${boundary.capturedChangeCount} file card(s) pending`);
1190
1111
  return slimStatus(status);
1112
+ };
1113
+ // Stream epilogue + boundary re-entry for a recovery retry (poisoned-handle /
1114
+ // transport-timeout). The retry re-runs the agent AFTER the primary epilogue
1115
+ // and boundary already ran, so its stream state must be settled the same way:
1116
+ // - flush the enricher's buffered deltas onto the rows (the primary loop
1117
+ // applies them every iteration; the bare retry loop does not — without the
1118
+ // flush, a completed tool call never receives its completedAt evidence and
1119
+ // finalize's reconciliation sweep leaves it RUNNING forever);
1120
+ // - finalize streaming state and sync sub-agents;
1121
+ // - refresh streaming usage (the retry's turns accumulated in memory only)
1122
+ // and re-stamp completedAt (stamped below BEFORE the retry ran);
1123
+ // - re-enter the turn boundary so edits made by the retry reach the
1124
+ // file_review ledger / approval gates — without this, a retry's file
1125
+ // edits silently escape review (production case
1126
+ // aex_01kws27q1e2esvkqjpvectttxf).
1127
+ // Returns undefined for a cancelled retry: parity with the primary path,
1128
+ // where cancellation exits before the boundary — there is no review to open.
1129
+ const settleRetryTurn = async (retryResultStatus) => {
1130
+ accumulator.finalize();
1131
+ deltaEnricher.applyEnrichments(status.messages);
1132
+ deltaEnricher.finalize(status.messages);
1133
+ status.subAgentExecutions = accumulator.subAgentExecutions;
1134
+ if (usageAccumulator.hasTurns) {
1135
+ status.streamingUsage = create(StreamingUsageSummarySchema, usageAccumulator.snapshot());
1136
+ }
1137
+ // Re-flush the (dev-only) event recorder: flush rewrites the full JSONL,
1138
+ // so the recorded trace now includes the retry's events too.
1139
+ await eventRecorder?.flush();
1140
+ const retryBoundary = retryResultStatus === "cancelled" ? undefined : await runBoundary();
1141
+ // Phase 13 stamped completedAt BEFORE the retry ran. A terminal outcome
1142
+ // re-stamps it to the true end; a review pause CLEARS it — the primary
1143
+ // pause path never stamps it (a waiting turn is not complete).
1144
+ status.completedAt = retryBoundary?.waiting ? "" : utcTimestamp();
1145
+ return retryBoundary;
1146
+ };
1147
+ // The denial-settle wait applies only when a first denial stopped THIS run;
1148
+ // the recovery retries have no early stop and pass no promise.
1149
+ const boundary = await runBoundary(firstDenialDetected ? denialCancelSettled : undefined);
1150
+ if (boundary.waiting) {
1151
+ return enterApprovalPause(boundary);
1191
1152
  }
1192
1153
  // Phase 13: Map final result
1193
1154
  const result = await run.wait();
@@ -1301,7 +1262,9 @@ turnSeq) {
1301
1262
  if (Context.current().cancellationSignal.aborted)
1302
1263
  break;
1303
1264
  retryWatchdog.recordActivity();
1265
+ eventRecorder?.record(retryEvent, eventCount);
1304
1266
  accumulator.processEvent(retryEvent);
1267
+ eventCount++;
1305
1268
  if (retryEvent.type === "status") {
1306
1269
  const retryStatusEvent = retryEvent;
1307
1270
  if (retryStatusEvent.status === "ERROR" && retryStatusEvent.message) {
@@ -1317,6 +1280,15 @@ turnSeq) {
1317
1280
  const retryResult = await retryRun.wait();
1318
1281
  console.log(`ExecuteCursor retry run.wait(): execution=${executionId}, ` +
1319
1282
  `retryResult=${JSON.stringify(retryResult)}`);
1283
+ const retryBoundary = await settleRetryTurn(retryResult.status);
1284
+ if (retryBoundary?.waiting) {
1285
+ // The retry's edits/denials armed the gate — pause for review. On a
1286
+ // retry error this supersedes the failure, exactly as on the primary
1287
+ // path (a captured change pauses the turn before run.wait() is
1288
+ // consulted).
1289
+ console.log(`ExecuteCursor poisoned-handle recovery paused for review: execution=${executionId}`);
1290
+ return enterApprovalPause(retryBoundary);
1291
+ }
1320
1292
  if (retryResult.status === "finished") {
1321
1293
  status.phase = ExecutionPhase.EXECUTION_COMPLETED;
1322
1294
  console.log(`ExecuteCursor poisoned-handle recovery SUCCEEDED: execution=${executionId}`);
@@ -1398,7 +1370,9 @@ turnSeq) {
1398
1370
  if (Context.current().cancellationSignal.aborted)
1399
1371
  break;
1400
1372
  retryWatchdog.recordActivity();
1373
+ eventRecorder?.record(retryEvent, eventCount);
1401
1374
  accumulator.processEvent(retryEvent);
1375
+ eventCount++;
1402
1376
  if (retryEvent.type === "status") {
1403
1377
  const retryStatusEvent = retryEvent;
1404
1378
  if (retryStatusEvent.status === "ERROR" && retryStatusEvent.message) {
@@ -1412,6 +1386,14 @@ turnSeq) {
1412
1386
  retryWatchdog.stop();
1413
1387
  }
1414
1388
  const retryResult = await retryRun.wait();
1389
+ const retryBoundary = await settleRetryTurn(retryResult.status);
1390
+ if (retryBoundary?.waiting) {
1391
+ // The retry's edits/denials armed the gate — pause for review (see
1392
+ // the poisoned-handle branch above for the precedence rationale).
1393
+ console.log(`ExecuteCursor transport-timeout recovery paused for review: execution=${executionId}`);
1394
+ resolution = { ...resolution, agent: freshAgent, agentId: freshAgent.agentId, isNew: true };
1395
+ return enterApprovalPause(retryBoundary);
1396
+ }
1415
1397
  if (retryResult.status === "finished") {
1416
1398
  status.phase = ExecutionPhase.EXECUTION_COMPLETED;
1417
1399
  resolution = { ...resolution, agent: freshAgent, agentId: freshAgent.agentId, isNew: true };
@@ -1471,10 +1453,10 @@ turnSeq) {
1471
1453
  if (structuredOutput !== undefined) {
1472
1454
  status.structuredOutput = structuredOutput;
1473
1455
  }
1474
- // Plan mode: publish the final plan message as a plan.md artifact. The
1475
- // Cursor harness has no auto-publish pipeline, so this is the only
1476
- // artifact path; build storage from the same config-driven factory the
1477
- // native harness uses.
1456
+ // Plan mode: publish the final plan message as a plan artifact (named
1457
+ // from the plan's title). The Cursor harness has no auto-publish
1458
+ // pipeline, so this is the only artifact path; build storage from the
1459
+ // same config-driven factory the native harness uses.
1478
1460
  if (interactionMode === InteractionMode.PLAN && finalText && artifactStorage) {
1479
1461
  try {
1480
1462
  await publishPlanArtifact({ status, executionId, planText: finalText, artifactStorage });