@clawos-dev/clawd 0.2.226 → 0.2.228

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
@@ -6136,7 +6136,12 @@ var init_dispatch = __esm({
6136
6136
  prompt: external_exports.string(),
6137
6137
  // 跨设备 dispatch:非空 = A 角色,转发到该 contact deviceId 的 peer daemon;
6138
6138
  // 省略 = 本地 dispatch / B 角色本地执行。A 转发给 B 时 body 不带此字段。
6139
- targetDeviceId: external_exports.string().min(1).optional()
6139
+ targetDeviceId: external_exports.string().min(1).optional(),
6140
+ // 可选:override B session 使用的 claude 模型。缺省 = 用 persona 默认。
6141
+ // 合法值集与 claude adapter capabilities.models[].id 一致(见 daemon
6142
+ // tools/claude.ts CLAUDE_MODEL_IDS);handler 层做二次校验,非法值 fail-fast。
6143
+ // protocol 层保持 string 便于未来跨 tool 扩展(届时 union 收合法字面量)。
6144
+ model: external_exports.string().min(1).optional()
6140
6145
  });
6141
6146
  DispatchCompleteArgsSchema = external_exports.object({
6142
6147
  dispatchId: external_exports.string().min(1),
@@ -12542,7 +12547,7 @@ function encodeClaudeStdin(text) {
12542
12547
  };
12543
12548
  return JSON.stringify(frame) + "\n";
12544
12549
  }
12545
- var import_node_child_process, import_node_child_process2, import_node_fs15, ATTACHMENT_SILENT_SUBTYPES2, unknownTypeHandler, ATTACHMENT_RE, IMAGE_EXT_MIME, CLAUDE_MODELS, CLAUDE_PERMISSION_MODES, CLAUDE_CAPABILITIES, ClaudeAdapter;
12550
+ var import_node_child_process, import_node_child_process2, import_node_fs15, ATTACHMENT_SILENT_SUBTYPES2, unknownTypeHandler, ATTACHMENT_RE, IMAGE_EXT_MIME, CLAUDE_MODEL_IDS, CLAUDE_MODELS, _assertModelIdsAligned, CLAUDE_PERMISSION_MODES, CLAUDE_CAPABILITIES, ClaudeAdapter;
12546
12551
  var init_claude = __esm({
12547
12552
  "src/tools/claude.ts"() {
12548
12553
  "use strict";
@@ -12568,13 +12573,17 @@ var init_claude = __esm({
12568
12573
  gif: "image/gif",
12569
12574
  webp: "image/webp"
12570
12575
  };
12576
+ CLAUDE_MODEL_IDS = ["opus", "fable", "sonnet", "haiku"];
12571
12577
  CLAUDE_MODELS = [
12572
- { id: "", label: "Default", description: "CLI default model", contextWindowSize: 2e5, default: true },
12578
+ { id: "", label: "Default", description: "CLI default model", contextWindowSize: 1e6, default: true },
12573
12579
  { id: "opus", label: "Opus", description: "Most capable", contextWindowSize: 1e6 },
12574
- { id: "fable", label: "Fable", contextWindowSize: 2e5 },
12575
- { id: "sonnet", label: "Sonnet", description: "Fast, balanced", contextWindowSize: 2e5 },
12576
- { id: "haiku", label: "Haiku", description: "Fastest, lightest", contextWindowSize: 2e5 }
12580
+ { id: "fable", label: "Fable", contextWindowSize: 1e6 },
12581
+ { id: "sonnet", label: "Sonnet", description: "Fast, balanced", contextWindowSize: 1e6 },
12582
+ { id: "haiku", label: "Haiku", description: "Fastest, lightest", contextWindowSize: 1e6 }
12577
12583
  ];
12584
+ _assertModelIdsAligned = CLAUDE_MODELS.filter(
12585
+ (m2) => m2.id !== ""
12586
+ ).map((m2) => m2.id);
12578
12587
  CLAUDE_PERMISSION_MODES = [
12579
12588
  { id: "", label: "Default", description: "Ask for every tool call" },
12580
12589
  { id: "plan", label: "Plan", description: "Read-only, no code changes" },
@@ -44602,6 +44611,8 @@ var SessionManager = class {
44602
44611
  creatorPrincipalId: args.guestPrincipalId,
44603
44612
  ...args.guestDisplayName ? { creatorDisplayName: args.guestDisplayName } : {}
44604
44613
  } : {},
44614
+ // T-20:dispatch model override 落盘(reducer spawn 路径读 SessionFile.model 生成 --model flag)
44615
+ ...args.model ? { model: args.model } : {},
44605
44616
  createdAt: now,
44606
44617
  updatedAt: now
44607
44618
  };
@@ -48364,6 +48375,12 @@ function computeNextRunAtMs(schedule, fromMs) {
48364
48375
  }
48365
48376
 
48366
48377
  // src/shift/store.ts
48378
+ function normalizeSchedule(schedule, nowMs) {
48379
+ if (schedule.kind === "every" && schedule.anchorMs === void 0) {
48380
+ return { ...schedule, anchorMs: nowMs };
48381
+ }
48382
+ return schedule;
48383
+ }
48367
48384
  function createShiftStore(deps) {
48368
48385
  let shifts = [];
48369
48386
  let flushTimer = null;
@@ -48382,7 +48399,15 @@ function createShiftStore(deps) {
48382
48399
  if (parsed.version !== 1) {
48383
48400
  throw new Error(`shift.json: unsupported version ${parsed.version}`);
48384
48401
  }
48385
- shifts = parsed.shifts ?? [];
48402
+ const now = deps.now();
48403
+ shifts = (parsed.shifts ?? []).map((s) => {
48404
+ const schedule = normalizeSchedule(s.schedule, s.createdAtMs);
48405
+ if (schedule === s.schedule) return s;
48406
+ const gridNext = computeNextRunAtMs(schedule, now) ?? void 0;
48407
+ const oldNext = s.state.nextRunAtMs;
48408
+ const nextRunAtMs = gridNext !== void 0 && (oldNext === void 0 || gridNext < oldNext) ? gridNext : oldNext;
48409
+ return { ...s, schedule, state: { ...s.state, nextRunAtMs } };
48410
+ });
48386
48411
  }
48387
48412
  async function flushNow() {
48388
48413
  if (flushTimer) {
@@ -48415,9 +48440,11 @@ function createShiftStore(deps) {
48415
48440
  }
48416
48441
  function add(input) {
48417
48442
  const now = deps.now();
48418
- const nextRunAtMs = computeNextRunAtMs(input.schedule, now) ?? void 0;
48443
+ const schedule = normalizeSchedule(input.schedule, now);
48444
+ const nextRunAtMs = computeNextRunAtMs(schedule, now) ?? void 0;
48419
48445
  const shift = {
48420
48446
  ...input,
48447
+ schedule,
48421
48448
  id: (0, import_node_crypto6.randomUUID)(),
48422
48449
  createdAtMs: now,
48423
48450
  updatedAtMs: now,
@@ -48432,8 +48459,8 @@ function createShiftStore(deps) {
48432
48459
  const idx = shifts.findIndex((s) => s.id === id);
48433
48460
  if (idx < 0) throw new Error(`shift not found: ${id}`);
48434
48461
  const now = deps.now();
48435
- const newSchedule = patch.schedule ?? shifts[idx].schedule;
48436
- const nextRunAtMs = patch.schedule ? computeNextRunAtMs(patch.schedule, now) ?? void 0 : shifts[idx].state.nextRunAtMs;
48462
+ const newSchedule = patch.schedule ? normalizeSchedule(patch.schedule, now) : shifts[idx].schedule;
48463
+ const nextRunAtMs = patch.schedule ? computeNextRunAtMs(newSchedule, now) ?? void 0 : shifts[idx].state.nextRunAtMs;
48437
48464
  const merged = {
48438
48465
  ...shifts[idx],
48439
48466
  ...patch,
@@ -48529,13 +48556,10 @@ function createShiftScheduler(deps) {
48529
48556
  async function tickNow() {
48530
48557
  const now = deps.now();
48531
48558
  if (now < lastTickAtMs || now - lastTickAtMs > CLOCK_JUMP_THRESHOLD_MS) {
48532
- for (const shift of deps.store.list()) {
48533
- const next = computeNextRunAtMs(shift.schedule, now) ?? void 0;
48534
- deps.store.patchState(shift.id, { nextRunAtMs: next });
48535
- }
48536
48559
  deps.logger?.warn("shift.scheduler.clock_jump", {
48537
48560
  lastTickAtMs,
48538
- now
48561
+ now,
48562
+ gapMs: now - lastTickAtMs
48539
48563
  });
48540
48564
  }
48541
48565
  lastTickAtMs = now;
@@ -48806,12 +48830,19 @@ function canAccessPersona(grants, personaId, action) {
48806
48830
  }
48807
48831
 
48808
48832
  // src/handlers/persona-dispatch.ts
48833
+ init_claude();
48809
48834
  function buildPersonaDispatchHandlers(deps) {
48810
48835
  const { personaDispatchManager: mgr, spawnB, logger } = deps;
48811
48836
  const run = async (frame, _client, ctx) => {
48812
48837
  const { type: _t, requestId: _r, ...rest } = frame;
48813
48838
  const sourceSessionId = typeof rest.sessionId === "string" ? rest.sessionId : void 0;
48814
48839
  const args = DispatchRunArgsSchema.parse(rest);
48840
+ if (args.model !== void 0 && !CLAUDE_MODEL_IDS.includes(args.model)) {
48841
+ throw new ClawdError(
48842
+ ERROR_CODES.INVALID_PARAM,
48843
+ `model '${args.model}' not supported; allowed: ${CLAUDE_MODEL_IDS.join(", ")}`
48844
+ );
48845
+ }
48815
48846
  if (args.targetDeviceId) {
48816
48847
  if (ctx?.principal.kind === "guest") {
48817
48848
  throw new ClawdError(
@@ -48829,7 +48860,8 @@ function buildPersonaDispatchHandlers(deps) {
48829
48860
  const outcome2 = await deps.forwardToPeer({
48830
48861
  targetDeviceId: args.targetDeviceId,
48831
48862
  targetPersona: args.targetPersona,
48832
- prompt: args.prompt
48863
+ prompt: args.prompt,
48864
+ ...args.model ? { model: args.model } : {}
48833
48865
  });
48834
48866
  return {
48835
48867
  response: { type: "personaDispatch:run:ok", outcome: outcome2 }
@@ -48859,7 +48891,8 @@ function buildPersonaDispatchHandlers(deps) {
48859
48891
  targetPersona: args.targetPersona,
48860
48892
  prompt: args.prompt,
48861
48893
  guestPrincipalId: ctx.principal.id,
48862
- guestDisplayName: ctx.principal.displayName
48894
+ guestDisplayName: ctx.principal.displayName,
48895
+ ...args.model ? { model: args.model } : {}
48863
48896
  }).then(() => logger?.info("dispatch.spawnB.ok", { dispatchId: dispatchId2 })).catch((err) => {
48864
48897
  const reason = err instanceof Error ? err.message : String(err);
48865
48898
  logger?.warn("dispatch.spawnB.failed", { dispatchId: dispatchId2, reason });
@@ -48892,7 +48925,8 @@ function buildPersonaDispatchHandlers(deps) {
48892
48925
  dispatchId,
48893
48926
  sourceSessionId,
48894
48927
  targetPersona: args.targetPersona,
48895
- prompt: args.prompt
48928
+ prompt: args.prompt,
48929
+ ...args.model ? { model: args.model } : {}
48896
48930
  }).then(() => {
48897
48931
  logger?.info("dispatch.spawnB.ok", { dispatchId });
48898
48932
  }).catch((err) => {
@@ -48950,7 +48984,12 @@ async function forwardDispatchToPeer(args) {
48950
48984
  authorization: `Bearer ${args.contact.connectToken}`
48951
48985
  },
48952
48986
  // 注意:不带 targetDeviceId —— B 端据此判定为本地执行(B 角色)。
48953
- body: JSON.stringify({ targetPersona: args.targetPersona, prompt: args.prompt })
48987
+ body: JSON.stringify({
48988
+ targetPersona: args.targetPersona,
48989
+ prompt: args.prompt,
48990
+ // T-20: 条件展开防 undefined 污染 JSON。远端 daemon handler 会再校验一次 model 合法性。
48991
+ ...args.model ? { model: args.model } : {}
48992
+ })
48954
48993
  });
48955
48994
  } catch (err) {
48956
48995
  const msg = err instanceof Error ? err.message : String(err);
@@ -60173,11 +60212,13 @@ async function startDaemon(config) {
60173
60212
  sourceJsonlPath,
60174
60213
  // B 角色(跨设备):非空 → guest scope + creatorPrincipalId/creatorDisplayName = A。
60175
60214
  guestPrincipalId: args.guestPrincipalId,
60176
- guestDisplayName: args.guestDisplayName
60215
+ guestDisplayName: args.guestDisplayName,
60216
+ // T-20: 可选 model override 透到 SessionFile.model
60217
+ ...args.model ? { model: args.model } : {}
60177
60218
  });
60178
60219
  },
60179
60220
  // A 角色:从 ContactStore 取 peer 可达 URL + connect token,转发到对端 daemon /rpc。
60180
- forwardToPeer: async ({ targetDeviceId, targetPersona, prompt }) => {
60221
+ forwardToPeer: async ({ targetDeviceId, targetPersona, prompt, model }) => {
60181
60222
  const contact = contactStore.get(targetDeviceId);
60182
60223
  if (!contact || !contact.remoteUrl || !contact.connectToken) {
60183
60224
  return {
@@ -60188,7 +60229,9 @@ async function startDaemon(config) {
60188
60229
  return forwardDispatchToPeer({
60189
60230
  contact: { remoteUrl: contact.remoteUrl, connectToken: contact.connectToken },
60190
60231
  targetPersona,
60191
- prompt
60232
+ prompt,
60233
+ // T-20: 跨设备也透 model,让远端 daemon 用同一 claude 模型 spawn B
60234
+ ...model ? { model } : {}
60192
60235
  });
60193
60236
  },
60194
60237
  // B 角色:判断 targetPersona 是否 public(跨设备授权边界,private 拒)。