productfoo 0.0.0-stage → 1.0.1

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 (498) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +338 -2
  3. package/bin/foo +3 -0
  4. package/bin/foo.mjs +42 -0
  5. package/commands/bug.md +31 -0
  6. package/commands/discovery.md +28 -0
  7. package/commands/ideate.md +27 -0
  8. package/commands/init.md +28 -0
  9. package/commands/pitch.md +27 -0
  10. package/commands/preview.md +25 -0
  11. package/commands/research.md +31 -0
  12. package/commands/roast.md +27 -0
  13. package/commands/spec.md +30 -0
  14. package/config/pricing.yaml +198 -0
  15. package/config/routing-policy.yaml +171 -0
  16. package/dist-public/src/activeModel.d.ts +83 -0
  17. package/dist-public/src/activeModel.js +331 -0
  18. package/dist-public/src/agentAdapters.d.ts +122 -0
  19. package/dist-public/src/agentAdapters.js +178 -0
  20. package/dist-public/src/agentState.d.ts +43 -0
  21. package/dist-public/src/agentState.js +177 -0
  22. package/dist-public/src/bugCapsule.d.ts +94 -0
  23. package/dist-public/src/bugCapsule.js +227 -0
  24. package/dist-public/src/bugCapture.d.ts +26 -0
  25. package/dist-public/src/bugCapture.js +251 -0
  26. package/dist-public/src/bugOwnerLog.d.ts +93 -0
  27. package/dist-public/src/bugOwnerLog.js +304 -0
  28. package/dist-public/src/bugReportStore.d.ts +48 -0
  29. package/dist-public/src/bugReportStore.js +860 -0
  30. package/dist-public/src/bugSpecContext.d.ts +7 -0
  31. package/dist-public/src/bugSpecContext.js +129 -0
  32. package/dist-public/src/channelFreshness.d.ts +25 -0
  33. package/dist-public/src/channelFreshness.js +194 -0
  34. package/dist-public/src/cli.d.ts +30 -0
  35. package/dist-public/src/cli.js +1667 -0
  36. package/dist-public/src/cliArgs.d.ts +7 -0
  37. package/dist-public/src/cliArgs.js +44 -0
  38. package/dist-public/src/codeCatalog.d.ts +41 -0
  39. package/dist-public/src/codeCatalog.js +197 -0
  40. package/dist-public/src/commandContract.d.ts +84 -0
  41. package/dist-public/src/commandContract.js +390 -0
  42. package/dist-public/src/commandUsage.d.ts +3 -0
  43. package/dist-public/src/commandUsage.js +30 -0
  44. package/dist-public/src/commands/agent.d.ts +60 -0
  45. package/dist-public/src/commands/agent.js +1353 -0
  46. package/dist-public/src/commands/asp.d.ts +8 -0
  47. package/dist-public/src/commands/asp.js +669 -0
  48. package/dist-public/src/commands/evalCmd.d.ts +4 -0
  49. package/dist-public/src/commands/evalCmd.js +405 -0
  50. package/dist-public/src/commands/login.d.ts +25 -0
  51. package/dist-public/src/commands/login.js +94 -0
  52. package/dist-public/src/commands/loop.d.ts +16 -0
  53. package/dist-public/src/commands/loop.js +619 -0
  54. package/dist-public/src/commands/reconcile.d.ts +41 -0
  55. package/dist-public/src/commands/reconcile.js +292 -0
  56. package/dist-public/src/commands/runtime.d.ts +1 -0
  57. package/dist-public/src/commands/runtime.js +36 -0
  58. package/dist-public/src/commands/scanRedact.d.ts +8 -0
  59. package/dist-public/src/commands/scanRedact.js +266 -0
  60. package/dist-public/src/commands/setup.d.ts +28 -0
  61. package/dist-public/src/commands/setup.js +398 -0
  62. package/dist-public/src/commands/shared.d.ts +17 -0
  63. package/dist-public/src/commands/shared.js +147 -0
  64. package/dist-public/src/commands/source.d.ts +1 -0
  65. package/dist-public/src/commands/source.js +134 -0
  66. package/dist-public/src/commands/specCmd.d.ts +24 -0
  67. package/dist-public/src/commands/specCmd.js +794 -0
  68. package/dist-public/src/commands/update.d.ts +61 -0
  69. package/dist-public/src/commands/update.js +244 -0
  70. package/dist-public/src/comprehensionSignal.d.ts +20 -0
  71. package/dist-public/src/comprehensionSignal.js +166 -0
  72. package/dist-public/src/conductor/diversity.d.ts +48 -0
  73. package/dist-public/src/conductor/diversity.js +423 -0
  74. package/dist-public/src/conductor/engine.d.ts +7 -0
  75. package/dist-public/src/conductor/engine.js +295 -0
  76. package/dist-public/src/conductor/families/ideate.d.ts +2 -0
  77. package/dist-public/src/conductor/families/ideate.js +255 -0
  78. package/dist-public/src/conductor/families/interview.d.ts +2 -0
  79. package/dist-public/src/conductor/families/interview.js +39 -0
  80. package/dist-public/src/conductor/families/specSwarm.d.ts +2 -0
  81. package/dist-public/src/conductor/families/specSwarm.js +146 -0
  82. package/dist-public/src/conductor/familyDelivery.d.ts +6 -0
  83. package/dist-public/src/conductor/familyDelivery.js +37 -0
  84. package/dist-public/src/conductor/familyPackArtifact.d.ts +4 -0
  85. package/dist-public/src/conductor/familyPackArtifact.js +260 -0
  86. package/dist-public/src/conductor/ideateDecision.d.ts +6 -0
  87. package/dist-public/src/conductor/ideateDecision.js +237 -0
  88. package/dist-public/src/conductor/pack.d.ts +26 -0
  89. package/dist-public/src/conductor/pack.js +75 -0
  90. package/dist-public/src/conductor/types.d.ts +111 -0
  91. package/dist-public/src/conductor/types.js +7 -0
  92. package/dist-public/src/configPath.d.ts +2 -0
  93. package/dist-public/src/configPath.js +7 -0
  94. package/dist-public/src/discoveryArtifacts.d.ts +54 -0
  95. package/dist-public/src/discoveryArtifacts.js +467 -0
  96. package/dist-public/src/discoveryCommand.d.ts +127 -0
  97. package/dist-public/src/discoveryCommand.js +2148 -0
  98. package/dist-public/src/discoveryConnections.d.ts +339 -0
  99. package/dist-public/src/discoveryConnections.js +613 -0
  100. package/dist-public/src/discoveryDocs.d.ts +129 -0
  101. package/dist-public/src/discoveryDocs.js +577 -0
  102. package/dist-public/src/discoveryEvidence.d.ts +59 -0
  103. package/dist-public/src/discoveryEvidence.js +198 -0
  104. package/dist-public/src/discoveryFireflies.d.ts +25 -0
  105. package/dist-public/src/discoveryFireflies.js +551 -0
  106. package/dist-public/src/discoveryGmail.d.ts +12 -0
  107. package/dist-public/src/discoveryGmail.js +466 -0
  108. package/dist-public/src/discoveryGong.d.ts +12 -0
  109. package/dist-public/src/discoveryGong.js +491 -0
  110. package/dist-public/src/discoveryLearningRouting.d.ts +90 -0
  111. package/dist-public/src/discoveryLearningRouting.js +405 -0
  112. package/dist-public/src/discoveryMatching.d.ts +26 -0
  113. package/dist-public/src/discoveryMatching.js +73 -0
  114. package/dist-public/src/discoveryOutlook.d.ts +12 -0
  115. package/dist-public/src/discoveryOutlook.js +492 -0
  116. package/dist-public/src/discoveryProcess.d.ts +221 -0
  117. package/dist-public/src/discoveryProcess.js +703 -0
  118. package/dist-public/src/discoveryProof.d.ts +143 -0
  119. package/dist-public/src/discoveryProof.js +529 -0
  120. package/dist-public/src/discoveryProviders.d.ts +152 -0
  121. package/dist-public/src/discoveryProviders.js +756 -0
  122. package/dist-public/src/discoveryReadiness.d.ts +219 -0
  123. package/dist-public/src/discoveryReadiness.js +672 -0
  124. package/dist-public/src/discoveryReconcile.d.ts +57 -0
  125. package/dist-public/src/discoveryReconcile.js +424 -0
  126. package/dist-public/src/discoverySlack.d.ts +12 -0
  127. package/dist-public/src/discoverySlack.js +452 -0
  128. package/dist-public/src/discoverySources.d.ts +56 -0
  129. package/dist-public/src/discoverySources.js +322 -0
  130. package/dist-public/src/discoverySync.d.ts +52 -0
  131. package/dist-public/src/discoverySync.js +151 -0
  132. package/dist-public/src/discoverySynthesis.d.ts +57 -0
  133. package/dist-public/src/discoverySynthesis.js +215 -0
  134. package/dist-public/src/discoveryTeams.d.ts +12 -0
  135. package/dist-public/src/discoveryTeams.js +516 -0
  136. package/dist-public/src/discoveryZoom.d.ts +12 -0
  137. package/dist-public/src/discoveryZoom.js +523 -0
  138. package/dist-public/src/elicitationSurface.d.ts +13 -0
  139. package/dist-public/src/elicitationSurface.js +77 -0
  140. package/dist-public/src/errorReport.d.ts +99 -0
  141. package/dist-public/src/errorReport.js +443 -0
  142. package/dist-public/src/errorTaxonomy.d.ts +84 -0
  143. package/dist-public/src/errorTaxonomy.js +189 -0
  144. package/dist-public/src/evalCalibration.d.ts +82 -0
  145. package/dist-public/src/evalCalibration.js +169 -0
  146. package/dist-public/src/evalChecks.d.ts +21 -0
  147. package/dist-public/src/evalChecks.js +295 -0
  148. package/dist-public/src/evalCompile.d.ts +56 -0
  149. package/dist-public/src/evalCompile.js +58 -0
  150. package/dist-public/src/evalImprove.d.ts +60 -0
  151. package/dist-public/src/evalImprove.js +175 -0
  152. package/dist-public/src/evalJudge.d.ts +47 -0
  153. package/dist-public/src/evalJudge.js +143 -0
  154. package/dist-public/src/evalLabels.d.ts +100 -0
  155. package/dist-public/src/evalLabels.js +287 -0
  156. package/dist-public/src/evalRecord.d.ts +68 -0
  157. package/dist-public/src/evalRecord.js +165 -0
  158. package/dist-public/src/evalRoast.d.ts +88 -0
  159. package/dist-public/src/evalRoast.js +249 -0
  160. package/dist-public/src/evidenceIntake.d.ts +66 -0
  161. package/dist-public/src/evidenceIntake.js +356 -0
  162. package/dist-public/src/feedback.d.ts +78 -0
  163. package/dist-public/src/feedback.js +294 -0
  164. package/dist-public/src/firefliesClient.d.ts +106 -0
  165. package/dist-public/src/firefliesClient.js +258 -0
  166. package/dist-public/src/firstMoveTensions.d.ts +10 -0
  167. package/dist-public/src/firstMoveTensions.js +93 -0
  168. package/dist-public/src/gitRun.d.ts +21 -0
  169. package/dist-public/src/gitRun.js +50 -0
  170. package/dist-public/src/githubReconcile.d.ts +94 -0
  171. package/dist-public/src/githubReconcile.js +205 -0
  172. package/dist-public/src/gmailNango.d.ts +131 -0
  173. package/dist-public/src/gmailNango.js +329 -0
  174. package/dist-public/src/gongNango.d.ts +118 -0
  175. package/dist-public/src/gongNango.js +308 -0
  176. package/dist-public/src/grammarGate.d.ts +13 -0
  177. package/dist-public/src/grammarGate.js +72 -0
  178. package/dist-public/src/harnessGates.d.ts +65 -0
  179. package/dist-public/src/harnessGates.js +168 -0
  180. package/dist-public/src/humanSurfaceEval.d.ts +19 -0
  181. package/dist-public/src/humanSurfaceEval.js +156 -0
  182. package/dist-public/src/ideate.d.ts +43 -0
  183. package/dist-public/src/ideate.js +255 -0
  184. package/dist-public/src/ideateContext.d.ts +11 -0
  185. package/dist-public/src/ideateContext.js +83 -0
  186. package/dist-public/src/index.d.ts +2 -0
  187. package/dist-public/src/index.js +4 -0
  188. package/dist-public/src/initCommand.d.ts +25 -0
  189. package/dist-public/src/initCommand.js +558 -0
  190. package/dist-public/src/interviewGate.d.ts +25 -0
  191. package/dist-public/src/interviewGate.js +126 -0
  192. package/dist-public/src/jsonFile.d.ts +2 -0
  193. package/dist-public/src/jsonFile.js +22 -0
  194. package/dist-public/src/loopBudget.d.ts +182 -0
  195. package/dist-public/src/loopBudget.js +494 -0
  196. package/dist-public/src/loopContracts.d.ts +35 -0
  197. package/dist-public/src/loopContracts.js +364 -0
  198. package/dist-public/src/machineState.d.ts +1 -0
  199. package/dist-public/src/machineState.js +9 -0
  200. package/dist-public/src/notionNango.d.ts +93 -0
  201. package/dist-public/src/notionNango.js +279 -0
  202. package/dist-public/src/orchestrationResult.d.ts +11 -0
  203. package/dist-public/src/orchestrationResult.js +73 -0
  204. package/dist-public/src/outlookNango.d.ts +124 -0
  205. package/dist-public/src/outlookNango.js +327 -0
  206. package/dist-public/src/packetCorrections.d.ts +40 -0
  207. package/dist-public/src/packetCorrections.js +172 -0
  208. package/dist-public/src/packetGlossary.d.ts +6 -0
  209. package/dist-public/src/packetGlossary.js +42 -0
  210. package/dist-public/src/packetIndex.d.ts +54 -0
  211. package/dist-public/src/packetIndex.js +744 -0
  212. package/dist-public/src/packetIndexEntry.d.ts +3 -0
  213. package/dist-public/src/packetIndexEntry.js +128 -0
  214. package/dist-public/src/packetIndexServe.d.ts +13 -0
  215. package/dist-public/src/packetIndexServe.js +113 -0
  216. package/dist-public/src/packetIndexWorker.d.ts +7 -0
  217. package/dist-public/src/packetIndexWorker.js +45 -0
  218. package/dist-public/src/packetLifecycle.d.ts +30 -0
  219. package/dist-public/src/packetLifecycle.js +127 -0
  220. package/dist-public/src/packetReconcile.d.ts +73 -0
  221. package/dist-public/src/packetReconcile.js +386 -0
  222. package/dist-public/src/packetStatus.d.ts +125 -0
  223. package/dist-public/src/packetStatus.js +770 -0
  224. package/dist-public/src/packetValidation.d.ts +17 -0
  225. package/dist-public/src/packetValidation.js +333 -0
  226. package/dist-public/src/packetVerify.d.ts +73 -0
  227. package/dist-public/src/packetVerify.js +303 -0
  228. package/dist-public/src/packetWrite.d.ts +52 -0
  229. package/dist-public/src/packetWrite.js +448 -0
  230. package/dist-public/src/pitch.d.ts +38 -0
  231. package/dist-public/src/pitch.js +154 -0
  232. package/dist-public/src/processMemo.d.ts +8 -0
  233. package/dist-public/src/processMemo.js +23 -0
  234. package/dist-public/src/productDetect.d.ts +30 -0
  235. package/dist-public/src/productDetect.js +376 -0
  236. package/dist-public/src/profile.d.ts +62 -0
  237. package/dist-public/src/profile.js +334 -0
  238. package/dist-public/src/questionModel.d.ts +46 -0
  239. package/dist-public/src/questionModel.js +74 -0
  240. package/dist-public/src/questionText.d.ts +7 -0
  241. package/dist-public/src/questionText.js +38 -0
  242. package/dist-public/src/redactConfig.d.ts +2 -0
  243. package/dist-public/src/redactConfig.js +11 -0
  244. package/dist-public/src/redactEngine.d.ts +59 -0
  245. package/dist-public/src/redactEngine.js +509 -0
  246. package/dist-public/src/redactGate.d.ts +63 -0
  247. package/dist-public/src/redactGate.js +209 -0
  248. package/dist-public/src/repoRead.d.ts +29 -0
  249. package/dist-public/src/repoRead.js +160 -0
  250. package/dist-public/src/research.d.ts +242 -0
  251. package/dist-public/src/research.js +1314 -0
  252. package/dist-public/src/researchCompletion.d.ts +34 -0
  253. package/dist-public/src/researchCompletion.js +123 -0
  254. package/dist-public/src/researchExecution.d.ts +47 -0
  255. package/dist-public/src/researchExecution.js +309 -0
  256. package/dist-public/src/researchFormat.d.ts +21 -0
  257. package/dist-public/src/researchFormat.js +77 -0
  258. package/dist-public/src/researchGate.d.ts +86 -0
  259. package/dist-public/src/researchGate.js +1036 -0
  260. package/dist-public/src/researchReceipt.d.ts +76 -0
  261. package/dist-public/src/researchReceipt.js +148 -0
  262. package/dist-public/src/researchRender.d.ts +2 -0
  263. package/dist-public/src/researchRender.js +267 -0
  264. package/dist-public/src/researchSigningKey.d.ts +19 -0
  265. package/dist-public/src/researchSigningKey.js +95 -0
  266. package/dist-public/src/riskClassify.d.ts +42 -0
  267. package/dist-public/src/riskClassify.js +149 -0
  268. package/dist-public/src/routines.d.ts +42 -0
  269. package/dist-public/src/routines.js +269 -0
  270. package/dist-public/src/routingPolicy.d.ts +172 -0
  271. package/dist-public/src/routingPolicy.js +646 -0
  272. package/dist-public/src/secretInput.d.ts +8 -0
  273. package/dist-public/src/secretInput.js +89 -0
  274. package/dist-public/src/selfCheck.d.ts +7 -0
  275. package/dist-public/src/selfCheck.js +473 -0
  276. package/dist-public/src/selfCheckCache.d.ts +29 -0
  277. package/dist-public/src/selfCheckCache.js +147 -0
  278. package/dist-public/src/severityClass.d.ts +15 -0
  279. package/dist-public/src/severityClass.js +22 -0
  280. package/dist-public/src/skillClientPolicy.d.ts +8 -0
  281. package/dist-public/src/skillClientPolicy.js +16 -0
  282. package/dist-public/src/skillLocal.d.ts +98 -0
  283. package/dist-public/src/skillLocal.js +463 -0
  284. package/dist-public/src/skillTreeDigest.d.ts +9 -0
  285. package/dist-public/src/skillTreeDigest.js +64 -0
  286. package/dist-public/src/slackNango.d.ts +62 -0
  287. package/dist-public/src/slackNango.js +208 -0
  288. package/dist-public/src/social/arxiv.d.ts +19 -0
  289. package/dist-public/src/social/arxiv.js +96 -0
  290. package/dist-public/src/social/github.d.ts +2 -0
  291. package/dist-public/src/social/github.js +78 -0
  292. package/dist-public/src/social/hackernews.d.ts +2 -0
  293. package/dist-public/src/social/hackernews.js +67 -0
  294. package/dist-public/src/social/index.d.ts +10 -0
  295. package/dist-public/src/social/index.js +79 -0
  296. package/dist-public/src/social/reddit.d.ts +2 -0
  297. package/dist-public/src/social/reddit.js +125 -0
  298. package/dist-public/src/social/types.d.ts +73 -0
  299. package/dist-public/src/social/types.js +61 -0
  300. package/dist-public/src/social/xml.d.ts +10 -0
  301. package/dist-public/src/social/xml.js +49 -0
  302. package/dist-public/src/social/youtube.d.ts +7 -0
  303. package/dist-public/src/social/youtube.js +217 -0
  304. package/dist-public/src/sourceIngress.d.ts +488 -0
  305. package/dist-public/src/sourceIngress.js +1651 -0
  306. package/dist-public/src/specAfter.d.ts +187 -0
  307. package/dist-public/src/specAfter.js +814 -0
  308. package/dist-public/src/specBudget.d.ts +124 -0
  309. package/dist-public/src/specBudget.js +424 -0
  310. package/dist-public/src/specDecisionSignals.d.ts +38 -0
  311. package/dist-public/src/specDecisionSignals.js +121 -0
  312. package/dist-public/src/specLinkedCommits.d.ts +7 -0
  313. package/dist-public/src/specLinkedCommits.js +107 -0
  314. package/dist-public/src/specSections.d.ts +105 -0
  315. package/dist-public/src/specSections.js +718 -0
  316. package/dist-public/src/statusGlyphs.d.ts +18 -0
  317. package/dist-public/src/statusGlyphs.js +75 -0
  318. package/dist-public/src/swarmCommand.d.ts +10 -0
  319. package/dist-public/src/swarmCommand.js +96 -0
  320. package/dist-public/src/teamsNango.d.ts +114 -0
  321. package/dist-public/src/teamsNango.js +301 -0
  322. package/dist-public/src/tokenStore.d.ts +10 -0
  323. package/dist-public/src/tokenStore.js +77 -0
  324. package/dist-public/src/trustEnvelope.d.ts +78 -0
  325. package/dist-public/src/trustEnvelope.js +392 -0
  326. package/dist-public/src/untrustedText.d.ts +6 -0
  327. package/dist-public/src/untrustedText.js +20 -0
  328. package/dist-public/src/updateNotice.d.ts +41 -0
  329. package/dist-public/src/updateNotice.js +290 -0
  330. package/dist-public/src/vendorYaml.d.ts +1 -0
  331. package/dist-public/src/vendorYaml.js +7374 -0
  332. package/dist-public/src/workflowRunActuals.d.ts +67 -0
  333. package/dist-public/src/workflowRunActuals.js +184 -0
  334. package/dist-public/src/zendeskDiscovery.d.ts +12 -0
  335. package/dist-public/src/zendeskDiscovery.js +513 -0
  336. package/dist-public/src/zendeskNango.d.ts +131 -0
  337. package/dist-public/src/zendeskNango.js +344 -0
  338. package/dist-public/src/zoomNango.d.ts +106 -0
  339. package/dist-public/src/zoomNango.js +351 -0
  340. package/docs/CHANGELOG.md +32 -0
  341. package/docs/THIRD_PARTY_NOTICES.md +36 -0
  342. package/package.json +71 -4
  343. package/schemas/bug-capsule-receipt.schema.json +54 -0
  344. package/schemas/bug-capsule.schema.json +189 -0
  345. package/schemas/discovery-evidence-envelope.schema.json +79 -0
  346. package/schemas/discovery-processing-receipt.schema.json +71 -0
  347. package/schemas/discovery-service-contract.schema.json +189 -0
  348. package/skills/AGENTS.md +32 -0
  349. package/skills/README.md +287 -0
  350. package/skills/bug/SKILL.md +49 -0
  351. package/skills/build-preview-spec/SKILL.md +103 -0
  352. package/skills/capture-external-artifact/SKILL.md +23 -0
  353. package/skills/capture-external-artifact/agents/openai.yaml +4 -0
  354. package/skills/capture-external-artifact/references/extractor-libraries.md +27 -0
  355. package/skills/check-spec-health/SKILL.md +45 -0
  356. package/skills/check-spec-health/agents/openai.yaml +4 -0
  357. package/skills/competitive-positioning/SKILL.md +86 -0
  358. package/skills/competitive-positioning/agents/openai.yaml +4 -0
  359. package/skills/cpo-lens/SKILL.md +398 -0
  360. package/skills/cpo-lens/agents/openai.yaml +4 -0
  361. package/skills/create-prd/SKILL.md +26 -0
  362. package/skills/create-prd/agents/openai.yaml +4 -0
  363. package/skills/customer-discovery/SKILL.md +192 -0
  364. package/skills/design-system/SKILL.md +49 -0
  365. package/skills/design-system/agents/openai.yaml +4 -0
  366. package/skills/discussion-guide/SKILL.md +75 -0
  367. package/skills/discussion-guide/agents/openai.yaml +4 -0
  368. package/skills/firecrawl-research-papers/SKILL.md +96 -0
  369. package/skills/firecrawl-research-papers/agents/openai.yaml +4 -0
  370. package/skills/foo-roast/SKILL.md +325 -0
  371. package/skills/generate-gtm-brief/SKILL.md +41 -0
  372. package/skills/generate-gtm-brief/agents/openai.yaml +4 -0
  373. package/skills/generate-gtm-brief/references/gtm-brief-template.md +56 -0
  374. package/skills/gong-quotes/SKILL.md +43 -0
  375. package/skills/gong-quotes/agents/openai.yaml +4 -0
  376. package/skills/grill-me/SKILL.md +27 -0
  377. package/skills/grill-me/agents/openai.yaml +4 -0
  378. package/skills/ideate/SKILL.md +180 -0
  379. package/skills/ideate/references/family-packs.md +5 -0
  380. package/skills/initiative-tracker/SKILL.md +23 -0
  381. package/skills/initiative-tracker/agents/openai.yaml +4 -0
  382. package/skills/interop-review/SKILL.md +25 -0
  383. package/skills/interop-review/agents/openai.yaml +4 -0
  384. package/skills/interview-me/SKILL.md +404 -0
  385. package/skills/interview-me/agents/openai.yaml +4 -0
  386. package/skills/loop-library-audit/SKILL.md +123 -0
  387. package/skills/pitch/SKILL.md +110 -0
  388. package/skills/pitch/references/opportunity-assessment.md +136 -0
  389. package/skills/pitch/references/prfaq-template.md +54 -0
  390. package/skills/pmm-release-briefing/SKILL.md +27 -0
  391. package/skills/pmm-release-briefing/agents/openai.yaml +4 -0
  392. package/skills/pmm-release-briefing/references/variant-generalized.md +16 -0
  393. package/skills/positioning-messaging/SKILL.md +70 -0
  394. package/skills/positioning-messaging/agents/openai.yaml +4 -0
  395. package/skills/positioning-messaging/references/guest-insights.md +1400 -0
  396. package/skills/preview-swarm/SKILL.md +418 -0
  397. package/skills/preview-swarm/agents/openai.yaml +4 -0
  398. package/skills/preview-swarm/references/html-effectiveness-build-previews.md +168 -0
  399. package/skills/product-copywriting/SKILL.md +127 -0
  400. package/skills/product-copywriting/agents/openai.yaml +4 -0
  401. package/skills/product-copywriting/references/foo-voice.md +26 -0
  402. package/skills/product-shape-interview/SKILL.md +219 -0
  403. package/skills/product-shape-interview/agents/openai.yaml +4 -0
  404. package/skills/ready-for-release/SKILL.md +39 -0
  405. package/skills/ready-for-release/agents/openai.yaml +4 -0
  406. package/skills/redact-transcripts/SKILL.md +23 -0
  407. package/skills/redact-transcripts/agents/openai.yaml +4 -0
  408. package/skills/research-brief/SKILL.md +48 -0
  409. package/skills/research-brief/agents/openai.yaml +4 -0
  410. package/skills/research-refresh/SKILL.md +63 -0
  411. package/skills/research-refresh/references/worked-cases.md +44 -0
  412. package/skills/review-loop/SKILL.md +200 -0
  413. package/skills/review-spec/SKILL.md +32 -0
  414. package/skills/review-spec/agents/openai.yaml +4 -0
  415. package/skills/rfc-from-research/SKILL.md +42 -0
  416. package/skills/rfc-from-research/agents/openai.yaml +4 -0
  417. package/skills/rfc-from-research/references/rfc-pattern.md +69 -0
  418. package/skills/skill-registry.yaml +810 -0
  419. package/skills/spec-cleanup/SKILL.md +103 -0
  420. package/skills/spec-cleanup/agents/openai.yaml +4 -0
  421. package/skills/spec-swarm/SKILL.md +87 -0
  422. package/skills/spec-swarm/agents/openai.yaml +4 -0
  423. package/skills/spec-swarm/template/SKILL.template.md +94 -0
  424. package/skills/spec-swarm/template/personas/accessibility-inclusion.md +40 -0
  425. package/skills/spec-swarm/template/personas/ai-technical-pm.md +69 -0
  426. package/skills/spec-swarm/template/personas/analytics-engineering.md +39 -0
  427. package/skills/spec-swarm/template/personas/applied-ai-eval.md +64 -0
  428. package/skills/spec-swarm/template/personas/business-gm-pm.md +67 -0
  429. package/skills/spec-swarm/template/personas/consumer-craft-pm.md +68 -0
  430. package/skills/spec-swarm/template/personas/content-design.md +40 -0
  431. package/skills/spec-swarm/template/personas/cpo.md +42 -0
  432. package/skills/spec-swarm/template/personas/data-science.md +42 -0
  433. package/skills/spec-swarm/template/personas/developer-experience.md +65 -0
  434. package/skills/spec-swarm/template/personas/dx-product.md +70 -0
  435. package/skills/spec-swarm/template/personas/field.md +43 -0
  436. package/skills/spec-swarm/template/personas/finance-cfo.md +40 -0
  437. package/skills/spec-swarm/template/personas/growth-pm.md +67 -0
  438. package/skills/spec-swarm/template/personas/platform-pm.md +67 -0
  439. package/skills/spec-swarm/template/personas/pmm.md +43 -0
  440. package/skills/spec-swarm/template/personas/principal-product-designer.md +42 -0
  441. package/skills/spec-swarm/template/personas/product-ops-program.md +41 -0
  442. package/skills/spec-swarm/template/personas/qa-release.md +41 -0
  443. package/skills/spec-swarm/template/personas/responsible-ai-safety.md +64 -0
  444. package/skills/spec-swarm/template/personas/security-privacy-legal.md +40 -0
  445. package/skills/spec-swarm/template/personas/strategy-competitive.md +41 -0
  446. package/skills/spec-swarm/template/personas/technical-architect.md +42 -0
  447. package/skills/spec-swarm/template/personas/technical-lead.md +43 -0
  448. package/skills/spec-swarm/template/personas/user-research.md +41 -0
  449. package/skills/spec-swarm-accessibility-inclusion/SKILL.md +95 -0
  450. package/skills/spec-swarm-accessibility-inclusion/agents/openai.yaml +4 -0
  451. package/skills/spec-swarm-ai-technical-pm/SKILL.md +124 -0
  452. package/skills/spec-swarm-analytics-engineering/SKILL.md +94 -0
  453. package/skills/spec-swarm-analytics-engineering/agents/openai.yaml +4 -0
  454. package/skills/spec-swarm-applied-ai-eval/SKILL.md +120 -0
  455. package/skills/spec-swarm-business-gm-pm/SKILL.md +122 -0
  456. package/skills/spec-swarm-consumer-craft-pm/SKILL.md +122 -0
  457. package/skills/spec-swarm-content-design/SKILL.md +95 -0
  458. package/skills/spec-swarm-content-design/agents/openai.yaml +4 -0
  459. package/skills/spec-swarm-cpo/SKILL.md +94 -0
  460. package/skills/spec-swarm-cpo/agents/openai.yaml +4 -0
  461. package/skills/spec-swarm-data-science/SKILL.md +95 -0
  462. package/skills/spec-swarm-data-science/agents/openai.yaml +4 -0
  463. package/skills/spec-swarm-developer-experience/SKILL.md +121 -0
  464. package/skills/spec-swarm-dx-product/SKILL.md +126 -0
  465. package/skills/spec-swarm-field/SKILL.md +96 -0
  466. package/skills/spec-swarm-field/agents/openai.yaml +4 -0
  467. package/skills/spec-swarm-finance-cfo/SKILL.md +95 -0
  468. package/skills/spec-swarm-finance-cfo/agents/openai.yaml +4 -0
  469. package/skills/spec-swarm-growth-pm/SKILL.md +122 -0
  470. package/skills/spec-swarm-platform-pm/SKILL.md +122 -0
  471. package/skills/spec-swarm-pmm/SKILL.md +96 -0
  472. package/skills/spec-swarm-pmm/agents/openai.yaml +4 -0
  473. package/skills/spec-swarm-principal-product-designer/SKILL.md +95 -0
  474. package/skills/spec-swarm-principal-product-designer/agents/openai.yaml +4 -0
  475. package/skills/spec-swarm-product-ops-program/SKILL.md +96 -0
  476. package/skills/spec-swarm-product-ops-program/agents/openai.yaml +4 -0
  477. package/skills/spec-swarm-qa-release/SKILL.md +96 -0
  478. package/skills/spec-swarm-qa-release/agents/openai.yaml +4 -0
  479. package/skills/spec-swarm-responsible-ai-safety/SKILL.md +120 -0
  480. package/skills/spec-swarm-security-privacy-legal/SKILL.md +95 -0
  481. package/skills/spec-swarm-security-privacy-legal/agents/openai.yaml +4 -0
  482. package/skills/spec-swarm-strategy-competitive/SKILL.md +96 -0
  483. package/skills/spec-swarm-strategy-competitive/agents/openai.yaml +4 -0
  484. package/skills/spec-swarm-technical-architect/SKILL.md +95 -0
  485. package/skills/spec-swarm-technical-architect/agents/openai.yaml +4 -0
  486. package/skills/spec-swarm-technical-lead/SKILL.md +96 -0
  487. package/skills/spec-swarm-technical-lead/agents/openai.yaml +4 -0
  488. package/skills/spec-swarm-user-research/SKILL.md +95 -0
  489. package/skills/spec-swarm-user-research/agents/openai.yaml +4 -0
  490. package/skills/strategy-check/SKILL.md +170 -0
  491. package/skills/strategy-check/agents/openai.yaml +4 -0
  492. package/skills/study-digest/SKILL.md +41 -0
  493. package/skills/synthetic-customer/SKILL.md +68 -0
  494. package/skills/update-cli-spec/SKILL.md +69 -0
  495. package/skills/update-cli-spec/agents/openai.yaml +4 -0
  496. package/skills/voc-researcher/SKILL.md +45 -0
  497. package/skills/voc-researcher/agents/openai.yaml +4 -0
  498. package/skills-lock.json +35 -0
