@slock-ai/daemon 0.31.2 → 0.31.3-alpha.7

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.
@@ -40,6 +40,19 @@ function formatTarget(m) {
40
40
  }
41
41
  return `#${m.channel_name}`;
42
42
  }
43
+ function formatSearchTarget(result) {
44
+ if (result.channelType === "thread") {
45
+ const shortId = typeof result.channelName === "string" && result.channelName.startsWith("thread-") ? result.channelName.slice(7) : typeof result.threadId === "string" && result.threadId ? result.threadId.slice(0, 8) : result.channelName;
46
+ if (result.parentChannelType === "dm") {
47
+ return `dm:@${result.parentChannelName}:${shortId}`;
48
+ }
49
+ return `#${result.parentChannelName}:${shortId}`;
50
+ }
51
+ if (result.channelType === "dm") {
52
+ return `dm:@${result.channelName}`;
53
+ }
54
+ return `#${result.channelName}`;
55
+ }
43
56
  var server = new McpServer({
44
57
  name: "chat",
45
58
  version: "1.0.0"
@@ -71,11 +84,18 @@ server.tool(
71
84
  }
72
85
  const shortId = data.messageId ? data.messageId.slice(0, 8) : null;
73
86
  const replyHint = shortId ? ` (to reply in this message's thread, use target "${target.includes(":") ? target : target + ":" + shortId}")` : "";
87
+ let unreadSection = "";
88
+ if (data.recentUnread && data.recentUnread.length > 0) {
89
+ unreadSection = `
90
+
91
+ --- New messages you may have missed ---
92
+ ${formatMessages(data.recentUnread)}`;
93
+ }
74
94
  return {
75
95
  content: [
76
96
  {
77
97
  type: "text",
78
- text: `Message sent to ${target}. Message ID: ${data.messageId}${replyHint}`
98
+ text: `Message sent to ${target}. Message ID: ${data.messageId}${replyHint}${unreadSection}`
79
99
  }
80
100
  ]
81
101
  };
@@ -331,20 +351,86 @@ server.tool(
331
351
  }
332
352
  }
333
353
  );
