agent-worker 0.10.0 → 0.12.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 +165 -343
- package/dist/backends-DG5igQii.mjs +3 -0
- package/dist/{backends-BOAkfYyL.mjs → backends-DLaP0rMW.mjs} +45 -30
- package/dist/cli/index.mjs +1647 -1550
- package/dist/context-BqEyt2SF.mjs +4 -0
- package/dist/{display-pretty-CWoRE9FY.mjs → display-pretty-BCJq5v9d.mjs} +3 -3
- package/dist/index.d.mts +42 -30
- package/dist/index.mjs +561 -5
- package/dist/{logger-C3ekEOzi.mjs → logger-Bfdo83xL.mjs} +2 -2
- package/dist/memory-provider-BtLYtdQH.mjs +70 -0
- package/dist/{workflow-BGpkJlFb.mjs → runner-CQJYnM7D.mjs} +38 -277
- package/dist/worker-CJ5_b2_q.mjs +446 -0
- package/dist/workflow-CNlUyGit.mjs +247 -0
- package/package.json +8 -1
- package/dist/backends-e6gCxRZ9.mjs +0 -3
- package/dist/context-dgI2YCGG.mjs +0 -4
- package/dist/mcp-server-BQCQxv2v.mjs +0 -573
- package/dist/skills-xNmQZf8e.mjs +0 -1002
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
import { C as SDK_MODEL_ALIASES, S as CURSOR_MODEL_MAP, _ as execWithIdleTimeout, a as createMockBackend, b as CLAUDE_MODEL_MAP, c as CodexBackend, d as codexAdapter, f as createStreamParser, g as IdleTimeoutError, h as formatEvent, i as MockAIBackend, l as ClaudeCodeBackend, m as extractCodexResult, n as createBackend, o as SdkBackend, p as extractClaudeResult, r as listBackends, s as CursorBackend, t as checkBackends, u as claudeAdapter, v as DEFAULT_IDLE_TIMEOUT, w as getModelForBackend, x as CODEX_MODEL_MAP, y as BACKEND_DEFAULT_MODELS } from "./backends-BOAkfYyL.mjs";
|
|
2
|
-
|
|
3
|
-
export { listBackends };
|
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
import { _ as shouldUseResource, a as FileStorage, c as CONTEXT_DEFAULTS, d as RESOURCE_PREFIX, f as RESOURCE_SCHEME, g as generateResourceId, h as extractMentions, i as resolveContextDir, l as MENTION_PATTERN, m as createResourceRef, n as createFileContextProvider, o as MemoryStorage, p as calculatePriority, r as getDefaultContextDir, s as ContextProviderImpl, t as FileContextProvider, u as MESSAGE_LENGTH_THRESHOLD } from "./cli/index.mjs";
|
|
2
|
-
import { a as createMemoryContextProvider, i as MemoryContextProvider, n as formatProposal, r as formatProposalList, t as createContextMCPServer } from "./mcp-server-BQCQxv2v.mjs";
|
|
3
|
-
|
|
4
|
-
export { createFileContextProvider };
|
|
@@ -1,573 +0,0 @@
|
|
|
1
|
-
import { o as MemoryStorage, s as ContextProviderImpl } from "./cli/index.mjs";
|
|
2
|
-
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
3
|
-
import { dirname, join } from "node:path";
|
|
4
|
-
import { z } from "zod";
|
|
5
|
-
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
6
|
-
|
|
7
|
-
//#region src/workflow/context/memory-provider.ts
|
|
8
|
-
/**
|
|
9
|
-
* In-memory ContextProvider for testing.
|
|
10
|
-
* All domain logic is inherited from ContextProviderImpl;
|
|
11
|
-
* this class adds test helpers for inspection and cleanup.
|
|
12
|
-
*/
|
|
13
|
-
var MemoryContextProvider = class extends ContextProviderImpl {
|
|
14
|
-
memoryStorage;
|
|
15
|
-
constructor(validAgents) {
|
|
16
|
-
const storage = new MemoryStorage();
|
|
17
|
-
super(storage, validAgents);
|
|
18
|
-
this.memoryStorage = storage;
|
|
19
|
-
}
|
|
20
|
-
/** Get underlying MemoryStorage (for testing) */
|
|
21
|
-
getStorage() {
|
|
22
|
-
return this.memoryStorage;
|
|
23
|
-
}
|
|
24
|
-
/** Get all channel messages (for testing, unfiltered) */
|
|
25
|
-
async getMessages() {
|
|
26
|
-
return this.readChannel();
|
|
27
|
-
}
|
|
28
|
-
/** Clear all data (for testing) */
|
|
29
|
-
clear() {
|
|
30
|
-
this.memoryStorage.clear();
|
|
31
|
-
}
|
|
32
|
-
/** Get all resources (for testing) */
|
|
33
|
-
async getResources() {
|
|
34
|
-
const keys = await this.memoryStorage.list("resources/");
|
|
35
|
-
const map = /* @__PURE__ */ new Map();
|
|
36
|
-
for (const key of keys) {
|
|
37
|
-
const content = await this.memoryStorage.read(`resources/${key}`);
|
|
38
|
-
if (content !== null) {
|
|
39
|
-
const id = key.replace(/\.[^.]+$/, "");
|
|
40
|
-
map.set(id, content);
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
return map;
|
|
44
|
-
}
|
|
45
|
-
/** Get inbox state for an agent (for testing) */
|
|
46
|
-
async getInboxState(agent) {
|
|
47
|
-
const raw = await this.memoryStorage.read("_state/inbox.json");
|
|
48
|
-
if (!raw) return void 0;
|
|
49
|
-
try {
|
|
50
|
-
return JSON.parse(raw).readCursors?.[agent];
|
|
51
|
-
} catch {
|
|
52
|
-
return;
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
/** Get all documents (for testing) */
|
|
56
|
-
async getDocuments() {
|
|
57
|
-
const files = await this.memoryStorage.list("documents/");
|
|
58
|
-
const map = /* @__PURE__ */ new Map();
|
|
59
|
-
for (const file of files) {
|
|
60
|
-
const content = await this.memoryStorage.read(`documents/${file}`);
|
|
61
|
-
if (content !== null) map.set(file, content);
|
|
62
|
-
}
|
|
63
|
-
return map;
|
|
64
|
-
}
|
|
65
|
-
};
|
|
66
|
-
/**
|
|
67
|
-
* Create a memory context provider
|
|
68
|
-
*/
|
|
69
|
-
function createMemoryContextProvider(validAgents) {
|
|
70
|
-
return new MemoryContextProvider(validAgents);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
//#endregion
|
|
74
|
-
//#region src/workflow/context/proposals.ts
|
|
75
|
-
/**
|
|
76
|
-
* Format a proposal for display
|
|
77
|
-
*/
|
|
78
|
-
function formatProposal(proposal) {
|
|
79
|
-
const lines = [];
|
|
80
|
-
lines.push(`📋 **${proposal.title}** (${proposal.id})`);
|
|
81
|
-
lines.push(`Type: ${proposal.type} | Status: ${proposal.status}`);
|
|
82
|
-
if (proposal.description) lines.push(`\n${proposal.description}`);
|
|
83
|
-
lines.push("\nOptions:");
|
|
84
|
-
for (const option of proposal.options) {
|
|
85
|
-
const count = proposal.result?.counts[option.id] || 0;
|
|
86
|
-
const marker = proposal.result?.winner === option.id ? "✓ " : " ";
|
|
87
|
-
lines.push(`${marker}- ${option.label} (${option.id}): ${count} votes`);
|
|
88
|
-
}
|
|
89
|
-
if (proposal.result && Object.keys(proposal.result.votes).length > 0) {
|
|
90
|
-
lines.push("\nVotes:");
|
|
91
|
-
for (const [voter, choice] of Object.entries(proposal.result.votes)) lines.push(` @${voter} → ${choice}`);
|
|
92
|
-
}
|
|
93
|
-
if (proposal.status === "active" && proposal.expiresAt) {
|
|
94
|
-
const remaining = new Date(proposal.expiresAt).getTime() - Date.now();
|
|
95
|
-
const minutes = Math.max(0, Math.floor(remaining / 6e4));
|
|
96
|
-
lines.push(`\nExpires in: ${minutes} minutes`);
|
|
97
|
-
}
|
|
98
|
-
if (proposal.status === "resolved" && proposal.result?.winner) {
|
|
99
|
-
const winningOption = proposal.options.find((o) => o.id === proposal.result?.winner);
|
|
100
|
-
lines.push(`\n🏆 Winner: ${winningOption?.label || proposal.result.winner}`);
|
|
101
|
-
}
|
|
102
|
-
return lines.join("\n");
|
|
103
|
-
}
|
|
104
|
-
/**
|
|
105
|
-
* Format multiple proposals as a summary
|
|
106
|
-
*/
|
|
107
|
-
function formatProposalList(proposals) {
|
|
108
|
-
if (proposals.length === 0) return "(no proposals)";
|
|
109
|
-
return proposals.map((p) => {
|
|
110
|
-
const votes = Object.keys(p.result?.votes || {}).length;
|
|
111
|
-
return `- ${p.id}: ${p.title} [${p.status}] (${votes} votes)`;
|
|
112
|
-
}).join("\n");
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
//#endregion
|
|
116
|
-
//#region src/workflow/context/mcp-server.ts
|
|
117
|
-
/**
|
|
118
|
-
* Context MCP Server
|
|
119
|
-
* Provides context tools to agents via Model Context Protocol
|
|
120
|
-
*
|
|
121
|
-
* Tool Taxonomy:
|
|
122
|
-
* - Channel: channel_send, channel_read (public append-only log)
|
|
123
|
-
* - Team: team_members, team_doc_*, team_proposal_*, team_vote (shared workspace)
|
|
124
|
-
* - My: my_inbox, my_inbox_ack, my_status_set (personal agent tools)
|
|
125
|
-
* - Resource: resource_create, resource_read (general-purpose reference mechanism)
|
|
126
|
-
* - Feedback: feedback_submit (agent observations about tools/workflows, opt-in)
|
|
127
|
-
*/
|
|
128
|
-
/**
|
|
129
|
-
* Format inbox messages for display
|
|
130
|
-
*/
|
|
131
|
-
function formatInbox(messages) {
|
|
132
|
-
if (messages.length === 0) return JSON.stringify({
|
|
133
|
-
messages: [],
|
|
134
|
-
count: 0
|
|
135
|
-
});
|
|
136
|
-
return JSON.stringify({
|
|
137
|
-
messages: messages.map((m) => ({
|
|
138
|
-
id: m.entry.id,
|
|
139
|
-
from: m.entry.from,
|
|
140
|
-
content: m.entry.content,
|
|
141
|
-
timestamp: m.entry.timestamp,
|
|
142
|
-
priority: m.priority
|
|
143
|
-
})),
|
|
144
|
-
count: messages.length
|
|
145
|
-
});
|
|
146
|
-
}
|
|
147
|
-
/**
|
|
148
|
-
* Create an MCP server that exposes context tools
|
|
149
|
-
*
|
|
150
|
-
* Tool taxonomy:
|
|
151
|
-
* - Channel: channel_send, channel_read
|
|
152
|
-
* - Team: team_members, team_doc_read, team_doc_write, team_doc_append,
|
|
153
|
-
* team_doc_list, team_doc_create, team_proposal_create, team_vote,
|
|
154
|
-
* team_proposal_status, team_proposal_cancel
|
|
155
|
-
* - My: my_inbox, my_inbox_ack, my_status_set
|
|
156
|
-
* - Resource: resource_create, resource_read
|
|
157
|
-
*/
|
|
158
|
-
function createContextMCPServer(options) {
|
|
159
|
-
const { provider, validAgents, name = "workflow-context", version = "1.0.0", onMention, proposalManager, feedback: feedbackEnabled, debugLog } = options;
|
|
160
|
-
const feedbackEntries = [];
|
|
161
|
-
const logTool = (tool, agent, params) => {
|
|
162
|
-
if (!agent) return;
|
|
163
|
-
const paramsStr = Object.entries(params).filter(([_, v]) => v !== void 0).map(([k, v]) => {
|
|
164
|
-
const val = typeof v === "string" && v.length > 50 ? v.slice(0, 50) + "..." : v;
|
|
165
|
-
return `${k}=${JSON.stringify(val)}`;
|
|
166
|
-
}).join(", ");
|
|
167
|
-
provider.appendChannel(agent, `${tool}(${paramsStr})`, {
|
|
168
|
-
kind: "tool_call",
|
|
169
|
-
toolCall: {
|
|
170
|
-
name: tool,
|
|
171
|
-
args: paramsStr
|
|
172
|
-
}
|
|
173
|
-
}).catch(() => {});
|
|
174
|
-
};
|
|
175
|
-
const server = new McpServer({
|
|
176
|
-
name,
|
|
177
|
-
version
|
|
178
|
-
});
|
|
179
|
-
const agentConnections = /* @__PURE__ */ new Map();
|
|
180
|
-
server.tool("channel_send", `Send a message to the shared channel. Use @agent to mention/notify. Use "to" for private DMs. Long messages (> 2000 chars) are automatically converted to resources.`, {
|
|
181
|
-
message: z.string().describe("Message content, can include @mentions like @reviewer or @coder. Long messages are auto-converted to resources."),
|
|
182
|
-
to: z.string().optional().describe("Send as DM to a specific agent (private, only you and recipient see it)")
|
|
183
|
-
}, async ({ message, to }, extra) => {
|
|
184
|
-
const from = getAgentId(extra) || "anonymous";
|
|
185
|
-
logTool("channel_send", from, {
|
|
186
|
-
message,
|
|
187
|
-
to
|
|
188
|
-
});
|
|
189
|
-
const sendOpts = to ? { to } : void 0;
|
|
190
|
-
const msg = await provider.smartSend(from, message, sendOpts);
|
|
191
|
-
for (const target of msg.mentions) if (onMention) onMention(from, target, msg);
|
|
192
|
-
if (to && !msg.mentions.includes(to) && onMention) onMention(from, to, msg);
|
|
193
|
-
return { content: [{
|
|
194
|
-
type: "text",
|
|
195
|
-
text: JSON.stringify({
|
|
196
|
-
status: "sent",
|
|
197
|
-
timestamp: msg.timestamp,
|
|
198
|
-
mentions: msg.mentions,
|
|
199
|
-
to: msg.to
|
|
200
|
-
})
|
|
201
|
-
}] };
|
|
202
|
-
});
|
|
203
|
-
server.tool("channel_read", "Read messages from the shared channel. DMs and logs are automatically filtered based on your identity.", {
|
|
204
|
-
since: z.string().optional().describe("Read entries after this timestamp (ISO format)"),
|
|
205
|
-
limit: z.number().optional().describe("Maximum entries to return")
|
|
206
|
-
}, async ({ since, limit }, extra) => {
|
|
207
|
-
const agent = getAgentId(extra);
|
|
208
|
-
logTool("channel_read", agent, {
|
|
209
|
-
since,
|
|
210
|
-
limit
|
|
211
|
-
});
|
|
212
|
-
const entries = await provider.readChannel({
|
|
213
|
-
since,
|
|
214
|
-
limit,
|
|
215
|
-
agent
|
|
216
|
-
});
|
|
217
|
-
return { content: [{
|
|
218
|
-
type: "text",
|
|
219
|
-
text: JSON.stringify(entries)
|
|
220
|
-
}] };
|
|
221
|
-
});
|
|
222
|
-
server.tool("resource_create", "Store large content as a resource. Returns a reference (resource:id) usable in channel messages or documents.", {
|
|
223
|
-
content: z.string().describe("Content to store as resource"),
|
|
224
|
-
type: z.enum([
|
|
225
|
-
"markdown",
|
|
226
|
-
"json",
|
|
227
|
-
"text",
|
|
228
|
-
"diff"
|
|
229
|
-
]).optional().describe("Content type hint (default: text)")
|
|
230
|
-
}, async ({ content, type }, extra) => {
|
|
231
|
-
const createdBy = getAgentId(extra) || "anonymous";
|
|
232
|
-
logTool("resource_create", createdBy, {
|
|
233
|
-
type,
|
|
234
|
-
contentLen: content.length
|
|
235
|
-
});
|
|
236
|
-
const result = await provider.createResource(content, createdBy, type);
|
|
237
|
-
return { content: [{
|
|
238
|
-
type: "text",
|
|
239
|
-
text: JSON.stringify({
|
|
240
|
-
id: result.id,
|
|
241
|
-
ref: result.ref,
|
|
242
|
-
hint: `Use [description](${result.ref}) in messages or documents`
|
|
243
|
-
})
|
|
244
|
-
}] };
|
|
245
|
-
});
|
|
246
|
-
server.tool("resource_read", "Read resource content by ID. Use when you encounter resource:id references.", { id: z.string().describe("Resource ID (e.g., res_abc123)") }, async ({ id }) => {
|
|
247
|
-
const content = await provider.readResource(id);
|
|
248
|
-
if (content === null) return { content: [{
|
|
249
|
-
type: "text",
|
|
250
|
-
text: JSON.stringify({ error: `Resource not found: ${id}` })
|
|
251
|
-
}] };
|
|
252
|
-
return { content: [{
|
|
253
|
-
type: "text",
|
|
254
|
-
text: content
|
|
255
|
-
}] };
|
|
256
|
-
});
|
|
257
|
-
server.tool("my_inbox", "Check your unread inbox messages. Does NOT acknowledge — use my_inbox_ack after processing.", {}, async (_args, extra) => {
|
|
258
|
-
const agent = getAgentId(extra) || "anonymous";
|
|
259
|
-
logTool("my_inbox", agent, {});
|
|
260
|
-
const messages = await provider.getInbox(agent);
|
|
261
|
-
if (debugLog && messages.length > 0) debugLog(`[mcp:${agent}] my_inbox → ${messages.length} unread`);
|
|
262
|
-
return { content: [{
|
|
263
|
-
type: "text",
|
|
264
|
-
text: formatInbox(messages)
|
|
265
|
-
}] };
|
|
266
|
-
});
|
|
267
|
-
server.tool("my_inbox_ack", "Acknowledge inbox messages up to a message ID. Call after processing messages.", { until: z.string().describe("Acknowledge messages up to and including this message ID") }, async ({ until }, extra) => {
|
|
268
|
-
const agent = getAgentId(extra) || "anonymous";
|
|
269
|
-
logTool("my_inbox_ack", agent, { until });
|
|
270
|
-
await provider.ackInbox(agent, until);
|
|
271
|
-
return { content: [{
|
|
272
|
-
type: "text",
|
|
273
|
-
text: JSON.stringify({
|
|
274
|
-
status: "acknowledged",
|
|
275
|
-
until
|
|
276
|
-
})
|
|
277
|
-
}] };
|
|
278
|
-
});
|
|
279
|
-
server.tool("my_status_set", "Update your status and current task. Call when starting or completing work.", {
|
|
280
|
-
task: z.string().optional().describe("Current task description (what you're working on)"),
|
|
281
|
-
state: z.enum(["idle", "running"]).optional().describe("Agent state (running = working, idle = available)"),
|
|
282
|
-
metadata: z.record(z.unknown()).optional().describe("Additional metadata (e.g., PR number, file path)")
|
|
283
|
-
}, async (args, extra) => {
|
|
284
|
-
const agent = getAgentId(extra) || "anonymous";
|
|
285
|
-
logTool("my_status_set", agent, args);
|
|
286
|
-
const status = {};
|
|
287
|
-
if (args.task !== void 0) status.task = args.task;
|
|
288
|
-
if (args.state !== void 0) status.state = args.state;
|
|
289
|
-
if (args.metadata !== void 0) status.metadata = args.metadata;
|
|
290
|
-
await provider.setAgentStatus(agent, status);
|
|
291
|
-
return { content: [{
|
|
292
|
-
type: "text",
|
|
293
|
-
text: JSON.stringify({
|
|
294
|
-
status: "updated",
|
|
295
|
-
agent,
|
|
296
|
-
...status
|
|
297
|
-
})
|
|
298
|
-
}] };
|
|
299
|
-
});
|
|
300
|
-
server.tool("team_members", "List all agents in this workflow. Use to discover who you can @mention. Optionally includes agent status (state, current task).", { includeStatus: z.boolean().optional().describe("Include agent status information") }, async (args, extra) => {
|
|
301
|
-
const currentAgent = getAgentId(extra) || "anonymous";
|
|
302
|
-
const includeStatus = args.includeStatus ?? false;
|
|
303
|
-
const agents = validAgents.map((name) => ({
|
|
304
|
-
name,
|
|
305
|
-
mention: `@${name}`,
|
|
306
|
-
isYou: name === currentAgent
|
|
307
|
-
}));
|
|
308
|
-
const result = {
|
|
309
|
-
agents,
|
|
310
|
-
count: agents.length,
|
|
311
|
-
hint: "Use @agent in channel_send to mention other agents"
|
|
312
|
-
};
|
|
313
|
-
if (includeStatus) result.status = await provider.listAgentStatus();
|
|
314
|
-
return { content: [{
|
|
315
|
-
type: "text",
|
|
316
|
-
text: JSON.stringify(result)
|
|
317
|
-
}] };
|
|
318
|
-
});
|
|
319
|
-
server.tool("team_doc_read", "Read a shared team document.", { file: z.string().optional().describe("Document file path (default: notes.md)") }, async ({ file }, extra) => {
|
|
320
|
-
logTool("team_doc_read", getAgentId(extra), { file });
|
|
321
|
-
return { content: [{
|
|
322
|
-
type: "text",
|
|
323
|
-
text: await provider.readDocument(file) || "(empty document)"
|
|
324
|
-
}] };
|
|
325
|
-
});
|
|
326
|
-
server.tool("team_doc_write", "Write/replace a shared team document.", {
|
|
327
|
-
content: z.string().describe("New document content (replaces existing)"),
|
|
328
|
-
file: z.string().optional().describe("Document file path (default: notes.md)")
|
|
329
|
-
}, async ({ content, file }, extra) => {
|
|
330
|
-
logTool("team_doc_write", getAgentId(extra), {
|
|
331
|
-
file,
|
|
332
|
-
contentLen: content.length
|
|
333
|
-
});
|
|
334
|
-
await provider.writeDocument(content, file);
|
|
335
|
-
return { content: [{
|
|
336
|
-
type: "text",
|
|
337
|
-
text: `Document ${file || "notes.md"} written successfully`
|
|
338
|
-
}] };
|
|
339
|
-
});
|
|
340
|
-
server.tool("team_doc_append", "Append content to a shared team document.", {
|
|
341
|
-
content: z.string().describe("Content to append to the document"),
|
|
342
|
-
file: z.string().optional().describe("Document file path (default: notes.md)")
|
|
343
|
-
}, async ({ content, file }, extra) => {
|
|
344
|
-
logTool("team_doc_append", getAgentId(extra), {
|
|
345
|
-
file,
|
|
346
|
-
contentLen: content.length
|
|
347
|
-
});
|
|
348
|
-
await provider.appendDocument(content, file);
|
|
349
|
-
return { content: [{
|
|
350
|
-
type: "text",
|
|
351
|
-
text: `Content appended to ${file || "notes.md"}`
|
|
352
|
-
}] };
|
|
353
|
-
});
|
|
354
|
-
server.tool("team_doc_list", "List all shared team document files.", {}, async () => {
|
|
355
|
-
const files = await provider.listDocuments();
|
|
356
|
-
return { content: [{
|
|
357
|
-
type: "text",
|
|
358
|
-
text: JSON.stringify({
|
|
359
|
-
files,
|
|
360
|
-
count: files.length
|
|
361
|
-
})
|
|
362
|
-
}] };
|
|
363
|
-
});
|
|
364
|
-
server.tool("team_doc_create", "Create a new shared team document file.", {
|
|
365
|
-
file: z.string().describe("Document file path (e.g., \"findings/auth.md\")"),
|
|
366
|
-
content: z.string().describe("Initial document content")
|
|
367
|
-
}, async ({ file, content }) => {
|
|
368
|
-
await provider.createDocument(file, content);
|
|
369
|
-
return { content: [{
|
|
370
|
-
type: "text",
|
|
371
|
-
text: `Document ${file} created successfully`
|
|
372
|
-
}] };
|
|
373
|
-
});
|
|
374
|
-
if (proposalManager) {
|
|
375
|
-
server.tool("team_proposal_create", "Create a new proposal for team voting. Use for decisions, elections, approvals, or assignments.", {
|
|
376
|
-
type: z.enum([
|
|
377
|
-
"election",
|
|
378
|
-
"decision",
|
|
379
|
-
"approval",
|
|
380
|
-
"assignment"
|
|
381
|
-
]).describe("Type of proposal"),
|
|
382
|
-
title: z.string().describe("Brief title for the proposal"),
|
|
383
|
-
description: z.string().optional().describe("Detailed description"),
|
|
384
|
-
options: z.array(z.object({
|
|
385
|
-
id: z.string().describe("Unique option identifier"),
|
|
386
|
-
label: z.string().describe("Display label for the option")
|
|
387
|
-
})).optional().describe("Voting options (required except for approval type)"),
|
|
388
|
-
resolution: z.object({
|
|
389
|
-
type: z.enum([
|
|
390
|
-
"plurality",
|
|
391
|
-
"majority",
|
|
392
|
-
"unanimous"
|
|
393
|
-
]).optional().describe("How to determine winner"),
|
|
394
|
-
quorum: z.number().optional().describe("Minimum votes required"),
|
|
395
|
-
tieBreaker: z.enum([
|
|
396
|
-
"first",
|
|
397
|
-
"random",
|
|
398
|
-
"creator-decides"
|
|
399
|
-
]).optional().describe("How to break ties")
|
|
400
|
-
}).optional().describe("Resolution rules"),
|
|
401
|
-
binding: z.boolean().optional().describe("Whether result is binding (default: true)"),
|
|
402
|
-
timeoutSeconds: z.number().optional().describe("Timeout in seconds (default: 3600)")
|
|
403
|
-
}, async (params, extra) => {
|
|
404
|
-
const createdBy = getAgentId(extra) || "anonymous";
|
|
405
|
-
try {
|
|
406
|
-
const proposal = proposalManager.create({
|
|
407
|
-
type: params.type,
|
|
408
|
-
title: params.title,
|
|
409
|
-
description: params.description,
|
|
410
|
-
options: params.options,
|
|
411
|
-
resolution: params.resolution,
|
|
412
|
-
binding: params.binding,
|
|
413
|
-
timeoutSeconds: params.timeoutSeconds,
|
|
414
|
-
createdBy
|
|
415
|
-
});
|
|
416
|
-
const optionsList = proposal.options.map((o) => `${o.id}: ${o.label}`).join(", ");
|
|
417
|
-
const otherAgents = validAgents.filter((a) => a !== createdBy).map((a) => `@${a}`).join(" ");
|
|
418
|
-
await provider.appendChannel(createdBy, `Created proposal "${proposal.title}" (${proposal.id})\nOptions: ${optionsList}\nUse team_vote tool to cast your vote. ${otherAgents}`);
|
|
419
|
-
return { content: [{
|
|
420
|
-
type: "text",
|
|
421
|
-
text: JSON.stringify({
|
|
422
|
-
status: "created",
|
|
423
|
-
proposal: {
|
|
424
|
-
id: proposal.id,
|
|
425
|
-
title: proposal.title,
|
|
426
|
-
options: proposal.options,
|
|
427
|
-
expiresAt: proposal.expiresAt
|
|
428
|
-
}
|
|
429
|
-
})
|
|
430
|
-
}] };
|
|
431
|
-
} catch (error) {
|
|
432
|
-
return { content: [{
|
|
433
|
-
type: "text",
|
|
434
|
-
text: JSON.stringify({
|
|
435
|
-
status: "error",
|
|
436
|
-
error: error instanceof Error ? error.message : String(error)
|
|
437
|
-
})
|
|
438
|
-
}] };
|
|
439
|
-
}
|
|
440
|
-
});
|
|
441
|
-
server.tool("team_vote", "Cast your vote on a team proposal.", {
|
|
442
|
-
proposal: z.string().describe("Proposal ID (e.g., prop-1)"),
|
|
443
|
-
choice: z.string().describe("Option ID to vote for"),
|
|
444
|
-
reason: z.string().optional().describe("Optional reason for your vote")
|
|
445
|
-
}, async ({ proposal: proposalId, choice, reason }, extra) => {
|
|
446
|
-
const voter = getAgentId(extra) || "anonymous";
|
|
447
|
-
const result = proposalManager.vote({
|
|
448
|
-
proposalId,
|
|
449
|
-
voter,
|
|
450
|
-
choice,
|
|
451
|
-
reason
|
|
452
|
-
});
|
|
453
|
-
if (!result.success) return { content: [{
|
|
454
|
-
type: "text",
|
|
455
|
-
text: JSON.stringify({
|
|
456
|
-
status: "error",
|
|
457
|
-
error: result.error
|
|
458
|
-
})
|
|
459
|
-
}] };
|
|
460
|
-
const reasonText = reason ? ` (reason: ${reason})` : "";
|
|
461
|
-
await provider.appendChannel(voter, `Voted "${choice}" on ${proposalId}${reasonText}`);
|
|
462
|
-
if (result.resolved && result.proposal) {
|
|
463
|
-
const winnerOption = result.proposal.options.find((o) => o.id === result.proposal.result?.winner);
|
|
464
|
-
const mentions = Object.keys(result.proposal.result?.votes || {}).map((v) => `@${v}`).join(" ");
|
|
465
|
-
await provider.appendChannel("system", `Proposal ${proposalId} resolved! Winner: ${winnerOption?.label || result.proposal.result?.winner || "none"} ${mentions}`);
|
|
466
|
-
}
|
|
467
|
-
return { content: [{
|
|
468
|
-
type: "text",
|
|
469
|
-
text: JSON.stringify({
|
|
470
|
-
status: "voted",
|
|
471
|
-
proposal: proposalId,
|
|
472
|
-
choice,
|
|
473
|
-
resolved: result.resolved,
|
|
474
|
-
winner: result.proposal?.result?.winner
|
|
475
|
-
})
|
|
476
|
-
}] };
|
|
477
|
-
});
|
|
478
|
-
server.tool("team_proposal_status", "Check status of team proposals. Omit proposal ID to see all active proposals.", { proposal: z.string().optional().describe("Proposal ID (omit for all active)") }, async ({ proposal: proposalId }) => {
|
|
479
|
-
if (proposalId) {
|
|
480
|
-
const proposal = proposalManager.get(proposalId);
|
|
481
|
-
if (!proposal) return { content: [{
|
|
482
|
-
type: "text",
|
|
483
|
-
text: JSON.stringify({
|
|
484
|
-
status: "error",
|
|
485
|
-
error: `Proposal not found: ${proposalId}`
|
|
486
|
-
})
|
|
487
|
-
}] };
|
|
488
|
-
return { content: [{
|
|
489
|
-
type: "text",
|
|
490
|
-
text: formatProposal(proposal)
|
|
491
|
-
}] };
|
|
492
|
-
}
|
|
493
|
-
const activeProposals = proposalManager.list("active");
|
|
494
|
-
return { content: [{
|
|
495
|
-
type: "text",
|
|
496
|
-
text: activeProposals.length > 0 ? formatProposalList(activeProposals) : "(no active proposals)"
|
|
497
|
-
}] };
|
|
498
|
-
});
|
|
499
|
-
server.tool("team_proposal_cancel", "Cancel a proposal you created.", { proposal: z.string().describe("Proposal ID to cancel") }, async ({ proposal: proposalId }, extra) => {
|
|
500
|
-
const cancelledBy = getAgentId(extra) || "anonymous";
|
|
501
|
-
const result = proposalManager.cancel(proposalId, cancelledBy);
|
|
502
|
-
if (!result.success) return { content: [{
|
|
503
|
-
type: "text",
|
|
504
|
-
text: JSON.stringify({
|
|
505
|
-
status: "error",
|
|
506
|
-
error: result.error
|
|
507
|
-
})
|
|
508
|
-
}] };
|
|
509
|
-
await provider.appendChannel(cancelledBy, `Cancelled proposal ${proposalId}`);
|
|
510
|
-
return { content: [{
|
|
511
|
-
type: "text",
|
|
512
|
-
text: JSON.stringify({
|
|
513
|
-
status: "cancelled",
|
|
514
|
-
proposal: proposalId
|
|
515
|
-
})
|
|
516
|
-
}] };
|
|
517
|
-
});
|
|
518
|
-
}
|
|
519
|
-
if (feedbackEnabled) server.tool("feedback_submit", "Report a workflow improvement need. Use when you hit something inconvenient — a missing tool, an awkward step, or a capability you wished you had.", {
|
|
520
|
-
target: z.string().describe("The area this is about — a tool name, a workflow step, or a general area (e.g. file search, code review)."),
|
|
521
|
-
type: z.enum([
|
|
522
|
-
"missing",
|
|
523
|
-
"friction",
|
|
524
|
-
"suggestion"
|
|
525
|
-
]).describe("missing: a tool or capability you needed but didn't have. friction: something that works but is awkward or slow. suggestion: a concrete improvement idea."),
|
|
526
|
-
description: z.string().describe("What you needed or what could be improved. Be specific."),
|
|
527
|
-
context: z.string().optional().describe("Optional: what you were trying to do when you hit this.")
|
|
528
|
-
}, async ({ target, type, description, context: ctx }, extra) => {
|
|
529
|
-
logTool("feedback_submit", getAgentId(extra) || "anonymous", {
|
|
530
|
-
target,
|
|
531
|
-
type
|
|
532
|
-
});
|
|
533
|
-
const entry = {
|
|
534
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
535
|
-
target,
|
|
536
|
-
type,
|
|
537
|
-
description,
|
|
538
|
-
...ctx ? { context: ctx } : {}
|
|
539
|
-
};
|
|
540
|
-
if (feedbackEntries.length >= 50) feedbackEntries.shift();
|
|
541
|
-
feedbackEntries.push(entry);
|
|
542
|
-
return { content: [{
|
|
543
|
-
type: "text",
|
|
544
|
-
text: JSON.stringify({ status: "recorded" })
|
|
545
|
-
}] };
|
|
546
|
-
});
|
|
547
|
-
return {
|
|
548
|
-
server,
|
|
549
|
-
agentConnections,
|
|
550
|
-
validAgents,
|
|
551
|
-
proposalManager,
|
|
552
|
-
getFeedback: () => [...feedbackEntries]
|
|
553
|
-
};
|
|
554
|
-
}
|
|
555
|
-
/**
|
|
556
|
-
* Extract agent ID from MCP extra context
|
|
557
|
-
* The agent ID is set via X-Agent-Id header or session metadata
|
|
558
|
-
*/
|
|
559
|
-
function getAgentId(extra) {
|
|
560
|
-
if (!extra || typeof extra !== "object") return void 0;
|
|
561
|
-
if ("sessionId" in extra && typeof extra.sessionId === "string") {
|
|
562
|
-
const sid = extra.sessionId;
|
|
563
|
-
const match = sid.match(/^(.+)-[0-9a-f]{8}$/);
|
|
564
|
-
return match ? match[1] : sid;
|
|
565
|
-
}
|
|
566
|
-
if ("meta" in extra && extra.meta && typeof extra.meta === "object") {
|
|
567
|
-
const meta = extra.meta;
|
|
568
|
-
if ("agentId" in meta && typeof meta.agentId === "string") return meta.agentId;
|
|
569
|
-
}
|
|
570
|
-
}
|
|
571
|
-
|
|
572
|
-
//#endregion
|
|
573
|
-
export { createMemoryContextProvider as a, MemoryContextProvider as i, formatProposal as n, formatProposalList as r, createContextMCPServer as t };
|