diff options
Diffstat (limited to '.ai/scripts/cross-agent-comms/cross-agent-send.md')
| -rw-r--r-- | .ai/scripts/cross-agent-comms/cross-agent-send.md | 199 |
1 files changed, 0 insertions, 199 deletions
diff --git a/.ai/scripts/cross-agent-comms/cross-agent-send.md b/.ai/scripts/cross-agent-comms/cross-agent-send.md deleted file mode 100644 index 29bfb24..0000000 --- a/.ai/scripts/cross-agent-comms/cross-agent-send.md +++ /dev/null @@ -1,199 +0,0 @@ -# cross-agent-send - -**Purpose.** Send a cross-agent message file to a specific destination. Handles -peer-config lookup, GPG signing, atomic write (same-machine) or rsync push -(cross-machine), retry-with-backoff, and failure surfacing. - -This is the canonical writer. The protocol spec defers all writer mechanics to -this script. - -## Usage - -``` -cross-agent-send <destination> <message-file> [--no-sign] [--retries N] -``` - -### Positional arguments - -| Position | Meaning | Example | -|---|---|---| -| 1 | Destination as `<machine>.<project>` | `homelab.career`, `velox.career` | -| 2 | Message file (already-formatted `.org`) | `/tmp/my-message.org` | - -### Flags - -| Flag | Default | Purpose | -|---|---|---| -| `--no-sign` | (signing on) | Skip GPG signing. Use only for testing; receivers reject unsigned messages by default. | -| `--retries N` | 3 | Override retry count for cross-machine sends. | -| `--key <key-id>` | (user's primary key) | GPG key to sign with. Resolution order: `--key` flag, `GPG_USER` env, `git config user.signingkey`, then the first secret key in the keyring. | - -## Behavior - -### Filename generation (script-controlled) - -The script generates the canonical destination filename from the message's -frontmatter and sender context. The user's input filename is ignored — pass any -path, the script names the destination correctly: - -``` -<UTC-now>T<HHMMSS>Z-from-<sender-slug>-<short-conv-id>.org -``` - -`<sender-slug>` comes from the sender machine's project name (config or -hostname-based). `<short-conv-id>` is read from the message's -`#+CONVERSATION_ID` frontmatter field. UTC timestamp is generated at send time. - -The script also performs the **sender-side max-seen scan** before writing: it -reads the receiver's `from-agents/` directory, finds the highest existing -sequence in this conversation across both sender prefixes, and (best-effort) -suggests `max(seen) + 1` for the next sequence. The user/agent is responsible -for setting `#+SEQUENCE` in the message body; the script only advises. - -### Same-machine destinations - -Resolved when the destination's machine matches the current hostname (or is -not in `peers.toml` as a remote). Steps: - -1. Parse frontmatter; extract `CONVERSATION_ID` and `TIMESTAMP`. Validate per - the *Validation before send* section below. -2. Generate canonical filename per *Filename generation* above. -3. Sign: `gpg --detach-sign --armor --output <canonical>.asc --local-user <key> <input>`. -4. Compute target: read `peers.toml` for the project's `inbox_path`. If - missing, fall back to `~/projects/<project>/inbox/from-agents/`. -5. **Atomic write with strict ordering** (signature must precede message): - - Stage `.asc`: write to `<target>/.tmp.XXXXXX-<canonical>.asc`, - then `mv` to `<target>/<canonical>.asc`. - - **Then** stage `.org`: write to `<target>/.tmp.XXXXXX-<canonical>`, - then `mv` to `<target>/<canonical>`. - - Receivers only act on `.org` files; staging the `.asc` first guarantees - the signature is present when the receiver opens the message. Out-of-order - would race: receiver could read the `.org` before the `.asc` lands and - fail GPG verify even though the sender did everything right. -6. Exit 0 on success. Exit non-zero if any step fails. - -### Cross-machine destinations - -Steps: - -1. Parse + generate canonical filename, as same-machine steps 1-2. -2. Sign locally to `<input>.asc` (or a tmp staging file). -3. rsync push **with the same .asc-first ordering**: - - `rsync -a <input>.asc <ssh-user>@<host>:<inbox_path>/<canonical>.asc` - - **Then** `rsync -a <input> <ssh-user>@<host>:<inbox_path>/<canonical>` - rsync writes to a hidden temp file then renames atomically by default - (`--inplace` would defeat this; do not pass it). -4. Retry on failure: 5s, 30s, 120s backoff, then surface error. -5. On persistent failure: write a marker file to - `~/.local/state/cross-agent-comms/failed-sends/<timestamp>-<dest>-<canonical>.json` - containing the destination, message path, error, and retry log. Exit non-zero. - -### Validation before send - -- Destination resolves via `peers.toml` (or local fallback). If neither, exit - immediately with `destination not found in peers.toml; available: <list>`. -- Message file must be readable, non-empty, and have valid org-mode frontmatter - with **all** of the following required fields: - - `#+TITLE` - - `#+CONVERSATION_ID` - - `#+MESSAGE_TYPE` - - `#+SEQUENCE` - - `#+TIMESTAMP` - - `#+PROTOCOL_VERSION` (must equal `5` for v5) - - If any required field is missing or malformed, exit immediately with a parse - error naming the offending field. - -- Optional fields the script recognizes and passes through (no special - handling beyond preservation): - - `#+REQUIRES_TOOLS` — comma-separated tool/MCP slugs the receiver needs. - - `#+RELEASE_STATUS` — valid only on `MESSAGE_TYPE: release`. Values per - spec: `complete`, `cancelled`, `withdrawn-after-pushback`, - `abandoned-after-escalation`. - - `#+WORKFLOW_VERSION` — sender's version of the cross-agent-comms workflow - file. Currently advisory; receiver may warn on mismatch but does not block. - -## Configuration - -Reads `~/.config/cross-agent-comms/peers.toml` for peer routing: - -```toml -[peers.velox] -host = "velox.local" -ssh_user = "cjennings" - -# Optional: per-project inbox-path overrides for non-default layouts. -[projects.work] -inbox_path = "~/projects/work/inbox/from-agents" - -[projects.homelab] -inbox_path = "~/projects/homelab/inbox/from-agents" -``` - -If a project entry is omitted, defaults to `~/projects/<project>/inbox/from-agents`. - -## Failure modes - -| Symptom | Cause | Fix | -|---|---|---| -| `destination not found in peers.toml` | Misspelled destination, or peer not configured | Run `cross-agent-discover` to see available destinations. | -| `signing failed: no secret key` | GPG key missing or not in keyring | `gpg --list-secret-keys` to confirm. Override with `--key <id>`. | -| `signing failed: pinentry timed out` | Headless session, GUI pinentry unavailable | Confirm `pinentry-program` in `gpg-agent.conf` matches available pinentry. Per protocols.org, GUI pinentry works from Claude Code. | -| `rsync exit 255` | SSH unreachable | `cross-agent-discover --peer <name>` to confirm reachability. | -| `rsync exit 23` | Permission denied at destination | Check destination directory perms (`chmod 700`) and ownership. | -| Marker file written to `failed-sends/` | Persistent cross-machine failure | Inspect the marker's `error` field. After fixing, retry: `cross-agent-send <dest> <msg>` (the marker is for visibility; it does not auto-retry). | -| Receiver complains "unsigned message" | `--no-sign` was used in production | Don't use `--no-sign` outside testing. | - -## HALT awareness - -Checks `~/.config/cross-agent-comms/HALT` at the start of every send AND -between the `.asc` and `.org` rsync calls AND between each retry iteration. -On HALT exists, exits with code 5 ("halt active; remove -~/.config/cross-agent-comms/HALT to resume") without writing or pushing -further. - -Worst case: one in-flight send completes its current rsync step within a few -seconds before halt kicks in for the next step. New sends are blocked -immediately. No `pkill` needed — the per-iteration check stops things -naturally. - -If the HALT file exists but is unreadable (permissions wrong), fail-closed — -treat as if HALT is set. Safer than fail-open. - -See `cross-agent-halt.md` for the full halt mechanism. - -## Examples - -```bash -# Same-machine send -cross-agent-send homelab.career /tmp/my-message.org - -# Cross-machine send via Tailscale -cross-agent-send velox.career /tmp/my-message.org - -# Test send without signing (receiver will reject) -cross-agent-send homelab.career /tmp/test.org --no-sign - -# Override retry count for a flaky link -cross-agent-send velox.career /tmp/my-message.org --retries 10 - -# After a delivery failure, inspect the marker -cat ~/.local/state/cross-agent-comms/failed-sends/*.json | jq . -``` - -## Exit codes - -| Code | Meaning | -|---|---| -| 0 | Sent successfully. | -| 1 | General error (parse failure, signing failure, etc.). | -| 2 | Destination not found in peers.toml. | -| 3 | Cross-machine delivery failed after retries. Marker file written. | -| 4 | Frontmatter validation failed. | - -## See also - -- `cross-agent-discover` — validate destinations before sending. -- `cross-agent-watch` — receiver-side notification. -- `cross-agent-status` — see what's queued. -- `cross-agent-comms.org` — protocol spec, the "what" the script implements. |
