@myagentroam/agent 0.11.52 → 0.11.54
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/model/anthropic-messages.d.ts +4 -0
- package/dist/model/anthropic-messages.js +1 -1
- package/dist/model/native-context.d.ts +6 -0
- package/dist/model/native-context.js +15 -0
- package/dist/runtime/compaction/retained-context.d.ts +5 -0
- package/dist/runtime/compaction/retained-context.js +44 -1
- package/dist/runtime/context/gc-candidates-code.js +36 -21
- package/dist/runtime/context/gc-candidates-exec.js +33 -56
- package/dist/runtime/context/gc-candidates-read-patch.d.ts +1 -1
- package/dist/runtime/context/gc-candidates-read-patch.js +98 -15
- package/dist/runtime/context/gc-candidates-web.js +172 -8
- package/dist/runtime/context/gc-contracts.d.ts +2 -0
- package/dist/runtime/context/gc-economics.d.ts +0 -6
- package/dist/runtime/context/gc-economics.js +1 -23
- package/dist/runtime/context/gc-tokens.js +18 -8
- package/dist/runtime/context/gc.d.ts +1 -3
- package/dist/runtime/context/gc.js +19 -96
- package/dist/runtime/context/projection.js +11 -1
- package/dist/sdk/execution-compact-commit.d.ts +4 -2
- package/dist/sdk/execution-compact-commit.js +7 -3
- package/dist/sdk/execution-compaction.d.ts +1 -1
- package/dist/sdk/execution-compaction.js +21 -16
- package/dist/sdk/session-execution-factory.js +2 -1
- package/dist/session/code-mode-search-projection.d.ts +5 -0
- package/dist/session/code-mode-search-projection.js +46 -0
- package/dist/session/context-gc-checkpoint.js +233 -17
- package/dist/session/context-gc-contract.d.ts +3 -2
- package/dist/session/context-gc-exec-lifecycle.d.ts +11 -0
- package/dist/session/context-gc-exec-lifecycle.js +41 -0
- package/dist/session/context-gc-markup.d.ts +1 -0
- package/dist/session/context-gc-markup.js +4 -1
- package/dist/session/context-gc-observations.d.ts +5 -1
- package/dist/session/context-gc-observations.js +186 -33
- package/dist/session/context-gc-protection.d.ts +8 -0
- package/dist/session/context-gc-protection.js +83 -0
- package/dist/session/context-gc-replacements.d.ts +6 -1
- package/dist/session/context-gc-replacements.js +54 -6
- package/dist/session/context-gc-todo.d.ts +13 -0
- package/dist/session/context-gc-todo.js +47 -0
- package/dist/session/tool-callers.d.ts +7 -0
- package/dist/session/tool-callers.js +28 -0
- package/dist/tools/callers.d.ts +1 -7
- package/dist/tools/callers.js +1 -28
- package/dist/tools/code-mode/index.d.ts +1 -3
- package/dist/tools/code-mode/index.js +3 -45
- package/package.json +1 -1
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { completedTodoWrite, todoInputReplacement } from './context-gc-todo.js';
|
|
2
|
+
import { terminalExecLifecycles } from './context-gc-exec-lifecycle.js';
|
|
3
|
+
import { contextGcPatchResultFromCompletedPayload, contextGcPatchResultFromFailedPayload, contextGcExecInputReplacement, normalizeContextGcExecResult } from './context-gc-replacements.js';
|
|
3
4
|
import { parseCodeCalls, parseObservations } from './code-observation-projection.js';
|
|
4
5
|
/** Resolve private observation identity to real dispatch facts, not Provider tool calls. */
|
|
5
6
|
export function codeObservationEvidence(records, result, observation) {
|
|
@@ -29,8 +30,7 @@ function codeToolEvidence(records, result, source, outcome) {
|
|
|
29
30
|
isRecord(record.payload) &&
|
|
30
31
|
record.payload.callId === source.callId &&
|
|
31
32
|
record.payload.toolName === source.toolName &&
|
|
32
|
-
typeof record.payload.parentCallId === 'string'
|
|
33
|
-
isDeepStrictEqual(record.payload.input, source.input));
|
|
33
|
+
typeof record.payload.parentCallId === 'string');
|
|
34
34
|
if (!started || !isRecord(started.payload))
|
|
35
35
|
return undefined;
|
|
36
36
|
const parentId = started.payload.parentCallId;
|
|
@@ -47,20 +47,11 @@ function codeToolEvidence(records, result, source, outcome) {
|
|
|
47
47
|
return undefined;
|
|
48
48
|
const outerId = original.payload.callId;
|
|
49
49
|
const outer = records.findLast((record) => record.sequence < result.sequence &&
|
|
50
|
-
(record.type === 'tool.completed' ||
|
|
51
|
-
(outcome === 'failed' && record.type === 'tool.failed')) &&
|
|
50
|
+
(record.type === 'tool.completed' || record.type === 'tool.failed') &&
|
|
52
51
|
isRecord(record.payload) &&
|
|
53
52
|
record.payload.callId === outerId &&
|
|
54
53
|
(record.payload.toolName === 'code_exec' || record.payload.toolName === 'code_exec_wait'));
|
|
55
|
-
if (!outer
|
|
56
|
-
(outcome === 'completed'
|
|
57
|
-
? codeCellCompletionSequence(records, outer)
|
|
58
|
-
: partialPatchCellTerminalSequence(records, outer)) === undefined ||
|
|
59
|
-
(outcome === 'completed' &&
|
|
60
|
-
records.some((record) => record.sequence < result.sequence &&
|
|
61
|
-
record.type === 'tool.failed' &&
|
|
62
|
-
isRecord(record.payload) &&
|
|
63
|
-
record.payload.callId === outerId)))
|
|
54
|
+
if (!outer)
|
|
64
55
|
return undefined;
|
|
65
56
|
if (original.payload.callId !== parentId) {
|
|
66
57
|
const initial = records.find((record) => record.sequence < outer.sequence &&
|
|
@@ -102,22 +93,6 @@ function codeToolEvidence(records, result, source, outcome) {
|
|
|
102
93
|
};
|
|
103
94
|
return { callId: source.callId, call, terminal: completed, result };
|
|
104
95
|
}
|
|
105
|
-
function partialPatchCellTerminalSequence(records, result) {
|
|
106
|
-
if (!isRecord(result.payload) || !isRecord(result.payload.data))
|
|
107
|
-
return undefined;
|
|
108
|
-
const data = result.payload.data;
|
|
109
|
-
if (typeof data.cellId !== 'string')
|
|
110
|
-
return undefined;
|
|
111
|
-
const terminal = (status) => status === 'completed' || status === 'failed' || status === 'cancelled';
|
|
112
|
-
if (terminal(data.status))
|
|
113
|
-
return result.sequence;
|
|
114
|
-
if (data.status !== 'running')
|
|
115
|
-
return undefined;
|
|
116
|
-
return records.findLast((record) => record.type === 'code.cell.completed' &&
|
|
117
|
-
isRecord(record.payload) &&
|
|
118
|
-
record.payload.cellId === data.cellId &&
|
|
119
|
-
terminal(record.payload.status))?.sequence;
|
|
120
|
-
}
|
|
121
96
|
export function observationsFromRecord(record) {
|
|
122
97
|
return isRecord(record.payload) ? parseObservations(record.payload.observations) : undefined;
|
|
123
98
|
}
|
|
@@ -157,7 +132,81 @@ export function codePartialPatchCallEvidence(records, result, entry) {
|
|
|
157
132
|
export function callsFromRecord(record) {
|
|
158
133
|
return isRecord(record.payload) ? parseCodeCalls(record.payload.calls) : undefined;
|
|
159
134
|
}
|
|
160
|
-
export function retainedCodeInputs(records, parentCallId) {
|
|
135
|
+
export function retainedCodeInputs(records, parentCallId, todoEvidenceMature, execOptions) {
|
|
136
|
+
const initial = records.findLast((record) => record.type === 'tool.completed' &&
|
|
137
|
+
isRecord(record.payload) &&
|
|
138
|
+
record.payload.callId === parentCallId &&
|
|
139
|
+
record.payload.toolName === 'code_exec');
|
|
140
|
+
const initialData = initial && isRecord(initial.payload) && isRecord(initial.payload.data)
|
|
141
|
+
? initial.payload.data
|
|
142
|
+
: undefined;
|
|
143
|
+
const cellId = initialData?.cellId;
|
|
144
|
+
const inputs = new Map();
|
|
145
|
+
const nestedExecCallIds = new Set();
|
|
146
|
+
const nestedCallIds = new Set();
|
|
147
|
+
for (const record of records) {
|
|
148
|
+
if (record.type !== 'tool.started' ||
|
|
149
|
+
!isRecord(record.payload) ||
|
|
150
|
+
record.payload.parentCallId !== parentCallId ||
|
|
151
|
+
typeof record.payload.callId !== 'string')
|
|
152
|
+
continue;
|
|
153
|
+
nestedCallIds.add(record.payload.callId);
|
|
154
|
+
if (record.payload.toolName === 'exec' || record.payload.toolName === 'exec_wait')
|
|
155
|
+
nestedExecCallIds.add(record.payload.callId);
|
|
156
|
+
}
|
|
157
|
+
const waits = new Map();
|
|
158
|
+
for (const snapshot of records) {
|
|
159
|
+
if (snapshot.type === 'tool.completed' &&
|
|
160
|
+
isRecord(snapshot.payload) &&
|
|
161
|
+
snapshot.payload.toolName === 'code_exec_wait' &&
|
|
162
|
+
typeof snapshot.payload.callId === 'string')
|
|
163
|
+
waits.set(snapshot.payload.callId, snapshot);
|
|
164
|
+
if (snapshot.type !== 'model.context' ||
|
|
165
|
+
!isRecord(snapshot.payload) ||
|
|
166
|
+
snapshot.payload.role !== 'tool' ||
|
|
167
|
+
!Array.isArray(snapshot.payload.calls))
|
|
168
|
+
continue;
|
|
169
|
+
const calls = (callsFromRecord(snapshot) ?? []).filter((call) => nestedCallIds.has(call.callId));
|
|
170
|
+
if (calls.length === 0)
|
|
171
|
+
continue;
|
|
172
|
+
const outerId = snapshot.payload.callId;
|
|
173
|
+
if (outerId !== parentCallId) {
|
|
174
|
+
const outer = typeof outerId === 'string' ? waits.get(outerId) : undefined;
|
|
175
|
+
if (!outer ||
|
|
176
|
+
outer.sequence >= snapshot.sequence ||
|
|
177
|
+
!isRecord(outer.payload) ||
|
|
178
|
+
!isRecord(outer.payload.data) ||
|
|
179
|
+
typeof cellId !== 'string' ||
|
|
180
|
+
outer.payload.data.cellId !== cellId)
|
|
181
|
+
continue;
|
|
182
|
+
}
|
|
183
|
+
for (const call of calls)
|
|
184
|
+
inputs.set(call.callId, call.input);
|
|
185
|
+
}
|
|
186
|
+
const terminalExecs = new Map();
|
|
187
|
+
if (execOptions && nestedExecCallIds.size > 0) {
|
|
188
|
+
const processIds = new Set();
|
|
189
|
+
for (const callId of nestedExecCallIds) {
|
|
190
|
+
const input = inputs.get(callId);
|
|
191
|
+
if (isRecord(input) && typeof input.processId === 'string')
|
|
192
|
+
processIds.add(input.processId);
|
|
193
|
+
}
|
|
194
|
+
for (const record of records) {
|
|
195
|
+
if (!isRecord(record.payload) || typeof record.payload.callId !== 'string')
|
|
196
|
+
continue;
|
|
197
|
+
if (!nestedExecCallIds.has(record.payload.callId))
|
|
198
|
+
continue;
|
|
199
|
+
const data = record.type === 'tool.started' ? record.payload.input : record.payload.data;
|
|
200
|
+
if (isRecord(data) && typeof data.processId === 'string')
|
|
201
|
+
processIds.add(data.processId);
|
|
202
|
+
}
|
|
203
|
+
for (const lifecycle of terminalExecLifecycles(execFactsFromRecords(records, nestedExecCallIds, processIds), execOptions.activeProcessIds)) {
|
|
204
|
+
const boundary = lifecycle.at(-1).value.resultSequence;
|
|
205
|
+
if (boundary !== undefined && execOptions.evidenceMature(boundary))
|
|
206
|
+
for (const fact of lifecycle)
|
|
207
|
+
terminalExecs.set(fact.callId, boundary);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
161
210
|
return records.flatMap((record) => {
|
|
162
211
|
if (!isRecord(record.payload) ||
|
|
163
212
|
record.type !== 'tool.started' ||
|
|
@@ -176,12 +225,116 @@ export function retainedCodeInputs(records, parentCallId) {
|
|
|
176
225
|
next.type === 'tool.failed' &&
|
|
177
226
|
isRecord(next.payload) &&
|
|
178
227
|
next.payload.callId === call.callId);
|
|
228
|
+
const actualInput = inputs.has(String(call.callId))
|
|
229
|
+
? inputs.get(String(call.callId))
|
|
230
|
+
: call.input;
|
|
231
|
+
if ((call.toolName === 'exec' || call.toolName === 'exec_wait') &&
|
|
232
|
+
typeof call.callId === 'string' &&
|
|
233
|
+
terminalExecs.has(call.callId)) {
|
|
234
|
+
const summary = contextGcExecInputReplacement(actualInput, call.toolName);
|
|
235
|
+
if (!execOptions?.selected || execOptions.selected(call.callId, call.toolName, summary))
|
|
236
|
+
return [{ callId: call.callId, toolName: call.toolName, input: summary }];
|
|
237
|
+
}
|
|
238
|
+
if (call.toolName === 'todo_write' &&
|
|
239
|
+
todoEvidenceMature &&
|
|
240
|
+
isRecord(actualInput) &&
|
|
241
|
+
Array.isArray(actualInput.items)) {
|
|
242
|
+
const originalRecord = records.find((item) => completedTodoWrite(item)?.callId === call.callId);
|
|
243
|
+
const original = originalRecord && completedTodoWrite(originalRecord);
|
|
244
|
+
const successor = original &&
|
|
245
|
+
records
|
|
246
|
+
.flatMap((item) => {
|
|
247
|
+
const fact = completedTodoWrite(item);
|
|
248
|
+
return fact &&
|
|
249
|
+
fact.revision > original.revision &&
|
|
250
|
+
todoEvidenceMature(originalRecord, item)
|
|
251
|
+
? [{ record: item, ...fact }]
|
|
252
|
+
: [];
|
|
253
|
+
})
|
|
254
|
+
.sort((a, b) => b.revision - a.revision)[0];
|
|
255
|
+
if (original && successor)
|
|
256
|
+
return [
|
|
257
|
+
{
|
|
258
|
+
callId: call.callId,
|
|
259
|
+
toolName: call.toolName,
|
|
260
|
+
input: todoInputReplacement(original.revision, actualInput.items.length, successor.revision)
|
|
261
|
+
}
|
|
262
|
+
];
|
|
263
|
+
}
|
|
179
264
|
// Successful patches have durable file evidence; other inputs retain their direct-mode semantics.
|
|
180
265
|
return completedPatch
|
|
181
266
|
? []
|
|
182
|
-
: [{ callId: call.callId, toolName: call.toolName, input:
|
|
267
|
+
: [{ callId: call.callId, toolName: call.toolName, input: actualInput }];
|
|
183
268
|
});
|
|
184
269
|
}
|
|
270
|
+
function execFactsFromRecords(records, relevantCallIds, relevantProcessIds) {
|
|
271
|
+
const facts = new Map();
|
|
272
|
+
const completedByCallId = new Map();
|
|
273
|
+
const startedByCallId = new Map();
|
|
274
|
+
for (const record of records) {
|
|
275
|
+
if (isRecord(record.payload) &&
|
|
276
|
+
typeof record.payload.callId === 'string' &&
|
|
277
|
+
typeof record.payload.toolName === 'string') {
|
|
278
|
+
const key = `${record.payload.toolName}:${record.payload.callId}`;
|
|
279
|
+
if (record.type === 'tool.completed')
|
|
280
|
+
completedByCallId.set(key, record);
|
|
281
|
+
if (record.type === 'tool.started')
|
|
282
|
+
startedByCallId.set(key, record);
|
|
283
|
+
}
|
|
284
|
+
if (record.type !== 'model.context' || !isRecord(record.payload))
|
|
285
|
+
continue;
|
|
286
|
+
const payload = record.payload;
|
|
287
|
+
if (payload.role === 'tool_call' &&
|
|
288
|
+
(payload.name === 'exec' || payload.name === 'exec_wait') &&
|
|
289
|
+
typeof payload.callId === 'string') {
|
|
290
|
+
facts.set(payload.callId, {
|
|
291
|
+
value: {},
|
|
292
|
+
callId: payload.callId,
|
|
293
|
+
toolName: payload.name,
|
|
294
|
+
sequence: record.sequence,
|
|
295
|
+
...(isRecord(payload.arguments) ? { input: payload.arguments } : {})
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
if (payload.role !== 'tool')
|
|
299
|
+
continue;
|
|
300
|
+
if (typeof payload.callId === 'string') {
|
|
301
|
+
const fact = facts.get(payload.callId);
|
|
302
|
+
const completed = fact && completedByCallId.get(`${fact.toolName}:${fact.callId}`);
|
|
303
|
+
const result = completed && normalizeContextGcExecResult(payload.content, completed.payload);
|
|
304
|
+
if (fact && result)
|
|
305
|
+
facts.set(fact.callId, { ...fact, value: { resultSequence: record.sequence }, result });
|
|
306
|
+
}
|
|
307
|
+
for (const call of callsFromRecord(record) ?? []) {
|
|
308
|
+
if (call.toolName !== 'exec' && call.toolName !== 'exec_wait')
|
|
309
|
+
continue;
|
|
310
|
+
const started = startedByCallId.get(`${call.toolName}:${call.callId}`);
|
|
311
|
+
const completed = completedByCallId.get(`${call.toolName}:${call.callId}`);
|
|
312
|
+
const data = completed && isRecord(completed.payload) ? completed.payload.data : undefined;
|
|
313
|
+
const inputProcess = isRecord(call.input) ? call.input.processId : undefined;
|
|
314
|
+
const resultProcess = isRecord(data) ? data.processId : undefined;
|
|
315
|
+
if (!relevantCallIds.has(call.callId) &&
|
|
316
|
+
!(typeof inputProcess === 'string' && relevantProcessIds.has(inputProcess)) &&
|
|
317
|
+
!(typeof resultProcess === 'string' && relevantProcessIds.has(resultProcess)))
|
|
318
|
+
continue;
|
|
319
|
+
if (!started || !isRecord(started.payload) || started.payload.toolName !== call.toolName)
|
|
320
|
+
continue;
|
|
321
|
+
const evidence = codeCallEvidence(records, record, call);
|
|
322
|
+
const content = evidence && isRecord(evidence.result.payload) ? evidence.result.payload.content : undefined;
|
|
323
|
+
const result = evidence && normalizeContextGcExecResult(content, evidence.completed.payload);
|
|
324
|
+
facts.set(call.callId, {
|
|
325
|
+
value: result ? { resultSequence: record.sequence } : {},
|
|
326
|
+
callId: call.callId,
|
|
327
|
+
toolName: call.toolName,
|
|
328
|
+
sequence: started.sequence,
|
|
329
|
+
...(isRecord(call.input) ? { input: call.input } : {}),
|
|
330
|
+
...(result ? { result } : {})
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
return [...facts.values()].filter((fact) => relevantCallIds.has(fact.callId) ||
|
|
335
|
+
(typeof fact.input?.processId === 'string' && relevantProcessIds.has(fact.input.processId)) ||
|
|
336
|
+
(typeof fact.result?.processId === 'string' && relevantProcessIds.has(fact.result.processId)));
|
|
337
|
+
}
|
|
185
338
|
function isRecord(value) {
|
|
186
339
|
return !!value && typeof value === 'object' && !Array.isArray(value);
|
|
187
340
|
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { SessionRecord } from './record-contract.js';
|
|
2
|
+
/** One immutable boundary index, shared by all candidate anchors in this evaluation. */
|
|
3
|
+
export declare function buildContextGcProtection(records: readonly SessionRecord[], modelId: string, contextWindowTokens: number): {
|
|
4
|
+
after(anchor: number): {
|
|
5
|
+
rollouts: number;
|
|
6
|
+
thinking: number;
|
|
7
|
+
};
|
|
8
|
+
};
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { completedNativeThinkingIdentity } from '../model/native-context.js';
|
|
2
|
+
/** One immutable boundary index, shared by all candidate anchors in this evaluation. */
|
|
3
|
+
export function buildContextGcProtection(records, modelId, contextWindowTokens) {
|
|
4
|
+
const configurations = new Map();
|
|
5
|
+
const attempts = new Map();
|
|
6
|
+
const successful = [];
|
|
7
|
+
const thinking = [];
|
|
8
|
+
for (const record of records) {
|
|
9
|
+
if (!record.executionId ||
|
|
10
|
+
!['execution.header', 'model.attempt', 'model.context'].includes(record.type) ||
|
|
11
|
+
!isRecord(record.payload))
|
|
12
|
+
continue;
|
|
13
|
+
const key = record.executionId;
|
|
14
|
+
const payload = record.payload;
|
|
15
|
+
if (record.type === 'execution.header') {
|
|
16
|
+
configurations.set(key, {
|
|
17
|
+
modelId: typeof payload.modelId === 'string' ? payload.modelId : modelId,
|
|
18
|
+
window: typeof payload.contextWindowTokens === 'number'
|
|
19
|
+
? payload.contextWindowTokens
|
|
20
|
+
: contextWindowTokens
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
else if (record.type === 'model.attempt') {
|
|
24
|
+
if (payload.purpose !== 'EXECUTION')
|
|
25
|
+
continue;
|
|
26
|
+
if (payload.state === 'STARTED') {
|
|
27
|
+
const configuration = configurations.get(key);
|
|
28
|
+
attempts.set(key, {
|
|
29
|
+
start: record.sequence,
|
|
30
|
+
matching: payload.purpose === 'EXECUTION' &&
|
|
31
|
+
(configuration?.modelId ?? modelId) === modelId &&
|
|
32
|
+
(configuration?.window ?? contextWindowTokens) === contextWindowTokens,
|
|
33
|
+
native: new Set()
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
else if (payload.state === 'SUCCEEDED' || payload.state === 'FAILED') {
|
|
37
|
+
const attempt = attempts.get(key);
|
|
38
|
+
if (attempt?.matching) {
|
|
39
|
+
if (payload.state === 'SUCCEEDED')
|
|
40
|
+
successful.push(attempt.start);
|
|
41
|
+
for (const _identity of attempt.native)
|
|
42
|
+
thinking.push(attempt.start);
|
|
43
|
+
}
|
|
44
|
+
attempts.delete(key);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
else if (record.type === 'model.context') {
|
|
48
|
+
const attempt = attempts.get(key);
|
|
49
|
+
if (!attempt?.matching ||
|
|
50
|
+
(typeof payload.providerModelId === 'string' && payload.providerModelId !== modelId))
|
|
51
|
+
continue;
|
|
52
|
+
const identity = completedNativeThinkingIdentity(payload, record.recordId);
|
|
53
|
+
if (identity !== undefined)
|
|
54
|
+
attempt.native.add(identity);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
for (const attempt of attempts.values())
|
|
58
|
+
if (attempt.matching)
|
|
59
|
+
for (const _identity of attempt.native)
|
|
60
|
+
thinking.push(attempt.start);
|
|
61
|
+
successful.sort((a, b) => a - b);
|
|
62
|
+
thinking.sort((a, b) => a - b);
|
|
63
|
+
return {
|
|
64
|
+
after(anchor) {
|
|
65
|
+
return { rollouts: afterCount(successful, anchor), thinking: afterCount(thinking, anchor) };
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
function afterCount(values, anchor) {
|
|
70
|
+
let low = 0;
|
|
71
|
+
let high = values.length;
|
|
72
|
+
while (low < high) {
|
|
73
|
+
const middle = (low + high) >>> 1;
|
|
74
|
+
if (values[middle] <= anchor)
|
|
75
|
+
low = middle + 1;
|
|
76
|
+
else
|
|
77
|
+
high = middle;
|
|
78
|
+
}
|
|
79
|
+
return values.length - low;
|
|
80
|
+
}
|
|
81
|
+
function isRecord(value) {
|
|
82
|
+
return !!value && typeof value === 'object' && !Array.isArray(value);
|
|
83
|
+
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { ParsedReadContextItem } from '../prompts/read-context.js';
|
|
2
|
+
export declare function hasGenericContextGcPreview(originalContent: string): boolean;
|
|
1
3
|
import type { ContextGcWebToolName } from './context-gc-contract.js';
|
|
2
4
|
export type { ContextGcWebToolName } from './context-gc-contract.js';
|
|
3
5
|
export interface ContextGcCodeModeInput {
|
|
@@ -6,7 +8,7 @@ export interface ContextGcCodeModeInput {
|
|
|
6
8
|
}
|
|
7
9
|
export declare function normalizeContextGcCodeModeInput(arguments_: unknown): ContextGcCodeModeInput | undefined;
|
|
8
10
|
export declare function contextGcCodeModeInputReplacement(input: ContextGcCodeModeInput, retainedInputs?: readonly unknown[]): unknown;
|
|
9
|
-
export declare function contextGcCodeModeOutputReplacement(originalContent: string, truncated: boolean): string;
|
|
11
|
+
export declare function contextGcCodeModeOutputReplacement(originalContent: string, truncated: boolean, value?: boolean): string;
|
|
10
12
|
export interface ContextGcPatchFile {
|
|
11
13
|
readonly path: string;
|
|
12
14
|
readonly targetPath?: string;
|
|
@@ -54,10 +56,13 @@ export declare function contextGcStaleReadReplacement(input: {
|
|
|
54
56
|
readonly content: string;
|
|
55
57
|
readonly operation: Exclude<ContextGcPatchFile['operation'], 'added'>;
|
|
56
58
|
}): string;
|
|
59
|
+
export declare function contextGcReadRangeCovers(original: ParsedReadContextItem, later: ParsedReadContextItem): boolean;
|
|
60
|
+
export declare function contextGcCoveredReadReplacement(original: ParsedReadContextItem, successorCallId: string): string;
|
|
57
61
|
export declare function contextGcWebOutputReplacement(toolName: ContextGcWebToolName, originalContent: string, truncated: boolean): string;
|
|
58
62
|
/** Tools with their own evidence and lifecycle policies are never handled as generic output. */
|
|
59
63
|
export declare function isGenericContextGcTool(toolName: string): boolean;
|
|
60
64
|
export declare function contextGcToolOutputReplacement(toolName: string, originalContent: string, truncated: boolean): string;
|
|
65
|
+
export declare function contextGcSkillOutputReplacement(name: string, originalContent: string, successorCallId: string): string;
|
|
61
66
|
export declare function contextGcExecInputReplacement(arguments_: unknown, toolName?: string): unknown;
|
|
62
67
|
export declare function contextGcExecOutputReplacement(result: ContextGcExecResult): string;
|
|
63
68
|
export declare function normalizeContextGcExecResult(modelContent: unknown, completedPayload: unknown): ContextGcExecResult | undefined;
|
|
@@ -5,6 +5,10 @@ const EXEC_INPUT_COMPLETE_CHARACTERS = 512;
|
|
|
5
5
|
const EXEC_INPUT_PREVIEW_CHARACTERS = 256;
|
|
6
6
|
const EXEC_OUTPUT_PREVIEW_CHARACTERS = 512;
|
|
7
7
|
const CODE_MODE_OUTPUT_PREVIEW_CHARACTERS = 512;
|
|
8
|
+
const GENERIC_OUTPUT_PREVIEW_CHARACTERS = 768;
|
|
9
|
+
export function hasGenericContextGcPreview(originalContent) {
|
|
10
|
+
return [...originalContent].length > GENERIC_OUTPUT_PREVIEW_CHARACTERS * 2;
|
|
11
|
+
}
|
|
8
12
|
export function normalizeContextGcCodeModeInput(arguments_) {
|
|
9
13
|
if (typeof arguments_ === 'string' && arguments_.length > 0)
|
|
10
14
|
return { code: arguments_, inputMode: 'freeform' };
|
|
@@ -25,8 +29,8 @@ export function contextGcCodeModeInputReplacement(input, retainedInputs = []) {
|
|
|
25
29
|
});
|
|
26
30
|
return input.inputMode === 'freeform' ? content : { code: content };
|
|
27
31
|
}
|
|
28
|
-
export function contextGcCodeModeOutputReplacement(originalContent, truncated) {
|
|
29
|
-
const preview = unicodeHeadTail(originalContent, CODE_MODE_OUTPUT_PREVIEW_CHARACTERS);
|
|
32
|
+
export function contextGcCodeModeOutputReplacement(originalContent, truncated, value = false) {
|
|
33
|
+
const preview = unicodeHeadTail(originalContent, value ? GENERIC_OUTPUT_PREVIEW_CHARACTERS : CODE_MODE_OUTPUT_PREVIEW_CHARACTERS);
|
|
30
34
|
return JSON.stringify({
|
|
31
35
|
originalBytes: Buffer.byteLength(originalContent),
|
|
32
36
|
lines: lineCount(originalContent),
|
|
@@ -70,6 +74,34 @@ export function contextGcStaleReadReplacement(input) {
|
|
|
70
74
|
contextGc: 'This read predates a successful apply_patch. Re-read the path before relying on its contents.'
|
|
71
75
|
});
|
|
72
76
|
}
|
|
77
|
+
export function contextGcReadRangeCovers(original, later) {
|
|
78
|
+
const oldStart = original.metadata.startLine;
|
|
79
|
+
const oldEnd = original.metadata.endLine;
|
|
80
|
+
const newStart = later.metadata.startLine;
|
|
81
|
+
const newEnd = later.metadata.endLine;
|
|
82
|
+
return (!original.pathTruncated &&
|
|
83
|
+
!later.pathTruncated &&
|
|
84
|
+
later.metadata.previewComplete === true &&
|
|
85
|
+
later.metadata.oversizedLine === undefined &&
|
|
86
|
+
!later.content.startsWith('<mar_context_gc ') &&
|
|
87
|
+
typeof oldStart === 'number' &&
|
|
88
|
+
typeof oldEnd === 'number' &&
|
|
89
|
+
typeof newStart === 'number' &&
|
|
90
|
+
typeof newEnd === 'number' &&
|
|
91
|
+
newStart <= oldStart &&
|
|
92
|
+
newEnd >= oldEnd);
|
|
93
|
+
}
|
|
94
|
+
export function contextGcCoveredReadReplacement(original, successorCallId) {
|
|
95
|
+
return contextGcMarkup('stale_read', {
|
|
96
|
+
path: original.path,
|
|
97
|
+
formatVersion: 2,
|
|
98
|
+
originalBytes: Buffer.byteLength(original.content),
|
|
99
|
+
startLine: original.metadata.startLine,
|
|
100
|
+
endLine: original.metadata.endLine,
|
|
101
|
+
supersededByCallId: successorCallId,
|
|
102
|
+
contextGc: 'This visible range was covered by a later read.'
|
|
103
|
+
});
|
|
104
|
+
}
|
|
73
105
|
export function contextGcWebOutputReplacement(toolName, originalContent, truncated) {
|
|
74
106
|
const preview = unicodeHeadTail(originalContent, EXEC_OUTPUT_PREVIEW_CHARACTERS);
|
|
75
107
|
return JSON.stringify({
|
|
@@ -93,11 +125,16 @@ export function isGenericContextGcTool(toolName) {
|
|
|
93
125
|
'code_exec',
|
|
94
126
|
'code_exec_wait',
|
|
95
127
|
'web_search',
|
|
96
|
-
'web_fetch'
|
|
128
|
+
'web_fetch',
|
|
129
|
+
'skill',
|
|
130
|
+
'question',
|
|
131
|
+
'todo_read',
|
|
132
|
+
'todo_write',
|
|
133
|
+
'tool_search'
|
|
97
134
|
].includes(toolName));
|
|
98
135
|
}
|
|
99
136
|
export function contextGcToolOutputReplacement(toolName, originalContent, truncated) {
|
|
100
|
-
const preview = unicodeHeadTail(originalContent,
|
|
137
|
+
const preview = unicodeHeadTail(originalContent, GENERIC_OUTPUT_PREVIEW_CHARACTERS);
|
|
101
138
|
return JSON.stringify({
|
|
102
139
|
toolName,
|
|
103
140
|
originalBytes: Buffer.byteLength(originalContent),
|
|
@@ -108,6 +145,17 @@ export function contextGcToolOutputReplacement(toolName, originalContent, trunca
|
|
|
108
145
|
contextGc: 'Detailed tool output was reclaimed after six successful rollouts. An incomplete preview is not the complete result; the tool was not replayed.'
|
|
109
146
|
});
|
|
110
147
|
}
|
|
148
|
+
export function contextGcSkillOutputReplacement(name, originalContent, successorCallId) {
|
|
149
|
+
const firstLine = originalContent.split('\n', 1)[0] ?? '';
|
|
150
|
+
return JSON.stringify({
|
|
151
|
+
name,
|
|
152
|
+
...(firstLine.startsWith('<mar_skill ') && firstLine.length <= 1024
|
|
153
|
+
? { source: firstLine }
|
|
154
|
+
: {}),
|
|
155
|
+
supersededByCallId: successorCallId,
|
|
156
|
+
contextGc: 'This guidance was replaced by a later visible load of the same Skill.'
|
|
157
|
+
});
|
|
158
|
+
}
|
|
111
159
|
export function contextGcExecInputReplacement(arguments_, toolName = 'exec') {
|
|
112
160
|
if (!isRecord(arguments_))
|
|
113
161
|
return contextGcMarkup('terminal_exec_input', {
|
|
@@ -154,7 +202,7 @@ export function contextGcExecOutputReplacement(result) {
|
|
|
154
202
|
modelContextBytes: Buffer.byteLength(result.modelContextContent),
|
|
155
203
|
modelContextPreview: unicodeHeadTail(result.modelContextContent, RAW_MODEL_CONTEXT_PREVIEW_CHARACTERS),
|
|
156
204
|
contextGcFormatVersion: 3,
|
|
157
|
-
contextGc: 'The model-visible terminal result was truncated after execution and is no longer valid JSON. Detailed terminal input/output was reclaimed after the process reached a stable terminal state.
|
|
205
|
+
contextGc: 'The model-visible terminal result was truncated after execution and is no longer valid JSON. Detailed terminal input/output was reclaimed after the process reached a stable terminal state.'
|
|
158
206
|
});
|
|
159
207
|
const stdout = result.stdout;
|
|
160
208
|
const stderr = result.stderr;
|
|
@@ -182,7 +230,7 @@ export function contextGcExecOutputReplacement(result) {
|
|
|
182
230
|
result.stderrBytes === Buffer.byteLength(stderr) &&
|
|
183
231
|
stderrPreview === stderr,
|
|
184
232
|
contextGcFormatVersion: 3,
|
|
185
|
-
contextGc: 'Detailed terminal input/output was reclaimed after the process reached a stable terminal state. An incomplete preview is not the complete result.
|
|
233
|
+
contextGc: 'Detailed terminal input/output was reclaimed after the process reached a stable terminal state. An incomplete preview is not the complete result.'
|
|
186
234
|
});
|
|
187
235
|
}
|
|
188
236
|
export function normalizeContextGcExecResult(modelContent, completedPayload) {
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { SessionRecord } from './record-contract.js';
|
|
2
|
+
export declare function todoRevision(value: unknown): number | undefined;
|
|
3
|
+
/** A successful write is a domain fact even if its output was not observed. */
|
|
4
|
+
export declare function completedTodoWrite(record: SessionRecord): {
|
|
5
|
+
callId: string;
|
|
6
|
+
revision: number;
|
|
7
|
+
} | undefined;
|
|
8
|
+
export declare function visibleTodoSnapshot(content: string): {
|
|
9
|
+
revision: number;
|
|
10
|
+
count: number;
|
|
11
|
+
} | undefined;
|
|
12
|
+
export declare function todoInputReplacement(revision: number, count: number, successorRevision: number): unknown;
|
|
13
|
+
export declare function todoSnapshotReplacement(revision: number, count: number, successorRevision: number): string;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
function object(value) {
|
|
2
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
3
|
+
}
|
|
4
|
+
export function todoRevision(value) {
|
|
5
|
+
return object(value) && Number.isSafeInteger(value.revision) && value.revision >= 0
|
|
6
|
+
? value.revision
|
|
7
|
+
: undefined;
|
|
8
|
+
}
|
|
9
|
+
/** A successful write is a domain fact even if its output was not observed. */
|
|
10
|
+
export function completedTodoWrite(record) {
|
|
11
|
+
if (record.type !== 'tool.completed' ||
|
|
12
|
+
!object(record.payload) ||
|
|
13
|
+
record.payload.type !== 'tool.completed' ||
|
|
14
|
+
record.payload.toolName !== 'todo_write' ||
|
|
15
|
+
typeof record.payload.callId !== 'string')
|
|
16
|
+
return undefined;
|
|
17
|
+
const revision = todoRevision(record.payload.data);
|
|
18
|
+
return revision === undefined ? undefined : { callId: record.payload.callId, revision };
|
|
19
|
+
}
|
|
20
|
+
export function visibleTodoSnapshot(content) {
|
|
21
|
+
try {
|
|
22
|
+
const value = JSON.parse(content);
|
|
23
|
+
const revision = todoRevision(value);
|
|
24
|
+
return revision !== undefined && object(value) && Array.isArray(value.items)
|
|
25
|
+
? { revision, count: value.items.length }
|
|
26
|
+
: undefined;
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
return undefined;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
export function todoInputReplacement(revision, count, successorRevision) {
|
|
33
|
+
return {
|
|
34
|
+
contextGc: 'Todo input was superseded by a successful later revision.',
|
|
35
|
+
revision,
|
|
36
|
+
count,
|
|
37
|
+
supersededByRevision: successorRevision
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
export function todoSnapshotReplacement(revision, count, successorRevision) {
|
|
41
|
+
return JSON.stringify({
|
|
42
|
+
contextGc: 'Todo snapshot was superseded by a successful later revision.',
|
|
43
|
+
revision,
|
|
44
|
+
count,
|
|
45
|
+
supersededByRevision: successorRevision
|
|
46
|
+
});
|
|
47
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { ClientToolDefinition } from '../tools/contracts.js';
|
|
2
|
+
import type { ToolCaller } from '../tools/contracts.js';
|
|
3
|
+
/** Normalize trusted Host policy independently of discovery, Agent scope and parallel safety. */
|
|
4
|
+
export declare function toolCallers(tool: ClientToolDefinition): readonly ToolCaller[];
|
|
5
|
+
export declare function allowsToolCaller(tool: ClientToolDefinition, caller: ToolCaller): boolean;
|
|
6
|
+
/** Code Mode Only changes model exposure, never the Host's underlying caller permissions. */
|
|
7
|
+
export declare function allowsDirectTool(tool: ClientToolDefinition, codeModeEnabled: boolean): boolean;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { AgentError } from '../error.js';
|
|
2
|
+
/** Normalize trusted Host policy independently of discovery, Agent scope and parallel safety. */
|
|
3
|
+
export function toolCallers(tool) {
|
|
4
|
+
if (tool.codeModeExposure !== undefined && tool.codeModeExposure !== 'both')
|
|
5
|
+
throw new AgentError('MAR_AGENT_TOOL_INPUT_INVALID', 'Code Mode exposure is invalid.');
|
|
6
|
+
const value = tool.allowedCallers;
|
|
7
|
+
if (value === undefined)
|
|
8
|
+
return tool.name.startsWith('mcp__') ? ['direct', 'programmatic'] : ['direct'];
|
|
9
|
+
if (!Array.isArray(value) ||
|
|
10
|
+
value.length === 0 ||
|
|
11
|
+
value.some((caller) => caller !== 'direct' && caller !== 'programmatic'))
|
|
12
|
+
throw new AgentError('MAR_AGENT_TOOL_INPUT_INVALID', 'Tool caller policy is invalid.');
|
|
13
|
+
return ['direct', 'programmatic'].filter((caller) => value.includes(caller));
|
|
14
|
+
}
|
|
15
|
+
export function allowsToolCaller(tool, caller) {
|
|
16
|
+
// Control tools cannot be widened by a Host override or historical definition.
|
|
17
|
+
if (caller === 'programmatic' &&
|
|
18
|
+
['code_exec', 'code_exec_wait', 'tool_search', 'question', 'skill', 'sleep'].includes(tool.name))
|
|
19
|
+
return false;
|
|
20
|
+
return toolCallers(tool).includes(caller);
|
|
21
|
+
}
|
|
22
|
+
/** Code Mode Only changes model exposure, never the Host's underlying caller permissions. */
|
|
23
|
+
export function allowsDirectTool(tool, codeModeEnabled) {
|
|
24
|
+
return (allowsToolCaller(tool, 'direct') &&
|
|
25
|
+
(!codeModeEnabled ||
|
|
26
|
+
tool.codeModeExposure === 'both' ||
|
|
27
|
+
!allowsToolCaller(tool, 'programmatic')));
|
|
28
|
+
}
|
package/dist/tools/callers.d.ts
CHANGED
|
@@ -1,7 +1 @@
|
|
|
1
|
-
|
|
2
|
-
import type { ToolCaller } from './contracts.js';
|
|
3
|
-
/** Normalize trusted Host policy independently of discovery, Agent scope and parallel safety. */
|
|
4
|
-
export declare function toolCallers(tool: ClientToolDefinition): readonly ToolCaller[];
|
|
5
|
-
export declare function allowsToolCaller(tool: ClientToolDefinition, caller: ToolCaller): boolean;
|
|
6
|
-
/** Code Mode Only changes model exposure, never the Host's underlying caller permissions. */
|
|
7
|
-
export declare function allowsDirectTool(tool: ClientToolDefinition, codeModeEnabled: boolean): boolean;
|
|
1
|
+
export { toolCallers, allowsToolCaller, allowsDirectTool } from '../session/tool-callers.js';
|
package/dist/tools/callers.js
CHANGED
|
@@ -1,28 +1 @@
|
|
|
1
|
-
|
|
2
|
-
/** Normalize trusted Host policy independently of discovery, Agent scope and parallel safety. */
|
|
3
|
-
export function toolCallers(tool) {
|
|
4
|
-
if (tool.codeModeExposure !== undefined && tool.codeModeExposure !== 'both')
|
|
5
|
-
throw new AgentError('MAR_AGENT_TOOL_INPUT_INVALID', 'Code Mode exposure is invalid.');
|
|
6
|
-
const value = tool.allowedCallers;
|
|
7
|
-
if (value === undefined)
|
|
8
|
-
return tool.name.startsWith('mcp__') ? ['direct', 'programmatic'] : ['direct'];
|
|
9
|
-
if (!Array.isArray(value) ||
|
|
10
|
-
value.length === 0 ||
|
|
11
|
-
value.some((caller) => caller !== 'direct' && caller !== 'programmatic'))
|
|
12
|
-
throw new AgentError('MAR_AGENT_TOOL_INPUT_INVALID', 'Tool caller policy is invalid.');
|
|
13
|
-
return ['direct', 'programmatic'].filter((caller) => value.includes(caller));
|
|
14
|
-
}
|
|
15
|
-
export function allowsToolCaller(tool, caller) {
|
|
16
|
-
// Control tools cannot be widened by a Host override or historical definition.
|
|
17
|
-
if (caller === 'programmatic' &&
|
|
18
|
-
['code_exec', 'code_exec_wait', 'tool_search', 'question', 'skill', 'sleep'].includes(tool.name))
|
|
19
|
-
return false;
|
|
20
|
-
return toolCallers(tool).includes(caller);
|
|
21
|
-
}
|
|
22
|
-
/** Code Mode Only changes model exposure, never the Host's underlying caller permissions. */
|
|
23
|
-
export function allowsDirectTool(tool, codeModeEnabled) {
|
|
24
|
-
return (allowsToolCaller(tool, 'direct') &&
|
|
25
|
-
(!codeModeEnabled ||
|
|
26
|
-
tool.codeModeExposure === 'both' ||
|
|
27
|
-
!allowsToolCaller(tool, 'programmatic')));
|
|
28
|
-
}
|
|
1
|
+
export { toolCallers, allowsToolCaller, allowsDirectTool } from '../session/tool-callers.js';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ClientToolDefinition } from '../contracts.js';
|
|
2
|
-
import type { SessionModelMessage as ModelMessage } from '../../session/model-request.js';
|
|
3
2
|
import type { LocalProcessExecutor } from '../../process/contracts.js';
|
|
3
|
+
export { projectCodeModeSearchResult } from '../../session/code-mode-search-projection.js';
|
|
4
4
|
import { type CellOutput } from './cells.js';
|
|
5
5
|
import type { CodeModeEngineFactory } from './engine.js';
|
|
6
6
|
import type { ToolExecutionContext, ToolExecutionResult } from '../contracts.js';
|
|
@@ -9,8 +9,6 @@ export declare const codeModeToolDefinition: ClientToolDefinition;
|
|
|
9
9
|
export declare const codeExecWaitToolDefinition: ClientToolDefinition;
|
|
10
10
|
/** Deferred definitions travel in search results, not in the stable Code Mode description. */
|
|
11
11
|
export declare function projectCodeModeTools(tools: readonly ClientToolDefinition[], residentTools?: readonly ClientToolDefinition[]): ClientToolDefinition[];
|
|
12
|
-
/** Preserve native search definitions while providing the same discovery evidence to other protocols. */
|
|
13
|
-
export declare function projectCodeModeSearchResult(message: ModelMessage, codeModeEnabled: boolean): ModelMessage;
|
|
14
12
|
export declare class CodeModeTool {
|
|
15
13
|
#private;
|
|
16
14
|
private readonly paths;
|