@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.
Files changed (258) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/README.md +16 -5
  3. package/dist/{acp-2BEHC4DL.js → acp-SK4MD6MM.js} +10 -10
  4. package/dist/{agents-LNNFTM53.js → agents-2FN2K6ME.js} +30 -25
  5. package/dist/assets/codewiki.json +1 -1
  6. package/dist/{auth-KXXFI2VS.js → auth-QIYZWM5I.js} +13 -13
  7. package/dist/{chunk-KHSFENX2.js → chunk-3HAPLH5M.js} +10 -10
  8. package/dist/{chunk-24I7BN55.js → chunk-465YSENW.js} +2 -2
  9. package/dist/{chunk-4OC57DA6.js → chunk-4DGYLA73.js} +53 -2
  10. package/dist/{chunk-CYQKWTG3.js → chunk-4DWFMQDR.js} +4 -4
  11. package/dist/{chunk-E2ER4LJF.js → chunk-5C3AQNDW.js} +25 -1
  12. package/dist/{chunk-22NAGB7X.js → chunk-5C77SEEY.js} +5 -94
  13. package/dist/{chunk-43AOLP7E.js → chunk-5FR74PWO.js} +2 -1
  14. package/dist/{chunk-K7T3E2SR.js → chunk-5UJ6ECTS.js} +10 -9
  15. package/dist/{chunk-6US73PDB.js → chunk-6M7VS3J3.js} +5 -5
  16. package/dist/{chunk-CJUB2JJ2.js → chunk-6TUKSZVF.js} +5 -5
  17. package/dist/{chunk-5JGRAMKL.js → chunk-AB4XIIVB.js} +8 -6
  18. package/dist/{chunk-R46L2BIR.js → chunk-BMWK7ZIZ.js} +14 -20
  19. package/dist/{chunk-4BPJXDWC.js → chunk-C4JBQ5SR.js} +30 -14
  20. package/dist/{chunk-XXQNGV4M.js → chunk-CEYBNUGC.js} +243 -63
  21. package/dist/{chunk-VEZEGCGW.js → chunk-D4MDIG46.js} +20 -18
  22. package/dist/chunk-DJNLUABN.js +843 -0
  23. package/dist/{chunk-RY3LY4J5.js → chunk-DMD2AGVS.js} +5 -4
  24. package/dist/{chunk-KOHPCX4K.js → chunk-DOOEX22V.js} +2 -2
  25. package/dist/chunk-DQA7QLMD.js +123 -0
  26. package/dist/chunk-DR52UMZW.js +21 -0
  27. package/dist/{chunk-XF5N4U5A.js → chunk-EBEFWSGL.js} +6 -5
  28. package/dist/{chunk-EYPA3EGJ.js → chunk-EELBMBT6.js} +120 -13
  29. package/dist/{chunk-CKXWIANG.js → chunk-EOOQZZDE.js} +16 -14
  30. package/dist/{chunk-WR67VIZY.js → chunk-FOT2FX5J.js} +63 -5
  31. package/dist/{chunk-FYYLNIL5.js → chunk-GH5622CP.js} +2 -2
  32. package/dist/chunk-GWS3VEIW.js +195 -0
  33. package/dist/{chunk-LYF7OHWH.js → chunk-J7PIKKWC.js} +8 -463
  34. package/dist/{chunk-NILBFAPG.js → chunk-JNXPYBB4.js} +2 -2
  35. package/dist/{chunk-4VP4KH3K.js → chunk-JRIO5UD2.js} +4 -4
  36. package/dist/{chunk-6XXKFVSN.js → chunk-JTSEDYVQ.js} +7 -7
  37. package/dist/{chunk-QKMUKYO7.js → chunk-KCMKRQX4.js} +236 -84
  38. package/dist/chunk-KZ2H5X4G.js +1026 -0
  39. package/dist/{chunk-QNQHSOLF.js → chunk-LADCF22A.js} +12 -12
  40. package/dist/chunk-M4AKACEO.js +382 -0
  41. package/dist/{chunk-XYDYPRZI.js → chunk-MXI6J5JF.js} +7 -7
  42. package/dist/{chunk-G7MUEIGA.js → chunk-OB5HIGJY.js} +1 -1
  43. package/dist/{chunk-EKY57CSP.js → chunk-OBMAI2DP.js} +61 -767
  44. package/dist/chunk-PD3MESLB.js +242 -0
  45. package/dist/{chunk-ZRGEBJ4T.js → chunk-QCTRSGHQ.js} +21 -21
  46. package/dist/chunk-RVG5JXAL.js +41 -0
  47. package/dist/{chunk-RD5U66HV.js → chunk-SROCI7ZU.js} +7 -7
  48. package/dist/{chunk-MFFY33HR.js → chunk-THKY7CD7.js} +466 -205
  49. package/dist/{chunk-PCZJO5TI.js → chunk-UFQ3F4FW.js} +13 -178
  50. package/dist/{chunk-AD2SYQYC.js → chunk-UHXRNZ2J.js} +121 -3
  51. package/dist/chunk-UND3GU2L.js +103 -0
  52. package/dist/{chunk-QM3F2GKX.js → chunk-UUANF5CR.js} +2247 -2096
  53. package/dist/chunk-UVDSQ6LW.js +472 -0
  54. package/dist/{chunk-DJVECN66.js → chunk-VQNODYQ4.js} +14 -14
  55. package/dist/{chunk-3BPUFZDL.js → chunk-VREKEFLL.js} +3 -3
  56. package/dist/{chunk-PBTHKCPN.js → chunk-WJHBC77E.js} +6 -6
  57. package/dist/{chunk-XE2VEJHX.js → chunk-X2KV5FXT.js} +2 -2
  58. package/dist/{chunk-ZXF4XRKW.js → chunk-XEGB6BCN.js} +157 -7
  59. package/dist/{chunk-E25LMLRW.js → chunk-YD734TPH.js} +2 -2
  60. package/dist/{verifiers-NCBTHHN2.js → chunk-YTYFXUI3.js} +65 -322
  61. package/dist/{chunk-OH3TOQTB.js → chunk-ZGH7FGS5.js} +13 -7
  62. package/dist/cli/index.js +32 -30
  63. package/dist/{clio-M2KGYUFZ.js → clio-WBVQEBKO.js} +7 -7
  64. package/dist/{code-nav-GQNL7XA6.js → code-nav-FGGFIE7L.js} +3 -3
  65. package/dist/{components-5TTYYX6G.js → components-F7OEATSO.js} +4 -4
  66. package/dist/{config-XUUYQIWO.js → config-TRBL3RCF.js} +34 -29
  67. package/dist/{configure-IHJ7YOMV.js → configure-OLCVPHNM.js} +15 -15
  68. package/dist/{context-74JLXAWD.js → context-MJIJ6GOX.js} +11 -11
  69. package/dist/{context-ZQ7SIFJV.js → context-WFPKQSM6.js} +19 -3
  70. package/dist/{context-75MIWW3U.js → context-XEWE3MOJ.js} +31 -26
  71. package/dist/{context-clear-GYKWNUML.js → context-clear-KNOS2JPB.js} +31 -26
  72. package/dist/{context-working-set-UX5KEP4J.js → context-working-set-EUXAZI6N.js} +8 -8
  73. package/dist/{dispatch-runner-GIJBHNFL.js → dispatch-runner-B7MTOVKL.js} +313 -53
  74. package/dist/{docs-6FZSCG5B.js → docs-FLJTIDSE.js} +4 -4
  75. package/dist/{doctor-SVJ5BZCW.js → doctor-RN4YKO2X.js} +14 -14
  76. package/dist/{eval-CG6LLBLD.js → eval-RUBJVSNQ.js} +8 -7
  77. package/dist/{evidence-ZYFIEN42.js → evidence-JZNBUOQZ.js} +30 -25
  78. package/dist/{evolve-QGEXEMDW.js → evolve-FJVC4KKI.js} +30 -25
  79. package/dist/{extensions-ADGNCJJD.js → extensions-IQL36S7K.js} +4 -4
  80. package/dist/{fleet-S5R4ZOQY.js → fleet-BDKYJFCP.js} +214 -360
  81. package/dist/fleet-commands-ZFIWZSB3.js +70 -0
  82. package/dist/fleet-graph-Y6HPXIVF.js +125 -0
  83. package/dist/fleet-new-RDVJLHHH.js +48 -0
  84. package/dist/fleet-validate-BIYREGIK.js +79 -0
  85. package/dist/{init-5DRU55YR.js → init-LQUB5COQ.js} +44 -37
  86. package/dist/library-NJAHIGG4.js +217 -0
  87. package/dist/{memory-7YKKR6UC.js → memory-OG6HOYKM.js} +31 -26
  88. package/dist/{models-ZPOLRU2C.js → models-5ZG5XY7J.js} +21 -20
  89. package/dist/{monitor-US5F5YGZ.js → monitor-TJ7AMTGB.js} +49 -30
  90. package/dist/{orchestrator-E2AL4T5N.js → orchestrator-WZYB54DM.js} +3827 -581
  91. package/dist/{paths-E7KYAQWE.js → paths-XUC7GS6E.js} +4 -4
  92. package/dist/{reset-KZ652EK6.js → reset-PXQT45IY.js} +7 -7
  93. package/dist/{run-SRNBKDWD.js → run-FQ74YF62.js} +53 -45
  94. package/dist/{share-CGZE33UP.js → share-FW7SVCL3.js} +33 -9
  95. package/dist/{skills-S2X4DLY5.js → skills-7E7IRB3R.js} +24 -8
  96. package/dist/{skills-eval-W2GGIC4R.js → skills-eval-LI75W6OK.js} +34 -27
  97. package/dist/{targets-54SWINWB.js → targets-4CIFKCTW.js} +23 -22
  98. package/dist/{terminal-lease-SAIF2OGY.js → terminal-lease-WUZY7ZV5.js} +4 -4
  99. package/dist/{uninstall-BVLWXKBT.js → uninstall-7FV7IP4E.js} +4 -4
  100. package/dist/{upgrade-JKAR27XC.js → upgrade-K2HVIVMQ.js} +20 -19
  101. package/dist/{usage-MSAWCLX4.js → usage-GTZELZQX.js} +116 -49
  102. package/dist/verifiers-RLAHT27O.js +336 -0
  103. package/dist/{verify-X5HDROLA.js → verify-BX3BRKH5.js} +7 -6
  104. package/dist/{wiki-generate-GUSOQ6ZP.js → wiki-generate-ASIFASCN.js} +45 -37
  105. package/dist/worker/entry.js +38 -35
  106. package/docs/README.md +3 -2
  107. package/docs/acp.md +1 -1
  108. package/docs/alcf-provider.md +1 -1
  109. package/docs/architecture.md +2 -2
  110. package/docs/artifact-versions.md +10 -6
  111. package/docs/built-in-agents.md +26 -2
  112. package/docs/capacity-and-scheduling.md +1 -1
  113. package/docs/commands-and-modes.md +82 -2
  114. package/docs/configuration-and-targets.md +79 -2
  115. package/docs/context-engine.md +1 -1
  116. package/docs/development-pipeline.md +1 -1
  117. package/docs/dispatch-architecture-rationale.md +1 -1
  118. package/docs/documentation-coverage.md +3 -3
  119. package/docs/documentation-guide.md +3 -3
  120. package/docs/eval-runner.md +1 -1
  121. package/docs/evals-internal.md +1 -1
  122. package/docs/evidence-and-memory.md +5 -5
  123. package/docs/evolution.md +1 -1
  124. package/docs/exit-codes-and-output.md +4 -1
  125. package/docs/extensions-and-sharing.md +6 -2
  126. package/docs/fleet-demo-runbook.md +2 -2
  127. package/docs/fleet-dispatch.md +197 -10
  128. package/docs/git-commit-provenance.md +2 -2
  129. package/docs/glossary.md +1 -1
  130. package/docs/installation-and-lifecycle.md +2 -2
  131. package/docs/middleware-and-components.md +2 -1
  132. package/docs/model-catalog.md +1 -1
  133. package/docs/observability.md +55 -8
  134. package/docs/proactive-memory.md +1 -1
  135. package/docs/prompt-envelope-and-tools.md +1 -1
  136. package/docs/provider-adapter-cookbook.md +1 -1
  137. package/docs/release-cut-checklist.md +79 -64
  138. package/docs/resource-library.md +59 -0
  139. package/docs/safety-model.md +2 -2
  140. package/docs/scientific-validation.md +3 -3
  141. package/docs/session-lifecycle.md +37 -1
  142. package/docs/skills-marketplace.md +16 -3
  143. package/docs/tool-usage.md +14 -7
  144. package/docs/trace-store.md +1 -1
  145. package/docs/troubleshooting.md +1 -1
  146. package/docs/tui-design.md +1 -1
  147. package/docs/worker-dispatch-mechanics.md +3 -3
  148. package/package.json +1 -1
  149. package/src/cli/fleet-commands.ts +37 -0
  150. package/src/cli/fleet-graph.ts +102 -0
  151. package/src/cli/fleet-new.ts +36 -0
  152. package/src/cli/fleet-preflight.ts +121 -0
  153. package/src/cli/fleet-validate.ts +30 -0
  154. package/src/cli/fleet.ts +173 -335
  155. package/src/cli/index.ts +3 -1
  156. package/src/cli/library.ts +190 -0
  157. package/src/cli/share.ts +13 -1
  158. package/src/cli/usage.ts +111 -19
  159. package/src/core/bus-events.ts +4 -0
  160. package/src/core/commit-attribution.ts +4 -4
  161. package/src/core/config.ts +130 -0
  162. package/src/core/defaults.ts +81 -0
  163. package/src/domains/agents/builtins/architect.md +1 -0
  164. package/src/domains/agents/builtins/oracle.md +33 -0
  165. package/src/domains/agents/catalog.ts +13 -1
  166. package/src/domains/agents/fleet-contract.ts +278 -16
  167. package/src/domains/agents/index.ts +14 -0
  168. package/src/domains/agents/result-contract.ts +235 -1
  169. package/src/domains/config/classify.ts +4 -0
  170. package/src/domains/dispatch/active-route-planner.ts +14 -0
  171. package/src/domains/dispatch/backoff.ts +2 -1
  172. package/src/domains/dispatch/capability-match.ts +1 -0
  173. package/src/domains/dispatch/checkout-writer-lease.ts +175 -0
  174. package/src/domains/dispatch/contract.ts +34 -0
  175. package/src/domains/dispatch/delegation-plan.ts +167 -0
  176. package/src/domains/dispatch/execution-plan.ts +76 -5
  177. package/src/domains/dispatch/execution-role.ts +3 -1
  178. package/src/domains/dispatch/execution-scheduler.ts +183 -67
  179. package/src/domains/dispatch/extension.ts +258 -9
  180. package/src/domains/dispatch/fleet-gate.ts +14 -0
  181. package/src/domains/dispatch/fleet-plan.ts +63 -3
  182. package/src/domains/dispatch/fleet-run.ts +737 -0
  183. package/src/domains/dispatch/gate-role-prompts.ts +9 -0
  184. package/src/domains/dispatch/host-verification.ts +178 -0
  185. package/src/domains/dispatch/index.ts +38 -0
  186. package/src/domains/dispatch/intent.ts +159 -0
  187. package/src/domains/dispatch/receipt-integrity.ts +8 -4
  188. package/src/domains/dispatch/state.ts +36 -3
  189. package/src/domains/dispatch/types.ts +51 -6
  190. package/src/domains/dispatch/validation.ts +66 -6
  191. package/src/domains/evidence/trust-status.ts +10 -1
  192. package/src/domains/middleware/index.ts +15 -0
  193. package/src/domains/middleware/watchdog.ts +281 -0
  194. package/src/domains/observability/contract.ts +3 -1
  195. package/src/domains/observability/cost.ts +12 -1
  196. package/src/domains/observability/extension.ts +2 -2
  197. package/src/domains/observability/index.ts +10 -0
  198. package/src/domains/observability/out-of-turn-usage.ts +223 -0
  199. package/src/domains/resources/index.ts +20 -0
  200. package/src/domains/resources/library.ts +326 -0
  201. package/src/domains/resources/skills/marketplace.ts +37 -12
  202. package/src/domains/session/handoff.ts +629 -0
  203. package/src/domains/share/archive.ts +67 -2
  204. package/src/entry/orchestrator.ts +37 -0
  205. package/src/interactive/bus-notices.ts +26 -0
  206. package/src/interactive/chat-loop.ts +235 -1
  207. package/src/interactive/chat-renderer.ts +22 -0
  208. package/src/interactive/cost-overlay.ts +31 -3
  209. package/src/interactive/council-dispatch.ts +30 -0
  210. package/src/interactive/council-grid.ts +213 -0
  211. package/src/interactive/council.ts +99 -0
  212. package/src/interactive/dispatch-board.ts +260 -16
  213. package/src/interactive/fleet-run-preview.ts +307 -0
  214. package/src/interactive/footer/notifications.ts +219 -0
  215. package/src/interactive/handoff-round.ts +56 -0
  216. package/src/interactive/interactive-application.ts +43 -1
  217. package/src/interactive/interactive-event-projection.ts +9 -1
  218. package/src/interactive/interactive-slash-runtime.ts +52 -2
  219. package/src/interactive/interactive-subscriptions.ts +14 -2
  220. package/src/interactive/oracle.ts +179 -0
  221. package/src/interactive/overlay-ask-user-lifecycle.ts +6 -0
  222. package/src/interactive/overlay-general-openers.ts +190 -1
  223. package/src/interactive/overlay-key-routing.ts +17 -1
  224. package/src/interactive/overlay-lifecycle.ts +41 -1
  225. package/src/interactive/overlay-permission-lifecycle.ts +10 -0
  226. package/src/interactive/overlay-resource-openers.ts +11 -3
  227. package/src/interactive/overlay-session-lifecycle.ts +234 -2
  228. package/src/interactive/overlays/fleet-run-approval.ts +208 -0
  229. package/src/interactive/overlays/handoff-review.ts +185 -0
  230. package/src/interactive/overlays/library-install-confirm.ts +151 -0
  231. package/src/interactive/overlays/list-overlay.ts +168 -2
  232. package/src/interactive/overlays/settings.ts +101 -4
  233. package/src/interactive/overlays/side-question.ts +139 -0
  234. package/src/interactive/overlays/skills-hub.ts +401 -15
  235. package/src/interactive/side-question.ts +171 -0
  236. package/src/interactive/slash-commands.ts +432 -5
  237. package/src/interactive/slash-spec.ts +19 -6
  238. package/src/interactive/theme/tokens.ts +30 -0
  239. package/src/interactive/turn-middleware.ts +15 -1
  240. package/src/interactive/watchdog-run.ts +75 -0
  241. package/src/interactive/worker-share.ts +56 -1
  242. package/src/interactive/worker-stream.ts +7 -0
  243. package/src/tools/bootstrap.ts +3 -0
  244. package/src/tools/compete-worktrees.ts +13 -79
  245. package/src/tools/dispatch-admission.ts +242 -8
  246. package/src/tools/dispatch-arguments.ts +57 -1
  247. package/src/tools/dispatch-plan.ts +136 -6
  248. package/src/tools/dispatch-runner.ts +319 -13
  249. package/src/tools/dispatch-types.ts +20 -1
  250. package/src/tools/dispatch.ts +72 -2
  251. package/src/tools/monitor.ts +16 -0
  252. package/src/tools/profiles.ts +18 -4
  253. package/src/tools/task-worktree.ts +238 -0
  254. package/src/tools/verify/authoring.ts +61 -1
  255. package/src/tools/verify/scripts.ts +62 -0
  256. package/src/tools/worker-evidence.ts +2 -1
  257. package/src/worker/spec-contract.ts +1 -0
  258. package/dist/chunk-HC4CLZ2Y.js +0 -68
