@wagemule/daemon 0.1.3 → 0.1.4
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 +2 -2
- package/dist/main.cjs +156 -11
- package/dist/main.cjs.map +3 -3
- package/package.json +1 -1
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.
|
|
10
|
+
npx -y @wagemule/daemon@0.1.4 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.
|
|
@@ -26,7 +26,7 @@ wm-daemon start --server-url "https://your-server" --api-key "daemon_xxx"
|
|
|
26
26
|
|
|
27
27
|
## Feishu delegation
|
|
28
28
|
|
|
29
|
-
The npm package depends on the official `@larksuite/cli`, so `npx -y @wagemule/daemon@0.1.
|
|
29
|
+
The npm package depends on the official `@larksuite/cli`, so `npx -y @wagemule/daemon@0.1.4 ...` also installs the `lark-cli` binary. When a platform human enables Feishu delegation for an agent, the daemon injects the official Lark/Feishu CLI skills into that agent workspace and writes a local `.wm/lark-cli` wrapper.
|
|
30
30
|
|
|
31
31
|
The wrapper reads the delegated user token from `.wm/feishu/token` at runtime and sets `LARKSUITE_CLI_APP_ID`, `LARKSUITE_CLI_USER_ACCESS_TOKEN`, `LARKSUITE_CLI_DEFAULT_AS=user`, `LARKSUITE_CLI_STRICT_MODE=user`, and `LARKSUITE_CLI_BRAND=feishu`. The token is not embedded in the wrapper. If the token is missing or cleared, `lark-cli` exits with `Feishu token unavailable`.
|
|
32
32
|
|
package/dist/main.cjs
CHANGED
|
@@ -612,7 +612,18 @@ ${userPrompt}`;
|
|
|
612
612
|
}
|
|
613
613
|
async handleServerRequest(request) {
|
|
614
614
|
if (request.method === "session/request_permission") {
|
|
615
|
-
|
|
615
|
+
const runInput = this.activePrompt?.runInput ?? this.startInput;
|
|
616
|
+
if (this.activePermissionMode === "full_access") {
|
|
617
|
+
return permissionOutcome(selectPermissionOption(request.params, true));
|
|
618
|
+
}
|
|
619
|
+
if (!runInput?.requestPermission) return permissionOutcome(selectPermissionOption(request.params, false));
|
|
620
|
+
const decision = await runInput.requestPermission({
|
|
621
|
+
kind: "session",
|
|
622
|
+
title: "Runtime permission requested",
|
|
623
|
+
description: summarizeAcpPermissionParams(request.params),
|
|
624
|
+
metadata: { method: request.method, params: request.params }
|
|
625
|
+
});
|
|
626
|
+
return permissionOutcome(selectPermissionOption(request.params, decision.action === "approve"));
|
|
616
627
|
}
|
|
617
628
|
return {};
|
|
618
629
|
}
|
|
@@ -720,6 +731,14 @@ function emit(events, input, event) {
|
|
|
720
731
|
events.push(event);
|
|
721
732
|
input.onEvent?.(event);
|
|
722
733
|
}
|
|
734
|
+
function summarizeAcpPermissionParams(params) {
|
|
735
|
+
if (!params || typeof params !== "object") return "Runtime requested permission.";
|
|
736
|
+
const record = params;
|
|
737
|
+
const reason = typeof record.reason === "string" ? record.reason : void 0;
|
|
738
|
+
const title = typeof record.title === "string" ? record.title : void 0;
|
|
739
|
+
const command = typeof record.command === "string" ? record.command : void 0;
|
|
740
|
+
return [title, command, reason].filter(Boolean).join("\n") || JSON.stringify(record).slice(0, 500);
|
|
741
|
+
}
|
|
723
742
|
function modelArgs(args, model, enabled = true) {
|
|
724
743
|
if (!enabled || !model || model === "default") return args;
|
|
725
744
|
return [...args, "--model", model];
|
|
@@ -1509,15 +1528,41 @@ var CodexAdapter = class {
|
|
|
1509
1528
|
}
|
|
1510
1529
|
}
|
|
1511
1530
|
async handleServerRequest(request) {
|
|
1512
|
-
|
|
1531
|
+
const runInput = this.activeTurn?.runInput ?? (this.startInput ? this.startInputAsRunInput("") : void 0);
|
|
1532
|
+
if (runInput?.permissionMode === "full_access" || !runInput?.requestPermission) {
|
|
1533
|
+
if (request.method === "item/fileChange/requestApproval") return { decision: "accept" };
|
|
1534
|
+
if (request.method === "item/permissions/requestApproval") return { permissions: {}, scope: "turn" };
|
|
1513
1535
|
return { decision: "accept" };
|
|
1514
1536
|
}
|
|
1537
|
+
if (request.method === "item/fileChange/requestApproval") {
|
|
1538
|
+
const decision = await runInput.requestPermission({
|
|
1539
|
+
kind: "file_change",
|
|
1540
|
+
title: "File change approval requested",
|
|
1541
|
+
description: summarizePermissionParams(request.params),
|
|
1542
|
+
metadata: { method: request.method, params: request.params }
|
|
1543
|
+
});
|
|
1544
|
+
return { decision: decision.action === "approve" ? "accept" : "reject" };
|
|
1545
|
+
}
|
|
1515
1546
|
if (request.method === "item/permissions/requestApproval") {
|
|
1516
|
-
|
|
1547
|
+
const decision = await runInput.requestPermission({
|
|
1548
|
+
kind: "permission",
|
|
1549
|
+
title: "Command permission requested",
|
|
1550
|
+
description: summarizePermissionParams(request.params),
|
|
1551
|
+
metadata: { method: request.method, params: request.params }
|
|
1552
|
+
});
|
|
1553
|
+
return decision.action === "approve" ? { permissions: {}, scope: "turn" } : { permissions: null, scope: "turn" };
|
|
1517
1554
|
}
|
|
1518
1555
|
return { decision: "accept" };
|
|
1519
1556
|
}
|
|
1520
1557
|
};
|
|
1558
|
+
function summarizePermissionParams(params) {
|
|
1559
|
+
if (!params || typeof params !== "object") return "Runtime requested permission.";
|
|
1560
|
+
const record = params;
|
|
1561
|
+
const command = typeof record.command === "string" ? record.command : void 0;
|
|
1562
|
+
const path14 = typeof record.path === "string" ? record.path : void 0;
|
|
1563
|
+
const reason = typeof record.reason === "string" ? record.reason : void 0;
|
|
1564
|
+
return [command, path14, reason].filter(Boolean).join("\n") || JSON.stringify(record).slice(0, 500);
|
|
1565
|
+
}
|
|
1521
1566
|
function codexPermissionOptions(permissionMode) {
|
|
1522
1567
|
if (permissionMode === "full_access") {
|
|
1523
1568
|
return { approvalPolicy: "never", sandbox: "danger-full-access" };
|
|
@@ -2608,7 +2653,7 @@ if (fs.existsSync(envPath)) {
|
|
|
2608
2653
|
|
|
2609
2654
|
const args = process.argv.slice(2);
|
|
2610
2655
|
const [group, command] = args;
|
|
2611
|
-
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";
|
|
2656
|
+
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; wm inbox feishu-draft create/sent/failed";
|
|
2612
2657
|
|
|
2613
2658
|
function valueAfter(name) {
|
|
2614
2659
|
const index = args.indexOf(name);
|
|
@@ -2620,7 +2665,7 @@ function hasFlag(name) {
|
|
|
2620
2665
|
}
|
|
2621
2666
|
|
|
2622
2667
|
function positionalText() {
|
|
2623
|
-
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"]);
|
|
2668
|
+
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", "--delegation-id", "--human-id", "--feishu-message-id", "--chat-id", "--sender-name", "--sender-open-id", "--original-text", "--draft-text", "--risk", "--message-id", "--error"]);
|
|
2624
2669
|
const values = [];
|
|
2625
2670
|
for (let index = 2; index < args.length; index++) {
|
|
2626
2671
|
const arg = args[index];
|
|
@@ -2755,6 +2800,11 @@ function formatMemberFind(payload) {
|
|
|
2755
2800
|
return lines.join("\\n");
|
|
2756
2801
|
}
|
|
2757
2802
|
|
|
2803
|
+
function formatInboxDraft(payload, fallback) {
|
|
2804
|
+
const item = payload.item || {};
|
|
2805
|
+
return \`\${fallback}: \${item.id || "unknown"} status=\${item.status || "unknown"}\`;
|
|
2806
|
+
}
|
|
2807
|
+
|
|
2758
2808
|
async function main() {
|
|
2759
2809
|
const agentId = process.env.WM_AGENT_ID || process.env.WM_DAEMON_AGENT_ID;
|
|
2760
2810
|
if (group === "auth" && command === "whoami") {
|
|
@@ -2845,6 +2895,43 @@ async function main() {
|
|
|
2845
2895
|
printResult(payload, formatMemberFind(payload));
|
|
2846
2896
|
return;
|
|
2847
2897
|
}
|
|
2898
|
+
if (group === "inbox" && command === "feishu-draft") {
|
|
2899
|
+
const action = args[2];
|
|
2900
|
+
if (action === "create") {
|
|
2901
|
+
const draftText = (valueAfter("--draft-text") || await stdin()).trim();
|
|
2902
|
+
const payload = await request("POST", \`/internal/agent/\${agentId}/inbox/feishu-draft\`, {
|
|
2903
|
+
delegation_id: valueAfter("--delegation-id"),
|
|
2904
|
+
human_id: valueAfter("--human-id"),
|
|
2905
|
+
feishu_message_id: valueAfter("--feishu-message-id"),
|
|
2906
|
+
chat_id: valueAfter("--chat-id"),
|
|
2907
|
+
sender_name: valueAfter("--sender-name"),
|
|
2908
|
+
sender_open_id: valueAfter("--sender-open-id"),
|
|
2909
|
+
original_text: valueAfter("--original-text"),
|
|
2910
|
+
draft_text: draftText,
|
|
2911
|
+
risk: valueAfter("--risk"),
|
|
2912
|
+
});
|
|
2913
|
+
printResult(payload, formatInboxDraft(payload, "Feishu draft created"));
|
|
2914
|
+
return;
|
|
2915
|
+
}
|
|
2916
|
+
if (action === "sent") {
|
|
2917
|
+
const inboxId = valueAfter("--id");
|
|
2918
|
+
if (!inboxId) throw new Error("--id is required");
|
|
2919
|
+
const payload = await request("POST", \`/internal/agent/\${agentId}/inbox/feishu-draft/\${encodeURIComponent(inboxId)}/sent\`, {
|
|
2920
|
+
message_id: valueAfter("--message-id"),
|
|
2921
|
+
});
|
|
2922
|
+
printResult(payload, formatInboxDraft(payload, "Feishu draft sent"));
|
|
2923
|
+
return;
|
|
2924
|
+
}
|
|
2925
|
+
if (action === "failed") {
|
|
2926
|
+
const inboxId = valueAfter("--id");
|
|
2927
|
+
if (!inboxId) throw new Error("--id is required");
|
|
2928
|
+
const error = (valueAfter("--error") || await stdin()).trim();
|
|
2929
|
+
if (!error) throw new Error("--error is required");
|
|
2930
|
+
const payload = await request("POST", \`/internal/agent/\${agentId}/inbox/feishu-draft/\${encodeURIComponent(inboxId)}/failed\`, { error });
|
|
2931
|
+
printResult(payload, formatInboxDraft(payload, "Feishu draft failed"));
|
|
2932
|
+
return;
|
|
2933
|
+
}
|
|
2934
|
+
}
|
|
2848
2935
|
if (group === "task" && command === "list") {
|
|
2849
2936
|
const status = valueAfter("--status");
|
|
2850
2937
|
const suffix = status ? \`?status=\${encodeURIComponent(status)}\` : "";
|
|
@@ -3801,7 +3888,7 @@ var import_ws = __toESM(require("ws"));
|
|
|
3801
3888
|
// package.json
|
|
3802
3889
|
var package_default = {
|
|
3803
3890
|
name: "@wagemule/daemon",
|
|
3804
|
-
version: "0.1.
|
|
3891
|
+
version: "0.1.4",
|
|
3805
3892
|
private: false,
|
|
3806
3893
|
description: "Wage Mule local daemon for connecting local agent runtimes to Workspace Server.",
|
|
3807
3894
|
main: "./dist/main.cjs",
|
|
@@ -4891,6 +4978,7 @@ var AgentProcessManager = class {
|
|
|
4891
4978
|
stoppedLaunches = /* @__PURE__ */ new Set();
|
|
4892
4979
|
lastActivities = /* @__PURE__ */ new Map();
|
|
4893
4980
|
pendingMigrations = /* @__PURE__ */ new Map();
|
|
4981
|
+
pendingPermissions = /* @__PURE__ */ new Map();
|
|
4894
4982
|
fakeRuntimeChildren = /* @__PURE__ */ new Map();
|
|
4895
4983
|
feishuDelegationTimers = /* @__PURE__ */ new Map();
|
|
4896
4984
|
modelDetector;
|
|
@@ -4984,6 +5072,8 @@ var AgentProcessManager = class {
|
|
|
4984
5072
|
this.reminderCache.snapshot(message.agentId, message.reminders);
|
|
4985
5073
|
} else if (message.type === "agent:runtime_profile:migration") {
|
|
4986
5074
|
this.pendingMigrations.set(message.agentId, message.migrationKey);
|
|
5075
|
+
} else if (message.type === "agent:permission_decision") {
|
|
5076
|
+
this.resolvePermissionDecision(message.agentId, message.requestId, message.action, message.metadata);
|
|
4987
5077
|
}
|
|
4988
5078
|
}
|
|
4989
5079
|
startFeishuDelegationSchedule(delegation, config) {
|
|
@@ -4994,6 +5084,7 @@ var AgentProcessManager = class {
|
|
|
4994
5084
|
});
|
|
4995
5085
|
if (!delegation.enabled) return;
|
|
4996
5086
|
const intervalMs = Math.max(6e4, delegation.interval_seconds * 1e3);
|
|
5087
|
+
const lookbackMinutes = Math.min(60, Math.max(10, Math.ceil(delegation.interval_seconds * 2 / 60)));
|
|
4997
5088
|
const timer = setInterval(() => {
|
|
4998
5089
|
const wakeMessage = {
|
|
4999
5090
|
target: "feishu:delegation",
|
|
@@ -5006,14 +5097,18 @@ var AgentProcessManager = class {
|
|
|
5006
5097
|
"Feishu delegation tick.",
|
|
5007
5098
|
`Human ID: ${delegation.human_id}`,
|
|
5008
5099
|
`Delegation ID: ${delegation.id}`,
|
|
5009
|
-
|
|
5010
|
-
"
|
|
5100
|
+
`Read Feishu messages from the last ${lookbackMinutes} minutes only. Never send, reply, react, or mutate Feishu during this tick.`,
|
|
5101
|
+
"For each new inbound Feishu message that needs a reply, draft a concise reply and create exactly one inbox item with:",
|
|
5102
|
+
"wm inbox feishu-draft create --delegation-id <delegation_id> --human-id <human_id> --feishu-message-id <message_id> --chat-id <chat_id> --sender-name <name> --sender-open-id <open_id> --original-text <summary> --draft-text <draft>",
|
|
5103
|
+
"Deduplicate by Feishu message_id. If there are no new relevant messages, do nothing.",
|
|
5104
|
+
"Do not call lark-cli im +messages-send or any other Feishu send command unless a later approved inbox task explicitly asks you to send."
|
|
5011
5105
|
].join("\n")
|
|
5012
5106
|
};
|
|
5013
5107
|
void this.enqueueStart({
|
|
5014
5108
|
agentId: delegation.delegate_agent_id,
|
|
5015
5109
|
config: { ...config, feishuDelegationEnabled: true },
|
|
5016
|
-
wakeMessage
|
|
5110
|
+
wakeMessage,
|
|
5111
|
+
deliveredMessages: [wakeMessage]
|
|
5017
5112
|
}).catch((error) => {
|
|
5018
5113
|
this.options.sendToServer({
|
|
5019
5114
|
type: "feishu:wake_failed",
|
|
@@ -5178,6 +5273,44 @@ var AgentProcessManager = class {
|
|
|
5178
5273
|
this.lastActivities.set(msg.agentId, { activity: msg.activity, detail: msg.detail, launchId: msg.launchId, clientSeq: msg.clientSeq });
|
|
5179
5274
|
this.options.sendToServer({ type: "agent:activity", ...msg });
|
|
5180
5275
|
}
|
|
5276
|
+
requestRuntimePermission(agentId, launchId, request) {
|
|
5277
|
+
const requestId = `perm_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 10)}`;
|
|
5278
|
+
this.log(`permission request agent=${agentId} request=${requestId} kind=${request.kind}`);
|
|
5279
|
+
this.sendActivity({
|
|
5280
|
+
agentId,
|
|
5281
|
+
activity: "working",
|
|
5282
|
+
detail: "Waiting for permission",
|
|
5283
|
+
launchId,
|
|
5284
|
+
entries: [{ kind: "status", text: request.title, timestamp: Date.now() }],
|
|
5285
|
+
pendingInboxCount: this.queuedInboxCount(agentId) + 1
|
|
5286
|
+
});
|
|
5287
|
+
this.options.sendToServer({
|
|
5288
|
+
type: "agent:permission_request",
|
|
5289
|
+
agentId,
|
|
5290
|
+
requestId,
|
|
5291
|
+
launchId,
|
|
5292
|
+
kind: request.kind,
|
|
5293
|
+
title: request.title,
|
|
5294
|
+
description: request.description,
|
|
5295
|
+
metadata: request.metadata
|
|
5296
|
+
});
|
|
5297
|
+
return new Promise((resolve) => {
|
|
5298
|
+
const timeout = setTimeout(() => {
|
|
5299
|
+
this.pendingPermissions.delete(requestId);
|
|
5300
|
+
this.log(`permission timeout agent=${agentId} request=${requestId}`);
|
|
5301
|
+
resolve({ action: "reject", metadata: { reason: "permission timeout" } });
|
|
5302
|
+
}, 10 * 6e4);
|
|
5303
|
+
this.pendingPermissions.set(requestId, { agentId, timeout, resolve });
|
|
5304
|
+
});
|
|
5305
|
+
}
|
|
5306
|
+
resolvePermissionDecision(agentId, requestId, action, metadata) {
|
|
5307
|
+
const pending = this.pendingPermissions.get(requestId);
|
|
5308
|
+
if (!pending || pending.agentId !== agentId) return;
|
|
5309
|
+
clearTimeout(pending.timeout);
|
|
5310
|
+
this.pendingPermissions.delete(requestId);
|
|
5311
|
+
this.log(`permission decision agent=${agentId} request=${requestId} action=${action}`);
|
|
5312
|
+
pending.resolve({ action, metadata });
|
|
5313
|
+
}
|
|
5181
5314
|
async ensureAgentWorkspace(agentId, config) {
|
|
5182
5315
|
const running = this.running.get(agentId);
|
|
5183
5316
|
if (running && !config?.feishuDelegationEnabled) return { workspace: running.workspace };
|
|
@@ -5313,6 +5446,7 @@ var AgentProcessManager = class {
|
|
|
5313
5446
|
|
|
5314
5447
|
[Runtime Profile Migration] Your runtime configuration has been updated. Before handling the above, re-ground yourself in the new context, then run: \`${workspace.wrapperPath} profile migration-ack --key ${migrationKey}\`` : basePrompt;
|
|
5315
5448
|
const tracker = new ActivityTracker();
|
|
5449
|
+
const requestPermission = (request) => this.requestRuntimePermission(agentId, launchId, request);
|
|
5316
5450
|
const onEvent = (event) => {
|
|
5317
5451
|
this.log(`runtime event agent=${agentId} type=${event.type}${event.content ? ` text=${compact(event.content)}` : ""}`);
|
|
5318
5452
|
const running = this.running.get(agentId);
|
|
@@ -5391,7 +5525,8 @@ var AgentProcessManager = class {
|
|
|
5391
5525
|
env: runtimeEnv,
|
|
5392
5526
|
runtimeSessionId: config.sessionId,
|
|
5393
5527
|
timeoutMs: 18e4,
|
|
5394
|
-
onEvent
|
|
5528
|
+
onEvent,
|
|
5529
|
+
requestPermission
|
|
5395
5530
|
});
|
|
5396
5531
|
this.log(`runtime spawned agent=${agentId} runtime=${config.runtime} pid=${adapter.pid ?? "-"} persistent=true`);
|
|
5397
5532
|
} else {
|
|
@@ -5418,7 +5553,8 @@ var AgentProcessManager = class {
|
|
|
5418
5553
|
runtimeSessionId: config.sessionId,
|
|
5419
5554
|
timeoutMs: 18e4,
|
|
5420
5555
|
abortSignal: controller.signal,
|
|
5421
|
-
onEvent
|
|
5556
|
+
onEvent,
|
|
5557
|
+
requestPermission
|
|
5422
5558
|
});
|
|
5423
5559
|
}
|
|
5424
5560
|
if (result.runtimeSessionId) {
|
|
@@ -5652,6 +5788,7 @@ var AgentProcessManager = class {
|
|
|
5652
5788
|
this.idleConfigs.delete(agentId);
|
|
5653
5789
|
this.startingInboxes.delete(agentId);
|
|
5654
5790
|
this.runningInboxes.delete(agentId);
|
|
5791
|
+
this.rejectPendingPermissionsForAgent(agentId, "agent stopped");
|
|
5655
5792
|
const running = this.running.get(agentId);
|
|
5656
5793
|
if (!running) {
|
|
5657
5794
|
this.options.sendToServer({ type: "agent:status", agentId, status: "inactive" });
|
|
@@ -5679,6 +5816,14 @@ var AgentProcessManager = class {
|
|
|
5679
5816
|
new Promise((resolve) => setTimeout(resolve, 5e3))
|
|
5680
5817
|
]);
|
|
5681
5818
|
}
|
|
5819
|
+
rejectPendingPermissionsForAgent(agentId, reason) {
|
|
5820
|
+
for (const [requestId, pending] of this.pendingPermissions) {
|
|
5821
|
+
if (pending.agentId !== agentId) continue;
|
|
5822
|
+
clearTimeout(pending.timeout);
|
|
5823
|
+
this.pendingPermissions.delete(requestId);
|
|
5824
|
+
pending.resolve({ action: "reject", metadata: { reason } });
|
|
5825
|
+
}
|
|
5826
|
+
}
|
|
5682
5827
|
async runFakeRuntime(input) {
|
|
5683
5828
|
const { agentId, config, workspace, wakeMessage, abortSignal, launchId } = input;
|
|
5684
5829
|
let child;
|