@zerwiz/ymir 0.1.4 → 0.1.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 (865) hide show
  1. package/.agents/README.md +291 -0
  2. package/.agents/agents/bragi-marketer.md +111 -0
  3. package/.agents/agents/brokk.md +352 -0
  4. package/.agents/agents/forseti-reviewer.md +93 -0
  5. package/.agents/agents/frigg-consent.md +77 -0
  6. package/.agents/agents/groa-update.md +77 -0
  7. package/.agents/agents/hnoss-designer.md +69 -0
  8. package/.agents/agents/huginn-researcher.md +99 -0
  9. package/.agents/agents/jord-projects.md +78 -0
  10. package/.agents/agents/kvasir-scout.md +91 -0
  11. package/.agents/agents/mimir-planner.md +92 -0
  12. package/.agents/agents/muninn-stow.md +77 -0
  13. package/.agents/agents/saga-bearings.md +78 -0
  14. package/.agents/agents/sindri-developer.md +114 -0
  15. package/.agents/agents/snotra-documenter.md +105 -0
  16. package/.agents/agents/syn-recovery.md +78 -0
  17. package/.agents/agents/tyr-check.md +78 -0
  18. package/.agents/agents/urdh-hold.md +77 -0
  19. package/.agents/agents/volundr-smith.md +80 -0
  20. package/.agents/agents/vor-diagnostics.md +77 -0
  21. package/.agents/assets/agents/naming.md +50 -0
  22. package/.agents/assets/agents/registry.md +93 -0
  23. package/.agents/assets/agents/runtime.md +36 -0
  24. package/.agents/assets/agents/toon-tasks-cli.md +42 -0
  25. package/.agents/assets/templates/company_entity.template.md +37 -0
  26. package/.agents/backend/README.md +74 -0
  27. package/.agents/backend/backends/cmux.sh +649 -0
  28. package/.agents/backend/backends/herdr-eventwait.py +157 -0
  29. package/.agents/backend/backends/herdr-workspace-move.py +114 -0
  30. package/.agents/backend/backends/herdr.sh +3386 -0
  31. package/.agents/backend/backends/orca.sh +290 -0
  32. package/.agents/backend/backends/tmux.sh +357 -0
  33. package/.agents/backend/backends/zellij.sh +700 -0
  34. package/.agents/backend/fm-afk-launch.sh +654 -0
  35. package/.agents/backend/fm-afk-return.sh +241 -0
  36. package/.agents/backend/fm-afk-start.sh +172 -0
  37. package/.agents/backend/fm-arm-command-policy.mjs +960 -0
  38. package/.agents/backend/fm-arm-pretool-check.sh +200 -0
  39. package/.agents/backend/fm-backend-hometag-lib.sh +52 -0
  40. package/.agents/backend/fm-backend.sh +981 -0
  41. package/.agents/backend/fm-backlog-handoff.sh +909 -0
  42. package/.agents/backend/fm-backlog-receive.sh +187 -0
  43. package/.agents/backend/fm-backlog-transition-lib.sh +779 -0
  44. package/.agents/backend/fm-bearings-board.sh +204 -0
  45. package/.agents/backend/fm-bearings-snapshot.sh +546 -0
  46. package/.agents/backend/fm-bootstrap.sh +1568 -0
  47. package/.agents/backend/fm-branch-outcome.sh +203 -0
  48. package/.agents/backend/fm-branch-prompt.sh +101 -0
  49. package/.agents/backend/fm-brief.sh +452 -0
  50. package/.agents/backend/fm-busy-event.sh +234 -0
  51. package/.agents/backend/fm-busy-lib.sh +986 -0
  52. package/.agents/backend/fm-captain-hold.sh +1001 -0
  53. package/.agents/backend/fm-cd-command-policy.mjs +153 -0
  54. package/.agents/backend/fm-cd-pretool-check.sh +190 -0
  55. package/.agents/backend/fm-check-lib.sh +74 -0
  56. package/.agents/backend/fm-check-register.sh +42 -0
  57. package/.agents/backend/fm-check-unregister.sh +52 -0
  58. package/.agents/backend/fm-classify-lib.sh +1770 -0
  59. package/.agents/backend/fm-claude-stop-autoarm.sh +334 -0
  60. package/.agents/backend/fm-composer-lib.sh +1416 -0
  61. package/.agents/backend/fm-config-inherit-lib.sh +1202 -0
  62. package/.agents/backend/fm-config-push.sh +254 -0
  63. package/.agents/backend/fm-control-lib.sh +251 -0
  64. package/.agents/backend/fm-control.sh +878 -0
  65. package/.agents/backend/fm-crew-state.sh +625 -0
  66. package/.agents/backend/fm-cursor-lib.sh +243 -0
  67. package/.agents/backend/fm-decision-hold.sh +232 -0
  68. package/.agents/backend/fm-doc-audience-check.sh +269 -0
  69. package/.agents/backend/fm-dod-lib.sh +67 -0
  70. package/.agents/backend/fm-ensure-agents-md.sh +253 -0
  71. package/.agents/backend/fm-extension-launch-barrier.mjs +129 -0
  72. package/.agents/backend/fm-extension.mjs +2577 -0
  73. package/.agents/backend/fm-extension.sh +16 -0
  74. package/.agents/backend/fm-ff-lib.sh +420 -0
  75. package/.agents/backend/fm-fleet-snapshot.sh +1463 -0
  76. package/.agents/backend/fm-fleet-sync.sh +458 -0
  77. package/.agents/backend/fm-fleet-view.sh +96 -0
  78. package/.agents/backend/fm-gate-refuse-lib.sh +102 -0
  79. package/.agents/backend/fm-guard.sh +242 -0
  80. package/.agents/backend/fm-harness.sh +195 -0
  81. package/.agents/backend/fm-herdr-ci-cleanup.sh +115 -0
  82. package/.agents/backend/fm-herdr-lab.sh +346 -0
  83. package/.agents/backend/fm-herdr-session-cleanup.sh +337 -0
  84. package/.agents/backend/fm-home-seed.sh +984 -0
  85. package/.agents/backend/fm-home-summary-refresh.sh +255 -0
  86. package/.agents/backend/fm-hook-host-lib.sh +36 -0
  87. package/.agents/backend/fm-inactive-reconcile.sh +531 -0
  88. package/.agents/backend/fm-inbox.sh +399 -0
  89. package/.agents/backend/fm-install-actionlint.sh +84 -0
  90. package/.agents/backend/fm-install-herdr.sh +96 -0
  91. package/.agents/backend/fm-install-shellcheck.sh +84 -0
  92. package/.agents/backend/fm-install-treehouse.sh +95 -0
  93. package/.agents/backend/fm-kimi-turnend-hook.sh +276 -0
  94. package/.agents/backend/fm-lease-lib.sh +218 -0
  95. package/.agents/backend/fm-lease.sh +189 -0
  96. package/.agents/backend/fm-line-cap-lib.sh +51 -0
  97. package/.agents/backend/fm-lint-workflows.sh +137 -0
  98. package/.agents/backend/fm-lint.sh +591 -0
  99. package/.agents/backend/fm-lock-lib.sh +104 -0
  100. package/.agents/backend/fm-lock.sh +107 -0
  101. package/.agents/backend/fm-marker-lib.sh +12 -0
  102. package/.agents/backend/fm-merge-local.sh +74 -0
  103. package/.agents/backend/fm-merge-outcome-lib.sh +138 -0
  104. package/.agents/backend/fm-nm-run-lib.sh +124 -0
  105. package/.agents/backend/fm-on.sh +125 -0
  106. package/.agents/backend/fm-operational-input.sh +253 -0
  107. package/.agents/backend/fm-peek.sh +46 -0
  108. package/.agents/backend/fm-pending-reply-lib.sh +1409 -0
  109. package/.agents/backend/fm-pr-check.sh +135 -0
  110. package/.agents/backend/fm-pr-lib.sh +1016 -0
  111. package/.agents/backend/fm-pr-merge.sh +707 -0
  112. package/.agents/backend/fm-pr-poll.sh +110 -0
  113. package/.agents/backend/fm-primary-scope-lib.sh +33 -0
  114. package/.agents/backend/fm-procevent-extension-capture.pl +259 -0
  115. package/.agents/backend/fm-procevent-lavish.sh +628 -0
  116. package/.agents/backend/fm-procevent-lib.sh +892 -0
  117. package/.agents/backend/fm-procevent-quota.sh +290 -0
  118. package/.agents/backend/fm-procevent-remote-reply.sh +577 -0
  119. package/.agents/backend/fm-procevent-when.sh +504 -0
  120. package/.agents/backend/fm-procevent.sh +1466 -0
  121. package/.agents/backend/fm-project-mode.sh +92 -0
  122. package/.agents/backend/fm-project-origin-lib.sh +180 -0
  123. package/.agents/backend/fm-promote.sh +282 -0
  124. package/.agents/backend/fm-public-followup-emit.sh +260 -0
  125. package/.agents/backend/fm-public-followup-lib.sh +398 -0
  126. package/.agents/backend/fm-public-followup.sh +1330 -0
  127. package/.agents/backend/fm-push-transition-lib.sh +168 -0
  128. package/.agents/backend/fm-quota-axi-lib.sh +93 -0
  129. package/.agents/backend/fm-quota-choose.sh +384 -0
  130. package/.agents/backend/fm-remote-delta-read.sh +189 -0
  131. package/.agents/backend/fm-remote-doctor.sh +800 -0
  132. package/.agents/backend/fm-remote-entrypoint.sh +193 -0
  133. package/.agents/backend/fm-remote-file.sh +234 -0
  134. package/.agents/backend/fm-remote-home-provision.sh +259 -0
  135. package/.agents/backend/fm-remote-home-seed.sh +259 -0
  136. package/.agents/backend/fm-remote-inherit-push.sh +90 -0
  137. package/.agents/backend/fm-remote-inherit.sh +179 -0
  138. package/.agents/backend/fm-remote-job-lib.sh +1241 -0
  139. package/.agents/backend/fm-remote-job-reap-orphans.sh +137 -0
  140. package/.agents/backend/fm-remote-job-worker.sh +1124 -0
  141. package/.agents/backend/fm-remote-readiness-lib.sh +44 -0
  142. package/.agents/backend/fm-remote-secondmate-control.sh +340 -0
  143. package/.agents/backend/fm-review-diff.sh +158 -0
  144. package/.agents/backend/fm-secondmate-charter-lib.sh +44 -0
  145. package/.agents/backend/fm-secondmate-nudge-lib.sh +68 -0
  146. package/.agents/backend/fm-secondmate-parent-lib.sh +70 -0
  147. package/.agents/backend/fm-secondmate-reconcile.sh +371 -0
  148. package/.agents/backend/fm-secondmate-registry-lib.sh +311 -0
  149. package/.agents/backend/fm-secondmate-report.sh +88 -0
  150. package/.agents/backend/fm-send.sh +1059 -0
  151. package/.agents/backend/fm-session-lock-lib.sh +176 -0
  152. package/.agents/backend/fm-session-start.sh +970 -0
  153. package/.agents/backend/fm-sessionstart-cursor.sh +40 -0
  154. package/.agents/backend/fm-sessionstart-nudge.sh +45 -0
  155. package/.agents/backend/fm-sessionstart-run.sh +145 -0
  156. package/.agents/backend/fm-spawn.sh +3153 -0
  157. package/.agents/backend/fm-startup-memory-budget-lib.sh +224 -0
  158. package/.agents/backend/fm-startup-memory-budget.sh +94 -0
  159. package/.agents/backend/fm-startup-network.sh +642 -0
  160. package/.agents/backend/fm-stow-cascade.sh +251 -0
  161. package/.agents/backend/fm-subagent-pretool-check.sh +207 -0
  162. package/.agents/backend/fm-supervise-daemon.sh +1732 -0
  163. package/.agents/backend/fm-supervision-instructions.sh +215 -0
  164. package/.agents/backend/fm-supervision-lib.sh +90 -0
  165. package/.agents/backend/fm-supervisor-target-lib.sh +78 -0
  166. package/.agents/backend/fm-tangle-lib.sh +53 -0
  167. package/.agents/backend/fm-task-inbox-lib.sh +402 -0
  168. package/.agents/backend/fm-tasks-axi-lib.sh +123 -0
  169. package/.agents/backend/fm-teardown.sh +2922 -0
  170. package/.agents/backend/fm-test-isolation-proof.sh +588 -0
  171. package/.agents/backend/fm-test-run.sh +2191 -0
  172. package/.agents/backend/fm-timeout-lib.sh +141 -0
  173. package/.agents/backend/fm-timing-lib.sh +179 -0
  174. package/.agents/backend/fm-tmux-lib.sh +291 -0
  175. package/.agents/backend/fm-tool-update-check.sh +898 -0
  176. package/.agents/backend/fm-trace-context-lib.sh +227 -0
  177. package/.agents/backend/fm-transition-lib.sh +103 -0
  178. package/.agents/backend/fm-turnend-guard-cursor.sh +391 -0
  179. package/.agents/backend/fm-turnend-guard-grok.sh +90 -0
  180. package/.agents/backend/fm-turnend-guard.sh +432 -0
  181. package/.agents/backend/fm-update.sh +110 -0
  182. package/.agents/backend/fm-vendor-auth-probe.sh +190 -0
  183. package/.agents/backend/fm-voice-client.py +1373 -0
  184. package/.agents/backend/fm-voice-relay.py +1256 -0
  185. package/.agents/backend/fm-wake-drain.sh +592 -0
  186. package/.agents/backend/fm-wake-grant.sh +127 -0
  187. package/.agents/backend/fm-wake-lib.sh +1849 -0
  188. package/.agents/backend/fm-watch-arm.sh +602 -0
  189. package/.agents/backend/fm-watch-checkpoint.sh +109 -0
  190. package/.agents/backend/fm-watch.sh +1962 -0
  191. package/.agents/backend/fm-x-dismiss.sh +116 -0
  192. package/.agents/backend/fm-x-followup.sh +286 -0
  193. package/.agents/backend/fm-x-lib.sh +1000 -0
  194. package/.agents/backend/fm-x-link.sh +234 -0
  195. package/.agents/backend/fm-x-poll.sh +185 -0
  196. package/.agents/backend/fm-x-reply.sh +385 -0
  197. package/.agents/backend/fm_voice_frame.py +166 -0
  198. package/.agents/backend/fm_voice_records.py +574 -0
  199. package/.agents/backend/model-bridge.py +360 -0
  200. package/.agents/backend/opencode-go-bridge.py +30 -0
  201. package/.agents/bus/README.md +12 -0
  202. package/.agents/bus/messages.json +1 -0
  203. package/.agents/config/agents.machine.example.yaml +16 -0
  204. package/.agents/config/agents.yaml.example +72 -0
  205. package/.agents/config/crew-dispatch.json +22 -0
  206. package/.agents/config/cron.yaml +11 -0
  207. package/.agents/config/eindri-dispatch.json +67 -0
  208. package/.agents/config/eindri-harness +1 -0
  209. package/.agents/config/startup-memory-budget +1 -0
  210. package/.agents/filebrowser/.filebrowser.json +6 -0
  211. package/.agents/filebrowser/README.md +11 -0
  212. package/.agents/gateway/README.md +12 -0
  213. package/.agents/github/README.md +15 -0
  214. package/.agents/github/webhooks/.gitkeep +0 -0
  215. package/.agents/github/workflows/.gitkeep +0 -0
  216. package/.agents/harness/opencode/plugins/lib/rodd-operational-input.js +37 -0
  217. package/.agents/harness/opencode/plugins/package.json +4 -0
  218. package/.agents/harness/opencode/plugins/saga-sessionstart.js +77 -0
  219. package/.agents/harness/opencode/plugins/syn-cd-check.js +61 -0
  220. package/.agents/harness/opencode/plugins/syn-pretool-check.js +68 -0
  221. package/.agents/harness/opencode/plugins/syn-turnend-guard.js +105 -0
  222. package/.agents/harness/opencode/plugins/syn-watch-arm.js +521 -0
  223. package/.agents/memory/README.md +46 -0
  224. package/.agents/memory/runes_audit.md +5 -0
  225. package/.agents/migrations/0001-hodd-layout.sh +38 -0
  226. package/.agents/migrations/0002-a2a-mcp.sh +27 -0
  227. package/.agents/migrations/0003-private-data-separation.sh +121 -0
  228. package/.agents/migrations/0004-hoard-and-realms.sh +104 -0
  229. package/.agents/migrations/0005-roots-out-of-tree.sh +158 -0
  230. package/.agents/sandbox/Dockerfile.utgard +19 -0
  231. package/.agents/sandbox/README.md +14 -0
  232. package/.agents/sandbox/utgard.config.json +14 -0
  233. package/.agents/skills/README.md +59 -0
  234. package/.agents/skills/bragi-marketing/README.md +3 -0
  235. package/.agents/skills/bragi-marketing/SKILL.md +82 -0
  236. package/.agents/skills/eindri-homes/README.md +3 -0
  237. package/.agents/skills/eindri-homes/SKILL.md +291 -0
  238. package/.agents/skills/eindri-homes/assets/control-plane.md +122 -0
  239. package/.agents/skills/eindri-homes/assets/dispatch.md +80 -0
  240. package/.agents/skills/frigg-consent/README.md +3 -0
  241. package/.agents/skills/frigg-consent/SKILL.md +56 -0
  242. package/.agents/skills/galdr-ymirsystem/README.md +3 -0
  243. package/.agents/skills/galdr-ymirsystem/SKILL.md +159 -0
  244. package/.agents/skills/galdr-ymirsystem/assets/README.md +74 -0
  245. package/.agents/skills/galdr-ymirsystem/assets/brokk-distro-runtime.md +390 -0
  246. package/.agents/skills/galdr-ymirsystem/assets/build-method.md +113 -0
  247. package/.agents/skills/galdr-ymirsystem/assets/build-tool-categories.md +178 -0
  248. package/.agents/skills/galdr-ymirsystem/assets/eindri-orchestration.md +518 -0
  249. package/.agents/skills/galdr-ymirsystem/assets/eindri-profiles.md +136 -0
  250. package/.agents/skills/galdr-ymirsystem/assets/harness-integration/README.md +736 -0
  251. package/.agents/skills/galdr-ymirsystem/assets/harness-integration/claude-code.md +197 -0
  252. package/.agents/skills/galdr-ymirsystem/assets/harness-integration/codex.md +191 -0
  253. package/.agents/skills/galdr-ymirsystem/assets/harness-integration/cursor.md +193 -0
  254. package/.agents/skills/galdr-ymirsystem/assets/harness-integration/opencode.md +262 -0
  255. package/.agents/skills/galdr-ymirsystem/assets/harness-integration/pi.md +295 -0
  256. package/.agents/skills/galdr-ymirsystem/assets/hlidskjalf-ui.md +539 -0
  257. package/.agents/skills/galdr-ymirsystem/assets/installation.md +763 -0
  258. package/.agents/skills/galdr-ymirsystem/assets/local-models/HOST-DATA.example.md +72 -0
  259. package/.agents/skills/galdr-ymirsystem/assets/local-models.md +274 -0
  260. package/.agents/skills/galdr-ymirsystem/assets/memory-well.md +163 -0
  261. package/.agents/skills/galdr-ymirsystem/assets/nornir-jobs.md +415 -0
  262. package/.agents/skills/galdr-ymirsystem/assets/norse-naming.md +325 -0
  263. package/.agents/skills/galdr-ymirsystem/assets/odrerir-hall.md +133 -0
  264. package/.agents/skills/galdr-ymirsystem/assets/pi-boot/einherjar-spawn.schema.json +81 -0
  265. package/.agents/skills/galdr-ymirsystem/assets/pi-boot/herdr-profile.toml +75 -0
  266. package/.agents/skills/galdr-ymirsystem/assets/pi-boot/pi-profile.yml +101 -0
  267. package/.agents/skills/galdr-ymirsystem/assets/pi-boot/supervision-tree.yml +163 -0
  268. package/.agents/skills/galdr-ymirsystem/assets/pi-boot-guide.md +58 -0
  269. package/.agents/skills/galdr-ymirsystem/assets/porting-upstream-to-norse.md +301 -0
  270. package/.agents/skills/galdr-ymirsystem/assets/principles.md +385 -0
  271. package/.agents/skills/galdr-ymirsystem/assets/reference-adoption.md +99 -0
  272. package/.agents/skills/galdr-ymirsystem/assets/registry.md +379 -0
  273. package/.agents/skills/galdr-ymirsystem/assets/runtime-compliance.md +463 -0
  274. package/.agents/skills/galdr-ymirsystem/assets/runtime-components.md +553 -0
  275. package/.agents/skills/galdr-ymirsystem/assets/smidja.md +180 -0
  276. package/.agents/skills/galdr-ymirsystem/schemas/toon-schemas.md +190 -0
  277. package/.agents/skills/galdr-ymirsystem/scripts/README.md +45 -0
  278. package/.agents/skills/galdr-ymirsystem/scripts/bench-one.sh +248 -0
  279. package/.agents/skills/galdr-ymirsystem/scripts/compliance-check.sh +503 -0
  280. package/.agents/skills/galdr-ymirsystem/scripts/gpu-sample.sh +43 -0
  281. package/.agents/skills/galdr-ymirsystem/scripts/stop-all.sh +34 -0
  282. package/.agents/skills/galdr-ymirsystem/scripts/toon-check.py +188 -0
  283. package/.agents/skills/gjallarhorn-relay/README.md +3 -0
  284. package/.agents/skills/gjallarhorn-relay/SKILL.md +283 -0
  285. package/.agents/skills/groa-update/SKILL.md +90 -0
  286. package/.agents/skills/hamr-adapters/README.md +3 -0
  287. package/.agents/skills/hamr-adapters/SKILL.md +123 -0
  288. package/.agents/skills/hamr-adapters/references/common/control-and-recovery.md +37 -0
  289. package/.agents/skills/hamr-adapters/references/common/dispatch.md +32 -0
  290. package/.agents/skills/hamr-adapters/references/common/model-and-effort.md +42 -0
  291. package/.agents/skills/hamr-adapters/references/common/primary-hooks.md +40 -0
  292. package/.agents/skills/hamr-adapters/references/harness/claude.md +55 -0
  293. package/.agents/skills/hamr-adapters/references/harness/codex.md +43 -0
  294. package/.agents/skills/hamr-adapters/references/harness/cursor.md +75 -0
  295. package/.agents/skills/hamr-adapters/references/harness/grok.md +69 -0
  296. package/.agents/skills/hamr-adapters/references/harness/kimi.md +51 -0
  297. package/.agents/skills/hamr-adapters/references/harness/muse.md +70 -0
  298. package/.agents/skills/hamr-adapters/references/harness/opencode.md +42 -0
  299. package/.agents/skills/hamr-adapters/references/harness/pi.md +56 -0
  300. package/.agents/skills/herdr-panes/README.md +3 -0
  301. package/.agents/skills/herdr-panes/SKILL.md +92 -0
  302. package/.agents/skills/herdr-panes/assets/herdr-backend.md +346 -0
  303. package/.agents/skills/herdr-panes/assets/tmux-backend.md +115 -0
  304. package/.agents/skills/hnoss-design/DESIGN.md +19 -0
  305. package/.agents/skills/hnoss-design/README.md +3 -0
  306. package/.agents/skills/hnoss-design/SKILL.md +83 -0
  307. package/.agents/skills/hvild-afk/README.md +3 -0
  308. package/.agents/skills/hvild-afk/SKILL.md +241 -0
  309. package/.agents/skills/jord-projects/README.md +3 -0
  310. package/.agents/skills/jord-projects/SKILL.md +92 -0
  311. package/.agents/skills/lifecycle/SKILL.md +56 -0
  312. package/.agents/skills/lifecycle/smoke_test.sh +57 -0
  313. package/.agents/skills/lifecycle/start.sh +21 -0
  314. package/.agents/skills/lifecycle/status.sh +35 -0
  315. package/.agents/skills/lifecycle/stop.sh +16 -0
  316. package/.agents/skills/muninn-stow/README.md +3 -0
  317. package/.agents/skills/muninn-stow/SKILL.md +309 -0
  318. package/.agents/skills/no-mistakes/SKILL.md +379 -0
  319. package/.agents/skills/nornir-schedule/README.md +3 -0
  320. package/.agents/skills/nornir-schedule/SKILL.md +25 -0
  321. package/.agents/skills/nornir-schedule/assets/events.md +137 -0
  322. package/.agents/skills/nornir-schedule/assets/quota.md +131 -0
  323. package/.agents/skills/nsr-compliance/README.md +3 -0
  324. package/.agents/skills/nsr-compliance/SKILL.md +29 -0
  325. package/.agents/skills/nsr-compliance/assets/nsr/README.md +40 -0
  326. package/.agents/skills/nsr-compliance/assets/nsr/assets/RULES/README.md +15 -0
  327. package/.agents/skills/nsr-compliance/assets/nsr/assets/RULES/git-workflow.md +18 -0
  328. package/.agents/skills/nsr-compliance/assets/nsr/assets/RULES/security.md +22 -0
  329. package/.agents/skills/nsr-compliance/assets/nsr/assets/RULES/tech-stack.md +8 -0
  330. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/SKILL.md +71 -0
  331. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/config/core_four.yaml +16 -0
  332. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/gates/check_danger.sh +102 -0
  333. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/gates/check_wiring.sh +69 -0
  334. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/gates/validate_code.sh +29 -0
  335. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/gates/verify_docs.py +66 -0
  336. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/generate.sh +109 -0
  337. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/harness/envelopes/result_envelope.json +14 -0
  338. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/harness/envelopes/task_envelope.json +20 -0
  339. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/harness/runner.py +303 -0
  340. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/telemetry/logger.py +112 -0
  341. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/README.md +18 -0
  342. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/config/README.md +10 -0
  343. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/gates/README.md +12 -0
  344. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/gates/check_env.sh +23 -0
  345. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/gates/check_paths.sh +20 -0
  346. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/gates/check_platform.sh +23 -0
  347. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/harness/README.md +11 -0
  348. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/harness/envelopes/README.md +10 -0
  349. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/installer/README.md +10 -0
  350. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/installer/install.sh +125 -0
  351. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/telemetry/README.md +11 -0
  352. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/README.md +20 -0
  353. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/features/README.md +36 -0
  354. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/features/SKILL-template.md +14 -0
  355. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/git_ops/README.md +25 -0
  356. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/git_ops/SKILL.md +13 -0
  357. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/git_ops/create_branch.sh +6 -0
  358. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/git_ops/safe_commit.sh +9 -0
  359. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/git_ops/sync_upstream.sh +6 -0
  360. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/lifecycle/README.md +27 -0
  361. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/lifecycle/SKILL.md +15 -0
  362. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/lifecycle/smoke_test.sh +12 -0
  363. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/lifecycle/start.sh +19 -0
  364. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/lifecycle/status.sh +12 -0
  365. package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/lifecycle/stop.sh +15 -0
  366. package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/README.md +18 -0
  367. package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/config/README.md +10 -0
  368. package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/config/core_four.yaml +13 -0
  369. package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/gates/README.md +12 -0
  370. package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/gates/check_danger.sh +102 -0
  371. package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/gates/check_env.sh +23 -0
  372. package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/gates/check_paths.sh +20 -0
  373. package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/gates/check_platform.sh +23 -0
  374. package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/gates/check_wiring.sh +69 -0
  375. package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/harness/README.md +11 -0
  376. package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/harness/envelopes/README.md +10 -0
  377. package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/harness/envelopes/result_envelope.json +14 -0
  378. package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/harness/envelopes/task_envelope.json +20 -0
  379. package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/harness/runner.py +303 -0
  380. package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/installer/README.md +10 -0
  381. package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/installer/install.sh +125 -0
  382. package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/telemetry/README.md +11 -0
  383. package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/telemetry/logger.py +112 -0
  384. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/BEST_PRACTICES/README.md +14 -0
  385. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/BEST_PRACTICES/api.md +17 -0
  386. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/BEST_PRACTICES/compliance-wiring.md +155 -0
  387. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/BEST_PRACTICES/frontend.md +15 -0
  388. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/README.md +11 -0
  389. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/deployment/README.md +23 -0
  390. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/deployment/clients/README.md +16 -0
  391. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/deployment/clients/template-README.md +11 -0
  392. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/deployment/deploy.sh +49 -0
  393. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/deployment/envs/README.md +12 -0
  394. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/deployment/envs/development.env.example +17 -0
  395. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/deployment/envs/production.env.example +17 -0
  396. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/deployment/envs/staging.env.example +17 -0
  397. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/deployment/tenants/README.md +11 -0
  398. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/deployment/tenants/template.env.example +8 -0
  399. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/DEVELOPER_SETUP/README.md +26 -0
  400. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/DEVELOPER_SETUP/agent.md +7 -0
  401. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/DEVELOPER_SETUP/developers/dev-1.md +20 -0
  402. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/DEVELOPER_SETUP/linux.md +9 -0
  403. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/DEVELOPER_SETUP/macos.md +10 -0
  404. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/DEVELOPER_SETUP/shared.md +19 -0
  405. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/DEVELOPER_SETUP/windows.md +14 -0
  406. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/HOSTING/README.md +25 -0
  407. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/HOSTING/_template.md +41 -0
  408. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/HOSTING/hosting-1.md +41 -0
  409. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/README.md +21 -0
  410. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/RUNBOOK/README.md +15 -0
  411. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/RUNBOOK/deployment.md +19 -0
  412. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/RUNBOOK/disaster-recovery.md +15 -0
  413. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/RUNBOOK/ops-tasks.md +14 -0
  414. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/features/README.md +16 -0
  415. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/project-skills.md +146 -0
  416. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/research/README.md +11 -0
  417. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/research/software-compliance-research.md +269 -0
  418. package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/working-with-agents.md +260 -0
  419. package/.agents/skills/nsr-compliance/assets/nsr/assets/root/AGENTS.md +38 -0
  420. package/.agents/skills/nsr-compliance/assets/nsr/assets/root/ARCHITECTURE.md +36 -0
  421. package/.agents/skills/nsr-compliance/assets/nsr/assets/root/BEST_PRACTICES.md +27 -0
  422. package/.agents/skills/nsr-compliance/assets/nsr/assets/root/CI_CD.md +23 -0
  423. package/.agents/skills/nsr-compliance/assets/nsr/assets/root/FEATURES.md +15 -0
  424. package/.agents/skills/nsr-compliance/assets/nsr/assets/root/README.md +22 -0
  425. package/.agents/skills/nsr-compliance/assets/nsr/assets/root/RULES.md +37 -0
  426. package/.agents/skills/nsr-compliance/assets/nsr/assets/root/STRUCTURE.md +45 -0
  427. package/.agents/skills/nsr-compliance/assets/nsr/assets/root/TECH_STACK.md +28 -0
  428. package/.agents/skills/nsr-compliance/assets/nsr/scaffold-spec.md +63 -0
  429. package/.agents/skills/nsr-compliance/assets/nsr/scripts/scaffold.sh +103 -0
  430. package/.agents/skills/nsr-compliance/assets/nsr/scripts/validate.sh +52 -0
  431. package/.agents/skills/nsr-compliance/assets/nsrcompliance/SKILL.md +71 -0
  432. package/.agents/skills/nsr-compliance/assets/nsrcompliance/config/core_four.yaml +16 -0
  433. package/.agents/skills/nsr-compliance/assets/nsrcompliance/gates/check_danger.sh +102 -0
  434. package/.agents/skills/nsr-compliance/assets/nsrcompliance/gates/check_wiring.sh +69 -0
  435. package/.agents/skills/nsr-compliance/assets/nsrcompliance/gates/validate_code.sh +29 -0
  436. package/.agents/skills/nsr-compliance/assets/nsrcompliance/gates/verify_docs.py +66 -0
  437. package/.agents/skills/nsr-compliance/assets/nsrcompliance/generate.sh +109 -0
  438. package/.agents/skills/nsr-compliance/assets/nsrcompliance/harness/envelopes/result_envelope.json +14 -0
  439. package/.agents/skills/nsr-compliance/assets/nsrcompliance/harness/envelopes/task_envelope.json +20 -0
  440. package/.agents/skills/nsr-compliance/assets/nsrcompliance/harness/runner.py +303 -0
  441. package/.agents/skills/nsr-compliance/assets/nsrcompliance/telemetry/logger.py +112 -0
  442. package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/README.md +18 -0
  443. package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/config/README.md +10 -0
  444. package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/gates/README.md +12 -0
  445. package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/gates/check_env.sh +23 -0
  446. package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/gates/check_paths.sh +20 -0
  447. package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/gates/check_platform.sh +23 -0
  448. package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/harness/README.md +11 -0
  449. package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/harness/envelopes/README.md +10 -0
  450. package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/installer/README.md +10 -0
  451. package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/installer/install.sh +125 -0
  452. package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/telemetry/README.md +11 -0
  453. package/.agents/skills/pr-ops/README.md +3 -0
  454. package/.agents/skills/pr-ops/SKILL.md +109 -0
  455. package/.agents/skills/ratatoskr-a2a/README.md +3 -0
  456. package/.agents/skills/ratatoskr-a2a/SKILL.md +148 -0
  457. package/.agents/skills/rules-check-drift/SKILL.md +81 -0
  458. package/.agents/skills/saga-bearings/README.md +3 -0
  459. package/.agents/skills/saga-bearings/SKILL.md +26 -0
  460. package/.agents/skills/saga-bearings/assets/bearings.md +161 -0
  461. package/.agents/skills/saga-bearings/assets/board-template.html +731 -0
  462. package/.agents/skills/saga-bearings/assets/recap.md +56 -0
  463. package/.agents/skills/syn-recovery/README.md +3 -0
  464. package/.agents/skills/syn-recovery/SKILL.md +54 -0
  465. package/.agents/skills/tyr-check/README.md +3 -0
  466. package/.agents/skills/tyr-check/SKILL.md +218 -0
  467. package/.agents/skills/urdh-hold/README.md +3 -0
  468. package/.agents/skills/urdh-hold/SKILL.md +25 -0
  469. package/.agents/skills/urdh-hold/assets/decisions.md +15 -0
  470. package/.agents/skills/urdh-hold/assets/hold.md +54 -0
  471. package/.agents/skills/vor-diagnostics/README.md +3 -0
  472. package/.agents/skills/vor-diagnostics/SKILL.md +68 -0
  473. package/.agents/skills/ymir-host/README.md +3 -0
  474. package/.agents/skills/ymir-host/SKILL.md +35 -0
  475. package/.agents/skills/ymir-host/assets/desktop.md +36 -0
  476. package/.agents/skills/ymir-host/assets/install.md +69 -0
  477. package/.agents/skills/ymir-host/assets/omarchy.md +297 -0
  478. package/.agents/skills/ymir-host/assets/runbooks.md +15 -0
  479. package/.agents/skills/ymir-host/assets/thjazi.md +220 -0
  480. package/.agents/skills/ymir-host/assets/update.md +64 -0
  481. package/.agents/tests/assets/board-render-harness.mjs +117 -0
  482. package/.agents/tests/cmux-test-safety.sh +51 -0
  483. package/.agents/tests/fixtures.sh +291 -0
  484. package/.agents/tests/fm-afk-inject-e2e.test.sh +428 -0
  485. package/.agents/tests/fm-afk-inject-herdr-e2e.test.sh +536 -0
  486. package/.agents/tests/fm-afk-launch.test.sh +961 -0
  487. package/.agents/tests/fm-afk-pi-herdr-return-e2e.test.sh +297 -0
  488. package/.agents/tests/fm-afk-return.test.sh +283 -0
  489. package/.agents/tests/fm-arm-pretool-check.test.sh +482 -0
  490. package/.agents/tests/fm-ask-user-authority.test.sh +42 -0
  491. package/.agents/tests/fm-backend-autodetect-smoke.test.sh +176 -0
  492. package/.agents/tests/fm-backend-cmux-smoke.test.sh +188 -0
  493. package/.agents/tests/fm-backend-cmux.test.sh +1166 -0
  494. package/.agents/tests/fm-backend-herdr-eventwait-smoke.test.sh +136 -0
  495. package/.agents/tests/fm-backend-herdr-eventwait.test.py +106 -0
  496. package/.agents/tests/fm-backend-herdr-focus-flash-e2e.test.sh +409 -0
  497. package/.agents/tests/fm-backend-herdr-launcher-workspace-e2e.test.sh +437 -0
  498. package/.agents/tests/fm-backend-herdr-presentation-e2e.test.sh +1424 -0
  499. package/.agents/tests/fm-backend-herdr-prune-safety-e2e.test.sh +180 -0
  500. package/.agents/tests/fm-backend-herdr-respawn-idem-e2e.test.sh +182 -0
  501. package/.agents/tests/fm-backend-herdr-smoke.test.sh +355 -0
  502. package/.agents/tests/fm-backend-herdr-workspace-per-home-e2e.test.sh +251 -0
  503. package/.agents/tests/fm-backend-herdr.test.sh +4606 -0
  504. package/.agents/tests/fm-backend-orca.test.sh +1357 -0
  505. package/.agents/tests/fm-backend-tmux-smoke.test.sh +173 -0
  506. package/.agents/tests/fm-backend-zellij-smoke.test.sh +205 -0
  507. package/.agents/tests/fm-backend-zellij.test.sh +1360 -0
  508. package/.agents/tests/fm-backend.test.sh +1142 -0
  509. package/.agents/tests/fm-backlog-atomicity.test.sh +2302 -0
  510. package/.agents/tests/fm-backlog-handoff.test.sh +1360 -0
  511. package/.agents/tests/fm-bearings-board-render.test.sh +135 -0
  512. package/.agents/tests/fm-bearings-board.test.sh +414 -0
  513. package/.agents/tests/fm-bearings-snapshot.test.sh +1990 -0
  514. package/.agents/tests/fm-bootstrap-network-parallel.test.sh +316 -0
  515. package/.agents/tests/fm-bootstrap.test.sh +1178 -0
  516. package/.agents/tests/fm-branch-supervision.test.sh +552 -0
  517. package/.agents/tests/fm-brief.test.sh +785 -0
  518. package/.agents/tests/fm-busy-adapter-wiring.test.sh +324 -0
  519. package/.agents/tests/fm-busy-state.test.sh +465 -0
  520. package/.agents/tests/fm-calm-pi-extension.test.sh +3979 -0
  521. package/.agents/tests/fm-captain-hold-lifecycle.test.sh +1187 -0
  522. package/.agents/tests/fm-cd-pretool-check.test.sh +400 -0
  523. package/.agents/tests/fm-check-unregister.test.sh +198 -0
  524. package/.agents/tests/fm-classify-corr-token.test.sh +543 -0
  525. package/.agents/tests/fm-classify-decision-key.test.sh +340 -0
  526. package/.agents/tests/fm-claude-stop-autoarm-live-e2e.test.sh +164 -0
  527. package/.agents/tests/fm-claude-stop-autoarm.test.sh +1190 -0
  528. package/.agents/tests/fm-cmux-claude-composer-live-e2e.test.sh +110 -0
  529. package/.agents/tests/fm-codex-continuity-live-e2e.test.sh +55 -0
  530. package/.agents/tests/fm-composer-ghost.test.sh +716 -0
  531. package/.agents/tests/fm-composer-lib.test.sh +666 -0
  532. package/.agents/tests/fm-composer-matrix-live-e2e.test.sh +220 -0
  533. package/.agents/tests/fm-control-herdr-smoke.test.sh +149 -0
  534. package/.agents/tests/fm-control-relaunch.test.sh +1530 -0
  535. package/.agents/tests/fm-control.test.sh +910 -0
  536. package/.agents/tests/fm-crew-state.test.sh +1610 -0
  537. package/.agents/tests/fm-cursor-harness.test.sh +404 -0
  538. package/.agents/tests/fm-cursor-primary-live-e2e.test.sh +217 -0
  539. package/.agents/tests/fm-cursor-primary.test.sh +708 -0
  540. package/.agents/tests/fm-daemon.test.sh +2740 -0
  541. package/.agents/tests/fm-documentation-audiences.test.sh +141 -0
  542. package/.agents/tests/fm-ensure-agents-md.test.sh +371 -0
  543. package/.agents/tests/fm-extension-binding.test.sh +2187 -0
  544. package/.agents/tests/fm-fleet-snapshot-view.test.sh +816 -0
  545. package/.agents/tests/fm-fleet-sync.test.sh +721 -0
  546. package/.agents/tests/fm-gate-refuse.test.sh +356 -0
  547. package/.agents/tests/fm-gitignore-config.test.sh +89 -0
  548. package/.agents/tests/fm-gotmp.test.sh +230 -0
  549. package/.agents/tests/fm-grok-continuity-live-e2e.test.sh +112 -0
  550. package/.agents/tests/fm-grok-harness.test.sh +117 -0
  551. package/.agents/tests/fm-grok-stop-live-e2e.test.sh +219 -0
  552. package/.agents/tests/fm-guard-stale-banner.test.sh +709 -0
  553. package/.agents/tests/fm-harness-adapter-instructions-live-e2e.test.sh +134 -0
  554. package/.agents/tests/fm-harness-adapter-references.test.sh +30 -0
  555. package/.agents/tests/fm-harness-liveness-drift-live-e2e.test.sh +151 -0
  556. package/.agents/tests/fm-herdr-lab.test.sh +243 -0
  557. package/.agents/tests/fm-herdr-session-cleanup-e2e.test.sh +146 -0
  558. package/.agents/tests/fm-herdr-session-cleanup.test.sh +329 -0
  559. package/.agents/tests/fm-herdr-submit-confirm-live-e2e.test.sh +125 -0
  560. package/.agents/tests/fm-herdr-version-floor-live-e2e.test.sh +144 -0
  561. package/.agents/tests/fm-home-summary-refresh.test.sh +971 -0
  562. package/.agents/tests/fm-inactive-reconcile.test.sh +485 -0
  563. package/.agents/tests/fm-kimi-harness.test.sh +687 -0
  564. package/.agents/tests/fm-lint-workflows.test.sh +532 -0
  565. package/.agents/tests/fm-lint.test.sh +1023 -0
  566. package/.agents/tests/fm-muse-harness.test.sh +932 -0
  567. package/.agents/tests/fm-muse-signals-live-e2e.test.sh +205 -0
  568. package/.agents/tests/fm-no-mistakes-required.test.sh +72 -0
  569. package/.agents/tests/fm-on.test.sh +507 -0
  570. package/.agents/tests/fm-opencode-primary-live-e2e.test.sh +357 -0
  571. package/.agents/tests/fm-operational-input.test.sh +160 -0
  572. package/.agents/tests/fm-peek-remote.test.sh +110 -0
  573. package/.agents/tests/fm-pending-reply.test.sh +1288 -0
  574. package/.agents/tests/fm-pi-branch-extension.test.sh +3182 -0
  575. package/.agents/tests/fm-pi-branch-live-e2e.test.sh +527 -0
  576. package/.agents/tests/fm-pi-primary-live-e2e.test.sh +345 -0
  577. package/.agents/tests/fm-pi-primary-types.test.sh +68 -0
  578. package/.agents/tests/fm-pi-watch-extension.test.sh +2843 -0
  579. package/.agents/tests/fm-pr-check-security.test.sh +2140 -0
  580. package/.agents/tests/fm-pr-merge.test.sh +2135 -0
  581. package/.agents/tests/fm-procevent-quota.test.sh +225 -0
  582. package/.agents/tests/fm-procevent-when.test.sh +406 -0
  583. package/.agents/tests/fm-procevent.test.sh +1745 -0
  584. package/.agents/tests/fm-project-origin.test.sh +110 -0
  585. package/.agents/tests/fm-public-followup.test.sh +2272 -0
  586. package/.agents/tests/fm-quota-array-dispatch-live-e2e.test.sh +526 -0
  587. package/.agents/tests/fm-quota-choose.test.sh +626 -0
  588. package/.agents/tests/fm-remote-backlog-handoff.test.sh +453 -0
  589. package/.agents/tests/fm-remote-doctor.test.sh +625 -0
  590. package/.agents/tests/fm-remote-entrypoint.test.sh +59 -0
  591. package/.agents/tests/fm-remote-job-orphan-reap.test.sh +205 -0
  592. package/.agents/tests/fm-remote-job.test.sh +768 -0
  593. package/.agents/tests/fm-remote-reply.test.sh +520 -0
  594. package/.agents/tests/fm-remote-secondmate-lifecycle-e2e.test.sh +1256 -0
  595. package/.agents/tests/fm-remote-secondmate-parent-binding.test.sh +326 -0
  596. package/.agents/tests/fm-remote-secondmate-trace-context.test.sh +310 -0
  597. package/.agents/tests/fm-remote-transport-lanes.test.sh +431 -0
  598. package/.agents/tests/fm-review-diff.test.sh +176 -0
  599. package/.agents/tests/fm-secondmate-harness.test.sh +2600 -0
  600. package/.agents/tests/fm-secondmate-lifecycle-e2e.test.sh +259 -0
  601. package/.agents/tests/fm-secondmate-liveness.test.sh +559 -0
  602. package/.agents/tests/fm-secondmate-reconcile.test.sh +732 -0
  603. package/.agents/tests/fm-secondmate-safety.test.sh +2972 -0
  604. package/.agents/tests/fm-secondmate-sync.test.sh +913 -0
  605. package/.agents/tests/fm-send-inbox-doorbell-live-e2e.test.sh +206 -0
  606. package/.agents/tests/fm-send-inbox.test.sh +352 -0
  607. package/.agents/tests/fm-send-popup-settle.test.sh +167 -0
  608. package/.agents/tests/fm-send-remote-delivery.test.sh +796 -0
  609. package/.agents/tests/fm-send-resolve-key.test.sh +551 -0
  610. package/.agents/tests/fm-send-secondmate-marker-herdr-e2e.test.sh +187 -0
  611. package/.agents/tests/fm-send-secondmate-marker.test.sh +278 -0
  612. package/.agents/tests/fm-send-settle.test.sh +147 -0
  613. package/.agents/tests/fm-send-strict.test.sh +241 -0
  614. package/.agents/tests/fm-session-lock-ancestry.test.sh +365 -0
  615. package/.agents/tests/fm-session-start.test.sh +2515 -0
  616. package/.agents/tests/fm-sessionstart-hook-live-e2e.test.sh +631 -0
  617. package/.agents/tests/fm-sessionstart-instruction-refresh-live-e2e.test.sh +230 -0
  618. package/.agents/tests/fm-sessionstart-nudge.test.sh +1039 -0
  619. package/.agents/tests/fm-shared-captain-inheritance.test.sh +404 -0
  620. package/.agents/tests/fm-spawn-batch.test.sh +148 -0
  621. package/.agents/tests/fm-spawn-dispatch-profile.test.sh +827 -0
  622. package/.agents/tests/fm-spawn-pool-base-freshen.test.sh +440 -0
  623. package/.agents/tests/fm-spawn-worktree-settle.test.sh +147 -0
  624. package/.agents/tests/fm-startup-memory-budget.test.sh +333 -0
  625. package/.agents/tests/fm-startup-network.test.sh +712 -0
  626. package/.agents/tests/fm-stow-cascade.test.sh +370 -0
  627. package/.agents/tests/fm-subagent-pretool-check.test.sh +291 -0
  628. package/.agents/tests/fm-supervision-events.test.sh +156 -0
  629. package/.agents/tests/fm-supervision-instructions.test.sh +189 -0
  630. package/.agents/tests/fm-tangle-guard.test.sh +277 -0
  631. package/.agents/tests/fm-task-delivery.test.sh +420 -0
  632. package/.agents/tests/fm-task-inbox.test.sh +513 -0
  633. package/.agents/tests/fm-teardown-endpoint-safety.test.sh +374 -0
  634. package/.agents/tests/fm-teardown.test.sh +2665 -0
  635. package/.agents/tests/fm-test-fixture-cleanup.test.sh +172 -0
  636. package/.agents/tests/fm-test-fixtures.test.sh +132 -0
  637. package/.agents/tests/fm-test-isolation-proof.test.sh +278 -0
  638. package/.agents/tests/fm-test-run.test.sh +1196 -0
  639. package/.agents/tests/fm-tmux-agent-liveness.test.sh +354 -0
  640. package/.agents/tests/fm-tmux-submit-busy.test.sh +340 -0
  641. package/.agents/tests/fm-tool-update-check.test.sh +1044 -0
  642. package/.agents/tests/fm-trace-context-lib.test.sh +253 -0
  643. package/.agents/tests/fm-trace-context-spawn.test.sh +581 -0
  644. package/.agents/tests/fm-transition-lib.test.sh +54 -0
  645. package/.agents/tests/fm-turnend-guard.test.sh +1822 -0
  646. package/.agents/tests/fm-update.test.sh +304 -0
  647. package/.agents/tests/fm-vendor-auth-probe.test.sh +395 -0
  648. package/.agents/tests/fm-voice-relay.test.sh +4741 -0
  649. package/.agents/tests/fm-wake-daemon-lifecycle-e2e.test.sh +171 -0
  650. package/.agents/tests/fm-wake-drain-open-decisions-cursor.test.sh +356 -0
  651. package/.agents/tests/fm-wake-drain-open-decisions.test.sh +226 -0
  652. package/.agents/tests/fm-wake-drain-unread-status.test.sh +379 -0
  653. package/.agents/tests/fm-wake-queue.test.sh +1224 -0
  654. package/.agents/tests/fm-watch-arm.test.sh +821 -0
  655. package/.agents/tests/fm-watch-checkpoint.test.sh +87 -0
  656. package/.agents/tests/fm-watch-recovery-loop.test.sh +223 -0
  657. package/.agents/tests/fm-watch-triage.test.sh +3889 -0
  658. package/.agents/tests/fm-watcher-lock.test.sh +1133 -0
  659. package/.agents/tests/fm-x-mode.test.sh +3042 -0
  660. package/.agents/tests/gleipnir-guard-pretool-check.test.sh +51 -0
  661. package/.agents/tests/gleipnir-machine-lock.test.sh +130 -0
  662. package/.agents/tests/herdr-test-safety.sh +42 -0
  663. package/.agents/tests/lib.sh +315 -0
  664. package/.agents/tests/remote-herdr-fixture.sh +125 -0
  665. package/.agents/tests/secondmate-helpers.sh +193 -0
  666. package/.agents/tests/smoke.test.sh +93 -0
  667. package/.agents/tests/wake-helpers.sh +338 -0
  668. package/.agents/tests/zellij-test-safety.sh +60 -0
  669. package/.agents/tools/README.md +10 -0
  670. package/.agents/tools/bin/tasks-cli +267 -0
  671. package/.agents/tools/tasks-cli.ts +216 -0
  672. package/AGENTS.md +498 -0
  673. package/CHANGELOG.md +1913 -0
  674. package/LICENSE +1 -1
  675. package/NOTICE +63 -0
  676. package/README.md +114 -60
  677. package/RULES/01-domains.md +51 -0
  678. package/RULES/02-agents.md +78 -0
  679. package/RULES/03-houses.md +61 -0
  680. package/RULES/04-hoard.md +97 -0
  681. package/RULES/05-platforms.md +67 -0
  682. package/RULES/06-append-only.md +108 -0
  683. package/RULES/07-config.md +101 -0
  684. package/RULES/08-delivery-gate.md +75 -0
  685. package/RULES/09-electron.md +58 -0
  686. package/RULES/README.md +22 -0
  687. package/bin/README.md +107 -0
  688. package/bin/a2a-mcp.sh +133 -0
  689. package/bin/a2a-serve.py +103 -0
  690. package/bin/a2a-serve.sh +8 -0
  691. package/bin/a2a-talk.sh +50 -0
  692. package/bin/a2abridge-ensure.sh +108 -0
  693. package/bin/agent-run.sh +43 -0
  694. package/bin/agents-config.sh +256 -0
  695. package/bin/apodex-smoke-test.sh +105 -0
  696. package/bin/bifrost-bridge.sh +131 -0
  697. package/bin/bifrost-ingress.sh +77 -0
  698. package/bin/bootstrap-macos.sh +112 -0
  699. package/bin/bootstrap-windows.ps1 +112 -0
  700. package/bin/branch-guard.sh +64 -0
  701. package/bin/brokk +73 -0
  702. package/bin/brokk-classify-lib.sh +1770 -0
  703. package/bin/brokk-lease-lib.sh +222 -0
  704. package/bin/brokk-lease.sh +193 -0
  705. package/bin/brokk-lint.sh +65 -0
  706. package/bin/brokk-send.sh +51 -0
  707. package/bin/brokk-update.sh +9 -0
  708. package/bin/brokk-wake-grant.sh +127 -0
  709. package/bin/brokk-wake-lib.sh +1849 -0
  710. package/bin/changelog-assemble.sh +154 -0
  711. package/bin/changelog-guard.sh +197 -0
  712. package/bin/crash-sense.sh +173 -0
  713. package/bin/design-check.sh +80 -0
  714. package/bin/design-icon.sh +153 -0
  715. package/bin/desktop-place.sh +224 -0
  716. package/bin/docs-guard.sh +39 -0
  717. package/bin/editor-place.sh +225 -0
  718. package/bin/eindri-acclaim.sh +47 -0
  719. package/bin/eindri-control.sh +28 -0
  720. package/bin/eindri-role.sh +83 -0
  721. package/bin/eindri-seat.sh +83 -0
  722. package/bin/eindri-seen.sh +54 -0
  723. package/bin/eindri-send.sh +15 -0
  724. package/bin/eindri-start.sh +98 -0
  725. package/bin/eindri-watch.sh +57 -0
  726. package/bin/einherjar-spawn.sh +576 -0
  727. package/bin/eir-doctor.sh +158 -0
  728. package/bin/erindi-brief.sh +271 -0
  729. package/bin/feature-inventory.sh +73 -0
  730. package/bin/fleet-apply.sh +81 -0
  731. package/bin/github-deploy.sh +94 -0
  732. package/bin/gjallarhorn-expose.sh +136 -0
  733. package/bin/gjallarhorn-notify.sh +71 -0
  734. package/bin/gjallarhorn-purge.sh +67 -0
  735. package/bin/gjallarhorn-tunnel.sh +85 -0
  736. package/bin/gleipnir-lock-lib.sh +284 -0
  737. package/bin/groa-update.sh +130 -0
  738. package/bin/hall-snapshot.sh +111 -0
  739. package/bin/hamr-harness.sh +246 -0
  740. package/bin/heimdall-ssh-keys.sh +374 -0
  741. package/bin/herdr-agents.py +44 -0
  742. package/bin/herdr-ensure.sh +104 -0
  743. package/bin/herdr-run.sh +452 -0
  744. package/bin/hermes-ensure.sh +88 -0
  745. package/bin/hlidskjalf-agents.sh +172 -0
  746. package/bin/hlidskjalf-usage.sh +196 -0
  747. package/bin/hoard-guard.sh +135 -0
  748. package/bin/hoard-lib.sh +105 -0
  749. package/bin/hodd.sh +102 -0
  750. package/bin/host-sense.sh +199 -0
  751. package/bin/huginn-research-worker.sh +121 -0
  752. package/bin/local-model-lock.sh +70 -0
  753. package/bin/mimir-bridge.py +259 -0
  754. package/bin/mimir-bridge.sh +100 -0
  755. package/bin/mimir-ingest.sh +131 -0
  756. package/bin/mimir-reflect.sh +27 -0
  757. package/bin/mimir.sh +113 -0
  758. package/bin/mjollnir-webhook.sh +72 -0
  759. package/bin/mjollnir.sh +104 -0
  760. package/bin/model-hardware.sh +96 -0
  761. package/bin/model-resolve.sh +160 -0
  762. package/bin/models-detect.sh +178 -0
  763. package/bin/nornir-cron-start.sh +162 -0
  764. package/bin/nornir-job-daily-briefing.sh +191 -0
  765. package/bin/nornir-job-git-sync.sh +152 -0
  766. package/bin/nornir-job-hall-snapshot.sh +28 -0
  767. package/bin/nornir-job-memory-housekeeping.sh +114 -0
  768. package/bin/nornir-job-nsr-compliance.sh +54 -0
  769. package/bin/nornir-job-observer.sh +166 -0
  770. package/bin/nornir-job-skillopt-sleep.sh +76 -0
  771. package/bin/npm-publish.sh +136 -0
  772. package/bin/omarchy-hook-install.sh +60 -0
  773. package/bin/omarchy-install.sh +161 -0
  774. package/bin/omarchy-plugins.sh +187 -0
  775. package/bin/omarchy-sense.sh +162 -0
  776. package/bin/perm-guard.sh +30 -0
  777. package/bin/pi-agent.sh +68 -0
  778. package/bin/pi-ensure.sh +77 -0
  779. package/bin/pi-local.sh +37 -0
  780. package/bin/pi-model-agnostic.sh +71 -0
  781. package/bin/pi-seat.sh +63 -0
  782. package/bin/prereq-ensure.sh +166 -0
  783. package/bin/private-guard.sh +58 -0
  784. package/bin/project-git.sh +69 -0
  785. package/bin/public-guard.sh +71 -0
  786. package/bin/ratatoskr.sh +49 -0
  787. package/bin/realm-lib.sh +32 -0
  788. package/bin/repo-scrub.sh +62 -0
  789. package/bin/rodd-operational-input.sh +250 -0
  790. package/bin/runes-append.sh +198 -0
  791. package/bin/saga-session-start.sh +172 -0
  792. package/bin/saga-sessionstart-run.sh +79 -0
  793. package/bin/saga-wake-drain.sh +41 -0
  794. package/bin/secret-guard.sh +63 -0
  795. package/bin/sessrumnir-ensure.sh +100 -0
  796. package/bin/sessrumnir-sync.sh +237 -0
  797. package/bin/sessrumnir.sh +104 -0
  798. package/bin/skillopt-setup.sh +76 -0
  799. package/bin/skuld-branch-outcome.sh +203 -0
  800. package/bin/skuld-branch-prompt.sh +101 -0
  801. package/bin/smidja-bootstrap.sh +94 -0
  802. package/bin/smidja-observe.sh +86 -0
  803. package/bin/syn-arm-pretool-check.sh +57 -0
  804. package/bin/syn-asset-pretool-check.sh +89 -0
  805. package/bin/syn-cd-pretool-check.sh +28 -0
  806. package/bin/syn-guard-pretool-check.sh +83 -0
  807. package/bin/syn-spawn-pretool-check.sh +58 -0
  808. package/bin/syn-turnend-guard.sh +36 -0
  809. package/bin/syn-watch-arm.sh +99 -0
  810. package/bin/tailscale-sync.sh +203 -0
  811. package/bin/telegram-bot.sh +82 -0
  812. package/bin/toolchain.sh +74 -0
  813. package/bin/ui-truth-check.sh +75 -0
  814. package/bin/utgard.sh +111 -0
  815. package/bin/valhalla.sh +115 -0
  816. package/bin/valknut-load.sh +298 -0
  817. package/bin/vor-crew-state.sh +124 -0
  818. package/bin/wedge-notify.sh +60 -0
  819. package/bin/workspace-provision.sh +80 -0
  820. package/bin/workspace-rag.sh +163 -0
  821. package/bin/wyrd-db.sh +47 -0
  822. package/bin/yggdrasil.sh +152 -0
  823. package/bin/ymir-edit.sh +145 -0
  824. package/bin/ymir-install.sh +806 -0
  825. package/bin/ymir-invite.sh +93 -0
  826. package/bin/ymir-isolation.sh +88 -0
  827. package/bin/ymir-migrate.sh +61 -0
  828. package/bin/ymir-plan.sh +356 -0
  829. package/bin/ymir-platform.sh +348 -0
  830. package/bin/ymir-say.sh +110 -0
  831. package/bin/ymir-setup-auth.sh +119 -0
  832. package/bin/ymir-validate.sh +193 -0
  833. package/bin/ymir.js +74 -16
  834. package/docs/Architecture.md +339 -0
  835. package/docs/design.md +351 -0
  836. package/docs/installations/README.md +63 -0
  837. package/docs/installations/ymir-home.md +93 -0
  838. package/docs/integration.md +105 -0
  839. package/docs/lore.md +1060 -0
  840. package/docs/research/agent-harnesses.md +290 -0
  841. package/docs/runbooks/README.md +19 -0
  842. package/docs/runbooks/a2a.md +91 -0
  843. package/docs/runbooks/agent-permissions.md +99 -0
  844. package/docs/runbooks/agents.md +57 -0
  845. package/docs/runbooks/models.md +69 -0
  846. package/docs/runbooks/realm-onboarding.md +60 -0
  847. package/docs/runbooks/secrets-and-hoard.md +60 -0
  848. package/docs/runbooks/updates-and-migrations.md +110 -0
  849. package/docs/runbooks/worktrees-isolation.md +72 -0
  850. package/docs/session-start.md +105 -0
  851. package/docs/skillopt-integration.md +321 -0
  852. package/docs/sync-system.md +66 -0
  853. package/docs/workspaces.md +104 -0
  854. package/docs/ymir-rut.md +260 -0
  855. package/package.json +54 -14
  856. package/packaging/build.sh +74 -0
  857. package/packaging/macos/Ymir Installer.command +43 -0
  858. package/packaging/windows/ymir-setup.nsi +74 -0
  859. package/scripts/README.md +22 -0
  860. package/scripts/electron.sh +269 -0
  861. package/scripts/lower.sh +34 -0
  862. package/scripts/raise.sh +42 -0
  863. package/scripts/start.sh +217 -0
  864. package/scripts/stop.sh +113 -0
  865. package/install.sh +0 -35
