@iowarp/clio-coder 0.3.2 → 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 +269 -458
- package/CONTRIBUTING.md +1 -1
- package/README.md +3 -3
- package/dist/{acp-BIYHVZIM.js → acp-S5R4RR5B.js} +7 -6
- package/dist/{agents-YT6SSRIT.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-WMSVI4G2.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-WVO7V2QY.js → chunk-35MKKU5R.js} +4 -4
- package/dist/{chunk-LBNRH5WM.js → chunk-3HZ5RWN2.js} +5 -5
- package/dist/{chunk-AGYYIBLL.js → chunk-3JLKSKD7.js} +2 -2
- package/dist/{chunk-MBS4V7ZP.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-77VKQEHF.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-J5HN4RYU.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-G2DE3C7R.js → chunk-EDRHSCIE.js} +4 -4
- package/dist/{chunk-4KLWL3UC.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-EPVUXGXG.js → chunk-HV5X7OR2.js} +14 -12
- package/dist/{chunk-TZTZS7QK.js → chunk-HXG4IURW.js} +5 -3
- package/dist/{chunk-IGLFWIYI.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-V4RXGQ5Q.js → chunk-KRPY7NTG.js} +10 -7
- package/dist/chunk-LL4KHSZI.js +22 -0
- package/dist/{chunk-KJ5LWLOE.js → chunk-MEQ45TQ4.js} +15 -9
- package/dist/{chunk-5UUP6MWO.js → chunk-MV3K5QF2.js} +5 -436
- package/dist/{chunk-AO4RKG4M.js → chunk-N4CZJQRK.js} +5 -5
- package/dist/{chunk-ARBGF5F7.js → chunk-NILBFAPG.js} +14 -8
- package/dist/chunk-OZNBF4L3.js +23 -0
- package/dist/{verify-G6V4D2G7.js → chunk-PCZJO5TI.js} +127 -42
- package/dist/chunk-QQK64KLB.js +1360 -0
- package/dist/{chunk-6EJV5X2W.js → chunk-QQL5RT5M.js} +979 -1619
- package/dist/{chunk-LZSJBIVT.js → chunk-QWU7ZBO7.js} +70 -720
- package/dist/{chunk-2EHAIA3X.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-7EYHLWU7.js → chunk-UZHIZC5S.js} +7 -7
- package/dist/{chunk-QTYWRVRA.js → chunk-VAWWTKDP.js} +8 -8
- package/dist/{chunk-X75S7HFS.js → chunk-VEZEGCGW.js} +214 -20
- package/dist/{chunk-OHHN2SO4.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-3ZXDFGR5.js → chunk-X6COSD2O.js} +5 -5
- package/dist/chunk-ZGVHUX3M.js +66 -0
- package/dist/{chunk-MAW544W2.js → chunk-ZWMF7253.js} +4 -4
- package/dist/{chunk-MQSRRFWA.js → chunk-ZYKPLLNQ.js} +563 -546
- package/dist/cli/index.js +27 -23
- package/dist/{clio-4LY5K2AC.js → clio-J5JIOIDS.js} +7 -6
- package/dist/{code-nav-7AX6FYE6.js → code-nav-AXCXSBHX.js} +5 -3
- package/dist/{config-GTLUW2PR.js → config-OEBMIN2U.js} +37 -27
- package/dist/{configure-R6A64DHX.js → configure-PUQOSIXQ.js} +16 -13
- package/dist/{context-5VKGUVJJ.js → context-EKDCKUUZ.js} +82 -7
- package/dist/{context-RW5HC47S.js → context-MGSE4Z2T.js} +33 -23
- package/dist/{context-JFZEJ7W5.js → context-URSXPBCK.js} +17 -9
- package/dist/{context-clear-6ZHBAZZT.js → context-clear-KDAJRNUK.js} +33 -23
- package/dist/context-working-set-SBKMPPI2.js +1552 -0
- package/dist/{dispatch-runner-VKBRCWQC.js → dispatch-runner-MSWN72NK.js} +43 -29
- package/dist/{doctor-KI767GSN.js → doctor-7BSE27PJ.js} +10 -10
- package/dist/{eval-XSSNATB4.js → eval-IZGDOO4H.js} +9 -8
- package/dist/{evidence-UA6AWDQQ.js → evidence-SR7WXB5B.js} +51 -23
- package/dist/{evolve-QNTFGV6Z.js → evolve-K7VE2CBX.js} +30 -20
- package/dist/{fleet-Q7UOMUSG.js → fleet-7XMJNQNF.js} +48 -38
- package/dist/{fleet-preflight-DDN536IT.js → fleet-preflight-AQNAH644.js} +3 -3
- package/dist/{init-WBB65ZHQ.js → init-JGNPAYXT.js} +41 -31
- package/dist/{memory-MD3O64RI.js → memory-4ALKDJ4Q.js} +32 -22
- package/dist/{models-BZU34YWD.js → models-ZMMLFJNN.js} +22 -19
- package/dist/{monitor-MEQA5C3I.js → monitor-2F3T5KHP.js} +55 -43
- package/dist/{orchestrator-CGFKEP27.js → orchestrator-ORHT43JB.js} +2507 -1896
- package/dist/{reset-L2FQEE3E.js → reset-NXGTYNUO.js} +4 -3
- package/dist/{run-IV4Q6RLN.js → run-RF4WJGMT.js} +51 -41
- package/dist/{share-S5BZQC5I.js → share-UT3W6E4M.js} +5 -4
- package/dist/{skills-LQEKRDTN.js → skills-PSACKC5Q.js} +2 -2
- package/dist/{skills-eval-3DC4HEWS.js → skills-eval-WJSI55RZ.js} +34 -24
- package/dist/{targets-C4SSGQOB.js → targets-PIIRAOYS.js} +23 -20
- package/dist/{terminal-lease-IT5JW2NR.js → terminal-lease-ULWXWNVY.js} +5 -4
- package/dist/{upgrade-7TT7SQ3G.js → upgrade-346TZ6AV.js} +18 -17
- package/dist/{usage-GV4PKT3M.js → usage-6KKXR32N.js} +34 -24
- package/dist/verifiers-4UUM6TEE.js +1214 -0
- package/dist/verify-X5HDROLA.js +25 -0
- package/dist/{wiki-generate-DQF6Z66B.js → wiki-generate-7STOCIFZ.js} +42 -31
- package/dist/worker/entry.js +33 -24
- package/docs/README.md +8 -7
- 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 +6 -6
- package/docs/middleware-and-components.md +1 -1
- package/docs/model-catalog.md +7 -9
- package/docs/observability.md +4 -4
- package/docs/performance-methodology.md +2 -2
- 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 +35 -35
- 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 +2 -2
- 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/git-commit-attribution.ts +46 -21
- 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/config/keybindings.ts +3 -3
- 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 +571 -244
- package/src/interactive/chat-renderer.ts +79 -39
- 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/editor-submit.ts +26 -1
- package/src/interactive/footer/widgets.ts +22 -20
- package/src/interactive/footer-panel.ts +6 -1
- package/src/interactive/interactive-application.ts +2 -0
- package/src/interactive/interactive-event-projection.ts +12 -0
- package/src/interactive/interactive-slash-runtime.ts +49 -8
- 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/overlays/ask-user.ts +146 -24
- package/src/interactive/renderers/tool-execution.ts +167 -56
- package/src/interactive/session-transcript.ts +2 -2
- package/src/interactive/slash-commands.ts +29 -2
- package/src/interactive/status/index.ts +12 -1
- package/src/interactive/status/reasoning.ts +87 -0
- package/src/interactive/status/summary.ts +13 -2
- package/src/interactive/transcript-detail.ts +120 -0
- 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 +18 -6
- 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/presentation.ts +107 -0
- package/src/tools/registry.ts +65 -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-MNA4JGU4.js +0 -255
- package/dist/chunk-SRF2PJNW.js +0 -184
- package/dist/chunk-T6YILFSB.js +0 -80
- package/dist/chunk-VAKQQHWR.js +0 -434
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
isAutonomyLevel,
|
|
7
7
|
mapAutonomy,
|
|
8
8
|
typedValidationSummary
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-7RXG6QRZ.js";
|
|
10
10
|
import {
|
|
11
11
|
ROUTE_POLICY_VERSION,
|
|
12
12
|
deriveExecutionRole,
|
|
@@ -15,36 +15,40 @@ import {
|
|
|
15
15
|
isRoutingIntent,
|
|
16
16
|
routePriorForLatencyClass,
|
|
17
17
|
sameRouteIdentity
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-22NAGB7X.js";
|
|
19
19
|
import {
|
|
20
20
|
AGENT_AUTOMATION_AUTHORITIES,
|
|
21
|
-
agentSpecFingerprint
|
|
21
|
+
agentSpecFingerprint
|
|
22
|
+
} from "./chunk-AOCYTWAV.js";
|
|
23
|
+
import {
|
|
22
24
|
detectValidationCommand,
|
|
23
25
|
extractCommandDeleteTargets,
|
|
24
26
|
extractCommandWriteTargets,
|
|
25
27
|
tokenizeShellLike,
|
|
26
28
|
toolMutationPaths
|
|
27
|
-
} from "./chunk-
|
|
29
|
+
} from "./chunk-MV3K5QF2.js";
|
|
28
30
|
import {
|
|
31
|
+
expandPath,
|
|
29
32
|
resolveReadPath
|
|
30
33
|
} from "./chunk-OQE5J4C6.js";
|
|
31
|
-
import {
|
|
32
|
-
isSkillActivation
|
|
33
|
-
} from "./chunk-T6YILFSB.js";
|
|
34
34
|
import {
|
|
35
35
|
DEFAULT_MAX_LINES,
|
|
36
36
|
OBSERVATION_POLICY_SLACK_BYTES,
|
|
37
37
|
OBSERVE_SELF_CAPS,
|
|
38
|
+
deterministicDiagnosticSummary,
|
|
38
39
|
finalizeObservation,
|
|
39
|
-
formatSize,
|
|
40
40
|
isSessionOffloadPath,
|
|
41
41
|
observationBudgetExhausted,
|
|
42
42
|
reserveObservation,
|
|
43
43
|
splitLinesForCounting,
|
|
44
44
|
truncateHead,
|
|
45
45
|
truncateTail
|
|
46
|
-
} from "./chunk-
|
|
46
|
+
} from "./chunk-5M54SPOL.js";
|
|
47
47
|
import {
|
|
48
|
+
formatSize
|
|
49
|
+
} from "./chunk-OZNBF4L3.js";
|
|
50
|
+
import {
|
|
51
|
+
byteLength,
|
|
48
52
|
truncateUtf8
|
|
49
53
|
} from "./chunk-6XLNIQDB.js";
|
|
50
54
|
import {
|
|
@@ -56,12 +60,24 @@ import {
|
|
|
56
60
|
} from "./chunk-CGKSTWHD.js";
|
|
57
61
|
import {
|
|
58
62
|
THINKING_LEVELS
|
|
59
|
-
} from "./chunk-
|
|
63
|
+
} from "./chunk-4OC57DA6.js";
|
|
64
|
+
import {
|
|
65
|
+
gitCommitAttributionEnabled,
|
|
66
|
+
reportCommitAttributionDiagnostic,
|
|
67
|
+
resolveSafeCwd,
|
|
68
|
+
withManagedGitCommitAttributionEnvironment
|
|
69
|
+
} from "./chunk-VEZEGCGW.js";
|
|
70
|
+
import {
|
|
71
|
+
clampTimerDelayMs
|
|
72
|
+
} from "./chunk-FQ4SKYE4.js";
|
|
60
73
|
import {
|
|
61
74
|
GUARDRAIL_DEFAULTS,
|
|
62
75
|
GUARDRAIL_ENV_VARS,
|
|
63
76
|
resolveGuardrail
|
|
64
77
|
} from "./chunk-4ZG3XFUR.js";
|
|
78
|
+
import {
|
|
79
|
+
AI_AGENT_NAME
|
|
80
|
+
} from "./chunk-6EJMN2Y3.js";
|
|
65
81
|
import {
|
|
66
82
|
init_esm_shims
|
|
67
83
|
} from "./chunk-3R73A4XB.js";
|
|
@@ -137,6 +153,255 @@ function applyCodewikiWorkerPolicy(tools, context) {
|
|
|
137
153
|
return tools.filter((tool) => !isCodewikiTool(tool));
|
|
138
154
|
}
|
|
139
155
|
|
|
156
|
+
// src/core/bash-exec.ts
|
|
157
|
+
init_esm_shims();
|
|
158
|
+
import { spawn } from "node:child_process";
|
|
159
|
+
import { StringDecoder } from "node:string_decoder";
|
|
160
|
+
var BASH_HARD_CAP_BYTES = 16 * 1024 * 1024;
|
|
161
|
+
var CLIO_CONTROL_ENV_KEYS = ["CLIO_CODER_INTERACTIVE", "CLIO_CODER_RESUME_SESSION_ID"];
|
|
162
|
+
var BASH_UPDATE_THROTTLE_MS = 100;
|
|
163
|
+
function buildToolEnv() {
|
|
164
|
+
const env = { ...process.env };
|
|
165
|
+
env.AI_AGENT = AI_AGENT_NAME;
|
|
166
|
+
for (const key of CLIO_CONTROL_ENV_KEYS) {
|
|
167
|
+
Reflect.deleteProperty(env, key);
|
|
168
|
+
}
|
|
169
|
+
return env;
|
|
170
|
+
}
|
|
171
|
+
var LOGIN_ENV_CAPTURE_TIMEOUT_MS = 1e4;
|
|
172
|
+
var loginEnvCapture = null;
|
|
173
|
+
function parseNullDelimitedEnv(raw) {
|
|
174
|
+
const env = {};
|
|
175
|
+
for (const entry of raw.split("\0")) {
|
|
176
|
+
if (entry.length === 0) continue;
|
|
177
|
+
const eq = entry.indexOf("=");
|
|
178
|
+
if (eq <= 0) continue;
|
|
179
|
+
env[entry.slice(0, eq)] = entry.slice(eq + 1);
|
|
180
|
+
}
|
|
181
|
+
return typeof env.PATH === "string" && env.PATH.length > 0 ? env : null;
|
|
182
|
+
}
|
|
183
|
+
function captureLoginEnv() {
|
|
184
|
+
return new Promise((resolve) => {
|
|
185
|
+
let stdout = "";
|
|
186
|
+
let settled = false;
|
|
187
|
+
const finish = (value) => {
|
|
188
|
+
if (settled) return;
|
|
189
|
+
settled = true;
|
|
190
|
+
clearTimeout(timer);
|
|
191
|
+
resolve(value);
|
|
192
|
+
};
|
|
193
|
+
const child = spawn("/bin/bash", ["-lc", "env -0"], {
|
|
194
|
+
env: buildToolEnv(),
|
|
195
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
196
|
+
});
|
|
197
|
+
const timer = setTimeout(() => {
|
|
198
|
+
child.kill("SIGKILL");
|
|
199
|
+
finish(null);
|
|
200
|
+
}, LOGIN_ENV_CAPTURE_TIMEOUT_MS);
|
|
201
|
+
child.stdout?.on("data", (chunk) => {
|
|
202
|
+
stdout += chunk.toString("utf8");
|
|
203
|
+
});
|
|
204
|
+
child.on("error", () => finish(null));
|
|
205
|
+
child.on("close", (code) => finish(code === 0 ? parseNullDelimitedEnv(stdout) : null));
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
async function bashSpawnPlan() {
|
|
209
|
+
loginEnvCapture ??= captureLoginEnv();
|
|
210
|
+
const captured = await loginEnvCapture;
|
|
211
|
+
if (captured === null) return { mode: "-lc", env: buildToolEnv() };
|
|
212
|
+
const env = { ...process.env, ...captured };
|
|
213
|
+
env.AI_AGENT = AI_AGENT_NAME;
|
|
214
|
+
for (const key of CLIO_CONTROL_ENV_KEYS) {
|
|
215
|
+
Reflect.deleteProperty(env, key);
|
|
216
|
+
}
|
|
217
|
+
return { mode: "-c", env };
|
|
218
|
+
}
|
|
219
|
+
function combineBashOutput(result) {
|
|
220
|
+
const { stdout, stderr } = result;
|
|
221
|
+
return stderr.length > 0 ? `${stdout}${stdout.endsWith("\n") || stdout.length === 0 ? "" : "\n"}${stderr}` : stdout;
|
|
222
|
+
}
|
|
223
|
+
async function runBashCommand(command, options = {}) {
|
|
224
|
+
const plan = await bashSpawnPlan();
|
|
225
|
+
const attribution = withManagedGitCommitAttributionEnvironment(plan.env, {
|
|
226
|
+
cwd: options.cwd ?? process.cwd(),
|
|
227
|
+
enabled: gitCommitAttributionEnabled(process.env)
|
|
228
|
+
});
|
|
229
|
+
reportCommitAttributionDiagnostic(attribution.diagnostic);
|
|
230
|
+
return new Promise((resolve) => {
|
|
231
|
+
const timeout = clampTimerDelayMs(options.timeoutMs ?? 3e5);
|
|
232
|
+
let aborted = false;
|
|
233
|
+
let timedOut = false;
|
|
234
|
+
let settled = false;
|
|
235
|
+
let timeoutId = null;
|
|
236
|
+
let killGraceTimer = null;
|
|
237
|
+
let killSent = false;
|
|
238
|
+
let stdout = "";
|
|
239
|
+
let stderr = "";
|
|
240
|
+
const stdoutDecoder = new StringDecoder("utf8");
|
|
241
|
+
const stderrDecoder = new StringDecoder("utf8");
|
|
242
|
+
let decodersFinished = false;
|
|
243
|
+
let outputBytes = 0;
|
|
244
|
+
let outputCapped = false;
|
|
245
|
+
let updateTimer = null;
|
|
246
|
+
let updateDirty = false;
|
|
247
|
+
let lastUpdateAt = 0;
|
|
248
|
+
const child = spawn("/bin/bash", [plan.mode, command], {
|
|
249
|
+
...options.cwd === void 0 ? {} : { cwd: options.cwd },
|
|
250
|
+
env: attribution.env,
|
|
251
|
+
detached: process.platform !== "win32",
|
|
252
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
253
|
+
});
|
|
254
|
+
const finishDecoders = () => {
|
|
255
|
+
if (decodersFinished) return;
|
|
256
|
+
decodersFinished = true;
|
|
257
|
+
if (outputCapped) return;
|
|
258
|
+
stdout += stdoutDecoder.end();
|
|
259
|
+
stderr += stderrDecoder.end();
|
|
260
|
+
};
|
|
261
|
+
const emitUpdate = () => {
|
|
262
|
+
if (options.onUpdate === void 0 || !updateDirty) return;
|
|
263
|
+
updateDirty = false;
|
|
264
|
+
lastUpdateAt = Date.now();
|
|
265
|
+
try {
|
|
266
|
+
options.onUpdate({ stdout, stderr, outputBytes: Math.min(outputBytes, BASH_HARD_CAP_BYTES) });
|
|
267
|
+
} catch {
|
|
268
|
+
}
|
|
269
|
+
};
|
|
270
|
+
const clearUpdateTimer = () => {
|
|
271
|
+
if (updateTimer === null) return;
|
|
272
|
+
clearTimeout(updateTimer);
|
|
273
|
+
updateTimer = null;
|
|
274
|
+
};
|
|
275
|
+
const scheduleUpdate = () => {
|
|
276
|
+
if (options.onUpdate === void 0) return;
|
|
277
|
+
updateDirty = true;
|
|
278
|
+
const delay = BASH_UPDATE_THROTTLE_MS - (Date.now() - lastUpdateAt);
|
|
279
|
+
if (delay <= 0) {
|
|
280
|
+
clearUpdateTimer();
|
|
281
|
+
emitUpdate();
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
284
|
+
updateTimer ??= setTimeout(() => {
|
|
285
|
+
updateTimer = null;
|
|
286
|
+
emitUpdate();
|
|
287
|
+
}, delay);
|
|
288
|
+
};
|
|
289
|
+
const finishUpdates = () => {
|
|
290
|
+
clearUpdateTimer();
|
|
291
|
+
emitUpdate();
|
|
292
|
+
};
|
|
293
|
+
if (options.onUpdate !== void 0) {
|
|
294
|
+
updateDirty = true;
|
|
295
|
+
emitUpdate();
|
|
296
|
+
}
|
|
297
|
+
const clearKillGraceTimer = () => {
|
|
298
|
+
if (!killGraceTimer) return;
|
|
299
|
+
clearTimeout(killGraceTimer);
|
|
300
|
+
killGraceTimer = null;
|
|
301
|
+
};
|
|
302
|
+
const sendSignal = (signalName) => {
|
|
303
|
+
const pid = child.pid;
|
|
304
|
+
if (pid && process.platform !== "win32") {
|
|
305
|
+
try {
|
|
306
|
+
process.kill(-pid, signalName);
|
|
307
|
+
return;
|
|
308
|
+
} catch {
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
child.kill(signalName);
|
|
312
|
+
};
|
|
313
|
+
const killChild = () => {
|
|
314
|
+
if (killSent) return;
|
|
315
|
+
killSent = true;
|
|
316
|
+
sendSignal("SIGTERM");
|
|
317
|
+
killGraceTimer = setTimeout(() => {
|
|
318
|
+
sendSignal("SIGKILL");
|
|
319
|
+
}, 5e3);
|
|
320
|
+
};
|
|
321
|
+
function onAbort() {
|
|
322
|
+
aborted = true;
|
|
323
|
+
killChild();
|
|
324
|
+
}
|
|
325
|
+
if (timeout > 0) {
|
|
326
|
+
timeoutId = setTimeout(() => {
|
|
327
|
+
timedOut = true;
|
|
328
|
+
killChild();
|
|
329
|
+
}, timeout);
|
|
330
|
+
}
|
|
331
|
+
if (options.signal?.aborted) {
|
|
332
|
+
onAbort();
|
|
333
|
+
} else {
|
|
334
|
+
options.signal?.addEventListener("abort", onAbort, { once: true });
|
|
335
|
+
}
|
|
336
|
+
const appendChunk = (target, chunk) => {
|
|
337
|
+
if (outputCapped) return;
|
|
338
|
+
const remaining = BASH_HARD_CAP_BYTES - outputBytes;
|
|
339
|
+
const accepted = chunk.byteLength <= remaining ? chunk : chunk.subarray(0, Math.max(0, remaining));
|
|
340
|
+
outputBytes += accepted.byteLength;
|
|
341
|
+
if (accepted.byteLength > 0) {
|
|
342
|
+
const decoded = target === "stdout" ? stdoutDecoder.write(accepted) : stderrDecoder.write(accepted);
|
|
343
|
+
if (target === "stdout") stdout += decoded;
|
|
344
|
+
else stderr += decoded;
|
|
345
|
+
}
|
|
346
|
+
if (accepted.byteLength < chunk.byteLength) {
|
|
347
|
+
outputCapped = true;
|
|
348
|
+
scheduleUpdate();
|
|
349
|
+
killChild();
|
|
350
|
+
return;
|
|
351
|
+
}
|
|
352
|
+
scheduleUpdate();
|
|
353
|
+
};
|
|
354
|
+
child.stdout?.on("data", (chunk) => appendChunk("stdout", chunk));
|
|
355
|
+
child.stderr?.on("data", (chunk) => appendChunk("stderr", chunk));
|
|
356
|
+
child.on("error", (error) => {
|
|
357
|
+
if (settled) return;
|
|
358
|
+
settled = true;
|
|
359
|
+
if (timeoutId) clearTimeout(timeoutId);
|
|
360
|
+
clearKillGraceTimer();
|
|
361
|
+
finishDecoders();
|
|
362
|
+
finishUpdates();
|
|
363
|
+
options.signal?.removeEventListener("abort", onAbort);
|
|
364
|
+
resolve({
|
|
365
|
+
error,
|
|
366
|
+
stdout,
|
|
367
|
+
stderr,
|
|
368
|
+
exitCode: null,
|
|
369
|
+
signal: null,
|
|
370
|
+
aborted,
|
|
371
|
+
timedOut,
|
|
372
|
+
outputCapped,
|
|
373
|
+
outputBytes
|
|
374
|
+
});
|
|
375
|
+
});
|
|
376
|
+
child.on("close", (code, signalName) => {
|
|
377
|
+
if (settled) return;
|
|
378
|
+
settled = true;
|
|
379
|
+
if (timeoutId) clearTimeout(timeoutId);
|
|
380
|
+
clearKillGraceTimer();
|
|
381
|
+
finishDecoders();
|
|
382
|
+
finishUpdates();
|
|
383
|
+
options.signal?.removeEventListener("abort", onAbort);
|
|
384
|
+
const error = code === 0 && signalName === null ? null : {
|
|
385
|
+
name: "Error",
|
|
386
|
+
message: signalName ? `command terminated by ${signalName}` : `command exited with code ${code ?? "?"}`,
|
|
387
|
+
code: code ?? void 0,
|
|
388
|
+
signal: signalName ?? void 0
|
|
389
|
+
};
|
|
390
|
+
resolve({
|
|
391
|
+
error,
|
|
392
|
+
stdout,
|
|
393
|
+
stderr,
|
|
394
|
+
exitCode: code,
|
|
395
|
+
signal: signalName,
|
|
396
|
+
aborted,
|
|
397
|
+
timedOut,
|
|
398
|
+
outputCapped,
|
|
399
|
+
outputBytes
|
|
400
|
+
});
|
|
401
|
+
});
|
|
402
|
+
});
|
|
403
|
+
}
|
|
404
|
+
|
|
140
405
|
// src/tools/network-policy.ts
|
|
141
406
|
init_esm_shims();
|
|
142
407
|
var NO_NETWORK_TOOLS_ENV = "CLIO_CODER_NO_NETWORK_TOOLS";
|
|
@@ -831,525 +1096,9 @@ function runKindSupportsLiveSteering(kind) {
|
|
|
831
1096
|
return kind === "http" || kind === "sdk";
|
|
832
1097
|
}
|
|
833
1098
|
|
|
834
|
-
// src/domains/session/task-board.ts
|
|
835
|
-
init_esm_shims();
|
|
836
|
-
import { randomUUID } from "node:crypto";
|
|
837
|
-
|
|
838
|
-
// src/domains/session/entries.ts
|
|
839
|
-
init_esm_shims();
|
|
840
|
-
var SESSION_ENTRY_KINDS = [
|
|
841
|
-
"message",
|
|
842
|
-
"bashExecution",
|
|
843
|
-
"custom",
|
|
844
|
-
"modelChange",
|
|
845
|
-
"thinkingLevelChange",
|
|
846
|
-
"fileEntry",
|
|
847
|
-
"branchSummary",
|
|
848
|
-
"compactionSummary",
|
|
849
|
-
"sessionInfo",
|
|
850
|
-
"label",
|
|
851
|
-
"protectedArtifact",
|
|
852
|
-
"skillActivation",
|
|
853
|
-
"taskLedger",
|
|
854
|
-
"decisionLedger",
|
|
855
|
-
"workerRun"
|
|
856
|
-
];
|
|
857
|
-
function isRecord(value) {
|
|
858
|
-
return !!value && typeof value === "object";
|
|
859
|
-
}
|
|
860
|
-
function isString(value) {
|
|
861
|
-
return typeof value === "string";
|
|
862
|
-
}
|
|
863
|
-
function isOptionalString(value) {
|
|
864
|
-
return value === void 0 || typeof value === "string";
|
|
865
|
-
}
|
|
866
|
-
function isOptionalBoolean(value) {
|
|
867
|
-
return value === void 0 || typeof value === "boolean";
|
|
868
|
-
}
|
|
869
|
-
function isNumber(value) {
|
|
870
|
-
return typeof value === "number" && Number.isFinite(value);
|
|
871
|
-
}
|
|
872
|
-
function isPositiveInteger(value) {
|
|
873
|
-
return isNumber(value) && Number.isInteger(value) && value > 0;
|
|
874
|
-
}
|
|
875
|
-
function isOptionalNumber(value) {
|
|
876
|
-
return value === void 0 || isNumber(value);
|
|
877
|
-
}
|
|
878
|
-
function isNullableString(value) {
|
|
879
|
-
return value === null || typeof value === "string";
|
|
880
|
-
}
|
|
881
|
-
function isOneOf(value, allowed) {
|
|
882
|
-
return typeof value === "string" && allowed.includes(value);
|
|
883
|
-
}
|
|
884
|
-
function isStringArray(value) {
|
|
885
|
-
return Array.isArray(value) && value.every((item) => typeof item === "string");
|
|
886
|
-
}
|
|
887
|
-
function hasBaseSessionEntryFields(v) {
|
|
888
|
-
return isString(v.turnId) && isNullableString(v.parentTurnId) && isString(v.timestamp);
|
|
889
|
-
}
|
|
890
|
-
var MESSAGE_ROLES = ["user", "assistant", "tool_call", "tool_result", "system", "checkpoint"];
|
|
891
|
-
var FILE_OPERATIONS = ["read", "write", "edit", "create", "delete"];
|
|
892
|
-
var COMPACTION_TRIGGERS = ["auto", "force", "overflow"];
|
|
893
|
-
var PROTECTED_ARTIFACT_SOURCES = [
|
|
894
|
-
"validation",
|
|
895
|
-
"middleware",
|
|
896
|
-
"user",
|
|
897
|
-
"session"
|
|
898
|
-
];
|
|
899
|
-
var TASK_LEDGER_STATUSES = ["pending", "active", "completed", "blocked", "cancelled"];
|
|
900
|
-
var TASK_LEDGER_EVIDENCE_STATUSES = [
|
|
901
|
-
"required",
|
|
902
|
-
"pending",
|
|
903
|
-
"passed",
|
|
904
|
-
"failed",
|
|
905
|
-
"missing"
|
|
906
|
-
];
|
|
907
|
-
var DECISION_STATUSES = ["active", "superseded"];
|
|
908
|
-
var DECISION_INTERVIEW_STATUSES = ["complete", "cancelled"];
|
|
909
|
-
var WORKER_RUN_ORIGINS = ["user", "agent"];
|
|
910
|
-
var WORKER_RUN_RUNTIME_KINDS = ["clio", "acp", "claude-sdk", "claude-code"];
|
|
911
|
-
function isWorkerRunRuntime(value) {
|
|
912
|
-
if (!isRecord(value)) return false;
|
|
913
|
-
return isOneOf(value.kind, WORKER_RUN_RUNTIME_KINDS) && isOptionalString(value.targetId) && isOptionalString(value.wireModelId) && isOptionalString(value.peerId);
|
|
914
|
-
}
|
|
915
|
-
function isTaskLedgerGoal(value) {
|
|
916
|
-
if (!isRecord(value)) return false;
|
|
917
|
-
if (!isString(value.id) || !isString(value.title)) return false;
|
|
918
|
-
if (!isOneOf(value.status, TASK_LEDGER_STATUSES)) return false;
|
|
919
|
-
if (value.parentGoalId !== void 0 && !isNullableString(value.parentGoalId)) return false;
|
|
920
|
-
const hasPairedProvenance = value.origin === "user" ? isString(value.userTaskId) && /^u[1-9]\d*$/.test(value.userTaskId) : (value.origin === void 0 || value.origin === "agent") && value.userTaskId === void 0;
|
|
921
|
-
return isOptionalString(value.description) && hasPairedProvenance;
|
|
922
|
-
}
|
|
923
|
-
function isTaskLedgerEvidence(value) {
|
|
924
|
-
if (!isRecord(value)) return false;
|
|
925
|
-
if (!isString(value.id) || !isString(value.description)) return false;
|
|
926
|
-
if (!isOneOf(value.status, TASK_LEDGER_EVIDENCE_STATUSES)) return false;
|
|
927
|
-
return isOptionalString(value.command) && isOptionalString(value.artifactPath) && isOptionalString(value.observedAt) && isOptionalString(value.notes);
|
|
928
|
-
}
|
|
929
|
-
function isDecisionRecord(value) {
|
|
930
|
-
if (!isRecord(value)) return false;
|
|
931
|
-
if (!isString(value.key) || !/^[a-z0-9]+(?:_[a-z0-9]+)*$/.test(value.key)) return false;
|
|
932
|
-
if (!isString(value.value) || !isOneOf(value.status, DECISION_STATUSES) || !isString(value.decidedAt)) return false;
|
|
933
|
-
if (!isOptionalString(value.label) || !isOptionalString(value.source_question)) return false;
|
|
934
|
-
if (!isOptionalString(value.revisedAt) || !isOptionalString(value.correction)) return false;
|
|
935
|
-
return value.status === "superseded" ? isString(value.revisedAt) : value.revisedAt === void 0 && value.correction === void 0;
|
|
936
|
-
}
|
|
937
|
-
function isOptionalCompactionUsage(value) {
|
|
938
|
-
if (value === void 0) return true;
|
|
939
|
-
if (!isRecord(value)) return false;
|
|
940
|
-
const cost = value.cost;
|
|
941
|
-
return isNumber(value.input) && isNumber(value.output) && isNumber(value.cacheRead) && isNumber(value.cacheWrite) && isNumber(value.reasoning) && isNumber(value.totalTokens) && isNumber(value.apiCalls) && isRecord(cost) && isNumber(cost.total);
|
|
942
|
-
}
|
|
943
|
-
function isProtectedArtifact(value) {
|
|
944
|
-
if (!isRecord(value)) return false;
|
|
945
|
-
return isString(value.path) && isString(value.protectedAt) && isString(value.reason) && isOneOf(value.source, PROTECTED_ARTIFACT_SOURCES) && isOptionalString(value.validationCommand) && isOptionalNumber(value.validationExitCode);
|
|
946
|
-
}
|
|
947
|
-
function isSessionHeader(value) {
|
|
948
|
-
if (!isRecord(value)) return false;
|
|
949
|
-
return value.type === "session" && isPositiveInteger(value.version) && isString(value.id) && isString(value.timestamp) && isString(value.cwd) && isOptionalString(value.parentSession) && isOptionalString(value.parentTurnId);
|
|
950
|
-
}
|
|
951
|
-
function isSessionEntry(value) {
|
|
952
|
-
if (!value || typeof value !== "object") return false;
|
|
953
|
-
const v = value;
|
|
954
|
-
if (!hasBaseSessionEntryFields(v)) return false;
|
|
955
|
-
if (!isOneOf(v.kind, SESSION_ENTRY_KINDS)) return false;
|
|
956
|
-
switch (v.kind) {
|
|
957
|
-
case "message":
|
|
958
|
-
return Object.hasOwn(v, "payload") && isOneOf(v.role, MESSAGE_ROLES);
|
|
959
|
-
case "bashExecution":
|
|
960
|
-
return isString(v.command) && isString(v.output) && (v.exitCode === null || isNumber(v.exitCode)) && typeof v.cancelled === "boolean" && typeof v.truncated === "boolean" && isOptionalString(v.fullOutputPath) && isOptionalBoolean(v.excludeFromContext);
|
|
961
|
-
case "custom":
|
|
962
|
-
return isString(v.customType) && isOptionalBoolean(v.display);
|
|
963
|
-
case "modelChange":
|
|
964
|
-
return isString(v.provider) && isString(v.modelId) && isOptionalString(v.target);
|
|
965
|
-
case "thinkingLevelChange":
|
|
966
|
-
return isString(v.thinkingLevel);
|
|
967
|
-
case "fileEntry":
|
|
968
|
-
return isString(v.path) && isOneOf(v.operation, FILE_OPERATIONS) && isOptionalNumber(v.bytes) && isOptionalString(v.hash);
|
|
969
|
-
case "branchSummary":
|
|
970
|
-
return isString(v.fromTurnId) && isString(v.summary);
|
|
971
|
-
case "compactionSummary":
|
|
972
|
-
return isString(v.summary) && isNumber(v.tokensBefore) && isString(v.firstKeptTurnId) && isOptionalCompactionUsage(v.usage) && (v.trigger === void 0 || isOneOf(v.trigger, COMPACTION_TRIGGERS)) && isOptionalNumber(v.tokensAfter) && isOptionalNumber(v.messagesSummarized) && isOptionalBoolean(v.isSplitTurn);
|
|
973
|
-
case "sessionInfo":
|
|
974
|
-
return isOptionalString(v.name) && isOptionalString(v.targetTurnId) && isOptionalString(v.label);
|
|
975
|
-
case "label":
|
|
976
|
-
return isString(v.targetTurnId) && isOptionalString(v.label);
|
|
977
|
-
case "protectedArtifact":
|
|
978
|
-
return v.action === "protect" && isProtectedArtifact(v.artifact) && isOptionalString(v.toolName) && isOptionalString(v.toolCallId) && isOptionalString(v.runId) && isOptionalString(v.correlationId);
|
|
979
|
-
case "skillActivation":
|
|
980
|
-
return isSkillActivation(v.activation);
|
|
981
|
-
case "taskLedger":
|
|
982
|
-
return isOptionalString(v.boardId) && Array.isArray(v.goals) && v.goals.every(isTaskLedgerGoal) && Array.isArray(v.subgoals) && v.subgoals.every(isTaskLedgerGoal) && isStringArray(v.activeRunIds) && Array.isArray(v.requiredValidationEvidence) && v.requiredValidationEvidence.every(isTaskLedgerEvidence);
|
|
983
|
-
case "decisionLedger":
|
|
984
|
-
return isString(v.interviewId) && isOneOf(v.interviewStatus, DECISION_INTERVIEW_STATUSES) && isString(v.startedAt) && isString(v.endedAt) && isNumber(v.roundCount) && Number.isInteger(v.roundCount) && v.roundCount >= 0 && isOptionalString(v.summary) && isOptionalString(v.transcriptPath) && Array.isArray(v.decisions) && v.decisions.every(isDecisionRecord);
|
|
985
|
-
case "workerRun":
|
|
986
|
-
return isString(v.assignmentId) && isString(v.runId) && isOneOf(v.origin, WORKER_RUN_ORIGINS) && isString(v.agentId) && isWorkerRunRuntime(v.runtime) && isOptionalString(v.parentToolCallId);
|
|
987
|
-
}
|
|
988
|
-
return false;
|
|
989
|
-
}
|
|
990
|
-
|
|
991
|
-
// src/domains/session/task-board.ts
|
|
992
|
-
var TASK_BOARD_GOAL_ID = "board";
|
|
993
|
-
var LEGACY_TASK_BOARD_ID = "legacy";
|
|
994
|
-
function isOpenUserTask(task) {
|
|
995
|
-
return task.origin === "user" && (task.status === "pending" || task.status === "active");
|
|
996
|
-
}
|
|
997
|
-
function taskBoardCounts(board) {
|
|
998
|
-
const counts = {
|
|
999
|
-
total: board.tasks.length,
|
|
1000
|
-
completed: 0,
|
|
1001
|
-
active: 0,
|
|
1002
|
-
pending: 0,
|
|
1003
|
-
blocked: 0,
|
|
1004
|
-
cancelled: 0,
|
|
1005
|
-
open: 0
|
|
1006
|
-
};
|
|
1007
|
-
for (const task of board.tasks) {
|
|
1008
|
-
if (task.status === "completed") counts.completed += 1;
|
|
1009
|
-
else if (task.status === "active") counts.active += 1;
|
|
1010
|
-
else if (task.status === "pending") counts.pending += 1;
|
|
1011
|
-
else if (task.status === "blocked") counts.blocked += 1;
|
|
1012
|
-
else counts.cancelled += 1;
|
|
1013
|
-
}
|
|
1014
|
-
counts.open = counts.pending + counts.active;
|
|
1015
|
-
return counts;
|
|
1016
|
-
}
|
|
1017
|
-
function boardStatus(tasks) {
|
|
1018
|
-
const counts = taskBoardCounts({ tasks });
|
|
1019
|
-
if (counts.total === 0) return "pending";
|
|
1020
|
-
if (counts.active > 0) return "active";
|
|
1021
|
-
if (counts.open === 0 && counts.blocked === 0) return "completed";
|
|
1022
|
-
if (counts.blocked > 0 && counts.open === 0) return "blocked";
|
|
1023
|
-
return "pending";
|
|
1024
|
-
}
|
|
1025
|
-
function toTaskLedgerEntryFields(board, now) {
|
|
1026
|
-
const evidence = [];
|
|
1027
|
-
const subgoals = board.tasks.map((task) => {
|
|
1028
|
-
if (task.status === "completed" && task.evidence) {
|
|
1029
|
-
evidence.push({
|
|
1030
|
-
id: `${task.id}.evidence`,
|
|
1031
|
-
description: task.evidence,
|
|
1032
|
-
status: "passed",
|
|
1033
|
-
observedAt: now.toISOString()
|
|
1034
|
-
});
|
|
1035
|
-
}
|
|
1036
|
-
const goal = {
|
|
1037
|
-
id: task.id,
|
|
1038
|
-
title: task.title,
|
|
1039
|
-
status: task.status,
|
|
1040
|
-
parentGoalId: TASK_BOARD_GOAL_ID
|
|
1041
|
-
};
|
|
1042
|
-
if (task.origin) goal.origin = task.origin;
|
|
1043
|
-
if (task.userTaskId) goal.userTaskId = task.userTaskId;
|
|
1044
|
-
if (task.reason) goal.description = task.reason;
|
|
1045
|
-
return goal;
|
|
1046
|
-
});
|
|
1047
|
-
return {
|
|
1048
|
-
kind: "taskLedger",
|
|
1049
|
-
parentTurnId: null,
|
|
1050
|
-
boardId: board.boardId,
|
|
1051
|
-
goals: [{ id: TASK_BOARD_GOAL_ID, title: board.title, status: boardStatus(board.tasks) }],
|
|
1052
|
-
subgoals,
|
|
1053
|
-
activeRunIds: [...board.activeRunIds],
|
|
1054
|
-
requiredValidationEvidence: evidence
|
|
1055
|
-
};
|
|
1056
|
-
}
|
|
1057
|
-
function isTaskLedgerShaped(value) {
|
|
1058
|
-
return isSessionEntry(value) && value.kind === "taskLedger";
|
|
1059
|
-
}
|
|
1060
|
-
function foldTaskBoard(entries) {
|
|
1061
|
-
let last = null;
|
|
1062
|
-
for (const raw of entries) {
|
|
1063
|
-
if (isTaskLedgerShaped(raw)) last = toEntryView(raw);
|
|
1064
|
-
}
|
|
1065
|
-
return last;
|
|
1066
|
-
}
|
|
1067
|
-
function foldSessionTaskHistory(entries) {
|
|
1068
|
-
const boards = /* @__PURE__ */ new Map();
|
|
1069
|
-
for (const [index, raw] of entries.entries()) {
|
|
1070
|
-
if (!isTaskLedgerShaped(raw)) continue;
|
|
1071
|
-
const view = toEntryView(raw);
|
|
1072
|
-
if (!view) continue;
|
|
1073
|
-
const boardId = typeof raw.boardId === "string" && raw.boardId.length > 0 ? raw.boardId : LEGACY_TASK_BOARD_ID;
|
|
1074
|
-
const timestamp = typeof raw.timestamp === "string" ? raw.timestamp : "";
|
|
1075
|
-
if (boardId === LEGACY_TASK_BOARD_ID) {
|
|
1076
|
-
boards.set(boardId, {
|
|
1077
|
-
boardId,
|
|
1078
|
-
title: view.title,
|
|
1079
|
-
tasks: new Map(view.tasks.map((task) => [task.id, task])),
|
|
1080
|
-
lastSnapshotAt: timestamp,
|
|
1081
|
-
lastIndex: index
|
|
1082
|
-
});
|
|
1083
|
-
continue;
|
|
1084
|
-
}
|
|
1085
|
-
const existing = boards.get(boardId);
|
|
1086
|
-
if (!existing) {
|
|
1087
|
-
boards.set(boardId, {
|
|
1088
|
-
boardId,
|
|
1089
|
-
title: view.title,
|
|
1090
|
-
tasks: new Map(view.tasks.map((task) => [task.id, task])),
|
|
1091
|
-
lastSnapshotAt: timestamp,
|
|
1092
|
-
lastIndex: index
|
|
1093
|
-
});
|
|
1094
|
-
continue;
|
|
1095
|
-
}
|
|
1096
|
-
existing.title = view.title;
|
|
1097
|
-
for (const task of view.tasks) existing.tasks.set(task.id, task);
|
|
1098
|
-
existing.lastSnapshotAt = timestamp;
|
|
1099
|
-
existing.lastIndex = index;
|
|
1100
|
-
}
|
|
1101
|
-
return [...boards.values()].sort((left, right) => right.lastIndex - left.lastIndex).map(({ lastIndex: _lastIndex, tasks, ...board }) => ({ ...board, tasks: [...tasks.values()] }));
|
|
1102
|
-
}
|
|
1103
|
-
function toEntryView(entry) {
|
|
1104
|
-
const boardGoal = entry.goals[0];
|
|
1105
|
-
if (!boardGoal) return null;
|
|
1106
|
-
const evidenceByTask = /* @__PURE__ */ new Map();
|
|
1107
|
-
for (const item of entry.requiredValidationEvidence) {
|
|
1108
|
-
const taskId = item.id.endsWith(".evidence") ? item.id.slice(0, -".evidence".length) : item.id;
|
|
1109
|
-
evidenceByTask.set(taskId, item.description);
|
|
1110
|
-
}
|
|
1111
|
-
return {
|
|
1112
|
-
boardId: entry.boardId ?? LEGACY_TASK_BOARD_ID,
|
|
1113
|
-
title: boardGoal.title,
|
|
1114
|
-
tasks: entry.subgoals.map((goal) => {
|
|
1115
|
-
const task = {
|
|
1116
|
-
id: goal.id,
|
|
1117
|
-
title: goal.title,
|
|
1118
|
-
status: goal.status,
|
|
1119
|
-
origin: goal.origin ?? "agent"
|
|
1120
|
-
};
|
|
1121
|
-
if (goal.userTaskId) task.userTaskId = goal.userTaskId;
|
|
1122
|
-
if (goal.description) task.reason = goal.description;
|
|
1123
|
-
const evidence = evidenceByTask.get(goal.id);
|
|
1124
|
-
if (evidence) task.evidence = evidence;
|
|
1125
|
-
return task;
|
|
1126
|
-
}),
|
|
1127
|
-
// Run linkage is process-live: the runs recorded in old entries ended
|
|
1128
|
-
// with the process that dispatched them, so a refold starts empty and
|
|
1129
|
-
// the historical linkage stays readable in the earlier entries.
|
|
1130
|
-
activeRunIds: []
|
|
1131
|
-
};
|
|
1132
|
-
}
|
|
1133
|
-
function nextTaskId(tasks) {
|
|
1134
|
-
let max = 0;
|
|
1135
|
-
for (const task of tasks) {
|
|
1136
|
-
const match = /^t(\d+)$/.exec(task.id);
|
|
1137
|
-
if (match) max = Math.max(max, Number(match[1]));
|
|
1138
|
-
}
|
|
1139
|
-
return max + 1;
|
|
1140
|
-
}
|
|
1141
|
-
function normalizeTitles(titles) {
|
|
1142
|
-
return titles.map((title) => title.trim()).filter((title) => title.length > 0);
|
|
1143
|
-
}
|
|
1144
|
-
function applyMutation(board, mutation) {
|
|
1145
|
-
if (mutation.op === "plan") {
|
|
1146
|
-
const titles = normalizeTitles(mutation.tasks);
|
|
1147
|
-
if (mutation.title.trim().length === 0) return { error: "plan requires a non-empty title" };
|
|
1148
|
-
if (titles.length === 0) return { error: "plan requires at least one task" };
|
|
1149
|
-
const retained = board?.tasks.filter(isOpenUserTask) ?? [];
|
|
1150
|
-
const notes2 = [];
|
|
1151
|
-
const replacedOpen = board ? taskBoardCounts(board).open - retained.length : 0;
|
|
1152
|
-
if (board && replacedOpen > 0) {
|
|
1153
|
-
notes2.push(`replaced board "${board.title}" with ${replacedOpen} agent task(s) still open`);
|
|
1154
|
-
}
|
|
1155
|
-
if (retained.length > 0) notes2.push(`preserved ${retained.length} open operator task(s)`);
|
|
1156
|
-
const start = nextTaskId(retained);
|
|
1157
|
-
return {
|
|
1158
|
-
board: {
|
|
1159
|
-
boardId: "",
|
|
1160
|
-
title: mutation.title.trim(),
|
|
1161
|
-
tasks: [
|
|
1162
|
-
...retained,
|
|
1163
|
-
...titles.map((title, index) => ({
|
|
1164
|
-
id: `t${start + index}`,
|
|
1165
|
-
title,
|
|
1166
|
-
status: "pending",
|
|
1167
|
-
origin: "agent"
|
|
1168
|
-
}))
|
|
1169
|
-
],
|
|
1170
|
-
// Runs in flight outlive a board swap: they belong to the session,
|
|
1171
|
-
// so the fresh board inherits the linkage until the runs finish.
|
|
1172
|
-
activeRunIds: board?.activeRunIds ?? []
|
|
1173
|
-
},
|
|
1174
|
-
notes: notes2
|
|
1175
|
-
};
|
|
1176
|
-
}
|
|
1177
|
-
if (mutation.op === "pick") {
|
|
1178
|
-
const title = mutation.title.trim();
|
|
1179
|
-
if (title.length === 0) return { error: "pick requires a non-empty operator task title" };
|
|
1180
|
-
if (!/^u[1-9]\d*$/.test(mutation.userTaskId)) return { error: "pick requires an operator task id like u3" };
|
|
1181
|
-
if (board?.tasks.some(
|
|
1182
|
-
(task) => task.userTaskId === mutation.userTaskId && (task.status === "pending" || task.status === "active" || task.status === "completed")
|
|
1183
|
-
)) {
|
|
1184
|
-
return { error: `operator task ${mutation.userTaskId} is already linked to durable board work` };
|
|
1185
|
-
}
|
|
1186
|
-
const tasks2 = board?.tasks ?? [];
|
|
1187
|
-
const picked = {
|
|
1188
|
-
id: `t${nextTaskId(tasks2)}`,
|
|
1189
|
-
title,
|
|
1190
|
-
status: "pending",
|
|
1191
|
-
origin: "user",
|
|
1192
|
-
userTaskId: mutation.userTaskId
|
|
1193
|
-
};
|
|
1194
|
-
return {
|
|
1195
|
-
board: {
|
|
1196
|
-
boardId: board?.boardId ?? "",
|
|
1197
|
-
title: board?.title ?? "Operator tasks",
|
|
1198
|
-
tasks: [...tasks2, picked],
|
|
1199
|
-
activeRunIds: board?.activeRunIds ?? []
|
|
1200
|
-
},
|
|
1201
|
-
notes: []
|
|
1202
|
-
};
|
|
1203
|
-
}
|
|
1204
|
-
if (board === null) return { error: 'no task board yet; declare one with action="plan" first' };
|
|
1205
|
-
if (mutation.op === "add") {
|
|
1206
|
-
const titles = normalizeTitles(mutation.tasks);
|
|
1207
|
-
if (titles.length === 0) return { error: "add requires at least one task" };
|
|
1208
|
-
const start = nextTaskId(board.tasks);
|
|
1209
|
-
const added = titles.map((title, index) => ({
|
|
1210
|
-
id: `t${start + index}`,
|
|
1211
|
-
title,
|
|
1212
|
-
status: "pending",
|
|
1213
|
-
origin: "agent"
|
|
1214
|
-
}));
|
|
1215
|
-
return { board: { ...board, tasks: [...board.tasks, ...added] }, notes: [] };
|
|
1216
|
-
}
|
|
1217
|
-
const target = board.tasks.find((task) => task.id === mutation.id);
|
|
1218
|
-
if (!target) return { error: `task ${mutation.id} not found on the board` };
|
|
1219
|
-
if (mutation.op === "block" && mutation.reason.trim().length === 0) {
|
|
1220
|
-
return { error: "block requires a reason so the ledger records why work stopped" };
|
|
1221
|
-
}
|
|
1222
|
-
if (mutation.op === "done") {
|
|
1223
|
-
if (mutation.evidence.trim().length === 0) {
|
|
1224
|
-
return {
|
|
1225
|
-
error: "done requires note as the evidence that the task actually finished (the command you ran, the file:line you verified). If the work did not happen, use block with a reason or drop it."
|
|
1226
|
-
};
|
|
1227
|
-
}
|
|
1228
|
-
if (target.status === "pending") {
|
|
1229
|
-
return {
|
|
1230
|
-
error: `task ${target.id} was never started, so it cannot be completed. Start it and do the work, or record the truth with block (reason) or drop.`
|
|
1231
|
-
};
|
|
1232
|
-
}
|
|
1233
|
-
}
|
|
1234
|
-
const notes = [];
|
|
1235
|
-
const tasks = board.tasks.map((task) => {
|
|
1236
|
-
if (task.id !== target.id) {
|
|
1237
|
-
if (mutation.op === "start" && task.status === "active") {
|
|
1238
|
-
notes.push(`parked ${task.id} back to pending (one task active at a time)`);
|
|
1239
|
-
return { ...task, status: "pending" };
|
|
1240
|
-
}
|
|
1241
|
-
return task;
|
|
1242
|
-
}
|
|
1243
|
-
return applyStatusMutation(task, mutation);
|
|
1244
|
-
});
|
|
1245
|
-
return { board: { ...board, tasks }, notes };
|
|
1246
|
-
}
|
|
1247
|
-
function applyStatusMutation(task, mutation) {
|
|
1248
|
-
switch (mutation.op) {
|
|
1249
|
-
case "start":
|
|
1250
|
-
return { ...task, status: "active" };
|
|
1251
|
-
case "done": {
|
|
1252
|
-
const done = { ...task, status: "completed", evidence: mutation.evidence.trim() };
|
|
1253
|
-
delete done.reason;
|
|
1254
|
-
return done;
|
|
1255
|
-
}
|
|
1256
|
-
case "block":
|
|
1257
|
-
return { ...task, status: "blocked", reason: mutation.reason.trim() };
|
|
1258
|
-
case "drop": {
|
|
1259
|
-
const dropped = { ...task, status: "cancelled" };
|
|
1260
|
-
if (mutation.reason?.trim()) dropped.reason = mutation.reason.trim();
|
|
1261
|
-
return dropped;
|
|
1262
|
-
}
|
|
1263
|
-
}
|
|
1264
|
-
}
|
|
1265
|
-
function createTaskBoardStore(deps = {}) {
|
|
1266
|
-
let cachedSessionId;
|
|
1267
|
-
let dirty = true;
|
|
1268
|
-
let board = null;
|
|
1269
|
-
const syncToSession = () => {
|
|
1270
|
-
const sessionId = deps.getSessionId?.() ?? null;
|
|
1271
|
-
if (!dirty && cachedSessionId === sessionId) return;
|
|
1272
|
-
dirty = false;
|
|
1273
|
-
cachedSessionId = sessionId;
|
|
1274
|
-
try {
|
|
1275
|
-
board = deps.readEntries ? foldTaskBoard(deps.readEntries()) : null;
|
|
1276
|
-
} catch {
|
|
1277
|
-
board = null;
|
|
1278
|
-
}
|
|
1279
|
-
};
|
|
1280
|
-
const persist = (next) => {
|
|
1281
|
-
try {
|
|
1282
|
-
deps.appendEntry?.(toTaskLedgerEntryFields(next, deps.now?.() ?? /* @__PURE__ */ new Date()));
|
|
1283
|
-
} catch {
|
|
1284
|
-
}
|
|
1285
|
-
};
|
|
1286
|
-
const mutationRequiresAcknowledgement = (current, mutation) => {
|
|
1287
|
-
if (mutation.op === "pick") return true;
|
|
1288
|
-
if (mutation.op === "plan") return current?.tasks.some(isOpenUserTask) ?? false;
|
|
1289
|
-
if (mutation.op !== "done") return false;
|
|
1290
|
-
return current?.tasks.some((task) => task.id === mutation.id && task.origin === "user") ?? false;
|
|
1291
|
-
};
|
|
1292
|
-
return {
|
|
1293
|
-
snapshot() {
|
|
1294
|
-
syncToSession();
|
|
1295
|
-
return board;
|
|
1296
|
-
},
|
|
1297
|
-
cachedSnapshot() {
|
|
1298
|
-
const sessionId = deps.getSessionId?.() ?? null;
|
|
1299
|
-
return !dirty && cachedSessionId === sessionId ? board : null;
|
|
1300
|
-
},
|
|
1301
|
-
historySnapshot() {
|
|
1302
|
-
syncToSession();
|
|
1303
|
-
try {
|
|
1304
|
-
return deps.readEntries ? foldSessionTaskHistory(deps.readEntries()) : [];
|
|
1305
|
-
} catch {
|
|
1306
|
-
return [];
|
|
1307
|
-
}
|
|
1308
|
-
},
|
|
1309
|
-
apply(mutation) {
|
|
1310
|
-
syncToSession();
|
|
1311
|
-
const result = applyMutation(board, mutation);
|
|
1312
|
-
if ("error" in result) return { ok: false, message: result.error };
|
|
1313
|
-
const needsBoardId = mutation.op === "plan" || result.board.boardId.length === 0;
|
|
1314
|
-
const nextBoard = needsBoardId ? { ...result.board, boardId: deps.createBoardId?.() ?? randomUUID() } : result.board;
|
|
1315
|
-
if (mutationRequiresAcknowledgement(board, mutation)) {
|
|
1316
|
-
if (!deps.appendEntry) {
|
|
1317
|
-
return { ok: false, message: "durable task-ledger persistence is unavailable for operator-linked work" };
|
|
1318
|
-
}
|
|
1319
|
-
try {
|
|
1320
|
-
deps.appendEntry(toTaskLedgerEntryFields(nextBoard, deps.now?.() ?? /* @__PURE__ */ new Date()));
|
|
1321
|
-
} catch (error) {
|
|
1322
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
1323
|
-
return { ok: false, message: `could not persist operator-linked task board: ${message}` };
|
|
1324
|
-
}
|
|
1325
|
-
board = nextBoard;
|
|
1326
|
-
return { ok: true, board: nextBoard, notes: result.notes };
|
|
1327
|
-
}
|
|
1328
|
-
board = nextBoard;
|
|
1329
|
-
persist(nextBoard);
|
|
1330
|
-
return { ok: true, board: nextBoard, notes: result.notes };
|
|
1331
|
-
},
|
|
1332
|
-
attachRun(runId) {
|
|
1333
|
-
syncToSession();
|
|
1334
|
-
if (board === null || runId.length === 0 || board.activeRunIds.includes(runId)) return;
|
|
1335
|
-
board = { ...board, activeRunIds: [...board.activeRunIds, runId] };
|
|
1336
|
-
persist(board);
|
|
1337
|
-
},
|
|
1338
|
-
detachRun(runId) {
|
|
1339
|
-
syncToSession();
|
|
1340
|
-
if (board === null || !board.activeRunIds.includes(runId)) return;
|
|
1341
|
-
board = { ...board, activeRunIds: board.activeRunIds.filter((id) => id !== runId) };
|
|
1342
|
-
persist(board);
|
|
1343
|
-
},
|
|
1344
|
-
invalidate() {
|
|
1345
|
-
dirty = true;
|
|
1346
|
-
}
|
|
1347
|
-
};
|
|
1348
|
-
}
|
|
1349
|
-
|
|
1350
1099
|
// src/engine/antigravity/subprocess-runtime.ts
|
|
1351
1100
|
init_esm_shims();
|
|
1352
|
-
import { spawn } from "node:child_process";
|
|
1101
|
+
import { spawn as spawn2 } from "node:child_process";
|
|
1353
1102
|
|
|
1354
1103
|
// src/engine/external-subprocess.ts
|
|
1355
1104
|
init_esm_shims();
|
|
@@ -1464,7 +1213,7 @@ async function readStdout(child, emit, state) {
|
|
|
1464
1213
|
}
|
|
1465
1214
|
function startAntigravityWorkerRun(input, emit) {
|
|
1466
1215
|
const args = buildAgyArgs(input);
|
|
1467
|
-
const child =
|
|
1216
|
+
const child = spawn2(ANTIGRAVITY_BINARY, args, {
|
|
1468
1217
|
cwd: process.cwd(),
|
|
1469
1218
|
env: process.env,
|
|
1470
1219
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -1519,7 +1268,7 @@ function startAntigravityWorkerRun(input, emit) {
|
|
|
1519
1268
|
|
|
1520
1269
|
// src/engine/claude/subprocess-runtime.ts
|
|
1521
1270
|
init_esm_shims();
|
|
1522
|
-
import { spawn as
|
|
1271
|
+
import { spawn as spawn3 } from "node:child_process";
|
|
1523
1272
|
import { createInterface } from "node:readline";
|
|
1524
1273
|
|
|
1525
1274
|
// src/engine/claude/session-id.ts
|
|
@@ -1712,7 +1461,7 @@ async function readJsonLines(child, emit, state) {
|
|
|
1712
1461
|
}
|
|
1713
1462
|
function startClaudeCodeWorkerRun(input, emit) {
|
|
1714
1463
|
const args = buildClaudeCodeArgs(input);
|
|
1715
|
-
const child =
|
|
1464
|
+
const child = spawn3("claude", args, {
|
|
1716
1465
|
cwd: process.cwd(),
|
|
1717
1466
|
env: process.env,
|
|
1718
1467
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -1778,6 +1527,271 @@ var WORKER_RUNTIME_MEDIATES_CLIO_DISPATCH = false;
|
|
|
1778
1527
|
// src/tools/builtin-tool-catalog.ts
|
|
1779
1528
|
init_esm_shims();
|
|
1780
1529
|
|
|
1530
|
+
// src/tools/bash.ts
|
|
1531
|
+
init_esm_shims();
|
|
1532
|
+
|
|
1533
|
+
// src/tools/presentation.ts
|
|
1534
|
+
init_esm_shims();
|
|
1535
|
+
var FOLDED = { foldDefault: "folded", showDiffWhenFolded: false, failureExcerpt: true };
|
|
1536
|
+
var FOLDED_WITH_DIFF = {
|
|
1537
|
+
foldDefault: "folded",
|
|
1538
|
+
showDiffWhenFolded: true,
|
|
1539
|
+
failureExcerpt: true
|
|
1540
|
+
};
|
|
1541
|
+
var TOOL_PRESENTATION = {
|
|
1542
|
+
[ToolNames.Read]: FOLDED,
|
|
1543
|
+
[ToolNames.Grep]: FOLDED,
|
|
1544
|
+
[ToolNames.Find]: FOLDED,
|
|
1545
|
+
[ToolNames.Ls]: FOLDED,
|
|
1546
|
+
[ToolNames.CodeNav]: FOLDED,
|
|
1547
|
+
[ToolNames.Context]: FOLDED,
|
|
1548
|
+
[ToolNames.CredentialPresent]: FOLDED,
|
|
1549
|
+
[ToolNames.Write]: FOLDED_WITH_DIFF,
|
|
1550
|
+
[ToolNames.Edit]: FOLDED_WITH_DIFF,
|
|
1551
|
+
[ToolNames.Bash]: FOLDED,
|
|
1552
|
+
[ToolNames.Git]: FOLDED,
|
|
1553
|
+
[ToolNames.Verify]: FOLDED,
|
|
1554
|
+
[ToolNames.Dispatch]: FOLDED,
|
|
1555
|
+
[ToolNames.Monitor]: FOLDED,
|
|
1556
|
+
[ToolNames.Steer]: FOLDED,
|
|
1557
|
+
[ToolNames.Tasks]: FOLDED,
|
|
1558
|
+
[ToolNames.Ledger]: FOLDED,
|
|
1559
|
+
[ToolNames.WebFetch]: FOLDED,
|
|
1560
|
+
[ToolNames.AskUser]: FOLDED,
|
|
1561
|
+
[ToolNames.Artifact]: FOLDED
|
|
1562
|
+
};
|
|
1563
|
+
function readStringField(args, key) {
|
|
1564
|
+
if (typeof args !== "object" || args === null || Array.isArray(args)) return null;
|
|
1565
|
+
const value = args[key];
|
|
1566
|
+
return typeof value === "string" && value.length > 0 ? value : null;
|
|
1567
|
+
}
|
|
1568
|
+
function classifyResourceRead(toolName, args) {
|
|
1569
|
+
if (toolName !== ToolNames.Read) return null;
|
|
1570
|
+
const path3 = readStringField(args, "path");
|
|
1571
|
+
if (path3 === null) return null;
|
|
1572
|
+
const normalized = path3.replace(/\\/g, "/");
|
|
1573
|
+
const base = normalized.split("/").pop() ?? "";
|
|
1574
|
+
if (base === "SKILL.md") return "skill";
|
|
1575
|
+
if (base === "CLIO-CODER.md") return "handbook";
|
|
1576
|
+
if (base === "AGENTS.md") return "agents";
|
|
1577
|
+
if (/(^|\/)docs\//.test(normalized)) return "docs";
|
|
1578
|
+
return null;
|
|
1579
|
+
}
|
|
1580
|
+
function toolPresentationPolicy(toolName, args) {
|
|
1581
|
+
if (classifyResourceRead(toolName, args) !== null) return FOLDED;
|
|
1582
|
+
return TOOL_PRESENTATION[toolName] ?? FOLDED;
|
|
1583
|
+
}
|
|
1584
|
+
|
|
1585
|
+
// src/tools/bash.ts
|
|
1586
|
+
var BASH_DISPLAY_MAX_BYTES = 16 * 1024;
|
|
1587
|
+
var BASH_OUTPUT_POLICIES = /* @__PURE__ */ new Set(["full", "bounded", "summary", "metadata-only"]);
|
|
1588
|
+
function bashContextDisposition(policy) {
|
|
1589
|
+
if (policy === "full") {
|
|
1590
|
+
return { mode: "full", maxBytes: BASH_DISPLAY_MAX_BYTES, downgradeExcerpt: "tail" };
|
|
1591
|
+
}
|
|
1592
|
+
if (policy === "summary") {
|
|
1593
|
+
return {
|
|
1594
|
+
mode: "summary",
|
|
1595
|
+
maxBytes: BASH_DISPLAY_MAX_BYTES,
|
|
1596
|
+
strategy: "diagnostic",
|
|
1597
|
+
redact: true
|
|
1598
|
+
};
|
|
1599
|
+
}
|
|
1600
|
+
if (policy === "metadata-only") return { mode: "metadata-only", maxBytes: BASH_DISPLAY_MAX_BYTES };
|
|
1601
|
+
return { mode: "bounded", maxBytes: BASH_DISPLAY_MAX_BYTES, excerpt: "tail" };
|
|
1602
|
+
}
|
|
1603
|
+
var BASH_DEFAULT_RESULT_DISPOSITION = {
|
|
1604
|
+
presentation: {
|
|
1605
|
+
...toolPresentationPolicy(ToolNames.Bash, void 0),
|
|
1606
|
+
maxBytes: BASH_DISPLAY_MAX_BYTES,
|
|
1607
|
+
overflow: "tail"
|
|
1608
|
+
},
|
|
1609
|
+
context: bashContextDisposition("bounded")
|
|
1610
|
+
};
|
|
1611
|
+
function normalizeBashArguments(args) {
|
|
1612
|
+
return args.output_policy === void 0 ? { ...args, output_policy: "bounded" } : args;
|
|
1613
|
+
}
|
|
1614
|
+
function bashOutputPolicy(args) {
|
|
1615
|
+
const value = args.output_policy;
|
|
1616
|
+
return typeof value === "string" && BASH_OUTPUT_POLICIES.has(value) ? value : null;
|
|
1617
|
+
}
|
|
1618
|
+
function resolveBashResultDisposition(args, declared = BASH_DEFAULT_RESULT_DISPOSITION) {
|
|
1619
|
+
const policy = bashOutputPolicy(args) ?? "bounded";
|
|
1620
|
+
const base = declared ?? BASH_DEFAULT_RESULT_DISPOSITION;
|
|
1621
|
+
return { presentation: base.presentation, context: bashContextDisposition(policy) };
|
|
1622
|
+
}
|
|
1623
|
+
function normalizedBashDisposition(policy) {
|
|
1624
|
+
return {
|
|
1625
|
+
presentation: { ...BASH_DEFAULT_RESULT_DISPOSITION.presentation, maxBytes: BASH_DISPLAY_MAX_BYTES },
|
|
1626
|
+
context: bashContextDisposition(policy)
|
|
1627
|
+
};
|
|
1628
|
+
}
|
|
1629
|
+
function shapeBashProgress(rawOutput, outputBytes, policy) {
|
|
1630
|
+
const disposition = normalizedBashDisposition(policy);
|
|
1631
|
+
let output;
|
|
1632
|
+
if (policy === "metadata-only") {
|
|
1633
|
+
output = `bash: running; captured ${outputBytes} bytes; stdout/stderr omitted by metadata-only policy`;
|
|
1634
|
+
} else if (policy === "summary") {
|
|
1635
|
+
output = deterministicDiagnosticSummary(rawOutput, BASH_DISPLAY_MAX_BYTES, true).text;
|
|
1636
|
+
} else {
|
|
1637
|
+
output = truncateTail(rawOutput, {
|
|
1638
|
+
maxLines: DEFAULT_MAX_LINES,
|
|
1639
|
+
maxBytes: disposition.context.maxBytes
|
|
1640
|
+
}).content;
|
|
1641
|
+
}
|
|
1642
|
+
return {
|
|
1643
|
+
kind: "ok",
|
|
1644
|
+
output,
|
|
1645
|
+
details: {
|
|
1646
|
+
resultSize: {
|
|
1647
|
+
bytes: outputBytes,
|
|
1648
|
+
shownBytes: byteLength(output),
|
|
1649
|
+
maxBytes: BASH_DISPLAY_MAX_BYTES,
|
|
1650
|
+
truncated: byteLength(rawOutput) > byteLength(output),
|
|
1651
|
+
policy
|
|
1652
|
+
}
|
|
1653
|
+
}
|
|
1654
|
+
};
|
|
1655
|
+
}
|
|
1656
|
+
function bashResultDetails(result, rawOutput, outcome) {
|
|
1657
|
+
return {
|
|
1658
|
+
outcome,
|
|
1659
|
+
exitCode: result.exitCode,
|
|
1660
|
+
signal: result.signal,
|
|
1661
|
+
timedOut: result.timedOut,
|
|
1662
|
+
aborted: result.aborted,
|
|
1663
|
+
outputCapped: result.outputCapped,
|
|
1664
|
+
outputBytes: result.outputBytes,
|
|
1665
|
+
retainedBytes: byteLength(rawOutput),
|
|
1666
|
+
stdoutBytes: byteLength(result.stdout),
|
|
1667
|
+
stderrBytes: byteLength(result.stderr)
|
|
1668
|
+
};
|
|
1669
|
+
}
|
|
1670
|
+
var OBSERVE_COMMAND_PATTERN = /^\s*(?:cat|head|tail|ls|grep|rg|find)\b/;
|
|
1671
|
+
var OBSERVE_NUDGE_SESSION_LIMIT = 64;
|
|
1672
|
+
var observeNudgeSeenSessions = /* @__PURE__ */ new Set();
|
|
1673
|
+
function observeToolsNudge(command, sessionId) {
|
|
1674
|
+
if (!OBSERVE_COMMAND_PATTERN.test(command)) return "";
|
|
1675
|
+
const key = sessionId ?? "no-session";
|
|
1676
|
+
if (observeNudgeSeenSessions.has(key)) return "";
|
|
1677
|
+
if (observeNudgeSeenSessions.size >= OBSERVE_NUDGE_SESSION_LIMIT) {
|
|
1678
|
+
const oldest = observeNudgeSeenSessions.values().next().value;
|
|
1679
|
+
if (oldest !== void 0) observeNudgeSeenSessions.delete(oldest);
|
|
1680
|
+
}
|
|
1681
|
+
observeNudgeSeenSessions.add(key);
|
|
1682
|
+
return "\n\n[note: prefer the structured observe tools over shell file inspection: read pages files with offset/limit, ls lists directories, grep and find search, code_nav maps symbols. Their results are capped and continuable; keep bash for builds, git, and scripts.]";
|
|
1683
|
+
}
|
|
1684
|
+
var bashTool = {
|
|
1685
|
+
name: ToolNames.Bash,
|
|
1686
|
+
description: "Execute a bash command inside the session workspace. output_policy controls model context: bounded keeps the diagnostic tail and is the default; summary keeps deterministic redacted diagnostics; metadata-only keeps facts and retrieval; full is admitted only within the result budget.",
|
|
1687
|
+
parameters: typebox_exports.Object({
|
|
1688
|
+
command: typebox_exports.String({ description: "Bash command to execute." }),
|
|
1689
|
+
cwd: typebox_exports.Optional(
|
|
1690
|
+
typebox_exports.String({
|
|
1691
|
+
description: "Omit this. The command already runs at the session workspace root. Set it only to run in a subdirectory of that root, as a relative path; an absolute path outside the root is blocked."
|
|
1692
|
+
})
|
|
1693
|
+
),
|
|
1694
|
+
timeout_ms: typebox_exports.Optional(typebox_exports.Number({ description: "Timeout in milliseconds." })),
|
|
1695
|
+
output_policy: typebox_exports.Optional(
|
|
1696
|
+
typebox_exports.Union([typebox_exports.Literal("full"), typebox_exports.Literal("bounded"), typebox_exports.Literal("summary"), typebox_exports.Literal("metadata-only")], {
|
|
1697
|
+
description: "Model-context disposition. Omit for bounded tail output. Use summary for noisy runs, metadata-only when only status and retrieval matter, and full only for known-small output."
|
|
1698
|
+
})
|
|
1699
|
+
)
|
|
1700
|
+
}),
|
|
1701
|
+
baseActionClass: "execute",
|
|
1702
|
+
executionMode: "sequential",
|
|
1703
|
+
prepareArguments: normalizeBashArguments,
|
|
1704
|
+
resolveResultDisposition: resolveBashResultDisposition,
|
|
1705
|
+
async run(args, options) {
|
|
1706
|
+
args = normalizeBashArguments(args);
|
|
1707
|
+
if (typeof args.command !== "string" || args.command.length === 0) {
|
|
1708
|
+
return { kind: "error", message: "bash: missing command argument" };
|
|
1709
|
+
}
|
|
1710
|
+
const outputPolicy = bashOutputPolicy(args);
|
|
1711
|
+
if (outputPolicy === null) {
|
|
1712
|
+
return {
|
|
1713
|
+
kind: "error",
|
|
1714
|
+
message: "bash: output_policy must be one of full, bounded, summary, or metadata-only (omit it for bounded)",
|
|
1715
|
+
details: { outcome: "invalid-arguments", exitCode: null }
|
|
1716
|
+
};
|
|
1717
|
+
}
|
|
1718
|
+
const cwdArg = typeof args.cwd === "string" && args.cwd.length > 0 ? args.cwd : void 0;
|
|
1719
|
+
let cwd;
|
|
1720
|
+
try {
|
|
1721
|
+
cwd = resolveSafeCwd(cwdArg === void 0 ? void 0 : expandPath(cwdArg), process.cwd());
|
|
1722
|
+
} catch {
|
|
1723
|
+
return {
|
|
1724
|
+
kind: "error",
|
|
1725
|
+
message: `bash: cwd '${cwdArg}' escapes workspace root '${process.cwd()}'; use a typed tool or a project policy entry with explicit cwd`
|
|
1726
|
+
};
|
|
1727
|
+
}
|
|
1728
|
+
const timeout = typeof args.timeout_ms === "number" && args.timeout_ms > 0 ? args.timeout_ms : 3e5;
|
|
1729
|
+
try {
|
|
1730
|
+
const runOptions = {
|
|
1731
|
+
cwd,
|
|
1732
|
+
timeoutMs: timeout,
|
|
1733
|
+
...options?.signal === void 0 ? {} : { signal: options.signal }
|
|
1734
|
+
};
|
|
1735
|
+
if (options?.onUpdate !== void 0) {
|
|
1736
|
+
runOptions.onUpdate = (progress) => {
|
|
1737
|
+
options.onUpdate?.(shapeBashProgress(combineBashOutput(progress), progress.outputBytes, outputPolicy));
|
|
1738
|
+
};
|
|
1739
|
+
}
|
|
1740
|
+
const result = await runBashCommand(args.command, runOptions);
|
|
1741
|
+
const { error, aborted, timedOut, outputCapped } = result;
|
|
1742
|
+
const rawOutput = combineBashOutput(result);
|
|
1743
|
+
const output = rawOutput.trim();
|
|
1744
|
+
if (aborted) {
|
|
1745
|
+
const status = "bash: command aborted";
|
|
1746
|
+
return {
|
|
1747
|
+
kind: "error",
|
|
1748
|
+
message: output.length > 0 ? `${output}
|
|
1749
|
+
|
|
1750
|
+
${status}` : status,
|
|
1751
|
+
details: bashResultDetails(result, rawOutput, "abort")
|
|
1752
|
+
};
|
|
1753
|
+
}
|
|
1754
|
+
if (timedOut) {
|
|
1755
|
+
const status = `bash: command timed out after ${timeout}ms`;
|
|
1756
|
+
return {
|
|
1757
|
+
kind: "error",
|
|
1758
|
+
message: output.length > 0 ? `${output}
|
|
1759
|
+
|
|
1760
|
+
${status}` : status,
|
|
1761
|
+
details: bashResultDetails(result, rawOutput, "timeout")
|
|
1762
|
+
};
|
|
1763
|
+
}
|
|
1764
|
+
if (outputCapped) {
|
|
1765
|
+
const status = `bash: command output exceeded ${BASH_HARD_CAP_BYTES} bytes and was stopped`;
|
|
1766
|
+
return {
|
|
1767
|
+
kind: "error",
|
|
1768
|
+
message: output.length > 0 ? `${output}
|
|
1769
|
+
|
|
1770
|
+
${status}` : status,
|
|
1771
|
+
details: bashResultDetails(result, rawOutput, "output-cap")
|
|
1772
|
+
};
|
|
1773
|
+
}
|
|
1774
|
+
if (error) {
|
|
1775
|
+
const code = typeof error.code === "number" ? error.code : error.code;
|
|
1776
|
+
const status = `bash: command failed (exit ${code ?? "?"})`;
|
|
1777
|
+
const message = output.length > 0 ? `${output}
|
|
1778
|
+
|
|
1779
|
+
${status}` : `${status}: ${error.message}`;
|
|
1780
|
+
return { kind: "error", message, details: bashResultDetails(result, rawOutput, "nonzero") };
|
|
1781
|
+
}
|
|
1782
|
+
const body = rawOutput.length > 0 ? rawOutput : "(no output)";
|
|
1783
|
+
return {
|
|
1784
|
+
kind: "ok",
|
|
1785
|
+
output: `${body}${observeToolsNudge(args.command, options?.sessionId)}`,
|
|
1786
|
+
details: bashResultDetails(result, rawOutput, "success")
|
|
1787
|
+
};
|
|
1788
|
+
} catch (err) {
|
|
1789
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1790
|
+
return { kind: "error", message: `bash: ${msg}` };
|
|
1791
|
+
}
|
|
1792
|
+
}
|
|
1793
|
+
};
|
|
1794
|
+
|
|
1781
1795
|
// src/tools/read.ts
|
|
1782
1796
|
init_esm_shims();
|
|
1783
1797
|
import { readFileSync, statSync } from "node:fs";
|
|
@@ -1979,7 +1993,7 @@ var TOOL_METADATA = {
|
|
|
1979
1993
|
"Use a narrower query or scope to inspect omitted content."
|
|
1980
1994
|
),
|
|
1981
1995
|
costLatency: "local_fast",
|
|
1982
|
-
promptHint: 'Call context with scope="skills" to list installed and marketplace skills; when one matches the task, or the operator names a skill or asks how one works, suggest the operator run /skill <name> (a marketplace skill is offered for install) and never load it uninvited. When the user message carries a skill request, first load that skill via context (scope="skills", name=<skill>) before doing anything else.'
|
|
1996
|
+
promptHint: 'Call context with scope="skills" to list installed and marketplace skills; when one matches the task, or the operator names a skill or asks how one works, suggest the operator run /skill <name> (a marketplace skill is offered for install) and never load it uninvited. When the user message carries a skill request, first load that skill via context (scope="skills", name=<skill>) before doing anything else. When an [evicted ...] marker names content you need, recall it with context(scope="recall", ref=...); re-read the file only when the marker says it changed.'
|
|
1983
1997
|
},
|
|
1984
1998
|
[ToolNames.CredentialPresent]: {
|
|
1985
1999
|
objective: "Check whether a credential key is present without returning its value.",
|
|
@@ -2007,15 +2021,18 @@ var TOOL_METADATA = {
|
|
|
2007
2021
|
resultSizePolicy: exactMutationPolicy,
|
|
2008
2022
|
costLatency: "local_fast"
|
|
2009
2023
|
},
|
|
2010
|
-
// EXECUTE: the safe-exec spine (
|
|
2024
|
+
// EXECUTE: the safe-exec spine (canonical shaping offloads Bash overflow).
|
|
2011
2025
|
[ToolNames.Bash]: {
|
|
2012
2026
|
objective: "Execute an explicit shell command when narrower tools are insufficient.",
|
|
2013
2027
|
uiLabel: "Shell",
|
|
2014
2028
|
retrySafety: "unknown",
|
|
2015
|
-
resultSizePolicy:
|
|
2016
|
-
"Use a narrower command or a dedicated verification/read/search tool to inspect omitted output."
|
|
2017
|
-
|
|
2018
|
-
|
|
2029
|
+
resultSizePolicy: {
|
|
2030
|
+
...summaryPolicy("Use a narrower command or a dedicated verification/read/search tool to inspect omitted output."),
|
|
2031
|
+
offloadMaxBytes: BASH_HARD_CAP_BYTES
|
|
2032
|
+
},
|
|
2033
|
+
resultDisposition: BASH_DEFAULT_RESULT_DISPOSITION,
|
|
2034
|
+
costLatency: "local_slow",
|
|
2035
|
+
promptHint: "Bash output_policy defaults to bounded diagnostic tail. Use summary for noisy commands, metadata-only when only outcome and retrieval matter, and full only when output is known to fit the bounded result budget."
|
|
2019
2036
|
},
|
|
2020
2037
|
[ToolNames.Git]: {
|
|
2021
2038
|
objective: "Read-only git inspection: status, diff, or log.",
|
|
@@ -2136,7 +2153,7 @@ function toolPromptHintsForNames(names) {
|
|
|
2136
2153
|
}
|
|
2137
2154
|
function withBuiltinMetadata(spec) {
|
|
2138
2155
|
const metadata = TOOL_METADATA[spec.name];
|
|
2139
|
-
return metadata ? withMetadata(spec, metadata) : spec;
|
|
2156
|
+
return metadata ? withMetadata(spec, { ...metadata, presentation: toolPresentationPolicy(spec.name, void 0) }) : spec;
|
|
2140
2157
|
}
|
|
2141
2158
|
function builtin(spec, sourceInfo) {
|
|
2142
2159
|
return withBuiltinMetadata(withSourceInfo(spec, sourceInfo));
|
|
@@ -2924,7 +2941,7 @@ function isValidLineage(value) {
|
|
|
2924
2941
|
// src/engine/claude/tool-safety.ts
|
|
2925
2942
|
init_esm_shims();
|
|
2926
2943
|
import { performance } from "node:perf_hooks";
|
|
2927
|
-
function
|
|
2944
|
+
function isRecord(value) {
|
|
2928
2945
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2929
2946
|
}
|
|
2930
2947
|
function stringField(record2, ...keys) {
|
|
@@ -3190,7 +3207,7 @@ function emitClaudeToolPermissionDecision(input) {
|
|
|
3190
3207
|
return decision;
|
|
3191
3208
|
}
|
|
3192
3209
|
function coerceToolInput(value) {
|
|
3193
|
-
return
|
|
3210
|
+
return isRecord(value) ? value : {};
|
|
3194
3211
|
}
|
|
3195
3212
|
|
|
3196
3213
|
// src/domains/dispatch/agent-candidates.ts
|
|
@@ -3381,6 +3398,11 @@ export {
|
|
|
3381
3398
|
emitClaudeToolPermissionDecision,
|
|
3382
3399
|
coerceToolInput,
|
|
3383
3400
|
WORKER_RUNTIME_MEDIATES_CLIO_DISPATCH,
|
|
3401
|
+
combineBashOutput,
|
|
3402
|
+
runBashCommand,
|
|
3403
|
+
classifyResourceRead,
|
|
3404
|
+
toolPresentationPolicy,
|
|
3405
|
+
bashTool,
|
|
3384
3406
|
readMaxBytes,
|
|
3385
3407
|
readTool,
|
|
3386
3408
|
toolPromptHintsForNames,
|
|
@@ -3418,11 +3440,6 @@ export {
|
|
|
3418
3440
|
runKindSupportsLiveSteering,
|
|
3419
3441
|
projectExecutionHandoffs,
|
|
3420
3442
|
DISPATCH_BRIEFING_MAX_BYTES,
|
|
3421
|
-
validateJobSpec
|
|
3422
|
-
isSessionHeader,
|
|
3423
|
-
isSessionEntry,
|
|
3424
|
-
taskBoardCounts,
|
|
3425
|
-
foldSessionTaskHistory,
|
|
3426
|
-
createTaskBoardStore
|
|
3443
|
+
validateJobSpec
|
|
3427
3444
|
};
|
|
3428
|
-
//# sourceMappingURL=chunk-
|
|
3445
|
+
//# sourceMappingURL=chunk-ZYKPLLNQ.js.map
|