shennian 0.3.1 → 0.3.3

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 (296) hide show
  1. package/README.md +3 -1
  2. package/dist/publish-build-manifest.json +291 -341
  3. package/dist/src/agents/codex.js +1 -1
  4. package/dist/src/agents/command-spec.js +2 -2
  5. package/dist/src/agents/detect.js +1 -1
  6. package/dist/src/agents/model-registry/discovery.js +2 -2
  7. package/dist/src/agents/model-registry/parsers.d.ts +1 -0
  8. package/dist/src/agents/model-registry/parsers.js +4 -4
  9. package/dist/src/agents/model-registry/service.js +1 -1
  10. package/dist/src/agents/native-capabilities.d.ts +10 -0
  11. package/dist/src/agents/native-capabilities.js +1 -0
  12. package/dist/src/agents/platform-instructions.js +5 -5
  13. package/dist/src/agents/runtime-compatibility.d.ts +11 -0
  14. package/dist/src/agents/runtime-compatibility.js +1 -0
  15. package/dist/src/agents/workbuddy.d.ts +44 -0
  16. package/dist/src/agents/workbuddy.js +3 -0
  17. package/dist/src/commands/daemon.d.ts +1 -0
  18. package/dist/src/commands/daemon.js +9 -9
  19. package/dist/src/commands/integration.d.ts +2 -0
  20. package/dist/src/commands/integration.js +1 -0
  21. package/dist/src/commands/manager.js +1 -2
  22. package/dist/src/commands/pair-browser.d.ts +43 -0
  23. package/dist/src/commands/pair-browser.js +1 -0
  24. package/dist/src/commands/pair.d.ts +2 -0
  25. package/dist/src/commands/pair.js +11 -8
  26. package/dist/src/commands/room.d.ts +13 -0
  27. package/dist/src/commands/room.js +4 -0
  28. package/dist/src/commands/upgrade.d.ts +3 -0
  29. package/dist/src/commands/upgrade.js +1 -1
  30. package/dist/src/config/index.d.ts +3 -1
  31. package/dist/src/config/index.js +2 -1
  32. package/dist/src/daemon-ipc/client.d.ts +8 -0
  33. package/dist/src/daemon-ipc/client.js +3 -0
  34. package/dist/src/daemon-ipc/paths.d.ts +7 -0
  35. package/dist/src/daemon-ipc/paths.js +1 -0
  36. package/dist/src/daemon-ipc/protocol.d.ts +30 -0
  37. package/dist/src/daemon-ipc/protocol.js +1 -0
  38. package/dist/src/daemon-ipc/secret.d.ts +3 -0
  39. package/dist/src/daemon-ipc/secret.js +3 -0
  40. package/dist/src/daemon-ipc/server.d.ts +13 -0
  41. package/dist/src/daemon-ipc/server.js +3 -0
  42. package/dist/src/index.js +2 -2
  43. package/dist/src/integrations/claude-room-plugin-lifecycle.d.ts +47 -0
  44. package/dist/src/integrations/claude-room-plugin-lifecycle.js +1 -0
  45. package/dist/src/integrations/codex-room-plugin-lifecycle.d.ts +49 -0
  46. package/dist/src/integrations/codex-room-plugin-lifecycle.js +1 -0
  47. package/dist/src/integrations/room-plugin-bundles.d.ts +23 -0
  48. package/dist/src/integrations/room-plugin-bundles.js +1 -0
  49. package/dist/src/integrations/room-plugin-lifecycle-common.d.ts +17 -0
  50. package/dist/src/integrations/room-plugin-lifecycle-common.js +1 -0
  51. package/dist/src/integrations/room-plugin-shared.d.ts +24 -0
  52. package/dist/src/integrations/room-plugin-shared.js +2 -0
  53. package/dist/src/integrations/room-session-hook.d.ts +30 -0
  54. package/dist/src/integrations/room-session-hook.js +2 -0
  55. package/dist/src/integrations/workbuddy-plugin-bundle.d.ts +15 -0
  56. package/dist/src/integrations/workbuddy-plugin-bundle.js +1 -0
  57. package/dist/src/integrations/workbuddy-plugin-lifecycle.d.ts +52 -0
  58. package/dist/src/integrations/workbuddy-plugin-lifecycle.js +2 -0
  59. package/dist/src/integrations/workbuddy-session-hook.d.ts +22 -0
  60. package/dist/src/integrations/workbuddy-session-hook.js +1 -0
  61. package/dist/src/manager/prompt.d.ts +1 -1
  62. package/dist/src/manager/prompt.js +1 -1
  63. package/dist/src/manager/retired-external-channel-compat.d.ts +78 -0
  64. package/dist/src/manager/retired-external-channel-compat.js +1 -0
  65. package/dist/src/manager/runtime.d.ts +1 -3
  66. package/dist/src/manager/runtime.js +7 -7
  67. package/dist/src/relay/client.d.ts +2 -0
  68. package/dist/src/relay/client.js +1 -1
  69. package/dist/src/room/activation-audit.d.ts +15 -0
  70. package/dist/src/room/activation-audit.js +2 -0
  71. package/dist/src/room/activation-policy.d.ts +8 -0
  72. package/dist/src/room/activation-policy.js +1 -0
  73. package/dist/src/room/activation-service.d.ts +25 -0
  74. package/dist/src/room/activation-service.js +2 -0
  75. package/dist/src/room/agent-tool.d.ts +62 -0
  76. package/dist/src/room/agent-tool.js +1 -0
  77. package/dist/src/room/attachment-cache.d.ts +40 -0
  78. package/dist/src/room/attachment-cache.js +2 -0
  79. package/dist/src/room/attachment-upload.d.ts +20 -0
  80. package/dist/src/room/attachment-upload.js +1 -0
  81. package/dist/src/room/authorized-action-coordinator.d.ts +40 -0
  82. package/dist/src/room/authorized-action-coordinator.js +1 -0
  83. package/dist/src/room/authorized-action-ipc.d.ts +11 -0
  84. package/dist/src/room/authorized-action-ipc.js +1 -0
  85. package/dist/src/room/device-authorization-client.d.ts +49 -0
  86. package/dist/src/room/device-authorization-client.js +1 -0
  87. package/dist/src/room/host-session-observation-ipc.d.ts +9 -0
  88. package/dist/src/room/host-session-observation-ipc.js +1 -0
  89. package/dist/src/room/host-session-observations.d.ts +54 -0
  90. package/dist/src/room/host-session-observations.js +2 -0
  91. package/dist/src/room/invite-bootstrap.d.ts +35 -0
  92. package/dist/src/room/invite-bootstrap.js +1 -0
  93. package/dist/src/room/invocation-token-ipc.d.ts +9 -0
  94. package/dist/src/room/invocation-token-ipc.js +1 -0
  95. package/dist/src/room/invocation-token.d.ts +53 -0
  96. package/dist/src/room/invocation-token.js +1 -0
  97. package/dist/src/room/jsonl-mirror.d.ts +73 -0
  98. package/dist/src/room/jsonl-mirror.js +6 -0
  99. package/dist/src/room/managed-room-binding.d.ts +42 -0
  100. package/dist/src/room/managed-room-binding.js +1 -0
  101. package/dist/src/room/managed-room-session.d.ts +143 -0
  102. package/dist/src/room/managed-room-session.js +3 -0
  103. package/dist/src/room/mirror-sync-service.d.ts +33 -0
  104. package/dist/src/room/mirror-sync-service.js +1 -0
  105. package/dist/src/room/operator-ipc.d.ts +11 -0
  106. package/dist/src/room/operator-ipc.js +1 -0
  107. package/dist/src/room/pending-authorized-actions.d.ts +69 -0
  108. package/dist/src/room/pending-authorized-actions.js +3 -0
  109. package/dist/src/room/room-action-client.d.ts +210 -0
  110. package/dist/src/room/room-action-client.js +1 -0
  111. package/dist/src/room/room-action-ipc.d.ts +33 -0
  112. package/dist/src/room/room-action-ipc.js +1 -0
  113. package/dist/src/room/room-action-schema.d.ts +3 -0
  114. package/dist/src/room/room-action-schema.js +1 -0
  115. package/dist/src/room/room-authorized-action-executor.d.ts +23 -0
  116. package/dist/src/room/room-authorized-action-executor.js +1 -0
  117. package/dist/src/room/room-binding-client.d.ts +51 -0
  118. package/dist/src/room/room-binding-client.js +1 -0
  119. package/dist/src/room/room-binding-ipc.d.ts +29 -0
  120. package/dist/src/room/room-binding-ipc.js +1 -0
  121. package/dist/src/room/session-registry-ipc.d.ts +14 -0
  122. package/dist/src/room/session-registry-ipc.js +1 -0
  123. package/dist/src/room/session-registry.d.ts +128 -0
  124. package/dist/src/room/session-registry.js +2 -0
  125. package/dist/src/room/tool-schema.d.ts +118 -0
  126. package/dist/src/room/tool-schema.js +1 -0
  127. package/dist/src/room/tool-server.d.ts +5 -0
  128. package/dist/src/room/tool-server.js +3 -0
  129. package/dist/src/session/handlers/agent-capabilities.d.ts +3 -0
  130. package/dist/src/session/handlers/agent-capabilities.js +1 -0
  131. package/dist/src/session/handlers/agent-config.js +1 -1
  132. package/dist/src/session/handlers/chat.js +2 -2
  133. package/dist/src/session/handlers/control.js +1 -1
  134. package/dist/src/session/handlers/fs.d.ts +1 -0
  135. package/dist/src/session/handlers/fs.js +1 -1
  136. package/dist/src/session/handlers/room-managed.d.ts +4 -0
  137. package/dist/src/session/handlers/room-managed.js +1 -0
  138. package/dist/src/session/manager.d.ts +18 -6
  139. package/dist/src/session/manager.js +1 -1
  140. package/dist/src/session/types.d.ts +5 -0
  141. package/dist/src/upgrade/engine.d.ts +14 -1
  142. package/dist/src/upgrade/engine.js +2 -2
  143. package/dist/src/upgrade/scheduler.d.ts +45 -0
  144. package/dist/src/upgrade/scheduler.js +1 -0
  145. package/node_modules/@shennian/wire/dist/agent-capabilities.d.ts +17 -0
  146. package/node_modules/@shennian/wire/dist/agent-capabilities.d.ts.map +1 -0
  147. package/node_modules/@shennian/wire/dist/agent-capabilities.js +2 -0
  148. package/node_modules/@shennian/wire/dist/agent-capabilities.js.map +1 -0
  149. package/node_modules/@shennian/wire/dist/agent-workspace.d.ts +41 -0
  150. package/node_modules/@shennian/wire/dist/agent-workspace.d.ts.map +1 -0
  151. package/node_modules/@shennian/wire/dist/agent-workspace.js +3 -0
  152. package/node_modules/@shennian/wire/dist/agent-workspace.js.map +1 -0
  153. package/node_modules/@shennian/wire/dist/index.d.ts +3 -1
  154. package/node_modules/@shennian/wire/dist/index.d.ts.map +1 -1
  155. package/node_modules/@shennian/wire/dist/index.js +3 -1
  156. package/node_modules/@shennian/wire/dist/index.js.map +1 -1
  157. package/node_modules/@shennian/wire/dist/machine.d.ts +1 -0
  158. package/node_modules/@shennian/wire/dist/machine.d.ts.map +1 -1
  159. package/node_modules/@shennian/wire/dist/methods.d.ts +1 -1
  160. package/node_modules/@shennian/wire/dist/methods.d.ts.map +1 -1
  161. package/node_modules/@shennian/wire/dist/room.d.ts +112 -0
  162. package/node_modules/@shennian/wire/dist/room.d.ts.map +1 -0
  163. package/node_modules/@shennian/wire/dist/room.js +4 -0
  164. package/node_modules/@shennian/wire/dist/room.js.map +1 -0
  165. package/node_modules/@shennian/wire/dist/session.d.ts +15 -2
  166. package/node_modules/@shennian/wire/dist/session.d.ts.map +1 -1
  167. package/node_modules/@shennian/wire/dist/session.js +1 -1
  168. package/node_modules/@shennian/wire/dist/session.js.map +1 -1
  169. package/package.json +3 -5
  170. package/dist/scripts/wechat-rpa-confirmation.mjs +0 -5
  171. package/dist/src/agents/pi-context.d.ts +0 -42
  172. package/dist/src/agents/pi-context.js +0 -12
  173. package/dist/src/agents/pi.d.ts +0 -70
  174. package/dist/src/agents/pi.js +0 -17
  175. package/dist/src/channels/base.d.ts +0 -144
  176. package/dist/src/channels/base.js +0 -1
  177. package/dist/src/channels/registry.d.ts +0 -8
  178. package/dist/src/channels/registry.js +0 -1
  179. package/dist/src/channels/reply-split.d.ts +0 -1
  180. package/dist/src/channels/reply-split.js +0 -10
  181. package/dist/src/channels/runtime.d.ts +0 -138
  182. package/dist/src/channels/runtime.js +0 -5
  183. package/dist/src/channels/secret-registry.d.ts +0 -35
  184. package/dist/src/channels/secret-registry.js +0 -1
  185. package/dist/src/channels/websocket.d.ts +0 -55
  186. package/dist/src/channels/websocket.js +0 -8
  187. package/dist/src/channels/wechat-channel/anchor.d.ts +0 -10
  188. package/dist/src/channels/wechat-channel/anchor.js +0 -1
  189. package/dist/src/channels/wechat-channel/automation-lane.d.ts +0 -35
  190. package/dist/src/channels/wechat-channel/automation-lane.js +0 -2
  191. package/dist/src/channels/wechat-channel/client.d.ts +0 -283
  192. package/dist/src/channels/wechat-channel/client.js +0 -1
  193. package/dist/src/channels/wechat-channel/cooldown.d.ts +0 -21
  194. package/dist/src/channels/wechat-channel/cooldown.js +0 -1
  195. package/dist/src/channels/wechat-channel/core/fixture-compare.d.ts +0 -52
  196. package/dist/src/channels/wechat-channel/core/fixture-compare.js +0 -1
  197. package/dist/src/channels/wechat-channel/core/media-action-plan.d.ts +0 -3
  198. package/dist/src/channels/wechat-channel/core/media-action-plan.js +0 -1
  199. package/dist/src/channels/wechat-channel/core/schema.d.ts +0 -67
  200. package/dist/src/channels/wechat-channel/core/schema.js +0 -1
  201. package/dist/src/channels/wechat-channel/core/trace-comparator.d.ts +0 -28
  202. package/dist/src/channels/wechat-channel/core/trace-comparator.js +0 -1
  203. package/dist/src/channels/wechat-channel/fingerprint.d.ts +0 -31
  204. package/dist/src/channels/wechat-channel/fingerprint.js +0 -1
  205. package/dist/src/channels/wechat-channel/helper-assets.d.ts +0 -103
  206. package/dist/src/channels/wechat-channel/helper-assets.js +0 -1
  207. package/dist/src/channels/wechat-channel/helper-client.d.ts +0 -87
  208. package/dist/src/channels/wechat-channel/helper-client.js +0 -3
  209. package/dist/src/channels/wechat-channel/helper-protocol.d.ts +0 -122
  210. package/dist/src/channels/wechat-channel/helper-protocol.js +0 -1
  211. package/dist/src/channels/wechat-channel/human-coordination.d.ts +0 -25
  212. package/dist/src/channels/wechat-channel/human-coordination.js +0 -1
  213. package/dist/src/channels/wechat-channel/index.d.ts +0 -21
  214. package/dist/src/channels/wechat-channel/index.js +0 -1
  215. package/dist/src/channels/wechat-channel/ledger.d.ts +0 -48
  216. package/dist/src/channels/wechat-channel/ledger.js +0 -2
  217. package/dist/src/channels/wechat-channel/media-cache-resolver.d.ts +0 -42
  218. package/dist/src/channels/wechat-channel/media-cache-resolver.js +0 -1
  219. package/dist/src/channels/wechat-channel/media-resolver.d.ts +0 -186
  220. package/dist/src/channels/wechat-channel/media-resolver.js +0 -1
  221. package/dist/src/channels/wechat-channel/message-key.d.ts +0 -19
  222. package/dist/src/channels/wechat-channel/message-key.js +0 -1
  223. package/dist/src/channels/wechat-channel/observer.d.ts +0 -171
  224. package/dist/src/channels/wechat-channel/observer.js +0 -7
  225. package/dist/src/channels/wechat-channel/outbound-ledger.d.ts +0 -96
  226. package/dist/src/channels/wechat-channel/outbound-ledger.js +0 -2
  227. package/dist/src/channels/wechat-channel/outbound-sender.d.ts +0 -74
  228. package/dist/src/channels/wechat-channel/outbound-sender.js +0 -1
  229. package/dist/src/channels/wechat-channel/pacing.d.ts +0 -3
  230. package/dist/src/channels/wechat-channel/pacing.js +0 -1
  231. package/dist/src/channels/wechat-channel/preflight.d.ts +0 -46
  232. package/dist/src/channels/wechat-channel/preflight.js +0 -1
  233. package/dist/src/channels/wechat-channel/runner.d.ts +0 -43
  234. package/dist/src/channels/wechat-channel/runner.js +0 -1
  235. package/dist/src/channels/wechat-channel/runtime.d.ts +0 -50
  236. package/dist/src/channels/wechat-channel/runtime.js +0 -1
  237. package/dist/src/channels/wechat-channel/scheduler.d.ts +0 -74
  238. package/dist/src/channels/wechat-channel/scheduler.js +0 -1
  239. package/dist/src/channels/wechat-channel/vector-store.d.ts +0 -44
  240. package/dist/src/channels/wechat-channel/vector-store.js +0 -1
  241. package/dist/src/channels/wechat-rpa/macos-flow.d.ts +0 -55
  242. package/dist/src/channels/wechat-rpa/macos-flow.js +0 -1
  243. package/dist/src/channels/wechat-rpa/macos.d.ts +0 -11
  244. package/dist/src/channels/wechat-rpa/macos.js +0 -21
  245. package/dist/src/channels/wechat-rpa/normalizer.d.ts +0 -49
  246. package/dist/src/channels/wechat-rpa/normalizer.js +0 -7
  247. package/dist/src/channels/wechat-rpa/product-channel.d.ts +0 -76
  248. package/dist/src/channels/wechat-rpa/product-channel.js +0 -16
  249. package/dist/src/channels/wechat-rpa-session-sync.d.ts +0 -26
  250. package/dist/src/channels/wechat-rpa-session-sync.js +0 -1
  251. package/dist/src/channels/wechat-rpa.d.ts +0 -104
  252. package/dist/src/channels/wechat-rpa.js +0 -6
  253. package/dist/src/channels/wecom.d.ts +0 -51
  254. package/dist/src/channels/wecom.js +0 -4
  255. package/dist/src/commands/external-attachments.d.ts +0 -9
  256. package/dist/src/commands/external-attachments.js +0 -1
  257. package/dist/src/commands/external.d.ts +0 -2
  258. package/dist/src/commands/external.js +0 -1
  259. package/dist/src/commands/helper-runtime-official-download.d.ts +0 -80
  260. package/dist/src/commands/helper-runtime-official-download.js +0 -1
  261. package/dist/src/commands/runtime.d.ts +0 -120
  262. package/dist/src/commands/runtime.js +0 -1
  263. package/dist/src/commands/wechat/command.d.ts +0 -3
  264. package/dist/src/commands/wechat/command.js +0 -1
  265. package/dist/src/commands/wechat/direct.d.ts +0 -14
  266. package/dist/src/commands/wechat/direct.js +0 -2
  267. package/dist/src/commands/wechat/doctor.d.ts +0 -2
  268. package/dist/src/commands/wechat/doctor.js +0 -1
  269. package/dist/src/commands/wechat/ipc.d.ts +0 -9
  270. package/dist/src/commands/wechat/ipc.js +0 -1
  271. package/dist/src/commands/wechat/operations.d.ts +0 -3
  272. package/dist/src/commands/wechat/operations.js +0 -4
  273. package/dist/src/commands/wechat/output.d.ts +0 -7
  274. package/dist/src/commands/wechat/output.js +0 -4
  275. package/dist/src/commands/wechat/types.d.ts +0 -142
  276. package/dist/src/commands/wechat/types.js +0 -1
  277. package/dist/src/commands/wechat/utils.d.ts +0 -16
  278. package/dist/src/commands/wechat/utils.js +0 -1
  279. package/dist/src/commands/wechat.d.ts +0 -6
  280. package/dist/src/commands/wechat.js +0 -1
  281. package/dist/src/devtools/wechat-channel-action-smoke.d.ts +0 -105
  282. package/dist/src/devtools/wechat-channel-action-smoke.js +0 -10
  283. package/dist/src/devtools/wechat-channel-download-report.d.ts +0 -111
  284. package/dist/src/devtools/wechat-channel-download-report.js +0 -2
  285. package/dist/src/devtools/wechat-channel-markdown-report.d.ts +0 -6
  286. package/dist/src/devtools/wechat-channel-markdown-report.js +0 -5
  287. package/dist/src/devtools/wechat-channel-product-steps.d.ts +0 -9
  288. package/dist/src/devtools/wechat-channel-product-steps.js +0 -40
  289. package/dist/src/session/handlers/skills.d.ts +0 -7
  290. package/dist/src/session/handlers/skills.js +0 -1
  291. package/dist/src/session/handlers/wechat-helper-runtime.d.ts +0 -33
  292. package/dist/src/session/handlers/wechat-helper-runtime.js +0 -1
  293. package/dist/src/skills/registry.d.ts +0 -16
  294. package/dist/src/skills/registry.js +0 -15
  295. package/dist/src/skills/setup.d.ts +0 -11
  296. package/dist/src/skills/setup.js +0 -1
