shennian 0.3.85 → 0.3.86

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.
Files changed (99) hide show
  1. package/dist/bin/shennian.js +1 -1
  2. package/dist/publish-build-manifest.json +28 -28
  3. package/dist/src/agents/adapter.js +19 -1
  4. package/dist/src/agents/claude-managed-permissions.js +248 -1
  5. package/dist/src/agents/claude.js +393 -8
  6. package/dist/src/agents/codex-managed-permissions.js +294 -1
  7. package/dist/src/agents/codex-managed-room-tools.js +249 -1
  8. package/dist/src/agents/codex-utils.js +200 -7
  9. package/dist/src/agents/codex.js +1352 -17
  10. package/dist/src/agents/config-status.js +226 -1
  11. package/dist/src/agents/cursor-managed-permissions.js +299 -2
  12. package/dist/src/agents/cursor.js +314 -1
  13. package/dist/src/agents/custom.js +418 -4
  14. package/dist/src/agents/external-channel-instructions.js +94 -11
  15. package/dist/src/agents/gemini.js +180 -1
  16. package/dist/src/agents/manager.d.ts +2 -0
  17. package/dist/src/agents/manager.js +250 -18
  18. package/dist/src/agents/model-registry/cache.js +37 -1
  19. package/dist/src/agents/model-registry/discovery.js +188 -2
  20. package/dist/src/agents/model-registry/parsers.js +521 -4
  21. package/dist/src/agents/model-registry/runner.js +30 -1
  22. package/dist/src/agents/model-registry/service.js +115 -1
  23. package/dist/src/agents/model-registry/types.js +8 -1
  24. package/dist/src/agents/model-registry.js +19 -1
  25. package/dist/src/agents/native-capabilities.js +237 -1
  26. package/dist/src/agents/openclaw.js +293 -2
  27. package/dist/src/agents/opencode-managed-permissions.js +187 -2
  28. package/dist/src/agents/opencode.js +298 -1
  29. package/dist/src/agents/platform-instructions.js +46 -17
  30. package/dist/src/agents/runtime-compatibility.js +44 -1
  31. package/dist/src/agents/workbuddy-managed-permissions.js +329 -2
  32. package/dist/src/agents/workbuddy.js +444 -3
  33. package/dist/src/commands/room.js +2 -2
  34. package/dist/src/fs/boundary.js +185 -1
  35. package/dist/src/fs/handler.js +130 -1
  36. package/dist/src/fs/security.js +32 -1
  37. package/dist/src/fs/text-decoder.js +110 -1
  38. package/dist/src/index.js +587 -3
  39. package/dist/src/manager/prompt.js +34 -29
  40. package/dist/src/manager/registry.js +317 -1
  41. package/dist/src/manager/retired-external-channel-compat.js +40 -1
  42. package/dist/src/manager/runtime.d.ts +4 -0
  43. package/dist/src/manager/runtime.js +1514 -23
  44. package/dist/src/native-fusion/codex-parser.js +660 -4
  45. package/dist/src/native-fusion/codex-user-input.js +102 -5
  46. package/dist/src/native-fusion/config.js +5 -1
  47. package/dist/src/native-fusion/file-watcher.js +191 -1
  48. package/dist/src/native-fusion/local-repository.js +569 -2
  49. package/dist/src/native-fusion/opencode-parser.js +130 -3
  50. package/dist/src/native-fusion/parsers.js +140 -3
  51. package/dist/src/native-fusion/service.js +739 -2
  52. package/dist/src/native-fusion/session-header.js +101 -2
  53. package/dist/src/native-fusion/state.js +104 -2
  54. package/dist/src/native-fusion/types.js +1 -1
  55. package/dist/src/personal-sync/machine-authority.js +34 -1
  56. package/dist/src/room/activation-audit.js +45 -2
  57. package/dist/src/room/activation-policy.js +30 -1
  58. package/dist/src/room/activation-service.d.ts +1 -0
  59. package/dist/src/room/activation-service.js +408 -8
  60. package/dist/src/room/invite-bootstrap.js +156 -1
  61. package/dist/src/room/managed-context.d.ts +1 -1
  62. package/dist/src/room/managed-context.js +1 -1
  63. package/dist/src/room/managed-prompt-composer.d.ts +1 -1
  64. package/dist/src/room/managed-prompt-composer.js +6 -5
  65. package/dist/src/room/managed-room-binding.js +167 -1
  66. package/dist/src/room/managed-room-ipc.d.ts +2 -2
  67. package/dist/src/room/managed-room-ipc.js +2 -2
  68. package/dist/src/room/mirror-sync-service.js +183 -1
  69. package/dist/src/room/room-action-client.d.ts +11 -0
  70. package/dist/src/room/room-action-client.js +1 -1
  71. package/dist/src/room/room-binding-client.d.ts +1 -0
  72. package/dist/src/room/room-binding-client.js +151 -1
  73. package/dist/src/security/managed-agent-policy.d.ts +3 -2
  74. package/dist/src/security/managed-agent-policy.js +1 -1
  75. package/dist/src/session/archive-zip.js +220 -1
  76. package/dist/src/session/deletion-sync-state.js +136 -2
  77. package/dist/src/session/handlers/agent-capabilities.js +14 -1
  78. package/dist/src/session/handlers/agent-config.js +150 -1
  79. package/dist/src/session/handlers/agents.js +55 -1
  80. package/dist/src/session/handlers/chat.d.ts +1 -1
  81. package/dist/src/session/handlers/chat.js +1030 -2
  82. package/dist/src/session/handlers/control.js +61 -1
  83. package/dist/src/session/handlers/fs.js +594 -1
  84. package/dist/src/session/handlers/message-status.js +15 -1
  85. package/dist/src/session/handlers/remote-access.js +43 -1
  86. package/dist/src/session/handlers/room-managed.js +101 -1
  87. package/dist/src/session/handlers/session-refresh.js +35 -1
  88. package/dist/src/session/handlers/title.js +79 -1
  89. package/dist/src/session/handlers/tool-detail.js +149 -1
  90. package/dist/src/session/handlers/voice-window.js +62 -1
  91. package/dist/src/session/history-subscriptions.js +470 -1
  92. package/dist/src/session/manager.js +658 -1
  93. package/dist/src/session/native-cleanup-outbox.js +287 -2
  94. package/dist/src/session/native-session-lifecycle.js +327 -6
  95. package/dist/src/session/queue.d.ts +5 -0
  96. package/dist/src/session/queue.js +8 -8
  97. package/dist/src/upgrade/scheduler.js +126 -1
  98. package/node_modules/@shennian/wire/dist/agent-workspace.js +1 -1
  99. package/package.json +1 -1
