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,491 @@
|
|
|
1
|
+
// `foo discovery gong …` — the local worker for a company Gong connection
|
|
2
|
+
// (gong-discovery-connector-v0, D-010 local worker mode).
|
|
3
|
+
//
|
|
4
|
+
// This is the runtime adapter the discovery core was designed to be driven by:
|
|
5
|
+
// it lists bounded call metadata inside an approved study or inbox scope,
|
|
6
|
+
// normalizes it through the Gong adapter, hands the closed session shape to
|
|
7
|
+
// `foo discovery match`, opens exactly one proof-authorized transcript into
|
|
8
|
+
// process memory, synthesizes the sanitized envelope in one model request,
|
|
9
|
+
// and hands the envelope to `foo discovery apply`. The transcript never
|
|
10
|
+
// reaches argv, stdin, a file, Git, a log, an error, or the host agent's
|
|
11
|
+
// context; every surface this module writes is content-free.
|
|
12
|
+
//
|
|
13
|
+
// What it is not: the hosted, cross-host service the Nango discovery packet
|
|
14
|
+
// specifies. Ownership isolation between workspace members, signed service
|
|
15
|
+
// receipts, and remote hosts wait for that service. This worker runs where the
|
|
16
|
+
// repository working tree is, with the company credential brokered by Nango.
|
|
17
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
18
|
+
import path from "node:path";
|
|
19
|
+
import { hasFlag } from "./cliArgs.js";
|
|
20
|
+
import { loadDiscoveryRun } from "./discoveryArtifacts.js";
|
|
21
|
+
import { discoveryTargetDir, gongScopeValidationError, inboxScope, isInboxTarget, localRoot, localTargetName, runDiscoveryCommand, } from "./discoveryCommand.js";
|
|
22
|
+
import { openAuthorizedTranscript } from "./discoveryProof.js";
|
|
23
|
+
import { gongDiscoveryAdapter } from "./discoveryProviders.js";
|
|
24
|
+
import { planDiscoverySync } from "./discoverySync.js";
|
|
25
|
+
import { anthropicSynthesisTransport, defaultSynthesisModel, synthesisCredentialPresent, synthesizeDiscoveryEnvelope } from "./discoverySynthesis.js";
|
|
26
|
+
import { GONG_POLICY, GongClient, GongRuntimeError, gongConnectionConfigured, gongCredentialBroker, resolveGongConnection } from "./gongNango.js";
|
|
27
|
+
const MATCHABLE_STUDY_STATES = new Set(["awaiting_match", "ambiguous_match", "needs_source_approval", "needs_provider_access", "needs_provider_connector", "needs_research_proof"]);
|
|
28
|
+
const DEFAULT_LOOKBACK_DAYS = 14;
|
|
29
|
+
const SUPPORT_STATE = "contract_only";
|
|
30
|
+
const MODE = "local_worker";
|
|
31
|
+
function result(status, options = {}) {
|
|
32
|
+
return { schema_version: "0.1", status, writes: [], errors: [], handoff: null, next_actions: [], coverage: {}, ...options };
|
|
33
|
+
}
|
|
34
|
+
function exitCodeFor(status) {
|
|
35
|
+
return ["ambiguous_match", "needs_source_approval", "needs_provider_access", "needs_provider_connector", "needs_research_proof", "invalid", "budget_exhausted"].includes(status) ? 1 : 0;
|
|
36
|
+
}
|
|
37
|
+
function outcome(value, json) {
|
|
38
|
+
const exitCode = exitCodeFor(value.status);
|
|
39
|
+
if (json)
|
|
40
|
+
return { output: `${JSON.stringify(value, null, 2)}\n`, exitCode, result: value };
|
|
41
|
+
const lines = [`Discovery (gong): ${value.status}.`];
|
|
42
|
+
for (const error of value.errors)
|
|
43
|
+
lines.push(`- ${error.message}`);
|
|
44
|
+
if (value.handoff)
|
|
45
|
+
lines.push(`Handoff: ${value.handoff}`);
|
|
46
|
+
for (const action of value.next_actions)
|
|
47
|
+
lines.push(`Next: ${action}`);
|
|
48
|
+
return { output: `${lines.join("\n")}\n`, exitCode, result: value };
|
|
49
|
+
}
|
|
50
|
+
function invalid(message, json, code = "E_DISCOVERY_VALIDATION", targetId, coverage = {}) {
|
|
51
|
+
return outcome(result("invalid", { study_id: targetId, errors: [{ code, message }], coverage }), json);
|
|
52
|
+
}
|
|
53
|
+
// Typed provider failures map to the discovery command's closed status set.
|
|
54
|
+
// Messages are productfoo's own words: no provider body, id, or credential.
|
|
55
|
+
function fromRuntimeError(error, json, targetId, coverage) {
|
|
56
|
+
const base = { study_id: targetId, coverage: { ...coverage, provider_error: error.code, mode: MODE } };
|
|
57
|
+
switch (error.code) {
|
|
58
|
+
case "not_connected":
|
|
59
|
+
return outcome(result("needs_provider_connector", { ...base, errors: [{ code: "E_DISCOVERY_PROVIDER_CONNECTOR", message: error.message }], handoff: "Connect the company Gong account through Nango and set NANGO_SECRET_KEY and FOO_GONG_NANGO_CONNECTION (plus NANGO_HOST / FOO_GONG_NANGO_INTEGRATION when they differ from the defaults); or, for this runtime only, set FOO_GONG_ACCESS_KEY and FOO_GONG_ACCESS_KEY_SECRET from an administrator-issued Gong access key." }), json);
|
|
60
|
+
case "auth_expired":
|
|
61
|
+
case "access_denied":
|
|
62
|
+
return outcome(result("needs_provider_access", { ...base, errors: [{ code: "E_DISCOVERY_PROVIDER_ACCESS", message: error.message }], handoff: "Ask the Gong administrator to refresh the connection or grant the read permission, then rerun." }), json);
|
|
63
|
+
case "budget_exhausted":
|
|
64
|
+
// A rate-limit stop is the quota half of R-004: the run ends, nothing
|
|
65
|
+
// widens, nothing retries. The next run needs a narrower window.
|
|
66
|
+
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);
|
|
67
|
+
default:
|
|
68
|
+
return outcome(result("invalid", { ...base, errors: [{ code: "E_DISCOVERY_VALIDATION", message: error.message }], handoff: error.code === "transcript_too_large" || error.code === "provider_timeout" ? "Nothing was written and no retry was attempted; a new attempt needs a fresh match." : null }), json);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
function isoDaysAgo(now, days) {
|
|
72
|
+
return new Date(Date.parse(now) - days * 86_400_000).toISOString();
|
|
73
|
+
}
|
|
74
|
+
function loadTarget(root, targetId, env, now, phase = "list") {
|
|
75
|
+
if (isInboxTarget(targetId)) {
|
|
76
|
+
const scope = inboxScope(root, targetId);
|
|
77
|
+
if (scope.provider.toLowerCase() !== "gong")
|
|
78
|
+
throw new Error("this inbox scope is not a Gong scope");
|
|
79
|
+
if (scope.state !== "approved")
|
|
80
|
+
throw new Error("this inbox scope is not approved");
|
|
81
|
+
// R-004: validation is pre-flight — an invalid scope never reaches the
|
|
82
|
+
// provider. The workspace env selector (set below) is narrowing only and
|
|
83
|
+
// never counts toward the selector minimum.
|
|
84
|
+
const scopeError = gongScopeValidationError({
|
|
85
|
+
window: scope.time_window,
|
|
86
|
+
named_external_participants: scope.participant_allowlist,
|
|
87
|
+
external_companies_or_domains: scope.domain_allowlist,
|
|
88
|
+
title_tokens: scope.title_tokens,
|
|
89
|
+
call_owners: scope.call_owners,
|
|
90
|
+
});
|
|
91
|
+
if (scopeError)
|
|
92
|
+
throw new Error(scopeError);
|
|
93
|
+
const from = new Date(scope.time_window.start);
|
|
94
|
+
const to = new Date(scope.time_window.end);
|
|
95
|
+
return { target_id: targetId, lane: "unassigned_call_evidence", dir: discoveryTargetDir(root, targetId), connection_alias: scope.connection_alias, window: { from: from.toISOString(), to: to.toISOString() }, workspace: env.FOO_GONG_WORKSPACE?.trim() || undefined };
|
|
96
|
+
}
|
|
97
|
+
const dir = discoveryTargetDir(root, targetId);
|
|
98
|
+
const run = loadDiscoveryRun(dir);
|
|
99
|
+
if (run.source.provider.toLowerCase() !== "gong")
|
|
100
|
+
throw new Error("this study is not a Gong study");
|
|
101
|
+
// Listing needs a matchable study; processing needs the state `match` left
|
|
102
|
+
// behind (ready_to_synthesize) — the proof check decides the rest.
|
|
103
|
+
const allowed = phase === "process" ? run.state === "ready_to_synthesize" || MATCHABLE_STUDY_STATES.has(run.state) : MATCHABLE_STUDY_STATES.has(run.state);
|
|
104
|
+
if (!allowed)
|
|
105
|
+
throw new Error(`study ${targetId} is not ready for provider reads (state ${run.state})`);
|
|
106
|
+
let from;
|
|
107
|
+
let to;
|
|
108
|
+
if (run.match.expected_local_date) {
|
|
109
|
+
const day = Date.parse(`${run.match.expected_local_date}T00:00:00Z`);
|
|
110
|
+
if (!Number.isFinite(day))
|
|
111
|
+
throw new Error("the study's expected date is invalid");
|
|
112
|
+
from = new Date(day - 86_400_000).toISOString();
|
|
113
|
+
to = new Date(day + 2 * 86_400_000).toISOString();
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
const lookback = Number(env.FOO_GONG_LOOKBACK_DAYS ?? DEFAULT_LOOKBACK_DAYS);
|
|
117
|
+
if (!Number.isFinite(lookback) || lookback <= 0 || lookback > 90)
|
|
118
|
+
throw new Error("FOO_GONG_LOOKBACK_DAYS must be between 1 and 90");
|
|
119
|
+
from = isoDaysAgo(now, lookback);
|
|
120
|
+
to = now;
|
|
121
|
+
}
|
|
122
|
+
// R-004 applies to study scopes too: a gong study names its customer — the
|
|
123
|
+
// study target is the named external participant or company, and the match
|
|
124
|
+
// constraints narrow further. A study with neither is invalid.
|
|
125
|
+
const selectorError = gongScopeValidationError({
|
|
126
|
+
window: { start: from, end: to },
|
|
127
|
+
named_external_participants: [...(run.match?.required_participants ?? []), ...(run.target?.name?.trim() ? [run.target.name.trim()] : [])],
|
|
128
|
+
external_companies_or_domains: run.target?.organization?.trim() ? [run.target.organization.trim()] : [],
|
|
129
|
+
title_tokens: run.match?.required_title_tokens ?? [],
|
|
130
|
+
});
|
|
131
|
+
if (selectorError)
|
|
132
|
+
throw new Error(selectorError);
|
|
133
|
+
return { target_id: targetId, lane: "study_linked", dir, connection_alias: run.source.connection_alias, window: { from, to }, workspace: env.FOO_GONG_WORKSPACE?.trim() || undefined };
|
|
134
|
+
}
|
|
135
|
+
function toRecord(call, parties, transcriptScope) {
|
|
136
|
+
const started = call.started ?? call.scheduled;
|
|
137
|
+
if (!started)
|
|
138
|
+
return null;
|
|
139
|
+
return {
|
|
140
|
+
id: call.id,
|
|
141
|
+
title: call.title?.trim() || "Untitled call",
|
|
142
|
+
started,
|
|
143
|
+
duration_seconds: call.duration,
|
|
144
|
+
// A listed Gong call is a recorded, processed call; a zero-length record
|
|
145
|
+
// is not a completed conversation.
|
|
146
|
+
state: (call.duration ?? 0) > 0 ? "completed" : "in_progress",
|
|
147
|
+
url: call.url,
|
|
148
|
+
parties: (parties ?? []).map((party) => ({ name: party.name, email: party.emailAddress, title: party.title, affiliation: party.affiliation, speaker_id: party.speakerId })),
|
|
149
|
+
transcript_access: transcriptScope === "missing" ? "missing" : "granted",
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
// R-004: a call with no external party is the company talking to itself and
|
|
153
|
+
// is excluded by default. A call whose party data is absent stays in — the
|
|
154
|
+
// scope's own selectors still have to admit it.
|
|
155
|
+
function internalOnly(parties) {
|
|
156
|
+
if (!parties || parties.length === 0)
|
|
157
|
+
return false;
|
|
158
|
+
return parties.every((party) => party.affiliation?.trim().toLowerCase() === "internal");
|
|
159
|
+
}
|
|
160
|
+
async function connect(deps) {
|
|
161
|
+
const connection = await resolveGongConnection({ env: deps.env, fetchImpl: deps.fetchImpl });
|
|
162
|
+
const client = new GongClient(connection.credential, { fetchImpl: deps.fetchImpl, baseUrl: deps.env.FOO_GONG_API_BASE, sleep: deps.sleep, fetchTimeoutMs: deps.fetchTimeoutMs });
|
|
163
|
+
return { connection, client };
|
|
164
|
+
}
|
|
165
|
+
async function listSessions(target, connection, client) {
|
|
166
|
+
const listing = await client.listCalls({ from: target.window.from, to: target.window.to, workspaceId: target.workspace });
|
|
167
|
+
// A ceiling stop is final for the run (R-004/R-010): no further provider
|
|
168
|
+
// reads happen — the parties read waits for a window that fits the agreed
|
|
169
|
+
// pages, never for a wider one.
|
|
170
|
+
if (listing.truncated)
|
|
171
|
+
return { sessions: [], listed: listing.calls.length, pages: listing.pages, truncated: true, internal_only_excluded: 0, incomplete: 0 };
|
|
172
|
+
const parties = listing.calls.length ? await client.parties(listing.calls.map((call) => call.id)) : new Map();
|
|
173
|
+
const sessions = [];
|
|
174
|
+
let internalExcluded = 0;
|
|
175
|
+
let incomplete = 0;
|
|
176
|
+
for (const call of listing.calls) {
|
|
177
|
+
const own = parties.get(call.id);
|
|
178
|
+
if (internalOnly(own)) {
|
|
179
|
+
internalExcluded += 1;
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
const record = toRecord(call, own, connection.transcript_scope);
|
|
183
|
+
if (!record) {
|
|
184
|
+
incomplete += 1;
|
|
185
|
+
continue;
|
|
186
|
+
}
|
|
187
|
+
const session = gongDiscoveryAdapter.normalizeSession(record, target.connection_alias);
|
|
188
|
+
if (session.completion_state !== "completed") {
|
|
189
|
+
incomplete += 1;
|
|
190
|
+
continue;
|
|
191
|
+
}
|
|
192
|
+
sessions.push({ ...session, access: connection.transcript_scope === "missing" ? "denied" : "available" });
|
|
193
|
+
}
|
|
194
|
+
return { sessions, listed: listing.calls.length, pages: listing.pages, truncated: listing.truncated, internal_only_excluded: internalExcluded, incomplete };
|
|
195
|
+
}
|
|
196
|
+
async function readyCommand(root, json, deps) {
|
|
197
|
+
const env = deps.env;
|
|
198
|
+
const coverage = {
|
|
199
|
+
provider: "gong",
|
|
200
|
+
mode: MODE,
|
|
201
|
+
support_state: SUPPORT_STATE,
|
|
202
|
+
connection_owner: "workspace",
|
|
203
|
+
configured: gongConnectionConfigured(env),
|
|
204
|
+
credential_broker: gongCredentialBroker(env),
|
|
205
|
+
synthesis: { available: synthesisCredentialPresent(env), model: defaultSynthesisModel(env) },
|
|
206
|
+
policy: { ...GONG_POLICY },
|
|
207
|
+
};
|
|
208
|
+
try {
|
|
209
|
+
const { connection, client } = await connect(deps);
|
|
210
|
+
// The readiness probe is one metadata call inside a one-hour window: it
|
|
211
|
+
// proves the credential and the listing permission without reading calls.
|
|
212
|
+
const now = deps.now();
|
|
213
|
+
await client.listCalls({ from: isoDaysAgo(now, 1 / 24), to: now, workspaceId: env.FOO_GONG_WORKSPACE?.trim() || undefined });
|
|
214
|
+
coverage.connection = connection.connection_ref;
|
|
215
|
+
coverage.credential_broker = connection.broker;
|
|
216
|
+
coverage.credential_type = connection.credential_type;
|
|
217
|
+
coverage.capabilities = { list_sessions: true, open_transcript: connection.transcript_scope !== "missing", stable_link: true, timestamps: true, transcript_scope: connection.transcript_scope };
|
|
218
|
+
coverage.provider_calls = client.requests;
|
|
219
|
+
return outcome(result("ok", { coverage, next_actions: [
|
|
220
|
+
...(connection.broker === "direct" ? ["Credential broker is direct: this runtime holds an administrator-issued access key itself. Refresh and revoke happen in Gong, not centrally; move to Nango for the company connection."] : []),
|
|
221
|
+
"Approve a bounded Gong scope, then run foo discovery gong list <study-id|inbox:scope-id>.", ...(coverage.synthesis && !coverage.synthesis.available ? ["Set ANTHROPIC_API_KEY (or ANTHROPIC_AUTH_TOKEN) before processing a transcript."] : [])
|
|
222
|
+
] }), json);
|
|
223
|
+
}
|
|
224
|
+
catch (error) {
|
|
225
|
+
if (error instanceof GongRuntimeError)
|
|
226
|
+
return fromRuntimeError(error, json, undefined, coverage);
|
|
227
|
+
return invalid("Gong readiness failed closed without exposing private source content.", json, "E_DISCOVERY_VALIDATION", undefined, coverage);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
async function listCommand(root, targetId, json, deps) {
|
|
231
|
+
if (!targetId)
|
|
232
|
+
return invalid("Usage: foo discovery gong list <study-id|inbox:scope-id>", json);
|
|
233
|
+
const now = deps.now();
|
|
234
|
+
let target;
|
|
235
|
+
try {
|
|
236
|
+
target = loadTarget(root, targetId, deps.env, now);
|
|
237
|
+
}
|
|
238
|
+
catch (error) {
|
|
239
|
+
return invalid(error instanceof Error ? error.message : "invalid discovery target", json, "E_DISCOVERY_VALIDATION", targetId);
|
|
240
|
+
}
|
|
241
|
+
const coverage = { mode: MODE, window: target.window, lane: target.lane, transcript_opened: false };
|
|
242
|
+
try {
|
|
243
|
+
const { connection, client } = await connect(deps);
|
|
244
|
+
coverage.connection = connection.connection_ref;
|
|
245
|
+
const listed = await listSessions(target, connection, client);
|
|
246
|
+
Object.assign(coverage, { calls_listed: listed.listed, pages: listed.pages, truncated: listed.truncated, internal_only_excluded: listed.internal_only_excluded, incomplete_excluded: listed.incomplete, candidates_evaluated: listed.sessions.length, provider_calls: client.requests });
|
|
247
|
+
if (listed.truncated) {
|
|
248
|
+
return outcome(result("budget_exhausted", {
|
|
249
|
+
study_id: targetId,
|
|
250
|
+
coverage: { ...coverage, provider_error: "budget_exhausted" },
|
|
251
|
+
errors: [{ code: "E_DISCOVERY_VALIDATION", message: `The window holds more calls than the ${GONG_POLICY.max_list_pages}-page ceiling; narrow the window or selectors instead of widening the read.` }],
|
|
252
|
+
handoff: "Narrow the approved window or selectors and list again; the run never widens the window and never retries.",
|
|
253
|
+
}), json);
|
|
254
|
+
}
|
|
255
|
+
if (listed.sessions.length === 0)
|
|
256
|
+
return outcome(result("awaiting_match", { study_id: targetId, coverage, next_actions: ["No completed call with an external party was listed inside the approved window."] }), json);
|
|
257
|
+
if (target.lane === "study_linked") {
|
|
258
|
+
const matched = await runDiscoveryCommand(root, ["match", targetId, "--sessions-stdin", "--json"], JSON.stringify(listed.sessions), now);
|
|
259
|
+
return outcome({ ...matched.result, coverage: { ...matched.result.coverage, ...coverage }, next_actions: [...matched.result.next_actions, ...(matched.result.status === "ok" ? [`Run foo discovery gong process ${targetId} to open the one matched transcript and apply the sanitized evidence.`] : [])] }, json);
|
|
260
|
+
}
|
|
261
|
+
// Inbox: one session at a time so each source gets its own one-time proof.
|
|
262
|
+
let lastMiss = null;
|
|
263
|
+
let evaluated = 0;
|
|
264
|
+
for (const session of listed.sessions) {
|
|
265
|
+
evaluated += 1;
|
|
266
|
+
const matched = await runDiscoveryCommand(root, ["match", targetId, "--sessions-stdin", "--json"], JSON.stringify([session]), now);
|
|
267
|
+
if (matched.result.status === "awaiting_match") {
|
|
268
|
+
lastMiss = matched.result;
|
|
269
|
+
continue;
|
|
270
|
+
}
|
|
271
|
+
const remaining = listed.sessions.length - evaluated;
|
|
272
|
+
return outcome({
|
|
273
|
+
...matched.result,
|
|
274
|
+
coverage: { ...matched.result.coverage, ...coverage, candidates_evaluated: evaluated, candidates_remaining: remaining },
|
|
275
|
+
next_actions: [
|
|
276
|
+
...matched.result.next_actions,
|
|
277
|
+
...(matched.result.status === "ok" ? [`Run foo discovery gong process ${targetId} to open this one transcript and apply the sanitized evidence.`] : []),
|
|
278
|
+
...(remaining > 0 ? [`${remaining} more listed call(s) were not evaluated; run list again after this one is processed.`] : []),
|
|
279
|
+
],
|
|
280
|
+
}, json);
|
|
281
|
+
}
|
|
282
|
+
return outcome(result("awaiting_match", { study_id: targetId, coverage: { ...coverage, candidates_evaluated: evaluated }, next_actions: lastMiss?.next_actions ?? ["No listed call matched the approved scope; nothing was opened."] }), json);
|
|
283
|
+
}
|
|
284
|
+
catch (error) {
|
|
285
|
+
if (error instanceof GongRuntimeError)
|
|
286
|
+
return fromRuntimeError(error, json, targetId, coverage);
|
|
287
|
+
return invalid("Gong listing failed closed without exposing private source content.", json, "E_DISCOVERY_VALIDATION", targetId, coverage);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
function readMatch(root, targetId) {
|
|
291
|
+
const file = path.join(localRoot(root), localTargetName(targetId), "match.json");
|
|
292
|
+
if (!existsSync(file))
|
|
293
|
+
return null;
|
|
294
|
+
try {
|
|
295
|
+
return JSON.parse(readFileSync(file, "utf8"));
|
|
296
|
+
}
|
|
297
|
+
catch {
|
|
298
|
+
return null;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
function studyHypotheses(dir) {
|
|
302
|
+
const file = path.join(dir, "study.md");
|
|
303
|
+
if (!existsSync(file))
|
|
304
|
+
return [];
|
|
305
|
+
const out = [];
|
|
306
|
+
for (const line of readFileSync(file, "utf8").split("\n")) {
|
|
307
|
+
const match = /^\s*(?:[-*]|\d+\.)?\s*\**(H-\d{3})\**\s*[:—–-]?\s*(.+?)\s*$/.exec(line);
|
|
308
|
+
if (match && !out.some((entry) => entry.id === match[1]))
|
|
309
|
+
out.push({ id: match[1], text: match[2] });
|
|
310
|
+
}
|
|
311
|
+
return out;
|
|
312
|
+
}
|
|
313
|
+
function mmss(ms) {
|
|
314
|
+
const total = Math.max(0, Math.floor((ms ?? 0) / 1000));
|
|
315
|
+
return `${String(Math.floor(total / 60)).padStart(2, "0")}:${String(total % 60).padStart(2, "0")}`;
|
|
316
|
+
}
|
|
317
|
+
// Role labels only: the speaker map is used inside this request to say who is
|
|
318
|
+
// the customer and who is the company, and the names never enter the text.
|
|
319
|
+
function renderTranscript(monologues, roles) {
|
|
320
|
+
const lines = [];
|
|
321
|
+
for (const monologue of monologues) {
|
|
322
|
+
const role = (monologue.speakerId && roles.get(monologue.speakerId)) || "Unknown speaker";
|
|
323
|
+
for (const sentence of monologue.sentences)
|
|
324
|
+
lines.push(`[${mmss(sentence.start)}] ${role}: ${sentence.text}`);
|
|
325
|
+
}
|
|
326
|
+
return lines.join("\n");
|
|
327
|
+
}
|
|
328
|
+
function containsIdentity(envelopeText, identities) {
|
|
329
|
+
const haystack = envelopeText.toLowerCase();
|
|
330
|
+
return identities.some((identity) => identity.length >= 4 && haystack.includes(identity.toLowerCase()));
|
|
331
|
+
}
|
|
332
|
+
async function processCommand(root, targetId, json, deps) {
|
|
333
|
+
if (!targetId)
|
|
334
|
+
return invalid("Usage: foo discovery gong process <study-id|inbox:scope-id>", json);
|
|
335
|
+
const now = deps.now();
|
|
336
|
+
let target;
|
|
337
|
+
try {
|
|
338
|
+
target = loadTarget(root, targetId, deps.env, now, "process");
|
|
339
|
+
}
|
|
340
|
+
catch (error) {
|
|
341
|
+
return invalid(error instanceof Error ? error.message : "invalid discovery target", json, "E_DISCOVERY_VALIDATION", targetId);
|
|
342
|
+
}
|
|
343
|
+
const match = readMatch(root, targetId);
|
|
344
|
+
if (!match || !Array.isArray(match.candidates) || match.candidates.length === 0)
|
|
345
|
+
return invalid("No matched Gong call is recorded for this target; run foo discovery gong list first.", json, "E_DISCOVERY_VALIDATION", targetId);
|
|
346
|
+
if (match.candidates.length > 1)
|
|
347
|
+
return outcome(result("ambiguous_match", { study_id: targetId, errors: [{ code: "E_DISCOVERY_AMBIGUOUS_MATCH", message: "Several calls matched; a person must select one before any transcript is opened." }], handoff: `foo discovery review ${targetId} select-source --fingerprint <sha256> --by <person>` }), json);
|
|
348
|
+
const candidate = match.candidates[0];
|
|
349
|
+
const session = candidate.session;
|
|
350
|
+
const callId = session.runtime_handle;
|
|
351
|
+
if (!synthesisCredentialPresent(deps.env))
|
|
352
|
+
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 transcript was not opened." }], handoff: "Set ANTHROPIC_API_KEY (or ANTHROPIC_AUTH_TOKEN) for this runtime, then rerun process." }), json);
|
|
353
|
+
const model = defaultSynthesisModel(deps.env);
|
|
354
|
+
const coverage = { mode: MODE, lane: target.lane, policy_version: GONG_POLICY.policy_version, model };
|
|
355
|
+
const hypotheses = target.lane === "study_linked" ? studyHypotheses(target.dir) : [];
|
|
356
|
+
if (target.lane === "study_linked" && hypotheses.length === 0)
|
|
357
|
+
return invalid("The study records no H-### hypotheses in study.md; study-linked evidence needs one to test.", json, "E_DISCOVERY_VALIDATION", targetId);
|
|
358
|
+
let client;
|
|
359
|
+
try {
|
|
360
|
+
const connected = await connect(deps);
|
|
361
|
+
client = connected.client;
|
|
362
|
+
coverage.connection = connected.connection.connection_ref;
|
|
363
|
+
if (connected.connection.transcript_scope === "missing") {
|
|
364
|
+
return outcome(result("needs_provider_access", { study_id: targetId, errors: [{ code: "E_DISCOVERY_PROVIDER_ACCESS", message: "The Gong credential has no transcript permission; the call was not opened." }], handoff: session.stable_url ? `Request transcript access for ${session.stable_url}` : "Request transcript access from the Gong administrator.", coverage }), json);
|
|
365
|
+
}
|
|
366
|
+
const parties = (await client.parties([callId])).get(callId) ?? [];
|
|
367
|
+
const roles = new Map();
|
|
368
|
+
const identities = [];
|
|
369
|
+
for (const party of parties) {
|
|
370
|
+
if (party.speakerId)
|
|
371
|
+
roles.set(party.speakerId, party.affiliation?.trim().toLowerCase() === "internal" ? "Company representative" : "Customer");
|
|
372
|
+
if (party.name?.trim())
|
|
373
|
+
identities.push(party.name.trim());
|
|
374
|
+
if (party.emailAddress?.trim())
|
|
375
|
+
identities.push(party.emailAddress.trim());
|
|
376
|
+
}
|
|
377
|
+
let openError;
|
|
378
|
+
let transcriptText;
|
|
379
|
+
try {
|
|
380
|
+
const monologues = await openAuthorizedTranscript({ study_dir: target.dir, local_root: localRoot(root), study_id: targetId, source_fingerprint: candidate.source_fingerprint, now }, async () => {
|
|
381
|
+
try {
|
|
382
|
+
return await client.transcript(callId);
|
|
383
|
+
}
|
|
384
|
+
catch (error) {
|
|
385
|
+
if (error instanceof GongRuntimeError)
|
|
386
|
+
openError = error;
|
|
387
|
+
throw error;
|
|
388
|
+
}
|
|
389
|
+
});
|
|
390
|
+
transcriptText = renderTranscript(monologues, roles);
|
|
391
|
+
}
|
|
392
|
+
catch (error) {
|
|
393
|
+
if (openError)
|
|
394
|
+
return fromRuntimeError(openError, json, targetId, { ...coverage, transcript_body_requests: client.requests.transcript });
|
|
395
|
+
return outcome(result("needs_research_proof", { study_id: targetId, errors: [{ code: "E_DISCOVERY_RESEARCH_PROOF", message: "The transcript was not opened: the discovery proof is missing, expired, already used, or no longer matches the approval." }], handoff: `Run foo discovery gong list ${targetId} again to re-match and mint a fresh proof.`, coverage }), json);
|
|
396
|
+
}
|
|
397
|
+
coverage.transcript_body_requests = client.requests.transcript;
|
|
398
|
+
const estimatedTokens = Math.ceil(transcriptText.length / 4);
|
|
399
|
+
if (estimatedTokens > GONG_POLICY.max_model_input_tokens) {
|
|
400
|
+
transcriptText = null;
|
|
401
|
+
return fromRuntimeError(new GongRuntimeError("transcript_too_large", "the transcript exceeds the model-input ceiling; nothing was synthesized"), json, targetId, coverage);
|
|
402
|
+
}
|
|
403
|
+
let synthesized;
|
|
404
|
+
try {
|
|
405
|
+
synthesized = await synthesizeDiscoveryEnvelope({
|
|
406
|
+
target_id: targetId,
|
|
407
|
+
lane: target.lane,
|
|
408
|
+
source: { provider: "gong", ...(session.stable_url ? { stable_url: session.stable_url } : {}), source_fingerprint: candidate.source_fingerprint, completed_at: session.completed_at, locator_capability: session.capabilities.timestamps ? "timestamp" : "none" },
|
|
409
|
+
title: session.title,
|
|
410
|
+
hypotheses,
|
|
411
|
+
transcript_text: transcriptText,
|
|
412
|
+
transcript_complete: true,
|
|
413
|
+
caveats: session.stable_url ? [] : ["The provider returned no canonical call link for this call."],
|
|
414
|
+
}, { model, transport: deps.transport });
|
|
415
|
+
}
|
|
416
|
+
catch {
|
|
417
|
+
return invalid("Synthesis did not produce a valid closed envelope; nothing was written and the transcript was discarded.", json, "E_DISCOVERY_INVALID_ENVELOPE", targetId, coverage);
|
|
418
|
+
}
|
|
419
|
+
finally {
|
|
420
|
+
transcriptText = null;
|
|
421
|
+
}
|
|
422
|
+
coverage.synthesis_usage = synthesized.usage ?? null;
|
|
423
|
+
// Privacy screen beyond the envelope validator: party names and addresses
|
|
424
|
+
// from the provider's own metadata must not appear in derived text.
|
|
425
|
+
const derivedText = [
|
|
426
|
+
...synthesized.envelope.findings.flatMap((finding) => [finding.headline, finding.paraphrase, finding.dissent]),
|
|
427
|
+
...synthesized.envelope.coverage.caveats,
|
|
428
|
+
].join("\n");
|
|
429
|
+
if (containsIdentity(derivedText, identities))
|
|
430
|
+
return invalid("Synthesis leaked a participant identity into derived text; nothing was written.", json, "E_DISCOVERY_INVALID_ENVELOPE", targetId, coverage);
|
|
431
|
+
const applied = await runDiscoveryCommand(root, ["apply", targetId, "--evidence-stdin", "--json"], JSON.stringify(synthesized.envelope), now);
|
|
432
|
+
return outcome({ ...applied.result, coverage: { ...applied.result.coverage, ...coverage } }, json);
|
|
433
|
+
}
|
|
434
|
+
catch (error) {
|
|
435
|
+
if (error instanceof GongRuntimeError)
|
|
436
|
+
return fromRuntimeError(error, json, targetId, { ...coverage, transcript_body_requests: client?.requests.transcript ?? 0 });
|
|
437
|
+
return invalid("Gong processing failed closed without exposing private source content.", json, "E_DISCOVERY_VALIDATION", targetId, coverage);
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
async function syncCommand(root, json, deps) {
|
|
441
|
+
const now = deps.now();
|
|
442
|
+
const plan = planDiscoverySync(root, now);
|
|
443
|
+
const lanes = plan.lanes.filter((lane) => lane.provider.toLowerCase() === "gong");
|
|
444
|
+
if (lanes.length === 0)
|
|
445
|
+
return outcome(result("clean_no_op", { errors: plan.errors.map((message) => ({ code: "E_DISCOVERY_VALIDATION", message })), next_actions: ["No approved Gong lane is active; approve a study source or an inbox scope first."], coverage: { mode: MODE, lanes: 0, connector_reads: 0 } }), json);
|
|
446
|
+
const results = [];
|
|
447
|
+
// Metadata and match only: a transcript is opened only by an explicit
|
|
448
|
+
// `process` on one target. The loop stays unarmed (R-010).
|
|
449
|
+
for (const lane of lanes) {
|
|
450
|
+
const targetId = lane.kind === "inbox" ? `inbox:${lane.scope_id}` : lane.study_id;
|
|
451
|
+
const listed = await listCommand(root, targetId, true, deps);
|
|
452
|
+
results.push({ lane_id: lane.lane_id, status: listed.result.status, handoff: listed.result.handoff });
|
|
453
|
+
}
|
|
454
|
+
const ready = results.filter((entry) => entry.status === "ok").map((entry) => entry.lane_id);
|
|
455
|
+
return outcome(result("ok", {
|
|
456
|
+
errors: plan.errors.map((message) => ({ code: "E_DISCOVERY_VALIDATION", message })),
|
|
457
|
+
next_actions: [
|
|
458
|
+
...ready.map((laneId) => `${laneId}: one call matched — run foo discovery gong process ${laneId.replace(/^study:/, "")} to open it.`),
|
|
459
|
+
...results.filter((entry) => entry.status !== "ok").map((entry) => `${entry.lane_id}: ${entry.status}${entry.handoff ? ` — ${entry.handoff}` : ""}`),
|
|
460
|
+
],
|
|
461
|
+
coverage: { mode: MODE, lanes: results.length, transcripts_opened: 0, results },
|
|
462
|
+
}), json);
|
|
463
|
+
}
|
|
464
|
+
export async function runGongDiscovery(root, args, deps = {}) {
|
|
465
|
+
const json = hasFlag(args, "--json");
|
|
466
|
+
const env = deps.env ?? process.env;
|
|
467
|
+
const resolved = {
|
|
468
|
+
fetchImpl: deps.fetchImpl ?? globalThis.fetch,
|
|
469
|
+
env,
|
|
470
|
+
now: deps.now ?? (() => new Date().toISOString()),
|
|
471
|
+
sleep: deps.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms))),
|
|
472
|
+
transport: deps.transport ?? anthropicSynthesisTransport({ fetchImpl: deps.fetchImpl, env }),
|
|
473
|
+
fetchTimeoutMs: deps.fetchTimeoutMs ?? GONG_POLICY.fetch_timeout_ms,
|
|
474
|
+
};
|
|
475
|
+
const command = args[0];
|
|
476
|
+
const target = args[1] && !args[1].startsWith("--") ? args[1] : undefined;
|
|
477
|
+
try {
|
|
478
|
+
if (command === "ready")
|
|
479
|
+
return await readyCommand(root, json, resolved);
|
|
480
|
+
if (command === "list")
|
|
481
|
+
return await listCommand(root, target, json, resolved);
|
|
482
|
+
if (command === "process")
|
|
483
|
+
return await processCommand(root, target, json, resolved);
|
|
484
|
+
if (command === "sync")
|
|
485
|
+
return await syncCommand(root, json, resolved);
|
|
486
|
+
return invalid("Try: ready | list <study-id|inbox:scope-id> | process <study-id|inbox:scope-id> | sync", json);
|
|
487
|
+
}
|
|
488
|
+
catch {
|
|
489
|
+
return invalid("Gong discovery failed closed without exposing private source content.", json);
|
|
490
|
+
}
|
|
491
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
export type LearningHomeType = "idea" | "research" | "backlog";
|
|
2
|
+
export type PlacementConfidence = "low" | "medium" | "high";
|
|
3
|
+
export type LearningDestination = {
|
|
4
|
+
type: LearningHomeType;
|
|
5
|
+
ref: string;
|
|
6
|
+
path: string;
|
|
7
|
+
title: string;
|
|
8
|
+
text: string;
|
|
9
|
+
blocks: string[];
|
|
10
|
+
};
|
|
11
|
+
export type LearningRouteCandidate = {
|
|
12
|
+
destination: LearningDestination;
|
|
13
|
+
score: number;
|
|
14
|
+
};
|
|
15
|
+
export type LearningClusterFinding = {
|
|
16
|
+
finding_id: string;
|
|
17
|
+
inbox_scope_id: string;
|
|
18
|
+
headline: string;
|
|
19
|
+
paraphrase: string;
|
|
20
|
+
dissent?: string;
|
|
21
|
+
proposal_text?: string;
|
|
22
|
+
};
|
|
23
|
+
export type LearningSignalCluster = {
|
|
24
|
+
cluster_key: string;
|
|
25
|
+
status: "emerging" | "eligible_for_synthesis";
|
|
26
|
+
source_finding_ids: string[];
|
|
27
|
+
source_scope_ids: string[];
|
|
28
|
+
findings: LearningClusterFinding[];
|
|
29
|
+
};
|
|
30
|
+
export type HypothesisSynthesis = {
|
|
31
|
+
source_finding_ids: string[];
|
|
32
|
+
claim: string;
|
|
33
|
+
dissent: string;
|
|
34
|
+
falsifier: string;
|
|
35
|
+
next_evidence: string;
|
|
36
|
+
starting_confidence: "low" | "medium";
|
|
37
|
+
};
|
|
38
|
+
export type ValidatedHypothesisSynthesis = HypothesisSynthesis & {
|
|
39
|
+
source_scope_ids: string[];
|
|
40
|
+
};
|
|
41
|
+
export type LearningRouteDecision = {
|
|
42
|
+
kind: "home";
|
|
43
|
+
destination: LearningDestination;
|
|
44
|
+
basis: "exact_ref" | "semantic";
|
|
45
|
+
placement_confidence: "medium" | "high";
|
|
46
|
+
why: string;
|
|
47
|
+
score: number;
|
|
48
|
+
margin: number;
|
|
49
|
+
} | {
|
|
50
|
+
kind: "unmatched";
|
|
51
|
+
candidates: LearningRouteCandidate[];
|
|
52
|
+
why: string;
|
|
53
|
+
} | {
|
|
54
|
+
kind: "hypothesis";
|
|
55
|
+
claim: string;
|
|
56
|
+
dissent: string;
|
|
57
|
+
falsifier: string;
|
|
58
|
+
next_evidence: string;
|
|
59
|
+
starting_confidence: "low" | "medium";
|
|
60
|
+
why: string;
|
|
61
|
+
} | {
|
|
62
|
+
kind: "unverified";
|
|
63
|
+
reason: "conflict" | "insufficient_evidence" | "incomplete_hypothesis";
|
|
64
|
+
candidates: LearningRouteCandidate[];
|
|
65
|
+
why: string;
|
|
66
|
+
};
|
|
67
|
+
export declare const LEARNING_ROUTING_POLICY: {
|
|
68
|
+
readonly semantic_minimum: 0.45;
|
|
69
|
+
readonly semantic_margin: 0.12;
|
|
70
|
+
readonly catch_all_shared_max: 5;
|
|
71
|
+
readonly synthesis_source_overlap_max: 0.82;
|
|
72
|
+
};
|
|
73
|
+
export declare function buildLearningDestinationCatalog(root: string): LearningDestination[];
|
|
74
|
+
export declare function clusterUnmatchedLearnings(input: LearningClusterFinding[]): LearningSignalCluster[];
|
|
75
|
+
export declare function validateHypothesisSynthesis(input: {
|
|
76
|
+
cluster: LearningSignalCluster;
|
|
77
|
+
synthesis: HypothesisSynthesis;
|
|
78
|
+
}): {
|
|
79
|
+
ok: true;
|
|
80
|
+
value: ValidatedHypothesisSynthesis;
|
|
81
|
+
} | {
|
|
82
|
+
ok: false;
|
|
83
|
+
reason: string;
|
|
84
|
+
};
|
|
85
|
+
export declare function routeLearning(input: {
|
|
86
|
+
finding: Record<string, unknown>;
|
|
87
|
+
proposal?: Record<string, unknown> | null;
|
|
88
|
+
destinations: LearningDestination[];
|
|
89
|
+
sharedTargetCounts?: ReadonlyMap<string, number>;
|
|
90
|
+
}): LearningRouteDecision;
|