@chainingintention/pi-web-cn 1.202610.0 → 1.202610.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 (333) hide show
  1. package/README.md +60 -106
  2. package/dist/.plugins-ready +1 -0
  3. package/dist/cli.js +161 -45
  4. package/dist/cli.js.map +1 -1
  5. package/dist/client/assets/index-CFhaGb5m.js +4357 -0
  6. package/dist/client/assets/vendor-editor-core-YWog6mAR.js +10 -0
  7. package/dist/client/assets/vendor-editor-languages-DOCw3vc9.js +28 -0
  8. package/dist/client/index.html +5 -5
  9. package/dist/config.js +30 -6
  10. package/dist/config.js.map +1 -1
  11. package/dist/docker/piWebDockerCommandPlan.js +13 -4
  12. package/dist/docker/piWebDockerCommandPlan.js.map +1 -1
  13. package/dist/nativeServices/installedServiceDefinitions.js +23 -10
  14. package/dist/nativeServices/installedServiceDefinitions.js.map +1 -1
  15. package/dist/nativeServices/serviceAction.js +78 -20
  16. package/dist/nativeServices/serviceAction.js.map +1 -1
  17. package/dist/nativeServices/serviceDoctor.js +339 -67
  18. package/dist/nativeServices/serviceDoctor.js.map +1 -1
  19. package/dist/nativeServices/servicePlan.js +2 -2
  20. package/dist/nativeServices/servicePlan.js.map +1 -1
  21. package/dist/pi-packages/captains-log/README.md +11 -0
  22. package/dist/pi-packages/captains-log/dist/browser/channelProtocol.js +29 -0
  23. package/dist/pi-packages/captains-log/dist/browser/index.js +146 -0
  24. package/dist/pi-packages/captains-log/dist/browser/markdown.js +67 -0
  25. package/dist/pi-packages/captains-log/dist/browser/panel.js +151 -0
  26. package/dist/pi-packages/captains-log/dist/browser/protocol.js +26 -0
  27. package/dist/pi-packages/captains-log/dist/companion.js +126 -0
  28. package/dist/pi-packages/captains-log/dist/roundtrip.js +62 -0
  29. package/dist/pi-packages/captains-log/dist/server.js +210 -0
  30. package/dist/pi-packages/captains-log/dist/store.js +75 -0
  31. package/dist/pi-packages/captains-log/docs/usage.md +29 -0
  32. package/dist/pi-packages/captains-log/package.json +35 -0
  33. package/dist/pi-packages/captains-log/tsconfig.json +21 -0
  34. package/dist/pi-packages/captains-log/vite.config.mjs +28 -0
  35. package/dist/pi-packages/relays/pi-web-plugin.js +9 -9
  36. package/dist/pi-packages/relays/prompts/relay.md +1 -1
  37. package/dist/pi-packages/relays/relaysPanelElement.js +8 -8
  38. package/dist/pi-packages/relays/skills/relay-runner/SKILL.md +21 -13
  39. package/dist/pi-web-plugins/files/browser/assets/files-icon-DZObYhxb.svg +3 -0
  40. package/dist/pi-web-plugins/files/browser/pi-web-plugin.js +456 -0
  41. package/dist/pi-web-plugins/files/package.json +15 -0
  42. package/dist/pi-web-plugins/git/browser/git-panel.js +55 -32
  43. package/dist/pi-web-plugins/git/browser/pi-web-plugin.js +2 -2
  44. package/dist/pi-web-plugins/git/git-backend.js +19 -26
  45. package/dist/pi-web-plugins/git/gitStatusCache.js +48 -0
  46. package/dist/pi-web-plugins/git/package.json +1 -0
  47. package/dist/pi-web-plugins/git/server-plugin.js +11 -10
  48. package/dist/pi-web-plugins/info/infoInternals.js +4 -1
  49. package/dist/pi-web-plugins/info/package.json +1 -1
  50. package/dist/pi-web-plugins/info/pi-web-plugin.js +8 -8
  51. package/dist/pi-web-plugins/mermaid/browser/mermaid-engine.js +3590 -0
  52. package/dist/pi-web-plugins/mermaid/browser/pi-web-plugin.js +6 -0
  53. package/dist/pi-web-plugins/mermaid/package.json +13 -0
  54. package/dist/pi-web-plugins/terminal/browser/pi-web-plugin.js +210 -0
  55. package/dist/pi-web-plugins/terminal/package.json +16 -0
  56. package/dist/pi-web-plugins/terminal/server-plugin.js +365 -0
  57. package/dist/pi-web-plugins/terminal/terminalService.js +435 -0
  58. package/dist/pi-web-plugins/updates/package.json +1 -1
  59. package/dist/pi-web-plugins/updates/pi-web-plugin.js +69 -68
  60. package/dist/pi-web-plugins/workspace-tasks/package.json +1 -1
  61. package/dist/pi-web-plugins/workspace-tasks/pi-web-plugin.js +3 -3
  62. package/dist/piWebVersionReport.js +47 -24
  63. package/dist/piWebVersionReport.js.map +1 -1
  64. package/dist/plugin-api.d.ts +260 -25
  65. package/dist/plugin-api.js +1 -0
  66. package/dist/pluginRecoveryCli.js +3 -2
  67. package/dist/pluginRecoveryCli.js.map +1 -1
  68. package/dist/server/activity/workspaceActivityService.js +5 -5
  69. package/dist/server/activity/workspaceActivityService.js.map +1 -1
  70. package/dist/server/app.js +71 -44
  71. package/dist/server/app.js.map +1 -1
  72. package/dist/server/audit/managementAuditStore.js +1 -1
  73. package/dist/server/browserMessageProjection.js +0 -2
  74. package/dist/server/browserMessageProjection.js.map +1 -1
  75. package/dist/server/configRoutes.js +1 -2
  76. package/dist/server/configRoutes.js.map +1 -1
  77. package/dist/server/knownPiPackages.js +12 -0
  78. package/dist/server/knownPiPackages.js.map +1 -0
  79. package/dist/server/machines/machineClient.js +2 -2
  80. package/dist/server/machines/machineClient.js.map +1 -1
  81. package/dist/server/machines/machinePluginProxyRoutes.js +120 -21
  82. package/dist/server/machines/machinePluginProxyRoutes.js.map +1 -1
  83. package/dist/server/machines/machineProxyRoutes.js +80 -22
  84. package/dist/server/machines/machineProxyRoutes.js.map +1 -1
  85. package/dist/server/machines/machineService.js +22 -0
  86. package/dist/server/machines/machineService.js.map +1 -1
  87. package/dist/server/managementEmbed.js +2 -2
  88. package/dist/server/managementEmbed.js.map +1 -1
  89. package/dist/server/managementPrivileges.js +7 -1
  90. package/dist/server/managementPrivileges.js.map +1 -1
  91. package/dist/server/managementTerminalProxyRoutes.js +124 -0
  92. package/dist/server/managementTerminalProxyRoutes.js.map +1 -0
  93. package/dist/server/normalAuth.js +5 -1
  94. package/dist/server/normalAuth.js.map +1 -1
  95. package/dist/server/notices/serverNoticeStore.js +50 -6
  96. package/dist/server/notices/serverNoticeStore.js.map +1 -1
  97. package/dist/server/piPackageService.js +5 -5
  98. package/dist/server/piPackageService.js.map +1 -1
  99. package/dist/server/piWebPluginCatalog.js +211 -34
  100. package/dist/server/piWebPluginCatalog.js.map +1 -1
  101. package/dist/server/piWebPluginLifecycle.js +29 -3
  102. package/dist/server/piWebPluginLifecycle.js.map +1 -1
  103. package/dist/server/piWebPluginService.js +176 -291
  104. package/dist/server/piWebPluginService.js.map +1 -1
  105. package/dist/server/piWebStatus.js +45 -55
  106. package/dist/server/piWebStatus.js.map +1 -1
  107. package/dist/server/pluginCallbackDrain.js +35 -0
  108. package/dist/server/pluginCallbackDrain.js.map +1 -0
  109. package/dist/server/plugins/pluginBackendChannelProxyAdmission.js +84 -0
  110. package/dist/server/plugins/pluginBackendChannelProxyAdmission.js.map +1 -0
  111. package/dist/server/plugins/pluginBackendChannelProxyCoordinator.js +234 -0
  112. package/dist/server/plugins/pluginBackendChannelProxyCoordinator.js.map +1 -0
  113. package/dist/server/plugins/pluginBackendChannelProxyRoutes.js +63 -0
  114. package/dist/server/plugins/pluginBackendChannelProxyRoutes.js.map +1 -0
  115. package/dist/server/plugins/pluginBackendProxyRoutes.js +7 -4
  116. package/dist/server/plugins/pluginBackendProxyRoutes.js.map +1 -1
  117. package/dist/server/plugins/pluginBackendRegistry.js +773 -0
  118. package/dist/server/plugins/pluginBackendRegistry.js.map +1 -0
  119. package/dist/server/plugins/serverPluginPiSessionEventsCapability.js +34 -0
  120. package/dist/server/plugins/serverPluginPiSessionEventsCapability.js.map +1 -0
  121. package/dist/server/plugins/serverPluginPiSessionsCapability.js +185 -0
  122. package/dist/server/plugins/serverPluginPiSessionsCapability.js.map +1 -0
  123. package/dist/server/plugins/serverPluginRuntime.js +1049 -107
  124. package/dist/server/plugins/serverPluginRuntime.js.map +1 -1
  125. package/dist/server/plugins/serverPluginWorkspacesCapability.js +114 -0
  126. package/dist/server/plugins/serverPluginWorkspacesCapability.js.map +1 -0
  127. package/dist/server/projectTrustRoutes.js +86 -0
  128. package/dist/server/projectTrustRoutes.js.map +1 -0
  129. package/dist/server/projects/projectLifecycleService.js +84 -0
  130. package/dist/server/projects/projectLifecycleService.js.map +1 -0
  131. package/dist/server/projects/projectService.js +2 -0
  132. package/dist/server/projects/projectService.js.map +1 -1
  133. package/dist/server/realtime/sessionEventHub.js +35 -41
  134. package/dist/server/realtime/sessionEventHub.js.map +1 -1
  135. package/dist/server/sessiond/agentHttpDispatcher.js +108 -0
  136. package/dist/server/sessiond/agentHttpDispatcher.js.map +1 -0
  137. package/dist/server/sessiond/pluginBackendChannelRoutes.js +397 -0
  138. package/dist/server/sessiond/pluginBackendChannelRoutes.js.map +1 -0
  139. package/dist/server/sessiond/pluginBackendRoutes.js +19 -9
  140. package/dist/server/sessiond/pluginBackendRoutes.js.map +1 -1
  141. package/dist/server/sessiond/projectMutationRoutes.js +38 -0
  142. package/dist/server/sessiond/projectMutationRoutes.js.map +1 -0
  143. package/dist/server/sessiond/sessionDaemonShutdown.js +36 -0
  144. package/dist/server/sessiond/sessionDaemonShutdown.js.map +1 -0
  145. package/dist/server/sessiond/sessionDaemonStartup.js +30 -4
  146. package/dist/server/sessiond/sessionDaemonStartup.js.map +1 -1
  147. package/dist/server/sessiond/sessionProxyRoutes.js +47 -15
  148. package/dist/server/sessiond/sessionProxyRoutes.js.map +1 -1
  149. package/dist/server/sessiond/sessionServiceDependencies.js +4 -0
  150. package/dist/server/sessiond/sessionServiceDependencies.js.map +1 -1
  151. package/dist/server/sessiond/sessiondStateOwnership.js +174 -39
  152. package/dist/server/sessiond/sessiondStateOwnership.js.map +1 -1
  153. package/dist/server/sessiond/workspaceRemovalRoutes.js +1 -1
  154. package/dist/server/sessiond/workspaceRemovalRoutes.js.map +1 -1
  155. package/dist/server/sessiond.js +226 -56
  156. package/dist/server/sessiond.js.map +1 -1
  157. package/dist/server/sessions/askUserTool.js +0 -3
  158. package/dist/server/sessions/askUserTool.js.map +1 -1
  159. package/dist/server/sessions/attachmentService.js +0 -1
  160. package/dist/server/sessions/attachmentService.js.map +1 -1
  161. package/dist/server/sessions/authService.js +8 -1
  162. package/dist/server/sessions/authService.js.map +1 -1
  163. package/dist/server/sessions/builtinCommands.js +2 -2
  164. package/dist/server/sessions/builtinCommands.js.map +1 -1
  165. package/dist/server/sessions/clientSessionPreview.js +14 -0
  166. package/dist/server/sessions/clientSessionPreview.js.map +1 -0
  167. package/dist/server/sessions/dockerEnvironmentFacts.js +82 -9
  168. package/dist/server/sessions/dockerEnvironmentFacts.js.map +1 -1
  169. package/dist/server/sessions/managementAgentTools.js +11 -6
  170. package/dist/server/sessions/managementAgentTools.js.map +1 -1
  171. package/dist/server/sessions/managementPermissionSystem.js +16 -4
  172. package/dist/server/sessions/managementPermissionSystem.js.map +1 -1
  173. package/dist/server/sessions/messagePaging.js +2 -2
  174. package/dist/server/sessions/messagePaging.js.map +1 -1
  175. package/dist/server/sessions/modelCatalogRefresher.js +4 -3
  176. package/dist/server/sessions/modelCatalogRefresher.js.map +1 -1
  177. package/dist/server/sessions/oauthLoginFlowService.js +2 -5
  178. package/dist/server/sessions/oauthLoginFlowService.js.map +1 -1
  179. package/dist/server/sessions/pendingAskStore.js +0 -3
  180. package/dist/server/sessions/pendingAskStore.js.map +1 -1
  181. package/dist/server/sessions/piSessionEventConnections.js +65 -0
  182. package/dist/server/sessions/piSessionEventConnections.js.map +1 -0
  183. package/dist/server/sessions/piSessionManagerGateway.js +56 -3
  184. package/dist/server/sessions/piSessionManagerGateway.js.map +1 -1
  185. package/dist/server/sessions/piSessionService.js +785 -450
  186. package/dist/server/sessions/piSessionService.js.map +1 -1
  187. package/dist/server/sessions/projectAuthService.js +29 -2
  188. package/dist/server/sessions/projectAuthService.js.map +1 -1
  189. package/dist/server/sessions/projectSettingsScope.js +12 -4
  190. package/dist/server/sessions/projectSettingsScope.js.map +1 -1
  191. package/dist/server/sessions/projectTrust.js +119 -0
  192. package/dist/server/sessions/projectTrust.js.map +1 -0
  193. package/dist/server/sessions/sessionActivityMarker.js +105 -0
  194. package/dist/server/sessions/sessionActivityMarker.js.map +1 -0
  195. package/dist/server/sessions/sessionArchiveMapping.js +2 -8
  196. package/dist/server/sessions/sessionArchiveMapping.js.map +1 -1
  197. package/dist/server/sessions/sessionCommandService.js +12 -10
  198. package/dist/server/sessions/sessionCommandService.js.map +1 -1
  199. package/dist/server/sessions/sessionEnvironmentFacts.js +5 -4
  200. package/dist/server/sessions/sessionEnvironmentFacts.js.map +1 -1
  201. package/dist/server/sessions/sessionModelScope.js +42 -5
  202. package/dist/server/sessions/sessionModelScope.js.map +1 -1
  203. package/dist/server/sessions/sessionNameGenerator.js +6 -6
  204. package/dist/server/sessions/sessionNameGenerator.js.map +1 -1
  205. package/dist/server/sessions/sessionRoutes.js +63 -15
  206. package/dist/server/sessions/sessionRoutes.js.map +1 -1
  207. package/dist/server/sessions/sessionTreeProjection.js +29 -29
  208. package/dist/server/sessions/sessionTreeProjection.js.map +1 -1
  209. package/dist/server/sessions/sessionUiEvents.js +6 -7
  210. package/dist/server/sessions/sessionUiEvents.js.map +1 -1
  211. package/dist/server/sessions/sessionUnreadStore.js +26 -1
  212. package/dist/server/sessions/sessionUnreadStore.js.map +1 -1
  213. package/dist/server/sessions/spawnSessionTool.js +3 -3
  214. package/dist/server/sessions/spawnSessionTool.js.map +1 -1
  215. package/dist/server/sessions/spawnSubsessionTool.js +5 -5
  216. package/dist/server/sessions/spawnSubsessionTool.js.map +1 -1
  217. package/dist/server/sessions/spawnTargetResolver.js +1 -1
  218. package/dist/server/sessions/transcriptMessages.js +54 -0
  219. package/dist/server/sessions/transcriptMessages.js.map +1 -0
  220. package/dist/server/status/machineStatusService.js +3 -1
  221. package/dist/server/status/machineStatusService.js.map +1 -1
  222. package/dist/server/terminals/{terminalService.js → managementCommandRunService.js} +79 -107
  223. package/dist/server/terminals/managementCommandRunService.js.map +1 -0
  224. package/dist/server/terminals/managementTerminalRoutes.js +91 -0
  225. package/dist/server/terminals/managementTerminalRoutes.js.map +1 -0
  226. package/dist/server/terminals/requiredTerminalService.js +103 -0
  227. package/dist/server/terminals/requiredTerminalService.js.map +1 -0
  228. package/dist/server/webSocketBridge.js +339 -0
  229. package/dist/server/webSocketBridge.js.map +1 -1
  230. package/dist/server/workbench/gatewayIntegration.js +4 -1
  231. package/dist/server/workbench/gatewayIntegration.js.map +1 -1
  232. package/dist/server/workspaceExplorerRoutes.js +13 -12
  233. package/dist/server/workspaceExplorerRoutes.js.map +1 -1
  234. package/dist/server/workspaces/fileContentService.js +9 -7
  235. package/dist/server/workspaces/fileContentService.js.map +1 -1
  236. package/dist/server/workspaces/fileOperationService.js +4 -3
  237. package/dist/server/workspaces/fileOperationService.js.map +1 -1
  238. package/dist/server/workspaces/projectWorkspaceCwds.js +0 -5
  239. package/dist/server/workspaces/projectWorkspaceCwds.js.map +1 -1
  240. package/dist/server/workspaces/sessionDaemonWorkspaceCatalog.js +36 -12
  241. package/dist/server/workspaces/sessionDaemonWorkspaceCatalog.js.map +1 -1
  242. package/dist/server/workspaces/workspaceCatalog.js +6 -4
  243. package/dist/server/workspaces/workspaceCatalog.js.map +1 -1
  244. package/dist/server/workspaces/workspaceDeletionRoutes.js +5 -57
  245. package/dist/server/workspaces/workspaceDeletionRoutes.js.map +1 -1
  246. package/dist/server/workspaces/workspaceProviderRegistry.js +75 -24
  247. package/dist/server/workspaces/workspaceProviderRegistry.js.map +1 -1
  248. package/dist/server/workspaces/workspaceRemovalService.js +25 -3
  249. package/dist/server/workspaces/workspaceRemovalService.js.map +1 -1
  250. package/dist/server-plugin-api.d.ts +274 -44
  251. package/dist/server-plugin-api.js +319 -2
  252. package/dist/server-plugin-api.js.map +1 -1
  253. package/dist/serverPluginRecovery.js +4 -0
  254. package/dist/serverPluginRecovery.js.map +1 -1
  255. package/dist/sessiond/activeAgentProfile.js +27 -2
  256. package/dist/sessiond/activeAgentProfile.js.map +1 -1
  257. package/dist/sessiond/config.js +4 -3
  258. package/dist/sessiond/config.js.map +1 -1
  259. package/dist/sessiond/sessionDaemonClient.js +29 -8
  260. package/dist/sessiond/sessionDaemonClient.js.map +1 -1
  261. package/dist/shared/activeAgentProfile.js +2 -34
  262. package/dist/shared/activeAgentProfile.js.map +1 -1
  263. package/dist/shared/activity.js +0 -3
  264. package/dist/shared/activity.js.map +1 -1
  265. package/dist/shared/apiTypes.js +7 -2
  266. package/dist/shared/apiTypes.js.map +1 -1
  267. package/dist/shared/capabilities.js +5 -0
  268. package/dist/shared/capabilities.js.map +1 -1
  269. package/dist/shared/federatedRoutes.js +49 -9
  270. package/dist/shared/federatedRoutes.js.map +1 -1
  271. package/dist/shared/machinePluginIds.js +30 -7
  272. package/dist/shared/machinePluginIds.js.map +1 -1
  273. package/dist/shared/machineStatus.js +35 -1
  274. package/dist/shared/machineStatus.js.map +1 -1
  275. package/dist/shared/piWebStatusParsing.js +29 -0
  276. package/dist/shared/piWebStatusParsing.js.map +1 -1
  277. package/dist/shared/pluginApiTypes.d.ts +24 -9
  278. package/dist/shared/pluginApiTypes.js +2 -0
  279. package/dist/shared/pluginApiTypes.js.map +1 -0
  280. package/dist/shared/pluginBackendProtocol.js +163 -2
  281. package/dist/shared/pluginBackendProtocol.js.map +1 -1
  282. package/dist/shared/pluginIds.js +10 -1
  283. package/dist/shared/pluginIds.js.map +1 -1
  284. package/dist/shared/requiredTerminalPlugin.js +6 -0
  285. package/dist/shared/requiredTerminalPlugin.js.map +1 -0
  286. package/dist/shared/serverNoticeContract.js +88 -0
  287. package/dist/shared/serverNoticeContract.js.map +1 -0
  288. package/dist/shared/sessionDefaults.js +41 -0
  289. package/dist/shared/sessionDefaults.js.map +1 -0
  290. package/dist/shared/workspaceFiles.js.map +1 -1
  291. package/docs/config.md +270 -323
  292. package/docs/plugins.md +171 -956
  293. package/examples/session-bridge-plugin/README.md +20 -0
  294. package/examples/session-bridge-plugin/docs/usage.md +54 -0
  295. package/examples/session-bridge-plugin/package.json +26 -0
  296. package/examples/session-bridge-plugin/src/browser/index.ts +87 -0
  297. package/examples/session-bridge-plugin/src/browser/protocol.ts +24 -0
  298. package/examples/session-bridge-plugin/src/companion.ts +65 -0
  299. package/examples/session-bridge-plugin/src/reviewRun.ts +39 -0
  300. package/examples/session-bridge-plugin/src/server.ts +85 -0
  301. package/examples/session-bridge-plugin/src/store.ts +80 -0
  302. package/examples/session-bridge-plugin/tsconfig.json +21 -0
  303. package/examples/workspace-provider-plugin/README.md +39 -0
  304. package/examples/workspace-provider-plugin/package.json +25 -0
  305. package/examples/workspace-provider-plugin/src/browser/index.ts +69 -0
  306. package/examples/workspace-provider-plugin/src/server.ts +68 -0
  307. package/examples/workspace-provider-plugin/tsconfig.json +21 -0
  308. package/package.json +22 -13
  309. package/skills/README.md +27 -2
  310. package/skills/relay-runner/SKILL.md +21 -13
  311. package/dist/client/assets/CodeViewer-CZHnKRwQ.js +0 -4
  312. package/dist/client/assets/TerminalPanel-B_EaRPHY.js +0 -187
  313. package/dist/client/assets/index-Bv1v-Jz7.js +0 -4717
  314. package/dist/client/assets/vendor-editor-core-vUi74DnD.js +0 -12
  315. package/dist/client/assets/vendor-editor-languages-DoaXUodB.js +0 -46
  316. package/dist/client/assets/vendor-editor-legacy-CYBnW6ZU.js +0 -1
  317. package/dist/client/assets/vendor-terminal-BrP-ENHg.css +0 -1
  318. package/dist/client/assets/vendor-terminal-D8k4UKM2.js +0 -35
  319. package/dist/server/git/gitService.js +0 -316
  320. package/dist/server/git/gitService.js.map +0 -1
  321. package/dist/server/gitRoutes.js +0 -23
  322. package/dist/server/gitRoutes.js.map +0 -1
  323. package/dist/server/terminalProxyRoutes.js +0 -181
  324. package/dist/server/terminalProxyRoutes.js.map +0 -1
  325. package/dist/server/terminals/terminalRoutes.js +0 -164
  326. package/dist/server/terminals/terminalRoutes.js.map +0 -1
  327. package/dist/server/terminals/terminalService.js.map +0 -1
  328. package/dist/server/terminals/terminalSize.js +0 -17
  329. package/dist/server/terminals/terminalSize.js.map +0 -1
  330. package/dist/server/workspaces/gitWorktreeDiscovery.js +0 -43
  331. package/dist/server/workspaces/gitWorktreeDiscovery.js.map +0 -1
  332. package/dist/server/workspaces/workspaceService.js +0 -52
  333. package/dist/server/workspaces/workspaceService.js.map +0 -1