@@ -0,0 +1,75 @@
1
+ /**
2
+ * The watchdog's run: one read-only verifier dispatch, one transcript notice.
3
+ *
4
+ * The middleware registration decides when to fire and what the turn changed;
5
+ * this decides nothing. It dispatches through the ordinary path so admission,
6
+ * the receipt, and the Fleet Runs island treat a watchdog run exactly as they
7
+ * treat any other, reads the failed checks out of the `verifier-report`
8
+ * contract, and hands the operator one line. A passing report produces nothing
9
+ * at all.
10
+ */
11
+
12
+ import { BusChannels } from "../core/bus-events.js";
13
+ import type { SafeEventBus } from "../core/event-bus.js";
14
+ import { parseVerifierResult } from "../domains/agents/index.js";
15
+ import type { DispatchContract, DispatchRequest } from "../domains/dispatch/contract.js";
16
+ import { type AgentRoleFactsResolver, requestExecutionRole } from "../domains/dispatch/execution-role.js";
17
+ import { WATCHDOG_TASK, type WatchdogTrigger, watchdogBriefing } from "../domains/middleware/index.js";
18
+ import { watchdogBlockersNotice } from "./bus-notices.js";
19
+
20
+ /** The builtin recipe the watchdog reviews with. */
21
+ export const WATCHDOG_AGENT_ID = "verifier";
22
+
23
+ export interface WatchdogRunDeps {
24
+ dispatch: DispatchContract;
25
+ bus?: SafeEventBus;
26
+ getAgentRoleFacts?: AgentRoleFactsResolver;
27
+ /** `watchdog.target` when the operator set one; the session's active target otherwise. */
28
+ target?: string | undefined;
29
+ /** One transcript line for the operator. Absent means the finding has nowhere to go and the run is skipped. */
30
+ emitNotice?: (text: string) => void;
31
+ }
32
+
33
+ /**
34
+ * Dispatch one watchdog review and report what it found.
35
+ *
36
+ * Resolves when the run has settled, whatever the outcome. A watchdog that
37
+ * failed is not a turn that failed: the review is advisory, so a broken run
38
+ * stays silent rather than raising an alarm about itself in the middle of the
39
+ * operator's work.
40
+ */
41
+ export async function runWatchdogReview(trigger: WatchdogTrigger, deps: WatchdogRunDeps): Promise<void> {
42
+ if (!deps.emitNotice) return;
43
+ const request: DispatchRequest = {
44
+ agentId: WATCHDOG_AGENT_ID,
45
+ task: WATCHDOG_TASK,
46
+ briefing: watchdogBriefing(trigger),
47
+ executionRole: requestExecutionRole({
48
+ agentId: WATCHDOG_AGENT_ID,
49
+ ...(deps.getAgentRoleFacts ? { resolveFacts: deps.getAgentRoleFacts } : {}),
50
+ }),
51
+ requestOrigin: "internal",
52
+ // Narrowed below whatever the session holds. A reviewer that could write
53
+ // would be a second builder nobody admitted.
54
+ autonomy: "read-only",
55
+ ...(deps.target ? { target: deps.target } : {}),
56
+ };
57
+ const progressBus = deps.dispatch.ownsProgressBus?.(deps.bus) === true ? undefined : deps.bus;
58
+ try {
59
+ const handle = await deps.dispatch.dispatch(request);
60
+ for await (const event of handle.events) {
61
+ const typed = event as { type?: string };
62
+ if (!typed.type || typed.type === "heartbeat") continue;
63
+ progressBus?.emit(BusChannels.DispatchProgress, { runId: handle.runId, agentId: WATCHDOG_AGENT_ID, event });
64
+ }
65
+ const receipt = await handle.finalPromise;
66
+ const report = parseVerifierResult(receipt.output?.text ?? null);
67
+ if (report === null) return;
68
+ const notice = watchdogBlockersNotice(report.checks);
69
+ if (notice === null) return;
70
+ deps.emitNotice(notice.text);
71
+ } catch {
72
+ // An advisory review that could not run says nothing. The receipt, if one
73
+ // was written at all, is in the dispatch board like every other run.
74
+ }
75
+ }
@@ -11,6 +11,7 @@
11
11
  * Pure: no I/O, no bus, no session.
