@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.
- package/dist/.build-fingerprint +1 -1
- package/dist/activities/execute-cursor/attachment-resolver.js +1 -1
- package/dist/activities/execute-cursor/attachment-resolver.js.map +1 -1
- package/dist/activities/execute-cursor/capture-flow.d.ts +26 -15
- package/dist/activities/execute-cursor/capture-flow.js +56 -18
- package/dist/activities/execute-cursor/capture-flow.js.map +1 -1
- package/dist/activities/execute-cursor/command-provenance.d.ts +11 -25
- package/dist/activities/execute-cursor/command-provenance.js +25 -115
- package/dist/activities/execute-cursor/command-provenance.js.map +1 -1
- package/dist/activities/execute-cursor/index.js +297 -478
- package/dist/activities/execute-cursor/index.js.map +1 -1
- package/dist/activities/execute-cursor/skill-resolver.js +1 -1
- package/dist/activities/execute-cursor/skill-resolver.js.map +1 -1
- package/dist/activities/execute-cursor/todo-tracker.d.ts +6 -1
- package/dist/activities/execute-cursor/todo-tracker.js +15 -43
- package/dist/activities/execute-cursor/todo-tracker.js.map +1 -1
- package/dist/activities/execute-cursor/turn-stream.d.ts +141 -0
- package/dist/activities/execute-cursor/turn-stream.js +249 -0
- package/dist/activities/execute-cursor/turn-stream.js.map +1 -0
- package/dist/activities/execute-deep-agent/command-provenance.d.ts +61 -0
- package/dist/activities/execute-deep-agent/command-provenance.js +72 -0
- package/dist/activities/execute-deep-agent/command-provenance.js.map +1 -0
- package/dist/activities/execute-deep-agent/index.js +88 -20
- package/dist/activities/execute-deep-agent/index.js.map +1 -1
- package/dist/activities/execute-deep-agent/status-builder.js +8 -1
- package/dist/activities/execute-deep-agent/status-builder.js.map +1 -1
- package/dist/activities/execute-deep-agent/v3-status-builder.js +12 -1
- package/dist/activities/execute-deep-agent/v3-status-builder.js.map +1 -1
- package/dist/activities/execute-deep-agent/writeback-coordinator.js +5 -1
- package/dist/activities/execute-deep-agent/writeback-coordinator.js.map +1 -1
- package/dist/otel.js +10 -0
- package/dist/otel.js.map +1 -1
- package/dist/shared/filereview/cas-progress.d.ts +63 -0
- package/dist/shared/filereview/cas-progress.js +128 -0
- package/dist/shared/filereview/cas-progress.js.map +1 -0
- package/dist/shared/filereview/cas-substrate.d.ts +29 -0
- package/dist/shared/filereview/cas-substrate.js +46 -21
- package/dist/shared/filereview/cas-substrate.js.map +1 -1
- package/dist/shared/filereview/command-provenance.d.ts +93 -0
- package/dist/shared/filereview/command-provenance.js +132 -0
- package/dist/shared/filereview/command-provenance.js.map +1 -0
- package/dist/shared/filereview/git-substrate.d.ts +9 -3
- package/dist/shared/filereview/git-substrate.js +4 -0
- package/dist/shared/filereview/git-substrate.js.map +1 -1
- package/dist/shared/filereview/index.d.ts +4 -3
- package/dist/shared/filereview/index.js +3 -2
- package/dist/shared/filereview/index.js.map +1 -1
- package/dist/shared/filereview/progress.d.ts +105 -34
- package/dist/shared/filereview/progress.js +96 -34
- package/dist/shared/filereview/progress.js.map +1 -1
- package/dist/shared/plan-mode-prompt.d.ts +9 -0
- package/dist/shared/plan-mode-prompt.js +18 -0
- package/dist/shared/plan-mode-prompt.js.map +1 -1
- package/dist/shared/todos.d.ts +56 -0
- package/dist/shared/todos.js +98 -0
- package/dist/shared/todos.js.map +1 -0
- package/dist/shared/tool-row.d.ts +16 -0
- package/dist/shared/tool-row.js +31 -0
- package/dist/shared/tool-row.js.map +1 -1
- package/dist/shared/workspace/git-identity.d.ts +36 -0
- package/dist/shared/workspace/git-identity.js +39 -0
- package/dist/shared/workspace/git-identity.js.map +1 -0
- package/dist/shared/workspace/local-backend.d.ts +6 -2
- package/dist/shared/workspace/local-backend.js +6 -2
- package/dist/shared/workspace/local-backend.js.map +1 -1
- package/dist/shared/workspace/stigmer-link.d.ts +54 -0
- package/dist/shared/workspace/stigmer-link.js +92 -0
- package/dist/shared/workspace/stigmer-link.js.map +1 -0
- package/dist/shared/workspace/types.d.ts +6 -4
- package/package.json +2 -2
- package/src/__tests__/otel-turn-span.test.ts +61 -0
- package/src/activities/execute-cursor/__tests__/progress-substrate.test.ts +169 -0
- package/src/activities/execute-cursor/__tests__/turn-stream.test.ts +349 -0
- package/src/activities/execute-cursor/attachment-resolver.ts +1 -1
- package/src/activities/execute-cursor/capture-flow.ts +71 -25
- package/src/activities/execute-cursor/command-provenance.ts +25 -120
- package/src/activities/execute-cursor/index.ts +345 -505
- package/src/activities/execute-cursor/skill-resolver.ts +1 -1
- package/src/activities/execute-cursor/todo-tracker.ts +17 -59
- package/src/activities/execute-cursor/turn-stream.ts +418 -0
- package/src/activities/execute-deep-agent/__tests__/command-provenance.test.ts +252 -0
- package/src/activities/execute-deep-agent/__tests__/status-builder.test.ts +78 -0
- package/src/activities/execute-deep-agent/__tests__/v3-status-builder.test.ts +105 -1
- package/src/activities/execute-deep-agent/__tests__/writeback-coordinator.test.ts +30 -3
- package/src/activities/execute-deep-agent/command-provenance.ts +102 -0
- package/src/activities/execute-deep-agent/index.ts +107 -20
- package/src/activities/execute-deep-agent/status-builder.ts +9 -0
- package/src/activities/execute-deep-agent/v3-status-builder.ts +13 -0
- package/src/activities/execute-deep-agent/writeback-coordinator.ts +5 -1
- package/src/otel.ts +8 -0
- package/src/shared/__tests__/todos.test.ts +216 -0
- package/src/shared/filereview/__tests__/cas-progress.test.ts +228 -0
- package/src/shared/filereview/__tests__/cas-substrate.test.ts +66 -0
- package/src/shared/filereview/__tests__/command-provenance.test.ts +252 -0
- package/src/shared/filereview/__tests__/progress.test.ts +112 -10
- package/src/shared/filereview/cas-progress.ts +170 -0
- package/src/shared/filereview/cas-substrate.ts +69 -24
- package/src/shared/filereview/command-provenance.ts +180 -0
- package/src/shared/filereview/git-substrate.ts +13 -3
- package/src/shared/filereview/index.ts +15 -1
- package/src/shared/filereview/progress.ts +171 -47
- package/src/shared/plan-mode-prompt.ts +18 -0
- package/src/shared/todos.ts +126 -0
- package/src/shared/tool-row.ts +34 -0
- package/src/shared/workspace/__tests__/git-identity.test.ts +124 -0
- package/src/shared/workspace/__tests__/stigmer-link.test.ts +173 -0
- package/src/shared/workspace/git-identity.ts +41 -0
- package/src/shared/workspace/local-backend.ts +6 -2
- package/src/shared/workspace/stigmer-link.ts +97 -0
- package/src/shared/workspace/types.ts +6 -4
- package/dist/activities/execute-cursor/stigmer-link.d.ts +0 -35
- package/dist/activities/execute-cursor/stigmer-link.js +0 -73
- package/dist/activities/execute-cursor/stigmer-link.js.map +0 -1
- package/src/activities/execute-cursor/stigmer-link.ts +0 -78
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Cursor harness's stream phase — the single per-event consumption loop that
|
|
3
|
+
* turns a live Cursor SDK run into the streamed transcript, mid-run progress, and
|
|
4
|
+
* the flags that decide how the turn ends.
|
|
5
|
+
*
|
|
6
|
+
* Extracted from the activity entry point (index.ts) so that BOTH the primary
|
|
7
|
+
* turn and the two recovery retries (poisoned-handle / transport-timeout) drive
|
|
8
|
+
* the exact same code. The retries previously re-implemented a stripped "bare"
|
|
9
|
+
* loop that dropped live persist, DD-32/DD-33 mid-run progress, sub-agent
|
|
10
|
+
* tracking, the first-denial early stop, and correct pause/stall handling — so a
|
|
11
|
+
* retry froze the UI and mis-handled a mid-retry pause. One loop removes that
|
|
12
|
+
* drift by construction.
|
|
13
|
+
*
|
|
14
|
+
* This mirrors the deep-agent harness's `streamExecution` seam
|
|
15
|
+
* (execute-deep-agent/streaming.ts): injected `heartbeat`/`isCancelled` so it
|
|
16
|
+
* runs without Temporal, a structural `StreamableRun` so it runs without the live
|
|
17
|
+
* SDK, and a result (the terminal reason) instead of internal terminal mapping.
|
|
18
|
+
* The one deliberate difference: this consumer REPORTS why the stream ended and
|
|
19
|
+
* leaves the terminal MAPPING to the activity, because Cursor's mapping is
|
|
20
|
+
* entangled with worker-shutdown-vs-pause disambiguation, the turn boundary,
|
|
21
|
+
* structured output, and recovery retries — all of which must stay in index.ts.
|
|
22
|
+
*/
|
|
23
|
+
import { create } from "@bufbuild/protobuf";
|
|
24
|
+
import { CancelledFailure } from "@temporalio/activity";
|
|
25
|
+
import { ExecutionControlSignal } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
|
|
26
|
+
import { StreamingUsageSummarySchema } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/usage_pb";
|
|
27
|
+
import { startStallWatchdog, StallTimeoutError, } from "../../shared/stall-watchdog.js";
|
|
28
|
+
import { shouldPersistStreamingStatus } from "./persist-decision.js";
|
|
29
|
+
import { readDenialLedger } from "./approval-state.js";
|
|
30
|
+
import { captureFileChangeProgress, } from "../../shared/filereview/progress.js";
|
|
31
|
+
export function newTurnStreamState() {
|
|
32
|
+
return {
|
|
33
|
+
pauseDetected: false,
|
|
34
|
+
stallDetected: false,
|
|
35
|
+
stallError: undefined,
|
|
36
|
+
firstDenialDetected: false,
|
|
37
|
+
denialLedgerDirty: false,
|
|
38
|
+
denialCancelSettled: undefined,
|
|
39
|
+
platformStopSignaled: false,
|
|
40
|
+
streamErrorMessage: undefined,
|
|
41
|
+
lastToolName: undefined,
|
|
42
|
+
eventCount: 0,
|
|
43
|
+
firstTurnAttributionLogged: false,
|
|
44
|
+
stallWatchdog: undefined,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Build the shared onDelta callback. The Cursor SDK's fine-grained delta channel
|
|
49
|
+
* carries token usage, live shell output, and precise tool-call timings; it also
|
|
50
|
+
* fires far more often than discrete stream events, so it is where the stall
|
|
51
|
+
* timer is reset during a long model generation. A heartbeat CancelledFailure
|
|
52
|
+
* here flags the pause so the loop and epilogue treat it as a user pause (the
|
|
53
|
+
* bare retry loops swallowed this — the cause of the mid-retry mislabel).
|
|
54
|
+
*/
|
|
55
|
+
export function makeCursorTurnOnDelta(deps) {
|
|
56
|
+
const { usageAccumulator, deltaEnricher, heartbeat, promptEstimatedTokens, executionId, state } = deps;
|
|
57
|
+
return ({ update }) => {
|
|
58
|
+
// Reset the stall timer on the delta channel too: a long model generation
|
|
59
|
+
// emits token deltas but few discrete stream events, so resetting only in the
|
|
60
|
+
// stream loop would false-positive a stall.
|
|
61
|
+
state.stallWatchdog?.recordActivity();
|
|
62
|
+
if (update.type === "turn-ended" && update.usage) {
|
|
63
|
+
usageAccumulator.addTurn(update.usage);
|
|
64
|
+
if (!state.firstTurnAttributionLogged) {
|
|
65
|
+
state.firstTurnAttributionLogged = true;
|
|
66
|
+
const sdkInputTokens = update.usage.inputTokens ?? 0;
|
|
67
|
+
const cursorOverhead = Math.max(0, sdkInputTokens - promptEstimatedTokens);
|
|
68
|
+
console.log(`ExecuteCursor context attribution (first turn): execution=${executionId}, ` +
|
|
69
|
+
`sdkInputTokens=${sdkInputTokens}, stigmerPreamble=${promptEstimatedTokens}, ` +
|
|
70
|
+
`cursorOverhead=${cursorOverhead} (estimated)`);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
deltaEnricher.processDelta(update);
|
|
74
|
+
try {
|
|
75
|
+
heartbeat();
|
|
76
|
+
}
|
|
77
|
+
catch (hbErr) {
|
|
78
|
+
if (hbErr instanceof CancelledFailure) {
|
|
79
|
+
state.pauseDetected = true;
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
throw hbErr;
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Consume a Cursor SDK run to completion (or to a clean early stop), driving the
|
|
88
|
+
* transcript, todos, sub-agent tracking, live persist, and DD-32/DD-33 mid-run
|
|
89
|
+
* progress, and reporting WHY the stream ended.
|
|
90
|
+
*
|
|
91
|
+
* Arms + stops its own stall watchdog (stored on `state.stallWatchdog` so the
|
|
92
|
+
* shared onDelta can reset it and the activity's finally can stop it as a
|
|
93
|
+
* backstop). Mutates `deps.status` + `deps.state` in place. The caller owns the
|
|
94
|
+
* post-stream finalize and the terminal mapping.
|
|
95
|
+
*/
|
|
96
|
+
export async function consumeCursorTurnStream(run, deps) {
|
|
97
|
+
const { status, accumulator, todoTracker, deltaEnricher, eventRecorder, scheduler, usageAccumulator, progressSubstrate, progressState, changeSetId, hitlDir, executionId, stallTimeoutMs, persist, heartbeat, isCancelled, state, } = deps;
|
|
98
|
+
// Arm the stall watchdog now that the run exists. The activity's periodic
|
|
99
|
+
// heartbeat proves the process is alive, not that the agent is progressing: if
|
|
100
|
+
// the stream wedges (a tool call or model connection that never returns), no
|
|
101
|
+
// event/delta arrives and the turn would hang forever. On stall we end the run
|
|
102
|
+
// cleanly via the SDK's run.cancel() (guarded by supports("cancel")), which
|
|
103
|
+
// unblocks the for-await; the caller then reports EXECUTION_FAILED with a
|
|
104
|
+
// recognizable, actionable message.
|
|
105
|
+
state.stallWatchdog = startStallWatchdog(stallTimeoutMs, (idleMs) => {
|
|
106
|
+
state.stallDetected = true;
|
|
107
|
+
state.stallError = new StallTimeoutError(idleMs, state.lastToolName ? `last tool: ${state.lastToolName}` : undefined);
|
|
108
|
+
console.warn(`ExecuteCursor stall detected: execution=${executionId}, idleMs=${idleMs}, lastTool=${state.lastToolName ?? "none"}`);
|
|
109
|
+
if (run.supports?.("cancel")) {
|
|
110
|
+
void run.cancel().catch((cancelErr) => {
|
|
111
|
+
console.warn(`ExecuteCursor run.cancel() after stall failed (non-fatal): execution=${executionId}, ` +
|
|
112
|
+
`error=${cancelErr instanceof Error ? cancelErr.message : cancelErr}`);
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
try {
|
|
117
|
+
for await (const event of run.stream()) {
|
|
118
|
+
if (state.pauseDetected || isCancelled()) {
|
|
119
|
+
state.pauseDetected = true;
|
|
120
|
+
break;
|
|
121
|
+
}
|
|
122
|
+
if (state.stallDetected)
|
|
123
|
+
break;
|
|
124
|
+
// Progress: reset the stall timer on every stream event.
|
|
125
|
+
state.stallWatchdog.recordActivity();
|
|
126
|
+
if (event.type === "tool_call" && typeof event.name === "string") {
|
|
127
|
+
state.lastToolName = event.name;
|
|
128
|
+
}
|
|
129
|
+
eventRecorder?.record(event, state.eventCount);
|
|
130
|
+
accumulator.processEvent(event);
|
|
131
|
+
todoTracker.processEvent(event);
|
|
132
|
+
if (event.type === "tool_call" && event.name === "task") {
|
|
133
|
+
accumulator.trackSubAgentExecution(event);
|
|
134
|
+
}
|
|
135
|
+
// First-denial stop (HITL clean pause). In CAPTURE mode this fires only for
|
|
136
|
+
// an IRREVERSIBLE tool the hook still gates (shell, MCP, or a gitignored
|
|
137
|
+
// write/delete) — file edits flow freely and are captured at the turn
|
|
138
|
+
// boundary, so they never enter the ledger. In the deny-gate FALLBACK
|
|
139
|
+
// (non-git workspace) it fires for every gated file edit too. Either way:
|
|
140
|
+
// the preToolUse hook appends to the denial ledger the instant it gates a
|
|
141
|
+
// tool — before Cursor surfaces the failure to the model — and the fs
|
|
142
|
+
// watcher flips denialLedgerDirty the moment that write lands. Confirming
|
|
143
|
+
// the flag with a read on the very next event (of ANY type — thinking
|
|
144
|
+
// deltas arrive within milliseconds) ends the turn before the model's
|
|
145
|
+
// reaction can persist: waiting for the next tool_call event let the full
|
|
146
|
+
// post-denial reaction (thinking, narration, a workaround shell) stream and
|
|
147
|
+
// persist live (production case aex_01kwj07f7g23c3wp9sn8496z5g). The
|
|
148
|
+
// tool_call-event read stays as the backstop for platforms where fs.watch
|
|
149
|
+
// is unreliable; the current event was already accumulated above, so the
|
|
150
|
+
// anchor's own row is always present for the turn-boundary gate overlay.
|
|
151
|
+
if (!state.firstDenialDetected && hitlDir && (state.denialLedgerDirty || event.type === "tool_call")) {
|
|
152
|
+
state.denialLedgerDirty = false;
|
|
153
|
+
const denials = await readDenialLedger(hitlDir);
|
|
154
|
+
if (denials.length > 0) {
|
|
155
|
+
state.firstDenialDetected = true;
|
|
156
|
+
console.log(`ExecuteCursor first denial detected (${denials.length} ledger ` +
|
|
157
|
+
`entr${denials.length === 1 ? "y" : "ies"}); stopping turn to pause ` +
|
|
158
|
+
`cleanly for approval: execution=${executionId}`);
|
|
159
|
+
if (run.supports?.("cancel")) {
|
|
160
|
+
// Kept (not fire-and-forget): awaited timeboxed by the turn boundary
|
|
161
|
+
// so the ledger read and tree capture see a stopped agent.
|
|
162
|
+
state.denialCancelSettled = run.cancel().then(() => { }, (cancelErr) => {
|
|
163
|
+
console.warn(`ExecuteCursor run.cancel() after first denial failed (non-fatal): ` +
|
|
164
|
+
`execution=${executionId}, ` +
|
|
165
|
+
`error=${cancelErr instanceof Error ? cancelErr.message : cancelErr}`);
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
break;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
deltaEnricher.applyEnrichments(status.messages);
|
|
172
|
+
state.eventCount++;
|
|
173
|
+
if (event.type === "status") {
|
|
174
|
+
console.log(`ExecuteCursor stream status: execution=${executionId}, status=${JSON.stringify(event)}`);
|
|
175
|
+
const statusEvent = event;
|
|
176
|
+
if (statusEvent.status === "ERROR" && statusEvent.message) {
|
|
177
|
+
state.streamErrorMessage = statusEvent.message;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
const shouldPersist = shouldPersistStreamingStatus({
|
|
181
|
+
deltaEnricherDirty: deltaEnricher.isDirty,
|
|
182
|
+
todosDirty: todoTracker.isDirty,
|
|
183
|
+
contentDirty: accumulator.isDirty,
|
|
184
|
+
}, scheduler, state.eventCount);
|
|
185
|
+
if (usageAccumulator.hasTurns) {
|
|
186
|
+
status.streamingUsage = create(StreamingUsageSummarySchema, usageAccumulator.snapshot());
|
|
187
|
+
}
|
|
188
|
+
if (shouldPersist) {
|
|
189
|
+
// Sync sub-agent executions into status before every persist so the live
|
|
190
|
+
// UI reflects delegation (including the IN_PROGRESS state) while the
|
|
191
|
+
// parent is still running — matching the native harness.
|
|
192
|
+
status.subAgentExecutions = accumulator.subAgentExecutions;
|
|
193
|
+
// Mid-run live capture (DD-32 / DD-33): attach the "N files changed so
|
|
194
|
+
// far" snapshot onto status.file_change_progress, throttled internally by
|
|
195
|
+
// the floor. Never authoritative — the turn-boundary candidate remains
|
|
196
|
+
// the reviewed diff.
|
|
197
|
+
if (progressSubstrate) {
|
|
198
|
+
await captureFileChangeProgress({
|
|
199
|
+
status,
|
|
200
|
+
changeSetId,
|
|
201
|
+
substrate: progressSubstrate,
|
|
202
|
+
state: progressState,
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
const signal = await persist(status);
|
|
206
|
+
deltaEnricher.markPersisted();
|
|
207
|
+
todoTracker.markPersisted();
|
|
208
|
+
accumulator.markPersisted();
|
|
209
|
+
scheduler.markUpdateSent(state.eventCount);
|
|
210
|
+
heartbeat();
|
|
211
|
+
if (signal === ExecutionControlSignal.STOP) {
|
|
212
|
+
state.platformStopSignaled = true;
|
|
213
|
+
console.warn(`ExecuteCursor platform stop signal received: execution=${executionId}`);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
if (state.platformStopSignaled) {
|
|
217
|
+
console.log(`ExecuteCursor stopping stream due to platform stop signal: execution=${executionId}`);
|
|
218
|
+
break;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
catch (streamErr) {
|
|
223
|
+
// run.cancel() — from the stall watchdog or the first-denial stop — can make
|
|
224
|
+
// the stream iterator reject as it tears down; that is the expected teardown
|
|
225
|
+
// for both, so swallow it and fall through. Anything else is a genuine stream
|
|
226
|
+
// failure — rethrow it to the activity's error handler.
|
|
227
|
+
if (!state.stallDetected && !state.firstDenialDetected)
|
|
228
|
+
throw streamErr;
|
|
229
|
+
console.warn(`ExecuteCursor stream ended via cancel: execution=${executionId}, ` +
|
|
230
|
+
`stall=${state.stallDetected}, firstDenial=${state.firstDenialDetected}`);
|
|
231
|
+
}
|
|
232
|
+
finally {
|
|
233
|
+
state.stallWatchdog.stop();
|
|
234
|
+
}
|
|
235
|
+
// Report why the stream ended, in the same precedence the activity's epilogue
|
|
236
|
+
// applies: a stall (an EXECUTION_FAILED terminal) outranks everything; a
|
|
237
|
+
// platform stop and a first denial are distinct proceed/return outcomes; a
|
|
238
|
+
// pause is the fallback for a cancellation with no other cause.
|
|
239
|
+
if (state.stallDetected)
|
|
240
|
+
return "stalled";
|
|
241
|
+
if (state.platformStopSignaled)
|
|
242
|
+
return "platform-stop";
|
|
243
|
+
if (state.firstDenialDetected)
|
|
244
|
+
return "first-denial";
|
|
245
|
+
if (state.pauseDetected || isCancelled())
|
|
246
|
+
return "paused";
|
|
247
|
+
return "completed";
|
|
248
|
+
}
|
|
249
|
+
//# sourceMappingURL=turn-stream.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"turn-stream.js","sourceRoot":"","sources":["../../../src/activities/execute-cursor/turn-stream.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Approved-command turn provenance (DD-28) — the DEEP-AGENT (native) harness
|
|
3
|
+
* adapter over the shared qualification rule ({@link qualifyTurnCommandProvenance}).
|
|
4
|
+
*
|
|
5
|
+
* The deep-agent's consent model differs from Cursor's in exactly two ways, and
|
|
6
|
+
* this adapter is precisely those two differences:
|
|
7
|
+
*
|
|
8
|
+
* 1. IDENTITY turn scoping (not positional). Cursor scopes a turn by the message
|
|
9
|
+
* index its stream started at. The deep-agent cannot: an approved command
|
|
10
|
+
* executes IN PLACE at its seeded transcript position (it was proposed in a
|
|
11
|
+
* prior segment, seeded into this segment, and updated to COMPLETED by
|
|
12
|
+
* `StatusBuilder.findResumableSeededToolCall`). So "this turn's calls" are
|
|
13
|
+
* those whose id is absent from a pre-stream {@link collectSettledToolCallIds}
|
|
14
|
+
* snapshot, and "executed" is a COMPLETED status.
|
|
15
|
+
*
|
|
16
|
+
* 2. SAME-ROW direct consent (not a grant token). A gated deep-agent tool pauses
|
|
17
|
+
* on a LangGraph interrupt; SubmitApproval writes `approval_action` onto that
|
|
18
|
+
* same row, and the row keeps its id + `approval_action` when it executes on
|
|
19
|
+
* resume. So the executed command IS its own consent row — cite its own id.
|
|
20
|
+
* (The server's PreserveApprovalFields keeps that `approval_action` on the row
|
|
21
|
+
* across the status write regardless of its new status, so the backend's
|
|
22
|
+
* verification finds it.)
|
|
23
|
+
*
|
|
24
|
+
* A turn that delegated ANY sub-agent fails closed (DD-28 D1: "delegated zero
|
|
25
|
+
* sub-agents"). A sub-agent's writes fold into the parent turn's change set
|
|
26
|
+
* (DD-19) but are not attributable to a consented top-level command. The
|
|
27
|
+
* top-level `task` call (a SUBAGENT kind) already trips the shared rule's
|
|
28
|
+
* `!== SHELL` fail-closed; the explicit sub-agent-novelty guard below also covers
|
|
29
|
+
* the (currently-impossible under the replace-per-turn model) case of a sub-agent
|
|
30
|
+
* that mutates across a turn boundary without a fresh top-level `task` row.
|
|
31
|
+
*
|
|
32
|
+
* (Trust boundary + fail-closed contract: see the shared module.)
|
|
33
|
+
*/
|
|
34
|
+
import type { AgentExecutionStatus } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/api_pb";
|
|
35
|
+
import type { TurnCommandProvenance } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/filereview_pb";
|
|
36
|
+
export interface DeepAgentCommandProvenanceInputs {
|
|
37
|
+
/** The status at capture time — top-level messages + sub-agent executions. */
|
|
38
|
+
readonly status: AgentExecutionStatus;
|
|
39
|
+
/**
|
|
40
|
+
* The top-level tool-call ids that had already SETTLED before this turn's
|
|
41
|
+
* stream (from {@link collectSettledToolCallIds}). A top-level call whose id is
|
|
42
|
+
* absent is this-turn's; the shared rule then keeps only the COMPLETED ones.
|
|
43
|
+
*/
|
|
44
|
+
readonly priorSettledToolCallIds: ReadonlySet<string>;
|
|
45
|
+
/**
|
|
46
|
+
* The sub-agent tool-call ids that existed before this turn's stream (from
|
|
47
|
+
* {@link collectSubAgentToolCallIds}). Any growth means a sub-agent acted this
|
|
48
|
+
* turn → fail closed.
|
|
49
|
+
*/
|
|
50
|
+
readonly priorSubAgentToolCallIds: ReadonlySet<string>;
|
|
51
|
+
/** True when the pre-armed spec.auto_approve_all bypassed the gate. */
|
|
52
|
+
readonly globalBypass: boolean;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Derive the {@link TurnCommandProvenance} for a deep-agent turn, or undefined
|
|
56
|
+
* when it does not qualify. Scopes this turn's top-level calls by id-novelty,
|
|
57
|
+
* resolves consent from the row's own server-authored `approval_action`, fails
|
|
58
|
+
* closed on any sub-agent activity, and delegates the DD-28 rule to
|
|
59
|
+
* {@link qualifyTurnCommandProvenance}.
|
|
60
|
+
*/
|
|
61
|
+
export declare function deriveTurnCommandProvenance(inputs: DeepAgentCommandProvenanceInputs): TurnCommandProvenance | undefined;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Approved-command turn provenance (DD-28) — the DEEP-AGENT (native) harness
|
|
3
|
+
* adapter over the shared qualification rule ({@link qualifyTurnCommandProvenance}).
|
|
4
|
+
*
|
|
5
|
+
* The deep-agent's consent model differs from Cursor's in exactly two ways, and
|
|
6
|
+
* this adapter is precisely those two differences:
|
|
7
|
+
*
|
|
8
|
+
* 1. IDENTITY turn scoping (not positional). Cursor scopes a turn by the message
|
|
9
|
+
* index its stream started at. The deep-agent cannot: an approved command
|
|
10
|
+
* executes IN PLACE at its seeded transcript position (it was proposed in a
|
|
11
|
+
* prior segment, seeded into this segment, and updated to COMPLETED by
|
|
12
|
+
* `StatusBuilder.findResumableSeededToolCall`). So "this turn's calls" are
|
|
13
|
+
* those whose id is absent from a pre-stream {@link collectSettledToolCallIds}
|
|
14
|
+
* snapshot, and "executed" is a COMPLETED status.
|
|
15
|
+
*
|
|
16
|
+
* 2. SAME-ROW direct consent (not a grant token). A gated deep-agent tool pauses
|
|
17
|
+
* on a LangGraph interrupt; SubmitApproval writes `approval_action` onto that
|
|
18
|
+
* same row, and the row keeps its id + `approval_action` when it executes on
|
|
19
|
+
* resume. So the executed command IS its own consent row — cite its own id.
|
|
20
|
+
* (The server's PreserveApprovalFields keeps that `approval_action` on the row
|
|
21
|
+
* across the status write regardless of its new status, so the backend's
|
|
22
|
+
* verification finds it.)
|
|
23
|
+
*
|
|
24
|
+
* A turn that delegated ANY sub-agent fails closed (DD-28 D1: "delegated zero
|
|
25
|
+
* sub-agents"). A sub-agent's writes fold into the parent turn's change set
|
|
26
|
+
* (DD-19) but are not attributable to a consented top-level command. The
|
|
27
|
+
* top-level `task` call (a SUBAGENT kind) already trips the shared rule's
|
|
28
|
+
* `!== SHELL` fail-closed; the explicit sub-agent-novelty guard below also covers
|
|
29
|
+
* the (currently-impossible under the replace-per-turn model) case of a sub-agent
|
|
30
|
+
* that mutates across a turn boundary without a fresh top-level `task` row.
|
|
31
|
+
*
|
|
32
|
+
* (Trust boundary + fail-closed contract: see the shared module.)
|
|
33
|
+
*/
|
|
34
|
+
import { ApprovalAction, ToolCallStatus, } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
|
|
35
|
+
import { qualifyTurnCommandProvenance } from "../../shared/filereview/command-provenance.js";
|
|
36
|
+
import { collectSubAgentToolCallIds } from "../../shared/tool-row.js";
|
|
37
|
+
/**
|
|
38
|
+
* Derive the {@link TurnCommandProvenance} for a deep-agent turn, or undefined
|
|
39
|
+
* when it does not qualify. Scopes this turn's top-level calls by id-novelty,
|
|
40
|
+
* resolves consent from the row's own server-authored `approval_action`, fails
|
|
41
|
+
* closed on any sub-agent activity, and delegates the DD-28 rule to
|
|
42
|
+
* {@link qualifyTurnCommandProvenance}.
|
|
43
|
+
*/
|
|
44
|
+
export function deriveTurnCommandProvenance(inputs) {
|
|
45
|
+
const { status, priorSettledToolCallIds, priorSubAgentToolCallIds, globalBypass } = inputs;
|
|
46
|
+
// DD-28 D1 cond 2: any sub-agent activity this turn disqualifies. A sub-agent
|
|
47
|
+
// that ran this turn contributes at least one tool-call id absent from the
|
|
48
|
+
// pre-stream snapshot.
|
|
49
|
+
for (const id of collectSubAgentToolCallIds(status.subAgentExecutions)) {
|
|
50
|
+
if (!priorSubAgentToolCallIds.has(id))
|
|
51
|
+
return undefined;
|
|
52
|
+
}
|
|
53
|
+
const messages = status.messages;
|
|
54
|
+
// This turn's top-level calls: those not already settled before the stream.
|
|
55
|
+
const turnToolCalls = messages
|
|
56
|
+
.flatMap((m) => m.toolCalls)
|
|
57
|
+
.filter((tc) => !priorSettledToolCallIds.has(tc.id));
|
|
58
|
+
return qualifyTurnCommandProvenance({
|
|
59
|
+
turnToolCalls,
|
|
60
|
+
messages,
|
|
61
|
+
// A deep-agent command executed iff its row reached COMPLETED this turn.
|
|
62
|
+
isExecutedCommand: (tc) => tc.status === ToolCallStatus.TOOL_CALL_COMPLETED,
|
|
63
|
+
// The gated command carries its own server-authored approval_action, written
|
|
64
|
+
// in place by SubmitApproval and preserved when the row executes on resume.
|
|
65
|
+
resolveDirectConsent: (tc) => tc.approvalAction === ApprovalAction.APPROVE ||
|
|
66
|
+
tc.approvalAction === ApprovalAction.APPROVE_ALL
|
|
67
|
+
? tc.id
|
|
68
|
+
: undefined,
|
|
69
|
+
globalBypass,
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=command-provenance.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"command-provenance.js","sourceRoot":"","sources":["../../../src/activities/execute-deep-agent/command-provenance.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAEH,OAAO,EACL,cAAc,EACd,cAAc,GACf,MAAM,8DAA8D,CAAC;AAGtE,OAAO,EAAE,4BAA4B,EAAE,MAAM,+CAA+C,CAAC;AAC7F,OAAO,EAAE,0BAA0B,EAAE,MAAM,0BAA0B,CAAC;AAqBtE;;;;;;GAMG;AACH,MAAM,UAAU,2BAA2B,CACzC,MAAwC;IAExC,MAAM,EAAE,MAAM,EAAE,uBAAuB,EAAE,wBAAwB,EAAE,YAAY,EAAE,GAAG,MAAM,CAAC;IAE3F,8EAA8E;IAC9E,2EAA2E;IAC3E,uBAAuB;IACvB,KAAK,MAAM,EAAE,IAAI,0BAA0B,CAAC,MAAM,CAAC,kBAAkB,CAAC,EAAE,CAAC;QACvE,IAAI,CAAC,wBAAwB,CAAC,GAAG,CAAC,EAAE,CAAC;YAAE,OAAO,SAAS,CAAC;IAC1D,CAAC;IAED,MAAM,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC;IACjC,4EAA4E;IAC5E,MAAM,aAAa,GAAG,QAAQ;SAC3B,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;SAC3B,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,uBAAuB,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;IAEvD,OAAO,4BAA4B,CAAC;QAClC,aAAa;QACb,QAAQ;QACR,yEAAyE;QACzE,iBAAiB,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,MAAM,KAAK,cAAc,CAAC,mBAAmB;QAC3E,6EAA6E;QAC7E,4EAA4E;QAC5E,oBAAoB,EAAE,CAAC,EAAE,EAAE,EAAE,CAC3B,EAAE,CAAC,cAAc,KAAK,cAAc,CAAC,OAAO;YAC5C,EAAE,CAAC,cAAc,KAAK,cAAc,CAAC,WAAW;YAC9C,CAAC,CAAC,EAAE,CAAC,EAAE;YACP,CAAC,CAAC,SAAS;QACf,YAAY;KACb,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -19,6 +19,7 @@ import { activityStarted, activityFinished } from "../../idle-watchdog.js";
|
|
|
19
19
|
import { normalizeActivityInput } from "../../shared/activity-input.js";
|
|
20
20
|
import { persistStatus, reportSetupProgress, slimStatus, utcTimestamp } from "../../shared/status.js";
|
|
21
21
|
import { acquireWorkspaceLock, WorkspaceLockCancelledError, WorkspaceLockTimeoutError, } from "../../shared/workspace/workspace-lock.js";
|
|
22
|
+
import { ensureStigmerSymlink, removeStigmerSymlink, } from "../../shared/workspace/stigmer-link.js";
|
|
22
23
|
import { publishPlanArtifact } from "../../shared/plan-artifact.js";
|
|
23
24
|
import { classifyTool } from "../../shared/tool-kind.js";
|
|
24
25
|
import { POLICY_ENGINE_VERSION, toProtoPolicySource, } from "../../shared/approval-policy.js";
|
|
@@ -33,12 +34,14 @@ import { processPostStream } from "./post-stream.js";
|
|
|
33
34
|
import { resolveResumeInput } from "./hitl.js";
|
|
34
35
|
import { captureApprovalArtifacts } from "./approval-file-change.js";
|
|
35
36
|
import { applyCaptureDecisions, captureBaselineToLedger, captureCandidateToLedger, } from "../../shared/filereview/capture.js";
|
|
36
|
-
import { captureFileChangeProgress, newProgressCaptureState, } from "../../shared/filereview/progress.js";
|
|
37
|
+
import { captureFileChangeProgress, createGitProgressSubstrate, createHybridProgressSubstrate, newProgressCaptureState, } from "../../shared/filereview/progress.js";
|
|
38
|
+
import { createCasProgressSubstrate, } from "../../shared/filereview/cas-progress.js";
|
|
37
39
|
import { hasCandidateCaptured } from "../../shared/filereview/events.js";
|
|
38
40
|
import { casBlobReader } from "../../shared/filereview/cas-substrate.js";
|
|
39
41
|
import { partitionIgnoredPathsBySecret } from "../../shared/filereview/secret-paths.js";
|
|
40
|
-
import { collectSubAgentToolCallIds, withholdSecretContentFromMessages } from "../../shared/tool-row.js";
|
|
42
|
+
import { collectSettledToolCallIds, collectSubAgentToolCallIds, withholdSecretContentFromMessages, } from "../../shared/tool-row.js";
|
|
41
43
|
import { stampFlowedFileEditRows, stampFlowedSubAgentFileEditRows } from "./stamp-flowed-rows.js";
|
|
44
|
+
import { deriveTurnCommandProvenance } from "./command-provenance.js";
|
|
42
45
|
/** The harness id stamped on the deep-agent's file-review ledger events. */
|
|
43
46
|
const DEEP_AGENT_HARNESS_ID = "deep-agent";
|
|
44
47
|
export function createDeepAgentActivities(config) {
|
|
@@ -170,6 +173,21 @@ export function createDeepAgentActivities(config) {
|
|
|
170
173
|
}
|
|
171
174
|
throw lockErr;
|
|
172
175
|
}
|
|
176
|
+
// Bridge the workspace to the session's platform dir so the agent's
|
|
177
|
+
// file tools can read platform-mounted content (`.stigmer/inputs/…`
|
|
178
|
+
// attachments incl. the approved plan, `.stigmer/skills/…`) — the
|
|
179
|
+
// deepagents FilesystemBackend resolves paths against the workspace
|
|
180
|
+
// root and knows nothing of the LocalWorkspaceBackend's `.stigmer`
|
|
181
|
+
// routing. Two ordering invariants (see shared/workspace/stigmer-link.ts):
|
|
182
|
+
// - AFTER the lock: the link is a tree mutation; sessions sharing one
|
|
183
|
+
// attached directory must not re-point it under a running turn.
|
|
184
|
+
// - BEFORE the baseline capture below: present in both the baseline
|
|
185
|
+
// and candidate trees, the link cancels out of the file-review diff
|
|
186
|
+
// even when the workspace's git excludes do not list `.stigmer`.
|
|
187
|
+
// Removed in cleanup() (the finally below), before the lock releases.
|
|
188
|
+
if (setup.workspaceBackend.platformDir) {
|
|
189
|
+
await ensureStigmerSymlink(gitRoot, setup.workspaceBackend.platformDir);
|
|
190
|
+
}
|
|
173
191
|
// (1) Capture-mode resume — reconcile any DECIDED change set FIRST (this
|
|
174
192
|
// drops the per-execution refs), before the next baseline re-pins them
|
|
175
193
|
// (refs are executionId-keyed). Then (2) short-circuit a pure file-review
|
|
@@ -254,8 +272,10 @@ export function createDeepAgentActivities(config) {
|
|
|
254
272
|
}
|
|
255
273
|
}
|
|
256
274
|
// Turn start (capture mode): pin the pre-turn tree + author BASELINE. Taken
|
|
257
|
-
// AFTER any reconcile above and AFTER performSetup's workspace writes
|
|
258
|
-
// the
|
|
275
|
+
// AFTER any reconcile above and AFTER performSetup's workspace writes and
|
|
276
|
+
// the `.stigmer` symlink above, so the baseline absorbs the reconciled
|
|
277
|
+
// state and the runner-owned files (the symlink then cancels out of the
|
|
278
|
+
// baseline→candidate diff).
|
|
259
279
|
let captureBaselineTree = "";
|
|
260
280
|
// Snapshot the sub-agent tool-call ids that exist BEFORE this turn's
|
|
261
281
|
// stream, so the turn-boundary stamp scopes itself to sub-agent rows
|
|
@@ -263,6 +283,12 @@ export function createDeepAgentActivities(config) {
|
|
|
263
283
|
// seeds prior sub-agents — but computed, not assumed, so it stays correct
|
|
264
284
|
// if that ever changes; see collectSubAgentToolCallIds).
|
|
265
285
|
const priorSubAgentToolCallIds = collectSubAgentToolCallIds(initialStatus.subAgentExecutions);
|
|
286
|
+
// Snapshot the top-level tool-call ids already SETTLED before this turn's
|
|
287
|
+
// stream, so the approved-command provenance (DD-28) scopes itself to THIS
|
|
288
|
+
// turn's executed commands by identity. The deep-agent's approved shell
|
|
289
|
+
// executes in place at its seeded position, so the Cursor positional scope
|
|
290
|
+
// would miss it — see execute-deep-agent/command-provenance.ts.
|
|
291
|
+
const priorSettledToolCallIds = collectSettledToolCallIds(initialStatus.messages);
|
|
266
292
|
if (setup.captureMode) {
|
|
267
293
|
captureBaselineTree = await captureBaselineToLedger({
|
|
268
294
|
status: initialStatus,
|
|
@@ -273,14 +299,31 @@ export function createDeepAgentActivities(config) {
|
|
|
273
299
|
gitWorkspace: setup.gitWorkspace,
|
|
274
300
|
});
|
|
275
301
|
}
|
|
276
|
-
// Per-turn state for mid-run live capture (DD-32
|
|
277
|
-
//
|
|
278
|
-
//
|
|
279
|
-
//
|
|
280
|
-
//
|
|
302
|
+
// Per-turn state + substrate for mid-run live capture (DD-32 / DD-33). The
|
|
303
|
+
// floor lives in progressState; the substrate is chosen for this turn's
|
|
304
|
+
// workspace shape and owns its own short-circuit cache. An atomic snapshot
|
|
305
|
+
// of the shared observer feeds the CAS/HYBRID substrates — copied
|
|
306
|
+
// synchronously so a concurrent sub-agent write cannot mutate it mid-read.
|
|
281
307
|
const progressState = newProgressCaptureState();
|
|
282
|
-
const
|
|
283
|
-
const
|
|
308
|
+
const casObserver = setup.casObserver;
|
|
309
|
+
const readObserverTouched = () => ({
|
|
310
|
+
before: new Map(casObserver.before),
|
|
311
|
+
blockedSecretPaths: new Set(casObserver.blockedSecretPaths),
|
|
312
|
+
});
|
|
313
|
+
// Git tree -> hybrid (numstat for tracked + observer for gitignored);
|
|
314
|
+
// non-git -> cas over the observer (no baseline tree needed). Undefined
|
|
315
|
+
// outside capture mode (writes are deny-gated, nothing is captured).
|
|
316
|
+
const progressSubstrate = !setup.captureMode
|
|
317
|
+
? undefined
|
|
318
|
+
: setup.gitWorkspace
|
|
319
|
+
? captureBaselineTree
|
|
320
|
+
? createHybridProgressSubstrate(createGitProgressSubstrate({
|
|
321
|
+
workspaceRoot: gitRoot,
|
|
322
|
+
executionId,
|
|
323
|
+
baselineTree: captureBaselineTree,
|
|
324
|
+
}), createCasProgressSubstrate({ workspaceRoot: gitRoot, read: readObserverTouched }))
|
|
325
|
+
: undefined
|
|
326
|
+
: createCasProgressSubstrate({ workspaceRoot: gitRoot, read: readObserverTouched });
|
|
284
327
|
const cancellationSignal = Context.current().cancellationSignal;
|
|
285
328
|
const result = await streamExecution({
|
|
286
329
|
agentGraph: setup.agentGraph,
|
|
@@ -305,20 +348,21 @@ export function createDeepAgentActivities(config) {
|
|
|
305
348
|
globalBypass: setup.globalBypass,
|
|
306
349
|
},
|
|
307
350
|
streamVersion: setup.streamVersion,
|
|
308
|
-
// Mid-run live capture (DD-32): attach file_change_progress
|
|
309
|
-
// scheduled persist
|
|
310
|
-
//
|
|
311
|
-
//
|
|
351
|
+
// Mid-run live capture (DD-32 / DD-33): attach file_change_progress
|
|
352
|
+
// before each scheduled persist, throttled by the floor inside
|
|
353
|
+
// captureFileChangeProgress. The substrate (git / non-git CAS / hybrid)
|
|
354
|
+
// was chosen for this turn above; the deep-agent's only runner-owned
|
|
355
|
+
// tree entry is the `.stigmer` symlink, created before the baseline
|
|
356
|
+
// so it appears identically in every capture and cancels out of the
|
|
357
|
+
// diff — the git slice therefore needs no excludePaths, matching its
|
|
358
|
+
// turn-boundary candidate capture.
|
|
312
359
|
beforePersist: async (status) => {
|
|
313
|
-
if (!
|
|
360
|
+
if (!progressSubstrate)
|
|
314
361
|
return;
|
|
315
|
-
}
|
|
316
362
|
await captureFileChangeProgress({
|
|
317
363
|
status,
|
|
318
|
-
gitRoot,
|
|
319
|
-
executionId,
|
|
320
364
|
changeSetId,
|
|
321
|
-
|
|
365
|
+
substrate: progressSubstrate,
|
|
322
366
|
state: progressState,
|
|
323
367
|
});
|
|
324
368
|
},
|
|
@@ -366,6 +410,23 @@ export function createDeepAgentActivities(config) {
|
|
|
366
410
|
? FileCaptureClass.GIT_IGNORED_CAPTURED
|
|
367
411
|
: FileCaptureClass.NON_GIT_CAS;
|
|
368
412
|
const { casCaptures, unreviewablePaths } = await buildCasTurnCaptures(setup.casObserver, gitRoot, casCaptureClass);
|
|
413
|
+
// Approved-command turn facts (DD-28): when every mutation-capable call
|
|
414
|
+
// this turn was a consented shell command, attach the provenance so the
|
|
415
|
+
// backend can verify the cited consent rows and auto-keep the set
|
|
416
|
+
// instead of arming a second review gate. Fail-closed: any non-qualifying
|
|
417
|
+
// turn attaches nothing and reviews manually exactly as before. Attached
|
|
418
|
+
// only when captureCandidateToLedger actually authors a CANDIDATE.
|
|
419
|
+
const commandProvenance = deriveTurnCommandProvenance({
|
|
420
|
+
status: initialStatus,
|
|
421
|
+
priorSettledToolCallIds,
|
|
422
|
+
priorSubAgentToolCallIds,
|
|
423
|
+
globalBypass: setup.globalBypass,
|
|
424
|
+
});
|
|
425
|
+
if (commandProvenance) {
|
|
426
|
+
console.log(`[ExecuteDeepAgent] capture: turn qualifies for approved-command auto-keep ` +
|
|
427
|
+
`(consent rows: ${commandProvenance.consentToolCallIds.join(",") || "(auto_approve_all)"}); ` +
|
|
428
|
+
`attaching provenance to candidate (execution=${executionId})`);
|
|
429
|
+
}
|
|
369
430
|
await captureCandidateToLedger({
|
|
370
431
|
status: initialStatus,
|
|
371
432
|
gitRoot,
|
|
@@ -378,6 +439,7 @@ export function createDeepAgentActivities(config) {
|
|
|
378
439
|
unreviewablePaths,
|
|
379
440
|
unreviewableCaptureClass: casCaptureClass,
|
|
380
441
|
gitWorkspace: setup.gitWorkspace,
|
|
442
|
+
commandProvenance,
|
|
381
443
|
});
|
|
382
444
|
// Review is pending iff a CANDIDATE was actually authored (the seam
|
|
383
445
|
// drops no-op captures), so a turn that only touched-then-unchanged an
|
|
@@ -736,5 +798,11 @@ async function cleanup(setup) {
|
|
|
736
798
|
console.warn("[ExecuteDeepAgent] MCP connection cleanup failed:", err);
|
|
737
799
|
}
|
|
738
800
|
}
|
|
801
|
+
// Drop the workspace→platform `.stigmer` symlink so an attached repo is
|
|
802
|
+
// left untouched between turns (issue #173 semantics; a multi-turn session
|
|
803
|
+
// recreates it next turn). Runs in the activity finally, BEFORE the
|
|
804
|
+
// workspace lock releases, so the next queued turn baselines a clean tree.
|
|
805
|
+
// Best-effort and symlink-only: a real `.stigmer` directory is never removed.
|
|
806
|
+
await removeStigmerSymlink(setup.workspaceBackend.rootDir);
|
|
739
807
|
}
|
|
740
808
|
//# sourceMappingURL=index.js.map
|