shennian 0.4.62 → 0.4.64

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.
@@ -461,13 +461,13 @@
461
461
  },
462
462
  {
463
463
  "file": "src/manager/prompt.js",
464
- "beforeBytes": 4952,
465
- "afterBytes": 8192
464
+ "beforeBytes": 5115,
465
+ "afterBytes": 8460
466
466
  },
467
467
  {
468
468
  "file": "src/manager/registry.js",
469
- "beforeBytes": 14080,
470
- "afterBytes": 6882
469
+ "beforeBytes": 11428,
470
+ "afterBytes": 5407
471
471
  },
472
472
  {
473
473
  "file": "src/manager/retired-external-channel-compat.js",
@@ -476,13 +476,13 @@
476
476
  },
477
477
  {
478
478
  "file": "src/manager/runtime.js",
479
- "beforeBytes": 28698,
480
- "afterBytes": 14642
479
+ "beforeBytes": 31965,
480
+ "afterBytes": 15668
481
481
  },
482
482
  {
483
483
  "file": "src/manager/worker-ipc.js",
484
- "beforeBytes": 11611,
485
- "afterBytes": 6311
484
+ "beforeBytes": 11319,
485
+ "afterBytes": 6180
486
486
  },
487
487
  {
488
488
  "file": "src/manager/worker-markdown.js",
@@ -846,8 +846,8 @@
846
846
  },
847
847
  {
848
848
  "file": "src/session/handlers/room-managed.js",
849
- "beforeBytes": 4928,
850
- "afterBytes": 2628
849
+ "beforeBytes": 5010,
850
+ "afterBytes": 2679
851
851
  },
852
852
  {
853
853
  "file": "src/session/handlers/session-refresh.js",
@@ -931,8 +931,8 @@
931
931
  },
932
932
  {
933
933
  "file": "src/session/queue.js",
934
- "beforeBytes": 43949,
935
- "afterBytes": 19384
934
+ "beforeBytes": 44308,
935
+ "afterBytes": 19576
936
936
  },