12
12
  */
13
13
 
14
+ import type { CouncilReport } from "../domains/agents/result-contract.js";
14
15
  import { WORKER_OUTPUT_MAX_BYTES } from "../domains/dispatch/event-pump.js";
15
16
  import { truncateUtf8 } from "../tools/truncate-utf8.js";
16
17
  import { type WorkerEntryState, workerAskedByModel } from "./worker-stream.js";
@@ -56,12 +57,66 @@ export const WORKER_SHARE_ORIGIN = "shared by the operator";
56
57
  * produced no text is not worth a turn.
57
58
  */
58
59
  export function formatWorkerShareNote(facts: WorkerShareFacts): string | null {
59
- const text = facts.text.trim();
60
+ return shareNote(facts, facts.text);
61
+ }
62
+
63
+ /** The header every shared note carries, plus one bounded body under it. */
64
+ function shareNote(facts: WorkerShareFacts, body: string): string | null {
65
+ const text = body.trim();
60
66
  if (text.length === 0) return null;
61
67
  const header = `${WORKER_SHARE_NOTE_PREFIX} ${facts.agentId} · run ${facts.runId} · ${outcomeWord(facts.outcome)} · ${WORKER_SHARE_ORIGIN}`;
62
68
  return `${header}\n${truncateUtf8(text, WORKER_SHARE_MAX_BYTES, SHARE_TRUNCATION_MARKER)}`;
63
69
  }
