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,466 @@
1
+ // `foo discovery gmail …` — the local worker for a verified user's Gmail
2
+ // connection (gmail-discovery-connector-v0, per-user mailbox, D-002).
3
+ //
4
+ // This is the gong worker contract (R-011) with the email class's stricter
5
+ // composition: listing happens only inside an approved inbox scope naming
6
+ // explicit Gmail labels, a bounded window, and a customer selector (R-004);
7
+ // the readiness probe never lists (a label-less threads.list would be a
8
+ // mailbox-wide read, so `ready` proves the credential with one profile call);
9
+ // listing fetches thread headers only (format=metadata); and process opens
10
+ // exactly one proof-authorized thread's bodies into process memory, reduces
11
+ // them in one bounded model request to a PII-minimized closed envelope,
12
+ // screens derived text against the thread's own participants, applies
13
+ // locally, and discards the text in a `finally` (R-007). Attachments are
14
+ // never opened (D-003) — the client has no attachment method at all. Message
15
+ // text never reaches argv, stdin, a file, Git, a log, an error, or the host
16
+ // agent's context; every surface this module writes is content-free.
17
+ //
18
+ // What it is not: live support. Gmail reports `contract_only` until the
19
+ // feasibility report (T-001) proves the API surface and a real per-user
20
+ // dogfood run (T-008) succeeds — and the connection belongs to the verified
21
+ // user whose mailbox it is, never to the workspace (R-010).
22
+ import { existsSync, readFileSync } from "node:fs";
23
+ import path from "node:path";
24
+ import { hasFlag } from "./cliArgs.js";
25
+ import { discoveryTargetDir, gmailScopeValidationError, inboxScope, isInboxTarget, localRoot, localTargetName, runDiscoveryCommand, } from "./discoveryCommand.js";
26
+ import { openAuthorizedTranscript } from "./discoveryProof.js";
27
+ import { gmailDiscoveryAdapter, gmailInternalOnly, gmailThreadAdmissibility } from "./discoveryProviders.js";
28
+ import { planDiscoverySync } from "./discoverySync.js";
29
+ import { anthropicSynthesisTransport, defaultSynthesisModel, synthesisCredentialPresent, synthesizeDiscoveryEnvelope } from "./discoverySynthesis.js";
30
+ import { GMAIL_POLICY, GmailClient, GmailRuntimeError, gmailConnectionConfigured, gmailCredentialBroker, resolveGmailConnection } from "./gmailNango.js";
31
+ const SUPPORT_STATE = "contract_only";
32
+ const MODE = "local_worker";
33
+ function result(status, options = {}) {
34
+ return { schema_version: "0.1", status, writes: [], errors: [], handoff: null, next_actions: [], coverage: {}, ...options };
35
+ }
36
+ function exitCodeFor(status) {
37
+ return ["ambiguous_match", "needs_source_approval", "needs_provider_access", "needs_provider_connector", "needs_research_proof", "invalid", "budget_exhausted"].includes(status) ? 1 : 0;
38
+ }
39
+ function outcome(value, json) {
40
+ const exitCode = exitCodeFor(value.status);
41
+ if (json)
42
+ return { output: `${JSON.stringify(value, null, 2)}\n`, exitCode, result: value };
43
+ const lines = [`Discovery (gmail): ${value.status}.`];
44
+ for (const error of value.errors)
45
+ lines.push(`- ${error.message}`);
46
+ if (value.handoff)
47
+ lines.push(`Handoff: ${value.handoff}`);
48
+ for (const action of value.next_actions)
49
+ lines.push(`Next: ${action}`);
50
+ return { output: `${lines.join("\n")}\n`, exitCode, result: value };
51
+ }
52
+ function invalid(message, json, code = "E_DISCOVERY_VALIDATION", targetId, coverage = {}) {
53
+ return outcome(result("invalid", { study_id: targetId, errors: [{ code, message }], coverage }), json);
54
+ }
55
+ // Typed provider failures map to the discovery command's closed status set.
56
+ // Messages are productfoo's own words: no provider body, address, or credential.
57
+ function fromRuntimeError(error, json, targetId, coverage) {
58
+ const base = { study_id: targetId, coverage: { ...coverage, provider_error: error.code, mode: MODE } };
59
+ switch (error.code) {
60
+ case "not_connected":
61
+ return outcome(result("needs_provider_connector", { ...base, errors: [{ code: "E_DISCOVERY_PROVIDER_CONNECTOR", message: error.message }], handoff: "Connect the Gmail mailbox through Nango and set NANGO_SECRET_KEY and FOO_GMAIL_NANGO_CONNECTION (plus NANGO_HOST / FOO_GMAIL_NANGO_INTEGRATION when they differ from the defaults); the connection belongs to the verified user whose mailbox it is." }), json);
62
+ case "auth_expired":
63
+ case "access_denied":
64
+ return outcome(result("needs_provider_access", { ...base, errors: [{ code: "E_DISCOVERY_PROVIDER_ACCESS", message: error.message }], handoff: "Reconnect the mailbox in Nango or grant the missing least-privilege read scope, then rerun." }), json);
65
+ case "budget_exhausted":
66
+ // A rate-limit stop is final for the run (R-004): the run ends, nothing
67
+ // widens, nothing retries. The next run needs a narrower window.
68
+ return outcome(result("budget_exhausted", { ...base, errors: [{ code: "E_DISCOVERY_VALIDATION", message: error.message }], handoff: "The agreed page or quota ceiling stopped this run; narrow the window or selectors and rerun — productfoo never widens the window or auto-retries." }), json);
69
+ default:
70
+ return outcome(result("invalid", { ...base, errors: [{ code: "E_DISCOVERY_VALIDATION", message: error.message }], handoff: error.code === "thread_too_large" || error.code === "provider_timeout" ? "Nothing was written and no retry was attempted; a new attempt needs a fresh match." : null }), json);
71
+ }
72
+ }
73
+ // D-001's pinned boundary (T-005): a thread is settled when its last message
74
+ // predates the scope window's end by the quiet period, unless it carries an
75
+ // explicit done label.
76
+ function quietPeriodBoundary(windowEnd, quietPeriodHours) {
77
+ return new Date(Date.parse(windowEnd) - quietPeriodHours * 3_600_000).toISOString();
78
+ }
79
+ // R-004: the gmail scope is the strictest composition — validation is
80
+ // pre-flight, so an invalid scope never reaches the provider.
81
+ function gmailTarget(root, targetId) {
82
+ if (!isInboxTarget(targetId)) {
83
+ // V0 keeps every Gmail read inside an approved, label-bearing scope; the
84
+ // study lane is deferred with the hosted service.
85
+ throw new Error("gmail evidence is inbox-scoped in V0: approve an inbox scope naming explicit labels, a bounded window, and a customer selector (R-004)");
86
+ }
87
+ const scope = inboxScope(root, targetId);
88
+ if (scope.provider.toLowerCase() !== "gmail")
89
+ throw new Error("this inbox scope is not a Gmail scope");
90
+ if (scope.state !== "approved")
91
+ throw new Error("this inbox scope is not approved");
92
+ // The workspace env selector is narrowing only and never counts toward the
93
+ // selector minimum.
94
+ const selectors = {
95
+ window: scope.time_window,
96
+ labels: scope.labels,
97
+ external_domains: scope.domain_allowlist,
98
+ external_names: scope.participant_allowlist,
99
+ include_internal_only: false,
100
+ };
101
+ const scopeError = gmailScopeValidationError(selectors);
102
+ if (scopeError)
103
+ throw new Error(scopeError);
104
+ return {
105
+ target_id: targetId,
106
+ lane: "unassigned_call_evidence",
107
+ dir: discoveryTargetDir(root, targetId),
108
+ connection_alias: scope.connection_alias,
109
+ window: { from: new Date(scope.time_window.start).toISOString(), to: new Date(scope.time_window.end).toISOString() },
110
+ labels: scope.labels ?? [],
111
+ external_domains: scope.domain_allowlist ?? [],
112
+ external_names: scope.participant_allowlist ?? [],
113
+ };
114
+ }
115
+ function toRecord(thread, ownerEmail, bodyAccess, quietPeriodEndsAt) {
116
+ if (thread.messages.length === 0)
117
+ return null;
118
+ const last = thread.messages[thread.messages.length - 1];
119
+ const lastAt = last.internal_date ? new Date(Number(last.internal_date)).toISOString() : undefined;
120
+ if (!lastAt || !Number.isFinite(Date.parse(lastAt)))
121
+ return null;
122
+ const participants = [];
123
+ for (const message of thread.messages) {
124
+ if (message.from?.display_name || message.from?.email)
125
+ participants.push({ ...(message.from.display_name ? { display_name: message.from.display_name } : {}), ...(message.from.email ? { email: message.from.email } : {}) });
126
+ for (const recipient of message.to ?? [])
127
+ participants.push({ ...(recipient.display_name ? { display_name: recipient.display_name } : {}), ...(recipient.email ? { email: recipient.email } : {}) });
128
+ }
129
+ const labels = [...new Set(thread.messages.flatMap((message) => message.labels ?? []))];
130
+ const subject = [...thread.messages].reverse().map((message) => message.subject?.trim()).find(Boolean);
131
+ return {
132
+ thread_id: thread.id,
133
+ snippet_subject: subject || last.subject?.trim() || "",
134
+ last_message_at: lastAt,
135
+ participants,
136
+ labels,
137
+ quiet_period_ends_at: quietPeriodEndsAt,
138
+ ...(ownerEmail ? { mailbox_owner_email: ownerEmail } : {}),
139
+ ...(bodyAccess === "granted" ? { body_access: "granted" } : { body_access: "missing" }),
140
+ };
141
+ }
142
+ async function connect(deps) {
143
+ const connection = await resolveGmailConnection({ env: deps.env, fetchImpl: deps.fetchImpl });
144
+ const client = new GmailClient(connection.credential, { fetchImpl: deps.fetchImpl, baseUrl: deps.env.FOO_GMAIL_API_BASE, sleep: deps.sleep, fetchTimeoutMs: deps.fetchTimeoutMs });
145
+ return { connection, client };
146
+ }
147
+ // The one bounded model request (R-007): render the thread's text bodies into
148
+ // role-labeled lines. The owner is "Mailbox owner"; every other address is
149
+ // "Customer". The rendered text exists only inside this function's caller.
150
+ function renderThread(thread, ownerEmail) {
151
+ const lines = [];
152
+ for (const message of thread.messages) {
153
+ const at = message.internal_date ? new Date(Number(message.internal_date)).toISOString().slice(0, 16).replace("T", " ") : "unknown date";
154
+ const role = ownerEmail && message.from?.email?.toLowerCase() === ownerEmail.toLowerCase() ? "Mailbox owner" : "Customer";
155
+ const body = message.body_text?.trim() || "(no text/plain part in this message)";
156
+ lines.push(`[${at}] ${role}: ${body.replaceAll(/\s+/g, " ").trim()}`);
157
+ }
158
+ return lines.join("\n");
159
+ }
160
+ function containsIdentity(envelopeText, identities) {
161
+ const haystack = envelopeText.toLowerCase();
162
+ return identities.some((identity) => identity.length >= 4 && haystack.includes(identity.toLowerCase()));
163
+ }
164
+ async function readyCommand(_root, json, deps) {
165
+ const env = deps.env;
166
+ const coverage = {
167
+ provider: "gmail",
168
+ mode: MODE,
169
+ support_state: SUPPORT_STATE,
170
+ // R-010: the mailbox is personal even at work — ownership is reported
171
+ // honestly, never emulated as workspace access.
172
+ connection_owner: "user",
173
+ configured: gmailConnectionConfigured(env),
174
+ credential_broker: gmailCredentialBroker(env),
175
+ synthesis: { available: synthesisCredentialPresent(env), model: defaultSynthesisModel(env) },
176
+ policy: { ...GMAIL_POLICY },
177
+ };
178
+ try {
179
+ const { connection, client } = await connect(deps);
180
+ // The readiness probe is one profile call: it proves the credential and
181
+ // reports the mailbox identity this connection owns, without listing —
182
+ // a label-less threads.list would be a mailbox-wide read (R-004).
183
+ const profile = await client.profile();
184
+ coverage.connection = connection.connection_ref;
185
+ coverage.credential_broker = connection.broker;
186
+ coverage.credential_type = connection.credential_type;
187
+ coverage.body_scope = connection.body_scope;
188
+ coverage.capabilities = { list_sessions: "pending_approved_scope", open_bodies: connection.body_scope === "granted", stable_link: false, timestamps: false, link_form: "none declared in V0 (R-006)" };
189
+ coverage.provider_calls = client.requests;
190
+ return outcome(result("ok", { coverage, next_actions: [
191
+ "Approve a bounded Gmail scope (explicit labels, a time window, and a customer selector), then run foo discovery gmail list <inbox:scope-id>.",
192
+ ...(connection.body_scope !== "granted" ? ["The connection lacks gmail.readonly: listing works, but process reports needs_provider_access until the least-privilege body scope is granted."] : []),
193
+ ...(coverage.synthesis && !coverage.synthesis.available ? ["Set ANTHROPIC_API_KEY (or ANTHROPIC_AUTH_TOKEN) before processing a thread."] : []),
194
+ ] }), json);
195
+ }
196
+ catch (error) {
197
+ if (error instanceof GmailRuntimeError)
198
+ return fromRuntimeError(error, json, undefined, coverage);
199
+ return invalid("Gmail readiness failed closed without exposing private source content.", json, "E_DISCOVERY_VALIDATION", undefined, coverage);
200
+ }
201
+ }
202
+ async function listCommand(root, targetId, json, deps) {
203
+ if (!targetId)
204
+ return invalid("Usage: foo discovery gmail list <inbox:scope-id>", json);
205
+ const now = deps.now();
206
+ let target;
207
+ try {
208
+ target = gmailTarget(root, targetId);
209
+ }
210
+ catch (error) {
211
+ return invalid(error instanceof Error ? error.message : "invalid discovery target", json, "E_DISCOVERY_VALIDATION", targetId);
212
+ }
213
+ const coverage = { mode: MODE, window: target.window, lane: target.lane, labels: target.labels.length, bodies_opened: false };
214
+ try {
215
+ const { connection, client } = await connect(deps);
216
+ coverage.connection = connection.connection_ref;
217
+ coverage.body_scope = connection.body_scope;
218
+ const profile = await client.profile();
219
+ const quietPeriodEndsAt = quietPeriodBoundary(target.window.to, GMAIL_POLICY.quiet_period_hours);
220
+ const listing = await client.listThreads({ label_ids: target.labels, from: target.window.from, to: target.window.to });
221
+ // A ceiling stop is final for the run (R-004): no further provider reads
222
+ // happen — the run waits for a window that fits the agreed pages, never
223
+ // for a wider one.
224
+ if (listing.truncated) {
225
+ return outcome(result("budget_exhausted", {
226
+ study_id: targetId,
227
+ coverage: { ...coverage, provider_error: "budget_exhausted", threads_listed: listing.threads.length, pages: listing.pages },
228
+ errors: [{ code: "E_DISCOVERY_VALIDATION", message: `The window holds more threads than the ${GMAIL_POLICY.max_list_pages}-page ceiling; narrow the window or selectors instead of widening the read.` }],
229
+ handoff: "Narrow the approved window or selectors and list again; the run never widens the window and never retries.",
230
+ }), json);
231
+ }
232
+ const sessions = [];
233
+ let internalExcluded = 0;
234
+ let incomplete = 0;
235
+ for (const ref of listing.threads) {
236
+ // Header-only reads (format=metadata): the listing never opens a body.
237
+ const thread = await client.threadMetadata(ref.id);
238
+ const record = toRecord(thread, profile.email, connection.body_scope, quietPeriodEndsAt);
239
+ if (!record) {
240
+ incomplete += 1;
241
+ continue;
242
+ }
243
+ if (gmailInternalOnly(record)) {
244
+ internalExcluded += 1;
245
+ continue;
246
+ }
247
+ // D-001: only settled threads are evidence; the quiet-period boundary
248
+ // and the done-label override are applied by the adapter.
249
+ if (gmailThreadAdmissibility(record) !== "completed") {
250
+ incomplete += 1;
251
+ continue;
252
+ }
253
+ sessions.push({ ...gmailDiscoveryAdapter.normalizeSession(record, target.connection_alias), access: "available" });
254
+ }
255
+ Object.assign(coverage, { threads_listed: listing.threads.length, pages: listing.pages, internal_only_excluded: internalExcluded, incomplete_excluded: incomplete, candidates_evaluated: sessions.length, provider_calls: client.requests });
256
+ if (sessions.length === 0)
257
+ return outcome(result("awaiting_match", { study_id: targetId, coverage, next_actions: ["No settled thread with an external participant was listed inside the approved scope."] }), json);
258
+ // One session at a time so each source gets its own one-time proof.
259
+ let lastMiss = null;
260
+ let evaluated = 0;
261
+ for (const session of sessions) {
262
+ evaluated += 1;
263
+ const matched = await runDiscoveryCommand(root, ["match", targetId, "--sessions-stdin", "--json"], JSON.stringify([session]), now);
264
+ if (matched.result.status === "awaiting_match") {
265
+ lastMiss = matched.result;
266
+ continue;
267
+ }
268
+ // R-008: the shared gate refuses a session that cannot open its
269
+ // transcript generically (needs_provider_connector); for Gmail the actual
270
+ // cause is observable — a missing body scope is an access problem, not a
271
+ // connector one. The match state the shared command already wrote stays
272
+ // intact: degrade to needs_provider_access with the honest handoff,
273
+ // preserving the metadata and the match.
274
+ if (matched.result.status === "needs_provider_connector" && connection.body_scope !== "granted") {
275
+ return outcome(result("needs_provider_access", {
276
+ study_id: targetId,
277
+ errors: [{ code: "E_DISCOVERY_PROVIDER_ACCESS", message: "The Gmail connection lacks the gmail.readonly scope; the thread's metadata matched, but its body cannot be opened." }],
278
+ handoff: "Grant the least-privilege read scope (gmail.readonly) on the connection in Nango, then rerun foo discovery gmail process.",
279
+ coverage: { ...matched.result.coverage, ...coverage, candidates_evaluated: evaluated, candidates_remaining: sessions.length - evaluated },
280
+ }), json);
281
+ }
282
+ const remaining = sessions.length - evaluated;
283
+ return outcome({
284
+ ...matched.result,
285
+ coverage: { ...matched.result.coverage, ...coverage, candidates_evaluated: evaluated, candidates_remaining: remaining },
286
+ next_actions: [
287
+ ...matched.result.next_actions,
288
+ ...(matched.result.status === "ok" ? [`Run foo discovery gmail process ${targetId} to open this one thread and apply the sanitized evidence.`] : []),
289
+ ...(remaining > 0 ? [`${remaining} more listed thread(s) were not evaluated; run list again after this one is processed.`] : []),
290
+ ],
291
+ }, json);
292
+ }
293
+ return outcome(result("awaiting_match", { study_id: targetId, coverage: { ...coverage, candidates_evaluated: evaluated }, next_actions: lastMiss?.next_actions ?? ["No listed thread matched the approved scope; nothing was opened."] }), json);
294
+ }
295
+ catch (error) {
296
+ if (error instanceof GmailRuntimeError)
297
+ return fromRuntimeError(error, json, targetId, coverage);
298
+ return invalid("Gmail listing failed closed without exposing private source content.", json, "E_DISCOVERY_VALIDATION", targetId, coverage);
299
+ }
300
+ }
301
+ function readMatch(root, targetId) {
302
+ const file = path.join(localRoot(root), localTargetName(targetId), "match.json");
303
+ if (!existsSync(file))
304
+ return null;
305
+ try {
306
+ return JSON.parse(readFileSync(file, "utf8"));
307
+ }
308
+ catch {
309
+ return null;
310
+ }
311
+ }
312
+ async function processCommand(root, targetId, json, deps) {
313
+ if (!targetId)
314
+ return invalid("Usage: foo discovery gmail process <inbox:scope-id>", json);
315
+ const now = deps.now();
316
+ let target;
317
+ try {
318
+ target = gmailTarget(root, targetId);
319
+ }
320
+ catch (error) {
321
+ return invalid(error instanceof Error ? error.message : "invalid discovery target", json, "E_DISCOVERY_VALIDATION", targetId);
322
+ }
323
+ const match = readMatch(root, targetId);
324
+ if (!match || !Array.isArray(match.candidates) || match.candidates.length === 0)
325
+ return invalid("No matched Gmail thread is recorded for this target; run foo discovery gmail list first.", json, "E_DISCOVERY_VALIDATION", targetId);
326
+ if (match.candidates.length > 1)
327
+ return outcome(result("ambiguous_match", { study_id: targetId, errors: [{ code: "E_DISCOVERY_AMBIGUOUS_MATCH", message: "Several threads matched; a person must select one before any thread is opened." }], handoff: `foo discovery review ${targetId} select-source --fingerprint <sha256> --by <person>` }), json);
328
+ const candidate = match.candidates[0];
329
+ const session = candidate.session;
330
+ const threadId = session.runtime_handle;
331
+ if (!synthesisCredentialPresent(deps.env))
332
+ return outcome(result("needs_research_proof", { study_id: targetId, errors: [{ code: "E_DISCOVERY_RESEARCH_PROOF", message: "No model credential is available for server-side synthesis, so the thread was not opened." }], handoff: "Set ANTHROPIC_API_KEY (or ANTHROPIC_AUTH_TOKEN) for this runtime, then rerun process." }), json);
333
+ const model = defaultSynthesisModel(deps.env);
334
+ const coverage = { mode: MODE, lane: target.lane, policy_version: GMAIL_POLICY.policy_version, model, bodies_opened: false };
335
+ let client;
336
+ try {
337
+ const connected = await connect(deps);
338
+ client = connected.client;
339
+ coverage.connection = connected.connection.connection_ref;
340
+ if (connected.connection.body_scope !== "granted") {
341
+ // R-008: a missing body scope degrades reads to needs_provider_access
342
+ // while metadata and the matching state stay intact.
343
+ return outcome(result("needs_provider_access", { study_id: targetId, errors: [{ code: "E_DISCOVERY_PROVIDER_ACCESS", message: "The Gmail connection lacks the gmail.readonly scope; the thread was not opened." }], handoff: "Grant the least-privilege read scope (gmail.readonly) on the connection in Nango, then rerun process.", coverage }), json);
344
+ }
345
+ const profile = await client.profile();
346
+ const identities = [];
347
+ for (const participant of [profile.email, ...(session.participants ?? [])]) {
348
+ if (participant?.trim())
349
+ identities.push(participant.trim());
350
+ }
351
+ let openError;
352
+ let threadText;
353
+ try {
354
+ threadText = await openAuthorizedTranscript({ study_dir: target.dir, local_root: localRoot(root), study_id: targetId, source_fingerprint: candidate.source_fingerprint, now }, async () => {
355
+ try {
356
+ // R-007: exactly one thread identifier, read under the minted proof.
357
+ return await client.thread(threadId).then((thread) => renderThread(thread, profile.email));
358
+ }
359
+ catch (error) {
360
+ if (error instanceof GmailRuntimeError)
361
+ openError = error;
362
+ throw error;
363
+ }
364
+ });
365
+ }
366
+ catch (error) {
367
+ if (openError)
368
+ return fromRuntimeError(openError, json, targetId, { ...coverage, thread_body_requests: client.requests.thread });
369
+ return outcome(result("needs_research_proof", { study_id: targetId, errors: [{ code: "E_DISCOVERY_RESEARCH_PROOF", message: "The thread was not opened: the discovery proof is missing, expired, already used, or no longer matches the approval." }], handoff: `Run foo discovery gmail list ${targetId} again to re-match and mint a fresh proof.` }), json);
370
+ }
371
+ coverage.thread_body_requests = client.requests.thread;
372
+ const estimatedTokens = Math.ceil(threadText.length / 4);
373
+ if (estimatedTokens > GMAIL_POLICY.max_model_input_tokens) {
374
+ return fromRuntimeError(new GmailRuntimeError("thread_too_large", "the thread exceeds the model-input ceiling; nothing was synthesized"), json, targetId, coverage);
375
+ }
376
+ let synthesized;
377
+ try {
378
+ synthesized = await synthesizeDiscoveryEnvelope({
379
+ target_id: targetId,
380
+ lane: target.lane,
381
+ source: { provider: "gmail", source_fingerprint: candidate.source_fingerprint, completed_at: session.completed_at, locator_capability: "none" },
382
+ title: session.title,
383
+ hypotheses: [],
384
+ // The thread's text body is the "transcript" here: role-labeled,
385
+ // screen-then-discard, never quoted verbatim by the model.
386
+ transcript_text: threadText,
387
+ transcript_complete: true,
388
+ caveats: ["Gmail declares no stable link form in V0 (R-006): the thread is identified by its matched fingerprint only.", "Attachments were never opened (D-003)."],
389
+ }, { model, transport: deps.transport });
390
+ }
391
+ catch {
392
+ // R-007: the thread text is discarded with this frame — it never
393
+ // reaches disk, a log, or an error surface, even when synthesis throws.
394
+ return invalid("Synthesis did not produce a valid closed envelope; nothing was written and the thread was discarded.", json, "E_DISCOVERY_INVALID_ENVELOPE", targetId, coverage);
395
+ }
396
+ coverage.synthesis_usage = synthesized.usage ?? null;
397
+ // Privacy screen beyond the envelope validator: the mailbox owner's
398
+ // address and the thread's own participant labels must not appear in
399
+ // derived text.
400
+ const derivedText = [
401
+ ...synthesized.envelope.findings.flatMap((finding) => [finding.headline, finding.paraphrase, finding.dissent]),
402
+ ...synthesized.envelope.coverage.caveats,
403
+ ].join("\n");
404
+ if (containsIdentity(derivedText, identities))
405
+ return invalid("Synthesis leaked a participant identity into derived text; nothing was written.", json, "E_DISCOVERY_INVALID_ENVELOPE", targetId, coverage);
406
+ const applied = await runDiscoveryCommand(root, ["apply", targetId, "--evidence-stdin", "--json"], JSON.stringify(synthesized.envelope), now);
407
+ return outcome({ ...applied.result, coverage: { ...applied.result.coverage, ...coverage } }, json);
408
+ }
409
+ catch (error) {
410
+ if (error instanceof GmailRuntimeError)
411
+ return fromRuntimeError(error, json, targetId, { ...coverage, thread_body_requests: client?.requests.thread ?? 0 });
412
+ return invalid("Gmail processing failed closed without exposing private source content.", json, "E_DISCOVERY_VALIDATION", targetId, coverage);
413
+ }
414
+ }
415
+ async function syncCommand(root, json, deps) {
416
+ const now = deps.now();
417
+ const plan = planDiscoverySync(root, now);
418
+ const lanes = plan.lanes.filter((lane) => lane.provider.toLowerCase() === "gmail");
419
+ if (lanes.length === 0)
420
+ return outcome(result("clean_no_op", { errors: plan.errors.map((message) => ({ code: "E_DISCOVERY_VALIDATION", message })), next_actions: ["No approved Gmail lane is active; approve an inbox scope naming explicit labels first."], coverage: { mode: MODE, lanes: 0, connector_reads: 0 } }), json);
421
+ const results = [];
422
+ // Metadata and match only: a thread body is opened only by an explicit
423
+ // `process` on one target. The loop stays unarmed (R-007/V-010).
424
+ for (const lane of lanes) {
425
+ const targetId = `inbox:${lane.scope_id}`;
426
+ const listed = await listCommand(root, targetId, true, deps);
427
+ results.push({ lane_id: lane.lane_id, status: listed.result.status, handoff: listed.result.handoff });
428
+ }
429
+ const ready = results.filter((entry) => entry.status === "ok").map((entry) => entry.lane_id);
430
+ return outcome(result("ok", {
431
+ errors: plan.errors.map((message) => ({ code: "E_DISCOVERY_VALIDATION", message })),
432
+ next_actions: [
433
+ ...ready.map((laneId) => `${laneId}: one thread matched — run foo discovery gmail process ${laneId} to open it.`),
434
+ ...results.filter((entry) => entry.status !== "ok").map((entry) => `${entry.lane_id}: ${entry.status}${entry.handoff ? ` — ${entry.handoff}` : ""}`),
435
+ ],
436
+ coverage: { mode: MODE, lanes: results.length, threads_opened: 0, results },
437
+ }), json);
438
+ }
439
+ export async function runGmailDiscovery(root, args, deps = {}) {
440
+ const json = hasFlag(args, "--json");
441
+ const env = deps.env ?? process.env;
442
+ const resolved = {
443
+ fetchImpl: deps.fetchImpl ?? globalThis.fetch,
444
+ env,
445
+ now: deps.now ?? (() => new Date().toISOString()),
446
+ sleep: deps.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms))),
447
+ transport: deps.transport ?? anthropicSynthesisTransport({ fetchImpl: deps.fetchImpl, env }),
448
+ fetchTimeoutMs: deps.fetchTimeoutMs ?? GMAIL_POLICY.fetch_timeout_ms,
449
+ };
450
+ const command = args[0];
451
+ const target = args[1] && !args[1].startsWith("--") ? args[1] : undefined;
452
+ try {
453
+ if (command === "ready")
454
+ return await readyCommand(root, json, resolved);
455
+ if (command === "list")
456
+ return await listCommand(root, target, json, resolved);
457
+ if (command === "process")
458
+ return await processCommand(root, target, json, resolved);
459
+ if (command === "sync")
460
+ return await syncCommand(root, json, resolved);
461
+ return invalid("Try: ready | list <inbox:scope-id> | process <inbox:scope-id> | sync", json);
462
+ }
463
+ catch {
464
+ return invalid("Gmail discovery failed closed without exposing private source content.", json);
465
+ }
466
+ }
@@ -0,0 +1,12 @@
1
+ import { type DiscoveryCommandOutcome } from "./discoveryCommand.js";
2
+ import { type SynthesisTransport } from "./discoverySynthesis.js";
3
+ import { type FetchLike } from "./gongNango.js";
4
+ export type GongDiscoveryDeps = {
5
+ fetchImpl?: FetchLike;
6
+ env?: NodeJS.ProcessEnv;
7
+ now?: () => string;
8
+ sleep?: (ms: number) => Promise<void>;
9
+ transport?: SynthesisTransport;
10
+ fetchTimeoutMs?: number;
11
+ };
12
+ export declare function runGongDiscovery(root: string, args: string[], deps?: GongDiscoveryDeps): Promise<DiscoveryCommandOutcome>;