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,279 @@
|
|
|
1
|
+
// Notion through Nango (notion-discovery-connector-v0, contract-only docs slice).
|
|
2
|
+
//
|
|
3
|
+
// Nango is the credential broker (the packet's External Prerequisites: Nango
|
|
4
|
+
// as the OAuth broker for the company Notion connection). Everything here is
|
|
5
|
+
// content-free on every surface productfoo owns: the credential is held in process
|
|
6
|
+
// memory for one operation, the Nango connection id is never printed (an
|
|
7
|
+
// opaque `connection_ref` stands in for it), and errors carry a typed code
|
|
8
|
+
// plus a bounded message that never embeds a provider response.
|
|
9
|
+
//
|
|
10
|
+
// The Notion API shapes below are beliefs owned by the packet's feasibility
|
|
11
|
+
// task (T-001) and pinned by fixtures in test/discoveryNotionRuntime.test.ts;
|
|
12
|
+
// the receipt-backed feasibility report (R-003) is what promotes them from
|
|
13
|
+
// belief to verified. Support stays contract_only until then.
|
|
14
|
+
//
|
|
15
|
+
// Read-only by construction (R-007): only documented read operations are
|
|
16
|
+
// reachable; any other method or path routes through the single write-refusal
|
|
17
|
+
// choke point so the refusal is testable rather than scattered. The
|
|
18
|
+
// workspace-wide search endpoint is deliberately NOT on the allow-list — a
|
|
19
|
+
// workspace-wide listing is exactly what R-004 refuses.
|
|
20
|
+
import { createHash } from "node:crypto";
|
|
21
|
+
import { refuseSourceWrite } from "./sourceIngress.js";
|
|
22
|
+
export class NotionRuntimeError extends Error {
|
|
23
|
+
code;
|
|
24
|
+
constructor(code, message) {
|
|
25
|
+
super(message);
|
|
26
|
+
this.code = code;
|
|
27
|
+
this.name = "NotionRuntimeError";
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
// Versioned policy ceilings (the Granola/Gong policy pattern): hard limits the
|
|
31
|
+
// runtime enforces and reports, so a later change is explicit rather than
|
|
32
|
+
// silent. Every ceiling fails closed.
|
|
33
|
+
export const NOTION_POLICY = {
|
|
34
|
+
policy_version: "notion-docs-0.1",
|
|
35
|
+
max_response_bytes: 2 * 1024 * 1024,
|
|
36
|
+
fetch_timeout_ms: 90_000,
|
|
37
|
+
max_model_input_tokens: 100_000,
|
|
38
|
+
max_list_records: 50,
|
|
39
|
+
min_request_interval_ms: 350,
|
|
40
|
+
};
|
|
41
|
+
const DEFAULT_NANGO_HOST = "https://api.nango.dev";
|
|
42
|
+
const DEFAULT_NOTION_BASE = "https://api.notion.com";
|
|
43
|
+
// Belief owned by T-001: the Notion API version header pins response shapes.
|
|
44
|
+
const NOTION_API_VERSION = "2022-06-28";
|
|
45
|
+
function nangoConfigured(env) {
|
|
46
|
+
return Boolean(env.NANGO_SECRET_KEY?.trim() && env.FOO_NOTION_NANGO_CONNECTION?.trim());
|
|
47
|
+
}
|
|
48
|
+
function directConfigured(env) {
|
|
49
|
+
return Boolean(env.FOO_NOTION_API_TOKEN?.trim());
|
|
50
|
+
}
|
|
51
|
+
export function notionConnectionConfigured(env) {
|
|
52
|
+
return nangoConfigured(env) || directConfigured(env);
|
|
53
|
+
}
|
|
54
|
+
export function notionCredentialBroker(env) {
|
|
55
|
+
if (nangoConfigured(env))
|
|
56
|
+
return "nango";
|
|
57
|
+
if (directConfigured(env))
|
|
58
|
+
return "direct";
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
function opaqueConnectionRef(integration, connectionId) {
|
|
62
|
+
return `notion:${createHash("sha256").update(`${integration}\n${connectionId}`).digest("hex").slice(0, 16)}`;
|
|
63
|
+
}
|
|
64
|
+
async function fetchWithTimeout(fetchImpl, url, init, timeoutMs) {
|
|
65
|
+
const controller = new AbortController();
|
|
66
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
67
|
+
try {
|
|
68
|
+
return await fetchImpl(url, { ...init, signal: controller.signal });
|
|
69
|
+
}
|
|
70
|
+
catch (error) {
|
|
71
|
+
if (controller.signal.aborted)
|
|
72
|
+
throw new NotionRuntimeError("provider_timeout", "the provider did not answer within the fetch ceiling");
|
|
73
|
+
throw new NotionRuntimeError("network", `network failure reaching ${new URL(url).hostname}`);
|
|
74
|
+
}
|
|
75
|
+
finally {
|
|
76
|
+
clearTimeout(timer);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
// Reads a response body while counting toward the byte ceiling, so oversized
|
|
80
|
+
// page content is dropped before it is ever materialized in full.
|
|
81
|
+
async function readBounded(response, maxBytes) {
|
|
82
|
+
const declared = Number(response.headers.get("content-length") ?? "0");
|
|
83
|
+
if (Number.isFinite(declared) && declared > maxBytes)
|
|
84
|
+
throw new NotionRuntimeError("content_too_large", "the provider response exceeds the byte ceiling");
|
|
85
|
+
if (!response.body) {
|
|
86
|
+
const buffer = await response.arrayBuffer();
|
|
87
|
+
if (buffer.byteLength > maxBytes)
|
|
88
|
+
throw new NotionRuntimeError("content_too_large", "the provider response exceeds the byte ceiling");
|
|
89
|
+
return new TextDecoder().decode(buffer);
|
|
90
|
+
}
|
|
91
|
+
const reader = response.body.getReader();
|
|
92
|
+
const chunks = [];
|
|
93
|
+
let total = 0;
|
|
94
|
+
for (;;) {
|
|
95
|
+
const { done, value } = await reader.read();
|
|
96
|
+
if (done)
|
|
97
|
+
break;
|
|
98
|
+
total += value.byteLength;
|
|
99
|
+
if (total > maxBytes) {
|
|
100
|
+
await reader.cancel().catch(() => undefined);
|
|
101
|
+
chunks.length = 0;
|
|
102
|
+
throw new NotionRuntimeError("content_too_large", "the provider response exceeds the byte ceiling");
|
|
103
|
+
}
|
|
104
|
+
chunks.push(value);
|
|
105
|
+
}
|
|
106
|
+
return new TextDecoder().decode(Buffer.concat(chunks));
|
|
107
|
+
}
|
|
108
|
+
function parseJson(text, label) {
|
|
109
|
+
try {
|
|
110
|
+
const parsed = JSON.parse(text);
|
|
111
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
|
|
112
|
+
throw new Error("not an object");
|
|
113
|
+
return parsed;
|
|
114
|
+
}
|
|
115
|
+
catch {
|
|
116
|
+
throw new NotionRuntimeError("provider_failure", `${label} returned a response productfoo could not read`);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Resolve the company's Notion credential. Nango is the broker: it holds the
|
|
121
|
+
* OAuth credential, refreshes it, and is where an administrator revokes it.
|
|
122
|
+
* When Nango is not configured, an administrator-issued integration token set
|
|
123
|
+
* directly in this runtime's environment (FOO_NOTION_API_TOKEN) is accepted as
|
|
124
|
+
* the direct fallback — the same read-only surface, minus the central refresh
|
|
125
|
+
* and revoke story, and reported as `direct` so it is never mistaken for the
|
|
126
|
+
* company connection. Either way productfoo receives the credential for one operation
|
|
127
|
+
* and never persists or prints it.
|
|
128
|
+
*/
|
|
129
|
+
export async function resolveNotionConnection(input) {
|
|
130
|
+
const broker = notionCredentialBroker(input.env);
|
|
131
|
+
if (broker === null) {
|
|
132
|
+
throw new NotionRuntimeError("not_connected", "Notion is not connected: set NANGO_SECRET_KEY and FOO_NOTION_NANGO_CONNECTION for the company connection through Nango, or FOO_NOTION_API_TOKEN for a direct administrator-issued integration token");
|
|
133
|
+
}
|
|
134
|
+
if (broker === "direct") {
|
|
135
|
+
const token = input.env.FOO_NOTION_API_TOKEN.trim();
|
|
136
|
+
return {
|
|
137
|
+
connection_ref: `notion:direct:${createHash("sha256").update(token).digest("hex").slice(0, 16)}`,
|
|
138
|
+
broker: "direct",
|
|
139
|
+
credential: { type: "oauth2", access_token: token },
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
const secret = input.env.NANGO_SECRET_KEY.trim();
|
|
143
|
+
const connectionId = input.env.FOO_NOTION_NANGO_CONNECTION.trim();
|
|
144
|
+
const integration = input.env.FOO_NOTION_NANGO_INTEGRATION?.trim() || "notion";
|
|
145
|
+
const host = (input.env.NANGO_HOST?.trim() || DEFAULT_NANGO_HOST).replace(/\/+$/, "");
|
|
146
|
+
const url = `${host}/connection/${encodeURIComponent(connectionId)}?provider_config_key=${encodeURIComponent(integration)}`;
|
|
147
|
+
const response = await fetchWithTimeout(input.fetchImpl, url, { method: "GET", headers: { authorization: `Bearer ${secret}`, accept: "application/json" } }, NOTION_POLICY.fetch_timeout_ms);
|
|
148
|
+
if (response.status === 401 || response.status === 403)
|
|
149
|
+
throw new NotionRuntimeError("not_connected", "Nango refused the secret key; reconnect Notion");
|
|
150
|
+
if (response.status === 404)
|
|
151
|
+
throw new NotionRuntimeError("not_connected", "Nango has no Notion connection with that reference; connect Notion first");
|
|
152
|
+
if (!response.ok)
|
|
153
|
+
throw new NotionRuntimeError("provider_failure", `Nango answered ${response.status} while resolving the connection`);
|
|
154
|
+
const body = parseJson(await readBounded(response, 256 * 1024), "Nango");
|
|
155
|
+
const credentials = (body.credentials ?? {});
|
|
156
|
+
const type = String(credentials.type ?? "").toUpperCase();
|
|
157
|
+
const token = String(credentials.access_token ?? "");
|
|
158
|
+
if (type !== "OAUTH2" || !token)
|
|
159
|
+
throw new NotionRuntimeError("not_connected", "the Nango connection carries no usable Notion OAuth token; reconnect Notion");
|
|
160
|
+
return { connection_ref: opaqueConnectionRef(integration, connectionId), broker: "nango", credential: { type: "oauth2", access_token: token } };
|
|
161
|
+
}
|
|
162
|
+
const READ_PATHS = new Set(["page", "blocks_children", "database_query"]);
|
|
163
|
+
/**
|
|
164
|
+
* Minimal read-only Notion REST client. Only the three documented read
|
|
165
|
+
* operations are reachable — page metadata, one page's block content, and a
|
|
166
|
+
* bounded database query. Any other method or path routes through the single
|
|
167
|
+
* write-refusal choke point (R-007), and the workspace-wide search endpoint is
|
|
168
|
+
* absent from the allow-list entirely (R-004).
|
|
169
|
+
*/
|
|
170
|
+
export class NotionClient {
|
|
171
|
+
credential;
|
|
172
|
+
options;
|
|
173
|
+
requests = { page: 0, content: 0, database: 0 };
|
|
174
|
+
lastRequestAt = 0;
|
|
175
|
+
baseUrl;
|
|
176
|
+
sleep;
|
|
177
|
+
now;
|
|
178
|
+
constructor(credential, options) {
|
|
179
|
+
this.credential = credential;
|
|
180
|
+
this.options = options;
|
|
181
|
+
this.baseUrl = (options.baseUrl ?? DEFAULT_NOTION_BASE).replace(/\/+$/, "");
|
|
182
|
+
this.sleep = options.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
|
|
183
|
+
this.now = options.now ?? (() => Date.now());
|
|
184
|
+
}
|
|
185
|
+
async request(kind, pathname, body, maxBytes = NOTION_POLICY.max_response_bytes) {
|
|
186
|
+
if (!READ_PATHS.has(kind))
|
|
187
|
+
refuseSourceWrite(`notion ${kind} ${pathname}`);
|
|
188
|
+
const wait = this.lastRequestAt + NOTION_POLICY.min_request_interval_ms - this.now();
|
|
189
|
+
if (wait > 0)
|
|
190
|
+
await this.sleep(wait);
|
|
191
|
+
this.lastRequestAt = this.now();
|
|
192
|
+
const response = await fetchWithTimeout(this.options.fetchImpl, `${this.baseUrl}${pathname}`, {
|
|
193
|
+
method: body !== undefined ? "POST" : "GET",
|
|
194
|
+
headers: { authorization: `Bearer ${this.credential.access_token}`, accept: "application/json", "notion-version": NOTION_API_VERSION, ...(body !== undefined ? { "content-type": "application/json" } : {}) },
|
|
195
|
+
...(body !== undefined ? { body: JSON.stringify(body) } : {}),
|
|
196
|
+
}, NOTION_POLICY.fetch_timeout_ms);
|
|
197
|
+
if (response.status === 401)
|
|
198
|
+
throw new NotionRuntimeError("auth_expired", "Notion rejected the credential; refresh or reconnect");
|
|
199
|
+
if (response.status === 403)
|
|
200
|
+
throw new NotionRuntimeError("access_denied", "Notion denied this read; the connection lacks the required permission");
|
|
201
|
+
if (response.status === 429)
|
|
202
|
+
throw new NotionRuntimeError("budget_exhausted", "Notion rate limit reached; no retry was attempted");
|
|
203
|
+
if (response.status === 404)
|
|
204
|
+
throw new NotionRuntimeError("provider_failure", "Notion reported the requested record does not exist or is not shared with the connection");
|
|
205
|
+
if (!response.ok)
|
|
206
|
+
throw new NotionRuntimeError("provider_failure", `Notion answered ${response.status}`);
|
|
207
|
+
return parseJson(await readBounded(response, maxBytes), "Notion");
|
|
208
|
+
}
|
|
209
|
+
/** One page's metadata — title, last-edited time, canonical link. No content. */
|
|
210
|
+
async page(pageId) {
|
|
211
|
+
this.requests.page += 1;
|
|
212
|
+
const body = await this.request("page", `/v1/pages/${encodeURIComponent(pageId)}`, undefined, 256 * 1024);
|
|
213
|
+
const title = plainText(body.properties?.title);
|
|
214
|
+
return {
|
|
215
|
+
id: typeof body.id === "string" ? body.id : pageId,
|
|
216
|
+
title: typeof title === "string" ? title : undefined,
|
|
217
|
+
last_edited_time: typeof body.last_edited_time === "string" ? body.last_edited_time : undefined,
|
|
218
|
+
url: typeof body.url === "string" ? body.url : undefined,
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Exactly one page's content (R-005): the page's block children flattened to
|
|
223
|
+
* plain text, read once, bounded by the byte ceiling. The caller is the
|
|
224
|
+
* proof-gated summarize flow; no other path may call this.
|
|
225
|
+
*/
|
|
226
|
+
async pageContent(pageId) {
|
|
227
|
+
this.requests.content += 1;
|
|
228
|
+
const body = await this.request("blocks_children", `/v1/blocks/${encodeURIComponent(pageId)}/children`, undefined, NOTION_POLICY.max_response_bytes);
|
|
229
|
+
const blocks = Array.isArray(body.results) ? body.results : [];
|
|
230
|
+
const lines = [];
|
|
231
|
+
for (const block of blocks) {
|
|
232
|
+
// A Notion block carries its text under the type key
|
|
233
|
+
// ({type: "paragraph", paragraph: {rich_text: [...]}}); some property
|
|
234
|
+
// shapes carry rich_text directly.
|
|
235
|
+
const kind = typeof block.type === "string" ? block.type : "";
|
|
236
|
+
const payload = kind && block[kind] && typeof block[kind] === "object" ? block[kind] : block;
|
|
237
|
+
const text = plainText(payload) ?? plainText(block);
|
|
238
|
+
if (text)
|
|
239
|
+
lines.push(text);
|
|
240
|
+
}
|
|
241
|
+
return lines.join("\n");
|
|
242
|
+
}
|
|
243
|
+
/** Bounded metadata listing for one approved database selection. No content. */
|
|
244
|
+
async queryDatabase(databaseId) {
|
|
245
|
+
this.requests.database += 1;
|
|
246
|
+
const rows = [];
|
|
247
|
+
let cursor;
|
|
248
|
+
for (;;) {
|
|
249
|
+
const body = await this.request("database_query", `/v1/databases/${encodeURIComponent(databaseId)}/query`, { page_size: NOTION_POLICY.max_list_records, ...(cursor ? { start_cursor: cursor } : {}) }, 1024 * 1024);
|
|
250
|
+
for (const entry of Array.isArray(body.results) ? body.results : []) {
|
|
251
|
+
if (typeof entry.id !== "string")
|
|
252
|
+
continue;
|
|
253
|
+
const properties = (entry.properties ?? {});
|
|
254
|
+
rows.push({
|
|
255
|
+
id: entry.id,
|
|
256
|
+
title: plainText(properties[Object.keys(properties).find((key) => properties[key]?.type === "title") ?? ""] ?? {}),
|
|
257
|
+
last_edited_time: typeof entry.last_edited_time === "string" ? entry.last_edited_time : undefined,
|
|
258
|
+
url: typeof entry.url === "string" ? entry.url : undefined,
|
|
259
|
+
});
|
|
260
|
+
if (rows.length >= NOTION_POLICY.max_list_records)
|
|
261
|
+
return rows;
|
|
262
|
+
}
|
|
263
|
+
cursor = typeof body.next_cursor === "string" && body.next_cursor ? body.next_cursor : undefined;
|
|
264
|
+
if (!cursor)
|
|
265
|
+
return rows;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
// Flattens a Notion rich-text array (or any property object carrying one)
|
|
270
|
+
// to its plain text. Belief owned by T-001, pinned by fixtures.
|
|
271
|
+
function plainText(value) {
|
|
272
|
+
if (!value || typeof value !== "object")
|
|
273
|
+
return undefined;
|
|
274
|
+
const rich = value.rich_text ?? value.title;
|
|
275
|
+
if (!Array.isArray(rich))
|
|
276
|
+
return undefined;
|
|
277
|
+
const text = rich.map((entry) => (entry && typeof entry === "object" && typeof entry.plain_text === "string" ? entry.plain_text : "")).join("");
|
|
278
|
+
return text.trim() ? text : undefined;
|
|
279
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export type OrchestrationResult = {
|
|
2
|
+
recommendation: string;
|
|
3
|
+
alternatives: readonly string[];
|
|
4
|
+
falsified: readonly string[];
|
|
5
|
+
dissent: string;
|
|
6
|
+
what_would_change_our_mind: string;
|
|
7
|
+
next_action: string;
|
|
8
|
+
};
|
|
9
|
+
export declare const ORCHESTRATION_RESULT_INSTRUCTION = "Finish with one recommendation, at most two alternatives, what was falsified, the strongest dissent, what would change the recommendation, and the next action. Keep full records as backing evidence.";
|
|
10
|
+
export declare function validateOrchestrationResult(value: unknown): readonly string[];
|
|
11
|
+
export declare function renderOrchestrationResult(value: OrchestrationResult): string;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
// orchestration-efficiency-v0 R-005: the decision is the product; orchestration
|
|
2
|
+
// records remain backing evidence. This closed shape deliberately excludes
|
|
3
|
+
// agents, phases, scores, token details, and internal IDs from the primary
|
|
4
|
+
// human result.
|
|
5
|
+
export const ORCHESTRATION_RESULT_INSTRUCTION = "Finish with one recommendation, at most two alternatives, what was falsified, the strongest dissent, what would change the recommendation, and the next action. Keep full records as backing evidence.";
|
|
6
|
+
const FIELDS = new Set([
|
|
7
|
+
"recommendation",
|
|
8
|
+
"alternatives",
|
|
9
|
+
"falsified",
|
|
10
|
+
"dissent",
|
|
11
|
+
"what_would_change_our_mind",
|
|
12
|
+
"next_action",
|
|
13
|
+
]);
|
|
14
|
+
function isObject(value) {
|
|
15
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
16
|
+
}
|
|
17
|
+
function nonEmptyString(value) {
|
|
18
|
+
return typeof value === "string" && value.trim().length > 0;
|
|
19
|
+
}
|
|
20
|
+
function stringArray(value) {
|
|
21
|
+
return Array.isArray(value) && value.every(nonEmptyString);
|
|
22
|
+
}
|
|
23
|
+
export function validateOrchestrationResult(value) {
|
|
24
|
+
if (!isObject(value))
|
|
25
|
+
return ["result must be an object"];
|
|
26
|
+
const findings = [];
|
|
27
|
+
for (const key of Object.keys(value)) {
|
|
28
|
+
if (!FIELDS.has(key))
|
|
29
|
+
findings.push(`unknown field: ${key}`);
|
|
30
|
+
}
|
|
31
|
+
for (const field of ["recommendation", "dissent", "what_would_change_our_mind", "next_action"]) {
|
|
32
|
+
if (!nonEmptyString(value[field]))
|
|
33
|
+
findings.push(`${field} must be a non-empty string`);
|
|
34
|
+
}
|
|
35
|
+
if (!stringArray(value.alternatives)) {
|
|
36
|
+
findings.push("alternatives must be an array of non-empty strings");
|
|
37
|
+
}
|
|
38
|
+
else if (value.alternatives.length > 2) {
|
|
39
|
+
findings.push("alternatives must contain at most 2 alternatives");
|
|
40
|
+
}
|
|
41
|
+
if (!stringArray(value.falsified)) {
|
|
42
|
+
findings.push("falsified must be an array of non-empty strings");
|
|
43
|
+
}
|
|
44
|
+
return findings;
|
|
45
|
+
}
|
|
46
|
+
function bullets(values, empty) {
|
|
47
|
+
return values.length > 0 ? values.map((value) => `- ${value.trim()}`) : [`- ${empty}`];
|
|
48
|
+
}
|
|
49
|
+
export function renderOrchestrationResult(value) {
|
|
50
|
+
const findings = validateOrchestrationResult(value);
|
|
51
|
+
if (findings.length > 0)
|
|
52
|
+
throw new Error(`invalid orchestration result: ${findings.join("; ")}`);
|
|
53
|
+
return [
|
|
54
|
+
"Recommendation",
|
|
55
|
+
value.recommendation.trim(),
|
|
56
|
+
"",
|
|
57
|
+
"Alternatives",
|
|
58
|
+
...bullets(value.alternatives, "None."),
|
|
59
|
+
"",
|
|
60
|
+
"What was falsified",
|
|
61
|
+
...bullets(value.falsified, "Nothing recorded."),
|
|
62
|
+
"",
|
|
63
|
+
"Strongest dissent",
|
|
64
|
+
value.dissent.trim(),
|
|
65
|
+
"",
|
|
66
|
+
"What would change this",
|
|
67
|
+
value.what_would_change_our_mind.trim(),
|
|
68
|
+
"",
|
|
69
|
+
"Next action",
|
|
70
|
+
value.next_action.trim(),
|
|
71
|
+
"",
|
|
72
|
+
].join("\n");
|
|
73
|
+
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
export type FetchLike = typeof globalThis.fetch;
|
|
2
|
+
export type OutlookRuntimeErrorCode = "not_connected" | "auth_expired" | "access_denied" | "provider_failure" | "network" | "provider_timeout" | "conversation_too_large" | "budget_exhausted";
|
|
3
|
+
export declare class OutlookRuntimeError extends Error {
|
|
4
|
+
readonly code: OutlookRuntimeErrorCode;
|
|
5
|
+
constructor(code: OutlookRuntimeErrorCode, message: string);
|
|
6
|
+
}
|
|
7
|
+
export type OutlookConnection = {
|
|
8
|
+
connection_ref: string;
|
|
9
|
+
broker: "nango";
|
|
10
|
+
credential: {
|
|
11
|
+
type: "oauth2";
|
|
12
|
+
access_token: string;
|
|
13
|
+
};
|
|
14
|
+
credential_type: "oauth2";
|
|
15
|
+
body_scope: "granted" | "missing" | "unknown";
|
|
16
|
+
};
|
|
17
|
+
export declare const OUTLOOK_POLICY: {
|
|
18
|
+
readonly policy_version: "outlook-local-worker-0.1";
|
|
19
|
+
readonly max_response_bytes: number;
|
|
20
|
+
readonly fetch_timeout_ms: 90000;
|
|
21
|
+
readonly max_model_input_tokens: 100000;
|
|
22
|
+
readonly max_list_pages: 5;
|
|
23
|
+
readonly min_request_interval_ms: 350;
|
|
24
|
+
readonly quiet_period_hours: 48;
|
|
25
|
+
};
|
|
26
|
+
export type OutlookConnectionEnv = Record<string, string | undefined>;
|
|
27
|
+
export declare function outlookConnectionConfigured(env: OutlookConnectionEnv): boolean;
|
|
28
|
+
export declare function outlookCredentialBroker(_env: OutlookConnectionEnv): "nango" | null;
|
|
29
|
+
/**
|
|
30
|
+
* Resolve the verified user's Outlook credential through Nango. There is no
|
|
31
|
+
* direct fallback (D-002): the mailbox belongs to one person, so the OAuth
|
|
32
|
+
* grant is always brokered, refreshable, and revocable centrally. productfoo receives
|
|
33
|
+
* the token for one command and never persists or prints it.
|
|
34
|
+
*/
|
|
35
|
+
export declare function resolveOutlookConnection(input: {
|
|
36
|
+
env: OutlookConnectionEnv;
|
|
37
|
+
fetchImpl: FetchLike;
|
|
38
|
+
}): Promise<OutlookConnection>;
|
|
39
|
+
export type OutlookParticipant = {
|
|
40
|
+
display_name?: string;
|
|
41
|
+
smtp?: string;
|
|
42
|
+
};
|
|
43
|
+
export type OutlookMessage = {
|
|
44
|
+
id: string;
|
|
45
|
+
conversation_id?: string;
|
|
46
|
+
received_at?: string;
|
|
47
|
+
categories?: string[];
|
|
48
|
+
from?: OutlookParticipant;
|
|
49
|
+
to?: OutlookParticipant[];
|
|
50
|
+
subject?: string;
|
|
51
|
+
body_text?: string;
|
|
52
|
+
};
|
|
53
|
+
type OutlookClientOptions = {
|
|
54
|
+
fetchImpl: FetchLike;
|
|
55
|
+
baseUrl?: string;
|
|
56
|
+
sleep?: (ms: number) => Promise<void>;
|
|
57
|
+
now?: () => number;
|
|
58
|
+
fetchTimeoutMs?: number;
|
|
59
|
+
};
|
|
60
|
+
/**
|
|
61
|
+
* Minimal read-only Microsoft Graph client. Only documented read operations
|
|
62
|
+
* are reachable — every request method is GET and anything else routes
|
|
63
|
+
* through the single write-refusal choke point (R-009), so the refusal is
|
|
64
|
+
* testable rather than scattered. There is no attachment fetch method at all
|
|
65
|
+
* (D-003): Graph models attachments as separate resources, and this client
|
|
66
|
+
* never asks for them.
|
|
67
|
+
*/
|
|
68
|
+
export declare class OutlookClient {
|
|
69
|
+
private readonly credential;
|
|
70
|
+
private readonly options;
|
|
71
|
+
readonly requests: {
|
|
72
|
+
profile: number;
|
|
73
|
+
list: number;
|
|
74
|
+
metadata: number;
|
|
75
|
+
conversation: number;
|
|
76
|
+
};
|
|
77
|
+
private lastRequestAt;
|
|
78
|
+
private readonly baseUrl;
|
|
79
|
+
private readonly fetchTimeoutMs;
|
|
80
|
+
private readonly sleep;
|
|
81
|
+
private readonly now;
|
|
82
|
+
constructor(credential: {
|
|
83
|
+
type: "oauth2";
|
|
84
|
+
access_token: string;
|
|
85
|
+
}, options: OutlookClientOptions);
|
|
86
|
+
private request;
|
|
87
|
+
/** The mailbox owner's address (metadata only) — defines "internal" for a per-user mailbox (R-004). */
|
|
88
|
+
profile(): Promise<{
|
|
89
|
+
email?: string;
|
|
90
|
+
}>;
|
|
91
|
+
/**
|
|
92
|
+
* Bounded metadata listing inside ONE explicit approved folder (R-004): a
|
|
93
|
+
* time window expressed as Graph's receivedDateTime range filter, a page
|
|
94
|
+
* ceiling, no message bodies. Pagination re-issues the SAME pinned filter
|
|
95
|
+
* with the next page's skiptoken — the approved window and folder are
|
|
96
|
+
* byte-identical on every page and a listing that exceeds the ceiling
|
|
97
|
+
* returns `truncated` and the run stops; it never widens.
|
|
98
|
+
*/
|
|
99
|
+
listFolderMessages(scope: {
|
|
100
|
+
folder: string;
|
|
101
|
+
from: string;
|
|
102
|
+
to: string;
|
|
103
|
+
}): Promise<{
|
|
104
|
+
messages: OutlookMessage[];
|
|
105
|
+
pages: number;
|
|
106
|
+
truncated: boolean;
|
|
107
|
+
}>;
|
|
108
|
+
/**
|
|
109
|
+
* One conversation's headers only — the bounded listing read (R-004):
|
|
110
|
+
* participants and dates without any message body. Bodies are fetched
|
|
111
|
+
* solely by `conversation()` under the minted proof (R-007).
|
|
112
|
+
*/
|
|
113
|
+
conversationMetadata(conversationId: string): Promise<OutlookMessage[]>;
|
|
114
|
+
/**
|
|
115
|
+
* Exactly one conversation's messages (R-007). This method refuses to ask
|
|
116
|
+
* for more than one identifier — a fixture observes that no request ever
|
|
117
|
+
* carries one. Bodies arrive as text (the Prefer header asks Graph for the
|
|
118
|
+
* text twin); an HTML body is dropped, never stored. Attachment resources
|
|
119
|
+
* are never fetched — there is no method for them (D-003).
|
|
120
|
+
*/
|
|
121
|
+
conversation(conversationId: string): Promise<OutlookMessage[]>;
|
|
122
|
+
private parseMessage;
|
|
123
|
+
}
|
|
124
|
+
export {};
|