@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,1059 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Steer a task by durable record: write the message into the task's steering
|
|
3
|
+
# inbox and ring a constant doorbell line into its terminal, best-effort.
|
|
4
|
+
# Usage: fm-send.sh <target> [--resolve-key <key>]... [--fire-and-forget <delivery-id>] <text...>
|
|
5
|
+
# <target> may be an exact task id, a legacy fm-<id> task label resolved
|
|
6
|
+
# through this home's state/<id>.meta, or an explicit well-formed backend
|
|
7
|
+
# target. fm-send refuses unresolved guesses rather than falling back to a
|
|
8
|
+
# tmux window search, because a "successful" send to the wrong endpoint is
|
|
9
|
+
# worse than a loud failure.
|
|
10
|
+
# Special keys instead of text: fm-send.sh <target> --key Enter
|
|
11
|
+
# Key support is backend-specific: tmux/herdr support Escape, Enter, and C-c;
|
|
12
|
+
# Orca currently supports Enter and C-c only, and rejects Escape.
|
|
13
|
+
#
|
|
14
|
+
# Two data planes:
|
|
15
|
+
#
|
|
16
|
+
# INBOX - the default for text to a task recorded in this home, local and
|
|
17
|
+
# remote alike. The message is appended as a durable sequenced record under
|
|
18
|
+
# the task's steering inbox (newlines are legal) - state/<id>.inbox/ for a
|
|
19
|
+
# local task, or the remote home's host-local inbox reached through fm-on.sh
|
|
20
|
+
# for a remote secondmate - and the terminal receives only one short constant
|
|
21
|
+
# self-describing doorbell line plus Enter, best-effort. The durable record IS
|
|
22
|
+
# the delivery, so the record's fate alone governs the exit: 0 = the steer is
|
|
23
|
+
# durably sent (recorded); nonzero = nothing was confirmed delivered and a
|
|
24
|
+
# resend is appropriate (unresolvable target, an endpoint that cannot be
|
|
25
|
+
# locked and revalidated or that retired or changed, an unwritable record, a
|
|
26
|
+
# failed or lost remote transport) or a decision-close append failed after
|
|
27
|
+
# delivery (the error then carries the exact manual close). The remote enqueue
|
|
28
|
+
# is idempotent: the remote leg deduplicates an exact re-run of the same
|
|
29
|
+
# request onto the existing record (bin/fm-task-inbox-lib.sh), so after a lost
|
|
30
|
+
# transport (ssh exit 255, completion unknown) fm-send retries the same leg
|
|
31
|
+
# once itself. For an ordinary reply-bearing request, a later re-run is
|
|
32
|
+
# idempotent only through the printed FM_PENDING_REPLY_EXISTING_CORR=<corr>
|
|
33
|
+
# command: it preserves the same correlation, body, and record, while a plain
|
|
34
|
+
# re-run mints a new correlation and delivers a separate record. An explicit
|
|
35
|
+
# fire-and-forget request instead retries with its same caller-supplied delivery
|
|
36
|
+
# id. A still-unconfirmed reply-bearing request keeps its reply expectation
|
|
37
|
+
# preserved for the record that may have landed.
|
|
38
|
+
# Pending-reply bookkeeping trouble after a durable enqueue NEVER exits
|
|
39
|
+
# nonzero: with the recovery marker stored the watcher reconciles it silently,
|
|
40
|
+
# and with both the commit and the marker lost the send prints a distinct
|
|
41
|
+
# "reply-tracking-degraded (steer delivered, do not resend)" warning instead,
|
|
42
|
+
# because a resend-inviting status there would duplicate a delivered
|
|
43
|
+
# instruction. There is no delivered-unconfirmed
|
|
44
|
+
# outcome on this plane: "did the doorbell land" is no longer the question -
|
|
45
|
+
# "was the message acted on" is, and that is answered asynchronously for an
|
|
46
|
+
# ordinary record by the worker's acknowledgement move into handled/, with the
|
|
47
|
+
# watcher re-ringing an unacknowledged message and escalating a stuck one. An
|
|
48
|
+
# explicit fire-and-forget record is excluded from that ladder.
|
|
49
|
+
# bin/fm-task-inbox-lib.sh owns the record format, the doorbell line, and the
|
|
50
|
+
# re-ring ladder. The composer pre-check before the ring is ADVISORY only: when
|
|
51
|
+
# the composer visibly holds pending text the ring is skipped with a notice and
|
|
52
|
+
# the watcher re-rings an ordinary record later; no composer verdict is
|
|
53
|
+
# delivery proof on this plane, and a failed ring never fails the send.
|
|
54
|
+
#
|
|
55
|
+
# TYPED - the LOCAL text that must reach the terminal itself: a harness-native
|
|
56
|
+
# invocation (a leading "/", or a leading "$" to a codex target) must reach
|
|
57
|
+
# the harness's own parser, and an explicit backend target names an endpoint,
|
|
58
|
+
# not a task, so it stays typed even when local metadata happens to match it
|
|
59
|
+
# (the same boundary that keeps it unmarked and outside --resolve-key). These
|
|
60
|
+
# type the literal
|
|
61
|
+
# text through the target backend's verified submit core: typed ONCE, then
|
|
62
|
+
# Enter retried (never retyped) until the backend confirms a submit or reports
|
|
63
|
+
# an inconclusive send. Typed-plane exit contract: 0 = submit confirmed;
|
|
64
|
+
# 3 = the text was typed into the live endpoint and
|
|
65
|
+
# Enter was sent, but the submit read-back stayed unconfirmed (verify the pane
|
|
66
|
+
# before any resend, and never re-type blindly; a marked request's
|
|
67
|
+
# pending-reply expectation stays armed because this outcome is not a proven
|
|
68
|
+
# failure); any other nonzero = the send failed and nothing may be assumed
|
|
69
|
+
# delivered. Submission dispatches through the target's recorded backend; the
|
|
70
|
+
# tmux adapter shares its composer/submit core with the away-mode daemon via
|
|
71
|
+
# bin/fm-tmux-lib.sh. Tune with FM_SEND_RETRIES (default 3) / FM_SEND_SLEEP
|
|
72
|
+
# (0.4). Slash commands, and codex `$...` skill invocations resolved through
|
|
73
|
+
# harness meta, get a longer pre-Enter settle so completion popups do not
|
|
74
|
+
# swallow Enter. A remote secondmate target has no typed text plane at all:
|
|
75
|
+
# every remote text steer rides the inbox (a marked secondmate request already
|
|
76
|
+
# reaches the harness as marker-prefixed chat rather than a parser command, so
|
|
77
|
+
# routing a remote "/..." or "$..." through the record changes nothing the
|
|
78
|
+
# parser would have seen); only --key still crosses to the remote pane as a
|
|
79
|
+
# keystroke.
|
|
80
|
+
#
|
|
81
|
+
# Stage-1 compatibility boundary: classification uses the original pre-marker
|
|
82
|
+
# text, but secondmate marking still precedes every typed submission. Therefore
|
|
83
|
+
# a marked parser-native secondmate invocation intentionally reaches the harness
|
|
84
|
+
# as marker-prefixed chat rather than executing as a parser command. This is a
|
|
85
|
+
# pre-existing interaction retained for byte compatibility in this local-inbox
|
|
86
|
+
# stage; do not move the marker behind the invocation or omit it here. Follow-up
|
|
87
|
+
# fm-send-secondmate-harness-invocation-r1 owns that behavior.
|
|
88
|
+
#
|
|
89
|
+
# From-firstmate marker: when the resolved target is a task selector whose meta
|
|
90
|
+
# records kind=secondmate, the message uses the live-charter-compatible
|
|
91
|
+
# from-firstmate carrier owned by bin/fm-operational-input.sh so the secondmate
|
|
92
|
+
# routes its reply via its status file or a status-pointed doc instead of
|
|
93
|
+
# stranding it in chat the main firstmate never reads. On the inbox plane the
|
|
94
|
+
# marker travels verbatim inside the recorded body. A crewmate/scout target,
|
|
95
|
+
# an explicit backend-target escape-hatch target, and the --key path are never
|
|
96
|
+
# marked - their behavior is unchanged.
|
|
97
|
+
#
|
|
98
|
+
# Parent-owned pending-reply expectation: every newly marked secondmate request
|
|
99
|
+
# except an explicit --fire-and-forget delivery receives a privacy-safe
|
|
100
|
+
# correlation id and a durable parent record under state/pending-replies/ before
|
|
101
|
+
# delivery (bin/fm-pending-reply-lib.sh). Delivery
|
|
102
|
+
# success and reply success are separate facts: delivery never resolves the
|
|
103
|
+
# expectation. On the inbox plane the durable enqueue IS delivery to the task's
|
|
104
|
+
# record, so the expectation is marked delivered at enqueue time; when that
|
|
105
|
+
# bookkeeping commit fails after its durable recovery marker is stored, the
|
|
106
|
+
# send remains successful and watcher reconciliation owns the repair, and when
|
|
107
|
+
# the commit and marker are BOTH lost the send still remains successful with a
|
|
108
|
+
# reply-tracking-degraded warning naming the expectation an operator must
|
|
109
|
+
# inspect (it can no longer reconcile or escalate on its own). Only a
|
|
110
|
+
# failed enqueue discards the expectation. On the typed plane an unconfirmed submit (exit 3) keeps
|
|
111
|
+
# it armed rather than dropping it, and only a proven send failure discards it.
|
|
112
|
+
# Set FM_PENDING_REPLY_EXISTING_CORR=<id> when re-sending a recovery request
|
|
113
|
+
# for an already-open expectation so a second record is not created. Direct
|
|
114
|
+
# unmarked captain input never creates one. A marked secondmate instruction
|
|
115
|
+
# sent with --fire-and-forget <16-hex-delivery-id> uses the same inbox transport
|
|
116
|
+
# without creating a reply expectation; its delivery id makes uncertain retries
|
|
117
|
+
# idempotent while allowing a later identical instruction to be distinct.
|
|
118
|
+
#
|
|
119
|
+
# Remote secondmate delivery: the send crosses fm-on.sh to a host-local leg
|
|
120
|
+
# (bin/fm-remote-secondmate-control.sh cmd_send) that writes the message as a
|
|
121
|
+
# durable record into the remote home's steering inbox and rings the remote
|
|
122
|
+
# doorbell, best-effort. The remote record is the delivery, exactly as it is
|
|
123
|
+
# locally: leg exit 0 means durably recorded (fm-send then exits 0, marks the
|
|
124
|
+
# pending-reply expectation delivered, and closes any --resolve-key
|
|
125
|
+
# decisions), and any real remote failure fails loudly with the remote leg's
|
|
126
|
+
# own stderr attached. Transport loss (ssh exit 255) means completion unknown,
|
|
127
|
+
# so fm-send retries the identical leg once - safe because the remote write
|
|
128
|
+
# deduplicates the same request onto the same record - and a still-lost
|
|
129
|
+
# transport exits nonzero while preserving a reply-bearing marked request's
|
|
130
|
+
# expectation, since the record may have landed. Its error prints the exact
|
|
131
|
+
# FM_PENDING_REPLY_EXISTING_CORR=<id> resend command that preserves the body
|
|
132
|
+
# and makes a later remote enqueue deduplicate onto that same record. An
|
|
133
|
+
# unconfirmed fire-and-forget request exits 3 and names the same delivery id to
|
|
134
|
+
# retry. Every remote transport attempt is bounded by FM_SEND_REMOTE_BUDGET
|
|
135
|
+
# seconds (default 30, and any override must be a positive integer): a bound
|
|
136
|
+
# hit is completion-unknown and exits through this same unconfirmed contract
|
|
137
|
+
# instead of waiting out a busy remote queue.
|
|
138
|
+
# The remote host runs no re-ring ladder of its own: a swallowed ordinary
|
|
139
|
+
# doorbell surfaces through the parent's pending-reply recovery and escalation,
|
|
140
|
+
# whose recovery request re-rings the remote doorbell when it is enqueued;
|
|
141
|
+
# fire-and-forget delivery deliberately arms neither mechanism. Internal
|
|
142
|
+
# semantic callers may set FM_SEND_EXPECTED_SPAWN_GEN or
|
|
143
|
+
# FM_SEND_EXPECTED_REMOTE_HOST to require that sampled identity to still match
|
|
144
|
+
# during the final locked remote-route validation; unset or empty guards do not
|
|
145
|
+
# change ordinary sends.
|
|
146
|
+
#
|
|
147
|
+
# Decision closure (answerer-closes): pass --resolve-key <key> (repeatable,
|
|
148
|
+
# before the message) when this send answers an open keyed needs-decision: or
|
|
149
|
+
# blocked: record in the target task's state/<id>.status. fm-send itself
|
|
150
|
+
# appends the closing "resolved [key=<key>]: answered: <capped excerpt>" line
|
|
151
|
+
# to that status file, so the captain-facing OPEN DECISIONS record closes at
|
|
152
|
+
# answer time and never depends on the busy worker writing a matching resolved
|
|
153
|
+
# line. On the inbox plane the close happens at ENQUEUE time, because enqueue
|
|
154
|
+
# is durable delivery to the task's record; the worker reading the answer late
|
|
155
|
+
# is covered by the acknowledgement re-ring ladder. On the typed plane it
|
|
156
|
+
# still waits for the confirmed submit. The close is a LOCAL append for every
|
|
157
|
+
# target kind - crewmate, scout, local secondmate, and remote secondmate alike
|
|
158
|
+
# - because the open-decision ledger fm-wake-drain folds lives in this home's
|
|
159
|
+
# own state dir (a remote mate's escalations reach it through the
|
|
160
|
+
# parent-replies ingest); only the answer message crosses the backend or
|
|
161
|
+
# remote transport.
|
|
162
|
+
#
|
|
163
|
+
# Chat is also a channel that carries keyed captain answers, so the same flag
|
|
164
|
+
# feeds bin/fm-captain-hold.sh's one keyed-answer intake for any key that names
|
|
165
|
+
# a captain-held task in this home - the key as a task id itself, or through
|
|
166
|
+
# the legacy `<task>-decision-<key>` identity for pre-collapse rows. fm-send
|
|
167
|
+
# closes nothing itself; it hands the intake `<task-id>\t<answer>\t<label>`
|
|
168
|
+
# exactly as every other channel does, and the intake owns what that means.
|
|
169
|
+
# This is what lets an answer reach a decision that has already been
|
|
170
|
+
# transferred from the live status log to its durable captain-held task, which
|
|
171
|
+
# the status ledger alone can no longer close.
|
|
172
|
+
#
|
|
173
|
+
# Each named key must therefore currently be open in ONE of the two ledgers: open
|
|
174
|
+
# in this home's status log per status_open_decisions (bin/fm-classify-lib.sh), or
|
|
175
|
+
# a still-open captain-held task resolved as above. A key in neither is refused
|
|
176
|
+
# before sending, so a mistyped key cannot deliver an answer while silently
|
|
177
|
+
# orphaning the decision. A failed or unconfirmed send never closes a key; a
|
|
178
|
+
# delivered answer whose closing append fails exits nonzero with the exact
|
|
179
|
+
# manual close command, leaving the decision open to re-surface (the safe
|
|
180
|
+
# direction). A send without the flag never closes anything: a routine steer,
|
|
181
|
+
# working:, or done: event still cannot clear a captain decision. The flag is
|
|
182
|
+
# refused with --key, with an explicit backend target (no task ledger in this
|
|
183
|
+
# home), and with an empty message.
|
|
184
|
+
#
|
|
185
|
+
# After a successful TYPED-plane submit fm-send pauses FM_SEND_SETTLE seconds
|
|
186
|
+
# (default 1, 0 disables) before returning: submit confirmation only proves the
|
|
187
|
+
# text was accepted, but the harness needs a beat to spin up the turn before its
|
|
188
|
+
# busy footer appears, so an immediate peek would otherwise see the stale idle
|
|
189
|
+
# pane. The pause is typed-plane-only; the inbox plane, the shared submit core
|
|
190
|
+
# (used by the away-mode daemon, which only needs "submitted"), and the --key
|
|
191
|
+
# path do not pay it.
|
|
192
|
+
set -eu
|
|
193
|
+
|
|
194
|
+
FM_SEND_ORIGINAL_ARGS=("$@")
|
|
195
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
196
|
+
FM_ROOT="${FM_ROOT_OVERRIDE:-$(cd "$SCRIPT_DIR/.." && pwd)}"
|
|
197
|
+
|
|
198
|
+
# shellcheck source=bin/fm-gate-refuse-lib.sh
|
|
199
|
+
. "$SCRIPT_DIR/fm-gate-refuse-lib.sh"
|
|
200
|
+
# Fail closed before any fleet mutation: a no-mistakes gate agent must never steer
|
|
201
|
+
# a crewmate (see bin/fm-gate-refuse-lib.sh).
|
|
202
|
+
fm_refuse_if_gate_agent
|
|
203
|
+
|
|
204
|
+
if [ -z "${FM_HOME+x}" ] || [ -z "${FM_HOME:-}" ]; then
|
|
205
|
+
echo "error: FM_HOME is not set; fm-send refuses to resolve targets without an explicit firstmate home" >&2
|
|
206
|
+
exit 1
|
|
207
|
+
fi
|
|
208
|
+
|
|
209
|
+
STATE="${FM_STATE_OVERRIDE:-$FM_HOME/state}"
|
|
210
|
+
if [ ! -d "$FM_HOME" ]; then
|
|
211
|
+
echo "error: FM_HOME '$FM_HOME' is not a directory; fm-send cannot resolve this home's state" >&2
|
|
212
|
+
exit 1
|
|
213
|
+
fi
|
|
214
|
+
if [ ! -d "$STATE" ]; then
|
|
215
|
+
echo "error: state dir '$STATE' is missing; fm-send cannot resolve targets for FM_HOME '$FM_HOME'" >&2
|
|
216
|
+
exit 1
|
|
217
|
+
fi
|
|
218
|
+
|
|
219
|
+
# shellcheck source=bin/fm-backend.sh
|
|
220
|
+
. "$SCRIPT_DIR/fm-backend.sh"
|
|
221
|
+
# shellcheck source=bin/fm-control-lib.sh
|
|
222
|
+
. "$SCRIPT_DIR/fm-control-lib.sh"
|
|
223
|
+
# shellcheck source=bin/fm-marker-lib.sh
|
|
224
|
+
. "$SCRIPT_DIR/fm-marker-lib.sh"
|
|
225
|
+
# shellcheck source=bin/fm-pending-reply-lib.sh
|
|
226
|
+
. "$SCRIPT_DIR/fm-pending-reply-lib.sh"
|
|
227
|
+
# shellcheck source=bin/fm-classify-lib.sh
|
|
228
|
+
. "$SCRIPT_DIR/fm-classify-lib.sh"
|
|
229
|
+
# shellcheck source=bin/fm-line-cap-lib.sh
|
|
230
|
+
. "$SCRIPT_DIR/fm-line-cap-lib.sh"
|
|
231
|
+
# shellcheck source=bin/fm-wake-lib.sh
|
|
232
|
+
. "$SCRIPT_DIR/fm-wake-lib.sh"
|
|
233
|
+
# shellcheck source=bin/fm-task-inbox-lib.sh
|
|
234
|
+
. "$SCRIPT_DIR/fm-task-inbox-lib.sh"
|
|
235
|
+
# shellcheck source=bin/fm-timeout-lib.sh
|
|
236
|
+
. "$SCRIPT_DIR/fm-timeout-lib.sh"
|
|
237
|
+
|
|
238
|
+
FM_GUARD_CONTINUE_LINE='This is a supervision warning only; the requested message WILL still be sent.' "$SCRIPT_DIR/fm-guard.sh" || true
|
|
239
|
+
|
|
240
|
+
fm_send_id_from_meta() { # <meta-file>
|
|
241
|
+
local base
|
|
242
|
+
base=${1##*/}
|
|
243
|
+
printf '%s' "${base%.meta}"
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
# fm_send_clear_after_interrupt: muse RESTORES the interrupted prompt back into
|
|
247
|
+
# the composer when Escape cancels a turn, as real bright text (verified: fg
|
|
248
|
+
# 38;2;204;211;219, luminance ~210, muse 0.1.0-R708.1), not de-emphasised ghost
|
|
249
|
+
# text. Classifying that as pending input is correct - the text really is
|
|
250
|
+
# unsubmitted - but leaving it there means the NEXT steer types onto the end of
|
|
251
|
+
# it and submits both as one garbled message. Ctrl-U clears the composer
|
|
252
|
+
# (verified), so the interrupt is not complete until it has been sent. A failed
|
|
253
|
+
# clear is loud rather than silent, because the alternative is a corrupted steer.
|
|
254
|
+
# WHICH adapters need that clear, and which key clears them, comes from the one
|
|
255
|
+
# control-plane capability table (bin/fm-control-lib.sh) rather than a second
|
|
256
|
+
# copy here - the same table bin/fm-control.sh's interrupt verb reads.
|
|
257
|
+
fm_send_clear_after_interrupt() { # <key>
|
|
258
|
+
local key=$1 family clear
|
|
259
|
+
[ "$key" = Escape ] || return 0
|
|
260
|
+
family=$(fm_control_harness_family "$TARGET_HARNESS") || return 0
|
|
261
|
+
clear=$(fm_control_interrupt_clear_key "$family") || return 0
|
|
262
|
+
[ -n "$clear" ] || return 0
|
|
263
|
+
[ "$TARGET_BACKEND" != remote ] || return 0
|
|
264
|
+
if ! fm_backend_send_key "$TARGET_BACKEND" "$T" "$clear" "$EXPECTED_LABEL"; then
|
|
265
|
+
echo "error: Escape reached $T, but the $TARGET_HARNESS composer could not be cleared; it still holds the restored prompt. Clear it before sending the next message." >&2
|
|
266
|
+
return 1
|
|
267
|
+
fi
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
fm_send_normalize_key() { # <key>
|
|
271
|
+
case "$1" in
|
|
272
|
+
Escape|escape|Esc|esc) printf '%s' Escape ;;
|
|
273
|
+
*) printf '%s' "$1" ;;
|
|
274
|
+
esac
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
fm_send_record_interrupt() { # <key>
|
|
278
|
+
local key=$1 id gen
|
|
279
|
+
[ "$key" = Escape ] || return 0
|
|
280
|
+
case "$TARGET_HARNESS" in claude*) : ;; *) return 0 ;; esac
|
|
281
|
+
[ -n "$TARGET_META" ] || return 0
|
|
282
|
+
id=$(fm_send_id_from_meta "$TARGET_META")
|
|
283
|
+
[ -f "$STATE/$id.busy-gen" ] || return 0
|
|
284
|
+
gen=$(fm_meta_get "$TARGET_META" busy_gen)
|
|
285
|
+
if [ -n "$gen" ]; then
|
|
286
|
+
"$FM_ROOT/bin/fm-busy-event.sh" apply "$STATE" "$id" idle \
|
|
287
|
+
--gen "$gen" --source fm-interrupt --event interrupt
|
|
288
|
+
else
|
|
289
|
+
"$FM_ROOT/bin/fm-busy-event.sh" apply "$STATE" "$id" idle \
|
|
290
|
+
--current-gen --source fm-interrupt --event interrupt
|
|
291
|
+
fi || {
|
|
292
|
+
echo "error: key '$key' reached $T, but the Claude interrupt state could not be recorded for $id" >&2
|
|
293
|
+
return 1
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
fm_send_meta_for_key_value() { # <state-dir> <key> <value>
|
|
298
|
+
local state=$1 key=$2 value=$3 meta got
|
|
299
|
+
for meta in "$state"/*.meta; do
|
|
300
|
+
[ -e "$meta" ] || continue
|
|
301
|
+
got=$(fm_meta_get "$meta" "$key")
|
|
302
|
+
[ "$got" = "$value" ] || continue
|
|
303
|
+
printf '%s' "$meta"
|
|
304
|
+
return 0
|
|
305
|
+
done
|
|
306
|
+
return 1
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
fm_send_count_colons() { # <string>
|
|
310
|
+
local s=$1 no_colons
|
|
311
|
+
no_colons=${s//:/}
|
|
312
|
+
printf '%s' $(( ${#s} - ${#no_colons} ))
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
fm_send_resolve_target() { # <raw-target>
|
|
316
|
+
local raw=$1 meta pane_meta target backend assumed colons id session hint
|
|
317
|
+
|
|
318
|
+
RESOLVED_TARGET=""
|
|
319
|
+
TARGET_BACKEND=""
|
|
320
|
+
TARGET_HARNESS=""
|
|
321
|
+
EXPECTED_LABEL=""
|
|
322
|
+
TARGET_META=""
|
|
323
|
+
TARGET_SELECTOR=""
|
|
324
|
+
TARGET_REMOTE_ID=""
|
|
325
|
+
TARGET_REMOTE_HOST=""
|
|
326
|
+
RESOLUTION_TRIED=""
|
|
327
|
+
|
|
328
|
+
meta=$(fm_backend_meta_for_selector "$raw" "$STATE" 2>/dev/null || true)
|
|
329
|
+
if [ -n "$meta" ]; then
|
|
330
|
+
if [ -n "$(fm_meta_get "$meta" remote_host)" ]; then
|
|
331
|
+
id=$(fm_send_id_from_meta "$meta")
|
|
332
|
+
RESOLVED_TARGET="remote:$id"
|
|
333
|
+
TARGET_BACKEND=remote
|
|
334
|
+
TARGET_META=$meta
|
|
335
|
+
TARGET_HARNESS=$(fm_meta_get "$meta" harness)
|
|
336
|
+
EXPECTED_LABEL="fm-$id"
|
|
337
|
+
TARGET_SELECTOR=1
|
|
338
|
+
TARGET_REMOTE_ID=$id
|
|
339
|
+
TARGET_REMOTE_HOST=$(fm_meta_get "$meta" remote_host)
|
|
340
|
+
RESOLUTION_TRIED="meta=$meta; placement=remote"
|
|
341
|
+
return 0
|
|
342
|
+
fi
|
|
343
|
+
RESOLUTION_TRIED="meta=$meta; backend=from-meta"
|
|
344
|
+
target=$(fm_backend_target_of_meta "$meta")
|
|
345
|
+
if [ -z "$target" ]; then
|
|
346
|
+
echo "error: no backend target recorded in $meta (tried $RESOLUTION_TRIED)" >&2
|
|
347
|
+
return 1
|
|
348
|
+
fi
|
|
349
|
+
backend=$(fm_backend_of_meta "$meta")
|
|
350
|
+
RESOLVED_TARGET=$target
|
|
351
|
+
TARGET_BACKEND=$backend
|
|
352
|
+
TARGET_META=$meta
|
|
353
|
+
TARGET_HARNESS=$(fm_meta_get "$meta" harness)
|
|
354
|
+
EXPECTED_LABEL=$(fm_backend_expected_label_of_selector "$raw" "$STATE")
|
|
355
|
+
TARGET_SELECTOR=1
|
|
356
|
+
return 0
|
|
357
|
+
fi
|
|
358
|
+
|
|
359
|
+
case "$raw" in
|
|
360
|
+
fm-*:*)
|
|
361
|
+
# A named Herdr session may itself begin with "fm-". Keep that explicit
|
|
362
|
+
# session:pane target on the validated backend-target path below rather
|
|
363
|
+
# than mistaking it for an unresolved task selector.
|
|
364
|
+
;;
|
|
365
|
+
fm-*)
|
|
366
|
+
RESOLUTION_TRIED="meta=$STATE/$raw.meta; legacy-meta=$STATE/${raw#fm-}.meta; backend=none"
|
|
367
|
+
echo "error: no metadata for $raw in $STATE (tried $RESOLUTION_TRIED); pass a well-formed explicit backend target only when targeting outside this firstmate home" >&2
|
|
368
|
+
return 1
|
|
369
|
+
;;
|
|
370
|
+
esac
|
|
371
|
+
|
|
372
|
+
pane_meta=$(fm_send_meta_for_key_value "$STATE" herdr_pane_id "$raw" 2>/dev/null || true)
|
|
373
|
+
if [ -n "$pane_meta" ]; then
|
|
374
|
+
session=$(fm_meta_get "$pane_meta" herdr_session)
|
|
375
|
+
hint="${session:-<herdr-session>}:$raw"
|
|
376
|
+
id=$(fm_send_id_from_meta "$pane_meta")
|
|
377
|
+
echo "error: target '$raw' matches herdr_pane_id in $pane_meta but is missing its herdr session prefix; expected <herdr-session>:<pane-id> such as '$hint' or use 'fm-$id' (tried meta=$STATE/$raw.meta; backend=herdr)" >&2
|
|
378
|
+
return 1
|
|
379
|
+
fi
|
|
380
|
+
|
|
381
|
+
meta=$(fm_backend_meta_for_window "$raw" "$STATE" 2>/dev/null || true)
|
|
382
|
+
if [ -n "$meta" ]; then
|
|
383
|
+
target=$(fm_backend_target_of_meta "$meta")
|
|
384
|
+
if [ -z "$target" ]; then
|
|
385
|
+
echo "error: no backend target recorded in $meta (tried explicit target '$raw' via recorded window/terminal; backend=from-meta)" >&2
|
|
386
|
+
return 1
|
|
387
|
+
fi
|
|
388
|
+
RESOLVED_TARGET=$target
|
|
389
|
+
TARGET_BACKEND=$(fm_backend_of_meta "$meta")
|
|
390
|
+
TARGET_META=$meta
|
|
391
|
+
TARGET_HARNESS=$(fm_meta_get "$meta" harness)
|
|
392
|
+
RESOLUTION_TRIED="explicit target '$raw' matched $meta; backend=$TARGET_BACKEND"
|
|
393
|
+
return 0
|
|
394
|
+
fi
|
|
395
|
+
|
|
396
|
+
case "$raw" in
|
|
397
|
+
*:*)
|
|
398
|
+
colons=$(fm_send_count_colons "$raw")
|
|
399
|
+
if [ "$colons" -ge 2 ]; then
|
|
400
|
+
assumed=herdr
|
|
401
|
+
else
|
|
402
|
+
assumed=tmux
|
|
403
|
+
fi
|
|
404
|
+
if ! fm_backend_target_exists "$assumed" "$raw"; then
|
|
405
|
+
echo "error: explicit target '$raw' is not a live $assumed endpoint (tried meta=$STATE/$raw.meta; metadata window/terminal lookup; backend=$assumed). Use fm-<id> for a recorded task/lane, or pass a target whose backend endpoint can be verified." >&2
|
|
406
|
+
return 1
|
|
407
|
+
fi
|
|
408
|
+
RESOLVED_TARGET=$raw
|
|
409
|
+
TARGET_BACKEND=$assumed
|
|
410
|
+
RESOLUTION_TRIED="meta=$STATE/$raw.meta; metadata window/terminal lookup; backend=$assumed; endpoint=verified"
|
|
411
|
+
return 0
|
|
412
|
+
;;
|
|
413
|
+
esac
|
|
414
|
+
|
|
415
|
+
echo "error: target '$raw' is not resolvable (tried meta=$STATE/$raw.meta; metadata window/terminal lookup; backend=none). Use fm-$raw for a recorded task/lane, or pass a well-formed explicit backend target such as session:window." >&2
|
|
416
|
+
return 1
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
RAW_TARGET=$1
|
|
420
|
+
fm_send_resolve_target "$RAW_TARGET" || exit 1
|
|
421
|
+
T=$RESOLVED_TARGET
|
|
422
|
+
shift
|
|
423
|
+
|
|
424
|
+
# Supervision lease guard: a steer is overlap territory between the two Pi
|
|
425
|
+
# supervision actors, so refuse while the OTHER actor holds this task's live
|
|
426
|
+
# lease. A home with no supervision branch has no lease files and passes
|
|
427
|
+
# untouched (contract: bin/fm-lease-lib.sh).
|
|
428
|
+
# shellcheck source=bin/fm-lease-lib.sh
|
|
429
|
+
. "$SCRIPT_DIR/fm-lease-lib.sh"
|
|
430
|
+
if [ -n "$TARGET_META" ]; then
|
|
431
|
+
LEASE_GUARD_TASK=$(fm_send_id_from_meta "$TARGET_META")
|
|
432
|
+
if [ -n "$LEASE_GUARD_TASK" ]; then
|
|
433
|
+
fm_lease_guard "$LEASE_GUARD_TASK" "steer (fm-send)"
|
|
434
|
+
trap 'fm_lease_guard_release' EXIT
|
|
435
|
+
fi
|
|
436
|
+
fi
|
|
437
|
+
|
|
438
|
+
# Collect --resolve-key flags (answerer-closes; see the header contract). They
|
|
439
|
+
# must precede --key or the message text; everything after the last flag is the
|
|
440
|
+
# message exactly as before, so ordinary sends are byte-identical.
|
|
441
|
+
RESOLVE_KEYS=
|
|
442
|
+
FIRE_AND_FORGET_ID=
|
|
443
|
+
fm_send_add_resolve_key() { # <key>
|
|
444
|
+
local k=$1
|
|
445
|
+
case "$k" in
|
|
446
|
+
''|*[!A-Za-z0-9._-]*)
|
|
447
|
+
echo "error: --resolve-key '$k' is not a valid decision key (allowed: A-Z a-z 0-9 . _ -)" >&2
|
|
448
|
+
return 1
|
|
449
|
+
;;
|
|
450
|
+
esac
|
|
451
|
+
case " $RESOLVE_KEYS " in
|
|
452
|
+
*" $k "*)
|
|
453
|
+
echo "error: duplicate --resolve-key '$k'" >&2
|
|
454
|
+
return 1
|
|
455
|
+
;;
|
|
456
|
+
esac
|
|
457
|
+
RESOLVE_KEYS="${RESOLVE_KEYS}${RESOLVE_KEYS:+ }$k"
|
|
458
|
+
}
|
|
459
|
+
while :; do
|
|
460
|
+
case "${1:-}" in
|
|
461
|
+
--resolve-key)
|
|
462
|
+
[ $# -ge 2 ] || { echo "error: --resolve-key requires a key" >&2; exit 1; }
|
|
463
|
+
fm_send_add_resolve_key "$2" || exit 1
|
|
464
|
+
shift 2
|
|
465
|
+
;;
|
|
466
|
+
--resolve-key=*)
|
|
467
|
+
fm_send_add_resolve_key "${1#--resolve-key=}" || exit 1
|
|
468
|
+
shift
|
|
469
|
+
;;
|
|
470
|
+
--fire-and-forget)
|
|
471
|
+
[ $# -ge 2 ] || { echo "error: --fire-and-forget requires a delivery id" >&2; exit 1; }
|
|
472
|
+
[ -z "$FIRE_AND_FORGET_ID" ] || { echo "error: duplicate --fire-and-forget" >&2; exit 1; }
|
|
473
|
+
FIRE_AND_FORGET_ID=$2
|
|
474
|
+
shift 2
|
|
475
|
+
;;
|
|
476
|
+
--fire-and-forget=*)
|
|
477
|
+
[ -z "$FIRE_AND_FORGET_ID" ] || { echo "error: duplicate --fire-and-forget" >&2; exit 1; }
|
|
478
|
+
FIRE_AND_FORGET_ID=${1#--fire-and-forget=}
|
|
479
|
+
shift
|
|
480
|
+
;;
|
|
481
|
+
*) break ;;
|
|
482
|
+
esac
|
|
483
|
+
done
|
|
484
|
+
|
|
485
|
+
if [ "$TARGET_BACKEND" != remote ]; then
|
|
486
|
+
fm_backend_validate "$TARGET_BACKEND" || exit 1
|
|
487
|
+
fi
|
|
488
|
+
|
|
489
|
+
# Classify a from-firstmate -> secondmate request. Only a task selector resolved
|
|
490
|
+
# through this home's meta whose authoritative kind is secondmate is marked: the
|
|
491
|
+
# secondmate then routes its reply via the status path (see fm-marker-lib.sh).
|
|
492
|
+
# An explicit backend target (the escape hatch for endpoints outside this home)
|
|
493
|
+
# and any crewmate/scout target are left unmarked, and so is the --key path.
|
|
494
|
+
MARK_FROM_FIRSTMATE=0
|
|
495
|
+
PENDING_REPLY_CORR=
|
|
496
|
+
PENDING_REPLY_CREATED=0
|
|
497
|
+
TARGET_TASK_ID=
|
|
498
|
+
fm_send_known_undelivered_cleanup() {
|
|
499
|
+
[ -n "$PENDING_REPLY_CORR" ] || return 0
|
|
500
|
+
if [ "$PENDING_REPLY_CREATED" = 1 ]; then
|
|
501
|
+
fm_pending_reply_discard_undelivered "$STATE" "$PENDING_REPLY_CORR"
|
|
502
|
+
else
|
|
503
|
+
fm_pending_reply_reset_known_undelivered "$STATE" "$PENDING_REPLY_CORR"
|
|
504
|
+
fi
|
|
505
|
+
}
|
|
506
|
+
if [ -n "$TARGET_SELECTOR" ] && [ -n "$TARGET_META" ] && [ "$(fm_meta_get "$TARGET_META" kind)" = secondmate ]; then
|
|
507
|
+
MARK_FROM_FIRSTMATE=1
|
|
508
|
+
TARGET_TASK_ID=$(fm_send_id_from_meta "$TARGET_META")
|
|
509
|
+
fi
|
|
510
|
+
|
|
511
|
+
# Validate the answerer-closes request before any durable mutation or send: the
|
|
512
|
+
# target must have a task ledger in THIS home, the send must carry an answer
|
|
513
|
+
# message, and every named key must be open right now in that ledger per the
|
|
514
|
+
# ONE authoritative fold (status_open_decisions). Refusing here, before the
|
|
515
|
+
# send, is what keeps a mistyped key loud instead of delivering an answer that
|
|
516
|
+
# silently leaves its decision open.
|
|
517
|
+
RESOLVE_STATUS_FILE=
|
|
518
|
+
# Which ledger each answered key belongs to. A key still open in the status log
|
|
519
|
+
# is owned by the status log: fm-captain-hold's `complete` closes that live copy
|
|
520
|
+
# at the moment it transfers a decision to its durable captain-held task, so
|
|
521
|
+
# "still open in status" and "already held" are the two sides of one transfer,
|
|
522
|
+
# never both at once. Checking the backlog only for keys the status log no
|
|
523
|
+
# longer owns also keeps the common path free of any backlog read.
|
|
524
|
+
RESOLVE_STATUS_KEYS=
|
|
525
|
+
RESOLVE_HOLD_KEYS=
|
|
526
|
+
|
|
527
|
+
# Resolve a --resolve-key key that the status log no longer owns to the
|
|
528
|
+
# captain-held task that carries it: the key as a task id itself (the collapsed
|
|
529
|
+
# identity - a captain call IS a task held for the captain), then the legacy
|
|
530
|
+
# derived `<task>-decision-<key>` identity for pre-collapse rows. Answerable
|
|
531
|
+
# means not closed and still carrying the captain-hold annotations tasks-axi
|
|
532
|
+
# preserves even past a hold-until date.
|
|
533
|
+
fm_send_hold_resolved_id() { # <task-id> <decision-key>
|
|
534
|
+
local show id state hold_kind
|
|
535
|
+
command -v tasks-axi >/dev/null 2>&1 || return 1
|
|
536
|
+
for id in "$2" "$1-decision-$2"; do
|
|
537
|
+
show=$( (cd "$FM_HOME" && tasks-axi show "$id" --full) 2>/dev/null ) || continue
|
|
538
|
+
state=$(printf '%s\n' "$show" | sed -n 's/^ state: //p' | head -1)
|
|
539
|
+
hold_kind=$(printf '%s\n' "$show" | sed -n 's/^ hold_kind: //p' | head -1)
|
|
540
|
+
[ "$state" != "done" ] || continue
|
|
541
|
+
[ "$hold_kind" = captain ] || continue
|
|
542
|
+
printf '%s\n' "$id"
|
|
543
|
+
return 0
|
|
544
|
+
done
|
|
545
|
+
return 1
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
if [ -n "$FIRE_AND_FORGET_ID" ]; then
|
|
549
|
+
printf '%s' "$FIRE_AND_FORGET_ID" | grep -Eq '^[a-f0-9]{16}$' \
|
|
550
|
+
|| { echo "error: --fire-and-forget delivery id must be 16 lowercase hex characters" >&2; exit 1; }
|
|
551
|
+
[ "$MARK_FROM_FIRSTMATE" = 1 ] \
|
|
552
|
+
|| { echo "error: --fire-and-forget requires a recorded secondmate task selector" >&2; exit 1; }
|
|
553
|
+
[ -z "$RESOLVE_KEYS" ] \
|
|
554
|
+
|| { echo "error: --fire-and-forget cannot accompany --resolve-key" >&2; exit 1; }
|
|
555
|
+
fi
|
|
556
|
+
|
|
557
|
+
if [ -n "$RESOLVE_KEYS" ]; then
|
|
558
|
+
if [ -z "$TARGET_SELECTOR" ] || [ -z "$TARGET_META" ]; then
|
|
559
|
+
echo "error: --resolve-key needs a task selector resolved through this home's metadata; an explicit backend target has no decision ledger here" >&2
|
|
560
|
+
exit 1
|
|
561
|
+
fi
|
|
562
|
+
if [ "${1:-}" = "--key" ]; then
|
|
563
|
+
echo "error: --resolve-key cannot accompany --key; answering a decision requires a text answer" >&2
|
|
564
|
+
exit 1
|
|
565
|
+
fi
|
|
566
|
+
if [ -z "$*" ]; then
|
|
567
|
+
echo "error: --resolve-key requires a nonempty answer message" >&2
|
|
568
|
+
exit 1
|
|
569
|
+
fi
|
|
570
|
+
RESOLVE_TASK_ID=$(fm_send_id_from_meta "$TARGET_META")
|
|
571
|
+
RESOLVE_STATUS_FILE="$STATE/$RESOLVE_TASK_ID.status"
|
|
572
|
+
resolve_open_set=$(status_open_decisions "$RESOLVE_STATUS_FILE")
|
|
573
|
+
for k in $RESOLVE_KEYS; do
|
|
574
|
+
case "$resolve_open_set" in
|
|
575
|
+
"$k"$'\t'*|*$'\n'"$k"$'\t'*)
|
|
576
|
+
RESOLVE_STATUS_KEYS="${RESOLVE_STATUS_KEYS}${RESOLVE_STATUS_KEYS:+ }$k"
|
|
577
|
+
continue
|
|
578
|
+
;;
|
|
579
|
+
esac
|
|
580
|
+
# Not open in the status log. A decision already transferred to its durable
|
|
581
|
+
# captain-held task is exactly this case, and it is answerable - just
|
|
582
|
+
# through the other ledger - so check there before refusing.
|
|
583
|
+
if resolved_hold_id=$(fm_send_hold_resolved_id "$RESOLVE_TASK_ID" "$k"); then
|
|
584
|
+
RESOLVE_HOLD_KEYS="${RESOLVE_HOLD_KEYS}${RESOLVE_HOLD_KEYS:+ }$resolved_hold_id"
|
|
585
|
+
continue
|
|
586
|
+
fi
|
|
587
|
+
echo "error: --resolve-key '$k': no open decision or blocker with that key in $RESOLVE_STATUS_FILE, and no captain-held task '$k' or '$RESOLVE_TASK_ID-decision-$k' still open (already closed or mistyped). Re-check the OPEN DECISIONS listing, then resend without that key or with the right one; nothing was sent." >&2
|
|
588
|
+
exit 1
|
|
589
|
+
done
|
|
590
|
+
fi
|
|
591
|
+
|
|
592
|
+
# Close each answered decision in this home's ledger, only after the answer is
|
|
593
|
+
# durably sent: enqueued on the inbox plane, submit-confirmed on the typed
|
|
594
|
+
# plane. An append failure exits nonzero with the manual close
|
|
595
|
+
# command; the decision then stays open and re-surfaces, never silently lost.
|
|
596
|
+
# The close is this home's own bookkeeping, written by the very turn that
|
|
597
|
+
# answered the decision, so it goes through the guarded self-announced append
|
|
598
|
+
# (bin/fm-wake-lib.sh) and does not wake this same session again; any
|
|
599
|
+
# concurrent foreign status bytes leave the watcher's wake path untouched.
|
|
600
|
+
fm_send_close_resolved_keys() { # <answer-text>
|
|
601
|
+
local note=$1 k line append_rc
|
|
602
|
+
note=$(printf '%s' "$note" | tr '\n\r\t' ' ' | LC_ALL=C tr -d '\000-\037\177')
|
|
603
|
+
for k in $RESOLVE_STATUS_KEYS; do
|
|
604
|
+
line="resolved [key=$k]: answered: $note"
|
|
605
|
+
fm_cap_line_var "$line"
|
|
606
|
+
append_rc=0
|
|
607
|
+
fm_wake_status_append_self_announced "$STATE" "$RESOLVE_STATUS_FILE" "$FM_LINE_CAP_LINE" || append_rc=$?
|
|
608
|
+
if [ "$append_rc" -eq 2 ]; then
|
|
609
|
+
echo "error: the answer was delivered to $T, but decision key '$k' could not be closed in $RESOLVE_STATUS_FILE. Close it manually with: echo 'resolved [key=$k]: <how it was answered>' >> $RESOLVE_STATUS_FILE - do not resend the answer." >&2
|
|
610
|
+
return 1
|
|
611
|
+
fi
|
|
612
|
+
done
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
# Feed the answered captain-held tasks to the ONE keyed-answer intake, as keyed
|
|
616
|
+
# lines, exactly the way every other channel does. fm-send decides nothing here:
|
|
617
|
+
# it does not build a decision record or choose a close path; the keys were
|
|
618
|
+
# already resolved to task ids above, so the intake needs no legacy origin.
|
|
619
|
+
fm_send_feed_resolved_holds() { # <answer-text>
|
|
620
|
+
local note=$1 k lines=''
|
|
621
|
+
[ -n "$RESOLVE_HOLD_KEYS" ] || return 0
|
|
622
|
+
note=$(printf '%s' "$note" | tr '\n\r\t' ' ' | LC_ALL=C tr -d '\000-\037\177')
|
|
623
|
+
for k in $RESOLVE_HOLD_KEYS; do
|
|
624
|
+
lines="${lines}${k}"$'\t'"${note}"$'\t'$'\n'
|
|
625
|
+
done
|
|
626
|
+
if ! printf '%s' "$lines" | "$SCRIPT_DIR/fm-captain-hold.sh" answers \
|
|
627
|
+
--source "a firstmate answer sent to $RESOLVE_TASK_ID" >/dev/null 2>&1; then
|
|
628
|
+
echo "error: the answer was delivered to $T, but this captain-held task could not be closed: ${RESOLVE_HOLD_KEYS}. Close it with fm-captain-hold.sh answer - do not resend the answer." >&2
|
|
629
|
+
return 1
|
|
630
|
+
fi
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
# Resolve the target's harness from its meta (recorded by fm-spawn), used only to
|
|
634
|
+
# scope the codex `$<skill>` popup-settle below. A task selector carries
|
|
635
|
+
# meta; an explicit backend-target escape hatch has none, so its harness is
|
|
636
|
+
# unknown and treated as non-codex (the safe default that keeps the fast path).
|
|
637
|
+
# The target's BACKEND comes from selector meta, from matching an explicit target
|
|
638
|
+
# back to recorded meta, or from strict explicit-target shape validation.
|
|
639
|
+
# Do not add a separate passive liveness preflight here. Active send paths own
|
|
640
|
+
# backend readiness: herdr, for example, must route through its session-aware
|
|
641
|
+
# target_ready path before sending, while zellij verifies pane labels in its
|
|
642
|
+
# send implementation. A failed backend send is still surfaced below as a hard
|
|
643
|
+
# error with the attempted resolution attached.
|
|
644
|
+
|
|
645
|
+
if [ "${1:-}" = "--key" ]; then
|
|
646
|
+
[ -z "$FIRE_AND_FORGET_ID" ] \
|
|
647
|
+
|| { echo "error: --fire-and-forget cannot accompany --key" >&2; exit 1; }
|
|
648
|
+
case "$*" in
|
|
649
|
+
*--resolve-key*)
|
|
650
|
+
echo "error: --resolve-key cannot accompany --key; answering a decision requires a text answer" >&2
|
|
651
|
+
exit 1
|
|
652
|
+
;;
|
|
653
|
+
esac
|
|
654
|
+
key=$2
|
|
655
|
+
semantic_key=$(fm_send_normalize_key "$key")
|
|
656
|
+
if [ "$TARGET_BACKEND" = remote ]; then
|
|
657
|
+
FM_SEND_REMOTE_BUDGET=${FM_SEND_REMOTE_BUDGET:-30}
|
|
658
|
+
case "$FM_SEND_REMOTE_BUDGET" in
|
|
659
|
+
''|*[!0-9]*|0)
|
|
660
|
+
echo "error: FM_SEND_REMOTE_BUDGET must be a positive integer: $FM_SEND_REMOTE_BUDGET" >&2
|
|
661
|
+
exit 1
|
|
662
|
+
;;
|
|
663
|
+
esac
|
|
664
|
+
if ! fm_run_timed "$FM_SEND_REMOTE_BUDGET" "$SCRIPT_DIR/fm-on.sh" "$TARGET_REMOTE_ID" \
|
|
665
|
+
fm-remote-secondmate-control.sh key "$TARGET_REMOTE_ID" "$key" < /dev/null; then
|
|
666
|
+
echo "error: key '$key' not sent to remote secondmate $TARGET_REMOTE_ID; completion may be unknown" >&2
|
|
667
|
+
exit 1
|
|
668
|
+
fi
|
|
669
|
+
elif ! fm_backend_send_key "$TARGET_BACKEND" "$T" "$key" "$EXPECTED_LABEL"; then
|
|
670
|
+
echo "error: key '$key' not sent to $T ($TARGET_BACKEND send failed; tried $RESOLUTION_TRIED)" >&2
|
|
671
|
+
exit 1
|
|
672
|
+
fi
|
|
673
|
+
fm_send_clear_after_interrupt "$semantic_key" || exit 1
|
|
674
|
+
fm_send_record_interrupt "$semantic_key" || exit 1
|
|
675
|
+
else
|
|
676
|
+
MESSAGE=$*
|
|
677
|
+
if [ "$TARGET_BACKEND" = remote ]; then
|
|
678
|
+
FM_SEND_REMOTE_BUDGET=${FM_SEND_REMOTE_BUDGET:-30}
|
|
679
|
+
case "$FM_SEND_REMOTE_BUDGET" in
|
|
680
|
+
''|*[!0-9]*|0)
|
|
681
|
+
echo "error: FM_SEND_REMOTE_BUDGET must be a positive integer: $FM_SEND_REMOTE_BUDGET" >&2
|
|
682
|
+
exit 1
|
|
683
|
+
;;
|
|
684
|
+
esac
|
|
685
|
+
fi
|
|
686
|
+
# The pre-marker answer text, kept for the closing resolved note so the
|
|
687
|
+
# durable ledger records the plain answer without marker or corr bytes.
|
|
688
|
+
RESOLVE_ANSWER_TEXT=$MESSAGE
|
|
689
|
+
if [ "$MARK_FROM_FIRSTMATE" = 1 ] && [ -n "$FIRE_AND_FORGET_ID" ]; then
|
|
690
|
+
fm_message_mark_from_firstmate "$MESSAGE" MESSAGE
|
|
691
|
+
MESSAGE="${FM_FROMFIRST_MARK}delivery=${FIRE_AND_FORGET_ID} ${MESSAGE#"$FM_FROMFIRST_MARK"}"
|
|
692
|
+
FM_SEND_IDEMPOTENT=1
|
|
693
|
+
elif [ "$MARK_FROM_FIRSTMATE" = 1 ]; then
|
|
694
|
+
# Reuse an existing correlation id for recovery resends; otherwise create a
|
|
695
|
+
# durable parent expectation before delivery. Transport success never
|
|
696
|
+
# resolves that expectation (see fm-pending-reply-lib.sh).
|
|
697
|
+
existing_corr_explicit=0
|
|
698
|
+
if [ "${FM_PENDING_REPLY_EXISTING_CORR+x}" = x ]; then
|
|
699
|
+
existing_corr_explicit=1
|
|
700
|
+
existing_corr=$FM_PENDING_REPLY_EXISTING_CORR
|
|
701
|
+
else
|
|
702
|
+
existing_corr=$(fm_pending_reply_extract_corr "$MESSAGE")
|
|
703
|
+
fi
|
|
704
|
+
if [ -n "$existing_corr" ] \
|
|
705
|
+
&& fm_pending_reply_corr_reusable "$STATE" "$existing_corr" "$TARGET_TASK_ID"; then
|
|
706
|
+
PENDING_REPLY_CORR=$existing_corr
|
|
707
|
+
else
|
|
708
|
+
if [ "$existing_corr_explicit" = 1 ]; then
|
|
709
|
+
echo "error: explicitly requested pending-reply correlation '${existing_corr:-empty}' is not reusable for $TARGET_TASK_ID; refusing to mint a replacement correlation" >&2
|
|
710
|
+
exit 1
|
|
711
|
+
fi
|
|
712
|
+
if [ -z "$TARGET_TASK_ID" ]; then
|
|
713
|
+
echo "error: cannot create pending-reply expectation without a resolvable secondmate task id" >&2
|
|
714
|
+
exit 1
|
|
715
|
+
fi
|
|
716
|
+
PENDING_REPLY_CORR=$(fm_pending_reply_create "$FM_HOME" "$STATE" "$TARGET_TASK_ID" "$MESSAGE") \
|
|
717
|
+
|| { echo "error: failed to create parent pending-reply expectation for $TARGET_TASK_ID" >&2; exit 1; }
|
|
718
|
+
PENDING_REPLY_CREATED=1
|
|
719
|
+
fi
|
|
720
|
+
fm_pending_reply_embed_corr "$MESSAGE" "$PENDING_REPLY_CORR" MESSAGE
|
|
721
|
+
if [ "$PENDING_REPLY_CREATED" != 1 ] \
|
|
722
|
+
&& fm_pending_reply_delivery_attempt_unresolved "$STATE" "$PENDING_REPLY_CORR"; then
|
|
723
|
+
if [ "$TARGET_BACKEND" = remote ]; then
|
|
724
|
+
if ! fm_pending_reply_reset_known_undelivered "$STATE" "$PENDING_REPLY_CORR"; then
|
|
725
|
+
echo "error: pending-reply delivery for $TARGET_TASK_ID could not be reset for an idempotent remote resend of correlation $PENDING_REPLY_CORR" >&2
|
|
726
|
+
exit 1
|
|
727
|
+
fi
|
|
728
|
+
else
|
|
729
|
+
echo "error: pending-reply delivery for $TARGET_TASK_ID is unresolved; refusing to resend correlation $PENDING_REPLY_CORR" >&2
|
|
730
|
+
exit 1
|
|
731
|
+
fi
|
|
732
|
+
fi
|
|
733
|
+
if ! fm_pending_reply_prepare_delivery "$STATE" "$PENDING_REPLY_CORR"; then
|
|
734
|
+
[ "$PENDING_REPLY_CREATED" != 1 ] \
|
|
735
|
+
|| fm_pending_reply_discard_undelivered "$STATE" "$PENDING_REPLY_CORR" || true
|
|
736
|
+
echo "error: failed to durably prepare pending-reply delivery for $TARGET_TASK_ID" >&2
|
|
737
|
+
exit 1
|
|
738
|
+
fi
|
|
739
|
+
fi
|
|
740
|
+
# Data-plane selection (see the header): text addressed to a task selector
|
|
741
|
+
# resolved through this home's metadata rides the inbox plane, unless it is
|
|
742
|
+
# a LOCAL harness-native invocation that must reach the harness's own parser
|
|
743
|
+
# - a leading "/" (slash command), or a leading "$" to a codex target (skill
|
|
744
|
+
# invocation). A remote secondmate selector always rides the inbox: its
|
|
745
|
+
# requests are marked, and a marked request reaches the harness as
|
|
746
|
+
# marker-prefixed chat rather than a parser command anyway, so no remote
|
|
747
|
+
# text has a typed plane to lose. An explicit backend target stays typed
|
|
748
|
+
# even when it happens to match local metadata: it names an endpoint, not a
|
|
749
|
+
# task, the same boundary that keeps it unmarked and outside --resolve-key.
|
|
750
|
+
# Classification reads the pre-marker text so a marked secondmate request
|
|
751
|
+
# and a plain crewmate steer classify identically. It deliberately does NOT
|
|
752
|
+
# promise that a marked parser-native secondmate request executes as a parser
|
|
753
|
+
# command: the pre-existing marker-first wire bytes are retained in stage 1.
|
|
754
|
+
INBOX_PLANE=0
|
|
755
|
+
if [ -n "$TARGET_SELECTOR" ]; then
|
|
756
|
+
if [ -n "$FIRE_AND_FORGET_ID" ] || [ "$TARGET_BACKEND" = remote ]; then
|
|
757
|
+
INBOX_PLANE=1
|
|
758
|
+
else
|
|
759
|
+
case "$RESOLVE_ANSWER_TEXT" in
|
|
760
|
+
/*) ;;
|
|
761
|
+
\$*) [ "$TARGET_HARNESS" = codex ] || INBOX_PLANE=1 ;;
|
|
762
|
+
*) INBOX_PLANE=1 ;;
|
|
763
|
+
esac
|
|
764
|
+
fi
|
|
765
|
+
fi
|
|
766
|
+
if [ "$INBOX_PLANE" = 1 ] && [ "$TARGET_BACKEND" = remote ]; then
|
|
767
|
+
# Remote inbox leg: the message becomes a durable record in the remote
|
|
768
|
+
# home's steering inbox, written idempotently by the host-local leg, then
|
|
769
|
+
# the remote doorbell rings, best-effort. One identical retry after ssh
|
|
770
|
+
# 255 is safe by that idempotence; a still-lost transport preserves a
|
|
771
|
+
# reply-bearing request's expectation, while fire-and-forget reports the
|
|
772
|
+
# delivery id that must be reused, because the record may have landed.
|
|
773
|
+
# Every transport attempt is bounded by FM_SEND_REMOTE_BUDGET seconds
|
|
774
|
+
# (default 30, overridable) so a busy remote queue cannot hold this send
|
|
775
|
+
# open indefinitely; a bound hit exits through the same
|
|
776
|
+
# unconfirmed-delivery contract.
|
|
777
|
+
REMOTE_META_LOCK=$(fm_meta_lock_path "$TARGET_META") || exit 1
|
|
778
|
+
if ! fm_task_inbox_lock_acquire "$REMOTE_META_LOCK"; then
|
|
779
|
+
if [ "$PENDING_REPLY_CREATED" = 1 ] && [ -n "$PENDING_REPLY_CORR" ]; then
|
|
780
|
+
fm_pending_reply_discard_undelivered "$STATE" "$PENDING_REPLY_CORR" || true
|
|
781
|
+
fi
|
|
782
|
+
echo "error: steer not sent to remote secondmate $TARGET_REMOTE_ID: its parent task metadata could not be locked for final delivery validation" >&2
|
|
783
|
+
exit 1
|
|
784
|
+
fi
|
|
785
|
+
CURRENT_REMOTE_ID=
|
|
786
|
+
CURRENT_REMOTE_HOST=
|
|
787
|
+
CURRENT_REMOTE_SPAWN_GEN=
|
|
788
|
+
if [ -f "$TARGET_META" ]; then
|
|
789
|
+
CURRENT_REMOTE_ID=$(fm_send_id_from_meta "$TARGET_META")
|
|
790
|
+
CURRENT_REMOTE_HOST=$(fm_meta_get "$TARGET_META" remote_host)
|
|
791
|
+
CURRENT_REMOTE_SPAWN_GEN=$(fm_meta_get "$TARGET_META" spawn_gen)
|
|
792
|
+
fi
|
|
793
|
+
if [ "$CURRENT_REMOTE_ID" != "$TARGET_REMOTE_ID" ] \
|
|
794
|
+
|| { [ -n "${FM_SEND_EXPECTED_SPAWN_GEN:-}" ] \
|
|
795
|
+
&& [ "$CURRENT_REMOTE_SPAWN_GEN" != "$FM_SEND_EXPECTED_SPAWN_GEN" ]; } \
|
|
796
|
+
|| { [ -n "${FM_SEND_EXPECTED_REMOTE_HOST:-}" ] \
|
|
797
|
+
&& [ "$CURRENT_REMOTE_HOST" != "$FM_SEND_EXPECTED_REMOTE_HOST" ]; } \
|
|
798
|
+
|| [ -z "$CURRENT_REMOTE_HOST" ] \
|
|
799
|
+
|| [ "$CURRENT_REMOTE_HOST" != "$TARGET_REMOTE_HOST" ]; then
|
|
800
|
+
fm_lock_release "$REMOTE_META_LOCK"
|
|
801
|
+
if [ "$PENDING_REPLY_CREATED" = 1 ] && [ -n "$PENDING_REPLY_CORR" ]; then
|
|
802
|
+
fm_pending_reply_discard_undelivered "$STATE" "$PENDING_REPLY_CORR" || true
|
|
803
|
+
fi
|
|
804
|
+
echo "error: steer not sent to remote secondmate $TARGET_REMOTE_ID: its parent task retired or changed route during target resolution" >&2
|
|
805
|
+
exit 1
|
|
806
|
+
fi
|
|
807
|
+
remote_rc=0
|
|
808
|
+
remote_completion_unknown=0
|
|
809
|
+
REMOTE_SEND_ARGS=("$TARGET_REMOTE_ID" "$MESSAGE")
|
|
810
|
+
[ -z "$FIRE_AND_FORGET_ID" ] || REMOTE_SEND_ARGS+=(fire-and-forget)
|
|
811
|
+
# Each transport attempt is bounded by FM_SEND_REMOTE_BUDGET seconds.
|
|
812
|
+
# fm_run_timed's 124 means the attempt was killed at the bound with remote
|
|
813
|
+
# completion unknown - the enqueue may have landed - so it exits through
|
|
814
|
+
# the same unconfirmed-delivery contract as a lost transport, without a
|
|
815
|
+
# retry that would only wait out the same busy remote queue again. (A
|
|
816
|
+
# remote job's own timeout also relays as 124; treating it as unconfirmed
|
|
817
|
+
# stays safe because the remote enqueue deduplicates.)
|
|
818
|
+
fm_run_timed "$FM_SEND_REMOTE_BUDGET" "$SCRIPT_DIR/fm-on.sh" "$TARGET_REMOTE_ID" \
|
|
819
|
+
fm-remote-secondmate-control.sh send "${REMOTE_SEND_ARGS[@]}" < /dev/null || remote_rc=$?
|
|
820
|
+
if [ "$remote_rc" -eq 124 ]; then
|
|
821
|
+
remote_completion_unknown=1
|
|
822
|
+
elif [ "$remote_rc" -eq 255 ]; then
|
|
823
|
+
remote_completion_unknown=1
|
|
824
|
+
remote_rc=0
|
|
825
|
+
fm_run_timed "$FM_SEND_REMOTE_BUDGET" "$SCRIPT_DIR/fm-on.sh" "$TARGET_REMOTE_ID" \
|
|
826
|
+
fm-remote-secondmate-control.sh send "${REMOTE_SEND_ARGS[@]}" < /dev/null || remote_rc=$?
|
|
827
|
+
fi
|
|
828
|
+
fm_lock_release "$REMOTE_META_LOCK"
|
|
829
|
+
if [ "$remote_rc" -ne 0 ] && [ "$remote_completion_unknown" -eq 1 ]; then
|
|
830
|
+
if [ -n "$FIRE_AND_FORGET_ID" ]; then
|
|
831
|
+
echo "error: fire-and-forget steer to remote secondmate $TARGET_REMOTE_ID is unconfirmed (delivery-id=$FIRE_AND_FORGET_ID); retry only with the same delivery id" >&2
|
|
832
|
+
exit 3
|
|
833
|
+
fi
|
|
834
|
+
if [ -n "$PENDING_REPLY_CORR" ]; then
|
|
835
|
+
fm_pending_reply_mark_delivery_unknown "$STATE" "$PENDING_REPLY_CORR" || true
|
|
836
|
+
fi
|
|
837
|
+
if [ "$remote_rc" -eq 255 ]; then
|
|
838
|
+
echo "error: steer to remote secondmate $TARGET_REMOTE_ID is unconfirmed (transport lost twice; remote completion unknown). Only the correlation-reusing resend below is idempotent and lands on the same remote inbox record:" >&2
|
|
839
|
+
elif [ "$remote_rc" -eq 124 ]; then
|
|
840
|
+
echo "error: steer to remote secondmate $TARGET_REMOTE_ID is unconfirmed (the remote transport did not complete within its ${FM_SEND_REMOTE_BUDGET}s budget; remote completion unknown). Only the correlation-reusing resend below is idempotent and lands on the same remote inbox record:" >&2
|
|
841
|
+
else
|
|
842
|
+
echo "error: steer to remote secondmate $TARGET_REMOTE_ID is unconfirmed (the first transport attempt had unknown completion and the retry failed). Only the correlation-reusing resend below is idempotent and lands on the same remote inbox record:" >&2
|
|
843
|
+
fi
|
|
844
|
+
resend_home=$(cd "$FM_HOME" 2>/dev/null && pwd) || resend_home=$FM_HOME
|
|
845
|
+
printf 'FM_HOME=%q ' "$resend_home" >&2
|
|
846
|
+
if [ "${FM_STATE_OVERRIDE+x}" = x ]; then
|
|
847
|
+
resend_state=$(cd "$STATE" 2>/dev/null && pwd) || resend_state=$STATE
|
|
848
|
+
printf 'FM_STATE_OVERRIDE=%q ' "$resend_state" >&2
|
|
849
|
+
fi
|
|
850
|
+
printf 'FM_PENDING_REPLY_EXISTING_CORR=%q %q' "$PENDING_REPLY_CORR" "$SCRIPT_DIR/fm-send.sh" >&2
|
|
851
|
+
for resend_arg in "${FM_SEND_ORIGINAL_ARGS[@]}"; do
|
|
852
|
+
printf ' %q' "$resend_arg" >&2
|
|
853
|
+
done
|
|
854
|
+
printf '\n' >&2
|
|
855
|
+
exit 1
|
|
856
|
+
fi
|
|
857
|
+
if [ "$remote_rc" -ne 0 ]; then
|
|
858
|
+
fm_send_known_undelivered_cleanup || \
|
|
859
|
+
echo "error: known-undelivered pending-reply state could not be reset for $TARGET_TASK_ID" >&2
|
|
860
|
+
echo "error: steer not sent to remote secondmate $TARGET_REMOTE_ID (the remote steering-inbox record could not be written; the remote leg's stderr above has the reason)" >&2
|
|
861
|
+
exit 1
|
|
862
|
+
fi
|
|
863
|
+
# The remote record is durable delivery, exactly as a local enqueue is.
|
|
864
|
+
if [ -n "$PENDING_REPLY_CORR" ]; then
|
|
865
|
+
if fm_pending_reply_confirm_delivery "$STATE" "$PENDING_REPLY_CORR"; then
|
|
866
|
+
:
|
|
867
|
+
else
|
|
868
|
+
delivery_commit_status=$?
|
|
869
|
+
if [ "$delivery_commit_status" = 2 ]; then
|
|
870
|
+
echo "notice: the steer was durably recorded in the remote inbox, but its pending-reply delivery commit failed; a durable recovery marker was stored and the watcher will reconcile it. Do not resend." >&2
|
|
871
|
+
else
|
|
872
|
+
echo "warning: reply-tracking-degraded (steer delivered, do not resend): the steer was durably recorded in the remote inbox, but its pending-reply delivery commit and recovery marker both failed, so the reply expectation for this request may not reconcile on its own. Inspect $STATE." >&2
|
|
873
|
+
fi
|
|
874
|
+
fi
|
|
875
|
+
fi
|
|
876
|
+
if [ -n "$RESOLVE_KEYS" ]; then
|
|
877
|
+
fm_send_close_resolved_keys "$RESOLVE_ANSWER_TEXT" || exit 1
|
|
878
|
+
fm_send_feed_resolved_holds "$RESOLVE_ANSWER_TEXT" || exit 1
|
|
879
|
+
fi
|
|
880
|
+
exit 0
|
|
881
|
+
fi
|
|
882
|
+
if [ "$INBOX_PLANE" = 1 ]; then
|
|
883
|
+
INBOX_TASK_ID=$(fm_send_id_from_meta "$TARGET_META")
|
|
884
|
+
INBOX_META_LOCK=$(fm_meta_lock_path "$TARGET_META") || exit 1
|
|
885
|
+
if ! fm_task_inbox_lock_acquire "$INBOX_META_LOCK"; then
|
|
886
|
+
if [ "$PENDING_REPLY_CREATED" = 1 ] && [ -n "$PENDING_REPLY_CORR" ]; then
|
|
887
|
+
fm_pending_reply_discard_undelivered "$STATE" "$PENDING_REPLY_CORR" || true
|
|
888
|
+
fi
|
|
889
|
+
echo "error: steer not sent to $INBOX_TASK_ID: its task metadata could not be locked for final delivery validation" >&2
|
|
890
|
+
exit 1
|
|
891
|
+
fi
|
|
892
|
+
CURRENT_INBOX_TARGET=
|
|
893
|
+
CURRENT_INBOX_BACKEND=
|
|
894
|
+
CURRENT_INBOX_SPAWN_GEN=
|
|
895
|
+
if [ -f "$TARGET_META" ]; then
|
|
896
|
+
CURRENT_INBOX_TARGET=$(fm_backend_target_of_meta "$TARGET_META")
|
|
897
|
+
CURRENT_INBOX_BACKEND=$(fm_backend_of_meta "$TARGET_META")
|
|
898
|
+
CURRENT_INBOX_SPAWN_GEN=$(fm_meta_get "$TARGET_META" spawn_gen)
|
|
899
|
+
fi
|
|
900
|
+
if [ "$CURRENT_INBOX_TARGET" != "$T" ] \
|
|
901
|
+
|| [ "$CURRENT_INBOX_BACKEND" != "$TARGET_BACKEND" ] \
|
|
902
|
+
|| { [ -n "${FM_SEND_EXPECTED_SPAWN_GEN:-}" ] \
|
|
903
|
+
&& [ "$CURRENT_INBOX_SPAWN_GEN" != "$FM_SEND_EXPECTED_SPAWN_GEN" ]; } \
|
|
904
|
+
|| [ -n "$(fm_meta_get "$TARGET_META" remote_host)" ]; then
|
|
905
|
+
fm_lock_release "$INBOX_META_LOCK"
|
|
906
|
+
if [ "$PENDING_REPLY_CREATED" = 1 ] && [ -n "$PENDING_REPLY_CORR" ]; then
|
|
907
|
+
fm_pending_reply_discard_undelivered "$STATE" "$PENDING_REPLY_CORR" || true
|
|
908
|
+
fi
|
|
909
|
+
echo "error: steer not sent to $INBOX_TASK_ID: the task retired or changed endpoint during target resolution" >&2
|
|
910
|
+
exit 1
|
|
911
|
+
fi
|
|
912
|
+
if [ "${FM_SEND_IDEMPOTENT:-0}" = 1 ]; then
|
|
913
|
+
INBOX_RECORD=$(fm_task_inbox_write_idempotent "$STATE" "$INBOX_TASK_ID" "$MESSAGE" \
|
|
914
|
+
"${FIRE_AND_FORGET_ID:+fire-and-forget}") || inbox_write_rc=$?
|
|
915
|
+
else
|
|
916
|
+
INBOX_RECORD=$(fm_task_inbox_write "$STATE" "$INBOX_TASK_ID" "$MESSAGE" \
|
|
917
|
+
"${FIRE_AND_FORGET_ID:+fire-and-forget}") || inbox_write_rc=$?
|
|
918
|
+
fi
|
|
919
|
+
if [ "${inbox_write_rc:-0}" -ne 0 ]; then
|
|
920
|
+
fm_lock_release "$INBOX_META_LOCK"
|
|
921
|
+
if [ "$PENDING_REPLY_CREATED" = 1 ] && [ -n "$PENDING_REPLY_CORR" ]; then
|
|
922
|
+
fm_pending_reply_discard_undelivered "$STATE" "$PENDING_REPLY_CORR" || true
|
|
923
|
+
fi
|
|
924
|
+
echo "error: steer not sent to $INBOX_TASK_ID: its inbox record could not be written under $STATE/$INBOX_TASK_ID.inbox" >&2
|
|
925
|
+
exit 1
|
|
926
|
+
fi
|
|
927
|
+
fm_lock_release "$INBOX_META_LOCK"
|
|
928
|
+
# Enqueue IS durable delivery to the task's record: mark the pending
|
|
929
|
+
# expectation delivered now, without resolving it - only a correlated
|
|
930
|
+
# parent report acknowledges the request.
|
|
931
|
+
if [ -n "$PENDING_REPLY_CORR" ]; then
|
|
932
|
+
if fm_pending_reply_confirm_delivery "$STATE" "$PENDING_REPLY_CORR"; then
|
|
933
|
+
:
|
|
934
|
+
else
|
|
935
|
+
delivery_commit_status=$?
|
|
936
|
+
if [ "$delivery_commit_status" = 2 ]; then
|
|
937
|
+
echo "notice: the steer was recorded at $INBOX_RECORD, but its pending-reply delivery commit failed; a durable recovery marker was stored and the watcher will reconcile it. Do not resend." >&2
|
|
938
|
+
else
|
|
939
|
+
# Both the commit and its recovery marker failed. The durable inbox
|
|
940
|
+
# record is what delivers the steer, so the send still SUCCEEDED:
|
|
941
|
+
# a nonzero here would read as undelivered to every automated caller
|
|
942
|
+
# and invite a duplicate enqueue - the exact defect this plane
|
|
943
|
+
# removes. Surface the degradation as its own distinct,
|
|
944
|
+
# non-resend-inviting condition instead: reply tracking for this
|
|
945
|
+
# request may not resolve or escalate on its own until an operator
|
|
946
|
+
# inspects it.
|
|
947
|
+
echo "warning: reply-tracking-degraded (steer delivered, do not resend): the steer was durably recorded at $INBOX_RECORD, but its pending-reply delivery commit and recovery marker both failed, so the reply expectation for this request may not reconcile on its own. Inspect $STATE." >&2
|
|
948
|
+
fi
|
|
949
|
+
fi
|
|
950
|
+
fi
|
|
951
|
+
# The answer is durably sent: close each answered decision at enqueue time
|
|
952
|
+
# (answerer-closes; see the header contract).
|
|
953
|
+
if [ -n "$RESOLVE_KEYS" ]; then
|
|
954
|
+
fm_send_close_resolved_keys "$RESOLVE_ANSWER_TEXT" || exit 1
|
|
955
|
+
fm_send_feed_resolved_holds "$RESOLVE_ANSWER_TEXT" || exit 1
|
|
956
|
+
fi
|
|
957
|
+
# Ring the doorbell, best-effort: no ring outcome changes the exit status,
|
|
958
|
+
# because the watcher's re-ring ladder owns loss detection from here.
|
|
959
|
+
ring_rc=0
|
|
960
|
+
fm_task_inbox_ring "$TARGET_BACKEND" "$T" "$INBOX_RECORD" "$EXPECTED_LABEL" || ring_rc=$?
|
|
961
|
+
case "$ring_rc" in
|
|
962
|
+
1) echo "fm-send: doorbell skipped (composer visibly holds pending text); the steer is durably recorded at $INBOX_RECORD and the watcher will re-ring" >&2 ;;
|
|
963
|
+
2) echo "fm-send: doorbell did not reach $T; the steer is durably recorded at $INBOX_RECORD and the watcher will re-ring" >&2 ;;
|
|
964
|
+
esac
|
|
965
|
+
exit 0
|
|
966
|
+
fi
|
|
967
|
+
# Slash commands open a completion popup in some TUIs (verified on codex);
|
|
968
|
+
# submitting too fast selects nothing, so give the popup time to settle before
|
|
969
|
+
# the (retried) Enter. Codex opens the same kind of popup for a `$<skill>`
|
|
970
|
+
# invocation, so a `$...` message to a codex target gets the same settle. That
|
|
971
|
+
# `$` case is scoped to codex on purpose: unlike `/`, a leading `$` commonly
|
|
972
|
+
# starts ordinary text ("$5/month", "$HOME"), so a universal `$` rule would
|
|
973
|
+
# needlessly slow plain text to claude/opencode/pi. The target backend's
|
|
974
|
+
# verified submit retry still backs the settle up either way.
|
|
975
|
+
case "$*" in
|
|
976
|
+
/*) settle=1.2 ;;
|
|
977
|
+
\$*)
|
|
978
|
+
if [ "$TARGET_HARNESS" = codex ]; then settle=1.2; else settle=0.3; fi
|
|
979
|
+
;;
|
|
980
|
+
*) settle=0.3 ;;
|
|
981
|
+
esac
|
|
982
|
+
retries=${FM_SEND_RETRIES:-3}
|
|
983
|
+
sleep_s=${FM_SEND_SLEEP:-0.4}
|
|
984
|
+
# Type once, submit, verify. Only exact empty confirms delivery; every other
|
|
985
|
+
# verdict preserves the loud refusal boundary. Only LOCAL targets reach this
|
|
986
|
+
# block: remote text rides the inbox leg above, and remote --key exits
|
|
987
|
+
# earlier.
|
|
988
|
+
send_rc=0
|
|
989
|
+
if verdict=$(fm_backend_send_text_submit "$TARGET_BACKEND" "$T" "$MESSAGE" "$retries" "$sleep_s" "$settle" "$EXPECTED_LABEL"); then
|
|
990
|
+
:
|
|
991
|
+
else
|
|
992
|
+
send_rc=$?
|
|
993
|
+
fi
|
|
994
|
+
if [ "$send_rc" -ne 0 ]; then
|
|
995
|
+
fm_send_known_undelivered_cleanup || \
|
|
996
|
+
echo "error: known-undelivered pending-reply state could not be reset for $TARGET_TASK_ID" >&2
|
|
997
|
+
echo "error: text not sent to $T ($TARGET_BACKEND send failed; tried $RESOLUTION_TRIED)" >&2
|
|
998
|
+
exit 1
|
|
999
|
+
fi
|
|
1000
|
+
case "$verdict" in
|
|
1001
|
+
empty)
|
|
1002
|
+
;;
|
|
1003
|
+
send-failed)
|
|
1004
|
+
fm_send_known_undelivered_cleanup || \
|
|
1005
|
+
echo "error: known-undelivered pending-reply state could not be reset for $TARGET_TASK_ID" >&2
|
|
1006
|
+
echo "error: text not sent to $T ($TARGET_BACKEND send failed; tried $RESOLUTION_TRIED)" >&2
|
|
1007
|
+
exit 1
|
|
1008
|
+
;;
|
|
1009
|
+
pending)
|
|
1010
|
+
# The text was typed into the live target and Enter was sent; only the
|
|
1011
|
+
# submit read-back stayed unconfirmed (e.g. a busy harness queues the
|
|
1012
|
+
# steer and keeps rendering it). That is not a proven failure, so never
|
|
1013
|
+
# re-type the message: verify the pane instead. Exit 3 is the documented
|
|
1014
|
+
# delivered-unconfirmed status.
|
|
1015
|
+
# The pending-reply expectation is deliberately NOT discarded here:
|
|
1016
|
+
# dropping it would silently stop tracking a marked request that very
|
|
1017
|
+
# likely landed. It stays armed on its unconfirmed-delivery marker, so a
|
|
1018
|
+
# correlated report still resolves it and an unanswered one still
|
|
1019
|
+
# surfaces through the library's own reconciliation
|
|
1020
|
+
# (bin/fm-pending-reply-lib.sh).
|
|
1021
|
+
echo "fm-send: text delivered to $T but submission is unconfirmed (verdict=pending; tried $RESOLUTION_TRIED); do not retype or blindly resend - verify with fm-peek.sh, then re-send '--key Enter' only if the composer still holds the text" >&2
|
|
1022
|
+
exit 3
|
|
1023
|
+
;;
|
|
1024
|
+
*)
|
|
1025
|
+
if [ "$PENDING_REPLY_CREATED" = 1 ] && [ -n "$PENDING_REPLY_CORR" ]; then
|
|
1026
|
+
fm_pending_reply_discard_undelivered "$STATE" "$PENDING_REPLY_CORR" || true
|
|
1027
|
+
fi
|
|
1028
|
+
echo "error: text not submitted to $T (delivery unconfirmed; verdict=${verdict:-unknown}; tried $RESOLUTION_TRIED)" >&2
|
|
1029
|
+
exit 1
|
|
1030
|
+
;;
|
|
1031
|
+
esac
|
|
1032
|
+
# Delivery confirmed. Mark the pending expectation delivered without resolving
|
|
1033
|
+
# it: only a correlated parent report acknowledges the request.
|
|
1034
|
+
if [ -n "$PENDING_REPLY_CORR" ]; then
|
|
1035
|
+
if fm_pending_reply_confirm_delivery "$STATE" "$PENDING_REPLY_CORR"; then
|
|
1036
|
+
:
|
|
1037
|
+
else
|
|
1038
|
+
delivery_commit_status=$?
|
|
1039
|
+
if [ "$delivery_commit_status" = 2 ]; then
|
|
1040
|
+
echo "error: text was delivered to $T, but its pending-reply delivery commit failed; a durable recovery marker was stored and the watcher will reconcile it. Do not resend." >&2
|
|
1041
|
+
else
|
|
1042
|
+
echo "error: text was delivered to $T, but its pending-reply delivery commit and recovery marker both failed. Do not resend; inspect $STATE manually." >&2
|
|
1043
|
+
fi
|
|
1044
|
+
exit 1
|
|
1045
|
+
fi
|
|
1046
|
+
fi
|
|
1047
|
+
# Delivery is fully confirmed: close each answered decision in this home's
|
|
1048
|
+
# ledger (answerer-closes; see the header contract).
|
|
1049
|
+
if [ -n "$RESOLVE_KEYS" ]; then
|
|
1050
|
+
fm_send_close_resolved_keys "$RESOLVE_ANSWER_TEXT" || exit 1
|
|
1051
|
+
fm_send_feed_resolved_holds "$RESOLVE_ANSWER_TEXT" || exit 1
|
|
1052
|
+
fi
|
|
1053
|
+
# Submit landed with exact empty. Confirmation only proves the text was
|
|
1054
|
+
# accepted; the harness still needs a beat to spin up the
|
|
1055
|
+
# turn before its busy footer shows. Pause so an immediate peek catches the
|
|
1056
|
+
# crewmate actually working instead of the stale idle pane. FM_SEND_SETTLE=0
|
|
1057
|
+
# disables it. Scoped to this path only, never the shared submit core.
|
|
1058
|
+
[ "${FM_SEND_SETTLE:-1}" = 0 ] || sleep "${FM_SEND_SETTLE:-1}"
|
|
1059
|
+
fi
|