@grknbyk/agent-wire 0.13.3 → 0.13.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/README.md CHANGED
@@ -20,10 +20,10 @@ produces a conversation nobody can audit. In a channel, the humans who own those
20
20
  agents read the whole exchange, scroll back through it, and step in by typing.
21
21
 
22
22
  ```
23
- 🔥 grkn => *mira
23
+ 🔥 grkn => @mira
24
24
  migration 0042 is on dev now, txn_date is a DATE not a TIMESTAMP
25
25
 
26
- ⚡ mira => *grkn
26
+ ⚡ mira => @grkn
27
27
  got it, rewriting the report query
28
28
  ```
29
29
 
@@ -254,15 +254,15 @@ Running `agent-wire` with no arguments shows where you stand:
254
254
  │ agent-hcm ○ off 1 held │
255
255
  │ agent-lab ○ off 1 held │
256
256
  ├────────────────── PEERS ───────────────────┤
257
- @ Zoë * kai * mira │
258
- * warehouse-… * robin ! nox │
257
+ + Zoë @ kai @ mira │
258
+ @ warehouse-… @ robin ! nox │
259
259
  ├────────────────── STATE ───────────────────┤
260
260
  │ workspace Acme poll 14s ago │
261
261
  └────────────────────────────────────────────┘
262
262
  ```
263
263
 
264
- The peers section lists everyone this agent has heard from: `*` for an agent,
265
- `@` for a human typing in the channel, `!` for a name that has been forged.
264
+ The peers section lists everyone this agent has heard from: `@` for an agent,
265
+ `+` for a human typing in the channel, `!` for a name that has been forged.
266
266
  Anything too wide for its column ends in `…`, so one long nickname costs its own
267
267
  row a character instead of pushing the border out.
268
268
 
@@ -412,17 +412,22 @@ Every message carries a handle at the right edge of its header line, padded to
412
412
  column 60 so a scrolled channel has one straight edge to read down:
413
413
 
414
414
  ```
415
- 🔥 grkn => *sinan wms-agents@k7m2pq
416
- 🚀 hakan-akduman => @Sinan wms-agents@zpbxdf
415
+ 🔥 grkn => @sinan wms-agents@k7m2pq
416
+ 🚀 hakan-akduman => +Sinan wms-agents@zpbxdf
417
417
  🛰️ mehmet-emin-kaya => all wms-agents@8g88zm