64
70
 
71
+ /** One member's answer under its roster label, or its failure when it produced none. */
72
+ function councilMemberLine(member: CouncilReport["members"][number]): string {
73
+ if (member.failed !== undefined) return `[${member.label}] failed: ${member.failed.reason}`;
74
+ const verdict = member.verdict === undefined ? "" : ` (verdict ${member.verdict})`;
75
+ return `[${member.label}]${verdict} ${member.answer.trim()}`;
76
+ }
77
+
78
+ /** The synthesis line: what the council concluded, and how it concluded it. */
79
+ function councilSynthesisLines(synthesis: CouncilReport["synthesis"]): string[] {
80
+ if (synthesis.kind === "none") return ["[synthesis none] the members answered separately; no synthesis was run."];
81
+ const facts = [
82
+ ...(synthesis.verdict !== undefined ? [`verdict ${synthesis.verdict}`] : []),
83
+ ...(synthesis.tally !== undefined
84
+ ? [
85
+ `tally ${Object.entries(synthesis.tally)
86
+ .map(([key, count]) => `${key}=${count}`)
87
+ .join(" ")}`,
88
+ ]
89
+ : []),
90
+ ...(synthesis.judgeRunId !== undefined ? [`judge run ${synthesis.judgeRunId}`] : []),
91
+ ];
92
+ const head = `[synthesis ${synthesis.kind}]${facts.length > 0 ? ` ${facts.join(" · ")}` : ""}`;
93
+ const text = synthesis.text?.trim() ?? "";
94
+ return text.length > 0 ? [head, text] : [head];
95
+ }
96
+
97
+ /**
98
+ * A whole council as one operator note: every member's labelled answer, in the
99
+ * order the report seals them, then the synthesis. Sharing a synthesis run id
100
+ * means sharing what the council decided, and the members' answers are what it
101
+ * decided from, so they travel together in one bounded block rather than as
102
+ * several notes the main agent would have to reassemble.
103
+ */
104
+ export function formatCouncilShareNote(facts: WorkerShareFacts, report: CouncilReport): string | null {
105
+ const finalRound = report.members.reduce((highest, member) => Math.max(highest, member.round), 1);
106
+ const body = [
107
+ ...report.members.filter((member) => member.round === finalRound).map(councilMemberLine),
108
+ ...councilSynthesisLines(report.synthesis),
109
+ ].join("\n\n");
110
+ return shareNote(facts, body);
111
+ }
112
+
113
+ /** One council member's answer, labelled with the roster label the operator watched it under. */
114
+ export function formatCouncilMemberShareNote(facts: WorkerShareFacts, label: string): string | null {
115
+ const text = facts.text.trim();
116
+ if (text.length === 0) return null;
117
+ return shareNote(facts, `[${label}] ${text}`);
118
+ }
119
+
65
120
  /**
66
121
  * Whether operator text is a shared worker note. The header is the first line,
67
122
  * so the chat loop can tell the honesty rail a worker result entered the turn
@@ -113,6 +113,12 @@ export interface WorkerEntryState {
113
113
  /** Every attempt of this assignment, oldest first. */