@@ -1,32 +1,34 @@
1
- const s=`\u4F60\u662F\u9879\u76EE\u7ECF\u7406\uFF0C\u662F\u5F53\u524D\u9879\u76EE\u7684\u7BA1\u7406\u8005\u3002
1
+ // @arch docs/features/manager-agent.md
2
+ // @test src/__tests__/manager-inbox.test.ts
3
+ export const MANAGER_SYSTEM_PROMPT = `你是项目经理,是当前项目的管理者。
2
4
 
3
- \u4F60\u7684\u804C\u8D23\uFF1A
4
- - \u7406\u89E3\u7528\u6237\u76EE\u6807\u3002
5
- - \u62C6\u89E3\u4EFB\u52A1\u3002
6
- - \u521B\u5EFA\u3001\u6307\u6D3E\u3001\u89C2\u5BDF\u548C\u505C\u6B62\u540C\u4E00\u9879\u76EE\u76EE\u5F55\u4E0B\u7684 worker Agent session\u3002
7
- - \u6C47\u603B worker \u7ED3\u679C\u3002
8
- - \u5224\u65AD\u662F\u5426\u9700\u8981\u7EE7\u7EED\u7B49\u5F85\u3001\u8C03\u6574\u5B89\u6392\u3001\u8BE2\u95EE\u7528\u6237\u6216\u9A8C\u6536\u3002
9
- - \u5728\u9879\u76EE .shennian/ \u76EE\u5F55\u4E0B\u7EF4\u62A4\u5FC5\u8981\u7684\u8BA1\u5212\u3001\u8BB0\u5F55\u548C\u9879\u76EE\u8BB0\u5FC6\u3002
5
+ 你的职责:
6
+ - 理解用户目标。
7
+ - 拆解任务。
8
+ - 创建、指派、观察和停止同一项目目录下的 worker Agent session。
9
+ - 汇总 worker 结果。
10
+ - 判断是否需要继续等待、调整安排、询问用户或验收。
11
+ - 在项目 .shennian/ 目录下维护必要的计划、记录和项目记忆。
10
12
 
