@iowarp/clio-coder 0.3.1 → 0.3.2
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 +90 -2
- package/CONTRIBUTING.md +23 -23
- package/README.md +284 -613
- package/dist/{acp-FPR54DGL.js → acp-BIYHVZIM.js} +43 -53
- package/dist/{agents-OGPIHPJH.js → agents-YT6SSRIT.js} +43 -26
- package/dist/assets/codewiki.json +1 -1
- package/dist/{auth-IC3K6NIZ.js → auth-5TWEIYDN.js} +20 -12
- package/dist/{chunk-PV4JUBVJ.js → chunk-2EHAIA3X.js} +40 -21
- package/dist/{chunk-IS3ONKU3.js → chunk-2IR2NMPA.js} +6 -4
- package/dist/chunk-2SFS6XQE.js +122 -0
- package/dist/chunk-2VTFPG5O.js +48 -0
- package/dist/{chunk-MAR7Y6HW.js → chunk-3ZXDFGR5.js} +23 -16
- package/dist/chunk-4BJ5BYCE.js +61 -0
- package/dist/{chunk-474KN5II.js → chunk-4BPJXDWC.js} +111 -181
- package/dist/chunk-4KLWL3UC.js +18 -0
- package/dist/chunk-4VP4KH3K.js +962 -0
- package/dist/chunk-4ZG3XFUR.js +77 -0
- package/dist/chunk-5B2AEOW5.js +5407 -0
- package/dist/{chunk-K2ITRMHZ.js → chunk-5TSRNF4G.js} +6 -138
- package/dist/{chunk-OLBBMFRD.js → chunk-5UUP6MWO.js} +24 -62
- package/dist/chunk-65DEGPJ6.js +52 -0
- package/dist/chunk-6EJMN2Y3.js +17 -0
- package/dist/chunk-6EJV5X2W.js +16405 -0
- package/dist/chunk-6N5PTWMY.js +136 -0
- package/dist/chunk-6XLNIQDB.js +27 -0
- package/dist/{chunk-TEKV33Q5.js → chunk-77VKQEHF.js} +65 -33
- package/dist/chunk-7CR24IG7.js +242 -0
- package/dist/{chunk-M5T5VO65.js → chunk-7EYHLWU7.js} +837 -635
- package/dist/chunk-7MNJORFF.js +22 -0
- package/dist/{chunk-KY56HMHH.js → chunk-A3CYT5EX.js} +125 -31
- package/dist/chunk-AGYYIBLL.js +1069 -0
- package/dist/chunk-AO4RKG4M.js +277 -0
- package/dist/{chunk-GB6QRBXN.js → chunk-APJ265NV.js} +54 -1187
- package/dist/chunk-ARBGF5F7.js +174 -0
- package/dist/{chunk-673JJUWJ.js → chunk-BMEMKKIT.js} +2 -2
- package/dist/chunk-CBCAPZAA.js +229 -0
- package/dist/chunk-CMZWFGD2.js +352 -0
- package/dist/chunk-ECH6PKUQ.js +39 -0
- package/dist/chunk-ED4KHGC3.js +143 -0
- package/dist/chunk-EKMEHE4H.js +340 -0
- package/dist/{chunk-RPTR2H26.js → chunk-EPVUXGXG.js} +21 -15
- package/dist/chunk-FCSXB6T2.js +338 -0
- package/dist/chunk-FJ3H4MN5.js +48 -0
- package/dist/chunk-FQ4SKYE4.js +29 -0
- package/dist/chunk-G2DE3C7R.js +644 -0
- package/dist/chunk-G4BMMOKF.js +182 -0
- package/dist/{chunk-ZPY3JZ5E.js → chunk-GGXXDWE4.js} +183 -1233
- package/dist/chunk-HC4CLZ2Y.js +68 -0
- package/dist/{chunk-LU4TK2PR.js → chunk-HFSBBKSQ.js} +5 -56
- package/dist/{chunk-PIUMUEMV.js → chunk-HKIYEGME.js} +10 -6
- package/dist/chunk-I4HZDVNP.js +73 -0
- package/dist/{chunk-4QKXUHSR.js → chunk-IGLFWIYI.js} +70 -20
- package/dist/chunk-IKCO5N3L.js +162 -0
- package/dist/chunk-IR4CFBFN.js +56 -0
- package/dist/{chunk-PFEFKVGL.js → chunk-J5HN4RYU.js} +13 -11
- package/dist/{chunk-R5KLMSBV.js → chunk-J5Q24KAG.js} +2 -2
- package/dist/{chunk-K5XEMXTI.js → chunk-JVCV3ICN.js} +1 -1
- package/dist/chunk-KJ5LWLOE.js +1077 -0
- package/dist/chunk-LBMZMYH2.js +285 -0
- package/dist/{chunk-G34LV2PF.js → chunk-LBNRH5WM.js} +84 -170
- package/dist/{chunk-H6F6BYOH.js → chunk-LZSJBIVT.js} +7003 -7434
- package/dist/{chunk-HQQID6OA.js → chunk-M6SHUN7Q.js} +5 -5
- package/dist/chunk-MAW544W2.js +1882 -0
- package/dist/chunk-MBS4V7ZP.js +217 -0
- package/dist/{chunk-FST4FYJB.js → chunk-MFFY33HR.js} +99 -140
- package/dist/chunk-MNA4JGU4.js +255 -0
- package/dist/chunk-MQSRRFWA.js +3428 -0
- package/dist/{chunk-BSU2YIWB.js → chunk-MVVUPGPW.js} +131 -136
- package/dist/chunk-OAO4GE4M.js +619 -0
- package/dist/chunk-OHHN2SO4.js +5135 -0
- package/dist/chunk-OKGUZO2U.js +34 -0
- package/dist/{chunk-GAEBEQVI.js → chunk-OOJYHWRB.js} +32 -346
- package/dist/{chunk-Q5WJOSJ7.js → chunk-OQ33BKR3.js} +2 -1
- package/dist/chunk-OQE5J4C6.js +73 -0
- package/dist/{chunk-KKNLWXI6.js → chunk-ORBHGJC5.js} +8 -8
- package/dist/chunk-POHLU5DW.js +1186 -0
- package/dist/chunk-QKMUKYO7.js +4961 -0
- package/dist/{chunk-ASND7OZK.js → chunk-QTYWRVRA.js} +13 -13
- package/dist/{chunk-EYOKLTMF.js → chunk-SRF2PJNW.js} +17 -3
- package/dist/chunk-SST6Z5JA.js +80 -0
- package/dist/chunk-STBPMHSX.js +2456 -0
- package/dist/chunk-T6YILFSB.js +80 -0
- package/dist/chunk-TZTZS7QK.js +227 -0
- package/dist/chunk-UOV2BYIW.js +107 -0
- package/dist/{chunk-Q3RUPKEJ.js → chunk-V4RXGQ5Q.js} +58 -189
- package/dist/chunk-VAKQQHWR.js +434 -0
- package/dist/chunk-VG7TBQIY.js +128 -0
- package/dist/chunk-VJWL6YS5.js +244 -0
- package/dist/chunk-WEH5XRJQ.js +32 -0
- package/dist/chunk-WMSVI4G2.js +2095 -0
- package/dist/chunk-WVO7V2QY.js +797 -0
- package/dist/chunk-X4RCMKVQ.js +641 -0
- package/dist/chunk-X75S7HFS.js +374 -0
- package/dist/chunk-XN3L4EYL.js +46 -0
- package/dist/{chunk-RDLVBZEO.js → chunk-YCWGATWI.js} +6 -4
- package/dist/chunk-YHZX5GEU.js +193 -0
- package/dist/chunk-YXLYO42X.js +91 -0
- package/dist/{chunk-NMOX6HFD.js → chunk-ZDOOVTXZ.js} +29 -77
- package/dist/chunk-ZI647VB5.js +37 -0
- package/dist/{chunk-C4PTHK7P.js → chunk-ZWLZP4ZT.js} +5 -5
- package/dist/cli/index.js +62 -54
- package/dist/clio-4LY5K2AC.js +25 -0
- package/dist/code-nav-7AX6FYE6.js +600 -0
- package/dist/codewiki/build-worker.js +66 -0
- package/dist/compile-cache-CVJMMODC.js +18 -0
- package/dist/{components-DMAOEKFB.js → components-KELWS457.js} +11 -6
- package/dist/{config-IRUQ7SE4.js → config-GTLUW2PR.js} +92 -55
- package/dist/configure-R6A64DHX.js +42 -0
- package/dist/context-5VKGUVJJ.js +866 -0
- package/dist/{context-3KWFLHJG.js → context-JFZEJ7W5.js} +15 -13
- package/dist/{context-5RADCKTR.js → context-RW5HC47S.js} +71 -35
- package/dist/{context-clear-7TSNPAAI.js → context-clear-6ZHBAZZT.js} +54 -28
- package/dist/{context-index-W4RLWOQH.js → context-index-BZ4UYMTC.js} +30 -24
- package/dist/dispatch-runner-VKBRCWQC.js +1997 -0
- package/dist/{docs-5AWSPS37.js → docs-2C2LTVT2.js} +23 -10
- package/dist/{doctor-UC5NAJYQ.js → doctor-KI767GSN.js} +27 -17
- package/dist/{eval-U6TJHRLX.js → eval-XSSNATB4.js} +29 -16
- package/dist/{evidence-YEGUW4L3.js → evidence-UA6AWDQQ.js} +46 -26
- package/dist/{evolve-TXARCTPG.js → evolve-QNTFGV6Z.js} +45 -25
- package/dist/{extensions-OZFJ3A3G.js → extensions-QVDOHDGJ.js} +16 -7
- package/dist/{fleet-6G3DHNYE.js → fleet-Q7UOMUSG.js} +163 -54
- package/dist/{fleet-preflight-DSNT37JK.js → fleet-preflight-DDN536IT.js} +7 -4
- package/dist/{init-KZ5QTF6M.js → init-WBB65ZHQ.js} +69 -32
- package/dist/{memory-73ESV5YC.js → memory-MD3O64RI.js} +48 -27
- package/dist/{models-A4PVNWJK.js → models-BZU34YWD.js} +39 -25
- package/dist/monitor-MEQA5C3I.js +661 -0
- package/dist/{chunk-FCIH3BIZ.js → orchestrator-CGFKEP27.js} +11832 -8687
- package/dist/{paths-C4H6IV77.js → paths-UXLN5YYZ.js} +10 -5
- package/dist/{preload-6WVMHX3A.js → preload-P6DGH2PZ.js} +2 -2
- package/dist/{reset-BGW6OGMV.js → reset-L2FQEE3E.js} +16 -10
- package/dist/{run-YTPEYQOH.js → run-IV4Q6RLN.js} +101 -61
- package/dist/{share-YIFFV4NQ.js → share-S5BZQC5I.js} +15 -8
- package/dist/{skills-2V6RA3OQ.js → skills-LQEKRDTN.js} +34 -14
- package/dist/{skills-eval-S2TVJO4F.js → skills-eval-3DC4HEWS.js} +70 -34
- package/dist/steer-GGWFUJUD.js +77 -0
- package/dist/{targets-TYXLPB23.js → targets-C4SSGQOB.js} +43 -27
- package/dist/terminal-lease-IT5JW2NR.js +395 -0
- package/dist/{trace-GGOJ6Q6Z.js → trace-PNCASAXC.js} +41 -16
- package/dist/{chunk-N6F52NLF.js → tree-sitter-HGKH6LG4.js} +28 -2306
- package/dist/{uninstall-LLLT4F4W.js → uninstall-FZCQCDKC.js} +10 -5
- package/dist/{upgrade-33G2LMM5.js → upgrade-7TT7SQ3G.js} +45 -25
- package/dist/{usage-ZAFSXKKG.js → usage-GV4PKT3M.js} +62 -31
- package/dist/verify-G6V4D2G7.js +716 -0
- package/dist/web-fetch-2YHJ3KTG.js +638 -0
- package/dist/{wiki-generate-NUQCVOQ3.js → wiki-generate-DQF6Z66B.js} +74 -34
- package/dist/worker/entry.js +221 -36
- package/dist/workspace-G4ZWUIPR.js +22 -0
- package/docs/README.md +22 -17
- package/docs/acp.md +168 -16
- package/docs/alcf-provider.md +1 -1
- package/docs/architecture.md +136 -7
- 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 +114 -71
- package/docs/config-knobs-audit.md +1 -3
- package/docs/configuration-and-targets.md +174 -46
- package/docs/context-engine.md +29 -6
- package/docs/development-pipeline.md +26 -1
- package/docs/dispatch-architecture-rationale.md +1 -1
- package/docs/documentation-coverage.md +2 -2
- package/docs/documentation-guide.md +1 -1
- package/docs/environment-variables.md +13 -5
- package/docs/eval-runner.md +1 -1
- package/docs/evals-internal.md +1 -1
- package/docs/evidence-and-memory.md +6 -2
- package/docs/evolution.md +2 -2
- package/docs/exit-codes-and-output.md +15 -9
- package/docs/extensions-and-sharing.md +9 -9
- package/docs/fleet-dispatch.md +7 -5
- package/docs/git-commit-provenance.md +120 -0
- package/docs/glossary.md +1 -1
- package/docs/installation-and-lifecycle.md +34 -27
- package/docs/middleware-and-components.md +1 -1
- package/docs/model-catalog.md +45 -14
- package/docs/observability.md +8 -5
- package/docs/performance-methodology.md +491 -0
- package/docs/pi-boundary.md +72 -0
- package/docs/proactive-memory.md +3 -3
- package/docs/prompt-envelope-and-tools.md +24 -3
- package/docs/provider-adapter-cookbook.md +57 -4
- package/docs/release-cut-checklist.md +129 -115
- package/docs/safety-model.md +9 -5
- package/docs/scientific-validation.md +3 -3
- package/docs/session-lifecycle.md +55 -12
- package/docs/skills-marketplace.md +12 -8
- package/docs/time-conventions.md +1 -1
- package/docs/tool-usage.md +3 -3
- package/docs/trace-store.md +1 -1
- package/docs/troubleshooting.md +10 -7
- package/docs/tui-design.md +47 -10
- package/docs/worker-dispatch-mechanics.md +1 -1
- package/package.json +19 -22
- package/skills/coding/ast-grep/SKILL.md +136 -0
- package/skills/coding/ast-grep/evals.md +56 -0
- package/skills/coding/ast-grep/references/rule_reference.md +297 -0
- package/skills/coding/coding-standards/SKILL.md +113 -0
- package/skills/coding/coding-standards/evals.md +34 -0
- package/skills/coding/prototype/SKILL.md +86 -0
- package/skills/coding/prototype/evals.md +42 -0
- package/skills/coding/prototype/references/LOGIC.md +67 -0
- package/skills/coding/prototype/references/UI.md +112 -0
- package/skills/coding/tdd/SKILL.md +101 -0
- package/skills/coding/tdd/evals.md +41 -0
- package/skills/coding/tdd/references/mocking.md +59 -0
- package/skills/coding/tdd/references/tests.md +77 -0
- package/skills/context/context-handoff/SKILL.md +126 -0
- package/skills/context/context-handoff/evals.md +57 -0
- package/skills/context/context-handoff/scripts/new-handoff.sh +26 -0
- package/skills/context/context-prime/SKILL.md +95 -0
- package/skills/context/context-prime/evals.md +54 -0
- package/skills/meta/clio-dev/SKILL.md +91 -0
- package/skills/meta/clio-dev/evals.md +45 -0
- package/skills/meta/clio-test/SKILL.md +130 -0
- package/skills/meta/clio-test/evals.md +43 -0
- package/skills/meta/clio-test/references/harness.md +97 -0
- package/skills/meta/clio-test/references/test-map.md +59 -0
- package/skills/meta/credentials/SKILL.md +125 -0
- package/skills/meta/credentials/evals.md +104 -0
- package/skills/meta/find-skills/SKILL.md +72 -0
- package/skills/meta/find-skills/evals.md +47 -0
- package/skills/meta/herdr/SKILL.md +127 -0
- package/skills/meta/herdr/evals.md +38 -0
- package/skills/meta/skill-craft/SKILL.md +102 -0
- package/skills/meta/skill-craft/evals.md +41 -0
- package/skills/planning/architecture/SKILL.md +129 -0
- package/skills/planning/architecture/evals.md +36 -0
- package/skills/planning/backlog/SKILL.md +90 -0
- package/skills/planning/backlog/evals.md +43 -0
- package/skills/planning/prd/SKILL.md +82 -0
- package/skills/planning/prd/evals.md +49 -0
- package/skills/planning/product-intent/SKILL.md +112 -0
- package/skills/planning/product-intent/evals.md +36 -0
- package/skills/planning/tech-spec/SKILL.md +115 -0
- package/skills/planning/tech-spec/evals.md +47 -0
- package/skills/registry.yaml +136 -0
- package/skills/research/arxiv-literature/SKILL.md +104 -0
- package/skills/research/arxiv-literature/evals.md +58 -0
- package/skills/research/experiment-protocol/SKILL.md +122 -0
- package/skills/research/experiment-protocol/evals.md +91 -0
- package/skills/research/scientific-debugging/SKILL.md +119 -0
- package/skills/research/scientific-debugging/evals.md +138 -0
- package/skills/research/scientific-modernization/SKILL.md +138 -0
- package/skills/research/scientific-modernization/evals.md +84 -0
- package/skills/workflow/design-council/SKILL.md +139 -0
- package/skills/workflow/design-council/evals.md +97 -0
- package/skills/workflow/grill-me/SKILL.md +186 -0
- package/skills/workflow/grill-me/evals.md +78 -0
- package/skills/workflow/workflow-distiller/SKILL.md +136 -0
- package/skills/workflow/workflow-distiller/evals.md +107 -0
- package/src/cli/acp.ts +31 -4
- package/src/cli/clio.ts +68 -6
- package/src/cli/config-inspect.ts +28 -22
- package/src/cli/configure.ts +47 -9
- package/src/cli/context-clear.ts +2 -2
- package/src/cli/context-index.ts +21 -23
- package/src/cli/context.ts +13 -8
- package/src/cli/default-target.ts +9 -17
- package/src/cli/docs.ts +11 -5
- package/src/cli/evidence.ts +4 -1
- package/src/cli/extensions.ts +10 -1
- package/src/cli/fleet.ts +47 -6
- package/src/cli/index.ts +55 -26
- package/src/cli/memory.ts +3 -1
- package/src/cli/models.ts +1 -1
- package/src/cli/modes/json-stream.ts +37 -1
- package/src/cli/modes/print.ts +24 -9
- package/src/cli/run.ts +2 -2
- package/src/cli/skills-eval.ts +23 -8
- package/src/cli/skills.ts +19 -4
- package/src/cli/targets.ts +4 -0
- package/src/cli/text-layout.ts +15 -5
- package/src/cli/trace.ts +62 -14
- package/src/cli/upgrade.ts +18 -2
- package/src/cli/usage.ts +10 -3
- package/src/cli/wiki-generate.ts +2 -1
- package/src/core/agent-environment.ts +7 -0
- package/src/core/bash-exec.ts +72 -1
- package/src/core/boot-trace.ts +9 -4
- package/src/core/bus-events.ts +20 -4
- package/src/core/commit-attribution.ts +157 -0
- package/src/core/compile-cache.ts +159 -0
- package/src/core/config.ts +131 -2
- package/src/core/defaults.ts +39 -5
- package/src/core/domain-loader.ts +12 -5
- package/src/core/git-commit-attribution.ts +362 -0
- package/src/core/incomplete-installation.ts +45 -0
- package/src/core/response-schema.ts +1 -1
- package/src/core/safe-exec.ts +13 -1
- package/src/core/settings-layers.ts +155 -21
- package/src/core/skill-activation.ts +1 -1
- package/src/core/startup-timer.ts +3 -3
- package/src/core/state-file-lock.ts +13 -1
- package/src/core/termination.ts +78 -5
- package/src/domains/config/classify.ts +15 -3
- package/src/domains/config/extension.ts +19 -13
- package/src/domains/config/index.ts +10 -0
- package/src/domains/config/keybindings.ts +42 -6
- package/src/domains/context/bootstrap-prompt.ts +1 -1
- package/src/domains/context/bootstrap.ts +111 -18
- package/src/domains/context/clear.ts +16 -11
- package/src/domains/context/clio-md.ts +111 -9
- package/src/domains/context/codewiki/artifact.ts +400 -0
- package/src/domains/context/codewiki/build-worker-protocol.ts +24 -0
- package/src/domains/context/codewiki/build-worker.ts +54 -0
- package/src/domains/context/codewiki/coordinator.ts +182 -0
- package/src/domains/context/codewiki/indexer.ts +59 -144
- package/src/domains/context/codewiki/paths.ts +67 -0
- package/src/domains/context/codewiki/schema.ts +80 -0
- package/src/domains/context/codewiki/tree-sitter.ts +1 -1
- package/src/domains/context/contract.ts +11 -5
- package/src/domains/context/extension.ts +94 -143
- package/src/domains/context/fingerprint.ts +3 -1
- package/src/domains/context/index.ts +12 -22
- package/src/domains/context/project-metadata.ts +19 -0
- package/src/domains/context/prompt-context.ts +9 -10
- package/src/domains/context/refresh.ts +29 -21
- package/src/domains/context/runtime.ts +17 -0
- package/src/domains/context/wiki/generate.ts +39 -34
- package/src/domains/context/wiki/plan.ts +1 -1
- package/src/domains/context/wiki/prompts.ts +21 -8
- package/src/domains/dispatch/code-step.ts +20 -1
- package/src/domains/dispatch/extension.ts +158 -21
- package/src/domains/dispatch/failure-classification.ts +6 -0
- package/src/domains/dispatch/fleet-commit-attribution.ts +56 -0
- package/src/domains/dispatch/orphan-recovery.ts +50 -8
- package/src/domains/dispatch/receipt-integrity.ts +5 -0
- package/src/domains/dispatch/state.ts +31 -6
- package/src/domains/dispatch/transport.ts +2 -1
- package/src/domains/dispatch/types.ts +14 -0
- package/src/domains/dispatch/worker-spawn.ts +21 -2
- package/src/domains/eval/metrics/context.ts +1 -1
- package/src/domains/eval/types.ts +0 -1
- package/src/domains/evidence/build.ts +41 -1
- package/src/domains/lifecycle/migrations/2026-08-18-lmstudio-runtime-id.ts +52 -0
- package/src/domains/lifecycle/migrations/index.ts +24 -4
- package/src/domains/middleware/hooks-io.ts +12 -0
- package/src/domains/middleware/skills-reminder.ts +30 -15
- package/src/domains/prompts/compiler.ts +142 -84
- package/src/domains/prompts/contract.ts +18 -2
- package/src/domains/prompts/extension.ts +39 -7
- package/src/domains/prompts/fragment-loader.ts +0 -1
- package/src/domains/prompts/fragments/identity/clio.md +2 -4
- package/src/domains/prompts/fragments/identity/docs-routing.md +10 -0
- package/src/domains/prompts/fragments/identity/self-awareness.md +1 -45
- package/src/domains/prompts/fragments/operating/contract.md +4 -50
- package/src/domains/prompts/fragments/operating/delegation.md +42 -0
- package/src/domains/prompts/fragments/operating/skills.md +26 -0
- package/src/domains/prompts/fragments/operating/worker.md +16 -0
- package/src/domains/prompts/fragments/safety/auto-edit.md +5 -5
- package/src/domains/prompts/fragments/safety/full-auto.md +3 -3
- package/src/domains/prompts/fragments/safety/read-only.md +4 -4
- package/src/domains/prompts/fragments/safety/suggest.md +2 -2
- package/src/domains/prompts/fragments/wiki/page.md +10 -0
- package/src/domains/prompts/fragments/wiki/plan.md +10 -0
- package/src/domains/prompts/preload.ts +3 -3
- package/src/domains/providers/auth/api-key.ts +1 -1
- package/src/domains/providers/auth/backend-file.ts +20 -10
- package/src/domains/providers/auth/backend-memory.ts +59 -4
- package/src/domains/providers/auth/boot-status.ts +65 -0
- package/src/domains/providers/auth/oauth.ts +2 -1
- package/src/domains/providers/auth/storage.ts +97 -38
- package/src/domains/providers/capabilities.ts +12 -4
- package/src/domains/providers/contract.ts +15 -4
- package/src/domains/providers/extension.ts +18 -6
- package/src/domains/providers/model-runtime-capabilities.ts +15 -4
- package/src/domains/providers/models/local-models/clio-local-coding-targets.yaml +118 -35
- package/src/domains/providers/plugins.ts +5 -3
- package/src/domains/providers/probe/fingerprint.ts +25 -5
- package/src/domains/providers/registry.ts +31 -10
- package/src/domains/providers/runtimes/boot-manifest.ts +55 -0
- package/src/domains/providers/runtimes/builtins.ts +2 -2
- package/src/domains/providers/runtimes/common/lmstudio-http.ts +423 -0
- package/src/domains/providers/runtimes/common/local-synth.ts +6 -7
- package/src/domains/providers/runtimes/local-native/lmstudio.ts +241 -0
- package/src/domains/providers/support.ts +6 -3
- package/src/domains/providers/types/local-model-quirks.ts +7 -9
- package/src/domains/providers/types/runtime-descriptor.ts +12 -1
- package/src/domains/providers/types/target-descriptor.ts +22 -0
- package/src/domains/resources/contract.ts +0 -1
- package/src/domains/resources/extension.ts +1 -3
- package/src/domains/resources/loader.ts +3 -4
- package/src/domains/resources/prompts/loader.ts +16 -2
- package/src/domains/resources/prompts/substitute.ts +1 -65
- package/src/domains/resources/skills/content-hash.ts +2 -0
- package/src/domains/resources/skills/install.ts +17 -0
- package/src/domains/resources/skills/loader.ts +17 -10
- package/src/domains/resources/skills/marketplace.ts +55 -9
- package/src/domains/safety/action-classifier.ts +4 -2
- package/src/domains/safety/audit.ts +8 -2
- package/src/domains/safety/extension.ts +1 -1
- package/src/domains/session/compaction/branch-summary.ts +3 -2
- package/src/domains/session/compaction/cut-point.ts +2 -1
- package/src/domains/session/compaction/tokens.ts +2 -1
- package/src/domains/session/context-ledger.ts +14 -0
- package/src/domains/session/contract.ts +15 -0
- package/src/domains/session/decision-board.ts +190 -0
- package/src/domains/session/entries.ts +66 -3
- package/src/domains/session/extension.ts +93 -12
- package/src/domains/session/retry.ts +10 -18
- package/src/domains/session/session-artifacts.ts +107 -0
- package/src/domains/session/task-board.ts +207 -13
- package/src/domains/session/tree/active-path.ts +44 -5
- package/src/domains/session/tree/fork.ts +26 -27
- package/src/domains/session/tree/preview.ts +2 -2
- package/src/domains/session/workspace/git-probe.ts +17 -11
- package/src/domains/user-tasks/store.ts +297 -0
- package/src/engine/acp/errors.ts +96 -0
- package/src/engine/acp/server.ts +1728 -146
- package/src/engine/acp/transport.ts +135 -14
- package/src/engine/acp/types.ts +26 -0
- package/src/engine/agent.ts +3 -3
- package/src/engine/ai.ts +32 -27
- package/src/engine/alcf-oauth.ts +26 -19
- package/src/engine/api-registry.ts +223 -0
- package/src/engine/apis/index.ts +3 -7
- package/src/engine/apis/llamacpp-residency.ts +49 -9
- package/src/engine/apis/lmstudio-residency.ts +5 -21
- package/src/engine/apis/lmstudio.ts +243 -0
- package/src/engine/apis/ollama-native.ts +24 -3
- package/src/engine/apis/openai-completions.ts +170 -91
- package/src/engine/apis/residency.ts +139 -3
- package/src/engine/apis/types.ts +16 -0
- package/src/engine/env-api-keys.ts +98 -0
- package/src/engine/gemma-channel-filter.ts +223 -0
- package/src/engine/instrumented-tui.ts +192 -0
- package/src/engine/messages.ts +14 -0
- package/src/engine/models.ts +42 -0
- package/src/engine/oauth.ts +16 -12
- package/src/engine/prompt-templates.ts +1 -0
- package/src/engine/provider-payload.ts +16 -59
- package/src/engine/strip-tokenizer-sentinels.ts +1 -1
- package/src/engine/truncate.ts +9 -0
- package/src/engine/tui.ts +17 -9
- package/src/engine/types.ts +3 -6
- package/src/engine/worker-runtime-capabilities.ts +5 -0
- package/src/engine/worker-runtime.ts +1 -1
- package/src/engine/worker-tools.ts +9 -4
- package/src/entry/boot-options.ts +50 -0
- package/src/entry/orchestrator.ts +288 -150
- package/src/interactive/application-controller.ts +89 -2
- package/src/interactive/chat-loop.ts +266 -41
- package/src/interactive/chat-panel.ts +173 -47
- package/src/interactive/chat-renderer.ts +262 -72
- package/src/interactive/clio-editor.ts +3 -8
- package/src/interactive/command-fallbacks.ts +2 -2
- package/src/interactive/context-overlay.ts +27 -1
- package/src/interactive/editor-submit.ts +228 -24
- package/src/interactive/export-html/ansi-to-html.ts +161 -0
- package/src/interactive/export-html/index.ts +51 -0
- package/src/interactive/export-html/template.ts +45 -0
- package/src/interactive/export-html/tool-renderer.ts +54 -0
- package/src/interactive/footer/dashboard.ts +4 -0
- package/src/interactive/footer/notifications.ts +1 -1
- package/src/interactive/footer/widgets.ts +20 -2
- package/src/interactive/footer-panel.ts +2 -2
- package/src/interactive/format-time.ts +14 -2
- package/src/interactive/interactive-application.ts +201 -17
- package/src/interactive/interactive-event-projection.ts +6 -1
- package/src/interactive/interactive-input-runtime.ts +50 -4
- package/src/interactive/interactive-presentation.ts +151 -19
- package/src/interactive/interactive-shell.ts +268 -14
- package/src/interactive/interactive-slash-runtime.ts +176 -114
- package/src/interactive/interactive-tickers.ts +38 -7
- package/src/interactive/keybinding-manager.ts +1 -1
- package/src/interactive/layout.ts +40 -3
- package/src/interactive/overlay-frame.ts +1 -1
- package/src/interactive/overlay-general-openers.ts +58 -1
- package/src/interactive/overlay-key-routing.ts +3 -0
- package/src/interactive/overlay-lifecycle.ts +13 -0
- package/src/interactive/overlay-permission-lifecycle.ts +2 -1
- package/src/interactive/overlay-session-lifecycle.ts +69 -12
- package/src/interactive/overlays/decisions.ts +300 -0
- package/src/interactive/overlays/help-reference.ts +15 -10
- package/src/interactive/overlays/model-selector.ts +34 -16
- package/src/interactive/overlays/session-selector.ts +18 -0
- package/src/interactive/overlays/settings.ts +105 -17
- package/src/interactive/overlays/skills-hub.ts +4 -4
- package/src/interactive/overlays/tree-selector.ts +41 -6
- package/src/interactive/render-trace.ts +499 -90
- package/src/interactive/renderers/compaction-summary.ts +2 -2
- package/src/interactive/renderers/diff.ts +115 -104
- package/src/interactive/renderers/mermaid.ts +53 -0
- package/src/interactive/renderers/tool-execution.ts +386 -133
- package/src/interactive/renderers/worker-entry.ts +20 -4
- package/src/interactive/session-switch-settlement.ts +10 -0
- package/src/interactive/slash-autocomplete.ts +6 -114
- package/src/interactive/slash-commands.ts +135 -47
- package/src/interactive/slash-spec.ts +9 -38
- package/src/interactive/status/controller.ts +5 -1
- package/src/interactive/stdout-backpressure.ts +99 -0
- package/src/interactive/stream-pacer.ts +530 -0
- package/src/interactive/stream-pacing-policy.ts +66 -0
- package/src/interactive/tasks-overlay.ts +368 -14
- package/src/interactive/terminal-lease.ts +485 -0
- package/src/interactive/theme/tokens.ts +1 -1
- package/src/interactive/turn-context.ts +4 -3
- package/src/interactive/turn-persistence.ts +30 -13
- package/src/interactive/turn-queues.ts +12 -0
- package/src/interactive/turn-recovery.ts +25 -8
- package/src/interactive/turn-runtime.ts +79 -12
- package/src/interactive/turn-state.ts +10 -0
- package/src/interactive/view/artifacts.ts +114 -4
- package/src/interactive/view/view-overlay.ts +3 -0
- package/src/interactive/welcome-dashboard.ts +17 -16
- package/src/interactive/worker-receipts.ts +52 -3
- package/src/interactive/worker-stream.ts +5 -1
- package/src/tools/agent-tools.ts +23 -3
- package/src/tools/artifact.ts +2 -2
- package/src/tools/ask-user.ts +23 -13
- package/src/tools/bash.ts +30 -2
- package/src/tools/bootstrap.ts +34 -431
- package/src/tools/builtin-tool-catalog.ts +265 -0
- package/src/tools/codewiki/code-nav-surface.ts +29 -0
- package/src/tools/codewiki/code-nav.ts +8 -22
- package/src/tools/codewiki/shared.ts +41 -38
- package/src/tools/context/docs-engine.ts +14 -3
- package/src/tools/context/index.ts +107 -28
- package/src/tools/context/surface.ts +19 -0
- package/src/tools/core-bootstrap.ts +168 -0
- package/src/tools/credential-present.ts +5 -5
- package/src/tools/dispatch-admission.ts +533 -0
- package/src/tools/dispatch-background.ts +54 -0
- package/src/tools/dispatch-event-text.ts +6 -0
- package/src/tools/dispatch-plan.ts +9 -4
- package/src/tools/dispatch-run-events.ts +238 -0
- package/src/tools/dispatch-runner.ts +2370 -0
- package/src/tools/dispatch-scout-admission.ts +295 -0
- package/src/tools/dispatch-types.ts +77 -0
- package/src/tools/dispatch.ts +67 -3161
- package/src/tools/find.ts +4 -2
- package/src/tools/grep.ts +2 -2
- package/src/tools/lazy-tool.ts +60 -0
- package/src/tools/ledger.ts +3 -3
- package/src/tools/monitor-surface.ts +36 -0
- package/src/tools/monitor.ts +2 -32
- package/src/tools/observers.ts +2 -2
- package/src/tools/registry.ts +39 -27
- package/src/tools/safe-exec.ts +2 -2
- package/src/tools/steer-surface.ts +17 -0
- package/src/tools/steer.ts +2 -13
- package/src/tools/tasks.ts +108 -11
- package/src/tools/truncate.ts +25 -184
- package/src/tools/verify/frontend.ts +3 -1
- package/src/tools/verify/index.ts +3 -38
- package/src/tools/verify/surface.ts +46 -0
- package/src/tools/web-fetch-surface.ts +23 -0
- package/src/tools/web-fetch.ts +2 -20
- package/src/tools/write.ts +7 -2
- package/src/worker/entry.ts +39 -2
- package/src/worker/spec-contract.ts +26 -5
- package/dist/chunk-7SS2CTV2.js +0 -61361
- package/dist/chunk-DKGKUHFA.js +0 -924
- package/dist/chunk-GEP36Y4X.js +0 -12796
- package/dist/chunk-XYWBQRDM.js +0 -137
- package/dist/clio-BZVGEUFJ.js +0 -58
- package/dist/configure-S7S6F6CL.js +0 -32
- package/docs/html/agents_blueprint.html +0 -936
- package/docs/html/alcf_blueprint.html +0 -324
- package/docs/html/architecture_blueprint.html +0 -850
- package/docs/html/commands_blueprint.html +0 -939
- package/docs/html/config_knobs_audit_blueprint.html +0 -178
- package/docs/html/configuration_blueprint.html +0 -1080
- package/docs/html/context_blueprint.html +0 -603
- package/docs/html/documentation_blueprint.html +0 -832
- package/docs/html/environment_blueprint.html +0 -404
- package/docs/html/eval_blueprint.html +0 -743
- package/docs/html/evals_internal_blueprint.html +0 -190
- package/docs/html/evolution_blueprint.html +0 -674
- package/docs/html/extensions_blueprint.html +0 -2065
- package/docs/html/fleet_dispatch_blueprint.html +0 -286
- package/docs/html/index.html +0 -919
- package/docs/html/lifecycle_blueprint.html +0 -723
- package/docs/html/memory_blueprint.html +0 -699
- package/docs/html/middleware_blueprint.html +0 -664
- package/docs/html/models_blueprint.html +0 -2366
- package/docs/html/observability_blueprint.html +0 -683
- package/docs/html/provider_adapter_blueprint.html +0 -245
- package/docs/html/safety_blueprint.html +0 -1386
- package/docs/html/shared.css +0 -571
- package/docs/html/shared.js +0 -143
- package/docs/html/skills_blueprint.html +0 -671
- package/docs/html/soak_blueprint.html +0 -182
- package/docs/html/tool_usage_blueprint.html +0 -350
- package/docs/html/tools_blueprint.html +0 -2249
- package/docs/html/trace_blueprint.html +0 -235
- package/docs/html/tui_design_blueprint.html +0 -374
- package/docs/html/validation_blueprint.html +0 -961
- package/docs/html/worker_dispatch_blueprint.html +0 -231
- package/src/core/release.ts +0 -2
- package/src/domains/providers/runtimes/common/lmstudio-logger.ts +0 -32
- package/src/domains/providers/runtimes/local-native/lmstudio-native.ts +0 -491
- package/src/engine/apis/lmstudio-native.ts +0 -1438
- package/src/engine/apis/thinking-replay.ts +0 -11
- package/src/tools/string-enum.ts +0 -15
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { runBashCommand } from "../core/bash-exec.js";
|
|
1
|
+
import { combineBashOutput, runBashCommand } from "../core/bash-exec.js";
|
|
2
|
+
import type { PendingSkillRequest } from "../core/skill-activation.js";
|
|
2
3
|
import type { DispatchContract } from "../domains/dispatch/contract.js";
|
|
3
4
|
import type { SessionContract, SessionEntry } from "../domains/session/index.js";
|
|
4
5
|
import type { ChatLoop } from "./chat-loop.js";
|
|
5
6
|
import type { ChatPanel } from "./chat-panel.js";
|
|
6
|
-
import { renderBashExecutionEntry } from "./chat-renderer.js";
|
|
7
7
|
import { bashExecutionEntryInput, parseEditorBashCommand } from "./editor-bash.js";
|
|
8
8
|
import {
|
|
9
9
|
formatSteerCandidates,
|
|
@@ -12,7 +12,8 @@ import {
|
|
|
12
12
|
resolveSteerTarget,
|
|
13
13
|
} from "./editor-steer.js";
|
|
14
14
|
import { type ExternalEditResult, editTextExternally, resolveExternalEditor } from "./external-editor.js";
|
|
15
|
-
import {
|
|
15
|
+
import { type BashTranscriptExecution, renderBashTranscriptExecution } from "./renderers/tool-execution.js";
|
|
16
|
+
import { parseSlashCommand, type RunIo, type SlashCommand, type SlashCommandDispatchResult } from "./slash-commands.js";
|
|
16
17
|
|
|
17
18
|
const EDITOR_BASH_TIMEOUT_MS = 300_000;
|
|
18
19
|
|
|
@@ -26,13 +27,8 @@ const EDITOR_BASH_TIMEOUT_MS = 300_000;
|
|
|
26
27
|
* operator can see which flag the usage line is complaining about and fix that
|
|
27
28
|
* one token.
|
|
28
29
|
*
|
|
29
|
-
* `unknown-command`
|
|
30
|
-
*
|
|
31
|
-
* against, and the restored text has no cursor placement of its own: the next
|
|
32
|
-
* keystrokes land in front of the leftover `/token`, which no longer parses as
|
|
33
|
-
* a command, so the whole concatenation goes to the model as a chat message the
|
|
34
|
-
* operator never wrote. An empty line after "is not a command" costs a retype
|
|
35
|
-
* and says exactly what happened.
|
|
30
|
+
* `unknown-command` cannot be decided here: a prompt template can still claim
|
|
31
|
+
* it during dispatch. Its post-dispatch verdict is handled at the call site.
|
|
36
32
|
*
|
|
37
33
|
* A command that ran and failed has done work, and chat text belongs to the
|
|
38
34
|
* transcript, so both also leave the line empty.
|
|
@@ -44,6 +40,10 @@ function isRejectedCommand(command: SlashCommand): boolean {
|
|
|
44
40
|
export interface EditorSubmitExpansion {
|
|
45
41
|
text: string;
|
|
46
42
|
images: ReadonlyArray<unknown>;
|
|
43
|
+
/** Paths the draft referenced inline; the idle path hands them to the chat loop for rule scoping. */
|
|
44
|
+
workingContextPaths?: ReadonlyArray<string>;
|
|
45
|
+
/** Skill requests parsed out of the draft; the idle path hands them to the chat loop as the pending-skill policy. */
|
|
46
|
+
pendingSkillRequests?: ReadonlyArray<PendingSkillRequest>;
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
export interface EditorSubmitEditor {
|
|
@@ -58,13 +58,18 @@ export interface EditorSubmitUi {
|
|
|
58
58
|
requestRender(force?: boolean): void;
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
-
type EditorSubmitChat = Pick<
|
|
61
|
+
type EditorSubmitChat = Pick<
|
|
62
|
+
ChatLoop,
|
|
63
|
+
"clearQueuedFollowUps" | "interruptRefusal" | "isStreaming" | "queueFollowUp" | "submit" | "whenSettled"
|
|
64
|
+
>;
|
|
62
65
|
type EditorSubmitDispatch = Pick<DispatchContract, "snapshot" | "steer">;
|
|
63
66
|
type EditorSubmitSession = Pick<SessionContract, "appendEntry" | "current" | "tree">;
|
|
64
67
|
|
|
65
68
|
export interface EditorSubmitSessionTranscript {
|
|
66
69
|
ensureSessionForLocalEntry(): void;
|
|
67
70
|
refreshChatContextFromSession(leafTurnId: string | null): void;
|
|
71
|
+
/** Count a prompt the operator submitted; the idle path does this through the slash runtime. */
|
|
72
|
+
recordSubmittedTurn(): void;
|
|
68
73
|
}
|
|
69
74
|
|
|
70
75
|
export interface EditorSubmitDeps {
|
|
@@ -76,7 +81,10 @@ export interface EditorSubmitDeps {
|
|
|
76
81
|
session?: EditorSubmitSession;
|
|
77
82
|
sessionTranscript: EditorSubmitSessionTranscript;
|
|
78
83
|
chatPanel: Pick<ChatPanel, "appendReplayBlock">;
|
|
79
|
-
|
|
84
|
+
beforeSemanticBoundary?: (reason: string) => void;
|
|
85
|
+
settleVisibleFrame?: (reason: string) => Promise<void>;
|
|
86
|
+
dispatchCommand: (text: string) => SlashCommandDispatchResult;
|
|
87
|
+
dispatchCommandAsync?: (text: string, signal?: AbortSignal) => Promise<void>;
|
|
80
88
|
/** Idempotently collapses a fresh-session launchpad before any handler can append output. */
|
|
81
89
|
collapseLaunchpadBeforeSubmit?: () => void;
|
|
82
90
|
expandSubmit: (text: string) => Promise<EditorSubmitExpansion>;
|
|
@@ -93,15 +101,27 @@ export interface EditorSubmitController {
|
|
|
93
101
|
openExternalEditorForInput(): void;
|
|
94
102
|
handleEditorSteerMention(mention: { target: string; text: string }): boolean;
|
|
95
103
|
submitEditorText(text: string): void;
|
|
104
|
+
/** Admit an immutable boot record without reading or mutating the live draft. */
|
|
105
|
+
admitCapturedText(text: string, signal?: AbortSignal): Promise<void>;
|
|
96
106
|
queueFollowUpFromEditor(): void;
|
|
107
|
+
/**
|
|
108
|
+
* Interrupt mode: cancel the active run and deliver the draft now. Idle, it
|
|
109
|
+
* is a plain send. The chat loop owns cancel → settle → submit and the two
|
|
110
|
+
* refusals (attached dispatch, parked permission ask), which degrade the
|
|
111
|
+
* message to next-slot delivery with a notice.
|
|
112
|
+
*/
|
|
113
|
+
interruptFromEditor(): void;
|
|
97
114
|
restoreQueuedFollowUpsToEditor(): void;
|
|
98
115
|
hasActiveEditorBash(): boolean;
|
|
99
116
|
cancelActiveEditorBash(): boolean;
|
|
100
117
|
}
|
|
101
118
|
|
|
119
|
+
type EditorSteerSubmission = "unhandled" | "accepted" | "rejected";
|
|
120
|
+
|
|
102
121
|
/** Owns editor submission and the one local bash process attached to it. */
|
|
103
122
|
export function createEditorSubmitController(deps: EditorSubmitDeps): EditorSubmitController {
|
|
104
123
|
let activeEditorBash: AbortController | null = null;
|
|
124
|
+
let activeEditorBashSettlement: Promise<void> | null = null;
|
|
105
125
|
|
|
106
126
|
const runEditorBash = (text: string): boolean => {
|
|
107
127
|
const parsed = parseEditorBashCommand(text);
|
|
@@ -126,13 +146,39 @@ export function createEditorSubmitController(deps: EditorSubmitDeps): EditorSubm
|
|
|
126
146
|
activeEditorBash = null;
|
|
127
147
|
return true;
|
|
128
148
|
}
|
|
149
|
+
const startedAt = performance.now();
|
|
150
|
+
const execution: BashTranscriptExecution = {
|
|
151
|
+
command: parsed.command,
|
|
152
|
+
output: "",
|
|
153
|
+
running: true,
|
|
154
|
+
totalBytes: 0,
|
|
155
|
+
excludeFromContext: parsed.excludeFromContext,
|
|
156
|
+
};
|
|
157
|
+
deps.chatPanel.appendReplayBlock(
|
|
158
|
+
(width) =>
|
|
159
|
+
renderBashTranscriptExecution(
|
|
160
|
+
{
|
|
161
|
+
...execution,
|
|
162
|
+
...(execution.running ? { elapsedMs: Math.max(0, performance.now() - startedAt) } : {}),
|
|
163
|
+
},
|
|
164
|
+
width,
|
|
165
|
+
),
|
|
166
|
+
() => execution.running,
|
|
167
|
+
);
|
|
168
|
+
deps.ui.requestRender();
|
|
129
169
|
|
|
130
|
-
|
|
170
|
+
let settlement!: Promise<void>;
|
|
171
|
+
settlement = (async () => {
|
|
131
172
|
try {
|
|
132
173
|
const result = await (deps.runBash ?? runBashCommand)(parsed.command, {
|
|
133
174
|
cwd: deps.getCwd?.() ?? process.cwd(),
|
|
134
175
|
timeoutMs: EDITOR_BASH_TIMEOUT_MS,
|
|
135
176
|
signal: abort.signal,
|
|
177
|
+
onUpdate: (progress) => {
|
|
178
|
+
execution.output = combineBashOutput(progress);
|
|
179
|
+
execution.totalBytes = progress.outputBytes;
|
|
180
|
+
deps.ui.requestRender();
|
|
181
|
+
},
|
|
136
182
|
});
|
|
137
183
|
const input = bashExecutionEntryInput({
|
|
138
184
|
command: parsed.command,
|
|
@@ -149,17 +195,34 @@ export function createEditorSubmitController(deps: EditorSubmitDeps): EditorSubm
|
|
|
149
195
|
timestamp: deps.nowIso?.() ?? new Date().toISOString(),
|
|
150
196
|
} as SessionEntry);
|
|
151
197
|
if (entry.kind === "bashExecution") {
|
|
152
|
-
|
|
198
|
+
execution.output = entry.output;
|
|
199
|
+
execution.running = false;
|
|
200
|
+
execution.exitCode = entry.exitCode;
|
|
201
|
+
execution.cancelled = entry.cancelled;
|
|
202
|
+
execution.truncated = entry.truncated;
|
|
203
|
+
execution.fullOutputPath = entry.fullOutputPath;
|
|
204
|
+
execution.totalBytes = Math.max(execution.totalBytes ?? 0, Buffer.byteLength(combineBashOutput(result), "utf8"));
|
|
153
205
|
}
|
|
154
|
-
|
|
206
|
+
// `parentTurnId` was the leaf when the command started and stays the
|
|
207
|
+
// entry's anchor. The chat leaf is a different question: a prompt
|
|
208
|
+
// may have landed while the command ran, so restore the leaf the
|
|
209
|
+
// session has now, not the one captured then. Restoring the stale
|
|
210
|
+
// one wedged every later submit on a parent that was no longer
|
|
211
|
+
// the leaf.
|
|
212
|
+
deps.sessionTranscript.refreshChatContextFromSession(deps.session?.tree().leafId ?? parentTurnId);
|
|
155
213
|
} catch (err) {
|
|
156
214
|
const msg = err instanceof Error ? err.message : String(err);
|
|
215
|
+
execution.running = false;
|
|
216
|
+
execution.error = msg;
|
|
157
217
|
deps.io.stderr(`[bash] ${msg}\n`);
|
|
158
218
|
} finally {
|
|
159
219
|
if (activeEditorBash === abort) activeEditorBash = null;
|
|
220
|
+
if (activeEditorBashSettlement === settlement) activeEditorBashSettlement = null;
|
|
160
221
|
deps.ui.requestRender();
|
|
161
222
|
}
|
|
162
223
|
})();
|
|
224
|
+
activeEditorBashSettlement = settlement;
|
|
225
|
+
void settlement;
|
|
163
226
|
return true;
|
|
164
227
|
};
|
|
165
228
|
|
|
@@ -186,14 +249,14 @@ export function createEditorSubmitController(deps: EditorSubmitDeps): EditorSubm
|
|
|
186
249
|
deps.ui.requestRender(true);
|
|
187
250
|
};
|
|
188
251
|
|
|
189
|
-
const
|
|
252
|
+
const submitEditorSteerMention = (mention: { target: string; text: string }): EditorSteerSubmission => {
|
|
190
253
|
let running: RunningDispatchRef[] = [];
|
|
191
254
|
try {
|
|
192
255
|
running = deps.dispatch.snapshot().running.map((run) => ({ runId: run.runId, agentId: run.agentId }));
|
|
193
256
|
} catch {
|
|
194
257
|
running = [];
|
|
195
258
|
}
|
|
196
|
-
if (running.length === 0) return
|
|
259
|
+
if (running.length === 0) return "unhandled";
|
|
197
260
|
const resolution = resolveSteerTarget(mention.target, running);
|
|
198
261
|
if (resolution.kind === "match") {
|
|
199
262
|
try {
|
|
@@ -203,11 +266,12 @@ export function createEditorSubmitController(deps: EditorSubmitDeps): EditorSubm
|
|
|
203
266
|
`steer queued for ${resolution.run.agentId} (${resolution.run.runId}); awaiting worker acknowledgement`,
|
|
204
267
|
`steer:${resolution.run.runId}`,
|
|
205
268
|
);
|
|
269
|
+
return "accepted";
|
|
206
270
|
} catch (err) {
|
|
207
271
|
const msg = err instanceof Error ? err.message : String(err);
|
|
208
272
|
deps.notify("error", `steer to @${mention.target} failed: ${msg}`, `steer:${mention.target}`);
|
|
273
|
+
return "rejected";
|
|
209
274
|
}
|
|
210
|
-
return true;
|
|
211
275
|
}
|
|
212
276
|
if (resolution.kind === "ambiguous") {
|
|
213
277
|
deps.notify(
|
|
@@ -215,34 +279,127 @@ export function createEditorSubmitController(deps: EditorSubmitDeps): EditorSubm
|
|
|
215
279
|
`@${mention.target} matches ${resolution.candidates.length} runs: ${formatSteerCandidates(resolution.candidates)}; use a runId prefix`,
|
|
216
280
|
`steer:${mention.target}`,
|
|
217
281
|
);
|
|
218
|
-
return
|
|
282
|
+
return "rejected";
|
|
219
283
|
}
|
|
220
284
|
deps.notify(
|
|
221
285
|
"warning",
|
|
222
286
|
`no running dispatch matches @${mention.target}; running: ${formatSteerCandidates(running)}`,
|
|
223
287
|
`steer:${mention.target}`,
|
|
224
288
|
);
|
|
225
|
-
return
|
|
289
|
+
return "rejected";
|
|
226
290
|
};
|
|
227
291
|
|
|
292
|
+
const handleEditorSteerMention = (mention: { target: string; text: string }): boolean =>
|
|
293
|
+
submitEditorSteerMention(mention) !== "unhandled";
|
|
294
|
+
|
|
228
295
|
const submitEditorText = (text: string): void => {
|
|
229
296
|
const trimmed = text.trim();
|
|
230
297
|
if (trimmed.length === 0) return;
|
|
231
298
|
deps.collapseLaunchpadBeforeSubmit?.();
|
|
232
299
|
if (parseEditorBashCommand(text)) {
|
|
233
|
-
if (!deps.chat.isStreaming() && !activeEditorBash)
|
|
300
|
+
if (!deps.chat.isStreaming() && !activeEditorBash) {
|
|
301
|
+
deps.editor.addToHistory(text);
|
|
302
|
+
deps.editor.setText("");
|
|
303
|
+
} else {
|
|
304
|
+
// Pi Editor clears the buffer before invoking onSubmit. A command
|
|
305
|
+
// refused by either admission guard is still a draft, so put it back
|
|
306
|
+
// for the operator instead of silently discarding it.
|
|
307
|
+
deps.editor.setText(text);
|
|
308
|
+
}
|
|
234
309
|
if (runEditorBash(text)) deps.ui.requestRender();
|
|
235
310
|
return;
|
|
236
311
|
}
|
|
237
312
|
const steerMention = parseEditorSteerMention(trimmed);
|
|
238
|
-
if (steerMention
|
|
313
|
+
if (steerMention) {
|
|
314
|
+
const submission = submitEditorSteerMention(steerMention);
|
|
315
|
+
if (submission !== "unhandled") {
|
|
316
|
+
if (submission === "accepted") {
|
|
317
|
+
deps.editor.addToHistory(text);
|
|
318
|
+
deps.editor.setText("");
|
|
319
|
+
} else {
|
|
320
|
+
// Resolution and dispatch failures are correctable rejections. Pi
|
|
321
|
+
// has already cleared the editor, so explicitly restore the draft.
|
|
322
|
+
deps.editor.setText(text);
|
|
323
|
+
}
|
|
324
|
+
deps.ui.requestRender();
|
|
325
|
+
return;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
const command = parseSlashCommand(trimmed);
|
|
329
|
+
if (isRejectedCommand(command)) {
|
|
330
|
+
deps.editor.setText(text);
|
|
331
|
+
deps.dispatchCommand(trimmed);
|
|
332
|
+
} else if (command.kind === "unknown-command") {
|
|
333
|
+
const result = deps.dispatchCommand(trimmed);
|
|
334
|
+
if (result === "rejected") {
|
|
335
|
+
deps.editor.setText(text);
|
|
336
|
+
} else {
|
|
337
|
+
deps.editor.addToHistory(text);
|
|
338
|
+
deps.editor.setText("");
|
|
339
|
+
}
|
|
340
|
+
} else {
|
|
239
341
|
deps.editor.addToHistory(text);
|
|
240
342
|
deps.editor.setText("");
|
|
343
|
+
deps.dispatchCommand(trimmed);
|
|
344
|
+
}
|
|
345
|
+
deps.ui.requestRender();
|
|
346
|
+
};
|
|
347
|
+
|
|
348
|
+
const awaitWithAbort = async (promise: Promise<void>, signal?: AbortSignal): Promise<void> => {
|
|
349
|
+
if (!signal) return await promise;
|
|
350
|
+
signal.throwIfAborted();
|
|
351
|
+
let abort: (() => void) | null = null;
|
|
352
|
+
try {
|
|
353
|
+
await Promise.race([
|
|
354
|
+
promise,
|
|
355
|
+
new Promise<never>((_resolve, reject) => {
|
|
356
|
+
abort = () => reject(signal.reason ?? new DOMException("aborted", "AbortError"));
|
|
357
|
+
signal.addEventListener("abort", abort, { once: true });
|
|
358
|
+
}),
|
|
359
|
+
]);
|
|
360
|
+
} finally {
|
|
361
|
+
if (abort) signal.removeEventListener("abort", abort);
|
|
362
|
+
}
|
|
363
|
+
};
|
|
364
|
+
|
|
365
|
+
const admitCapturedText = async (text: string, signal?: AbortSignal): Promise<void> => {
|
|
366
|
+
const trimmed = text.trim();
|
|
367
|
+
if (trimmed.length === 0) return;
|
|
368
|
+
signal?.throwIfAborted();
|
|
369
|
+
deps.collapseLaunchpadBeforeSubmit?.();
|
|
370
|
+
if (parseEditorBashCommand(text)) {
|
|
371
|
+
while (deps.chat.isStreaming()) await awaitWithAbort(deps.chat.whenSettled(), signal);
|
|
372
|
+
while (activeEditorBashSettlement) await awaitWithAbort(activeEditorBashSettlement, signal);
|
|
373
|
+
signal?.throwIfAborted();
|
|
374
|
+
deps.editor.addToHistory(text);
|
|
375
|
+
runEditorBash(text);
|
|
241
376
|
deps.ui.requestRender();
|
|
242
377
|
return;
|
|
243
378
|
}
|
|
244
|
-
|
|
245
|
-
|
|
379
|
+
const steerMention = parseEditorSteerMention(trimmed);
|
|
380
|
+
if (steerMention) {
|
|
381
|
+
const submission = submitEditorSteerMention(steerMention);
|
|
382
|
+
if (submission !== "unhandled") {
|
|
383
|
+
if (submission === "rejected") {
|
|
384
|
+
throw new Error("queued boot steer was not admitted; preserving it for recovery");
|
|
385
|
+
}
|
|
386
|
+
deps.editor.addToHistory(text);
|
|
387
|
+
deps.ui.requestRender();
|
|
388
|
+
return;
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
const command = parseSlashCommand(trimmed);
|
|
392
|
+
if (isRejectedCommand(command)) {
|
|
393
|
+
signal?.throwIfAborted();
|
|
394
|
+
if (deps.dispatchCommandAsync) await deps.dispatchCommandAsync(trimmed, signal);
|
|
395
|
+
else deps.dispatchCommand(trimmed);
|
|
396
|
+
deps.ui.requestRender();
|
|
397
|
+
throw new Error("queued boot command needs correction; preserving it for recovery");
|
|
398
|
+
}
|
|
399
|
+
deps.editor.addToHistory(text);
|
|
400
|
+
signal?.throwIfAborted();
|
|
401
|
+
if (deps.dispatchCommandAsync) await deps.dispatchCommandAsync(trimmed, signal);
|
|
402
|
+
else deps.dispatchCommand(trimmed);
|
|
246
403
|
deps.ui.requestRender();
|
|
247
404
|
};
|
|
248
405
|
|
|
@@ -261,6 +418,7 @@ export function createEditorSubmitController(deps: EditorSubmitDeps): EditorSubm
|
|
|
261
418
|
deps.io.stderr("[follow-up] image references cannot be queued while a response is streaming\n");
|
|
262
419
|
return;
|
|
263
420
|
}
|
|
421
|
+
deps.beforeSemanticBoundary?.("follow-up-submit");
|
|
264
422
|
if (!deps.chat.queueFollowUp(submitted.text)) {
|
|
265
423
|
deps.io.stderr("[follow-up] no active response to queue against\n");
|
|
266
424
|
return;
|
|
@@ -274,6 +432,50 @@ export function createEditorSubmitController(deps: EditorSubmitDeps): EditorSubm
|
|
|
274
432
|
});
|
|
275
433
|
};
|
|
276
434
|
|
|
435
|
+
const interruptFromEditor = (): void => {
|
|
436
|
+
const text = deps.editor.getText().trim();
|
|
437
|
+
if (text.length === 0) return;
|
|
438
|
+
if (!deps.chat.isStreaming()) {
|
|
439
|
+
deps.editor.setText("");
|
|
440
|
+
submitEditorText(text);
|
|
441
|
+
deps.ui.requestRender();
|
|
442
|
+
return;
|
|
443
|
+
}
|
|
444
|
+
void (async () => {
|
|
445
|
+
const submitted = await deps.expandSubmit(text);
|
|
446
|
+
if (submitted.images.length > 0) {
|
|
447
|
+
deps.io.stderr("[interrupt] image references cannot be sent while a response is streaming\n");
|
|
448
|
+
return;
|
|
449
|
+
}
|
|
450
|
+
deps.editor.addToHistory(text);
|
|
451
|
+
// Same restore Esc performs: when the interrupt will really cancel the
|
|
452
|
+
// run, the queued steers and follow-ups come back to the editor rather
|
|
453
|
+
// than vanishing with the cancelled run. A refused interrupt cancels
|
|
454
|
+
// nothing, so the queue stays put.
|
|
455
|
+
const restored = deps.chat.interruptRefusal() === null ? deps.chat.clearQueuedFollowUps() : [];
|
|
456
|
+
deps.editor.setText(restored.join("\n\n"));
|
|
457
|
+
deps.ui.requestRender();
|
|
458
|
+
// An interrupt is a fresh prompt, so it carries what the idle path
|
|
459
|
+
// carries: the launchpad collapse, the submitted-turn count, and the
|
|
460
|
+
// expansion's working-context paths and pending skill requests. Only
|
|
461
|
+
// images stay behind, rejected above, as they are for a follow-up.
|
|
462
|
+
deps.collapseLaunchpadBeforeSubmit?.();
|
|
463
|
+
deps.sessionTranscript.recordSubmittedTurn();
|
|
464
|
+
const paths = submitted.workingContextPaths ?? [];
|
|
465
|
+
const skillRequests = submitted.pendingSkillRequests ?? [];
|
|
466
|
+
deps.beforeSemanticBoundary?.("interrupt-submit");
|
|
467
|
+
await deps.chat.submit(submitted.text, {
|
|
468
|
+
steering: "interrupt",
|
|
469
|
+
...(paths.length > 0 ? { workingContextPaths: paths } : {}),
|
|
470
|
+
...(skillRequests.length > 0 ? { pendingSkillRequests: skillRequests } : {}),
|
|
471
|
+
});
|
|
472
|
+
await deps.settleVisibleFrame?.("interrupt-submit-return");
|
|
473
|
+
})().catch((err) => {
|
|
474
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
475
|
+
deps.io.stderr(`[interrupt] ${msg}\n`);
|
|
476
|
+
});
|
|
477
|
+
};
|
|
478
|
+
|
|
277
479
|
const restoreQueuedFollowUpsToEditor = (): void => {
|
|
278
480
|
const restored = deps.chat.clearQueuedFollowUps();
|
|
279
481
|
if (restored.length === 0) {
|
|
@@ -291,7 +493,9 @@ export function createEditorSubmitController(deps: EditorSubmitDeps): EditorSubm
|
|
|
291
493
|
openExternalEditorForInput,
|
|
292
494
|
handleEditorSteerMention,
|
|
293
495
|
submitEditorText,
|
|
496
|
+
admitCapturedText,
|
|
294
497
|
queueFollowUpFromEditor,
|
|
498
|
+
interruptFromEditor,
|
|
295
499
|
restoreQueuedFollowUpsToEditor,
|
|
296
500
|
hasActiveEditorBash: () => activeEditorBash !== null,
|
|
297
501
|
cancelActiveEditorBash: () => {
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Terminal ANSI to HTML conversion adapted from pi-coding-agent 0.84.0's
|
|
3
|
+
* `dist/core/export-html/ansi-to-html.js`.
|
|
4
|
+
*
|
|
5
|
+
* Clio's transcript also contains OSC 133 shell-integration markers and may
|
|
6
|
+
* contain other non-SGR terminal controls. Those controls are discarded here;
|
|
7
|
+
* only SGR presentation survives as inline HTML styles.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const ANSI_COLORS = [
|
|
11
|
+
"rgb(0,0,0)",
|
|
12
|
+
"rgb(128,0,0)",
|
|
13
|
+
"rgb(0,128,0)",
|
|
14
|
+
"rgb(128,128,0)",
|
|
15
|
+
"rgb(0,0,128)",
|
|
16
|
+
"rgb(128,0,128)",
|
|
17
|
+
"rgb(0,128,128)",
|
|
18
|
+
"rgb(192,192,192)",
|
|
19
|
+
"rgb(128,128,128)",
|
|
20
|
+
"rgb(255,0,0)",
|
|
21
|
+
"rgb(0,255,0)",
|
|
22
|
+
"rgb(255,255,0)",
|
|
23
|
+
"rgb(0,0,255)",
|
|
24
|
+
"rgb(255,0,255)",
|
|
25
|
+
"rgb(0,255,255)",
|
|
26
|
+
"rgb(255,255,255)",
|
|
27
|
+
] as const;
|
|
28
|
+
|
|
29
|
+
interface TextStyle {
|
|
30
|
+
fg: string | null;
|
|
31
|
+
bg: string | null;
|
|
32
|
+
bold: boolean;
|
|
33
|
+
dim: boolean;
|
|
34
|
+
italic: boolean;
|
|
35
|
+
underline: boolean;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function color256ToHex(index: number): string {
|
|
39
|
+
const bounded = Math.max(0, Math.min(255, index));
|
|
40
|
+
if (bounded < 16) return ANSI_COLORS[bounded] ?? ANSI_COLORS[0];
|
|
41
|
+
if (bounded < 232) {
|
|
42
|
+
const cubeIndex = bounded - 16;
|
|
43
|
+
const component = (value: number): number => (value === 0 ? 0 : 55 + value * 40);
|
|
44
|
+
return `rgb(${component(Math.floor(cubeIndex / 36))},${component(Math.floor((cubeIndex % 36) / 6))},${component(cubeIndex % 6)})`;
|
|
45
|
+
}
|
|
46
|
+
const gray = 8 + (bounded - 232) * 10;
|
|
47
|
+
return `rgb(${gray},${gray},${gray})`;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function escapeHtml(text: string): string {
|
|
51
|
+
return text
|
|
52
|
+
.replace(/&/gu, "&")
|
|
53
|
+
.replace(/</gu, "<")
|
|
54
|
+
.replace(/>/gu, ">")
|
|
55
|
+
.replace(/"/gu, """)
|
|
56
|
+
.replace(/'/gu, "'");
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function createEmptyStyle(): TextStyle {
|
|
60
|
+
return { fg: null, bg: null, bold: false, dim: false, italic: false, underline: false };
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function styleToInlineCss(style: TextStyle): string {
|
|
64
|
+
const parts: string[] = [];
|
|
65
|
+
if (style.fg) parts.push(`color:${style.fg}`);
|
|
66
|
+
if (style.bg) parts.push(`background-color:${style.bg}`);
|
|
67
|
+
if (style.bold) parts.push("font-weight:bold");
|
|
68
|
+
if (style.dim) parts.push("opacity:0.6");
|
|
69
|
+
if (style.italic) parts.push("font-style:italic");
|
|
70
|
+
if (style.underline) parts.push("text-decoration:underline");
|
|
71
|
+
return parts.join(";");
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function hasStyle(style: TextStyle): boolean {
|
|
75
|
+
return style.fg !== null || style.bg !== null || style.bold || style.dim || style.italic || style.underline;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function applySgrCode(params: number[], style: TextStyle): void {
|
|
79
|
+
let index = 0;
|
|
80
|
+
while (index < params.length) {
|
|
81
|
+
const code = params[index] ?? 0;
|
|
82
|
+
if (code === 0) Object.assign(style, createEmptyStyle());
|
|
83
|
+
else if (code === 1) style.bold = true;
|
|
84
|
+
else if (code === 2) style.dim = true;
|
|
85
|
+
else if (code === 3) style.italic = true;
|
|
86
|
+
else if (code === 4) style.underline = true;
|
|
87
|
+
else if (code === 22) {
|
|
88
|
+
style.bold = false;
|
|
89
|
+
style.dim = false;
|
|
90
|
+
} else if (code === 23) style.italic = false;
|
|
91
|
+
else if (code === 24) style.underline = false;
|
|
92
|
+
else if (code >= 30 && code <= 37) style.fg = ANSI_COLORS[code - 30] ?? null;
|
|
93
|
+
else if (code === 38) {
|
|
94
|
+
if (params[index + 1] === 5 && params[index + 2] !== undefined) {
|
|
95
|
+
style.fg = color256ToHex(params[index + 2] ?? 0);
|
|
96
|
+
index += 2;
|
|
97
|
+
} else if (params[index + 1] === 2 && params[index + 4] !== undefined) {
|
|
98
|
+
style.fg = `rgb(${params[index + 2]},${params[index + 3]},${params[index + 4]})`;
|
|
99
|
+
index += 4;
|
|
100
|
+
}
|
|
101
|
+
} else if (code === 39) style.fg = null;
|
|
102
|
+
else if (code >= 40 && code <= 47) style.bg = ANSI_COLORS[code - 40] ?? null;
|
|
103
|
+
else if (code === 48) {
|
|
104
|
+
if (params[index + 1] === 5 && params[index + 2] !== undefined) {
|
|
105
|
+
style.bg = color256ToHex(params[index + 2] ?? 0);
|
|
106
|
+
index += 2;
|
|
107
|
+
} else if (params[index + 1] === 2 && params[index + 4] !== undefined) {
|
|
108
|
+
style.bg = `rgb(${params[index + 2]},${params[index + 3]},${params[index + 4]})`;
|
|
109
|
+
index += 4;
|
|
110
|
+
}
|
|
111
|
+
} else if (code === 49) style.bg = null;
|
|
112
|
+
else if (code >= 90 && code <= 97) style.fg = ANSI_COLORS[code - 90 + 8] ?? null;
|
|
113
|
+
else if (code >= 100 && code <= 107) style.bg = ANSI_COLORS[code - 100 + 8] ?? null;
|
|
114
|
+
index += 1;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// CSI, OSC, DCS/SOS/PM/APC, and single-character ESC sequences. SGR CSI
|
|
119
|
+
// sequences are interpreted; every other terminal control is removed.
|
|
120
|
+
const ESC = String.fromCharCode(27);
|
|
121
|
+
const BEL = String.fromCharCode(7);
|
|
122
|
+
const TERMINAL_SEQUENCE = new RegExp(
|
|
123
|
+
`${ESC}(?:\\[([0-?]*[ -/]*)([@-~])|\\][^${BEL}${ESC}]*(?:${BEL}|${ESC}\\\\)|[PX^_][\\s\\S]*?${ESC}\\\\|[@-_])`,
|
|
124
|
+
"gu",
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
export function ansiToHtml(text: string): string {
|
|
128
|
+
const style = createEmptyStyle();
|
|
129
|
+
let result = "";
|
|
130
|
+
let lastIndex = 0;
|
|
131
|
+
let spanOpen = false;
|
|
132
|
+
TERMINAL_SEQUENCE.lastIndex = 0;
|
|
133
|
+
let match = TERMINAL_SEQUENCE.exec(text);
|
|
134
|
+
while (match !== null) {
|
|
135
|
+
result += escapeHtml(text.slice(lastIndex, match.index));
|
|
136
|
+
if (match[2] === "m") {
|
|
137
|
+
if (spanOpen) result += "</span>";
|
|
138
|
+
const raw = match[1] ?? "";
|
|
139
|
+
const params = raw.length > 0 ? raw.split(";").map((part) => Number.parseInt(part, 10) || 0) : [0];
|
|
140
|
+
applySgrCode(params, style);
|
|
141
|
+
spanOpen = hasStyle(style);
|
|
142
|
+
if (spanOpen) result += `<span style="${styleToInlineCss(style)}">`;
|
|
143
|
+
}
|
|
144
|
+
lastIndex = match.index + match[0].length;
|
|
145
|
+
match = TERMINAL_SEQUENCE.exec(text);
|
|
146
|
+
}
|
|
147
|
+
result += escapeHtml(text.slice(lastIndex));
|
|
148
|
+
if (spanOpen) result += "</span>";
|
|
149
|
+
// A raw C0 control is never meaningful inside the exported document. Tabs
|
|
150
|
+
// remain useful in preformatted tool output and are safe in HTML text.
|
|
151
|
+
return Array.from(result)
|
|
152
|
+
.filter((character) => {
|
|
153
|
+
const code = character.charCodeAt(0);
|
|
154
|
+
return code === 9 || (code >= 32 && code !== 127);
|
|
155
|
+
})
|
|
156
|
+
.join("");
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export function ansiLinesToHtml(lines: ReadonlyArray<string>): string {
|
|
160
|
+
return lines.map((line) => `<div class="ansi-line">${ansiToHtml(line) || " "}</div>`).join("\n");
|
|
161
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { htmlTemplate } from "./template.js";
|
|
2
|
+
import { renderTranscriptHtml } from "./tool-renderer.js";
|
|
3
|
+
|
|
4
|
+
export const MAX_HTML_EXPORT_BYTES = 2 * 1024 * 1024;
|
|
5
|
+
const TEMPLATE_RESERVE_BYTES = 8 * 1024;
|
|
6
|
+
|
|
7
|
+
export interface HtmlSessionExportInput {
|
|
8
|
+
sessionId: string;
|
|
9
|
+
exportedAt: string;
|
|
10
|
+
ansiLines: ReadonlyArray<string>;
|
|
11
|
+
maxBytes?: number;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Build the bounded, self-contained HTML document used by `/export`.
|
|
16
|
+
* Whole rendered rows are admitted until the byte budget is full, so the
|
|
17
|
+
* result never cuts an ANSI sequence, Unicode scalar, span, or tool section.
|
|
18
|
+
*/
|
|
19
|
+
export function renderSessionHtml(input: HtmlSessionExportInput): string {
|
|
20
|
+
const maxBytes = input.maxBytes ?? MAX_HTML_EXPORT_BYTES;
|
|
21
|
+
if (!Number.isSafeInteger(maxBytes) || maxBytes < TEMPLATE_RESERVE_BYTES * 2) {
|
|
22
|
+
throw new Error(`HTML export size limit must be at least ${TEMPLATE_RESERVE_BYTES * 2} bytes`);
|
|
23
|
+
}
|
|
24
|
+
const lineBudget = maxBytes - TEMPLATE_RESERVE_BYTES;
|
|
25
|
+
const selected: string[] = [];
|
|
26
|
+
let renderedBytes = 0;
|
|
27
|
+
let truncated = false;
|
|
28
|
+
for (const line of input.ansiLines) {
|
|
29
|
+
const rendered = renderTranscriptHtml([line]);
|
|
30
|
+
const bytes = Buffer.byteLength(rendered, "utf8") + 1;
|
|
31
|
+
if (renderedBytes + bytes > lineBudget) {
|
|
32
|
+
truncated = true;
|
|
33
|
+
break;
|
|
34
|
+
}
|
|
35
|
+
selected.push(line);
|
|
36
|
+
renderedBytes += bytes;
|
|
37
|
+
}
|
|
38
|
+
const html = htmlTemplate({
|
|
39
|
+
sessionId: input.sessionId,
|
|
40
|
+
exportedAt: input.exportedAt,
|
|
41
|
+
transcriptHtml: renderTranscriptHtml(selected),
|
|
42
|
+
truncated,
|
|
43
|
+
});
|
|
44
|
+
if (Buffer.byteLength(html, "utf8") > maxBytes) {
|
|
45
|
+
throw new Error(`HTML export exceeded its ${maxBytes}-byte size limit`);
|
|
46
|
+
}
|
|
47
|
+
return html;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export { ansiLinesToHtml, ansiToHtml } from "./ansi-to-html.js";
|
|
51
|
+
export { renderTranscriptHtml } from "./tool-renderer.js";
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { escapeHtml } from "./ansi-to-html.js";
|
|
2
|
+
|
|
3
|
+
export interface HtmlTemplateInput {
|
|
4
|
+
sessionId: string;
|
|
5
|
+
exportedAt: string;
|
|
6
|
+
transcriptHtml: string;
|
|
7
|
+
truncated: boolean;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/** A self-contained document: no scripts, fonts, stylesheets, or remote URLs. */
|
|
11
|
+
export function htmlTemplate(input: HtmlTemplateInput): string {
|
|
12
|
+
const title = `Clio session ${input.sessionId}`;
|
|
13
|
+
return `<!doctype html>
|
|
14
|
+
<html lang="en">
|
|
15
|
+
<head>
|
|
16
|
+
<meta charset="utf-8">
|
|
17
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
18
|
+
<title>${escapeHtml(title)}</title>
|
|
19
|
+
<style>
|
|
20
|
+
:root { color-scheme: dark; --page: rgb(16,23,25); --card: rgb(23,33,36); --tool: rgb(18,28,31); --text: rgb(220,231,233); --muted: rgb(138,153,164); --frame: rgb(47,93,90); --accent: rgb(70,229,208); }
|
|
21
|
+
* { box-sizing: border-box; }
|
|
22
|
+
body { margin: 0; background: var(--page); color: var(--text); font: 14px/1.45 ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace; }
|
|
23
|
+
main { width: min(112ch, calc(100% - 32px)); margin: 32px auto; }
|
|
24
|
+
header { margin-bottom: 20px; padding: 18px 20px; border: 1px solid var(--frame); border-radius: 10px; background: var(--card); }
|
|
25
|
+
h1 { margin: 0 0 6px; color: var(--accent); font: 600 20px/1.3 system-ui, sans-serif; }
|
|
26
|
+
header p { margin: 0; color: var(--muted); }
|
|
27
|
+
.transcript { white-space: pre-wrap; overflow-wrap: anywhere; }
|
|
28
|
+
.ansi-line { min-height: 1.45em; }
|
|
29
|
+
.transcript-row { padding: 0 12px; }
|
|
30
|
+
.tool-row { margin: 8px 0; padding: 9px 12px; border-left: 2px solid var(--frame); border-radius: 0 7px 7px 0; background: var(--tool); }
|
|
31
|
+
.truncated { margin-top: 18px; padding: 10px 12px; border: 1px solid rgb(128,96,52); border-radius: 7px; color: rgb(255,180,84); }
|
|
32
|
+
@media print { :root { color-scheme: light; --page: rgb(255,255,255); --card: rgb(244,247,247); --tool: rgb(247,249,249); --text: rgb(23,33,36); --muted: rgb(82,96,103); --frame: rgb(145,170,168); --accent: rgb(8,125,114); } main { width: 100%; margin: 0; } }
|
|
33
|
+
</style>
|
|
34
|
+
</head>
|
|
35
|
+
<body>
|
|
36
|
+
<main>
|
|
37
|
+
<header><h1>${escapeHtml(title)}</h1><p>Exported ${escapeHtml(input.exportedAt)}</p></header>
|
|
38
|
+
<section class="transcript" aria-label="Session transcript">
|
|
39
|
+
${input.transcriptHtml}
|
|
40
|
+
</section>
|
|
41
|
+
${input.truncated ? ' <p class="truncated">The transcript was truncated to keep this HTML export within its size limit.</p>\n' : ""} </main>
|
|
42
|
+
</body>
|
|
43
|
+
</html>
|
|
44
|
+
`;
|
|
45
|
+
}
|