@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
|
@@ -10,10 +10,12 @@ import type { DispatchContract } from "../domains/dispatch/contract.js";
|
|
|
10
10
|
import type { ExtensionsContract } from "../domains/extensions/index.js";
|
|
11
11
|
import type { InteropContract } from "../domains/interop/index.js";
|
|
12
12
|
import type { TaskMemoryOperatorStatus } from "../domains/memory/index.js";
|
|
13
|
+
import { openDetachedBatchViews } from "../domains/middleware/index.js";
|
|
13
14
|
import type { ObservabilityContract } from "../domains/observability/index.js";
|
|
14
15
|
import type { ProvidersContract, ThinkingLevel } from "../domains/providers/index.js";
|
|
15
16
|
import type { ResourcesContract } from "../domains/resources/index.js";
|
|
16
17
|
import type { FleetNodeSnapshot } from "../domains/scheduling/cluster.js";
|
|
18
|
+
import type { SchedulingContract } from "../domains/scheduling/contract.js";
|
|
17
19
|
import type { DecisionLedgerEntry } from "../domains/session/entries.js";
|
|
18
20
|
import type { SessionContract, SessionEntry, TaskBoardSnapshot } from "../domains/session/index.js";
|
|
19
21
|
import type { ShareContract } from "../domains/share/index.js";
|
|
@@ -26,8 +28,10 @@ import { APPLICATION_DOUBLE_TAP_MS, type ApplicationController } from "./applica
|
|
|
26
28
|
import type { ChatLoop, ChatLoopEvent } from "./chat-loop.js";
|
|
27
29
|
import { emitCommandNotice } from "./command-fallbacks.js";
|
|
28
30
|
import { appendNotice } from "./command-output.js";
|
|
31
|
+
import { dispatchCouncilThroughRegistry } from "./council-dispatch.js";
|
|
29
32
|
import { createDispatchSteering } from "./dispatch-steering.js";
|
|
30
33
|
import { createEditorSubmitController } from "./editor-submit.js";
|
|
34
|
+
import { createInteractiveDesktopNotifications } from "./footer/notifications.js";
|
|
31
35
|
import { createInteractiveEventProjection } from "./interactive-event-projection.js";
|
|
32
36
|
import { createInteractiveInputRuntime } from "./interactive-input-runtime.js";
|
|
33
37
|
import { createInteractivePresentation } from "./interactive-presentation.js";
|
|
@@ -108,6 +112,8 @@ export interface InteractiveDeps {
|
|
|
108
112
|
providers: ProvidersContract;
|
|
109
113
|
dispatch: DispatchContract;
|
|
110
114
|
agents?: AgentsContract;
|
|
115
|
+
/** Session budget state `/fleet run` shows as the ceiling a plan is admitted under. */
|
|
116
|
+
scheduling?: SchedulingContract;
|
|
111
117
|
observability: ObservabilityContract;
|
|
112
118
|
chat: ChatLoop;
|
|
113
119
|
/** Fired once after the first real TUI render transaction issued all of its terminal writes. */
|
|
@@ -541,6 +547,16 @@ export async function createInteractiveApplication(deps: InteractiveDeps): Promi
|
|
|
541
547
|
} = presentation;
|
|
542
548
|
refreshPresentationFooter = () => footer.refresh();
|
|
543
549
|
const agentProgress = createAgentProgress(terminal);
|
|
550
|
+
// Desktop notifications are a protocol write on the terminal owner, issued
|
|
551
|
+
// outside any render transaction, so the sequence carries frameId null and
|
|
552
|
+
// never lands inside a frame. A non-TTY process never emits one even when
|
|
553
|
+
// `CLIO_CODER_INTERACTIVE=1` forced the interactive surface on.
|
|
554
|
+
const desktopNotifications = createInteractiveDesktopNotifications({
|
|
555
|
+
write: (data) => terminal.write(data),
|
|
556
|
+
enabled: () => deps.getSettings?.().terminal.notify ?? false,
|
|
557
|
+
interactiveTty: () => process.stdout.isTTY === true,
|
|
558
|
+
getOpenBatches: () => openDetachedBatchViews(deps.dispatch),
|
|
559
|
+
});
|
|
544
560
|
const busNoticeSink = {
|
|
545
561
|
appendReplayBlock: (renderBlock: Parameters<typeof chatPanel.appendReplayBlock>[0]) =>
|
|
546
562
|
chatRenderer.mutate(() => chatPanel.appendReplayBlock(renderBlock), "bus-notice"),
|
|
@@ -570,6 +586,7 @@ export async function createInteractiveApplication(deps: InteractiveDeps): Promi
|
|
|
570
586
|
setLastTurnSummary: (summary) => presentation.setLastTurnSummary(summary),
|
|
571
587
|
startTerminalProgress: () => agentProgress.start(),
|
|
572
588
|
stopTerminalProgress: () => agentProgress.stop(),
|
|
589
|
+
onTurnEnded: () => desktopNotifications.turnEnded(),
|
|
573
590
|
refreshLiveWorkspaceGit,
|
|
574
591
|
refreshFooter: () => footer.refresh(),
|
|
575
592
|
requestRender: () => tui.requestRender(),
|
|
@@ -587,6 +604,10 @@ export async function createInteractiveApplication(deps: InteractiveDeps): Promi
|
|
|
587
604
|
// The overlay reads the report this process already produced at boot; it
|
|
588
605
|
// never probes on a keystroke.
|
|
589
606
|
const interop = deps.interop;
|
|
607
|
+
// `/council` reaches execution only through the tool registry, so the
|
|
608
|
+
// approval overlay this application already installs on it is the same one an
|
|
609
|
+
// operator-typed council parks.
|
|
610
|
+
const toolRegistry = deps.toolRegistry;
|
|
590
611
|
const interopSurface = interop ? interopOverlaySurface(interop, (level, text) => notify(level, text)) : null;
|
|
591
612
|
const slashRuntime = createInteractiveSlashRuntime({
|
|
592
613
|
io,
|
|
@@ -626,8 +647,12 @@ export async function createInteractiveApplication(deps: InteractiveDeps): Promi
|
|
|
626
647
|
readStructuredEntries,
|
|
627
648
|
expandSubmit: (text) => expandInteractiveSubmitAsync(text, deps.resources),
|
|
628
649
|
openAskUser: (questions, options) => openAskUserOverlayState(questions, options),
|
|
629
|
-
openSkillsHub: () => openSkillsHubState(),
|
|
650
|
+
openSkillsHub: (tab) => openSkillsHubState(tab),
|
|
630
651
|
openCost: () => openCostOverlayState(),
|
|
652
|
+
openSideQuestion: (question) => openSideQuestionOverlayState(question),
|
|
653
|
+
startHandoff: (goal) => startHandoffState(goal),
|
|
654
|
+
startFleetRun: (name, vars) => startFleetRunState(name, vars),
|
|
655
|
+
...(toolRegistry ? { runCouncilDispatch: (args) => dispatchCouncilThroughRegistry(toolRegistry, args) } : {}),
|
|
631
656
|
openContextView: () => openContextViewOverlayState(),
|
|
632
657
|
openTasks: () => openTasksOverlayState(),
|
|
633
658
|
openDecisions: () => openDecisionsOverlayState(),
|
|
@@ -705,6 +730,7 @@ export async function createInteractiveApplication(deps: InteractiveDeps): Promi
|
|
|
705
730
|
notify,
|
|
706
731
|
terminal,
|
|
707
732
|
dispatchBoard,
|
|
733
|
+
setFleetRunPhase: (runId, phase) => dispatchBoardStore.setFleetPhase(runId, phase),
|
|
708
734
|
chatPanel,
|
|
709
735
|
resetTranscript,
|
|
710
736
|
io,
|
|
@@ -714,11 +740,26 @@ export async function createInteractiveApplication(deps: InteractiveDeps): Promi
|
|
|
714
740
|
keybindings,
|
|
715
741
|
editor,
|
|
716
742
|
getSlashContext: () => slashRuntime.context,
|
|
743
|
+
// One terminal owner, so the external editor a handoff review opens gets
|
|
744
|
+
// the screen the same way the composer's own `$EDITOR` opener does.
|
|
745
|
+
suspendTerminal: (run) => {
|
|
746
|
+
try {
|
|
747
|
+
tui.stop();
|
|
748
|
+
return run();
|
|
749
|
+
} finally {
|
|
750
|
+
tui.start();
|
|
751
|
+
tui.requestRender(true);
|
|
752
|
+
}
|
|
753
|
+
},
|
|
754
|
+
onOperatorParked: () => desktopNotifications.approvalParked(),
|
|
717
755
|
});
|
|
718
756
|
const {
|
|
719
757
|
closeOverlay,
|
|
720
758
|
openAskUserOverlayState,
|
|
721
759
|
openCostOverlayState,
|
|
760
|
+
openSideQuestionOverlayState,
|
|
761
|
+
startHandoffState,
|
|
762
|
+
startFleetRunState,
|
|
722
763
|
openContextViewOverlayState,
|
|
723
764
|
openContextResetOverlayState,
|
|
724
765
|
openTasksOverlayState,
|
|
@@ -810,6 +851,7 @@ export async function createInteractiveApplication(deps: InteractiveDeps): Promi
|
|
|
810
851
|
renderContextIsland: interactiveTickers.renderContextIsland,
|
|
811
852
|
requestRender: () => tui.requestRender(),
|
|
812
853
|
notify,
|
|
854
|
+
onDispatchSettled: () => desktopNotifications.dispatchSettled(),
|
|
813
855
|
// The reducer mutates one state object per assignment, so the panel is
|
|
814
856
|
// handed that object rather than a copy: a streamed delta reaches the
|
|
815
857
|
// screen by invalidating a cached render, not by rebuilding the entry.
|
|
@@ -77,6 +77,11 @@ export interface InteractiveEventProjectionDeps {
|
|
|
77
77
|
setLastTurnSummary: (summary: TurnSummary) => void;
|
|
78
78
|
startTerminalProgress: () => void;
|
|
79
79
|
stopTerminalProgress: () => void;
|
|
80
|
+
/**
|
|
81
|
+
* One model turn reached its end. Wired to the desktop notification; absent
|
|
82
|
+
* on hosts that do not own a terminal to notify through.
|
|
83
|
+
*/
|
|
84
|
+
onTurnEnded?: () => void;
|
|
80
85
|
refreshLiveWorkspaceGit: (force: boolean) => void;
|
|
81
86
|
refreshFooter: () => void;
|
|
82
87
|
requestRender: () => void;
|
|
@@ -214,7 +219,10 @@ export function createInteractiveEventProjection(deps: InteractiveEventProjectio
|
|
|
214
219
|
deps.chat.onEvent((event) => {
|
|
215
220
|
const showProgress = deps.getSettings?.().terminal.showTerminalProgress ?? false;
|
|
216
221
|
if (event.type === "agent_start" && showProgress) deps.startTerminalProgress();
|
|
217
|
-
else if (event.type === "agent_end")
|
|
222
|
+
else if (event.type === "agent_end") {
|
|
223
|
+
deps.stopTerminalProgress();
|
|
224
|
+
deps.onTurnEnded?.();
|
|
225
|
+
}
|
|
218
226
|
}),
|
|
219
227
|
deps.bus.on(BusChannels.RunAborted, () => {
|
|
220
228
|
deps.stopTerminalProgress();
|
|
@@ -16,9 +16,12 @@ import {
|
|
|
16
16
|
type ThinkingLevel,
|
|
17
17
|
thinkingLevelChoiceLabel,
|
|
18
18
|
} from "../domains/providers/index.js";
|
|
19
|
-
import type { ResourcesContract } from "../domains/resources/index.js";
|
|
19
|
+
import type { LibraryEntryKind, ResourcesContract } from "../domains/resources/index.js";
|
|
20
20
|
import { installSkill } from "../domains/resources/skills/marketplace.js";
|
|
21
|
+
import { foldDecisionBoard } from "../domains/session/decision-board.js";
|
|
21
22
|
import type { SessionContract, SessionEntry } from "../domains/session/index.js";
|
|
23
|
+
import { foldTaskBoard } from "../domains/session/task-board.js";
|
|
24
|
+
import { filterEntriesToActivePath } from "../domains/session/tree/active-path.js";
|
|
22
25
|
import type { ShareContract } from "../domains/share/index.js";
|
|
23
26
|
import type { UserTasksStore } from "../domains/user-tasks/store.js";
|
|
24
27
|
import { stripTerminalSequences } from "../engine/tui.js";
|
|
@@ -32,9 +35,11 @@ import { appendNotice, appendOperatorCommand } from "./command-output.js";
|
|
|
32
35
|
import { runOperatorRecall } from "./context-recall-command.js";
|
|
33
36
|
import { renderSessionHtml } from "./export-html/index.js";
|
|
34
37
|
import { dateLocal } from "./format-time.js";
|
|
38
|
+
import type { OracleDigestSources } from "./oracle.js";
|
|
35
39
|
import type { SettingsCenterRowId, SettingsSectionId } from "./overlays/settings.js";
|
|
36
40
|
import {
|
|
37
41
|
type ContextClearCommandOptions,
|
|
42
|
+
type CouncilDispatchOutcome,
|
|
38
43
|
dispatchSlashCommand,
|
|
39
44
|
type InitCommandOptions,
|
|
40
45
|
parseSlashCommand,
|
|
@@ -108,8 +113,21 @@ export interface InteractiveSlashRuntimeDeps {
|
|
|
108
113
|
session?: Pick<SessionContract, "tree" | "current" | "appendEntry">;
|
|
109
114
|
expandSubmit: (text: string) => Promise<InteractiveSlashSubmitExpansion>;
|
|
110
115
|
openAskUser: AskUserHandler;
|
|
111
|
-
openSkillsHub: () => void;
|
|
116
|
+
openSkillsHub: (tab?: LibraryEntryKind) => void;
|
|
112
117
|
openCost: () => void;
|
|
118
|
+
/** Run one `/btw` side question in its own overlay. */
|
|
119
|
+
openSideQuestion: (question: string) => void;
|
|
120
|
+
/** Run one `/handoff <goal>`: extract, review, and seed a successor session. */
|
|
121
|
+
startHandoff: (goal: string) => void;
|
|
122
|
+
/** Run one `/fleet run <name>`: approval preview first, dispatch only on accept. */
|
|
123
|
+
startFleetRun?: (name: string, vars: Readonly<Record<string, string>>) => void;
|
|
124
|
+
/**
|
|
125
|
+
* Run one `/council <task>`: the prepared dispatch-tool arguments go through
|
|
126
|
+
* the ordinary tool-admission path, so supervised autonomy parks the call and
|
|
127
|
+
* the approval overlay opens before any member runs. Absent on a host with no
|
|
128
|
+
* tool registry, in which case `/council` says so.
|
|
129
|
+
*/
|
|
130
|
+
runCouncilDispatch?: (args: Readonly<Record<string, unknown>>) => Promise<CouncilDispatchOutcome>;
|
|
113
131
|
openContextView: () => void;
|
|
114
132
|
openTasks: () => void;
|
|
115
133
|
openDecisions: () => void;
|
|
@@ -186,6 +204,29 @@ export function createInteractiveSlashRuntime(deps: InteractiveSlashRuntimeDeps)
|
|
|
186
204
|
const resources = deps.resources;
|
|
187
205
|
const userTasks = deps.userTasks;
|
|
188
206
|
|
|
207
|
+
/**
|
|
208
|
+
* The record `/oracle` briefs its advisor on. Entries are filtered to the
|
|
209
|
+
* active branch first, the same way the task board, compaction, and the
|
|
210
|
+
* finish contract read them (issue #94): after a `/tree` switch the raw file
|
|
211
|
+
* still holds the abandoned turns, and an unscoped fold would brief the
|
|
212
|
+
* advisor on decisions the operator walked away from.
|
|
213
|
+
*/
|
|
214
|
+
const oracleBriefingSources = (): Omit<OracleDigestSources, "question"> => {
|
|
215
|
+
const sessionId = deps.chat.getSessionId();
|
|
216
|
+
if (sessionId === null) return { decisions: [], tasks: [], compactionSummary: null };
|
|
217
|
+
const leafTurnId = deps.session?.tree(sessionId).leafId ?? undefined;
|
|
218
|
+
const entries = filterEntriesToActivePath(deps.readStructuredEntries(sessionId), leafTurnId);
|
|
219
|
+
let compactionSummary: string | null = null;
|
|
220
|
+
for (const entry of entries) {
|
|
221
|
+
if (entry.kind === "compactionSummary") compactionSummary = entry.summary;
|
|
222
|
+
}
|
|
223
|
+
return {
|
|
224
|
+
decisions: foldDecisionBoard(entries),
|
|
225
|
+
tasks: foldTaskBoard(entries)?.tasks ?? [],
|
|
226
|
+
compactionSummary,
|
|
227
|
+
};
|
|
228
|
+
};
|
|
229
|
+
|
|
189
230
|
const appendCommandNotice: SlashCommandContext["notice"] = (level, text) => {
|
|
190
231
|
appendNotice(level, text, {
|
|
191
232
|
appendReplayBlock: (renderBlock) => deps.chatPanel.appendReplayBlock(renderBlock),
|
|
@@ -342,6 +383,15 @@ export function createInteractiveSlashRuntime(deps: InteractiveSlashRuntimeDeps)
|
|
|
342
383
|
: deps.share.importArchive(resolve(archivePath), importOptions);
|
|
343
384
|
},
|
|
344
385
|
openCost: deps.openCost,
|
|
386
|
+
openSideQuestion: deps.openSideQuestion,
|
|
387
|
+
// `/oracle` is refused, never queued, while a turn streams: the digest it
|
|
388
|
+
// packs describes the record as it stands.
|
|
389
|
+
isTurnInFlight: () => deps.chat.isStreaming(),
|
|
390
|
+
oracleBriefing: oracleBriefingSources,
|
|
391
|
+
startHandoff: deps.startHandoff,
|
|
392
|
+
...(deps.startFleetRun ? { startFleetRun: deps.startFleetRun } : {}),
|
|
393
|
+
...(deps.getSettings ? { getWorkerRosters: () => deps.getSettings?.().workers.rosters ?? {} } : {}),
|
|
394
|
+
...(deps.runCouncilDispatch ? { runCouncilDispatch: deps.runCouncilDispatch } : {}),
|
|
345
395
|
openContextView: deps.openContextView,
|
|
346
396
|
openTasks: deps.openTasks,
|
|
347
397
|
openDecisions: deps.openDecisions,
|
|
@@ -37,6 +37,12 @@ export interface InteractiveSubscriptionsDeps {
|
|
|
37
37
|
recordWorkerRun?: (fields: WorkerRunEntryFields) => void;
|
|
38
38
|
/** Sealed-receipt reader, injected by tests. Defaults to `<state>/receipts/<runId>.json`. */
|
|
39
39
|
readWorkerReceipt?: WorkerReceiptReader;
|
|
40
|
+
/**
|
|
41
|
+
* A dispatch run reached a terminal state. The desktop notification uses it
|
|
42
|
+
* to look for a detached batch that settled with this run; nothing else
|
|
43
|
+
* derives state from it.
|
|
44
|
+
*/
|
|
45
|
+
onDispatchSettled?: () => void;
|
|
40
46
|
}
|
|
41
47
|
|
|
42
48
|
export interface InteractiveSubscriptions {
|
|
@@ -89,8 +95,14 @@ export function createInteractiveSubscriptions(deps: InteractiveSubscriptionsDep
|
|
|
89
95
|
}),
|
|
90
96
|
),
|
|
91
97
|
deps.bus.on(BusChannels.RunAborted, folded(workers.aborted)),
|
|
92
|
-
deps.bus.on(BusChannels.DispatchCompleted,
|
|
93
|
-
|
|
98
|
+
deps.bus.on(BusChannels.DispatchCompleted, (payload) => {
|
|
99
|
+
folded(workers.completed)(payload);
|
|
100
|
+
deps.onDispatchSettled?.();
|
|
101
|
+
}),
|
|
102
|
+
deps.bus.on(BusChannels.DispatchFailed, (payload) => {
|
|
103
|
+
folded(workers.failed)(payload);
|
|
104
|
+
deps.onDispatchSettled?.();
|
|
105
|
+
}),
|
|
94
106
|
deps.bus.on(BusChannels.ContextActivity, () => {
|
|
95
107
|
deps.refreshFooter();
|
|
96
108
|
deps.renderContextIsland();
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `/oracle <question>`: the briefing an advisor is given, and the note its
|
|
3
|
+
* answer becomes.
|
|
4
|
+
*
|
|
5
|
+
* Oracle never receives a forked transcript. A transcript is the session's raw
|
|
6
|
+
* history, and handing it to an advisor makes the advisor agree with whatever
|
|
7
|
+
* the session already believes, which is the opposite of what an operator asks
|
|
8
|
+
* an oracle for. It is briefed on the record instead: the decisions the
|
|
9
|
+
* operator settled, the work the board still owes, and the last compaction
|
|
10
|
+
* summary when one exists.
|
|
11
|
+
*
|
|
12
|
+
* Pure: no I/O, no bus, no session, no dispatch. The caller owns the run.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import type { OracleResult } from "../domains/agents/index.js";
|
|
16
|
+
import type { DecisionLedgerEntry } from "../domains/session/entries.js";
|
|
17
|
+
import type { TaskBoardTask } from "../domains/session/task-board.js";
|
|
18
|
+
|
|
19
|
+
/** The recipe `/oracle` dispatches. Shadow, so `/run` can never reach it. */
|
|
20
|
+
export const ORACLE_AGENT_ID = "oracle";
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Total bound on the packed briefing, in UTF-8 bytes. Well under the 64 KiB
|
|
24
|
+
* an internal-origin dispatch briefing may carry, because the point of the
|
|
25
|
+
* digest is that a small local advisor can hold all of it at once.
|
|
26
|
+
*/
|
|
27
|
+
export const ORACLE_DIGEST_MAX_BYTES = 12 * 1024;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Per-section byte caps. They sum to 11776, which leaves 512 bytes of the
|
|
31
|
+
* total for the section headers and blank lines the packer writes between
|
|
32
|
+
* them. Each section is clamped on its own before the whole digest is clamped
|
|
33
|
+
* again, so one runaway section can never starve the others: a 40 KiB
|
|
34
|
+
* compaction summary loses its own tail rather than the question's.
|
|
35
|
+
*/
|
|
36
|
+
export const ORACLE_DECISIONS_MAX_BYTES = 5120;
|
|
37
|
+
export const ORACLE_TASKS_MAX_BYTES = 3072;
|
|
38
|
+
export const ORACLE_COMPACTION_MAX_BYTES = 2048;
|
|
39
|
+
export const ORACLE_QUESTION_MAX_BYTES = 1536;
|
|
40
|
+
|
|
41
|
+
/** Row caps, applied before the byte caps. Newest decisions and lowest task ids first. */
|
|
42
|
+
export const ORACLE_DECISIONS_MAX_ROWS = 24;
|
|
43
|
+
export const ORACLE_TASKS_MAX_ROWS = 24;
|
|
44
|
+
|
|
45
|
+
/** Appended wherever a cap cut content, so the advisor knows it is reading a tail-less record. */
|
|
46
|
+
export const ORACLE_TRUNCATION_MARKER = "\n[truncated]";
|
|
47
|
+
|
|
48
|
+
export const ORACLE_NO_DECISIONS = "(no settled decisions on this branch)";
|
|
49
|
+
export const ORACLE_NO_TASKS = "(no open tasks on the board)";
|
|
50
|
+
|
|
51
|
+
export interface OracleDigestSources {
|
|
52
|
+
/** Settled interviews, newest first, exactly as the decision board folds them. */
|
|
53
|
+
decisions: ReadonlyArray<DecisionLedgerEntry>;
|
|
54
|
+
/** The live task board's tasks; the packer keeps the open ones. */
|
|
55
|
+
tasks: ReadonlyArray<TaskBoardTask>;
|
|
56
|
+
/** The most recent compaction summary on the active branch, or null when the session never compacted. */
|
|
57
|
+
compactionSummary: string | null;
|
|
58
|
+
question: string;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export interface OracleDigest {
|
|
62
|
+
text: string;
|
|
63
|
+
/** True when any cap cut content. The digest says so in its own text as well. */
|
|
64
|
+
truncated: boolean;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function byteLength(text: string): number {
|
|
68
|
+
return Buffer.byteLength(text, "utf8");
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Clamp to `maxBytes` including the marker, never past a UTF-8 code point. A
|
|
73
|
+
* marker that pushed the result over its own cap would make the section caps
|
|
74
|
+
* unable to add up to the total, which is the one property the packer relies
|
|
75
|
+
* on.
|
|
76
|
+
*/
|
|
77
|
+
function clamp(text: string, maxBytes: number): { text: string; truncated: boolean } {
|
|
78
|
+
if (byteLength(text) <= maxBytes) return { text, truncated: false };
|
|
79
|
+
const room = Math.max(0, maxBytes - byteLength(ORACLE_TRUNCATION_MARKER));
|
|
80
|
+
const buffer = Buffer.from(text, "utf8");
|
|
81
|
+
let cut = Math.min(room, buffer.byteLength);
|
|
82
|
+
while (cut > 0) {
|
|
83
|
+
const next = buffer[cut];
|
|
84
|
+
if (next === undefined || (next & 0xc0) !== 0x80) break;
|
|
85
|
+
cut -= 1;
|
|
86
|
+
}
|
|
87
|
+
return { text: `${buffer.subarray(0, cut).toString("utf8")}${ORACLE_TRUNCATION_MARKER}`, truncated: true };
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** One decision line. Superseded decisions carry their correction so a reversal is visible. */
|
|
91
|
+
function decisionLine(decision: DecisionLedgerEntry["decisions"][number]): string {
|
|
92
|
+
const label = decision.label ? ` (${decision.label})` : "";
|
|
93
|
+
if (decision.status === "superseded") {
|
|
94
|
+
const correction = decision.correction ? `; corrected to ${decision.correction}` : "";
|
|
95
|
+
return `- ${decision.key}${label}: ${decision.value} [superseded${correction}]`;
|
|
96
|
+
}
|
|
97
|
+
return `- ${decision.key}${label}: ${decision.value}`;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function taskLine(task: TaskBoardTask): string {
|
|
101
|
+
return `- ${task.id} [${task.status}] ${task.title}`;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function isOpenTask(task: TaskBoardTask): boolean {
|
|
105
|
+
return task.status === "pending" || task.status === "active";
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Pack the briefing. Sections are fixed and always present, so an advisor
|
|
110
|
+
* reading an empty one learns that the board is empty rather than that the
|
|
111
|
+
* packer omitted it.
|
|
112
|
+
*/
|
|
113
|
+
export function packOracleDigest(sources: OracleDigestSources): OracleDigest {
|
|
114
|
+
let truncated = false;
|
|
115
|
+
|
|
116
|
+
const decisionRows = sources.decisions
|
|
117
|
+
.flatMap((entry) => entry.decisions.map(decisionLine))
|
|
118
|
+
.slice(0, ORACLE_DECISIONS_MAX_ROWS);
|
|
119
|
+
if (decisionRows.length < sources.decisions.reduce((total, entry) => total + entry.decisions.length, 0)) {
|
|
120
|
+
truncated = true;
|
|
121
|
+
}
|
|
122
|
+
const decisions = clamp(decisionRows.join("\n") || ORACLE_NO_DECISIONS, ORACLE_DECISIONS_MAX_BYTES);
|
|
123
|
+
truncated = truncated || decisions.truncated;
|
|
124
|
+
|
|
125
|
+
const openTasks = sources.tasks.filter(isOpenTask);
|
|
126
|
+
const taskRows = openTasks.map(taskLine).slice(0, ORACLE_TASKS_MAX_ROWS);
|
|
127
|
+
if (taskRows.length < openTasks.length) truncated = true;
|
|
128
|
+
const tasks = clamp(taskRows.join("\n") || ORACLE_NO_TASKS, ORACLE_TASKS_MAX_BYTES);
|
|
129
|
+
truncated = truncated || tasks.truncated;
|
|
130
|
+
|
|
131
|
+
const summaryText = sources.compactionSummary?.trim() ?? "";
|
|
132
|
+
const summary = clamp(summaryText, ORACLE_COMPACTION_MAX_BYTES);
|
|
133
|
+
truncated = truncated || summary.truncated;
|
|
134
|
+
|
|
135
|
+
const question = clamp(sources.question.trim(), ORACLE_QUESTION_MAX_BYTES);
|
|
136
|
+
truncated = truncated || question.truncated;
|
|
137
|
+
|
|
138
|
+
const sections = [
|
|
139
|
+
"## Settled decisions",
|
|
140
|
+
decisions.text,
|
|
141
|
+
"",
|
|
142
|
+
"## Open tasks",
|
|
143
|
+
tasks.text,
|
|
144
|
+
"",
|
|
145
|
+
"## Last compaction summary",
|
|
146
|
+
summary.text.length > 0 ? summary.text : "(this session has not compacted)",
|
|
147
|
+
"",
|
|
148
|
+
"## Question",
|
|
149
|
+
question.text,
|
|
150
|
+
];
|
|
151
|
+
const whole = clamp(sections.join("\n"), ORACLE_DIGEST_MAX_BYTES);
|
|
152
|
+
return { text: whole.text, truncated: truncated || whole.truncated };
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/** The bounded task an oracle run is given. The briefing carries the record; this carries the job. */
|
|
156
|
+
export const ORACLE_TASK =
|
|
157
|
+
"Answer the question in the briefing's Question section. Check it against the settled decisions first, " +
|
|
158
|
+
"then return the strongest challenge you can support and the evidence that would change your mind.";
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* The advisor's structured answer as the prose the operator note carries. The
|
|
162
|
+
* contract keeps the four fields typed so a run cannot half-answer; the note
|
|
163
|
+
* renders them because the main agent reads prose, not a payload.
|
|
164
|
+
*/
|
|
165
|
+
export function formatOracleAnswer(result: OracleResult): string {
|
|
166
|
+
const cited =
|
|
167
|
+
result.citedDecisions.length === 0
|
|
168
|
+
? "Cited decisions: none bear on this question."
|
|
169
|
+
: `Cited decisions: ${result.citedDecisions.join(", ")}`;
|
|
170
|
+
return [
|
|
171
|
+
`Verdict: ${result.verdict}`,
|
|
172
|
+
"",
|
|
173
|
+
`Strongest challenge: ${result.challenge}`,
|
|
174
|
+
"",
|
|
175
|
+
`What would change its mind: ${result.changesMyMind}`,
|
|
176
|
+
"",
|
|
177
|
+
cited,
|
|
178
|
+
].join("\n");
|
|
179
|
+
}
|
|
@@ -14,6 +14,11 @@ export interface OverlayAskUserLifecycleDeps {
|
|
|
14
14
|
requestRender(): void;
|
|
15
15
|
registerHandler?(handler: AskUserHandler): () => void;
|
|
16
16
|
openAskUserOverlay?: typeof openAskUserOverlay;
|
|
17
|
+
/**
|
|
18
|
+
* An ask_user request parked waiting for the operator. Fired when the
|
|
19
|
+
* overlay session opens, not per question, so one interview notifies once.
|
|
20
|
+
*/
|
|
21
|
+
onOperatorParked?(): void;
|
|
17
22
|
}
|
|
18
23
|
|
|
19
24
|
export interface OverlayAskUserLifecycle {
|
|
@@ -61,6 +66,7 @@ export function createOverlayAskUserLifecycle(deps: OverlayAskUserLifecycleDeps)
|
|
|
61
66
|
deps.setOverlayState("ask-user");
|
|
62
67
|
session = openSession(deps.tui, { onCancel: () => pendingCancel?.() });
|
|
63
68
|
deps.setOverlayHandle(session);
|
|
69
|
+
deps.onOperatorParked?.();
|
|
64
70
|
deps.requestRender();
|
|
65
71
|
return session;
|
|
66
72
|
};
|