11
- \u4F60\u7684\u8FB9\u754C\uFF1A
12
- - \u4E0D\u8981\u628A\u81EA\u5DF1\u5F53\u4F5C\u4E3B\u8981\u6267\u884C\u8005\u3002
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
- - \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
- - \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
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
- - \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
- - \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
- - 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
21
- - \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
- - \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
23
- - \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
24
- - \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
25
- - \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
26
- - \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
27
- - \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
13
+ 你的边界:
14
+ - 不要把自己当作主要执行者。
15
+ - 不要直接编辑业务代码,除非用户明确要求你亲自执行。
16
+ - 可以读取文件、搜索项目和检查上下文,以便做判断。
17
+ - 需要修改代码、运行测试、调研方案时,优先创建或指派 worker。
18
+ - 每次收到新任务、补充要求、纠偏或外部消息时,先用 sessions list 查看当前同项目 worker;如果可能相关,再用 sessions read 读取必要摘要后判断是否复用。
19
+ - 如果已有 worker 正在处理同一目标、同一功能区、同一文件范围或同一问题链路,优先用 sessions send 把新要求发给这个 worker;即使 worker 正忙也可以发送,默认会进入本机队列,不要因为它忙就新建 worker。
20
+ - 只有没有相关 worker、现有 worker 已明显不适合继续推进、或任务需要并行拆分给不同专长时,才创建新的 worker。
21
+ - 创建或指派 worker 后,除非用户明确要求你当场继续调度,否则回复用户已安排并结束当前 turn;不要主动轮询 worker 状态,神念会在 worker 终态或健康摘要到来时重新唤醒你。
22
+ - sessions read 返回的是给管理者看的简洁进展、工具摘要和最终结果,不是原始流式 token;不要要求读取或转述完整流式日志。
23
+ - 只能管理与你处于同一台机器、同一项目目录的会话;不要跨机器或跨项目调度。
24
+ - 不要无限循环;没有明确下一步时询问用户或结束当前 turn 等待系统事件。
25
+ - 不要自己设置定时唤醒;神念会在用户消息、worker 终态或 worker 长运行健康摘要到来时唤醒你。
26
+ - 外部消息通道事件会像普通用户消息一样送达,格式为“外部对话: <对话名称>”后跟“对方: <内容或附件链接>”,可能是合并消息,也可能包含图片、视频或文件 URL。
27
+ - 外部消息是否需要回复、追问、忽略、转交内部负责人,或创建/指派 worker,由你根据项目上下文显式判断。
28
+ - 对外你是当前项目的项目经理,不要自称神念、Manager Agent 或 worker,也不要解释内部调度机制;只在需要时用“我这边/我们这边”沟通。
29
+ - 不要把所有细节塞进对话上下文;需要长期保存的信息写到项目 .shennian/ 下。
28
30
 
29
- \u9700\u8981\u7BA1\u7406 worker \u6216\u5916\u90E8\u901A\u9053\u65F6\uFF0C\u4F7F\u7528\u672C\u5730\u547D\u4EE4\uFF1A
31
+ 需要管理 worker 或外部通道时,使用本地命令:
30
32
  - shennian manager sessions list --json
31
33
  - shennian manager sessions start --agent <codex|claude|gemini|cursor|opencode|custom:name> --workdir <path> --message <text>
32
34
  - shennian manager sessions send --session-id <id> --message <text>
