@iowarp/clio-coder 0.3.7 → 0.3.9
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 +80 -0
- package/README.md +13 -4
- package/dist/{acp-SK4MD6MM.js → acp-7LOELQFP.js} +13 -13
- package/dist/{agents-2FN2K6ME.js → agents-FIBG2SHA.js} +41 -37
- package/dist/assets/codewiki.json +1 -1
- package/dist/{auth-QIYZWM5I.js → auth-OI4LIH2I.js} +31 -24
- package/dist/builtins-AD25UL3C.js +17 -0
- package/dist/{chunk-5FR74PWO.js → chunk-2JDWVJND.js} +2 -2
- package/dist/chunk-3DPEIQKN.js +113 -0
- package/dist/{chunk-EOOQZZDE.js → chunk-3DUR4WUA.js} +19 -19
- package/dist/{chunk-WHJYKASB.js → chunk-3MRC2YSQ.js} +2 -2
- package/dist/{chunk-EBEFWSGL.js → chunk-3UUY7R3Z.js} +14 -10
- package/dist/{chunk-LADCF22A.js → chunk-3V5AYSEQ.js} +113 -54
- package/dist/{chunk-BMWK7ZIZ.js → chunk-465CC7FK.js} +16 -13
- package/dist/{chunk-5WIGXA4T.js → chunk-47CMYGET.js} +111 -4
- package/dist/{chunk-CEYBNUGC.js → chunk-4H6ULJ3H.js} +378 -36
- package/dist/{chunk-YTYFXUI3.js → chunk-4LJX2PUC.js} +9 -9
- package/dist/{chunk-DOOEX22V.js → chunk-56KB5IJP.js} +5 -5
- package/dist/{chunk-SPULKLCF.js → chunk-5DQRIYDZ.js} +2 -2
- package/dist/{chunk-TSHXZTOQ.js → chunk-5HFBWUMU.js} +23 -11
- package/dist/{chunk-5UJ6ECTS.js → chunk-5PVQ4SRS.js} +80 -8
- package/dist/{chunk-ZWLZP4ZT.js → chunk-5QKCQQ3E.js} +359 -17
- package/dist/{chunk-6M7VS3J3.js → chunk-5T7RBWN2.js} +111 -5
- package/dist/chunk-774ILSRL.js +172 -0
- package/dist/chunk-7C6RYZGQ.js +391 -0
- package/dist/{chunk-GH5622CP.js → chunk-A2NJGIB3.js} +2 -2
- package/dist/{chunk-C4JBQ5SR.js → chunk-AD7Y7STJ.js} +6 -6
- package/dist/{chunk-GEYXPTRF.js → chunk-AEYBF3TB.js} +33 -12
- package/dist/{chunk-2SFS6XQE.js → chunk-AMKHQW3C.js} +3 -2
- package/dist/{chunk-D4MDIG46.js → chunk-B5CSFE7B.js} +7 -7
- package/dist/{chunk-MXI6J5JF.js → chunk-B5XRQOLB.js} +10 -10
- package/dist/{chunk-X2KV5FXT.js → chunk-BVDVID7E.js} +2 -2
- package/dist/{chunk-JNXPYBB4.js → chunk-CA42X6KT.js} +3 -3
- package/dist/{chunk-VREKEFLL.js → chunk-D73KXYPF.js} +3 -3
- package/dist/{chunk-JTSEDYVQ.js → chunk-DG4M6ZUE.js} +7 -7
- package/dist/{chunk-DQA7QLMD.js → chunk-EBOC7MT3.js} +10 -25
- package/dist/{chunk-KZ2H5X4G.js → chunk-ECUO3KDP.js} +129 -14
- package/dist/{chunk-JRIO5UD2.js → chunk-EQ63NRB7.js} +5 -5
- package/dist/{chunk-YD734TPH.js → chunk-FALJGAWU.js} +2 -2
- package/dist/{chunk-GWS3VEIW.js → chunk-FWDFM5ZU.js} +24 -3
- package/dist/{chunk-XEGB6BCN.js → chunk-GAYUJ7LE.js} +68 -14
- package/dist/{chunk-UND3GU2L.js → chunk-H7IXIC72.js} +2 -2
- package/dist/{chunk-IR4CFBFN.js → chunk-HAY4ZE2P.js} +12 -12
- package/dist/{chunk-UVDSQ6LW.js → chunk-HCBCAYZU.js} +74 -147
- package/dist/{chunk-4DWFMQDR.js → chunk-HJB5IUKP.js} +89 -145
- package/dist/{chunk-M4AKACEO.js → chunk-HKO36JWF.js} +33 -5
- package/dist/{chunk-KCMKRQX4.js → chunk-HPCTNZM2.js} +45 -82
- package/dist/{chunk-465YSENW.js → chunk-IFBNV6H6.js} +3 -3
- package/dist/{chunk-FJ3H4MN5.js → chunk-IHKBWSXF.js} +2 -2
- package/dist/chunk-JEQQR47K.js +3025 -0
- package/dist/{chunk-FO5ZOVUY.js → chunk-KV2AOLDF.js} +27 -7
- package/dist/chunk-LU7P4LHA.js +33 -0
- package/dist/{chunk-6TUKSZVF.js → chunk-LXPJXFM5.js} +11 -11
- package/dist/{chunk-VQNODYQ4.js → chunk-MIX5N5AC.js} +488 -3668
- package/dist/chunk-MLOK6ZOS.js +2888 -0
- package/dist/{chunk-ZZMN5OM4.js → chunk-MV2VUEJC.js} +2 -2
- package/dist/{chunk-MVVUPGPW.js → chunk-MXHC5QYU.js} +6 -6
- package/dist/{chunk-OBMAI2DP.js → chunk-N3PBVRTZ.js} +12 -388
- package/dist/{chunk-WJHBC77E.js → chunk-N5XKWMDW.js} +17 -7
- package/dist/{chunk-5C3AQNDW.js → chunk-NNNWO6F2.js} +124 -36
- package/dist/{chunk-UFQ3F4FW.js → chunk-NQ6UCCOD.js} +4 -4
- package/dist/chunk-NUGM5KR6.js +165 -0
- package/dist/{chunk-DMD2AGVS.js → chunk-NZU6YDNV.js} +20 -18
- package/dist/{chunk-WHGPSPT5.js → chunk-O6I4CIEU.js} +151 -13
- package/dist/{chunk-XN3L4EYL.js → chunk-OEDBCISO.js} +2 -2
- package/dist/{chunk-PD3MESLB.js → chunk-P3JGPQFL.js} +4 -4
- package/dist/{chunk-UHXRNZ2J.js → chunk-PNY46YEY.js} +23 -6
- package/dist/{chunk-THKY7CD7.js → chunk-PZ4I4JE2.js} +134 -29
- package/dist/{chunk-SROCI7ZU.js → chunk-QQ7EKM72.js} +5 -5
- package/dist/{chunk-QCTRSGHQ.js → chunk-R7LNVMCS.js} +91 -53
- package/dist/{chunk-GOXNB3AO.js → chunk-RAPCMZL4.js} +75 -4
- package/dist/chunk-RKKLTLYB.js +45 -0
- package/dist/{chunk-OB5HIGJY.js → chunk-RKRLDWD3.js} +4 -1
- package/dist/{chunk-DJNLUABN.js → chunk-S4COXYBG.js} +588 -32
- package/dist/{chunk-3HAPLH5M.js → chunk-T3Z6VAAF.js} +172 -11
- package/dist/{chunk-FOT2FX5J.js → chunk-TD7UE2L5.js} +12 -10
- package/dist/{chunk-UUANF5CR.js → chunk-TEO2TLVN.js} +856 -967
- package/dist/{chunk-FCSXB6T2.js → chunk-UOSL25KY.js} +14 -2
- package/dist/{chunk-EELBMBT6.js → chunk-VKBMFOYV.js} +74 -15
- package/dist/chunk-VO2LKSTM.js +165 -0
- package/dist/{chunk-5C77SEEY.js → chunk-VPTUJU4P.js} +3 -3
- package/dist/{chunk-WEH5XRJQ.js → chunk-WIE7ZOSW.js} +2 -2
- package/dist/{chunk-J7PIKKWC.js → chunk-WXCJ7VME.js} +8 -8
- package/dist/{chunk-4DGYLA73.js → chunk-XDOQXGFO.js} +22 -7
- package/dist/{chunk-PPAMZ32Z.js → chunk-XK56QHLX.js} +6 -1
- package/dist/{chunk-AB4XIIVB.js → chunk-YKOFT37S.js} +6 -6
- package/dist/chunk-YSEHGPCT.js +127 -0
- package/dist/{chunk-HFSBBKSQ.js → chunk-YW7UVM5V.js} +138 -3
- package/dist/cli/index.js +32 -32
- package/dist/{clio-WBVQEBKO.js → clio-LT5V7SSZ.js} +9 -9
- package/dist/{code-nav-FGGFIE7L.js → code-nav-LMW275PA.js} +5 -5
- package/dist/codewiki/build-worker.js +4 -4
- package/dist/{components-F7OEATSO.js → components-ZFA3SAER.js} +8 -8
- package/dist/{config-TRBL3RCF.js → config-RXS5T3JT.js} +98 -65
- package/dist/{configure-OLCVPHNM.js → configure-2WYWSCSD.js} +26 -22
- package/dist/{context-MJIJ6GOX.js → context-I3BTOTCS.js} +12 -12
- package/dist/{context-XEWE3MOJ.js → context-MVOORGMF.js} +54 -47
- package/dist/{context-WFPKQSM6.js → context-PALKKQYL.js} +28 -28
- package/dist/{context-clear-KNOS2JPB.js → context-clear-N2WOYZ2K.js} +53 -46
- package/dist/{context-index-SSR5ECNE.js → context-index-HNG3MOME.js} +6 -6
- package/dist/{context-working-set-EUXAZI6N.js → context-working-set-MIEVECVZ.js} +17 -18
- package/dist/{dispatch-runner-B7MTOVKL.js → dispatch-runner-VVA4SRRH.js} +90 -61
- package/dist/{docs-FLJTIDSE.js → docs-7LQ23DLM.js} +8 -8
- package/dist/doctor-TWBWFK5V.js +165 -0
- package/dist/eval-IJ5VEZDJ.js +4483 -0
- package/dist/{evidence-JZNBUOQZ.js → evidence-L5APPXNV.js} +68 -61
- package/dist/{evolve-FJVC4KKI.js → evolve-RGNKFJ52.js} +47 -40
- package/dist/{extensions-IQL36S7K.js → extensions-7WYWUX5A.js} +13 -7
- package/dist/{fleet-BDKYJFCP.js → fleet-6CNVBZZP.js} +113 -76
- package/dist/{fleet-commands-ZFIWZSB3.js → fleet-commands-L2SXSYEI.js} +10 -10
- package/dist/{fleet-graph-Y6HPXIVF.js → fleet-graph-2J3OOIPO.js} +17 -15
- package/dist/{fleet-preflight-BHSNPBMH.js → fleet-preflight-CZRJ4JP5.js} +5 -6
- package/dist/{fleet-validate-BIYREGIK.js → fleet-validate-C5RI6DP7.js} +20 -19
- package/dist/{init-LQUB5COQ.js → init-VBN2ACVA.js} +70 -63
- package/dist/{library-NJAHIGG4.js → library-JHGUMLY2.js} +22 -20
- package/dist/{memory-OG6HOYKM.js → memory-K4OQIYWG.js} +49 -42
- package/dist/{models-5ZG5XY7J.js → models-2NCZUWDD.js} +35 -29
- package/dist/{monitor-TJ7AMTGB.js → monitor-MMVTJABD.js} +64 -45
- package/dist/{orchestrator-WZYB54DM.js → orchestrator-ZKBPCHW6.js} +1971 -520
- package/dist/{paths-XUC7GS6E.js → paths-DBXMZMDU.js} +5 -5
- package/dist/registry-LG64LTF4.js +11 -0
- package/dist/{reset-PXQT45IY.js → reset-DD5JGOY3.js} +11 -11
- package/dist/{run-FQ74YF62.js → run-QEGNX7FL.js} +89 -83
- package/dist/{share-FW7SVCL3.js → share-JKD3BQMW.js} +20 -18
- package/dist/{skills-7E7IRB3R.js → skills-LMQIKDOZ.js} +23 -21
- package/dist/{skills-eval-LI75W6OK.js → skills-eval-I7X2774U.js} +59 -52
- package/dist/{steer-GGWFUJUD.js → steer-CF5TDANS.js} +3 -3
- package/dist/support-I7LOJLIF.js +38 -0
- package/dist/{targets-4CIFKCTW.js → targets-RUSR6B5Z.js} +77 -42
- package/dist/{terminal-lease-WUZY7ZV5.js → terminal-lease-QYVORFR4.js} +6 -4
- package/dist/{trace-PNCASAXC.js → trace-ODOQIVIW.js} +61 -6
- package/dist/{uninstall-7FV7IP4E.js → uninstall-ZJF5H5ZN.js} +8 -8
- package/dist/{upgrade-K2HVIVMQ.js → upgrade-XANW3FXB.js} +29 -26
- package/dist/{usage-GTZELZQX.js → usage-4H7ZRXQT.js} +110 -61
- package/dist/{verifiers-RLAHT27O.js → verifiers-UZXNBZEB.js} +13 -13
- package/dist/{verify-BX3BRKH5.js → verify-BVKWTNDL.js} +9 -9
- package/dist/{wiki-generate-ASIFASCN.js → wiki-generate-MY7WV2QI.js} +76 -69
- package/dist/worker/entry.js +69 -66
- package/docs/alcf-provider.md +1 -1
- package/docs/architecture.md +1 -1
- package/docs/artifact-versions.md +11 -5
- package/docs/built-in-agents.md +1 -1
- package/docs/capacity-and-scheduling.md +23 -2
- package/docs/commands-and-modes.md +2 -2
- package/docs/configuration-and-targets.md +37 -5
- package/docs/context-engine.md +63 -4
- package/docs/documentation-coverage.md +3 -3
- package/docs/documentation-guide.md +1 -1
- package/docs/environment-variables.md +2 -0
- package/docs/eval-runner.md +262 -11
- package/docs/evals-internal.md +72 -2
- package/docs/evidence-and-memory.md +77 -12
- package/docs/evolution.md +1 -1
- package/docs/extensions-and-sharing.md +3 -1
- package/docs/fleet-dispatch.md +34 -9
- package/docs/glossary.md +21 -1
- package/docs/installation-and-lifecycle.md +1 -1
- package/docs/middleware-and-components.md +1 -1
- package/docs/model-catalog.md +1 -1
- package/docs/observability.md +54 -3
- package/docs/proactive-memory.md +127 -14
- package/docs/prompt-envelope-and-tools.md +22 -2
- package/docs/provider-adapter-cookbook.md +1 -1
- package/docs/release-cut-checklist.md +60 -41
- package/docs/safety-model.md +1 -1
- package/docs/scientific-validation.md +1 -1
- package/docs/skills-marketplace.md +1 -1
- package/docs/tool-usage.md +1 -1
- package/docs/trace-store.md +1 -1
- package/docs/troubleshooting.md +87 -0
- package/docs/tui-design.md +1 -1
- package/docs/worker-dispatch-mechanics.md +1 -1
- package/package.json +2 -2
- package/src/cli/agents.ts +1 -1
- package/src/cli/argv.ts +5 -0
- package/src/cli/config-inspect.ts +33 -6
- package/src/cli/config.ts +1 -1
- package/src/cli/configure.ts +107 -23
- package/src/cli/doctor-state-size.ts +82 -0
- package/src/cli/doctor.ts +7 -1
- package/src/cli/eval.ts +80 -16
- package/src/cli/evidence.ts +30 -25
- package/src/cli/extensions.ts +5 -1
- package/src/cli/fleet-preflight.ts +2 -12
- package/src/cli/fleet.ts +32 -3
- package/src/cli/shared.ts +1 -0
- package/src/cli/targets.ts +45 -11
- package/src/cli/trace.ts +63 -4
- package/src/cli/usage.ts +63 -14
- package/src/cli/validate-model.ts +60 -5
- package/src/core/bus-events.ts +54 -1
- package/src/core/cache-telemetry.ts +42 -0
- package/src/core/commit-attribution.ts +4 -4
- package/src/core/config.ts +18 -0
- package/src/core/defaults.ts +36 -6
- package/src/core/endpoint-key.ts +27 -0
- package/src/core/path-boundary.ts +100 -0
- package/src/core/residency-target-key.ts +25 -0
- package/src/core/response-schema.ts +36 -2
- package/src/domains/agents/extension.ts +2 -11
- package/src/domains/agents/fleet-contract.ts +30 -12
- package/src/domains/agents/recipe.ts +7 -1
- package/src/domains/agents/registry.ts +73 -5
- package/src/domains/agents/result-contract.ts +128 -17
- package/src/domains/agents/write-boundary.ts +15 -50
- package/src/domains/config/classify.ts +3 -0
- package/src/domains/context/codewiki/coordinator.ts +12 -4
- package/src/domains/context/project-rules.ts +51 -1
- package/src/domains/dispatch/admission.ts +40 -3
- package/src/domains/dispatch/assignment-reconcile.ts +22 -5
- package/src/domains/dispatch/assignment-store.ts +151 -14
- package/src/domains/dispatch/capacity-lease.ts +98 -9
- package/src/domains/dispatch/contract.ts +26 -1
- package/src/domains/dispatch/delegation-plan.ts +2 -5
- package/src/domains/dispatch/execution-plan.ts +44 -4
- package/src/domains/dispatch/execution-role.ts +9 -1
- package/src/domains/dispatch/extension.ts +309 -96
- package/src/domains/dispatch/fleet-run.ts +78 -4
- package/src/domains/dispatch/gate-role-prompts.ts +38 -0
- package/src/domains/dispatch/heartbeat.ts +32 -8
- package/src/domains/dispatch/index.ts +6 -1
- package/src/domains/dispatch/intent-requirements.ts +40 -0
- package/src/domains/dispatch/intent.ts +84 -8
- package/src/domains/dispatch/orphan-recovery.ts +5 -0
- package/src/domains/dispatch/path-scope.ts +370 -0
- package/src/domains/dispatch/receipt-integrity.ts +2 -1
- package/src/domains/dispatch/reservation-store.ts +116 -8
- package/src/domains/dispatch/state.ts +4 -0
- package/src/domains/dispatch/types.ts +14 -7
- package/src/domains/dispatch/validation.ts +6 -3
- package/src/domains/dispatch/worker-spawn.ts +25 -11
- package/src/domains/dispatch/write-boundary-enforcer.ts +62 -0
- package/src/domains/dispatch/write-boundary.ts +262 -22
- package/src/domains/eval/artifacts/store.ts +62 -0
- package/src/domains/eval/compare/behavioral.ts +224 -0
- package/src/domains/eval/compare/compare.ts +355 -2
- package/src/domains/eval/compare/envelope.ts +128 -0
- package/src/domains/eval/compare/gates.ts +24 -6
- package/src/domains/eval/compare/thresholds.ts +30 -3
- package/src/domains/eval/execution-provenance.ts +240 -0
- package/src/domains/eval/metrics/aggregate.ts +136 -0
- package/src/domains/eval/metrics/call-ledger-stream.ts +112 -0
- package/src/domains/eval/metrics/evidence.ts +79 -2
- package/src/domains/eval/metrics/tracked.ts +413 -0
- package/src/domains/eval/provenance.ts +117 -0
- package/src/domains/eval/reports/comparison.ts +128 -0
- package/src/domains/eval/reports/junit.ts +17 -3
- package/src/domains/eval/reports/markdown.ts +3 -3
- package/src/domains/eval/reports/text.ts +14 -0
- package/src/domains/eval/run-compare.ts +20 -0
- package/src/domains/eval/runners/clio-run.ts +139 -2
- package/src/domains/eval/runners/external-command.ts +28 -3
- package/src/domains/eval/schema/adapter.ts +111 -0
- package/src/domains/eval/schema/artifact.ts +20 -0
- package/src/domains/eval/schema/behavioral-metrics.ts +204 -0
- package/src/domains/eval/schema/behavioral.ts +520 -0
- package/src/domains/eval/schema/execution-envelope.ts +194 -0
- package/src/domains/eval/schema/serving.ts +74 -0
- package/src/domains/eval/schema/suite.ts +38 -8
- package/src/domains/eval/schema/validate.ts +58 -3
- package/src/domains/eval/schema/verdict.ts +237 -0
- package/src/domains/eval/suites/resolve.ts +2 -0
- package/src/domains/eval/suites/run.ts +264 -33
- package/src/domains/eval/verifiers/command.ts +2 -1
- package/src/domains/eval/workspaces/temp-copy.ts +145 -13
- package/src/domains/evidence/build.ts +68 -21
- package/src/domains/evidence/eval.ts +2 -12
- package/src/domains/evidence/findings-markdown.ts +33 -0
- package/src/domains/evidence/index.ts +21 -0
- package/src/domains/evidence/provenance.ts +46 -11
- package/src/domains/evidence/run-trust.ts +7 -113
- package/src/domains/evidence/trust-projection.ts +274 -0
- package/src/domains/evidence/trust-status.ts +145 -17
- package/src/domains/evidence/types.ts +4 -0
- package/src/domains/extensions/compatibility.ts +285 -0
- package/src/domains/extensions/discovery.ts +126 -4
- package/src/domains/extensions/resources.ts +21 -9
- package/src/domains/extensions/state.ts +18 -5
- package/src/domains/extensions/types.ts +6 -1
- package/src/domains/lifecycle/doctor.ts +209 -2
- package/src/domains/memory/index.ts +14 -0
- package/src/domains/memory/task-bank-promotion.ts +64 -0
- package/src/domains/memory/task-memory-policy.ts +77 -8
- package/src/domains/memory/task-memory-spend.ts +131 -0
- package/src/domains/memory/task-memory-status.ts +7 -0
- package/src/domains/memory/task-memory-telemetry.ts +2 -0
- package/src/domains/middleware/index.ts +1 -0
- package/src/domains/middleware/memory-intervention.ts +69 -5
- package/src/domains/middleware/memory-step-endpoint.ts +71 -0
- package/src/domains/observability/background-memory-usage.ts +140 -0
- package/src/domains/observability/cost.ts +1 -1
- package/src/domains/observability/index.ts +7 -0
- package/src/domains/observability/out-of-turn-usage.ts +51 -2
- package/src/domains/observability/trace-store.ts +192 -2
- package/src/domains/prompts/compiler.ts +100 -13
- package/src/domains/prompts/contract.ts +3 -5
- package/src/domains/providers/endpoint-capacity.ts +96 -0
- package/src/domains/providers/extension.ts +30 -2
- package/src/domains/providers/index.ts +10 -0
- package/src/domains/providers/models/local-models/clio-local-coding-targets.yaml +243 -1
- package/src/domains/providers/runtime-resolution.ts +8 -1
- package/src/domains/providers/runtimes/common/probe-helpers.ts +31 -9
- package/src/domains/providers/runtimes/local-native/llamacpp-anthropic.ts +1 -1
- package/src/domains/providers/runtimes/local-native/llamacpp-completion.ts +1 -1
- package/src/domains/providers/runtimes/local-native/llamacpp-embed.ts +1 -1
- package/src/domains/providers/runtimes/local-native/llamacpp-rerank.ts +1 -1
- package/src/domains/providers/runtimes/local-native/llamacpp.ts +4 -1
- package/src/domains/providers/runtimes/local-native/lmstudio.ts +4 -1
- package/src/domains/providers/runtimes/local-native/ollama-native.ts +6 -1
- package/src/domains/providers/types/capability-flags.ts +2 -0
- package/src/domains/providers/types/target-descriptor.ts +2 -0
- package/src/domains/resources/common-loader.ts +3 -0
- package/src/domains/resources/prompts/loader.ts +184 -17
- package/src/domains/safety/call-target.ts +52 -0
- package/src/domains/safety/policy-engine.ts +5 -5
- package/src/domains/safety/run-effects.ts +96 -2
- package/src/domains/safety/scope.ts +7 -12
- package/src/domains/session/context-accounting.ts +52 -1
- package/src/domains/session/context-ledger.ts +37 -13
- package/src/domains/session/index.ts +6 -0
- package/src/domains/session/prompt-cache.ts +140 -0
- package/src/domains/session/prompt-manifest.ts +42 -0
- package/src/engine/acp/adapter.ts +18 -3
- package/src/engine/acp/server.ts +4 -1
- package/src/engine/ai.ts +35 -0
- package/src/engine/apis/llamacpp-residency.ts +55 -3
- package/src/engine/apis/lmstudio.ts +25 -5
- package/src/engine/apis/ollama-native.ts +2 -1
- package/src/engine/apis/openai-completions.ts +80 -17
- package/src/engine/apis/residency-lock.ts +3 -1
- package/src/engine/apis/residency.ts +34 -1
- package/src/engine/prompt-templates.ts +18 -1
- package/src/engine/provider-payload.ts +29 -1
- package/src/engine/worker-runtime.ts +6 -3
- package/src/entry/orchestrator.ts +176 -30
- package/src/interactive/chat-loop-messages.ts +26 -7
- package/src/interactive/chat-loop.ts +318 -41
- package/src/interactive/chat-panel.ts +62 -8
- package/src/interactive/clio-editor.ts +45 -8
- package/src/interactive/context-activity.ts +5 -1
- package/src/interactive/context-meter.ts +1 -1
- package/src/interactive/context-overlay.ts +40 -10
- package/src/interactive/cost-overlay.ts +64 -6
- package/src/interactive/dispatch-board.ts +127 -5
- package/src/interactive/fleet-run-preview.ts +41 -15
- package/src/interactive/handoff-round.ts +41 -2
- package/src/interactive/interactive-application.ts +24 -1
- package/src/interactive/interactive-event-projection.ts +14 -0
- package/src/interactive/interactive-input-runtime.ts +8 -0
- package/src/interactive/interactive-presentation.ts +4 -0
- package/src/interactive/interactive-shell.ts +20 -17
- package/src/interactive/interactive-slash-runtime.ts +27 -4
- package/src/interactive/memory-overlay.ts +8 -0
- package/src/interactive/mutation-preview.ts +295 -0
- package/src/interactive/overlay-general-openers.ts +16 -0
- package/src/interactive/overlay-key-routing.ts +38 -0
- package/src/interactive/overlay-lifecycle.ts +38 -5
- package/src/interactive/overlay-permission-lifecycle.ts +22 -2
- package/src/interactive/overlay-session-lifecycle.ts +73 -9
- package/src/interactive/overlays/ask-user.ts +91 -19
- package/src/interactive/overlays/help-reference.ts +4 -0
- package/src/interactive/overlays/prompts.ts +11 -1
- package/src/interactive/overlays/settings.ts +176 -48
- package/src/interactive/permission-hint.ts +34 -2
- package/src/interactive/permission-overlay.ts +159 -9
- package/src/interactive/prewarm.ts +197 -0
- package/src/interactive/render-trace.ts +162 -15
- package/src/interactive/renderers/tool-execution.ts +4 -0
- package/src/interactive/side-question.ts +58 -1
- package/src/interactive/slash-commands.ts +7 -2
- package/src/interactive/status/controller.ts +11 -0
- package/src/interactive/status/state-machine.ts +54 -2
- package/src/interactive/status/types.ts +7 -0
- package/src/interactive/terminal-lease.ts +2 -0
- package/src/interactive/turn-context.ts +299 -31
- package/src/interactive/turn-persistence.ts +14 -4
- package/src/interactive/turn-prewarm.ts +364 -0
- package/src/interactive/turn-queues.ts +7 -4
- package/src/interactive/turn-runtime.ts +8 -1
- package/src/interactive/turn-state.ts +23 -0
- package/src/interactive/view/artifacts.ts +42 -9
- package/src/interactive/view/view-overlay.ts +43 -6
- package/src/interactive/worker-receipts.ts +14 -2
- package/src/interactive/worker-stream.ts +8 -0
- package/src/tools/ask-user.ts +43 -2
- package/src/tools/dispatch-admission.ts +12 -13
- package/src/tools/dispatch-arguments.ts +27 -0
- package/src/tools/dispatch-plan.ts +46 -9
- package/src/tools/dispatch-runner.ts +48 -13
- package/src/tools/dispatch-scout.ts +1 -1
- package/src/tools/monitor.ts +13 -0
- package/src/tools/registry.ts +16 -0
- package/src/tools/worker-evidence.ts +19 -13
- package/src/worker/spec-contract.ts +2 -1
- package/dist/chunk-AOCYTWAV.js +0 -449
- package/dist/chunk-HWUFFB6L.js +0 -83
- package/dist/chunk-R346GLFC.js +0 -31
- package/dist/chunk-ZGH7FGS5.js +0 -1079
- package/dist/doctor-RN4YKO2X.js +0 -87
- package/dist/eval-RUBJVSNQ.js +0 -2557
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
import type { HintEntry } from "./overlay-frame.js";
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* The key that opens and closes local inspection of a parked mutation. It lives
|
|
5
|
+
* here rather than beside the preview builder so the composer rail can name it
|
|
6
|
+
* without pulling the diff machinery into the Stage 0 boot shell closure.
|
|
7
|
+
*/
|
|
8
|
+
export const MUTATION_PREVIEW_KEY = "v";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Whether this card has a mutation to read, and whether it is open.
|
|
12
|
+
* `none` covers every ask with nothing local to inspect, including a worker
|
|
13
|
+
* escalation, whose arguments never leave the worker.
|
|
14
|
+
*/
|
|
15
|
+
export type PermissionInspectionHint = "none" | "closed" | "open";
|
|
16
|
+
|
|
3
17
|
/**
|
|
4
18
|
* The keys that answer a parked call, as data, so the dialog footer and the
|
|
5
19
|
* composer rail render the same policy at any width.
|
|
@@ -16,17 +30,35 @@ import type { HintEntry } from "./overlay-frame.js";
|
|
|
16
30
|
*
|
|
17
31
|
* Esc says `deny`, not `close`: closing the dialog denies the call, and on the
|
|
18
32
|
* one surface where a misread is a wrong decision about a tool call the key
|
|
19
|
-
* names its effect.
|
|
33
|
+
* names its effect. It keeps that meaning while the mutation is open, so the
|
|
34
|
+
* inspect key is what puts the mutation away again.
|
|
20
35
|
*
|
|
21
36
|
* With a draft in the composer, Enter is inert (issue #186): the habitual send
|
|
22
37
|
* key must not resolve toward "allow" on a safety rail. The entry then says so
|
|
23
38
|
* and names the key that clears the draft, which the router passes through.
|
|
39
|
+
*
|
|
40
|
+
* A parked `write` or `edit` adds the inspect key (issue #254). It is
|
|
41
|
+
* droppable, and deliberately so: at 40 columns the four entries do not fit,
|
|
42
|
+
* and the three keys that answer the call outrank the one that reads it. The
|
|
43
|
+
* key keeps working at every width; the `/help` Autonomy & safety net topic is
|
|
44
|
+
* where it is documented for the widths that cannot show it.
|
|
24
45
|
*/
|
|
25
|
-
export function permissionHintEntries(
|
|
46
|
+
export function permissionHintEntries(
|
|
47
|
+
composerHasDraft = false,
|
|
48
|
+
inspection: PermissionInspectionHint = "none",
|
|
49
|
+
): HintEntry[] {
|
|
26
50
|
return [
|
|
27
51
|
composerHasDraft
|
|
28
52
|
? { key: "Backspace", verb: "clear the draft to allow", short: "clear draft", critical: true }
|
|
29
53
|
: { key: "Enter", verb: "allow once", short: "allow", critical: true },
|
|
54
|
+
...(inspection === "none"
|
|
55
|
+
? []
|
|
56
|
+
: inspection === "open"
|
|
57
|
+
? [
|
|
58
|
+
{ key: "↑↓", verb: "scroll", critical: false },
|
|
59
|
+
{ key: MUTATION_PREVIEW_KEY, verb: "hide mutation", short: "hide", critical: false },
|
|
60
|
+
]
|
|
61
|
+
: [{ key: MUTATION_PREVIEW_KEY, verb: "inspect mutation", short: "inspect", critical: false }]),
|
|
30
62
|
{ key: "s", verb: "stop turn", short: "stop", critical: true },
|
|
31
63
|
// With a draft, deny and stop are the only immediate answers, so Esc
|
|
32
64
|
// outranks the narrowing that would otherwise drop it first.
|
|
@@ -6,13 +6,21 @@ import {
|
|
|
6
6
|
decisionFactsForPermission,
|
|
7
7
|
} from "../domains/safety/decision-presentation.js";
|
|
8
8
|
import type { Component, OverlayOptions, TUI } from "../engine/tui.js";
|
|
9
|
+
import {
|
|
10
|
+
MUTATION_PREVIEW_VISIBLE_ROWS,
|
|
11
|
+
type MutationFacts,
|
|
12
|
+
type MutationInspector,
|
|
13
|
+
type MutationPreview,
|
|
14
|
+
mutationFactsLine,
|
|
15
|
+
mutationPreviewWindow,
|
|
16
|
+
} from "./mutation-preview.js";
|
|
9
17
|
import { fitHintEntries } from "./overlay-frame.js";
|
|
10
|
-
import { permissionHintEntries } from "./permission-hint.js";
|
|
18
|
+
import { MUTATION_PREVIEW_KEY, type PermissionInspectionHint, permissionHintEntries } from "./permission-hint.js";
|
|
11
19
|
import type { ClioToken } from "./theme/index.js";
|
|
12
20
|
|
|
13
21
|
export { type AskAxis, askAxis } from "../domains/safety/approval-axis.js";
|
|
14
22
|
export { describeCallTarget, sanitizeCallTargetText } from "../domains/safety/call-target.js";
|
|
15
|
-
export { permissionHintEntries } from "./permission-hint.js";
|
|
23
|
+
export { MUTATION_PREVIEW_KEY, type PermissionInspectionHint, permissionHintEntries } from "./permission-hint.js";
|
|
16
24
|
|
|
17
25
|
export interface ApprovalRequestView {
|
|
18
26
|
requestId: string;
|
|
@@ -34,6 +42,14 @@ export interface ApprovalRequestView {
|
|
|
34
42
|
* when nothing meaningful is derivable.
|
|
35
43
|
*/
|
|
36
44
|
target?: string;
|
|
45
|
+
/**
|
|
46
|
+
* Size and digest facts for a parked `write` or `edit`, and nothing else
|
|
47
|
+
* about it. The mutation text is deliberately absent: this view is what
|
|
48
|
+
* reaches the transcript row, the parked notice, and the approval-state
|
|
49
|
+
* event, so anything on it has already left the overlay. The text itself is
|
|
50
|
+
* held by the inspector the overlay opener is given (issue #254).
|
|
51
|
+
*/
|
|
52
|
+
mutation?: MutationFacts;
|
|
37
53
|
queueDepth?: number;
|
|
38
54
|
}
|
|
39
55
|
|
|
@@ -79,11 +95,59 @@ export function permissionOverlayPlacement(
|
|
|
79
95
|
};
|
|
80
96
|
}
|
|
81
97
|
|
|
82
|
-
|
|
83
|
-
|
|
98
|
+
/**
|
|
99
|
+
* The overlay body, plus the inspection state the key router drives. The
|
|
100
|
+
* preview is built on the first toggle rather than per frame, so the file read
|
|
101
|
+
* and the diff happen once when the operator asks for them.
|
|
102
|
+
*/
|
|
103
|
+
export interface PermissionOverlayBodyHandle extends Component {
|
|
104
|
+
/** Whether this card has a mutation the operator can read locally. */
|
|
105
|
+
canInspect(): boolean;
|
|
106
|
+
isInspecting(): boolean;
|
|
107
|
+
toggleInspect(): void;
|
|
108
|
+
scrollInspect(delta: number): void;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
class PermissionOverlayBody implements PermissionOverlayBodyHandle {
|
|
112
|
+
private preview: MutationPreview | null = null;
|
|
113
|
+
private scroll = 0;
|
|
114
|
+
private lastLineCount = 0;
|
|
115
|
+
|
|
116
|
+
constructor(
|
|
117
|
+
private readonly view: ApprovalRequestView,
|
|
118
|
+
private readonly inspect?: MutationInspector,
|
|
119
|
+
) {}
|
|
120
|
+
|
|
121
|
+
canInspect(): boolean {
|
|
122
|
+
return this.inspect !== undefined;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
isInspecting(): boolean {
|
|
126
|
+
return this.preview !== null;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
toggleInspect(): void {
|
|
130
|
+
if (this.inspect === undefined) return;
|
|
131
|
+
if (this.preview !== null) {
|
|
132
|
+
this.preview = null;
|
|
133
|
+
this.scroll = 0;
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
this.preview = this.inspect();
|
|
137
|
+
this.scroll = 0;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
scrollInspect(delta: number): void {
|
|
141
|
+
if (this.preview === null) return;
|
|
142
|
+
const maxScroll = Math.max(0, this.lastLineCount - MUTATION_PREVIEW_VISIBLE_ROWS);
|
|
143
|
+
this.scroll = Math.max(0, Math.min(this.scroll + delta, maxScroll));
|
|
144
|
+
}
|
|
84
145
|
|
|
85
146
|
render(width: number): string[] {
|
|
86
|
-
return permissionOverlayLines(this.view, width);
|
|
147
|
+
if (this.preview === null) return permissionOverlayLines(this.view, width);
|
|
148
|
+
const rendered = permissionInspectionLines(this.view, this.preview, width, this.scroll);
|
|
149
|
+
this.lastLineCount = rendered.wrappedLineCount;
|
|
150
|
+
return rendered.lines;
|
|
87
151
|
}
|
|
88
152
|
|
|
89
153
|
invalidate(): void {}
|
|
@@ -167,14 +231,74 @@ export function permissionOverlayTone(view: ApprovalRequestView): ClioToken {
|
|
|
167
231
|
* three columns the bottom border spends on `─ ` and the trailing space come
|
|
168
232
|
* off the top, which is what `buildResponsiveHint` does for every other frame.
|
|
169
233
|
*/
|
|
170
|
-
export function permissionOverlayHint(
|
|
171
|
-
|
|
234
|
+
export function permissionOverlayHint(
|
|
235
|
+
innerWidth: number,
|
|
236
|
+
composerHasDraft = false,
|
|
237
|
+
inspection: PermissionInspectionHint = "none",
|
|
238
|
+
): string {
|
|
239
|
+
return fitHintEntries(permissionHintEntries(composerHasDraft, inspection), innerWidth - 3);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/** The tools whose parked call mutates a file, so a card without a preview owes the operator a reason. */
|
|
243
|
+
function isFileMutationTool(tool: string): boolean {
|
|
244
|
+
return tool === "write" || tool === "edit";
|
|
172
245
|
}
|
|
173
246
|
|
|
174
247
|
function actionConsequence(presentation: DecisionPresentation, id: "deny" | "stop"): string {
|
|
175
248
|
return presentation.requiredActions.find((action) => action.id === id)?.consequence ?? "";
|
|
176
249
|
}
|
|
177
250
|
|
|
251
|
+
/**
|
|
252
|
+
* Hard wrap for mutation text. Prose wraps on words; a proposed file line and a
|
|
253
|
+
* diff row do not, because the column a character sits in is part of what the
|
|
254
|
+
* operator is reading. The continuation marker says the row was folded rather
|
|
255
|
+
* than cut, which is the difference between a wrap and unmarked truncation.
|
|
256
|
+
*/
|
|
257
|
+
function wrapMutationLine(value: string, max: number): string[] {
|
|
258
|
+
if (max <= 1) return [value];
|
|
259
|
+
if (value.length <= max) return [value];
|
|
260
|
+
const lines: string[] = [];
|
|
261
|
+
let remaining = value;
|
|
262
|
+
while (remaining.length > max) {
|
|
263
|
+
lines.push(`${remaining.slice(0, max - 1)}↩`);
|
|
264
|
+
remaining = remaining.slice(max - 1);
|
|
265
|
+
}
|
|
266
|
+
lines.push(remaining);
|
|
267
|
+
return lines;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* The card with the mutation open. The tool, target, and digest facts stay at
|
|
272
|
+
* the top so the bytes on screen remain tied to the call they belong to, and
|
|
273
|
+
* the standing safety prose gives way to the mutation itself: the operator
|
|
274
|
+
* opened this to read what changes, and the keys that answer the ask are on the
|
|
275
|
+
* footer throughout.
|
|
276
|
+
*/
|
|
277
|
+
function permissionInspectionLines(
|
|
278
|
+
view: ApprovalRequestView,
|
|
279
|
+
preview: MutationPreview,
|
|
280
|
+
width: number,
|
|
281
|
+
scroll: number,
|
|
282
|
+
): { lines: string[]; wrappedLineCount: number } {
|
|
283
|
+
const content = Math.max(8, Math.floor(width));
|
|
284
|
+
const wrapped = preview.body.flatMap((line) => wrapMutationLine(line, content));
|
|
285
|
+
const pane = mutationPreviewWindow(wrapped, scroll);
|
|
286
|
+
const notes: string[] = [];
|
|
287
|
+
if (preview.neutralized) notes.push("control characters shown as ·");
|
|
288
|
+
if (preview.tabsExpanded) notes.push("tabs shown as spaces");
|
|
289
|
+
const lines = [
|
|
290
|
+
field("Tool: ", `${view.tool} · Action: ${view.actionClass}`, content),
|
|
291
|
+
...(view.target !== undefined && view.target.length > 0 ? [field("Target: ", view.target, content)] : []),
|
|
292
|
+
...wrapSentence(`Mutation: ${mutationFactsLine(preview.facts)}`, content),
|
|
293
|
+
"",
|
|
294
|
+
...wrapSentence(preview.heading, content),
|
|
295
|
+
...pane.window,
|
|
296
|
+
...(pane.position === null ? [] : [pane.position]),
|
|
297
|
+
...(notes.length > 0 ? [`Rendered safely: ${notes.join(", ")}.`] : []),
|
|
298
|
+
];
|
|
299
|
+
return { lines, wrappedLineCount: wrapped.length };
|
|
300
|
+
}
|
|
301
|
+
|
|
178
302
|
/**
|
|
179
303
|
* The overlay's body, laid out for the width the frame gives it.
|
|
180
304
|
*
|
|
@@ -193,6 +317,24 @@ export function permissionOverlayLines(view: ApprovalRequestView, width: number)
|
|
|
193
317
|
const lines = [
|
|
194
318
|
field("Tool: ", `${view.tool} · Action: ${view.actionClass}`, content),
|
|
195
319
|
...(view.target !== undefined && view.target.length > 0 ? [field("Target: ", view.target, content)] : []),
|
|
320
|
+
// Size and digest stay on the collapsed card whether or not the operator
|
|
321
|
+
// opens the mutation, so the decision always carries the identity of the
|
|
322
|
+
// bytes it applies to.
|
|
323
|
+
// Wrapped, never ellipsized: at 40 columns the digest is the tail of the
|
|
324
|
+
// line, and ellipsizing it would drop the one fact that ties the decision
|
|
325
|
+
// to the bytes it applies to.
|
|
326
|
+
...(view.mutation !== undefined
|
|
327
|
+
? [
|
|
328
|
+
...wrapSentence(`Mutation: ${mutationFactsLine(view.mutation)}`, content),
|
|
329
|
+
...wrapSentence(`Press ${MUTATION_PREVIEW_KEY} to read it before deciding.`, content),
|
|
330
|
+
]
|
|
331
|
+
: []),
|
|
332
|
+
...(view.mutation === undefined && view.origin.kind === "worker" && isFileMutationTool(view.tool)
|
|
333
|
+
? wrapSentence(
|
|
334
|
+
"No local preview: this call's arguments stay inside the worker, so you are approving the target above without inspecting the mutation.",
|
|
335
|
+
content,
|
|
336
|
+
)
|
|
337
|
+
: []),
|
|
196
338
|
...wrapSentence(`Requested by: ${presentation.requestedByCopy}`, content),
|
|
197
339
|
...(view.artifact !== undefined
|
|
198
340
|
? [
|
|
@@ -215,6 +357,14 @@ export function permissionOverlayLines(view: ApprovalRequestView, width: number)
|
|
|
215
357
|
return lines;
|
|
216
358
|
}
|
|
217
359
|
|
|
218
|
-
|
|
219
|
-
|
|
360
|
+
/**
|
|
361
|
+
* The overlay body for one parked call. The inspector, when present, is the
|
|
362
|
+
* only path the mutation text takes: it is never placed on `view`, so nothing
|
|
363
|
+
* that renders or forwards the view can carry the bytes with it.
|
|
364
|
+
*/
|
|
365
|
+
export function createPermissionOverlayBody(
|
|
366
|
+
view: ApprovalRequestView,
|
|
367
|
+
inspect?: MutationInspector,
|
|
368
|
+
): PermissionOverlayBodyHandle {
|
|
369
|
+
return new PermissionOverlayBody(view, inspect);
|
|
220
370
|
}
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The session pre-warm round: the request the next turn would send, minus the
|
|
3
|
+
* operator's text, issued as soon as the prefix is known.
|
|
4
|
+
*
|
|
5
|
+
* On a local server prefill is the cost. A fresh session's first turn prefills
|
|
6
|
+
* the whole compiled prompt plus the tool schemas before the model emits a
|
|
7
|
+
* token, and a resumed session's first turn prefills the entire replayed
|
|
8
|
+
* history. Both are paid after the operator presses Enter, and both are fully
|
|
9
|
+
* determined before they type anything. llama.cpp picks the slot with the
|
|
10
|
+
* longest common prefix and re-evaluates only the suffix, so sending the prefix
|
|
11
|
+
* early leaves the processed KV in the slot for the real turn to land on.
|
|
12
|
+
*
|
|
13
|
+
* The payload is not hand-assembled. It goes through `streamSimple`, the same
|
|
14
|
+
* dispatcher `createEngineAgent` hands pi-agent-core as its `streamFn`, with the
|
|
15
|
+
* same system prompt, the same tool schemas, the same replayed messages, and the
|
|
16
|
+
* same thinking level, so `withSamplingOverrides`, the thinking composition, and
|
|
17
|
+
* `cache_prompt` resolve exactly as they do for a turn. Any byte that differs
|
|
18
|
+
* ahead of the user turn would defeat the purpose.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
import type { BackendCompletionTimings } from "../core/cache-telemetry.js";
|
|
22
|
+
import { streamSimple } from "../engine/ai.js";
|
|
23
|
+
import type { AgentMessage, AgentTool, EngineModel, Usage } from "../engine/types.js";
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* The one user message appended so the chat template renders the prefix up to
|
|
27
|
+
* the user turn. One character: it has to exist for the template to close the
|
|
28
|
+
* prefix, and everything after the last shared byte is re-evaluated by the real
|
|
29
|
+
* turn anyway.
|
|
30
|
+
*/
|
|
31
|
+
export const PREWARM_USER_TEXT = ".";
|
|
32
|
+
|
|
33
|
+
/** Output budget for the round. The tokens the model produces are not the point. */
|
|
34
|
+
export const PREWARM_MAX_TOKENS = 1;
|
|
35
|
+
|
|
36
|
+
/** Which trigger asked for the pre-warm. Recorded on the ledger entry. */
|
|
37
|
+
export type PrewarmTrigger = "session-start" | "resume" | "compaction";
|
|
38
|
+
|
|
39
|
+
export interface PrewarmContext {
|
|
40
|
+
systemPrompt: string;
|
|
41
|
+
messages: ReadonlyArray<AgentMessage>;
|
|
42
|
+
tools: ReadonlyArray<AgentTool>;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* The roles pi-agent-core's default `convertToLlm` keeps when it turns the
|
|
47
|
+
* agent's message list into provider messages. The pre-warm applies the same
|
|
48
|
+
* filter so its message array is the one a turn would send, not the agent's
|
|
49
|
+
* internal list.
|
|
50
|
+
*/
|
|
51
|
+
const LLM_MESSAGE_ROLES: ReadonlySet<string> = new Set(["user", "assistant", "toolResult"]);
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* The prefix of the next turn's request: system prompt, tool schemas, and the
|
|
55
|
+
* replayed conversation, with no user turn yet.
|
|
56
|
+
*/
|
|
57
|
+
function prewarmPrefix(state: {
|
|
58
|
+
systemPrompt: string;
|
|
59
|
+
messages: ReadonlyArray<AgentMessage>;
|
|
60
|
+
tools: ReadonlyArray<AgentTool>;
|
|
61
|
+
}): PrewarmContext {
|
|
62
|
+
return {
|
|
63
|
+
systemPrompt: state.systemPrompt,
|
|
64
|
+
messages: state.messages.filter((message) => LLM_MESSAGE_ROLES.has((message as { role?: string }).role ?? "")),
|
|
65
|
+
tools: [...state.tools],
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export interface PrewarmRoundInput {
|
|
70
|
+
model: EngineModel;
|
|
71
|
+
/** Live agent state the next turn would send. Read-only; the round copies it. */
|
|
72
|
+
state: {
|
|
73
|
+
systemPrompt: string;
|
|
74
|
+
messages: ReadonlyArray<AgentMessage>;
|
|
75
|
+
tools: ReadonlyArray<AgentTool>;
|
|
76
|
+
thinkingLevel: string;
|
|
77
|
+
};
|
|
78
|
+
apiKey?: string;
|
|
79
|
+
signal?: AbortSignal;
|
|
80
|
+
/** Test seam. Production uses the engine dispatcher a turn runs on. */
|
|
81
|
+
streamFn?: typeof streamSimple;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export interface PrewarmRoundResult {
|
|
85
|
+
/** True when the operator submitted (or the session moved) before the round settled. */
|
|
86
|
+
aborted: boolean;
|
|
87
|
+
/** Provider usage for the round; null when the backend reported none. */
|
|
88
|
+
usage: Usage | null;
|
|
89
|
+
/** Backend prefill and prediction timings when the server reported them. */
|
|
90
|
+
backend: BackendCompletionTimings | null;
|
|
91
|
+
/** Wall clock the round spent, in the shape a turn's assistant entry uses. */
|
|
92
|
+
timing: { ttftMs: number | null; apiMs: number };
|
|
93
|
+
/** Provider error text when the round failed outright. */
|
|
94
|
+
errorMessage: string | null;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function usageOf(message: unknown): Usage | null {
|
|
98
|
+
if (message === null || typeof message !== "object") return null;
|
|
99
|
+
const usage = (message as { usage?: unknown }).usage;
|
|
100
|
+
return usage !== null && typeof usage === "object" ? (usage as Usage) : null;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function backendOf(message: unknown): BackendCompletionTimings | null {
|
|
104
|
+
if (message === null || typeof message !== "object") return null;
|
|
105
|
+
const backend = (message as { backendTimings?: unknown }).backendTimings;
|
|
106
|
+
return backend !== null && typeof backend === "object" ? (backend as BackendCompletionTimings) : null;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Prompt tokens the backend actually processed for one pre-warm. The server's
|
|
111
|
+
* own prompt count is authoritative when it reported one; otherwise the
|
|
112
|
+
* provider usage prompt side, cached tokens folded in, is the honest figure.
|
|
113
|
+
* Null when neither exists, which is different from a measured zero.
|
|
114
|
+
*/
|
|
115
|
+
export function prewarmPromptTokens(result: Pick<PrewarmRoundResult, "usage" | "backend">): number | null {
|
|
116
|
+
if (result.backend) return result.backend.promptTokens;
|
|
117
|
+
const usage = result.usage;
|
|
118
|
+
if (!usage) return null;
|
|
119
|
+
const total = (usage.input || 0) + (usage.cacheRead || 0) + (usage.cacheWrite || 0);
|
|
120
|
+
return total > 0 ? total : null;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Send the prefix and wait for the backend to acknowledge it. Resolves rather
|
|
125
|
+
* than rejects on a provider failure: a pre-warm is an optimization, and a
|
|
126
|
+
* backend that refuses it must not be able to break the session it was warming.
|
|
127
|
+
*/
|
|
128
|
+
export async function runPrewarmRound(input: PrewarmRoundInput): Promise<PrewarmRoundResult> {
|
|
129
|
+
const prefix = prewarmPrefix(input.state);
|
|
130
|
+
const context = {
|
|
131
|
+
systemPrompt: prefix.systemPrompt,
|
|
132
|
+
messages: [
|
|
133
|
+
...prefix.messages,
|
|
134
|
+
{ role: "user", content: [{ type: "text", text: PREWARM_USER_TEXT }], timestamp: Date.now() },
|
|
135
|
+
],
|
|
136
|
+
tools: prefix.tools,
|
|
137
|
+
};
|
|
138
|
+
// pi-agent-core maps `thinkingLevel: "off"` onto an absent `reasoning`; the
|
|
139
|
+
// pre-warm has to make the same mapping or the thinking composition resolves
|
|
140
|
+
// against a different level and the rendered template moves.
|
|
141
|
+
const options: Record<string, unknown> = { maxTokens: PREWARM_MAX_TOKENS };
|
|
142
|
+
if (input.state.thinkingLevel !== "off") options.reasoning = input.state.thinkingLevel;
|
|
143
|
+
if (input.apiKey !== undefined) options.apiKey = input.apiKey;
|
|
144
|
+
if (input.signal !== undefined) options.signal = input.signal;
|
|
145
|
+
|
|
146
|
+
const send = input.streamFn ?? streamSimple;
|
|
147
|
+
const startedAt = performance.now();
|
|
148
|
+
let firstDeltaAt: number | null = null;
|
|
149
|
+
const elapsed = (): number => Math.round(Math.max(0, performance.now() - startedAt));
|
|
150
|
+
const ttft = (): number | null => (firstDeltaAt === null ? null : Math.round(Math.max(0, firstDeltaAt - startedAt)));
|
|
151
|
+
|
|
152
|
+
try {
|
|
153
|
+
const events = send(
|
|
154
|
+
input.model,
|
|
155
|
+
context as unknown as Parameters<typeof streamSimple>[1],
|
|
156
|
+
options as unknown as Parameters<typeof streamSimple>[2],
|
|
157
|
+
);
|
|
158
|
+
for await (const event of events) {
|
|
159
|
+
if (firstDeltaAt === null && event.type !== "start") firstDeltaAt = performance.now();
|
|
160
|
+
if (event.type === "done") {
|
|
161
|
+
return {
|
|
162
|
+
aborted: false,
|
|
163
|
+
usage: usageOf(event.message),
|
|
164
|
+
backend: backendOf(event.message),
|
|
165
|
+
timing: { ttftMs: ttft(), apiMs: elapsed() },
|
|
166
|
+
errorMessage: null,
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
if (event.type === "error") {
|
|
170
|
+
const failed = event.error as { stopReason?: unknown; errorMessage?: unknown };
|
|
171
|
+
const aborted = event.reason === "aborted" || failed.stopReason === "aborted" || input.signal?.aborted === true;
|
|
172
|
+
return {
|
|
173
|
+
aborted,
|
|
174
|
+
usage: usageOf(event.error),
|
|
175
|
+
backend: backendOf(event.error),
|
|
176
|
+
timing: { ttftMs: ttft(), apiMs: elapsed() },
|
|
177
|
+
errorMessage: aborted ? null : typeof failed.errorMessage === "string" ? failed.errorMessage : "pre-warm failed",
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
} catch (error) {
|
|
182
|
+
return {
|
|
183
|
+
aborted: input.signal?.aborted === true,
|
|
184
|
+
usage: null,
|
|
185
|
+
backend: null,
|
|
186
|
+
timing: { ttftMs: ttft(), apiMs: elapsed() },
|
|
187
|
+
errorMessage: input.signal?.aborted === true ? null : error instanceof Error ? error.message : String(error),
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
return {
|
|
191
|
+
aborted: input.signal?.aborted === true,
|
|
192
|
+
usage: null,
|
|
193
|
+
backend: null,
|
|
194
|
+
timing: { ttftMs: ttft(), apiMs: elapsed() },
|
|
195
|
+
errorMessage: null,
|
|
196
|
+
};
|
|
197
|
+
}
|