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,322 @@
1
+ const PROVIDER_HOSTS = {
2
+ // notes.granola.ai is Granola's share-link host — the owner pastes exactly these
3
+ // (customer-signal-inbox-v0: its absence forced source_url null across the whole
4
+ // first batch and refused an owner-approved ingestion).
5
+ granola: ["app.granola.ai", "granola.ai", "notes.granola.ai"],
6
+ fakecalls: ["review.fakecalls.test"],
7
+ // Fireflies transcript links are believed path-based under fireflies.ai
8
+ // (fireflies-discovery-connector-v0 R-006 — belief owned by the packet's
9
+ // feasibility task): the global no-query rule applies, so hosts alone are
10
+ // declared and no ProviderLinkForm is needed.
11
+ fireflies: ["app.fireflies.ai", "fireflies.ai"],
12
+ // notion-discovery-connector-v0 R-006: Notion's canonical page form is
13
+ // https://www.notion.so/<page-id> — no query string. The global no-query rule
14
+ // is what refuses tokenized share links (they carry query parameters); the
15
+ // V0 stance is refuse, never de-tokenize, until the feasibility task (T-001)
16
+ // verifies the exact link semantics.
17
+ notion: ["notion.so", "www.notion.so"],
18
+ };
19
+ const PROVIDER_LINK_FORMS = {
20
+ // Belief, owned by the packet's feasibility task: Gong links a call as
21
+ // https://app.gong.io/call?id=<call-id> (or under a company subdomain).
22
+ gong: { hosts: ["app.gong.io"], host_suffix: ".app.gong.io", path: "/call", query: { name: "id", pattern: /^[A-Za-z0-9_-]{4,64}$/ } },
23
+ // Microsoft Teams declares NO host and NO link form yet
24
+ // (microsoft-teams-discovery-connector-v0 R-006): Graph join URLs are
25
+ // tokenized and are never stable links, and the canonical meeting/recording
26
+ // link form is owned by the packet's feasibility task. Until that task
27
+ // verifies a form, every Teams URL is refused here — never cleaned — and
28
+ // the adapter reports stable_link: false. Other providers stay unchanged.
29
+ };
30
+ // Path-based canonical links on a per-workspace subdomain
31
+ // (zendesk-discovery-connector-v0 R-006): the host is exactly one subdomain
32
+ // label plus the declared suffix, the path is the declared canonical form,
33
+ // and the global no-query/no-fragment rules apply unchanged — a tokenized or
34
+ // authenticated link is refused, never cleaned. Providers without an entry
35
+ // keep the exact-host rule byte-for-byte.
36
+ const PROVIDER_PATH_FORMS = {
37
+ // Belief, owned by the packet's feasibility task: Zendesk links a ticket as
38
+ // https://<subdomain>.zendesk.com/agent/tickets/<numeric-id>.
39
+ zendesk: { suffix: ".zendesk.com", path: /^\/agent\/tickets\/\d+$/ },
40
+ // Belief, owned by the packet's feasibility task (T-001): Zoom links a
41
+ // recording as https://<subdomain>.zoom.us/recording/<id>. Because the
42
+ // canonical form carries the identifier in the path and needs no query
43
+ // parameter, Zoom declares no `ProviderLinkForm`; tokenized variants
44
+ // (`/rec/share/...`, `?pwd=`, `?access_token=`) match no rule and are
45
+ // refused, never cleaned.
46
+ zoom: { suffix: ".zoom.us", path: /^\/recording\/[A-Za-z0-9_-]{16,128}$/ },
47
+ };
48
+ // Path-based canonical links on a per-workspace subdomain
49
+ // (zendesk-discovery-connector-v0 R-006): the host is exactly one subdomain
50
+ // label plus the declared suffix, the path is the declared canonical form,
51
+ // and the global no-query/no-fragment rules apply unchanged — a tokenized or
52
+ // authenticated link is refused, never cleaned. Providers without an entry
53
+ // keep the exact-host rule byte-for-byte.
54
+ export function providerLinkForm(provider) {
55
+ return PROVIDER_LINK_FORMS[provider.toLowerCase()];
56
+ }
57
+ const PROVIDER_PATH_TEMPLATE_FORMS = {
58
+ // Belief, owned by the packet's feasibility task: Slack permalinks are
59
+ // https://<workspace>.slack.com/archives/<channel>/p<ts> where the ts is the
60
+ // message timestamp with the dot removed — a 10-digit epoch second plus a
61
+ // 6-digit microsecond field, 16 digits total. The path-template grammar
62
+ // matches whole segments only, so the `p` prefix lives inside the
63
+ // placeholder's own pattern.
64
+ slack: { hosts: ["slack.com"], host_suffix: ".slack.com", template: "/archives/<channel>/<permalink_ts>", placeholders: { channel: /^C[A-Za-z0-9]{8,}$/, permalink_ts: /^p\d{16}$/ } },
65
+ };
66
+ const PATH_PLACEHOLDER = /^<(\w+)>$/;
67
+ function sanitizeDeclaredPathTemplateForm(provider, url, form) {
68
+ const host = url.hostname.toLowerCase();
69
+ const hostOk = form.hosts.includes(host) ||
70
+ (form.host_suffix !== undefined && host.endsWith(form.host_suffix) && /^[a-z0-9-]+$/.test(host.slice(0, -form.host_suffix.length)));
71
+ if (!hostOk)
72
+ throw new Error(`stable URL must use an approved host for ${provider}`);
73
+ if (url.hash)
74
+ throw new Error("stable URL must not contain a fragment");
75
+ if (url.search)
76
+ throw new Error(`stable URL must not carry a query string for ${provider}`);
77
+ const templateSegments = form.template.split("/").filter(Boolean);
78
+ const actualSegments = url.pathname.split("/").filter(Boolean);
79
+ if (templateSegments.length !== actualSegments.length)
80
+ throw new Error(`stable URL must use the canonical ${provider} path`);
81
+ const captured = {};
82
+ for (const [index, segment] of templateSegments.entries()) {
83
+ const placeholder = PATH_PLACEHOLDER.exec(segment);
84
+ if (!placeholder) {
85
+ if (segment !== actualSegments[index])
86
+ throw new Error(`stable URL must use the canonical ${provider} path`);
87
+ continue;
88
+ }
89
+ const pattern = form.placeholders[placeholder[1]];
90
+ if (!pattern || !pattern.test(actualSegments[index]))
91
+ throw new Error(`stable URL ${placeholder[1]} does not look like a ${provider} identifier`);
92
+ captured[placeholder[1]] = actualSegments[index];
93
+ }
94
+ const rebuilt = templateSegments.map((segment) => {
95
+ const placeholder = PATH_PLACEHOLDER.exec(segment);
96
+ return placeholder ? captured[placeholder[1]] : segment;
97
+ }).join("/");
98
+ return `https://${host}/${rebuilt}`;
99
+ }
100
+ function sanitizeDeclaredLinkForm(provider, url, form) {
101
+ const host = url.hostname.toLowerCase();
102
+ const hostOk = form.hosts.includes(host) ||
103
+ (form.host_suffix !== undefined && host.endsWith(form.host_suffix) && /^[a-z0-9-]+$/.test(host.slice(0, -form.host_suffix.length)));
104
+ if (!hostOk)
105
+ throw new Error(`stable URL must use an approved host for ${provider}`);
106
+ if (url.hash)
107
+ throw new Error("stable URL must not contain a fragment");
108
+ if (url.pathname !== form.path)
109
+ throw new Error(`stable URL must use the canonical ${provider} path`);
110
+ const params = [...url.searchParams.entries()];
111
+ if (params.length !== 1 || params[0][0] !== form.query.name)
112
+ throw new Error(`stable URL must carry exactly the ${form.query.name} parameter for ${provider}`);
113
+ const id = params[0][1];
114
+ if (!form.query.pattern.test(id))
115
+ throw new Error(`stable URL ${form.query.name} does not look like a ${provider} identifier`);
116
+ return `https://${host}${form.path}?${form.query.name}=${id}`;
117
+ }
118
+ export function sanitizeDiscoveryStableUrl(provider, value) {
119
+ let url;
120
+ try {
121
+ url = new URL(value);
122
+ }
123
+ catch {
124
+ throw new Error("stable URL is invalid");
125
+ }
126
+ if (url.protocol !== "https:")
127
+ throw new Error("stable URL must use HTTPS");
128
+ if (url.username || url.password)
129
+ throw new Error("stable URL must not contain credentials");
130
+ const templateForm = PROVIDER_PATH_TEMPLATE_FORMS[provider.toLowerCase()];
131
+ if (templateForm)
132
+ return sanitizeDeclaredPathTemplateForm(provider, url, templateForm);
133
+ const form = PROVIDER_LINK_FORMS[provider.toLowerCase()];
134
+ if (form)
135
+ return sanitizeDeclaredLinkForm(provider, url, form);
136
+ if (url.search)
137
+ throw new Error("stable URL must not contain a query string");
138
+ if (url.hash)
139
+ throw new Error("stable URL must not contain a fragment");
140
+ const pathForm = PROVIDER_PATH_FORMS[provider.toLowerCase()];
141
+ if (pathForm) {
142
+ const host = url.hostname.toLowerCase();
143
+ const hostOk = host.endsWith(pathForm.suffix) && /^[a-z0-9-]+$/.test(host.slice(0, -pathForm.suffix.length));
144
+ if (!hostOk)
145
+ throw new Error(`stable URL must use an approved host for ${provider}`);
146
+ if (!pathForm.path.test(url.pathname))
147
+ throw new Error(`stable URL must use the canonical ${provider} path`);
148
+ return url.toString();
149
+ }
150
+ const approved = PROVIDER_HOSTS[provider.toLowerCase()];
151
+ if (!approved || !approved.includes(url.hostname.toLowerCase()))
152
+ throw new Error(`stable URL must use an approved host for ${provider}`);
153
+ return url.toString();
154
+ }
155
+ export function isValidLinkedinUrl(value) {
156
+ try {
157
+ const url = new URL(value);
158
+ return url.protocol === "https:" && (url.hostname === "linkedin.com" || url.hostname.endsWith(".linkedin.com"));
159
+ }
160
+ catch {
161
+ return false;
162
+ }
163
+ }
164
+ const EMAIL_SHAPE = /[\w.+-]+@[\w-]+\.[\w.-]+/;
165
+ // Domains where the mailbox says nothing about the company. Deriving "Gmail"
166
+ // as an employer is noise, not a candidate.
167
+ const FREE_MAIL_DOMAINS = new Set([
168
+ "gmail.com", "googlemail.com", "yahoo.com", "ymail.com", "outlook.com", "hotmail.com",
169
+ "live.com", "msn.com", "icloud.com", "me.com", "mac.com", "aol.com", "proton.me",
170
+ "protonmail.com", "pm.me", "fastmail.com", "hey.com", "gmx.com", "zoho.com",
171
+ ]);
172
+ // R-004: the email-privacy rule. The company is derived from the DOMAIN and the
173
+ // address itself is discarded here, before any hint exists — a raw attendee
174
+ // email must never persist in a committed file. Free-mail domains yield null.
175
+ export function companyFromEmailDomain(email) {
176
+ const at = email.lastIndexOf("@");
177
+ if (at < 1)
178
+ return null;
179
+ const domain = email.slice(at + 1).trim().toLowerCase();
180
+ if (!domain || FREE_MAIL_DOMAINS.has(domain))
181
+ return null;
182
+ const base = domain.split(".")[0];
183
+ if (!base || /^\d+$/.test(base))
184
+ return null;
185
+ return base.charAt(0).toUpperCase() + base.slice(1);
186
+ }
187
+ // Parses a provider display label. "Chris Exampleton from Exampleton Brothers"
188
+ // yields the name AND the company; a plain capitalized person name yields the
189
+ // name; an email-shaped or unrecognized label yields nothing — conservative on
190
+ // purpose, because every hint is a suggestion a person will read.
191
+ const DISPLAY_NAME = /^([A-Z][a-zA-Z'’.-]*(?: [A-Z][a-zA-Z'’.-]*)+)$/;
192
+ const DISPLAY_NAME_WITH_COMPANY = /^([A-Z][a-zA-Z'’.-]*(?: [A-Z][a-zA-Z'’.-]*)+)\s+(?:from|of|at)\s+([A-Z][A-Za-z0-9&.'’-]*(?: [A-Z][A-Za-z0-9&.'’-]*){0,3})$/;
193
+ export function contactHintsFromDisplayLabel(label, source) {
194
+ const trimmed = label.trim();
195
+ if (!trimmed || EMAIL_SHAPE.test(trimmed))
196
+ return [];
197
+ const withCompany = DISPLAY_NAME_WITH_COMPANY.exec(trimmed);
198
+ if (withCompany) {
199
+ return [
200
+ { field: "full_name", value: withCompany[1], source },
201
+ { field: "company", value: withCompany[2].replace(/[.,;:]$/, ""), source },
202
+ ];
203
+ }
204
+ const plain = DISPLAY_NAME.exec(trimmed);
205
+ return plain ? [{ field: "full_name", value: plain[1], source }] : [];
206
+ }
207
+ export function dedupeContactHints(hints) {
208
+ const seen = new Set();
209
+ const out = [];
210
+ for (const hint of hints) {
211
+ const key = `${hint.field}:${hint.value.trim().toLowerCase()}`;
212
+ if (seen.has(key))
213
+ continue;
214
+ seen.add(key);
215
+ out.push(hint);
216
+ }
217
+ return out;
218
+ }
219
+ export function adapterCapabilityState(capabilities) {
220
+ if (!capabilities.list_sessions || !capabilities.open_transcript)
221
+ return "needs_provider_connector";
222
+ if (!capabilities.stable_link || !capabilities.timestamps)
223
+ return "ready_with_coverage_caveat";
224
+ return "ready";
225
+ }
226
+ export function validateNormalizedSession(session) {
227
+ const errors = [];
228
+ if (session.schema_version !== "0.1")
229
+ errors.push("schema_version must be 0.1");
230
+ if (!session.provider?.trim())
231
+ errors.push("provider is required");
232
+ if (!session.connection_alias?.trim())
233
+ errors.push("connection_alias is required");
234
+ if (!session.runtime_handle?.trim())
235
+ errors.push("runtime_handle is required in private runtime memory");
236
+ if (session.completion_state !== "completed")
237
+ errors.push("completion_state must be completed");
238
+ if (!session.title?.trim())
239
+ errors.push("title is required");
240
+ if (!Array.isArray(session.participants))
241
+ errors.push("participants must be an array");
242
+ if (!Number.isFinite(Date.parse(session.completed_at)))
243
+ errors.push("completed_at must be an ISO timestamp");
244
+ if (session.stable_url) {
245
+ try {
246
+ sanitizeDiscoveryStableUrl(session.provider, session.stable_url);
247
+ }
248
+ catch (error) {
249
+ errors.push(error instanceof Error ? error.message : "invalid stable URL");
250
+ }
251
+ }
252
+ if (session.contact_hints !== undefined) {
253
+ if (!Array.isArray(session.contact_hints))
254
+ errors.push("contact_hints must be an array");
255
+ else {
256
+ for (const [index, hint] of session.contact_hints.entries()) {
257
+ const label = `contact hint ${index + 1}`;
258
+ if (!hint || typeof hint !== "object" || Array.isArray(hint)) {
259
+ errors.push(`${label} must be an object`);
260
+ continue;
261
+ }
262
+ if (hint.field !== "full_name" && hint.field !== "company" && hint.field !== "title" && hint.field !== "linkedin")
263
+ errors.push(`${label}: invalid field`);
264
+ if (typeof hint.value !== "string" || !hint.value.trim())
265
+ errors.push(`${label}: value is required`);
266
+ else if (EMAIL_SHAPE.test(hint.value))
267
+ errors.push(`${label}: value must not contain an email address — derive the company from the domain and keep the address out`);
268
+ else if (hint.field === "linkedin" && !isValidLinkedinUrl(hint.value))
269
+ errors.push(`${label}: linkedin value must be an https linkedin.com URL`);
270
+ if (typeof hint.source !== "string" || !hint.source.trim())
271
+ errors.push(`${label}: source is required`);
272
+ }
273
+ }
274
+ }
275
+ return errors;
276
+ }
277
+ const NORMALIZED_SESSION_KEYS = new Set([
278
+ "schema_version",
279
+ "provider",
280
+ "connection_alias",
281
+ "runtime_handle",
282
+ "completed_at",
283
+ "completion_state",
284
+ "title",
285
+ "participants",
286
+ "stable_url",
287
+ "capabilities",
288
+ "access",
289
+ "contact_hints",
290
+ ]);
291
+ const CAPABILITY_KEYS = new Set(["list_sessions", "open_transcript", "stable_link", "timestamps"]);
292
+ const CONTACT_HINT_KEYS = new Set(["field", "value", "source"]);
293
+ export function assertNormalizedSessionShape(value) {
294
+ if (!value || typeof value !== "object" || Array.isArray(value))
295
+ throw new Error("session metadata must be an object");
296
+ const session = value;
297
+ for (const key of Object.keys(session)) {
298
+ if (!NORMALIZED_SESSION_KEYS.has(key))
299
+ throw new Error(`session metadata contains forbidden or unknown field ${key}`);
300
+ }
301
+ if (!session.capabilities || typeof session.capabilities !== "object" || Array.isArray(session.capabilities))
302
+ throw new Error("session capabilities must be an object");
303
+ for (const key of Object.keys(session.capabilities)) {
304
+ if (!CAPABILITY_KEYS.has(key))
305
+ throw new Error(`session capabilities contain unknown field ${key}`);
306
+ }
307
+ if (session.contact_hints !== undefined) {
308
+ if (!Array.isArray(session.contact_hints))
309
+ throw new Error("session contact_hints must be an array");
310
+ for (const hint of session.contact_hints) {
311
+ if (!hint || typeof hint !== "object" || Array.isArray(hint))
312
+ throw new Error("session contact hints must be objects");
313
+ for (const key of Object.keys(hint)) {
314
+ if (!CONTACT_HINT_KEYS.has(key))
315
+ throw new Error(`session contact hint contains forbidden or unknown field ${key}`);
316
+ }
317
+ }
318
+ }
319
+ const errors = validateNormalizedSession(session);
320
+ if (errors.length)
321
+ throw new Error(`invalid normalized session metadata: ${errors.join("; ")}`);
322
+ }
@@ -0,0 +1,52 @@
1
+ export type DiscoverySyncAction = "list_metadata" | "match" | "open_authorized_transcript" | "apply_sanitized_evidence";
2
+ export type DiscoverySyncLane = {
3
+ lane_id: string;
4
+ kind: "study" | "inbox";
5
+ provider: string;
6
+ connection_alias: string;
7
+ study_id?: string;
8
+ scope_id?: string;
9
+ lifecycle: string;
10
+ allowed_actions: DiscoverySyncAction[];
11
+ };
12
+ export type DiscoverySyncPlan = {
13
+ schema_version: "0.1";
14
+ generated_at: string;
15
+ lanes: DiscoverySyncLane[];
16
+ errors: string[];
17
+ connector_reads: 0;
18
+ };
19
+ export declare function planDiscoverySync(root: string, now: string): DiscoverySyncPlan;
20
+ export type DiscoveryLaneResult = {
21
+ lane_id: string;
22
+ provider: string;
23
+ status: "clean_no_op" | "needs_review" | "blocked" | "provider_failure";
24
+ writes: string[];
25
+ error_code?: string;
26
+ };
27
+ export type DiscoverySyncAggregate = {
28
+ status: "clean_no_op" | "ok" | "needs_review" | "invalid";
29
+ writes: string[];
30
+ errors: Array<{
31
+ lane_id: string;
32
+ provider: string;
33
+ code: string;
34
+ }>;
35
+ coverage: {
36
+ lanes: number;
37
+ succeeded: number;
38
+ failed: number;
39
+ partial_failure: boolean;
40
+ };
41
+ };
42
+ export declare function aggregateDiscoverySync(results: DiscoveryLaneResult[]): DiscoverySyncAggregate;
43
+ export type RepoSnapshot = {
44
+ root: string;
45
+ files: Record<string, string>;
46
+ };
47
+ export declare function snapshotDiscoverySyncPaths(root: string): RepoSnapshot;
48
+ export declare function validateDiscoverySyncWrites(before: RepoSnapshot, after: RepoSnapshot): {
49
+ ok: boolean;
50
+ changed: string[];
51
+ forbidden: string[];
52
+ };
@@ -0,0 +1,151 @@
1
+ import { existsSync, readFileSync, readdirSync } from "node:fs";
2
+ import path from "node:path";
3
+ import { approvalStates, sourceKey } from "./sourceIngress.js";
4
+ import { loadDiscoveryRun } from "./discoveryArtifacts.js";
5
+ import { parse } from "./vendorYaml.js";
6
+ const TERMINAL = new Set(["complete", "revoked"]);
7
+ function actionsFor(lifecycle) {
8
+ if (lifecycle === "awaiting_interview" || lifecycle === "draft" || lifecycle === "needs_review")
9
+ return [];
10
+ if (lifecycle === "ready_to_synthesize")
11
+ return ["open_authorized_transcript", "apply_sanitized_evidence"];
12
+ return ["list_metadata", "match", "open_authorized_transcript", "apply_sanitized_evidence"];
13
+ }
14
+ function hasCustomerSelector(scope) {
15
+ // call_owners is a gong selector kind (gong-discovery-connector-v0 R-004)
16
+ // and organizations/brands are zendesk kinds (zendesk-discovery-connector-v0
17
+ // R-004); other providers leave them unset, so this stays the generic inbox
18
+ // rule.
19
+ return [scope.participant_allowlist, scope.domain_allowlist, scope.title_tokens, scope.call_owners, scope.organizations, scope.brands]
20
+ .some((values) => Array.isArray(values) && values.some((value) => typeof value === "string" && value.trim()));
21
+ }
22
+ function scopeApprovalAllows(inboxDir, scope) {
23
+ if (!scope.provider || !scope.connection_alias || !scope.scope_id)
24
+ return false;
25
+ const approval = approvalStates(inboxDir).get(sourceKey({ provider: scope.provider, connection_id: scope.connection_alias }));
26
+ // Approvals accumulate per scope (#528): any approved scope on the connection
27
+ // may cover this inbox scope; scopes arrive trimmed from approvalStates.
28
+ return (approval?.state === "approved" &&
29
+ approval.scopes.some((approved) => approved === "*" || approved === scope.scope_id || approved === `scope:${scope.scope_id}`));
30
+ }
31
+ export function planDiscoverySync(root, now) {
32
+ const lanes = [];
33
+ const errors = [];
34
+ const discoveryRoot = path.join(root, "discovery");
35
+ if (!existsSync(discoveryRoot))
36
+ return { schema_version: "0.1", generated_at: now, lanes, errors: ["discovery/ is missing"], connector_reads: 0 };
37
+ for (const entry of readdirSync(discoveryRoot, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
38
+ if (!entry.isDirectory() || entry.name === "inbox")
39
+ continue;
40
+ const dir = path.join(discoveryRoot, entry.name);
41
+ try {
42
+ const run = loadDiscoveryRun(dir);
43
+ if (TERMINAL.has(run.state))
44
+ continue;
45
+ lanes.push({
46
+ lane_id: `study:${run.study_id}`,
47
+ kind: "study",
48
+ provider: run.source.provider,
49
+ connection_alias: run.source.connection_alias,
50
+ study_id: run.study_id,
51
+ lifecycle: run.state,
52
+ allowed_actions: actionsFor(run.state),
53
+ });
54
+ }
55
+ catch {
56
+ errors.push(`${entry.name}: invalid discovery run`);
57
+ }
58
+ }
59
+ const inboxDir = path.join(discoveryRoot, "inbox");
60
+ const scopePath = path.join(inboxDir, "scope.yaml");
61
+ if (existsSync(scopePath)) {
62
+ let scopes = [];
63
+ try {
64
+ const doc = parse(readFileSync(scopePath, "utf8"));
65
+ scopes = Array.isArray(doc.scopes) ? doc.scopes : [];
66
+ }
67
+ catch {
68
+ errors.push("inbox/scope.yaml is invalid");
69
+ }
70
+ for (const scope of scopes) {
71
+ if (scope.state !== "approved")
72
+ continue;
73
+ if (!hasCustomerSelector(scope)) {
74
+ errors.push(`${scope.scope_id ?? "inbox scope"}: approved ingestion requires at least one customer selector`);
75
+ continue;
76
+ }
77
+ if (!scope.time_window?.start || !scope.time_window?.end) {
78
+ errors.push(`${scope.scope_id ?? "inbox scope"}: bounded time window is required`);
79
+ continue;
80
+ }
81
+ if (!scopeApprovalAllows(inboxDir, scope)) {
82
+ errors.push(`${scope.scope_id ?? "inbox scope"}: matching source approval is missing`);
83
+ continue;
84
+ }
85
+ if (!scope.scope_id || !scope.provider || !scope.connection_alias) {
86
+ errors.push("inbox scope identity is incomplete");
87
+ continue;
88
+ }
89
+ lanes.push({
90
+ lane_id: `inbox:${scope.scope_id}`,
91
+ kind: "inbox",
92
+ provider: scope.provider,
93
+ connection_alias: scope.connection_alias,
94
+ scope_id: scope.scope_id,
95
+ lifecycle: "approved",
96
+ allowed_actions: ["list_metadata", "match", "open_authorized_transcript", "apply_sanitized_evidence"],
97
+ });
98
+ }
99
+ }
100
+ return { schema_version: "0.1", generated_at: now, lanes, errors, connector_reads: 0 };
101
+ }
102
+ export function aggregateDiscoverySync(results) {
103
+ const errors = results
104
+ .filter((entry) => entry.status === "provider_failure" || entry.status === "blocked")
105
+ .map((entry) => ({ lane_id: entry.lane_id, provider: entry.provider, code: entry.error_code ?? "E_DISCOVERY_VALIDATION" }));
106
+ const writes = [...new Set(results.flatMap((entry) => entry.writes))].sort();
107
+ const succeeded = results.length - errors.length;
108
+ const status = results.length === 0
109
+ ? "clean_no_op"
110
+ : results.some((entry) => entry.status === "needs_review")
111
+ ? "needs_review"
112
+ : errors.length === results.length
113
+ ? "invalid"
114
+ : writes.length
115
+ ? "ok"
116
+ : "clean_no_op";
117
+ return { status, writes, errors, coverage: { lanes: results.length, succeeded, failed: errors.length, partial_failure: errors.length > 0 && succeeded > 0 } };
118
+ }
119
+ function snapshotFiles(root, dir = root) {
120
+ const files = {};
121
+ if (!existsSync(dir))
122
+ return files;
123
+ for (const entry of readdirSync(dir, { withFileTypes: true })) {
124
+ if ([".git", "node_modules", "dist"].includes(entry.name))
125
+ continue;
126
+ const full = path.join(dir, entry.name);
127
+ const relative = path.relative(root, full).split(path.sep).join("/");
128
+ if (entry.isDirectory()) {
129
+ if (relative === ".foo" || relative.startsWith(".foo/")) {
130
+ if (relative !== ".foo" && relative !== ".foo/discovery" && !relative.startsWith(".foo/discovery/"))
131
+ continue;
132
+ }
133
+ Object.assign(files, snapshotFiles(root, full));
134
+ }
135
+ else if (entry.isFile()) {
136
+ files[relative] = readFileSync(full, "utf8");
137
+ }
138
+ }
139
+ return files;
140
+ }
141
+ export function snapshotDiscoverySyncPaths(root) {
142
+ return { root: path.resolve(root), files: snapshotFiles(path.resolve(root)) };
143
+ }
144
+ export function validateDiscoverySyncWrites(before, after) {
145
+ if (before.root !== after.root)
146
+ return { ok: false, changed: [], forbidden: ["snapshot roots differ"] };
147
+ const paths = new Set([...Object.keys(before.files), ...Object.keys(after.files)]);
148
+ const changed = [...paths].filter((file) => before.files[file] !== after.files[file]).sort();
149
+ const forbidden = changed.filter((file) => !file.startsWith("discovery/") && !file.startsWith(".foo/discovery/"));
150
+ return { ok: forbidden.length === 0, changed, forbidden };
151
+ }
@@ -0,0 +1,57 @@
1
+ import { type DiscoveryEvidenceEnvelope, type DiscoveryLane } from "./discoveryEvidence.js";
2
+ export type SynthesisTransport = (request: {
3
+ model: string;
4
+ system: string;
5
+ user: string;
6
+ max_tokens: number;
7
+ }) => Promise<{
8
+ text: string;
9
+ stop_reason?: string;
10
+ usage?: {
11
+ input_tokens: number;
12
+ output_tokens: number;
13
+ };
14
+ }>;
15
+ export type SynthesisInput = {
16
+ target_id: string;
17
+ lane: DiscoveryLane;
18
+ source: {
19
+ provider: string;
20
+ stable_url?: string;
21
+ source_fingerprint: string;
22
+ completed_at: string;
23
+ locator_capability: "timestamp" | "section" | "none";
24
+ };
25
+ title: string;
26
+ hypotheses: Array<{
27
+ id: string;
28
+ text: string;
29
+ }>;
30
+ transcript_text: string;
31
+ transcript_complete: boolean;
32
+ caveats?: string[];
33
+ };
34
+ export type SynthesisResult = {
35
+ envelope: DiscoveryEvidenceEnvelope;
36
+ model: string;
37
+ usage?: {
38
+ input_tokens: number;
39
+ output_tokens: number;
40
+ };
41
+ };
42
+ export declare const SYNTHESIS_MAX_OUTPUT_TOKENS = 8000;
43
+ export declare function defaultSynthesisModel(env?: NodeJS.ProcessEnv): string;
44
+ export declare function synthesisCredentialPresent(env?: NodeJS.ProcessEnv): boolean;
45
+ export declare const SYNTHESIS_SYSTEM_PROMPT: string;
46
+ export declare function synthesisUserMessage(input: SynthesisInput): string;
47
+ export declare function anthropicSynthesisTransport(deps?: {
48
+ fetchImpl?: typeof globalThis.fetch;
49
+ env?: NodeJS.ProcessEnv;
50
+ }): SynthesisTransport;
51
+ export declare function extractSynthesisJsonObject(text: string): Record<string, unknown>;
52
+ export declare function constructEnvelopeFromSynthesis(input: SynthesisInput, raw: Record<string, unknown>): DiscoveryEvidenceEnvelope;
53
+ export declare function buildEnvelopeFromSynthesis(input: SynthesisInput, raw: Record<string, unknown>): DiscoveryEvidenceEnvelope;
54
+ export declare function synthesizeDiscoveryEnvelope(input: SynthesisInput, options: {
55
+ model: string;
56
+ transport: SynthesisTransport;
57
+ }): Promise<SynthesisResult>;