@@ -0,0 +1,151 @@
1
+ // Generated from pi-packages/captains-log/src/browser/panel.ts. Do not edit directly.
2
+ import { renderCaptainMarkdown } from "./markdown.js";
3
+ export function selectedSourceSession(context) {
4
+ if (context.state?.selectedMachine && context.state.selectedMachine.id !== context.machine.id)
5
+ return;
6
+ const selected = context.state?.selectedSession;
7
+ if (!selected || selected.archived || selected.pending || selected.cwd !== context.workspace.path)
8
+ return;
9
+ return { id: selected.id, name: selected.name !== undefined && selected.name.trim() !== "" ? selected.name : `会话 ${selected.id.slice(0, 8)}` };
10
+ }
11
+ export function captainStatusLabel(status) {
12
+ return { running: "处理中", completed: "已完成", failed: "失败", interrupted: "已中断" }[status];
13
+ }
14
+ const stageLabels = {
15
+ "Backend admitted translation": "后端已接收转述请求",
16
+ "Backend sent request to native companion": "后端已向原生伴随扩展发送请求",
17
+ "Native companion received request": "原生伴随扩展已收到请求",
18
+ "Native Pi agent running": "原生 Pi 代理正在运行",
19
+ "Backend received correlated final reply": "后端已收到匹配请求的最终回复",
20
+ "Source reply read without changing source": "已读取源回复,未改动源会话",
21
+ "Pirate conversation selected": "已选择船长会话",
22
+ "Backend recorded failure (not success)": "后端已记录失败,不是成功结果",
23
+ "Backend saved completed roundtrip": "后端已保存完成结果",
24
+ };
25
+ const errorLabels = {
26
+ "Captain is already working.": "船长正在处理,请等待当前转述完成。",
27
+ "Select a source session other than the pirate conversation.": "请选择其他源会话,不能转述船长会话本身。",
28
+ "Selected session is not hosted in this workspace on this machine": "所选会话不在当前机器的这个工作区中运行。",
29
+ "Source session must be idle with no pending messages. Wait for it to finish, then translate again.": "源会话必须空闲且没有待处理消息。请等待它完成后再转述。",
30
+ "Source session has no completed assistant text reply on its active branch.": "源会话当前分支没有已完成的助手文本回复。",
31
+ "Source reply exceeds the translation limit (48000 characters).": "源回复超过转述长度上限(48000 个字符)。",
32
+ "Companion not ready or session busy; open Captain's Log in Sessions, wait for idle, then retry": "伴随扩展尚未就绪或会话忙碌;请在会话列表中打开船长会话,等待空闲后重试。",
33
+ "Captain request interrupted by session shutdown or reload": "会话关闭或重新加载中断了船长请求。",
34
+ "Another prompt interfered with this Captain request; translation was not saved": "其他提示干扰了本次船长请求,转述结果未保存。",
35
+ "Another user message interfered with this Captain request": "其他用户消息干扰了本次船长请求。",
36
+ "Captain request error": "船长请求出错。",
37
+ "Captain request aborted": "船长请求已中止。",
38
+ "Captain request failed": "船长请求失败。",
39
+ "Captain request ended without a successful final text response": "船长请求结束时没有成功的最终文本回复。",
40
+ "Captain reply exceeded the storage limit": "船长回复超过存储长度上限。",
41
+ "Invalid translation text": "转述文本无效。",
42
+ "Invalid companion reply": "伴随扩展回复无效。",
43
+ "Captain connection interrupted; agent work may continue in Sessions": "船长连接已中断,代理可能仍在会话中继续处理。",
44
+ "No companion receipt within 5 seconds. Check package enablement/trust; inspect Sessions before retrying.": "5 秒内未收到伴随扩展确认。请检查包是否已启用并获得信任,重试前先查看会话。",
45
+ "Captain request exceeded 10 minutes; agent work may continue in Sessions. No translation saved.": "船长请求已超过 10 分钟,代理可能仍在会话中继续处理;转述结果未保存。",
46
+ "Captain's Log stopped before recording completion. Inspect the previous session before retrying. Nothing was resent automatically.": "船长日志在记录完成结果前已停止。请先检查之前的会话再重试,没有自动重发任何请求。",
47
+ };
48
+ function displayCaptainError(text) {
49
+ const prefix = "Captain could not finish this request. Inspect the session before retrying; nothing was resent automatically.\n";
50
+ if (text.startsWith(prefix))
51
+ return `船长未能完成本次请求。重试前请检查会话,没有自动重发任何请求。\n${displayCaptainError(text.slice(prefix.length))}`;
52
+ const message = text.startsWith("Error: ") ? text.slice(7) : text;
53
+ return errorLabels[message] ?? text;
54
+ }
55
+ function dateLabel(value) {
56
+ const date = new Date(value);
57
+ return Number.isNaN(date.getTime()) ? value : date.toLocaleString(undefined, { month: "short", day: "numeric", hour: "2-digit", minute: "2-digit" });
58
+ }
59
+ export function renderCaptainPanel(html, view) {
60
+ const working = view.entries.some((entry) => entry.status === "running");
61
+ const pirateSelected = view.source !== undefined && view.entries.some((entry) => entry.sessionId === view.source?.id);
62
+ const disabled = !view.connected || view.connecting || view.pending || working || !view.source || pirateSelected;
63
+ const selected = view.selected;
64
+ const sourceSessionId = selected?.sourceSessionId;
65
+ const status = view.connecting ? "连接中…" : !view.connected ? "连接已断开" : view.pending ? "正在读取最后一条回复…"
66
+ : working ? "船长正在转述…" : !view.source ? "请先选择当前工作区中的会话。" : pirateSelected ? "请选择其他会话,当前会话是船长会话。"
67
+ : "可以开始转述";
68
+ return html `<style>${styles}</style>
69
+ <section class="captains-log" aria-label="船长日志">
70
+ <div class="captain-heading"><h2>⚓ 船长日志</h2><p class="muted">让戏剧化的海盗船长转述最后一条回复。</p></div>
71
+ <div class="captain-source"><span class="muted">选中会话</span><strong>${view.source?.name ?? "未选择会话"}</strong></div>
72
+ <button class="captain-primary" ?disabled=${disabled} @click=${view.onTranslate}>让船长转述</button>
73
+ <p class="captain-status" role="status" aria-live="polite">${status}
74
+ ${!view.connected && !view.connecting ? html `<button @click=${view.onReconnect}>重新连接</button>` : null}
75
+ </p>
76
+ ${!view.connected && !view.connecting ? html `<p class="captain-notice">重新连接以查看结果。船长可能仍在处理,不会再次发送你的请求。</p>` : null}
77
+ ${view.notice !== undefined && view.notice !== "" ? html `<p class="captain-notice" role="alert">${displayCaptainError(view.notice)}</p>` : null}
78
+ ${selected ? html `<article class="captain-reply" aria-label="船长转述">
79
+ <div class="captain-reply-heading"><strong>${selected.status === "running" ? "转述中…" : selected.id === view.entries[0]?.id ? "最近转述" : "历史转述"}</strong><time datetime=${selected.createdAt}>${dateLabel(selected.createdAt)}</time></div>
80
+ ${selected.status === "completed" ? html `<div class="captain-answer">${selected.text ? renderCaptainMarkdown(html, selected.text) : "正在接收船长回复…"}</div>`
81
+ : selected.status === "running" ? html `<p class="muted">正在读取源回复并改写为海盗风格,不向源会话发送消息。</p>`
82
+ : html `<div class="captain-notice" role="alert"><strong>${selected.status === "interrupted" ? "转述已中断" : "无法转述该回复"}</strong><p>${displayCaptainError(selected.text) || "请查看诊断详情,待会话就绪后重试。"}</p></div>`}
83
+ ${sourceSessionId !== undefined && sourceSessionId !== "" ? html `<button @click=${() => { view.onOpenSource(sourceSessionId); }}>打开源会话</button>` : null}
84
+ </article>` : html `<p class="captain-empty muted">选择会话后转述其最新已完成的助手回复,原会话保持不变。</p>`}
85
+ <div class="captain-secondary">
86
+ ${view.entries.length > 1 ? html `<details><summary>历史转述 <span class="muted">(${view.entries.length})</span></summary>
87
+ <div class="captain-history">${view.entries.map((entry) => html `<button class=${entry.id === selected?.id ? "selected" : ""} ?disabled=${!view.connected} @click=${() => { view.onRead(entry.id); }}>
88
+ <span>${dateLabel(entry.createdAt)} · ${entry.status === "completed" ? "已转述" : entry.status === "running" ? "处理中" : "未完成"}</span>
89
+ </button>`)}</div>
90
+ </details>` : null}
91
+ <details class="captain-diagnostics"><summary>诊断</summary>
92
+ <p>${displayCaptainError(view.status)}</p>
93
+ ${selected ? html `<dl><dt>机器</dt><dd>${view.machineName}</dd><dt>源会话</dt><dd>${selected.sourceSessionId ?? "未知"}</dd><dt>船长会话</dt><dd>${selected.sessionId || "尚未创建"}</dd><dt>请求</dt><dd>${selected.id}</dd></dl>
94
+ <p>${selected.question === "Translate last reply" ? "转述最后一条回复" : selected.question}</p><ol>${selected.stages.map((stage) => html `<li>${stageLabels[stage] ?? stage}</li>`)}</ol>` : null}
95
+ </details>
96
+ <p class="captain-footnote muted">使用你配置的模型,可能产生费用。可用时复用船长会话,否则自动创建。</p>
97
+ </div>
98
+ </section>`;
99
+ }
100
+ // Light DOM inherits workspace typography/buttons. Extra rules are package-scoped and use host themes.
101
+ const styles = `
102
+ captains-log-lifetime { display: block; min-width: 0; }
103
+ .captains-log { box-sizing: border-box; padding: 16px; min-width: 0; color: inherit; font: inherit; line-height: 1.5; }
104
+ .captains-log *, .captains-log *::before, .captains-log *::after { box-sizing: border-box; }
105
+ .captains-log h2, .captains-log p { margin: 0; }
106
+ .captains-log h2 { font-size: 15px; }
107
+ .captains-log button { font: inherit; }
108
+ .captains-log button:hover:not(:disabled) { background: var(--pi-surface-hover); }
109
+ .captains-log button:disabled { opacity: .5; cursor: not-allowed; }
110
+ .captains-log button:focus-visible, .captains-log summary:focus-visible { outline: 2px solid var(--pi-accent); outline-offset: 2px; }
111
+ .captains-log .captain-primary { border-color: var(--pi-accent-border); background: var(--pi-selection-bg); padding: 8px 12px; }
112
+ .captains-log .captain-source { display: grid; gap: 3px; margin: 18px 0 12px; padding: 10px 12px; background: var(--pi-surface); border: 1px solid var(--pi-border-muted); border-radius: 8px; overflow-wrap: anywhere; }
113
+ .captains-log .captain-source span { font-size: 11px; }
114
+ .captains-log .captain-status { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; margin: 10px 0 16px; color: var(--pi-muted); font-size: 12px; }
115
+ .captains-log .captain-notice { padding: 12px; margin-bottom: 12px; border: 1px solid var(--pi-warning-border); border-radius: 8px; background: var(--pi-warning-surface); overflow-wrap: anywhere; white-space: pre-wrap; }
116
+ .captains-log .captain-notice p { margin-top: 6px; }
117
+ .captains-log .captain-empty { padding: 16px 0; }
118
+ .captains-log .captain-reply { padding: 16px 0; border-top: 1px solid var(--pi-border-muted); }
119
+ .captains-log .captain-reply-heading { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 10px; }
120
+ .captains-log time { color: var(--pi-muted); font-size: 11px; }
121
+ .captains-log .captain-answer { line-height: 1.65; overflow-wrap: anywhere; min-width: 0; }
122
+ .captains-log .captain-answer > :first-child { margin-top: 0; }
123
+ .captains-log .captain-answer > :last-child { margin-bottom: 0; }
124
+ .captains-log .captain-answer p, .captains-log .captain-answer ul, .captains-log .captain-answer ol { margin: .65em 0; }
125
+ .captains-log .captain-answer h1, .captains-log .captain-answer h2, .captains-log .captain-answer h3,
126
+ .captains-log .captain-answer h4, .captains-log .captain-answer h5, .captains-log .captain-answer h6 { margin: 1em 0 .4em; line-height: 1.3; }
127
+ .captains-log .captain-answer h1 { font-size: 1.5em; }
128
+ .captains-log .captain-answer h2 { font-size: 1.3em; }
129
+ .captains-log .captain-answer h3 { font-size: 1.15em; }
130
+ .captains-log .captain-answer ul, .captains-log .captain-answer ol { padding-left: 1.7em; }
131
+ .captains-log .captain-answer blockquote { margin: .75em 0; padding-left: 1em; border-left: 3px solid var(--pi-border-muted); color: var(--pi-muted); }
132
+ .captains-log .captain-answer code { font-family: monospace; background: var(--pi-surface); border-radius: 3px; padding: .1em .25em; }
133
+ .captains-log .captain-answer pre { max-width: 100%; overflow: auto; padding: 12px; background: var(--pi-surface); border: 1px solid var(--pi-border-muted); border-radius: 6px; white-space: pre; overflow-wrap: normal; }
134
+ .captains-log .captain-answer pre code { padding: 0; background: none; }
135
+ .captains-log .captain-answer a { color: var(--pi-accent); text-decoration: underline; }
136
+ .captains-log .captain-answer a:focus-visible { outline: 2px solid var(--pi-accent); outline-offset: 2px; }
137
+ .captains-log .captain-table-scroll { max-width: 100%; overflow: auto; margin: .75em 0; }
138
+ .captains-log .captain-answer table { border-collapse: collapse; width: 100%; }
139
+ .captains-log .captain-answer th, .captains-log .captain-answer td { border: 1px solid var(--pi-border-muted); padding: 6px 10px; text-align: left; }
140
+ .captains-log .captain-answer th { background: var(--pi-surface); }
141
+ .captains-log .captain-answer hr { border: 0; border-top: 1px solid var(--pi-border-muted); }
142
+ .captains-log .captain-secondary { border-top: 1px solid var(--pi-border-muted); margin-top: 12px; padding-top: 6px; }
143
+ .captains-log details { padding: 8px 0; }
144
+ .captains-log summary { cursor: pointer; color: var(--pi-muted); }
145
+ .captains-log details p { margin: 8px 0; overflow-wrap: anywhere; }
146
+ .captains-log .captain-history { display: grid; gap: 6px; padding-top: 8px; }
147
+ .captains-log .captain-history button { text-align: left; min-width: 0; }
148
+ .captains-log dl { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 4px 10px; font-size: 11px; }
149
+ .captains-log dd { margin: 0; overflow-wrap: anywhere; }
150
+ .captains-log .captain-footnote { font-size: 11px; margin-top: 10px; }
151
+ `;
@@ -0,0 +1,26 @@
1
+ // Generated from pi-packages/captains-log/src/browser/protocol.ts. Do not edit directly.
2
+ export const CAPTAIN_REQUEST = "captains-log:v1:request";
3
+ export const CAPTAIN_REPLY = "captains-log:v1:reply";
4
+ export const SOURCE_REQUEST = "captains-log:v1:source-request";
5
+ export const SOURCE_REPLY = "captains-log:v1:source-reply";
6
+ export const PIRATE_INTRO = "We are pirates. Always speak pirate.";
7
+ export const PIRATE_MARKER = "captains-log:pirate-introduced";
8
+ export const MAX_FINDINGS = 48_000;
9
+ export const MAX_QUESTION = 8_000;
10
+ export const UUID = /^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/;
11
+ export function isLogEntry(value) {
12
+ if (typeof value !== "object" || value === null)
13
+ return false;
14
+ return "id" in value && typeof value.id === "string" && UUID.test(value.id)
15
+ && "createdAt" in value && typeof value.createdAt === "string"
16
+ && "sessionId" in value && typeof value.sessionId === "string" && value.sessionId.length <= 512
17
+ && (!("sourceSessionId" in value) || value.sourceSessionId === undefined || (typeof value.sourceSessionId === "string" && !!value.sourceSessionId.trim() && value.sourceSessionId.length <= 512))
18
+ && "question" in value && typeof value.question === "string" && value.question.length <= MAX_QUESTION
19
+ && "status" in value && ["running", "completed", "failed", "interrupted"].includes(String(value.status))
20
+ && (!("failureCode" in value) || value.failureCode === undefined || (typeof value.failureCode === "string" && ["session-unavailable", "request-failed", "interrupted"].includes(value.failureCode)))
21
+ && "stages" in value && Array.isArray(value.stages) && value.stages.length <= 16 && value.stages.every((s) => typeof s === "string" && s.length <= 256)
22
+ && "text" in value && typeof value.text === "string" && value.text.length <= MAX_FINDINGS;
23
+ }
24
+ export function captainPrompt(text) {
25
+ return `Retell this as a theatrical pirate captain briefing the crew. Rewrite it from scratch: nautical metaphors, colorful pirate phrasing, and a little humor. Preserve the important facts, warnings, and next steps; keep code and commands exact. Don't merely sprinkle “arrr” onto the original. Always speak pirate.\n\nSource reply:\n${text}`;
26
+ }
@@ -0,0 +1,126 @@
1
+ // Generated from pi-packages/captains-log/src/companion.ts. Do not edit directly.
2
+ import { MAX_FINDINGS, UUID, CAPTAIN_REPLY, CAPTAIN_REQUEST, SOURCE_REQUEST, SOURCE_REPLY, PIRATE_INTRO, PIRATE_MARKER, captainPrompt } from "./browser/protocol.js";
3
+ export default function companion(pi) {
4
+ let context;
5
+ let active;
6
+ const reply = (requestId, result) => { pi.events.emit(CAPTAIN_REPLY, { requestId, ...result }); };
7
+ pi.on("session_start", (_event, ctx) => { context = ctx; });
8
+ pi.on("session_shutdown", () => {
9
+ if (active)
10
+ reply(active.requestId, { status: "failed", error: "Captain request interrupted by session shutdown or reload" });
11
+ active = undefined;
12
+ context = undefined;
13
+ });
14
+ pi.on("before_agent_start", (event) => {
15
+ if (!active)
16
+ return;
17
+ if (!active.started && event.prompt === active.prompt) {
18
+ active.started = true;
19
+ if (event.prompt === PIRATE_INTRO)
20
+ pi.appendEntry(PIRATE_MARKER, {});
21
+ reply(active.requestId, { status: "running" });
22
+ }
23
+ else
24
+ active.error = "Another prompt interfered with this Captain request; translation was not saved";
25
+ });
26
+ pi.on("message_start", (event) => {
27
+ if (!active || event.message.role !== "user")
28
+ return;
29
+ const content = event.message.content;
30
+ const text = typeof content === "string" ? content : content.filter((part) => part.type === "text").map((part) => part.text).join("");
31
+ if (text !== active.prompt)
32
+ active.error = "Another user message interfered with this Captain request";
33
+ });
34
+ pi.on("tool_result", (event) => {
35
+ if (active?.started === true && event.isError)
36
+ active.error = `Captain tool failed: ${event.toolName}. Inspect the conversation and retry explicitly.`;
37
+ });
38
+ pi.on("message_end", (event) => {
39
+ if (active?.started !== true || event.message.role !== "assistant")
40
+ return;
41
+ active.text = event.message.content.filter((part) => part.type === "text").map((part) => part.text).join("\n");
42
+ active.stopReason = event.message.stopReason;
43
+ if (event.message.stopReason === "error" || event.message.stopReason === "aborted") {
44
+ active.error = event.message.errorMessage ?? `Captain request ${event.message.stopReason}`;
45
+ }
46
+ });
47
+ // agent_end can precede automatic retries/compaction. Settlement is the final boundary.
48
+ pi.on("agent_settled", () => {
49
+ if (active?.started !== true)
50
+ return;
51
+ const result = active;
52
+ active = undefined;
53
+ const error = result.error ?? (result.stopReason !== "stop" || !result.text.trim()
54
+ ? "Captain request ended without a successful final text response" : result.text.length > MAX_FINDINGS
55
+ ? "Captain reply exceeded the storage limit" : undefined);
56
+ if (error === undefined && result.nextPrompt !== undefined) {
57
+ active = { requestId: result.requestId, prompt: result.nextPrompt, started: false, text: "", stopReason: "" };
58
+ try {
59
+ pi.sendUserMessage(active.prompt);
60
+ }
61
+ catch (failure) {
62
+ active = undefined;
63
+ reply(result.requestId, { status: "failed", error: String(failure) });
64
+ }
65
+ return;
66
+ }
67
+ reply(result.requestId, error !== undefined ? { status: "failed", error } : { status: "completed", text: result.text });
68
+ });
69
+ // Read only this companion's active branch; never prompt, rename, or append to the source.
70
+ pi.events.on(SOURCE_REQUEST, (data) => {
71
+ if (typeof data !== "object" || data === null || !("requestId" in data)
72
+ || typeof data.requestId !== "string" || !UUID.test(data.requestId))
73
+ return;
74
+ const respond = (result) => { pi.events.emit(SOURCE_REPLY, { requestId: data.requestId, ...result }); };
75
+ try {
76
+ if (!context || active || !context.isIdle() || context.hasPendingMessages())
77
+ throw new Error("Source session must be idle with no pending messages. Wait for it to finish, then translate again.");
78
+ if (context.sessionManager.getEntries().some((entry) => entry.type === "custom" && (entry.customType === PIRATE_MARKER || entry.customType === "captains-log:request")))
79
+ throw new Error("Select a source session other than the pirate conversation.");
80
+ for (const entry of [...context.sessionManager.getBranch()].reverse()) {
81
+ if (entry.type !== "message" || entry.message.role !== "assistant" || entry.message.stopReason !== "stop")
82
+ continue;
83
+ const text = entry.message.content.filter((part) => part.type === "text").map((part) => part.text).join("\n");
84
+ if (!text.trim())
85
+ continue;
86
+ if (text.length > MAX_FINDINGS)
87
+ throw new Error("Source reply exceeds the translation limit (48000 characters).");
88
+ respond({ status: "completed", text });
89
+ return;
90
+ }
91
+ throw new Error("Source session has no completed assistant text reply on its active branch.");
92
+ }
93
+ catch (error) {
94
+ respond({ status: "failed", error: error instanceof Error ? error.message : String(error) });
95
+ }
96
+ });
97
+ // Pi owns listeners and replaces them on /reload. No background resources at load time.
98
+ pi.events.on(CAPTAIN_REQUEST, (data) => {
99
+ if (typeof data !== "object" || data === null || !("requestId" in data)
100
+ || typeof data.requestId !== "string" || !UUID.test(data.requestId))
101
+ return;
102
+ if (!("text" in data) || typeof data.text !== "string" || !data.text.trim() || data.text.length > MAX_FINDINGS) {
103
+ reply(data.requestId, { status: "failed", error: "Invalid translation text" });
104
+ return;
105
+ }
106
+ const { requestId } = data;
107
+ if (!context || active || !context.isIdle() || context.hasPendingMessages()) {
108
+ reply(requestId, { status: "failed", error: "Companion not ready or session busy; open Captain's Log in Sessions, wait for idle, then retry" });
109
+ return;
110
+ }
111
+ const introduced = context.sessionManager.getEntries().some((entry) => entry.type === "custom" && entry.customType === PIRATE_MARKER);
112
+ const translation = captainPrompt(data.text);
113
+ const prompt = introduced ? translation : PIRATE_INTRO;
114
+ active = { requestId, prompt, ...(introduced ? {} : { nextPrompt: translation }), started: false, text: "", stopReason: "" };
115
+ try {
116
+ pi.setSessionName("Captain's Log");
117
+ pi.appendEntry("captains-log:request", { requestId });
118
+ reply(requestId, { status: "accepted" });
119
+ pi.sendUserMessage(prompt);
120
+ }
121
+ catch (error) {
122
+ active = undefined;
123
+ reply(requestId, { status: "failed", error: error instanceof Error ? error.message : String(error) });
124
+ }
125
+ });
126
+ }
@@ -0,0 +1,62 @@
1
+ // Generated from pi-packages/captains-log/src/roundtrip.ts. Do not edit directly.
2
+ import { MAX_FINDINGS, CAPTAIN_REPLY, CAPTAIN_REQUEST, SOURCE_REPLY, SOURCE_REQUEST } from "./browser/protocol.js";
3
+ export function collectReply(connection, requestId, text, lifetime, progress) {
4
+ return collect(connection, requestId, { requestId, text }, CAPTAIN_REQUEST, CAPTAIN_REPLY, lifetime, progress);
5
+ }
6
+ export function collectSourceReply(connection, requestId, lifetime, progress) {
7
+ return collect(connection, requestId, { requestId }, SOURCE_REQUEST, SOURCE_REPLY, lifetime, progress);
8
+ }
9
+ /** Subscribe before sending. Never retry an ambiguous send or cancel Pi on timeout. */
10
+ async function collect(connection, requestId, payload, requestEvent, replyEvent, lifetime, progress) {
11
+ const signal = AbortSignal.any([lifetime, connection.signal]);
12
+ signal.throwIfAborted();
13
+ let unsubscribe;
14
+ let onAbort;
15
+ let receiptTimer;
16
+ let completionTimer;
17
+ try {
18
+ return await new Promise((resolve, reject) => {
19
+ onAbort = () => { reject(new Error("Captain connection interrupted; agent work may continue in Sessions")); };
20
+ signal.addEventListener("abort", onAbort, { once: true });
21
+ receiptTimer = setTimeout(() => { reject(new Error("No companion receipt within 5 seconds. Check package enablement/trust; inspect Sessions before retrying.")); }, 5_000);
22
+ completionTimer = setTimeout(() => { reject(new Error("Captain request exceeded 10 minutes; agent work may continue in Sessions. No translation saved.")); }, 600_000);
23
+ unsubscribe = connection.on(replyEvent, (data) => {
24
+ if (typeof data !== "object" || data === null || !("requestId" in data) || data.requestId !== requestId)
25
+ return;
26
+ if (!("status" in data))
27
+ return;
28
+ if (data.status === "accepted") {
29
+ clearTimeout(receiptTimer);
30
+ progress("Native companion received request");
31
+ return;
32
+ }
33
+ if (data.status === "running") {
34
+ progress("Native Pi agent running");
35
+ return;
36
+ }
37
+ if (data.status === "failed") {
38
+ reject(new Error("error" in data && typeof data.error === "string" ? data.error.slice(0, 4000) : "Captain request failed"));
39
+ }
40
+ else if (data.status === "completed") {
41
+ if (!("text" in data) || typeof data.text !== "string" || !data.text.trim() || data.text.length > MAX_FINDINGS) {
42
+ reject(new Error("Invalid companion reply"));
43
+ }
44
+ else {
45
+ progress("Backend received correlated final reply");
46
+ resolve(data.text);
47
+ }
48
+ }
49
+ });
50
+ // Native listeners can reply synchronously.
51
+ progress("Backend sent request to native companion");
52
+ connection.emit(requestEvent, payload);
53
+ });
54
+ }
55
+ finally {
56
+ clearTimeout(receiptTimer);
57
+ clearTimeout(completionTimer);
58
+ if (onAbort)
59
+ signal.removeEventListener("abort", onAbort);
60
+ unsubscribe?.();
61
+ }
62
+ }
@@ -0,0 +1,210 @@
1
+ // Generated from pi-packages/captains-log/src/server.ts. Do not edit directly.
2
+ import { randomUUID } from "node:crypto";
3
+ import { PI_WEB_HOST_PI_SESSIONS_CAPABILITY, PI_WEB_HOST_PI_SESSION_EVENTS_CAPABILITY } from "@chainingintention/pi-web-cn/server-plugin-api";
4
+ import {} from "./browser/protocol.js";
5
+ import { LOG_CHANNEL, entryFrames, isCaptainClientFrame } from "./browser/channelProtocol.js";
6
+ import { collectReply, collectSourceReply } from "./roundtrip.js";
7
+ import { LogStore, logScope } from "./store.js";
8
+ const plugin = {
9
+ apiVersion: 3,
10
+ name: "Captain's Log",
11
+ requires: [PI_WEB_HOST_PI_SESSIONS_CAPABILITY, PI_WEB_HOST_PI_SESSION_EVENTS_CAPABILITY],
12
+ activate(host) {
13
+ const store = new LogStore(host.dataDirectory);
14
+ const active = new Map();
15
+ const unsaved = new Map();
16
+ const currentSessions = new Map();
17
+ const subscribers = new Map();
18
+ const channelCleanups = new Set();
19
+ function publish(scope, entry, saved) {
20
+ for (const frame of entryFrames(entry, saved))
21
+ for (const send of subscribers.get(scope) ?? [])
22
+ send(frame);
23
+ }
24
+ const tasks = new Set();
25
+ let sessions;
26
+ let events;
27
+ function visible(scope, entry) {
28
+ const live = active.get(scope);
29
+ if (live?.id === entry.id)
30
+ return live;
31
+ return unsaved.get(entry.id) ?? (entry.status === "running"
32
+ ? { ...entry, status: "interrupted", failureCode: "interrupted", text: "Captain's Log stopped before recording completion. Inspect the previous session before retrying. Nothing was resent automatically." }
33
+ : entry);
34
+ }
35
+ return {
36
+ start({ capabilities }) {
37
+ sessions = capabilities.resolve(PI_WEB_HOST_PI_SESSIONS_CAPABILITY);
38
+ events = capabilities.resolve(PI_WEB_HOST_PI_SESSION_EVENTS_CAPABILITY);
39
+ },
40
+ peer: {
41
+ async request(context) {
42
+ context.signal.throwIfAborted();
43
+ host.lifetimeSignal.throwIfAborted();
44
+ const selection = { projectId: context.project.id, workspaceId: context.workspace.id };
45
+ const scope = logScope(selection.projectId, selection.workspaceId);
46
+ if (context.operation === "list") {
47
+ const entries = (await store.list(scope)).map((entry) => visible(scope, entry));
48
+ const live = active.get(scope);
49
+ if (live && !entries.some((entry) => entry.id === live.id))
50
+ entries.unshift(live);
51
+ return entries.map((entry) => ({ ...entry, text: "" }));
52
+ }
53
+ if (context.operation === "read") {
54
+ const live = active.get(scope);
55
+ return { ...(live?.id === context.input ? live : visible(scope, await store.read(scope, context.input))) };
56
+ }
57
+ throw new Error("Unknown Captain operation (translations require the log channel)");
58
+ },
59
+ openChannel(context) {
60
+ context.signal.throwIfAborted();
61
+ host.lifetimeSignal.throwIfAborted();
62
+ if (context.operation !== LOG_CHANNEL || context.input !== null)
63
+ throw new Error("Invalid Captain channel open");
64
+ const selection = { projectId: context.project.id, workspaceId: context.workspace.id };
65
+ const scope = logScope(selection.projectId, selection.workspaceId);
66
+ let listening = true;
67
+ const send = (frame) => {
68
+ if (!listening)
69
+ return;
70
+ try {
71
+ context.send(frame.type === "entry" ? { ...frame, entry: { ...frame.entry } } : frame);
72
+ }
73
+ catch (error) {
74
+ cleanup();
75
+ host.logger.warn(`Captain subscriber disconnected: ${String(error)}`);
76
+ }
77
+ };
78
+ const cleanup = () => {
79
+ listening = false;
80
+ subscribers.get(scope)?.delete(send);
81
+ if ((subscribers.get(scope)?.size ?? 0) === 0)
82
+ subscribers.delete(scope);
83
+ context.signal.removeEventListener("abort", cleanup);
84
+ host.lifetimeSignal.removeEventListener("abort", cleanup);
85
+ channelCleanups.delete(cleanup);
86
+ };
87
+ if (!subscribers.has(scope))
88
+ subscribers.set(scope, new Set());
89
+ subscribers.get(scope)?.add(send);
90
+ context.signal.addEventListener("abort", cleanup, { once: true });
91
+ host.lifetimeSignal.addEventListener("abort", cleanup, { once: true });
92
+ channelCleanups.add(cleanup);
93
+ return {
94
+ close: cleanup,
95
+ async receive(data, signal) {
96
+ signal.throwIfAborted();
97
+ host.lifetimeSignal.throwIfAborted();
98
+ if (!isCaptainClientFrame(data))
99
+ throw new Error("Invalid Captain translation frame");
100
+ const reject = (message) => { send({ type: "rejected", requestId: data.requestId, message: message.slice(0, 2000) }); };
101
+ if (active.has(scope)) {
102
+ reject("Captain is already working.");
103
+ return;
104
+ }
105
+ const entry = { id: randomUUID(), createdAt: new Date().toISOString(), sessionId: "", question: "Translate last reply", sourceSessionId: data.sourceSessionId, status: "running", stages: ["Backend admitted translation"], text: "Preparing Captain's Log…" };
106
+ // Lock before any I/O so concurrent tabs cannot create two dedicated sessions.
107
+ active.set(scope, entry);
108
+ try {
109
+ const previous = await store.list(scope);
110
+ // Strictly increasing admission times make persisted session selection independent of UUID ties.
111
+ const latestTime = Math.max(0, ...previous.map((record) => Date.parse(record.createdAt)).filter(Number.isFinite));
112
+ entry.createdAt = new Date(Math.max(Date.now(), latestTime + 1)).toISOString();
113
+ entry.sessionId = currentSessions.get(scope) ?? previous.find((record) => record.sessionId)?.sessionId ?? "";
114
+ if (entry.sessionId === data.sourceSessionId || previous.some((record) => record.sessionId === data.sourceSessionId))
115
+ throw new Error("Select a source session other than the pirate conversation.");
116
+ await store.save(scope, entry);
117
+ }
118
+ catch (error) {
119
+ active.delete(scope);
120
+ reject(String(error));
121
+ return;
122
+ }
123
+ send({ type: "admitted", requestId: data.requestId, id: entry.id });
124
+ publish(scope, entry, true);
125
+ const progress = (stage) => {
126
+ if (!entry.stages.includes(stage) && entry.stages.length < 16)
127
+ entry.stages.push(stage);
128
+ publish(scope, entry, false);
129
+ };
130
+ const task = (async () => {
131
+ try {
132
+ host.lifetimeSignal.throwIfAborted();
133
+ const source = await events.connect({ ...selection, sessionId: data.sourceSessionId });
134
+ let sourceText;
135
+ try {
136
+ sourceText = await collectSourceReply(source, entry.id, host.lifetimeSignal, progress);
137
+ }
138
+ finally {
139
+ source.close();
140
+ }
141
+ progress("Source reply read without changing source");
142
+ const createPirate = async () => {
143
+ ({ sessionId: entry.sessionId } = await sessions.create(selection));
144
+ currentSessions.set(scope, entry.sessionId);
145
+ // Persist identity before any potentially ambiguous send.
146
+ await store.save(scope, entry);
147
+ return events.connect({ ...selection, sessionId: entry.sessionId });
148
+ };
149
+ const previousSessionId = entry.sessionId;
150
+ const connection = previousSessionId
151
+ ? await events.connect({ ...selection, sessionId: previousSessionId }).catch((error) => {
152
+ // Only a definitive rejection before sending permits automatic replacement.
153
+ if (error instanceof Error && error.message === "Selected session is not hosted in this workspace on this machine")
154
+ return createPirate();
155
+ throw error;
156
+ })
157
+ : await createPirate();
158
+ progress("Pirate conversation selected");
159
+ try {
160
+ entry.text = await collectReply(connection, entry.id, sourceText, host.lifetimeSignal, progress);
161
+ }
162
+ finally {
163
+ connection.close();
164
+ }
165
+ entry.status = "completed";
166
+ }
167
+ catch (error) {
168
+ entry.status = "failed";
169
+ entry.failureCode ??= "request-failed";
170
+ entry.text = `Captain could not finish this request. Inspect the session before retrying; nothing was resent automatically.\n${error instanceof Error ? error.message : String(error)}`.slice(0, 4000);
171
+ progress("Backend recorded failure (not success)");
172
+ }
173
+ let saved = false;
174
+ try {
175
+ if (entry.status === "completed")
176
+ entry.stages.push("Backend saved completed roundtrip");
177
+ await store.save(scope, entry);
178
+ saved = true;
179
+ }
180
+ catch (error) {
181
+ entry.status = "failed";
182
+ entry.failureCode = "request-failed";
183
+ entry.stages = entry.stages.filter((stage) => stage !== "Backend saved completed roundtrip");
184
+ entry.text = `Could not save Captain's Log: ${String(error)}. Inspect session ${entry.sessionId}.`.slice(0, 4000);
185
+ unsaved.set(entry.id, { ...entry });
186
+ host.logger.error(entry.text);
187
+ }
188
+ finally {
189
+ active.delete(scope);
190
+ publish(scope, entry, saved);
191
+ }
192
+ })();
193
+ tasks.add(task);
194
+ void task.finally(() => { tasks.delete(task); });
195
+ },
196
+ };
197
+ },
198
+ },
199
+ async dispose() {
200
+ await Promise.all(tasks);
201
+ for (const cleanup of channelCleanups)
202
+ cleanup();
203
+ active.clear();
204
+ unsaved.clear();
205
+ currentSessions.clear();
206
+ },
207
+ };
208
+ },
209
+ };
210
+ export default plugin;