@stigmer/runner 3.1.3 → 3.1.4
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/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 +296 -477
- package/dist/activities/execute-cursor/index.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 +60 -18
- 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/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.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/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/capture-flow.ts +71 -25
- package/src/activities/execute-cursor/command-provenance.ts +25 -120
- package/src/activities/execute-cursor/index.ts +344 -504
- 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/command-provenance.ts +102 -0
- package/src/activities/execute-deep-agent/index.ts +74 -18
- 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/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 +9 -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
|
@@ -189,6 +189,66 @@ export async function snapshotCasChangeSet(opts: {
|
|
|
189
189
|
};
|
|
190
190
|
}
|
|
191
191
|
|
|
192
|
+
/**
|
|
193
|
+
* The display-facing classification of one CAS path's before/after bytes: the
|
|
194
|
+
* change kind, whether either side is binary, and the `+N −M` line counts.
|
|
195
|
+
*/
|
|
196
|
+
export interface CasChangeClassification {
|
|
197
|
+
/** Path before the change (workspace-relative). Empty for ADD. */
|
|
198
|
+
readonly pathBefore: string;
|
|
199
|
+
/** Path after the change. Empty for DELETE. */
|
|
200
|
+
readonly pathAfter: string;
|
|
201
|
+
readonly kind: FileChangeKind;
|
|
202
|
+
/** A NUL byte on either side — the diff cannot render as text. */
|
|
203
|
+
readonly isBinary: boolean;
|
|
204
|
+
/** `+N −M` from the in-memory bytes; absent for a binary or oversized side. */
|
|
205
|
+
readonly lineCounts?: LineChangeCounts;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Classify one CAS path's before/after bytes. Returns `undefined` for a
|
|
210
|
+
* non-change — both sides absent, or a touch that left the bytes unchanged —
|
|
211
|
+
* exactly as the git substrate's diff omits unchanged files.
|
|
212
|
+
*
|
|
213
|
+
* This is the SINGLE classification authority for a CAS before/after pair: the
|
|
214
|
+
* turn-boundary capture ({@link buildCasCapturedFile}) and the mid-run progress
|
|
215
|
+
* producer ({@link ./cas-progress.js}) both route through it, so the live "N
|
|
216
|
+
* files changed so far" strip and the reviewed change set can never disagree on
|
|
217
|
+
* an edge case (the trailing-newline class of bug {@link ./line-counts.js}
|
|
218
|
+
* warns about). Kind, no-op detection, binary detection, and counting live here
|
|
219
|
+
* and nowhere else.
|
|
220
|
+
*/
|
|
221
|
+
export function classifyCasChange(
|
|
222
|
+
path: string,
|
|
223
|
+
beforeBuf: Buffer | null,
|
|
224
|
+
afterBuf: Buffer | null,
|
|
225
|
+
): CasChangeClassification | undefined {
|
|
226
|
+
if (beforeBuf === null && afterBuf === null) return undefined;
|
|
227
|
+
// A touch that did not change the bytes is not a reviewable change.
|
|
228
|
+
if (beforeBuf && afterBuf && beforeBuf.equals(afterBuf)) return undefined;
|
|
229
|
+
|
|
230
|
+
const kind =
|
|
231
|
+
beforeBuf === null
|
|
232
|
+
? FileChangeKind.ADD
|
|
233
|
+
: afterBuf === null
|
|
234
|
+
? FileChangeKind.DELETE
|
|
235
|
+
: FileChangeKind.MODIFY;
|
|
236
|
+
const isBinary =
|
|
237
|
+
(beforeBuf !== null && bytesLookBinary(beforeBuf)) ||
|
|
238
|
+
(afterBuf !== null && bytesLookBinary(afterBuf));
|
|
239
|
+
|
|
240
|
+
return {
|
|
241
|
+
pathBefore: kind === FileChangeKind.ADD ? "" : path,
|
|
242
|
+
pathAfter: kind === FileChangeKind.DELETE ? "" : path,
|
|
243
|
+
kind,
|
|
244
|
+
isBinary,
|
|
245
|
+
// A binary change has no text line diff to count.
|
|
246
|
+
lineCounts: isBinary
|
|
247
|
+
? undefined
|
|
248
|
+
: countLineChanges(beforeBuf?.toString("utf8"), afterBuf?.toString("utf8")),
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
|
|
192
252
|
/**
|
|
193
253
|
* Store one path's before/after blobs and classify the change. Returns
|
|
194
254
|
* `undefined` for a no-op (both sides absent, or an unchanged touch).
|
|
@@ -199,44 +259,29 @@ async function buildCasCapturedFile(
|
|
|
199
259
|
capture: CasPathCapture,
|
|
200
260
|
): Promise<CasCapturedFile | undefined> {
|
|
201
261
|
const { path, before, after, captureClass } = capture;
|
|
202
|
-
if (before === null && after === null) return undefined;
|
|
203
|
-
|
|
204
262
|
const beforeBuf = before === null ? null : Buffer.from(before);
|
|
205
263
|
const afterBuf = after === null ? null : Buffer.from(after);
|
|
206
264
|
|
|
207
|
-
|
|
208
|
-
if (
|
|
265
|
+
const classification = classifyCasChange(path, beforeBuf, afterBuf);
|
|
266
|
+
if (!classification) return undefined;
|
|
209
267
|
|
|
210
268
|
const beforeRef = beforeBuf ? await storeBlob(storage, executionId, beforeBuf) : undefined;
|
|
211
269
|
const afterRef = afterBuf ? await storeBlob(storage, executionId, afterBuf) : undefined;
|
|
212
270
|
|
|
213
|
-
const kind =
|
|
214
|
-
beforeRef === undefined
|
|
215
|
-
? FileChangeKind.ADD
|
|
216
|
-
: afterRef === undefined
|
|
217
|
-
? FileChangeKind.DELETE
|
|
218
|
-
: FileChangeKind.MODIFY;
|
|
219
|
-
|
|
220
|
-
const isCreate = kind === FileChangeKind.ADD;
|
|
221
|
-
const isDelete = kind === FileChangeKind.DELETE;
|
|
222
|
-
const isBinary = beforeRef?.isBinary || afterRef?.isBinary;
|
|
223
|
-
|
|
224
271
|
return {
|
|
225
|
-
pathBefore:
|
|
226
|
-
pathAfter:
|
|
227
|
-
kind,
|
|
272
|
+
pathBefore: classification.pathBefore,
|
|
273
|
+
pathAfter: classification.pathAfter,
|
|
274
|
+
kind: classification.kind,
|
|
228
275
|
captureClass,
|
|
229
276
|
before: beforeRef,
|
|
230
277
|
after: afterRef,
|
|
231
278
|
// Binary on either side means the diff cannot render as text; the change set
|
|
232
279
|
// then cannot be approved as complete (parity with the git substrate).
|
|
233
|
-
diffComplete: !isBinary,
|
|
280
|
+
diffComplete: !classification.isBinary,
|
|
234
281
|
// Display counts are taken NOW, while the text bytes are still in memory —
|
|
235
|
-
// after this they exist only as offloaded blobs
|
|
236
|
-
//
|
|
237
|
-
lineCounts:
|
|
238
|
-
? undefined
|
|
239
|
-
: countLineChanges(beforeBuf?.toString("utf8"), afterBuf?.toString("utf8")),
|
|
282
|
+
// after this they exist only as offloaded blobs (the same counts the mid-run
|
|
283
|
+
// progress producer derives via classifyCasChange).
|
|
284
|
+
lineCounts: classification.lineCounts,
|
|
240
285
|
};
|
|
241
286
|
}
|
|
242
287
|
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The DD-28 approved-command turn qualification rule — the harness-agnostic core
|
|
3
|
+
* of the auto-keep policy's runner-side facts.
|
|
4
|
+
*
|
|
5
|
+
* A turn whose ONLY mutation source was shell commands the human had already
|
|
6
|
+
* authorized should not re-gate its file effects at the turn-boundary review —
|
|
7
|
+
* the user consented to the command, and the command's file effects are the
|
|
8
|
+
* consented outcome. The server cannot derive this itself (tool calls carry no
|
|
9
|
+
* turn marker), so the runner — the only component that owns turn scoping —
|
|
10
|
+
* derives the facts and attaches them to the CANDIDATE_CAPTURED event as
|
|
11
|
+
* {@link TurnCommandProvenance}.
|
|
12
|
+
*
|
|
13
|
+
* This module owns the *qualification rule* both harnesses share; each harness
|
|
14
|
+
* supplies only what genuinely differs — how "this turn's tool calls" are scoped,
|
|
15
|
+
* and how a single executed command's DIRECT consent row is identified — via
|
|
16
|
+
* {@link CommandProvenanceCoreInputs}. Keeping the rule in one place is what makes
|
|
17
|
+
* the Cursor and deep-agent qualifications provably identical (a second copy would
|
|
18
|
+
* drift on edge cases — the divergence the AI-Engineer role warns against).
|
|
19
|
+
*
|
|
20
|
+
* TRUST BOUNDARY. This module asserts turn FACTS (which tools ran this turn),
|
|
21
|
+
* the same trust level as the captured bytes themselves. It never asserts
|
|
22
|
+
* CONSENT: a resolved consent id merely POINTS at a transcript row whose
|
|
23
|
+
* `approval_action` was authored by the server's SubmitApproval (and is
|
|
24
|
+
* preserved against runner writes) — the backend re-verifies every claimed row
|
|
25
|
+
* against that server-owned record before authoring the policy decision, so a
|
|
26
|
+
* runner cannot mint authorization it was never given.
|
|
27
|
+
*
|
|
28
|
+
* FAIL-CLOSED. Every uncertainty disqualifies (returns undefined → the set
|
|
29
|
+
* reviews manually, exactly as before DD-28): a file-tool call, an MCP tool, a
|
|
30
|
+
* sub-agent delegation, an unrecognized tool name, or an executed shell command
|
|
31
|
+
* with no provable consent source. Being conservative here costs only an extra
|
|
32
|
+
* review; being permissive would silently waive one.
|
|
33
|
+
*/
|
|
34
|
+
|
|
35
|
+
import { create } from "@bufbuild/protobuf";
|
|
36
|
+
import type { AgentMessage, ToolCall } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/message_pb";
|
|
37
|
+
import { TurnCommandProvenanceSchema } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/filereview_pb";
|
|
38
|
+
import type { TurnCommandProvenance } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/filereview_pb";
|
|
39
|
+
import { ApprovalAction, ToolKind } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
|
|
40
|
+
import { classifyTool, toolApprovalCategory } from "../tool-kind.js";
|
|
41
|
+
import { isToolCallRowHidden } from "../tool-row.js";
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Tool kinds that can never mutate the workspace: their presence in a turn is
|
|
45
|
+
* irrelevant to change-set provenance. Everything OUTSIDE this set is either a
|
|
46
|
+
* consent-mapped shell command or a disqualifier — never silently ignored.
|
|
47
|
+
*/
|
|
48
|
+
export const NON_MUTATING_KINDS: ReadonlySet<ToolKind> = new Set([
|
|
49
|
+
ToolKind.FILE_READ,
|
|
50
|
+
ToolKind.SEARCH,
|
|
51
|
+
ToolKind.LIST,
|
|
52
|
+
ToolKind.FETCH,
|
|
53
|
+
ToolKind.WEB_SEARCH,
|
|
54
|
+
ToolKind.THINK,
|
|
55
|
+
ToolKind.TODO,
|
|
56
|
+
]);
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* The harness-supplied inputs to the shared DD-28 qualification rule. The two
|
|
60
|
+
* function inputs are exactly the two things that differ between harnesses
|
|
61
|
+
* (turn scoping is expressed by which calls appear in {@link turnToolCalls}).
|
|
62
|
+
*/
|
|
63
|
+
export interface CommandProvenanceCoreInputs {
|
|
64
|
+
/**
|
|
65
|
+
* This turn's tool calls, already scoped by the harness (Cursor: the positional
|
|
66
|
+
* slice from the turn's first streamed message; deep-agent: the calls whose id
|
|
67
|
+
* is new since a pre-stream settled snapshot). Order is preserved but irrelevant.
|
|
68
|
+
*/
|
|
69
|
+
readonly turnToolCalls: readonly ToolCall[];
|
|
70
|
+
/**
|
|
71
|
+
* The WHOLE transcript, for the APPROVE_ALL lease lookup — a lease is
|
|
72
|
+
* run-lifetime and its granting row usually lives in a prior turn.
|
|
73
|
+
*/
|
|
74
|
+
readonly messages: readonly AgentMessage[];
|
|
75
|
+
/**
|
|
76
|
+
* Whether a (shell) tool call actually EXECUTED this turn, as opposed to being
|
|
77
|
+
* a pending/denied gate that produced no mutation. A non-executed shell neither
|
|
78
|
+
* needs consent nor disqualifies (Cursor: not in the denial ledger; deep-agent:
|
|
79
|
+
* status is COMPLETED).
|
|
80
|
+
*/
|
|
81
|
+
readonly isExecutedCommand: (tc: ToolCall) => boolean;
|
|
82
|
+
/**
|
|
83
|
+
* The tool-call id of the row whose server-authored `approval_action` DIRECTLY
|
|
84
|
+
* authorized this executed command, or undefined when none does (then the lease
|
|
85
|
+
* / global-bypass fallbacks apply). Cursor maps a reinvocation grant token back
|
|
86
|
+
* to its consent row; the deep-agent's gated command IS its own consent row
|
|
87
|
+
* (same id, `approval_action` written in place).
|
|
88
|
+
*/
|
|
89
|
+
readonly resolveDirectConsent: (tc: ToolCall) => string | undefined;
|
|
90
|
+
/** True when the pre-armed spec.auto_approve_all bypassed the gate. */
|
|
91
|
+
readonly globalBypass: boolean;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Apply the DD-28 qualification rule to a harness-scoped turn, returning the
|
|
96
|
+
* {@link TurnCommandProvenance} to attach to the candidate — or undefined when
|
|
97
|
+
* the turn does not qualify (any non-shell mutation-capable call, any unknown
|
|
98
|
+
* tool, any executed command without a provable consent source, or no executed
|
|
99
|
+
* consented command at all).
|
|
100
|
+
*/
|
|
101
|
+
export function qualifyTurnCommandProvenance(
|
|
102
|
+
inputs: CommandProvenanceCoreInputs,
|
|
103
|
+
): TurnCommandProvenance | undefined {
|
|
104
|
+
const { turnToolCalls, messages, isExecutedCommand, resolveDirectConsent, globalBypass } = inputs;
|
|
105
|
+
|
|
106
|
+
const consentIds = new Set<string>();
|
|
107
|
+
let authorizedByAutoApproveAll = false;
|
|
108
|
+
let executedCommandCount = 0;
|
|
109
|
+
|
|
110
|
+
for (const tc of turnToolCalls) {
|
|
111
|
+
// A hidden row is a collapsed twin/reaction — it never executed.
|
|
112
|
+
if (isToolCallRowHidden(tc)) continue;
|
|
113
|
+
|
|
114
|
+
const kind = classifyTool(tc.name, tc.mcpServerSlug);
|
|
115
|
+
if (NON_MUTATING_KINDS.has(kind)) continue;
|
|
116
|
+
|
|
117
|
+
if (kind !== ToolKind.SHELL) {
|
|
118
|
+
// A file tool (mixed turn), a sub-agent (unattributable mutations), an
|
|
119
|
+
// MCP tool (opaque side effects), or an unrecognized name — the turn's
|
|
120
|
+
// mutations cannot be attributed to consented commands. Fail closed.
|
|
121
|
+
return undefined;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (!isExecutedCommand(tc)) {
|
|
125
|
+
// A pending / denied shell — this turn's gate, not a mutation source.
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
executedCommandCount++;
|
|
129
|
+
|
|
130
|
+
const consentId = resolveDirectConsent(tc);
|
|
131
|
+
if (consentId) {
|
|
132
|
+
consentIds.add(consentId);
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
const leaseConsentId = findLeaseConsentId(messages, tc.name);
|
|
136
|
+
if (leaseConsentId) {
|
|
137
|
+
consentIds.add(leaseConsentId);
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
if (globalBypass) {
|
|
141
|
+
authorizedByAutoApproveAll = true;
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
144
|
+
// An executed shell command with no grant, no lease, and no bypass should
|
|
145
|
+
// be impossible (every un-consented shell is gated); if it ever happens,
|
|
146
|
+
// the honest answer is a manual review, not a waived one.
|
|
147
|
+
return undefined;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// A turn that executed no consented command has nothing to attribute the
|
|
151
|
+
// change set to — whatever changed came from somewhere else. Manual review.
|
|
152
|
+
if (executedCommandCount === 0) return undefined;
|
|
153
|
+
|
|
154
|
+
return create(TurnCommandProvenanceSchema, {
|
|
155
|
+
consentToolCallIds: [...consentIds],
|
|
156
|
+
authorizedByAutoApproveAll,
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Find the transcript row whose APPROVE_ALL authored the run-lifetime lease
|
|
162
|
+
* covering `toolName`'s category — the consent row a lease-executed command
|
|
163
|
+
* cites. Searched across the whole transcript (a lease usually originates in a
|
|
164
|
+
* prior turn); the first (earliest) APPROVE_ALL of the category is the lease's
|
|
165
|
+
* origin. Returns undefined when no lease row exists for the category.
|
|
166
|
+
*/
|
|
167
|
+
export function findLeaseConsentId(
|
|
168
|
+
messages: readonly AgentMessage[],
|
|
169
|
+
toolName: string,
|
|
170
|
+
): string | undefined {
|
|
171
|
+
const category = toolApprovalCategory(toolName);
|
|
172
|
+
if (!category) return undefined;
|
|
173
|
+
for (const msg of messages) {
|
|
174
|
+
for (const tc of msg.toolCalls) {
|
|
175
|
+
if (tc.approvalAction !== ApprovalAction.APPROVE_ALL) continue;
|
|
176
|
+
if (toolApprovalCategory(tc.name) === category) return tc.id;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
return undefined;
|
|
180
|
+
}
|
|
@@ -325,8 +325,14 @@ export interface GitProgressEntry {
|
|
|
325
325
|
readonly linesRemoved: number;
|
|
326
326
|
}
|
|
327
327
|
|
|
328
|
-
/**
|
|
329
|
-
|
|
328
|
+
/**
|
|
329
|
+
* Result of {@link captureProgressDelta} — the git-numstat delta. Distinct from
|
|
330
|
+
* the substrate-neutral `ProgressDelta` (in {@link ./progress.js}) that the
|
|
331
|
+
* git/cas/hybrid substrates emit: this carries the working-tree sha (for the
|
|
332
|
+
* short-circuit) and git's `FileChangeType` entries; the neutral one carries
|
|
333
|
+
* proto `FileChangeKind` entries and an honest total.
|
|
334
|
+
*/
|
|
335
|
+
export interface GitProgressDelta {
|
|
330
336
|
/** The post-diff working-tree sha, for the caller's short-circuit cache. */
|
|
331
337
|
readonly afterTree: string;
|
|
332
338
|
/** One count-only entry per changed file (no content). */
|
|
@@ -357,7 +363,7 @@ export async function captureProgressDelta(
|
|
|
357
363
|
baselineTree: string,
|
|
358
364
|
excludePaths: readonly string[] = [],
|
|
359
365
|
lastTreeSha?: string,
|
|
360
|
-
): Promise<
|
|
366
|
+
): Promise<GitProgressDelta | undefined> {
|
|
361
367
|
const gitDir = await resolveGitDir(gitRoot);
|
|
362
368
|
const afterTree = await writeWorkingTree(gitRoot, gitDir, "progress", executionId, excludePaths);
|
|
363
369
|
if (lastTreeSha !== undefined && afterTree === lastTreeSha) return undefined;
|
|
@@ -36,20 +36,32 @@ export {
|
|
|
36
36
|
export {
|
|
37
37
|
captureProgressDelta,
|
|
38
38
|
isGitWorkTree,
|
|
39
|
+
type GitProgressDelta,
|
|
39
40
|
type GitProgressEntry,
|
|
40
|
-
type ProgressDelta,
|
|
41
41
|
} from "./git-substrate.js";
|
|
42
42
|
|
|
43
43
|
export {
|
|
44
44
|
buildFileChangeProgress,
|
|
45
45
|
captureFileChangeProgress,
|
|
46
|
+
createGitProgressSubstrate,
|
|
47
|
+
createHybridProgressSubstrate,
|
|
46
48
|
newProgressCaptureState,
|
|
47
49
|
PROGRESS_CAPTURE_MIN_INTERVAL_MS,
|
|
48
50
|
PROGRESS_MAX_ENTRIES,
|
|
49
51
|
shouldCaptureProgress,
|
|
52
|
+
type ProgressCapture,
|
|
50
53
|
type ProgressCaptureState,
|
|
54
|
+
type ProgressDelta,
|
|
55
|
+
type ProgressEntry,
|
|
56
|
+
type ProgressSubstrate,
|
|
51
57
|
} from "./progress.js";
|
|
52
58
|
|
|
59
|
+
export {
|
|
60
|
+
createCasProgressSubstrate,
|
|
61
|
+
type CasTouchedReader,
|
|
62
|
+
type CasTouchedSnapshot,
|
|
63
|
+
} from "./cas-progress.js";
|
|
64
|
+
|
|
53
65
|
export {
|
|
54
66
|
countLineChanges,
|
|
55
67
|
LINE_COUNT_MAX_BYTES,
|
|
@@ -61,12 +73,14 @@ export {
|
|
|
61
73
|
casBlobKey,
|
|
62
74
|
casBlobReader,
|
|
63
75
|
casManifestKey,
|
|
76
|
+
classifyCasChange,
|
|
64
77
|
loadCasManifest,
|
|
65
78
|
restoreCasToBaseline,
|
|
66
79
|
snapshotCasChangeSet,
|
|
67
80
|
type BlobReader,
|
|
68
81
|
type CasBlobRef,
|
|
69
82
|
type CasCapturedFile,
|
|
83
|
+
type CasChangeClassification,
|
|
70
84
|
type CasManifest,
|
|
71
85
|
type CasPathCapture,
|
|
72
86
|
type CasSnapshotRef,
|