sandoichi 0.4.1 → 0.5.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 +6 -2
- package/index.mjs +75 -0
- package/package.json +1 -1
- package/src/accounting-cli.mjs +1 -1
- package/src/artifact-cli.mjs +67 -0
- package/src/artifact-lifecycle.mjs +67 -0
- package/src/artifact-recovery.mjs +138 -0
- package/src/artifact-store.mjs +44 -0
- package/src/cache-attribution.mjs +13 -3
- package/src/context-audit-cli.mjs +104 -0
- package/src/context-capture.mjs +200 -0
- package/src/context-classifier.mjs +142 -0
- package/src/context-footprint.mjs +299 -0
- package/src/context-transform.mjs +146 -17
- package/src/core.mjs +300 -35
- package/src/f1-telemetry.mjs +80 -0
- package/src/f4-telemetry.mjs +183 -0
- package/src/gateway-gate-cli.mjs +88 -0
- package/src/gateway-gate.mjs +412 -0
- package/src/history-archive.mjs +80 -0
- package/src/history-disclosure.mjs +70 -0
- package/src/hook-cli.mjs +17 -1
- package/src/lazy-mcp-gateway-stdio.mjs +59 -0
- package/src/lazy-mcp-gateway.mjs +295 -0
- package/src/mcp-server.mjs +72 -11
- package/src/metrics.mjs +4 -3
- package/src/provider-usage.mjs +103 -23
- package/src/proxy.mjs +435 -19
- package/src/result-disclosure.mjs +115 -0
- package/src/slice.mjs +419 -0
- package/src/statusline.mjs +5 -9
- package/src/telemetry.mjs +155 -29
|
@@ -1,8 +1,13 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
|
|
1
3
|
import { estimateTokens } from './core.mjs';
|
|
2
4
|
import { dedupeHistory } from './history-dedupe.mjs';
|
|
3
5
|
import { selectHistoryCandidates, validateMaxHistoryTokens } from './history-budget.mjs';
|
|
4
6
|
import { shakeHistoricalResult } from './history-shake.mjs';
|
|
5
7
|
import { compactHistoricalStructure } from './history-structure.mjs';
|
|
8
|
+
import { buildHistoryDisclosure } from './history-disclosure.mjs';
|
|
9
|
+
import { prepareHistoryArtifact, persistHistoryArtifact } from './history-archive.mjs';
|
|
10
|
+
import { createRedactionProfile } from './redaction-profile.mjs';
|
|
6
11
|
|
|
7
12
|
const SUPERSEDED = '[sando superseded by newer read]';
|
|
8
13
|
const USELESS = '[sando elided useless success]';
|
|
@@ -11,6 +16,11 @@ const USELESS_SUCCESSES = new Set([
|
|
|
11
16
|
'no output.',
|
|
12
17
|
'(no output)',
|
|
13
18
|
]);
|
|
19
|
+
const DEFAULT_REDACTION_PROFILE = createRedactionProfile();
|
|
20
|
+
const DEFAULT_HISTORY_ARCHIVE_RETAIN_RESULTS = 3;
|
|
21
|
+
// A marker is useful only when the historical observation is materially larger
|
|
22
|
+
// than the recovery instructions it replaces. Small results stay inline.
|
|
23
|
+
const DEFAULT_HISTORY_ARCHIVE_MIN_BYTES = 3072;
|
|
14
24
|
|
|
15
25
|
function object(value) {
|
|
16
26
|
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
@@ -75,10 +85,35 @@ function resultError(item, text) {
|
|
|
75
85
|
return /^\s*(?:error|failed|failure)\b[:\s-]*/i.test(text);
|
|
76
86
|
}
|
|
77
87
|
|
|
88
|
+
function resultSuccess(item, provider, text) {
|
|
89
|
+
if (resultError(item, text)) return false;
|
|
90
|
+
if (provider === 'anthropic') return item.is_error !== true;
|
|
91
|
+
return item.ok === true || ['completed', 'success', 'succeeded'].includes(item.status);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function nativeCodexExecSafety(provider, call, record) {
|
|
95
|
+
const item = record.entry.item;
|
|
96
|
+
const output = item[record.entry.key];
|
|
97
|
+
if (provider !== 'openai-responses' || call?.providerType !== 'function_call'
|
|
98
|
+
|| call.name !== 'exec_command' || item.type !== 'function_call_output') return null;
|
|
99
|
+
if (typeof output !== 'string') return false;
|
|
100
|
+
if (item.error !== undefined || item.is_error === true || item.ok === false
|
|
101
|
+
|| (item.status !== undefined && !['completed', 'success', 'succeeded'].includes(item.status))) return false;
|
|
102
|
+
const lines = output.split(/\r?\n/);
|
|
103
|
+
const outputLine = lines.indexOf('Output:');
|
|
104
|
+
if (outputLine < 0) return false;
|
|
105
|
+
const exits = lines.slice(0, outputLine).filter((line) => line.startsWith('Process exited with code '));
|
|
106
|
+
return exits.length === 1 && exits[0] === 'Process exited with code 0';
|
|
107
|
+
}
|
|
108
|
+
|
|
78
109
|
function useless(text) {
|
|
79
110
|
return USELESS_SUCCESSES.has(text.trim().toLowerCase());
|
|
80
111
|
}
|
|
81
112
|
|
|
113
|
+
function historyArchiveMarker(text) {
|
|
114
|
+
return /^\[sando archived result sando:sha256:[a-f0-9]{64}; /.test(text) && text.endsWith(']');
|
|
115
|
+
}
|
|
116
|
+
|
|
82
117
|
function resultText(value) {
|
|
83
118
|
if (typeof value === 'string') return value;
|
|
84
119
|
if (Array.isArray(value) && value.length > 0
|
|
@@ -167,7 +202,7 @@ function collectResponses(body) {
|
|
|
167
202
|
if (['function_call', 'custom_tool_call'].includes(item?.type)) {
|
|
168
203
|
entries.push({
|
|
169
204
|
kind: 'call', id: item.call_id, name: item.name,
|
|
170
|
-
input: parseArguments(item.arguments ?? item.input), position,
|
|
205
|
+
input: parseArguments(item.arguments ?? item.input), position, providerType: item.type,
|
|
171
206
|
});
|
|
172
207
|
} else if (['function_call_output', 'custom_tool_call_output'].includes(item?.type)) {
|
|
173
208
|
entries.push({
|
|
@@ -185,13 +220,14 @@ const COLLECTORS = {
|
|
|
185
220
|
'openai-responses': collectResponses,
|
|
186
221
|
};
|
|
187
222
|
|
|
188
|
-
function historyRecords(entries, calls) {
|
|
223
|
+
function historyRecords(entries, calls, provider) {
|
|
189
224
|
return entries.flatMap((entry) => {
|
|
190
225
|
if (entry.kind !== 'result') return [];
|
|
191
226
|
const call = calls.get(entry.id);
|
|
192
227
|
const text = resultText(entry.item[entry.key]);
|
|
193
228
|
if (!call || text === null) return [];
|
|
194
229
|
const isError = resultError(entry.item, text);
|
|
230
|
+
const safe = resultSuccess(entry.item, provider, text);
|
|
195
231
|
return [{
|
|
196
232
|
id: entry.id,
|
|
197
233
|
toolName: call.name,
|
|
@@ -199,7 +235,7 @@ function historyRecords(entries, calls) {
|
|
|
199
235
|
output: entry.item[entry.key],
|
|
200
236
|
current: entry.current,
|
|
201
237
|
historical: !entry.current,
|
|
202
|
-
safe
|
|
238
|
+
safe,
|
|
203
239
|
isError,
|
|
204
240
|
position: entry.position,
|
|
205
241
|
estimatedTokens: estimateTokens(text),
|
|
@@ -224,7 +260,29 @@ function collectHistoryRecords(provider, body) {
|
|
|
224
260
|
calls.delete(id);
|
|
225
261
|
results.delete(id);
|
|
226
262
|
}
|
|
227
|
-
return historyRecords(entries, calls);
|
|
263
|
+
return historyRecords(entries, calls, provider);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
const DEFAULT_STRATEGIES = Object.freeze({
|
|
267
|
+
supersededRead: true,
|
|
268
|
+
producerUseless: true,
|
|
269
|
+
exactDuplicate: true,
|
|
270
|
+
repeatedLines: true,
|
|
271
|
+
historyShake: true,
|
|
272
|
+
recoverableArchive: false,
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
function strategyPolicy(policy) {
|
|
276
|
+
if (!object(policy) || !Object.hasOwn(policy, 'strategies')) return DEFAULT_STRATEGIES;
|
|
277
|
+
if (!object(policy.strategies)) throw new TypeError('strategies must be an object');
|
|
278
|
+
const strategies = { ...DEFAULT_STRATEGIES };
|
|
279
|
+
for (const [name, enabled] of Object.entries(policy.strategies)) {
|
|
280
|
+
if (!Object.hasOwn(strategies, name) || typeof enabled !== 'boolean') {
|
|
281
|
+
throw new TypeError(`invalid context strategy: ${name}`);
|
|
282
|
+
}
|
|
283
|
+
strategies[name] = enabled;
|
|
284
|
+
}
|
|
285
|
+
return strategies;
|
|
228
286
|
}
|
|
229
287
|
|
|
230
288
|
export function detectProviderBody(body, headers = {}) {
|
|
@@ -321,19 +379,47 @@ function suffixTokensByPosition(body) {
|
|
|
321
379
|
return suffix;
|
|
322
380
|
}
|
|
323
381
|
|
|
324
|
-
export function transformProviderRequest({ provider, body, policy, idleMs } = {}) {
|
|
382
|
+
export function transformProviderRequest({ provider, body, policy, idleMs, redactionProfile, historyArchiveRoot } = {}) {
|
|
325
383
|
const clone = structuredClone(body);
|
|
326
384
|
const estimatedInputTokens = estimate(body);
|
|
327
385
|
const selectedProvider = provider ?? detectProviderBody(body);
|
|
386
|
+
const strategies = strategyPolicy(policy);
|
|
328
387
|
const collector = COLLECTORS[selectedProvider];
|
|
329
388
|
let supersededReads = 0;
|
|
330
389
|
let elidedUselessSuccesses = 0;
|
|
331
390
|
let deduplicatedResults = 0;
|
|
332
391
|
let compactedStructures = 0;
|
|
333
392
|
let shakenResults = 0;
|
|
393
|
+
let archivedResults = 0;
|
|
394
|
+
let archiveRedactionSkips = 0;
|
|
395
|
+
let archiveNoSavingsSkips = 0;
|
|
396
|
+
let archiveSizeSkips = 0;
|
|
397
|
+
const disclosures = [];
|
|
398
|
+
const disclose = (record, reason, originalText, visibleText, recovery = 'rerun-tool') => {
|
|
399
|
+
if (typeof originalText !== 'string' || typeof visibleText !== 'string') return;
|
|
400
|
+
disclosures.push(buildHistoryDisclosure({
|
|
401
|
+
toolName: record.toolName, reason, originalText, visibleText, recovery, redactionProfile,
|
|
402
|
+
}));
|
|
403
|
+
};
|
|
334
404
|
const maxHistoryTokens = object(policy) && Object.hasOwn(policy, 'maxHistoryTokens')
|
|
335
405
|
? validateMaxHistoryTokens(policy.maxHistoryTokens)
|
|
336
406
|
: null;
|
|
407
|
+
const historyArchiveRetainResults = object(policy) && Object.hasOwn(policy, 'historyArchiveRetainResults')
|
|
408
|
+
? policy.historyArchiveRetainResults
|
|
409
|
+
: DEFAULT_HISTORY_ARCHIVE_RETAIN_RESULTS;
|
|
410
|
+
if (!Number.isSafeInteger(historyArchiveRetainResults) || historyArchiveRetainResults < 0) {
|
|
411
|
+
throw new TypeError('historyArchiveRetainResults must be a non-negative safe integer');
|
|
412
|
+
}
|
|
413
|
+
const historyArchiveMinBytes = object(policy) && Object.hasOwn(policy, 'historyArchiveMinBytes')
|
|
414
|
+
? policy.historyArchiveMinBytes
|
|
415
|
+
: DEFAULT_HISTORY_ARCHIVE_MIN_BYTES;
|
|
416
|
+
if (!Number.isSafeInteger(historyArchiveMinBytes) || historyArchiveMinBytes < 0) {
|
|
417
|
+
throw new TypeError('historyArchiveMinBytes must be a non-negative safe integer');
|
|
418
|
+
}
|
|
419
|
+
if (strategies.recoverableArchive
|
|
420
|
+
&& (typeof historyArchiveRoot !== 'string' || !path.isAbsolute(historyArchiveRoot))) {
|
|
421
|
+
throw new TypeError('historyArchiveRoot must be an absolute path');
|
|
422
|
+
}
|
|
337
423
|
const budgetTriggered = maxHistoryTokens !== null
|
|
338
424
|
&& BigInt(estimatedInputTokens) * 5n > BigInt(maxHistoryTokens) * 4n;
|
|
339
425
|
// Don't rewrite warm cached history unless the rewrite reclaims enough of the suffix
|
|
@@ -383,42 +469,73 @@ export function transformProviderRequest({ provider, body, policy, idleMs } = {}
|
|
|
383
469
|
results.delete(id);
|
|
384
470
|
}
|
|
385
471
|
|
|
472
|
+
const archiveRecords = historyRecords(entries, calls, selectedProvider);
|
|
473
|
+
const resultEntries = entries.filter((entry) => entry.kind === 'result');
|
|
474
|
+
const recentEntries = new Set(historyArchiveRetainResults === 0
|
|
475
|
+
? []
|
|
476
|
+
: resultEntries.slice(-historyArchiveRetainResults));
|
|
477
|
+
const archivedIds = new Set();
|
|
478
|
+
if (strategies.recoverableArchive) {
|
|
479
|
+
const profile = redactionProfile ?? DEFAULT_REDACTION_PROFILE;
|
|
480
|
+
if (!profile || typeof profile.redact !== 'function') throw new TypeError('history archive redaction profile is invalid');
|
|
481
|
+
for (const record of archiveRecords) {
|
|
482
|
+
const nativeSafety = nativeCodexExecSafety(selectedProvider, calls.get(record.id), record);
|
|
483
|
+
const safe = nativeSafety ?? record.safe;
|
|
484
|
+
if (!safe || !record.historical || recentEntries.has(record.entry)) continue;
|
|
485
|
+
const text = resultText(record.output);
|
|
486
|
+
if (text === null || historyArchiveMarker(text)) continue;
|
|
487
|
+
if (Buffer.byteLength(text, 'utf8') < historyArchiveMinBytes) { archiveSizeSkips += 1; continue; }
|
|
488
|
+
if (profile.redact(text).text !== text) { archiveRedactionSkips += 1; continue; }
|
|
489
|
+
const artifact = prepareHistoryArtifact({ root: historyArchiveRoot, content: text });
|
|
490
|
+
const reclaimed = reclaimedTokens(text, artifact.marker);
|
|
491
|
+
if (reclaimed === 0) { archiveNoSavingsSkips += 1; continue; }
|
|
492
|
+
if (cacheProtected(record.entry, reclaimed)) { cacheProtectedSkips += 1; continue; }
|
|
493
|
+
persistHistoryArtifact(artifact);
|
|
494
|
+
replaceResult(record.entry.item, record.entry.key, artifact.marker);
|
|
495
|
+
archivedIds.add(record.id);
|
|
496
|
+
archivedResults += 1;
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
|
|
386
500
|
const reads = [];
|
|
387
501
|
for (const call of calls.values()) {
|
|
388
502
|
const result = results.get(call.id);
|
|
389
503
|
const identity = readIdentity(call.name, call.input);
|
|
390
504
|
const text = result && resultText(result.item[result.key]);
|
|
391
505
|
if (!result || !identity || text === null) continue;
|
|
392
|
-
if (
|
|
506
|
+
if (resultSuccess(result.item, selectedProvider, text)) reads.push({ call, result, identity, text });
|
|
393
507
|
}
|
|
394
508
|
reads.sort((a, b) => a.call.position - b.call.position);
|
|
395
|
-
for (let index = 0; index < reads.length; index += 1) {
|
|
509
|
+
for (let index = 0; strategies.supersededRead && index < reads.length; index += 1) {
|
|
396
510
|
const old = reads[index];
|
|
397
|
-
if (old.result.current) continue;
|
|
511
|
+
if (old.result.current || archivedIds.has(old.call.id)) continue;
|
|
398
512
|
const newer = reads.slice(index + 1).find((candidate) =>
|
|
399
513
|
covers(candidate.identity, old.identity) && !useless(candidate.text));
|
|
400
514
|
if (!newer) continue;
|
|
401
515
|
if (cacheProtected(old.result, reclaimedTokens(old.text, SUPERSEDED))) { cacheProtectedSkips += 1; continue; }
|
|
402
516
|
replaceResult(old.result.item, old.result.key, SUPERSEDED);
|
|
517
|
+
disclose({ toolName: old.call.name }, 'superseded-read', old.text, SUPERSEDED);
|
|
403
518
|
supersededReads += 1;
|
|
404
519
|
}
|
|
405
520
|
|
|
406
|
-
for (const [id, result] of results) {
|
|
407
|
-
if (!calls.has(id) || result.current) continue;
|
|
521
|
+
for (const [id, result] of strategies.producerUseless ? results : []) {
|
|
522
|
+
if (!calls.has(id) || result.current || archivedIds.has(id)) continue;
|
|
408
523
|
const text = resultText(result.item[result.key]);
|
|
409
524
|
if (text === null) continue;
|
|
410
|
-
if (text === SUPERSEDED ||
|
|
525
|
+
if (text === SUPERSEDED || !resultSuccess(result.item, selectedProvider, text) || !useless(text)) continue;
|
|
411
526
|
if (cacheProtected(result, reclaimedTokens(text, USELESS))) { cacheProtectedSkips += 1; continue; }
|
|
412
527
|
replaceResult(result.item, result.key, USELESS);
|
|
528
|
+
disclose({ toolName: calls.get(id).name }, 'useless-success', text, USELESS);
|
|
413
529
|
elidedUselessSuccesses += 1;
|
|
414
530
|
}
|
|
415
531
|
|
|
416
|
-
const records = historyRecords(entries, calls);
|
|
532
|
+
const records = historyRecords(entries, calls, selectedProvider);
|
|
417
533
|
const candidates = maxHistoryTokens === null
|
|
418
|
-
? records.filter((record) => record.safe && record.historical)
|
|
419
|
-
: selectHistoryCandidates({ bodyTokens: estimatedInputTokens, maxHistoryTokens, candidates: records })
|
|
534
|
+
? records.filter((record) => record.safe && record.historical && !archivedIds.has(record.id))
|
|
535
|
+
: selectHistoryCandidates({ bodyTokens: estimatedInputTokens, maxHistoryTokens, candidates: records })
|
|
536
|
+
.filter((record) => !archivedIds.has(record.id));
|
|
420
537
|
const candidateIds = new Set(candidates.map((candidate) => candidate.id));
|
|
421
|
-
const reductions = dedupeHistory(records);
|
|
538
|
+
const reductions = strategies.exactDuplicate ? dedupeHistory(records) : { entries: [] };
|
|
422
539
|
const recordsById = new Map(records.map((record) => [record.id, record]));
|
|
423
540
|
for (const reduced of reductions.entries) {
|
|
424
541
|
if (!candidateIds.has(reduced.id)) continue;
|
|
@@ -427,10 +544,11 @@ export function transformProviderRequest({ provider, body, policy, idleMs } = {}
|
|
|
427
544
|
if (cacheProtected(original.entry, reclaimedTokens(
|
|
428
545
|
resultText(original.output) ?? '', resultText(reduced.output) ?? ''))) { cacheProtectedSkips += 1; continue; }
|
|
429
546
|
replaceResult(original.entry.item, original.entry.key, reduced.output);
|
|
547
|
+
disclose(original, 'duplicate-history', resultText(original.output) ?? '', resultText(reduced.output) ?? '', 'newer-result');
|
|
430
548
|
deduplicatedResults += 1;
|
|
431
549
|
}
|
|
432
550
|
|
|
433
|
-
for (const record of records) {
|
|
551
|
+
for (const record of strategies.repeatedLines ? records : []) {
|
|
434
552
|
if (!candidateIds.has(record.id)) continue;
|
|
435
553
|
const text = resultText(record.entry.item[record.entry.key]);
|
|
436
554
|
if (text === null) continue;
|
|
@@ -443,10 +561,11 @@ export function transformProviderRequest({ provider, body, policy, idleMs } = {}
|
|
|
443
561
|
if (compacted === text) continue;
|
|
444
562
|
if (cacheProtected(record.entry, reclaimedTokens(text, compacted))) { cacheProtectedSkips += 1; continue; }
|
|
445
563
|
replaceResult(record.entry.item, record.entry.key, compacted);
|
|
564
|
+
disclose(record, 'repeated-lines', text, compacted);
|
|
446
565
|
compactedStructures += 1;
|
|
447
566
|
}
|
|
448
567
|
|
|
449
|
-
if (maxHistoryTokens !== null && budgetTriggered) {
|
|
568
|
+
if (strategies.historyShake && maxHistoryTokens !== null && budgetTriggered) {
|
|
450
569
|
for (const record of records) {
|
|
451
570
|
if (!candidateIds.has(record.id)) continue;
|
|
452
571
|
const text = resultText(record.entry.item[record.entry.key]);
|
|
@@ -460,6 +579,7 @@ export function transformProviderRequest({ provider, body, policy, idleMs } = {}
|
|
|
460
579
|
if (!shaken.changed) continue;
|
|
461
580
|
if (cacheProtected(record.entry, reclaimedTokens(text, shaken.text))) { cacheProtectedSkips += 1; continue; }
|
|
462
581
|
replaceResult(record.entry.item, record.entry.key, shaken.text);
|
|
582
|
+
disclose(record, 'history-shake', text, shaken.text);
|
|
463
583
|
shakenResults += 1;
|
|
464
584
|
}
|
|
465
585
|
}
|
|
@@ -471,10 +591,12 @@ export function transformProviderRequest({ provider, body, policy, idleMs } = {}
|
|
|
471
591
|
if (deduplicatedResults > 0) reasons.push('duplicate-history');
|
|
472
592
|
if (compactedStructures > 0) reasons.push('repeated-lines');
|
|
473
593
|
if (shakenResults > 0) reasons.push('history-shake');
|
|
594
|
+
if (archivedResults > 0) reasons.unshift('recoverable-archive');
|
|
474
595
|
return {
|
|
475
596
|
body: clone,
|
|
476
597
|
changed: reasons.length > 0,
|
|
477
598
|
reasons,
|
|
599
|
+
disclosures,
|
|
478
600
|
stats: {
|
|
479
601
|
estimatedInputTokens,
|
|
480
602
|
estimatedOutputTokens: estimate(clone),
|
|
@@ -483,6 +605,13 @@ export function transformProviderRequest({ provider, body, policy, idleMs } = {}
|
|
|
483
605
|
deduplicatedResults,
|
|
484
606
|
compactedStructures,
|
|
485
607
|
shakenResults,
|
|
608
|
+
archivedResults,
|
|
609
|
+
archiveRedactionSkips,
|
|
610
|
+
archiveNoSavingsSkips,
|
|
611
|
+
archiveSizeSkips,
|
|
612
|
+
historyDisclosureCount: disclosures.length,
|
|
613
|
+
historyDisclosureOriginalBytes: disclosures.reduce((total, item) => total + item.bytes.original, 0),
|
|
614
|
+
historyDisclosureVisibleBytes: disclosures.reduce((total, item) => total + item.bytes.visible, 0),
|
|
486
615
|
budgetTriggered,
|
|
487
616
|
cacheProtectedSkips,
|
|
488
617
|
cacheRewriteRatio: cacheWarm ? cacheRewriteRatio : null,
|