@zerwiz/ymir 0.1.16 → 0.1.18

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 (171) hide show
  1. package/.agents/config/app-repos.yaml +38 -0
  2. package/.claude/settings.json +31 -0
  3. package/.codex/hooks.json +43 -0
  4. package/.cursor/hooks.json +33 -0
  5. package/.pi/extensions/README.md +39 -0
  6. package/.pi/extensions/gna-pi-watch.ts +15 -0
  7. package/.pi/extensions/lib/ro-assistant-layout.ts +96 -0
  8. package/.pi/extensions/lib/ro-operational-user-layout.ts +145 -0
  9. package/.pi/extensions/lib/ro-visibility.ts +99 -0
  10. package/.pi/extensions/lib/ro-working-ship.ts +243 -0
  11. package/.pi/extensions/lib/rodd-operational-input.ts +62 -0
  12. package/.pi/extensions/lib/skuld-branch-dispatch.ts +252 -0
  13. package/.pi/extensions/lib/skuld-branch-model-picker.ts +77 -0
  14. package/.pi/extensions/lib/vordr-sessionstart-supervisor.mjs +48 -0
  15. package/.pi/extensions/ro.ts +11 -0
  16. package/.pi/extensions/skuld-branch-supervision.ts +11 -0
  17. package/.pi/extensions/syn-turnend-guard.ts +11 -0
  18. package/.pi/mcp.json +16 -0
  19. package/.pi/mcp.json.example +11 -0
  20. package/.pi/npm/.gitignore +2 -0
  21. package/.pi/settings.json +8 -0
  22. package/.pi/shared/extensions/gna-pi-watch.ts +750 -0
  23. package/.pi/shared/extensions/herdr-agent-state.ts +257 -0
  24. package/.pi/shared/extensions/open-editor.ts +509 -0
  25. package/.pi/shared/extensions/ro.ts +504 -0
  26. package/.pi/shared/extensions/skuld-branch-supervision.ts +1535 -0
  27. package/.pi/shared/extensions/syn-turnend-guard.ts +683 -0
  28. package/.pi/shared/extensions/todo.ts +133 -0
  29. package/.pi/shared/extensions/ymir-subagents.ts +287 -0
  30. package/.pi/shared/extensions/ymir-well.ts +130 -0
  31. package/CHANGELOG.md +23 -0
  32. package/assets/Ymir.md +4654 -0
  33. package/assets/healer.html +717 -0
  34. package/assets/icon-family/family-sheet.png +0 -0
  35. package/assets/icon-family/hlidskjalf.svg +1 -0
  36. package/assets/icon-family/sessrumnir.svg +1 -0
  37. package/assets/icon-family/smidja.svg +1 -0
  38. package/assets/icon-family/ymir.svg +1 -0
  39. package/assets/mimirswell.html +880 -0
  40. package/assets/mock/README.md +16 -0
  41. package/assets/mock/feeds.ts +175 -0
  42. package/assets/mock/mock.ts +408 -0
  43. package/assets/reference/docs/agent-control.md +122 -0
  44. package/assets/reference/docs/apodex/Appdex_as_Orchestrator_Planner_Reviewer_Assessment.md +176 -0
  45. package/assets/reference/docs/apodex/Final Report(1).md +198 -0
  46. package/assets/reference/docs/apodex/Final Report(2).md +54 -0
  47. package/assets/reference/docs/apodex/Final Report(3).md +40 -0
  48. package/assets/reference/docs/apodex/Final Report(4).md +234 -0
  49. package/assets/reference/docs/apodex/Final Report(5).md +13 -0
  50. package/assets/reference/docs/apodex/Final Report.md +82 -0
  51. package/assets/reference/docs/architecture.md +392 -0
  52. package/assets/reference/docs/arm-pretool-check.md +247 -0
  53. package/assets/reference/docs/calm-mode-feasibility.md +541 -0
  54. package/assets/reference/docs/calm.md +59 -0
  55. package/assets/reference/docs/captain-hold-lifecycle.md +96 -0
  56. package/assets/reference/docs/cd-guard.md +165 -0
  57. package/assets/reference/docs/cmux-backend.md +133 -0
  58. package/assets/reference/docs/codex-app-backend.md +57 -0
  59. package/assets/reference/docs/configuration.md +924 -0
  60. package/assets/reference/docs/documentation-audiences.json +468 -0
  61. package/assets/reference/docs/documentation-audiences.md +28 -0
  62. package/assets/reference/docs/examples/crew-dispatch.json +26 -0
  63. package/assets/reference/docs/examples/process-event-extension/file-signal.mjs +96 -0
  64. package/assets/reference/docs/examples/process-event-extension/firstmate-extension.json +15 -0
  65. package/assets/reference/docs/examples/watched-tools.json +24 -0
  66. package/assets/reference/docs/examples/wedge-alarm +22 -0
  67. package/assets/reference/docs/extension-bindings.md +237 -0
  68. package/assets/reference/docs/fm-test-isolation-proof.json +37 -0
  69. package/assets/reference/docs/fm-test-isolation-proof.md +149 -0
  70. package/assets/reference/docs/fm-test-portable-shards.md +118 -0
  71. package/assets/reference/docs/gitlab-merge-watch.md +275 -0
  72. package/assets/reference/docs/herdr-backend.md +346 -0
  73. package/assets/reference/docs/orca-backend.md +84 -0
  74. package/assets/reference/docs/pi-supervision-branch-poster.svg +125 -0
  75. package/assets/reference/docs/pi-supervision-branch.md +96 -0
  76. package/assets/reference/docs/plans/agentic-engineering-workflow.md +395 -0
  77. package/assets/reference/docs/plans/agentic-factory-implementation-plan.md +615 -0
  78. package/assets/reference/docs/plans/command-factory-enhancement-plan.md +420 -0
  79. package/assets/reference/docs/plans/phase-1-layer3-program-design.md +134 -0
  80. package/assets/reference/docs/plans/phase-2-vertical-slices.md +100 -0
  81. package/assets/reference/docs/plans/phase-3-measurable-goals.md +106 -0
  82. package/assets/reference/docs/plans/phase-4-context-budget.md +106 -0
  83. package/assets/reference/docs/plans/phase-5-pre-mortem-confidence.md +100 -0
  84. package/assets/reference/docs/plans/phase-6-incident-to-agent.md +109 -0
  85. package/assets/reference/docs/remote-secondmates.md +268 -0
  86. package/assets/reference/docs/scripts.md +146 -0
  87. package/assets/reference/docs/sessionstart-nudge.md +115 -0
  88. package/assets/reference/docs/subagent-guard.md +383 -0
  89. package/assets/reference/docs/supervision-protocols/claude.md +27 -0
  90. package/assets/reference/docs/supervision-protocols/codex.md +16 -0
  91. package/assets/reference/docs/supervision-protocols/cursor.md +31 -0
  92. package/assets/reference/docs/supervision-protocols/grok.md +39 -0
  93. package/assets/reference/docs/supervision-protocols/opencode.md +17 -0
  94. package/assets/reference/docs/supervision-protocols/pi.md +33 -0
  95. package/assets/reference/docs/supervision-protocols/unknown.md +13 -0
  96. package/assets/reference/docs/tmux-backend.md +115 -0
  97. package/assets/reference/docs/trace-context.md +117 -0
  98. package/assets/reference/docs/turnend-guard.md +170 -0
  99. package/assets/reference/docs/verification/dispatch-auth.md +204 -0
  100. package/assets/reference/docs/verification/muse.md +221 -0
  101. package/assets/reference/docs/verification/process-event-sources.md +208 -0
  102. package/assets/reference/docs/verification/public-followup.md +119 -0
  103. package/assets/reference/docs/verification/runtime-backends.md +1022 -0
  104. package/assets/reference/docs/verification/stow-memory.md +53 -0
  105. package/assets/reference/docs/verification/supervision.md +534 -0
  106. package/assets/reference/docs/verification/trace-context.md +31 -0
  107. package/assets/reference/docs/voice-relay.md +295 -0
  108. package/assets/reference/docs/watcher-continuity.md +119 -0
  109. package/assets/reference/docs/wedge-alarm.md +39 -0
  110. package/assets/reference/docs/zellij-backend.md +114 -0
  111. package/assets/reference/index.html +1172 -0
  112. package/assets/reference/skills/afk/SKILL.md +241 -0
  113. package/assets/reference/skills/ahoy/SKILL.md +56 -0
  114. package/assets/reference/skills/ask-user-authority/SKILL.md +56 -0
  115. package/assets/reference/skills/bearings/SKILL.md +161 -0
  116. package/assets/reference/skills/bearings/assets/board-template.html +731 -0
  117. package/assets/reference/skills/bootstrap-diagnostics/SKILL.md +68 -0
  118. package/assets/reference/skills/captain-hold-lifecycle/SKILL.md +54 -0
  119. package/assets/reference/skills/decision-hold-lifecycle/SKILL.md +15 -0
  120. package/assets/reference/skills/diagnostic-reasoning/SKILL.md +53 -0
  121. package/assets/reference/skills/firstmate-codexapp/SKILL.md +110 -0
  122. package/assets/reference/skills/firstmate-codexapp/agents/openai.yaml +4 -0
  123. package/assets/reference/skills/firstmate-coding-guidelines/SKILL.md +127 -0
  124. package/assets/reference/skills/firstmate-orca/SKILL.md +92 -0
  125. package/assets/reference/skills/fmx-respond/SKILL.md +283 -0
  126. package/assets/reference/skills/harness-adapters/SKILL.md +95 -0
  127. package/assets/reference/skills/harness-adapters/references/common/control-and-recovery.md +37 -0
  128. package/assets/reference/skills/harness-adapters/references/common/dispatch.md +32 -0
  129. package/assets/reference/skills/harness-adapters/references/common/model-and-effort.md +42 -0
  130. package/assets/reference/skills/harness-adapters/references/common/primary-hooks.md +40 -0
  131. package/assets/reference/skills/harness-adapters/references/harness/claude.md +55 -0
  132. package/assets/reference/skills/harness-adapters/references/harness/codex.md +43 -0
  133. package/assets/reference/skills/harness-adapters/references/harness/cursor.md +75 -0
  134. package/assets/reference/skills/harness-adapters/references/harness/grok.md +69 -0
  135. package/assets/reference/skills/harness-adapters/references/harness/kimi.md +51 -0
  136. package/assets/reference/skills/harness-adapters/references/harness/muse.md +70 -0
  137. package/assets/reference/skills/harness-adapters/references/harness/opencode.md +42 -0
  138. package/assets/reference/skills/harness-adapters/references/harness/pi.md +56 -0
  139. package/assets/reference/skills/process-event-sources/SKILL.md +137 -0
  140. package/assets/reference/skills/project-management/SKILL.md +92 -0
  141. package/assets/reference/skills/quota-array-dispatch/SKILL.md +131 -0
  142. package/assets/reference/skills/secondmate-provisioning/SKILL.md +254 -0
  143. package/assets/reference/skills/stow/SKILL.md +309 -0
  144. package/assets/reference/skills/stuck-crewmate-recovery/SKILL.md +54 -0
  145. package/assets/reference/skills/updatefirstmate/SKILL.md +64 -0
  146. package/assets/skills/README.md +5 -0
  147. package/assets/yggdrasil.html +649 -0
  148. package/assets/ymir-banner-01.png +0 -0
  149. package/assets/ymir-banner-02.png +0 -0
  150. package/assets/ymir-banner-03.png +0 -0
  151. package/assets/ymir-banner-04.png +0 -0
  152. package/assets/ymir-banner-05.png +0 -0
  153. package/assets/ymir-banner-06.png +0 -0
  154. package/assets/ymir-emblem-darksteel.svg +63 -0
  155. package/assets/ymir-emblem-forged.svg +55 -0
  156. package/assets/ymir-emblem-slate.svg +55 -0
  157. package/assets/ymir-mark-algiz-anvil.svg +9 -0
  158. package/assets/ymir-stave.svg +53 -0
  159. package/assets/ymir.html +563 -0
  160. package/bin/ymir-install.sh +6 -1
  161. package/data/learnings.md +33 -0
  162. package/data/local-models.md +34 -0
  163. package/data/operator.md +15 -0
  164. package/data/projects.md +28 -0
  165. package/data/realm.md +1 -0
  166. package/deploy/Containerfile +27 -0
  167. package/deploy/README.md +104 -0
  168. package/deploy/compose/compose.yaml +45 -0
  169. package/deploy/env.example +30 -0
  170. package/deploy/quadlet/ymir.container +57 -0
  171. package/package.json +10 -8
