@bolloon/bolloon-agent 0.4.28 → 0.4.30

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 (473) hide show
  1. package/bin/bolloon.cjs +18 -148
  2. package/bin/bolloon.cmd +6 -3
  3. package/dist/agents/contacts/chain.js +579 -0
  4. package/dist/agents/contacts/cli.js +158 -0
  5. package/dist/agents/contacts/consent.js +124 -0
  6. package/dist/agents/contacts/grant-payload.js +45 -0
  7. package/dist/agents/contacts/grants.js +451 -0
  8. package/dist/agents/contacts/policy.js +264 -0
  9. package/dist/agents/contacts/preview-types.js +6 -0
  10. package/dist/agents/contacts/providers.js +341 -0
  11. package/dist/agents/contacts/store.js +326 -0
  12. package/dist/agents/contacts/tools.js +154 -0
  13. package/dist/agents/contacts/types.js +148 -0
  14. package/dist/agents/pi-sdk-tools.js +3 -1
  15. package/dist/agents/pi-sdk.js +14 -1
  16. package/dist/cli/update-commands.js +349 -0
  17. package/dist/cli-entry.js +41 -55
  18. package/dist/electron-build/agents/agent-gateway.js +169 -0
  19. package/dist/electron-build/agents/agent-gateway.js.map +1 -0
  20. package/dist/electron-build/agents/agent-identity-store.js +212 -0
  21. package/dist/electron-build/agents/agent-identity-store.js.map +1 -0
  22. package/dist/electron-build/agents/agent-identity.js +196 -0
  23. package/dist/electron-build/agents/agent-identity.js.map +1 -0
  24. package/dist/electron-build/agents/agent-manifest-protocol.js +151 -0
  25. package/dist/electron-build/agents/agent-manifest-protocol.js.map +1 -0
  26. package/dist/electron-build/agents/agent-registry.js +217 -0
  27. package/dist/electron-build/agents/agent-registry.js.map +1 -0
  28. package/dist/electron-build/agents/agent-reputation.js +60 -0
  29. package/dist/electron-build/agents/agent-reputation.js.map +1 -0
  30. package/dist/electron-build/agents/agent-service-client.js +186 -0
  31. package/dist/electron-build/agents/agent-service-client.js.map +1 -0
  32. package/dist/electron-build/agents/browser-cdp.js +765 -0
  33. package/dist/electron-build/agents/browser-cdp.js.map +1 -0
  34. package/dist/electron-build/agents/computer-use.js +341 -0
  35. package/dist/electron-build/agents/computer-use.js.map +1 -0
  36. package/dist/electron-build/agents/constraint-layer.js +210 -0
  37. package/dist/electron-build/agents/constraint-layer.js.map +1 -0
  38. package/dist/electron-build/agents/contacts/chain.js +619 -0
  39. package/dist/electron-build/agents/contacts/chain.js.map +1 -0
  40. package/dist/electron-build/agents/contacts/consent.js +163 -0
  41. package/dist/electron-build/agents/contacts/consent.js.map +1 -0
  42. package/dist/electron-build/agents/contacts/grant-payload.js +52 -0
  43. package/dist/electron-build/agents/contacts/grant-payload.js.map +1 -0
  44. package/dist/electron-build/agents/contacts/grants.js +500 -0
  45. package/dist/electron-build/agents/contacts/grants.js.map +1 -0
  46. package/dist/electron-build/agents/contacts/policy.js +271 -0
  47. package/dist/electron-build/agents/contacts/policy.js.map +1 -0
  48. package/dist/electron-build/agents/contacts/preview-types.js +8 -0
  49. package/dist/electron-build/agents/contacts/preview-types.js.map +1 -0
  50. package/dist/electron-build/agents/contacts/providers.js +384 -0
  51. package/dist/electron-build/agents/contacts/providers.js.map +1 -0
  52. package/dist/electron-build/agents/contacts/store.js +370 -0
  53. package/dist/electron-build/agents/contacts/store.js.map +1 -0
  54. package/dist/electron-build/agents/contacts/tools.js +159 -0
  55. package/dist/electron-build/agents/contacts/tools.js.map +1 -0
  56. package/dist/electron-build/agents/contacts/types.js +166 -0
  57. package/dist/electron-build/agents/contacts/types.js.map +1 -0
  58. package/dist/electron-build/agents/decision-store.js +292 -0
  59. package/dist/electron-build/agents/decision-store.js.map +1 -0
  60. package/dist/electron-build/agents/deny-pipeline.js +125 -0
  61. package/dist/electron-build/agents/deny-pipeline.js.map +1 -0
  62. package/dist/electron-build/agents/economic-policy.js +172 -0
  63. package/dist/electron-build/agents/economic-policy.js.map +1 -0
  64. package/dist/electron-build/agents/error-classifier.js +125 -0
  65. package/dist/electron-build/agents/error-classifier.js.map +1 -0
  66. package/dist/electron-build/agents/execution-supervisor.js +539 -0
  67. package/dist/electron-build/agents/execution-supervisor.js.map +1 -0
  68. package/dist/electron-build/agents/external-events.js +171 -0
  69. package/dist/electron-build/agents/external-events.js.map +1 -0
  70. package/dist/electron-build/agents/gateway-join.js +315 -0
  71. package/dist/electron-build/agents/gateway-join.js.map +1 -0
  72. package/dist/electron-build/agents/gateway-network.js +505 -0
  73. package/dist/electron-build/agents/gateway-network.js.map +1 -0
  74. package/dist/electron-build/agents/goal-criteria.js +133 -0
  75. package/dist/electron-build/agents/goal-criteria.js.map +1 -0
  76. package/dist/electron-build/agents/goal-resume.js +364 -0
  77. package/dist/electron-build/agents/goal-resume.js.map +1 -0
  78. package/dist/electron-build/agents/goal-store.js +587 -0
  79. package/dist/electron-build/agents/goal-store.js.map +1 -0
  80. package/dist/electron-build/agents/intent-classifier.js +104 -0
  81. package/dist/electron-build/agents/intent-classifier.js.map +1 -0
  82. package/dist/electron-build/agents/iroh-secret.js +70 -0
  83. package/dist/electron-build/agents/iroh-secret.js.map +1 -0
  84. package/dist/electron-build/agents/loop-review.js +101 -0
  85. package/dist/electron-build/agents/loop-review.js.map +1 -0
  86. package/dist/electron-build/agents/memory-recall.js +154 -0
  87. package/dist/electron-build/agents/memory-recall.js.map +1 -0
  88. package/dist/electron-build/agents/p2p-document-tools.js +328 -0
  89. package/dist/electron-build/agents/p2p-document-tools.js.map +1 -0
  90. package/dist/electron-build/agents/parse-tool-call.js +564 -0
  91. package/dist/electron-build/agents/parse-tool-call.js.map +1 -0
  92. package/dist/electron-build/agents/patch-tool.js +205 -0
  93. package/dist/electron-build/agents/patch-tool.js.map +1 -0
  94. package/dist/electron-build/agents/payment-approval.js +183 -0
  95. package/dist/electron-build/agents/payment-approval.js.map +1 -0
  96. package/dist/electron-build/agents/payment-gate.js +139 -0
  97. package/dist/electron-build/agents/payment-gate.js.map +1 -0
  98. package/dist/electron-build/agents/permission-mode.js +124 -0
  99. package/dist/electron-build/agents/permission-mode.js.map +1 -0
  100. package/dist/electron-build/agents/pi-harness.js +268 -0
  101. package/dist/electron-build/agents/pi-harness.js.map +1 -0
  102. package/dist/electron-build/agents/pi-sdk-session-factory.js +117 -0
  103. package/dist/electron-build/agents/pi-sdk-session-factory.js.map +1 -0
  104. package/dist/electron-build/agents/pi-sdk-session-manager.js +335 -0
  105. package/dist/electron-build/agents/pi-sdk-session-manager.js.map +1 -0
  106. package/dist/electron-build/agents/pi-sdk-tools.js +4042 -0
  107. package/dist/electron-build/agents/pi-sdk-tools.js.map +1 -0
  108. package/dist/electron-build/agents/pi-sdk-types.js +17 -0
  109. package/dist/electron-build/agents/pi-sdk-types.js.map +1 -0
  110. package/dist/electron-build/agents/pi-sdk.js +3335 -0
  111. package/dist/electron-build/agents/pi-sdk.js.map +1 -0
  112. package/dist/electron-build/agents/plan-store.js +212 -0
  113. package/dist/electron-build/agents/plan-store.js.map +1 -0
  114. package/dist/electron-build/agents/pre-tool-validator.js +200 -0
  115. package/dist/electron-build/agents/pre-tool-validator.js.map +1 -0
  116. package/dist/electron-build/agents/process-runner.js +148 -0
  117. package/dist/electron-build/agents/process-runner.js.map +1 -0
  118. package/dist/electron-build/agents/react-loop.js +131 -0
  119. package/dist/electron-build/agents/react-loop.js.map +1 -0
  120. package/dist/electron-build/agents/resource-store.js +180 -0
  121. package/dist/electron-build/agents/resource-store.js.map +1 -0
  122. package/dist/electron-build/agents/resource-token.js +118 -0
  123. package/dist/electron-build/agents/resource-token.js.map +1 -0
  124. package/dist/electron-build/agents/resource-wallet.js +84 -0
  125. package/dist/electron-build/agents/resource-wallet.js.map +1 -0
  126. package/dist/electron-build/agents/run-store.js +854 -0
  127. package/dist/electron-build/agents/run-store.js.map +1 -0
  128. package/dist/electron-build/agents/runner-resolver.js +267 -0
  129. package/dist/electron-build/agents/runner-resolver.js.map +1 -0
  130. package/dist/electron-build/agents/session-store.js +294 -0
  131. package/dist/electron-build/agents/session-store.js.map +1 -0
  132. package/dist/electron-build/agents/shell-guard.js +465 -0
  133. package/dist/electron-build/agents/shell-guard.js.map +1 -0
  134. package/dist/electron-build/agents/shell-tool.js +102 -0
  135. package/dist/electron-build/agents/shell-tool.js.map +1 -0
  136. package/dist/electron-build/agents/skill-loader.js +216 -0
  137. package/dist/electron-build/agents/skill-loader.js.map +1 -0
  138. package/dist/electron-build/agents/skill-readiness.js +174 -0
  139. package/dist/electron-build/agents/skill-readiness.js.map +1 -0
  140. package/dist/electron-build/agents/skill-share.js +394 -0
  141. package/dist/electron-build/agents/skill-share.js.map +1 -0
  142. package/dist/electron-build/agents/skill-supervisor-link.js +75 -0
  143. package/dist/electron-build/agents/skill-supervisor-link.js.map +1 -0
  144. package/dist/electron-build/agents/skill-writer.js +347 -0
  145. package/dist/electron-build/agents/skill-writer.js.map +1 -0
  146. package/dist/electron-build/agents/skills-manager.js +763 -0
  147. package/dist/electron-build/agents/skills-manager.js.map +1 -0
  148. package/dist/electron-build/agents/supervisor-host.js +294 -0
  149. package/dist/electron-build/agents/supervisor-host.js.map +1 -0
  150. package/dist/electron-build/agents/task/local-seller.js +188 -0
  151. package/dist/electron-build/agents/task/local-seller.js.map +1 -0
  152. package/dist/electron-build/agents/task/report-card.js +152 -0
  153. package/dist/electron-build/agents/task/report-card.js.map +1 -0
  154. package/dist/electron-build/agents/task/resource-advisor.js +222 -0
  155. package/dist/electron-build/agents/task/resource-advisor.js.map +1 -0
  156. package/dist/electron-build/agents/task/task-budget.js +111 -0
  157. package/dist/electron-build/agents/task/task-budget.js.map +1 -0
  158. package/dist/electron-build/agents/task/task-runner.js +670 -0
  159. package/dist/electron-build/agents/task/task-runner.js.map +1 -0
  160. package/dist/electron-build/agents/task-state.js +266 -0
  161. package/dist/electron-build/agents/task-state.js.map +1 -0
  162. package/dist/electron-build/agents/tool-registry.js +141 -0
  163. package/dist/electron-build/agents/tool-registry.js.map +1 -0
  164. package/dist/electron-build/agents/treasury-bridge.js +165 -0
  165. package/dist/electron-build/agents/treasury-bridge.js.map +1 -0
  166. package/dist/electron-build/agents/user-questions.js +223 -0
  167. package/dist/electron-build/agents/user-questions.js.map +1 -0
  168. package/dist/electron-build/agents/verify-recipe.js +90 -0
  169. package/dist/electron-build/agents/verify-recipe.js.map +1 -0
  170. package/dist/electron-build/agents/workflow-engine.js +222 -0
  171. package/dist/electron-build/agents/workflow-engine.js.map +1 -0
  172. package/dist/electron-build/agents/workflow-pivot-loop.js +988 -0
  173. package/dist/electron-build/agents/workflow-pivot-loop.js.map +1 -0
  174. package/dist/electron-build/agents/write-staging.js +133 -0
  175. package/dist/electron-build/agents/write-staging.js.map +1 -0
  176. package/dist/electron-build/agents/x402/goal-run-bridge.js +115 -0
  177. package/dist/electron-build/agents/x402/goal-run-bridge.js.map +1 -0
  178. package/dist/electron-build/agents/x402/milestone-settlement.js +163 -0
  179. package/dist/electron-build/agents/x402/milestone-settlement.js.map +1 -0
  180. package/dist/electron-build/agents/x402/paid-info-protocol.js +274 -0
  181. package/dist/electron-build/agents/x402/paid-info-protocol.js.map +1 -0
  182. package/dist/electron-build/agents/x402/paid-info-store.js +409 -0
  183. package/dist/electron-build/agents/x402/paid-info-store.js.map +1 -0
  184. package/dist/electron-build/agents/x402/paid-info-tools.js +346 -0
  185. package/dist/electron-build/agents/x402/paid-info-tools.js.map +1 -0
  186. package/dist/electron-build/agents/x402/payment-recovery.js +330 -0
  187. package/dist/electron-build/agents/x402/payment-recovery.js.map +1 -0
  188. package/dist/electron-build/agents/x402/resource-contract.js +533 -0
  189. package/dist/electron-build/agents/x402/resource-contract.js.map +1 -0
  190. package/dist/electron-build/agents/x402/settlement-state.js +432 -0
  191. package/dist/electron-build/agents/x402/settlement-state.js.map +1 -0
  192. package/dist/electron-build/agents/x402/trade.js +294 -0
  193. package/dist/electron-build/agents/x402/trade.js.map +1 -0
  194. package/dist/electron-build/agents/x402/transaction-protocol.js +109 -0
  195. package/dist/electron-build/agents/x402/transaction-protocol.js.map +1 -0
  196. package/dist/electron-build/agents/x402/transaction-store.js +404 -0
  197. package/dist/electron-build/agents/x402/transaction-store.js.map +1 -0
  198. package/dist/electron-build/agents/x402/x402Pay.js +377 -0
  199. package/dist/electron-build/agents/x402/x402Pay.js.map +1 -0
  200. package/dist/electron-build/bollharness-integration/context-router-judgment.js +269 -0
  201. package/dist/electron-build/bollharness-integration/context-router-judgment.js.map +1 -0
  202. package/dist/electron-build/bollharness-integration/context-router.js +584 -0
  203. package/dist/electron-build/bollharness-integration/context-router.js.map +1 -0
  204. package/dist/electron-build/bollharness-integration/gate-state-machine.js +362 -0
  205. package/dist/electron-build/bollharness-integration/gate-state-machine.js.map +1 -0
  206. package/dist/electron-build/bollharness-integration/gate-transition-hooks.js +136 -0
  207. package/dist/electron-build/bollharness-integration/gate-transition-hooks.js.map +1 -0
  208. package/dist/electron-build/bollharness-integration/guard-checker.js +400 -0
  209. package/dist/electron-build/bollharness-integration/guard-checker.js.map +1 -0
  210. package/dist/electron-build/bollharness-integration/index.js +123 -0
  211. package/dist/electron-build/bollharness-integration/index.js.map +1 -0
  212. package/dist/electron-build/bollharness-integration/integration.js +531 -0
  213. package/dist/electron-build/bollharness-integration/integration.js.map +1 -0
  214. package/dist/electron-build/bollharness-integration/skill-adapter.js +608 -0
  215. package/dist/electron-build/bollharness-integration/skill-adapter.js.map +1 -0
  216. package/dist/electron-build/bootstrap/bootstrap.js +173 -0
  217. package/dist/electron-build/bootstrap/bootstrap.js.map +1 -0
  218. package/dist/electron-build/bootstrap/context-collector.js +358 -0
  219. package/dist/electron-build/bootstrap/context-collector.js.map +1 -0
  220. package/dist/electron-build/bootstrap/context-hierarchy.js +262 -0
  221. package/dist/electron-build/bootstrap/context-hierarchy.js.map +1 -0
  222. package/dist/electron-build/bootstrap/context-manager.js +211 -0
  223. package/dist/electron-build/bootstrap/context-manager.js.map +1 -0
  224. package/dist/electron-build/bootstrap/context-os.js +277 -0
  225. package/dist/electron-build/bootstrap/context-os.js.map +1 -0
  226. package/dist/electron-build/bootstrap/event-log.js +203 -0
  227. package/dist/electron-build/bootstrap/event-log.js.map +1 -0
  228. package/dist/electron-build/bootstrap/exhaust-scrubber.js +277 -0
  229. package/dist/electron-build/bootstrap/exhaust-scrubber.js.map +1 -0
  230. package/dist/electron-build/bootstrap/lifecycle-hooks.js +282 -0
  231. package/dist/electron-build/bootstrap/lifecycle-hooks.js.map +1 -0
  232. package/dist/electron-build/bootstrap/memory-compressor.js +377 -0
  233. package/dist/electron-build/bootstrap/memory-compressor.js.map +1 -0
  234. package/dist/electron-build/bootstrap/persona-loader.js +219 -0
  235. package/dist/electron-build/bootstrap/persona-loader.js.map +1 -0
  236. package/dist/electron-build/bootstrap/project-context.js +162 -0
  237. package/dist/electron-build/bootstrap/project-context.js.map +1 -0
  238. package/dist/electron-build/bootstrap/project-state.js +205 -0
  239. package/dist/electron-build/bootstrap/project-state.js.map +1 -0
  240. package/dist/electron-build/bootstrap/session-window.js +162 -0
  241. package/dist/electron-build/bootstrap/session-window.js.map +1 -0
  242. package/dist/electron-build/bootstrap/vector-index.js +291 -0
  243. package/dist/electron-build/bootstrap/vector-index.js.map +1 -0
  244. package/dist/electron-build/constraint-runtime/src/tools/PolymarketSDK/cancelOrder.js +22 -0
  245. package/dist/electron-build/constraint-runtime/src/tools/PolymarketSDK/cancelOrder.js.map +1 -0
  246. package/dist/electron-build/constraint-runtime/src/tools/PolymarketSDK/clobShared.js +86 -0
  247. package/dist/electron-build/constraint-runtime/src/tools/PolymarketSDK/clobShared.js.map +1 -0
  248. package/dist/electron-build/constraint-runtime/src/tools/PolymarketSDK/createOrder.js +37 -0
  249. package/dist/electron-build/constraint-runtime/src/tools/PolymarketSDK/createOrder.js.map +1 -0
  250. package/dist/electron-build/constraint-runtime/src/tools/PolymarketSDK/getMarket.js +20 -0
  251. package/dist/electron-build/constraint-runtime/src/tools/PolymarketSDK/getMarket.js.map +1 -0
  252. package/dist/electron-build/constraint-runtime/src/tools/PolymarketSDK/getOrders.js +29 -0
  253. package/dist/electron-build/constraint-runtime/src/tools/PolymarketSDK/getOrders.js.map +1 -0
  254. package/dist/electron-build/constraint-runtime/src/tools/PolymarketSDK/listMarkets.js +28 -0
  255. package/dist/electron-build/constraint-runtime/src/tools/PolymarketSDK/listMarkets.js.map +1 -0
  256. package/dist/electron-build/constraint-runtime/src/tools/SafeSDK/deploySafe.js +10 -0
  257. package/dist/electron-build/constraint-runtime/src/tools/SafeSDK/deploySafe.js.map +1 -0
  258. package/dist/electron-build/constraint-runtime/src/tools/WalletTools/autoPay.js +68 -0
  259. package/dist/electron-build/constraint-runtime/src/tools/WalletTools/autoPay.js.map +1 -0
  260. package/dist/electron-build/constraint-runtime/src/tools/WalletTools/createWallet.js +14 -0
  261. package/dist/electron-build/constraint-runtime/src/tools/WalletTools/createWallet.js.map +1 -0
  262. package/dist/electron-build/constraint-runtime/src/tools/WalletTools/getBalance.js +17 -0
  263. package/dist/electron-build/constraint-runtime/src/tools/WalletTools/getBalance.js.map +1 -0
  264. package/dist/electron-build/constraint-runtime/src/tools/WalletTools/importWallet.js +59 -0
  265. package/dist/electron-build/constraint-runtime/src/tools/WalletTools/importWallet.js.map +1 -0
  266. package/dist/electron-build/constraint-runtime/src/tools/WalletTools/sendTransaction.js +29 -0
  267. package/dist/electron-build/constraint-runtime/src/tools/WalletTools/sendTransaction.js.map +1 -0
  268. package/dist/electron-build/constraint-runtime/src/tools/WalletTools/signMessage.js +14 -0
  269. package/dist/electron-build/constraint-runtime/src/tools/WalletTools/signMessage.js.map +1 -0
  270. package/dist/electron-build/constraint-runtime/src/tools/WalletTools/transferToken.js +29 -0
  271. package/dist/electron-build/constraint-runtime/src/tools/WalletTools/transferToken.js.map +1 -0
  272. package/dist/electron-build/constraints/index.js +11 -0
  273. package/dist/electron-build/constraints/index.js.map +1 -0
  274. package/dist/electron-build/context-compaction/auto-compact.js +182 -0
  275. package/dist/electron-build/context-compaction/auto-compact.js.map +1 -0
  276. package/dist/electron-build/context-compaction/budget-gate.js +32 -0
  277. package/dist/electron-build/context-compaction/budget-gate.js.map +1 -0
  278. package/dist/electron-build/context-compaction/budget-reduce.js +39 -0
  279. package/dist/electron-build/context-compaction/budget-reduce.js.map +1 -0
  280. package/dist/electron-build/context-compaction/context-collapse.js +71 -0
  281. package/dist/electron-build/context-compaction/context-collapse.js.map +1 -0
  282. package/dist/electron-build/context-compaction/index.js +38 -0
  283. package/dist/electron-build/context-compaction/index.js.map +1 -0
  284. package/dist/electron-build/context-compaction/microcompact.js +55 -0
  285. package/dist/electron-build/context-compaction/microcompact.js.map +1 -0
  286. package/dist/electron-build/context-compaction/pipeline.js +128 -0
  287. package/dist/electron-build/context-compaction/pipeline.js.map +1 -0
  288. package/dist/electron-build/context-compaction/snip.js +50 -0
  289. package/dist/electron-build/context-compaction/snip.js.map +1 -0
  290. package/dist/electron-build/context-compaction/token-estimator.js +40 -0
  291. package/dist/electron-build/context-compaction/token-estimator.js.map +1 -0
  292. package/dist/electron-build/context-compaction/types.js +21 -0
  293. package/dist/electron-build/context-compaction/types.js.map +1 -0
  294. package/dist/electron-build/cron/tick-lock.js +247 -0
  295. package/dist/electron-build/cron/tick-lock.js.map +1 -0
  296. package/dist/electron-build/documents/reader.js +131 -0
  297. package/dist/electron-build/documents/reader.js.map +1 -0
  298. package/dist/electron-build/documents/store.js +220 -0
  299. package/dist/electron-build/documents/store.js.map +1 -0
  300. package/dist/electron-build/external-engines/delegate-handle.js +76 -0
  301. package/dist/electron-build/external-engines/delegate-handle.js.map +1 -0
  302. package/dist/electron-build/external-engines/delegate.js +225 -0
  303. package/dist/electron-build/external-engines/delegate.js.map +1 -0
  304. package/dist/electron-build/external-engines/discovery.js +448 -0
  305. package/dist/electron-build/external-engines/discovery.js.map +1 -0
  306. package/dist/electron-build/external-engines/types.js +13 -0
  307. package/dist/electron-build/external-engines/types.js.map +1 -0
  308. package/dist/electron-build/hooks/hooks-engine.js +367 -0
  309. package/dist/electron-build/hooks/hooks-engine.js.map +1 -0
  310. package/dist/electron-build/judgeness/reflect.js +100 -0
  311. package/dist/electron-build/judgeness/reflect.js.map +1 -0
  312. package/dist/electron-build/judgeness/store.js +536 -0
  313. package/dist/electron-build/judgeness/store.js.map +1 -0
  314. package/dist/electron-build/judgeness/types.js +21 -0
  315. package/dist/electron-build/judgeness/types.js.map +1 -0
  316. package/dist/electron-build/llm/config-store.js +551 -0
  317. package/dist/electron-build/llm/config-store.js.map +1 -0
  318. package/dist/electron-build/llm/error-lessons.js +76 -0
  319. package/dist/electron-build/llm/error-lessons.js.map +1 -0
  320. package/dist/electron-build/llm/pi-ai.js +830 -0
  321. package/dist/electron-build/llm/pi-ai.js.map +1 -0
  322. package/dist/electron-build/llm/system-prompt/registry.js +318 -0
  323. package/dist/electron-build/llm/system-prompt/registry.js.map +1 -0
  324. package/dist/electron-build/llm/system-prompt/strip-hibsml.js +55 -0
  325. package/dist/electron-build/llm/system-prompt/strip-hibsml.js.map +1 -0
  326. package/dist/electron-build/locales/en.js +32 -0
  327. package/dist/electron-build/locales/en.js.map +1 -0
  328. package/dist/electron-build/locales/zh.js +30 -0
  329. package/dist/electron-build/locales/zh.js.map +1 -0
  330. package/dist/electron-build/lsp/lsp-manager.js +327 -0
  331. package/dist/electron-build/lsp/lsp-manager.js.map +1 -0
  332. package/dist/electron-build/lsp/lsp-tools.js +226 -0
  333. package/dist/electron-build/lsp/lsp-tools.js.map +1 -0
  334. package/dist/electron-build/migration/external-agent-migrator.js +451 -0
  335. package/dist/electron-build/migration/external-agent-migrator.js.map +1 -0
  336. package/dist/electron-build/network/agent-network.js +775 -0
  337. package/dist/electron-build/network/agent-network.js.map +1 -0
  338. package/dist/electron-build/network/goal-event-bridge.js +62 -0
  339. package/dist/electron-build/network/goal-event-bridge.js.map +1 -0
  340. package/dist/electron-build/network/iroh-integration.js +117 -0
  341. package/dist/electron-build/network/iroh-integration.js.map +1 -0
  342. package/dist/electron-build/network/iroh-transport.js +550 -0
  343. package/dist/electron-build/network/iroh-transport.js.map +1 -0
  344. package/dist/electron-build/network/p2p.js +1103 -0
  345. package/dist/electron-build/network/p2p.js.map +1 -0
  346. package/dist/electron-build/network/storage/adapters/json-adapter.js +416 -0
  347. package/dist/electron-build/network/storage/adapters/json-adapter.js.map +1 -0
  348. package/dist/electron-build/network/storage/types.js +18 -0
  349. package/dist/electron-build/network/storage/types.js.map +1 -0
  350. package/dist/electron-build/orbitdb/agent-tools.js +402 -0
  351. package/dist/electron-build/orbitdb/agent-tools.js.map +1 -0
  352. package/dist/electron-build/orbitdb/cid-database.js +284 -0
  353. package/dist/electron-build/orbitdb/cid-database.js.map +1 -0
  354. package/dist/electron-build/orbitdb/context-store.js +99 -0
  355. package/dist/electron-build/orbitdb/context-store.js.map +1 -0
  356. package/dist/electron-build/orbitdb/ipfs-node.js +120 -0
  357. package/dist/electron-build/orbitdb/ipfs-node.js.map +1 -0
  358. package/dist/electron-build/orbitdb/kanban-store.js +299 -0
  359. package/dist/electron-build/orbitdb/kanban-store.js.map +1 -0
  360. package/dist/electron-build/orbitdb/task-store.js +155 -0
  361. package/dist/electron-build/orbitdb/task-store.js.map +1 -0
  362. package/dist/electron-build/orbitdb/trajectory-store.js +194 -0
  363. package/dist/electron-build/orbitdb/trajectory-store.js.map +1 -0
  364. package/dist/electron-build/orbitdb/ui-cid.js +77 -0
  365. package/dist/electron-build/orbitdb/ui-cid.js.map +1 -0
  366. package/dist/electron-build/pi-ecosystem-a2ui/index.js +89 -0
  367. package/dist/electron-build/pi-ecosystem-a2ui/index.js.map +1 -0
  368. package/dist/electron-build/pi-ecosystem-goals/index.js +512 -0
  369. package/dist/electron-build/pi-ecosystem-goals/index.js.map +1 -0
  370. package/dist/electron-build/pi-ecosystem-judgment/adaptive-scan.js +325 -0
  371. package/dist/electron-build/pi-ecosystem-judgment/adaptive-scan.js.map +1 -0
  372. package/dist/electron-build/pi-ecosystem-judgment/causal-judge.js +492 -0
  373. package/dist/electron-build/pi-ecosystem-judgment/causal-judge.js.map +1 -0
  374. package/dist/electron-build/pi-ecosystem-judgment/cleanup.js +167 -0
  375. package/dist/electron-build/pi-ecosystem-judgment/cleanup.js.map +1 -0
  376. package/dist/electron-build/pi-ecosystem-judgment/decision.js +321 -0
  377. package/dist/electron-build/pi-ecosystem-judgment/decision.js.map +1 -0
  378. package/dist/electron-build/pi-ecosystem-judgment/detect-hook.js +175 -0
  379. package/dist/electron-build/pi-ecosystem-judgment/detect-hook.js.map +1 -0
  380. package/dist/electron-build/pi-ecosystem-judgment/distill-prompt.js +232 -0
  381. package/dist/electron-build/pi-ecosystem-judgment/distill-prompt.js.map +1 -0
  382. package/dist/electron-build/pi-ecosystem-judgment/distillation.js +304 -0
  383. package/dist/electron-build/pi-ecosystem-judgment/distillation.js.map +1 -0
  384. package/dist/electron-build/pi-ecosystem-judgment/evolve-judgment.js +176 -0
  385. package/dist/electron-build/pi-ecosystem-judgment/evolve-judgment.js.map +1 -0
  386. package/dist/electron-build/pi-ecosystem-judgment/human-value-pipeline.js +65 -0
  387. package/dist/electron-build/pi-ecosystem-judgment/human-value-pipeline.js.map +1 -0
  388. package/dist/electron-build/pi-ecosystem-judgment/human-value-store.js +808 -0
  389. package/dist/electron-build/pi-ecosystem-judgment/human-value-store.js.map +1 -0
  390. package/dist/electron-build/pi-ecosystem-judgment/index.js +508 -0
  391. package/dist/electron-build/pi-ecosystem-judgment/index.js.map +1 -0
  392. package/dist/electron-build/pi-ecosystem-judgment/injection-gate.js +334 -0
  393. package/dist/electron-build/pi-ecosystem-judgment/injection-gate.js.map +1 -0
  394. package/dist/electron-build/pi-ecosystem-judgment/monitor-gate.js +228 -0
  395. package/dist/electron-build/pi-ecosystem-judgment/monitor-gate.js.map +1 -0
  396. package/dist/electron-build/pi-ecosystem-judgment/value-injection.js +598 -0
  397. package/dist/electron-build/pi-ecosystem-judgment/value-injection.js.map +1 -0
  398. package/dist/electron-build/pi-ecosystem-mcp/index.js +643 -0
  399. package/dist/electron-build/pi-ecosystem-mcp/index.js.map +1 -0
  400. package/dist/electron-build/pi-ecosystem-mcp/ui-tools.js +116 -0
  401. package/dist/electron-build/pi-ecosystem-mcp/ui-tools.js.map +1 -0
  402. package/dist/electron-build/pi-ecosystem-subagents/index.js +352 -0
  403. package/dist/electron-build/pi-ecosystem-subagents/index.js.map +1 -0
  404. package/dist/electron-build/security/builtin-guards.js +133 -0
  405. package/dist/electron-build/security/builtin-guards.js.map +1 -0
  406. package/dist/electron-build/security/context-router-tool.js +111 -0
  407. package/dist/electron-build/security/context-router-tool.js.map +1 -0
  408. package/dist/electron-build/security/input-scanner.js +263 -0
  409. package/dist/electron-build/security/input-scanner.js.map +1 -0
  410. package/dist/electron-build/security/react-harness.js +148 -0
  411. package/dist/electron-build/security/react-harness.js.map +1 -0
  412. package/dist/electron-build/security/tool-gate.js +291 -0
  413. package/dist/electron-build/security/tool-gate.js.map +1 -0
  414. package/dist/electron-build/setup/setup-store.js +648 -0
  415. package/dist/electron-build/setup/setup-store.js.map +1 -0
  416. package/dist/electron-build/social/channels/ChannelManager.js +444 -0
  417. package/dist/electron-build/social/channels/ChannelManager.js.map +1 -0
  418. package/dist/electron-build/social/channels/DiapChannelBridge.js +451 -0
  419. package/dist/electron-build/social/channels/DiapChannelBridge.js.map +1 -0
  420. package/dist/electron-build/social/channels/InterestMatcher.js +135 -0
  421. package/dist/electron-build/social/channels/InterestMatcher.js.map +1 -0
  422. package/dist/electron-build/social/channels/types.js +21 -0
  423. package/dist/electron-build/social/channels/types.js.map +1 -0
  424. package/dist/electron-build/social/global-shared-context.js +368 -0
  425. package/dist/electron-build/social/global-shared-context.js.map +1 -0
  426. package/dist/electron-build/social/heartbeat.js +882 -0
  427. package/dist/electron-build/social/heartbeat.js.map +1 -0
  428. package/dist/electron-build/storage/did-catalog-bridge.js +314 -0
  429. package/dist/electron-build/storage/did-catalog-bridge.js.map +1 -0
  430. package/dist/electron-build/storage/did-catalog.js +272 -0
  431. package/dist/electron-build/storage/did-catalog.js.map +1 -0
  432. package/dist/electron-build/utils/auto-update.js +140 -558
  433. package/dist/electron-build/utils/auto-update.js.map +1 -1
  434. package/dist/electron-build/utils/module-context.js +98 -0
  435. package/dist/electron-build/utils/module-context.js.map +1 -0
  436. package/dist/electron-build/utils/runtime-bootstrap.js +745 -0
  437. package/dist/electron-build/utils/runtime-bootstrap.js.map +1 -0
  438. package/dist/electron-build/utils/update-manager.js +770 -0
  439. package/dist/electron-build/utils/update-manager.js.map +1 -0
  440. package/dist/electron-build/utils/update-state.js +406 -0
  441. package/dist/electron-build/utils/update-state.js.map +1 -0
  442. package/dist/electron-build/utils/version-info.js +562 -0
  443. package/dist/electron-build/utils/version-info.js.map +1 -0
  444. package/dist/electron-build/web/i18n.js +146 -0
  445. package/dist/electron-build/web/i18n.js.map +1 -0
  446. package/dist/electron-build/web/routes-x402-info.js +232 -0
  447. package/dist/electron-build/web/routes-x402-info.js.map +1 -0
  448. package/dist/electron-build/web/server-storage.js +390 -0
  449. package/dist/electron-build/web/server-storage.js.map +1 -0
  450. package/dist/electron-build/web/server-types.js +52 -0
  451. package/dist/electron-build/web/server-types.js.map +1 -0
  452. package/dist/electron-build/web/task-deps.js +41 -0
  453. package/dist/electron-build/web/task-deps.js.map +1 -0
  454. package/dist/index.js +58 -27
  455. package/dist/llm/system-prompt/registry.js +6 -3
  456. package/dist/security/tool-gate.js +4 -0
  457. package/dist/utils/auto-update.js +138 -525
  458. package/dist/utils/module-context.js +59 -0
  459. package/dist/utils/runtime-bootstrap.js +682 -0
  460. package/dist/utils/update-cli.js +82 -0
  461. package/dist/utils/update-health.js +401 -0
  462. package/dist/utils/update-manager.js +716 -0
  463. package/dist/utils/update-state.js +353 -0
  464. package/dist/utils/version-info.js +510 -0
  465. package/dist/web/mobile-contacts.js +329 -0
  466. package/dist/web/mobile-core.js +481 -15
  467. package/dist/web/mobile.html +32 -0
  468. package/dist/web/mobile.js +139 -0
  469. package/dist/web/routes-contacts.js +420 -0
  470. package/dist/web/server.js +5 -0
  471. package/package.json +1 -1
  472. package/scripts/build-cli.js +32 -151
  473. package/scripts/postinstall.js +153 -76