@@ -0,0 +1,210 @@
1
+ import type { RoomActivationMode, RoomMessageView } from '@shennian/wire';
2
+ type FetchLike = typeof fetch;
3
+ type BindingTarget = {
4
+ machineToken: string;
5
+ roomId: string;
6
+ bindingId: string;
7
+ };
8
+ export type RemoteRoomAgentStatus = {
9
+ bindingId: string;
10
+ roomId: string;
11
+ agentIdentityId: string;
12
+ agentMembershipId: string | null;
13
+ status: 'pending_approval' | 'active' | 'paused' | 'detached';
14
+ activationMode: RoomActivationMode;
15
+ lastDeliveredSequence: string;
16
+ lastActivatedSequence: string;
17
+ room: {
18
+ name: string;
19
+ status: string;
20
+ lastSequence: string;
21
+ };
22
+ };
23
+ export type RemoteRoomJoinResult = {
24
+ outcome: 'joined' | 'pending_approval';
25
+ room: {
26
+ id: string;
27
+ name: string;
28
+ webUrl: string;
29
+ };
30
+ agentIdentity: {
31
+ id: string;
32
+ displayName: string;
33
+ };
34
+ binding: {
35
+ id: string;
36
+ roomId: string;
37
+ agentIdentityId: string;
38
+ agentMembershipId: string | null;
39
+ status: 'pending_approval' | 'active' | 'paused';
40
+ activationMode: RoomActivationMode;
41
+ };
42
+ joinRequestId?: string;
43
+ idempotent: boolean;
44
+ };
45
+ export type RemoteRoomAttachmentCapability = {
46
+ attachmentId: string;
47
+ roomId: string;
48
+ safeName: string;
49
+ mimeType: string;
50
+ byteSize: string;
51
+ sha256: string;
52
+ downloadUrl: string;
53
+ expiresAt: string;
54
+ };
55
+ export type RemoteRoomAttachmentUpload = {
56
+ attachment: {
57
+ id: string;
58
+ roomId: string;
59
+ name: string;
60
+ mimeType: string;
61
+ byteSize: string;
62
+ sha256: string;
63
+ status: 'pending' | 'uploaded';
64
+ };
65
+ upload: {
66
+ method: 'PUT';
67
+ url: string;
68
+ headers: Record<string, string>;
69
+ expiresAt: string;
70
+ };
71
+ idempotent: boolean;
72
+ };
73
+ export type RemoteRoomCreateResult = {
74
+ outcome: 'created';
75
+ room: {
76
+ id: string;
77
+ name: string;
78
+ visibility: 'private';
79
+ joinPolicy: 'approval';
80
+ webUrl: string;
81
+ inviteUrl: string;
82
+ };
83
+ agentIdentity: {
84
+ id: string;
85
+ displayName: string;
86
+ };
87
+ binding: {
88
+ id: string;
89
+ roomId: string;
90
+ agentIdentityId: string;
91
+ agentMembershipId: string;
92
+ status: 'active' | 'paused';
93
+ activationMode: RoomActivationMode;
94
+ };
95
+ idempotent: boolean;
96
+ };
97
+ export type RemoteOperatorRoomCreateResult = {
98
+ outcome: 'created';
99
+ room: {
100
+ id: string;
101
+ name: string;
102
+ visibility: 'private';
103
+ joinPolicy: 'approval';
104
+ webUrl: string;
105
+ };
106
+ };
107
+ export declare class RoomActionHttpError extends Error {
108
+ readonly code: string;
109
+ constructor(code: string, message: string);
110
+ }
111
+ export declare class HttpRoomActionClient {
112
+ private readonly fetchImpl;
113
+ private readonly baseUrl;
114
+ constructor(serverUrl: string, fetchImpl?: FetchLike);
115
+ createOperatorRoom(input: {
116
+ machineToken: string;
117
+ name: string;
118
+ }): Promise<RemoteOperatorRoomCreateResult>;
119
+ resolveNativeSession(input: {
120
+ machineToken: string;
121
+ runtimeAdapter: 'codex' | 'claude_code' | 'workbuddy';
122
+ sourceSessionKey: string;
123
+ }): Promise<{
124
+ nianSessionId: string;
125
+ created: boolean;
126
+ }>;
127
+ createRoom(input: {
128
+ machineToken: string;
129
+ bindingIntentId: string;
130
+ nianSessionId: string;
131
+ runtimeAdapter: 'codex' | 'claude_code' | 'workbuddy';
132
+ sessionRegistryKey: string;
133
+ name: string;
134
+ agentAlias: string;
135
+ agentDescription?: string;
136
+ activationCapability: 'managed' | 'idle_resume' | 'notify_only';
137
+ activationMode: RoomActivationMode;
138
+ }): Promise<RemoteRoomCreateResult>;
139
+ joinRoom(input: {
140
+ machineToken: string;
141
+ bindingIntentId: string;
142
+ nianSessionId: string;
143
+ runtimeAdapter: 'codex' | 'claude_code' | 'workbuddy';
144
+ sessionRegistryKey: string;
145
+ inviteToken: string;
146
+ message?: string;
147
+ agentAlias: string;
148
+ agentDescription?: string;
149
+ activationCapability: 'managed' | 'idle_resume' | 'notify_only';
150
+ activationMode: RoomActivationMode;
151
+ }): Promise<RemoteRoomJoinResult>;
152
+ status(target: BindingTarget): Promise<RemoteRoomAgentStatus>;
153
+ getAttachmentCapability(target: BindingTarget & {
154
+ attachmentId: string;
155
+ }): Promise<RemoteRoomAttachmentCapability>;
156
+ initializeAttachment(target: BindingTarget & {
157
+ clientUploadId: string;
158
+ name: string;
159
+ mediaKind: 'image' | 'audio' | 'file';
160
+ mimeType: string;
161
+ byteSize: number;
162
+ sha256: string;
163
+ }): Promise<RemoteRoomAttachmentUpload>;
164
+ completeAttachment(target: BindingTarget & {
165
+ attachmentId: string;
166
+ }): Promise<{
167
+ attachment: {
168
+ attachmentId: string;
169
+ mediaKind: 'image' | 'audio' | 'file';
170
+ name: string;
171
+ mimeType: string;
172
+ byteSize: string;
173
+ sha256: string;
174
+ status: 'ready';
175
+ };
176
+ idempotent: boolean;
177
+ }>;
178
+ read(target: BindingTarget & {
179
+ afterSequence: string;
180
+ limit: number;
181
+ }): Promise<{
182
+ messages: RoomMessageView[];
183
+ nextSequence: string;
184
+ hasMore: boolean;
185
+ historyFromSequence: string;
186
+ roomName: string;
187
+ }>;
188
+ send(target: BindingTarget & {
189
+ clientMessageId: string;
190
+ text: string;
191
+ mentionMembershipIds?: string[];
192
+ replyToMessageId?: string;
193
+ activationCauseMessageId?: string;
194
+ attachmentIds?: string[];
195
+ }): Promise<{
196
+ message: RoomMessageView;
197
+ idempotent: boolean;
198
+ }>;
199
+ mode(target: BindingTarget & {
200
+ activationMode: RoomActivationMode;
201
+ }): Promise<{
202
+ bindingId: string;
203
+ roomId: string;
204
+ status: 'active' | 'paused';
205
+ activationMode: RoomActivationMode;
206
+ idempotent: boolean;
207
+ }>;
208
+ private request;
209
+ }
210
+ export {};
@@ -0,0 +1 @@
1
+ class c extends Error{code;constructor(e,o){super(o),this.code=e,this.name="RoomActionHttpError"}}class j{fetchImpl;baseUrl;constructor(e,o=fetch){this.fetchImpl=o,this.baseUrl=e.replace(/\/+$/,"")}async createOperatorRoom(e){let o;try{o=await this.fetchImpl(`${this.baseUrl}/api/rooms/actions/operator/create`,{method:"POST",headers:{authorization:`Bearer ${e.machineToken}`,"content-type":"application/json"},body:JSON.stringify({name:e.name}),signal:AbortSignal.timeout(1e4)})}catch{throw new c("room_action_server_unavailable","Shennian Room service is unavailable.")}const t=await m(o);if(!o.ok)throw p(o.status,t);return I(t,e,this.baseUrl)}async resolveNativeSession(e){let o;try{o=await this.fetchImpl(`${this.baseUrl}/api/rooms/actions/resolve-session`,{method:"POST",headers:{authorization:`Bearer ${e.machineToken}`,"content-type":"application/json"},body:JSON.stringify({runtimeAdapter:e.runtimeAdapter,sourceSessionKey:e.sourceSessionKey}),signal:AbortSignal.timeout(1e4)})}catch{throw new c("room_action_server_unavailable","Shennian Room service is unavailable.")}const t=await m(o);if(!o.ok)throw p(o.status,t);(!t||typeof t!="object")&&a();const n=t;return(typeof n.nianSessionId!="string"||!/^[A-Za-z0-9_.:@/-]{1,512}$/.test(n.nianSessionId)||typeof n.created!="boolean")&&a(),n}async createRoom(e){let o;try{o=await this.fetchImpl(`${this.baseUrl}/api/rooms/actions/create`,{method:"POST",headers:{authorization:`Bearer ${e.machineToken}`,"content-type":"application/json"},body:JSON.stringify({bindingIntentId:e.bindingIntentId,nianSessionId:e.nianSessionId,runtimeAdapter:e.runtimeAdapter,sessionRegistryKey:e.sessionRegistryKey,name:e.name,agentAlias:e.agentAlias,agentDescription:e.agentDescription,activationCapability:e.activationCapability,activationMode:e.activationMode}),signal:AbortSignal.timeout(1e4)})}catch{throw new c("room_action_server_unavailable","Shennian Room service is unavailable.")}const t=await m(o);if(!o.ok)throw p(o.status,t);return g(t,e,this.baseUrl)}async joinRoom(e){let o;try{o=await this.fetchImpl(`${this.baseUrl}/api/rooms/actions/join`,{method:"POST",headers:{authorization:`Bearer ${e.machineToken}`,"content-type":"application/json"},body:JSON.stringify({bindingIntentId:e.bindingIntentId,nianSessionId:e.nianSessionId,runtimeAdapter:e.runtimeAdapter,sessionRegistryKey:e.sessionRegistryKey,inviteToken:e.inviteToken,message:e.message,agentAlias:e.agentAlias,agentDescription:e.agentDescription,activationCapability:e.activationCapability,activationMode:e.activationMode}),signal:AbortSignal.timeout(1e4)})}catch{throw new c("room_action_server_unavailable","Shennian Room service is unavailable.")}const t=await m(o);if(!o.ok)throw p(o.status,t);return v(t,e,this.baseUrl)}async status(e){const o=await this.request(e,"status");return b(o,e)}async getAttachmentCapability(e){const o=await this.request(e,`attachments/${encodeURIComponent(e.attachmentId)}/download-capability`);(!o||typeof o!="object")&&a();const t=o;return(t.attachmentId!==e.attachmentId||t.roomId!==e.roomId||typeof t.safeName!="string"||!S(t.safeName)||typeof t.mimeType!="string"||t.mimeType.length<1||t.mimeType.length>191||typeof t.byteSize!="string"||!d(t.byteSize)||typeof t.sha256!="string"||!/^[a-f0-9]{64}$/.test(t.sha256)||!h(t.downloadUrl)||typeof t.expiresAt!="string"||!Number.isFinite(Date.parse(t.expiresAt)))&&a(),t}async initializeAttachment(e){const o=await this.request(e,"attachments/init",{method:"POST",body:JSON.stringify({clientUploadId:e.clientUploadId,name:e.name,mediaKind:e.mediaKind,mimeType:e.mimeType,byteSize:e.byteSize,sha256:e.sha256})});(!o||typeof o!="object")&&a();const t=o,n=t.attachment,r=t.upload;return(!n||typeof n.id!="string"||n.roomId!==e.roomId||n.name.length<1||n.mimeType!==e.mimeType||n.byteSize!==String(e.byteSize)||n.sha256!==e.sha256||n.status!=="pending"&&n.status!=="uploaded"||!r||r.method!=="PUT"||!h(r.url)||!r.headers||r.headers["content-type"]!==e.mimeType||r.headers["x-oss-meta-sha256"]!==e.sha256||typeof r.expiresAt!="string"||Date.parse(r.expiresAt)<=Date.now()||typeof t.idempotent!="boolean")&&a(),t}async completeAttachment(e){const o=await this.request(e,`attachments/${encodeURIComponent(e.attachmentId)}/complete`,{method:"POST"});(!o||typeof o!="object")&&a();const t=o;typeof t.idempotent!="boolean"&&a();const n=t.attachment;return(!n||n.attachmentId!==e.attachmentId||n.mediaKind!=="image"&&n.mediaKind!=="audio"&&n.mediaKind!=="file"||typeof n.name!="string"||typeof n.mimeType!="string"||typeof n.byteSize!="string"||!d(n.byteSize)||typeof n.sha256!="string"||!/^[a-f0-9]{64}$/.test(n.sha256)||n.status!=="ready")&&a(),{attachment:n,idempotent:t.idempotent}}async read(e){const o=new URLSearchParams({after:e.afterSequence,limit:String(e.limit)}),t=await this.request(e,`messages?${o}`);(!t||typeof t!="object")&&a();const n=t;return(!Array.isArray(n.messages)||typeof n.nextSequence!="string"||!d(n.nextSequence)||typeof n.hasMore!="boolean"||typeof n.historyFromSequence!="string"||!d(n.historyFromSequence)||typeof n.roomName!="string")&&a(),{messages:n.messages.map(s=>l(s,e.roomId)),nextSequence:n.nextSequence,hasMore:n.hasMore,historyFromSequence:n.historyFromSequence,roomName:n.roomName}}async send(e){const o=await this.request(e,"messages",{method:"POST",body:JSON.stringify({clientMessageId:e.clientMessageId,text:e.text,mentionMembershipIds:e.mentionMembershipIds,replyToMessageId:e.replyToMessageId,activationCauseMessageId:e.activationCauseMessageId,attachmentIds:e.attachmentIds})});(!o||typeof o!="object")&&a();const t=o;return typeof t.idempotent!="boolean"&&a(),{message:l(t.message,e.roomId),idempotent:t.idempotent}}async mode(e){const o=await this.request(e,"mode",{method:"PATCH",body:JSON.stringify({activationMode:e.activationMode})});(!o||typeof o!="object")&&a();const t=o;return(t.bindingId!==e.bindingId||t.roomId!==e.roomId||t.status!=="active"&&t.status!=="paused"||!f(t.activationMode)||t.activationMode!==e.activationMode||typeof t.idempotent!="boolean")&&a(),t}async request(e,o,t={}){let n;try{n=await this.fetchImpl(`${this.baseUrl}/api/rooms/${encodeURIComponent(e.roomId)}/bindings/${encodeURIComponent(e.bindingId)}/${o}`,{...t,headers:{authorization:`Bearer ${e.machineToken}`,...t.body?{"content-type":"application/json"}:{}},signal:AbortSignal.timeout(1e4)})}catch{throw new c("room_action_server_unavailable","Shennian Room service is unavailable.")}if(!n.ok){let r=`room_action_http_${n.status}`;try{const s=await n.json();typeof s.error=="string"&&/^[a-z0-9_-]{1,80}$/.test(s.error)&&(r=s.error)}catch{}throw new c(r,"Shennian Room action failed.")}try{return await n.json()}catch{a()}}}function b(i,e){(!i||typeof i!="object")&&a();const o=i,t=o.room;(o.bindingId!==e.bindingId||o.roomId!==e.roomId||typeof o.agentIdentityId!="string"||o.agentMembershipId!==null&&typeof o.agentMembershipId!="string"||o.status!=="pending_approval"&&o.status!=="active"&&o.status!=="paused"&&o.status!=="detached"||!f(o.activationMode)||typeof o.lastDeliveredSequence!="string"||!d(o.lastDeliveredSequence)||typeof o.lastActivatedSequence!="string"||!d(o.lastActivatedSequence)||!t||typeof t!="object")&&a(),(o.status==="pending_approval"&&o.agentMembershipId!==null||(o.status==="active"||o.status==="paused")&&typeof o.agentMembershipId!="string")&&a();const n=t;return(typeof n.name!="string"||typeof n.status!="string"||typeof n.lastSequence!="string"||!d(n.lastSequence))&&a(),o}function l(i,e){(!i||typeof i!="object")&&a();const o=i;if((typeof o.id!="string"||o.roomId!==e||typeof o.sequence!="string"||!d(o.sequence)||!o.author||typeof o.author.membershipId!="string"||!Array.isArray(o.attachments)||typeof o.createdAt!="string")&&a(),o.activationCause!=null){const t=o.activationCause;(typeof t.causeMessageId!="string"||typeof t.rootCauseMessageId!="string"||!Number.isInteger(t.hopCount)||t.hopCount<1||t.hopCount>32)&&a()}return o}function g(i,e,o){(!i||typeof i!="object")&&a();const t=i,n=t.room,r=t.agentIdentity,s=t.binding;return(t.outcome!=="created"||!n||typeof n.id!="string"||n.name!==e.name||n.visibility!=="private"||n.joinPolicy!=="approval"||!y(n.webUrl,o,`/spaces/${n.id}`)||!w(n.inviteUrl,o)||!r||typeof r.id!="string"||r.displayName!==e.agentAlias||!s||typeof s.id!="string"||s.roomId!==n.id||s.agentIdentityId!==r.id||typeof s.agentMembershipId!="string"||s.status!=="active"&&s.status!=="paused"||s.activationMode!==e.activationMode||typeof t.idempotent!="boolean")&&a(),t}function I(i,e,o){(!i||typeof i!="object")&&a();const t=i,n=t.room;return(t.outcome!=="created"||!n||typeof n.id!="string"||n.name!==e.name||n.visibility!=="private"||n.joinPolicy!=="approval"||!y(n.webUrl,o,`/spaces/${n.id}`))&&a(),t}function v(i,e,o){(!i||typeof i!="object")&&a();const t=i,n=t.room,r=t.agentIdentity,s=t.binding;return(t.outcome!=="joined"&&t.outcome!=="pending_approval"||!n||typeof n.id!="string"||typeof n.name!="string"||!y(n.webUrl,o,`/spaces/${n.id}`)||!r||typeof r.id!="string"||r.displayName!==e.agentAlias||!s||typeof s.id!="string"||s.roomId!==n.id||s.agentIdentityId!==r.id||s.activationMode!==e.activationMode||typeof t.idempotent!="boolean")&&a(),(t.outcome==="pending_approval"&&(s.status!=="pending_approval"||s.agentMembershipId!==null||typeof t.joinRequestId!="string")||t.outcome==="joined"&&(s.status!=="active"&&s.status!=="paused"||typeof s.agentMembershipId!="string"||t.joinRequestId!==void 0))&&a(),t}function d(i){return/^(0|[1-9]\d{0,19})$/.test(i)}function S(i){return i.length<=255&&i===i.trim()&&i!=="."&&i!==".."&&!/[\\/\0-\x1f\x7f]/.test(i)}function h(i){if(typeof i!="string")return!1;try{const e=new URL(i);return e.protocol==="https:"&&!e.username&&!e.password&&!e.hash}catch{return!1}}function f(i){return i==="auto_follow"||i==="mention_only"||i==="paused"}function a(){throw new c("invalid_room_action_response","Invalid Room action response.")}async function m(i){try{return await i.json()}catch{a()}}function p(i,e){let o=`room_action_http_${i}`;if(e&&typeof e=="object"){const t=e.error;typeof t=="string"&&/^[a-z0-9_-]{1,80}$/.test(t)&&(o=t)}return new c(o,"Shennian Room action failed.")}function y(i,e,o){return u(i,e,t=>t.pathname===o)}function w(i,e){return u(i,e,o=>/^\/spaces\/join\/[A-Za-z0-9_-]{43}$/.test(o.pathname))}function u(i,e,o){if(typeof i!="string")return!1;try{const t=new URL(i),n=new URL(e);return t.hostname===n.hostname&&(t.protocol==="https:"||n.protocol==="http:"&&t.protocol==="http:")&&!t.username&&!t.password&&!t.search&&!t.hash&&o(t)}catch{return!1}}export{j as HttpRoomActionClient,c as RoomActionHttpError};
@@ -0,0 +1,33 @@
1
+ import type { DaemonIpcHandler } from '../daemon-ipc/server.js';
2
+ import { RoomInvocationTokenAuthority } from './invocation-token.js';
3
+ import type { RoomAttachmentCache } from './attachment-cache.js';
4
+ import { HttpRoomActionClient } from './room-action-client.js';
5
+ import { RoomSessionRegistry } from './session-registry.js';
6
+ import type { RoomAttachmentUploader } from './attachment-upload.js';
7
+ import type { RoomHostSessionObservationStore } from './host-session-observations.js';
8
+ type RoomMirrorWriter = {
9
+ append(input: {
10
+ messages: Awaited<ReturnType<HttpRoomActionClient['read']>>['messages'];
11
+ historyFromSequence: string;
12
+ roomName?: string | null;
13
+ }): unknown;
14
+ markAccessRevoked(): void;
15
+ search(input: {
16
+ query: string;
17
+ limit?: number;
18
+ }): unknown;
19
+ };
20
+ export declare function createRoomActionIpcHandlers(input: {
21
+ authority: RoomInvocationTokenAuthority;
22
+ registry: RoomSessionRegistry;
23
+ actionClient: Pick<HttpRoomActionClient, 'createRoom' | 'joinRoom' | 'read' | 'send' | 'mode' | 'status'>;
24
+ getMachineCredentials: () => {
25
+ machineId?: string;
26
+ machineToken?: string;
27
+ };
28
+ mirrorFactory?: (roomId: string) => RoomMirrorWriter;
29
+ attachmentCache?: Pick<RoomAttachmentCache, 'materialize'>;
30
+ attachmentUploader?: Pick<RoomAttachmentUploader, 'upload'>;
31
+ observations?: RoomHostSessionObservationStore;
32
+ }): Record<string, DaemonIpcHandler>;
33
+ export {};
@@ -0,0 +1 @@
1
+ import{DaemonIpcError as a}from"../daemon-ipc/protocol.js";import{normalizeRoomActionParameters as R}from"./room-action-schema.js";function B(e){return{"room-action.execute":async o=>{const h=T(o),l=e.getMachineCredentials();if(!l.machineId?.trim()||!l.machineToken?.trim())throw new a("machine_not_authorized","This machine is not authorized yet.");const s=w(h.registryKey,"registryKey",64),A=w(h.invocationToken,"invocationToken",128),d=S(h.action),u=R(d,h.actionParameters);return e.registry.runExclusive(s,async()=>{let y;try{y=e.authority.consume({invocationToken:A,machineId:l.machineId,machineCredential:l.machineToken,registryKey:s,action:d,targetTool:"shennian_room",actionParameters:u})}catch(t){throw C(t)}const r=e.registry.getByRegistryKey(s),i=r?.binding;if(!r)throw new a("room_session_unavailable","The exact Agent session is unavailable.");if(d==="room.create"){if(i&&i.status!=="detached")throw new a("session_already_bound","This Agent session is already bound to a Room.");if(!y.bindingIntentId)throw new a("binding_intent_missing","Room create intent is missing.");const t=u,n=await e.actionClient.createRoom({machineToken:l.machineToken,bindingIntentId:y.bindingIntentId,nianSessionId:r.nianSessionId,runtimeAdapter:r.runtimeAdapter,sessionRegistryKey:s,name:t.name,agentAlias:t.agentAlias,agentDescription:t.agentDescription,activationCapability:_(r.capabilities),activationMode:t.activationMode});return e.registry.setBinding({registryKey:s,bindingId:n.binding.id,roomId:n.room.id,agentIdentityId:n.binding.agentIdentityId,agentMembershipId:n.binding.agentMembershipId,status:n.binding.status,activationMode:n.binding.activationMode}),g(n)}if(d==="room.join"){if(i&&i.status!=="detached")throw new a("session_already_bound","This Agent session is already bound to a Room.");if(!y.bindingIntentId)throw new a("binding_intent_missing","Room join intent is missing.");const t=u,n=await e.actionClient.joinRoom({machineToken:l.machineToken,bindingIntentId:y.bindingIntentId,nianSessionId:r.nianSessionId,runtimeAdapter:r.runtimeAdapter,sessionRegistryKey:s,inviteToken:k(t.inviteUrl),message:t.message,agentAlias:t.agentAlias,agentDescription:t.agentDescription,activationCapability:_(r.capabilities),activationMode:t.activationMode});return e.registry.setBinding({registryKey:s,bindingId:n.binding.id,roomId:n.room.id,agentIdentityId:n.binding.agentIdentityId,agentMembershipId:n.binding.agentMembershipId,status:n.binding.status,activationMode:n.binding.activationMode}),g(n)}if(!i)throw new a("room_not_bound","This Agent session is not bound to a Room.");if(d!=="room.status"&&(i.status==="detached"||i.status==="pending_approval"))throw new a(i.status==="pending_approval"?"room_binding_pending_approval":"room_binding_detached",i.status==="pending_approval"?"This Agent is waiting for Room approval.":"This Agent Room binding must be repaired before use.");const I={machineToken:l.machineToken,roomId:i.roomId,bindingId:i.bindingId};if(d==="room.status"){const t=await e.actionClient.status(I);e.registry.setBinding({registryKey:s,bindingId:t.bindingId,roomId:t.roomId,agentIdentityId:t.agentIdentityId,agentMembershipId:t.agentMembershipId,status:t.status,activationMode:t.activationMode}),t.status==="detached"&&e.mirrorFactory?.(t.roomId).markAccessRevoked();const n=e.registry.getByRegistryKey(s),c=n?.binding,m=e.observations?.getBySource(r);return g({...t,hostSession:{state:n?.lifecycleState??"closed",lastReportedEvent:m?.lastEventName??null,lastActiveAt:m?.lastActiveAt??null,lastIdleAt:m?.lastIdleAt??null,closedAt:m?.closedAt??null,closedConfirmed:m?.lifecycleState==="closed",activationCapability:_(r.capabilities)},localActivation:{queued:c?.activationQueue.length??0,inFlight:!!c?.activeActivation,pausedUntil:c?.loopBreaker.pausedUntil??null}})}if(d==="room.read"){const t=u;if(t.query){const b=e.mirrorFactory?.(i.roomId);if(!b)throw new a("room_mirror_unavailable","Room mirror is unavailable.");return g({source:"local_mirror",matches:b.search({query:t.query,limit:t.limit})})}const n=await e.actionClient.read({...I,afterSequence:t.afterSequence,limit:t.limit});e.mirrorFactory?.(i.roomId).append({messages:n.messages,historyFromSequence:n.historyFromSequence,roomName:n.roomName});const c=BigInt(n.nextSequence)>BigInt(i.lastDeliveredSequence)?n.nextSequence:i.lastDeliveredSequence;e.registry.advanceCursor({registryKey:s,cursor:"delivered",sequence:c});const m=[];for(const b of t.attachmentIds??[]){const v=n.messages.flatMap(p=>p.attachments).find(p=>p.attachmentId===b);if(!v||v.status!=="ready")throw new a("attachment_not_in_read_result","Requested attachment is not available in this read result.");if(!e.attachmentCache)throw new a("attachment_cache_unavailable","Room attachment cache is unavailable.");m.push({attachmentId:b,...await e.attachmentCache.materialize({...I,attachmentId:b,expected:{downloadRef:v.downloadRef,byteSize:v.byteSize,sha256:v.sha256}})})}return g({...n,materializedAttachments:m})}if(d==="room.send"){const t=u;let n;if(t.files?.length){const c=e.observations?.getBySource(r);if(!c?.workspacePath||!e.attachmentUploader)throw new a("room_attachment_upload_unavailable","The exact Agent workspace is unavailable for attachment upload.");n=await e.attachmentUploader.upload({...I,workspacePath:c.workspacePath,clientMessageId:t.clientMessageId,files:t.files})}return g(await e.actionClient.send({...I,clientMessageId:t.clientMessageId,text:t.text,mentionMembershipIds:t.mentionMembershipIds,replyToMessageId:t.replyToMessageId,attachmentIds:n,activationCauseMessageId:i.activeActivation?.causeMessageId}))}const M=u,f=await e.actionClient.mode({...I,...M});return e.registry.setBinding({registryKey:s,bindingId:i.bindingId,roomId:i.roomId,agentIdentityId:i.agentIdentityId,agentMembershipId:i.agentMembershipId,status:f.status,activationMode:f.activationMode}),g(f)})}}}function T(e){if(!e||Array.isArray(e)||typeof e!="object")throw new a("invalid_params","Room action parameters are required.");return e}function w(e,o,h){if(typeof e!="string"||!e.trim()||e.length>h)throw new a("invalid_params",`${o} is invalid.`);return e.trim()}function S(e){const o=w(e,"action",64);if(o!=="room.read"&&o!=="room.create"&&o!=="room.join"&&o!=="room.send"&&o!=="room.mode"&&o!=="room.status")throw new a("room_action_not_implemented","Room action is not available yet.");return o}function k(e){const o=new URL(e).pathname.split("/").at(-1);if(!o||!/^[A-Za-z0-9_-]{43}$/.test(o))throw new a("invalid_params","inviteUrl is invalid.");return o}function _(e){return e.canResumeIdleSession||e.canActivateWhileHostOpen?"idle_resume":"notify_only"}function C(e){return e&&typeof e=="object"&&"code"in e&&typeof e.code=="string"&&"message"in e&&typeof e.message=="string"?new a(e.code,e.message):new a("room_invocation_failed","Room invocation failed.")}function g(e){return JSON.parse(JSON.stringify(e))}export{B as createRoomActionIpcHandlers};
@@ -0,0 +1,3 @@
1
+ import type { RoomInvocationAction } from './invocation-token.js';
2
+ import type { JsonValue } from './pending-authorized-actions.js';
3
+ export declare function normalizeRoomActionParameters(action: Exclude<RoomInvocationAction, 'room.bind'>, value: JsonValue | undefined): JsonValue;
@@ -0,0 +1 @@
1
+ import{DaemonIpcError as l}from"../daemon-ipc/protocol.js";function I(t,n){const e=h(n);switch(t){case"room.status":return d(e,[]),{};case"room.read":{d(e,["afterSequence","limit","attachmentIds","query"]);const s=o(e.query,"query",200),i=e.afterSequence??"0",a=e.limit??50;(typeof i!="string"||!/^(0|[1-9]\d{0,19})$/.test(i))&&r("afterSequence"),(typeof a!="number"||!Number.isInteger(a)||a<1||a>100)&&r("limit");const m=g(e.attachmentIds,"attachmentIds",10);return s&&(e.afterSequence!=null||m)&&r("parameters"),s?{query:s,limit:a}:{afterSequence:i,limit:a,...m?{attachmentIds:m}:{}}}case"room.send":{d(e,["clientMessageId","text","files","mentionMembershipIds","replyToMessageId"]);const s=c(e.clientMessageId,"clientMessageId",191),i=o(e.text,"text",2e4),a=u(e.files,"files",10);!i&&!a&&r("parameters");const m=g(e.mentionMembershipIds,"mentionMembershipIds",20),p=o(e.replyToMessageId,"replyToMessageId",191);return{clientMessageId:s,text:i??"",...a?{files:a}:{},...m?{mentionMembershipIds:m}:{},...p?{replyToMessageId:p}:{}}}case"room.mode":return d(e,["activationMode"]),{activationMode:f(e.activationMode)};case"room.create":return d(e,["name","agentAlias","agentDescription","activationMode"]),{name:c(e.name,"name",80),agentAlias:c(e.agentAlias,"agentAlias",80),...o(e.agentDescription,"agentDescription",1e3)?{agentDescription:o(e.agentDescription,"agentDescription",1e3)}:{},activationMode:f(e.activationMode??"auto_follow")};case"room.join":return d(e,["inviteUrl","agentAlias","agentDescription","message","activationMode"]),{inviteUrl:y(e.inviteUrl),agentAlias:c(e.agentAlias,"agentAlias",80),...o(e.agentDescription,"agentDescription",1e3)?{agentDescription:o(e.agentDescription,"agentDescription",1e3)}:{},activationMode:f(e.activationMode??"auto_follow"),...o(e.message,"message",500)?{message:o(e.message,"message",500)}:{}}}}function u(t,n,e){return t==null?void 0:((!Array.isArray(t)||t.length<1||t.length>e)&&r(n),t.map(i=>((typeof i!="string"||!i.trim()||i.length>4096||/[\0\r\n]/.test(i))&&r(n),i.trim())))}function h(t){return t==null?{}:((Array.isArray(t)||typeof t!="object")&&r("parameters"),t)}function d(t,n){const e=new Set(n);Object.keys(t).some(s=>!e.has(s))&&r("parameters")}function c(t,n,e){return(typeof t!="string"||!t.trim()||t.length>e||/[\0\r]/.test(t))&&r(n),t.trim()}function o(t,n,e){if(!(t==null||t===""))return c(t,n,e)}function g(t,n,e){if(t==null)return;(!Array.isArray(t)||t.length>e)&&r(n);const s=t.map(i=>c(i,n,191));return new Set(s).size!==s.length&&r(n),s.length>0?s:void 0}function f(t){if(t==="auto"||t==="auto_follow")return"auto_follow";if(t==="mention"||t==="mention_only")return"mention_only";if(t==="paused")return"paused";r("activationMode")}function y(t){const n=c(t,"inviteUrl",2048);let e;try{e=new URL(n)}catch{r("inviteUrl")}return(e.protocol!=="https:"||e.hostname!=="shennian.net"||!/^\/spaces\/join\/[A-Za-z0-9_-]{43}$/.test(e.pathname)||e.username||e.password||e.search||e.hash)&&r("inviteUrl"),e.toString()}function r(t){throw new l("invalid_params",`${t} is invalid.`)}export{I as normalizeRoomActionParameters};
@@ -0,0 +1,23 @@
1
+ import type { AuthorizedActionExecutor } from './authorized-action-coordinator.js';
2
+ import type { RoomHostSessionObservationStore } from './host-session-observations.js';
3
+ import type { HttpRoomBindingClient } from './room-binding-client.js';
4
+ import type { JsonValue, PendingAuthorizedAction } from './pending-authorized-actions.js';
5
+ import type { RoomSessionRegistry } from './session-registry.js';
6
+ type Credentials = {
7
+ machineId: string;
8
+ machineToken: string;
9
+ };
10
+ export declare class RoomAuthorizedActionExecutor implements AuthorizedActionExecutor {
11
+ private readonly deps;
12
+ constructor(deps: {
13
+ registry: RoomSessionRegistry;
14
+ observations: RoomHostSessionObservationStore;
15
+ actionClient: Pick<HttpRoomBindingClient, 'resolveNativeSession' | 'createRoom' | 'joinRoom'>;
16
+ getMachineCredentials: () => {
17
+ machineId?: string;
18
+ machineToken?: string;
19
+ };
20
+ });
21
+ execute(action: PendingAuthorizedAction, claimed?: Credentials): Promise<JsonValue>;
22
+ }
23
+ export {};
@@ -0,0 +1 @@
1
+ import g from"node:path";import{normalizeRoomActionParameters as f}from"./room-action-schema.js";class H{deps;constructor(e){this.deps=e}async execute(e,m){const s=I(m??this.deps.getMachineCredentials()),n=A(e),r=this.deps.observations.observe({machineId:s.machineId,runtimeAdapter:n.runtimeAdapter,sourceSessionKey:n.sourceSessionKey,hostContextHash:n.hostContextHash,workspaceHash:n.workspaceHash,workspacePath:n.workspacePath,lifecycleState:"active",eventName:"PreToolUse"}),c=await this.deps.actionClient.resolveNativeSession({machineToken:s.machineToken,runtimeAdapter:n.runtimeAdapter,sourceSessionKey:n.sourceSessionKey}),i=!!r.workspacePath,p=this.deps.registry.register({machineId:s.machineId,runtimeAdapter:n.runtimeAdapter,sourceSessionKey:n.sourceSessionKey,nianSessionId:c.nianSessionId,hostProcessIdentity:`${n.runtimeAdapter}:${r.hostContextHash}:${r.registrationEpoch}`,capabilities:{canIdentifySession:!0,canBindAutomatically:!0,canOpenEmbeddedRoomPage:!1,canResumeIdleSession:i,canActivateWhileHostOpen:!1}}),y=i?"idle_resume":"notify_only",l=`${n.action.replace(".","-")}:${e.id}`,h={machineToken:s.machineToken,bindingIntentId:l,nianSessionId:c.nianSessionId,runtimeAdapter:n.runtimeAdapter,sessionRegistryKey:p.registryKey,agentAlias:n.parameters.agentAlias,agentDescription:n.parameters.agentDescription,activationCapability:y,activationMode:n.parameters.activationMode},o=n.action==="room.create"?await this.deps.actionClient.createRoom({...h,name:n.parameters.name}):await this.deps.actionClient.joinRoom({...h,inviteToken:w(n.parameters.inviteUrl),message:n.parameters.message});return this.deps.registry.setBinding({registryKey:p.registryKey,bindingId:o.binding.id,roomId:o.room.id,agentIdentityId:o.binding.agentIdentityId,agentMembershipId:o.binding.agentMembershipId,status:o.binding.status,activationMode:o.binding.activationMode}),JSON.parse(JSON.stringify(o))}}function A(t){const e=t.canonicalActionPayload;(!e||Array.isArray(e)||typeof e!="object"||e.schemaVersion!==1)&&a();const m=k(e.runtimeAdapter),s=d(e.sourceSessionKey,1024),n=u(e.hostContextHash),r=e.workspaceHash==null?null:u(e.workspaceHash),c=S(e.workspacePath);e.lifecycleState!=="active"&&a(),(e.action!==t.actionType||e.action!=="room.create"&&e.action!=="room.join")&&a();const i=f(e.action,e.actionParameters);return(!i||Array.isArray(i)||typeof i!="object")&&a(),{runtimeAdapter:m,sourceSessionKey:s,hostContextHash:n,workspaceHash:r,workspacePath:c,action:e.action,parameters:{name:typeof i.name=="string"?i.name:"",inviteUrl:typeof i.inviteUrl=="string"?i.inviteUrl:"",agentAlias:d(i.agentAlias,80),...typeof i.agentDescription=="string"?{agentDescription:i.agentDescription}:{},activationMode:b(i.activationMode),...typeof i.message=="string"?{message:i.message}:{}}}}function I(t){if(!t.machineId?.trim()||!t.machineToken?.trim())throw new Error("machine_credentials_unavailable");return{machineId:t.machineId.trim(),machineToken:t.machineToken.trim()}}function k(t){return t!=="codex"&&t!=="claude_code"&&t!=="workbuddy"&&a(),t}function b(t){return t!=="auto_follow"&&t!=="mention_only"&&t!=="paused"&&a(),t}function d(t,e){return(typeof t!="string"||!t.trim()||t.length>e||/[\0\r\n]/.test(t))&&a(),t.trim()}function u(t){const e=d(t,64);return/^[a-f0-9]{64}$/.test(e)||a(),e}function S(t){if(t==null||t==="")return null;const e=d(t,4096);return g.isAbsolute(e)||a(),e}function w(t){const e=/^https:\/\/shennian\.net\/spaces\/join\/([A-Za-z0-9_-]{43})$/.exec(t);return e||a(),e[1]}function a(){throw new Error("invalid_room_authorized_action")}export{H as RoomAuthorizedActionExecutor};
@@ -0,0 +1,51 @@
1
+ import type { RoomActivationCapability, RoomActivationMode, RoomBindingStatus, RoomRuntimeAdapter } from '@shennian/wire';
2
+ import { HttpRoomActionClient } from './room-action-client.js';
3
+ type FetchLike = typeof fetch;
4
+ export type CreateRemoteRoomBindingInput = {
5
+ machineToken: string;
6
+ bindingIntentId: string;
7
+ roomId: string;
8
+ agentIdentityId: string;
9
+ nianSessionId: string;
10
+ runtimeAdapter: RoomRuntimeAdapter;
11
+ sessionRegistryKey: string;
12
+ activationCapability: RoomActivationCapability;
13
+ activationMode: RoomActivationMode;
14
+ };
15
+ export type RemoteRoomBinding = {
16
+ id: string;
17
+ roomId: string;
18
+ agentIdentityId: string;
19
+ agentMembershipId: string;
20
+ nianSessionId: string;
21
+ runtimeAdapter: RoomRuntimeAdapter;
22
+ activationCapability: RoomActivationCapability;
23
+ activationMode: RoomActivationMode;
24
+ status: RoomBindingStatus;
25
+ };
26
+ export declare class RoomBindingHttpError extends Error {
27
+ readonly code: string;
28
+ constructor(code: string, message: string);
29
+ }
30
+ export declare class HttpRoomBindingClient {
31
+ private readonly fetchImpl;
32
+ private readonly baseUrl;
33
+ private readonly actionClient;
34
+ constructor(serverUrl: string, fetchImpl?: FetchLike);
35
+ status: HttpRoomActionClient['status'];
36
+ getAttachmentCapability: HttpRoomActionClient['getAttachmentCapability'];
37
+ initializeAttachment: HttpRoomActionClient['initializeAttachment'];
38
+ completeAttachment: HttpRoomActionClient['completeAttachment'];
39
+ createRoom: HttpRoomActionClient['createRoom'];
40
+ createOperatorRoom: HttpRoomActionClient['createOperatorRoom'];
41
+ resolveNativeSession: HttpRoomActionClient['resolveNativeSession'];
42
+ joinRoom: HttpRoomActionClient['joinRoom'];
43
+ read: HttpRoomActionClient['read'];
44
+ send: HttpRoomActionClient['send'];
45
+ mode: HttpRoomActionClient['mode'];
46
+ create(input: CreateRemoteRoomBindingInput): Promise<{
47
+ binding: RemoteRoomBinding;
48
+ idempotent: boolean;
49
+ }>;
50
+ }
51
+ export {};
@@ -0,0 +1 @@
1
+ import{HttpRoomActionClient as r}from"./room-action-client.js";class o extends Error{code;constructor(t,e){super(e),this.code=t,this.name="RoomBindingHttpError"}}class l{fetchImpl;baseUrl;actionClient;constructor(t,e=fetch){this.fetchImpl=e,this.baseUrl=t.replace(/\/+$/,""),this.actionClient=new r(t,e)}status=t=>this.actionClient.status(t);getAttachmentCapability=t=>this.actionClient.getAttachmentCapability(t);initializeAttachment=t=>this.actionClient.initializeAttachment(t);completeAttachment=t=>this.actionClient.completeAttachment(t);createRoom=t=>this.actionClient.createRoom(t);createOperatorRoom=t=>this.actionClient.createOperatorRoom(t);resolveNativeSession=t=>this.actionClient.resolveNativeSession(t);joinRoom=t=>this.actionClient.joinRoom(t);read=t=>this.actionClient.read(t);send=t=>this.actionClient.send(t);mode=t=>this.actionClient.mode(t);async create(t){let e;try{e=await this.fetchImpl(`${this.baseUrl}/api/rooms/${encodeURIComponent(t.roomId)}/bindings`,{method:"POST",headers:{authorization:`Bearer ${t.machineToken}`,"content-type":"application/json"},body:JSON.stringify({bindingIntentId:t.bindingIntentId,agentIdentityId:t.agentIdentityId,nianSessionId:t.nianSessionId,runtimeAdapter:t.runtimeAdapter,sessionRegistryKey:t.sessionRegistryKey,activationCapability:t.activationCapability,activationMode:t.activationMode}),signal:AbortSignal.timeout(1e4)})}catch{throw new o("room_binding_server_unavailable","Shennian Room service is unavailable.")}if(!e.ok){let s=`room_binding_http_${e.status}`;try{const a=await e.json();typeof a.error=="string"&&/^[a-z0-9_-]{1,80}$/.test(a.error)&&(s=a.error)}catch{}throw new o(s,"Shennian Room binding request failed.")}let i;try{i=await e.json()}catch{throw new o("invalid_room_binding_response","Invalid Room binding response.")}return d(i,t)}}function d(n,t){if(!n||typeof n!="object")throw new o("invalid_room_binding_response","Invalid Room binding response.");const e=n,i=e.binding;if(!i||typeof i.id!="string"||typeof i.agentMembershipId!="string"||i.roomId!==t.roomId||i.agentIdentityId!==t.agentIdentityId||i.nianSessionId!==t.nianSessionId||i.runtimeAdapter!==t.runtimeAdapter||i.activationCapability!==t.activationCapability||i.activationMode!==t.activationMode||!["pending_approval","active","paused","detached"].includes(i.status??"")||typeof e.idempotent!="boolean")throw new o("invalid_room_binding_response","Room binding response does not match the request.");return{binding:i,idempotent:e.idempotent}}export{l as HttpRoomBindingClient,o as RoomBindingHttpError};
@@ -0,0 +1,29 @@
1
+ import type { RoomActivationCapability, RoomActivationMode } from '@shennian/wire';
2
+ import type { DaemonIpcHandler } from '../daemon-ipc/server.js';
3
+ import type { JsonValue } from './pending-authorized-actions.js';
4
+ import { RoomInvocationTokenAuthority } from './invocation-token.js';
5
+ import { HttpRoomBindingClient } from './room-binding-client.js';
6
+ import { RoomSessionRegistry } from './session-registry.js';
7
+ import type { RoomJsonlMirror } from './jsonl-mirror.js';
8
+ import type { RoomAttachmentCache } from './attachment-cache.js';
9
+ import type { RoomAttachmentUploader } from './attachment-upload.js';
10
+ import type { RoomHostSessionObservationStore } from './host-session-observations.js';
11
+ export declare function createRoomBindingIpcHandlers(input: {
12
+ authority: RoomInvocationTokenAuthority;
13
+ registry: RoomSessionRegistry;
14
+ bindingClient: HttpRoomBindingClient;
15
+ getMachineCredentials: () => {
16
+ machineId?: string;
17
+ machineToken?: string;
18
+ };
19
+ mirrorFactory?: (roomId: string) => Pick<RoomJsonlMirror, 'append' | 'markAccessRevoked' | 'search'>;
20
+ attachmentCache?: Pick<RoomAttachmentCache, 'materialize'>;
21
+ attachmentUploader?: Pick<RoomAttachmentUploader, 'upload'>;
22
+ observations?: RoomHostSessionObservationStore;
23
+ }): Record<string, DaemonIpcHandler>;
24
+ export declare function buildRoomBindingActionParameters(input: {
25
+ roomId: string;
26
+ agentIdentityId: string;
27
+ activationCapability?: RoomActivationCapability;
28
+ activationMode?: RoomActivationMode;
29
+ }): JsonValue;
@@ -0,0 +1 @@
1
+ import{DaemonIpcError as a}from"../daemon-ipc/protocol.js";import{createRoomActionIpcHandlers as b}from"./room-action-ipc.js";function _(n){return{...b({authority:n.authority,registry:n.registry,actionClient:n.bindingClient,getMachineCredentials:n.getMachineCredentials,mirrorFactory:n.mirrorFactory,attachmentCache:n.attachmentCache,attachmentUploader:n.attachmentUploader,observations:n.observations}),"room-binding.execute":async i=>{const e=v(i),r=n.getMachineCredentials();if(!r.machineId?.trim()||!r.machineToken?.trim())throw new a("machine_not_authorized","This machine is not authorized yet.");const l=c(e.invocationToken,"invocationToken",128),m=c(e.registryKey,"registryKey",64),d=c(e.roomId,"roomId",191),s=c(e.agentIdentityId,"agentIdentityId",191),g=p(e.activationCapability),y=u(e.activationMode),h=f({roomId:d,agentIdentityId:s,activationCapability:g,activationMode:y});return n.registry.runExclusive(m,async()=>{const o=n.authority.consume({invocationToken:l,machineId:r.machineId,machineCredential:r.machineToken,registryKey:m,action:"room.bind",targetTool:"shennian_room",actionParameters:h});if(!o.bindingIntentId)throw new a("binding_intent_missing","Room Binding intent is missing.");const t=await n.bindingClient.create({machineToken:r.machineToken,bindingIntentId:o.bindingIntentId,roomId:d,agentIdentityId:s,nianSessionId:o.nianSessionId,runtimeAdapter:o.runtimeAdapter,sessionRegistryKey:o.registryKey,activationCapability:g,activationMode:y}),I=n.registry.setBinding({registryKey:m,bindingId:t.binding.id,roomId:d,agentIdentityId:s,agentMembershipId:t.binding.agentMembershipId,status:t.binding.status,activationMode:t.binding.activationMode});return{binding:{id:t.binding.id,roomId:d,agentIdentityId:s,agentMembershipId:t.binding.agentMembershipId,nianSessionId:o.nianSessionId,runtimeAdapter:o.runtimeAdapter,activationCapability:g,activationMode:t.binding.activationMode,status:t.binding.status},idempotent:t.idempotent,panelContextKey:I.panelContextKey}})}}}function f(n){return{roomId:n.roomId,agentIdentityId:n.agentIdentityId,activationCapability:n.activationCapability??"notify_only",activationMode:n.activationMode??"auto_follow"}}function v(n){if(!n||Array.isArray(n)||typeof n!="object")throw new a("invalid_params","Room Binding parameters are required.");return n}function c(n,i,e){if(typeof n!="string"||!n.trim()||n.length>e)throw new a("invalid_params",`${i} is invalid.`);return n.trim()}function p(n){const i=n??"notify_only";if(i!=="managed"&&i!=="idle_resume"&&i!=="notify_only")throw new a("invalid_params","activationCapability is invalid.");return i}function u(n){const i=n??"auto_follow";if(i!=="auto_follow"&&i!=="mention_only"&&i!=="paused")throw new a("invalid_params","activationMode is invalid.");return i}export{f as buildRoomBindingActionParameters,_ as createRoomBindingIpcHandlers};
@@ -0,0 +1,14 @@
1
+ import type { DaemonIpcHandler } from '../daemon-ipc/server.js';
2
+ import type { RoomHostSessionObservationStore } from './host-session-observations.js';
3
+ import type { HttpRoomActionClient } from './room-action-client.js';
4
+ import { RoomSessionRegistry } from './session-registry.js';
5
+ export declare function createRoomSessionRegistryIpcHandlers(input: {
6
+ registry: RoomSessionRegistry;
7
+ observations?: RoomHostSessionObservationStore;
8
+ sessionResolver?: Pick<HttpRoomActionClient, 'resolveNativeSession'>;
9
+ getMachineCredentials?: () => {
10
+ machineId?: string;
11
+ machineToken?: string;
12
+ };
13
+ getMachineId?: () => string | undefined;
14
+ }): Record<string, DaemonIpcHandler>;
@@ -0,0 +1 @@
1
+ import{DaemonIpcError as o}from"../daemon-ipc/protocol.js";function I(e){return{"room-session.register":t=>{const i=h(t),s=u(e).machineId?.trim();if(!s)throw new o("machine_not_authorized","This machine is not authorized yet.");const n=e.registry.register({machineId:s,runtimeAdapter:f(i.runtimeAdapter),sourceSessionKey:c(i.sourceSessionKey,"sourceSessionKey",1024),nianSessionId:c(i.nianSessionId,"nianSessionId",512),hostProcessIdentity:c(i.hostProcessIdentity,"hostProcessIdentity",512),capabilities:S(i.capabilities)});return m(n)},"room-session.resolve":async t=>{const i=h(t),s=u(e),n=s.machineId?.trim();if(!n)throw new o("machine_not_authorized","This machine is not authorized yet.");const a=f(i.runtimeAdapter),d=c(i.sourceSessionKey,"sourceSessionKey",1024),y=e.registry.getBySource({machineId:n,runtimeAdapter:a,sourceSessionKey:d});if(y)return m(y);if(!s.machineToken?.trim())throw new o("machine_not_authorized","This machine is not authorized yet.");const r=e.observations?.getBySource({machineId:n,runtimeAdapter:a,sourceSessionKey:d});if(!r||r.lifecycleState==="closed")throw new o("room_session_not_observed","The exact native Agent session has not been observed on this machine.");if(!e.sessionResolver)throw new o("room_session_not_found","Native Agent session is not registered.");const g=await e.sessionResolver.resolveNativeSession({machineToken:s.machineToken,runtimeAdapter:a,sourceSessionKey:d}),p=!!r.workspacePath,l=e.registry.register({machineId:n,runtimeAdapter:a,sourceSessionKey:d,nianSessionId:g.nianSessionId,hostProcessIdentity:`${a}:${r.hostContextHash}:${r.registrationEpoch}`,capabilities:{canIdentifySession:!0,canBindAutomatically:!0,canOpenEmbeddedRoomPage:!1,canResumeIdleSession:p,canActivateWhileHostOpen:!1}});return r.lifecycleState==="idle"&&e.registry.updateLifecycle(l.registryKey,"inactive"),m(e.registry.getByRegistryKey(l.registryKey))}}}function u(e){return e.getMachineCredentials?.()??{machineId:e.getMachineId?.()}}function m(e){return{registryKey:e.registryKey,nianSessionId:e.nianSessionId,panelContextKey:e.panelContextKey,runtimeAdapter:e.runtimeAdapter,registrationEpoch:e.registrationEpoch,lifecycleState:e.lifecycleState,capabilities:e.capabilities,binding:e.binding,lastSeenAt:e.lastSeenAt}}function h(e){if(!e||Array.isArray(e)||typeof e!="object")throw new o("invalid_params","Room session parameters are required.");return e}function c(e,t,i){if(typeof e!="string"||!e.trim()||e.length>i)throw new o("invalid_params",`${t} is invalid.`);return e.trim()}function f(e){const t=c(e,"runtimeAdapter",32);if(t!=="codex"&&t!=="claude_code"&&t!=="workbuddy")throw new o("invalid_params","runtimeAdapter is unsupported.");return t}function S(e){const t=h(e),i=["canIdentifySession","canBindAutomatically","canOpenEmbeddedRoomPage","canResumeIdleSession","canActivateWhileHostOpen"],s={};for(const n of i){if(typeof t[n]!="boolean")throw new o("invalid_params",`${n} must be boolean.`);s[n]=t[n]}return s}export{I as createRoomSessionRegistryIpcHandlers};
@@ -0,0 +1,128 @@
1
+ import type { RoomActivationMode, RoomBindingStatus, RoomRuntimeAdapter } from '@shennian/wire';
2
+ export type RoomSessionCapabilities = {
3
+ canIdentifySession: boolean;
4
+ canBindAutomatically: boolean;
5
+ canOpenEmbeddedRoomPage: boolean;
6
+ canResumeIdleSession: boolean;
7
+ canActivateWhileHostOpen: boolean;
8
+ };
9
+ export type RoomSessionBindingState = {
10
+ bindingId: string;
11
+ roomId: string;
12
+ agentIdentityId: string;
13
+ agentMembershipId: string | null;
14
+ status: RoomBindingStatus;
15
+ activationMode: RoomActivationMode;
16
+ lastDeliveredSequence: string;
17
+ lastActivatedSequence: string;
18
+ activationQueue: RoomActivationQueueItem[];
19
+ activeActivation: RoomActivationQueueItem | null;
20
+ loopBreaker: {
21
+ windowStartedAt: string | null;
22
+ activationCount: number;
23
+ pausedUntil: string | null;
24
+ };
25
+ };
26
+ export type RoomActivationQueueItem = {
27
+ causeMessageId: string;
28
+ rootCauseMessageId: string;
29
+ sequence: string;
30
+ hopCount: number;
31
+ enqueuedAt: string;
32
+ attempts: number;
33
+ };
34
+ export type RoomSessionRegistration = {
35
+ schemaVersion: 1;
36
+ registryKey: string;
37
+ machineId: string;
38
+ runtimeAdapter: RoomRuntimeAdapter;
39
+ sourceSessionKey: string;
40
+ nianSessionId: string;
41
+ panelContextKey: string;
42
+ hostProcessIdentity: string;
43
+ registrationEpoch: number;
44
+ lifecycleState: 'active' | 'inactive' | 'closed';
45
+ capabilities: RoomSessionCapabilities;
46
+ binding: RoomSessionBindingState | null;
47
+ registeredAt: string;
48
+ lastSeenAt: string;
49
+ updatedAt: string;
50
+ };
51
+ type RegisterInput = {
52
+ machineId: string;
53
+ runtimeAdapter: RoomRuntimeAdapter;
54
+ sourceSessionKey: string;
55
+ nianSessionId: string;
56
+ hostProcessIdentity: string;
57
+ capabilities: RoomSessionCapabilities;
58
+ now?: Date;
59
+ };
60
+ export declare class RoomSessionRegistryError extends Error {
61
+ readonly code: string;
62
+ constructor(code: string, message: string);
63
+ }
64
+ export declare class RoomSessionRegistry {
65
+ readonly filePath: string;
66
+ private state;
67
+ private readonly mutexTails;
68
+ constructor(filePath?: string);
69
+ register(input: RegisterInput): RoomSessionRegistration;
70
+ getBySource(input: Pick<RegisterInput, 'machineId' | 'runtimeAdapter' | 'sourceSessionKey'>): RoomSessionRegistration | null;
71
+ getByNianSessionId(nianSessionId: string): RoomSessionRegistration | null;
72
+ getByRegistryKey(registryKey: string): RoomSessionRegistration | null;
73
+ list(): RoomSessionRegistration[];
74
+ setBinding(input: {
75
+ registryKey: string;
76
+ bindingId: string;
77
+ roomId: string;
78
+ agentIdentityId: string;
79
+ agentMembershipId: string | null;
80
+ status: RoomBindingStatus;
81
+ activationMode?: RoomActivationMode;
82
+ now?: Date;
83
+ }): RoomSessionRegistration;
84
+ detachBinding(registryKey: string, now?: Date): RoomSessionRegistration;
85
+ advanceCursor(input: {
86
+ registryKey: string;
87
+ cursor: 'delivered' | 'activated';
88
+ sequence: string;
89
+ now?: Date;
90
+ }): RoomSessionRegistration;
91
+ enqueueActivation(registryKey: string, item: RoomActivationQueueItem, now?: Date): RoomSessionRegistration;
92
+ dequeueActivation(registryKey: string, causeMessageId: string, now?: Date): RoomSessionRegistration;
93
+ markActivationDispatching(registryKey: string, causeMessageId: string, now?: Date): RoomSessionRegistration;
94
+ clearActiveActivation(registryKey: string, now?: Date): RoomSessionRegistration;
95
+ retryActiveActivation(input: {
96
+ registryKey: string;
97
+ maxAttempts: number;
98
+ pauseMs: number;
99
+ now?: Date;
100
+ }): {
101
+ queued: boolean;
102
+ attempts: number;
103
+ };
104
+ claimActivation(input: {
105
+ registryKey: string;
106
+ windowMs: number;
107
+ maxActivations: number;
108
+ pauseMs: number;
109
+ now?: Date;
110
+ }): {
111
+ allowed: boolean;
112
+ pausedUntil: string | null;
113
+ newlyPaused?: boolean;
114
+ };
115
+ updateLifecycle(registryKey: string, lifecycleState: 'active' | 'inactive' | 'closed', now?: Date): RoomSessionRegistration;
116
+ runExclusive<T>(registryKey: string, operation: () => Promise<T>): Promise<T>;
117
+ private requireRecord;
118
+ private requireBoundRecord;
119
+ private touchAndPersist;
120
+ private load;
121
+ private persist;
122
+ }
123
+ export declare function buildRoomSessionRegistryKey(input: {
124
+ machineId: string;
125
+ runtimeAdapter: RoomRuntimeAdapter;
126
+ sourceSessionKey: string;
127
+ }): string;
128
+ export {};
@@ -0,0 +1,2 @@
1
+ import y from"node:crypto";import c from"node:fs";import b from"node:path";import{resolveShennianPath as S}from"../config/index.js";const _=500,R=/^[A-Za-z0-9_.:@/-]{1,512}$/,q=new Set(["codex","claude_code","workbuddy"]),p=new Set(["pending_approval","active","paused","detached"]),I=new Set(["auto_follow","mention_only","paused"]);class o extends Error{code;constructor(e,t){super(t),this.code=e,this.name="RoomSessionRegistryError"}}class T{filePath;state;mutexTails=new Map;constructor(e=S("runtime","room-session-registry.json")){this.filePath=e,this.state=this.load()}register(e){r(e.machineId,"machine_id"),v(e.runtimeAdapter),w(e.sourceSessionKey),r(e.nianSessionId,"nian_session_id"),r(e.hostProcessIdentity,"host_process_identity"),A(e.capabilities);const t=m(e),s=(e.now??new Date).toISOString(),n=this.state.records[t];if(Object.values(this.state.records).find(g=>g.registryKey!==t&&g.nianSessionId===e.nianSessionId&&g.lifecycleState!=="closed"))throw new o("nian_session_already_registered","This Shennian session is already registered to another native Agent session.");const u=!!(n&&n.hostProcessIdentity!==e.hostProcessIdentity),l={schemaVersion:1,registryKey:t,machineId:e.machineId,runtimeAdapter:e.runtimeAdapter,sourceSessionKey:e.sourceSessionKey,nianSessionId:e.nianSessionId,panelContextKey:e.nianSessionId,hostProcessIdentity:e.hostProcessIdentity,registrationEpoch:n?n.registrationEpoch+(u?1:0):1,lifecycleState:"active",capabilities:{...e.capabilities},binding:n?.binding??null,registeredAt:n?.registeredAt??s,lastSeenAt:s,updatedAt:s};return this.state.records[t]=l,this.persist(),d(l)}getBySource(e){const t=this.state.records[m(e)];return t?d(t):null}getByNianSessionId(e){r(e,"nian_session_id");const t=Object.values(this.state.records).find(s=>s.nianSessionId===e&&s.lifecycleState!=="closed");return t?d(t):null}getByRegistryKey(e){if(!/^[a-f0-9]{64}$/.test(e))throw new o("invalid_registry_key","Invalid Room session registry key.");const t=this.state.records[e];return t?d(t):null}list(){return Object.values(this.state.records).sort((e,t)=>e.registeredAt.localeCompare(t.registeredAt)).map(d)}setBinding(e){const t=this.requireRecord(e.registryKey);for(const[n,a]of Object.entries({binding_id:e.bindingId,room_id:e.roomId,agent_identity_id:e.agentIdentityId}))r(a,n);if(!p.has(e.status))throw new o("invalid_binding_status","Invalid binding status.");if(e.status==="pending_approval"&&e.agentMembershipId!==null||(e.status==="active"||e.status==="paused")&&e.agentMembershipId===null)throw new o("invalid_agent_membership_id","Pending Room bindings cannot claim an Agent membership.");e.agentMembershipId!==null&&r(e.agentMembershipId,"agent_membership_id");const s=e.activationMode??"auto_follow";if(!I.has(s))throw new o("invalid_activation_mode","Invalid activation mode.");if(t.binding&&t.binding.status!=="detached"&&(t.binding.bindingId!==e.bindingId||t.binding.roomId!==e.roomId))throw new o("session_already_bound","An Agent session can have only one active Room binding.");return t.binding={bindingId:e.bindingId,roomId:e.roomId,agentIdentityId:e.agentIdentityId,agentMembershipId:e.agentMembershipId,status:e.status,activationMode:s,lastDeliveredSequence:t.binding?.lastDeliveredSequence??"0",lastActivatedSequence:t.binding?.lastActivatedSequence??"0",activationQueue:t.binding?.activationQueue??[],activeActivation:t.binding?.activeActivation??null,loopBreaker:t.binding?.loopBreaker??{windowStartedAt:null,activationCount:0,pausedUntil:null}},this.touchAndPersist(t,e.now),d(t)}detachBinding(e,t=new Date){const s=this.requireRecord(e);return s.binding&&(s.binding.status="detached",s.binding.activationMode="paused",s.binding.activationQueue=[]),this.touchAndPersist(s,t),d(s)}advanceCursor(e){const t=this.requireBoundRecord(e.registryKey);h(e.sequence);const s=e.cursor==="delivered"?"lastDeliveredSequence":"lastActivatedSequence";if(BigInt(e.sequence)<BigInt(t.binding[s]))throw new o("cursor_regression","Room cursor cannot move backwards.");return t.binding[s]=e.sequence,this.touchAndPersist(t,e.now),d(t)}enqueueActivation(e,t,s=new Date){const n=this.requireBoundRecord(e);f(t);const a=n.binding.activationQueue;if(!a.some(u=>u.causeMessageId===t.causeMessageId)){if(a.length>=_)throw new o("activation_queue_full","Agent activation queue is full.");a.push(d(t))}return this.touchAndPersist(n,s),d(n)}dequeueActivation(e,t,s=new Date){const n=this.requireBoundRecord(e);return n.binding.activationQueue=n.binding.activationQueue.filter(a=>a.causeMessageId!==t),this.touchAndPersist(n,s),d(n)}markActivationDispatching(e,t,s=new Date){const n=this.requireBoundRecord(e),a=n.binding.activationQueue.find(u=>u.causeMessageId===t);if(!a)throw new o("activation_not_found","Agent activation is unavailable.");return n.binding.activeActivation=d(a),n.binding.activationQueue=n.binding.activationQueue.filter(u=>u.causeMessageId!==t),this.touchAndPersist(n,s),d(n)}clearActiveActivation(e,t=new Date){const s=this.requireBoundRecord(e);return s.binding.activeActivation=null,this.touchAndPersist(s,t),d(s)}retryActiveActivation(e){const t=this.requireBoundRecord(e.registryKey),s=t.binding.activeActivation;if(!s)return{queued:!1,attempts:0};const n=e.now??new Date,a={...s,attempts:s.attempts+1};return t.binding.activeActivation=null,a.attempts<e.maxAttempts?(t.binding.activationQueue.some(u=>u.causeMessageId===a.causeMessageId)||t.binding.activationQueue.unshift(a),this.touchAndPersist(t,n),{queued:!0,attempts:a.attempts}):(t.binding.loopBreaker.pausedUntil=new Date(n.getTime()+e.pauseMs).toISOString(),this.touchAndPersist(t,n),{queued:!1,attempts:a.attempts})}claimActivation(e){const t=this.requireBoundRecord(e.registryKey),s=e.now??new Date,n=t.binding.loopBreaker;return n.pausedUntil&&Date.parse(n.pausedUntil)>s.getTime()?{allowed:!1,pausedUntil:n.pausedUntil,newlyPaused:!1}:((!n.windowStartedAt||s.getTime()-Date.parse(n.windowStartedAt)>=e.windowMs)&&(n.windowStartedAt=s.toISOString(),n.activationCount=0,n.pausedUntil=null),n.activationCount>=e.maxActivations?(n.pausedUntil=new Date(s.getTime()+e.pauseMs).toISOString(),this.touchAndPersist(t,s),{allowed:!1,pausedUntil:n.pausedUntil,newlyPaused:!0}):(n.activationCount+=1,this.touchAndPersist(t,s),{allowed:!0,pausedUntil:null}))}updateLifecycle(e,t,s=new Date){const n=this.requireRecord(e);return n.lifecycleState=t,t==="closed"&&n.binding&&(n.binding.status="detached",n.binding.activationMode="paused",n.binding.activationQueue=[]),this.touchAndPersist(n,s),d(n)}async runExclusive(e,t){this.requireRecord(e);const s=this.mutexTails.get(e)??Promise.resolve();let n;const a=new Promise(l=>{n=l}),u=s.then(()=>a);this.mutexTails.set(e,u),await s;try{return await t()}finally{n(),this.mutexTails.get(e)===u&&this.mutexTails.delete(e)}}requireRecord(e){const t=this.state.records[e];if(!t)throw new o("session_registration_not_found","Agent session is not registered.");return t}requireBoundRecord(e){const t=this.requireRecord(e);if(!t.binding||t.binding.status!=="active"&&t.binding.status!=="paused")throw new o("session_not_bound","Agent session has no active Room binding.");return t}touchAndPersist(e,t=new Date){e.updatedAt=t.toISOString(),this.persist()}load(){try{const e=c.lstatSync(this.filePath);if(!e.isFile()||e.isSymbolicLink())throw new o("unsafe_session_registry","Room session registry is not a regular file.");process.platform!=="win32"&&(e.mode&63)!==0&&c.chmodSync(this.filePath,384);const t=JSON.parse(c.readFileSync(this.filePath,"utf8"));return P(t)}catch(e){if(B(e))return{schemaVersion:1,records:{}};throw e instanceof o?e:new o("corrupt_session_registry","Room Agent session registry is corrupt.")}}persist(){const e=b.dirname(this.filePath);c.mkdirSync(e,{recursive:!0,mode:448}),process.platform!=="win32"&&c.chmodSync(e,448);const t=`${this.filePath}.tmp-${process.pid}-${y.randomBytes(6).toString("hex")}`;try{if(c.writeFileSync(t,`${JSON.stringify(this.state,null,2)}
2
+ `,{encoding:"utf8",mode:384,flag:"wx"}),process.platform!=="win32"&&c.chmodSync(t,384),process.platform==="win32"&&c.existsSync(this.filePath)){const s=c.lstatSync(this.filePath);if(!s.isFile()||s.isSymbolicLink())throw new o("unsafe_session_registry","Refusing to replace an unsafe Room session registry.");c.unlinkSync(this.filePath)}c.renameSync(t,this.filePath),process.platform!=="win32"&&c.chmodSync(this.filePath,384)}finally{try{c.unlinkSync(t)}catch{}}}}function m(i){return r(i.machineId,"machine_id"),v(i.runtimeAdapter),w(i.sourceSessionKey),y.createHash("sha256").update(`${i.machineId}\0${i.runtimeAdapter}\0${i.sourceSessionKey}`,"utf8").digest("hex")}function P(i){if(!i||typeof i!="object"||i.schemaVersion!==1)throw new o("corrupt_session_registry","Unsupported Room session registry.");const e=i.records;if(!e||typeof e!="object"||Array.isArray(e))throw new o("corrupt_session_registry","Room session records are invalid.");const t={};for(const[s,n]of Object.entries(e)){if(M(n),s!==n.registryKey||s!==m(n))throw new o("corrupt_session_registry","Room session registry key mismatch.");t[s]={...n,binding:n.binding?{...n.binding,activeActivation:n.binding.activeActivation??null}:null}}return{schemaVersion:1,records:t}}function M(i){if(!i||i.schemaVersion!==1)throw new o("corrupt_session_registry","Invalid registration.");if(r(i.machineId,"machine_id"),v(i.runtimeAdapter),w(i.sourceSessionKey),r(i.nianSessionId,"nian_session_id"),r(i.hostProcessIdentity,"host_process_identity"),i.panelContextKey!==i.nianSessionId)throw new o("corrupt_session_registry","Invalid panel context.");if(!Number.isInteger(i.registrationEpoch)||i.registrationEpoch<1)throw new o("corrupt_session_registry","Invalid registration epoch.");A(i.capabilities),i.binding&&x(i.binding)}function x(i){if(r(i.bindingId,"binding_id"),r(i.roomId,"room_id"),r(i.agentIdentityId,"agent_identity_id"),!p.has(i.status))throw new o("corrupt_session_registry","Invalid binding status.");if(i.status==="pending_approval"&&i.agentMembershipId!==null||(i.status==="active"||i.status==="paused")&&i.agentMembershipId===null)throw new o("corrupt_session_registry","Invalid Agent membership.");if(i.agentMembershipId!==null&&r(i.agentMembershipId,"agent_membership_id"),!I.has(i.activationMode))throw new o("corrupt_session_registry","Invalid activation mode.");if(h(i.lastDeliveredSequence),h(i.lastActivatedSequence),!Array.isArray(i.activationQueue)||i.activationQueue.length>_)throw new o("corrupt_session_registry","Invalid activation queue.");i.activationQueue.forEach(f),i.activeActivation!=null&&f(i.activeActivation)}function f(i){if(r(i.causeMessageId,"cause_message_id"),r(i.rootCauseMessageId,"root_cause_message_id"),h(i.sequence),!Number.isInteger(i.hopCount)||i.hopCount<0||i.hopCount>32)throw new o("invalid_hop_count","Invalid Agent activation hop count.");if(!Number.isInteger(i.attempts)||i.attempts<0||i.attempts>100)throw new o("invalid_activation_attempts","Invalid Agent activation attempts.");if(!Number.isFinite(new Date(i.enqueuedAt).getTime()))throw new o("invalid_activation_time","Invalid Agent activation time.")}function A(i){if(!i||["canIdentifySession","canBindAutomatically","canOpenEmbeddedRoomPage","canResumeIdleSession","canActivateWhileHostOpen"].some(t=>typeof i[t]!="boolean"))throw new o("invalid_session_capabilities","Invalid Agent session capabilities.")}function v(i){if(!q.has(i))throw new o("invalid_runtime_adapter","Unsupported Room runtime adapter.")}function w(i){if(!i||i.length>1024||/[\0\r\n]/.test(i))throw new o("invalid_source_session_key","Invalid native Agent session key.")}function r(i,e){if(!R.test(i))throw new o(`invalid_${e}`,`Invalid ${e}.`)}function h(i){if(!/^(0|[1-9]\d{0,19})$/.test(i))throw new o("invalid_room_sequence","Invalid Room sequence.")}function d(i){return structuredClone(i)}function B(i){return typeof i=="object"&&i!==null&&"code"in i&&i.code==="ENOENT"}export{T as RoomSessionRegistry,o as RoomSessionRegistryError,m as buildRoomSessionRegistryKey};