@clawos-dev/clawd 0.2.178-beta.358.7f6c694 → 0.2.178

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
@@ -253,7 +253,11 @@ var init_events = __esm({
253
253
  "skill-hint",
254
254
  "meta-text",
255
255
  "attachment-skills",
256
- "attachment-deferred-tools"
256
+ "attachment-deferred-tools",
257
+ // clawd shift v1:scheduler fire 时 daemon 给新 cc 的首条 user message 用
258
+ // <clawd-shift-fire> wrap 起头;classifyUserMessage 识别后路由到 UserMetaLine 折叠渲染,
259
+ // 不显示成 user 气泡(避免 instruction 文本被当 owner 实时对话)。
260
+ "clawd-shift-fire"
257
261
  ];
258
262
  SKILL_SOURCE_VALUES = ["builtin", "global", "project", "plugin"];
259
263
  AGENT_SOURCE_VALUES = [
@@ -4784,6 +4788,16 @@ var init_schemas = __esm({
4784
4788
  * 反查路径走错;按 #750 一族防线处理,跟 ownerPersonaId 同地点 strip)。
4785
4789
  */
4786
4790
  dispatchedFromSessionId: external_exports.string().min(1).optional(),
4791
+ /**
4792
+ * Shift fire 派生的 session 记录调 addShift 的 source cc session id(稳定 key);
4793
+ * sidebar 据此把 fire 出的 session 折叠在 source session 下,跟 dispatchedFromSessionId
4794
+ * 平行但渲染不同 icon(AlarmClock 区别于 dispatch 的 CornerDownRight),让 owner 一眼
4795
+ * 分清"派生于人为委派"还是"派生于定时唤起"。非 shift fire session 缺省。
4796
+ * 跟 dispatchedFromSessionId / forkedFromSessionId 三者互斥(同一 session 不会兼任)。
4797
+ * mirror SessionFile 同 dispatchedFromSessionId 处理(shift fire 本地概念,理论上不会
4798
+ * mirror,留 strip 防御)。
4799
+ */
4800
+ shiftFiredFromSessionId: external_exports.string().min(1).optional(),
4787
4801
  createdAt: external_exports.string().min(1),
4788
4802
  updatedAt: external_exports.string().min(1)
4789
4803
  });
@@ -11230,6 +11244,7 @@ function classifyUserMessage(input) {
11230
11244
  if (SYSTEM_REMINDER_RE.test(text) || OPENSPEC_BLOCK_RE.test(text)) {
11231
11245
  return { meta: "system-reminder" };
11232
11246
  }
11247
+ if (CLAWD_SHIFT_FIRE_RE.test(text)) return { meta: "clawd-shift-fire" };
11233
11248
  if (SKILL_HINT_RE.test(text)) return { meta: "skill-hint" };
11234
11249
  const flags = raw;
11235
11250
  if (flags.isMeta === true || flags.isSynthetic === true) return { meta: "meta-text" };
@@ -11444,7 +11459,7 @@ function readCurrentContent(filePath) {
11444
11459
  return null;
11445
11460
  }
11446
11461
  }
11447
- var import_node_fs10, import_node_os5, import_node_path9, TASK_NOTIFICATION_RE, TASK_ID_RE, TOOL_USE_ID_RE, SLASH_COMMAND_RE, LOCAL_COMMAND_RE, SYSTEM_REMINDER_RE, OPENSPEC_BLOCK_RE, SKILL_HINT_RE, ATTACHMENT_SILENT_SUBTYPES, ClaudeHistoryReader;
11462
+ var import_node_fs10, import_node_os5, import_node_path9, TASK_NOTIFICATION_RE, TASK_ID_RE, TOOL_USE_ID_RE, SLASH_COMMAND_RE, LOCAL_COMMAND_RE, SYSTEM_REMINDER_RE, OPENSPEC_BLOCK_RE, SKILL_HINT_RE, CLAWD_SHIFT_FIRE_RE, ATTACHMENT_SILENT_SUBTYPES, ClaudeHistoryReader;
11448
11463
  var init_claude_history = __esm({
11449
11464
  "src/tools/claude-history.ts"() {
11450
11465
  "use strict";
@@ -11461,6 +11476,7 @@ var init_claude_history = __esm({
11461
11476
  SYSTEM_REMINDER_RE = /^\s*<system-reminder\b/i;
11462
11477
  OPENSPEC_BLOCK_RE = /^\s*<!--\s*OPENSPEC\b/i;
11463
11478
  SKILL_HINT_RE = /^\s*Base directory for this skill:\s/;
11479
+ CLAWD_SHIFT_FIRE_RE = /^\s*<clawd-shift-fire\b/i;
11464
11480
  ATTACHMENT_SILENT_SUBTYPES = /* @__PURE__ */ new Set([
11465
11481
  "hook_additional_context",
11466
11482
  "hook_success",
@@ -41261,6 +41277,22 @@ function composeGuestSandbox(base, userWorkDir, spawnCwd) {
41261
41277
  return s;
41262
41278
  }
41263
41279
 
41280
+ // src/shift/wrap.ts
41281
+ function wrapShiftFirePrompt(args) {
41282
+ return [
41283
+ `<clawd-shift-fire target-persona="${escapeAttr(args.targetPersona)}">`,
41284
+ "This is a scheduled shift fire \u2014 not a live user message.",
41285
+ "Respond as an assistant turn (start your output directly).",
41286
+ "",
41287
+ "Instruction:",
41288
+ args.prompt,
41289
+ "</clawd-shift-fire>"
41290
+ ].join("\n");
41291
+ }
41292
+ function escapeAttr(v2) {
41293
+ return v2.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
41294
+ }
41295
+
41264
41296
  // src/session/reducer.ts
41265
41297
  init_runtime();
41266
41298
 
@@ -41313,7 +41345,7 @@ function buildConnectionPrompt(args) {
41313
41345
  }
41314
41346
  if (args.workDir) {
41315
41347
  result += `
41316
- \u4F60\u7684\u6587\u4EF6\u5DE5\u4F5C\u76EE\u5F55\u662F ${args.workDir}\uFF0C\u6240\u6709\u6587\u4EF6\u8BFB\u5199\u90FD\u653E\u5728\u8FD9\u4E2A\u76EE\u5F55\u4E0B\u3002persona \u76EE\u5F55\u5BF9\u4F60\u53EA\u8BFB\uFF0C\u4E0D\u8981\u5F80\u91CC\u5199\u3002`;
41348
+ \u4F60\u7684\u6587\u4EF6\u5DE5\u4F5C\u76EE\u5F55\u662F ${args.workDir}\uFF0C\u8FD9\u4E2A\u76EE\u5F55\u5BF9\u4F60\u53EF\u8BFB\u53EF\u5199\uFF0C\u6587\u4EF6\u7684\u8BFB\u53D6/\u521B\u5EFA/\u4FEE\u6539\u90FD\u5728\u8FD9\u91CC\u8FDB\u884C\u3002persona \u76EE\u5F55\u5BF9\u4F60\u53EA\u8BFB\uFF0C\u4E0D\u8981\u5F80\u91CC\u5199\u3002`;
41317
41349
  }
41318
41350
  return result;
41319
41351
  }
@@ -43268,10 +43300,14 @@ var SessionManager = class {
43268
43300
  // 强制走 deps.store(即 default scope),不走 scopeForFile:mirror 物理落 default 即可。
43269
43301
  // 远端 creator / origin 字段原值保留(不被本机 ctx 派生覆写)。
43270
43302
  //
43271
- // strip 远端 personaId / dispatchedFromSessionId:mirror SessionFile 上这两个字段都指向
43272
- // 朋友 daemon 上的本地 id,本机不持有对应 persona / 源 session。留着会让按这些字段反查的
43273
- // 本机消费者(MyPersonasSection 按 ownerPersonaId 反查 persona、sidebar 按
43274
- // dispatchedFromSessionId 折叠 sub-session)走错分支。写入前清掉。#750 一族防线。
43303
+ // strip 远端 personaId / dispatchedFromSessionId / shiftFiredFromSessionId:mirror
43304
+ // SessionFile 上这些字段都指向朋友 daemon 上的本地 id,本机不持有对应 persona /
43305
+ // 源 session。留着会让按这些字段反查的本机消费者(MyPersonasSection 按 ownerPersonaId
43306
+ // 反查 persona、sidebar 按 dispatchedFromSessionId / shiftFiredFromSessionId 折叠
43307
+ // sub-session)走错分支。写入前清掉。#750 一族防线。
43308
+ //
43309
+ // shift fire 是本地 daemon scheduler 行为,理论上不产生 mirror SessionFile,但留 strip
43310
+ // 防御(同 dispatchedFromSessionId 处理姿势对齐)。
43275
43311
  //
43276
43312
  // 与 manager.create / manager.update 区别:
43277
43313
  // - 不走 newRunner / attachObserver / cwd 校验(mirror 是纯 metadata 层)
@@ -43280,6 +43316,7 @@ var SessionManager = class {
43280
43316
  const {
43281
43317
  ownerPersonaId: _strippedPid,
43282
43318
  dispatchedFromSessionId: _strippedDsp,
43319
+ shiftFiredFromSessionId: _strippedSft,
43283
43320
  ...rest
43284
43321
  } = file;
43285
43322
  return this.deps.store.write(rest);
@@ -43982,6 +44019,7 @@ var SessionManager = class {
43982
44019
  cwd,
43983
44020
  tool: "claude",
43984
44021
  ownerPersonaId: args.targetPersona,
44022
+ ...args.sourceSessionId ? { shiftFiredFromSessionId: args.sourceSessionId } : {},
43985
44023
  createdAt: now,
43986
44024
  updatedAt: now
43987
44025
  };
@@ -44017,7 +44055,16 @@ var SessionManager = class {
44017
44055
  });
44018
44056
  const runner = this.ensureRunnerForScope(file, scope);
44019
44057
  this.deps.logger?.info("shift.createShiftSession.runner-ready", { sessionId });
44020
- runner.input({ kind: "command", command: { kind: "send", text: args.prompt } });
44058
+ runner.input({
44059
+ kind: "command",
44060
+ command: {
44061
+ kind: "send",
44062
+ text: wrapShiftFirePrompt({
44063
+ prompt: args.prompt,
44064
+ targetPersona: args.targetPersona
44065
+ })
44066
+ }
44067
+ });
44021
44068
  const kill = () => {
44022
44069
  const r = this.runners.get(sessionId);
44023
44070
  if (!r) return;
@@ -47539,7 +47586,8 @@ function createShiftScheduler(deps) {
47539
47586
  shiftId: shift.id,
47540
47587
  targetPersona: shift.targetPersona,
47541
47588
  prompt: shift.prompt,
47542
- timeoutMs
47589
+ timeoutMs,
47590
+ ...shift.createdBySessionId ? { sourceSessionId: shift.createdBySessionId } : {}
47543
47591
  });
47544
47592
  sessionId = spawned.sessionId;
47545
47593
  const startRun = {
@@ -47593,8 +47641,12 @@ function createShiftScheduler(deps) {
47593
47641
 
47594
47642
  // src/shift/spawn.ts
47595
47643
  function createShiftSpawnFn(deps) {
47596
- return async ({ targetPersona, prompt, timeoutMs }) => {
47597
- const created = deps.sessionManager.createShiftSession({ targetPersona, prompt });
47644
+ return async ({ targetPersona, prompt, timeoutMs, sourceSessionId }) => {
47645
+ const created = deps.sessionManager.createShiftSession({
47646
+ targetPersona,
47647
+ prompt,
47648
+ ...sourceSessionId ? { sourceSessionId } : {}
47649
+ });
47598
47650
  let resolveOuter = () => {
47599
47651
  };
47600
47652
  const ended = new Promise((r) => {
@@ -47648,12 +47700,14 @@ function validateSchedule(schedule) {
47648
47700
  function buildShiftInternalHandlers(deps) {
47649
47701
  const add = async (frame) => {
47650
47702
  const persona = readPersonaId(frame);
47703
+ const callerSessionId = typeof frame.sessionId === "string" ? frame.sessionId : void 0;
47651
47704
  const { type: _t, requestId: _r, personaId: _p, sessionId: _s, ...rest } = frame;
47652
47705
  const args = rest;
47653
47706
  validateSchedule(args.schedule);
47654
47707
  const shift = deps.store.add({
47655
47708
  ownerId: deps.ownerIdProvider(),
47656
47709
  createdByPersona: persona,
47710
+ ...callerSessionId ? { createdBySessionId: callerSessionId } : {},
47657
47711
  name: args.name,
47658
47712
  enabled: true,
47659
47713
  schedule: args.schedule,
@@ -55677,22 +55731,11 @@ function buildAppBuilderHandlers(deps) {
55677
55731
  }
55678
55732
 
55679
55733
  // src/handlers/shift.ts
55680
- var UI_DEFAULT_DURATION_MS = 30 * 6e4;
55681
- var UI_MAX_DURATION_MS = 60 * 6e4;
55682
- function pickUiDurationMs(timeoutMs) {
55683
- return Math.min(timeoutMs ?? UI_DEFAULT_DURATION_MS, UI_MAX_DURATION_MS);
55684
- }
55685
- function everyMsToCron(everyMs) {
55686
- if (everyMs <= 6e4) return "* * * * *";
55687
- const minutes = Math.floor(everyMs / 6e4);
55688
- if (minutes < 60) return `*/${minutes} * * * *`;
55689
- const hours = Math.floor(minutes / 60);
55690
- if (hours < 24) return `0 */${hours} * * *`;
55691
- const days = Math.max(1, Math.floor(hours / 24));
55692
- return `0 0 */${days} * *`;
55693
- }
55694
- function daemonShiftToUiShift(d, nowMs) {
55695
- const durationMs = pickUiDurationMs(d.timeoutMs);
55734
+ function daemonShiftToUiShift(d, _nowMs) {
55735
+ if (d.schedule.kind === "at") {
55736
+ const atMs = Date.parse(d.schedule.at);
55737
+ if (Number.isNaN(atMs)) return null;
55738
+ }
55696
55739
  const noteParts = [d.name];
55697
55740
  if (d.prompt && d.prompt.length > 0) {
55698
55741
  const promptPreview = d.prompt.length > 120 ? `${d.prompt.slice(0, 117)}\u2026` : d.prompt;
@@ -55700,49 +55743,12 @@ function daemonShiftToUiShift(d, nowMs) {
55700
55743
  }
55701
55744
  if (!d.enabled) noteParts.push("(disabled)");
55702
55745
  const note = noteParts.join(" \u2014 ");
55703
- switch (d.schedule.kind) {
55704
- case "at": {
55705
- const startMs = Date.parse(d.schedule.at);
55706
- if (Number.isNaN(startMs)) return null;
55707
- if (startMs + durationMs <= nowMs) return null;
55708
- return {
55709
- id: d.id,
55710
- agentId: d.targetPersona,
55711
- schedule: {
55712
- kind: "single",
55713
- start: new Date(startMs).toISOString(),
55714
- end: new Date(startMs + durationMs).toISOString()
55715
- },
55716
- note
55717
- };
55718
- }
55719
- case "cron": {
55720
- return {
55721
- id: d.id,
55722
- agentId: d.targetPersona,
55723
- schedule: {
55724
- kind: "recurring",
55725
- cron: d.schedule.expr,
55726
- ...d.schedule.tz ? { tz: d.schedule.tz } : {},
55727
- durationMs
55728
- },
55729
- note
55730
- };
55731
- }
55732
- case "every": {
55733
- const cron = everyMsToCron(d.schedule.everyMs);
55734
- return {
55735
- id: d.id,
55736
- agentId: d.targetPersona,
55737
- schedule: {
55738
- kind: "recurring",
55739
- cron,
55740
- durationMs
55741
- },
55742
- note
55743
- };
55744
- }
55745
- }
55746
+ return {
55747
+ id: d.id,
55748
+ agentId: d.targetPersona,
55749
+ schedule: d.schedule,
55750
+ note
55751
+ };
55746
55752
  }
55747
55753
  function buildShiftHandlers(deps) {
55748
55754
  return {
@@ -21251,7 +21251,9 @@ async function main() {
21251
21251
  inputSchema: {
21252
21252
  name: external_exports.string().describe("short label shown in shift.json + future UI"),
21253
21253
  schedule: ScheduleSchema,
21254
- prompt: external_exports.string().describe("first user message injected when the new cc starts"),
21254
+ prompt: external_exports.string().describe(
21255
+ "instruction the spawned cc executes on startup (NOT a chat message from the owner). Write what you want the persona to DO or SAY at fire time, in instruction form \u2014 daemon wraps it in a <clawd-shift-fire> tag so the spawned cc treats it as a scheduled action and responds as an assistant turn (no user-bubble appears in the owner's UI)."
21256
+ ),
21255
21257
  targetPersona: external_exports.string().optional().describe("which persona to spawn; defaults to the calling persona"),
21256
21258
  timeoutMs: external_exports.number().optional().describe("hard timeout per fire; default 30 minutes")
21257
21259
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clawos-dev/clawd",
3
- "version": "0.2.178-beta.358.7f6c694",
3
+ "version": "0.2.178",
4
4
  "description": "Standalone clawd daemon — Claude Code (and future Codex) session server over WebSocket",
5
5
  "type": "module",
6
6
  "license": "MIT",