@xopcai/xopc 0.0.140 → 0.0.142

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 (250) hide show
  1. package/dist/browser-ext/manifest.json +1 -1
  2. package/dist/extensions/telegram/xopc.extension.json +1 -1
  3. package/dist/gateway/static/root/assets/{agent-avatar-dicebear-DtONoxBN.js → agent-avatar-dicebear-BawPxiu5.js} +1 -1
  4. package/dist/gateway/static/root/assets/agents-CyvCIHnB.js +2 -0
  5. package/dist/gateway/static/root/assets/{app-management-settings-panel-BRBxLECY.js → app-management-settings-panel-81xd1p9R.js} +1 -1
  6. package/dist/gateway/static/root/assets/{appearance-settings-ChvyuSEt.js → appearance-settings-DYeQsYU8.js} +1 -1
  7. package/dist/gateway/static/root/assets/{apps-page-DgHWTnjs.js → apps-page-DK_Vx6nl.js} +1 -1
  8. package/dist/gateway/static/root/assets/{archive-plugin-CdIoagem.js → archive-plugin-D_oa7oti.js} +1 -1
  9. package/dist/gateway/static/root/assets/{attachment-load-Ch411Kht.js → attachment-load-Cd-bwI2N.js} +1 -1
  10. package/dist/gateway/static/root/assets/{automation-suggestion-card-SVq27yw2.js → automation-suggestion-card-B1LV3YYv.js} +1 -1
  11. package/dist/gateway/static/root/assets/automations-page-fzrovqhv.js +4 -0
  12. package/dist/gateway/static/root/assets/{binary-plugins-D8cV-emn.js → binary-plugins-O8svLKWw.js} +1 -1
  13. package/dist/gateway/static/root/assets/{block-editor-CXDkby6y.js → block-editor-Dhj5f4Bx.js} +47 -47
  14. package/dist/gateway/static/root/assets/browser-settings-page-BDn-n-UB.js +8 -0
  15. package/dist/gateway/static/root/assets/{capability-presets-api-DLxI--Ge.js → capability-presets-api-dJy1Eqgx.js} +1 -1
  16. package/dist/gateway/static/root/assets/{capability-presets-settings-panel-DKyRbnlH.js → capability-presets-settings-panel-Co5ipo_Q.js} +1 -1
  17. package/dist/gateway/static/root/assets/{channel-recipient-api-DllrPmp-.js → channel-recipient-api-Bfuhp2ty.js} +1 -1
  18. package/dist/gateway/static/root/assets/channels-settings-BWlpaxg9.js +1 -0
  19. package/dist/gateway/static/root/assets/channels-status-swr-D_jkIZLV.js +1 -0
  20. package/dist/gateway/static/root/assets/connectors-page-BcekbW4q.js +2 -0
  21. package/dist/gateway/static/root/assets/{desktop-pet-Bz9ommjr.js → desktop-pet-DFMfIb5F.js} +1 -1
  22. package/dist/gateway/static/root/assets/{desktop-pet-settings-BnLgxgoR.js → desktop-pet-settings-BJCUuL8Y.js} +1 -1
  23. package/dist/gateway/static/root/assets/{directory-picker-path-field-DLsCwFaf.js → directory-picker-path-field-BrYt1gAr.js} +1 -1
  24. package/dist/gateway/static/root/assets/{dreaming-settings-BzkF1-XT.js → dreaming-settings-BN1Z2mIo.js} +3 -3
  25. package/dist/gateway/static/root/assets/{extension-debug-page-CdERvf3L.js → extension-debug-page-Bh630pLD.js} +1 -1
  26. package/dist/gateway/static/root/assets/{extension-page-D1fz474Z.js → extension-page-B-1KxvVy.js} +1 -1
  27. package/dist/gateway/static/root/assets/{extension-settings-page-DoQMkNOK.js → extension-settings-page-BF9NACI3.js} +1 -1
  28. package/dist/gateway/static/root/assets/{fetch-BID89uCH.js → fetch-B1WtuprY.js} +1 -1
  29. package/dist/gateway/static/root/assets/{gateway-config-api-Rygo8UAs.js → gateway-config-api-De2N821l.js} +1 -1
  30. package/dist/gateway/static/root/assets/{gateway-settings-kb8KIeQL.js → gateway-settings-DN9sNREz.js} +1 -1
  31. package/dist/gateway/static/root/assets/{gateway-startup-retry-BkrLLlpE.js → gateway-startup-retry-nEujFSDj.js} +1 -1
  32. package/dist/gateway/static/root/assets/goal-create-dialog-BpoQW8cD.js +1 -0
  33. package/dist/gateway/static/root/assets/goal-detail-page-CBRSPd0b.js +4 -0
  34. package/dist/gateway/static/root/assets/{goals-config-api-BuY6-l0a.js → goals-config-api-DUy3eEse.js} +1 -1
  35. package/dist/gateway/static/root/assets/goals-page-BCcBi407.js +2 -0
  36. package/dist/gateway/static/root/assets/{goals-settings-DoH5fZWo.js → goals-settings-pkTXzXkh.js} +1 -1
  37. package/dist/gateway/static/root/assets/{heartbeat-settings-BqDs48B-.js → heartbeat-settings-ezsufgLQ.js} +1 -1
  38. package/dist/gateway/static/root/assets/{index-CrPRlGMg.js → index-DcUaXYeb.js} +65 -65
  39. package/dist/gateway/static/root/assets/keyboard-shortcuts-settings-CVIwwM15.js +1 -0
  40. package/dist/gateway/static/root/assets/logs-page-DQtTRXtj.js +7 -0
  41. package/dist/gateway/static/root/assets/{markdown-split-CSt32FWE.js → markdown-split-B_6us9j7.js} +1 -1
  42. package/dist/gateway/static/root/assets/{media-plugins-ByItHxBj.js → media-plugins-D_fryIyj.js} +1 -1
  43. package/dist/gateway/static/root/assets/messages-BXNELNBa.js +3 -0
  44. package/dist/gateway/static/root/assets/models-hub-panel-BP2M9n6Q.js +2 -0
  45. package/dist/gateway/static/root/assets/notes-page-CspveYaO.js +1 -0
  46. package/dist/gateway/static/root/assets/notes-workbench-BHwzJ0OC.js +2 -0
  47. package/dist/gateway/static/root/assets/{page-tabs-DOfMaq_L.js → page-tabs-D_8jGlqx.js} +1 -1
  48. package/dist/gateway/static/root/assets/{preview-open-alternatives-BArxASu7.js → preview-open-alternatives-BBLMK8Mp.js} +1 -1
  49. package/dist/gateway/static/root/assets/project-detail-page-Dy3rsobs.js +1 -0
  50. package/dist/gateway/static/root/assets/projects-page-CWKpPE0L.js +1 -0
  51. package/dist/gateway/static/root/assets/remote-access-hub-CqNRyXEo.js +1 -0
  52. package/dist/gateway/static/root/assets/{save-bar-controls-BRxlkhG8.js → save-bar-controls-DVS3FR5r.js} +1 -1
  53. package/dist/gateway/static/root/assets/{schedule-field-BzM1iyOc.js → schedule-field-D6vKd6-T.js} +1 -1
  54. package/dist/gateway/static/root/assets/{schema-form-ChVcm0DX.js → schema-form-BPy4lbIh.js} +1 -1
  55. package/dist/gateway/static/root/assets/sessions-page-MEME7ivF.js +1 -0
  56. package/dist/gateway/static/root/assets/settings-advanced-gate-JVd0qaSF.js +1 -0
  57. package/dist/gateway/static/root/assets/{settings-collapsible-section-BcD9xrvY.js → settings-collapsible-section-WzdikxPt.js} +1 -1
  58. package/dist/gateway/static/root/assets/{settings-form-section-Cu30CXVl.js → settings-form-section-CvfzhBV-.js} +1 -1
  59. package/dist/gateway/static/root/assets/{settings-loading-skeleton-Cfd37IYb.js → settings-loading-skeleton-BxR-Ulhi.js} +1 -1
  60. package/dist/gateway/static/root/assets/{settings-page-EINHyzLo.js → settings-page-DYEbB1EV.js} +1 -1
  61. package/dist/gateway/static/root/assets/{settings-page-layout-C15zixct.js → settings-page-layout-CKefvbYi.js} +1 -1
  62. package/dist/gateway/static/root/assets/{setup-status-panel-Bu7SdpD4.js → setup-status-panel-dWN8BJ3z.js} +1 -1
  63. package/dist/gateway/static/root/assets/{share-preview-page-CjjAFV-v.js → share-preview-page-D8foINzP.js} +1 -1
  64. package/dist/gateway/static/root/assets/shares-settings-Tgbv1s7G.js +1 -0
  65. package/dist/gateway/static/root/assets/skill-reload-api-D_YK0b_Z.js +1 -0
  66. package/dist/gateway/static/root/assets/skills-page-DGytfoZK.js +2 -0
  67. package/dist/gateway/static/root/assets/{system-settings-panel-DlQMtzPf.js → system-settings-panel-BokgAgf7.js} +1 -1
  68. package/dist/gateway/static/root/assets/{typed-models-lib-BS1BwExE.js → typed-models-lib-BwHlNL3L.js} +1 -1
  69. package/dist/gateway/static/root/assets/{use-channel-catalog-EA4hfbsP.js → use-channel-catalog-DR11ap3A.js} +1 -1
  70. package/dist/gateway/static/root/assets/{user-profile-settings-panel-B7RCdmSp.js → user-profile-settings-panel-8YtygcJp.js} +1 -1
  71. package/dist/gateway/static/root/assets/{utils-BrhEgVaa.js → utils-BKUWB-TT.js} +1 -1
  72. package/dist/gateway/static/root/assets/voice-api-key-field-BVH71EFf.js +1 -0
  73. package/dist/gateway/static/root/assets/work-item-detail-page-JeTE7XNM.js +1 -0
  74. package/dist/gateway/static/root/assets/workflow-run-setup-panel-CL_rnAUB.js +3 -0
  75. package/dist/gateway/static/root/assets/{workflow-start-dialog-BZCY7N42.js → workflow-start-dialog-_u8Uc1ze.js} +1 -1
  76. package/dist/gateway/static/root/assets/workflows-page-ixgk_y1l.js +18 -0
  77. package/dist/gateway/static/root/index.html +4 -4
  78. package/dist/package.js +1 -1
  79. package/dist/src/agent/agent-manager.js +13 -21
  80. package/dist/src/agent/agent-manager.js.map +1 -1
  81. package/dist/src/agent/background-review/run-background-review.js +1 -0
  82. package/dist/src/agent/background-review/run-background-review.js.map +1 -1
  83. package/dist/src/agent/embedded/session-runner.js +5 -5
  84. package/dist/src/agent/embedded/session-runner.js.map +1 -1
  85. package/dist/src/agent/embedded/xopc-auth-storage.d.ts +4 -3
  86. package/dist/src/agent/embedded/xopc-auth-storage.js +6 -6
  87. package/dist/src/agent/embedded/xopc-auth-storage.js.map +1 -1
  88. package/dist/src/agent/embedded/xopc-stream-bridge.js +1 -0
  89. package/dist/src/agent/embedded/xopc-stream-bridge.js.map +1 -1
  90. package/dist/src/agent/goals/checklist-judge.js +3 -3
  91. package/dist/src/agent/goals/checklist-judge.js.map +1 -1
  92. package/dist/src/agent/goals/judge.js +2 -2
  93. package/dist/src/agent/goals/judge.js.map +1 -1
  94. package/dist/src/agent/image/understanding/pi-ai-provider.js +2 -2
  95. package/dist/src/agent/image/understanding/pi-ai-provider.js.map +1 -1
  96. package/dist/src/agent/memory/access-policy.d.ts +12 -0
  97. package/dist/src/agent/memory/access-policy.js +44 -0
  98. package/dist/src/agent/memory/access-policy.js.map +1 -0
  99. package/dist/src/agent/memory/builtin-provider.d.ts +4 -1
  100. package/dist/src/agent/memory/builtin-provider.js +51 -16
  101. package/dist/src/agent/memory/builtin-provider.js.map +1 -1
  102. package/dist/src/agent/memory/compaction.js +2 -2
  103. package/dist/src/agent/memory/compaction.js.map +1 -1
  104. package/dist/src/agent/memory/context/planner.d.ts +1 -0
  105. package/dist/src/agent/memory/context/planner.js +9 -5
  106. package/dist/src/agent/memory/context/planner.js.map +1 -1
  107. package/dist/src/agent/memory/create-memory-manager.d.ts +1 -1
  108. package/dist/src/agent/memory/create-memory-manager.js +8 -5
  109. package/dist/src/agent/memory/create-memory-manager.js.map +1 -1
  110. package/dist/src/agent/memory/dreaming/config.d.ts +2 -0
  111. package/dist/src/agent/memory/dreaming/config.js +13 -1
  112. package/dist/src/agent/memory/dreaming/config.js.map +1 -1
  113. package/dist/src/agent/memory/dreaming/deep-promotion.d.ts +2 -0
  114. package/dist/src/agent/memory/dreaming/deep-promotion.js +24 -3
  115. package/dist/src/agent/memory/dreaming/deep-promotion.js.map +1 -1
  116. package/dist/src/agent/memory/dreaming/light-sweep.d.ts +1 -0
  117. package/dist/src/agent/memory/dreaming/light-sweep.js +184 -16
  118. package/dist/src/agent/memory/dreaming/light-sweep.js.map +1 -1
  119. package/dist/src/agent/memory/dreaming/promotion-lifecycle.d.ts +30 -0
  120. package/dist/src/agent/memory/dreaming/promotion-lifecycle.js +85 -0
  121. package/dist/src/agent/memory/dreaming/promotion-lifecycle.js.map +1 -0
  122. package/dist/src/agent/memory/dreaming/rem-patterns.d.ts +4 -0
  123. package/dist/src/agent/memory/dreaming/rem-patterns.js +76 -53
  124. package/dist/src/agent/memory/dreaming/rem-patterns.js.map +1 -1
  125. package/dist/src/agent/memory/dreaming/short-term-store.d.ts +10 -0
  126. package/dist/src/agent/memory/dreaming/short-term-store.js +157 -79
  127. package/dist/src/agent/memory/dreaming/short-term-store.js.map +1 -1
  128. package/dist/src/agent/memory/manager.d.ts +7 -0
  129. package/dist/src/agent/memory/manager.js +31 -6
  130. package/dist/src/agent/memory/manager.js.map +1 -1
  131. package/dist/src/agent/memory/memory-config.d.ts +1 -1
  132. package/dist/src/agent/memory/memory-config.js +3 -3
  133. package/dist/src/agent/memory/memory-config.js.map +1 -1
  134. package/dist/src/agent/memory/sensitivity.d.ts +3 -0
  135. package/dist/src/agent/memory/sensitivity.js +13 -0
  136. package/dist/src/agent/memory/sensitivity.js.map +1 -0
  137. package/dist/src/agent/memory/understanding/service.js +10 -14
  138. package/dist/src/agent/memory/understanding/service.js.map +1 -1
  139. package/dist/src/agent/memory/user-context-coordinator.d.ts +1 -0
  140. package/dist/src/agent/memory/user-context-coordinator.js +2 -0
  141. package/dist/src/agent/memory/user-context-coordinator.js.map +1 -1
  142. package/dist/src/agent/orchestration/agent-orchestrator.js +8 -1
  143. package/dist/src/agent/orchestration/agent-orchestrator.js.map +1 -1
  144. package/dist/src/agent/service/btw-query.d.ts +4 -0
  145. package/dist/src/agent/service/btw-query.js +94 -13
  146. package/dist/src/agent/service/btw-query.js.map +1 -1
  147. package/dist/src/agent/session/session-inspector.js +9 -1
  148. package/dist/src/agent/session/session-inspector.js.map +1 -1
  149. package/dist/src/agent/tools/dreaming-tool.js +3 -12
  150. package/dist/src/agent/tools/dreaming-tool.js.map +1 -1
  151. package/dist/src/agent/tools/factory.js +14 -2
  152. package/dist/src/agent/tools/factory.js.map +1 -1
  153. package/dist/src/agent/tools/memory-tool.d.ts +3 -0
  154. package/dist/src/agent/tools/memory-tool.js +6 -3
  155. package/dist/src/agent/tools/memory-tool.js.map +1 -1
  156. package/dist/src/agent/tools/session-search-tool.js +2 -2
  157. package/dist/src/agent/tools/session-search-tool.js.map +1 -1
  158. package/dist/src/agent/tools/web-extract.js +2 -2
  159. package/dist/src/agent/tools/web-extract.js.map +1 -1
  160. package/dist/src/agent/workspace-runtime/registry.d.ts +6 -6
  161. package/dist/src/agent/workspace-runtime/registry.js +19 -12
  162. package/dist/src/agent/workspace-runtime/registry.js.map +1 -1
  163. package/dist/src/agent-manifest/schema.d.ts +4 -0
  164. package/dist/src/agent-manifest/schema.js +2 -1
  165. package/dist/src/agent-manifest/schema.js.map +1 -1
  166. package/dist/src/agent-runtime/index.js +1 -1
  167. package/dist/src/agent-runtime/runtime-profile.js +1 -1
  168. package/dist/src/ai-assist/text-assist.d.ts +2 -1
  169. package/dist/src/ai-assist/text-assist.js +10 -23
  170. package/dist/src/ai-assist/text-assist.js.map +1 -1
  171. package/dist/src/auth/credentials.js +5 -2
  172. package/dist/src/auth/credentials.js.map +1 -1
  173. package/dist/src/auth/oauth/github-copilot.d.ts +3 -4
  174. package/dist/src/auth/oauth/github-copilot.js +66 -2
  175. package/dist/src/auth/oauth/github-copilot.js.map +1 -0
  176. package/dist/src/auth/oauth/index.js +2 -2
  177. package/dist/src/automations/draft/automation-draft-service.js +3 -3
  178. package/dist/src/automations/draft/automation-draft-service.js.map +1 -1
  179. package/dist/src/chat-commands/builtins/review.js +22 -5
  180. package/dist/src/chat-commands/builtins/review.js.map +1 -1
  181. package/dist/src/chat-commands/types.d.ts +4 -0
  182. package/dist/src/config/schema.d.ts +4 -0
  183. package/dist/src/gateway/hono/lib/config-payload.d.ts +2 -0
  184. package/dist/src/gateway/hono/routes/dreaming.js +28 -21
  185. package/dist/src/gateway/hono/routes/dreaming.js.map +1 -1
  186. package/dist/src/gateway/hono/routes/memory.js +16 -4
  187. package/dist/src/gateway/hono/routes/memory.js.map +1 -1
  188. package/dist/src/gateway/hono/routes/models.js +1 -0
  189. package/dist/src/gateway/hono/routes/models.js.map +1 -1
  190. package/dist/src/gateway/hono/routes/voice.js +2 -2
  191. package/dist/src/gateway/hono/routes/voice.js.map +1 -1
  192. package/dist/src/goals/goal-contract-draft.js +2 -2
  193. package/dist/src/goals/goal-contract-draft.js.map +1 -1
  194. package/dist/src/goals/goal-evidence-review.js +2 -2
  195. package/dist/src/goals/goal-evidence-review.js.map +1 -1
  196. package/dist/src/notes/service.js +2 -2
  197. package/dist/src/notes/service.js.map +1 -1
  198. package/dist/src/providers/constants.d.ts +2 -0
  199. package/dist/src/providers/constants.js +11 -0
  200. package/dist/src/providers/constants.js.map +1 -0
  201. package/dist/src/providers/extension-stream-bridge.js +1 -0
  202. package/dist/src/providers/extension-stream-bridge.js.map +1 -1
  203. package/dist/src/providers/index.d.ts +3 -3
  204. package/dist/src/providers/index.js +5 -4
  205. package/dist/src/providers/index.js.map +1 -1
  206. package/dist/src/providers/model-call.d.ts +17 -0
  207. package/dist/src/providers/model-call.js +60 -0
  208. package/dist/src/providers/model-call.js.map +1 -0
  209. package/dist/src/session/session-title.js +2 -2
  210. package/dist/src/session/session-title.js.map +1 -1
  211. package/dist/src/storage/sqlite/fts.d.ts +6 -0
  212. package/dist/src/storage/sqlite/fts.js +70 -1
  213. package/dist/src/storage/sqlite/fts.js.map +1 -1
  214. package/dist/src/storage/sqlite/memory-index-repository.js +16 -7
  215. package/dist/src/storage/sqlite/memory-index-repository.js.map +1 -1
  216. package/dist/src/storage/sqlite/memory-records-repository.d.ts +18 -0
  217. package/dist/src/storage/sqlite/memory-records-repository.js +55 -8
  218. package/dist/src/storage/sqlite/memory-records-repository.js.map +1 -1
  219. package/dist/src/voice/tts/summarize.js +2 -2
  220. package/dist/src/voice/tts/summarize.js.map +1 -1
  221. package/dist/src/workflows/draft/workflow-draft-service.js +2 -2
  222. package/dist/src/workflows/draft/workflow-draft-service.js.map +1 -1
  223. package/package.json +5 -5
  224. package/dist/gateway/static/root/assets/agents-Dl9EWnUh.js +0 -2
  225. package/dist/gateway/static/root/assets/automations-page-CFt5Ik4q.js +0 -4
  226. package/dist/gateway/static/root/assets/browser-settings-page-ColJPwer.js +0 -8
  227. package/dist/gateway/static/root/assets/channels-settings-BDLHV3Jx.js +0 -1
  228. package/dist/gateway/static/root/assets/channels-status-swr-EUwWYx8l.js +0 -1
  229. package/dist/gateway/static/root/assets/connectors-page-CA752gY8.js +0 -2
  230. package/dist/gateway/static/root/assets/goal-create-dialog-DVAHnifk.js +0 -1
  231. package/dist/gateway/static/root/assets/goal-detail-page-BuOEQZn_.js +0 -4
  232. package/dist/gateway/static/root/assets/goals-page-WV4cHvD-.js +0 -2
  233. package/dist/gateway/static/root/assets/keyboard-shortcuts-settings-D51yogNZ.js +0 -1
  234. package/dist/gateway/static/root/assets/logs-page-CL22jleU.js +0 -7
  235. package/dist/gateway/static/root/assets/messages-DbNQuFdA.js +0 -3
  236. package/dist/gateway/static/root/assets/models-hub-panel-BkV9hDc-.js +0 -2
  237. package/dist/gateway/static/root/assets/notes-page-BK9yydrL.js +0 -1
  238. package/dist/gateway/static/root/assets/notes-workbench-Dd0v-hK2.js +0 -2
  239. package/dist/gateway/static/root/assets/project-detail-page-DSPhB3do.js +0 -1
  240. package/dist/gateway/static/root/assets/projects-page-C-fZk1f7.js +0 -1
  241. package/dist/gateway/static/root/assets/remote-access-hub-BGaVc3t7.js +0 -1
  242. package/dist/gateway/static/root/assets/sessions-page-DbnzgQOP.js +0 -1
  243. package/dist/gateway/static/root/assets/settings-advanced-gate-CuDBoz5k.js +0 -1
  244. package/dist/gateway/static/root/assets/shares-settings-06IYvwDt.js +0 -1
  245. package/dist/gateway/static/root/assets/skill-reload-api-0ODH0DXl.js +0 -1
  246. package/dist/gateway/static/root/assets/skills-page-3W2-wDyO.js +0 -2
  247. package/dist/gateway/static/root/assets/voice-api-key-field-BPn1ZEga.js +0 -1
  248. package/dist/gateway/static/root/assets/work-item-detail-page-BQGTTsz1.js +0 -1
  249. package/dist/gateway/static/root/assets/workflow-run-setup-panel-na-N4jZ6.js +0 -3
  250. package/dist/gateway/static/root/assets/workflows-page-CjeVDs4h.js +0 -18
