@clawos-dev/clawd 0.2.164 → 0.2.165
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/dist/cli.cjs +202 -43
- package/dist/dispatch/mcp-server.cjs +6 -1
- package/package.json +1 -1
package/dist/cli.cjs
CHANGED
|
@@ -105,6 +105,12 @@ var init_methods = __esm({
|
|
|
105
105
|
"persona:get",
|
|
106
106
|
"persona:update",
|
|
107
107
|
"persona:delete",
|
|
108
|
+
// ---- personaDispatch:* (跨设备 dispatch) ----
|
|
109
|
+
// personaDispatch:run 有两个入口:本地 cc 经 /api/rpc(EXPOSED_RPC_METHODS,127.0.0.1 无 token);
|
|
110
|
+
// 跨设备时 A 的 peer daemon 经 /rpc(Bearer connect token)。后者要求它是 WS METHOD_NAME +
|
|
111
|
+
// METHOD_GRANT_MAP capability-scoped(handler 自检 persona.public + assertGrant(send))。
|
|
112
|
+
// personaDispatch:complete 仅 B 本地 cc 调(/api/rpc),不跨设备,故不列入 WS method。
|
|
113
|
+
"personaDispatch:run",
|
|
108
114
|
// ---- session:pty 双向透传(仅 CLAWD_CC_MODE=tui 才生效,对应 session:pty push 帧的上行) ----
|
|
109
115
|
// session:pty:input — UI 把用户键盘字节(base64 UTF-8)发给 daemon,daemon 直写 pty stdin。
|
|
110
116
|
// 高频帧(每次按键 1 帧),不入 reducer / 不广播。response 是 fire-and-forget 风格 ack。
|
|
@@ -5982,7 +5988,10 @@ var init_dispatch = __esm({
|
|
|
5982
5988
|
]);
|
|
5983
5989
|
DispatchRunArgsSchema = external_exports.object({
|
|
5984
5990
|
targetPersona: external_exports.string().min(1),
|
|
5985
|
-
prompt: external_exports.string()
|
|
5991
|
+
prompt: external_exports.string(),
|
|
5992
|
+
// 跨设备 dispatch:非空 = A 角色,转发到该 contact deviceId 的 peer daemon;
|
|
5993
|
+
// 省略 = 本地 dispatch / B 角色本地执行。A 转发给 B 时 body 不带此字段。
|
|
5994
|
+
targetDeviceId: external_exports.string().min(1).optional()
|
|
5986
5995
|
});
|
|
5987
5996
|
DispatchCompleteArgsSchema = external_exports.object({
|
|
5988
5997
|
dispatchId: external_exports.string().min(1),
|
|
@@ -32142,6 +32151,14 @@ tool \u4F1A\u963B\u585E\u6302\u8D77\u7B49\u5BF9\u65B9\u4EBA\u683C\u5E72\u5B8C\uF
|
|
|
32142
32151
|
- \u4E00\u53E5\u8BDD\u91CC\u591A\u4E2A \`@persona/<id>\` \u65F6\u9009\u7B2C\u4E00\u4E2A\uFF08tool \u4E00\u6B21\u53EA\u5904\u7406\u4E00\u4E2A target\uFF09
|
|
32143
32152
|
- \u59D4\u6D3E\u5931\u8D25\uFF08tool_result.is_error=true\uFF09\u65F6\u5982\u5B9E\u544A\u8BC9\u7528\u6237\u539F\u56E0\uFF0C**\u4E0D\u8981\u91CD\u8BD5**
|
|
32144
32153
|
|
|
32154
|
+
### \u8DE8\u8BBE\u5907\u59D4\u6D3E\uFF08remote persona\uFF09
|
|
32155
|
+
|
|
32156
|
+
\u5F53 @ token \u5F62\u5982 \`@persona/<id>@<\u5BF9\u65B9>\`\uFF08\`<\u5BF9\u65B9>\` \u662F\u8054\u7CFB\u4EBA\u540D\uFF09\u65F6\uFF0C\u662F\u628A\u4EFB\u52A1\u59D4\u6D3E\u7ED9\u90A3\u53F0\u8BBE\u5907\u4E0A\u7684 persona\u3002\u9664 targetPersona / prompt \u5916\u518D\u5E26\u4E00\u4E2A\u53C2\u6570\uFF1A
|
|
32157
|
+
|
|
32158
|
+
- \`targetDeviceId = <\u5BF9\u65B9\u5BF9\u5E94\u7684 contact deviceId>\`
|
|
32159
|
+
|
|
32160
|
+
\u4F60\u4E0D\u77E5\u9053\u786E\u5207 deviceId \u65F6\u6309\u8054\u7CFB\u4EBA\u540D\u539F\u6837\u586B \`targetDeviceId\`\uFF0Cdaemon \u4F1A\u89E3\u6790\uFF1B\u89E3\u6790\u4E0D\u5230 / \u5BF9\u65B9\u672A\u6388\u6743\u4F1A\u8FD4\u56DE\u660E\u786E\u9519\u8BEF\uFF0C\u7167\u5E38\u628A\u9519\u8BEF\u8F6C\u8FF0\u7ED9\u7528\u6237\u5373\u53EF\u3002\u5176\u4F59\u884C\u4E3A\uFF08\u963B\u585E\u7B49\u7ED3\u679C\u3001\u5931\u8D25\u4E0D\u91CD\u8BD5\uFF09\u4E0E\u672C\u5730\u59D4\u6D3E\u4E00\u81F4\u3002
|
|
32161
|
+
|
|
32145
32162
|
## \u88AB dispatch \u63A5\u5230\u4EFB\u52A1
|
|
32146
32163
|
|
|
32147
32164
|
\u5982\u679C\u4F60\u7684 session \u7B2C\u4E00\u6761 user message \u662F\u4EE5 \`[Dispatched from owner \u2014 another persona session\u59D4\u6D3E\u7ED9\u4F60\u6267\u884C\u4EFB\u52A1]\` \u8D77\u5934\u7684 plain text \u4EFB\u52A1\u5305\uFF0C\u4F60\u662F\u88AB\u53E6\u4E00\u4E2A persona \u59D4\u6D3E\u6765\u5E72\u6D3B\u7684\uFF08\u4EFB\u52A1\u5305\u4E0D\u7528 \`<system-reminder>\` \u5305\u662F\u4E3A\u4E86 reducer dedup \u5DE5\u4F5C\u6B63\u5E38\uFF0C\u89C1 manager.ts buildDispatchTaskPack \u6CE8\u91CA\uFF09\uFF1A
|
|
@@ -34666,7 +34683,12 @@ var SessionManager = class {
|
|
|
34666
34683
|
throw new Error("createDispatchedSession: personaDispatchManager missing in ManagerDeps");
|
|
34667
34684
|
}
|
|
34668
34685
|
const sessionId = newSessionId();
|
|
34669
|
-
const scope = {
|
|
34686
|
+
const scope = args.guestPrincipalId ? {
|
|
34687
|
+
kind: "persona",
|
|
34688
|
+
personaId: args.targetPersona,
|
|
34689
|
+
mode: "guest",
|
|
34690
|
+
capId: args.guestPrincipalId
|
|
34691
|
+
} : {
|
|
34670
34692
|
kind: "persona",
|
|
34671
34693
|
personaId: args.targetPersona,
|
|
34672
34694
|
mode: "owner"
|
|
@@ -34679,6 +34701,13 @@ var SessionManager = class {
|
|
|
34679
34701
|
tool: "claude",
|
|
34680
34702
|
ownerPersonaId: args.targetPersona,
|
|
34681
34703
|
dispatchedFromSessionId: args.sourceSessionId,
|
|
34704
|
+
// 跨设备显示关键(spec §8):creatorPrincipalId = A 的 deviceId,让 A 的
|
|
34705
|
+
// useRemoteSessionsByOwner(按 creatorPrincipalId === A.deviceId 过滤)能 list 到此 B session。
|
|
34706
|
+
// creatorDisplayName = A 的 displayName,供 guest meta callerDisplayName(reducer 要求)。
|
|
34707
|
+
...args.guestPrincipalId ? {
|
|
34708
|
+
creatorPrincipalId: args.guestPrincipalId,
|
|
34709
|
+
...args.guestDisplayName ? { creatorDisplayName: args.guestDisplayName } : {}
|
|
34710
|
+
} : {},
|
|
34682
34711
|
createdAt: now,
|
|
34683
34712
|
updatedAt: now
|
|
34684
34713
|
};
|
|
@@ -37908,12 +37937,104 @@ function resolveClawdTicketsApiKey() {
|
|
|
37908
37937
|
|
|
37909
37938
|
// src/handlers/persona-dispatch.ts
|
|
37910
37939
|
init_protocol();
|
|
37940
|
+
|
|
37941
|
+
// src/permission/capability.ts
|
|
37942
|
+
function matchResource(grant, target) {
|
|
37943
|
+
if (grant.type === "*") return true;
|
|
37944
|
+
if (grant.type !== target.type) return false;
|
|
37945
|
+
if (grant.id === "*") return true;
|
|
37946
|
+
return grant.id === target.id;
|
|
37947
|
+
}
|
|
37948
|
+
function assertGrant(grants, resource, action) {
|
|
37949
|
+
for (const g2 of grants) {
|
|
37950
|
+
if (!matchResource(g2.resource, resource)) continue;
|
|
37951
|
+
if (g2.actions.includes(action)) return true;
|
|
37952
|
+
if (g2.actions.includes("admin")) return true;
|
|
37953
|
+
}
|
|
37954
|
+
return false;
|
|
37955
|
+
}
|
|
37956
|
+
|
|
37957
|
+
// src/permission/session-access.ts
|
|
37958
|
+
function canAccessSession(ctx, sessionId, action, deps) {
|
|
37959
|
+
if (ctx.principal.kind === "owner") return true;
|
|
37960
|
+
const file = deps.readSession(sessionId);
|
|
37961
|
+
if (!file) return true;
|
|
37962
|
+
return canAccessPersona(ctx.grants, file.ownerPersonaId, action);
|
|
37963
|
+
}
|
|
37964
|
+
function canAccessPersona(grants, personaId, action) {
|
|
37965
|
+
if (!personaId) return false;
|
|
37966
|
+
const resource = { type: "persona", id: personaId };
|
|
37967
|
+
return assertGrant(grants, resource, action);
|
|
37968
|
+
}
|
|
37969
|
+
|
|
37970
|
+
// src/handlers/persona-dispatch.ts
|
|
37911
37971
|
function buildPersonaDispatchHandlers(deps) {
|
|
37912
37972
|
const { personaDispatchManager: mgr, spawnB, logger } = deps;
|
|
37913
|
-
const run = async (frame) => {
|
|
37973
|
+
const run = async (frame, _client, ctx) => {
|
|
37914
37974
|
const { type: _t, requestId: _r, ...rest } = frame;
|
|
37915
37975
|
const sourceSessionId = typeof rest.sessionId === "string" ? rest.sessionId : void 0;
|
|
37916
37976
|
const args = DispatchRunArgsSchema.parse(rest);
|
|
37977
|
+
if (args.targetDeviceId) {
|
|
37978
|
+
if (ctx?.principal.kind === "guest") {
|
|
37979
|
+
throw new ClawdError(
|
|
37980
|
+
ERROR_CODES.UNAUTHORIZED,
|
|
37981
|
+
"cross-device forward cannot be initiated by a guest"
|
|
37982
|
+
);
|
|
37983
|
+
}
|
|
37984
|
+
if (!deps.forwardToPeer) {
|
|
37985
|
+
throw new Error("cross-device dispatch not wired (forwardToPeer missing)");
|
|
37986
|
+
}
|
|
37987
|
+
logger?.info("dispatch.run.forward", {
|
|
37988
|
+
targetDeviceId: args.targetDeviceId,
|
|
37989
|
+
targetPersona: args.targetPersona
|
|
37990
|
+
});
|
|
37991
|
+
const outcome2 = await deps.forwardToPeer({
|
|
37992
|
+
targetDeviceId: args.targetDeviceId,
|
|
37993
|
+
targetPersona: args.targetPersona,
|
|
37994
|
+
prompt: args.prompt
|
|
37995
|
+
});
|
|
37996
|
+
return {
|
|
37997
|
+
response: { type: "personaDispatch:run:ok", outcome: outcome2 }
|
|
37998
|
+
};
|
|
37999
|
+
}
|
|
38000
|
+
if (ctx?.principal.kind === "guest") {
|
|
38001
|
+
const isPublic = deps.getPersonaPublic?.(args.targetPersona) ?? false;
|
|
38002
|
+
if (!isPublic || !canAccessPersona(ctx.grants, args.targetPersona, "send")) {
|
|
38003
|
+
throw new ClawdError(
|
|
38004
|
+
ERROR_CODES.UNAUTHORIZED,
|
|
38005
|
+
`persona not dispatchable: ${args.targetPersona}`
|
|
38006
|
+
);
|
|
38007
|
+
}
|
|
38008
|
+
const { dispatchId: dispatchId2 } = mgr.start({
|
|
38009
|
+
sourceSessionId: ctx.principal.id,
|
|
38010
|
+
targetPersona: args.targetPersona
|
|
38011
|
+
});
|
|
38012
|
+
logger?.info("dispatch.run.received.guest", {
|
|
38013
|
+
dispatchId: dispatchId2,
|
|
38014
|
+
sourcePrincipal: ctx.principal.id,
|
|
38015
|
+
targetPersona: args.targetPersona,
|
|
38016
|
+
promptLen: args.prompt.length
|
|
38017
|
+
});
|
|
38018
|
+
void spawnB({
|
|
38019
|
+
dispatchId: dispatchId2,
|
|
38020
|
+
sourceSessionId: ctx.principal.id,
|
|
38021
|
+
targetPersona: args.targetPersona,
|
|
38022
|
+
prompt: args.prompt,
|
|
38023
|
+
guestPrincipalId: ctx.principal.id,
|
|
38024
|
+
guestDisplayName: ctx.principal.displayName
|
|
38025
|
+
}).then(() => logger?.info("dispatch.spawnB.ok", { dispatchId: dispatchId2 })).catch((err) => {
|
|
38026
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
38027
|
+
logger?.warn("dispatch.spawnB.failed", { dispatchId: dispatchId2, reason });
|
|
38028
|
+
mgr.complete(dispatchId2, {
|
|
38029
|
+
kind: "failure",
|
|
38030
|
+
reason: `failed to spawn B: ${reason}`
|
|
38031
|
+
});
|
|
38032
|
+
});
|
|
38033
|
+
const outcome2 = await mgr.wait(dispatchId2);
|
|
38034
|
+
return {
|
|
38035
|
+
response: { type: "personaDispatch:run:ok", outcome: outcome2 }
|
|
38036
|
+
};
|
|
38037
|
+
}
|
|
37917
38038
|
if (!sourceSessionId) {
|
|
37918
38039
|
throw new Error(
|
|
37919
38040
|
"personaDispatch:run requires sessionId (caller must pass x-clawd-session-id header)"
|
|
@@ -37972,6 +38093,46 @@ function buildPersonaDispatchHandlers(deps) {
|
|
|
37972
38093
|
};
|
|
37973
38094
|
}
|
|
37974
38095
|
|
|
38096
|
+
// src/dispatch/peer-forward.ts
|
|
38097
|
+
function wsUrlToHttp(url) {
|
|
38098
|
+
if (url.startsWith("wss://")) return "https://" + url.slice("wss://".length);
|
|
38099
|
+
if (url.startsWith("ws://")) return "http://" + url.slice("ws://".length);
|
|
38100
|
+
return url;
|
|
38101
|
+
}
|
|
38102
|
+
async function forwardDispatchToPeer(args) {
|
|
38103
|
+
const f = args.fetchImpl ?? fetch;
|
|
38104
|
+
const base = wsUrlToHttp(args.contact.remoteUrl).replace(/\/+$/, "");
|
|
38105
|
+
const url = `${base}/rpc/personaDispatch:run`;
|
|
38106
|
+
let res;
|
|
38107
|
+
try {
|
|
38108
|
+
res = await f(url, {
|
|
38109
|
+
method: "POST",
|
|
38110
|
+
headers: {
|
|
38111
|
+
"content-type": "application/json",
|
|
38112
|
+
authorization: `Bearer ${args.contact.connectToken}`
|
|
38113
|
+
},
|
|
38114
|
+
// 注意:不带 targetDeviceId —— B 端据此判定为本地执行(B 角色)。
|
|
38115
|
+
body: JSON.stringify({ targetPersona: args.targetPersona, prompt: args.prompt })
|
|
38116
|
+
});
|
|
38117
|
+
} catch (err) {
|
|
38118
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
38119
|
+
return { kind: "failure", reason: `forward to peer failed: ${msg}` };
|
|
38120
|
+
}
|
|
38121
|
+
let json;
|
|
38122
|
+
try {
|
|
38123
|
+
json = await res.json();
|
|
38124
|
+
} catch {
|
|
38125
|
+
return {
|
|
38126
|
+
kind: "failure",
|
|
38127
|
+
reason: `peer returned non-JSON response (HTTP ${res.status})`
|
|
38128
|
+
};
|
|
38129
|
+
}
|
|
38130
|
+
if (json.ok === false) {
|
|
38131
|
+
return { kind: "failure", reason: `peer rejected: ${json.error}: ${json.message}` };
|
|
38132
|
+
}
|
|
38133
|
+
return json.result.outcome;
|
|
38134
|
+
}
|
|
38135
|
+
|
|
37975
38136
|
// src/tools/codex-history.ts
|
|
37976
38137
|
var import_node_child_process5 = require("child_process");
|
|
37977
38138
|
var DEFAULT_TIMEOUT_MS = 8e3;
|
|
@@ -42472,35 +42633,6 @@ function forkSession(input) {
|
|
|
42472
42633
|
return { forkedToolSessionId, forkedFilePath };
|
|
42473
42634
|
}
|
|
42474
42635
|
|
|
42475
|
-
// src/permission/capability.ts
|
|
42476
|
-
function matchResource(grant, target) {
|
|
42477
|
-
if (grant.type === "*") return true;
|
|
42478
|
-
if (grant.type !== target.type) return false;
|
|
42479
|
-
if (grant.id === "*") return true;
|
|
42480
|
-
return grant.id === target.id;
|
|
42481
|
-
}
|
|
42482
|
-
function assertGrant(grants, resource, action) {
|
|
42483
|
-
for (const g2 of grants) {
|
|
42484
|
-
if (!matchResource(g2.resource, resource)) continue;
|
|
42485
|
-
if (g2.actions.includes(action)) return true;
|
|
42486
|
-
if (g2.actions.includes("admin")) return true;
|
|
42487
|
-
}
|
|
42488
|
-
return false;
|
|
42489
|
-
}
|
|
42490
|
-
|
|
42491
|
-
// src/permission/session-access.ts
|
|
42492
|
-
function canAccessSession(ctx, sessionId, action, deps) {
|
|
42493
|
-
if (ctx.principal.kind === "owner") return true;
|
|
42494
|
-
const file = deps.readSession(sessionId);
|
|
42495
|
-
if (!file) return true;
|
|
42496
|
-
return canAccessPersona(ctx.grants, file.ownerPersonaId, action);
|
|
42497
|
-
}
|
|
42498
|
-
function canAccessPersona(grants, personaId, action) {
|
|
42499
|
-
if (!personaId) return false;
|
|
42500
|
-
const resource = { type: "persona", id: personaId };
|
|
42501
|
-
return assertGrant(grants, resource, action);
|
|
42502
|
-
}
|
|
42503
|
-
|
|
42504
42636
|
// src/handlers/session.ts
|
|
42505
42637
|
init_protocol();
|
|
42506
42638
|
function buildSessionHandlers(deps) {
|
|
@@ -46138,6 +46270,10 @@ var METHOD_GRANT_MAP = {
|
|
|
46138
46270
|
"persona:get": CAPABILITY_SCOPED,
|
|
46139
46271
|
"persona:update": ADMIN_ANY,
|
|
46140
46272
|
"persona:delete": ADMIN_ANY,
|
|
46273
|
+
// 跨设备 dispatch:A 的 peer daemon 凭 connect token 经 /rpc 调进来;dispatcher 放行,
|
|
46274
|
+
// run handler 内做 persona.public + assertGrant(send) 双重校验(guest grants 是 wildcard,
|
|
46275
|
+
// 单 assertGrant 不够)。本地 cc 仍走 /api/rpc(EXPOSED_RPC_METHODS),不经此表。
|
|
46276
|
+
"personaDispatch:run": CAPABILITY_SCOPED,
|
|
46141
46277
|
// 2026-05-26 修 clawd people TUI 渲染 mix 串扰回归(参考 f1133ff0 原 owner 路径
|
|
46142
46278
|
// snapshot/buffer/flush 修复):之前三件套 ADMIN_ANY 把 guest 拦在 dispatcher 401,
|
|
46143
46279
|
// 导致 XtermPanel 重挂载拉 snapshot 在 guest 路径不生效,老的字节流断裂串扰回来。
|
|
@@ -47212,29 +47348,52 @@ async function startDaemon(config) {
|
|
|
47212
47348
|
sourceSessionId: args.sourceSessionId
|
|
47213
47349
|
});
|
|
47214
47350
|
const sourceFile = manager.findOwnedSession(args.sourceSessionId);
|
|
47215
|
-
if (!sourceFile) {
|
|
47351
|
+
if (!sourceFile && !args.guestPrincipalId) {
|
|
47216
47352
|
throw new Error(`personaDispatch:run source session not found: ${args.sourceSessionId}`);
|
|
47217
47353
|
}
|
|
47218
|
-
|
|
47219
|
-
|
|
47220
|
-
|
|
47221
|
-
|
|
47222
|
-
|
|
47223
|
-
|
|
47224
|
-
|
|
47354
|
+
let sourceJsonlPath = "(no transcript yet \u2014 operate from the task description alone)";
|
|
47355
|
+
if (sourceFile && sourceFile.toolSessionId) {
|
|
47356
|
+
sourceJsonlPath = import_node_path48.default.join(
|
|
47357
|
+
import_node_os20.default.homedir(),
|
|
47358
|
+
".claude",
|
|
47359
|
+
"projects",
|
|
47360
|
+
cwdToHashDir(sourceFile.cwd),
|
|
47361
|
+
`${sourceFile.toolSessionId}.jsonl`
|
|
47362
|
+
);
|
|
47363
|
+
}
|
|
47225
47364
|
logger.info("dispatch.spawnB.source-resolved", {
|
|
47226
47365
|
dispatchId: args.dispatchId,
|
|
47227
47366
|
sourceJsonlPath,
|
|
47228
|
-
hasToolSessionId: Boolean(sourceFile
|
|
47367
|
+
hasToolSessionId: Boolean(sourceFile?.toolSessionId)
|
|
47229
47368
|
});
|
|
47230
47369
|
manager.createDispatchedSession({
|
|
47231
47370
|
dispatchId: args.dispatchId,
|
|
47232
47371
|
sourceSessionId: args.sourceSessionId,
|
|
47233
47372
|
targetPersona: args.targetPersona,
|
|
47234
47373
|
prompt: args.prompt,
|
|
47235
|
-
sourceJsonlPath
|
|
47374
|
+
sourceJsonlPath,
|
|
47375
|
+
// B 角色(跨设备):非空 → guest scope + creatorPrincipalId/creatorDisplayName = A。
|
|
47376
|
+
guestPrincipalId: args.guestPrincipalId,
|
|
47377
|
+
guestDisplayName: args.guestDisplayName
|
|
47236
47378
|
});
|
|
47237
|
-
}
|
|
47379
|
+
},
|
|
47380
|
+
// A 角色:从 ContactStore 取 peer 可达 URL + connect token,转发到对端 daemon /rpc。
|
|
47381
|
+
forwardToPeer: async ({ targetDeviceId, targetPersona, prompt }) => {
|
|
47382
|
+
const contact = contactStore.get(targetDeviceId);
|
|
47383
|
+
if (!contact || !contact.remoteUrl || !contact.connectToken) {
|
|
47384
|
+
return {
|
|
47385
|
+
kind: "failure",
|
|
47386
|
+
reason: `unknown or unreachable contact: ${targetDeviceId}`
|
|
47387
|
+
};
|
|
47388
|
+
}
|
|
47389
|
+
return forwardDispatchToPeer({
|
|
47390
|
+
contact: { remoteUrl: contact.remoteUrl, connectToken: contact.connectToken },
|
|
47391
|
+
targetPersona,
|
|
47392
|
+
prompt
|
|
47393
|
+
});
|
|
47394
|
+
},
|
|
47395
|
+
// B 角色:判断 targetPersona 是否 public(跨设备授权边界,private 拒)。
|
|
47396
|
+
getPersonaPublic: (personaId) => personaRegistry.get(personaId)?.public ?? false
|
|
47238
47397
|
});
|
|
47239
47398
|
handlers = { ...handlers, ...dispatchHandlers };
|
|
47240
47399
|
const authResolver = new AuthContextResolver({
|
|
@@ -39576,7 +39576,9 @@ async function handlePersonaDispatchToolCall(input, ctx) {
|
|
|
39576
39576
|
},
|
|
39577
39577
|
body: JSON.stringify({
|
|
39578
39578
|
targetPersona: input.targetPersona,
|
|
39579
|
-
prompt: input.prompt
|
|
39579
|
+
prompt: input.prompt,
|
|
39580
|
+
// 跨设备:非空时透传,daemon run handler 据此走 A 角色转发分支。
|
|
39581
|
+
...input.targetDeviceId ? { targetDeviceId: input.targetDeviceId } : {}
|
|
39580
39582
|
})
|
|
39581
39583
|
});
|
|
39582
39584
|
} catch (err) {
|
|
@@ -39708,6 +39710,9 @@ async function main() {
|
|
|
39708
39710
|
targetPersona: external_exports.string().describe("persona id (e.g. persona-bug-fixer)"),
|
|
39709
39711
|
prompt: external_exports.string().describe(
|
|
39710
39712
|
"the task to perform, with the @persona/<id> token stripped out"
|
|
39713
|
+
),
|
|
39714
|
+
targetDeviceId: external_exports.string().optional().describe(
|
|
39715
|
+
"contact deviceId of the remote device when delegating cross-device (omit for a local persona)"
|
|
39711
39716
|
)
|
|
39712
39717
|
}
|
|
39713
39718
|
},
|