@@ -0,0 +1,38 @@
1
+ # App repositories — PUBLIC map (the five surfaces)
2
+ #
3
+ # The fork path: a fresh clone has no private hoard (no
4
+ # `$YMIR_HOME/hodd/identity/projects.yaml`), so bin/ymir-install.sh's `apps`
5
+ # step falls back to THIS file to clone the five app repos. The hoard registry
6
+ # stays authoritative when present; this file is the fork's road.
7
+ #
8
+ # Block shape must match the installer's awk (repo: apps/<path> + git: dict).
9
+
10
+ - id: hlidskjalf
11
+ name: Hlidskjalf
12
+ repo: apps/hlidskjalf
13
+ posture: direct-PR
14
+ git: { host: github.com, owner: zerwiz, repo: hlidskjalf, remote: origin, default_branch: main, auth: gh }
15
+
16
+ - id: hlidskjalf-mobile
17
+ name: Hlidskjalf Mobile
18
+ repo: apps/hlidskjalf-mobile
19
+ posture: direct-PR
20
+ git: { host: github.com, owner: zerwiz, repo: hlidskjalf-mobile, remote: origin, default_branch: main, auth: gh }
21
+
22
+ - id: odrerir
23
+ name: Óðrerir
24
+ repo: apps/odrerir
25
+ posture: direct-PR
26
+ git: { host: github.com, owner: zerwiz, repo: odrerir, remote: origin, default_branch: main, auth: gh }
27
+
28
+ - id: sessrumnir
29
+ name: Sessrúmnir
30
+ repo: apps/sessrumnir
31
+ posture: direct-PR
32
+ git: { host: github.com, owner: zerwiz, repo: sessrumnir, remote: origin, default_branch: main, auth: gh }
33
+
34
+ - id: smidja
35
+ name: Smíðja factory
36
+ repo: apps/smidja-factory
37
+ posture: direct-PR
38
+ git: { host: github.com, owner: zerwiz, repo: smidja, remote: origin, default_branch: main, auth: gh }
@@ -0,0 +1,31 @@
1
+ {
2
+ "hooks": {
3
+ "SessionStart": [
4
+ {
5
+ "hooks": [
6
+ {
7
+ "type": "command",
8
+ "command": "[ -z \"${GROK_AGENT:-}${GROK_HOOK_EVENT:-}\" ] || exit 0; exec \"$CLAUDE_PROJECT_DIR\"/bin/saga-sessionstart-run.sh",
9
+ "timeout": 180
10
+ }
11
+ ]
12
+ }
13
+ ],
14
+ "Stop": [
15
+ {
16
+ "hooks": [
17
+ {
18
+ "type": "command",
19
+ "command": "[ -z \"${GROK_AGENT:-}${GROK_HOOK_EVENT:-}\" ] || exit 0; exec \"$CLAUDE_PROJECT_DIR\"/bin/syn-turnend-guard.sh --claude"
20
+ },
21
+ {
22
+ "type": "command",
23
+ "command": "[ -z \"${GROK_AGENT:-}${GROK_HOOK_EVENT:-}\" ] || exit 0; out=$(exec \"$CLAUDE_PROJECT_DIR\"/bin/syn-watch-arm.sh --restart); rc=$?; case \"$out\" in *signal:*|*stale:*|*check:*|*heartbeat:*) printf '%s\n' \"$out\" >&2; exit 2;; esac; exit $rc",
24
+ "asyncRewake": true,
25
+ "timeout": 28800
26
+ }
27
+ ]
28
+ }
29
+ ]
30
+ }
31
+ }
@@ -0,0 +1,43 @@
1
+ {
2
+ "hooks": {
3
+ "SessionStart": [
4
+ {
5
+ "hooks": [
6
+ {
7
+ "type": "command",
8
+ "command": "bash -lc 'payload=$(cat 2>/dev/null || true); [ -n \"$payload\" ] || exit 0; command -v jq >/dev/null 2>&1 || exit 0; root=$(pwd -P) || exit 0; [ -x \"$root/bin/saga-sessionstart-run.sh\" ] || exit 0; [ -f \"$root/AGENTS.md\" ] || exit 0; [ -f \"$root/.codex/hooks.json\" ] || exit 0; jq -e \"any(.hooks.SessionStart[]?.hooks[]?.command?; type == \\\"string\\\" and contains(\\\"saga-sessionstart-run.sh\\\"))\" \"$root/.codex/hooks.json\" >/dev/null 2>&1 || exit 0; printf \"%s\" \"$payload\" | \"$root/bin/saga-sessionstart-run.sh\"'",
9
+ "timeout": 180
10
+ }
11
+ ]
12
+ }
13
+ ],
14
+ "PreToolUse": [
15
+ {
16
+ "matcher": "Bash",
17
+ "hooks": [
18
+ {
19
+ "type": "command",
20
+ "command": "bash -lc 'payload=$(cat 2>/dev/null || true); [ -n \"$payload\" ] || exit 0; command -v jq >/dev/null 2>&1 || exit 0; root=$(pwd -P) || exit 0; [ -x \"$root/bin/syn-arm-pretool-check.sh\" ] || exit 0; [ -f \"$root/AGENTS.md\" ] || exit 0; [ -f \"$root/.codex/hooks.json\" ] || exit 0; jq -e \"any(.hooks.PreToolUse[]?.hooks[]?.command?; type == \\\"string\\\" and contains(\\\"syn-arm-pretool-check.sh\\\"))\" \"$root/.codex/hooks.json\" >/dev/null 2>&1 || exit 0; printf \"%s\" \"$payload\" | \"$root/bin/syn-arm-pretool-check.sh\"'",
21
+ "timeout": 10
22
+ },
23
+ {
24
+ "type": "command",
25
+ "command": "bash -lc 'payload=$(cat 2>/dev/null || true); [ -n \"$payload\" ] || exit 0; command -v jq >/dev/null 2>&1 || exit 0; root=$(pwd -P) || exit 0; [ -x \"$root/bin/syn-cd-pretool-check.sh\" ] || exit 0; [ -f \"$root/AGENTS.md\" ] || exit 0; [ -f \"$root/.codex/hooks.json\" ] || exit 0; jq -e \"any(.hooks.PreToolUse[]?.hooks[]?.command?; type == \\\"string\\\" and contains(\\\"syn-cd-pretool-check.sh\\\"))\" \"$root/.codex/hooks.json\" >/dev/null 2>&1 || exit 0; printf \"%s\" \"$payload\" | \"$root/bin/syn-cd-pretool-check.sh\"'",
26
+ "timeout": 10
27
+ }
28
+ ]
29
+ }
30
+ ],
31
+ "Stop": [
32
+ {
33
+ "hooks": [
34
+ {
35
+ "type": "command",
36
+ "command": "bash -lc 'payload=$(cat 2>/dev/null || true); [ -n \"$payload\" ] || exit 0; command -v jq >/dev/null 2>&1 || exit 0; root=$(pwd -P) || exit 0; [ -x \"$root/bin/syn-turnend-guard.sh\" ] || exit 0; [ -f \"$root/AGENTS.md\" ] || exit 0; [ -f \"$root/.codex/hooks.json\" ] || exit 0; jq -e \"any(.hooks.Stop[]?.hooks[]?.command?; type == \\\"string\\\" and contains(\\\"syn-turnend-guard.sh\\\"))\" \"$root/.codex/hooks.json\" >/dev/null 2>&1 || exit 0; printf \"%s\" \"$payload\" | \"$root/bin/syn-turnend-guard.sh\"'",
37
+ "timeout": 30
38
+ }
39
+ ]
40
+ }
41
+ ]
42
+ }
43
+ }
@@ -0,0 +1,33 @@
1
+ {
2
+ "version": 1,
3
+ "hooks": {
4
+ "sessionStart": [
5
+ {
6
+ "type": "command",
7
+ "command": "digest=$(\"$CURSOR_PROJECT_DIR\"/bin/saga-sessionstart-run.sh --source startup </dev/null 2>/dev/null); [ -n \"$digest\" ] || exit 0; command -v jq >/dev/null 2>&1 || exit 0; jq -n --arg c \"$digest\" '{additional_context:$c}' 2>/dev/null || true",
8
+ "timeout": 180
9
+ }
10
+ ],
11
+ "stop": [
12
+ {
13
+ "type": "command",
14
+ "command": "guard=$(\"$CURSOR_PROJECT_DIR\"/bin/syn-turnend-guard.sh 2>&1 </dev/null); rc=$?; [ \"$rc\" -eq 2 ] || exit 0; command -v jq >/dev/null 2>&1 || exit 0; jq -n --arg m \"$guard\" '{followup_message:$m}' 2>/dev/null || true",
15
+ "timeout": 30
16
+ }
17
+ ],
18
+ "preToolUse": [
19
+ {
20
+ "matcher": "Shell",
21
+ "type": "command",
22
+ "command": "\"$CURSOR_PROJECT_DIR\"/bin/syn-arm-pretool-check.sh --cursor",
23
+ "timeout": 10
24
+ },
25
+ {
26
+ "matcher": "Shell",
27
+ "type": "command",
28
+ "command": "\"$CURSOR_PROJECT_DIR\"/bin/syn-cd-pretool-check.sh --cursor",
29
+ "timeout": 10
30
+ }
31
+ ]
32
+ }
33
+ }
@@ -0,0 +1,39 @@
1
+ # Pi extensions — Brokk distro runtime
2
+
3
+ These extensions make a Pi session open as **Brokk** the way Ymir intends: the
4
+ primary takes the high seat automatically, the Sága session-start context is
5
+ injected before the first turn, and supervision stays alive across the session.
6
+ Ported from the validated upstream agent-distro reference for
7
+ [plan 29](../../docs/plans/29-brokk-distro-runtime.md).
8
+
9
+ | File | Norse role | Purpose |
10
+ |---|---|---|
11
+ | `syn-turnend-guard.ts` | **Sýn** (watchful sight) | inject the Sága digest at session start, re-emit on compaction, refuse a blind turn end, PreToolUse seatbelts |
12
+ | `gna-pi-watch.ts` | **Gná** (Frigg's messenger) | watcher continuity: arm, re-arm, deliver actionable wakes; emits the Skuld dispatch offer |
13
+ | `ro.ts` | **Ró** (calm/peace) | calm presentation: hides transcript chrome, replaces the working row with a longship, `/ro` toggle |
14
+ | `skuld-branch-supervision.ts` | **Skuld** (the Norn of what shall be) | supervision branch: handles routine wakes with a cheaper model; `/skuld-model` |
15
+ | `lib/vordr-sessionstart-supervisor.mjs` | **Vörðr** (warden) | supervise the digest child process |
16
+ | `lib/rodd-operational-input.ts` | **Rödd** (voice) | structured operational-message wire bridge |
17
+ | `lib/ro-*.ts` | **Ró** helpers | visibility, assistant/user layout adapters, working longship |
18
+ | `lib/skuld-branch-*.ts` | **Skuld** helpers | dispatch handshake + model picker |
19
+
20
+ ## Wiring
21
+
22
+ - **Sága** digest: `bin/saga-session-start.sh`, routed by `bin/saga-sessionstart-run.sh`.
23
+ - **Gná** watcher: `bin/syn-watch-arm.sh`; turn-end check: `bin/syn-turnend-guard.sh`.
24
+ - **Gleipnir** lock: `bin/gleipnir-lock-lib.sh` (writes `state/.lock`).
25
+ - **Rödd** wire: `bin/rodd-operational-input.sh`.
26
+
27
+ The harness passes `BROKK_SESSION_PID` so the session lock is bound to the live
28
+ Pi process, not the short-lived digest helper.
29
+
30
+ ## Other harnesses
31
+
32
+ OpenCode, Claude Code, Codex, and Cursor adapters are wired too; see
33
+ `docs/plans/29-brokk-distro-runtime.md` §7 and
34
+ `.agents/skills/galdr-ymirsystem/assets/harness-integration/`. Grok is not yet implemented.
35
+
36
+ ## Agents
37
+
38
+ Agents live in `.agents/agents/` and are bound per tool by `bin/valknut-load.sh`
39
+ (OpenCode reads `.opencode/agents/`; Pi links resolve under `.pi/agents/`).
@@ -0,0 +1,15 @@
1
+ /**
2
+ * A no-op extension, kept only so nothing loads TWICE.
3
+ *
4
+ * An extension that registers the same tools as one already loaded from the global
5
+ * home makes pi refuse the duplicate — `Tool "gna_watch_arm" conflicts with …`. But a
6
+ * file that exports no factory at all is its own error:
7
+ * "Extension does not export a valid factory function".
8
+ * So this exports a valid factory that registers nothing.
9
+ *
10
+ * The real extension is `gna-pi-watch`, in the shared source
11
+ * (`.pi/shared/extensions/`), deployed to the one global home.
12
+ */
13
+ export default function well(): void {
14
+ // deliberately empty: this seat is not the extension's home.
15
+ }
@@ -0,0 +1,96 @@
1
+ // Verified against Pi 0.81.1 and 0.82.0, which export AssistantMessageComponent with an
2
+ // updateContent method. installRoAssistantLayout() probes that exact method and throws
3
+ // if it is missing; ro.ts catches that and skips only this adapter with a diagnostic
4
+ // instead of blocking Ró or Pi.
5
+ // This layout removes collapsed thinking and the mid-turn assistant text blocks
6
+ // classified as "assistant-working-note" from a shallow presentation copy. The message
7
+ // itself, model context, session storage, and export rendering are never touched.
8
+ // ./ro-visibility.ts owns which classes Ró hides.
9
+ import type { AssistantMessageComponent as PiAssistantMessageComponent } from "@earendil-works/pi-coding-agent";
10
+ import * as PiCodingAgent from "@earendil-works/pi-coding-agent";
11
+ import { roPresentationHides } from "./ro-visibility.ts";
12
+
13
+ type AssistantMessage = Parameters<PiAssistantMessageComponent["updateContent"]>[0];
14
+
15
+ type AssistantMessagePresentationState = {
16
+ hiddenThinkingLabel: string;
17
+ hideThinkingBlock: boolean;
18
+ lastMessage?: AssistantMessage;
19
+ };
20
+
21
+ type CalmAssistantLayoutPatch = {
22
+ hidesThinking: () => boolean;
23
+ hidesWorkingNote: () => boolean;
24
+ };
25
+
26
+ // A mid-turn assistant message is one the model did not end its response with: Pi's
27
+ // agent loop runs its tool calls and then issues another assistant message. stopReason
28
+ // is intrinsic to each message and is already set while the message streams, so this
29
+ // layout never has to ask whether the turn ended. It stays "pending" until the tool
30
+ // call materializes, which is why a working note is briefly visible before it
31
+ // collapses; suppressing pending text would also stop a genuine reply from streaming.
32
+ function isMidTurnAssistantMessage(message: AssistantMessage): boolean {
33
+ if (message.stopReason === "toolUse") return true;
34
+ return (
35
+ message.stopReason === "length" &&
36
+ message.content.some((block) => block.type === "toolCall")
37
+ );
38
+ }
39
+
40
+ // Keep the introduction-version symbol stable so a compatible upgrade cannot
41
+ // double-patch a live process.
42
+ const CALM_ASSISTANT_LAYOUT_PATCH = Symbol.for(
43
+ "brokk:ro-assistant-layout:pi-0.81.1",
44
+ );
45
+
46
+ export function installRoAssistantLayout(): void {
47
+ const registry = globalThis as typeof globalThis & {
48
+ [key: symbol]: CalmAssistantLayoutPatch | undefined;
49
+ };
50
+ const hidesThinking = (): boolean => roPresentationHides("assistant-thinking");
51
+ const hidesWorkingNote = (): boolean => roPresentationHides("assistant-working-note");
52
+ const installed = registry[CALM_ASSISTANT_LAYOUT_PATCH];
53
+ if (installed) {
54
+ installed.hidesThinking = hidesThinking;
55
+ installed.hidesWorkingNote = hidesWorkingNote;
56
+ return;
57
+ }
58
+
59
+ const patch: CalmAssistantLayoutPatch = { hidesThinking, hidesWorkingNote };
60
+ const AssistantMessageComponent = PiCodingAgent.AssistantMessageComponent;
61
+ if (typeof AssistantMessageComponent !== "function") {
62
+ throw new Error("Brokk Ró requires Pi AssistantMessageComponent");
63
+ }
64
+ const originalUpdateContent = AssistantMessageComponent.prototype.updateContent;
65
+ if (typeof originalUpdateContent !== "function") {
66
+ throw new Error("Brokk Ró requires Pi AssistantMessageComponent.updateContent");
67
+ }
68
+
69
+ AssistantMessageComponent.prototype.updateContent = function (
70
+ message: AssistantMessage,
71
+ ): void {
72
+ const state = this as unknown as AssistantMessagePresentationState;
73
+ const hideThinking =
74
+ state.hiddenThinkingLabel === "" &&
75
+ state.hideThinkingBlock &&
76
+ patch.hidesThinking();
77
+ const hideWorkingNote =
78
+ patch.hidesWorkingNote() && isMidTurnAssistantMessage(message);
79
+ const presentationMessage =
80
+ hideThinking || hideWorkingNote
81
+ ? {
82
+ ...message,
83
+ content: message.content.filter(
84
+ (block) =>
85
+ !(hideThinking && block.type === "thinking") &&
86
+ !(hideWorkingNote && block.type === "text"),
87
+ ),
88
+ }
89
+ : message;
90
+
91
+ originalUpdateContent.call(this, presentationMessage);
92
+ if (presentationMessage !== message) state.lastMessage = message;
93
+ };
94
+
95
+ registry[CALM_ASSISTANT_LAYOUT_PATCH] = patch;
96
+ }
@@ -0,0 +1,145 @@
1
+ // Verified against Pi 0.81.1 and 0.82.0, which add the ordinary-user spacer and row
2
+ // together via InteractiveMode.addMessageToChat. This adapter probes that exact method
3
+ // and throws if it is missing; ro.ts catches that and skips only this adapter with a
4
+ // diagnostic instead of blocking Ró or Pi. It changes only that presentation and never
5
+ // message delivery.
6
+ import type { UserMessageComponent as PiUserMessageComponent } from "@earendil-works/pi-coding-agent";
7
+ import * as PiCodingAgent from "@earendil-works/pi-coding-agent";
8
+ import { roPresentationHides } from "./ro-visibility.ts";
9
+ import { classifyRoddCurrentOperationalText } from "./rodd-operational-input.ts";
10
+
11
+ type UserMessageConstructorArgs = ConstructorParameters<typeof PiUserMessageComponent>;
12
+ type UserMessageLike = {
13
+ role: string;
14
+ content: unknown;
15
+ };
16
+ type AddMessageOptions = {
17
+ populateHistory?: boolean;
18
+ };
19
+ type InteractiveModePresentation = {
20
+ chatContainer: {
21
+ children: unknown[];
22
+ addChild(component: PiUserMessageComponent): void;
23
+ };
24
+ editor: {
25
+ addToHistory?(text: string): void;
26
+ };
27
+ getMarkdownThemeWithSettings(): UserMessageConstructorArgs[1];
28
+ getUserMessageText(message: UserMessageLike): string;
29
+ outputPad: number;
30
+ };
31
+ type InteractiveModePrototype = {
32
+ addMessageToChat(
33
+ this: InteractiveModePresentation,
34
+ message: UserMessageLike,
35
+ options?: AddMessageOptions,
36
+ ): void;
37
+ };
38
+ type CalmOperationalUserLayoutPatch = {
39
+ hidesOperationalInput: () => boolean;
40
+ isOperationalInput: (text: string) => boolean;
41
+ };
42
+
43
+ // Keep the introduction-version symbol stable so a compatible upgrade cannot
44
+ // double-patch a live process.
45
+ const CALM_OPERATIONAL_USER_LAYOUT_PATCH = Symbol.for(
46
+ "brokk:ro-operational-user-layout:pi-0.81.1",
47
+ );
48
+ const LEGACY_CALM_OPERATIONAL_PREFIX = "\u2063Supervisor escalate (";
49
+
50
+ function contentIsTextOnly(content: unknown): boolean {
51
+ if (typeof content === "string") return true;
52
+ if (!Array.isArray(content) || content.length === 0) return false;
53
+ return content.every(
54
+ (block) =>
55
+ typeof block === "object" &&
56
+ block !== null &&
57
+ (block as { type?: unknown }).type === "text" &&
58
+ typeof (block as { text?: unknown }).text === "string",
59
+ );
60
+ }
61
+
62
+ export function installRoOperationalUserLayout(): void {
63
+ const registry = globalThis as typeof globalThis & {
64
+ [key: symbol]: CalmOperationalUserLayoutPatch | undefined;
65
+ };
66
+ const hidesOperationalInput = (): boolean => roPresentationHides("synthetic-user");
67
+ const isOperationalInput = (text: string): boolean => {
68
+ if (!text.includes("\u2063")) return false;
69
+ return (
70
+ classifyRoddCurrentOperationalText(text) !== undefined ||
71
+ text.startsWith(LEGACY_CALM_OPERATIONAL_PREFIX)
72
+ );
73
+ };
74
+ const installed = registry[CALM_OPERATIONAL_USER_LAYOUT_PATCH];
75
+ if (installed) {
76
+ installed.hidesOperationalInput = hidesOperationalInput;
77
+ installed.isOperationalInput = isOperationalInput;
78
+ return;
79
+ }
80
+
81
+ const patch: CalmOperationalUserLayoutPatch = {
82
+ hidesOperationalInput,
83
+ isOperationalInput,
84
+ };
85
+ const InteractiveMode = PiCodingAgent.InteractiveMode;
86
+ if (typeof InteractiveMode !== "function") {
87
+ throw new Error("Brokk Ró requires Pi InteractiveMode");
88
+ }
89
+ const prototype = InteractiveMode.prototype as unknown as InteractiveModePrototype;
90
+ const originalAddMessageToChat = prototype.addMessageToChat;
91
+ if (typeof originalAddMessageToChat !== "function") {
92
+ throw new Error("Brokk Ró requires Pi InteractiveMode.addMessageToChat");
93
+ }
94
+
95
+ const UserMessageComponent = PiCodingAgent.UserMessageComponent;
96
+ if (typeof UserMessageComponent !== "function") {
97
+ throw new Error("Brokk Ró requires Pi UserMessageComponent");
98
+ }
99
+ class CalmOperationalUserMessageComponent extends UserMessageComponent {
100
+ private readonly hasLeadingSpacer: boolean;
101
+
102
+ constructor(
103
+ text: UserMessageConstructorArgs[0],
104
+ markdownTheme: UserMessageConstructorArgs[1],
105
+ outputPad: number,
106
+ hasLeadingSpacer: boolean,
107
+ ) {
108
+ super(text, markdownTheme, outputPad);
109
+ this.hasLeadingSpacer = hasLeadingSpacer;
110
+ }
111
+
112
+ override render(width: number): string[] {
113
+ if (patch.hidesOperationalInput()) return [];
114
+ const lines = super.render(width);
115
+ return this.hasLeadingSpacer ? ["", ...lines] : lines;
116
+ }
117
+ }
118
+
119
+ prototype.addMessageToChat = function (
120
+ message: UserMessageLike,
121
+ options?: AddMessageOptions,
122
+ ): void {
123
+ if (message.role !== "user" || !contentIsTextOnly(message.content)) {
124
+ originalAddMessageToChat.call(this, message, options);
125
+ return;
126
+ }
127
+
128
+ const text = this.getUserMessageText(message);
129
+ if (!text || !patch.isOperationalInput(text)) {
130
+ originalAddMessageToChat.call(this, message, options);
131
+ return;
132
+ }
133
+
134
+ const component = new CalmOperationalUserMessageComponent(
135
+ text,
136
+ this.getMarkdownThemeWithSettings(),
137
+ this.outputPad,
138
+ this.chatContainer.children.length > 0,
139
+ );
140
+ this.chatContainer.addChild(component);
141
+ if (options?.populateHistory) this.editor.addToHistory?.(text);
142
+ };
143
+
144
+ registry[CALM_OPERATIONAL_USER_LAYOUT_PATCH] = patch;
145
+ }
@@ -0,0 +1,99 @@
1
+ import {
2
+ getMarkdownTheme,
3
+ type ExtensionAPI,
4
+ UserMessageComponent,
5
+ } from "@earendil-works/pi-coding-agent";
6
+ export const RO_TRANSCRIPT_CLASSES = [
7
+ "genuine-user-prompt",
8
+ "genuine-agent-response",
9
+ "assistant-working-note",
10
+ "assistant-thinking",
11
+ "assistant-tool-call",
12
+ "tool-result",
13
+ "tool-image",
14
+ "user-bash",
15
+ "skill-invocation",
16
+ "custom-message",
17
+ "custom-entry",
18
+ "compaction-summary",
19
+ "branch-summary",
20
+ "working-status",
21
+ "command-status",
22
+ "system-notice",
23
+ "cache-notice",
24
+ "project-trust-warning",
25
+ "synthetic-user",
26
+ "synthetic-assistant",
27
+ "unknown",
28
+ ] as const;
29
+
30
+ export type CalmTranscriptClass = (typeof RO_TRANSCRIPT_CLASSES)[number];
31
+
32
+ // Ró is on or off. "assistant-working-note" is deliberately absent from the allowlist:
33
+ // Ró hides mid-turn assistant working notes, keeping the genuine final reply.
34
+ const CALM_VISIBLE_CLASSES = new Set<CalmTranscriptClass>([
35
+ "genuine-user-prompt",
36
+ "genuine-agent-response",
37
+ "working-status",
38
+ ]);
39
+
40
+ // Legacy session entries from Ró versions before 2026-07-23 retain this
41
+ // presentation type. New operational input stays user-role and is never rerouted.
42
+ export const RO_SYNTHETIC_PRESENTATION_TYPE = "brokk-synthetic-input-presentation";
43
+ export const RO_PRESENTATION_EVENT = "brokk:ro-presentation";
44
+
45
+ export type RoPresentationState = {
46
+ active: boolean;
47
+ stockExportRendering: boolean;
48
+ };
49
+
50
+ export const RO_SYNTHETIC_KINDS = [
51
+ "session-start",
52
+ "watcher",
53
+ "turn-end-guard",
54
+ "away-supervisor",
55
+ "from-brokk",
56
+ "launch-brief",
57
+ "legacy-operational",
58
+ ] as const;
59
+
60
+ export type BrokkSyntheticKind = (typeof RO_SYNTHETIC_KINDS)[number];
61
+ type BrokkSyntheticPresentation = {
62
+ content: string;
63
+ kind: BrokkSyntheticKind;
64
+ };
65
+
66
+ let ro = false;
67
+ let stockExportRendering = false;
68
+
69
+ export function roTranscriptClassIsVisible(itemClass: CalmTranscriptClass): boolean {
70
+ return CALM_VISIBLE_CLASSES.has(itemClass);
71
+ }
72
+
73
+ export function setCalmPresentation(active: boolean): void {
74
+ ro = active;
75
+ }
76
+
77
+ export function setCalmStockExportRendering(active: boolean): void {
78
+ stockExportRendering = active;
79
+ }
80
+
81
+ export function calmPresentationIsActive(): boolean {
82
+ return ro;
83
+ }
84
+
85
+ export function roPresentationHides(itemClass: CalmTranscriptClass): boolean {
86
+ return ro && !stockExportRendering && !roTranscriptClassIsVisible(itemClass);
87
+ }
88
+
89
+ export function registerBrokkSyntheticPresentation(pi: ExtensionAPI): void {
90
+ pi.registerEntryRenderer<BrokkSyntheticPresentation>(
91
+ RO_SYNTHETIC_PRESENTATION_TYPE,
92
+ (entry) => {
93
+ if (roPresentationHides("synthetic-user")) return undefined;
94
+ const data = entry.data;
95
+ if (!data || typeof data.content !== "string") return undefined;
96
+ return new UserMessageComponent(data.content, getMarkdownTheme());
97
+ },
98
+ );
99
+ }