@zerwiz/ymir 0.1.4 → 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 -59
- 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,385 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Post firstmate's composed answer back to the relay for a pending X-mode mention.
|
|
3
|
+
#
|
|
4
|
+
# Usage: fm-x-reply.sh <request_id> [--image <path>] <text>
|
|
5
|
+
# fm-x-reply.sh <request_id> [--image <path>] --text-file <path>
|
|
6
|
+
# fm-x-reply.sh <request_id> [--image <path>] -
|
|
7
|
+
# fm-x-reply.sh <request_id> --followup [--image <path>] ...
|
|
8
|
+
# fm-x-reply.sh <request_id> ... --receipt-file <path>
|
|
9
|
+
#
|
|
10
|
+
# --receipt-file <path> writes {request_id, endpoint, chunks, dry_run} to <path>
|
|
11
|
+
# after the reply lands, so a caller that must record HOW MANY messages were
|
|
12
|
+
# posted (bin/fm-public-followup.sh, building a typed delivery receipt) does not
|
|
13
|
+
# have to re-derive the split. Omitted by default and never written on failure,
|
|
14
|
+
# so stdout, exit codes, and every existing caller stay unchanged.
|
|
15
|
+
#
|
|
16
|
+
# The --text-file / stdin forms exist so a caller never has to inline reply text
|
|
17
|
+
# (which may be influenced by a public mention) into a shell command, where shell
|
|
18
|
+
# expansion or quote-breakage could bite. fmx-respond uses them; the positional
|
|
19
|
+
# <text> form is kept for back-compat and tests.
|
|
20
|
+
#
|
|
21
|
+
# Optional --image <path> attaches one local image file to the answer or followup
|
|
22
|
+
# POST body as {media_type,data_base64}. Supported extension mapping includes
|
|
23
|
+
# PNG, JPEG, GIF, WebP, BMP, and TIFF. If long text becomes a thread, the relay
|
|
24
|
+
# attaches that image to the first/opener message only.
|
|
25
|
+
#
|
|
26
|
+
# Two endpoints, one client. By default the reply is the single answer to a
|
|
27
|
+
# mention, POSTed to $RELAY/connector/answer. With --followup it is instead one
|
|
28
|
+
# of up to three later "here's where things stand" replies for a mention that
|
|
29
|
+
# spawned real work, POSTed to $RELAY/connector/followup; the relay retains the
|
|
30
|
+
# request binding for a 7-day window after the initial answer and accepts up to
|
|
31
|
+
# three thread-bound follow-ups against it. --followup may appear
|
|
32
|
+
# anywhere after the request_id; everything else (thread-split, payload shape,
|
|
33
|
+
# dry-run, never-inline safety) is identical, so only the endpoint and the
|
|
34
|
+
# dry-run marker differ.
|
|
35
|
+
#
|
|
36
|
+
# POSTs to $RELAY/connector/<answer|followup> with the bearer token. The relay
|
|
37
|
+
# binds the reply to the exact post it recorded for that request_id, so this
|
|
38
|
+
# client only ever echoes the relay-issued request_id and NEVER names a platform
|
|
39
|
+
# message id.
|
|
40
|
+
# On success it echoes ONLY that request_id; on a non-2xx (or transport failure)
|
|
41
|
+
# it exits non-zero so the caller knows the post did not land. The confirmed
|
|
42
|
+
# relay contract for an exhausted follow-up binding is HTTP 409 from
|
|
43
|
+
# /connector/followup, optionally with {"error":"followup_unavailable"} in the
|
|
44
|
+
# response body. This client always maps a follow-up 409 to exit code 9 so
|
|
45
|
+
# fm-x-followup.sh can tell "exhausted binding" apart from a transient post
|
|
46
|
+
# failure worth retrying; the body marker only sharpens the diagnostic. That
|
|
47
|
+
# relay-side 409 is secondary: after the relay's own cleanup sweep, a very-late
|
|
48
|
+
# call can instead see a benign no-op 200, so fm-x-followup.sh's local
|
|
49
|
+
# window/cap pruning remains the primary guard.
|
|
50
|
+
#
|
|
51
|
+
# Reply platform + split budget are resolved per axis: an explicit
|
|
52
|
+
# FMX_REPLY_PLATFORM / FMX_REPLY_MAX_CHARS env override wins (fm-x-followup passes
|
|
53
|
+
# recorded task-link context this way); otherwise resolution runs the durable
|
|
54
|
+
# per-request context registry -> the still-present inbox payload -> an
|
|
55
|
+
# authoritative relay lookup by request_id (fm-x-lib.sh:fmx_resolve_reply_context).
|
|
56
|
+
# The relay step is confined to a live follow-up so the answer path and every
|
|
57
|
+
# dry-run stay network-free. This is what keeps a delayed request-id follow-up on
|
|
58
|
+
# the ORIGINAL platform's budget even after the inbox is drained and with no task
|
|
59
|
+
# link surviving. FAIL-SAFE: if a --followup reply's platform/budget cannot be
|
|
60
|
+
# authoritatively resolved, this REFUSES with exit 8 (distinct from the 409 exit
|
|
61
|
+
# 9) rather than posting with a locally defaulted budget - firstmate holds and
|
|
62
|
+
# retries it.
|
|
63
|
+
#
|
|
64
|
+
# Long replies auto-split into a numbered thread. X stays within
|
|
65
|
+
# FMX_X_REPLY_MAX_CHARS, default 280. Discord uses
|
|
66
|
+
# FMX_DISCORD_REPLY_MAX_CHARS, default 1900, safely below Discord's 2000
|
|
67
|
+
# character message limit. A reply that fits in one message sends
|
|
68
|
+
# {request_id, text}; a thread sends {request_id, text, texts:[chunk,...]} where
|
|
69
|
+
# `texts` is the ordered "(k/n)" chunks for the relay to post as chained replies,
|
|
70
|
+
# and `text` is the first chunk so a relay that only reads `text` still posts the
|
|
71
|
+
# opener. If --image is present, the relay attaches it to this opener. At most
|
|
72
|
+
# FMX_X_THREAD_MAX messages (default 25) are produced.
|
|
73
|
+
#
|
|
74
|
+
# Live post config (home .env, FMX_ENV_FILE, or env): FMX_PAIRING_TOKEN
|
|
75
|
+
# (required), FMX_RELAY_URL (default https://myfirstmate.io). Auth:
|
|
76
|
+
# Authorization: Bearer <token>.
|
|
77
|
+
#
|
|
78
|
+
# Preview / dry-run: with FMX_DRY_RUN set (truthy), the reply is NOT posted.
|
|
79
|
+
# Instead the would-be POST body ({request_id, text}, or {request_id, text,
|
|
80
|
+
# texts} for a thread) is recorded to state/x-outbox/<request_id>.json and a "DRY
|
|
81
|
+
# RUN" summary is printed to stderr; stdout still echoes the request_id and the
|
|
82
|
+
# exit is 0, so the loop runs end to end without a public post. A follow-up
|
|
83
|
+
# dry-run additionally carries an "endpoint":"followup" marker in the recorded
|
|
84
|
+
# body so a preview is self-describing; the live POST body is unchanged. With
|
|
85
|
+
# --image, the dry-run record replaces image bytes with a compact image marker
|
|
86
|
+
# {media_type,bytes,source_path}, not the base64 bytes. Dry-run needs neither a
|
|
87
|
+
# token nor the relay.
|
|
88
|
+
set -u
|
|
89
|
+
|
|
90
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
91
|
+
FM_ROOT="${FM_ROOT_OVERRIDE:-$(cd "$SCRIPT_DIR/.." && pwd)}"
|
|
92
|
+
FM_HOME="${FM_HOME:-${FM_ROOT_OVERRIDE:-$FM_ROOT}}"
|
|
93
|
+
STATE="${FM_STATE_OVERRIDE:-$FM_HOME/state}"
|
|
94
|
+
# shellcheck source=bin/fm-x-lib.sh
|
|
95
|
+
. "$SCRIPT_DIR/fm-x-lib.sh"
|
|
96
|
+
|
|
97
|
+
TMP_FILES=()
|
|
98
|
+
cleanup_tmp_files() {
|
|
99
|
+
if [ "${#TMP_FILES[@]}" -gt 0 ]; then
|
|
100
|
+
rm -f "${TMP_FILES[@]}"
|
|
101
|
+
fi
|
|
102
|
+
}
|
|
103
|
+
trap cleanup_tmp_files EXIT
|
|
104
|
+
|
|
105
|
+
reply_make_tmp_file() {
|
|
106
|
+
local var_name=$1 file
|
|
107
|
+
file=$(mktemp "${TMPDIR:-/tmp}/fm-x-reply.XXXXXX") || return 1
|
|
108
|
+
TMP_FILES+=("$file")
|
|
109
|
+
printf -v "$var_name" '%s' "$file"
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
# write_reply_receipt <chunks> <dry-run-0|1>: record what this reply actually
|
|
113
|
+
# sent, for a caller that has to build a typed delivery receipt. Only ever called
|
|
114
|
+
# on success. A write failure is reported but never changes the exit status: the
|
|
115
|
+
# reply already landed, and claiming otherwise would invite a duplicate post.
|
|
116
|
+
write_reply_receipt() {
|
|
117
|
+
[ -n "$RECEIPT_FILE" ] || return 0
|
|
118
|
+
if ! (umask 077; jq -n --arg r "$REQ" --arg e "$ENDPOINT" --argjson c "$1" --argjson d "$2" \
|
|
119
|
+
'{request_id:$r, endpoint:$e, chunks:$c, dry_run:($d == 1)}' > "$RECEIPT_FILE"); then
|
|
120
|
+
echo "fm-x-reply: warning: posted but could not write the receipt to $RECEIPT_FILE" >&2
|
|
121
|
+
fi
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
usage() {
|
|
125
|
+
echo "usage: fm-x-reply.sh <request_id> [--followup] [--image <path>] [--receipt-file <path>] <text> | ... --text-file <path> | ... -" >&2
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
help() {
|
|
129
|
+
cat <<'EOF'
|
|
130
|
+
usage: fm-x-reply.sh <request_id> [--followup] [--image <path>] [--receipt-file <path>] <text>
|
|
131
|
+
fm-x-reply.sh <request_id> [--followup] [--image <path>] [--receipt-file <path>] --text-file <path>
|
|
132
|
+
fm-x-reply.sh <request_id> [--followup] [--image <path>] [--receipt-file <path>] -
|
|
133
|
+
|
|
134
|
+
Post a public-safe X-mode answer to the relay, or a completion follow-up with --followup.
|
|
135
|
+
|
|
136
|
+
Options:
|
|
137
|
+
--followup POST to /connector/followup instead of /connector/answer.
|
|
138
|
+
--image <path> Attach one local image file; threaded replies attach it to the opener tweet or message.
|
|
139
|
+
--receipt-file <path>
|
|
140
|
+
After a successful reply, write {request_id, endpoint, chunks, dry_run} to <path>.
|
|
141
|
+
--text-file <path>
|
|
142
|
+
Read reply text from a file instead of the command line.
|
|
143
|
+
- Read reply text from stdin.
|
|
144
|
+
--help Show this help.
|
|
145
|
+
EOF
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
case "${1:-}" in
|
|
149
|
+
--help|-h) help; exit 0 ;;
|
|
150
|
+
esac
|
|
151
|
+
|
|
152
|
+
REQ=${1:-}
|
|
153
|
+
if [ -z "$REQ" ]; then
|
|
154
|
+
usage
|
|
155
|
+
exit 2
|
|
156
|
+
fi
|
|
157
|
+
shift
|
|
158
|
+
|
|
159
|
+
# --followup selects the relay's /connector/followup endpoint instead of
|
|
160
|
+
# /connector/answer; it may appear anywhere after the request_id, so strip it out
|
|
161
|
+
# along with --image and process the remaining args (the text source) exactly as
|
|
162
|
+
# the answer path always has.
|
|
163
|
+
FOLLOWUP=0
|
|
164
|
+
IMAGE_PATH=
|
|
165
|
+
RECEIPT_FILE=
|
|
166
|
+
ARGS=()
|
|
167
|
+
while [ "$#" -gt 0 ]; do
|
|
168
|
+
case "$1" in
|
|
169
|
+
--followup) FOLLOWUP=1 ;;
|
|
170
|
+
--image)
|
|
171
|
+
shift
|
|
172
|
+
if [ "$#" -lt 1 ] || [ -z "$1" ]; then
|
|
173
|
+
echo "fm-x-reply: missing --image path" >&2
|
|
174
|
+
usage
|
|
175
|
+
exit 2
|
|
176
|
+
fi
|
|
177
|
+
IMAGE_PATH=$1
|
|
178
|
+
;;
|
|
179
|
+
--receipt-file)
|
|
180
|
+
shift
|
|
181
|
+
if [ "$#" -lt 1 ] || [ -z "$1" ]; then
|
|
182
|
+
echo "fm-x-reply: missing --receipt-file path" >&2
|
|
183
|
+
usage
|
|
184
|
+
exit 2
|
|
185
|
+
fi
|
|
186
|
+
RECEIPT_FILE=$1
|
|
187
|
+
;;
|
|
188
|
+
*) ARGS+=("$1") ;;
|
|
189
|
+
esac
|
|
190
|
+
shift
|
|
191
|
+
done
|
|
192
|
+
if [ "${#ARGS[@]}" -lt 1 ]; then
|
|
193
|
+
usage
|
|
194
|
+
exit 2
|
|
195
|
+
fi
|
|
196
|
+
set -- "${ARGS[@]}"
|
|
197
|
+
|
|
198
|
+
case "$1" in
|
|
199
|
+
--text-file)
|
|
200
|
+
if [ "$#" -lt 2 ]; then
|
|
201
|
+
echo "usage: fm-x-reply.sh <request_id> [--followup] [--image <path>] --text-file <path>" >&2
|
|
202
|
+
exit 2
|
|
203
|
+
fi
|
|
204
|
+
TEXT=$(cat -- "$2") || { echo "fm-x-reply: cannot read text file: $2" >&2; exit 1; }
|
|
205
|
+
;;
|
|
206
|
+
-)
|
|
207
|
+
TEXT=$(cat)
|
|
208
|
+
;;
|
|
209
|
+
*)
|
|
210
|
+
TEXT=$1
|
|
211
|
+
;;
|
|
212
|
+
esac
|
|
213
|
+
if [ -z "$TEXT" ]; then
|
|
214
|
+
echo "fm-x-reply: empty reply text" >&2
|
|
215
|
+
exit 2
|
|
216
|
+
fi
|
|
217
|
+
|
|
218
|
+
# The endpoint is the only behavioral difference between an answer and a
|
|
219
|
+
# follow-up; everything below (split, payload, dry-run, post) is shared.
|
|
220
|
+
if [ "$FOLLOWUP" = 1 ]; then
|
|
221
|
+
ENDPOINT=followup
|
|
222
|
+
else
|
|
223
|
+
ENDPOINT=answer
|
|
224
|
+
fi
|
|
225
|
+
|
|
226
|
+
fmx_load_config
|
|
227
|
+
|
|
228
|
+
# The request_id becomes a filename (inbox/outbox record), so never trust it into
|
|
229
|
+
# a path even though the relay issues it.
|
|
230
|
+
case "$REQ" in
|
|
231
|
+
''|.*|*[!A-Za-z0-9._-]*) echo "fm-x-reply: unsafe request_id: $REQ" >&2; exit 2 ;;
|
|
232
|
+
esac
|
|
233
|
+
|
|
234
|
+
command -v jq >/dev/null 2>&1 || { echo "fm-x-reply: jq not found" >&2; exit 1; }
|
|
235
|
+
|
|
236
|
+
# Resolve the reply platform + split budget. An explicit env override wins per
|
|
237
|
+
# axis (fm-x-followup passes recorded task-link context this way); otherwise
|
|
238
|
+
# resolve through the durable per-request context registry, then the still-present
|
|
239
|
+
# inbox payload, then - for a follow-up posted live by request_id after the inbox
|
|
240
|
+
# has been drained - an AUTHORITATIVE relay lookup. The relay step is confined to
|
|
241
|
+
# the follow-up path so the answer path and every dry-run stay network-free
|
|
242
|
+
# (fm-x-lib.sh owns the resolution-order contract).
|
|
243
|
+
ALLOW_RELAY=0
|
|
244
|
+
if [ -n "${FMX_REPLY_PLATFORM:-}" ] && [ -n "${FMX_REPLY_MAX_CHARS:-}" ]; then
|
|
245
|
+
REQ_PLATFORM=${FMX_REPLY_PLATFORM}
|
|
246
|
+
REQ_EXPLICIT_MAX=${FMX_REPLY_MAX_CHARS}
|
|
247
|
+
else
|
|
248
|
+
if [ "$FOLLOWUP" = 1 ] && [ -z "$FMX_DRY" ] && [ -n "$FMX_TOKEN" ]; then
|
|
249
|
+
ALLOW_RELAY=1
|
|
250
|
+
fi
|
|
251
|
+
REPLY_CONTEXT=$(fmx_resolve_reply_context "$STATE" "$REQ" "$ALLOW_RELAY") || {
|
|
252
|
+
echo "fm-x-reply: failed to resolve request platform context" >&2
|
|
253
|
+
exit 1
|
|
254
|
+
}
|
|
255
|
+
REQ_PLATFORM=${FMX_REPLY_PLATFORM:-$(printf '%s' "$REPLY_CONTEXT" | jq -r '.platform // ""')}
|
|
256
|
+
REQ_EXPLICIT_MAX=${FMX_REPLY_MAX_CHARS:-$(printf '%s' "$REPLY_CONTEXT" | jq -r '.reply_max_chars // ""')}
|
|
257
|
+
fi
|
|
258
|
+
case "$REQ_PLATFORM" in
|
|
259
|
+
discord|x|'') ;;
|
|
260
|
+
twitter) REQ_PLATFORM=x ;;
|
|
261
|
+
*) REQ_PLATFORM= ;;
|
|
262
|
+
esac
|
|
263
|
+
case "$REQ_EXPLICIT_MAX" in
|
|
264
|
+
''|*[!0-9]*) REQ_EXPLICIT_MAX= ;;
|
|
265
|
+
esac
|
|
266
|
+
# Was the platform/budget authoritatively resolved by any source (override,
|
|
267
|
+
# registry, inbox, or relay)? Drives the follow-up fail-safe below.
|
|
268
|
+
CONTEXT_RESOLVED=0
|
|
269
|
+
if [ -n "$REQ_PLATFORM" ] && [ -n "$REQ_EXPLICIT_MAX" ]; then
|
|
270
|
+
CONTEXT_RESOLVED=1
|
|
271
|
+
fi
|
|
272
|
+
|
|
273
|
+
if [ "$FOLLOWUP" = 1 ] && [ "$CONTEXT_RESOLVED" = 0 ]; then
|
|
274
|
+
relay_note=
|
|
275
|
+
[ "$ALLOW_RELAY" = 1 ] && relay_note=", and the relay did not supply the missing value by request_id"
|
|
276
|
+
printf 'fm-x-reply: refusing follow-up for %s: could not authoritatively determine both the reply platform and explicit budget (local per-request context was incomplete%s). Hold and retry once both values are recoverable.\n' \
|
|
277
|
+
"$REQ" "$relay_note" >&2
|
|
278
|
+
exit 8
|
|
279
|
+
fi
|
|
280
|
+
REPLY_MAX=$(fmx_reply_limit_for_platform "$REQ_PLATFORM" "$REQ_EXPLICIT_MAX")
|
|
281
|
+
|
|
282
|
+
IMAGE_PAYLOAD_FILE=
|
|
283
|
+
IMAGE_PREVIEW=
|
|
284
|
+
PAYLOAD_FILE=
|
|
285
|
+
RESPONSE_BODY_FILE=
|
|
286
|
+
if [ -n "$IMAGE_PATH" ]; then
|
|
287
|
+
reply_make_tmp_file IMAGE_PAYLOAD_FILE || {
|
|
288
|
+
echo "fm-x-reply: cannot create image payload temp file" >&2; exit 1; }
|
|
289
|
+
IMAGE_PREVIEW=$(fmx_image_payload_file "$IMAGE_PATH" fm-x-reply "$IMAGE_PAYLOAD_FILE") || exit 1
|
|
290
|
+
printf '%s' "$IMAGE_PREVIEW" | jq -e . >/dev/null 2>&1 || {
|
|
291
|
+
echo "fm-x-reply: failed to build image preview" >&2; exit 1; }
|
|
292
|
+
fi
|
|
293
|
+
|
|
294
|
+
# Auto-split a long reply into a numbered thread using the target platform's
|
|
295
|
+
# per-message budget. A reply that fits in one message stays single and
|
|
296
|
+
# unnumbered.
|
|
297
|
+
CHUNKS=$(printf '%s' "$TEXT" | fmx_split_thread "$REPLY_MAX" "$FMX_THREAD_MAX") || {
|
|
298
|
+
echo "fm-x-reply: failed to split reply into a thread" >&2
|
|
299
|
+
exit 1
|
|
300
|
+
}
|
|
301
|
+
N=$(printf '%s' "$CHUNKS" | jq 'length' 2>/dev/null) || N=
|
|
302
|
+
case "$N" in ''|*[!0-9]*) echo "fm-x-reply: failed to split reply into a thread" >&2; exit 1 ;; esac
|
|
303
|
+
[ "$N" -gt 0 ] || { echo "fm-x-reply: empty reply text" >&2; exit 2; }
|
|
304
|
+
|
|
305
|
+
# Build the body with jq so the text and optional image object are correctly
|
|
306
|
+
# JSON-escaped. A single message sends {request_id, text}; a thread also sends
|
|
307
|
+
# {texts: [...]} for the relay to post as chained replies. When image is present
|
|
308
|
+
# on a thread, the relay attaches it to the first chunk only.
|
|
309
|
+
reply_make_tmp_file PAYLOAD_FILE || {
|
|
310
|
+
echo "fm-x-reply: cannot create request payload temp file" >&2; exit 1; }
|
|
311
|
+
if [ -n "$IMAGE_PAYLOAD_FILE" ]; then
|
|
312
|
+
fmx_reply_payload_json "$REQ" "$CHUNKS" "$N" "$IMAGE_PAYLOAD_FILE" > "$PAYLOAD_FILE" || {
|
|
313
|
+
echo "fm-x-reply: failed to build request payload" >&2; exit 1; }
|
|
314
|
+
else
|
|
315
|
+
fmx_reply_payload_json "$REQ" "$CHUNKS" "$N" > "$PAYLOAD_FILE" || {
|
|
316
|
+
echo "fm-x-reply: failed to build request payload" >&2; exit 1; }
|
|
317
|
+
fi
|
|
318
|
+
|
|
319
|
+
# Preview / dry-run: surface what we WOULD post and stop, without auth or network.
|
|
320
|
+
if [ -n "$FMX_DRY" ]; then
|
|
321
|
+
outbox_dir="$STATE/x-outbox"
|
|
322
|
+
# The recorded body is the would-be POST body, except image bytes are replaced
|
|
323
|
+
# by a compact marker. A follow-up preview additionally carries an
|
|
324
|
+
# "endpoint":"followup" marker so an outbox record is self-describing.
|
|
325
|
+
OUTREC=$(fmx_reply_outbox_json "$REQ" "$CHUNKS" "$N" "$FOLLOWUP" "$IMAGE_PREVIEW") || {
|
|
326
|
+
echo "fm-x-reply: failed to build dry-run outbox record" >&2; exit 1; }
|
|
327
|
+
printf '%s\n' "$OUTREC" \
|
|
328
|
+
| fmx_private_artifact_publish_stdin "$outbox_dir" "$REQ.json" 600 || {
|
|
329
|
+
echo "fm-x-reply: cannot write dry-run outbox: $outbox_dir/$REQ.json" >&2
|
|
330
|
+
exit 1
|
|
331
|
+
}
|
|
332
|
+
if [ "$N" -le 1 ]; then
|
|
333
|
+
printf 'fm-x-reply: DRY RUN - would POST to %s/connector/%s (recorded: state/x-outbox/%s.json): %s\n' \
|
|
334
|
+
"$FMX_RELAY" "$ENDPOINT" "$REQ" "$(printf '%s' "$CHUNKS" | jq -r '.[0]')" >&2
|
|
335
|
+
else
|
|
336
|
+
printf 'fm-x-reply: DRY RUN - would POST a %s-tweet thread to %s/connector/%s (recorded: state/x-outbox/%s.json):\n' \
|
|
337
|
+
"$N" "$FMX_RELAY" "$ENDPOINT" "$REQ" >&2
|
|
338
|
+
printf '%s' "$CHUNKS" | jq -r '.[]' | while IFS= read -r __chunk; do printf ' %s\n' "$__chunk" >&2; done
|
|
339
|
+
fi
|
|
340
|
+
write_reply_receipt "$N" 1
|
|
341
|
+
printf '%s\n' "$REQ"
|
|
342
|
+
exit 0
|
|
343
|
+
fi
|
|
344
|
+
|
|
345
|
+
if [ -z "$FMX_TOKEN" ]; then
|
|
346
|
+
echo "fm-x-reply: X mode not configured (no FMX_PAIRING_TOKEN)" >&2
|
|
347
|
+
exit 1
|
|
348
|
+
fi
|
|
349
|
+
reply_make_tmp_file RESPONSE_BODY_FILE || {
|
|
350
|
+
echo "fm-x-reply: cannot create relay response temp file" >&2; exit 1; }
|
|
351
|
+
code=$(fmx_post_json "$ENDPOINT" "$PAYLOAD_FILE" "$RESPONSE_BODY_FILE")
|
|
352
|
+
post_rc=$?
|
|
353
|
+
case "$post_rc" in
|
|
354
|
+
0) : ;;
|
|
355
|
+
127) echo "fm-x-reply: curl not found" >&2; exit 1 ;;
|
|
356
|
+
3) echo "fm-x-reply: invalid FMX_PAIRING_TOKEN" >&2; exit 1 ;;
|
|
357
|
+
*) echo "fm-x-reply: request to relay failed" >&2; exit 1 ;;
|
|
358
|
+
esac
|
|
359
|
+
|
|
360
|
+
case "$code" in
|
|
361
|
+
2[0-9][0-9])
|
|
362
|
+
if [ "$FOLLOWUP" = 0 ]; then
|
|
363
|
+
fmx_context_registry_set "$STATE" "$REQ" "$REQ_PLATFORM" "$REQ_EXPLICIT_MAX" 1 2>/dev/null \
|
|
364
|
+
|| echo "fm-x-reply: warning: could not retain reply context for $REQ" >&2
|
|
365
|
+
fi
|
|
366
|
+
write_reply_receipt "$N" 0
|
|
367
|
+
printf '%s\n' "$REQ"
|
|
368
|
+
;;
|
|
369
|
+
409)
|
|
370
|
+
if [ "$FOLLOWUP" = 1 ]; then
|
|
371
|
+
if [ -s "$RESPONSE_BODY_FILE" ] && {
|
|
372
|
+
jq -e '.error == "followup_unavailable"' "$RESPONSE_BODY_FILE" >/dev/null 2>&1 ||
|
|
373
|
+
grep -F 'followup_unavailable' "$RESPONSE_BODY_FILE" >/dev/null 2>&1
|
|
374
|
+
}; then
|
|
375
|
+
echo "fm-x-reply: relay rejected the follow-up (confirmed followup_unavailable marker): HTTP 409" >&2
|
|
376
|
+
else
|
|
377
|
+
echo "fm-x-reply: relay rejected the follow-up (HTTP 409 cap/window exhaustion; marker absent)" >&2
|
|
378
|
+
fi
|
|
379
|
+
exit 9
|
|
380
|
+
fi
|
|
381
|
+
echo "fm-x-reply: relay returned HTTP $code" >&2
|
|
382
|
+
exit 1
|
|
383
|
+
;;
|
|
384
|
+
*) echo "fm-x-reply: relay returned HTTP $code" >&2; exit 1 ;;
|
|
385
|
+
esac
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
"""fm_voice_frame.py - the wire format between the voice client and the relay.
|
|
2
|
+
|
|
3
|
+
The client and the relay share one bidirectional byte stream: an SSH exec
|
|
4
|
+
channel, where the client's stdout is the relay's stdin and the relay's stdout
|
|
5
|
+
is the client's stdin. Audio and control therefore travel together and need
|
|
6
|
+
framing. A frame is a 1 byte kind, a 4 byte unsigned big-endian payload length,
|
|
7
|
+
then exactly that many payload bytes.
|
|
8
|
+
|
|
9
|
+
Kinds the client sends up to the relay:
|
|
10
|
+
S talk start, empty payload
|
|
11
|
+
A captured audio, 16000 Hz mono signed 16-bit little-endian
|
|
12
|
+
E talk end, empty payload
|
|
13
|
+
Q quit, empty payload
|
|
14
|
+
|
|
15
|
+
Kinds the relay sends down to the client:
|
|
16
|
+
A reply audio, 24000 Hz mono signed 16-bit little-endian
|
|
17
|
+
T JSON {"role": ..., "text": ...}, one transcript line
|
|
18
|
+
V JSON {"event": ..., ...}, a notice such as a queued request or a failed turn
|
|
19
|
+
M JSON {"mark": ..., "since_talk_end": ..., "tool_calls": ...}, one relay-side
|
|
20
|
+
timing mark. since_talk_end is seconds from the moment the captain stopped
|
|
21
|
+
talking, which is the instant every figure in this build is measured from.
|
|
22
|
+
The marks the relay sends are tool_use, first_audio, first_audio_wire,
|
|
23
|
+
tool_answered and reply_end; bin/fm-voice-relay.py owns what each means.
|
|
24
|
+
B bye, empty payload
|
|
25
|
+
|
|
26
|
+
Audio is raw PCM rather than base64 because base64 belongs to the Bedrock
|
|
27
|
+
event protocol, not to this hop, and the extra third of the bytes would sit
|
|
28
|
+
inside the latency this build exists to measure.
|
|
29
|
+
|
|
30
|
+
This module is the owner of the contract above and of the sample rates;
|
|
31
|
+
docs/voice-relay.md is the operator-facing guide and points here for the format.
|
|
32
|
+
This module is copied to the laptop beside fm-voice-client.py, so it imports
|
|
33
|
+
nothing outside the standard library.
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
import json
|
|
37
|
+
import struct
|
|
38
|
+
|
|
39
|
+
HEADER = struct.Struct(">cI")
|
|
40
|
+
|
|
41
|
+
# The relay writes this once before its first frame and the client discards
|
|
42
|
+
# everything ahead of it. `ssh host command` runs the command through the login
|
|
43
|
+
# shell, so a shell startup file that prints to stdout would otherwise land in
|
|
44
|
+
# front of the first frame and desynchronise the stream, which reads as a
|
|
45
|
+
# baffling protocol error rather than as the chatty shell it is.
|
|
46
|
+
MAGIC = b"FMVOICE1"
|
|
47
|
+
|
|
48
|
+
# One second of 24000 Hz 16-bit mono is 48000 bytes, so this ceiling is far
|
|
49
|
+
# above any real chunk while still rejecting a desynchronised stream early.
|
|
50
|
+
MAX_PAYLOAD = 1 << 20
|
|
51
|
+
|
|
52
|
+
TALK_START = b"S"
|
|
53
|
+
AUDIO = b"A"
|
|
54
|
+
TALK_END = b"E"
|
|
55
|
+
QUIT = b"Q"
|
|
56
|
+
TEXT = b"T"
|
|
57
|
+
NOTICE = b"V"
|
|
58
|
+
MARK = b"M"
|
|
59
|
+
BYE = b"B"
|
|
60
|
+
|
|
61
|
+
KINDS = (TALK_START, AUDIO, TALK_END, QUIT, TEXT, NOTICE, MARK, BYE)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class FrameError(Exception):
|
|
65
|
+
"""A frame could not be encoded or decoded."""
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def check_header(kind, length):
|
|
69
|
+
"""Raise FrameError unless a decoded header is one this format allows.
|
|
70
|
+
|
|
71
|
+
Both directions of the stream decode headers, and audio that happens to
|
|
72
|
+
look like one must be rejected identically wherever that happens, so the
|
|
73
|
+
rules live here rather than beside each decoder.
|
|
74
|
+
"""
|
|
75
|
+
if kind not in KINDS:
|
|
76
|
+
raise FrameError("unknown frame kind: {!r}".format(kind))
|
|
77
|
+
if length > MAX_PAYLOAD:
|
|
78
|
+
raise FrameError("payload of {} bytes exceeds the {} byte limit".format(
|
|
79
|
+
length, MAX_PAYLOAD))
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def encode(kind, payload=b""):
|
|
83
|
+
"""Return the wire bytes for one frame."""
|
|
84
|
+
check_header(kind, len(payload))
|
|
85
|
+
return HEADER.pack(kind, len(payload)) + payload
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def encode_json(kind, obj):
|
|
89
|
+
"""Return the wire bytes for one frame carrying a compact JSON payload."""
|
|
90
|
+
return encode(kind, json.dumps(obj, separators=(",", ":")).encode("utf-8"))
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def decode_json(payload):
|
|
94
|
+
"""Return the object in a JSON frame payload."""
|
|
95
|
+
try:
|
|
96
|
+
return json.loads(payload.decode("utf-8"))
|
|
97
|
+
except (UnicodeDecodeError, ValueError) as exc:
|
|
98
|
+
raise FrameError("payload is not JSON: {}".format(exc))
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
class Reader:
|
|
102
|
+
"""Read frames from a blocking binary stream.
|
|
103
|
+
|
|
104
|
+
read() returns a (kind, payload) pair, or None once the peer has closed
|
|
105
|
+
the stream cleanly between frames. A stream that ends part way through a
|
|
106
|
+
frame raises FrameError, because a truncated frame is a real fault and
|
|
107
|
+
silently treating it as end of input would hide a dropped connection.
|
|
108
|
+
"""
|
|
109
|
+
|
|
110
|
+
def __init__(self, stream):
|
|
111
|
+
self._stream = stream
|
|
112
|
+
|
|
113
|
+
def _exact(self, count, what):
|
|
114
|
+
"""Return exactly count bytes, or None if the stream ended before any.
|
|
115
|
+
|
|
116
|
+
Ending part way through raises rather than returning None, because the
|
|
117
|
+
two are not the same fault and only the caller reading a header can
|
|
118
|
+
treat nothing-at-all as end of input. A partial header returned as None
|
|
119
|
+
would be read as a clean close, and a dropped connection would be
|
|
120
|
+
recorded as a turn the model simply did not answer.
|
|
121
|
+
"""
|
|
122
|
+
parts = []
|
|
123
|
+
have = 0
|
|
124
|
+
while have < count:
|
|
125
|
+
chunk = self._stream.read(count - have)
|
|
126
|
+
if not chunk:
|
|
127
|
+
if have:
|
|
128
|
+
raise FrameError(
|
|
129
|
+
"stream ended after {} of the {} bytes of a {}".format(
|
|
130
|
+
have, count, what))
|
|
131
|
+
return None
|
|
132
|
+
parts.append(chunk)
|
|
133
|
+
have += len(chunk)
|
|
134
|
+
return b"".join(parts)
|
|
135
|
+
|
|
136
|
+
def read(self):
|
|
137
|
+
head = self._exact(HEADER.size, "frame header")
|
|
138
|
+
if head is None:
|
|
139
|
+
return None
|
|
140
|
+
kind, length = HEADER.unpack(head)
|
|
141
|
+
check_header(kind, length)
|
|
142
|
+
if length == 0:
|
|
143
|
+
return kind, b""
|
|
144
|
+
payload = self._exact(length, "payload")
|
|
145
|
+
if payload is None:
|
|
146
|
+
raise FrameError("stream ended inside a {} byte payload".format(length))
|
|
147
|
+
return kind, payload
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
class Writer:
|
|
151
|
+
"""Write frames to a blocking binary stream, flushing each one.
|
|
152
|
+
|
|
153
|
+
Every frame is flushed because a buffered reply frame is indistinguishable
|
|
154
|
+
from a slow model, and this build exists to measure the difference.
|
|
155
|
+
"""
|
|
156
|
+
|
|
157
|
+
def __init__(self, stream):
|
|
158
|
+
self._stream = stream
|
|
159
|
+
|
|
160
|
+
def send(self, kind, payload=b""):
|
|
161
|
+
self._stream.write(encode(kind, payload))
|
|
162
|
+
self._stream.flush()
|
|
163
|
+
|
|
164
|
+
def send_json(self, kind, obj):
|
|
165
|
+
self._stream.write(encode_json(kind, obj))
|
|
166
|
+
self._stream.flush()
|