@goodandready/dsh-approval-gate 0.1.25 → 0.1.27
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/CHANGELOG.md +233 -218
- package/lib/client.js +548 -0
- package/lib/diff-viewer.js +371 -0
- package/lib/index.js +14 -4
- package/lib/learning.js +5 -0
- package/lib/timeline-service.js +333 -0
- package/lib/turn-card.js +286 -0
- package/package.json +12 -3
|
@@ -0,0 +1,371 @@
|
|
|
1
|
+
// lib/diff-viewer.js
|
|
2
|
+
// Unified Diff Generator and Hunk Folding Engine for DSH Approval Gate (#74, #75).
|
|
3
|
+
// Zero-dependency pure JavaScript Myers-style line diffing with folding support.
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @typedef {Object} DiffLine
|
|
7
|
+
* @property {'eq' | 'add' | 'del'} type
|
|
8
|
+
* @property {string} text
|
|
9
|
+
* @property {number | null} oldLine
|
|
10
|
+
* @property {number | null} newLine
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* @typedef {Object} FoldItem
|
|
15
|
+
* @property {'fold'} type
|
|
16
|
+
* @property {number} count
|
|
17
|
+
* @property {number} startOldLine
|
|
18
|
+
* @property {number} startNewLine
|
|
19
|
+
* @property {DiffLine[]} foldedLines
|
|
20
|
+
* @property {boolean} expanded
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* @typedef {Object} Hunk
|
|
25
|
+
* @property {number} oldStart
|
|
26
|
+
* @property {number} oldCount
|
|
27
|
+
* @property {number} newStart
|
|
28
|
+
* @property {number} newCount
|
|
29
|
+
* @property {string} header
|
|
30
|
+
* @property {(DiffLine | FoldItem)[]} items
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Splits text into lines preserving empty lines.
|
|
35
|
+
*
|
|
36
|
+
* @param {string} text
|
|
37
|
+
* @returns {string[]}
|
|
38
|
+
*/
|
|
39
|
+
export function splitLines(text) {
|
|
40
|
+
if (typeof text !== "string") return [];
|
|
41
|
+
const lines = text.split(/\r?\n/);
|
|
42
|
+
if (lines.length > 0 && lines[lines.length - 1] === "") {
|
|
43
|
+
lines.pop();
|
|
44
|
+
}
|
|
45
|
+
return lines;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Computes Longest Common Subsequence matrix for two arrays of lines.
|
|
50
|
+
*
|
|
51
|
+
* @param {string[]} a
|
|
52
|
+
* @param {string[]} b
|
|
53
|
+
* @returns {number[][]}
|
|
54
|
+
*/
|
|
55
|
+
function lcsMatrix(a, b) {
|
|
56
|
+
const m = a.length;
|
|
57
|
+
const n = b.length;
|
|
58
|
+
const dp = Array.from({ length: m + 1 }, () => new Uint32Array(n + 1));
|
|
59
|
+
for (let i = 0; i < m; i++) {
|
|
60
|
+
for (let j = 0; j < n; j++) {
|
|
61
|
+
if (a[i] === b[j]) {
|
|
62
|
+
dp[i + 1][j + 1] = dp[i][j] + 1;
|
|
63
|
+
} else {
|
|
64
|
+
dp[i + 1][j + 1] = Math.max(dp[i + 1][j], dp[i][j + 1]);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return dp;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Generates raw diff lines between old and new text.
|
|
73
|
+
*
|
|
74
|
+
* @param {string} oldText
|
|
75
|
+
* @param {string} newText
|
|
76
|
+
* @returns {DiffLine[]}
|
|
77
|
+
*/
|
|
78
|
+
export function generateDiffLines(oldText, newText) {
|
|
79
|
+
const a = splitLines(oldText);
|
|
80
|
+
const b = splitLines(newText);
|
|
81
|
+
const dp = lcsMatrix(a, b);
|
|
82
|
+
|
|
83
|
+
let i = a.length;
|
|
84
|
+
let j = b.length;
|
|
85
|
+
/** @type {DiffLine[]} */
|
|
86
|
+
const reversed = [];
|
|
87
|
+
|
|
88
|
+
while (i > 0 || j > 0) {
|
|
89
|
+
if (i > 0 && j > 0 && a[i - 1] === b[j - 1]) {
|
|
90
|
+
reversed.push({ type: "eq", text: a[i - 1], oldLine: i, newLine: j });
|
|
91
|
+
i--;
|
|
92
|
+
j--;
|
|
93
|
+
} else if (j > 0 && (i === 0 || dp[i][j - 1] >= dp[i - 1][j])) {
|
|
94
|
+
reversed.push({ type: "add", text: b[j - 1], oldLine: null, newLine: j });
|
|
95
|
+
j--;
|
|
96
|
+
} else if (i > 0 && (j === 0 || dp[i][j - 1] < dp[i - 1][j])) {
|
|
97
|
+
reversed.push({ type: "del", text: a[i - 1], oldLine: i, newLine: null });
|
|
98
|
+
i--;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return reversed.reverse();
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Groups diff lines into hunks with context and hunk folding (#75).
|
|
107
|
+
*
|
|
108
|
+
* @param {DiffLine[]} lines
|
|
109
|
+
* @param {Object} [options]
|
|
110
|
+
* @param {number} [options.contextLines=3]
|
|
111
|
+
* @param {number} [options.minFoldLines=4]
|
|
112
|
+
* @returns {Hunk[]}
|
|
113
|
+
*/
|
|
114
|
+
export function groupHunks(lines, options = {}) {
|
|
115
|
+
const ctx = typeof options.contextLines === "number" ? options.contextLines : 3;
|
|
116
|
+
const minFold = typeof options.minFoldLines === "number" ? options.minFoldLines : 4;
|
|
117
|
+
if (!lines || lines.length === 0) return [];
|
|
118
|
+
|
|
119
|
+
// Find all modified indices
|
|
120
|
+
const isChanged = lines.map((l) => l.type !== "eq");
|
|
121
|
+
const anyChange = isChanged.some(Boolean);
|
|
122
|
+
if (!anyChange) {
|
|
123
|
+
return [];
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Group changed clusters with context
|
|
127
|
+
const ranges = [];
|
|
128
|
+
let curStart = null;
|
|
129
|
+
let curEnd = null;
|
|
130
|
+
|
|
131
|
+
for (let k = 0; k < lines.length; k++) {
|
|
132
|
+
if (isChanged[k]) {
|
|
133
|
+
const start = Math.max(0, k - ctx);
|
|
134
|
+
const end = Math.min(lines.length - 1, k + ctx);
|
|
135
|
+
if (curStart === null) {
|
|
136
|
+
curStart = start;
|
|
137
|
+
curEnd = end;
|
|
138
|
+
} else if (start <= curEnd + 1) {
|
|
139
|
+
curEnd = Math.max(curEnd, end);
|
|
140
|
+
} else {
|
|
141
|
+
ranges.push({ start: curStart, end: curEnd });
|
|
142
|
+
curStart = start;
|
|
143
|
+
curEnd = end;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
if (curStart !== null) {
|
|
148
|
+
ranges.push({ start: curStart, end: curEnd });
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/** @type {Hunk[]} */
|
|
152
|
+
const hunks = [];
|
|
153
|
+
let lastRangeEnd = -1;
|
|
154
|
+
|
|
155
|
+
for (let idx = 0; idx < ranges.length; idx++) {
|
|
156
|
+
const r = ranges[idx];
|
|
157
|
+
const prevEnd = lastRangeEnd;
|
|
158
|
+
const hunkLines = lines.slice(r.start, r.end + 1);
|
|
159
|
+
|
|
160
|
+
let oldStart = 0;
|
|
161
|
+
let oldCount = 0;
|
|
162
|
+
let newStart = 0;
|
|
163
|
+
let newCount = 0;
|
|
164
|
+
|
|
165
|
+
for (const l of hunkLines) {
|
|
166
|
+
if (l.type === "eq" || l.type === "del") {
|
|
167
|
+
oldCount++;
|
|
168
|
+
if (!oldStart && l.oldLine) oldStart = l.oldLine;
|
|
169
|
+
}
|
|
170
|
+
if (l.type === "eq" || l.type === "add") {
|
|
171
|
+
newCount++;
|
|
172
|
+
if (!newStart && l.newLine) newStart = l.newLine;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/** @type {(DiffLine | FoldItem)[]} */
|
|
177
|
+
const items = [];
|
|
178
|
+
|
|
179
|
+
// Leading fold if there are unchanged lines before this range (#75)
|
|
180
|
+
if (r.start > prevEnd + 1) {
|
|
181
|
+
const leadingLines = lines.slice(prevEnd + 1, r.start);
|
|
182
|
+
if (leadingLines.length >= minFold) {
|
|
183
|
+
items.push({
|
|
184
|
+
type: "fold",
|
|
185
|
+
count: leadingLines.length,
|
|
186
|
+
startOldLine: leadingLines[0].oldLine,
|
|
187
|
+
startNewLine: leadingLines[0].newLine,
|
|
188
|
+
foldedLines: leadingLines,
|
|
189
|
+
expanded: false,
|
|
190
|
+
});
|
|
191
|
+
} else {
|
|
192
|
+
items.push(...leadingLines);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// Inside range: check for fold streaks
|
|
197
|
+
let eqStreak = [];
|
|
198
|
+
const flushStreak = () => {
|
|
199
|
+
if (eqStreak.length >= minFold) {
|
|
200
|
+
items.push({
|
|
201
|
+
type: "fold",
|
|
202
|
+
count: eqStreak.length,
|
|
203
|
+
startOldLine: eqStreak[0].oldLine,
|
|
204
|
+
startNewLine: eqStreak[0].newLine,
|
|
205
|
+
foldedLines: eqStreak.slice(),
|
|
206
|
+
expanded: false,
|
|
207
|
+
});
|
|
208
|
+
} else {
|
|
209
|
+
items.push(...eqStreak);
|
|
210
|
+
}
|
|
211
|
+
eqStreak = [];
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
for (const l of hunkLines) {
|
|
215
|
+
if (l.type === "eq") {
|
|
216
|
+
eqStreak.push(l);
|
|
217
|
+
} else {
|
|
218
|
+
if (eqStreak.length > 0) flushStreak();
|
|
219
|
+
items.push(l);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
if (eqStreak.length > 0) flushStreak();
|
|
223
|
+
|
|
224
|
+
// Trailing fold if this is the last range and there are remaining lines
|
|
225
|
+
if (idx === ranges.length - 1 && r.end < lines.length - 1) {
|
|
226
|
+
const trailingLines = lines.slice(r.end + 1);
|
|
227
|
+
if (trailingLines.length >= minFold) {
|
|
228
|
+
items.push({
|
|
229
|
+
type: "fold",
|
|
230
|
+
count: trailingLines.length,
|
|
231
|
+
startOldLine: trailingLines[0].oldLine,
|
|
232
|
+
startNewLine: trailingLines[0].newLine,
|
|
233
|
+
foldedLines: trailingLines,
|
|
234
|
+
expanded: false,
|
|
235
|
+
});
|
|
236
|
+
} else {
|
|
237
|
+
items.push(...trailingLines);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
const header = `@@ -${oldStart || 1},${oldCount} +${newStart || 1},${newCount} @@`;
|
|
242
|
+
hunks.push({
|
|
243
|
+
oldStart: oldStart || 1,
|
|
244
|
+
oldCount,
|
|
245
|
+
newStart: newStart || 1,
|
|
246
|
+
newCount,
|
|
247
|
+
header,
|
|
248
|
+
items,
|
|
249
|
+
});
|
|
250
|
+
lastRangeEnd = r.end;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
return hunks;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Formats hunks as standard Unified Diff string (#74).
|
|
258
|
+
*
|
|
259
|
+
* @param {Hunk[]} hunks
|
|
260
|
+
* @param {Object} [options]
|
|
261
|
+
* @param {string} [options.oldFile='a/file']
|
|
262
|
+
* @param {string} [options.newFile='b/file']
|
|
263
|
+
* @returns {string}
|
|
264
|
+
*/
|
|
265
|
+
export function formatUnifiedDiff(hunks, options = {}) {
|
|
266
|
+
const oldFile = options.oldFile || "a/file";
|
|
267
|
+
const newFile = options.newFile || "b/file";
|
|
268
|
+
if (!hunks || hunks.length === 0) return "";
|
|
269
|
+
|
|
270
|
+
const out = [`--- ${oldFile}`, `+++ ${newFile}`];
|
|
271
|
+
|
|
272
|
+
for (const h of hunks) {
|
|
273
|
+
out.push(h.header);
|
|
274
|
+
for (const item of h.items) {
|
|
275
|
+
if (item.type === "fold") {
|
|
276
|
+
out.push(`@@ ... [${item.count} unchanged lines folded] ... @@`);
|
|
277
|
+
if (item.expanded) {
|
|
278
|
+
for (const fl of item.foldedLines) {
|
|
279
|
+
out.push(` ${fl.text}`);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
} else if (item.type === "eq") {
|
|
283
|
+
out.push(` ${item.text}`);
|
|
284
|
+
} else if (item.type === "add") {
|
|
285
|
+
out.push(`+${item.text}`);
|
|
286
|
+
} else if (item.type === "del") {
|
|
287
|
+
out.push(`-${item.text}`);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
return out.join("\n");
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* Parses a unified diff string into hunks and lines.
|
|
297
|
+
*
|
|
298
|
+
* @param {string} diffText
|
|
299
|
+
* @returns {Hunk[]}
|
|
300
|
+
*/
|
|
301
|
+
export function parseUnifiedDiff(diffText) {
|
|
302
|
+
if (typeof diffText !== "string" || !diffText.trim()) return [];
|
|
303
|
+
const lines = splitLines(diffText);
|
|
304
|
+
/** @type {Hunk[]} */
|
|
305
|
+
const hunks = [];
|
|
306
|
+
let curHunk = null;
|
|
307
|
+
let curOldLine = 1;
|
|
308
|
+
let curNewLine = 1;
|
|
309
|
+
|
|
310
|
+
for (const l of lines) {
|
|
311
|
+
if (l.startsWith("---") || l.startsWith("+++")) {
|
|
312
|
+
continue;
|
|
313
|
+
}
|
|
314
|
+
const hMatch = l.match(/^@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@/);
|
|
315
|
+
if (hMatch) {
|
|
316
|
+
if (curHunk) hunks.push(curHunk);
|
|
317
|
+
curOldLine = parseInt(hMatch[1], 10);
|
|
318
|
+
curNewLine = parseInt(hMatch[3], 10);
|
|
319
|
+
curHunk = {
|
|
320
|
+
oldStart: curOldLine,
|
|
321
|
+
oldCount: hMatch[2] !== undefined ? parseInt(hMatch[2], 10) : 1,
|
|
322
|
+
newStart: curNewLine,
|
|
323
|
+
newCount: hMatch[4] !== undefined ? parseInt(hMatch[4], 10) : 1,
|
|
324
|
+
header: l,
|
|
325
|
+
items: [],
|
|
326
|
+
};
|
|
327
|
+
continue;
|
|
328
|
+
}
|
|
329
|
+
if (!curHunk) continue;
|
|
330
|
+
|
|
331
|
+
if (l.startsWith("+")) {
|
|
332
|
+
curHunk.items.push({ type: "add", text: l.slice(1), oldLine: null, newLine: curNewLine++ });
|
|
333
|
+
} else if (l.startsWith("-")) {
|
|
334
|
+
curHunk.items.push({ type: "del", text: l.slice(1), oldLine: curOldLine++, newLine: null });
|
|
335
|
+
} else if (l.startsWith(" ")) {
|
|
336
|
+
curHunk.items.push({ type: "eq", text: l.slice(1), oldLine: curOldLine++, newLine: curNewLine++ });
|
|
337
|
+
} else if (l.startsWith("@@ ...")) {
|
|
338
|
+
const fMatch = l.match(/\[(\d+) unchanged lines folded\]/);
|
|
339
|
+
const count = fMatch ? parseInt(fMatch[1], 10) : 0;
|
|
340
|
+
curHunk.items.push({
|
|
341
|
+
type: "fold",
|
|
342
|
+
count,
|
|
343
|
+
startOldLine: curOldLine,
|
|
344
|
+
startNewLine: curNewLine,
|
|
345
|
+
foldedLines: [],
|
|
346
|
+
expanded: false,
|
|
347
|
+
});
|
|
348
|
+
curOldLine += count;
|
|
349
|
+
curNewLine += count;
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
if (curHunk) hunks.push(curHunk);
|
|
353
|
+
return hunks;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* Toggles fold expansion in a hunk item (#75).
|
|
358
|
+
*
|
|
359
|
+
* @param {Hunk} hunk
|
|
360
|
+
* @param {number} itemIndex
|
|
361
|
+
* @returns {boolean}
|
|
362
|
+
*/
|
|
363
|
+
export function toggleFold(hunk, itemIndex) {
|
|
364
|
+
if (!hunk || !Array.isArray(hunk.items)) return false;
|
|
365
|
+
const it = hunk.items[itemIndex];
|
|
366
|
+
if (it && it.type === "fold") {
|
|
367
|
+
it.expanded = !it.expanded;
|
|
368
|
+
return true;
|
|
369
|
+
}
|
|
370
|
+
return false;
|
|
371
|
+
}
|
package/lib/index.js
CHANGED
|
@@ -11,6 +11,8 @@ import { MEASURE_KINDS, classifyMeasureKind } from "./measure-kinds.js";
|
|
|
11
11
|
import { LineageManager } from "./lineage.js";
|
|
12
12
|
import { generateAgentGuidance, injectAgentGuidance, extractGuidance, GUIDANCE_MARKER } from "./guidance.js";
|
|
13
13
|
import { isRedundantSandboxRequest, buildRedundantSandboxRemediation, checkEscalationJustification } from "./escalation.js";
|
|
14
|
+
import { TimelineService } from "./timeline-service.js";
|
|
15
|
+
import { TurnCardTracker } from "./turn-card.js";
|
|
14
16
|
import Schema from "@deepseek-ai/schemastery";
|
|
15
17
|
|
|
16
18
|
if (typeof Schema.prototype?.volatile !== "function") {
|
|
@@ -405,6 +407,11 @@ export function apply(ctx, config) {
|
|
|
405
407
|
}),
|
|
406
408
|
};
|
|
407
409
|
provideServicePolyfill(ctx, "approvalGate", gateService);
|
|
410
|
+
const turnCard = new TurnCardTracker();
|
|
411
|
+
const timeline = new TimelineService({ turnCardTracker: turnCard, learner: engine.learner, engine });
|
|
412
|
+
timeline.registerRoutes(ctx);
|
|
413
|
+
gateService.timeline = timeline;
|
|
414
|
+
gateService.turnCard = turnCard;
|
|
408
415
|
|
|
409
416
|
const hasPreExecute = ctx && typeof ctx.on === "function";
|
|
410
417
|
if (hasPreExecute) {
|
|
@@ -428,6 +435,7 @@ export function apply(ctx, config) {
|
|
|
428
435
|
return { kind: "ask", reason: askMessage(hit, who, translate, ctx && ctx.logger) };
|
|
429
436
|
}
|
|
430
437
|
|
|
438
|
+
timeline.recordEvent({ sessionId, turnId: execution?.turnId, tool: execution?.name, command: execution?.arguments?.command, snippet: decision.snippet, verdict: decision.verdict, stage: decision.stage, reason: decision.reason, timeoutMs: decision.timeoutMs });
|
|
431
439
|
if (decision.dryRun) {
|
|
432
440
|
if (ctx && ctx.logger && typeof ctx.logger.info === "function") {
|
|
433
441
|
ctx.logger.info(`[dsh-approval-gate:DRY-RUN] would block rule=${decision.reason} (${decision.originalVerdict})${who}`);
|
|
@@ -497,6 +505,7 @@ export function apply(ctx, config) {
|
|
|
497
505
|
return denyMessage(hit, who, translate, ctx && ctx.logger);
|
|
498
506
|
}
|
|
499
507
|
|
|
508
|
+
timeline.recordEvent({ sessionId, turnId: execution?.turnId, tool: execution?.name, command: execution?.arguments?.command, snippet: decision.snippet, verdict: decision.verdict, stage: decision.stage, reason: decision.reason });
|
|
500
509
|
if (decision.dryRun) {
|
|
501
510
|
if (ctx && ctx.logger && typeof ctx.logger.info === "function") {
|
|
502
511
|
ctx.logger.info(`[dsh-approval-gate:DRY-RUN] would block rule=${decision.reason} (${decision.originalVerdict})${who}`);
|
|
@@ -530,12 +539,10 @@ export function apply(ctx, config) {
|
|
|
530
539
|
return mount();
|
|
531
540
|
}
|
|
532
541
|
|
|
533
|
-
export { SecurityEngine } from "./engine.js";
|
|
534
542
|
export { BandsEngine } from "./bands.js";
|
|
535
543
|
export { CircuitBreaker, CIRCUIT_STATE_ACTIVE, CIRCUIT_STATE_TRIPPED, CIRCUIT_STATE_RESUMED } from "./breaker.js";
|
|
536
544
|
export { SessionGrantStore, DEFAULT_GRANT_TTL_MS, DEFAULT_GRANT_MAX_USES, matchGrantCriteria } from "./grants.js";
|
|
537
545
|
export { canonicalizeCall, canonicalString, hashCall, sortKeys } from "./canonical.js";
|
|
538
|
-
export { expandHome, resolveSafePath, isPathTraversal, canonicalizeFsPath } from "./paths.js";
|
|
539
546
|
export { splitSubcommands } from "./tokenizer.js";
|
|
540
547
|
export { redactCredentials, redactUrl } from "./redact.js";
|
|
541
548
|
export { CommandDispatcher, isReviewerAgent, extractFilePath, TOOL_KIND_SHELL, TOOL_KIND_FILE_WRITE, TOOL_KIND_FILE_READ, TOOL_KIND_NETWORK, TOOL_KIND_GENERIC } from "./dispatcher.js";
|
|
@@ -548,7 +555,7 @@ export { RetryExhaustionGuard, DEFAULT_MAX_RETRY_ATTEMPTS } from "./retry-exhaus
|
|
|
548
555
|
export { QualityLedger, MAX_LEDGER_EVENTS_PER_SESSION, CURRENT_SCHEMA_VERSION, SUPPORTED_SCHEMA_VERSIONS, migrateVerdictRecord } from "./ledger.js";
|
|
549
556
|
export { AuditLogger, DEFAULT_AUDIT_LOG_RELATIVE, DEFAULT_MAX_AUDIT_BYTES, DEFAULT_MAX_AUDIT_LINES, DEFAULT_RETAIN_LINES, auditRotateContent } from "./audit-log.js";
|
|
550
557
|
export { BlockCounters, categorizeReason } from "./counters.js";
|
|
551
|
-
export { isCriticalOperation } from "./engine.js";
|
|
558
|
+
export { SecurityEngine, isCriticalOperation } from "./engine.js";
|
|
552
559
|
export { parseYaml, parseScalar, stripComment } from "./yaml-parser.js";
|
|
553
560
|
export { matchConditionValue, evaluateWhen } from "./when-matcher.js";
|
|
554
561
|
export { HotwordsMatcher, DEFAULT_HOTWORDS, extractWords } from "./hotwords.js";
|
|
@@ -556,7 +563,7 @@ export { DeclarativeRulesEngine, BUILTIN_RULES_FILE, WORKSPACE_RULES_REL, USER_R
|
|
|
556
563
|
export { normalizeChatCompletionsUrl, withLlmRetry, sanitizeClassifierInput, truncateClassifierContext, isLowRiskExecution, isShapeRejectError, extractJsonPayload, ClassifierVerdictSchema, validateClassifierResponse, LlmClassifier } from "./classifier.js";
|
|
557
564
|
export { ContractGuard } from "./contract-guard.js";
|
|
558
565
|
export { OverengineeringGuard, DEFAULT_MAX_FILES_PER_SESSION } from "./overengineering.js";
|
|
559
|
-
export { resolveTempRoot, isGlobalTempPath, checkTempIsolation } from "./paths.js";
|
|
566
|
+
export { expandHome, resolveSafePath, isPathTraversal, canonicalizeFsPath, resolveTempRoot, isGlobalTempPath, checkTempIsolation } from "./paths.js";
|
|
560
567
|
export { computePayloadSize, scanPromptInjection } from "./inspect.js";
|
|
561
568
|
export { RISK_BUCKETS, RISK_TAGS, categorizeRiskBucket, extractRiskTags } from "./risk.js";
|
|
562
569
|
export { ClassifierLatencyTracker, DEFAULT_CLASSIFIER_SLA_MS } from "./latency.js";
|
|
@@ -582,3 +589,6 @@ export { DEFAULT_HUMAN_TIMEOUT_MS, RACE_WINNERS, RaceHumanHandle, raceHumanDecis
|
|
|
582
589
|
export { DEFAULT_HMR_DEBOUNCE_MS, RulesHmrWatcher } from "./rules-hmr.js";
|
|
583
590
|
export { NETWORK_POLICIES, SANDBOX_NETWORK_MAP, resolveNetworkPolicy, parseDestination, checkNetworkAccess } from "./network-sandbox.js";
|
|
584
591
|
export { LocalEgressProxy } from "./forward-proxy.js";
|
|
592
|
+
export { splitLines, generateDiffLines, groupHunks, formatUnifiedDiff, parseUnifiedDiff, toggleFold } from "./diff-viewer.js";
|
|
593
|
+
export { TurnCardTracker, formatTurnCardSummary } from "./turn-card.js";
|
|
594
|
+
export { TimelineService } from "./timeline-service.js";
|
package/lib/learning.js
CHANGED
|
@@ -218,6 +218,11 @@ export class ConfirmationLearner {
|
|
|
218
218
|
return rules;
|
|
219
219
|
}
|
|
220
220
|
|
|
221
|
+
revoke(sessionId, key) {
|
|
222
|
+
const sessionMap = this._getSessionMap(sessionId);
|
|
223
|
+
return sessionMap.delete(key);
|
|
224
|
+
}
|
|
225
|
+
|
|
221
226
|
clearSession(sessionId) {
|
|
222
227
|
const key = sessionId || "default";
|
|
223
228
|
this.sessions.delete(key);
|