114
114
  attempts: WorkerAttempt[];
115
115
  pending: boolean;
116
+ /**
117
+ * Council provenance, present only on a run of a council group. `/share`
118
+ * reads it so a member's answer reaches the main agent under the roster label
119
+ * the operator watched it under, rather than as one unattributed voice.
120
+ */
121
+ council?: { group: string; label: string; color?: string; round: number };
116
122
  receipt?: WorkerReceiptSummary;
117
123
  /** Agent origin: the tool call whose execution spawned this run. */
118
124
  parentToolCallId?: string;
@@ -323,6 +329,7 @@ export function createWorkerStream(options: WorkerStreamOptions = {}): WorkerStr
323
329
  progress: progress.snapshot(),
324
330
  attempts: [{ runId, targetLabel: workerTargetLabel(runtime) }],
325
331
  pending: true,
332
+ ...(payload.council !== undefined ? { council: { ...payload.council } } : {}),
326
333
  ...(payload.parentToolCallId !== undefined ? { parentToolCallId: payload.parentToolCallId } : {}),
327
334
  };
328
335
  byAssignment.set(assignmentId, entry);
@@ -1,3 +1,4 @@
1
+ import type { WorkerRosters } from "../core/defaults.js";
1
2
  import type { SafeEventBus } from "../core/event-bus.js";