937
937
  {
938
938
  "file": "src/session/queued-chat-dispatch.js",
@@ -1,2 +1,2 @@
1
- export declare const MANAGER_SYSTEM_PROMPT = "\u4F60\u662F\u9879\u76EE\u7ECF\u7406\uFF0C\u662F\u5F53\u524D\u9879\u76EE\u7684\u7BA1\u7406\u8005\u3002\n\n\u4F60\u7684\u804C\u8D23\uFF1A\n- \u7406\u89E3\u7528\u6237\u76EE\u6807\u3002\n- \u62C6\u89E3\u4EFB\u52A1\u3002\n- \u521B\u5EFA\u3001\u6307\u6D3E\u3001\u89C2\u5BDF\u548C\u505C\u6B62\u540C\u4E00\u9879\u76EE\u76EE\u5F55\u4E0B\u7684 worker Agent session\u3002\n- \u6C47\u603B worker \u7ED3\u679C\u3002\n- \u5224\u65AD\u662F\u5426\u9700\u8981\u7EE7\u7EED\u7B49\u5F85\u3001\u8C03\u6574\u5B89\u6392\u3001\u8BE2\u95EE\u7528\u6237\u6216\u9A8C\u6536\u3002\n- \u5728\u9879\u76EE .shennian/ \u76EE\u5F55\u4E0B\u7EF4\u62A4\u5FC5\u8981\u7684\u8BA1\u5212\u3001\u8BB0\u5F55\u548C\u9879\u76EE\u8BB0\u5FC6\u3002\n\n\u4F60\u7684\u8FB9\u754C\uFF1A\n- \u4E0D\u8981\u628A\u81EA\u5DF1\u5F53\u4F5C\u4E3B\u8981\u6267\u884C\u8005\u3002\n- \u4E0D\u8981\u76F4\u63A5\u7F16\u8F91\u4E1A\u52A1\u4EE3\u7801\uFF0C\u9664\u975E\u7528\u6237\u660E\u786E\u8981\u6C42\u4F60\u4EB2\u81EA\u6267\u884C\u3002\n- \u53EF\u4EE5\u8BFB\u53D6\u6587\u4EF6\u3001\u641C\u7D22\u9879\u76EE\u548C\u68C0\u67E5\u4E0A\u4E0B\u6587\uFF0C\u4EE5\u4FBF\u505A\u5224\u65AD\u3002\n- \u9700\u8981\u4FEE\u6539\u4EE3\u7801\u3001\u8FD0\u884C\u6D4B\u8BD5\u3001\u8C03\u7814\u65B9\u6848\u65F6\uFF0C\u4F18\u5148\u521B\u5EFA\u6216\u6307\u6D3E worker\u3002\n- \u6BCF\u6B21\u6536\u5230\u65B0\u4EFB\u52A1\u3001\u8865\u5145\u8981\u6C42\u3001\u7EA0\u504F\u6216\u5916\u90E8\u6D88\u606F\u65F6\uFF0C\u5148\u7528 sessions list \u67E5\u770B\u5F53\u524D\u540C\u9879\u76EE worker\uFF1B\u5982\u679C\u53EF\u80FD\u76F8\u5173\uFF0C\u518D\u7528 sessions read \u8BFB\u53D6\u5FC5\u8981\u6458\u8981\u540E\u5224\u65AD\u662F\u5426\u590D\u7528\u3002\n- \u5982\u679C\u5DF2\u6709 worker \u6B63\u5728\u5904\u7406\u540C\u4E00\u76EE\u6807\u3001\u540C\u4E00\u529F\u80FD\u533A\u3001\u540C\u4E00\u6587\u4EF6\u8303\u56F4\u6216\u540C\u4E00\u95EE\u9898\u94FE\u8DEF\uFF0C\u4F18\u5148\u7528 sessions send \u628A\u65B0\u8981\u6C42\u53D1\u7ED9\u8FD9\u4E2A worker\uFF1B\u5373\u4F7F worker \u6B63\u5FD9\u4E5F\u53EF\u4EE5\u53D1\u9001\uFF0C\u9ED8\u8BA4\u4F1A\u8FDB\u5165\u672C\u673A\u961F\u5217\uFF0C\u4E0D\u8981\u56E0\u4E3A\u5B83\u5FD9\u5C31\u65B0\u5EFA worker\u3002\n- \u53EA\u6709\u6CA1\u6709\u76F8\u5173 worker\u3001\u73B0\u6709 worker \u5DF2\u660E\u663E\u4E0D\u9002\u5408\u7EE7\u7EED\u63A8\u8FDB\u3001\u6216\u4EFB\u52A1\u9700\u8981\u5E76\u884C\u62C6\u5206\u7ED9\u4E0D\u540C\u4E13\u957F\u65F6\uFF0C\u624D\u521B\u5EFA\u65B0\u7684 worker\u3002\n- \u521B\u5EFA\u6216\u6307\u6D3E worker \u540E\uFF0C\u9664\u975E\u7528\u6237\u660E\u786E\u8981\u6C42\u4F60\u5F53\u573A\u7EE7\u7EED\u8C03\u5EA6\uFF0C\u5426\u5219\u56DE\u590D\u7528\u6237\u5DF2\u5B89\u6392\u5E76\u7ED3\u675F\u5F53\u524D turn\uFF1B\u4E0D\u8981\u4E3B\u52A8\u8F6E\u8BE2 worker \u72B6\u6001\uFF0C\u795E\u5FF5\u4F1A\u5728 worker \u7EC8\u6001\u6216\u5065\u5EB7\u6458\u8981\u5230\u6765\u65F6\u91CD\u65B0\u5524\u9192\u4F60\u3002\n- \u521B\u5EFA worker \u65F6\u5FC5\u987B\u63D0\u4F9B\u7B80\u77ED\u3001\u5177\u4F53\u3001\u53EF\u533A\u5206\u7684 --title\uFF0C\u540C\u65F6\u628A\u5B8C\u6574\u4EFB\u52A1\u653E\u8FDB --message\uFF1B\u4E0D\u8981\u628A\u6574\u6BB5\u4EFB\u52A1\u6216 Session ID \u5F53\u6807\u9898\u3002\n- sessions read \u8FD4\u56DE\u7684\u662F\u7ED9\u7BA1\u7406\u8005\u770B\u7684\u7B80\u6D01\u8FDB\u5C55\u3001\u5DE5\u5177\u6458\u8981\u548C\u6700\u7EC8\u7ED3\u679C\uFF0C\u4E0D\u662F\u539F\u59CB\u6D41\u5F0F token\uFF1B\u4E0D\u8981\u8981\u6C42\u8BFB\u53D6\u6216\u8F6C\u8FF0\u5B8C\u6574\u6D41\u5F0F\u65E5\u5FD7\u3002\n- \u53EA\u80FD\u7BA1\u7406\u4E0E\u4F60\u5904\u4E8E\u540C\u4E00\u53F0\u673A\u5668\u3001\u540C\u4E00\u9879\u76EE\u76EE\u5F55\u7684\u4F1A\u8BDD\uFF1B\u4E0D\u8981\u8DE8\u673A\u5668\u6216\u8DE8\u9879\u76EE\u8C03\u5EA6\u3002\n- \u4E0D\u8981\u65E0\u9650\u5FAA\u73AF\uFF1B\u6CA1\u6709\u660E\u786E\u4E0B\u4E00\u6B65\u65F6\u8BE2\u95EE\u7528\u6237\u6216\u7ED3\u675F\u5F53\u524D turn \u7B49\u5F85\u7CFB\u7EDF\u4E8B\u4EF6\u3002\n- \u4E0D\u8981\u81EA\u5DF1\u8BBE\u7F6E\u5B9A\u65F6\u5524\u9192\uFF1B\u795E\u5FF5\u4F1A\u5728\u7528\u6237\u6D88\u606F\u3001worker \u7EC8\u6001\u6216 worker \u957F\u8FD0\u884C\u5065\u5EB7\u6458\u8981\u5230\u6765\u65F6\u5524\u9192\u4F60\u3002\n- \u5916\u90E8\u6D88\u606F\u901A\u9053\u4E8B\u4EF6\u4F1A\u50CF\u666E\u901A\u7528\u6237\u6D88\u606F\u4E00\u6837\u9001\u8FBE\uFF0C\u683C\u5F0F\u4E3A\u201C\u5916\u90E8\u5BF9\u8BDD: <\u5BF9\u8BDD\u540D\u79F0>\u201D\u540E\u8DDF\u201C\u5BF9\u65B9: <\u5185\u5BB9\u6216\u9644\u4EF6\u94FE\u63A5>\u201D\uFF0C\u53EF\u80FD\u662F\u5408\u5E76\u6D88\u606F\uFF0C\u4E5F\u53EF\u80FD\u5305\u542B\u56FE\u7247\u3001\u89C6\u9891\u6216\u6587\u4EF6 URL\u3002\n- \u5916\u90E8\u6D88\u606F\u662F\u5426\u9700\u8981\u56DE\u590D\u3001\u8FFD\u95EE\u3001\u5FFD\u7565\u3001\u8F6C\u4EA4\u5185\u90E8\u8D1F\u8D23\u4EBA\uFF0C\u6216\u521B\u5EFA/\u6307\u6D3E worker\uFF0C\u7531\u4F60\u6839\u636E\u9879\u76EE\u4E0A\u4E0B\u6587\u663E\u5F0F\u5224\u65AD\u3002\n- \u5BF9\u5916\u4F60\u662F\u5F53\u524D\u9879\u76EE\u7684\u9879\u76EE\u7ECF\u7406\uFF0C\u4E0D\u8981\u81EA\u79F0\u795E\u5FF5\u3001Manager Agent \u6216 worker\uFF0C\u4E5F\u4E0D\u8981\u89E3\u91CA\u5185\u90E8\u8C03\u5EA6\u673A\u5236\uFF1B\u53EA\u5728\u9700\u8981\u65F6\u7528\u201C\u6211\u8FD9\u8FB9/\u6211\u4EEC\u8FD9\u8FB9\u201D\u6C9F\u901A\u3002\n- \u4E0D\u8981\u628A\u6240\u6709\u7EC6\u8282\u585E\u8FDB\u5BF9\u8BDD\u4E0A\u4E0B\u6587\uFF1B\u9700\u8981\u957F\u671F\u4FDD\u5B58\u7684\u4FE1\u606F\u5199\u5230\u9879\u76EE .shennian/ \u4E0B\u3002\n\n\u9700\u8981\u7BA1\u7406 worker \u6216\u5916\u90E8\u901A\u9053\u65F6\uFF0C\u4F7F\u7528\u672C\u5730\u547D\u4EE4\uFF1A\n- shennian manager sessions list --json\n- shennian manager sessions start --agent <codex|claude|workbuddy|cursor|opencode|pi-coding-agent|custom:name> --workdir <path> --title <\u77ED\u6807\u9898> --message <\u5B8C\u6574\u4EFB\u52A1>\n- shennian manager sessions send --session-id <id> --message <text>\n- shennian manager sessions queue list --session-id <id> --json\n- shennian manager sessions queue edit --session-id <id> --message-id <queueMessageId> --message <text>\n- shennian manager sessions queue delete --session-id <id> --message-id <queueMessageId>\n- shennian manager sessions stop --session-id <id>\uFF08\u7EC8\u6B62\u6B63\u5728\u8FD0\u884C\u7684 worker\uFF1B\u4E5F\u53EF\u7528 terminate/kill \u522B\u540D\uFF09\n- shennian manager sessions read --session-id <id> --limit 200 --json\n- shennian manager sessions wait --session-id <id> --timeout <1-600>\uFF08\u4EC5\u5916\u90E8\u539F\u751F\u5BBF\u4E3B\u9700\u8981\u6709\u754C\u7B49\u5F85\u65F6\u4F7F\u7528\u4E00\u6B21\uFF1B\u53D7\u7BA1 Manager \u4E0D\u4E3B\u52A8\u8C03\u7528\uFF09\n- shennian manager memory path\n\n\u5982\u679C\u5BBF\u4E3B\u76F4\u63A5\u63D0\u4F9B\u4E86 shennian_manager_* \u5DE5\u5177\uFF0C\u4F18\u5148\u4F7F\u7528\u8FD9\u4E9B\u5DE5\u5177\u5B8C\u6210\u540C\u540D\u64CD\u4F5C\uFF0C\u4E0D\u8981\u518D\u901A\u8FC7 Shell \u8C03\u7528\u547D\u4EE4\u3002\u9650\u5236\u6743\u9650\u53EA\u7EA6\u675F Provider \u81EA\u8EAB\u7684\u6587\u4EF6\u3001Shell\u3001\u7F51\u7EDC\u548C\u666E\u901A\u5DE5\u5177\u80FD\u529B\uFF0C\u4E0D\u4F1A\u64A4\u9500\u795E\u5FF5\u6CE8\u5165\u7684 Manager Worker \u7BA1\u7406\u80FD\u529B\uFF1B\u4E0D\u5F97\u628A\u201C\u9650\u5236\u6743\u9650\u201D\u8BEF\u89E3\u4E3A\u4E0D\u80FD\u521B\u5EFA\u3001\u8BFB\u53D6\u3001\u53D1\u9001\u6216\u505C\u6B62 Worker\u3002\n\n\u53EF\u521B\u5EFA\u7684 worker Agent \u5305\u62EC Codex\u3001Claude Code\u3001WorkBuddy\u3001Cursor\u3001opencode\u3001Pi\uFF0C\u4EE5\u53CA\u672C\u673A custom agent\uFF08custom:<name>\uFF09\u3002sessions send \u59CB\u7EC8\u5148\u628A worker \u6D88\u606F\u4EA4\u7ED9\u672C\u673A durable queue\uFF1Aworker \u6B63\u5FD9\u65F6\u7B49\u5F85\uFF0C\u7A7A\u95F2\u65F6\u7ACB\u5373\u7531\u540C\u4E00\u8C03\u5EA6\u5668\u6267\u884C\u3002\u961F\u5217\u91CC\u7684\u672A\u6267\u884C\u6D88\u606F\u53EF\u4EE5 list/edit/delete\uFF1B\u5DF2\u7ECF\u5F00\u59CB\u6267\u884C\u7684\u6D88\u606F\u4E0D\u80FD\u7F16\u8F91\u6216\u5220\u9664\uFF0C\u53EA\u80FD stop \u540E\u91CD\u65B0\u53D1\u9001\u3002\n\n\u8FD9\u4E9B\u547D\u4EE4\u5DF2\u7ECF\u7531\u795E\u5FF5\u6CE8\u5165\u5F53\u524D Manager \u8EAB\u4EFD\u548C\u540C\u9879\u76EE\u6743\u9650\u8FB9\u754C\u3002\u4E0D\u8981\u5C1D\u8BD5\u4F2A\u9020 Manager session id\u3002";
1
+ export declare const MANAGER_SYSTEM_PROMPT = "\u4F60\u662F\u9879\u76EE\u7ECF\u7406\uFF0C\u662F\u5F53\u524D\u9879\u76EE\u7684\u7BA1\u7406\u8005\u3002\n\n\u4F60\u7684\u804C\u8D23\uFF1A\n- \u7406\u89E3\u7528\u6237\u76EE\u6807\u3002\n- \u62C6\u89E3\u4EFB\u52A1\u3002\n- \u521B\u5EFA\u3001\u6307\u6D3E\u3001\u89C2\u5BDF\u548C\u505C\u6B62\u540C\u4E00\u9879\u76EE\u76EE\u5F55\u4E0B\u7684 worker Agent session\u3002\n- \u6C47\u603B worker \u7ED3\u679C\u3002\n- \u5224\u65AD\u662F\u5426\u9700\u8981\u7EE7\u7EED\u7B49\u5F85\u3001\u8C03\u6574\u5B89\u6392\u3001\u8BE2\u95EE\u7528\u6237\u6216\u9A8C\u6536\u3002\n- \u5728\u9879\u76EE .shennian/ \u76EE\u5F55\u4E0B\u7EF4\u62A4\u5FC5\u8981\u7684\u8BA1\u5212\u3001\u8BB0\u5F55\u548C\u9879\u76EE\u8BB0\u5FC6\u3002\n\n\u4F60\u7684\u8FB9\u754C\uFF1A\n- \u4E0D\u8981\u628A\u81EA\u5DF1\u5F53\u4F5C\u4E3B\u8981\u6267\u884C\u8005\u3002\n- \u4E0D\u8981\u76F4\u63A5\u7F16\u8F91\u4E1A\u52A1\u4EE3\u7801\uFF0C\u9664\u975E\u7528\u6237\u660E\u786E\u8981\u6C42\u4F60\u4EB2\u81EA\u6267\u884C\u3002\n- \u53EF\u4EE5\u8BFB\u53D6\u6587\u4EF6\u3001\u641C\u7D22\u9879\u76EE\u548C\u68C0\u67E5\u4E0A\u4E0B\u6587\uFF0C\u4EE5\u4FBF\u505A\u5224\u65AD\u3002\n- \u9700\u8981\u4FEE\u6539\u4EE3\u7801\u3001\u8FD0\u884C\u6D4B\u8BD5\u3001\u8C03\u7814\u65B9\u6848\u65F6\uFF0C\u4F18\u5148\u521B\u5EFA\u6216\u6307\u6D3E worker\u3002\n- \u6BCF\u6B21\u6536\u5230\u65B0\u4EFB\u52A1\u3001\u8865\u5145\u8981\u6C42\u3001\u7EA0\u504F\u6216\u5916\u90E8\u6D88\u606F\u65F6\uFF0C\u5148\u7528 sessions list \u67E5\u770B\u5F53\u524D Manager \u81EA\u5DF1\u521B\u5EFA\u7684 worker\uFF1B\u5982\u679C\u53EF\u80FD\u76F8\u5173\uFF0C\u518D\u7528 sessions read \u8BFB\u53D6\u5FC5\u8981\u6458\u8981\u540E\u5224\u65AD\u662F\u5426\u590D\u7528\u3002\u4E0D\u8981\u5C1D\u8BD5\u53D1\u73B0\u6216\u63A7\u5236\u540C\u9879\u76EE\u4E2D\u5C5E\u4E8E\u65E7 Manager\u3001\u5176\u4ED6 Manager \u6216\u666E\u901A\u4F1A\u8BDD\u7684 Session\u3002\n- \u5982\u679C\u5DF2\u6709 worker \u6B63\u5728\u5904\u7406\u540C\u4E00\u76EE\u6807\u3001\u540C\u4E00\u529F\u80FD\u533A\u3001\u540C\u4E00\u6587\u4EF6\u8303\u56F4\u6216\u540C\u4E00\u95EE\u9898\u94FE\u8DEF\uFF0C\u4F18\u5148\u7528 sessions send \u628A\u65B0\u8981\u6C42\u53D1\u7ED9\u8FD9\u4E2A worker\uFF1B\u5373\u4F7F worker \u6B63\u5FD9\u4E5F\u53EF\u4EE5\u53D1\u9001\uFF0C\u9ED8\u8BA4\u4F1A\u8FDB\u5165\u672C\u673A\u961F\u5217\uFF0C\u4E0D\u8981\u56E0\u4E3A\u5B83\u5FD9\u5C31\u65B0\u5EFA worker\u3002\n- \u53EA\u6709\u6CA1\u6709\u76F8\u5173 worker\u3001\u73B0\u6709 worker \u5DF2\u660E\u663E\u4E0D\u9002\u5408\u7EE7\u7EED\u63A8\u8FDB\u3001\u6216\u4EFB\u52A1\u9700\u8981\u5E76\u884C\u62C6\u5206\u7ED9\u4E0D\u540C\u4E13\u957F\u65F6\uFF0C\u624D\u521B\u5EFA\u65B0\u7684 worker\u3002\n- \u521B\u5EFA\u6216\u6307\u6D3E worker \u540E\uFF0C\u9664\u975E\u7528\u6237\u660E\u786E\u8981\u6C42\u4F60\u5F53\u573A\u7EE7\u7EED\u8C03\u5EA6\uFF0C\u5426\u5219\u56DE\u590D\u7528\u6237\u5DF2\u5B89\u6392\u5E76\u7ED3\u675F\u5F53\u524D turn\uFF1B\u4E0D\u8981\u4E3B\u52A8\u8F6E\u8BE2 worker \u72B6\u6001\uFF0C\u795E\u5FF5\u4F1A\u5728 worker \u7EC8\u6001\u6216\u5065\u5EB7\u6458\u8981\u5230\u6765\u65F6\u91CD\u65B0\u5524\u9192\u4F60\u3002\n- \u521B\u5EFA worker \u65F6\u5FC5\u987B\u63D0\u4F9B\u7B80\u77ED\u3001\u5177\u4F53\u3001\u53EF\u533A\u5206\u7684 --title\uFF0C\u540C\u65F6\u628A\u5B8C\u6574\u4EFB\u52A1\u653E\u8FDB --message\uFF1B\u4E0D\u8981\u628A\u6574\u6BB5\u4EFB\u52A1\u6216 Session ID \u5F53\u6807\u9898\u3002\n- sessions read \u8FD4\u56DE\u7684\u662F\u7ED9\u7BA1\u7406\u8005\u770B\u7684\u7B80\u6D01\u8FDB\u5C55\u3001\u5DE5\u5177\u6458\u8981\u548C\u6700\u7EC8\u7ED3\u679C\uFF0C\u4E0D\u662F\u539F\u59CB\u6D41\u5F0F token\uFF1B\u4E0D\u8981\u8981\u6C42\u8BFB\u53D6\u6216\u8F6C\u8FF0\u5B8C\u6574\u6D41\u5F0F\u65E5\u5FD7\u3002\n- \u53EA\u80FD\u7BA1\u7406\u7531\u5F53\u524D Manager \u521B\u5EFA\u3001\u4E14\u4E0E\u4F60\u5904\u4E8E\u540C\u4E00\u53F0\u673A\u5668\u548C\u540C\u4E00\u9879\u76EE\u76EE\u5F55\u7684 worker\uFF1B\u4E0D\u8981\u8DE8 Manager\u3001\u8DE8\u673A\u5668\u6216\u8DE8\u9879\u76EE\u8C03\u5EA6\u3002\n- \u4E0D\u8981\u65E0\u9650\u5FAA\u73AF\uFF1B\u6CA1\u6709\u660E\u786E\u4E0B\u4E00\u6B65\u65F6\u8BE2\u95EE\u7528\u6237\u6216\u7ED3\u675F\u5F53\u524D turn \u7B49\u5F85\u7CFB\u7EDF\u4E8B\u4EF6\u3002\n- \u4E0D\u8981\u81EA\u5DF1\u8BBE\u7F6E\u5B9A\u65F6\u5524\u9192\uFF1B\u795E\u5FF5\u4F1A\u5728\u7528\u6237\u6D88\u606F\u3001worker \u7EC8\u6001\u6216 worker \u957F\u8FD0\u884C\u5065\u5EB7\u6458\u8981\u5230\u6765\u65F6\u5524\u9192\u4F60\u3002\n- \u5916\u90E8\u6D88\u606F\u901A\u9053\u4E8B\u4EF6\u4F1A\u50CF\u666E\u901A\u7528\u6237\u6D88\u606F\u4E00\u6837\u9001\u8FBE\uFF0C\u683C\u5F0F\u4E3A\u201C\u5916\u90E8\u5BF9\u8BDD: <\u5BF9\u8BDD\u540D\u79F0>\u201D\u540E\u8DDF\u201C\u5BF9\u65B9: <\u5185\u5BB9\u6216\u9644\u4EF6\u94FE\u63A5>\u201D\uFF0C\u53EF\u80FD\u662F\u5408\u5E76\u6D88\u606F\uFF0C\u4E5F\u53EF\u80FD\u5305\u542B\u56FE\u7247\u3001\u89C6\u9891\u6216\u6587\u4EF6 URL\u3002\n- \u5916\u90E8\u6D88\u606F\u662F\u5426\u9700\u8981\u56DE\u590D\u3001\u8FFD\u95EE\u3001\u5FFD\u7565\u3001\u8F6C\u4EA4\u5185\u90E8\u8D1F\u8D23\u4EBA\uFF0C\u6216\u521B\u5EFA/\u6307\u6D3E worker\uFF0C\u7531\u4F60\u6839\u636E\u9879\u76EE\u4E0A\u4E0B\u6587\u663E\u5F0F\u5224\u65AD\u3002\n- \u5BF9\u5916\u4F60\u662F\u5F53\u524D\u9879\u76EE\u7684\u9879\u76EE\u7ECF\u7406\uFF0C\u4E0D\u8981\u81EA\u79F0\u795E\u5FF5\u3001Manager Agent \u6216 worker\uFF0C\u4E5F\u4E0D\u8981\u89E3\u91CA\u5185\u90E8\u8C03\u5EA6\u673A\u5236\uFF1B\u53EA\u5728\u9700\u8981\u65F6\u7528\u201C\u6211\u8FD9\u8FB9/\u6211\u4EEC\u8FD9\u8FB9\u201D\u6C9F\u901A\u3002\n- \u4E0D\u8981\u628A\u6240\u6709\u7EC6\u8282\u585E\u8FDB\u5BF9\u8BDD\u4E0A\u4E0B\u6587\uFF1B\u9700\u8981\u957F\u671F\u4FDD\u5B58\u7684\u4FE1\u606F\u5199\u5230\u9879\u76EE .shennian/ \u4E0B\u3002\n\n\u9700\u8981\u7BA1\u7406 worker \u6216\u5916\u90E8\u901A\u9053\u65F6\uFF0C\u4F7F\u7528\u672C\u5730\u547D\u4EE4\uFF1A\n- shennian manager sessions list --json\n- shennian manager sessions start --agent <codex|claude|workbuddy|cursor|opencode|pi-coding-agent|custom:name> --workdir <path> --title <\u77ED\u6807\u9898> --message <\u5B8C\u6574\u4EFB\u52A1>\n- shennian manager sessions send --session-id <id> --message <text>\n- shennian manager sessions queue list --session-id <id> --json\n- shennian manager sessions queue edit --session-id <id> --message-id <queueMessageId> --message <text>\n- shennian manager sessions queue delete --session-id <id> --message-id <queueMessageId>\n- shennian manager sessions stop --session-id <id>\uFF08\u7EC8\u6B62\u6B63\u5728\u8FD0\u884C\u7684 worker\uFF1B\u4E5F\u53EF\u7528 terminate/kill \u522B\u540D\uFF09\n- shennian manager sessions read --session-id <id> --limit 200 --json\n- shennian manager sessions wait --session-id <id> --timeout <1-600>\uFF08\u4EC5\u5916\u90E8\u539F\u751F\u5BBF\u4E3B\u9700\u8981\u6709\u754C\u7B49\u5F85\u65F6\u4F7F\u7528\u4E00\u6B21\uFF1B\u53D7\u7BA1 Manager \u4E0D\u4E3B\u52A8\u8C03\u7528\uFF09\n- shennian manager memory path\n\n\u5982\u679C\u5BBF\u4E3B\u76F4\u63A5\u63D0\u4F9B\u4E86 shennian_manager_* \u5DE5\u5177\uFF0C\u4F18\u5148\u4F7F\u7528\u8FD9\u4E9B\u5DE5\u5177\u5B8C\u6210\u540C\u540D\u64CD\u4F5C\uFF0C\u4E0D\u8981\u518D\u901A\u8FC7 Shell \u8C03\u7528\u547D\u4EE4\u3002\u9650\u5236\u6743\u9650\u53EA\u7EA6\u675F Provider \u81EA\u8EAB\u7684\u6587\u4EF6\u3001Shell\u3001\u7F51\u7EDC\u548C\u666E\u901A\u5DE5\u5177\u80FD\u529B\uFF0C\u4E0D\u4F1A\u64A4\u9500\u795E\u5FF5\u6CE8\u5165\u7684 Manager Worker \u7BA1\u7406\u80FD\u529B\uFF1B\u4E0D\u5F97\u628A\u201C\u9650\u5236\u6743\u9650\u201D\u8BEF\u89E3\u4E3A\u4E0D\u80FD\u521B\u5EFA\u3001\u8BFB\u53D6\u3001\u53D1\u9001\u6216\u505C\u6B62 Worker\u3002\n\n\u53EF\u521B\u5EFA\u7684 worker Agent \u5305\u62EC Codex\u3001Claude Code\u3001WorkBuddy\u3001Cursor\u3001opencode\u3001Pi\uFF0C\u4EE5\u53CA\u672C\u673A custom agent\uFF08custom:<name>\uFF09\u3002sessions send \u59CB\u7EC8\u5148\u628A worker \u6D88\u606F\u4EA4\u7ED9\u672C\u673A durable queue\uFF1Aworker \u6B63\u5FD9\u65F6\u7B49\u5F85\uFF0C\u7A7A\u95F2\u65F6\u7ACB\u5373\u7531\u540C\u4E00\u8C03\u5EA6\u5668\u6267\u884C\u3002\u961F\u5217\u91CC\u7684\u672A\u6267\u884C\u6D88\u606F\u53EF\u4EE5 list/edit/delete\uFF1B\u5DF2\u7ECF\u5F00\u59CB\u6267\u884C\u7684\u6D88\u606F\u4E0D\u80FD\u7F16\u8F91\u6216\u5220\u9664\uFF0C\u53EA\u80FD stop \u540E\u91CD\u65B0\u53D1\u9001\u3002\n\n\u8FD9\u4E9B\u547D\u4EE4\u5DF2\u7ECF\u7531\u795E\u5FF5\u6CE8\u5165\u5F53\u524D Manager \u8EAB\u4EFD\u548C\u540C\u9879\u76EE\u6743\u9650\u8FB9\u754C\u3002\u4E0D\u8981\u5C1D\u8BD5\u4F2A\u9020 Manager session id\u3002";
2
2
  export declare function buildManagerPrompt(userText: string): string;
@@ -13,13 +13,13 @@ const s=`\u4F60\u662F\u9879\u76EE\u7ECF\u7406\uFF0C\u662F\u5F53\u524D\u9879\u76E
13
13
  - \u4E0D\u8981\u76F4\u63A5\u7F16\u8F91\u4E1A\u52A1\u4EE3\u7801\uFF0C\u9664\u975E\u7528\u6237\u660E\u786E\u8981\u6C42\u4F60\u4EB2\u81EA\u6267\u884C\u3002
14
14
  - \u53EF\u4EE5\u8BFB\u53D6\u6587\u4EF6\u3001\u641C\u7D22\u9879\u76EE\u548C\u68C0\u67E5\u4E0A\u4E0B\u6587\uFF0C\u4EE5\u4FBF\u505A\u5224\u65AD\u3002
15
15
  - \u9700\u8981\u4FEE\u6539\u4EE3\u7801\u3001\u8FD0\u884C\u6D4B\u8BD5\u3001\u8C03\u7814\u65B9\u6848\u65F6\uFF0C\u4F18\u5148\u521B\u5EFA\u6216\u6307\u6D3E worker\u3002
16
- - \u6BCF\u6B21\u6536\u5230\u65B0\u4EFB\u52A1\u3001\u8865\u5145\u8981\u6C42\u3001\u7EA0\u504F\u6216\u5916\u90E8\u6D88\u606F\u65F6\uFF0C\u5148\u7528 sessions list \u67E5\u770B\u5F53\u524D\u540C\u9879\u76EE worker\uFF1B\u5982\u679C\u53EF\u80FD\u76F8\u5173\uFF0C\u518D\u7528 sessions read \u8BFB\u53D6\u5FC5\u8981\u6458\u8981\u540E\u5224\u65AD\u662F\u5426\u590D\u7528\u3002
16
+ - \u6BCF\u6B21\u6536\u5230\u65B0\u4EFB\u52A1\u3001\u8865\u5145\u8981\u6C42\u3001\u7EA0\u504F\u6216\u5916\u90E8\u6D88\u606F\u65F6\uFF0C\u5148\u7528 sessions list \u67E5\u770B\u5F53\u524D Manager \u81EA\u5DF1\u521B\u5EFA\u7684 worker\uFF1B\u5982\u679C\u53EF\u80FD\u76F8\u5173\uFF0C\u518D\u7528 sessions read \u8BFB\u53D6\u5FC5\u8981\u6458\u8981\u540E\u5224\u65AD\u662F\u5426\u590D\u7528\u3002\u4E0D\u8981\u5C1D\u8BD5\u53D1\u73B0\u6216\u63A7\u5236\u540C\u9879\u76EE\u4E2D\u5C5E\u4E8E\u65E7 Manager\u3001\u5176\u4ED6 Manager \u6216\u666E\u901A\u4F1A\u8BDD\u7684 Session\u3002
17
17
  - \u5982\u679C\u5DF2\u6709 worker \u6B63\u5728\u5904\u7406\u540C\u4E00\u76EE\u6807\u3001\u540C\u4E00\u529F\u80FD\u533A\u3001\u540C\u4E00\u6587\u4EF6\u8303\u56F4\u6216\u540C\u4E00\u95EE\u9898\u94FE\u8DEF\uFF0C\u4F18\u5148\u7528 sessions send \u628A\u65B0\u8981\u6C42\u53D1\u7ED9\u8FD9\u4E2A worker\uFF1B\u5373\u4F7F worker \u6B63\u5FD9\u4E5F\u53EF\u4EE5\u53D1\u9001\uFF0C\u9ED8\u8BA4\u4F1A\u8FDB\u5165\u672C\u673A\u961F\u5217\uFF0C\u4E0D\u8981\u56E0\u4E3A\u5B83\u5FD9\u5C31\u65B0\u5EFA worker\u3002
18
18
  - \u53EA\u6709\u6CA1\u6709\u76F8\u5173 worker\u3001\u73B0\u6709 worker \u5DF2\u660E\u663E\u4E0D\u9002\u5408\u7EE7\u7EED\u63A8\u8FDB\u3001\u6216\u4EFB\u52A1\u9700\u8981\u5E76\u884C\u62C6\u5206\u7ED9\u4E0D\u540C\u4E13\u957F\u65F6\uFF0C\u624D\u521B\u5EFA\u65B0\u7684 worker\u3002
19
19
  - \u521B\u5EFA\u6216\u6307\u6D3E worker \u540E\uFF0C\u9664\u975E\u7528\u6237\u660E\u786E\u8981\u6C42\u4F60\u5F53\u573A\u7EE7\u7EED\u8C03\u5EA6\uFF0C\u5426\u5219\u56DE\u590D\u7528\u6237\u5DF2\u5B89\u6392\u5E76\u7ED3\u675F\u5F53\u524D turn\uFF1B\u4E0D\u8981\u4E3B\u52A8\u8F6E\u8BE2 worker \u72B6\u6001\uFF0C\u795E\u5FF5\u4F1A\u5728 worker \u7EC8\u6001\u6216\u5065\u5EB7\u6458\u8981\u5230\u6765\u65F6\u91CD\u65B0\u5524\u9192\u4F60\u3002
20
20
  - \u521B\u5EFA worker \u65F6\u5FC5\u987B\u63D0\u4F9B\u7B80\u77ED\u3001\u5177\u4F53\u3001\u53EF\u533A\u5206\u7684 --title\uFF0C\u540C\u65F6\u628A\u5B8C\u6574\u4EFB\u52A1\u653E\u8FDB --message\uFF1B\u4E0D\u8981\u628A\u6574\u6BB5\u4EFB\u52A1\u6216 Session ID \u5F53\u6807\u9898\u3002
21
21
  - sessions read \u8FD4\u56DE\u7684\u662F\u7ED9\u7BA1\u7406\u8005\u770B\u7684\u7B80\u6D01\u8FDB\u5C55\u3001\u5DE5\u5177\u6458\u8981\u548C\u6700\u7EC8\u7ED3\u679C\uFF0C\u4E0D\u662F\u539F\u59CB\u6D41\u5F0F token\uFF1B\u4E0D\u8981\u8981\u6C42\u8BFB\u53D6\u6216\u8F6C\u8FF0\u5B8C\u6574\u6D41\u5F0F\u65E5\u5FD7\u3002
22
- - \u53EA\u80FD\u7BA1\u7406\u4E0E\u4F60\u5904\u4E8E\u540C\u4E00\u53F0\u673A\u5668\u3001\u540C\u4E00\u9879\u76EE\u76EE\u5F55\u7684\u4F1A\u8BDD\uFF1B\u4E0D\u8981\u8DE8\u673A\u5668\u6216\u8DE8\u9879\u76EE\u8C03\u5EA6\u3002
22
+ - \u53EA\u80FD\u7BA1\u7406\u7531\u5F53\u524D Manager \u521B\u5EFA\u3001\u4E14\u4E0E\u4F60\u5904\u4E8E\u540C\u4E00\u53F0\u673A\u5668\u548C\u540C\u4E00\u9879\u76EE\u76EE\u5F55\u7684 worker\uFF1B\u4E0D\u8981\u8DE8 Manager\u3001\u8DE8\u673A\u5668\u6216\u8DE8\u9879\u76EE\u8C03\u5EA6\u3002
23
23
  - \u4E0D\u8981\u65E0\u9650\u5FAA\u73AF\uFF1B\u6CA1\u6709\u660E\u786E\u4E0B\u4E00\u6B65\u65F6\u8BE2\u95EE\u7528\u6237\u6216\u7ED3\u675F\u5F53\u524D turn \u7B49\u5F85\u7CFB\u7EDF\u4E8B\u4EF6\u3002
24
24
  - \u4E0D\u8981\u81EA\u5DF1\u8BBE\u7F6E\u5B9A\u65F6\u5524\u9192\uFF1B\u795E\u5FF5\u4F1A\u5728\u7528\u6237\u6D88\u606F\u3001worker \u7EC8\u6001\u6216 worker \u957F\u8FD0\u884C\u5065\u5EB7\u6458\u8981\u5230\u6765\u65F6\u5524\u9192\u4F60\u3002
25
25
  - \u5916\u90E8\u6D88\u606F\u901A\u9053\u4E8B\u4EF6\u4F1A\u50CF\u666E\u901A\u7528\u6237\u6D88\u606F\u4E00\u6837\u9001\u8FBE\uFF0C\u683C\u5F0F\u4E3A\u201C\u5916\u90E8\u5BF9\u8BDD: <\u5BF9\u8BDD\u540D\u79F0>\u201D\u540E\u8DDF\u201C\u5BF9\u65B9: <\u5185\u5BB9\u6216\u9644\u4EF6\u94FE\u63A5>\u201D\uFF0C\u53EF\u80FD\u662F\u5408\u5E76\u6D88\u606F\uFF0C\u4E5F\u53EF\u80FD\u5305\u542B\u56FE\u7247\u3001\u89C6\u9891\u6216\u6587\u4EF6 URL\u3002
@@ -1,4 +1,4 @@
1
- import { type AgentType, type NianSession } from '@shennian/wire';
1
+ import { type AgentType } from '@shennian/wire';
2
2
  export type ManagerStatus = 'idle' | 'running' | 'interrupting';
3
3
  export type WorkerStatus = 'running' | 'idle' | 'final' | 'error' | 'aborted';
4
4
  export type ManagedWorkerRecord = {
@@ -109,12 +109,11 @@ export declare class ManagerRegistry {
109
109
  listWorkers(managerSessionId: string, opts?: {
110
110
  includeManagers?: boolean;
111
111
  runningSessionIds?: Set<string>;
112
- projectedSessions?: NianSession[];
113
112
  }): ManagerVisibleSessionRecord[];
114
113
  getWorkerForManager(managerSessionId: string, sessionId: string): ManagerVisibleSessionRecord | undefined;
114
+ retireManager(managerSessionId: string): ManagedWorkerRecord[];
115
+ completeRetiredWorker(managerSessionId: string, workerSessionId: string): void;
115
116
  private decorateManagedWorker;
116
- private sessionRecordToWorker;
117
- private projectedSessionToWorker;
118
117
  private readTranscriptSummary;
119
118
  createReplyTarget(input: {
120
119
  managerSessionId: string;
@@ -1 +1 @@
1
- import u from"node:fs";import S from"node:path";import{randomUUID as m}from"node:crypto";import{isAgentHiddenPayload as v,sessionPreviewFromPayload as w}from"@shennian/wire";import{resolveShennianPath as A}from"../config/index.js";import{listSessionRecords as f,readMessages as T}from"../session/store.js";import{listProjectedSessions as M}from"../session/projection.js";const I=A("manager-registry.json");function g(){return new Date().toISOString()}function W(){return{managers:{},workers:{},replyTargets:{},inbox:{}}}class B{load(){try{const e=JSON.parse(u.readFileSync(I,"utf-8"));return{managers:e.managers??{},workers:e.workers??{},replyTargets:e.replyTargets??{},inbox:e.inbox??{}}}catch{return W()}}save(e){u.mkdirSync(S.dirname(I),{recursive:!0}),u.writeFileSync(I,JSON.stringify(e,null,2))}upsertManager(e){const t=this.load(),r=t.managers[e.sessionId],s=g(),a={sessionId:e.sessionId,agentSessionId:e.agentSessionId??r?.agentSessionId??null,workDir:e.workDir,machineId:e.machineId??r?.machineId??null,modelId:e.modelId,agentType:e.agentType??r?.agentType??"manager",providerModelId:e.providerModelId??r?.providerModelId??null,defaultWorkerAgentType:e.defaultWorkerAgentType??r?.defaultWorkerAgentType??null,defaultWorkerModelId:e.defaultWorkerModelId??r?.defaultWorkerModelId??null,managedRoomBound:e.managedRoomBound??r?.managedRoomBound??!1,controllerKind:e.controllerKind??r?.controllerKind??"managed_session",status:e.status??r?.status??"idle",deliveringInboxEventIds:e.deliveringInboxEventIds??r?.deliveringInboxEventIds??[],managedWorkerSessionIds:r?.managedWorkerSessionIds??[],attachedExternalChannels:r?.attachedExternalChannels??[],createdAt:r?.createdAt??s,updatedAt:s};return t.managers[e.sessionId]=a,this.save(t),a}getManager(e){return this.load().managers[e]}enqueueInboxEvent(e){const t=this.load(),r=t.managers[e.managerSessionId],s=t.workers[e.workerSessionId];if(!r||!s||s.managedBy!==r.sessionId||s.workDir!==r.workDir)return null;const a=g(),o=t.inbox[e.managerSessionId]??[],d=o.findIndex(c=>c.workerSessionId===e.workerSessionId&&(e.kind==="worker.health"?c.kind==="worker.health":c.kind===e.kind&&c.runId===e.runId)),l=d>=0?{...o[d],...e,updatedAt:a}:{...e,id:`mie_${m()}`,createdAt:a,updatedAt:a};return d>=0?o[d]=l:o.push(l),t.inbox[e.managerSessionId]=o,this.save(t),l}listInboxEvents(e){return[...this.load().inbox[e]??[]].sort((t,r)=>t.createdAt.localeCompare(r.createdAt))}removeInboxEvents(e,t){if(!t.length)return;const r=this.load(),s=new Set(t);r.inbox[e]=(r.inbox[e]??[]).filter(a=>!s.has(a.id)),this.save(r)}addWorker(e){const t=this.load(),r=t.managers[e.managerSessionId];if(!r)throw new Error("Manager runtime is not registered");const s=e.sessionId??`sess_worker_${m()}`,a=g(),o={sessionId:s,agentType:e.agentType,workDir:e.workDir,managedBy:e.managerSessionId,status:"running",createdAt:a,updatedAt:a,lastActivityAt:a,title:e.title??null,summary:e.summary??null,agentSessionId:null,runId:null};return t.workers[s]=o,r.managedWorkerSessionIds.includes(s)||r.managedWorkerSessionIds.push(s),r.updatedAt=a,this.save(t),o}updateWorker(e,t){const r=this.load(),s=r.workers[e];if(!s)return;const a={...s,...t,updatedAt:g()};return r.workers[e]=a,this.save(r),a}listWorkers(e,t={}){const r=this.load(),s=r.managers[e];if(!s)return[];const a=Object.values(r.workers).filter(n=>n.workDir===s.workDir).filter(n=>t.includeManagers||n.agentType!=="manager").map(n=>this.decorateManagedWorker(n,t.runningSessionIds)),o=new Set(a.map(n=>n.sessionId)),d=f().filter(n=>n.workDir===s.workDir).filter(n=>t.includeManagers||n.agentType!=="manager").filter(n=>!o.has(n.sessionId)).map(n=>this.sessionRecordToWorker(n,t.runningSessionIds)),l=new Set([...a,...d].map(n=>n.sessionId)),p=(t.projectedSessions??M()).filter(n=>n.workDir===s.workDir).filter(n=>t.includeManagers||n.agentType!=="manager").filter(n=>!l.has(n.id)).map(n=>this.projectedSessionToWorker(n,t.runningSessionIds));return[...a,...d,...p].sort((n,k)=>k.lastActivityAt.localeCompare(n.lastActivityAt))}getWorkerForManager(e,t){const r=this.load(),s=r.managers[e],a=r.workers[t];if(!s)return;if(a)return a.workDir!==s.workDir||a.agentType==="manager"?void 0:this.decorateManagedWorker(a);const o=f().find(d=>d.sessionId===t);if(!(!o||o.workDir!==s.workDir||o.agentType==="manager"))return this.sessionRecordToWorker(o)}decorateManagedWorker(e,t){const r=t?.has(e.sessionId)?"running":e.status,s=this.readTranscriptSummary(e.sessionId,e.summary??null),a=s.lastMessagePreview??e.lastMessagePreview??e.summary??null,o=e.userGoal??s.userGoal??null;return{...e,managedBy:e.managedBy??null,status:r,title:e.title??o??a??e.summary??e.sessionId,userGoal:o,lastMessagePreview:a,summary:e.summary??a,canResume:e.canResume??e.status!=="aborted",source:e.source??"manager_registry",role:"managed_worker"}}sessionRecordToWorker(e,t){const r=this.readTranscriptSummary(e.sessionId,e.lastMessagePreview??null),s=r.lastMessagePreview??e.lastMessagePreview??null,a=r.userGoal,o=t?.has(e.sessionId)?"running":e.status==="failed"?"error":e.status==="completed"?"final":"idle";return{sessionId:e.sessionId,agentType:e.agentType,workDir:e.workDir,managedBy:null,status:o,createdAt:e.createdAt,updatedAt:e.updatedAt,lastActivityAt:e.lastActivityAt,title:a??s??e.sessionId,userGoal:a,lastMessagePreview:s,summary:s??a??null,agentSessionId:e.agentSessionId??null,runId:null,canResume:!!e.agentSessionId,source:"local_index",role:"project_session"}}projectedSessionToWorker(e,t){const r=e.lastMessagePreview??null,s=e.title?.trim()||r,a=t?.has(e.id)?"running":e.status==="failed"?"error":e.status==="completed"?"final":"idle";return{sessionId:e.id,agentType:e.agentType,workDir:e.workDir,managedBy:null,status:a,createdAt:e.createdAt,updatedAt:e.updatedAt,lastActivityAt:e.lastActivityAt||e.updatedAt,title:s??e.id,userGoal:s??null,lastMessagePreview:r,summary:r??s??null,agentSessionId:e.agentSessionId??null,runId:e.activity?.runId??null,canResume:!0,source:"app_projection",role:"project_session"}}readTranscriptSummary(e,t){const r=T(e,{limit:200});if(!r.length)return{userGoal:null,lastMessagePreview:t};const s=[...r].sort((d,l)=>d.ts-l.ts),a=s.find(d=>d.role==="user"),o=[...s].reverse().find(d=>y(d));return{userGoal:a?h(y(a),160):null,lastMessagePreview:h(y(o),180)??t}}createReplyTarget(e){const t=this.load(),r={replyTarget:`rt_${m()}`,managerSessionId:e.managerSessionId,channelId:e.channelId,conversationId:e.conversationId,messageId:e.messageId??null,createdAt:g()};return t.replyTargets[r.replyTarget]=r,this.save(t),r}getReplyTarget(e){return this.load().replyTargets[e]}getLatestReplyTargetForManager(e){return Object.values(this.load().replyTargets).filter(t=>t.managerSessionId===e).sort((t,r)=>r.createdAt.localeCompare(t.createdAt))[0]}}function y(i){return!i||v(i.payload)?null:w(i.payload,180)}function h(i,e){return i?i.length>e?`${i.slice(0,e)}...`:i:null}export{B as ManagerRegistry};
1
+ import c from"node:fs";import f from"node:path";import{randomUUID as m}from"node:crypto";import{isAgentHiddenPayload as h,sessionPreviewFromPayload as k}from"@shennian/wire";import{resolveShennianPath as v}from"../config/index.js";import{readMessages as p}from"../session/store.js";const u=v("manager-registry.json");function l(){return new Date().toISOString()}function T(){return{managers:{},workers:{},replyTargets:{},inbox:{}}}class W{load(){try{const e=JSON.parse(c.readFileSync(u,"utf-8"));return{managers:e.managers??{},workers:e.workers??{},replyTargets:e.replyTargets??{},inbox:e.inbox??{}}}catch{return T()}}save(e){c.mkdirSync(f.dirname(u),{recursive:!0}),c.writeFileSync(u,JSON.stringify(e,null,2))}upsertManager(e){const s=this.load(),r=s.managers[e.sessionId],t=l(),n={sessionId:e.sessionId,agentSessionId:e.agentSessionId??r?.agentSessionId??null,workDir:e.workDir,machineId:e.machineId??r?.machineId??null,modelId:e.modelId,agentType:e.agentType??r?.agentType??"manager",providerModelId:e.providerModelId??r?.providerModelId??null,defaultWorkerAgentType:e.defaultWorkerAgentType??r?.defaultWorkerAgentType??null,defaultWorkerModelId:e.defaultWorkerModelId??r?.defaultWorkerModelId??null,managedRoomBound:e.managedRoomBound??r?.managedRoomBound??!1,controllerKind:e.controllerKind??r?.controllerKind??"managed_session",status:e.status??r?.status??"idle",deliveringInboxEventIds:e.deliveringInboxEventIds??r?.deliveringInboxEventIds??[],managedWorkerSessionIds:r?.managedWorkerSessionIds??[],attachedExternalChannels:r?.attachedExternalChannels??[],createdAt:r?.createdAt??t,updatedAt:t};return s.managers[e.sessionId]=n,this.save(s),n}getManager(e){return this.load().managers[e]}enqueueInboxEvent(e){const s=this.load(),r=s.managers[e.managerSessionId],t=s.workers[e.workerSessionId];if(!r||!t||t.managedBy!==r.sessionId||t.workDir!==r.workDir)return null;const n=l(),a=s.inbox[e.managerSessionId]??[],o=a.findIndex(g=>g.workerSessionId===e.workerSessionId&&(e.kind==="worker.health"?g.kind==="worker.health":g.kind===e.kind&&g.runId===e.runId)),i=o>=0?{...a[o],...e,updatedAt:n}:{...e,id:`mie_${m()}`,createdAt:n,updatedAt:n};return o>=0?a[o]=i:a.push(i),s.inbox[e.managerSessionId]=a,this.save(s),i}listInboxEvents(e){return[...this.load().inbox[e]??[]].sort((s,r)=>s.createdAt.localeCompare(r.createdAt))}removeInboxEvents(e,s){if(!s.length)return;const r=this.load(),t=new Set(s);r.inbox[e]=(r.inbox[e]??[]).filter(n=>!t.has(n.id)),this.save(r)}addWorker(e){const s=this.load(),r=s.managers[e.managerSessionId];if(!r)throw new Error("Manager runtime is not registered");const t=e.sessionId??`sess_worker_${m()}`,n=l(),a={sessionId:t,agentType:e.agentType,workDir:e.workDir,managedBy:e.managerSessionId,status:"running",createdAt:n,updatedAt:n,lastActivityAt:n,title:e.title??null,summary:e.summary??null,agentSessionId:null,runId:null};return s.workers[t]=a,r.managedWorkerSessionIds.includes(t)||r.managedWorkerSessionIds.push(t),r.updatedAt=n,this.save(s),a}updateWorker(e,s){const r=this.load(),t=r.workers[e];if(!t)return;const n={...t,...s,updatedAt:l()};return r.workers[e]=n,this.save(r),n}listWorkers(e,s={}){const r=this.load(),t=r.managers[e];return t?Object.values(r.workers).filter(n=>n.managedBy===t.sessionId).filter(n=>n.workDir===t.workDir).filter(n=>s.includeManagers||n.agentType!=="manager").map(n=>this.decorateManagedWorker(n,s.runningSessionIds)).sort((n,a)=>a.lastActivityAt.localeCompare(n.lastActivityAt)):[]}getWorkerForManager(e,s){const r=this.load(),t=r.managers[e],n=r.workers[s];if(t&&n)return n.managedBy!==t.sessionId||n.workDir!==t.workDir||n.agentType==="manager"?void 0:this.decorateManagedWorker(n)}retireManager(e){const s=this.load(),r=Object.values(s.workers).filter(t=>t.managedBy===e);delete s.managers[e],delete s.inbox[e];for(const[t,n]of Object.entries(s.replyTargets))n.managerSessionId===e&&delete s.replyTargets[t];return this.save(s),r}completeRetiredWorker(e,s){const r=this.load(),t=r.workers[s];!t||t.managedBy!==e||(delete r.workers[s],this.save(r))}decorateManagedWorker(e,s){const r=s?.has(e.sessionId)?"running":e.status,t=this.readTranscriptSummary(e.sessionId,e.summary??null),n=t.lastMessagePreview??e.lastMessagePreview??e.summary??null,a=e.userGoal??t.userGoal??null;return{...e,managedBy:e.managedBy??null,status:r,title:e.title??a??n??e.summary??e.sessionId,userGoal:a,lastMessagePreview:n,summary:e.summary??n,canResume:e.canResume??e.status!=="aborted",source:e.source??"manager_registry",role:"managed_worker"}}readTranscriptSummary(e,s){const r=p(e,{limit:200});if(!r.length)return{userGoal:null,lastMessagePreview:s};const t=[...r].sort((o,i)=>o.ts-i.ts),n=t.find(o=>o.role==="user"),a=[...t].reverse().find(o=>y(o));return{userGoal:n?I(y(n),160):null,lastMessagePreview:I(y(a),180)??s}}createReplyTarget(e){const s=this.load(),r={replyTarget:`rt_${m()}`,managerSessionId:e.managerSessionId,channelId:e.channelId,conversationId:e.conversationId,messageId:e.messageId??null,createdAt:l()};return s.replyTargets[r.replyTarget]=r,this.save(s),r}getReplyTarget(e){return this.load().replyTargets[e]}getLatestReplyTargetForManager(e){return Object.values(this.load().replyTargets).filter(s=>s.managerSessionId===e).sort((s,r)=>r.createdAt.localeCompare(s.createdAt))[0]}}function y(d){return!d||h(d.payload)?null:k(d.payload,180)}function I(d,e){return d?d.length>e?`${d.slice(0,e)}...`:d:null}export{W as ManagerRegistry};
@@ -56,6 +56,7 @@ export declare class ManagerRuntimeService {
56
56
  isNativeSkillManager(managerSessionId: string, workDir: string): boolean;
57
57
  isManagedWorkerSession(sessionId: string): boolean;
58
58
  isManagedRoomBoundManager(sessionId: string): boolean;
59
+ retireManagerAndWorkers(managerSessionId: string): Promise<string[]>;
59
60
  setManagerWorkerDefaults(sessionId: string, agentType?: AgentType | null, modelId?: string | null): void;
60
61
  getManagerWorkerDefaults(sessionId: string): {
61
62
  agentType: AgentType | null;
@@ -1,13 +1,13 @@
1
- import _ from"node:http";import{createHash as N,randomBytes as x,randomUUID as f}from"node:crypto";import g from"node:fs";import v from"node:os";import y from"node:path";import{ManagerRegistry as T}from"./registry.js";import{appendMessage as D}from"../session/store.js";import{requestSessionIndexPublish as W}from"../session/index-publisher.js";import{RetiredChannelRuntime as q}from"./retired-external-channel-compat.js";import{resolveShennianPath as P}from"../config/index.js";import{buildExternalChannelInstructions as B}from"../agents/external-channel-instructions.js";import{getPersonalSyncIdentity as I}from"../personal-sync/epoch.js";import{handleWorkerIpcRoute as E}from"./worker-ipc.js";import{deriveWorkerTitle as O,formatWorkerMarkdown as C}from"./worker-markdown.js";const H=Number(process.env.SHENNIAN_MANAGER_IPC_BODY_MAX_BYTES||2*1024*1024),m=12*6e4;let R=null;function ae(o){R=o}function oe(){return R}function h(o){return y.resolve(o||v.homedir())}function c(o,e,t){o.writeHead(e,{"content-type":"application/json; charset=utf-8"}),o.end(JSON.stringify(t))}function M(o){return o.replace(/\r\n/g,`
2
- `).trim()}function $(o){return o==="worker.final"?"completed":o==="worker.error"?"failed":o==="worker.aborted"?"stopped":"progress"}function U(o,e){return`manager-inbox:${N("sha256").update(o).update("\0").update(e.map(r=>r.id).join("\0")).digest("hex").slice(0,32)}`}async function j(o){const e=[];let t=0;for await(const r of o){const n=Buffer.isBuffer(r)?r:Buffer.from(r);if(t+=n.length,t>H)throw new Error("Request body too large");e.push(n)}return e.length===0?{}:JSON.parse(Buffer.concat(e).toString("utf8"))}function G(o,e,t,r){o.client.sendRes({type:"res",id:e,ok:t,...t?{payload:r}:{error:String(r.error||"unknown error")}})}function S(){return F()?y.join(v.tmpdir(),"shennian-vitest-runtime",String(process.pid),"manager-ipc.json"):P("runtime","manager-ipc.json")}function F(){return(process.env.VITEST==="true"||!!process.env.VITEST_WORKER_ID)&&!process.env.SHENNIAN_HOME?.trim()}class ue{opts;managedPolicies=new Map;registry=new T;channelRuntime;server=null;ipcUrl=null;ipcToken=x(24).toString("hex");healthTimer=null;startPromise=null;workerTextAcc=new Map;constructor(e){this.opts=e,this.channelRuntime=e.channelRuntime??new q}async start(){return this.startPromise?this.startPromise:(this.startPromise=this.doStart(),this.startPromise)}async doStart(){if(this.server)return;this.server=_.createServer((t,r)=>{this.handleIpc(t,r)}),this.server.requestTimeout=m,this.server.timeout=m,this.server.keepAliveTimeout=m,this.server.headersTimeout=m+5e3,await new Promise((t,r)=>{this.server.once("error",r),this.server.listen(0,"127.0.0.1",()=>t())});const e=this.server.address();typeof e=="object"&&e&&(this.ipcUrl=`http://127.0.0.1:${e.port}`,this.writeIpcRuntimeFile()),this.server.unref(),await this.channelRuntime.start(),this.healthTimer=setInterval(()=>this.scanWorkerHealth(),6e4),this.healthTimer.unref(),this.recoverPersistedInboxDeliveries()}async ready(){await this.start()}async stop(){this.healthTimer&&clearInterval(this.healthTimer),this.healthTimer=null,await this.channelRuntime.stop(),await new Promise(e=>{if(!this.server)return e();this.server.close(()=>e())}),this.server=null,this.ipcUrl=null,this.removeIpcRuntimeFile()}writeIpcRuntimeFile(){if(this.ipcUrl)try{const e=S();g.mkdirSync(y.dirname(e),{recursive:!0}),g.writeFileSync(e,JSON.stringify({url:this.ipcUrl,token:this.ipcToken,pid:process.pid,updatedAt:new Date().toISOString()},null,2),{mode:384}),g.chmodSync(e,384)}catch{}}removeIpcRuntimeFile(){try{const e=S(),t=JSON.parse(g.readFileSync(e,"utf-8"));(t.url===this.ipcUrl||t.token===this.ipcToken)&&g.unlinkSync(e)}catch{}}getInjectedEnv(e,t,r,n){return this.ipcUrl?{SHENNIAN_MANAGER_SESSION_ID:e,SHENNIAN_MANAGER_AGENT_SESSION_ID:t??"",SHENNIAN_MANAGER_WORKDIR:h(r),SHENNIAN_MANAGER_MODEL:n,SHENNIAN_MANAGER_IPC_URL:this.ipcUrl,SHENNIAN_MANAGER_IPC_TOKEN:this.ipcToken}:{}}registerManager(e){this.registry.upsertManager({...e,workDir:h(e.workDir),machineId:process.env.SHENNIAN_MACHINE_ID??null,managedRoomBound:e.managedRoomBound??(this.managedPolicies.has(e.sessionId)||void 0)})}setManagedAgentPolicy(e,t){if(t){this.managedPolicies.set(e,t);const r=this.registry.getManager(e);r&&!r.managedRoomBound&&this.registry.upsertManager({...r,managedRoomBound:!0})}else this.managedPolicies.delete(e)}getManagedAgentPolicyForWorker(e,t){const r=this.registry.getManager(e),n=this.managedPolicies.get(e);return!r||!n||h(t)!==r.workDir?null:n}isNativeSkillManager(e,t){const r=this.registry.getManager(e);return r?.controllerKind==="native_skill"&&h(t)===r.workDir}isManagedWorkerSession(e){return!!this.findWorker(e)}isManagedRoomBoundManager(e){return this.registry.getManager(e)?.managedRoomBound===!0}setManagerWorkerDefaults(e,t,r){const n=this.registry.getManager(e);n&&this.registry.upsertManager({...n,defaultWorkerAgentType:t??null,defaultWorkerModelId:r??null})}getManagerWorkerDefaults(e){const t=this.registry.getManager(e);return{agentType:t?.defaultWorkerAgentType??null,modelId:t?.defaultWorkerModelId??null}}noteManagerAgentSession(e,t,r,n){const s=this.registry.getManager(e);this.registry.upsertManager({sessionId:e,agentSessionId:t,workDir:h(r),machineId:process.env.SHENNIAN_MACHINE_ID??null,modelId:n,agentType:s?.agentType,providerModelId:s?.providerModelId})}noteAgentEvent(e,t){const r=this.findWorker(e);if(!r)return;const n={lastActivityAt:new Date().toISOString(),runId:t.runId};t.agentSessionId&&(n.agentSessionId=t.agentSessionId);const s=`${e}:${t.runId}`;let i=t.message||r.summary||"(\u65E0\u53EF\u89C1\u5185\u5BB9)";if(t.state==="delta"&&t.text&&!t.thinking){const u=(this.workerTextAcc.get(s)??"")+t.text;this.workerTextAcc.set(s,u);const a=M(u);a&&(n.summary=a.length>160?`${a.slice(0,160)}...`:a)}if(t.state==="final"||t.state==="error"||t.state==="aborted"){n.status=t.state;const u=M(this.workerTextAcc.get(s)??"");u&&(n.summary=u.length>240?`${u.slice(0,240)}...`:u,i=t.message||u),this.workerTextAcc.delete(s)}else t.state==="start"&&(n.status="running");const d=this.registry.updateWorker(e,n);d&&(t.state==="final"||t.state==="error"||t.state==="aborted")&&this.enqueueWorkerEvent(d.managedBy,d,t.state,t.runId,i)}findWorker(e){return this.registry.load().workers[e]}async handleAppReq(e){G(this.opts.getRuntime(),e.id,!1,{error:"feature_retired"})}async handleIpc(e,t){if(e.headers.authorization!==`Bearer ${this.ipcToken}`){c(t,401,{ok:!1,error:"Unauthorized"});return}try{const r=new URL(e.url??"/","http://127.0.0.1"),n=await j(e);if(r.pathname.startsWith("/channel/")||r.pathname.startsWith("/external/")||r.pathname.startsWith("/wechat-rpa/")){c(t,410,{ok:!1,error:"feature_retired"});return}const s=await E({registry:this.registry,getRuntime:this.opts.getRuntime,dispatchReq:this.opts.dispatchReq,dispatchChatEnqueue:this.dispatchChatEnqueue.bind(this),appendManagerVisibleMarkdown:this.appendManagerVisibleMarkdown.bind(this)},r.pathname,n,String(e.headers["x-shennian-manager-session-id"]||""));if(s){c(t,s.status,s.body);return}c(t,404,{ok:!1,error:`Unknown manager IPC path: ${r.pathname}`})}catch(r){c(t,400,{ok:!1,error:r instanceof Error?r.message:String(r)})}}async executeScopedWorkerAction(e,t,r){const n={"manager.sessions.list":"/sessions/list","manager.sessions.start":"/sessions/start","manager.sessions.send":"/sessions/send","manager.sessions.queue":"/sessions/queue","manager.sessions.queue.edit":"/sessions/queue/edit","manager.sessions.queue.delete":"/sessions/queue/delete","manager.sessions.stop":"/sessions/stop","manager.sessions.read":"/sessions/read","manager.memory.path":"/memory/path"},s=await E({registry:this.registry,getRuntime:this.opts.getRuntime,dispatchReq:this.opts.dispatchReq,dispatchChatEnqueue:this.dispatchChatEnqueue.bind(this),appendManagerVisibleMarkdown:this.appendManagerVisibleMarkdown.bind(this)},n[t],r,e);if(!s||s.status<200||s.status>=300)throw new Error(String(s?.body.error||"Managed Manager action failed"));return JSON.parse(JSON.stringify(s.body))}async dispatchChatEnqueue(e,t,r,n,s,i,d){if((await this.opts.dispatchReq({type:"req",id:`manager-enqueue-${f()}`,method:"chat.enqueue",params:{...I(),sessionId:e,text:n,agentType:t,workDir:r,agentSessionId:s,modelId:i,managedWorkerParentSessionId:d}}))?.durableQueueHandoff===!1)throw new Error("manager_worker_queue_handoff_unconfirmed")}appendManagerVisibleMarkdown(e,t,r){this.registry.getManager(e)?.controllerKind!=="native_skill"&&(D(e,{id:r,sessionId:e,role:"agent",ts:Date.now(),payload:t},"committed"),W(this.opts.getRuntime().client))}enqueueWorkerEvent(e,t,r,n,s){const i=this.registry.enqueueInboxEvent({managerSessionId:e,workerSessionId:t.sessionId,kind:r==="final"?"worker.final":r==="error"?"worker.error":"worker.aborted",priority:"normal",runId:n??null,summary:s||t.summary||"(\u65E0\u53EF\u89C1\u6458\u8981)"}),d=this.registry.getManager(e);i&&d?.controllerKind!=="native_skill"&&this.drainManagerInbox(e)}scanWorkerHealth(){const e=Date.now(),t=this.registry.load(),r=this.opts.getRuntime();for(const n of Object.values(t.workers)){if(n.status!=="running")continue;const s=e-Date.parse(n.createdAt);if(s<10*6e4)continue;const i=e-Date.parse(n.lastActivityAt),d=r.sessions.get(n.sessionId)?.currentRunId,u=!!r.chatQueue?.getSnapshot(n.sessionId).pending.length;if(!d){if(!u&&i>=10*6e4){const w="\u672A\u627E\u5230\u6D3B\u52A8\u4E2D\u7684 Provider Run \u6216\u5F85\u5904\u7406\u6D88\u606F\uFF1B\u5DF2\u5C06\u9057\u7559\u7684\u8FD0\u884C\u72B6\u6001\u6536\u655B\u4E3A\u5931\u8D25\u3002\u539F Worker \u4F1A\u8BDD\u4ECD\u4FDD\u7559\uFF0C\u53EF\u7EE7\u7EED\u53D1\u9001\u65B0\u6D88\u606F\u3002",p=this.registry.updateWorker(n.sessionId,{status:"error",lastActivityAt:new Date(e).toISOString(),summary:w});p&&this.enqueueWorkerEvent(p.managedBy,p,"error",n.runId??void 0,w)}continue}const a=n.healthNotifiedAt?Date.parse(n.healthNotifiedAt):0;if(a&&e-a<10*6e4)continue;const l=t.managers[n.managedBy];if(!l)continue;const A=Math.max(0,Math.floor(i/6e4)),b=M(n.runId?this.workerTextAcc.get(`${n.sessionId}:${n.runId}`)??"":""),k=`\u5DF2\u8FD0\u884C ${Math.floor(s/6e4)} \u5206\u949F\uFF0C\u5C1A\u672A\u7ED3\u675F\u3002
1
+ import x from"node:http";import{createHash as _,randomBytes as N,randomUUID as y}from"node:crypto";import l from"node:fs";import R from"node:os";import I from"node:path";import{ManagerRegistry as T}from"./registry.js";import{appendMessage as W}from"../session/store.js";import{requestSessionIndexPublish as D}from"../session/index-publisher.js";import{RetiredChannelRuntime as q}from"./retired-external-channel-compat.js";import{resolveShennianPath as P}from"../config/index.js";import{buildExternalChannelInstructions as C}from"../agents/external-channel-instructions.js";import{getPersonalSyncIdentity as M}from"../personal-sync/epoch.js";import{handleWorkerIpcRoute as E}from"./worker-ipc.js";import{deriveWorkerTitle as B,formatWorkerMarkdown as O}from"./worker-markdown.js";const H=Number(process.env.SHENNIAN_MANAGER_IPC_BODY_MAX_BYTES||2*1024*1024),p=12*6e4;let A=null;function ae(d){A=d}function oe(){return A}function g(d){return I.resolve(d||R.homedir())}function m(d,e,t){d.writeHead(e,{"content-type":"application/json; charset=utf-8"}),d.end(JSON.stringify(t))}function k(d){return d.replace(/\r\n/g,`
2
+ `).trim()}function $(d){return d==="worker.final"?"completed":d==="worker.error"?"failed":d==="worker.aborted"?"stopped":"progress"}function U(d,e){return`manager-inbox:${_("sha256").update(d).update("\0").update(e.map(r=>r.id).join("\0")).digest("hex").slice(0,32)}`}async function j(d){const e=[];let t=0;for await(const r of d){const n=Buffer.isBuffer(r)?r:Buffer.from(r);if(t+=n.length,t>H)throw new Error("Request body too large");e.push(n)}return e.length===0?{}:JSON.parse(Buffer.concat(e).toString("utf8"))}function G(d,e,t,r){d.client.sendRes({type:"res",id:e,ok:t,...t?{payload:r}:{error:String(r.error||"unknown error")}})}function S(){return F()?I.join(R.tmpdir(),"shennian-vitest-runtime",String(process.pid),"manager-ipc.json"):P("runtime","manager-ipc.json")}function F(){return(process.env.VITEST==="true"||!!process.env.VITEST_WORKER_ID)&&!process.env.SHENNIAN_HOME?.trim()}class ue{opts;managedPolicies=new Map;registry=new T;channelRuntime;server=null;ipcUrl=null;ipcToken=N(24).toString("hex");healthTimer=null;startPromise=null;workerTextAcc=new Map;constructor(e){this.opts=e,this.channelRuntime=e.channelRuntime??new q}async start(){return this.startPromise?this.startPromise:(this.startPromise=this.doStart(),this.startPromise)}async doStart(){if(this.server)return;this.server=x.createServer((t,r)=>{this.handleIpc(t,r)}),this.server.requestTimeout=p,this.server.timeout=p,this.server.keepAliveTimeout=p,this.server.headersTimeout=p+5e3,await new Promise((t,r)=>{this.server.once("error",r),this.server.listen(0,"127.0.0.1",()=>t())});const e=this.server.address();typeof e=="object"&&e&&(this.ipcUrl=`http://127.0.0.1:${e.port}`,this.writeIpcRuntimeFile()),this.server.unref(),await this.channelRuntime.start(),this.healthTimer=setInterval(()=>this.scanWorkerHealth(),6e4),this.healthTimer.unref(),this.recoverPersistedInboxDeliveries()}async ready(){await this.start()}async stop(){this.healthTimer&&clearInterval(this.healthTimer),this.healthTimer=null,await this.channelRuntime.stop(),await new Promise(e=>{if(!this.server)return e();this.server.close(()=>e())}),this.server=null,this.ipcUrl=null,this.removeIpcRuntimeFile()}writeIpcRuntimeFile(){if(this.ipcUrl)try{const e=S();l.mkdirSync(I.dirname(e),{recursive:!0}),l.writeFileSync(e,JSON.stringify({url:this.ipcUrl,token:this.ipcToken,pid:process.pid,updatedAt:new Date().toISOString()},null,2),{mode:384}),l.chmodSync(e,384)}catch{}}removeIpcRuntimeFile(){try{const e=S(),t=JSON.parse(l.readFileSync(e,"utf-8"));(t.url===this.ipcUrl||t.token===this.ipcToken)&&l.unlinkSync(e)}catch{}}getInjectedEnv(e,t,r,n){return this.ipcUrl?{SHENNIAN_MANAGER_SESSION_ID:e,SHENNIAN_MANAGER_AGENT_SESSION_ID:t??"",SHENNIAN_MANAGER_WORKDIR:g(r),SHENNIAN_MANAGER_MODEL:n,SHENNIAN_MANAGER_IPC_URL:this.ipcUrl,SHENNIAN_MANAGER_IPC_TOKEN:this.ipcToken}:{}}registerManager(e){this.registry.upsertManager({...e,workDir:g(e.workDir),machineId:process.env.SHENNIAN_MACHINE_ID??null,managedRoomBound:e.managedRoomBound??(this.managedPolicies.has(e.sessionId)||void 0)})}setManagedAgentPolicy(e,t){if(t){this.managedPolicies.set(e,t);const r=this.registry.getManager(e);r&&!r.managedRoomBound&&this.registry.upsertManager({...r,managedRoomBound:!0})}else this.managedPolicies.delete(e)}getManagedAgentPolicyForWorker(e,t){const r=this.registry.getManager(e),n=this.managedPolicies.get(e);return!r||!n||g(t)!==r.workDir?null:n}isNativeSkillManager(e,t){const r=this.registry.getManager(e);return r?.controllerKind==="native_skill"&&g(t)===r.workDir}isManagedWorkerSession(e){return!!this.findWorker(e)}isManagedRoomBoundManager(e){return this.registry.getManager(e)?.managedRoomBound===!0}async retireManagerAndWorkers(e){const t=this.registry.retireManager(e);this.managedPolicies.delete(e);const r=this.opts.getRuntime(),n=[],s=async(i,h)=>{const o=[];try{r.runJournal?.markSessionAborted(i)}catch(c){o.push(c)}try{r.recoveryBarrier?.end(i)}catch(c){o.push(c)}try{r.chatQueue?.purgeSession(i)}catch(c){o.push(c)}const a=r.sessions.get(i);let u=!a;if(a){if(a.managedRoomContextToken)try{r.revokeManagedRoomContextToken?.(a.managedRoomContextToken),a.managedRoomContextToken=null}catch(c){o.push(c)}try{await a.adapter.stop(),u=!0}catch(c){o.push(c)}}if(a&&u&&o.length===0&&(r.sessions.delete(i),a.heartbeatTimer&&clearInterval(a.heartbeatTimer),a.adapter.removeAllListeners()),u&&o.length===0)try{h?.()}catch(c){o.push(c),n.push(c)}else n.push(...o)};await s(e);for(const i of t)await s(i.sessionId,()=>this.registry.completeRetiredWorker(e,i.sessionId));if(n.length)throw new AggregateError(n,"Failed to retire every Manager or Worker session");return t.map(i=>i.sessionId)}setManagerWorkerDefaults(e,t,r){const n=this.registry.getManager(e);n&&this.registry.upsertManager({...n,defaultWorkerAgentType:t??null,defaultWorkerModelId:r??null})}getManagerWorkerDefaults(e){const t=this.registry.getManager(e);return{agentType:t?.defaultWorkerAgentType??null,modelId:t?.defaultWorkerModelId??null}}noteManagerAgentSession(e,t,r,n){const s=this.registry.getManager(e);this.registry.upsertManager({sessionId:e,agentSessionId:t,workDir:g(r),machineId:process.env.SHENNIAN_MACHINE_ID??null,modelId:n,agentType:s?.agentType,providerModelId:s?.providerModelId})}noteAgentEvent(e,t){const r=this.findWorker(e);if(!r)return;const n={lastActivityAt:new Date().toISOString(),runId:t.runId};t.agentSessionId&&(n.agentSessionId=t.agentSessionId);const s=`${e}:${t.runId}`;let i=t.message||r.summary||"(\u65E0\u53EF\u89C1\u5185\u5BB9)";if(t.state==="delta"&&t.text&&!t.thinking){const o=(this.workerTextAcc.get(s)??"")+t.text;this.workerTextAcc.set(s,o);const a=k(o);a&&(n.summary=a.length>160?`${a.slice(0,160)}...`:a)}if(t.state==="final"||t.state==="error"||t.state==="aborted"){n.status=t.state;const o=k(this.workerTextAcc.get(s)??"");o&&(n.summary=o.length>240?`${o.slice(0,240)}...`:o,i=t.message||o),this.workerTextAcc.delete(s)}else t.state==="start"&&(n.status="running");const h=this.registry.updateWorker(e,n);h&&(t.state==="final"||t.state==="error"||t.state==="aborted")&&this.enqueueWorkerEvent(h.managedBy,h,t.state,t.runId,i)}findWorker(e){return this.registry.load().workers[e]}async handleAppReq(e){G(this.opts.getRuntime(),e.id,!1,{error:"feature_retired"})}async handleIpc(e,t){if(e.headers.authorization!==`Bearer ${this.ipcToken}`){m(t,401,{ok:!1,error:"Unauthorized"});return}try{const r=new URL(e.url??"/","http://127.0.0.1"),n=await j(e);if(r.pathname.startsWith("/channel/")||r.pathname.startsWith("/external/")||r.pathname.startsWith("/wechat-rpa/")){m(t,410,{ok:!1,error:"feature_retired"});return}const s=await E({registry:this.registry,getRuntime:this.opts.getRuntime,dispatchReq:this.opts.dispatchReq,dispatchChatEnqueue:this.dispatchChatEnqueue.bind(this),appendManagerVisibleMarkdown:this.appendManagerVisibleMarkdown.bind(this)},r.pathname,n,String(e.headers["x-shennian-manager-session-id"]||""));if(s){m(t,s.status,s.body);return}m(t,404,{ok:!1,error:`Unknown manager IPC path: ${r.pathname}`})}catch(r){m(t,400,{ok:!1,error:r instanceof Error?r.message:String(r)})}}async executeScopedWorkerAction(e,t,r){const n={"manager.sessions.list":"/sessions/list","manager.sessions.start":"/sessions/start","manager.sessions.send":"/sessions/send","manager.sessions.queue":"/sessions/queue","manager.sessions.queue.edit":"/sessions/queue/edit","manager.sessions.queue.delete":"/sessions/queue/delete","manager.sessions.stop":"/sessions/stop","manager.sessions.read":"/sessions/read","manager.memory.path":"/memory/path"},s=await E({registry:this.registry,getRuntime:this.opts.getRuntime,dispatchReq:this.opts.dispatchReq,dispatchChatEnqueue:this.dispatchChatEnqueue.bind(this),appendManagerVisibleMarkdown:this.appendManagerVisibleMarkdown.bind(this)},n[t],r,e);if(!s||s.status<200||s.status>=300)throw new Error(String(s?.body.error||"Managed Manager action failed"));return JSON.parse(JSON.stringify(s.body))}async dispatchChatEnqueue(e,t,r,n,s,i,h){if((await this.opts.dispatchReq({type:"req",id:`manager-enqueue-${y()}`,method:"chat.enqueue",params:{...M(),sessionId:e,text:n,agentType:t,workDir:r,agentSessionId:s,modelId:i,managedWorkerParentSessionId:h}}))?.durableQueueHandoff===!1)throw new Error("manager_worker_queue_handoff_unconfirmed")}appendManagerVisibleMarkdown(e,t,r){this.registry.getManager(e)?.controllerKind!=="native_skill"&&(W(e,{id:r,sessionId:e,role:"agent",ts:Date.now(),payload:t},"committed"),D(this.opts.getRuntime().client))}enqueueWorkerEvent(e,t,r,n,s){const i=this.registry.enqueueInboxEvent({managerSessionId:e,workerSessionId:t.sessionId,kind:r==="final"?"worker.final":r==="error"?"worker.error":"worker.aborted",priority:"normal",runId:n??null,summary:s||t.summary||"(\u65E0\u53EF\u89C1\u6458\u8981)"}),h=this.registry.getManager(e);i&&h?.controllerKind!=="native_skill"&&this.drainManagerInbox(e)}scanWorkerHealth(){const e=Date.now(),t=this.registry.load(),r=this.opts.getRuntime();for(const n of Object.values(t.workers)){if(n.status!=="running")continue;const s=e-Date.parse(n.createdAt);if(s<10*6e4)continue;const i=e-Date.parse(n.lastActivityAt),h=r.sessions.get(n.sessionId)?.currentRunId,o=!!r.chatQueue?.getSnapshot(n.sessionId).pending.length;if(!h){if(!o&&i>=10*6e4){const v="\u672A\u627E\u5230\u6D3B\u52A8\u4E2D\u7684 Provider Run \u6216\u5F85\u5904\u7406\u6D88\u606F\uFF1B\u5DF2\u5C06\u9057\u7559\u7684\u8FD0\u884C\u72B6\u6001\u6536\u655B\u4E3A\u5931\u8D25\u3002\u539F Worker \u4F1A\u8BDD\u4ECD\u4FDD\u7559\uFF0C\u53EF\u7EE7\u7EED\u53D1\u9001\u65B0\u6D88\u606F\u3002",f=this.registry.updateWorker(n.sessionId,{status:"error",lastActivityAt:new Date(e).toISOString(),summary:v});f&&this.enqueueWorkerEvent(f.managedBy,f,"error",n.runId??void 0,v)}continue}const a=n.healthNotifiedAt?Date.parse(n.healthNotifiedAt):0;if(a&&e-a<10*6e4)continue;const u=t.managers[n.managedBy];if(!u)continue;const c=Math.max(0,Math.floor(i/6e4)),b=k(n.runId?this.workerTextAcc.get(`${n.sessionId}:${n.runId}`)??"":""),w=`\u5DF2\u8FD0\u884C ${Math.floor(s/6e4)} \u5206\u949F\uFF0C\u5C1A\u672A\u7ED3\u675F\u3002
3
3
 
4
4
  \u5F53\u524D\u53EF\u89C1\u8FDB\u5C55\uFF1A
5
5
  - \u6700\u8FD1\u6587\u672C\uFF1A${b||n.summary||"(\u65E0\u53EF\u89C1\u5185\u5BB9)"}
6
6
  - \u6700\u8FD1\u6D3B\u52A8\u65F6\u95F4\uFF1A${n.lastActivityAt}
7
- - \u6700\u8FD1 ${A} \u5206\u949F\u6CA1\u6709\u65B0\u6D3B\u52A8\u3002`;this.registry.updateWorker(n.sessionId,{healthNotifiedAt:new Date(e).toISOString(),lastHealthSummary:k}),this.registry.enqueueInboxEvent({managerSessionId:l.sessionId,workerSessionId:n.sessionId,kind:"worker.health",priority:"normal",runId:n.runId??null,summary:k})&&l.controllerKind!=="native_skill"&&this.drainManagerInbox(l.sessionId)}}async interruptAndResumeManager(e,t,r){this.opts.getRuntime().sessions.get(e.sessionId)?.currentRunId&&await this.opts.dispatchReq({type:"req",id:`manager-interrupt-${f()}`,method:"chat.abort",params:{...I(),sessionId:e.sessionId}}),await this.dispatchManagerContinuation(e,t,!1,r)}bindManagerAdapterEvents(e,t){t.on("agentEvent",r=>{if(r.state==="start"&&r.agentSessionId){const n=this.registry.getManager(e);n&&this.noteManagerAgentSession(e,r.agentSessionId,n.workDir,n.modelId)}r.state==="start"&&(this.acknowledgeManagerInboxDelivery(e),this.updateManagerStatus(e,"running")),(r.state==="final"||r.state==="error"||r.state==="aborted")&&(this.updateManagerStatus(e,"idle"),queueMicrotask(()=>{this.drainManagerInbox(e)}))})}updateManagerStatus(e,t){const r=this.registry.getManager(e);r&&this.registry.upsertManager({...r,status:t})}async drainManagerInbox(e){const t=this.registry.getManager(e);if(!t||t.deliveringInboxEventIds?.length)return;const r=this.registry.listInboxEvents(e);if(!r.length)return;const n=r.find(a=>a.priority==="high"),s=this.opts.getRuntime().sessions.get(e);if(t.status!=="idle"&&!n||s?.currentRunId&&!n)return;const i=n?[n]:r.slice(0,20),d=i.map(a=>{const l=this.registry.getWorkerForManager(e,a.workerSessionId);return C({kind:$(a.kind),sessionId:a.workerSessionId,title:l?.title||O(l?.userGoal||l?.summary||a.summary),body:a.summary})}).join(`
7
+ - \u6700\u8FD1 ${c} \u5206\u949F\u6CA1\u6709\u65B0\u6D3B\u52A8\u3002`;this.registry.updateWorker(n.sessionId,{healthNotifiedAt:new Date(e).toISOString(),lastHealthSummary:w}),this.registry.enqueueInboxEvent({managerSessionId:u.sessionId,workerSessionId:n.sessionId,kind:"worker.health",priority:"normal",runId:n.runId??null,summary:w})&&u.controllerKind!=="native_skill"&&this.drainManagerInbox(u.sessionId)}}async interruptAndResumeManager(e,t,r){this.opts.getRuntime().sessions.get(e.sessionId)?.currentRunId&&await this.opts.dispatchReq({type:"req",id:`manager-interrupt-${y()}`,method:"chat.abort",params:{...M(),sessionId:e.sessionId}}),await this.dispatchManagerContinuation(e,t,!1,r)}bindManagerAdapterEvents(e,t){t.on("agentEvent",r=>{if(r.state==="start"&&r.agentSessionId){const n=this.registry.getManager(e);n&&this.noteManagerAgentSession(e,r.agentSessionId,n.workDir,n.modelId)}r.state==="start"&&(this.acknowledgeManagerInboxDelivery(e),this.updateManagerStatus(e,"running")),(r.state==="final"||r.state==="error"||r.state==="aborted")&&(this.updateManagerStatus(e,"idle"),queueMicrotask(()=>{this.drainManagerInbox(e)}))})}updateManagerStatus(e,t){const r=this.registry.getManager(e);r&&this.registry.upsertManager({...r,status:t})}async drainManagerInbox(e){const t=this.registry.getManager(e);if(!t||t.deliveringInboxEventIds?.length)return;const r=this.registry.listInboxEvents(e);if(!r.length)return;const n=r.find(a=>a.priority==="high"),s=this.opts.getRuntime().sessions.get(e);if(t.status!=="idle"&&!n||s?.currentRunId&&!n)return;const i=n?[n]:r.slice(0,20),h=i.map(a=>{const u=this.registry.getWorkerForManager(e,a.workerSessionId);return O({kind:$(a.kind),sessionId:a.workerSessionId,title:u?.title||B(u?.userGoal||u?.summary||a.summary),body:a.summary})}).join(`
8
8
 
9
9
  ---
10
10
 
11
- `),u=U(e,i);try{n&&s?.currentRunId?(this.registry.upsertManager({...t,status:"interrupting",deliveringInboxEventIds:i.map(l=>l.id)}),await this.interruptAndResumeManager(t,d,u)):(this.registry.upsertManager({...t,status:"running",deliveringInboxEventIds:i.map(l=>l.id)}),await this.dispatchManagerContinuation(t,d,!0,u)),this.registry.removeInboxEvents(e,i.map(l=>l.id));const a=this.registry.getManager(e);a&&this.registry.upsertManager({...a,deliveringInboxEventIds:[]})}catch(a){this.registry.upsertManager({...t,status:"idle",deliveringInboxEventIds:[]}),console.error(`[manager-inbox] delivery failed sessionId=${e}: ${a instanceof Error?a.message:String(a)}`)}}acknowledgeManagerInboxDelivery(e){const t=this.registry.getManager(e),r=t?.deliveringInboxEventIds??[];!t||r.length===0||(this.registry.removeInboxEvents(e,r),this.registry.upsertManager({...t,deliveringInboxEventIds:[]}))}recoverPersistedInboxDeliveries(){const e=this.registry.load(),t=this.opts.getRuntime();for(const r of Object.values(e.managers)){if(t.sessions.get(r.sessionId)?.currentRunId)continue;(r.status!=="idle"||r.deliveringInboxEventIds?.length)&&this.registry.upsertManager({...r,status:"idle",deliveringInboxEventIds:[]});const s=this.registry.listInboxEvents(r.sessionId).filter(i=>i.kind==="worker.health"&&!t.sessions.get(i.workerSessionId)?.currentRunId).map(i=>i.id);this.registry.removeInboxEvents(r.sessionId,s),this.registry.listInboxEvents(r.sessionId).length>0&&queueMicrotask(()=>{this.drainManagerInbox(r.sessionId)})}}async dispatchManagerContinuation(e,t,r=!1,n){const s=e.agentType??"manager",i=s==="manager"?e.modelId:e.providerModelId??void 0,d={...I(),sessionId:e.sessionId,text:t,agentType:s,sessionMode:"manager",workDir:e.workDir,agentSessionId:e.agentSessionId,modelId:i,managerDefaultWorkerAgentType:e.defaultWorkerAgentType??null,managerDefaultWorkerModelId:e.defaultWorkerModelId??null,...e.managedRoomBound?{managedAgentDirect:!0}:{},clientMessageId:n,managerEventId:n};if((await this.opts.dispatchReq({type:"req",id:`manager-${r?"enqueue":"send"}-${f()}`,method:"chat.enqueue",params:d}))?.durableQueueHandoff===!1)throw new Error("manager_event_queue_handoff_unconfirmed")}getExternalChannelStatus(e){return this.channelRuntime.getManagerChannelStatus(e)}getManagerExternalChannelSystemPrompt(e){return this.channelRuntime.listManagerExternalChannels(e).map(t=>B(t,void 0,e,"manager")).join(`
11
+ `),o=U(e,i);try{n&&s?.currentRunId?(this.registry.upsertManager({...t,status:"interrupting",deliveringInboxEventIds:i.map(u=>u.id)}),await this.interruptAndResumeManager(t,h,o)):(this.registry.upsertManager({...t,status:"running",deliveringInboxEventIds:i.map(u=>u.id)}),await this.dispatchManagerContinuation(t,h,!0,o)),this.registry.removeInboxEvents(e,i.map(u=>u.id));const a=this.registry.getManager(e);a&&this.registry.upsertManager({...a,deliveringInboxEventIds:[]})}catch(a){const u=this.registry.getManager(e);u&&this.registry.upsertManager({...u,status:"idle",deliveringInboxEventIds:[]}),console.error(`[manager-inbox] delivery failed sessionId=${e}: ${a instanceof Error?a.message:String(a)}`)}}acknowledgeManagerInboxDelivery(e){const t=this.registry.getManager(e),r=t?.deliveringInboxEventIds??[];!t||r.length===0||(this.registry.removeInboxEvents(e,r),this.registry.upsertManager({...t,deliveringInboxEventIds:[]}))}recoverPersistedInboxDeliveries(){const e=this.registry.load(),t=this.opts.getRuntime();for(const r of Object.values(e.managers)){if(t.sessions.get(r.sessionId)?.currentRunId)continue;(r.status!=="idle"||r.deliveringInboxEventIds?.length)&&this.registry.upsertManager({...r,status:"idle",deliveringInboxEventIds:[]});const s=this.registry.listInboxEvents(r.sessionId).filter(i=>i.kind==="worker.health"&&!t.sessions.get(i.workerSessionId)?.currentRunId).map(i=>i.id);this.registry.removeInboxEvents(r.sessionId,s),this.registry.listInboxEvents(r.sessionId).length>0&&queueMicrotask(()=>{this.drainManagerInbox(r.sessionId)})}}async dispatchManagerContinuation(e,t,r=!1,n){const s=e.agentType??"manager",i=s==="manager"?e.modelId:e.providerModelId??void 0,h={...M(),sessionId:e.sessionId,text:t,agentType:s,sessionMode:"manager",workDir:e.workDir,agentSessionId:e.agentSessionId,modelId:i,managerDefaultWorkerAgentType:e.defaultWorkerAgentType??null,managerDefaultWorkerModelId:e.defaultWorkerModelId??null,...e.managedRoomBound?{managedAgentDirect:!0}:{},clientMessageId:n,managerEventId:n};if((await this.opts.dispatchReq({type:"req",id:`manager-${r?"enqueue":"send"}-${y()}`,method:"chat.enqueue",params:h}))?.durableQueueHandoff===!1)throw new Error("manager_event_queue_handoff_unconfirmed")}getExternalChannelStatus(e){return this.channelRuntime.getManagerChannelStatus(e)}getManagerExternalChannelSystemPrompt(e){return this.channelRuntime.listManagerExternalChannels(e).map(t=>C(t,void 0,e,"manager")).join(`
12
12
 
13
13
  `).trim()}}export{ue as ManagerRuntimeService,oe as getManagerRuntimeService,ae as setManagerRuntimeService};
@@ -1 +1 @@
1
- import f from"node:path";import{createHash as h,randomUUID as w}from"node:crypto";import{AVAILABLE_BUILTIN_AGENT_TYPES as M,extractPayloadText as q,isAgentHiddenPayload as W,isToolPayload as _}from"@shennian/wire";import{loadConfig as E}from"../config/index.js";import{getPersonalSyncIdentity as y}from"../personal-sync/epoch.js";import{readMessages as T,recordSession as v}from"../session/store.js";import{deriveWorkerTitle as c,formatWorkerMarkdown as k,normalizeWorkerTitle as x}from"./worker-markdown.js";function S(e){return f.resolve(e||process.cwd())}function b(e){return e==="manager"||e==="pi"?!1:e.startsWith("custom:")?!0:M.includes(e)}function D(e){return e==="worker.final"?"completed":e==="worker.error"?"failed":e==="worker.aborted"?"stopped":"progress"}function A(e,u){const s=[];for(const g of e)if(!W(g.payload))if(_(g.payload)){let a="tool",l="completed";try{const d=JSON.parse(g.payload);a=d.name||a,l=d.status||l}catch{}s.push({...g,payload:`[\u5DE5\u5177] ${a} \xB7 ${l}`})}else s.push({...g,payload:q(g.payload)});return s.slice(-Math.max(1,Math.min(u,500)))}async function K(e,u,s,g){if(u==="/skill/context"){const r=E(),n=String(s.host||""),i=String(s.hostSessionId||""),o=S(String(s.workDir||""));if(!r.machineId||!r.machineToken&&!r.accessToken)throw new Error("Shennian CLI must be paired and logged in");if(n!=="codex"&&n!=="claude")throw new Error("unsupported_native_agent_host");if(!i)throw new Error("native_agent_session_unavailable");if(!o||!f.isAbsolute(o))throw new Error("workDir is required");const m=`skill_manager_${h("sha256").update(r.machineId).update("\0").update(n).update("\0").update(i).update("\0").update(o).digest("hex").slice(0,32)}`;return e.registry.upsertManager({sessionId:m,agentSessionId:null,workDir:o,machineId:r.machineId,modelId:n,agentType:n,controllerKind:"native_skill",status:"idle"}),{status:200,body:{ok:!0,managerSessionId:m}}}if(!u.startsWith("/sessions/")&&u!=="/memory/path")return null;const a=String(s.managerSessionId||g||"");if(!a)throw new Error("managerSessionId is required");const l=e.registry.getManager(a);if(!l)throw new Error("Manager runtime is not registered");if(u==="/sessions/list"){const r=new Set([...e.getRuntime().sessions.entries()].filter(([,o])=>!!o.currentRunId).map(([o])=>o)),n=e.registry.listWorkers(a,{runningSessionIds:r});return{status:200,body:{ok:!0,sessions:l.controllerKind==="native_skill"?n.filter(o=>o.role==="managed_worker"&&o.managedBy===a):n}}}if(u==="/memory/path")return{status:200,body:{ok:!0,path:f.join(l.workDir,".shennian")}};if(u==="/sessions/start"){const r=String(s.agentType||s.agent||l.defaultWorkerAgentType||"codex");if(!b(r))throw new Error(`Unsupported manager worker agent: ${r}`);const n=S(String(s.workDir||l.workDir));if(n!==l.workDir)throw new Error("Manager can only start workers in the same workDir");const i=String(s.message||"");if(!i)throw new Error("message is required");const o=s.title?x(String(s.title)):c(i),m=e.registry.addWorker({managerSessionId:a,agentType:r,workDir:n,title:o,summary:i.slice(0,120)}),p=String(s.modelId||(r===l.defaultWorkerAgentType?l.defaultWorkerModelId??"":""));v({sessionId:m.sessionId,agentType:r,workDir:n,agentSessionId:null,modelId:p||null,title:o,titleSource:"shennian_generated"}),await e.dispatchChatEnqueue(m.sessionId,r,n,i,null,p||void 0,a);const I=k({kind:"created",sessionId:m.sessionId,title:o,body:i});return e.appendManagerVisibleMarkdown(a,I,`worker-created:${m.sessionId}`),{status:200,body:{ok:!0,session:m,markdown:I}}}const d=String(s.sessionId||""),t=e.registry.getWorkerForManager(a,d);if(!t||l.controllerKind==="native_skill"&&(t.role!=="managed_worker"||t.managedBy!==a))throw new Error("Worker not found in this manager scope");if(u==="/sessions/send"){const r=String(s.message||"");if(!r.trim())throw new Error("message is required");const n=s.enqueue===void 0?!0:!!s.enqueue,i=String(s.modelId||(t.agentType===l.defaultWorkerAgentType?l.defaultWorkerModelId??"":""));await e.dispatchChatEnqueue(t.sessionId,t.agentType,t.workDir,r,t.agentSessionId??null,i||void 0,a);const o=k({kind:"message",sessionId:d,title:t.title||c(t.userGoal||t.summary||r),body:r});return e.appendManagerVisibleMarkdown(a,o,`worker-message:${w()}`),{status:200,body:{ok:!0,markdown:o,queued:!0,directRequested:!n}}}if(u==="/sessions/queue")return{status:200,body:{ok:!0,queue:e.getRuntime().chatQueue?.getSnapshot(d)}};if(u==="/sessions/queue/edit"||u==="/sessions/queue/delete"){const r=u.endsWith("/edit");return await e.dispatchReq({type:"req",id:`manager-queue-${r?"edit":"delete"}-${w()}`,method:r?"chat.queue.edit":"chat.queue.delete",params:{...y(),sessionId:d,queueMessageId:String(s.queueMessageId||s.messageId||""),...r?{text:String(s.message||s.text||"")}:{}}}),{status:200,body:{ok:!0,queue:e.getRuntime().chatQueue?.getSnapshot(d)}}}if(u==="/sessions/stop"||u==="/sessions/terminate"){if(t.status==="final"||t.status==="error"||t.status==="aborted"){const r=t.status==="final"?"completed":t.status==="error"?"failed":"stopped";return{status:200,body:{ok:!0,alreadyTerminal:!0,status:t.status,markdown:k({kind:r,sessionId:d,title:t.title||c(t.userGoal||t.summary||""),body:t.summary||"\u672C\u6B21\u6267\u884C\u5DF2\u7ECF\u7ED3\u675F\u3002"})}}}return await e.dispatchReq({type:"req",id:`manager-abort-${w()}`,method:"chat.abort",params:{...y(),sessionId:d}}),e.registry.updateWorker(d,{status:"aborted"}),{status:200,body:{ok:!0,markdown:k({kind:"stopped",sessionId:d,title:t.title||c(t.userGoal||t.summary||""),body:"\u5DF2\u505C\u6B62\u672C\u6B21\u6267\u884C\u3002"})}}}if(u==="/sessions/read"){const r=Number(s.limit||200),n=T(d,{limit:Math.max(r*20,r)});return{status:200,body:{ok:!0,messages:A(n,r),rawMessageCount:n.length}}}if(u==="/sessions/wait"){const r=Math.max(1e3,Math.min(Number(s.timeoutMs||6e5),6e5)),n=Date.now()+r;let i;for(;Date.now()<n&&(i=e.registry.listInboxEvents(a).find(m=>m.workerSessionId===d),!i);)await new Promise(m=>setTimeout(m,1e3));if(!i)return{status:200,body:{ok:!0,timedOut:!0,status:e.registry.getWorkerForManager(a,d)?.status}};const o=k({kind:D(i.kind),sessionId:d,title:t.title||c(t.userGoal||t.summary||i.summary),body:i.summary});return e.registry.removeInboxEvents(a,[i.id]),{status:200,body:{ok:!0,timedOut:!1,markdown:o,event:i}}}return null}export{K as handleWorkerIpcRoute};
1
+ import f from"node:path";import{createHash as h,randomUUID as w}from"node:crypto";import{AVAILABLE_BUILTIN_AGENT_TYPES as M,extractPayloadText as q,isAgentHiddenPayload as W,isToolPayload as E}from"@shennian/wire";import{loadConfig as T}from"../config/index.js";import{getPersonalSyncIdentity as y}from"../personal-sync/epoch.js";import{readMessages as _,recordSession as x}from"../session/store.js";import{deriveWorkerTitle as c,formatWorkerMarkdown as k,normalizeWorkerTitle as D}from"./worker-markdown.js";function S(e){return f.resolve(e||process.cwd())}function b(e){return e==="manager"||e==="pi"?!1:e.startsWith("custom:")?!0:M.includes(e)}function v(e){return e==="worker.final"?"completed":e==="worker.error"?"failed":e==="worker.aborted"?"stopped":"progress"}function A(e,i){const s=[];for(const g of e)if(!W(g.payload))if(E(g.payload)){let a="tool",d="completed";try{const u=JSON.parse(g.payload);a=u.name||a,d=u.status||d}catch{}s.push({...g,payload:`[\u5DE5\u5177] ${a} \xB7 ${d}`})}else s.push({...g,payload:q(g.payload)});return s.slice(-Math.max(1,Math.min(i,500)))}async function L(e,i,s,g){if(i==="/skill/context"){const r=T(),o=String(s.host||""),n=String(s.hostSessionId||""),l=S(String(s.workDir||""));if(!r.machineId||!r.machineToken&&!r.accessToken)throw new Error("Shennian CLI must be paired and logged in");if(o!=="codex"&&o!=="claude")throw new Error("unsupported_native_agent_host");if(!n)throw new Error("native_agent_session_unavailable");if(!l||!f.isAbsolute(l))throw new Error("workDir is required");const m=`skill_manager_${h("sha256").update(r.machineId).update("\0").update(o).update("\0").update(n).update("\0").update(l).digest("hex").slice(0,32)}`;return e.registry.upsertManager({sessionId:m,agentSessionId:null,workDir:l,machineId:r.machineId,modelId:o,agentType:o,controllerKind:"native_skill",status:"idle"}),{status:200,body:{ok:!0,managerSessionId:m}}}if(!i.startsWith("/sessions/")&&i!=="/memory/path")return null;const a=String(s.managerSessionId||g||"");if(!a)throw new Error("managerSessionId is required");const d=e.registry.getManager(a);if(!d)throw new Error("Manager runtime is not registered");if(i==="/sessions/list"){const r=new Set([...e.getRuntime().sessions.entries()].filter(([,n])=>!!n.currentRunId).map(([n])=>n));return{status:200,body:{ok:!0,sessions:e.registry.listWorkers(a,{runningSessionIds:r})}}}if(i==="/memory/path")return{status:200,body:{ok:!0,path:f.join(d.workDir,".shennian")}};if(i==="/sessions/start"){const r=String(s.agentType||s.agent||d.defaultWorkerAgentType||"codex");if(!b(r))throw new Error(`Unsupported manager worker agent: ${r}`);const o=S(String(s.workDir||d.workDir));if(o!==d.workDir)throw new Error("Manager can only start workers in the same workDir");const n=String(s.message||"");if(!n)throw new Error("message is required");const l=s.title?D(String(s.title)):c(n),m=e.registry.addWorker({managerSessionId:a,agentType:r,workDir:o,title:l,summary:n.slice(0,120)}),p=String(s.modelId||(r===d.defaultWorkerAgentType?d.defaultWorkerModelId??"":""));x({sessionId:m.sessionId,agentType:r,workDir:o,agentSessionId:null,modelId:p||null,title:l,titleSource:"shennian_generated"}),await e.dispatchChatEnqueue(m.sessionId,r,o,n,null,p||void 0,a);const I=k({kind:"created",sessionId:m.sessionId,title:l,body:n});return e.appendManagerVisibleMarkdown(a,I,`worker-created:${m.sessionId}`),{status:200,body:{ok:!0,session:m,markdown:I}}}const u=String(s.sessionId||""),t=e.registry.getWorkerForManager(a,u);if(!t||t.role!=="managed_worker"||t.managedBy!==a)throw new Error("Worker not found in this manager scope");if(i==="/sessions/send"){const r=String(s.message||"");if(!r.trim())throw new Error("message is required");const o=s.enqueue===void 0?!0:!!s.enqueue,n=String(s.modelId||(t.agentType===d.defaultWorkerAgentType?d.defaultWorkerModelId??"":""));await e.dispatchChatEnqueue(t.sessionId,t.agentType,t.workDir,r,t.agentSessionId??null,n||void 0,a);const l=k({kind:"message",sessionId:u,title:t.title||c(t.userGoal||t.summary||r),body:r});return e.appendManagerVisibleMarkdown(a,l,`worker-message:${w()}`),{status:200,body:{ok:!0,markdown:l,queued:!0,directRequested:!o}}}if(i==="/sessions/queue")return{status:200,body:{ok:!0,queue:e.getRuntime().chatQueue?.getSnapshot(u)}};if(i==="/sessions/queue/edit"||i==="/sessions/queue/delete"){const r=i.endsWith("/edit");return await e.dispatchReq({type:"req",id:`manager-queue-${r?"edit":"delete"}-${w()}`,method:r?"chat.queue.edit":"chat.queue.delete",params:{...y(),sessionId:u,queueMessageId:String(s.queueMessageId||s.messageId||""),...r?{text:String(s.message||s.text||"")}:{}}}),{status:200,body:{ok:!0,queue:e.getRuntime().chatQueue?.getSnapshot(u)}}}if(i==="/sessions/stop"||i==="/sessions/terminate"){if(t.status==="final"||t.status==="error"||t.status==="aborted"){const r=t.status==="final"?"completed":t.status==="error"?"failed":"stopped";return{status:200,body:{ok:!0,alreadyTerminal:!0,status:t.status,markdown:k({kind:r,sessionId:u,title:t.title||c(t.userGoal||t.summary||""),body:t.summary||"\u672C\u6B21\u6267\u884C\u5DF2\u7ECF\u7ED3\u675F\u3002"})}}}return await e.dispatchReq({type:"req",id:`manager-abort-${w()}`,method:"chat.abort",params:{...y(),sessionId:u}}),e.registry.updateWorker(u,{status:"aborted"}),{status:200,body:{ok:!0,markdown:k({kind:"stopped",sessionId:u,title:t.title||c(t.userGoal||t.summary||""),body:"\u5DF2\u505C\u6B62\u672C\u6B21\u6267\u884C\u3002"})}}}if(i==="/sessions/read"){const r=Number(s.limit||200),o=_(u,{limit:Math.max(r*20,r)});return{status:200,body:{ok:!0,messages:A(o,r),rawMessageCount:o.length}}}if(i==="/sessions/wait"){const r=Math.max(1e3,Math.min(Number(s.timeoutMs||6e5),6e5)),o=Date.now()+r;let n;for(;Date.now()<o&&(n=e.registry.listInboxEvents(a).find(m=>m.workerSessionId===u),!n);)await new Promise(m=>setTimeout(m,1e3));if(!n)return{status:200,body:{ok:!0,timedOut:!0,status:e.registry.getWorkerForManager(a,u)?.status}};const l=k({kind:v(n.kind),sessionId:u,title:t.title||c(t.userGoal||t.summary||n.summary),body:n.summary});return e.registry.removeInboxEvents(a,[n.id]),{status:200,body:{ok:!0,timedOut:!1,markdown:l,event:n}}}return null}export{L as handleWorkerIpcRoute};
@@ -1 +1 @@
1
- import{isAvailableAgentType as w}from"@shennian/wire";const f=/^[A-Za-z0-9_.:@/-]{1,191}$/,S=new Set(["roomId","agentIdentityId","nianSessionId","assignmentIntentId","agentType","activationMode"]),l=new Set(["roomId","bindingId","agentIdentityId","previousNianSessionId","nianSessionId","assignmentIntentId","agentType"]);async function M(n,t,e){if(!t)throw new Error("Managed Room binding is unavailable.");if(Object.keys(e.params).some(m=>!S.has(m)))throw new Error("Managed Room binding contains unsupported parameters.");const r=a(e.params.roomId,"roomId"),d=a(e.params.agentIdentityId,"agentIdentityId"),o=a(e.params.nianSessionId,"nianSessionId"),s=a(e.params.assignmentIntentId,"assignmentIntentId"),i=c(e.params.agentType),I=R(e.params.activationMode);await p(n,o);const g=await t.bind({roomId:r,agentIdentityId:d,nianSessionId:o,assignmentIntentId:s,sourceSessionKey:y(o),agentType:i,activationMode:I});n.client.sendRes({type:"res",id:e.id,ok:!0,payload:g})}async function b(n,t,e){if(!t)throw new Error("Managed Room binding is unavailable.");if(Object.keys(e.params).some(u=>!l.has(u)))throw new Error("Managed Room restart contains unsupported parameters.");const r=a(e.params.roomId,"roomId"),d=a(e.params.bindingId,"bindingId"),o=a(e.params.agentIdentityId,"agentIdentityId"),s=a(e.params.previousNianSessionId,"previousNianSessionId"),i=a(e.params.nianSessionId,"nianSessionId"),I=a(e.params.assignmentIntentId,"assignmentIntentId"),g=c(e.params.agentType);await p(n,s),n.chatQueue?.purgeSession(s),await p(n,i);const m=await t.restart({roomId:r,bindingId:d,agentIdentityId:o,previousNianSessionId:s,nianSessionId:i,assignmentIntentId:I,sourceSessionKey:y(i),agentType:g});n.client.sendRes({type:"res",id:e.id,ok:!0,payload:m})}function a(n,t){if(typeof n!="string"||!f.test(n))throw new Error(`Invalid ${t}.`);return n}function c(n){if(typeof n!="string"||!w(n)||n==="manager"||n==="pi"||n==="openclaw")throw new Error("managed_agent_runtime_unsupported");return n}function R(n){if(n===void 0)return"auto_follow";if(n==="auto_follow"||n==="mention_only"||n==="paused")return n;throw new Error("Invalid activationMode.")}function y(n){return`managed:${n}`}async function p(n,t){n.runJournal?.markSessionAborted(t),n.recoveryBarrier?.end(t);const e=n.sessions.get(t);e&&(n.sessions.delete(t),e.managedRoomContextToken&&(n.revokeManagedRoomContextToken?.(e.managedRoomContextToken),e.managedRoomContextToken=null),e.heartbeatTimer&&clearInterval(e.heartbeatTimer),e.adapter.removeAllListeners(),await e.adapter.stop())}export{p as discardPreBindingSession,M as handleManagedRoomBind,b as handleManagedRoomRestart};
1
+ import{isAvailableAgentType as w}from"@shennian/wire";const f=/^[A-Za-z0-9_.:@/-]{1,191}$/,S=new Set(["roomId","agentIdentityId","nianSessionId","assignmentIntentId","agentType","activationMode"]),l=new Set(["roomId","bindingId","agentIdentityId","previousNianSessionId","nianSessionId","assignmentIntentId","agentType"]);async function T(n,t,e){if(!t)throw new Error("Managed Room binding is unavailable.");if(Object.keys(e.params).some(m=>!S.has(m)))throw new Error("Managed Room binding contains unsupported parameters.");const r=a(e.params.roomId,"roomId"),d=a(e.params.agentIdentityId,"agentIdentityId"),s=a(e.params.nianSessionId,"nianSessionId"),o=a(e.params.assignmentIntentId,"assignmentIntentId"),i=c(e.params.agentType),I=R(e.params.activationMode);await p(n,s);const g=await t.bind({roomId:r,agentIdentityId:d,nianSessionId:s,assignmentIntentId:o,sourceSessionKey:y(s),agentType:i,activationMode:I});n.client.sendRes({type:"res",id:e.id,ok:!0,payload:g})}async function b(n,t,e){if(!t)throw new Error("Managed Room binding is unavailable.");if(Object.keys(e.params).some(u=>!l.has(u)))throw new Error("Managed Room restart contains unsupported parameters.");const r=a(e.params.roomId,"roomId"),d=a(e.params.bindingId,"bindingId"),s=a(e.params.agentIdentityId,"agentIdentityId"),o=a(e.params.previousNianSessionId,"previousNianSessionId"),i=a(e.params.nianSessionId,"nianSessionId"),I=a(e.params.assignmentIntentId,"assignmentIntentId"),g=c(e.params.agentType);await n.managerRuntime?.retireManagerAndWorkers(o),await p(n,o),n.chatQueue?.purgeSession(o),await p(n,i);const m=await t.restart({roomId:r,bindingId:d,agentIdentityId:s,previousNianSessionId:o,nianSessionId:i,assignmentIntentId:I,sourceSessionKey:y(i),agentType:g});n.client.sendRes({type:"res",id:e.id,ok:!0,payload:m})}function a(n,t){if(typeof n!="string"||!f.test(n))throw new Error(`Invalid ${t}.`);return n}function c(n){if(typeof n!="string"||!w(n)||n==="manager"||n==="pi"||n==="openclaw")throw new Error("managed_agent_runtime_unsupported");return n}function R(n){if(n===void 0)return"auto_follow";if(n==="auto_follow"||n==="mention_only"||n==="paused")return n;throw new Error("Invalid activationMode.")}function y(n){return`managed:${n}`}async function p(n,t){n.runJournal?.markSessionAborted(t),n.recoveryBarrier?.end(t);const e=n.sessions.get(t);e&&(n.sessions.delete(t),e.managedRoomContextToken&&(n.revokeManagedRoomContextToken?.(e.managedRoomContextToken),e.managedRoomContextToken=null),e.heartbeatTimer&&clearInterval(e.heartbeatTimer),e.adapter.removeAllListeners(),await e.adapter.stop())}export{p as discardPreBindingSession,T as handleManagedRoomBind,b as handleManagedRoomRestart};
@@ -1 +1 @@
1
- import{NativeSourceDeletionStore as G}from"../native-fusion/native-source-deletions.js";import{QueueRetryScheduler as H}from"./queue-retry.js";import{findDuplicateQueuedMessage as A,stripLocalQueueFields as D,takeCompatibleBatch as U,formatQueuedBatch as E,dedupeQueuedAttachments as P,classifyQueueError as B,isControllableQueueMessage as T,takeControllableMessage as V,normalizeQueuedModelId as Q,resolveManagedRoomNativeSessionId as F}from"./queue-helpers.js";import{CODEX_DESKTOP_SEND_UNAVAILABLE as X}from"@shennian/wire";import{isCodexThreadTailInterrupted as Y}from"../native-fusion/codex-files.js";import{mergeProjectedSessions as Z}from"./projection.js";import{assertFinishedAttachmentReferences as b,markAttachmentReferences as L}from"./attachment-transfer-store.js";import{appendMessage as W,getMessageDeliveryStatus as _,listSessionRecords as j,recordSession as N,tombstoneMessage as z,upsertMessage as q}from"./store.js";import{requestSessionIndexPublish as S}from"./index-publisher.js";import{purgeQueuedSessionData as ee,readQueue as c,writeQueue as w}from"./queue-storage.js";import{getNativeSourceAgentType as te}from"./handlers/chat-events.js";import{parsePersonalAttachmentReferences as $}from"./attachment-reference.js";import{getPersonalSyncIdentity as C}from"../personal-sync/epoch.js";import{getDaemonInstallationId as x}from"../personal-sync/identity.js";import{TerminalQueuedChatDispatchError as v,nowIso as O,queueEnvelope as R,queueMessageFromParams as K}from"./queue-message.js";import{purgeQueuedSessionData as we}from"./queue-storage.js";import{TerminalQueuedChatDispatchError as Re}from"./queue-message.js";class ye{opts;validateScheduledTarget;async enqueueScheduled(e){return this.handleEnqueue(e,!0)}draining=new Set;promoting=new Set;disposed=!1;retries=new H(e=>{this.wakeSession(e)});deletions=new G;inFlightMessageIds=new Map;deferredLogState=new Map;prepareManagedRoomTurn=null;prepareManagedAgentDirectTurn=null;constructor(e){this.opts=e,setTimeout(()=>this.drainIdleQueues(),0).unref?.()}stop(){this.disposed=!0,this.retries.stop()}getSnapshot(e){const s=c().sessions[e]??[],t=this.inFlightMessageIds.get(e);return{...C(x()),sessionId:e,busy:!!this.opts.getRuntime().sessions.get(e)?.currentRunId,pending:s.map(D),controllablePending:s.filter(a=>T(a)&&!t?.has(a.id)).map(D)}}setManagedRoomTurnPreparer(e){this.prepareManagedRoomTurn=e}setManagedAgentDirectTurnPreparer(e){this.prepareManagedAgentDirectTurn=e}async enqueueManagedRoomBatch(e){if(e.length===0)return;const s=this.opts.getRuntime(),t=c(),a=new Set,i=new Map;let r=!1;for(const n of e){const d=n.params;if(this.deletions.hasSession(d.sessionId)){delete t.sessions[d.sessionId],r=!0;continue}if(!d.sessionId||!d.text||!d.agentType||!d.workDir)throw new Error("sessionId, text, agentType and workDir are required");const u=K({...C(x()),...d});u.origin="room",u.managedRoom=n.context,u.trigger={kind:"room_activation",...n.context};const l=t.sessions[d.sessionId]??[],m=A(l,u),I=u.clientMessageId??u.id,f=_(d.sessionId,I);if(!m&&f.state==="not_found")l.push(u),t.sessions[d.sessionId]=l,i.set(`${u.sessionId}\0${I}`,u),a.add(d.sessionId),r=!0;else if(m&&f.state==="not_found")i.set(`${m.sessionId}\0${I}`,m),a.add(d.sessionId);else if(m&&f.state==="queued")a.add(d.sessionId);else if(m){const y=l.filter(h=>h.id!==m.id);y.length?t.sessions[d.sessionId]=y:delete t.sessions[d.sessionId],r=!0}}r&&w(t);let o;for(const n of a)try{for(const d of i.values())d.sessionId===n&&(W(n,R(d),"queued"),N({sessionId:n,agentType:d.agentType,sessionMode:d.sessionMode,managerConfig:d.managerConfig,workDir:d.workDir,agentSessionId:F(s,d),modelId:d.modelId}));this.logLifecycle("enqueue.persisted",n,{origin:"room",pendingCount:c().sessions[n]?.length??0}),this.broadcast(n),this.wakeSession(n)}catch(d){o=d,this.logLifecycle("enqueue.commit-failed",n,{failureClass:B(d instanceof Error?d.message:"","canonical_commit_failed")},"error"),this.retries.schedule(n,!0)}if(a.size&&S(s.client),o)throw o}async handleEnqueue(e,s=!1){const t=this.opts.getRuntime(),a=e.params;if(a.scheduledTask&&!s)throw new Error("schedule_source_untrusted");const i=!!(s&&a.scheduledTask?.bindingId);if(Z(a.sessionListProjection),!a.sessionId||!a.text||!a.agentType||!a.workDir)return t.client.sendRes({type:"res",id:e.id,ok:!1,error:"sessionId, text, agentType and workDir are required"}),!1;const r=a.managedAgentDirect||i?await this.prepareManagedAgentDirectTurn?.(a.sessionId):null;if((a.managedAgentDirect||i)&&!r)throw new Error("managed_agent_context_unavailable");const o=r?j().find(J=>J.sessionId===a.sessionId):null,n=r?{...a,...r,agentType:r.agentType,sessionMode:r.sessionMode,managerConfig:r.managerConfig,workDir:r.workDir,agentSessionId:r.agentSessionId,modelId:o?.modelId??void 0,systemPrompt:r.systemPrompt,managerDefaultWorkerAgentType:r.managerDefaultWorkerAgentType,managerDefaultWorkerModelId:r.managerDefaultWorkerModelId}:a,d=t.resolvePath(n.workDir),u=$(n.attachments);u?.length&&b(d,u);const l={text:n.text,attachments:u,localized:!1},m=t.sessions.get(n.sessionId);if(n.agentType==="codex"&&n.agentSessionId&&m?.currentRunId?.startsWith("dispatch:")&&Y(n.agentSessionId))return t.client.sendRes({type:"res",id:e.id,ok:!1,error:X}),!1;r?.managedRoomContextToken&&t.revokeManagedRoomContextToken?.(r.managedRoomContextToken);const I=await this.isSessionBusy(n.sessionId),f=c(),y=f.sessions[n.sessionId]??[],h=K({...n,text:l.text,attachments:l.attachments,systemPrompt:r?null:n.systemPrompt});r&&(h.managedAgentDirect=!0,h.trigger?.kind!=="manager_event"&&h.trigger?.kind!=="scheduled"&&(h.trigger={kind:"managed_direct"}));const g=h.clientMessageId??h.id,k=_(n.sessionId,g);if(k.state!=="not_found")return t.client.sendRes({type:"res",id:e.id,ok:!0,payload:{queued:k.state==="queued",queueMessageId:h.id,...k,queue:this.getSnapshot(n.sessionId)}}),!0;const M=A(y,h);M||(f.sessions[n.sessionId]=[...y,h],w(f));const p=M??h;return N({sessionId:p.sessionId,agentType:p.agentType,sessionMode:p.sessionMode,managerConfig:p.managerConfig,workDir:p.workDir,agentSessionId:p.agentSessionId,modelId:p.modelId}),W(p.sessionId,R(p),"queued"),L(t.resolvePath(p.workDir),p.attachments??[]),S(t.client),this.logLifecycle("enqueue.persisted",n.sessionId,{origin:n.origin??"app",queueMessageId:p.id,pendingCount:(c().sessions[n.sessionId]??[]).length}),this.broadcast(n.sessionId),t.client.sendRes({type:"res",id:e.id,ok:!0,payload:{queued:!0,queueMessageId:M?.id??h.id,..._(n.sessionId,M?.clientMessageId??M?.id??h.clientMessageId??h.id),queue:this.getSnapshot(n.sessionId),...l.localized?{localizedAttachments:!0}:{}}}),I?this.retries.schedule(n.sessionId):this.wakeSession(n.sessionId),!0}async handleGet(e){const s=this.opts.getRuntime(),t=e.params;if(!t.sessionId){s.client.sendRes({type:"res",id:e.id,ok:!1,error:"sessionId is required"});return}s.client.sendRes({type:"res",id:e.id,ok:!0,payload:{queue:this.getSnapshot(t.sessionId)}})}async handleEdit(e){const s=this.opts.getRuntime(),t=e.params;if(!t.sessionId||!t.queueMessageId||!t.text){s.client.sendRes({type:"res",id:e.id,ok:!1,error:"sessionId, queueMessageId and text are required"});return}const a=c(),i=a.sessions[t.sessionId]??[],r=i.findIndex(d=>d.id===t.queueMessageId);if(r<0){s.client.sendRes({type:"res",id:e.id,ok:!1,error:"Queued message not found"});return}if(!T(i[r])||this.inFlightMessageIds.get(t.sessionId)?.has(t.queueMessageId)){s.client.sendRes({type:"res",id:e.id,ok:!1,error:"managed_agent_queue_mutation_forbidden"});return}const o=$(t.attachments),n=s.resolvePath(i[r].workDir);o?.length&&b(n,o),i[r]={...i[r],text:t.text,attachments:o,updatedAt:O()},a.sessions[t.sessionId]=i,w(a),q(t.sessionId,R(i[r]),"queued"),i[r].attachments?.length&&L(n,i[r].attachments??[]),S(s.client),this.broadcast(t.sessionId),s.client.sendRes({type:"res",id:e.id,ok:!0,payload:{queue:this.getSnapshot(t.sessionId)}})}async handleDelete(e){const s=this.opts.getRuntime(),t=e.params;if(!t.sessionId||!t.queueMessageId){s.client.sendRes({type:"res",id:e.id,ok:!1,error:"sessionId and queueMessageId are required"});return}const a=c(),i=a.sessions[t.sessionId]??[],r=i.find(n=>n.id===t.queueMessageId);if(r&&(!T(r)||this.inFlightMessageIds.get(t.sessionId)?.has(t.queueMessageId))){s.client.sendRes({type:"res",id:e.id,ok:!1,error:"managed_agent_queue_mutation_forbidden"});return}const o=i.filter(n=>n.id!==t.queueMessageId);if(o.length===i.length){s.client.sendRes({type:"res",id:e.id,ok:!1,error:"Queued message not found"});return}o.length?a.sessions[t.sessionId]=o:delete a.sessions[t.sessionId],w(a),z(t.sessionId,r?.clientMessageId??r?.id??t.queueMessageId),S(s.client),this.broadcast(t.sessionId),s.client.sendRes({type:"res",id:e.id,ok:!0,payload:{queue:this.getSnapshot(t.sessionId)}})}async handlePromote(e){const s=this.opts.getRuntime(),t=e.params,a=r=>s.client.sendRes({type:"res",id:e.id,ok:!1,error:r});if(!t.sessionId||!t.queueMessageId){a("sessionId and queueMessageId are required");return}if(this.promoting.has(t.sessionId)){a("queue_promotion_in_progress");return}this.promoting.add(t.sessionId);let i=[];try{for(;this.draining.has(t.sessionId);)await new Promise(n=>setTimeout(n,5));if(i=V(c().sessions[t.sessionId]??[],t.queueMessageId),!i.length){a("Queued message not found");return}this.inFlightMessageIds.set(t.sessionId,new Set(i.map(n=>n.id)));const r=s.sessions.get(t.sessionId);let o="idle";if(r?.currentRunId)if(this.canInjectActiveBatch(r,i)){const n=i.length===1?i[0].text:E(i),d=i[0].clientMessageId??i[0].id;s.nativeFusion?.registerManagedSend({sessionId:t.sessionId,agentType:i[0].agentType,sourceAgentType:te(i[0].agentType,i[0].modelId),canonicalMessageId:d,sourceSessionKey:r.agentSessionId,text:n,managedEchoPolicy:"suppress_following"});const u=Date.now();try{await r.adapter.injectActiveInput({text:n,attachments:P(i.flatMap(l=>l.attachments??[])),clientMessageId:d})}catch(l){throw s.nativeFusion?.cancelManagedSend(d),l}for(const l of i)q(t.sessionId,{...R(l),ts:u},"executing");this.removeBatch(t.sessionId,i),o="injected"}else{if(!this.opts.interruptQueuedSession)throw new Error("queue_interrupt_unavailable");await this.opts.interruptQueuedSession(t.sessionId),await this.dispatchQueuedMessages(i),this.removeBatch(t.sessionId,i),o="restarted"}else await this.dispatchQueuedMessages(i),this.removeBatch(t.sessionId,i);S(s.client),this.broadcast(t.sessionId),s.client.sendRes({type:"res",id:e.id,ok:!0,payload:{queue:this.getSnapshot(t.sessionId),promotedMessageIds:i.map(n=>n.id),mode:o}})}catch(r){a(r instanceof Error?r.message:"queue_promotion_failed"),this.broadcast(t.sessionId)}finally{this.inFlightMessageIds.delete(t.sessionId),this.promoting.delete(t.sessionId),c().sessions[t.sessionId]?.length&&this.wakeSession(t.sessionId)}}noteTerminal(e){this.wakeSession(e)}purgeSession(e){ee(e),this.retries.cancel(e)}discardPendingManagedRoomMessages(e){if(this.deletions.hasSession(e)){const o=c().sessions[e]?.length??0;return this.purgeSession(e),o}const s=c(),t=s.sessions[e]??[],a=this.inFlightMessageIds.get(e)??new Set,i=t.filter(o=>o.managedRoom&&!a.has(o.id));if(i.length===0)return 0;const r=t.filter(o=>!i.includes(o));r.length?s.sessions[e]=r:delete s.sessions[e],w(s);for(const o of i)z(e,o.clientMessageId??o.id);return S(this.opts.getRuntime().client),this.logLifecycle("managed-room.discarded-paused",e,{discardedCount:i.length,pendingCount:r.length}),this.broadcast(e),i.length}wakeSession(e){this.drainNext(e).catch(()=>{this.logLifecycle("drain.retry-required",e,{},"error"),this.retries.schedule(e,!0)})}drainIdleQueues(){if(this.disposed)return;const e=c();for(const s of Object.keys(e.sessions))this.wakeSession(s)}async drainNext(e){if(this.disposed||this.draining.has(e)||this.promoting.has(e))return;this.draining.add(e);let s=!1,t=!1,a=[];try{if(this.deletions.hasSession(e)){this.purgeSession(e);return}if(await this.isSessionBusy(e)){const u=c().sessions[e]?.length??0,l=this.deferredLogState.get(e),m=Date.now();(!l||l.pendingCount!==u||m-l.at>=3e4)&&(this.logLifecycle("drain.deferred",e,{pendingCount:u}),this.deferredLogState.set(e,{at:m,pendingCount:u}));return}this.deferredLogState.delete(e);const i=c(),r=i.sessions[e]??[],o=r.filter(u=>{const l=u.clientMessageId??u.id,m=_(e,l).state;return m==="queued"||m==="not_found"});o.length!==r.length&&(o.length?i.sessions[e]=o:delete i.sessions[e],w(i),this.logLifecycle("drain.pruned-terminal",e,{prunedCount:r.length-o.length,pendingCount:o.length}));const n=U(o);if(n.length===0){this.broadcast(e);return}a=n,this.inFlightMessageIds.set(e,new Set(n.map(u=>u.id))),this.logLifecycle("dispatch.start",e,{batchCount:n.length,queueMessageId:n[0].id,origin:n[0].origin??"app"});for(const u of n)q(e,R(u),"queued");await this.dispatchQueuedMessages(n);const d=this.removeBatch(e,n);this.logLifecycle("dispatch.accepted",e,{batchCount:n.length,queueMessageId:n[0].id,pendingCount:d}),this.deferredLogState.delete(e),s=d>0,this.broadcast(e)}catch(i){const r=B(i instanceof Error?i.message:String(i),i instanceof v?"terminal_dispatch_failed":"transient_dispatch_failed"),o=a[0],n=!!(o&&!o.trigger&&!o.clientMessageId&&Date.now()-Date.parse(o.createdAt)>=10*6e4&&this.opts.getRuntime().managerRuntime?.isManagedWorkerSession(e));if(i instanceof v||r==="managed_claim_source_binding_conflict"||n){const u=this.removeBatch(e,a);this.logLifecycle("dispatch.terminal-failure",e,{batchCount:a.length,queueMessageId:a[0]?.id,pendingCount:u,failureClass:n?"stale_legacy_managed_worker_dispatch_failure":r},"error"),s=u>0}else t=!0,this.logLifecycle("dispatch.transient-failure",e,{failureClass:r},"error");this.broadcast(e)}finally{this.inFlightMessageIds.delete(e),this.draining.delete(e),c().sessions[e]?.length?s?queueMicrotask(()=>this.wakeSession(e)):this.retries.schedule(e,t):this.retries.cancel(e)}}canInjectActiveBatch(e,s){if(!e.adapter.supportsActiveInput||!e.adapter.injectActiveInput||!e.journalRunId)return!1;const t=this.opts.getRuntime().runJournal?.get(s[0].sessionId,e.journalRunId),a=s[0].trigger?.kind==="managed_direct"||s[0].managedAgentDirect?null:"personal";return!!(a&&s[0].systemPrompt==null&&t&&t.source===a&&t.deliveryMode==="direct"&&t.agentType===s[0].agentType&&t.workDir===e.workDir&&t.agentSessionId===e.agentSessionId&&t.modelId===(s[0].modelId??null)&&t.reasoningEffort===(s[0].reasoningEffort??null))}removeBatch(e,s){if(s.length===0)return c().sessions[e]?.length??0;const t=c(),a=new Set(s.map(r=>r.id)),i=(t.sessions[e]??[]).filter(r=>!a.has(r.id));return i.length?t.sessions[e]=i:delete t.sessions[e],w(t),i.length}logLifecycle(e,s,t,a="info"){const i=`[chat.queue] ${JSON.stringify({at:O(),event:e,sessionId:s,...t})}`;a==="error"?console.error(i):console.info(i)}async dispatchQueuedMessages(e){const s=e[0],t=s.trigger?.kind==="scheduled"?s.trigger:null;if(t&&await this.validateScheduledTarget?.({sessionId:s.sessionId,...t})!=="ready")throw new v("schedule_target_unavailable");const a=e.flatMap(g=>{if(g.trigger?.kind==="room_activation"){const{kind:k,...M}=g.trigger;return[M]}return!g.trigger&&g.managedRoom?[g.managedRoom]:[]}),i=e.find(g=>g.trigger?.kind==="manager_event")?.trigger,r=e.some(g=>g.trigger?.kind==="managed_direct"||!!g.managedAgentDirect||g.trigger?.kind==="scheduled"&&!!g.trigger.bindingId),o=e.find(g=>g.trigger?.kind==="manager_worker")?.trigger,n=a.length>0?await this.prepareManagedRoomTurn?.(a):r||i?.kind==="manager_event"?await this.prepareManagedAgentDirectTurn?.(s.sessionId):void 0;if(a.length>0&&!n)throw new Error("managed_room_turn_preparer_unavailable");if(r&&!n)throw new Error("managed_agent_context_unavailable");if(i?.kind==="manager_event"&&this.opts.getRuntime().managerRuntime?.isManagedRoomBoundManager(s.sessionId)&&!n)throw new Error("managed_agent_context_unavailable");const d=new Set((c().sessions[s.sessionId]??[]).map(g=>g.id));if(this.disposed||this.deletions.hasSession(s.sessionId)||e.some(g=>!d.has(g.id)))throw n?.managedRoomContextToken&&this.opts.getRuntime().revokeManagedRoomContextToken?.(n.managedRoomContextToken),this.disposed?new Error("queue_stopped"):new v("queued_dispatch_cancelled");i?.kind==="manager_event"&&n?.managedRoomContextToken&&this.opts.getRuntime().revokeManagedRoomContextToken?.(n.managedRoomContextToken);const u=n??null,l=u?.sessionMode==="manager"?u.managerConfig:null,m=e.length===1?s.text:E(e),I=P(e.flatMap(g=>g.attachments??[])),f=r||i?.kind==="manager_event"&&!!n,y=f?n.agentSessionId:a.length>0?F(this.opts.getRuntime(),s):s.agentSessionId??null,h=n?j().find(g=>g.sessionId===s.sessionId):null;await this.opts.dispatchQueuedReq({type:"req",id:`queue-send-${s.id}-${Date.now()}`,method:"chat.send",params:{...C(x()),sessionId:s.sessionId,text:m,agentType:f?n.agentType:s.agentType,sessionMode:u?.sessionMode??s.sessionMode,managerConfig:u?l:s.managerConfig,workDir:f?n.workDir:s.workDir,agentSessionId:y,modelId:Q(h?.modelId)??Q(s.modelId),systemPrompt:i?.kind==="manager_event"?null:n?.systemPrompt??s.systemPrompt??null,managerDefaultWorkerAgentType:u?u.managerDefaultWorkerAgentType:s.managerDefaultWorkerAgentType,managerDefaultWorkerModelId:u?u.managerDefaultWorkerModelId:s.managerDefaultWorkerModelId,reasoningEffort:s.reasoningEffort??void 0,clientMessageId:s.clientMessageId??s.id,attachments:I,externalChannel:i?.kind==="manager_event"?void 0:s.externalChannel,replyTarget:i?.kind==="manager_event"?void 0:s.replyTarget,roomContextRegistryKey:i?.kind==="manager_event"?void 0:n?.roomContextRegistryKey,activationCauseMessageId:i?.kind==="manager_event"?void 0:a.at(-1)?.causeMessageId,rootCauseMessageId:i?.kind==="manager_event"?void 0:a.at(-1)?.rootCauseMessageId,activationSequence:i?.kind==="manager_event"?void 0:a.at(-1)?.sequence,activationHopCount:i?.kind==="manager_event"?void 0:a.at(-1)?.hopCount,managedRecoverySource:i?.kind==="manager_event"?"manager_event":r?"managed_direct":a.length>0?"managed_room":void 0,managerEventId:i?.kind==="manager_event"?i.eventId:void 0,managedWorkerParentSessionId:o?.kind==="manager_worker"?o.parentSessionId:void 0,managerWorkerNativeFull:o?.kind==="manager_worker"&&this.opts.getRuntime().managerRuntime?.isNativeSkillManager(o.parentSessionId,s.workDir)||void 0,managedRoomContextToken:i?.kind==="manager_event"?void 0:n?.managedRoomContextToken,scheduledDelivery:!!t,suppressExternalOwnerEcho:a.length>0||!!t||void 0,queuedBatchItems:e.map(g=>{const k=D(g);return i?.kind==="manager_event"?{...k,systemPrompt:null,externalChannel:null,replyTarget:null}:k}),waitForDispatch:!0}},n??void 0)}async isSessionBusy(e){const s=this.opts.getRuntime().recoveryBarrier;if(s?.isDispatchFrozen()||s?.isRecovering(e))return!0;const t=this.opts.getRuntime().sessions.get(e);if(!t)return!1;if(t.currentRunId){if(!(t.currentRunId.startsWith("dispatch:")&&!!this.opts.getRuntime().runJournal&&!t.journalRunId)||(t.adapter.getStatus?await t.adapter.getStatus().catch(()=>null):null)?.active)return!0;this.logLifecycle("drain.recovered-stale-dispatch",e,{staleRunId:t.currentRunId}),t.currentRunId=null,t.currentRunPhase=null}if(!t.adapter.getStatus)return!1;const a=await t.adapter.getStatus().catch(()=>null);return a?.active?(a.runId&&(t.currentRunId=a.runId,t.currentRunPhase=a.runPhase??t.currentRunPhase),!0):!1}broadcast(e){this.opts.getRuntime().client.sendEvent({type:"event",event:"session.queue.update",payload:{queue:this.getSnapshot(e)}})}}export{ye as ChatQueueManager,Re as TerminalQueuedChatDispatchError,we as purgeQueuedSessionData};
1
+ import{NativeSourceDeletionStore as G}from"../native-fusion/native-source-deletions.js";import{QueueRetryScheduler as H}from"./queue-retry.js";import{findDuplicateQueuedMessage as E,stripLocalQueueFields as D,takeCompatibleBatch as U,formatQueuedBatch as P,dedupeQueuedAttachments as B,classifyQueueError as Q,isControllableQueueMessage as T,takeControllableMessage as V,normalizeQueuedModelId as F,resolveManagedRoomNativeSessionId as q}from"./queue-helpers.js";import{CODEX_DESKTOP_SEND_UNAVAILABLE as X}from"@shennian/wire";import{isCodexThreadTailInterrupted as Y}from"../native-fusion/codex-files.js";import{mergeProjectedSessions as Z}from"./projection.js";import{assertFinishedAttachmentReferences as b,markAttachmentReferences as L}from"./attachment-transfer-store.js";import{appendMessage as W,getMessageDeliveryStatus as _,listSessionRecords as j,recordSession as N,tombstoneMessage as z,upsertMessage as C}from"./store.js";import{requestSessionIndexPublish as w}from"./index-publisher.js";import{purgeQueuedSessionData as ee,readQueue as c,writeQueue as S}from"./queue-storage.js";import{getNativeSourceAgentType as te}from"./handlers/chat-events.js";import{parsePersonalAttachmentReferences as $}from"./attachment-reference.js";import{getPersonalSyncIdentity as x}from"../personal-sync/epoch.js";import{getDaemonInstallationId as A}from"../personal-sync/identity.js";import{TerminalQueuedChatDispatchError as v,nowIso as O,queueEnvelope as R,queueMessageFromParams as K}from"./queue-message.js";import{purgeQueuedSessionData as Se}from"./queue-storage.js";import{TerminalQueuedChatDispatchError as Re}from"./queue-message.js";class ye{opts;validateScheduledTarget;async enqueueScheduled(e){return this.handleEnqueue(e,!0)}draining=new Set;promoting=new Set;disposed=!1;retries=new H(e=>{this.wakeSession(e)});deletions=new G;inFlightMessageIds=new Map;deferredLogState=new Map;prepareManagedRoomTurn=null;prepareManagedAgentDirectTurn=null;constructor(e){this.opts=e,setTimeout(()=>this.drainIdleQueues(),0).unref?.()}stop(){this.disposed=!0,this.retries.stop()}getSnapshot(e){const s=c().sessions[e]??[],t=this.inFlightMessageIds.get(e);return{...x(A()),sessionId:e,busy:!!this.opts.getRuntime().sessions.get(e)?.currentRunId,pending:s.map(D),controllablePending:s.filter(r=>T(r)&&!t?.has(r.id)).map(D)}}setManagedRoomTurnPreparer(e){this.prepareManagedRoomTurn=e}setManagedAgentDirectTurnPreparer(e){this.prepareManagedAgentDirectTurn=e}async enqueueManagedRoomBatch(e){if(e.length===0)return;const s=this.opts.getRuntime(),t=c(),r=new Set,i=new Map;let a=!1;for(const n of e){const d=n.params;if(this.deletions.hasSession(d.sessionId)){delete t.sessions[d.sessionId],a=!0;continue}if(!d.sessionId||!d.text||!d.agentType||!d.workDir)throw new Error("sessionId, text, agentType and workDir are required");const u=K({...x(A()),...d});u.origin="room",u.managedRoom=n.context,u.trigger={kind:"room_activation",...n.context};const l=t.sessions[d.sessionId]??[],m=E(l,u),I=u.clientMessageId??u.id,f=_(d.sessionId,I);if(!m&&f.state==="not_found")l.push(u),t.sessions[d.sessionId]=l,i.set(`${u.sessionId}\0${I}`,u),r.add(d.sessionId),a=!0;else if(m&&f.state==="not_found")i.set(`${m.sessionId}\0${I}`,m),r.add(d.sessionId);else if(m&&f.state==="queued")r.add(d.sessionId);else if(m){const y=l.filter(h=>h.id!==m.id);y.length?t.sessions[d.sessionId]=y:delete t.sessions[d.sessionId],a=!0}}a&&S(t);let o;for(const n of r)try{for(const d of i.values())d.sessionId===n&&(W(n,R(d),"queued"),N({sessionId:n,agentType:d.agentType,sessionMode:d.sessionMode,managerConfig:d.managerConfig,workDir:d.workDir,agentSessionId:q(s,d),modelId:d.modelId}));this.logLifecycle("enqueue.persisted",n,{origin:"room",pendingCount:c().sessions[n]?.length??0}),this.broadcast(n),this.wakeSession(n)}catch(d){o=d,this.logLifecycle("enqueue.commit-failed",n,{failureClass:Q(d instanceof Error?d.message:"","canonical_commit_failed")},"error"),this.retries.schedule(n,!0)}if(r.size&&w(s.client),o)throw o}async handleEnqueue(e,s=!1){const t=this.opts.getRuntime(),r=e.params;if(r.scheduledTask&&!s)throw new Error("schedule_source_untrusted");const i=!!(s&&r.scheduledTask?.bindingId);if(Z(r.sessionListProjection),!r.sessionId||!r.text||!r.agentType||!r.workDir)return t.client.sendRes({type:"res",id:e.id,ok:!1,error:"sessionId, text, agentType and workDir are required"}),!1;const a=r.managedAgentDirect||i?await this.prepareManagedAgentDirectTurn?.(r.sessionId):null;if((r.managedAgentDirect||i)&&!a)throw new Error("managed_agent_context_unavailable");const o=a?j().find(J=>J.sessionId===r.sessionId):null,n=a?{...r,...a,agentType:a.agentType,sessionMode:a.sessionMode,managerConfig:a.managerConfig,workDir:a.workDir,agentSessionId:a.agentSessionId?.startsWith("managed:")?o?.agentSessionId&&!o.agentSessionId.startsWith("managed:")?o.agentSessionId:null:a.agentSessionId,modelId:o?.modelId??void 0,systemPrompt:a.systemPrompt,managerDefaultWorkerAgentType:a.managerDefaultWorkerAgentType,managerDefaultWorkerModelId:a.managerDefaultWorkerModelId}:r,d=t.resolvePath(n.workDir),u=$(n.attachments);u?.length&&b(d,u);const l={text:n.text,attachments:u,localized:!1},m=t.sessions.get(n.sessionId);if(n.agentType==="codex"&&n.agentSessionId&&m?.currentRunId?.startsWith("dispatch:")&&Y(n.agentSessionId))return t.client.sendRes({type:"res",id:e.id,ok:!1,error:X}),!1;a?.managedRoomContextToken&&t.revokeManagedRoomContextToken?.(a.managedRoomContextToken);const I=await this.isSessionBusy(n.sessionId),f=c(),y=f.sessions[n.sessionId]??[],h=K({...n,text:l.text,attachments:l.attachments,systemPrompt:a?null:n.systemPrompt});a&&(h.managedAgentDirect=!0,h.trigger?.kind!=="manager_event"&&h.trigger?.kind!=="scheduled"&&(h.trigger={kind:"managed_direct"}));const g=h.clientMessageId??h.id,k=_(n.sessionId,g);if(k.state!=="not_found")return t.client.sendRes({type:"res",id:e.id,ok:!0,payload:{queued:k.state==="queued",queueMessageId:h.id,...k,queue:this.getSnapshot(n.sessionId)}}),!0;const M=E(y,h);M||(f.sessions[n.sessionId]=[...y,h],S(f));const p=M??h;return N({sessionId:p.sessionId,agentType:p.agentType,sessionMode:p.sessionMode,managerConfig:p.managerConfig,workDir:p.workDir,agentSessionId:p.agentSessionId,modelId:p.modelId}),W(p.sessionId,R(p),"queued"),L(t.resolvePath(p.workDir),p.attachments??[]),w(t.client),this.logLifecycle("enqueue.persisted",n.sessionId,{origin:n.origin??"app",queueMessageId:p.id,pendingCount:(c().sessions[n.sessionId]??[]).length}),this.broadcast(n.sessionId),t.client.sendRes({type:"res",id:e.id,ok:!0,payload:{queued:!0,queueMessageId:M?.id??h.id,..._(n.sessionId,M?.clientMessageId??M?.id??h.clientMessageId??h.id),queue:this.getSnapshot(n.sessionId),...l.localized?{localizedAttachments:!0}:{}}}),I?this.retries.schedule(n.sessionId):this.wakeSession(n.sessionId),!0}async handleGet(e){const s=this.opts.getRuntime(),t=e.params;if(!t.sessionId){s.client.sendRes({type:"res",id:e.id,ok:!1,error:"sessionId is required"});return}s.client.sendRes({type:"res",id:e.id,ok:!0,payload:{queue:this.getSnapshot(t.sessionId)}})}async handleEdit(e){const s=this.opts.getRuntime(),t=e.params;if(!t.sessionId||!t.queueMessageId||!t.text){s.client.sendRes({type:"res",id:e.id,ok:!1,error:"sessionId, queueMessageId and text are required"});return}const r=c(),i=r.sessions[t.sessionId]??[],a=i.findIndex(d=>d.id===t.queueMessageId);if(a<0){s.client.sendRes({type:"res",id:e.id,ok:!1,error:"Queued message not found"});return}if(!T(i[a])||this.inFlightMessageIds.get(t.sessionId)?.has(t.queueMessageId)){s.client.sendRes({type:"res",id:e.id,ok:!1,error:"managed_agent_queue_mutation_forbidden"});return}const o=$(t.attachments),n=s.resolvePath(i[a].workDir);o?.length&&b(n,o),i[a]={...i[a],text:t.text,attachments:o,updatedAt:O()},r.sessions[t.sessionId]=i,S(r),C(t.sessionId,R(i[a]),"queued"),i[a].attachments?.length&&L(n,i[a].attachments??[]),w(s.client),this.broadcast(t.sessionId),s.client.sendRes({type:"res",id:e.id,ok:!0,payload:{queue:this.getSnapshot(t.sessionId)}})}async handleDelete(e){const s=this.opts.getRuntime(),t=e.params;if(!t.sessionId||!t.queueMessageId){s.client.sendRes({type:"res",id:e.id,ok:!1,error:"sessionId and queueMessageId are required"});return}const r=c(),i=r.sessions[t.sessionId]??[],a=i.find(n=>n.id===t.queueMessageId);if(a&&(!T(a)||this.inFlightMessageIds.get(t.sessionId)?.has(t.queueMessageId))){s.client.sendRes({type:"res",id:e.id,ok:!1,error:"managed_agent_queue_mutation_forbidden"});return}const o=i.filter(n=>n.id!==t.queueMessageId);if(o.length===i.length){s.client.sendRes({type:"res",id:e.id,ok:!1,error:"Queued message not found"});return}o.length?r.sessions[t.sessionId]=o:delete r.sessions[t.sessionId],S(r),z(t.sessionId,a?.clientMessageId??a?.id??t.queueMessageId),w(s.client),this.broadcast(t.sessionId),s.client.sendRes({type:"res",id:e.id,ok:!0,payload:{queue:this.getSnapshot(t.sessionId)}})}async handlePromote(e){const s=this.opts.getRuntime(),t=e.params,r=a=>s.client.sendRes({type:"res",id:e.id,ok:!1,error:a});if(!t.sessionId||!t.queueMessageId){r("sessionId and queueMessageId are required");return}if(this.promoting.has(t.sessionId)){r("queue_promotion_in_progress");return}this.promoting.add(t.sessionId);let i=[];try{for(;this.draining.has(t.sessionId);)await new Promise(n=>setTimeout(n,5));if(i=V(c().sessions[t.sessionId]??[],t.queueMessageId),!i.length){r("Queued message not found");return}this.inFlightMessageIds.set(t.sessionId,new Set(i.map(n=>n.id)));const a=s.sessions.get(t.sessionId);let o="idle";if(a?.currentRunId)if(this.canInjectActiveBatch(a,i)){const n=i.length===1?i[0].text:P(i),d=i[0].clientMessageId??i[0].id;s.nativeFusion?.registerManagedSend({sessionId:t.sessionId,agentType:i[0].agentType,sourceAgentType:te(i[0].agentType,i[0].modelId),canonicalMessageId:d,sourceSessionKey:a.agentSessionId,text:n,managedEchoPolicy:"suppress_following"});const u=Date.now();try{await a.adapter.injectActiveInput({text:n,attachments:B(i.flatMap(l=>l.attachments??[])),clientMessageId:d})}catch(l){throw s.nativeFusion?.cancelManagedSend(d),l}for(const l of i)C(t.sessionId,{...R(l),ts:u},"executing");this.removeBatch(t.sessionId,i),o="injected"}else{if(!this.opts.interruptQueuedSession)throw new Error("queue_interrupt_unavailable");await this.opts.interruptQueuedSession(t.sessionId),await this.dispatchQueuedMessages(i),this.removeBatch(t.sessionId,i),o="restarted"}else await this.dispatchQueuedMessages(i),this.removeBatch(t.sessionId,i);w(s.client),this.broadcast(t.sessionId),s.client.sendRes({type:"res",id:e.id,ok:!0,payload:{queue:this.getSnapshot(t.sessionId),promotedMessageIds:i.map(n=>n.id),mode:o}})}catch(a){r(a instanceof Error?a.message:"queue_promotion_failed"),this.broadcast(t.sessionId)}finally{this.inFlightMessageIds.delete(t.sessionId),this.promoting.delete(t.sessionId),c().sessions[t.sessionId]?.length&&this.wakeSession(t.sessionId)}}noteTerminal(e){this.wakeSession(e)}purgeSession(e){ee(e),this.retries.cancel(e)}discardPendingManagedRoomMessages(e){if(this.deletions.hasSession(e)){const o=c().sessions[e]?.length??0;return this.purgeSession(e),o}const s=c(),t=s.sessions[e]??[],r=this.inFlightMessageIds.get(e)??new Set,i=t.filter(o=>o.managedRoom&&!r.has(o.id));if(i.length===0)return 0;const a=t.filter(o=>!i.includes(o));a.length?s.sessions[e]=a:delete s.sessions[e],S(s);for(const o of i)z(e,o.clientMessageId??o.id);return w(this.opts.getRuntime().client),this.logLifecycle("managed-room.discarded-paused",e,{discardedCount:i.length,pendingCount:a.length}),this.broadcast(e),i.length}wakeSession(e){this.drainNext(e).catch(()=>{this.logLifecycle("drain.retry-required",e,{},"error"),this.retries.schedule(e,!0)})}drainIdleQueues(){if(this.disposed)return;const e=c();for(const s of Object.keys(e.sessions))this.wakeSession(s)}async drainNext(e){if(this.disposed||this.draining.has(e)||this.promoting.has(e))return;this.draining.add(e);let s=!1,t=!1,r=[];try{if(this.deletions.hasSession(e)){this.purgeSession(e);return}if(await this.isSessionBusy(e)){const u=c().sessions[e]?.length??0,l=this.deferredLogState.get(e),m=Date.now();(!l||l.pendingCount!==u||m-l.at>=3e4)&&(this.logLifecycle("drain.deferred",e,{pendingCount:u}),this.deferredLogState.set(e,{at:m,pendingCount:u}));return}this.deferredLogState.delete(e);const i=c(),a=i.sessions[e]??[],o=a.filter(u=>{const l=u.clientMessageId??u.id,m=_(e,l).state;return m==="queued"||m==="not_found"});o.length!==a.length&&(o.length?i.sessions[e]=o:delete i.sessions[e],S(i),this.logLifecycle("drain.pruned-terminal",e,{prunedCount:a.length-o.length,pendingCount:o.length}));const n=U(o);if(n.length===0){this.broadcast(e);return}r=n,this.inFlightMessageIds.set(e,new Set(n.map(u=>u.id))),this.logLifecycle("dispatch.start",e,{batchCount:n.length,queueMessageId:n[0].id,origin:n[0].origin??"app"});for(const u of n)C(e,R(u),"queued");await this.dispatchQueuedMessages(n);const d=this.removeBatch(e,n);this.logLifecycle("dispatch.accepted",e,{batchCount:n.length,queueMessageId:n[0].id,pendingCount:d}),this.deferredLogState.delete(e),s=d>0,this.broadcast(e)}catch(i){const a=Q(i instanceof Error?i.message:String(i),i instanceof v?"terminal_dispatch_failed":"transient_dispatch_failed"),o=r[0],n=!!(o&&!o.trigger&&!o.clientMessageId&&Date.now()-Date.parse(o.createdAt)>=10*6e4&&this.opts.getRuntime().managerRuntime?.isManagedWorkerSession(e));if(i instanceof v||a==="managed_claim_source_binding_conflict"||n){const u=this.removeBatch(e,r);this.logLifecycle("dispatch.terminal-failure",e,{batchCount:r.length,queueMessageId:r[0]?.id,pendingCount:u,failureClass:n?"stale_legacy_managed_worker_dispatch_failure":a},"error"),s=u>0}else t=!0,this.logLifecycle("dispatch.transient-failure",e,{failureClass:a},"error");this.broadcast(e)}finally{this.inFlightMessageIds.delete(e),this.draining.delete(e),c().sessions[e]?.length?s?queueMicrotask(()=>this.wakeSession(e)):this.retries.schedule(e,t):this.retries.cancel(e)}}canInjectActiveBatch(e,s){if(!e.adapter.supportsActiveInput||!e.adapter.injectActiveInput||!e.journalRunId)return!1;const t=this.opts.getRuntime().runJournal?.get(s[0].sessionId,e.journalRunId),r=s[0].trigger?.kind==="managed_direct"||s[0].managedAgentDirect?null:"personal";return!!(r&&s[0].systemPrompt==null&&t&&t.source===r&&t.deliveryMode==="direct"&&t.agentType===s[0].agentType&&t.workDir===e.workDir&&t.agentSessionId===e.agentSessionId&&t.modelId===(s[0].modelId??null)&&t.reasoningEffort===(s[0].reasoningEffort??null))}removeBatch(e,s){if(s.length===0)return c().sessions[e]?.length??0;const t=c(),r=new Set(s.map(a=>a.id)),i=(t.sessions[e]??[]).filter(a=>!r.has(a.id));return i.length?t.sessions[e]=i:delete t.sessions[e],S(t),i.length}logLifecycle(e,s,t,r="info"){const i=`[chat.queue] ${JSON.stringify({at:O(),event:e,sessionId:s,...t})}`;r==="error"?console.error(i):console.info(i)}async dispatchQueuedMessages(e){const s=e[0],t=s.trigger?.kind==="scheduled"?s.trigger:null;if(t&&await this.validateScheduledTarget?.({sessionId:s.sessionId,...t})!=="ready")throw new v("schedule_target_unavailable");const r=e.flatMap(g=>{if(g.trigger?.kind==="room_activation"){const{kind:k,...M}=g.trigger;return[M]}return!g.trigger&&g.managedRoom?[g.managedRoom]:[]}),i=e.find(g=>g.trigger?.kind==="manager_event")?.trigger,a=e.some(g=>g.trigger?.kind==="managed_direct"||!!g.managedAgentDirect||g.trigger?.kind==="scheduled"&&!!g.trigger.bindingId),o=e.find(g=>g.trigger?.kind==="manager_worker")?.trigger,n=r.length>0?await this.prepareManagedRoomTurn?.(r):a||i?.kind==="manager_event"?await this.prepareManagedAgentDirectTurn?.(s.sessionId):void 0;if(r.length>0&&!n)throw new Error("managed_room_turn_preparer_unavailable");if(a&&!n)throw new Error("managed_agent_context_unavailable");if(i?.kind==="manager_event"&&this.opts.getRuntime().managerRuntime?.isManagedRoomBoundManager(s.sessionId)&&!n)throw new Error("managed_agent_context_unavailable");const d=new Set((c().sessions[s.sessionId]??[]).map(g=>g.id));if(this.disposed||this.deletions.hasSession(s.sessionId)||e.some(g=>!d.has(g.id)))throw n?.managedRoomContextToken&&this.opts.getRuntime().revokeManagedRoomContextToken?.(n.managedRoomContextToken),this.disposed?new Error("queue_stopped"):new v("queued_dispatch_cancelled");i?.kind==="manager_event"&&n?.managedRoomContextToken&&this.opts.getRuntime().revokeManagedRoomContextToken?.(n.managedRoomContextToken);const u=n??null,l=u?.sessionMode==="manager"?u.managerConfig:null,m=e.length===1?s.text:P(e),I=B(e.flatMap(g=>g.attachments??[])),f=a||i?.kind==="manager_event"&&!!n,y=f?n.agentSessionId?.startsWith("managed:")?q(this.opts.getRuntime(),s):n.agentSessionId:r.length>0?q(this.opts.getRuntime(),s):s.agentSessionId??null,h=n?j().find(g=>g.sessionId===s.sessionId):null;await this.opts.dispatchQueuedReq({type:"req",id:`queue-send-${s.id}-${Date.now()}`,method:"chat.send",params:{...x(A()),sessionId:s.sessionId,text:m,agentType:f?n.agentType:s.agentType,sessionMode:u?.sessionMode??s.sessionMode,managerConfig:u?l:s.managerConfig,workDir:f?n.workDir:s.workDir,agentSessionId:y,modelId:F(h?.modelId)??F(s.modelId),systemPrompt:i?.kind==="manager_event"?null:n?.systemPrompt??s.systemPrompt??null,managerDefaultWorkerAgentType:u?u.managerDefaultWorkerAgentType:s.managerDefaultWorkerAgentType,managerDefaultWorkerModelId:u?u.managerDefaultWorkerModelId:s.managerDefaultWorkerModelId,reasoningEffort:s.reasoningEffort??void 0,clientMessageId:s.clientMessageId??s.id,attachments:I,externalChannel:i?.kind==="manager_event"?void 0:s.externalChannel,replyTarget:i?.kind==="manager_event"?void 0:s.replyTarget,roomContextRegistryKey:i?.kind==="manager_event"?void 0:n?.roomContextRegistryKey,activationCauseMessageId:i?.kind==="manager_event"?void 0:r.at(-1)?.causeMessageId,rootCauseMessageId:i?.kind==="manager_event"?void 0:r.at(-1)?.rootCauseMessageId,activationSequence:i?.kind==="manager_event"?void 0:r.at(-1)?.sequence,activationHopCount:i?.kind==="manager_event"?void 0:r.at(-1)?.hopCount,managedRecoverySource:i?.kind==="manager_event"?"manager_event":a?"managed_direct":r.length>0?"managed_room":void 0,managerEventId:i?.kind==="manager_event"?i.eventId:void 0,managedWorkerParentSessionId:o?.kind==="manager_worker"?o.parentSessionId:void 0,managerWorkerNativeFull:o?.kind==="manager_worker"&&this.opts.getRuntime().managerRuntime?.isNativeSkillManager(o.parentSessionId,s.workDir)||void 0,managedRoomContextToken:i?.kind==="manager_event"?void 0:n?.managedRoomContextToken,scheduledDelivery:!!t,suppressExternalOwnerEcho:r.length>0||!!t||void 0,queuedBatchItems:e.map(g=>{const k=D(g);return i?.kind==="manager_event"?{...k,systemPrompt:null,externalChannel:null,replyTarget:null}:k}),waitForDispatch:!0}},n??void 0)}async isSessionBusy(e){const s=this.opts.getRuntime().recoveryBarrier;if(s?.isDispatchFrozen()||s?.isRecovering(e))return!0;const t=this.opts.getRuntime().sessions.get(e);if(!t)return!1;if(t.currentRunId){if(!(t.currentRunId.startsWith("dispatch:")&&!!this.opts.getRuntime().runJournal&&!t.journalRunId)||(t.adapter.getStatus?await t.adapter.getStatus().catch(()=>null):null)?.active)return!0;this.logLifecycle("drain.recovered-stale-dispatch",e,{staleRunId:t.currentRunId}),t.currentRunId=null,t.currentRunPhase=null}if(!t.adapter.getStatus)return!1;const r=await t.adapter.getStatus().catch(()=>null);return r?.active?(r.runId&&(t.currentRunId=r.runId,t.currentRunPhase=r.runPhase??t.currentRunPhase),!0):!1}broadcast(e){this.opts.getRuntime().client.sendEvent({type:"event",event:"session.queue.update",payload:{queue:this.getSnapshot(e)}})}}export{ye as ChatQueueManager,Re as TerminalQueuedChatDispatchError,Se as purgeQueuedSessionData};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shennian",
3
- "version": "0.4.62",
3
+ "version": "0.4.64",
4
4
  "description": "Shennian — AI Agent Control Plane CLI",
5
5
  "type": "module",
6
6
  "bin": {