shennian 0.5.0 → 0.5.2

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 (342) hide show
  1. package/README.md +2 -2
  2. package/dist/publish-build-manifest.json +165 -125
  3. package/dist/src/agents/acp/adapter.js +2 -2
  4. package/dist/src/agents/acp/capability-cache.js +2 -2
  5. package/dist/src/agents/acp/catalog.js +2 -2
  6. package/dist/src/agents/acp/manifests.js +2 -2
  7. package/dist/src/agents/claude.js +8 -6
  8. package/dist/src/agents/codex.js +2 -2
  9. package/dist/src/agents/command-spec.js +4 -4
  10. package/dist/src/agents/cursor.js +1 -1
  11. package/dist/src/agents/custom-recovery-conformance.js +2 -2
  12. package/dist/src/agents/gemini.js +1 -1
  13. package/dist/src/agents/model-registry/discovery.js +3 -3
  14. package/dist/src/agents/model-registry/service.js +1 -1
  15. package/dist/src/agents/model-registry.js +1 -1
  16. package/dist/src/agents/openclaw.js +2 -2
  17. package/dist/src/agents/opencode.js +1 -1
  18. package/dist/src/agents/pi-coding-agent.js +3 -4
  19. package/dist/src/agents/pi-session-ownership.js +2 -2
  20. package/dist/src/agents/spawn-errors.js +1 -0
  21. package/dist/src/agents/windows-agent-paths.js +1 -0
  22. package/dist/src/commands/agent-readiness.js +2 -2
  23. package/dist/src/commands/daemon-service-spec.js +1 -1
  24. package/dist/src/commands/daemon.js +6 -6
  25. package/dist/src/commands/queue.js +1 -0
  26. package/dist/src/commands/room.js +3 -3
  27. package/dist/src/config/index.js +2 -2
  28. package/dist/src/fs/atomic-rename.js +1 -0
  29. package/dist/src/index.js +3 -3
  30. package/dist/src/native-fusion/claude-entry-parser.js +7 -0
  31. package/dist/src/native-fusion/local-repository.js +2 -2
  32. package/dist/src/native-fusion/native-source-deletions.js +2 -2
  33. package/dist/src/native-fusion/parsers.js +1 -3
  34. package/dist/src/native-fusion/state.js +2 -2
  35. package/dist/src/personal-sync/epoch.js +2 -2
  36. package/dist/src/personal-sync/identity.js +2 -2
  37. package/dist/src/relay/client.js +1 -1
  38. package/dist/src/room/activation-service.js +6 -6
  39. package/dist/src/room/attachment-cache.js +2 -2
  40. package/dist/src/room/host-session-observations.js +2 -2
  41. package/dist/src/room/managed-context.js +1 -1
  42. package/dist/src/room/managed-room-ipc.js +2 -2
  43. package/dist/src/room/mirror-sync-service.js +1 -1
  44. package/dist/src/room/public-attachment-cache.js +2 -2
  45. package/dist/src/room/room-error-guidance.js +1 -0
  46. package/dist/src/room/session-registry.js +2 -2
  47. package/dist/src/security/managed-agent-policy.js +1 -1
  48. package/dist/src/security/managed-agent-runtime.js +1 -1
  49. package/dist/src/session/attachment-transfer-store.js +2 -2
  50. package/dist/src/session/canonical-store.js +5 -5
  51. package/dist/src/session/deletion-sync-state.js +2 -2
  52. package/dist/src/session/handlers/agent-workspace-validation.js +1 -1
  53. package/dist/src/session/handlers/chat-adapter-config.js +1 -1
  54. package/dist/src/session/handlers/chat-events.js +2 -2
  55. package/dist/src/session/handlers/chat-recovery.js +1 -1
  56. package/dist/src/session/handlers/chat.js +1 -1
  57. package/dist/src/session/manager.js +1 -1
  58. package/dist/src/session/native-cleanup-outbox.js +2 -2
  59. package/dist/src/session/queue-diagnostics.js +1 -0
  60. package/dist/src/session/queue-dispatch-failure.js +1 -0
  61. package/dist/src/session/queue-helpers.js +5 -5
  62. package/dist/src/session/queue-retry.js +1 -1
  63. package/dist/src/session/queue-storage.js +2 -2
  64. package/dist/src/session/queue.js +1 -1
  65. package/dist/src/session/room-reply-notice.js +1 -0
  66. package/dist/src/session/run-recovery/journal.js +2 -2
  67. package/dist/src/session/run-stall.js +1 -0
  68. package/dist/src/session/store.js +2 -2
  69. package/dist/src/session/tool-detail-store.js +2 -2
  70. package/node_modules/@shennian/wire/dist/agent-capabilities.js +1 -1
  71. package/node_modules/@shennian/wire/dist/agent-workspace.js +1 -144
  72. package/node_modules/@shennian/wire/dist/envelope.js +1 -1
  73. package/node_modules/@shennian/wire/dist/events.js +1 -1
  74. package/node_modules/@shennian/wire/dist/external-message-format.js +3 -70
  75. package/node_modules/@shennian/wire/dist/frames.js +1 -1
  76. package/node_modules/@shennian/wire/dist/fs.js +2 -7
  77. package/node_modules/@shennian/wire/dist/index.js +1 -21
  78. package/node_modules/@shennian/wire/dist/machine.js +1 -1
  79. package/node_modules/@shennian/wire/dist/message-payload.js +8 -307
  80. package/node_modules/@shennian/wire/dist/methods.js +1 -3
  81. package/node_modules/@shennian/wire/dist/native-session.js +1 -1
  82. package/node_modules/@shennian/wire/dist/project-fs.js +1 -462
  83. package/node_modules/@shennian/wire/dist/project-path.js +1 -25
  84. package/node_modules/@shennian/wire/dist/project.js +1 -3
  85. package/node_modules/@shennian/wire/dist/relay-connectivity.js +1 -29
  86. package/node_modules/@shennian/wire/dist/room.js +1 -20
  87. package/node_modules/@shennian/wire/dist/schedule.js +1 -1
  88. package/node_modules/@shennian/wire/dist/session-sync.js +1 -123
  89. package/node_modules/@shennian/wire/dist/session.js +1 -60
  90. package/node_modules/@shennian/wire/dist/tool-detail.js +1 -9
  91. package/node_modules/@shennian/wire/dist/voice-chief.js +1 -3
  92. package/package.json +3 -8
  93. package/dist/bin/shennian.d.ts +0 -2
  94. package/dist/src/agent-env.d.ts +0 -44
  95. package/dist/src/agents/acp/adapter.d.ts +0 -82
  96. package/dist/src/agents/acp/auth-state.d.ts +0 -3
  97. package/dist/src/agents/acp/auth.d.ts +0 -7
  98. package/dist/src/agents/acp/capability-cache.d.ts +0 -14
  99. package/dist/src/agents/acp/catalog.d.ts +0 -11
  100. package/dist/src/agents/acp/client-services.d.ts +0 -37
  101. package/dist/src/agents/acp/discovery.d.ts +0 -10
  102. package/dist/src/agents/acp/events.d.ts +0 -4
  103. package/dist/src/agents/acp/manifests.d.ts +0 -63
  104. package/dist/src/agents/acp/metrics.d.ts +0 -24
  105. package/dist/src/agents/acp/package-commands.d.ts +0 -4
  106. package/dist/src/agents/acp/process-client.d.ts +0 -41
  107. package/dist/src/agents/acp/process-tree.d.ts +0 -2
  108. package/dist/src/agents/acp/types.d.ts +0 -31
  109. package/dist/src/agents/acp/watcher.d.ts +0 -22
  110. package/dist/src/agents/adapter.d.ts +0 -180
  111. package/dist/src/agents/auth-status.d.ts +0 -4
  112. package/dist/src/agents/claude-managed-permissions.d.ts +0 -33
  113. package/dist/src/agents/claude.d.ts +0 -69
  114. package/dist/src/agents/codex-managed-permissions.d.ts +0 -32
  115. package/dist/src/agents/codex-managed-room-tools.d.ts +0 -41
  116. package/dist/src/agents/codex-protocol.d.ts +0 -103
  117. package/dist/src/agents/codex-runtime.d.ts +0 -91
  118. package/dist/src/agents/codex-utils.d.ts +0 -29
  119. package/dist/src/agents/codex.d.ts +0 -59
  120. package/dist/src/agents/command-spec.d.ts +0 -55
  121. package/dist/src/agents/config-status.d.ts +0 -18
  122. package/dist/src/agents/cursor-managed-permissions.d.ts +0 -28
  123. package/dist/src/agents/cursor.d.ts +0 -44
  124. package/dist/src/agents/custom-managed-runtime.d.ts +0 -37
  125. package/dist/src/agents/custom-recovery-conformance.d.ts +0 -2
  126. package/dist/src/agents/custom.d.ts +0 -62
  127. package/dist/src/agents/detect.d.ts +0 -23
  128. package/dist/src/agents/event-terminal-gate.d.ts +0 -16
  129. package/dist/src/agents/external-channel-instructions.d.ts +0 -4
  130. package/dist/src/agents/gemini.d.ts +0 -32
  131. package/dist/src/agents/manager.d.ts +0 -57
  132. package/dist/src/agents/model-registry/cache.d.ts +0 -4
  133. package/dist/src/agents/model-registry/discovery.d.ts +0 -4
  134. package/dist/src/agents/model-registry/opencode-api.d.ts +0 -2
  135. package/dist/src/agents/model-registry/parsers.d.ts +0 -20
  136. package/dist/src/agents/model-registry/runner.d.ts +0 -6
  137. package/dist/src/agents/model-registry/service.d.ts +0 -10
  138. package/dist/src/agents/model-registry/types.d.ts +0 -39
  139. package/dist/src/agents/model-registry.d.ts +0 -17
  140. package/dist/src/agents/native-capabilities.d.ts +0 -10
  141. package/dist/src/agents/openclaw.d.ts +0 -29
  142. package/dist/src/agents/opencode-managed-permissions.d.ts +0 -25
  143. package/dist/src/agents/opencode.d.ts +0 -46
  144. package/dist/src/agents/pi-coding-agent-managed-permissions.d.ts +0 -7
  145. package/dist/src/agents/pi-coding-agent.d.ts +0 -63
  146. package/dist/src/agents/pi-managed-extension.d.ts +0 -12
  147. package/dist/src/agents/pi-managed-model-broker.d.ts +0 -27
  148. package/dist/src/agents/pi-managed-model-broker.js +0 -1
  149. package/dist/src/agents/pi-rpc-client.d.ts +0 -31
  150. package/dist/src/agents/pi-session-ownership.d.ts +0 -2
  151. package/dist/src/agents/platform-instructions.d.ts +0 -5
  152. package/dist/src/agents/runtime-compatibility.d.ts +0 -11
  153. package/dist/src/agents/workbuddy-managed-permissions.d.ts +0 -37
  154. package/dist/src/agents/workbuddy.d.ts +0 -63
  155. package/dist/src/commands/agent-readiness.d.ts +0 -17
  156. package/dist/src/commands/agent.d.ts +0 -2
  157. package/dist/src/commands/daemon-instance-lock.d.ts +0 -21
  158. package/dist/src/commands/daemon-process.d.ts +0 -6
  159. package/dist/src/commands/daemon-service-spec.d.ts +0 -26
  160. package/dist/src/commands/daemon-start-wait.d.ts +0 -9
  161. package/dist/src/commands/daemon-state.d.ts +0 -57
  162. package/dist/src/commands/daemon-windows.d.ts +0 -33
  163. package/dist/src/commands/daemon.d.ts +0 -67
  164. package/dist/src/commands/integration.d.ts +0 -3
  165. package/dist/src/commands/logout.d.ts +0 -3
  166. package/dist/src/commands/manager.d.ts +0 -2
  167. package/dist/src/commands/pair-authorization.d.ts +0 -3
  168. package/dist/src/commands/pair-browser.d.ts +0 -44
  169. package/dist/src/commands/pair-qr.d.ts +0 -4
  170. package/dist/src/commands/pair.d.ts +0 -19
  171. package/dist/src/commands/room.d.ts +0 -16
  172. package/dist/src/commands/runtime.d.ts +0 -2
  173. package/dist/src/commands/schedule.d.ts +0 -2
  174. package/dist/src/commands/tools.d.ts +0 -2
  175. package/dist/src/commands/upgrade.d.ts +0 -31
  176. package/dist/src/commands/worker.d.ts +0 -11
  177. package/dist/src/config/index.d.ts +0 -58
  178. package/dist/src/daemon-ipc/agent-readiness.d.ts +0 -15
  179. package/dist/src/daemon-ipc/client.d.ts +0 -8
  180. package/dist/src/daemon-ipc/control.d.ts +0 -16
  181. package/dist/src/daemon-ipc/json.d.ts +0 -3
  182. package/dist/src/daemon-ipc/paths.d.ts +0 -15
  183. package/dist/src/daemon-ipc/protocol.d.ts +0 -31
  184. package/dist/src/daemon-ipc/secret.d.ts +0 -3
  185. package/dist/src/daemon-ipc/server.d.ts +0 -20
  186. package/dist/src/daemon-log.d.ts +0 -9
  187. package/dist/src/env-path.d.ts +0 -15
  188. package/dist/src/fs/boundary.d.ts +0 -23
  189. package/dist/src/fs/handler.d.ts +0 -26
  190. package/dist/src/fs/security.d.ts +0 -2
  191. package/dist/src/fs/text-decoder.d.ts +0 -10
  192. package/dist/src/index.d.ts +0 -1
  193. package/dist/src/log-redaction.d.ts +0 -4
  194. package/dist/src/log-reporter.d.ts +0 -41
  195. package/dist/src/manager/prompt.d.ts +0 -2
  196. package/dist/src/manager/registry.d.ts +0 -184
  197. package/dist/src/manager/retired-external-channel-compat.d.ts +0 -78
  198. package/dist/src/manager/runtime.d.ts +0 -90
  199. package/dist/src/manager/worker-ipc.d.ts +0 -28
  200. package/dist/src/manager/worker-markdown.d.ts +0 -10
  201. package/dist/src/native-fusion/codex-files.d.ts +0 -10
  202. package/dist/src/native-fusion/codex-message-parser.d.ts +0 -9
  203. package/dist/src/native-fusion/codex-parser.d.ts +0 -8
  204. package/dist/src/native-fusion/codex-user-input.d.ts +0 -11
  205. package/dist/src/native-fusion/config.d.ts +0 -1
  206. package/dist/src/native-fusion/file-identity.d.ts +0 -9
  207. package/dist/src/native-fusion/file-watcher.d.ts +0 -26
  208. package/dist/src/native-fusion/local-repository.d.ts +0 -90
  209. package/dist/src/native-fusion/native-source-deletions.d.ts +0 -47
  210. package/dist/src/native-fusion/opencode-parser.d.ts +0 -29
  211. package/dist/src/native-fusion/parser-common.d.ts +0 -35
  212. package/dist/src/native-fusion/parsers.d.ts +0 -13
  213. package/dist/src/native-fusion/service.d.ts +0 -64
  214. package/dist/src/native-fusion/session-header.d.ts +0 -13
  215. package/dist/src/native-fusion/session-native-sources.d.ts +0 -24
  216. package/dist/src/native-fusion/source-visibility-audit.d.ts +0 -16
  217. package/dist/src/native-fusion/source-visibility.d.ts +0 -25
  218. package/dist/src/native-fusion/state.d.ts +0 -4
  219. package/dist/src/native-fusion/types.d.ts +0 -82
  220. package/dist/src/personal-sync/epoch.d.ts +0 -17
  221. package/dist/src/personal-sync/identity.d.ts +0 -1
  222. package/dist/src/personal-sync/machine-authority.d.ts +0 -12
  223. package/dist/src/personal-sync/metrics.d.ts +0 -25
  224. package/dist/src/personal-sync/paths.d.ts +0 -5
  225. package/dist/src/region.d.ts +0 -15
  226. package/dist/src/relay/client.d.ts +0 -81
  227. package/dist/src/room/activation-audit.d.ts +0 -15
  228. package/dist/src/room/activation-policy.d.ts +0 -8
  229. package/dist/src/room/activation-service.d.ts +0 -77
  230. package/dist/src/room/attachment-cache.d.ts +0 -44
  231. package/dist/src/room/attachment-upload.d.ts +0 -20
  232. package/dist/src/room/device-authorization-client.d.ts +0 -91
  233. package/dist/src/room/host-session-observations.d.ts +0 -54
  234. package/dist/src/room/invite-bootstrap.d.ts +0 -49
  235. package/dist/src/room/invite-qr.d.ts +0 -6
  236. package/dist/src/room/live-delivery.d.ts +0 -5
  237. package/dist/src/room/managed-context.d.ts +0 -49
  238. package/dist/src/room/managed-prompt-composer.d.ts +0 -47
  239. package/dist/src/room/managed-room-binding.d.ts +0 -69
  240. package/dist/src/room/managed-room-ipc.d.ts +0 -65
  241. package/dist/src/room/mirror-sync-service.d.ts +0 -49
  242. package/dist/src/room/public-attachment-cache.d.ts +0 -34
  243. package/dist/src/room/public-room-client.d.ts +0 -37
  244. package/dist/src/room/registration-authority.d.ts +0 -4
  245. package/dist/src/room/room-action-client-types.d.ts +0 -137
  246. package/dist/src/room/room-action-client.d.ts +0 -116
  247. package/dist/src/room/room-binding-client.d.ts +0 -62
  248. package/dist/src/room/session-registry.d.ts +0 -158
  249. package/dist/src/runtime-distribution.d.ts +0 -7
  250. package/dist/src/schedules/personal-context.d.ts +0 -2
  251. package/dist/src/schedules/server-client.d.ts +0 -19
  252. package/dist/src/security/managed-agent-credential-paths.d.ts +0 -6
  253. package/dist/src/security/managed-agent-executable.d.ts +0 -19
  254. package/dist/src/security/managed-agent-policy.d.ts +0 -66
  255. package/dist/src/security/managed-agent-runtime.d.ts +0 -44
  256. package/dist/src/session/archive-zip.d.ts +0 -10
  257. package/dist/src/session/attachment-reference.d.ts +0 -2
  258. package/dist/src/session/attachment-transfer-store.d.ts +0 -45
  259. package/dist/src/session/canonical-store.d.ts +0 -104
  260. package/dist/src/session/deletion-sync-state.d.ts +0 -20
  261. package/dist/src/session/deletion-sync-validation.d.ts +0 -2
  262. package/dist/src/session/deletion-synchronizer.d.ts +0 -9
  263. package/dist/src/session/handlers/agent-capabilities.d.ts +0 -3
  264. package/dist/src/session/handlers/agent-config.d.ts +0 -7
  265. package/dist/src/session/handlers/agent-control.d.ts +0 -4
  266. package/dist/src/session/handlers/agent-workspace-validation.d.ts +0 -28
  267. package/dist/src/session/handlers/agents.d.ts +0 -11
  268. package/dist/src/session/handlers/chat-adapter-config.d.ts +0 -11
  269. package/dist/src/session/handlers/chat-context.d.ts +0 -31
  270. package/dist/src/session/handlers/chat-events.d.ts +0 -5
  271. package/dist/src/session/handlers/chat-recovery.d.ts +0 -4
  272. package/dist/src/session/handlers/chat-session-lifecycle.d.ts +0 -9
  273. package/dist/src/session/handlers/chat.d.ts +0 -6
  274. package/dist/src/session/handlers/control.d.ts +0 -5
  275. package/dist/src/session/handlers/fs.d.ts +0 -14
  276. package/dist/src/session/handlers/message-status.d.ts +0 -3
  277. package/dist/src/session/handlers/pi-validation-session.d.ts +0 -7
  278. package/dist/src/session/handlers/pi-validation-session.js +0 -1
  279. package/dist/src/session/handlers/project-fs.d.ts +0 -11
  280. package/dist/src/session/handlers/remote-access.d.ts +0 -4
  281. package/dist/src/session/handlers/room-managed.d.ts +0 -6
  282. package/dist/src/session/handlers/session-refresh.d.ts +0 -3
  283. package/dist/src/session/handlers/title.d.ts +0 -3
  284. package/dist/src/session/handlers/tool-detail.d.ts +0 -3
  285. package/dist/src/session/handlers/voice-window.d.ts +0 -3
  286. package/dist/src/session/history-subscriptions.d.ts +0 -29
  287. package/dist/src/session/index-publisher.d.ts +0 -3
  288. package/dist/src/session/manager-event-authority.d.ts +0 -3
  289. package/dist/src/session/manager.d.ts +0 -95
  290. package/dist/src/session/native-cleanup-outbox.d.ts +0 -64
  291. package/dist/src/session/native-session-lifecycle.d.ts +0 -56
  292. package/dist/src/session/project-fs-store.d.ts +0 -43
  293. package/dist/src/session/projection.d.ts +0 -3
  294. package/dist/src/session/queue-control.d.ts +0 -27
  295. package/dist/src/session/queue-helpers.d.ts +0 -20
  296. package/dist/src/session/queue-message.d.ts +0 -35
  297. package/dist/src/session/queue-retry.d.ts +0 -11
  298. package/dist/src/session/queue-storage.d.ts +0 -8
  299. package/dist/src/session/queue-types.d.ts +0 -31
  300. package/dist/src/session/queue.d.ts +0 -73
  301. package/dist/src/session/queued-chat-dispatch.d.ts +0 -12
  302. package/dist/src/session/run-recovery/barrier.d.ts +0 -12
  303. package/dist/src/session/run-recovery/coordinator.d.ts +0 -32
  304. package/dist/src/session/run-recovery/journal.d.ts +0 -30
  305. package/dist/src/session/run-recovery/schema.d.ts +0 -48
  306. package/dist/src/session/store.d.ts +0 -128
  307. package/dist/src/session/tool-detail-store.d.ts +0 -17
  308. package/dist/src/session/turn-trigger.d.ts +0 -43
  309. package/dist/src/session/types.d.ts +0 -93
  310. package/dist/src/tools/markdown-to-pdf.d.ts +0 -42
  311. package/dist/src/upgrade/channel.d.ts +0 -5
  312. package/dist/src/upgrade/distribution.d.ts +0 -21
  313. package/dist/src/upgrade/engine.d.ts +0 -104
  314. package/dist/src/upgrade/scheduler.d.ts +0 -60
  315. package/dist/src/upgrade/updater-bootstrap.d.ts +0 -12
  316. package/dist/src/upgrade/updater-launcher.d.ts +0 -37
  317. package/dist/src/upgrade/updater-protocol.d.ts +0 -40
  318. package/dist/src/upgrade/updater-service-entry.d.ts +0 -7
  319. package/dist/src/upgrade/updater-worker.d.ts +0 -57
  320. package/dist/src/windows-shell.d.ts +0 -6
  321. package/node_modules/@shennian/wire/dist/agent-capabilities.d.ts +0 -16
  322. package/node_modules/@shennian/wire/dist/agent-workspace.d.ts +0 -133
  323. package/node_modules/@shennian/wire/dist/envelope.d.ts +0 -7
  324. package/node_modules/@shennian/wire/dist/events.d.ts +0 -125
  325. package/node_modules/@shennian/wire/dist/external-message-format.d.ts +0 -37
  326. package/node_modules/@shennian/wire/dist/frames.d.ts +0 -33
  327. package/node_modules/@shennian/wire/dist/fs.d.ts +0 -107
  328. package/node_modules/@shennian/wire/dist/index.d.ts +0 -21
  329. package/node_modules/@shennian/wire/dist/machine.d.ts +0 -160
  330. package/node_modules/@shennian/wire/dist/message-payload.d.ts +0 -71
  331. package/node_modules/@shennian/wire/dist/methods.d.ts +0 -5
  332. package/node_modules/@shennian/wire/dist/native-session.d.ts +0 -20
  333. package/node_modules/@shennian/wire/dist/project-fs.d.ts +0 -146
  334. package/node_modules/@shennian/wire/dist/project-path.d.ts +0 -4
  335. package/node_modules/@shennian/wire/dist/project.d.ts +0 -16
  336. package/node_modules/@shennian/wire/dist/relay-connectivity.d.ts +0 -30
  337. package/node_modules/@shennian/wire/dist/room.d.ts +0 -190
  338. package/node_modules/@shennian/wire/dist/schedule.d.ts +0 -37
  339. package/node_modules/@shennian/wire/dist/session-sync.d.ts +0 -406
  340. package/node_modules/@shennian/wire/dist/session.d.ts +0 -465
  341. package/node_modules/@shennian/wire/dist/tool-detail.d.ts +0 -38
  342. package/node_modules/@shennian/wire/dist/voice-chief.d.ts +0 -116
