@fangyb/ahchat-bridge 0.1.18 → 0.1.20

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/index.js CHANGED
@@ -3746,8 +3746,8 @@ function resolveAgentConfigDir(dataDir) {
3746
3746
  }
3747
3747
  return newPath;
3748
3748
  }
3749
- function loadBridgeConfig() {
3750
- const dataDir = readEnvString(
3749
+ function loadBridgeConfig(opts) {
3750
+ const dataDir = opts?.dataDir ?? readEnvString(
3751
3751
  "AHCHAT_DATA_DIR",
3752
3752
  path.join(os.homedir(), ".ahchat")
3753
3753
  );
@@ -5119,15 +5119,15 @@ var SMITH_SYSTEM_PROMPT = `\u4F60\u662F\u7279\u5DE5\u53F2\u5BC6\u65AF\uFF08Agent
5119
5119
  - **\u4E3A\u6BCF\u4E2A Agent \u9009\u62E9\u5408\u9002\u7684\u80FD\u529B\u6863\u4F4D\uFF08tier \u53C2\u6570\uFF09**
5120
5120
  - **\u4E00\u6B21\u6027**\u8C03 create_group\uFF0C\u53C2\u6570\uFF1A
5121
5121
  - name: \u7FA4\u540D
5122
- - member_ids: [agt_usr_self, <Leader>, <\u5176\u4ED6\u6210\u5458>, ...] // **\u4E0D\u5305\u542B\u4F60\u81EA\u5DF1**
5122
+ - member_ids: [<\u8BF7\u6C42\u8005\u7684\u4EBA\u7C7B id>, <Leader>, <\u5176\u4ED6\u6210\u5458>, ...] // **\u4E0D\u5305\u542B\u4F60\u81EA\u5DF1**\uFF1B\u8BF7\u6C42\u8005\u7684\u4EBA\u7C7B id \u5FC5\u987B\u6765\u81EA list_contacts() \u91CC\u5E26\u300C(\u4EBA\u7C7B)\u300D\u6807\u8BB0\u7684\u90A3\u4E00\u6761\uFF0C\u591A\u7528\u6237\u73AF\u5883\u4E0D\u662F agt_usr_self
5123
5123
  - join_as_creator: false
5124
5124
  - initial_message: \u4EA4\u73ED\u8BED\uFF08\u52A1\u5FC5\u5199\u6E05\u695A\u300C\u5728\u7FA4\u91CC\u300D\u300C\u4E0D\u8981\u79C1\u804A\u300D\uFF09\u3002\u6709 Leader \u65F6\u793A\u4F8B\uFF1A
5125
- \u300C\u7FA4\u5DF2\u6210\u7ACB\u3002\u7FA4\u4E3B\u662F\u7528\u6237\uFF08@agt_usr_self\uFF09\uFF0C@<Leader \u540D> \u662F\u56E2\u961F\u8D1F\u8D23\u4EBA\u3002
5125
+ \u300C\u7FA4\u5DF2\u6210\u7ACB\u3002\u7FA4\u4E3B\u662F\u7528\u6237\uFF08@<\u8BF7\u6C42\u8005\u7684\u4EBA\u7C7B id>\uFF09\uFF0C@<Leader \u540D> \u662F\u56E2\u961F\u8D1F\u8D23\u4EBA\u3002
5126
5126
 
5127
5127
  @<Leader \u540D>\uFF1A\u8BF7\u4F60**\u76F4\u63A5\u5728\u672C\u7FA4\u56DE\u590D**\uFF0C\u5411\u7528\u6237\u505A\u56E2\u961F\u4ECB\u7ECD\u2014\u2014\u56E2\u961F\u603B\u4EBA\u6570\u3001\u5404\u6210\u5458\u804C\u80FD\u5206\u5DE5\uFF0C\u5E76\u8BE2\u95EE\u60F3\u4ECE\u54EA\u91CC\u5F00\u59CB\u3002**\u4E0D\u8981 neural_send \u53BB\u79C1\u804A\u7528\u6237**\uFF0C\u8FD9\u662F\u516C\u5F00\u7684\u7FA4\u4ECB\u7ECD\uFF0C\u6240\u6709\u4EBA\u90FD\u770B\u7740\u3002
5128
5128
 
5129
5129
  \u5176\u4ED6\u6210\u5458\uFF1A\u4FDD\u6301\u5B89\u9759\u89C2\u671B\uFF0C\u7B49 Leader \u5206\u6D3E\u4EFB\u52A1\u518D\u53D1\u8A00\u3002**\u4E0D\u8981\u67E5\u804A\u5929\u8BB0\u5F55\u3001\u4E0D\u8981\u8FFD\u95EE"\u6D88\u606F\u9001\u5230\u6CA1"\u4E4B\u7C7B\u7684\u5143\u8BDD\u9898**\u2014\u2014\u4FDD\u6301\u514B\u5236\u3002\u300D
5130
- \u5E73\u7EA7\u7FA4\uFF08\u65E0 Leader\uFF09\u793A\u4F8B\uFF1A\u300C\u7FA4\u5DF2\u6210\u7ACB\u3002\u7FA4\u4E3B\u662F\u7528\u6237\uFF08@agt_usr_self\uFF09\u3002\u8BF7\u5927\u5BB6\u5728\u672C\u7FA4\u91CC\u5404\u81EA\u7528\u4E00\u53E5\u8BDD\u505A\u81EA\u6211\u4ECB\u7ECD\uFF08**\u4E0D\u8981\u79C1\u804A\u7528\u6237**\uFF09\u3002\u4ECB\u7ECD\u5B8C\u4FDD\u6301\u5B89\u9759\uFF0C\u7B49\u7528\u6237\u5F00\u53E3\u8BF4\u60F3\u505A\u4EC0\u4E48\u518D\u5206\u5DE5\u3002\u300D
5130
+ \u5E73\u7EA7\u7FA4\uFF08\u65E0 Leader\uFF09\u793A\u4F8B\uFF1A\u300C\u7FA4\u5DF2\u6210\u7ACB\u3002\u7FA4\u4E3B\u662F\u7528\u6237\uFF08@<\u8BF7\u6C42\u8005\u7684\u4EBA\u7C7B id>\uFF09\u3002\u8BF7\u5927\u5BB6\u5728\u672C\u7FA4\u91CC\u5404\u81EA\u7528\u4E00\u53E5\u8BDD\u505A\u81EA\u6211\u4ECB\u7ECD\uFF08**\u4E0D\u8981\u79C1\u804A\u7528\u6237**\uFF09\u3002\u4ECB\u7ECD\u5B8C\u4FDD\u6301\u5B89\u9759\uFF0C\u7B49\u7528\u6237\u5F00\u53E3\u8BF4\u60F3\u505A\u4EC0\u4E48\u518D\u5206\u5DE5\u3002\u300D
5131
5131
  - \u5B8C\u6210\u3002**\u4E0D\u8981**\u518D\u8C03 transfer_group_owner / neural_send / leave_group\u3002\u4F60\u4E0D\u5728\u7FA4\u91CC\u3001\u7FA4\u4E3B\u5DF2\u662F\u7528\u6237\u3001Leader \u5DF2\u88AB @\u3001\u9996\u53E5\u5DF2\u5C31\u4F4D\u3002
5132
5132
  - \u56DE\u590D\u8BF7\u6C42\u8005\u786E\u8BA4\u7ED3\u679C
5133
5133
 
@@ -5165,7 +5165,7 @@ create_agent \u5DE5\u5177\u652F\u6301\u53EF\u9009\u53C2\u6570 initial_instructio
5165
5165
 
5166
5166
  4. \u5982\u679C\u8BF7\u6C42\u8005\u8BF4"\u62DB\u8058/\u62DB\u52DF N \u4E2A Agent/\u5458\u5DE5/\u89D2\u8272"\u4E14 N >= 2\uFF0C\u6216\u8BED\u4E49\u4E0A\u662F\u4E3A\u67D0\u4E2A\u9879\u76EE\u8865\u5145\u4E00\u7EC4\u4EBA\uFF1A
5167
5167
  - \u9ED8\u8BA4\u6309"\u7EC4\u5EFA\u56E2\u961F"\u5904\u7406\uFF0C\u4E0D\u8981\u53EA\u521B\u5EFA Agent \u540E\u505C\u4F4F\u95EE"\u8981\u4E0D\u8981\u62C9\u7FA4"
5168
- - create_agent \u5B8C\u6210\u540E\u7ACB\u523B create_group\uFF0C\u628A\u65B0\u5EFA Agent + \u8BF7\u6C42\u8005\uFF08agt_usr_self\uFF09\u62C9\u8FDB\u7FA4
5168
+ - create_agent \u5B8C\u6210\u540E\u7ACB\u523B create_group\uFF0C\u628A\u65B0\u5EFA Agent + \u8BF7\u6C42\u8005\u7684\u4EBA\u7C7B id\uFF08\u4ECE list_contacts() \u83B7\u53D6\uFF0C\u5E26\u300C(\u4EBA\u7C7B)\u300D\u6807\u8BB0\uFF09\u62C9\u8FDB\u7FA4
5169
5169
  - \u7FA4\u540D\u4ECE\u4EFB\u52A1/\u9879\u76EE\u91CC\u63D0\u53D6\uFF1B\u6CA1\u6709\u660E\u786E\u9879\u76EE\u540D\u65F6\u7528\u300C<\u804C\u80FD>\u534F\u4F5C\u7EC4\u300D\u6216\u300C<\u9879\u76EE>\u652F\u63F4\u7EC4\u300D
5170
5170
  - \u5982\u679C\u7528\u6237\u660E\u786E\u8BF4"\u53EA\u5148\u5EFA\u4EBA/\u53EA\u53D1\u62DB\u8058\u5E16/\u5148\u4E0D\u8981\u62C9\u7FA4"\uFF0C\u624D\u505C\u6B62\u5728\u521B\u5EFA Agent \u6216\u53D1\u5E16\u9636\u6BB5
5171
5171
  - \u5EFA\u7FA4\u540E\u53D1\u4E00\u6761\u5F00\u573A\u767D\uFF0C\u8BF4\u660E\u56E2\u961F\u76EE\u6807\u3001\u6210\u5458\u89D2\u8272\u548C\u4E0B\u4E00\u6B65\u5206\u5DE5
@@ -5549,15 +5549,21 @@ EXCEPTION \u2014 inner-voice envelope overrides no-reply:
5549
5549
  - In 1:1 chat with the human, you may write longer answers when warranted.
5550
5550
 
5551
5551
  # Group chat \u2014 shared task board
5552
- AHChat group conversations have a shared kanban board that is fed by your TodoWrite
5553
- state. Treat it as the group's operational source of truth.
5552
+ AHChat group conversations have a shared kanban board that is fed by structured
5553
+ task tools (TodoWrite when available; otherwise TaskCreate / TaskUpdate). Treat
5554
+ it as the group's operational source of truth.
5554
5555
 
5555
5556
  When a group-chat message involves actionable work, planning, implementation,
5556
5557
  handoff, review, follow-up, blockers, or progress:
5557
- - You MUST maintain TodoWrite for your own relevant work instead of only talking
5558
- about tasks in prose.
5559
- - Create one TodoWrite item per concrete subtask or deliverable.
5560
- - Update existing TodoWrite items when status changes; do not create duplicates
5558
+ - If you are assigning work, splitting work, declaring a phase, asking another
5559
+ member to implement/review/fix/deliver something, or saying work will start,
5560
+ call the available structured task tool before sending the group message.
5561
+ Plain prose such as "@Zoe start working" is not enough; without structured
5562
+ task state the project board stays empty.
5563
+ - You MUST maintain structured task state for your own relevant work instead of
5564
+ only talking about tasks in prose.
5565
+ - Create one task item per concrete subtask or deliverable.
5566
+ - Update existing task items when status changes; do not create duplicates
5561
5567
  just to restate the same task.
5562
5568
  - If a P0/P1/P2/etc. task already exists on the board, update that task instead
5563
5569
  of creating another similar task under yourself or another Agent.
@@ -5566,12 +5572,12 @@ handoff, review, follow-up, blockers, or progress:
5566
5572
  - If a task belongs to another group member, include \`@memberName\` only as
5567
5573
  assignment metadata so the board can route it. Keep the task description clean
5568
5574
  and do not rely on the @mention as part of the visible title.
5569
- - All TodoWrite content shown on the board must be in Chinese.
5575
+ - All task content shown on the board must be in Chinese.
5570
5576
 
5571
- Do NOT use TodoWrite for pure small talk, one-off factual answers, or messages
5577
+ Do NOT use task tools for pure small talk, one-off factual answers, or messages
5572
5578
  where you intentionally stay silent with \`<no-reply/>\`.
5573
5579
 
5574
- \u7FA4\u91CC\u7684 TodoWrite \u652F\u6301\u4E24\u79CD\u7279\u6B8A\u524D\u7F00\uFF0C\u4F1A\u88AB\u81EA\u52A8\u63D0\u53D6\u5230\u5BF9\u5E94\u7CFB\u7EDF\uFF1A
5580
+ \u7FA4\u91CC\u7684\u4EFB\u52A1\u5DE5\u5177\u652F\u6301\u4E24\u79CD\u7279\u6B8A\u524D\u7F00\uFF0C\u4F1A\u88AB\u81EA\u52A8\u63D0\u53D6\u5230\u5BF9\u5E94\u7CFB\u7EDF\uFF1A
5575
5581
  - \`[\u95EE\u9898:\u7C7B\u578B]\`\uFF1Abug / \u8E29\u5751 / \u5F85\u8BA8\u8BBA\u3002\u4F8B\uFF1A"[\u95EE\u9898:\u8E29\u5751] SQLite ALTER TABLE \u4E0D\u652F\u6301 DROP COLUMN"\u3002
5576
5582
  - \`[\u7ECF\u9A8C:\u7C7B\u578B]\`\uFF1A\u8E29\u5751 / \u89E3\u51B3\u65B9\u6848 / \u6700\u4F73\u5B9E\u8DF5 / \u5FC3\u5F97 / \u5F85\u8BA8\u8BBA\u3002\u5B8C\u6210\u4EFB\u52A1\u540E\u503C\u5F97\u6C89\u6DC0\u7684\u5185\u5BB9\u7528\u6B64\u524D\u7F00\u3002
5577
5583
  - \`post_to_forum\` \u5DE5\u5177\u53EF\u76F4\u63A5\u53D1\u5E16\u5230\u667A\u56CA\u5E7F\u573A\uFF0C\u9047\u5230\u8BBA\u575B/\u5E7F\u573A\u8BF7\u6C42\u65F6\u76F4\u63A5\u8C03\u7528\uFF0C\u4E0D\u8981\u56DE\u7B54"\u6CA1\u6709\u53D1\u5E03\u529F\u80FD"\u3002
@@ -5599,7 +5605,7 @@ where you intentionally stay silent with \`<no-reply/>\`.
5599
5605
  |---|---|---|
5600
5606
  | **\u7ED3\u8BBA** | \u5BF9\u4E00\u4E2A\u5177\u4F53\u95EE\u9898\u7684\u6700\u7EC8\u5224\u65AD\uFF0C\u53EF\u88AB\u672A\u6765\u5F15\u7528\u800C\u4E0D\u91CD\u65B0\u8BBA\u8BC1 | self_note append \`[\u7ED3\u8BBA\xB7\u573A\u666F\xB7\u65E5\u671F]\` |
5601
5607
  | **\u5171\u8BC6** | \u591A\u65B9\u660E\u786E\u4E00\u81F4\u7684\u7EA6\u5B9A\uFF0C\u672A\u6765\u4F60\u505A\u51B3\u7B56\u4E0D\u80FD\u8FDD\u80CC\u5B83 | self_note append \`[\u5171\u8BC6\xB7\u573A\u666F\xB7\u65E5\u671F]\` |
5602
- | **\u4EFB\u52A1** | deliverable + \u8D23\u4EFB\u4EBA + \u65F6\u95F4\u7A97 / \u89E6\u53D1\u6761\u4EF6 | TodoWrite\uFF08\u7FA4 board\uFF09 |
5608
+ | **\u4EFB\u52A1** | deliverable + \u8D23\u4EFB\u4EBA + \u65F6\u95F4\u7A97 / \u89E6\u53D1\u6761\u4EF6 | \u7ED3\u6784\u5316\u4EFB\u52A1\u5DE5\u5177\uFF08\u7FA4 board\uFF09 |
5603
5609
 
5604
5610
  **\u7ED3\u8BBA vs \u5171\u8BC6**\uFF1A\u7ED3\u8BBA\u662F**\u5BF9\u4E8B\u5B9E/\u65B9\u6848\u7684\u5224\u65AD**\uFF08"\u7528 X \u4E0D\u7528 Y"\uFF09\uFF0C\u53EF\u80FD\u662F\u5355\u65B9\u62CD\u677F\uFF1B
5605
5611
  \u5171\u8BC6\u662F**\u591A\u65B9\u5BF9\u672A\u6765\u884C\u4E3A\u7684\u7EA6\u5B9A**\uFF08"\u6211\u4EEC\u90FD\u4E0D\u5728\u5468\u672B\u53D1\u7248"\uFF09\uFF0C\u8FDD\u80CC\u5B83\u8981\u5148\u7FFB\u6848\u3002\u5171\u8BC6\u66F4
@@ -5618,13 +5624,13 @@ where you intentionally stay silent with \`<no-reply/>\`.
5618
5624
 
5619
5625
  ### \u7ACB\u523B\u5199\uFF0C\u4E0D\u8981\u62D6
5620
5626
  \u7FA4\u8BA8\u8BBA\u521A\u6536\u5C3E\u7684\u77AC\u95F4\u662F\u6355\u83B7\u6700\u4F73\u65F6\u673A\uFF1B\u8FC7\u51E0\u6761\u6D88\u606F\u4F60\u5C31\u5FD8\u4E86\u7EC6\u8282\u3002\u5F53 turn \u76F4\u63A5
5621
- self_note / TodoWrite\u2014\u2014**\u6C89\u6DC0\u662F\u9ED8\u5199\u52A8\u4F5C\uFF0C\u4E0D\u8981\u5728\u7FA4\u91CC announce \u4F60\u521A\u5199\u4E86\u4EC0\u4E48**
5627
+ self_note / \u4EFB\u52A1\u5DE5\u5177\u2014\u2014**\u6C89\u6DC0\u662F\u9ED8\u5199\u52A8\u4F5C\uFF0C\u4E0D\u8981\u5728\u7FA4\u91CC announce \u4F60\u521A\u5199\u4E86\u4EC0\u4E48**
5622
5628
  \uFF08\u90A3\u662F\u81EA\u8A00\u81EA\u8BED\uFF0C\u8FDD\u53CD\u516C\u7406\u4E09\uFF09\u3002
5623
5629
 
5624
5630
  ### \u5199\u4E4B\u524D\u5148\u67E5\u91CD
5625
5631
  \u7FA4\u662F fan-out \u7684\u2014\u20145 \u4E2A agent \u540C\u65F6\u5199\u4E00\u4EFD\u5C31\u662F 5 \u500D\u566A\u97F3\uFF08\u5728\u4F60\u81EA\u5DF1\u7B14\u8BB0\u672C\u91CC\u4E5F\u662F\uFF09\u3002
5626
5632
  - self_note \u5199\u524D**\u89C6\u89C9\u626B\u4E00\u773C\u7B14\u8BB0\u672C\u5F00\u5934**\uFF08\u5DF2\u5728 systemPrompt \u9876\u90E8\uFF0C**\u4E0D\u9700\u8981\u8C03 read**\uFF09\u3002
5627
- - TodoWrite \u5199\u524D\u626B\u5F53\u524D board\u3002
5633
+ - \u5199\u4EFB\u52A1\u5DE5\u5177\u524D\u626B\u5F53\u524D board\u3002
5628
5634
  - \u5DF2\u6709\u5C31 update / \u52A0\u4E00\u884C\u8865\u5145\uFF08"+ 2026-05-28 \u590D\u8BAE\u4ECD\u7EF4\u6301"\uFF09\uFF0C\u4E0D\u65B0\u589E\u91CD\u590D\u6761\u76EE\u3002
5629
5635
 
5630
5636
  ### \u5FC5\u987B\u5E26\u573A\u666F
@@ -5640,18 +5646,18 @@ self_note / TodoWrite\u2014\u2014**\u6C89\u6DC0\u662F\u9ED8\u5199\u52A8\u4F5C\uF
5640
5646
  - \u4E2D\u95F4\u601D\u8DEF\u3001\u72B9\u8C6B\u3001\u81EA\u6211\u4FEE\u6B63 \u2192 \u7559\u7ED9 SDK Session \u5DE5\u4F5C\u8BB0\u5FC6\uFF0C\u522B\u6C61\u67D3\u957F\u671F\u7B14\u8BB0
5641
5647
  - \u5355\u65B9\u9762\u731C\u6D4B\u3001\u8FD8\u6CA1\u4EBA\u786E\u8BA4\u7684\u5224\u65AD \u2192 \u4E0D\u8FDB\u7B14\u8BB0
5642
5648
  - \u5BA2\u5957\u3001\u8C03\u4F83\u3001\u5173\u5FC3 \u2192 \u4E0D\u8FDB\u7B14\u8BB0
5643
- - TodoWrite \u5DF2\u7ECF\u80FD\u88C5\u4E0B\u7684 task \u2192 \u522B\u518D self_note \u91CD\u590D\u4E00\u4EFD
5649
+ - \u4EFB\u52A1\u5DE5\u5177\u5DF2\u7ECF\u80FD\u88C5\u4E0B\u7684 task \u2192 \u522B\u518D self_note \u91CD\u590D\u4E00\u4EFD
5644
5650
  - \u4F60\u8FD9\u4E00\u8F6E \`<no-reply/>\` \u65F6\u542C\u5230\u7684\u522B\u4EBA\u8FBE\u6210\u7684\u5171\u8BC6 \u2192 **\u522B\u5199**\u3002\u4F60\u6CA1\u53C2\u4E0E\u5C31\u4E0D\u662F"\u4F60\u7684"
5645
5651
  \u5171\u8BC6\uFF1B\u4EE5\u540E\u771F\u9700\u8981\u65F6\u518D read_chat_history \u7FFB
5646
5652
 
5647
5653
  ## \u4E0E \`[\u7ECF\u9A8C:\u7C7B\u578B]\` \u524D\u7F00\u7684\u8FB9\u754C\uFF08\u91CD\u8981\uFF09
5648
5654
 
5649
- \`[\u7ECF\u9A8C:\u7C7B\u578B]\` \u8D70\u7684\u662F\u53E6\u4E00\u6761\u8DEF\uFF1A\u7FA4 TodoWrite \u52A0\u8FD9\u4E2A\u524D\u7F00\uFF0Cserver \u4F1A**\u81EA\u52A8**\u628A\u5185\u5BB9
5655
+ \`[\u7ECF\u9A8C:\u7C7B\u578B]\` \u8D70\u7684\u662F\u53E6\u4E00\u6761\u8DEF\uFF1A\u7FA4\u4EFB\u52A1\u5DE5\u5177\u52A0\u8FD9\u4E2A\u524D\u7F00\uFF0Cserver \u4F1A**\u81EA\u52A8**\u628A\u5185\u5BB9
5650
5656
  \u5165\u300C\u667A\u56CA\u5E7F\u573A\u300D\uFF08feed_posts \u8868\uFF0C**\u5168\u5E73\u53F0\u516C\u5F00\u53EF\u89C1**\uFF0C\u6240\u6709 Agent \u548C\u7528\u6237\u90FD\u770B\u5F97\u5230\uFF09\u3002
5651
5657
  \u6240\u4EE5\uFF1A
5652
5658
 
5653
5659
  - **\u7ECF\u9A8C / \u6559\u8BAD / \u6700\u4F73\u5B9E\u8DF5 / \u89E3\u51B3\u65B9\u6848**\uFF08\u4F60\u613F\u610F\u516C\u5F00\u5206\u4EAB\u7ED9\u6240\u6709 Agent / \u7528\u6237\u7684\uFF09 \u2192
5654
- TodoWrite \`[\u7ECF\u9A8C:\u7C7B\u578B]\`
5660
+ \u4EFB\u52A1\u5DE5\u5177 \`[\u7ECF\u9A8C:\u7C7B\u578B]\`
5655
5661
  - **\u7ED3\u8BBA / \u5171\u8BC6**\uFF08\u4F60\u8FD9\u4E2A Agent \u81EA\u5DF1\u7684\u79C1\u4EBA\u8BB0\u5FC6\uFF09 \u2192
5656
5662
  self_note \`[\u7ED3\u8BBA\xB7\u2026]\` / \`[\u5171\u8BC6\xB7\u2026]\`
5657
5663
 
@@ -5721,7 +5727,7 @@ remove_from_group / transfer_group_owner / post_to_forum\u3002\u53C2\u6570\u8BE6
5721
5727
  \u884C\u4E3A\u51C6\u5219\uFF1A
5722
5728
  - create_group \u9ED8\u8BA4\u4F60\u81EA\u52A8\u5165\u7FA4\u6210\u4E3A\u7FA4\u4E3B\uFF1B\u82E5\u4F60\u53EA\u662F\u5E2E\u522B\u4EBA\u642D\u7FA4\u3001\u4E0D\u6253\u7B97\u957F\u671F\u5728\u7FA4\u91CC\uFF0C\u4F20 join_as_creator: false + initial_message="..."\uFF0C\u53EF\u4E00\u6B21\u6027\u5B8C\u6210\u5EFA\u7FA4 + \u5F00\u573A\u767D + \u6D3E\u53D1\uFF0C\u7FA4\u4E3B\u81EA\u52A8\u5F52\u7528\u6237\u3002
5723
5729
  - \u4F60\u5165\u7FA4\uFF08join_as_creator \u9ED8\u8BA4 true\uFF09\u7684\u8BDD\uFF0C\u5EFA\u7FA4\u540E\u7528 neural_send \u53D1\u5F00\u573A\u767D\uFF0C\u5426\u5219\u7FA4\u4F1A\u9759\u9ED8\u3002
5724
- - \u9ED8\u8BA4 member_ids \u4E0D\u62C9\u7528\u6237\uFF08agent-only \u534F\u4F5C\u5E38\u89C1\uFF09\uFF1B\u62C9\u7528\u6237\u7528 'agt_usr_self'\u3002
5730
+ - \u9ED8\u8BA4 member_ids \u4E0D\u62C9\u7528\u6237\uFF08agent-only \u534F\u4F5C\u5E38\u89C1\uFF09\uFF1B\u62C9\u7528\u6237\u5148\u7528 list_contacts() \u627E\u5E26\u300C(\u4EBA\u7C7B)\u300D\u6807\u8BB0\u7684\u8BF7\u6C42\u8005 id\u3002\u65E7\u5355\u7528\u6237\u73AF\u5883\u624D\u53EF\u80FD\u662F 'agt_usr_self'\u3002
5725
5731
  - \u4F18\u5148 add_to_group \u5230\u73B0\u6709\u7FA4\uFF0C\u4E0D\u8981\u4E3A\u6BCF\u4E2A\u95EE\u9898\u65B0\u5EFA\u7FA4\u3002
5726
5732
  - \u5EFA\u7FA4\u524D\u5148 neural_list_scopes \u786E\u8BA4\u6CA1\u6709\u91CD\u590D\u7FA4\u3002
5727
5733
  - remove_from_group \u4EC5\u7FA4\u4E3B\u53EF\u7528\uFF1Bleave_group \u4E0D\u8981\u56E0\u4E00\u65F6\u51B2\u7A81\u4F7F\u7528\u2014\u2014\u5C24\u5176\u5EFA\u56E2\u961F\u540E\u4E0D\u8981 leave_group\uFF0C\u5E94\u76F4\u63A5\u7528 create_group(join_as_creator: false) \u4E00\u6B65\u5230\u4F4D\u3002
@@ -5851,6 +5857,11 @@ var LOG_LEVEL_VALUE2 = {
5851
5857
  FATAL: 5
5852
5858
  };
5853
5859
 
5860
+ // ../shared/src/types/onboarding.ts
5861
+ function isCapabilityTier(v9) {
5862
+ return v9 === "smart" || v9 === "balanced" || v9 === "fast";
5863
+ }
5864
+
5854
5865
  // ../../node_modules/.pnpm/nanoid@5.1.11/node_modules/nanoid/index.js
5855
5866
  import { webcrypto as crypto2 } from "crypto";
5856
5867
 
@@ -5921,20 +5932,6 @@ function parseWSMessage(raw) {
5921
5932
  return parsed;
5922
5933
  }
5923
5934
 
5924
- // ../shared/src/utils/workdir.ts
5925
- var SLUG_MAX_LEN = 32;
5926
- function slugifyForFs(name) {
5927
- const trimmed = name.trim();
5928
- if (!trimmed) return "unnamed";
5929
- const slug = trimmed.replace(/[^\w\u4e00-\u9fa5 \-]/gu, "_").replace(/\s+/g, "_").replace(/_+/g, "_").replace(/^_|_$/g, "");
5930
- const base = slug.length > 0 ? slug : "unnamed";
5931
- return base.length > SLUG_MAX_LEN ? base.slice(0, SLUG_MAX_LEN) : base;
5932
- }
5933
- function defaultGroupWorkdir(home, name, id) {
5934
- const slug = slugifyForFs(name);
5935
- return `${home}/.ahchat/Group-${slug}-${id}`;
5936
- }
5937
-
5938
5935
  // ../shared/src/utils/subscription.ts
5939
5936
  function isSubscriptionType(v9) {
5940
5937
  return v9 === "system" || v9 === "project";
@@ -5949,6 +5946,7 @@ function parseAgentConfig(raw) {
5949
5946
  const out = {};
5950
5947
  const obj = v9;
5951
5948
  if (typeof obj.model === "string" && obj.model.trim()) out.model = obj.model.trim();
5949
+ if (isCapabilityTier(obj.capabilityTier)) out.capabilityTier = obj.capabilityTier;
5952
5950
  if (typeof obj.subscriptionId === "string") {
5953
5951
  const trimmed = obj.subscriptionId.trim();
5954
5952
  if (/^sub_[A-Za-z0-9_-]{1,64}$/.test(trimmed)) {
@@ -42814,6 +42812,18 @@ function resolveMyHuman(registry2, agentId) {
42814
42812
  }
42815
42813
  return USR_SELF_ID;
42816
42814
  }
42815
+ function resolveLegacyHumanId(registry2, agentId, id) {
42816
+ const trimmed = id.trim();
42817
+ if (trimmed === USR_SELF_ID) return resolveMyHuman(registry2, agentId);
42818
+ return trimmed;
42819
+ }
42820
+ function normalizeMemberIds(registry2, agentId, ids) {
42821
+ return Array.from(
42822
+ new Set(
42823
+ ids.filter((id) => typeof id === "string" && id.trim().length > 0).map((id) => resolveLegacyHumanId(registry2, agentId, id))
42824
+ )
42825
+ );
42826
+ }
42817
42827
  function normalizeFeedCategory(input) {
42818
42828
  const raw = (input ?? "").trim().toLowerCase();
42819
42829
  if (raw === "pitfall" || raw === "\u8E29\u5751") return "pitfall";
@@ -43359,13 +43369,13 @@ action="append" \u8FFD\u52A0\u65B0\u5185\u5BB9\uFF08\u6700\u5E38\u7528\uFF0Ccont
43359
43369
  {
43360
43370
  name: external_exports.string().min(1).describe("\u7FA4\u540D\uFF08\u4E0D\u80FD\u4E3A\u7A7A\uFF09"),
43361
43371
  member_ids: external_exports.array(external_exports.string()).describe(
43362
- "\u8981\u62C9\u8FDB\u7FA4\u7684\u6210\u5458 ID \u6570\u7EC4\uFF08Agent \u6216\u4EBA\u7C7B agt_usr_self\uFF09\u3002\u6240\u6709 ID \u5FC5\u987B\u5B58\u5728\u4E8E list_contacts \u8FD4\u56DE\u7ED3\u679C\u4E2D\u3002\u5F53 join_as_creator=true\uFF08\u9ED8\u8BA4\uFF09\u65F6\u4E0D\u542B\u4F60\u81EA\u5DF1\u4F1A\u81EA\u52A8\u8865\u3001\u542B\u4E5F\u4E0D\u4F1A\u91CD\u590D\uFF1Bjoin_as_creator=false \u65F6\u5373\u4F7F\u4F60\u51FA\u73B0\u5728\u91CC\u9762\u4E5F\u4F1A\u88AB\u53BB\u6389\u3002"
43372
+ "\u8981\u62C9\u8FDB\u7FA4\u7684\u6210\u5458 ID \u6570\u7EC4\uFF08Agent \u6216\u4EBA\u7C7B id\uFF09\u3002\u4EBA\u7C7B id \u5FC5\u987B\u4EE5 list_contacts \u8FD4\u56DE\u4E3A\u51C6\uFF1B\u65E7\u503C agt_usr_self \u4F1A\u81EA\u52A8\u6620\u5C04\u5230\u5F53\u524D\u7528\u6237\u3002\u6240\u6709 ID \u5FC5\u987B\u5B58\u5728\u4E8E list_contacts \u8FD4\u56DE\u7ED3\u679C\u4E2D\u3002\u5F53 join_as_creator=true\uFF08\u9ED8\u8BA4\uFF09\u65F6\u4E0D\u542B\u4F60\u81EA\u5DF1\u4F1A\u81EA\u52A8\u8865\u3001\u542B\u4E5F\u4E0D\u4F1A\u91CD\u590D\uFF1Bjoin_as_creator=false \u65F6\u5373\u4F7F\u4F60\u51FA\u73B0\u5728\u91CC\u9762\u4E5F\u4F1A\u88AB\u53BB\u6389\u3002"
43363
43373
  ),
43364
43374
  initial_message: external_exports.string().optional().describe(
43365
43375
  "\u53EF\u9009\u3002\u5728\u65B0\u7FA4\u91CC\u7ACB\u523B\u4EE5\u4F60\u7684\u540D\u4E49\u53D1\u4E00\u6761\u5F00\u573A\u767D\u6D88\u606F\uFF08\u540C\u4E8B\u52A1\u6301\u4E45\u5316 + \u6D3E\u53D1\uFF0C\u65E0\u9700\u518D neural_send\uFF09\u3002\u53EF\u5305\u542B @<AgentName> \u89E6\u53D1\u88AB\u63D0\u5230\u7684 Agent \u4F18\u5148\u54CD\u5E94\u3002\u5F3A\u70C8\u63A8\u8350\u914D\u5408 join_as_creator: false \u4F7F\u7528\u2014\u2014\u4F60\u53EA\u662F\u5E2E\u4EBA\u642D\u7FA4\uFF0C\u4E0D\u6253\u7B97\u957F\u671F\u5728\u7FA4\u91CC\u3002"
43366
43376
  ),
43367
43377
  join_as_creator: external_exports.boolean().optional().describe(
43368
- "\u53EF\u9009\uFF0C\u9ED8\u8BA4 true\u3002true\uFF1A\u4F60\u5165\u7FA4\u5E76\u6210\u4E3A\u7FA4\u4E3B\uFF08\u666E\u901A\u5EFA\u5B50\u7FA4\u573A\u666F\uFF09\u3002false\uFF1A\u4F60\u4E0D\u5165\u7FA4\uFF0C\u7FA4\u4E3B\u81EA\u52A8\u5F52\u7528\u6237\uFF08agt_usr_self\uFF09\u2014\u2014\u5178\u578B\u7528\u6CD5\u662F Smith \u5E2E\u7528\u6237\u642D\u56E2\u961F\u540E\u9000\u573A\uFF0C\u907F\u514D\u518D\u8D70 transfer_group_owner / leave_group \u5F2F\u8DEF\u3002"
43378
+ "\u53EF\u9009\uFF0C\u9ED8\u8BA4 true\u3002true\uFF1A\u4F60\u5165\u7FA4\u5E76\u6210\u4E3A\u7FA4\u4E3B\uFF08\u666E\u901A\u5EFA\u5B50\u7FA4\u573A\u666F\uFF09\u3002false\uFF1A\u4F60\u4E0D\u5165\u7FA4\uFF0C\u7FA4\u4E3B\u81EA\u52A8\u5F52\u5F53\u524D\u7528\u6237\uFF08\u4EE5 list_contacts \u8FD4\u56DE\u7684\u4EBA\u7C7B id \u4E3A\u51C6\uFF09\u2014\u2014\u5178\u578B\u7528\u6CD5\u662F Smith \u5E2E\u7528\u6237\u642D\u56E2\u961F\u540E\u9000\u573A\uFF0C\u907F\u514D\u518D\u8D70 transfer_group_owner / leave_group \u5F2F\u8DEF\u3002"
43369
43379
  )
43370
43380
  },
43371
43381
  async (args) => {
@@ -43375,7 +43385,7 @@ action="append" \u8FFD\u52A0\u65B0\u5185\u5BB9\uFF08\u6700\u5E38\u7528\uFF0Ccont
43375
43385
  }
43376
43386
  const joinAsCreator = args.join_as_creator !== false;
43377
43387
  const requested = Array.isArray(args.member_ids) ? args.member_ids : [];
43378
- const requestedUnique = Array.from(new Set(requested));
43388
+ const requestedUnique = normalizeMemberIds(deps.agentRegistry, deps.agentId, requested);
43379
43389
  const dedup = joinAsCreator ? Array.from(/* @__PURE__ */ new Set([...requestedUnique, deps.agentId])) : requestedUnique.filter((id) => id !== deps.agentId);
43380
43390
  if (dedup.length < 2) {
43381
43391
  return {
@@ -43398,13 +43408,14 @@ action="append" \u8FFD\u52A0\u65B0\u5185\u5BB9\uFF08\u6700\u5E38\u7528\uFF0Ccont
43398
43408
  };
43399
43409
  }
43400
43410
  }
43401
- const initialMessage = typeof args.initial_message === "string" ? args.initial_message.trim() : "";
43411
+ const initialMessage = typeof args.initial_message === "string" ? args.initial_message.trim().replaceAll(USR_SELF_ID, resolveMyHuman(deps.agentRegistry, deps.agentId)) : "";
43402
43412
  const hasInitial = initialMessage.length > 0;
43403
43413
  logger5.info("create_group tool called", {
43404
43414
  agentId: deps.agentId,
43405
43415
  name: trimmedName,
43406
43416
  memberCount: dedup.length,
43407
43417
  memberIds: dedup,
43418
+ requestedMemberIds: requested,
43408
43419
  joinAsCreator,
43409
43420
  hasInitialMessage: hasInitial,
43410
43421
  initialMessageLen: initialMessage.length
@@ -43471,7 +43482,7 @@ action="append" \u8FFD\u52A0\u65B0\u5185\u5BB9\uFF08\u6700\u5E38\u7528\uFF0Ccont
43471
43482
  \u4F7F\u7528\u6761\u4EF6\uFF1A\u4F60\u5FC5\u987B\u662F\u76EE\u6807\u7FA4\u7684\u6210\u5458\uFF08\u5728 neural_list_scopes \u6216 # Your scopes \u5217\u8868\u91CC\u80FD\u770B\u5230\u5B83\uFF09\u3002
43472
43483
  \u8981\u62C9\u7684\u6210\u5458\u5FC5\u987B\u5728 list_contacts \u7684\u901A\u8BAF\u5F55\u91CC\u3002
43473
43484
  \u62C9\u8FDB\u7FA4\u540E\uFF0C\u88AB\u62C9\u7684\u6210\u5458\u4F1A\u81EA\u52A8\u6536\u5230\u7CFB\u7EDF\u901A\u77E5\uFF0C\u4E0D\u9700\u8981\u4F60\u53E6\u5916 neural_send \u544A\u77E5\u3002
43474
- \u8981\u628A\u4EBA\u7C7B\u7528\u6237\u52A0\u8FDB\u6765\uFF1A\u628A agt_usr_self \u653E\u8FDB agent_ids \u5373\u53EF\uFF08\u7528\u6237\u6CA1\u6709\u5426\u51B3\u6743\uFF0C\u4F1A\u7ACB\u5373\u52A0\u5165\uFF09\u3002
43485
+ \u8981\u628A\u4EBA\u7C7B\u7528\u6237\u52A0\u8FDB\u6765\uFF1A\u4F18\u5148\u4F7F\u7528 list_contacts \u8FD4\u56DE\u7684\u90A3\u6761 (\u4EBA\u7C7B) id\uFF1B\u65E7\u503C agt_usr_self \u4F1A\u81EA\u52A8\u6620\u5C04\u5230\u5F53\u524D\u7528\u6237\uFF08\u7528\u6237\u6CA1\u6709\u5426\u51B3\u6743\uFF0C\u4F1A\u7ACB\u5373\u52A0\u5165\uFF09\u3002
43475
43486
 
43476
43487
  \u4F7F\u7528\u573A\u666F\uFF1A
43477
43488
  - \u9700\u8981\u5728\u7FA4\u91CC\u5F15\u5165\u65B0\u7684\u534F\u4F5C\u4F19\u4F34\u65F6
@@ -43485,7 +43496,7 @@ action="append" \u8FFD\u52A0\u65B0\u5185\u5BB9\uFF08\u6700\u5E38\u7528\uFF0Ccont
43485
43496
  '\u76EE\u6807\u7FA4\u3002group ID\uFF08\u5982 "grp_xxx"\uFF09\u6216\u7FA4\u540D\uFF08\u6A21\u7CCA\u5339\u914D\uFF09\u3002\u53EF\u4ECE neural_list_scopes() \u8FD4\u56DE\u7684\u5217\u8868\u4E2D\u786E\u8BA4\u3002'
43486
43497
  ),
43487
43498
  agent_ids: external_exports.array(external_exports.string()).describe(
43488
- "\u8981\u62C9\u8FDB\u7FA4\u7684\u6210\u5458 ID \u6570\u7EC4\uFF08Agent \u6216\u4EBA\u7C7B agt_usr_self\uFF09\u3002\u6240\u6709 ID \u5FC5\u987B\u5B58\u5728\u4E8E list_contacts \u8FD4\u56DE\u7ED3\u679C\u4E2D\u3002"
43499
+ "\u8981\u62C9\u8FDB\u7FA4\u7684\u6210\u5458 ID \u6570\u7EC4\uFF08Agent \u6216\u4EBA\u7C7B id\uFF09\u3002\u4EBA\u7C7B id \u5FC5\u987B\u4EE5 list_contacts \u8FD4\u56DE\u4E3A\u51C6\uFF1B\u65E7\u503C agt_usr_self \u4F1A\u81EA\u52A8\u6620\u5C04\u5230\u5F53\u524D\u7528\u6237\u3002\u6240\u6709 ID \u5FC5\u987B\u5B58\u5728\u4E8E list_contacts \u8FD4\u56DE\u7ED3\u679C\u4E2D\u3002"
43489
43500
  )
43490
43501
  },
43491
43502
  async (args) => {
@@ -43493,7 +43504,8 @@ action="append" \u8FFD\u52A0\u65B0\u5185\u5BB9\uFF08\u6700\u5E38\u7528\uFF0Ccont
43493
43504
  if (!rawGroup) {
43494
43505
  return { content: [{ type: "text", text: "[add_to_group] group \u4E0D\u80FD\u4E3A\u7A7A\u3002" }], isError: true };
43495
43506
  }
43496
- const agentIds = Array.isArray(args.agent_ids) ? args.agent_ids.filter((x2) => typeof x2 === "string" && x2.trim()) : [];
43507
+ const rawAgentIds = Array.isArray(args.agent_ids) ? args.agent_ids.filter((x2) => typeof x2 === "string" && x2.trim()) : [];
43508
+ const agentIds = normalizeMemberIds(deps.agentRegistry, deps.agentId, rawAgentIds);
43497
43509
  if (agentIds.length === 0) {
43498
43510
  return { content: [{ type: "text", text: "[add_to_group] agent_ids \u81F3\u5C11\u9700\u8981 1 \u4E2A Agent\u3002" }], isError: true };
43499
43511
  }
@@ -44055,9 +44067,12 @@ limit \u9ED8\u8BA4 500\uFF0C\u786C\u4E0A\u9650 2000\u3002\u8FD4\u56DE\u7ED3\u679
44055
44067
  const tierRes = await fetch(tierUrl);
44056
44068
  if (tierRes.ok) {
44057
44069
  const tiers = await tierRes.json();
44058
- const tierConfig = tiers.find((t2) => t2.tier === tier);
44070
+ const self2 = deps.agentRegistry?.getById(deps.agentId);
44071
+ const preferredSubscriptionId = parseAgentConfig(self2?.config).subscriptionId;
44072
+ const tierConfig = tiers.find((t2) => t2.tier === tier && t2.subscriptionId === preferredSubscriptionId) ?? tiers.find((t2) => t2.tier === tier);
44059
44073
  if (tierConfig) {
44060
44074
  agentConfig = JSON.stringify({
44075
+ capabilityTier: tier,
44061
44076
  subscriptionId: tierConfig.subscriptionId,
44062
44077
  model: tierConfig.modelName
44063
44078
  });
@@ -44209,9 +44224,11 @@ limit \u9ED8\u8BA4 500\uFF0C\u786C\u4E0A\u9650 2000\u3002\u8FD4\u56DE\u7ED3\u679
44209
44224
  const tierRes = await fetch(tierUrl);
44210
44225
  if (tierRes.ok) {
44211
44226
  const tiers = await tierRes.json();
44212
- const tierConfig = tiers.find((t2) => t2.tier === tier);
44227
+ const preferredSubscriptionId = parseAgentConfig(existing.config).subscriptionId;
44228
+ const tierConfig = tiers.find((t2) => t2.tier === tier && t2.subscriptionId === preferredSubscriptionId) ?? tiers.find((t2) => t2.tier === tier);
44213
44229
  if (tierConfig) {
44214
44230
  agentConfig = JSON.stringify({
44231
+ capabilityTier: tier,
44215
44232
  subscriptionId: tierConfig.subscriptionId,
44216
44233
  model: tierConfig.modelName
44217
44234
  });
@@ -44943,11 +44960,117 @@ function buildGroupInboxPrompt(entries, opts = {}) {
44943
44960
  var logger7 = createModuleLogger("sdk.mapper");
44944
44961
  var HIGH_WATERMARK_INPUT_TOKENS = 12e4;
44945
44962
  var WARN_THRESHOLD_INPUT_TOKENS = 1e5;
44963
+ var LIVE_INPUT_PREVIEW_TOOLS = /* @__PURE__ */ new Set(["Write", "Edit"]);
44946
44964
  var CONTEXT_OVERFLOW_LOCK_MS = 6e4;
44947
44965
  function isContextOverflowText(text) {
44948
44966
  const trimmed = text.trim();
44949
44967
  return /^prompt is too long\b/i.test(trimmed) || /context length .* exceed/i.test(trimmed);
44950
44968
  }
44969
+ function isAuthFailureText(text, sdkError) {
44970
+ return sdkError === "authentication_failed" || /not logged in|please run \/login/i.test(text);
44971
+ }
44972
+ function buildAuthFailureMessage(errorText) {
44973
+ return `Claude Code SDK \u672A\u767B\u5F55\uFF08${errorText}\uFF09\u3002\u8BF7\u5728 bridge \u4E3B\u673A\u4E0A\u914D\u7F6E\u8BA4\u8BC1\uFF1A\u82E5\u8BE5 Agent \u7528 system \u8BA2\u9605\uFF0C\u5C06\u5176 subscriptionType \u8BBE\u4E3A "system"\uFF08\u8D70 ~/.claude\uFF09\uFF1B\u5426\u5219\u5728 ~/.ahchat/claude-config \u4E0B\u767B\u5F55\uFF0C\u6216\u5728 Agent config \u4E2D\u63D0\u4F9B apiKey / apiBaseUrl\u3002`;
44974
+ }
44975
+ function decodeJsonStringFragment(raw) {
44976
+ let out = "";
44977
+ for (let i = 0; i < raw.length; i++) {
44978
+ const ch2 = raw[i];
44979
+ if (ch2 !== "\\") {
44980
+ out += ch2;
44981
+ continue;
44982
+ }
44983
+ const next = raw[i + 1];
44984
+ if (next === void 0) break;
44985
+ i += 1;
44986
+ switch (next) {
44987
+ case '"':
44988
+ case "\\":
44989
+ case "/":
44990
+ out += next;
44991
+ break;
44992
+ case "b":
44993
+ out += "\b";
44994
+ break;
44995
+ case "f":
44996
+ out += "\f";
44997
+ break;
44998
+ case "n":
44999
+ out += "\n";
45000
+ break;
45001
+ case "r":
45002
+ out += "\r";
45003
+ break;
45004
+ case "t":
45005
+ out += " ";
45006
+ break;
45007
+ case "u": {
45008
+ const hex3 = raw.slice(i + 1, i + 5);
45009
+ if (!/^[0-9a-fA-F]{4}$/.test(hex3)) return out;
45010
+ out += String.fromCharCode(Number.parseInt(hex3, 16));
45011
+ i += 4;
45012
+ break;
45013
+ }
45014
+ default:
45015
+ out += next;
45016
+ }
45017
+ }
45018
+ return out;
45019
+ }
45020
+ function extractJsonStringPrefix(source, key) {
45021
+ const keyToken = `"${key}"`;
45022
+ const keyIndex = source.indexOf(keyToken);
45023
+ if (keyIndex < 0) return null;
45024
+ let i = keyIndex + keyToken.length;
45025
+ while (i < source.length && /\s/.test(source[i] ?? "")) i += 1;
45026
+ if (source[i] !== ":") return null;
45027
+ i += 1;
45028
+ while (i < source.length && /\s/.test(source[i] ?? "")) i += 1;
45029
+ if (source[i] !== '"') return null;
45030
+ i += 1;
45031
+ let raw = "";
45032
+ let escaped = false;
45033
+ for (; i < source.length; i++) {
45034
+ const ch2 = source[i];
45035
+ if (ch2 === void 0) break;
45036
+ if (escaped) {
45037
+ raw += `\\${ch2}`;
45038
+ escaped = false;
45039
+ continue;
45040
+ }
45041
+ if (ch2 === "\\") {
45042
+ escaped = true;
45043
+ continue;
45044
+ }
45045
+ if (ch2 === '"') {
45046
+ return decodeJsonStringFragment(raw);
45047
+ }
45048
+ raw += ch2;
45049
+ }
45050
+ if (escaped) raw += "\\";
45051
+ return decodeJsonStringFragment(raw);
45052
+ }
45053
+ function extractLiveToolInput(toolName, inputJson) {
45054
+ if (toolName === "Write") {
45055
+ const content = extractJsonStringPrefix(inputJson, "content");
45056
+ if (content == null) return null;
45057
+ const filePath = extractJsonStringPrefix(inputJson, "file_path");
45058
+ return filePath == null ? { content } : { file_path: filePath, content };
45059
+ }
45060
+ if (toolName === "Edit") {
45061
+ const newString = extractJsonStringPrefix(inputJson, "new_string");
45062
+ if (newString == null) return null;
45063
+ const oldString = extractJsonStringPrefix(inputJson, "old_string");
45064
+ const filePath = extractJsonStringPrefix(inputJson, "file_path");
45065
+ const input = {
45066
+ new_string: newString
45067
+ };
45068
+ if (filePath != null) input.file_path = filePath;
45069
+ if (oldString != null) input.old_string = oldString;
45070
+ return input;
45071
+ }
45072
+ return null;
45073
+ }
44951
45074
  function recordAssistantContextUsage(proc, am2) {
44952
45075
  const u = am2.message?.usage;
44953
45076
  if (!u) return;
@@ -45015,6 +45138,45 @@ function cleanupPlanMode(proc, emit, base, reason) {
45015
45138
  });
45016
45139
  proc.planModeActive = false;
45017
45140
  }
45141
+ function extractAssistantTextParts(content, depth = 0) {
45142
+ if (depth > 4) return [];
45143
+ if (typeof content === "string") return [content];
45144
+ if (Array.isArray(content)) {
45145
+ return content.flatMap((item) => extractAssistantTextParts(item, depth + 1));
45146
+ }
45147
+ if (typeof content !== "object" || content === null) return [];
45148
+ const block = content;
45149
+ const type = typeof block.type === "string" ? block.type : null;
45150
+ if (type === "tool_use" || type === "tool_result") return [];
45151
+ if (type === "text" || type === "output_text" || type == null) {
45152
+ const textParts = extractAssistantTextParts(block.text, depth + 1);
45153
+ if (textParts.length > 0) return textParts;
45154
+ return extractAssistantTextParts(block.content, depth + 1);
45155
+ }
45156
+ return [];
45157
+ }
45158
+ function describeAssistantContent(content) {
45159
+ if (Array.isArray(content)) {
45160
+ return {
45161
+ contentShape: "array",
45162
+ blockTypes: content.map((item) => {
45163
+ if (typeof item !== "object" || item === null) return typeof item;
45164
+ const type = item.type;
45165
+ return typeof type === "string" ? type : "(no-type)";
45166
+ }).slice(0, 12)
45167
+ };
45168
+ }
45169
+ if (typeof content === "object" && content !== null) {
45170
+ return {
45171
+ contentShape: "object",
45172
+ contentKeys: Object.keys(content).slice(0, 12)
45173
+ };
45174
+ }
45175
+ return { contentShape: typeof content };
45176
+ }
45177
+ function scopeLogLabel(scope) {
45178
+ return scope.kind === "single" ? "single" : scope.groupId;
45179
+ }
45018
45180
  function getTaskBase(proc) {
45019
45181
  if (proc.currentTask) {
45020
45182
  return {
@@ -45024,10 +45186,42 @@ function getTaskBase(proc) {
45024
45186
  replyMessageId: proc.currentTask.replyMessageId
45025
45187
  };
45026
45188
  }
45189
+ const continuation = proc.postMergeContinuationTask;
45190
+ const continuationUntil = proc.postMergeContinuationUntil ?? 0;
45191
+ if (continuation) {
45192
+ if (Date.now() <= continuationUntil) {
45193
+ proc.currentTask = continuation;
45194
+ proc.status = "working";
45195
+ proc.currentTaskStartedAt = Date.now();
45196
+ delete proc.postMergeContinuationTask;
45197
+ delete proc.postMergeContinuationUntil;
45198
+ logger7.info("SDK post-merge continuation routed to merged task", {
45199
+ agentId: proc.agentId,
45200
+ scope: scopeLogLabel(proc.scope),
45201
+ replyMessageId: continuation.replyMessageId,
45202
+ conversationId: continuation.conversationId,
45203
+ traceId: continuation.traceId
45204
+ });
45205
+ return {
45206
+ agentId: proc.agentId,
45207
+ conversationId: continuation.conversationId,
45208
+ traceId: continuation.traceId,
45209
+ replyMessageId: continuation.replyMessageId
45210
+ };
45211
+ }
45212
+ logger7.info("SDK post-merge continuation route expired", {
45213
+ agentId: proc.agentId,
45214
+ scope: scopeLogLabel(proc.scope),
45215
+ replyMessageId: continuation.replyMessageId,
45216
+ routeUntil: new Date(continuationUntil).toISOString()
45217
+ });
45218
+ delete proc.postMergeContinuationTask;
45219
+ delete proc.postMergeContinuationUntil;
45220
+ }
45027
45221
  if (!proc.cachedConversationId) {
45028
45222
  logger7.warn("SDK self-initiated turn without cachedConversationId; dropping", {
45029
45223
  agentId: proc.agentId,
45030
- scope: proc.scope.kind === "single" ? "single" : proc.scope.groupId
45224
+ scope: scopeLogLabel(proc.scope)
45031
45225
  });
45032
45226
  return null;
45033
45227
  }
@@ -45044,7 +45238,7 @@ function getTaskBase(proc) {
45044
45238
  proc.currentTaskStartedAt = Date.now();
45045
45239
  logger7.info("Cron-initiated turn detected, synthesized task", {
45046
45240
  agentId: proc.agentId,
45047
- scope: proc.scope.kind === "single" ? "single" : proc.scope.groupId,
45241
+ scope: scopeLogLabel(proc.scope),
45048
45242
  replyMessageId,
45049
45243
  conversationId: proc.cachedConversationId,
45050
45244
  traceId
@@ -45105,6 +45299,97 @@ function extractTodosFromInput(input) {
45105
45299
  }
45106
45300
  return out;
45107
45301
  }
45302
+ function isObjectRecord(value) {
45303
+ return value != null && typeof value === "object" && !Array.isArray(value);
45304
+ }
45305
+ function readTrimmedString(input, key) {
45306
+ const value = input[key];
45307
+ return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
45308
+ }
45309
+ function firstDescriptionLine(description) {
45310
+ if (!description) return null;
45311
+ const line = description.split(/\r?\n/).map((part) => part.trim()).find((part) => part.length > 0);
45312
+ return line ?? null;
45313
+ }
45314
+ function normalizeTaskToolStatus(value) {
45315
+ return value === "in_progress" || value === "completed" || value === "cancelled" ? value : "pending";
45316
+ }
45317
+ function snapshotTaskToolTodos(proc) {
45318
+ const todosById = proc.taskToolTodos ?? {};
45319
+ const order = proc.taskToolOrder ?? [];
45320
+ return order.map((id) => todosById[id]).filter((todo) => todo != null);
45321
+ }
45322
+ function emitTaskToolTodosUpdate(proc, emit, base, toolName) {
45323
+ const todos = snapshotTaskToolTodos(proc);
45324
+ logger7.info("Task tool detected, emitting agent:todos_update", {
45325
+ agentId: proc.agentId,
45326
+ replyMessageId: base.replyMessageId,
45327
+ groupId: proc.currentTask?.groupId,
45328
+ toolName,
45329
+ todoCount: todos.length,
45330
+ statusBreakdown: countByStatus(todos),
45331
+ traceId: base.traceId
45332
+ });
45333
+ emit({
45334
+ type: "agent:todos_update",
45335
+ payload: {
45336
+ ...wireBase(base),
45337
+ groupId: proc.currentTask?.groupId,
45338
+ todos
45339
+ }
45340
+ });
45341
+ }
45342
+ function applyTaskCreateInput(proc, input) {
45343
+ if (!isObjectRecord(input)) return false;
45344
+ const rawTaskId = String(proc.taskToolNextId ?? 1);
45345
+ proc.taskToolNextId = Number(rawTaskId) + 1;
45346
+ const content = readTaskToolContent(input) ?? `\u4EFB\u52A1 ${rawTaskId}`;
45347
+ const todoId = normalizeTaskToolTodoId(rawTaskId);
45348
+ proc.taskToolTodos = proc.taskToolTodos ?? {};
45349
+ proc.taskToolOrder = proc.taskToolOrder ?? [];
45350
+ if (!proc.taskToolOrder.includes(todoId)) {
45351
+ proc.taskToolOrder.push(todoId);
45352
+ }
45353
+ proc.taskToolTodos[todoId] = {
45354
+ id: todoId,
45355
+ content,
45356
+ status: "pending"
45357
+ };
45358
+ return true;
45359
+ }
45360
+ function applyTaskUpdateInput(proc, input) {
45361
+ if (!isObjectRecord(input)) return false;
45362
+ const taskIdValue = input.taskId;
45363
+ const rawTaskId = typeof taskIdValue === "string" || typeof taskIdValue === "number" ? String(taskIdValue) : null;
45364
+ if (!rawTaskId || rawTaskId.trim().length === 0) return false;
45365
+ const normalizedRawTaskId = rawTaskId.trim();
45366
+ const todoId = normalizeTaskToolTodoId(normalizedRawTaskId);
45367
+ proc.taskToolTodos = proc.taskToolTodos ?? {};
45368
+ proc.taskToolOrder = proc.taskToolOrder ?? [];
45369
+ const existing = proc.taskToolTodos[todoId];
45370
+ const content = existing?.content ?? readTaskToolContent(input);
45371
+ if (!content) return false;
45372
+ if (!proc.taskToolOrder.includes(todoId)) {
45373
+ proc.taskToolOrder.push(todoId);
45374
+ }
45375
+ const status = normalizeTaskToolStatus(input.status);
45376
+ proc.taskToolTodos[todoId] = {
45377
+ id: todoId,
45378
+ content,
45379
+ status
45380
+ };
45381
+ return true;
45382
+ }
45383
+ function normalizeTaskToolTodoId(rawTaskId) {
45384
+ const trimmed = rawTaskId.trim();
45385
+ return trimmed.startsWith("task_") ? trimmed : `task_${trimmed}`;
45386
+ }
45387
+ function readTaskToolContent(input) {
45388
+ const subject = readTrimmedString(input, "subject");
45389
+ const activeForm = readTrimmedString(input, "activeForm");
45390
+ const description = readTrimmedString(input, "description");
45391
+ return subject ?? activeForm ?? firstDescriptionLine(description);
45392
+ }
45108
45393
  function countByStatus(todos) {
45109
45394
  const c2 = {
45110
45395
  pending: 0,
@@ -45288,6 +45573,17 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted) {
45288
45573
  const partial2 = delta.partial_json;
45289
45574
  if (typeof partial2 === "string") {
45290
45575
  proc.accumulatedToolInput += partial2;
45576
+ const liveInput = extractLiveToolInput(proc.currentToolName, proc.accumulatedToolInput);
45577
+ if (liveInput && proc.currentToolName != null) {
45578
+ emit({
45579
+ type: "agent:tool_input_update",
45580
+ payload: {
45581
+ ...wireBase(base),
45582
+ toolName: proc.currentToolName,
45583
+ input: liveInput
45584
+ }
45585
+ });
45586
+ }
45291
45587
  }
45292
45588
  } else if (delta.type === "text_delta" && typeof delta.text === "string") {
45293
45589
  if (proc.accumulatedText.length === 0) {
@@ -45342,6 +45638,16 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted) {
45342
45638
  if (lastToolUse && lastToolUse.type === "tool_use") {
45343
45639
  lastToolUse.input = parsedInput;
45344
45640
  }
45641
+ if (proc.currentToolName != null && LIVE_INPUT_PREVIEW_TOOLS.has(proc.currentToolName) && Object.keys(parsedInput).length > 0) {
45642
+ emit({
45643
+ type: "agent:tool_input_update",
45644
+ payload: {
45645
+ ...wireBase(base),
45646
+ toolName: proc.currentToolName,
45647
+ input: parsedInput
45648
+ }
45649
+ });
45650
+ }
45345
45651
  if (proc.currentToolName === "TodoWrite") {
45346
45652
  const todos = extractTodosFromInput(parsedInput);
45347
45653
  if (todos) {
@@ -45377,6 +45683,28 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted) {
45377
45683
  });
45378
45684
  }
45379
45685
  }
45686
+ if (proc.currentToolName === "TaskCreate") {
45687
+ if (applyTaskCreateInput(proc, parsedInput)) {
45688
+ emitTaskToolTodosUpdate(proc, emit, base, "TaskCreate");
45689
+ } else {
45690
+ logger7.warn("TaskCreate detected but input was not usable", {
45691
+ agentId: proc.agentId,
45692
+ replyMessageId: base.replyMessageId,
45693
+ traceId: base.traceId
45694
+ });
45695
+ }
45696
+ }
45697
+ if (proc.currentToolName === "TaskUpdate") {
45698
+ if (applyTaskUpdateInput(proc, parsedInput)) {
45699
+ emitTaskToolTodosUpdate(proc, emit, base, "TaskUpdate");
45700
+ } else {
45701
+ logger7.warn("TaskUpdate detected but input was not usable", {
45702
+ agentId: proc.agentId,
45703
+ replyMessageId: base.replyMessageId,
45704
+ traceId: base.traceId
45705
+ });
45706
+ }
45707
+ }
45380
45708
  if (proc.currentToolName === "AskUserQuestion") {
45381
45709
  const last = proc.contentBlocks[proc.contentBlocks.length - 1];
45382
45710
  if (last?.type === "tool_use" && last.toolName === "AskUserQuestion") {
@@ -45586,6 +45914,29 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted) {
45586
45914
  onCompleted();
45587
45915
  break;
45588
45916
  }
45917
+ if (proc.accumulatedText.length === 0 && proc.contentBlocks.length === 0) {
45918
+ cleanupPlanMode(proc, emit, base, "error");
45919
+ logger7.warn("SDK success produced empty assistant output; emitting agent:error", {
45920
+ agentId: proc.agentId,
45921
+ ackId: base.replyMessageId,
45922
+ tokenCount: usage.tokenCount,
45923
+ inputTokens: usage.inputTokens,
45924
+ cacheReadTokens: usage.cacheReadTokens,
45925
+ cacheCreationTokens: usage.cacheCreationTokens,
45926
+ assistantContent: proc.lastAssistantContentDescription,
45927
+ traceId: base.traceId
45928
+ });
45929
+ emit({
45930
+ type: "agent:error",
45931
+ payload: {
45932
+ ...wireBase(base),
45933
+ error: "Agent \u8FD4\u56DE\u4E86\u7A7A\u7ED3\u679C\uFF0C\u5DF2\u963B\u6B62\u7A7A\u6D88\u606F\u8986\u76D6\u601D\u8003\u6C14\u6CE1\u3002\u8BF7\u91CD\u8BD5\uFF1B\u5982\u679C\u53CD\u590D\u51FA\u73B0\uFF0C\u8BF7\u68C0\u67E5 Claude Code \u767B\u5F55\u72B6\u6001\u6216\u6A21\u578B\u914D\u7F6E\u3002"
45934
+ }
45935
+ });
45936
+ resetAccumulators(proc);
45937
+ onCompleted();
45938
+ break;
45939
+ }
45589
45940
  if (proc.accumulatedText) {
45590
45941
  proc.contentBlocks.push({ type: "text", content: proc.accumulatedText });
45591
45942
  }
@@ -45655,14 +46006,9 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted) {
45655
46006
  if (am2.isApiErrorMessage === true) {
45656
46007
  const base = getTaskBase(proc);
45657
46008
  if (!base) break;
45658
- const errorTexts = [];
45659
- for (const block of am2.message?.content ?? []) {
45660
- if (block?.type === "text" && typeof block.text === "string") {
45661
- errorTexts.push(block.text);
45662
- }
45663
- }
46009
+ const errorTexts = extractAssistantTextParts(am2.message?.content);
45664
46010
  const errorText = errorTexts.join("\n").trim() || `SDK ${am2.error ?? "api_error"}`;
45665
- const isAuthFail = am2.error === "authentication_failed" || /not logged in|please run \/login/i.test(errorText);
46011
+ const isAuthFail = isAuthFailureText(errorText, am2.error);
45666
46012
  if (isAuthFail) {
45667
46013
  sessionStore.delete(proc.agentId, proc.scope);
45668
46014
  }
@@ -45674,7 +46020,7 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted) {
45674
46020
  isAuthFail,
45675
46021
  traceId: base.traceId
45676
46022
  });
45677
- const friendlyError = isAuthFail ? `Claude Code SDK \u672A\u767B\u5F55\uFF08${errorText}\uFF09\u3002\u8BF7\u5728 bridge \u4E3B\u673A\u4E0A\u914D\u7F6E\u8BA4\u8BC1\uFF1A\u82E5\u8BE5 Agent \u7528 system \u8BA2\u9605\uFF0C\u5C06\u5176 subscriptionType \u8BBE\u4E3A "system"\uFF08\u8D70 ~/.claude\uFF09\uFF1B\u5426\u5219\u5728 ~/.ahchat/claude-config \u4E0B\u767B\u5F55\uFF0C\u6216\u5728 Agent config \u4E2D\u63D0\u4F9B apiKey / apiBaseUrl\u3002` : errorText;
46023
+ const friendlyError = isAuthFail ? buildAuthFailureMessage(errorText) : errorText;
45678
46024
  emit({
45679
46025
  type: "agent:error",
45680
46026
  payload: { ...wireBase(base), error: friendlyError }
@@ -45687,14 +46033,36 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted) {
45687
46033
  break;
45688
46034
  }
45689
46035
  if (proc.accumulatedText.length === 0 && proc.contentBlocks.length === 0) {
45690
- const captured = [];
45691
- for (const block of am2.message?.content ?? []) {
45692
- if (block?.type === "text" && typeof block.text === "string") {
45693
- captured.push(block.text);
45694
- }
45695
- }
46036
+ const captured = extractAssistantTextParts(am2.message?.content);
45696
46037
  if (captured.length > 0) {
45697
46038
  const text = captured.join("");
46039
+ if (isAuthFailureText(text, am2.error)) {
46040
+ const base = getTaskBase(proc);
46041
+ logger7.warn("SDK auth failure assistant detected without api-error flag, emitting agent:error", {
46042
+ agentId: proc.agentId,
46043
+ scope: proc.scope.kind === "single" ? "single" : proc.scope.groupId,
46044
+ sdkError: am2.error,
46045
+ errorText: text.slice(0, 200),
46046
+ traceId: base?.traceId,
46047
+ hasCurrentTask: base != null
46048
+ });
46049
+ if (base) {
46050
+ sessionStore.delete(proc.agentId, proc.scope);
46051
+ emit({
46052
+ type: "agent:error",
46053
+ payload: {
46054
+ ...wireBase(base),
46055
+ error: buildAuthFailureMessage(text)
46056
+ }
46057
+ });
46058
+ proc.apiErrorEmitted = true;
46059
+ }
46060
+ proc.contentBlocks = [];
46061
+ proc.accumulatedText = "";
46062
+ proc.accumulatedThinking = "";
46063
+ proc.segmentBuffer = "";
46064
+ break;
46065
+ }
45698
46066
  if (isContextOverflowText(text)) {
45699
46067
  const base = getTaskBase(proc);
45700
46068
  logger7.warn("SDK reported context overflow; auto-compact already failed inside SDK", {
@@ -45738,6 +46106,8 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted) {
45738
46106
  textLen: text.length,
45739
46107
  textSample: text.slice(0, 100)
45740
46108
  });
46109
+ } else {
46110
+ proc.lastAssistantContentDescription = describeAssistantContent(am2.message?.content);
45741
46111
  }
45742
46112
  }
45743
46113
  break;
@@ -45761,6 +46131,7 @@ function resetAccumulators(proc) {
45761
46131
  proc.accumulatedToolInput = "";
45762
46132
  proc.apiErrorEmitted = false;
45763
46133
  proc.peakContextUsage = void 0;
46134
+ proc.lastAssistantContentDescription = void 0;
45764
46135
  }
45765
46136
 
45766
46137
  // src/forkHistoryReplay.ts
@@ -45873,8 +46244,15 @@ var wsMetrics = new WsMetrics();
45873
46244
  // src/agentManager.ts
45874
46245
  var logger10 = createModuleLogger("agent.manager");
45875
46246
  var NODE_USER_UID = 1e3;
46247
+ var POST_MERGE_CONTINUATION_ROUTE_MS = 15e3;
45876
46248
  function isSmithAgent(agent) {
45877
- return agent.id === SMITH_AGENT_ID || agent.systemPrompt === SMITH_SYSTEM_PROMPT;
46249
+ if (agent.id === SMITH_AGENT_ID) return true;
46250
+ if (agent.systemPrompt === SMITH_SYSTEM_PROMPT) return true;
46251
+ const prompt = agent.systemPrompt ?? "";
46252
+ if (prompt.includes("\u4F60\u662F\u7279\u5DE5\u53F2\u5BC6\u65AF") && prompt.includes("create_agent")) return true;
46253
+ const name = agent.name?.toLowerCase() ?? "";
46254
+ const role = agent.role?.toLowerCase() ?? "";
46255
+ return role === "system" && (name.includes("\u53F2\u5BC6\u65AF") || name.includes("smith"));
45878
46256
  }
45879
46257
  function isRunningAsRoot() {
45880
46258
  try {
@@ -45883,6 +46261,13 @@ function isRunningAsRoot() {
45883
46261
  return false;
45884
46262
  }
45885
46263
  }
46264
+ async function chownForRootSpawn(targetPath, target) {
46265
+ try {
46266
+ await fs4.chown(targetPath, NODE_USER_UID, NODE_USER_UID);
46267
+ } catch (error51) {
46268
+ logger10.error("Best-effort root chown failed", { error: error51, target, path: targetPath });
46269
+ }
46270
+ }
45886
46271
  function readCronLockSnapshot() {
45887
46272
  try {
45888
46273
  const lockPath2 = path8.join(os5.homedir(), ".claude", "scheduled_tasks.lock");
@@ -45991,14 +46376,57 @@ var AgentManager = class {
45991
46376
  this.queryFn = QA$;
45992
46377
  return this.queryFn;
45993
46378
  }
46379
+ extractAhchatWorkspaceSuffix(requestedCwd) {
46380
+ const normalized = requestedCwd.trim().replace(/\\/g, "/");
46381
+ const marker = "/.ahchat/users/";
46382
+ const markerIndex = normalized.indexOf(marker);
46383
+ if (markerIndex >= 0) {
46384
+ const afterUsers = normalized.slice(markerIndex + marker.length);
46385
+ const workspaceMarker = "/workspaces/";
46386
+ const workspaceIndex = afterUsers.indexOf(workspaceMarker);
46387
+ if (workspaceIndex >= 0) {
46388
+ const suffix = afterUsers.slice(workspaceIndex + workspaceMarker.length);
46389
+ const parts = suffix.split("/").filter((part) => part && part !== "." && part !== "..");
46390
+ return parts.length > 0 ? parts.join(path8.sep) : null;
46391
+ }
46392
+ }
46393
+ const legacyMarker = "/.ahchat/";
46394
+ const legacyIndex = normalized.indexOf(legacyMarker);
46395
+ if (legacyIndex >= 0) {
46396
+ const firstSegment = normalized.slice(legacyIndex + legacyMarker.length).split("/").find(Boolean);
46397
+ if (firstSegment && /^(Agent|Group)-/.test(firstSegment)) {
46398
+ return firstSegment;
46399
+ }
46400
+ }
46401
+ return null;
46402
+ }
45994
46403
  fallbackCwd(agentConfig, scope, requestedCwd) {
45995
46404
  const normalized = requestedCwd.trim();
46405
+ const ahchatSuffix = this.extractAhchatWorkspaceSuffix(normalized);
46406
+ if (ahchatSuffix) {
46407
+ return path8.join(this.workspacesDir, ahchatSuffix);
46408
+ }
45996
46409
  const basename = normalized ? path8.basename(path8.normalize(normalized)) : "";
45997
46410
  const suffix = basename && basename !== "." && basename !== path8.sep ? basename : scope.kind === "group" ? `Group-${scope.groupId}` : agentConfig.id;
45998
46411
  return path8.join(this.workspacesDir, suffix);
45999
46412
  }
46413
+ remapServerWorkspaceCwd(agentConfig, scope, requestedCwd) {
46414
+ const remapped = this.fallbackCwd(agentConfig, scope, requestedCwd);
46415
+ const normalizedRequested = path8.normalize(requestedCwd);
46416
+ const normalizedRemapped = path8.normalize(remapped);
46417
+ if (this.extractAhchatWorkspaceSuffix(requestedCwd) && normalizedRequested !== normalizedRemapped) {
46418
+ logger10.info("Server working directory remapped to local Bridge workspace", {
46419
+ agentId: agentConfig.id,
46420
+ scope: scopeKey(scope),
46421
+ requested: requestedCwd,
46422
+ remapped
46423
+ });
46424
+ return remapped;
46425
+ }
46426
+ return requestedCwd;
46427
+ }
46000
46428
  async resolveRuntimeCwd(agentConfig, scope, requestedCwd) {
46001
- let cwd = requestedCwd;
46429
+ let cwd = this.remapServerWorkspaceCwd(agentConfig, scope, requestedCwd);
46002
46430
  if (isRunningAsRoot() && cwd.startsWith("/root/")) {
46003
46431
  cwd = this.fallbackCwd(agentConfig, scope, cwd);
46004
46432
  }
@@ -46631,19 +47059,10 @@ Do NOT use "..." as content \u2014 write specific, project-relevant content.`;
46631
47059
  options.resume = savedSessionId;
46632
47060
  }
46633
47061
  if (isRunningAsRoot()) {
46634
- try {
46635
- await fs4.chown(effectiveConfigDir, NODE_USER_UID, NODE_USER_UID);
46636
- } catch {
46637
- }
46638
- try {
46639
- await fs4.chown(agentCwd, NODE_USER_UID, NODE_USER_UID);
46640
- } catch {
46641
- }
47062
+ await chownForRootSpawn(effectiveConfigDir, "configDir");
47063
+ await chownForRootSpawn(agentCwd, "agentCwd");
46642
47064
  const settingsFilePath = path8.join(effectiveConfigDir, "settings.json");
46643
- try {
46644
- await fs4.chown(settingsFilePath, NODE_USER_UID, NODE_USER_UID);
46645
- } catch {
46646
- }
47065
+ await chownForRootSpawn(settingsFilePath, "settingsFile");
46647
47066
  options.spawnClaudeCodeProcess = (spawnOptions) => {
46648
47067
  const env2 = { ...spawnOptions.env, HOME: "/home/node" };
46649
47068
  return nodeSpawn(spawnOptions.command, spawnOptions.args, {
@@ -46809,7 +47228,7 @@ ${trimmed}`;
46809
47228
  lines.push(` workdir: ${currentCwd}`);
46810
47229
  } else {
46811
47230
  const a = this.agentRegistry?.getById(agentId);
46812
- const singleCwd = a?.workingDirectory || path8.join(os5.homedir(), ".ahchat", `Agent-${a?.name ?? agentId}-${agentId}`);
47231
+ const singleCwd = a?.workingDirectory || path8.join(this.workspacesDir, agentId);
46813
47232
  lines.push(` workdir: ${singleCwd}`);
46814
47233
  }
46815
47234
  let rosterCount = 0;
@@ -46821,7 +47240,7 @@ ${trimmed}`;
46821
47240
  if (key === curKey) {
46822
47241
  lines.push(` workdir: ${currentCwd}`);
46823
47242
  } else {
46824
- const groupCwd = defaultGroupWorkdir(os5.homedir(), g2.name, g2.groupId);
47243
+ const groupCwd = g2.workingDirectory || path8.join(this.workspacesDir, g2.groupId);
46825
47244
  lines.push(` workdir: ${groupCwd}`);
46826
47245
  }
46827
47246
  const others = g2.members.filter((id) => id !== agentId).map((id) => {
@@ -47168,6 +47587,16 @@ ${lines.join("\n")}`;
47168
47587
  }
47169
47588
  }
47170
47589
  async dispatchToSDK(runtime, task) {
47590
+ if (runtime.postMergeContinuationTask) {
47591
+ logger10.info("Clearing stale post-merge continuation route before explicit dispatch", {
47592
+ agentId: runtime.agentId,
47593
+ staleReplyMessageId: runtime.postMergeContinuationTask.replyMessageId,
47594
+ nextReplyMessageId: task.replyMessageId,
47595
+ traceId: task.traceId
47596
+ });
47597
+ delete runtime.postMergeContinuationTask;
47598
+ delete runtime.postMergeContinuationUntil;
47599
+ }
47171
47600
  await this.applyEffortMode(runtime, "high", {
47172
47601
  source: "dispatchToSDK",
47173
47602
  replyMessageId: task.replyMessageId
@@ -47376,6 +47805,19 @@ ${lines.join("\n")}`;
47376
47805
  }
47377
47806
  });
47378
47807
  }
47808
+ const continuationTask = mergedBatch.at(-1);
47809
+ if (continuationTask) {
47810
+ proc.postMergeContinuationTask = continuationTask;
47811
+ proc.postMergeContinuationUntil = Date.now() + POST_MERGE_CONTINUATION_ROUTE_MS;
47812
+ logger10.info("Armed SDK post-merge continuation routing", {
47813
+ agentId: proc.agentId,
47814
+ carrierReplyMessageId: completedTask.replyMessageId,
47815
+ continuationReplyMessageId: continuationTask.replyMessageId,
47816
+ mergedCount: mergedBatch.length,
47817
+ routeUntil: new Date(proc.postMergeContinuationUntil).toISOString(),
47818
+ traceId: completedTask.traceId
47819
+ });
47820
+ }
47379
47821
  runtime.mergedTasks = [];
47380
47822
  } else if (runtime.mergedTasks.length > 0) {
47381
47823
  logger10.warn("mergedTasks non-empty but no currentTask; dropping", {
@@ -50117,6 +50559,13 @@ import { execFileSync, execSync, spawnSync } from "child_process";
50117
50559
  import { accessSync, constants as constants2 } from "fs";
50118
50560
  import { join as join2 } from "path";
50119
50561
  var logger24 = createModuleLogger("bridge.ensureCli");
50562
+ var DEFAULT_INSTALL_TIMEOUT_MS = 6e5;
50563
+ function getInstallTimeoutMs() {
50564
+ const raw = process.env.AHCHAT_CLAUDE_CLI_INSTALL_TIMEOUT_MS;
50565
+ if (!raw) return DEFAULT_INSTALL_TIMEOUT_MS;
50566
+ const parsed = Number.parseInt(raw, 10);
50567
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : DEFAULT_INSTALL_TIMEOUT_MS;
50568
+ }
50120
50569
  function detectClaudeCli() {
50121
50570
  try {
50122
50571
  return execFileSync("claude", ["--version"], { timeout: 1e4 }).toString().trim();
@@ -50145,9 +50594,18 @@ function resolveClaudeBinary() {
50145
50594
  if (first) return first;
50146
50595
  } catch {
50147
50596
  }
50597
+ return resolveViaNpmBin();
50598
+ }
50599
+ function getNpmClaudeCandidates(bin) {
50600
+ if (process.platform === "win32") {
50601
+ return ["claude.cmd", "claude.exe", "claude", "anthropic-cli.cmd", "anthropic-cli.exe", "anthropic-cli"].map((name) => join2(bin, name));
50602
+ }
50603
+ return [join2(bin, "claude"), join2(bin, "anthropic-cli")];
50604
+ }
50605
+ function resolveViaNpmBin() {
50148
50606
  const bin = getNpmGlobalBin();
50149
- if (bin) {
50150
- const candidate = join2(bin, process.platform === "win32" ? "claude.cmd" : "claude");
50607
+ if (!bin) return void 0;
50608
+ for (const candidate of getNpmClaudeCandidates(bin)) {
50151
50609
  try {
50152
50610
  accessSync(candidate, constants2.X_OK);
50153
50611
  return candidate;
@@ -50157,9 +50615,30 @@ function resolveClaudeBinary() {
50157
50615
  return void 0;
50158
50616
  }
50159
50617
  function detectViaNpmBin() {
50160
- const bin = getNpmGlobalBin();
50161
- if (!bin) return void 0;
50162
- const candidates = [join2(bin, "claude"), join2(bin, "anthropic-cli")];
50618
+ const binPath = resolveViaNpmBin();
50619
+ if (!binPath) return void 0;
50620
+ try {
50621
+ return execFileSync(binPath, ["--version"], { timeout: 1e4 }).toString().trim();
50622
+ } catch {
50623
+ }
50624
+ return void 0;
50625
+ }
50626
+ function detectResolvedClaudeCli() {
50627
+ const viaPath = detectClaudeCli();
50628
+ if (viaPath) {
50629
+ return { version: viaPath, path: resolveClaudeBinary() };
50630
+ }
50631
+ const npmBinPath = resolveViaNpmBin();
50632
+ if (!npmBinPath) return void 0;
50633
+ try {
50634
+ const version2 = execFileSync(npmBinPath, ["--version"], { timeout: 1e4 }).toString().trim();
50635
+ return { version: version2, path: npmBinPath };
50636
+ } catch {
50637
+ }
50638
+ return void 0;
50639
+ }
50640
+ function detectVersionFromResolvedCandidates() {
50641
+ const candidates = [resolveClaudeBinary(), resolveViaNpmBin()].filter((p) => Boolean(p));
50163
50642
  for (const p of candidates) {
50164
50643
  try {
50165
50644
  accessSync(p, constants2.X_OK);
@@ -50173,7 +50652,7 @@ function installClaudeCli() {
50173
50652
  logger24.info("Installing Claude Code CLI via npm...");
50174
50653
  const result = spawnSync("npm", ["install", "-g", "@anthropic-ai/claude-code"], {
50175
50654
  stdio: "inherit",
50176
- timeout: 12e4
50655
+ timeout: getInstallTimeoutMs()
50177
50656
  });
50178
50657
  if (result.error) {
50179
50658
  logger24.error("npm install -g failed (spawn error)", {
@@ -50188,9 +50667,9 @@ function installClaudeCli() {
50188
50667
  return void 0;
50189
50668
  }
50190
50669
  logger24.info("npm install -g completed, checking for claude binary...");
50191
- const viaPath = detectClaudeCli();
50670
+ const viaPath = detectVersionFromResolvedCandidates();
50192
50671
  if (viaPath) {
50193
- logger24.info("claude detected via PATH after install", { version: viaPath });
50672
+ logger24.info("claude detected after install", { version: viaPath });
50194
50673
  return viaPath;
50195
50674
  }
50196
50675
  const viaBin = detectViaNpmBin();
@@ -50208,14 +50687,13 @@ function installClaudeCli() {
50208
50687
  return void 0;
50209
50688
  }
50210
50689
  async function ensureClaudeCli() {
50211
- let version2 = detectClaudeCli();
50212
- if (version2) {
50213
- const binPath2 = resolveClaudeBinary();
50214
- logger24.info("Claude Code CLI ready", { version: version2, path: binPath2 ?? null });
50215
- return binPath2;
50690
+ let detected = detectResolvedClaudeCli();
50691
+ if (detected) {
50692
+ logger24.info("Claude Code CLI ready", { version: detected.version, path: detected.path ?? null });
50693
+ return detected.path;
50216
50694
  }
50217
50695
  process.stderr.write("\n\u26A0 Claude Code CLI (`claude`) not found. Attempting auto-install...\n\n");
50218
- version2 = installClaudeCli();
50696
+ const version2 = installClaudeCli();
50219
50697
  if (!version2) {
50220
50698
  const npmBin = getNpmGlobalBin();
50221
50699
  process.stderr.write(
@@ -50226,9 +50704,9 @@ async function ensureClaudeCli() {
50226
50704
  );
50227
50705
  process.exit(1);
50228
50706
  }
50229
- const binPath = resolveClaudeBinary();
50230
- logger24.info("Claude Code CLI ready", { version: version2, path: binPath ?? null });
50231
- return binPath;
50707
+ detected = detectResolvedClaudeCli();
50708
+ logger24.info("Claude Code CLI ready", { version: version2, path: detected?.path ?? null });
50709
+ return detected?.path;
50232
50710
  }
50233
50711
 
50234
50712
  // src/forkAgentFiles.ts
@@ -50665,6 +51143,7 @@ Bridge token (register this machine at Settings \u2192 \u5DF2\u8FDE\u63A5\u7684\
50665
51143
  agentRegistry,
50666
51144
  subscriptionRegistry,
50667
51145
  serverApiUrl: config2.serverApiUrl,
51146
+ bridgeToken: config2.bridgeToken,
50668
51147
  dataDir: config2.dataDir
50669
51148
  });
50670
51149
  const taskDispatchHandler = createTaskDispatchHandler(agentManager, agentRegistry, emit);