talking-stick 0.2.0 → 0.4.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 +51 -49
- package/dist/cli/install-commands.js +83 -36
- package/dist/cli/instructions-commands.js +113 -0
- package/dist/cli/output.js +5 -2
- package/dist/cli/registry.js +18 -27
- 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 +3 -2
- package/dist/install-audit.js +21 -0
- package/dist/install-migration.js +84 -0
- package/dist/install.js +0 -69
- package/dist/instructions.js +256 -0
- 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/2026-05-06-harness-instructions-v6-converged.md +220 -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/releases/0.4.0.md +71 -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 +132 -104
- package/dist/mcp-server.js +0 -244
- package/dist/server.js +0 -3
package/dist/mcp-server.js
DELETED
|
@@ -1,244 +0,0 @@
|
|
|
1
|
-
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
-
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
3
|
-
import { z } from "zod";
|
|
4
|
-
import { isProtocolError } from "./errors.js";
|
|
5
|
-
import { deriveMcpHarnessIdentity } from "./identity.js";
|
|
6
|
-
import { createSystemProcessInspector } from "./process-utils.js";
|
|
7
|
-
import { TalkingStickCommands } from "./commands.js";
|
|
8
|
-
import { TalkingStickService } from "./service.js";
|
|
9
|
-
const handoffSchema = z
|
|
10
|
-
.object({
|
|
11
|
-
status: z.string(),
|
|
12
|
-
next_action: z.string(),
|
|
13
|
-
artifacts: z
|
|
14
|
-
.array(z.object({
|
|
15
|
-
path: z.string(),
|
|
16
|
-
lines: z.array(z.number().int()).length(2).optional(),
|
|
17
|
-
role: z.enum(["examine", "review", "edit", "context", "output"]),
|
|
18
|
-
note: z.string().optional()
|
|
19
|
-
}))
|
|
20
|
-
.optional(),
|
|
21
|
-
open_questions: z.array(z.string()).optional(),
|
|
22
|
-
do_not: z.array(z.string()).optional()
|
|
23
|
-
})
|
|
24
|
-
.passthrough();
|
|
25
|
-
export function createMcpServer(service = new TalkingStickService()) {
|
|
26
|
-
const commands = new TalkingStickCommands(service);
|
|
27
|
-
const resolveConnectionIdentity = createConnectionIdentityResolver();
|
|
28
|
-
const server = new McpServer({
|
|
29
|
-
name: "talking-stick",
|
|
30
|
-
version: "0.1.2"
|
|
31
|
-
});
|
|
32
|
-
server.registerTool("list_rooms", {
|
|
33
|
-
title: "List Rooms",
|
|
34
|
-
description: "List talking-stick rooms, optionally scoped to a path.",
|
|
35
|
-
inputSchema: {
|
|
36
|
-
context_path: z.string().optional()
|
|
37
|
-
}
|
|
38
|
-
}, async (input) => toolJson(() => service.listRooms(input)));
|
|
39
|
-
server.registerTool("join_path", {
|
|
40
|
-
title: "Join Path",
|
|
41
|
-
description: "Join the room resolved from an invocation context path.",
|
|
42
|
-
inputSchema: {
|
|
43
|
-
context_path: z.string().min(1),
|
|
44
|
-
force_new: z.boolean().optional(),
|
|
45
|
-
agent_id_override: z.string().min(1).optional()
|
|
46
|
-
}
|
|
47
|
-
}, async (input, extra) => toolJson(() => commands.joinPath(resolveConnectionIdentity(extra.sessionId, input.agent_id_override), {
|
|
48
|
-
context_path: input.context_path,
|
|
49
|
-
force_new: input.force_new
|
|
50
|
-
})));
|
|
51
|
-
server.registerTool("leave_room", {
|
|
52
|
-
title: "Leave Room",
|
|
53
|
-
description: "Explicitly leave a room. The room is deleted when no active members remain.",
|
|
54
|
-
inputSchema: {
|
|
55
|
-
room_id: z.string().min(1)
|
|
56
|
-
}
|
|
57
|
-
}, async (input, extra) => toolJson(() => commands.leaveRoom(resolveConnectionIdentity(extra.sessionId), input)));
|
|
58
|
-
server.registerTool("kick_member", {
|
|
59
|
-
title: "Kick Member",
|
|
60
|
-
description: "Remove an idle member from a room. Without force, only succeeds if the target's process is detected gone past the silence-grace window.",
|
|
61
|
-
inputSchema: {
|
|
62
|
-
room_id: z.string().min(1),
|
|
63
|
-
target_agent_id: z.string().min(1),
|
|
64
|
-
force: z.boolean().optional(),
|
|
65
|
-
reason: z.string().optional()
|
|
66
|
-
}
|
|
67
|
-
}, async (input, extra) => toolJson(() => commands.kickMember(resolveConnectionIdentity(extra.sessionId), input)));
|
|
68
|
-
server.registerTool("wait_for_turn", {
|
|
69
|
-
title: "Wait For Turn",
|
|
70
|
-
description: "Poll until the caller can claim the stick or takeover is available.",
|
|
71
|
-
inputSchema: {
|
|
72
|
-
room_id: z.string().min(1),
|
|
73
|
-
max_wait_ms: z.number().int().nonnegative().optional()
|
|
74
|
-
}
|
|
75
|
-
}, async (input, extra) => toolJson(() => commands.waitForTurn(resolveConnectionIdentity(extra.sessionId), input)));
|
|
76
|
-
server.registerTool("heartbeat", {
|
|
77
|
-
title: "Heartbeat",
|
|
78
|
-
description: "Renew the current owner's lease.",
|
|
79
|
-
inputSchema: ownerMutationSchema()
|
|
80
|
-
}, async (input, extra) => toolJson(() => commands.heartbeat(resolveConnectionIdentity(extra.sessionId), input)));
|
|
81
|
-
server.registerTool("release_stick", {
|
|
82
|
-
title: "Release Stick",
|
|
83
|
-
description: "Release the stick to the next active member in sequence.",
|
|
84
|
-
inputSchema: {
|
|
85
|
-
...ownerMutationSchema(),
|
|
86
|
-
handoff: handoffSchema
|
|
87
|
-
}
|
|
88
|
-
}, async (input, extra) => toolJson(() => commands.releaseStick(resolveConnectionIdentity(extra.sessionId), input)));
|
|
89
|
-
server.registerTool("pass_stick", {
|
|
90
|
-
title: "Pass Stick",
|
|
91
|
-
description: "Pass the stick to a specific active member.",
|
|
92
|
-
inputSchema: {
|
|
93
|
-
...ownerMutationSchema(),
|
|
94
|
-
to_agent_id: z.string().min(1),
|
|
95
|
-
handoff: handoffSchema
|
|
96
|
-
}
|
|
97
|
-
}, async (input, extra) => toolJson(() => commands.passStick(resolveConnectionIdentity(extra.sessionId), input)));
|
|
98
|
-
server.registerTool("takeover_stick", {
|
|
99
|
-
title: "Takeover Stick",
|
|
100
|
-
description: "Explicitly take over after claim timeout or owner lease timeout.",
|
|
101
|
-
inputSchema: {
|
|
102
|
-
room_id: z.string().min(1),
|
|
103
|
-
expected_turn_id: z.number().int().nonnegative(),
|
|
104
|
-
reason: z.string().min(1)
|
|
105
|
-
}
|
|
106
|
-
}, async (input, extra) => toolJson(() => commands.takeoverStick(resolveConnectionIdentity(extra.sessionId), input)));
|
|
107
|
-
server.registerTool("get_room_state", {
|
|
108
|
-
title: "Get Room State",
|
|
109
|
-
description: "Read the current projected room state and membership.",
|
|
110
|
-
inputSchema: {
|
|
111
|
-
room_id: z.string().min(1)
|
|
112
|
-
}
|
|
113
|
-
}, async (input, extra) => toolJson(() => commands.getRoomState({
|
|
114
|
-
...input,
|
|
115
|
-
agent_id: resolveConnectionIdentity(extra.sessionId).agent_id
|
|
116
|
-
})));
|
|
117
|
-
server.registerTool("get_room_events", {
|
|
118
|
-
title: "Get Room Events",
|
|
119
|
-
description: "Read the append-only event log for a room.",
|
|
120
|
-
inputSchema: {
|
|
121
|
-
room_id: z.string().min(1),
|
|
122
|
-
after_event_seq: z.number().int().nonnegative().optional(),
|
|
123
|
-
limit: z.number().int().positive().optional()
|
|
124
|
-
}
|
|
125
|
-
}, async (input, extra) => toolJson(() => commands.getRoomEvents({
|
|
126
|
-
...input,
|
|
127
|
-
agent_id: resolveConnectionIdentity(extra.sessionId).agent_id
|
|
128
|
-
})));
|
|
129
|
-
server.registerTool("send_message", {
|
|
130
|
-
title: "Send Message",
|
|
131
|
-
description: "Send a transient message into the room event log. Routes via to_agent_id; omit it for room broadcast.",
|
|
132
|
-
inputSchema: {
|
|
133
|
-
room_id: z.string().min(1),
|
|
134
|
-
body: z.string().min(1),
|
|
135
|
-
to_agent_id: z.string().min(1).optional(),
|
|
136
|
-
delivery_hint: z.enum(["normal", "interrupt"]).optional()
|
|
137
|
-
}
|
|
138
|
-
}, async (input, extra) => toolJson(() => commands.sendMessage(resolveConnectionIdentity(extra.sessionId), input)));
|
|
139
|
-
server.registerTool("wait_for_events", {
|
|
140
|
-
title: "Wait for Events",
|
|
141
|
-
description: "Long-poll the room event log past a cursor with optional event_type, target, and sender filters.",
|
|
142
|
-
inputSchema: {
|
|
143
|
-
room_id: z.string().min(1),
|
|
144
|
-
after_event_seq: z.number().int().nonnegative().optional(),
|
|
145
|
-
event_type: z
|
|
146
|
-
.union([z.string().min(1), z.array(z.string().min(1)).min(1)])
|
|
147
|
-
.optional(),
|
|
148
|
-
target_agent_id: z.string().min(1).optional(),
|
|
149
|
-
from_agent_id: z.string().min(1).optional(),
|
|
150
|
-
max_wait_ms: z.number().int().nonnegative().optional()
|
|
151
|
-
}
|
|
152
|
-
}, async (input, extra) => toolJson(() => commands.waitForEvents({
|
|
153
|
-
room_id: input.room_id,
|
|
154
|
-
after_event_seq: input.after_event_seq,
|
|
155
|
-
event_type: input.event_type,
|
|
156
|
-
target_agent_id: input.target_agent_id,
|
|
157
|
-
from_agent_id: input.from_agent_id,
|
|
158
|
-
max_wait_ms: input.max_wait_ms,
|
|
159
|
-
agent_id: resolveConnectionIdentity(extra.sessionId).agent_id
|
|
160
|
-
})));
|
|
161
|
-
server.registerTool("add_note", {
|
|
162
|
-
title: "Add Note",
|
|
163
|
-
description: "Leave an async note on a room. Any joined member can author; authoring refreshes presence.",
|
|
164
|
-
inputSchema: {
|
|
165
|
-
room_id: z.string().min(1),
|
|
166
|
-
body: z.string().min(1),
|
|
167
|
-
turn_id: z.number().int().nonnegative().optional()
|
|
168
|
-
}
|
|
169
|
-
}, async (input, extra) => toolJson(() => commands.addNote(resolveConnectionIdentity(extra.sessionId), input)));
|
|
170
|
-
server.registerTool("list_notes", {
|
|
171
|
-
title: "List Notes",
|
|
172
|
-
description: "List notes for a room. Default view hides resolved notes; pagination uses after_note_id.",
|
|
173
|
-
inputSchema: {
|
|
174
|
-
room_id: z.string().min(1),
|
|
175
|
-
after_note_id: z.string().min(1).optional(),
|
|
176
|
-
include_resolved: z.boolean().optional(),
|
|
177
|
-
limit: z.number().int().positive().max(200).optional()
|
|
178
|
-
}
|
|
179
|
-
}, async (input, extra) => toolJson(() => commands.listNotes(resolveConnectionIdentity(extra.sessionId), input)));
|
|
180
|
-
return server;
|
|
181
|
-
}
|
|
182
|
-
export function createConnectionIdentityResolver(options = {}) {
|
|
183
|
-
const inspector = options.inspector ?? createSystemProcessInspector({ cacheTtlMs: 60_000 });
|
|
184
|
-
const connectionOverrides = new Map();
|
|
185
|
-
const connectionIdentities = new Map();
|
|
186
|
-
return (sessionId, override) => {
|
|
187
|
-
const key = sessionId ?? "__stdio__";
|
|
188
|
-
if (override) {
|
|
189
|
-
connectionOverrides.set(key, override);
|
|
190
|
-
connectionIdentities.delete(key);
|
|
191
|
-
}
|
|
192
|
-
const cached = connectionIdentities.get(key);
|
|
193
|
-
if (cached) {
|
|
194
|
-
return cached;
|
|
195
|
-
}
|
|
196
|
-
const identity = deriveMcpHarnessIdentity({
|
|
197
|
-
sessionId,
|
|
198
|
-
agentId: connectionOverrides.get(key),
|
|
199
|
-
inspector
|
|
200
|
-
});
|
|
201
|
-
connectionIdentities.set(key, identity);
|
|
202
|
-
return identity;
|
|
203
|
-
};
|
|
204
|
-
}
|
|
205
|
-
export async function runStdioServer() {
|
|
206
|
-
const service = new TalkingStickService();
|
|
207
|
-
const server = createMcpServer(service);
|
|
208
|
-
process.on("exit", () => service.close());
|
|
209
|
-
await server.connect(new StdioServerTransport());
|
|
210
|
-
}
|
|
211
|
-
function ownerMutationSchema() {
|
|
212
|
-
return {
|
|
213
|
-
room_id: z.string().min(1),
|
|
214
|
-
lease_id: z.string().min(1),
|
|
215
|
-
expected_turn_id: z.number().int().nonnegative()
|
|
216
|
-
};
|
|
217
|
-
}
|
|
218
|
-
async function toolJson(fn) {
|
|
219
|
-
try {
|
|
220
|
-
const result = await fn();
|
|
221
|
-
return {
|
|
222
|
-
content: [
|
|
223
|
-
{
|
|
224
|
-
type: "text",
|
|
225
|
-
text: JSON.stringify(result, null, 2)
|
|
226
|
-
}
|
|
227
|
-
]
|
|
228
|
-
};
|
|
229
|
-
}
|
|
230
|
-
catch (error) {
|
|
231
|
-
if (isProtocolError(error)) {
|
|
232
|
-
return {
|
|
233
|
-
isError: true,
|
|
234
|
-
content: [
|
|
235
|
-
{
|
|
236
|
-
type: "text",
|
|
237
|
-
text: JSON.stringify(error.toJSON(), null, 2)
|
|
238
|
-
}
|
|
239
|
-
]
|
|
240
|
-
};
|
|
241
|
-
}
|
|
242
|
-
throw error;
|
|
243
|
-
}
|
|
244
|
-
}
|
package/dist/server.js
DELETED