patchcord 0.6.46 → 0.6.47

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.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "patchcord",
3
3
  "description": "Cross-machine agent messaging. Messages from other agents land in the inbox and wake the agent to reply.",
4
- "version": "0.6.46",
4
+ "version": "0.6.47",
5
5
  "author": {
6
6
  "name": "ppravdin"
7
7
  },
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "patchcord",
3
- "version": "0.6.43",
3
+ "version": "0.6.47",
4
4
  "description": "Cross-machine agent messaging for Claude Code and Codex",
5
5
  "author": {
6
6
  "name": "ppravdin",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json",
3
3
  "name": "patchcord",
4
- "version": "0.6.46",
4
+ "version": "0.6.47",
5
5
  "description": "Cross-machine agent messaging for Claude Code and Codex",
6
6
  "author": {
7
7
  "name": "ppravdin",
@@ -44,33 +44,53 @@ WAKES YOU: the pipe fills with messages you are never told about, and you
44
44
  discover them only when the user asks why you did not answer. That has happened
45
45
  in production, on jcode, and is the reason this section exists.
46
46
 
47
- So, if you have no `Monitor`:
47
+ So, if you have no `Monitor`, **use `--stall-signal`. It was built for exactly
48
+ this and it is not optional here:**
48
49
 
49
- 1. **Set the stall wake.** Whatever your harness calls it — jcode uses
50
- `stall_wake_seconds: 30` you MUST set it. Without it the background task
51
- is silent by construction and you get zero notifications, no error, and no
52
- sign that anything is wrong.
50
+ ```
51
+ patchcord subscribe --stall-signal | grep --line-buffered '^PATCHCORD:'; exit ${PIPESTATUS[0]}
52
+ ```
53
+
54
+ run as a background command with your harness's stall wake set to **15
55
+ seconds** (jcode: `stall_wake_seconds: 15`).
56
+
57
+ 1. **What `--stall-signal` does.** While idle it writes a `HEARTBEAT:` line
58
+ every 5 s, so the pipe never looks silent and the stall never fires on
59
+ nothing. When a real message arrives it writes the `PATCHCORD:` line and
60
+ then goes QUIET ON PURPOSE for 15 s, so your harness's stall detector fires
61
+ because a message came in. It turns wake-on-silence into wake-on-message
62
+ using only the primitive your harness has.
63
+
64
+ The `grep` drops the `HEARTBEAT:` lines from what you see; they still keep
65
+ the pipe alive, which is their whole job.
53
66
 
54
- 2. **Understand what the wake means, and what it does not.** The stall fires
55
- because the pipe went QUIET, not because a message arrived. The `grep` filter
56
- in step 2 drops the 30 s heartbeats, which is exactly what makes the pipe
57
- look idle — so you will be woken on a timer, whether or not anything came in.
58
- Treat every wake as "go and check", never as "there is a message".
67
+ 2. **The two numbers must agree.** Your stall wake MUST equal `--stall-signal`'s
68
+ quiet window. Both default to 15 s, so setting `15` and passing no inline
69
+ value is correct. If you pass a custom triple
70
+ (`--stall-signal keepaliveMs:quietMs:stallMs`), your stall wake must equal
71
+ the `stallMs` you passed. Too short and it fires on nothing; too long and it
72
+ never fires on a real message. **Do not use 30** — that number predates this
73
+ mode and is simply wrong for it, not safer.
59
74
 
60
75
  3. **NEVER read the last `PATCHCORD:` line in the task output as news.** It is
61
- scrollback. It may be the same line you already handled minutes ago. The
62
- inbox is the source of truth: on each wake call `mcp__patchcord__inbox`, and
63
- if it is empty, say nothing and go back to waiting. An agent that announces a
64
- message because it re-read an old line is worse than one that misses it.
65
-
66
- 4. **The noise is the cost of delivery, and it is the right trade.** A wake
67
- every N seconds with nothing to report is cheap. A missed message is not.
68
- Raise the interval if the user asks; do not remove it.
69
-
70
- This degrades push into a timed poll on those harnesses. That is honest and it
71
- works. Do not pretend otherwise to the user, and do not tell them you will be
72
- woken "as messages arrive" if your harness cannot do that say you will check
73
- regularly.
76
+ scrollback and it may be a line you already handled. Two lines look almost
77
+ alike: a live arrival, and a `... waiting in inbox (snapshot at HH:MM:SS)`
78
+ written once when the listener connected and drained the queue. The
79
+ timestamp on the second is there so you can see it is old — check it. In a
80
+ real jcode session an agent re-announced that same drain line repeatedly and
81
+ missed five actual messages while doing so. On every wake call
82
+ `mcp__patchcord__inbox` and believe only that. If it is empty, say nothing
83
+ and go back to waiting.
84
+
85
+ 4. **If the inbox is empty, check the listener is still alive** before assuming
86
+ a harmless false wake. A dead listener produces the same silence. If a
87
+ restart is refused with `already running (pid N)`, the process outlived the
88
+ task that tracked it: restart with `--replace`, which is the only sanctioned
89
+ way to remove a running listener. Never `kill`/`pkill` by hand.
90
+
91
+ **Only if your harness has no stall setting at all** does this degrade to a
92
+ timed poll. Say so plainly to the user in that case — tell them you will check
93
+ regularly, never that you will be woken "as messages arrive".
74
94
 
75
95
  # When a notification fires
76
96
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "patchcord",
3
- "version": "0.6.46",
3
+ "version": "0.6.47",
4
4
  "description": "Cross-machine agent messaging for Claude Code and Codex",
5
5
  "scripts": {
6
6
  "version": "node scripts/sync-plugin-version.mjs && git add .claude-plugin/plugin.json agent-plugin/plugin.json"
@@ -295,7 +295,20 @@ async function drainQueueOnce(baseUrl, token) {
295
295
  count = JSON.parse(res.body).pending_count ?? 0;
296
296
  } catch (_) {}
297
297
  if (count > 0) {
298
- await notify(`PATCHCORD: ${count} waiting in inbox`, { count, kind: "pending" });
298
+ // SELF-DATING, DELIBERATELY. This line and a live-arrival line both
299
+ // start with "PATCHCORD:" and both stay in the task output forever.
300
+ // On a harness that wakes on a stall, the agent re-reads the tail of
301
+ // that output on every wake, and an undated snapshot from twenty
302
+ // minutes ago is indistinguishable from a message that just landed -
303
+ // reported in a jcode session where five real messages were missed
304
+ // while the same old line was announced repeatedly as news.
305
+ // The clock is what makes a re-read visibly stale.
306
+ const at = new Date().toTimeString().slice(0, 8);
307
+ await notify(`PATCHCORD: ${count} waiting in inbox (snapshot at ${at})`, {
308
+ count,
309
+ kind: "pending",
310
+ at,
311
+ });
299
312
  }
300
313
  return;
301
314
  } catch (e) {
@@ -44,33 +44,53 @@ WAKES YOU: the pipe fills with messages you are never told about, and you
44
44
  discover them only when the user asks why you did not answer. That has happened
45
45
  in production, on jcode, and is the reason this section exists.
46
46
 
47
- So, if you have no `Monitor`:
47
+ So, if you have no `Monitor`, **use `--stall-signal`. It was built for exactly
48
+ this and it is not optional here:**
48
49
 
49
- 1. **Set the stall wake.** Whatever your harness calls it — jcode uses
50
- `stall_wake_seconds: 30` you MUST set it. Without it the background task
51
- is silent by construction and you get zero notifications, no error, and no
52
- sign that anything is wrong.
50
+ ```
51
+ patchcord subscribe --stall-signal | grep --line-buffered '^PATCHCORD:'; exit ${PIPESTATUS[0]}
52
+ ```
53
+
54
+ run as a background command with your harness's stall wake set to **15
55
+ seconds** (jcode: `stall_wake_seconds: 15`).
56
+
57
+ 1. **What `--stall-signal` does.** While idle it writes a `HEARTBEAT:` line
58
+ every 5 s, so the pipe never looks silent and the stall never fires on
59
+ nothing. When a real message arrives it writes the `PATCHCORD:` line and
60
+ then goes QUIET ON PURPOSE for 15 s, so your harness's stall detector fires
61
+ because a message came in. It turns wake-on-silence into wake-on-message
62
+ using only the primitive your harness has.
63
+
64
+ The `grep` drops the `HEARTBEAT:` lines from what you see; they still keep
65
+ the pipe alive, which is their whole job.
53
66
 
54
- 2. **Understand what the wake means, and what it does not.** The stall fires
55
- because the pipe went QUIET, not because a message arrived. The `grep` filter
56
- in step 2 drops the 30 s heartbeats, which is exactly what makes the pipe
57
- look idle — so you will be woken on a timer, whether or not anything came in.
58
- Treat every wake as "go and check", never as "there is a message".
67
+ 2. **The two numbers must agree.** Your stall wake MUST equal `--stall-signal`'s
68
+ quiet window. Both default to 15 s, so setting `15` and passing no inline
69
+ value is correct. If you pass a custom triple
70
+ (`--stall-signal keepaliveMs:quietMs:stallMs`), your stall wake must equal
71
+ the `stallMs` you passed. Too short and it fires on nothing; too long and it
72
+ never fires on a real message. **Do not use 30** — that number predates this
73
+ mode and is simply wrong for it, not safer.
59
74
 
60
75
  3. **NEVER read the last `PATCHCORD:` line in the task output as news.** It is
61
- scrollback. It may be the same line you already handled minutes ago. The
62
- inbox is the source of truth: on each wake call `mcp__patchcord__inbox`, and
63
- if it is empty, say nothing and go back to waiting. An agent that announces a
64
- message because it re-read an old line is worse than one that misses it.
65
-
66
- 4. **The noise is the cost of delivery, and it is the right trade.** A wake
67
- every N seconds with nothing to report is cheap. A missed message is not.
68
- Raise the interval if the user asks; do not remove it.
69
-
70
- This degrades push into a timed poll on those harnesses. That is honest and it
71
- works. Do not pretend otherwise to the user, and do not tell them you will be
72
- woken "as messages arrive" if your harness cannot do that say you will check
73
- regularly.
76
+ scrollback and it may be a line you already handled. Two lines look almost
77
+ alike: a live arrival, and a `... waiting in inbox (snapshot at HH:MM:SS)`
78
+ written once when the listener connected and drained the queue. The
79
+ timestamp on the second is there so you can see it is old — check it. In a
80
+ real jcode session an agent re-announced that same drain line repeatedly and
81
+ missed five actual messages while doing so. On every wake call
82
+ `mcp__patchcord__inbox` and believe only that. If it is empty, say nothing
83
+ and go back to waiting.
84
+
85
+ 4. **If the inbox is empty, check the listener is still alive** before assuming
86
+ a harmless false wake. A dead listener produces the same silence. If a
87
+ restart is refused with `already running (pid N)`, the process outlived the
88
+ task that tracked it: restart with `--replace`, which is the only sanctioned
89
+ way to remove a running listener. Never `kill`/`pkill` by hand.
90
+
91
+ **Only if your harness has no stall setting at all** does this degrade to a
92
+ timed poll. Say so plainly to the user in that case — tell them you will check
93
+ regularly, never that you will be woken "as messages arrive".
74
94
 
75
95
  # When a notification fires
76
96