2
3
  import type { AgentSpec } from "../domains/agents/spec.js";
3
4
  import type { DispatchContract } from "../domains/dispatch/contract.js";
@@ -22,6 +23,7 @@ export interface ToolBootstrapDeps extends CoreToolBootstrapDeps {
22
23
  getAgentRoleFacts?: AgentRoleFactsResolver;
23
24
  getAutonomy?: () => AutonomyLevel;
24
25
  getCostCeilingUsd?: () => number;
26
+ getWorkerRosters?: () => WorkerRosters;
25
27
  dispatchBackground?: DispatchBackgroundRegistry;
26
28
  }
27
29
 
@@ -44,6 +46,7 @@ export function registerAllTools(registry: ToolRegistry, deps: ToolBootstrapDeps
44
46
  ...(deps.getAgentRoleFacts ? { getAgentRoleFacts: deps.getAgentRoleFacts } : {}),
45
47
  ...(deps.getAutonomy ? { getAutonomy: deps.getAutonomy } : {}),
46
48
  ...(deps.getCostCeilingUsd ? { getCostCeilingUsd: deps.getCostCeilingUsd } : {}),
49
+ ...(deps.getWorkerRosters ? { getWorkerRosters: deps.getWorkerRosters } : {}),
47
50
  ...(deps.dispatchBackground ? { background: deps.dispatchBackground } : {}),
48
51
  };
