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.
- package/LICENSE +201 -0
- package/README.md +338 -2
- package/bin/foo +3 -0
- package/bin/foo.mjs +42 -0
- package/commands/bug.md +31 -0
- package/commands/discovery.md +28 -0
- package/commands/ideate.md +27 -0
- package/commands/init.md +28 -0
- package/commands/pitch.md +27 -0
- package/commands/preview.md +25 -0
- package/commands/research.md +31 -0
- package/commands/roast.md +27 -0
- package/commands/spec.md +30 -0
- package/config/pricing.yaml +198 -0
- package/config/routing-policy.yaml +171 -0
- package/dist-public/src/activeModel.d.ts +83 -0
- package/dist-public/src/activeModel.js +331 -0
- package/dist-public/src/agentAdapters.d.ts +122 -0
- package/dist-public/src/agentAdapters.js +178 -0
- package/dist-public/src/agentState.d.ts +43 -0
- package/dist-public/src/agentState.js +177 -0
- package/dist-public/src/bugCapsule.d.ts +94 -0
- package/dist-public/src/bugCapsule.js +227 -0
- package/dist-public/src/bugCapture.d.ts +26 -0
- package/dist-public/src/bugCapture.js +251 -0
- package/dist-public/src/bugOwnerLog.d.ts +93 -0
- package/dist-public/src/bugOwnerLog.js +304 -0
- package/dist-public/src/bugReportStore.d.ts +48 -0
- package/dist-public/src/bugReportStore.js +860 -0
- package/dist-public/src/bugSpecContext.d.ts +7 -0
- package/dist-public/src/bugSpecContext.js +129 -0
- package/dist-public/src/channelFreshness.d.ts +25 -0
- package/dist-public/src/channelFreshness.js +194 -0
- package/dist-public/src/cli.d.ts +30 -0
- package/dist-public/src/cli.js +1667 -0
- package/dist-public/src/cliArgs.d.ts +7 -0
- package/dist-public/src/cliArgs.js +44 -0
- package/dist-public/src/codeCatalog.d.ts +41 -0
- package/dist-public/src/codeCatalog.js +197 -0
- package/dist-public/src/commandContract.d.ts +84 -0
- package/dist-public/src/commandContract.js +390 -0
- package/dist-public/src/commandUsage.d.ts +3 -0
- package/dist-public/src/commandUsage.js +30 -0
- package/dist-public/src/commands/agent.d.ts +60 -0
- package/dist-public/src/commands/agent.js +1353 -0
- package/dist-public/src/commands/asp.d.ts +8 -0
- package/dist-public/src/commands/asp.js +669 -0
- package/dist-public/src/commands/evalCmd.d.ts +4 -0
- package/dist-public/src/commands/evalCmd.js +405 -0
- package/dist-public/src/commands/login.d.ts +25 -0
- package/dist-public/src/commands/login.js +94 -0
- package/dist-public/src/commands/loop.d.ts +16 -0
- package/dist-public/src/commands/loop.js +619 -0
- package/dist-public/src/commands/reconcile.d.ts +41 -0
- package/dist-public/src/commands/reconcile.js +292 -0
- package/dist-public/src/commands/runtime.d.ts +1 -0
- package/dist-public/src/commands/runtime.js +36 -0
- package/dist-public/src/commands/scanRedact.d.ts +8 -0
- package/dist-public/src/commands/scanRedact.js +266 -0
- package/dist-public/src/commands/setup.d.ts +28 -0
- package/dist-public/src/commands/setup.js +398 -0
- package/dist-public/src/commands/shared.d.ts +17 -0
- package/dist-public/src/commands/shared.js +147 -0
- package/dist-public/src/commands/source.d.ts +1 -0
- package/dist-public/src/commands/source.js +134 -0
- package/dist-public/src/commands/specCmd.d.ts +24 -0
- package/dist-public/src/commands/specCmd.js +794 -0
- package/dist-public/src/commands/update.d.ts +61 -0
- package/dist-public/src/commands/update.js +244 -0
- package/dist-public/src/comprehensionSignal.d.ts +20 -0
- package/dist-public/src/comprehensionSignal.js +166 -0
- package/dist-public/src/conductor/diversity.d.ts +48 -0
- package/dist-public/src/conductor/diversity.js +423 -0
- package/dist-public/src/conductor/engine.d.ts +7 -0
- package/dist-public/src/conductor/engine.js +295 -0
- package/dist-public/src/conductor/families/ideate.d.ts +2 -0
- package/dist-public/src/conductor/families/ideate.js +255 -0
- package/dist-public/src/conductor/families/interview.d.ts +2 -0
- package/dist-public/src/conductor/families/interview.js +39 -0
- package/dist-public/src/conductor/families/specSwarm.d.ts +2 -0
- package/dist-public/src/conductor/families/specSwarm.js +146 -0
- package/dist-public/src/conductor/familyDelivery.d.ts +6 -0
- package/dist-public/src/conductor/familyDelivery.js +37 -0
- package/dist-public/src/conductor/familyPackArtifact.d.ts +4 -0
- package/dist-public/src/conductor/familyPackArtifact.js +260 -0
- package/dist-public/src/conductor/ideateDecision.d.ts +6 -0
- package/dist-public/src/conductor/ideateDecision.js +237 -0
- package/dist-public/src/conductor/pack.d.ts +26 -0
- package/dist-public/src/conductor/pack.js +75 -0
- package/dist-public/src/conductor/types.d.ts +111 -0
- package/dist-public/src/conductor/types.js +7 -0
- package/dist-public/src/configPath.d.ts +2 -0
- package/dist-public/src/configPath.js +7 -0
- package/dist-public/src/discoveryArtifacts.d.ts +54 -0
- package/dist-public/src/discoveryArtifacts.js +467 -0
- package/dist-public/src/discoveryCommand.d.ts +127 -0
- package/dist-public/src/discoveryCommand.js +2148 -0
- package/dist-public/src/discoveryConnections.d.ts +339 -0
- package/dist-public/src/discoveryConnections.js +613 -0
- package/dist-public/src/discoveryDocs.d.ts +129 -0
- package/dist-public/src/discoveryDocs.js +577 -0
- package/dist-public/src/discoveryEvidence.d.ts +59 -0
- package/dist-public/src/discoveryEvidence.js +198 -0
- package/dist-public/src/discoveryFireflies.d.ts +25 -0
- package/dist-public/src/discoveryFireflies.js +551 -0
- package/dist-public/src/discoveryGmail.d.ts +12 -0
- package/dist-public/src/discoveryGmail.js +466 -0
- package/dist-public/src/discoveryGong.d.ts +12 -0
- package/dist-public/src/discoveryGong.js +491 -0
- package/dist-public/src/discoveryLearningRouting.d.ts +90 -0
- package/dist-public/src/discoveryLearningRouting.js +405 -0
- package/dist-public/src/discoveryMatching.d.ts +26 -0
- package/dist-public/src/discoveryMatching.js +73 -0
- package/dist-public/src/discoveryOutlook.d.ts +12 -0
- package/dist-public/src/discoveryOutlook.js +492 -0
- package/dist-public/src/discoveryProcess.d.ts +221 -0
- package/dist-public/src/discoveryProcess.js +703 -0
- package/dist-public/src/discoveryProof.d.ts +143 -0
- package/dist-public/src/discoveryProof.js +529 -0
- package/dist-public/src/discoveryProviders.d.ts +152 -0
- package/dist-public/src/discoveryProviders.js +756 -0
- package/dist-public/src/discoveryReadiness.d.ts +219 -0
- package/dist-public/src/discoveryReadiness.js +672 -0
- package/dist-public/src/discoveryReconcile.d.ts +57 -0
- package/dist-public/src/discoveryReconcile.js +424 -0
- package/dist-public/src/discoverySlack.d.ts +12 -0
- package/dist-public/src/discoverySlack.js +452 -0
- package/dist-public/src/discoverySources.d.ts +56 -0
- package/dist-public/src/discoverySources.js +322 -0
- package/dist-public/src/discoverySync.d.ts +52 -0
- package/dist-public/src/discoverySync.js +151 -0
- package/dist-public/src/discoverySynthesis.d.ts +57 -0
- package/dist-public/src/discoverySynthesis.js +215 -0
- package/dist-public/src/discoveryTeams.d.ts +12 -0
- package/dist-public/src/discoveryTeams.js +516 -0
- package/dist-public/src/discoveryZoom.d.ts +12 -0
- package/dist-public/src/discoveryZoom.js +523 -0
- package/dist-public/src/elicitationSurface.d.ts +13 -0
- package/dist-public/src/elicitationSurface.js +77 -0
- package/dist-public/src/errorReport.d.ts +99 -0
- package/dist-public/src/errorReport.js +443 -0
- package/dist-public/src/errorTaxonomy.d.ts +84 -0
- package/dist-public/src/errorTaxonomy.js +189 -0
- package/dist-public/src/evalCalibration.d.ts +82 -0
- package/dist-public/src/evalCalibration.js +169 -0
- package/dist-public/src/evalChecks.d.ts +21 -0
- package/dist-public/src/evalChecks.js +295 -0
- package/dist-public/src/evalCompile.d.ts +56 -0
- package/dist-public/src/evalCompile.js +58 -0
- package/dist-public/src/evalImprove.d.ts +60 -0
- package/dist-public/src/evalImprove.js +175 -0
- package/dist-public/src/evalJudge.d.ts +47 -0
- package/dist-public/src/evalJudge.js +143 -0
- package/dist-public/src/evalLabels.d.ts +100 -0
- package/dist-public/src/evalLabels.js +287 -0
- package/dist-public/src/evalRecord.d.ts +68 -0
- package/dist-public/src/evalRecord.js +165 -0
- package/dist-public/src/evalRoast.d.ts +88 -0
- package/dist-public/src/evalRoast.js +249 -0
- package/dist-public/src/evidenceIntake.d.ts +66 -0
- package/dist-public/src/evidenceIntake.js +356 -0
- package/dist-public/src/feedback.d.ts +78 -0
- package/dist-public/src/feedback.js +294 -0
- package/dist-public/src/firefliesClient.d.ts +106 -0
- package/dist-public/src/firefliesClient.js +258 -0
- package/dist-public/src/firstMoveTensions.d.ts +10 -0
- package/dist-public/src/firstMoveTensions.js +93 -0
- package/dist-public/src/gitRun.d.ts +21 -0
- package/dist-public/src/gitRun.js +50 -0
- package/dist-public/src/githubReconcile.d.ts +94 -0
- package/dist-public/src/githubReconcile.js +205 -0
- package/dist-public/src/gmailNango.d.ts +131 -0
- package/dist-public/src/gmailNango.js +329 -0
- package/dist-public/src/gongNango.d.ts +118 -0
- package/dist-public/src/gongNango.js +308 -0
- package/dist-public/src/grammarGate.d.ts +13 -0
- package/dist-public/src/grammarGate.js +72 -0
- package/dist-public/src/harnessGates.d.ts +65 -0
- package/dist-public/src/harnessGates.js +168 -0
- package/dist-public/src/humanSurfaceEval.d.ts +19 -0
- package/dist-public/src/humanSurfaceEval.js +156 -0
- package/dist-public/src/ideate.d.ts +43 -0
- package/dist-public/src/ideate.js +255 -0
- package/dist-public/src/ideateContext.d.ts +11 -0
- package/dist-public/src/ideateContext.js +83 -0
- package/dist-public/src/index.d.ts +2 -0
- package/dist-public/src/index.js +4 -0
- package/dist-public/src/initCommand.d.ts +25 -0
- package/dist-public/src/initCommand.js +558 -0
- package/dist-public/src/interviewGate.d.ts +25 -0
- package/dist-public/src/interviewGate.js +126 -0
- package/dist-public/src/jsonFile.d.ts +2 -0
- package/dist-public/src/jsonFile.js +22 -0
- package/dist-public/src/loopBudget.d.ts +182 -0
- package/dist-public/src/loopBudget.js +494 -0
- package/dist-public/src/loopContracts.d.ts +35 -0
- package/dist-public/src/loopContracts.js +364 -0
- package/dist-public/src/machineState.d.ts +1 -0
- package/dist-public/src/machineState.js +9 -0
- package/dist-public/src/notionNango.d.ts +93 -0
- package/dist-public/src/notionNango.js +279 -0
- package/dist-public/src/orchestrationResult.d.ts +11 -0
- package/dist-public/src/orchestrationResult.js +73 -0
- package/dist-public/src/outlookNango.d.ts +124 -0
- package/dist-public/src/outlookNango.js +327 -0
- package/dist-public/src/packetCorrections.d.ts +40 -0
- package/dist-public/src/packetCorrections.js +172 -0
- package/dist-public/src/packetGlossary.d.ts +6 -0
- package/dist-public/src/packetGlossary.js +42 -0
- package/dist-public/src/packetIndex.d.ts +54 -0
- package/dist-public/src/packetIndex.js +744 -0
- package/dist-public/src/packetIndexEntry.d.ts +3 -0
- package/dist-public/src/packetIndexEntry.js +128 -0
- package/dist-public/src/packetIndexServe.d.ts +13 -0
- package/dist-public/src/packetIndexServe.js +113 -0
- package/dist-public/src/packetIndexWorker.d.ts +7 -0
- package/dist-public/src/packetIndexWorker.js +45 -0
- package/dist-public/src/packetLifecycle.d.ts +30 -0
- package/dist-public/src/packetLifecycle.js +127 -0
- package/dist-public/src/packetReconcile.d.ts +73 -0
- package/dist-public/src/packetReconcile.js +386 -0
- package/dist-public/src/packetStatus.d.ts +125 -0
- package/dist-public/src/packetStatus.js +770 -0
- package/dist-public/src/packetValidation.d.ts +17 -0
- package/dist-public/src/packetValidation.js +333 -0
- package/dist-public/src/packetVerify.d.ts +73 -0
- package/dist-public/src/packetVerify.js +303 -0
- package/dist-public/src/packetWrite.d.ts +52 -0
- package/dist-public/src/packetWrite.js +448 -0
- package/dist-public/src/pitch.d.ts +38 -0
- package/dist-public/src/pitch.js +154 -0
- package/dist-public/src/processMemo.d.ts +8 -0
- package/dist-public/src/processMemo.js +23 -0
- package/dist-public/src/productDetect.d.ts +30 -0
- package/dist-public/src/productDetect.js +376 -0
- package/dist-public/src/profile.d.ts +62 -0
- package/dist-public/src/profile.js +334 -0
- package/dist-public/src/questionModel.d.ts +46 -0
- package/dist-public/src/questionModel.js +74 -0
- package/dist-public/src/questionText.d.ts +7 -0
- package/dist-public/src/questionText.js +38 -0
- package/dist-public/src/redactConfig.d.ts +2 -0
- package/dist-public/src/redactConfig.js +11 -0
- package/dist-public/src/redactEngine.d.ts +59 -0
- package/dist-public/src/redactEngine.js +509 -0
- package/dist-public/src/redactGate.d.ts +63 -0
- package/dist-public/src/redactGate.js +209 -0
- package/dist-public/src/repoRead.d.ts +29 -0
- package/dist-public/src/repoRead.js +160 -0
- package/dist-public/src/research.d.ts +242 -0
- package/dist-public/src/research.js +1314 -0
- package/dist-public/src/researchCompletion.d.ts +34 -0
- package/dist-public/src/researchCompletion.js +123 -0
- package/dist-public/src/researchExecution.d.ts +47 -0
- package/dist-public/src/researchExecution.js +309 -0
- package/dist-public/src/researchFormat.d.ts +21 -0
- package/dist-public/src/researchFormat.js +77 -0
- package/dist-public/src/researchGate.d.ts +86 -0
- package/dist-public/src/researchGate.js +1036 -0
- package/dist-public/src/researchReceipt.d.ts +76 -0
- package/dist-public/src/researchReceipt.js +148 -0
- package/dist-public/src/researchRender.d.ts +2 -0
- package/dist-public/src/researchRender.js +267 -0
- package/dist-public/src/researchSigningKey.d.ts +19 -0
- package/dist-public/src/researchSigningKey.js +95 -0
- package/dist-public/src/riskClassify.d.ts +42 -0
- package/dist-public/src/riskClassify.js +149 -0
- package/dist-public/src/routines.d.ts +42 -0
- package/dist-public/src/routines.js +269 -0
- package/dist-public/src/routingPolicy.d.ts +172 -0
- package/dist-public/src/routingPolicy.js +646 -0
- package/dist-public/src/secretInput.d.ts +8 -0
- package/dist-public/src/secretInput.js +89 -0
- package/dist-public/src/selfCheck.d.ts +7 -0
- package/dist-public/src/selfCheck.js +473 -0
- package/dist-public/src/selfCheckCache.d.ts +29 -0
- package/dist-public/src/selfCheckCache.js +147 -0
- package/dist-public/src/severityClass.d.ts +15 -0
- package/dist-public/src/severityClass.js +22 -0
- package/dist-public/src/skillClientPolicy.d.ts +8 -0
- package/dist-public/src/skillClientPolicy.js +16 -0
- package/dist-public/src/skillLocal.d.ts +98 -0
- package/dist-public/src/skillLocal.js +463 -0
- package/dist-public/src/skillTreeDigest.d.ts +9 -0
- package/dist-public/src/skillTreeDigest.js +64 -0
- package/dist-public/src/slackNango.d.ts +62 -0
- package/dist-public/src/slackNango.js +208 -0
- package/dist-public/src/social/arxiv.d.ts +19 -0
- package/dist-public/src/social/arxiv.js +96 -0
- package/dist-public/src/social/github.d.ts +2 -0
- package/dist-public/src/social/github.js +78 -0
- package/dist-public/src/social/hackernews.d.ts +2 -0
- package/dist-public/src/social/hackernews.js +67 -0
- package/dist-public/src/social/index.d.ts +10 -0
- package/dist-public/src/social/index.js +79 -0
- package/dist-public/src/social/reddit.d.ts +2 -0
- package/dist-public/src/social/reddit.js +125 -0
- package/dist-public/src/social/types.d.ts +73 -0
- package/dist-public/src/social/types.js +61 -0
- package/dist-public/src/social/xml.d.ts +10 -0
- package/dist-public/src/social/xml.js +49 -0
- package/dist-public/src/social/youtube.d.ts +7 -0
- package/dist-public/src/social/youtube.js +217 -0
- package/dist-public/src/sourceIngress.d.ts +488 -0
- package/dist-public/src/sourceIngress.js +1651 -0
- package/dist-public/src/specAfter.d.ts +187 -0
- package/dist-public/src/specAfter.js +814 -0
- package/dist-public/src/specBudget.d.ts +124 -0
- package/dist-public/src/specBudget.js +424 -0
- package/dist-public/src/specDecisionSignals.d.ts +38 -0
- package/dist-public/src/specDecisionSignals.js +121 -0
- package/dist-public/src/specLinkedCommits.d.ts +7 -0
- package/dist-public/src/specLinkedCommits.js +107 -0
- package/dist-public/src/specSections.d.ts +105 -0
- package/dist-public/src/specSections.js +718 -0
- package/dist-public/src/statusGlyphs.d.ts +18 -0
- package/dist-public/src/statusGlyphs.js +75 -0
- package/dist-public/src/swarmCommand.d.ts +10 -0
- package/dist-public/src/swarmCommand.js +96 -0
- package/dist-public/src/teamsNango.d.ts +114 -0
- package/dist-public/src/teamsNango.js +301 -0
- package/dist-public/src/tokenStore.d.ts +10 -0
- package/dist-public/src/tokenStore.js +77 -0
- package/dist-public/src/trustEnvelope.d.ts +78 -0
- package/dist-public/src/trustEnvelope.js +392 -0
- package/dist-public/src/untrustedText.d.ts +6 -0
- package/dist-public/src/untrustedText.js +20 -0
- package/dist-public/src/updateNotice.d.ts +41 -0
- package/dist-public/src/updateNotice.js +290 -0
- package/dist-public/src/vendorYaml.d.ts +1 -0
- package/dist-public/src/vendorYaml.js +7374 -0
- package/dist-public/src/workflowRunActuals.d.ts +67 -0
- package/dist-public/src/workflowRunActuals.js +184 -0
- package/dist-public/src/zendeskDiscovery.d.ts +12 -0
- package/dist-public/src/zendeskDiscovery.js +513 -0
- package/dist-public/src/zendeskNango.d.ts +131 -0
- package/dist-public/src/zendeskNango.js +344 -0
- package/dist-public/src/zoomNango.d.ts +106 -0
- package/dist-public/src/zoomNango.js +351 -0
- package/docs/CHANGELOG.md +32 -0
- package/docs/THIRD_PARTY_NOTICES.md +36 -0
- package/package.json +71 -4
- package/schemas/bug-capsule-receipt.schema.json +54 -0
- package/schemas/bug-capsule.schema.json +189 -0
- package/schemas/discovery-evidence-envelope.schema.json +79 -0
- package/schemas/discovery-processing-receipt.schema.json +71 -0
- package/schemas/discovery-service-contract.schema.json +189 -0
- package/skills/AGENTS.md +32 -0
- package/skills/README.md +287 -0
- package/skills/bug/SKILL.md +49 -0
- package/skills/build-preview-spec/SKILL.md +103 -0
- package/skills/capture-external-artifact/SKILL.md +23 -0
- package/skills/capture-external-artifact/agents/openai.yaml +4 -0
- package/skills/capture-external-artifact/references/extractor-libraries.md +27 -0
- package/skills/check-spec-health/SKILL.md +45 -0
- package/skills/check-spec-health/agents/openai.yaml +4 -0
- package/skills/competitive-positioning/SKILL.md +86 -0
- package/skills/competitive-positioning/agents/openai.yaml +4 -0
- package/skills/cpo-lens/SKILL.md +398 -0
- package/skills/cpo-lens/agents/openai.yaml +4 -0
- package/skills/create-prd/SKILL.md +26 -0
- package/skills/create-prd/agents/openai.yaml +4 -0
- package/skills/customer-discovery/SKILL.md +192 -0
- package/skills/design-system/SKILL.md +49 -0
- package/skills/design-system/agents/openai.yaml +4 -0
- package/skills/discussion-guide/SKILL.md +75 -0
- package/skills/discussion-guide/agents/openai.yaml +4 -0
- package/skills/firecrawl-research-papers/SKILL.md +96 -0
- package/skills/firecrawl-research-papers/agents/openai.yaml +4 -0
- package/skills/foo-roast/SKILL.md +325 -0
- package/skills/generate-gtm-brief/SKILL.md +41 -0
- package/skills/generate-gtm-brief/agents/openai.yaml +4 -0
- package/skills/generate-gtm-brief/references/gtm-brief-template.md +56 -0
- package/skills/gong-quotes/SKILL.md +43 -0
- package/skills/gong-quotes/agents/openai.yaml +4 -0
- package/skills/grill-me/SKILL.md +27 -0
- package/skills/grill-me/agents/openai.yaml +4 -0
- package/skills/ideate/SKILL.md +180 -0
- package/skills/ideate/references/family-packs.md +5 -0
- package/skills/initiative-tracker/SKILL.md +23 -0
- package/skills/initiative-tracker/agents/openai.yaml +4 -0
- package/skills/interop-review/SKILL.md +25 -0
- package/skills/interop-review/agents/openai.yaml +4 -0
- package/skills/interview-me/SKILL.md +404 -0
- package/skills/interview-me/agents/openai.yaml +4 -0
- package/skills/loop-library-audit/SKILL.md +123 -0
- package/skills/pitch/SKILL.md +110 -0
- package/skills/pitch/references/opportunity-assessment.md +136 -0
- package/skills/pitch/references/prfaq-template.md +54 -0
- package/skills/pmm-release-briefing/SKILL.md +27 -0
- package/skills/pmm-release-briefing/agents/openai.yaml +4 -0
- package/skills/pmm-release-briefing/references/variant-generalized.md +16 -0
- package/skills/positioning-messaging/SKILL.md +70 -0
- package/skills/positioning-messaging/agents/openai.yaml +4 -0
- package/skills/positioning-messaging/references/guest-insights.md +1400 -0
- package/skills/preview-swarm/SKILL.md +418 -0
- package/skills/preview-swarm/agents/openai.yaml +4 -0
- package/skills/preview-swarm/references/html-effectiveness-build-previews.md +168 -0
- package/skills/product-copywriting/SKILL.md +127 -0
- package/skills/product-copywriting/agents/openai.yaml +4 -0
- package/skills/product-copywriting/references/foo-voice.md +26 -0
- package/skills/product-shape-interview/SKILL.md +219 -0
- package/skills/product-shape-interview/agents/openai.yaml +4 -0
- package/skills/ready-for-release/SKILL.md +39 -0
- package/skills/ready-for-release/agents/openai.yaml +4 -0
- package/skills/redact-transcripts/SKILL.md +23 -0
- package/skills/redact-transcripts/agents/openai.yaml +4 -0
- package/skills/research-brief/SKILL.md +48 -0
- package/skills/research-brief/agents/openai.yaml +4 -0
- package/skills/research-refresh/SKILL.md +63 -0
- package/skills/research-refresh/references/worked-cases.md +44 -0
- package/skills/review-loop/SKILL.md +200 -0
- package/skills/review-spec/SKILL.md +32 -0
- package/skills/review-spec/agents/openai.yaml +4 -0
- package/skills/rfc-from-research/SKILL.md +42 -0
- package/skills/rfc-from-research/agents/openai.yaml +4 -0
- package/skills/rfc-from-research/references/rfc-pattern.md +69 -0
- package/skills/skill-registry.yaml +810 -0
- package/skills/spec-cleanup/SKILL.md +103 -0
- package/skills/spec-cleanup/agents/openai.yaml +4 -0
- package/skills/spec-swarm/SKILL.md +87 -0
- package/skills/spec-swarm/agents/openai.yaml +4 -0
- package/skills/spec-swarm/template/SKILL.template.md +94 -0
- package/skills/spec-swarm/template/personas/accessibility-inclusion.md +40 -0
- package/skills/spec-swarm/template/personas/ai-technical-pm.md +69 -0
- package/skills/spec-swarm/template/personas/analytics-engineering.md +39 -0
- package/skills/spec-swarm/template/personas/applied-ai-eval.md +64 -0
- package/skills/spec-swarm/template/personas/business-gm-pm.md +67 -0
- package/skills/spec-swarm/template/personas/consumer-craft-pm.md +68 -0
- package/skills/spec-swarm/template/personas/content-design.md +40 -0
- package/skills/spec-swarm/template/personas/cpo.md +42 -0
- package/skills/spec-swarm/template/personas/data-science.md +42 -0
- package/skills/spec-swarm/template/personas/developer-experience.md +65 -0
- package/skills/spec-swarm/template/personas/dx-product.md +70 -0
- package/skills/spec-swarm/template/personas/field.md +43 -0
- package/skills/spec-swarm/template/personas/finance-cfo.md +40 -0
- package/skills/spec-swarm/template/personas/growth-pm.md +67 -0
- package/skills/spec-swarm/template/personas/platform-pm.md +67 -0
- package/skills/spec-swarm/template/personas/pmm.md +43 -0
- package/skills/spec-swarm/template/personas/principal-product-designer.md +42 -0
- package/skills/spec-swarm/template/personas/product-ops-program.md +41 -0
- package/skills/spec-swarm/template/personas/qa-release.md +41 -0
- package/skills/spec-swarm/template/personas/responsible-ai-safety.md +64 -0
- package/skills/spec-swarm/template/personas/security-privacy-legal.md +40 -0
- package/skills/spec-swarm/template/personas/strategy-competitive.md +41 -0
- package/skills/spec-swarm/template/personas/technical-architect.md +42 -0
- package/skills/spec-swarm/template/personas/technical-lead.md +43 -0
- package/skills/spec-swarm/template/personas/user-research.md +41 -0
- package/skills/spec-swarm-accessibility-inclusion/SKILL.md +95 -0
- package/skills/spec-swarm-accessibility-inclusion/agents/openai.yaml +4 -0
- package/skills/spec-swarm-ai-technical-pm/SKILL.md +124 -0
- package/skills/spec-swarm-analytics-engineering/SKILL.md +94 -0
- package/skills/spec-swarm-analytics-engineering/agents/openai.yaml +4 -0
- package/skills/spec-swarm-applied-ai-eval/SKILL.md +120 -0
- package/skills/spec-swarm-business-gm-pm/SKILL.md +122 -0
- package/skills/spec-swarm-consumer-craft-pm/SKILL.md +122 -0
- package/skills/spec-swarm-content-design/SKILL.md +95 -0
- package/skills/spec-swarm-content-design/agents/openai.yaml +4 -0
- package/skills/spec-swarm-cpo/SKILL.md +94 -0
- package/skills/spec-swarm-cpo/agents/openai.yaml +4 -0
- package/skills/spec-swarm-data-science/SKILL.md +95 -0
- package/skills/spec-swarm-data-science/agents/openai.yaml +4 -0
- package/skills/spec-swarm-developer-experience/SKILL.md +121 -0
- package/skills/spec-swarm-dx-product/SKILL.md +126 -0
- package/skills/spec-swarm-field/SKILL.md +96 -0
- package/skills/spec-swarm-field/agents/openai.yaml +4 -0
- package/skills/spec-swarm-finance-cfo/SKILL.md +95 -0
- package/skills/spec-swarm-finance-cfo/agents/openai.yaml +4 -0
- package/skills/spec-swarm-growth-pm/SKILL.md +122 -0
- package/skills/spec-swarm-platform-pm/SKILL.md +122 -0
- package/skills/spec-swarm-pmm/SKILL.md +96 -0
- package/skills/spec-swarm-pmm/agents/openai.yaml +4 -0
- package/skills/spec-swarm-principal-product-designer/SKILL.md +95 -0
- package/skills/spec-swarm-principal-product-designer/agents/openai.yaml +4 -0
- package/skills/spec-swarm-product-ops-program/SKILL.md +96 -0
- package/skills/spec-swarm-product-ops-program/agents/openai.yaml +4 -0
- package/skills/spec-swarm-qa-release/SKILL.md +96 -0
- package/skills/spec-swarm-qa-release/agents/openai.yaml +4 -0
- package/skills/spec-swarm-responsible-ai-safety/SKILL.md +120 -0
- package/skills/spec-swarm-security-privacy-legal/SKILL.md +95 -0
- package/skills/spec-swarm-security-privacy-legal/agents/openai.yaml +4 -0
- package/skills/spec-swarm-strategy-competitive/SKILL.md +96 -0
- package/skills/spec-swarm-strategy-competitive/agents/openai.yaml +4 -0
- package/skills/spec-swarm-technical-architect/SKILL.md +95 -0
- package/skills/spec-swarm-technical-architect/agents/openai.yaml +4 -0
- package/skills/spec-swarm-technical-lead/SKILL.md +96 -0
- package/skills/spec-swarm-technical-lead/agents/openai.yaml +4 -0
- package/skills/spec-swarm-user-research/SKILL.md +95 -0
- package/skills/spec-swarm-user-research/agents/openai.yaml +4 -0
- package/skills/strategy-check/SKILL.md +170 -0
- package/skills/strategy-check/agents/openai.yaml +4 -0
- package/skills/study-digest/SKILL.md +41 -0
- package/skills/synthetic-customer/SKILL.md +68 -0
- package/skills/update-cli-spec/SKILL.md +69 -0
- package/skills/update-cli-spec/agents/openai.yaml +4 -0
- package/skills/voc-researcher/SKILL.md +45 -0
- package/skills/voc-researcher/agents/openai.yaml +4 -0
- package/skills-lock.json +35 -0
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
// Gmail through Nango (gmail-discovery-connector-v0, local worker mode).
|
|
2
|
+
//
|
|
3
|
+
// Nango is the credential broker (R-001 reuses the gong shape verbatim): productfoo
|
|
4
|
+
// asks Nango for the verified user's Gmail credential and then talks to the
|
|
5
|
+
// Gmail REST API directly, so message bodies never transit Nango. The
|
|
6
|
+
// connection is per-user (D-002) — a mailbox is personal even at work — so
|
|
7
|
+
// unlike Gong there is no administrator-issued direct-key fallback: a personal
|
|
8
|
+
// OAuth grant through Nango is the only path, and readiness reports that
|
|
9
|
+
// broker truthfully. Credentials are held in process memory for one command,
|
|
10
|
+
// the Nango connection id is never printed (an opaque `connection_ref` stands
|
|
11
|
+
// in for it), and errors carry a typed code plus a bounded message that never
|
|
12
|
+
// embeds a provider response.
|
|
13
|
+
//
|
|
14
|
+
// Gmail API shapes below are beliefs owned by the packet's feasibility task
|
|
15
|
+
// (T-001) and pinned by fixtures in test/discoveryGmailRuntime.test.ts;
|
|
16
|
+
// nothing here is verified-live, and support stays contract_only.
|
|
17
|
+
import { createHash } from "node:crypto";
|
|
18
|
+
import { refuseSourceWrite } from "./sourceIngress.js";
|
|
19
|
+
export class GmailRuntimeError extends Error {
|
|
20
|
+
code;
|
|
21
|
+
constructor(code, message) {
|
|
22
|
+
super(message);
|
|
23
|
+
this.code = code;
|
|
24
|
+
this.name = "GmailRuntimeError";
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
// Versioned policy (R-007): hard ceilings the worker enforces and reports, so
|
|
28
|
+
// a later change is explicit rather than silent. quiet_period_hours is D-001's
|
|
29
|
+
// pinned quiet period (T-005): a thread is settled evidence once its last
|
|
30
|
+
// message predates the scope window's end by this much, unless an explicit
|
|
31
|
+
// done label (GMAIL_DONE_LABELS) marks it sooner.
|
|
32
|
+
export const GMAIL_POLICY = {
|
|
33
|
+
policy_version: "gmail-local-worker-0.1",
|
|
34
|
+
max_response_bytes: 8 * 1024 * 1024,
|
|
35
|
+
fetch_timeout_ms: 90_000,
|
|
36
|
+
max_model_input_tokens: 100_000,
|
|
37
|
+
max_list_pages: 5,
|
|
38
|
+
min_request_interval_ms: 350,
|
|
39
|
+
quiet_period_hours: 48,
|
|
40
|
+
};
|
|
41
|
+
// Least-privilege read-only Gmail scopes (R-003). Listing bounded metadata
|
|
42
|
+
// needs gmail.metadata; reading a selected thread's message bodies needs
|
|
43
|
+
// gmail.readonly — never the full mail scope. A connection without the body
|
|
44
|
+
// scope stays useful for metadata and reports needs_provider_access for reads
|
|
45
|
+
// (R-008).
|
|
46
|
+
const GMAIL_BODY_SCOPE = "gmail.readonly";
|
|
47
|
+
const DEFAULT_NANGO_HOST = "https://api.nango.dev";
|
|
48
|
+
const DEFAULT_GMAIL_BASE = "https://gmail.googleapis.com";
|
|
49
|
+
function nangoConfigured(env) {
|
|
50
|
+
return Boolean(env.NANGO_SECRET_KEY?.trim() && env.FOO_GMAIL_NANGO_CONNECTION?.trim());
|
|
51
|
+
}
|
|
52
|
+
export function gmailConnectionConfigured(env) {
|
|
53
|
+
return nangoConfigured(env);
|
|
54
|
+
}
|
|
55
|
+
export function gmailCredentialBroker(_env) {
|
|
56
|
+
// Per-user ownership (D-002): Nango is the only broker. The parameter keeps
|
|
57
|
+
// the gong broker signature so callers stay parallel.
|
|
58
|
+
return "nango";
|
|
59
|
+
}
|
|
60
|
+
function opaqueConnectionRef(integration, connectionId) {
|
|
61
|
+
return `gmail:${createHash("sha256").update(`${integration}\n${connectionId}`).digest("hex").slice(0, 16)}`;
|
|
62
|
+
}
|
|
63
|
+
async function fetchWithTimeout(fetchImpl, url, init, timeoutMs) {
|
|
64
|
+
const controller = new AbortController();
|
|
65
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
66
|
+
try {
|
|
67
|
+
return await fetchImpl(url, { ...init, signal: controller.signal });
|
|
68
|
+
}
|
|
69
|
+
catch (error) {
|
|
70
|
+
if (controller.signal.aborted)
|
|
71
|
+
throw new GmailRuntimeError("provider_timeout", "the provider did not answer within the fetch ceiling");
|
|
72
|
+
throw new GmailRuntimeError("network", `network failure reaching ${new URL(url).hostname}`);
|
|
73
|
+
}
|
|
74
|
+
finally {
|
|
75
|
+
clearTimeout(timer);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
// Reads a response body while counting toward the byte ceiling, so an
|
|
79
|
+
// oversized thread is dropped before it is ever materialized in full.
|
|
80
|
+
async function readBounded(response, maxBytes) {
|
|
81
|
+
const declared = Number(response.headers.get("content-length") ?? "0");
|
|
82
|
+
if (Number.isFinite(declared) && declared > maxBytes)
|
|
83
|
+
throw new GmailRuntimeError("thread_too_large", "the provider response exceeds the byte ceiling");
|
|
84
|
+
if (!response.body) {
|
|
85
|
+
const buffer = await response.arrayBuffer();
|
|
86
|
+
if (buffer.byteLength > maxBytes)
|
|
87
|
+
throw new GmailRuntimeError("thread_too_large", "the provider response exceeds the byte ceiling");
|
|
88
|
+
return new TextDecoder().decode(buffer);
|
|
89
|
+
}
|
|
90
|
+
const reader = response.body.getReader();
|
|
91
|
+
const chunks = [];
|
|
92
|
+
let total = 0;
|
|
93
|
+
for (;;) {
|
|
94
|
+
const { done, value } = await reader.read();
|
|
95
|
+
if (done)
|
|
96
|
+
break;
|
|
97
|
+
total += value.byteLength;
|
|
98
|
+
if (total > maxBytes) {
|
|
99
|
+
await reader.cancel().catch(() => undefined);
|
|
100
|
+
chunks.length = 0;
|
|
101
|
+
throw new GmailRuntimeError("thread_too_large", "the provider response exceeds the byte ceiling");
|
|
102
|
+
}
|
|
103
|
+
chunks.push(value);
|
|
104
|
+
}
|
|
105
|
+
return new TextDecoder().decode(Buffer.concat(chunks));
|
|
106
|
+
}
|
|
107
|
+
function parseJson(text, label) {
|
|
108
|
+
try {
|
|
109
|
+
const parsed = JSON.parse(text);
|
|
110
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
|
|
111
|
+
throw new Error("not an object");
|
|
112
|
+
return parsed;
|
|
113
|
+
}
|
|
114
|
+
catch {
|
|
115
|
+
throw new GmailRuntimeError("provider_failure", `${label} returned a response productfoo could not read`);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Resolve the verified user's Gmail credential through Nango. There is no
|
|
120
|
+
* direct fallback (D-002): the mailbox belongs to one person, so the OAuth
|
|
121
|
+
* grant is always brokered, refreshable, and revocable centrally. productfoo receives
|
|
122
|
+
* the token for one command and never persists or prints it.
|
|
123
|
+
*/
|
|
124
|
+
export async function resolveGmailConnection(input) {
|
|
125
|
+
if (!nangoConfigured(input.env)) {
|
|
126
|
+
throw new GmailRuntimeError("not_connected", "Gmail is not connected: connect the 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)");
|
|
127
|
+
}
|
|
128
|
+
const secret = input.env.NANGO_SECRET_KEY.trim();
|
|
129
|
+
const connectionId = input.env.FOO_GMAIL_NANGO_CONNECTION.trim();
|
|
130
|
+
const integration = input.env.FOO_GMAIL_NANGO_INTEGRATION?.trim() || "google-mail";
|
|
131
|
+
const host = (input.env.NANGO_HOST?.trim() || DEFAULT_NANGO_HOST).replace(/\/+$/, "");
|
|
132
|
+
const url = `${host}/connection/${encodeURIComponent(connectionId)}?provider_config_key=${encodeURIComponent(integration)}`;
|
|
133
|
+
const response = await fetchWithTimeout(input.fetchImpl, url, { method: "GET", headers: { authorization: `Bearer ${secret}`, accept: "application/json" } }, GMAIL_POLICY.fetch_timeout_ms);
|
|
134
|
+
if (response.status === 401 || response.status === 403)
|
|
135
|
+
throw new GmailRuntimeError("not_connected", "Nango refused the secret key; reconnect Gmail");
|
|
136
|
+
if (response.status === 404)
|
|
137
|
+
throw new GmailRuntimeError("not_connected", "Nango has no Gmail connection with that reference; connect Gmail first");
|
|
138
|
+
if (!response.ok)
|
|
139
|
+
throw new GmailRuntimeError("provider_failure", `Nango answered ${response.status} while resolving the connection`);
|
|
140
|
+
const body = parseJson(await readBounded(response, 256 * 1024), "Nango");
|
|
141
|
+
const credentials = (body.credentials ?? {});
|
|
142
|
+
const type = String(credentials.type ?? "").toUpperCase();
|
|
143
|
+
if (type !== "OAUTH2")
|
|
144
|
+
throw new GmailRuntimeError("not_connected", "the Nango connection uses a credential type productfoo does not support for Gmail");
|
|
145
|
+
const token = String(credentials.access_token ?? "");
|
|
146
|
+
if (!token)
|
|
147
|
+
throw new GmailRuntimeError("auth_expired", "the Nango connection carries no usable Gmail access token; refresh or reconnect");
|
|
148
|
+
const raw = (credentials.raw ?? {});
|
|
149
|
+
const scopeValue = typeof raw.scope === "string" ? raw.scope : typeof credentials.scope === "string" ? credentials.scope : "";
|
|
150
|
+
const scopes = scopeValue.split(/[\s,]+/).filter(Boolean);
|
|
151
|
+
const bodyScope = scopes.length === 0 ? "unknown" : scopes.some((scope) => scope.includes(GMAIL_BODY_SCOPE)) ? "granted" : "missing";
|
|
152
|
+
return { connection_ref: opaqueConnectionRef(integration, connectionId), broker: "nango", credential: { type: "oauth2", access_token: token }, credential_type: "oauth2", body_scope: bodyScope };
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Minimal read-only Gmail REST client. Only documented read operations are
|
|
156
|
+
* reachable — every request method is GET and anything else routes through
|
|
157
|
+
* the single write-refusal choke point (R-009), so the refusal is testable
|
|
158
|
+
* rather than scattered. There is no attachment fetch method at all (D-003).
|
|
159
|
+
*/
|
|
160
|
+
export class GmailClient {
|
|
161
|
+
credential;
|
|
162
|
+
options;
|
|
163
|
+
requests = { profile: 0, list: 0, metadata: 0, thread: 0 };
|
|
164
|
+
lastRequestAt = 0;
|
|
165
|
+
baseUrl;
|
|
166
|
+
fetchTimeoutMs;
|
|
167
|
+
sleep;
|
|
168
|
+
now;
|
|
169
|
+
constructor(credential, options) {
|
|
170
|
+
this.credential = credential;
|
|
171
|
+
this.options = options;
|
|
172
|
+
this.baseUrl = (options.baseUrl ?? DEFAULT_GMAIL_BASE).replace(/\/+$/, "");
|
|
173
|
+
this.fetchTimeoutMs = options.fetchTimeoutMs ?? GMAIL_POLICY.fetch_timeout_ms;
|
|
174
|
+
this.sleep = options.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
|
|
175
|
+
this.now = options.now ?? (() => Date.now());
|
|
176
|
+
}
|
|
177
|
+
async request(method, pathname, maxBytes = GMAIL_POLICY.max_response_bytes) {
|
|
178
|
+
// R-009: the client is GET-shaped by construction; a future caller that
|
|
179
|
+
// tries a write lands here, at the one choke point — the same gate the
|
|
180
|
+
// Gong client applies to its non-read methods.
|
|
181
|
+
if (method !== "GET")
|
|
182
|
+
refuseSourceWrite(`gmail ${method} ${pathname}`);
|
|
183
|
+
const wait = this.lastRequestAt + GMAIL_POLICY.min_request_interval_ms - this.now();
|
|
184
|
+
if (wait > 0)
|
|
185
|
+
await this.sleep(wait);
|
|
186
|
+
this.lastRequestAt = this.now();
|
|
187
|
+
const response = await fetchWithTimeout(this.options.fetchImpl, `${this.baseUrl}${pathname}`, { method: "GET", headers: { authorization: `Bearer ${this.credential.access_token}`, accept: "application/json" } }, this.fetchTimeoutMs);
|
|
188
|
+
if (response.status === 401)
|
|
189
|
+
throw new GmailRuntimeError("auth_expired", "Gmail rejected the credential; refresh or reconnect");
|
|
190
|
+
if (response.status === 403)
|
|
191
|
+
throw new GmailRuntimeError("access_denied", "Gmail denied this read; the connection lacks the required scope");
|
|
192
|
+
if (response.status === 429)
|
|
193
|
+
throw new GmailRuntimeError("budget_exhausted", "Gmail rate limit reached; no retry was attempted");
|
|
194
|
+
if (response.status === 404)
|
|
195
|
+
throw new GmailRuntimeError("provider_failure", "Gmail reported the requested record does not exist");
|
|
196
|
+
if (!response.ok)
|
|
197
|
+
throw new GmailRuntimeError("provider_failure", `Gmail answered ${response.status}`);
|
|
198
|
+
return parseJson(await readBounded(response, maxBytes), "Gmail");
|
|
199
|
+
}
|
|
200
|
+
/** The mailbox owner's address (metadata only) — defines "internal" for a per-user mailbox (R-004). */
|
|
201
|
+
async profile() {
|
|
202
|
+
this.requests.profile += 1;
|
|
203
|
+
const body = await this.request("GET", "/gmail/v1/users/me/profile", 16 * 1024);
|
|
204
|
+
const email = typeof body.emailAddress === "string" ? body.emailAddress : undefined;
|
|
205
|
+
return email ? { email } : {};
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Bounded metadata listing (R-004): explicit labels, a time window expressed
|
|
209
|
+
* as Gmail's after/before query operators, a page ceiling, no message
|
|
210
|
+
* bodies. A listing that exceeds the ceiling returns `truncated` and the
|
|
211
|
+
* run stops — it never widens.
|
|
212
|
+
*/
|
|
213
|
+
async listThreads(scope) {
|
|
214
|
+
const afterSeconds = Math.floor(Date.parse(scope.from) / 1000);
|
|
215
|
+
const beforeSeconds = Math.floor(Date.parse(scope.to) / 1000);
|
|
216
|
+
if (!Number.isFinite(afterSeconds) || !Number.isFinite(beforeSeconds))
|
|
217
|
+
throw new GmailRuntimeError("provider_failure", "the approved window is not a valid time range");
|
|
218
|
+
const threads = [];
|
|
219
|
+
let pageToken;
|
|
220
|
+
let pages = 0;
|
|
221
|
+
for (;;) {
|
|
222
|
+
if (pages >= GMAIL_POLICY.max_list_pages)
|
|
223
|
+
return { threads, pages, truncated: true };
|
|
224
|
+
const params = new URLSearchParams({ maxPageSize: "100", q: `after:${afterSeconds} before:${beforeSeconds}` });
|
|
225
|
+
// labelIds is a repeated parameter, not a joined one.
|
|
226
|
+
for (const label of scope.label_ids.slice(0, 10))
|
|
227
|
+
params.append("labelIds", label);
|
|
228
|
+
if (pageToken)
|
|
229
|
+
params.set("pageToken", pageToken);
|
|
230
|
+
this.requests.list += 1;
|
|
231
|
+
const body = await this.request("GET", `/gmail/v1/users/me/threads?${params.toString()}`, 4 * 1024 * 1024);
|
|
232
|
+
pages += 1;
|
|
233
|
+
for (const entry of Array.isArray(body.threads) ? body.threads : []) {
|
|
234
|
+
if (!entry || typeof entry.id !== "string")
|
|
235
|
+
continue;
|
|
236
|
+
threads.push({ id: entry.id, snippet: typeof entry.snippet === "string" ? entry.snippet : undefined });
|
|
237
|
+
}
|
|
238
|
+
pageToken = typeof body.nextPageToken === "string" && body.nextPageToken ? body.nextPageToken : undefined;
|
|
239
|
+
if (!pageToken)
|
|
240
|
+
return { threads, pages, truncated: false };
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* One thread's headers only (format=metadata) — the bounded listing read
|
|
245
|
+
* (R-004): participants and dates without any message body. Bodies are
|
|
246
|
+
* fetched solely by `thread()` under the minted proof (R-007).
|
|
247
|
+
*/
|
|
248
|
+
async threadMetadata(threadId) {
|
|
249
|
+
if (!threadId || /\s/.test(threadId))
|
|
250
|
+
throw new GmailRuntimeError("provider_failure", "a single well-formed thread identifier is required");
|
|
251
|
+
this.requests.metadata += 1;
|
|
252
|
+
const body = await this.request("GET", `/gmail/v1/users/me/threads/${encodeURIComponent(threadId)}?format=metadata`);
|
|
253
|
+
const messagesRaw = Array.isArray(body.messages) ? body.messages : [];
|
|
254
|
+
const messages = messagesRaw.map((message) => this.parseMessage(message));
|
|
255
|
+
if (messages.length === 0)
|
|
256
|
+
throw new GmailRuntimeError("access_denied", "Gmail returned no messages for the selected thread");
|
|
257
|
+
return { id: threadId, messages };
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Exactly one thread's messages (R-007). This method refuses to ask for
|
|
261
|
+
* more than one identifier — a fixture observes that no request ever
|
|
262
|
+
* carries one. Attachment parts are skipped, never fetched (D-003).
|
|
263
|
+
*/
|
|
264
|
+
async thread(threadId) {
|
|
265
|
+
if (!threadId || /\s/.test(threadId))
|
|
266
|
+
throw new GmailRuntimeError("provider_failure", "a single well-formed thread identifier is required");
|
|
267
|
+
this.requests.thread += 1;
|
|
268
|
+
const body = await this.request("GET", `/gmail/v1/users/me/threads/${encodeURIComponent(threadId)}?format=full`);
|
|
269
|
+
if (typeof body.id === "string" && body.id !== threadId)
|
|
270
|
+
throw new GmailRuntimeError("provider_failure", "Gmail returned a different thread than the one selected");
|
|
271
|
+
const messagesRaw = Array.isArray(body.messages) ? body.messages : [];
|
|
272
|
+
const messages = messagesRaw.map((message) => this.parseMessage(message));
|
|
273
|
+
if (messages.length === 0)
|
|
274
|
+
throw new GmailRuntimeError("access_denied", "Gmail returned no messages for the selected thread");
|
|
275
|
+
return { id: threadId, messages };
|
|
276
|
+
}
|
|
277
|
+
parseMessage(message) {
|
|
278
|
+
const payload = (message.payload ?? {});
|
|
279
|
+
const headers = Array.isArray(payload.headers) ? payload.headers : [];
|
|
280
|
+
const header = (name) => headers.find((entry) => entry.name?.toLowerCase() === name.toLowerCase())?.value;
|
|
281
|
+
const from = parseEmailAddress(header("From"));
|
|
282
|
+
const to = (header("To") ?? "").split(",").map(parseEmailAddress).filter((entry) => Boolean(entry));
|
|
283
|
+
return {
|
|
284
|
+
id: typeof message.id === "string" ? message.id : "",
|
|
285
|
+
internal_date: typeof message.internalDate === "string" ? message.internalDate : undefined,
|
|
286
|
+
labels: Array.isArray(message.labelIds) ? message.labelIds.filter((label) => typeof label === "string") : undefined,
|
|
287
|
+
...(from ? { from } : {}),
|
|
288
|
+
to: to.length ? to : undefined,
|
|
289
|
+
subject: header("Subject"),
|
|
290
|
+
body_text: decodeTextBody(payload),
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
// Parses "Display Name <local@domain>" or "local@domain" into the adapter's
|
|
295
|
+
// participant shape. Unparseable values are dropped — never guessed.
|
|
296
|
+
function parseEmailAddress(value) {
|
|
297
|
+
const trimmed = value?.trim();
|
|
298
|
+
if (!trimmed)
|
|
299
|
+
return undefined;
|
|
300
|
+
const angled = /^(.*?)<([^<>\s]+@[^<>\s]+)>\s*$/.exec(trimmed);
|
|
301
|
+
if (angled)
|
|
302
|
+
return { ...(angled[1].trim() ? { display_name: angled[1].trim().replace(/^"|"$/g, "") } : {}), email: angled[2].toLowerCase() };
|
|
303
|
+
return /^[^\s<>]+@[^\s<>]+$/.test(trimmed) ? { email: trimmed.toLowerCase() } : undefined;
|
|
304
|
+
}
|
|
305
|
+
// Decodes text/plain parts of the already-fetched thread response. Attachment
|
|
306
|
+
// parts (anything with an attachmentId) are never decoded and never fetched —
|
|
307
|
+
// D-003 refuses them structurally.
|
|
308
|
+
function decodeTextBody(payload) {
|
|
309
|
+
const parts = Array.isArray(payload.parts) ? payload.parts : [];
|
|
310
|
+
const candidates = [payload, ...parts];
|
|
311
|
+
for (const part of candidates) {
|
|
312
|
+
if (typeof part.attachmentId === "string" && part.attachmentId)
|
|
313
|
+
continue;
|
|
314
|
+
const mimeType = typeof part.mimeType === "string" ? part.mimeType : "";
|
|
315
|
+
const body = (part.body ?? {});
|
|
316
|
+
const data = typeof body.data === "string" ? body.data : undefined;
|
|
317
|
+
if (!data)
|
|
318
|
+
continue;
|
|
319
|
+
if (mimeType && mimeType !== "text/plain")
|
|
320
|
+
continue;
|
|
321
|
+
try {
|
|
322
|
+
return Buffer.from(data, "base64url").toString("utf8");
|
|
323
|
+
}
|
|
324
|
+
catch {
|
|
325
|
+
continue;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
return undefined;
|
|
329
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
export type FetchLike = typeof globalThis.fetch;
|
|
2
|
+
export type GongRuntimeErrorCode = "not_connected" | "auth_expired" | "access_denied" | "provider_failure" | "network" | "provider_timeout" | "transcript_too_large" | "budget_exhausted";
|
|
3
|
+
export declare class GongRuntimeError extends Error {
|
|
4
|
+
readonly code: GongRuntimeErrorCode;
|
|
5
|
+
constructor(code: GongRuntimeErrorCode, message: string);
|
|
6
|
+
}
|
|
7
|
+
export type GongCredential = {
|
|
8
|
+
type: "basic";
|
|
9
|
+
username: string;
|
|
10
|
+
password: string;
|
|
11
|
+
} | {
|
|
12
|
+
type: "oauth2";
|
|
13
|
+
access_token: string;
|
|
14
|
+
};
|
|
15
|
+
export type GongConnection = {
|
|
16
|
+
connection_ref: string;
|
|
17
|
+
broker: "nango" | "direct";
|
|
18
|
+
credential: GongCredential;
|
|
19
|
+
credential_type: "basic" | "oauth2";
|
|
20
|
+
transcript_scope: "granted" | "missing" | "unknown";
|
|
21
|
+
};
|
|
22
|
+
export declare const GONG_POLICY: {
|
|
23
|
+
readonly policy_version: "gong-local-worker-0.1";
|
|
24
|
+
readonly max_response_bytes: number;
|
|
25
|
+
readonly fetch_timeout_ms: 90000;
|
|
26
|
+
readonly max_model_input_tokens: 100000;
|
|
27
|
+
readonly max_list_pages: 5;
|
|
28
|
+
readonly min_request_interval_ms: 350;
|
|
29
|
+
};
|
|
30
|
+
export type GongConnectionEnv = Record<string, string | undefined>;
|
|
31
|
+
export declare function gongConnectionConfigured(env: GongConnectionEnv): boolean;
|
|
32
|
+
export declare function gongCredentialBroker(env: GongConnectionEnv): "nango" | "direct" | null;
|
|
33
|
+
/**
|
|
34
|
+
* Resolve the company's Gong credential. Nango is the primary broker: it holds
|
|
35
|
+
* the credential, refreshes it, and is where an administrator revokes it. When
|
|
36
|
+
* Nango is not configured, an administrator-issued access key and secret set
|
|
37
|
+
* directly in this runtime's environment (FOO_GONG_ACCESS_KEY /
|
|
38
|
+
* FOO_GONG_ACCESS_KEY_SECRET) is accepted as the direct fallback — the same
|
|
39
|
+
* company credential, minus the central refresh and revoke story. Either way
|
|
40
|
+
* productfoo receives it for one command and never persists or prints it.
|
|
41
|
+
*/
|
|
42
|
+
export declare function resolveGongConnection(input: {
|
|
43
|
+
env: GongConnectionEnv;
|
|
44
|
+
fetchImpl: FetchLike;
|
|
45
|
+
}): Promise<GongConnection>;
|
|
46
|
+
export type GongListedCall = {
|
|
47
|
+
id: string;
|
|
48
|
+
title?: string;
|
|
49
|
+
url?: string;
|
|
50
|
+
scheduled?: string;
|
|
51
|
+
started?: string;
|
|
52
|
+
duration?: number;
|
|
53
|
+
workspaceId?: string;
|
|
54
|
+
};
|
|
55
|
+
export type GongParty = {
|
|
56
|
+
name?: string;
|
|
57
|
+
emailAddress?: string;
|
|
58
|
+
title?: string;
|
|
59
|
+
affiliation?: string;
|
|
60
|
+
speakerId?: string;
|
|
61
|
+
};
|
|
62
|
+
export type GongTranscriptMonologue = {
|
|
63
|
+
speakerId?: string;
|
|
64
|
+
topic?: string;
|
|
65
|
+
sentences: Array<{
|
|
66
|
+
start?: number;
|
|
67
|
+
end?: number;
|
|
68
|
+
text: string;
|
|
69
|
+
}>;
|
|
70
|
+
};
|
|
71
|
+
type GongClientOptions = {
|
|
72
|
+
fetchImpl: FetchLike;
|
|
73
|
+
baseUrl?: string;
|
|
74
|
+
sleep?: (ms: number) => Promise<void>;
|
|
75
|
+
now?: () => number;
|
|
76
|
+
fetchTimeoutMs?: number;
|
|
77
|
+
};
|
|
78
|
+
/**
|
|
79
|
+
* Minimal read-only Gong REST client. Only documented read operations are
|
|
80
|
+
* reachable; any other method or path routes through the single write-refusal
|
|
81
|
+
* choke point (R-009) so the refusal is testable rather than scattered.
|
|
82
|
+
*/
|
|
83
|
+
export declare class GongClient {
|
|
84
|
+
private readonly credential;
|
|
85
|
+
private readonly options;
|
|
86
|
+
readonly requests: {
|
|
87
|
+
list: number;
|
|
88
|
+
extensive: number;
|
|
89
|
+
transcript: number;
|
|
90
|
+
};
|
|
91
|
+
private lastRequestAt;
|
|
92
|
+
private readonly baseUrl;
|
|
93
|
+
private readonly sleep;
|
|
94
|
+
private readonly now;
|
|
95
|
+
private readonly fetchTimeoutMs;
|
|
96
|
+
constructor(credential: GongCredential, options: GongClientOptions);
|
|
97
|
+
private authorization;
|
|
98
|
+
private request;
|
|
99
|
+
/** Bounded metadata listing: one time window, a page ceiling, no content. */
|
|
100
|
+
listCalls(window: {
|
|
101
|
+
from: string;
|
|
102
|
+
to: string;
|
|
103
|
+
workspaceId?: string;
|
|
104
|
+
}): Promise<{
|
|
105
|
+
calls: GongListedCall[];
|
|
106
|
+
pages: number;
|
|
107
|
+
truncated: boolean;
|
|
108
|
+
}>;
|
|
109
|
+
/** Party metadata for the given calls (names, titles, affiliation, speaker ids) — no content fields are requested. */
|
|
110
|
+
parties(callIds: string[]): Promise<Map<string, GongParty[]>>;
|
|
111
|
+
/**
|
|
112
|
+
* Exactly one call's transcript (R-007). Gong's transcript operation is
|
|
113
|
+
* batch-capable; this method refuses to ask for more than one identifier and
|
|
114
|
+
* a fixture observes that no request ever carries more.
|
|
115
|
+
*/
|
|
116
|
+
transcript(callId: string): Promise<GongTranscriptMonologue[]>;
|
|
117
|
+
}
|
|
118
|
+
export {};
|