@clawos-dev/clawd 0.2.164 → 0.2.165-beta.341.c72ef11

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 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。
@@ -4704,9 +4710,6 @@ var init_schemas = __esm({
4704
4710
  // 不受 parent clear / re-spawn 影响);sidebar 据此挂 fork 图标。非 fork session 该字段缺省。
4705
4711
  // 旧字段 forkedFromToolSessionId 已弃用,daemon 启动时一次性迁移老数据
4706
4712
  forkedFromSessionId: external_exports.string().min(1).optional(),
4707
- // 快速问答临时会话标记(spec 2026-06-23-fork-quick-ask):true = 关闭抽屉即删、
4708
- // 不进 sidebar 树、daemon 启动清扫孤儿。普通会话缺省。
4709
- ephemeral: external_exports.boolean().optional(),
4710
4713
  // owner-mode persona session 身份标识;UI 用它在 SessionList 过滤 + jump,
4711
4714
  // daemon 用它做 idempotent dedupe + spawn 时派生 ctx.personaMode='owner'
4712
4715
  ownerPersonaId: external_exports.string().min(1).optional(),
@@ -4995,8 +4998,6 @@ var init_schemas = __esm({
4995
4998
  iconKey: external_exports.string().optional(),
4996
4999
  // session:fork 派生 session 时由 UI 传入源 sessionId,daemon 写到 SessionFile.forkedFromSessionId
4997
5000
  forkedFromSessionId: external_exports.string().min(1).optional(),
4998
- // 快速问答:创建时打 ephemeral 标记,daemon 写到 SessionFile.ephemeral
4999
- ephemeral: external_exports.boolean().optional(),
5000
5001
  // owner-mode persona session:传此字段时 daemon 自行派生 cwd 和启动参数,
5001
5002
  // 且按 ownerPersonaId 做 idempotent dedupe(每 persona 永远只有一个 owner session)
5002
5003
  ownerPersonaId: external_exports.string().min(1).optional()
@@ -5982,7 +5983,10 @@ var init_dispatch = __esm({
5982
5983
  ]);
5983
5984
  DispatchRunArgsSchema = external_exports.object({
5984
5985
  targetPersona: external_exports.string().min(1),
5985
- prompt: external_exports.string()
5986
+ prompt: external_exports.string(),
5987
+ // 跨设备 dispatch:非空 = A 角色,转发到该 contact deviceId 的 peer daemon;
5988
+ // 省略 = 本地 dispatch / B 角色本地执行。A 转发给 B 时 body 不带此字段。
5989
+ targetDeviceId: external_exports.string().min(1).optional()
5986
5990
  });
5987
5991
  DispatchCompleteArgsSchema = external_exports.object({
5988
5992
  dispatchId: external_exports.string().min(1),
@@ -32142,6 +32146,14 @@ tool \u4F1A\u963B\u585E\u6302\u8D77\u7B49\u5BF9\u65B9\u4EBA\u683C\u5E72\u5B8C\uF
32142
32146
  - \u4E00\u53E5\u8BDD\u91CC\u591A\u4E2A \`@persona/<id>\` \u65F6\u9009\u7B2C\u4E00\u4E2A\uFF08tool \u4E00\u6B21\u53EA\u5904\u7406\u4E00\u4E2A target\uFF09
32143
32147
  - \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
32148
 
32149
+ ### \u8DE8\u8BBE\u5907\u59D4\u6D3E\uFF08remote persona\uFF09
32150
+
32151
+ \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
32152
+
32153
+ - \`targetDeviceId = <\u5BF9\u65B9\u5BF9\u5E94\u7684 contact deviceId>\`
32154
+
32155
+ \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
32156
+
32145
32157
  ## \u88AB dispatch \u63A5\u5230\u4EFB\u52A1
32146
32158
 
32147
32159
  \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
@@ -33956,7 +33968,6 @@ var SessionManager = class {
33956
33968
  forkedFromSessionId: args.forkedFromSessionId,
33957
33969
  ownerPersonaId: args.ownerPersonaId,
33958
33970
  appBuilderProject: args.appBuilderProject,
33959
- ephemeral: args.ephemeral,
33960
33971
  creatorPrincipalId,
33961
33972
  creatorDisplayName,
33962
33973
  createdAt: iso,
@@ -34125,19 +34136,6 @@ var SessionManager = class {
34125
34136
  this.writeOwned(updated);
34126
34137
  return { response: updated, broadcast: [] };
34127
34138
  }
34128
- /**
34129
- * 启动清扫:删除所有磁盘残留的 ephemeral(快速问答临时)session。
34130
- * 客户端崩溃 / 没关抽屉会留下永久不可见的孤儿(ephemeral 被 sidebar 过滤,用户自己也删不掉)。
34131
- * 启动时调用——此刻无客户端连接,纯文件删除,不广播。
34132
- */
34133
- sweepEphemeralOnStartup() {
34134
- for (const file of this.listAllOwned()) {
34135
- if (file.ephemeral) {
34136
- this.deleteOwned(file.sessionId);
34137
- this.deps.logger?.info?.(`swept ephemeral session ${file.sessionId}`);
34138
- }
34139
- }
34140
- }
34141
34139
  delete(args) {
34142
34140
  const existing = this.findOwnedSession(args.sessionId);
34143
34141
  if (!existing) throw new ClawdError(ERROR_CODES.SESSION_NOT_FOUND, args.sessionId);
@@ -34666,7 +34664,12 @@ var SessionManager = class {
34666
34664
  throw new Error("createDispatchedSession: personaDispatchManager missing in ManagerDeps");
34667
34665
  }
34668
34666
  const sessionId = newSessionId();
34669
- const scope = {
34667
+ const scope = args.guestPrincipalId ? {
34668
+ kind: "persona",
34669
+ personaId: args.targetPersona,
34670
+ mode: "guest",
34671
+ capId: args.guestPrincipalId
34672
+ } : {
34670
34673
  kind: "persona",
34671
34674
  personaId: args.targetPersona,
34672
34675
  mode: "owner"
@@ -34679,6 +34682,13 @@ var SessionManager = class {
34679
34682
  tool: "claude",
34680
34683
  ownerPersonaId: args.targetPersona,
34681
34684
  dispatchedFromSessionId: args.sourceSessionId,
34685
+ // 跨设备显示关键(spec §8):creatorPrincipalId = A 的 deviceId,让 A 的
34686
+ // useRemoteSessionsByOwner(按 creatorPrincipalId === A.deviceId 过滤)能 list 到此 B session。
34687
+ // creatorDisplayName = A 的 displayName,供 guest meta callerDisplayName(reducer 要求)。
34688
+ ...args.guestPrincipalId ? {
34689
+ creatorPrincipalId: args.guestPrincipalId,
34690
+ ...args.guestDisplayName ? { creatorDisplayName: args.guestDisplayName } : {}
34691
+ } : {},
34682
34692
  createdAt: now,
34683
34693
  updatedAt: now
34684
34694
  };
@@ -37908,12 +37918,104 @@ function resolveClawdTicketsApiKey() {
37908
37918
 
37909
37919
  // src/handlers/persona-dispatch.ts
37910
37920
  init_protocol();
37921
+
37922
+ // src/permission/capability.ts
37923
+ function matchResource(grant, target) {
37924
+ if (grant.type === "*") return true;
37925
+ if (grant.type !== target.type) return false;
37926
+ if (grant.id === "*") return true;
37927
+ return grant.id === target.id;
37928
+ }
37929
+ function assertGrant(grants, resource, action) {
37930
+ for (const g2 of grants) {
37931
+ if (!matchResource(g2.resource, resource)) continue;
37932
+ if (g2.actions.includes(action)) return true;
37933
+ if (g2.actions.includes("admin")) return true;
37934
+ }
37935
+ return false;
37936
+ }
37937
+
37938
+ // src/permission/session-access.ts
37939
+ function canAccessSession(ctx, sessionId, action, deps) {
37940
+ if (ctx.principal.kind === "owner") return true;
37941
+ const file = deps.readSession(sessionId);
37942
+ if (!file) return true;
37943
+ return canAccessPersona(ctx.grants, file.ownerPersonaId, action);
37944
+ }
37945
+ function canAccessPersona(grants, personaId, action) {
37946
+ if (!personaId) return false;
37947
+ const resource = { type: "persona", id: personaId };
37948
+ return assertGrant(grants, resource, action);
37949
+ }
37950
+
37951
+ // src/handlers/persona-dispatch.ts
37911
37952
  function buildPersonaDispatchHandlers(deps) {
37912
37953
  const { personaDispatchManager: mgr, spawnB, logger } = deps;
37913
- const run = async (frame) => {
37954
+ const run = async (frame, _client, ctx) => {
37914
37955
  const { type: _t, requestId: _r, ...rest } = frame;
37915
37956
  const sourceSessionId = typeof rest.sessionId === "string" ? rest.sessionId : void 0;
37916
37957
  const args = DispatchRunArgsSchema.parse(rest);
37958
+ if (args.targetDeviceId) {
37959
+ if (ctx?.principal.kind === "guest") {
37960
+ throw new ClawdError(
37961
+ ERROR_CODES.UNAUTHORIZED,
37962
+ "cross-device forward cannot be initiated by a guest"
37963
+ );
37964
+ }
37965
+ if (!deps.forwardToPeer) {
37966
+ throw new Error("cross-device dispatch not wired (forwardToPeer missing)");
37967
+ }
37968
+ logger?.info("dispatch.run.forward", {
37969
+ targetDeviceId: args.targetDeviceId,
37970
+ targetPersona: args.targetPersona
37971
+ });
37972
+ const outcome2 = await deps.forwardToPeer({
37973
+ targetDeviceId: args.targetDeviceId,
37974
+ targetPersona: args.targetPersona,
37975
+ prompt: args.prompt
37976
+ });
37977
+ return {
37978
+ response: { type: "personaDispatch:run:ok", outcome: outcome2 }
37979
+ };
37980
+ }
37981
+ if (ctx?.principal.kind === "guest") {
37982
+ const isPublic = deps.getPersonaPublic?.(args.targetPersona) ?? false;
37983
+ if (!isPublic || !canAccessPersona(ctx.grants, args.targetPersona, "send")) {
37984
+ throw new ClawdError(
37985
+ ERROR_CODES.UNAUTHORIZED,
37986
+ `persona not dispatchable: ${args.targetPersona}`
37987
+ );
37988
+ }
37989
+ const { dispatchId: dispatchId2 } = mgr.start({
37990
+ sourceSessionId: ctx.principal.id,
37991
+ targetPersona: args.targetPersona
37992
+ });
37993
+ logger?.info("dispatch.run.received.guest", {
37994
+ dispatchId: dispatchId2,
37995
+ sourcePrincipal: ctx.principal.id,
37996
+ targetPersona: args.targetPersona,
37997
+ promptLen: args.prompt.length
37998
+ });
37999
+ void spawnB({
38000
+ dispatchId: dispatchId2,
38001
+ sourceSessionId: ctx.principal.id,
38002
+ targetPersona: args.targetPersona,
38003
+ prompt: args.prompt,
38004
+ guestPrincipalId: ctx.principal.id,
38005
+ guestDisplayName: ctx.principal.displayName
38006
+ }).then(() => logger?.info("dispatch.spawnB.ok", { dispatchId: dispatchId2 })).catch((err) => {
38007
+ const reason = err instanceof Error ? err.message : String(err);
38008
+ logger?.warn("dispatch.spawnB.failed", { dispatchId: dispatchId2, reason });
38009
+ mgr.complete(dispatchId2, {
38010
+ kind: "failure",
38011
+ reason: `failed to spawn B: ${reason}`
38012
+ });
38013
+ });
38014
+ const outcome2 = await mgr.wait(dispatchId2);
38015
+ return {
38016
+ response: { type: "personaDispatch:run:ok", outcome: outcome2 }
38017
+ };
38018
+ }
37917
38019
  if (!sourceSessionId) {
37918
38020
  throw new Error(
37919
38021
  "personaDispatch:run requires sessionId (caller must pass x-clawd-session-id header)"
@@ -37972,6 +38074,46 @@ function buildPersonaDispatchHandlers(deps) {
37972
38074
  };
37973
38075
  }
37974
38076
 
38077
+ // src/dispatch/peer-forward.ts
38078
+ function wsUrlToHttp(url) {
38079
+ if (url.startsWith("wss://")) return "https://" + url.slice("wss://".length);
38080
+ if (url.startsWith("ws://")) return "http://" + url.slice("ws://".length);
38081
+ return url;
38082
+ }
38083
+ async function forwardDispatchToPeer(args) {
38084
+ const f = args.fetchImpl ?? fetch;
38085
+ const base = wsUrlToHttp(args.contact.remoteUrl).replace(/\/+$/, "");
38086
+ const url = `${base}/rpc/personaDispatch:run`;
38087
+ let res;
38088
+ try {
38089
+ res = await f(url, {
38090
+ method: "POST",
38091
+ headers: {
38092
+ "content-type": "application/json",
38093
+ authorization: `Bearer ${args.contact.connectToken}`
38094
+ },
38095
+ // 注意:不带 targetDeviceId —— B 端据此判定为本地执行(B 角色)。
38096
+ body: JSON.stringify({ targetPersona: args.targetPersona, prompt: args.prompt })
38097
+ });
38098
+ } catch (err) {
38099
+ const msg = err instanceof Error ? err.message : String(err);
38100
+ return { kind: "failure", reason: `forward to peer failed: ${msg}` };
38101
+ }
38102
+ let json;
38103
+ try {
38104
+ json = await res.json();
38105
+ } catch {
38106
+ return {
38107
+ kind: "failure",
38108
+ reason: `peer returned non-JSON response (HTTP ${res.status})`
38109
+ };
38110
+ }
38111
+ if (json.ok === false) {
38112
+ return { kind: "failure", reason: `peer rejected: ${json.error}: ${json.message}` };
38113
+ }
38114
+ return json.result.outcome;
38115
+ }
38116
+
37975
38117
  // src/tools/codex-history.ts
37976
38118
  var import_node_child_process5 = require("child_process");
37977
38119
  var DEFAULT_TIMEOUT_MS = 8e3;
@@ -42472,35 +42614,6 @@ function forkSession(input) {
42472
42614
  return { forkedToolSessionId, forkedFilePath };
42473
42615
  }
42474
42616
 
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
42617
  // src/handlers/session.ts
42505
42618
  init_protocol();
42506
42619
  function buildSessionHandlers(deps) {
@@ -46138,6 +46251,10 @@ var METHOD_GRANT_MAP = {
46138
46251
  "persona:get": CAPABILITY_SCOPED,
46139
46252
  "persona:update": ADMIN_ANY,
46140
46253
  "persona:delete": ADMIN_ANY,
46254
+ // 跨设备 dispatch:A 的 peer daemon 凭 connect token 经 /rpc 调进来;dispatcher 放行,
46255
+ // run handler 内做 persona.public + assertGrant(send) 双重校验(guest grants 是 wildcard,
46256
+ // 单 assertGrant 不够)。本地 cc 仍走 /api/rpc(EXPOSED_RPC_METHODS),不经此表。
46257
+ "personaDispatch:run": CAPABILITY_SCOPED,
46141
46258
  // 2026-05-26 修 clawd people TUI 渲染 mix 串扰回归(参考 f1133ff0 原 owner 路径
46142
46259
  // snapshot/buffer/flush 修复):之前三件套 ADMIN_ANY 把 guest 拦在 dispatcher 401,
46143
46260
  // 导致 XtermPanel 重挂载拉 snapshot 在 guest 路径不生效,老的字节流断裂串扰回来。
@@ -47212,29 +47329,52 @@ async function startDaemon(config) {
47212
47329
  sourceSessionId: args.sourceSessionId
47213
47330
  });
47214
47331
  const sourceFile = manager.findOwnedSession(args.sourceSessionId);
47215
- if (!sourceFile) {
47332
+ if (!sourceFile && !args.guestPrincipalId) {
47216
47333
  throw new Error(`personaDispatch:run source session not found: ${args.sourceSessionId}`);
47217
47334
  }
47218
- const sourceJsonlPath = sourceFile.toolSessionId ? import_node_path48.default.join(
47219
- import_node_os20.default.homedir(),
47220
- ".claude",
47221
- "projects",
47222
- cwdToHashDir(sourceFile.cwd),
47223
- `${sourceFile.toolSessionId}.jsonl`
47224
- ) : "(no transcript yet \u2014 operate from the task description alone)";
47335
+ let sourceJsonlPath = "(no transcript yet \u2014 operate from the task description alone)";
47336
+ if (sourceFile && sourceFile.toolSessionId) {
47337
+ sourceJsonlPath = import_node_path48.default.join(
47338
+ import_node_os20.default.homedir(),
47339
+ ".claude",
47340
+ "projects",
47341
+ cwdToHashDir(sourceFile.cwd),
47342
+ `${sourceFile.toolSessionId}.jsonl`
47343
+ );
47344
+ }
47225
47345
  logger.info("dispatch.spawnB.source-resolved", {
47226
47346
  dispatchId: args.dispatchId,
47227
47347
  sourceJsonlPath,
47228
- hasToolSessionId: Boolean(sourceFile.toolSessionId)
47348
+ hasToolSessionId: Boolean(sourceFile?.toolSessionId)
47229
47349
  });
47230
47350
  manager.createDispatchedSession({
47231
47351
  dispatchId: args.dispatchId,
47232
47352
  sourceSessionId: args.sourceSessionId,
47233
47353
  targetPersona: args.targetPersona,
47234
47354
  prompt: args.prompt,
47235
- sourceJsonlPath
47355
+ sourceJsonlPath,
47356
+ // B 角色(跨设备):非空 → guest scope + creatorPrincipalId/creatorDisplayName = A。
47357
+ guestPrincipalId: args.guestPrincipalId,
47358
+ guestDisplayName: args.guestDisplayName
47236
47359
  });
47237
- }
47360
+ },
47361
+ // A 角色:从 ContactStore 取 peer 可达 URL + connect token,转发到对端 daemon /rpc。
47362
+ forwardToPeer: async ({ targetDeviceId, targetPersona, prompt }) => {
47363
+ const contact = contactStore.get(targetDeviceId);
47364
+ if (!contact || !contact.remoteUrl || !contact.connectToken) {
47365
+ return {
47366
+ kind: "failure",
47367
+ reason: `unknown or unreachable contact: ${targetDeviceId}`
47368
+ };
47369
+ }
47370
+ return forwardDispatchToPeer({
47371
+ contact: { remoteUrl: contact.remoteUrl, connectToken: contact.connectToken },
47372
+ targetPersona,
47373
+ prompt
47374
+ });
47375
+ },
47376
+ // B 角色:判断 targetPersona 是否 public(跨设备授权边界,private 拒)。
47377
+ getPersonaPublic: (personaId) => personaRegistry.get(personaId)?.public ?? false
47238
47378
  });
47239
47379
  handlers = { ...handlers, ...dispatchHandlers };
47240
47380
  const authResolver = new AuthContextResolver({
@@ -47424,7 +47564,6 @@ async function startDaemon(config) {
47424
47564
  }
47425
47565
  }
47426
47566
  });
47427
- manager.sweepEphemeralOnStartup();
47428
47567
  await wss.start();
47429
47568
  const url = `ws://${config.host}:${config.port}`;
47430
47569
  let stateSnapshot = {
@@ -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
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clawos-dev/clawd",
3
- "version": "0.2.164",
3
+ "version": "0.2.165-beta.341.c72ef11",
4
4
  "description": "Standalone clawd daemon — Claude Code (and future Codex) session server over WebSocket",
5
5
  "type": "module",
6
6
  "license": "MIT",