@wagemule/daemon 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -7,7 +7,7 @@ Wage Mule local daemon connects local agent runtimes to a Workspace Server.
7
7
  Use the command shown in the Wage Mule Web machine page. It pins the daemon version and includes the pairing token for the current space:
8
8
 
9
9
  ```bash
10
- npx -y @wagemule/daemon@0.1.0 start --server-url "https://your-server" --api-key "daemon_xxx" --space-id "space_xxx"
10
+ npx -y @wagemule/daemon@0.1.2 start --server-url "https://your-server" --api-key "daemon_xxx"
11
11
  ```
12
12
 
13
13
  The v0.1 daemon runs in the foreground. Keep the terminal open while the machine should stay online.
@@ -28,7 +28,6 @@ wm-daemon start --server-url "https://your-server" --api-key "daemon_xxx"
28
28
 
29
29
  - `--server-url`: Workspace Server HTTP URL.
30
30
  - `--api-key`: daemon pairing or reconnect token from the Web machine page.
31
- - `--space-id`: optional active space id. The Web command includes it when needed.
32
31
  - `--name`: optional display name for this machine.
33
32
  - `--root`: optional daemon root directory. Defaults to `~/.wm`.
34
33
 
package/dist/main.cjs CHANGED
@@ -34,6 +34,7 @@ __export(main_exports, {
34
34
  runCli: () => runCli
35
35
  });
36
36
  module.exports = __toCommonJS(main_exports);
37
+ var import_node_os9 = __toESM(require("node:os"));
37
38
 
38
39
  // src/process/command.ts
39
40
  var import_node_child_process = require("node:child_process");
@@ -2136,7 +2137,7 @@ var import_node_os = __toESM(require("node:os"));
2136
2137
  var import_node_path3 = __toESM(require("node:path"));