354
+ server.tool(
355
+ "search_messages",
356
+ "Search messages visible to the agent. Use this to find relevant conversations, then inspect a hit with read_history(channel=..., around=messageId).",
357
+ {
358
+ query: z.string().describe("Search query"),
359
+ channel: z.string().optional().describe("Optional target to scope the search, e.g. '#general', 'dm:@richard', '#general:abcd1234'"),
360
+ limit: z.number().default(10).describe("Max number of search results to return (default 10, max 20)")
361
+ },
362
+ async ({ query, channel, limit }) => {
363
+ try {
364
+ const trimmed = query.trim();
365
+ if (!trimmed) {
366
+ return {
367
+ content: [{ type: "text", text: "Search query cannot be empty." }]
368
+ };
369
+ }
370
+ const params = new URLSearchParams();
371
+ params.set("q", trimmed);
372
+ params.set("limit", String(Math.min(limit, 20)));
373
+ if (channel) params.set("channel", channel);
374
+ const res = await fetch(
375
+ `${serverUrl}/internal/agent/${agentId}/search?${params}`,
376
+ { method: "GET", headers: commonHeaders }
377
+ );
378
+ const data = await res.json();
379
+ if (!res.ok) {
380
+ return {
381
+ content: [{ type: "text", text: `Error: ${data.error}` }]
382
+ };
383
+ }
384
+ if (!data.results || data.results.length === 0) {
385
+ return {
386
+ content: [{ type: "text", text: "No search results." }]
387
+ };
388
+ }
389
+ const formatted = data.results.map((result, index) => {
390
+ const target = formatSearchTarget(result);
391
+ const threadInfo = result.channelType === "thread" ? `
392
+ thread: ${result.parentChannelName} -> ${target}` : "";
393
+ return [
394
+ `[${index + 1}] msg=${result.id} seq=${result.seq} time=${toLocalTime(result.createdAt)}`,
395
+ `target: ${target}${threadInfo}`,
396
+ `sender: @${result.senderName}${result.senderType === "agent" ? " (agent)" : ""}`,
397
+ `content: ${result.content}`,
398
+ `match: ${result.snippet}`,
399
+ `next: read_history(channel="${target}", around="${result.id}", limit=20)`
400
+ ].join("\n");
401
+ }).join("\n\n");
402
+ return {
403
+ content: [{
404
+ type: "text",
405
+ text: `## Search Results for "${trimmed}" (${data.results.length} results)
406
+
407
+ ${formatted}`
408
+ }]
409
+ };
410
+ } catch (err) {
411
+ return {
412
+ isError: true,
413
+ content: [{ type: "text", text: `Error: ${err.message}` }]
414
+ };
415
+ }
416
+ }
417
+ );
334
418
  server.tool(
335
419
  "read_history",
336
- "Read message history for a channel, DM, or thread. Use the same target format: '#channel', 'dm:@name', '#channel:id' for threads, 'dm:@name:id' for DM threads. Supports pagination: use 'before' to load older messages, 'after' to load messages after a seq number (e.g. to catch up on unread).",
420
+ "Read message history for a channel, DM, or thread. Use the same target format: '#channel', 'dm:@name', '#channel:id' for threads, 'dm:@name:id' for DM threads. Supports pagination via 'before' / 'after', and context jumps via 'around' (messageId or seq).",
337
421
  {
338
422
  channel: z.string().describe("The target to read history from \u2014 e.g. '#general', 'dm:@richard', '#general:abcd1234', 'dm:@richard:abcd1234'"),
339
423
  limit: z.number().default(50).describe("Max number of messages to return (default 50, max 100)"),
424
+ around: z.union([z.string(), z.number()]).optional().describe("Center the result window around a messageId or seq in this channel/thread."),
340
425
  before: z.number().optional().describe("Return messages before this seq number (for backward pagination). Omit for latest messages."),
341
426
  after: z.number().optional().describe("Return messages after this seq number (for catching up on unread). Returns oldest-first.")
342
427
  },
343
- async ({ channel, limit, before, after }) => {
428
+ async ({ channel, limit, around, before, after }) => {
344
429
  try {
345
430
  const params = new URLSearchParams();
346
431
  params.set("channel", channel);
347
432
  params.set("limit", String(Math.min(limit, 100)));
433
+ if (around !== void 0) params.set("around", String(around));
348
434
  if (before) params.set("before", String(before));
349
435
  if (after) params.set("after", String(after));
350
436
  const res = await fetch(
@@ -379,6 +465,12 @@ server.tool(
379
465
  footer = `
380
466
 
381
467
  --- ${data.historyLimitMessage || "Message history is limited on this plan."} ---`;
468
+ } else if (around && data.messages.length > 0 && (data.has_older || data.has_newer)) {
469
+ const minSeq = data.messages[0].seq;
470
+ const maxSeq = data.messages[data.messages.length - 1].seq;
471
+ footer = `
472
+
473
+ --- Context window shown. Use before=${minSeq} to load older messages or after=${maxSeq} to load newer messages. ---`;
382
474
  } else if (data.has_more && data.messages.length > 0) {
383
475
  if (after) {
384
476
  const maxSeq = data.messages[data.messages.length - 1].seq;
@@ -392,8 +484,8 @@ server.tool(
392
484
  --- ${data.messages.length} messages shown. Use before=${minSeq} to load older messages. ---`;
393
485
  }
394
486
  }
395
- let header = `## Message History for ${channel} (${data.messages.length} messages)`;
396
- if (data.last_read_seq > 0 && !after && !before) {
487
+ let header = `## Message History for ${channel}${around ? ` around ${around}` : ""} (${data.messages.length} messages)`;
488
+ if (data.last_read_seq > 0 && !after && !before && !around) {
397
489
  header += `
398
490
  Your last read position: seq ${data.last_read_seq}. Use read_history(channel="${channel}", after=${data.last_read_seq}) to see only unread messages.`;
399
491
  }
package/dist/index.js CHANGED
@@ -207,14 +207,15 @@ You have MCP tools from the "chat" server. Use ONLY these for communication:
207
207
  1. **${t("check_messages")}** \u2014 Non-blocking check for new messages. Use freely during work \u2014 at natural breakpoints or after notifications.
208
208
  2. **${t("send_message")}** \u2014 Send a message to a channel or DM.
209
209
  3. **${t("list_server")}** \u2014 List all channels in this server, which ones you have joined, plus all agents and humans.
210
- 4. **${t("read_history")}** \u2014 Read past messages from a channel or DM.
211
- 5. **${t("list_tasks")}** \u2014 View a channel's task board.
212
- 6. **${t("create_tasks")}** \u2014 Create new task-messages in a channel (supports batch; equivalent to sending a new message and publishing it as a task-message, not claiming it for yourself).
213
- 7. **${t("claim_tasks")}** \u2014 Claim tasks by number (supports batch, handles conflicts).
214
- 8. **${t("unclaim_task")}** \u2014 Release your claim on a task.
215
- 9. **${t("update_task_status")}** \u2014 Change a task's status (e.g. to in_review or done).
216
- 10. **${t("upload_file")}** \u2014 Upload an image file to attach to a message. Returns an attachment ID to pass to send_message.
217
- 11. **${t("view_file")}** \u2014 Download an attached image by its attachment ID so you can view it. Use when messages contain image attachments.
210
+ 4. **${t("read_history")}** \u2014 Read past messages from a channel, DM, or thread. Supports \`before\` / \`after\` pagination and \`around\` for centered context.
211
+ 5. **${t("search_messages")}** \u2014 Search messages visible to you, then inspect a hit with \`${t("read_history")}\`.
212
+ 6. **${t("list_tasks")}** \u2014 View a channel's task board.
213
+ 7. **${t("create_tasks")}** \u2014 Create new task-messages in a channel (supports batch; equivalent to sending a new message and publishing it as a task-message, not claiming it for yourself).
214
+ 8. **${t("claim_tasks")}** \u2014 Claim tasks by number (supports batch, handles conflicts).
215
+ 9. **${t("unclaim_task")}** \u2014 Release your claim on a task.
216
+ 10. **${t("update_task_status")}** \u2014 Change a task's status (e.g. to in_review or done).
217
+ 11. **${t("upload_file")}** \u2014 Upload an image file to attach to a message. Returns an attachment ID to pass to send_message.
218
+ 12. **${t("view_file")}** \u2014 Download an attached image by its attachment ID so you can view it. Use when messages contain image attachments.
218
219
 
219
220
  CRITICAL RULES:
220
221
  ${criticalRules.join("\n")}
@@ -282,6 +283,8 @@ Each channel has a **name** and optionally a **description** that define its pur
282
283
 
283
284
  \`read_history(channel="#channel-name")\` or \`read_history(channel="dm:@peer-name")\` or \`read_history(channel="#channel:shortid")\`
284
285
 
286
+ To jump directly to a specific hit with nearby context, use \`read_history(channel="...", around="messageId")\` or \`read_history(channel="...", around=12345)\`.
287
+
285
288
  ### Tasks
286
289
 
287
290
  When someone sends a message that asks you to do something \u2014 fix a bug, write code, review a PR, deploy, investigate an issue \u2014 that is work. Claim it before you start.
@@ -626,6 +629,7 @@ var ClaudeDriver = class {
626
629
  if (name === "mcp__chat__update_task_status") return "Updating task status\u2026";
627
630
  if (name === "mcp__chat__list_server") return "Listing server\u2026";
628
631
  if (name === "mcp__chat__read_history") return "Reading history\u2026";
632
+ if (name === "mcp__chat__search_messages") return "Searching messages\u2026";
629
633
  if (name === "mcp__chat__check_messages") return "Checking messages\u2026";
630
634
  if (name.startsWith("mcp__chat__")) return "";
631
635
  if (name === "Read" || name === "read_file") return "Reading file\u2026";
@@ -657,6 +661,7 @@ var ClaudeDriver = class {
657
661
  return input.target || input.channel || (input.dm_to ? `DM:@${input.dm_to}` : "");
658
662
  }
659
663
  if (name === "mcp__chat__read_history") return input.target || input.channel || "";
664
+ if (name === "mcp__chat__search_messages") return input.query || "";
660
665
  if (name === "mcp__chat__list_tasks") return input.channel || "";
661
666
  if (name === "mcp__chat__create_tasks") return input.channel || "";
662
667
  if (name === "mcp__chat__claim_tasks") {
@@ -876,6 +881,7 @@ var CodexDriver = class {
876
881
  if (name === `${this.mcpToolPrefix}update_task_status`) return "Updating task status\u2026";
877
882
  if (name === `${this.mcpToolPrefix}list_server`) return "Listing server\u2026";
878
883
  if (name === `${this.mcpToolPrefix}read_history`) return "Reading history\u2026";
884
+ if (name === `${this.mcpToolPrefix}search_messages`) return "Searching messages\u2026";
879
885
  if (name === `${this.mcpToolPrefix}check_messages`) return "Checking messages\u2026";
880
886
  if (name.startsWith(this.mcpToolPrefix)) return "";
881
887
  if (name === "shell" || name === "command_execution") return "Running command\u2026";
@@ -901,6 +907,7 @@ var CodexDriver = class {
901
907
  return input.target || input.channel || (input.dm_to ? `DM:@${input.dm_to}` : "");
902
908
  }
903
909
  if (name === `${this.mcpToolPrefix}read_history`) return input.target || input.channel || "";
910
+ if (name === `${this.mcpToolPrefix}search_messages`) return input.query || "";
904
911
  if (name === `${this.mcpToolPrefix}list_tasks`) return input.channel || "";
905
912
  if (name === `${this.mcpToolPrefix}create_tasks`) return input.channel || "";
906
913
  if (name === `${this.mcpToolPrefix}claim_tasks`) {
@@ -1968,16 +1975,9 @@ Note: While you are busy, you may receive [System notification: ...] messages ab
1968
1975
  case "error": {
1969
1976
  this.flushPendingTrajectory(agentId);
1970
1977
  if (ap) ap.lastRuntimeError = event.message;
1971
- const currentActivity = ap?.lastActivity || "working";
1972
- const currentDetail = ap?.lastActivityDetail || "";
1973
- this.sendToServer({
1974
- type: "agent:activity",
1975
- agentId,
1976
- activity: currentActivity,
1977
- detail: currentDetail,
1978
- entries: [{ kind: "text", text: `Error: ${event.message}` }],
1979
- launchId: ap?.launchId || void 0
1980
- });
1978
+ this.broadcastActivity(agentId, "error", event.message, [
1979
+ { kind: "text", text: `Error: ${event.message}` }
1980
+ ]);
1981
1981
  break;
1982
1982
  }
1983
1983
  }
@@ -2115,6 +2115,39 @@ var RUNTIMES = [
2115
2115
  { id: "gemini", displayName: "Gemini CLI", binary: "gemini", supported: false },
2116
2116
  { id: "kimi", displayName: "Kimi CLI", binary: "kimi", supported: true }
2117
2117
  ];
2118
+ var PLAN_CONFIG = {
2119
+ free: {
2120
+ displayName: "Hobby",
2121
+ limits: { maxMachines: 2, maxAgents: 5, maxChannels: 5, messageHistoryDays: 30, includedAgents: 5 },
2122
+ comingSoon: false,
2123
+ price: 0,
2124
+ extraAgentPrice: 0
2125
+ },
2126
+ founder: {
2127
+ displayName: "Founder",
2128
+ limits: { maxMachines: -1, maxAgents: -1, maxChannels: -1, messageHistoryDays: -1, includedAgents: -1 },
2129
+ comingSoon: false,
2130
+ price: 0,
2131
+ extraAgentPrice: 0
2132
+ }
2133
+ };
2134
+ var DISPLAY_PLAN_CONFIG = {
2135
+ free: PLAN_CONFIG.free,
2136
+ pro: {
2137
+ displayName: "Team",
2138
+ limits: { maxMachines: 8, maxAgents: 40, maxChannels: 20, messageHistoryDays: -1, includedAgents: 40 },
2139
+ comingSoon: true,
2140
+ price: 20,
2141
+ extraAgentPrice: 0
2142
+ },
2143
+ max: {
2144
+ displayName: "Business",
2145
+ limits: { maxMachines: 40, maxAgents: 200, maxChannels: -1, messageHistoryDays: -1, includedAgents: 200 },
2146
+ comingSoon: true,
2147
+ price: 200,
2148
+ extraAgentPrice: 0
2149
+ }
2150
+ };
2118
2151
 
2119
2152
  // src/index.ts
2120
2153
  var require2 = createRequire(import.meta.url);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@slock-ai/daemon",
3
- "version": "0.31.2",
3
+ "version": "0.31.3-alpha.7",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "slock-daemon": "dist/index.js"
@@ -16,27 +16,28 @@
16
16
  "publishConfig": {
17
17
  "access": "public"
18
18
  },
19
- "dependencies": {
20
- "@modelcontextprotocol/sdk": "^1.29.0",
21
- "ws": "^8.20.0",
22
- "zod": "^4.3.6"
23
- },
24
- "devDependencies": {
25
- "@types/node": "^25.5.0",
26
- "@types/ws": "^8.18.1",
27
- "tsup": "^8.5.1",
28
- "typescript": "^5.9.3",
29
- "@slock-ai/shared": "0.1.0"
30
- },
31
19
  "scripts": {
32
20
  "dev": "tsx watch src/index.ts",
33
21
  "start": "tsx src/index.ts",
34
22
  "build": "tsup",
35
23
  "test": "node --import tsx --test --test-force-exit 'src/**/*.test.ts'",
24
+ "prepublishOnly": "pnpm run build",
36
25
  "typecheck": "tsc --noEmit",
37
26
  "release:patch": "npm version patch --no-git-tag-version && cd ../.. && pnpm install --lockfile-only && git add packages/daemon/package.json pnpm-lock.yaml && git commit -m \"chore: bump @slock-ai/daemon to v$(node -p \"require('./packages/daemon/package.json').version\")\" && git tag daemon-v$(node -p \"require('./packages/daemon/package.json').version\") && git push && git push --tags",
38
27
  "release:minor": "npm version minor --no-git-tag-version && cd ../.. && pnpm install --lockfile-only && git add packages/daemon/package.json pnpm-lock.yaml && git commit -m \"chore: bump @slock-ai/daemon to v$(node -p \"require('./packages/daemon/package.json').version\")\" && git tag daemon-v$(node -p \"require('./packages/daemon/package.json').version\") && git push && git push --tags",
39
28
  "release:major": "npm version major --no-git-tag-version && cd ../.. && pnpm install --lockfile-only && git add packages/daemon/package.json pnpm-lock.yaml && git commit -m \"chore: bump @slock-ai/daemon to v$(node -p \"require('./packages/daemon/package.json').version\")\" && git tag daemon-v$(node -p \"require('./packages/daemon/package.json').version\") && git push && git push --tags",
40
29
  "release:alpha": "npm version prerelease --preid=alpha --no-git-tag-version && cd ../.. && pnpm install --lockfile-only && git add packages/daemon/package.json pnpm-lock.yaml && git commit -m \"chore: bump @slock-ai/daemon to v$(node -p \"require('./packages/daemon/package.json').version\")\" && git tag daemon-v$(node -p \"require('./packages/daemon/package.json').version\") && git push && git push --tags"
30
+ },
31
+ "dependencies": {
32
+ "@modelcontextprotocol/sdk": "^1.29.0",
33
+ "ws": "^8.20.0",
34
+ "zod": "^4.3.6"
35
+ },
36
+ "devDependencies": {
37
+ "@slock-ai/shared": "workspace:*",
38
+ "@types/node": "^25.5.0",
39
+ "@types/ws": "^8.18.1",
40
+ "tsup": "^8.5.1",
41
+ "typescript": "^5.9.3"
41
42
  }
42
- }
43
+ }