talking-stick 0.2.0 → 0.3.0
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 +34 -50
- package/dist/cli/install-commands.js +76 -36
- package/dist/cli/output.js +2 -2
- package/dist/cli/registry.js +13 -32
- package/dist/cli/room-commands.js +1 -1
- package/dist/cli/startup-maintenance.js +27 -1
- package/dist/cli.js +2 -2
- package/dist/config.js +2 -2
- package/dist/identity.js +4 -4
- package/dist/index.js +2 -2
- package/dist/install-audit.js +21 -0
- package/dist/install-migration.js +84 -0
- package/dist/install.js +0 -69
- package/dist/update-migration.js +135 -0
- package/docs/plans/2026-05-04-diff-walker-design.md +585 -0
- package/docs/plans/2026-05-05-cli-only-coordination.md +224 -0
- package/docs/plans/out-of-band-signaling-implementation.md +5 -5
- package/docs/receive-consumer-contract.md +8 -6
- package/docs/releases/0.3.0.md +77 -0
- package/docs/talking-stick-plan.md +3 -2
- package/package.json +4 -3
- package/scripts/postinstall-mcp-cleanup.cjs +25 -0
- package/skills/talking-stick/SKILL.md +124 -103
- package/dist/mcp-server.js +0 -244
- package/dist/server.js +0 -3
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
# CLI-Only Coordination And MCP Removal
|
|
2
|
+
|
|
3
|
+
**Status:** draft by `codex:d4bc2492`, incorporating Claude OOB review. **Branch:** `oob-identity-fix-plan`.
|
|
4
|
+
|
|
5
|
+
## Why This Change
|
|
6
|
+
|
|
7
|
+
Out-of-band messaging exposed a deeper problem: Talking Stick has two harness integration paths, CLI and MCP, and they do not share the same process context. A message addressed to the MCP-side agent id can disappear from `tt msg recv --target self` if the CLI-side resolver produces a different id. Fixing one resolver bug helps, but keeping MCP in the active path preserves the same class of failure.
|
|
8
|
+
|
|
9
|
+
The new direction is simpler: **the `tt` CLI is the only harness contract.** Agents coordinate by running `tt` commands. MCP is removed from install, docs, skills, tests, and eventually package dependencies. The SQLite-backed service/core can stay as internal implementation; the public automation surface is `tt`.
|
|
10
|
+
|
|
11
|
+
## Goals
|
|
12
|
+
|
|
13
|
+
- Make every agent workflow expressible through CLI commands: `tt join`, `tt wait`, `tt release`, `tt assign`, `tt take`, `tt state`, `tt events`, `tt notes`, and `tt msg`.
|
|
14
|
+
- Make ambient receive CLI-first: `tt events --follow --json` for live stdout consumers, `tt events --wait --after <cursor> --json` for process-completion consumers, and `tt msg recv` only for messages-only consumers. `--wait` and `--follow` default to `--target self`.
|
|
15
|
+
- Remove MCP registration from `tt install` and remove `tt mcp` as a supported command.
|
|
16
|
+
- Automatically remove stale Talking Stick MCP registrations from every supported harness during update/first-run migration.
|
|
17
|
+
- Update the bundled skill so harnesses prefer CLI even when MCP tools exist in older installations.
|
|
18
|
+
- Replace MCP smoke coverage with real child-process CLI smoke coverage, including SIGTERM/cursor behavior.
|
|
19
|
+
- Keep long turns safe by making the CLI-owned lease guardian a first-class contract of `tt wait` / `tt take`.
|
|
20
|
+
- Preserve human-friendly CLI defaults: human shells get readable text unless `--json` is explicit; harness-detected shells get JSON where machine output is expected.
|
|
21
|
+
|
|
22
|
+
## Non-Goals
|
|
23
|
+
|
|
24
|
+
- No backward compatibility promise for MCP users beyond automatic cleanup of stale Talking Stick-owned MCP registrations. This is an intentional breaking simplification.
|
|
25
|
+
- No plugin requirement. Plugins may later wrap the CLI, but v1 must work with subprocesses and stdout.
|
|
26
|
+
- No daemon. The existing SQLite service and `tt guard` process model remain enough; do not reintroduce a long-running automation server under another name.
|
|
27
|
+
- No change to the single-writer room semantics.
|
|
28
|
+
|
|
29
|
+
## Target Architecture
|
|
30
|
+
|
|
31
|
+
### Public Surface
|
|
32
|
+
|
|
33
|
+
`tt` is the public API. Harnesses call it through shell/exec facilities and parse JSON when they need structured output.
|
|
34
|
+
|
|
35
|
+
Core command mapping:
|
|
36
|
+
|
|
37
|
+
| Need | CLI |
|
|
38
|
+
|---|---|
|
|
39
|
+
| Join room | `tt join [path] --json` |
|
|
40
|
+
| Wait/claim | `tt wait [path] --json` |
|
|
41
|
+
| Probe | `tt try [path] --json` |
|
|
42
|
+
| Release | `tt release [path] --stdin` |
|
|
43
|
+
| Assign specific next holder | `tt assign <agent_id> [path] --stdin` |
|
|
44
|
+
| Operator takeover | `tt take [path] --operator-requested --reason ... --json` |
|
|
45
|
+
| State | `tt state [path] --json` |
|
|
46
|
+
| Events | `tt events [path] --after N --target any --json` |
|
|
47
|
+
| Notes | `tt notes add/list ... --json` |
|
|
48
|
+
| Send OOB | `tt msg send <recipient|room> <body...> --json` |
|
|
49
|
+
| Ambient receive | `tt events --follow --json` |
|
|
50
|
+
| Messages-only receive | `tt msg recv --follow --json` |
|
|
51
|
+
|
|
52
|
+
The skill should teach these exact commands. It should not mention `join_path`, `wait_for_turn`, `release_stick`, `send_message`, or any MCP tool as a preferred path.
|
|
53
|
+
|
|
54
|
+
Successful `tt wait` and `tt take` calls must start or repair the internal `tt guard` lease guardian and return its `guardian_pid` in JSON. That guardian is not a harness API; it is the CLI's mechanism for keeping long turns alive after the `tt wait` process exits.
|
|
55
|
+
|
|
56
|
+
### Internal Surface
|
|
57
|
+
|
|
58
|
+
Keep `TalkingStickService` / `TalkingStickCommands` as internal TypeScript modules backing the CLI. They are not the harness API. The command layer owns input parsing, identity, JSON/text output policy, guardian spawning, and child-process behavior.
|
|
59
|
+
|
|
60
|
+
The CLI-only rule applies to external harness coordination. Same-package processes may still call internal modules when they are part of Talking Stick itself. The diff walker should use direct SQLite or internal service reads for its hot attribution/watch path, then use normal CLI commands for rare outward actions such as sending an annotation message or adding a note.
|
|
61
|
+
|
|
62
|
+
### Holder Lease Guardian
|
|
63
|
+
|
|
64
|
+
Claude's main review concern is load-bearing: if `tt wait` exits and no process continues heartbeating, a holder doing a long edit can time out and look stale. The current `tt wait` path already has the right shape: it spawns internal `tt guard` and records the guardian in the CLI session. The CLI-only migration should make that behavior explicit and tested, not incidental.
|
|
65
|
+
|
|
66
|
+
Required behavior:
|
|
67
|
+
|
|
68
|
+
- `tt wait --json` returning `your_turn` includes `guardian_pid`.
|
|
69
|
+
- `tt take --json` returning success includes `guardian_pid`.
|
|
70
|
+
- If the caller already owns the turn and the old guardian is gone, `tt wait` repairs it by spawning a replacement.
|
|
71
|
+
- `tt release`, `tt pass`, and `tt assign` do not require the original `tt wait` process to still exist; they rely on the persisted CLI session and identity.
|
|
72
|
+
- Tests kill the guardian and prove the next owner command either repairs or reports the problem clearly.
|
|
73
|
+
|
|
74
|
+
This avoids a harness-level `tt heartbeat` timer and avoids wrapping all work in a daemon-like `tt hold` command.
|
|
75
|
+
|
|
76
|
+
### Process Cost
|
|
77
|
+
|
|
78
|
+
CLI-only coordination pays a Node startup and SQLite-open cost for every command. That is acceptable for `tt wait` cycles, handoffs, notes, and OOB messages. Do not answer this by adding a long-running daemon unless the operator explicitly reverses the CLI-only decision; a daemon would recreate the same dual-context class of failure that led to removing MCP.
|
|
79
|
+
|
|
80
|
+
### Identity
|
|
81
|
+
|
|
82
|
+
With MCP removed, issue #19 becomes a CLI-only identity reliability issue instead of a CLI-vs-MCP comparison. The resolver still needs to be stable across repeated shell-outs from the same harness:
|
|
83
|
+
|
|
84
|
+
1. `TT_HARNESS_AGENT_ID` wins when explicitly exported.
|
|
85
|
+
2. Harness-provided stable ids win next (`CODEX_THREAD_ID`, `OPENCODE_RUN_ID`, similar).
|
|
86
|
+
3. Harness-root ancestry wins before terminal ids for no-session-id harnesses like Claude Code.
|
|
87
|
+
4. Terminal ids (`ITERM_SESSION_ID`, `CMUX_TAB_ID`, etc.) are fallback only after no harness root can be found.
|
|
88
|
+
5. Human fallback remains `human:<username>`.
|
|
89
|
+
|
|
90
|
+
Live receivers should either use the resolved harness id directly or run under `TT_HARNESS_AGENT_ID=<agent_id>` when a wrapper has already joined a room as that id.
|
|
91
|
+
|
|
92
|
+
### Install
|
|
93
|
+
|
|
94
|
+
`tt install` should become a skill/bootstrap installer, not an MCP installer. Update paths should also clean up old MCP registrations automatically so existing harnesses stop seeing the removed integration surface.
|
|
95
|
+
|
|
96
|
+
Proposed behavior:
|
|
97
|
+
|
|
98
|
+
- `tt install <harness...> | --all [--print] [--copy] [--link]`: install/update the bundled skill only.
|
|
99
|
+
- Remove `tt install-skill` / `tt uninstall-skill` once `tt install` owns skill installation. There is no compatibility requirement to keep parallel install verbs.
|
|
100
|
+
- `tt uninstall`: remove the skill install and Talking Stick-owned stale MCP config entries.
|
|
101
|
+
- Remove MCP config writes for Claude, Codex, Gemini, and OpenCode.
|
|
102
|
+
- Remove `DEFAULT_SERVER_COMMAND = ["tt", "mcp"]`.
|
|
103
|
+
- Remove native `harness mcp add/remove` command planning.
|
|
104
|
+
|
|
105
|
+
### Update-Time MCP Cleanup
|
|
106
|
+
|
|
107
|
+
Backward compatibility means removing the stale broken path, not keeping it alive. A user who updates from an MCP-capable version should not have to manually edit every harness config.
|
|
108
|
+
|
|
109
|
+
Required behavior:
|
|
110
|
+
|
|
111
|
+
- The package update lifecycle runs a cleanup pass when the package manager executes install/update scripts.
|
|
112
|
+
- `tt self-update` in versions that know about this migration runs the cleanup pass after the package manager completes.
|
|
113
|
+
- The first normal `tt` invocation after a package version change runs the same cleanup pass, covering users who update from an older `tt self-update`, skip lifecycle scripts, or use `npm update -g`, `mise`, `pnpm`, or another manager.
|
|
114
|
+
- Cleanup targets all supported harness config locations, not only the currently detected harness. Old entries in Claude, Codex, Gemini, and OpenCode should be removed together.
|
|
115
|
+
- The cleanup is idempotent and silent when nothing changes. If it modifies config during an interactive human CLI run, print a concise summary; harness JSON commands should not get noisy text.
|
|
116
|
+
- Always append an audit entry for removals with harness, config path, and removed server key/name so an operator can inspect why an MCP entry disappeared.
|
|
117
|
+
- Only remove Talking Stick-owned MCP entries by strict inverse-of-install matching: canonical server name/key `talking-stick` plus the exact command/args shape this installer previously wrote for that harness.
|
|
118
|
+
- Leave hand-edited or custom entries alone, even if they mention `talking-stick`, when the name/key or command/args no longer match the canonical installed shape.
|
|
119
|
+
- Do not remove unrelated MCP servers or unrelated harness settings.
|
|
120
|
+
- Config writes remain atomic and should preserve formatting where the existing installer machinery already can.
|
|
121
|
+
|
|
122
|
+
Implementation shape:
|
|
123
|
+
|
|
124
|
+
- Add `removeStaleMcpRegistrations({ harnesses: "all", reason: "update" })` in the install/maintenance layer.
|
|
125
|
+
- Track the last package version that completed migration in the Talking Stick data dir, separate from room state, so startup can cheaply decide whether to run it.
|
|
126
|
+
- Expose a non-interactive internal cleanup entrypoint that package lifecycle scripts can call without joining rooms or emitting harness JSON.
|
|
127
|
+
- Reuse the existing harness config discovery/write helpers and adapter-level install shape. Removal should be the inverse of install, not a second parser/matcher.
|
|
128
|
+
- Delete MCP add planning after cleanup support lands, but keep the adapter-owned stale-entry removal path.
|
|
129
|
+
- Add fixture tests for each supported harness proving a stale canonical Talking Stick MCP entry is removed, a neighboring unrelated MCP entry survives, and a hand-edited Talking Stick-looking entry survives.
|
|
130
|
+
- The audit log lives in a dedicated `${TALKING_STICK_DATA_DIR}/update-migrations.log`, not the general maintenance log. Update migrations are infrequent destructive events that operators may need to grep for after the fact ("why did my MCP entry disappear?"); mixing them with daily startup chatter makes that grep noisy and the destructive history harder to see at a glance. Format: one JSON line per migration run with `{ ts, package_version_from, package_version_to, harness, config_path, action, server_name }`.
|
|
131
|
+
|
|
132
|
+
### OOB Messaging And Ambient Receive
|
|
133
|
+
|
|
134
|
+
OOB stays on the existing `room_events` / `message_sent` substrate, but all live receive UX is CLI-based.
|
|
135
|
+
|
|
136
|
+
The recommended ambient consumer is **`tt events --follow --json`**, not `tt msg recv --follow`. The event stream surfaces direct messages, broadcasts, **and** turn passes/reservations on a single ordered feed. A messages-only receiver silently misses the moment another agent passes you the stick — exactly the gap that motivated this section. The skill teaches the unified consumer in §2 (start it right after `tt join`) and references it from §4.5.
|
|
137
|
+
|
|
138
|
+
Required receive contract:
|
|
139
|
+
|
|
140
|
+
- `tt events --follow --json` emits one JSON event per line for messages, broadcasts, and turn handoffs targeting the caller.
|
|
141
|
+
- It exits cleanly on SIGTERM/SIGHUP and writes the cursor to stderr as today.
|
|
142
|
+
- `tt events --wait --after <cursor> --json` exits after the next matching batch for harnesses that cannot monitor long-running stdout.
|
|
143
|
+
- Event receive is observer-only. It does not claim the stick, does not start a guardian, and must not be treated as permission to edit. A receiver that wakes on `pass`, `release`, or `assignment` still has to run `tt wait --json` and get `your_turn` before touching shared files.
|
|
144
|
+
- `tt msg recv --follow` and `tt msg recv --wait` remain available as messages-only feeds for harnesses that explicitly want the narrower stream, but the skill marks them as fallback rather than primary.
|
|
145
|
+
- First launch starts at the room tail unless `--after` is explicit, avoiding historical replay.
|
|
146
|
+
- Direct messages and room broadcasts are included for `target=self`; the caller's own broadcasts are excluded.
|
|
147
|
+
|
|
148
|
+
## Implementation Sequence
|
|
149
|
+
|
|
150
|
+
### Stage 1 — Skill And Plan
|
|
151
|
+
|
|
152
|
+
This PR:
|
|
153
|
+
|
|
154
|
+
- Add this plan.
|
|
155
|
+
- Update `skills/talking-stick/SKILL.md` to be CLI-only.
|
|
156
|
+
- Remove MCP-first language from the OOB skill section.
|
|
157
|
+
- Keep source code unchanged except docs/skill, so Claude can review direction before the destructive code pass.
|
|
158
|
+
|
|
159
|
+
### Stage 2 — CLI Identity Hardening
|
|
160
|
+
|
|
161
|
+
- Change resolver precedence so harness-root ancestry beats terminal-session fallback when a harness env marker exists but no stable session id is present.
|
|
162
|
+
- Add tests for Claude CLI shell-out matching repeated invocations from the same harness root.
|
|
163
|
+
- Add child-process tests proving `tt events --wait` / `tt events --follow` default to self and receive direct messages and turn handoffs addressed to the active CLI member without `TT_HARNESS_AGENT_ID`.
|
|
164
|
+
- Keep narrower coverage proving `tt msg recv` receives direct messages without widening into non-message events.
|
|
165
|
+
- Add guardian tests proving `tt wait` / `tt take` return a live `guardian_pid`, and that a subsequent `tt wait` repairs a dead guardian for an existing owner.
|
|
166
|
+
- Keep `TT_HARNESS_AGENT_ID` override as the explicit escape hatch.
|
|
167
|
+
|
|
168
|
+
### Stage 3 — Installer De-MCP
|
|
169
|
+
|
|
170
|
+
- Change `tt install` / `tt uninstall` to manage skill installs plus cleanup of stale Talking Stick MCP entries.
|
|
171
|
+
- Add automatic stale MCP cleanup to package update lifecycle, `tt self-update`, and first-run-after-version-change startup maintenance.
|
|
172
|
+
- Delete MCP add planning from `src/install.ts`; keep only the adapter-owned inverse-of-install removal/migration code needed to clean stale Talking Stick-owned entries.
|
|
173
|
+
- Remove stale Talking Stick MCP entries during `tt uninstall` too; do not remove unrelated harness config.
|
|
174
|
+
- Update README install instructions.
|
|
175
|
+
- Add tests showing `tt install --all` skips missing harnesses silently and does not write MCP config.
|
|
176
|
+
- Add update-migration tests covering all supported harnesses, idempotency, and preservation of unrelated MCP servers.
|
|
177
|
+
|
|
178
|
+
### Stage 4 — Remove MCP Server Surface
|
|
179
|
+
|
|
180
|
+
- Remove `tt mcp` from the command registry and help.
|
|
181
|
+
- Delete `src/mcp-server.ts` and MCP-specific tests.
|
|
182
|
+
- Delete `src/server.ts` unless another stdio entrypoint still needs it.
|
|
183
|
+
- Remove `@modelcontextprotocol/sdk` from `package.json`.
|
|
184
|
+
- Remove MCP exports from `src/index.ts`.
|
|
185
|
+
- Replace MCP smoke tests with CLI child-process smoke tests covering join/wait/release/msg/notes.
|
|
186
|
+
|
|
187
|
+
### Stage 5 — Docs And Release Notes
|
|
188
|
+
|
|
189
|
+
- Rewrite README around CLI-first coordination.
|
|
190
|
+
- Update `docs/receive-consumer-contract.md` to describe CLI subprocess consumers, not MCP wrappers.
|
|
191
|
+
- Update older OOB plan docs with a superseded note pointing here.
|
|
192
|
+
- Add a release note marking the change as breaking.
|
|
193
|
+
|
|
194
|
+
### Stage 6 — Live Dogfood
|
|
195
|
+
|
|
196
|
+
Before merging the code-removal PR:
|
|
197
|
+
|
|
198
|
+
- Start Claude receiver with `tt events --follow --json`.
|
|
199
|
+
- Start Codex receiver the same way.
|
|
200
|
+
- Send direct and broadcast messages both directions.
|
|
201
|
+
- Verify `tt wait --json`, `tt release --stdin`, and `tt assign <agent>` work from harness shell-outs.
|
|
202
|
+
- Verify no MCP subprocess is required for the room to operate.
|
|
203
|
+
|
|
204
|
+
## Risks
|
|
205
|
+
|
|
206
|
+
- **Harnesses without reliable shell execution.** If a harness cannot run `tt`, it cannot use Talking Stick. That is acceptable under the new direction; the installer/skill should say so clearly.
|
|
207
|
+
- **Lease expiry during long turns.** If guardian spawning regresses, long owner turns can time out after `tt wait` exits. Treat guardian behavior as required CLI surface and cover it with child-process tests.
|
|
208
|
+
- **Per-command process cost.** Repeated `tt` shell-outs pay Node startup and SQLite open cost. Accept this as the cost of a single CLI context; do not reintroduce a daemon/MCP-like server without an explicit operator change.
|
|
209
|
+
- **Long-running receiver lifecycle.** Some harnesses cannot monitor stdout from a running child. Keep `--wait --after` as the supported fallback.
|
|
210
|
+
- **Combined wait/event command ambiguity.** A future `tt await`-style command needs a separate design pass. It must distinguish observer events from ownership results so an agent cannot mistake a message or handoff wake for permission to edit.
|
|
211
|
+
- **Skill drift.** Installed skills may still have old MCP-first text. Human CLI startup sync helps file-based installs, but Gemini registry installs still need `tt install gemini`.
|
|
212
|
+
- **Config cleanup safety.** Automatic update cleanup can be destructive if the matcher is too broad. Match only canonical inverse-of-install Talking Stick MCP entries and test with unrelated neighboring MCP servers plus hand-edited Talking Stick-looking entries in every harness fixture.
|
|
213
|
+
- **Large test deletion.** Removing MCP tests can accidentally reduce protocol coverage. Replace them with CLI tests before deleting assertions.
|
|
214
|
+
- **Diff walker hot path.** A live watcher should not spawn `tt` for every file event. Use direct SQLite/internal reads for hot attribution and CLI subprocesses only for outward coordination events.
|
|
215
|
+
|
|
216
|
+
## Decisions From Claude Review
|
|
217
|
+
|
|
218
|
+
1. `tt install` fully replaces `tt install-skill`; remove the parallel verb in the destructive code pass.
|
|
219
|
+
2. `tt mcp` is removed immediately rather than kept as a migration shim.
|
|
220
|
+
3. Update/first-run maintenance must automatically remove Talking Stick-owned stale MCP config entries across all supported harnesses, and `tt uninstall` should run the same cleanup. It must not touch unrelated harness config.
|
|
221
|
+
4. Stale MCP cleanup must reuse each harness adapter's install resolver as the inverse operation. Do not reimplement config matching separately.
|
|
222
|
+
5. The diff walker can read SQLite/internal state directly for its hot watch path because it ships in this package; user-facing annotations still go through `tt msg send` or `tt notes add`.
|
|
223
|
+
6. The skill's primary ambient receiver is `tt events --follow --json`, not `tt msg recv --follow`. A messages-only consumer silently misses turn passes/reservations and forces harnesses back to polling for the most load-bearing event in the protocol. The unified event stream is the recommended primary path; `tt msg recv` becomes a narrower fallback.
|
|
224
|
+
7. Cleanup audit lives in a dedicated `update-migrations.log`, not folded into general maintenance logging. Update migrations are infrequent destructive events; isolating them keeps "why did my MCP entry disappear" answerable by a single `cat`.
|
|
@@ -350,7 +350,7 @@ Four bound parameters of the caller's agent_id. Acceptable; SQLite's planner han
|
|
|
350
350
|
Add three policy values:
|
|
351
351
|
|
|
352
352
|
```ts
|
|
353
|
-
waitForEventsMaxWaitMs: number; // default
|
|
353
|
+
waitForEventsMaxWaitMs: number; // default 110_000 (matches waitForTurn)
|
|
354
354
|
waitForEventsPollMs: number; // default 250 (matches waitForTurn)
|
|
355
355
|
waitForEventsBatchLimit: number; // default 100
|
|
356
356
|
```
|
|
@@ -662,7 +662,7 @@ The talking stick guarantees single-writer authority over shared workspace state
|
|
|
662
662
|
|
|
663
663
|
**Send.** `tt msg send <recipient> "<body>"` (or `mcp send_message`). Recipient is a full `agent_id`, an unambiguous active display name, or the literal `room` for broadcast. `--interrupt` flags the message as time-sensitive; the receiver decides whether to act on it now.
|
|
664
664
|
|
|
665
|
-
**Receive.** Use the receive mode your harness can actually observe. If it can monitor stdout from a long-running child, run `tt
|
|
665
|
+
**Receive.** Use the receive mode your harness can actually observe. If it can monitor stdout from a long-running child, run `tt events --follow`; each incoming event lands as one JSON line. If it can only notice that a background command completed, run `tt events --wait --after <last_event_seq>`; it exits on the next matching batch, then you start it again with the returned cursor. SIGTERM exits cleanly; restart with `--after <last_event_seq>` to resume. Use `tt msg recv` only for messages-only consumers that intentionally do not want turn handoff events.
|
|
666
666
|
|
|
667
667
|
**When to message vs note vs handoff.**
|
|
668
668
|
|
|
@@ -672,12 +672,12 @@ The talking stick guarantees single-writer authority over shared workspace state
|
|
|
672
672
|
|
|
673
673
|
**Messages are not private.** Any room member can read any message via `get_room_events` or `tt events --follow --target any`. `to_agent_id` is routing, not ACL.
|
|
674
674
|
|
|
675
|
-
**Messages do not grant the stick.** A non-holder paging the holder does not gain write authority. The holder may act on the message immediately or defer until handoff.
|
|
675
|
+
**Messages and event wakes do not grant the stick.** A non-holder paging the holder does not gain write authority, and a receiver waking on `pass`, `release`, or `assignment` still has to run `tt wait` and receive `your_turn` before editing. The holder may act on the message immediately or defer until handoff.
|
|
676
676
|
|
|
677
|
-
**Stay in the wait loop in parallel.** A `tt
|
|
677
|
+
**Stay in the wait loop in parallel.** A `tt events --wait`, `tt events --follow`, or `tt msg recv` subprocess does not replace `wait_for_turn`. Keep waiting for your turn; receive processes are side channels.
|
|
678
678
|
```
|
|
679
679
|
|
|
680
|
-
Also: a one-line addition to §1 ("Check that Talking Stick is available") noting that `tt msg recv --wait`
|
|
680
|
+
Also: a one-line addition to §1 ("Check that Talking Stick is available") noting that `tt events --follow`, `tt msg recv --wait`, or `tt msg recv --follow` may be running as sibling processes and should be left alone.
|
|
681
681
|
|
|
682
682
|
### 4.1 Skill propagation
|
|
683
683
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Receive Consumer Contract
|
|
2
2
|
|
|
3
|
-
`send_message` appends `message_sent` events to the room event log. `wait_for_events` is the canonical receive primitive. CLI consumers (`tt msg recv --wait`, `tt msg recv --follow`) and future harness-native consumers should share the same cursor and retry rules.
|
|
3
|
+
`send_message` appends `message_sent` events to the room event log. `wait_for_events` is the canonical receive primitive. CLI consumers (`tt events --wait`, `tt events --follow`, `tt msg recv --wait`, `tt msg recv --follow`) and future harness-native consumers should share the same cursor and retry rules.
|
|
4
4
|
|
|
5
5
|
## Delivery
|
|
6
6
|
|
|
@@ -11,20 +11,22 @@
|
|
|
11
11
|
|
|
12
12
|
## Receive Modes
|
|
13
13
|
|
|
14
|
-
- Use `tt
|
|
15
|
-
- Use `tt
|
|
14
|
+
- Use `tt events --follow --after <cursor>` when the harness can monitor stdout from a long-running child. Each output line is one `RoomEvent` JSON object.
|
|
15
|
+
- Use `tt events --wait --after <cursor>` when the harness can only notice process completion. The process exits after the next matching batch or timeout; restart it with the latest processed cursor.
|
|
16
|
+
- Use `tt msg recv --wait` or `tt msg recv --follow` only when the consumer intentionally wants messages without turn handoffs.
|
|
16
17
|
- If no `--after` is supplied in `--wait` or `--follow` mode, the CLI starts from the current event tail to avoid flooding a new consumer with history.
|
|
17
18
|
- A one-shot `tt msg recv --after <cursor>` is a non-blocking drain operation.
|
|
18
19
|
|
|
19
20
|
## Filtering
|
|
20
21
|
|
|
21
|
-
- `target=self` receives direct messages to the caller plus broadcasts from other agents. It excludes the caller's own broadcasts.
|
|
22
|
-
- `target=any` receives all matching messages and is intended for audit/debug views.
|
|
22
|
+
- `target=self` is the default for `--wait` and `--follow`. It receives direct messages to the caller plus broadcasts from other agents. It excludes the caller's own broadcasts.
|
|
23
|
+
- `target=any` receives all matching events/messages and is intended for audit/debug views.
|
|
23
24
|
- `--from <agent>` resolves a full `agent_id` or unambiguous active display name and is enforced server-side.
|
|
24
25
|
|
|
25
26
|
## Consumer Responsibilities
|
|
26
27
|
|
|
27
|
-
- Keep `wait_for_turn` running separately.
|
|
28
|
+
- Keep `wait_for_turn` / `tt wait` running separately. Receive processes do not claim or grant the stick, even when they return pass, release, or assignment events.
|
|
29
|
+
- Treat an event wake as a prompt to read, reply, or retry `tt wait`. It is not permission to mutate shared files; only a `your_turn` wait result with a live guardian grants ownership.
|
|
28
30
|
- Decide how to surface `delivery_hint=interrupt`; the server only records the hint.
|
|
29
31
|
- Dedupe on `event_id` if restart replay is possible.
|
|
30
32
|
- Treat message bodies as room-visible text, not private data.
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# Talking Stick 0.3.0
|
|
2
|
+
|
|
3
|
+
Date: 2026-05-05
|
|
4
|
+
|
|
5
|
+
Breaking release that makes the `tt` CLI the only harness integration contract.
|
|
6
|
+
Talking Stick no longer installs or serves an MCP adapter. Agents coordinate by
|
|
7
|
+
running `tt` subprocesses for join/wait/handoff, notes, messages, and event
|
|
8
|
+
receive.
|
|
9
|
+
|
|
10
|
+
## Breaking Changes
|
|
11
|
+
|
|
12
|
+
### MCP server surface removed
|
|
13
|
+
|
|
14
|
+
Removed the MCP stdio server implementation, `tt mcp` command registration,
|
|
15
|
+
MCP-specific tests, and the `@modelcontextprotocol/sdk` dependency. The package
|
|
16
|
+
exports no MCP server helpers. `tt --help` no longer advertises MCP startup, and
|
|
17
|
+
`tt install` no longer writes MCP server config.
|
|
18
|
+
|
|
19
|
+
### `tt install` is skill-only
|
|
20
|
+
|
|
21
|
+
`tt install <harness>` now installs or refreshes the bundled
|
|
22
|
+
`talking-stick` skill for Claude Code, Codex, Gemini, and OpenCode. The older
|
|
23
|
+
`tt install-skill` and `tt uninstall-skill` command surface is gone because
|
|
24
|
+
`tt install` / `tt uninstall` own skill installation directly.
|
|
25
|
+
|
|
26
|
+
## Migration
|
|
27
|
+
|
|
28
|
+
### Stale MCP cleanup
|
|
29
|
+
|
|
30
|
+
Updates remove stale Talking Stick MCP registrations from older installs instead
|
|
31
|
+
of keeping the broken dual integration path alive.
|
|
32
|
+
|
|
33
|
+
Cleanup runs from:
|
|
34
|
+
|
|
35
|
+
- package postinstall when installed under `node_modules/talking-stick`
|
|
36
|
+
- `tt self-update` after the package manager command returns
|
|
37
|
+
- the first normal installed-package `tt` invocation after a package-version
|
|
38
|
+
change
|
|
39
|
+
- explicit `tt install` and `tt uninstall`
|
|
40
|
+
|
|
41
|
+
Each run appends JSONL audit entries to
|
|
42
|
+
`${TALKING_STICK_DATA_DIR}/update-migrations.log`. OpenCode cleanup is
|
|
43
|
+
shape-strict: only the canonical `mcp.talking-stick` entry with `["tt", "mcp"]`
|
|
44
|
+
is removed. Claude Code, Codex, and Gemini use their native `mcp remove`
|
|
45
|
+
commands for the old `talking-stick` server name.
|
|
46
|
+
|
|
47
|
+
## CLI-Only Runtime
|
|
48
|
+
|
|
49
|
+
The bundled skill now teaches harnesses to start
|
|
50
|
+
`tt events --follow --json` as the ambient receiver, keep
|
|
51
|
+
`tt wait --json` running for turn ownership, and verify the
|
|
52
|
+
returned guardian pid before long edits. `tt msg recv` remains a messages-only
|
|
53
|
+
fallback; the unified event stream is the primary OOB path because turn
|
|
54
|
+
handoffs and messages share one ordered feed.
|
|
55
|
+
|
|
56
|
+
For harnesses that cannot consume a long-running stdout stream, the documented
|
|
57
|
+
fallback is `tt events --wait --after <cursor> --json` as an observer-only wake
|
|
58
|
+
process alongside the normal `tt wait --json` ownership loop. Event wakes do not
|
|
59
|
+
grant the stick; agents still need a `your_turn` wait result and live guardian
|
|
60
|
+
before editing.
|
|
61
|
+
|
|
62
|
+
CLI identity resolution now prefers stable harness ancestry over transient
|
|
63
|
+
terminal ids when no explicit harness session id exists. That keeps repeated
|
|
64
|
+
shell-outs from one harness attached to the same room member.
|
|
65
|
+
|
|
66
|
+
## Verification
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
npm run typecheck
|
|
70
|
+
npm test
|
|
71
|
+
npm run build
|
|
72
|
+
git diff --check
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Stage validation covered the migration runner, install/uninstall/self-update
|
|
76
|
+
cleanup wiring, child-process CLI receive behavior, guardian repair, full-suite
|
|
77
|
+
tests after MCP deletion, and built `dist/` output with no MCP/server files.
|
|
@@ -742,8 +742,9 @@ Recommended defaults (product scale, sized for real agent work rather than chat
|
|
|
742
742
|
owner_lease_ttl_ms = 45 * 60 * 1000; // 45 minutes
|
|
743
743
|
heartbeat_interval_ms = 5 * 60 * 1000; // 5 minutes
|
|
744
744
|
claim_ttl_ms = 20 * 60 * 1000; // 20 minutes
|
|
745
|
-
wait_for_turn_max_wait_ms =
|
|
745
|
+
wait_for_turn_max_wait_ms = 110 * 1000; // 110 seconds
|
|
746
746
|
wait_for_turn_poll_ms = 250; // transport polling cadence
|
|
747
|
+
wait_for_events_max_wait_ms = 110 * 1000; // 110 seconds
|
|
747
748
|
presence_ttl_ms = 4 * 60 * 60 * 1000; // 4 hours
|
|
748
749
|
waiter_grace_ms = 10 * 1000; // 10 seconds
|
|
749
750
|
```
|
|
@@ -1158,5 +1159,5 @@ presence TTL: 4 hours
|
|
|
1158
1159
|
close semantics: no `close_room` tool in the MVP implementation;
|
|
1159
1160
|
rooms remain resumable and can become dormant
|
|
1160
1161
|
when nobody is live
|
|
1161
|
-
wait_for_turn max wait:
|
|
1162
|
+
wait_for_turn max wait: 110 seconds, polled at 250 ms
|
|
1162
1163
|
```
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "talking-stick",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"description": "CLI coordination tool for path-scoped agent handoffs.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"tt": "dist/cli.js"
|
|
@@ -11,18 +11,19 @@
|
|
|
11
11
|
},
|
|
12
12
|
"files": [
|
|
13
13
|
"dist",
|
|
14
|
+
"scripts",
|
|
14
15
|
"skills",
|
|
15
16
|
"docs",
|
|
16
17
|
"README.md"
|
|
17
18
|
],
|
|
18
19
|
"scripts": {
|
|
19
20
|
"build": "tsc -p tsconfig.build.json && chmod +x dist/cli.js",
|
|
21
|
+
"postinstall": "node scripts/postinstall-mcp-cleanup.cjs",
|
|
20
22
|
"prepare": "tsc -p tsconfig.build.json && chmod +x dist/cli.js",
|
|
21
23
|
"test": "vitest run",
|
|
22
24
|
"typecheck": "tsc -p tsconfig.json --noEmit"
|
|
23
25
|
},
|
|
24
26
|
"dependencies": {
|
|
25
|
-
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
26
27
|
"better-sqlite3": "^12.9.0",
|
|
27
28
|
"zod": "^3.25.76"
|
|
28
29
|
},
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
const { spawnSync } = require("node:child_process");
|
|
3
|
+
const fs = require("node:fs");
|
|
4
|
+
const path = require("node:path");
|
|
5
|
+
|
|
6
|
+
const cliPath = path.resolve(__dirname, "..", "dist", "cli.js");
|
|
7
|
+
const packageRoot = path.resolve(__dirname, "..").replace(/\\/g, "/");
|
|
8
|
+
|
|
9
|
+
if (
|
|
10
|
+
process.env.TALKING_STICK_DISABLE_MCP_MIGRATION ||
|
|
11
|
+
!packageRoot.includes("/node_modules/talking-stick") ||
|
|
12
|
+
!fs.existsSync(cliPath)
|
|
13
|
+
) {
|
|
14
|
+
process.exit(0);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
spawnSync(process.execPath, [cliPath, "migrate-mcp", "--reason", "update", "--quiet"], {
|
|
18
|
+
stdio: "ignore",
|
|
19
|
+
env: {
|
|
20
|
+
...process.env,
|
|
21
|
+
TALKING_STICK_SKIP_STARTUP_MAINTENANCE: "1"
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
process.exit(0);
|