@@ -0,0 +1,264 @@
1
+ /**
2
+ * contacts/policy.ts — 联系方式调用策略门 (2026-09-19, Phase 6)
3
+ *
4
+ * 联系方式是高风险动作, **不许直接暴露给 Agent**。调用链固定:
5
+ * Agent → PiAgentHarness → contact policy → consent → recipient → rate → preview → provider → send → evidence
6
+ *
7
+ * 本文件只做"能不能发/要不要人批"的判定 + 可审计预览, 不碰 provider、不写盘。
8
+ * 判定顺序固定, 每种拒绝都给出**机器可读的 blockKind** (供测试与审计), 不返回泛泛的"不允许"。
9
+ */
10
+ import { readGoal } from '../goal-store.js';
11
+ import { evaluateGrant, categoriesOf } from './grants.js';
12
+ import { scanForbidden } from './types.js';
13
+ import { scanSensitive } from './types.js';
14
+ const CHANNEL_LABEL = {
15
+ 'local-sink': '本地落盘 (未真实外发)',
16
+ 'http-webhook': '短信网关 HTTP (真实外发)',
17
+ smtp: 'SMTP 邮件 (真实外发)',
18
+ };
19
+ function reallySentFor(provider) {
20
+ return provider === 'smtp' || provider === 'http-webhook';
21
+ }
22
+ /** 任务级 auto_send 授权: 用户明确给某个 Goal 开过才行 (默认没有) */
23
+ export function taskAutoAuthorized(goal, contact) {
24
+ if (!goal)
25
+ return false;
26
+ const flag = goal.continuation?.autoSendContacts;
27
+ if (flag !== true)
28
+ return false;
29
+ if (contact.policy !== 'auto_send' && contact.policy !== 'send_after_approval')
30
+ return false;
31
+ if (contact.limits.requireApprovalEachTime)
32
+ return false;
33
+ return true;
34
+ }
35
+ export async function decideContactAction(input, ctx) {
36
+ const now = ctx.now ? ctx.now() : Date.now();
37
+ const base = {
38
+ allowed: false, requiresApproval: false, sensitive: [], firstContact: false, reallySent: false,
39
+ authorizationMode: 'one_time', approvalSkipped: false, policyDecision: 'not_evaluated', forbiddenCategories: [],
40
+ };
41
+ if (input.action === 'list')
42
+ return { ...base, allowed: true };
43
+ if (!input.contactId)
44
+ return { ...base, blockKind: 'not_found', reason: '缺少 contactId (禁止按模糊姓名自动发送)' };
45
+ const contact = await ctx.store.getContact(input.contactId);
46
+ if (!contact)
47
+ return { ...base, blockKind: 'not_found', reason: `contactId=${input.contactId} 不存在` };
48
+ // 1) 验证状态
49
+ if (contact.revokedAt || contact.verificationStatus === 'revoked') {
50
+ return { ...base, blockKind: 'consent_revoked', reason: '授权已被撤销 —— 历史证据保留, 但后续调用一律拒绝' };
51
+ }
52
+ if (contact.verificationStatus === 'blocked')
53
+ return { ...base, blockKind: 'contact_blocked', reason: '该联系方式已被标记 blocked' };
54
+ if (contact.verificationStatus === 'expired')
55
+ return { ...base, blockKind: 'verification_expired', reason: '验证已过期, 需重新验证' };
56
+ if (contact.verificationStatus !== 'verified') {
57
+ return { ...base, blockKind: 'unverified_contact', reason: `未验证的联系方式不能用于真实发送 (当前 ${contact.verificationStatus})` };
58
+ }
59
+ // 2) 通道是否配置好
60
+ const reallySent = reallySentFor(contact.provider);
61
+ if (reallySent && contact.secretRef && !(await ctx.store.secrets.has(contact.secretRef))) {
62
+ return { ...base, reallySent, blockKind: 'provider_not_configured', reason: `通道 ${contact.provider} 需要 secretRef=${contact.secretRef}, 但 Secret Store 里没有` };
63
+ }
64
+ // 3) 批量永远禁止
65
+ const recipients = input.recipients || [input.contactId];
66
+ if (recipients.length > 1) {
67
+ return { ...base, reallySent, blockKind: 'batch_forbidden', reason: `批量发送永远禁止 (收到 ${recipients.length} 个收件人)` };
68
+ }
69
+ // 4) 正常发送才要求正文
70
+ if ((input.action === 'send' || input.action === 'preview') && !input.body) {
71
+ return { ...base, reallySent, blockKind: 'missing_content', reason: '缺少正文' };
72
+ }
73
+ // 7) 频率限制
74
+ const limits = contact.limits || { dailyMax: 5, perTaskMax: 3, requireApprovalEachTime: false };
75
+ const today = await ctx.store.countSentToday(contact.contactId, now);
76
+ const forGoal = input.goalId ? await ctx.store.countSentForGoal(input.goalId) : 0;
77
+ const usage = { today, todayMax: limits.dailyMax, forGoal, goalMax: limits.perTaskMax };
78
+ // 7.5) 持久授权判定 (Phase 5): Grant 是否存在/有效/覆盖 channel·contact·task·content
79
+ const forbidden = scanForbidden(`${input.subject || ''}\n${input.body || ''}`);
80
+ base.forbiddenCategories = forbidden;
81
+ const sensitiveEarly = scanSensitive(`${input.subject || ''}\n${input.body || ''}`);
82
+ const scope0 = contact.consentScope || { kinds: [], taskRefs: [], grantedAt: '', grantedBy: '' };
83
+ const limits0 = contact.limits || { dailyMax: 5, perTaskMax: 3, requireApprovalEachTime: false };
84
+ const usage0 = {
85
+ today: await ctx.store.countSentToday(contact.contactId, now),
86
+ todayMax: limits0.dailyMax,
87
+ forGoal: input.goalId ? await ctx.store.countSentForGoal(input.goalId) : 0,
88
+ goalMax: limits0.perTaskMax,
89
+ };
90
+ let grantEval = null;
91
+ if (ctx.grants?.ready)
92
+ await ctx.grants.ready();
93
+ if (ctx.grants?.corrupt) {
94
+ // Grant 存储读不出来 → **拒绝自动发送**, 且大声说出来 (不静默当成"无权限"或"已授权")
95
+ base.grantBlock = 'grant_store_unreadable';
96
+ base.policyDecision = 'grant_store_unreadable:refuse_auto_send';
97
+ base.authorizationMode = 'one_time';
98
+ return { ...base, reallySent, usage: usage0, allowed: true, requiresApproval: true,
99
+ approvalReason: 'policy',
100
+ reason: '联系方式授权文件损坏/不可读 → 已拒绝自动发送, 需要人工批准并修复 grants.json (不会静默当成无权限或已授权)' };
101
+ }
102
+ if (ctx.grants) {
103
+ // 优先拿"管辖 Grant"(含撤销/暂停) 以给出精确原因; 拿不到再退到 activeFor
104
+ const g = ctx.grants.latestFor
105
+ ? await ctx.grants.latestFor(contact.ownerDid)
106
+ : await ctx.grants.activeFor(contact.ownerDid, now);
107
+ grantEval = evaluateGrant(g, {
108
+ ownerDid: contact.ownerDid,
109
+ channel: contact.kind,
110
+ contactId: contact.contactId,
111
+ contactVerified: contact.verificationStatus === 'verified',
112
+ contactRevoked: !!contact.revokedAt,
113
+ taskBoundToGoal: !!input.goalId && scope0.taskRefs.includes(input.goalId),
114
+ goalId: input.goalId,
115
+ goalMatchesGrant: ctx.goalMatchesGrant,
116
+ sensitiveCategories: categoriesOf(sensitiveEarly),
117
+ hasForbiddenContent: forbidden.length > 0,
118
+ deviceTrusted: ctx.deviceTrusted,
119
+ }, now);
120
+ if (grantEval.grant)
121
+ base.grant = { grantId: grantEval.grant.grantId, level: grantEval.grant.level, version: grantEval.grant.grantVersion };
122
+ base.authorizationMode = grantEval.authorizationMode;
123
+ base.approvalSkipped = grantEval.approvalSkipped;
124
+ base.policyDecision = grantEval.policyDecision;
125
+ if (!grantEval.ok) {
126
+ // 硬拒 (撤销/暂停/范围外/禁放行类别/设备不可信) → 直接不允许
127
+ // 例外: grant_missing / grant_suspended / grant_sensitive_content_denied → 退回人工批准 (不是"不允许")
128
+ const soft = grantEval.needsHumanApproval && (grantEval.block === 'grant_missing' || grantEval.block === 'grant_suspended' || grantEval.block === 'grant_sensitive_content_denied');
129
+ base.grantBlock = grantEval.block;
130
+ base.grantMissing = grantEval.block === 'grant_missing';
131
+ if (!soft) {
132
+ return { ...base, reallySent, usage: usage0,
133
+ blockKind: grantEval.block === 'forbidden_content_category' ? 'forbidden_content_category' : 'grant_denied',
134
+ reason: grantEval.reason };
135
+ }
136
+ base.approvalReason = grantEval.block === 'grant_sensitive_content_denied' ? 'sensitive_content' : 'policy';
137
+ }
138
+ }
139
+ // 8) contact 级硬禁 (与任务无关): draft_only
140
+ if (contact.policy === 'draft_only' && input.action === 'send') {
141
+ return { ...base, reallySent, usage, blockKind: 'policy_draft_only', reason: '该联系方式策略为 draft_only (只允许起草, 不允许发送)' };
142
+ }
143
+ // 8.5) 同一 requestId 不得重复发送 (幂等)
144
+ if (input.action === 'send' && input.requestId) {
145
+ const prior = await ctx.store.findSend(input.requestId);
146
+ if (prior && prior.status === 'sent') {
147
+ return { ...base, reallySent, usage, blockKind: 'duplicate_request', reason: `requestId=${input.requestId} 已经发送过 (状态 ${prior.status}), 不重复发送` };
148
+ }
149
+ if (ctx.hasPendingConsent && await ctx.hasPendingConsent(input.requestId)) {
150
+ return { ...base, reallySent, usage, blockKind: 'duplicate_request', reason: `requestId=${input.requestId} 已有待批准请求, 不重复排队` };
151
+ }
152
+ }
153
+ // 8.6) 频率限制 (contact 级 + 任务级)
154
+ if (input.action === 'send' && today >= limits.dailyMax) {
155
+ return { ...base, reallySent, usage, blockKind: 'rate_limited', reason: `今日已发 ${today}/${limits.dailyMax} 条, 超出发送频率` };
156
+ }
157
+ if (input.action === 'send' && input.goalId && forGoal >= limits.perTaskMax) {
158
+ return { ...base, reallySent, usage, blockKind: 'rate_limited', reason: `本任务已发 ${forGoal}/${limits.perTaskMax} 条, 超出任务上限` };
159
+ }
160
+ // 8.7) 任务边界: 是否绑定到这个任务 (发送必须有任务归属)
161
+ let goal = null;
162
+ if (input.goalId) {
163
+ goal = await readGoal(input.goalId);
164
+ if (!goal)
165
+ return { ...base, reallySent, blockKind: 'not_bound_to_task', reason: `goalId=${input.goalId} 不存在` };
166
+ if (goal.status === 'completed' || goal.status === 'failed') {
167
+ return { ...base, reallySent, usage, blockKind: 'goal_closed', reason: `任务已 ${goal.status}, 不允许再对外发送` };
168
+ }
169
+ }
170
+ const scope = contact.consentScope || { kinds: [], taskRefs: [], grantedAt: '', grantedBy: '' };
171
+ const boundToTask = !!input.goalId && (scope.taskRefs.includes(input.goalId) || (!!input.taskKind && scope.kinds.includes(input.taskKind)) || scope.kinds.includes('any'));
172
+ // "范围被授权覆盖" = 授权有效, 或只是内容敏感/暂停这类**软**情形 (软情形退回一次性人工批准,
173
+ // 而不是报"未绑定任务"这种会误导人的原因)
174
+ const SOFT_GRANT_COVER = new Set(['grant_sensitive_content_denied', 'grant_suspended']);
175
+ const coveredByGrant = !!(grantEval && (grantEval.ok || (!!grantEval.block && SOFT_GRANT_COVER.has(grantEval.block))));
176
+ if (input.action === 'send' && !boundToTask && !coveredByGrant) {
177
+ return { ...base, reallySent, usage, blockKind: 'not_bound_to_task',
178
+ reason: `该联系方式没有授权给任务 ${input.goalId || '(未指定)'} (未绑定任务联系人, 也没有覆盖它的长期授权)` };
179
+ }
180
+ // 9) 需要人工批准的几种情况
181
+ const sensitive = sensitiveEarly;
182
+ const firstContact = !(ctx.hasSentBefore ? await ctx.hasSentBefore(contact.contactId) : (await ctx.store.listSends()).some((s) => s.contactId === contact.contactId && s.status === 'sent'));
183
+ let requiresApproval = false;
184
+ let approvalReason;
185
+ // 持久授权命中且允许自动发送 → 跳过人工批准 (但下面的检查一个都不少)
186
+ if (grantEval && grantEval.ok && grantEval.approvalSkipped) {
187
+ requiresApproval = false;
188
+ approvalReason = undefined;
189
+ }
190
+ else if (firstContact && !(grantEval && grantEval.needsHumanApproval)) {
191
+ requiresApproval = true;
192
+ approvalReason = 'first_contact';
193
+ }
194
+ else if (grantEval && !grantEval.ok && grantEval.needsHumanApproval) {
195
+ requiresApproval = true;
196
+ approvalReason = base.approvalReason || 'policy';
197
+ }
198
+ else if (sensitive.length > 0) {
199
+ requiresApproval = true;
200
+ approvalReason = 'sensitive_content';
201
+ }
202
+ else if (limits.requireApprovalEachTime) {
203
+ requiresApproval = true;
204
+ approvalReason = 'each_time';
205
+ }
206
+ else if (contact.policy === 'send_after_approval' && !taskAutoAuthorized(goal, contact)) {
207
+ requiresApproval = true;
208
+ approvalReason = 'policy';
209
+ }
210
+ return {
211
+ allowed: true,
212
+ requiresApproval,
213
+ approvalReason,
214
+ sensitive,
215
+ firstContact,
216
+ reallySent,
217
+ usage,
218
+ grant: base.grant,
219
+ grantBlock: base.grantBlock,
220
+ grantMissing: base.grantMissing,
221
+ authorizationMode: base.authorizationMode,
222
+ approvalSkipped: requiresApproval ? false : base.approvalSkipped,
223
+ policyDecision: base.policyDecision,
224
+ forbiddenCategories: base.forbiddenCategories,
225
+ preview: buildPreview({ contact, input, reallySent, sensitive, firstContact, requiresApproval, approvalReason, usage, goal }),
226
+ };
227
+ }
228
+ export function buildPreview(b) {
229
+ const name = (b.contact.aliases && b.contact.aliases[0]) || b.contact.displayValue;
230
+ return {
231
+ contactName: name,
232
+ channel: b.contact.kind,
233
+ provider: b.contact.provider,
234
+ channelLabel: CHANNEL_LABEL[b.contact.provider] || b.contact.provider,
235
+ reallySent: b.reallySent,
236
+ recipient: b.contact.displayValue, // 只有脱敏值
237
+ subject: b.input.subject,
238
+ goalId: b.input.goalId,
239
+ goalObjective: b.goal?.objective,
240
+ permission: '单条发送 (批量永远禁止)',
241
+ usage: b.usage,
242
+ replyExpected: undefined,
243
+ firstContact: b.firstContact,
244
+ requiresApproval: b.requiresApproval,
245
+ approvalReason: b.approvalReason,
246
+ sensitive: b.sensitive.map((s) => `${s.kind}: ${s.hint}`),
247
+ lines: [
248
+ '将联系:',
249
+ ` 联系人: ${name}`,
250
+ ` 通道: ${b.contact.kind === 'phone' ? '手机号' : '邮箱'} · ${CHANNEL_LABEL[b.contact.provider] || b.contact.provider}`,
251
+ ` 收件人: ${b.contact.displayValue} (脱敏)`,
252
+ b.input.subject ? ` 主题: ${b.input.subject}` : ' 主题: (无)',
253
+ ` 任务: ${b.input.goalId || '(未绑定任务)'}${b.goal?.objective ? ` — ${String(b.goal.objective).slice(0, 60)}` : ''}`,
254
+ ` 权限: 单条发送 (今日 ${b.usage.today}/${b.usage.todayMax} · 本任务 ${b.usage.forGoal}/${b.usage.goalMax})`,
255
+ ` 首次联系: ${b.firstContact ? '是 (必须人工批准)' : '否'}`,
256
+ ` 敏感内容: ${b.sensitive.length ? b.sensitive.map((s) => s.kind).join(', ') + ' (必须人工批准)' : '无'}`,
257
+ ` 是否需要批准: ${b.requiresApproval ? `是 (${b.approvalReason || 'policy'})` : '否'}`,
258
+ ],
259
+ };
260
+ }
261
+ /** 人读渲染 (CLI / Web 卡片 / 审批流程都用这一份) */
262
+ export function renderPreview(p) {
263
+ return p.lines.join('\n');
264
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * contacts/preview-types.ts — 预览/限额的共享类型 (2026-09-19)
3
+ *
4
+ * 单独一个文件是为了断开 types.ts ↔ policy.ts 的循环引用: 两边都要用这两个形状。
5
+ */
6
+ export {};
@@ -0,0 +1,341 @@
1
+ /**
2
+ * contacts/providers.ts — 通道适配器 + 验证码挑战 (2026-09-19, Phase 2/3)
3
+ *
4
+ * 三种通道, 各自**如实**:
5
+ * local-sink 本地落盘 (outbox/*.json) —— 真写文件, 但**没有真外发**; 预览/证据里必须
6
+ * 标成"本地落盘(非真实外发)", 不许让用户以为对方收到了。
7
+ * http-webhook 真 HTTP POST (短信网关/自建服务), 需要 secretRef (Bearer token)
8
+ * smtp 真 SMTP 会话 (net/tls, AUTH LOGIN), 需要 secretRef (host/port/user/pass/from)
9
+ *
10
+ * 未配置 = 明确报 `provider_not_configured` 并拒绝发送, **绝不假装成功**。
11
+ *
12
+ * 验证码挑战 (Phase 2/3):
13
+ * 验证码只以 sha256(salt+code) 落盘, 明文只在"投递那一刻"存在; 有次数上限、有过期、一次性。
14
+ */
15
+ import * as fsp from 'fs/promises';
16
+ import * as net from 'net';
17
+ import * as tls from 'tls';
18
+ import * as path from 'path';
19
+ import * as crypto from 'crypto';
20
+ // ── 本地落盘 (真写文件, 但明确不是真外发) ────────────────────────────────────
21
+ export const localSinkProvider = {
22
+ id: 'local-sink',
23
+ kind: 'phone', // kind 仅作声明; 实际两种都能落
24
+ emulatesRealSend: false,
25
+ async configured() { return true; },
26
+ async deliver(msg, ctx) {
27
+ const file = path.join(ctx.store.dir, 'outbox', `${msg.requestId}.json`);
28
+ await fsp.mkdir(path.dirname(file), { recursive: true });
29
+ await fsp.writeFile(file, JSON.stringify({
30
+ // 明文收件人只落在这个本地文件里 (它就是"发件箱"), 不进 evidence/ledger
31
+ to: msg.to, kind: msg.kind, subject: msg.subject, body: msg.body,
32
+ threadToken: msg.threadToken, requestId: msg.requestId, at: new Date().toISOString(),
33
+ channel: 'local-sink', note: '本地落盘通道: 未真实外发 (仅供开发/验收)',
34
+ }, null, 2), 'utf8');
35
+ return { ok: true, providerMessageId: `local-${msg.requestId}`, reallySent: false };
36
+ },
37
+ };
38
+ // ── HTTP 网关 (真外发, 需要 secret) ─────────────────────────────────────────
39
+ export const httpWebhookProvider = {
40
+ id: 'http-webhook',
41
+ kind: 'phone',
42
+ emulatesRealSend: true,
43
+ async configured(store, secretRef) { return !!(secretRef && await store.secrets.has(secretRef)); },
44
+ async deliver(msg, ctx) {
45
+ const secret = await ctx.store.secrets.get(ctx.secretRef);
46
+ if (!secret)
47
+ return { ok: false, reallySent: false, error: `provider_not_configured: 缺少 secretRef=${ctx.secretRef}`, errorClass: 'provider_not_configured' };
48
+ let cfg;
49
+ try {
50
+ cfg = JSON.parse(secret.value);
51
+ }
52
+ catch {
53
+ return { ok: false, reallySent: false, error: 'secret 不是合法 JSON', errorClass: 'provider_not_configured' };
54
+ }
55
+ if (!cfg?.endpoint)
56
+ return { ok: false, reallySent: false, error: 'secret 缺 endpoint', errorClass: 'provider_not_configured' };
57
+ try {
58
+ const res = await fetch(cfg.endpoint, {
59
+ method: 'POST',
60
+ headers: { 'content-type': 'application/json', ...(cfg.token ? { authorization: `Bearer ${cfg.token}` } : {}) },
61
+ body: JSON.stringify({ to: msg.to, text: msg.body, threadToken: msg.threadToken, requestId: msg.requestId }),
62
+ });
63
+ const text = await res.text().catch(() => '');
64
+ if (!res.ok) {
65
+ const cls = res.status === 401 || res.status === 403 ? 'provider_auth_failed' : 'provider_rejected';
66
+ return { ok: false, reallySent: false, error: `HTTP ${res.status}: ${text.slice(0, 120)}`, errorClass: cls };
67
+ }
68
+ let id = '';
69
+ try {
70
+ id = String(JSON.parse(text)?.id || JSON.parse(text)?.sid || '');
71
+ }
72
+ catch { /* 网关没给 id 就不编 */ }
73
+ return { ok: true, reallySent: true, providerMessageId: id || `http-${msg.requestId}` };
74
+ }
75
+ catch (err) {
76
+ return { ok: false, reallySent: false, error: String(err?.message || err), errorClass: 'provider_unreachable' };
77
+ }
78
+ },
79
+ };
80
+ class SmtpSession {
81
+ sock;
82
+ buf = '';
83
+ /** 已收但还没被读走的完整行 —— 必须排队, 不能"没人等就丢" */
84
+ lines = [];
85
+ waiters = [];
86
+ closed = false;
87
+ constructor(sock) {
88
+ this.sock = sock;
89
+ sock.setEncoding('utf8');
90
+ sock.on('data', (d) => {
91
+ this.buf += d;
92
+ let idx;
93
+ while ((idx = this.buf.indexOf('\r\n')) >= 0) {
94
+ const line = this.buf.slice(0, idx);
95
+ this.buf = this.buf.slice(idx + 2);
96
+ const w = this.waiters.shift();
97
+ if (w)
98
+ w(line);
99
+ else
100
+ this.lines.push(line); // 2026-09-19: 排队而不是丢弃
101
+ }
102
+ });
103
+ sock.on('error', (err) => this.fail(new Error(err?.message || 'smtp_socket_error')));
104
+ sock.on('close', () => this.fail(new Error('smtp_connection_closed')));
105
+ }
106
+ fail(err) {
107
+ this.closed = true;
108
+ const ws = this.waiters;
109
+ this.waiters = [];
110
+ for (const w of ws)
111
+ w(`__ERROR__${err.message}`);
112
+ }
113
+ /**
114
+ * 读一条"完整"应答 (SMTP 多行应答: `250-续行` 到 `250 最后一行` 才算完)。
115
+ * 2026-09-19 真跑抓到: 一条 chunk 里带多行时, 早先的实现只喂第一个等待者、**其余行被丢掉** →
116
+ * 客户端死等超时 (表现就是"一发 EHLO 就 smtp_timeout")。
117
+ */
118
+ async readReply(timeoutMs = 15000) {
119
+ const lines = [];
120
+ const deadline = Date.now() + timeoutMs;
121
+ while (Date.now() < deadline) {
122
+ const line = await this.nextLine(deadline - Date.now());
123
+ if (line.startsWith('__ERROR__'))
124
+ throw new Error(line.slice('__ERROR__'.length));
125
+ lines.push(line);
126
+ if (/^\d{3} /.test(line))
127
+ break; // 空格 = 最后一行
128
+ }
129
+ return lines.join('\n');
130
+ }
131
+ nextLine(timeoutMs) {
132
+ if (this.lines.length)
133
+ return Promise.resolve(this.lines.shift());
134
+ if (this.closed)
135
+ return Promise.reject(new Error('smtp_connection_closed'));
136
+ return new Promise((resolve, reject) => {
137
+ const t = setTimeout(() => {
138
+ this.waiters = this.waiters.filter((w) => w !== fn);
139
+ reject(new Error('smtp_timeout'));
140
+ }, Math.max(1, timeoutMs));
141
+ const fn = (l) => { clearTimeout(t); resolve(l); };
142
+ this.waiters.push(fn);
143
+ });
144
+ }
145
+ async cmd(text) {
146
+ this.sock.write(text + '\r\n');
147
+ return this.readReply();
148
+ }
149
+ write(text) { this.sock.write(text); }
150
+ end() { try {
151
+ this.sock.end();
152
+ }
153
+ catch { /* 已断 */ } }
154
+ }
155
+ function classifySmtpError(err, code) {
156
+ const msg = String(err?.message || err || '').toLowerCase();
157
+ if (msg.includes('smtp_timeout'))
158
+ return 'provider_unreachable';
159
+ if (msg.includes('econnrefused') || msg.includes('etimedout') || msg.includes('enotfound') || code === 'ECONNREFUSED')
160
+ return 'provider_unreachable';
161
+ return 'unknown';
162
+ }
163
+ /** 真 SMTP 发送 (net / tls, AUTH LOGIN)。信封里的收件人只在这里出现。 */
164
+ export async function smtpDeliver(cfg, msg) {
165
+ let sock;
166
+ let s;
167
+ try {
168
+ sock = cfg.secure
169
+ ? tls.connect({ host: cfg.host, port: cfg.port, servername: cfg.tlsServername || cfg.host })
170
+ : net.connect({ host: cfg.host, port: cfg.port });
171
+ // 2026-09-19: **先挂监听再等连接** —— 服务器可能在 TCP 建好那一刻就把 "220 ..." 发过来,
172
+ // 监听器挂晚了会丢问候语, 表现是"客户端干等到超时"(真跑 SMTP 时抓到过)。
173
+ s = new SmtpSession(sock);
174
+ await new Promise((resolve, reject) => {
175
+ const onErr = (e) => reject(e);
176
+ if (sock.connecting === false) {
177
+ resolve();
178
+ return;
179
+ }
180
+ sock.once('error', onErr);
181
+ const ev = cfg.secure ? 'secureConnect' : 'connect';
182
+ sock.once(ev, () => { sock.off('error', onErr); resolve(); });
183
+ setTimeout(() => reject(new Error('connect_timeout')), 12000).unref?.();
184
+ });
185
+ }
186
+ catch (err) {
187
+ return { ok: false, reallySent: false, error: String(err?.message || err), errorClass: 'provider_unreachable' };
188
+ }
189
+ try {
190
+ const greet = await s.readReply();
191
+ if (!/^220/.test(greet))
192
+ return { ok: false, reallySent: false, error: `SMTP 问候异常: ${greet.slice(0, 80)}`, errorClass: 'provider_rejected' };
193
+ const ehlo = await s.cmd(`EHLO bolloon.local`);
194
+ if (!/^250/.test(ehlo))
195
+ return { ok: false, reallySent: false, error: `EHLO 被拒: ${ehlo.slice(0, 80)}`, errorClass: 'provider_rejected' };
196
+ if (cfg.user && cfg.pass) {
197
+ const a1 = await s.cmd('AUTH LOGIN');
198
+ if (!/^334/.test(a1))
199
+ return { ok: false, reallySent: false, error: `AUTH LOGIN 不支持: ${a1.slice(0, 80)}`, errorClass: 'provider_auth_failed' };
200
+ const a2 = await s.cmd(Buffer.from(cfg.user).toString('base64'));
201
+ if (!/^334/.test(a2))
202
+ return { ok: false, reallySent: false, error: `用户名被拒: ${a2.slice(0, 80)}`, errorClass: 'provider_auth_failed' };
203
+ const a3 = await s.cmd(Buffer.from(cfg.pass).toString('base64'));
204
+ if (!/^235/.test(a3))
205
+ return { ok: false, reallySent: false, error: `认证失败: ${a3.slice(0, 80)}`, errorClass: 'provider_auth_failed' };
206
+ }
207
+ const mf = await s.cmd(`MAIL FROM:<${cfg.from}>`);
208
+ if (!/^250/.test(mf))
209
+ return { ok: false, reallySent: false, error: `MAIL FROM 被拒: ${mf.slice(0, 80)}`, errorClass: 'provider_rejected' };
210
+ const rc = await s.cmd(`RCPT TO:<${msg.to}>`);
211
+ if (!/^250/.test(rc))
212
+ return { ok: false, reallySent: false, error: `RCPT TO 被拒: ${rc.slice(0, 80)}`, errorClass: 'invalid_recipient' };
213
+ const dt = await s.cmd('DATA');
214
+ if (!/^354/.test(dt))
215
+ return { ok: false, reallySent: false, error: `DATA 被拒: ${dt.slice(0, 80)}`, errorClass: 'provider_rejected' };
216
+ const body = msg.body.replace(/\r?\n/g, '\r\n').replace(/^\./gm, '..');
217
+ const headers = [
218
+ `From: ${msg.fromName ? `"${msg.fromName}" <${cfg.from}>` : cfg.from}`,
219
+ `To: <${msg.to}>`,
220
+ `Subject: ${msg.subject || '(无主题)'}`,
221
+ `Date: ${new Date().toUTCString()}`,
222
+ `Message-ID: <${msg.requestId}@bolloon>`,
223
+ `X-Bolloon-Thread: ${msg.threadToken}`,
224
+ `X-Bolloon-Request: ${msg.requestId}`,
225
+ 'MIME-Version: 1.0',
226
+ 'Content-Type: text/plain; charset=utf-8',
227
+ ].join('\r\n');
228
+ const sent = await s.cmd(`${headers}\r\n\r\n${body}\r\n.`);
229
+ if (!/^250/.test(sent))
230
+ return { ok: false, reallySent: false, error: `发送被拒: ${sent.slice(0, 120)}`, errorClass: 'provider_rejected' };
231
+ s.cmd('QUIT').catch(() => { });
232
+ const id = (sent.match(/250[ -](?:2\.0\.0\s+)?(?:Ok\s+)?(?:queued as\s+)?(\S+)/i)?.[1]) || `smtp-${msg.requestId}`;
233
+ return { ok: true, reallySent: true, providerMessageId: id };
234
+ }
235
+ catch (err) {
236
+ return { ok: false, reallySent: false, error: String(err?.message || err), errorClass: classifySmtpError(err, err?.code) };
237
+ }
238
+ finally {
239
+ s.end();
240
+ }
241
+ }
242
+ export const smtpProvider = {
243
+ id: 'smtp',
244
+ kind: 'email',
245
+ emulatesRealSend: true,
246
+ async configured(store, secretRef) { return !!(secretRef && await store.secrets.has(secretRef)); },
247
+ async deliver(msg, ctx) {
248
+ const secret = await ctx.store.secrets.get(ctx.secretRef);
249
+ if (!secret)
250
+ return { ok: false, reallySent: false, error: `provider_not_configured: 缺少 secretRef=${ctx.secretRef}`, errorClass: 'provider_not_configured' };
251
+ let cfg;
252
+ try {
253
+ cfg = JSON.parse(secret.value);
254
+ }
255
+ catch {
256
+ return { ok: false, reallySent: false, error: 'secret 不是合法 JSON', errorClass: 'provider_not_configured' };
257
+ }
258
+ if (!cfg?.host || !cfg?.from)
259
+ return { ok: false, reallySent: false, error: 'secret 缺 host/from', errorClass: 'provider_not_configured' };
260
+ return smtpDeliver({ ...cfg, port: cfg.port || 587 }, msg);
261
+ },
262
+ };
263
+ const PROVIDERS = {
264
+ 'local-sink': localSinkProvider,
265
+ 'http-webhook': httpWebhookProvider,
266
+ smtp: smtpProvider,
267
+ };
268
+ export function getProvider(id) {
269
+ return PROVIDERS[id] || null;
270
+ }
271
+ export function listProviders() {
272
+ return Object.values(PROVIDERS).map((p) => ({ id: p.id, kind: p.kind, reallySent: p.emulatesRealSend }));
273
+ }
274
+ const OTP_TTL_MS = 10 * 60_000;
275
+ const OTP_MAX_ATTEMPTS = 5;
276
+ export function generateOtp() {
277
+ return String(crypto.randomInt(0, 1_000_000)).padStart(6, '0');
278
+ }
279
+ export function hashCode(code, salt) {
280
+ return crypto.createHash('sha256').update(`${salt}:${code}`).digest('hex');
281
+ }
282
+ export class OtpStore {
283
+ dir;
284
+ file;
285
+ constructor(dir) {
286
+ this.dir = dir;
287
+ this.file = path.join(dir, 'otp.json');
288
+ }
289
+ async list() {
290
+ try {
291
+ return JSON.parse(await fsp.readFile(this.file, 'utf8'));
292
+ }
293
+ catch (err) {
294
+ if (err?.code === 'ENOENT')
295
+ return [];
296
+ throw err;
297
+ }
298
+ }
299
+ async write(all) {
300
+ const tmp = `${this.file}.tmp-${process.pid}`;
301
+ await fsp.mkdir(this.dir, { recursive: true });
302
+ await fsp.writeFile(tmp, JSON.stringify(all.slice(-200), null, 2), 'utf8');
303
+ await fsp.rename(tmp, this.file);
304
+ }
305
+ /** 生成挑战 —— 返回明文 once (调用方马上投递, 不落盘) */
306
+ async issue(opts) {
307
+ const now = opts.now ?? Date.now();
308
+ const code = generateOtp();
309
+ const salt = crypto.randomBytes(8).toString('hex');
310
+ const challenge = {
311
+ challengeId: `otp-${now.toString(36)}-${crypto.randomBytes(3).toString('hex')}`,
312
+ contactId: opts.contactId, kind: opts.kind, provider: opts.provider,
313
+ salt, codeHash: hashCode(code, salt), attempts: 0, maxAttempts: OTP_MAX_ATTEMPTS,
314
+ createdAt: new Date(now).toISOString(), expiresAt: new Date(now + OTP_TTL_MS).toISOString(),
315
+ };
316
+ const all = await this.list();
317
+ all.push(challenge);
318
+ await this.write(all);
319
+ return { challenge, code };
320
+ }
321
+ async verify(challengeId, code, now = Date.now()) {
322
+ const all = await this.list();
323
+ const c = all.find((x) => x.challengeId === challengeId);
324
+ if (!c)
325
+ return { ok: false, reason: 'challenge_not_found' };
326
+ if (c.usedAt)
327
+ return { ok: false, reason: 'already_used' };
328
+ if (Date.parse(c.expiresAt) < now)
329
+ return { ok: false, reason: 'expired' };
330
+ if (c.attempts >= c.maxAttempts)
331
+ return { ok: false, reason: 'too_many_attempts' };
332
+ c.attempts += 1;
333
+ if (hashCode(String(code).trim(), c.salt) !== c.codeHash) {
334
+ await this.write(all);
335
+ return { ok: false, reason: 'wrong_code' };
336
+ }
337
+ c.usedAt = new Date(now).toISOString();
338
+ await this.write(all);
339
+ return { ok: true };
340
+ }
341
+ }