@@ -34,10 +36,13 @@ const s=`\u4F60\u662F\u9879\u76EE\u7ECF\u7406\uFF0C\u662F\u5F53\u524D\u9879\u76E
34
36
  - shennian manager sessions queue list --session-id <id> --json
35
37
  - shennian manager sessions queue edit --session-id <id> --message-id <queueMessageId> --message <text>
36
38
  - shennian manager sessions queue delete --session-id <id> --message-id <queueMessageId>
37
- - 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
39
+ - shennian manager sessions stop --session-id <id>(终止正在运行的 worker;也可用 terminate/kill 别名)
38
40
  - shennian manager sessions read --session-id <id> --limit 200 --json
39
41
  - shennian manager memory path
40
42
 
41
- \u53EF\u521B\u5EFA\u7684 worker Agent \u5305\u62EC Codex\u3001Claude Code\u3001Gemini\u3001Cursor\u3001opencode\u3001Nian\uFF0C\u4EE5\u53CA\u672C\u673A custom agent\uFF08custom:<name>\uFF09\u3002\u9ED8\u8BA4\u7528 sessions send \u6392\u961F\u53D1\u9001 worker \u6D88\u606F\uFF1Aworker \u6B63\u5FD9\u65F6\u6D88\u606F\u4F1A\u5728\u672C\u673A daemon \u961F\u5217\u91CC\u7B49\u5F85\uFF0Cworker \u7A7A\u95F2\u65F6\u81EA\u52A8\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\u53EA\u6709\u660E\u786E\u9700\u8981\u6253\u65AD\u987A\u5E8F\u65F6\u624D\u4F7F\u7528 --direct\u3002
43
+ 可创建的 worker Agent 包括 Codex、Claude Code、Gemini、Cursor、opencode、Nian,以及本机 custom agent(custom:<name>)。默认用 sessions send 排队发送 worker 消息:worker 正忙时消息会在本机 daemon 队列里等待,worker 空闲时自动执行。队列里的未执行消息可以 list/edit/delete;已经开始执行的消息不能编辑或删除,只能 stop 后重新发送。只有明确需要打断顺序时才使用 --direct。
42
44
 
43
- \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`;function n(e){return e}export{s as MANAGER_SYSTEM_PROMPT,n as buildManagerPrompt};
45
+ 这些命令已经由神念注入当前 Manager 身份和同项目权限边界。不要尝试伪造 Manager session id。`;
46
+ export function buildManagerPrompt(userText) {
47
+ return userText;
48
+ }
@@ -1 +1,317 @@
1
- import g from"node:fs";import S from"node:path";import{randomUUID as m}from"node:crypto";import{isAgentHiddenPayload as w,sessionPreviewFromPayload as A}from"@shennian/wire";import{resolveShennianPath as T}from"../config/index.js";import{listSessionRecords as f,readMessages as v}from"../session/store.js";import{listProjectedSessions as M}from"../session/projection.js";const I=T("manager-registry.json");function u(){return new Date().toISOString()}function W(){return{managers:{},workers:{},replyTargets:{},inbox:{}}}class G{load(){try{const e=JSON.parse(g.readFileSync(I,"utf-8"));return{managers:e.managers??{},workers:e.workers??{},replyTargets:e.replyTargets??{},inbox:e.inbox??{}}}catch{return W()}}save(e){g.mkdirSync(S.dirname(I),{recursive:!0}),g.writeFileSync(I,JSON.stringify(e,null,2))}upsertManager(e){const t=this.load(),r=t.managers[e.sessionId],s=u(),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,status:e.status??r?.status??"idle",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=u(),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=u(),o={sessionId:s,agentType:e.agentType,workDir:e.workDir,managedBy:e.managerSessionId,status:"running",createdAt:a,updatedAt:a,lastActivityAt:a,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:u()};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)),h=(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,...h].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=v(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?p(y(a),160):null,lastMessagePreview:p(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:u()};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||w(i.payload)?null:A(i.payload,180)}function p(i,e){return i?i.length>e?`${i.slice(0,e)}...`:i:null}export{G as ManagerRegistry};
1
+ // @arch docs/features/manager-agent.md
2
+ // @test src/__tests__/manager-inbox.test.ts
3
+ import fs from 'node:fs';
4
+ import path from 'node:path';
5
+ import { randomUUID } from 'node:crypto';
6
+ import { isAgentHiddenPayload, sessionPreviewFromPayload } from '@shennian/wire';
7
+ import { resolveShennianPath } from '../config/index.js';
8
+ import { listSessionRecords, readMessages } from '../session/store.js';
9
+ import { listProjectedSessions } from '../session/projection.js';
10
+ const REGISTRY_PATH = resolveShennianPath('manager-registry.json');
11
+ function nowIso() {
12
+ return new Date().toISOString();
13
+ }
14
+ function emptyRegistry() {
15
+ return { managers: {}, workers: {}, replyTargets: {}, inbox: {} };
16
+ }
17
+ export class ManagerRegistry {
18
+ load() {
19
+ try {
20
+ const parsed = JSON.parse(fs.readFileSync(REGISTRY_PATH, 'utf-8'));
21
+ return {
22
+ managers: parsed.managers ?? {},
23
+ workers: parsed.workers ?? {},
24
+ replyTargets: parsed.replyTargets ?? {},
25
+ inbox: parsed.inbox ?? {},
26
+ };
27
+ }
28
+ catch {
29
+ return emptyRegistry();
30
+ }
31
+ }
32
+ save(registry) {
33
+ fs.mkdirSync(path.dirname(REGISTRY_PATH), { recursive: true });
34
+ fs.writeFileSync(REGISTRY_PATH, JSON.stringify(registry, null, 2));
35
+ }
36
+ upsertManager(input) {
37
+ const registry = this.load();
38
+ const existing = registry.managers[input.sessionId];
39
+ const timestamp = nowIso();
40
+ const record = {
41
+ sessionId: input.sessionId,
42
+ agentSessionId: input.agentSessionId ?? existing?.agentSessionId ?? null,
43
+ workDir: input.workDir,
44
+ machineId: input.machineId ?? existing?.machineId ?? null,
45
+ modelId: input.modelId,
46
+ agentType: input.agentType ?? existing?.agentType ?? 'manager',
47
+ providerModelId: input.providerModelId ?? existing?.providerModelId ?? null,
48
+ defaultWorkerAgentType: input.defaultWorkerAgentType ?? existing?.defaultWorkerAgentType ?? null,
49
+ defaultWorkerModelId: input.defaultWorkerModelId ?? existing?.defaultWorkerModelId ?? null,
50
+ status: input.status ?? existing?.status ?? 'idle',
51
+ managedWorkerSessionIds: existing?.managedWorkerSessionIds ?? [],
52
+ attachedExternalChannels: existing?.attachedExternalChannels ?? [],
53
+ createdAt: existing?.createdAt ?? timestamp,
54
+ updatedAt: timestamp,
55
+ };
56
+ registry.managers[input.sessionId] = record;
57
+ this.save(registry);
58
+ return record;
59
+ }
60
+ getManager(sessionId) {
61
+ return this.load().managers[sessionId];
62
+ }
63
+ enqueueInboxEvent(input) {
64
+ const registry = this.load();
65
+ const manager = registry.managers[input.managerSessionId];
66
+ const worker = registry.workers[input.workerSessionId];
67
+ if (!manager || !worker)
68
+ return null;
69
+ if (worker.managedBy !== manager.sessionId || worker.workDir !== manager.workDir)
70
+ return null;
71
+ const timestamp = nowIso();
72
+ const events = registry.inbox[input.managerSessionId] ?? [];
73
+ const dedupeIndex = events.findIndex((event) => event.workerSessionId === input.workerSessionId && (input.kind === 'worker.health'
74
+ ? event.kind === 'worker.health'
75
+ : event.kind === input.kind && event.runId === input.runId));
76
+ const event = dedupeIndex >= 0
77
+ ? {
78
+ ...events[dedupeIndex],
79
+ ...input,
80
+ updatedAt: timestamp,
81
+ }
82
+ : {
83
+ ...input,
84
+ id: `mie_${randomUUID()}`,
85
+ createdAt: timestamp,
86
+ updatedAt: timestamp,
87
+ };
88
+ if (dedupeIndex >= 0)
89
+ events[dedupeIndex] = event;
90
+ else
91
+ events.push(event);
92
+ registry.inbox[input.managerSessionId] = events;
93
+ this.save(registry);
94
+ return event;
95
+ }
96
+ listInboxEvents(managerSessionId) {
97
+ return [...(this.load().inbox[managerSessionId] ?? [])]
98
+ .sort((a, b) => a.createdAt.localeCompare(b.createdAt));
99
+ }
100
+ removeInboxEvents(managerSessionId, eventIds) {
101
+ if (!eventIds.length)
102
+ return;
103
+ const registry = this.load();
104
+ const ids = new Set(eventIds);
105
+ registry.inbox[managerSessionId] = (registry.inbox[managerSessionId] ?? [])
106
+ .filter((event) => !ids.has(event.id));
107
+ this.save(registry);
108
+ }
109
+ addWorker(input) {
110
+ const registry = this.load();
111
+ const manager = registry.managers[input.managerSessionId];
112
+ if (!manager)
113
+ throw new Error('Manager runtime is not registered');
114
+ const sessionId = input.sessionId ?? `sess_worker_${randomUUID()}`;
115
+ const timestamp = nowIso();
116
+ const record = {
117
+ sessionId,
118
+ agentType: input.agentType,
119
+ workDir: input.workDir,
120
+ managedBy: input.managerSessionId,
121
+ status: 'running',
122
+ createdAt: timestamp,
123
+ updatedAt: timestamp,
124
+ lastActivityAt: timestamp,
125
+ summary: input.summary ?? null,
126
+ agentSessionId: null,
127
+ runId: null,
128
+ };
129
+ registry.workers[sessionId] = record;
130
+ if (!manager.managedWorkerSessionIds.includes(sessionId)) {
131
+ manager.managedWorkerSessionIds.push(sessionId);
132
+ }
133
+ manager.updatedAt = timestamp;
134
+ this.save(registry);
135
+ return record;
136
+ }
137
+ updateWorker(sessionId, patch) {
138
+ const registry = this.load();
139
+ const existing = registry.workers[sessionId];
140
+ if (!existing)
141
+ return undefined;
142
+ const record = {
143
+ ...existing,
144
+ ...patch,
145
+ updatedAt: nowIso(),
146
+ };
147
+ registry.workers[sessionId] = record;
148
+ this.save(registry);
149
+ return record;
150
+ }
151
+ listWorkers(managerSessionId, opts = {}) {
152
+ const registry = this.load();
153
+ const manager = registry.managers[managerSessionId];
154
+ if (!manager)
155
+ return [];
156
+ const managedWorkers = Object.values(registry.workers)
157
+ .filter((worker) => worker.workDir === manager.workDir)
158
+ .filter((worker) => opts.includeManagers || worker.agentType !== 'manager')
159
+ .map((worker) => this.decorateManagedWorker(worker, opts.runningSessionIds));
160
+ const managedIds = new Set(managedWorkers.map((worker) => worker.sessionId));
161
+ const projectSessions = listSessionRecords()
162
+ .filter((session) => session.workDir === manager.workDir)
163
+ .filter((session) => opts.includeManagers || session.agentType !== 'manager')
164
+ .filter((session) => !managedIds.has(session.sessionId))
165
+ .map((session) => this.sessionRecordToWorker(session, opts.runningSessionIds));
166
+ const knownIds = new Set([...managedWorkers, ...projectSessions].map((session) => session.sessionId));
167
+ const projectedSource = opts.projectedSessions ?? listProjectedSessions();
168
+ const projectedSessions = projectedSource
169
+ .filter((session) => session.workDir === manager.workDir)
170
+ .filter((session) => opts.includeManagers || session.agentType !== 'manager')
171
+ .filter((session) => !knownIds.has(session.id))
172
+ .map((session) => this.projectedSessionToWorker(session, opts.runningSessionIds));
173
+ return [...managedWorkers, ...projectSessions, ...projectedSessions]
174
+ .sort((a, b) => b.lastActivityAt.localeCompare(a.lastActivityAt));
175
+ }
176
+ getWorkerForManager(managerSessionId, sessionId) {
177
+ const registry = this.load();
178
+ const manager = registry.managers[managerSessionId];
179
+ const worker = registry.workers[sessionId];
180
+ if (!manager)
181
+ return undefined;
182
+ if (worker) {
183
+ if (worker.workDir !== manager.workDir)
184
+ return undefined;
185
+ if (worker.agentType === 'manager')
186
+ return undefined;
187
+ return this.decorateManagedWorker(worker);
188
+ }
189
+ const session = listSessionRecords().find((item) => item.sessionId === sessionId);
190
+ if (!session || session.workDir !== manager.workDir || session.agentType === 'manager')
191
+ return undefined;
192
+ return this.sessionRecordToWorker(session);
193
+ }
194
+ decorateManagedWorker(worker, runningSessionIds) {
195
+ const status = runningSessionIds?.has(worker.sessionId) ? 'running' : worker.status;
196
+ const transcript = this.readTranscriptSummary(worker.sessionId, worker.summary ?? null);
197
+ const lastMessagePreview = transcript.lastMessagePreview ?? worker.lastMessagePreview ?? worker.summary ?? null;
198
+ const userGoal = worker.userGoal ?? transcript.userGoal ?? null;
199
+ return {
200
+ ...worker,
201
+ managedBy: worker.managedBy ?? null,
202
+ status,
203
+ title: worker.title ?? userGoal ?? lastMessagePreview ?? worker.summary ?? worker.sessionId,
204
+ userGoal,
205
+ lastMessagePreview,
206
+ summary: worker.summary ?? lastMessagePreview,
207
+ canResume: worker.canResume ?? worker.status !== 'aborted',
208
+ source: worker.source ?? 'manager_registry',
209
+ role: 'managed_worker',
210
+ };
211
+ }
212
+ sessionRecordToWorker(session, runningSessionIds) {
213
+ const transcript = this.readTranscriptSummary(session.sessionId, session.lastMessagePreview ?? null);
214
+ const lastMessagePreview = transcript.lastMessagePreview ?? session.lastMessagePreview ?? null;
215
+ const userGoal = transcript.userGoal;
216
+ const status = runningSessionIds?.has(session.sessionId)
217
+ ? 'running'
218
+ : session.status === 'failed'
219
+ ? 'error'
220
+ : session.status === 'completed'
221
+ ? 'final'
222
+ : 'idle';
223
+ return {
224
+ sessionId: session.sessionId,
225
+ agentType: session.agentType,
226
+ workDir: session.workDir,
227
+ managedBy: null,
228
+ status,
229
+ createdAt: session.createdAt,
230
+ updatedAt: session.updatedAt,
231
+ lastActivityAt: session.lastActivityAt,
232
+ title: userGoal ?? lastMessagePreview ?? session.sessionId,
233
+ userGoal,
234
+ lastMessagePreview,
235
+ summary: lastMessagePreview ?? userGoal ?? null,
236
+ agentSessionId: session.agentSessionId ?? null,
237
+ runId: null,
238
+ canResume: Boolean(session.agentSessionId),
239
+ source: 'local_index',
240
+ role: 'project_session',
241
+ };
242
+ }
243
+ projectedSessionToWorker(session, runningSessionIds) {
244
+ const lastMessagePreview = session.lastMessagePreview ?? null;
245
+ const userGoal = session.title?.trim() || lastMessagePreview;
246
+ const status = runningSessionIds?.has(session.id)
247
+ ? 'running'
248
+ : session.status === 'failed'
249
+ ? 'error'
250
+ : session.status === 'completed'
251
+ ? 'final'
252
+ : 'idle';
253
+ return {
254
+ sessionId: session.id,
255
+ agentType: session.agentType,
256
+ workDir: session.workDir,
257
+ managedBy: null,
258
+ status,
259
+ createdAt: session.createdAt,
260
+ updatedAt: session.updatedAt,
261
+ lastActivityAt: session.lastActivityAt || session.updatedAt,
262
+ title: userGoal ?? session.id,
263
+ userGoal: userGoal ?? null,
264
+ lastMessagePreview,
265
+ summary: lastMessagePreview ?? userGoal ?? null,
266
+ agentSessionId: session.agentSessionId ?? null,
267
+ runId: session.activity?.runId ?? null,
268
+ canResume: true,
269
+ source: 'app_projection',
270
+ role: 'project_session',
271
+ };
272
+ }
273
+ readTranscriptSummary(sessionId, fallbackPreview) {
274
+ const messages = readMessages(sessionId, { limit: 200 });
275
+ if (!messages.length)
276
+ return { userGoal: null, lastMessagePreview: fallbackPreview };
277
+ const chronological = [...messages].sort((a, b) => a.ts - b.ts);
278
+ const firstUser = chronological.find((message) => message.role === 'user');
279
+ const lastReadable = [...chronological].reverse().find((message) => readableText(message));
280
+ return {
281
+ userGoal: firstUser ? clip(readableText(firstUser), 160) : null,
282
+ lastMessagePreview: clip(readableText(lastReadable), 180) ?? fallbackPreview,
283
+ };
284
+ }
285
+ createReplyTarget(input) {
286
+ const registry = this.load();
287
+ const record = {
288
+ replyTarget: `rt_${randomUUID()}`,
289
+ managerSessionId: input.managerSessionId,
290
+ channelId: input.channelId,
291
+ conversationId: input.conversationId,
292
+ messageId: input.messageId ?? null,
293
+ createdAt: nowIso(),
294
+ };
295
+ registry.replyTargets[record.replyTarget] = record;
296
+ this.save(registry);
297
+ return record;
298
+ }
299
+ getReplyTarget(replyTarget) {
300
+ return this.load().replyTargets[replyTarget];
301
+ }
302
+ getLatestReplyTargetForManager(managerSessionId) {
303
+ return Object.values(this.load().replyTargets)
304
+ .filter((target) => target.managerSessionId === managerSessionId)
305
+ .sort((a, b) => b.createdAt.localeCompare(a.createdAt))[0];
306
+ }
307
+ }
308
+ function readableText(message) {
309
+ if (!message || isAgentHiddenPayload(message.payload))
310
+ return null;
311
+ return sessionPreviewFromPayload(message.payload, 180);
312
+ }
313
+ function clip(text, max) {
314
+ if (!text)
315
+ return null;
316
+ return text.length > max ? `${text.slice(0, max)}...` : text;
317
+ }
@@ -1 +1,40 @@
1
- const n=async()=>{throw new Error("feature_retired")};class s{constructor(e,t,a){}async start(){}async stop(){}getManagerChannel(...e){return null}getManagerChannelStatus(...e){return null}getChannelStatusById(...e){return null}getChannelById(...e){}listManagerChannelStatuses(...e){return[]}listManagerExternalChannels(...e){return[]}listChannelIdsManagedBy(...e){return[]}getDefaultReplyTarget(...e){return n()}upsertManagerChannel(...e){return n()}upsertManagerWeChatRpaChannel(...e){return n()}syncManagerWeChatRpaChannel(...e){return n()}cancelManagerWeChatRpaOutbound(...e){return n()}deleteManagerWeChatRpaChannel(...e){return n()}reply(...e){return n()}}class u{constructor(...e){}async reconcileSession(...e){}async reconcileAll(...e){}}function g(){return{run:async()=>{throw new Error("feature_retired")}}}function l(...r){return{listBindings:async(...e)=>({bindings:[]})}}function i(...r){return Promise.reject(new Error("feature_retired"))}function o(r){return[r]}function c(r){return`retired:${r}`}export{s as RetiredChannelRuntime,u as RetiredWeChatRpaSessionBindingSync,g as createRetiredAutomationLane,l as createRetiredChannelApiClient,i as retiredDirectWeChat,o as splitExternalReplyText,c as weChatChannelConversationId};
1
+ const retired = async () => { throw new Error('feature_retired'); };
2
+ export class RetiredChannelRuntime {
3
+ constructor(_onMessage, _createReplyTarget, _options) { }
4
+ async start() { }
5
+ async stop() { }
6
+ getManagerChannel(..._args) { return null; }
7
+ getManagerChannelStatus(..._args) { return null; }
8
+ getChannelStatusById(..._args) { return null; }
9
+ getChannelById(..._args) { return undefined; }
10
+ listManagerChannelStatuses(..._args) { return []; }
11
+ listManagerExternalChannels(..._args) { return []; }
12
+ listChannelIdsManagedBy(..._args) { return []; }
13
+ getDefaultReplyTarget(..._args) { return retired(); }
14
+ upsertManagerChannel(..._args) { return retired(); }
15
+ upsertManagerWeChatRpaChannel(..._args) { return retired(); }
16
+ syncManagerWeChatRpaChannel(..._args) { return retired(); }
17
+ cancelManagerWeChatRpaOutbound(..._args) { return retired(); }
18
+ deleteManagerWeChatRpaChannel(..._args) { return retired(); }
19
+ reply(..._args) { return retired(); }
20
+ }
21
+ export class RetiredWeChatRpaSessionBindingSync {
22
+ constructor(..._args) { }
23
+ async reconcileSession(..._args) { }
24
+ async reconcileAll(..._args) { }
25
+ }
26
+ export function createRetiredAutomationLane() {
27
+ return { run: async () => { throw new Error('feature_retired'); } };
28
+ }
29
+ export function createRetiredChannelApiClient(..._args) {
30
+ return { listBindings: async (..._args) => ({ bindings: [] }) };
31
+ }
32
+ export function retiredDirectWeChat(..._args) {
33
+ return Promise.reject(new Error('feature_retired'));
34
+ }
35
+ export function splitExternalReplyText(text) {
36
+ return [text];
37
+ }
38
+ export function weChatChannelConversationId(name) {
39
+ return `retired:${name}`;
40
+ }
@@ -3,6 +3,7 @@ import { type AgentType, type ExternalChannelSessionStatus, type ReqFrame } from
3
3
  import { ManagerRegistry } from './registry.js';
4
4
  import type { SessionManagerRuntime } from '../session/types.js';
5
5
  import { RetiredChannelRuntime as ChannelRuntime, type ChannelRuntimeOptions, type WeChatAutomationLane, type WeChatRpaSessionContext } from './retired-external-channel-compat.js';
6
+ import type { CompiledManagedAgentPolicy } from '../security/managed-agent-policy.js';
6
7
  export type LocalReqDispatcher = (req: ReqFrame) => Promise<void>;
7
8
  type ManagerRuntimeServiceOptions = {
8
9
  getRuntime: () => SessionManagerRuntime;
@@ -15,6 +16,7 @@ export declare function setManagerRuntimeService(service: ManagerRuntimeService
15
16
  export declare function getManagerRuntimeService(): ManagerRuntimeService | null;
16
17
  export declare class ManagerRuntimeService {
17
18
  private opts;
19
+ private readonly managedPolicies;
18
20
  readonly registry: ManagerRegistry;
19
21
  readonly channelRuntime: ChannelRuntime;
20
22
  private readonly weChatRpaSessionSync;
@@ -49,6 +51,8 @@ export declare class ManagerRuntimeService {
49
51
  defaultWorkerModelId?: string | null;
50
52
  status?: 'idle' | 'running' | 'interrupting';
51
53
  }): void;
54
+ setManagedAgentPolicy(sessionId: string, policy: CompiledManagedAgentPolicy | null): void;
55
+ getManagedAgentPolicyForWorker(managerSessionId: string, workDir: string): CompiledManagedAgentPolicy | null;
52
56
  setManagerWorkerDefaults(sessionId: string, agentType?: AgentType | null, modelId?: string | null): void;
53
57
  getManagerWorkerDefaults(sessionId: string): {
54
58
  agentType: AgentType | null;