@@ -1,6 +1,6 @@
1
1
  import { init_sync_provider_auth, resolveProviderApiKeySync } from "../../auth/sync-provider-auth.js";
2
2
  import { getApiKeySync, init_providers } from "../../providers/index.js";
3
- import { AuthStorage } from "@earendil-works/pi-coding-agent";
3
+ import { InMemoryCredentialStore } from "@earendil-works/pi-ai";
4
4
  //#region src/agent/embedded/xopc-auth-storage.ts
5
5
  init_sync_provider_auth();
6
6
  init_providers();
@@ -15,18 +15,18 @@ function resolveXopcProviderApiKey(providerId) {
15
15
  * pi-coding-agent {@link createAgentSession} defaults to `~/.pi/agent/auth.json` and env.
16
16
  * Gateway / webchat turns inject xopc credentials through {@link applyXopcProviderApiKey}.
17
17
  */
18
- function createEmbeddedAuthStorage() {
19
- return AuthStorage.create();
18
+ function createEmbeddedCredentialStore() {
19
+ return new InMemoryCredentialStore();
20
20
  }
21
21
  /**
22
22
  * Inject the resolved key as a runtime override (highest-priority, in-memory only) before
23
23
  * the session starts so request-time auth resolution finds it.
24
24
  */
25
- function applyXopcProviderApiKey(auth, providerId) {
25
+ async function applyXopcProviderApiKey(modelRuntime, providerId) {
26
26
  const key = resolveXopcProviderApiKey(providerId);
27
- if (key && key !== "extension-managed") auth.setRuntimeApiKey(providerId, key);
27
+ if (key && key !== "extension-managed") await modelRuntime.setRuntimeApiKey(providerId, key);
28
28
  }
29
29
  //#endregion
30
- export { applyXopcProviderApiKey, createEmbeddedAuthStorage, resolveXopcProviderApiKey };
30
+ export { applyXopcProviderApiKey, createEmbeddedCredentialStore, resolveXopcProviderApiKey };
31
31
 
32
32
  //# sourceMappingURL=xopc-auth-storage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"xopc-auth-storage.js","names":[],"sources":["../../../../src/agent/embedded/xopc-auth-storage.ts"],"sourcesContent":["import { AuthStorage } from '@earendil-works/pi-coding-agent';\n\nimport { resolveProviderApiKeySync } from '../../auth/sync-provider-auth.js';\nimport { getApiKeySync } from '../../providers/index.js';\n\n/**\n * Resolve API keys the same way as {@link AgentManager} / gateway settings:\n * auth-profiles (agent + global), OAuth files, models.json, then env.\n */\nexport function resolveXopcProviderApiKey(providerId: string): string | undefined {\n return resolveProviderApiKeySync(providerId) ?? getApiKeySync(providerId);\n}\n\n/**\n * pi-coding-agent {@link createAgentSession} defaults to `~/.pi/agent/auth.json` and env.\n * Gateway / webchat turns inject xopc credentials through {@link applyXopcProviderApiKey}.\n */\nexport function createEmbeddedAuthStorage(): AuthStorage {\n return AuthStorage.create();\n}\n\n/**\n * Inject the resolved key as a runtime override (highest-priority, in-memory only) before\n * the session starts so request-time auth resolution finds it.\n */\nexport function applyXopcProviderApiKey(auth: AuthStorage, providerId: string): void {\n const key = resolveXopcProviderApiKey(providerId);\n if (key && key !== 'extension-managed') {\n auth.setRuntimeApiKey(providerId, key);\n }\n}\n"],"mappings":";;;;yBAE6E;gBACpB;;;;;AAMzD,SAAgB,0BAA0B,YAAwC;AAChF,QAAO,0BAA0B,WAAW,IAAI,cAAc,WAAW;;;;;;AAO3E,SAAgB,4BAAyC;AACvD,QAAO,YAAY,QAAQ;;;;;;AAO7B,SAAgB,wBAAwB,MAAmB,YAA0B;CACnF,MAAM,MAAM,0BAA0B,WAAW;AACjD,KAAI,OAAO,QAAQ,oBACjB,MAAK,iBAAiB,YAAY,IAAI"}
1
+ {"version":3,"file":"xopc-auth-storage.js","names":[],"sources":["../../../../src/agent/embedded/xopc-auth-storage.ts"],"sourcesContent":["import { InMemoryCredentialStore } from '@earendil-works/pi-ai';\nimport type { ModelRuntime } from '@earendil-works/pi-coding-agent';\n\nimport { resolveProviderApiKeySync } from '../../auth/sync-provider-auth.js';\nimport { getApiKeySync } from '../../providers/index.js';\n\n/**\n * Resolve API keys the same way as {@link AgentManager} / gateway settings:\n * auth-profiles (agent + global), OAuth files, models.json, then env.\n */\nexport function resolveXopcProviderApiKey(providerId: string): string | undefined {\n return resolveProviderApiKeySync(providerId) ?? getApiKeySync(providerId);\n}\n\n/**\n * pi-coding-agent {@link createAgentSession} defaults to `~/.pi/agent/auth.json` and env.\n * Gateway / webchat turns inject xopc credentials through {@link applyXopcProviderApiKey}.\n */\nexport function createEmbeddedCredentialStore(): InMemoryCredentialStore {\n return new InMemoryCredentialStore();\n}\n\n/**\n * Inject the resolved key as a runtime override (highest-priority, in-memory only) before\n * the session starts so request-time auth resolution finds it.\n */\nexport async function applyXopcProviderApiKey(\n modelRuntime: Pick<ModelRuntime, 'setRuntimeApiKey'>,\n providerId: string,\n): Promise<void> {\n const key = resolveXopcProviderApiKey(providerId);\n if (key && key !== 'extension-managed') {\n await modelRuntime.setRuntimeApiKey(providerId, key);\n }\n}\n"],"mappings":";;;;yBAG6E;gBACpB;;;;;AAMzD,SAAgB,0BAA0B,YAAwC;AAChF,QAAO,0BAA0B,WAAW,IAAI,cAAc,WAAW;;;;;;AAO3E,SAAgB,gCAAyD;AACvE,QAAO,IAAI,yBAAyB;;;;;;AAOtC,eAAsB,wBACpB,cACA,YACe;CACf,MAAM,MAAM,0BAA0B,WAAW;AACjD,KAAI,OAAO,QAAQ,oBACjB,OAAM,aAAa,iBAAiB,YAAY,IAAI"}
@@ -1,3 +1,4 @@
1
+ import "../../providers/constants.js";
1
2
  import { init_providers } from "../../providers/index.js";
2
3
  import { createExtensionAwareStreamFn } from "../../providers/extension-stream-bridge.js";
3
4
  //#region src/agent/embedded/xopc-stream-bridge.ts
@@ -1 +1 @@
1
- {"version":3,"file":"xopc-stream-bridge.js","names":[],"sources":["../../../../src/agent/embedded/xopc-stream-bridge.ts"],"sourcesContent":["import type { Model, Api, Context, SimpleStreamOptions } from '@earendil-works/pi-ai';\nimport type { StreamFn } from '@earendil-works/pi-agent-core';\n\nimport { EXTENSION_PROVIDER_BASE_URL } from '../../providers/index.js';\nimport { createExtensionAwareStreamFn } from '../../providers/extension-stream-bridge.js';\n\n/**\n * pi-coding-agent's default {@link createAgentSession} streamFn always routes through\n * pi-ai's HTTP `streamSimple`, so xopc plugin providers (whose models carry the sentinel\n * `EXTENSION_PROVIDER_BASE_URL`) never reach their {@link ProviderPluginRegistry}\n * implementation. Wrap the original streamFn so plugin models go through xopc's bridge\n * and everything else falls through to the original auth-aware streamFn.\n *\n * Use this by reassigning {@link Agent.streamFn} after `createAgentSession` returns.\n */\nexport function wrapStreamFnForXopcExtensions(originalStreamFn: StreamFn): StreamFn {\n const extensionStreamFn = createExtensionAwareStreamFn();\n\n return ((model: Model<Api>, context: Context, options?: SimpleStreamOptions) => {\n if (model.baseUrl === EXTENSION_PROVIDER_BASE_URL) {\n return extensionStreamFn(model, context, options);\n }\n return originalStreamFn(model, context, options);\n }) as StreamFn;\n}\n"],"mappings":";;;gBAGuE;;;;;;;;;;AAYvE,SAAgB,8BAA8B,kBAAsC;CAClF,MAAM,oBAAoB,8BAA8B;AAExD,UAAS,OAAmB,SAAkB,YAAkC;AAC9E,MAAI,MAAM,YAAA,8BACR,QAAO,kBAAkB,OAAO,SAAS,QAAQ;AAEnD,SAAO,iBAAiB,OAAO,SAAS,QAAQ"}
1
+ {"version":3,"file":"xopc-stream-bridge.js","names":[],"sources":["../../../../src/agent/embedded/xopc-stream-bridge.ts"],"sourcesContent":["import type { Model, Api, Context, SimpleStreamOptions } from '@earendil-works/pi-ai';\nimport type { StreamFn } from '@earendil-works/pi-agent-core';\n\nimport { EXTENSION_PROVIDER_BASE_URL } from '../../providers/index.js';\nimport { createExtensionAwareStreamFn } from '../../providers/extension-stream-bridge.js';\n\n/**\n * pi-coding-agent's default {@link createAgentSession} streamFn always routes through\n * pi-ai's HTTP `streamSimple`, so xopc plugin providers (whose models carry the sentinel\n * `EXTENSION_PROVIDER_BASE_URL`) never reach their {@link ProviderPluginRegistry}\n * implementation. Wrap the original streamFn so plugin models go through xopc's bridge\n * and everything else falls through to the original auth-aware streamFn.\n *\n * Use this by reassigning {@link Agent.streamFn} after `createAgentSession` returns.\n */\nexport function wrapStreamFnForXopcExtensions(originalStreamFn: StreamFn): StreamFn {\n const extensionStreamFn = createExtensionAwareStreamFn();\n\n return ((model: Model<Api>, context: Context, options?: SimpleStreamOptions) => {\n if (model.baseUrl === EXTENSION_PROVIDER_BASE_URL) {\n return extensionStreamFn(model, context, options);\n }\n return originalStreamFn(model, context, options);\n }) as StreamFn;\n}\n"],"mappings":";;;;gBAGuE;;;;;;;;;;AAYvE,SAAgB,8BAA8B,kBAAsC;CAClF,MAAM,oBAAoB,8BAA8B;AAExD,UAAS,OAAmB,SAAkB,YAAkC;AAC9E,MAAI,MAAM,YAAA,8BACR,QAAO,kBAAkB,OAAO,SAAS,QAAQ;AAEnD,SAAO,iBAAiB,OAAO,SAAS,QAAQ"}
@@ -1,8 +1,8 @@
1
1
  import { init_providers, resolveModel } from "../../providers/index.js";
2
+ import { completeWithResolvedCredentials } from "../../providers/model-call.js";
2
3
  import { JUDGE_REASON_EN } from "../../i18n/goals-bundle.js";
3
4
  import { goalUiLocaleOrFallback, judgeReasonText, judgeResponseLanguageNote, localizeJudgeReasonText } from "./goal-locale.js";
4
5
  import { DEFAULT_JUDGE_TIMEOUT_MS, extractAssistantText, getAssistantMessageErrorReason, resolveGoalJudgeApiKey, stripCodeFences, truncateGoalText } from "./judge.js";
5
- import { complete } from "@earendil-works/pi-ai/compat";
6
6
  //#region src/agent/goals/checklist-judge.ts
7
7
  init_providers();
8
8
  const GOAL_TRUNC = 4e3;
@@ -59,7 +59,7 @@ async function decomposeGoalChecklist(opts) {
59
59
  timestamp: Date.now()
60
60
  };
61
61
  const apiKey = await resolveGoalJudgeApiKey(model);
62
- const result = await complete(model, { messages: [user] }, {
62
+ const result = await completeWithResolvedCredentials(model, { messages: [user] }, {
63
63
  apiKey,
64
64
  maxTokens: 2e3,
65
65
  temperature: 0,
@@ -138,7 +138,7 @@ async function evaluateGoalChecklistJudge(opts) {
138
138
  timestamp: Date.now()
139
139
  };
140
140
  const apiKey = await resolveGoalJudgeApiKey(model);
141
- const result = await complete(model, { messages: [user] }, {
141
+ const result = await completeWithResolvedCredentials(model, { messages: [user] }, {
142
142
  apiKey,
143
143
  maxTokens: 1500,
144
144
  temperature: 0,
@@ -1 +1 @@
1
- {"version":3,"file":"checklist-judge.js","names":[],"sources":["../../../../src/agent/goals/checklist-judge.ts"],"sourcesContent":["import type { UserMessage } from '@earendil-works/pi-ai';\nimport { complete } from '@earendil-works/pi-ai/compat';\n\nimport { resolveModel } from '../../providers/index.js';\n\nimport {\n DEFAULT_JUDGE_TIMEOUT_MS,\n extractAssistantText,\n getAssistantMessageErrorReason,\n resolveGoalJudgeApiKey,\n stripCodeFences,\n truncateGoalText,\n} from './judge.js';\nimport type { GoalUiLocale } from './goal-locale.js';\nimport {\n goalUiLocaleOrFallback,\n judgeReasonText,\n judgeResponseLanguageNote,\n JUDGE_REASON_EN,\n localizeJudgeReasonText,\n} from './goal-locale.js';\n\nconst GOAL_TRUNC = 4000;\nconst RESPONSE_SNIPPET = 4000;\nconst HISTORY_TRUNC = 24_000;\n\nconst DECOMPOSE_SYSTEM = (\n 'You break a user goal into an EXTREMELY detailed checklist of concrete, verifiable completion criteria. ' +\n 'Bias toward more items. Each item is one factual statement about finished work.\\n\\n' +\n 'If existing acceptance criteria are provided, return only missing supplemental criteria. ' +\n 'Do not repeat, rewrite, or replace existing criteria.\\n\\n' +\n 'Reply ONLY with a single JSON object on one line:\\n' +\n '{\"items\":[{\"text\":\"...\"},...]}\\n' +\n 'Use at least 3 items when the goal warrants it.'\n);\n\nconst DECOMPOSE_USER = 'Goal:\\n{goal}\\n\\nReturn JSON only.';\n\nconst EVALUATE_CHECKLIST_SYSTEM = (\n 'You evaluate an autonomous agent\\'s progress on a goal with a numbered checklist.\\n' +\n 'For each pending item, decide if evidence in the agent snippet and/or history excerpt shows it is satisfied.\\n' +\n 'Flip pending→completed only with clear evidence; pending→impossible only if truly unachievable here.\\n' +\n 'Do not regress completed/impossible items — omit them from updates.\\n\\n' +\n 'Reply ONLY with one JSON object on one line:\\n' +\n '{\"verdict\":\"continue|done|blocked|needs_input\",\"confidence\":0.0,\"reason\":\"...\",' +\n '\"nextAction\":\"...\",\"missingEvidence\":[\"...\"],\"userQuestion\":\"...\",' +\n '\"updates\":[{\"index\":1,\"status\":\"completed|impossible\",\"evidence\":\"short citation\"}],' +\n '\"new_items\":[{\"text\":\"...\"}]}\\n' +\n 'Use verdict=done only when all checklist criteria are complete/impossible. ' +\n 'Use needs_input when the next step requires user input. Use blocked when external progress is impossible.'\n);\n\nfunction extractJsonObject(raw: string): Record<string, unknown> | null {\n if (!raw?.trim()) return null;\n const text = stripCodeFences(raw);\n try {\n const data = JSON.parse(text) as unknown;\n return data && typeof data === 'object' && !Array.isArray(data) ? (data as Record<string, unknown>) : null;\n } catch {\n const start = text.indexOf('{');\n const end = text.lastIndexOf('}');\n if (start === -1 || end <= start) return null;\n try {\n const data = JSON.parse(text.slice(start, end + 1)) as unknown;\n return data && typeof data === 'object' && !Array.isArray(data) ? (data as Record<string, unknown>) : null;\n } catch {\n return null;\n }\n }\n}\n\nexport type DecomposeChecklistResult = {\n items: { text: string }[];\n parseFailed: boolean;\n errorReason?: string;\n};\n\nexport async function decomposeGoalChecklist(opts: {\n goal: string;\n existingChecklist?: string;\n judgeModelRef: string;\n signal?: AbortSignal;\n judgeTimeoutMs?: number;\n uiLocale?: GoalUiLocale;\n}): Promise<DecomposeChecklistResult> {\n const locale = goalUiLocaleOrFallback(opts.uiLocale);\n const goal = opts.goal.trim();\n if (!goal)\n return { items: [], parseFailed: false, errorReason: judgeReasonText('empty_goal', locale) };\n\n let model: ReturnType<typeof resolveModel>;\n try {\n model = resolveModel(opts.judgeModelRef);\n } catch {\n return {\n items: [],\n parseFailed: false,\n errorReason: judgeReasonText('judge_model_not_configured', locale),\n };\n }\n\n const timeoutMs =\n typeof opts.judgeTimeoutMs === 'number' && Number.isFinite(opts.judgeTimeoutMs)\n ? Math.max(5_000, Math.min(120_000, Math.floor(opts.judgeTimeoutMs)))\n : DEFAULT_JUDGE_TIMEOUT_MS;\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeoutMs);\n const merged = opts.signal ? AbortSignal.any([opts.signal, controller.signal]) : controller.signal;\n\n try {\n const existingChecklist = opts.existingChecklist?.trim();\n const decomposeUser = DECOMPOSE_USER.replace('{goal}', truncateGoalText(goal, GOAL_TRUNC)) +\n (existingChecklist\n ? `\\n\\nExisting acceptance criteria:\\n${truncateGoalText(existingChecklist, GOAL_TRUNC)}\\n\\nReturn only missing supplemental criteria as JSON.`\n : '');\n const user: UserMessage = {\n role: 'user',\n content:\n `${DECOMPOSE_SYSTEM}\\n\\n${decomposeUser}` +\n judgeResponseLanguageNote(locale),\n timestamp: Date.now(),\n };\n const apiKey = await resolveGoalJudgeApiKey(model);\n const result = await complete(\n model,\n { messages: [user] },\n { apiKey, maxTokens: 2000, temperature: 0, signal: merged },\n );\n const errorReason = getAssistantMessageErrorReason(result);\n if (errorReason) {\n return {\n items: [],\n parseFailed: false,\n errorReason: judgeReasonText('decompose_call_failed', locale),\n };\n }\n\n const text = extractAssistantText(result.content);\n const data = extractJsonObject(text);\n if (!data)\n return {\n items: [],\n parseFailed: true,\n errorReason: judgeReasonText('decompose_reply_not_json', locale),\n };\n const rawItems = data.items ?? data.checklist;\n if (!Array.isArray(rawItems))\n return {\n items: [],\n parseFailed: true,\n errorReason: judgeReasonText('missing_items_array', locale),\n };\n const items: { text: string }[] = [];\n for (const row of rawItems) {\n if (typeof row === 'string') {\n const t = row.trim();\n if (t) items.push({ text: t });\n } else if (row && typeof row === 'object' && !Array.isArray(row)) {\n const t = String((row as { text?: string }).text ?? '').trim();\n if (t) items.push({ text: t });\n }\n }\n if (!items.length)\n return { items: [], parseFailed: true, errorReason: judgeReasonText('empty_checklist', locale) };\n return { items, parseFailed: false };\n } catch {\n return {\n items: [],\n parseFailed: false,\n errorReason: judgeReasonText('decompose_call_failed', locale),\n };\n } finally {\n clearTimeout(timer);\n }\n}\n\nexport type ChecklistJudgeUpdate = {\n index: number;\n status: 'completed' | 'impossible';\n evidence?: string | null;\n};\n\nexport type ChecklistEvaluateParsed = {\n verdict: 'continue' | 'done' | 'blocked' | 'needs_input';\n confidence: number;\n updates: ChecklistJudgeUpdate[];\n newItems: { text: string }[];\n reason: string;\n nextAction?: string;\n missingEvidence?: string[];\n userQuestion?: string;\n};\n\nexport type EvaluateChecklistResult = {\n parsed: ChecklistEvaluateParsed;\n parseFailed: boolean;\n};\n\nexport async function evaluateGoalChecklistJudge(opts: {\n goal: string;\n numberedChecklist: string;\n lastResponse: string;\n historyExcerpt: string;\n goalContextExcerpt?: string;\n judgeModelRef: string;\n signal?: AbortSignal;\n judgeTimeoutMs?: number;\n uiLocale?: GoalUiLocale;\n}): Promise<EvaluateChecklistResult> {\n const locale = goalUiLocaleOrFallback(opts.uiLocale);\n let model: ReturnType<typeof resolveModel>;\n try {\n model = resolveModel(opts.judgeModelRef);\n } catch {\n return {\n parsed: {\n verdict: 'continue',\n confidence: 0,\n updates: [],\n newItems: [],\n reason: judgeReasonText('judge_model_not_configured', locale),\n },\n parseFailed: false,\n };\n }\n\n const userBody =\n `Goal:\\n${truncateGoalText(opts.goal, 2000)}\\n\\n` +\n `Current checklist (1-based indices):\\n${opts.numberedChecklist}\\n\\n` +\n `Original goal context and attachments:\\n${truncateGoalText(opts.goalContextExcerpt ?? '', 4000) || '(none)'}\\n\\n` +\n `Agent's most recent response (snippet):\\n${truncateGoalText(opts.lastResponse, RESPONSE_SNIPPET)}\\n\\n` +\n `Recent conversation excerpt (JSON or text, may be truncated):\\n` +\n `${truncateGoalText(opts.historyExcerpt, HISTORY_TRUNC) || '(none)'}`;\n\n const timeoutMs =\n typeof opts.judgeTimeoutMs === 'number' && Number.isFinite(opts.judgeTimeoutMs)\n ? Math.max(5_000, Math.min(120_000, Math.floor(opts.judgeTimeoutMs)))\n : DEFAULT_JUDGE_TIMEOUT_MS;\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeoutMs);\n const merged = opts.signal ? AbortSignal.any([opts.signal, controller.signal]) : controller.signal;\n\n try {\n const user: UserMessage = {\n role: 'user',\n content: `${EVALUATE_CHECKLIST_SYSTEM}\\n\\n${userBody}` + judgeResponseLanguageNote(locale),\n timestamp: Date.now(),\n };\n const apiKey = await resolveGoalJudgeApiKey(model);\n const result = await complete(\n model,\n { messages: [user] },\n { apiKey, maxTokens: 1500, temperature: 0, signal: merged },\n );\n const errorReason = getAssistantMessageErrorReason(result);\n if (errorReason) {\n return {\n parsed: {\n verdict: 'continue',\n confidence: 0,\n updates: [],\n newItems: [],\n reason: judgeReasonText('judge_call_failed', locale),\n },\n parseFailed: false,\n };\n }\n\n const text = extractAssistantText(result.content);\n const data = extractJsonObject(text);\n if (!data) {\n return {\n parsed: {\n verdict: 'continue',\n confidence: 0,\n updates: [],\n newItems: [],\n reason: judgeReasonText('judge_reply_not_json', locale),\n },\n parseFailed: true,\n };\n }\n const updatesRaw = data.updates;\n const newRaw = data.new_items ?? data.newItems;\n const verdictRaw = String(data.verdict ?? '').trim().toLowerCase();\n const verdict =\n verdictRaw === 'done' || verdictRaw === 'blocked' || verdictRaw === 'needs_input'\n ? verdictRaw\n : 'continue';\n const confidenceRaw = Number(data.confidence);\n const confidence = Number.isFinite(confidenceRaw) ? Math.max(0, Math.min(1, confidenceRaw)) : 0;\n const nextAction = typeof data.nextAction === 'string' ? data.nextAction.trim() : undefined;\n const userQuestion = typeof data.userQuestion === 'string' ? data.userQuestion.trim() : undefined;\n const missingEvidenceRaw = data.missingEvidence ?? data.missing_evidence;\n const trimmed = typeof data.reason === 'string' ? data.reason.trim() : '';\n const inner = trimmed || JUDGE_REASON_EN.no_reason_provided;\n const reason = localizeJudgeReasonText(inner, locale);\n const updates: ChecklistJudgeUpdate[] = [];\n if (Array.isArray(updatesRaw)) {\n for (const u of updatesRaw) {\n if (!u || typeof u !== 'object' || Array.isArray(u)) continue;\n const rec = u as Record<string, unknown>;\n const idx1 = Number(rec.index);\n if (!Number.isFinite(idx1)) continue;\n const idx0 = Math.floor(idx1) - 1;\n const st = String(rec.status ?? '').trim().toLowerCase();\n if (st !== 'completed' && st !== 'impossible') continue;\n const evidence = typeof rec.evidence === 'string' ? rec.evidence : null;\n updates.push({ index: idx0, status: st, evidence });\n }\n }\n const newItems: { text: string }[] = [];\n if (Array.isArray(newRaw)) {\n for (const row of newRaw) {\n if (typeof row === 'string') {\n const t = row.trim();\n if (t) newItems.push({ text: t });\n } else if (row && typeof row === 'object' && !Array.isArray(row)) {\n const t = String((row as { text?: string }).text ?? '').trim();\n if (t) newItems.push({ text: t });\n }\n }\n }\n const missingEvidence: string[] = [];\n if (Array.isArray(missingEvidenceRaw)) {\n for (const item of missingEvidenceRaw) {\n if (typeof item === 'string' && item.trim()) missingEvidence.push(item.trim());\n }\n }\n return {\n parsed: {\n verdict,\n confidence,\n updates,\n newItems,\n reason: reason || judgeReasonText('no_reason_provided', locale),\n ...(nextAction ? { nextAction } : {}),\n ...(missingEvidence.length ? { missingEvidence } : {}),\n ...(userQuestion ? { userQuestion } : {}),\n },\n parseFailed: false,\n };\n } catch {\n return {\n parsed: {\n verdict: 'continue',\n confidence: 0,\n updates: [],\n newItems: [],\n reason: judgeReasonText('judge_call_failed', locale),\n },\n parseFailed: false,\n };\n } finally {\n clearTimeout(timer);\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"checklist-judge.js","names":[],"sources":["../../../../src/agent/goals/checklist-judge.ts"],"sourcesContent":["import type { UserMessage } from '@earendil-works/pi-ai';\n\nimport { resolveModel } from '../../providers/index.js';\nimport { completeWithResolvedCredentials } from '../../providers/model-call.js';\n\nimport {\n DEFAULT_JUDGE_TIMEOUT_MS,\n extractAssistantText,\n getAssistantMessageErrorReason,\n resolveGoalJudgeApiKey,\n stripCodeFences,\n truncateGoalText,\n} from './judge.js';\nimport type { GoalUiLocale } from './goal-locale.js';\nimport {\n goalUiLocaleOrFallback,\n judgeReasonText,\n judgeResponseLanguageNote,\n JUDGE_REASON_EN,\n localizeJudgeReasonText,\n} from './goal-locale.js';\n\nconst GOAL_TRUNC = 4000;\nconst RESPONSE_SNIPPET = 4000;\nconst HISTORY_TRUNC = 24_000;\n\nconst DECOMPOSE_SYSTEM = (\n 'You break a user goal into an EXTREMELY detailed checklist of concrete, verifiable completion criteria. ' +\n 'Bias toward more items. Each item is one factual statement about finished work.\\n\\n' +\n 'If existing acceptance criteria are provided, return only missing supplemental criteria. ' +\n 'Do not repeat, rewrite, or replace existing criteria.\\n\\n' +\n 'Reply ONLY with a single JSON object on one line:\\n' +\n '{\"items\":[{\"text\":\"...\"},...]}\\n' +\n 'Use at least 3 items when the goal warrants it.'\n);\n\nconst DECOMPOSE_USER = 'Goal:\\n{goal}\\n\\nReturn JSON only.';\n\nconst EVALUATE_CHECKLIST_SYSTEM = (\n 'You evaluate an autonomous agent\\'s progress on a goal with a numbered checklist.\\n' +\n 'For each pending item, decide if evidence in the agent snippet and/or history excerpt shows it is satisfied.\\n' +\n 'Flip pending→completed only with clear evidence; pending→impossible only if truly unachievable here.\\n' +\n 'Do not regress completed/impossible items — omit them from updates.\\n\\n' +\n 'Reply ONLY with one JSON object on one line:\\n' +\n '{\"verdict\":\"continue|done|blocked|needs_input\",\"confidence\":0.0,\"reason\":\"...\",' +\n '\"nextAction\":\"...\",\"missingEvidence\":[\"...\"],\"userQuestion\":\"...\",' +\n '\"updates\":[{\"index\":1,\"status\":\"completed|impossible\",\"evidence\":\"short citation\"}],' +\n '\"new_items\":[{\"text\":\"...\"}]}\\n' +\n 'Use verdict=done only when all checklist criteria are complete/impossible. ' +\n 'Use needs_input when the next step requires user input. Use blocked when external progress is impossible.'\n);\n\nfunction extractJsonObject(raw: string): Record<string, unknown> | null {\n if (!raw?.trim()) return null;\n const text = stripCodeFences(raw);\n try {\n const data = JSON.parse(text) as unknown;\n return data && typeof data === 'object' && !Array.isArray(data) ? (data as Record<string, unknown>) : null;\n } catch {\n const start = text.indexOf('{');\n const end = text.lastIndexOf('}');\n if (start === -1 || end <= start) return null;\n try {\n const data = JSON.parse(text.slice(start, end + 1)) as unknown;\n return data && typeof data === 'object' && !Array.isArray(data) ? (data as Record<string, unknown>) : null;\n } catch {\n return null;\n }\n }\n}\n\nexport type DecomposeChecklistResult = {\n items: { text: string }[];\n parseFailed: boolean;\n errorReason?: string;\n};\n\nexport async function decomposeGoalChecklist(opts: {\n goal: string;\n existingChecklist?: string;\n judgeModelRef: string;\n signal?: AbortSignal;\n judgeTimeoutMs?: number;\n uiLocale?: GoalUiLocale;\n}): Promise<DecomposeChecklistResult> {\n const locale = goalUiLocaleOrFallback(opts.uiLocale);\n const goal = opts.goal.trim();\n if (!goal)\n return { items: [], parseFailed: false, errorReason: judgeReasonText('empty_goal', locale) };\n\n let model: ReturnType<typeof resolveModel>;\n try {\n model = resolveModel(opts.judgeModelRef);\n } catch {\n return {\n items: [],\n parseFailed: false,\n errorReason: judgeReasonText('judge_model_not_configured', locale),\n };\n }\n\n const timeoutMs =\n typeof opts.judgeTimeoutMs === 'number' && Number.isFinite(opts.judgeTimeoutMs)\n ? Math.max(5_000, Math.min(120_000, Math.floor(opts.judgeTimeoutMs)))\n : DEFAULT_JUDGE_TIMEOUT_MS;\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeoutMs);\n const merged = opts.signal ? AbortSignal.any([opts.signal, controller.signal]) : controller.signal;\n\n try {\n const existingChecklist = opts.existingChecklist?.trim();\n const decomposeUser = DECOMPOSE_USER.replace('{goal}', truncateGoalText(goal, GOAL_TRUNC)) +\n (existingChecklist\n ? `\\n\\nExisting acceptance criteria:\\n${truncateGoalText(existingChecklist, GOAL_TRUNC)}\\n\\nReturn only missing supplemental criteria as JSON.`\n : '');\n const user: UserMessage = {\n role: 'user',\n content:\n `${DECOMPOSE_SYSTEM}\\n\\n${decomposeUser}` +\n judgeResponseLanguageNote(locale),\n timestamp: Date.now(),\n };\n const apiKey = await resolveGoalJudgeApiKey(model);\n const result = await completeWithResolvedCredentials(\n model,\n { messages: [user] },\n { apiKey, maxTokens: 2000, temperature: 0, signal: merged },\n );\n const errorReason = getAssistantMessageErrorReason(result);\n if (errorReason) {\n return {\n items: [],\n parseFailed: false,\n errorReason: judgeReasonText('decompose_call_failed', locale),\n };\n }\n\n const text = extractAssistantText(result.content);\n const data = extractJsonObject(text);\n if (!data)\n return {\n items: [],\n parseFailed: true,\n errorReason: judgeReasonText('decompose_reply_not_json', locale),\n };\n const rawItems = data.items ?? data.checklist;\n if (!Array.isArray(rawItems))\n return {\n items: [],\n parseFailed: true,\n errorReason: judgeReasonText('missing_items_array', locale),\n };\n const items: { text: string }[] = [];\n for (const row of rawItems) {\n if (typeof row === 'string') {\n const t = row.trim();\n if (t) items.push({ text: t });\n } else if (row && typeof row === 'object' && !Array.isArray(row)) {\n const t = String((row as { text?: string }).text ?? '').trim();\n if (t) items.push({ text: t });\n }\n }\n if (!items.length)\n return { items: [], parseFailed: true, errorReason: judgeReasonText('empty_checklist', locale) };\n return { items, parseFailed: false };\n } catch {\n return {\n items: [],\n parseFailed: false,\n errorReason: judgeReasonText('decompose_call_failed', locale),\n };\n } finally {\n clearTimeout(timer);\n }\n}\n\nexport type ChecklistJudgeUpdate = {\n index: number;\n status: 'completed' | 'impossible';\n evidence?: string | null;\n};\n\nexport type ChecklistEvaluateParsed = {\n verdict: 'continue' | 'done' | 'blocked' | 'needs_input';\n confidence: number;\n updates: ChecklistJudgeUpdate[];\n newItems: { text: string }[];\n reason: string;\n nextAction?: string;\n missingEvidence?: string[];\n userQuestion?: string;\n};\n\nexport type EvaluateChecklistResult = {\n parsed: ChecklistEvaluateParsed;\n parseFailed: boolean;\n};\n\nexport async function evaluateGoalChecklistJudge(opts: {\n goal: string;\n numberedChecklist: string;\n lastResponse: string;\n historyExcerpt: string;\n goalContextExcerpt?: string;\n judgeModelRef: string;\n signal?: AbortSignal;\n judgeTimeoutMs?: number;\n uiLocale?: GoalUiLocale;\n}): Promise<EvaluateChecklistResult> {\n const locale = goalUiLocaleOrFallback(opts.uiLocale);\n let model: ReturnType<typeof resolveModel>;\n try {\n model = resolveModel(opts.judgeModelRef);\n } catch {\n return {\n parsed: {\n verdict: 'continue',\n confidence: 0,\n updates: [],\n newItems: [],\n reason: judgeReasonText('judge_model_not_configured', locale),\n },\n parseFailed: false,\n };\n }\n\n const userBody =\n `Goal:\\n${truncateGoalText(opts.goal, 2000)}\\n\\n` +\n `Current checklist (1-based indices):\\n${opts.numberedChecklist}\\n\\n` +\n `Original goal context and attachments:\\n${truncateGoalText(opts.goalContextExcerpt ?? '', 4000) || '(none)'}\\n\\n` +\n `Agent's most recent response (snippet):\\n${truncateGoalText(opts.lastResponse, RESPONSE_SNIPPET)}\\n\\n` +\n `Recent conversation excerpt (JSON or text, may be truncated):\\n` +\n `${truncateGoalText(opts.historyExcerpt, HISTORY_TRUNC) || '(none)'}`;\n\n const timeoutMs =\n typeof opts.judgeTimeoutMs === 'number' && Number.isFinite(opts.judgeTimeoutMs)\n ? Math.max(5_000, Math.min(120_000, Math.floor(opts.judgeTimeoutMs)))\n : DEFAULT_JUDGE_TIMEOUT_MS;\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeoutMs);\n const merged = opts.signal ? AbortSignal.any([opts.signal, controller.signal]) : controller.signal;\n\n try {\n const user: UserMessage = {\n role: 'user',\n content: `${EVALUATE_CHECKLIST_SYSTEM}\\n\\n${userBody}` + judgeResponseLanguageNote(locale),\n timestamp: Date.now(),\n };\n const apiKey = await resolveGoalJudgeApiKey(model);\n const result = await completeWithResolvedCredentials(\n model,\n { messages: [user] },\n { apiKey, maxTokens: 1500, temperature: 0, signal: merged },\n );\n const errorReason = getAssistantMessageErrorReason(result);\n if (errorReason) {\n return {\n parsed: {\n verdict: 'continue',\n confidence: 0,\n updates: [],\n newItems: [],\n reason: judgeReasonText('judge_call_failed', locale),\n },\n parseFailed: false,\n };\n }\n\n const text = extractAssistantText(result.content);\n const data = extractJsonObject(text);\n if (!data) {\n return {\n parsed: {\n verdict: 'continue',\n confidence: 0,\n updates: [],\n newItems: [],\n reason: judgeReasonText('judge_reply_not_json', locale),\n },\n parseFailed: true,\n };\n }\n const updatesRaw = data.updates;\n const newRaw = data.new_items ?? data.newItems;\n const verdictRaw = String(data.verdict ?? '').trim().toLowerCase();\n const verdict =\n verdictRaw === 'done' || verdictRaw === 'blocked' || verdictRaw === 'needs_input'\n ? verdictRaw\n : 'continue';\n const confidenceRaw = Number(data.confidence);\n const confidence = Number.isFinite(confidenceRaw) ? Math.max(0, Math.min(1, confidenceRaw)) : 0;\n const nextAction = typeof data.nextAction === 'string' ? data.nextAction.trim() : undefined;\n const userQuestion = typeof data.userQuestion === 'string' ? data.userQuestion.trim() : undefined;\n const missingEvidenceRaw = data.missingEvidence ?? data.missing_evidence;\n const trimmed = typeof data.reason === 'string' ? data.reason.trim() : '';\n const inner = trimmed || JUDGE_REASON_EN.no_reason_provided;\n const reason = localizeJudgeReasonText(inner, locale);\n const updates: ChecklistJudgeUpdate[] = [];\n if (Array.isArray(updatesRaw)) {\n for (const u of updatesRaw) {\n if (!u || typeof u !== 'object' || Array.isArray(u)) continue;\n const rec = u as Record<string, unknown>;\n const idx1 = Number(rec.index);\n if (!Number.isFinite(idx1)) continue;\n const idx0 = Math.floor(idx1) - 1;\n const st = String(rec.status ?? '').trim().toLowerCase();\n if (st !== 'completed' && st !== 'impossible') continue;\n const evidence = typeof rec.evidence === 'string' ? rec.evidence : null;\n updates.push({ index: idx0, status: st, evidence });\n }\n }\n const newItems: { text: string }[] = [];\n if (Array.isArray(newRaw)) {\n for (const row of newRaw) {\n if (typeof row === 'string') {\n const t = row.trim();\n if (t) newItems.push({ text: t });\n } else if (row && typeof row === 'object' && !Array.isArray(row)) {\n const t = String((row as { text?: string }).text ?? '').trim();\n if (t) newItems.push({ text: t });\n }\n }\n }\n const missingEvidence: string[] = [];\n if (Array.isArray(missingEvidenceRaw)) {\n for (const item of missingEvidenceRaw) {\n if (typeof item === 'string' && item.trim()) missingEvidence.push(item.trim());\n }\n }\n return {\n parsed: {\n verdict,\n confidence,\n updates,\n newItems,\n reason: reason || judgeReasonText('no_reason_provided', locale),\n ...(nextAction ? { nextAction } : {}),\n ...(missingEvidence.length ? { missingEvidence } : {}),\n ...(userQuestion ? { userQuestion } : {}),\n },\n parseFailed: false,\n };\n } catch {\n return {\n parsed: {\n verdict: 'continue',\n confidence: 0,\n updates: [],\n newItems: [],\n reason: judgeReasonText('judge_call_failed', locale),\n },\n parseFailed: false,\n };\n } finally {\n clearTimeout(timer);\n }\n}\n"],"mappings":"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"}
@@ -1,7 +1,7 @@
1
1
  import { getApiKey, init_providers, resolveModel } from "../../providers/index.js";
2
+ import { completeWithResolvedCredentials } from "../../providers/model-call.js";
2
3
  import { JUDGE_REASON_EN } from "../../i18n/goals-bundle.js";
3
4
  import { goalUiLocaleOrFallback, judgeFreeformBuiltinMessages, judgeResponseLanguageNote, localizeJudgeReasonText } from "./goal-locale.js";
4
- import { complete } from "@earendil-works/pi-ai/compat";
5
5
  //#region src/agent/goals/judge.ts
6
6
  init_providers();
7
7
  const JUDGE_RESPONSE_SNIPPET_CHARS = 4e3;
@@ -144,7 +144,7 @@ async function judgeGoalHermesStyle(goal, lastResponse, judgeModelRef, signal, o
144
144
  timestamp: Date.now()
145
145
  };
146
146
  const apiKey = await resolveGoalJudgeApiKey(model);
147
- const result = await complete(model, { messages: [combinedUser] }, {
147
+ const result = await completeWithResolvedCredentials(model, { messages: [combinedUser] }, {
148
148
  apiKey,
149
149
  maxTokens: 200,
150
150
  temperature: 0,
@@ -1 +1 @@
1
- {"version":3,"file":"judge.js","names":[],"sources":["../../../../src/agent/goals/judge.ts"],"sourcesContent":["import type { UserMessage } from '@earendil-works/pi-ai';\nimport { complete } from '@earendil-works/pi-ai/compat';\n\nimport { getApiKey, resolveModel } from '../../providers/index.js';\n\nimport type { GoalUiLocale } from './goal-locale.js';\nimport {\n goalUiLocaleOrFallback,\n judgeFreeformBuiltinMessages,\n JUDGE_REASON_EN,\n judgeResponseLanguageNote,\n localizeJudgeReasonText,\n} from './goal-locale.js';\n\nconst JUDGE_RESPONSE_SNIPPET_CHARS = 4000;\n\n/**\n * Extract visible text from a pi-ai `AssistantMessage.content` array.\n * Handles `TextContent` (`type: 'text'`) and falls back to `ThinkingContent`\n * (`type: 'thinking'`) when the text blocks are empty — reasoning models\n * (DeepSeek-R1, Qwen-thinking, etc.) may place the entire response inside\n * thinking blocks, leaving the text portion blank.\n */\nexport function extractAssistantText(content: unknown): string {\n if (!Array.isArray(content)) return '';\n\n let textParts = '';\n let thinkingParts = '';\n\n for (const block of content) {\n if (!block || typeof block !== 'object') continue;\n const typed = block as Record<string, unknown>;\n const blockType = typed.type;\n if (blockType === 'text' && typeof typed.text === 'string') {\n textParts += typed.text;\n } else if (blockType === 'thinking') {\n const thinking =\n typeof typed.thinking === 'string'\n ? typed.thinking\n : typeof typed.text === 'string'\n ? typed.text\n : '';\n thinkingParts += thinking;\n }\n }\n\n // Prefer text blocks; fall back to thinking blocks when text is empty.\n return textParts.trim() ? textParts : thinkingParts;\n}\n\n/**\n * Strip markdown code fences (opening AND closing) from raw model output.\n * Handles `` ```json ``, `` ``` ``, and trailing `` ``` `` with optional whitespace.\n */\nexport function stripCodeFences(raw: string): string {\n let text = raw.trim();\n\n // Remove opening code fence: ```<optional-lang>\\n\n const openMatch = text.match(/^`{3,}[^\\n]*\\n?/);\n if (openMatch) {\n text = text.slice(openMatch[0].length);\n }\n\n // Remove closing code fence: \\n```<optional-whitespace>\n const closeMatch = text.match(/\\n?`{3,}\\s*$/);\n if (closeMatch) {\n text = text.slice(0, -closeMatch[0].length);\n }\n\n return text.trim();\n}\n\n/** Mirrors `hermes_cli/goals.py` — strict judge, JSON-only reply. */\nexport const JUDGE_SYSTEM_PROMPT =\n 'You are a strict judge evaluating whether an autonomous agent has ' +\n \"achieved a user's stated goal. You receive the goal text and the \" +\n \"agent's most recent response. Your only job is to decide whether \" +\n 'the goal is fully satisfied based on that response.\\n\\n' +\n 'A goal is DONE only when:\\n' +\n '- The response explicitly confirms the goal was completed, OR\\n' +\n '- The response clearly shows the final deliverable was produced, OR\\n' +\n '- The response explains the goal is unachievable / blocked / needs ' +\n 'user input (treat this as DONE with reason describing the block).\\n\\n' +\n 'Otherwise the goal is NOT done — CONTINUE.\\n\\n' +\n 'Reply ONLY with a single JSON object on one line:\\n' +\n '{\"done\": <true|false>, \"reason\": \"<one-sentence rationale>\"}';\n\nexport const JUDGE_USER_PROMPT_TEMPLATE =\n 'Goal:\\n{goal}\\n\\n' + \"Agent's most recent response:\\n{response}\\n\\n\" + 'Is the goal satisfied?';\n\nexport const DEFAULT_JUDGE_TIMEOUT_MS = 60_000;\n\nexport function truncateGoalText(text: string, limit: number): string {\n if (!text) return '';\n if (text.length <= limit) return text;\n return text.slice(0, limit) + '… [truncated]';\n}\n\nexport async function resolveGoalJudgeApiKey(\n model: ReturnType<typeof resolveModel>,\n): Promise<string | undefined> {\n try {\n return await getApiKey(model.provider);\n } catch {\n return undefined;\n }\n}\n\nexport function getAssistantMessageErrorReason(message: unknown): string | null {\n if (!message || typeof message !== 'object') return null;\n\n const record = message as Record<string, unknown>;\n const stopReason = record.stopReason;\n const errorMessage = typeof record.errorMessage === 'string' ? record.errorMessage.trim() : '';\n\n if (stopReason === 'error') {\n return errorMessage || 'Judge model call failed.';\n }\n\n return null;\n}\n\n/** Parse judge JSON — fail-open to **continue** (Hermes semantics). */\nexport function parseJudgeResponseFailOpen(raw: string): {\n done: boolean;\n reason: string;\n parseFailed: boolean;\n} {\n if (!raw?.trim()) {\n return { done: false, reason: JUDGE_REASON_EN.judge_returned_empty, parseFailed: true };\n }\n\n const text = stripCodeFences(raw);\n\n let data: Record<string, unknown> | null = null;\n try {\n data = JSON.parse(text) as Record<string, unknown>;\n } catch {\n const start = text.indexOf('{');\n const end = text.lastIndexOf('}');\n if (start !== -1 && end > start) {\n try {\n data = JSON.parse(text.slice(start, end + 1)) as Record<string, unknown>;\n } catch {\n data = null;\n }\n }\n }\n\n if (!data || typeof data !== 'object') {\n return { done: false, reason: JUDGE_REASON_EN.judge_reply_not_json, parseFailed: true };\n }\n\n const doneVal = data.done;\n let done: boolean;\n if (typeof doneVal === 'string') {\n done = ['true', 'yes', '1', 'done'].includes(doneVal.trim().toLowerCase());\n } else {\n done = Boolean(doneVal);\n }\n const reason = typeof data.reason === 'string' ? data.reason.trim() : '';\n return { done, reason: reason || JUDGE_REASON_EN.no_reason_provided, parseFailed: false };\n}\n\nexport type GoalJudgeVerdict = 'done' | 'continue' | 'skipped';\n\n/**\n * Ask the configured model whether the goal is satisfied.\n * Fail-open: any error → `continue` (Hermes — turn budget is the backstop).\n */\nexport async function judgeGoalHermesStyle(\n goal: string,\n lastResponse: string,\n judgeModelRef: string,\n signal?: AbortSignal,\n opts?: { judgeTimeoutMs?: number; uiLocale?: GoalUiLocale },\n): Promise<{ verdict: GoalJudgeVerdict; reason: string; parseFailed: boolean }> {\n const locale = goalUiLocaleOrFallback(opts?.uiLocale);\n const b = judgeFreeformBuiltinMessages(locale);\n\n if (!goal.trim()) {\n return { verdict: 'skipped', reason: b.emptyGoal, parseFailed: false };\n }\n if (!lastResponse.trim()) {\n return { verdict: 'continue', reason: b.emptyResponse, parseFailed: false };\n }\n\n let model: ReturnType<typeof resolveModel>;\n try {\n model = resolveModel(judgeModelRef);\n } catch {\n return { verdict: 'continue', reason: b.noModel, parseFailed: false };\n }\n\n const userContent =\n JUDGE_USER_PROMPT_TEMPLATE.replace('{goal}', truncateGoalText(goal, 2000)).replace(\n '{response}',\n truncateGoalText(lastResponse, JUDGE_RESPONSE_SNIPPET_CHARS),\n ) + judgeResponseLanguageNote(locale);\n\n const timeoutMs =\n typeof opts?.judgeTimeoutMs === 'number' && Number.isFinite(opts.judgeTimeoutMs)\n ? Math.max(5_000, Math.min(120_000, Math.floor(opts.judgeTimeoutMs)))\n : DEFAULT_JUDGE_TIMEOUT_MS;\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeoutMs);\n const merged = signal ? AbortSignal.any([signal, controller.signal]) : controller.signal;\n\n try {\n const combinedUser: UserMessage = {\n role: 'user',\n content: `${JUDGE_SYSTEM_PROMPT}\\n\\n${userContent}`,\n timestamp: Date.now(),\n };\n\n const apiKey = await resolveGoalJudgeApiKey(model);\n const result = await complete(\n model,\n {\n messages: [combinedUser],\n },\n { apiKey, maxTokens: 200, temperature: 0, signal: merged },\n );\n const errorReason = getAssistantMessageErrorReason(result);\n if (errorReason) {\n return { verdict: 'continue', reason: b.callFailed, parseFailed: false };\n }\n\n const text = extractAssistantText(result.content);\n const { done, reason, parseFailed } = parseJudgeResponseFailOpen(text);\n const reasonOut = localizeJudgeReasonText(reason, locale);\n return { verdict: done ? 'done' : 'continue', reason: reasonOut, parseFailed };\n } catch {\n return { verdict: 'continue', reason: b.callFailed, parseFailed: false };\n } finally {\n clearTimeout(timer);\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"judge.js","names":[],"sources":["../../../../src/agent/goals/judge.ts"],"sourcesContent":["import type { UserMessage } from '@earendil-works/pi-ai';\n\nimport { getApiKey, resolveModel } from '../../providers/index.js';\nimport { completeWithResolvedCredentials } from '../../providers/model-call.js';\n\nimport type { GoalUiLocale } from './goal-locale.js';\nimport {\n goalUiLocaleOrFallback,\n judgeFreeformBuiltinMessages,\n JUDGE_REASON_EN,\n judgeResponseLanguageNote,\n localizeJudgeReasonText,\n} from './goal-locale.js';\n\nconst JUDGE_RESPONSE_SNIPPET_CHARS = 4000;\n\n/**\n * Extract visible text from a pi-ai `AssistantMessage.content` array.\n * Handles `TextContent` (`type: 'text'`) and falls back to `ThinkingContent`\n * (`type: 'thinking'`) when the text blocks are empty — reasoning models\n * (DeepSeek-R1, Qwen-thinking, etc.) may place the entire response inside\n * thinking blocks, leaving the text portion blank.\n */\nexport function extractAssistantText(content: unknown): string {\n if (!Array.isArray(content)) return '';\n\n let textParts = '';\n let thinkingParts = '';\n\n for (const block of content) {\n if (!block || typeof block !== 'object') continue;\n const typed = block as Record<string, unknown>;\n const blockType = typed.type;\n if (blockType === 'text' && typeof typed.text === 'string') {\n textParts += typed.text;\n } else if (blockType === 'thinking') {\n const thinking =\n typeof typed.thinking === 'string'\n ? typed.thinking\n : typeof typed.text === 'string'\n ? typed.text\n : '';\n thinkingParts += thinking;\n }\n }\n\n // Prefer text blocks; fall back to thinking blocks when text is empty.\n return textParts.trim() ? textParts : thinkingParts;\n}\n\n/**\n * Strip markdown code fences (opening AND closing) from raw model output.\n * Handles `` ```json ``, `` ``` ``, and trailing `` ``` `` with optional whitespace.\n */\nexport function stripCodeFences(raw: string): string {\n let text = raw.trim();\n\n // Remove opening code fence: ```<optional-lang>\\n\n const openMatch = text.match(/^`{3,}[^\\n]*\\n?/);\n if (openMatch) {\n text = text.slice(openMatch[0].length);\n }\n\n // Remove closing code fence: \\n```<optional-whitespace>\n const closeMatch = text.match(/\\n?`{3,}\\s*$/);\n if (closeMatch) {\n text = text.slice(0, -closeMatch[0].length);\n }\n\n return text.trim();\n}\n\n/** Mirrors `hermes_cli/goals.py` — strict judge, JSON-only reply. */\nexport const JUDGE_SYSTEM_PROMPT =\n 'You are a strict judge evaluating whether an autonomous agent has ' +\n \"achieved a user's stated goal. You receive the goal text and the \" +\n \"agent's most recent response. Your only job is to decide whether \" +\n 'the goal is fully satisfied based on that response.\\n\\n' +\n 'A goal is DONE only when:\\n' +\n '- The response explicitly confirms the goal was completed, OR\\n' +\n '- The response clearly shows the final deliverable was produced, OR\\n' +\n '- The response explains the goal is unachievable / blocked / needs ' +\n 'user input (treat this as DONE with reason describing the block).\\n\\n' +\n 'Otherwise the goal is NOT done — CONTINUE.\\n\\n' +\n 'Reply ONLY with a single JSON object on one line:\\n' +\n '{\"done\": <true|false>, \"reason\": \"<one-sentence rationale>\"}';\n\nexport const JUDGE_USER_PROMPT_TEMPLATE =\n 'Goal:\\n{goal}\\n\\n' + \"Agent's most recent response:\\n{response}\\n\\n\" + 'Is the goal satisfied?';\n\nexport const DEFAULT_JUDGE_TIMEOUT_MS = 60_000;\n\nexport function truncateGoalText(text: string, limit: number): string {\n if (!text) return '';\n if (text.length <= limit) return text;\n return text.slice(0, limit) + '… [truncated]';\n}\n\nexport async function resolveGoalJudgeApiKey(\n model: ReturnType<typeof resolveModel>,\n): Promise<string | undefined> {\n try {\n return await getApiKey(model.provider);\n } catch {\n return undefined;\n }\n}\n\nexport function getAssistantMessageErrorReason(message: unknown): string | null {\n if (!message || typeof message !== 'object') return null;\n\n const record = message as Record<string, unknown>;\n const stopReason = record.stopReason;\n const errorMessage = typeof record.errorMessage === 'string' ? record.errorMessage.trim() : '';\n\n if (stopReason === 'error') {\n return errorMessage || 'Judge model call failed.';\n }\n\n return null;\n}\n\n/** Parse judge JSON — fail-open to **continue** (Hermes semantics). */\nexport function parseJudgeResponseFailOpen(raw: string): {\n done: boolean;\n reason: string;\n parseFailed: boolean;\n} {\n if (!raw?.trim()) {\n return { done: false, reason: JUDGE_REASON_EN.judge_returned_empty, parseFailed: true };\n }\n\n const text = stripCodeFences(raw);\n\n let data: Record<string, unknown> | null = null;\n try {\n data = JSON.parse(text) as Record<string, unknown>;\n } catch {\n const start = text.indexOf('{');\n const end = text.lastIndexOf('}');\n if (start !== -1 && end > start) {\n try {\n data = JSON.parse(text.slice(start, end + 1)) as Record<string, unknown>;\n } catch {\n data = null;\n }\n }\n }\n\n if (!data || typeof data !== 'object') {\n return { done: false, reason: JUDGE_REASON_EN.judge_reply_not_json, parseFailed: true };\n }\n\n const doneVal = data.done;\n let done: boolean;\n if (typeof doneVal === 'string') {\n done = ['true', 'yes', '1', 'done'].includes(doneVal.trim().toLowerCase());\n } else {\n done = Boolean(doneVal);\n }\n const reason = typeof data.reason === 'string' ? data.reason.trim() : '';\n return { done, reason: reason || JUDGE_REASON_EN.no_reason_provided, parseFailed: false };\n}\n\nexport type GoalJudgeVerdict = 'done' | 'continue' | 'skipped';\n\n/**\n * Ask the configured model whether the goal is satisfied.\n * Fail-open: any error → `continue` (Hermes — turn budget is the backstop).\n */\nexport async function judgeGoalHermesStyle(\n goal: string,\n lastResponse: string,\n judgeModelRef: string,\n signal?: AbortSignal,\n opts?: { judgeTimeoutMs?: number; uiLocale?: GoalUiLocale },\n): Promise<{ verdict: GoalJudgeVerdict; reason: string; parseFailed: boolean }> {\n const locale = goalUiLocaleOrFallback(opts?.uiLocale);\n const b = judgeFreeformBuiltinMessages(locale);\n\n if (!goal.trim()) {\n return { verdict: 'skipped', reason: b.emptyGoal, parseFailed: false };\n }\n if (!lastResponse.trim()) {\n return { verdict: 'continue', reason: b.emptyResponse, parseFailed: false };\n }\n\n let model: ReturnType<typeof resolveModel>;\n try {\n model = resolveModel(judgeModelRef);\n } catch {\n return { verdict: 'continue', reason: b.noModel, parseFailed: false };\n }\n\n const userContent =\n JUDGE_USER_PROMPT_TEMPLATE.replace('{goal}', truncateGoalText(goal, 2000)).replace(\n '{response}',\n truncateGoalText(lastResponse, JUDGE_RESPONSE_SNIPPET_CHARS),\n ) + judgeResponseLanguageNote(locale);\n\n const timeoutMs =\n typeof opts?.judgeTimeoutMs === 'number' && Number.isFinite(opts.judgeTimeoutMs)\n ? Math.max(5_000, Math.min(120_000, Math.floor(opts.judgeTimeoutMs)))\n : DEFAULT_JUDGE_TIMEOUT_MS;\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeoutMs);\n const merged = signal ? AbortSignal.any([signal, controller.signal]) : controller.signal;\n\n try {\n const combinedUser: UserMessage = {\n role: 'user',\n content: `${JUDGE_SYSTEM_PROMPT}\\n\\n${userContent}`,\n timestamp: Date.now(),\n };\n\n const apiKey = await resolveGoalJudgeApiKey(model);\n const result = await completeWithResolvedCredentials(\n model,\n {\n messages: [combinedUser],\n },\n { apiKey, maxTokens: 200, temperature: 0, signal: merged },\n );\n const errorReason = getAssistantMessageErrorReason(result);\n if (errorReason) {\n return { verdict: 'continue', reason: b.callFailed, parseFailed: false };\n }\n\n const text = extractAssistantText(result.content);\n const { done, reason, parseFailed } = parseJudgeResponseFailOpen(text);\n const reasonOut = localizeJudgeReasonText(reason, locale);\n return { verdict: done ? 'done' : 'continue', reason: reasonOut, parseFailed };\n } catch {\n return { verdict: 'continue', reason: b.callFailed, parseFailed: false };\n } finally {\n clearTimeout(timer);\n }\n}\n"],"mappings":"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"}
@@ -1,7 +1,7 @@
1
1
  import { getApiKey, getModelsByProvider, init_providers, resolveModel } from "../../../providers/index.js";
2
+ import { completeWithResolvedCredentials } from "../../../providers/model-call.js";
2
3
  import { registerImageUnderstandingProvider, registerImageUnderstandingProviderFactory } from "./provider-registry.js";
3
4
  import { coerceImageAssistantText } from "../image-helpers.js";
4
- import { complete } from "@earendil-works/pi-ai/compat";
5
5
  //#region src/agent/image/understanding/pi-ai-provider.ts
6
6
  init_providers();
7
7
  function resolveMaxTokens(modelMaxTokens, requestedMaxTokens = 4096) {
@@ -39,7 +39,7 @@ function buildPiAiImageUnderstandingProvider(providerId) {
39
39
  const timeoutSignal = typeof AbortSignal !== "undefined" && typeof AbortSignal.timeout === "function" ? AbortSignal.timeout(timeoutMs) : void 0;
40
40
  return {
41
41
  text: coerceImageAssistantText({
42
- message: await complete(model, context, {
42
+ message: await completeWithResolvedCredentials(model, context, {
43
43
  apiKey,
44
44
  maxTokens,
45
45
  signal: (() => {
@@ -1 +1 @@
1
- {"version":3,"file":"pi-ai-provider.js","names":[],"sources":["../../../../../src/agent/image/understanding/pi-ai-provider.ts"],"sourcesContent":["import { complete, type Api, type Context, type Model } from '@earendil-works/pi-ai/compat';\nimport { resolveModel, getApiKey, getModelsByProvider } from '../../../providers/index.js';\nimport { coerceImageAssistantText } from '../image-helpers.js';\nimport {\n registerImageUnderstandingProvider,\n registerImageUnderstandingProviderFactory,\n} from './provider-registry.js';\nimport type { ImageUnderstandingProvider } from './types.js';\n\nfunction resolveMaxTokens(modelMaxTokens: number | undefined, requestedMaxTokens = 4096): number {\n if (\n typeof modelMaxTokens !== 'number' ||\n !Number.isFinite(modelMaxTokens) ||\n modelMaxTokens <= 0\n ) {\n return requestedMaxTokens;\n }\n return Math.min(requestedMaxTokens, modelMaxTokens);\n}\n\nexport function buildPiAiImageUnderstandingProvider(providerId: string): ImageUnderstandingProvider {\n return {\n id: providerId,\n label: `pi-ai (${providerId})`,\n async isConfigured() {\n const apiKey = await getApiKey(providerId);\n return Boolean(apiKey);\n },\n async describeImages(modelId, request) {\n const modelRef = `${providerId}/${modelId}`;\n const model = resolveModel(modelRef) as Model<Api>;\n if (!model.input?.includes('image')) {\n throw new Error(`Model does not support images: ${modelRef}`);\n }\n const apiKey = await getApiKey(providerId);\n if (!apiKey) {\n throw new Error(`No API key configured for provider: ${providerId}`);\n }\n\n const context: Context = {\n messages: [\n {\n role: 'user',\n content: [\n { type: 'text', text: request.prompt },\n ...request.images.map((img) => ({\n type: 'image' as const,\n data: img.buffer.toString('base64'),\n mimeType: img.mimeType || 'image/jpeg',\n })),\n ],\n timestamp: Date.now(),\n },\n ],\n };\n\n const maxTokens = resolveMaxTokens(model.maxTokens, request.maxTokens ?? 512);\n const timeoutMs = request.timeoutMs ?? 60_000;\n const timeoutSignal =\n typeof AbortSignal !== 'undefined' && typeof AbortSignal.timeout === 'function'\n ? AbortSignal.timeout(timeoutMs)\n : undefined;\n const signal = (() => {\n if (timeoutSignal && request.signal && typeof AbortSignal.any === 'function') {\n return AbortSignal.any([request.signal, timeoutSignal]);\n }\n if (request.signal) {\n return request.signal;\n }\n if (timeoutSignal) {\n return timeoutSignal;\n }\n const controller = new AbortController();\n setTimeout(() => controller.abort(), timeoutMs);\n return controller.signal;\n })();\n\n const message = await complete(model, context, {\n apiKey,\n maxTokens,\n signal,\n });\n\n const text = coerceImageAssistantText({\n message,\n provider: providerId,\n model: modelId,\n });\n return { text, provider: providerId, model: modelId };\n },\n };\n}\n\nfor (const providerId of ['openai', 'anthropic', 'google']) {\n registerImageUnderstandingProvider(buildPiAiImageUnderstandingProvider(providerId));\n}\n\nregisterImageUnderstandingProviderFactory((providerId) => {\n const hasVisionModel = getModelsByProvider(providerId).some((model) =>\n model.input?.includes('image'),\n );\n return hasVisionModel ? buildPiAiImageUnderstandingProvider(providerId) : undefined;\n});\n"],"mappings":";;;;;gBAC2F;AAQ3F,SAAS,iBAAiB,gBAAoC,qBAAqB,MAAc;AAC/F,KACE,OAAO,mBAAmB,YAC1B,CAAC,OAAO,SAAS,eAAe,IAChC,kBAAkB,EAElB,QAAO;AAET,QAAO,KAAK,IAAI,oBAAoB,eAAe;;AAGrD,SAAgB,oCAAoC,YAAgD;AAClG,QAAO;EACL,IAAI;EACJ,OAAO,UAAU,WAAW;EAC5B,MAAM,eAAe;GACnB,MAAM,SAAS,MAAM,UAAU,WAAW;AAC1C,UAAO,QAAQ,OAAO;;EAExB,MAAM,eAAe,SAAS,SAAS;GACrC,MAAM,WAAW,GAAG,WAAW,GAAG;GAClC,MAAM,QAAQ,aAAa,SAAS;AACpC,OAAI,CAAC,MAAM,OAAO,SAAS,QAAQ,CACjC,OAAM,IAAI,MAAM,kCAAkC,WAAW;GAE/D,MAAM,SAAS,MAAM,UAAU,WAAW;AAC1C,OAAI,CAAC,OACH,OAAM,IAAI,MAAM,uCAAuC,aAAa;GAGtE,MAAM,UAAmB,EACvB,UAAU,CACR;IACE,MAAM;IACN,SAAS,CACP;KAAE,MAAM;KAAQ,MAAM,QAAQ;KAAQ,EACtC,GAAG,QAAQ,OAAO,KAAK,SAAS;KAC9B,MAAM;KACN,MAAM,IAAI,OAAO,SAAS,SAAS;KACnC,UAAU,IAAI,YAAY;KAC3B,EAAE,CACJ;IACD,WAAW,KAAK,KAAK;IACtB,CACF,EACF;GAED,MAAM,YAAY,iBAAiB,MAAM,WAAW,QAAQ,aAAa,IAAI;GAC7E,MAAM,YAAY,QAAQ,aAAa;GACvC,MAAM,gBACJ,OAAO,gBAAgB,eAAe,OAAO,YAAY,YAAY,aACjE,YAAY,QAAQ,UAAU,GAC9B,KAAA;AA2BN,UAAO;IAAE,MALI,yBAAyB;KACpC,SAAA,MAPoB,SAAS,OAAO,SAAS;MAC7C;MACA;MACA,eAlBoB;AACpB,WAAI,iBAAiB,QAAQ,UAAU,OAAO,YAAY,QAAQ,WAChE,QAAO,YAAY,IAAI,CAAC,QAAQ,QAAQ,cAAc,CAAC;AAEzD,WAAI,QAAQ,OACV,QAAO,QAAQ;AAEjB,WAAI,cACF,QAAO;OAET,MAAM,aAAa,IAAI,iBAAiB;AACxC,wBAAiB,WAAW,OAAO,EAAE,UAAU;AAC/C,cAAO,WAAW;UAMZ;MACP,CAAC;KAIA,UAAU;KACV,OAAO;KACR,CACY;IAAE,UAAU;IAAY,OAAO;IAAS;;EAExD;;AAGH,KAAK,MAAM,cAAc;CAAC;CAAU;CAAa;CAAS,CACxD,oCAAmC,oCAAoC,WAAW,CAAC;AAGrF,2CAA2C,eAAe;AAIxD,QAHuB,oBAAoB,WAAW,CAAC,MAAM,UAC3D,MAAM,OAAO,SAAS,QAAQ,CAEX,GAAG,oCAAoC,WAAW,GAAG,KAAA;EAC1E"}
1
+ {"version":3,"file":"pi-ai-provider.js","names":[],"sources":["../../../../../src/agent/image/understanding/pi-ai-provider.ts"],"sourcesContent":["import { type Api, type Context, type Model } from '@earendil-works/pi-ai/compat';\nimport { resolveModel, getApiKey, getModelsByProvider } from '../../../providers/index.js';\nimport { completeWithResolvedCredentials } from '../../../providers/model-call.js';\nimport { coerceImageAssistantText } from '../image-helpers.js';\nimport {\n registerImageUnderstandingProvider,\n registerImageUnderstandingProviderFactory,\n} from './provider-registry.js';\nimport type { ImageUnderstandingProvider } from './types.js';\n\nfunction resolveMaxTokens(modelMaxTokens: number | undefined, requestedMaxTokens = 4096): number {\n if (\n typeof modelMaxTokens !== 'number' ||\n !Number.isFinite(modelMaxTokens) ||\n modelMaxTokens <= 0\n ) {\n return requestedMaxTokens;\n }\n return Math.min(requestedMaxTokens, modelMaxTokens);\n}\n\nexport function buildPiAiImageUnderstandingProvider(providerId: string): ImageUnderstandingProvider {\n return {\n id: providerId,\n label: `pi-ai (${providerId})`,\n async isConfigured() {\n const apiKey = await getApiKey(providerId);\n return Boolean(apiKey);\n },\n async describeImages(modelId, request) {\n const modelRef = `${providerId}/${modelId}`;\n const model = resolveModel(modelRef) as Model<Api>;\n if (!model.input?.includes('image')) {\n throw new Error(`Model does not support images: ${modelRef}`);\n }\n const apiKey = await getApiKey(providerId);\n if (!apiKey) {\n throw new Error(`No API key configured for provider: ${providerId}`);\n }\n\n const context: Context = {\n messages: [\n {\n role: 'user',\n content: [\n { type: 'text', text: request.prompt },\n ...request.images.map((img) => ({\n type: 'image' as const,\n data: img.buffer.toString('base64'),\n mimeType: img.mimeType || 'image/jpeg',\n })),\n ],\n timestamp: Date.now(),\n },\n ],\n };\n\n const maxTokens = resolveMaxTokens(model.maxTokens, request.maxTokens ?? 512);\n const timeoutMs = request.timeoutMs ?? 60_000;\n const timeoutSignal =\n typeof AbortSignal !== 'undefined' && typeof AbortSignal.timeout === 'function'\n ? AbortSignal.timeout(timeoutMs)\n : undefined;\n const signal = (() => {\n if (timeoutSignal && request.signal && typeof AbortSignal.any === 'function') {\n return AbortSignal.any([request.signal, timeoutSignal]);\n }\n if (request.signal) {\n return request.signal;\n }\n if (timeoutSignal) {\n return timeoutSignal;\n }\n const controller = new AbortController();\n setTimeout(() => controller.abort(), timeoutMs);\n return controller.signal;\n })();\n\n const message = await completeWithResolvedCredentials(model, context, {\n apiKey,\n maxTokens,\n signal,\n });\n\n const text = coerceImageAssistantText({\n message,\n provider: providerId,\n model: modelId,\n });\n return { text, provider: providerId, model: modelId };\n },\n };\n}\n\nfor (const providerId of ['openai', 'anthropic', 'google']) {\n registerImageUnderstandingProvider(buildPiAiImageUnderstandingProvider(providerId));\n}\n\nregisterImageUnderstandingProviderFactory((providerId) => {\n const hasVisionModel = getModelsByProvider(providerId).some((model) =>\n model.input?.includes('image'),\n );\n return hasVisionModel ? buildPiAiImageUnderstandingProvider(providerId) : undefined;\n});\n"],"mappings":";;;;;gBAC2F;AAS3F,SAAS,iBAAiB,gBAAoC,qBAAqB,MAAc;AAC/F,KACE,OAAO,mBAAmB,YAC1B,CAAC,OAAO,SAAS,eAAe,IAChC,kBAAkB,EAElB,QAAO;AAET,QAAO,KAAK,IAAI,oBAAoB,eAAe;;AAGrD,SAAgB,oCAAoC,YAAgD;AAClG,QAAO;EACL,IAAI;EACJ,OAAO,UAAU,WAAW;EAC5B,MAAM,eAAe;GACnB,MAAM,SAAS,MAAM,UAAU,WAAW;AAC1C,UAAO,QAAQ,OAAO;;EAExB,MAAM,eAAe,SAAS,SAAS;GACrC,MAAM,WAAW,GAAG,WAAW,GAAG;GAClC,MAAM,QAAQ,aAAa,SAAS;AACpC,OAAI,CAAC,MAAM,OAAO,SAAS,QAAQ,CACjC,OAAM,IAAI,MAAM,kCAAkC,WAAW;GAE/D,MAAM,SAAS,MAAM,UAAU,WAAW;AAC1C,OAAI,CAAC,OACH,OAAM,IAAI,MAAM,uCAAuC,aAAa;GAGtE,MAAM,UAAmB,EACvB,UAAU,CACR;IACE,MAAM;IACN,SAAS,CACP;KAAE,MAAM;KAAQ,MAAM,QAAQ;KAAQ,EACtC,GAAG,QAAQ,OAAO,KAAK,SAAS;KAC9B,MAAM;KACN,MAAM,IAAI,OAAO,SAAS,SAAS;KACnC,UAAU,IAAI,YAAY;KAC3B,EAAE,CACJ;IACD,WAAW,KAAK,KAAK;IACtB,CACF,EACF;GAED,MAAM,YAAY,iBAAiB,MAAM,WAAW,QAAQ,aAAa,IAAI;GAC7E,MAAM,YAAY,QAAQ,aAAa;GACvC,MAAM,gBACJ,OAAO,gBAAgB,eAAe,OAAO,YAAY,YAAY,aACjE,YAAY,QAAQ,UAAU,GAC9B,KAAA;AA2BN,UAAO;IAAE,MALI,yBAAyB;KACpC,SAAA,MAPoB,gCAAgC,OAAO,SAAS;MACpE;MACA;MACA,eAlBoB;AACpB,WAAI,iBAAiB,QAAQ,UAAU,OAAO,YAAY,QAAQ,WAChE,QAAO,YAAY,IAAI,CAAC,QAAQ,QAAQ,cAAc,CAAC;AAEzD,WAAI,QAAQ,OACV,QAAO,QAAQ;AAEjB,WAAI,cACF,QAAO;OAET,MAAM,aAAa,IAAI,iBAAiB;AACxC,wBAAiB,WAAW,OAAO,EAAE,UAAU;AAC/C,cAAO,WAAW;UAMZ;MACP,CAAC;KAIA,UAAU;KACV,OAAO;KACR,CACY;IAAE,UAAU;IAAY,OAAO;IAAS;;EAExD;;AAGH,KAAK,MAAM,cAAc;CAAC;CAAU;CAAa;CAAS,CACxD,oCAAmC,oCAAoC,WAAW,CAAC;AAGrF,2CAA2C,eAAe;AAIxD,QAHuB,oBAAoB,WAAW,CAAC,MAAM,UAC3D,MAAM,OAAO,SAAS,QAAQ,CAEX,GAAG,oCAAoC,WAAW,GAAG,KAAA;EAC1E"}
@@ -0,0 +1,12 @@
1
+ import type { Config } from '../../config/schema.js';
2
+ import type { MemoryRecord, MemoryScope } from './types.js';
3
+ export type CrossAgentSharingMode = 'deny' | 'readOnly' | 'allow';
4
+ export interface MemoryAccessPolicy {
5
+ requesterAgentId: string;
6
+ readableAgentIds: readonly string[];
7
+ canReadAgent(ownerAgentId: string): boolean;
8
+ canReadRecord(record: MemoryRecord, scope?: Partial<MemoryScope>): boolean;
9
+ canSubmitCandidate(ownerAgentId: string): boolean;
10
+ }
11
+ /** Resolve bilateral cross-agent memory grants for one requesting agent. */
12
+ export declare function resolveMemoryAccessPolicy(config: Config, requestedAgentId: string): MemoryAccessPolicy;
@@ -0,0 +1,44 @@
1
+ import { init_agent_session_key, normalizeAgentId } from "../../routing/agent-session-key.js";
2
+ import { resolveEffectiveAgentManifestForAgent } from "../../config/agent-profile.js";
3
+ //#region src/agent/memory/access-policy.ts
4
+ init_agent_session_key();
5
+ function sharingMode(config, agentId) {
6
+ return resolveEffectiveAgentManifestForAgent(config, agentId).memory.privacy?.crossAgentSharing ?? "deny";
7
+ }
8
+ /** Resolve bilateral cross-agent memory grants for one requesting agent. */
9
+ function resolveMemoryAccessPolicy(config, requestedAgentId) {
10
+ const requesterAgentId = normalizeAgentId(requestedAgentId);
11
+ const requesterManifest = resolveEffectiveAgentManifestForAgent(config, requesterAgentId);
12
+ const requesterMode = requesterManifest.memory.privacy?.crossAgentSharing ?? "deny";
13
+ const enabledAgentIds = config.agents.list.filter((entry) => entry.enabled !== false).map((entry) => normalizeAgentId(entry.id));
14
+ const canReadAgent = (ownerAgentId) => {
15
+ const owner = normalizeAgentId(ownerAgentId);
16
+ if (owner === requesterAgentId) return true;
17
+ if (requesterManifest.memory.mode === "off" || requesterMode === "deny" || !enabledAgentIds.includes(owner)) return false;
18
+ return resolveEffectiveAgentManifestForAgent(config, owner).memory.mode !== "off" && sharingMode(config, owner) !== "deny";
19
+ };
20
+ const canReadRecord = (record, scope) => {
21
+ if (!canReadAgent(record.scope.agentId)) return false;
22
+ if (normalizeAgentId(record.scope.agentId) !== requesterAgentId && record.sensitivity && record.sensitivity !== "normal") return false;
23
+ if (record.scope.sessionKey && record.scope.sessionKey !== scope?.sessionKey) return false;
24
+ if (record.scope.projectId && record.scope.projectId !== scope?.projectId) return false;
25
+ return true;
26
+ };
27
+ const canSubmitCandidate = (ownerAgentId) => {
28
+ const owner = normalizeAgentId(ownerAgentId);
29
+ if (owner === requesterAgentId) return true;
30
+ if (requesterManifest.memory.mode === "off" || requesterMode !== "allow" || !enabledAgentIds.includes(owner)) return false;
31
+ return resolveEffectiveAgentManifestForAgent(config, owner).memory.mode !== "off" && sharingMode(config, owner) === "allow";
32
+ };
33
+ return {
34
+ requesterAgentId,
35
+ readableAgentIds: [requesterAgentId, ...enabledAgentIds.filter((agentId) => agentId !== requesterAgentId && canReadAgent(agentId))],
36
+ canReadAgent,
37
+ canReadRecord,
38
+ canSubmitCandidate
39
+ };
40
+ }
41
+ //#endregion
42
+ export { resolveMemoryAccessPolicy };
43
+
44
+ //# sourceMappingURL=access-policy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"access-policy.js","names":[],"sources":["../../../../src/agent/memory/access-policy.ts"],"sourcesContent":["import type { Config } from '../../config/schema.js';\nimport { resolveEffectiveAgentManifestForAgent } from '../../config/agent-profile.js';\nimport { normalizeAgentId } from '../../routing/agent-session-key.js';\nimport type { MemoryRecord, MemoryScope } from './types.js';\n\nexport type CrossAgentSharingMode = 'deny' | 'readOnly' | 'allow';\n\nexport interface MemoryAccessPolicy {\n requesterAgentId: string;\n readableAgentIds: readonly string[];\n canReadAgent(ownerAgentId: string): boolean;\n canReadRecord(record: MemoryRecord, scope?: Partial<MemoryScope>): boolean;\n canSubmitCandidate(ownerAgentId: string): boolean;\n}\n\nfunction sharingMode(config: Config, agentId: string): CrossAgentSharingMode {\n return resolveEffectiveAgentManifestForAgent(config, agentId)\n .memory.privacy?.crossAgentSharing ?? 'deny';\n}\n\n/** Resolve bilateral cross-agent memory grants for one requesting agent. */\nexport function resolveMemoryAccessPolicy(\n config: Config,\n requestedAgentId: string,\n): MemoryAccessPolicy {\n const requesterAgentId = normalizeAgentId(requestedAgentId);\n const requesterManifest = resolveEffectiveAgentManifestForAgent(config, requesterAgentId);\n const requesterMode = requesterManifest.memory.privacy?.crossAgentSharing ?? 'deny';\n const enabledAgentIds = config.agents.list\n .filter((entry) => entry.enabled !== false)\n .map((entry) => normalizeAgentId(entry.id));\n\n const canReadAgent = (ownerAgentId: string): boolean => {\n const owner = normalizeAgentId(ownerAgentId);\n if (owner === requesterAgentId) return true;\n if (\n requesterManifest.memory.mode === 'off'\n || requesterMode === 'deny'\n || !enabledAgentIds.includes(owner)\n ) return false;\n const ownerManifest = resolveEffectiveAgentManifestForAgent(config, owner);\n return ownerManifest.memory.mode !== 'off' && sharingMode(config, owner) !== 'deny';\n };\n\n const canReadRecord = (record: MemoryRecord, scope?: Partial<MemoryScope>): boolean => {\n if (!canReadAgent(record.scope.agentId)) return false;\n const crossAgent = normalizeAgentId(record.scope.agentId) !== requesterAgentId;\n if (crossAgent && record.sensitivity && record.sensitivity !== 'normal') {\n return false;\n }\n if (record.scope.sessionKey && record.scope.sessionKey !== scope?.sessionKey) return false;\n if (record.scope.projectId && record.scope.projectId !== scope?.projectId) return false;\n return true;\n };\n\n const canSubmitCandidate = (ownerAgentId: string): boolean => {\n const owner = normalizeAgentId(ownerAgentId);\n if (owner === requesterAgentId) return true;\n if (\n requesterManifest.memory.mode === 'off'\n || requesterMode !== 'allow'\n || !enabledAgentIds.includes(owner)\n ) return false;\n const ownerManifest = resolveEffectiveAgentManifestForAgent(config, owner);\n return ownerManifest.memory.mode !== 'off' && sharingMode(config, owner) === 'allow';\n };\n\n return {\n requesterAgentId,\n readableAgentIds: [requesterAgentId, ...enabledAgentIds.filter(\n (agentId) => agentId !== requesterAgentId && canReadAgent(agentId),\n )],\n canReadAgent,\n canReadRecord,\n canSubmitCandidate,\n };\n}\n"],"mappings":";;;wBAEsE;AAatE,SAAS,YAAY,QAAgB,SAAwC;AAC3E,QAAO,sCAAsC,QAAQ,QAAQ,CAC1D,OAAO,SAAS,qBAAqB;;;AAI1C,SAAgB,0BACd,QACA,kBACoB;CACpB,MAAM,mBAAmB,iBAAiB,iBAAiB;CAC3D,MAAM,oBAAoB,sCAAsC,QAAQ,iBAAiB;CACzF,MAAM,gBAAgB,kBAAkB,OAAO,SAAS,qBAAqB;CAC7E,MAAM,kBAAkB,OAAO,OAAO,KACnC,QAAQ,UAAU,MAAM,YAAY,MAAM,CAC1C,KAAK,UAAU,iBAAiB,MAAM,GAAG,CAAC;CAE7C,MAAM,gBAAgB,iBAAkC;EACtD,MAAM,QAAQ,iBAAiB,aAAa;AAC5C,MAAI,UAAU,iBAAkB,QAAO;AACvC,MACE,kBAAkB,OAAO,SAAS,SAC/B,kBAAkB,UAClB,CAAC,gBAAgB,SAAS,MAAM,CACnC,QAAO;AAET,SADsB,sCAAsC,QAAQ,MAChD,CAAC,OAAO,SAAS,SAAS,YAAY,QAAQ,MAAM,KAAK;;CAG/E,MAAM,iBAAiB,QAAsB,UAA0C;AACrF,MAAI,CAAC,aAAa,OAAO,MAAM,QAAQ,CAAE,QAAO;AAEhD,MADmB,iBAAiB,OAAO,MAAM,QAAQ,KAAK,oBAC5C,OAAO,eAAe,OAAO,gBAAgB,SAC7D,QAAO;AAET,MAAI,OAAO,MAAM,cAAc,OAAO,MAAM,eAAe,OAAO,WAAY,QAAO;AACrF,MAAI,OAAO,MAAM,aAAa,OAAO,MAAM,cAAc,OAAO,UAAW,QAAO;AAClF,SAAO;;CAGT,MAAM,sBAAsB,iBAAkC;EAC5D,MAAM,QAAQ,iBAAiB,aAAa;AAC5C,MAAI,UAAU,iBAAkB,QAAO;AACvC,MACE,kBAAkB,OAAO,SAAS,SAC/B,kBAAkB,WAClB,CAAC,gBAAgB,SAAS,MAAM,CACnC,QAAO;AAET,SADsB,sCAAsC,QAAQ,MAChD,CAAC,OAAO,SAAS,SAAS,YAAY,QAAQ,MAAM,KAAK;;AAG/E,QAAO;EACL;EACA,kBAAkB,CAAC,kBAAkB,GAAG,gBAAgB,QACrD,YAAY,YAAY,oBAAoB,aAAa,QAAQ,CACnE,CAAC;EACF;EACA;EACA;EACD"}
@@ -1,4 +1,5 @@
1
1
  import type { BuiltinMemoryStore } from './builtin-memory-store.js';
2
+ import type { MemoryAccessPolicy } from './access-policy.js';
2
3
  import type { MemoryProvider, MemoryProviderInitOptions } from './provider.js';
3
4
  import type { MemoryDeleteRequest, MemoryListRequest, MemoryReadRequest, MemoryReadResult, MemoryRecord, MemorySearchRequest, MemorySearchResult, MemoryUpdateRequest, MemoryWriteRequest, MemoryWriteResult, MemorySyncEvent } from './types.js';
4
5
  /**
@@ -7,6 +8,7 @@ import type { MemoryDeleteRequest, MemoryListRequest, MemoryReadRequest, MemoryR
7
8
  */
8
9
  export declare class BuiltinMemoryProvider implements MemoryProvider {
9
10
  private readonly store;
11
+ private readonly accessPolicy?;
10
12
  readonly id = "local";
11
13
  readonly displayName = "Local Knowledge Store";
12
14
  readonly capabilities: {
@@ -24,7 +26,7 @@ export declare class BuiltinMemoryProvider implements MemoryProvider {
24
26
  };
25
27
  private workspaceDir;
26
28
  private agentId;
27
- constructor(store: BuiltinMemoryStore);
29
+ constructor(store: BuiltinMemoryStore, accessPolicy?: MemoryAccessPolicy);
28
30
  isAvailable(): boolean;
29
31
  initialize(_sessionId: string, options?: MemoryProviderInitOptions): Promise<void>;
30
32
  systemPromptBlock(): string;
@@ -39,4 +41,5 @@ export declare class BuiltinMemoryProvider implements MemoryProvider {
39
41
  delete(request: MemoryDeleteRequest): Promise<MemoryWriteResult>;
40
42
  sync(event: MemorySyncEvent): void;
41
43
  shutdown(): Promise<void>;
44
+ private canReadRecord;
42
45
  }
@@ -1,7 +1,9 @@
1
+ import { init_agent_session_key, normalizeAgentId } from "../../routing/agent-session-key.js";
1
2
  import { appendMemorySignal, deleteMemoryRecord, getMemoryRecord, listMemoryRecords, searchMemoryRecords, upsertMemoryRecord } from "../../storage/sqlite/memory-records-repository.js";
2
3
  import "../../storage/sqlite/index.js";
3
4
  import { memoryGet, memorySearch } from "../prompt/memory/index.js";
4
5
  //#region src/agent/memory/builtin-provider.ts
6
+ init_agent_session_key();
5
7
  /**
6
8
  * Builtin local memory provider backed by profile memory, agent-home curated files,
7
9
  * and the local SQLite FTS index.
@@ -24,15 +26,16 @@ var BuiltinMemoryProvider = class {
24
26
  };
25
27
  workspaceDir = "";
26
28
  agentId = "main";
27
- constructor(store) {
29
+ constructor(store, accessPolicy) {
28
30
  this.store = store;
31
+ this.accessPolicy = accessPolicy;
29
32
  }
30
33
  isAvailable() {
31
34
  return true;
32
35
  }
33
36
  async initialize(_sessionId, options) {
34
37
  this.workspaceDir = options?.workspace ?? options?.agentWorkspace ?? this.store.workspaceDir;
35
- this.agentId = String(options?.agentId ?? this.agentId);
38
+ this.agentId = normalizeAgentId(String(options?.agentId ?? this.agentId));
36
39
  }
37
40
  systemPromptBlock() {
38
41
  return "";
@@ -42,24 +45,27 @@ var BuiltinMemoryProvider = class {
42
45
  return [];
43
46
  }
44
47
  async search(request) {
45
- const agentId = request.scope?.agentId ?? this.agentId;
46
48
  const workspaceId = request.scope?.workspaceId ?? this.workspaceDir;
47
- const recordHits = searchMemoryRecords({
49
+ const recordHits = (this.accessPolicy?.readableAgentIds ?? [this.agentId]).flatMap((ownerAgentId) => searchMemoryRecords({
48
50
  query: request.query,
49
- agentId,
50
- workspaceId,
51
+ agentId: ownerAgentId,
52
+ workspaceId: ownerAgentId === this.agentId ? workspaceId : void 0,
53
+ visibleToSessionKey: request.scope?.sessionKey,
54
+ unscopedSessionOnly: request.scope?.sessionKey == null,
55
+ visibleToProjectId: request.scope?.projectId,
56
+ unscopedProjectOnly: request.scope?.projectId == null,
51
57
  providerId: this.id,
52
58
  kinds: request.kinds,
53
59
  maxResults: request.maxResults,
54
60
  minScore: request.minScore
55
- });
61
+ })).filter((result) => this.canReadRecord(result.record, request.scope)).sort((left, right) => right.score - left.score).slice(0, request.maxResults ?? 5);
56
62
  if (recordHits.length > 0) return recordHits;
57
63
  return (await memorySearch(this.workspaceDir, request.query, {
58
64
  maxResults: request.maxResults,
59
65
  minScore: request.minScore,
60
66
  memoriesDir: this.store.memoriesDir,
61
67
  userMemoryPath: this.store.userMemoryPath,
62
- agentId
68
+ agentId: this.agentId
63
69
  })).map((entry) => {
64
70
  const start = entry.lineNumbers[0] ?? 1;
65
71
  const end = entry.lineNumbers[entry.lineNumbers.length - 1] ?? start;
@@ -70,7 +76,7 @@ var BuiltinMemoryProvider = class {
70
76
  id,
71
77
  providerId: this.id,
72
78
  kind,
73
- agentId,
79
+ agentId: this.agentId,
74
80
  workspaceId,
75
81
  content: entry.lines,
76
82
  source: {
@@ -96,7 +102,8 @@ var BuiltinMemoryProvider = class {
96
102
  async read(request) {
97
103
  if (request.id && !request.path) {
98
104
  const existing = getMemoryRecord(request.id);
99
- if (existing) return { record: existing };
105
+ if (existing && this.canReadRecord(existing, request.scope)) return { record: existing };
106
+ if (existing) return null;
100
107
  }
101
108
  const result = memoryGet(this.workspaceDir, request.path ?? request.id ?? "", request.from, request.lines, this.store.memoriesDir, this.store.userMemoryPath);
102
109
  if (!result) return null;
@@ -106,7 +113,7 @@ var BuiltinMemoryProvider = class {
106
113
  id: `${path}#L${result.lineNumbers.start}-L${result.lineNumbers.end}`,
107
114
  providerId: this.id,
108
115
  kind: inferKindFromPath(path),
109
- agentId: request.scope?.agentId ?? this.agentId,
116
+ agentId: this.agentId,
110
117
  workspaceId: request.scope?.workspaceId ?? this.workspaceDir,
111
118
  content: result.content,
112
119
  source: {
@@ -132,6 +139,8 @@ var BuiltinMemoryProvider = class {
132
139
  providerId: this.id,
133
140
  agentId: request.scope?.agentId ?? this.agentId,
134
141
  workspaceId: request.scope?.workspaceId ?? this.workspaceDir,
142
+ visibleToSessionKey: request.scope?.sessionKey,
143
+ visibleToProjectId: request.scope?.projectId,
135
144
  kind: request.kind,
136
145
  status: request.status,
137
146
  canonicalKey: request.canonicalKey,
@@ -154,22 +163,33 @@ var BuiltinMemoryProvider = class {
154
163
  }
155
164
  async write(request) {
156
165
  const target = request.target ?? (request.kind === "user_profile" ? "user" : "memory");
166
+ const ownerAgentId = normalizeAgentId(request.scope?.agentId ?? this.agentId);
167
+ const isCrossAgentWrite = ownerAgentId !== this.agentId;
168
+ if (isCrossAgentWrite && request.status !== "candidate") return {
169
+ success: false,
170
+ error: "Cross-agent writes must be submitted as candidates"
171
+ };
172
+ if (isCrossAgentWrite && !this.accessPolicy?.canSubmitCandidate(ownerAgentId)) return {
173
+ success: false,
174
+ error: "Cross-agent candidate submission is not allowed"
175
+ };
157
176
  if (request.status === "candidate") return {
158
177
  success: true,
159
178
  message: "Memory candidate added to inbox",
160
179
  record: upsertMemoryRecord({
161
180
  providerId: this.id,
162
181
  kind: request.kind,
163
- agentId: request.scope?.agentId ?? this.agentId,
164
- workspaceId: request.scope?.workspaceId ?? this.workspaceDir,
165
- sessionKey: request.scope?.sessionKey,
182
+ agentId: ownerAgentId,
183
+ workspaceId: isCrossAgentWrite ? request.scope?.workspaceId : request.scope?.workspaceId ?? this.workspaceDir,
184
+ sessionKey: isCrossAgentWrite ? void 0 : request.scope?.sessionKey,
185
+ projectId: isCrossAgentWrite ? void 0 : request.scope?.projectId,
166
186
  content: request.content,
167
187
  source: {
168
188
  ...request.source ?? {},
169
189
  provider: this.id
170
190
  },
171
191
  confidence: request.confidence,
172
- tags: request.tags,
192
+ tags: isCrossAgentWrite ? [...request.tags ?? [], `shared-from:${this.agentId}`] : request.tags,
173
193
  status: "candidate",
174
194
  sensitivity: request.sensitivity,
175
195
  canonicalKey: request.canonicalKey,
@@ -198,7 +218,7 @@ var BuiltinMemoryProvider = class {
198
218
  id: `curated:${target}:${Date.now()}`,
199
219
  providerId: this.id,
200
220
  kind: request.kind,
201
- agentId: request.scope?.agentId ?? this.agentId,
221
+ agentId: this.agentId,
202
222
  workspaceId: request.scope?.workspaceId ?? this.workspaceDir,
203
223
  sessionKey: request.scope?.sessionKey,
204
224
  content: request.content,
@@ -225,6 +245,10 @@ var BuiltinMemoryProvider = class {
225
245
  }
226
246
  async update(request) {
227
247
  const target = request.target ?? "memory";
248
+ if (((request.id ? getMemoryRecord(request.id) : null)?.scope.agentId ?? request.scope?.agentId ?? this.agentId) !== this.agentId) return {
249
+ success: false,
250
+ error: "Cross-agent memory updates are not allowed"
251
+ };
228
252
  const result = await this.store.replace(target, request.matchText ?? request.id ?? "", request.content);
229
253
  if (!result.success) return {
230
254
  success: false,
@@ -273,6 +297,10 @@ var BuiltinMemoryProvider = class {
273
297
  }
274
298
  async delete(request) {
275
299
  const target = request.target ?? "memory";
300
+ if (((request.id ? getMemoryRecord(request.id) : null)?.scope.agentId ?? request.scope?.agentId ?? this.agentId) !== this.agentId) return {
301
+ success: false,
302
+ error: "Cross-agent memory deletion is not allowed"
303
+ };
276
304
  const result = await this.store.remove(target, request.matchText ?? request.id ?? "");
277
305
  if (!result.success) return {
278
306
  success: false,
@@ -304,6 +332,13 @@ var BuiltinMemoryProvider = class {
304
332
  });
305
333
  }
306
334
  async shutdown() {}
335
+ canReadRecord(record, scope) {
336
+ if (this.accessPolicy) return this.accessPolicy.canReadRecord(record, scope);
337
+ if (record.scope.agentId !== this.agentId) return false;
338
+ if (record.scope.sessionKey && record.scope.sessionKey !== scope?.sessionKey) return false;
339
+ if (record.scope.projectId && record.scope.projectId !== scope?.projectId) return false;
340
+ return true;
341
+ }
307
342
  };
308
343
  function inferKindFromPath(path) {
309
344
  const normalized = path.replace(/\\/g, "/");