2137
2138
  async function scanAgentSkills(input) {
2138
2139
  const homeDir = input.homeDir ?? import_node_os.default.homedir();
2139
- const workspaceDir = import_node_path3.default.join(input.rootDir, input.serverNamespace, input.machineId, input.agentId);
2140
+ const workspaceDir = input.machineDirectoryName ? import_node_path3.default.join(input.rootDir, input.machineDirectoryName, input.agentDirectoryName ?? input.agentId) : import_node_path3.default.join(input.rootDir, input.serverNamespace, input.machineId, input.agentId);
2140
2141
  return {
2141
2142
  global: await scanGroupedSkillRoots(runtimeSkillRoots(homeDir, input.runtime), "global"),
2142
2143
  workspace: await scanGroupedSkillRoots(workspaceSkillRoots(workspaceDir, input.runtime), "workspace")
@@ -2289,8 +2290,8 @@ function skillTemplatesForWorkspace(input) {
2289
2290
 
2290
2291
  // src/workspace/agent-workspace.ts
2291
2292
  async function createAgentWorkspace(input) {
2292
- const serverDir = input.machineId ? import_node_path4.default.join(input.rootDir, input.serverNamespace, input.machineId) : import_node_path4.default.join(input.rootDir, input.serverNamespace);
2293
- const agentDir = import_node_path4.default.join(serverDir, input.agentId);
2293
+ const serverDir = input.machineId ? import_node_path4.default.join(input.rootDir, input.machineDirectoryName) : import_node_path4.default.join(input.rootDir, input.serverNamespace);
2294
+ const agentDir = import_node_path4.default.join(serverDir, input.agentDirectoryName);
2294
2295
  const wmDir = import_node_path4.default.join(agentDir, ".wm");
2295
2296
  const notesDir = import_node_path4.default.join(agentDir, "notes");
2296
2297
  const runtimeSessionsDir = import_node_path4.default.join(wmDir, "runtime-sessions");
@@ -2415,24 +2416,26 @@ async function buildSystemPrompt(input, paths) {
2415
2416
  "",
2416
2417
  "1. `wm message check` -- Non-blocking check for new channel, DM, or thread messages at natural breakpoints.",
2417
2418
  "2. `wm message send` -- Send a channel, DM, or thread reply through the platform.",
2418
- "3. `wm message read` -- Read visible history with pagination or around a specific message.",
2419
- "4. `wm message search` -- Search visible channel and DM history before answering historical questions.",
2420
- "5. `wm server info` -- Inspect the current server, visible channels, humans, agents, and memberships.",
2421
- "6. `wm channel members` -- Inspect members of a channel, DM, or thread target.",
2422
- "7. `wm task list` -- View task messages for a channel or relevant target.",
2423
- "8. `wm task claim` -- Claim a task before doing work that changes files, runs tools, or performs multi-step investigation.",
2424
- "9. `wm task unclaim` -- Release a task you cannot or should not continue.",
2425
- "10. `wm task update` -- Move a task through statuses such as todo, in_progress, in_review, and done.",
2426
- "11. `wm task create` -- Create genuine new follow-up work when no canonical message/task exists.",
2427
- "12. `wm attachment upload` -- Upload a local artifact and reference it in a platform message.",
2428
- "13. `wm attachment view` -- Download and inspect a platform attachment.",
2429
- "14. `wm profile show` -- Inspect your own profile or a visible human/agent profile.",
2430
- "15. `wm profile update` -- Update your display name, description, or avatar when explicitly asked.",
2431
- "16. `wm reminder schedule` -- Schedule a platform-visible reminder for follow-up work.",
2432
- "17. `wm reminder list` -- List reminders and their lifecycle history.",
2433
- "18. `wm reminder snooze` -- Push an existing reminder later.",
2434
- "19. `wm reminder update` -- Change a reminder without creating a duplicate.",
2435
- "20. `wm reminder cancel` -- Cancel a reminder that is no longer needed.",
2419
+ "3. `wm dm send` -- Send a private agent-to-agent DM, optionally waiting for a reply.",
2420
+ "4. `wm message read` -- Read visible history with pagination or around a specific message.",
2421
+ "5. `wm message search` -- Search visible channel and DM history before answering historical questions.",
2422
+ "6. `wm server info` -- Inspect the current server, visible channels, humans, agents, and memberships.",
2423
+ "7. `wm channel members` -- Inspect members of a channel, DM, or thread target.",
2424
+ "8. `wm member find` -- Find platform members by Feishu open_id.",
2425
+ "9. `wm task list` -- View task messages for a channel or relevant target.",
2426
+ "10. `wm task claim` -- Claim a task before doing work that changes files, runs tools, or performs multi-step investigation.",
2427
+ "11. `wm task unclaim` -- Release a task you cannot or should not continue.",
2428
+ "12. `wm task update` -- Move a task through statuses such as todo, in_progress, in_review, and done.",
2429
+ "13. `wm task create` -- Create genuine new follow-up work when no canonical message/task exists.",
2430
+ "14. `wm attachment upload` -- Upload a local artifact and reference it in a platform message.",
2431
+ "15. `wm attachment view` -- Download and inspect a platform attachment.",
2432
+ "16. `wm profile show` -- Inspect your own profile or a visible human/agent profile.",
2433
+ "17. `wm profile update` -- Update your display name, description, or avatar when explicitly asked.",
2434
+ "18. `wm reminder schedule` -- Schedule a platform-visible reminder for follow-up work.",
2435
+ "19. `wm reminder list` -- List reminders and their lifecycle history.",
2436
+ "20. `wm reminder snooze` -- Push an existing reminder later.",
2437
+ "21. `wm reminder update` -- Change a reminder without creating a duplicate.",
2438
+ "22. `wm reminder cancel` -- Cancel a reminder that is no longer needed.",
2436
2439
  "",
2437
2440
  "Diagnostic command:",
2438
2441
  "",
@@ -2444,6 +2447,7 @@ async function buildSystemPrompt(input, paths) {
2444
2447
  "",
2445
2448
  "- Other agents may be running on different machines. Do not use local processes, workspaces, server activity, task lists, or runtime sessions to answer what another agent is doing.",
2446
2449
  '- When a human asks you to ask, check, confirm, contact, or get status from another agent, your first platform action is to ask that agent in the original message thread with `wm message send --target "#channel:<message>"` and an @mention.',
2450
+ "- When the request explicitly calls for a private agent DM, use `wm dm send --to-agent-id <agent_id>` instead of a thread mention.",
2447
2451
  "- Wait for that agent's platform reply via delivered messages, `wm message check`, or recent unread output; then summarize the reply in the same thread.",
2448
2452
  "- `wm server info`, `wm channel members`, and `wm task list` are discovery tools. They are not authoritative answers for another agent's current work.",
2449
2453
  "",
@@ -2533,7 +2537,9 @@ async function buildWorkspaceSkillsSection(input) {
2533
2537
  const result = await scanAgentSkills({
2534
2538
  rootDir: input.rootDir,
2535
2539
  serverNamespace: input.serverNamespace,
2540
+ machineDirectoryName: input.machineDirectoryName,
2536
2541
  machineId: input.machineId ?? "",
2542
+ agentDirectoryName: input.agentDirectoryName,
2537
2543
  agentId: input.agentId,
2538
2544
  runtime: input.runtime
2539
2545
  }).catch(() => ({ global: [], workspace: [] }));
@@ -2589,7 +2595,7 @@ if (fs.existsSync(envPath)) {
2589
2595
 
2590
2596
  const args = process.argv.slice(2);
2591
2597
  const [group, command] = args;
2592
- const help = "Available commands: wm auth whoami; wm message send/check/read/search; wm server info; wm channel members; wm channel members; wm task list/claim/unclaim/update/create; wm attachment upload/view; wm profile show/update; wm reminder schedule/list/snooze/update/cancel";
2598
+ const help = "Available commands: wm auth whoami; wm message send/check/read/search; wm dm send; wm server info; wm channel members; wm member find; wm task list/claim/unclaim/update/create; wm attachment upload/view; wm profile show/update; wm reminder schedule/list/snooze/update/cancel";
2593
2599
 
2594
2600
  function valueAfter(name) {
2595
2601
  const index = args.indexOf(name);
@@ -2601,7 +2607,7 @@ function hasFlag(name) {
2601
2607
  }
2602
2608
 
2603
2609
  function positionalText() {
2604
- const knownOptions = new Set(["--target", "--channel", "--text", "--content", "--attachment-ids", "--query", "--q", "--around", "--status", "--id", "--task-id", "--title", "--description", "--file", "--at", "--every", "--role", "--instructions", "--body"]);
2610
+ const knownOptions = new Set(["--target", "--channel", "--text", "--content", "--attachment-ids", "--query", "--q", "--around", "--status", "--id", "--task-id", "--title", "--description", "--file", "--at", "--every", "--role", "--instructions", "--body", "--to-agent-id", "--timeout", "--feishu-open-id", "--open-id"]);
2605
2611
  const values = [];
2606
2612
  for (let index = 2; index < args.length; index++) {
2607
2613
  const arg = args[index];
@@ -2671,6 +2677,17 @@ function formatSendResult(result, target) {
2671
2677
  return lines.join("\\n");
2672
2678
  }
2673
2679
 
2680
+ function formatDmSendResult(payload) {
2681
+ const messageId = payload.messageId || payload.message?.id || "unknown";
2682
+ const lines = [\`DM sent. Message ID: \${messageId}\`];
2683
+ if (payload.reply) {
2684
+ lines.push("", "--- Reply ---", messageLine(payload.reply));
2685
+ } else if (payload.timed_out) {
2686
+ lines.push("Timed out waiting for reply.");
2687
+ }
2688
+ return lines.join("\\n");
2689
+ }
2690
+
2674
2691
  function formatMessages(payload) {
2675
2692
  const messages = payload.messages || [];
2676
2693
  if (!messages.length) return "No messages.";
@@ -2705,6 +2722,26 @@ function formatMembers(payload) {
2705
2722
  return members.map((member) => \`- \${member.mention} type=\${member.type}\${member.runtime ? \` runtime=\${member.runtime}\` : ""}\`).join("\\n");
2706
2723
  }
2707
2724
 
2725
+ function formatMemberFind(payload) {
2726
+ const lines = [];
2727
+ if (payload.human) {
2728
+ lines.push(\`Human: @\${payload.human.name} (\${payload.human.id})\`);
2729
+ if (payload.human.feishu_open_id) lines.push(\`Feishu open_id: \${payload.human.feishu_open_id}\`);
2730
+ if (payload.human.email) lines.push(\`Email: \${payload.human.email}\`);
2731
+ } else {
2732
+ lines.push("Human: not found");
2733
+ }
2734
+ lines.push("Agents:");
2735
+ if (payload.agents?.length) {
2736
+ for (const agent of payload.agents) {
2737
+ lines.push(\`- @\${agent.name} id=\${agent.agent_id} runtime=\${agent.runtime} machine=\${agent.machine_id} \${agent.online ? "online" : "offline"}\`);
2738
+ }
2739
+ } else {
2740
+ lines.push("- none");
2741
+ }
2742
+ return lines.join("\\n");
2743
+ }
2744
+
2708
2745
  async function main() {
2709
2746
  const agentId = process.env.WM_AGENT_ID || process.env.WM_DAEMON_AGENT_ID;
2710
2747
  if (group === "auth" && command === "whoami") {
@@ -2736,6 +2773,20 @@ async function main() {
2736
2773
  printResult(payload, formatSendResult(payload, target));
2737
2774
  return;
2738
2775
  }
2776
+ if (group === "dm" && command === "send") {
2777
+ const toAgentId = valueAfter("--to-agent-id");
2778
+ if (!toAgentId) throw new Error("--to-agent-id is required");
2779
+ const text = (valueAfter("--text") || valueAfter("--content") || await stdin() || positionalText()).trim();
2780
+ if (!text) throw new Error("message text is required; use --text, --content, stdin, or a positional message");
2781
+ const payload = await request("POST", \`/internal/agent/\${agentId}/dm/send\`, {
2782
+ to_agent_id: toAgentId,
2783
+ text,
2784
+ wait_for_reply: hasFlag("--wait-for-reply"),
2785
+ timeout_ms: valueAfter("--timeout") ? Number(valueAfter("--timeout")) : undefined,
2786
+ });
2787
+ printResult(payload, formatDmSendResult(payload));
2788
+ return;
2789
+ }
2739
2790
  if (group === "message" && command === "check") {
2740
2791
  const payload = await request("GET", \`/internal/agent/\${agentId}/receive\`);
2741
2792
  printResult(payload, formatMessages(payload).replace("No messages.", "No new messages."));
@@ -2774,6 +2825,13 @@ async function main() {
2774
2825
  printResult(payload, formatMembers(payload));
2775
2826
  return;
2776
2827
  }
2828
+ if (group === "member" && command === "find") {
2829
+ const openId = (valueAfter("--feishu-open-id") || valueAfter("--open-id") || "").trim();
2830
+ if (!openId) throw new Error("--feishu-open-id is required");
2831
+ const payload = await request("GET", \`/internal/agent/\${agentId}/members/find?feishu_open_id=\${encodeURIComponent(openId)}\`);
2832
+ printResult(payload, formatMemberFind(payload));
2833
+ return;
2834
+ }
2777
2835
  if (group === "task" && command === "list") {
2778
2836
  const status = valueAfter("--status");
2779
2837
  const suffix = status ? \`?status=\${encodeURIComponent(status)}\` : "";
@@ -2889,7 +2947,11 @@ async function runAdapterSmokeTest(input) {
2889
2947
  input.runtime,
2890
2948
  "adapter runtime must match requested runtime"
2891
2949
  );
2892
- const workspace = await createAgentWorkspace(input);
2950
+ const workspace = await createAgentWorkspace({
2951
+ ...input,
2952
+ machineDirectoryName: input.machineDirectoryName ?? input.serverNamespace,
2953
+ agentDirectoryName: input.agentDirectoryName ?? input.agentId
2954
+ });
2893
2955
  const runDir = input.runDir ?? workspace.agentDir;
2894
2956
  const systemPrompt = await (0, import_promises5.readFile)(workspace.systemPromptPath, "utf8");
2895
2957
  const result = await input.adapter.run({
@@ -3033,6 +3095,8 @@ async function createLabAgent(input) {
3033
3095
  rootDir: input.rootDir,
3034
3096
  serverNamespace: input.serverNamespace,
3035
3097
  serverId: `lab:${input.serverNamespace}`,
3098
+ machineDirectoryName: input.serverNamespace,
3099
+ agentDirectoryName: agentId,
3036
3100
  daemonId: import_node_os4.default.hostname() || "local",
3037
3101
  agentId,
3038
3102
  agentName: input.agentName,
@@ -3724,7 +3788,7 @@ var import_ws = __toESM(require("ws"));
3724
3788
  // package.json
3725
3789
  var package_default = {
3726
3790
  name: "@wagemule/daemon",
3727
- version: "0.1.0",
3791
+ version: "0.1.2",
3728
3792
  private: false,
3729
3793
  description: "Wage Mule local daemon for connecting local agent runtimes to Workspace Server.",
3730
3794
  main: "./dist/main.cjs",
@@ -4706,9 +4770,7 @@ var WorkspaceBrowser = class {
4706
4770
  }
4707
4771
  rootDirForAgent(agentId, serverNamespace, machineId) {
4708
4772
  if (typeof this.options === "function") return this.options(agentId);
4709
- const scope = serverNamespace && machineId ? { serverNamespace, machineId } : this.options.workspaceScopeForAgent(agentId);
4710
- if (!scope) return void 0;
4711
- return import_node_path12.default.join(this.options.rootDir, scope.serverNamespace, scope.machineId, agentId);
4773
+ return this.options.workspaceDirectoryForAgent(agentId, serverNamespace, machineId);
4712
4774
  }
4713
4775
  resolveInside(base, relativePath) {
4714
4776
  const resolved = import_node_path12.default.resolve(base, relativePath);
@@ -4723,6 +4785,21 @@ function isSafeWorkspaceDirectoryName(directoryName) {
4723
4785
  directoryName && directoryName !== "." && directoryName !== ".." && !directoryName.includes("/") && !directoryName.includes("\\") && !directoryName.includes("..")
4724
4786
  );
4725
4787
  }
4788
+ function displayNameToWorkspaceDirectoryName(displayName, fallback = "machine") {
4789
+ const cleaned = displayName.trim().normalize("NFKC").replace(/[\/\\:]+/g, "-").replace(/\s+/g, "-").replace(/[^A-Za-z0-9._-]+/g, "-").replace(/-+/g, "-").replace(/^[.-]+|[.-]+$/g, "").slice(0, 80);
4790
+ return cleaned || fallback;
4791
+ }
4792
+ function namedWorkspaceDirectoryName(displayName, id, fallback = "item") {
4793
+ const namePart = displayNameToWorkspaceDirectoryName(displayName, fallback);
4794
+ const idPart = workspaceDirectoryIdPart(namePart, id);
4795
+ return `${namePart}-${idPart}`;
4796
+ }
4797
+ function workspaceDirectoryIdPart(namePart, id) {
4798
+ let idPart = displayNameToWorkspaceDirectoryName(id, "id");
4799
+ if (idPart.startsWith(`${namePart}-`)) idPart = idPart.slice(namePart.length + 1);
4800
+ idPart = idPart.replace(/^(machine|agent|org)[_-]/, "");
4801
+ return idPart || displayNameToWorkspaceDirectoryName(id, "id");
4802
+ }
4726
4803
 
4727
4804
  // src/agent-manager/agent-process-manager.ts
4728
4805
  var AgentProcessManager = class {
@@ -4731,9 +4808,11 @@ var AgentProcessManager = class {
4731
4808
  this.modelDetector = options.modelDetector ?? new RuntimeModelDetector();
4732
4809
  this.browser = new WorkspaceBrowser({
4733
4810
  rootDir: this.options.rootDir,
4734
- workspaceScopeForAgent: (agentId) => {
4811
+ workspaceDirectoryForAgent: (agentId, serverNamespace, machineId) => {
4735
4812
  const config = this.running.get(agentId)?.config ?? this.idleConfigs.get(agentId)?.config;
4736
- return config ? { serverNamespace: config.serverNamespace, machineId: config.machineId } : void 0;
4813
+ if (config) return this.resolveWorkspaceDirectory(config, agentId);
4814
+ if (serverNamespace && machineId) return this.resolveWorkspaceDirectoryForParts(machineId, machineId, agentId, agentId);
4815
+ return void 0;
4737
4816
  }
4738
4817
  });
4739
4818
  this.reminderCache.onFire((job) => {
@@ -4801,9 +4880,9 @@ var AgentProcessManager = class {
4801
4880
  } else if (message.type === "agent:workspace:list") {
4802
4881
  await this.replyWorkspaceList(message.agentId, message.requestId, message.path, message.serverNamespace, message.machineId, message.config);
4803
4882
  } else if (message.type === "agent:workspace:read") {
4804
- await this.replyWorkspaceRead(message.agentId, message.requestId, message.path, message.serverNamespace, message.machineId);
4883
+ await this.replyWorkspaceRead(message.agentId, message.requestId, message.path, message.serverNamespace, message.machineId, message.config);
4805
4884
  } else if (message.type === "agent:workspace:write") {
4806
- await this.replyWorkspaceWrite(message.agentId, message.requestId, message.path, message.content, message.serverNamespace, message.machineId);
4885
+ await this.replyWorkspaceWrite(message.agentId, message.requestId, message.path, message.content, message.serverNamespace, message.machineId, message.config);
4807
4886
  } else if (message.type === "agent:skills:list") {
4808
4887
  await this.replyAgentSkillsList(message.agentId, message.requestId, message.runtime, message.serverNamespace, message.machineId, message.config);
4809
4888
  } else if (message.type === "agent:workspace:init") {
@@ -4834,9 +4913,9 @@ var AgentProcessManager = class {
4834
4913
  const results = await this.detectRuntimeModels(message.runtimes ?? ["cursor", "claude", "codex", "gemini", "kimi", "hermes", "opencode"]);
4835
4914
  this.options.sendToServer({ type: "machine:runtime_models:result", requestId: message.requestId, results });
4836
4915
  } else if (message.type === "machine:workspace:scan") {
4837
- await this.replyMachineWorkspaceScan(message.requestId, message.serverNamespace, message.machineId, message.knownAgentIds ?? []);
4916
+ await this.replyMachineWorkspaceScan(message.requestId, message.serverNamespace, message.machineId, message.machineName, message.knownAgentIds ?? []);
4838
4917
  } else if (message.type === "machine:workspace:delete") {
4839
- await this.replyMachineWorkspaceDelete(message.requestId, message.serverNamespace, message.machineId, message.directoryName);
4918
+ await this.replyMachineWorkspaceDelete(message.requestId, message.serverNamespace, message.machineId, message.machineName, message.directoryName);
4840
4919
  } else if (message.type === "agent:reset-workspace") {
4841
4920
  await this.replyResetWorkspace(message.requestId, message.agentId, message.config);
4842
4921
  } else if (message.type === "agent:diagnostic") {
@@ -4973,7 +5052,9 @@ var AgentProcessManager = class {
4973
5052
  rootDir: this.options.rootDir,
4974
5053
  serverNamespace: config.serverNamespace,
4975
5054
  serverId: config.spaceId,
5055
+ machineDirectoryName: this.machineDirectoryName(config),
4976
5056
  machineId: config.machineId,
5057
+ agentDirectoryName: this.agentDirectoryName(config, agentId),
4977
5058
  daemonId: import_node_os8.default.hostname() || "local-daemon",
4978
5059
  agentId,
4979
5060
  agentName: config.agentName,
@@ -5043,16 +5124,18 @@ var AgentProcessManager = class {
5043
5124
  }
5044
5125
  async ensureAgentWorkspace(agentId, config) {
5045
5126
  const running = this.running.get(agentId);
5046
- if (running) return { workspace: running.workspace };
5127
+ if (running && !config?.feishuDelegationEnabled) return { workspace: running.workspace };
5047
5128
  const idle = this.idleConfigs.get(agentId);
5048
- if (idle?.workspace) return { workspace: idle.workspace };
5129
+ if (idle?.workspace && !config?.feishuDelegationEnabled) return { workspace: idle.workspace };
5049
5130
  const effectiveConfig = config ?? idle?.config;
5050
5131
  if (!effectiveConfig) return { missing: true };
5051
5132
  const workspace = await createAgentWorkspace({
5052
5133
  rootDir: this.options.rootDir,
5053
5134
  serverNamespace: effectiveConfig.serverNamespace,
5054
5135
  serverId: effectiveConfig.spaceId,
5136
+ machineDirectoryName: this.machineDirectoryName(effectiveConfig),
5055
5137
  machineId: effectiveConfig.machineId,
5138
+ agentDirectoryName: this.agentDirectoryName(effectiveConfig, agentId),
5056
5139
  daemonId: import_node_os8.default.hostname() || "local-daemon",
5057
5140
  agentId,
5058
5141
  agentName: effectiveConfig.agentName,
@@ -5060,6 +5143,10 @@ var AgentProcessManager = class {
5060
5143
  role: effectiveConfig.role,
5061
5144
  feishuDelegationEnabled: effectiveConfig.feishuDelegationEnabled
5062
5145
  });
5146
+ if (running) {
5147
+ running.workspace = workspace;
5148
+ running.config = effectiveConfig;
5149
+ }
5063
5150
  this.idleConfigs.set(agentId, { config: effectiveConfig, sessionId: effectiveConfig.sessionId, workspace });
5064
5151
  return { workspace };
5065
5152
  }
@@ -5071,7 +5158,7 @@ var AgentProcessManager = class {
5071
5158
  requestId,
5072
5159
  agentId,
5073
5160
  ok: true,
5074
- workspacePath: workspace ? `~/.wm/${config.serverNamespace}/${config.machineId}/${agentId}/` : void 0
5161
+ workspacePath: workspace ? this.workspaceDisplayPath(config, agentId) : void 0
5075
5162
  });
5076
5163
  } catch (error) {
5077
5164
  this.options.sendToServer({
@@ -5651,9 +5738,7 @@ var AgentProcessManager = class {
5651
5738
  return;
5652
5739
  }
5653
5740
  const effectiveConfig = config ?? this.running.get(agentId)?.config ?? this.idleConfigs.get(agentId)?.config;
5654
- const namespace = serverNamespace ?? effectiveConfig?.serverNamespace;
5655
- const machine = machineId ?? effectiveConfig?.machineId;
5656
- const entries = await this.browser.listFiles(agentId, dirPath, namespace, machine);
5741
+ const entries = await this.browser.listFiles(agentId, dirPath, serverNamespace ?? effectiveConfig?.serverNamespace, machineId ?? effectiveConfig?.machineId);
5657
5742
  this.options.sendToServer({ type: "agent:workspace:file_tree", agentId, requestId, entries });
5658
5743
  } catch (error) {
5659
5744
  this.log(`workspace list failed agent=${agentId} request=${requestId} error=${error instanceof Error ? error.message : String(error)}`);
@@ -5666,10 +5751,11 @@ var AgentProcessManager = class {
5666
5751
  });
5667
5752
  }
5668
5753
  }
5669
- async replyWorkspaceRead(agentId, requestId, filePath, serverNamespace, machineId) {
5754
+ async replyWorkspaceRead(agentId, requestId, filePath, serverNamespace, machineId, config) {
5670
5755
  try {
5671
5756
  this.log(`workspace read agent=${agentId} request=${requestId} path=${filePath}`);
5672
- const file = await this.browser.readFile(agentId, filePath, serverNamespace, machineId);
5757
+ const effectiveConfig = config ?? this.running.get(agentId)?.config ?? this.idleConfigs.get(agentId)?.config;
5758
+ const file = await this.browser.readFile(agentId, filePath, serverNamespace ?? effectiveConfig?.serverNamespace, machineId ?? effectiveConfig?.machineId);
5673
5759
  this.options.sendToServer({ type: "agent:workspace:file_content", agentId, requestId, file });
5674
5760
  } catch (error) {
5675
5761
  this.log(`workspace read failed agent=${agentId} request=${requestId} error=${error instanceof Error ? error.message : String(error)}`);
@@ -5682,10 +5768,11 @@ var AgentProcessManager = class {
5682
5768
  });
5683
5769
  }
5684
5770
  }
5685
- async replyWorkspaceWrite(agentId, requestId, filePath, content, serverNamespace, machineId) {
5771
+ async replyWorkspaceWrite(agentId, requestId, filePath, content, serverNamespace, machineId, config) {
5686
5772
  try {
5687
5773
  this.log(`workspace write agent=${agentId} request=${requestId} path=${filePath}`);
5688
- const result = await this.browser.writeFile(agentId, filePath, content, serverNamespace, machineId);
5774
+ const effectiveConfig = config ?? this.running.get(agentId)?.config ?? this.idleConfigs.get(agentId)?.config;
5775
+ const result = await this.browser.writeFile(agentId, filePath, content, serverNamespace ?? effectiveConfig?.serverNamespace, machineId ?? effectiveConfig?.machineId);
5689
5776
  this.options.sendToServer({ type: "agent:workspace:write_result", agentId, requestId, ...result });
5690
5777
  } catch (error) {
5691
5778
  this.options.sendToServer({
@@ -5708,7 +5795,9 @@ var AgentProcessManager = class {
5708
5795
  const result = await scanAgentSkills({
5709
5796
  rootDir: this.options.rootDir,
5710
5797
  serverNamespace: namespace,
5798
+ machineDirectoryName: this.machineDirectoryName(effectiveConfig),
5711
5799
  machineId: machine,
5800
+ agentDirectoryName: effectiveConfig ? this.agentDirectoryName(effectiveConfig, agentId) : namedWorkspaceDirectoryName(agentId, agentId, "agent"),
5712
5801
  agentId,
5713
5802
  runtime
5714
5803
  });
@@ -5724,9 +5813,9 @@ var AgentProcessManager = class {
5724
5813
  });
5725
5814
  }
5726
5815
  }
5727
- async replyMachineWorkspaceScan(requestId, serverNamespace, machineId, knownAgentIds) {
5816
+ async replyMachineWorkspaceScan(requestId, serverNamespace, machineId, machineName, knownAgentIds) {
5728
5817
  try {
5729
- const entries = await this.scanWorkspaces(serverNamespace, machineId, new Set(knownAgentIds));
5818
+ const entries = await this.scanWorkspaces(serverNamespace, machineId, machineName, new Set(knownAgentIds));
5730
5819
  this.options.sendToServer({ type: "machine:workspace:scan/result", requestId, entries });
5731
5820
  } catch (error) {
5732
5821
  this.options.sendToServer({
@@ -5737,11 +5826,11 @@ var AgentProcessManager = class {
5737
5826
  });
5738
5827
  }
5739
5828
  }
5740
- async replyMachineWorkspaceDelete(requestId, serverNamespace, machineId, directoryName) {
5829
+ async replyMachineWorkspaceDelete(requestId, serverNamespace, machineId, machineName, directoryName) {
5741
5830
  try {
5742
5831
  if (!isSafeWorkspaceDirectoryName(directoryName)) throw new Error("unsafe workspace directory name");
5743
5832
  if (this.running.has(directoryName) || this.starting.has(directoryName)) throw new Error("cannot delete a running workspace");
5744
- const target = this.resolveWorkspaceDirectory(serverNamespace, machineId, directoryName);
5833
+ const target = this.resolveWorkspaceDirectoryName(machineName ?? machineId, machineId, directoryName);
5745
5834
  await (0, import_promises12.rm)(target, { recursive: true, force: true });
5746
5835
  this.options.sendToServer({ type: "machine:workspace:delete/result", requestId, directoryName, ok: true });
5747
5836
  } catch (error) {
@@ -5757,13 +5846,15 @@ var AgentProcessManager = class {
5757
5846
  async replyResetWorkspace(requestId, agentId, config) {
5758
5847
  try {
5759
5848
  await this.stopAgent(agentId, "reset-workspace");
5760
- const target = this.resolveWorkspaceDirectory(config.serverNamespace, config.machineId, agentId);
5849
+ const target = this.resolveWorkspaceDirectory(config, agentId);
5761
5850
  await (0, import_promises12.rm)(target, { recursive: true, force: true });
5762
5851
  const workspace = await createAgentWorkspace({
5763
5852
  rootDir: this.options.rootDir,
5764
5853
  serverNamespace: config.serverNamespace,
5765
5854
  serverId: config.spaceId,
5855
+ machineDirectoryName: this.machineDirectoryName(config),
5766
5856
  machineId: config.machineId,
5857
+ agentDirectoryName: this.agentDirectoryName(config, agentId),
5767
5858
  daemonId: import_node_os8.default.hostname() || "local-daemon",
5768
5859
  agentId,
5769
5860
  agentName: config.agentName,
@@ -5808,7 +5899,7 @@ var AgentProcessManager = class {
5808
5899
  const idle = this.idleConfigs.get(agentId);
5809
5900
  const effectiveConfig = running?.config ?? idle?.config ?? config;
5810
5901
  const workspace = running?.workspace ?? idle?.workspace;
5811
- const workspacePath = workspace?.agentDir ?? (effectiveConfig ? this.resolveWorkspaceDirectory(effectiveConfig.serverNamespace, effectiveConfig.machineId, agentId) : void 0);
5902
+ const workspacePath = workspace?.agentDir ?? (effectiveConfig ? this.resolveWorkspaceDirectory(effectiveConfig, agentId) : void 0);
5812
5903
  const workspaceExists = workspacePath ? await pathExists(workspacePath) : false;
5813
5904
  return {
5814
5905
  agentId,
@@ -5824,8 +5915,8 @@ var AgentProcessManager = class {
5824
5915
  lastActivity: this.lastActivities.get(agentId)
5825
5916
  };
5826
5917
  }
5827
- async scanWorkspaces(serverNamespace, machineId, knownAgentIds) {
5828
- const machineRoot = this.resolveMachineRoot(serverNamespace, machineId);
5918
+ async scanWorkspaces(serverNamespace, machineId, machineName, knownAgentIds) {
5919
+ const machineRoot = this.resolveMachineRoot(machineName ?? machineId, machineId);
5829
5920
  const names = await (0, import_promises12.readdir)(machineRoot, { withFileTypes: true }).catch((error) => {
5830
5921
  if (error.code === "ENOENT") return [];
5831
5922
  throw error;
@@ -5833,14 +5924,15 @@ var AgentProcessManager = class {
5833
5924
  const entries = [];
5834
5925
  for (const entry of names) {
5835
5926
  if (!entry.isDirectory() || !isSafeWorkspaceDirectoryName(entry.name)) continue;
5836
- const fullPath = this.resolveWorkspaceDirectory(serverNamespace, machineId, entry.name);
5927
+ const fullPath = this.resolveWorkspaceDirectoryName(machineName ?? machineId, machineId, entry.name);
5837
5928
  const summary = await summarizeDirectory(fullPath);
5929
+ const agentId = agentIdFromDirectoryName(entry.name, knownAgentIds);
5838
5930
  entries.push({
5839
5931
  directoryName: entry.name,
5840
- agentId: entry.name,
5841
- workspacePath: `~/.wm/${serverNamespace}/${machineId}/${entry.name}/`,
5842
- knownToServer: knownAgentIds.has(entry.name),
5843
- running: this.running.has(entry.name) || this.starting.has(entry.name),
5932
+ agentId,
5933
+ workspacePath: `~/.wm/${this.machineDirectoryNameForParts(machineName ?? machineId, machineId)}/${entry.name}/`,
5934
+ knownToServer: knownAgentIds.has(agentId),
5935
+ running: this.running.has(agentId) || this.starting.has(agentId),
5844
5936
  fileCount: summary.fileCount,
5845
5937
  totalSizeBytes: summary.totalSizeBytes,
5846
5938
  lastModified: summary.lastModified
@@ -5848,25 +5940,36 @@ var AgentProcessManager = class {
5848
5940
  }
5849
5941
  return entries.sort((a, b) => b.lastModified - a.lastModified || a.directoryName.localeCompare(b.directoryName));
5850
5942
  }
5851
- resolveNamespaceRoot(serverNamespace) {
5852
- if (!isSafeWorkspaceDirectoryName(serverNamespace)) throw new Error("unsafe server namespace");
5853
- const resolved = import_node_path13.default.resolve(this.options.rootDir, serverNamespace);
5943
+ resolveMachineRoot(machineName, machineId) {
5944
+ const machineDirectoryName = this.machineDirectoryNameForParts(machineName, machineId);
5945
+ if (!isSafeWorkspaceDirectoryName(machineDirectoryName)) throw new Error("unsafe machine directory");
5946
+ const resolved = import_node_path13.default.resolve(this.options.rootDir, machineDirectoryName);
5854
5947
  const base = import_node_path13.default.resolve(this.options.rootDir);
5855
5948
  if (resolved !== base && !resolved.startsWith(`${base}${import_node_path13.default.sep}`)) throw new Error("workspace root escapes daemon root");
5856
5949
  return resolved;
5857
5950
  }
5858
- resolveMachineRoot(serverNamespace, machineId) {
5859
- if (!isSafeWorkspaceDirectoryName(machineId)) throw new Error("unsafe machine id");
5860
- const namespaceRoot = this.resolveNamespaceRoot(serverNamespace);
5861
- const resolved = import_node_path13.default.resolve(namespaceRoot, machineId);
5862
- if (resolved !== namespaceRoot && !resolved.startsWith(`${namespaceRoot}${import_node_path13.default.sep}`)) {
5863
- throw new Error("machine workspace root escapes namespace");
5864
- }
5865
- return resolved;
5951
+ machineDirectoryName(config) {
5952
+ return this.machineDirectoryNameForParts(config?.machineName ?? this.options.machineDisplayName, config?.machineId ?? "machine");
5953
+ }
5954
+ machineDirectoryNameForParts(machineName, machineId) {
5955
+ return namedWorkspaceDirectoryName(machineName, machineId, "machine");
5956
+ }
5957
+ agentDirectoryName(config, agentId) {
5958
+ return namedWorkspaceDirectoryName(config.agentName, agentId, "agent");
5959
+ }
5960
+ workspaceDisplayPath(config, agentId) {
5961
+ return `~/.wm/${this.machineDirectoryName(config)}/${this.agentDirectoryName(config, agentId)}/`;
5866
5962
  }
5867
- resolveWorkspaceDirectory(serverNamespace, machineId, directoryName) {
5963
+ resolveWorkspaceDirectory(config, agentId) {
5964
+ return this.resolveWorkspaceDirectoryForParts(config.machineName ?? this.options.machineDisplayName, config.machineId, config.agentName, agentId);
5965
+ }
5966
+ resolveWorkspaceDirectoryForParts(machineName, machineId, agentName, agentId) {
5967
+ const directoryName = namedWorkspaceDirectoryName(agentName, agentId, "agent");
5968
+ return this.resolveWorkspaceDirectoryName(machineName, machineId, directoryName);
5969
+ }
5970
+ resolveWorkspaceDirectoryName(machineName, machineId, directoryName) {
5868
5971
  if (!isSafeWorkspaceDirectoryName(directoryName)) throw new Error("unsafe workspace directory name");
5869
- const machineRoot = this.resolveMachineRoot(serverNamespace, machineId);
5972
+ const machineRoot = this.resolveMachineRoot(machineName, machineId);
5870
5973
  const resolved = import_node_path13.default.resolve(machineRoot, directoryName);
5871
5974
  if (resolved !== machineRoot && !resolved.startsWith(`${machineRoot}${import_node_path13.default.sep}`)) {
5872
5975
  throw new Error("workspace directory escapes machine root");
@@ -6029,6 +6132,12 @@ async function summarizeDirectory(root) {
6029
6132
  await visit(root);
6030
6133
  return { fileCount, totalSizeBytes, lastModified };
6031
6134
  }
6135
+ function agentIdFromDirectoryName(directoryName, knownAgentIds) {
6136
+ for (const agentId of knownAgentIds) {
6137
+ if (directoryName.endsWith(`-${agentId}`)) return agentId;
6138
+ }
6139
+ return directoryName;
6140
+ }
6032
6141
  async function pathExists(targetPath) {
6033
6142
  try {
6034
6143
  await (0, import_promises12.stat)(targetPath);
@@ -6129,6 +6238,7 @@ async function runDaemon(input) {
6129
6238
  let connection;
6130
6239
  const manager = new AgentProcessManager({
6131
6240
  rootDir: input.rootDir,
6241
+ machineDisplayName: input.name?.trim() || import_node_os9.default.hostname() || "machine",
6132
6242
  sendToServer: (msg) => connection.send(msg),
6133
6243
  log
6134
6244
  });