49
52
  registry.register({
@@ -33,8 +33,15 @@ import {
33
33
  writeFileSync,
34
34
  } from "node:fs";
35
35
  import { hostname } from "node:os";
36
- import { isAbsolute, join, relative, resolve, sep } from "node:path";
36
+ import { join, relative, resolve } from "node:path";
37
37
  import type { RunKind } from "../domains/dispatch/types.js";
38
+ import {
39
+ commitWorktreePath,
40
+ isCanonicalWorktreePathInside,
41
+ mergeWorktreeBranch,
42
+ protectedPathsChangedByWorktreeBranch,
43
+ worktreeBranchDiffStat,
44
+ } from "./task-worktree.js";
38
45
 
39
46
  export interface CandidateWorktree {
40
47
  index: number;
@@ -300,20 +307,7 @@ function isCanonicalDirectoryAt(path: string): boolean {
300
307
  * be mistaken for a child of `/group-a` as it can with textual startsWith.
301
308
  */
302
309
  export function isCanonicalPathInside(parent: string, candidate: string): boolean {
303
- let canonicalParent: string;
304
- let canonicalCandidate: string;
305
- try {
306
- canonicalParent = realpathSync(parent);
307
- } catch {
308
- canonicalParent = resolve(parent);
309
- }
310
- try {
311
- canonicalCandidate = realpathSync(candidate);
312
- } catch {
313
- canonicalCandidate = resolve(candidate);
314
- }
315
- const rel = relative(canonicalParent, canonicalCandidate);
316
- return rel.length > 0 && rel !== ".." && !rel.startsWith(`..${sep}`) && !isAbsolute(rel);
310
+ return isCanonicalWorktreePathInside(parent, candidate);
317
311
  }
318
312
 
319
313
  function manifestFromUnknown(value: unknown): CompeteGroupManifest | null {
@@ -557,29 +551,12 @@ export function createCandidateWorktree(
557
551
  * builder changed nothing (an empty candidate is a legitimate ranking fact).
558
552
  */
559
553
  export function commitCandidateWork(worktree: CandidateWorktree, message: string): boolean {
560
- git(worktree.path, ["add", "-A"]);
561
- const staged = git(worktree.path, ["status", "--porcelain"]);
562
- if (staged.length === 0) return false;
563
- git(worktree.path, [
564
- "-c",
565
- `user.name=${COMPETE_COMMIT_IDENTITY}`,
566
- "-c",
567
- `user.email=${COMPETE_COMMIT_IDENTITY}@local`,
568
- "commit",
569
- "-m",
570
- message,
571
- "--no-verify",
572
- ]);
573
- return true;
554
+ return commitWorktreePath(worktree.path, COMPETE_COMMIT_IDENTITY, message);
574
555
  }
575
556
 
576
557
  /** One-line stat summary of what a candidate branch changed relative to HEAD. */
577
558
  export function candidateDiffStat(root: string, branch: string): string {
578
- try {
579
- return git(root, ["diff", "--shortstat", `HEAD...${branch}`]) || "no changes";
580
- } catch {
581
- return "diff unavailable";
582
- }
559
+ return worktreeBranchDiffStat(root, branch);
583
560
  }
584
561
 
585
562
  /**
@@ -592,31 +569,7 @@ export function protectedPathsChangedByCompeteBranch(
592
569
  branch: string,
593
570
  protectedPaths: ReadonlyArray<string>,
594
571
  ): string[] {
595
- const canonical = canonicalRoot(root);
596
- const protectedInside = protectedPaths
597
- .map((path) => resolve(path))
598
- .filter((path) => {
599
- const rel = relative(canonical, path);
600
- return rel.length > 0 && rel !== ".." && !rel.startsWith(`..${sep}`) && !isAbsolute(rel);
601
- });
602
- if (protectedInside.length === 0) return [];
603
- const changed = git(canonical, ["diff", "--name-only", "-z", "--no-renames", `HEAD...${branch}`])
604
- .split("\0")
605
- .filter((path) => path.length > 0)
606
- .map((path) => resolve(canonical, path));
607
- const blocked = new Set<string>();
608
- for (const candidate of changed) {
609
- for (const protectedPath of protectedInside) {
610
- const rel = relative(protectedPath, candidate);
611
- if (
612
- candidate === protectedPath ||
613
- (rel.length > 0 && rel !== ".." && !rel.startsWith(`..${sep}`) && !isAbsolute(rel))
614
- ) {
615
- blocked.add(protectedPath);
616
- }
617
- }
618
- }
619
- return [...blocked].sort();
572
+ return protectedPathsChangedByWorktreeBranch(root, branch, protectedPaths);
620
573
  }
621
574
 
622
575
  function registeredWorktreePaths(root: string): string[] {
@@ -687,26 +640,7 @@ export function removeCandidateWorktree(
687
640
  * the merge and reports failure so the operator decides.
688
641
  */
689
642
  export function mergeWinnerBranch(root: string, branch: string): { ok: true } | { ok: false; reason: string } {
690
- try {
691
- git(root, [
692
- "-c",
693
- `user.name=${COMPETE_COMMIT_IDENTITY}`,
694
- "-c",
695
- `user.email=${COMPETE_COMMIT_IDENTITY}@local`,
696
- "merge",
697
- "--no-edit",
698
- "--no-verify",
699
- branch,
700
- ]);
701
- return { ok: true };
702
- } catch (err) {
703
- try {
704
- git(root, ["merge", "--abort"]);
705
- } catch {
706
- // No merge in progress; nothing to abort.
707
- }
708
- return { ok: false, reason: err instanceof Error ? (err.message.split("\n")[0] ?? "merge failed") : String(err) };
709
- }
643
+ return mergeWorktreeBranch(root, branch, COMPETE_COMMIT_IDENTITY);
710
644
  }
711
645
 
712
646
  /**