@iowarp/clio-coder 0.3.3 → 0.3.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +39 -0
- package/CONTRIBUTING.md +1 -1
- package/README.md +3 -3
- package/dist/{acp-P2AQILE2.js → acp-S5R4RR5B.js} +7 -6
- package/dist/{agents-72W3BI7I.js → agents-P6DMMVZY.js} +24 -21
- package/dist/assets/codewiki.json +1 -1
- package/dist/{auth-5TWEIYDN.js → auth-2XCZLPKS.js} +12 -8
- package/dist/{chunk-GGXXDWE4.js → chunk-22NAGB7X.js} +2 -2
- package/dist/{chunk-2DJ2KNFG.js → chunk-2LZI5CAG.js} +133 -13
- package/dist/{chunk-OAO4GE4M.js → chunk-2TZWSW76.js} +2 -2
- package/dist/{chunk-OOJYHWRB.js → chunk-34475P3I.js} +2 -2
- package/dist/{chunk-4XUGQOHA.js → chunk-35MKKU5R.js} +4 -4
- package/dist/{chunk-LM5TQCJZ.js → chunk-3HZ5RWN2.js} +5 -5
- package/dist/{chunk-AGYYIBLL.js → chunk-3JLKSKD7.js} +2 -2
- package/dist/{chunk-KZWTDYJF.js → chunk-4JUF2NNX.js} +7 -7
- package/dist/{chunk-ZDOOVTXZ.js → chunk-4OC57DA6.js} +27 -4
- package/dist/chunk-5M54SPOL.js +926 -0
- package/dist/{chunk-STBPMHSX.js → chunk-7RXG6QRZ.js} +51 -11
- package/dist/{chunk-X6IAEBZR.js → chunk-A2GZF7DC.js} +5 -5
- package/dist/{chunk-A3CYT5EX.js → chunk-AD2SYQYC.js} +55 -2
- package/dist/chunk-AOCYTWAV.js +449 -0
- package/dist/chunk-BEY543CS.js +258 -0
- package/dist/{chunk-6N5PTWMY.js → chunk-BP4OYD6A.js} +32 -13
- package/dist/chunk-BPGS2WCQ.js +612 -0
- package/dist/{chunk-V6RTAOC2.js → chunk-BRXQQJFP.js} +8 -8
- package/dist/chunk-CFGTUFWB.js +67 -0
- package/dist/{chunk-CBCAPZAA.js → chunk-E25LMLRW.js} +2 -2
- package/dist/{chunk-DUYJ5IO6.js → chunk-EDRHSCIE.js} +4 -4
- package/dist/{chunk-XBXAASKX.js → chunk-EFADSJET.js} +2 -2
- package/dist/{chunk-M6SHUN7Q.js → chunk-FO5ZOVUY.js} +2 -2
- package/dist/chunk-FYYLNIL5.js +313 -0
- package/dist/{chunk-FNTMWMX5.js → chunk-HV5X7OR2.js} +14 -12
- package/dist/{chunk-TZTZS7QK.js → chunk-HXG4IURW.js} +5 -3
- package/dist/{chunk-5UFT4SUX.js → chunk-K6WL7QZT.js} +3 -3
- package/dist/chunk-K7VKOLQQ.js +15 -0
- package/dist/{chunk-BMEMKKIT.js → chunk-KOHPCX4K.js} +2 -2
- package/dist/{chunk-OC7FIQPC.js → chunk-KRPY7NTG.js} +10 -7
- package/dist/chunk-LL4KHSZI.js +22 -0
- package/dist/{chunk-DSELYM6W.js → chunk-MEQ45TQ4.js} +15 -9
- package/dist/{chunk-5UUP6MWO.js → chunk-MV3K5QF2.js} +5 -436
- package/dist/{chunk-6SGHMWE3.js → chunk-N4CZJQRK.js} +5 -5
- package/dist/{chunk-TZK7PACC.js → chunk-NILBFAPG.js} +14 -8
- package/dist/chunk-OZNBF4L3.js +23 -0
- package/dist/{verify-375KUB3Y.js → chunk-PCZJO5TI.js} +127 -42
- package/dist/chunk-QQK64KLB.js +1360 -0
- package/dist/{chunk-SRDMMSEP.js → chunk-QQL5RT5M.js} +979 -1619
- package/dist/{chunk-LZSJBIVT.js → chunk-QWU7ZBO7.js} +70 -720
- package/dist/{chunk-2TLUCQVG.js → chunk-RD5U66HV.js} +3 -3
- package/dist/{chunk-OKGUZO2U.js → chunk-SPULKLCF.js} +4 -3
- package/dist/{chunk-OQ33BKR3.js → chunk-TTNYS3EA.js} +3 -60
- package/dist/chunk-TW3WDMVS.js +677 -0
- package/dist/chunk-TZSKNMZG.js +434 -0
- package/dist/{chunk-7MNJORFF.js → chunk-UL3WSD3F.js} +6 -1
- package/dist/{chunk-PIWWS5BL.js → chunk-UZHIZC5S.js} +7 -7
- package/dist/{chunk-UFIIWP2H.js → chunk-VAWWTKDP.js} +8 -8
- package/dist/{chunk-COU2UHX6.js → chunk-VEZEGCGW.js} +170 -2
- package/dist/{chunk-LW6DSM3M.js → chunk-VMNQ6OZA.js} +98 -202
- package/dist/chunk-VSNATDE6.js +122 -0
- package/dist/chunk-W6GROXXM.js +69 -0
- package/dist/chunk-WPQLXFOZ.js +375 -0
- package/dist/{chunk-ORBHGJC5.js → chunk-WR67VIZY.js} +3 -3
- package/dist/{chunk-PAJK6MAQ.js → chunk-X6COSD2O.js} +5 -5
- package/dist/chunk-ZGVHUX3M.js +66 -0
- package/dist/{chunk-LWLEKMDQ.js → chunk-ZYKPLLNQ.js} +510 -547
- package/dist/cli/index.js +27 -23
- package/dist/{clio-JOU4FXVA.js → clio-J5JIOIDS.js} +7 -6
- package/dist/{code-nav-7AX6FYE6.js → code-nav-AXCXSBHX.js} +5 -3
- package/dist/{config-XCDVKR23.js → config-OEBMIN2U.js} +37 -27
- package/dist/{configure-4GAP54ZW.js → configure-PUQOSIXQ.js} +16 -13
- package/dist/{context-5VKGUVJJ.js → context-EKDCKUUZ.js} +82 -7
- package/dist/{context-4UOGGLQ5.js → context-MGSE4Z2T.js} +33 -23
- package/dist/{context-77FM5DV5.js → context-URSXPBCK.js} +17 -9
- package/dist/{context-clear-XXJRLCJJ.js → context-clear-KDAJRNUK.js} +33 -23
- package/dist/context-working-set-SBKMPPI2.js +1552 -0
- package/dist/{dispatch-runner-QPRDDBDX.js → dispatch-runner-MSWN72NK.js} +43 -29
- package/dist/{doctor-HR46URBJ.js → doctor-7BSE27PJ.js} +10 -10
- package/dist/{eval-XSSNATB4.js → eval-IZGDOO4H.js} +9 -8
- package/dist/{evidence-6HG2PY2B.js → evidence-SR7WXB5B.js} +51 -23
- package/dist/{evolve-K7YU3NCY.js → evolve-K7VE2CBX.js} +30 -20
- package/dist/{fleet-VY3HHKN6.js → fleet-7XMJNQNF.js} +48 -38
- package/dist/{fleet-preflight-DDN536IT.js → fleet-preflight-AQNAH644.js} +3 -3
- package/dist/{init-JYGXI3FK.js → init-JGNPAYXT.js} +41 -31
- package/dist/{memory-WFZMGYHX.js → memory-4ALKDJ4Q.js} +32 -22
- package/dist/{models-I5QWSEOM.js → models-ZMMLFJNN.js} +22 -19
- package/dist/{monitor-GE4ID3IA.js → monitor-2F3T5KHP.js} +55 -43
- package/dist/{orchestrator-EM5MC3HM.js → orchestrator-ORHT43JB.js} +572 -388
- package/dist/{reset-L2FQEE3E.js → reset-NXGTYNUO.js} +4 -3
- package/dist/{run-ZU3QMZPZ.js → run-RF4WJGMT.js} +51 -41
- package/dist/{share-S5BZQC5I.js → share-UT3W6E4M.js} +5 -4
- package/dist/{skills-X5VXCRNQ.js → skills-PSACKC5Q.js} +2 -2
- package/dist/{skills-eval-WKIHWTHR.js → skills-eval-WJSI55RZ.js} +34 -24
- package/dist/{targets-SNCPI2NR.js → targets-PIIRAOYS.js} +23 -20
- package/dist/{terminal-lease-BNAHVHBS.js → terminal-lease-ULWXWNVY.js} +4 -3
- package/dist/{upgrade-JQHHPQ4K.js → upgrade-346TZ6AV.js} +18 -17
- package/dist/{usage-OR4O5SMZ.js → usage-6KKXR32N.js} +34 -24
- package/dist/verifiers-4UUM6TEE.js +1214 -0
- package/dist/verify-X5HDROLA.js +25 -0
- package/dist/{wiki-generate-UEXP2ARI.js → wiki-generate-7STOCIFZ.js} +42 -31
- package/dist/worker/entry.js +33 -24
- package/docs/README.md +7 -6
- package/docs/acp.md +1 -1
- package/docs/alcf-provider.md +1 -1
- package/docs/architecture.md +2 -2
- package/docs/artifact-versions.md +1 -1
- package/docs/built-in-agents.md +1 -1
- package/docs/capacity-and-scheduling.md +1 -1
- package/docs/commands-and-modes.md +53 -21
- package/docs/config-knobs-audit.md +1 -2
- package/docs/configuration-and-targets.md +15 -1
- package/docs/context-engine.md +64 -12
- package/docs/context-working-set.md +194 -0
- package/docs/development-pipeline.md +1 -1
- package/docs/documentation-coverage.md +5 -5
- package/docs/documentation-guide.md +6 -5
- package/docs/environment-variables.md +2 -1
- package/docs/eval-runner.md +1 -1
- package/docs/evals-internal.md +14 -1
- package/docs/evidence-and-memory.md +74 -2
- package/docs/evolution.md +1 -1
- package/docs/exit-codes-and-output.md +1 -1
- package/docs/extensions-and-sharing.md +2 -2
- package/docs/fleet-dispatch.md +22 -7
- package/docs/glossary.md +21 -1
- package/docs/installation-and-lifecycle.md +2 -2
- package/docs/middleware-and-components.md +1 -1
- package/docs/model-catalog.md +7 -9
- package/docs/observability.md +4 -4
- package/docs/proactive-memory.md +1 -1
- package/docs/prompt-envelope-and-tools.md +4 -4
- package/docs/provider-adapter-cookbook.md +1 -1
- package/docs/release-cut-checklist.md +31 -31
- package/docs/safety-model.md +23 -4
- package/docs/scientific-validation.md +21 -3
- package/docs/session-lifecycle.md +3 -3
- package/docs/skills-marketplace.md +1 -1
- package/docs/tool-usage.md +79 -12
- package/docs/trace-store.md +1 -1
- package/docs/troubleshooting.md +1 -1
- package/docs/tui-design.md +1 -1
- package/docs/worker-dispatch-mechanics.md +11 -1
- package/package.json +8 -11
- package/skills/meta/clio-test/SKILL.md +20 -17
- package/skills/meta/clio-test/evals.md +3 -3
- package/skills/meta/clio-test/references/harness.md +35 -6
- package/skills/meta/clio-test/references/test-map.md +20 -10
- package/skills/registry.yaml +2 -2
- package/skills/skill-marketplace.json +1 -1
- package/src/cli/context-working-set.ts +513 -0
- package/src/cli/context.ts +8 -0
- package/src/cli/evidence.ts +20 -2
- package/src/cli/index.ts +4 -0
- package/src/cli/verifiers.ts +325 -0
- package/src/core/bash-exec.ts +39 -14
- package/src/core/bus-events.ts +19 -4
- package/src/core/config.ts +54 -0
- package/src/core/defaults.ts +50 -3
- package/src/core/verification-scripts.ts +6 -0
- package/src/domains/agents/builtins/verifier.md +3 -0
- package/src/domains/config/classify.ts +1 -0
- package/src/domains/context/working-set/contract.ts +161 -0
- package/src/domains/context/working-set/defaults.ts +28 -0
- package/src/domains/context/working-set/engine.ts +203 -0
- package/src/domains/context/working-set/fold.ts +62 -0
- package/src/domains/context/working-set/horizon.ts +38 -0
- package/src/domains/context/working-set/marker.ts +103 -0
- package/src/domains/context/working-set/path-index.ts +436 -0
- package/src/domains/context/working-set/payload.ts +152 -0
- package/src/domains/context/working-set/policies/age-horizon.ts +55 -0
- package/src/domains/context/working-set/policies/index.ts +21 -0
- package/src/domains/context/working-set/policies/structural.ts +160 -0
- package/src/domains/context/working-set/project.ts +132 -0
- package/src/domains/context/working-set/protect.ts +109 -0
- package/src/domains/context/working-set/recall.ts +177 -0
- package/src/domains/context/working-set/replay/controls.ts +112 -0
- package/src/domains/context/working-set/replay/load-clio.ts +199 -0
- package/src/domains/context/working-set/replay/metrics.ts +185 -0
- package/src/domains/context/working-set/replay/reference-graph.ts +79 -0
- package/src/domains/context/working-set/replay/report.ts +139 -0
- package/src/domains/context/working-set/replay/runner.ts +325 -0
- package/src/domains/context/working-set/replay/synthetic.ts +422 -0
- package/src/domains/context/working-set/replay/trace.ts +21 -0
- package/src/domains/context/working-set/visible.ts +54 -0
- package/src/domains/evidence/build.ts +112 -45
- package/src/domains/evidence/eval.ts +24 -7
- package/src/domains/evidence/index.ts +53 -0
- package/src/domains/evidence/ordering.ts +12 -0
- package/src/domains/evidence/run-trust.ts +221 -0
- package/src/domains/evidence/store.ts +46 -6
- package/src/domains/evidence/trust-status.ts +854 -0
- package/src/domains/evidence/types.ts +26 -0
- package/src/domains/middleware/memory-intervention.ts +3 -0
- package/src/domains/middleware/stalled-turn.ts +165 -4
- package/src/domains/safety/autonomy.ts +1 -1
- package/src/domains/safety/default-path-policy.ts +8 -0
- package/src/domains/safety/finish-contract.ts +4 -3
- package/src/domains/safety/policy-engine.ts +48 -6
- package/src/domains/session/compaction/compact.ts +23 -1
- package/src/domains/session/compaction/cut-point.ts +2 -0
- package/src/domains/session/compaction/tokens.ts +16 -1
- package/src/domains/session/context-ledger.ts +2 -0
- package/src/domains/session/entries.ts +107 -1
- package/src/domains/session/manager.ts +9 -2
- package/src/domains/session/migrations/index.ts +22 -3
- package/src/engine/acp/server.ts +3 -0
- package/src/engine/agent.ts +18 -1
- package/src/engine/session.ts +9 -3
- package/src/entry/orchestrator.ts +16 -4
- package/src/interactive/chat-loop-messages.ts +18 -6
- package/src/interactive/chat-panel.ts +17 -1
- package/src/interactive/chat-renderer.ts +30 -21
- package/src/interactive/context-meter.ts +10 -0
- package/src/interactive/context-overlay.ts +81 -6
- package/src/interactive/context-recall-command.ts +110 -0
- package/src/interactive/interactive-slash-runtime.ts +37 -1
- package/src/interactive/model-session-replay.ts +21 -0
- package/src/interactive/overlay-general-openers.ts +6 -0
- package/src/interactive/overlay-session-lifecycle.ts +8 -4
- package/src/interactive/renderers/tool-execution.ts +18 -2
- package/src/interactive/session-transcript.ts +2 -2
- package/src/interactive/slash-commands.ts +29 -2
- package/src/interactive/turn-context.ts +238 -88
- package/src/interactive/turn-middleware.ts +6 -6
- package/src/tools/agent-tools.ts +11 -4
- package/src/tools/bash.ts +144 -82
- package/src/tools/builtin-tool-catalog.ts +11 -5
- package/src/tools/context/index.ts +105 -3
- package/src/tools/context/surface.ts +3 -2
- package/src/tools/core-bootstrap.ts +21 -0
- package/src/tools/dispatch-runner.ts +9 -7
- package/src/tools/monitor.ts +28 -20
- package/src/tools/registry.ts +59 -7
- package/src/tools/result-disposition.ts +550 -0
- package/src/tools/result-shaping.ts +262 -19
- package/src/tools/safe-exec.ts +2 -0
- package/src/tools/verify/authoring.ts +1119 -0
- package/src/tools/verify/catalog.ts +346 -0
- package/src/tools/verify/index.ts +13 -3
- package/src/tools/verify/scripts.ts +135 -37
- package/src/tools/verify/surface.ts +9 -5
- package/src/tools/worker-evidence.ts +35 -12
- package/dist/chunk-J7CWMCQD.js +0 -255
- package/dist/chunk-T6YILFSB.js +0 -80
- package/dist/chunk-VAKQQHWR.js +0 -434
- package/dist/chunk-VPAYEGVX.js +0 -184
|
@@ -1,8 +1,16 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
1
2
|
import { mkdirSync, writeFileSync } from "node:fs";
|
|
2
3
|
import { isAbsolute, join, relative, resolve } from "node:path";
|
|
3
4
|
import { clioStateDir } from "../core/xdg.js";
|
|
4
5
|
import type { ToolInvokeOptions, ToolResult, ToolResultDetails, ToolSpec } from "./registry.js";
|
|
5
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
normalizeToolResultDisposition,
|
|
8
|
+
projectToolResultContext,
|
|
9
|
+
type ToolResultDisposition,
|
|
10
|
+
type ToolResultDispositionFallback,
|
|
11
|
+
type ToolResultDispositionMetadata,
|
|
12
|
+
} from "./result-disposition.js";
|
|
13
|
+
import { DEFAULT_MAX_BYTES, DEFAULT_MAX_LINES, formatSize, truncateTail } from "./truncate.js";
|
|
6
14
|
import { byteLength, truncateUtf8 } from "./truncate-utf8.js";
|
|
7
15
|
|
|
8
16
|
// Backstop for tools without an explicit resultSizePolicy. Sits above the
|
|
@@ -12,6 +20,7 @@ import { byteLength, truncateUtf8 } from "./truncate-utf8.js";
|
|
|
12
20
|
export const DEFAULT_TOOL_RESULT_MAX_BYTES = DEFAULT_MAX_BYTES + 2 * 1024;
|
|
13
21
|
const RESULT_TRUNCATION_MARKER = "\n[tool result truncated]";
|
|
14
22
|
const RESULT_OFFLOAD_MAX_BYTES = 10 * 1024 * 1024;
|
|
23
|
+
const TAIL_NOTICE_RESERVE_BYTES = 512;
|
|
15
24
|
|
|
16
25
|
type ToolResultShapeContext = Pick<ToolInvokeOptions, "sessionId" | "toolCallId">;
|
|
17
26
|
|
|
@@ -33,6 +42,13 @@ function followUpHint(spec: ToolSpec): string {
|
|
|
33
42
|
);
|
|
34
43
|
}
|
|
35
44
|
|
|
45
|
+
function offloadMaxBytesFor(spec: ToolSpec): number {
|
|
46
|
+
const configured = spec.metadata?.resultSizePolicy?.offloadMaxBytes;
|
|
47
|
+
return typeof configured === "number" && Number.isFinite(configured) && configured > 0
|
|
48
|
+
? Math.floor(configured)
|
|
49
|
+
: RESULT_OFFLOAD_MAX_BYTES;
|
|
50
|
+
}
|
|
51
|
+
|
|
36
52
|
function detailsRecord(details: ToolResultDetails | undefined, key: string): Record<string, unknown> | null {
|
|
37
53
|
const candidate = details?.[key];
|
|
38
54
|
return candidate !== null && typeof candidate === "object" && !Array.isArray(candidate)
|
|
@@ -41,10 +57,10 @@ function detailsRecord(details: ToolResultDetails | undefined, key: string): Rec
|
|
|
41
57
|
}
|
|
42
58
|
|
|
43
59
|
/**
|
|
44
|
-
* A result that already spilled its full text to scratch (
|
|
45
|
-
*
|
|
46
|
-
* `observation.offloadPath`) is left alone;
|
|
47
|
-
*
|
|
60
|
+
* A result that already spilled its full text to scratch (the canonical
|
|
61
|
+
* tail-biased presentation path via `resultSize.offloadPath`, or an OBSERVE
|
|
62
|
+
* tool's envelope via `observation.offloadPath`) is left alone; a second pass
|
|
63
|
+
* would only cut the deliberate continuation notice.
|
|
48
64
|
*/
|
|
49
65
|
function existingOffloadPath(details: ToolResultDetails | undefined): string | null {
|
|
50
66
|
const fromResultSize = detailsRecord(details, "resultSize")?.offloadPath;
|
|
@@ -53,6 +69,13 @@ function existingOffloadPath(details: ToolResultDetails | undefined): string | n
|
|
|
53
69
|
return typeof fromObservation === "string" && fromObservation.length > 0 ? fromObservation : null;
|
|
54
70
|
}
|
|
55
71
|
|
|
72
|
+
function existingDisposition(details: ToolResultDetails | undefined): ToolResultDispositionMetadata | null {
|
|
73
|
+
const disposition = detailsRecord(details, "resultDisposition");
|
|
74
|
+
return disposition?.version === 1 && disposition.applications === 1
|
|
75
|
+
? (disposition as unknown as ToolResultDispositionMetadata)
|
|
76
|
+
: null;
|
|
77
|
+
}
|
|
78
|
+
|
|
56
79
|
function safePathSegment(value: string): string {
|
|
57
80
|
const safe = value
|
|
58
81
|
.trim()
|
|
@@ -61,10 +84,6 @@ function safePathSegment(value: string): string {
|
|
|
61
84
|
return safe.length > 0 ? safe : "unnamed";
|
|
62
85
|
}
|
|
63
86
|
|
|
64
|
-
function timestampSegment(): string {
|
|
65
|
-
return new Date().toISOString().replace(/[^A-Za-z0-9._-]/g, "_");
|
|
66
|
-
}
|
|
67
|
-
|
|
68
87
|
function offloadBody(text: string, bytes: number, maxBytes: number): string {
|
|
69
88
|
if (bytes <= maxBytes) return text;
|
|
70
89
|
const notice = `\n[clio-coder scratch output truncated at ${maxBytes} bytes; original size ${bytes} bytes]`;
|
|
@@ -74,22 +93,28 @@ function offloadBody(text: string, bytes: number, maxBytes: number): string {
|
|
|
74
93
|
|
|
75
94
|
/**
|
|
76
95
|
* Persist the full text of a tool result to a per-session scratch file and
|
|
77
|
-
* return its path (or null if the write fails).
|
|
78
|
-
*
|
|
79
|
-
*
|
|
80
|
-
*
|
|
96
|
+
* return its path (or null if the write fails). Self-shaping callers use this
|
|
97
|
+
* to spill complete output before truncating and set
|
|
98
|
+
* `details.resultSize.offloadPath`; canonical disposition shaping also uses it
|
|
99
|
+
* once when presentation or model context omits captured content.
|
|
100
|
+
*
|
|
101
|
+
* The file is named by the sha256 of the captured text, so the path is a pure
|
|
102
|
+
* function of the bytes: identical captures share one file and the
|
|
103
|
+
* `retrieve=` header, the eviction marker, and the resume transcript carry the
|
|
104
|
+
* same bytes every time, which is what keeps the prompt-cache prefix stable
|
|
105
|
+
* across runs. Pass `digest` when the caller already hashed the text.
|
|
81
106
|
*/
|
|
82
107
|
export function writeToolOffload(
|
|
83
108
|
text: string,
|
|
84
109
|
context: ToolResultShapeContext | undefined,
|
|
85
110
|
maxBytes: number = RESULT_OFFLOAD_MAX_BYTES,
|
|
111
|
+
digest: string = createHash("sha256").update(text).digest("hex"),
|
|
86
112
|
): string | null {
|
|
87
113
|
try {
|
|
88
114
|
const bytes = byteLength(text);
|
|
89
115
|
const sessionId = safePathSegment(context?.sessionId ?? "no-session");
|
|
90
|
-
const callId = safePathSegment(context?.toolCallId ?? timestampSegment());
|
|
91
116
|
const dir = join(clioStateDir(), "scratch", sessionId);
|
|
92
|
-
const path = join(dir, `${
|
|
117
|
+
const path = join(dir, `${digest}.txt`);
|
|
93
118
|
mkdirSync(dir, { recursive: true });
|
|
94
119
|
writeFileSync(path, offloadBody(text, bytes, maxBytes), "utf8");
|
|
95
120
|
return path;
|
|
@@ -145,9 +170,10 @@ function shapeJsonOverflow(
|
|
|
145
170
|
bytes: number,
|
|
146
171
|
maxBytes: number,
|
|
147
172
|
context: ToolResultShapeContext | undefined,
|
|
173
|
+
offloadMaxBytes: number,
|
|
148
174
|
): ToolResult {
|
|
149
175
|
const observation = detailsRecord(result.details, "observation");
|
|
150
|
-
const offloadPath = writeToolOffload(text, context);
|
|
176
|
+
const offloadPath = writeToolOffload(text, context, offloadMaxBytes);
|
|
151
177
|
const next =
|
|
152
178
|
typeof observation?.next === "string" && observation.next.length > 0 ? observation.next : followUpHint(spec);
|
|
153
179
|
const stub = JSON.stringify({
|
|
@@ -170,16 +196,64 @@ function shapeJsonOverflow(
|
|
|
170
196
|
return { ...result, message: stub, details: mergeDetails(result.details, resultSize) };
|
|
171
197
|
}
|
|
172
198
|
|
|
173
|
-
|
|
199
|
+
function shapeTailOverflow(
|
|
200
|
+
spec: ToolSpec,
|
|
201
|
+
result: ToolResult,
|
|
202
|
+
text: string,
|
|
203
|
+
bytes: number,
|
|
204
|
+
maxBytes: number,
|
|
205
|
+
context: ToolResultShapeContext | undefined,
|
|
206
|
+
offloadMaxBytes: number,
|
|
207
|
+
): ToolResult {
|
|
208
|
+
const truncation = truncateTail(text, {
|
|
209
|
+
maxLines: DEFAULT_MAX_LINES,
|
|
210
|
+
maxBytes: Math.max(1, maxBytes - TAIL_NOTICE_RESERVE_BYTES),
|
|
211
|
+
});
|
|
212
|
+
const offloadPath = writeToolOffload(text, context, offloadMaxBytes);
|
|
213
|
+
const startLine = truncation.totalLines - truncation.outputLines + 1;
|
|
214
|
+
const scope = truncation.lastLinePartial
|
|
215
|
+
? `last ${formatSize(truncation.outputBytes)} of line ${truncation.totalLines} (line is large)`
|
|
216
|
+
: `lines ${startLine}-${truncation.totalLines} of ${truncation.totalLines}`;
|
|
217
|
+
const location =
|
|
218
|
+
offloadPath === null
|
|
219
|
+
? ""
|
|
220
|
+
: ` Full output saved to ${offloadPath}; ${OFFLOAD_POINTER_NOTE}; read it with offset/limit.`;
|
|
221
|
+
const note = `[Output tail-truncated: showing ${scope} (${formatSize(maxBytes)} display limit).${location}]`;
|
|
222
|
+
const resultSize = {
|
|
223
|
+
bytes,
|
|
224
|
+
shownBytes: truncation.outputBytes,
|
|
225
|
+
maxBytes,
|
|
226
|
+
truncated: true,
|
|
227
|
+
policy: "tail",
|
|
228
|
+
followUpHint: followUpHint(spec),
|
|
229
|
+
...(offloadPath === null ? {} : { offloadPath }),
|
|
230
|
+
};
|
|
231
|
+
const displayed = `${truncation.content}\n\n${note}`;
|
|
232
|
+
if (result.kind === "ok") return { ...result, output: displayed, details: mergeDetails(result.details, resultSize) };
|
|
233
|
+
return { ...result, message: displayed, details: mergeDetails(result.details, resultSize) };
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
function shapeLegacyToolResult(
|
|
237
|
+
spec: ToolSpec,
|
|
238
|
+
result: ToolResult,
|
|
239
|
+
context?: ToolResultShapeContext,
|
|
240
|
+
overflow: "head" | "tail" = "head",
|
|
241
|
+
): ToolResult {
|
|
174
242
|
if (existingOffloadPath(result.details) !== null) return result;
|
|
175
243
|
const maxBytes = maxBytesFor(spec);
|
|
244
|
+
const offloadMaxBytes = offloadMaxBytesFor(spec);
|
|
176
245
|
const text = result.kind === "ok" ? result.output : result.message;
|
|
177
246
|
const bytes = byteLength(text);
|
|
178
247
|
if (bytes <= maxBytes) return result;
|
|
179
248
|
const observation = detailsRecord(result.details, "observation");
|
|
180
|
-
if (observation?.format === "json")
|
|
249
|
+
if (observation?.format === "json") {
|
|
250
|
+
return shapeJsonOverflow(spec, result, text, bytes, maxBytes, context, offloadMaxBytes);
|
|
251
|
+
}
|
|
252
|
+
if (overflow === "tail") {
|
|
253
|
+
return shapeTailOverflow(spec, result, text, bytes, maxBytes, context, offloadMaxBytes);
|
|
254
|
+
}
|
|
181
255
|
const truncated = truncateUtf8(text, maxBytes, RESULT_TRUNCATION_MARKER);
|
|
182
|
-
const offloadPath = writeToolOffload(text, context);
|
|
256
|
+
const offloadPath = writeToolOffload(text, context, offloadMaxBytes);
|
|
183
257
|
const resultSize = {
|
|
184
258
|
bytes,
|
|
185
259
|
shownBytes: byteLength(truncated),
|
|
@@ -202,3 +276,172 @@ export function shapeToolResult(spec: ToolSpec, result: ToolResult, context?: To
|
|
|
202
276
|
details: mergeDetails(result.details, resultSize),
|
|
203
277
|
};
|
|
204
278
|
}
|
|
279
|
+
|
|
280
|
+
function specWithDisplayCap(spec: ToolSpec, maxBytes: number): ToolSpec {
|
|
281
|
+
const metadata = spec.metadata;
|
|
282
|
+
if (metadata === undefined) return spec;
|
|
283
|
+
return {
|
|
284
|
+
...spec,
|
|
285
|
+
metadata: {
|
|
286
|
+
...metadata,
|
|
287
|
+
resultSizePolicy: { ...metadata.resultSizePolicy, maxBytes },
|
|
288
|
+
},
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
function resultText(result: ToolResult): string {
|
|
293
|
+
return result.kind === "ok" ? result.output : result.message;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* The operator-facing trace of a disposition that was narrowed rather than
|
|
298
|
+
* requested. Without it a throwing resolver degrades every call of that tool
|
|
299
|
+
* to metadata-only with nothing on the transcript row to say so.
|
|
300
|
+
*/
|
|
301
|
+
function withFallbackNotice(result: ToolResult, fallback: ToolResultDispositionFallback): ToolResult {
|
|
302
|
+
const note = `\n\n[result disposition fell back to metadata-only: ${fallback.reason} (${fallback.message}); the model received facts and retrieval only]`;
|
|
303
|
+
return result.kind === "ok"
|
|
304
|
+
? { ...result, output: `${result.output}${note}` }
|
|
305
|
+
: { ...result, message: `${result.message}${note}` };
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
function numberField(record: Record<string, unknown> | null, key: string): number | null {
|
|
309
|
+
const value = record?.[key];
|
|
310
|
+
return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : null;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
function capturedBytesFor(result: ToolResult, text: string): number {
|
|
314
|
+
const declared = numberField(detailsRecord(result.details, "resultSize"), "bytes");
|
|
315
|
+
return Math.max(byteLength(text), declared ?? 0);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
function resultWasTruncated(result: ToolResult): boolean {
|
|
319
|
+
return detailsRecord(result.details, "resultSize")?.truncated === true;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
function withOffloadMetadata(
|
|
323
|
+
result: ToolResult,
|
|
324
|
+
offloadPath: string,
|
|
325
|
+
capturedBytes: number,
|
|
326
|
+
displayedBytes: number,
|
|
327
|
+
maxBytes: number,
|
|
328
|
+
hint: string,
|
|
329
|
+
): ToolResult {
|
|
330
|
+
const current = detailsRecord(result.details, "resultSize") ?? {};
|
|
331
|
+
const resultSize = {
|
|
332
|
+
bytes: numberField(current, "bytes") ?? capturedBytes,
|
|
333
|
+
shownBytes: numberField(current, "shownBytes") ?? displayedBytes,
|
|
334
|
+
maxBytes: numberField(current, "maxBytes") ?? maxBytes,
|
|
335
|
+
truncated: current.truncated === true,
|
|
336
|
+
policy: typeof current.policy === "string" ? current.policy : "disposition",
|
|
337
|
+
followUpHint: typeof current.followUpHint === "string" ? current.followUpHint : hint,
|
|
338
|
+
...current,
|
|
339
|
+
offloadPath,
|
|
340
|
+
};
|
|
341
|
+
return { ...result, details: mergeDetails(result.details, resultSize) };
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* Apply legacy shaping or one declared canonical disposition. Registry calls
|
|
346
|
+
* this once, after middleware annotations have produced the terminal result.
|
|
347
|
+
*/
|
|
348
|
+
export function shapeToolResult(
|
|
349
|
+
spec: ToolSpec,
|
|
350
|
+
result: ToolResult,
|
|
351
|
+
context?: ToolResultShapeContext,
|
|
352
|
+
requestedDisposition?: ToolResultDisposition,
|
|
353
|
+
): ToolResult {
|
|
354
|
+
if (existingDisposition(result.details) !== null) return result;
|
|
355
|
+
const hardMaxBytes = maxBytesFor(spec);
|
|
356
|
+
const disposition = normalizeToolResultDisposition(spec, hardMaxBytes, requestedDisposition);
|
|
357
|
+
if (disposition === null) return shapeLegacyToolResult(spec, result, context);
|
|
358
|
+
|
|
359
|
+
const capturedText = resultText(result);
|
|
360
|
+
const capturedBytes = capturedBytesFor(result, capturedText);
|
|
361
|
+
let displayed = shapeLegacyToolResult(
|
|
362
|
+
specWithDisplayCap(spec, disposition.presentation.maxBytes),
|
|
363
|
+
result,
|
|
364
|
+
context,
|
|
365
|
+
disposition.presentation.overflow,
|
|
366
|
+
);
|
|
367
|
+
if (disposition.fallback !== undefined) displayed = withFallbackNotice(displayed, disposition.fallback);
|
|
368
|
+
let displayedText = resultText(displayed);
|
|
369
|
+
let displayedBytes = byteLength(displayedText);
|
|
370
|
+
let offloadPath = existingOffloadPath(displayed.details);
|
|
371
|
+
const projectionInput = {
|
|
372
|
+
text: capturedText,
|
|
373
|
+
kind: result.kind,
|
|
374
|
+
details: displayed.details,
|
|
375
|
+
disposition,
|
|
376
|
+
capturedBytes,
|
|
377
|
+
displayedBytes,
|
|
378
|
+
offloadPath,
|
|
379
|
+
followUpHint: followUpHint(spec),
|
|
380
|
+
};
|
|
381
|
+
// Project first, then decide on retrieval. A projection that already carries
|
|
382
|
+
// every captured byte (a short `summary`, a `full` inside budget) needs no
|
|
383
|
+
// scratch artifact and must not claim truncation; anything that genuinely
|
|
384
|
+
// omits content earns one offload and the honest omission facts with it.
|
|
385
|
+
let projection = projectToolResultContext(projectionInput);
|
|
386
|
+
if (offloadPath === null && projection.truncated) {
|
|
387
|
+
offloadPath = writeToolOffload(
|
|
388
|
+
capturedText,
|
|
389
|
+
context,
|
|
390
|
+
offloadMaxBytesFor(spec),
|
|
391
|
+
projection.summaryProvenance?.sourceSha256,
|
|
392
|
+
);
|
|
393
|
+
if (offloadPath !== null) {
|
|
394
|
+
displayed = withOffloadMetadata(
|
|
395
|
+
displayed,
|
|
396
|
+
offloadPath,
|
|
397
|
+
capturedBytes,
|
|
398
|
+
displayedBytes,
|
|
399
|
+
disposition.presentation.maxBytes,
|
|
400
|
+
followUpHint(spec),
|
|
401
|
+
);
|
|
402
|
+
displayedText = resultText(displayed);
|
|
403
|
+
displayedBytes = byteLength(displayedText);
|
|
404
|
+
projection = projectToolResultContext({
|
|
405
|
+
...projectionInput,
|
|
406
|
+
details: displayed.details,
|
|
407
|
+
displayedBytes,
|
|
408
|
+
offloadPath,
|
|
409
|
+
});
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
const metadata: ToolResultDispositionMetadata = {
|
|
413
|
+
version: 1,
|
|
414
|
+
applications: 1,
|
|
415
|
+
presentation: { ...disposition.presentation, content: displayedText },
|
|
416
|
+
context: {
|
|
417
|
+
requestedMode: disposition.context.mode,
|
|
418
|
+
appliedMode: projection.appliedMode,
|
|
419
|
+
maxBytes: disposition.context.maxBytes,
|
|
420
|
+
...(disposition.context.mode === "bounded" && disposition.context.excerpt !== undefined
|
|
421
|
+
? { excerpt: disposition.context.excerpt }
|
|
422
|
+
: {}),
|
|
423
|
+
...(disposition.context.mode === "full" && disposition.context.downgradeExcerpt !== undefined
|
|
424
|
+
? { excerpt: disposition.context.downgradeExcerpt }
|
|
425
|
+
: {}),
|
|
426
|
+
...(disposition.context.mode === "summary" && disposition.context.strategy !== undefined
|
|
427
|
+
? { summaryStrategy: disposition.context.strategy }
|
|
428
|
+
: {}),
|
|
429
|
+
},
|
|
430
|
+
capturedBytes,
|
|
431
|
+
displayedBytes,
|
|
432
|
+
contextBytes: byteLength(projection.text),
|
|
433
|
+
presentationTruncated: resultWasTruncated(displayed),
|
|
434
|
+
contextTruncated: projection.truncated,
|
|
435
|
+
...(projection.downgrade === undefined ? {} : { downgrade: projection.downgrade }),
|
|
436
|
+
...(offloadPath === null ? {} : { offloadPath }),
|
|
437
|
+
retrieval:
|
|
438
|
+
offloadPath === null ? followUpHint(spec) : `read ${offloadPath} with offset and limit to retrieve omitted content`,
|
|
439
|
+
...(projection.summaryProvenance === undefined ? {} : { summaryProvenance: projection.summaryProvenance }),
|
|
440
|
+
...(disposition.fallback === undefined ? {} : { fallback: disposition.fallback }),
|
|
441
|
+
};
|
|
442
|
+
return {
|
|
443
|
+
...displayed,
|
|
444
|
+
details: { ...(displayed.details ?? {}), resultDisposition: metadata },
|
|
445
|
+
modelContext: projection.text,
|
|
446
|
+
};
|
|
447
|
+
}
|
package/src/tools/safe-exec.ts
CHANGED
|
@@ -35,9 +35,11 @@ export function maxOutputArg(args: Record<string, unknown>): number {
|
|
|
35
35
|
function resultDetails(result: SafeCommandResult): ToolResultDetails {
|
|
36
36
|
return {
|
|
37
37
|
command: [result.file, ...result.args].join(" "),
|
|
38
|
+
argv: [result.file, ...result.args],
|
|
38
39
|
cwd: result.cwd,
|
|
39
40
|
exitCode: result.exitCode,
|
|
40
41
|
durationMs: result.durationMs,
|
|
42
|
+
aborted: result.aborted,
|
|
41
43
|
timedOut: result.timedOut,
|
|
42
44
|
outputCapped: result.outputCapped,
|
|
43
45
|
};
|