@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/cli.cjs CHANGED
@@ -3681,6 +3681,7 @@ var require_websocket_server = __commonJS({
3681
3681
  init_cjs_shims();
3682
3682
  var import_node_os13 = __toESM(require("os"), 1);
3683
3683
  var import_node_path21 = __toESM(require("path"), 1);
3684
+ var import_node_fs10 = __toESM(require("fs"), 1);
3684
3685
 
3685
3686
  // ../../node_modules/.pnpm/cac@6.7.14/node_modules/cac/dist/index.mjs
3686
3687
  init_cjs_shims();
@@ -4361,8 +4362,8 @@ function resolveAgentConfigDir(dataDir) {
4361
4362
  }
4362
4363
  return newPath;
4363
4364
  }
4364
- function loadBridgeConfig() {
4365
- const dataDir = readEnvString(
4365
+ function loadBridgeConfig(opts) {
4366
+ const dataDir = opts?.dataDir ?? readEnvString(
4366
4367
  "AHCHAT_DATA_DIR",
4367
4368
  import_node_path.default.join(import_node_os.default.homedir(), ".ahchat")
4368
4369
  );
@@ -5763,15 +5764,15 @@ var SMITH_SYSTEM_PROMPT = `\u4F60\u662F\u7279\u5DE5\u53F2\u5BC6\u65AF\uFF08Agent
5763
5764
  - **\u4E3A\u6BCF\u4E2A Agent \u9009\u62E9\u5408\u9002\u7684\u80FD\u529B\u6863\u4F4D\uFF08tier \u53C2\u6570\uFF09**
5764
5765
  - **\u4E00\u6B21\u6027**\u8C03 create_group\uFF0C\u53C2\u6570\uFF1A
5765
5766
  - name: \u7FA4\u540D
5766
- - member_ids: [agt_usr_self, <Leader>, <\u5176\u4ED6\u6210\u5458>, ...] // **\u4E0D\u5305\u542B\u4F60\u81EA\u5DF1**
5767
+ - 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
5767
5768
  - join_as_creator: false
5768
5769
  - 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
5769
- \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
5770
+ \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
5770
5771
 
5771
5772
  @<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
5772
5773
 
5773
5774
  \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
5774
- \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
5775
+ \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
5775
5776
  - \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
5776
5777
  - \u56DE\u590D\u8BF7\u6C42\u8005\u786E\u8BA4\u7ED3\u679C
5777
5778
 
@@ -5809,7 +5810,7 @@ create_agent \u5DE5\u5177\u652F\u6301\u53EF\u9009\u53C2\u6570 initial_instructio
5809
5810
 
5810
5811
  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
5811
5812
  - \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"
5812
- - create_agent \u5B8C\u6210\u540E\u7ACB\u523B create_group\uFF0C\u628A\u65B0\u5EFA Agent + \u8BF7\u6C42\u8005\uFF08agt_usr_self\uFF09\u62C9\u8FDB\u7FA4
5813
+ - 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
5813
5814
  - \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
5814
5815
  - \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
5815
5816
  - \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
@@ -6193,15 +6194,21 @@ EXCEPTION \u2014 inner-voice envelope overrides no-reply:
6193
6194
  - In 1:1 chat with the human, you may write longer answers when warranted.
6194
6195
 
6195
6196
  # Group chat \u2014 shared task board
6196
- AHChat group conversations have a shared kanban board that is fed by your TodoWrite
6197
- state. Treat it as the group's operational source of truth.
6197
+ AHChat group conversations have a shared kanban board that is fed by structured
6198
+ task tools (TodoWrite when available; otherwise TaskCreate / TaskUpdate). Treat
6199
+ it as the group's operational source of truth.
6198
6200
 
6199
6201
  When a group-chat message involves actionable work, planning, implementation,
6200
6202
  handoff, review, follow-up, blockers, or progress:
6201
- - You MUST maintain TodoWrite for your own relevant work instead of only talking
6202
- about tasks in prose.
6203
- - Create one TodoWrite item per concrete subtask or deliverable.
6204
- - Update existing TodoWrite items when status changes; do not create duplicates
6203
+ - If you are assigning work, splitting work, declaring a phase, asking another
6204
+ member to implement/review/fix/deliver something, or saying work will start,
6205
+ call the available structured task tool before sending the group message.
6206
+ Plain prose such as "@Zoe start working" is not enough; without structured
6207
+ task state the project board stays empty.
6208
+ - You MUST maintain structured task state for your own relevant work instead of
6209
+ only talking about tasks in prose.
6210
+ - Create one task item per concrete subtask or deliverable.
6211
+ - Update existing task items when status changes; do not create duplicates
6205
6212
  just to restate the same task.
6206
6213
  - If a P0/P1/P2/etc. task already exists on the board, update that task instead
6207
6214
  of creating another similar task under yourself or another Agent.
@@ -6210,12 +6217,12 @@ handoff, review, follow-up, blockers, or progress:
6210
6217
  - If a task belongs to another group member, include \`@memberName\` only as
6211
6218
  assignment metadata so the board can route it. Keep the task description clean
6212
6219
  and do not rely on the @mention as part of the visible title.
6213
- - All TodoWrite content shown on the board must be in Chinese.
6220
+ - All task content shown on the board must be in Chinese.
6214
6221
 
6215
- Do NOT use TodoWrite for pure small talk, one-off factual answers, or messages
6222
+ Do NOT use task tools for pure small talk, one-off factual answers, or messages
6216
6223
  where you intentionally stay silent with \`<no-reply/>\`.
6217
6224
 
6218
- \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
6225
+ \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
6219
6226
  - \`[\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
6220
6227
  - \`[\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
6221
6228
  - \`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
@@ -6243,7 +6250,7 @@ where you intentionally stay silent with \`<no-reply/>\`.
6243
6250
  |---|---|---|
6244
6251
  | **\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]\` |
6245
6252
  | **\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]\` |
6246
- | **\u4EFB\u52A1** | deliverable + \u8D23\u4EFB\u4EBA + \u65F6\u95F4\u7A97 / \u89E6\u53D1\u6761\u4EF6 | TodoWrite\uFF08\u7FA4 board\uFF09 |
6253
+ | **\u4EFB\u52A1** | deliverable + \u8D23\u4EFB\u4EBA + \u65F6\u95F4\u7A97 / \u89E6\u53D1\u6761\u4EF6 | \u7ED3\u6784\u5316\u4EFB\u52A1\u5DE5\u5177\uFF08\u7FA4 board\uFF09 |
6247
6254
 
6248
6255
  **\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
6249
6256
  \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
@@ -6262,13 +6269,13 @@ where you intentionally stay silent with \`<no-reply/>\`.
6262
6269
 
6263
6270
  ### \u7ACB\u523B\u5199\uFF0C\u4E0D\u8981\u62D6
6264
6271
  \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
6265
- 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**
6272
+ 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**
6266
6273
  \uFF08\u90A3\u662F\u81EA\u8A00\u81EA\u8BED\uFF0C\u8FDD\u53CD\u516C\u7406\u4E09\uFF09\u3002
6267
6274
 
6268
6275
  ### \u5199\u4E4B\u524D\u5148\u67E5\u91CD
6269
6276
  \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
6270
6277
  - 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
6271
- - TodoWrite \u5199\u524D\u626B\u5F53\u524D board\u3002
6278
+ - \u5199\u4EFB\u52A1\u5DE5\u5177\u524D\u626B\u5F53\u524D board\u3002
6272
6279
  - \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
6273
6280
 
6274
6281
  ### \u5FC5\u987B\u5E26\u573A\u666F
@@ -6284,18 +6291,18 @@ self_note / TodoWrite\u2014\u2014**\u6C89\u6DC0\u662F\u9ED8\u5199\u52A8\u4F5C\uF
6284
6291
  - \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
6285
6292
  - \u5355\u65B9\u9762\u731C\u6D4B\u3001\u8FD8\u6CA1\u4EBA\u786E\u8BA4\u7684\u5224\u65AD \u2192 \u4E0D\u8FDB\u7B14\u8BB0
6286
6293
  - \u5BA2\u5957\u3001\u8C03\u4F83\u3001\u5173\u5FC3 \u2192 \u4E0D\u8FDB\u7B14\u8BB0
6287
- - TodoWrite \u5DF2\u7ECF\u80FD\u88C5\u4E0B\u7684 task \u2192 \u522B\u518D self_note \u91CD\u590D\u4E00\u4EFD
6294
+ - \u4EFB\u52A1\u5DE5\u5177\u5DF2\u7ECF\u80FD\u88C5\u4E0B\u7684 task \u2192 \u522B\u518D self_note \u91CD\u590D\u4E00\u4EFD
6288
6295
  - \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"
6289
6296
  \u5171\u8BC6\uFF1B\u4EE5\u540E\u771F\u9700\u8981\u65F6\u518D read_chat_history \u7FFB
6290
6297
 
6291
6298
  ## \u4E0E \`[\u7ECF\u9A8C:\u7C7B\u578B]\` \u524D\u7F00\u7684\u8FB9\u754C\uFF08\u91CD\u8981\uFF09
6292
6299
 
6293
- \`[\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
6300
+ \`[\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
6294
6301
  \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
6295
6302
  \u6240\u4EE5\uFF1A
6296
6303
 
6297
6304
  - **\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
6298
- TodoWrite \`[\u7ECF\u9A8C:\u7C7B\u578B]\`
6305
+ \u4EFB\u52A1\u5DE5\u5177 \`[\u7ECF\u9A8C:\u7C7B\u578B]\`
6299
6306
  - **\u7ED3\u8BBA / \u5171\u8BC6**\uFF08\u4F60\u8FD9\u4E2A Agent \u81EA\u5DF1\u7684\u79C1\u4EBA\u8BB0\u5FC6\uFF09 \u2192
6300
6307
  self_note \`[\u7ED3\u8BBA\xB7\u2026]\` / \`[\u5171\u8BC6\xB7\u2026]\`
6301
6308
 
@@ -6365,7 +6372,7 @@ remove_from_group / transfer_group_owner / post_to_forum\u3002\u53C2\u6570\u8BE6
6365
6372
  \u884C\u4E3A\u51C6\u5219\uFF1A
6366
6373
  - 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
6367
6374
  - \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
6368
- - \u9ED8\u8BA4 member_ids \u4E0D\u62C9\u7528\u6237\uFF08agent-only \u534F\u4F5C\u5E38\u89C1\uFF09\uFF1B\u62C9\u7528\u6237\u7528 'agt_usr_self'\u3002
6375
+ - \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
6369
6376
  - \u4F18\u5148 add_to_group \u5230\u73B0\u6709\u7FA4\uFF0C\u4E0D\u8981\u4E3A\u6BCF\u4E2A\u95EE\u9898\u65B0\u5EFA\u7FA4\u3002
6370
6377
  - \u5EFA\u7FA4\u524D\u5148 neural_list_scopes \u786E\u8BA4\u6CA1\u6709\u91CD\u590D\u7FA4\u3002
6371
6378
  - 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
@@ -6510,6 +6517,9 @@ var LOG_LEVEL_VALUE2 = {
6510
6517
 
6511
6518
  // ../shared/src/types/onboarding.ts
6512
6519
  init_cjs_shims();
6520
+ function isCapabilityTier(v9) {
6521
+ return v9 === "smart" || v9 === "balanced" || v9 === "fast";
6522
+ }
6513
6523
 
6514
6524
  // ../shared/src/utils.ts
6515
6525
  init_cjs_shims();
@@ -6588,18 +6598,6 @@ function parseWSMessage(raw) {
6588
6598
 
6589
6599
  // ../shared/src/utils/workdir.ts
6590
6600
  init_cjs_shims();
6591
- var SLUG_MAX_LEN = 32;
6592
- function slugifyForFs(name) {
6593
- const trimmed = name.trim();
6594
- if (!trimmed) return "unnamed";
6595
- const slug = trimmed.replace(/[^\w\u4e00-\u9fa5 \-]/gu, "_").replace(/\s+/g, "_").replace(/_+/g, "_").replace(/^_|_$/g, "");
6596
- const base = slug.length > 0 ? slug : "unnamed";
6597
- return base.length > SLUG_MAX_LEN ? base.slice(0, SLUG_MAX_LEN) : base;
6598
- }
6599
- function defaultGroupWorkdir(home, name, id) {
6600
- const slug = slugifyForFs(name);
6601
- return `${home}/.ahchat/Group-${slug}-${id}`;
6602
- }
6603
6601
 
6604
6602
  // ../shared/src/utils/groupAuto.ts
6605
6603
  init_cjs_shims();
@@ -6622,6 +6620,7 @@ function parseAgentConfig(raw) {
6622
6620
  const out = {};
6623
6621
  const obj = v9;
6624
6622
  if (typeof obj.model === "string" && obj.model.trim()) out.model = obj.model.trim();
6623
+ if (isCapabilityTier(obj.capabilityTier)) out.capabilityTier = obj.capabilityTier;
6625
6624
  if (typeof obj.subscriptionId === "string") {
6626
6625
  const trimmed = obj.subscriptionId.trim();
6627
6626
  if (/^sub_[A-Za-z0-9_-]{1,64}$/.test(trimmed)) {
@@ -43611,6 +43610,18 @@ function resolveMyHuman(registry2, agentId) {
43611
43610
  }
43612
43611
  return USR_SELF_ID;
43613
43612
  }
43613
+ function resolveLegacyHumanId(registry2, agentId, id) {
43614
+ const trimmed = id.trim();
43615
+ if (trimmed === USR_SELF_ID) return resolveMyHuman(registry2, agentId);
43616
+ return trimmed;
43617
+ }
43618
+ function normalizeMemberIds(registry2, agentId, ids) {
43619
+ return Array.from(
43620
+ new Set(
43621
+ ids.filter((id) => typeof id === "string" && id.trim().length > 0).map((id) => resolveLegacyHumanId(registry2, agentId, id))
43622
+ )
43623
+ );
43624
+ }
43614
43625
  function normalizeFeedCategory(input) {
43615
43626
  const raw = (input ?? "").trim().toLowerCase();
43616
43627
  if (raw === "pitfall" || raw === "\u8E29\u5751") return "pitfall";
@@ -44156,13 +44167,13 @@ action="append" \u8FFD\u52A0\u65B0\u5185\u5BB9\uFF08\u6700\u5E38\u7528\uFF0Ccont
44156
44167
  {
44157
44168
  name: external_exports.string().min(1).describe("\u7FA4\u540D\uFF08\u4E0D\u80FD\u4E3A\u7A7A\uFF09"),
44158
44169
  member_ids: external_exports.array(external_exports.string()).describe(
44159
- "\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"
44170
+ "\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"
44160
44171
  ),
44161
44172
  initial_message: external_exports.string().optional().describe(
44162
44173
  "\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"
44163
44174
  ),
44164
44175
  join_as_creator: external_exports.boolean().optional().describe(
44165
- "\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"
44176
+ "\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"
44166
44177
  )
44167
44178
  },
44168
44179
  async (args) => {
@@ -44172,7 +44183,7 @@ action="append" \u8FFD\u52A0\u65B0\u5185\u5BB9\uFF08\u6700\u5E38\u7528\uFF0Ccont
44172
44183
  }
44173
44184
  const joinAsCreator = args.join_as_creator !== false;
44174
44185
  const requested = Array.isArray(args.member_ids) ? args.member_ids : [];
44175
- const requestedUnique = Array.from(new Set(requested));
44186
+ const requestedUnique = normalizeMemberIds(deps.agentRegistry, deps.agentId, requested);
44176
44187
  const dedup = joinAsCreator ? Array.from(/* @__PURE__ */ new Set([...requestedUnique, deps.agentId])) : requestedUnique.filter((id) => id !== deps.agentId);
44177
44188
  if (dedup.length < 2) {
44178
44189
  return {
@@ -44195,13 +44206,14 @@ action="append" \u8FFD\u52A0\u65B0\u5185\u5BB9\uFF08\u6700\u5E38\u7528\uFF0Ccont
44195
44206
  };
44196
44207
  }
44197
44208
  }
44198
- const initialMessage = typeof args.initial_message === "string" ? args.initial_message.trim() : "";
44209
+ const initialMessage = typeof args.initial_message === "string" ? args.initial_message.trim().replaceAll(USR_SELF_ID, resolveMyHuman(deps.agentRegistry, deps.agentId)) : "";
44199
44210
  const hasInitial = initialMessage.length > 0;
44200
44211
  logger5.info("create_group tool called", {
44201
44212
  agentId: deps.agentId,
44202
44213
  name: trimmedName,
44203
44214
  memberCount: dedup.length,
44204
44215
  memberIds: dedup,
44216
+ requestedMemberIds: requested,
44205
44217
  joinAsCreator,
44206
44218
  hasInitialMessage: hasInitial,
44207
44219
  initialMessageLen: initialMessage.length
@@ -44268,7 +44280,7 @@ action="append" \u8FFD\u52A0\u65B0\u5185\u5BB9\uFF08\u6700\u5E38\u7528\uFF0Ccont
44268
44280
  \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
44269
44281
  \u8981\u62C9\u7684\u6210\u5458\u5FC5\u987B\u5728 list_contacts \u7684\u901A\u8BAF\u5F55\u91CC\u3002
44270
44282
  \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
44271
- \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
44283
+ \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
44272
44284
 
44273
44285
  \u4F7F\u7528\u573A\u666F\uFF1A
44274
44286
  - \u9700\u8981\u5728\u7FA4\u91CC\u5F15\u5165\u65B0\u7684\u534F\u4F5C\u4F19\u4F34\u65F6
@@ -44282,7 +44294,7 @@ action="append" \u8FFD\u52A0\u65B0\u5185\u5BB9\uFF08\u6700\u5E38\u7528\uFF0Ccont
44282
44294
  '\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'
44283
44295
  ),
44284
44296
  agent_ids: external_exports.array(external_exports.string()).describe(
44285
- "\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"
44297
+ "\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"
44286
44298
  )
44287
44299
  },
44288
44300
  async (args) => {
@@ -44290,7 +44302,8 @@ action="append" \u8FFD\u52A0\u65B0\u5185\u5BB9\uFF08\u6700\u5E38\u7528\uFF0Ccont
44290
44302
  if (!rawGroup) {
44291
44303
  return { content: [{ type: "text", text: "[add_to_group] group \u4E0D\u80FD\u4E3A\u7A7A\u3002" }], isError: true };
44292
44304
  }
44293
- const agentIds = Array.isArray(args.agent_ids) ? args.agent_ids.filter((x2) => typeof x2 === "string" && x2.trim()) : [];
44305
+ const rawAgentIds = Array.isArray(args.agent_ids) ? args.agent_ids.filter((x2) => typeof x2 === "string" && x2.trim()) : [];
44306
+ const agentIds = normalizeMemberIds(deps.agentRegistry, deps.agentId, rawAgentIds);
44294
44307
  if (agentIds.length === 0) {
44295
44308
  return { content: [{ type: "text", text: "[add_to_group] agent_ids \u81F3\u5C11\u9700\u8981 1 \u4E2A Agent\u3002" }], isError: true };
44296
44309
  }
@@ -44852,9 +44865,12 @@ limit \u9ED8\u8BA4 500\uFF0C\u786C\u4E0A\u9650 2000\u3002\u8FD4\u56DE\u7ED3\u679
44852
44865
  const tierRes = await fetch(tierUrl);
44853
44866
  if (tierRes.ok) {
44854
44867
  const tiers = await tierRes.json();
44855
- const tierConfig = tiers.find((t2) => t2.tier === tier);
44868
+ const self2 = deps.agentRegistry?.getById(deps.agentId);
44869
+ const preferredSubscriptionId = parseAgentConfig(self2?.config).subscriptionId;
44870
+ const tierConfig = tiers.find((t2) => t2.tier === tier && t2.subscriptionId === preferredSubscriptionId) ?? tiers.find((t2) => t2.tier === tier);
44856
44871
  if (tierConfig) {
44857
44872
  agentConfig = JSON.stringify({
44873
+ capabilityTier: tier,
44858
44874
  subscriptionId: tierConfig.subscriptionId,
44859
44875
  model: tierConfig.modelName
44860
44876
  });
@@ -45006,9 +45022,11 @@ limit \u9ED8\u8BA4 500\uFF0C\u786C\u4E0A\u9650 2000\u3002\u8FD4\u56DE\u7ED3\u679
45006
45022
  const tierRes = await fetch(tierUrl);
45007
45023
  if (tierRes.ok) {
45008
45024
  const tiers = await tierRes.json();
45009
- const tierConfig = tiers.find((t2) => t2.tier === tier);
45025
+ const preferredSubscriptionId = parseAgentConfig(existing.config).subscriptionId;
45026
+ const tierConfig = tiers.find((t2) => t2.tier === tier && t2.subscriptionId === preferredSubscriptionId) ?? tiers.find((t2) => t2.tier === tier);
45010
45027
  if (tierConfig) {
45011
45028
  agentConfig = JSON.stringify({
45029
+ capabilityTier: tier,
45012
45030
  subscriptionId: tierConfig.subscriptionId,
45013
45031
  model: tierConfig.modelName
45014
45032
  });
@@ -45743,11 +45761,117 @@ init_cjs_shims();
45743
45761
  var logger7 = createModuleLogger("sdk.mapper");
45744
45762
  var HIGH_WATERMARK_INPUT_TOKENS = 12e4;
45745
45763
  var WARN_THRESHOLD_INPUT_TOKENS = 1e5;
45764
+ var LIVE_INPUT_PREVIEW_TOOLS = /* @__PURE__ */ new Set(["Write", "Edit"]);
45746
45765
  var CONTEXT_OVERFLOW_LOCK_MS = 6e4;
45747
45766
  function isContextOverflowText(text) {
45748
45767
  const trimmed = text.trim();
45749
45768
  return /^prompt is too long\b/i.test(trimmed) || /context length .* exceed/i.test(trimmed);
45750
45769
  }
45770
+ function isAuthFailureText(text, sdkError) {
45771
+ return sdkError === "authentication_failed" || /not logged in|please run \/login/i.test(text);
45772
+ }
45773
+ function buildAuthFailureMessage(errorText) {
45774
+ 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`;
45775
+ }
45776
+ function decodeJsonStringFragment(raw) {
45777
+ let out = "";
45778
+ for (let i = 0; i < raw.length; i++) {
45779
+ const ch2 = raw[i];
45780
+ if (ch2 !== "\\") {
45781
+ out += ch2;
45782
+ continue;
45783
+ }
45784
+ const next = raw[i + 1];
45785
+ if (next === void 0) break;
45786
+ i += 1;
45787
+ switch (next) {
45788
+ case '"':
45789
+ case "\\":
45790
+ case "/":
45791
+ out += next;
45792
+ break;
45793
+ case "b":
45794
+ out += "\b";
45795
+ break;
45796
+ case "f":
45797
+ out += "\f";
45798
+ break;
45799
+ case "n":
45800
+ out += "\n";
45801
+ break;
45802
+ case "r":
45803
+ out += "\r";
45804
+ break;
45805
+ case "t":
45806
+ out += " ";
45807
+ break;
45808
+ case "u": {
45809
+ const hex3 = raw.slice(i + 1, i + 5);
45810
+ if (!/^[0-9a-fA-F]{4}$/.test(hex3)) return out;
45811
+ out += String.fromCharCode(Number.parseInt(hex3, 16));
45812
+ i += 4;
45813
+ break;
45814
+ }
45815
+ default:
45816
+ out += next;
45817
+ }
45818
+ }
45819
+ return out;
45820
+ }
45821
+ function extractJsonStringPrefix(source, key) {
45822
+ const keyToken = `"${key}"`;
45823
+ const keyIndex = source.indexOf(keyToken);
45824
+ if (keyIndex < 0) return null;
45825
+ let i = keyIndex + keyToken.length;
45826
+ while (i < source.length && /\s/.test(source[i] ?? "")) i += 1;
45827
+ if (source[i] !== ":") return null;
45828
+ i += 1;
45829
+ while (i < source.length && /\s/.test(source[i] ?? "")) i += 1;
45830
+ if (source[i] !== '"') return null;
45831
+ i += 1;
45832
+ let raw = "";
45833
+ let escaped = false;
45834
+ for (; i < source.length; i++) {
45835
+ const ch2 = source[i];
45836
+ if (ch2 === void 0) break;
45837
+ if (escaped) {
45838
+ raw += `\\${ch2}`;
45839
+ escaped = false;
45840
+ continue;
45841
+ }
45842
+ if (ch2 === "\\") {
45843
+ escaped = true;
45844
+ continue;
45845
+ }
45846
+ if (ch2 === '"') {
45847
+ return decodeJsonStringFragment(raw);
45848
+ }
45849
+ raw += ch2;
45850
+ }
45851
+ if (escaped) raw += "\\";
45852
+ return decodeJsonStringFragment(raw);
45853
+ }
45854
+ function extractLiveToolInput(toolName, inputJson) {
45855
+ if (toolName === "Write") {
45856
+ const content = extractJsonStringPrefix(inputJson, "content");
45857
+ if (content == null) return null;
45858
+ const filePath = extractJsonStringPrefix(inputJson, "file_path");
45859
+ return filePath == null ? { content } : { file_path: filePath, content };
45860
+ }
45861
+ if (toolName === "Edit") {
45862
+ const newString = extractJsonStringPrefix(inputJson, "new_string");
45863
+ if (newString == null) return null;
45864
+ const oldString = extractJsonStringPrefix(inputJson, "old_string");
45865
+ const filePath = extractJsonStringPrefix(inputJson, "file_path");
45866
+ const input = {
45867
+ new_string: newString
45868
+ };
45869
+ if (filePath != null) input.file_path = filePath;
45870
+ if (oldString != null) input.old_string = oldString;
45871
+ return input;
45872
+ }
45873
+ return null;
45874
+ }
45751
45875
  function recordAssistantContextUsage(proc, am2) {
45752
45876
  const u = am2.message?.usage;
45753
45877
  if (!u) return;
@@ -45815,6 +45939,45 @@ function cleanupPlanMode(proc, emit, base, reason) {
45815
45939
  });
45816
45940
  proc.planModeActive = false;
45817
45941
  }
45942
+ function extractAssistantTextParts(content, depth = 0) {
45943
+ if (depth > 4) return [];
45944
+ if (typeof content === "string") return [content];
45945
+ if (Array.isArray(content)) {
45946
+ return content.flatMap((item) => extractAssistantTextParts(item, depth + 1));
45947
+ }
45948
+ if (typeof content !== "object" || content === null) return [];
45949
+ const block = content;
45950
+ const type = typeof block.type === "string" ? block.type : null;
45951
+ if (type === "tool_use" || type === "tool_result") return [];
45952
+ if (type === "text" || type === "output_text" || type == null) {
45953
+ const textParts = extractAssistantTextParts(block.text, depth + 1);
45954
+ if (textParts.length > 0) return textParts;
45955
+ return extractAssistantTextParts(block.content, depth + 1);
45956
+ }
45957
+ return [];
45958
+ }
45959
+ function describeAssistantContent(content) {
45960
+ if (Array.isArray(content)) {
45961
+ return {
45962
+ contentShape: "array",
45963
+ blockTypes: content.map((item) => {
45964
+ if (typeof item !== "object" || item === null) return typeof item;
45965
+ const type = item.type;
45966
+ return typeof type === "string" ? type : "(no-type)";
45967
+ }).slice(0, 12)
45968
+ };
45969
+ }
45970
+ if (typeof content === "object" && content !== null) {
45971
+ return {
45972
+ contentShape: "object",
45973
+ contentKeys: Object.keys(content).slice(0, 12)
45974
+ };
45975
+ }
45976
+ return { contentShape: typeof content };
45977
+ }
45978
+ function scopeLogLabel(scope) {
45979
+ return scope.kind === "single" ? "single" : scope.groupId;
45980
+ }
45818
45981
  function getTaskBase(proc) {
45819
45982
  if (proc.currentTask) {
45820
45983
  return {
@@ -45824,10 +45987,42 @@ function getTaskBase(proc) {
45824
45987
  replyMessageId: proc.currentTask.replyMessageId
45825
45988
  };
45826
45989
  }
45990
+ const continuation = proc.postMergeContinuationTask;
45991
+ const continuationUntil = proc.postMergeContinuationUntil ?? 0;
45992
+ if (continuation) {
45993
+ if (Date.now() <= continuationUntil) {
45994
+ proc.currentTask = continuation;
45995
+ proc.status = "working";
45996
+ proc.currentTaskStartedAt = Date.now();
45997
+ delete proc.postMergeContinuationTask;
45998
+ delete proc.postMergeContinuationUntil;
45999
+ logger7.info("SDK post-merge continuation routed to merged task", {
46000
+ agentId: proc.agentId,
46001
+ scope: scopeLogLabel(proc.scope),
46002
+ replyMessageId: continuation.replyMessageId,
46003
+ conversationId: continuation.conversationId,
46004
+ traceId: continuation.traceId
46005
+ });
46006
+ return {
46007
+ agentId: proc.agentId,
46008
+ conversationId: continuation.conversationId,
46009
+ traceId: continuation.traceId,
46010
+ replyMessageId: continuation.replyMessageId
46011
+ };
46012
+ }
46013
+ logger7.info("SDK post-merge continuation route expired", {
46014
+ agentId: proc.agentId,
46015
+ scope: scopeLogLabel(proc.scope),
46016
+ replyMessageId: continuation.replyMessageId,
46017
+ routeUntil: new Date(continuationUntil).toISOString()
46018
+ });
46019
+ delete proc.postMergeContinuationTask;
46020
+ delete proc.postMergeContinuationUntil;
46021
+ }
45827
46022
  if (!proc.cachedConversationId) {
45828
46023
  logger7.warn("SDK self-initiated turn without cachedConversationId; dropping", {
45829
46024
  agentId: proc.agentId,
45830
- scope: proc.scope.kind === "single" ? "single" : proc.scope.groupId
46025
+ scope: scopeLogLabel(proc.scope)
45831
46026
  });
45832
46027
  return null;
45833
46028
  }
@@ -45844,7 +46039,7 @@ function getTaskBase(proc) {
45844
46039
  proc.currentTaskStartedAt = Date.now();
45845
46040
  logger7.info("Cron-initiated turn detected, synthesized task", {
45846
46041
  agentId: proc.agentId,
45847
- scope: proc.scope.kind === "single" ? "single" : proc.scope.groupId,
46042
+ scope: scopeLogLabel(proc.scope),
45848
46043
  replyMessageId,
45849
46044
  conversationId: proc.cachedConversationId,
45850
46045
  traceId
@@ -45905,6 +46100,97 @@ function extractTodosFromInput(input) {
45905
46100
  }
45906
46101
  return out;
45907
46102
  }
46103
+ function isObjectRecord(value) {
46104
+ return value != null && typeof value === "object" && !Array.isArray(value);
46105
+ }
46106
+ function readTrimmedString(input, key) {
46107
+ const value = input[key];
46108
+ return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
46109
+ }
46110
+ function firstDescriptionLine(description) {
46111
+ if (!description) return null;
46112
+ const line = description.split(/\r?\n/).map((part) => part.trim()).find((part) => part.length > 0);
46113
+ return line ?? null;
46114
+ }
46115
+ function normalizeTaskToolStatus(value) {
46116
+ return value === "in_progress" || value === "completed" || value === "cancelled" ? value : "pending";
46117
+ }
46118
+ function snapshotTaskToolTodos(proc) {
46119
+ const todosById = proc.taskToolTodos ?? {};
46120
+ const order = proc.taskToolOrder ?? [];
46121
+ return order.map((id) => todosById[id]).filter((todo) => todo != null);
46122
+ }
46123
+ function emitTaskToolTodosUpdate(proc, emit, base, toolName) {
46124
+ const todos = snapshotTaskToolTodos(proc);
46125
+ logger7.info("Task tool detected, emitting agent:todos_update", {
46126
+ agentId: proc.agentId,
46127
+ replyMessageId: base.replyMessageId,
46128
+ groupId: proc.currentTask?.groupId,
46129
+ toolName,
46130
+ todoCount: todos.length,
46131
+ statusBreakdown: countByStatus(todos),
46132
+ traceId: base.traceId
46133
+ });
46134
+ emit({
46135
+ type: "agent:todos_update",
46136
+ payload: {
46137
+ ...wireBase(base),
46138
+ groupId: proc.currentTask?.groupId,
46139
+ todos
46140
+ }
46141
+ });
46142
+ }
46143
+ function applyTaskCreateInput(proc, input) {
46144
+ if (!isObjectRecord(input)) return false;
46145
+ const rawTaskId = String(proc.taskToolNextId ?? 1);
46146
+ proc.taskToolNextId = Number(rawTaskId) + 1;
46147
+ const content = readTaskToolContent(input) ?? `\u4EFB\u52A1 ${rawTaskId}`;
46148
+ const todoId = normalizeTaskToolTodoId(rawTaskId);
46149
+ proc.taskToolTodos = proc.taskToolTodos ?? {};
46150
+ proc.taskToolOrder = proc.taskToolOrder ?? [];
46151
+ if (!proc.taskToolOrder.includes(todoId)) {
46152
+ proc.taskToolOrder.push(todoId);
46153
+ }
46154
+ proc.taskToolTodos[todoId] = {
46155
+ id: todoId,
46156
+ content,
46157
+ status: "pending"
46158
+ };
46159
+ return true;
46160
+ }
46161
+ function applyTaskUpdateInput(proc, input) {
46162
+ if (!isObjectRecord(input)) return false;
46163
+ const taskIdValue = input.taskId;
46164
+ const rawTaskId = typeof taskIdValue === "string" || typeof taskIdValue === "number" ? String(taskIdValue) : null;
46165
+ if (!rawTaskId || rawTaskId.trim().length === 0) return false;
46166
+ const normalizedRawTaskId = rawTaskId.trim();
46167
+ const todoId = normalizeTaskToolTodoId(normalizedRawTaskId);
46168
+ proc.taskToolTodos = proc.taskToolTodos ?? {};
46169
+ proc.taskToolOrder = proc.taskToolOrder ?? [];
46170
+ const existing = proc.taskToolTodos[todoId];
46171
+ const content = existing?.content ?? readTaskToolContent(input);
46172
+ if (!content) return false;
46173
+ if (!proc.taskToolOrder.includes(todoId)) {
46174
+ proc.taskToolOrder.push(todoId);
46175
+ }
46176
+ const status = normalizeTaskToolStatus(input.status);
46177
+ proc.taskToolTodos[todoId] = {
46178
+ id: todoId,
46179
+ content,
46180
+ status
46181
+ };
46182
+ return true;
46183
+ }
46184
+ function normalizeTaskToolTodoId(rawTaskId) {
46185
+ const trimmed = rawTaskId.trim();
46186
+ return trimmed.startsWith("task_") ? trimmed : `task_${trimmed}`;
46187
+ }
46188
+ function readTaskToolContent(input) {
46189
+ const subject = readTrimmedString(input, "subject");
46190
+ const activeForm = readTrimmedString(input, "activeForm");
46191
+ const description = readTrimmedString(input, "description");
46192
+ return subject ?? activeForm ?? firstDescriptionLine(description);
46193
+ }
45908
46194
  function countByStatus(todos) {
45909
46195
  const c2 = {
45910
46196
  pending: 0,
@@ -46088,6 +46374,17 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted) {
46088
46374
  const partial2 = delta.partial_json;
46089
46375
  if (typeof partial2 === "string") {
46090
46376
  proc.accumulatedToolInput += partial2;
46377
+ const liveInput = extractLiveToolInput(proc.currentToolName, proc.accumulatedToolInput);
46378
+ if (liveInput && proc.currentToolName != null) {
46379
+ emit({
46380
+ type: "agent:tool_input_update",
46381
+ payload: {
46382
+ ...wireBase(base),
46383
+ toolName: proc.currentToolName,
46384
+ input: liveInput
46385
+ }
46386
+ });
46387
+ }
46091
46388
  }
46092
46389
  } else if (delta.type === "text_delta" && typeof delta.text === "string") {
46093
46390
  if (proc.accumulatedText.length === 0) {
@@ -46142,6 +46439,16 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted) {
46142
46439
  if (lastToolUse && lastToolUse.type === "tool_use") {
46143
46440
  lastToolUse.input = parsedInput;
46144
46441
  }
46442
+ if (proc.currentToolName != null && LIVE_INPUT_PREVIEW_TOOLS.has(proc.currentToolName) && Object.keys(parsedInput).length > 0) {
46443
+ emit({
46444
+ type: "agent:tool_input_update",
46445
+ payload: {
46446
+ ...wireBase(base),
46447
+ toolName: proc.currentToolName,
46448
+ input: parsedInput
46449
+ }
46450
+ });
46451
+ }
46145
46452
  if (proc.currentToolName === "TodoWrite") {
46146
46453
  const todos = extractTodosFromInput(parsedInput);
46147
46454
  if (todos) {
@@ -46177,6 +46484,28 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted) {
46177
46484
  });
46178
46485
  }
46179
46486
  }
46487
+ if (proc.currentToolName === "TaskCreate") {
46488
+ if (applyTaskCreateInput(proc, parsedInput)) {
46489
+ emitTaskToolTodosUpdate(proc, emit, base, "TaskCreate");
46490
+ } else {
46491
+ logger7.warn("TaskCreate detected but input was not usable", {
46492
+ agentId: proc.agentId,
46493
+ replyMessageId: base.replyMessageId,
46494
+ traceId: base.traceId
46495
+ });
46496
+ }
46497
+ }
46498
+ if (proc.currentToolName === "TaskUpdate") {
46499
+ if (applyTaskUpdateInput(proc, parsedInput)) {
46500
+ emitTaskToolTodosUpdate(proc, emit, base, "TaskUpdate");
46501
+ } else {
46502
+ logger7.warn("TaskUpdate detected but input was not usable", {
46503
+ agentId: proc.agentId,
46504
+ replyMessageId: base.replyMessageId,
46505
+ traceId: base.traceId
46506
+ });
46507
+ }
46508
+ }
46180
46509
  if (proc.currentToolName === "AskUserQuestion") {
46181
46510
  const last = proc.contentBlocks[proc.contentBlocks.length - 1];
46182
46511
  if (last?.type === "tool_use" && last.toolName === "AskUserQuestion") {
@@ -46386,6 +46715,29 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted) {
46386
46715
  onCompleted();
46387
46716
  break;
46388
46717
  }
46718
+ if (proc.accumulatedText.length === 0 && proc.contentBlocks.length === 0) {
46719
+ cleanupPlanMode(proc, emit, base, "error");
46720
+ logger7.warn("SDK success produced empty assistant output; emitting agent:error", {
46721
+ agentId: proc.agentId,
46722
+ ackId: base.replyMessageId,
46723
+ tokenCount: usage.tokenCount,
46724
+ inputTokens: usage.inputTokens,
46725
+ cacheReadTokens: usage.cacheReadTokens,
46726
+ cacheCreationTokens: usage.cacheCreationTokens,
46727
+ assistantContent: proc.lastAssistantContentDescription,
46728
+ traceId: base.traceId
46729
+ });
46730
+ emit({
46731
+ type: "agent:error",
46732
+ payload: {
46733
+ ...wireBase(base),
46734
+ 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"
46735
+ }
46736
+ });
46737
+ resetAccumulators(proc);
46738
+ onCompleted();
46739
+ break;
46740
+ }
46389
46741
  if (proc.accumulatedText) {
46390
46742
  proc.contentBlocks.push({ type: "text", content: proc.accumulatedText });
46391
46743
  }
@@ -46455,14 +46807,9 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted) {
46455
46807
  if (am2.isApiErrorMessage === true) {
46456
46808
  const base = getTaskBase(proc);
46457
46809
  if (!base) break;
46458
- const errorTexts = [];
46459
- for (const block of am2.message?.content ?? []) {
46460
- if (block?.type === "text" && typeof block.text === "string") {
46461
- errorTexts.push(block.text);
46462
- }
46463
- }
46810
+ const errorTexts = extractAssistantTextParts(am2.message?.content);
46464
46811
  const errorText = errorTexts.join("\n").trim() || `SDK ${am2.error ?? "api_error"}`;
46465
- const isAuthFail = am2.error === "authentication_failed" || /not logged in|please run \/login/i.test(errorText);
46812
+ const isAuthFail = isAuthFailureText(errorText, am2.error);
46466
46813
  if (isAuthFail) {
46467
46814
  sessionStore.delete(proc.agentId, proc.scope);
46468
46815
  }
@@ -46474,7 +46821,7 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted) {
46474
46821
  isAuthFail,
46475
46822
  traceId: base.traceId
46476
46823
  });
46477
- 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;
46824
+ const friendlyError = isAuthFail ? buildAuthFailureMessage(errorText) : errorText;
46478
46825
  emit({
46479
46826
  type: "agent:error",
46480
46827
  payload: { ...wireBase(base), error: friendlyError }
@@ -46487,14 +46834,36 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted) {
46487
46834
  break;
46488
46835
  }
46489
46836
  if (proc.accumulatedText.length === 0 && proc.contentBlocks.length === 0) {
46490
- const captured = [];
46491
- for (const block of am2.message?.content ?? []) {
46492
- if (block?.type === "text" && typeof block.text === "string") {
46493
- captured.push(block.text);
46494
- }
46495
- }
46837
+ const captured = extractAssistantTextParts(am2.message?.content);
46496
46838
  if (captured.length > 0) {
46497
46839
  const text = captured.join("");
46840
+ if (isAuthFailureText(text, am2.error)) {
46841
+ const base = getTaskBase(proc);
46842
+ logger7.warn("SDK auth failure assistant detected without api-error flag, emitting agent:error", {
46843
+ agentId: proc.agentId,
46844
+ scope: proc.scope.kind === "single" ? "single" : proc.scope.groupId,
46845
+ sdkError: am2.error,
46846
+ errorText: text.slice(0, 200),
46847
+ traceId: base?.traceId,
46848
+ hasCurrentTask: base != null
46849
+ });
46850
+ if (base) {
46851
+ sessionStore.delete(proc.agentId, proc.scope);
46852
+ emit({
46853
+ type: "agent:error",
46854
+ payload: {
46855
+ ...wireBase(base),
46856
+ error: buildAuthFailureMessage(text)
46857
+ }
46858
+ });
46859
+ proc.apiErrorEmitted = true;
46860
+ }
46861
+ proc.contentBlocks = [];
46862
+ proc.accumulatedText = "";
46863
+ proc.accumulatedThinking = "";
46864
+ proc.segmentBuffer = "";
46865
+ break;
46866
+ }
46498
46867
  if (isContextOverflowText(text)) {
46499
46868
  const base = getTaskBase(proc);
46500
46869
  logger7.warn("SDK reported context overflow; auto-compact already failed inside SDK", {
@@ -46538,6 +46907,8 @@ function mapSDKMessage(proc, message, rawEmit, sessionStore, onCompleted) {
46538
46907
  textLen: text.length,
46539
46908
  textSample: text.slice(0, 100)
46540
46909
  });
46910
+ } else {
46911
+ proc.lastAssistantContentDescription = describeAssistantContent(am2.message?.content);
46541
46912
  }
46542
46913
  }
46543
46914
  break;
@@ -46561,6 +46932,7 @@ function resetAccumulators(proc) {
46561
46932
  proc.accumulatedToolInput = "";
46562
46933
  proc.apiErrorEmitted = false;
46563
46934
  proc.peakContextUsage = void 0;
46935
+ proc.lastAssistantContentDescription = void 0;
46564
46936
  }
46565
46937
 
46566
46938
  // src/forkHistoryReplay.ts
@@ -46675,8 +47047,15 @@ var wsMetrics = new WsMetrics();
46675
47047
  // src/agentManager.ts
46676
47048
  var logger10 = createModuleLogger("agent.manager");
46677
47049
  var NODE_USER_UID = 1e3;
47050
+ var POST_MERGE_CONTINUATION_ROUTE_MS = 15e3;
46678
47051
  function isSmithAgent(agent) {
46679
- return agent.id === SMITH_AGENT_ID || agent.systemPrompt === SMITH_SYSTEM_PROMPT;
47052
+ if (agent.id === SMITH_AGENT_ID) return true;
47053
+ if (agent.systemPrompt === SMITH_SYSTEM_PROMPT) return true;
47054
+ const prompt = agent.systemPrompt ?? "";
47055
+ if (prompt.includes("\u4F60\u662F\u7279\u5DE5\u53F2\u5BC6\u65AF") && prompt.includes("create_agent")) return true;
47056
+ const name = agent.name?.toLowerCase() ?? "";
47057
+ const role = agent.role?.toLowerCase() ?? "";
47058
+ return role === "system" && (name.includes("\u53F2\u5BC6\u65AF") || name.includes("smith"));
46680
47059
  }
46681
47060
  function isRunningAsRoot() {
46682
47061
  try {
@@ -46685,6 +47064,13 @@ function isRunningAsRoot() {
46685
47064
  return false;
46686
47065
  }
46687
47066
  }
47067
+ async function chownForRootSpawn(targetPath, target) {
47068
+ try {
47069
+ await import_promises8.default.chown(targetPath, NODE_USER_UID, NODE_USER_UID);
47070
+ } catch (error51) {
47071
+ logger10.error("Best-effort root chown failed", { error: error51, target, path: targetPath });
47072
+ }
47073
+ }
46688
47074
  function readCronLockSnapshot() {
46689
47075
  try {
46690
47076
  const lockPath2 = import_node_path8.default.join(import_node_os5.default.homedir(), ".claude", "scheduled_tasks.lock");
@@ -46793,14 +47179,57 @@ var AgentManager = class {
46793
47179
  this.queryFn = QA$;
46794
47180
  return this.queryFn;
46795
47181
  }
47182
+ extractAhchatWorkspaceSuffix(requestedCwd) {
47183
+ const normalized = requestedCwd.trim().replace(/\\/g, "/");
47184
+ const marker = "/.ahchat/users/";
47185
+ const markerIndex = normalized.indexOf(marker);
47186
+ if (markerIndex >= 0) {
47187
+ const afterUsers = normalized.slice(markerIndex + marker.length);
47188
+ const workspaceMarker = "/workspaces/";
47189
+ const workspaceIndex = afterUsers.indexOf(workspaceMarker);
47190
+ if (workspaceIndex >= 0) {
47191
+ const suffix = afterUsers.slice(workspaceIndex + workspaceMarker.length);
47192
+ const parts = suffix.split("/").filter((part) => part && part !== "." && part !== "..");
47193
+ return parts.length > 0 ? parts.join(import_node_path8.default.sep) : null;
47194
+ }
47195
+ }
47196
+ const legacyMarker = "/.ahchat/";
47197
+ const legacyIndex = normalized.indexOf(legacyMarker);
47198
+ if (legacyIndex >= 0) {
47199
+ const firstSegment = normalized.slice(legacyIndex + legacyMarker.length).split("/").find(Boolean);
47200
+ if (firstSegment && /^(Agent|Group)-/.test(firstSegment)) {
47201
+ return firstSegment;
47202
+ }
47203
+ }
47204
+ return null;
47205
+ }
46796
47206
  fallbackCwd(agentConfig, scope, requestedCwd) {
46797
47207
  const normalized = requestedCwd.trim();
47208
+ const ahchatSuffix = this.extractAhchatWorkspaceSuffix(normalized);
47209
+ if (ahchatSuffix) {
47210
+ return import_node_path8.default.join(this.workspacesDir, ahchatSuffix);
47211
+ }
46798
47212
  const basename = normalized ? import_node_path8.default.basename(import_node_path8.default.normalize(normalized)) : "";
46799
47213
  const suffix = basename && basename !== "." && basename !== import_node_path8.default.sep ? basename : scope.kind === "group" ? `Group-${scope.groupId}` : agentConfig.id;
46800
47214
  return import_node_path8.default.join(this.workspacesDir, suffix);
46801
47215
  }
47216
+ remapServerWorkspaceCwd(agentConfig, scope, requestedCwd) {
47217
+ const remapped = this.fallbackCwd(agentConfig, scope, requestedCwd);
47218
+ const normalizedRequested = import_node_path8.default.normalize(requestedCwd);
47219
+ const normalizedRemapped = import_node_path8.default.normalize(remapped);
47220
+ if (this.extractAhchatWorkspaceSuffix(requestedCwd) && normalizedRequested !== normalizedRemapped) {
47221
+ logger10.info("Server working directory remapped to local Bridge workspace", {
47222
+ agentId: agentConfig.id,
47223
+ scope: scopeKey(scope),
47224
+ requested: requestedCwd,
47225
+ remapped
47226
+ });
47227
+ return remapped;
47228
+ }
47229
+ return requestedCwd;
47230
+ }
46802
47231
  async resolveRuntimeCwd(agentConfig, scope, requestedCwd) {
46803
- let cwd = requestedCwd;
47232
+ let cwd = this.remapServerWorkspaceCwd(agentConfig, scope, requestedCwd);
46804
47233
  if (isRunningAsRoot() && cwd.startsWith("/root/")) {
46805
47234
  cwd = this.fallbackCwd(agentConfig, scope, cwd);
46806
47235
  }
@@ -47433,19 +47862,10 @@ Do NOT use "..." as content \u2014 write specific, project-relevant content.`;
47433
47862
  options.resume = savedSessionId;
47434
47863
  }
47435
47864
  if (isRunningAsRoot()) {
47436
- try {
47437
- await import_promises8.default.chown(effectiveConfigDir, NODE_USER_UID, NODE_USER_UID);
47438
- } catch {
47439
- }
47440
- try {
47441
- await import_promises8.default.chown(agentCwd, NODE_USER_UID, NODE_USER_UID);
47442
- } catch {
47443
- }
47865
+ await chownForRootSpawn(effectiveConfigDir, "configDir");
47866
+ await chownForRootSpawn(agentCwd, "agentCwd");
47444
47867
  const settingsFilePath = import_node_path8.default.join(effectiveConfigDir, "settings.json");
47445
- try {
47446
- await import_promises8.default.chown(settingsFilePath, NODE_USER_UID, NODE_USER_UID);
47447
- } catch {
47448
- }
47868
+ await chownForRootSpawn(settingsFilePath, "settingsFile");
47449
47869
  options.spawnClaudeCodeProcess = (spawnOptions) => {
47450
47870
  const env2 = { ...spawnOptions.env, HOME: "/home/node" };
47451
47871
  return (0, import_node_child_process.spawn)(spawnOptions.command, spawnOptions.args, {
@@ -47611,7 +48031,7 @@ ${trimmed}`;
47611
48031
  lines.push(` workdir: ${currentCwd}`);
47612
48032
  } else {
47613
48033
  const a = this.agentRegistry?.getById(agentId);
47614
- const singleCwd = a?.workingDirectory || import_node_path8.default.join(import_node_os5.default.homedir(), ".ahchat", `Agent-${a?.name ?? agentId}-${agentId}`);
48034
+ const singleCwd = a?.workingDirectory || import_node_path8.default.join(this.workspacesDir, agentId);
47615
48035
  lines.push(` workdir: ${singleCwd}`);
47616
48036
  }
47617
48037
  let rosterCount = 0;
@@ -47623,7 +48043,7 @@ ${trimmed}`;
47623
48043
  if (key === curKey) {
47624
48044
  lines.push(` workdir: ${currentCwd}`);
47625
48045
  } else {
47626
- const groupCwd = defaultGroupWorkdir(import_node_os5.default.homedir(), g2.name, g2.groupId);
48046
+ const groupCwd = g2.workingDirectory || import_node_path8.default.join(this.workspacesDir, g2.groupId);
47627
48047
  lines.push(` workdir: ${groupCwd}`);
47628
48048
  }
47629
48049
  const others = g2.members.filter((id) => id !== agentId).map((id) => {
@@ -47970,6 +48390,16 @@ ${lines.join("\n")}`;
47970
48390
  }
47971
48391
  }
47972
48392
  async dispatchToSDK(runtime, task) {
48393
+ if (runtime.postMergeContinuationTask) {
48394
+ logger10.info("Clearing stale post-merge continuation route before explicit dispatch", {
48395
+ agentId: runtime.agentId,
48396
+ staleReplyMessageId: runtime.postMergeContinuationTask.replyMessageId,
48397
+ nextReplyMessageId: task.replyMessageId,
48398
+ traceId: task.traceId
48399
+ });
48400
+ delete runtime.postMergeContinuationTask;
48401
+ delete runtime.postMergeContinuationUntil;
48402
+ }
47973
48403
  await this.applyEffortMode(runtime, "high", {
47974
48404
  source: "dispatchToSDK",
47975
48405
  replyMessageId: task.replyMessageId
@@ -48178,6 +48608,19 @@ ${lines.join("\n")}`;
48178
48608
  }
48179
48609
  });
48180
48610
  }
48611
+ const continuationTask = mergedBatch.at(-1);
48612
+ if (continuationTask) {
48613
+ proc.postMergeContinuationTask = continuationTask;
48614
+ proc.postMergeContinuationUntil = Date.now() + POST_MERGE_CONTINUATION_ROUTE_MS;
48615
+ logger10.info("Armed SDK post-merge continuation routing", {
48616
+ agentId: proc.agentId,
48617
+ carrierReplyMessageId: completedTask.replyMessageId,
48618
+ continuationReplyMessageId: continuationTask.replyMessageId,
48619
+ mergedCount: mergedBatch.length,
48620
+ routeUntil: new Date(proc.postMergeContinuationUntil).toISOString(),
48621
+ traceId: completedTask.traceId
48622
+ });
48623
+ }
48181
48624
  runtime.mergedTasks = [];
48182
48625
  } else if (runtime.mergedTasks.length > 0) {
48183
48626
  logger10.warn("mergedTasks non-empty but no currentTask; dropping", {
@@ -50937,6 +51380,13 @@ var import_node_child_process2 = require("child_process");
50937
51380
  var import_node_fs8 = require("fs");
50938
51381
  var import_node_path15 = require("path");
50939
51382
  var logger24 = createModuleLogger("bridge.ensureCli");
51383
+ var DEFAULT_INSTALL_TIMEOUT_MS = 6e5;
51384
+ function getInstallTimeoutMs() {
51385
+ const raw = process.env.AHCHAT_CLAUDE_CLI_INSTALL_TIMEOUT_MS;
51386
+ if (!raw) return DEFAULT_INSTALL_TIMEOUT_MS;
51387
+ const parsed = Number.parseInt(raw, 10);
51388
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : DEFAULT_INSTALL_TIMEOUT_MS;
51389
+ }
50940
51390
  function detectClaudeCli() {
50941
51391
  try {
50942
51392
  return (0, import_node_child_process2.execFileSync)("claude", ["--version"], { timeout: 1e4 }).toString().trim();
@@ -50965,9 +51415,18 @@ function resolveClaudeBinary() {
50965
51415
  if (first) return first;
50966
51416
  } catch {
50967
51417
  }
51418
+ return resolveViaNpmBin();
51419
+ }
51420
+ function getNpmClaudeCandidates(bin) {
51421
+ if (process.platform === "win32") {
51422
+ return ["claude.cmd", "claude.exe", "claude", "anthropic-cli.cmd", "anthropic-cli.exe", "anthropic-cli"].map((name) => (0, import_node_path15.join)(bin, name));
51423
+ }
51424
+ return [(0, import_node_path15.join)(bin, "claude"), (0, import_node_path15.join)(bin, "anthropic-cli")];
51425
+ }
51426
+ function resolveViaNpmBin() {
50968
51427
  const bin = getNpmGlobalBin();
50969
- if (bin) {
50970
- const candidate = (0, import_node_path15.join)(bin, process.platform === "win32" ? "claude.cmd" : "claude");
51428
+ if (!bin) return void 0;
51429
+ for (const candidate of getNpmClaudeCandidates(bin)) {
50971
51430
  try {
50972
51431
  (0, import_node_fs8.accessSync)(candidate, import_node_fs8.constants.X_OK);
50973
51432
  return candidate;
@@ -50977,9 +51436,30 @@ function resolveClaudeBinary() {
50977
51436
  return void 0;
50978
51437
  }
50979
51438
  function detectViaNpmBin() {
50980
- const bin = getNpmGlobalBin();
50981
- if (!bin) return void 0;
50982
- const candidates = [(0, import_node_path15.join)(bin, "claude"), (0, import_node_path15.join)(bin, "anthropic-cli")];
51439
+ const binPath = resolveViaNpmBin();
51440
+ if (!binPath) return void 0;
51441
+ try {
51442
+ return (0, import_node_child_process2.execFileSync)(binPath, ["--version"], { timeout: 1e4 }).toString().trim();
51443
+ } catch {
51444
+ }
51445
+ return void 0;
51446
+ }
51447
+ function detectResolvedClaudeCli() {
51448
+ const viaPath = detectClaudeCli();
51449
+ if (viaPath) {
51450
+ return { version: viaPath, path: resolveClaudeBinary() };
51451
+ }
51452
+ const npmBinPath = resolveViaNpmBin();
51453
+ if (!npmBinPath) return void 0;
51454
+ try {
51455
+ const version2 = (0, import_node_child_process2.execFileSync)(npmBinPath, ["--version"], { timeout: 1e4 }).toString().trim();
51456
+ return { version: version2, path: npmBinPath };
51457
+ } catch {
51458
+ }
51459
+ return void 0;
51460
+ }
51461
+ function detectVersionFromResolvedCandidates() {
51462
+ const candidates = [resolveClaudeBinary(), resolveViaNpmBin()].filter((p) => Boolean(p));
50983
51463
  for (const p of candidates) {
50984
51464
  try {
50985
51465
  (0, import_node_fs8.accessSync)(p, import_node_fs8.constants.X_OK);
@@ -50993,7 +51473,7 @@ function installClaudeCli() {
50993
51473
  logger24.info("Installing Claude Code CLI via npm...");
50994
51474
  const result = (0, import_node_child_process2.spawnSync)("npm", ["install", "-g", "@anthropic-ai/claude-code"], {
50995
51475
  stdio: "inherit",
50996
- timeout: 12e4
51476
+ timeout: getInstallTimeoutMs()
50997
51477
  });
50998
51478
  if (result.error) {
50999
51479
  logger24.error("npm install -g failed (spawn error)", {
@@ -51008,9 +51488,9 @@ function installClaudeCli() {
51008
51488
  return void 0;
51009
51489
  }
51010
51490
  logger24.info("npm install -g completed, checking for claude binary...");
51011
- const viaPath = detectClaudeCli();
51491
+ const viaPath = detectVersionFromResolvedCandidates();
51012
51492
  if (viaPath) {
51013
- logger24.info("claude detected via PATH after install", { version: viaPath });
51493
+ logger24.info("claude detected after install", { version: viaPath });
51014
51494
  return viaPath;
51015
51495
  }
51016
51496
  const viaBin = detectViaNpmBin();
@@ -51028,14 +51508,13 @@ function installClaudeCli() {
51028
51508
  return void 0;
51029
51509
  }
51030
51510
  async function ensureClaudeCli() {
51031
- let version2 = detectClaudeCli();
51032
- if (version2) {
51033
- const binPath2 = resolveClaudeBinary();
51034
- logger24.info("Claude Code CLI ready", { version: version2, path: binPath2 ?? null });
51035
- return binPath2;
51511
+ let detected = detectResolvedClaudeCli();
51512
+ if (detected) {
51513
+ logger24.info("Claude Code CLI ready", { version: detected.version, path: detected.path ?? null });
51514
+ return detected.path;
51036
51515
  }
51037
51516
  process.stderr.write("\n\u26A0 Claude Code CLI (`claude`) not found. Attempting auto-install...\n\n");
51038
- version2 = installClaudeCli();
51517
+ const version2 = installClaudeCli();
51039
51518
  if (!version2) {
51040
51519
  const npmBin = getNpmGlobalBin();
51041
51520
  process.stderr.write(
@@ -51046,9 +51525,9 @@ async function ensureClaudeCli() {
51046
51525
  );
51047
51526
  process.exit(1);
51048
51527
  }
51049
- const binPath = resolveClaudeBinary();
51050
- logger24.info("Claude Code CLI ready", { version: version2, path: binPath ?? null });
51051
- return binPath;
51528
+ detected = detectResolvedClaudeCli();
51529
+ logger24.info("Claude Code CLI ready", { version: version2, path: detected?.path ?? null });
51530
+ return detected?.path;
51052
51531
  }
51053
51532
 
51054
51533
  // src/forkAgentFiles.ts
@@ -51378,9 +51857,9 @@ function isRunningAsRoot2() {
51378
51857
  }
51379
51858
  async function syncClaudeCredentialsToNodeAccessibleDir(agentConfigDir) {
51380
51859
  const rootClaudeDir = import_node_path19.default.join(process.env.HOME ?? "/root", ".claude");
51381
- const fs15 = await import("fs/promises");
51860
+ const fs16 = await import("fs/promises");
51382
51861
  try {
51383
- await fs15.access(rootClaudeDir);
51862
+ await fs16.access(rootClaudeDir);
51384
51863
  } catch {
51385
51864
  logger28.info("No /root/.claude to sync", { rootClaudeDir });
51386
51865
  return;
@@ -51390,7 +51869,7 @@ async function syncClaudeCredentialsToNodeAccessibleDir(agentConfigDir) {
51390
51869
  const src = import_node_path19.default.join(rootClaudeDir, file2);
51391
51870
  const dest = import_node_path19.default.join(agentConfigDir, file2);
51392
51871
  try {
51393
- await fs15.copyFile(src, dest);
51872
+ await fs16.copyFile(src, dest);
51394
51873
  logger28.info("Synced credential file", { file: file2, from: src, to: dest });
51395
51874
  } catch {
51396
51875
  logger28.debug("Credential file not present, skipping", { file: file2, src });
@@ -51398,15 +51877,15 @@ async function syncClaudeCredentialsToNodeAccessibleDir(agentConfigDir) {
51398
51877
  }
51399
51878
  }
51400
51879
  async function chownRecursive(dirPath, uid, gid) {
51401
- const fs15 = await import("fs/promises");
51880
+ const fs16 = await import("fs/promises");
51402
51881
  try {
51403
- await fs15.chown(dirPath, uid, gid);
51882
+ await fs16.chown(dirPath, uid, gid);
51404
51883
  } catch {
51405
51884
  logger28.debug("chown skipped", { dirPath, uid, gid });
51406
51885
  }
51407
51886
  let entries;
51408
51887
  try {
51409
- entries = await fs15.readdir(dirPath, { withFileTypes: true });
51888
+ entries = await fs16.readdir(dirPath, { withFileTypes: true });
51410
51889
  } catch {
51411
51890
  return;
51412
51891
  }
@@ -51416,7 +51895,7 @@ async function chownRecursive(dirPath, uid, gid) {
51416
51895
  await chownRecursive(fullPath, uid, gid);
51417
51896
  } else {
51418
51897
  try {
51419
- await fs15.chown(fullPath, uid, gid);
51898
+ await fs16.chown(fullPath, uid, gid);
51420
51899
  } catch {
51421
51900
  logger28.debug("chown skipped", { fullPath, uid, gid });
51422
51901
  }
@@ -51489,6 +51968,7 @@ Bridge token (register this machine at Settings \u2192 \u5DF2\u8FDE\u63A5\u7684\
51489
51968
  agentRegistry,
51490
51969
  subscriptionRegistry,
51491
51970
  serverApiUrl: config2.serverApiUrl,
51971
+ bridgeToken: config2.bridgeToken,
51492
51972
  dataDir: config2.dataDir
51493
51973
  });
51494
51974
  const taskDispatchHandler = createTaskDispatchHandler(agentManager, agentRegistry, emit);
@@ -52059,6 +52539,25 @@ function isProtocolRegistered() {
52059
52539
 
52060
52540
  // src/cli.ts
52061
52541
  var logger30 = createModuleLogger("bridge");
52542
+ function readCliVersion() {
52543
+ const candidates = [
52544
+ import_node_path21.default.resolve(__dirname, "../package.json"),
52545
+ import_node_path21.default.resolve(__dirname, "../../package.json"),
52546
+ import_node_path21.default.resolve(process.cwd(), "packages/bridge/package.json")
52547
+ ];
52548
+ for (const candidate of candidates) {
52549
+ if (!import_node_fs10.default.existsSync(candidate)) continue;
52550
+ try {
52551
+ const parsed = JSON.parse(import_node_fs10.default.readFileSync(candidate, "utf8"));
52552
+ if (parsed && typeof parsed === "object" && "version" in parsed && typeof parsed.version === "string" && parsed.version.length > 0) {
52553
+ return parsed.version;
52554
+ }
52555
+ } catch (e7) {
52556
+ logger30.warn("Unable to read CLI package version candidate", { error: e7, candidate });
52557
+ }
52558
+ }
52559
+ return "0.1.20";
52560
+ }
52062
52561
  function resolveDataDir(dataDir) {
52063
52562
  const userHome = process.env.USERPROFILE || import_node_os13.default.homedir();
52064
52563
  if (/^~[/\\]/.test(dataDir)) {
@@ -52096,17 +52595,14 @@ function parseAhchatUrl(url2) {
52096
52595
  }
52097
52596
  }
52098
52597
  async function run(args) {
52099
- let config2 = loadBridgeConfig();
52598
+ const dataDir = args.dataDir ? resolveDataDir(args.dataDir) : void 0;
52599
+ let config2 = loadBridgeConfig(dataDir ? { dataDir } : void 0);
52100
52600
  if (args.serverUrl) {
52101
52601
  const wsUrl = new URL(args.serverUrl);
52102
52602
  const httpBase = `${wsUrl.protocol === "wss:" ? "https" : "http"}://${wsUrl.host}`;
52103
52603
  config2 = { ...config2, serverUrl: args.serverUrl, serverApiUrl: httpBase };
52104
52604
  }
52105
52605
  if (args.token) config2 = { ...config2, bridgeToken: args.token };
52106
- if (args.dataDir) {
52107
- const resolved = resolveDataDir(args.dataDir);
52108
- config2 = { ...config2, dataDir: resolved };
52109
- }
52110
52606
  if (args.logLevel) config2 = { ...config2, logLevel: args.logLevel };
52111
52607
  await startBridge(config2);
52112
52608
  }
@@ -52143,5 +52639,5 @@ cli.command("status", "Check if protocol handler is registered").action(() => {
52143
52639
  if (!registered) process.stdout.write('Run "npx @fangyb/ahchat-bridge install" to register it.\n');
52144
52640
  });
52145
52641
  cli.help();
52146
- cli.version("0.1.0");
52642
+ cli.version(readCliVersion());
52147
52643
  cli.parse();