@mikode13/harness 4.0.1 → 5.1.0
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/README.md +299 -17
- package/dist/agent/domain/agent.d.ts +32 -1
- package/dist/agent/domain/agent.js.map +1 -1
- package/dist/agent/domain/approval.d.ts +9 -0
- package/dist/agent/domain/approval.js.map +1 -1
- package/dist/agent/domain/runContext.d.ts +31 -0
- package/dist/agent/domain/runContext.js +47 -0
- package/dist/agent/domain/runContext.js.map +1 -0
- package/dist/diff/domain/unifiedDiff.d.ts +19 -0
- package/dist/diff/domain/unifiedDiff.js +166 -0
- package/dist/diff/domain/unifiedDiff.js.map +1 -0
- package/dist/engines/domain/model/historyFollower.d.ts +41 -0
- package/dist/engines/domain/model/historyFollower.js +158 -0
- package/dist/engines/domain/model/historyFollower.js.map +1 -0
- package/dist/engines/domain/model/llmAgent.d.ts +42 -4
- package/dist/engines/domain/model/llmAgent.js +287 -31
- package/dist/engines/domain/model/llmAgent.js.map +1 -1
- package/dist/factory/infrastructure/agentFactory.d.ts +14 -0
- package/dist/factory/infrastructure/agentFactory.js +20 -2
- package/dist/factory/infrastructure/agentFactory.js.map +1 -1
- package/dist/factory/infrastructure/agentLLMFactory.d.ts +119 -14
- package/dist/factory/infrastructure/agentLLMFactory.js +284 -57
- package/dist/factory/infrastructure/agentLLMFactory.js.map +1 -1
- package/dist/index.d.ts +13 -5
- package/dist/index.js +5 -2
- package/dist/index.js.map +1 -1
- package/dist/llm/domain/conversation.d.ts +44 -4
- package/dist/llm/domain/conversation.js +56 -7
- package/dist/llm/domain/conversation.js.map +1 -1
- package/dist/llm/domain/llm.d.ts +3 -1
- package/dist/llm/domain/llm.js.map +1 -1
- package/dist/llm/domain/tool.d.ts +13 -0
- package/dist/llm/domain/tool.js +12 -1
- package/dist/llm/domain/tool.js.map +1 -1
- package/dist/llm/infrastructure/claudeLLMClient.d.ts +3 -2
- package/dist/llm/infrastructure/claudeLLMClient.js +38 -10
- package/dist/llm/infrastructure/claudeLLMClient.js.map +1 -1
- package/dist/llm/infrastructure/openAILLMClient.d.ts +2 -1
- package/dist/llm/infrastructure/openAILLMClient.js +67 -17
- package/dist/llm/infrastructure/openAILLMClient.js.map +1 -1
- package/dist/orchestration/domain/model/orchestratorAgent.d.ts +17 -3
- package/dist/orchestration/domain/model/orchestratorAgent.js +65 -8
- package/dist/orchestration/domain/model/orchestratorAgent.js.map +1 -1
- package/dist/recovery/domain/fileChanges.d.ts +31 -0
- package/dist/recovery/domain/fileChanges.js +48 -0
- package/dist/recovery/domain/fileChanges.js.map +1 -0
- package/dist/recovery/domain/history.d.ts +114 -0
- package/dist/recovery/domain/history.js +62 -0
- package/dist/recovery/domain/history.js.map +1 -0
- package/dist/recovery/domain/recoveryStore.d.ts +233 -0
- package/dist/recovery/domain/recoveryStore.js +32 -0
- package/dist/recovery/domain/recoveryStore.js.map +1 -0
- package/dist/recovery/domain/retention.d.ts +33 -0
- package/dist/recovery/domain/retention.js +90 -0
- package/dist/recovery/domain/retention.js.map +1 -0
- package/dist/recovery/domain/runChanges.d.ts +37 -0
- package/dist/recovery/domain/runChanges.js +109 -0
- package/dist/recovery/domain/runChanges.js.map +1 -0
- package/dist/recovery/domain/runTree.d.ts +32 -0
- package/dist/recovery/domain/runTree.js +90 -0
- package/dist/recovery/domain/runTree.js.map +1 -0
- package/dist/recovery/infrastructure/createHistory.d.ts +11 -0
- package/dist/recovery/infrastructure/createHistory.js +17 -0
- package/dist/recovery/infrastructure/createHistory.js.map +1 -0
- package/dist/recovery/infrastructure/fileRecoveryStore.d.ts +133 -0
- package/dist/recovery/infrastructure/fileRecoveryStore.js +1043 -0
- package/dist/recovery/infrastructure/fileRecoveryStore.js.map +1 -0
- package/dist/recovery/infrastructure/workspaceFiles.d.ts +30 -0
- package/dist/recovery/infrastructure/workspaceFiles.js +125 -0
- package/dist/recovery/infrastructure/workspaceFiles.js.map +1 -0
- package/dist/tools/domain/accessPolicy.d.ts +30 -0
- package/dist/tools/domain/accessPolicy.js +11 -0
- package/dist/tools/domain/accessPolicy.js.map +1 -0
- package/dist/tools/domain/commands.d.ts +93 -0
- package/dist/tools/domain/commands.js +191 -0
- package/dist/tools/domain/commands.js.map +1 -0
- package/dist/tools/domain/editResponse.d.ts +14 -0
- package/dist/tools/domain/editResponse.js +79 -0
- package/dist/tools/domain/editResponse.js.map +1 -0
- package/dist/tools/domain/fileEdits.d.ts +64 -0
- package/dist/tools/domain/fileEdits.js +21 -0
- package/dist/tools/domain/fileEdits.js.map +1 -0
- package/dist/tools/domain/lineLength.d.ts +7 -0
- package/dist/tools/domain/lineLength.js +26 -0
- package/dist/tools/domain/lineLength.js.map +1 -0
- package/dist/tools/domain/preparedCall.d.ts +44 -0
- package/dist/tools/domain/preparedCall.js +26 -0
- package/dist/tools/domain/preparedCall.js.map +1 -0
- package/dist/tools/domain/readRegistry.d.ts +46 -0
- package/dist/tools/domain/readRegistry.js +62 -0
- package/dist/tools/domain/readRegistry.js.map +1 -0
- package/dist/tools/domain/textLines.d.ts +26 -0
- package/dist/tools/domain/textLines.js +44 -0
- package/dist/tools/domain/textLines.js.map +1 -0
- package/dist/tools/domain/tool.d.ts +14 -0
- package/dist/tools/domain/tool.js +7 -1
- package/dist/tools/domain/tool.js.map +1 -1
- package/dist/tools/domain/v4aPatch.d.ts +17 -0
- package/dist/tools/domain/v4aPatch.js +236 -0
- package/dist/tools/domain/v4aPatch.js.map +1 -0
- package/dist/tools/infrastructure/boundedWorkspace.d.ts +11 -2
- package/dist/tools/infrastructure/boundedWorkspace.js +9 -1
- package/dist/tools/infrastructure/boundedWorkspace.js.map +1 -1
- package/dist/tools/infrastructure/commandCheckpoint.d.ts +55 -0
- package/dist/tools/infrastructure/commandCheckpoint.js +178 -0
- package/dist/tools/infrastructure/commandCheckpoint.js.map +1 -0
- package/dist/tools/infrastructure/commandProcess.d.ts +47 -0
- package/dist/tools/infrastructure/commandProcess.js +217 -0
- package/dist/tools/infrastructure/commandProcess.js.map +1 -0
- package/dist/tools/infrastructure/commandTool.d.ts +36 -0
- package/dist/tools/infrastructure/commandTool.js +285 -0
- package/dist/tools/infrastructure/commandTool.js.map +1 -0
- package/dist/tools/infrastructure/createWorkspace.d.ts +10 -0
- package/dist/tools/infrastructure/createWorkspace.js +11 -3
- package/dist/tools/infrastructure/createWorkspace.js.map +1 -1
- package/dist/tools/infrastructure/defineTool.d.ts +18 -2
- package/dist/tools/infrastructure/defineTool.js +52 -6
- package/dist/tools/infrastructure/defineTool.js.map +1 -1
- package/dist/tools/infrastructure/editFormats.d.ts +22 -0
- package/dist/tools/infrastructure/editFormats.js +243 -0
- package/dist/tools/infrastructure/editFormats.js.map +1 -0
- package/dist/tools/infrastructure/fileEditor.d.ts +64 -0
- package/dist/tools/infrastructure/fileEditor.js +306 -0
- package/dist/tools/infrastructure/fileEditor.js.map +1 -0
- package/dist/tools/infrastructure/fileTools.d.ts +81 -0
- package/dist/tools/infrastructure/fileTools.js +152 -0
- package/dist/tools/infrastructure/fileTools.js.map +1 -0
- package/dist/tools/infrastructure/gitIgnoreRules.d.ts +15 -0
- package/dist/tools/infrastructure/gitIgnoreRules.js +74 -0
- package/dist/tools/infrastructure/gitIgnoreRules.js.map +1 -0
- package/dist/tools/infrastructure/gitWorkspace.d.ts +1 -1
- package/dist/tools/infrastructure/gitWorkspace.js +1 -1
- package/dist/tools/infrastructure/gitWorkspace.js.map +1 -1
- package/dist/tools/infrastructure/ignoreFileChanges.d.ts +14 -0
- package/dist/tools/infrastructure/ignoreFileChanges.js +62 -0
- package/dist/tools/infrastructure/ignoreFileChanges.js.map +1 -0
- package/dist/tools/infrastructure/policyWorkspace.d.ts +52 -0
- package/dist/tools/infrastructure/policyWorkspace.js +32 -0
- package/dist/tools/infrastructure/policyWorkspace.js.map +1 -0
- package/dist/tools/infrastructure/ripgrepWorkspace.d.ts +2 -1
- package/dist/tools/infrastructure/ripgrepWorkspace.js +1 -1
- package/dist/tools/infrastructure/ripgrepWorkspace.js.map +1 -1
- package/dist/tools/infrastructure/rootsAccessPolicy.d.ts +100 -0
- package/dist/tools/infrastructure/rootsAccessPolicy.js +301 -0
- package/dist/tools/infrastructure/rootsAccessPolicy.js.map +1 -0
- package/dist/tools/infrastructure/runProcess.d.ts +4 -1
- package/dist/tools/infrastructure/runProcess.js +4 -2
- package/dist/tools/infrastructure/runProcess.js.map +1 -1
- package/dist/tools/infrastructure/secretPaths.d.ts +8 -0
- package/dist/tools/infrastructure/secretPaths.js +76 -0
- package/dist/tools/infrastructure/secretPaths.js.map +1 -0
- package/dist/tools/infrastructure/showChangesTool.d.ts +12 -0
- package/dist/tools/infrastructure/showChangesTool.js +44 -0
- package/dist/tools/infrastructure/showChangesTool.js.map +1 -0
- package/dist/tools/infrastructure/trackedEdits.d.ts +33 -0
- package/dist/tools/infrastructure/trackedEdits.js +70 -0
- package/dist/tools/infrastructure/trackedEdits.js.map +1 -0
- package/dist/tools/infrastructure/trackedReadFile.d.ts +21 -0
- package/dist/tools/infrastructure/trackedReadFile.js +79 -0
- package/dist/tools/infrastructure/trackedReadFile.js.map +1 -0
- package/dist/tools/infrastructure/workspaceTools.d.ts +34 -0
- package/dist/tools/infrastructure/workspaceTools.js +69 -40
- package/dist/tools/infrastructure/workspaceTools.js.map +1 -1
- package/dist/tools/infrastructure/workspaceWrites.d.ts +23 -0
- package/dist/tools/infrastructure/workspaceWrites.js +42 -0
- package/dist/tools/infrastructure/workspaceWrites.js.map +1 -0
- package/dist/tools/infrastructure/writeSession.d.ts +35 -0
- package/dist/tools/infrastructure/writeSession.js +81 -0
- package/dist/tools/infrastructure/writeSession.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Line diffs in the unified format, with Myers' algorithm. This module imports nothing, so it can
|
|
3
|
+
* move to a package of its own unchanged.
|
|
4
|
+
*/
|
|
5
|
+
export interface LineDiffOptions {
|
|
6
|
+
/** Unchanged lines shown around each change. Defaults to 3, as in git. */
|
|
7
|
+
context?: number;
|
|
8
|
+
/**
|
|
9
|
+
* The most lines two texts may differ by before the diff stops looking for the smallest
|
|
10
|
+
* change and shows the differing middle as removed, then added. Memory grows with its square.
|
|
11
|
+
*/
|
|
12
|
+
maxDistance?: number;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* The hunks that turn `before` into `after`, each with its `@@` header, or `''` when the texts
|
|
16
|
+
* are equal. Lines are compared whole, a `\r` included, and a last line without a newline
|
|
17
|
+
* differs from the same line with one, as git shows it.
|
|
18
|
+
*/
|
|
19
|
+
export declare function unifiedDiff(before: string, after: string, options?: LineDiffOptions): string;
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Line diffs in the unified format, with Myers' algorithm. This module imports nothing, so it can
|
|
3
|
+
* move to a package of its own unchanged.
|
|
4
|
+
*/
|
|
5
|
+
const noNewline = '\';
|
|
6
|
+
/**
|
|
7
|
+
* The hunks that turn `before` into `after`, each with its `@@` header, or `''` when the texts
|
|
8
|
+
* are equal. Lines are compared whole, a `\r` included, and a last line without a newline
|
|
9
|
+
* differs from the same line with one, as git shows it.
|
|
10
|
+
*/
|
|
11
|
+
export function unifiedDiff(before, after, options = {}) {
|
|
12
|
+
const { context = 3, maxDistance = 1000 } = options;
|
|
13
|
+
const operations = diffLines(lines(before), lines(after), maxDistance);
|
|
14
|
+
return hunks(operations, context).join('\n');
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Each line keeps its newline, so the last line of a text that does not end in one compares
|
|
18
|
+
* as different from the same line followed by a newline.
|
|
19
|
+
*/
|
|
20
|
+
function lines(text) {
|
|
21
|
+
if (text === '')
|
|
22
|
+
return [];
|
|
23
|
+
const parts = text.split('\n');
|
|
24
|
+
const last = parts.pop() ?? '';
|
|
25
|
+
const result = parts.map(part => `${part}\n`);
|
|
26
|
+
if (last !== '')
|
|
27
|
+
result.push(last);
|
|
28
|
+
return result;
|
|
29
|
+
}
|
|
30
|
+
function diffLines(a, b, maxDistance) {
|
|
31
|
+
// The shared start and end need no search, and most edits leave most of a file alone.
|
|
32
|
+
let start = 0;
|
|
33
|
+
while (start < a.length && start < b.length && a[start] === b[start])
|
|
34
|
+
start++;
|
|
35
|
+
let endA = a.length;
|
|
36
|
+
let endB = b.length;
|
|
37
|
+
while (endA > start && endB > start && a[endA - 1] === b[endB - 1]) {
|
|
38
|
+
endA--;
|
|
39
|
+
endB--;
|
|
40
|
+
}
|
|
41
|
+
const middle = shortestEdit(a.slice(start, endA), b.slice(start, endB), maxDistance) ??
|
|
42
|
+
replaceAll(a.slice(start, endA), b.slice(start, endB));
|
|
43
|
+
return [
|
|
44
|
+
...a.slice(0, start).map(line => ({ kind: 'equal', line })),
|
|
45
|
+
...middle,
|
|
46
|
+
...a.slice(endA).map(line => ({ kind: 'equal', line })),
|
|
47
|
+
];
|
|
48
|
+
}
|
|
49
|
+
function replaceAll(a, b) {
|
|
50
|
+
return [
|
|
51
|
+
...a.map(line => ({ kind: 'delete', line })),
|
|
52
|
+
...b.map(line => ({ kind: 'insert', line })),
|
|
53
|
+
];
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Myers' greedy search for the fewest deletions and insertions, or `undefined` when that number
|
|
57
|
+
* exceeds `maxDistance`. After round `d`, `trace[d][k + d]` is the furthest point reached on
|
|
58
|
+
* diagonal `k` (x − y) with `d` edits; walking back through the rounds recovers the edits.
|
|
59
|
+
*/
|
|
60
|
+
function shortestEdit(a, b, maxDistance) {
|
|
61
|
+
const n = a.length;
|
|
62
|
+
const m = b.length;
|
|
63
|
+
const limit = Math.min(n + m, maxDistance);
|
|
64
|
+
const offset = limit + 1;
|
|
65
|
+
const furthest = new Int32Array(2 * limit + 3);
|
|
66
|
+
const trace = [];
|
|
67
|
+
for (let d = 0; d <= limit; d++) {
|
|
68
|
+
for (let k = -d; k <= d; k += 2) {
|
|
69
|
+
// Down from diagonal k + 1 is an insertion, right from k − 1 a deletion.
|
|
70
|
+
const down = k === -d || (k !== d && (furthest[offset + k - 1] ?? 0) < (furthest[offset + k + 1] ?? 0));
|
|
71
|
+
let x = down ? (furthest[offset + k + 1] ?? 0) : (furthest[offset + k - 1] ?? 0) + 1;
|
|
72
|
+
let y = x - k;
|
|
73
|
+
while (x < n && y < m && a[x] === b[y]) {
|
|
74
|
+
x++;
|
|
75
|
+
y++;
|
|
76
|
+
}
|
|
77
|
+
furthest[offset + k] = x;
|
|
78
|
+
if (x >= n && y >= m) {
|
|
79
|
+
trace.push(furthest.slice(offset - d, offset + d + 1));
|
|
80
|
+
return backtrack(a, b, trace);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
trace.push(furthest.slice(offset - d, offset + d + 1));
|
|
84
|
+
}
|
|
85
|
+
return undefined;
|
|
86
|
+
}
|
|
87
|
+
function backtrack(a, b, trace) {
|
|
88
|
+
const operations = [];
|
|
89
|
+
let x = a.length;
|
|
90
|
+
let y = b.length;
|
|
91
|
+
for (let d = trace.length - 1; d > 0; d--) {
|
|
92
|
+
const previous = trace[d - 1] ?? new Int32Array();
|
|
93
|
+
const at = (k) => previous[k + d - 1] ?? 0;
|
|
94
|
+
const k = x - y;
|
|
95
|
+
const down = k === -d || (k !== d && at(k - 1) < at(k + 1));
|
|
96
|
+
const previousK = down ? k + 1 : k - 1;
|
|
97
|
+
const previousX = at(previousK);
|
|
98
|
+
const previousY = previousX - previousK;
|
|
99
|
+
// The matching run that followed this round's one edit.
|
|
100
|
+
while (x > previousX && y > previousY) {
|
|
101
|
+
operations.push({ kind: 'equal', line: a[--x] ?? '' });
|
|
102
|
+
y--;
|
|
103
|
+
}
|
|
104
|
+
if (down)
|
|
105
|
+
operations.push({ kind: 'insert', line: b[--y] ?? '' });
|
|
106
|
+
else
|
|
107
|
+
operations.push({ kind: 'delete', line: a[--x] ?? '' });
|
|
108
|
+
}
|
|
109
|
+
while (x > 0 && y > 0) {
|
|
110
|
+
operations.push({ kind: 'equal', line: a[--x] ?? '' });
|
|
111
|
+
y--;
|
|
112
|
+
}
|
|
113
|
+
return operations.reverse();
|
|
114
|
+
}
|
|
115
|
+
/** Groups the changes, each with `context` lines around it, into `@@` hunks. */
|
|
116
|
+
function hunks(operations, context) {
|
|
117
|
+
const output = [];
|
|
118
|
+
const changed = operations.flatMap((operation, index) => operation.kind === 'equal' ? [] : [index]);
|
|
119
|
+
if (changed.length === 0)
|
|
120
|
+
return output;
|
|
121
|
+
// Line numbers before and after each operation, counted from 0.
|
|
122
|
+
const positions = [];
|
|
123
|
+
let a = 0;
|
|
124
|
+
let b = 0;
|
|
125
|
+
for (const operation of operations) {
|
|
126
|
+
positions.push({ a, b });
|
|
127
|
+
if (operation.kind !== 'insert')
|
|
128
|
+
a++;
|
|
129
|
+
if (operation.kind !== 'delete')
|
|
130
|
+
b++;
|
|
131
|
+
}
|
|
132
|
+
let first = 0;
|
|
133
|
+
while (first < changed.length) {
|
|
134
|
+
// Two changes closer than twice the context share their context, so they share a hunk.
|
|
135
|
+
let last = first;
|
|
136
|
+
while (last + 1 < changed.length &&
|
|
137
|
+
(changed[last + 1] ?? 0) - (changed[last] ?? 0) <= 2 * context + 1) {
|
|
138
|
+
last++;
|
|
139
|
+
}
|
|
140
|
+
const from = Math.max(0, (changed[first] ?? 0) - context);
|
|
141
|
+
const to = Math.min(operations.length, (changed[last] ?? 0) + context + 1);
|
|
142
|
+
const slice = operations.slice(from, to);
|
|
143
|
+
const oldCount = slice.filter(operation => operation.kind !== 'insert').length;
|
|
144
|
+
const newCount = slice.filter(operation => operation.kind !== 'delete').length;
|
|
145
|
+
const start = positions[from] ?? { a: 0, b: 0 };
|
|
146
|
+
output.push(`@@ -${range(start.a, oldCount)} +${range(start.b, newCount)} @@`);
|
|
147
|
+
for (const operation of slice) {
|
|
148
|
+
const sign = operation.kind === 'equal' ? ' ' : operation.kind === 'delete' ? '-' : '+';
|
|
149
|
+
if (operation.line.endsWith('\n'))
|
|
150
|
+
output.push(sign + operation.line.slice(0, -1));
|
|
151
|
+
else
|
|
152
|
+
output.push(sign + operation.line, noNewline);
|
|
153
|
+
}
|
|
154
|
+
first = last + 1;
|
|
155
|
+
}
|
|
156
|
+
return output;
|
|
157
|
+
}
|
|
158
|
+
/** A hunk's side as git writes it: a side with no lines names the line before it. */
|
|
159
|
+
function range(start, count) {
|
|
160
|
+
if (count === 0)
|
|
161
|
+
return `${String(start)},0`;
|
|
162
|
+
if (count === 1)
|
|
163
|
+
return String(start + 1);
|
|
164
|
+
return `${String(start + 1)},${String(count)}`;
|
|
165
|
+
}
|
|
166
|
+
//# sourceMappingURL=unifiedDiff.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"unifiedDiff.js","sourceRoot":"","sources":["../../../src/diff/domain/unifiedDiff.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Line diffs in the unified format, with Myers' algorithm. This module imports nothing, so it can\n * move to a package of its own unchanged.\n */\n\nexport interface LineDiffOptions {\n\t/** Unchanged lines shown around each change. Defaults to 3, as in git. */\n\tcontext?: number;\n\t/**\n\t * The most lines two texts may differ by before the diff stops looking for the smallest\n\t * change and shows the differing middle as removed, then added. Memory grows with its square.\n\t */\n\tmaxDistance?: number;\n}\n\ninterface Operation {\n\tkind: 'equal' | 'delete' | 'insert';\n\tline: string;\n}\n\nconst noNewline = '\\\';\n\n/**\n * The hunks that turn `before` into `after`, each with its `@@` header, or `''` when the texts\n * are equal. Lines are compared whole, a `\\r` included, and a last line without a newline\n * differs from the same line with one, as git shows it.\n */\nexport function unifiedDiff(before: string, after: string, options: LineDiffOptions = {}): string {\n\tconst { context = 3, maxDistance = 1000 } = options;\n\tconst operations = diffLines(lines(before), lines(after), maxDistance);\n\treturn hunks(operations, context).join('\\n');\n}\n\n/**\n * Each line keeps its newline, so the last line of a text that does not end in one compares\n * as different from the same line followed by a newline.\n */\nfunction lines(text: string): string[] {\n\tif (text === '') return [];\n\tconst parts = text.split('\\n');\n\tconst last = parts.pop() ?? '';\n\tconst result = parts.map(part => `${part}\\n`);\n\tif (last !== '') result.push(last);\n\treturn result;\n}\n\nfunction diffLines(a: string[], b: string[], maxDistance: number): Operation[] {\n\t// The shared start and end need no search, and most edits leave most of a file alone.\n\tlet start = 0;\n\twhile (start < a.length && start < b.length && a[start] === b[start]) start++;\n\tlet endA = a.length;\n\tlet endB = b.length;\n\twhile (endA > start && endB > start && a[endA - 1] === b[endB - 1]) {\n\t\tendA--;\n\t\tendB--;\n\t}\n\n\tconst middle =\n\t\tshortestEdit(a.slice(start, endA), b.slice(start, endB), maxDistance) ??\n\t\treplaceAll(a.slice(start, endA), b.slice(start, endB));\n\n\treturn [\n\t\t...a.slice(0, start).map(line => ({ kind: 'equal' as const, line })),\n\t\t...middle,\n\t\t...a.slice(endA).map(line => ({ kind: 'equal' as const, line })),\n\t];\n}\n\nfunction replaceAll(a: string[], b: string[]): Operation[] {\n\treturn [\n\t\t...a.map(line => ({ kind: 'delete' as const, line })),\n\t\t...b.map(line => ({ kind: 'insert' as const, line })),\n\t];\n}\n\n/**\n * Myers' greedy search for the fewest deletions and insertions, or `undefined` when that number\n * exceeds `maxDistance`. After round `d`, `trace[d][k + d]` is the furthest point reached on\n * diagonal `k` (x − y) with `d` edits; walking back through the rounds recovers the edits.\n */\nfunction shortestEdit(a: string[], b: string[], maxDistance: number): Operation[] | undefined {\n\tconst n = a.length;\n\tconst m = b.length;\n\tconst limit = Math.min(n + m, maxDistance);\n\tconst offset = limit + 1;\n\tconst furthest = new Int32Array(2 * limit + 3);\n\tconst trace: Int32Array[] = [];\n\n\tfor (let d = 0; d <= limit; d++) {\n\t\tfor (let k = -d; k <= d; k += 2) {\n\t\t\t// Down from diagonal k + 1 is an insertion, right from k − 1 a deletion.\n\t\t\tconst down =\n\t\t\t\tk === -d || (k !== d && (furthest[offset + k - 1] ?? 0) < (furthest[offset + k + 1] ?? 0));\n\t\t\tlet x = down ? (furthest[offset + k + 1] ?? 0) : (furthest[offset + k - 1] ?? 0) + 1;\n\t\t\tlet y = x - k;\n\t\t\twhile (x < n && y < m && a[x] === b[y]) {\n\t\t\t\tx++;\n\t\t\t\ty++;\n\t\t\t}\n\t\t\tfurthest[offset + k] = x;\n\t\t\tif (x >= n && y >= m) {\n\t\t\t\ttrace.push(furthest.slice(offset - d, offset + d + 1));\n\t\t\t\treturn backtrack(a, b, trace);\n\t\t\t}\n\t\t}\n\t\ttrace.push(furthest.slice(offset - d, offset + d + 1));\n\t}\n\treturn undefined;\n}\n\nfunction backtrack(a: string[], b: string[], trace: Int32Array[]): Operation[] {\n\tconst operations: Operation[] = [];\n\tlet x = a.length;\n\tlet y = b.length;\n\n\tfor (let d = trace.length - 1; d > 0; d--) {\n\t\tconst previous = trace[d - 1] ?? new Int32Array();\n\t\tconst at = (k: number): number => previous[k + d - 1] ?? 0;\n\t\tconst k = x - y;\n\t\tconst down = k === -d || (k !== d && at(k - 1) < at(k + 1));\n\t\tconst previousK = down ? k + 1 : k - 1;\n\t\tconst previousX = at(previousK);\n\t\tconst previousY = previousX - previousK;\n\n\t\t// The matching run that followed this round's one edit.\n\t\twhile (x > previousX && y > previousY) {\n\t\t\toperations.push({ kind: 'equal', line: a[--x] ?? '' });\n\t\t\ty--;\n\t\t}\n\t\tif (down) operations.push({ kind: 'insert', line: b[--y] ?? '' });\n\t\telse operations.push({ kind: 'delete', line: a[--x] ?? '' });\n\t}\n\twhile (x > 0 && y > 0) {\n\t\toperations.push({ kind: 'equal', line: a[--x] ?? '' });\n\t\ty--;\n\t}\n\n\treturn operations.reverse();\n}\n\n/** Groups the changes, each with `context` lines around it, into `@@` hunks. */\nfunction hunks(operations: Operation[], context: number): string[] {\n\tconst output: string[] = [];\n\tconst changed = operations.flatMap((operation, index) =>\n\t\toperation.kind === 'equal' ? [] : [index],\n\t);\n\tif (changed.length === 0) return output;\n\n\t// Line numbers before and after each operation, counted from 0.\n\tconst positions: { a: number; b: number }[] = [];\n\tlet a = 0;\n\tlet b = 0;\n\tfor (const operation of operations) {\n\t\tpositions.push({ a, b });\n\t\tif (operation.kind !== 'insert') a++;\n\t\tif (operation.kind !== 'delete') b++;\n\t}\n\n\tlet first = 0;\n\twhile (first < changed.length) {\n\t\t// Two changes closer than twice the context share their context, so they share a hunk.\n\t\tlet last = first;\n\t\twhile (\n\t\t\tlast + 1 < changed.length &&\n\t\t\t(changed[last + 1] ?? 0) - (changed[last] ?? 0) <= 2 * context + 1\n\t\t) {\n\t\t\tlast++;\n\t\t}\n\t\tconst from = Math.max(0, (changed[first] ?? 0) - context);\n\t\tconst to = Math.min(operations.length, (changed[last] ?? 0) + context + 1);\n\t\tconst slice = operations.slice(from, to);\n\t\tconst oldCount = slice.filter(operation => operation.kind !== 'insert').length;\n\t\tconst newCount = slice.filter(operation => operation.kind !== 'delete').length;\n\t\tconst start = positions[from] ?? { a: 0, b: 0 };\n\n\t\toutput.push(`@@ -${range(start.a, oldCount)} +${range(start.b, newCount)} @@`);\n\t\tfor (const operation of slice) {\n\t\t\tconst sign = operation.kind === 'equal' ? ' ' : operation.kind === 'delete' ? '-' : '+';\n\t\t\tif (operation.line.endsWith('\\n')) output.push(sign + operation.line.slice(0, -1));\n\t\t\telse output.push(sign + operation.line, noNewline);\n\t\t}\n\t\tfirst = last + 1;\n\t}\n\treturn output;\n}\n\n/** A hunk's side as git writes it: a side with no lines names the line before it. */\nfunction range(start: number, count: number): string {\n\tif (count === 0) return `${String(start)},0`;\n\tif (count === 1) return String(start + 1);\n\treturn `${String(start + 1)},${String(count)}`;\n}\n"]}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { Conversation } from '#src/llm/domain/conversation';
|
|
2
|
+
import type { RecoveryStore, Revision } from '#src/recovery/domain/recoveryStore';
|
|
3
|
+
/** What an agent reads of its workspace's history. */
|
|
4
|
+
export type HistoryReader = Pick<RecoveryStore, 'listRuns' | 'listRevisions' | 'readRun'>;
|
|
5
|
+
/** Where the conversation goes on after the workspace moved, and what the moves left behind. */
|
|
6
|
+
export interface Followed {
|
|
7
|
+
/** The turn the next one follows. */
|
|
8
|
+
turn: number | undefined;
|
|
9
|
+
/**
|
|
10
|
+
* The turns on the path to the last turn recorded whose run the workspace no longer holds:
|
|
11
|
+
* what the moves since undid.
|
|
12
|
+
*/
|
|
13
|
+
undone: number[];
|
|
14
|
+
/** The moves since the conversation last looked, oldest first. */
|
|
15
|
+
revisions: Revision[];
|
|
16
|
+
/** The latest move, so the next look starts after it. */
|
|
17
|
+
latestRevision: number;
|
|
18
|
+
/** Where the workspace is: `undefined` for the start of its history. */
|
|
19
|
+
head: string | undefined;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Picks the turn a conversation goes on from, given where the workspace is now. A turn belongs
|
|
23
|
+
* to the run it wrote in; a turn that wrote nothing belongs to the run of the turn it followed.
|
|
24
|
+
* Of the turns whose run is on the line from the start to the head, it picks the one nearest
|
|
25
|
+
* the head, and of those the newest. A run retention chained into a later one counts as that
|
|
26
|
+
* one, and a run retention removed is off every line.
|
|
27
|
+
*/
|
|
28
|
+
export declare function followHistory(conversation: Conversation, history: HistoryReader, seenRevision: number): Promise<Followed>;
|
|
29
|
+
/**
|
|
30
|
+
* What the model is told when the workspace was moved back past some of its turns: that they
|
|
31
|
+
* were undone and no longer show, what they asked, or a summary of what they tried, the files
|
|
32
|
+
* those runs had changed, the files a move left as they were, and the user's reasons.
|
|
33
|
+
*/
|
|
34
|
+
export declare function undoNote({ conversation, history, followed, summary, }: {
|
|
35
|
+
conversation: Conversation;
|
|
36
|
+
history: HistoryReader;
|
|
37
|
+
followed: Followed;
|
|
38
|
+
summary?: string;
|
|
39
|
+
}): Promise<string>;
|
|
40
|
+
/** The undone turns as plain text, for a model to summarize. Bounded, and marked when cut. */
|
|
41
|
+
export declare function transcriptOf(conversation: Conversation, turns: number[], maxChars?: number): string;
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import { HistoryExpiredError } from '#src/recovery/domain/runTree';
|
|
2
|
+
// A prompt is named, not repeated: enough to recognise it.
|
|
3
|
+
const promptChars = 300;
|
|
4
|
+
/**
|
|
5
|
+
* Picks the turn a conversation goes on from, given where the workspace is now. A turn belongs
|
|
6
|
+
* to the run it wrote in; a turn that wrote nothing belongs to the run of the turn it followed.
|
|
7
|
+
* Of the turns whose run is on the line from the start to the head, it picks the one nearest
|
|
8
|
+
* the head, and of those the newest. A run retention chained into a later one counts as that
|
|
9
|
+
* one, and a run retention removed is off every line.
|
|
10
|
+
*/
|
|
11
|
+
export async function followHistory(conversation, history, seenRevision) {
|
|
12
|
+
const { head, runs } = await history.listRuns();
|
|
13
|
+
const revisions = await history.listRevisions();
|
|
14
|
+
const keeper = new Map();
|
|
15
|
+
for (const run of runs) {
|
|
16
|
+
keeper.set(run.runId, run.runId);
|
|
17
|
+
for (const absorbed of run.absorbed ?? [])
|
|
18
|
+
keeper.set(absorbed, run.runId);
|
|
19
|
+
}
|
|
20
|
+
const parents = new Map(runs.map(run => [run.runId, run.parentRunId]));
|
|
21
|
+
const line = [];
|
|
22
|
+
for (let runId = head; runId !== undefined; runId = parents.get(runId))
|
|
23
|
+
line.unshift(runId);
|
|
24
|
+
// -1 is the start of the history; `undefined` is a turn off the line.
|
|
25
|
+
const depths = [];
|
|
26
|
+
const runsOf = [];
|
|
27
|
+
let best;
|
|
28
|
+
for (const { index, parent, runId } of conversation.describeTurns()) {
|
|
29
|
+
const run = runId ?? (parent === undefined ? undefined : runsOf[parent]);
|
|
30
|
+
runsOf.push(run);
|
|
31
|
+
const kept = run === undefined ? undefined : keeper.get(run);
|
|
32
|
+
const position = kept === undefined ? -1 : line.indexOf(kept);
|
|
33
|
+
// -1 for the start, -2 for a run off the line or no longer kept.
|
|
34
|
+
const own = run === undefined ? -1 : position === -1 ? -2 : position;
|
|
35
|
+
const onLine = own >= -1 && (parent === undefined || depths[parent] !== undefined);
|
|
36
|
+
depths.push(onLine ? own : undefined);
|
|
37
|
+
const bestDepth = best === undefined ? -2 : (depths[best] ?? -2);
|
|
38
|
+
if (onLine && own >= bestDepth)
|
|
39
|
+
best = index;
|
|
40
|
+
}
|
|
41
|
+
const kept = new Set(conversation.path(best));
|
|
42
|
+
return {
|
|
43
|
+
turn: best,
|
|
44
|
+
// A turn left on another branch whose run is still on the line was not undone: a redo
|
|
45
|
+
// passed it by. Only a turn whose run left the line was.
|
|
46
|
+
undone: conversation
|
|
47
|
+
.path(conversation.currentTurn)
|
|
48
|
+
.filter(index => !kept.has(index) && depths[index] === undefined),
|
|
49
|
+
revisions: revisions.filter(revision => revision.revision > seenRevision),
|
|
50
|
+
latestRevision: revisions.at(-1)?.revision ?? seenRevision,
|
|
51
|
+
head,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* What the model is told when the workspace was moved back past some of its turns: that they
|
|
56
|
+
* were undone and no longer show, what they asked, or a summary of what they tried, the files
|
|
57
|
+
* those runs had changed, the files a move left as they were, and the user's reasons.
|
|
58
|
+
*/
|
|
59
|
+
export async function undoNote({ conversation, history, followed, summary, }) {
|
|
60
|
+
const lines = [
|
|
61
|
+
followed.head === undefined
|
|
62
|
+
? 'Note from the harness: the user moved the workspace back to how it was before any change an agent made. What the turns below asked for was undone, and those turns are no longer in this conversation.'
|
|
63
|
+
: 'Note from the harness: the user moved the workspace back to an earlier run. What the turns below asked for was undone, and those turns are no longer in this conversation.',
|
|
64
|
+
];
|
|
65
|
+
if (summary) {
|
|
66
|
+
lines.push('', 'What was tried, and what went wrong:', summary);
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
lines.push('', 'What they asked:');
|
|
70
|
+
for (const turn of followed.undone) {
|
|
71
|
+
const prompt = promptOf(conversation.messagesOf(turn)[0]);
|
|
72
|
+
if (prompt !== undefined)
|
|
73
|
+
lines.push(`- «${shorten(prompt, promptChars)}»`);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
let root;
|
|
77
|
+
const files = new Set();
|
|
78
|
+
const runs = new Set(conversation
|
|
79
|
+
.describeTurns()
|
|
80
|
+
.filter(turn => followed.undone.includes(turn.index))
|
|
81
|
+
.flatMap(turn => (turn.runId ? [turn.runId] : [])));
|
|
82
|
+
for (const runId of runs) {
|
|
83
|
+
const record = await changesOf(history, runId);
|
|
84
|
+
if (!record)
|
|
85
|
+
continue;
|
|
86
|
+
root = record.record.root;
|
|
87
|
+
for (const entry of record.entries)
|
|
88
|
+
files.add(entry.path);
|
|
89
|
+
}
|
|
90
|
+
if (files.size > 0) {
|
|
91
|
+
lines.push('', `Files those runs had changed: ${[...files].map(path => relativeTo(root, path)).join(', ')}`);
|
|
92
|
+
}
|
|
93
|
+
const conflicts = [
|
|
94
|
+
...new Set(followed.revisions.flatMap(revision => revision.conflicts.map(conflict => conflict.path))),
|
|
95
|
+
];
|
|
96
|
+
if (conflicts.length > 0) {
|
|
97
|
+
lines.push('', `Files left as they were, because they had changed since: ${conflicts.map(path => relativeTo(root, path)).join(', ')}`);
|
|
98
|
+
}
|
|
99
|
+
const reasons = followed.revisions.flatMap(revision => revision.reason ? [revision.reason] : []);
|
|
100
|
+
if (reasons.length > 0) {
|
|
101
|
+
lines.push('', `The user's reason: ${reasons.map(reason => `«${reason}»`).join(' ')}`);
|
|
102
|
+
}
|
|
103
|
+
lines.push('', 'Check the current state of a file before relying on what an earlier turn saw of it.');
|
|
104
|
+
return lines.join('\n');
|
|
105
|
+
}
|
|
106
|
+
/** The undone turns as plain text, for a model to summarize. Bounded, and marked when cut. */
|
|
107
|
+
export function transcriptOf(conversation, turns, maxChars = 30_000) {
|
|
108
|
+
const lines = [];
|
|
109
|
+
for (const turn of turns) {
|
|
110
|
+
for (const message of conversation.messagesOf(turn)) {
|
|
111
|
+
for (const part of message.content) {
|
|
112
|
+
if (part.type === 'text')
|
|
113
|
+
lines.push(`${message.role}: ${part.text}`);
|
|
114
|
+
if (part.type === 'toolCall') {
|
|
115
|
+
lines.push(`${message.role} called ${part.name} ${shorten(JSON.stringify(part.input), 300)}`);
|
|
116
|
+
}
|
|
117
|
+
if (part.type === 'toolResult') {
|
|
118
|
+
lines.push(`${part.name} ${part.isError ? 'failed' : 'returned'}: ${shorten(part.output, 300)}`);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
const text = lines.join('\n');
|
|
124
|
+
return text.length <= maxChars ? text : `${text.slice(0, maxChars)}\n[the rest was cut]`;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* A run's record and changes. A run retention chained into a later one is read from the run that
|
|
128
|
+
* holds its changes now, which names that run's own files too: they were undone with it, since a
|
|
129
|
+
* run is only off the line when the run that holds it is. A run retention removed names nothing.
|
|
130
|
+
*/
|
|
131
|
+
async function changesOf(history, runId) {
|
|
132
|
+
const tried = new Set();
|
|
133
|
+
for (let id = runId; id !== undefined && !tried.has(id);) {
|
|
134
|
+
tried.add(id);
|
|
135
|
+
try {
|
|
136
|
+
return await history.readRun(id);
|
|
137
|
+
}
|
|
138
|
+
catch (error) {
|
|
139
|
+
if (!(error instanceof HistoryExpiredError))
|
|
140
|
+
throw error;
|
|
141
|
+
id = error.keptIn;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
return undefined;
|
|
145
|
+
}
|
|
146
|
+
/** The prompt of a turn: its first message's last text, after any note the harness put first. */
|
|
147
|
+
function promptOf(message) {
|
|
148
|
+
const texts = message?.content.flatMap(part => (part.type === 'text' ? [part.text] : []));
|
|
149
|
+
return texts?.at(-1);
|
|
150
|
+
}
|
|
151
|
+
function shorten(text, chars) {
|
|
152
|
+
const flat = text.replace(/\s+/g, ' ').trim();
|
|
153
|
+
return flat.length <= chars ? flat : `${flat.slice(0, chars)}…`;
|
|
154
|
+
}
|
|
155
|
+
function relativeTo(root, path) {
|
|
156
|
+
return root !== undefined && path.startsWith(`${root}/`) ? path.slice(root.length + 1) : path;
|
|
157
|
+
}
|
|
158
|
+
//# sourceMappingURL=historyFollower.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"historyFollower.js","sourceRoot":"","sources":["../../../../src/engines/domain/model/historyFollower.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Conversation } from '#src/llm/domain/conversation';\nimport type { Message } from '#src/llm/domain/message';\nimport type { RecoveryStore, Revision } from '#src/recovery/domain/recoveryStore';\nimport { HistoryExpiredError } from '#src/recovery/domain/runTree';\n\n/** What an agent reads of its workspace's history. */\nexport type HistoryReader = Pick<RecoveryStore, 'listRuns' | 'listRevisions' | 'readRun'>;\n\n/** Where the conversation goes on after the workspace moved, and what the moves left behind. */\nexport interface Followed {\n\t/** The turn the next one follows. */\n\tturn: number | undefined;\n\t/**\n\t * The turns on the path to the last turn recorded whose run the workspace no longer holds:\n\t * what the moves since undid.\n\t */\n\tundone: number[];\n\t/** The moves since the conversation last looked, oldest first. */\n\trevisions: Revision[];\n\t/** The latest move, so the next look starts after it. */\n\tlatestRevision: number;\n\t/** Where the workspace is: `undefined` for the start of its history. */\n\thead: string | undefined;\n}\n\n// A prompt is named, not repeated: enough to recognise it.\nconst promptChars = 300;\n\n/**\n * Picks the turn a conversation goes on from, given where the workspace is now. A turn belongs\n * to the run it wrote in; a turn that wrote nothing belongs to the run of the turn it followed.\n * Of the turns whose run is on the line from the start to the head, it picks the one nearest\n * the head, and of those the newest. A run retention chained into a later one counts as that\n * one, and a run retention removed is off every line.\n */\nexport async function followHistory(\n\tconversation: Conversation,\n\thistory: HistoryReader,\n\tseenRevision: number,\n): Promise<Followed> {\n\tconst { head, runs } = await history.listRuns();\n\tconst revisions = await history.listRevisions();\n\n\tconst keeper = new Map<string, string>();\n\tfor (const run of runs) {\n\t\tkeeper.set(run.runId, run.runId);\n\t\tfor (const absorbed of run.absorbed ?? []) keeper.set(absorbed, run.runId);\n\t}\n\tconst parents = new Map(runs.map(run => [run.runId, run.parentRunId]));\n\tconst line: string[] = [];\n\tfor (let runId = head; runId !== undefined; runId = parents.get(runId)) line.unshift(runId);\n\n\t// -1 is the start of the history; `undefined` is a turn off the line.\n\tconst depths: (number | undefined)[] = [];\n\tconst runsOf: (string | undefined)[] = [];\n\tlet best: number | undefined;\n\tfor (const { index, parent, runId } of conversation.describeTurns()) {\n\t\tconst run = runId ?? (parent === undefined ? undefined : runsOf[parent]);\n\t\trunsOf.push(run);\n\t\tconst kept = run === undefined ? undefined : keeper.get(run);\n\t\tconst position = kept === undefined ? -1 : line.indexOf(kept);\n\t\t// -1 for the start, -2 for a run off the line or no longer kept.\n\t\tconst own = run === undefined ? -1 : position === -1 ? -2 : position;\n\t\tconst onLine = own >= -1 && (parent === undefined || depths[parent] !== undefined);\n\t\tdepths.push(onLine ? own : undefined);\n\t\tconst bestDepth = best === undefined ? -2 : (depths[best] ?? -2);\n\t\tif (onLine && own >= bestDepth) best = index;\n\t}\n\n\tconst kept = new Set(conversation.path(best));\n\treturn {\n\t\tturn: best,\n\t\t// A turn left on another branch whose run is still on the line was not undone: a redo\n\t\t// passed it by. Only a turn whose run left the line was.\n\t\tundone: conversation\n\t\t\t.path(conversation.currentTurn)\n\t\t\t.filter(index => !kept.has(index) && depths[index] === undefined),\n\t\trevisions: revisions.filter(revision => revision.revision > seenRevision),\n\t\tlatestRevision: revisions.at(-1)?.revision ?? seenRevision,\n\t\thead,\n\t};\n}\n\n/**\n * What the model is told when the workspace was moved back past some of its turns: that they\n * were undone and no longer show, what they asked, or a summary of what they tried, the files\n * those runs had changed, the files a move left as they were, and the user's reasons.\n */\nexport async function undoNote({\n\tconversation,\n\thistory,\n\tfollowed,\n\tsummary,\n}: {\n\tconversation: Conversation;\n\thistory: HistoryReader;\n\tfollowed: Followed;\n\tsummary?: string;\n}): Promise<string> {\n\tconst lines = [\n\t\tfollowed.head === undefined\n\t\t\t? 'Note from the harness: the user moved the workspace back to how it was before any change an agent made. What the turns below asked for was undone, and those turns are no longer in this conversation.'\n\t\t\t: 'Note from the harness: the user moved the workspace back to an earlier run. What the turns below asked for was undone, and those turns are no longer in this conversation.',\n\t];\n\n\tif (summary) {\n\t\tlines.push('', 'What was tried, and what went wrong:', summary);\n\t} else {\n\t\tlines.push('', 'What they asked:');\n\t\tfor (const turn of followed.undone) {\n\t\t\tconst prompt = promptOf(conversation.messagesOf(turn)[0]);\n\t\t\tif (prompt !== undefined) lines.push(`- «${shorten(prompt, promptChars)}»`);\n\t\t}\n\t}\n\n\tlet root: string | undefined;\n\tconst files = new Set<string>();\n\tconst runs = new Set(\n\t\tconversation\n\t\t\t.describeTurns()\n\t\t\t.filter(turn => followed.undone.includes(turn.index))\n\t\t\t.flatMap(turn => (turn.runId ? [turn.runId] : [])),\n\t);\n\tfor (const runId of runs) {\n\t\tconst record = await changesOf(history, runId);\n\t\tif (!record) continue;\n\t\troot = record.record.root;\n\t\tfor (const entry of record.entries) files.add(entry.path);\n\t}\n\tif (files.size > 0) {\n\t\tlines.push(\n\t\t\t'',\n\t\t\t`Files those runs had changed: ${[...files].map(path => relativeTo(root, path)).join(', ')}`,\n\t\t);\n\t}\n\n\tconst conflicts = [\n\t\t...new Set(\n\t\t\tfollowed.revisions.flatMap(revision => revision.conflicts.map(conflict => conflict.path)),\n\t\t),\n\t];\n\tif (conflicts.length > 0) {\n\t\tlines.push(\n\t\t\t'',\n\t\t\t`Files left as they were, because they had changed since: ${conflicts.map(path => relativeTo(root, path)).join(', ')}`,\n\t\t);\n\t}\n\tconst reasons = followed.revisions.flatMap(revision =>\n\t\trevision.reason ? [revision.reason] : [],\n\t);\n\tif (reasons.length > 0) {\n\t\tlines.push('', `The user's reason: ${reasons.map(reason => `«${reason}»`).join(' ')}`);\n\t}\n\n\tlines.push(\n\t\t'',\n\t\t'Check the current state of a file before relying on what an earlier turn saw of it.',\n\t);\n\treturn lines.join('\\n');\n}\n\n/** The undone turns as plain text, for a model to summarize. Bounded, and marked when cut. */\nexport function transcriptOf(\n\tconversation: Conversation,\n\tturns: number[],\n\tmaxChars = 30_000,\n): string {\n\tconst lines: string[] = [];\n\tfor (const turn of turns) {\n\t\tfor (const message of conversation.messagesOf(turn)) {\n\t\t\tfor (const part of message.content) {\n\t\t\t\tif (part.type === 'text') lines.push(`${message.role}: ${part.text}`);\n\t\t\t\tif (part.type === 'toolCall') {\n\t\t\t\t\tlines.push(\n\t\t\t\t\t\t`${message.role} called ${part.name} ${shorten(JSON.stringify(part.input), 300)}`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tif (part.type === 'toolResult') {\n\t\t\t\t\tlines.push(\n\t\t\t\t\t\t`${part.name} ${part.isError ? 'failed' : 'returned'}: ${shorten(part.output, 300)}`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tconst text = lines.join('\\n');\n\treturn text.length <= maxChars ? text : `${text.slice(0, maxChars)}\\n[the rest was cut]`;\n}\n\n/**\n * A run's record and changes. A run retention chained into a later one is read from the run that\n * holds its changes now, which names that run's own files too: they were undone with it, since a\n * run is only off the line when the run that holds it is. A run retention removed names nothing.\n */\nasync function changesOf(\n\thistory: HistoryReader,\n\trunId: string,\n): Promise<Awaited<ReturnType<HistoryReader['readRun']>> | undefined> {\n\tconst tried = new Set<string>();\n\tfor (let id: string | undefined = runId; id !== undefined && !tried.has(id);) {\n\t\ttried.add(id);\n\t\ttry {\n\t\t\treturn await history.readRun(id);\n\t\t} catch (error) {\n\t\t\tif (!(error instanceof HistoryExpiredError)) throw error;\n\t\t\tid = error.keptIn;\n\t\t}\n\t}\n\treturn undefined;\n}\n\n/** The prompt of a turn: its first message's last text, after any note the harness put first. */\nfunction promptOf(message: Message | undefined): string | undefined {\n\tconst texts = message?.content.flatMap(part => (part.type === 'text' ? [part.text] : []));\n\treturn texts?.at(-1);\n}\n\nfunction shorten(text: string, chars: number): string {\n\tconst flat = text.replace(/\\s+/g, ' ').trim();\n\treturn flat.length <= chars ? flat : `${flat.slice(0, chars)}…`;\n}\n\nfunction relativeTo(root: string | undefined, path: string): string {\n\treturn root !== undefined && path.startsWith(`${root}/`) ? path.slice(root.length + 1) : path;\n}\n"]}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { type Agent, type AgentResponse, type RunOptions } from '#src/agent/domain/agent';
|
|
2
2
|
import type { LLMClient } from '#src/llm/domain/llm';
|
|
3
3
|
import type { Message } from '#src/llm/domain/message';
|
|
4
|
-
import type
|
|
4
|
+
import { type AgentTool } from '#src/tools/domain/preparedCall';
|
|
5
|
+
import type { ILogger } from '#src/shared/domain/logger';
|
|
6
|
+
import { type HistoryReader } from './historyFollower.ts';
|
|
5
7
|
/**
|
|
6
8
|
* An agent whose conversation is owned by MiKode: every run sends the whole context to a
|
|
7
9
|
* stateless `LLMClient`. Within a run the model may call the agent's tools; the agent runs them
|
|
@@ -16,15 +18,40 @@ export declare class LLMAgent implements Agent {
|
|
|
16
18
|
private readonly toolDefinitions;
|
|
17
19
|
private readonly maxSteps;
|
|
18
20
|
private readonly autoApprove;
|
|
19
|
-
|
|
21
|
+
private readonly logger;
|
|
22
|
+
private readonly history;
|
|
23
|
+
private readonly summarizer;
|
|
24
|
+
private seenRevision;
|
|
25
|
+
private readonly reads;
|
|
26
|
+
constructor({ llmClient, messages, tools, maxSteps, autoApprove, logger, history, summarizer, }: {
|
|
20
27
|
llmClient: LLMClient;
|
|
21
28
|
messages?: Message[];
|
|
22
|
-
tools?:
|
|
23
|
-
/** How many calls to the model one run may make before it fails. */
|
|
29
|
+
tools?: AgentTool[];
|
|
30
|
+
/** How many calls to the model one run may make before it fails. Defaults to 65. */
|
|
24
31
|
maxSteps?: number;
|
|
25
32
|
/** Runs every tool call without asking, `destructive` ones included. Meant for CI. */
|
|
26
33
|
autoApprove?: boolean;
|
|
34
|
+
/** Where a failure the agent absorbs is reported. The factories always pass one. */
|
|
35
|
+
logger?: ILogger;
|
|
36
|
+
/**
|
|
37
|
+
* The history of the workspace this agent writes to. With it, each run first follows the
|
|
38
|
+
* moves the user made through that history: the conversation goes back with the
|
|
39
|
+
* workspace, and the model is told what was undone.
|
|
40
|
+
*/
|
|
41
|
+
history?: HistoryReader;
|
|
42
|
+
/**
|
|
43
|
+
* A cheap model that summarizes what the undone turns tried and what went wrong. Without
|
|
44
|
+
* one, the model is told what those turns asked, word for word.
|
|
45
|
+
*/
|
|
46
|
+
summarizer?: LLMClient;
|
|
27
47
|
});
|
|
48
|
+
private followHistory;
|
|
49
|
+
/**
|
|
50
|
+
* Asks the summarizer what the undone turns tried and what went wrong. A summary is a help,
|
|
51
|
+
* not a need: one that fails is reported and the plain note is used instead.
|
|
52
|
+
*/
|
|
53
|
+
private summarizeUndone;
|
|
54
|
+
private warnSummaryFailed;
|
|
28
55
|
private llmCall;
|
|
29
56
|
/**
|
|
30
57
|
* Undefined when the call may run; otherwise why it may not, which the model receives in its
|
|
@@ -40,5 +67,16 @@ export declare class LLMAgent implements Agent {
|
|
|
40
67
|
private runTool;
|
|
41
68
|
/** One at a time and in order, so their effects and their narration never interleave. */
|
|
42
69
|
private runTools;
|
|
70
|
+
/**
|
|
71
|
+
* Runs inside the context of the run that reached it, or in one of its own that it ends with
|
|
72
|
+
* the run: completed, failed or cancelled. Only a run that owns its context names the history
|
|
73
|
+
* run it recorded, since the run is over only when that context ends.
|
|
74
|
+
*/
|
|
43
75
|
run(prompt: string, options: RunOptions): Promise<AgentResponse>;
|
|
76
|
+
/**
|
|
77
|
+
* What the run started ends here, but its result stands: the changes it recorded are already
|
|
78
|
+
* on disk, so a record that could not be closed is reported, not turned into a failure.
|
|
79
|
+
*/
|
|
80
|
+
private finishContext;
|
|
81
|
+
private runSteps;
|
|
44
82
|
}
|