@@ -0,0 +1,143 @@
1
+ import { type DiscoveryLifecycle } from "./discoveryArtifacts.js";
2
+ import type { DiscoverySession } from "./discoverySources.js";
3
+ import { type DiscoveryProcessingReceipt } from "./discoveryProcess.js";
4
+ import { type DiscoveryRegisteredTarget } from "./discoveryConnections.js";
5
+ import { type DiscoveryEvidenceEnvelope } from "./discoveryEvidence.js";
6
+ import type { TrustStore } from "./trustEnvelope.js";
7
+ export type DiscoveryProofStatus = "ready_to_synthesize" | "awaiting_match" | "ambiguous_match" | "needs_source_approval" | "needs_provider_access" | "needs_provider_connector" | "needs_research_proof" | "invalid";
8
+ export type AuthorizationInput = {
9
+ study_dir: string;
10
+ local_root: string;
11
+ study_id: string;
12
+ study_state: DiscoveryLifecycle;
13
+ match_status: "none" | "unique" | "ambiguous";
14
+ session: DiscoverySession;
15
+ now: string;
16
+ };
17
+ export type AuthorizationResult = {
18
+ status: DiscoveryProofStatus;
19
+ source_fingerprint: string;
20
+ proof_ref?: string;
21
+ handoff?: string;
22
+ };
23
+ type ProofReceipt = {
24
+ schema_version: "0.1";
25
+ kind: "discovery_research_proof";
26
+ study_id: string;
27
+ provider: string;
28
+ connection_alias: string;
29
+ source_fingerprint: string;
30
+ approval_digest: string;
31
+ issued_at: string;
32
+ expires_at: string;
33
+ phase: "ready" | "opened";
34
+ opened_at?: string;
35
+ capabilities: {
36
+ open_transcript: true;
37
+ stable_link: boolean;
38
+ timestamps: boolean;
39
+ };
40
+ };
41
+ declare function approvalDigest(studyDir: string): string;
42
+ export declare const approvalLedgerDigest: typeof approvalDigest;
43
+ export declare function mintDiscoveryProof(localRoot: string, input: {
44
+ study_id: string;
45
+ session: DiscoverySession;
46
+ approval_digest: string;
47
+ now: string;
48
+ }): ProofReceipt;
49
+ export declare function authorizeDiscoverySource(input: AuthorizationInput): AuthorizationResult;
50
+ export declare function assertOpenedDiscoveryProof(localRoot: string, studyId: string, fingerprint: string, now: string): void;
51
+ export declare function markDiscoveryProofOpened(input: {
52
+ study_dir: string;
53
+ local_root: string;
54
+ study_id: string;
55
+ source_fingerprint: string;
56
+ now: string;
57
+ }): void;
58
+ export declare function openAuthorizedTranscript<T>(input: {
59
+ study_dir: string;
60
+ local_root: string;
61
+ study_id: string;
62
+ source_fingerprint: string;
63
+ now: string;
64
+ }, open: () => Promise<T>): Promise<T>;
65
+ export type ApprovedSourceProofReceipt = {
66
+ schema_version: "0.1";
67
+ kind: "approved_source_proof";
68
+ target_id: string;
69
+ provider: string;
70
+ connection_alias: string;
71
+ selection_fingerprint: string;
72
+ approval_digest: string;
73
+ issued_at: string;
74
+ expires_at: string;
75
+ phase: "ready" | "opened";
76
+ opened_at?: string;
77
+ };
78
+ export declare function mintApprovedSourceProof(localRoot: string, input: {
79
+ target_id: string;
80
+ provider: string;
81
+ connection_alias: string;
82
+ selection_fingerprint: string;
83
+ approval_digest: string;
84
+ now: string;
85
+ }): ApprovedSourceProofReceipt;
86
+ /**
87
+ * The single authorized open of an approved source's content (R-005): the
88
+ * receipt must be ready and inside its ten-minute window, and the packet's
89
+ * approval ledger must still admit this exact selection at the moment of the
90
+ * read — a revocation between mint and open invalidates the proof. On any
91
+ * failure the receipt is purged and the error is replaced by a bounded
92
+ * message, so neither content nor provider errors survive the boundary.
93
+ */
94
+ export declare function openApprovedSourceContent<T>(input: {
95
+ packet_dir: string;
96
+ local_root: string;
97
+ target_id: string;
98
+ selection_fingerprint: string;
99
+ selection: {
100
+ page_id?: string;
101
+ database_id?: string;
102
+ };
103
+ now: string;
104
+ }, open: () => Promise<T>): Promise<T>;
105
+ export declare function consumeApprovedSourceProof(localRoot: string, targetId: string, fingerprint: string, now: string): void;
106
+ export declare function consumeDiscoveryProof(localRoot: string, studyId: string, fingerprint: string, now: string): void;
107
+ export declare function purgeDiscoveryLocalState(localRoot: string, studyId: string): {
108
+ removed: boolean;
109
+ path: string;
110
+ };
111
+ export declare function purgeDiscoveryLocalFingerprint(localRoot: string, fingerprint: string): string[];
112
+ export type DiscoveryRetentionResult = {
113
+ removed: string[];
114
+ retained: string[];
115
+ failures: string[];
116
+ };
117
+ export declare function purgeDiscoveryRetention(root: string, now: string, retentionDays?: number): DiscoveryRetentionResult;
118
+ export declare const DISCOVERY_PORTABLE_BUNDLE_SCHEMA_VERSION = "0.1";
119
+ export type DiscoveryPortableBundle = {
120
+ schema_version: "0.1";
121
+ kind: "discovery_portable_bundle";
122
+ envelope: DiscoveryEvidenceEnvelope;
123
+ processing_receipt: DiscoveryProcessingReceipt;
124
+ };
125
+ export type DiscoveryBundleVerification = {
126
+ ok: true;
127
+ bundle: DiscoveryPortableBundle;
128
+ receipt_hash: string;
129
+ registry: DiscoveryRegisteredTarget;
130
+ } | {
131
+ ok: false;
132
+ message: string;
133
+ };
134
+ export declare function loadDiscoveryReceiptTrustStore(root: string): TrustStore<"foo">;
135
+ export declare function verifyDiscoveryBundleForApply(input: {
136
+ bundle: unknown;
137
+ target_id: string;
138
+ root: string;
139
+ study_dir: string;
140
+ trust_store?: TrustStore<"foo">;
141
+ now: string;
142
+ }): DiscoveryBundleVerification;
143
+ export {};
@@ -0,0 +1,529 @@
1
+ import { createHash, randomBytes } from "node:crypto";
2
+ import { existsSync, mkdirSync, readFileSync, readdirSync, renameSync, rmSync, writeFileSync } from "node:fs";
3
+ import path from "node:path";
4
+ import { approvalStates, sourceKey, notionScopeAllowsSelection } from "./sourceIngress.js";
5
+ import { sourceFingerprint } from "./discoveryMatching.js";
6
+ import { loadDiscoveryRun } from "./discoveryArtifacts.js";
7
+ import { discoveryEnvelopeDigest, verifyDiscoveryProcessingReceipt, } from "./discoveryProcess.js";
8
+ import { loadDiscoveryRegisteredTargets } from "./discoveryConnections.js";
9
+ import { HOSTED_SOURCE_FINGERPRINT, validateDiscoveryEvidence, } from "./discoveryEvidence.js";
10
+ function safeDiscoveryTarget(targetId) {
11
+ if (/^study_[a-z0-9_]+$/.test(targetId))
12
+ return targetId;
13
+ if (/^inbox:[a-z0-9_]+$/.test(targetId))
14
+ return targetId.replace(":", "_");
15
+ if (/^docs_[a-z0-9_]+$/.test(targetId))
16
+ return targetId;
17
+ throw new Error("invalid discovery target id");
18
+ }
19
+ function receiptPath(localRoot, studyId) {
20
+ return path.join(path.resolve(localRoot), safeDiscoveryTarget(studyId), "proof.json");
21
+ }
22
+ function approvalDigest(studyDir) {
23
+ const events = path.join(studyDir, "events.jsonl");
24
+ const text = existsSync(events)
25
+ ? readFileSync(events, "utf8").split("\n").filter((line) => /com\.adaptivespec\.source\.(?:approved|revoked)/.test(line)).join("\n")
26
+ : "";
27
+ return `sha256:${createHash("sha256").update(text).digest("hex")}`;
28
+ }
29
+ // Exported for the docs evidence class (notion-discovery-connector-v0 T-004):
30
+ // the same approval-ledger digest, bound into an approved-source proof so the
31
+ // one content read is pinned to the exact ledger the approval lives in.
32
+ export const approvalLedgerDigest = approvalDigest;
33
+ function scopeAllowsStudy(scope, studyId) {
34
+ const value = (scope ?? "").trim();
35
+ if (studyId.startsWith("inbox:")) {
36
+ const scopeId = studyId.slice("inbox:".length);
37
+ return value === "*" || value === scopeId || value === `scope:${scopeId}`;
38
+ }
39
+ return value === "*" || value === studyId || value === `study:${studyId}`;
40
+ }
41
+ function currentApproval(input) {
42
+ const state = approvalStates(input.study_dir).get(sourceKey({ provider: input.session.provider, connection_id: input.session.connection_alias }));
43
+ // Approvals accumulate per scope (#528): ANY approved scope on the connection
44
+ // may authorize this study/inbox target — one connection, many bounded scopes.
45
+ const ok = state?.state === "approved" && state.scopes.some((scope) => scopeAllowsStudy(scope, input.study_id));
46
+ return { ok, digest: approvalDigest(input.study_dir) };
47
+ }
48
+ function sourceWasRevoked(studyDir, fingerprint) {
49
+ const events = path.join(studyDir, "events.jsonl");
50
+ if (!existsSync(events))
51
+ return false;
52
+ for (const line of readFileSync(events, "utf8").split("\n")) {
53
+ if (!line.trim())
54
+ continue;
55
+ try {
56
+ const event = JSON.parse(line);
57
+ if (event.type === "com.adaptivespec.discovery.source_revoked" && event.data?.source_fingerprint === fingerprint)
58
+ return true;
59
+ }
60
+ catch {
61
+ continue;
62
+ }
63
+ }
64
+ return false;
65
+ }
66
+ export function mintDiscoveryProof(localRoot, input) {
67
+ if (!input.approval_digest.startsWith("sha256:"))
68
+ throw new Error("discovery proof requires source approval");
69
+ if (!input.session.capabilities.open_transcript || input.session.access !== "available")
70
+ throw new Error("discovery proof requires transcript capability and access");
71
+ const issued = new Date(input.now);
72
+ if (!Number.isFinite(issued.getTime()))
73
+ throw new Error("discovery proof requires a valid timestamp");
74
+ const receipt = {
75
+ schema_version: "0.1",
76
+ kind: "discovery_research_proof",
77
+ study_id: input.study_id,
78
+ provider: input.session.provider,
79
+ connection_alias: input.session.connection_alias,
80
+ source_fingerprint: sourceFingerprint(input.session),
81
+ approval_digest: input.approval_digest,
82
+ issued_at: issued.toISOString(),
83
+ expires_at: new Date(issued.getTime() + 10 * 60_000).toISOString(),
84
+ phase: "ready",
85
+ capabilities: {
86
+ open_transcript: true,
87
+ stable_link: input.session.capabilities.stable_link,
88
+ timestamps: input.session.capabilities.timestamps,
89
+ },
90
+ };
91
+ const file = receiptPath(localRoot, input.study_id);
92
+ mkdirSync(path.dirname(file), { recursive: true });
93
+ writeFileSync(file, `${JSON.stringify(receipt, null, 2)}\n`, { mode: 0o600 });
94
+ return receipt;
95
+ }
96
+ export function authorizeDiscoverySource(input) {
97
+ const fingerprint = sourceFingerprint(input.session);
98
+ if (input.study_state === "revoked")
99
+ return { status: "invalid", source_fingerprint: fingerprint, handoff: "This study is revoked; future ingestion is blocked." };
100
+ if (sourceWasRevoked(input.study_dir, fingerprint))
101
+ return { status: "invalid", source_fingerprint: fingerprint, handoff: "This source was revoked; future ingestion is blocked." };
102
+ if (input.match_status === "none")
103
+ return { status: "awaiting_match", source_fingerprint: fingerprint };
104
+ if (input.match_status === "ambiguous")
105
+ return { status: "ambiguous_match", source_fingerprint: fingerprint, handoff: "Select one source before transcript access." };
106
+ if (!input.session.capabilities.list_sessions || !input.session.capabilities.open_transcript) {
107
+ return { status: "needs_provider_connector", source_fingerprint: fingerprint, handoff: "The provider connector cannot open transcripts." };
108
+ }
109
+ if (input.session.access !== "available") {
110
+ return { status: "needs_provider_access", source_fingerprint: fingerprint, handoff: input.session.stable_url ? `Request full transcript access from ${input.session.stable_url}` : "Request full transcript access from the provider." };
111
+ }
112
+ const approval = currentApproval(input);
113
+ if (!approval.ok) {
114
+ const approvalScope = input.study_id.startsWith("inbox:")
115
+ ? `scope:${input.study_id.slice("inbox:".length)}`
116
+ : `study:${input.study_id}`;
117
+ return {
118
+ status: "needs_source_approval",
119
+ source_fingerprint: fingerprint,
120
+ handoff: `foo source approve ${path.relative(process.cwd(), input.study_dir) || input.study_dir} --provider ${input.session.provider} --connection ${input.session.connection_alias} --scope ${approvalScope} --by <person>`,
121
+ };
122
+ }
123
+ try {
124
+ mintDiscoveryProof(input.local_root, { study_id: input.study_id, session: input.session, approval_digest: approval.digest, now: input.now });
125
+ }
126
+ catch {
127
+ return { status: "needs_research_proof", source_fingerprint: fingerprint, handoff: "productfoo could not establish the discovery proof; no transcript was opened." };
128
+ }
129
+ return { status: "ready_to_synthesize", source_fingerprint: fingerprint, proof_ref: receiptPath(input.local_root, input.study_id) };
130
+ }
131
+ function readReceipt(localRoot, studyId) {
132
+ const file = receiptPath(localRoot, studyId);
133
+ if (!existsSync(file))
134
+ throw new Error("authorized discovery proof is missing");
135
+ let parsed;
136
+ try {
137
+ parsed = JSON.parse(readFileSync(file, "utf8"));
138
+ }
139
+ catch {
140
+ throw new Error("authorized discovery proof is invalid");
141
+ }
142
+ const receipt = parsed;
143
+ if (receipt.schema_version !== "0.1" || receipt.kind !== "discovery_research_proof" || receipt.study_id !== studyId)
144
+ throw new Error("authorized discovery proof is invalid");
145
+ return receipt;
146
+ }
147
+ export function assertOpenedDiscoveryProof(localRoot, studyId, fingerprint, now) {
148
+ const receipt = readReceipt(localRoot, studyId);
149
+ if (receipt.phase !== "opened")
150
+ throw new Error("discovery research proof has not opened the selected source");
151
+ if (receipt.source_fingerprint !== fingerprint)
152
+ throw new Error("opened discovery proof does not match the evidence source");
153
+ if (!Number.isFinite(Date.parse(now)) || Date.parse(now) > Date.parse(receipt.expires_at))
154
+ throw new Error("opened discovery proof expired");
155
+ }
156
+ function readyReceipt(input) {
157
+ const receipt = readReceipt(input.local_root, input.study_id);
158
+ if (receipt.phase !== "ready")
159
+ throw new Error("authorized discovery proof was already opened");
160
+ if (receipt.source_fingerprint !== input.source_fingerprint)
161
+ throw new Error("authorized discovery proof does not match the selected source");
162
+ const now = Date.parse(input.now);
163
+ if (!Number.isFinite(now) || now > Date.parse(receipt.expires_at))
164
+ throw new Error("authorized discovery proof expired");
165
+ const state = approvalStates(input.study_dir).get(sourceKey({ provider: receipt.provider, connection_id: receipt.connection_alias }));
166
+ // Same accumulated-scope check as authorization (#528): any approved scope
167
+ // that admits this target keeps the proof valid; the digest still pins the
168
+ // exact approval ledger the proof was minted against.
169
+ if (state?.state !== "approved" || !state.scopes.some((scope) => scopeAllowsStudy(scope, input.study_id)) || approvalDigest(input.study_dir) !== receipt.approval_digest) {
170
+ throw new Error("authorized discovery proof no longer matches source approval");
171
+ }
172
+ return receipt;
173
+ }
174
+ export function markDiscoveryProofOpened(input) {
175
+ const receipt = readyReceipt(input);
176
+ const opened = { ...receipt, phase: "opened", opened_at: new Date(input.now).toISOString() };
177
+ writeFileSync(receiptPath(input.local_root, input.study_id), `${JSON.stringify(opened, null, 2)}\n`, { mode: 0o600 });
178
+ }
179
+ export async function openAuthorizedTranscript(input, open) {
180
+ readyReceipt(input);
181
+ try {
182
+ const value = await open();
183
+ markDiscoveryProofOpened(input);
184
+ return value;
185
+ }
186
+ catch {
187
+ rmSync(receiptPath(input.local_root, input.study_id), { force: true });
188
+ throw new Error("provider transcript access failed; no source content was retained");
189
+ }
190
+ }
191
+ function readSourceReceipt(localRoot, targetId) {
192
+ const file = receiptPath(localRoot, targetId);
193
+ if (!existsSync(file))
194
+ throw new Error("authorized source proof is missing");
195
+ let parsed;
196
+ try {
197
+ parsed = JSON.parse(readFileSync(file, "utf8"));
198
+ }
199
+ catch {
200
+ throw new Error("authorized source proof is invalid");
201
+ }
202
+ const receipt = parsed;
203
+ if (receipt.schema_version !== "0.1" || receipt.kind !== "approved_source_proof" || receipt.target_id !== targetId)
204
+ throw new Error("authorized source proof is invalid");
205
+ return receipt;
206
+ }
207
+ // Same accumulated-scope check as authorization (#528), through the docs
208
+ // boundary: an admitted selection is one the stored approved scope explicitly
209
+ // names — never a wildcard, never a crawler listing.
210
+ function currentDocsApproval(input) {
211
+ const state = approvalStates(input.packet_dir).get(sourceKey({ provider: input.provider, connection_id: input.connection_alias }));
212
+ const ok = state?.state === "approved" && state.scopes.some((scope) => notionScopeAllowsSelection(scope, input.selection));
213
+ return { ok, digest: approvalDigest(input.packet_dir) };
214
+ }
215
+ export function mintApprovedSourceProof(localRoot, input) {
216
+ if (!input.approval_digest.startsWith("sha256:"))
217
+ throw new Error("approved-source proof requires source approval");
218
+ if (!input.selection_fingerprint.startsWith("sha256:"))
219
+ throw new Error("approved-source proof requires a selection fingerprint");
220
+ const issued = new Date(input.now);
221
+ if (!Number.isFinite(issued.getTime()))
222
+ throw new Error("approved-source proof requires a valid timestamp");
223
+ const receipt = {
224
+ schema_version: "0.1",
225
+ kind: "approved_source_proof",
226
+ target_id: input.target_id,
227
+ provider: input.provider,
228
+ connection_alias: input.connection_alias,
229
+ selection_fingerprint: input.selection_fingerprint,
230
+ approval_digest: input.approval_digest,
231
+ issued_at: issued.toISOString(),
232
+ expires_at: new Date(issued.getTime() + 10 * 60_000).toISOString(),
233
+ phase: "ready",
234
+ };
235
+ const file = receiptPath(localRoot, input.target_id);
236
+ mkdirSync(path.dirname(file), { recursive: true });
237
+ writeFileSync(file, `${JSON.stringify(receipt, null, 2)}\n`, { mode: 0o600 });
238
+ return receipt;
239
+ }
240
+ function readySourceReceipt(input) {
241
+ const receipt = readSourceReceipt(input.local_root, input.target_id);
242
+ if (receipt.phase !== "ready")
243
+ throw new Error("authorized source proof was already opened");
244
+ if (receipt.selection_fingerprint !== input.selection_fingerprint)
245
+ throw new Error("authorized source proof does not match the selected source");
246
+ const now = Date.parse(input.now);
247
+ if (!Number.isFinite(now) || now > Date.parse(receipt.expires_at))
248
+ throw new Error("authorized source proof expired");
249
+ const approval = currentDocsApproval({ packet_dir: input.packet_dir, provider: receipt.provider, connection_alias: receipt.connection_alias, selection: input.selection });
250
+ if (!approval.ok || approval.digest !== receipt.approval_digest)
251
+ throw new Error("authorized source proof no longer matches source approval");
252
+ return receipt;
253
+ }
254
+ /**
255
+ * The single authorized open of an approved source's content (R-005): the
256
+ * receipt must be ready and inside its ten-minute window, and the packet's
257
+ * approval ledger must still admit this exact selection at the moment of the
258
+ * read — a revocation between mint and open invalidates the proof. On any
259
+ * failure the receipt is purged and the error is replaced by a bounded
260
+ * message, so neither content nor provider errors survive the boundary.
261
+ */
262
+ export async function openApprovedSourceContent(input, open) {
263
+ readySourceReceipt(input);
264
+ try {
265
+ const value = await open();
266
+ const receipt = readSourceReceipt(input.local_root, input.target_id);
267
+ writeFileSync(receiptPath(input.local_root, input.target_id), `${JSON.stringify({ ...receipt, phase: "opened", opened_at: new Date(input.now).toISOString() }, null, 2)}\n`, { mode: 0o600 });
268
+ return value;
269
+ }
270
+ catch (error) {
271
+ if (error instanceof Error && (error.message.includes("source approval") || error.message.includes("already opened") || error.message.includes("expired") || error.message.includes("invalid")))
272
+ throw error;
273
+ // Typed runtime errors (byte ceilings, auth, rate limits) carry bounded,
274
+ // content-free messages — re-throw them so callers can classify; only
275
+ // untyped failures are replaced by the bounded boundary message.
276
+ if (error instanceof Error && typeof error.code === "string") {
277
+ rmSync(receiptPath(input.local_root, input.target_id), { force: true });
278
+ throw error;
279
+ }
280
+ rmSync(receiptPath(input.local_root, input.target_id), { force: true });
281
+ throw new Error("provider content access failed; no source content was retained");
282
+ }
283
+ }
284
+ // Single-use close-out: once the summary is recorded the proof is consumed so
285
+ // no receipt lingers (zero residue).
286
+ //
287
+ // Consumption must be atomic under concurrent local invocation (audit F12):
288
+ // rmSync alone cannot tell two consumers apart — both succeed and the one-time
289
+ // proof is used twice. The claim is an exclusive rename: exactly one
290
+ // concurrent consumer can move the receipt; every loser finds it gone and
291
+ // fails instead of re-consuming. A consumer that crashes between claim and
292
+ // delete leaves only a .consuming copy — receipt metadata, never content.
293
+ function consumeReceiptFile(file, missing) {
294
+ const claimed = `${file}.consuming-${randomBytes(6).toString("hex")}`;
295
+ try {
296
+ renameSync(file, claimed);
297
+ }
298
+ catch {
299
+ throw new Error(missing);
300
+ }
301
+ rmSync(claimed, { force: true });
302
+ }
303
+ // Single-use close-out (docs boundary): once the summary is recorded the proof
304
+ // is consumed so no receipt lingers (zero residue).
305
+ export function consumeApprovedSourceProof(localRoot, targetId, fingerprint, now) {
306
+ const receipt = readSourceReceipt(localRoot, targetId);
307
+ if (receipt.phase !== "opened")
308
+ throw new Error("approved-source proof has not opened the selected source");
309
+ if (receipt.selection_fingerprint !== fingerprint)
310
+ throw new Error("opened source proof does not match the evidence source");
311
+ if (!Number.isFinite(Date.parse(now)) || Date.parse(now) > Date.parse(receipt.expires_at))
312
+ throw new Error("opened source proof expired");
313
+ consumeReceiptFile(receiptPath(localRoot, targetId), "authorized source proof is missing");
314
+ try {
315
+ rmSync(path.dirname(receiptPath(localRoot, targetId)), { recursive: false });
316
+ }
317
+ catch { /* other minimum local state may remain */ }
318
+ }
319
+ export function consumeDiscoveryProof(localRoot, studyId, fingerprint, now) {
320
+ assertOpenedDiscoveryProof(localRoot, studyId, fingerprint, now);
321
+ consumeReceiptFile(receiptPath(localRoot, studyId), "authorized discovery proof is missing");
322
+ const dir = path.dirname(receiptPath(localRoot, studyId));
323
+ try {
324
+ rmSync(dir, { recursive: false });
325
+ }
326
+ catch { /* other minimum local state may remain */ }
327
+ }
328
+ export function purgeDiscoveryLocalState(localRoot, studyId) {
329
+ const target = path.join(path.resolve(localRoot), safeDiscoveryTarget(studyId));
330
+ const removed = existsSync(target);
331
+ if (removed)
332
+ rmSync(target, { recursive: true, force: true });
333
+ return { removed, path: target };
334
+ }
335
+ export function purgeDiscoveryLocalFingerprint(localRoot, fingerprint) {
336
+ const root = path.resolve(localRoot);
337
+ if (!existsSync(root))
338
+ return [];
339
+ const removed = [];
340
+ for (const entry of readdirSync(root, { withFileTypes: true })) {
341
+ if (!entry.isDirectory())
342
+ continue;
343
+ const dir = path.join(root, entry.name);
344
+ const files = ["match.json", "proof.json"].map((name) => path.join(dir, name)).filter(existsSync);
345
+ if (!files.some((file) => readFileSync(file, "utf8").includes(fingerprint)))
346
+ continue;
347
+ rmSync(dir, { recursive: true, force: true });
348
+ removed.push(dir);
349
+ }
350
+ return removed;
351
+ }
352
+ export function purgeDiscoveryRetention(root, now, retentionDays = 30) {
353
+ const resolvedRoot = path.resolve(root);
354
+ const discoveryRoot = path.join(resolvedRoot, "discovery");
355
+ const localRoot = path.join(resolvedRoot, ".foo", "discovery");
356
+ const removed = [];
357
+ const retained = [];
358
+ const failures = [];
359
+ const current = Date.parse(now);
360
+ if (!Number.isFinite(current) || !Number.isFinite(retentionDays) || retentionDays < 0) {
361
+ return { removed, retained, failures: ["retention requires a valid timestamp and non-negative day count"] };
362
+ }
363
+ if (!existsSync(discoveryRoot))
364
+ return { removed, retained, failures };
365
+ for (const entry of readdirSync(discoveryRoot, { withFileTypes: true })) {
366
+ if (!entry.isDirectory() || entry.name === "inbox")
367
+ continue;
368
+ let run;
369
+ try {
370
+ run = loadDiscoveryRun(path.join(discoveryRoot, entry.name));
371
+ }
372
+ catch {
373
+ failures.push(`${entry.name}: discovery run could not be read; local state was retained`);
374
+ continue;
375
+ }
376
+ if (run.state !== "complete" && run.state !== "revoked")
377
+ continue;
378
+ const localRelative = `.foo/discovery/${run.study_id}`;
379
+ const localDir = path.join(localRoot, run.study_id);
380
+ if (!existsSync(localDir))
381
+ continue;
382
+ let due = run.state === "revoked";
383
+ if (run.state === "complete") {
384
+ const updatedAt = Date.parse(run.updated_at);
385
+ if (!Number.isFinite(updatedAt)) {
386
+ failures.push(`${run.study_id}: invalid updated_at; ${localRelative} was retained`);
387
+ retained.push(localRelative);
388
+ continue;
389
+ }
390
+ due = current - updatedAt >= retentionDays * 24 * 60 * 60_000;
391
+ }
392
+ if (!due) {
393
+ retained.push(localRelative);
394
+ continue;
395
+ }
396
+ try {
397
+ const result = purgeDiscoveryLocalState(localRoot, run.study_id);
398
+ if (result.removed && !existsSync(result.path))
399
+ removed.push(localRelative);
400
+ else
401
+ failures.push(`${run.study_id}: cleanup incomplete; ${localRelative} remains`);
402
+ }
403
+ catch {
404
+ failures.push(`${run.study_id}: cleanup failed; ${localRelative} remains`);
405
+ }
406
+ }
407
+ return { removed: removed.sort(), retained: retained.sort(), failures };
408
+ }
409
+ // --- Receipt-verified local apply (nango-discovery-connector-v0 T-005) ------
410
+ //
411
+ // Hosted processing (T-004) returns a signed payload-free receipt beside the
412
+ // sanitized envelope as the needs_local_apply portable bundle. Applying that
413
+ // bundle locally re-verifies everything locally checkable — bundle shape,
414
+ // signature, signing key, expiry, envelope hash, subject/workspace/target/
415
+ // lane bindings, source fingerprint family, the CURRENT approval digest, and
416
+ // local revocation/suspension state — and only then asks the service
417
+ // `discovery_receipt_status` immediately before writing. Every refusal leaves
418
+ // disk untouched (R-007, RISK-012, C-009).
419
+ export const DISCOVERY_PORTABLE_BUNDLE_SCHEMA_VERSION = "0.1";
420
+ const BUNDLE_KEYS = new Set(["schema_version", "kind", "envelope", "processing_receipt"]);
421
+ // The pinned productfoo verification keys for discovery receipts (D-012: published
422
+ // through the authenticated productfoo service). A missing or malformed file yields
423
+ // an EMPTY store — verification fails closed on unknown keys, never open.
424
+ export function loadDiscoveryReceiptTrustStore(root) {
425
+ const file = path.join(path.resolve(root), ".foo", "discovery", "trust.json");
426
+ if (!existsSync(file))
427
+ return { namespace: "foo", keys: [] };
428
+ const empty = { namespace: "foo", keys: [] };
429
+ let parsed;
430
+ try {
431
+ parsed = JSON.parse(readFileSync(file, "utf8"));
432
+ }
433
+ catch {
434
+ return empty;
435
+ }
436
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
437
+ return empty;
438
+ const raw = parsed;
439
+ if (raw.schema_version !== "0.1" || raw.kind !== "discovery_receipt_trust_store" || !Array.isArray(raw.keys))
440
+ return empty;
441
+ const keys = raw.keys.filter((entry) => {
442
+ if (!entry || typeof entry !== "object" || Array.isArray(entry))
443
+ return false;
444
+ const key = entry;
445
+ return (typeof key.key_id === "string" &&
446
+ typeof key.public_key_pem === "string" &&
447
+ (key.status === "active" || key.status === "retired" || key.status === "revoked") &&
448
+ typeof key.valid_from === "string" &&
449
+ (key.valid_until === null || typeof key.valid_until === "string"));
450
+ });
451
+ return { namespace: "foo", keys };
452
+ }
453
+ export function verifyDiscoveryBundleForApply(input) {
454
+ if (!input.bundle || typeof input.bundle !== "object" || Array.isArray(input.bundle)) {
455
+ return { ok: false, message: "the portable bundle is not an object" };
456
+ }
457
+ const raw = input.bundle;
458
+ for (const key of Object.keys(raw)) {
459
+ if (!BUNDLE_KEYS.has(key))
460
+ return { ok: false, message: `the portable bundle carries an unknown field: ${key}` };
461
+ }
462
+ if (raw.schema_version !== DISCOVERY_PORTABLE_BUNDLE_SCHEMA_VERSION || raw.kind !== "discovery_portable_bundle") {
463
+ return { ok: false, message: "the portable bundle is not a 0.1 discovery_portable_bundle" };
464
+ }
465
+ // A hosted bundle may only be applied against a locally registered,
466
+ // human-confirmed target (T-002 registration; T-006 boundary). No local
467
+ // registration record means there is no one to hold the grant — fail closed.
468
+ const registry = loadDiscoveryRegisteredTargets(input.root).find((record) => record.target_id === input.target_id);
469
+ if (!registry) {
470
+ return { ok: false, message: "no locally registered, human-confirmed target exists for this bundle; nothing can be applied" };
471
+ }
472
+ if (registry.suspension) {
473
+ return { ok: false, message: "this target's hosted access is suspended locally; re-register the target before applying hosted evidence" };
474
+ }
475
+ if (registry.lane === "study") {
476
+ const run = loadDiscoveryRun(input.study_dir);
477
+ if (run.study_id !== input.target_id)
478
+ return { ok: false, message: "the registered target does not match the study being applied" };
479
+ if (run.state === "revoked")
480
+ return { ok: false, message: "this study is revoked; no evidence can be applied" };
481
+ }
482
+ const lane = registry.lane === "inbox" ? "unassigned_call_evidence" : "study_linked";
483
+ const context = { expected_study_id: input.target_id, expected_lane: lane };
484
+ let envelope;
485
+ try {
486
+ envelope = validateDiscoveryEvidence(raw.envelope, context);
487
+ }
488
+ catch (error) {
489
+ return { ok: false, message: `the bundle's evidence envelope failed the closed contract — ${error instanceof Error ? error.message : "unknown error"}` };
490
+ }
491
+ // A hosted-registered target's evidence carries the hosted fingerprint
492
+ // family; a local sha256 fingerprint here is a family substitution (the
493
+ // local proof path binds local fingerprints to the proof, not the bundle).
494
+ if (!HOSTED_SOURCE_FINGERPRINT.test(envelope.source.source_fingerprint)) {
495
+ return { ok: false, message: "the bundle's evidence carries a non-hosted source fingerprint; nothing was applied" };
496
+ }
497
+ if (sourceWasRevoked(input.study_dir, envelope.source.source_fingerprint)) {
498
+ return { ok: false, message: "this source was revoked locally; no evidence can be applied" };
499
+ }
500
+ // The receipt must have been minted against the approval digest that is
501
+ // current RIGHT NOW — an approval or revocation recorded locally after
502
+ // processing invalidates the bundle (stale approval, C-009).
503
+ const expectation = {
504
+ subject_ref: registry.subject_ref,
505
+ workspace_ref: registry.workspace_ref,
506
+ connection_ref: registry.connection_ref,
507
+ target_ref: registry.target_ref,
508
+ target_id: registry.target_id,
509
+ lane: registry.lane,
510
+ source_fingerprint: envelope.source.source_fingerprint,
511
+ approval_digest: approvalDigest(input.study_dir),
512
+ revocation_epoch: registry.revocation_epoch,
513
+ envelope_hash: discoveryEnvelopeDigest(envelope),
514
+ };
515
+ const verdict = verifyDiscoveryProcessingReceipt({
516
+ receipt: raw.processing_receipt,
517
+ trust_store: input.trust_store ?? loadDiscoveryReceiptTrustStore(input.root),
518
+ expectation,
519
+ now: input.now,
520
+ });
521
+ if (!verdict.ok)
522
+ return { ok: false, message: `the processing receipt failed verification — ${verdict.message}` };
523
+ return {
524
+ ok: true,
525
+ bundle: { schema_version: "0.1", kind: "discovery_portable_bundle", envelope, processing_receipt: raw.processing_receipt },
526
+ receipt_hash: verdict.receipt_hash,
527
+ registry,
528
+ };
529
+ }