418
418
  ```
419
419
 
420
- The recipient carries the same two markers the status panel uses: `*` for an
421
- agent, `@` for a person. One name often belongs to both, the agent `sinan` and
422
- the colleague Sinan, and without the marker you cannot tell which one a line was
423
- addressed to. A name that is neither a pinned agent nor a resolved Slack user
424
- stays bare, because a marker there would be a guess. The sender is never marked:
425
- every one of these lines was written by an agent.
420
+ The recipient carries the same two markers the status panel uses: `@` for an
421
+ agent, `+` for a person. `@` is the character you already type to call an agent,
422
+ so it means the same thing in both places. People get `+` because Slack's
423
+ markdown gives it no meaning, while `*` would open a bold run and `~` a struck
424
+ one on a line the sender does not control.
425
+
426
+ One name often belongs to both, the agent `sinan` and the colleague Sinan, and
427
+ without the marker you cannot tell which one a line was addressed to. A name that
428
+ is neither a pinned agent nor a resolved Slack user stays bare, because a marker
429
+ there would be a guess. The sender is never marked: every one of these lines was
430
+ written by an agent.
426
431
 
427
432
  To point your agent at one line you scrolled past, say "wms-agents@k7m2pq oku".
428
433
  It fetches that message whatever channel it came from and whether it was already
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@grknbyk/agent-wire",
3
- "version": "0.13.3",
3
+ "version": "0.13.5",
4
4
  "description": "Let AI coding agents message each other through a shared Slack channel, over MCP.",
5
5
  "type": "module",
6
6
  "license": "MIT",
package/src/protocol.mjs CHANGED
@@ -68,21 +68,30 @@ export function displayWidth(text) {
68
68
  // one line costs less than losing a character of somebody's name.
69
69
  export const HEADER_WIDTH = 60;
70
70
 
71
- // The status panel already reads * for an agent and @ for a human, so the header
72
- // borrows the same two characters rather than inventing a third vocabulary. One
73
- // name can belong to both (the agent "sinan" and the person Sinan), and the whole
74
- // point of the marker is that the header says which one a message went to.
71
+ // @ is the character a person types to call an agent, so it means agent here too
72
+ // and one name carries one meaning in both places. People get +, which Slack gives
73
+ // no markdown meaning: * would open a bold run and ~ a struck one, on a line the
74
+ // sender does not control.
75
+ //
76
+ // One name often belongs to both, the agent "sinan" and the colleague Sinan, and
77
+ // saying which one a message went to is the whole point of the marker.
75
78
  //
76
79
  // Only the recipient is marked. Every header line was written by an agent, so a
77
80
  // marker on the sender would have been the one field that can never vary.
78
81
  //
79
82
  // A name nobody has placed stays bare. Guessing "human" for an unknown recipient
80
83
  // would put the marker on exactly the messages it is least sure about.
81
- const RECIPIENT_MARK = { agent: '*', human: '@' };
84
+ const RECIPIENT_MARK = { agent: '@', human: '+' };
82
85
 
83
86
  export const addressLine = ({ from, to, toKind }) =>
84
87
  `${from} => ${to === 'all' ? 'all' : `${RECIPIENT_MARK[toKind] ?? ''}${to}`}`;
85
88
 
89
+ // A mark set as ":fire:" is six characters here and one emoji in Slack, so
90
+ // measuring the string overshot the padding by four columns on every line this
91
+ // agent sent. Slack is the only place this header is read, so Slack's width wins.
92
+ const SHORTCODE = /^:[a-z0-9_+-]+:$/i;
93
+ const markWidth = (mark) => (SHORTCODE.test(mark) ? 2 : displayWidth(mark));
94
+
86
95
  export function formatMessage({ mark, from, to, toKind, text, ref, channel }) {
87
96
  const left = `${mark ? `${mark} ` : ''}${addressLine({ from, to, toKind })}`;
88
97
  if (!ref) return `${left}\n${toSlackText(text)}\n`;
@@ -92,14 +101,15 @@ export function formatMessage({ mark, from, to, toKind, text, ref, channel }) {
92
101
  if (!channel) throw new TypeError(`formatMessage: ref ${ref} with no channel to put in front of it`);
93
102
 
94
103
  const handle = `${channel}@${ref}`;
95
- const gap = Math.max(1, HEADER_WIDTH - displayWidth(left) - handle.length);
104
+ const drawn = mark ? markWidth(mark) + 1 + displayWidth(addressLine({ from, to, toKind })) : displayWidth(left);
105
+ const gap = Math.max(1, HEADER_WIDTH - drawn - handle.length);
96
106
  return `${left}${' '.repeat(gap)}${handle}\n${toSlackText(text)}\n`;
97
107
  }
98
108
 
99
- // The leading * on a sender is ours; a *bold* line is not, which is why the name
100
- // after it still cannot contain one. The recipient's own marker is stripped by the
101
- // pattern rather than kept, because the payload is what routing reads.
102
- const HEADER = /^(?:(?<mark>\S+)\s+)?\*?(?<from>[^\s=*]+)\s*=>\s*[@*]?(?<to>\S+?)(?:\s+(?<refChannel>[a-z0-9][\w.-]*)?@(?<ref>[a-z2-9]{4,12}))?$/;
109
+ // A sender is never marked now, but 0.13.3 marked it with a *, so the pattern
110
+ // still allows one, and the recipient still accepts * for the same reason. Markers
111
+ // are stripped rather than kept: routing reads the signed payload, not this line.
112
+ const HEADER = /^(?:(?<mark>\S+)\s+)?\*?(?<from>[^\s=*]+)\s*=>\s*[@*+]?(?<to>\S+?)(?:\s+(?<refChannel>[a-z0-9][\w.-]*)?@(?<ref>[a-z2-9]{4,12}))?$/;
103
113
 
104
114
  export function parseMessage(raw) {
105
115
  const lines = fromSlackText(String(raw ?? '').replace(/\r\n/g, '\n')).trim().split('\n');
package/src/status.mjs CHANGED
@@ -109,7 +109,8 @@ function unreadByChannel() {
109
109
  // reader already associates with a machine and a person, and `!` is the one that
110
110
  // stops the eye. All three are ASCII, so no terminal draws them double-width and
111
111
  // tips a row over its border.
112
- const peerMark = (peer) => (peer.everForged ? '!' : peer.kind === 'human' ? '@' : '*');
112
+ // The same two the header uses: @ calls an agent, + is a person.
113
+ const peerMark = (peer) => (peer.everForged ? '!' : peer.kind === 'human' ? '+' : '@');
113
114
 
114
115
  export function renderStatus(config) {
115
116
  const counts = unreadByChannel();