@aexol/spectral 0.9.210 → 0.9.212
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/agent/agents.d.ts.map +1 -1
- package/dist/agent/agents.js +40 -0
- package/dist/commands/serve.d.ts.map +1 -1
- package/dist/commands/serve.js +24 -4
- package/dist/extensions/browser/tools/console.d.ts.map +1 -1
- package/dist/extensions/browser/tools/console.js +22 -2
- package/dist/extensions/browser/tools/evaluate.d.ts.map +1 -1
- package/dist/extensions/browser/tools/evaluate.js +28 -4
- package/dist/extensions/browser/tools/network.d.ts.map +1 -1
- package/dist/extensions/browser/tools/network.js +22 -2
- package/dist/extensions/seo/tools/fetch.d.ts.map +1 -1
- package/dist/extensions/seo/tools/fetch.js +59 -8
- package/dist/extensions/web/tools/fetch.d.ts.map +1 -1
- package/dist/extensions/web/tools/fetch.js +28 -15
- package/dist/generated/zeus/const.d.ts.map +1 -1
- package/dist/generated/zeus/const.js +27 -0
- package/dist/generated/zeus/index.d.ts +122 -0
- package/dist/generated/zeus/index.d.ts.map +1 -1
- package/dist/memory/tool-output-compressor.d.ts.map +1 -1
- package/dist/memory/tool-output-compressor.js +53 -2
- package/dist/relay/client.d.ts +11 -0
- package/dist/relay/client.d.ts.map +1 -1
- package/dist/relay/client.js +24 -6
- package/dist/relay/history-projection.d.ts.map +1 -1
- package/dist/relay/history-projection.js +7 -0
- package/dist/sdk/coding-agent/core/tools/offload.d.ts +102 -0
- package/dist/sdk/coding-agent/core/tools/offload.d.ts.map +1 -0
- package/dist/sdk/coding-agent/core/tools/offload.js +207 -0
- package/dist/server/agent-bridge.d.ts.map +1 -1
- package/dist/server/agent-bridge.js +21 -0
- package/dist/server/offload-gc.d.ts +21 -0
- package/dist/server/offload-gc.d.ts.map +1 -0
- package/dist/server/offload-gc.js +111 -0
- package/dist/server/session-stream.d.ts +2 -0
- package/dist/server/session-stream.d.ts.map +1 -1
- package/dist/server/session-stream.js +166 -9
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tool-output-compressor.d.ts","sourceRoot":"","sources":["../../src/memory/tool-output-compressor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AACjE,OAAO,KAAK,EAEX,gBAAgB,EAIhB,MAAM,8CAA8C,CAAC;AACtD,OAAO,EAGN,KAAK,0BAA0B,EAC/B,MAAM,+CAA+C,CAAC;AACvD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"tool-output-compressor.d.ts","sourceRoot":"","sources":["../../src/memory/tool-output-compressor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AACjE,OAAO,KAAK,EAEX,gBAAgB,EAIhB,MAAM,8CAA8C,CAAC;AACtD,OAAO,EAGN,KAAK,0BAA0B,EAC/B,MAAM,+CAA+C,CAAC;AACvD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAQ5C,MAAM,MAAM,gBAAgB,GAAG,0BAA0B,CAAC;AAE1D,eAAO,MAAM,yBAAyB,EAAE,gBAEvC,CAAC;AAgsBF,MAAM,WAAW,gBAAgB;IAChC,aAAa,EAAE,MAAM,CAAC;IACtB,eAAe,EAAE,MAAM,CAAC;IACxB,cAAc,EAAE,MAAM,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,8BAA8B;IAC9C,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,MAAM,CAAC;IACtB,eAAe,EAAE,MAAM,CAAC;IACxB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,aAAa,EAAE,MAAM,CAAC;QAAC,eAAe,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAC3F;AA8BD,wBAAgB,iCAAiC,CAAC,OAAO,EAAE,OAAO,GAAG,8BAA8B,CAElG;AAED,wBAAgB,mCAAmC,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAE1E;AAgGD;;;;;GAKG;AACH,wBAAgB,0BAA0B,CACzC,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,OAAO,EACd,MAAM,EAAE;IAAE,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CAAE,EAC7F,GAAG,CAAC,EAAE,IAAI,CAAC,gBAAgB,EAAE,iBAAiB,CAAC,GAC7C,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,GAAG,SAAS,CAYvG;AAED,wBAAgB,4BAA4B,CAC3C,GAAG,EAAE,YAAY,EACjB,OAAO,EAAE,OAAO,GACd,IAAI,CAmCN"}
|
|
@@ -29,6 +29,7 @@
|
|
|
29
29
|
*/
|
|
30
30
|
import { DEFAULT_COMPACTION_POLICY, } from "../sdk/coding-agent/core/compaction/policy.js";
|
|
31
31
|
import { debugLog } from "./debug-log.js";
|
|
32
|
+
import { isAlreadyOffloaded, offloadLargeResult } from "../sdk/coding-agent/core/tools/offload.js";
|
|
32
33
|
export const DEFAULT_COMPRESSOR_CONFIG = {
|
|
33
34
|
...DEFAULT_COMPACTION_POLICY.toolOutputCompressor,
|
|
34
35
|
};
|
|
@@ -672,6 +673,55 @@ function resolveCompressorConfig(ctx) {
|
|
|
672
673
|
const manager = ctx.settingsManager;
|
|
673
674
|
return manager?.getCompactionPolicy?.().toolOutputCompressor ?? DEFAULT_COMPRESSOR_CONFIG;
|
|
674
675
|
}
|
|
676
|
+
// ============================================================================
|
|
677
|
+
// Large-result offload fallback
|
|
678
|
+
// ============================================================================
|
|
679
|
+
/**
|
|
680
|
+
* Fallback offload for tool types without native preview+file handling.
|
|
681
|
+
*
|
|
682
|
+
* - Skips read/bash (they already compress natively) and anything already
|
|
683
|
+
* carrying a `Full output:` footer (notably the bash offload path).
|
|
684
|
+
* - Skips error results and non-text/binary content.
|
|
685
|
+
* - Persists the full text via `offloadLargeResult()` and swaps the event
|
|
686
|
+
* text for preview + footer. Small results pass through untouched.
|
|
687
|
+
*
|
|
688
|
+
* The `tool_result` hook carries no session id, so offloads land under the
|
|
689
|
+
* shared `offloads/shared/` namespace.
|
|
690
|
+
*/
|
|
691
|
+
function maybeOffloadLargeResult(event) {
|
|
692
|
+
if (event.toolName === "read" || event.toolName === "bash")
|
|
693
|
+
return undefined;
|
|
694
|
+
if (event.isError)
|
|
695
|
+
return undefined;
|
|
696
|
+
const texts = event.content.filter((c) => c.type === "text" && typeof c.text === "string");
|
|
697
|
+
if (texts.length === 0)
|
|
698
|
+
return undefined;
|
|
699
|
+
const original = texts.map((c) => c.text).join("\n");
|
|
700
|
+
if (!original || original.includes("\0"))
|
|
701
|
+
return undefined;
|
|
702
|
+
if (isAlreadyOffloaded(original))
|
|
703
|
+
return undefined;
|
|
704
|
+
let offloaded;
|
|
705
|
+
try {
|
|
706
|
+
offloaded = offloadLargeResult(original, event.toolName, "shared");
|
|
707
|
+
}
|
|
708
|
+
catch {
|
|
709
|
+
return undefined;
|
|
710
|
+
}
|
|
711
|
+
if (!offloaded.fullOutputPath || !offloaded.truncation)
|
|
712
|
+
return undefined;
|
|
713
|
+
return {
|
|
714
|
+
content: [
|
|
715
|
+
...event.content.filter((c) => c.type !== "text"),
|
|
716
|
+
{ type: "text", text: offloaded.preview },
|
|
717
|
+
],
|
|
718
|
+
details: {
|
|
719
|
+
...(typeof event.details === "object" && event.details !== null ? event.details : {}),
|
|
720
|
+
fullOutputPath: offloaded.fullOutputPath,
|
|
721
|
+
offloadTruncation: offloaded.truncation,
|
|
722
|
+
},
|
|
723
|
+
};
|
|
724
|
+
}
|
|
675
725
|
function compressedContentResult(runtime, event, toolName, original, compressed) {
|
|
676
726
|
if (!compressed || compressed === original)
|
|
677
727
|
return undefined;
|
|
@@ -734,7 +784,8 @@ export function registerToolOutputCompressor(ext, runtime) {
|
|
|
734
784
|
const compressed = compressBashOutput(event, config);
|
|
735
785
|
return compressedContentResult(runtime, event, "bash", original, compressed);
|
|
736
786
|
}
|
|
737
|
-
//
|
|
738
|
-
|
|
787
|
+
// All other tool types (web_fetch, browser_*, seo_*, pdf_*, …): fall back
|
|
788
|
+
// to large-result offloading so no truncated output is ever lost.
|
|
789
|
+
return maybeOffloadLargeResult(_event);
|
|
739
790
|
});
|
|
740
791
|
}
|
package/dist/relay/client.d.ts
CHANGED
|
@@ -122,6 +122,17 @@ export declare class RelayClient extends EventEmitter {
|
|
|
122
122
|
/** Guards the async window of `scheduleReconnect` (health check). */
|
|
123
123
|
private reconnectInFlight;
|
|
124
124
|
constructor(opts: RelayClientOptions);
|
|
125
|
+
/**
|
|
126
|
+
* Emit a non-fatal error without being able to crash the host process.
|
|
127
|
+
*
|
|
128
|
+
* The `error` event is documented as non-fatal (parse failure, transport
|
|
129
|
+
* noise, send failure), but a bare `this.emit("error", ...)` on a Node
|
|
130
|
+
* EventEmitter THROWS when no listener is registered. Embeddings that
|
|
131
|
+
* never subscribe to `error` (SDK consumers, `serve` running in silent
|
|
132
|
+
* mode, ad-hoc clients in tests) would die on the first transport hiccup
|
|
133
|
+
* instead of logging it. When a listener exists, behaviour is unchanged.
|
|
134
|
+
*/
|
|
135
|
+
private emitError;
|
|
125
136
|
/** Open the connection. Idempotent — calling twice is a no-op. */
|
|
126
137
|
connect(): void;
|
|
127
138
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/relay/client.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/relay/client.ts"],"names":[],"mappings":"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"}
|
package/dist/relay/client.js
CHANGED
|
@@ -124,6 +124,24 @@ export class RelayClient extends EventEmitter {
|
|
|
124
124
|
this.exit = opts.exit ?? ((code) => process.exit(code));
|
|
125
125
|
this.runtimeInfo = opts.runtimeInfo;
|
|
126
126
|
}
|
|
127
|
+
/**
|
|
128
|
+
* Emit a non-fatal error without being able to crash the host process.
|
|
129
|
+
*
|
|
130
|
+
* The `error` event is documented as non-fatal (parse failure, transport
|
|
131
|
+
* noise, send failure), but a bare `this.emit("error", ...)` on a Node
|
|
132
|
+
* EventEmitter THROWS when no listener is registered. Embeddings that
|
|
133
|
+
* never subscribe to `error` (SDK consumers, `serve` running in silent
|
|
134
|
+
* mode, ad-hoc clients in tests) would die on the first transport hiccup
|
|
135
|
+
* instead of logging it. When a listener exists, behaviour is unchanged.
|
|
136
|
+
*/
|
|
137
|
+
emitError(err) {
|
|
138
|
+
if (this.listenerCount("error") > 0) {
|
|
139
|
+
this.emit("error", err);
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
143
|
+
this.logger.warn(`Relay error: ${msg}`);
|
|
144
|
+
}
|
|
127
145
|
/** Open the connection. Idempotent — calling twice is a no-op. */
|
|
128
146
|
connect() {
|
|
129
147
|
if (this.disposed)
|
|
@@ -213,7 +231,7 @@ export class RelayClient extends EventEmitter {
|
|
|
213
231
|
parsed = JSON.parse(data.toString());
|
|
214
232
|
}
|
|
215
233
|
catch (err) {
|
|
216
|
-
this.
|
|
234
|
+
this.emitError(err);
|
|
217
235
|
return;
|
|
218
236
|
}
|
|
219
237
|
// Heartbeat: reply pong inline, do NOT propagate.
|
|
@@ -257,7 +275,7 @@ export class RelayClient extends EventEmitter {
|
|
|
257
275
|
// `ws` emits 'error' before 'close'. Reconnect is driven from the
|
|
258
276
|
// `close` handler (or `forceReconnect` for suspected-dead sockets);
|
|
259
277
|
// scheduling it here would double-schedule.
|
|
260
|
-
this.
|
|
278
|
+
this.emitError(err);
|
|
261
279
|
});
|
|
262
280
|
ws.on("close", (code, reason) => {
|
|
263
281
|
this.handleDisconnect(ws, code, reason?.toString() ?? "");
|
|
@@ -312,7 +330,7 @@ export class RelayClient extends EventEmitter {
|
|
|
312
330
|
clearTimeout(this.heartbeatPongTimer);
|
|
313
331
|
this.heartbeatPongTimer = null;
|
|
314
332
|
}
|
|
315
|
-
this.
|
|
333
|
+
this.emitError(err);
|
|
316
334
|
this.forceReconnect("heartbeat-error");
|
|
317
335
|
}
|
|
318
336
|
}, HEARTBEAT_INTERVAL_MS);
|
|
@@ -428,7 +446,7 @@ export class RelayClient extends EventEmitter {
|
|
|
428
446
|
clearTimeout(this.sendTimer);
|
|
429
447
|
this.sendTimer = null;
|
|
430
448
|
}
|
|
431
|
-
this.
|
|
449
|
+
this.emitError(err);
|
|
432
450
|
this.forceReconnect("send-error");
|
|
433
451
|
}
|
|
434
452
|
}
|
|
@@ -456,7 +474,7 @@ export class RelayClient extends EventEmitter {
|
|
|
456
474
|
ws.terminate();
|
|
457
475
|
}
|
|
458
476
|
catch (err) {
|
|
459
|
-
this.
|
|
477
|
+
this.emitError(err);
|
|
460
478
|
}
|
|
461
479
|
this.handleDisconnect(ws, 1006, reason);
|
|
462
480
|
}
|
|
@@ -641,7 +659,7 @@ export class RelayClient extends EventEmitter {
|
|
|
641
659
|
// A synchronous throw from the WebSocket constructor (DNS, bad
|
|
642
660
|
// URL, etc.) must not kill the daemon — log it and re-schedule
|
|
643
661
|
// so the backoff continues forever.
|
|
644
|
-
this.
|
|
662
|
+
this.emitError(err);
|
|
645
663
|
void this.scheduleReconnect().catch(() => { });
|
|
646
664
|
}
|
|
647
665
|
}, delay);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"history-projection.d.ts","sourceRoot":"","sources":["../../src/relay/history-projection.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AACH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAErD,8EAA8E;AAC9E,eAAO,MAAM,yBAAyB,QAAa,CAAC;AAEpD,6DAA6D;AAC7D,eAAO,MAAM,iBAAiB,iDAAiD,CAAC;
|
|
1
|
+
{"version":3,"file":"history-projection.d.ts","sourceRoot":"","sources":["../../src/relay/history-projection.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AACH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAErD,8EAA8E;AAC9E,eAAO,MAAM,yBAAyB,QAAa,CAAC;AAEpD,6DAA6D;AAC7D,eAAO,MAAM,iBAAiB,iDAAiD,CAAC;AAwMhF;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAwC9D;AAED;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,SAAS,WAAW,EAAE,GAAG,WAAW,EAAE,CAMtF"}
|
|
@@ -72,6 +72,13 @@ function projectToolResult(event) {
|
|
|
72
72
|
compactDetails.status = details.status;
|
|
73
73
|
if ("mode" in details)
|
|
74
74
|
compactDetails.mode = details.mode;
|
|
75
|
+
// Preserve offload pointers so the agent can re-read full outputs.
|
|
76
|
+
if ("fullOutputPath" in details)
|
|
77
|
+
compactDetails.fullOutputPath = details.fullOutputPath;
|
|
78
|
+
if ("offloadTruncation" in details)
|
|
79
|
+
compactDetails.offloadTruncation = details.offloadTruncation;
|
|
80
|
+
if ("truncation" in details)
|
|
81
|
+
compactDetails.truncation = truncateStrings(details.truncation);
|
|
75
82
|
projectedResult = { ...result, details: compactDetails };
|
|
76
83
|
}
|
|
77
84
|
if (typeof projectedResult.content === "string") {
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Large-result offloading for tool outputs.
|
|
3
|
+
*
|
|
4
|
+
* Goal: no truncated tool result is ever lost. The full output goes to a
|
|
5
|
+
* file under `<configDir>/offloads/<sessionId>/`, and only a head+tail
|
|
6
|
+
* preview plus a footer (path + re-read instructions) enters the LLM context.
|
|
7
|
+
* The agent can then page through the file with `read` (offset/limit) or
|
|
8
|
+
* `bash` (sed/head/tail).
|
|
9
|
+
*
|
|
10
|
+
* This is the generic version of the pattern pioneered by the bash tool
|
|
11
|
+
* (preview + `Full output: <path>` footer). Small results pass through
|
|
12
|
+
* untouched — no file, no overhead. Binary-looking content (NUL bytes) is
|
|
13
|
+
* never offloaded.
|
|
14
|
+
*/
|
|
15
|
+
/** Default byte threshold above which a result is offloaded to disk. */
|
|
16
|
+
export declare const DEFAULT_OFFLOAD_BYTES: number;
|
|
17
|
+
/** Default line threshold above which a result is offloaded to disk. */
|
|
18
|
+
export declare const DEFAULT_OFFLOAD_LINES = 2000;
|
|
19
|
+
/** Preview head size (lines kept from the start). */
|
|
20
|
+
export declare const PREVIEW_HEAD_LINES = 100;
|
|
21
|
+
/** Preview tail size (lines kept from the end). */
|
|
22
|
+
export declare const PREVIEW_TAIL_LINES = 100;
|
|
23
|
+
/** Emergency byte cap for previews (head+tail can still be huge with long lines). */
|
|
24
|
+
export declare const PREVIEW_MAX_BYTES: number;
|
|
25
|
+
/** Marker identifying an already-offloaded result (bash tool compat). */
|
|
26
|
+
export declare const OFFLOAD_MARKER = "Full output:";
|
|
27
|
+
/** Anchored marker check: only a real `Full output: <path>.log` footer counts. */
|
|
28
|
+
export declare const OFFLOAD_PATH_RE: RegExp;
|
|
29
|
+
/**
|
|
30
|
+
* Parse a positive-int env override. Returns `fallback` when the variable is
|
|
31
|
+
* unset, unparseable, zero, or negative — a non-positive budget would
|
|
32
|
+
* otherwise disable offloading/GC entirely, so it is treated as "not set".
|
|
33
|
+
*/
|
|
34
|
+
export declare function envPositiveInt(name: string, fallback: number): number;
|
|
35
|
+
/** Effective byte limit (override via `SPECTRAL_OFFLOAD_BYTES`). */
|
|
36
|
+
export declare function getOffloadByteLimit(): number;
|
|
37
|
+
/** Effective line limit (override via `SPECTRAL_OFFLOAD_LINES`). */
|
|
38
|
+
export declare function getOffloadLineLimit(): number;
|
|
39
|
+
export interface OffloadTruncation {
|
|
40
|
+
originalBytes: number;
|
|
41
|
+
shownBytes: number;
|
|
42
|
+
headLines: number;
|
|
43
|
+
tailLines: number;
|
|
44
|
+
omittedLines: number;
|
|
45
|
+
totalLines: number;
|
|
46
|
+
fullOutputPath: string;
|
|
47
|
+
}
|
|
48
|
+
export interface OffloadResult {
|
|
49
|
+
/** Model-facing text: full original for small results, preview+footer for large ones. */
|
|
50
|
+
preview: string;
|
|
51
|
+
/** Absolute path of the persisted full output, or null when nothing was written. */
|
|
52
|
+
fullOutputPath: string | null;
|
|
53
|
+
/** Truncation metadata, or null when nothing was offloaded. */
|
|
54
|
+
truncation: OffloadTruncation | null;
|
|
55
|
+
}
|
|
56
|
+
export declare function countLines(text: string): number;
|
|
57
|
+
/** Marker for results that were already offloaded (or produced by the bash tool). */
|
|
58
|
+
export declare function isAlreadyOffloaded(text: string): boolean;
|
|
59
|
+
/**
|
|
60
|
+
* Whether `text` is large enough to deserve offloading.
|
|
61
|
+
* Binary-looking content (NUL bytes) is never offloaded.
|
|
62
|
+
*/
|
|
63
|
+
export declare function shouldOffload(text: string): boolean;
|
|
64
|
+
export interface PreviewResult {
|
|
65
|
+
preview: string;
|
|
66
|
+
headLines: number;
|
|
67
|
+
tailLines: number;
|
|
68
|
+
omittedLines: number;
|
|
69
|
+
totalLines: number;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Build a head+tail preview. Never cuts in the middle of a line (except via
|
|
73
|
+
* the emergency byte cap below); the gap is replaced with a
|
|
74
|
+
* `[... N lines omitted ...]` marker. When head+tail itself exceeds
|
|
75
|
+
* `PREVIEW_MAX_BYTES` (e.g. a single giant line), the preview is cut
|
|
76
|
+
* mid-line with a `[... N bytes omitted ...]` marker so the LLM context
|
|
77
|
+
* never receives an unbounded preview.
|
|
78
|
+
*/
|
|
79
|
+
export declare function buildPreview(text: string, headLines?: number, tailLines?: number): PreviewResult;
|
|
80
|
+
export interface OffloadFooterOptions {
|
|
81
|
+
headLines?: number;
|
|
82
|
+
tailLines?: number;
|
|
83
|
+
totalLines?: number;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Single-line footer appended to a preview. Carries the full-output path plus
|
|
87
|
+
* a copy-pasteable re-read hint for the agent.
|
|
88
|
+
*/
|
|
89
|
+
export declare function formatOffloadFooter(path: string, shown: number, total: number, opts?: OffloadFooterOptions): string;
|
|
90
|
+
/**
|
|
91
|
+
* Resolve (and create) the offload file path for a tool result:
|
|
92
|
+
* `<configDir>/offloads/<sessionId>/<tool>-<timestamp>-<rand>.log`.
|
|
93
|
+
* Honors `SPECTRAL_CONFIG_DIR` via `getConfigDir()`.
|
|
94
|
+
*/
|
|
95
|
+
export declare function resolveOffloadPath(sessionId: string, toolName: string): string;
|
|
96
|
+
/**
|
|
97
|
+
* Offload `text` to disk when it exceeds the size thresholds.
|
|
98
|
+
* Small (or binary, or already-offloaded) results are returned unchanged
|
|
99
|
+
* with `fullOutputPath: null` — no file, no overhead.
|
|
100
|
+
*/
|
|
101
|
+
export declare function offloadLargeResult(text: string, toolName: string, sessionId: string): OffloadResult;
|
|
102
|
+
//# sourceMappingURL=offload.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"offload.d.ts","sourceRoot":"","sources":["../../../../../src/sdk/coding-agent/core/tools/offload.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAQH,wEAAwE;AACxE,eAAO,MAAM,qBAAqB,QAAY,CAAC;AAC/C,wEAAwE;AACxE,eAAO,MAAM,qBAAqB,OAAO,CAAC;AAC1C,qDAAqD;AACrD,eAAO,MAAM,kBAAkB,MAAM,CAAC;AACtC,mDAAmD;AACnD,eAAO,MAAM,kBAAkB,MAAM,CAAC;AACtC,qFAAqF;AACrF,eAAO,MAAM,iBAAiB,QAAoB,CAAC;AACnD,yEAAyE;AACzE,eAAO,MAAM,cAAc,iBAAiB,CAAC;AAC7C,kFAAkF;AAClF,eAAO,MAAM,eAAe,QAA0B,CAAC;AAEvD;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAKrE;AAED,oEAAoE;AACpE,wBAAgB,mBAAmB,IAAI,MAAM,CAE5C;AAED,oEAAoE;AACpE,wBAAgB,mBAAmB,IAAI,MAAM,CAE5C;AAED,MAAM,WAAW,iBAAiB;IACjC,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,aAAa;IAC7B,yFAAyF;IACzF,OAAO,EAAE,MAAM,CAAC;IAChB,oFAAoF;IACpF,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,+DAA+D;IAC/D,UAAU,EAAE,iBAAiB,GAAG,IAAI,CAAC;CACrC;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAK/C;AAED,qFAAqF;AACrF,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAExD;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAKnD;AAED,MAAM,WAAW,aAAa;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAC3B,IAAI,EAAE,MAAM,EACZ,SAAS,GAAE,MAA2B,EACtC,SAAS,GAAE,MAA2B,GACpC,aAAa,CAmBf;AAmCD,MAAM,WAAW,oBAAoB;IACpC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAClC,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,EACb,IAAI,GAAE,oBAAyB,GAC7B,MAAM,CAiBR;AAOD;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAK9E;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,aAAa,CA6BnG"}
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Large-result offloading for tool outputs.
|
|
3
|
+
*
|
|
4
|
+
* Goal: no truncated tool result is ever lost. The full output goes to a
|
|
5
|
+
* file under `<configDir>/offloads/<sessionId>/`, and only a head+tail
|
|
6
|
+
* preview plus a footer (path + re-read instructions) enters the LLM context.
|
|
7
|
+
* The agent can then page through the file with `read` (offset/limit) or
|
|
8
|
+
* `bash` (sed/head/tail).
|
|
9
|
+
*
|
|
10
|
+
* This is the generic version of the pattern pioneered by the bash tool
|
|
11
|
+
* (preview + `Full output: <path>` footer). Small results pass through
|
|
12
|
+
* untouched — no file, no overhead. Binary-looking content (NUL bytes) is
|
|
13
|
+
* never offloaded.
|
|
14
|
+
*/
|
|
15
|
+
import { randomBytes } from "node:crypto";
|
|
16
|
+
import { mkdirSync, writeFileSync } from "node:fs";
|
|
17
|
+
import { join } from "node:path";
|
|
18
|
+
import { getConfigDir } from "../../../../config.js";
|
|
19
|
+
import { DEFAULT_MAX_BYTES } from "./truncate.js";
|
|
20
|
+
/** Default byte threshold above which a result is offloaded to disk. */
|
|
21
|
+
export const DEFAULT_OFFLOAD_BYTES = 50 * 1024; // 50KB
|
|
22
|
+
/** Default line threshold above which a result is offloaded to disk. */
|
|
23
|
+
export const DEFAULT_OFFLOAD_LINES = 2000;
|
|
24
|
+
/** Preview head size (lines kept from the start). */
|
|
25
|
+
export const PREVIEW_HEAD_LINES = 100;
|
|
26
|
+
/** Preview tail size (lines kept from the end). */
|
|
27
|
+
export const PREVIEW_TAIL_LINES = 100;
|
|
28
|
+
/** Emergency byte cap for previews (head+tail can still be huge with long lines). */
|
|
29
|
+
export const PREVIEW_MAX_BYTES = DEFAULT_MAX_BYTES;
|
|
30
|
+
/** Marker identifying an already-offloaded result (bash tool compat). */
|
|
31
|
+
export const OFFLOAD_MARKER = "Full output:";
|
|
32
|
+
/** Anchored marker check: only a real `Full output: <path>.log` footer counts. */
|
|
33
|
+
export const OFFLOAD_PATH_RE = /Full output: \S+\.log/;
|
|
34
|
+
/**
|
|
35
|
+
* Parse a positive-int env override. Returns `fallback` when the variable is
|
|
36
|
+
* unset, unparseable, zero, or negative — a non-positive budget would
|
|
37
|
+
* otherwise disable offloading/GC entirely, so it is treated as "not set".
|
|
38
|
+
*/
|
|
39
|
+
export function envPositiveInt(name, fallback) {
|
|
40
|
+
const raw = process.env[name];
|
|
41
|
+
if (!raw)
|
|
42
|
+
return fallback;
|
|
43
|
+
const parsed = Number.parseInt(raw, 10);
|
|
44
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
|
|
45
|
+
}
|
|
46
|
+
/** Effective byte limit (override via `SPECTRAL_OFFLOAD_BYTES`). */
|
|
47
|
+
export function getOffloadByteLimit() {
|
|
48
|
+
return envPositiveInt("SPECTRAL_OFFLOAD_BYTES", DEFAULT_OFFLOAD_BYTES);
|
|
49
|
+
}
|
|
50
|
+
/** Effective line limit (override via `SPECTRAL_OFFLOAD_LINES`). */
|
|
51
|
+
export function getOffloadLineLimit() {
|
|
52
|
+
return envPositiveInt("SPECTRAL_OFFLOAD_LINES", DEFAULT_OFFLOAD_LINES);
|
|
53
|
+
}
|
|
54
|
+
export function countLines(text) {
|
|
55
|
+
if (text.length === 0)
|
|
56
|
+
return 0;
|
|
57
|
+
const lines = text.split("\n");
|
|
58
|
+
// A trailing newline does not start a new line.
|
|
59
|
+
return text.endsWith("\n") ? lines.length - 1 : lines.length;
|
|
60
|
+
}
|
|
61
|
+
/** Marker for results that were already offloaded (or produced by the bash tool). */
|
|
62
|
+
export function isAlreadyOffloaded(text) {
|
|
63
|
+
return OFFLOAD_PATH_RE.test(text);
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Whether `text` is large enough to deserve offloading.
|
|
67
|
+
* Binary-looking content (NUL bytes) is never offloaded.
|
|
68
|
+
*/
|
|
69
|
+
export function shouldOffload(text) {
|
|
70
|
+
if (!text || text.includes("\0"))
|
|
71
|
+
return false;
|
|
72
|
+
if (isAlreadyOffloaded(text))
|
|
73
|
+
return false;
|
|
74
|
+
if (Buffer.byteLength(text, "utf-8") > getOffloadByteLimit())
|
|
75
|
+
return true;
|
|
76
|
+
return countLines(text) > getOffloadLineLimit();
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Build a head+tail preview. Never cuts in the middle of a line (except via
|
|
80
|
+
* the emergency byte cap below); the gap is replaced with a
|
|
81
|
+
* `[... N lines omitted ...]` marker. When head+tail itself exceeds
|
|
82
|
+
* `PREVIEW_MAX_BYTES` (e.g. a single giant line), the preview is cut
|
|
83
|
+
* mid-line with a `[... N bytes omitted ...]` marker so the LLM context
|
|
84
|
+
* never receives an unbounded preview.
|
|
85
|
+
*/
|
|
86
|
+
export function buildPreview(text, headLines = PREVIEW_HEAD_LINES, tailLines = PREVIEW_TAIL_LINES) {
|
|
87
|
+
const rawLines = text.split("\n");
|
|
88
|
+
// Drop the phantom empty element from a trailing newline.
|
|
89
|
+
const lines = text.endsWith("\n") && rawLines.length > 0 ? rawLines.slice(0, -1) : rawLines;
|
|
90
|
+
const totalLines = lines.length;
|
|
91
|
+
if (totalLines <= headLines + tailLines) {
|
|
92
|
+
return capPreviewBytes(text, {
|
|
93
|
+
preview: text,
|
|
94
|
+
headLines: totalLines,
|
|
95
|
+
tailLines: 0,
|
|
96
|
+
omittedLines: 0,
|
|
97
|
+
totalLines,
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
const head = lines.slice(0, headLines);
|
|
101
|
+
const tail = lines.slice(totalLines - tailLines);
|
|
102
|
+
const omittedLines = totalLines - headLines - tailLines;
|
|
103
|
+
const preview = [...head, `[... ${omittedLines} lines omitted ...]`, ...tail].join("\n");
|
|
104
|
+
return capPreviewBytes(preview, { preview, headLines, tailLines, omittedLines, totalLines });
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Emergency byte cap for previews. Long-line blobs (e.g. minified JSON on a
|
|
108
|
+
* single line) can make head+tail previews arbitrarily large; cut them
|
|
109
|
+
* mid-line with a byte-omitted marker. UTF-8 safe via Buffer slicing.
|
|
110
|
+
*/
|
|
111
|
+
function capPreviewBytes(textOrResult, fallback) {
|
|
112
|
+
const text = typeof textOrResult === "string" ? textOrResult : textOrResult.preview;
|
|
113
|
+
const base = typeof textOrResult === "string"
|
|
114
|
+
? fallback
|
|
115
|
+
: {
|
|
116
|
+
preview: textOrResult.preview,
|
|
117
|
+
headLines: textOrResult.headLines,
|
|
118
|
+
tailLines: textOrResult.tailLines,
|
|
119
|
+
omittedLines: textOrResult.omittedLines,
|
|
120
|
+
totalLines: textOrResult.totalLines,
|
|
121
|
+
};
|
|
122
|
+
if (Buffer.byteLength(text, "utf-8") <= PREVIEW_MAX_BYTES) {
|
|
123
|
+
return { ...base, preview: text };
|
|
124
|
+
}
|
|
125
|
+
const buf = Buffer.from(text, "utf-8");
|
|
126
|
+
// Leave room for the marker; walk back to a UTF-8 char boundary.
|
|
127
|
+
const markerReserve = 64;
|
|
128
|
+
let end = Math.max(0, PREVIEW_MAX_BYTES - markerReserve);
|
|
129
|
+
while (end > 0 && end < buf.length && (buf[end] & 0xc0) === 0x80)
|
|
130
|
+
end--;
|
|
131
|
+
const omitted = buf.length - end;
|
|
132
|
+
const preview = buf.subarray(0, end).toString("utf-8") + `\n[... ${omitted} bytes omitted ...]`;
|
|
133
|
+
return { ...base, preview };
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Single-line footer appended to a preview. Carries the full-output path plus
|
|
137
|
+
* a copy-pasteable re-read hint for the agent.
|
|
138
|
+
*/
|
|
139
|
+
export function formatOffloadFooter(path, shown, total, opts = {}) {
|
|
140
|
+
const headLines = opts.headLines ?? PREVIEW_HEAD_LINES;
|
|
141
|
+
const tailLines = opts.tailLines ?? PREVIEW_TAIL_LINES;
|
|
142
|
+
// Guard against inconsistent callers (e.g. byte-counted `shown` on a
|
|
143
|
+
// line-based total): never claim to show more than exists.
|
|
144
|
+
const safeShown = Math.min(shown, total);
|
|
145
|
+
const safeTotal = Math.max(total, 0);
|
|
146
|
+
let readHint;
|
|
147
|
+
if (opts.totalLines !== undefined && opts.totalLines > headLines + tailLines) {
|
|
148
|
+
const tailStart = opts.totalLines - tailLines + 1;
|
|
149
|
+
readHint =
|
|
150
|
+
`Read with: sed -n '1,${headLines}p' ${path} | head -c ${DEFAULT_MAX_BYTES} ` +
|
|
151
|
+
`or sed -n '${tailStart},${opts.totalLines}p' ${path} | head -c ${DEFAULT_MAX_BYTES}`;
|
|
152
|
+
}
|
|
153
|
+
else {
|
|
154
|
+
readHint = `Read with: sed -n '1,${headLines}p' ${path} | head -c ${DEFAULT_MAX_BYTES}`;
|
|
155
|
+
}
|
|
156
|
+
return `[Truncated: showing ${safeShown} of ${safeTotal} bytes. Full output: ${path}. ${readHint}]`;
|
|
157
|
+
}
|
|
158
|
+
function sanitizeSegment(segment) {
|
|
159
|
+
const cleaned = segment.replace(/[^a-zA-Z0-9_-]/g, "_").slice(0, 64);
|
|
160
|
+
return cleaned || "shared";
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Resolve (and create) the offload file path for a tool result:
|
|
164
|
+
* `<configDir>/offloads/<sessionId>/<tool>-<timestamp>-<rand>.log`.
|
|
165
|
+
* Honors `SPECTRAL_CONFIG_DIR` via `getConfigDir()`.
|
|
166
|
+
*/
|
|
167
|
+
export function resolveOffloadPath(sessionId, toolName) {
|
|
168
|
+
const dir = join(getConfigDir(), "offloads", sanitizeSegment(sessionId));
|
|
169
|
+
mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
170
|
+
const stamp = `${Date.now()}-${randomBytes(4).toString("hex")}`;
|
|
171
|
+
return join(dir, `${sanitizeSegment(toolName)}-${stamp}.log`);
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Offload `text` to disk when it exceeds the size thresholds.
|
|
175
|
+
* Small (or binary, or already-offloaded) results are returned unchanged
|
|
176
|
+
* with `fullOutputPath: null` — no file, no overhead.
|
|
177
|
+
*/
|
|
178
|
+
export function offloadLargeResult(text, toolName, sessionId) {
|
|
179
|
+
if (!shouldOffload(text)) {
|
|
180
|
+
return { preview: text, fullOutputPath: null, truncation: null };
|
|
181
|
+
}
|
|
182
|
+
const originalBytes = Buffer.byteLength(text, "utf-8");
|
|
183
|
+
const { preview, headLines, tailLines, omittedLines, totalLines } = buildPreview(text);
|
|
184
|
+
const fullOutputPath = resolveOffloadPath(sessionId, toolName);
|
|
185
|
+
// Owner-only: offloads may contain secrets (headers, tokens, page bodies).
|
|
186
|
+
writeFileSync(fullOutputPath, text, { encoding: "utf-8", mode: 0o600 });
|
|
187
|
+
// `shown` is measured on the actually displayed preview text.
|
|
188
|
+
const footer = formatOffloadFooter(fullOutputPath, Buffer.byteLength(preview, "utf-8"), originalBytes, {
|
|
189
|
+
headLines,
|
|
190
|
+
tailLines,
|
|
191
|
+
totalLines,
|
|
192
|
+
});
|
|
193
|
+
const previewWithFooter = `${preview}\n${footer}`;
|
|
194
|
+
return {
|
|
195
|
+
preview: previewWithFooter,
|
|
196
|
+
fullOutputPath,
|
|
197
|
+
truncation: {
|
|
198
|
+
originalBytes,
|
|
199
|
+
shownBytes: Buffer.byteLength(previewWithFooter, "utf-8"),
|
|
200
|
+
headLines,
|
|
201
|
+
tailLines,
|
|
202
|
+
omittedLines,
|
|
203
|
+
totalLines,
|
|
204
|
+
fullOutputPath,
|
|
205
|
+
},
|
|
206
|
+
};
|
|
207
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent-bridge.d.ts","sourceRoot":"","sources":["../../src/server/agent-bridge.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiDG;AAaH,OAAO,EAQL,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,EAEtB,MAAM,8BAA8B,CAAC;AAatC,OAAO,EAEL,KAAK,aAAa,EACnB,MAAM,8BAA8B,CAAC;AAatC,OAAO,EACL,kBAAkB,IAAI,yBAAyB,EAEhD,MAAM,0BAA0B,CAAC;AAClC,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAC;AAC1D,OAAO,KAAK,EAAE,eAAe,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAG3E,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAEpE,UAAU,4BAA4B;IACpC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,cAAc,CAAC,EAAE,QAAQ,GAAG,MAAM,CAAC;CACpC;AAiBD,sEAAsE;AACtE,MAAM,MAAM,oBAAoB,GAAG,OAAO,yBAAyB,CAAC;AAmCpE,MAAM,WAAW,kBAAkB;IACjC,kFAAkF;IAClF,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,8EAA8E;IAC9E,kBAAkB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,qFAAqF;IACrF,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,2EAA2E;IAC3E,GAAG,EAAE,MAAM,CAAC;IACZ,qFAAqF;IACrF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;;OAKG;IACH,UAAU,EAAE,MAAM,CAAC;IACnB;;;;OAIG;IACH,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,8EAA8E;IAC9E,IAAI,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,CAAC;IACnC;;;;;OAKG;IACH,0BAA0B,EAAE,CAAC,GAAG,EAAE;QAChC,SAAS,EAAE,MAAM,CAAC;QAClB,OAAO,EAAE,MAAM,CAAC;QAChB,WAAW,EAAE,MAAM,CAAC;QACpB,6EAA6E;QAC7E,gBAAgB,CAAC,EAAE,MAAM,CAAC;KAC3B,KAAK,IAAI,CAAC;IACX;;;OAGG;IACH,yBAAyB,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IACxD;;;;OAIG;IACH,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,KAAK,KAAK,IAAI,CAAC;IAC/B;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,WAAW,EAAE,CAAC;IACxB;;;;OAIG;IACH,cAAc,CAAC,EAAE,qBAAqB,GAAG,IAAI,CAAC;IAC9C;;;;OAIG;IACH,kBAAkB,CAAC,EAAE,oBAAoB,CAAC;IAC1C;;;;;OAKG;IACH,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,qEAAqE;IACrE,kBAAkB,CAAC,EAAE,kBAAkB,CAAC;IACxC;;;;;;;;;;;OAWG;IACH,yBAAyB,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACtE;
|
|
1
|
+
{"version":3,"file":"agent-bridge.d.ts","sourceRoot":"","sources":["../../src/server/agent-bridge.ts"],"names":[],"mappings":"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"}
|
|
@@ -446,6 +446,13 @@ function parseWireToolEvents(eventsJsonl) {
|
|
|
446
446
|
* images and other rich results.
|
|
447
447
|
*/
|
|
448
448
|
const MAX_REHYDRATED_TEXT = 100_000;
|
|
449
|
+
/**
|
|
450
|
+
* Ceiling for blocks that already carry an offload footer (`Full output:`).
|
|
451
|
+
* These blocks are a bounded preview + a re-readable path, so cutting them
|
|
452
|
+
* with the generic limit would destroy the footer. Keep them whole up to
|
|
453
|
+
* this size; larger ones lose their middle but keep head + footer.
|
|
454
|
+
*/
|
|
455
|
+
const MAX_REHYDRATED_OFFLOAD_TEXT = 200_000;
|
|
449
456
|
function sanitizeRehydratedBlock(block) {
|
|
450
457
|
// Tool-result image blocks are not accepted by every provider; the raw
|
|
451
458
|
// result remains in events_jsonl for the UI, while the model gets a compact reference.
|
|
@@ -460,6 +467,20 @@ function sanitizeRehydratedBlock(block) {
|
|
|
460
467
|
// Truncate oversized text blocks to prevent context overflow.
|
|
461
468
|
if (block.type !== "text")
|
|
462
469
|
return block;
|
|
470
|
+
if (block.text.includes("Full output:")) {
|
|
471
|
+
if (block.text.length <= MAX_REHYDRATED_OFFLOAD_TEXT)
|
|
472
|
+
return block;
|
|
473
|
+
// Keep the head (preview) and the tail (offload footer), drop the middle.
|
|
474
|
+
const keepHead = Math.floor(MAX_REHYDRATED_OFFLOAD_TEXT * 0.7);
|
|
475
|
+
const keepTail = MAX_REHYDRATED_OFFLOAD_TEXT - keepHead;
|
|
476
|
+
const omitted = block.text.length - keepHead - keepTail;
|
|
477
|
+
return {
|
|
478
|
+
...block,
|
|
479
|
+
text: block.text.slice(0, keepHead) +
|
|
480
|
+
`\n\n[... ${omitted.toLocaleString()} bytes omitted from rehydrated offload preview ...]\n\n` +
|
|
481
|
+
block.text.slice(block.text.length - keepTail),
|
|
482
|
+
};
|
|
483
|
+
}
|
|
463
484
|
if (block.text.length <= MAX_REHYDRATED_TEXT)
|
|
464
485
|
return block;
|
|
465
486
|
const omitted = block.text.length - MAX_REHYDRATED_TEXT;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Garbage collection for large-result offload files.
|
|
3
|
+
*
|
|
4
|
+
* Offloads live under `<configDir>/offloads/<sessionId>/*.log`. This sweep
|
|
5
|
+
* removes per-session directories older than `SPECTRAL_OFFLOAD_TTL_DAYS`
|
|
6
|
+
* (default 7) and, when the total still exceeds `SPECTRAL_OFFLOAD_MAX_MB`
|
|
7
|
+
* (default 500), evicts the oldest sessions first.
|
|
8
|
+
*
|
|
9
|
+
* Best-effort and synchronous: callers fire-and-forget it during preflight.
|
|
10
|
+
*/
|
|
11
|
+
export declare const DEFAULT_OFFLOAD_TTL_DAYS = 7;
|
|
12
|
+
export declare const DEFAULT_OFFLOAD_MAX_MB = 500;
|
|
13
|
+
export declare function getOffloadTtlMs(): number;
|
|
14
|
+
export declare function getOffloadMaxBytes(): number;
|
|
15
|
+
export interface OffloadSweepResult {
|
|
16
|
+
removedSessions: number;
|
|
17
|
+
removedBytes: number;
|
|
18
|
+
}
|
|
19
|
+
/** Remove stale/over-budget offload directories. Never throws. */
|
|
20
|
+
export declare function sweepOffloads(root?: string): OffloadSweepResult;
|
|
21
|
+
//# sourceMappingURL=offload-gc.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"offload-gc.d.ts","sourceRoot":"","sources":["../../src/server/offload-gc.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAOH,eAAO,MAAM,wBAAwB,IAAI,CAAC;AAC1C,eAAO,MAAM,sBAAsB,MAAM,CAAC;AAE1C,wBAAgB,eAAe,IAAI,MAAM,CAGxC;AAED,wBAAgB,kBAAkB,IAAI,MAAM,CAG3C;AAsBD,MAAM,WAAW,kBAAkB;IACjC,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,kEAAkE;AAClE,wBAAgB,aAAa,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,kBAAkB,CAoD/D"}
|