@@ -1,2 +1,2 @@
1
- import I from"node:crypto";import u from"node:fs";import P from"node:path";import{isRoomRuntimeAdapter as M}from"@shennian/wire";import{resolveShennianPath as C}from"../config/index.js";const f=500,m=20,D=/^[A-Za-z0-9_.:@/-]{1,512}$/,b=new Set(["pending_approval","active","paused","detached"]),S=new Set(["auto_follow","mention_only","paused"]);class o extends Error{code;constructor(e,i){super(i),this.code=e,this.name="RoomSessionRegistryError"}}class j{filePath;state;mutexTails=new Map;constructor(e=C("runtime","room-session-registry.json"),i=new Date){this.filePath=e,this.state=this.load(),this.markPersistedActivationsInterrupted(i)&&this.persist()}register(e){c(e.machineId,"machine_id"),_(e.runtimeAdapter),A(e.sourceSessionKey),c(e.nianSessionId,"nian_session_id"),c(e.hostProcessIdentity,"host_process_identity"),q(e.capabilities);const i=p(e),s=(e.now??new Date).toISOString(),n=this.state.records[i];if(n?.lifecycleState==="closed")throw new o("session_registration_closed","A retired session cannot be registered again.");if(Object.values(this.state.records).find(g=>g.registryKey!==i&&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 a=!!(n&&n.hostProcessIdentity!==e.hostProcessIdentity),l={schemaVersion:1,registryKey:i,machineId:e.machineId,runtimeAdapter:e.runtimeAdapter,sourceSessionKey:e.sourceSessionKey,nianSessionId:e.nianSessionId,panelContextKey:e.nianSessionId,hostProcessIdentity:e.hostProcessIdentity,registrationEpoch:n?n.registrationEpoch+(a?1:0):1,lifecycleState:"active",capabilities:{...e.capabilities},binding:n?.binding??null,registeredAt:n?.registeredAt??s,lastSeenAt:s,updatedAt:s};return this.state.records[i]=l,this.persist(),d(l)}getBySource(e){const i=this.state.records[p(e)];return i?d(i):null}getByNianSessionId(e){c(e,"nian_session_id");const i=Object.values(this.state.records).find(s=>s.nianSessionId===e&&s.lifecycleState!=="closed");return i?d(i):null}getByRegistryKey(e){if(!/^[a-f0-9]{64}$/.test(e))throw new o("invalid_registry_key","Invalid Room session registry key.");const i=this.state.records[e];return i?d(i):null}list(){return Object.values(this.state.records).sort((e,i)=>e.registeredAt.localeCompare(i.registeredAt)).map(d)}setBinding(e){const i=this.requireRecord(e.registryKey);for(const[a,l]of Object.entries({binding_id:e.bindingId,room_id:e.roomId,agent_identity_id:e.agentIdentityId}))c(l,a);if(!b.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&&c(e.agentMembershipId,"agent_membership_id");const s=e.activationMode??"auto_follow";if(!S.has(s))throw new o("invalid_activation_mode","Invalid activation mode.");if(i.binding&&i.binding.status!=="detached"&&(i.binding.bindingId!==e.bindingId||i.binding.roomId!==e.roomId))throw new o("session_already_bound","An Agent session can have only one active Room binding.");if(i.lifecycleState==="closed")throw new o("session_registration_closed","A retired session cannot be rebound.");e.initialSequence!==void 0&&h(e.initialSequence);const n=Object.values(this.state.records).filter(a=>a.registryKey!==i.registryKey&&a.lifecycleState!=="closed"&&a.binding?.bindingId===e.bindingId&&a.binding.status!=="detached");if(e.status!=="detached"&&n.length&&!e.replaceOwner)throw new o("binding_already_owned","A Room binding can have only one live session.");if(e.replaceOwner)for(const a of Object.values(this.state.records))a.registryKey===i.registryKey||a.binding?.bindingId!==e.bindingId||(a.lifecycleState="closed",a.registrationEpoch+=1,a.binding.status="detached",a.binding.activationMode="paused",a.binding.activationQueue=[],a.binding.activeActivation=null,a.binding.mentionContext=[],a.updatedAt=(e.now??new Date).toISOString());const r=i.binding?.activationMode;return i.binding={bindingId:e.bindingId,roomId:e.roomId,agentIdentityId:e.agentIdentityId,agentMembershipId:e.agentMembershipId,status:e.status,activationMode:s,lastDeliveredSequence:i.binding?.lastDeliveredSequence??e.initialSequence??"0",lastActivatedSequence:i.binding?.lastActivatedSequence??e.initialSequence??"0",activationQueue:i.binding?.activationQueue??[],activeActivation:i.binding?.activeActivation??null,mentionContext:r==="mention_only"&&s==="mention_only"?i.binding?.mentionContext??[]:[],loopBreaker:i.binding?.loopBreaker??{windowStartedAt:null,activationCount:0,pausedUntil:null}},this.touchAndPersist(i,e.now),d(i)}detachBinding(e,i=new Date){const s=this.requireRecord(e);return s.binding&&(s.binding.status="detached",s.binding.activationMode="paused",s.binding.activationQueue=[],s.binding.activeActivation=null,s.binding.mentionContext=[]),this.touchAndPersist(s,i),d(s)}advanceCursor(e){const i=this.requireBoundRecord(e.registryKey);h(e.sequence);const s=e.cursor==="delivered"?"lastDeliveredSequence":"lastActivatedSequence";if(BigInt(e.sequence)<BigInt(i.binding[s]))throw new o("cursor_regression","Room cursor cannot move backwards.");return i.binding[s]=e.sequence,this.touchAndPersist(i,e.now),d(i)}enqueueActivation(e,i,s=new Date,n=!1){const r=this.requireBoundRecord(e);y(i);const a=r.binding.activationQueue;if(!a.some(l=>l.causeMessageId===i.causeMessageId)){if(a.length>=f)throw new o("activation_queue_full","Agent activation queue is full.");a.push(d(i))}return n&&(r.binding.mentionContext=[]),this.touchAndPersist(r,s),d(r)}appendMentionContext(e,i,s=new Date){const n=this.requireBoundRecord(e);w(i);const r=n.binding.mentionContext??[];return r.some(a=>a.sequence===i.sequence)||(r.push(d(i)),n.binding.mentionContext=r.slice(-m)),this.touchAndPersist(n,s),d(n)}replaceMentionContext(e,i,s=new Date){const n=this.requireBoundRecord(e);for(const r of i)w(r);return n.binding.mentionContext=i.slice(-m).map(d),this.touchAndPersist(n,s),d(n)}retireLegacyActivationState(e,i=new Date){const s=this.requireBoundRecord(e);return s.binding.activationQueue=[],s.binding.activeActivation=null,this.touchAndPersist(s,i),d(s)}dequeueActivation(e,i,s=new Date){const n=this.requireBoundRecord(e);return n.binding.activationQueue=n.binding.activationQueue.filter(r=>r.causeMessageId!==i),this.touchAndPersist(n,s),d(n)}markActivationDispatching(e,i,s=new Date){const n=this.requireBoundRecord(e),r=n.binding.activationQueue.find(a=>a.causeMessageId===i);if(!r)throw new o("activation_not_found","Agent activation is unavailable.");return n.binding.activeActivation={...d(r),dispatchState:"dispatching",dispatchedAt:s.toISOString(),interruptedAt:void 0},n.binding.activationQueue=n.binding.activationQueue.filter(a=>a.causeMessageId!==i),this.touchAndPersist(n,s),d(n)}clearActiveActivation(e,i=new Date){const s=this.requireBoundRecord(e);return s.binding.activeActivation=null,this.touchAndPersist(s,i),d(s)}retryActiveActivation(e){const i=this.requireBoundRecord(e.registryKey),s=i.binding.activeActivation;if(!s)return{queued:!1,attempts:0};if(x(s))throw new o("activation_interrupted_requires_explicit_retry","Interrupted Agent activation requires an explicit retry.");return this.requeueActiveActivation(i,e)}requeueActiveActivation(e,i){const s=e.binding.activeActivation,n=i.now??new Date,{dispatchState:r,dispatchedAt:a,interruptedAt:l,...g}=s,v={...g,attempts:s.attempts+1};return e.binding.activeActivation=null,v.attempts<i.maxAttempts?(e.binding.activationQueue.some(R=>R.causeMessageId===v.causeMessageId)||e.binding.activationQueue.unshift(v),this.touchAndPersist(e,n),{queued:!0,attempts:v.attempts}):(e.binding.loopBreaker.pausedUntil=new Date(n.getTime()+i.pauseMs).toISOString(),this.touchAndPersist(e,n),{queued:!1,attempts:v.attempts})}markPersistedActivationsInterrupted(e){const i=e.toISOString();let s=!1;for(const n of Object.values(this.state.records)){const r=n.binding?.activeActivation;!r||x(r)||(r.dispatchState="interrupted",r.interruptedAt=i,n.updatedAt=i,s=!0)}return s}claimActivation(e){const i=this.requireBoundRecord(e.registryKey),s=e.now??new Date,n=i.binding.loopBreaker;return e.causeMessageId&&n.claimedMessageIds?.includes(e.causeMessageId)?{allowed:!0,pausedUntil:null}: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(i,s),{allowed:!1,pausedUntil:n.pausedUntil,newlyPaused:!0}):(n.activationCount+=1,e.causeMessageId&&(n.claimedMessageIds=[...n.claimedMessageIds??[],e.causeMessageId].slice(-f)),this.touchAndPersist(i,s),{allowed:!0,pausedUntil:null}))}updateLifecycle(e,i,s=new Date){const n=this.requireRecord(e);if(n.lifecycleState==="closed"&&i!=="closed")throw new o("session_registration_closed","A retired session cannot be reopened.");return n.lifecycleState!==i&&(n.registrationEpoch+=1),n.lifecycleState=i,i==="closed"&&n.binding&&(n.binding.status="detached",n.binding.activationMode="paused",n.binding.activationQueue=[],n.binding.activeActivation=null,n.binding.mentionContext=[]),this.touchAndPersist(n,s),d(n)}async runExclusive(e,i){this.requireRecord(e);const s=this.mutexTails.get(e)??Promise.resolve();let n;const r=new Promise(l=>{n=l}),a=s.then(()=>r);this.mutexTails.set(e,a),await s;try{return await i()}finally{n(),this.mutexTails.get(e)===a&&this.mutexTails.delete(e)}}requireRecord(e){const i=this.state.records[e];if(!i)throw new o("session_registration_not_found","Agent session is not registered.");return i}requireBoundRecord(e){const i=this.requireRecord(e);if(!i.binding||i.binding.status!=="active"&&i.binding.status!=="paused")throw new o("session_not_bound","Agent session has no active Room binding.");return i}touchAndPersist(e,i=new Date){e.updatedAt=i.toISOString(),this.persist()}load(){try{const e=u.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&&u.chmodSync(this.filePath,384);const i=JSON.parse(u.readFileSync(this.filePath,"utf8"));return T(i)}catch(e){if(k(e))return{schemaVersion:1,records:{}};throw e instanceof o?e:new o("corrupt_session_registry","Room Agent session registry is corrupt.")}}persist(){const e=P.dirname(this.filePath);u.mkdirSync(e,{recursive:!0,mode:448}),process.platform!=="win32"&&u.chmodSync(e,448);const i=`${this.filePath}.tmp-${process.pid}-${I.randomBytes(6).toString("hex")}`;try{if(u.writeFileSync(i,`${JSON.stringify(this.state,null,2)}
2
- `,{encoding:"utf8",mode:384,flag:"wx"}),process.platform!=="win32"&&u.chmodSync(i,384),process.platform==="win32"&&u.existsSync(this.filePath)){const s=u.lstatSync(this.filePath);if(!s.isFile()||s.isSymbolicLink())throw new o("unsafe_session_registry","Refusing to replace an unsafe Room session registry.");u.unlinkSync(this.filePath)}u.renameSync(i,this.filePath),process.platform!=="win32"&&u.chmodSync(this.filePath,384)}finally{try{u.unlinkSync(i)}catch{}}}}function p(t){return c(t.machineId,"machine_id"),_(t.runtimeAdapter),A(t.sourceSessionKey),I.createHash("sha256").update(`${t.machineId}\0${t.runtimeAdapter}\0${t.sourceSessionKey}`,"utf8").digest("hex")}function T(t){if(!t||typeof t!="object"||t.schemaVersion!==1)throw new o("corrupt_session_registry","Unsupported Room session registry.");const e=t.records;if(!e||typeof e!="object"||Array.isArray(e))throw new o("corrupt_session_registry","Room session records are invalid.");const i={};for(const[s,n]of Object.entries(e)){if(O(n),s!==n.registryKey||s!==p(n))throw new o("corrupt_session_registry","Room session registry key mismatch.");i[s]={...n,binding:n.binding?{...n.binding,activeActivation:n.binding.activeActivation??null,mentionContext:n.binding.mentionContext??[]}:null}}return{schemaVersion:1,records:i}}function O(t){if(!t||t.schemaVersion!==1)throw new o("corrupt_session_registry","Invalid registration.");if(c(t.machineId,"machine_id"),_(t.runtimeAdapter),A(t.sourceSessionKey),c(t.nianSessionId,"nian_session_id"),c(t.hostProcessIdentity,"host_process_identity"),t.panelContextKey!==t.nianSessionId)throw new o("corrupt_session_registry","Invalid panel context.");if(!Number.isInteger(t.registrationEpoch)||t.registrationEpoch<1)throw new o("corrupt_session_registry","Invalid registration epoch.");q(t.capabilities),t.binding&&B(t.binding)}function B(t){if(c(t.bindingId,"binding_id"),c(t.roomId,"room_id"),c(t.agentIdentityId,"agent_identity_id"),!b.has(t.status))throw new o("corrupt_session_registry","Invalid binding status.");if(t.status==="pending_approval"&&t.agentMembershipId!==null||(t.status==="active"||t.status==="paused")&&t.agentMembershipId===null)throw new o("corrupt_session_registry","Invalid Agent membership.");if(t.agentMembershipId!==null&&c(t.agentMembershipId,"agent_membership_id"),!S.has(t.activationMode))throw new o("corrupt_session_registry","Invalid activation mode.");if(h(t.lastDeliveredSequence),h(t.lastActivatedSequence),!Array.isArray(t.activationQueue)||t.activationQueue.length>f)throw new o("corrupt_session_registry","Invalid activation queue.");if(t.activationQueue.forEach(y),t.activeActivation!=null&&y(t.activeActivation),t.mentionContext!==void 0&&(!Array.isArray(t.mentionContext)||t.mentionContext.length>m))throw new o("corrupt_session_registry","Invalid mention context.");t.mentionContext?.forEach(w)}function w(t){if(h(t.sequence),typeof t.deliveryText!="string"||!t.deliveryText.trim()||t.deliveryText.length>24e3)throw new o("invalid_mention_context","Invalid mention context.")}function y(t){if(c(t.causeMessageId,"cause_message_id"),c(t.rootCauseMessageId,"root_cause_message_id"),h(t.sequence),!Number.isInteger(t.hopCount)||t.hopCount<0||t.hopCount>32)throw new o("invalid_hop_count","Invalid Agent activation hop count.");if(!Number.isInteger(t.attempts)||t.attempts<0||t.attempts>100)throw new o("invalid_activation_attempts","Invalid Agent activation attempts.");if(!Number.isFinite(new Date(t.enqueuedAt).getTime()))throw new o("invalid_activation_time","Invalid Agent activation time.");if(t.deliveryText!==void 0&&(typeof t.deliveryText!="string"||!t.deliveryText.trim()||t.deliveryText.length>24e3))throw new o("invalid_activation_text","Invalid Agent activation text.");if(t.systemPrompt!==void 0&&(typeof t.systemPrompt!="string"||!t.systemPrompt.trim()||t.systemPrompt.length>24e3))throw new o("invalid_activation_prompt","Invalid Agent activation prompt.");if(t.promptSnapshot!==void 0&&!E(t.promptSnapshot))throw new o("invalid_activation_prompt_snapshot","Invalid Agent activation prompt snapshot.");if(t.dispatchState!==void 0&&t.dispatchState!=="dispatching"&&t.dispatchState!=="interrupted")throw new o("invalid_activation_state","Invalid Agent activation state.");if(t.dispatchedAt!==void 0&&!Number.isFinite(new Date(t.dispatchedAt).getTime()))throw new o("invalid_activation_dispatch_time","Invalid Agent activation dispatch time.");if(t.dispatchState===void 0&&t.dispatchedAt!==void 0)throw new o("invalid_activation_dispatch_time","Queued Agent activation cannot have a dispatch time.");if(t.dispatchState==="interrupted"&&(t.interruptedAt===void 0||!Number.isFinite(new Date(t.interruptedAt).getTime())))throw new o("invalid_activation_interrupted_time","Invalid Agent activation interruption time.");if(t.dispatchState!=="interrupted"&&t.interruptedAt!==void 0)throw new o("invalid_activation_interrupted_time","Unexpected Agent activation interruption time.")}function E(t){if(!t||typeof t!="object"||Array.isArray(t))return!1;const e=t,i=s=>s===null||typeof s=="string"&&/^[a-f0-9]{64}$/.test(s);return typeof e.managedRoomPolicyVersion=="string"&&e.managedRoomPolicyVersion.length<=64&&i(e.sessionModeInstructionsHash)&&i(e.agentSystemPromptHash)&&typeof e.trustedRoomContextHash=="string"&&/^[a-f0-9]{64}$/.test(e.trustedRoomContextHash)}function x(t){return t.dispatchState==="interrupted"}function q(t){if(!t||["canIdentifySession","canBindAutomatically","canOpenEmbeddedRoomPage","canResumeIdleSession","canActivateWhileHostOpen"].some(i=>typeof t[i]!="boolean"))throw new o("invalid_session_capabilities","Invalid Agent session capabilities.")}function _(t){if(!M(t))throw new o("invalid_runtime_adapter","Unsupported Room runtime adapter.")}function A(t){if(!t||t.length>1024||/[\0\r\n]/.test(t))throw new o("invalid_source_session_key","Invalid native Agent session key.")}function c(t,e){if(!D.test(t))throw new o(`invalid_${e}`,`Invalid ${e}.`)}function h(t){if(!/^(0|[1-9]\d{0,19})$/.test(t))throw new o("invalid_room_sequence","Invalid Room sequence.")}function d(t){return structuredClone(t)}function k(t){return typeof t=="object"&&t!==null&&"code"in t&&t.code==="ENOENT"}export{j as RoomSessionRegistry,o as RoomSessionRegistryError,p as buildRoomSessionRegistryKey,x as isInterruptedRoomActivation};
1
+ import I from"node:crypto";import u from"node:fs";import{renameSyncWithRetry as P}from"../fs/atomic-rename.js";import M from"node:path";import{isRoomRuntimeAdapter as C}from"@shennian/wire";import{resolveShennianPath as D}from"../config/index.js";const f=500,m=20,T=/^[A-Za-z0-9_.:@/-]{1,512}$/,b=new Set(["pending_approval","active","paused","detached"]),S=new Set(["auto_follow","mention_only","paused"]);class o extends Error{code;constructor(e,i){super(i),this.code=e,this.name="RoomSessionRegistryError"}}class V{filePath;state;mutexTails=new Map;constructor(e=D("runtime","room-session-registry.json"),i=new Date){this.filePath=e,this.state=this.load(),this.markPersistedActivationsInterrupted(i)&&this.persist()}register(e){c(e.machineId,"machine_id"),_(e.runtimeAdapter),A(e.sourceSessionKey),c(e.nianSessionId,"nian_session_id"),c(e.hostProcessIdentity,"host_process_identity"),q(e.capabilities);const i=p(e),s=(e.now??new Date).toISOString(),n=this.state.records[i];if(n?.lifecycleState==="closed")throw new o("session_registration_closed","A retired session cannot be registered again.");if(Object.values(this.state.records).find(g=>g.registryKey!==i&&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 a=!!(n&&n.hostProcessIdentity!==e.hostProcessIdentity),l={schemaVersion:1,registryKey:i,machineId:e.machineId,runtimeAdapter:e.runtimeAdapter,sourceSessionKey:e.sourceSessionKey,nianSessionId:e.nianSessionId,panelContextKey:e.nianSessionId,hostProcessIdentity:e.hostProcessIdentity,registrationEpoch:n?n.registrationEpoch+(a?1:0):1,lifecycleState:"active",capabilities:{...e.capabilities},binding:n?.binding??null,registeredAt:n?.registeredAt??s,lastSeenAt:s,updatedAt:s};return this.state.records[i]=l,this.persist(),d(l)}getBySource(e){const i=this.state.records[p(e)];return i?d(i):null}getByNianSessionId(e){c(e,"nian_session_id");const i=Object.values(this.state.records).find(s=>s.nianSessionId===e&&s.lifecycleState!=="closed");return i?d(i):null}getByRegistryKey(e){if(!/^[a-f0-9]{64}$/.test(e))throw new o("invalid_registry_key","Invalid Room session registry key.");const i=this.state.records[e];return i?d(i):null}list(){return Object.values(this.state.records).sort((e,i)=>e.registeredAt.localeCompare(i.registeredAt)).map(d)}setBinding(e){const i=this.requireRecord(e.registryKey);for(const[a,l]of Object.entries({binding_id:e.bindingId,room_id:e.roomId,agent_identity_id:e.agentIdentityId}))c(l,a);if(!b.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&&c(e.agentMembershipId,"agent_membership_id");const s=e.activationMode??"auto_follow";if(!S.has(s))throw new o("invalid_activation_mode","Invalid activation mode.");if(i.binding&&i.binding.status!=="detached"&&(i.binding.bindingId!==e.bindingId||i.binding.roomId!==e.roomId))throw new o("session_already_bound","An Agent session can have only one active Room binding.");if(i.lifecycleState==="closed")throw new o("session_registration_closed","A retired session cannot be rebound.");e.initialSequence!==void 0&&h(e.initialSequence);const n=Object.values(this.state.records).filter(a=>a.registryKey!==i.registryKey&&a.lifecycleState!=="closed"&&a.binding?.bindingId===e.bindingId&&a.binding.status!=="detached");if(e.status!=="detached"&&n.length&&!e.replaceOwner)throw new o("binding_already_owned","A Room binding can have only one live session.");if(e.replaceOwner)for(const a of Object.values(this.state.records))a.registryKey===i.registryKey||a.binding?.bindingId!==e.bindingId||(a.lifecycleState="closed",a.registrationEpoch+=1,a.binding.status="detached",a.binding.activationMode="paused",a.binding.activationQueue=[],a.binding.activeActivation=null,a.binding.mentionContext=[],a.updatedAt=(e.now??new Date).toISOString());const r=i.binding?.activationMode;return i.binding={bindingId:e.bindingId,roomId:e.roomId,agentIdentityId:e.agentIdentityId,agentMembershipId:e.agentMembershipId,status:e.status,activationMode:s,lastDeliveredSequence:i.binding?.lastDeliveredSequence??e.initialSequence??"0",lastActivatedSequence:i.binding?.lastActivatedSequence??e.initialSequence??"0",activationQueue:i.binding?.activationQueue??[],activeActivation:i.binding?.activeActivation??null,mentionContext:r==="mention_only"&&s==="mention_only"?i.binding?.mentionContext??[]:[],loopBreaker:i.binding?.loopBreaker??{windowStartedAt:null,activationCount:0,pausedUntil:null}},this.touchAndPersist(i,e.now),d(i)}detachBinding(e,i=new Date){const s=this.requireRecord(e);return s.binding&&(s.binding.status="detached",s.binding.activationMode="paused",s.binding.activationQueue=[],s.binding.activeActivation=null,s.binding.mentionContext=[]),this.touchAndPersist(s,i),d(s)}advanceCursor(e){const i=this.requireBoundRecord(e.registryKey);h(e.sequence);const s=e.cursor==="delivered"?"lastDeliveredSequence":"lastActivatedSequence";if(BigInt(e.sequence)<BigInt(i.binding[s]))throw new o("cursor_regression","Room cursor cannot move backwards.");return i.binding[s]=e.sequence,this.touchAndPersist(i,e.now),d(i)}enqueueActivation(e,i,s=new Date,n=!1){const r=this.requireBoundRecord(e);y(i);const a=r.binding.activationQueue;if(!a.some(l=>l.causeMessageId===i.causeMessageId)){if(a.length>=f)throw new o("activation_queue_full","Agent activation queue is full.");a.push(d(i))}return n&&(r.binding.mentionContext=[]),this.touchAndPersist(r,s),d(r)}appendMentionContext(e,i,s=new Date){const n=this.requireBoundRecord(e);w(i);const r=n.binding.mentionContext??[];return r.some(a=>a.sequence===i.sequence)||(r.push(d(i)),n.binding.mentionContext=r.slice(-m)),this.touchAndPersist(n,s),d(n)}replaceMentionContext(e,i,s=new Date){const n=this.requireBoundRecord(e);for(const r of i)w(r);return n.binding.mentionContext=i.slice(-m).map(d),this.touchAndPersist(n,s),d(n)}retireLegacyActivationState(e,i=new Date){const s=this.requireBoundRecord(e);return s.binding.activationQueue=[],s.binding.activeActivation=null,this.touchAndPersist(s,i),d(s)}dequeueActivation(e,i,s=new Date){const n=this.requireBoundRecord(e);return n.binding.activationQueue=n.binding.activationQueue.filter(r=>r.causeMessageId!==i),this.touchAndPersist(n,s),d(n)}markActivationDispatching(e,i,s=new Date){const n=this.requireBoundRecord(e),r=n.binding.activationQueue.find(a=>a.causeMessageId===i);if(!r)throw new o("activation_not_found","Agent activation is unavailable.");return n.binding.activeActivation={...d(r),dispatchState:"dispatching",dispatchedAt:s.toISOString(),interruptedAt:void 0},n.binding.activationQueue=n.binding.activationQueue.filter(a=>a.causeMessageId!==i),this.touchAndPersist(n,s),d(n)}clearActiveActivation(e,i=new Date){const s=this.requireBoundRecord(e);return s.binding.activeActivation=null,this.touchAndPersist(s,i),d(s)}retryActiveActivation(e){const i=this.requireBoundRecord(e.registryKey),s=i.binding.activeActivation;if(!s)return{queued:!1,attempts:0};if(x(s))throw new o("activation_interrupted_requires_explicit_retry","Interrupted Agent activation requires an explicit retry.");return this.requeueActiveActivation(i,e)}requeueActiveActivation(e,i){const s=e.binding.activeActivation,n=i.now??new Date,{dispatchState:r,dispatchedAt:a,interruptedAt:l,...g}=s,v={...g,attempts:s.attempts+1};return e.binding.activeActivation=null,v.attempts<i.maxAttempts?(e.binding.activationQueue.some(R=>R.causeMessageId===v.causeMessageId)||e.binding.activationQueue.unshift(v),this.touchAndPersist(e,n),{queued:!0,attempts:v.attempts}):(e.binding.loopBreaker.pausedUntil=new Date(n.getTime()+i.pauseMs).toISOString(),this.touchAndPersist(e,n),{queued:!1,attempts:v.attempts})}markPersistedActivationsInterrupted(e){const i=e.toISOString();let s=!1;for(const n of Object.values(this.state.records)){const r=n.binding?.activeActivation;!r||x(r)||(r.dispatchState="interrupted",r.interruptedAt=i,n.updatedAt=i,s=!0)}return s}claimActivation(e){const i=this.requireBoundRecord(e.registryKey),s=e.now??new Date,n=i.binding.loopBreaker;return e.causeMessageId&&n.claimedMessageIds?.includes(e.causeMessageId)?{allowed:!0,pausedUntil:null}: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(i,s),{allowed:!1,pausedUntil:n.pausedUntil,newlyPaused:!0}):(n.activationCount+=1,e.causeMessageId&&(n.claimedMessageIds=[...n.claimedMessageIds??[],e.causeMessageId].slice(-f)),this.touchAndPersist(i,s),{allowed:!0,pausedUntil:null}))}updateLifecycle(e,i,s=new Date){const n=this.requireRecord(e);if(n.lifecycleState==="closed"&&i!=="closed")throw new o("session_registration_closed","A retired session cannot be reopened.");return n.lifecycleState!==i&&(n.registrationEpoch+=1),n.lifecycleState=i,i==="closed"&&n.binding&&(n.binding.status="detached",n.binding.activationMode="paused",n.binding.activationQueue=[],n.binding.activeActivation=null,n.binding.mentionContext=[]),this.touchAndPersist(n,s),d(n)}async runExclusive(e,i){this.requireRecord(e);const s=this.mutexTails.get(e)??Promise.resolve();let n;const r=new Promise(l=>{n=l}),a=s.then(()=>r);this.mutexTails.set(e,a),await s;try{return await i()}finally{n(),this.mutexTails.get(e)===a&&this.mutexTails.delete(e)}}requireRecord(e){const i=this.state.records[e];if(!i)throw new o("session_registration_not_found","Agent session is not registered.");return i}requireBoundRecord(e){const i=this.requireRecord(e);if(!i.binding||i.binding.status!=="active"&&i.binding.status!=="paused")throw new o("session_not_bound","Agent session has no active Room binding.");return i}touchAndPersist(e,i=new Date){e.updatedAt=i.toISOString(),this.persist()}load(){try{const e=u.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&&u.chmodSync(this.filePath,384);const i=JSON.parse(u.readFileSync(this.filePath,"utf8"));return O(i)}catch(e){if(Q(e))return{schemaVersion:1,records:{}};throw e instanceof o?e:new o("corrupt_session_registry","Room Agent session registry is corrupt.")}}persist(){const e=M.dirname(this.filePath);u.mkdirSync(e,{recursive:!0,mode:448}),process.platform!=="win32"&&u.chmodSync(e,448);const i=`${this.filePath}.tmp-${process.pid}-${I.randomBytes(6).toString("hex")}`;try{if(u.writeFileSync(i,`${JSON.stringify(this.state,null,2)}
2
+ `,{encoding:"utf8",mode:384,flag:"wx"}),process.platform!=="win32"&&u.chmodSync(i,384),process.platform==="win32"&&u.existsSync(this.filePath)){const s=u.lstatSync(this.filePath);if(!s.isFile()||s.isSymbolicLink())throw new o("unsafe_session_registry","Refusing to replace an unsafe Room session registry.");u.unlinkSync(this.filePath)}P(i,this.filePath),process.platform!=="win32"&&u.chmodSync(this.filePath,384)}finally{try{u.unlinkSync(i)}catch{}}}}function p(t){return c(t.machineId,"machine_id"),_(t.runtimeAdapter),A(t.sourceSessionKey),I.createHash("sha256").update(`${t.machineId}\0${t.runtimeAdapter}\0${t.sourceSessionKey}`,"utf8").digest("hex")}function O(t){if(!t||typeof t!="object"||t.schemaVersion!==1)throw new o("corrupt_session_registry","Unsupported Room session registry.");const e=t.records;if(!e||typeof e!="object"||Array.isArray(e))throw new o("corrupt_session_registry","Room session records are invalid.");const i={};for(const[s,n]of Object.entries(e)){if(B(n),s!==n.registryKey||s!==p(n))throw new o("corrupt_session_registry","Room session registry key mismatch.");i[s]={...n,binding:n.binding?{...n.binding,activeActivation:n.binding.activeActivation??null,mentionContext:n.binding.mentionContext??[]}:null}}return{schemaVersion:1,records:i}}function B(t){if(!t||t.schemaVersion!==1)throw new o("corrupt_session_registry","Invalid registration.");if(c(t.machineId,"machine_id"),_(t.runtimeAdapter),A(t.sourceSessionKey),c(t.nianSessionId,"nian_session_id"),c(t.hostProcessIdentity,"host_process_identity"),t.panelContextKey!==t.nianSessionId)throw new o("corrupt_session_registry","Invalid panel context.");if(!Number.isInteger(t.registrationEpoch)||t.registrationEpoch<1)throw new o("corrupt_session_registry","Invalid registration epoch.");q(t.capabilities),t.binding&&E(t.binding)}function E(t){if(c(t.bindingId,"binding_id"),c(t.roomId,"room_id"),c(t.agentIdentityId,"agent_identity_id"),!b.has(t.status))throw new o("corrupt_session_registry","Invalid binding status.");if(t.status==="pending_approval"&&t.agentMembershipId!==null||(t.status==="active"||t.status==="paused")&&t.agentMembershipId===null)throw new o("corrupt_session_registry","Invalid Agent membership.");if(t.agentMembershipId!==null&&c(t.agentMembershipId,"agent_membership_id"),!S.has(t.activationMode))throw new o("corrupt_session_registry","Invalid activation mode.");if(h(t.lastDeliveredSequence),h(t.lastActivatedSequence),!Array.isArray(t.activationQueue)||t.activationQueue.length>f)throw new o("corrupt_session_registry","Invalid activation queue.");if(t.activationQueue.forEach(y),t.activeActivation!=null&&y(t.activeActivation),t.mentionContext!==void 0&&(!Array.isArray(t.mentionContext)||t.mentionContext.length>m))throw new o("corrupt_session_registry","Invalid mention context.");t.mentionContext?.forEach(w)}function w(t){if(h(t.sequence),typeof t.deliveryText!="string"||!t.deliveryText.trim()||t.deliveryText.length>24e3)throw new o("invalid_mention_context","Invalid mention context.")}function y(t){if(c(t.causeMessageId,"cause_message_id"),c(t.rootCauseMessageId,"root_cause_message_id"),h(t.sequence),!Number.isInteger(t.hopCount)||t.hopCount<0||t.hopCount>32)throw new o("invalid_hop_count","Invalid Agent activation hop count.");if(!Number.isInteger(t.attempts)||t.attempts<0||t.attempts>100)throw new o("invalid_activation_attempts","Invalid Agent activation attempts.");if(!Number.isFinite(new Date(t.enqueuedAt).getTime()))throw new o("invalid_activation_time","Invalid Agent activation time.");if(t.deliveryText!==void 0&&(typeof t.deliveryText!="string"||!t.deliveryText.trim()||t.deliveryText.length>24e3))throw new o("invalid_activation_text","Invalid Agent activation text.");if(t.systemPrompt!==void 0&&(typeof t.systemPrompt!="string"||!t.systemPrompt.trim()||t.systemPrompt.length>24e3))throw new o("invalid_activation_prompt","Invalid Agent activation prompt.");if(t.promptSnapshot!==void 0&&!k(t.promptSnapshot))throw new o("invalid_activation_prompt_snapshot","Invalid Agent activation prompt snapshot.");if(t.dispatchState!==void 0&&t.dispatchState!=="dispatching"&&t.dispatchState!=="interrupted")throw new o("invalid_activation_state","Invalid Agent activation state.");if(t.dispatchedAt!==void 0&&!Number.isFinite(new Date(t.dispatchedAt).getTime()))throw new o("invalid_activation_dispatch_time","Invalid Agent activation dispatch time.");if(t.dispatchState===void 0&&t.dispatchedAt!==void 0)throw new o("invalid_activation_dispatch_time","Queued Agent activation cannot have a dispatch time.");if(t.dispatchState==="interrupted"&&(t.interruptedAt===void 0||!Number.isFinite(new Date(t.interruptedAt).getTime())))throw new o("invalid_activation_interrupted_time","Invalid Agent activation interruption time.");if(t.dispatchState!=="interrupted"&&t.interruptedAt!==void 0)throw new o("invalid_activation_interrupted_time","Unexpected Agent activation interruption time.")}function k(t){if(!t||typeof t!="object"||Array.isArray(t))return!1;const e=t,i=s=>s===null||typeof s=="string"&&/^[a-f0-9]{64}$/.test(s);return typeof e.managedRoomPolicyVersion=="string"&&e.managedRoomPolicyVersion.length<=64&&i(e.sessionModeInstructionsHash)&&i(e.agentSystemPromptHash)&&typeof e.trustedRoomContextHash=="string"&&/^[a-f0-9]{64}$/.test(e.trustedRoomContextHash)}function x(t){return t.dispatchState==="interrupted"}function q(t){if(!t||["canIdentifySession","canBindAutomatically","canOpenEmbeddedRoomPage","canResumeIdleSession","canActivateWhileHostOpen"].some(i=>typeof t[i]!="boolean"))throw new o("invalid_session_capabilities","Invalid Agent session capabilities.")}function _(t){if(!C(t))throw new o("invalid_runtime_adapter","Unsupported Room runtime adapter.")}function A(t){if(!t||t.length>1024||/[\0\r\n]/.test(t))throw new o("invalid_source_session_key","Invalid native Agent session key.")}function c(t,e){if(!T.test(t))throw new o(`invalid_${e}`,`Invalid ${e}.`)}function h(t){if(!/^(0|[1-9]\d{0,19})$/.test(t))throw new o("invalid_room_sequence","Invalid Room sequence.")}function d(t){return structuredClone(t)}function Q(t){return typeof t=="object"&&t!==null&&"code"in t&&t.code==="ENOENT"}export{V as RoomSessionRegistry,o as RoomSessionRegistryError,p as buildRoomSessionRegistryKey,x as isInterruptedRoomActivation};
@@ -1 +1 @@
1
- import c from"node:fs/promises";import a from"node:path";import{managedAgentExecutionMode as y,parseManagedAgentPermissionPolicy as b,resolveManagedAgentEffectivePermissionPolicy as _}from"@shennian/wire";import{getManagedAgentBaseEnvKeys as k,getManagedAgentProviderEnvKeys as u,getManagedAgentSessionEnvKeys as d}from"../agent-env.js";import{collectPolicyEnforcementGaps as x,resolveManagedAgentRuntimeProtection as P,resolveManagedAgentSecurityProfile as w}from"./managed-agent-runtime.js";import{resolveProtectedShennianDirectories as A}from"./managed-agent-credential-paths.js";function V(e){return!!(e&&(e.executionMode==="native-full"||e.requestedPolicy.executionMode==="native-full"))}function z(e){return{...e.enforcement,effectivePolicy:{version:e.adapterPolicy.version,executionMode:e.executionMode??"restricted",fileAccess:e.adapterPolicy.fileAccess,externalCapabilities:{...e.adapterPolicy.externalCapabilities}}}}async function I(e){const r=b(e.policy);if(!r)return{ok:!1,errorCode:"managed_agent_policy_invalid"};const t=y(r);if((t==="native-full"||r.fileAccess==="host-full")&&!e.hostFullAccessConfirmed)return{ok:!1,errorCode:"managed_agent_host_full_confirmation_required"};const o=await h(e.workDir,e.resolveWorkDir);if(!o.ok)return o;const i=C(e),n=P(i),l=w(i),s=t==="restricted"?x(r,l):{unavailableCapabilities:[],overgrantedCapabilities:[]},v=g([...t==="restricted"&&n.level!=="hard"?["runtime.hardIsolation"]:[],...t==="restricted"?n.unsupportedCapabilities:[],...s.unavailableCapabilities,...s.overgrantedCapabilities]),p=_(r,n),m={level:n.level,effectivePolicy:p,unsupportedCapabilities:v,runtimeVersion:n.runtimeVersion,platform:n.platform,...n.executableFingerprint?{executableFingerprint:n.executableFingerprint}:{}};return{ok:!0,value:{requestedPolicy:F(r),executionMode:t,enforcement:m,adapterPolicy:{version:r.version,workDir:o.workDir,fileAccess:r.fileAccess,externalCapabilities:{...r.externalCapabilities},denySensitiveFiles:!0,failClosed:!0},environmentPolicy:{inheritDaemonEnv:!1,loadLoginShell:!1,providerEnvKeys:[...u(e.agentType)],sessionEnvKeys:[...d(e.agentType)],shellSubprocess:{inherit:"none",allowedEnvKeys:g([...k(e.platform),...d(e.agentType)]),deniedEnvKeys:[...u(e.agentType)]}}}}}function C(e){return{agentType:e.agentType,runtimeVersion:e.runtimeVersion,platform:e.platform,isWsl:e.isWsl,executableFingerprint:e.executableFingerprint,requireExecutableFingerprint:e.requireExecutableFingerprint,securityDeclaration:e.securityDeclaration}}async function h(e,r){const t=e.trim();if(!t||!a.isAbsolute(t))return{ok:!1,errorCode:"managed_agent_workdir_invalid"};try{const o=await(r??M)(t);if(!o||!a.isAbsolute(o))return{ok:!1,errorCode:"managed_agent_workdir_invalid"};const i=a.normalize(o);return await E(i)?{ok:!1,errorCode:"managed_agent_workdir_invalid"}:{ok:!0,workDir:i}}catch{return{ok:!1,errorCode:"managed_agent_workdir_unavailable"}}}async function M(e){const r=await c.realpath(e);if(!(await c.stat(r)).isDirectory())throw new Error("managed_agent_workdir_not_directory");return r}async function E(e){return A().some(t=>D(e,t,process.platform))}function D(e,r,t){const o=l=>{const s=a.resolve(l);return t==="win32"?s.toLowerCase():s},i=o(e),n=o(r);return f(i,n)||f(n,i)}function f(e,r){const t=a.relative(e,r);return t===""||!t.startsWith("..")&&!a.isAbsolute(t)}function F(e){return{...e,externalCapabilities:{...e.externalCapabilities}}}function g(e){return[...new Set(e)]}export{I as compileManagedAgentPolicy,V as isManagedAgentNativeFull,z as resolveManagedAgentAdapterEnforcement,h as validateManagedAgentWorkDir};
1
+ import c from"node:fs/promises";import a from"node:path";import{managedAgentExecutionMode as y,parseManagedAgentPermissionPolicy as _,resolveManagedAgentEffectivePermissionPolicy as b}from"@shennian/wire";import{getManagedAgentBaseEnvKeys as k,getManagedAgentProviderEnvKeys as d,getManagedAgentSessionEnvKeys as u}from"../agent-env.js";import{collectPolicyEnforcementGaps as C,isRestrictedRoomChatUnsupported as x,resolveManagedAgentRuntimeProtection as P,resolveManagedAgentSecurityProfile as w}from"./managed-agent-runtime.js";import{resolveProtectedShennianDirectories as A}from"./managed-agent-credential-paths.js";function z(e){return!!(e&&(e.executionMode==="native-full"||e.requestedPolicy.executionMode==="native-full"))}function I(e){return{...e.enforcement,effectivePolicy:{version:e.adapterPolicy.version,executionMode:e.executionMode??"restricted",fileAccess:e.adapterPolicy.fileAccess,externalCapabilities:{...e.adapterPolicy.externalCapabilities}}}}async function B(e){const r=_(e.policy);if(!r)return{ok:!1,errorCode:"managed_agent_policy_invalid"};const t=y(r);if((t==="native-full"||r.fileAccess==="host-full")&&!e.hostFullAccessConfirmed)return{ok:!1,errorCode:"managed_agent_host_full_confirmation_required"};if(t==="restricted"&&x(e.agentType))return{ok:!1,errorCode:"managed_agent_restricted_room_unsupported"};const o=await M(e.workDir,e.resolveWorkDir);if(!o.ok)return o;const i=h(e),n=P(i),l=w(i),s=t==="restricted"?C(r,l):{unavailableCapabilities:[],overgrantedCapabilities:[]},v=g([...t==="restricted"&&n.level!=="hard"?["runtime.hardIsolation"]:[],...t==="restricted"?n.unsupportedCapabilities:[],...s.unavailableCapabilities,...s.overgrantedCapabilities]),p=b(r,n),m={level:n.level,effectivePolicy:p,unsupportedCapabilities:v,runtimeVersion:n.runtimeVersion,platform:n.platform,...n.executableFingerprint?{executableFingerprint:n.executableFingerprint}:{}};return{ok:!0,value:{requestedPolicy:q(r),executionMode:t,enforcement:m,adapterPolicy:{version:r.version,workDir:o.workDir,fileAccess:r.fileAccess,externalCapabilities:{...r.externalCapabilities},denySensitiveFiles:!0,failClosed:!0},environmentPolicy:{inheritDaemonEnv:!1,loadLoginShell:!1,providerEnvKeys:[...d(e.agentType)],sessionEnvKeys:[...u(e.agentType)],shellSubprocess:{inherit:"none",allowedEnvKeys:g([...k(e.platform),...u(e.agentType)]),deniedEnvKeys:[...d(e.agentType)]}}}}}function h(e){return{agentType:e.agentType,runtimeVersion:e.runtimeVersion,platform:e.platform,isWsl:e.isWsl,executableFingerprint:e.executableFingerprint,requireExecutableFingerprint:e.requireExecutableFingerprint,securityDeclaration:e.securityDeclaration}}async function M(e,r){const t=e.trim();if(!t||!a.isAbsolute(t))return{ok:!1,errorCode:"managed_agent_workdir_invalid"};try{const o=await(r??E)(t);if(!o||!a.isAbsolute(o))return{ok:!1,errorCode:"managed_agent_workdir_invalid"};const i=a.normalize(o);return await D(i)?{ok:!1,errorCode:"managed_agent_workdir_invalid"}:{ok:!0,workDir:i}}catch{return{ok:!1,errorCode:"managed_agent_workdir_unavailable"}}}async function E(e){const r=await c.realpath(e);if(!(await c.stat(r)).isDirectory())throw new Error("managed_agent_workdir_not_directory");return r}async function D(e){return A().some(t=>F(e,t,process.platform))}function F(e,r,t){const o=l=>{const s=a.resolve(l);return t==="win32"?s.toLowerCase():s},i=o(e),n=o(r);return f(i,n)||f(n,i)}function f(e,r){const t=a.relative(e,r);return t===""||!t.startsWith("..")&&!a.isAbsolute(t)}function q(e){return{...e,externalCapabilities:{...e.externalCapabilities}}}function g(e){return[...new Set(e)]}export{B as compileManagedAgentPolicy,z as isManagedAgentNativeFull,I as resolveManagedAgentAdapterEnforcement,M as validateManagedAgentWorkDir};
@@ -1 +1 @@
1
- import g from"node:fs";import p from"node:os";const l={"workspace-read":"fileAccess.workspaceRead","workspace-write":"fileAccess.workspaceWrite","host-full":"fileAccess.hostFull"},o={commandNetwork:"externalCapabilities.commandNetwork",webSearch:"externalCapabilities.webSearch",browser:"externalCapabilities.browser",computerUse:"externalCapabilities.computerUse",mcp:"externalCapabilities.mcp"},b=[...Object.values(l),...Object.values(o)],c=new Map;function h(e,t){c.set(e,t)}function E(e){c.delete(e)}function R(e){const t=e.runtimeVersion?.trim()||"unknown",i=x(e),r=e.securityDeclaration??c.get(e.agentType),n=r?.platforms[i];return!r||!n||t==="unknown"||e.requireExecutableFingerprint&&!e.executableFingerprint||r.minimumRuntimeVersion&&!u(t,r.minimumRuntimeVersion)||r.maximumRuntimeVersionExclusive&&u(t,r.maximumRuntimeVersionExclusive)?{level:"unverified",runtimeVersion:t,platform:i,...e.executableFingerprint?{executableFingerprint:e.executableFingerprint}:{},unsupportedCapabilities:[...b]}:{level:n.level,runtimeVersion:t,platform:i,...e.executableFingerprint?{executableFingerprint:e.executableFingerprint}:{},unsupportedCapabilities:A(n),capabilityMatrix:{fileAccess:{...n.fileAccess},externalCapabilities:Object.fromEntries(Object.entries(n.externalCapabilities).map(([s,a])=>[s,{...a}]))}}}function S(e){const t=e.runtimeVersion?.trim();if(!t)return null;const i=e.securityDeclaration??c.get(e.agentType);return!i||i.minimumRuntimeVersion&&!u(t,i.minimumRuntimeVersion)||i.maximumRuntimeVersionExclusive&&u(t,i.maximumRuntimeVersionExclusive)?null:i.platforms[x(e)]??null}function P(e,t){if(!t)return{overgrantedCapabilities:[...b],unavailableCapabilities:[]};const i=[...t.knownGaps??[]],r=[];t.fileAccess[e.fileAccess]||(e.fileAccess!=="host-full"&&t.fileAccess["host-full"]?i.push(l[e.fileAccess]):r.push(l[e.fileAccess]));for(const[n,s]of Object.entries(e.externalCapabilities)){const a=t.externalCapabilities[n];s&&!a.allow&&r.push(o[n]),!s&&!a.deny&&i.push(o[n])}return{overgrantedCapabilities:m(i),unavailableCapabilities:m(r)}}function A(e){const t=[...e.knownGaps??[]];for(const[i,r]of Object.entries(e.fileAccess))r||t.push(l[i]);for(const[i,r]of Object.entries(e.externalCapabilities))(!r.allow||!r.deny)&&t.push(o[i]);return m(t)}function x(e){const t=e.platform??process.platform;return t==="linux"&&(e.isWsl??C())?"wsl":t==="darwin"||t==="linux"||t==="win32"?t:"unsupported"}function C(){if(process.platform!=="linux")return!1;if(process.env.WSL_DISTRO_NAME||process.env.WSL_INTEROP)return!0;try{return/microsoft|wsl/i.test(`${p.release()} ${g.readFileSync("/proc/version","utf-8")}`)}catch{return/microsoft|wsl/i.test(p.release())}}function u(e,t){const i=d(e),r=d(t);if(!i||!r)return!1;const n=Math.max(i.length,r.length);for(let s=0;s<n;s+=1){const a=i[s]??0,f=r[s]??0;if(a!==f)return a>f}return!0}function d(e){const t=e.trim().match(/(?:^|\s|v)(\d+(?:\.\d+){0,3})(?:[-+\s]|$)/i);return t?.[1]?t[1].split(".").map(Number):null}function m(e){return[...new Set(e)]}export{b as ALL_MANAGED_AGENT_POLICY_CAPABILITIES,P as collectPolicyEnforcementGaps,h as registerManagedAgentRuntimeSecurity,R as resolveManagedAgentRuntimeProtection,S as resolveManagedAgentSecurityProfile,E as unregisterManagedAgentRuntimeSecurity};
1
+ import A from"node:fs";import f from"node:os";const o={"workspace-read":"fileAccess.workspaceRead","workspace-write":"fileAccess.workspaceWrite","host-full":"fileAccess.hostFull"},l={commandNetwork:"externalCapabilities.commandNetwork",webSearch:"externalCapabilities.webSearch",browser:"externalCapabilities.browser",computerUse:"externalCapabilities.computerUse",mcp:"externalCapabilities.mcp"},b=[...Object.values(o),...Object.values(l)],c=new Map;function h(e,t){c.set(e,t)}function E(e){c.delete(e)}const C=new Set(["opencode"]);function S(e){return C.has(e)}function O(e){const t=e.runtimeVersion?.trim()||"unknown",i=x(e),r=e.securityDeclaration??c.get(e.agentType),n=r?.platforms[i];return!r||!n||t==="unknown"||e.requireExecutableFingerprint&&!e.executableFingerprint||r.minimumRuntimeVersion&&!u(t,r.minimumRuntimeVersion)||r.maximumRuntimeVersionExclusive&&u(t,r.maximumRuntimeVersionExclusive)?{level:"unverified",runtimeVersion:t,platform:i,...e.executableFingerprint?{executableFingerprint:e.executableFingerprint}:{},unsupportedCapabilities:[...b]}:{level:n.level,runtimeVersion:t,platform:i,...e.executableFingerprint?{executableFingerprint:e.executableFingerprint}:{},unsupportedCapabilities:g(n),capabilityMatrix:{fileAccess:{...n.fileAccess},externalCapabilities:Object.fromEntries(Object.entries(n.externalCapabilities).map(([s,a])=>[s,{...a}]))}}}function P(e){const t=e.runtimeVersion?.trim();if(!t)return null;const i=e.securityDeclaration??c.get(e.agentType);return!i||i.minimumRuntimeVersion&&!u(t,i.minimumRuntimeVersion)||i.maximumRuntimeVersionExclusive&&u(t,i.maximumRuntimeVersionExclusive)?null:i.platforms[x(e)]??null}function _(e,t){if(!t)return{overgrantedCapabilities:[...b],unavailableCapabilities:[]};const i=[...t.knownGaps??[]],r=[];t.fileAccess[e.fileAccess]||(e.fileAccess!=="host-full"&&t.fileAccess["host-full"]?i.push(o[e.fileAccess]):r.push(o[e.fileAccess]));for(const[n,s]of Object.entries(e.externalCapabilities)){const a=t.externalCapabilities[n];s&&!a.allow&&r.push(l[n]),!s&&!a.deny&&i.push(l[n])}return{overgrantedCapabilities:m(i),unavailableCapabilities:m(r)}}function g(e){const t=[...e.knownGaps??[]];for(const[i,r]of Object.entries(e.fileAccess))r||t.push(o[i]);for(const[i,r]of Object.entries(e.externalCapabilities))(!r.allow||!r.deny)&&t.push(l[i]);return m(t)}function x(e){const t=e.platform??process.platform;return t==="linux"&&(e.isWsl??w())?"wsl":t==="darwin"||t==="linux"||t==="win32"?t:"unsupported"}function w(){if(process.platform!=="linux")return!1;if(process.env.WSL_DISTRO_NAME||process.env.WSL_INTEROP)return!0;try{return/microsoft|wsl/i.test(`${f.release()} ${A.readFileSync("/proc/version","utf-8")}`)}catch{return/microsoft|wsl/i.test(f.release())}}function u(e,t){const i=d(e),r=d(t);if(!i||!r)return!1;const n=Math.max(i.length,r.length);for(let s=0;s<n;s+=1){const a=i[s]??0,p=r[s]??0;if(a!==p)return a>p}return!0}function d(e){const t=e.trim().match(/(?:^|\s|v)(\d+(?:\.\d+){0,3})(?:[-+\s]|$)/i);return t?.[1]?t[1].split(".").map(Number):null}function m(e){return[...new Set(e)]}export{b as ALL_MANAGED_AGENT_POLICY_CAPABILITIES,_ as collectPolicyEnforcementGaps,S as isRestrictedRoomChatUnsupported,h as registerManagedAgentRuntimeSecurity,O as resolveManagedAgentRuntimeProtection,P as resolveManagedAgentSecurityProfile,E as unregisterManagedAgentRuntimeSecurity};
@@ -1,2 +1,2 @@
1
- import M from"node:crypto";import o from"node:fs";import s from"node:path";import{serializeFsTransferManifest as X}from"@shennian/wire";const Y=600*1e3,K=1440*60*1e3,U=1024*1024,D=1024*1024*1024,q=2e3,H=1024*1024*1024,S=1,$="__file__",C=/^[a-f0-9]{64}$/i,O=/^[A-Za-z0-9_-]{8,128}$/;function l(e){return s.join(e,".uploads")}function w(e){return s.join(l(e),".staging")}function E(e){return s.join(l(e),".metadata")}function m(e,r){return s.join(w(e),r)}function T(e,r){return s.join(m(e,r),"manifest.json")}function A(e,r){return s.join(E(e),`${r.toLowerCase()}.json`)}function d(e,r){const t=s.relative(s.resolve(e),s.resolve(r));if(!(t===""||!t.startsWith("..")&&!s.isAbsolute(t)))throw new Error("attachment_path_outside_uploads")}function z(e){if(!O.test(e))throw new Error("invalid_transfer_id")}function g(e){if(!C.test(e))throw new Error("invalid_attachment_sha256");return e.toLowerCase()}function V(e){if(!e||e.trim()!==e||s.basename(e)!==e||e==="."||e==="..")throw new Error("invalid_attachment_name");if(Array.from(e).some(r=>r.charCodeAt(0)<=31))throw new Error("invalid_attachment_name");return e}function p(e){if(!e||e.trim()!==e||e.includes("\\"))throw new Error("invalid_attachment_relative_path");if(s.posix.isAbsolute(e)||s.win32.isAbsolute(e))throw new Error("invalid_attachment_relative_path");if(e.split("/").some(t=>!t||t==="."||t===".."))throw new Error("invalid_attachment_relative_path");return e}function L(e,r,t){if(!Number.isSafeInteger(e)||e<0||e>r)throw new Error(`invalid_${t}`);return e}function k(e,r){o.mkdirSync(s.dirname(e),{recursive:!0});const t=`${e}.${process.pid}.tmp`;o.writeFileSync(t,`${JSON.stringify(r)}
2
- `,{mode:384}),o.renameSync(t,e)}function u(e){try{return JSON.parse(o.readFileSync(e,"utf8"))}catch{return null}}function P(e){const r=M.createHash("sha256"),t=o.openSync(e,"r"),n=Buffer.allocUnsafe(256*1024);try{let i=0;do i=o.readSync(t,n,0,n.length,null),i>0&&r.update(n.subarray(0,i));while(i>0)}finally{o.closeSync(t)}return r.digest("hex")}function x(e){return M.createHash("sha256").update(X(e)).digest("hex")}function y(e,r,t){const n=s.join(m(e,r),"payload"),i=t?s.join(n,...t.split("/")):n;return d(m(e,r),i),i}function B(e,r){z(r);const t=u(T(e,r));if(!t||t.version!==S||t.transferId!==r||s.resolve(t.rootPath)!==s.resolve(e))throw new Error("invalid_transfer_id");return d(l(e),t.targetPath),t}function F(e){k(T(e.rootPath,e.transferId),e)}function Z(e,r){if(!o.existsSync(e))return e;const t=s.parse(e);for(let n=0;n<1e3;n+=1){const i=n===0?r.slice(0,10):`${r.slice(0,10)}-${n+1}`,a=s.join(t.dir,`${t.name}-${i}${t.ext}`);if(!o.existsSync(a))return a}throw new Error("attachment_name_conflict")}function N(e,r){const t=u(A(e,r));if(!t||t.version!==S||t.sha256!==r)return null;try{d(l(e),t.path);const n=o.statSync(t.path);if(t.kind==="file"&&(!n.isFile()||n.size!==t.byteSize||P(t.path)!==t.sha256))return null;if(t.kind==="folder"){if(!n.isDirectory()||!t.files?.length)return null;for(const i of t.files){const a=s.join(t.path,...p(i.relativePath).split("/"));if(d(t.path,a),!o.statSync(a).isFile()||o.statSync(a).size!==i.size||P(a)!==i.sha256)return null}if(x(t.files)!==t.sha256)return null}return t}catch{return null}}function R(e,r){return{transferId:e.transferId,path:e.path,name:e.name,mimeType:e.mimeType,kind:e.kind,byteSize:e.byteSize,sha256:e.sha256,reused:r}}function G(e){if(e.kind==="file")return[{relativePath:$,size:e.totalSize,sha256:e.sha256,receivedSize:0}];if(!e.manifest?.length||e.manifest.length>q)throw new Error("invalid_attachment_manifest");const r=new Set;let t=0;const n=e.manifest.map(i=>{const a=p(i.relativePath);if(r.has(a))throw new Error("duplicate_attachment_relative_path");r.add(a);const f=L(i.size,D,"attachment_size");if(t+=f,t>H)throw new Error("attachment_folder_too_large");return{...i,relativePath:a,size:f,sha256:g(i.sha256),receivedSize:0}});if(t!==e.totalSize)throw new Error("attachment_total_size_mismatch");if(x(n)!==e.sha256)throw new Error("attachment_manifest_hash_mismatch");return n}function ae(e,r=Date.now()){z(e.transferId);const t=V(e.name),n=g(e.sha256),i=e.kind==="folder"?H:D,a=L(e.totalSize,i,"attachment_total_size");if(!e.rootPath||!s.isAbsolute(e.rootPath))throw new Error("invalid_attachment_root");if(!e.mimeType||e.mimeType.length>255)throw new Error("invalid_attachment_mime_type");Q(e.rootPath,r);const f=N(e.rootPath,n);if(f&&f.kind===e.kind&&f.byteSize===a)return{transferId:e.transferId,path:f.path,reused:!0,offsets:{},attachment:{...R(f,!0),transferId:e.transferId}};const c=u(T(e.rootPath,e.transferId));if(c){if(c.sha256!==n||c.totalSize!==a||c.name!==t||c.kind!==e.kind||c.mimeType!==e.mimeType)throw new Error("transfer_identity_conflict");return c.lastActivityAt=r,F(c),{transferId:e.transferId,path:c.targetPath,reused:!1,offsets:Object.fromEntries(c.files.map(_=>[_.relativePath,_.receivedSize]))}}const h=G({...e,name:t,sha256:n,totalSize:a}),v=l(e.rootPath);o.mkdirSync(w(e.rootPath),{recursive:!0,mode:448}),o.mkdirSync(E(e.rootPath),{recursive:!0,mode:448});const W=m(e.rootPath,e.transferId);o.mkdirSync(W,{recursive:!1,mode:448});const b=s.join(v,t);d(v,b);const I=Z(b,n);if(e.kind==="folder"){o.mkdirSync(y(e.rootPath,e.transferId),{recursive:!0,mode:448});for(const _ of h){const j=y(e.rootPath,e.transferId,_.relativePath);o.mkdirSync(s.dirname(j),{recursive:!0,mode:448}),o.writeFileSync(j,Buffer.alloc(0),{mode:384})}}else o.writeFileSync(y(e.rootPath,e.transferId),Buffer.alloc(0),{mode:384});const J={version:S,transferId:e.transferId,rootPath:s.resolve(e.rootPath),uploadsRoot:v,kind:e.kind,name:t,mimeType:e.mimeType,totalSize:a,sha256:n,targetPath:I,createdAt:r,lastActivityAt:r,files:h};return F(J),{transferId:e.transferId,path:I,reused:!1,offsets:Object.fromEntries(h.map(_=>[_.relativePath,0]))}}function ie(e){const r=B(e.rootPath,e.transferId),t=r.kind==="file"?$:p(e.relativePath??""),n=r.files.find(a=>a.relativePath===t);if(!n)throw new Error("invalid_attachment_relative_path");if(!Number.isSafeInteger(e.offset)||e.offset!==n.receivedSize)throw new Error(e.offset<n.receivedSize?"duplicate_attachment_chunk":"out_of_order_attachment_chunk");if(e.data.length<=0||e.data.length>U)throw new Error("invalid_attachment_chunk_size");if(e.offset+e.data.length>n.size)throw new Error("attachment_chunk_exceeds_size");const i=y(e.rootPath,e.transferId,r.kind==="folder"?n.relativePath:void 0);return o.appendFileSync(i,e.data),n.receivedSize+=e.data.length,r.lastActivityAt=e.now??Date.now(),F(r),{written:e.data.length,nextOffset:n.receivedSize}}function oe(e,r,t=Date.now()){const n=B(e,r);for(const c of n.files){if(c.receivedSize!==c.size)throw new Error("attachment_transfer_incomplete");const h=y(e,r,n.kind==="folder"?c.relativePath:void 0);if(o.statSync(h).size!==c.size)throw new Error("attachment_size_mismatch");if(P(h)!==c.sha256)throw new Error("attachment_hash_mismatch")}if(n.kind==="folder"&&x(n.files)!==n.sha256)throw new Error("attachment_manifest_hash_mismatch");const i=N(e,n.sha256);if(i&&i.kind===n.kind&&i.byteSize===n.totalSize)return o.rmSync(m(e,r),{recursive:!0,force:!0}),{...R(i,!0),transferId:r};o.mkdirSync(s.dirname(n.targetPath),{recursive:!0,mode:448});const a=y(e,r);o.renameSync(a,n.targetPath);const f={version:S,transferId:r,path:n.targetPath,name:n.name,mimeType:n.mimeType,kind:n.kind,byteSize:n.totalSize,sha256:n.sha256,finishedAt:t,referencedAt:null,...n.kind==="folder"?{files:n.files.map(({receivedSize:c,...h})=>h)}:{}};return k(A(e,n.sha256),f),o.rmSync(m(e,r),{recursive:!0,force:!0}),R(f,!1)}function se(e,r){z(r);const t=m(e,r);d(w(e),t);const n=o.existsSync(t);return o.rmSync(t,{recursive:!0,force:!0}),n}function ce(e,r,t=Date.now()){for(const n of r){if(!C.test(n.sha256))continue;const i=A(e,n.sha256),a=u(i);!a||s.resolve(a.path)!==s.resolve(n.path)||(d(l(e),a.path),a.referencedAt=a.referencedAt??t,k(i,a))}}function fe(e,r){for(const t of r){const n=g(t.sha256),i=N(e,n);if(!i)throw new Error("attachment_not_finished");const a=i.kind==="folder"?"folder":i.mimeType.startsWith("image/")?"image":"file";if(s.resolve(i.path)!==s.resolve(t.path)||i.name!==t.name||i.mimeType!==t.mimeType||i.byteSize!==t.byteSize||a!==t.kind)throw new Error("attachment_reference_mismatch")}}function Q(e,r=Date.now()){const t={stagingRemoved:0,orphanRemoved:0},n=w(e);try{for(const a of o.readdirSync(n,{withFileTypes:!0})){if(!a.isDirectory()||!O.test(a.name))continue;const f=s.join(n,a.name),h=u(s.join(f,"manifest.json"))?.lastActivityAt??o.statSync(f).mtimeMs;r-h<Y||(d(n,f),o.rmSync(f,{recursive:!0,force:!0}),t.stagingRemoved+=1)}}catch(a){if(a.code!=="ENOENT")throw a}const i=E(e);try{for(const a of o.readdirSync(i,{withFileTypes:!0})){if(!a.isFile()||!a.name.endsWith(".json"))continue;const f=s.join(i,a.name),c=u(f);!c||c.referencedAt!==null||r-c.finishedAt<K||(d(l(e),c.path),o.rmSync(c.path,{recursive:c.kind==="folder",force:!0}),o.rmSync(f,{force:!0}),t.orphanRemoved+=1)}}catch(a){if(a.code!=="ENOENT")throw a}return t}export{U as ATTACHMENT_MAX_CHUNK_BYTES,D as ATTACHMENT_MAX_FILE_BYTES,H as ATTACHMENT_MAX_FOLDER_BYTES,q as ATTACHMENT_MAX_FOLDER_FILES,K as ATTACHMENT_ORPHAN_TTL_MS,Y as ATTACHMENT_TRANSFER_IDLE_MS,se as abortAttachmentTransfer,ie as appendAttachmentTransferChunk,fe as assertFinishedAttachmentReferences,oe as finishAttachmentTransfer,ce as markAttachmentReferences,ae as startAttachmentTransfer,Q as sweepAttachmentTransfers};
1
+ import M from"node:crypto";import s from"node:fs";import{renameSyncWithRetry as D}from"../fs/atomic-rename.js";import o from"node:path";import{serializeFsTransferManifest as Y}from"@shennian/wire";const K=600*1e3,U=1440*60*1e3,q=1024*1024,H=1024*1024*1024,V=2e3,$=1024*1024*1024,S=1,C="__file__",O=/^[a-f0-9]{64}$/i,L=/^[A-Za-z0-9_-]{8,128}$/;function l(e){return o.join(e,".uploads")}function w(e){return o.join(l(e),".staging")}function E(e){return o.join(l(e),".metadata")}function m(e,r){return o.join(w(e),r)}function T(e,r){return o.join(m(e,r),"manifest.json")}function A(e,r){return o.join(E(e),`${r.toLowerCase()}.json`)}function d(e,r){const t=o.relative(o.resolve(e),o.resolve(r));if(!(t===""||!t.startsWith("..")&&!o.isAbsolute(t)))throw new Error("attachment_path_outside_uploads")}function z(e){if(!L.test(e))throw new Error("invalid_transfer_id")}function p(e){if(!O.test(e))throw new Error("invalid_attachment_sha256");return e.toLowerCase()}function Z(e){if(!e||e.trim()!==e||o.basename(e)!==e||e==="."||e==="..")throw new Error("invalid_attachment_name");if(Array.from(e).some(r=>r.charCodeAt(0)<=31))throw new Error("invalid_attachment_name");return e}function g(e){if(!e||e.trim()!==e||e.includes("\\"))throw new Error("invalid_attachment_relative_path");if(o.posix.isAbsolute(e)||o.win32.isAbsolute(e))throw new Error("invalid_attachment_relative_path");if(e.split("/").some(t=>!t||t==="."||t===".."))throw new Error("invalid_attachment_relative_path");return e}function B(e,r,t){if(!Number.isSafeInteger(e)||e<0||e>r)throw new Error(`invalid_${t}`);return e}function k(e,r){s.mkdirSync(o.dirname(e),{recursive:!0});const t=`${e}.${process.pid}.tmp`;s.writeFileSync(t,`${JSON.stringify(r)}
2
+ `,{mode:384}),D(t,e)}function u(e){try{return JSON.parse(s.readFileSync(e,"utf8"))}catch{return null}}function P(e){const r=M.createHash("sha256"),t=s.openSync(e,"r"),n=Buffer.allocUnsafe(256*1024);try{let i=0;do i=s.readSync(t,n,0,n.length,null),i>0&&r.update(n.subarray(0,i));while(i>0)}finally{s.closeSync(t)}return r.digest("hex")}function x(e){return M.createHash("sha256").update(Y(e)).digest("hex")}function y(e,r,t){const n=o.join(m(e,r),"payload"),i=t?o.join(n,...t.split("/")):n;return d(m(e,r),i),i}function W(e,r){z(r);const t=u(T(e,r));if(!t||t.version!==S||t.transferId!==r||o.resolve(t.rootPath)!==o.resolve(e))throw new Error("invalid_transfer_id");return d(l(e),t.targetPath),t}function F(e){k(T(e.rootPath,e.transferId),e)}function G(e,r){if(!s.existsSync(e))return e;const t=o.parse(e);for(let n=0;n<1e3;n+=1){const i=n===0?r.slice(0,10):`${r.slice(0,10)}-${n+1}`,a=o.join(t.dir,`${t.name}-${i}${t.ext}`);if(!s.existsSync(a))return a}throw new Error("attachment_name_conflict")}function R(e,r){const t=u(A(e,r));if(!t||t.version!==S||t.sha256!==r)return null;try{d(l(e),t.path);const n=s.statSync(t.path);if(t.kind==="file"&&(!n.isFile()||n.size!==t.byteSize||P(t.path)!==t.sha256))return null;if(t.kind==="folder"){if(!n.isDirectory()||!t.files?.length)return null;for(const i of t.files){const a=o.join(t.path,...g(i.relativePath).split("/"));if(d(t.path,a),!s.statSync(a).isFile()||s.statSync(a).size!==i.size||P(a)!==i.sha256)return null}if(x(t.files)!==t.sha256)return null}return t}catch{return null}}function N(e,r){return{transferId:e.transferId,path:e.path,name:e.name,mimeType:e.mimeType,kind:e.kind,byteSize:e.byteSize,sha256:e.sha256,reused:r}}function Q(e){if(e.kind==="file")return[{relativePath:C,size:e.totalSize,sha256:e.sha256,receivedSize:0}];if(!e.manifest?.length||e.manifest.length>V)throw new Error("invalid_attachment_manifest");const r=new Set;let t=0;const n=e.manifest.map(i=>{const a=g(i.relativePath);if(r.has(a))throw new Error("duplicate_attachment_relative_path");r.add(a);const f=B(i.size,H,"attachment_size");if(t+=f,t>$)throw new Error("attachment_folder_too_large");return{...i,relativePath:a,size:f,sha256:p(i.sha256),receivedSize:0}});if(t!==e.totalSize)throw new Error("attachment_total_size_mismatch");if(x(n)!==e.sha256)throw new Error("attachment_manifest_hash_mismatch");return n}function oe(e,r=Date.now()){z(e.transferId);const t=Z(e.name),n=p(e.sha256),i=e.kind==="folder"?$:H,a=B(e.totalSize,i,"attachment_total_size");if(!e.rootPath||!o.isAbsolute(e.rootPath))throw new Error("invalid_attachment_root");if(!e.mimeType||e.mimeType.length>255)throw new Error("invalid_attachment_mime_type");ee(e.rootPath,r);const f=R(e.rootPath,n);if(f&&f.kind===e.kind&&f.byteSize===a)return{transferId:e.transferId,path:f.path,reused:!0,offsets:{},attachment:{...N(f,!0),transferId:e.transferId}};const c=u(T(e.rootPath,e.transferId));if(c){if(c.sha256!==n||c.totalSize!==a||c.name!==t||c.kind!==e.kind||c.mimeType!==e.mimeType)throw new Error("transfer_identity_conflict");return c.lastActivityAt=r,F(c),{transferId:e.transferId,path:c.targetPath,reused:!1,offsets:Object.fromEntries(c.files.map(_=>[_.relativePath,_.receivedSize]))}}const h=Q({...e,name:t,sha256:n,totalSize:a}),v=l(e.rootPath);s.mkdirSync(w(e.rootPath),{recursive:!0,mode:448}),s.mkdirSync(E(e.rootPath),{recursive:!0,mode:448});const J=m(e.rootPath,e.transferId);s.mkdirSync(J,{recursive:!1,mode:448});const b=o.join(v,t);d(v,b);const I=G(b,n);if(e.kind==="folder"){s.mkdirSync(y(e.rootPath,e.transferId),{recursive:!0,mode:448});for(const _ of h){const j=y(e.rootPath,e.transferId,_.relativePath);s.mkdirSync(o.dirname(j),{recursive:!0,mode:448}),s.writeFileSync(j,Buffer.alloc(0),{mode:384})}}else s.writeFileSync(y(e.rootPath,e.transferId),Buffer.alloc(0),{mode:384});const X={version:S,transferId:e.transferId,rootPath:o.resolve(e.rootPath),uploadsRoot:v,kind:e.kind,name:t,mimeType:e.mimeType,totalSize:a,sha256:n,targetPath:I,createdAt:r,lastActivityAt:r,files:h};return F(X),{transferId:e.transferId,path:I,reused:!1,offsets:Object.fromEntries(h.map(_=>[_.relativePath,0]))}}function se(e){const r=W(e.rootPath,e.transferId),t=r.kind==="file"?C:g(e.relativePath??""),n=r.files.find(a=>a.relativePath===t);if(!n)throw new Error("invalid_attachment_relative_path");if(!Number.isSafeInteger(e.offset)||e.offset!==n.receivedSize)throw new Error(e.offset<n.receivedSize?"duplicate_attachment_chunk":"out_of_order_attachment_chunk");if(e.data.length<=0||e.data.length>q)throw new Error("invalid_attachment_chunk_size");if(e.offset+e.data.length>n.size)throw new Error("attachment_chunk_exceeds_size");const i=y(e.rootPath,e.transferId,r.kind==="folder"?n.relativePath:void 0);return s.appendFileSync(i,e.data),n.receivedSize+=e.data.length,r.lastActivityAt=e.now??Date.now(),F(r),{written:e.data.length,nextOffset:n.receivedSize}}function ce(e,r,t=Date.now()){const n=W(e,r);for(const c of n.files){if(c.receivedSize!==c.size)throw new Error("attachment_transfer_incomplete");const h=y(e,r,n.kind==="folder"?c.relativePath:void 0);if(s.statSync(h).size!==c.size)throw new Error("attachment_size_mismatch");if(P(h)!==c.sha256)throw new Error("attachment_hash_mismatch")}if(n.kind==="folder"&&x(n.files)!==n.sha256)throw new Error("attachment_manifest_hash_mismatch");const i=R(e,n.sha256);if(i&&i.kind===n.kind&&i.byteSize===n.totalSize)return s.rmSync(m(e,r),{recursive:!0,force:!0}),{...N(i,!0),transferId:r};s.mkdirSync(o.dirname(n.targetPath),{recursive:!0,mode:448});const a=y(e,r);D(a,n.targetPath);const f={version:S,transferId:r,path:n.targetPath,name:n.name,mimeType:n.mimeType,kind:n.kind,byteSize:n.totalSize,sha256:n.sha256,finishedAt:t,referencedAt:null,...n.kind==="folder"?{files:n.files.map(({receivedSize:c,...h})=>h)}:{}};return k(A(e,n.sha256),f),s.rmSync(m(e,r),{recursive:!0,force:!0}),N(f,!1)}function fe(e,r){z(r);const t=m(e,r);d(w(e),t);const n=s.existsSync(t);return s.rmSync(t,{recursive:!0,force:!0}),n}function he(e,r,t=Date.now()){for(const n of r){if(!O.test(n.sha256))continue;const i=A(e,n.sha256),a=u(i);!a||o.resolve(a.path)!==o.resolve(n.path)||(d(l(e),a.path),a.referencedAt=a.referencedAt??t,k(i,a))}}function de(e,r){for(const t of r){const n=p(t.sha256),i=R(e,n);if(!i)throw new Error("attachment_not_finished");const a=i.kind==="folder"?"folder":i.mimeType.startsWith("image/")?"image":"file";if(o.resolve(i.path)!==o.resolve(t.path)||i.name!==t.name||i.mimeType!==t.mimeType||i.byteSize!==t.byteSize||a!==t.kind)throw new Error("attachment_reference_mismatch")}}function ee(e,r=Date.now()){const t={stagingRemoved:0,orphanRemoved:0},n=w(e);try{for(const a of s.readdirSync(n,{withFileTypes:!0})){if(!a.isDirectory()||!L.test(a.name))continue;const f=o.join(n,a.name),h=u(o.join(f,"manifest.json"))?.lastActivityAt??s.statSync(f).mtimeMs;r-h<K||(d(n,f),s.rmSync(f,{recursive:!0,force:!0}),t.stagingRemoved+=1)}}catch(a){if(a.code!=="ENOENT")throw a}const i=E(e);try{for(const a of s.readdirSync(i,{withFileTypes:!0})){if(!a.isFile()||!a.name.endsWith(".json"))continue;const f=o.join(i,a.name),c=u(f);!c||c.referencedAt!==null||r-c.finishedAt<U||(d(l(e),c.path),s.rmSync(c.path,{recursive:c.kind==="folder",force:!0}),s.rmSync(f,{force:!0}),t.orphanRemoved+=1)}}catch(a){if(a.code!=="ENOENT")throw a}return t}export{q as ATTACHMENT_MAX_CHUNK_BYTES,H as ATTACHMENT_MAX_FILE_BYTES,$ as ATTACHMENT_MAX_FOLDER_BYTES,V as ATTACHMENT_MAX_FOLDER_FILES,U as ATTACHMENT_ORPHAN_TTL_MS,K as ATTACHMENT_TRANSFER_IDLE_MS,fe as abortAttachmentTransfer,se as appendAttachmentTransferChunk,de as assertFinishedAttachmentReferences,ce as finishAttachmentTransfer,he as markAttachmentReferences,oe as startAttachmentTransfer,ee as sweepAttachmentTransfers};
@@ -1,5 +1,5 @@
1
- import M from"node:crypto";import o from"node:fs";import l from"node:path";import{SESSION_SYNC_DATA_EPOCH as k,SESSION_SYNC_PROTOCOL_VERSION as _}from"@shennian/wire";const v=1,E=64,b="mutations-000001.jsonl";class q extends Error{constructor(e,t){super(e,t),this.name="CanonicalStoreError"}}class I extends q{constructor(e,t){super(e,t),this.name="CanonicalStoreCorruptionError"}}class D extends q{constructor(e){super(e),this.name="CanonicalStoreConflictError"}}class Z{rootDir;daemonInstallationId;mutationListeners=new Set;constructor(e,t){if(!t)throw new q("daemonInstallationId is required");this.rootDir=l.resolve(e),this.daemonInstallationId=t}commit(e){J(e);const t=this.sessionDir(e.sessionId);this.assertHealthy(t),o.mkdirSync(this.messageStateDir(t),{recursive:!0,mode:448});let s=this.loadManifestOrRebuild(e.sessionId);const n=this.readMessageState(t,e.messageId),a=z(e);if(n&&n.contentHash===a&&n.tombstoned===(e.mutation==="tombstone"))return{bodyRevision:s.bodyRevision,sequence:n.sequence,mutation:this.findMutationByRevision(e.sessionId,n.lastRevision),duplicate:!0};if(e.mutation==="append"&&n)throw new D(`message ${e.messageId} already exists in session ${e.sessionId}; use upsert`);const r=n?.sequence??s.nextSequence,g=s.bodyRevision+1,S=e.committedAt??new Date().toISOString(),y={protocolVersion:_,dataEpoch:k,daemonInstallationId:this.daemonInstallationId,schemaVersion:v,mutationId:U(e.sessionId,e.messageId,e.mutation,n?.contentHash??"none",a),sessionId:e.sessionId,revision:g,mutation:e.mutation,messageId:e.messageId,sequence:r,...e.message?{message:e.message}:{},...e.deliveryState?{deliveryState:e.deliveryState}:{},committedAt:S,contentHash:a},m=l.join(t,b),u=W(m,`${JSON.stringify(y)}
2
- `),c={schemaVersion:v,sessionId:e.sessionId,messageId:e.messageId,sequence:r,lastRevision:g,contentHash:a,tombstoned:e.mutation==="tombstone",...e.message?{message:e.message}:{},...e.deliveryState?{deliveryState:e.deliveryState}:{}};w(this.messageStateFile(t,r,e.messageId),c);const f=this.readSparseIndex(t,e.sessionId);(g===1||(g-1)%E===0)&&(f.entries.push({revision:g,offset:u}),w(l.join(t,"sparse-index.json"),f)),s={...s,bodyRevision:g,nextSequence:n?s.nextSequence:r+1,firstAvailableSeq:s.firstAvailableSeq??r,lastAvailableSeq:Math.max(s.lastAvailableSeq??r,r),mutationCount:s.mutationCount+1,visibleMessageCount:s.visibleMessageCount+(!n&&e.mutation!=="tombstone"?1:0)-(n&&!n.tombstoned&&e.mutation==="tombstone"?1:0),lastVisibleMessageId:e.mutation==="tombstone"?s.lastVisibleMessageId===e.messageId?this.findLastVisibleMessageId(t,e.messageId):s.lastVisibleMessageId:!n||n.tombstoned?e.messageId:s.lastVisibleMessageId,lastBodyCommittedAt:S,logByteLength:o.statSync(m).size,updatedAt:S},w(l.join(t,"manifest.json"),s);for(const x of this.mutationListeners)try{x(y)}catch(R){console.error("[canonical-store] mutation listener failed",R)}return{bodyRevision:g,sequence:r,mutation:y,duplicate:!1}}subscribe(e){return this.mutationListeners.add(e),()=>this.mutationListeners.delete(e)}append(e,t,s){return this.commit({sessionId:e,mutation:"append",messageId:t.id,message:t,deliveryState:s})}upsert(e,t,s){return this.commit({sessionId:e,mutation:"upsert",messageId:t.id,message:t,deliveryState:s})}tombstone(e,t,s){return this.commit({sessionId:e,mutation:"tombstone",messageId:t,deliveryState:s})}readRecent(e,t){return this.readPage(e,{limit:t})}readPage(e,t){const s=O(t.limit),n=this.sessionDir(e);if(!o.existsSync(n))return H();const a=this.loadManifestOrRebuild(e),r=this.listMessageStateFiles(n).filter(m=>t.beforeSequence===void 0||m.sequence<t.beforeSequence).sort((m,u)=>u.sequence-m.sequence),g=[];let S=0;for(const m of r){S+=1;const u=p(l.join(this.messageStateDir(n),m.name));if(!u.tombstoned&&u.message&&g.push(u),g.length>=s)break}g.sort((m,u)=>m.sequence-u.sequence);const y=g.length>0&&r.slice(S).some(m=>{const u=p(l.join(this.messageStateDir(n),m.name));return!u.tombstoned&&!!u.message});return{bodyRevision:a.bodyRevision,firstAvailableSeq:a.firstAvailableSeq,lastAvailableSeq:a.lastAvailableSeq,hasOlder:y,messages:g.map(m=>m.message),entries:g.map(m=>({sequence:m.sequence,revision:m.lastRevision,message:m.message,...m.deliveryState?{deliveryState:m.deliveryState}:{}}))}}readPageAtRevision(e,t){const s=O(t.limit),n=this.sessionDir(e);if(!o.existsSync(n))return H();const a=this.loadManifestOrRebuild(e);if(!Number.isSafeInteger(t.revision)||t.revision<0||t.revision>a.bodyRevision)throw new q("snapshot revision is unavailable");const r=new Map,g=B(l.join(n,b),0);for(const c of g.split(`
3
- `)){if(!c)continue;const f=A(c,e);if(f.revision>t.revision)break;r.set(f.messageId,{schemaVersion:v,sessionId:e,messageId:f.messageId,sequence:f.sequence,lastRevision:f.revision,contentHash:f.contentHash,tombstoned:f.mutation==="tombstone",...f.message?{message:f.message}:{},...f.deliveryState?{deliveryState:f.deliveryState}:{}})}const S=[...r.values()].filter(c=>!c.tombstoned&&c.message&&(t.beforeSequence===void 0||c.sequence<t.beforeSequence)).sort((c,f)=>f.sequence-c.sequence),y=S.slice(0,s).sort((c,f)=>c.sequence-f.sequence),u=[...r.values()].filter(c=>!c.tombstoned&&c.message).map(c=>c.sequence);return{bodyRevision:t.revision,firstAvailableSeq:u.length?Math.min(...u):null,lastAvailableSeq:u.length?Math.max(...u):null,hasOlder:S.length>y.length,messages:y.map(c=>c.message),entries:y.map(c=>({sequence:c.sequence,revision:c.lastRevision,message:c.message,...c.deliveryState?{deliveryState:c.deliveryState}:{}}))}}readDeltas(e,t,s){if(!Number.isSafeInteger(t)||t<0)throw new q("afterRevision must be a non-negative safe integer");const n=O(s),a=this.sessionDir(e);if(!o.existsSync(a))return[];const r=this.loadManifestOrRebuild(e);if(t>=r.bodyRevision)return[];const g=this.readSparseIndex(a,e),S=t+1,y=[...g.entries].reverse().find(c=>c.revision<=S),m=B(l.join(a,b),y?.offset??0),u=[];for(const c of m.split(`
4
- `)){if(!c)continue;const f=A(c,e);if(f.revision>t&&u.push(f),u.length>=n)break}return u}getManifest(e){const t=this.sessionDir(e);return o.existsSync(t)?this.loadManifestOrRebuild(e):null}getMessageState(e,t){const s=this.sessionDir(e);if(!o.existsSync(s))return null;this.loadManifestOrRebuild(e);const n=this.readMessageState(s,t);return n?{sequence:n.sequence,lastRevision:n.lastRevision,tombstoned:n.tombstoned,message:n.message,deliveryState:n.deliveryState}:null}completeStartedUserMessages(e,t){const s=this.sessionDir(e);if(!o.existsSync(s))return[];this.loadManifestOrRebuild(e);const n=[];for(const a of this.listMessageStateFiles(s).sort((r,g)=>g.sequence-r.sequence)){const r=p(l.join(this.messageStateDir(s),a.name));r.tombstoned||r.message?.role!=="user"||r.deliveryState!=="executing"||r.message.ts>t||n.push(this.upsert(e,r.message,"completed"))}return n}updateMessageDeliveryState(e,t,s){const n=this.getMessageState(e,t);return!n?.message||n.tombstoned?null:this.upsert(e,n.message,s)}listSessionIds(){const e=l.join(this.rootDir,"sessions");if(!o.existsSync(e))return[];const t=[];for(const s of o.readdirSync(e,{withFileTypes:!0})){if(!s.isDirectory())continue;const n=l.join(e,s.name,"manifest.json");o.existsSync(n)&&t.push(p(n).sessionId)}return t.sort()}purgeSession(e){if(!e)throw new q("sessionId is required");const t=l.resolve(this.rootDir,"sessions"),s=l.resolve(t,N(e));if(l.dirname(s)!==t)throw new q("invalid session path");o.rmSync(s,{recursive:!0,force:!0})}findLastVisibleMessageId(e,t){for(const s of this.listMessageStateFiles(e).sort((n,a)=>a.sequence-n.sequence)){const n=p(l.join(this.messageStateDir(e),s.name));if(n.messageId!==t&&!n.tombstoned&&n.message)return n.messageId}return null}rebuild(e){const t=this.sessionDir(e);this.assertHealthy(t),o.mkdirSync(t,{recursive:!0,mode:448});const s=l.join(t,"manifest.json");if(o.existsSync(s))try{const d=JSON.parse(o.readFileSync(s,"utf8"));if(d.daemonInstallationId&&d.daemonInstallationId!==this.daemonInstallationId)throw new D(`canonical session ${e} belongs to a different daemon installation`)}catch(d){if(d instanceof D)throw d}const n=l.join(t,b);if(!o.existsSync(n)){const d=T(e,this.daemonInstallationId);return w(l.join(t,"manifest.json"),d),w(l.join(t,"sparse-index.json"),C(e)),d}const a=o.readFileSync(n);let r=a.length;if(a.length>0&&a[a.length-1]!==10){const d=a.lastIndexOf(10);r=d<0?0:d+1,o.truncateSync(n,r)}const g=a.subarray(0,r).toString("utf8"),S=new Map,y=C(e);let m=1,u=0,c=new Date(0).toISOString();try{for(const d of g.match(/.*\n/g)??[]){const j=d.slice(0,-1);if(!j){u+=Buffer.byteLength(d);continue}const h=A(j,e);if(h.revision!==m)throw new I(`non-contiguous revision in ${e}: expected ${m}, got ${h.revision}`);(h.revision===1||(h.revision-1)%E===0)&&y.entries.push({revision:h.revision,offset:u});const F=S.get(h.messageId);if(F&&F.sequence!==h.sequence)throw new I(`sequence changed for message ${h.messageId}`);S.set(h.messageId,{schemaVersion:v,sessionId:e,messageId:h.messageId,sequence:h.sequence,lastRevision:h.revision,contentHash:h.contentHash,tombstoned:h.mutation==="tombstone",...h.message?{message:h.message}:{},...h.deliveryState?{deliveryState:h.deliveryState}:{}}),m+=1,u+=Buffer.byteLength(d),c=h.committedAt}}catch(d){this.quarantineCorruptSegment(e,n,d)}const f=this.messageStateDir(t);o.rmSync(f,{recursive:!0,force:!0}),o.mkdirSync(f,{recursive:!0,mode:448});for(const d of S.values())w(this.messageStateFile(t,d.sequence,d.messageId),d);w(l.join(t,"sparse-index.json"),y);const x=[...S.values()].map(d=>d.sequence),R=[...S.values()].filter(d=>!d.tombstoned&&!!d.message).sort((d,j)=>d.sequence-j.sequence),$=m-1,V={schemaVersion:v,daemonInstallationId:this.daemonInstallationId,sessionId:e,bodyRevision:$,nextSequence:x.length===0?1:Math.max(...x)+1,firstAvailableSeq:x.length===0?null:Math.min(...x),lastAvailableSeq:x.length===0?null:Math.max(...x),mutationCount:$,visibleMessageCount:R.length,lastVisibleMessageId:R.at(-1)?.messageId??null,lastBodyCommittedAt:$>0?c:null,segment:b,logByteLength:r,updatedAt:c};return w(l.join(t,"manifest.json"),V),V}loadManifestOrRebuild(e){const t=this.sessionDir(e);this.assertHealthy(t);const s=l.join(t,"manifest.json"),n=l.join(t,b);if(!o.existsSync(s))return this.rebuild(e);try{const a=p(s);P(a,e,this.daemonInstallationId);const r=o.existsSync(n)?o.statSync(n).size:0;return a.logByteLength===r?a:this.rebuild(e)}catch(a){if(a instanceof D)throw a;return this.rebuild(e)}}findMutationByRevision(e,t){const s=this.readDeltas(e,t-1,1)[0];if(!s||s.revision!==t)throw new I(`missing mutation revision ${t} in ${e}`);return s}readSparseIndex(e,t){const s=l.join(e,"sparse-index.json");if(!o.existsSync(s))return C(t);const n=p(s);if(n.schemaVersion!==v||n.sessionId!==t||n.segment!==b)throw new I(`invalid sparse index for session ${t}`);return n}readMessageState(e,t){const s=`.${L(t)}.json`,n=this.listMessageStateFiles(e).find(r=>r.name.endsWith(s));if(!n)return null;const a=p(l.join(this.messageStateDir(e),n.name));if(a.messageId!==t)throw new I(`message state hash collision for ${t}`);return a}listMessageStateFiles(e){const t=this.messageStateDir(e);return o.existsSync(t)?o.readdirSync(t).map(s=>({name:s,match:/^(\d{16})\.[a-f0-9]{32}\.json$/.exec(s)})).filter(s=>!!s.match).map(s=>({name:s.name,sequence:Number(s.match[1])})):[]}quarantineCorruptSegment(e,t,s){const n=l.join(this.rootDir,"quarantine");o.mkdirSync(n,{recursive:!0,mode:448});const a=l.join(n,`${N(e)}-${Date.now()}-${b}`);throw o.renameSync(t,a),w(l.join(this.sessionDir(e),"CORRUPT.json"),{schemaVersion:v,sessionId:e,quarantined:a,detectedAt:new Date().toISOString(),reason:s instanceof Error?s.message:String(s)}),new I(`canonical segment for ${e} was quarantined at ${a}`,{cause:s})}assertHealthy(e){const t=l.join(e,"CORRUPT.json");if(o.existsSync(t))throw new I(`canonical session is quarantined: ${t}`)}sessionDir(e){return l.join(this.rootDir,"sessions",N(e))}messageStateDir(e){return l.join(e,"messages")}messageStateFile(e,t,s){return l.join(this.messageStateDir(e),`${String(t).padStart(16,"0")}.${L(s)}.json`)}}function J(i){if(!i.sessionId||!i.messageId)throw new q("sessionId and messageId are required");if(i.message&&(i.message.id!==i.messageId||i.message.sessionId!==i.sessionId))throw new q("message identity does not match commit identity");if(i.mutation!=="tombstone"&&!i.message)throw new q(`${i.mutation} mutation requires a message`)}function P(i,e,t){if(i.daemonInstallationId!==t)throw new D(`canonical session ${e} belongs to a different daemon installation`);if(i.schemaVersion!==v||i.sessionId!==e||i.segment!==b||!Number.isSafeInteger(i.bodyRevision)||i.bodyRevision<0)throw new Error(`invalid canonical manifest for ${e}`)}function A(i,e){let t;try{t=JSON.parse(i)}catch(s){throw new I(`malformed canonical mutation in ${e}`,{cause:s})}if(t.schemaVersion!==v||t.sessionId!==e||!Number.isSafeInteger(t.revision)||t.revision<1||!Number.isSafeInteger(t.sequence)||t.sequence<1||!["append","upsert","tombstone"].includes(t.mutation))throw new I(`invalid canonical mutation in ${e}`);return t}function T(i,e){return{schemaVersion:v,daemonInstallationId:e,sessionId:i,bodyRevision:0,nextSequence:1,firstAvailableSeq:null,lastAvailableSeq:null,mutationCount:0,visibleMessageCount:0,lastVisibleMessageId:null,lastBodyCommittedAt:null,segment:b,logByteLength:0,updatedAt:new Date(0).toISOString()}}function C(i){return{schemaVersion:v,sessionId:i,segment:b,entries:[]}}function H(){return{bodyRevision:0,firstAvailableSeq:null,lastAvailableSeq:null,hasOlder:!1,messages:[],entries:[]}}function O(i){if(!Number.isSafeInteger(i)||i<1||i>500)throw new q("limit must be an integer between 1 and 500");return i}function N(i){return M.createHash("sha256").update(i).digest("hex")}function L(i){return M.createHash("sha256").update(i).digest("hex").slice(0,32)}function z(i){return M.createHash("sha256").update(JSON.stringify({mutation:i.mutation,message:i.message??null,deliveryState:i.deliveryState??null})).digest("hex")}function U(i,e,t,s,n){return M.createHash("sha256").update(`${i}\0${e}\0${t}\0${s}\0${n}`).digest("hex")}function W(i,e){o.mkdirSync(l.dirname(i),{recursive:!0,mode:448});const t=o.openSync(i,"a",384);try{const s=o.fstatSync(t).size;return o.writeFileSync(t,e,"utf8"),o.fsyncSync(t),s}finally{o.closeSync(t)}}function w(i,e){const t=l.dirname(i);o.mkdirSync(t,{recursive:!0,mode:448});const s=`${i}.tmp-${process.pid}-${M.randomBytes(6).toString("hex")}`;let n;try{n=o.openSync(s,"wx",384),o.writeFileSync(n,`${JSON.stringify(e,null,2)}
5
- `,"utf8"),o.fsyncSync(n),o.closeSync(n),n=void 0,o.renameSync(s,i),Y(t)}finally{n!==void 0&&o.closeSync(n);try{o.unlinkSync(s)}catch{}}}function Y(i){if(process.platform==="win32")return;const e=o.openSync(i,"r");try{o.fsyncSync(e)}finally{o.closeSync(e)}}function p(i){try{return JSON.parse(o.readFileSync(i,"utf8"))}catch(e){throw new I(`failed to read canonical JSON file ${i}`,{cause:e})}}function B(i,e){const t=o.openSync(i,"r");try{const s=o.fstatSync(t).size;if(e>=s)return"";const n=Buffer.allocUnsafe(s-e);return o.readSync(t,n,0,n.length,e),n.toString("utf8")}finally{o.closeSync(t)}}export{Z as CanonicalSessionStore,D as CanonicalStoreConflictError,I as CanonicalStoreCorruptionError,q as CanonicalStoreError};
1
+ import M from"node:crypto";import o from"node:fs";import{renameSyncWithRetry as E}from"../fs/atomic-rename.js";import l from"node:path";import{SESSION_SYNC_DATA_EPOCH as _,SESSION_SYNC_PROTOCOL_VERSION as J}from"@shennian/wire";const v=1,H=64,b="mutations-000001.jsonl";class q extends Error{constructor(e,t){super(e,t),this.name="CanonicalStoreError"}}class I extends q{constructor(e,t){super(e,t),this.name="CanonicalStoreCorruptionError"}}class D extends q{constructor(e){super(e),this.name="CanonicalStoreConflictError"}}class te{rootDir;daemonInstallationId;mutationListeners=new Set;constructor(e,t){if(!t)throw new q("daemonInstallationId is required");this.rootDir=l.resolve(e),this.daemonInstallationId=t}commit(e){P(e);const t=this.sessionDir(e.sessionId);this.assertHealthy(t),o.mkdirSync(this.messageStateDir(t),{recursive:!0,mode:448});let s=this.loadManifestOrRebuild(e.sessionId);const n=this.readMessageState(t,e.messageId),a=U(e);if(n&&n.contentHash===a&&n.tombstoned===(e.mutation==="tombstone"))return{bodyRevision:s.bodyRevision,sequence:n.sequence,mutation:this.findMutationByRevision(e.sessionId,n.lastRevision),duplicate:!0};if(e.mutation==="append"&&n)throw new D(`message ${e.messageId} already exists in session ${e.sessionId}; use upsert`);const r=n?.sequence??s.nextSequence,g=s.bodyRevision+1,S=e.committedAt??new Date().toISOString(),y={protocolVersion:J,dataEpoch:_,daemonInstallationId:this.daemonInstallationId,schemaVersion:v,mutationId:W(e.sessionId,e.messageId,e.mutation,n?.contentHash??"none",a),sessionId:e.sessionId,revision:g,mutation:e.mutation,messageId:e.messageId,sequence:r,...e.message?{message:e.message}:{},...e.deliveryState?{deliveryState:e.deliveryState}:{},committedAt:S,contentHash:a},m=l.join(t,b),u=Y(m,`${JSON.stringify(y)}
2
+ `),c={schemaVersion:v,sessionId:e.sessionId,messageId:e.messageId,sequence:r,lastRevision:g,contentHash:a,tombstoned:e.mutation==="tombstone",...e.message?{message:e.message}:{},...e.deliveryState?{deliveryState:e.deliveryState}:{}};w(this.messageStateFile(t,r,e.messageId),c);const f=this.readSparseIndex(t,e.sessionId);(g===1||(g-1)%H===0)&&(f.entries.push({revision:g,offset:u}),w(l.join(t,"sparse-index.json"),f)),s={...s,bodyRevision:g,nextSequence:n?s.nextSequence:r+1,firstAvailableSeq:s.firstAvailableSeq??r,lastAvailableSeq:Math.max(s.lastAvailableSeq??r,r),mutationCount:s.mutationCount+1,visibleMessageCount:s.visibleMessageCount+(!n&&e.mutation!=="tombstone"?1:0)-(n&&!n.tombstoned&&e.mutation==="tombstone"?1:0),lastVisibleMessageId:e.mutation==="tombstone"?s.lastVisibleMessageId===e.messageId?this.findLastVisibleMessageId(t,e.messageId):s.lastVisibleMessageId:!n||n.tombstoned?e.messageId:s.lastVisibleMessageId,lastBodyCommittedAt:S,logByteLength:o.statSync(m).size,updatedAt:S},w(l.join(t,"manifest.json"),s);for(const x of this.mutationListeners)try{x(y)}catch(R){console.error("[canonical-store] mutation listener failed",R)}return{bodyRevision:g,sequence:r,mutation:y,duplicate:!1}}subscribe(e){return this.mutationListeners.add(e),()=>this.mutationListeners.delete(e)}append(e,t,s){return this.commit({sessionId:e,mutation:"append",messageId:t.id,message:t,deliveryState:s})}upsert(e,t,s){return this.commit({sessionId:e,mutation:"upsert",messageId:t.id,message:t,deliveryState:s})}tombstone(e,t,s){return this.commit({sessionId:e,mutation:"tombstone",messageId:t,deliveryState:s})}readRecent(e,t){return this.readPage(e,{limit:t})}readPage(e,t){const s=O(t.limit),n=this.sessionDir(e);if(!o.existsSync(n))return L();const a=this.loadManifestOrRebuild(e),r=this.listMessageStateFiles(n).filter(m=>t.beforeSequence===void 0||m.sequence<t.beforeSequence).sort((m,u)=>u.sequence-m.sequence),g=[];let S=0;for(const m of r){S+=1;const u=p(l.join(this.messageStateDir(n),m.name));if(!u.tombstoned&&u.message&&g.push(u),g.length>=s)break}g.sort((m,u)=>m.sequence-u.sequence);const y=g.length>0&&r.slice(S).some(m=>{const u=p(l.join(this.messageStateDir(n),m.name));return!u.tombstoned&&!!u.message});return{bodyRevision:a.bodyRevision,firstAvailableSeq:a.firstAvailableSeq,lastAvailableSeq:a.lastAvailableSeq,hasOlder:y,messages:g.map(m=>m.message),entries:g.map(m=>({sequence:m.sequence,revision:m.lastRevision,message:m.message,...m.deliveryState?{deliveryState:m.deliveryState}:{}}))}}readPageAtRevision(e,t){const s=O(t.limit),n=this.sessionDir(e);if(!o.existsSync(n))return L();const a=this.loadManifestOrRebuild(e);if(!Number.isSafeInteger(t.revision)||t.revision<0||t.revision>a.bodyRevision)throw new q("snapshot revision is unavailable");const r=new Map,g=k(l.join(n,b),0);for(const c of g.split(`
3
+ `)){if(!c)continue;const f=A(c,e);if(f.revision>t.revision)break;r.set(f.messageId,{schemaVersion:v,sessionId:e,messageId:f.messageId,sequence:f.sequence,lastRevision:f.revision,contentHash:f.contentHash,tombstoned:f.mutation==="tombstone",...f.message?{message:f.message}:{},...f.deliveryState?{deliveryState:f.deliveryState}:{}})}const S=[...r.values()].filter(c=>!c.tombstoned&&c.message&&(t.beforeSequence===void 0||c.sequence<t.beforeSequence)).sort((c,f)=>f.sequence-c.sequence),y=S.slice(0,s).sort((c,f)=>c.sequence-f.sequence),u=[...r.values()].filter(c=>!c.tombstoned&&c.message).map(c=>c.sequence);return{bodyRevision:t.revision,firstAvailableSeq:u.length?Math.min(...u):null,lastAvailableSeq:u.length?Math.max(...u):null,hasOlder:S.length>y.length,messages:y.map(c=>c.message),entries:y.map(c=>({sequence:c.sequence,revision:c.lastRevision,message:c.message,...c.deliveryState?{deliveryState:c.deliveryState}:{}}))}}readDeltas(e,t,s){if(!Number.isSafeInteger(t)||t<0)throw new q("afterRevision must be a non-negative safe integer");const n=O(s),a=this.sessionDir(e);if(!o.existsSync(a))return[];const r=this.loadManifestOrRebuild(e);if(t>=r.bodyRevision)return[];const g=this.readSparseIndex(a,e),S=t+1,y=[...g.entries].reverse().find(c=>c.revision<=S),m=k(l.join(a,b),y?.offset??0),u=[];for(const c of m.split(`
4
+ `)){if(!c)continue;const f=A(c,e);if(f.revision>t&&u.push(f),u.length>=n)break}return u}getManifest(e){const t=this.sessionDir(e);return o.existsSync(t)?this.loadManifestOrRebuild(e):null}getMessageState(e,t){const s=this.sessionDir(e);if(!o.existsSync(s))return null;this.loadManifestOrRebuild(e);const n=this.readMessageState(s,t);return n?{sequence:n.sequence,lastRevision:n.lastRevision,tombstoned:n.tombstoned,message:n.message,deliveryState:n.deliveryState}:null}completeStartedUserMessages(e,t){const s=this.sessionDir(e);if(!o.existsSync(s))return[];this.loadManifestOrRebuild(e);const n=[];for(const a of this.listMessageStateFiles(s).sort((r,g)=>g.sequence-r.sequence)){const r=p(l.join(this.messageStateDir(s),a.name));r.tombstoned||r.message?.role!=="user"||r.deliveryState!=="executing"||r.message.ts>t||n.push(this.upsert(e,r.message,"completed"))}return n}updateMessageDeliveryState(e,t,s){const n=this.getMessageState(e,t);return!n?.message||n.tombstoned?null:this.upsert(e,n.message,s)}listSessionIds(){const e=l.join(this.rootDir,"sessions");if(!o.existsSync(e))return[];const t=[];for(const s of o.readdirSync(e,{withFileTypes:!0})){if(!s.isDirectory())continue;const n=l.join(e,s.name,"manifest.json");o.existsSync(n)&&t.push(p(n).sessionId)}return t.sort()}purgeSession(e){if(!e)throw new q("sessionId is required");const t=l.resolve(this.rootDir,"sessions"),s=l.resolve(t,N(e));if(l.dirname(s)!==t)throw new q("invalid session path");o.rmSync(s,{recursive:!0,force:!0})}findLastVisibleMessageId(e,t){for(const s of this.listMessageStateFiles(e).sort((n,a)=>a.sequence-n.sequence)){const n=p(l.join(this.messageStateDir(e),s.name));if(n.messageId!==t&&!n.tombstoned&&n.message)return n.messageId}return null}rebuild(e){const t=this.sessionDir(e);this.assertHealthy(t),o.mkdirSync(t,{recursive:!0,mode:448});const s=l.join(t,"manifest.json");if(o.existsSync(s))try{const d=JSON.parse(o.readFileSync(s,"utf8"));if(d.daemonInstallationId&&d.daemonInstallationId!==this.daemonInstallationId)throw new D(`canonical session ${e} belongs to a different daemon installation`)}catch(d){if(d instanceof D)throw d}const n=l.join(t,b);if(!o.existsSync(n)){const d=z(e,this.daemonInstallationId);return w(l.join(t,"manifest.json"),d),w(l.join(t,"sparse-index.json"),C(e)),d}const a=o.readFileSync(n);let r=a.length;if(a.length>0&&a[a.length-1]!==10){const d=a.lastIndexOf(10);r=d<0?0:d+1,o.truncateSync(n,r)}const g=a.subarray(0,r).toString("utf8"),S=new Map,y=C(e);let m=1,u=0,c=new Date(0).toISOString();try{for(const d of g.match(/.*\n/g)??[]){const j=d.slice(0,-1);if(!j){u+=Buffer.byteLength(d);continue}const h=A(j,e);if(h.revision!==m)throw new I(`non-contiguous revision in ${e}: expected ${m}, got ${h.revision}`);(h.revision===1||(h.revision-1)%H===0)&&y.entries.push({revision:h.revision,offset:u});const F=S.get(h.messageId);if(F&&F.sequence!==h.sequence)throw new I(`sequence changed for message ${h.messageId}`);S.set(h.messageId,{schemaVersion:v,sessionId:e,messageId:h.messageId,sequence:h.sequence,lastRevision:h.revision,contentHash:h.contentHash,tombstoned:h.mutation==="tombstone",...h.message?{message:h.message}:{},...h.deliveryState?{deliveryState:h.deliveryState}:{}}),m+=1,u+=Buffer.byteLength(d),c=h.committedAt}}catch(d){this.quarantineCorruptSegment(e,n,d)}const f=this.messageStateDir(t);o.rmSync(f,{recursive:!0,force:!0}),o.mkdirSync(f,{recursive:!0,mode:448});for(const d of S.values())w(this.messageStateFile(t,d.sequence,d.messageId),d);w(l.join(t,"sparse-index.json"),y);const x=[...S.values()].map(d=>d.sequence),R=[...S.values()].filter(d=>!d.tombstoned&&!!d.message).sort((d,j)=>d.sequence-j.sequence),$=m-1,V={schemaVersion:v,daemonInstallationId:this.daemonInstallationId,sessionId:e,bodyRevision:$,nextSequence:x.length===0?1:Math.max(...x)+1,firstAvailableSeq:x.length===0?null:Math.min(...x),lastAvailableSeq:x.length===0?null:Math.max(...x),mutationCount:$,visibleMessageCount:R.length,lastVisibleMessageId:R.at(-1)?.messageId??null,lastBodyCommittedAt:$>0?c:null,segment:b,logByteLength:r,updatedAt:c};return w(l.join(t,"manifest.json"),V),V}loadManifestOrRebuild(e){const t=this.sessionDir(e);this.assertHealthy(t);const s=l.join(t,"manifest.json"),n=l.join(t,b);if(!o.existsSync(s))return this.rebuild(e);try{const a=p(s);T(a,e,this.daemonInstallationId);const r=o.existsSync(n)?o.statSync(n).size:0;return a.logByteLength===r?a:this.rebuild(e)}catch(a){if(a instanceof D)throw a;return this.rebuild(e)}}findMutationByRevision(e,t){const s=this.readDeltas(e,t-1,1)[0];if(!s||s.revision!==t)throw new I(`missing mutation revision ${t} in ${e}`);return s}readSparseIndex(e,t){const s=l.join(e,"sparse-index.json");if(!o.existsSync(s))return C(t);const n=p(s);if(n.schemaVersion!==v||n.sessionId!==t||n.segment!==b)throw new I(`invalid sparse index for session ${t}`);return n}readMessageState(e,t){const s=`.${B(t)}.json`,n=this.listMessageStateFiles(e).find(r=>r.name.endsWith(s));if(!n)return null;const a=p(l.join(this.messageStateDir(e),n.name));if(a.messageId!==t)throw new I(`message state hash collision for ${t}`);return a}listMessageStateFiles(e){const t=this.messageStateDir(e);return o.existsSync(t)?o.readdirSync(t).map(s=>({name:s,match:/^(\d{16})\.[a-f0-9]{32}\.json$/.exec(s)})).filter(s=>!!s.match).map(s=>({name:s.name,sequence:Number(s.match[1])})):[]}quarantineCorruptSegment(e,t,s){const n=l.join(this.rootDir,"quarantine");o.mkdirSync(n,{recursive:!0,mode:448});const a=l.join(n,`${N(e)}-${Date.now()}-${b}`);throw E(t,a),w(l.join(this.sessionDir(e),"CORRUPT.json"),{schemaVersion:v,sessionId:e,quarantined:a,detectedAt:new Date().toISOString(),reason:s instanceof Error?s.message:String(s)}),new I(`canonical segment for ${e} was quarantined at ${a}`,{cause:s})}assertHealthy(e){const t=l.join(e,"CORRUPT.json");if(o.existsSync(t))throw new I(`canonical session is quarantined: ${t}`)}sessionDir(e){return l.join(this.rootDir,"sessions",N(e))}messageStateDir(e){return l.join(e,"messages")}messageStateFile(e,t,s){return l.join(this.messageStateDir(e),`${String(t).padStart(16,"0")}.${B(s)}.json`)}}function P(i){if(!i.sessionId||!i.messageId)throw new q("sessionId and messageId are required");if(i.message&&(i.message.id!==i.messageId||i.message.sessionId!==i.sessionId))throw new q("message identity does not match commit identity");if(i.mutation!=="tombstone"&&!i.message)throw new q(`${i.mutation} mutation requires a message`)}function T(i,e,t){if(i.daemonInstallationId!==t)throw new D(`canonical session ${e} belongs to a different daemon installation`);if(i.schemaVersion!==v||i.sessionId!==e||i.segment!==b||!Number.isSafeInteger(i.bodyRevision)||i.bodyRevision<0)throw new Error(`invalid canonical manifest for ${e}`)}function A(i,e){let t;try{t=JSON.parse(i)}catch(s){throw new I(`malformed canonical mutation in ${e}`,{cause:s})}if(t.schemaVersion!==v||t.sessionId!==e||!Number.isSafeInteger(t.revision)||t.revision<1||!Number.isSafeInteger(t.sequence)||t.sequence<1||!["append","upsert","tombstone"].includes(t.mutation))throw new I(`invalid canonical mutation in ${e}`);return t}function z(i,e){return{schemaVersion:v,daemonInstallationId:e,sessionId:i,bodyRevision:0,nextSequence:1,firstAvailableSeq:null,lastAvailableSeq:null,mutationCount:0,visibleMessageCount:0,lastVisibleMessageId:null,lastBodyCommittedAt:null,segment:b,logByteLength:0,updatedAt:new Date(0).toISOString()}}function C(i){return{schemaVersion:v,sessionId:i,segment:b,entries:[]}}function L(){return{bodyRevision:0,firstAvailableSeq:null,lastAvailableSeq:null,hasOlder:!1,messages:[],entries:[]}}function O(i){if(!Number.isSafeInteger(i)||i<1||i>500)throw new q("limit must be an integer between 1 and 500");return i}function N(i){return M.createHash("sha256").update(i).digest("hex")}function B(i){return M.createHash("sha256").update(i).digest("hex").slice(0,32)}function U(i){return M.createHash("sha256").update(JSON.stringify({mutation:i.mutation,message:i.message??null,deliveryState:i.deliveryState??null})).digest("hex")}function W(i,e,t,s,n){return M.createHash("sha256").update(`${i}\0${e}\0${t}\0${s}\0${n}`).digest("hex")}function Y(i,e){o.mkdirSync(l.dirname(i),{recursive:!0,mode:448});const t=o.openSync(i,"a",384);try{const s=o.fstatSync(t).size;return o.writeFileSync(t,e,"utf8"),o.fsyncSync(t),s}finally{o.closeSync(t)}}function w(i,e){const t=l.dirname(i);o.mkdirSync(t,{recursive:!0,mode:448});const s=`${i}.tmp-${process.pid}-${M.randomBytes(6).toString("hex")}`;let n;try{n=o.openSync(s,"wx",384),o.writeFileSync(n,`${JSON.stringify(e,null,2)}
5
+ `,"utf8"),o.fsyncSync(n),o.closeSync(n),n=void 0,E(s,i),G(t)}finally{n!==void 0&&o.closeSync(n);try{o.unlinkSync(s)}catch{}}}function G(i){if(process.platform==="win32")return;const e=o.openSync(i,"r");try{o.fsyncSync(e)}finally{o.closeSync(e)}}function p(i){try{return JSON.parse(o.readFileSync(i,"utf8"))}catch(e){throw new I(`failed to read canonical JSON file ${i}`,{cause:e})}}function k(i,e){const t=o.openSync(i,"r");try{const s=o.fstatSync(t).size;if(e>=s)return"";const n=Buffer.allocUnsafe(s-e);return o.readSync(t,n,0,n.length,e),n.toString("utf8")}finally{o.closeSync(t)}}export{te as CanonicalSessionStore,D as CanonicalStoreConflictError,I as CanonicalStoreCorruptionError,q as CanonicalStoreError};
@@ -1,2 +1,2 @@
1
- import S from"node:crypto";import n from"node:fs";import f from"node:path";import{resolvePersonalSyncV1Path as A}from"../personal-sync/paths.js";import{getDaemonInstallationId as m}from"../personal-sync/identity.js";import{recordPersonalSyncLocalMetric as y}from"../personal-sync/metrics.js";const w=new Set(["transport_unavailable","temporary_unavailable","invalid_deletion_sync_response","unstructured_rejection","deletion_sync_failed"]);class p extends Error{code;retryable=!1;constructor(e){super(`session deletion sync stopped: ${e}`),this.code=e,this.name="DeletionSyncTerminalError"}}class u extends Error{retryAfterMs;retryable=!0;constructor(e){super(`session deletion sync retry delayed by ${e}ms`),this.retryAfterMs=e,this.name="DeletionSyncRetryWaitError"}}function h(){return a().lastAppliedRevision}function a(){const t=s();if(!n.existsSync(t))return{schemaVersion:1,daemonInstallationId:m(),lastAppliedRevision:0,updatedAt:new Date(0).toISOString()};const e=JSON.parse(n.readFileSync(t,"utf8"));if(e.schemaVersion!==1||e.daemonInstallationId!==m()||!Number.isSafeInteger(e.lastAppliedRevision)||e.lastAppliedRevision<0)throw new Error("invalid deletion sync state");return e}function v(t){if(!Number.isSafeInteger(t)||t<0)throw new Error("invalid deletion sync revision");const e=h();if(t<e)throw new Error("deletion sync revision cannot move backwards");t!==e&&l(s(),{...a(),lastAppliedRevision:t,updatedAt:new Date().toISOString()})}function _(t=Date.now()){const e=a();if(e.terminalCode)if(w.has(e.terminalCode)){l(s(),{...e,retryAttempts:0,nextAttemptAt:null,terminalCode:null,updatedAt:new Date(t).toISOString()});return}else throw new p(e.terminalCode);if(e.nextAttemptAt){const o=Date.parse(e.nextAttemptAt)-t;if(o>0)throw new u(o)}}function C(t){const e=a(),o=Math.min((e.retryAttempts??0)+1,t.maxAttempts??8),i=t.now??Date.now();if(!t.retryable)throw l(s(),{...e,retryAttempts:o,nextAttemptAt:null,terminalCode:t.code,updatedAt:new Date(i).toISOString()}),y("permanentStops"),new p(t.code);const r=Math.min(5e3*2**Math.max(0,o-1),360*60*1e3),c=Math.round(r*.2*(Math.random()*2-1)),d=r+c;throw l(s(),{...e,retryAttempts:o,terminalCode:null,nextAttemptAt:new Date(i+d).toISOString(),updatedAt:new Date(i).toISOString()}),y("transientRetries"),new u(d)}function O(){const t=a();!t.retryAttempts&&!t.nextAttemptAt&&!t.terminalCode||l(s(),{...t,retryAttempts:0,nextAttemptAt:null,terminalCode:null,updatedAt:new Date().toISOString()})}function s(){return A("deletion-sync-state.json")}function l(t,e){const o=f.dirname(t);n.mkdirSync(o,{recursive:!0,mode:448});const i=`${t}.tmp-${process.pid}-${S.randomBytes(6).toString("hex")}`;let r;try{if(r=n.openSync(i,"wx",384),n.writeFileSync(r,`${JSON.stringify(e,null,2)}
2
- `,"utf8"),n.fsyncSync(r),n.closeSync(r),r=void 0,n.renameSync(i,t),process.platform!=="win32"){const c=n.openSync(o,"r");try{n.fsyncSync(c)}finally{n.closeSync(c)}}}finally{r!==void 0&&n.closeSync(r);try{n.unlinkSync(i)}catch{}}}export{u as DeletionSyncRetryWaitError,p as DeletionSyncTerminalError,_ as assertDeletionSyncRunnable,O as clearDeletionSyncFailure,h as readDeletionSyncRevision,C as recordDeletionSyncFailure,v as writeDeletionSyncRevision};
1
+ import S from"node:crypto";import n from"node:fs";import{renameSyncWithRetry as f}from"../fs/atomic-rename.js";import A from"node:path";import{resolvePersonalSyncV1Path as w}from"../personal-sync/paths.js";import{getDaemonInstallationId as m}from"../personal-sync/identity.js";import{recordPersonalSyncLocalMetric as y}from"../personal-sync/metrics.js";const h=new Set(["transport_unavailable","temporary_unavailable","invalid_deletion_sync_response","unstructured_rejection","deletion_sync_failed"]);class p extends Error{code;retryable=!1;constructor(e){super(`session deletion sync stopped: ${e}`),this.code=e,this.name="DeletionSyncTerminalError"}}class u extends Error{retryAfterMs;retryable=!0;constructor(e){super(`session deletion sync retry delayed by ${e}ms`),this.retryAfterMs=e,this.name="DeletionSyncRetryWaitError"}}function x(){return a().lastAppliedRevision}function a(){const t=s();if(!n.existsSync(t))return{schemaVersion:1,daemonInstallationId:m(),lastAppliedRevision:0,updatedAt:new Date(0).toISOString()};const e=JSON.parse(n.readFileSync(t,"utf8"));if(e.schemaVersion!==1||e.daemonInstallationId!==m()||!Number.isSafeInteger(e.lastAppliedRevision)||e.lastAppliedRevision<0)throw new Error("invalid deletion sync state");return e}function C(t){if(!Number.isSafeInteger(t)||t<0)throw new Error("invalid deletion sync revision");const e=x();if(t<e)throw new Error("deletion sync revision cannot move backwards");t!==e&&l(s(),{...a(),lastAppliedRevision:t,updatedAt:new Date().toISOString()})}function O(t=Date.now()){const e=a();if(e.terminalCode)if(h.has(e.terminalCode)){l(s(),{...e,retryAttempts:0,nextAttemptAt:null,terminalCode:null,updatedAt:new Date(t).toISOString()});return}else throw new p(e.terminalCode);if(e.nextAttemptAt){const o=Date.parse(e.nextAttemptAt)-t;if(o>0)throw new u(o)}}function b(t){const e=a(),o=Math.min((e.retryAttempts??0)+1,t.maxAttempts??8),i=t.now??Date.now();if(!t.retryable)throw l(s(),{...e,retryAttempts:o,nextAttemptAt:null,terminalCode:t.code,updatedAt:new Date(i).toISOString()}),y("permanentStops"),new p(t.code);const r=Math.min(5e3*2**Math.max(0,o-1),360*60*1e3),c=Math.round(r*.2*(Math.random()*2-1)),d=r+c;throw l(s(),{...e,retryAttempts:o,terminalCode:null,nextAttemptAt:new Date(i+d).toISOString(),updatedAt:new Date(i).toISOString()}),y("transientRetries"),new u(d)}function M(){const t=a();!t.retryAttempts&&!t.nextAttemptAt&&!t.terminalCode||l(s(),{...t,retryAttempts:0,nextAttemptAt:null,terminalCode:null,updatedAt:new Date().toISOString()})}function s(){return w("deletion-sync-state.json")}function l(t,e){const o=A.dirname(t);n.mkdirSync(o,{recursive:!0,mode:448});const i=`${t}.tmp-${process.pid}-${S.randomBytes(6).toString("hex")}`;let r;try{if(r=n.openSync(i,"wx",384),n.writeFileSync(r,`${JSON.stringify(e,null,2)}
2
+ `,"utf8"),n.fsyncSync(r),n.closeSync(r),r=void 0,f(i,t),process.platform!=="win32"){const c=n.openSync(o,"r");try{n.fsyncSync(c)}finally{n.closeSync(c)}}}finally{r!==void 0&&n.closeSync(r);try{n.unlinkSync(i)}catch{}}}export{u as DeletionSyncRetryWaitError,p as DeletionSyncTerminalError,O as assertDeletionSyncRunnable,M as clearDeletionSyncFailure,x as readDeletionSyncRevision,b as recordDeletionSyncFailure,C as writeDeletionSyncRevision};
@@ -1 +1 @@
1
- import{registerPiValidationSession as I}from"./pi-validation-session.js";import y from"node:fs/promises";import b from"node:path";import{randomUUID as C}from"node:crypto";import{isAvailableAgentType as E}from"@shennian/wire";import{createAgent as N}from"../../agents/adapter.js";import{buildManagedAgentEnv as S}from"../../agents/config-status.js";import{ManagerModeAdapter as T}from"../../agents/manager.js";import{NativeSessionLifecycleRegistry as O}from"../native-session-lifecycle.js";import{validateManagedAgentWorkDir as M}from"../../security/managed-agent-policy.js";const x="\u8BF7\u56DE\u590D hello\uFF0C\u7528\u4E8E\u786E\u8BA4\u4F60\u5DF2\u51C6\u5907\u597D\u3002",R=6e4,W=new O;async function L(e,r={}){try{return await P(e,r)}catch(t){const a=t instanceof Error?t.message:"";return{ok:!1,errorCode:a==="agent_validation_cleanup_failed"?a:v(t,"start")}}}async function P(e,r){if(!E(e.runtimeAdapter)||e.runtimeAdapter==="manager"||e.runtimeAdapter==="pi"||e.runtimeAdapter==="openclaw")return{ok:!1,errorCode:"agent_validation_runtime_unavailable"};if(!b.isAbsolute(e.workDir.trim()))return{ok:!1,errorCode:"agent_validation_workdir_invalid"};let t;try{t=await(r.resolveWorkDir??U)(e.workDir)}catch(i){return{ok:!1,errorCode:V(i)}}if(!(r.validateWorkDir?await r.validateWorkDir(t):(await M(t,async()=>t)).ok))return{ok:!1,errorCode:"agent_validation_workdir_invalid"};const s=(r.createAdapter??N)(e.runtimeAdapter);if(!s)return{ok:!1,errorCode:"agent_validation_runtime_unavailable"};const o=e.sessionMode==="manager"?new T(s,e.runtimeAdapter):s;let f=`agent-validation-${C()}`,l,u="",m="",_=null,c=null;const h=new Promise(i=>{c=i}),p=i=>{i.agentSessionId&&(u=i.agentSessionId,r.ignoreNativeSession?.(e.runtimeAdapter,u)),i.state==="delta"&&!i.thinking&&i.text&&(m+=i.text),i.state==="final"?(i.text&&(m+=i.text),c?.({kind:"final"})):(i.state==="error"||i.state==="aborted")&&c?.({kind:"error",error:i.message??i.state})},w=i=>c?.({kind:"error",error:i});o.on("agentEvent",p),o.on("error",w);const D=async()=>{e.runtimeAdapter==="pi-coding-agent"&&(l=await(r.registerPiSession??I)(),f=l.sessionId),o.configure?.({sessionId:f,env:S(e.runtimeAdapter)});try{await o.start(f,t,null,{persistNativeSession:!1})}catch(n){return{ok:!1,errorCode:v(n,"start")}}const i=new Promise(n=>{_=setTimeout(()=>n({kind:"timeout"}),r.timeoutMs??R)}),k=o.send(x,e.modelId??void 0).then(()=>h).catch(n=>({kind:"error",error:n})),d=await Promise.race([k,i]);return d.kind==="timeout"?{ok:!1,errorCode:"agent_validation_response_timeout"}:d.kind==="error"?{ok:!1,errorCode:v(d.error,"response")}:m.toLowerCase().includes("hello")?{ok:!0}:{ok:!1,errorCode:"agent_validation_response_mismatch"}};let A,g=!1;try{A=await D()}finally{_&&clearTimeout(_),o.removeListener("agentEvent",p),o.removeListener("error",w);try{await o.stop()}catch{l&&(g=!0)}finally{await l?.dispose().catch(()=>{g=!0})}u&&(r.cleanupNativeSession??((k,d,n)=>{W.execute(k,d,{workDir:n}).catch(()=>{})}))(e.runtimeAdapter,u,t)}return g?{ok:!1,errorCode:"agent_validation_cleanup_failed"}:A}async function Y(e,r){const t=j(r.params);if(!t){e.client.sendRes({type:"res",id:r.id,ok:!1,error:"agent_validation_invalid_configuration"});return}const a=await L(t,{ignoreNativeSession:(s,o)=>{e.nativeFusion?.ignoreNativeSource(s,o)}});e.client.sendRes(a.ok?{type:"res",id:r.id,ok:!0,payload:{ready:!0}}:{type:"res",id:r.id,ok:!1,error:a.errorCode})}async function U(e){const r=e.trim();if(!r)throw Object.assign(new Error("workdir_required"),{code:"EINVAL"});const t=await y.realpath(r);if(!(await y.stat(t)).isDirectory())throw Object.assign(new Error("workdir_not_directory"),{code:"ENOTDIR"});return t}function V(e){const r=F(e);return r==="ENOENT"?"agent_validation_workdir_not_found":r==="ENOTDIR"?"agent_validation_workdir_not_directory":r==="EACCES"||r==="EPERM"?"agent_validation_workdir_unavailable":"agent_validation_workdir_invalid"}function v(e,r){const t=e instanceof Error?e.message:String(e);return t==="agent_validation_server_upgrade_required"?t:/model.*(?:not found|unavailable|unsupported|invalid|access denied|permission denied|not (?:allowed|authorized))|(?:access|permission) denied.*model|unknown model/i.test(t)?"agent_validation_model_unavailable":/quota|insufficient.*(?:credit|balance)|billing|rate.?limit|\b429\b/i.test(t)?"agent_validation_quota_exceeded":/not found|enoent|command .* missing|is .* installed/i.test(t)?"agent_validation_runtime_unavailable":/unauth|authentication|not logged|login required|api key|token.*missing|forbidden|401|403/i.test(t)?"agent_validation_runtime_auth_required":/ECONNREFUSED|ECONNRESET|ENOTFOUND|ETIMEDOUT|network|fetch failed|connection.*(?:failed|closed|refused)/i.test(t)?"agent_validation_network_failed":r==="start"?"agent_validation_start_failed":"agent_validation_response_failed"}function F(e){return typeof e=="object"&&e&&"code"in e?String(e.code):""}function j(e){const r=typeof e.runtimeAdapter=="string"?e.runtimeAdapter:"",t=typeof e.workDir=="string"?e.workDir:"",a=e.sessionMode==="manager"?"manager":"standard";return!E(r)||!t.trim()?null:{runtimeAdapter:r,workDir:t,sessionMode:a,modelId:typeof e.modelId=="string"?e.modelId:null}}export{Y as handleAgentWorkspaceValidation,L as validateAgentWorkspaceConfiguration};
1
+ import p from"node:fs/promises";import D from"node:path";import{randomUUID as h}from"node:crypto";import{isAvailableAgentType as A}from"@shennian/wire";import{createAgent as I}from"../../agents/adapter.js";import{buildManagedAgentEnv as b}from"../../agents/config-status.js";import{ManagerModeAdapter as N}from"../../agents/manager.js";import{NativeSessionLifecycleRegistry as C}from"../native-session-lifecycle.js";import{validateManagedAgentWorkDir as S}from"../../security/managed-agent-policy.js";const T="\u8BF7\u56DE\u590D hello\uFF0C\u7528\u4E8E\u786E\u8BA4\u4F60\u5DF2\u51C6\u5907\u597D\u3002",O=6e4,M=new C;async function x(e,r={}){try{return await R(e,r)}catch(t){const n=t instanceof Error?t.message:"";return{ok:!1,errorCode:n==="agent_validation_cleanup_failed"?n:_(t,"start")}}}async function R(e,r){if(!A(e.runtimeAdapter)||e.runtimeAdapter==="manager"||e.runtimeAdapter==="pi"||e.runtimeAdapter==="openclaw")return{ok:!1,errorCode:"agent_validation_runtime_unavailable"};if(!D.isAbsolute(e.workDir.trim()))return{ok:!1,errorCode:"agent_validation_workdir_invalid"};let t;try{t=await(r.resolveWorkDir??W)(e.workDir)}catch(i){return{ok:!1,errorCode:L(i)}}if(!(r.validateWorkDir?await r.validateWorkDir(t):(await S(t,async()=>t)).ok))return{ok:!1,errorCode:"agent_validation_workdir_invalid"};const s=(r.createAdapter??I)(e.runtimeAdapter);if(!s)return{ok:!1,errorCode:"agent_validation_runtime_unavailable"};const o=e.sessionMode==="manager"?new N(s,e.runtimeAdapter):s,g=`agent-validation-${h()}`;let l="",u="",f=null,c=null;const y=new Promise(i=>{c=i}),k=i=>{i.agentSessionId&&(l=i.agentSessionId,r.ignoreNativeSession?.(e.runtimeAdapter,l)),i.state==="delta"&&!i.thinking&&i.text&&(u+=i.text),i.state==="final"?(i.text&&(u+=i.text),c?.({kind:"final"})):(i.state==="error"||i.state==="aborted")&&c?.({kind:"error",error:i.message??i.state})},v=i=>c?.({kind:"error",error:i});o.on("agentEvent",k),o.on("error",v);const E=async()=>{o.configure?.({sessionId:g,env:b(e.runtimeAdapter)});try{await o.start(g,t,null,{persistNativeSession:!1})}catch(a){return{ok:!1,errorCode:_(a,"start")}}const i=new Promise(a=>{f=setTimeout(()=>a({kind:"timeout"}),r.timeoutMs??O)}),m=o.send(T,e.modelId??void 0).then(()=>y).catch(a=>({kind:"error",error:a})),d=await Promise.race([m,i]);return d.kind==="timeout"?{ok:!1,errorCode:"agent_validation_response_timeout"}:d.kind==="error"?{ok:!1,errorCode:_(d.error,"response")}:u.toLowerCase().includes("hello")?{ok:!0}:{ok:!1,errorCode:"agent_validation_response_mismatch"}};let w;try{w=await E()}finally{f&&clearTimeout(f),o.removeListener("agentEvent",k),o.removeListener("error",v),await o.stop().catch(()=>{}),l&&(r.cleanupNativeSession??((m,d,a)=>{M.execute(m,d,{workDir:a}).catch(()=>{})}))(e.runtimeAdapter,l,t)}return w}async function J(e,r){const t=U(r.params);if(!t){e.client.sendRes({type:"res",id:r.id,ok:!1,error:"agent_validation_invalid_configuration"});return}const n=await x(t,{ignoreNativeSession:(s,o)=>{e.nativeFusion?.ignoreNativeSource(s,o)}});e.client.sendRes(n.ok?{type:"res",id:r.id,ok:!0,payload:{ready:!0}}:{type:"res",id:r.id,ok:!1,error:n.errorCode})}async function W(e){const r=e.trim();if(!r)throw Object.assign(new Error("workdir_required"),{code:"EINVAL"});const t=await p.realpath(r);if(!(await p.stat(t)).isDirectory())throw Object.assign(new Error("workdir_not_directory"),{code:"ENOTDIR"});return t}function L(e){const r=P(e);return r==="ENOENT"?"agent_validation_workdir_not_found":r==="ENOTDIR"?"agent_validation_workdir_not_directory":r==="EACCES"||r==="EPERM"?"agent_validation_workdir_unavailable":"agent_validation_workdir_invalid"}function _(e,r){const t=e instanceof Error?e.message:String(e);return t==="agent_validation_server_upgrade_required"?t:/model.*(?:not found|unavailable|unsupported|invalid|access denied|permission denied|not (?:allowed|authorized))|(?:access|permission) denied.*model|unknown model/i.test(t)?"agent_validation_model_unavailable":/quota|insufficient.*(?:credit|balance)|billing|rate.?limit|\b429\b/i.test(t)?"agent_validation_quota_exceeded":/not found|enoent|command .* missing|is .* installed/i.test(t)?"agent_validation_runtime_unavailable":/unauth|authentication|not logged|login required|api key|token.*missing|forbidden|401|403/i.test(t)?"agent_validation_runtime_auth_required":/ECONNREFUSED|ECONNRESET|ENOTFOUND|ETIMEDOUT|network|fetch failed|connection.*(?:failed|closed|refused)/i.test(t)?"agent_validation_network_failed":r==="start"?"agent_validation_start_failed":"agent_validation_response_failed"}function P(e){return typeof e=="object"&&e&&"code"in e?String(e.code):""}function U(e){const r=typeof e.runtimeAdapter=="string"?e.runtimeAdapter:"",t=typeof e.workDir=="string"?e.workDir:"",n=e.sessionMode==="manager"?"manager":"standard";return!A(r)||!t.trim()?null:{runtimeAdapter:r,workDir:t,sessionMode:n,modelId:typeof e.modelId=="string"?e.modelId:null}}export{J as handleAgentWorkspaceValidation,x as validateAgentWorkspaceConfiguration};
@@ -1 +1 @@
1
- import{buildManagedAgentEnv as c}from"../../agents/config-status.js";import{lookupClaudeTranscriptCwd as i}from"../../native-fusion/parsers.js";import{getManagedRoomIpcRoot as d,MANAGED_ROOM_IPC_ROOT_ENV as p}from"../../daemon-ipc/paths.js";function x(n){return c(n)}function f(n,o,e){return{}}function T(n,o,e,t,a,r,u,m){const l=e==="codex";n.configure?.({sessionId:o,externalChannel:t??null,env:{...x(e),...f(o,t,a),...r&&!l?{SHENNIAN_MANAGED_ROOM_CONTEXT:r,[p]:d()}:{}},...u?{managedAgentPolicy:u}:{},...l?{managedRoomContextToken:r??null,managedRoomExecutor:r?m??null:null}:{}})}function k(n,o,e){const t=o.managedRoomContextToken;t&&t!==e&&n.revokeManagedRoomContextToken?.(t),o.managedRoomContextToken=e}function v(n,o){const e=o?.managedRoomContextToken;!o||!e||(o.managedRoomContextToken=null,n.revokeManagedRoomContextToken?.(e))}function M(n,o,e){if(n!=="claude"||!e)return o;const t=o.trim();return!!t&&t.startsWith("-")&&!t.includes("/")?i(e)??o:o}export{T as configureAdapterForSession,f as externalChannelEnv,x as managedProviderEnv,M as maybeResolveClaudeImportedWorkDir,k as replaceManagedRoomContextToken,v as revokeManagedRoomContextToken};
1
+ import{buildManagedAgentEnv as m}from"../../agents/config-status.js";import{lookupClaudeTranscriptCwd as d}from"../../native-fusion/parsers.js";import{getManagedRoomIpcRoot as i,MANAGED_ROOM_IPC_ROOT_ENV as p}from"../../daemon-ipc/paths.js";function f(n){return m(n)}function x(n,e,o){return{}}function T(n,e,o,t,u,r,a,c){const l=o==="codex";n.configure?.({sessionId:e,externalChannel:t??null,env:{...f(o),...x(e,t,u),...r&&!l?{SHENNIAN_MANAGED_ROOM_CONTEXT:r,[p]:i()}:{}},...a?{managedAgentPolicy:a}:{},...l?{managedRoomContextToken:r??null,managedRoomExecutor:r?c??null:null}:{}})}function k(n,e,o){const t=e.managedRoomContextToken;t&&t!==o&&n.revokeManagedRoomContextToken?.(t),e.managedRoomContextToken=o,e.roomSendOutcome={successes:0,failureCodes:[]}}function s(n,e){const o=e?.managedRoomContextToken;!e||!o||(e.managedRoomContextToken=null,n.revokeManagedRoomContextToken?.(o))}function v(n,e,o){if(n!=="claude"||!o)return e;const t=e.trim();return!!t&&t.startsWith("-")&&!t.includes("/")?d(o)??e:e}export{T as configureAdapterForSession,x as externalChannelEnv,f as managedProviderEnv,v as maybeResolveClaudeImportedWorkDir,k as replaceManagedRoomContextToken,s as revokeManagedRoomContextToken};
@@ -1,2 +1,2 @@
1
- import{buildApprovalPendingPayload as $}from"@shennian/wire";import{createHash as x}from"node:crypto";import{AgentEventTerminalGate as k}from"../../agents/event-terminal-gate.js";import{reportLog as w}from"../../log-reporter.js";import{registerInternalNativeSource as M}from"../../native-fusion/source-visibility.js";import{requestSessionIndexPublish as f}from"../index-publisher.js";import{appendMessage as g,recordSession as p,upsertMessage as y}from"../store.js";import{writeToolDetail as D}from"../tool-detail-store.js";import{refreshMachineAgents as _}from"./agents.js";function S(a,e){const n=e?.managedRoomContextToken;!e||!n||(e.managedRoomContextToken=null,a.revokeManagedRoomContextToken?.(n))}function P(a){const e=a.indexOf(`
2
- `),n=e>0?Math.min(e,80):Math.min(a.length,80);return a.slice(0,n)}function q(a,e,n){return`${a}:${x("sha256").update(`${e}\0${n??""}`).digest("hex")}`}function C(a,e,n={}){if(a.state==="tool-call"||a.state==="tool-result"){const u={runId:a.runId,sourceSeq:a.seq};return{state:a.state,runId:a.runId,seq:a.seq,sessionId:e,detailRef:u,status:a.state==="tool-call"?"running":"completed",detailAvailable:!0,...a.durationMs!==void 0?{durationMs:a.durationMs}:{},...a.name?{name:a.name}:{},...a.source?{source:a.source}:{},...a.agentSessionId?{agentSessionId:a.agentSessionId}:{},...n}}return{...a,sessionId:e,...n}}const j=3e4;function O(a){return a.state==="heartbeat"?a.runPhase??null:a.state==="tool-call"||a.state==="tool-result"?"tool_running":a.state==="approval-pending"?"waiting_approval":a.state==="delta"?a.thinking?"thinking":"streaming_text":a.state==="init"||a.state==="start"?"thinking":null}function R(a,e){return a!=="manager"?a:e==="claude"?"claude":"codex"}function Y(a,e,n,u){let I=null;const T=new k;function m(t,r={}){a.client.sendAgentEvent({type:"event",event:"agent",payload:C(t,e,r),seq:t.seq,id:`agent-evt-${t.runId}-${t.seq}`})}function A(t){t?.heartbeatTimer&&(clearInterval(t.heartbeatTimer),t.heartbeatTimer=null)}function E(t){if(!t.currentRunId||!t.currentRunPhase)return;const r=t.heartbeatSeq++;a.client.sendAgentEvent({type:"event",event:"agent",payload:{state:"heartbeat",sessionId:e,runId:t.currentRunId,seq:r,runPhase:t.currentRunPhase},seq:r,id:`agent-heartbeat-${t.currentRunId}-${r}-${Date.now()}`})}function b(t){!t||t.heartbeatTimer||(t.heartbeatTimer=setInterval(()=>{E(t)},j),t.heartbeatTimer.unref?.())}function i(t){const r=t?.pendingTextEvent;!t||!r||!r.text||(g(e,{id:`agent-${r.runId}-${r.seq}`,sessionId:e,role:"agent",ts:Date.now(),payload:r.text}),f(a.client),m({state:"delta",runId:r.runId,seq:r.seq,text:r.text,thinking:r.thinking||void 0}),t.pendingTextEvent=null)}u.on("agentEvent",t=>{if(!T.accept(t))return;t.errorCode==="acp_auth_required"&&_(a).catch(()=>{});const r=a.sessions.get(e),c=O(t),d=t.state==="final"||t.state==="error"||t.state==="aborted";if(r&&(r.nextEventSeq=t.seq+1,d||(r.currentRunId=t.runId),!d&&c&&(r.currentRunPhase=c,b(r)),t.agentSessionId&&(r.agentSessionId=t.agentSessionId),r.journalRunId)){const o={agentSessionId:t.agentSessionId??r.agentSessionId,providerRunId:t.runId||null,lastObservedEventSeq:t.seq,...d?{}:{phase:c==="waiting_approval"?"waiting_approval":"running"}};a.runJournal?.update(e,r.journalRunId,o)}if(a.managerRuntime?.noteAgentEvent(e,t),t.state==="delta"&&t.thinking)return;t.state!=="delta"&&w({level:"info",sessionId:e,wsEvent:`agent.${t.state}`,wsDirection:"out",metadata:{runId:t.runId,seq:t.seq,agentType:n}});let s=!1;if(t.state==="tool-call"||t.state==="tool-result"){const o=t.toolCallId?`agent-tool-${x("sha256").update(`${t.runId}\0${t.toolCallId}`).digest("hex")}`:`agent-${t.runId}-${t.seq}`;D({sessionId:e,toolCallId:o,detailRevision:t.seq,ts:Date.now(),name:t.name??"tool",...t.args!==void 0?{args:t.args}:{},...t.result!==void 0?{result:t.result}:{},status:t.toolStatus??(t.state==="tool-call"?"running":"completed"),...t.durationMs!==void 0?{durationMs:t.durationMs}:{},detailRef:{runId:t.runId,sourceSeq:t.seq}}),(t.toolCallId?y:g)(e,{id:o,sessionId:e,role:"agent",ts:Date.now(),payload:JSON.stringify({v:1,type:t.state==="tool-call"?"tool_use":"tool_result",name:t.name,toolCallId:o,status:t.toolStatus??(t.state==="tool-call"?"running":"completed"),...t.durationMs!==void 0?{durationMs:t.durationMs}:{},detailRef:{runId:t.runId,sourceSeq:t.seq},detailRevision:t.seq,detailAvailable:!0,resultOmitted:t.state==="tool-result"})}),s=!0}else t.state==="approval-pending"?(g(e,{id:`agent-${t.runId||"run"}-${t.seq}`,sessionId:e,role:"agent",ts:Date.now(),payload:$({...t.approval,permission:t.permission})}),s=!0,r&&p({sessionId:e,agentType:n,workDir:r.workDir,agentSessionId:r.agentSessionId,attentionId:q("approval",t.runId,t.seq)})):(t.state==="error"||t.state==="aborted")&&t.message&&(g(e,{id:`agent-${t.runId||"run"}-${t.seq}`,sessionId:e,role:"agent",ts:Date.now(),payload:t.message}),s=!0);s&&f(a.client);const l=`${e}:${t.runId}`;if(t.state==="delta"&&!t.thinking&&t.text&&a.runTextAcc.set(l,(a.runTextAcc.get(l)??"")+t.text),t.agentSessionId&&t.agentSessionId!==I&&(r?.nativeSessionVisibility==="internal"&&M(e,R(n,t.source)??n,t.agentSessionId),I=t.agentSessionId,a.nativeFusion?.noteManagedSourceSession(e,R(n,t.source),t.agentSessionId),r&&p({sessionId:e,agentType:n,workDir:r.workDir,agentSessionId:t.agentSessionId}),a.client.sendEvent({type:"event",event:"session.update",payload:{session:{id:e,agentType:n,agentSessionId:t.agentSessionId}}})),t.state==="delta"){const o=t.text??"";if(!o)return;r?.pendingTextEvent&&r.pendingTextEvent.runId!==t.runId&&i(r),r&&!r.pendingTextEvent&&(r.pendingTextEvent={runId:t.runId,seq:t.seq,text:"",thinking:!1}),r?.pendingTextEvent&&(r.pendingTextEvent.text+=o,r.pendingTextEvent.seq=t.seq);return}let h={};if(t.state==="final"){i(r);const o=a.runTextAcc.get(l)??"";o&&(h={messageSummary:P(o)}),a.runTextAcc.delete(l)}else t.state==="error"||t.state==="aborted"?(i(r),a.runTextAcc.delete(l)):(t.state==="tool-call"||t.state==="tool-result"||t.state==="approval-pending")&&i(r);d&&r?.currentRunId===t.runId&&(S(a,r),r.currentRunId=null,r.currentRunPhase=null,r.nextEventSeq=0,A(r),a.chatQueue?.noteTerminal(e),p({sessionId:e,agentType:n,workDir:r.workDir,agentSessionId:r.agentSessionId,status:t.state==="final"?"completed":"failed",attentionId:q("terminal",t.runId),lastActivityAt:new Date().toISOString()}),f(a.client),r.journalRunId&&(a.runJournal?.remove(e,r.journalRunId),a.recoveryBarrier?.end(e),r.journalRunId=null)),m(t,h)}),u.on("error",t=>{console.error(`[chat.send] adapter error sessionId=${e} agentType=${n}: ${t.message}`);const r=a.sessions.get(e);S(a,r),r?.journalRunId&&(a.runJournal?.update(e,r.journalRunId,{phase:"terminal"}),a.runJournal?.remove(e,r.journalRunId),a.recoveryBarrier?.end(e),r.journalRunId=null),a.sessions.delete(e),a.chatQueue?.noteTerminal(e),a.client.sendEvent({type:"event",event:"agent",payload:{state:"error",sessionId:e,message:t.message,runId:"",seq:0}})})}export{Y as bindAdapterEvents,R as getNativeSourceAgentType};
1
+ import{buildApprovalPendingPayload as w}from"@shennian/wire";import{createHash as x}from"node:crypto";import{AgentEventTerminalGate as T}from"../../agents/event-terminal-gate.js";import{recordAcpAgentModels as M}from"../../agents/model-registry.js";import{reportLog as D}from"../../log-reporter.js";import{registerInternalNativeSource as k}from"../../native-fusion/source-visibility.js";import{requestSessionIndexPublish as c}from"../index-publisher.js";import{noteRunActivity as _}from"../run-stall.js";import{appendMessage as u,recordSession as m,upsertMessage as P}from"../store.js";import{writeToolDetail as j}from"../tool-detail-store.js";import{refreshMachineAgents as O}from"./agents.js";import{roomReplyFailureNotice as C}from"../room-reply-notice.js";function N(a){const r=a.indexOf(`
2
+ `),o=r>0?Math.min(r,80):Math.min(a.length,80);return a.slice(0,o)}function A(a,r,o){return`${a}:${x("sha256").update(`${r}\0${o??""}`).digest("hex")}`}function B(a,r,o={}){if(a.state==="tool-call"||a.state==="tool-result"){const i={runId:a.runId,sourceSeq:a.seq};return{state:a.state,runId:a.runId,seq:a.seq,sessionId:r,detailRef:i,status:a.state==="tool-call"?"running":"completed",detailAvailable:!0,...a.durationMs!==void 0?{durationMs:a.durationMs}:{},...a.name?{name:a.name}:{},...a.source?{source:a.source}:{},...a.agentSessionId?{agentSessionId:a.agentSessionId}:{},...o}}return{...a,sessionId:r,...o}}const H=3e4;function J(a){return a.state==="heartbeat"?a.runPhase??null:a.state==="tool-call"||a.state==="tool-result"?"tool_running":a.state==="approval-pending"?"waiting_approval":a.state==="delta"?a.thinking?"thinking":"streaming_text":a.state==="init"||a.state==="start"?"thinking":null}function R(a,r){return a!=="manager"?a:r==="claude"?"claude":"codex"}function v(a,r,o,i){let I=null;const E=new T;function S(t,n={}){a.client.sendAgentEvent({type:"event",event:"agent",payload:B(t,r,n),seq:t.seq,id:`agent-evt-${t.runId}-${t.seq}`})}o.startsWith("acp:")&&i.on("sessionOptions",()=>{try{const t=i.getSessionOptions?.().models??[];if(!M(o,t))return;const n=a.getAgentSnapshot?.();if(!n)return;a.client.sendEvent({type:"event",event:"machine.agents",payload:{agentList:n.map(e=>e.type),agents:n}})}catch{}});function $(t){t?.heartbeatTimer&&(clearInterval(t.heartbeatTimer),t.heartbeatTimer=null)}function y(t){if(!t.currentRunId||!t.currentRunPhase)return;const n=t.heartbeatSeq++;a.client.sendAgentEvent({type:"event",event:"agent",payload:{state:"heartbeat",sessionId:r,runId:t.currentRunId,seq:n,runPhase:t.currentRunPhase},seq:n,id:`agent-heartbeat-${t.currentRunId}-${n}-${Date.now()}`})}function b(t){!t||t.heartbeatTimer||(t.heartbeatTimer=setInterval(()=>{y(t)},H),t.heartbeatTimer.unref?.())}function g(t){const n=t?.pendingTextEvent;!t||!n||!n.text||(u(r,{id:`agent-${n.runId}-${n.seq}`,sessionId:r,role:"agent",ts:Date.now(),payload:n.text}),c(a.client),S({state:"delta",runId:n.runId,seq:n.seq,text:n.text,thinking:n.thinking||void 0}),t.pendingTextEvent=null)}i.on("agentEvent",t=>{if(!E.accept(t))return;let n=!1;t.errorCode==="acp_auth_required"&&O(a).catch(()=>{});const e=a.sessions.get(r),p=J(t),s=t.state==="final"||t.state==="error"||t.state==="aborted";if(e&&_(r,e,s),e&&(e.nextEventSeq=t.seq+1,s||(e.currentRunId=t.runId),!s&&p&&(e.currentRunPhase=p,b(e)),t.agentSessionId&&(e.agentSessionId=t.agentSessionId),e.journalRunId)){const l={agentSessionId:t.agentSessionId??e.agentSessionId,providerRunId:t.runId||null,lastObservedEventSeq:t.seq,...s?{}:{phase:p==="waiting_approval"?"waiting_approval":"running"}};a.runJournal?.update(r,e.journalRunId,l)}if(a.managerRuntime?.noteAgentEvent(r,t),t.state==="delta"&&t.thinking)return;t.state!=="delta"&&D({level:"info",sessionId:r,wsEvent:`agent.${t.state}`,wsDirection:"out",metadata:{runId:t.runId,seq:t.seq,agentType:o}});let f=!1;if(t.state==="tool-call"||t.state==="tool-result"){const l=t.toolCallId?`agent-tool-${x("sha256").update(`${t.runId}\0${t.toolCallId}`).digest("hex")}`:`agent-${t.runId}-${t.seq}`;j({sessionId:r,toolCallId:l,detailRevision:t.seq,ts:Date.now(),name:t.name??"tool",...t.args!==void 0?{args:t.args}:{},...t.result!==void 0?{result:t.result}:{},status:t.toolStatus??(t.state==="tool-call"?"running":"completed"),...t.durationMs!==void 0?{durationMs:t.durationMs}:{},detailRef:{runId:t.runId,sourceSeq:t.seq}}),(t.toolCallId?P:u)(r,{id:l,sessionId:r,role:"agent",ts:Date.now(),payload:JSON.stringify({v:1,type:t.state==="tool-call"?"tool_use":"tool_result",name:t.name,toolCallId:l,status:t.toolStatus??(t.state==="tool-call"?"running":"completed"),...t.durationMs!==void 0?{durationMs:t.durationMs}:{},detailRef:{runId:t.runId,sourceSeq:t.seq},detailRevision:t.seq,detailAvailable:!0,resultOmitted:t.state==="tool-result"})}),f=!0}else t.state==="approval-pending"?(u(r,{id:`agent-${t.runId||"run"}-${t.seq}`,sessionId:r,role:"agent",ts:Date.now(),payload:w({...t.approval,permission:t.permission})}),f=!0,e&&m({sessionId:r,agentType:o,workDir:e.workDir,agentSessionId:e.agentSessionId,attentionId:A("approval",t.runId,t.seq)})):(t.state==="error"||t.state==="aborted")&&t.message&&(u(r,{id:`agent-${t.runId||"run"}-${t.seq}`,sessionId:r,role:"agent",ts:Date.now(),payload:t.message}),f=!0);f&&c(a.client);const d=`${r}:${t.runId}`;if(t.state==="delta"&&!t.thinking&&t.text&&a.runTextAcc.set(d,(a.runTextAcc.get(d)??"")+t.text),t.agentSessionId&&t.agentSessionId!==I&&(e?.nativeSessionVisibility==="internal"&&k(r,R(o,t.source)??o,t.agentSessionId),I=t.agentSessionId,a.nativeFusion?.noteManagedSourceSession(r,R(o,t.source),t.agentSessionId),e&&m({sessionId:r,agentType:o,workDir:e.workDir,agentSessionId:t.agentSessionId}),a.client.sendEvent({type:"event",event:"session.update",payload:{session:{id:r,agentType:o,agentSessionId:t.agentSessionId}}})),t.state==="delta"){const l=t.text??"";if(!l)return;e?.pendingTextEvent&&e.pendingTextEvent.runId!==t.runId&&g(e),e&&!e.pendingTextEvent&&(e.pendingTextEvent={runId:t.runId,seq:t.seq,text:"",thinking:!1}),e?.pendingTextEvent&&(e.pendingTextEvent.text+=l,e.pendingTextEvent.seq=t.seq);return}let q={};if(t.state==="final"){g(e);const l=e?C(e):null;l&&(n=!0,u(r,{id:`room-reply-notice-${t.runId}`,sessionId:r,role:"agent",ts:Date.now(),payload:l}),c(a.client));const h=a.runTextAcc.get(d)??"";h&&(q={messageSummary:N(h)}),a.runTextAcc.delete(d)}else t.state==="error"||t.state==="aborted"?(g(e),a.runTextAcc.delete(d)):(t.state==="tool-call"||t.state==="tool-result"||t.state==="approval-pending")&&g(e);s&&e?.currentRunId===t.runId&&(e.currentRunId=null,e.currentRunPhase=null,e.nextEventSeq=0,$(e),a.chatQueue?.noteTerminal(r),m({sessionId:r,agentType:o,workDir:e.workDir,agentSessionId:e.agentSessionId,status:t.state==="final"&&!n?"completed":"failed",attentionId:A("terminal",t.runId),lastActivityAt:new Date().toISOString()}),c(a.client),e.journalRunId&&(a.runJournal?.remove(r,e.journalRunId),a.recoveryBarrier?.end(r),e.journalRunId=null)),S(t,q)}),i.on("error",t=>{console.error(`[chat.send] adapter error sessionId=${r} agentType=${o}: ${t.message}`);const n=a.sessions.get(r);n?.journalRunId&&(a.runJournal?.update(r,n.journalRunId,{phase:"terminal"}),a.runJournal?.remove(r,n.journalRunId),a.recoveryBarrier?.end(r),n.journalRunId=null),a.sessions.delete(r),a.chatQueue?.noteTerminal(r),a.chatQueue?.isDispatching?.(r)||(u(r,{id:`agent-error-${r}-${Date.now()}`,sessionId:r,role:"agent",ts:Date.now(),payload:t.message}),c(a.client)),a.client.sendEvent({type:"event",event:"agent",payload:{state:"error",sessionId:r,message:t.message,runId:"",seq:0}})})}export{v as bindAdapterEvents,R as getNativeSourceAgentType};
@@ -1 +1 @@
1
- import{createHash as u}from"node:crypto";import{buildManagedAgentPermissionPrompt as g}from"../../room/managed-prompt-composer.js";import{configureAdapterForSession as d,replaceManagedRoomContextToken as p}from"./chat-adapter-config.js";import{createActiveSession as y}from"./chat-session-lifecycle.js";import{validateManagedRoomExecutionContext as c}from"./chat.js";async function h(a,e,m=()=>{}){let o=null,s=null,i=null,r;if(e.source==="managed_room"||e.source==="managed_direct"||e.source==="manager_event"&&e.managedPolicyVersion){const n=await a.prepareManagedRecovery?.(e);if(!n)return{result:"recovery_failed",reason:"permission_context_unavailable",message:"managed recovery requires a fresh permission context"};if(n.agentType!==e.agentType||n.sessionMode!==e.sessionMode||a.resolvePath(n.workDir)!==a.resolvePath(e.workDir)||n.managedRoomPromptSnapshot.managedRoomPolicyVersion!==e.managedPolicyVersion||n.managedRoomPromptSnapshot.runtimePermissionPolicyHash!==e.managedRuntimePolicyHash)return a.revokeManagedRoomContextToken?.(n.managedRoomContextToken),{result:"recovery_failed",reason:"permission_context_unavailable",message:"managed recovery authority no longer matches the interrupted run"};if(o=n.managedAgentPolicy,e.source==="manager_event"||e.source==="managed_direct")s=n.managedRoomContextToken,i=n.systemPrompt,r={source:"managed_private",recovery:"upgrade"};else{const l={source:"room_activation",managedAgentPolicy:n.managedAgentPolicy,managedRoomPromptSnapshot:n.managedRoomPromptSnapshot};c(l,n.systemPrompt,n.managedRoomContextToken),s=n.managedRoomContextToken,i=n.systemPrompt,r={...l,recovery:"upgrade"}}}else if(e.source==="manager_worker"&&!e.managerWorkerNativeFull){const n=e.parentSessionId;if(o=n?a.managerRuntime?.getManagedAgentPolicyForWorker(n,e.workDir)??null:null,!o)return{result:"recovery_failed",reason:"permission_context_unavailable",message:"manager worker recovery requires the current parent policy"};if(u("sha256").update(JSON.stringify({requestedPolicy:o.requestedPolicy,enforcement:o.enforcement})).digest("hex")!==e.managedRuntimePolicyHash)return{result:"recovery_failed",reason:"permission_context_unavailable",message:"manager worker policy changed after interruption"};i=g({requestedPolicy:o.requestedPolicy,enforcement:o.enforcement,resolvedWorkDir:o.adapterPolicy.workDir})}r??={source:"upgrade_recovery",recovery:"upgrade"};let t=a.sessions.get(e.sessionId);return t||(t=await y(a,e.sessionId,e.agentType,e.sessionMode,e.workDir,e.agentSessionId,null,null,o,e.nativeSessionVisibility)),s&&(p(a,t,s),d(t.adapter,e.sessionId,e.agentType,null,null,s,o,a.managedRoomExecutor)),t.journalRunId=e.runId,t.currentRunId=e.providerRunId??`recovery:${e.runId}`,t.currentRunPhase="thinking",e.agentType==="codex"&&e.deliveryMode==="external_owner_queue"&&e.agentSessionId&&e.externalRunId&&a.nativeFusion?.restoreExternalObservation({sessionId:e.sessionId,sourceSessionKey:e.agentSessionId,runId:e.externalRunId,canonicalMessageId:e.clientMessageId,createdAt:Date.parse(e.createdAt)})?{result:"reattached"}:t.adapter.recoverInterruptedRun({sessionId:e.sessionId,shennianRunId:e.runId,workDir:e.workDir,agentSessionId:e.agentSessionId,modelId:e.modelId,reasoningEffort:e.reasoningEffort,providerRunId:e.providerRunId,lastObservedEventSeq:e.lastObservedEventSeq,continueDispatchState:e.continueDispatchState,systemPrompt:i,sendContext:r,beforeContinueDispatch:m})}export{h as recoverJournaledRun};
1
+ import{createHash as u}from"node:crypto";import{buildManagedAgentPermissionPrompt as g}from"../../room/managed-prompt-composer.js";import{configureAdapterForSession as d,replaceManagedRoomContextToken as p}from"./chat-adapter-config.js";import{createActiveSession as y}from"./chat-session-lifecycle.js";import{validateManagedRoomExecutionContext as c}from"./chat.js";async function h(a,e,m=()=>{}){let o=null,s=null,i=null,r;if(e.source==="managed_room"||e.source==="managed_direct"||e.source==="manager_event"&&e.managedPolicyVersion){const n=await a.prepareManagedRecovery?.(e);if(!n)return{result:"recovery_failed",reason:"permission_context_unavailable",message:"managed recovery requires a fresh permission context"};if(n.agentType!==e.agentType||n.sessionMode!==e.sessionMode||a.resolvePath(n.workDir)!==a.resolvePath(e.workDir)||n.managedRoomPromptSnapshot.managedRoomPolicyVersion!==e.managedPolicyVersion||n.managedRoomPromptSnapshot.runtimePermissionPolicyHash!==e.managedRuntimePolicyHash)return a.revokeManagedRoomContextToken?.(n.managedRoomContextToken),{result:"recovery_failed",reason:"permission_context_unavailable",message:"managed recovery authority no longer matches the interrupted run"};if(o=n.managedAgentPolicy,e.source==="manager_event"||e.source==="managed_direct")s=n.managedRoomContextToken,i=n.systemPrompt,r={source:"managed_private",recovery:"upgrade"};else{const l={source:"room_activation",managedAgentPolicy:n.managedAgentPolicy,managedRoomPromptSnapshot:n.managedRoomPromptSnapshot};c(l,n.systemPrompt,n.managedRoomContextToken),s=n.managedRoomContextToken,i=n.systemPrompt,r={...l,recovery:"upgrade"}}}else if(e.source==="manager_worker"&&!e.managerWorkerNativeFull){const n=e.parentSessionId;if(o=n?a.managerRuntime?.getManagedAgentPolicyForWorker(n,e.workDir)??null:null,!o)return{result:"recovery_failed",reason:"permission_context_unavailable",message:"manager worker recovery requires the current parent policy"};if(u("sha256").update(JSON.stringify({requestedPolicy:o.requestedPolicy,enforcement:o.enforcement})).digest("hex")!==e.managedRuntimePolicyHash)return{result:"recovery_failed",reason:"permission_context_unavailable",message:"manager worker policy changed after interruption"};i=g({requestedPolicy:o.requestedPolicy,enforcement:o.enforcement,resolvedWorkDir:o.adapterPolicy.workDir})}r??={source:"upgrade_recovery",recovery:"upgrade"};let t=a.sessions.get(e.sessionId);return t||(t=await y(a,e.sessionId,e.agentType,e.sessionMode,e.workDir,e.agentSessionId,null,null,o,e.nativeSessionVisibility)),s&&(p(a,t,s),d(t.adapter,e.sessionId,e.agentType,null,null,s,o,a.managedRoomExecutor)),t.journalRunId=e.runId,t.currentRunId=e.providerRunId??`recovery:${e.runId}`,t.currentRunPhase="thinking",t.lastProviderActivityAt=Date.now(),e.agentType==="codex"&&e.deliveryMode==="external_owner_queue"&&e.agentSessionId&&e.externalRunId&&a.nativeFusion?.restoreExternalObservation({sessionId:e.sessionId,sourceSessionKey:e.agentSessionId,runId:e.externalRunId,canonicalMessageId:e.clientMessageId,createdAt:Date.parse(e.createdAt)})?{result:"reattached"}:t.adapter.recoverInterruptedRun({sessionId:e.sessionId,shennianRunId:e.runId,workDir:e.workDir,agentSessionId:e.agentSessionId,modelId:e.modelId,reasoningEffort:e.reasoningEffort,providerRunId:e.providerRunId,lastObservedEventSeq:e.lastObservedEventSeq,continueDispatchState:e.continueDispatchState,systemPrompt:i,sendContext:r,beforeContinueDispatch:m})}export{h as recoverJournaledRun};
@@ -1 +1 @@
1
- import{buildUserMessagePayload as ie}from"@shennian/wire";import{createHash as Me,randomUUID as Ae}from"node:crypto";import de from"node:os";import{reportLog as b}from"../../log-reporter.js";import{getPersonalSyncIdentity as Pe}from"../../personal-sync/epoch.js";import{buildManagedAgentPermissionPrompt as Ee}from"../../room/managed-prompt-composer.js";import{parsePersonalAttachmentReferences as De}from"../attachment-reference.js";import{assertFinishedAttachmentReferences as be,markAttachmentReferences as Te}from"../attachment-transfer-store.js";import{requestSessionIndexPublish as _}from"../index-publisher.js";import{isTrustedManagerEvent as xe}from"../manager-event-authority.js";import{mergeProjectedSessions as Ce}from"../projection.js";import{appendMessage as le,getDaemonInstallationId as ce,getMessageDeliveryStatus as $e,upsertMessage as S}from"../store.js";import{configureAdapterForSession as ue,maybeResolveClaudeImportedWorkDir as We,replaceManagedRoomContextToken as ge,revokeManagedRoomContextToken as me}from"./chat-adapter-config.js";import{resolveNativeSessionVisibility as qe,sendSessionUpdateEvent as je,validateManagedRoomExecutionContext as Fe}from"./chat-context.js";import{getNativeSourceAgentType as Be}from"./chat-events.js";import{createActiveSession as Je,disposeSession as C,prepareAdapterTurn as Oe,rememberProcessedReqId as ze,sendAdapterTurn as pe}from"./chat-session-lifecycle.js";import{sendSessionUpdateEvent as mn,validateManagedRoomExecutionContext as pn}from"./chat-context.js";function fe(e,t){return`Agent send failed: ${t instanceof Error?t.message:String(t)}`}function Ue(e,t){const o=e.sessions.get(t),u=o?.currentRunId;if(!o||!u)return;const n=o.nextEventSeq;e.runTextAcc.delete(`${t}:${u}`),me(e,o),o.pendingTextEvent=null,o.currentRunId=null,o.currentRunPhase=null,o.nextEventSeq=0,o.heartbeatTimer&&(clearInterval(o.heartbeatTimer),o.heartbeatTimer=null),e.client.sendAgentEvent({type:"event",event:"agent",payload:{state:"aborted",sessionId:t,runId:u,seq:n},seq:n,id:`agent-evt-${u}-${n}`}),o.journalRunId&&(e.runJournal?.remove(t,o.journalRunId),e.recoveryBarrier?.end(t),o.journalRunId=null),e.chatQueue?.noteTerminal(t)}async function ln(e,t,o){const u=o?t.params.managedRoomContextToken??null:null;let n=!1;try{await He(e,t,o,()=>{n=!0})}finally{u&&!n&&e.revokeManagedRoomContextToken?.(u)}}async function He(e,t,o,u){if(e.processedReqIds.has(t.id)){e.client.sendRes({type:"res",id:t.id,ok:!0});return}const{sessionId:n,text:Q,agentType:A,sessionMode:ye,managerConfig:L,workDir:ve,agentSessionId:m,modelId:h,systemPrompt:Z,managerDefaultWorkerAgentType:$,managerDefaultWorkerModelId:W,reasoningEffort:q,clientMessageId:he,sessionListProjection:Ie,waitForDispatch:G,responseId:Re,managedRoomContextToken:I,suppressExternalOwnerEcho:_e,queuedBatchItems:j,managedWorkerParentSessionId:g,managerWorkerRoomOrigin:F,managerWorkerNativeFull:B,managedRecoverySource:P}=t.params,l=Re||t.id,X=t.params;if((X.managerEventId||X.managerEventRoomOrigin||g||F||B||P)&&!xe(t))throw new Error("manager_event_source_untrusted");Ce(Ie);const T=null,J="";if(o?.source==="room_activation"&&(Fe(o,Z,I),A==="manager"))throw new Error("managed_agent_manager_mode_unsupported");if(o?.source==="manager_event"){if(!o.managedAgentPolicy)throw new Error("managed_agent_policy_missing");if(!I||!/^mrc_[A-Za-z0-9_-]{43}$/.test(I))throw new Error("managed_room_context_unavailable")}if(ze(e,t.id),!n||!Q){e.processedReqIds.delete(t.id),e.client.sendRes({type:"res",id:l,ok:!1,error:"sessionId and text are required"});return}if(e.recoveryBarrier?.isDispatchFrozen()){e.processedReqIds.delete(t.id),e.client.sendRes({type:"res",id:l,ok:!1,error:"upgrade_in_progress"});return}e.recoveryBarrier?.isRecovering(n)&&await e.recoveryBarrier.wait(n);const d=A,R=d==="manager"||ye==="manager"?"manager":"standard";R==="manager"&&e.managerRuntime?.setManagerWorkerDefaults(n,$??null,W??null);const E=(d==="claude"||d==="codex"||d==="workbuddy"||d==="pi-coding-agent"||d.startsWith("acp:"))&&typeof q=="string"&&q.trim()?q.trim():void 0,y=e.resolvePath(We(d,ve||de.homedir(),m)||de.homedir()),f=o?.managedAgentPolicy??(g?e.managerRuntime?.getManagedAgentPolicyForWorker(g,y)??null:null);if(g&&!B&&!f)throw new Error("managed_worker_permission_context_unavailable");const O=qe(n,d,R,!!(f||g||o)),z=g&&f?Ee({requestedPolicy:f.requestedPolicy,enforcement:f.enforcement,resolvedWorkDir:f.adapterPolicy.workDir}):Z??null,Se=y,x=De(t.params.attachments);x?.length&&be(y,x);const p={text:Q,attachments:x,localized:!1};let r=e.sessions.get(n);if(r){if(r.lastActiveAt=Date.now(),r.agentType!==d||(r.sessionMode??(r.agentType==="manager"?"manager":"standard"))!==R||r.workDir!==y||(r.nativeSessionVisibility??"user")!==O||JSON.stringify(r.externalChannel??null)!==JSON.stringify(T??null)){e.sessions.delete(n);try{await C(e,r)}catch{e.processedReqIds.delete(t.id)}r=void 0}else if(m&&r.agentSessionId!==m)try{await r.adapter.resume(m),r.agentSessionId=m}catch{e.sessions.delete(n);try{await C(e,r)}catch{e.processedReqIds.delete(t.id)}r=void 0}}if(!r)try{r=await Je(e,n,d,R,y,m,T,J,f,O)}catch(a){const i=a instanceof Error&&a.message.startsWith("Unsupported agent:")?a.message:`Failed to start ${A}: ${a instanceof Error?a.message:String(a)}`;console.error(`[chat.send] start failed reqId=${t.id} sessionId=${n} agentType=${A} workDir=${y} agentSessionId=${m??""}: ${i}`),e.client.sendEvent({type:"event",event:"agent",payload:{state:"error",sessionId:n,message:i,runId:"",seq:0}}),e.processedReqIds.delete(t.id),e.client.sendRes({type:"res",id:l,ok:!1,error:i});return}const c={id:he??`user-${t.id}`,sessionId:n,role:"user",ts:Date.now(),payload:ie(p.text,p.attachments)},Y=Array.isArray(j)&&j.length>0?j.map(a=>({id:a.clientMessageId??a.id,sessionId:n,role:P==="manager_event"||t.params.scheduledDelivery===!0?"agent":"user",ts:P==="manager_event"||o?.source==="room_activation"?Date.parse(a.createdAt):c.ts,payload:ie(a.text,a.attachments)})):null,k=Y??[c],U=$e(n,c.id),ee=U.state==="queued";if(U.state!=="not_found"&&!ee){e.client.sendRes({type:"res",id:l,ok:!0,payload:U});return}if(G){(o?.source==="room_activation"||o?.source==="manager_event")&&ue(r.adapter,n,d,T,J,I,o.managedAgentPolicy,e.managedRoomExecutor);try{await Oe(r.adapter,p.text,h,E,p.attachments,z,o?.source==="room_activation"?{source:"room_activation"}:o?.source==="manager_event"?{source:"managed_private"}:void 0)}catch(a){e.processedReqIds.delete(t.id),e.sessions.delete(n),await C(e,r),e.client.sendRes({type:"res",id:l,ok:!1,error:a instanceof Error?a.message:String(a)});return}}b({level:"info",sessionId:n,wsEvent:"chat.send.start",metadata:{reqId:t.id,agentType:d,sessionMode:R,managerConfig:L??null,modelId:h,managerDefaultWorkerAgentType:$??null,managerDefaultWorkerModelId:W??null,reasoningEffort:E,...o?.source==="room_activation"?{managedRoomPromptSnapshot:o.managedRoomPromptSnapshot}:{}}});const w=(a,i)=>({...Pe(ce()),sessionId:n,clientMessageId:c.id,messageId:c.id,deliveryState:a,bodyRevision:i,committedAt:new Date(c.ts).toISOString()}),H=a=>{e.nativeFusion?.registerManagedSend({sessionId:n,agentType:d,sourceAgentType:Be(d,h),canonicalMessageId:c.id,sourceSessionKey:r.agentSessionId??m??null,text:p.text,managedEchoPolicy:a?.deliveryMode==="external_owner_queue"&&!_e?"import_following":"suppress_following",externalRunId:a?.deliveryMode==="external_owner_queue"?`codex-external-${a.queuedSubmissionId??c.id}`:null})},ne=()=>{r.currentRunId||(r.currentRunId=`dispatch:${t.id}`,r.currentRunPhase="thinking",r.nextEventSeq=0)},ae=()=>{if(!e.runJournal||r.journalRunId)return;const a=`run:${Ae()}`;e.runJournal.begin({daemonInstallationId:ce(),sessionId:n,runId:a,clientMessageId:c.id,agentType:d,sessionMode:R,workDir:y,agentSessionId:r.agentSessionId??m??null,modelId:h??null,reasoningEffort:E??null,nativeSessionVisibility:O,providerRunId:null,source:P==="manager_event"?"manager_event":o?.source==="room_activation"?P==="managed_direct"?"managed_direct":"managed_room":g?"manager_worker":"personal",parentSessionId:g??null,managerWorkerNativeFull:B===!0,roomContextRegistryKey:typeof t.params.roomContextRegistryKey=="string"?t.params.roomContextRegistryKey:null,activationCauseMessageId:typeof t.params.activationCauseMessageId=="string"?t.params.activationCauseMessageId:null,rootCauseMessageId:typeof t.params.rootCauseMessageId=="string"?t.params.rootCauseMessageId:null,activationSequence:typeof t.params.activationSequence=="string"?t.params.activationSequence:null,activationHopCount:typeof t.params.activationHopCount=="number"?t.params.activationHopCount:null,managedPolicyVersion:o?.source==="room_activation"?o.managedRoomPromptSnapshot.managedRoomPolicyVersion:o?.source==="manager_event"?o.managedRoomPolicyVersion:null,managedRuntimePolicyHash:(o?.source==="room_activation"?o.managedRoomPromptSnapshot.runtimePermissionPolicyHash:o?.source==="manager_event"?o.managedRuntimePolicyHash:null)??(f?Me("sha256").update(JSON.stringify({requestedPolicy:f.requestedPolicy,enforcement:f.enforcement})).digest("hex"):null)}),r.journalRunId=a},oe=()=>{if(H(),ee){let a=0;for(const[i,s]of k.entries()){const v=S(n,s,"queued");i===0&&(a=v)}return w("queued",a)}try{je(e,{sessionId:n,agentType:d,sessionMode:R,managerConfig:L??null,workDir:Se,agentSessionId:r.agentSessionId??m??null,modelId:h,managerDefaultWorkerAgentType:$??null,managerDefaultWorkerModelId:W??null});let a=0;for(const[i,s]of k.entries()){const v=Y?S(n,s,"committed"):le(n,s,"committed");i===0&&(a=v)}return Te(y,x??[]),_(e.client),w("committed",a)}catch(a){throw e.nativeFusion?.cancelManagedSend?.(c.id),a}},te=a=>{r.journalRunId&&e.runJournal?.update(n,r.journalRunId,{deliveryMode:a?.deliveryMode==="external_owner_queue"?"external_owner_queue":"direct",externalRunId:a?.deliveryMode==="external_owner_queue"?`codex-external-${a.queuedSubmissionId??c.id}`:null})},re=a=>{r.currentRunId||(r.nextEventSeq=0),te(a),H(a);const i=a?.deliveryMode==="external_owner_queue"?"queued":"executing";let s=0;for(const[v,D]of k.entries()){const M=S(n,D,i);v===0&&(s=M)}return _(e.client),w(i,s)},N=async(a,i)=>{e.nativeFusion?.cancelManagedSend?.(c.id);const s=fe(d,a);if(console.error(`[chat.send] send failed reqId=${t.id} sessionId=${n} agentType=${A} workDir=${y} agentSessionId=${r.agentSessionId??m??""}: ${s}`),r.adapter.hasPendingAuthenticationRetry?.()===!0){i&&e.client.sendRes({type:"res",id:l,ok:!0,payload:w("queued",0)});return}e.sessions.delete(n),r.journalRunId&&(e.runJournal?.update(n,r.journalRunId,{phase:"terminal"}),e.runJournal?.remove(n,r.journalRunId),e.recoveryBarrier?.end(n),r.journalRunId=null);try{await C(e,r)}catch{}if(e.client.sendEvent({type:"event",event:"agent",payload:{state:"error",sessionId:n,message:s,runId:"",seq:0}}),!i){const D={id:`agent-error-${t.id}-${Date.now()}`,sessionId:n,role:"agent",ts:Date.now(),payload:s};le(n,D),_(e.client)}i&&(e.processedReqIds.delete(t.id),e.client.sendRes({type:"res",id:l,ok:!1,error:s}))};if(G){try{oe()}catch(s){e.processedReqIds.delete(t.id),e.client.sendRes({type:"res",id:l,ok:!1,error:`Durable message commit failed: ${s instanceof Error?s.message:String(s)}`});return}ae(),ne();let a;try{(o?.source==="room_activation"||o?.source==="manager_event")&&(ge(e,r,I),u());const s=p.attachments;g&&e.managerRuntime?.prepareWorkerTurnOrigin(n,F),a=await pe(r.adapter,p.text,h,E,s,z,o?.source==="room_activation"?{source:"room_activation"}:o?.source==="manager_event"?{source:"managed_private"}:void 0),b({level:"info",sessionId:n,wsEvent:"chat.send.done",metadata:{reqId:t.id}})}catch(s){if(g&&e.managerRuntime?.prepareWorkerTurnOrigin(n),r.adapter.hasPendingAuthenticationRetry?.()){let M=0;for(const[V,K]of k.entries()){const we=S(n,K,"queued");V===0&&(M=we)}_(e.client),await N(s,!1),e.client.sendRes({type:"res",id:l,ok:!0,payload:w("queued",M)});return}let v=0;for(const[M,V]of k.entries()){const K=S(n,V,"failed");M===0&&(v=K)}_(e.client);const D=fe(d,s);await N(s,!1),e.client.sendRes({type:"res",id:l,ok:!0,payload:{...w("failed",v),failureMessage:D}});return}const i=re(a??void 0);e.client.sendRes({type:"res",id:l,ok:!0,payload:{...i,...p.localized?{localizedAttachments:!0}:{},...a?.deliveryMode==="external_owner_queue"?{queued:!0,deliveryMode:a.deliveryMode,queuedSubmissionId:a.queuedSubmissionId}:{}}}),b({level:"info",sessionId:n,wsEvent:"chat.send.res",metadata:{reqId:t.id,ok:!0}});return}let se;try{se=oe()}catch(a){e.processedReqIds.delete(t.id),e.client.sendRes({type:"res",id:l,ok:!1,error:`Durable message commit failed: ${a instanceof Error?a.message:String(a)}`});return}ae(),ne(),(o?.source==="room_activation"||o?.source==="manager_event")&&(ue(r.adapter,n,d,T,J,I,o.managedAgentPolicy,e.managedRoomExecutor),ge(e,r,I),u()),re(),e.client.sendRes({type:"res",id:l,ok:!0,payload:{...se,...p.localized?{localizedAttachments:!0}:{}}}),b({level:"info",sessionId:n,wsEvent:"chat.send.res",metadata:{reqId:t.id,ok:!0}});const ke=p.attachments;g&&e.managerRuntime?.prepareWorkerTurnOrigin(n,F),pe(r.adapter,p.text,h,E,ke,z,o?.source==="room_activation"?{source:"room_activation"}:o?.source==="manager_event"?{source:"managed_private"}:void 0).then(a=>{if(a?.deliveryMode==="external_owner_queue"){te(a),H(a);for(const i of k)S(n,i,"queued");_(e.client)}b({level:"info",sessionId:n,wsEvent:"chat.send.done",metadata:{reqId:t.id}})}).catch(a=>{g&&e.managerRuntime?.prepareWorkerTurnOrigin(n),S(n,c,r.adapter.hasPendingAuthenticationRetry?.()?"queued":"failed"),_(e.client),N(a,!1)})}async function cn(e,t){const{sessionId:o,discardPending:u}=t.params,n=e.sessions.get(o);if(n){n.journalRunId&&e.runJournal?.markAborted(o,n.journalRunId),me(e,n);try{await n.adapter.stop()}catch{e.client.sendRes({type:"res",id:t.id,ok:!1,error:"agent_stop_failed"});return}Ue(e,o),e.activityPublisher?.publish(o,null)}u===!0&&e.chatQueue?.purgeSession(o),e.client.sendRes({type:"res",id:t.id,ok:!0})}export{cn as handleChatAbort,ln as handleChatSend,mn as sendSessionUpdateEvent,pn as validateManagedRoomExecutionContext};
1
+ import{buildUserMessagePayload as de}from"@shennian/wire";import{createHash as Ae,randomUUID as Me}from"node:crypto";import le from"node:os";import{reportLog as b}from"../../log-reporter.js";import{getPersonalSyncIdentity as Pe}from"../../personal-sync/epoch.js";import{buildManagedAgentPermissionPrompt as Ee}from"../../room/managed-prompt-composer.js";import{parsePersonalAttachmentReferences as De}from"../attachment-reference.js";import{assertFinishedAttachmentReferences as Te,markAttachmentReferences as be}from"../attachment-transfer-store.js";import{requestSessionIndexPublish as R}from"../index-publisher.js";import{isTrustedManagerEvent as $e}from"../manager-event-authority.js";import{mergeProjectedSessions as xe}from"../projection.js";import{appendMessage as ce,getDaemonInstallationId as ue,getMessageDeliveryStatus as Ce,upsertMessage as _}from"../store.js";import{configureAdapterForSession as ge,maybeResolveClaudeImportedWorkDir as We,replaceManagedRoomContextToken as pe,revokeManagedRoomContextToken as me}from"./chat-adapter-config.js";import{resolveNativeSessionVisibility as qe,sendSessionUpdateEvent as je,validateManagedRoomExecutionContext as Fe}from"./chat-context.js";import{getNativeSourceAgentType as Oe}from"./chat-events.js";import{noteRunActivity as Be}from"../run-stall.js";import{createActiveSession as Je,disposeSession as C,prepareAdapterTurn as ze,rememberProcessedReqId as Ue,sendAdapterTurn as fe}from"./chat-session-lifecycle.js";import{sendSessionUpdateEvent as vn,validateManagedRoomExecutionContext as hn}from"./chat-context.js";function ye(e,n){return`Agent send failed: ${n instanceof Error?n.message:String(n)}`}function He(e,n){const t=e.sessions.get(n),s=t?.currentRunId;if(!t||!s)return;const o=t.nextEventSeq;e.runTextAcc.delete(`${n}:${s}`),me(e,t),t.pendingTextEvent=null,Be(n,t,!0),t.currentRunId=null,t.currentRunPhase=null,t.nextEventSeq=0,t.heartbeatTimer&&(clearInterval(t.heartbeatTimer),t.heartbeatTimer=null),e.client.sendAgentEvent({type:"event",event:"agent",payload:{state:"aborted",sessionId:n,runId:s,seq:o},seq:o,id:`agent-evt-${s}-${o}`}),t.journalRunId&&(e.runJournal?.remove(n,t.journalRunId),e.recoveryBarrier?.end(n),t.journalRunId=null),e.chatQueue?.noteTerminal(n)}async function pn(e,n,t){const s=t?n.params.managedRoomContextToken??null:null;let o=!1;try{await Ne(e,n,t,()=>{o=!0})}finally{s&&!o&&e.revokeManagedRoomContextToken?.(s)}}async function Ne(e,n,t,s){if(e.processedReqIds.has(n.id)){e.client.sendRes({type:"res",id:n.id,ok:!0});return}const{sessionId:o,text:p,agentType:I,sessionMode:W,managerConfig:Z,workDir:ve,agentSessionId:m,modelId:S,systemPrompt:G,managerDefaultWorkerAgentType:q,managerDefaultWorkerModelId:j,reasoningEffort:F,clientMessageId:he,sessionListProjection:Ie,waitForDispatch:X,responseId:Re,managedRoomContextToken:w,suppressExternalOwnerEcho:_e,queuedBatchItems:O,managedWorkerParentSessionId:g,managerWorkerRoomOrigin:B,managerWorkerNativeFull:J,managedRecoverySource:E}=n.params,c=Re||n.id,Y=n.params;if((Y.managerEventId||Y.managerEventRoomOrigin||g||B||J||E)&&!$e(n))throw new Error("manager_event_source_untrusted");xe(Ie);const $=null,z="";if(t?.source==="room_activation"&&(Fe(t,G,w),I==="manager"))throw new Error("managed_agent_manager_mode_unsupported");if(t?.source==="manager_event"){if(!t.managedAgentPolicy)throw new Error("managed_agent_policy_missing");if(!w||!/^mrc_[A-Za-z0-9_-]{43}$/.test(w))throw new Error("managed_room_context_unavailable")}if(Ue(e,n.id),!o||!p){e.processedReqIds.delete(n.id),e.client.sendRes({type:"res",id:c,ok:!1,error:"sessionId and text are required"});return}if(e.recoveryBarrier?.isDispatchFrozen()){e.processedReqIds.delete(n.id),e.client.sendRes({type:"res",id:c,ok:!1,error:"upgrade_in_progress"});return}e.recoveryBarrier?.isRecovering(o)&&await e.recoveryBarrier.wait(o);const l=I,k=l==="manager"||W==="manager"?"manager":"standard";k==="manager"&&e.managerRuntime?.setManagerWorkerDefaults(o,q??null,j??null);const D=(l==="claude"||l==="codex"||l==="workbuddy"||l==="pi-coding-agent"||l.startsWith("acp:"))&&typeof F=="string"&&F.trim()?F.trim():void 0,v=e.resolvePath(We(l,ve||le.homedir(),m)||le.homedir()),y=t?.managedAgentPolicy??(g?e.managerRuntime?.getManagedAgentPolicyForWorker(g,v)??null:null);if(g&&!J&&!y)throw new Error("managed_worker_permission_context_unavailable");const U=qe(o,l,k,!!(y||g||t)),H=g&&y?Ee({requestedPolicy:y.requestedPolicy,enforcement:y.enforcement,resolvedWorkDir:y.adapterPolicy.workDir}):G??null,Se=v,x=De(n.params.attachments);x?.length&&Te(v,x);const f={text:p,attachments:x,localized:!1};let r=e.sessions.get(o);if(r){if(r.lastActiveAt=Date.now(),r.agentType!==l||(r.sessionMode??(r.agentType==="manager"?"manager":"standard"))!==k||r.workDir!==v||(r.nativeSessionVisibility??"user")!==U||JSON.stringify(r.externalChannel??null)!==JSON.stringify($??null)){e.sessions.delete(o);try{await C(e,r)}catch{e.processedReqIds.delete(n.id)}r=void 0}else if(m&&r.agentSessionId!==m)try{await r.adapter.resume(m),r.agentSessionId=m}catch{e.sessions.delete(o);try{await C(e,r)}catch{e.processedReqIds.delete(n.id)}r=void 0}}if(!r)try{r=await Je(e,o,l,k,v,m,$,z,y,U)}catch(a){const d=a instanceof Error&&a.message.startsWith("Unsupported agent:")?a.message:`Failed to start ${I}: ${a instanceof Error?a.message:String(a)}`;console.error(`[chat.send] start failed reqId=${n.id} sessionId=${o} agentType=${I} workDir=${v} agentSessionId=${m??""}: ${d}`),e.client.sendEvent({type:"event",event:"agent",payload:{state:"error",sessionId:o,message:d,runId:"",seq:0}}),e.processedReqIds.delete(n.id),e.client.sendRes({type:"res",id:c,ok:!1,error:d});return}const u={id:he??`user-${n.id}`,sessionId:o,role:"user",ts:Date.now(),payload:de(f.text,f.attachments)},ee=Array.isArray(O)&&O.length>0?O.map(a=>({id:a.clientMessageId??a.id,sessionId:o,role:E==="manager_event"||n.params.scheduledDelivery===!0?"agent":"user",ts:E==="manager_event"||t?.source==="room_activation"?Date.parse(a.createdAt):u.ts,payload:de(a.text,a.attachments)})):null,A=ee??[u],N=Ce(o,u.id),ne=N.state==="queued";if(N.state!=="not_found"&&!ne){e.client.sendRes({type:"res",id:c,ok:!0,payload:N});return}if(X){(t?.source==="room_activation"||t?.source==="manager_event")&&ge(r.adapter,o,l,$,z,w,t.managedAgentPolicy,e.managedRoomExecutor);try{await ze(r.adapter,f.text,S,D,f.attachments,H,t?.source==="room_activation"?{source:"room_activation"}:t?.source==="manager_event"?{source:"managed_private"}:void 0)}catch(a){e.processedReqIds.delete(n.id),e.sessions.delete(o),await C(e,r),e.client.sendRes({type:"res",id:c,ok:!1,error:a instanceof Error?a.message:String(a)});return}}b({level:"info",sessionId:o,wsEvent:"chat.send.start",metadata:{reqId:n.id,agentType:l,sessionMode:k,managerConfig:Z??null,modelId:S,managerDefaultWorkerAgentType:q??null,managerDefaultWorkerModelId:j??null,reasoningEffort:D,...t?.source==="room_activation"?{managedRoomPromptSnapshot:t.managedRoomPromptSnapshot}:{}}});const M=(a,d)=>({...Pe(ue()),sessionId:o,clientMessageId:u.id,messageId:u.id,deliveryState:a,bodyRevision:d,committedAt:new Date(u.ts).toISOString()}),V=a=>{e.nativeFusion?.registerManagedSend({sessionId:o,agentType:l,sourceAgentType:Oe(l,S),canonicalMessageId:u.id,sourceSessionKey:r.agentSessionId??m??null,text:f.text,managedEchoPolicy:a?.deliveryMode==="external_owner_queue"&&!_e?"import_following":"suppress_following",externalRunId:a?.deliveryMode==="external_owner_queue"?`codex-external-${a.queuedSubmissionId??u.id}`:null})},ae=()=>{r.currentRunId||(r.currentRunId=`dispatch:${n.id}`,r.currentRunPhase="thinking",r.nextEventSeq=0,r.lastProviderActivityAt=Date.now())},te=()=>{if(!e.runJournal||r.journalRunId)return;const a=`run:${Me()}`;e.runJournal.begin({daemonInstallationId:ue(),sessionId:o,runId:a,clientMessageId:u.id,agentType:l,sessionMode:k,workDir:v,agentSessionId:r.agentSessionId??m??null,modelId:S??null,reasoningEffort:D??null,nativeSessionVisibility:U,providerRunId:null,source:E==="manager_event"?"manager_event":t?.source==="room_activation"?E==="managed_direct"?"managed_direct":"managed_room":g?"manager_worker":"personal",parentSessionId:g??null,managerWorkerNativeFull:J===!0,roomContextRegistryKey:typeof n.params.roomContextRegistryKey=="string"?n.params.roomContextRegistryKey:null,activationCauseMessageId:typeof n.params.activationCauseMessageId=="string"?n.params.activationCauseMessageId:null,rootCauseMessageId:typeof n.params.rootCauseMessageId=="string"?n.params.rootCauseMessageId:null,activationSequence:typeof n.params.activationSequence=="string"?n.params.activationSequence:null,activationHopCount:typeof n.params.activationHopCount=="number"?n.params.activationHopCount:null,managedPolicyVersion:t?.source==="room_activation"?t.managedRoomPromptSnapshot.managedRoomPolicyVersion:t?.source==="manager_event"?t.managedRoomPolicyVersion:null,managedRuntimePolicyHash:(t?.source==="room_activation"?t.managedRoomPromptSnapshot.runtimePermissionPolicyHash:t?.source==="manager_event"?t.managedRuntimePolicyHash:null)??(y?Ae("sha256").update(JSON.stringify({requestedPolicy:y.requestedPolicy,enforcement:y.enforcement})).digest("hex"):null)}),r.journalRunId=a},oe=()=>{if(V(),ne){let a=0;for(const[d,i]of A.entries()){const h=_(o,i,"queued");d===0&&(a=h)}return M("queued",a)}try{je(e,{sessionId:o,agentType:l,sessionMode:k,managerConfig:Z??null,workDir:Se,agentSessionId:r.agentSessionId??m??null,modelId:S,managerDefaultWorkerAgentType:q??null,managerDefaultWorkerModelId:j??null});let a=0;for(const[d,i]of A.entries()){const h=ee?_(o,i,"committed"):ce(o,i,"committed");d===0&&(a=h)}return be(v,x??[]),R(e.client),M("committed",a)}catch(a){throw e.nativeFusion?.cancelManagedSend?.(u.id),a}},re=a=>{r.journalRunId&&e.runJournal?.update(o,r.journalRunId,{deliveryMode:a?.deliveryMode==="external_owner_queue"?"external_owner_queue":"direct",externalRunId:a?.deliveryMode==="external_owner_queue"?`codex-external-${a.queuedSubmissionId??u.id}`:null})},se=a=>{r.currentRunId||(r.nextEventSeq=0),re(a),V(a);const d=a?.deliveryMode==="external_owner_queue"?"queued":"executing";let i=0;for(const[h,T]of A.entries()){const P=_(o,T,d);h===0&&(i=P)}return R(e.client),M(d,i)},K=async(a,d)=>{e.nativeFusion?.cancelManagedSend?.(u.id);const i=ye(l,a);if(console.error(`[chat.send] send failed reqId=${n.id} sessionId=${o} agentType=${I} workDir=${v} agentSessionId=${r.agentSessionId??m??""}: ${i}`),r.adapter.hasPendingAuthenticationRetry?.()===!0){d&&e.client.sendRes({type:"res",id:c,ok:!0,payload:M("queued",0)});return}e.sessions.delete(o),r.journalRunId&&(e.runJournal?.update(o,r.journalRunId,{phase:"terminal"}),e.runJournal?.remove(o,r.journalRunId),e.recoveryBarrier?.end(o),r.journalRunId=null);try{await C(e,r)}catch{}if(e.client.sendEvent({type:"event",event:"agent",payload:{state:"error",sessionId:o,message:i,runId:"",seq:0}}),!d){const T={id:`agent-error-${n.id}-${Date.now()}`,sessionId:o,role:"agent",ts:Date.now(),payload:i};ce(o,T),R(e.client)}d&&(e.processedReqIds.delete(n.id),e.client.sendRes({type:"res",id:c,ok:!1,error:i}))};if(X){try{oe()}catch(i){e.processedReqIds.delete(n.id),e.client.sendRes({type:"res",id:c,ok:!1,error:`Durable message commit failed: ${i instanceof Error?i.message:String(i)}`});return}te(),ae();let a;try{(t?.source==="room_activation"||t?.source==="manager_event")&&(pe(e,r,w),s());const i=f.attachments;g&&e.managerRuntime?.prepareWorkerTurnOrigin(o,B),a=await fe(r.adapter,f.text,S,D,i,H,t?.source==="room_activation"?{source:"room_activation"}:t?.source==="manager_event"?{source:"managed_private"}:void 0),b({level:"info",sessionId:o,wsEvent:"chat.send.done",metadata:{reqId:n.id}})}catch(i){if(g&&e.managerRuntime?.prepareWorkerTurnOrigin(o),r.adapter.hasPendingAuthenticationRetry?.()){let P=0;for(const[L,Q]of A.entries()){const ke=_(o,Q,"queued");L===0&&(P=ke)}R(e.client),await K(i,!1),e.client.sendRes({type:"res",id:c,ok:!0,payload:M("queued",P)});return}let h=0;for(const[P,L]of A.entries()){const Q=_(o,L,"failed");P===0&&(h=Q)}R(e.client);const T=ye(l,i);await K(i,!1),e.client.sendRes({type:"res",id:c,ok:!0,payload:{...M("failed",h),failureMessage:T}});return}const d=se(a??void 0);e.client.sendRes({type:"res",id:c,ok:!0,payload:{...d,...f.localized?{localizedAttachments:!0}:{},...a?.deliveryMode==="external_owner_queue"?{queued:!0,deliveryMode:a.deliveryMode,queuedSubmissionId:a.queuedSubmissionId}:{}}}),b({level:"info",sessionId:o,wsEvent:"chat.send.res",metadata:{reqId:n.id,ok:!0}});return}let ie;try{ie=oe()}catch(a){e.processedReqIds.delete(n.id),e.client.sendRes({type:"res",id:c,ok:!1,error:`Durable message commit failed: ${a instanceof Error?a.message:String(a)}`});return}te(),ae(),(t?.source==="room_activation"||t?.source==="manager_event")&&(ge(r.adapter,o,l,$,z,w,t.managedAgentPolicy,e.managedRoomExecutor),pe(e,r,w),s()),se(),e.client.sendRes({type:"res",id:c,ok:!0,payload:{...ie,...f.localized?{localizedAttachments:!0}:{}}}),b({level:"info",sessionId:o,wsEvent:"chat.send.res",metadata:{reqId:n.id,ok:!0}});const we=f.attachments;g&&e.managerRuntime?.prepareWorkerTurnOrigin(o,B),fe(r.adapter,f.text,S,D,we,H,t?.source==="room_activation"?{source:"room_activation"}:t?.source==="manager_event"?{source:"managed_private"}:void 0).then(a=>{if(a?.deliveryMode==="external_owner_queue"){re(a),V(a);for(const d of A)_(o,d,"queued");R(e.client)}b({level:"info",sessionId:o,wsEvent:"chat.send.done",metadata:{reqId:n.id}})}).catch(a=>{g&&e.managerRuntime?.prepareWorkerTurnOrigin(o),_(o,u,r.adapter.hasPendingAuthenticationRetry?.()?"queued":"failed"),R(e.client),K(a,!1)})}const Ve=2e4;async function Ke(e,n){let t;const s=new Promise(o=>{t=setTimeout(()=>o("timed_out"),n)});try{return await Promise.race([e.adapter.stop().then(()=>"stopped"),s])}finally{clearTimeout(t)}}async function mn(e,n,t=Ve){const{sessionId:s,discardPending:o}=n.params,p=e.sessions.get(s);if(p){p.journalRunId&&e.runJournal?.markAborted(s,p.journalRunId),me(e,p);let I;try{I=await Ke(p,t)}catch{e.client.sendRes({type:"res",id:n.id,ok:!1,error:"agent_stop_failed"});return}const W=p.currentRunId;He(e,s),I==="timed_out"&&(e.sessions.delete(s),p.adapter.removeAllListeners(),p.adapter.stop().catch(()=>{}),console.error(`[chat.abort] agent did not stop within ${t}ms; ended the run by force sessionId=${s}`),_(s,{id:`stop-forced-${W??n.id}`,sessionId:s,role:"agent",ts:Date.now(),payload:"AI \u6CA1\u6709\u54CD\u5E94\u201C\u505C\u6B62\u201D\u8BF7\u6C42\uFF0C\u795E\u5FF5\u5DF2\u5F3A\u5236\u7ED3\u675F\u8FD9\u4E00\u8F6E\u3002\u53EF\u4EE5\u76F4\u63A5\u53D1\u9001\u65B0\u6D88\u606F\u7EE7\u7EED\uFF1B\u5982\u679C\u53CD\u590D\u51FA\u73B0\uFF0C\u8BF7\u68C0\u67E5\u8FD9\u53F0\u7535\u8111\u4E0A\u8BE5 AI \u7684\u8FD0\u884C\u72B6\u6001\u3002"}),R(e.client)),e.activityPublisher?.publish(s,null)}o===!0&&e.chatQueue?.purgeSession(s),e.client.sendRes({type:"res",id:n.id,ok:!0})}export{Ve as STOP_TIMEOUT_MS,mn as handleChatAbort,pn as handleChatSend,vn as sendSessionUpdateEvent,hn as validateManagedRoomExecutionContext};