@iowarp/clio-coder 0.3.6 → 0.3.7
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 +28 -0
- package/README.md +16 -5
- package/dist/{acp-2BEHC4DL.js → acp-SK4MD6MM.js} +10 -10
- package/dist/{agents-LNNFTM53.js → agents-2FN2K6ME.js} +30 -25
- package/dist/assets/codewiki.json +1 -1
- package/dist/{auth-KXXFI2VS.js → auth-QIYZWM5I.js} +13 -13
- package/dist/{chunk-KHSFENX2.js → chunk-3HAPLH5M.js} +10 -10
- package/dist/{chunk-24I7BN55.js → chunk-465YSENW.js} +2 -2
- package/dist/{chunk-4OC57DA6.js → chunk-4DGYLA73.js} +53 -2
- package/dist/{chunk-CYQKWTG3.js → chunk-4DWFMQDR.js} +4 -4
- package/dist/{chunk-E2ER4LJF.js → chunk-5C3AQNDW.js} +25 -1
- package/dist/{chunk-22NAGB7X.js → chunk-5C77SEEY.js} +5 -94
- package/dist/{chunk-43AOLP7E.js → chunk-5FR74PWO.js} +2 -1
- package/dist/{chunk-K7T3E2SR.js → chunk-5UJ6ECTS.js} +10 -9
- package/dist/{chunk-6US73PDB.js → chunk-6M7VS3J3.js} +5 -5
- package/dist/{chunk-CJUB2JJ2.js → chunk-6TUKSZVF.js} +5 -5
- package/dist/{chunk-5JGRAMKL.js → chunk-AB4XIIVB.js} +8 -6
- package/dist/{chunk-R46L2BIR.js → chunk-BMWK7ZIZ.js} +14 -20
- package/dist/{chunk-4BPJXDWC.js → chunk-C4JBQ5SR.js} +30 -14
- package/dist/{chunk-XXQNGV4M.js → chunk-CEYBNUGC.js} +243 -63
- package/dist/{chunk-VEZEGCGW.js → chunk-D4MDIG46.js} +20 -18
- package/dist/chunk-DJNLUABN.js +843 -0
- package/dist/{chunk-RY3LY4J5.js → chunk-DMD2AGVS.js} +5 -4
- package/dist/{chunk-KOHPCX4K.js → chunk-DOOEX22V.js} +2 -2
- package/dist/chunk-DQA7QLMD.js +123 -0
- package/dist/chunk-DR52UMZW.js +21 -0
- package/dist/{chunk-XF5N4U5A.js → chunk-EBEFWSGL.js} +6 -5
- package/dist/{chunk-EYPA3EGJ.js → chunk-EELBMBT6.js} +120 -13
- package/dist/{chunk-CKXWIANG.js → chunk-EOOQZZDE.js} +16 -14
- package/dist/{chunk-WR67VIZY.js → chunk-FOT2FX5J.js} +63 -5
- package/dist/{chunk-FYYLNIL5.js → chunk-GH5622CP.js} +2 -2
- package/dist/chunk-GWS3VEIW.js +195 -0
- package/dist/{chunk-LYF7OHWH.js → chunk-J7PIKKWC.js} +8 -463
- package/dist/{chunk-NILBFAPG.js → chunk-JNXPYBB4.js} +2 -2
- package/dist/{chunk-4VP4KH3K.js → chunk-JRIO5UD2.js} +4 -4
- package/dist/{chunk-6XXKFVSN.js → chunk-JTSEDYVQ.js} +7 -7
- package/dist/{chunk-QKMUKYO7.js → chunk-KCMKRQX4.js} +236 -84
- package/dist/chunk-KZ2H5X4G.js +1026 -0
- package/dist/{chunk-QNQHSOLF.js → chunk-LADCF22A.js} +12 -12
- package/dist/chunk-M4AKACEO.js +382 -0
- package/dist/{chunk-XYDYPRZI.js → chunk-MXI6J5JF.js} +7 -7
- package/dist/{chunk-G7MUEIGA.js → chunk-OB5HIGJY.js} +1 -1
- package/dist/{chunk-EKY57CSP.js → chunk-OBMAI2DP.js} +61 -767
- package/dist/chunk-PD3MESLB.js +242 -0
- package/dist/{chunk-ZRGEBJ4T.js → chunk-QCTRSGHQ.js} +21 -21
- package/dist/chunk-RVG5JXAL.js +41 -0
- package/dist/{chunk-RD5U66HV.js → chunk-SROCI7ZU.js} +7 -7
- package/dist/{chunk-MFFY33HR.js → chunk-THKY7CD7.js} +466 -205
- package/dist/{chunk-PCZJO5TI.js → chunk-UFQ3F4FW.js} +13 -178
- package/dist/{chunk-AD2SYQYC.js → chunk-UHXRNZ2J.js} +121 -3
- package/dist/chunk-UND3GU2L.js +103 -0
- package/dist/{chunk-QM3F2GKX.js → chunk-UUANF5CR.js} +2247 -2096
- package/dist/chunk-UVDSQ6LW.js +472 -0
- package/dist/{chunk-DJVECN66.js → chunk-VQNODYQ4.js} +14 -14
- package/dist/{chunk-3BPUFZDL.js → chunk-VREKEFLL.js} +3 -3
- package/dist/{chunk-PBTHKCPN.js → chunk-WJHBC77E.js} +6 -6
- package/dist/{chunk-XE2VEJHX.js → chunk-X2KV5FXT.js} +2 -2
- package/dist/{chunk-ZXF4XRKW.js → chunk-XEGB6BCN.js} +157 -7
- package/dist/{chunk-E25LMLRW.js → chunk-YD734TPH.js} +2 -2
- package/dist/{verifiers-NCBTHHN2.js → chunk-YTYFXUI3.js} +65 -322
- package/dist/{chunk-OH3TOQTB.js → chunk-ZGH7FGS5.js} +13 -7
- package/dist/cli/index.js +32 -30
- package/dist/{clio-M2KGYUFZ.js → clio-WBVQEBKO.js} +7 -7
- package/dist/{code-nav-GQNL7XA6.js → code-nav-FGGFIE7L.js} +3 -3
- package/dist/{components-5TTYYX6G.js → components-F7OEATSO.js} +4 -4
- package/dist/{config-XUUYQIWO.js → config-TRBL3RCF.js} +34 -29
- package/dist/{configure-IHJ7YOMV.js → configure-OLCVPHNM.js} +15 -15
- package/dist/{context-74JLXAWD.js → context-MJIJ6GOX.js} +11 -11
- package/dist/{context-ZQ7SIFJV.js → context-WFPKQSM6.js} +19 -3
- package/dist/{context-75MIWW3U.js → context-XEWE3MOJ.js} +31 -26
- package/dist/{context-clear-GYKWNUML.js → context-clear-KNOS2JPB.js} +31 -26
- package/dist/{context-working-set-UX5KEP4J.js → context-working-set-EUXAZI6N.js} +8 -8
- package/dist/{dispatch-runner-GIJBHNFL.js → dispatch-runner-B7MTOVKL.js} +313 -53
- package/dist/{docs-6FZSCG5B.js → docs-FLJTIDSE.js} +4 -4
- package/dist/{doctor-SVJ5BZCW.js → doctor-RN4YKO2X.js} +14 -14
- package/dist/{eval-CG6LLBLD.js → eval-RUBJVSNQ.js} +8 -7
- package/dist/{evidence-ZYFIEN42.js → evidence-JZNBUOQZ.js} +30 -25
- package/dist/{evolve-QGEXEMDW.js → evolve-FJVC4KKI.js} +30 -25
- package/dist/{extensions-ADGNCJJD.js → extensions-IQL36S7K.js} +4 -4
- package/dist/{fleet-S5R4ZOQY.js → fleet-BDKYJFCP.js} +214 -360
- package/dist/fleet-commands-ZFIWZSB3.js +70 -0
- package/dist/fleet-graph-Y6HPXIVF.js +125 -0
- package/dist/fleet-new-RDVJLHHH.js +48 -0
- package/dist/fleet-validate-BIYREGIK.js +79 -0
- package/dist/{init-5DRU55YR.js → init-LQUB5COQ.js} +44 -37
- package/dist/library-NJAHIGG4.js +217 -0
- package/dist/{memory-7YKKR6UC.js → memory-OG6HOYKM.js} +31 -26
- package/dist/{models-ZPOLRU2C.js → models-5ZG5XY7J.js} +21 -20
- package/dist/{monitor-US5F5YGZ.js → monitor-TJ7AMTGB.js} +49 -30
- package/dist/{orchestrator-E2AL4T5N.js → orchestrator-WZYB54DM.js} +3827 -581
- package/dist/{paths-E7KYAQWE.js → paths-XUC7GS6E.js} +4 -4
- package/dist/{reset-KZ652EK6.js → reset-PXQT45IY.js} +7 -7
- package/dist/{run-SRNBKDWD.js → run-FQ74YF62.js} +53 -45
- package/dist/{share-CGZE33UP.js → share-FW7SVCL3.js} +33 -9
- package/dist/{skills-S2X4DLY5.js → skills-7E7IRB3R.js} +24 -8
- package/dist/{skills-eval-W2GGIC4R.js → skills-eval-LI75W6OK.js} +34 -27
- package/dist/{targets-54SWINWB.js → targets-4CIFKCTW.js} +23 -22
- package/dist/{terminal-lease-SAIF2OGY.js → terminal-lease-WUZY7ZV5.js} +4 -4
- package/dist/{uninstall-BVLWXKBT.js → uninstall-7FV7IP4E.js} +4 -4
- package/dist/{upgrade-JKAR27XC.js → upgrade-K2HVIVMQ.js} +20 -19
- package/dist/{usage-MSAWCLX4.js → usage-GTZELZQX.js} +116 -49
- package/dist/verifiers-RLAHT27O.js +336 -0
- package/dist/{verify-X5HDROLA.js → verify-BX3BRKH5.js} +7 -6
- package/dist/{wiki-generate-GUSOQ6ZP.js → wiki-generate-ASIFASCN.js} +45 -37
- package/dist/worker/entry.js +38 -35
- package/docs/README.md +3 -2
- package/docs/acp.md +1 -1
- package/docs/alcf-provider.md +1 -1
- package/docs/architecture.md +2 -2
- package/docs/artifact-versions.md +10 -6
- package/docs/built-in-agents.md +26 -2
- package/docs/capacity-and-scheduling.md +1 -1
- package/docs/commands-and-modes.md +82 -2
- package/docs/configuration-and-targets.md +79 -2
- package/docs/context-engine.md +1 -1
- package/docs/development-pipeline.md +1 -1
- package/docs/dispatch-architecture-rationale.md +1 -1
- package/docs/documentation-coverage.md +3 -3
- package/docs/documentation-guide.md +3 -3
- package/docs/eval-runner.md +1 -1
- package/docs/evals-internal.md +1 -1
- package/docs/evidence-and-memory.md +5 -5
- package/docs/evolution.md +1 -1
- package/docs/exit-codes-and-output.md +4 -1
- package/docs/extensions-and-sharing.md +6 -2
- package/docs/fleet-demo-runbook.md +2 -2
- package/docs/fleet-dispatch.md +197 -10
- package/docs/git-commit-provenance.md +2 -2
- package/docs/glossary.md +1 -1
- package/docs/installation-and-lifecycle.md +2 -2
- package/docs/middleware-and-components.md +2 -1
- package/docs/model-catalog.md +1 -1
- package/docs/observability.md +55 -8
- package/docs/proactive-memory.md +1 -1
- package/docs/prompt-envelope-and-tools.md +1 -1
- package/docs/provider-adapter-cookbook.md +1 -1
- package/docs/release-cut-checklist.md +79 -64
- package/docs/resource-library.md +59 -0
- package/docs/safety-model.md +2 -2
- package/docs/scientific-validation.md +3 -3
- package/docs/session-lifecycle.md +37 -1
- package/docs/skills-marketplace.md +16 -3
- package/docs/tool-usage.md +14 -7
- package/docs/trace-store.md +1 -1
- package/docs/troubleshooting.md +1 -1
- package/docs/tui-design.md +1 -1
- package/docs/worker-dispatch-mechanics.md +3 -3
- package/package.json +1 -1
- package/src/cli/fleet-commands.ts +37 -0
- package/src/cli/fleet-graph.ts +102 -0
- package/src/cli/fleet-new.ts +36 -0
- package/src/cli/fleet-preflight.ts +121 -0
- package/src/cli/fleet-validate.ts +30 -0
- package/src/cli/fleet.ts +173 -335
- package/src/cli/index.ts +3 -1
- package/src/cli/library.ts +190 -0
- package/src/cli/share.ts +13 -1
- package/src/cli/usage.ts +111 -19
- package/src/core/bus-events.ts +4 -0
- package/src/core/commit-attribution.ts +4 -4
- package/src/core/config.ts +130 -0
- package/src/core/defaults.ts +81 -0
- package/src/domains/agents/builtins/architect.md +1 -0
- package/src/domains/agents/builtins/oracle.md +33 -0
- package/src/domains/agents/catalog.ts +13 -1
- package/src/domains/agents/fleet-contract.ts +278 -16
- package/src/domains/agents/index.ts +14 -0
- package/src/domains/agents/result-contract.ts +235 -1
- package/src/domains/config/classify.ts +4 -0
- package/src/domains/dispatch/active-route-planner.ts +14 -0
- package/src/domains/dispatch/backoff.ts +2 -1
- package/src/domains/dispatch/capability-match.ts +1 -0
- package/src/domains/dispatch/checkout-writer-lease.ts +175 -0
- package/src/domains/dispatch/contract.ts +34 -0
- package/src/domains/dispatch/delegation-plan.ts +167 -0
- package/src/domains/dispatch/execution-plan.ts +76 -5
- package/src/domains/dispatch/execution-role.ts +3 -1
- package/src/domains/dispatch/execution-scheduler.ts +183 -67
- package/src/domains/dispatch/extension.ts +258 -9
- package/src/domains/dispatch/fleet-gate.ts +14 -0
- package/src/domains/dispatch/fleet-plan.ts +63 -3
- package/src/domains/dispatch/fleet-run.ts +737 -0
- package/src/domains/dispatch/gate-role-prompts.ts +9 -0
- package/src/domains/dispatch/host-verification.ts +178 -0
- package/src/domains/dispatch/index.ts +38 -0
- package/src/domains/dispatch/intent.ts +159 -0
- package/src/domains/dispatch/receipt-integrity.ts +8 -4
- package/src/domains/dispatch/state.ts +36 -3
- package/src/domains/dispatch/types.ts +51 -6
- package/src/domains/dispatch/validation.ts +66 -6
- package/src/domains/evidence/trust-status.ts +10 -1
- package/src/domains/middleware/index.ts +15 -0
- package/src/domains/middleware/watchdog.ts +281 -0
- package/src/domains/observability/contract.ts +3 -1
- package/src/domains/observability/cost.ts +12 -1
- package/src/domains/observability/extension.ts +2 -2
- package/src/domains/observability/index.ts +10 -0
- package/src/domains/observability/out-of-turn-usage.ts +223 -0
- package/src/domains/resources/index.ts +20 -0
- package/src/domains/resources/library.ts +326 -0
- package/src/domains/resources/skills/marketplace.ts +37 -12
- package/src/domains/session/handoff.ts +629 -0
- package/src/domains/share/archive.ts +67 -2
- package/src/entry/orchestrator.ts +37 -0
- package/src/interactive/bus-notices.ts +26 -0
- package/src/interactive/chat-loop.ts +235 -1
- package/src/interactive/chat-renderer.ts +22 -0
- package/src/interactive/cost-overlay.ts +31 -3
- package/src/interactive/council-dispatch.ts +30 -0
- package/src/interactive/council-grid.ts +213 -0
- package/src/interactive/council.ts +99 -0
- package/src/interactive/dispatch-board.ts +260 -16
- package/src/interactive/fleet-run-preview.ts +307 -0
- package/src/interactive/footer/notifications.ts +219 -0
- package/src/interactive/handoff-round.ts +56 -0
- package/src/interactive/interactive-application.ts +43 -1
- package/src/interactive/interactive-event-projection.ts +9 -1
- package/src/interactive/interactive-slash-runtime.ts +52 -2
- package/src/interactive/interactive-subscriptions.ts +14 -2
- package/src/interactive/oracle.ts +179 -0
- package/src/interactive/overlay-ask-user-lifecycle.ts +6 -0
- package/src/interactive/overlay-general-openers.ts +190 -1
- package/src/interactive/overlay-key-routing.ts +17 -1
- package/src/interactive/overlay-lifecycle.ts +41 -1
- package/src/interactive/overlay-permission-lifecycle.ts +10 -0
- package/src/interactive/overlay-resource-openers.ts +11 -3
- package/src/interactive/overlay-session-lifecycle.ts +234 -2
- package/src/interactive/overlays/fleet-run-approval.ts +208 -0
- package/src/interactive/overlays/handoff-review.ts +185 -0
- package/src/interactive/overlays/library-install-confirm.ts +151 -0
- package/src/interactive/overlays/list-overlay.ts +168 -2
- package/src/interactive/overlays/settings.ts +101 -4
- package/src/interactive/overlays/side-question.ts +139 -0
- package/src/interactive/overlays/skills-hub.ts +401 -15
- package/src/interactive/side-question.ts +171 -0
- package/src/interactive/slash-commands.ts +432 -5
- package/src/interactive/slash-spec.ts +19 -6
- package/src/interactive/theme/tokens.ts +30 -0
- package/src/interactive/turn-middleware.ts +15 -1
- package/src/interactive/watchdog-run.ts +75 -0
- package/src/interactive/worker-share.ts +56 -1
- package/src/interactive/worker-stream.ts +7 -0
- package/src/tools/bootstrap.ts +3 -0
- package/src/tools/compete-worktrees.ts +13 -79
- package/src/tools/dispatch-admission.ts +242 -8
- package/src/tools/dispatch-arguments.ts +57 -1
- package/src/tools/dispatch-plan.ts +136 -6
- package/src/tools/dispatch-runner.ts +319 -13
- package/src/tools/dispatch-types.ts +20 -1
- package/src/tools/dispatch.ts +72 -2
- package/src/tools/monitor.ts +16 -0
- package/src/tools/profiles.ts +18 -4
- package/src/tools/task-worktree.ts +238 -0
- package/src/tools/verify/authoring.ts +61 -1
- package/src/tools/verify/scripts.ts +62 -0
- package/src/tools/worker-evidence.ts +2 -1
- package/src/worker/spec-contract.ts +1 -0
- package/dist/chunk-HC4CLZ2Y.js +0 -68
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { BusChannels } from "../core/bus-events.js";
|
|
2
2
|
import { THINKING_LEVELS } from "../core/defaults.js";
|
|
3
3
|
import type { SafeEventBus } from "../core/event-bus.js";
|
|
4
|
+
import { parseCouncilReport, parseOracleResult } from "../domains/agents/index.js";
|
|
4
5
|
import type { AgentSpec } from "../domains/agents/spec.js";
|
|
5
6
|
import type { ContextInitOptions } from "../domains/context/init-options.js";
|
|
6
7
|
import type { DispatchContract, DispatchRequest } from "../domains/dispatch/contract.js";
|
|
@@ -12,16 +13,46 @@ import type { InstalledExtension } from "../domains/extensions/index.js";
|
|
|
12
13
|
import type { InteropAgentId, InteropProposal, InteropReport } from "../domains/interop/index.js";
|
|
13
14
|
import type { ProvidersContract, ResolvedModelRef } from "../domains/providers/index.js";
|
|
14
15
|
import { resolveModelReference } from "../domains/providers/index.js";
|
|
15
|
-
import type {
|
|
16
|
+
import type {
|
|
17
|
+
LibraryEntryKind,
|
|
18
|
+
PromptTemplate,
|
|
19
|
+
PromptTemplateExpansion,
|
|
20
|
+
ResourceList,
|
|
21
|
+
} from "../domains/resources/index.js";
|
|
16
22
|
import { parseSkillCommand } from "../domains/resources/index.js";
|
|
17
23
|
import type { ShareImportPlan } from "../domains/share/index.js";
|
|
18
24
|
import type { UserTask } from "../domains/user-tasks/store.js";
|
|
19
25
|
import { isToolProfileName, TOOL_PROFILE_NAMES, type ToolProfileName } from "../tools/profiles.js";
|
|
20
26
|
import type { NoticeLevel } from "./command-output.js";
|
|
27
|
+
import {
|
|
28
|
+
buildCouncilDispatchArgs,
|
|
29
|
+
COUNCIL_MAX_ROUNDS,
|
|
30
|
+
COUNCIL_SYNTHESIS_MODES,
|
|
31
|
+
type CouncilCommandOptions,
|
|
32
|
+
type CouncilRosters,
|
|
33
|
+
isCouncilSynthesisMode,
|
|
34
|
+
resolveCouncilRoster,
|
|
35
|
+
} from "./council.js";
|
|
36
|
+
import { COUNCIL_SYNTHESIS_LABEL } from "./council-grid.js";
|
|
37
|
+
import {
|
|
38
|
+
formatOracleAnswer,
|
|
39
|
+
ORACLE_AGENT_ID,
|
|
40
|
+
ORACLE_TASK,
|
|
41
|
+
type OracleDigestSources,
|
|
42
|
+
packOracleDigest,
|
|
43
|
+
} from "./oracle.js";
|
|
21
44
|
import { SETTINGS_SECTIONS, type SettingsCenterRowId, type SettingsSectionId } from "./overlays/settings.js";
|
|
45
|
+
import { isLibraryTab, LIBRARY_TABS } from "./overlays/skills-hub.js";
|
|
22
46
|
import type { CommandArgsSpec, CommandPositionalSpec, ParsedArgs } from "./slash-spec.js";
|
|
23
47
|
import { matchFromSpec, usageLine } from "./slash-spec.js";
|
|
24
|
-
import {
|
|
48
|
+
import {
|
|
49
|
+
formatCouncilMemberShareNote,
|
|
50
|
+
formatCouncilShareNote,
|
|
51
|
+
formatWorkerShareNote,
|
|
52
|
+
selectWorkerRunToShare,
|
|
53
|
+
type WorkerShareFacts,
|
|
54
|
+
workerShareFactsFromEntry,
|
|
55
|
+
} from "./worker-share.js";
|
|
25
56
|
import type { WorkerEntryState } from "./worker-stream.js";
|
|
26
57
|
|
|
27
58
|
/**
|
|
@@ -47,6 +78,7 @@ type SlashCommandVariant =
|
|
|
47
78
|
/** `ref` is the turnId an `[evicted ...]` marker names. */
|
|
48
79
|
| { kind: "context-recall"; ref: string }
|
|
49
80
|
| { kind: "skill-selector" }
|
|
81
|
+
| { kind: "library"; tab?: LibraryEntryKind }
|
|
50
82
|
| { kind: "skill-invocation"; text: string }
|
|
51
83
|
| { kind: "prompts" }
|
|
52
84
|
| { kind: "extensions" }
|
|
@@ -57,6 +89,19 @@ type SlashCommandVariant =
|
|
|
57
89
|
| { kind: "run-usage" }
|
|
58
90
|
| { kind: "delegate"; agentId: string; task: string; source: string; share?: boolean }
|
|
59
91
|
| { kind: "delegate-usage" }
|
|
92
|
+
| { kind: "btw"; question: string }
|
|
93
|
+
| { kind: "btw-usage" }
|
|
94
|
+
/** `/oracle <question>`: one read-only advisory run briefed on the record, never on the transcript. */
|
|
95
|
+
| { kind: "oracle"; question: string }
|
|
96
|
+
| { kind: "oracle-usage" }
|
|
97
|
+
/** `/council <task>`: one read-only council of roster members, dispatched through the dispatch tool. */
|
|
98
|
+
| { kind: "council"; task: string; options: CouncilCommandOptions }
|
|
99
|
+
| { kind: "council-usage"; reason?: string }
|
|
100
|
+
| { kind: "handoff"; goal: string }
|
|
101
|
+
| { kind: "handoff-usage" }
|
|
102
|
+
/** `/fleet run <name>`: compile the plan, show it for approval, dispatch on accept. */
|
|
103
|
+
| { kind: "fleet-run"; name: string; vars: Record<string, string> }
|
|
104
|
+
| { kind: "fleet-run-usage"; reason?: string }
|
|
60
105
|
| { kind: "agents" }
|
|
61
106
|
| { kind: "cost" }
|
|
62
107
|
| { kind: "context-view" }
|
|
@@ -109,6 +154,16 @@ export type SetOutputVerbosityResult =
|
|
|
109
154
|
| { status: "unsupported"; verbosity: string; supported: ReadonlyArray<string> }
|
|
110
155
|
| { status: "unavailable" };
|
|
111
156
|
|
|
157
|
+
/**
|
|
158
|
+
* What one admitted dispatch-tool call reports back to `/council`. The council
|
|
159
|
+
* itself is watched on the board and in the transcript, so success is silent;
|
|
160
|
+
* only a refusal or a failure is worth a notice.
|
|
161
|
+
*/
|
|
162
|
+
export type CouncilDispatchOutcome =
|
|
163
|
+
| { status: "ok" }
|
|
164
|
+
| { status: "blocked"; reason: string }
|
|
165
|
+
| { status: "error"; message: string };
|
|
166
|
+
|
|
112
167
|
export type TaskMemorySeedCommandResult =
|
|
113
168
|
| { status: "seeded"; seeded: number; skipped: number; source: string }
|
|
114
169
|
| { status: "disabled" }
|
|
@@ -276,6 +331,136 @@ async function handleDelegate(
|
|
|
276
331
|
);
|
|
277
332
|
}
|
|
278
333
|
|
|
334
|
+
/**
|
|
335
|
+
* `/oracle <question>`: one advisory run, dispatched like any other.
|
|
336
|
+
*
|
|
337
|
+
* Nothing here is a side channel. The run goes through the ordinary dispatch
|
|
338
|
+
* path, so admission, the receipt, and the Fleet Runs island treat it exactly
|
|
339
|
+
* as they treat a `/run`; `requestOrigin: "internal"` is what lets a shadow
|
|
340
|
+
* recipe be reached at all, and `autonomy: "read-only"` narrows the worker
|
|
341
|
+
* below whatever the session holds. The answer reaches the main agent the way
|
|
342
|
+
* `/share` puts one there: an operator-authored note on the ordinary user-turn
|
|
343
|
+
* path, never a fabricated tool result.
|
|
344
|
+
*/
|
|
345
|
+
async function handleOracle(question: string, ctx: SlashCommandContext): Promise<void> {
|
|
346
|
+
if (!ctx.submitOperatorNote) {
|
|
347
|
+
ctx.notice("error", "/oracle is not wired in this session; there is nowhere to put the answer");
|
|
348
|
+
return;
|
|
349
|
+
}
|
|
350
|
+
// Refused, never queued. The digest describes the record as it stands and a
|
|
351
|
+
// turn still in flight is about to change it, so an answer written against
|
|
352
|
+
// the old record would be advice about a session that no longer exists.
|
|
353
|
+
if (ctx.isTurnInFlight?.() === true) {
|
|
354
|
+
ctx.notice("warn", "a turn is in flight; /oracle is refused rather than queued");
|
|
355
|
+
return;
|
|
356
|
+
}
|
|
357
|
+
const sources = ctx.oracleBriefing?.();
|
|
358
|
+
if (!sources) {
|
|
359
|
+
ctx.notice("error", "/oracle needs the session record, which is not wired in this session");
|
|
360
|
+
return;
|
|
361
|
+
}
|
|
362
|
+
const digest = packOracleDigest({ ...sources, question });
|
|
363
|
+
const request: DispatchRequest = {
|
|
364
|
+
agentId: ORACLE_AGENT_ID,
|
|
365
|
+
task: ORACLE_TASK,
|
|
366
|
+
briefing: digest.text,
|
|
367
|
+
executionRole: requestExecutionRole({
|
|
368
|
+
agentId: ORACLE_AGENT_ID,
|
|
369
|
+
...(ctx.getAgentRoleFacts ? { resolveFacts: ctx.getAgentRoleFacts } : {}),
|
|
370
|
+
}),
|
|
371
|
+
requestOrigin: "internal",
|
|
372
|
+
autonomy: "read-only",
|
|
373
|
+
};
|
|
374
|
+
const progressBus = ctx.dispatch.ownsProgressBus?.(ctx.bus) === true ? undefined : ctx.bus;
|
|
375
|
+
try {
|
|
376
|
+
const handle = await ctx.dispatch.dispatch(request);
|
|
377
|
+
for await (const event of handle.events) {
|
|
378
|
+
const typed = event as { type?: string };
|
|
379
|
+
if (!typed.type || typed.type === "heartbeat") continue;
|
|
380
|
+
progressBus?.emit(BusChannels.DispatchProgress, { runId: handle.runId, agentId: ORACLE_AGENT_ID, event });
|
|
381
|
+
}
|
|
382
|
+
const receipt = await handle.finalPromise;
|
|
383
|
+
const parsed = parseOracleResult(receipt.output?.text ?? null);
|
|
384
|
+
if (parsed === null) {
|
|
385
|
+
ctx.notice("error", `/oracle run ${receipt.runId} returned no usable answer (${receipt.outcome})`);
|
|
386
|
+
return;
|
|
387
|
+
}
|
|
388
|
+
const note = formatWorkerShareNote({
|
|
389
|
+
agentId: ORACLE_AGENT_ID,
|
|
390
|
+
runId: receipt.runId,
|
|
391
|
+
outcome: receipt.outcome,
|
|
392
|
+
text: formatOracleAnswer(parsed),
|
|
393
|
+
});
|
|
394
|
+
if (note === null) {
|
|
395
|
+
ctx.notice("error", `/oracle run ${receipt.runId} produced no text to share`);
|
|
396
|
+
return;
|
|
397
|
+
}
|
|
398
|
+
ctx.submitOperatorNote(note);
|
|
399
|
+
} catch (err) {
|
|
400
|
+
ctx.notice("error", `/oracle failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
/**
|
|
405
|
+
* `/council <task>`: one council, dispatched exactly as the model dispatches one.
|
|
406
|
+
*
|
|
407
|
+
* The command builds dispatch-tool arguments and hands them to the tool
|
|
408
|
+
* registry. That is the whole of it, and it is deliberate: a council is a
|
|
409
|
+
* plan-scale dispatch, so supervised autonomy parks the call and the approval
|
|
410
|
+
* overlay shows the same artifact it shows for a model-asked council, naming
|
|
411
|
+
* every member's label, target, model, node, round count, and synthesis mode.
|
|
412
|
+
* A private path would have had to reproduce that, and would eventually have
|
|
413
|
+
* reproduced it wrongly.
|
|
414
|
+
*/
|
|
415
|
+
async function handleCouncil(task: string, options: CouncilCommandOptions, ctx: SlashCommandContext): Promise<void> {
|
|
416
|
+
// Refused, never queued, exactly as `/oracle` and `/fleet run` are. An
|
|
417
|
+
// approved council describes the workspace as it stands, and a turn in
|
|
418
|
+
// flight is about to change it.
|
|
419
|
+
if (ctx.isTurnInFlight?.() === true) {
|
|
420
|
+
ctx.notice("warn", "a turn is in flight; /council is refused rather than queued");
|
|
421
|
+
return;
|
|
422
|
+
}
|
|
423
|
+
if (!ctx.runCouncilDispatch) {
|
|
424
|
+
ctx.notice("error", "/council is not wired in this session");
|
|
425
|
+
return;
|
|
426
|
+
}
|
|
427
|
+
const rosters = ctx.getWorkerRosters?.();
|
|
428
|
+
if (rosters === undefined) {
|
|
429
|
+
ctx.notice("error", "/council needs workers.rosters, which is not wired in this session");
|
|
430
|
+
return;
|
|
431
|
+
}
|
|
432
|
+
const resolved = resolveCouncilRoster(options.roster, rosters);
|
|
433
|
+
if (!resolved.ok) {
|
|
434
|
+
ctx.notice("error", resolved.reason);
|
|
435
|
+
return;
|
|
436
|
+
}
|
|
437
|
+
try {
|
|
438
|
+
const outcome = await ctx.runCouncilDispatch(buildCouncilDispatchArgs(task, resolved.roster, options));
|
|
439
|
+
if (outcome.status === "blocked") ctx.notice("warn", `/council was not admitted: ${outcome.reason}`);
|
|
440
|
+
else if (outcome.status === "error") ctx.notice("error", `/council failed: ${outcome.message}`);
|
|
441
|
+
} catch (err) {
|
|
442
|
+
ctx.notice("error", `/council failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
/**
|
|
447
|
+
* The note a run becomes, with a council run treated as what it is.
|
|
448
|
+
*
|
|
449
|
+
* A council synthesis run seals the whole `council-report`, so sharing its id
|
|
450
|
+
* shares the council: every final member's labelled answer and the synthesis,
|
|
451
|
+
* as one bounded block. A member run seals one voice, so it travels under the
|
|
452
|
+
* roster label the operator watched it under. Every other run keeps the note it
|
|
453
|
+
* has always had. A synthesis whose sealed text does not parse as a report is
|
|
454
|
+
* shared verbatim rather than dropped: the operator asked for that run.
|
|
455
|
+
*/
|
|
456
|
+
function councilAwareShareNote(facts: WorkerShareFacts, entry: WorkerEntryState): string | null {
|
|
457
|
+
const council = entry.council;
|
|
458
|
+
if (council === undefined) return formatWorkerShareNote(facts);
|
|
459
|
+
if (council.label !== COUNCIL_SYNTHESIS_LABEL) return formatCouncilMemberShareNote(facts, council.label);
|
|
460
|
+
const report = parseCouncilReport(facts.text);
|
|
461
|
+
return report === null ? formatWorkerShareNote(facts) : formatCouncilShareNote(facts, report);
|
|
462
|
+
}
|
|
463
|
+
|
|
279
464
|
/**
|
|
280
465
|
* `/share [runId]`: hand a finished worker run's answer to the main agent.
|
|
281
466
|
*
|
|
@@ -299,7 +484,7 @@ function shareWorkerRun(runId: string | undefined, ctx: SlashCommandContext): vo
|
|
|
299
484
|
return;
|
|
300
485
|
}
|
|
301
486
|
const facts = workerShareFactsFromEntry(entry);
|
|
302
|
-
const note = facts === null ? null :
|
|
487
|
+
const note = facts === null ? null : councilAwareShareNote(facts, entry);
|
|
303
488
|
if (note === null) {
|
|
304
489
|
ctx.notice("error", `run ${entry.runId} produced no text to share`);
|
|
305
490
|
return;
|
|
@@ -352,7 +537,7 @@ export interface SlashCommandContext {
|
|
|
352
537
|
* until the host wires a session.
|
|
353
538
|
*/
|
|
354
539
|
runContextRecall?: (ref: string) => void;
|
|
355
|
-
openSkillsHub?: () => void;
|
|
540
|
+
openSkillsHub?: (tab?: LibraryEntryKind) => void;
|
|
356
541
|
listPrompts: () => ResourceList<PromptTemplate>;
|
|
357
542
|
/**
|
|
358
543
|
* Resolve a `/name` against the loaded prompt templates. Absent when the host
|
|
@@ -380,6 +565,51 @@ export interface SlashCommandContext {
|
|
|
380
565
|
exportShareArchive?: (outPath: string) => { fileCount: number; path: string };
|
|
381
566
|
importShareArchive?: (path: string, options: { dryRun?: boolean; force?: boolean }) => ShareImportPlan;
|
|
382
567
|
openCost: () => void;
|
|
568
|
+
/**
|
|
569
|
+
* `/btw <question>`: one model round beside the session, answered in an
|
|
570
|
+
* overlay. Nothing about it enters the transcript, the ledger, or the task
|
|
571
|
+
* board, so the workers a fleet run briefs never see the question or its
|
|
572
|
+
* answer.
|
|
573
|
+
*/
|
|
574
|
+
openSideQuestion: (question: string) => void;
|
|
575
|
+
/**
|
|
576
|
+
* The record `/oracle` briefs its advisor on: settled decisions, the task
|
|
577
|
+
* board, and the last compaction summary. Absent on a host with no session,
|
|
578
|
+
* in which case `/oracle` says so instead of briefing on nothing.
|
|
579
|
+
*/
|
|
580
|
+
oracleBriefing?: () => Omit<OracleDigestSources, "question">;
|
|
581
|
+
/**
|
|
582
|
+
* Configured `workers.rosters`, which is what `/council` seats a council
|
|
583
|
+
* from. Absent on a host with no settings, in which case the command says so
|
|
584
|
+
* rather than dispatching a roster name it could not check.
|
|
585
|
+
*/
|
|
586
|
+
getWorkerRosters?: () => CouncilRosters;
|
|
587
|
+
/**
|
|
588
|
+
* Run one dispatch-tool call with prepared council arguments through the
|
|
589
|
+
* ordinary tool-admission path. The council command owns no dispatch path of
|
|
590
|
+
* its own, so admission, the approval overlay, receipts, and the board treat
|
|
591
|
+
* an operator council exactly as they treat a model-asked one.
|
|
592
|
+
*/
|
|
593
|
+
runCouncilDispatch?: (args: Readonly<Record<string, unknown>>) => Promise<CouncilDispatchOutcome>;
|
|
594
|
+
/**
|
|
595
|
+
* True while a turn is streaming. `/oracle` is refused then, never queued:
|
|
596
|
+
* the digest describes the record as it stands and an in-flight turn is
|
|
597
|
+
* about to change it.
|
|
598
|
+
*/
|
|
599
|
+
isTurnInFlight?: () => boolean;
|
|
600
|
+
/**
|
|
601
|
+
* `/handoff <goal>`: extract this session's working state for a stated goal,
|
|
602
|
+
* review the document, and seed a successor session with it. A session
|
|
603
|
+
* operation only. It writes no memory promotion candidate and never calls
|
|
604
|
+
* the task-memory bank.
|
|
605
|
+
*/
|
|
606
|
+
startHandoff: (goal: string) => void;
|
|
607
|
+
/**
|
|
608
|
+
* `/fleet run <name>`: compile the contract's plan, show every wave, agent,
|
|
609
|
+
* target, boundary, budget, and code-step argv for approval, and dispatch
|
|
610
|
+
* only what the operator accepted. Absent on a host with no fleet wiring.
|
|
611
|
+
*/
|
|
612
|
+
startFleetRun?: (name: string, vars: Readonly<Record<string, string>>) => void;
|
|
383
613
|
/** Open the read-only `/context` overlay: categorized context-window ledger. */
|
|
384
614
|
openContextView: () => void;
|
|
385
615
|
/** Open the read-only `/tasks` overlay: the session task board with receipts. */
|
|
@@ -591,6 +821,26 @@ export const BUILTIN_SLASH_COMMANDS: ReadonlyArray<BuiltinSlashCommand> = [
|
|
|
591
821
|
}
|
|
592
822
|
},
|
|
593
823
|
},
|
|
824
|
+
{
|
|
825
|
+
name: "library",
|
|
826
|
+
description: "Open the Skills Hub on a resource library tab",
|
|
827
|
+
group: "Run",
|
|
828
|
+
kinds: ["library"],
|
|
829
|
+
args: { positionals: [{ name: "kind", required: false }] },
|
|
830
|
+
fromArgs(parsed) {
|
|
831
|
+
if (parsed.error) return { kind: "usage-error", command: "library", reason: parsed.error };
|
|
832
|
+
const tab = parsed.positionals[0];
|
|
833
|
+
if (tab === undefined) return { kind: "library" };
|
|
834
|
+
if (!isLibraryTab(tab)) {
|
|
835
|
+
const known = LIBRARY_TABS.map((entry) => entry.id).join(", ");
|
|
836
|
+
return { kind: "usage-error", command: "library", reason: `Unknown kind: ${tab} (one of ${known})` };
|
|
837
|
+
}
|
|
838
|
+
return { kind: "library", tab };
|
|
839
|
+
},
|
|
840
|
+
handle(command, ctx) {
|
|
841
|
+
if (command.kind === "library") ctx.openSkillsHub?.(command.tab);
|
|
842
|
+
},
|
|
843
|
+
},
|
|
594
844
|
{
|
|
595
845
|
name: "prompts",
|
|
596
846
|
description: "List prompt templates",
|
|
@@ -850,6 +1100,108 @@ export const BUILTIN_SLASH_COMMANDS: ReadonlyArray<BuiltinSlashCommand> = [
|
|
|
850
1100
|
})();
|
|
851
1101
|
},
|
|
852
1102
|
},
|
|
1103
|
+
{
|
|
1104
|
+
name: "btw",
|
|
1105
|
+
description: "Ask a side question that never enters the session transcript",
|
|
1106
|
+
group: "Run",
|
|
1107
|
+
kinds: ["btw", "btw-usage"],
|
|
1108
|
+
args: {
|
|
1109
|
+
positionals: [{ name: "question", required: true, rest: true }],
|
|
1110
|
+
},
|
|
1111
|
+
fromArgs(parsed) {
|
|
1112
|
+
const question = parsed.rest?.trim() ?? "";
|
|
1113
|
+
if (parsed.error || question.length === 0) return { kind: "btw-usage" };
|
|
1114
|
+
return { kind: "btw", question };
|
|
1115
|
+
},
|
|
1116
|
+
handle(command, ctx) {
|
|
1117
|
+
if (command.kind === "btw-usage") {
|
|
1118
|
+
const entry = BUILTIN_SLASH_COMMANDS.find((candidate) => candidate.name === "btw");
|
|
1119
|
+
if (entry) ctx.notice("info", usageNotice(entry));
|
|
1120
|
+
return;
|
|
1121
|
+
}
|
|
1122
|
+
if (command.kind !== "btw") return;
|
|
1123
|
+
ctx.openSideQuestion(command.question);
|
|
1124
|
+
},
|
|
1125
|
+
},
|
|
1126
|
+
{
|
|
1127
|
+
name: "oracle",
|
|
1128
|
+
description: "Ask a read-only advisor to challenge a question against this session's settled decisions",
|
|
1129
|
+
group: "Run",
|
|
1130
|
+
kinds: ["oracle", "oracle-usage"],
|
|
1131
|
+
args: {
|
|
1132
|
+
positionals: [{ name: "question", required: true, rest: true }],
|
|
1133
|
+
},
|
|
1134
|
+
fromArgs(parsed) {
|
|
1135
|
+
const question = parsed.rest?.trim() ?? "";
|
|
1136
|
+
if (parsed.error || question.length === 0) return { kind: "oracle-usage" };
|
|
1137
|
+
return { kind: "oracle", question };
|
|
1138
|
+
},
|
|
1139
|
+
handle(command, ctx) {
|
|
1140
|
+
if (command.kind === "oracle-usage") {
|
|
1141
|
+
const entry = BUILTIN_SLASH_COMMANDS.find((candidate) => candidate.name === "oracle");
|
|
1142
|
+
if (entry) ctx.notice("info", usageNotice(entry));
|
|
1143
|
+
return;
|
|
1144
|
+
}
|
|
1145
|
+
if (command.kind !== "oracle") return;
|
|
1146
|
+
void (async () => {
|
|
1147
|
+
await handleOracle(command.question, ctx);
|
|
1148
|
+
ctx.render();
|
|
1149
|
+
})();
|
|
1150
|
+
},
|
|
1151
|
+
},
|
|
1152
|
+
{
|
|
1153
|
+
name: "council",
|
|
1154
|
+
description: "Ask a roster of read-only members the same task, with an optional vote or judge synthesis",
|
|
1155
|
+
group: "Run",
|
|
1156
|
+
kinds: ["council", "council-usage"],
|
|
1157
|
+
args: {
|
|
1158
|
+
parseFlagsBeforeRest: true,
|
|
1159
|
+
flags: [
|
|
1160
|
+
{ name: "--roster", takesValue: true, valueName: "name" },
|
|
1161
|
+
{ name: "--rounds", takesValue: true, valueName: "n" },
|
|
1162
|
+
{ name: "--synthesis", takesValue: true, values: COUNCIL_SYNTHESIS_MODES },
|
|
1163
|
+
],
|
|
1164
|
+
positionals: [{ name: "task", required: true, rest: true }],
|
|
1165
|
+
},
|
|
1166
|
+
fromArgs(parsed) {
|
|
1167
|
+
const task = parsed.rest?.trim() ?? "";
|
|
1168
|
+
if (parsed.error) return { kind: "council-usage", reason: parsed.error };
|
|
1169
|
+
if (task.length === 0) return { kind: "council-usage" };
|
|
1170
|
+
const options: CouncilCommandOptions = {};
|
|
1171
|
+
const roster = parsed.flags.get("--roster");
|
|
1172
|
+
if (typeof roster === "string") options.roster = roster;
|
|
1173
|
+
const rounds = parsed.flags.get("--rounds");
|
|
1174
|
+
if (typeof rounds === "string") {
|
|
1175
|
+
const parsedRounds = Number(rounds);
|
|
1176
|
+
// The tool bounds rounds at one to three, and a council that is
|
|
1177
|
+
// refused after admission has already shown the operator a plan it
|
|
1178
|
+
// cannot run, so the same bound is enforced where it was typed.
|
|
1179
|
+
if (!Number.isInteger(parsedRounds) || parsedRounds < 1 || parsedRounds > COUNCIL_MAX_ROUNDS) {
|
|
1180
|
+
return { kind: "council-usage", reason: `--rounds must be an integer 1..${COUNCIL_MAX_ROUNDS}` };
|
|
1181
|
+
}
|
|
1182
|
+
options.rounds = parsedRounds;
|
|
1183
|
+
}
|
|
1184
|
+
const synthesis = parsed.flags.get("--synthesis");
|
|
1185
|
+
if (typeof synthesis === "string") {
|
|
1186
|
+
if (!isCouncilSynthesisMode(synthesis)) return { kind: "council-usage" };
|
|
1187
|
+
options.synthesis = synthesis;
|
|
1188
|
+
}
|
|
1189
|
+
return { kind: "council", task, options };
|
|
1190
|
+
},
|
|
1191
|
+
handle(command, ctx) {
|
|
1192
|
+
if (command.kind === "council-usage") {
|
|
1193
|
+
const entry = BUILTIN_SLASH_COMMANDS.find((candidate) => candidate.name === "council");
|
|
1194
|
+
const usage = entry ? usageNotice(entry) : "usage: /council <task>";
|
|
1195
|
+
ctx.notice("info", command.reason ? `${command.reason}\n${usage}` : usage);
|
|
1196
|
+
return;
|
|
1197
|
+
}
|
|
1198
|
+
if (command.kind !== "council") return;
|
|
1199
|
+
void (async () => {
|
|
1200
|
+
await handleCouncil(command.task, command.options, ctx);
|
|
1201
|
+
ctx.render();
|
|
1202
|
+
})();
|
|
1203
|
+
},
|
|
1204
|
+
},
|
|
853
1205
|
{
|
|
854
1206
|
name: "agents",
|
|
855
1207
|
description: "List Clio agents and ACP delegation agents",
|
|
@@ -944,7 +1296,59 @@ export const BUILTIN_SLASH_COMMANDS: ReadonlyArray<BuiltinSlashCommand> = [
|
|
|
944
1296
|
}
|
|
945
1297
|
},
|
|
946
1298
|
},
|
|
947
|
-
|
|
1299
|
+
{
|
|
1300
|
+
name: "fleet",
|
|
1301
|
+
description: "Open Settings → Fleet, or run a fleet contract with an approval preview",
|
|
1302
|
+
group: "Configure",
|
|
1303
|
+
kinds: ["fleet-run", "fleet-run-usage"],
|
|
1304
|
+
subcommandDescriptions: { run: "Preview and run a repo-owned fleet contract" },
|
|
1305
|
+
args: {
|
|
1306
|
+
subcommands: {
|
|
1307
|
+
run: {
|
|
1308
|
+
flags: [{ name: "--var", takesValue: true, repeatable: true, valueName: "key=value" }],
|
|
1309
|
+
positionals: [{ name: "name", required: true }],
|
|
1310
|
+
},
|
|
1311
|
+
},
|
|
1312
|
+
},
|
|
1313
|
+
/**
|
|
1314
|
+
* `/fleet` alone keeps the settings deep link it has always been, and
|
|
1315
|
+
* only `run` parses as a command of its own, so an operator who types
|
|
1316
|
+
* `/fleet` out of habit still lands in Settings → Fleet. Anything else
|
|
1317
|
+
* on the line stays the usage error every other deep link reports.
|
|
1318
|
+
*/
|
|
1319
|
+
fromArgs(parsed) {
|
|
1320
|
+
if (parsed.subcommand !== "run") {
|
|
1321
|
+
return parsed.error
|
|
1322
|
+
? { kind: "usage-error", command: "fleet", reason: parsed.error }
|
|
1323
|
+
: { kind: "settings", section: "fleet" };
|
|
1324
|
+
}
|
|
1325
|
+
if (parsed.error) return { kind: "fleet-run-usage", reason: parsed.error };
|
|
1326
|
+
const name = parsed.positionals[0] ?? "";
|
|
1327
|
+
if (name.length === 0) return { kind: "fleet-run-usage" };
|
|
1328
|
+
const vars: Record<string, string> = {};
|
|
1329
|
+
for (const pair of parsed.flagValues.get("--var") ?? []) {
|
|
1330
|
+
const eq = pair.indexOf("=");
|
|
1331
|
+
const key = eq < 0 ? "" : pair.slice(0, eq).trim();
|
|
1332
|
+
if (key.length === 0) return { kind: "fleet-run-usage", reason: "--var requires key=value" };
|
|
1333
|
+
vars[key] = pair.slice(eq + 1);
|
|
1334
|
+
}
|
|
1335
|
+
return { kind: "fleet-run", name, vars };
|
|
1336
|
+
},
|
|
1337
|
+
handle(command, ctx) {
|
|
1338
|
+
if (command.kind === "fleet-run-usage") {
|
|
1339
|
+
const entry = BUILTIN_SLASH_COMMANDS.find((candidate) => candidate.name === "fleet");
|
|
1340
|
+
const usage = entry ? usageNotice(entry, "run") : "usage: /fleet run <name> [--var key=value ...]";
|
|
1341
|
+
ctx.notice("info", command.reason ? `${command.reason}\n${usage}` : usage);
|
|
1342
|
+
return;
|
|
1343
|
+
}
|
|
1344
|
+
if (command.kind !== "fleet-run") return;
|
|
1345
|
+
if (!ctx.startFleetRun) {
|
|
1346
|
+
ctx.notice("error", "/fleet run is not wired in this session");
|
|
1347
|
+
return;
|
|
1348
|
+
}
|
|
1349
|
+
ctx.startFleetRun(command.name, command.vars);
|
|
1350
|
+
},
|
|
1351
|
+
},
|
|
948
1352
|
{
|
|
949
1353
|
name: "decisions",
|
|
950
1354
|
description: "Show settled interview decisions and operator revisions",
|
|
@@ -1250,6 +1654,29 @@ export const BUILTIN_SLASH_COMMANDS: ReadonlyArray<BuiltinSlashCommand> = [
|
|
|
1250
1654
|
ctx.startNewSession();
|
|
1251
1655
|
},
|
|
1252
1656
|
},
|
|
1657
|
+
{
|
|
1658
|
+
name: "handoff",
|
|
1659
|
+
description: "Hand this session's working state to a fresh session for a stated goal",
|
|
1660
|
+
group: "Sessions",
|
|
1661
|
+
kinds: ["handoff", "handoff-usage"],
|
|
1662
|
+
args: {
|
|
1663
|
+
positionals: [{ name: "goal", required: true, rest: true }],
|
|
1664
|
+
},
|
|
1665
|
+
fromArgs(parsed) {
|
|
1666
|
+
const goal = parsed.rest?.trim() ?? "";
|
|
1667
|
+
if (parsed.error || goal.length === 0) return { kind: "handoff-usage" };
|
|
1668
|
+
return { kind: "handoff", goal };
|
|
1669
|
+
},
|
|
1670
|
+
handle(command, ctx) {
|
|
1671
|
+
if (command.kind === "handoff-usage") {
|
|
1672
|
+
const entry = BUILTIN_SLASH_COMMANDS.find((candidate) => candidate.name === "handoff");
|
|
1673
|
+
if (entry) ctx.notice("info", usageNotice(entry));
|
|
1674
|
+
return;
|
|
1675
|
+
}
|
|
1676
|
+
if (command.kind !== "handoff") return;
|
|
1677
|
+
ctx.startHandoff(command.goal);
|
|
1678
|
+
},
|
|
1679
|
+
},
|
|
1253
1680
|
{
|
|
1254
1681
|
name: "tree",
|
|
1255
1682
|
description: "Open session tree navigator",
|
|
@@ -154,18 +154,31 @@ export function parseArgs(spec: CommandArgsSpec, argsLine: string): ParsedArgs {
|
|
|
154
154
|
const positionalSpecs = currentSpec.positionals ?? [];
|
|
155
155
|
let positionalIndex = 0;
|
|
156
156
|
|
|
157
|
+
// A token ends at whitespace unless a quote is open: `--var task="do it"`
|
|
158
|
+
// and `--var "task=do it"` both carry the spaces through, and the quotes
|
|
159
|
+
// themselves are dropped. An unterminated quote runs to the end of the line.
|
|
157
160
|
const readToken = (): { token: string } | null => {
|
|
158
161
|
const tokenStart = index;
|
|
162
|
+
let token = "";
|
|
163
|
+
let quote: string | null = null;
|
|
159
164
|
while (index < argsLine.length) {
|
|
160
|
-
const char = argsLine[index];
|
|
161
|
-
if (
|
|
165
|
+
const char = argsLine[index] ?? "";
|
|
166
|
+
if (quote !== null) {
|
|
167
|
+
if (char === quote) quote = null;
|
|
168
|
+
else token += char;
|
|
162
169
|
index++;
|
|
163
|
-
|
|
164
|
-
|
|
170
|
+
continue;
|
|
171
|
+
}
|
|
172
|
+
if (char === '"' || char === "'") {
|
|
173
|
+
quote = char;
|
|
174
|
+
index++;
|
|
175
|
+
continue;
|
|
165
176
|
}
|
|
177
|
+
if (/\s/.test(char)) break;
|
|
178
|
+
token += char;
|
|
179
|
+
index++;
|
|
166
180
|
}
|
|
167
|
-
|
|
168
|
-
if (token.length === 0) return null;
|
|
181
|
+
if (index === tokenStart) return null;
|
|
169
182
|
return { token };
|
|
170
183
|
};
|
|
171
184
|
|
|
@@ -119,3 +119,33 @@ export function createClioTheme(options: { truecolor?: boolean; color?: boolean
|
|
|
119
119
|
fgSequence: (token) => (color ? `\u001b[${fgCode(TOKENS[token], truecolor)}m` : ""),
|
|
120
120
|
};
|
|
121
121
|
}
|
|
122
|
+
|
|
123
|
+
/** True when `value` names one of the theme's own tokens, so it can be painted as one. */
|
|
124
|
+
export function isClioToken(value: string): value is ClioToken {
|
|
125
|
+
return Object.hasOwn(TOKENS, value);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const HEX_COLOR = /^#([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/u;
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Paint text in a configured `#rrggbb` color.
|
|
132
|
+
*
|
|
133
|
+
* A council roster member may carry one. A roster color is the operator's own
|
|
134
|
+
* choice rather than a token from the palette, which is why a literal color
|
|
135
|
+
* reaches the screen here and nowhere else. Returns the text unchanged when
|
|
136
|
+
* color is disabled or the value is not a six-digit hex color, so a caller can
|
|
137
|
+
* fall back to a token without inspecting the string twice. A terminal without
|
|
138
|
+
* truecolor gets the nearest color in the 6x6x6 xterm cube.
|
|
139
|
+
*/
|
|
140
|
+
export function paintHex(text: string, hex: string, options: { truecolor?: boolean; color?: boolean } = {}): string {
|
|
141
|
+
const match = HEX_COLOR.exec(hex);
|
|
142
|
+
if (match === null) return text;
|
|
143
|
+
if (!(options.color ?? !colorDisabled())) return text;
|
|
144
|
+
const truecolor = options.truecolor ?? detectTruecolor();
|
|
145
|
+
const red = Number.parseInt(match[1] ?? "0", 16);
|
|
146
|
+
const green = Number.parseInt(match[2] ?? "0", 16);
|
|
147
|
+
const blue = Number.parseInt(match[3] ?? "0", 16);
|
|
148
|
+
if (truecolor) return `\u001b[38;2;${red};${green};${blue}m${text}${SGR_RESET}`;
|
|
149
|
+
const cube = (channel: number): number => Math.round((channel / 255) * 5);
|
|
150
|
+
return `\u001b[38;5;${16 + 36 * cube(red) + 6 * cube(green) + cube(blue)}m${text}${SGR_RESET}`;
|
|
151
|
+
}
|
|
@@ -25,7 +25,7 @@ export interface TurnMiddlewareDeps {
|
|
|
25
25
|
middleware?: MiddlewareContract | undefined;
|
|
26
26
|
session?: SessionContract | undefined;
|
|
27
27
|
middlewareToolChoice: MiddlewareToolChoiceControl;
|
|
28
|
-
emitNotice: (text: string) => void;
|
|
28
|
+
emitNotice: (text: string, level?: "info" | "warning" | "error") => void;
|
|
29
29
|
emitFooterNotice: (level: "info" | "success" | "warning" | "error", text: string, key: string) => void;
|
|
30
30
|
}
|
|
31
31
|
|
|
@@ -55,6 +55,14 @@ export interface TurnMiddleware {
|
|
|
55
55
|
* turn_end reminder takes.
|
|
56
56
|
*/
|
|
57
57
|
injectDeferredReminder(message: string, severity?: MiddlewareReminderSeverity): void;
|
|
58
|
+
/**
|
|
59
|
+
* Deliver an operator-facing finding produced after its turn boundary closed.
|
|
60
|
+
* The watchdog seam: its run settles well after the turn it reviewed, and
|
|
61
|
+
* what it found is for the person, not the model. Unlike a deferred
|
|
62
|
+
* reminder, this never joins the pending-reminder buffer and never appends a
|
|
63
|
+
* `middlewareReminder` entry, so nothing it says can enter the next request.
|
|
64
|
+
*/
|
|
65
|
+
emitDeferredNotice(text: string, level?: "info" | "warning"): void;
|
|
58
66
|
}
|
|
59
67
|
|
|
60
68
|
export function createTurnMiddleware(deps: TurnMiddlewareDeps): TurnMiddleware {
|
|
@@ -280,5 +288,11 @@ export function createTurnMiddleware(deps: TurnMiddlewareDeps): TurnMiddleware {
|
|
|
280
288
|
appendMiddlewareReminderEntry(text, level);
|
|
281
289
|
deps.emitNotice(text);
|
|
282
290
|
},
|
|
291
|
+
|
|
292
|
+
emitDeferredNotice(text, level = "warning"): void {
|
|
293
|
+
const message = text.trim();
|
|
294
|
+
if (message.length === 0) return;
|
|
295
|
+
deps.emitNotice(message, level);
|
|
296
|
+
},
|
|
283
297
|
};
|
|
284
298
|
}
|