@@ -0,0 +1,141 @@
1
+ #!/usr/bin/env bash
2
+ # fm-timeout-lib.sh - the single owner of bounded command execution.
3
+ #
4
+ # Sourced, never executed. Provides one hard-bound runner so no caller has to
5
+ # re-derive the coreutils/BSD/perl selection, and so every bounded call in this
6
+ # repo agrees on what "the bound was hit" means.
7
+ #
8
+ # fm_timeout_mechanism
9
+ # Prints the mechanism fm_run_timed will use on this host: "timeout",
10
+ # "gtimeout", "perl", or "bash". Set FM_TIMEOUT_MECHANISM_OVERRIDE=bash
11
+ # to force the dependency-free fallback.
12
+ #
13
+ # fm_run_timed <seconds> <command> [args...]
14
+ # Runs the command with a hard bound. Exit status is the command's own,
15
+ # except 124, which means the bound was hit (GNU timeout's convention,
16
+ # reproduced by the perl and bash fallbacks).
17
+ #
18
+ # A non-positive bound is not a bound: `timeout 0` and the perl fallback's
19
+ # `alarm 0` both disable the deadline, so callers must reject 0 before calling.
20
+ #
21
+ # All four mechanisms terminate the whole process GROUP, not just the direct
22
+ # child, so a hung grandchild (a vendor CLI spawned by a wrapper script, a git
23
+ # fetch spawned by a sweep) cannot outlive the bound. GNU/BSD `timeout` does
24
+ # this by default because it does not run the command in the foreground process
25
+ # group; the perl fallback does it explicitly with setpgrp plus a negative pid,
26
+ # and the bash fallback uses monitor mode to give the bounded child its own
27
+ # process group before signaling its negative pid.
28
+ set -u
29
+
30
+ fm_timeout_mechanism() {
31
+ if [ "${FM_TIMEOUT_MECHANISM_OVERRIDE:-}" = bash ]; then
32
+ printf 'bash\n'
33
+ elif command -v timeout >/dev/null 2>&1; then
34
+ printf 'timeout\n'
35
+ elif command -v gtimeout >/dev/null 2>&1; then
36
+ printf 'gtimeout\n'
37
+ elif command -v perl >/dev/null 2>&1; then
38
+ printf 'perl\n'
39
+ else
40
+ printf 'bash\n'
41
+ fi
42
+ }
43
+
44
+ fm_run_bash_timeout() {
45
+ local seconds=$1 command_status deadline_status child_pid watchdog_pid command_rc recorded_rc monitor_was_on=0
46
+ shift
47
+ command_status=$(mktemp "${TMPDIR:-/tmp}/fm-bash-timeout-command.XXXXXX" 2>/dev/null) || return 124
48
+ deadline_status="${command_status}.deadline"
49
+ case $- in *m*) monitor_was_on=1 ;; esac
50
+ set -m
51
+ (
52
+ set +m
53
+ "$@"
54
+ command_rc=$?
55
+ printf '%s\n' "$command_rc" > "$command_status"
56
+ exit "$command_rc"
57
+ ) &
58
+ child_pid=$!
59
+ (
60
+ set +m
61
+ sleep "$seconds"
62
+ printf 'expired\n' > "$deadline_status"
63
+ kill -TERM -- "-$child_pid" 2>/dev/null || true
64
+ sleep 0.2
65
+ kill -KILL -- "-$child_pid" 2>/dev/null || true
66
+ exit 124
67
+ ) &
68
+ watchdog_pid=$!
69
+ [ "$monitor_was_on" -eq 1 ] || set +m
70
+
71
+ if wait "$child_pid" 2>/dev/null; then
72
+ command_rc=0
73
+ else
74
+ command_rc=$?
75
+ fi
76
+ if [ -s "$deadline_status" ]; then
77
+ wait "$watchdog_pid" 2>/dev/null || true
78
+ command_rc=124
79
+ else
80
+ kill -TERM -- "-$watchdog_pid" 2>/dev/null || kill "$watchdog_pid" 2>/dev/null || true
81
+ wait "$watchdog_pid" 2>/dev/null || true
82
+ recorded_rc=$(cat "$command_status" 2>/dev/null || true)
83
+ case "$recorded_rc" in ''|*[!0-9]*) ;; *) command_rc=$recorded_rc ;; esac
84
+ fi
85
+ rm -f "$command_status" "$deadline_status" 2>/dev/null || true
86
+ return "$command_rc"
87
+ }
88
+
89
+ fm_run_external_timeout() {
90
+ local runner=$1 seconds=$2 status_file runner_pid runner_rc command_rc
91
+ shift 2
92
+ status_file=$(mktemp "${TMPDIR:-/tmp}/fm-timeout-status.XXXXXX" 2>/dev/null) || return 124
93
+ # Run timeout asynchronously so its pid - also the process-group id created
94
+ # by GNU/BSD timeout without --foreground - remains available for cleanup.
95
+ # A shell wrapper can exit promptly on TERM while one of its descendants
96
+ # ignores TERM; timeout then considers the command finished and does not send
97
+ # its configured KILL. Explicitly reap that leftover group on a real timeout.
98
+ # shellcheck disable=SC2016 # Expansion is deliberately deferred to the child shell.
99
+ "$runner" -k 1 "$seconds" bash -c '
100
+ status_file=$1
101
+ shift
102
+ "$@"
103
+ command_rc=$?
104
+ printf "%s\n" "$command_rc" > "$status_file"
105
+ exit "$command_rc"
106
+ ' _ "$status_file" "$@" &
107
+ runner_pid=$!
108
+ if wait "$runner_pid"; then
109
+ runner_rc=0
110
+ else
111
+ runner_rc=$?
112
+ fi
113
+ command_rc=$(cat "$status_file" 2>/dev/null || true)
114
+ rm -f "$status_file" 2>/dev/null || true
115
+ case "$command_rc" in
116
+ ''|*[!0-9]*) ;;
117
+ *) [ "$command_rc" -le 255 ] && return "$command_rc" ;;
118
+ esac
119
+ case "$runner_rc" in
120
+ 124|137)
121
+ kill -KILL -- "-$runner_pid" 2>/dev/null || true
122
+ return 124
123
+ ;;
124
+ *) return "$runner_rc" ;;
125
+ esac
126
+ }
127
+
128
+ fm_run_timed() { # <seconds> <command...>
129
+ local seconds=$1
130
+ shift
131
+ case "$(fm_timeout_mechanism)" in
132
+ timeout) fm_run_external_timeout timeout "$seconds" "$@" ;;
133
+ gtimeout) fm_run_external_timeout gtimeout "$seconds" "$@" ;;
134
+ perl)
135
+ perl -e 'my $t = shift; my $pid = fork; die "fork failed" unless defined $pid; if (!$pid) { setpgrp(0, 0); exec @ARGV } local $SIG{ALRM} = sub { kill "TERM", -$pid; select undef, undef, undef, 0.2; kill "KILL", -$pid; exit 124 }; alarm $t; waitpid $pid, 0; exit($? >> 8)' \
136
+ "$seconds" "$@"
137
+ ;;
138
+ bash) fm_run_bash_timeout "$seconds" "$@" ;;
139
+ *) return 124 ;;
140
+ esac
141
+ }
@@ -0,0 +1,179 @@
1
+ #!/usr/bin/env bash
2
+ # fm-timing-lib.sh - the single owner of the deferred network stage's elapsed-time
3
+ # instrumentation.
4
+ #
5
+ # Sourced, never executed.
6
+ #
7
+ # WHY THIS EXISTS. The deferred stage (bin/fm-startup-network.sh) publishes one
8
+ # aggregate started/finished pair, so a run that took a minute could not be
9
+ # attributed to a phase, a host, or a clone without re-running it by hand under
10
+ # manual tracing. These helpers record per-step elapsed times as the run happens,
11
+ # so the next slow run is answerable from the durable record alone.
12
+ #
13
+ # OFF BY DEFAULT, AND INERT. Every helper is a no-op unless FM_TIMING_LOG names a
14
+ # file. Nothing here talks to the network, waits, locks, or changes control flow:
15
+ # a failed append is discarded rather than propagated, because losing a diagnostic
16
+ # line must never change what a sweep does or how it exits.
17
+ #
18
+ # ENVIRONMENT, both exported by the stage that owns a run:
19
+ # FM_TIMING_LOG append-only record file. Unset or empty disables recording.
20
+ # Exported across process boundaries on purpose: the phases
21
+ # being measured run in bin/fm-bootstrap.sh and
22
+ # bin/fm-fleet-sync.sh, which are children of the stage.
23
+ # FM_TIMING_EPOCH_MS the run's start instant, so every record carries an offset
24
+ # from ONE origin even though the records are written by
25
+ # several processes. Defaults to the first recording
26
+ # process's own start, which keeps a hand-run child readable.
27
+ #
28
+ # RECORD FORMAT, one tab-separated line per measured step:
29
+ # v1 <TAB> scope <TAB> name <TAB> start-offset-ms <TAB> elapsed-ms <TAB> detail
30
+ # Appends are single short lines opened O_APPEND, so concurrent writers interleave
31
+ # whole lines rather than corrupting each other.
32
+ #
33
+ # NO SECRETS, BY CONSTRUCTION. `detail` names an identity - a secondmate id, a
34
+ # host, a clone directory name - and identities never contain whitespace, while
35
+ # the things that must never reach this file (a command line, an environment
36
+ # dump, a captured error) always do. So a detail carrying ANY whitespace is not a
37
+ # truncated identity, it is free text that arrived from somewhere it should not
38
+ # have, and it is dropped entirely rather than cleaned up: cleaning would leave
39
+ # the token in `KEY=secret` behind, because a credential is made of exactly the
40
+ # characters an identity is made of. What remains is then narrowed to
41
+ # [A-Za-z0-9._@:/+-] and truncated, which is what stops a detail from forging
42
+ # extra tab-separated records. Enforcing this here rather than at each call site
43
+ # is what makes "this file cannot carry a credential or an argv" checkable in one
44
+ # place.
45
+ set -u
46
+
47
+ FM_TIMING_DETAIL_MAX=${FM_TIMING_DETAIL_MAX:-80}
48
+
49
+ fm_timing_enabled() {
50
+ [ -n "${FM_TIMING_LOG:-}" ]
51
+ }
52
+
53
+ # Milliseconds since the epoch. EPOCHREALTIME is a bash builtin (no fork) whose
54
+ # decimal separator follows the locale, so both forms are accepted. A shell
55
+ # without it - macOS's system bash 3.2, which `env bash` still resolves to on a
56
+ # host with no newer bash on PATH - degrades to whole-second granularity rather
57
+ # than losing the timing entirely. That is a coarser answer to "which host was
58
+ # slow", not a missing one, because the steps being measured are seconds-scale.
59
+ fm_timing_now_ms() {
60
+ local raw sec frac
61
+ raw=${EPOCHREALTIME:-}
62
+ case "$raw" in
63
+ *[0-9][.,][0-9]*)
64
+ sec=${raw%%[.,]*}
65
+ frac=${raw#*[.,]}
66
+ frac="${frac}000"
67
+ frac=${frac:0:3}
68
+ case "$sec$frac" in
69
+ ''|*[!0-9]*) ;;
70
+ *) printf '%s\n' "$(( sec * 1000 + 10#$frac ))"; return 0 ;;
71
+ esac
72
+ ;;
73
+ esac
74
+ sec=$(date +%s 2>/dev/null || printf '0')
75
+ case "$sec" in ''|*[!0-9]*) sec=0 ;; esac
76
+ printf '%s\n' "$(( sec * 1000 ))"
77
+ }
78
+
79
+ # Ensure FM_TIMING_EPOCH_MS holds the origin every offset is measured from.
80
+ # Callers that own a run export it up front; a process that starts recording
81
+ # without one adopts its own first measurement so its records stay internally
82
+ # consistent instead of being silently dropped.
83
+ #
84
+ # This SETS the variable rather than printing it, because the caller must read it
85
+ # as "$FM_TIMING_EPOCH_MS" and not through a command substitution: a substitution
86
+ # runs in a subshell, so the lazily chosen origin would be discarded and every
87
+ # record would recompute it, flattening every start offset to zero.
88
+ fm_timing_epoch_ensure() {
89
+ case "${FM_TIMING_EPOCH_MS:-}" in
90
+ ''|*[!0-9]*)
91
+ FM_TIMING_EPOCH_MS=$(fm_timing_now_ms)
92
+ export FM_TIMING_EPOCH_MS
93
+ ;;
94
+ esac
95
+ }
96
+
97
+ # Start a run: point recording at <file> and stamp the shared origin. The file is
98
+ # created empty so a run that dies before its first record still publishes an
99
+ # empty-but-present artifact rather than looking like it was never instrumented.
100
+ fm_timing_start() { # <file>
101
+ local file=$1
102
+ [ -n "$file" ] || return 0
103
+ : > "$file" 2>/dev/null || return 0
104
+ FM_TIMING_LOG=$file
105
+ FM_TIMING_EPOCH_MS=$(fm_timing_now_ms)
106
+ export FM_TIMING_LOG FM_TIMING_EPOCH_MS
107
+ }
108
+
109
+ fm_timing_sanitize() { # <text>
110
+ local text=${1:-}
111
+ case "$text" in
112
+ *[[:space:]]*) printf '%s\n' 'unrecordable'; return 0 ;;
113
+ esac
114
+ text=${text//[!A-Za-z0-9._@:\/+-]/_}
115
+ printf '%s\n' "${text:0:$FM_TIMING_DETAIL_MAX}"
116
+ }
117
+
118
+ # Record one measured step. Never fails the caller: a missing log, an unwritable
119
+ # path, or a malformed start stamp all resolve to "no record", never an error.
120
+ fm_timing_record() { # <scope> <name> <start-ms> [detail]
121
+ local scope=$1 name=$2 start=$3 detail=${4:-} now offset elapsed
122
+ fm_timing_enabled || return 0
123
+ case "$start" in ''|*[!0-9]*) return 0 ;; esac
124
+ now=$(fm_timing_now_ms)
125
+ fm_timing_epoch_ensure
126
+ offset=$(( start - FM_TIMING_EPOCH_MS ))
127
+ [ "$offset" -ge 0 ] || offset=0
128
+ elapsed=$(( now - start ))
129
+ [ "$elapsed" -ge 0 ] || elapsed=0
130
+ printf 'v1\t%s\t%s\t%s\t%s\t%s\n' \
131
+ "$(fm_timing_sanitize "$scope")" "$(fm_timing_sanitize "$name")" \
132
+ "$offset" "$elapsed" "$(fm_timing_sanitize "$detail")" \
133
+ >> "$FM_TIMING_LOG" 2>/dev/null || true
134
+ return 0
135
+ }
136
+
137
+ # Render a recorded run for a human. Prints nothing at all when the file is
138
+ # missing or holds no record, so an uninstrumented or empty run adds no output.
139
+ fm_timing_render() { # <file>
140
+ local file=$1
141
+ [ -n "$file" ] && [ -s "$file" ] || return 0
142
+ awk -F'\t' '
143
+ $1 != "v1" || NF < 5 { next }
144
+ {
145
+ records++
146
+ scope[records] = $2; name[records] = $3
147
+ offset[records] = $4 + 0; elapsed[records] = $5 + 0
148
+ detail[records] = $6
149
+ }
150
+ END {
151
+ if (records == 0) exit 0
152
+ print "TIMINGS - where the deferred network checks spent their time (ms):"
153
+ for (i = 1; i <= records; i++) {
154
+ label = name[i]
155
+ if (detail[i] != "") label = label " " detail[i]
156
+ printf " %-11s %-42s start=+%-7d elapsed=%d\n", scope[i], label, offset[i], elapsed[i]
157
+ }
158
+ for (i = 1; i <= records; i++) {
159
+ for (j = i + 1; j <= records; j++) {
160
+ if (elapsed[j] > elapsed[i]) {
161
+ t = scope[i]; scope[i] = scope[j]; scope[j] = t
162
+ t = name[i]; name[i] = name[j]; name[j] = t
163
+ t = offset[i]; offset[i] = offset[j]; offset[j] = t
164
+ t = elapsed[i]; elapsed[i] = elapsed[j]; elapsed[j] = t
165
+ t = detail[i]; detail[i] = detail[j]; detail[j] = t
166
+ }
167
+ }
168
+ }
169
+ top = records < 3 ? records : 3
170
+ line = ""
171
+ for (i = 1; i <= top; i++) {
172
+ label = name[i]
173
+ if (detail[i] != "") label = label " " detail[i]
174
+ line = line (line == "" ? "" : ", ") scope[i] " " label " " elapsed[i] "ms"
175
+ }
176
+ print " slowest: " line
177
+ }
178
+ ' "$file" 2>/dev/null || true
179
+ }
@@ -0,0 +1,291 @@
1
+ #!/usr/bin/env bash
2
+ # fm-tmux-lib.sh — shared tmux pane primitives for firstmate.
3
+ #
4
+ # ONE tmux source for delivery-busy detection, composer capture primitives,
5
+ # and verified submit.
6
+ # Both the away-mode daemon and bin/fm-send.sh reach these primitives through
7
+ # backend dispatch, while bin/fm-composer-lib.sh owns the shared verdict.
8
+ #
9
+ # Composer shapes and verdicts are owned by bin/fm-composer-lib.sh.
10
+ # This file owns only tmux's styled capture, cursor and Pi identity primitives,
11
+ # delivery busy read, and submit conversions that consume the shared verdict.
12
+ # Styled captures remain internal; fm-peek and every human-facing capture stay
13
+ # plain.
14
+ #
15
+ # OpenCode's busy-queued Enter conversion accepts only structurally proven
16
+ # pending text after retries, while the separate turn-started conversion accepts
17
+ # an unknown post-Enter composer only after this submit observed an idle baseline
18
+ # become busy.
19
+ # The queued-Enter policy itself lives in fm_composer_queued_enter_verdict
20
+ # (bin/fm-composer-lib.sh); this file supplies tmux's pane-busy primitive.
21
+ #
22
+ # FM_COMPOSER_IDLE_RE is interpreted by the shared classifier with its structural
23
+ # and styling safety gates.
24
+ # FM_BUSY_REGEX overrides the rendered delivery-busy matching used here.
25
+ #
26
+ # NOT a task-state source: task busy state is owned by bin/fm-busy-lib.sh's
27
+ # semantic contract. The matching below serves only delivery guards: the submit
28
+ # acknowledgement and the away-mode supervisor-pane busy guard. Both ask about
29
+ # the pane receiving input, not the state of a recorded worker task. Matching
30
+ # stays harness-scoped so one harness's output cannot make another read busy.
31
+ #
32
+ # All functions are `set -u` and `set -e` safe (guarded tmux calls, explicit
33
+ # returns) so they can be sourced into either context.
34
+ #
35
+ # Composer classification is NOT owned here: every shape, glyph, border
36
+ # family, geometry rule, and verdict decision lives in the shared
37
+ # bin/fm-composer-lib.sh (fm_composer_classify_screen), sourced below and
38
+ # reused by every backend adapter so the decision cannot drift. This file
39
+ # keeps only tmux's genuine capture-side primitives - the styled pane
40
+ # capture, the #{cursor_y} cursor read, the pi foreground-process identity
41
+ # probe, and the capability descriptor - plus the busy detection and submit
42
+ # cores that consume the shared verdict.
43
+
44
+ # shellcheck source=bin/fm-composer-lib.sh
45
+ . "$(dirname -- "${BASH_SOURCE[0]}")/fm-composer-lib.sh"
46
+ # shellcheck source=bin/fm-cursor-lib.sh
47
+ . "$(dirname -- "${BASH_SOURCE[0]}")/fm-cursor-lib.sh"
48
+
49
+
50
+ # fm_tmux_strip_ghost: thin adapter over the shared, fleet-wide ghost extractor
51
+ # fm_composer_strip_ghost (bin/fm-composer-lib.sh). It drops de-emphasised
52
+ # ghost/placeholder runs - dim/faint (SGR 2, claude's/codex's/cursor's ghost) AND a
53
+ # dark/muted truecolor foreground (grok's placeholder) - from one captured,
54
+ # styled composer line and prints the plain, real-typed text. Kept as a named
55
+ # tmux entry point (and for existing callers/tests) but owns no logic of its own,
56
+ # so the tmux and herdr adapters cannot drift apart on what counts as ghost text.
57
+ fm_tmux_strip_ghost() { fm_composer_strip_ghost; }
58
+
59
+ # --- tmux composer capture and capability primitives ------------------------
60
+ #
61
+ # These four functions are the ONLY tmux-specific composer knowledge left:
62
+ # how to capture a styled screen, how to read the cursor row, how to probe a
63
+ # live pi agent, and the static capability facts. Every shape, glyph, border
64
+ # family, and verdict decision lives in the shared owner
65
+ # (bin/fm-composer-lib.sh, fm_composer_classify_screen), so a new harness
66
+ # shape is taught there once and never here.
67
+
68
+ # fm_tmux_composer_capture: the visible pane WITH ANSI styling. The styled
69
+ # capture is consumed internally by the classifier and is NEVER surfaced
70
+ # (fm-peek and every human/LLM-facing path stay plain).
71
+ fm_tmux_composer_capture() { # <target>
72
+ tmux capture-pane -e -p -t "$1" -S 0 -E - 2>/dev/null
73
+ }
74
+
75
+ # fm_tmux_composer_cursor_row: the pane's cursor row, zero-based, relative to
76
+ # the visible pane - tmux's genuine primitive that no other backend has.
77
+ fm_tmux_composer_cursor_row() { # <target>
78
+ tmux display-message -p -t "$1" '#{cursor_y}' 2>/dev/null
79
+ }
80
+
81
+ # fm_tmux_composer_caps: the tmux capability descriptor - static data, not
82
+ # logic (see the capability model in bin/fm-composer-lib.sh).
83
+ fm_tmux_composer_caps() {
84
+ printf 'styled=1\ncursor=1\nidentity=1\nrows=0\n'
85
+ }
86
+
87
+ # fm_tmux_composer_identity: the tmux agent-identity probe backing the
88
+ # separated (pi) composer shape, tmux's analogue of herdr's native
89
+ # `agent get`. It answers only for pi, from two live signals:
90
+ # - identity: the pane tty's FOREGROUND process group (pgid = tpgid, the
91
+ # same scoping as fm_backend_tmux_foreground_comms) contains a pi-family
92
+ # process (pi, pi-signed, pi-launcher - docs/verification/
93
+ # runtime-backends.md "Agent liveness name sources"), falling back to
94
+ # tmux's own foreground-derived #{pane_current_command}. A pane whose
95
+ # agent died to a shell has no pi foreground process and gets NO identity,
96
+ # which is exactly what keeps the strict blank-row rule honest: a blank
97
+ # row between two stale rules stays unknown.
98
+ # - status: pi's verified busy footer via fm_pane_is_busy, mapped onto the
99
+ # idle/working vocabulary herdr's probe reports natively.
100
+ # Prints "pi<TAB>idle" or "pi<TAB>working"; exits 1 when the pane is not a
101
+ # live pi.
102
+ fm_tmux_composer_identity() { # <target>
103
+ local target=$1 tty pgid tpgid comm found=0 status
104
+ tty=$(tmux display-message -p -t "$target" '#{pane_tty}' 2>/dev/null) || tty=
105
+ case "$tty" in
106
+ /dev/*)
107
+ while read -r _ pgid tpgid comm; do
108
+ [ -n "$comm" ] || continue
109
+ [ "$pgid" = "$tpgid" ] || continue
110
+ case "${comm##*/}" in
111
+ pi|pi-signed|pi-launcher|Pi) found=1 ;;
112
+ esac
113
+ done <<EOF
114
+ $(LC_ALL=C ps -t "${tty#/dev/}" -o pid=,pgid=,tpgid=,comm= 2>/dev/null)
115
+ EOF
116
+ ;;
117
+ esac
118
+ if [ "$found" -ne 1 ]; then
119
+ comm=$(tmux display-message -p -t "$target" '#{pane_current_command}' 2>/dev/null) || comm=
120
+ case "${comm##*/}" in
121
+ pi|pi-signed|pi-launcher) found=1 ;;
122
+ esac
123
+ fi
124
+ [ "$found" -eq 1 ] || return 1
125
+ status=$(fm_pane_busy_state "$target" pi)
126
+ case "$status" in
127
+ busy) printf 'pi\tworking' ;;
128
+ idle) printf 'pi\tidle' ;;
129
+ *) return 1 ;;
130
+ esac
131
+ }
132
+
133
+ # fm_tmux_composer_state: the tmux composer verdict - a thin adapter over the
134
+ # shared screen classifier. The verdict contract (empty | pending |
135
+ # pending-unproven | unknown, positive proof required for empty, unrecognized
136
+ # future verdicts failing safe) is owned by bin/fm-composer-lib.sh. Identity
137
+ # is fetched lazily, only when the classifier reports the verdict depends on
138
+ # it (a pi separator pair under the cursor), so the common read never pays
139
+ # for the process probe.
140
+ fm_tmux_composer_state() { # <target> -> empty|pending|pending-unproven|unknown
141
+ local target=$1 cy pane verdict identity
142
+ cy=$(fm_tmux_composer_cursor_row "$target") || { printf 'unknown'; return 0; }
143
+ case "$cy" in ''|*[!0-9]*) printf 'unknown'; return 0 ;; esac
144
+ pane=$(fm_tmux_composer_capture "$target") || { printf 'unknown'; return 0; }
145
+ verdict=$(fm_composer_classify_screen "$(fm_tmux_composer_caps)" "$pane" "$cy")
146
+ if [ "$verdict" = need-identity ]; then
147
+ if ! identity=$(fm_tmux_composer_identity "$target") || [ -z "$identity" ]; then
148
+ identity=probe-absent
149
+ fi
150
+ verdict=$(fm_composer_classify_screen "$(fm_tmux_composer_caps)" "$pane" "$cy" "$identity")
151
+ [ "$verdict" != need-identity ] || verdict=unknown
152
+ fi
153
+ # Cursor Agent CLI parks its terminal cursor OUTSIDE its composer, below the
154
+ # footer, with #{cursor_flag} 0 - so on a Cursor pane tmux's cursor row is not
155
+ # a composer locator and the cursor-anchored read can only ever answer
156
+ # `unknown`. Reclassify that pane the way every cursorless backend already
157
+ # classifies it, letting the bottom-most shape win, which is the same rule
158
+ # herdr, zellij, cmux, and orca use for every harness including this one.
159
+ # Gated on Cursor's own structural process identity, never on the verdict
160
+ # alone, so the strict blank-row posture that owns `unknown` for every other
161
+ # harness is untouched.
162
+ if [ "$verdict" = unknown ] && fm_tmux_pane_is_cursor "$target"; then
163
+ verdict=$(fm_composer_classify_screen "$(fm_tmux_composer_caps)" "$pane" '')
164
+ fi
165
+ printf '%s' "$verdict"
166
+ }
167
+
168
+ # fm_tmux_pane_is_cursor: true when the pane's FOREGROUND process group contains
169
+ # a genuine Cursor Agent CLI process. Cursor runs as a bundled node script, so
170
+ # tmux's own #{pane_current_command} reports a bare `node`; identity therefore
171
+ # comes from Cursor's name or install tree in the command path or argv[0], whose
172
+ # single owner is bin/fm-cursor-lib.sh. The foreground scoping (pgid = tpgid)
173
+ # matches fm_tmux_composer_identity, so a pane whose agent exited to a shell has
174
+ # no Cursor foreground process and gets no reclassification.
175
+ fm_tmux_pane_is_cursor() { # <target>
176
+ local target=$1 tty pid pgid tpgid comm args argv0
177
+ tty=$(tmux display-message -p -t "$target" '#{pane_tty}' 2>/dev/null) || return 1
178
+ case "$tty" in /dev/*) ;; *) return 1 ;; esac
179
+ while read -r pid pgid tpgid comm; do
180
+ [ -n "$comm" ] || continue
181
+ [ "$pgid" = "$tpgid" ] || continue
182
+ args=$(LC_ALL=C ps -p "$pid" -o args= 2>/dev/null) || args=
183
+ args=${args#"${args%%[![:space:]]*}"}
184
+ argv0=${args%%[[:space:]]*}
185
+ fm_cursor_process_matches "$comm" '' "$argv0" && return 0
186
+ done <<EOF
187
+ $(LC_ALL=C ps -t "${tty#/dev/}" -o pid=,pgid=,tpgid=,comm= 2>/dev/null)
188
+ EOF
189
+ return 1
190
+ }
191
+
192
+ # fm_pane_input_pending: 0 when the composer is not proven empty, so pending
193
+ # text, ambiguous structure, unreadable state, and future verdicts all defer.
194
+ fm_pane_input_pending() { # <target>
195
+ [ "$(fm_tmux_composer_state "$1")" != empty ]
196
+ }
197
+
198
+ # fm_pane_is_busy: 0 if the pane's last few non-blank lines show a busy footer
199
+ # (an agent mid-turn). Scans a 40-line tail like fm-watch.sh.
200
+ fm_pane_busy_state() { # <target> [harness] -> busy|idle|unknown
201
+ local win=$1 harness=${2:-} tail40 visible
202
+ tail40=$(tmux capture-pane -p -t "$win" -S -40 2>/dev/null) \
203
+ || { printf 'unknown'; return 0; }
204
+ visible=$(printf '%s' "$tail40" | grep -v '^[[:space:]]*$' | tail -12)
205
+ [ -n "$visible" ] || { printf 'unknown'; return 0; }
206
+ if printf '%s' "$visible" | fm_busy_lines_match "$harness"; then
207
+ printf 'busy'
208
+ else
209
+ printf 'idle'
210
+ fi
211
+ }
212
+
213
+ fm_pane_is_busy() { # <target> [harness]
214
+ [ "$(fm_pane_busy_state "$1" "${2:-}")" = busy ]
215
+ }
216
+
217
+ # fm_tmux_submit_core: type <text> into <target> ONCE, then submit with Enter,
218
+ # verifying the composer cleared. Retries Enter ONLY — never retypes, because a
219
+ # swallowed Enter leaves our text in the composer and retyping would duplicate
220
+ # it. Echoes the final proof-carrying verdict on stdout so callers can require
221
+ # exact `empty` before treating submission as confirmed.
222
+ # Busy-queued Enter (opencode 1.18.4): the harness accepts Enter while mid-turn
223
+ # and queues it for after the current turn, but keeps the typed text visible in
224
+ # the composer. Once the Enter-retry budget is spent and a structurally proven
225
+ # composer still reads "pending", the submit core falls back to
226
+ # `fm_pane_is_busy`: a busy pane means the Enter was accepted and queued (report
227
+ # `empty` so the caller does not re-send), while an idle pane keeps `pending` as
228
+ # a genuine swallow. Pending-unproven receives the same Enter retry budget but
229
+ # never reaches this exception.
230
+ # Turn-started confirmation (the strict blank-row posture's counterpart): a
231
+ # harness whose mid-turn screen the classifier cannot positively identify (pi
232
+ # replaces its separated composer while working) reads `unknown` right after a
233
+ # successful submit. When and only when the pane was IDLE before the text was
234
+ # typed, an idle-to-busy transition across our Enter is proof the harness
235
+ # accepted the submission - the same semantic signal herdr's native
236
+ # agent-state confirmation uses, read from the pane's verified busy footer.
237
+ # The busy read is polled across the remaining retry budget because the turn
238
+ # takes a beat to render. Without the baseline (a direct
239
+ # fm_tmux_submit_enter_core caller, or a pane already busy before typing) an
240
+ # `unknown` verdict is preserved untouched: busy conversion without the
241
+ # transition evidence could mark an undelivered message delivered.
242
+ fm_tmux_submit_enter_core() { # <target> <retries> <enter-sleep> [baseline-idle]
243
+ local target=$1 retries=$2 sleep_s=$3 baseline_idle=${4:-} i=0 j state busy_state
244
+ while :; do
245
+ tmux send-keys -t "$target" Enter 2>/dev/null || true
246
+ sleep "$sleep_s"
247
+ state=$(fm_tmux_composer_state "$target")
248
+ case "$state" in
249
+ pending|pending-unproven) ;;
250
+ unknown)
251
+ if [ "$baseline_idle" = 1 ]; then
252
+ j=0
253
+ while [ "$j" -lt "$retries" ]; do
254
+ if fm_pane_is_busy "$target"; then
255
+ printf 'empty'
256
+ return 0
257
+ fi
258
+ j=$((j + 1))
259
+ [ "$j" -ge "$retries" ] || sleep "$sleep_s"
260
+ done
261
+ fi
262
+ printf 'unknown'
263
+ return 0
264
+ ;;
265
+ *) printf '%s' "$state"; return 0 ;;
266
+ esac
267
+ i=$((i + 1))
268
+ [ "$i" -lt "$retries" ] || break
269
+ done
270
+ if [ "$state" != pending ]; then
271
+ printf '%s' "$state"
272
+ return 0
273
+ fi
274
+ # Retries exhausted, composer still shows proven pending.
275
+ # Busy conversion is owned by fm_composer_queued_enter_verdict.
276
+ busy_state=idle
277
+ fm_pane_is_busy "$target" && busy_state=busy
278
+ fm_composer_queued_enter_verdict "$state" "$busy_state"
279
+ }
280
+
281
+ fm_tmux_submit_core() { # <target> <text> <retries> <enter-sleep> <settle>
282
+ local target=$1 text=$2 retries=$3 sleep_s=$4 settle=$5 baseline_idle='' baseline_state
283
+ # The turn-started baseline must predate our own typing: a pane already
284
+ # busy before the text lands can turn "busy" for reasons unrelated to our
285
+ # Enter, so only a clean idle-to-busy transition may confirm a submit.
286
+ baseline_state=$(fm_pane_busy_state "$target")
287
+ [ "$baseline_state" = idle ] && baseline_idle=1
288
+ tmux send-keys -t "$target" -l "$text" 2>/dev/null || { printf 'send-failed'; return 0; }
289
+ sleep "$settle"
290
+ fm_tmux_submit_enter_core "$target" "$retries" "$sleep_s" "$baseline_idle"
291
+ }