feat(agents): message agents with or without Nodeterm - #765
Conversation
Deliver nonce-framed messages only to verified idle agent panes using tmux named buffers and post-write identity checks.\n\nConstraint: Treat paste submission as unverified receipt.\nTested: npm test; npm run lint; npm run package:check; live two-Codex tmux smoke test.
NagyVikt
left a comment
There was a problem hiding this comment.
🛡️ GitGuardex code-assist
4 finding(s) — 🟠 1 high · 🟡 3 medium
⛔ Merge gate: blocked — 1 blocking finding(s) (blocks on high/critical).
| Severity | Location | Finding |
|---|---|---|
| 🟠 high | src/agents/message.js:257 |
The claimed source identity is selected solely from caller-controlled --from-session input without proving that the caller owns that |
| 🟡 medium | src/agents/message.js:23-25 |
The message sanitizer leaves terminal control bytes such as Ctrl-C, Ctrl-D, backspace, and C1 controls intact. |
| 🟡 medium | src/agents/message.js:180 |
The delivery appends the envelope to any text already present in the target agent's composer. |
| 🟡 medium | src/agents/message.js:226 |
Source-session discovery only recognizes the exact worktree root as the current directory. |
Provider codex · commit 5018352
- HIGH src/agents/message.js:257 The claimed source identity is selected solely from caller-controlled `--from-session` input without proving that the caller owns that session. Any lane able to run this command can impersonate another active session in an envelope that instructs the recipient to trust the outer frame; fixing this requires authenticated per-session credentials or host-verified caller provenance rather than an ID lookup alone.
NagyVikt
left a comment
There was a problem hiding this comment.
🛡️ GitGuardex code-assist
3 finding(s) — 🟠 1 high · 🟡 2 medium
⛔ Merge gate: blocked — 1 blocking finding(s) (blocks on high/critical).
| Severity | Location | Finding |
|---|---|---|
| 🟠 high | src/agents/message.js:180 |
Delivery appends the envelope to any text already present in the target agent's composer. |
| 🟡 medium | src/agents/message.js:24-25 |
The message sanitizer leaves terminal control bytes other than NUL and ESC intact. |
| 🟡 medium | src/agents/message.js:227 |
Automatic source discovery only works when the process is launched from the exact worktree root. |
Provider codex · commit 91b5a8c
Clear stale composer drafts, strip PTY control bytes, accept worktree subdirectories, and authenticate the claimed sender via the live process tree.\n\nTested: focused message and CLI tests; lint; package check; flaky ownership test 3/3 isolated passes.
Document that explicit source identities are accepted only from the live source agent process.
NagyVikt
left a comment
There was a problem hiding this comment.
🛡️ GitGuardex code-assist
1 finding(s) — 🟡 1 medium
✅ Merge gate: pass — no blocking findings (blocks on high/critical).
| Severity | Location | Finding |
|---|---|---|
| 🟡 medium | src/agents/message.js:304-310 |
The idle-state check is subject to a race, so concurrent senders can both accept the same done snapshot and submit messages after the |
Provider codex · commit 508f86e
NagyVikt
left a comment
There was a problem hiding this comment.
🛡️ GitGuardex code-assist
3 finding(s) — 🟠 2 high · 🟡 1 medium
⛔ Merge gate: blocked — 2 blocking finding(s) (blocks on high/critical).
| Severity | Location | Finding |
|---|---|---|
| 🟠 high | src/agents/message.js:183 |
The delivery path unconditionally sends Ctrl-U into the target agent, which can erase an idle agent's unsent draft or fail to clear a |
| 🟠 high | src/agents/message.js:305 |
The idle-state check is an unlocked snapshot, so multiple senders or a target activity transition can pass it concurrently. |
| 🟡 medium | src/agents/message.js:217-219 |
Branch lookup returns the first historical session with that branch, even when it is stale or when the branch has been reused. |
Provider codex · commit 508f86e
- HIGH src/agents/message.js:183 The delivery path unconditionally sends Ctrl-U into the target agent, which can erase an idle agent's unsent draft or fail to clear a multiline composer. The subsequent paste and Enter can therefore destroy user input or submit the envelope combined with stale instructions; fixing this requires backend-specific composer-state verification and clearing rather than a blind keypress. - HIGH src/agents/message.js:305 The idle-state check is an unlocked snapshot, so multiple senders or a target activity transition can pass it concurrently. A later sender can clear or overwrite a message already being composed or processed; delivery needs a per-session lock and an atomic activity recheck immediately before writing.
NagyVikt
left a comment
There was a problem hiding this comment.
🛡️ GitGuardex code-assist
1 finding(s) — 🟡 1 medium
✅ Merge gate: pass — no blocking findings (blocks on high/critical).
| Severity | Location | Finding |
|---|---|---|
| 🟡 medium | src/agents/message.js:250-262 |
The delivery has a TOCTOU window that can concatenate and submit a newly typed draft. |
Provider codex · commit daf5b5c
Tested: node --test test/agents-message.test.js test/cli-args-dispatch.test.js
NagyVikt
left a comment
There was a problem hiding this comment.
🛡️ GitGuardex code-assist
1 finding(s) — 🟡 1 medium
✅ Merge gate: pass — no blocking findings (blocks on high/critical).
| Severity | Location | Finding |
|---|---|---|
| 🟡 medium | src/agents/message.js:386 |
The implementation always requires the CLI process to descend from the source agent, so documented human-initiated sends from a registered |
Provider codex · commit 6e1c66a
Constraint: Preserve process-ancestry authentication for every send.
NagyVikt
left a comment
There was a problem hiding this comment.
🛡️ GitGuardex code-assist
1 finding(s) — 🟡 1 medium
✅ Merge gate: pass — no blocking findings (blocks on high/critical).
| Severity | Location | Finding |
|---|---|---|
| 🟡 medium | src/cli/args.js:461 |
A missing message value can be mistaken for the next option and sent to the agent. |
Provider codex · commit 535e1a7
Tested: node --test test/cli-args-dispatch.test.js test/agents-message.test.js
NagyVikt
left a comment
There was a problem hiding this comment.
🛡️ GitGuardex code-assist
3 finding(s) — 🟡 2 medium · 🔵 1 low
✅ Merge gate: pass — no blocking findings (blocks on high/critical).
| Severity | Location | Finding |
|---|---|---|
| 🟡 medium | src/agents/message.js:218-220 |
A malformed or empty lock file permanently blocks delivery. |
| 🟡 medium | src/agents/message.js:460 |
The message can be appended to and submit a draft typed after the composer check. |
| 🔵 low | src/cli/args.js:461-463 |
Messages beginning with a hyphen are incorrectly rejected as missing text. |
Provider codex · commit 052cb3d
Also recheck the target composer immediately before paste.\n\nTested: node --test test/agents-message.test.js test/cli-args-dispatch.test.js; npm run lint; npm run package:check
Preserve guarded live delivery while adding a shared durable inbox for registered agents outside the Nodeterm runtime. Constraint: Authenticate source and inbox ownership before every local write or read. Tested: targeted 52 tests; full suite 1127 passed, 1 unrelated flaky test passed in isolation; lint; changed formatting; package check.
Reject ambiguous active branch matches and ignore historical session records. Tested: node --test test/agents-message.test.js; npm run format:check:changed; npm run lint.
Summary
gx agentsand the local MCP serverTest plan
node --test test/agents-message.test.js test/agents-sessions.test.js test/cli-args-dispatch.test.js test/mcp-server.test.js(52 passed)npm run lintnpm run format:check:changednpm run package:check