@zerwiz/ymir 0.1.3 → 0.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agents/README.md +291 -0
- package/.agents/agents/bragi-marketer.md +111 -0
- package/.agents/agents/brokk.md +352 -0
- package/.agents/agents/forseti-reviewer.md +93 -0
- package/.agents/agents/frigg-consent.md +77 -0
- package/.agents/agents/groa-update.md +77 -0
- package/.agents/agents/hnoss-designer.md +69 -0
- package/.agents/agents/huginn-researcher.md +99 -0
- package/.agents/agents/jord-projects.md +78 -0
- package/.agents/agents/kvasir-scout.md +91 -0
- package/.agents/agents/mimir-planner.md +92 -0
- package/.agents/agents/muninn-stow.md +77 -0
- package/.agents/agents/saga-bearings.md +78 -0
- package/.agents/agents/sindri-developer.md +114 -0
- package/.agents/agents/snotra-documenter.md +105 -0
- package/.agents/agents/syn-recovery.md +78 -0
- package/.agents/agents/tyr-check.md +78 -0
- package/.agents/agents/urdh-hold.md +77 -0
- package/.agents/agents/volundr-smith.md +80 -0
- package/.agents/agents/vor-diagnostics.md +77 -0
- package/.agents/assets/agents/naming.md +50 -0
- package/.agents/assets/agents/registry.md +93 -0
- package/.agents/assets/agents/runtime.md +36 -0
- package/.agents/assets/agents/toon-tasks-cli.md +42 -0
- package/.agents/assets/templates/company_entity.template.md +37 -0
- package/.agents/backend/backends/cmux.sh +649 -0
- package/.agents/backend/backends/herdr-eventwait.py +157 -0
- package/.agents/backend/backends/herdr-workspace-move.py +114 -0
- package/.agents/backend/backends/herdr.sh +3386 -0
- package/.agents/backend/backends/orca.sh +290 -0
- package/.agents/backend/backends/tmux.sh +357 -0
- package/.agents/backend/backends/zellij.sh +700 -0
- package/.agents/backend/fm-afk-launch.sh +654 -0
- package/.agents/backend/fm-afk-return.sh +241 -0
- package/.agents/backend/fm-afk-start.sh +172 -0
- package/.agents/backend/fm-arm-command-policy.mjs +960 -0
- package/.agents/backend/fm-arm-pretool-check.sh +200 -0
- package/.agents/backend/fm-backend-hometag-lib.sh +52 -0
- package/.agents/backend/fm-backend.sh +981 -0
- package/.agents/backend/fm-backlog-handoff.sh +909 -0
- package/.agents/backend/fm-backlog-receive.sh +187 -0
- package/.agents/backend/fm-backlog-transition-lib.sh +779 -0
- package/.agents/backend/fm-bearings-board.sh +204 -0
- package/.agents/backend/fm-bearings-snapshot.sh +546 -0
- package/.agents/backend/fm-bootstrap.sh +1568 -0
- package/.agents/backend/fm-branch-outcome.sh +203 -0
- package/.agents/backend/fm-branch-prompt.sh +101 -0
- package/.agents/backend/fm-brief.sh +452 -0
- package/.agents/backend/fm-busy-event.sh +234 -0
- package/.agents/backend/fm-busy-lib.sh +986 -0
- package/.agents/backend/fm-captain-hold.sh +1001 -0
- package/.agents/backend/fm-cd-command-policy.mjs +153 -0
- package/.agents/backend/fm-cd-pretool-check.sh +190 -0
- package/.agents/backend/fm-check-lib.sh +74 -0
- package/.agents/backend/fm-check-register.sh +42 -0
- package/.agents/backend/fm-check-unregister.sh +52 -0
- package/.agents/backend/fm-classify-lib.sh +1770 -0
- package/.agents/backend/fm-claude-stop-autoarm.sh +334 -0
- package/.agents/backend/fm-composer-lib.sh +1416 -0
- package/.agents/backend/fm-config-inherit-lib.sh +1202 -0
- package/.agents/backend/fm-config-push.sh +254 -0
- package/.agents/backend/fm-control-lib.sh +251 -0
- package/.agents/backend/fm-control.sh +878 -0
- package/.agents/backend/fm-crew-state.sh +625 -0
- package/.agents/backend/fm-cursor-lib.sh +243 -0
- package/.agents/backend/fm-decision-hold.sh +232 -0
- package/.agents/backend/fm-doc-audience-check.sh +269 -0
- package/.agents/backend/fm-dod-lib.sh +67 -0
- package/.agents/backend/fm-ensure-agents-md.sh +253 -0
- package/.agents/backend/fm-extension-launch-barrier.mjs +129 -0
- package/.agents/backend/fm-extension.mjs +2577 -0
- package/.agents/backend/fm-extension.sh +16 -0
- package/.agents/backend/fm-ff-lib.sh +420 -0
- package/.agents/backend/fm-fleet-snapshot.sh +1463 -0
- package/.agents/backend/fm-fleet-sync.sh +458 -0
- package/.agents/backend/fm-fleet-view.sh +96 -0
- package/.agents/backend/fm-gate-refuse-lib.sh +102 -0
- package/.agents/backend/fm-guard.sh +242 -0
- package/.agents/backend/fm-harness.sh +195 -0
- package/.agents/backend/fm-herdr-ci-cleanup.sh +115 -0
- package/.agents/backend/fm-herdr-lab.sh +346 -0
- package/.agents/backend/fm-herdr-session-cleanup.sh +337 -0
- package/.agents/backend/fm-home-seed.sh +984 -0
- package/.agents/backend/fm-home-summary-refresh.sh +255 -0
- package/.agents/backend/fm-hook-host-lib.sh +36 -0
- package/.agents/backend/fm-inactive-reconcile.sh +531 -0
- package/.agents/backend/fm-inbox.sh +399 -0
- package/.agents/backend/fm-install-actionlint.sh +84 -0
- package/.agents/backend/fm-install-herdr.sh +96 -0
- package/.agents/backend/fm-install-shellcheck.sh +84 -0
- package/.agents/backend/fm-install-treehouse.sh +95 -0
- package/.agents/backend/fm-kimi-turnend-hook.sh +276 -0
- package/.agents/backend/fm-lease-lib.sh +218 -0
- package/.agents/backend/fm-lease.sh +189 -0
- package/.agents/backend/fm-line-cap-lib.sh +51 -0
- package/.agents/backend/fm-lint-workflows.sh +137 -0
- package/.agents/backend/fm-lint.sh +591 -0
- package/.agents/backend/fm-lock-lib.sh +104 -0
- package/.agents/backend/fm-lock.sh +107 -0
- package/.agents/backend/fm-marker-lib.sh +12 -0
- package/.agents/backend/fm-merge-local.sh +74 -0
- package/.agents/backend/fm-merge-outcome-lib.sh +138 -0
- package/.agents/backend/fm-nm-run-lib.sh +124 -0
- package/.agents/backend/fm-on.sh +125 -0
- package/.agents/backend/fm-operational-input.sh +253 -0
- package/.agents/backend/fm-peek.sh +46 -0
- package/.agents/backend/fm-pending-reply-lib.sh +1409 -0
- package/.agents/backend/fm-pr-check.sh +135 -0
- package/.agents/backend/fm-pr-lib.sh +1016 -0
- package/.agents/backend/fm-pr-merge.sh +707 -0
- package/.agents/backend/fm-pr-poll.sh +110 -0
- package/.agents/backend/fm-primary-scope-lib.sh +33 -0
- package/.agents/backend/fm-procevent-extension-capture.pl +259 -0
- package/.agents/backend/fm-procevent-lavish.sh +628 -0
- package/.agents/backend/fm-procevent-lib.sh +892 -0
- package/.agents/backend/fm-procevent-quota.sh +290 -0
- package/.agents/backend/fm-procevent-remote-reply.sh +577 -0
- package/.agents/backend/fm-procevent-when.sh +504 -0
- package/.agents/backend/fm-procevent.sh +1466 -0
- package/.agents/backend/fm-project-mode.sh +92 -0
- package/.agents/backend/fm-project-origin-lib.sh +180 -0
- package/.agents/backend/fm-promote.sh +282 -0
- package/.agents/backend/fm-public-followup-emit.sh +260 -0
- package/.agents/backend/fm-public-followup-lib.sh +398 -0
- package/.agents/backend/fm-public-followup.sh +1330 -0
- package/.agents/backend/fm-push-transition-lib.sh +168 -0
- package/.agents/backend/fm-quota-axi-lib.sh +93 -0
- package/.agents/backend/fm-quota-choose.sh +384 -0
- package/.agents/backend/fm-remote-delta-read.sh +189 -0
- package/.agents/backend/fm-remote-doctor.sh +800 -0
- package/.agents/backend/fm-remote-entrypoint.sh +193 -0
- package/.agents/backend/fm-remote-file.sh +234 -0
- package/.agents/backend/fm-remote-home-provision.sh +259 -0
- package/.agents/backend/fm-remote-home-seed.sh +259 -0
- package/.agents/backend/fm-remote-inherit-push.sh +90 -0
- package/.agents/backend/fm-remote-inherit.sh +179 -0
- package/.agents/backend/fm-remote-job-lib.sh +1241 -0
- package/.agents/backend/fm-remote-job-reap-orphans.sh +137 -0
- package/.agents/backend/fm-remote-job-worker.sh +1124 -0
- package/.agents/backend/fm-remote-readiness-lib.sh +44 -0
- package/.agents/backend/fm-remote-secondmate-control.sh +340 -0
- package/.agents/backend/fm-review-diff.sh +158 -0
- package/.agents/backend/fm-secondmate-charter-lib.sh +44 -0
- package/.agents/backend/fm-secondmate-nudge-lib.sh +68 -0
- package/.agents/backend/fm-secondmate-parent-lib.sh +70 -0
- package/.agents/backend/fm-secondmate-reconcile.sh +371 -0
- package/.agents/backend/fm-secondmate-registry-lib.sh +311 -0
- package/.agents/backend/fm-secondmate-report.sh +88 -0
- package/.agents/backend/fm-send.sh +1059 -0
- package/.agents/backend/fm-session-lock-lib.sh +176 -0
- package/.agents/backend/fm-session-start.sh +970 -0
- package/.agents/backend/fm-sessionstart-cursor.sh +40 -0
- package/.agents/backend/fm-sessionstart-nudge.sh +45 -0
- package/.agents/backend/fm-sessionstart-run.sh +145 -0
- package/.agents/backend/fm-spawn.sh +3153 -0
- package/.agents/backend/fm-startup-memory-budget-lib.sh +224 -0
- package/.agents/backend/fm-startup-memory-budget.sh +94 -0
- package/.agents/backend/fm-startup-network.sh +642 -0
- package/.agents/backend/fm-stow-cascade.sh +251 -0
- package/.agents/backend/fm-subagent-pretool-check.sh +207 -0
- package/.agents/backend/fm-supervise-daemon.sh +1732 -0
- package/.agents/backend/fm-supervision-instructions.sh +215 -0
- package/.agents/backend/fm-supervision-lib.sh +90 -0
- package/.agents/backend/fm-supervisor-target-lib.sh +78 -0
- package/.agents/backend/fm-tangle-lib.sh +53 -0
- package/.agents/backend/fm-task-inbox-lib.sh +402 -0
- package/.agents/backend/fm-tasks-axi-lib.sh +123 -0
- package/.agents/backend/fm-teardown.sh +2922 -0
- package/.agents/backend/fm-test-isolation-proof.sh +588 -0
- package/.agents/backend/fm-test-run.sh +2191 -0
- package/.agents/backend/fm-timeout-lib.sh +141 -0
- package/.agents/backend/fm-timing-lib.sh +179 -0
- package/.agents/backend/fm-tmux-lib.sh +291 -0
- package/.agents/backend/fm-tool-update-check.sh +898 -0
- package/.agents/backend/fm-trace-context-lib.sh +227 -0
- package/.agents/backend/fm-transition-lib.sh +103 -0
- package/.agents/backend/fm-turnend-guard-cursor.sh +391 -0
- package/.agents/backend/fm-turnend-guard-grok.sh +90 -0
- package/.agents/backend/fm-turnend-guard.sh +432 -0
- package/.agents/backend/fm-update.sh +110 -0
- package/.agents/backend/fm-vendor-auth-probe.sh +190 -0
- package/.agents/backend/fm-voice-client.py +1373 -0
- package/.agents/backend/fm-voice-relay.py +1256 -0
- package/.agents/backend/fm-wake-drain.sh +592 -0
- package/.agents/backend/fm-wake-grant.sh +127 -0
- package/.agents/backend/fm-wake-lib.sh +1849 -0
- package/.agents/backend/fm-watch-arm.sh +602 -0
- package/.agents/backend/fm-watch-checkpoint.sh +109 -0
- package/.agents/backend/fm-watch.sh +1962 -0
- package/.agents/backend/fm-x-dismiss.sh +116 -0
- package/.agents/backend/fm-x-followup.sh +286 -0
- package/.agents/backend/fm-x-lib.sh +1000 -0
- package/.agents/backend/fm-x-link.sh +234 -0
- package/.agents/backend/fm-x-poll.sh +185 -0
- package/.agents/backend/fm-x-reply.sh +385 -0
- package/.agents/backend/fm_voice_frame.py +166 -0
- package/.agents/backend/fm_voice_records.py +574 -0
- package/.agents/backend/model-bridge.py +290 -0
- package/.agents/backend/opencode-go-bridge.py +30 -0
- package/.agents/bus/README.md +12 -0
- package/.agents/bus/messages.json +1 -0
- package/.agents/config/agents.machine.example.yaml +16 -0
- package/.agents/config/agents.yaml +50 -0
- package/.agents/config/agents.yaml.example +72 -0
- package/.agents/config/crew-dispatch.json +22 -0
- package/.agents/config/cron.yaml +9 -0
- package/.agents/config/eindri-dispatch.json +67 -0
- package/.agents/config/eindri-harness +1 -0
- package/.agents/config/startup-memory-budget +1 -0
- package/.agents/config/tailscale-sync.yaml +27 -0
- package/.agents/filebrowser/.filebrowser.json +6 -0
- package/.agents/filebrowser/README.md +11 -0
- package/.agents/gateway/README.md +12 -0
- package/.agents/github/README.md +15 -0
- package/.agents/github/webhooks/.gitkeep +0 -0
- package/.agents/github/workflows/.gitkeep +0 -0
- package/.agents/harness/opencode/plugins/lib/rodd-operational-input.js +37 -0
- package/.agents/harness/opencode/plugins/package.json +4 -0
- package/.agents/harness/opencode/plugins/saga-sessionstart.js +77 -0
- package/.agents/harness/opencode/plugins/syn-cd-check.js +61 -0
- package/.agents/harness/opencode/plugins/syn-pretool-check.js +68 -0
- package/.agents/harness/opencode/plugins/syn-turnend-guard.js +105 -0
- package/.agents/harness/opencode/plugins/syn-watch-arm.js +521 -0
- package/.agents/memory/README.md +46 -0
- package/.agents/memory/kaia.engram +0 -0
- package/.agents/memory/kaia.engram-shm +0 -0
- package/.agents/memory/kaia.engram-wal +0 -0
- package/.agents/memory/runes_audit.md +5 -0
- package/.agents/memory/well/episodes.jsonl +367 -0
- package/.agents/memory/well/workspace.jsonl +27 -0
- package/.agents/migrations/0001-hodd-layout.sh +38 -0
- package/.agents/migrations/0002-a2a-mcp.sh +27 -0
- package/.agents/migrations/0003-private-data-separation.sh +111 -0
- package/.agents/migrations/0004-hoard-and-realms.sh +104 -0
- package/.agents/sandbox/Dockerfile.utgard +19 -0
- package/.agents/sandbox/README.md +14 -0
- package/.agents/sandbox/utgard.config.json +14 -0
- package/.agents/skills/README.md +59 -0
- package/.agents/skills/bragi-marketing/README.md +3 -0
- package/.agents/skills/bragi-marketing/SKILL.md +82 -0
- package/.agents/skills/eindri-homes/README.md +3 -0
- package/.agents/skills/eindri-homes/SKILL.md +291 -0
- package/.agents/skills/eindri-homes/assets/control-plane.md +122 -0
- package/.agents/skills/eindri-homes/assets/dispatch.md +80 -0
- package/.agents/skills/frigg-consent/README.md +3 -0
- package/.agents/skills/frigg-consent/SKILL.md +56 -0
- package/.agents/skills/galdr-ymirsystem/README.md +3 -0
- package/.agents/skills/galdr-ymirsystem/SKILL.md +158 -0
- package/.agents/skills/galdr-ymirsystem/assets/README.md +74 -0
- package/.agents/skills/galdr-ymirsystem/assets/brokk-distro-runtime.md +390 -0
- package/.agents/skills/galdr-ymirsystem/assets/build-method.md +113 -0
- package/.agents/skills/galdr-ymirsystem/assets/build-tool-categories.md +178 -0
- package/.agents/skills/galdr-ymirsystem/assets/eindri-orchestration.md +518 -0
- package/.agents/skills/galdr-ymirsystem/assets/eindri-profiles.md +136 -0
- package/.agents/skills/galdr-ymirsystem/assets/harness-integration/README.md +673 -0
- package/.agents/skills/galdr-ymirsystem/assets/harness-integration/claude-code.md +197 -0
- package/.agents/skills/galdr-ymirsystem/assets/harness-integration/codex.md +191 -0
- package/.agents/skills/galdr-ymirsystem/assets/harness-integration/cursor.md +193 -0
- package/.agents/skills/galdr-ymirsystem/assets/harness-integration/opencode.md +262 -0
- package/.agents/skills/galdr-ymirsystem/assets/harness-integration/pi.md +295 -0
- package/.agents/skills/galdr-ymirsystem/assets/hlidskjalf-ui.md +496 -0
- package/.agents/skills/galdr-ymirsystem/assets/installation.md +571 -0
- package/.agents/skills/galdr-ymirsystem/assets/local-models/HOST-DATA.example.md +72 -0
- package/.agents/skills/galdr-ymirsystem/assets/local-models.md +274 -0
- package/.agents/skills/galdr-ymirsystem/assets/memory-well.md +161 -0
- package/.agents/skills/galdr-ymirsystem/assets/nornir-jobs.md +377 -0
- package/.agents/skills/galdr-ymirsystem/assets/norse-naming.md +325 -0
- package/.agents/skills/galdr-ymirsystem/assets/odrerir-hall.md +129 -0
- package/.agents/skills/galdr-ymirsystem/assets/pi-boot/einherjar-spawn.schema.json +81 -0
- package/.agents/skills/galdr-ymirsystem/assets/pi-boot/herdr-profile.toml +75 -0
- package/.agents/skills/galdr-ymirsystem/assets/pi-boot/pi-profile.yml +96 -0
- package/.agents/skills/galdr-ymirsystem/assets/pi-boot/supervision-tree.yml +163 -0
- package/.agents/skills/galdr-ymirsystem/assets/pi-boot-guide.md +58 -0
- package/.agents/skills/galdr-ymirsystem/assets/porting-upstream-to-norse.md +301 -0
- package/.agents/skills/galdr-ymirsystem/assets/principles.md +385 -0
- package/.agents/skills/galdr-ymirsystem/assets/reference-adoption.md +99 -0
- package/.agents/skills/galdr-ymirsystem/assets/registry.md +379 -0
- package/.agents/skills/galdr-ymirsystem/assets/runtime-compliance.md +463 -0
- package/.agents/skills/galdr-ymirsystem/assets/runtime-components.md +553 -0
- package/.agents/skills/galdr-ymirsystem/assets/smidja.md +175 -0
- package/.agents/skills/galdr-ymirsystem/schemas/toon-schemas.md +190 -0
- package/.agents/skills/galdr-ymirsystem/scripts/README.md +45 -0
- package/.agents/skills/galdr-ymirsystem/scripts/bench-one.sh +248 -0
- package/.agents/skills/galdr-ymirsystem/scripts/compliance-check.sh +360 -0
- package/.agents/skills/galdr-ymirsystem/scripts/gpu-sample.sh +43 -0
- package/.agents/skills/galdr-ymirsystem/scripts/stop-all.sh +34 -0
- package/.agents/skills/galdr-ymirsystem/scripts/toon-check.py +188 -0
- package/.agents/skills/gjallarhorn-relay/README.md +3 -0
- package/.agents/skills/gjallarhorn-relay/SKILL.md +283 -0
- package/.agents/skills/groa-update/SKILL.md +70 -0
- package/.agents/skills/hamr-adapters/README.md +3 -0
- package/.agents/skills/hamr-adapters/SKILL.md +123 -0
- package/.agents/skills/hamr-adapters/references/common/control-and-recovery.md +37 -0
- package/.agents/skills/hamr-adapters/references/common/dispatch.md +32 -0
- package/.agents/skills/hamr-adapters/references/common/model-and-effort.md +42 -0
- package/.agents/skills/hamr-adapters/references/common/primary-hooks.md +40 -0
- package/.agents/skills/hamr-adapters/references/harness/claude.md +55 -0
- package/.agents/skills/hamr-adapters/references/harness/codex.md +43 -0
- package/.agents/skills/hamr-adapters/references/harness/cursor.md +75 -0
- package/.agents/skills/hamr-adapters/references/harness/grok.md +69 -0
- package/.agents/skills/hamr-adapters/references/harness/kimi.md +51 -0
- package/.agents/skills/hamr-adapters/references/harness/muse.md +70 -0
- package/.agents/skills/hamr-adapters/references/harness/opencode.md +42 -0
- package/.agents/skills/hamr-adapters/references/harness/pi.md +56 -0
- package/.agents/skills/herdr-panes/README.md +3 -0
- package/.agents/skills/herdr-panes/SKILL.md +92 -0
- package/.agents/skills/herdr-panes/assets/herdr-backend.md +346 -0
- package/.agents/skills/herdr-panes/assets/tmux-backend.md +115 -0
- package/.agents/skills/hnoss-design/DESIGN.md +19 -0
- package/.agents/skills/hnoss-design/README.md +3 -0
- package/.agents/skills/hnoss-design/SKILL.md +83 -0
- package/.agents/skills/hvild-afk/README.md +3 -0
- package/.agents/skills/hvild-afk/SKILL.md +241 -0
- package/.agents/skills/jord-projects/README.md +3 -0
- package/.agents/skills/jord-projects/SKILL.md +92 -0
- package/.agents/skills/lifecycle/SKILL.md +56 -0
- package/.agents/skills/lifecycle/smoke_test.sh +52 -0
- package/.agents/skills/lifecycle/start.sh +21 -0
- package/.agents/skills/lifecycle/status.sh +35 -0
- package/.agents/skills/lifecycle/stop.sh +16 -0
- package/.agents/skills/muninn-stow/README.md +3 -0
- package/.agents/skills/muninn-stow/SKILL.md +309 -0
- package/.agents/skills/no-mistakes/SKILL.md +379 -0
- package/.agents/skills/nornir-schedule/README.md +3 -0
- package/.agents/skills/nornir-schedule/SKILL.md +25 -0
- package/.agents/skills/nornir-schedule/assets/events.md +137 -0
- package/.agents/skills/nornir-schedule/assets/quota.md +131 -0
- package/.agents/skills/nsr-compliance/README.md +3 -0
- package/.agents/skills/nsr-compliance/SKILL.md +29 -0
- package/.agents/skills/nsr-compliance/assets/nsr/README.md +40 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/RULES/README.md +15 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/RULES/git-workflow.md +18 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/RULES/security.md +22 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/RULES/tech-stack.md +8 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/SKILL.md +71 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/config/core_four.yaml +16 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/gates/check_danger.sh +102 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/gates/check_wiring.sh +69 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/gates/validate_code.sh +29 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/gates/verify_docs.py +66 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/generate.sh +109 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/harness/envelopes/result_envelope.json +14 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/harness/envelopes/task_envelope.json +20 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/harness/runner.py +303 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/telemetry/logger.py +112 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/README.md +18 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/config/README.md +10 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/gates/README.md +12 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/gates/check_env.sh +23 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/gates/check_paths.sh +20 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/gates/check_platform.sh +23 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/harness/README.md +11 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/harness/envelopes/README.md +10 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/installer/README.md +10 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/installer/install.sh +125 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/telemetry/README.md +11 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/README.md +20 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/features/README.md +36 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/features/SKILL-template.md +14 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/git_ops/README.md +25 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/git_ops/SKILL.md +13 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/git_ops/create_branch.sh +6 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/git_ops/safe_commit.sh +9 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/git_ops/sync_upstream.sh +6 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/lifecycle/README.md +27 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/lifecycle/SKILL.md +15 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/lifecycle/smoke_test.sh +12 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/lifecycle/start.sh +19 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/lifecycle/status.sh +12 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/lifecycle/stop.sh +15 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/README.md +18 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/config/README.md +10 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/config/core_four.yaml +13 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/gates/README.md +12 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/gates/check_danger.sh +102 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/gates/check_env.sh +23 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/gates/check_paths.sh +20 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/gates/check_platform.sh +23 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/gates/check_wiring.sh +69 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/harness/README.md +11 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/harness/envelopes/README.md +10 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/harness/envelopes/result_envelope.json +14 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/harness/envelopes/task_envelope.json +20 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/harness/runner.py +303 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/installer/README.md +10 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/installer/install.sh +125 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/telemetry/README.md +11 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/telemetry/logger.py +112 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/BEST_PRACTICES/README.md +14 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/BEST_PRACTICES/api.md +17 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/BEST_PRACTICES/compliance-wiring.md +155 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/BEST_PRACTICES/frontend.md +15 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/README.md +11 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/deployment/README.md +23 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/deployment/clients/README.md +16 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/deployment/clients/template-README.md +11 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/deployment/deploy.sh +49 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/deployment/envs/README.md +12 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/deployment/envs/development.env.example +17 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/deployment/envs/production.env.example +17 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/deployment/envs/staging.env.example +17 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/deployment/tenants/README.md +11 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/deployment/tenants/template.env.example +8 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/DEVELOPER_SETUP/README.md +26 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/DEVELOPER_SETUP/agent.md +7 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/DEVELOPER_SETUP/developers/dev-1.md +20 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/DEVELOPER_SETUP/linux.md +9 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/DEVELOPER_SETUP/macos.md +10 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/DEVELOPER_SETUP/shared.md +19 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/DEVELOPER_SETUP/windows.md +14 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/HOSTING/README.md +25 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/HOSTING/_template.md +41 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/HOSTING/hosting-1.md +41 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/README.md +21 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/RUNBOOK/README.md +15 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/RUNBOOK/deployment.md +19 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/RUNBOOK/disaster-recovery.md +15 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/RUNBOOK/ops-tasks.md +14 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/features/README.md +16 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/project-skills.md +146 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/research/README.md +11 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/research/software-compliance-research.md +269 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/working-with-agents.md +260 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/root/AGENTS.md +38 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/root/ARCHITECTURE.md +36 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/root/BEST_PRACTICES.md +27 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/root/CI_CD.md +23 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/root/FEATURES.md +15 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/root/README.md +22 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/root/RULES.md +37 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/root/STRUCTURE.md +45 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/root/TECH_STACK.md +28 -0
- package/.agents/skills/nsr-compliance/assets/nsr/scaffold-spec.md +63 -0
- package/.agents/skills/nsr-compliance/assets/nsr/scripts/scaffold.sh +103 -0
- package/.agents/skills/nsr-compliance/assets/nsr/scripts/validate.sh +52 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/SKILL.md +71 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/config/core_four.yaml +16 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/gates/check_danger.sh +102 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/gates/check_wiring.sh +69 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/gates/validate_code.sh +29 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/gates/verify_docs.py +66 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/generate.sh +109 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/harness/envelopes/result_envelope.json +14 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/harness/envelopes/task_envelope.json +20 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/harness/runner.py +303 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/telemetry/logger.py +112 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/README.md +18 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/config/README.md +10 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/gates/README.md +12 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/gates/check_env.sh +23 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/gates/check_paths.sh +20 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/gates/check_platform.sh +23 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/harness/README.md +11 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/harness/envelopes/README.md +10 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/installer/README.md +10 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/installer/install.sh +125 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/telemetry/README.md +11 -0
- package/.agents/skills/pr-ops/README.md +3 -0
- package/.agents/skills/pr-ops/SKILL.md +109 -0
- package/.agents/skills/ratatoskr-a2a/README.md +3 -0
- package/.agents/skills/ratatoskr-a2a/SKILL.md +148 -0
- package/.agents/skills/rules-check-drift/SKILL.md +81 -0
- package/.agents/skills/saga-bearings/README.md +3 -0
- package/.agents/skills/saga-bearings/SKILL.md +26 -0
- package/.agents/skills/saga-bearings/assets/bearings.md +161 -0
- package/.agents/skills/saga-bearings/assets/board-template.html +731 -0
- package/.agents/skills/saga-bearings/assets/recap.md +56 -0
- package/.agents/skills/syn-recovery/README.md +3 -0
- package/.agents/skills/syn-recovery/SKILL.md +54 -0
- package/.agents/skills/tyr-check/README.md +3 -0
- package/.agents/skills/tyr-check/SKILL.md +218 -0
- package/.agents/skills/urdh-hold/README.md +3 -0
- package/.agents/skills/urdh-hold/SKILL.md +25 -0
- package/.agents/skills/urdh-hold/assets/decisions.md +15 -0
- package/.agents/skills/urdh-hold/assets/hold.md +54 -0
- package/.agents/skills/vor-diagnostics/README.md +3 -0
- package/.agents/skills/vor-diagnostics/SKILL.md +68 -0
- package/.agents/skills/ymir-host/README.md +3 -0
- package/.agents/skills/ymir-host/SKILL.md +34 -0
- package/.agents/skills/ymir-host/assets/desktop.md +36 -0
- package/.agents/skills/ymir-host/assets/omarchy.md +297 -0
- package/.agents/skills/ymir-host/assets/runbooks.md +15 -0
- package/.agents/skills/ymir-host/assets/thjazi.md +220 -0
- package/.agents/skills/ymir-host/assets/update.md +64 -0
- package/.agents/tests/assets/board-render-harness.mjs +117 -0
- package/.agents/tests/cmux-test-safety.sh +51 -0
- package/.agents/tests/fixtures.sh +291 -0
- package/.agents/tests/fm-afk-inject-e2e.test.sh +428 -0
- package/.agents/tests/fm-afk-inject-herdr-e2e.test.sh +536 -0
- package/.agents/tests/fm-afk-launch.test.sh +961 -0
- package/.agents/tests/fm-afk-pi-herdr-return-e2e.test.sh +297 -0
- package/.agents/tests/fm-afk-return.test.sh +283 -0
- package/.agents/tests/fm-arm-pretool-check.test.sh +482 -0
- package/.agents/tests/fm-ask-user-authority.test.sh +42 -0
- package/.agents/tests/fm-backend-autodetect-smoke.test.sh +176 -0
- package/.agents/tests/fm-backend-cmux-smoke.test.sh +188 -0
- package/.agents/tests/fm-backend-cmux.test.sh +1166 -0
- package/.agents/tests/fm-backend-herdr-eventwait-smoke.test.sh +136 -0
- package/.agents/tests/fm-backend-herdr-eventwait.test.py +106 -0
- package/.agents/tests/fm-backend-herdr-focus-flash-e2e.test.sh +409 -0
- package/.agents/tests/fm-backend-herdr-launcher-workspace-e2e.test.sh +437 -0
- package/.agents/tests/fm-backend-herdr-presentation-e2e.test.sh +1424 -0
- package/.agents/tests/fm-backend-herdr-prune-safety-e2e.test.sh +180 -0
- package/.agents/tests/fm-backend-herdr-respawn-idem-e2e.test.sh +182 -0
- package/.agents/tests/fm-backend-herdr-smoke.test.sh +355 -0
- package/.agents/tests/fm-backend-herdr-workspace-per-home-e2e.test.sh +251 -0
- package/.agents/tests/fm-backend-herdr.test.sh +4606 -0
- package/.agents/tests/fm-backend-orca.test.sh +1357 -0
- package/.agents/tests/fm-backend-tmux-smoke.test.sh +173 -0
- package/.agents/tests/fm-backend-zellij-smoke.test.sh +205 -0
- package/.agents/tests/fm-backend-zellij.test.sh +1360 -0
- package/.agents/tests/fm-backend.test.sh +1142 -0
- package/.agents/tests/fm-backlog-atomicity.test.sh +2302 -0
- package/.agents/tests/fm-backlog-handoff.test.sh +1360 -0
- package/.agents/tests/fm-bearings-board-render.test.sh +135 -0
- package/.agents/tests/fm-bearings-board.test.sh +414 -0
- package/.agents/tests/fm-bearings-snapshot.test.sh +1990 -0
- package/.agents/tests/fm-bootstrap-network-parallel.test.sh +316 -0
- package/.agents/tests/fm-bootstrap.test.sh +1178 -0
- package/.agents/tests/fm-branch-supervision.test.sh +552 -0
- package/.agents/tests/fm-brief.test.sh +785 -0
- package/.agents/tests/fm-busy-adapter-wiring.test.sh +324 -0
- package/.agents/tests/fm-busy-state.test.sh +465 -0
- package/.agents/tests/fm-calm-pi-extension.test.sh +3979 -0
- package/.agents/tests/fm-captain-hold-lifecycle.test.sh +1187 -0
- package/.agents/tests/fm-cd-pretool-check.test.sh +400 -0
- package/.agents/tests/fm-check-unregister.test.sh +198 -0
- package/.agents/tests/fm-classify-corr-token.test.sh +543 -0
- package/.agents/tests/fm-classify-decision-key.test.sh +340 -0
- package/.agents/tests/fm-claude-stop-autoarm-live-e2e.test.sh +164 -0
- package/.agents/tests/fm-claude-stop-autoarm.test.sh +1190 -0
- package/.agents/tests/fm-cmux-claude-composer-live-e2e.test.sh +110 -0
- package/.agents/tests/fm-codex-continuity-live-e2e.test.sh +55 -0
- package/.agents/tests/fm-composer-ghost.test.sh +716 -0
- package/.agents/tests/fm-composer-lib.test.sh +666 -0
- package/.agents/tests/fm-composer-matrix-live-e2e.test.sh +220 -0
- package/.agents/tests/fm-control-herdr-smoke.test.sh +149 -0
- package/.agents/tests/fm-control-relaunch.test.sh +1530 -0
- package/.agents/tests/fm-control.test.sh +910 -0
- package/.agents/tests/fm-crew-state.test.sh +1610 -0
- package/.agents/tests/fm-cursor-harness.test.sh +404 -0
- package/.agents/tests/fm-cursor-primary-live-e2e.test.sh +217 -0
- package/.agents/tests/fm-cursor-primary.test.sh +708 -0
- package/.agents/tests/fm-daemon.test.sh +2740 -0
- package/.agents/tests/fm-documentation-audiences.test.sh +141 -0
- package/.agents/tests/fm-ensure-agents-md.test.sh +371 -0
- package/.agents/tests/fm-extension-binding.test.sh +2187 -0
- package/.agents/tests/fm-fleet-snapshot-view.test.sh +816 -0
- package/.agents/tests/fm-fleet-sync.test.sh +721 -0
- package/.agents/tests/fm-gate-refuse.test.sh +356 -0
- package/.agents/tests/fm-gitignore-config.test.sh +89 -0
- package/.agents/tests/fm-gotmp.test.sh +230 -0
- package/.agents/tests/fm-grok-continuity-live-e2e.test.sh +112 -0
- package/.agents/tests/fm-grok-harness.test.sh +117 -0
- package/.agents/tests/fm-grok-stop-live-e2e.test.sh +219 -0
- package/.agents/tests/fm-guard-stale-banner.test.sh +709 -0
- package/.agents/tests/fm-harness-adapter-instructions-live-e2e.test.sh +134 -0
- package/.agents/tests/fm-harness-adapter-references.test.sh +30 -0
- package/.agents/tests/fm-harness-liveness-drift-live-e2e.test.sh +151 -0
- package/.agents/tests/fm-herdr-lab.test.sh +243 -0
- package/.agents/tests/fm-herdr-session-cleanup-e2e.test.sh +146 -0
- package/.agents/tests/fm-herdr-session-cleanup.test.sh +329 -0
- package/.agents/tests/fm-herdr-submit-confirm-live-e2e.test.sh +125 -0
- package/.agents/tests/fm-herdr-version-floor-live-e2e.test.sh +144 -0
- package/.agents/tests/fm-home-summary-refresh.test.sh +971 -0
- package/.agents/tests/fm-inactive-reconcile.test.sh +485 -0
- package/.agents/tests/fm-kimi-harness.test.sh +687 -0
- package/.agents/tests/fm-lint-workflows.test.sh +532 -0
- package/.agents/tests/fm-lint.test.sh +1023 -0
- package/.agents/tests/fm-muse-harness.test.sh +932 -0
- package/.agents/tests/fm-muse-signals-live-e2e.test.sh +205 -0
- package/.agents/tests/fm-no-mistakes-required.test.sh +72 -0
- package/.agents/tests/fm-on.test.sh +507 -0
- package/.agents/tests/fm-opencode-primary-live-e2e.test.sh +357 -0
- package/.agents/tests/fm-operational-input.test.sh +160 -0
- package/.agents/tests/fm-peek-remote.test.sh +110 -0
- package/.agents/tests/fm-pending-reply.test.sh +1288 -0
- package/.agents/tests/fm-pi-branch-extension.test.sh +3182 -0
- package/.agents/tests/fm-pi-branch-live-e2e.test.sh +527 -0
- package/.agents/tests/fm-pi-primary-live-e2e.test.sh +345 -0
- package/.agents/tests/fm-pi-primary-types.test.sh +68 -0
- package/.agents/tests/fm-pi-watch-extension.test.sh +2843 -0
- package/.agents/tests/fm-pr-check-security.test.sh +2140 -0
- package/.agents/tests/fm-pr-merge.test.sh +2135 -0
- package/.agents/tests/fm-procevent-quota.test.sh +225 -0
- package/.agents/tests/fm-procevent-when.test.sh +406 -0
- package/.agents/tests/fm-procevent.test.sh +1745 -0
- package/.agents/tests/fm-project-origin.test.sh +110 -0
- package/.agents/tests/fm-public-followup.test.sh +2272 -0
- package/.agents/tests/fm-quota-array-dispatch-live-e2e.test.sh +526 -0
- package/.agents/tests/fm-quota-choose.test.sh +626 -0
- package/.agents/tests/fm-remote-backlog-handoff.test.sh +453 -0
- package/.agents/tests/fm-remote-doctor.test.sh +625 -0
- package/.agents/tests/fm-remote-entrypoint.test.sh +59 -0
- package/.agents/tests/fm-remote-job-orphan-reap.test.sh +205 -0
- package/.agents/tests/fm-remote-job.test.sh +768 -0
- package/.agents/tests/fm-remote-reply.test.sh +520 -0
- package/.agents/tests/fm-remote-secondmate-lifecycle-e2e.test.sh +1256 -0
- package/.agents/tests/fm-remote-secondmate-parent-binding.test.sh +326 -0
- package/.agents/tests/fm-remote-secondmate-trace-context.test.sh +310 -0
- package/.agents/tests/fm-remote-transport-lanes.test.sh +431 -0
- package/.agents/tests/fm-review-diff.test.sh +176 -0
- package/.agents/tests/fm-secondmate-harness.test.sh +2600 -0
- package/.agents/tests/fm-secondmate-lifecycle-e2e.test.sh +259 -0
- package/.agents/tests/fm-secondmate-liveness.test.sh +559 -0
- package/.agents/tests/fm-secondmate-reconcile.test.sh +732 -0
- package/.agents/tests/fm-secondmate-safety.test.sh +2972 -0
- package/.agents/tests/fm-secondmate-sync.test.sh +913 -0
- package/.agents/tests/fm-send-inbox-doorbell-live-e2e.test.sh +206 -0
- package/.agents/tests/fm-send-inbox.test.sh +352 -0
- package/.agents/tests/fm-send-popup-settle.test.sh +167 -0
- package/.agents/tests/fm-send-remote-delivery.test.sh +796 -0
- package/.agents/tests/fm-send-resolve-key.test.sh +551 -0
- package/.agents/tests/fm-send-secondmate-marker-herdr-e2e.test.sh +187 -0
- package/.agents/tests/fm-send-secondmate-marker.test.sh +278 -0
- package/.agents/tests/fm-send-settle.test.sh +147 -0
- package/.agents/tests/fm-send-strict.test.sh +241 -0
- package/.agents/tests/fm-session-lock-ancestry.test.sh +365 -0
- package/.agents/tests/fm-session-start.test.sh +2515 -0
- package/.agents/tests/fm-sessionstart-hook-live-e2e.test.sh +631 -0
- package/.agents/tests/fm-sessionstart-instruction-refresh-live-e2e.test.sh +230 -0
- package/.agents/tests/fm-sessionstart-nudge.test.sh +1039 -0
- package/.agents/tests/fm-shared-captain-inheritance.test.sh +404 -0
- package/.agents/tests/fm-spawn-batch.test.sh +148 -0
- package/.agents/tests/fm-spawn-dispatch-profile.test.sh +827 -0
- package/.agents/tests/fm-spawn-pool-base-freshen.test.sh +440 -0
- package/.agents/tests/fm-spawn-worktree-settle.test.sh +147 -0
- package/.agents/tests/fm-startup-memory-budget.test.sh +333 -0
- package/.agents/tests/fm-startup-network.test.sh +712 -0
- package/.agents/tests/fm-stow-cascade.test.sh +370 -0
- package/.agents/tests/fm-subagent-pretool-check.test.sh +291 -0
- package/.agents/tests/fm-supervision-events.test.sh +156 -0
- package/.agents/tests/fm-supervision-instructions.test.sh +189 -0
- package/.agents/tests/fm-tangle-guard.test.sh +277 -0
- package/.agents/tests/fm-task-delivery.test.sh +420 -0
- package/.agents/tests/fm-task-inbox.test.sh +513 -0
- package/.agents/tests/fm-teardown-endpoint-safety.test.sh +374 -0
- package/.agents/tests/fm-teardown.test.sh +2665 -0
- package/.agents/tests/fm-test-fixture-cleanup.test.sh +172 -0
- package/.agents/tests/fm-test-fixtures.test.sh +132 -0
- package/.agents/tests/fm-test-isolation-proof.test.sh +278 -0
- package/.agents/tests/fm-test-run.test.sh +1196 -0
- package/.agents/tests/fm-tmux-agent-liveness.test.sh +354 -0
- package/.agents/tests/fm-tmux-submit-busy.test.sh +340 -0
- package/.agents/tests/fm-tool-update-check.test.sh +1044 -0
- package/.agents/tests/fm-trace-context-lib.test.sh +253 -0
- package/.agents/tests/fm-trace-context-spawn.test.sh +581 -0
- package/.agents/tests/fm-transition-lib.test.sh +54 -0
- package/.agents/tests/fm-turnend-guard.test.sh +1822 -0
- package/.agents/tests/fm-update.test.sh +304 -0
- package/.agents/tests/fm-vendor-auth-probe.test.sh +395 -0
- package/.agents/tests/fm-voice-relay.test.sh +4741 -0
- package/.agents/tests/fm-wake-daemon-lifecycle-e2e.test.sh +171 -0
- package/.agents/tests/fm-wake-drain-open-decisions-cursor.test.sh +356 -0
- package/.agents/tests/fm-wake-drain-open-decisions.test.sh +226 -0
- package/.agents/tests/fm-wake-drain-unread-status.test.sh +379 -0
- package/.agents/tests/fm-wake-queue.test.sh +1224 -0
- package/.agents/tests/fm-watch-arm.test.sh +821 -0
- package/.agents/tests/fm-watch-checkpoint.test.sh +87 -0
- package/.agents/tests/fm-watch-recovery-loop.test.sh +223 -0
- package/.agents/tests/fm-watch-triage.test.sh +3889 -0
- package/.agents/tests/fm-watcher-lock.test.sh +1133 -0
- package/.agents/tests/fm-x-mode.test.sh +3042 -0
- package/.agents/tests/gleipnir-guard-pretool-check.test.sh +51 -0
- package/.agents/tests/gleipnir-machine-lock.test.sh +130 -0
- package/.agents/tests/herdr-test-safety.sh +42 -0
- package/.agents/tests/lib.sh +315 -0
- package/.agents/tests/remote-herdr-fixture.sh +125 -0
- package/.agents/tests/secondmate-helpers.sh +193 -0
- package/.agents/tests/smoke.test.sh +93 -0
- package/.agents/tests/wake-helpers.sh +338 -0
- package/.agents/tests/zellij-test-safety.sh +60 -0
- package/.agents/tools/README.md +10 -0
- package/.agents/tools/bin/tasks-cli +267 -0
- package/.agents/tools/tasks-cli.ts +216 -0
- package/AGENTS.md +454 -0
- package/CHANGELOG.md +881 -0
- package/LICENSE +1 -1
- package/NOTICE +63 -0
- package/README.md +113 -39
- package/RULES/01-domains.md +51 -0
- package/RULES/02-agents.md +78 -0
- package/RULES/03-houses.md +61 -0
- package/RULES/04-hoard.md +65 -0
- package/RULES/05-platforms.md +67 -0
- package/RULES/06-append-only.md +58 -0
- package/RULES/07-config.md +45 -0
- package/RULES/08-delivery-gate.md +75 -0
- package/RULES/README.md +20 -0
- package/bin/README.md +107 -0
- package/bin/a2a-mcp.sh +98 -0
- package/bin/a2a-serve.py +103 -0
- package/bin/a2a-serve.sh +8 -0
- package/bin/a2a-talk.sh +50 -0
- package/bin/a2abridge-ensure.sh +108 -0
- package/bin/agent-run.sh +43 -0
- package/bin/agents-config.sh +233 -0
- package/bin/apodex-smoke-test.sh +92 -0
- package/bin/bifrost-bridge.sh +112 -0
- package/bin/bifrost-ingress.sh +77 -0
- package/bin/bootstrap-macos.sh +107 -0
- package/bin/bootstrap-windows.ps1 +112 -0
- package/bin/branch-guard.sh +64 -0
- package/bin/brokk +73 -0
- package/bin/brokk-classify-lib.sh +1770 -0
- package/bin/brokk-lease-lib.sh +222 -0
- package/bin/brokk-lease.sh +193 -0
- package/bin/brokk-lint.sh +65 -0
- package/bin/brokk-send.sh +38 -0
- package/bin/brokk-update.sh +9 -0
- package/bin/brokk-wake-grant.sh +127 -0
- package/bin/brokk-wake-lib.sh +1849 -0
- package/bin/changelog-guard.sh +165 -0
- package/bin/crash-sense.sh +160 -0
- package/bin/desktop-place.sh +224 -0
- package/bin/docs-guard.sh +39 -0
- package/bin/editor-place.sh +207 -0
- package/bin/eindri-acclaim.sh +34 -0
- package/bin/eindri-control.sh +28 -0
- package/bin/eindri-role.sh +83 -0
- package/bin/eindri-seen.sh +41 -0
- package/bin/eindri-send.sh +15 -0
- package/bin/eindri-start.sh +85 -0
- package/bin/eindri-watch.sh +57 -0
- package/bin/einherjar-spawn.sh +576 -0
- package/bin/eir-doctor.sh +112 -0
- package/bin/erindi-brief.sh +271 -0
- package/bin/fleet-apply.sh +68 -0
- package/bin/github-deploy.sh +81 -0
- package/bin/gjallarhorn-expose.sh +102 -0
- package/bin/gjallarhorn-notify.sh +58 -0
- package/bin/gjallarhorn-purge.sh +54 -0
- package/bin/gjallarhorn-tunnel.sh +72 -0
- package/bin/gleipnir-lock-lib.sh +284 -0
- package/bin/groa-update.sh +122 -0
- package/bin/hall-snapshot.sh +92 -0
- package/bin/hamr-harness.sh +246 -0
- package/bin/heimdall-ssh-keys.sh +374 -0
- package/bin/herdr-agents.py +44 -0
- package/bin/herdr-ensure.sh +104 -0
- package/bin/herdr-run.sh +431 -0
- package/bin/hermes-ensure.sh +88 -0
- package/bin/hoard-lib.sh +25 -0
- package/bin/hodd.sh +78 -0
- package/bin/host-sense.sh +199 -0
- package/bin/huginn-research-worker.sh +108 -0
- package/bin/local-model-lock.sh +51 -0
- package/bin/mimir-bridge.py +226 -0
- package/bin/mimir-bridge.sh +87 -0
- package/bin/mimir-ingest.sh +131 -0
- package/bin/mimir.sh +113 -0
- package/bin/mjollnir-webhook.sh +59 -0
- package/bin/mjollnir.sh +85 -0
- package/bin/model-hardware.sh +82 -0
- package/bin/model-resolve.sh +147 -0
- package/bin/models-detect.sh +178 -0
- package/bin/nornir-cron-start.sh +162 -0
- package/bin/nornir-job-daily-briefing.sh +191 -0
- package/bin/nornir-job-git-sync.sh +152 -0
- package/bin/nornir-job-memory-housekeeping.sh +114 -0
- package/bin/nornir-job-observer.sh +166 -0
- package/bin/nornir-job-skillopt-sleep.sh +76 -0
- package/bin/omarchy-hook-install.sh +60 -0
- package/bin/omarchy-install.sh +148 -0
- package/bin/omarchy-plugins.sh +174 -0
- package/bin/omarchy-sense.sh +149 -0
- package/bin/perm-guard.sh +30 -0
- package/bin/pi-agent.sh +68 -0
- package/bin/pi-ensure.sh +77 -0
- package/bin/pi-local.sh +37 -0
- package/bin/pi-seat.sh +63 -0
- package/bin/prereq-ensure.sh +166 -0
- package/bin/private-guard.sh +38 -0
- package/bin/project-git.sh +55 -0
- package/bin/public-guard.sh +70 -0
- package/bin/ratatoskr.sh +49 -0
- package/bin/realm-lib.sh +32 -0
- package/bin/repo-scrub.sh +62 -0
- package/bin/rodd-operational-input.sh +250 -0
- package/bin/runes-append.sh +198 -0
- package/bin/saga-session-start.sh +172 -0
- package/bin/saga-sessionstart-run.sh +79 -0
- package/bin/saga-wake-drain.sh +41 -0
- package/bin/secret-guard.sh +63 -0
- package/bin/sessrumnir-ensure.sh +100 -0
- package/bin/sessrumnir-sync.sh +237 -0
- package/bin/sessrumnir.sh +91 -0
- package/bin/skillopt-setup.sh +76 -0
- package/bin/skuld-branch-outcome.sh +203 -0
- package/bin/skuld-branch-prompt.sh +101 -0
- package/bin/smidja-bootstrap.sh +90 -0
- package/bin/smidja-observe.sh +86 -0
- package/bin/syn-arm-pretool-check.sh +57 -0
- package/bin/syn-asset-pretool-check.sh +76 -0
- package/bin/syn-cd-pretool-check.sh +28 -0
- package/bin/syn-guard-pretool-check.sh +83 -0
- package/bin/syn-turnend-guard.sh +36 -0
- package/bin/syn-watch-arm.sh +99 -0
- package/bin/tailscale-sync.sh +190 -0
- package/bin/telegram-bot.sh +69 -0
- package/bin/toolchain.sh +74 -0
- package/bin/utgard.sh +111 -0
- package/bin/valhalla.sh +115 -0
- package/bin/valknut-load.sh +260 -0
- package/bin/vor-crew-state.sh +124 -0
- package/bin/wedge-notify.sh +47 -0
- package/bin/workspace-provision.sh +80 -0
- package/bin/workspace-rag.sh +150 -0
- package/bin/wyrd-db.sh +47 -0
- package/bin/yggdrasil.sh +152 -0
- package/bin/ymir-edit.sh +145 -0
- package/bin/ymir-install.sh +641 -0
- package/bin/ymir-invite.sh +93 -0
- package/bin/ymir-isolation.sh +88 -0
- package/bin/ymir-migrate.sh +61 -0
- package/bin/ymir-platform.sh +348 -0
- package/bin/ymir-say.sh +97 -0
- package/bin/ymir-setup-auth.sh +108 -0
- package/bin/ymir-validate.sh +173 -0
- package/bin/ymir.js +74 -16
- package/docs/Architecture.md +339 -0
- package/docs/design.md +351 -0
- package/docs/installations/README.md +63 -0
- package/docs/installations/ymir-home.md +93 -0
- package/docs/integration.md +105 -0
- package/docs/lore.md +1056 -0
- package/docs/research/agent-harnesses.md +290 -0
- package/docs/runbooks/README.md +19 -0
- package/docs/runbooks/a2a.md +91 -0
- package/docs/runbooks/agent-permissions.md +99 -0
- package/docs/runbooks/agents.md +57 -0
- package/docs/runbooks/models.md +69 -0
- package/docs/runbooks/realm-onboarding.md +60 -0
- package/docs/runbooks/secrets-and-hoard.md +60 -0
- package/docs/runbooks/updates-and-migrations.md +110 -0
- package/docs/runbooks/worktrees-isolation.md +72 -0
- package/docs/session-start.md +105 -0
- package/docs/skillopt-integration.md +321 -0
- package/docs/sync-system.md +66 -0
- package/docs/workspaces.md +104 -0
- package/docs/ymir-rut.md +260 -0
- package/package.json +44 -14
- package/packaging/build.sh +74 -0
- package/packaging/macos/Ymir Installer.command +43 -0
- package/packaging/windows/ymir-setup.nsi +74 -0
- package/scripts/README.md +22 -0
- package/scripts/electron.sh +210 -0
- package/scripts/start.sh +167 -0
- package/scripts/stop.sh +113 -0
- package/install.sh +0 -35
|
@@ -0,0 +1,986 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# fm-busy-lib.sh - the ONE owner of firstmate's semantic busy-state contract.
|
|
3
|
+
#
|
|
4
|
+
# Design source: the captain-approved semantic busy-state redesign
|
|
5
|
+
# (2026-07-28): each harness adapter reports turn lifecycle through a
|
|
6
|
+
# machine-readable semantic source it owns, classification always exposes
|
|
7
|
+
# which source produced it, and missing, malformed, stale, unsupported, or
|
|
8
|
+
# unverified semantic data is UNKNOWN - never idle. Endpoint death is the only
|
|
9
|
+
# process-level override and yields dead, never busy. Child processes, CPU,
|
|
10
|
+
# process sleep state, marker mtimes, and the old global UI-regex OR are not
|
|
11
|
+
# state signals here; state/<id>.turn-ended files remain wake NOTIFICATIONS
|
|
12
|
+
# owned by the watcher, not current-state truth.
|
|
13
|
+
#
|
|
14
|
+
# Record file: state/<id>.busy-state - exactly one line, atomically replaced
|
|
15
|
+
# by bin/fm-busy-event.sh (the only writer):
|
|
16
|
+
#
|
|
17
|
+
# v1 gen=<token> seq=<uint> state=<busy|idle|unknown> source=<token> event=<token> ts=<epoch>
|
|
18
|
+
#
|
|
19
|
+
# Gen sidecar: state/<id>.busy-gen - one token minted when the task's busy
|
|
20
|
+
# wiring is armed (fm-spawn, or a documented recovery re-arm). Every event
|
|
21
|
+
# must present the current gen; an event or record carrying any other gen is
|
|
22
|
+
# a stale incarnation and is rejected (written events) or classified unknown
|
|
23
|
+
# (read records). seq is a strictly increasing integer per gen, advanced
|
|
24
|
+
# under the writer's lock, so an out-of-order apply can never regress a
|
|
25
|
+
# newer record.
|
|
26
|
+
#
|
|
27
|
+
# Semantic sources written by adapters (fm_busy_sources_for_harness owns the
|
|
28
|
+
# per-harness trust table; a record whose source is not trusted for the
|
|
29
|
+
# task's recorded harness classifies unknown, so one adapter's writer can
|
|
30
|
+
# never classify another adapter):
|
|
31
|
+
# pi-ext Pi/pi-signed per-task extension (agent_start/agent_settled)
|
|
32
|
+
# opencode-plugin OpenCode per-task plugin (session.status)
|
|
33
|
+
# claude-hook Claude lifecycle hooks (UserPromptSubmit/Stop/StopFailure/SessionEnd)
|
|
34
|
+
# codex-hook, codex-appserver reserved: Codex, gated by
|
|
35
|
+
# fm_busy_codex_semantic_source
|
|
36
|
+
# kimi-wire, kimi-hook reserved: standalone Kimi, gated by fm_busy_kimi_verified
|
|
37
|
+
# Firstmate-owned sources accepted for every converted adapter:
|
|
38
|
+
# fm-spawn the launch-brief turn seeded at spawn
|
|
39
|
+
# fm-interrupt the legacy Claude fm-send --key Escape idle event
|
|
40
|
+
# fm-recovery a documented recovery reset after relaunch
|
|
41
|
+
# Classifier-only sources (never written into a record):
|
|
42
|
+
# endpoint-gone, herdr-native, grok-regex, muse-session-log,
|
|
43
|
+
# cursor-transcript, missing, malformed, gen-mismatch, source-mismatch,
|
|
44
|
+
# kimi-unverified, codex-unverified, capture-failed, no-target
|
|
45
|
+
#
|
|
46
|
+
# Classification (fm_busy_classify): busy | idle | unknown | dead, always
|
|
47
|
+
# with the producing source as the second token. Precedence:
|
|
48
|
+
# 1. dead endpoint (fm_busy_classify_live only) -> dead endpoint-gone
|
|
49
|
+
# 2. standalone Kimi before verification -> unknown kimi-unverified
|
|
50
|
+
# 3. a valid, gen-matching, source-trusted record -> its state and source
|
|
51
|
+
# 4. no record at all: herdr's native busy verdict is trusted as busy
|
|
52
|
+
# (generation state is sufficient for busy, not for idle), then the
|
|
53
|
+
# muse session-log and cursor transcript pull sources, then the Grok-only
|
|
54
|
+
# temporary regex fallback classifies a grok task from its rendered tail,
|
|
55
|
+
# then unknown missing
|
|
56
|
+
# 5. malformed, stale, or untrusted records -> unknown, never a fallback
|
|
57
|
+
# The Grok arm is the ONLY rendered-text classification that survives the
|
|
58
|
+
# redesign, because Grok's structured lifecycle was not credited-live-verified
|
|
59
|
+
# in the approved audit; it is scoped to harness=grok and can never classify
|
|
60
|
+
# another adapter. The delivery guards in bin/fm-composer-lib.sh match rendered
|
|
61
|
+
# footers for submit acknowledgement and away-mode supervisor injection only;
|
|
62
|
+
# neither is a recorded worker state source.
|
|
63
|
+
#
|
|
64
|
+
# The muse pull source is semantic, not rendered: it folds muse's own durable
|
|
65
|
+
# session event log. It has no writer, no arm, and no gen, because
|
|
66
|
+
# muse's default build ships no hook or plugin surface that could push events
|
|
67
|
+
# (its plugin engine reports "plugins are not available in this build" without
|
|
68
|
+
# MUSE_EXPERIMENTAL_PLUGINS). Nothing is armed for muse for the same reason
|
|
69
|
+
# standalone Kimi is not: a seeded record with no writer could never be
|
|
70
|
+
# cleared. See fm_busy_muse_run_state for the fold.
|
|
71
|
+
#
|
|
72
|
+
# The cursor pull source works the same way and for the same reason: it folds
|
|
73
|
+
# cursor's own durable per-conversation transcript, which brackets each turn
|
|
74
|
+
# with a role:user open and a typed turn_ended close that covers aborts. It has
|
|
75
|
+
# no writer, no arm, and no gen, so nothing is seeded that could never be
|
|
76
|
+
# cleared. See fm_busy_cursor_turn_state for the fold. Cursor's rendered
|
|
77
|
+
# `ctrl+c to stop` footer is deliberately not a state source here.
|
|
78
|
+
#
|
|
79
|
+
# Codex negotiation (fm_busy_codex_appserver_observable,
|
|
80
|
+
# fm_busy_codex_hooks_verified): the approved contract prefers Codex's
|
|
81
|
+
# app-server turn lifecycle with capability negotiation, and sanctions its
|
|
82
|
+
# stable lifecycle hooks as the intermediate. Neither is usable on the
|
|
83
|
+
# installed binary, so Codex classifies unknown codex-unverified rather than
|
|
84
|
+
# falling back to idle, and fm-spawn installs no Codex busy wiring.
|
|
85
|
+
# docs/verification/supervision.md owns the evidence for both probes.
|
|
86
|
+
#
|
|
87
|
+
# Sourcing: set -u and set -e safe; no subshell-unfriendly globals.
|
|
88
|
+
|
|
89
|
+
FM_BUSY_LIB_VERSION=v1
|
|
90
|
+
|
|
91
|
+
# Standalone-Kimi verification gate. Empty means no installed Kimi version
|
|
92
|
+
# has passed live verification, so every standalone Kimi task classifies
|
|
93
|
+
# unknown kimi-unverified and fm-spawn wires no Kimi busy events. Kimi's
|
|
94
|
+
# rendered moon-phase spinner is deliberately NOT a state source here: the
|
|
95
|
+
# approved redesign forbids inventing a Kimi UI signature, and that spinner
|
|
96
|
+
# is locale- and emoji-font-sensitive.
|
|
97
|
+
#
|
|
98
|
+
# Preferred source, in order: Wire mode's JSON-RPC `prompt` request lifetime,
|
|
99
|
+
# whose outstanding request exactly brackets a turn and returns finished,
|
|
100
|
+
# cancelled, or max_steps_reached (so it covers interruption, which `Stop`
|
|
101
|
+
# does not); then the documented lifecycle hooks, which must include
|
|
102
|
+
# `Interrupt` because Kimi documents that `Stop` does not fire on interrupts.
|
|
103
|
+
#
|
|
104
|
+
# To open the gate: install Kimi, live-verify the chosen source brackets a
|
|
105
|
+
# real turn on a firstmate-launched worker including the interrupt path,
|
|
106
|
+
# record the version, exact commands, and observed output in
|
|
107
|
+
# docs/verification/supervision.md, add the verified version string(s) here,
|
|
108
|
+
# and land the wiring in fm-spawn behind this same gate in the same change.
|
|
109
|
+
FM_BUSY_KIMI_VERIFIED_VERSIONS=""
|
|
110
|
+
|
|
111
|
+
fm_busy_kimi_verified() {
|
|
112
|
+
[ -n "$FM_BUSY_KIMI_VERIFIED_VERSIONS" ]
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
# fm_busy_codex_appserver_observable: capability/version negotiation for the
|
|
116
|
+
# Codex app-server turn lifecycle. Returns 0 only when a pane worker's turns
|
|
117
|
+
# are observable through the app-server protocol on the installed binary.
|
|
118
|
+
# codex-cli 0.145.0 verdict (live, 2026-07-28): NOT observable. The v2
|
|
119
|
+
# protocol does define the needed turn lifecycle (turn/started plus a
|
|
120
|
+
# turn/completed status of completed, interrupted, failed, or inProgress),
|
|
121
|
+
# but an interactive TUI worker neither starts nor attaches to the
|
|
122
|
+
# app-server daemon, and `codex app-server daemon start` refuses outside the
|
|
123
|
+
# managed standalone install, so no client can observe a pane worker's turns.
|
|
124
|
+
fm_busy_codex_appserver_observable() {
|
|
125
|
+
return 1
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
# fm_busy_codex_hooks_verified: the sanctioned intermediate - Codex's stable
|
|
129
|
+
# hooks engine (UserPromptSubmit to open a turn, Stop and SessionEnd to close
|
|
130
|
+
# it). Returns 0 only once those hooks are live-verified to fire for a
|
|
131
|
+
# firstmate-launched worker. codex-cli 0.145.0 verdict (live, 2026-07-28):
|
|
132
|
+
# NOT verified. Firstmate-written project hooks under <worktree>/.codex/
|
|
133
|
+
# never fired in an interactive pane whose directory trust was granted, nor
|
|
134
|
+
# under `codex exec`, in either case with --dangerously-bypass-hook-trust,
|
|
135
|
+
# while global hooks fired in the same runs. Codex additionally exposes no
|
|
136
|
+
# StopFailure hook, so an API-error turn end would need separate coverage
|
|
137
|
+
# even after the discovery problem is solved.
|
|
138
|
+
fm_busy_codex_hooks_verified() {
|
|
139
|
+
return 1
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
# fm_busy_codex_semantic_source: 0 when ANY verified Codex semantic source
|
|
143
|
+
# exists. fm-spawn arms and wires Codex only behind this gate, and the
|
|
144
|
+
# classifier reports unknown codex-unverified until it opens.
|
|
145
|
+
fm_busy_codex_semantic_source() {
|
|
146
|
+
fm_busy_codex_appserver_observable || fm_busy_codex_hooks_verified
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
fm_busy_record_path() { # <state-dir> <id>
|
|
150
|
+
printf '%s/%s.busy-state' "$1" "$2"
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
fm_busy_gen_path() { # <state-dir> <id>
|
|
154
|
+
printf '%s/%s.busy-gen' "$1" "$2"
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
# fm_busy_token_valid: conservative token charset shared by gen, source, and
|
|
158
|
+
# event fields. Anything else is malformed.
|
|
159
|
+
fm_busy_token_valid() { # <value>
|
|
160
|
+
case "${1:-}" in
|
|
161
|
+
''|*[!A-Za-z0-9._-]*) return 1 ;;
|
|
162
|
+
esac
|
|
163
|
+
return 0
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
# fm_busy_current_gen: the task's armed gen token, or failure when the busy
|
|
167
|
+
# contract has never been armed for this task.
|
|
168
|
+
fm_busy_current_gen() { # <state-dir> <id>
|
|
169
|
+
local gen_file gen
|
|
170
|
+
gen_file=$(fm_busy_gen_path "$1" "$2")
|
|
171
|
+
[ -f "$gen_file" ] || return 1
|
|
172
|
+
IFS= read -r gen < "$gen_file" 2>/dev/null || gen=
|
|
173
|
+
fm_busy_token_valid "$gen" || return 1
|
|
174
|
+
printf '%s' "$gen"
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
# fm_busy_sources_for_harness: the semantic sources trusted to classify a
|
|
178
|
+
# task recorded with <harness>. One line, space-separated, possibly empty.
|
|
179
|
+
# The firstmate-owned sources are appended for every converted adapter.
|
|
180
|
+
# Grok and muse deliberately trust nothing: neither has a semantic WRITER, so
|
|
181
|
+
# neither is armed, and both read their live source on demand in the classifier
|
|
182
|
+
# (grok's rendered tail, muse's session log) rather than through a stored
|
|
183
|
+
# record. Listing a source here without a writer that can clear it would seed a
|
|
184
|
+
# busy record nothing could ever settle.
|
|
185
|
+
fm_busy_sources_for_harness() { # <harness>
|
|
186
|
+
local adapter=
|
|
187
|
+
case "${1:-}" in
|
|
188
|
+
claude*) adapter=claude-hook ;;
|
|
189
|
+
codex*)
|
|
190
|
+
fm_busy_codex_semantic_source || { printf ''; return 0; }
|
|
191
|
+
adapter='codex-hook codex-appserver'
|
|
192
|
+
;;
|
|
193
|
+
opencode*) adapter=opencode-plugin ;;
|
|
194
|
+
pi|pi-signed) adapter=pi-ext ;;
|
|
195
|
+
kimi*)
|
|
196
|
+
fm_busy_kimi_verified || { printf ''; return 0; }
|
|
197
|
+
adapter='kimi-wire kimi-hook'
|
|
198
|
+
;;
|
|
199
|
+
*) printf ''; return 0 ;;
|
|
200
|
+
esac
|
|
201
|
+
printf '%s fm-spawn fm-interrupt fm-recovery' "$adapter"
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
fm_busy_source_trusted() { # <harness> <source>
|
|
205
|
+
local trusted
|
|
206
|
+
trusted=$(fm_busy_sources_for_harness "$1")
|
|
207
|
+
case " $trusted " in
|
|
208
|
+
*" $2 "*) return 0 ;;
|
|
209
|
+
esac
|
|
210
|
+
return 1
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
# fm_busy_record_read: parse and validate state/<id>.busy-state against the
|
|
214
|
+
# armed gen. Prints "<state> <source> <event> <seq>" for a valid record.
|
|
215
|
+
# Non-zero returns name the reason on stdout instead:
|
|
216
|
+
# missing no record file (or no armed gen and no record)
|
|
217
|
+
# malformed unparseable line, bad tokens, or a missing armed gen for an
|
|
218
|
+
# existing record
|
|
219
|
+
# gen-mismatch a record from a stale incarnation
|
|
220
|
+
fm_busy_record_read() { # <state-dir> <id>
|
|
221
|
+
local state=$1 id=$2 rec gen line extra ver f
|
|
222
|
+
local r_gen='' r_seq='' r_state='' r_source='' r_event='' r_ts=''
|
|
223
|
+
rec=$(fm_busy_record_path "$state" "$id")
|
|
224
|
+
if [ ! -f "$rec" ]; then
|
|
225
|
+
printf 'missing'
|
|
226
|
+
return 1
|
|
227
|
+
fi
|
|
228
|
+
if ! gen=$(fm_busy_current_gen "$state" "$id"); then
|
|
229
|
+
# A record without an armed gen has no incarnation to bind to.
|
|
230
|
+
printf 'malformed'
|
|
231
|
+
return 1
|
|
232
|
+
fi
|
|
233
|
+
# shellcheck disable=SC2034 # extra exists only to prove the record is one line
|
|
234
|
+
{ IFS= read -r line && ! IFS= read -r extra; } < "$rec" 2>/dev/null || {
|
|
235
|
+
printf 'malformed'
|
|
236
|
+
return 1
|
|
237
|
+
}
|
|
238
|
+
# `read -a` rather than `set --`: it never glob-expands a field and never
|
|
239
|
+
# touches the caller's positional parameters or shell options.
|
|
240
|
+
local -a fields
|
|
241
|
+
IFS=' ' read -r -a fields <<< "$line"
|
|
242
|
+
ver=${fields[0]:-}
|
|
243
|
+
[ "$ver" = "$FM_BUSY_LIB_VERSION" ] || { printf 'malformed'; return 1; }
|
|
244
|
+
for f in "${fields[@]:1}"; do
|
|
245
|
+
case "$f" in
|
|
246
|
+
gen=*) r_gen=${f#gen=} ;;
|
|
247
|
+
seq=*) r_seq=${f#seq=} ;;
|
|
248
|
+
state=*) r_state=${f#state=} ;;
|
|
249
|
+
source=*) r_source=${f#source=} ;;
|
|
250
|
+
event=*) r_event=${f#event=} ;;
|
|
251
|
+
ts=*) r_ts=${f#ts=} ;;
|
|
252
|
+
*) printf 'malformed'; return 1 ;;
|
|
253
|
+
esac
|
|
254
|
+
done
|
|
255
|
+
fm_busy_token_valid "$r_gen" || { printf 'malformed'; return 1; }
|
|
256
|
+
fm_busy_token_valid "$r_source" || { printf 'malformed'; return 1; }
|
|
257
|
+
fm_busy_token_valid "$r_event" || { printf 'malformed'; return 1; }
|
|
258
|
+
case "$r_seq" in ''|*[!0-9]*) printf 'malformed'; return 1 ;; esac
|
|
259
|
+
case "$r_ts" in ''|*[!0-9]*) printf 'malformed'; return 1 ;; esac
|
|
260
|
+
case "$r_state" in busy|idle|unknown) : ;; *) printf 'malformed'; return 1 ;; esac
|
|
261
|
+
if [ "$r_gen" != "$gen" ]; then
|
|
262
|
+
printf 'gen-mismatch'
|
|
263
|
+
return 1
|
|
264
|
+
fi
|
|
265
|
+
printf '%s %s %s %s' "$r_state" "$r_source" "$r_event" "$r_seq"
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
# ---------------------------------------------------------------------------
|
|
269
|
+
# muse session-log busy source
|
|
270
|
+
#
|
|
271
|
+
# muse persists an append-only session event log per session at
|
|
272
|
+
# <sessions-root>/YYYY/MM/DD/<session-uuid>/session.jsonl, and brackets every
|
|
273
|
+
# submitted turn with one run lifecycle pair. Verified live on muse
|
|
274
|
+
# 0.1.0-R708.1 across completed, interrupted, and killed-mid-turn turns:
|
|
275
|
+
# {"payload":{"kind":"run","run_id":"<uuid>","event":{"kind":"started",...
|
|
276
|
+
# {"payload":{"kind":"run","run_id":"<uuid>","event":{"kind":"terminal",
|
|
277
|
+
# "terminal":"completed"|"cancelled",...
|
|
278
|
+
# An Escape interrupt closes its run with terminal=cancelled, so unlike Claude's
|
|
279
|
+
# Stop hook this source covers the interrupt path itself. Any later
|
|
280
|
+
# run_retracted records follow the terminal rather than replacing it.
|
|
281
|
+
#
|
|
282
|
+
# Both halves of the fold are trusted. An open run is positive proof a turn is
|
|
283
|
+
# in flight, and a settled log is idle: the credentialed multi-step smoke showed
|
|
284
|
+
# one run pair spans a whole multi-step turn, including an Escape interrupt that
|
|
285
|
+
# closes the run with terminal=cancelled instead of continuing the turn in
|
|
286
|
+
# another run. This gives the settled log the same idle trust as the Claude and
|
|
287
|
+
# Pi push sources. A version allowlist would be false precision and a maintenance
|
|
288
|
+
# treadmill for an auto-updating vendor binary: busy classification receives
|
|
289
|
+
# only the normalized muse harness identity, while session metadata records
|
|
290
|
+
# semver 0.1.0 plus a build SHA that cannot be matched against it. Resolution
|
|
291
|
+
# failures - no sidecar, no matching log, an unreadable or run-free log - remain
|
|
292
|
+
# unknown because those prove nothing about the turn either way. See
|
|
293
|
+
# docs/verification/muse.md for the evidence.
|
|
294
|
+
# fm_busy_muse_binding_path: the per-task sidecar fm-spawn writes so the
|
|
295
|
+
# classifier binds a pane to its session log without re-deriving muse's data
|
|
296
|
+
# directory. It records sessions_root=<abs>, workspace_root=<abs>, one
|
|
297
|
+
# binding_id=<token>, and one prior_log=<abs> for each matching main log that
|
|
298
|
+
# predates this pane.
|
|
299
|
+
fm_busy_muse_binding_path() { # <state-dir> <id>
|
|
300
|
+
printf '%s/%s.muse-session' "$1" "$2"
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
fm_busy_muse_cache_path() { # <state-dir> <id>
|
|
304
|
+
printf '%s/%s.muse-session-current' "$1" "$2"
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
# fm_busy_muse_binding_field: read one field from the sidecar, or fail.
|
|
308
|
+
fm_busy_muse_binding_field() { # <state-dir> <id> <key>
|
|
309
|
+
local path line key=$3
|
|
310
|
+
path=$(fm_busy_muse_binding_path "$1" "$2")
|
|
311
|
+
[ -f "$path" ] || return 1
|
|
312
|
+
while IFS= read -r line || [ -n "$line" ]; do
|
|
313
|
+
case "$line" in
|
|
314
|
+
"$key="*)
|
|
315
|
+
line=${line#"$key="}
|
|
316
|
+
[ -n "$line" ] || return 1
|
|
317
|
+
printf '%s' "$line"
|
|
318
|
+
return 0
|
|
319
|
+
;;
|
|
320
|
+
esac
|
|
321
|
+
done < "$path"
|
|
322
|
+
return 1
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
# fm_busy_muse_matching_logs: every MAIN session log whose recorded
|
|
326
|
+
# workspace_root is this task's worktree. The depth bounds are what exclude
|
|
327
|
+
# muse's own native sub-agent logs, which live one directory deeper under
|
|
328
|
+
# subagent/<child-session-id>/session.jsonl and carry their own independent run
|
|
329
|
+
# lifecycle - folding a child's log would report the parent busy long after the
|
|
330
|
+
# parent's turn ended.
|
|
331
|
+
fm_busy_muse_matching_logs() { # <sessions-root> <workspace-root>
|
|
332
|
+
local root=$1 ws=$2
|
|
333
|
+
[ -d "$root" ] || return 1
|
|
334
|
+
command -v node >/dev/null 2>&1 || return 1
|
|
335
|
+
node - "$root" "$ws" <<'NODE'
|
|
336
|
+
const fs = require("fs");
|
|
337
|
+
const path = require("path");
|
|
338
|
+
const [root, workspace] = process.argv.slice(2);
|
|
339
|
+
|
|
340
|
+
function directories(parent) {
|
|
341
|
+
try {
|
|
342
|
+
return fs.readdirSync(parent, { withFileTypes: true })
|
|
343
|
+
.filter((entry) => entry.isDirectory())
|
|
344
|
+
.map((entry) => path.join(parent, entry.name));
|
|
345
|
+
} catch {
|
|
346
|
+
return [];
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
function metadataWorkspace(file) {
|
|
351
|
+
let descriptor;
|
|
352
|
+
try {
|
|
353
|
+
descriptor = fs.openSync(file, "r");
|
|
354
|
+
const buffer = Buffer.alloc(65536);
|
|
355
|
+
const length = fs.readSync(descriptor, buffer, 0, buffer.length, 0);
|
|
356
|
+
const newline = buffer.indexOf(10, 0);
|
|
357
|
+
if (newline < 0 || newline >= length) return null;
|
|
358
|
+
const record = JSON.parse(buffer.subarray(0, newline).toString("utf8"));
|
|
359
|
+
return record?.payload?.record?.workspace_root ?? null;
|
|
360
|
+
} catch {
|
|
361
|
+
return null;
|
|
362
|
+
} finally {
|
|
363
|
+
if (descriptor !== undefined) fs.closeSync(descriptor);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
for (const year of directories(root)) {
|
|
368
|
+
for (const month of directories(year)) {
|
|
369
|
+
for (const day of directories(month)) {
|
|
370
|
+
for (const session of directories(day)) {
|
|
371
|
+
const file = path.join(session, "session.jsonl");
|
|
372
|
+
try {
|
|
373
|
+
if (!fs.lstatSync(file).isFile()) continue;
|
|
374
|
+
} catch {
|
|
375
|
+
continue;
|
|
376
|
+
}
|
|
377
|
+
if (metadataWorkspace(file) === workspace) process.stdout.write(`${file}\n`);
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
NODE
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
fm_busy_muse_binding_has_prior_log() { # <state-dir> <id> <session-log>
|
|
386
|
+
local path line
|
|
387
|
+
path=$(fm_busy_muse_binding_path "$1" "$2")
|
|
388
|
+
[ -f "$path" ] || return 1
|
|
389
|
+
while IFS= read -r line || [ -n "$line" ]; do
|
|
390
|
+
[ "$line" = "prior_log=$3" ] && return 0
|
|
391
|
+
done < "$path"
|
|
392
|
+
return 1
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
fm_busy_muse_cache_field() { # <state-dir> <id> <key>
|
|
396
|
+
local path line key=$3
|
|
397
|
+
path=$(fm_busy_muse_cache_path "$1" "$2")
|
|
398
|
+
[ -f "$path" ] || return 1
|
|
399
|
+
while IFS= read -r line || [ -n "$line" ]; do
|
|
400
|
+
case "$line" in
|
|
401
|
+
"$key="*)
|
|
402
|
+
line=${line#"$key="}
|
|
403
|
+
[ -n "$line" ] || return 1
|
|
404
|
+
printf '%s' "$line"
|
|
405
|
+
return 0
|
|
406
|
+
;;
|
|
407
|
+
esac
|
|
408
|
+
done < "$path"
|
|
409
|
+
return 1
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
fm_busy_muse_main_log_path_valid() { # <sessions-root> <session-log>
|
|
413
|
+
local root=${1%/} log=$2 rel year month day session leaf
|
|
414
|
+
while :; do
|
|
415
|
+
case "$root" in
|
|
416
|
+
*'//'*) root=${root//\/\//\/} ;;
|
|
417
|
+
*) break ;;
|
|
418
|
+
esac
|
|
419
|
+
done
|
|
420
|
+
[ -n "$root" ] && [ -f "$log" ] && [ ! -L "$log" ] || return 1
|
|
421
|
+
case "$log" in
|
|
422
|
+
"$root"/*) rel=${log#"$root"/} ;;
|
|
423
|
+
*) return 1 ;;
|
|
424
|
+
esac
|
|
425
|
+
year=${rel%%/*}; rel=${rel#*/}
|
|
426
|
+
month=${rel%%/*}; rel=${rel#*/}
|
|
427
|
+
day=${rel%%/*}; rel=${rel#*/}
|
|
428
|
+
session=${rel%%/*}; leaf=${rel#*/}
|
|
429
|
+
[ -n "$year" ] && [ -n "$month" ] && [ -n "$day" ] && [ -n "$session" ] \
|
|
430
|
+
&& [ "$leaf" = session.jsonl ]
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
fm_busy_muse_namespace_day() { # <sessions-root>
|
|
434
|
+
printf '%s/%s' "${1%/}" "$(date '+%Y/%m/%d')"
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
fm_busy_muse_namespace_signature() { # <day-directory>
|
|
438
|
+
local first first_signature manifest='' path paths signature
|
|
439
|
+
if [ ! -d "$1" ]; then
|
|
440
|
+
printf '%s' missing
|
|
441
|
+
return 0
|
|
442
|
+
fi
|
|
443
|
+
paths=$(find "$1" -mindepth 2 -maxdepth 2 -type f -name session.jsonl -print 2>/dev/null) \
|
|
444
|
+
|| return 1
|
|
445
|
+
paths=$(printf '%s\n' "$paths" | LC_ALL=C sort) || return 1
|
|
446
|
+
while IFS= read -r path; do
|
|
447
|
+
[ -n "$path" ] || continue
|
|
448
|
+
first=$(sed -n '1p' "$path") || return 1
|
|
449
|
+
first_signature=$(printf '%s' "$first" | cksum | awk '{ print $1 ":" $2 }') || return 1
|
|
450
|
+
manifest="${manifest}${path}:${first_signature}
|
|
451
|
+
"
|
|
452
|
+
done <<EOF
|
|
453
|
+
$paths
|
|
454
|
+
EOF
|
|
455
|
+
signature=$(printf '%s' "$manifest" | cksum | awk '{ print $1 ":" $2 }') || return 1
|
|
456
|
+
[ -n "$signature" ] || return 1
|
|
457
|
+
printf '%s' "$signature"
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
fm_busy_muse_cached_session_log() { # <state-dir> <id> <root> <binding-id>
|
|
461
|
+
local cache_binding log cache_day cache_signature day signature
|
|
462
|
+
[ -n "$4" ] || return 1
|
|
463
|
+
cache_binding=$(fm_busy_muse_cache_field "$1" "$2" binding_id) || return 1
|
|
464
|
+
[ "$cache_binding" = "$4" ] || return 1
|
|
465
|
+
log=$(fm_busy_muse_cache_field "$1" "$2" session_log) || return 1
|
|
466
|
+
fm_busy_muse_main_log_path_valid "$3" "$log" || return 1
|
|
467
|
+
fm_busy_muse_binding_has_prior_log "$1" "$2" "$log" && return 1
|
|
468
|
+
cache_day=$(fm_busy_muse_cache_field "$1" "$2" namespace_day) || return 1
|
|
469
|
+
cache_signature=$(fm_busy_muse_cache_field "$1" "$2" namespace_signature) || return 1
|
|
470
|
+
day=$(fm_busy_muse_namespace_day "$3") || return 1
|
|
471
|
+
[ "$cache_day" = "$day" ] || return 1
|
|
472
|
+
signature=$(fm_busy_muse_namespace_signature "$day") || return 1
|
|
473
|
+
[ "$cache_signature" = "$signature" ] || return 1
|
|
474
|
+
printf '%s' "$log"
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
fm_busy_muse_cache_session_log() { # <state-dir> <id> <binding-id> <session-log> <namespace-day> <namespace-signature>
|
|
478
|
+
local cache tmp current
|
|
479
|
+
[ -n "$3" ] || return 0
|
|
480
|
+
current=$(fm_busy_muse_binding_field "$1" "$2" binding_id) || return 1
|
|
481
|
+
[ "$current" = "$3" ] || return 1
|
|
482
|
+
cache=$(fm_busy_muse_cache_path "$1" "$2")
|
|
483
|
+
tmp="$cache.tmp.$$"
|
|
484
|
+
{
|
|
485
|
+
printf 'binding_id=%s\n' "$3"
|
|
486
|
+
printf 'session_log=%s\n' "$4"
|
|
487
|
+
printf 'namespace_day=%s\n' "$5"
|
|
488
|
+
printf 'namespace_signature=%s\n' "$6"
|
|
489
|
+
} > "$tmp" || { rm -f "$tmp"; return 1; }
|
|
490
|
+
mv -f -- "$tmp" "$cache"
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
# fm_busy_muse_session_log: the one matching MAIN session log that did not
|
|
494
|
+
# exist when fm-spawn created this pane's binding. Multiple candidates are
|
|
495
|
+
# ambiguous and fail closed rather than guessing which pane owns either log.
|
|
496
|
+
fm_busy_muse_session_log() { # <state-dir> <id>
|
|
497
|
+
local root ws binding_id='' candidate selected='' cache namespace_day namespace_before namespace_after
|
|
498
|
+
root=$(fm_busy_muse_binding_field "$1" "$2" sessions_root) || return 1
|
|
499
|
+
ws=$(fm_busy_muse_binding_field "$1" "$2" workspace_root) || return 1
|
|
500
|
+
binding_id=$(fm_busy_muse_binding_field "$1" "$2" binding_id 2>/dev/null || true)
|
|
501
|
+
if cache=$(fm_busy_muse_cached_session_log "$1" "$2" "$root" "$binding_id"); then
|
|
502
|
+
printf '%s' "$cache"
|
|
503
|
+
return 0
|
|
504
|
+
fi
|
|
505
|
+
rm -f "$(fm_busy_muse_cache_path "$1" "$2")"
|
|
506
|
+
namespace_day=$(fm_busy_muse_namespace_day "$root") || return 1
|
|
507
|
+
namespace_before=$(fm_busy_muse_namespace_signature "$namespace_day") || return 1
|
|
508
|
+
while IFS= read -r candidate; do
|
|
509
|
+
[ -n "$candidate" ] || continue
|
|
510
|
+
fm_busy_muse_binding_has_prior_log "$1" "$2" "$candidate" && continue
|
|
511
|
+
[ -z "$selected" ] || return 1
|
|
512
|
+
selected=$candidate
|
|
513
|
+
done <<EOF
|
|
514
|
+
$(fm_busy_muse_matching_logs "$root" "$ws")
|
|
515
|
+
EOF
|
|
516
|
+
[ -n "$selected" ] || return 1
|
|
517
|
+
namespace_after=$(fm_busy_muse_namespace_signature "$namespace_day") || return 1
|
|
518
|
+
[ "$namespace_before" = "$namespace_after" ] || return 1
|
|
519
|
+
fm_busy_muse_cache_session_log "$1" "$2" "$binding_id" "$selected" \
|
|
520
|
+
"$namespace_day" "$namespace_after" || return 1
|
|
521
|
+
printf '%s' "$selected"
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
fm_busy_muse_run_events() { # <session-log>
|
|
525
|
+
[ -f "$1" ] || return 1
|
|
526
|
+
LC_ALL=C awk '
|
|
527
|
+
BEGIN { OFS = "\t"; pre = "\"payload\":{\"kind\":\"run\",\"run_id\":\"" }
|
|
528
|
+
{
|
|
529
|
+
p = index($0, pre)
|
|
530
|
+
if (p == 0) next
|
|
531
|
+
rest = substr($0, p + length(pre))
|
|
532
|
+
q = index(rest, "\"")
|
|
533
|
+
if (q == 0) next
|
|
534
|
+
rid = substr(rest, 1, q - 1)
|
|
535
|
+
rest = substr(rest, q)
|
|
536
|
+
head = "\",\"event\":{\"kind\":\""
|
|
537
|
+
if (substr(rest, 1, length(head)) != head) next
|
|
538
|
+
rest = substr(rest, length(head) + 1)
|
|
539
|
+
q = index(rest, "\"")
|
|
540
|
+
if (q == 0) next
|
|
541
|
+
ev = substr(rest, 1, q - 1)
|
|
542
|
+
terminal = ""
|
|
543
|
+
if (ev == "terminal") {
|
|
544
|
+
marker = "\"terminal\":\""
|
|
545
|
+
p = index(rest, marker)
|
|
546
|
+
if (p != 0) {
|
|
547
|
+
value = substr(rest, p + length(marker))
|
|
548
|
+
q = index(value, "\"")
|
|
549
|
+
if (q != 0) terminal = substr(value, 1, q - 1)
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
if (ev == "started" || ev == "terminal") print rid, ev, terminal
|
|
553
|
+
}
|
|
554
|
+
' "$1"
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
# fm_busy_muse_run_state: fold one session log to busy|settled|none.
|
|
558
|
+
# busy at least one run started with no matching terminal
|
|
559
|
+
# settled every started run reached a terminal
|
|
560
|
+
# none the log holds no run lifecycle records at all
|
|
561
|
+
# The match is anchored on the exact structural prefix rather than a bare
|
|
562
|
+
# "kind":"terminal" search, because muse also emits nested "record":{"kind":
|
|
563
|
+
# "terminal"} cleanup-effect payloads that are NOT run terminals and would
|
|
564
|
+
# otherwise close a run that is still in flight.
|
|
565
|
+
fm_busy_muse_run_state() { # <session-log>
|
|
566
|
+
[ -f "$1" ] || return 1
|
|
567
|
+
fm_busy_muse_run_events "$1" | LC_ALL=C awk -F '\t' '
|
|
568
|
+
$2 == "started" { open[$1] = 1; seen = 1 }
|
|
569
|
+
$2 == "terminal" { open[$1] = 0 }
|
|
570
|
+
END {
|
|
571
|
+
if (!seen) { print "none"; exit }
|
|
572
|
+
for (rid in open) if (open[rid] == 1) { print "busy"; exit }
|
|
573
|
+
print "settled"
|
|
574
|
+
}
|
|
575
|
+
'
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
fm_busy_muse_active_run_id() { # <session-log>
|
|
579
|
+
[ -f "$1" ] || return 1
|
|
580
|
+
fm_busy_muse_run_events "$1" | LC_ALL=C awk -F '\t' '
|
|
581
|
+
$2 == "started" { open[$1] = 1 }
|
|
582
|
+
$2 == "terminal" { open[$1] = 0 }
|
|
583
|
+
END {
|
|
584
|
+
for (rid in open) {
|
|
585
|
+
if (open[rid] != 1) continue
|
|
586
|
+
active = rid
|
|
587
|
+
count++
|
|
588
|
+
}
|
|
589
|
+
if (count != 1) exit 1
|
|
590
|
+
print active
|
|
591
|
+
}
|
|
592
|
+
'
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
fm_busy_muse_run_terminal() { # <session-log> <run-id>
|
|
596
|
+
[ -f "$1" ] && [ -n "${2:-}" ] || return 1
|
|
597
|
+
fm_busy_muse_run_events "$1" | LC_ALL=C awk -F '\t' -v wanted="$2" '
|
|
598
|
+
$1 == wanted && $2 == "terminal" && $3 != "" { terminal = $3 }
|
|
599
|
+
END {
|
|
600
|
+
if (terminal == "") exit 1
|
|
601
|
+
print terminal
|
|
602
|
+
}
|
|
603
|
+
'
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
# cursor conversation-transcript busy source
|
|
607
|
+
#
|
|
608
|
+
# cursor-agent persists an append-only JSONL transcript per conversation at
|
|
609
|
+
# <projects-root>/<workspace-slug>/agent-transcripts/<conversation-id>/<id>.jsonl
|
|
610
|
+
# and brackets every submitted turn. Verified live on cursor-agent
|
|
611
|
+
# 2026.08.11-e8db854:
|
|
612
|
+
# {"role":"user", ...} <- turn opens
|
|
613
|
+
# {"role":"assistant", ...} <- work
|
|
614
|
+
# {"type":"turn_ended","status":"success"} <- turn closes
|
|
615
|
+
# An Escape interrupt closes the turn with status "aborted", so like muse's
|
|
616
|
+
# session log - and unlike Claude's Stop hook - this source covers the manual
|
|
617
|
+
# interrupt path. Nothing is installed and no trust grant is needed: cursor
|
|
618
|
+
# writes this transcript on its own.
|
|
619
|
+
#
|
|
620
|
+
# Resolution deliberately does NOT reconstruct cursor's workspace-slug directory
|
|
621
|
+
# name. That slug is a lossy transformation of the workspace path (separators
|
|
622
|
+
# collapse), so rebuilding it would be a guess that silently binds the wrong
|
|
623
|
+
# pane. cursor writes the exact absolute path into each project directory's
|
|
624
|
+
# .workspace-trusted, so the binding matches on that recorded value instead.
|
|
625
|
+
#
|
|
626
|
+
# fm_busy_cursor_binding_path: the per-task sidecar fm-spawn writes. It records
|
|
627
|
+
# projects_root=<abs>, workspace_root=<abs>, and one prior_conversation=<id> for
|
|
628
|
+
# each conversation that already existed for that workspace when this pane
|
|
629
|
+
# launched, so a relaunched task cannot fold its predecessor's transcript.
|
|
630
|
+
fm_busy_cursor_binding_path() { # <state-dir> <id>
|
|
631
|
+
printf '%s/%s.cursor-session' "$1" "$2"
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
fm_busy_cursor_binding_field() { # <state-dir> <id> <key>
|
|
635
|
+
local path value
|
|
636
|
+
path=$(fm_busy_cursor_binding_path "$1" "$2")
|
|
637
|
+
[ -f "$path" ] || return 1
|
|
638
|
+
value=$(LC_ALL=C awk -F= -v k="$3" '$1 == k { sub(/^[^=]*=/, ""); print; exit }' "$path")
|
|
639
|
+
[ -n "$value" ] || return 1
|
|
640
|
+
printf '%s' "$value"
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
# fm_busy_cursor_project_dir: the project directory whose recorded
|
|
644
|
+
# .workspace-trusted workspacePath is exactly <workspace-root>. Exact-match
|
|
645
|
+
# only: a prefix or slug comparison would bind a nested worktree to its parent.
|
|
646
|
+
fm_busy_cursor_project_dir() { # <projects-root> <workspace-root>
|
|
647
|
+
local root=$1 want=$2 marker dir path
|
|
648
|
+
[ -d "$root" ] || return 1
|
|
649
|
+
for marker in "$root"/*/.workspace-trusted; do
|
|
650
|
+
[ -f "$marker" ] || continue
|
|
651
|
+
path=$(LC_ALL=C sed -n 's/.*"workspacePath"[[:space:]]*:[[:space:]]*"\(.*\)".*/\1/p' "$marker" | head -1)
|
|
652
|
+
[ -n "$path" ] || continue
|
|
653
|
+
[ "$path" = "$want" ] || continue
|
|
654
|
+
dir=${marker%/.workspace-trusted}
|
|
655
|
+
printf '%s' "$dir"
|
|
656
|
+
return 0
|
|
657
|
+
done
|
|
658
|
+
return 1
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
# fm_busy_cursor_transcript: the ONE transcript this pane owns, or failure.
|
|
662
|
+
# A conversation recorded as prior_conversation is excluded, so a relaunch in a
|
|
663
|
+
# reused worktree folds its own turn rather than the previous pane's. Requiring
|
|
664
|
+
# a UNIQUE remaining conversation is what keeps the binding honest: zero means
|
|
665
|
+
# no turn has been submitted yet and several means the pane cannot be told
|
|
666
|
+
# apart, and neither proves anything about the current turn.
|
|
667
|
+
fm_busy_cursor_transcript() { # <state-dir> <id>
|
|
668
|
+
local root workspace project dir conv found='' count=0 prior
|
|
669
|
+
root=$(fm_busy_cursor_binding_field "$1" "$2" projects_root) || return 1
|
|
670
|
+
workspace=$(fm_busy_cursor_binding_field "$1" "$2" workspace_root) || return 1
|
|
671
|
+
project=$(fm_busy_cursor_project_dir "$root" "$workspace") || return 1
|
|
672
|
+
prior=$(LC_ALL=C awk -F= '$1 == "prior_conversation" { sub(/^[^=]*=/, ""); print }' \
|
|
673
|
+
"$(fm_busy_cursor_binding_path "$1" "$2")" 2>/dev/null)
|
|
674
|
+
for dir in "$project"/agent-transcripts/*/; do
|
|
675
|
+
[ -d "$dir" ] || continue
|
|
676
|
+
conv=$(basename -- "${dir%/}")
|
|
677
|
+
printf '%s\n' "$prior" | grep -Fqx "$conv" && continue
|
|
678
|
+
[ -f "$dir$conv.jsonl" ] || continue
|
|
679
|
+
found="$dir$conv.jsonl"
|
|
680
|
+
count=$((count + 1))
|
|
681
|
+
done
|
|
682
|
+
[ "$count" = 1 ] && [ -n "$found" ] || return 1
|
|
683
|
+
printf '%s' "$found"
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
# fm_busy_cursor_turn_state: fold the transcript into busy | settled | none.
|
|
687
|
+
# Lifecycle records are matched on top-level fields of structurally valid JSON,
|
|
688
|
+
# so a turn whose own text mentions turn_ended cannot close it.
|
|
689
|
+
fm_busy_cursor_turn_state() { # <transcript>
|
|
690
|
+
[ -f "$1" ] || return 1
|
|
691
|
+
if command -v jq >/dev/null 2>&1; then
|
|
692
|
+
LC_ALL=C jq -Rr '
|
|
693
|
+
try (
|
|
694
|
+
fromjson
|
|
695
|
+
| if type == "object" and .type? == "turn_ended" then "close"
|
|
696
|
+
elif type == "object" and .role? == "user" then "open"
|
|
697
|
+
else "other"
|
|
698
|
+
end
|
|
699
|
+
) catch "malformed"
|
|
700
|
+
' "$1"
|
|
701
|
+
else
|
|
702
|
+
LC_ALL=C awk '
|
|
703
|
+
function ws( c) {
|
|
704
|
+
while (p <= n) {
|
|
705
|
+
c = substr(line, p, 1)
|
|
706
|
+
if (c != " " && c != "\t" && c != "\r") break
|
|
707
|
+
p++
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
function hex(c) {
|
|
711
|
+
if (c >= "0" && c <= "9") return c + 0
|
|
712
|
+
c = tolower(c)
|
|
713
|
+
return index("abcdef", c) + 9
|
|
714
|
+
}
|
|
715
|
+
function string( c, e, h, i, code, out) {
|
|
716
|
+
if (substr(line, p, 1) != "\"") return 0
|
|
717
|
+
p++; out = ""
|
|
718
|
+
while (p <= n) {
|
|
719
|
+
c = substr(line, p++, 1)
|
|
720
|
+
if (c == "\"") { value = out; kind = "string"; return 1 }
|
|
721
|
+
if (c ~ /[[:cntrl:]]/) return 0
|
|
722
|
+
if (c != "\\") { out = out c; continue }
|
|
723
|
+
if (p > n) return 0
|
|
724
|
+
e = substr(line, p++, 1)
|
|
725
|
+
if (e == "\"" || e == "\\" || e == "/") out = out e
|
|
726
|
+
else if (e ~ /^[bfnrt]$/) out = out "?"
|
|
727
|
+
else if (e == "u") {
|
|
728
|
+
h = substr(line, p, 4)
|
|
729
|
+
if (length(h) != 4 || h !~ /^[0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f]$/) return 0
|
|
730
|
+
code = 0
|
|
731
|
+
for (i = 1; i <= 4; i++) code = code * 16 + hex(substr(h, i, 1))
|
|
732
|
+
out = out (code < 128 ? sprintf("%c", code) : "?")
|
|
733
|
+
p += 4
|
|
734
|
+
} else return 0
|
|
735
|
+
}
|
|
736
|
+
return 0
|
|
737
|
+
}
|
|
738
|
+
function number( c) {
|
|
739
|
+
if (substr(line, p, 1) == "-") p++
|
|
740
|
+
c = substr(line, p, 1)
|
|
741
|
+
if (c == "0") {
|
|
742
|
+
p++
|
|
743
|
+
if (substr(line, p, 1) ~ /^[0-9]$/) return 0
|
|
744
|
+
} else if (c ~ /^[1-9]$/) {
|
|
745
|
+
do { p++; c = substr(line, p, 1) } while (c ~ /^[0-9]$/)
|
|
746
|
+
} else return 0
|
|
747
|
+
if (substr(line, p, 1) == ".") {
|
|
748
|
+
p++
|
|
749
|
+
if (substr(line, p, 1) !~ /^[0-9]$/) return 0
|
|
750
|
+
while (substr(line, p, 1) ~ /^[0-9]$/) p++
|
|
751
|
+
}
|
|
752
|
+
c = substr(line, p, 1)
|
|
753
|
+
if (c == "e" || c == "E") {
|
|
754
|
+
p++; c = substr(line, p, 1)
|
|
755
|
+
if (c == "+" || c == "-") p++
|
|
756
|
+
if (substr(line, p, 1) !~ /^[0-9]$/) return 0
|
|
757
|
+
while (substr(line, p, 1) ~ /^[0-9]$/) p++
|
|
758
|
+
}
|
|
759
|
+
kind = "number"; value = ""
|
|
760
|
+
return 1
|
|
761
|
+
}
|
|
762
|
+
function array(depth, c) {
|
|
763
|
+
p++; ws()
|
|
764
|
+
if (substr(line, p, 1) == "]") { p++; return 1 }
|
|
765
|
+
while (p <= n) {
|
|
766
|
+
if (!json(depth + 1)) return 0
|
|
767
|
+
ws(); c = substr(line, p, 1)
|
|
768
|
+
if (c == "]") { p++; return 1 }
|
|
769
|
+
if (c != ",") return 0
|
|
770
|
+
p++; ws()
|
|
771
|
+
}
|
|
772
|
+
return 0
|
|
773
|
+
}
|
|
774
|
+
function object(depth, c, key, vkind, vvalue, is_close, is_open) {
|
|
775
|
+
p++; ws()
|
|
776
|
+
if (substr(line, p, 1) == "}") { p++; kind = "object"; return 1 }
|
|
777
|
+
while (p <= n) {
|
|
778
|
+
if (!string()) return 0
|
|
779
|
+
key = value; ws()
|
|
780
|
+
if (substr(line, p, 1) != ":") return 0
|
|
781
|
+
p++; ws()
|
|
782
|
+
if (!json(depth + 1)) return 0
|
|
783
|
+
vkind = kind; vvalue = value
|
|
784
|
+
if (depth == 0 && key == "type") is_close = (vkind == "string" && vvalue == "turn_ended")
|
|
785
|
+
if (depth == 0 && key == "role") is_open = (vkind == "string" && vvalue == "user")
|
|
786
|
+
ws(); c = substr(line, p, 1)
|
|
787
|
+
if (c == "}") {
|
|
788
|
+
p++; kind = "object"; value = ""
|
|
789
|
+
if (depth == 0) event = (is_close ? "close" : (is_open ? "open" : "other"))
|
|
790
|
+
return 1
|
|
791
|
+
}
|
|
792
|
+
if (c != ",") return 0
|
|
793
|
+
p++; ws()
|
|
794
|
+
}
|
|
795
|
+
return 0
|
|
796
|
+
}
|
|
797
|
+
function json(depth, c, word) {
|
|
798
|
+
ws(); c = substr(line, p, 1)
|
|
799
|
+
if (c == "\"") return string()
|
|
800
|
+
if (c == "{") return object(depth)
|
|
801
|
+
if (c == "[") { kind = "array"; value = ""; return array(depth) }
|
|
802
|
+
if (c == "-" || c ~ /^[0-9]$/) return number()
|
|
803
|
+
word = substr(line, p)
|
|
804
|
+
if (substr(word, 1, 4) == "true" || substr(word, 1, 4) == "null") { p += 4; kind = "literal"; value = ""; return 1 }
|
|
805
|
+
if (substr(word, 1, 5) == "false") { p += 5; kind = "literal"; value = ""; return 1 }
|
|
806
|
+
return 0
|
|
807
|
+
}
|
|
808
|
+
{
|
|
809
|
+
line = $0; p = 1; n = length(line); event = "other"; kind = ""; value = ""
|
|
810
|
+
valid = json(0); ws()
|
|
811
|
+
print (valid && p > n ? event : "malformed")
|
|
812
|
+
}
|
|
813
|
+
' "$1"
|
|
814
|
+
fi | LC_ALL=C awk '
|
|
815
|
+
$0 == "close" { open = 0; seen = 1; malformed = 0; next }
|
|
816
|
+
$0 == "open" { open = 1; seen = 1; next }
|
|
817
|
+
$0 == "malformed" { if (!open) malformed = 1; next }
|
|
818
|
+
END {
|
|
819
|
+
if (!seen || (!open && malformed)) { print "none"; exit }
|
|
820
|
+
print (open ? "busy" : "settled")
|
|
821
|
+
}
|
|
822
|
+
'
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
# fm_busy_grok_tail_busy: the Grok-only temporary rendered-tail fallback.
|
|
826
|
+
# Consumes the tail on stdin; 0 when Grok's verified busy signature matches.
|
|
827
|
+
# FM_BUSY_REGEX still globally overrides the signature, mirroring the
|
|
828
|
+
# historical operator escape hatch.
|
|
829
|
+
fm_busy_grok_tail_busy() {
|
|
830
|
+
grep -v '^[[:space:]]*$' | tail -12 \
|
|
831
|
+
| grep -qiE "${FM_BUSY_REGEX:-${FM_DELIVERY_GROK_BUSY_REGEX_DEFAULT:-Ctrl\\+c:cancel}}"
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
# fm_busy_classify: semantic classification for a task whose endpoint the
|
|
835
|
+
# caller has already established as present. Prints "<verdict> <source>":
|
|
836
|
+
# busy|idle|unknown plus the producing source (see header). Never probes
|
|
837
|
+
# process state. <tail40> is optional pre-captured plain output used only by
|
|
838
|
+
# the Grok arm; when absent the Grok arm captures through fm_backend_capture
|
|
839
|
+
# if available, else reports unknown capture-failed.
|
|
840
|
+
fm_busy_classify() { # <backend> <target> <harness> <id> <state-dir> [tail40]
|
|
841
|
+
local backend=$1 target=$2 harness=$3 id=$4 state=$5 tail40=${6-}
|
|
842
|
+
local out rc r_state r_source native log
|
|
843
|
+
case "$harness" in
|
|
844
|
+
kimi*)
|
|
845
|
+
if ! fm_busy_kimi_verified; then
|
|
846
|
+
printf 'unknown kimi-unverified'
|
|
847
|
+
return 0
|
|
848
|
+
fi
|
|
849
|
+
;;
|
|
850
|
+
codex*)
|
|
851
|
+
if ! fm_busy_codex_semantic_source; then
|
|
852
|
+
printf 'unknown codex-unverified'
|
|
853
|
+
return 0
|
|
854
|
+
fi
|
|
855
|
+
;;
|
|
856
|
+
cursor*)
|
|
857
|
+
# Semantic, on demand: fold this task's bound conversation transcript. A
|
|
858
|
+
# turn open past its last close is positive proof of a turn in flight and
|
|
859
|
+
# a trailing turn_ended is a finished turn. Every other outcome - no
|
|
860
|
+
# sidecar, no resolvable transcript, an unreadable or record-free file -
|
|
861
|
+
# is unknown, never idle. The rendered `ctrl+c to stop` footer is
|
|
862
|
+
# deliberately NOT consulted here; see the source note above.
|
|
863
|
+
if ! log=$(fm_busy_cursor_transcript "$state" "$id"); then
|
|
864
|
+
printf 'unknown cursor-transcript'
|
|
865
|
+
return 0
|
|
866
|
+
fi
|
|
867
|
+
case "$(fm_busy_cursor_turn_state "$log" 2>/dev/null)" in
|
|
868
|
+
busy) printf 'busy cursor-transcript' ;;
|
|
869
|
+
settled) printf 'idle cursor-transcript' ;;
|
|
870
|
+
*) printf 'unknown cursor-transcript' ;;
|
|
871
|
+
esac
|
|
872
|
+
return 0
|
|
873
|
+
;;
|
|
874
|
+
esac
|
|
875
|
+
out=$(fm_busy_record_read "$state" "$id") && rc=0 || rc=$?
|
|
876
|
+
if [ "$rc" = 0 ]; then
|
|
877
|
+
r_state=${out%% *}
|
|
878
|
+
out=${out#* }
|
|
879
|
+
r_source=${out%% *}
|
|
880
|
+
if fm_busy_source_trusted "$harness" "$r_source"; then
|
|
881
|
+
printf '%s %s' "$r_state" "$r_source"
|
|
882
|
+
else
|
|
883
|
+
printf 'unknown source-mismatch'
|
|
884
|
+
fi
|
|
885
|
+
return 0
|
|
886
|
+
fi
|
|
887
|
+
case "$out" in
|
|
888
|
+
malformed|gen-mismatch)
|
|
889
|
+
printf 'unknown %s' "$out"
|
|
890
|
+
return 0
|
|
891
|
+
;;
|
|
892
|
+
esac
|
|
893
|
+
# No record at all. A native herdr busy verdict is semantic enough to trust
|
|
894
|
+
# for BUSY (streaming means a turn is running); native idle is narrower
|
|
895
|
+
# than turn state (a long foreground tool call reads idle) and stays
|
|
896
|
+
# unknown here.
|
|
897
|
+
if [ "$backend" = herdr ] && command -v fm_backend_busy_state >/dev/null 2>&1; then
|
|
898
|
+
native=$(fm_backend_busy_state "$backend" "$target" 2>/dev/null || true)
|
|
899
|
+
if [ "$native" = busy ]; then
|
|
900
|
+
printf 'busy herdr-native'
|
|
901
|
+
return 0
|
|
902
|
+
fi
|
|
903
|
+
fi
|
|
904
|
+
case "$harness" in
|
|
905
|
+
muse*)
|
|
906
|
+
# Semantic, on demand: fold this task's bound session log. An open run is
|
|
907
|
+
# positive proof of a turn in flight and a settled log is a finished turn.
|
|
908
|
+
# Every other outcome - no sidecar, no matching log, an unreadable or
|
|
909
|
+
# run-free log - is unknown, never idle.
|
|
910
|
+
if ! log=$(fm_busy_muse_session_log "$state" "$id"); then
|
|
911
|
+
printf 'unknown muse-session-log'
|
|
912
|
+
return 0
|
|
913
|
+
fi
|
|
914
|
+
case "$(fm_busy_muse_run_state "$log" 2>/dev/null)" in
|
|
915
|
+
busy) printf 'busy muse-session-log' ;;
|
|
916
|
+
settled) printf 'idle muse-session-log' ;;
|
|
917
|
+
*) printf 'unknown muse-session-log' ;;
|
|
918
|
+
esac
|
|
919
|
+
return 0
|
|
920
|
+
;;
|
|
921
|
+
grok*)
|
|
922
|
+
if [ -z "$tail40" ]; then
|
|
923
|
+
if command -v fm_backend_capture >/dev/null 2>&1; then
|
|
924
|
+
tail40=$(fm_backend_capture "$backend" "$target" 40 2>/dev/null) || {
|
|
925
|
+
printf 'unknown capture-failed'
|
|
926
|
+
return 0
|
|
927
|
+
}
|
|
928
|
+
else
|
|
929
|
+
printf 'unknown capture-failed'
|
|
930
|
+
return 0
|
|
931
|
+
fi
|
|
932
|
+
fi
|
|
933
|
+
if printf '%s' "$tail40" | fm_busy_grok_tail_busy; then
|
|
934
|
+
printf 'busy grok-regex'
|
|
935
|
+
else
|
|
936
|
+
printf 'idle grok-regex'
|
|
937
|
+
fi
|
|
938
|
+
return 0
|
|
939
|
+
;;
|
|
940
|
+
esac
|
|
941
|
+
printf 'unknown missing'
|
|
942
|
+
}
|
|
943
|
+
|
|
944
|
+
# fm_busy_classify_live: fm_busy_classify behind the one process-level
|
|
945
|
+
# override - a gone endpoint is dead, never busy. Requires fm-backend.sh to
|
|
946
|
+
# be sourced for fm_backend_target_exists.
|
|
947
|
+
fm_busy_classify_live() { # <backend> <target> <harness> <id> <state-dir> [expected-label]
|
|
948
|
+
local backend=$1 target=$2 harness=$3 id=$4 state=$5 label=${6-}
|
|
949
|
+
if [ -z "$target" ]; then
|
|
950
|
+
printf 'unknown no-target'
|
|
951
|
+
return 0
|
|
952
|
+
fi
|
|
953
|
+
if ! fm_backend_target_exists "$backend" "$target" "$label" 2>/dev/null; then
|
|
954
|
+
printf 'dead endpoint-gone'
|
|
955
|
+
return 0
|
|
956
|
+
fi
|
|
957
|
+
fm_busy_classify "$backend" "$target" "$harness" "$id" "$state"
|
|
958
|
+
}
|
|
959
|
+
|
|
960
|
+
# fm_busy_classify_meta: classify a task from its recorded metadata, so every
|
|
961
|
+
# consumer resolves backend, target, and harness the same way instead of
|
|
962
|
+
# re-deriving them. Requires fm-backend.sh to be sourced. <tail40> is
|
|
963
|
+
# optional pre-captured plain output reused by the Grok arm.
|
|
964
|
+
fm_busy_classify_meta() { # <meta-file> <id> <state-dir> [tail40]
|
|
965
|
+
local meta=$1 id=$2 state=$3 tail40=${4-} backend target harness
|
|
966
|
+
[ -f "$meta" ] || { printf 'unknown missing'; return 0; }
|
|
967
|
+
backend=$(fm_backend_of_meta "$meta")
|
|
968
|
+
target=$(fm_backend_target_of_meta "$meta")
|
|
969
|
+
harness=$(fm_meta_get "$meta" harness)
|
|
970
|
+
if [ -z "$target" ]; then
|
|
971
|
+
printf 'unknown no-target'
|
|
972
|
+
return 0
|
|
973
|
+
fi
|
|
974
|
+
fm_busy_classify "$backend" "$target" "$harness" "$id" "$state" "$tail40"
|
|
975
|
+
}
|
|
976
|
+
|
|
977
|
+
# fm_busy_is_busy: boolean view for callers that only gate on provable
|
|
978
|
+
# activity. 0 iff the classification verdict is exactly busy; idle, unknown,
|
|
979
|
+
# and dead all return 1, so an unknown can never be silently promoted to
|
|
980
|
+
# either boolean pole - callers that must distinguish idle from unknown read
|
|
981
|
+
# the full classification instead.
|
|
982
|
+
fm_busy_is_busy() { # <backend> <target> <harness> <id> <state-dir> [tail40]
|
|
983
|
+
local verdict
|
|
984
|
+
verdict=$(fm_busy_classify "$@")
|
|
985
|
+
[ "${verdict%% *}" = busy ]
|
|
986
|
+
}
|