@clawos-dev/clawd 0.2.382 → 0.2.383

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
@@ -450,8 +450,8 @@ var init_parseUtil = __esm({
450
450
  init_errors();
451
451
  init_en();
452
452
  makeIssue = (params) => {
453
- const { data, path: path87, errorMaps, issueData } = params;
454
- const fullPath = [...path87, ...issueData.path || []];
453
+ const { data, path: path89, errorMaps, issueData } = params;
454
+ const fullPath = [...path89, ...issueData.path || []];
455
455
  const fullIssue = {
456
456
  ...issueData,
457
457
  path: fullPath
@@ -762,11 +762,11 @@ var init_types = __esm({
762
762
  init_parseUtil();
763
763
  init_util();
764
764
  ParseInputLazyPath = class {
765
- constructor(parent, value, path87, key) {
765
+ constructor(parent, value, path89, key) {
766
766
  this._cachedPath = [];
767
767
  this.parent = parent;
768
768
  this.data = value;
769
- this._path = path87;
769
+ this._path = path89;
770
770
  this._key = key;
771
771
  }
772
772
  get path() {
@@ -6202,7 +6202,11 @@ var init_dispatch = __esm({
6202
6202
  // 时 daemon 层静默丢弃(避免半途改 label 造成 sidebar 抖动)。
6203
6203
  // 上限 60 字符——sidebar 显示 tolerance,卡在 dispatch 入口即可。
6204
6204
  // 协议层不硬拒 sessionLabel + dispatchId 同传,保留 forward-compat。
6205
- sessionLabel: external_exports.string().min(1).max(60).optional()
6205
+ sessionLabel: external_exports.string().min(1).max(60).optional(),
6206
+ // 上层消费者(Master)挂载话题/线语义:topicId / lineId / 验收标准 / rejectCount 等。
6207
+ // daemon **不解释**内容,只透传进台账行 DispatchRecord.meta(新开)或与既有 meta
6208
+ // merge(续派,见 handlers/persona-dispatch.ts)。与台账行 meta 同款自由 JSON 契约。
6209
+ meta: external_exports.record(external_exports.unknown()).optional()
6206
6210
  }).refine((v2) => v2.targetPersona !== void 0 || v2.dispatchId !== void 0, {
6207
6211
  message: "either targetPersona (new dispatch) or dispatchId (follow-up) is required"
6208
6212
  }).refine((v2) => !(v2.dispatchId !== void 0 && v2.targetDeviceId !== void 0), {
@@ -6594,6 +6598,202 @@ var init_shift_internal = __esm({
6594
6598
  }
6595
6599
  });
6596
6600
 
6601
+ // ../protocol/src/channel.ts
6602
+ var ChannelSchema, nonBlank, ChannelCreateArgsSchema, ChannelUpdateArgsSchema, ChannelDeleteArgsSchema, ChannelListResultSchema, ChannelResultSchema, ChannelDeleteResultSchema;
6603
+ var init_channel = __esm({
6604
+ "../protocol/src/channel.ts"() {
6605
+ "use strict";
6606
+ init_zod();
6607
+ ChannelSchema = external_exports.object({
6608
+ id: external_exports.string().min(1),
6609
+ name: external_exports.string().min(1),
6610
+ /** 该频道里可被派活的 persona */
6611
+ memberPersonaIds: external_exports.array(external_exports.string().min(1)),
6612
+ /** 人类成员(contact deviceId);M2 只存不用,P1 异步询问才消费 */
6613
+ memberContactDeviceIds: external_exports.array(external_exports.string().min(1)),
6614
+ createdAt: external_exports.string().min(1),
6615
+ updatedAt: external_exports.string().min(1)
6616
+ });
6617
+ nonBlank = external_exports.string().trim().min(1);
6618
+ ChannelCreateArgsSchema = external_exports.object({
6619
+ name: nonBlank,
6620
+ memberPersonaIds: external_exports.array(external_exports.string().min(1)).default([]),
6621
+ memberContactDeviceIds: external_exports.array(external_exports.string().min(1)).default([])
6622
+ });
6623
+ ChannelUpdateArgsSchema = external_exports.object({
6624
+ channelId: external_exports.string().min(1),
6625
+ name: nonBlank.optional(),
6626
+ memberPersonaIds: external_exports.array(external_exports.string().min(1)).optional(),
6627
+ memberContactDeviceIds: external_exports.array(external_exports.string().min(1)).optional()
6628
+ });
6629
+ ChannelDeleteArgsSchema = external_exports.object({ channelId: external_exports.string().min(1) });
6630
+ ChannelListResultSchema = external_exports.object({ channels: external_exports.array(ChannelSchema) });
6631
+ ChannelResultSchema = external_exports.object({ channel: ChannelSchema });
6632
+ ChannelDeleteResultSchema = external_exports.object({
6633
+ ok: external_exports.literal(true),
6634
+ /** 一并清掉的已结束话题数:不留悬挂记录,也不静默——删了几条明确回给调用方 */
6635
+ removedTopics: external_exports.number().int().min(0)
6636
+ });
6637
+ }
6638
+ });
6639
+
6640
+ // ../protocol/src/topic.ts
6641
+ var nonBlank2, TOPIC_STATES, TopicStateSchema, TopicActivityEntrySchema, TopicLineMetaSchema, TopicSchema, TopicLineViewSchema, TopicListArgsSchema, TopicGetArgsSchema, TopicCreateArgsSchema, TopicSetPlanArgsSchema, TopicAppendArgsSchema, TopicRejectArgsSchema, TopicAcceptArgsSchema, TopicCloseArgsSchema, TopicListItemSchema, TopicListResultSchema, TopicGetResultSchema, TopicCreateResultSchema, TopicRejectResultSchema, TopicAcceptResultSchema, TopicResultSchema;
6642
+ var init_topic = __esm({
6643
+ "../protocol/src/topic.ts"() {
6644
+ "use strict";
6645
+ init_zod();
6646
+ init_dispatch();
6647
+ nonBlank2 = external_exports.string().trim().min(1);
6648
+ TOPIC_STATES = ["planning", "running", "done", "archived"];
6649
+ TopicStateSchema = external_exports.enum(TOPIC_STATES);
6650
+ TopicActivityEntrySchema = external_exports.object({
6651
+ at: external_exports.string().min(1),
6652
+ actor: external_exports.string().min(1),
6653
+ action: external_exports.string().min(1),
6654
+ target: external_exports.string().optional(),
6655
+ reason: external_exports.string().min(1),
6656
+ /** 该动作确认的产出物(watcher 返回的 artifacts);影响面视图的唯一数据源 */
6657
+ artifacts: external_exports.array(nonBlank2).optional()
6658
+ });
6659
+ TopicLineMetaSchema = external_exports.object({
6660
+ rejectCount: external_exports.number().int().min(0),
6661
+ accepted: external_exports.boolean().default(false)
6662
+ });
6663
+ TopicSchema = external_exports.object({
6664
+ id: external_exports.string().min(1),
6665
+ channelId: external_exports.string().min(1),
6666
+ title: external_exports.string(),
6667
+ /** 该话题的 Master session;topic:create 时一并起,1:1 */
6668
+ masterSessionId: external_exports.string().min(1),
6669
+ goal: external_exports.string(),
6670
+ boundaries: external_exports.string(),
6671
+ acceptanceCriteria: external_exports.array(external_exports.string()),
6672
+ activity: external_exports.array(TopicActivityEntrySchema),
6673
+ /** lineId -> 线级元数据 */
6674
+ lineMeta: external_exports.record(TopicLineMetaSchema),
6675
+ state: TopicStateSchema,
6676
+ createdAt: external_exports.string().min(1),
6677
+ updatedAt: external_exports.string().min(1)
6678
+ });
6679
+ TopicLineViewSchema = external_exports.object({
6680
+ lineId: external_exports.string().min(1),
6681
+ dispatchId: external_exports.string().min(1),
6682
+ workerSessionId: external_exports.string().optional(),
6683
+ assignee: external_exports.string().min(1),
6684
+ focus: external_exports.string(),
6685
+ status: DispatchStatusSchema,
6686
+ acceptance: external_exports.array(external_exports.string()),
6687
+ rejectCount: external_exports.number().int().min(0),
6688
+ /** 已被 Master 判定合格(`topic:accept`)。UI 的「合格」态靠它,不是猜的 */
6689
+ accepted: external_exports.boolean()
6690
+ });
6691
+ TopicListArgsSchema = external_exports.object({ channelId: external_exports.string().min(1) });
6692
+ TopicGetArgsSchema = external_exports.object({
6693
+ topicId: external_exports.string().min(1).optional(),
6694
+ sessionId: external_exports.string().min(1).optional()
6695
+ }).refine((v2) => v2.topicId !== void 0 || v2.sessionId !== void 0, {
6696
+ message: "either topicId or sessionId is required"
6697
+ });
6698
+ TopicCreateArgsSchema = external_exports.object({
6699
+ channelId: external_exports.string().min(1),
6700
+ /** 可后补:spike 动线是老板先说一句话,标题随 topic:setPlan 落 */
6701
+ title: nonBlank2.optional()
6702
+ });
6703
+ TopicSetPlanArgsSchema = external_exports.object({
6704
+ topicId: external_exports.string().min(1),
6705
+ title: nonBlank2.optional(),
6706
+ goal: nonBlank2,
6707
+ boundaries: external_exports.string(),
6708
+ // 没有验收标准就不开工(PRD §6.1):否则 Master 无法自主判定收敛,
6709
+ // 只能照单全收 worker 自述,退化成传话筒。
6710
+ acceptanceCriteria: external_exports.array(nonBlank2).min(1)
6711
+ });
6712
+ TopicAppendArgsSchema = external_exports.object({
6713
+ topicId: external_exports.string().min(1),
6714
+ action: nonBlank2,
6715
+ target: nonBlank2.optional(),
6716
+ reason: nonBlank2,
6717
+ /** 该动作确认的产出物;影响面视图只吃这个字段 */
6718
+ artifacts: external_exports.array(nonBlank2).optional()
6719
+ });
6720
+ TopicRejectArgsSchema = external_exports.object({
6721
+ topicId: external_exports.string().min(1),
6722
+ lineId: nonBlank2,
6723
+ reason: nonBlank2
6724
+ });
6725
+ TopicAcceptArgsSchema = external_exports.object({
6726
+ topicId: external_exports.string().min(1),
6727
+ lineId: nonBlank2,
6728
+ /** 凭什么判合格(逐条标准对应到什么证据)——与打回同样必填 */
6729
+ reason: nonBlank2,
6730
+ /** watcher 认定的产出物,一并进影响面 */
6731
+ artifacts: external_exports.array(nonBlank2).optional()
6732
+ });
6733
+ TopicCloseArgsSchema = external_exports.object({
6734
+ topicId: external_exports.string().min(1),
6735
+ state: external_exports.enum(["done", "archived"])
6736
+ });
6737
+ TopicListItemSchema = TopicSchema.extend({
6738
+ lineCount: external_exports.number().int().min(0)
6739
+ });
6740
+ TopicListResultSchema = external_exports.object({ topics: external_exports.array(TopicListItemSchema) });
6741
+ TopicGetResultSchema = external_exports.object({
6742
+ topic: TopicSchema,
6743
+ lines: external_exports.array(TopicLineViewSchema)
6744
+ });
6745
+ TopicCreateResultSchema = external_exports.object({
6746
+ topicId: external_exports.string().min(1),
6747
+ masterSessionId: external_exports.string().min(1)
6748
+ });
6749
+ TopicRejectResultSchema = external_exports.object({
6750
+ rejectCount: external_exports.number().int().min(1),
6751
+ /** 连续两次打回仍不合格 → 必须上报老板(PRD §10.3),不靠 Master 自己数 */
6752
+ needsOwnerAttention: external_exports.boolean()
6753
+ });
6754
+ TopicAcceptResultSchema = external_exports.object({
6755
+ acceptedLines: external_exports.number().int().min(1),
6756
+ totalLines: external_exports.number().int().min(1),
6757
+ /** 全部线都合格了 → 可以收敛上报。由 daemon 数,不让 Master 自己盘 */
6758
+ allAccepted: external_exports.boolean()
6759
+ });
6760
+ TopicResultSchema = external_exports.object({ topic: TopicSchema });
6761
+ }
6762
+ });
6763
+
6764
+ // ../protocol/src/master.ts
6765
+ var LineDigestArgsSchema, LineDigestResultSchema, AUTHORITY_ACTIONS, AuthorityActionSchema, AuthorityRuleSchema, AuthorityTableSchema, AuthorityCheckArgsSchema, AuthorityCheckResultSchema;
6766
+ var init_master = __esm({
6767
+ "../protocol/src/master.ts"() {
6768
+ "use strict";
6769
+ init_zod();
6770
+ LineDigestArgsSchema = external_exports.object({
6771
+ /** 一条线 = 一条 dispatch;daemon 内部据此解析 worker session 与 transcript */
6772
+ dispatchId: external_exports.string().min(1)
6773
+ });
6774
+ LineDigestResultSchema = external_exports.object({
6775
+ digest: external_exports.string(),
6776
+ /** 抽取时是否因超上限被截断——watcher 据此避免"仅凭没看到就判 fail" */
6777
+ truncated: external_exports.boolean()
6778
+ });
6779
+ AUTHORITY_ACTIONS = ["ask", "allow", "deny"];
6780
+ AuthorityActionSchema = external_exports.enum(AUTHORITY_ACTIONS);
6781
+ AuthorityRuleSchema = external_exports.object({
6782
+ id: external_exports.string().min(1),
6783
+ desc: external_exports.string().min(1),
6784
+ action: AuthorityActionSchema
6785
+ });
6786
+ AuthorityTableSchema = external_exports.object({ rules: external_exports.array(AuthorityRuleSchema) });
6787
+ AuthorityCheckArgsSchema = external_exports.object({ ruleId: external_exports.string().min(1) });
6788
+ AuthorityCheckResultSchema = external_exports.object({
6789
+ action: AuthorityActionSchema,
6790
+ desc: external_exports.string(),
6791
+ /** 裁决出处:'table' / 'default' / 'fallback:<原因>'。异常兜底成 ask 时靠它说明为什么 */
6792
+ source: external_exports.string().min(1)
6793
+ });
6794
+ }
6795
+ });
6796
+
6597
6797
  // ../protocol/src/github.ts
6598
6798
  var GITHUB_PR_ROLES, REPO_SEGMENT, owner, repo, number, GithubPrListArgs, GithubPrDetailArgs, GithubPrMergeArgs, GithubPrCommentArgs, GithubPrSetReadyArgs, GithubCollaboratorsArgs, GithubPrRequestReviewersArgs;
6599
6799
  var init_github = __esm({
@@ -8025,6 +8225,9 @@ var init_methods = __esm({
8025
8225
  init_inbox();
8026
8226
  init_peer_exec();
8027
8227
  init_shift_internal();
8228
+ init_channel();
8229
+ init_topic();
8230
+ init_master();
8028
8231
  init_github();
8029
8232
  init_lark_bot_schemas();
8030
8233
  init_onboarding_schemas();
@@ -8213,6 +8416,68 @@ var init_methods = __esm({
8213
8416
  summary: "\u5217\u51FA owner \u7684\u5168\u90E8 shift\uFF08\u6392\u73ED\uFF09\uFF0C\u7ED9 UI \u65E5\u5386\u6E32\u67D3\u7528\uFF1Bowner-only",
8214
8417
  args: NO_ARGS
8215
8418
  },
8419
+ // ---- channel:*(Channel × Master:话题的聚合,纯组织单位)----
8420
+ "channel:list": {
8421
+ summary: "\u5217\u51FA\u5168\u90E8\u9891\u9053\uFF08Channel \xD7 Master \u7684\u7EC4\u7EC7\u5355\u4F4D\uFF1A\u6210\u5458\u540D\u5355 + \u8BDD\u9898\u7684\u805A\u5408\uFF09\uFF1Bowner-only\u3002\u65E0\u53C2\u6570",
8422
+ args: NO_ARGS
8423
+ },
8424
+ "channel:create": {
8425
+ summary: "\u5EFA\u9891\u9053\uFF1Aname \u975E\u7A7A\uFF0C\u6210\u5458 persona / \u8054\u7CFB\u4EBA\u53EF\u7A7A\u3002\u8FD4\u56DE\u65B0\u5EFA\u7684 Channel",
8426
+ args: ChannelCreateArgsSchema
8427
+ },
8428
+ "channel:update": {
8429
+ summary: "\u6539\u9891\u9053\uFF1AchannelId \u5FC5\u586B\uFF0Cname / \u6210\u5458\u5217\u8868\u6309\u9700\u5C40\u90E8\u66F4\u65B0",
8430
+ args: ChannelUpdateArgsSchema
8431
+ },
8432
+ "channel:delete": {
8433
+ summary: "\u5220\u9891\u9053\uFF1B\u5176\u4E0B\u4ECD\u6709\u672A\u7ED3\u675F\u8BDD\u9898\u65F6\u62D2\u7EDD\uFF08fail-fast\uFF0C\u4E0D\u7EA7\u8054\u5220\u8BDD\u9898\uFF09",
8434
+ args: ChannelDeleteArgsSchema
8435
+ },
8436
+ // ---- topic:*(话题:一个任务的容器;一个话题 = 一个 Master session)----
8437
+ "topic:list": {
8438
+ summary: "\u5217\u67D0\u9891\u9053\u4E0B\u5168\u90E8\u8BDD\u9898\uFF08\u542B\u72B6\u6001\uFF0C\u4F9B\u9891\u9053 flyout \u5217\u8868\uFF09",
8439
+ args: TopicListArgsSchema
8440
+ },
8441
+ "topic:get": {
8442
+ summary: "\u8BDD\u9898\u8BE6\u60C5\uFF1A\u4E09\u8981\u7D20 + \u6D41\u6C34 + lines\uFF08lines \u4ECE dispatch \u53F0\u8D26 join\uFF0C\u4E0D\u5355\u72EC\u843D\u5E93\uFF09\u3002topicId \u6216 sessionId \u4E8C\u9009\u4E00\u2014\u2014Master \u4E0D\u5E26\u53C2\u8C03\uFF08\u6865\u81EA\u52A8\u586B sessionId\uFF09\u5373\u300C\u67E5\u6211\u7ED1\u7684\u90A3\u6761\u8BDD\u9898\u300D\uFF0C\u4E5F\u662F\u5B83\u88AB\u6E05\u7A7A\u540E\u91CD\u5EFA\u73B0\u573A\u7684\u5165\u53E3",
8443
+ args: TopicGetArgsSchema,
8444
+ argsConstraints: {
8445
+ anyOf: [{ required: ["topicId"] }, { required: ["sessionId"] }]
8446
+ }
8447
+ },
8448
+ "topic:create": {
8449
+ summary: "\u5EFA\u8BDD\u9898\u5E76\u4E00\u5E76\u8D77\u8BE5\u8BDD\u9898\u7684 Master session\uFF081:1\uFF09\uFF0C\u8FD4\u56DE topicId + masterSessionId\u3002title \u53EF\u540E\u8865",
8450
+ args: TopicCreateArgsSchema
8451
+ },
8452
+ "topic:setPlan": {
8453
+ summary: "\u63A5\u6D3B\u5BF9\u9F50\u540E\u9501\u5B9A\u4E09\u8981\u7D20\uFF1BacceptanceCriteria \u5FC5\u987B\u975E\u7A7A\u2014\u2014\u6CA1\u6709\u9A8C\u6536\u6807\u51C6\u5C31\u4E0D\u5F00\u5DE5",
8454
+ args: TopicSetPlanArgsSchema
8455
+ },
8456
+ "topic:append": {
8457
+ summary: '\u5F80\u8BDD\u9898\u6D41\u6C34\u8FFD\u52A0\u4E00\u6761\uFF1Breason \u5FC5\u586B\uFF08\u8001\u677F\u4E8B\u540E\u770B\u7684\u662F"\u51ED\u4EC0\u4E48\u8FD9\u4E48\u5224\u65AD"\uFF09',
8458
+ args: TopicAppendArgsSchema
8459
+ },
8460
+ "topic:reject": {
8461
+ summary: "\u6253\u56DE\u67D0\u6761\u7EBF\uFF1A\u8BA1\u6570 +1 \u5E76\u540C\u6B65\u843D\u4E00\u6761\u6D41\u6C34\uFF1Breason \u5FC5\u586B\u3002\u8FD4\u56DE rejectCount \u4E0E needsOwnerAttention\uFF08\u22652 \u5373 true\uFF09",
8462
+ args: TopicRejectArgsSchema
8463
+ },
8464
+ "topic:accept": {
8465
+ summary: "\u5224\u67D0\u6761\u7EBF\u5408\u683C\uFF1A\u7F6E accepted \u5E76\u540C\u6B65\u843D\u4E00\u6761\u6D41\u6C34\uFF1Breason \u5FC5\u586B\uFF08\u4E0E\u6253\u56DE\u5BF9\u79F0\uFF0C\u53EA\u8BB0\u6253\u56DE\u7B49\u4E8E\u53EA\u8BB0\u5206\u5B50\uFF09\u3002\u8FD4\u56DE acceptedLines / totalLines / allAccepted\uFF0C\u6536\u655B\u65F6\u673A\u7531 daemon \u6570\u4E0D\u7531 Master \u76D8",
8466
+ args: TopicAcceptArgsSchema
8467
+ },
8468
+ "topic:close": {
8469
+ summary: "\u6536\u655B\u8BDD\u9898\uFF1Astate \u7F6E done / archived",
8470
+ args: TopicCloseArgsSchema
8471
+ },
8472
+ // ---- line:digest / master:*(核验与授权,Master 经 clawd-rpc 桥调)----
8473
+ "line:digest": {
8474
+ summary: "\u628A\u4E00\u6761\u7EBF\uFF08dispatch\uFF09\u7684 worker transcript \u62BD\u6210 \u226420KB digest\uFF1A\u6539\u52A8\u8FC7\u7684\u6587\u4EF6 / \u6267\u884C\u8FC7\u7684\u547D\u4EE4 / \u6210\u529F\u547D\u4EE4\u8F93\u51FA\u6458\u5F55 / \u62A5\u9519\u805A\u5408 / \u5C3E\u90E8 assistant \u8F93\u51FA\u3002\u6838\u9A8C subagent \u7528\u5B83\uFF0C\u907F\u514D\u76F4\u63A5\u8BFB\u53EF\u8FBE 10MB \u7684\u539F\u59CB\u6D41",
8475
+ args: LineDigestArgsSchema
8476
+ },
8477
+ "master:authorityCheck": {
8478
+ summary: "\u67E5 Master \u7684\u6388\u6743\u8BBE\u5B9A\uFF08\u5168\u5C40\u4E00\u5957\uFF09\uFF1A\u8FD4\u56DE ask / allow / deny\u3002\u8868\u7F3A\u5931\u3001\u8BFB\u574F\u3001\u7ED3\u6784\u4E0D\u5BF9\u3001action \u62FC\u9519\u3001id \u672A\u767B\u8BB0\uFF0C\u4E00\u5F8B\u88C1\u51B3 ask",
8479
+ args: AuthorityCheckArgsSchema
8480
+ },
8216
8481
  "github:status": {
8217
8482
  summary: "\u63A2\u6D4B\u672C\u673A gh CLI\uFF08PR \u6536\u4EF6\u7BB1\u524D\u7F6E\uFF09\uFF1A\u662F\u5426\u5B89\u88C5\u3001\u662F\u5426\u5DF2\u767B\u5F55 github.com\u3001\u767B\u5F55\u540D\u3002\u65E0\u53C2\u6570",
8218
8483
  args: NO_ARGS
@@ -8743,6 +9008,9 @@ var init_runtime = __esm({
8743
9008
  init_extension();
8744
9009
  init_feishu_auth();
8745
9010
  init_dispatch();
9011
+ init_channel();
9012
+ init_topic();
9013
+ init_master();
8746
9014
  init_log();
8747
9015
  init_visitor_schemas();
8748
9016
  init_github();
@@ -9024,8 +9292,8 @@ var require_req = __commonJS({
9024
9292
  if (req.originalUrl) {
9025
9293
  _req.url = req.originalUrl;
9026
9294
  } else {
9027
- const path87 = req.path;
9028
- _req.url = typeof path87 === "string" ? path87 : req.url ? req.url.path || req.url : void 0;
9295
+ const path89 = req.path;
9296
+ _req.url = typeof path89 === "string" ? path89 : req.url ? req.url.path || req.url : void 0;
9029
9297
  }
9030
9298
  if (req.query) {
9031
9299
  _req.query = req.query;
@@ -9190,14 +9458,14 @@ var require_redact = __commonJS({
9190
9458
  }
9191
9459
  return obj;
9192
9460
  }
9193
- function parsePath(path87) {
9461
+ function parsePath(path89) {
9194
9462
  const parts = [];
9195
9463
  let current = "";
9196
9464
  let inBrackets = false;
9197
9465
  let inQuotes = false;
9198
9466
  let quoteChar = "";
9199
- for (let i = 0; i < path87.length; i++) {
9200
- const char = path87[i];
9467
+ for (let i = 0; i < path89.length; i++) {
9468
+ const char = path89[i];
9201
9469
  if (!inBrackets && char === ".") {
9202
9470
  if (current) {
9203
9471
  parts.push(current);
@@ -9328,10 +9596,10 @@ var require_redact = __commonJS({
9328
9596
  return current;
9329
9597
  }
9330
9598
  function redactPaths(obj, paths, censor, remove = false) {
9331
- for (const path87 of paths) {
9332
- const parts = parsePath(path87);
9599
+ for (const path89 of paths) {
9600
+ const parts = parsePath(path89);
9333
9601
  if (parts.includes("*")) {
9334
- redactWildcardPath(obj, parts, censor, path87, remove);
9602
+ redactWildcardPath(obj, parts, censor, path89, remove);
9335
9603
  } else {
9336
9604
  if (remove) {
9337
9605
  removeKey(obj, parts);
@@ -9416,8 +9684,8 @@ var require_redact = __commonJS({
9416
9684
  }
9417
9685
  } else {
9418
9686
  if (afterWildcard.includes("*")) {
9419
- const wrappedCensor = typeof censor === "function" ? (value, path87) => {
9420
- const fullPath = [...pathArray.slice(0, pathLength), ...path87];
9687
+ const wrappedCensor = typeof censor === "function" ? (value, path89) => {
9688
+ const fullPath = [...pathArray.slice(0, pathLength), ...path89];
9421
9689
  return censor(value, fullPath);
9422
9690
  } : censor;
9423
9691
  redactWildcardPath(current, afterWildcard, wrappedCensor, originalPath, remove);
@@ -9452,8 +9720,8 @@ var require_redact = __commonJS({
9452
9720
  return null;
9453
9721
  }
9454
9722
  const pathStructure = /* @__PURE__ */ new Map();
9455
- for (const path87 of pathsToClone) {
9456
- const parts = parsePath(path87);
9723
+ for (const path89 of pathsToClone) {
9724
+ const parts = parsePath(path89);
9457
9725
  let current = pathStructure;
9458
9726
  for (let i = 0; i < parts.length; i++) {
9459
9727
  const part = parts[i];
@@ -9505,24 +9773,24 @@ var require_redact = __commonJS({
9505
9773
  }
9506
9774
  return cloneSelectively(obj, pathStructure);
9507
9775
  }
9508
- function validatePath(path87) {
9509
- if (typeof path87 !== "string") {
9776
+ function validatePath(path89) {
9777
+ if (typeof path89 !== "string") {
9510
9778
  throw new Error("Paths must be (non-empty) strings");
9511
9779
  }
9512
- if (path87 === "") {
9780
+ if (path89 === "") {
9513
9781
  throw new Error("Invalid redaction path ()");
9514
9782
  }
9515
- if (path87.includes("..")) {
9516
- throw new Error(`Invalid redaction path (${path87})`);
9783
+ if (path89.includes("..")) {
9784
+ throw new Error(`Invalid redaction path (${path89})`);
9517
9785
  }
9518
- if (path87.includes(",")) {
9519
- throw new Error(`Invalid redaction path (${path87})`);
9786
+ if (path89.includes(",")) {
9787
+ throw new Error(`Invalid redaction path (${path89})`);
9520
9788
  }
9521
9789
  let bracketCount = 0;
9522
9790
  let inQuotes = false;
9523
9791
  let quoteChar = "";
9524
- for (let i = 0; i < path87.length; i++) {
9525
- const char = path87[i];
9792
+ for (let i = 0; i < path89.length; i++) {
9793
+ const char = path89[i];
9526
9794
  if ((char === '"' || char === "'") && bracketCount > 0) {
9527
9795
  if (!inQuotes) {
9528
9796
  inQuotes = true;
@@ -9536,20 +9804,20 @@ var require_redact = __commonJS({
9536
9804
  } else if (char === "]" && !inQuotes) {
9537
9805
  bracketCount--;
9538
9806
  if (bracketCount < 0) {
9539
- throw new Error(`Invalid redaction path (${path87})`);
9807
+ throw new Error(`Invalid redaction path (${path89})`);
9540
9808
  }
9541
9809
  }
9542
9810
  }
9543
9811
  if (bracketCount !== 0) {
9544
- throw new Error(`Invalid redaction path (${path87})`);
9812
+ throw new Error(`Invalid redaction path (${path89})`);
9545
9813
  }
9546
9814
  }
9547
9815
  function validatePaths(paths) {
9548
9816
  if (!Array.isArray(paths)) {
9549
9817
  throw new TypeError("paths must be an array");
9550
9818
  }
9551
- for (const path87 of paths) {
9552
- validatePath(path87);
9819
+ for (const path89 of paths) {
9820
+ validatePath(path89);
9553
9821
  }
9554
9822
  }
9555
9823
  function slowRedact(options = {}) {
@@ -9717,8 +9985,8 @@ var require_redaction = __commonJS({
9717
9985
  if (shape[k2] === null) {
9718
9986
  o[k2] = (value) => topCensor(value, [k2]);
9719
9987
  } else {
9720
- const wrappedCensor = typeof censor === "function" ? (value, path87) => {
9721
- return censor(value, [k2, ...path87]);
9988
+ const wrappedCensor = typeof censor === "function" ? (value, path89) => {
9989
+ return censor(value, [k2, ...path89]);
9722
9990
  } : censor;
9723
9991
  o[k2] = Redact({
9724
9992
  paths: shape[k2],
@@ -9936,10 +10204,10 @@ var require_atomic_sleep = __commonJS({
9936
10204
  var require_sonic_boom = __commonJS({
9937
10205
  "../node_modules/.pnpm/sonic-boom@4.2.1/node_modules/sonic-boom/index.js"(exports2, module2) {
9938
10206
  "use strict";
9939
- var fs77 = require("fs");
10207
+ var fs79 = require("fs");
9940
10208
  var EventEmitter3 = require("events");
9941
10209
  var inherits = require("util").inherits;
9942
- var path87 = require("path");
10210
+ var path89 = require("path");
9943
10211
  var sleep2 = require_atomic_sleep();
9944
10212
  var assert = require("assert");
9945
10213
  var BUSY_WRITE_TIMEOUT = 100;
@@ -9993,20 +10261,20 @@ var require_sonic_boom = __commonJS({
9993
10261
  const mode = sonic.mode;
9994
10262
  if (sonic.sync) {
9995
10263
  try {
9996
- if (sonic.mkdir) fs77.mkdirSync(path87.dirname(file), { recursive: true });
9997
- const fd = fs77.openSync(file, flags, mode);
10264
+ if (sonic.mkdir) fs79.mkdirSync(path89.dirname(file), { recursive: true });
10265
+ const fd = fs79.openSync(file, flags, mode);
9998
10266
  fileOpened(null, fd);
9999
10267
  } catch (err) {
10000
10268
  fileOpened(err);
10001
10269
  throw err;
10002
10270
  }
10003
10271
  } else if (sonic.mkdir) {
10004
- fs77.mkdir(path87.dirname(file), { recursive: true }, (err) => {
10272
+ fs79.mkdir(path89.dirname(file), { recursive: true }, (err) => {
10005
10273
  if (err) return fileOpened(err);
10006
- fs77.open(file, flags, mode, fileOpened);
10274
+ fs79.open(file, flags, mode, fileOpened);
10007
10275
  });
10008
10276
  } else {
10009
- fs77.open(file, flags, mode, fileOpened);
10277
+ fs79.open(file, flags, mode, fileOpened);
10010
10278
  }
10011
10279
  }
10012
10280
  function SonicBoom(opts) {
@@ -10047,8 +10315,8 @@ var require_sonic_boom = __commonJS({
10047
10315
  this.flush = flushBuffer;
10048
10316
  this.flushSync = flushBufferSync;
10049
10317
  this._actualWrite = actualWriteBuffer;
10050
- fsWriteSync = () => fs77.writeSync(this.fd, this._writingBuf);
10051
- fsWrite = () => fs77.write(this.fd, this._writingBuf, this.release);
10318
+ fsWriteSync = () => fs79.writeSync(this.fd, this._writingBuf);
10319
+ fsWrite = () => fs79.write(this.fd, this._writingBuf, this.release);
10052
10320
  } else if (contentMode === void 0 || contentMode === kContentModeUtf8) {
10053
10321
  this._writingBuf = "";
10054
10322
  this.write = write;
@@ -10057,15 +10325,15 @@ var require_sonic_boom = __commonJS({
10057
10325
  this._actualWrite = actualWrite;
10058
10326
  fsWriteSync = () => {
10059
10327
  if (Buffer.isBuffer(this._writingBuf)) {
10060
- return fs77.writeSync(this.fd, this._writingBuf);
10328
+ return fs79.writeSync(this.fd, this._writingBuf);
10061
10329
  }
10062
- return fs77.writeSync(this.fd, this._writingBuf, "utf8");
10330
+ return fs79.writeSync(this.fd, this._writingBuf, "utf8");
10063
10331
  };
10064
10332
  fsWrite = () => {
10065
10333
  if (Buffer.isBuffer(this._writingBuf)) {
10066
- return fs77.write(this.fd, this._writingBuf, this.release);
10334
+ return fs79.write(this.fd, this._writingBuf, this.release);
10067
10335
  }
10068
- return fs77.write(this.fd, this._writingBuf, "utf8", this.release);
10336
+ return fs79.write(this.fd, this._writingBuf, "utf8", this.release);
10069
10337
  };
10070
10338
  } else {
10071
10339
  throw new Error(`SonicBoom supports "${kContentModeUtf8}" and "${kContentModeBuffer}", but passed ${contentMode}`);
@@ -10122,7 +10390,7 @@ var require_sonic_boom = __commonJS({
10122
10390
  }
10123
10391
  }
10124
10392
  if (this._fsync) {
10125
- fs77.fsyncSync(this.fd);
10393
+ fs79.fsyncSync(this.fd);
10126
10394
  }
10127
10395
  const len = this._len;
10128
10396
  if (this._reopening) {
@@ -10236,7 +10504,7 @@ var require_sonic_boom = __commonJS({
10236
10504
  const onDrain = () => {
10237
10505
  if (!this._fsync) {
10238
10506
  try {
10239
- fs77.fsync(this.fd, (err) => {
10507
+ fs79.fsync(this.fd, (err) => {
10240
10508
  this._flushPending = false;
10241
10509
  cb(err);
10242
10510
  });
@@ -10338,7 +10606,7 @@ var require_sonic_boom = __commonJS({
10338
10606
  const fd = this.fd;
10339
10607
  this.once("ready", () => {
10340
10608
  if (fd !== this.fd) {
10341
- fs77.close(fd, (err) => {
10609
+ fs79.close(fd, (err) => {
10342
10610
  if (err) {
10343
10611
  return this.emit("error", err);
10344
10612
  }
@@ -10387,7 +10655,7 @@ var require_sonic_boom = __commonJS({
10387
10655
  buf = this._bufs[0];
10388
10656
  }
10389
10657
  try {
10390
- const n = Buffer.isBuffer(buf) ? fs77.writeSync(this.fd, buf) : fs77.writeSync(this.fd, buf, "utf8");
10658
+ const n = Buffer.isBuffer(buf) ? fs79.writeSync(this.fd, buf) : fs79.writeSync(this.fd, buf, "utf8");
10391
10659
  const releasedBufObj = releaseWritingBuf(buf, this._len, n);
10392
10660
  buf = releasedBufObj.writingBuf;
10393
10661
  this._len = releasedBufObj.len;
@@ -10403,7 +10671,7 @@ var require_sonic_boom = __commonJS({
10403
10671
  }
10404
10672
  }
10405
10673
  try {
10406
- fs77.fsyncSync(this.fd);
10674
+ fs79.fsyncSync(this.fd);
10407
10675
  } catch {
10408
10676
  }
10409
10677
  }
@@ -10424,7 +10692,7 @@ var require_sonic_boom = __commonJS({
10424
10692
  buf = mergeBuf(this._bufs[0], this._lens[0]);
10425
10693
  }
10426
10694
  try {
10427
- const n = fs77.writeSync(this.fd, buf);
10695
+ const n = fs79.writeSync(this.fd, buf);
10428
10696
  buf = buf.subarray(n);
10429
10697
  this._len = Math.max(this._len - n, 0);
10430
10698
  if (buf.length <= 0) {
@@ -10452,13 +10720,13 @@ var require_sonic_boom = __commonJS({
10452
10720
  this._writingBuf = this._writingBuf.length ? this._writingBuf : this._bufs.shift() || "";
10453
10721
  if (this.sync) {
10454
10722
  try {
10455
- const written = Buffer.isBuffer(this._writingBuf) ? fs77.writeSync(this.fd, this._writingBuf) : fs77.writeSync(this.fd, this._writingBuf, "utf8");
10723
+ const written = Buffer.isBuffer(this._writingBuf) ? fs79.writeSync(this.fd, this._writingBuf) : fs79.writeSync(this.fd, this._writingBuf, "utf8");
10456
10724
  release(null, written);
10457
10725
  } catch (err) {
10458
10726
  release(err);
10459
10727
  }
10460
10728
  } else {
10461
- fs77.write(this.fd, this._writingBuf, release);
10729
+ fs79.write(this.fd, this._writingBuf, release);
10462
10730
  }
10463
10731
  }
10464
10732
  function actualWriteBuffer() {
@@ -10467,7 +10735,7 @@ var require_sonic_boom = __commonJS({
10467
10735
  this._writingBuf = this._writingBuf.length ? this._writingBuf : mergeBuf(this._bufs.shift(), this._lens.shift());
10468
10736
  if (this.sync) {
10469
10737
  try {
10470
- const written = fs77.writeSync(this.fd, this._writingBuf);
10738
+ const written = fs79.writeSync(this.fd, this._writingBuf);
10471
10739
  release(null, written);
10472
10740
  } catch (err) {
10473
10741
  release(err);
@@ -10476,7 +10744,7 @@ var require_sonic_boom = __commonJS({
10476
10744
  if (kCopyBuffer) {
10477
10745
  this._writingBuf = Buffer.from(this._writingBuf);
10478
10746
  }
10479
- fs77.write(this.fd, this._writingBuf, release);
10747
+ fs79.write(this.fd, this._writingBuf, release);
10480
10748
  }
10481
10749
  }
10482
10750
  function actualClose(sonic) {
@@ -10492,12 +10760,12 @@ var require_sonic_boom = __commonJS({
10492
10760
  sonic._lens = [];
10493
10761
  assert(typeof sonic.fd === "number", `sonic.fd must be a number, got ${typeof sonic.fd}`);
10494
10762
  try {
10495
- fs77.fsync(sonic.fd, closeWrapped);
10763
+ fs79.fsync(sonic.fd, closeWrapped);
10496
10764
  } catch {
10497
10765
  }
10498
10766
  function closeWrapped() {
10499
10767
  if (sonic.fd !== 1 && sonic.fd !== 2) {
10500
- fs77.close(sonic.fd, done);
10768
+ fs79.close(sonic.fd, done);
10501
10769
  } else {
10502
10770
  done();
10503
10771
  }
@@ -13632,11 +13900,11 @@ var init_lib = __esm({
13632
13900
  }
13633
13901
  }
13634
13902
  },
13635
- addToPath: function addToPath(path87, added, removed, oldPosInc, options) {
13636
- var last = path87.lastComponent;
13903
+ addToPath: function addToPath(path89, added, removed, oldPosInc, options) {
13904
+ var last = path89.lastComponent;
13637
13905
  if (last && !options.oneChangePerToken && last.added === added && last.removed === removed) {
13638
13906
  return {
13639
- oldPos: path87.oldPos + oldPosInc,
13907
+ oldPos: path89.oldPos + oldPosInc,
13640
13908
  lastComponent: {
13641
13909
  count: last.count + 1,
13642
13910
  added,
@@ -13646,7 +13914,7 @@ var init_lib = __esm({
13646
13914
  };
13647
13915
  } else {
13648
13916
  return {
13649
- oldPos: path87.oldPos + oldPosInc,
13917
+ oldPos: path89.oldPos + oldPosInc,
13650
13918
  lastComponent: {
13651
13919
  count: 1,
13652
13920
  added,
@@ -13956,24 +14224,46 @@ function safeStatMtime(p2) {
13956
14224
  return 0;
13957
14225
  }
13958
14226
  }
13959
- function readJsonlLines(file) {
13960
- let raw;
14227
+ function* streamJsonlLines(file) {
14228
+ let fd;
13961
14229
  try {
13962
- raw = import_node_fs6.default.readFileSync(file, "utf8");
14230
+ fd = import_node_fs6.default.openSync(file, "r");
13963
14231
  } catch (err) {
13964
- if (err.code === "ENOENT") return [];
14232
+ if (err.code === "ENOENT") return;
13965
14233
  throw err;
13966
14234
  }
13967
- const lines = [];
13968
- for (const line of raw.split("\n")) {
13969
- const t = line.trim();
13970
- if (!t) continue;
13971
- try {
13972
- lines.push(JSON.parse(t));
13973
- } catch {
14235
+ const decoder = new import_node_string_decoder.StringDecoder("utf8");
14236
+ const buf = Buffer.allocUnsafe(JSONL_CHUNK_BYTES);
14237
+ let pending = "";
14238
+ try {
14239
+ for (; ; ) {
14240
+ const n = import_node_fs6.default.readSync(fd, buf, 0, JSONL_CHUNK_BYTES, null);
14241
+ if (n === 0) break;
14242
+ pending += decoder.write(buf.subarray(0, n));
14243
+ let nl;
14244
+ while ((nl = pending.indexOf("\n")) >= 0) {
14245
+ const line = pending.slice(0, nl).trim();
14246
+ pending = pending.slice(nl + 1);
14247
+ if (!line) continue;
14248
+ try {
14249
+ yield JSON.parse(line);
14250
+ } catch {
14251
+ }
14252
+ }
14253
+ }
14254
+ const last = (pending + decoder.end()).trim();
14255
+ if (last) {
14256
+ try {
14257
+ yield JSON.parse(last);
14258
+ } catch {
14259
+ }
13974
14260
  }
14261
+ } finally {
14262
+ import_node_fs6.default.closeSync(fd);
13975
14263
  }
13976
- return lines;
14264
+ }
14265
+ function readJsonlLines(file) {
14266
+ return [...streamJsonlLines(file)];
13977
14267
  }
13978
14268
  function classifyUserMessage(input) {
13979
14269
  const text = input.text ?? "";
@@ -14178,10 +14468,10 @@ function attachmentToHistoryMessage(o, ts) {
14178
14468
  const memories = raw.map((m2) => {
14179
14469
  if (!m2 || typeof m2 !== "object") return null;
14180
14470
  const rec4 = m2;
14181
- const path87 = typeof rec4.path === "string" ? rec4.path : null;
14471
+ const path89 = typeof rec4.path === "string" ? rec4.path : null;
14182
14472
  const content = typeof rec4.content === "string" ? rec4.content : null;
14183
- if (!path87 || content == null) return null;
14184
- const entry = { path: path87, content };
14473
+ if (!path89 || content == null) return null;
14474
+ const entry = { path: path89, content };
14185
14475
  if (typeof rec4.mtimeMs === "number") entry.mtimeMs = rec4.mtimeMs;
14186
14476
  return entry;
14187
14477
  }).filter((m2) => m2 !== null);
@@ -14233,7 +14523,7 @@ function readCurrentContent(filePath) {
14233
14523
  return null;
14234
14524
  }
14235
14525
  }
14236
- var import_node_crypto3, import_node_fs6, import_node_os3, import_node_path5, 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;
14526
+ var import_node_crypto3, import_node_fs6, import_node_os3, import_node_path5, import_node_string_decoder, JSONL_CHUNK_BYTES, 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;
14237
14527
  var init_claude_history = __esm({
14238
14528
  "src/tools/claude-history.ts"() {
14239
14529
  "use strict";
@@ -14241,8 +14531,10 @@ var init_claude_history = __esm({
14241
14531
  import_node_fs6 = __toESM(require("fs"), 1);
14242
14532
  import_node_os3 = __toESM(require("os"), 1);
14243
14533
  import_node_path5 = __toESM(require("path"), 1);
14534
+ import_node_string_decoder = require("string_decoder");
14244
14535
  init_lib();
14245
14536
  init_tool_result_extra();
14537
+ JSONL_CHUNK_BYTES = 256 * 1024;
14246
14538
  TASK_NOTIFICATION_RE = /<task-notification\b[\s\S]*?<\/task-notification>/i;
14247
14539
  TASK_ID_RE = /<task-id>\s*([^<\s][^<]*?)\s*<\/task-id>/i;
14248
14540
  TOOL_USE_ID_RE = /<tool-use-id>\s*([^<\s][^<]*?)\s*<\/tool-use-id>/i;
@@ -14288,6 +14580,20 @@ var init_claude_history = __esm({
14288
14580
  const found = findTranscriptByToolSessionId(this.projectsRoot, toolSessionId);
14289
14581
  return found ? import_node_path5.default.dirname(found) : byCwd;
14290
14582
  }
14583
+ /**
14584
+ * 定位一条 session 的 transcript 文件;不存在返回 null。
14585
+ *
14586
+ * 对外开这一口是为了让 adapter 之外的消费者(`line:digest`)**不必自己拼路径**:
14587
+ * 「按 cwd 直取 → 按 tsid 全局兜底」这套 relocation 逻辑曾被抄成第二份,
14588
+ * 规则一改就静默漂移。路径编码(cwdToHashDir)与兜底顺序全仓只此一处。
14589
+ *
14590
+ * 返回 null 而不是不存在的路径:调用方要能区分「还没写盘」与「读到空 transcript」——
14591
+ * 后者会让核验把一条刚 spawn 的线判成假完成。
14592
+ */
14593
+ resolveTranscriptFile(cwd, toolSessionId) {
14594
+ const file = import_node_path5.default.join(this.resolveSessionDir(cwd, toolSessionId), `${toolSessionId}.jsonl`);
14595
+ return import_node_fs6.default.existsSync(file) ? file : null;
14596
+ }
14291
14597
  async listProjects() {
14292
14598
  let entries;
14293
14599
  try {
@@ -15032,10 +15338,10 @@ function parseAttachment(obj) {
15032
15338
  const memories = raw.map((m2) => {
15033
15339
  if (!m2 || typeof m2 !== "object") return null;
15034
15340
  const rec4 = m2;
15035
- const path87 = typeof rec4.path === "string" ? rec4.path : null;
15341
+ const path89 = typeof rec4.path === "string" ? rec4.path : null;
15036
15342
  const content = typeof rec4.content === "string" ? rec4.content : null;
15037
- if (!path87 || content == null) return null;
15038
- const out = { path: path87, content };
15343
+ if (!path89 || content == null) return null;
15344
+ const out = { path: path89, content };
15039
15345
  if (typeof rec4.mtimeMs === "number") out.mtimeMs = rec4.mtimeMs;
15040
15346
  return out;
15041
15347
  }).filter((m2) => m2 !== null);
@@ -34101,8 +34407,8 @@ var require_string_decoder = __commonJS({
34101
34407
  if (typeof nenc !== "string" && (Buffer2.isEncoding === isEncoding || !isEncoding(enc))) throw new Error("Unknown encoding: " + enc);
34102
34408
  return nenc || enc;
34103
34409
  }
34104
- exports2.StringDecoder = StringDecoder;
34105
- function StringDecoder(encoding) {
34410
+ exports2.StringDecoder = StringDecoder2;
34411
+ function StringDecoder2(encoding) {
34106
34412
  this.encoding = normalizeEncoding(encoding);
34107
34413
  var nb;
34108
34414
  switch (this.encoding) {
@@ -34129,7 +34435,7 @@ var require_string_decoder = __commonJS({
34129
34435
  this.lastTotal = 0;
34130
34436
  this.lastChar = Buffer2.allocUnsafe(nb);
34131
34437
  }
34132
- StringDecoder.prototype.write = function(buf) {
34438
+ StringDecoder2.prototype.write = function(buf) {
34133
34439
  if (buf.length === 0) return "";
34134
34440
  var r;
34135
34441
  var i;
@@ -34144,9 +34450,9 @@ var require_string_decoder = __commonJS({
34144
34450
  if (i < buf.length) return r ? r + this.text(buf, i) : this.text(buf, i);
34145
34451
  return r || "";
34146
34452
  };
34147
- StringDecoder.prototype.end = utf8End;
34148
- StringDecoder.prototype.text = utf8Text;
34149
- StringDecoder.prototype.fillLast = function(buf) {
34453
+ StringDecoder2.prototype.end = utf8End;
34454
+ StringDecoder2.prototype.text = utf8Text;
34455
+ StringDecoder2.prototype.fillLast = function(buf) {
34150
34456
  if (this.lastNeed <= buf.length) {
34151
34457
  buf.copy(this.lastChar, this.lastTotal - this.lastNeed, 0, this.lastNeed);
34152
34458
  return this.lastChar.toString(this.encoding, 0, this.lastTotal);
@@ -34318,7 +34624,7 @@ var require_stream_readable = __commonJS({
34318
34624
  }
34319
34625
  var BufferList = require_BufferList();
34320
34626
  var destroyImpl = require_destroy();
34321
- var StringDecoder;
34627
+ var StringDecoder2;
34322
34628
  util2.inherits(Readable3, Stream);
34323
34629
  var kProxyEvents = ["error", "close", "destroy", "pause", "resume"];
34324
34630
  function prependListener(emitter, event, fn) {
@@ -34360,8 +34666,8 @@ var require_stream_readable = __commonJS({
34360
34666
  this.decoder = null;
34361
34667
  this.encoding = null;
34362
34668
  if (options.encoding) {
34363
- if (!StringDecoder) StringDecoder = require_string_decoder().StringDecoder;
34364
- this.decoder = new StringDecoder(options.encoding);
34669
+ if (!StringDecoder2) StringDecoder2 = require_string_decoder().StringDecoder;
34670
+ this.decoder = new StringDecoder2(options.encoding);
34365
34671
  this.encoding = options.encoding;
34366
34672
  }
34367
34673
  }
@@ -34477,8 +34783,8 @@ var require_stream_readable = __commonJS({
34477
34783
  return this._readableState.flowing === false;
34478
34784
  };
34479
34785
  Readable3.prototype.setEncoding = function(enc) {
34480
- if (!StringDecoder) StringDecoder = require_string_decoder().StringDecoder;
34481
- this._readableState.decoder = new StringDecoder(enc);
34786
+ if (!StringDecoder2) StringDecoder2 = require_string_decoder().StringDecoder;
34787
+ this._readableState.decoder = new StringDecoder2(enc);
34482
34788
  this._readableState.encoding = enc;
34483
34789
  return this;
34484
34790
  };
@@ -36009,8 +36315,8 @@ var require_utils = __commonJS({
36009
36315
  var result = transform[inputType][outputType](input);
36010
36316
  return result;
36011
36317
  };
36012
- exports2.resolve = function(path87) {
36013
- var parts = path87.split("/");
36318
+ exports2.resolve = function(path89) {
36319
+ var parts = path89.split("/");
36014
36320
  var result = [];
36015
36321
  for (var index = 0; index < parts.length; index++) {
36016
36322
  var part = parts[index];
@@ -41863,18 +42169,18 @@ var require_object = __commonJS({
41863
42169
  var object = new ZipObject(name, zipObjectContent, o);
41864
42170
  this.files[name] = object;
41865
42171
  };
41866
- var parentFolder = function(path87) {
41867
- if (path87.slice(-1) === "/") {
41868
- path87 = path87.substring(0, path87.length - 1);
42172
+ var parentFolder = function(path89) {
42173
+ if (path89.slice(-1) === "/") {
42174
+ path89 = path89.substring(0, path89.length - 1);
41869
42175
  }
41870
- var lastSlash = path87.lastIndexOf("/");
41871
- return lastSlash > 0 ? path87.substring(0, lastSlash) : "";
42176
+ var lastSlash = path89.lastIndexOf("/");
42177
+ return lastSlash > 0 ? path89.substring(0, lastSlash) : "";
41872
42178
  };
41873
- var forceTrailingSlash = function(path87) {
41874
- if (path87.slice(-1) !== "/") {
41875
- path87 += "/";
42179
+ var forceTrailingSlash = function(path89) {
42180
+ if (path89.slice(-1) !== "/") {
42181
+ path89 += "/";
41876
42182
  }
41877
- return path87;
42183
+ return path89;
41878
42184
  };
41879
42185
  var folderAdd = function(name, createFolders) {
41880
42186
  createFolders = typeof createFolders !== "undefined" ? createFolders : defaults.createFolders;
@@ -42876,7 +43182,7 @@ var require_lib3 = __commonJS({
42876
43182
  // src/run-case/recorder.ts
42877
43183
  function startRunCaseRecorder(opts) {
42878
43184
  const now = opts.now ?? Date.now;
42879
- const dir = import_node_path69.default.dirname(opts.recordPath);
43185
+ const dir = import_node_path71.default.dirname(opts.recordPath);
42880
43186
  let stream = null;
42881
43187
  let closing = false;
42882
43188
  let closedSettled = false;
@@ -42916,12 +43222,12 @@ function startRunCaseRecorder(opts) {
42916
43222
  };
42917
43223
  return { tap, close, closed };
42918
43224
  }
42919
- var import_node_fs53, import_node_path69;
43225
+ var import_node_fs53, import_node_path71;
42920
43226
  var init_recorder = __esm({
42921
43227
  "src/run-case/recorder.ts"() {
42922
43228
  "use strict";
42923
43229
  import_node_fs53 = __toESM(require("fs"), 1);
42924
- import_node_path69 = __toESM(require("path"), 1);
43230
+ import_node_path71 = __toESM(require("path"), 1);
42925
43231
  }
42926
43232
  });
42927
43233
 
@@ -42964,7 +43270,7 @@ var init_wire = __esm({
42964
43270
  // src/run-case/controller.ts
42965
43271
  async function runController(opts) {
42966
43272
  const now = opts.now ?? Date.now;
42967
- const cwd = opts.cwd ?? (0, import_node_fs54.mkdtempSync)(import_node_path70.default.join(import_node_os25.default.tmpdir(), "clawd-runcase-"));
43273
+ const cwd = opts.cwd ?? (0, import_node_fs54.mkdtempSync)(import_node_path72.default.join(import_node_os25.default.tmpdir(), "clawd-runcase-"));
42968
43274
  const ownsCwd = opts.cwd === void 0;
42969
43275
  const recorder = startRunCaseRecorder({ recordPath: opts.record, now });
42970
43276
  const spawnCtx = { cwd };
@@ -43131,13 +43437,13 @@ async function runController(opts) {
43131
43437
  }
43132
43438
  return exitCode ?? 0;
43133
43439
  }
43134
- var import_node_fs54, import_node_os25, import_node_path70;
43440
+ var import_node_fs54, import_node_os25, import_node_path72;
43135
43441
  var init_controller = __esm({
43136
43442
  "src/run-case/controller.ts"() {
43137
43443
  "use strict";
43138
43444
  import_node_fs54 = require("fs");
43139
43445
  import_node_os25 = __toESM(require("os"), 1);
43140
- import_node_path70 = __toESM(require("path"), 1);
43446
+ import_node_path72 = __toESM(require("path"), 1);
43141
43447
  init_claude();
43142
43448
  init_stdout_splitter();
43143
43449
  init_permission_stdio();
@@ -43412,7 +43718,7 @@ Env (advanced):
43412
43718
  `;
43413
43719
 
43414
43720
  // src/index.ts
43415
- var import_node_path68 = __toESM(require("path"), 1);
43721
+ var import_node_path70 = __toESM(require("path"), 1);
43416
43722
  var import_node_fs52 = __toESM(require("fs"), 1);
43417
43723
  var import_node_os24 = __toESM(require("os"), 1);
43418
43724
 
@@ -49191,6 +49497,21 @@ var DEFAULT_PERSONAS = [
49191
49497
  denyRead: [".secrets/**"],
49192
49498
  network: true
49193
49499
  }
49500
+ },
49501
+ {
49502
+ // Master:spec superpowers/specs/2026-08-21-channel-master-m2-design.md
49503
+ // 代替老板操盘一群 worker persona:拆线 / 派活 / **验收 / 打回** / 收敛 / 上报。
49504
+ // 判断力靠 CLAUDE.md 人格 + .claude/agents/line-watcher.md(核验 subagent),
49505
+ // 纪律靠 daemon 侧 zod(`topic:*` 的 reason / acceptanceCriteria 非空在 RPC 入口拒,
49506
+ // 没有绕过路径)+ `master:authorityCheck` 查表。M1 那三个 bundle 内 .mjs 脚本已随
49507
+ // M2「MCP 化」整体拆除,别再照那套找。
49508
+ personaId: "persona-master",
49509
+ label: "Master",
49510
+ model: "opus",
49511
+ effort: "high",
49512
+ iconKey: "assist",
49513
+ public: false,
49514
+ sandboxProfile: DEFAULT_BYPASS_PROFILE
49194
49515
  }
49195
49516
  ];
49196
49517
  function bundleSiblingFromArgv(argv1, sibling) {
@@ -49290,7 +49611,7 @@ function copyBundleExtras(srcDir, dstDir) {
49290
49611
  }
49291
49612
  }
49292
49613
  }
49293
- var DAEMON_MANAGED_PATHS = ["extension-kit", "CLAUDE.md", ".mcp.json", ".claude/skills"];
49614
+ var DAEMON_MANAGED_PATHS = ["extension-kit", "CLAUDE.md", ".mcp.json", ".claude/skills", ".claude/agents"];
49294
49615
  function replaceManagedPath(src, dst) {
49295
49616
  if (!fs14.statSync(src).isDirectory()) {
49296
49617
  fs14.mkdirSync(path17.dirname(dst), { recursive: true });
@@ -50275,8 +50596,8 @@ function turnStartInput(text) {
50275
50596
  const items = [];
50276
50597
  let leftover = text;
50277
50598
  for (const m2 of text.matchAll(SKILL_RE)) {
50278
- const [marker, name, path87] = m2;
50279
- items.push({ type: "skill", name, path: path87 });
50599
+ const [marker, name, path89] = m2;
50600
+ items.push({ type: "skill", name, path: path89 });
50280
50601
  leftover = leftover.replace(marker, "");
50281
50602
  }
50282
50603
  for (const m2 of text.matchAll(ATTACHMENT_RE2)) {
@@ -52045,7 +52366,8 @@ var PersonaDispatchManager = class {
52045
52366
  sourceSessionId: args.sourceSessionId,
52046
52367
  ...args.sourcePersonaId ? { sourcePersonaId: args.sourcePersonaId } : {},
52047
52368
  targetPersonaId: args.targetPersona,
52048
- taskText: args.taskText
52369
+ taskText: args.taskText,
52370
+ ...args.meta ? { meta: args.meta } : {}
52049
52371
  });
52050
52372
  return { dispatchId };
52051
52373
  }
@@ -52194,11 +52516,203 @@ function createDispatchStore(deps) {
52194
52516
  return { load, flushNow, add, update, list, get, markInterruptedAsFailed };
52195
52517
  }
52196
52518
 
52519
+ // src/channel/json-store.ts
52520
+ var import_promises2 = __toESM(require("fs/promises"), 1);
52521
+ var import_node_path19 = __toESM(require("path"), 1);
52522
+ function createJsonStore(deps) {
52523
+ let items = [];
52524
+ let flushTimer = null;
52525
+ async function flushNow() {
52526
+ if (flushTimer) {
52527
+ clearTimeout(flushTimer);
52528
+ flushTimer = null;
52529
+ }
52530
+ const content = { version: 1, [deps.key]: items };
52531
+ await import_promises2.default.mkdir(import_node_path19.default.dirname(deps.filePath), { recursive: true });
52532
+ const tmp = `${deps.filePath}.tmp-${deps.now()}-${Math.floor(Math.random() * 1e6)}`;
52533
+ await import_promises2.default.writeFile(tmp, JSON.stringify(content, null, 2), "utf8");
52534
+ await import_promises2.default.rename(tmp, deps.filePath);
52535
+ }
52536
+ return {
52537
+ all: () => items,
52538
+ flushNow,
52539
+ async load() {
52540
+ let raw;
52541
+ try {
52542
+ raw = await import_promises2.default.readFile(deps.filePath, "utf8");
52543
+ } catch (e) {
52544
+ if (e.code === "ENOENT") {
52545
+ items = [];
52546
+ return;
52547
+ }
52548
+ throw e;
52549
+ }
52550
+ const parsed = JSON.parse(raw);
52551
+ if (parsed.version !== 1) {
52552
+ throw new Error(`${deps.key}.json: unsupported version ${String(parsed.version)}`);
52553
+ }
52554
+ items = parsed[deps.key] ?? [];
52555
+ },
52556
+ scheduleFlush() {
52557
+ if (flushTimer) return;
52558
+ flushTimer = setTimeout(() => {
52559
+ flushTimer = null;
52560
+ flushNow().catch((err) => deps.onFlushError?.(err));
52561
+ }, STORE_FLUSH_DEBOUNCE_MS);
52562
+ }
52563
+ };
52564
+ }
52565
+
52566
+ // src/channel/store.ts
52567
+ function createChannelStore(deps) {
52568
+ const file = createJsonStore({ ...deps, key: "channels" });
52569
+ const get = (id) => file.all().find((c) => c.id === id);
52570
+ return {
52571
+ load: file.load,
52572
+ flushNow: file.flushNow,
52573
+ list: () => [...file.all()],
52574
+ get,
52575
+ create(input) {
52576
+ const iso = new Date(deps.now()).toISOString();
52577
+ const c = {
52578
+ id: deps.genId(),
52579
+ name: input.name,
52580
+ memberPersonaIds: input.memberPersonaIds,
52581
+ memberContactDeviceIds: input.memberContactDeviceIds,
52582
+ createdAt: iso,
52583
+ updatedAt: iso
52584
+ };
52585
+ file.all().push(c);
52586
+ file.scheduleFlush();
52587
+ return c;
52588
+ },
52589
+ update(id, patch) {
52590
+ const c = get(id);
52591
+ if (!c) return void 0;
52592
+ if (patch.name !== void 0) c.name = patch.name;
52593
+ if (patch.memberPersonaIds !== void 0) c.memberPersonaIds = patch.memberPersonaIds;
52594
+ if (patch.memberContactDeviceIds !== void 0) {
52595
+ c.memberContactDeviceIds = patch.memberContactDeviceIds;
52596
+ }
52597
+ c.updatedAt = new Date(deps.now()).toISOString();
52598
+ file.scheduleFlush();
52599
+ return c;
52600
+ },
52601
+ remove(id) {
52602
+ const items = file.all();
52603
+ const i = items.findIndex((c) => c.id === id);
52604
+ if (i < 0) return false;
52605
+ items.splice(i, 1);
52606
+ file.scheduleFlush();
52607
+ return true;
52608
+ }
52609
+ };
52610
+ }
52611
+
52612
+ // src/channel/topic-store.ts
52613
+ function createTopicStore(deps) {
52614
+ const file = createJsonStore({ ...deps, key: "topics" });
52615
+ const get = (topicId) => file.all().find((t) => t.id === topicId);
52616
+ const touch = (t) => {
52617
+ t.updatedAt = new Date(deps.now()).toISOString();
52618
+ };
52619
+ const lineMetaOf = (t, lineId) => t.lineMeta[lineId] ??= { rejectCount: 0, accepted: false };
52620
+ return {
52621
+ load: file.load,
52622
+ flushNow: file.flushNow,
52623
+ get,
52624
+ getByMasterSession: (masterSessionId) => file.all().find((t) => t.masterSessionId === masterSessionId),
52625
+ listByChannel: (channelId) => file.all().filter((t) => t.channelId === channelId),
52626
+ create(input) {
52627
+ const iso = new Date(deps.now()).toISOString();
52628
+ const t = {
52629
+ id: deps.genId(),
52630
+ channelId: input.channelId,
52631
+ title: input.title ?? "",
52632
+ masterSessionId: input.masterSessionId,
52633
+ goal: "",
52634
+ boundaries: "",
52635
+ acceptanceCriteria: [],
52636
+ activity: [],
52637
+ lineMeta: {},
52638
+ state: "planning",
52639
+ createdAt: iso,
52640
+ updatedAt: iso
52641
+ };
52642
+ file.all().push(t);
52643
+ file.scheduleFlush();
52644
+ return t;
52645
+ },
52646
+ setPlan(topicId, plan) {
52647
+ const t = get(topicId);
52648
+ if (!t) return void 0;
52649
+ if (plan.title !== void 0) t.title = plan.title;
52650
+ t.goal = plan.goal;
52651
+ t.boundaries = plan.boundaries;
52652
+ t.acceptanceCriteria = plan.acceptanceCriteria;
52653
+ t.state = "running";
52654
+ touch(t);
52655
+ file.scheduleFlush();
52656
+ return t;
52657
+ },
52658
+ appendActivity(topicId, entry) {
52659
+ const t = get(topicId);
52660
+ if (!t) return void 0;
52661
+ t.activity.push({ at: new Date(deps.now()).toISOString(), ...entry });
52662
+ touch(t);
52663
+ file.scheduleFlush();
52664
+ return t;
52665
+ },
52666
+ bumpReject(topicId, lineId) {
52667
+ const t = get(topicId);
52668
+ if (!t) return void 0;
52669
+ const meta = lineMetaOf(t, lineId);
52670
+ meta.rejectCount += 1;
52671
+ meta.accepted = false;
52672
+ touch(t);
52673
+ file.scheduleFlush();
52674
+ return meta;
52675
+ },
52676
+ markAccepted(topicId, lineId) {
52677
+ const t = get(topicId);
52678
+ if (!t) return void 0;
52679
+ const meta = lineMetaOf(t, lineId);
52680
+ meta.accepted = true;
52681
+ touch(t);
52682
+ file.scheduleFlush();
52683
+ return meta;
52684
+ },
52685
+ close(topicId, state) {
52686
+ const t = get(topicId);
52687
+ if (!t) return void 0;
52688
+ t.state = state;
52689
+ touch(t);
52690
+ file.scheduleFlush();
52691
+ return t;
52692
+ },
52693
+ hasUnfinished(channelId) {
52694
+ return file.all().some((t) => t.channelId === channelId && (t.state === "planning" || t.state === "running"));
52695
+ },
52696
+ removeByChannel(channelId) {
52697
+ const items = file.all();
52698
+ let removed = 0;
52699
+ for (let i = items.length - 1; i >= 0; i--) {
52700
+ if (items[i].channelId === channelId) {
52701
+ items.splice(i, 1);
52702
+ removed += 1;
52703
+ }
52704
+ }
52705
+ if (removed) file.scheduleFlush();
52706
+ return removed;
52707
+ }
52708
+ };
52709
+ }
52710
+
52197
52711
  // src/dispatch/mcp-config.ts
52198
52712
  var import_node_fs20 = __toESM(require("fs"), 1);
52199
- var import_node_path19 = __toESM(require("path"), 1);
52713
+ var import_node_path20 = __toESM(require("path"), 1);
52200
52714
  function dispatchMcpConfigPath(dataDir) {
52201
- return import_node_path19.default.join(dataDir, "dispatch.mcp.json");
52715
+ return import_node_path20.default.join(dataDir, "dispatch.mcp.json");
52202
52716
  }
52203
52717
  function writeDispatchMcpConfig(args) {
52204
52718
  const cfgPath = dispatchMcpConfigPath(args.dataDir);
@@ -52224,9 +52738,9 @@ function writeDispatchMcpConfig(args) {
52224
52738
 
52225
52739
  // src/ticket/mcp-config.ts
52226
52740
  var import_node_fs21 = __toESM(require("fs"), 1);
52227
- var import_node_path20 = __toESM(require("path"), 1);
52741
+ var import_node_path21 = __toESM(require("path"), 1);
52228
52742
  function ticketMcpConfigPath(dataDir) {
52229
- return import_node_path20.default.join(dataDir, "ticket.mcp.json");
52743
+ return import_node_path21.default.join(dataDir, "ticket.mcp.json");
52230
52744
  }
52231
52745
  function writeTicketMcpConfig(args) {
52232
52746
  const cfgPath = ticketMcpConfigPath(args.dataDir);
@@ -52255,9 +52769,9 @@ function writeTicketMcpConfig(args) {
52255
52769
 
52256
52770
  // src/shift/mcp-config.ts
52257
52771
  var import_node_fs22 = __toESM(require("fs"), 1);
52258
- var import_node_path21 = __toESM(require("path"), 1);
52772
+ var import_node_path22 = __toESM(require("path"), 1);
52259
52773
  function shiftMcpConfigPath(dataDir) {
52260
- return import_node_path21.default.join(dataDir, "shift.mcp.json");
52774
+ return import_node_path22.default.join(dataDir, "shift.mcp.json");
52261
52775
  }
52262
52776
  async function writeShiftMcpConfig(args) {
52263
52777
  const cfgPath = shiftMcpConfigPath(args.dataDir);
@@ -52283,9 +52797,9 @@ async function writeShiftMcpConfig(args) {
52283
52797
 
52284
52798
  // src/inbox/mcp-config.ts
52285
52799
  var import_node_fs23 = __toESM(require("fs"), 1);
52286
- var import_node_path22 = __toESM(require("path"), 1);
52800
+ var import_node_path23 = __toESM(require("path"), 1);
52287
52801
  function inboxMcpConfigPath(dataDir) {
52288
- return import_node_path22.default.join(dataDir, "inbox.mcp.json");
52802
+ return import_node_path23.default.join(dataDir, "inbox.mcp.json");
52289
52803
  }
52290
52804
  async function writeInboxMcpConfig(args) {
52291
52805
  const cfgPath = inboxMcpConfigPath(args.dataDir);
@@ -52311,9 +52825,9 @@ async function writeInboxMcpConfig(args) {
52311
52825
 
52312
52826
  // src/peer-ops/mcp-config.ts
52313
52827
  var import_node_fs24 = __toESM(require("fs"), 1);
52314
- var import_node_path23 = __toESM(require("path"), 1);
52828
+ var import_node_path24 = __toESM(require("path"), 1);
52315
52829
  function peerOpsMcpConfigPath(dataDir) {
52316
- return import_node_path23.default.join(dataDir, "peer-ops.mcp.json");
52830
+ return import_node_path24.default.join(dataDir, "peer-ops.mcp.json");
52317
52831
  }
52318
52832
  function writePeerOpsMcpConfig(args) {
52319
52833
  const cfgPath = peerOpsMcpConfigPath(args.dataDir);
@@ -52337,9 +52851,9 @@ function writePeerOpsMcpConfig(args) {
52337
52851
 
52338
52852
  // src/rpc-tool/mcp-config.ts
52339
52853
  var import_node_fs25 = __toESM(require("fs"), 1);
52340
- var import_node_path24 = __toESM(require("path"), 1);
52854
+ var import_node_path25 = __toESM(require("path"), 1);
52341
52855
  function rpcToolMcpConfigPath(dataDir) {
52342
- return import_node_path24.default.join(dataDir, "rpc-tool.mcp.json");
52856
+ return import_node_path25.default.join(dataDir, "rpc-tool.mcp.json");
52343
52857
  }
52344
52858
  function writeRpcToolMcpConfig(args) {
52345
52859
  const cfgPath = rpcToolMcpConfigPath(args.dataDir);
@@ -52364,12 +52878,12 @@ function writeRpcToolMcpConfig(args) {
52364
52878
  }
52365
52879
 
52366
52880
  // src/dispatch/source-transcript.ts
52367
- var import_node_path25 = __toESM(require("path"), 1);
52881
+ var import_node_path26 = __toESM(require("path"), 1);
52368
52882
  init_claude_history();
52369
52883
  var NO_SOURCE_TRANSCRIPT = "(no transcript yet \u2014 operate from the task description alone)";
52370
52884
  function resolveSourceJsonlPath(sourceFile, home) {
52371
52885
  if (!sourceFile?.toolSessionId) return NO_SOURCE_TRANSCRIPT;
52372
- return import_node_path25.default.join(
52886
+ return import_node_path26.default.join(
52373
52887
  home,
52374
52888
  ".claude",
52375
52889
  "projects",
@@ -52379,8 +52893,8 @@ function resolveSourceJsonlPath(sourceFile, home) {
52379
52893
  }
52380
52894
 
52381
52895
  // src/shift/store.ts
52382
- var import_promises2 = __toESM(require("fs/promises"), 1);
52383
- var import_node_path26 = __toESM(require("path"), 1);
52896
+ var import_promises3 = __toESM(require("fs/promises"), 1);
52897
+ var import_node_path27 = __toESM(require("path"), 1);
52384
52898
  var import_node_crypto6 = require("crypto");
52385
52899
 
52386
52900
  // src/shift/schedule.ts
@@ -52427,7 +52941,7 @@ function createShiftStore(deps) {
52427
52941
  async function load() {
52428
52942
  let raw;
52429
52943
  try {
52430
- raw = await import_promises2.default.readFile(deps.filePath, "utf8");
52944
+ raw = await import_promises3.default.readFile(deps.filePath, "utf8");
52431
52945
  } catch (e) {
52432
52946
  if (e.code === "ENOENT") {
52433
52947
  shifts = [];
@@ -52455,10 +52969,10 @@ function createShiftStore(deps) {
52455
52969
  flushTimer = null;
52456
52970
  }
52457
52971
  const content = { version: 1, shifts };
52458
- await import_promises2.default.mkdir(import_node_path26.default.dirname(deps.filePath), { recursive: true });
52972
+ await import_promises3.default.mkdir(import_node_path27.default.dirname(deps.filePath), { recursive: true });
52459
52973
  const tmp = `${deps.filePath}.tmp-${deps.now()}-${Math.floor(Math.random() * 1e6)}`;
52460
- await import_promises2.default.writeFile(tmp, JSON.stringify(content, null, 2), "utf8");
52461
- await import_promises2.default.rename(tmp, deps.filePath);
52974
+ await import_promises3.default.writeFile(tmp, JSON.stringify(content, null, 2), "utf8");
52975
+ await import_promises3.default.rename(tmp, deps.filePath);
52462
52976
  }
52463
52977
  function scheduleFlush() {
52464
52978
  if (flushTimer) return;
@@ -53124,9 +53638,9 @@ async function refreshTunnelUrlOnStartup(deps) {
53124
53638
 
53125
53639
  // src/lark-bot-connector/binding-store.ts
53126
53640
  var import_node_fs26 = __toESM(require("fs"), 1);
53127
- var import_node_path27 = __toESM(require("path"), 1);
53641
+ var import_node_path28 = __toESM(require("path"), 1);
53128
53642
  function createLarkBotBindingStore(personaRoot) {
53129
- const filePath = (personaId2) => import_node_path27.default.join(personaRoot, safeFileName(personaId2), ".clawd", "lark-bot.json");
53643
+ const filePath = (personaId2) => import_node_path28.default.join(personaRoot, safeFileName(personaId2), ".clawd", "lark-bot.json");
53130
53644
  const read = (personaId2) => {
53131
53645
  const file = filePath(personaId2);
53132
53646
  if (!import_node_fs26.default.existsSync(file)) return null;
@@ -53140,7 +53654,7 @@ function createLarkBotBindingStore(personaRoot) {
53140
53654
  };
53141
53655
  const write = (personaId2, meta) => {
53142
53656
  const file = filePath(personaId2);
53143
- import_node_fs26.default.mkdirSync(import_node_path27.default.dirname(file), { recursive: true });
53657
+ import_node_fs26.default.mkdirSync(import_node_path28.default.dirname(file), { recursive: true });
53144
53658
  const tmp = `${file}.tmp-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2)}`;
53145
53659
  import_node_fs26.default.writeFileSync(tmp, JSON.stringify(meta, null, 2), { mode: 384 });
53146
53660
  import_node_fs26.default.renameSync(tmp, file);
@@ -53304,7 +53818,7 @@ function createLarkBotCloudClient(opts) {
53304
53818
 
53305
53819
  // src/lark-bot-connector/chat-router.ts
53306
53820
  var import_node_fs27 = __toESM(require("fs"), 1);
53307
- var import_node_path28 = __toESM(require("path"), 1);
53821
+ var import_node_path29 = __toESM(require("path"), 1);
53308
53822
  init_protocol();
53309
53823
  var ERROR_REPLY_TEXT = "\u5904\u7406\u51FA\u9519\u4E86\uFF0C\u8BF7\u91CD\u8BD5";
53310
53824
  var LOCAL_TURN_RETRIES = 2;
@@ -53370,7 +53884,7 @@ function createLarkChatRouter(deps) {
53370
53884
  }
53371
53885
  const dir = deps.mediaRootFor(env.chatId);
53372
53886
  import_node_fs27.default.mkdirSync(dir, { recursive: true });
53373
- const p2 = import_node_path28.default.join(dir, `${safeMediaName(ownerMessageId)}-${i}.${ext}`);
53887
+ const p2 = import_node_path29.default.join(dir, `${safeMediaName(ownerMessageId)}-${i}.${ext}`);
53374
53888
  import_node_fs27.default.writeFileSync(p2, r.data);
53375
53889
  lines.push(`[\u56FE\u7247: ${p2}]\uFF08\u7528 Read \u5DE5\u5177\u67E5\u770B\uFF09`);
53376
53890
  } catch (err) {
@@ -53393,7 +53907,7 @@ function createLarkChatRouter(deps) {
53393
53907
  }
53394
53908
  const dir = deps.mediaRootFor(env.chatId);
53395
53909
  import_node_fs27.default.mkdirSync(dir, { recursive: true });
53396
- const p2 = import_node_path28.default.join(dir, `${safeMediaName(ownerMessageId)}-${i}-${safeAttachmentName(f.name)}`);
53910
+ const p2 = import_node_path29.default.join(dir, `${safeMediaName(ownerMessageId)}-${i}-${safeAttachmentName(f.name)}`);
53397
53911
  import_node_fs27.default.writeFileSync(p2, r.data);
53398
53912
  lines.push(`[\u6587\u4EF6: ${p2}]\uFF08\u7528 Read \u5DE5\u5177\u67E5\u770B\uFF09`);
53399
53913
  } catch (err) {
@@ -53755,7 +54269,8 @@ function buildPersonaDispatchHandlers(deps) {
53755
54269
  outcome: void 0,
53756
54270
  completedAt: void 0,
53757
54271
  deliveredAt: void 0,
53758
- taskText: args.prompt.slice(0, TASK_TEXT_MAX)
54272
+ taskText: args.prompt.slice(0, TASK_TEXT_MAX),
54273
+ ...args.meta ? { meta: { ...rec4.meta ?? {}, ...args.meta } } : {}
53759
54274
  });
53760
54275
  mgr.reopen(args.dispatchId, {
53761
54276
  sourceSessionId,
@@ -53802,7 +54317,8 @@ function buildPersonaDispatchHandlers(deps) {
53802
54317
  const { dispatchId: dispatchId2 } = mgr.start({
53803
54318
  sourceSessionId,
53804
54319
  targetPersona,
53805
- taskText: args.prompt
54320
+ taskText: args.prompt,
54321
+ ...args.meta ? { meta: args.meta } : {}
53806
54322
  });
53807
54323
  logger?.info("dispatch.run.forward", {
53808
54324
  dispatchId: dispatchId2,
@@ -53834,7 +54350,8 @@ function buildPersonaDispatchHandlers(deps) {
53834
54350
  const { dispatchId: dispatchId2 } = mgr.start({
53835
54351
  sourceSessionId: ctx.principal.id,
53836
54352
  targetPersona,
53837
- taskText: args.prompt
54353
+ taskText: args.prompt,
54354
+ ...args.meta ? { meta: args.meta } : {}
53838
54355
  });
53839
54356
  logger?.info("dispatch.run.received.guest", {
53840
54357
  dispatchId: dispatchId2,
@@ -53870,7 +54387,8 @@ function buildPersonaDispatchHandlers(deps) {
53870
54387
  const { dispatchId } = mgr.start({
53871
54388
  sourceSessionId,
53872
54389
  targetPersona,
53873
- taskText: args.prompt
54390
+ taskText: args.prompt,
54391
+ ...args.meta ? { meta: args.meta } : {}
53874
54392
  });
53875
54393
  logger?.info("dispatch.run.received", {
53876
54394
  dispatchId,
@@ -54420,7 +54938,7 @@ var CodexHistoryReader = class {
54420
54938
  };
54421
54939
 
54422
54940
  // src/tools/codex-skills.ts
54423
- var import_node_path29 = __toESM(require("path"), 1);
54941
+ var import_node_path30 = __toESM(require("path"), 1);
54424
54942
  function str3(v2) {
54425
54943
  return typeof v2 === "string" ? v2 : void 0;
54426
54944
  }
@@ -54435,13 +54953,13 @@ function mapSkillsListResponse(res) {
54435
54953
  const r = s ?? {};
54436
54954
  const name = str3(r.name);
54437
54955
  if (!name) continue;
54438
- const path87 = str3(r.path);
54956
+ const path89 = str3(r.path);
54439
54957
  const description = str3(r.description);
54440
54958
  const isPlugin = name.includes(":");
54441
54959
  out.push({
54442
54960
  name,
54443
54961
  source: isPlugin ? "plugin" : "project",
54444
- ...path87 ? { path: path87 } : {},
54962
+ ...path89 ? { path: path89 } : {},
54445
54963
  ...description ? { description } : {},
54446
54964
  ...isPlugin ? { plugin: name.split(":")[0] } : {}
54447
54965
  });
@@ -54457,14 +54975,14 @@ async function listCodexSkills(cwd, deps = {}) {
54457
54975
  // 先写 extraRoots 再写 list(stdio 按写入顺序处理,fire-and-forget 不 await 回包;
54458
54976
  // codex-session.ts ready 序列同款,两处保持一致)。失败不阻塞 list。
54459
54977
  beforeRequest: (client) => {
54460
- void client.request("skills/extraRoots/set", { extraRoots: [import_node_path29.default.join(cwd, ".claude", "skills")] }).catch(() => void 0);
54978
+ void client.request("skills/extraRoots/set", { extraRoots: [import_node_path30.default.join(cwd, ".claude", "skills")] }).catch(() => void 0);
54461
54979
  }
54462
54980
  });
54463
54981
  return mapSkillsListResponse(res);
54464
54982
  }
54465
54983
 
54466
54984
  // src/tools/codex-turn-context.ts
54467
- var import_promises3 = __toESM(require("fs/promises"), 1);
54985
+ var import_promises4 = __toESM(require("fs/promises"), 1);
54468
54986
  function parseTurnContextFromRollout(raw) {
54469
54987
  const lines = raw.split("\n");
54470
54988
  for (let i = lines.length - 1; i >= 0; i--) {
@@ -54499,7 +55017,7 @@ async function readCodexTurnContext(args, deps = {}) {
54499
55017
  );
54500
55018
  const rolloutPath = str4(rec3(res?.thread)?.path);
54501
55019
  if (!rolloutPath) return null;
54502
- return parseTurnContextFromRollout(await import_promises3.default.readFile(rolloutPath, "utf8"));
55020
+ return parseTurnContextFromRollout(await import_promises4.default.readFile(rolloutPath, "utf8"));
54503
55021
  }
54504
55022
  function createCodexResolvedMetaHydrator(deps) {
54505
55023
  const fetchedThreadIds = /* @__PURE__ */ new Set();
@@ -54544,14 +55062,14 @@ function str4(v2) {
54544
55062
  // src/workspace/browser.ts
54545
55063
  var import_node_fs28 = __toESM(require("fs"), 1);
54546
55064
  var import_node_os8 = __toESM(require("os"), 1);
54547
- var import_node_path30 = __toESM(require("path"), 1);
55065
+ var import_node_path31 = __toESM(require("path"), 1);
54548
55066
  init_protocol();
54549
55067
  var MAX_FILE_BYTES2 = 2 * 1024 * 1024;
54550
55068
  function resolveInsideCwd(cwd, subpath) {
54551
- const absCwd = import_node_path30.default.resolve(cwd);
54552
- const joined = import_node_path30.default.resolve(absCwd, subpath ?? ".");
54553
- const rel = import_node_path30.default.relative(absCwd, joined);
54554
- if (rel.startsWith("..") || import_node_path30.default.isAbsolute(rel)) {
55069
+ const absCwd = import_node_path31.default.resolve(cwd);
55070
+ const joined = import_node_path31.default.resolve(absCwd, subpath ?? ".");
55071
+ const rel = import_node_path31.default.relative(absCwd, joined);
55072
+ if (rel.startsWith("..") || import_node_path31.default.isAbsolute(rel)) {
54555
55073
  throw new ClawdError(ERROR_CODES.INVALID_PATH, `path escapes cwd: ${subpath}`);
54556
55074
  }
54557
55075
  return joined;
@@ -54582,7 +55100,7 @@ var WorkspaceBrowser = class {
54582
55100
  mtime: ""
54583
55101
  };
54584
55102
  try {
54585
- const st = import_node_fs28.default.statSync(import_node_path30.default.join(full, d.name));
55103
+ const st = import_node_fs28.default.statSync(import_node_path31.default.join(full, d.name));
54586
55104
  entry.mtime = new Date(st.mtimeMs).toISOString();
54587
55105
  if (d.isFile()) entry.size = st.size;
54588
55106
  } catch {
@@ -54629,7 +55147,7 @@ var WorkspaceBrowser = class {
54629
55147
  // src/skills/agents-scanner.ts
54630
55148
  var import_node_fs29 = __toESM(require("fs"), 1);
54631
55149
  var import_node_os9 = __toESM(require("os"), 1);
54632
- var import_node_path31 = __toESM(require("path"), 1);
55150
+ var import_node_path32 = __toESM(require("path"), 1);
54633
55151
  var DEFAULT_POLICY_DIR_DARWIN = "/Library/Application Support/ClaudeCode/.claude/agents";
54634
55152
  function isDirLikeSync2(p2) {
54635
55153
  try {
@@ -54667,10 +55185,10 @@ function scanAgentsDir(dir, source, seen, out) {
54667
55185
  }
54668
55186
  for (const ent of entries) {
54669
55187
  if (!ent.name.endsWith(".md")) continue;
54670
- if (!ent.isFile() && !(ent.isSymbolicLink() && fileExistsSync(import_node_path31.default.join(dir, ent.name)))) {
55188
+ if (!ent.isFile() && !(ent.isSymbolicLink() && fileExistsSync(import_node_path32.default.join(dir, ent.name)))) {
54671
55189
  continue;
54672
55190
  }
54673
- const filePath = import_node_path31.default.join(dir, ent.name);
55191
+ const filePath = import_node_path32.default.join(dir, ent.name);
54674
55192
  const baseName = ent.name.replace(/\.md$/, "");
54675
55193
  if (seen.has(baseName)) continue;
54676
55194
  seen.add(baseName);
@@ -54693,7 +55211,7 @@ function scanPluginAgentsTree(root, pluginName, seen, out) {
54693
55211
  return;
54694
55212
  }
54695
55213
  for (const ent of entries) {
54696
- const childPath = import_node_path31.default.join(dir, ent.name);
55214
+ const childPath = import_node_path32.default.join(dir, ent.name);
54697
55215
  if (ent.isDirectory() || ent.isSymbolicLink() && isDirLikeSync2(childPath)) {
54698
55216
  walk2(childPath, [...namespaces, ent.name]);
54699
55217
  continue;
@@ -54718,9 +55236,9 @@ function scanPluginAgentsTree(root, pluginName, seen, out) {
54718
55236
  walk2(root, []);
54719
55237
  }
54720
55238
  function readInstalledPlugins2(home) {
54721
- const pluginsDir = import_node_path31.default.join(home, ".claude", "plugins");
54722
- const v2 = import_node_path31.default.join(pluginsDir, "installed_plugins_v2.json");
54723
- const v1 = import_node_path31.default.join(pluginsDir, "installed_plugins.json");
55239
+ const pluginsDir = import_node_path32.default.join(home, ".claude", "plugins");
55240
+ const v2 = import_node_path32.default.join(pluginsDir, "installed_plugins_v2.json");
55241
+ const v1 = import_node_path32.default.join(pluginsDir, "installed_plugins.json");
54724
55242
  let raw = null;
54725
55243
  for (const candidate of [v2, v1]) {
54726
55244
  try {
@@ -54749,19 +55267,19 @@ function readInstalledPlugins2(home) {
54749
55267
  return out;
54750
55268
  }
54751
55269
  function walkUpProjectAgentsDirs(startCwd, home, seen, out) {
54752
- let cur = import_node_path31.default.resolve(startCwd);
54753
- const fsRoot = import_node_path31.default.parse(cur).root;
55270
+ let cur = import_node_path32.default.resolve(startCwd);
55271
+ const fsRoot = import_node_path32.default.parse(cur).root;
54754
55272
  while (true) {
54755
- scanAgentsDir(import_node_path31.default.join(cur, ".claude", "agents"), "project", seen, out);
55273
+ scanAgentsDir(import_node_path32.default.join(cur, ".claude", "agents"), "project", seen, out);
54756
55274
  let hasGit = false;
54757
55275
  try {
54758
- hasGit = import_node_fs29.default.existsSync(import_node_path31.default.join(cur, ".git"));
55276
+ hasGit = import_node_fs29.default.existsSync(import_node_path32.default.join(cur, ".git"));
54759
55277
  } catch {
54760
55278
  }
54761
55279
  if (hasGit) return;
54762
55280
  if (cur === home) return;
54763
55281
  if (cur === fsRoot) return;
54764
- const parent = import_node_path31.default.dirname(cur);
55282
+ const parent = import_node_path32.default.dirname(cur);
54765
55283
  if (parent === cur) return;
54766
55284
  cur = parent;
54767
55285
  }
@@ -54796,7 +55314,7 @@ var AgentsScanner = class {
54796
55314
  }
54797
55315
  const fsBlock = [];
54798
55316
  scanAgentsDir(
54799
- import_node_path31.default.join(this.home, ".claude", "agents"),
55317
+ import_node_path32.default.join(this.home, ".claude", "agents"),
54800
55318
  "global",
54801
55319
  seen,
54802
55320
  fsBlock
@@ -54810,7 +55328,7 @@ var AgentsScanner = class {
54810
55328
  ...this.extraPluginRoots
54811
55329
  ];
54812
55330
  for (const { name, root } of plugins) {
54813
- const agentsRoot = import_node_path31.default.join(root, "agents");
55331
+ const agentsRoot = import_node_path32.default.join(root, "agents");
54814
55332
  scanPluginAgentsTree(agentsRoot, name, seen, fsBlock);
54815
55333
  }
54816
55334
  return [...builtinBlock, ...fsBlock];
@@ -54820,13 +55338,13 @@ var AgentsScanner = class {
54820
55338
  // src/observer/session-observer.ts
54821
55339
  var import_node_fs31 = __toESM(require("fs"), 1);
54822
55340
  var import_node_os11 = __toESM(require("os"), 1);
54823
- var import_node_path33 = __toESM(require("path"), 1);
55341
+ var import_node_path34 = __toESM(require("path"), 1);
54824
55342
  init_claude_history();
54825
55343
 
54826
55344
  // src/observer/subagent-meta-observer.ts
54827
55345
  var import_node_fs30 = __toESM(require("fs"), 1);
54828
55346
  var import_node_os10 = __toESM(require("os"), 1);
54829
- var import_node_path32 = __toESM(require("path"), 1);
55347
+ var import_node_path33 = __toESM(require("path"), 1);
54830
55348
  init_claude_history();
54831
55349
  var META_RE = /^agent-([A-Za-z0-9_-]+)\.meta\.json$/;
54832
55350
  var SubagentMetaObserver = class {
@@ -54839,7 +55357,7 @@ var SubagentMetaObserver = class {
54839
55357
  watches = /* @__PURE__ */ new Map();
54840
55358
  // public for spec only:测试直接拼路径写假 meta.json;生产 start() 内部自己解析
54841
55359
  resolveSubagentDir(cwd, toolSessionId) {
54842
- return import_node_path32.default.join(
55360
+ return import_node_path33.default.join(
54843
55361
  this.home,
54844
55362
  ".claude",
54845
55363
  "projects",
@@ -54895,7 +55413,7 @@ var SubagentMetaObserver = class {
54895
55413
  if (!m2) return;
54896
55414
  const agentId = m2[1];
54897
55415
  if (w2.emitted.has(agentId)) return;
54898
- const file = import_node_path32.default.join(w2.dirPath, name);
55416
+ const file = import_node_path33.default.join(w2.dirPath, name);
54899
55417
  let raw;
54900
55418
  try {
54901
55419
  raw = import_node_fs30.default.readFileSync(file, "utf8");
@@ -54955,7 +55473,7 @@ var SessionObserver = class {
54955
55473
  metaObserver;
54956
55474
  resolveJsonlPath(cwd, toolSessionId, override) {
54957
55475
  if (override) return override;
54958
- return import_node_path33.default.join(this.home, ".claude", "projects", cwdToHashDir(cwd), `${toolSessionId}.jsonl`);
55476
+ return import_node_path34.default.join(this.home, ".claude", "projects", cwdToHashDir(cwd), `${toolSessionId}.jsonl`);
54959
55477
  }
54960
55478
  start(args) {
54961
55479
  this.stop(args.sessionId);
@@ -54981,10 +55499,10 @@ var SessionObserver = class {
54981
55499
  lastDeriveMs: 0
54982
55500
  };
54983
55501
  try {
54984
- import_node_fs31.default.mkdirSync(import_node_path33.default.dirname(filePath), { recursive: true });
55502
+ import_node_fs31.default.mkdirSync(import_node_path34.default.dirname(filePath), { recursive: true });
54985
55503
  } catch {
54986
55504
  }
54987
- w2.watcher = import_node_fs31.default.watch(import_node_path33.default.dirname(filePath), { persistent: false }, (_event, changedName) => {
55505
+ w2.watcher = import_node_fs31.default.watch(import_node_path34.default.dirname(filePath), { persistent: false }, (_event, changedName) => {
54988
55506
  if (!changedName || !w2.filePath.endsWith(changedName)) return;
54989
55507
  this.poll(w2);
54990
55508
  });
@@ -55081,7 +55599,7 @@ var SessionObserver = class {
55081
55599
  const now = Date.now();
55082
55600
  if (now - w2.lastRelocateScanMs < 1e3) return;
55083
55601
  w2.lastRelocateScanMs = now;
55084
- const projectsRoot = import_node_path33.default.join(this.home, ".claude", "projects");
55602
+ const projectsRoot = import_node_path34.default.join(this.home, ".claude", "projects");
55085
55603
  const found = findTranscriptByToolSessionId(projectsRoot, w2.toolSessionId);
55086
55604
  if (!found || found === w2.filePath) return;
55087
55605
  try {
@@ -55090,7 +55608,7 @@ var SessionObserver = class {
55090
55608
  }
55091
55609
  w2.filePath = found;
55092
55610
  try {
55093
- w2.watcher = import_node_fs31.default.watch(import_node_path33.default.dirname(found), { persistent: false }, (_event, changedName) => {
55611
+ w2.watcher = import_node_fs31.default.watch(import_node_path34.default.dirname(found), { persistent: false }, (_event, changedName) => {
55094
55612
  if (!changedName || !w2.filePath.endsWith(changedName)) return;
55095
55613
  this.poll(w2);
55096
55614
  });
@@ -55213,7 +55731,7 @@ function listRegistered() {
55213
55731
  var import_node_child_process8 = require("child_process");
55214
55732
  var import_node_fs32 = __toESM(require("fs"), 1);
55215
55733
  var import_node_os12 = __toESM(require("os"), 1);
55216
- var import_node_path34 = __toESM(require("path"), 1);
55734
+ var import_node_path35 = __toESM(require("path"), 1);
55217
55735
  var TITLE_GEN_DIR_PREFIX = "clawd-title-";
55218
55736
  var TITLE_MODEL_BY_TOOL = {
55219
55737
  claude: "haiku",
@@ -55284,7 +55802,7 @@ async function generateSessionTitle(tool, firstMessage, deps = {}) {
55284
55802
  if (!text) return null;
55285
55803
  let workDir;
55286
55804
  try {
55287
- workDir = import_node_fs32.default.mkdtempSync(import_node_path34.default.join(import_node_os12.default.tmpdir(), TITLE_GEN_DIR_PREFIX));
55805
+ workDir = import_node_fs32.default.mkdtempSync(import_node_path35.default.join(import_node_os12.default.tmpdir(), TITLE_GEN_DIR_PREFIX));
55288
55806
  } catch {
55289
55807
  return null;
55290
55808
  }
@@ -55294,8 +55812,8 @@ async function generateSessionTitle(tool, firstMessage, deps = {}) {
55294
55812
  } catch {
55295
55813
  }
55296
55814
  };
55297
- const outFile = import_node_path34.default.join(workDir, `codex-title-${process.pid}-${++outFileSeq}-${Date.now()}.txt`);
55298
- const schemaFile = import_node_path34.default.join(workDir, "title-schema.json");
55815
+ const outFile = import_node_path35.default.join(workDir, `codex-title-${process.pid}-${++outFileSeq}-${Date.now()}.txt`);
55816
+ const schemaFile = import_node_path35.default.join(workDir, "title-schema.json");
55299
55817
  if (tool === "codex") {
55300
55818
  try {
55301
55819
  import_node_fs32.default.writeFileSync(schemaFile, CODEX_TITLE_SCHEMA);
@@ -56033,8 +56551,8 @@ async function authenticate(token, deps) {
56033
56551
  }
56034
56552
 
56035
56553
  // src/legacy-cleanup.ts
56036
- var fs40 = __toESM(require("fs/promises"), 1);
56037
- var path42 = __toESM(require("path"), 1);
56554
+ var fs41 = __toESM(require("fs/promises"), 1);
56555
+ var path43 = __toESM(require("path"), 1);
56038
56556
  var LEGACY_FILES = [
56039
56557
  "personal-capability.json",
56040
56558
  "received-capabilities.json",
@@ -56043,9 +56561,9 @@ var LEGACY_FILES = [
56043
56561
  async function cleanupLegacyCapabilityFiles(dataDir, logger) {
56044
56562
  const removed = [];
56045
56563
  for (const name of LEGACY_FILES) {
56046
- const p2 = path42.join(dataDir, name);
56564
+ const p2 = path43.join(dataDir, name);
56047
56565
  try {
56048
- await fs40.unlink(p2);
56566
+ await fs41.unlink(p2);
56049
56567
  removed.push(name);
56050
56568
  logger.debug("legacy capability file removed", { file: name });
56051
56569
  } catch (err) {
@@ -56057,13 +56575,13 @@ async function cleanupLegacyCapabilityFiles(dataDir, logger) {
56057
56575
  }
56058
56576
 
56059
56577
  // src/inbox/inbox-store.ts
56060
- var fs41 = __toESM(require("fs"), 1);
56061
- var path43 = __toESM(require("path"), 1);
56578
+ var fs42 = __toESM(require("fs"), 1);
56579
+ var path44 = __toESM(require("path"), 1);
56062
56580
  var INBOX_SUBDIR = "inbox";
56063
56581
  var InboxStore = class {
56064
56582
  constructor(dataDir) {
56065
56583
  this.dataDir = dataDir;
56066
- fs41.mkdirSync(this.dirPath(), { recursive: true });
56584
+ fs42.mkdirSync(this.dirPath(), { recursive: true });
56067
56585
  }
56068
56586
  dataDir;
56069
56587
  /**
@@ -56075,7 +56593,7 @@ var InboxStore = class {
56075
56593
  const file = this.filePath(peerDeviceId);
56076
56594
  let raw;
56077
56595
  try {
56078
- raw = fs41.readFileSync(file, "utf8");
56596
+ raw = fs42.readFileSync(file, "utf8");
56079
56597
  } catch (err) {
56080
56598
  if (err?.code === "ENOENT") return [];
56081
56599
  return [];
@@ -56091,7 +56609,7 @@ var InboxStore = class {
56091
56609
  const dir = this.dirPath();
56092
56610
  let entries;
56093
56611
  try {
56094
- entries = fs41.readdirSync(dir);
56612
+ entries = fs42.readdirSync(dir);
56095
56613
  } catch (err) {
56096
56614
  if (err?.code === "ENOENT") return [];
56097
56615
  return [];
@@ -56107,9 +56625,9 @@ var InboxStore = class {
56107
56625
  if (existing.some((m2) => m2.id === message.id)) return;
56108
56626
  const file = this.filePath(message.peerDeviceId);
56109
56627
  const line = JSON.stringify(message) + "\n";
56110
- fs41.appendFileSync(file, line, { mode: 384 });
56628
+ fs42.appendFileSync(file, line, { mode: 384 });
56111
56629
  try {
56112
- fs41.chmodSync(file, 384);
56630
+ fs42.chmodSync(file, 384);
56113
56631
  } catch {
56114
56632
  }
56115
56633
  }
@@ -56139,7 +56657,7 @@ var InboxStore = class {
56139
56657
  removeByPeerDeviceId(peerDeviceId) {
56140
56658
  const file = this.filePath(peerDeviceId);
56141
56659
  try {
56142
- fs41.unlinkSync(file);
56660
+ fs42.unlinkSync(file);
56143
56661
  } catch (err) {
56144
56662
  if (err?.code === "ENOENT") return;
56145
56663
  }
@@ -56148,18 +56666,18 @@ var InboxStore = class {
56148
56666
  const file = this.filePath(peerDeviceId);
56149
56667
  const tmp = `${file}.tmp-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2)}`;
56150
56668
  const content = messages.map((m2) => JSON.stringify(m2)).join("\n") + (messages.length > 0 ? "\n" : "");
56151
- fs41.writeFileSync(tmp, content, { mode: 384 });
56152
- fs41.renameSync(tmp, file);
56669
+ fs42.writeFileSync(tmp, content, { mode: 384 });
56670
+ fs42.renameSync(tmp, file);
56153
56671
  try {
56154
- fs41.chmodSync(file, 384);
56672
+ fs42.chmodSync(file, 384);
56155
56673
  } catch {
56156
56674
  }
56157
56675
  }
56158
56676
  dirPath() {
56159
- return path43.join(this.dataDir, INBOX_SUBDIR);
56677
+ return path44.join(this.dataDir, INBOX_SUBDIR);
56160
56678
  }
56161
56679
  filePath(peerDeviceId) {
56162
- return path43.join(this.dirPath(), `${peerDeviceId}.jsonl`);
56680
+ return path44.join(this.dirPath(), `${peerDeviceId}.jsonl`);
56163
56681
  }
56164
56682
  };
56165
56683
  function parseAllLines(raw) {
@@ -56247,8 +56765,8 @@ var InboxManager = class {
56247
56765
  };
56248
56766
 
56249
56767
  // src/state/contact-store.ts
56250
- var fs42 = __toESM(require("fs"), 1);
56251
- var path44 = __toESM(require("path"), 1);
56768
+ var fs43 = __toESM(require("fs"), 1);
56769
+ var path45 = __toESM(require("path"), 1);
56252
56770
  var FILE_NAME = "contacts.json";
56253
56771
  var ContactStore = class {
56254
56772
  constructor(dataDir) {
@@ -56258,10 +56776,10 @@ var ContactStore = class {
56258
56776
  contacts = /* @__PURE__ */ new Map();
56259
56777
  load() {
56260
56778
  this.contacts.clear();
56261
- const file = path44.join(this.dataDir, FILE_NAME);
56779
+ const file = path45.join(this.dataDir, FILE_NAME);
56262
56780
  let raw;
56263
56781
  try {
56264
- raw = fs42.readFileSync(file, "utf8");
56782
+ raw = fs43.readFileSync(file, "utf8");
56265
56783
  } catch (err) {
56266
56784
  if (err?.code !== "ENOENT") this.renameBak(file);
56267
56785
  return;
@@ -56330,20 +56848,20 @@ var ContactStore = class {
56330
56848
  return true;
56331
56849
  }
56332
56850
  flush() {
56333
- const file = path44.join(this.dataDir, FILE_NAME);
56851
+ const file = path45.join(this.dataDir, FILE_NAME);
56334
56852
  const tmp = `${file}.tmp-${process.pid}-${Date.now()}`;
56335
56853
  const content = JSON.stringify(
56336
56854
  { contacts: Array.from(this.contacts.values()) },
56337
56855
  null,
56338
56856
  2
56339
56857
  );
56340
- fs42.mkdirSync(this.dataDir, { recursive: true });
56341
- fs42.writeFileSync(tmp, content, { mode: 384 });
56342
- fs42.renameSync(tmp, file);
56858
+ fs43.mkdirSync(this.dataDir, { recursive: true });
56859
+ fs43.writeFileSync(tmp, content, { mode: 384 });
56860
+ fs43.renameSync(tmp, file);
56343
56861
  }
56344
56862
  renameBak(file) {
56345
56863
  try {
56346
- fs42.renameSync(file, `${file}.bak`);
56864
+ fs43.renameSync(file, `${file}.bak`);
56347
56865
  } catch {
56348
56866
  }
56349
56867
  }
@@ -56451,61 +56969,61 @@ async function connectRemote(args) {
56451
56969
  }
56452
56970
 
56453
56971
  // src/migrations/2026-05-20-flatten-sessions.ts
56454
- var fs43 = __toESM(require("fs"), 1);
56455
- var path45 = __toESM(require("path"), 1);
56972
+ var fs44 = __toESM(require("fs"), 1);
56973
+ var path46 = __toESM(require("path"), 1);
56456
56974
  var MIGRATION_FLAG_NAME = ".migration.v1.done";
56457
56975
  function migrateFlattenSessions(opts) {
56458
56976
  const dataDir = opts.dataDir;
56459
56977
  const now = opts.now ?? Date.now;
56460
- const sessionsDir = path45.join(dataDir, "sessions");
56461
- const flagPath = path45.join(sessionsDir, MIGRATION_FLAG_NAME);
56978
+ const sessionsDir = path46.join(dataDir, "sessions");
56979
+ const flagPath = path46.join(sessionsDir, MIGRATION_FLAG_NAME);
56462
56980
  if (existsSync3(flagPath)) {
56463
56981
  return { skipped: true, flagWritten: false, movedBare: 0, movedVmOwner: 0, archivedListener: 0 };
56464
56982
  }
56465
56983
  let movedBare = 0;
56466
56984
  let movedVmOwner = 0;
56467
56985
  let archivedListener = 0;
56468
- const defaultDir = path45.join(sessionsDir, "default");
56986
+ const defaultDir = path46.join(sessionsDir, "default");
56469
56987
  if (existsSync3(defaultDir)) {
56470
56988
  for (const entry of readdirSafe(defaultDir)) {
56471
56989
  if (!entry.endsWith(".json")) continue;
56472
- const src = path45.join(defaultDir, entry);
56473
- const dst = path45.join(sessionsDir, entry);
56474
- fs43.renameSync(src, dst);
56990
+ const src = path46.join(defaultDir, entry);
56991
+ const dst = path46.join(sessionsDir, entry);
56992
+ fs44.renameSync(src, dst);
56475
56993
  movedBare += 1;
56476
56994
  }
56477
56995
  rmdirIfEmpty(defaultDir);
56478
56996
  }
56479
56997
  for (const pid of readdirSafe(sessionsDir)) {
56480
- const personaDir = path45.join(sessionsDir, pid);
56998
+ const personaDir = path46.join(sessionsDir, pid);
56481
56999
  if (!isDir(personaDir)) continue;
56482
57000
  if (pid === "default") continue;
56483
- const ownerSrc = path45.join(personaDir, "owner");
57001
+ const ownerSrc = path46.join(personaDir, "owner");
56484
57002
  if (existsSync3(ownerSrc) && isDir(ownerSrc)) {
56485
- const ownerDst = path45.join(dataDir, "personas", pid, ".clawd", "sessions", "owner");
56486
- fs43.mkdirSync(ownerDst, { recursive: true });
57003
+ const ownerDst = path46.join(dataDir, "personas", pid, ".clawd", "sessions", "owner");
57004
+ fs44.mkdirSync(ownerDst, { recursive: true });
56487
57005
  for (const file of readdirSafe(ownerSrc)) {
56488
57006
  if (!file.endsWith(".json")) continue;
56489
- fs43.renameSync(path45.join(ownerSrc, file), path45.join(ownerDst, file));
57007
+ fs44.renameSync(path46.join(ownerSrc, file), path46.join(ownerDst, file));
56490
57008
  movedVmOwner += 1;
56491
57009
  }
56492
57010
  rmdirIfEmpty(ownerSrc);
56493
57011
  }
56494
- const listenerSrc = path45.join(personaDir, "listener");
57012
+ const listenerSrc = path46.join(personaDir, "listener");
56495
57013
  if (existsSync3(listenerSrc) && isDir(listenerSrc)) {
56496
- const archiveDst = path45.join(dataDir, ".legacy", `listener-${pid}`);
56497
- fs43.mkdirSync(archiveDst, { recursive: true });
57014
+ const archiveDst = path46.join(dataDir, ".legacy", `listener-${pid}`);
57015
+ fs44.mkdirSync(archiveDst, { recursive: true });
56498
57016
  for (const file of readdirSafe(listenerSrc)) {
56499
57017
  if (!file.endsWith(".json")) continue;
56500
- fs43.renameSync(path45.join(listenerSrc, file), path45.join(archiveDst, file));
57018
+ fs44.renameSync(path46.join(listenerSrc, file), path46.join(archiveDst, file));
56501
57019
  archivedListener += 1;
56502
57020
  }
56503
57021
  rmdirIfEmpty(listenerSrc);
56504
57022
  }
56505
57023
  rmdirIfEmpty(personaDir);
56506
57024
  }
56507
- fs43.mkdirSync(sessionsDir, { recursive: true });
56508
- fs43.writeFileSync(flagPath, JSON.stringify({ migratedAt: now() }, null, 2));
57025
+ fs44.mkdirSync(sessionsDir, { recursive: true });
57026
+ fs44.writeFileSync(flagPath, JSON.stringify({ migratedAt: now() }, null, 2));
56509
57027
  return {
56510
57028
  skipped: false,
56511
57029
  flagWritten: true,
@@ -56516,7 +57034,7 @@ function migrateFlattenSessions(opts) {
56516
57034
  }
56517
57035
  function existsSync3(p2) {
56518
57036
  try {
56519
- fs43.statSync(p2);
57037
+ fs44.statSync(p2);
56520
57038
  return true;
56521
57039
  } catch {
56522
57040
  return false;
@@ -56524,61 +57042,61 @@ function existsSync3(p2) {
56524
57042
  }
56525
57043
  function isDir(p2) {
56526
57044
  try {
56527
- return fs43.statSync(p2).isDirectory();
57045
+ return fs44.statSync(p2).isDirectory();
56528
57046
  } catch {
56529
57047
  return false;
56530
57048
  }
56531
57049
  }
56532
57050
  function readdirSafe(p2) {
56533
57051
  try {
56534
- return fs43.readdirSync(p2);
57052
+ return fs44.readdirSync(p2);
56535
57053
  } catch {
56536
57054
  return [];
56537
57055
  }
56538
57056
  }
56539
57057
  function rmdirIfEmpty(p2) {
56540
57058
  try {
56541
- fs43.rmdirSync(p2);
57059
+ fs44.rmdirSync(p2);
56542
57060
  } catch {
56543
57061
  }
56544
57062
  }
56545
57063
 
56546
57064
  // src/migrations/2026-08-18-drop-seeded-projects.ts
56547
- var fs44 = __toESM(require("fs"), 1);
56548
- var path46 = __toESM(require("path"), 1);
57065
+ var fs45 = __toESM(require("fs"), 1);
57066
+ var path47 = __toESM(require("path"), 1);
56549
57067
  var DROP_SEEDED_PROJECTS_FLAG = ".projects-seed-dropped.v1.done";
56550
57068
  function dropSeededProjects(opts) {
56551
- fs44.mkdirSync(opts.dataDir, { recursive: true });
56552
- const flag = path46.join(opts.dataDir, DROP_SEEDED_PROJECTS_FLAG);
56553
- if (fs44.existsSync(flag)) return { skipped: true, dropped: 0, backup: null };
56554
- const file = path46.join(opts.dataDir, "projects.json");
57069
+ fs45.mkdirSync(opts.dataDir, { recursive: true });
57070
+ const flag = path47.join(opts.dataDir, DROP_SEEDED_PROJECTS_FLAG);
57071
+ if (fs45.existsSync(flag)) return { skipped: true, dropped: 0, backup: null };
57072
+ const file = path47.join(opts.dataDir, "projects.json");
56555
57073
  let raw = null;
56556
57074
  let dropped = 0;
56557
57075
  try {
56558
- raw = fs44.readFileSync(file, "utf8");
57076
+ raw = fs45.readFileSync(file, "utf8");
56559
57077
  dropped = JSON.parse(raw).projects?.length ?? 0;
56560
57078
  } catch {
56561
57079
  }
56562
57080
  let backup = null;
56563
57081
  if (raw != null && dropped > 0) {
56564
- const dir = path46.join(opts.dataDir, "backups");
56565
- fs44.mkdirSync(dir, { recursive: true });
56566
- backup = path46.join(dir, `projects.seed-dropped.${(opts.now ?? Date.now)()}.json`);
56567
- fs44.writeFileSync(backup, raw);
57082
+ const dir = path47.join(opts.dataDir, "backups");
57083
+ fs45.mkdirSync(dir, { recursive: true });
57084
+ backup = path47.join(dir, `projects.seed-dropped.${(opts.now ?? Date.now)()}.json`);
57085
+ fs45.writeFileSync(backup, raw);
56568
57086
  }
56569
57087
  if (raw != null || dropped > 0) {
56570
- fs44.writeFileSync(file, JSON.stringify({ projects: [] }, null, 2));
57088
+ fs45.writeFileSync(file, JSON.stringify({ projects: [] }, null, 2));
56571
57089
  }
56572
- fs44.writeFileSync(flag, (/* @__PURE__ */ new Date()).toISOString());
57090
+ fs45.writeFileSync(flag, (/* @__PURE__ */ new Date()).toISOString());
56573
57091
  return { skipped: false, dropped, backup };
56574
57092
  }
56575
57093
 
56576
57094
  // src/transport/http-router.ts
56577
57095
  var import_node_fs34 = __toESM(require("fs"), 1);
56578
- var import_node_path38 = __toESM(require("path"), 1);
57096
+ var import_node_path39 = __toESM(require("path"), 1);
56579
57097
 
56580
57098
  // src/attachment/mime.ts
56581
- var import_node_path35 = __toESM(require("path"), 1);
57099
+ var import_node_path36 = __toESM(require("path"), 1);
56582
57100
  var TEXT_PLAIN = "text/plain; charset=utf-8";
56583
57101
  var EXT_TO_NATIVE_MIME = {
56584
57102
  // 图片
@@ -56685,7 +57203,7 @@ var TEXT_EXTENSIONS = /* @__PURE__ */ new Set([
56685
57203
  ".mk"
56686
57204
  ]);
56687
57205
  function lookupMime(filePathOrName) {
56688
- const ext = import_node_path35.default.extname(filePathOrName).toLowerCase();
57206
+ const ext = import_node_path36.default.extname(filePathOrName).toLowerCase();
56689
57207
  if (EXT_TO_NATIVE_MIME[ext]) return EXT_TO_NATIVE_MIME[ext];
56690
57208
  if (TEXT_EXTENSIONS.has(ext)) return TEXT_PLAIN;
56691
57209
  return "application/octet-stream";
@@ -56756,9 +57274,9 @@ function verifySignedUrl(secret, absPath, eRaw, s, now = Date.now) {
56756
57274
 
56757
57275
  // src/attachment/upload.ts
56758
57276
  var import_node_fs33 = __toESM(require("fs"), 1);
56759
- var import_node_path36 = __toESM(require("path"), 1);
57277
+ var import_node_path37 = __toESM(require("path"), 1);
56760
57278
  var import_node_crypto9 = __toESM(require("crypto"), 1);
56761
- var import_promises4 = require("stream/promises");
57279
+ var import_promises5 = require("stream/promises");
56762
57280
  var UploadError = class extends Error {
56763
57281
  constructor(code, message) {
56764
57282
  super(message);
@@ -56768,14 +57286,14 @@ var UploadError = class extends Error {
56768
57286
  code;
56769
57287
  };
56770
57288
  function assertValidFileName(fileName) {
56771
- if (fileName.length === 0 || fileName === "." || fileName === ".." || fileName.startsWith(".") || fileName.includes("/") || fileName.includes("\\") || fileName !== import_node_path36.default.basename(fileName)) {
57289
+ if (fileName.length === 0 || fileName === "." || fileName === ".." || fileName.startsWith(".") || fileName.includes("/") || fileName.includes("\\") || fileName !== import_node_path37.default.basename(fileName)) {
56772
57290
  throw new UploadError("INVALID_FILENAME", `fileName must be a plain basename, got: ${fileName}`);
56773
57291
  }
56774
57292
  }
56775
57293
  var HASH_PREFIX_LEN = 16;
56776
57294
  async function writeUploadedAttachment(args) {
56777
57295
  assertValidFileName(args.fileName);
56778
- const attachmentsRoot = import_node_path36.default.join(args.baseDir, ".attachments");
57296
+ const attachmentsRoot = import_node_path37.default.join(args.baseDir, ".attachments");
56779
57297
  try {
56780
57298
  import_node_fs33.default.mkdirSync(attachmentsRoot, { recursive: true });
56781
57299
  } catch (err) {
@@ -56783,12 +57301,12 @@ async function writeUploadedAttachment(args) {
56783
57301
  }
56784
57302
  const hasher = import_node_crypto9.default.createHash("sha256");
56785
57303
  let actualSize = 0;
56786
- const tmpPath = import_node_path36.default.join(
57304
+ const tmpPath = import_node_path37.default.join(
56787
57305
  attachmentsRoot,
56788
57306
  `.upload-${process.pid}-${Date.now()}-${import_node_crypto9.default.randomBytes(4).toString("hex")}`
56789
57307
  );
56790
57308
  try {
56791
- await (0, import_promises4.pipeline)(
57309
+ await (0, import_promises5.pipeline)(
56792
57310
  args.body,
56793
57311
  async function* (source) {
56794
57312
  for await (const chunk of source) {
@@ -56818,7 +57336,7 @@ async function writeUploadedAttachment(args) {
56818
57336
  );
56819
57337
  }
56820
57338
  const attachmentId = hasher.digest("hex").slice(0, HASH_PREFIX_LEN);
56821
- const hashDir = import_node_path36.default.join(attachmentsRoot, attachmentId);
57339
+ const hashDir = import_node_path37.default.join(attachmentsRoot, attachmentId);
56822
57340
  let finalFileName;
56823
57341
  let hashDirExists = false;
56824
57342
  try {
@@ -56836,7 +57354,7 @@ async function writeUploadedAttachment(args) {
56836
57354
  try {
56837
57355
  import_node_fs33.default.mkdirSync(hashDir, { recursive: true });
56838
57356
  finalFileName = args.fileName;
56839
- import_node_fs33.default.renameSync(tmpPath, import_node_path36.default.join(hashDir, finalFileName));
57357
+ import_node_fs33.default.renameSync(tmpPath, import_node_path37.default.join(hashDir, finalFileName));
56840
57358
  } catch (err) {
56841
57359
  try {
56842
57360
  import_node_fs33.default.unlinkSync(tmpPath);
@@ -56845,7 +57363,7 @@ async function writeUploadedAttachment(args) {
56845
57363
  throw new UploadError("STORAGE_ERROR", `rename failed: ${err.message}`);
56846
57364
  }
56847
57365
  }
56848
- const absPath = import_node_path36.default.join(hashDir, finalFileName);
57366
+ const absPath = import_node_path37.default.join(hashDir, finalFileName);
56849
57367
  const relPath = absPath;
56850
57368
  args.groupFileStore.upsert(args.scope, args.sessionId, {
56851
57369
  relPath: absPath,
@@ -56859,8 +57377,8 @@ async function writeUploadedAttachment(args) {
56859
57377
  }
56860
57378
 
56861
57379
  // src/extension/import.ts
56862
- var import_promises5 = __toESM(require("fs/promises"), 1);
56863
- var import_node_path37 = __toESM(require("path"), 1);
57380
+ var import_promises6 = __toESM(require("fs/promises"), 1);
57381
+ var import_node_path38 = __toESM(require("path"), 1);
56864
57382
  var import_node_os13 = __toESM(require("os"), 1);
56865
57383
  var import_jszip = __toESM(require_lib3(), 1);
56866
57384
  var ImportError = class extends Error {
@@ -56878,7 +57396,7 @@ async function importZip(buf, root) {
56878
57396
  throw new ImportError("ZIP_INVALID", `failed to load zip: ${e.message}`);
56879
57397
  }
56880
57398
  for (const name of Object.keys(zip.files)) {
56881
- if (name.includes("..") || name.startsWith("/") || import_node_path37.default.isAbsolute(name)) {
57399
+ if (name.includes("..") || name.startsWith("/") || import_node_path38.default.isAbsolute(name)) {
56882
57400
  throw new ImportError("ZIP_INVALID", `unsafe zip entry path: ${name}`);
56883
57401
  }
56884
57402
  }
@@ -56911,33 +57429,33 @@ async function importZip(buf, root) {
56911
57429
  );
56912
57430
  }
56913
57431
  }
56914
- const destDir = import_node_path37.default.join(root, manifest.id);
57432
+ const destDir = import_node_path38.default.join(root, manifest.id);
56915
57433
  let destExists = false;
56916
57434
  try {
56917
- await import_promises5.default.access(destDir);
57435
+ await import_promises6.default.access(destDir);
56918
57436
  destExists = true;
56919
57437
  } catch {
56920
57438
  }
56921
57439
  if (destExists) {
56922
57440
  throw new ImportError("ALREADY_EXISTS", `extension ${manifest.id} already installed`);
56923
57441
  }
56924
- const stage = await import_promises5.default.mkdtemp(import_node_path37.default.join(import_node_os13.default.tmpdir(), `clawd-ext-stage-${manifest.id}-`));
57442
+ const stage = await import_promises6.default.mkdtemp(import_node_path38.default.join(import_node_os13.default.tmpdir(), `clawd-ext-stage-${manifest.id}-`));
56925
57443
  try {
56926
57444
  for (const [name, entry] of Object.entries(zip.files)) {
56927
- const dest = import_node_path37.default.join(stage, name);
57445
+ const dest = import_node_path38.default.join(stage, name);
56928
57446
  if (entry.dir) {
56929
- await import_promises5.default.mkdir(dest, { recursive: true });
57447
+ await import_promises6.default.mkdir(dest, { recursive: true });
56930
57448
  continue;
56931
57449
  }
56932
- await import_promises5.default.mkdir(import_node_path37.default.dirname(dest), { recursive: true });
57450
+ await import_promises6.default.mkdir(import_node_path38.default.dirname(dest), { recursive: true });
56933
57451
  const content = await entry.async("nodebuffer");
56934
- await import_promises5.default.writeFile(dest, content);
57452
+ await import_promises6.default.writeFile(dest, content);
56935
57453
  }
56936
- await import_promises5.default.mkdir(root, { recursive: true });
56937
- await import_promises5.default.rename(stage, destDir);
57454
+ await import_promises6.default.mkdir(root, { recursive: true });
57455
+ await import_promises6.default.rename(stage, destDir);
56938
57456
  return { extId: manifest.id };
56939
57457
  } catch (e) {
56940
- await import_promises5.default.rm(stage, { recursive: true, force: true }).catch(() => {
57458
+ await import_promises6.default.rm(stage, { recursive: true, force: true }).catch(() => {
56941
57459
  });
56942
57460
  throw e;
56943
57461
  }
@@ -56959,7 +57477,7 @@ var SHARE_UI_ASSET_MIME = {
56959
57477
  ".wasm": "application/wasm"
56960
57478
  };
56961
57479
  function shareUiAssetMime(filePath) {
56962
- const ext = import_node_path38.default.extname(filePath).toLowerCase();
57480
+ const ext = import_node_path39.default.extname(filePath).toLowerCase();
56963
57481
  return SHARE_UI_ASSET_MIME[ext] ?? lookupMime(filePath);
56964
57482
  }
56965
57483
  var AUTHED_RPC_ROUTE_PREFIX = "/rpc/";
@@ -57058,7 +57576,7 @@ function isValidUploadFileName(fileName) {
57058
57576
  if (fileName === "." || fileName === "..") return false;
57059
57577
  if (fileName.startsWith(".")) return false;
57060
57578
  if (fileName.includes("/") || fileName.includes("\\")) return false;
57061
- return fileName === import_node_path38.default.basename(fileName);
57579
+ return fileName === import_node_path39.default.basename(fileName);
57062
57580
  }
57063
57581
  function createHttpRouter(deps) {
57064
57582
  return async (req, res) => {
@@ -57294,7 +57812,7 @@ function createHttpRouter(deps) {
57294
57812
  sendHtml(res, statusByCode[r.code], loader.renderErrorHtml(r.code, msgByCode[r.code]));
57295
57813
  return true;
57296
57814
  }
57297
- sendHtml(res, 200, loader.renderViewerHtml(import_node_path38.default.basename(r.absPath)));
57815
+ sendHtml(res, 200, loader.renderViewerHtml(import_node_path39.default.basename(r.absPath)));
57298
57816
  return true;
57299
57817
  }
57300
57818
  const ctx = deps.authResolver.resolveFromHeader(
@@ -57415,7 +57933,7 @@ function createHttpRouter(deps) {
57415
57933
  return true;
57416
57934
  }
57417
57935
  let absPath;
57418
- if (import_node_path38.default.isAbsolute(pathParam)) {
57936
+ if (import_node_path39.default.isAbsolute(pathParam)) {
57419
57937
  absPath = pathParam;
57420
57938
  } else if (deps.sessionStore) {
57421
57939
  const file = deps.sessionStore.read(sid);
@@ -57423,7 +57941,7 @@ function createHttpRouter(deps) {
57423
57941
  sendJson(res, 404, { code: "NOT_FOUND", message: `session ${sid} not found` });
57424
57942
  return true;
57425
57943
  }
57426
- absPath = import_node_path38.default.join(file.cwd, pathParam);
57944
+ absPath = import_node_path39.default.join(file.cwd, pathParam);
57427
57945
  } else {
57428
57946
  sendJson(res, 501, withCtx(ctx, { code: "NOT_IMPLEMENTED", message: "sessionStore not wired" }));
57429
57947
  return true;
@@ -57525,7 +58043,7 @@ function streamFile(res, absPath, logger) {
57525
58043
  return;
57526
58044
  }
57527
58045
  const mime = lookupMime(absPath);
57528
- const basename2 = import_node_path38.default.basename(absPath);
58046
+ const basename2 = import_node_path39.default.basename(absPath);
57529
58047
  res.writeHead(200, {
57530
58048
  "Content-Type": mime,
57531
58049
  "Content-Length": String(stat.size),
@@ -57543,7 +58061,7 @@ function streamFile(res, absPath, logger) {
57543
58061
 
57544
58062
  // src/attachment/gc.ts
57545
58063
  var import_node_fs35 = __toESM(require("fs"), 1);
57546
- var import_node_path39 = __toESM(require("path"), 1);
58064
+ var import_node_path40 = __toESM(require("path"), 1);
57547
58065
  var DEFAULT_TTL_MS = 30 * 24 * 3600 * 1e3;
57548
58066
  function runAttachmentGc(args) {
57549
58067
  const now = (args.now ?? Date.now)();
@@ -57552,12 +58070,12 @@ function runAttachmentGc(args) {
57552
58070
  for (const { scope, sessionId } of args.sessionScopes) {
57553
58071
  for (const entry of args.groupFileStore.list(scope, sessionId)) {
57554
58072
  if (entry.stale) continue;
57555
- if (import_node_path39.default.isAbsolute(entry.relPath)) liveAbs.add(entry.relPath);
58073
+ if (import_node_path40.default.isAbsolute(entry.relPath)) liveAbs.add(entry.relPath);
57556
58074
  }
57557
58075
  }
57558
58076
  const visited = /* @__PURE__ */ new Set();
57559
58077
  for (const { scope, sessionId } of args.sessionScopes) {
57560
- const legacyDir = args.getSessionCwd?.(sessionId) ?? import_node_path39.default.join(
58078
+ const legacyDir = args.getSessionCwd?.(sessionId) ?? import_node_path40.default.join(
57561
58079
  args.dataDir,
57562
58080
  "sessions",
57563
58081
  ...scopeSubPath(scope).map(safeFileName),
@@ -57570,7 +58088,7 @@ function runAttachmentGc(args) {
57570
58088
  return true;
57571
58089
  });
57572
58090
  for (const rootDir of roots) {
57573
- const attRoot = import_node_path39.default.join(rootDir, ".attachments");
58091
+ const attRoot = import_node_path40.default.join(rootDir, ".attachments");
57574
58092
  let hashDirs;
57575
58093
  try {
57576
58094
  hashDirs = import_node_fs35.default.readdirSync(attRoot);
@@ -57580,7 +58098,7 @@ function runAttachmentGc(args) {
57580
58098
  continue;
57581
58099
  }
57582
58100
  for (const hashDir of hashDirs) {
57583
- const hashDirAbs = import_node_path39.default.join(attRoot, hashDir);
58101
+ const hashDirAbs = import_node_path40.default.join(attRoot, hashDir);
57584
58102
  let files;
57585
58103
  try {
57586
58104
  files = import_node_fs35.default.readdirSync(hashDirAbs);
@@ -57588,7 +58106,7 @@ function runAttachmentGc(args) {
57588
58106
  continue;
57589
58107
  }
57590
58108
  for (const name of files) {
57591
- const file = import_node_path39.default.join(hashDirAbs, name);
58109
+ const file = import_node_path40.default.join(hashDirAbs, name);
57592
58110
  let stat;
57593
58111
  try {
57594
58112
  stat = import_node_fs35.default.statSync(file);
@@ -57620,7 +58138,7 @@ function runAttachmentGc(args) {
57620
58138
 
57621
58139
  // src/attachment/group.ts
57622
58140
  var import_node_fs36 = __toESM(require("fs"), 1);
57623
- var import_node_path40 = __toESM(require("path"), 1);
58141
+ var import_node_path41 = __toESM(require("path"), 1);
57624
58142
  var import_node_crypto10 = __toESM(require("crypto"), 1);
57625
58143
  init_protocol();
57626
58144
  var GroupFileStore = class {
@@ -57632,11 +58150,11 @@ var GroupFileStore = class {
57632
58150
  this.logger = opts.logger;
57633
58151
  }
57634
58152
  rootForScope(scope) {
57635
- return import_node_path40.default.join(this.dataDir, "sessions", ...scopeSubPath(scope).map(safeFileName));
58153
+ return import_node_path41.default.join(this.dataDir, "sessions", ...scopeSubPath(scope).map(safeFileName));
57636
58154
  }
57637
58155
  /** 与 SessionStore.filePath 平级,扩展名 .group-files.json */
57638
58156
  filePath(scope, sessionId) {
57639
- return import_node_path40.default.join(this.rootForScope(scope), `${safeFileName(sessionId)}.group-files.json`);
58157
+ return import_node_path41.default.join(this.rootForScope(scope), `${safeFileName(sessionId)}.group-files.json`);
57640
58158
  }
57641
58159
  cacheKey(scope, sessionId) {
57642
58160
  return scope.kind === "default" ? `default::${sessionId}` : `persona:${scope.personaId}:${scope.mode}::${sessionId}`;
@@ -57671,7 +58189,7 @@ var GroupFileStore = class {
57671
58189
  }
57672
58190
  writeFile(scope, sessionId, entries) {
57673
58191
  const file = this.filePath(scope, sessionId);
57674
- import_node_fs36.default.mkdirSync(import_node_path40.default.dirname(file), { recursive: true });
58192
+ import_node_fs36.default.mkdirSync(import_node_path41.default.dirname(file), { recursive: true });
57675
58193
  const tmp = `${file}.tmp-${process.pid}-${Date.now()}`;
57676
58194
  import_node_fs36.default.writeFileSync(tmp, JSON.stringify(entries, null, 2), { mode: 384 });
57677
58195
  import_node_fs36.default.renameSync(tmp, file);
@@ -57761,9 +58279,9 @@ var GroupFileStore = class {
57761
58279
 
57762
58280
  // src/discovery/state-file.ts
57763
58281
  var import_node_fs37 = __toESM(require("fs"), 1);
57764
- var import_node_path41 = __toESM(require("path"), 1);
58282
+ var import_node_path42 = __toESM(require("path"), 1);
57765
58283
  function defaultStateFilePath(dataDir) {
57766
- return import_node_path41.default.join(dataDir, "state.json");
58284
+ return import_node_path42.default.join(dataDir, "state.json");
57767
58285
  }
57768
58286
  function isPidAlive(pid) {
57769
58287
  if (!Number.isFinite(pid) || pid <= 0) return false;
@@ -57799,7 +58317,7 @@ var StateFileManager = class {
57799
58317
  return { status: "stale", existing };
57800
58318
  }
57801
58319
  write(state) {
57802
- import_node_fs37.default.mkdirSync(import_node_path41.default.dirname(this.file), { recursive: true });
58320
+ import_node_fs37.default.mkdirSync(import_node_path42.default.dirname(this.file), { recursive: true });
57803
58321
  const tmp = `${this.file}.tmp.${process.pid}.${Date.now()}`;
57804
58322
  import_node_fs37.default.writeFileSync(tmp, JSON.stringify(state, null, 2), { mode: 384 });
57805
58323
  import_node_fs37.default.renameSync(tmp, this.file);
@@ -57827,13 +58345,13 @@ function readSpawnedByDesktopFromEnv(env = process.env) {
57827
58345
 
57828
58346
  // src/tunnel/tunnel-manager.ts
57829
58347
  var import_node_fs41 = __toESM(require("fs"), 1);
57830
- var import_node_path45 = __toESM(require("path"), 1);
58348
+ var import_node_path46 = __toESM(require("path"), 1);
57831
58349
  var import_node_crypto11 = __toESM(require("crypto"), 1);
57832
58350
  var import_node_child_process11 = require("child_process");
57833
58351
 
57834
58352
  // src/tunnel/tunnel-store.ts
57835
58353
  var import_node_fs38 = __toESM(require("fs"), 1);
57836
- var import_node_path42 = __toESM(require("path"), 1);
58354
+ var import_node_path43 = __toESM(require("path"), 1);
57837
58355
  var TunnelStore = class {
57838
58356
  constructor(filePath) {
57839
58357
  this.filePath = filePath;
@@ -57852,7 +58370,7 @@ var TunnelStore = class {
57852
58370
  }
57853
58371
  }
57854
58372
  async set(v2) {
57855
- const dir = import_node_path42.default.dirname(this.filePath);
58373
+ const dir = import_node_path43.default.dirname(this.filePath);
57856
58374
  await import_node_fs38.default.promises.mkdir(dir, { recursive: true });
57857
58375
  const data = JSON.stringify(v2, null, 2);
57858
58376
  const tmp = `${this.filePath}.tmp.${process.pid}.${Date.now()}`;
@@ -57964,10 +58482,10 @@ function escape(v2) {
57964
58482
  // src/tunnel/frpc-binary.ts
57965
58483
  var import_node_fs39 = __toESM(require("fs"), 1);
57966
58484
  var import_node_os14 = __toESM(require("os"), 1);
57967
- var import_node_path43 = __toESM(require("path"), 1);
58485
+ var import_node_path44 = __toESM(require("path"), 1);
57968
58486
  var import_node_child_process9 = require("child_process");
57969
58487
  var import_node_stream3 = require("stream");
57970
- var import_promises6 = require("stream/promises");
58488
+ var import_promises7 = require("stream/promises");
57971
58489
  var FRPC_VERSION = "0.68.0";
57972
58490
  var UnsupportedPlatformError = class extends Error {
57973
58491
  constructor(platform, arch) {
@@ -58003,13 +58521,13 @@ async function ensureFrpcBinary(opts) {
58003
58521
  }
58004
58522
  const version2 = opts.version ?? FRPC_VERSION;
58005
58523
  const platform = opts.platform ?? detectPlatform();
58006
- const binDir = import_node_path43.default.join(opts.dataDir, "bin");
58524
+ const binDir = import_node_path44.default.join(opts.dataDir, "bin");
58007
58525
  import_node_fs39.default.mkdirSync(binDir, { recursive: true });
58008
58526
  cleanupStaleArtifacts(binDir);
58009
- const stableBin = import_node_path43.default.join(binDir, "frpc");
58527
+ const stableBin = import_node_path44.default.join(binDir, "frpc");
58010
58528
  if (import_node_fs39.default.existsSync(stableBin)) return stableBin;
58011
58529
  const partialBin = `${stableBin}.partial`;
58012
- const tarballPath = import_node_path43.default.join(binDir, `frp_${version2}_${platform.os}_${platform.arch}.tar.gz.partial`);
58530
+ const tarballPath = import_node_path44.default.join(binDir, `frp_${version2}_${platform.os}_${platform.arch}.tar.gz.partial`);
58013
58531
  try {
58014
58532
  const url = frpcDownloadUrl(version2, platform);
58015
58533
  await downloadToFile(url, tarballPath, opts.fetchImpl);
@@ -58035,7 +58553,7 @@ function cleanupStaleArtifacts(binDir) {
58035
58553
  }
58036
58554
  for (const name of entries) {
58037
58555
  if (name.endsWith(".partial") || name.startsWith("extract-")) {
58038
- const full = import_node_path43.default.join(binDir, name);
58556
+ const full = import_node_path44.default.join(binDir, name);
58039
58557
  try {
58040
58558
  import_node_fs39.default.rmSync(full, { recursive: true, force: true });
58041
58559
  } catch {
@@ -58058,10 +58576,10 @@ async function downloadToFile(url, dest, fetchImpl) {
58058
58576
  }
58059
58577
  const out = import_node_fs39.default.createWriteStream(dest);
58060
58578
  const nodeStream = import_node_stream3.Readable.fromWeb(res.body);
58061
- await (0, import_promises6.pipeline)(nodeStream, out);
58579
+ await (0, import_promises7.pipeline)(nodeStream, out);
58062
58580
  }
58063
58581
  async function extractFrpcFromTarball(tarball, binDir, version2, platform, destBin) {
58064
- const work = import_node_path43.default.join(binDir, `extract-${process.pid}-${Date.now()}`);
58582
+ const work = import_node_path44.default.join(binDir, `extract-${process.pid}-${Date.now()}`);
58065
58583
  import_node_fs39.default.mkdirSync(work, { recursive: true });
58066
58584
  try {
58067
58585
  await new Promise((resolve9, reject) => {
@@ -58070,7 +58588,7 @@ async function extractFrpcFromTarball(tarball, binDir, version2, platform, destB
58070
58588
  proc.on("exit", (code) => code === 0 ? resolve9() : reject(new Error(`tar exited ${code}`)));
58071
58589
  });
58072
58590
  const dirName = `frp_${version2}_${platform.os}_${platform.arch}`;
58073
- const src = import_node_path43.default.join(work, dirName, "frpc");
58591
+ const src = import_node_path44.default.join(work, dirName, "frpc");
58074
58592
  if (!import_node_fs39.default.existsSync(src)) {
58075
58593
  throw new Error(`frpc not found inside tarball at ${src}`);
58076
58594
  }
@@ -58082,10 +58600,10 @@ async function extractFrpcFromTarball(tarball, binDir, version2, platform, destB
58082
58600
 
58083
58601
  // src/tunnel/frpc-process.ts
58084
58602
  var import_node_fs40 = __toESM(require("fs"), 1);
58085
- var import_node_path44 = __toESM(require("path"), 1);
58603
+ var import_node_path45 = __toESM(require("path"), 1);
58086
58604
  var import_node_child_process10 = require("child_process");
58087
58605
  function frpcPidFilePath(dataDir) {
58088
- return import_node_path44.default.join(dataDir, "frpc.pid");
58606
+ return import_node_path45.default.join(dataDir, "frpc.pid");
58089
58607
  }
58090
58608
  function writeFrpcPid(dataDir, pid) {
58091
58609
  try {
@@ -58127,7 +58645,7 @@ function defaultSleep2(ms) {
58127
58645
  }
58128
58646
  async function killStaleFrpc(deps) {
58129
58647
  const pidFile = frpcPidFilePath(deps.dataDir);
58130
- const tomlPath = import_node_path44.default.join(deps.dataDir, "frpc.toml");
58648
+ const tomlPath = import_node_path45.default.join(deps.dataDir, "frpc.toml");
58131
58649
  const readPidFile = deps.readPidFileImpl ?? defaultReadPidFile;
58132
58650
  const isAlive = deps.isPidAliveImpl ?? defaultIsPidAlive;
58133
58651
  const killPid = deps.killPidImpl ?? defaultKillPid;
@@ -58199,7 +58717,7 @@ var DEFAULT_TUNNEL_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
58199
58717
  var TunnelManager = class {
58200
58718
  constructor(deps) {
58201
58719
  this.deps = deps;
58202
- this.store = deps.store ?? new TunnelStore(import_node_path45.default.join(deps.dataDir, "tunnel.json"));
58720
+ this.store = deps.store ?? new TunnelStore(import_node_path46.default.join(deps.dataDir, "tunnel.json"));
58203
58721
  this.ttlMs = deps.ttlMs ?? DEFAULT_TUNNEL_TTL_MS;
58204
58722
  this.startupTimeoutMs = deps.startupTimeoutMs ?? 15e3;
58205
58723
  }
@@ -58326,7 +58844,7 @@ var TunnelManager = class {
58326
58844
  dataDir: this.deps.dataDir,
58327
58845
  override: this.deps.frpcBinaryOverride ?? void 0
58328
58846
  });
58329
- const tomlPath = import_node_path45.default.join(this.deps.dataDir, "frpc.toml");
58847
+ const tomlPath = import_node_path46.default.join(this.deps.dataDir, "frpc.toml");
58330
58848
  const proxyName = `clawd-${t.subdomain}-${localPort}-${import_node_crypto11.default.randomBytes(3).toString("hex")}`;
58331
58849
  const toml = buildFrpcToml({
58332
58850
  serverAddr: t.frpsHost,
@@ -58341,9 +58859,9 @@ var TunnelManager = class {
58341
58859
  const proc = (this.deps.spawnImpl ?? import_node_child_process11.spawn)(frpcBin, ["-c", tomlPath], {
58342
58860
  stdio: ["ignore", "pipe", "pipe"]
58343
58861
  });
58344
- const logDir = import_node_path45.default.join(this.deps.dataDir, "log");
58862
+ const logDir = import_node_path46.default.join(this.deps.dataDir, "log");
58345
58863
  import_node_fs41.default.mkdirSync(logDir, { recursive: true });
58346
- const logFilePath = import_node_path45.default.join(logDir, "frpc.log");
58864
+ const logFilePath = import_node_path46.default.join(logDir, "frpc.log");
58347
58865
  const logStream = import_node_fs41.default.createWriteStream(logFilePath, { flags: "a", mode: 384 });
58348
58866
  logStream.on("error", () => {
58349
58867
  });
@@ -58427,15 +58945,15 @@ async function waitForFrpcReady(proc, timeoutMs) {
58427
58945
 
58428
58946
  // src/tunnel/device-key.ts
58429
58947
  var import_node_os15 = __toESM(require("os"), 1);
58430
- var import_node_path46 = __toESM(require("path"), 1);
58948
+ var import_node_path47 = __toESM(require("path"), 1);
58431
58949
  var import_node_crypto12 = __toESM(require("crypto"), 1);
58432
58950
  var DERIVE_SALT = "clawd-tunnel-device-v1";
58433
58951
  function deriveStableDeviceKey(opts = {}) {
58434
58952
  const hostname = opts.hostname ?? import_node_os15.default.hostname();
58435
58953
  const uid = opts.uid ?? (typeof import_node_os15.default.userInfo === "function" ? import_node_os15.default.userInfo().uid : 0);
58436
58954
  const home = opts.home ?? import_node_os15.default.homedir();
58437
- const defaultDataDir = import_node_path46.default.resolve(import_node_path46.default.join(home, ".clawd"));
58438
- const normalizedDataDir = opts.dataDir ? import_node_path46.default.resolve(opts.dataDir) : null;
58955
+ const defaultDataDir = import_node_path47.default.resolve(import_node_path47.default.join(home, ".clawd"));
58956
+ const normalizedDataDir = opts.dataDir ? import_node_path47.default.resolve(opts.dataDir) : null;
58439
58957
  const isDefaultDir = normalizedDataDir == null || normalizedDataDir === defaultDataDir;
58440
58958
  const input = isDefaultDir ? `${hostname}::${uid}` : `${hostname}::${uid}::${normalizedDataDir}`;
58441
58959
  return import_node_crypto12.default.createHmac("sha256", DERIVE_SALT).update(input).digest("hex").slice(0, 32);
@@ -58443,11 +58961,11 @@ function deriveStableDeviceKey(opts = {}) {
58443
58961
 
58444
58962
  // src/auth-store.ts
58445
58963
  var import_node_fs42 = __toESM(require("fs"), 1);
58446
- var import_node_path47 = __toESM(require("path"), 1);
58964
+ var import_node_path48 = __toESM(require("path"), 1);
58447
58965
  var import_node_crypto13 = __toESM(require("crypto"), 1);
58448
58966
  var AUTH_FILE_NAME = "auth.json";
58449
58967
  function authFilePath(dataDir) {
58450
- return import_node_path47.default.join(dataDir, AUTH_FILE_NAME);
58968
+ return import_node_path48.default.join(dataDir, AUTH_FILE_NAME);
58451
58969
  }
58452
58970
  function loadOrCreateAuthFile(opts) {
58453
58971
  const file = authFilePath(opts.dataDir);
@@ -58499,7 +59017,7 @@ function readAuthFile(file) {
58499
59017
  }
58500
59018
  }
58501
59019
  function writeAuthFile(file, content) {
58502
- import_node_fs42.default.mkdirSync(import_node_path47.default.dirname(file), { recursive: true });
59020
+ import_node_fs42.default.mkdirSync(import_node_path48.default.dirname(file), { recursive: true });
58503
59021
  import_node_fs42.default.writeFileSync(file, JSON.stringify(content, null, 2), { mode: 384 });
58504
59022
  try {
58505
59023
  import_node_fs42.default.chmodSync(file, 384);
@@ -58510,10 +59028,10 @@ function writeAuthFile(file, content) {
58510
59028
  // src/owner-profile.ts
58511
59029
  var import_node_fs43 = __toESM(require("fs"), 1);
58512
59030
  var import_node_os16 = __toESM(require("os"), 1);
58513
- var import_node_path48 = __toESM(require("path"), 1);
59031
+ var import_node_path49 = __toESM(require("path"), 1);
58514
59032
  var PROFILE_FILENAME = "profile.json";
58515
59033
  function profilePath(dataDir) {
58516
- return import_node_path48.default.join(dataDir, PROFILE_FILENAME);
59034
+ return import_node_path49.default.join(dataDir, PROFILE_FILENAME);
58517
59035
  }
58518
59036
  function loadOwnerProfile(dataDir) {
58519
59037
  let raw;
@@ -58551,12 +59069,12 @@ function loadOwnerDisplayName(dataDir) {
58551
59069
 
58552
59070
  // src/feishu-auth/owner-identity-store.ts
58553
59071
  var import_node_fs44 = __toESM(require("fs"), 1);
58554
- var import_node_path49 = __toESM(require("path"), 1);
59072
+ var import_node_path50 = __toESM(require("path"), 1);
58555
59073
  var OWNER_IDENTITY_FILE_NAME = "owner-identity.json";
58556
59074
  var OwnerIdentityStore = class {
58557
59075
  file;
58558
59076
  constructor(dataDir) {
58559
- this.file = import_node_path49.default.join(dataDir, OWNER_IDENTITY_FILE_NAME);
59077
+ this.file = import_node_path50.default.join(dataDir, OWNER_IDENTITY_FILE_NAME);
58560
59078
  }
58561
59079
  read() {
58562
59080
  let raw;
@@ -58589,7 +59107,7 @@ var OwnerIdentityStore = class {
58589
59107
  };
58590
59108
  }
58591
59109
  write(record3) {
58592
- import_node_fs44.default.mkdirSync(import_node_path49.default.dirname(this.file), { recursive: true });
59110
+ import_node_fs44.default.mkdirSync(import_node_path50.default.dirname(this.file), { recursive: true });
58593
59111
  import_node_fs44.default.writeFileSync(this.file, JSON.stringify(record3, null, 2), { mode: 384 });
58594
59112
  try {
58595
59113
  import_node_fs44.default.chmodSync(this.file, 384);
@@ -58735,9 +59253,9 @@ var CentralClientError = class extends Error {
58735
59253
  code;
58736
59254
  cause;
58737
59255
  };
58738
- async function centralRequest(opts, path87, init) {
59256
+ async function centralRequest(opts, path89, init) {
58739
59257
  const f = opts.fetchImpl ?? globalThis.fetch;
58740
- const url = `${opts.api.replace(/\/+$/, "")}${path87}`;
59258
+ const url = `${opts.api.replace(/\/+$/, "")}${path89}`;
58741
59259
  const ctrl = new AbortController();
58742
59260
  const timer = setTimeout(() => ctrl.abort(), opts.timeoutMs ?? 15e3);
58743
59261
  let res;
@@ -58931,8 +59449,8 @@ function verifyConnectToken(args) {
58931
59449
  }
58932
59450
 
58933
59451
  // src/feishu-auth/server-key.ts
58934
- var fs58 = __toESM(require("fs"), 1);
58935
- var path62 = __toESM(require("path"), 1);
59452
+ var fs59 = __toESM(require("fs"), 1);
59453
+ var path63 = __toESM(require("path"), 1);
58936
59454
  var FILE_NAME2 = "server-signing-key.json";
58937
59455
  var ServerKeyStore = class {
58938
59456
  constructor(dataDir) {
@@ -58940,12 +59458,12 @@ var ServerKeyStore = class {
58940
59458
  }
58941
59459
  dataDir;
58942
59460
  filePath() {
58943
- return path62.join(this.dataDir, FILE_NAME2);
59461
+ return path63.join(this.dataDir, FILE_NAME2);
58944
59462
  }
58945
59463
  /** 读缓存的公钥;无缓存 / 损坏 → null(调用方决定是否触发拉取) */
58946
59464
  read() {
58947
59465
  try {
58948
- const raw = fs58.readFileSync(this.filePath(), "utf8");
59466
+ const raw = fs59.readFileSync(this.filePath(), "utf8");
58949
59467
  const parsed = JSON.parse(raw);
58950
59468
  if (typeof parsed.publicKeyPem === "string" && parsed.publicKeyPem.includes("PUBLIC KEY")) {
58951
59469
  return parsed.publicKeyPem;
@@ -58960,12 +59478,12 @@ var ServerKeyStore = class {
58960
59478
  publicKeyPem,
58961
59479
  fetchedAt: (/* @__PURE__ */ new Date()).toISOString()
58962
59480
  };
58963
- fs58.mkdirSync(this.dataDir, { recursive: true });
58964
- fs58.writeFileSync(this.filePath(), JSON.stringify(content, null, 2), { mode: 384 });
59481
+ fs59.mkdirSync(this.dataDir, { recursive: true });
59482
+ fs59.writeFileSync(this.filePath(), JSON.stringify(content, null, 2), { mode: 384 });
58965
59483
  }
58966
59484
  clear() {
58967
59485
  try {
58968
- fs58.unlinkSync(this.filePath());
59486
+ fs59.unlinkSync(this.filePath());
58969
59487
  } catch {
58970
59488
  }
58971
59489
  }
@@ -58974,13 +59492,16 @@ var ServerKeyStore = class {
58974
59492
  // src/index.ts
58975
59493
  init_protocol();
58976
59494
 
59495
+ // src/handlers/index.ts
59496
+ var import_node_path66 = __toESM(require("path"), 1);
59497
+
58977
59498
  // src/handlers/session.ts
58978
59499
  init_protocol();
58979
59500
 
58980
59501
  // src/session/fork.ts
58981
59502
  var import_node_fs45 = __toESM(require("fs"), 1);
58982
59503
  var import_node_os17 = __toESM(require("os"), 1);
58983
- var import_node_path50 = __toESM(require("path"), 1);
59504
+ var import_node_path51 = __toESM(require("path"), 1);
58984
59505
  init_claude_history();
58985
59506
  function readJsonlEntries(file) {
58986
59507
  const raw = import_node_fs45.default.readFileSync(file, "utf8");
@@ -58996,19 +59517,19 @@ function readJsonlEntries(file) {
58996
59517
  return out;
58997
59518
  }
58998
59519
  function forkSession(input) {
58999
- const baseDir = input.baseDir ?? import_node_path50.default.join(import_node_os17.default.homedir(), ".claude");
59000
- let projectDir = import_node_path50.default.join(baseDir, "projects", cwdToHashDir(input.cwd));
59001
- let sourceFile = import_node_path50.default.join(projectDir, `${input.toolSessionId}.jsonl`);
59520
+ const baseDir = input.baseDir ?? import_node_path51.default.join(import_node_os17.default.homedir(), ".claude");
59521
+ let projectDir = import_node_path51.default.join(baseDir, "projects", cwdToHashDir(input.cwd));
59522
+ let sourceFile = import_node_path51.default.join(projectDir, `${input.toolSessionId}.jsonl`);
59002
59523
  if (!import_node_fs45.default.existsSync(sourceFile)) {
59003
59524
  const found = findTranscriptByToolSessionId(
59004
- import_node_path50.default.join(baseDir, "projects"),
59525
+ import_node_path51.default.join(baseDir, "projects"),
59005
59526
  input.toolSessionId
59006
59527
  );
59007
59528
  if (!found) {
59008
59529
  throw new Error(`fork: source transcript not found: ${sourceFile}`);
59009
59530
  }
59010
59531
  sourceFile = found;
59011
- projectDir = import_node_path50.default.dirname(found);
59532
+ projectDir = import_node_path51.default.dirname(found);
59012
59533
  }
59013
59534
  const entries = readJsonlEntries(sourceFile);
59014
59535
  const mainEntries = entries.filter((e) => e.isSidechain !== true);
@@ -59037,8 +59558,8 @@ function forkSession(input) {
59037
59558
  }
59038
59559
  forkedLines.push(JSON.stringify(forked));
59039
59560
  }
59040
- const outDir = input.targetCwd ? import_node_path50.default.join(baseDir, "projects", cwdToHashDir(input.targetCwd)) : projectDir;
59041
- const forkedFilePath = import_node_path50.default.join(outDir, `${forkedToolSessionId}.jsonl`);
59561
+ const outDir = input.targetCwd ? import_node_path51.default.join(baseDir, "projects", cwdToHashDir(input.targetCwd)) : projectDir;
59562
+ const forkedFilePath = import_node_path51.default.join(outDir, `${forkedToolSessionId}.jsonl`);
59042
59563
  import_node_fs45.default.mkdirSync(outDir, { recursive: true });
59043
59564
  import_node_fs45.default.writeFileSync(forkedFilePath, forkedLines.join("\n") + "\n", { mode: 384 });
59044
59565
  return { forkedToolSessionId, forkedFilePath };
@@ -59046,18 +59567,18 @@ function forkSession(input) {
59046
59567
 
59047
59568
  // src/session/external-scan.ts
59048
59569
  var import_node_fs46 = __toESM(require("fs"), 1);
59049
- var import_promises7 = __toESM(require("fs/promises"), 1);
59570
+ var import_promises8 = __toESM(require("fs/promises"), 1);
59050
59571
  var import_node_os18 = __toESM(require("os"), 1);
59051
- var import_node_path51 = __toESM(require("path"), 1);
59572
+ var import_node_path52 = __toESM(require("path"), 1);
59052
59573
  var import_node_module = require("module");
59053
59574
  var import_node_child_process12 = require("child_process");
59054
59575
  var import_meta4 = {};
59055
59576
  function defaultRoots() {
59056
59577
  const home = import_node_os18.default.homedir();
59057
59578
  return {
59058
- claudeProjectsDir: import_node_path51.default.join(home, ".claude", "projects"),
59059
- codexDir: import_node_path51.default.join(home, ".codex"),
59060
- clawdDir: import_node_path51.default.join(home, ".clawd")
59579
+ claudeProjectsDir: import_node_path52.default.join(home, ".claude", "projects"),
59580
+ codexDir: import_node_path52.default.join(home, ".codex"),
59581
+ clawdDir: import_node_path52.default.join(home, ".clawd")
59061
59582
  };
59062
59583
  }
59063
59584
  function width(s) {
@@ -59085,7 +59606,7 @@ function toMs(v2) {
59085
59606
  async function* jsonLines(file) {
59086
59607
  let raw;
59087
59608
  try {
59088
- raw = await import_promises7.default.readFile(file, "utf8");
59609
+ raw = await import_promises8.default.readFile(file, "utf8");
59089
59610
  } catch {
59090
59611
  return;
59091
59612
  }
@@ -59125,13 +59646,13 @@ async function scanClaude(roots) {
59125
59646
  if (!d.isDirectory()) continue;
59126
59647
  let files;
59127
59648
  try {
59128
- files = import_node_fs46.default.readdirSync(import_node_path51.default.join(roots.claudeProjectsDir, d.name));
59649
+ files = import_node_fs46.default.readdirSync(import_node_path52.default.join(roots.claudeProjectsDir, d.name));
59129
59650
  } catch {
59130
59651
  continue;
59131
59652
  }
59132
59653
  for (const name of files) {
59133
59654
  if (!name.endsWith(".jsonl")) continue;
59134
- const file = import_node_path51.default.join(roots.claudeProjectsDir, d.name, name);
59655
+ const file = import_node_path52.default.join(roots.claudeProjectsDir, d.name, name);
59135
59656
  let st;
59136
59657
  try {
59137
59658
  st = import_node_fs46.default.statSync(file);
@@ -59261,7 +59782,7 @@ async function codexFromRows(rows) {
59261
59782
  async function scanCodex(roots) {
59262
59783
  let dbs = [];
59263
59784
  try {
59264
- dbs = import_node_fs46.default.readdirSync(roots.codexDir).filter((n) => /^state_.*\.sqlite$/.test(n)).sort().map((n) => import_node_path51.default.join(roots.codexDir, n));
59785
+ dbs = import_node_fs46.default.readdirSync(roots.codexDir).filter((n) => /^state_.*\.sqlite$/.test(n)).sort().map((n) => import_node_path52.default.join(roots.codexDir, n));
59265
59786
  } catch {
59266
59787
  }
59267
59788
  if (!dbs.length) return [];
@@ -59270,14 +59791,14 @@ async function scanCodex(roots) {
59270
59791
  }
59271
59792
  async function scanExternalSessions(rootsIn) {
59272
59793
  const roots = { ...defaultRoots(), ...rootsIn };
59273
- const personaRoot = import_node_path51.default.join(roots.clawdDir, "personas");
59794
+ const personaRoot = import_node_path52.default.join(roots.clawdDir, "personas");
59274
59795
  const all = [...await scanClaude(roots), ...await scanCodex(roots)];
59275
59796
  const out = [];
59276
59797
  for (const c of all) {
59277
59798
  if (c.skip) continue;
59278
59799
  if (c.turns < 1) continue;
59279
59800
  if (!c.cwd || !isDir2(c.cwd)) continue;
59280
- if (c.cwd === roots.clawdDir || c.cwd === personaRoot || c.cwd.startsWith(personaRoot + import_node_path51.default.sep) || c.cwd.startsWith(roots.clawdDir + import_node_path51.default.sep)) {
59801
+ if (c.cwd === roots.clawdDir || c.cwd === personaRoot || c.cwd.startsWith(personaRoot + import_node_path52.default.sep) || c.cwd.startsWith(roots.clawdDir + import_node_path52.default.sep)) {
59281
59802
  continue;
59282
59803
  }
59283
59804
  out.push({
@@ -59295,12 +59816,12 @@ async function scanExternalSessions(rootsIn) {
59295
59816
 
59296
59817
  // src/session/import-ledger.ts
59297
59818
  var import_node_fs47 = __toESM(require("fs"), 1);
59298
- var import_node_path52 = __toESM(require("path"), 1);
59819
+ var import_node_path53 = __toESM(require("path"), 1);
59299
59820
  var LEDGER_FILENAME = "session-import-ledger.json";
59300
59821
  function readImportLedger(dataDir) {
59301
59822
  try {
59302
59823
  const parsed = JSON.parse(
59303
- import_node_fs47.default.readFileSync(import_node_path52.default.join(dataDir, LEDGER_FILENAME), "utf8")
59824
+ import_node_fs47.default.readFileSync(import_node_path53.default.join(dataDir, LEDGER_FILENAME), "utf8")
59304
59825
  );
59305
59826
  return Array.isArray(parsed) ? parsed : [];
59306
59827
  } catch {
@@ -59308,7 +59829,7 @@ function readImportLedger(dataDir) {
59308
59829
  }
59309
59830
  }
59310
59831
  function appendImportLedger(dataDir, entry) {
59311
- const file = import_node_path52.default.join(dataDir, LEDGER_FILENAME);
59832
+ const file = import_node_path53.default.join(dataDir, LEDGER_FILENAME);
59312
59833
  const entries = readImportLedger(dataDir);
59313
59834
  entries.push(entry);
59314
59835
  import_node_fs47.default.writeFileSync(file, JSON.stringify(entries, null, 2), { encoding: "utf8", mode: 384 });
@@ -59814,7 +60335,7 @@ function buildPermissionHandlers(deps) {
59814
60335
  }
59815
60336
 
59816
60337
  // src/handlers/history.ts
59817
- var path67 = __toESM(require("path"), 1);
60338
+ var path68 = __toESM(require("path"), 1);
59818
60339
  init_protocol();
59819
60340
 
59820
60341
  // src/session/recent-dirs.ts
@@ -59832,7 +60353,7 @@ function listRecentDirs(store, limit = 50) {
59832
60353
  }
59833
60354
 
59834
60355
  // src/permission/persona-paths.ts
59835
- var path66 = __toESM(require("path"), 1);
60356
+ var path67 = __toESM(require("path"), 1);
59836
60357
  function getAllowedPersonaIds(grants, action) {
59837
60358
  const ids = /* @__PURE__ */ new Set();
59838
60359
  for (const g2 of grants) {
@@ -59845,42 +60366,42 @@ function getAllowedPersonaIds(grants, action) {
59845
60366
  return ids;
59846
60367
  }
59847
60368
  function isGuestPathAllowed(grants, absPath, personaRoot, action = "read", userWorkDir) {
59848
- const target = path66.resolve(absPath);
60369
+ const target = path67.resolve(absPath);
59849
60370
  if (userWorkDir) {
59850
- const u = path66.resolve(userWorkDir);
59851
- const usep = u.endsWith(path66.sep) ? "" : path66.sep;
60371
+ const u = path67.resolve(userWorkDir);
60372
+ const usep = u.endsWith(path67.sep) ? "" : path67.sep;
59852
60373
  if (target === u || target.startsWith(u + usep)) return true;
59853
60374
  }
59854
- const root = path66.resolve(personaRoot);
59855
- const sep4 = root.endsWith(path66.sep) ? "" : path66.sep;
60375
+ const root = path67.resolve(personaRoot);
60376
+ const sep4 = root.endsWith(path67.sep) ? "" : path67.sep;
59856
60377
  if (!target.startsWith(root + sep4)) return false;
59857
- const rel = path66.relative(root, target);
60378
+ const rel = path67.relative(root, target);
59858
60379
  if (!rel || rel.startsWith("..")) return false;
59859
- const personaId2 = rel.split(path66.sep)[0];
60380
+ const personaId2 = rel.split(path67.sep)[0];
59860
60381
  if (!personaId2) return false;
59861
60382
  const allowed = getAllowedPersonaIds(grants, action);
59862
60383
  if (allowed === "*") return true;
59863
60384
  return allowed.has(personaId2);
59864
60385
  }
59865
60386
  function personaIdFromPath(absPath, personaRoot) {
59866
- const root = path66.resolve(personaRoot);
59867
- const target = path66.resolve(absPath);
59868
- const sep4 = root.endsWith(path66.sep) ? "" : path66.sep;
60387
+ const root = path67.resolve(personaRoot);
60388
+ const target = path67.resolve(absPath);
60389
+ const sep4 = root.endsWith(path67.sep) ? "" : path67.sep;
59869
60390
  if (!target.startsWith(root + sep4)) return null;
59870
- const rel = path66.relative(root, target);
60391
+ const rel = path67.relative(root, target);
59871
60392
  if (!rel || rel.startsWith("..")) return null;
59872
- const id = rel.split(path66.sep)[0];
60393
+ const id = rel.split(path67.sep)[0];
59873
60394
  return id || null;
59874
60395
  }
59875
60396
  function isPathWithin(dir, absPath) {
59876
- const d = path66.resolve(dir);
59877
- const t = path66.resolve(absPath);
59878
- const sep4 = d.endsWith(path66.sep) ? "" : path66.sep;
60397
+ const d = path67.resolve(dir);
60398
+ const t = path67.resolve(absPath);
60399
+ const sep4 = d.endsWith(path67.sep) ? "" : path67.sep;
59879
60400
  return t === d || t.startsWith(d + sep4);
59880
60401
  }
59881
60402
  function isPathInGuestBoundary(personaRoot, personaId2, userWorkDir, absPath) {
59882
60403
  if (userWorkDir && isPathWithin(userWorkDir, absPath)) return true;
59883
- return personaIdFromPath(path66.resolve(absPath), personaRoot) === personaId2;
60404
+ return personaIdFromPath(path67.resolve(absPath), personaRoot) === personaId2;
59884
60405
  }
59885
60406
 
59886
60407
  // src/handlers/history.ts
@@ -59933,7 +60454,7 @@ function buildHistoryHandlers(deps) {
59933
60454
  if (!pid) return false;
59934
60455
  return isGuestPathAllowed(
59935
60456
  ctx.grants,
59936
- path67.join(personaRoot, pid),
60457
+ path68.join(personaRoot, pid),
59937
60458
  personaRoot,
59938
60459
  "read",
59939
60460
  userWorkDir
@@ -59945,7 +60466,7 @@ function buildHistoryHandlers(deps) {
59945
60466
  };
59946
60467
  const list = async (frame, _client, ctx) => {
59947
60468
  const args = HistoryListArgs.parse(frame);
59948
- assertGuestPath(ctx, path67.resolve(args.projectPath), personaRoot, "history:list", usersRoot);
60469
+ assertGuestPath(ctx, path68.resolve(args.projectPath), personaRoot, "history:list", usersRoot);
59949
60470
  const sessions = await history.listSessions(args);
59950
60471
  return { response: { type: "history:list", sessions } };
59951
60472
  };
@@ -59993,14 +60514,14 @@ function buildHistoryHandlers(deps) {
59993
60514
  };
59994
60515
  const subagents = async (frame, _client, ctx) => {
59995
60516
  const args = HistorySubagentsArgs.parse(frame);
59996
- assertGuestPath(ctx, path67.resolve(args.cwd), personaRoot, "history:subagents", usersRoot);
60517
+ assertGuestPath(ctx, path68.resolve(args.cwd), personaRoot, "history:subagents", usersRoot);
59997
60518
  assertGuestOwnsToolSession(ctx, args.toolSessionId, "history:subagents");
59998
60519
  const subs = await historyReaderForToolSession(args.toolSessionId).listSubagents(args);
59999
60520
  return { response: { type: "history:subagents", subagents: subs } };
60000
60521
  };
60001
60522
  const subagentRead = async (frame, _client, ctx) => {
60002
60523
  const args = HistorySubagentReadArgs.parse(frame);
60003
- assertGuestPath(ctx, path67.resolve(args.cwd), personaRoot, "history:subagent-read", usersRoot);
60524
+ assertGuestPath(ctx, path68.resolve(args.cwd), personaRoot, "history:subagent-read", usersRoot);
60004
60525
  assertGuestOwnsToolSession(ctx, args.toolSessionId, "history:subagent-read");
60005
60526
  const res = await historyReaderForToolSession(args.toolSessionId).readSubagent(args);
60006
60527
  return { response: { type: "history:subagent-read", ...res } };
@@ -60010,7 +60531,7 @@ function buildHistoryHandlers(deps) {
60010
60531
  if (ctx?.principal.kind === "guest" && personaRoot) {
60011
60532
  const userWorkDir = usersRoot ? deriveUserWorkDir(ctx.principal.id, usersRoot) : void 0;
60012
60533
  const filtered = dirs.filter(
60013
- (d) => isGuestPathAllowed(ctx.grants, path67.resolve(d.cwd), personaRoot, "read", userWorkDir)
60534
+ (d) => isGuestPathAllowed(ctx.grants, path68.resolve(d.cwd), personaRoot, "read", userWorkDir)
60014
60535
  );
60015
60536
  return { response: { type: "history:recentDirs", dirs: filtered } };
60016
60537
  }
@@ -60027,17 +60548,17 @@ function buildHistoryHandlers(deps) {
60027
60548
  }
60028
60549
 
60029
60550
  // src/handlers/projects.ts
60030
- var fs62 = __toESM(require("fs"), 1);
60551
+ var fs63 = __toESM(require("fs"), 1);
60031
60552
  var os20 = __toESM(require("os"), 1);
60032
- var path68 = __toESM(require("path"), 1);
60553
+ var path69 = __toESM(require("path"), 1);
60033
60554
  var import_node_child_process13 = require("child_process");
60034
60555
  init_protocol();
60035
- var DEFAULT_PROJECTS_ROOT = path68.join(os20.homedir(), "Documents", "ChatGPT");
60556
+ var DEFAULT_PROJECTS_ROOT = path69.join(os20.homedir(), "Documents", "ChatGPT");
60036
60557
  function allocateDir(root, name) {
60037
- let candidate = path68.join(root, name);
60558
+ let candidate = path69.join(root, name);
60038
60559
  let n = 2;
60039
- while (fs62.existsSync(candidate)) {
60040
- candidate = path68.join(root, `${name} ${n}`);
60560
+ while (fs63.existsSync(candidate)) {
60561
+ candidate = path69.join(root, `${name} ${n}`);
60041
60562
  n += 1;
60042
60563
  }
60043
60564
  return candidate;
@@ -60065,7 +60586,7 @@ function buildProjectHandlers(deps) {
60065
60586
  throw new ClawdError(ERROR_CODES.INVALID_CWD, "project:reveal \u4EC5\u652F\u6301 macOS");
60066
60587
  }
60067
60588
  try {
60068
- if (!fs62.statSync(args.path).isDirectory()) throw new Error("not dir");
60589
+ if (!fs63.statSync(args.path).isDirectory()) throw new Error("not dir");
60069
60590
  } catch {
60070
60591
  throw new ClawdError(ERROR_CODES.INVALID_CWD, `not a directory: ${args.path}`);
60071
60592
  }
@@ -60081,7 +60602,7 @@ function buildProjectHandlers(deps) {
60081
60602
  let target;
60082
60603
  if (args.path) {
60083
60604
  try {
60084
- if (!fs62.statSync(args.path).isDirectory()) throw new Error("not dir");
60605
+ if (!fs63.statSync(args.path).isDirectory()) throw new Error("not dir");
60085
60606
  } catch {
60086
60607
  throw new ClawdError(ERROR_CODES.INVALID_CWD, `not a directory: ${args.path}`);
60087
60608
  }
@@ -60089,15 +60610,15 @@ function buildProjectHandlers(deps) {
60089
60610
  } else {
60090
60611
  const name = args.name;
60091
60612
  try {
60092
- fs62.mkdirSync(root, { recursive: true });
60613
+ fs63.mkdirSync(root, { recursive: true });
60093
60614
  target = allocateDir(root, name);
60094
- fs62.mkdirSync(target);
60615
+ fs63.mkdirSync(target);
60095
60616
  } catch (err) {
60096
60617
  throw new ClawdError(ERROR_CODES.INVALID_CWD, `\u521B\u5EFA\u9879\u76EE\u76EE\u5F55\u5931\u8D25: ${String(err)}`);
60097
60618
  }
60098
60619
  const git = (0, import_node_child_process13.spawnSync)("git", ["init"], { cwd: target, stdio: "ignore" });
60099
60620
  if (git.status !== 0) {
60100
- fs62.rmSync(target, { recursive: true, force: true });
60621
+ fs63.rmSync(target, { recursive: true, force: true });
60101
60622
  throw new ClawdError(
60102
60623
  ERROR_CODES.INVALID_CWD,
60103
60624
  `git init \u5931\u8D25\uFF08${git.error ? String(git.error) : `exit ${git.status}`}\uFF09: ${target}`
@@ -60122,7 +60643,7 @@ function buildProjectHandlers(deps) {
60122
60643
  }
60123
60644
 
60124
60645
  // src/handlers/workspace.ts
60125
- var path69 = __toESM(require("path"), 1);
60646
+ var path70 = __toESM(require("path"), 1);
60126
60647
  var os21 = __toESM(require("os"), 1);
60127
60648
  init_protocol();
60128
60649
  init_protocol();
@@ -60164,22 +60685,22 @@ function buildWorkspaceHandlers(deps) {
60164
60685
  const args = WorkspaceListArgs.parse(frame);
60165
60686
  const isGuest = ctx?.principal.kind === "guest";
60166
60687
  const fallbackCwd = isGuest && personaRoot ? personaRoot : os21.homedir();
60167
- const resolvedCwd = path69.resolve(args.cwd ?? fallbackCwd);
60168
- const target = args.path ? path69.resolve(resolvedCwd, args.path) : resolvedCwd;
60688
+ const resolvedCwd = path70.resolve(args.cwd ?? fallbackCwd);
60689
+ const target = args.path ? path70.resolve(resolvedCwd, args.path) : resolvedCwd;
60169
60690
  assertGuestPath2(ctx, target, personaRoot, "workspace:list", usersRoot);
60170
60691
  const res = workspace.list({ ...args, cwd: resolvedCwd });
60171
60692
  return { response: { type: "workspace:list", ...res } };
60172
60693
  };
60173
60694
  const read = async (frame, _client, ctx) => {
60174
60695
  const args = WorkspaceReadArgs.parse(frame);
60175
- const target = path69.isAbsolute(args.path) ? path69.resolve(args.path) : path69.resolve(args.cwd, args.path);
60696
+ const target = path70.isAbsolute(args.path) ? path70.resolve(args.path) : path70.resolve(args.cwd, args.path);
60176
60697
  assertGuestPath2(ctx, target, personaRoot, "workspace:read", usersRoot);
60177
60698
  const res = workspace.read(args);
60178
60699
  return { response: { type: "workspace:read", ...res } };
60179
60700
  };
60180
60701
  const skillsList = async (frame, _client, ctx) => {
60181
60702
  const args = SkillsListArgs.parse(frame);
60182
- const cwdAbs = path69.resolve(args.cwd);
60703
+ const cwdAbs = path70.resolve(args.cwd);
60183
60704
  assertGuestPath2(ctx, cwdAbs, personaRoot, "skills:list", usersRoot);
60184
60705
  const list2 = await getSkillsForTool(args.tool ?? "claude", cwdAbs);
60185
60706
  if (ctx?.principal.kind === "guest" && personaRoot) {
@@ -60191,7 +60712,7 @@ function buildWorkspaceHandlers(deps) {
60191
60712
  };
60192
60713
  const agentsList = async (frame, _client, ctx) => {
60193
60714
  const args = AgentsListArgs.parse(frame);
60194
- const cwdAbs = path69.resolve(args.cwd);
60715
+ const cwdAbs = path70.resolve(args.cwd);
60195
60716
  assertGuestPath2(ctx, cwdAbs, personaRoot, "agents:list", usersRoot);
60196
60717
  if (args.tool === "codex") {
60197
60718
  return { response: { type: "agents:list", agents: [] } };
@@ -60213,18 +60734,18 @@ function buildWorkspaceHandlers(deps) {
60213
60734
  }
60214
60735
 
60215
60736
  // src/handlers/git.ts
60216
- var path71 = __toESM(require("path"), 1);
60737
+ var path72 = __toESM(require("path"), 1);
60217
60738
  init_protocol();
60218
60739
  init_protocol();
60219
60740
 
60220
60741
  // src/workspace/git.ts
60221
60742
  var import_node_child_process14 = require("child_process");
60222
60743
  var import_node_fs48 = __toESM(require("fs"), 1);
60223
- var import_node_path53 = __toESM(require("path"), 1);
60744
+ var import_node_path54 = __toESM(require("path"), 1);
60224
60745
  var import_node_util = require("util");
60225
60746
  var pexec = (0, import_node_util.promisify)(import_node_child_process14.execFile);
60226
60747
  function normalizePath(p2) {
60227
- const resolved = import_node_path53.default.resolve(p2);
60748
+ const resolved = import_node_path54.default.resolve(p2);
60228
60749
  try {
60229
60750
  return import_node_fs48.default.realpathSync(resolved);
60230
60751
  } catch {
@@ -60300,7 +60821,7 @@ async function listGitBranches(cwd) {
60300
60821
  function assertGuestCwd(ctx, cwd, personaRoot, method, usersRoot) {
60301
60822
  if (!ctx || ctx.principal.kind !== "guest" || !personaRoot) return;
60302
60823
  const userWorkDir = usersRoot ? deriveUserWorkDir(ctx.principal.id, usersRoot) : void 0;
60303
- if (!isGuestPathAllowed(ctx.grants, path71.resolve(cwd), personaRoot, "read", userWorkDir)) {
60824
+ if (!isGuestPathAllowed(ctx.grants, path72.resolve(cwd), personaRoot, "read", userWorkDir)) {
60304
60825
  throw new ClawdError(
60305
60826
  ERROR_CODES.UNAUTHORIZED,
60306
60827
  `guest ${ctx.principal.id} cannot ${method} cwd ${cwd}`
@@ -61038,6 +61559,22 @@ var METHOD_GRANT_MAP = {
61038
61559
  // ---- shift:* (排班 mock,owner-only 读) ----
61039
61560
  // mock 阶段只 owner 用;接 guest 后再考虑分级
61040
61561
  "shift:list": ADMIN_ANY,
61562
+ // channel:* / topic:*(Channel × Master):owner 本机资源,owner-only 一刀切。
61563
+ // Master 是 owner-scope persona session,经 clawd-rpc 桥调,同样落在 ADMIN_ANY。
61564
+ "channel:list": ADMIN_ANY,
61565
+ "channel:create": ADMIN_ANY,
61566
+ "channel:update": ADMIN_ANY,
61567
+ "channel:delete": ADMIN_ANY,
61568
+ "topic:list": ADMIN_ANY,
61569
+ "topic:get": ADMIN_ANY,
61570
+ "topic:create": ADMIN_ANY,
61571
+ "topic:setPlan": ADMIN_ANY,
61572
+ "topic:append": ADMIN_ANY,
61573
+ "topic:reject": ADMIN_ANY,
61574
+ "topic:accept": ADMIN_ANY,
61575
+ "topic:close": ADMIN_ANY,
61576
+ "line:digest": ADMIN_ANY,
61577
+ "master:authorityCheck": ADMIN_ANY,
61041
61578
  // project:*(sidebar 项目分区,spec 2026-08-17):registry 是 owner 本机资源,owner-only 一刀切
61042
61579
  "project:list": ADMIN_ANY,
61043
61580
  "project:create": ADMIN_ANY,
@@ -61203,21 +61740,21 @@ function computeMethodAccess(args) {
61203
61740
  }
61204
61741
 
61205
61742
  // src/version.ts
61206
- var version = "0.2.382".length > 0 ? "0.2.382" : "dev";
61743
+ var version = "0.2.383".length > 0 ? "0.2.383" : "dev";
61207
61744
 
61208
61745
  // src/cli-probe/probe.ts
61209
- var fs64 = __toESM(require("fs"), 1);
61210
- var path72 = __toESM(require("path"), 1);
61746
+ var fs65 = __toESM(require("fs"), 1);
61747
+ var path73 = __toESM(require("path"), 1);
61211
61748
  var PKG_NAME = "@clawos-dev/clawd";
61212
61749
  var BIN_NAME = "clawd";
61213
61750
  var MAX_PARENT_HOPS = 5;
61214
61751
  function probeGlobalCli(pathEnv) {
61215
61752
  if (!pathEnv) return null;
61216
- for (const dir of pathEnv.split(path72.delimiter)) {
61753
+ for (const dir of pathEnv.split(path73.delimiter)) {
61217
61754
  if (!dir) continue;
61218
61755
  let real;
61219
61756
  try {
61220
- real = fs64.realpathSync(path72.join(dir, BIN_NAME));
61757
+ real = fs65.realpathSync(path73.join(dir, BIN_NAME));
61221
61758
  } catch {
61222
61759
  continue;
61223
61760
  }
@@ -61227,15 +61764,15 @@ function probeGlobalCli(pathEnv) {
61227
61764
  return null;
61228
61765
  }
61229
61766
  function readOwnPackageVersion(entryPath) {
61230
- let dir = path72.dirname(entryPath);
61767
+ let dir = path73.dirname(entryPath);
61231
61768
  for (let i = 0; i < MAX_PARENT_HOPS; i++) {
61232
61769
  try {
61233
- const raw = fs64.readFileSync(path72.join(dir, "package.json"), "utf8");
61770
+ const raw = fs65.readFileSync(path73.join(dir, "package.json"), "utf8");
61234
61771
  const pkg = JSON.parse(raw);
61235
61772
  if (pkg.name !== PKG_NAME) return null;
61236
61773
  return typeof pkg.version === "string" && pkg.version ? pkg.version : null;
61237
61774
  } catch {
61238
- const parent = path72.dirname(dir);
61775
+ const parent = path73.dirname(dir);
61239
61776
  if (parent === dir) return null;
61240
61777
  dir = parent;
61241
61778
  }
@@ -61398,7 +61935,7 @@ function buildPersonaHandlers(deps) {
61398
61935
  }
61399
61936
 
61400
61937
  // src/handlers/attachment.ts
61401
- var import_node_path54 = __toESM(require("path"), 1);
61938
+ var import_node_path55 = __toESM(require("path"), 1);
61402
61939
  init_protocol();
61403
61940
  init_protocol();
61404
61941
  var DEFAULT_TTL_SECONDS = 24 * 3600;
@@ -61478,12 +62015,12 @@ function buildAttachmentHandlers(deps) {
61478
62015
  `session ${args.sessionId} scope unresolved`
61479
62016
  );
61480
62017
  }
61481
- const cwdAbs = import_node_path54.default.resolve(sessionFile.cwd);
61482
- const candidateAbs = import_node_path54.default.isAbsolute(args.relPath) ? import_node_path54.default.resolve(args.relPath) : import_node_path54.default.resolve(cwdAbs, args.relPath);
62018
+ const cwdAbs = import_node_path55.default.resolve(sessionFile.cwd);
62019
+ const candidateAbs = import_node_path55.default.isAbsolute(args.relPath) ? import_node_path55.default.resolve(args.relPath) : import_node_path55.default.resolve(cwdAbs, args.relPath);
61483
62020
  guardAttachmentPath(ctx, args.sessionId, candidateAbs, "attachment.signUrl", "group-acl");
61484
62021
  const entries = deps.groupFileStore.list(scope, args.sessionId);
61485
62022
  const entry = entries.find((e) => {
61486
- const storedAbs = import_node_path54.default.isAbsolute(e.relPath) ? import_node_path54.default.resolve(e.relPath) : import_node_path54.default.resolve(cwdAbs, e.relPath);
62023
+ const storedAbs = import_node_path55.default.isAbsolute(e.relPath) ? import_node_path55.default.resolve(e.relPath) : import_node_path55.default.resolve(cwdAbs, e.relPath);
61487
62024
  return storedAbs === candidateAbs && !e.stale;
61488
62025
  });
61489
62026
  if (!entry) {
@@ -61508,7 +62045,7 @@ function buildAttachmentHandlers(deps) {
61508
62045
  if (!ctx || ctx.principal.kind !== "guest" || !deps.personaRoot || !deps.sessionStore) return;
61509
62046
  const f = deps.sessionStore.read(sessionId);
61510
62047
  if (!f) return;
61511
- assertGuestAttachmentPath(ctx, import_node_path54.default.resolve(f.cwd), deps.personaRoot, method, deps.usersRoot);
62048
+ assertGuestAttachmentPath(ctx, import_node_path55.default.resolve(f.cwd), deps.personaRoot, method, deps.usersRoot);
61512
62049
  }
61513
62050
  const groupAdd = async (frame, _client, ctx) => {
61514
62051
  if (!deps.groupFileStore || !deps.getSessionScope) {
@@ -61523,8 +62060,8 @@ function buildAttachmentHandlers(deps) {
61523
62060
  if (!scope) {
61524
62061
  throw new ClawdError(ERROR_CODES.VALIDATION_ERROR, `session ${args.sessionId} not found`);
61525
62062
  }
61526
- const cwdAbs = import_node_path54.default.resolve(deps.sessionStore?.read(args.sessionId)?.cwd ?? ".");
61527
- const candidateAbs = import_node_path54.default.isAbsolute(args.relPath) ? import_node_path54.default.resolve(args.relPath) : import_node_path54.default.resolve(cwdAbs, args.relPath);
62063
+ const cwdAbs = import_node_path55.default.resolve(deps.sessionStore?.read(args.sessionId)?.cwd ?? ".");
62064
+ const candidateAbs = import_node_path55.default.isAbsolute(args.relPath) ? import_node_path55.default.resolve(args.relPath) : import_node_path55.default.resolve(cwdAbs, args.relPath);
61528
62065
  guardAttachmentPath(ctx, args.sessionId, candidateAbs, "attachment.groupAdd", "cwd-subtree");
61529
62066
  const from = args.origin === "agent" ? "agent" : ctx?.principal.kind === "owner" ? "owner" : "agent";
61530
62067
  const size = 0;
@@ -61582,21 +62119,21 @@ function buildAttachmentHandlers(deps) {
61582
62119
  }
61583
62120
 
61584
62121
  // src/handlers/extension.ts
61585
- var import_promises11 = __toESM(require("fs/promises"), 1);
61586
- var import_node_path59 = __toESM(require("path"), 1);
62122
+ var import_promises12 = __toESM(require("fs/promises"), 1);
62123
+ var import_node_path60 = __toESM(require("path"), 1);
61587
62124
  init_protocol();
61588
62125
 
61589
62126
  // src/extension/bundle-zip.ts
61590
- var import_promises8 = __toESM(require("fs/promises"), 1);
61591
- var import_node_path55 = __toESM(require("path"), 1);
62127
+ var import_promises9 = __toESM(require("fs/promises"), 1);
62128
+ var import_node_path56 = __toESM(require("path"), 1);
61592
62129
  var import_node_crypto15 = __toESM(require("crypto"), 1);
61593
62130
  var import_jszip2 = __toESM(require_lib3(), 1);
61594
62131
  async function bundleExtensionDir(dir) {
61595
62132
  const entries = await listFilesSorted(dir);
61596
62133
  const zip = new import_jszip2.default();
61597
62134
  for (const rel of entries) {
61598
- const abs = import_node_path55.default.join(dir, rel);
61599
- const content = await import_promises8.default.readFile(abs);
62135
+ const abs = import_node_path56.default.join(dir, rel);
62136
+ const content = await import_promises9.default.readFile(abs);
61600
62137
  zip.file(rel, content, { date: FIXED_DATE });
61601
62138
  }
61602
62139
  const buffer = await zip.generateAsync({
@@ -61616,8 +62153,8 @@ async function listFilesSorted(rootDir) {
61616
62153
  return out;
61617
62154
  }
61618
62155
  async function walk(absRoot, relPrefix, out) {
61619
- const dirAbs = import_node_path55.default.join(absRoot, relPrefix);
61620
- const entries = await import_promises8.default.readdir(dirAbs, { withFileTypes: true });
62156
+ const dirAbs = import_node_path56.default.join(absRoot, relPrefix);
62157
+ const entries = await import_promises9.default.readdir(dirAbs, { withFileTypes: true });
61621
62158
  for (const e of entries) {
61622
62159
  if (IGNORE_BASENAMES.has(e.name)) continue;
61623
62160
  const rel = relPrefix ? `${relPrefix}/${e.name}` : e.name;
@@ -61669,26 +62206,26 @@ function computePublishCheck(args) {
61669
62206
  }
61670
62207
 
61671
62208
  // src/extension/install-flow.ts
61672
- var import_promises9 = __toESM(require("fs/promises"), 1);
61673
- var import_node_path57 = __toESM(require("path"), 1);
62209
+ var import_promises10 = __toESM(require("fs/promises"), 1);
62210
+ var import_node_path58 = __toESM(require("path"), 1);
61674
62211
  var import_node_os21 = __toESM(require("os"), 1);
61675
62212
  var import_node_crypto16 = __toESM(require("crypto"), 1);
61676
62213
  var import_jszip3 = __toESM(require_lib3(), 1);
61677
62214
 
61678
62215
  // src/extension/paths.ts
61679
62216
  var import_node_os20 = __toESM(require("os"), 1);
61680
- var import_node_path56 = __toESM(require("path"), 1);
62217
+ var import_node_path57 = __toESM(require("path"), 1);
61681
62218
  function clawdHomeRoot(override) {
61682
- return override ?? process.env.CLAWD_HOME ?? import_node_path56.default.join(import_node_os20.default.homedir(), ".clawd");
62219
+ return override ?? process.env.CLAWD_HOME ?? import_node_path57.default.join(import_node_os20.default.homedir(), ".clawd");
61683
62220
  }
61684
62221
  function extensionsRoot(override) {
61685
- return import_node_path56.default.join(clawdHomeRoot(override), "extensions");
62222
+ return import_node_path57.default.join(clawdHomeRoot(override), "extensions");
61686
62223
  }
61687
62224
  function publishedChannelsFile(override) {
61688
- return import_node_path56.default.join(clawdHomeRoot(override), "extensions-published.json");
62225
+ return import_node_path57.default.join(clawdHomeRoot(override), "extensions-published.json");
61689
62226
  }
61690
62227
  function bundleCacheRoot(override) {
61691
- return import_node_path56.default.join(clawdHomeRoot(override), "extension-bundles");
62228
+ return import_node_path57.default.join(clawdHomeRoot(override), "extension-bundles");
61692
62229
  }
61693
62230
 
61694
62231
  // src/extension/install-flow.ts
@@ -61715,7 +62252,7 @@ async function installFromChannel(args, deps) {
61715
62252
  throw new InstallError("ZIP_INVALID", `failed to load zip: ${e.message}`);
61716
62253
  }
61717
62254
  for (const name of Object.keys(zip.files)) {
61718
- if (name.includes("..") || name.startsWith("/") || import_node_path57.default.isAbsolute(name)) {
62255
+ if (name.includes("..") || name.startsWith("/") || import_node_path58.default.isAbsolute(name)) {
61719
62256
  throw new InstallError("ZIP_INVALID", `unsafe zip entry: ${name}`);
61720
62257
  }
61721
62258
  }
@@ -61747,10 +62284,10 @@ async function installFromChannel(args, deps) {
61747
62284
  );
61748
62285
  }
61749
62286
  const localExtId = namespacedExtId(ownerSlug, channelRef.ownerPrincipalId);
61750
- const destDir = import_node_path57.default.join(deps.extensionsRoot, localExtId);
62287
+ const destDir = import_node_path58.default.join(deps.extensionsRoot, localExtId);
61751
62288
  let destExists = false;
61752
62289
  try {
61753
- await import_promises9.default.access(destDir);
62290
+ await import_promises10.default.access(destDir);
61754
62291
  destExists = true;
61755
62292
  } catch {
61756
62293
  }
@@ -61760,28 +62297,28 @@ async function installFromChannel(args, deps) {
61760
62297
  `extension ${localExtId} already installed locally (from owner ${channelRef.ownerPrincipalId})`
61761
62298
  );
61762
62299
  }
61763
- const stage = await import_promises9.default.mkdtemp(
61764
- import_node_path57.default.join(import_node_os21.default.tmpdir(), `clawd-ext-install-${localExtId}-`)
62300
+ const stage = await import_promises10.default.mkdtemp(
62301
+ import_node_path58.default.join(import_node_os21.default.tmpdir(), `clawd-ext-install-${localExtId}-`)
61765
62302
  );
61766
62303
  try {
61767
62304
  for (const [name, entry] of Object.entries(zip.files)) {
61768
- const dest = import_node_path57.default.join(stage, name);
62305
+ const dest = import_node_path58.default.join(stage, name);
61769
62306
  if (entry.dir) {
61770
- await import_promises9.default.mkdir(dest, { recursive: true });
62307
+ await import_promises10.default.mkdir(dest, { recursive: true });
61771
62308
  continue;
61772
62309
  }
61773
- await import_promises9.default.mkdir(import_node_path57.default.dirname(dest), { recursive: true });
62310
+ await import_promises10.default.mkdir(import_node_path58.default.dirname(dest), { recursive: true });
61774
62311
  if (name === "manifest.json") {
61775
62312
  const rewritten = { ...parsed.data, id: localExtId };
61776
- await import_promises9.default.writeFile(dest, JSON.stringify(rewritten, null, 2));
62313
+ await import_promises10.default.writeFile(dest, JSON.stringify(rewritten, null, 2));
61777
62314
  } else {
61778
- await import_promises9.default.writeFile(dest, await entry.async("nodebuffer"));
62315
+ await import_promises10.default.writeFile(dest, await entry.async("nodebuffer"));
61779
62316
  }
61780
62317
  }
61781
- await import_promises9.default.mkdir(deps.extensionsRoot, { recursive: true });
61782
- await import_promises9.default.rename(stage, destDir);
62318
+ await import_promises10.default.mkdir(deps.extensionsRoot, { recursive: true });
62319
+ await import_promises10.default.rename(stage, destDir);
61783
62320
  } catch (e) {
61784
- await import_promises9.default.rm(stage, { recursive: true, force: true }).catch(() => {
62321
+ await import_promises10.default.rm(stage, { recursive: true, force: true }).catch(() => {
61785
62322
  });
61786
62323
  throw e;
61787
62324
  }
@@ -61790,8 +62327,8 @@ async function installFromChannel(args, deps) {
61790
62327
  }
61791
62328
 
61792
62329
  // src/extension/update-flow.ts
61793
- var import_promises10 = __toESM(require("fs/promises"), 1);
61794
- var import_node_path58 = __toESM(require("path"), 1);
62330
+ var import_promises11 = __toESM(require("fs/promises"), 1);
62331
+ var import_node_path59 = __toESM(require("path"), 1);
61795
62332
  var import_node_os22 = __toESM(require("os"), 1);
61796
62333
  var import_node_crypto17 = __toESM(require("crypto"), 1);
61797
62334
  var import_jszip4 = __toESM(require_lib3(), 1);
@@ -61808,11 +62345,11 @@ async function updateFromChannel(args, deps) {
61808
62345
  channelRef.extId,
61809
62346
  channelRef.ownerPrincipalId
61810
62347
  );
61811
- const liveDir = import_node_path58.default.join(deps.extensionsRoot, localExtId);
62348
+ const liveDir = import_node_path59.default.join(deps.extensionsRoot, localExtId);
61812
62349
  const prevDir = `${liveDir}.prev`;
61813
62350
  let existingVersion;
61814
62351
  try {
61815
- const raw = await import_promises10.default.readFile(import_node_path58.default.join(liveDir, "manifest.json"), "utf8");
62352
+ const raw = await import_promises11.default.readFile(import_node_path59.default.join(liveDir, "manifest.json"), "utf8");
61816
62353
  const parsed2 = ExtensionManifestSchema.safeParse(JSON.parse(raw));
61817
62354
  if (!parsed2.success) {
61818
62355
  throw new UpdateError(
@@ -61845,7 +62382,7 @@ async function updateFromChannel(args, deps) {
61845
62382
  throw new UpdateError("ZIP_INVALID", `failed to load zip: ${e.message}`);
61846
62383
  }
61847
62384
  for (const name of Object.keys(zip.files)) {
61848
- if (name.includes("..") || name.startsWith("/") || import_node_path58.default.isAbsolute(name)) {
62385
+ if (name.includes("..") || name.startsWith("/") || import_node_path59.default.isAbsolute(name)) {
61849
62386
  throw new UpdateError("ZIP_INVALID", `unsafe zip entry: ${name}`);
61850
62387
  }
61851
62388
  }
@@ -61877,31 +62414,31 @@ async function updateFromChannel(args, deps) {
61877
62414
  );
61878
62415
  }
61879
62416
  await deps.runtime.close(localExtId);
61880
- await import_promises10.default.rm(prevDir, { recursive: true, force: true });
61881
- await import_promises10.default.rename(liveDir, prevDir);
61882
- const stage = await import_promises10.default.mkdtemp(
61883
- import_node_path58.default.join(import_node_os22.default.tmpdir(), `clawd-ext-update-${localExtId}-`)
62417
+ await import_promises11.default.rm(prevDir, { recursive: true, force: true });
62418
+ await import_promises11.default.rename(liveDir, prevDir);
62419
+ const stage = await import_promises11.default.mkdtemp(
62420
+ import_node_path59.default.join(import_node_os22.default.tmpdir(), `clawd-ext-update-${localExtId}-`)
61884
62421
  );
61885
62422
  try {
61886
62423
  for (const [name, entry] of Object.entries(zip.files)) {
61887
- const dest = import_node_path58.default.join(stage, name);
62424
+ const dest = import_node_path59.default.join(stage, name);
61888
62425
  if (entry.dir) {
61889
- await import_promises10.default.mkdir(dest, { recursive: true });
62426
+ await import_promises11.default.mkdir(dest, { recursive: true });
61890
62427
  continue;
61891
62428
  }
61892
- await import_promises10.default.mkdir(import_node_path58.default.dirname(dest), { recursive: true });
62429
+ await import_promises11.default.mkdir(import_node_path59.default.dirname(dest), { recursive: true });
61893
62430
  if (name === "manifest.json") {
61894
62431
  const rewritten = { ...parsed.data, id: localExtId };
61895
- await import_promises10.default.writeFile(dest, JSON.stringify(rewritten, null, 2));
62432
+ await import_promises11.default.writeFile(dest, JSON.stringify(rewritten, null, 2));
61896
62433
  } else {
61897
- await import_promises10.default.writeFile(dest, await entry.async("nodebuffer"));
62434
+ await import_promises11.default.writeFile(dest, await entry.async("nodebuffer"));
61898
62435
  }
61899
62436
  }
61900
- await import_promises10.default.rename(stage, liveDir);
62437
+ await import_promises11.default.rename(stage, liveDir);
61901
62438
  } catch (e) {
61902
- await import_promises10.default.rm(stage, { recursive: true, force: true }).catch(() => {
62439
+ await import_promises11.default.rm(stage, { recursive: true, force: true }).catch(() => {
61903
62440
  });
61904
- await import_promises10.default.rename(prevDir, liveDir).catch(() => {
62441
+ await import_promises11.default.rename(prevDir, liveDir).catch(() => {
61905
62442
  });
61906
62443
  await deps.runtime.open(localExtId).catch(() => {
61907
62444
  });
@@ -61923,7 +62460,7 @@ async function updateFromChannel(args, deps) {
61923
62460
  reason: e.message
61924
62461
  };
61925
62462
  }
61926
- await import_promises10.default.rm(prevDir, { recursive: true, force: true });
62463
+ await import_promises11.default.rm(prevDir, { recursive: true, force: true });
61927
62464
  return {
61928
62465
  kind: "updated",
61929
62466
  extId: localExtId,
@@ -61931,9 +62468,9 @@ async function updateFromChannel(args, deps) {
61931
62468
  };
61932
62469
  }
61933
62470
  async function rollback(deps, localExtId, liveDir, prevDir) {
61934
- await import_promises10.default.rm(liveDir, { recursive: true, force: true }).catch(() => {
62471
+ await import_promises11.default.rm(liveDir, { recursive: true, force: true }).catch(() => {
61935
62472
  });
61936
- await import_promises10.default.rename(prevDir, liveDir).catch(() => {
62473
+ await import_promises11.default.rename(prevDir, liveDir).catch(() => {
61937
62474
  });
61938
62475
  await deps.runtime.open(localExtId).catch(() => {
61939
62476
  });
@@ -61956,18 +62493,18 @@ async function rewriteManifestVersion(root, extId, newVersion, previousPublished
61956
62493
  );
61957
62494
  }
61958
62495
  }
61959
- const manifestPath = import_node_path59.default.join(root, extId, "manifest.json");
62496
+ const manifestPath = import_node_path60.default.join(root, extId, "manifest.json");
61960
62497
  const manifest = await readManifest(root, extId);
61961
62498
  const next = { ...manifest, version: newVersion };
61962
62499
  const tmp = `${manifestPath}.tmp`;
61963
- await import_promises11.default.writeFile(tmp, JSON.stringify(next, null, 2));
61964
- await import_promises11.default.rename(tmp, manifestPath);
62500
+ await import_promises12.default.writeFile(tmp, JSON.stringify(next, null, 2));
62501
+ await import_promises12.default.rename(tmp, manifestPath);
61965
62502
  }
61966
62503
  async function readManifest(root, extId) {
61967
- const file = import_node_path59.default.join(root, extId, "manifest.json");
62504
+ const file = import_node_path60.default.join(root, extId, "manifest.json");
61968
62505
  let raw;
61969
62506
  try {
61970
- raw = await import_promises11.default.readFile(file, "utf8");
62507
+ raw = await import_promises12.default.readFile(file, "utf8");
61971
62508
  } catch (e) {
61972
62509
  if (e.code === "ENOENT") {
61973
62510
  throw new ClawdError(ERROR_CODES.INVALID_PARAM, `extension ${extId} not installed`);
@@ -62048,7 +62585,7 @@ function buildExtensionHandlers(deps) {
62048
62585
  };
62049
62586
  async function buildSnapshotMeta(extId) {
62050
62587
  const manifest = await readManifest(deps.root, extId);
62051
- const { sha256, buffer } = await bundleExtensionDir(import_node_path59.default.join(deps.root, extId));
62588
+ const { sha256, buffer } = await bundleExtensionDir(import_node_path60.default.join(deps.root, extId));
62052
62589
  return { manifest, contentHash: sha256, buffer };
62053
62590
  }
62054
62591
  const publish = async (frame, _client, ctx) => {
@@ -62225,7 +62762,7 @@ function buildExtensionHandlers(deps) {
62225
62762
  // src/app-builder/project-store.ts
62226
62763
  var import_node_fs49 = require("fs");
62227
62764
  var import_node_child_process15 = require("child_process");
62228
- var import_node_path60 = require("path");
62765
+ var import_node_path61 = require("path");
62229
62766
  init_protocol();
62230
62767
  var PROJECTS_DIR = "projects";
62231
62768
  var META_FILE = ".clawd-project.json";
@@ -62239,14 +62776,14 @@ var ProjectStore = class {
62239
62776
  root;
62240
62777
  /** projects/<name>/.clawd-project.json 路径 */
62241
62778
  metaPath(name) {
62242
- return (0, import_node_path60.join)(this.projectsRoot(), name, META_FILE);
62779
+ return (0, import_node_path61.join)(this.projectsRoot(), name, META_FILE);
62243
62780
  }
62244
62781
  /** projects/<name>/ 目录路径(cwd 用) */
62245
62782
  projectDir(name) {
62246
- return (0, import_node_path60.join)(this.projectsRoot(), name);
62783
+ return (0, import_node_path61.join)(this.projectsRoot(), name);
62247
62784
  }
62248
62785
  projectsRoot() {
62249
- return (0, import_node_path60.join)(this.root, PROJECTS_DIR);
62786
+ return (0, import_node_path61.join)(this.root, PROJECTS_DIR);
62250
62787
  }
62251
62788
  async list() {
62252
62789
  let entries;
@@ -62549,7 +63086,7 @@ var PublishJobRegistry = class {
62549
63086
  // src/app-builder/publish-job-runner.ts
62550
63087
  var import_node_child_process17 = require("child_process");
62551
63088
  var import_node_fs50 = require("fs");
62552
- var import_node_path61 = require("path");
63089
+ var import_node_path62 = require("path");
62553
63090
 
62554
63091
  // src/app-builder/publish-stage-parser.ts
62555
63092
  var STAGE_RE = /^\s*::stage::(build|deploy|verify)\s*$/;
@@ -62581,7 +63118,7 @@ async function startPublishJob(deps, args) {
62581
63118
  return { jobId: registry2.get(args.name).jobId, status: "already-publishing" };
62582
63119
  }
62583
63120
  const projDir = projectDir(args.name);
62584
- const logPath = (0, import_node_path61.join)(projDir, ".publish.log");
63121
+ const logPath = (0, import_node_path62.join)(projDir, ".publish.log");
62585
63122
  let logStream = null;
62586
63123
  try {
62587
63124
  logStream = (0, import_node_fs50.createWriteStream)(logPath, { flags: "w" });
@@ -62841,7 +63378,7 @@ async function recoverInterruptedJobs(deps) {
62841
63378
 
62842
63379
  // src/handlers/app-builder.ts
62843
63380
  init_protocol();
62844
- var import_node_path62 = require("path");
63381
+ var import_node_path63 = require("path");
62845
63382
  var import_node_fs51 = require("fs");
62846
63383
  var APP_BUILDER_PERSONAS = ["persona-app-builder", "persona-dataclaw-builder"];
62847
63384
  var DEV_SERVER_READY_TIMEOUT_MS = 3e4;
@@ -62992,8 +63529,8 @@ function buildAppBuilderHandlers(deps) {
62992
63529
  const project = await userStore.create(f.name, reservedPorts);
62993
63530
  try {
62994
63531
  const personaRoot = deps.resolvePersonaRoot ? deps.resolvePersonaRoot(session.ownerPersonaId ?? "") : deps.personaRoot;
62995
- const templateSrcDir = (0, import_node_path62.join)(personaRoot, "extension-kit", "examples", DEFAULT_TEMPLATE);
62996
- const scaffoldScript = (0, import_node_path62.join)(deps.deployKitRoot, "scripts", "new-extension.sh");
63532
+ const templateSrcDir = (0, import_node_path63.join)(personaRoot, "extension-kit", "examples", DEFAULT_TEMPLATE);
63533
+ const scaffoldScript = (0, import_node_path63.join)(deps.deployKitRoot, "scripts", "new-extension.sh");
62997
63534
  const scaffoldResult = await userStore.scaffold(project.name, templateSrcDir, scaffoldScript);
62998
63535
  deps.logger?.info("app-builder.scaffold.done", {
62999
63536
  name: project.name,
@@ -63197,7 +63734,7 @@ function buildAppBuilderHandlers(deps) {
63197
63734
  await userStore.clearPublishJob(args.name);
63198
63735
  }
63199
63736
  const personaRoot = deps.resolvePersonaRoot ? deps.resolvePersonaRoot(boundSession.ownerPersonaId ?? "") : deps.personaRoot;
63200
- const scriptPath = (0, import_node_path62.join)(deps.deployKitRoot, "scripts", "publish.sh");
63737
+ const scriptPath = (0, import_node_path63.join)(deps.deployKitRoot, "scripts", "publish.sh");
63201
63738
  deps.logger?.info("app-builder.publish.start", {
63202
63739
  name: args.name,
63203
63740
  sessionId: boundSession.sessionId,
@@ -63346,6 +63883,427 @@ function buildShiftHandlers(deps) {
63346
63883
  };
63347
63884
  }
63348
63885
 
63886
+ // src/handlers/channel.ts
63887
+ init_protocol();
63888
+ function buildChannelHandlers(deps) {
63889
+ return {
63890
+ "channel:list": async () => ({
63891
+ response: ChannelListResultSchema.parse({ channels: deps.channelStore.list() })
63892
+ }),
63893
+ "channel:create": async (frame) => {
63894
+ const args = ChannelCreateArgsSchema.parse(frame);
63895
+ const channel = deps.channelStore.create({
63896
+ name: args.name,
63897
+ memberPersonaIds: args.memberPersonaIds,
63898
+ memberContactDeviceIds: args.memberContactDeviceIds
63899
+ });
63900
+ return { response: ChannelResultSchema.parse({ channel }) };
63901
+ },
63902
+ "channel:update": async (frame) => {
63903
+ const args = ChannelUpdateArgsSchema.parse(frame);
63904
+ const channel = deps.channelStore.update(args.channelId, {
63905
+ ...args.name !== void 0 ? { name: args.name } : {},
63906
+ ...args.memberPersonaIds !== void 0 ? { memberPersonaIds: args.memberPersonaIds } : {},
63907
+ ...args.memberContactDeviceIds !== void 0 ? { memberContactDeviceIds: args.memberContactDeviceIds } : {}
63908
+ });
63909
+ if (!channel) {
63910
+ throw new ClawdError(ERROR_CODES.INVALID_PARAM, `channel not found: ${args.channelId}`);
63911
+ }
63912
+ return { response: ChannelResultSchema.parse({ channel }) };
63913
+ },
63914
+ "channel:delete": async (frame) => {
63915
+ const { channelId } = ChannelDeleteArgsSchema.parse(frame);
63916
+ if (!deps.channelStore.get(channelId)) {
63917
+ throw new ClawdError(ERROR_CODES.INVALID_PARAM, `channel not found: ${channelId}`);
63918
+ }
63919
+ if (deps.topicStore.hasUnfinished(channelId)) {
63920
+ throw new ClawdError(
63921
+ ERROR_CODES.INVALID_PARAM,
63922
+ "\u8BE5\u9891\u9053\u4E0B\u4ECD\u6709\u672A\u7ED3\u675F\u8BDD\u9898\uFF0C\u5148\u6536\u655B\u6216\u5F52\u6863\u5B83\u4EEC\u518D\u5220\u9891\u9053"
63923
+ );
63924
+ }
63925
+ const removedTopics = deps.topicStore.removeByChannel(channelId);
63926
+ deps.channelStore.remove(channelId);
63927
+ return { response: ChannelDeleteResultSchema.parse({ ok: true, removedTopics }) };
63928
+ }
63929
+ };
63930
+ }
63931
+
63932
+ // src/handlers/topic.ts
63933
+ init_protocol();
63934
+ var REJECT_ESCALATE_AT = 2;
63935
+ function joinLines(records, lineMeta) {
63936
+ const out = [];
63937
+ for (const r of records) {
63938
+ const meta = r.meta ?? {};
63939
+ if (typeof meta.lineId !== "string" || !meta.lineId) continue;
63940
+ out.push({
63941
+ lineId: meta.lineId,
63942
+ dispatchId: r.dispatchId,
63943
+ ...r.workerSessionId ? { workerSessionId: r.workerSessionId } : {},
63944
+ assignee: r.targetPersonaId,
63945
+ focus: r.taskText,
63946
+ status: r.status,
63947
+ acceptance: Array.isArray(meta.acceptance) ? meta.acceptance.map(String) : [],
63948
+ rejectCount: lineMeta[meta.lineId]?.rejectCount ?? 0,
63949
+ accepted: lineMeta[meta.lineId]?.accepted ?? false
63950
+ });
63951
+ }
63952
+ return out;
63953
+ }
63954
+ function buildTopicHandlers(deps) {
63955
+ const mustGet = (topicId) => {
63956
+ const t = deps.topicStore.get(topicId);
63957
+ if (!t) throw new ClawdError(ERROR_CODES.INVALID_PARAM, `topic not found: ${topicId}`);
63958
+ return t;
63959
+ };
63960
+ const linesOf = (t) => joinLines(deps.dispatchStore.list({ sourceSessionId: t.masterSessionId }), t.lineMeta);
63961
+ return {
63962
+ "topic:list": async (frame) => {
63963
+ const { channelId } = TopicListArgsSchema.parse(frame);
63964
+ const topics = deps.topicStore.listByChannel(channelId).map((t) => ({ ...t, lineCount: linesOf(t).length }));
63965
+ return { response: TopicListResultSchema.parse({ topics }) };
63966
+ },
63967
+ "topic:get": async (frame) => {
63968
+ const args = TopicGetArgsSchema.parse(frame);
63969
+ const topic = args.topicId ? mustGet(args.topicId) : deps.topicStore.getByMasterSession(args.sessionId);
63970
+ if (!topic) {
63971
+ throw new ClawdError(
63972
+ ERROR_CODES.INVALID_PARAM,
63973
+ `topic not found for session: ${args.sessionId}`
63974
+ );
63975
+ }
63976
+ return { response: TopicGetResultSchema.parse({ topic, lines: linesOf(topic) }) };
63977
+ },
63978
+ "topic:create": async (frame) => {
63979
+ const args = TopicCreateArgsSchema.parse(frame);
63980
+ if (!deps.channelStore.get(args.channelId)) {
63981
+ throw new ClawdError(ERROR_CODES.INVALID_PARAM, `channel not found: ${args.channelId}`);
63982
+ }
63983
+ const { sessionId } = deps.createMasterSession({ label: args.title ?? "\u65B0\u8BDD\u9898" });
63984
+ const topic = deps.topicStore.create({
63985
+ channelId: args.channelId,
63986
+ ...args.title ? { title: args.title } : {},
63987
+ masterSessionId: sessionId
63988
+ });
63989
+ return {
63990
+ response: TopicCreateResultSchema.parse({
63991
+ topicId: topic.id,
63992
+ masterSessionId: sessionId
63993
+ })
63994
+ };
63995
+ },
63996
+ "topic:setPlan": async (frame) => {
63997
+ const args = TopicSetPlanArgsSchema.parse(frame);
63998
+ mustGet(args.topicId);
63999
+ const topic = deps.topicStore.setPlan(args.topicId, {
64000
+ ...args.title ? { title: args.title } : {},
64001
+ goal: args.goal,
64002
+ boundaries: args.boundaries,
64003
+ acceptanceCriteria: args.acceptanceCriteria
64004
+ });
64005
+ return { response: TopicResultSchema.parse({ topic }) };
64006
+ },
64007
+ "topic:append": async (frame) => {
64008
+ const args = TopicAppendArgsSchema.parse(frame);
64009
+ mustGet(args.topicId);
64010
+ deps.topicStore.appendActivity(args.topicId, {
64011
+ actor: "master",
64012
+ action: args.action,
64013
+ ...args.target ? { target: args.target } : {},
64014
+ reason: args.reason,
64015
+ ...args.artifacts ? { artifacts: args.artifacts } : {}
64016
+ });
64017
+ return { response: { ok: true } };
64018
+ },
64019
+ "topic:reject": async (frame) => {
64020
+ const args = TopicRejectArgsSchema.parse(frame);
64021
+ mustGet(args.topicId);
64022
+ const { rejectCount } = deps.topicStore.bumpReject(args.topicId, args.lineId);
64023
+ deps.topicStore.appendActivity(args.topicId, {
64024
+ actor: "master",
64025
+ action: `\u6253\u56DE ${args.lineId}\uFF08\u7B2C ${rejectCount} \u6B21\uFF09`,
64026
+ target: args.lineId,
64027
+ reason: args.reason
64028
+ });
64029
+ return {
64030
+ response: TopicRejectResultSchema.parse({
64031
+ rejectCount,
64032
+ needsOwnerAttention: rejectCount >= REJECT_ESCALATE_AT
64033
+ })
64034
+ };
64035
+ },
64036
+ "topic:accept": async (frame) => {
64037
+ const args = TopicAcceptArgsSchema.parse(frame);
64038
+ const topic = mustGet(args.topicId);
64039
+ if (!linesOf(topic).some((l) => l.lineId === args.lineId)) {
64040
+ throw new ClawdError(
64041
+ ERROR_CODES.INVALID_PARAM,
64042
+ `line not found in this topic: ${args.lineId}`
64043
+ );
64044
+ }
64045
+ deps.topicStore.markAccepted(args.topicId, args.lineId);
64046
+ deps.topicStore.appendActivity(args.topicId, {
64047
+ actor: "master",
64048
+ action: `\u5224\u5B9A ${args.lineId} \u5408\u683C`,
64049
+ target: args.lineId,
64050
+ reason: args.reason,
64051
+ ...args.artifacts ? { artifacts: args.artifacts } : {}
64052
+ });
64053
+ const lines = linesOf(mustGet(args.topicId));
64054
+ const acceptedLines = lines.filter((l) => l.accepted).length;
64055
+ return {
64056
+ response: TopicAcceptResultSchema.parse({
64057
+ acceptedLines,
64058
+ totalLines: lines.length,
64059
+ allAccepted: acceptedLines === lines.length
64060
+ })
64061
+ };
64062
+ },
64063
+ "topic:close": async (frame) => {
64064
+ const args = TopicCloseArgsSchema.parse(frame);
64065
+ mustGet(args.topicId);
64066
+ const topic = deps.topicStore.close(args.topicId, args.state);
64067
+ return { response: TopicResultSchema.parse({ topic }) };
64068
+ }
64069
+ };
64070
+ }
64071
+
64072
+ // src/handlers/line.ts
64073
+ init_protocol();
64074
+ init_claude_history();
64075
+
64076
+ // src/channel/digest.ts
64077
+ var QUOTA = { files: 0.14, commands: 0.12, outputs: 0.22, errors: 0.24, tail: 0.28 };
64078
+ var WRITE_TOOLS = /* @__PURE__ */ new Set(["Write", "Edit", "MultiEdit", "NotebookEdit"]);
64079
+ var READ_TOOLS = /* @__PURE__ */ new Set(["Read", "NotebookRead"]);
64080
+ var DIGEST_DEFAULTS = { tailAssistant: 12, maxBytes: 2e4 };
64081
+ var firstLine = (s) => String(s ?? "").split("\n")[0].trim();
64082
+ var errSignature = (s) => firstLine(s).replace(/\d+/g, "N").replace(/(^|[\s'"`([])\/[^\s'"`)\]]+/g, "$1<path>").slice(0, 200);
64083
+ var blocksOf = (rec4) => {
64084
+ const c = rec4?.message?.content;
64085
+ return Array.isArray(c) ? c : [];
64086
+ };
64087
+ function takeWithin(lines, budget) {
64088
+ if (!lines.length) return { out: ["- \uFF08\u65E0\uFF09"], clipped: false };
64089
+ const out = [];
64090
+ let used = 0;
64091
+ for (let i = 0; i < lines.length; i++) {
64092
+ const size = Buffer.byteLength(lines[i], "utf8") + 1;
64093
+ if (used + size > budget && out.length) {
64094
+ out.push(`- \u2026\u672C\u8282\u8FD8\u6709 ${lines.length - i} \u6761\u672A\u5217\u51FA\uFF08\u672C\u8282\u914D\u989D ${budget} \u5B57\u8282\u5DF2\u6EE1\uFF09`);
64095
+ return { out, clipped: true };
64096
+ }
64097
+ out.push(lines[i]);
64098
+ used += size;
64099
+ }
64100
+ return { out, clipped: false };
64101
+ }
64102
+ function clampBytes(text, limit) {
64103
+ if (Buffer.byteLength(text, "utf8") <= limit) return text;
64104
+ let lo = 0;
64105
+ let hi = text.length;
64106
+ while (lo < hi) {
64107
+ const mid = Math.ceil((lo + hi) / 2);
64108
+ if (Buffer.byteLength(text.slice(0, mid), "utf8") <= limit) lo = mid;
64109
+ else hi = mid - 1;
64110
+ }
64111
+ return text.slice(0, lo);
64112
+ }
64113
+ function buildDigest(records, opts = {}) {
64114
+ const maxBytes = opts.maxBytes ?? DIGEST_DEFAULTS.maxBytes;
64115
+ const tailAssistant = opts.tailAssistant ?? DIGEST_DEFAULTS.tailAssistant;
64116
+ const filesWritten = /* @__PURE__ */ new Set();
64117
+ const filesRead = /* @__PURE__ */ new Set();
64118
+ const commands = /* @__PURE__ */ new Map();
64119
+ const errors = /* @__PURE__ */ new Map();
64120
+ const outputs = [];
64121
+ const pendingCmd = /* @__PURE__ */ new Map();
64122
+ let lastCmd = null;
64123
+ const tail = [];
64124
+ let toolCalls = 0;
64125
+ for (const rec4 of records) {
64126
+ for (const b2 of blocksOf(rec4)) {
64127
+ if (b2.type === "tool_use") {
64128
+ toolCalls += 1;
64129
+ const fp = b2.input?.file_path ?? b2.input?.notebook_path;
64130
+ if (fp && b2.name && WRITE_TOOLS.has(b2.name)) filesWritten.add(String(fp));
64131
+ else if (fp && b2.name && READ_TOOLS.has(b2.name)) filesRead.add(String(fp));
64132
+ if (b2.name === "Bash" && b2.input?.command) {
64133
+ const c = firstLine(b2.input.command);
64134
+ commands.set(c, (commands.get(c) ?? 0) + 1);
64135
+ if (b2.id) pendingCmd.set(b2.id, c);
64136
+ lastCmd = c;
64137
+ }
64138
+ } else if (b2.type === "tool_result" && !b2.is_error) {
64139
+ const cmd = b2.tool_use_id ? pendingCmd.get(b2.tool_use_id) : lastCmd;
64140
+ if (cmd) {
64141
+ const text2 = typeof b2.content === "string" ? b2.content : JSON.stringify(b2.content ?? "");
64142
+ const flat = text2.replace(/\s+/g, " ").trim();
64143
+ if (flat) {
64144
+ outputs.push(`- ${cmd} \u2192 \u2026${flat.slice(-220)}`);
64145
+ if (outputs.length > 40) outputs.shift();
64146
+ }
64147
+ if (b2.tool_use_id) pendingCmd.delete(b2.tool_use_id);
64148
+ }
64149
+ } else if (b2.type === "tool_result" && b2.is_error) {
64150
+ const text2 = typeof b2.content === "string" ? b2.content : JSON.stringify(b2.content ?? "");
64151
+ const sig = errSignature(text2);
64152
+ const prev = errors.get(sig);
64153
+ if (prev) prev.count += 1;
64154
+ else errors.set(sig, { count: 1, sample: firstLine(text2).slice(0, 300) });
64155
+ } else if (b2.type === "text" && rec4.type === "assistant") {
64156
+ tail.push(b2.text ?? "");
64157
+ if (tail.length > tailAssistant) tail.shift();
64158
+ }
64159
+ }
64160
+ }
64161
+ const budget = (k2) => Math.max(200, Math.floor(maxBytes * QUOTA[k2]));
64162
+ let anyClipped = false;
64163
+ const take = (lines, k2) => {
64164
+ const r = takeWithin(lines, budget(k2));
64165
+ if (r.clipped) anyClipped = true;
64166
+ return r.out;
64167
+ };
64168
+ const errLines = [...errors.entries()].sort((x, y2) => y2[1].count - x[1].count).map(([, v2]) => `- \xD7${v2.count} ${v2.sample}`);
64169
+ const cmdLines = [...commands.entries()].map(([c, n]) => n > 1 ? `- \xD7${n} ${c}` : `- ${c}`);
64170
+ const out = [
64171
+ "# worker transcript digest",
64172
+ ...opts.transcriptPath ? [`transcript: ${opts.transcriptPath}`] : [],
64173
+ `\u5DE5\u5177\u8C03\u7528\u603B\u6570: ${toolCalls}\uFF5C\u5199\u6587\u4EF6 ${filesWritten.size} \u4E2A\uFF5C\u8BFB\u6587\u4EF6 ${filesRead.size} \u4E2A\uFF5C\u4E0D\u540C\u547D\u4EE4 ${commands.size} \u6761\uFF5C\u62A5\u9519\u7B7E\u540D ${errors.size} \u79CD`,
64174
+ "",
64175
+ "## \u6539\u52A8\u8FC7\u7684\u6587\u4EF6\uFF08\u5F71\u54CD\u9762\uFF1B\u53EA\u7EDF\u8BA1 Write/Edit/MultiEdit/NotebookEdit\uFF09",
64176
+ ...take([...filesWritten].map((f) => `- ${f}`), "files"),
64177
+ "",
64178
+ "## \u6267\u884C\u8FC7\u7684\u547D\u4EE4\uFF08\u91CD\u590D\u7684\u6298\u53E0\u8BA1\u6570\uFF09",
64179
+ ...take(cmdLines, "commands"),
64180
+ "",
64181
+ "## \u62A5\u9519\uFF08\u6309\u7B7E\u540D\u805A\u5408\uFF1B\u91CD\u590D\u6B21\u6570\u9AD8 = \u649E\u5899\uFF09",
64182
+ ...take(errLines, "errors"),
64183
+ "",
64184
+ "## \u6210\u529F\u547D\u4EE4\u7684\u8F93\u51FA\u6458\u5F55\uFF08\u5404\u53D6\u5C3E\u90E8\uFF1B\u5224\u300C\u6D4B\u8BD5\u771F\u8DD1\u8FC7\u6CA1\u3001\u8FC7\u4E86\u51E0\u6761\u300D\u770B\u8FD9\u91CC\uFF09",
64185
+ ...take(outputs, "outputs"),
64186
+ "",
64187
+ `## \u6700\u540E ${tail.length} \u6BB5 assistant \u8F93\u51FA\uFF08\u4FDD\u7559\u539F\u6587\u6362\u884C\uFF0C\u6BCF\u6BB5\u622A\u65AD\u81F3 500 \u5B57\uFF09`,
64188
+ // 只留首行会把 worker 的收尾报告砍成「做完了」这种没信息量的一句,
64189
+ // 而那正是判「假完成」最需要的原文。
64190
+ ...take(tail.map((t) => `- ${String(t ?? "").slice(0, 500)}`), "tail")
64191
+ ];
64192
+ let text = out.join("\n");
64193
+ let truncated = anyClipped;
64194
+ if (Buffer.byteLength(text, "utf8") > maxBytes) {
64195
+ text = clampBytes(text, maxBytes);
64196
+ text += `
64197
+
64198
+ \u26A0\uFE0F digest \u5DF2\u622A\u65AD\uFF08\u4E0A\u9650 ${maxBytes} \u5B57\u8282\uFF09\u2014\u2014\u672A\u89C1\u5168\u90E8\u5185\u5BB9\uFF0C\u5224\u5B9A"\u6CA1\u505A"\u524D\u5148\u8981\u6C42 worker \u81EA\u8BC1\u3002`;
64199
+ truncated = true;
64200
+ } else if (anyClipped) {
64201
+ text += `
64202
+
64203
+ \u26A0\uFE0F digest \u5DF2\u622A\u65AD\uFF08\u6709\u5C0F\u8282\u8D85\u51FA\u914D\u989D\uFF0C\u89C1\u4E0A\u65B9\u300C\u672A\u5217\u51FA\u300D\u6807\u6CE8\uFF09\u2014\u2014\u672A\u89C1\u5168\u90E8\u5185\u5BB9\uFF0C\u5224\u5B9A"\u6CA1\u505A"\u524D\u5148\u8981\u6C42 worker \u81EA\u8BC1\u3002`;
64204
+ }
64205
+ return { digest: text, truncated };
64206
+ }
64207
+
64208
+ // src/handlers/line.ts
64209
+ function buildLineHandlers(deps) {
64210
+ return {
64211
+ "line:digest": async (frame) => {
64212
+ const args = LineDigestArgsSchema.parse(frame);
64213
+ const rec4 = deps.dispatchStore.get(args.dispatchId);
64214
+ if (!rec4) {
64215
+ throw new ClawdError(ERROR_CODES.INVALID_PARAM, `dispatch not found: ${args.dispatchId}`);
64216
+ }
64217
+ if (!rec4.workerSessionId) {
64218
+ throw new ClawdError(
64219
+ ERROR_CODES.INVALID_PARAM,
64220
+ `\u8BE5\u7EBF\u8FD8\u6CA1\u6709 worker \u4F1A\u8BDD\uFF08spawn \u672A\u5B8C\u6210\uFF09\uFF0C\u6682\u65F6\u65E0\u6CD5\u6838\u9A8C: ${args.dispatchId}`
64221
+ );
64222
+ }
64223
+ const sf = deps.getSessionFile(rec4.workerSessionId);
64224
+ if (!sf?.toolSessionId) {
64225
+ throw new ClawdError(
64226
+ ERROR_CODES.INVALID_PARAM,
64227
+ `worker \u4F1A\u8BDD\u5C1A\u672A\u4EA7\u751F transcript\uFF08\u8FD8\u6CA1\u8DD1\u8D77\u6765\uFF09: ${rec4.workerSessionId}`
64228
+ );
64229
+ }
64230
+ const file = deps.history.resolveTranscriptFile(sf.cwd, sf.toolSessionId);
64231
+ if (!file) {
64232
+ throw new ClawdError(
64233
+ ERROR_CODES.INVALID_PARAM,
64234
+ `transcript \u4E0D\u5B58\u5728\uFF08worker \u53EF\u80FD\u8FD8\u6CA1\u5199\u76D8\uFF09: ${sf.toolSessionId}`
64235
+ );
64236
+ }
64237
+ return {
64238
+ response: LineDigestResultSchema.parse(
64239
+ buildDigest(streamJsonlLines(file), { transcriptPath: file })
64240
+ )
64241
+ };
64242
+ }
64243
+ };
64244
+ }
64245
+
64246
+ // src/handlers/master.ts
64247
+ init_protocol();
64248
+
64249
+ // src/channel/authority.ts
64250
+ var import_promises13 = __toESM(require("fs/promises"), 1);
64251
+ init_protocol();
64252
+ var DEFAULT_AUTHORITY_RULES = [
64253
+ { id: "outbound-message", desc: "\u4EE5\u8001\u677F\u540D\u4E49\u7ED9\u5176\u4ED6\u4EBA\u53D1\u6D88\u606F\uFF08IM/\u90AE\u4EF6\uFF09", action: "ask" },
64254
+ { id: "push-remote", desc: "\u63A8\u4EE3\u7801\u5230\u8FDC\u7AEF / \u5408 PR / \u53D1\u5E03\u4E0A\u7EBF", action: "ask" },
64255
+ { id: "destructive", desc: "\u5220\u9664\u6570\u636E / \u52A8\u751F\u4EA7\u73AF\u5883", action: "ask" },
64256
+ { id: "spend", desc: "\u82B1\u94B1", action: "ask" }
64257
+ ];
64258
+ var askBecause = (reason) => ({
64259
+ action: "ask",
64260
+ desc: reason,
64261
+ source: `fallback:${reason}`
64262
+ });
64263
+ async function resolveAuthority(args) {
64264
+ let rules;
64265
+ let source;
64266
+ let raw = null;
64267
+ try {
64268
+ raw = await import_promises13.default.readFile(args.tablePath, "utf8");
64269
+ } catch (e) {
64270
+ if (e.code !== "ENOENT") {
64271
+ return askBecause(`\u6388\u6743\u8BBE\u5B9A\u8BFB\u4E0D\u51FA\u6765\uFF08${args.tablePath}\uFF09\uFF1A${e.message}`);
64272
+ }
64273
+ }
64274
+ if (raw === null) {
64275
+ rules = DEFAULT_AUTHORITY_RULES.map((r) => ({ ...r }));
64276
+ source = "default";
64277
+ } else {
64278
+ let parsed;
64279
+ try {
64280
+ parsed = JSON.parse(raw);
64281
+ } catch (e) {
64282
+ return askBecause(`\u6388\u6743\u8BBE\u5B9A\u4E0D\u662F\u5408\u6CD5 JSON\uFF08${args.tablePath}\uFF09\uFF1A${e.message}`);
64283
+ }
64284
+ const check = AuthorityTableSchema.safeParse(parsed);
64285
+ if (!check.success) {
64286
+ return askBecause(`\u6388\u6743\u8BBE\u5B9A\u7ED3\u6784\u4E0D\u5BF9\uFF08${args.tablePath}\uFF09\uFF1A${check.error.issues[0]?.message ?? "\u672A\u77E5"}`);
64287
+ }
64288
+ rules = check.data.rules;
64289
+ source = "table";
64290
+ }
64291
+ const hit = rules.find((r) => r.id === args.ruleId);
64292
+ if (!hit) return askBecause(`\u8868\u91CC\u6CA1\u6709 "${args.ruleId}" \u8FD9\u6761\uFF0C\u672A\u77E5\u52A8\u4F5C\u4E00\u5F8B\u505C\u4E0B\u95EE\u8001\u677F`);
64293
+ return { action: hit.action, desc: hit.desc, source };
64294
+ }
64295
+
64296
+ // src/handlers/master.ts
64297
+ function buildMasterHandlers(deps) {
64298
+ return {
64299
+ "master:authorityCheck": async (frame) => {
64300
+ const { ruleId } = AuthorityCheckArgsSchema.parse(frame);
64301
+ const result = await resolveAuthority({ tablePath: deps.authorityTablePath, ruleId });
64302
+ return { response: AuthorityCheckResultSchema.parse(result) };
64303
+ }
64304
+ };
64305
+ }
64306
+
63349
64307
  // src/handlers/github.ts
63350
64308
  init_protocol();
63351
64309
 
@@ -63616,12 +64574,12 @@ function buildVisitorHandlers(deps) {
63616
64574
  }
63617
64575
 
63618
64576
  // src/extension/registry.ts
63619
- var import_promises12 = __toESM(require("fs/promises"), 1);
63620
- var import_node_path63 = __toESM(require("path"), 1);
64577
+ var import_promises14 = __toESM(require("fs/promises"), 1);
64578
+ var import_node_path64 = __toESM(require("path"), 1);
63621
64579
  async function loadAll(root) {
63622
64580
  let entries;
63623
64581
  try {
63624
- entries = await import_promises12.default.readdir(root, { withFileTypes: true });
64582
+ entries = await import_promises14.default.readdir(root, { withFileTypes: true });
63625
64583
  } catch (e) {
63626
64584
  if (e.code === "ENOENT") return [];
63627
64585
  throw e;
@@ -63630,16 +64588,16 @@ async function loadAll(root) {
63630
64588
  for (const ent of entries) {
63631
64589
  if (!ent.isDirectory()) continue;
63632
64590
  if (ent.name.startsWith(".")) continue;
63633
- records.push(await loadOne(import_node_path63.default.join(root, ent.name), ent.name));
64591
+ records.push(await loadOne(import_node_path64.default.join(root, ent.name), ent.name));
63634
64592
  }
63635
64593
  records.sort((a, b2) => a.extId < b2.extId ? -1 : a.extId > b2.extId ? 1 : 0);
63636
64594
  return records;
63637
64595
  }
63638
64596
  async function loadOne(dir, dirName) {
63639
- const manifestPath = import_node_path63.default.join(dir, "manifest.json");
64597
+ const manifestPath = import_node_path64.default.join(dir, "manifest.json");
63640
64598
  let raw;
63641
64599
  try {
63642
- raw = await import_promises12.default.readFile(manifestPath, "utf8");
64600
+ raw = await import_promises14.default.readFile(manifestPath, "utf8");
63643
64601
  } catch {
63644
64602
  return {
63645
64603
  extId: dirName,
@@ -63680,8 +64638,8 @@ async function loadOne(dir, dirName) {
63680
64638
  }
63681
64639
 
63682
64640
  // src/extension/uninstall.ts
63683
- var import_promises13 = __toESM(require("fs/promises"), 1);
63684
- var import_node_path64 = __toESM(require("path"), 1);
64641
+ var import_promises15 = __toESM(require("fs/promises"), 1);
64642
+ var import_node_path65 = __toESM(require("path"), 1);
63685
64643
  var UninstallError = class extends Error {
63686
64644
  constructor(code, message) {
63687
64645
  super(message);
@@ -63690,14 +64648,14 @@ var UninstallError = class extends Error {
63690
64648
  code;
63691
64649
  };
63692
64650
  async function uninstall(deps) {
63693
- const dir = import_node_path64.default.join(deps.root, deps.extId);
64651
+ const dir = import_node_path65.default.join(deps.root, deps.extId);
63694
64652
  try {
63695
- await import_promises13.default.access(dir);
64653
+ await import_promises15.default.access(dir);
63696
64654
  } catch {
63697
64655
  throw new UninstallError("NOT_FOUND", `extension ${deps.extId} not installed`);
63698
64656
  }
63699
64657
  await deps.runtime.close(deps.extId);
63700
- await import_promises13.default.rm(dir, { recursive: true, force: true });
64658
+ await import_promises15.default.rm(dir, { recursive: true, force: true });
63701
64659
  }
63702
64660
 
63703
64661
  // src/handlers/index.ts
@@ -63916,6 +64874,22 @@ function buildMethodHandlers(deps) {
63916
64874
  resolvePersonaRoot: deps.resolvePersonaRoot,
63917
64875
  broadcastSessionFrame: deps.broadcastSessionFrame
63918
64876
  }),
64877
+ ...buildChannelHandlers({ channelStore: deps.channelStore, topicStore: deps.topicStore }),
64878
+ ...buildMasterHandlers({
64879
+ authorityTablePath: import_node_path66.default.join(deps.masterDataDir, "master-authority.json")
64880
+ }),
64881
+ ...buildLineHandlers({
64882
+ dispatchStore: deps.dispatchStore,
64883
+ getSessionFile: deps.getWorkerSessionFile,
64884
+ // transcript 定位/读取复用 history:* 那个同一个 reader,不另起一份路径规则
64885
+ history: deps.history
64886
+ }),
64887
+ ...buildTopicHandlers({
64888
+ topicStore: deps.topicStore,
64889
+ channelStore: deps.channelStore,
64890
+ dispatchStore: deps.dispatchStore,
64891
+ createMasterSession: deps.createMasterSession
64892
+ }),
63919
64893
  ...buildShiftHandlers({
63920
64894
  shiftStore: deps.shiftStore,
63921
64895
  ownerIdProvider: deps.shiftOwnerIdProvider,
@@ -64343,8 +65317,8 @@ async function dispatchRpc(method, frame, client, ctx, deps) {
64343
65317
 
64344
65318
  // src/extension/runtime.ts
64345
65319
  var import_node_child_process21 = require("child_process");
64346
- var import_node_path65 = __toESM(require("path"), 1);
64347
- var import_promises14 = require("timers/promises");
65320
+ var import_node_path67 = __toESM(require("path"), 1);
65321
+ var import_promises16 = require("timers/promises");
64348
65322
 
64349
65323
  // src/extension/port-allocator.ts
64350
65324
  var import_node_net2 = __toESM(require("net"), 1);
@@ -64444,7 +65418,7 @@ var Runtime = class {
64444
65418
  /\$CLAWOS_EXT_PORT/g,
64445
65419
  String(port)
64446
65420
  );
64447
- const dir = import_node_path65.default.join(this.root, extId);
65421
+ const dir = import_node_path67.default.join(this.root, extId);
64448
65422
  const env = {
64449
65423
  ...process.env,
64450
65424
  CLAWOS_EXT_PORT: String(port),
@@ -64520,7 +65494,7 @@ ${handle.stderrTail}`
64520
65494
  if (res.ok) return;
64521
65495
  } catch {
64522
65496
  }
64523
- await (0, import_promises14.setTimeout)(this.healthzIntervalMs);
65497
+ await (0, import_promises16.setTimeout)(this.healthzIntervalMs);
64524
65498
  }
64525
65499
  throw new RuntimeError(
64526
65500
  "HEALTHZ_FAILED",
@@ -64545,7 +65519,7 @@ ${handle.stderrTail}`
64545
65519
  };
64546
65520
  killGroup("SIGTERM");
64547
65521
  const exited = new Promise((resolve9) => child.once("exit", () => resolve9()));
64548
- const timed = (0, import_promises14.setTimeout)(5e3).then(() => "timeout");
65522
+ const timed = (0, import_promises16.setTimeout)(5e3).then(() => "timeout");
64549
65523
  const winner = await Promise.race([exited.then(() => "exited"), timed]);
64550
65524
  if (winner === "timeout" && child.exitCode === null) {
64551
65525
  killGroup("SIGKILL");
@@ -64555,8 +65529,8 @@ ${handle.stderrTail}`
64555
65529
  };
64556
65530
 
64557
65531
  // src/extension/published-channels.ts
64558
- var import_promises15 = __toESM(require("fs/promises"), 1);
64559
- var import_node_path66 = __toESM(require("path"), 1);
65532
+ var import_promises17 = __toESM(require("fs/promises"), 1);
65533
+ var import_node_path68 = __toESM(require("path"), 1);
64560
65534
  init_zod();
64561
65535
  var PublishedChannelsError = class extends Error {
64562
65536
  constructor(code, message) {
@@ -64586,7 +65560,7 @@ var PublishedChannelStore = class {
64586
65560
  this.channels.clear();
64587
65561
  let raw;
64588
65562
  try {
64589
- raw = await import_promises15.default.readFile(this.filePath, "utf8");
65563
+ raw = await import_promises17.default.readFile(this.filePath, "utf8");
64590
65564
  } catch (e) {
64591
65565
  if (e.code === "ENOENT") {
64592
65566
  this.loaded = true;
@@ -64655,15 +65629,15 @@ var PublishedChannelStore = class {
64655
65629
  )
64656
65630
  };
64657
65631
  const tmp = `${this.filePath}.tmp`;
64658
- await import_promises15.default.mkdir(import_node_path66.default.dirname(this.filePath), { recursive: true });
64659
- await import_promises15.default.writeFile(tmp, JSON.stringify(data, null, 2), { mode: 384 });
64660
- await import_promises15.default.rename(tmp, this.filePath);
65632
+ await import_promises17.default.mkdir(import_node_path68.default.dirname(this.filePath), { recursive: true });
65633
+ await import_promises17.default.writeFile(tmp, JSON.stringify(data, null, 2), { mode: 384 });
65634
+ await import_promises17.default.rename(tmp, this.filePath);
64661
65635
  }
64662
65636
  };
64663
65637
 
64664
65638
  // src/extension/bundle-cache.ts
64665
- var import_promises16 = __toESM(require("fs/promises"), 1);
64666
- var import_node_path67 = __toESM(require("path"), 1);
65639
+ var import_promises18 = __toESM(require("fs/promises"), 1);
65640
+ var import_node_path69 = __toESM(require("path"), 1);
64667
65641
  var BundleCache = class {
64668
65642
  constructor(rootDir) {
64669
65643
  this.rootDir = rootDir;
@@ -64671,17 +65645,17 @@ var BundleCache = class {
64671
65645
  rootDir;
64672
65646
  /** Atomic write: stage tmp → rename. Caller passes the hex sha256. */
64673
65647
  async write(snapshotHash, buffer) {
64674
- await import_promises16.default.mkdir(this.rootDir, { recursive: true });
64675
- const file = import_node_path67.default.join(this.rootDir, `${snapshotHash}.zip`);
65648
+ await import_promises18.default.mkdir(this.rootDir, { recursive: true });
65649
+ const file = import_node_path69.default.join(this.rootDir, `${snapshotHash}.zip`);
64676
65650
  const tmp = `${file}.tmp`;
64677
- await import_promises16.default.writeFile(tmp, buffer, { mode: 384 });
64678
- await import_promises16.default.rename(tmp, file);
65651
+ await import_promises18.default.writeFile(tmp, buffer, { mode: 384 });
65652
+ await import_promises18.default.rename(tmp, file);
64679
65653
  }
64680
65654
  /** Returns the bundle bytes, or null when the file doesn't exist. */
64681
65655
  async read(snapshotHash) {
64682
- const file = import_node_path67.default.join(this.rootDir, `${snapshotHash}.zip`);
65656
+ const file = import_node_path69.default.join(this.rootDir, `${snapshotHash}.zip`);
64683
65657
  try {
64684
- return await import_promises16.default.readFile(file);
65658
+ return await import_promises18.default.readFile(file);
64685
65659
  } catch (e) {
64686
65660
  if (e.code === "ENOENT") return null;
64687
65661
  throw e;
@@ -64689,8 +65663,8 @@ var BundleCache = class {
64689
65663
  }
64690
65664
  /** Idempotent — missing file is not an error. */
64691
65665
  async delete(snapshotHash) {
64692
- const file = import_node_path67.default.join(this.rootDir, `${snapshotHash}.zip`);
64693
- await import_promises16.default.rm(file, { force: true });
65666
+ const file = import_node_path69.default.join(this.rootDir, `${snapshotHash}.zip`);
65667
+ await import_promises18.default.rm(file, { force: true });
64694
65668
  }
64695
65669
  };
64696
65670
 
@@ -64712,24 +65686,24 @@ async function startDaemon(config) {
64712
65686
  sampling: logShippingCfg.sampling,
64713
65687
  homeDir: import_node_os24.default.homedir()
64714
65688
  });
64715
- const logDir = import_node_path68.default.join(config.dataDir, "log");
65689
+ const logDir = import_node_path70.default.join(config.dataDir, "log");
64716
65690
  import_node_fs52.default.mkdirSync(logDir, { recursive: true });
64717
65691
  const logger = createLogger({
64718
65692
  level: config.logLevel,
64719
- file: import_node_path68.default.join(logDir, "clawd.log"),
65693
+ file: import_node_path70.default.join(logDir, "clawd.log"),
64720
65694
  logClient
64721
65695
  });
64722
65696
  logger.info("starting clawd", { version, config: { port: config.port, host: config.host, dataDir: config.dataDir } });
64723
65697
  const screenIdleProbeLogger = createFileOnlyLogger({
64724
- file: import_node_path68.default.join(logDir, "screen-idle-probe.log"),
65698
+ file: import_node_path70.default.join(logDir, "screen-idle-probe.log"),
64725
65699
  level: "debug"
64726
65700
  });
64727
65701
  logger.info("screen-idle probe logger enabled", {
64728
- file: import_node_path68.default.join(logDir, "screen-idle-probe.log")
65702
+ file: import_node_path70.default.join(logDir, "screen-idle-probe.log")
64729
65703
  });
64730
65704
  const CAP_TARGETS = [
64731
- import_node_path68.default.join(logDir, "clawd.log"),
64732
- import_node_path68.default.join(logDir, "screen-idle-probe.log")
65705
+ import_node_path70.default.join(logDir, "clawd.log"),
65706
+ import_node_path70.default.join(logDir, "screen-idle-probe.log")
64733
65707
  ];
64734
65708
  const LOG_CAP_MAX_BYTES = 10 * 1024 * 1024;
64735
65709
  const LOG_CAP_KEEP_BYTES = 5 * 1024 * 1024;
@@ -64825,8 +65799,8 @@ async function startDaemon(config) {
64825
65799
  const agents = new AgentsScanner();
64826
65800
  const history = new ClaudeHistoryReader();
64827
65801
  let transport = null;
64828
- const personaStore = new PersonaStore(import_node_path68.default.join(config.dataDir, "personas"));
64829
- const usersRoot = import_node_path68.default.join(config.dataDir, "users");
65802
+ const personaStore = new PersonaStore(import_node_path70.default.join(config.dataDir, "personas"));
65803
+ const usersRoot = import_node_path70.default.join(config.dataDir, "users");
64830
65804
  const defaultsRoot = findDefaultsRoot(logger);
64831
65805
  if (defaultsRoot) {
64832
65806
  seedDefaultPersonas({ store: personaStore, defaultsRoot, logger });
@@ -64849,7 +65823,7 @@ async function startDaemon(config) {
64849
65823
  retirePersonaBuilder({ store: personaStore, logger });
64850
65824
  const groupFileStore = new GroupFileStore({ dataDir: config.dataDir, logger });
64851
65825
  const dispatchStore = createDispatchStore({
64852
- filePath: import_node_path68.default.join(config.dataDir, "dispatch.json"),
65826
+ filePath: import_node_path70.default.join(config.dataDir, "dispatch.json"),
64853
65827
  now: () => Date.now(),
64854
65828
  onFlushError: (err) => logger.error("dispatch store flush failed", {
64855
65829
  reason: err instanceof Error ? err.message : String(err)
@@ -64857,6 +65831,24 @@ async function startDaemon(config) {
64857
65831
  });
64858
65832
  await dispatchStore.load();
64859
65833
  dispatchStore.markInterruptedAsFailed((/* @__PURE__ */ new Date()).toISOString());
65834
+ const channelStore = createChannelStore({
65835
+ filePath: import_node_path70.default.join(config.dataDir, "channels.json"),
65836
+ now: () => Date.now(),
65837
+ genId: () => `ch-${v4_default().slice(0, 8)}`,
65838
+ onFlushError: (err) => logger.error("channel store flush failed", {
65839
+ reason: err instanceof Error ? err.message : String(err)
65840
+ })
65841
+ });
65842
+ await channelStore.load();
65843
+ const topicStore = createTopicStore({
65844
+ filePath: import_node_path70.default.join(config.dataDir, "topics.json"),
65845
+ now: () => Date.now(),
65846
+ genId: () => `tp-${v4_default().slice(0, 8)}`,
65847
+ onFlushError: (err) => logger.error("topic store flush failed", {
65848
+ reason: err instanceof Error ? err.message : String(err)
65849
+ })
65850
+ });
65851
+ await topicStore.load();
64860
65852
  let deliverPendingRef;
64861
65853
  const personaDispatchManager = new PersonaDispatchManager({
64862
65854
  genId: () => v4_default(),
@@ -64864,18 +65856,18 @@ async function startDaemon(config) {
64864
65856
  now: () => Date.now(),
64865
65857
  onCompleted: (sid) => deliverPendingRef?.(sid)
64866
65858
  });
64867
- const here = typeof __dirname === "string" ? __dirname : import_node_path68.default.dirname((0, import_node_url4.fileURLToPath)(import_meta7.url));
65859
+ const here = typeof __dirname === "string" ? __dirname : import_node_path70.default.dirname((0, import_node_url4.fileURLToPath)(import_meta7.url));
64868
65860
  const mcpConfigs = [];
64869
65861
  const mcpDaemonUrl = `http://127.0.0.1:${config.port}`;
64870
65862
  const dispatchServerCandidates = [
64871
- import_node_path68.default.join(here, "dispatch", "mcp-server.cjs"),
65863
+ import_node_path70.default.join(here, "dispatch", "mcp-server.cjs"),
64872
65864
  // 生产 dist/index → dist/dispatch/mcp-server.cjs
64873
- import_node_path68.default.join(here, "..", "dist", "dispatch", "mcp-server.cjs")
65865
+ import_node_path70.default.join(here, "..", "dist", "dispatch", "mcp-server.cjs")
64874
65866
  // dev tsx src/index → ../dist/dispatch/mcp-server.cjs
64875
65867
  ];
64876
65868
  const dispatchServerScriptPath = dispatchServerCandidates.find((p2) => import_node_fs52.default.existsSync(p2));
64877
65869
  if (dispatchServerScriptPath) {
64878
- const dispatchLogPath = import_node_path68.default.join(logDir, "dispatch-mcp-server.log");
65870
+ const dispatchLogPath = import_node_path70.default.join(logDir, "dispatch-mcp-server.log");
64879
65871
  const dispatchCfgPath = writeDispatchMcpConfig({
64880
65872
  dataDir: config.dataDir,
64881
65873
  serverScriptPath: dispatchServerScriptPath,
@@ -64895,12 +65887,12 @@ async function startDaemon(config) {
64895
65887
  });
64896
65888
  }
64897
65889
  const ticketServerCandidates = [
64898
- import_node_path68.default.join(here, "ticket", "mcp-server.cjs"),
64899
- import_node_path68.default.join(here, "..", "dist", "ticket", "mcp-server.cjs")
65890
+ import_node_path70.default.join(here, "ticket", "mcp-server.cjs"),
65891
+ import_node_path70.default.join(here, "..", "dist", "ticket", "mcp-server.cjs")
64900
65892
  ];
64901
65893
  const ticketServerScriptPath = ticketServerCandidates.find((p2) => import_node_fs52.default.existsSync(p2));
64902
65894
  if (ticketServerScriptPath) {
64903
- const ticketLogPath = import_node_path68.default.join(logDir, "ticket-mcp-server.log");
65895
+ const ticketLogPath = import_node_path70.default.join(logDir, "ticket-mcp-server.log");
64904
65896
  const ticketCfgPath = writeTicketMcpConfig({
64905
65897
  dataDir: config.dataDir,
64906
65898
  serverScriptPath: ticketServerScriptPath,
@@ -64922,12 +65914,12 @@ async function startDaemon(config) {
64922
65914
  });
64923
65915
  }
64924
65916
  const shiftServerCandidates = [
64925
- import_node_path68.default.join(here, "shift", "mcp-server.cjs"),
64926
- import_node_path68.default.join(here, "..", "dist", "shift", "mcp-server.cjs")
65917
+ import_node_path70.default.join(here, "shift", "mcp-server.cjs"),
65918
+ import_node_path70.default.join(here, "..", "dist", "shift", "mcp-server.cjs")
64927
65919
  ];
64928
65920
  const shiftServerScriptPath = shiftServerCandidates.find((p2) => import_node_fs52.default.existsSync(p2));
64929
65921
  if (shiftServerScriptPath) {
64930
- const shiftLogPath = import_node_path68.default.join(logDir, "shift-mcp-server.log");
65922
+ const shiftLogPath = import_node_path70.default.join(logDir, "shift-mcp-server.log");
64931
65923
  const shiftCfgPath = await writeShiftMcpConfig({
64932
65924
  dataDir: config.dataDir,
64933
65925
  serverScriptPath: shiftServerScriptPath,
@@ -64948,12 +65940,12 @@ async function startDaemon(config) {
64948
65940
  );
64949
65941
  }
64950
65942
  const inboxServerCandidates = [
64951
- import_node_path68.default.join(here, "inbox", "mcp-server.cjs"),
64952
- import_node_path68.default.join(here, "..", "dist", "inbox", "mcp-server.cjs")
65943
+ import_node_path70.default.join(here, "inbox", "mcp-server.cjs"),
65944
+ import_node_path70.default.join(here, "..", "dist", "inbox", "mcp-server.cjs")
64953
65945
  ];
64954
65946
  const inboxServerScriptPath = inboxServerCandidates.find((p2) => import_node_fs52.default.existsSync(p2));
64955
65947
  if (inboxServerScriptPath) {
64956
- const inboxLogPath = import_node_path68.default.join(logDir, "inbox-mcp-server.log");
65948
+ const inboxLogPath = import_node_path70.default.join(logDir, "inbox-mcp-server.log");
64957
65949
  const inboxCfgPath = await writeInboxMcpConfig({
64958
65950
  dataDir: config.dataDir,
64959
65951
  serverScriptPath: inboxServerScriptPath,
@@ -64974,8 +65966,8 @@ async function startDaemon(config) {
64974
65966
  );
64975
65967
  }
64976
65968
  const peerOpsServerCandidates = [
64977
- import_node_path68.default.join(here, "peer-ops", "mcp-server.cjs"),
64978
- import_node_path68.default.join(here, "..", "dist", "peer-ops", "mcp-server.cjs")
65969
+ import_node_path70.default.join(here, "peer-ops", "mcp-server.cjs"),
65970
+ import_node_path70.default.join(here, "..", "dist", "peer-ops", "mcp-server.cjs")
64979
65971
  ];
64980
65972
  const peerOpsServerScriptPath = peerOpsServerCandidates.find((p2) => import_node_fs52.default.existsSync(p2));
64981
65973
  if (peerOpsServerScriptPath) {
@@ -64997,12 +65989,12 @@ async function startDaemon(config) {
64997
65989
  );
64998
65990
  }
64999
65991
  const rpcToolServerCandidates = [
65000
- import_node_path68.default.join(here, "rpc-tool", "mcp-server.cjs"),
65001
- import_node_path68.default.join(here, "..", "dist", "rpc-tool", "mcp-server.cjs")
65992
+ import_node_path70.default.join(here, "rpc-tool", "mcp-server.cjs"),
65993
+ import_node_path70.default.join(here, "..", "dist", "rpc-tool", "mcp-server.cjs")
65002
65994
  ];
65003
65995
  const rpcToolServerScriptPath = rpcToolServerCandidates.find((p2) => import_node_fs52.default.existsSync(p2));
65004
65996
  if (rpcToolServerScriptPath) {
65005
- const rpcToolLogPath = import_node_path68.default.join(logDir, "rpc-tool-mcp-server.log");
65997
+ const rpcToolLogPath = import_node_path70.default.join(logDir, "rpc-tool-mcp-server.log");
65006
65998
  const rpcToolCfgPath = writeRpcToolMcpConfig({
65007
65999
  dataDir: config.dataDir,
65008
66000
  serverScriptPath: rpcToolServerScriptPath,
@@ -65023,7 +66015,7 @@ async function startDaemon(config) {
65023
66015
  );
65024
66016
  }
65025
66017
  const shiftStore = createShiftStore({
65026
- filePath: import_node_path68.default.join(config.dataDir, "shift.json"),
66018
+ filePath: import_node_path70.default.join(config.dataDir, "shift.json"),
65027
66019
  ownerIdProvider: () => ownerPrincipalId,
65028
66020
  now: () => Date.now()
65029
66021
  });
@@ -65045,7 +66037,7 @@ async function startDaemon(config) {
65045
66037
  getAdapter,
65046
66038
  historyReader: history,
65047
66039
  dataDir: config.dataDir,
65048
- personaRoot: import_node_path68.default.join(config.dataDir, "personas"),
66040
+ personaRoot: import_node_path70.default.join(config.dataDir, "personas"),
65049
66041
  // dispatch / shift 自动化 spawn 按 persona 绑定 tool 起 B session(codex persona 用 codex)。
65050
66042
  // personaRegistry 声明在 manager 之后(同作用域 const,调用时机在启动完成后,无 TDZ 风险)。
65051
66043
  getPersonaDefaults: (personaId2) => personaRegistry.get(personaId2),
@@ -65099,7 +66091,7 @@ async function startDaemon(config) {
65099
66091
  // 文件可能 agent 写完又被自己删(罕见),用 size=0 / fallback mime 兜底。
65100
66092
  attachmentGroup: {
65101
66093
  onFileEdit: (input) => {
65102
- const absPath = import_node_path68.default.isAbsolute(input.relPath) ? input.relPath : import_node_path68.default.join(input.cwd, input.relPath);
66094
+ const absPath = import_node_path70.default.isAbsolute(input.relPath) ? input.relPath : import_node_path70.default.join(input.cwd, input.relPath);
65103
66095
  let size = 0;
65104
66096
  try {
65105
66097
  size = import_node_fs52.default.statSync(absPath).size;
@@ -65132,9 +66124,9 @@ async function startDaemon(config) {
65132
66124
  logger.info("projects.seed-dropped", { dropped: droppedSeed.dropped, backup: droppedSeed.backup });
65133
66125
  }
65134
66126
  const projectsStore = new ProjectsStore({
65135
- file: import_node_path68.default.join(config.dataDir, "projects.json"),
66127
+ file: import_node_path70.default.join(config.dataDir, "projects.json"),
65136
66128
  clawdDir: config.dataDir,
65137
- codexStateFile: import_node_path68.default.join(import_node_os24.default.homedir(), ".codex", ".codex-global-state.json"),
66129
+ codexStateFile: import_node_path70.default.join(import_node_os24.default.homedir(), ".codex", ".codex-global-state.json"),
65138
66130
  logger
65139
66131
  });
65140
66132
  const observer = new SessionObserver({
@@ -65194,12 +66186,12 @@ async function startDaemon(config) {
65194
66186
  screenIdleProbeLogger
65195
66187
  }) : new ClaudeAdapter({ logger, historyReader: new ClaudeHistoryReader() });
65196
66188
  registerAdapter("claude", claudeAdapter);
65197
- const codexIsolatedHome = import_node_path68.default.join(config.dataDir, "codex-home");
66189
+ const codexIsolatedHome = import_node_path70.default.join(config.dataDir, "codex-home");
65198
66190
  try {
65199
66191
  import_node_fs52.default.mkdirSync(codexIsolatedHome, { recursive: true });
65200
66192
  for (const f of ["auth.json", "config.toml"]) {
65201
- const link = import_node_path68.default.join(codexIsolatedHome, f);
65202
- const target = import_node_path68.default.join(import_node_os24.default.homedir(), ".codex", f);
66193
+ const link = import_node_path70.default.join(codexIsolatedHome, f);
66194
+ const target = import_node_path70.default.join(import_node_os24.default.homedir(), ".codex", f);
65203
66195
  if (!import_node_fs52.default.existsSync(link) && import_node_fs52.default.existsSync(target)) import_node_fs52.default.symlinkSync(target, link);
65204
66196
  }
65205
66197
  } catch (err) {
@@ -65213,11 +66205,11 @@ async function startDaemon(config) {
65213
66205
  // projectPaths 每次调用现取 —— 用户新建 project 后立即生效,不用重启 daemon
65214
66206
  codexHomeFor: (cwd) => resolveCodexHome({
65215
66207
  cwd,
65216
- personaRoot: import_node_path68.default.join(config.dataDir, "personas"),
66208
+ personaRoot: import_node_path70.default.join(config.dataDir, "personas"),
65217
66209
  // 必须用 listMerged(Codex registry ∪ 叠加层):只用本地 list() 的话,用户在
65218
66210
  // Codex 里建的 project 不在表里,在它下面开的会话会被误判成隔离 → Codex 侧看不到
65219
66211
  projectPaths: projectsStore.listMerged().map((x) => x.path),
65220
- sharedHome: import_node_path68.default.join(import_node_os24.default.homedir(), ".codex"),
66212
+ sharedHome: import_node_path70.default.join(import_node_os24.default.homedir(), ".codex"),
65221
66213
  isolatedHome: codexIsolatedHome
65222
66214
  })
65223
66215
  })
@@ -65359,6 +66351,29 @@ async function startDaemon(config) {
65359
66351
  });
65360
66352
  const handlers = {
65361
66353
  ...buildMethodHandlers({
66354
+ channelStore,
66355
+ topicStore,
66356
+ dispatchStore,
66357
+ // line:digest 用:worker session 的 cwd / toolSessionId(transcript 定位的输入)
66358
+ getWorkerSessionFile: (sessionId) => {
66359
+ const f = manager.findOwnedSession(sessionId);
66360
+ return f ? { cwd: f.cwd, ...f.toolSessionId ? { toolSessionId: f.toolSessionId } : {} } : void 0;
66361
+ },
66362
+ masterDataDir: config.dataDir,
66363
+ // 建话题即起该话题的 Master session(1:1)。走与 session:create 同一条 manager.create
66364
+ // 链路,不复制会话创建逻辑;creatorPrincipalId 用本机 deviceId(owner ctx 的 principal.id)。
66365
+ createMasterSession: ({ label }) => {
66366
+ const { response } = manager.create(
66367
+ { ownerPersonaId: "persona-master", label },
66368
+ authFile.deviceId,
66369
+ ownerDisplayName
66370
+ );
66371
+ const sessionId = response.sessionId;
66372
+ if (typeof sessionId !== "string" || !sessionId) {
66373
+ throw new Error("createMasterSession: manager.create \u6CA1\u6709\u8FD4\u56DE sessionId");
66374
+ }
66375
+ return { sessionId };
66376
+ },
65362
66377
  // meta:methods 自省:飞书 gate getter + 最终 handler 表(下面还会 spread 进
65363
66378
  // dispatch / shift-internal 两组,只看 buildMethodHandlers 内部表会误报死方法)
65364
66379
  feishuActive: () => feishuActive,
@@ -65408,11 +66423,11 @@ async function startDaemon(config) {
65408
66423
  // 'persona/<pid>/owner',default 走 'default'。
65409
66424
  getSessionScope: (sid) => manager.findOwnedSessionScope(sid),
65410
66425
  // guest path guard:candidate 必须在 personaRoot 子树或调用者自己的 user-dir 下
65411
- personaRoot: import_node_path68.default.join(config.dataDir, "personas"),
66426
+ personaRoot: import_node_path70.default.join(config.dataDir, "personas"),
65412
66427
  usersRoot
65413
66428
  },
65414
66429
  // workspace/git/history/skills/agents handler 共用的 guest path guard 锚点
65415
- personaRoot: import_node_path68.default.join(config.dataDir, "personas"),
66430
+ personaRoot: import_node_path70.default.join(config.dataDir, "personas"),
65416
66431
  // v2 多人 persona 隔离:handler 派生 guest user-dir 放行
65417
66432
  usersRoot,
65418
66433
  // v2 Phase 6: whoami handler 装在 owner principal.displayName + persona 解析
@@ -65545,11 +66560,11 @@ async function startDaemon(config) {
65545
66560
  // 发布上线脚手架化 (spec 2026-06-03 §5.2):
65546
66561
  // appBuilderPersonaRoot 用于拼 publish.sh 绝对路径(persona-app-builder 安装在
65547
66562
  // dataDir/personas/persona-app-builder 之下,extension-kit/scripts/publish.sh 是相对路径)。
65548
- appBuilderPersonaRoot: import_node_path68.default.join(config.dataDir, "personas", "persona-app-builder"),
66563
+ appBuilderPersonaRoot: import_node_path70.default.join(config.dataDir, "personas", "persona-app-builder"),
65549
66564
  // 共享 deploy-kit 根:scaffold/publish 脚本骨架 + 阿里云凭证单一真源。
65550
- deployKitRoot: import_node_path68.default.join(config.dataDir, "deploy-kit"),
66565
+ deployKitRoot: import_node_path70.default.join(config.dataDir, "deploy-kit"),
65551
66566
  // scaffold/publish 按当前 session 的 persona 解析其安装根,让每个 persona 用自己的模板/注入配置。
65552
- resolvePersonaRoot: (personaId2) => import_node_path68.default.join(config.dataDir, "personas", personaId2),
66567
+ resolvePersonaRoot: (personaId2) => import_node_path70.default.join(config.dataDir, "personas", personaId2),
65553
66568
  // 发布上线脚手架化 (spec 2026-06-03 §5.2.2):
65554
66569
  // 复用 SessionManagerDeps.broadcastFrame 同款 dispatch 逻辑 —— runner 调 manager.send
65555
66570
  // 取回 broadcast 帧后逐帧 push 到 transport,跟 manager 自身的 deps 一致。
@@ -65654,7 +66669,7 @@ async function startDaemon(config) {
65654
66669
  });
65655
66670
  shiftScheduler.start();
65656
66671
  logger.info("shift scheduler started", { shiftCount: shiftStore.list().length });
65657
- const larkBotStore = createLarkBotBindingStore(import_node_path68.default.join(config.dataDir, "personas"));
66672
+ const larkBotStore = createLarkBotBindingStore(import_node_path70.default.join(config.dataDir, "personas"));
65658
66673
  const larkBotCloud = createLarkBotCloudClient({
65659
66674
  api: config.clawosApi ?? DEFAULT_CLAWOS_API,
65660
66675
  apiKey: resolveClawdTicketsApiKey(),
@@ -65668,7 +66683,7 @@ async function startDaemon(config) {
65668
66683
  store: larkBotStore,
65669
66684
  fetchResource: (appId, messageId, fileKey, maxBytes, type) => larkBotCloud.fetchResource(appId, messageId, fileKey, maxBytes, type),
65670
66685
  // 图片落 guest userWorkDir 下(capId = lark-<chat_id>):CC 经 --add-dir + allowRead carve 可 Read
65671
- mediaRootFor: (chatId) => import_node_path68.default.join(deriveUserWorkDir(`lark-${chatId}`, usersRoot), "lark-media"),
66686
+ mediaRootFor: (chatId) => import_node_path70.default.join(deriveUserWorkDir(`lark-${chatId}`, usersRoot), "lark-media"),
65672
66687
  isShuttingDown: () => larkDraining,
65673
66688
  logger: {
65674
66689
  warn: (msg) => logger.warn(msg),
@@ -65973,8 +66988,8 @@ async function startDaemon(config) {
65973
66988
  const lines = [
65974
66989
  `Tunnel: ${r.url}`,
65975
66990
  ...resolvedAuthToken ? [`Connect: ${connectUrl}`] : [],
65976
- `Frpc config: ${import_node_path68.default.join(config.dataDir, "frpc.toml")}`,
65977
- `Frpc log: ${import_node_path68.default.join(logDir, "frpc.log")}`
66991
+ `Frpc config: ${import_node_path70.default.join(config.dataDir, "frpc.toml")}`,
66992
+ `Frpc log: ${import_node_path70.default.join(logDir, "frpc.log")}`
65978
66993
  ];
65979
66994
  const width2 = Math.max(...lines.map((l) => l.length));
65980
66995
  const bar = "\u2550".repeat(width2 + 4);
@@ -65987,7 +67002,7 @@ ${bar}
65987
67002
 
65988
67003
  `);
65989
67004
  try {
65990
- const connectPath = import_node_path68.default.join(config.dataDir, "connect.txt");
67005
+ const connectPath = import_node_path70.default.join(config.dataDir, "connect.txt");
65991
67006
  import_node_fs52.default.writeFileSync(connectPath, lines.join("\n") + "\n", { mode: 384 });
65992
67007
  } catch {
65993
67008
  }
@@ -66051,6 +67066,17 @@ ${bar}
66051
67066
  await tunnelMgr.stop();
66052
67067
  }
66053
67068
  await wss.stop();
67069
+ for (const [name, store2] of [
67070
+ ["channels", channelStore],
67071
+ ["topics", topicStore]
67072
+ ]) {
67073
+ await store2.flushNow().catch((err) => {
67074
+ logger.warn("shutdown.store-flush-failed", {
67075
+ store: name,
67076
+ error: err instanceof Error ? err.message : String(err)
67077
+ });
67078
+ });
67079
+ }
66054
67080
  stateMgr.delete();
66055
67081
  if (logClient) await logClient.dispose();
66056
67082
  };
@@ -66063,7 +67089,7 @@ ${bar}
66063
67089
  };
66064
67090
  }
66065
67091
  function migrateDropPersonsDir(dataDir) {
66066
- const dir = import_node_path68.default.join(dataDir, "persons");
67092
+ const dir = import_node_path70.default.join(dataDir, "persons");
66067
67093
  try {
66068
67094
  import_node_fs52.default.rmSync(dir, { recursive: true, force: true });
66069
67095
  } catch {