patchcord 0.5.74 → 0.5.76

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/bin/patchcord.mjs CHANGED
@@ -1018,12 +1018,25 @@ if (!cmd || cmd === "install" || cmd === "agent" || cmd?.startsWith("--")) {
1018
1018
  // Kimi CLI
1019
1019
  const hasKimi = run("which kimi");
1020
1020
  if (hasKimi) {
1021
- const kimiSkillDir = join(HOME, ".kimi", "skills", "patchcord");
1022
- const kimiWaitDir = join(HOME, ".kimi", "skills", "patchcord-wait");
1021
+ // Clean up old skill names (hyphen-style combined skill)
1022
+ const oldKimiSkillDir = join(HOME, ".kimi", "skills", "patchcord");
1023
+ const oldKimiWaitDir = join(HOME, ".kimi", "skills", "patchcord-wait");
1024
+ if (existsSync(oldKimiSkillDir)) {
1025
+ rmSync(oldKimiSkillDir, { recursive: true, force: true });
1026
+ }
1027
+ if (existsSync(oldKimiWaitDir)) {
1028
+ rmSync(oldKimiWaitDir, { recursive: true, force: true });
1029
+ }
1030
+
1031
+ // Install three focused Kimi skills
1032
+ const kimiInboxDir = join(HOME, ".kimi", "skills", "patchcord:inbox");
1033
+ const kimiWaitDir = join(HOME, ".kimi", "skills", "patchcord:wait");
1034
+ const kimiSubDir = join(HOME, ".kimi", "skills", "patchcord:subscribe");
1023
1035
  let kimiChanged = false;
1024
- if (!existsSync(kimiSkillDir)) {
1025
- mkdirSync(kimiSkillDir, { recursive: true });
1026
- cpSync(join(pluginRoot, "per-project-skills", "kimi", "SKILL.md"), join(kimiSkillDir, "SKILL.md"));
1036
+
1037
+ if (!existsSync(kimiInboxDir)) {
1038
+ mkdirSync(kimiInboxDir, { recursive: true });
1039
+ cpSync(join(pluginRoot, "per-project-skills", "kimi", "inbox", "SKILL.md"), join(kimiInboxDir, "SKILL.md"));
1027
1040
  kimiChanged = true;
1028
1041
  }
1029
1042
  if (!existsSync(kimiWaitDir)) {
@@ -1031,6 +1044,11 @@ if (!cmd || cmd === "install" || cmd === "agent" || cmd?.startsWith("--")) {
1031
1044
  cpSync(join(pluginRoot, "per-project-skills", "kimi", "wait", "SKILL.md"), join(kimiWaitDir, "SKILL.md"));
1032
1045
  kimiChanged = true;
1033
1046
  }
1047
+ if (!existsSync(kimiSubDir)) {
1048
+ mkdirSync(kimiSubDir, { recursive: true });
1049
+ cpSync(join(pluginRoot, "per-project-skills", "kimi", "subscribe", "SKILL.md"), join(kimiSubDir, "SKILL.md"));
1050
+ kimiChanged = true;
1051
+ }
1034
1052
 
1035
1053
  // Install/update stop hook — fires after each Kimi turn to check inbox
1036
1054
  let hookChanged = false;
@@ -1792,14 +1810,22 @@ if (!cmd || cmd === "install" || cmd === "agent" || cmd?.startsWith("--")) {
1792
1810
  console.log(`\n ${green}✓${r} Kimi CLI configured: ${dim}${kimiPath}${r}`);
1793
1811
  console.log(` ${dim}Per-project — use the kimi-pc wrapper (see below) so Kimi loads this config.${r}`);
1794
1812
  }
1795
- // Install/update global skills
1796
- const kimiSkillDir = join(HOME, ".kimi", "skills", "patchcord");
1797
- const kimiWaitDir = join(HOME, ".kimi", "skills", "patchcord-wait");
1798
- mkdirSync(kimiSkillDir, { recursive: true });
1813
+ // Install/update global skills (remove old combined skill, install three focused ones)
1814
+ const oldKimiSkillDir = join(HOME, ".kimi", "skills", "patchcord");
1815
+ const oldKimiWaitDir = join(HOME, ".kimi", "skills", "patchcord-wait");
1816
+ if (existsSync(oldKimiSkillDir)) rmSync(oldKimiSkillDir, { recursive: true, force: true });
1817
+ if (existsSync(oldKimiWaitDir)) rmSync(oldKimiWaitDir, { recursive: true, force: true });
1818
+
1819
+ const kimiInboxDir = join(HOME, ".kimi", "skills", "patchcord:inbox");
1820
+ const kimiWaitDir = join(HOME, ".kimi", "skills", "patchcord:wait");
1821
+ const kimiSubDir = join(HOME, ".kimi", "skills", "patchcord:subscribe");
1822
+ mkdirSync(kimiInboxDir, { recursive: true });
1799
1823
  mkdirSync(kimiWaitDir, { recursive: true });
1800
- cpSync(join(pluginRoot, "per-project-skills", "kimi", "SKILL.md"), join(kimiSkillDir, "SKILL.md"));
1824
+ mkdirSync(kimiSubDir, { recursive: true });
1825
+ cpSync(join(pluginRoot, "per-project-skills", "kimi", "inbox", "SKILL.md"), join(kimiInboxDir, "SKILL.md"));
1801
1826
  cpSync(join(pluginRoot, "per-project-skills", "kimi", "wait", "SKILL.md"), join(kimiWaitDir, "SKILL.md"));
1802
- console.log(` ${green}✓${r} Skills installed: ${dim}patchcord${r}, ${dim}patchcord-wait${r}`);
1827
+ cpSync(join(pluginRoot, "per-project-skills", "kimi", "subscribe", "SKILL.md"), join(kimiSubDir, "SKILL.md"));
1828
+ console.log(` ${green}✓${r} Skills installed: ${dim}patchcord:inbox${r}, ${dim}patchcord:wait${r}, ${dim}patchcord:subscribe${r}`);
1803
1829
 
1804
1830
  // Install alias for per-project --mcp-config-file
1805
1831
  const aliasLine = `alias kimi-pc='kimi --mcp-config-file .kimi/mcp.json'`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "patchcord",
3
- "version": "0.5.74",
3
+ "version": "0.5.76",
4
4
  "description": "Cross-machine agent messaging for Claude Code and Codex",
5
5
  "author": "ppravdin",
6
6
  "license": "MIT",
@@ -1,12 +1,12 @@
1
1
  ---
2
- name: patchcord
2
+ name: patchcord:inbox
3
3
  description: >
4
4
  Cross-agent messaging for Kimi CLI via the Patchcord MCP server. Use when
5
5
  the user mentions other agents, inbox state, sending messages, who's online,
6
6
  or cross-machine coordination.
7
7
  ---
8
8
 
9
- # Patchcord for Kimi CLI
9
+ # patchcord:inbox
10
10
 
11
11
  You are connected to Patchcord through the MCP server configured in
12
12
  `.kimi/mcp.json` inside your project directory. Kimi normally uses a global
@@ -32,103 +32,86 @@ Reload your shell or run `source ~/.bashrc` (or `~/.zshrc`) to use it.
32
32
 
33
33
  ## Tools available
34
34
 
35
- - `inbox(all_agents?)` - read pending messages, current identity, and recently active agents. `all_agents=true` includes inactive agents. Returns a `groups` list (messages grouped by thread) alongside the legacy `pending` flat list. Presence tells you whether to wait for a reply after sending, not whether to send.
36
- - `send_message(to_agent, content, thread?)` - send a message. Comma-separated for multiple: `send_message("backend, frontend", "hello")`. Use `@username` for cross-user Gate messaging. `thread` is an optional slug to start or join a named thread: `send_message("backend", "...", thread="auth-migration")`. Messages support up to 50,000 characters - send full content, specs, and code as-is. Never summarize or truncate.
37
- - `reply(message_id, content?, defer?, resolve?)` - reply to a received message. Auto-inherits the thread of the original. `defer=true` keeps the original visible in inbox for later (survives context compaction). `resolve=true` closes the thread — stamps `thread_resolved_at` and notifies sender. Content is optional: use `reply(message_id, resolve=true)` to silently close without sending.
38
- - `wait_for_message(timeout_seconds?)` - block until incoming message arrives. Default 5 minutes. Known to error intermittently - if it fails, poll inbox() every 10-15 seconds as fallback.
35
+ - `inbox(all_agents?)` - read pending messages, current identity, and recently active agents. `all_agents=true` includes inactive agents. Returns a `groups` list (messages grouped by thread) alongside the legacy `pending` flat list.
36
+ - `send_message(to_agent, content, thread?)` - send a message. Comma-separated for multiple: `send_message("backend, frontend", "hello")`. Use `@username` for cross-user Gate messaging. `thread` is an optional slug to start or join a named thread.
37
+ - `reply(message_id, content?, defer?, resolve?)` - reply to a received message. Auto-inherits the thread of the original. `defer=true` keeps the original visible in inbox for later. `resolve=true` closes the thread.
38
+ - `wait_for_message(timeout_seconds?)` - block until incoming message arrives. Default 5 minutes. Known to error intermittently - if it fails, poll inbox() every 10-15 seconds as fallback. Detailed usage is in the `patchcord:wait` skill.
39
39
  - `attachment(...)` - upload, download, or relay files between agents (see File sharing below)
40
- - `recall(limit?, from_agent?, thread_id?)` - view recent message history including already-read messages. `from_agent` filters by sender. `thread_id` filters to a specific thread. For debugging only, not routine use.
40
+ - `recall(limit?, from_agent?, thread_id?)` - view recent message history including already-read messages. For debugging only, not routine use.
41
41
  - `unsend(message_id)` - take back a message before the recipient reads it
42
42
 
43
- ## Notification mechanisms
44
-
45
- Kimi has **two** complementary ways to learn about incoming messages:
46
-
47
- 1. **Stop hook** (always active) — after every turn, Kimi runs `~/.kimi/patchcord-stop-hook.sh`. It detects the project from `cwd` in stdin, walks up looking for `.kimi/mcp.json`, then checks the inbox. If messages are pending, it injects `📬 Patchcord: You have N pending message(s)` into the next turn's context. No background task needed.
48
-
49
- 2. **Background polling** (optional, proactive) — run `patchcord subscribe` to start a background task that polls the inbox every 30 seconds (or pass a custom interval: `patchcord subscribe 10`). When messages arrive, the task exits with code 0, triggering Kimi's auto-run. This wakes Kimi even during idle periods.
50
-
51
- ### Re-arming the background poll
52
-
53
- **Critical:** the background poll is **one-shot** — it exits on first detection and must be re-launched. Every time you read messages from `inbox()` or `wait_for_message()`, **re-arm the listener before doing any work**:
54
-
55
- ```
56
- Run: patchcord subscribe
57
- ```
58
-
59
- This ensures the next incoming message will also wake you up. If you forget, you will only be notified via the stop hook (which only fires at the end of your next turn).
60
-
61
43
  ## Do the work, never just acknowledge
62
44
 
63
45
  When you receive a message from another agent:
64
46
 
65
- 1. Do the task described in the message first. Update the file. Write the code. Fix the bug. Whatever it asks - do it.
66
- 2. Then reply with what you did. File paths, line numbers, concrete changes.
67
- 3. Never reply with only an acknowledgment. "Got it", "Will do", "Ready" are not acceptable as standalone replies.
47
+ 1. Do the task described in the message first. Update the file. Write the code. Fix the bug. Create the document. Whatever the message asks - do it.
48
+ 2. Then reply with what you did. Not what you plan to do. Not that you received it. What you actually did. File paths, line numbers, concrete changes.
49
+ 3. Never reply with only an acknowledgment. "Got it", "Understood", "Will do", "Ready" — these are not acceptable as standalone replies.
68
50
 
69
- The user can undo any change in seconds. A wrong action costs nothing. A useless ack wastes everyone's time.
70
-
71
- If you genuinely cannot act (missing file access, need credentials, ambiguous target): say specifically what's blocking you.
72
-
73
- If you can't do it right now: use `reply(message_id, "reason", defer=true)` to keep the message visible for later. Never silently skip a message.
74
-
75
- ## Startup
76
-
77
- Call `inbox()` once at session start. **If inbox is empty, say nothing about it — proceed silently with the user's task.**
78
-
79
- If there are pending actionable messages:
80
-
81
- 1. Do the work described in each message
82
- 2. Reply with what you did
83
- 3. Tell the user what came in and what you did about it
84
-
85
- Do not ask the user for permission to reply unless the requested action is destructive or requires secrets you do not have.
86
-
87
- ## Threads
51
+ The user can undo any change in seconds with git. A wrong action costs nothing. A useless ack wastes everyone's time.
88
52
 
89
- Named threads group related messages between a pair of agents. Use them for multi-turn tasks that need their own context.
53
+ **If a message contains a spec, update, or instruction - act on it immediately:**
54
+ - Spec received - update the relevant docs/code now, reply with what you changed
55
+ - Bug report received - investigate and fix now, reply with the fix
56
+ - Architecture decision received - update the relevant files now, reply with what you updated
57
+ - Role assignment received - start doing that role now, reply with first actions taken
90
58
 
91
- - **Start**: `send_message("backend", "track this here", thread="deploy-review")`
92
- - **Reply stays in thread automatically** — `reply()` inherits `thread_id` from the message you're replying to.
93
- - **Close**: `reply(message_id, "done", resolve=true)` — closes the thread and notifies sender.
94
- - **Filter history**: `recall(thread_id="<uuid>")` — only messages in that thread.
59
+ **If you genuinely cannot act** (missing file access, need credentials, ambiguous target): say specifically what's blocking you.
95
60
 
96
- `inbox()` `groups` field clusters pending messages by thread. Each group: `{ thread_id, thread_title, messages }`. `thread_id: null` = pair-level.
61
+ **If you can't do it right now** (busy with current task): use `reply(message_id, "reason why deferred", defer=true)`. This keeps the message visible in your inbox so you will come back to it. Never silently skip a message — you will forget it.
97
62
 
98
- ## Sending workflow
63
+ ## On session start or when prompted by a hook
99
64
 
100
- 1. `inbox()` - clear pending messages that block outbound sends. Note who's online (determines whether to wait after sending).
101
- 2. `send_message("agent", "specific question with paths and context")` - or `"agent1, agent2"` for multiple, or `"@username"` for cross-user Gate messaging. Add `thread="slug"` to group messages in a named thread.
102
- 3. If recipient is online: `wait_for_message()` - stay responsive for the response. If offline: skip the wait, tell the human the message is queued.
65
+ Call `inbox()`. It returns pending messages and recently active agents.
103
66
 
104
- Always send regardless of online/offline status. Messages are stored and delivered when the recipient checks inbox. Never refuse to send because an agent appears offline.
67
+ If there are pending messages, reply to all of them immediately. Do not ask the human first. Do not explain what you plan to reply. Just do the work described in each message, then reply with what you did, then tell the human what you received and what you did about it.
105
68
 
106
- After sending to an offline agent, tell the human: "Message sent. [agent] is not currently active - ask them to check their inbox."
69
+ ## Sending
107
70
 
108
- If send_message fails with a send gate error: call inbox(), reply to or resolve all pending messages, then retry the send.
71
+ 1. `inbox()` clear any pending messages that block outbound sends.
72
+ 2. `send_message("agent_name", "specific question with file paths and context")` — or `"agent1, agent2"` for multiple recipients. Use `@username` for cross-user Gate messaging. To start or join a named thread: `send_message("frontend", "content", thread="auth-migration")`.
73
+ 3. Always send regardless of recipient state. Messages are stored and delivered when the recipient checks inbox.
74
+ 4. If `send_message` fails with a send gate error: call `inbox()`, reply to or resolve all pending messages, then retry the send.
109
75
 
110
- ## Receiving workflow
76
+ ## Receiving
111
77
 
112
78
  Action requests older than 7d (per the `(Xd ago)` stamp): ask human before executing. Acks/FYIs silent-resolve at any age.
113
79
 
114
80
  1. Read the message from `inbox()` or `wait_for_message()`. Check `message.thread` / `message.thread_id` if present.
115
- 2. **Re-arm the background listener** — run `patchcord subscribe` so the next message will also wake you up.
116
- 3. Do the work - use real code, real files, real results from your project
81
+ 2. **Re-arm the background listener** — see `patchcord:subscribe` skill for how to run `patchcord subscribe` as a background task.
82
+ 3. Do the work use real code, real files, real results from your project
117
83
  4. Reply with the right flag:
118
84
  - `reply(message_id, "done: [details]")` — work done, sender might follow up. Thread auto-inherited.
119
85
  - `reply(message_id, "done: [details]", resolve=true)` — work done, thread closed.
120
86
  - `reply(message_id, resolve=true)` — silently close without sending anything.
121
87
  - `reply(message_id, "ack, prioritizing [other task] first", defer=true)` — acknowledged but work not done yet. Message stays in your inbox as a reminder.
122
- 5. If sender is online: `wait_for_message()` for follow-ups
88
+ 5. If sender is online: `wait_for_message()` for follow-ups. Detailed wait usage is in the `patchcord:wait` skill.
123
89
 
124
90
  When you have multiple pending messages, prioritize by urgency. Use `defer=true` for tasks you'll do later — if you reply without doing the work and don't defer, the message vanishes from your inbox and you will never remember to do it.
125
91
 
126
92
  Outdated deferred (work likely done, sender moved on): ask human "resolve [Xd]-old from [sender]?" before `reply(id, resolve=true)`. Don't unilaterally drop.
127
93
 
94
+ ## Threads
95
+
96
+ Named threads group related messages between a pair of agents. Use them for multi-turn tasks that need their own context.
97
+
98
+ - **Start**: `send_message("backend", "track this here", thread="deploy-review")`
99
+ - **Reply stays in thread automatically** — `reply()` inherits `thread_id` from the message you're replying to.
100
+ - **Close**: `reply(message_id, "done", resolve=true)` — closes the thread and notifies sender.
101
+ - **Filter history**: `recall(thread_id="<uuid>")` — only messages in that thread.
102
+
103
+ `inbox()` `groups` field clusters pending messages by thread. Each group: `{ thread_id, thread_title, messages }`. `thread_id: null` = pair-level.
104
+
128
105
  ## Cross-user messaging (Gate)
129
106
 
130
107
  To message a user outside your namespace, use `@username` as the to_agent. Example: `send_message("@maria", "hello")`. The message goes through their Gate - connection approval and guardrails apply. If the connection isn't approved yet, your message is held pending their approval (cap 5, 7-day TTL).
131
108
 
109
+ ### Humans
110
+
111
+ - Humans are NOT in the agents list. Use `send_message("@username", "...")` anyway — they don't need to be online or in the roster.
112
+ - The message goes through their Gate for approval. It may be held pending their approval (cap 5, 7-day TTL).
113
+ - Write plainly: who you are, what you need, no raw JSON or logs.
114
+
132
115
  ## File sharing
133
116
 
134
117
  **Files on disk → `patchcord upload` (CLI, preferred):**
@@ -0,0 +1,65 @@
1
+ ---
2
+ name: patchcord:subscribe
3
+ description: >
4
+ Start a background polling task that wakes Kimi when new Patchcord messages
5
+ arrive. Use when the user asks about notifications, background listening,
6
+ or when you need to re-arm the listener after reading messages.
7
+ ---
8
+
9
+ # patchcord:subscribe
10
+
11
+ Kimi uses **background polling** for proactive message notification. The `patchcord subscribe` command polls the inbox every N seconds and exits when messages are found, triggering Kimi's auto-run.
12
+
13
+ ## Start the listener
14
+
15
+ Run `patchcord subscribe` as a **background Shell task** with a short poll interval and long timeout:
16
+
17
+ ```
18
+ Shell:
19
+ command: patchcord subscribe 5
20
+ run_in_background: true
21
+ description: Patchcord inbox polling
22
+ timeout: 86400
23
+ ```
24
+
25
+ **`command: patchcord subscribe 5`** — poll every 5 seconds for responsive wake-up. Do not rely on the script's 30-second default; always pass an explicit interval.
26
+
27
+ **`timeout: 86400`** — background tasks default to 60 seconds, which is too short for polling. Use `86400` (24 hours, the maximum) so the listener stays active until a message arrives.
28
+
29
+ ## How it works
30
+
31
+ - Walks up from cwd looking for `.kimi/mcp.json`, falls back to global
32
+ - Polls `/api/inbox?status=pending` every N seconds
33
+ - When messages found: writes `~/.kimi/patchcord-subscribe-notify.txt` and **exits 0**
34
+ - Kimi's `BackgroundTaskManager` detects the terminal state
35
+ - If `background_autotrigger_armed = true`, Kimi auto-starts a new turn
36
+ - **One-shot** — must be re-launched after each detection
37
+ - **Pidfile guard** — prevents multiple concurrent instances
38
+
39
+ ## Re-arming the background poll
40
+
41
+ **Critical:** the background poll is **one-shot** — it exits on first detection and must be re-launched. Every time you read messages from `inbox()` or `wait_for_message()`, **re-arm the listener before doing any work**:
42
+
43
+ ```
44
+ Shell:
45
+ command: patchcord subscribe 5
46
+ run_in_background: true
47
+ description: Patchcord inbox polling
48
+ timeout: 86400
49
+ ```
50
+
51
+ This ensures the next incoming message will also wake you up.
52
+
53
+ ## Stop hook (secondary)
54
+
55
+ Kimi also has a Stop hook (`~/.kimi/patchcord-stop-hook.sh`) that checks the inbox at the end of each turn. If messages are pending, it triggers a new turn with the notification. This is a fallback — it only fires when a turn is already ending. The background poll is the primary proactive mechanism.
56
+
57
+ ## Limitations
58
+
59
+ ### Auto-trigger requires "arming"
60
+
61
+ Kimi only auto-triggers on background task completion after the user has sent at least one message in the session (`background_autotrigger_armed = true`). A fresh idle session with zero user turns will NOT auto-trigger.
62
+
63
+ ### Message consumption by other clients
64
+
65
+ If the same Patchcord identity is also used by Claude Code or Codex (with `patchcord subscribe` running), those clients will consume messages via their WebSocket listeners. The Kimi background poll competes with them. For reliable Kimi-only usage, use a dedicated agent identity.
@@ -1,22 +1,23 @@
1
1
  ---
2
- name: patchcord-wait
2
+ name: patchcord:wait
3
3
  description: >
4
- Block this turn waiting for one incoming Patchcord message via the
5
- wait_for_message MCP tool. Use when the user asks you to wait for a
6
- patchcord message.
4
+ Block this turn for up to 5 minutes waiting for one incoming Patchcord
5
+ message via the wait_for_message MCP tool. Single blocking call, no
6
+ background process. Use ONLY when the user explicitly runs /patchcord:wait
7
+ or asks you to wait for a patchcord message.
7
8
  ---
8
9
 
9
- # patchcord-wait
10
+ # patchcord:wait
10
11
 
11
- User asked you to wait for a Patchcord message. Use `wait_for_message()` only.
12
+ User invoked /patchcord:wait or asked you to wait for a Patchcord message. Use `wait_for_message()` only. Do NOT spawn a background listener.
12
13
 
13
14
  Call `wait_for_message()` to block until a message arrives (up to 5 minutes).
14
15
 
15
16
  When a message arrives:
16
17
 
17
18
  1. Read it — the tool returns from, content, and message_id. If it belongs to a thread, `thread` and `thread_id` will be set.
18
- 2. **Re-arm the background listener** — run `patchcord subscribe` so the next message will also wake you up.
19
- 3. Do the work described in the message first. Update the file, write the code, fix the bug - whatever it asks.
19
+ 2. **Re-arm the background listener** — see `patchcord:subscribe` skill for how to run `patchcord subscribe` so the next message will also wake you up.
20
+ 3. Do the work described in the message first. Update the file, write the code, fix the bug whatever it asks.
20
21
  4. Reply with what you did: `reply(message_id, "here's what I changed: [concrete details]")`. Thread is auto-inherited. Use `resolve=true` to close the thread when the task is fully done.
21
22
  5. Tell the human who wrote and what you did about it
22
23
  6. Call `wait_for_message()` again to keep listening
@@ -25,6 +26,6 @@ Loop until timeout or the human interrupts.
25
26
 
26
27
  If `wait_for_message()` errors, fall back to polling `inbox()` every 10-15 seconds instead of stopping the loop.
27
28
 
28
- Do not ask the human for permission to reply - just do the work, reply with results, then report.
29
+ Do not ask the human for permission to reply just do the work, reply with results, then report.
29
30
 
30
31
  **No ack chains.** If the arriving message is a clear ack ("Noted", "Got it", "Thanks", "Keep running") — close it silently with `reply(id, resolve=true)`, no content, and keep listening. Never text-reply to an ack. Never send "Noted" + resolve=true — that creates a new pending message the other side will feel compelled to answer.
@@ -81,6 +81,12 @@ Outdated deferred (work likely done, sender moved on): ask human "resolve [Xd]-o
81
81
 
82
82
  To message a user outside your namespace, use `@username` as the to_agent. Example: `send_message("@maria", "hello")`. The message goes through their Gate - connection approval and guardrails apply. If the connection isn't approved yet, your message is held pending their approval (cap 5, 7-day TTL).
83
83
 
84
+ ### Humans
85
+
86
+ - Humans are NOT in the agents list. Use `send_message("@username", "...")` anyway — they don't need to be online or in the roster.
87
+ - The message goes through their Gate for approval. It may be held pending their approval (cap 5, 7-day TTL).
88
+ - Write plainly: who you are, what you need, no raw JSON or logs.
89
+
84
90
  ## File sharing
85
91
 
86
92
  **Files on disk → `patchcord upload` (CLI, preferred):**