@hcu-lab.me/mcp-device 1.0.9 → 1.0.12
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/PRIVACY.md +21 -0
- package/README.md +58 -3
- package/SECURITY.md +67 -0
- package/config/revoked-approvals.json +1 -0
- package/control-plane/config/mcp-servers.example.toml +52 -0
- package/control-plane/config/mcp-servers.toml +52 -0
- package/control-plane/contracts/device-public-contract-v1.json +733 -0
- package/control-plane/scripts/GATEWAY-LICENSE.txt +7 -0
- package/control-plane/scripts/account-http.mjs +220 -0
- package/control-plane/scripts/account-store.mjs +368 -0
- package/control-plane/scripts/admin.mjs +68 -0
- package/control-plane/scripts/auth-session.mjs +411 -0
- package/control-plane/scripts/authenticated-mcp-wrapper.mjs +1281 -0
- package/control-plane/scripts/benchmark-command.mjs +9 -0
- package/control-plane/scripts/benchmark-mcp-catalog.mjs +174 -0
- package/control-plane/scripts/catalog-budget.mjs +87 -0
- package/control-plane/scripts/check-device-public-contract.mjs +20 -0
- package/control-plane/scripts/custom-tools/index.mjs +221 -0
- package/control-plane/scripts/custom-tools/path-utils.mjs +117 -0
- package/control-plane/scripts/custom-tools/response-utils.mjs +34 -0
- package/control-plane/scripts/dashboard-http.mjs +598 -0
- package/control-plane/scripts/device-access-policy.mjs +205 -0
- package/control-plane/scripts/device-admin.mjs +43 -0
- package/control-plane/scripts/device-audit.mjs +63 -0
- package/control-plane/scripts/device-broker.mjs +1461 -0
- package/control-plane/scripts/device-execution-tool-definitions.mjs +217 -0
- package/control-plane/scripts/device-inventory.mjs +128 -0
- package/control-plane/scripts/device-pairing-http.mjs +109 -0
- package/control-plane/scripts/device-pairing-store.mjs +245 -0
- package/control-plane/scripts/device-public-contract.mjs +165 -0
- package/control-plane/scripts/device-release.mjs +95 -0
- package/control-plane/scripts/device-secure-transport.mjs +128 -0
- package/control-plane/scripts/device-store.mjs +283 -0
- package/control-plane/scripts/device-usage.mjs +657 -0
- package/control-plane/scripts/embedded-device.mjs +149 -0
- package/control-plane/scripts/enduser-ui.mjs +45 -0
- package/control-plane/scripts/external-tool-broker.mjs +179 -0
- package/control-plane/scripts/oauth-authorization-server.mjs +589 -0
- package/control-plane/scripts/operator-dashboard-http.mjs +81 -0
- package/control-plane/scripts/project-inspection.mjs +227 -0
- package/control-plane/scripts/projects/project-id.mjs +69 -0
- package/control-plane/scripts/projects/trusted-roots-projects.mjs +375 -0
- package/control-plane/scripts/prompts/index.mjs +60 -0
- package/control-plane/scripts/remote-process-sessions.mjs +78 -0
- package/control-plane/scripts/remote-tool-result.mjs +9 -0
- package/control-plane/scripts/resources/index.mjs +166 -0
- package/control-plane/scripts/runtime-profile.mjs +54 -0
- package/control-plane/scripts/shell-tool-descriptor.mjs +17 -0
- package/control-plane/scripts/skill-source-safety.mjs +46 -0
- package/control-plane/scripts/skills/README.md +70 -0
- package/control-plane/scripts/skills/_upstream_licenses/anthropic-THIRD_PARTY_NOTICES.md +405 -0
- package/control-plane/scripts/skills/_upstream_licenses/hallmark-LICENSE.txt +21 -0
- package/control-plane/scripts/skills/_upstream_licenses/ponytail-LICENSE.txt +21 -0
- package/control-plane/scripts/skills/_upstream_licenses/stitch-skills-LICENSE.txt +201 -0
- package/control-plane/scripts/skills/_upstream_licenses/superpowers-LICENSE.txt +21 -0
- package/control-plane/scripts/skills/algorithmic-art/LICENSE.txt +202 -0
- package/control-plane/scripts/skills/algorithmic-art/SKILL.md +405 -0
- package/control-plane/scripts/skills/algorithmic-art/templates/generator_template.js +223 -0
- package/control-plane/scripts/skills/algorithmic-art/templates/viewer.html +599 -0
- package/control-plane/scripts/skills/brainstorming/SKILL.md +285 -0
- package/control-plane/scripts/skills/brainstorming/scripts/frame-template.html +213 -0
- package/control-plane/scripts/skills/brainstorming/scripts/helper.js +167 -0
- package/control-plane/scripts/skills/brainstorming/scripts/server.cjs +723 -0
- package/control-plane/scripts/skills/brainstorming/scripts/start-server.sh +209 -0
- package/control-plane/scripts/skills/brainstorming/scripts/stop-server.sh +120 -0
- package/control-plane/scripts/skills/brainstorming/spec-document-reviewer-prompt.md +49 -0
- package/control-plane/scripts/skills/brainstorming/visual-companion.md +299 -0
- package/control-plane/scripts/skills/brand-guidelines/LICENSE.txt +202 -0
- package/control-plane/scripts/skills/brand-guidelines/SKILL.md +73 -0
- package/control-plane/scripts/skills/claude-api/LICENSE.txt +202 -0
- package/control-plane/scripts/skills/claude-api/SKILL.md +570 -0
- package/control-plane/scripts/skills/claude-api/csharp/claude-api/README.md +362 -0
- package/control-plane/scripts/skills/claude-api/csharp/claude-api/batches.md +14 -0
- package/control-plane/scripts/skills/claude-api/csharp/claude-api/files-api.md +25 -0
- package/control-plane/scripts/skills/claude-api/csharp/claude-api/streaming.md +28 -0
- package/control-plane/scripts/skills/claude-api/csharp/claude-api/tool-use.md +164 -0
- package/control-plane/scripts/skills/claude-api/curl/examples.md +256 -0
- package/control-plane/scripts/skills/claude-api/curl/managed-agents.md +371 -0
- package/control-plane/scripts/skills/claude-api/go/claude-api/README.md +188 -0
- package/control-plane/scripts/skills/claude-api/go/claude-api/files-api.md +23 -0
- package/control-plane/scripts/skills/claude-api/go/claude-api/streaming.md +43 -0
- package/control-plane/scripts/skills/claude-api/go/claude-api/tool-use.md +220 -0
- package/control-plane/scripts/skills/claude-api/go/managed-agents/README.md +564 -0
- package/control-plane/scripts/skills/claude-api/java/claude-api/README.md +238 -0
- package/control-plane/scripts/skills/claude-api/java/claude-api/files-api.md +27 -0
- package/control-plane/scripts/skills/claude-api/java/claude-api/streaming.md +24 -0
- package/control-plane/scripts/skills/claude-api/java/claude-api/tool-use.md +228 -0
- package/control-plane/scripts/skills/claude-api/java/managed-agents/README.md +443 -0
- package/control-plane/scripts/skills/claude-api/php/claude-api/README.md +174 -0
- package/control-plane/scripts/skills/claude-api/php/claude-api/batches.md +15 -0
- package/control-plane/scripts/skills/claude-api/php/claude-api/files-api.md +13 -0
- package/control-plane/scripts/skills/claude-api/php/claude-api/streaming.md +27 -0
- package/control-plane/scripts/skills/claude-api/php/claude-api/tool-use.md +253 -0
- package/control-plane/scripts/skills/claude-api/php/managed-agents/README.md +443 -0
- package/control-plane/scripts/skills/claude-api/python/claude-api/README.md +565 -0
- package/control-plane/scripts/skills/claude-api/python/claude-api/batches.md +198 -0
- package/control-plane/scripts/skills/claude-api/python/claude-api/files-api.md +170 -0
- package/control-plane/scripts/skills/claude-api/python/claude-api/sdk-upgrade.md +286 -0
- package/control-plane/scripts/skills/claude-api/python/claude-api/streaming.md +179 -0
- package/control-plane/scripts/skills/claude-api/python/claude-api/tool-use.md +627 -0
- package/control-plane/scripts/skills/claude-api/python/managed-agents/README.md +335 -0
- package/control-plane/scripts/skills/claude-api/ruby/claude-api/README.md +134 -0
- package/control-plane/scripts/skills/claude-api/ruby/claude-api/streaming.md +16 -0
- package/control-plane/scripts/skills/claude-api/ruby/claude-api/tool-use.md +41 -0
- package/control-plane/scripts/skills/claude-api/ruby/managed-agents/README.md +394 -0
- package/control-plane/scripts/skills/claude-api/shared/admin-api.md +179 -0
- package/control-plane/scripts/skills/claude-api/shared/agent-design.md +101 -0
- package/control-plane/scripts/skills/claude-api/shared/anthropic-cli.md +269 -0
- package/control-plane/scripts/skills/claude-api/shared/claude-platform-on-aws.md +59 -0
- package/control-plane/scripts/skills/claude-api/shared/cost-optimization.md +233 -0
- package/control-plane/scripts/skills/claude-api/shared/error-codes.md +262 -0
- package/control-plane/scripts/skills/claude-api/shared/live-sources.md +166 -0
- package/control-plane/scripts/skills/claude-api/shared/managed-agents-api-reference.md +455 -0
- package/control-plane/scripts/skills/claude-api/shared/managed-agents-client-patterns.md +220 -0
- package/control-plane/scripts/skills/claude-api/shared/managed-agents-core.md +359 -0
- package/control-plane/scripts/skills/claude-api/shared/managed-agents-environments.md +226 -0
- package/control-plane/scripts/skills/claude-api/shared/managed-agents-events.md +290 -0
- package/control-plane/scripts/skills/claude-api/shared/managed-agents-memory.md +201 -0
- package/control-plane/scripts/skills/claude-api/shared/managed-agents-multiagent.md +260 -0
- package/control-plane/scripts/skills/claude-api/shared/managed-agents-onboarding.md +82 -0
- package/control-plane/scripts/skills/claude-api/shared/managed-agents-outcomes.md +108 -0
- package/control-plane/scripts/skills/claude-api/shared/managed-agents-overview.md +77 -0
- package/control-plane/scripts/skills/claude-api/shared/managed-agents-scheduled-deployments.md +156 -0
- package/control-plane/scripts/skills/claude-api/shared/managed-agents-self-hosted-sandboxes.md +282 -0
- package/control-plane/scripts/skills/claude-api/shared/managed-agents-tools.md +504 -0
- package/control-plane/scripts/skills/claude-api/shared/managed-agents-webhooks.md +143 -0
- package/control-plane/scripts/skills/claude-api/shared/model-migration.md +1872 -0
- package/control-plane/scripts/skills/claude-api/shared/models.md +142 -0
- package/control-plane/scripts/skills/claude-api/shared/platform-availability.md +52 -0
- package/control-plane/scripts/skills/claude-api/shared/prompt-audit.md +223 -0
- package/control-plane/scripts/skills/claude-api/shared/prompt-caching.md +292 -0
- package/control-plane/scripts/skills/claude-api/shared/token-counting.md +56 -0
- package/control-plane/scripts/skills/claude-api/shared/tool-use-concepts.md +526 -0
- package/control-plane/scripts/skills/claude-api/typescript/claude-api/README.md +405 -0
- package/control-plane/scripts/skills/claude-api/typescript/claude-api/batches.md +106 -0
- package/control-plane/scripts/skills/claude-api/typescript/claude-api/files-api.md +98 -0
- package/control-plane/scripts/skills/claude-api/typescript/claude-api/streaming.md +178 -0
- package/control-plane/scripts/skills/claude-api/typescript/claude-api/tool-use.md +586 -0
- package/control-plane/scripts/skills/claude-api/typescript/managed-agents/README.md +359 -0
- package/control-plane/scripts/skills/dispatching-parallel-agents/SKILL.md +167 -0
- package/control-plane/scripts/skills/enhance-prompt/README.md +34 -0
- package/control-plane/scripts/skills/enhance-prompt/SKILL.md +204 -0
- package/control-plane/scripts/skills/enhance-prompt/references/KEYWORDS.md +114 -0
- package/control-plane/scripts/skills/executing-plans/SKILL.md +371 -0
- package/control-plane/scripts/skills/executing-plans/scripts/task-done +52 -0
- package/control-plane/scripts/skills/executing-plans/scripts/task-start +28 -0
- package/control-plane/scripts/skills/extract-design-md/SKILL.md +358 -0
- package/control-plane/scripts/skills/extract-design-md/examples/DESIGN.md +210 -0
- package/control-plane/scripts/skills/extract-design-md/references/angular.md +122 -0
- package/control-plane/scripts/skills/extract-design-md/references/plain-css.md +161 -0
- package/control-plane/scripts/skills/extract-design-md/references/react-tailwind.md +157 -0
- package/control-plane/scripts/skills/extract-design-md/references/svelte.md +101 -0
- package/control-plane/scripts/skills/extract-design-md/references/vue.md +107 -0
- package/control-plane/scripts/skills/finishing-a-development-branch/SKILL.md +225 -0
- package/control-plane/scripts/skills/frontend-design/LICENSE.txt +177 -0
- package/control-plane/scripts/skills/frontend-design/SKILL.md +71 -0
- package/control-plane/scripts/skills/hallmark/SKILL.md +558 -0
- package/control-plane/scripts/skills/hallmark/references/anti-patterns.md +418 -0
- package/control-plane/scripts/skills/hallmark/references/assets.md +406 -0
- package/control-plane/scripts/skills/hallmark/references/color.md +95 -0
- package/control-plane/scripts/skills/hallmark/references/component-cookbook.md +263 -0
- package/control-plane/scripts/skills/hallmark/references/components/c1-outlined-chip.md +12 -0
- package/control-plane/scripts/skills/hallmark/references/components/c2-inline-form-as-cta.md +16 -0
- package/control-plane/scripts/skills/hallmark/references/components/c3-typographic-link.md +8 -0
- package/control-plane/scripts/skills/hallmark/references/components/c4-sticky-bottom-bar.md +16 -0
- package/control-plane/scripts/skills/hallmark/references/components/f1-bento-grid.md +20 -0
- package/control-plane/scripts/skills/hallmark/references/components/f2-sticky-scroll-stack.md +20 -0
- package/control-plane/scripts/skills/hallmark/references/components/f3-tabular-spec-sheet.md +11 -0
- package/control-plane/scripts/skills/hallmark/references/components/f4-step-sequence.md +11 -0
- package/control-plane/scripts/skills/hallmark/references/components/f5-annotated-screenshot.md +11 -0
- package/control-plane/scripts/skills/hallmark/references/components/f6-product-card-grid.md +41 -0
- package/control-plane/scripts/skills/hallmark/references/components/ft1-mast-headed.md +13 -0
- package/control-plane/scripts/skills/hallmark/references/components/ft2-inline-rule-single-line.md +10 -0
- package/control-plane/scripts/skills/hallmark/references/components/ft3-index-style-category-list.md +12 -0
- package/control-plane/scripts/skills/hallmark/references/components/ft4-dense-typographic.md +10 -0
- package/control-plane/scripts/skills/hallmark/references/components/ft5-statement.md +21 -0
- package/control-plane/scripts/skills/hallmark/references/components/ft6-letter-close.md +19 -0
- package/control-plane/scripts/skills/hallmark/references/components/ft7-newsletter-first.md +27 -0
- package/control-plane/scripts/skills/hallmark/references/components/ft8-marquee-scroll.md +25 -0
- package/control-plane/scripts/skills/hallmark/references/components/h1-marquee.md +15 -0
- package/control-plane/scripts/skills/hallmark/references/components/h2-split-diptych.md +15 -0
- package/control-plane/scripts/skills/hallmark/references/components/h3-quote-led.md +11 -0
- package/control-plane/scripts/skills/hallmark/references/components/h4-stat-led.md +14 -0
- package/control-plane/scripts/skills/hallmark/references/components/h5-letter-hero.md +11 -0
- package/control-plane/scripts/skills/hallmark/references/components/h6-photographic-fold.md +16 -0
- package/control-plane/scripts/skills/hallmark/references/components/h7-demo-video-clipped-by-viewport-edge.md +27 -0
- package/control-plane/scripts/skills/hallmark/references/components/h8-mockup-split-browser-framed.md +23 -0
- package/control-plane/scripts/skills/hallmark/references/components/h9-custom-illustration-centerpiece.md +27 -0
- package/control-plane/scripts/skills/hallmark/references/components/n1-wordmark-2-links.md +12 -0
- package/control-plane/scripts/skills/hallmark/references/components/n10-floating-on-scroll-morph.md +19 -0
- package/control-plane/scripts/skills/hallmark/references/components/n11-mega-menu.md +40 -0
- package/control-plane/scripts/skills/hallmark/references/components/n12-banner-retract.md +34 -0
- package/control-plane/scripts/skills/hallmark/references/components/n13-inline-cmdk-pill.md +39 -0
- package/control-plane/scripts/skills/hallmark/references/components/n1b-saas-three-section.md +35 -0
- package/control-plane/scripts/skills/hallmark/references/components/n2-floating-chip.md +14 -0
- package/control-plane/scripts/skills/hallmark/references/components/n3-side-rail.md +14 -0
- package/control-plane/scripts/skills/hallmark/references/components/n4-hidden-behind-k.md +9 -0
- package/control-plane/scripts/skills/hallmark/references/components/n5-floating-pill.md +28 -0
- package/control-plane/scripts/skills/hallmark/references/components/n6-newspaper-masthead.md +24 -0
- package/control-plane/scripts/skills/hallmark/references/components/n7-brutal-slab.md +22 -0
- package/control-plane/scripts/skills/hallmark/references/components/n8-terminal-command.md +21 -0
- package/control-plane/scripts/skills/hallmark/references/components/n9-edge-aligned-minimal.md +17 -0
- package/control-plane/scripts/skills/hallmark/references/components/s1-left-margin-numbered.md +15 -0
- package/control-plane/scripts/skills/hallmark/references/components/s2-hanging.md +13 -0
- package/control-plane/scripts/skills/hallmark/references/components/s3-sticky-pinned.md +19 -0
- package/control-plane/scripts/skills/hallmark/references/components/s4-inline-no-break.md +11 -0
- package/control-plane/scripts/skills/hallmark/references/components/s5-bottom-anchored.md +13 -0
- package/control-plane/scripts/skills/hallmark/references/components/t1-pull-quote-with-marginalia.md +12 -0
- package/control-plane/scripts/skills/hallmark/references/components/t2-logo-wall-hairline.md +19 -0
- package/control-plane/scripts/skills/hallmark/references/components/t3-single-huge-quote.md +11 -0
- package/control-plane/scripts/skills/hallmark/references/components/t4-numbered-stat-strip.md +14 -0
- package/control-plane/scripts/skills/hallmark/references/contract.md +25 -0
- package/control-plane/scripts/skills/hallmark/references/copy.md +182 -0
- package/control-plane/scripts/skills/hallmark/references/custom-craft.md +626 -0
- package/control-plane/scripts/skills/hallmark/references/custom-theme.md +367 -0
- package/control-plane/scripts/skills/hallmark/references/design-md.md +116 -0
- package/control-plane/scripts/skills/hallmark/references/export-formats.md +329 -0
- package/control-plane/scripts/skills/hallmark/references/floating-nav.md +89 -0
- package/control-plane/scripts/skills/hallmark/references/genres/atmospheric.md +69 -0
- package/control-plane/scripts/skills/hallmark/references/genres/editorial.md +72 -0
- package/control-plane/scripts/skills/hallmark/references/genres/modern-minimal.md +71 -0
- package/control-plane/scripts/skills/hallmark/references/genres/playful.md +70 -0
- package/control-plane/scripts/skills/hallmark/references/hero-enrichment.md +475 -0
- package/control-plane/scripts/skills/hallmark/references/imagery-kit.md +170 -0
- package/control-plane/scripts/skills/hallmark/references/interaction-and-states.md +208 -0
- package/control-plane/scripts/skills/hallmark/references/layout-and-space.md +112 -0
- package/control-plane/scripts/skills/hallmark/references/macrostructures/01-bento-grid.md +35 -0
- package/control-plane/scripts/skills/hallmark/references/macrostructures/02-long-document.md +34 -0
- package/control-plane/scripts/skills/hallmark/references/macrostructures/03-marquee-hero.md +31 -0
- package/control-plane/scripts/skills/hallmark/references/macrostructures/04-stat-led.md +34 -0
- package/control-plane/scripts/skills/hallmark/references/macrostructures/05-workbench.md +32 -0
- package/control-plane/scripts/skills/hallmark/references/macrostructures/06-conversational-faq.md +33 -0
- package/control-plane/scripts/skills/hallmark/references/macrostructures/07-manifesto.md +32 -0
- package/control-plane/scripts/skills/hallmark/references/macrostructures/08-photographic.md +34 -0
- package/control-plane/scripts/skills/hallmark/references/macrostructures/09-quote-led.md +32 -0
- package/control-plane/scripts/skills/hallmark/references/macrostructures/10-specimen.md +32 -0
- package/control-plane/scripts/skills/hallmark/references/macrostructures/11-catalogue.md +23 -0
- package/control-plane/scripts/skills/hallmark/references/macrostructures/12-letter.md +23 -0
- package/control-plane/scripts/skills/hallmark/references/macrostructures/13-index-first.md +23 -0
- package/control-plane/scripts/skills/hallmark/references/macrostructures/14-narrative-workflow.md +23 -0
- package/control-plane/scripts/skills/hallmark/references/macrostructures/15-split-studio.md +23 -0
- package/control-plane/scripts/skills/hallmark/references/macrostructures/16-feature-stack.md +23 -0
- package/control-plane/scripts/skills/hallmark/references/macrostructures/17-type-specimen.md +23 -0
- package/control-plane/scripts/skills/hallmark/references/macrostructures/18-portfolio-grid.md +23 -0
- package/control-plane/scripts/skills/hallmark/references/macrostructures/19-map-diagram.md +23 -0
- package/control-plane/scripts/skills/hallmark/references/macrostructures/20-ecosystem-index.md +23 -0
- package/control-plane/scripts/skills/hallmark/references/macrostructures/21-component-playground.md +23 -0
- package/control-plane/scripts/skills/hallmark/references/macrostructures.md +89 -0
- package/control-plane/scripts/skills/hallmark/references/microinteractions.md +258 -0
- package/control-plane/scripts/skills/hallmark/references/motion.md +109 -0
- package/control-plane/scripts/skills/hallmark/references/preview-examples.md +49 -0
- package/control-plane/scripts/skills/hallmark/references/responsive.md +138 -0
- package/control-plane/scripts/skills/hallmark/references/slop-test.md +192 -0
- package/control-plane/scripts/skills/hallmark/references/structure.md +160 -0
- package/control-plane/scripts/skills/hallmark/references/study.md +509 -0
- package/control-plane/scripts/skills/hallmark/references/themes/carnival.md +301 -0
- package/control-plane/scripts/skills/hallmark/references/themes/cobalt.md +146 -0
- package/control-plane/scripts/skills/hallmark/references/themes/grid.md +161 -0
- package/control-plane/scripts/skills/hallmark/references/themes/hum.md +403 -0
- package/control-plane/scripts/skills/hallmark/references/themes/lumen.md +478 -0
- package/control-plane/scripts/skills/hallmark/references/typography.md +243 -0
- package/control-plane/scripts/skills/hallmark/references/upstream/recipes.md +186 -0
- package/control-plane/scripts/skills/hallmark/references/upstream/study-examples.md +176 -0
- package/control-plane/scripts/skills/hallmark/references/upstream/tokens.css +1221 -0
- package/control-plane/scripts/skills/hallmark/references/verbs/audit.md +25 -0
- package/control-plane/scripts/skills/hallmark/references/verbs/redesign.md +269 -0
- package/control-plane/scripts/skills/human-comms/SKILL.md +29 -0
- package/control-plane/scripts/skills/index.mjs +432 -0
- package/control-plane/scripts/skills/internal-comms/LICENSE.txt +202 -0
- package/control-plane/scripts/skills/internal-comms/SKILL.md +32 -0
- package/control-plane/scripts/skills/internal-comms/examples/3p-updates.md +47 -0
- package/control-plane/scripts/skills/internal-comms/examples/company-newsletter.md +65 -0
- package/control-plane/scripts/skills/internal-comms/examples/faq-answers.md +30 -0
- package/control-plane/scripts/skills/internal-comms/examples/general-comms.md +16 -0
- package/control-plane/scripts/skills/mcp-adapter.mjs +94 -0
- package/control-plane/scripts/skills/mcp-builder/LICENSE.txt +202 -0
- package/control-plane/scripts/skills/mcp-builder/SKILL.md +236 -0
- package/control-plane/scripts/skills/mcp-builder/reference/evaluation.md +602 -0
- package/control-plane/scripts/skills/mcp-builder/reference/mcp_best_practices.md +249 -0
- package/control-plane/scripts/skills/mcp-builder/reference/node_mcp_server.md +970 -0
- package/control-plane/scripts/skills/mcp-builder/reference/python_mcp_server.md +719 -0
- package/control-plane/scripts/skills/mcp-builder/scripts/connections.py +151 -0
- package/control-plane/scripts/skills/mcp-builder/scripts/evaluation.py +373 -0
- package/control-plane/scripts/skills/mcp-builder/scripts/example_evaluation.xml +22 -0
- package/control-plane/scripts/skills/mcp-builder/scripts/requirements.txt +2 -0
- package/control-plane/scripts/skills/ponytail/SKILL.md +120 -0
- package/control-plane/scripts/skills/ponytail-audit/SKILL.md +41 -0
- package/control-plane/scripts/skills/ponytail-debt/SKILL.md +44 -0
- package/control-plane/scripts/skills/ponytail-gain/SKILL.md +50 -0
- package/control-plane/scripts/skills/ponytail-help/SKILL.md +71 -0
- package/control-plane/scripts/skills/ponytail-review/SKILL.md +57 -0
- package/control-plane/scripts/skills/receiving-code-review/SKILL.md +205 -0
- package/control-plane/scripts/skills/requesting-code-review/SKILL.md +95 -0
- package/control-plane/scripts/skills/requesting-code-review/code-reviewer.md +198 -0
- package/control-plane/scripts/skills/skill-creator/LICENSE.txt +202 -0
- package/control-plane/scripts/skills/skill-creator/SKILL.md +485 -0
- package/control-plane/scripts/skills/skill-creator/agents/analyzer.md +274 -0
- package/control-plane/scripts/skills/skill-creator/agents/comparator.md +202 -0
- package/control-plane/scripts/skills/skill-creator/agents/grader.md +223 -0
- package/control-plane/scripts/skills/skill-creator/assets/eval_review.html +146 -0
- package/control-plane/scripts/skills/skill-creator/eval-viewer/generate_review.py +471 -0
- package/control-plane/scripts/skills/skill-creator/eval-viewer/viewer.html +1325 -0
- package/control-plane/scripts/skills/skill-creator/references/schemas.md +430 -0
- package/control-plane/scripts/skills/skill-creator/scripts/__init__.py +0 -0
- package/control-plane/scripts/skills/skill-creator/scripts/aggregate_benchmark.py +401 -0
- package/control-plane/scripts/skills/skill-creator/scripts/generate_report.py +326 -0
- package/control-plane/scripts/skills/skill-creator/scripts/improve_description.py +247 -0
- package/control-plane/scripts/skills/skill-creator/scripts/package_skill.py +136 -0
- package/control-plane/scripts/skills/skill-creator/scripts/quick_validate.py +103 -0
- package/control-plane/scripts/skills/skill-creator/scripts/run_eval.py +310 -0
- package/control-plane/scripts/skills/skill-creator/scripts/run_loop.py +328 -0
- package/control-plane/scripts/skills/skill-creator/scripts/utils.py +47 -0
- package/control-plane/scripts/skills/slack-gif-creator/LICENSE.txt +202 -0
- package/control-plane/scripts/skills/slack-gif-creator/SKILL.md +254 -0
- package/control-plane/scripts/skills/slack-gif-creator/core/easing.py +234 -0
- package/control-plane/scripts/skills/slack-gif-creator/core/frame_composer.py +176 -0
- package/control-plane/scripts/skills/slack-gif-creator/core/gif_builder.py +269 -0
- package/control-plane/scripts/skills/slack-gif-creator/core/validators.py +136 -0
- package/control-plane/scripts/skills/slack-gif-creator/requirements.txt +4 -0
- package/control-plane/scripts/skills/sources.json +401 -0
- package/control-plane/scripts/skills/sources.lock.json +416 -0
- package/control-plane/scripts/skills/subagent-driven-development/SKILL.md +570 -0
- package/control-plane/scripts/skills/subagent-driven-development/implementer-prompt.md +154 -0
- package/control-plane/scripts/skills/subagent-driven-development/re-review-prompt.md +115 -0
- package/control-plane/scripts/skills/subagent-driven-development/scripts/review-package +53 -0
- package/control-plane/scripts/skills/subagent-driven-development/scripts/sdd-workspace +82 -0
- package/control-plane/scripts/skills/subagent-driven-development/scripts/task-brief +43 -0
- package/control-plane/scripts/skills/subagent-driven-development/task-reviewer-prompt.md +207 -0
- package/control-plane/scripts/skills/systematic-debugging/CREATION-LOG.md +119 -0
- package/control-plane/scripts/skills/systematic-debugging/SKILL.md +283 -0
- package/control-plane/scripts/skills/systematic-debugging/condition-based-waiting-example.ts +158 -0
- package/control-plane/scripts/skills/systematic-debugging/condition-based-waiting.md +115 -0
- package/control-plane/scripts/skills/systematic-debugging/defense-in-depth.md +122 -0
- package/control-plane/scripts/skills/systematic-debugging/find-polluter.sh +72 -0
- package/control-plane/scripts/skills/systematic-debugging/root-cause-tracing.md +169 -0
- package/control-plane/scripts/skills/systematic-debugging/test-academic.md +14 -0
- package/control-plane/scripts/skills/systematic-debugging/test-pressure-1.md +58 -0
- package/control-plane/scripts/skills/systematic-debugging/test-pressure-2.md +68 -0
- package/control-plane/scripts/skills/systematic-debugging/test-pressure-3.md +69 -0
- package/control-plane/scripts/skills/test-driven-development/SKILL.md +330 -0
- package/control-plane/scripts/skills/test-driven-development/writing-good-tests.md +198 -0
- package/control-plane/scripts/skills/theme-factory/LICENSE.txt +202 -0
- package/control-plane/scripts/skills/theme-factory/SKILL.md +59 -0
- package/control-plane/scripts/skills/theme-factory/theme-showcase.pdf +0 -0
- package/control-plane/scripts/skills/theme-factory/themes/arctic-frost.md +19 -0
- package/control-plane/scripts/skills/theme-factory/themes/botanical-garden.md +19 -0
- package/control-plane/scripts/skills/theme-factory/themes/desert-rose.md +19 -0
- package/control-plane/scripts/skills/theme-factory/themes/forest-canopy.md +19 -0
- package/control-plane/scripts/skills/theme-factory/themes/golden-hour.md +19 -0
- package/control-plane/scripts/skills/theme-factory/themes/midnight-galaxy.md +19 -0
- package/control-plane/scripts/skills/theme-factory/themes/modern-minimalist.md +19 -0
- package/control-plane/scripts/skills/theme-factory/themes/ocean-depths.md +19 -0
- package/control-plane/scripts/skills/theme-factory/themes/sunset-boulevard.md +19 -0
- package/control-plane/scripts/skills/theme-factory/themes/tech-innovation.md +19 -0
- package/control-plane/scripts/skills/using-git-worktrees/SKILL.md +167 -0
- package/control-plane/scripts/skills/verification-before-completion/SKILL.md +120 -0
- package/control-plane/scripts/skills/web-artifacts-builder/LICENSE.txt +202 -0
- package/control-plane/scripts/skills/web-artifacts-builder/SKILL.md +74 -0
- package/control-plane/scripts/skills/web-artifacts-builder/scripts/bundle-artifact.sh +54 -0
- package/control-plane/scripts/skills/web-artifacts-builder/scripts/init-artifact.sh +322 -0
- package/control-plane/scripts/skills/web-artifacts-builder/scripts/shadcn-components.tar.gz +0 -0
- package/control-plane/scripts/skills/webapp-testing/LICENSE.txt +202 -0
- package/control-plane/scripts/skills/webapp-testing/SKILL.md +96 -0
- package/control-plane/scripts/skills/webapp-testing/examples/console_logging.py +35 -0
- package/control-plane/scripts/skills/webapp-testing/examples/element_discovery.py +40 -0
- package/control-plane/scripts/skills/webapp-testing/examples/static_html_automation.py +33 -0
- package/control-plane/scripts/skills/webapp-testing/scripts/with_server.py +106 -0
- package/control-plane/scripts/skills/writing-plans/SKILL.md +192 -0
- package/control-plane/scripts/skills/writing-plans/plan-document-reviewer-prompt.md +49 -0
- package/control-plane/scripts/skills/writing-skills/SKILL.md +681 -0
- package/control-plane/scripts/skills/writing-skills/anthropic-best-practices.md +1150 -0
- package/control-plane/scripts/skills/writing-skills/examples/CLAUDE_MD_TESTING.md +189 -0
- package/control-plane/scripts/skills/writing-skills/graphviz-conventions.dot +172 -0
- package/control-plane/scripts/skills/writing-skills/persuasion-principles.md +187 -0
- package/control-plane/scripts/skills/writing-skills/render-graphs.js +169 -0
- package/control-plane/scripts/skills/writing-skills/testing-skills-with-subagents.md +384 -0
- package/control-plane/scripts/smoke-mcp-schemas.mjs +30 -0
- package/control-plane/scripts/surface-config.mjs +45 -0
- package/control-plane/scripts/sync-skills.mjs +370 -0
- package/control-plane/scripts/tool-metrics.mjs +74 -0
- package/control-plane/scripts/tool-risk.mjs +156 -0
- package/control-plane/scripts/tool-surface-stability.mjs +38 -0
- package/control-plane/scripts/upstreams/catalog-cache.mjs +28 -0
- package/control-plane/scripts/upstreams/config.mjs +158 -0
- package/control-plane/scripts/upstreams/diagnostics.mjs +21 -0
- package/control-plane/scripts/upstreams/http-client.mjs +67 -0
- package/control-plane/scripts/upstreams/manager.mjs +535 -0
- package/control-plane/scripts/upstreams/names.mjs +55 -0
- package/control-plane/scripts/upstreams/presets.mjs +60 -0
- package/control-plane/scripts/upstreams/resource-uri.mjs +29 -0
- package/control-plane/scripts/upstreams/stdio-client.mjs +73 -0
- package/control-plane/scripts/workspace-registry.mjs +147 -0
- package/dist/context/authorization.d.ts +5 -0
- package/dist/context/authorization.js +70 -0
- package/dist/context/capture.d.ts +46 -0
- package/dist/context/capture.js +187 -0
- package/dist/context/cli.d.ts +47 -0
- package/dist/context/cli.js +287 -0
- package/dist/context/device-adapter.d.ts +102 -0
- package/dist/context/device-adapter.js +79 -0
- package/dist/context/gateway.d.ts +354 -0
- package/dist/context/gateway.js +50 -0
- package/dist/context/graph.d.ts +63 -0
- package/dist/context/graph.js +214 -0
- package/dist/context/importer.d.ts +8 -0
- package/dist/context/importer.js +69 -0
- package/dist/context/indexer.d.ts +14 -0
- package/dist/context/indexer.js +57 -0
- package/dist/context/migrations/index.d.ts +4 -0
- package/dist/context/migrations/index.js +87 -0
- package/dist/context/redact.d.ts +15 -0
- package/dist/context/redact.js +43 -0
- package/dist/context/repositories.d.ts +9 -0
- package/dist/context/repositories.js +45 -0
- package/dist/context/search.d.ts +63 -0
- package/dist/context/search.js +207 -0
- package/dist/context/service.d.ts +278 -0
- package/dist/context/service.js +160 -0
- package/dist/context/store.d.ts +134 -0
- package/dist/context/store.js +722 -0
- package/dist/context/text.d.ts +4 -0
- package/dist/context/text.js +14 -0
- package/dist/context/tool-contract.d.ts +534 -0
- package/dist/context/tool-contract.js +45 -0
- package/dist/device/core.d.ts +29 -0
- package/dist/device/core.js +66 -0
- package/dist/device/device.d.ts +5 -0
- package/dist/device/device.js +94 -37
- package/dist/device/gateway-channel.d.ts +8 -1
- package/dist/device/gateway-channel.js +51 -3
- package/dist/device/gateway-tool-adapter.d.ts +11 -3
- package/dist/device/gateway-tool-adapter.js +141 -98
- package/dist/device/official-trust.d.ts +12 -0
- package/dist/device/official-trust.js +25 -1
- package/dist/device/runtime-owner.js +9 -1
- package/dist/distribution.d.ts +4 -0
- package/dist/distribution.js +8 -0
- package/dist/handlers/filesystem-handlers.js +53 -37
- package/dist/handlers/search-handlers.d.ts +1 -0
- package/dist/handlers/search-handlers.js +51 -1
- package/dist/log-palace/cli.d.ts +7 -0
- package/dist/log-palace/cli.js +89 -0
- package/dist/log-palace/formats.d.ts +26 -0
- package/dist/log-palace/formats.js +126 -0
- package/dist/log-palace/store.d.ts +129 -0
- package/dist/log-palace/store.js +856 -0
- package/dist/mcp-device.d.ts +1 -1
- package/dist/mcp-device.js +28 -6
- package/dist/npm-scripts/remote.js +1 -0
- package/dist/platform/activity-journal.d.ts +16 -0
- package/dist/platform/activity-journal.js +68 -0
- package/dist/platform/cli.d.ts +1 -0
- package/dist/platform/cli.js +300 -0
- package/dist/platform/coordination-lock.d.ts +45 -0
- package/dist/platform/coordination-lock.js +233 -0
- package/dist/platform/device-runtime.d.ts +16 -0
- package/dist/platform/device-runtime.js +83 -0
- package/dist/platform/knowledge-contract.d.ts +46 -0
- package/dist/platform/knowledge-contract.js +1 -0
- package/dist/platform/knowledge-mcp.d.ts +29 -0
- package/dist/platform/knowledge-mcp.js +95 -0
- package/dist/platform/knowledge-router.d.ts +64 -0
- package/dist/platform/knowledge-router.js +320 -0
- package/dist/platform/plugin-child.d.ts +1 -0
- package/dist/platform/plugin-child.js +115 -0
- package/dist/platform/plugin-contract.d.ts +76 -0
- package/dist/platform/plugin-contract.js +53 -0
- package/dist/platform/plugin-driver.d.ts +8 -0
- package/dist/platform/plugin-driver.js +443 -0
- package/dist/platform/plugin-host.d.ts +88 -0
- package/dist/platform/plugin-host.js +817 -0
- package/dist/platform/plugin-policy.d.ts +3 -0
- package/dist/platform/plugin-policy.js +29 -0
- package/dist/platform/plugin-services.d.ts +14 -0
- package/dist/platform/plugin-services.js +32 -0
- package/dist/platform/process-identity.d.ts +9 -0
- package/dist/platform/process-identity.js +68 -0
- package/dist/platform/runtime-lease.d.ts +4 -0
- package/dist/platform/runtime-lease.js +58 -0
- package/dist/platform/runtime-root.d.ts +9 -0
- package/dist/platform/runtime-root.js +61 -0
- package/dist/platform/state-backup.d.ts +92 -0
- package/dist/platform/state-backup.js +399 -0
- package/dist/plugins/reference/plugin.d.ts +3 -0
- package/dist/plugins/reference/plugin.js +13 -0
- package/dist/search-manager.d.ts +6 -0
- package/dist/search-manager.js +123 -0
- package/dist/server.d.ts +13 -0
- package/dist/server.js +7 -3
- package/dist/terminal-manager.d.ts +16 -0
- package/dist/terminal-manager.js +137 -42
- package/dist/tool-dispatcher.d.ts +11 -0
- package/dist/tool-dispatcher.js +228 -0
- package/dist/tools/edit.js +72 -1
- package/dist/tools/filesystem.d.ts +1 -1
- package/dist/tools/filesystem.js +2 -2
- package/dist/tools/improved-process-tools.js +91 -108
- package/dist/tools/schemas.d.ts +94 -2
- package/dist/tools/schemas.js +20 -6
- package/dist/types.d.ts +8 -0
- package/dist/utils/output-budget.d.ts +6 -0
- package/dist/utils/output-budget.js +15 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +29 -6
- package/skills/mcp-device-context/SKILL.md +40 -0
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { ContextStore } from './store.js';
|
|
2
|
+
/** Explicit trusted-local import. Missing scope is never inferred from the active pairing. */
|
|
3
|
+
export declare function importLegacy(store: ContextStore, sourceFile: string): {
|
|
4
|
+
imported: number;
|
|
5
|
+
duplicates: number;
|
|
6
|
+
malformed: number;
|
|
7
|
+
quarantined: number;
|
|
8
|
+
};
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { readFileSync, statSync } from 'node:fs';
|
|
2
|
+
import { createHash, randomBytes } from 'node:crypto';
|
|
3
|
+
/** Explicit trusted-local import. Missing scope is never inferred from the active pairing. */
|
|
4
|
+
export function importLegacy(store, sourceFile) {
|
|
5
|
+
if (statSync(sourceFile).size > 64 * 1024 * 1024)
|
|
6
|
+
throw new Error('BUDGET_EXCEEDED');
|
|
7
|
+
const result = { imported: 0, duplicates: 0, malformed: 0, quarantined: 0 };
|
|
8
|
+
const quarantine = (line, reason) => {
|
|
9
|
+
const fingerprint = createHash('sha256').update(reason).update(store.redactPayload(line).text).digest('hex');
|
|
10
|
+
store.registry.prepare('INSERT OR IGNORE INTO import_quarantine VALUES(?,?,?)').run(fingerprint, reason, Date.now());
|
|
11
|
+
};
|
|
12
|
+
for (const line of readFileSync(sourceFile, 'utf8').split(/\r?\n/)) {
|
|
13
|
+
if (!line.trim())
|
|
14
|
+
continue;
|
|
15
|
+
let event;
|
|
16
|
+
try {
|
|
17
|
+
event = JSON.parse(line);
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
result.malformed++;
|
|
21
|
+
quarantine(line, 'malformed');
|
|
22
|
+
continue;
|
|
23
|
+
}
|
|
24
|
+
if (!event || typeof event !== 'object' || Array.isArray(event) ||
|
|
25
|
+
(typeof event.text !== 'string' && !(typeof event.toolName === 'string' && typeof event.timestamp === 'string' && event.output !== undefined))) {
|
|
26
|
+
result.malformed++;
|
|
27
|
+
quarantine(line, 'malformed');
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
const owner = event.owner_key, repo = event.repo_uuid;
|
|
31
|
+
if (typeof owner !== 'string' || typeof repo !== 'string' || owner !== store.activeOwner() ||
|
|
32
|
+
!store.registry.prepare('SELECT 1 FROM repositories WHERE owner_key=? AND repo_uuid=?').get(owner, repo)) {
|
|
33
|
+
result.quarantined++;
|
|
34
|
+
quarantine(line, 'unknown_scope');
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
37
|
+
const payload = typeof event.text === 'string' ? event.text : JSON.stringify({ toolName: event.toolName, arguments: event.arguments, output: event.output });
|
|
38
|
+
const path = typeof event.path === 'string' ? event.path : event.arguments?.path;
|
|
39
|
+
const safe = store.redactPayload(payload, { path: typeof path === 'string' ? path : undefined });
|
|
40
|
+
// Deduplication hashes retained redacted text only, never the raw secret-bearing payload.
|
|
41
|
+
const id = typeof event.id === 'string' || typeof event.id === 'number' ? store.redactPayload(String(event.id)).hash : safe.hash;
|
|
42
|
+
const db = store.repoDatabase(owner, repo);
|
|
43
|
+
store.quotaWrite(owner, repo, safe.retainedBytes * 4 + 65536, () => {
|
|
44
|
+
db.exec('BEGIN IMMEDIATE');
|
|
45
|
+
try {
|
|
46
|
+
const existing = db.prepare('SELECT evidence_ref FROM legacy_imports WHERE legacy_id=? OR content_hash=? LIMIT 1').get(id, safe.hash);
|
|
47
|
+
if (existing) {
|
|
48
|
+
db.prepare('INSERT OR IGNORE INTO legacy_imports VALUES(?,?,?)').run(id, safe.hash, existing.evidence_ref);
|
|
49
|
+
result.duplicates++;
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
const ref = randomBytes(24).toString('hex');
|
|
53
|
+
db.prepare('INSERT INTO evidence(ref,text,hash,original_bytes,retained_bytes,redaction_state,partial) VALUES(?,?,?,?,?,?,?)')
|
|
54
|
+
.run(ref, safe.text, safe.hash, safe.originalBytes, safe.retainedBytes, safe.state, Number(safe.partial));
|
|
55
|
+
db.prepare('INSERT INTO events(invocation_id,accepted_at,payload_ref) VALUES(?,?,?)')
|
|
56
|
+
.run(`legacy:${id}`, Number.isSafeInteger(event.accepted_at) ? event.accepted_at : Date.now(), ref);
|
|
57
|
+
db.prepare('INSERT INTO legacy_imports VALUES(?,?,?)').run(id, safe.hash, ref);
|
|
58
|
+
result.imported++;
|
|
59
|
+
}
|
|
60
|
+
store.commitRepository(db);
|
|
61
|
+
}
|
|
62
|
+
catch (error) {
|
|
63
|
+
db.exec('ROLLBACK');
|
|
64
|
+
throw error;
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
return result;
|
|
69
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { ContextStore } from './store.js';
|
|
2
|
+
/** Incremental materialization of committed evidence, never filesystem discovery. */
|
|
3
|
+
export declare function sync(store: ContextStore, owner: string, repo: string, options?: {
|
|
4
|
+
max_events?: number;
|
|
5
|
+
rebuild?: boolean;
|
|
6
|
+
}): {
|
|
7
|
+
indexed: number;
|
|
8
|
+
generation: number | null;
|
|
9
|
+
indexed_through_event: number;
|
|
10
|
+
manifest_hash: string | number | bigint | Uint8Array<ArrayBufferLike> | null;
|
|
11
|
+
pending_events: number;
|
|
12
|
+
unknown_events: number;
|
|
13
|
+
fts_documents: number;
|
|
14
|
+
};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { fold } from './text.js';
|
|
3
|
+
import { materializeActivity } from './graph.js';
|
|
4
|
+
/** Incremental materialization of committed evidence, never filesystem discovery. */
|
|
5
|
+
export function sync(store, owner, repo, options = {}) {
|
|
6
|
+
const cap = options.max_events ?? 1000;
|
|
7
|
+
if (!Number.isInteger(cap) || cap < 1 || cap > 10000)
|
|
8
|
+
throw new Error('BUDGET_EXCEEDED');
|
|
9
|
+
const db = store.repoDatabase(owner, repo);
|
|
10
|
+
const currentManifest = store.manifest(owner, repo);
|
|
11
|
+
if (!options.rebuild && currentManifest.pending_events === 0 && currentManifest.generation !== null)
|
|
12
|
+
return { ...currentManifest, indexed: 0 };
|
|
13
|
+
const allocation = Number(db.prepare(`SELECT coalesce(sum(retained_bytes),0) AS n FROM
|
|
14
|
+
(SELECT p.retained_bytes FROM events e LEFT JOIN evidence p ON p.ref=e.payload_ref
|
|
15
|
+
WHERE e.event_id>? ORDER BY e.event_id LIMIT ?)`)
|
|
16
|
+
.get(options.rebuild ? 0 : currentManifest.indexed_through_event, cap)?.n) * 4 + cap * 1024 + 65536;
|
|
17
|
+
return store.quotaWrite(owner, repo, allocation, () => {
|
|
18
|
+
db.exec('BEGIN IMMEDIATE');
|
|
19
|
+
try {
|
|
20
|
+
const current = store.manifest(owner, repo);
|
|
21
|
+
if (options.rebuild && Number(db.prepare('SELECT count(*) AS n FROM events').get()?.n) > cap)
|
|
22
|
+
throw new Error('BUDGET_EXCEEDED');
|
|
23
|
+
const events = db.prepare(`SELECT e.event_id,e.accepted_at,e.payload_ref,p.text,p.retention_state FROM events e
|
|
24
|
+
LEFT JOIN evidence p ON p.ref=e.payload_ref WHERE e.event_id>? ORDER BY e.event_id LIMIT ?`).all(options.rebuild ? 0 : current.indexed_through_event, cap);
|
|
25
|
+
if (!options.rebuild && !events.length && current.generation !== null) {
|
|
26
|
+
store.commitRepository(db);
|
|
27
|
+
return { ...current, indexed: 0 };
|
|
28
|
+
}
|
|
29
|
+
const generation = Number(db.prepare("INSERT INTO generations(status,created_at) VALUES('shadow',?)").run(Date.now()).lastInsertRowid);
|
|
30
|
+
db.exec('CREATE INDEX IF NOT EXISTS activity_source ON activity_edges(source,edge_id); CREATE INDEX IF NOT EXISTS activity_target ON activity_edges(target,edge_id);');
|
|
31
|
+
if (options.rebuild)
|
|
32
|
+
db.exec('DELETE FROM evidence_fts');
|
|
33
|
+
for (const event of events) {
|
|
34
|
+
if (event.payload_ref === null || event.text === null || event.retention_state !== 'available')
|
|
35
|
+
continue;
|
|
36
|
+
// A replay refreshes derived data but preserves original membership and tombstones.
|
|
37
|
+
db.prepare(`INSERT INTO search_documents(ref,source_kind,timestamp,first_indexed_generation,last_indexed_generation)
|
|
38
|
+
VALUES(?,?,?,?,?) ON CONFLICT(ref) DO UPDATE SET last_indexed_generation=excluded.last_indexed_generation`)
|
|
39
|
+
.run(event.payload_ref, db.prepare("SELECT 1 FROM activity_nodes WHERE kind='checkpoint' AND evidence_ref=?").get(event.payload_ref) ? 'checkpoint' : 'history', event.accepted_at, generation, generation);
|
|
40
|
+
db.prepare('DELETE FROM evidence_fts WHERE ref=?').run(event.payload_ref);
|
|
41
|
+
materializeActivity(db, String(event.payload_ref), String(event.text));
|
|
42
|
+
// FTS receives folded redacted content only; evidence retains display text.
|
|
43
|
+
db.prepare('INSERT INTO evidence_fts(ref,text) VALUES(?,?)').run(event.payload_ref, fold(String(event.text)));
|
|
44
|
+
}
|
|
45
|
+
const through = events.length ? Number(events[events.length - 1].event_id) : current.indexed_through_event;
|
|
46
|
+
const digest = createHash('sha256').update(`${current.manifest_hash ?? ''}:${generation}:${through}`).digest('hex');
|
|
47
|
+
db.prepare("UPDATE generations SET status='published',indexed_through_event=?,manifest_hash=? WHERE generation_id=?").run(through, digest, generation);
|
|
48
|
+
db.prepare('UPDATE manifest SET generation=? WHERE singleton=1').run(generation);
|
|
49
|
+
store.commitRepository(db);
|
|
50
|
+
return { ...store.manifest(owner, repo), indexed: events.length };
|
|
51
|
+
}
|
|
52
|
+
catch (error) {
|
|
53
|
+
db.exec('ROLLBACK');
|
|
54
|
+
throw error;
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { DatabaseSync } from 'node:sqlite';
|
|
2
|
+
export declare function parseVersion(raw: unknown): number;
|
|
3
|
+
/** Versioned transactional migration; future schema is never replaced by an empty DB. */
|
|
4
|
+
export declare function migrate(db: DatabaseSync, kind: 'registry' | 'repo' | 'unscoped'): void;
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { fold } from '../text.js';
|
|
2
|
+
const registry = `
|
|
3
|
+
CREATE TABLE device_accounting(singleton INTEGER PRIMARY KEY CHECK(singleton=1), overhead_bytes INTEGER NOT NULL DEFAULT 0);
|
|
4
|
+
INSERT INTO device_accounting VALUES(1,0);
|
|
5
|
+
CREATE TABLE owners(owner_key TEXT PRIMARY KEY, pairing_hash TEXT NOT NULL, status TEXT NOT NULL CHECK(status IN ('active','sealed')), created_at INTEGER NOT NULL, accounted_bytes INTEGER NOT NULL DEFAULT 0);
|
|
6
|
+
CREATE UNIQUE INDEX one_active_owner ON owners(status) WHERE status='active';
|
|
7
|
+
CREATE TABLE repositories(repo_uuid TEXT PRIMARY KEY, owner_key TEXT NOT NULL REFERENCES owners(owner_key), identity TEXT NOT NULL, created_at INTEGER NOT NULL, accounted_bytes INTEGER NOT NULL DEFAULT 0, UNIQUE(owner_key,identity));
|
|
8
|
+
CREATE TABLE worktrees(worktree_uuid TEXT PRIMARY KEY, repo_uuid TEXT NOT NULL REFERENCES repositories(repo_uuid), canonical_root TEXT NOT NULL, UNIQUE(repo_uuid,canonical_root));
|
|
9
|
+
`;
|
|
10
|
+
const repo = `
|
|
11
|
+
CREATE TABLE evidence(ref TEXT PRIMARY KEY, text TEXT NOT NULL, hash TEXT NOT NULL, original_bytes INTEGER NOT NULL, retained_bytes INTEGER NOT NULL, redaction_state TEXT NOT NULL, partial INTEGER NOT NULL);
|
|
12
|
+
CREATE TABLE events(event_id INTEGER PRIMARY KEY, invocation_id TEXT UNIQUE NOT NULL, accepted_at INTEGER NOT NULL, execution_status TEXT NOT NULL DEFAULT 'succeeded', recording_status TEXT NOT NULL DEFAULT 'committed', payload_ref TEXT REFERENCES evidence(ref));
|
|
13
|
+
CREATE VIRTUAL TABLE evidence_fts USING fts5(ref UNINDEXED,text,tokenize='unicode61 remove_diacritics 2');
|
|
14
|
+
CREATE TABLE activity_nodes(node_id TEXT PRIMARY KEY, kind TEXT NOT NULL, evidence_ref TEXT REFERENCES evidence(ref));
|
|
15
|
+
CREATE TABLE activity_edges(edge_id TEXT PRIMARY KEY, source TEXT NOT NULL REFERENCES activity_nodes(node_id), target TEXT NOT NULL REFERENCES activity_nodes(node_id), relation TEXT NOT NULL, evidence_ref TEXT NOT NULL REFERENCES evidence(ref));
|
|
16
|
+
CREATE TABLE generations(generation_id INTEGER PRIMARY KEY, status TEXT NOT NULL CHECK(status IN ('shadow','published')), indexed_through_event INTEGER NOT NULL DEFAULT 0, manifest_hash TEXT, created_at INTEGER NOT NULL);
|
|
17
|
+
CREATE TABLE manifest(singleton INTEGER PRIMARY KEY CHECK(singleton=1), generation INTEGER REFERENCES generations(generation_id));
|
|
18
|
+
INSERT INTO manifest VALUES(1,NULL);
|
|
19
|
+
CREATE TABLE cursors(token_id TEXT PRIMARY KEY, generation INTEGER, query_hash TEXT NOT NULL, position INTEGER NOT NULL, expires_at INTEGER NOT NULL);
|
|
20
|
+
`;
|
|
21
|
+
const unscoped = `CREATE TABLE gaps(gap_id TEXT PRIMARY KEY, created_at INTEGER NOT NULL, reason TEXT NOT NULL CHECK(reason IN ('outside_repo','ambiguous_multi_repo','repo_resolution_failed','quota')));`;
|
|
22
|
+
export function parseVersion(raw) {
|
|
23
|
+
if (typeof raw !== 'number' && (typeof raw !== 'string' || !/^(0|[1-9]\d*)$/.test(raw))) {
|
|
24
|
+
throw new Error('INDEX_INCOMPATIBLE');
|
|
25
|
+
}
|
|
26
|
+
const version = Number(raw);
|
|
27
|
+
if (!Number.isInteger(version) || version < 0 || version > 3) {
|
|
28
|
+
throw new Error('INDEX_INCOMPATIBLE');
|
|
29
|
+
}
|
|
30
|
+
return version;
|
|
31
|
+
}
|
|
32
|
+
/** Versioned transactional migration; future schema is never replaced by an empty DB. */
|
|
33
|
+
export function migrate(db, kind) {
|
|
34
|
+
db.exec('PRAGMA busy_timeout=5000; PRAGMA secure_delete=ON');
|
|
35
|
+
let version = parseVersion(db.prepare('PRAGMA user_version').get()?.user_version);
|
|
36
|
+
if (version === 3)
|
|
37
|
+
return;
|
|
38
|
+
db.exec('BEGIN IMMEDIATE');
|
|
39
|
+
try {
|
|
40
|
+
// Another opener may have migrated while we waited for the write lock.
|
|
41
|
+
version = parseVersion(db.prepare('PRAGMA user_version').get()?.user_version);
|
|
42
|
+
if (version === 3) {
|
|
43
|
+
db.exec('COMMIT');
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
if (version === 0)
|
|
47
|
+
db.exec({ registry, repo, unscoped }[kind]);
|
|
48
|
+
if (kind === 'registry' && version < 2)
|
|
49
|
+
db.exec(`CREATE TABLE import_quarantine(fingerprint TEXT PRIMARY KEY, reason TEXT NOT NULL, first_seen INTEGER NOT NULL);`);
|
|
50
|
+
if (kind === 'repo' && version === 1) {
|
|
51
|
+
const fts = db.prepare("SELECT 1 FROM sqlite_master WHERE name='evidence_fts'").get();
|
|
52
|
+
if (fts)
|
|
53
|
+
db.exec(`
|
|
54
|
+
CREATE VIRTUAL TABLE evidence_fts_v2 USING fts5(ref UNINDEXED,text,tokenize='unicode61 remove_diacritics 2');
|
|
55
|
+
INSERT INTO evidence_fts_v2(ref,text) SELECT ref,text FROM evidence_fts;
|
|
56
|
+
DROP TABLE evidence_fts;
|
|
57
|
+
ALTER TABLE evidence_fts_v2 RENAME TO evidence_fts;
|
|
58
|
+
`);
|
|
59
|
+
}
|
|
60
|
+
if (kind === 'repo' && version < 2)
|
|
61
|
+
db.exec(`
|
|
62
|
+
CREATE TABLE legacy_imports(legacy_id TEXT PRIMARY KEY, content_hash TEXT NOT NULL, evidence_ref TEXT NOT NULL REFERENCES evidence(ref));
|
|
63
|
+
CREATE INDEX legacy_content ON legacy_imports(content_hash);
|
|
64
|
+
CREATE TABLE search_documents(ref TEXT PRIMARY KEY REFERENCES evidence(ref), source_kind TEXT NOT NULL, timestamp INTEGER NOT NULL,
|
|
65
|
+
first_indexed_generation INTEGER NOT NULL, last_indexed_generation INTEGER NOT NULL);
|
|
66
|
+
ALTER TABLE evidence ADD COLUMN retention_state TEXT NOT NULL DEFAULT 'available';
|
|
67
|
+
`);
|
|
68
|
+
if (kind === 'repo') {
|
|
69
|
+
db.exec('ALTER TABLE search_documents ADD COLUMN deleted_generation INTEGER;');
|
|
70
|
+
// Rebuild only already indexed redacted content with the new folding rule.
|
|
71
|
+
const rows = db.prepare('SELECT ref,text FROM evidence_fts').all();
|
|
72
|
+
db.exec('DELETE FROM evidence_fts');
|
|
73
|
+
const insert = db.prepare('INSERT INTO evidence_fts(ref,text) VALUES(?,?)');
|
|
74
|
+
for (const row of rows)
|
|
75
|
+
insert.run(row.ref, fold(String(row.text)));
|
|
76
|
+
}
|
|
77
|
+
db.exec('PRAGMA user_version=3; COMMIT');
|
|
78
|
+
}
|
|
79
|
+
catch (error) {
|
|
80
|
+
// COMMIT may already have released the transaction; preserve the original error.
|
|
81
|
+
try {
|
|
82
|
+
db.exec('ROLLBACK');
|
|
83
|
+
}
|
|
84
|
+
catch { /* No active transaction to roll back. */ }
|
|
85
|
+
throw error;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export declare const MAX_PAYLOAD_BYTES: number;
|
|
2
|
+
export interface RedactedPayload {
|
|
3
|
+
text: string;
|
|
4
|
+
hash: string;
|
|
5
|
+
originalBytes: number;
|
|
6
|
+
retainedBytes: number;
|
|
7
|
+
partial: boolean;
|
|
8
|
+
state: 'retained' | 'redacted' | 'excluded' | 'binary';
|
|
9
|
+
}
|
|
10
|
+
/** This is the sole payload persistence boundary. Hashes cover retained bytes only. */
|
|
11
|
+
export declare function redact(input: string | Buffer, options?: {
|
|
12
|
+
path?: string;
|
|
13
|
+
maxBytes?: number;
|
|
14
|
+
key?: Buffer;
|
|
15
|
+
}): RedactedPayload;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { createHash, createHmac, randomBytes } from 'node:crypto';
|
|
2
|
+
import { utf8Prefix } from './text.js';
|
|
3
|
+
// Non-persistence callers may redact without a store; persistence always supplies the installation key.
|
|
4
|
+
const ephemeralKey = randomBytes(32);
|
|
5
|
+
export const MAX_PAYLOAD_BYTES = 4 * 1024 * 1024;
|
|
6
|
+
/** This is the sole payload persistence boundary. Hashes cover retained bytes only. */
|
|
7
|
+
export function redact(input, options = {}) {
|
|
8
|
+
const cap = options.maxBytes ?? MAX_PAYLOAD_BYTES;
|
|
9
|
+
if (!Number.isInteger(cap) || cap < 0 || cap > MAX_PAYLOAD_BYTES)
|
|
10
|
+
throw new Error('BUDGET_EXCEEDED');
|
|
11
|
+
const tagKey = createHmac('sha256', options.key ?? ephemeralKey).update('redaction_tagging_v1').digest();
|
|
12
|
+
const bytes = Buffer.isBuffer(input) ? input : Buffer.from(input);
|
|
13
|
+
let text = bytes.toString('utf8');
|
|
14
|
+
let state = 'retained';
|
|
15
|
+
const path = options.path?.replace(/\\/g, '/');
|
|
16
|
+
if (path && /(?:^|\/)(?:\.env(?:\.[^/]*)?|id_(?:rsa|dsa|ecdsa|ed25519)(?:\.pub)?|credentials(?:\.[^/]*)?)(?:$|\/)/i.test(path)) {
|
|
17
|
+
text = '';
|
|
18
|
+
state = 'excluded';
|
|
19
|
+
}
|
|
20
|
+
else if (bytes.includes(0) || text.includes('\uFFFD') || /[\x01-\x08\x0e-\x1f]/.test(text) || /(?:^|\s)[A-Za-z0-9+/]{256,}={0,2}(?:$|\s)/.test(text)) {
|
|
21
|
+
text = '';
|
|
22
|
+
state = 'binary';
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
const patterns = [
|
|
26
|
+
[/-----BEGIN (?:[A-Z ]+ )?PRIVATE KEY-----[\s\S]*?(?:-----END (?:[A-Z ]+ )?PRIVATE KEY-----|$)/g, 'private_key'],
|
|
27
|
+
[/\bBearer\s+[A-Za-z0-9._~+\/-]+=*/gi, 'bearer'],
|
|
28
|
+
[/\b(?:api[_-]?key|access[_-]?token|secret[_-]?key)["']?\s*[:=]\s*(?:"[^"\r\n]*"|'[^'\r\n]*'|[^\s,;"'\[\]{}]+)/gi, 'api_key'],
|
|
29
|
+
[/\b(?:password|passwd|pwd)["']?\s*[:=]\s*(?:"[^"\r\n]*"|'[^'\r\n]*'|[^\s,;"'\[\]{}]+)/gi, 'password'],
|
|
30
|
+
[/\b(?:sk-[A-Za-z0-9_-]{16,}|AKIA[A-Z0-9]{16}|gh[pousr]_[A-Za-z0-9]{20,})\b/g, 'api_key'],
|
|
31
|
+
];
|
|
32
|
+
for (const [pattern, cls] of patterns)
|
|
33
|
+
text = text.replace(pattern, secret => {
|
|
34
|
+
state = 'redacted';
|
|
35
|
+
const tag = createHmac('sha256', tagKey).update(secret).digest('hex').slice(0, 16);
|
|
36
|
+
return `[REDACTED:${cls}:${tag}]`;
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
const partial = Buffer.byteLength(text) > cap;
|
|
40
|
+
text = utf8Prefix(text, cap);
|
|
41
|
+
return { text, hash: createHash('sha256').update(text).digest('hex'), originalBytes: bytes.length,
|
|
42
|
+
retainedBytes: Buffer.byteLength(text), partial, state };
|
|
43
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export interface RepositoryResolution {
|
|
2
|
+
root: string;
|
|
3
|
+
identity: string;
|
|
4
|
+
worktree: string;
|
|
5
|
+
commonDir: string;
|
|
6
|
+
}
|
|
7
|
+
export type PathGuard = (path: string) => string | Promise<string>;
|
|
8
|
+
/** Explicit trusted guard is mandatory; denial never falls back to a lexical path. */
|
|
9
|
+
export declare function resolveRepository(cwd: unknown, guard: PathGuard): Promise<RepositoryResolution | null>;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { execFileSync } from 'node:child_process';
|
|
2
|
+
import { realpathSync, statSync } from 'node:fs';
|
|
3
|
+
import { isAbsolute, resolve } from 'node:path';
|
|
4
|
+
import { createHash } from 'node:crypto';
|
|
5
|
+
const hash = (value) => createHash('sha256').update(value).digest('hex');
|
|
6
|
+
/** Explicit trusted guard is mandatory; denial never falls back to a lexical path. */
|
|
7
|
+
export async function resolveRepository(cwd, guard) {
|
|
8
|
+
if (Array.isArray(cwd))
|
|
9
|
+
throw new Error('SCOPE_AMBIGUOUS');
|
|
10
|
+
if (typeof cwd !== 'string' || !cwd)
|
|
11
|
+
throw new Error('SCOPE_REQUIRED');
|
|
12
|
+
if (!isAbsolute(cwd) || cwd.split(/[\\/]/).includes('..'))
|
|
13
|
+
throw new Error('ACCESS_DENIED');
|
|
14
|
+
const approved = await guard(cwd);
|
|
15
|
+
let canonical;
|
|
16
|
+
try {
|
|
17
|
+
canonical = realpathSync(approved);
|
|
18
|
+
if (!statSync(canonical).isDirectory())
|
|
19
|
+
throw new Error();
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
throw new Error('REPOSITORY_NOT_FOUND');
|
|
23
|
+
}
|
|
24
|
+
// Guard the real target too if a symlink was resolved.
|
|
25
|
+
if (resolve(canonical) !== resolve(approved))
|
|
26
|
+
await guard(canonical);
|
|
27
|
+
const git = (...args) => execFileSync('git', ['-C', canonical, ...args], {
|
|
28
|
+
encoding: 'utf8', timeout: 5000, maxBuffer: 64 * 1024, stdio: ['ignore', 'pipe', 'pipe'],
|
|
29
|
+
env: { ...process.env, GIT_DIR: undefined, GIT_WORK_TREE: undefined, GIT_COMMON_DIR: undefined },
|
|
30
|
+
}).trim();
|
|
31
|
+
try {
|
|
32
|
+
const root = realpathSync(git('rev-parse', '--show-toplevel'));
|
|
33
|
+
const commonDir = realpathSync(resolve(canonical, git('rev-parse', '--git-common-dir')));
|
|
34
|
+
await guard(root);
|
|
35
|
+
await guard(commonDir);
|
|
36
|
+
return { root, commonDir, identity: hash(commonDir), worktree: hash(root) };
|
|
37
|
+
}
|
|
38
|
+
catch (error) {
|
|
39
|
+
// A confirmed non-repository is unscoped. Permission and subprocess failures are not.
|
|
40
|
+
const failure = error;
|
|
41
|
+
if (String(failure.stderr ?? '').includes('not a git repository'))
|
|
42
|
+
return null;
|
|
43
|
+
throw new Error('ACCESS_DENIED');
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { ContextStore } from './store.js';
|
|
2
|
+
export interface SearchOptions {
|
|
3
|
+
query: string;
|
|
4
|
+
limit?: number;
|
|
5
|
+
max_bytes?: number;
|
|
6
|
+
cursor?: string;
|
|
7
|
+
source_types?: string[];
|
|
8
|
+
since?: number;
|
|
9
|
+
until?: number;
|
|
10
|
+
include_related?: boolean;
|
|
11
|
+
}
|
|
12
|
+
export interface ReadOptions {
|
|
13
|
+
refs: string[];
|
|
14
|
+
max_bytes?: number;
|
|
15
|
+
cursor?: string;
|
|
16
|
+
ranges?: {
|
|
17
|
+
ref: string;
|
|
18
|
+
start: number;
|
|
19
|
+
end?: number;
|
|
20
|
+
}[];
|
|
21
|
+
}
|
|
22
|
+
export declare function search(store: ContextStore, owner: string, repo: string, input: SearchOptions): {
|
|
23
|
+
schema_version: number;
|
|
24
|
+
ok: boolean;
|
|
25
|
+
scope: {
|
|
26
|
+
repository_ref: string;
|
|
27
|
+
};
|
|
28
|
+
index: {
|
|
29
|
+
generation: number | null;
|
|
30
|
+
indexed_through_event: number;
|
|
31
|
+
pending_events: number;
|
|
32
|
+
freshness: string;
|
|
33
|
+
};
|
|
34
|
+
coverage: {
|
|
35
|
+
complete: boolean;
|
|
36
|
+
reasons: string[];
|
|
37
|
+
};
|
|
38
|
+
items: Record<string, unknown>[];
|
|
39
|
+
partial: boolean;
|
|
40
|
+
next_cursor: string | null;
|
|
41
|
+
warnings: string[];
|
|
42
|
+
};
|
|
43
|
+
export declare function readEvidence(store: ContextStore, owner: string, repo: string, input: ReadOptions): {
|
|
44
|
+
schema_version: number;
|
|
45
|
+
ok: boolean;
|
|
46
|
+
scope: {
|
|
47
|
+
repository_ref: string;
|
|
48
|
+
};
|
|
49
|
+
index: {
|
|
50
|
+
generation: number | null;
|
|
51
|
+
indexed_through_event: number;
|
|
52
|
+
pending_events: number;
|
|
53
|
+
freshness: string;
|
|
54
|
+
};
|
|
55
|
+
coverage: {
|
|
56
|
+
complete: boolean;
|
|
57
|
+
reasons: string[];
|
|
58
|
+
};
|
|
59
|
+
items: Record<string, unknown>[];
|
|
60
|
+
partial: boolean;
|
|
61
|
+
next_cursor: string | null;
|
|
62
|
+
warnings: string[];
|
|
63
|
+
};
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { fold, utf8Prefix } from './text.js';
|
|
3
|
+
import { queryActivityGraph } from './graph.js';
|
|
4
|
+
const bytes = (value) => Buffer.byteLength(JSON.stringify(value));
|
|
5
|
+
const digest = (value) => createHash('sha256').update(JSON.stringify(value)).digest('hex');
|
|
6
|
+
function budget(value, fallback, ceiling) {
|
|
7
|
+
const cap = value ?? fallback;
|
|
8
|
+
if (!Number.isInteger(cap) || cap < 1 || cap > ceiling)
|
|
9
|
+
throw new Error('BUDGET_EXCEEDED');
|
|
10
|
+
return cap;
|
|
11
|
+
}
|
|
12
|
+
function envelope(store, owner, repo) {
|
|
13
|
+
const manifest = store.inspectManifest(owner, repo);
|
|
14
|
+
const reasons = manifest.unknown_events ? ['unknown_after_restart'] : [];
|
|
15
|
+
return { schema_version: 1, ok: true, scope: { repository_ref: repo },
|
|
16
|
+
index: { generation: manifest.generation, indexed_through_event: manifest.indexed_through_event,
|
|
17
|
+
pending_events: manifest.pending_events, freshness: manifest.generation === null ? 'missing' : manifest.pending_events ? 'stale' : 'current' },
|
|
18
|
+
coverage: { complete: !reasons.length, reasons }, items: [],
|
|
19
|
+
partial: false, next_cursor: null, warnings: [] };
|
|
20
|
+
}
|
|
21
|
+
function continuation(store, owner, repo, key, generation, cursor, pinned = false) {
|
|
22
|
+
// First-page keyset sentinel is the all-null tuple (last_match,
|
|
23
|
+
// last_timestamp, last_ref), not bucket 0 / MAX_SAFE_INTEGER / ''. The
|
|
24
|
+
// Outer WHERE (? IS NULL OR ...) guard bypasses the keyset predicate entirely,
|
|
25
|
+
// including rows at timestamp bounds. Continuations carry the last row tuple.
|
|
26
|
+
if (!cursor)
|
|
27
|
+
return { generation, position: 0, offset: 0, last_match: null, last_timestamp: null, last_ref: null, expires_at: Date.now() + 600000 };
|
|
28
|
+
const state = store.parseReadCursor(owner, repo, cursor);
|
|
29
|
+
if (state.key !== key || !Number.isSafeInteger(state.position) || Number(state.position) < 0 ||
|
|
30
|
+
!Number.isSafeInteger(state.offset) || Number(state.offset) < 0)
|
|
31
|
+
throw new Error('CURSOR_INVALID');
|
|
32
|
+
const validGeneration = pinned
|
|
33
|
+
? Number.isSafeInteger(state.generation) && Number(state.generation) >= 1 && generation !== null && Number(state.generation) <= generation
|
|
34
|
+
: state.generation === generation;
|
|
35
|
+
if (typeof state.expires_at !== 'number' || state.expires_at <= Date.now() || !validGeneration)
|
|
36
|
+
throw new Error('CURSOR_STALE');
|
|
37
|
+
if (pinned && !(state.last_match === null && state.last_timestamp === null && state.last_ref === null) &&
|
|
38
|
+
(![0, 1].includes(Number(state.last_match)) || typeof state.last_match !== 'number' || !Number.isSafeInteger(state.last_timestamp) || typeof state.last_ref !== 'string' || !/^[a-f0-9]{48}$/.test(state.last_ref)))
|
|
39
|
+
throw new Error('CURSOR_INVALID');
|
|
40
|
+
return { generation: state.generation, position: Number(state.position), offset: Number(state.offset),
|
|
41
|
+
last_match: state.last_match, last_timestamp: state.last_timestamp,
|
|
42
|
+
last_ref: state.last_ref, expires_at: state.expires_at };
|
|
43
|
+
}
|
|
44
|
+
export function search(store, owner, repo, input) {
|
|
45
|
+
store.authorize(owner, repo);
|
|
46
|
+
const query = input.query?.trim().normalize('NFC');
|
|
47
|
+
if (!query || Buffer.byteLength(query) > 4096)
|
|
48
|
+
throw new Error('BUDGET_EXCEEDED');
|
|
49
|
+
const limit = budget(input.limit, 8, 50), cap = budget(input.max_bytes, 65536, 131072);
|
|
50
|
+
const sources = input.source_types ?? ['history', 'checkpoint', 'activity'];
|
|
51
|
+
if (!Array.isArray(sources) || !sources.length || sources.some(s => !['history', 'checkpoint', 'activity'].includes(s)))
|
|
52
|
+
throw new Error('ACTION_UNSUPPORTED');
|
|
53
|
+
for (const time of [input.since, input.until])
|
|
54
|
+
if (time !== undefined && !Number.isSafeInteger(time))
|
|
55
|
+
throw new Error('BUDGET_EXCEEDED');
|
|
56
|
+
const result = envelope(store, owner, repo);
|
|
57
|
+
let generation = result.index.generation;
|
|
58
|
+
if (generation === null)
|
|
59
|
+
throw new Error('INDEX_MISSING');
|
|
60
|
+
if (input.include_related !== undefined && typeof input.include_related !== 'boolean')
|
|
61
|
+
throw new Error('ACTION_UNSUPPORTED');
|
|
62
|
+
const key = digest(['search', query, limit, sources, input.since, input.until, input.include_related]);
|
|
63
|
+
const state = continuation(store, owner, repo, key, generation, input.cursor, true);
|
|
64
|
+
generation = state.generation;
|
|
65
|
+
result.index.generation = generation;
|
|
66
|
+
const token = (row) => store.signReadCursor(owner, repo, { key, generation, position: 0, offset: 0, last_match: row?.score ?? state.last_match,
|
|
67
|
+
last_timestamp: row?.timestamp ?? state.last_timestamp,
|
|
68
|
+
last_ref: row?.ref ?? state.last_ref, expires_at: state.expires_at });
|
|
69
|
+
// Quote individual Unicode tokens; no user FTS operators or SQL are accepted.
|
|
70
|
+
const terms = fold(query).match(/[\p{L}\p{N}_]+/gu)?.slice(0, 64) ?? [];
|
|
71
|
+
const fts = terms.map(term => `"${term.replace(/"/g, '""')}"`).join(' OR ');
|
|
72
|
+
const ftsClause = terms.length ? 'UNION ALL SELECT ref,1 AS score FROM evidence_fts WHERE evidence_fts MATCH ?' : '';
|
|
73
|
+
return store.withReadDatabase(owner, repo, db => {
|
|
74
|
+
// Search ranks by discrete relevance buckets: 0 = exact substring, 1 = FTS
|
|
75
|
+
// token match, then timestamp DESC and ref ASC as stable tie-breakers.
|
|
76
|
+
// This deterministic ordering completely insulates keyset pagination from
|
|
77
|
+
// dynamic BM25 score shifts as corpus statistics change across generations.
|
|
78
|
+
// Current retention availability overrides pinned generation membership:
|
|
79
|
+
// purged evidence is never returned.
|
|
80
|
+
// Group each document before applying the keyset: filtering match rows first
|
|
81
|
+
// could resurrect an exact match as an FTS-only match on the next page.
|
|
82
|
+
// Bind order: substring, optional FTS, generation twice, each source, time
|
|
83
|
+
// bounds, then exactly seven keyset arguments in this SQL placeholder order:
|
|
84
|
+
// [last_match, last_match, last_match, last_timestamp,
|
|
85
|
+
// last_match, last_timestamp, last_ref], followed by limit.
|
|
86
|
+
// All seven keyset arguments are null on the first page.
|
|
87
|
+
// Total placeholders = 13 + sources.length + (terms.length ? 1 : 0).
|
|
88
|
+
const rows = db.prepare(`WITH matches AS (
|
|
89
|
+
SELECT ref,0 AS score FROM evidence WHERE instr(lower(text),lower(?))>0
|
|
90
|
+
${ftsClause}
|
|
91
|
+
), ranked AS (
|
|
92
|
+
SELECT d.ref,d.source_kind,d.timestamp,p.text,p.partial,p.redaction_state,min(m.score) AS score
|
|
93
|
+
FROM matches m JOIN search_documents d ON d.ref=m.ref JOIN evidence p ON p.ref=d.ref
|
|
94
|
+
WHERE d.first_indexed_generation<=? AND (d.deleted_generation IS NULL OR d.deleted_generation>?) AND p.retention_state='available'
|
|
95
|
+
AND d.source_kind IN (${sources.map(() => '?').join(',')}) AND d.timestamp>=? AND d.timestamp<=?
|
|
96
|
+
GROUP BY d.ref
|
|
97
|
+
) SELECT * FROM ranked
|
|
98
|
+
WHERE (? IS NULL OR score>? OR (score=? AND timestamp<?) OR (score=? AND timestamp=? AND ref>?))
|
|
99
|
+
ORDER BY score ASC,timestamp DESC,ref ASC LIMIT ?`)
|
|
100
|
+
.all(query, ...(terms.length ? [fts] : []), generation, generation, ...sources, input.since ?? 0, input.until ?? Number.MAX_SAFE_INTEGER, state.last_match, state.last_match, state.last_match, state.last_timestamp, state.last_match, state.last_timestamp, state.last_ref, limit + 1);
|
|
101
|
+
let count = 0;
|
|
102
|
+
for (const row of rows.slice(0, limit)) {
|
|
103
|
+
const item = { ref: row.ref, source_kind: row.source_kind, evidence_class: row.source_kind === 'checkpoint' ? 'reported' : 'observed', timestamp: row.timestamp,
|
|
104
|
+
label: 'Retained evidence', snippet: utf8Prefix(String(row.text), 512), match_reason: Number(row.score) === 0 ? 'exact' : 'fts',
|
|
105
|
+
redaction_state: row.redaction_state, truncated: Boolean(row.partial) || Buffer.byteLength(String(row.text)) > 512 };
|
|
106
|
+
const related = input.include_related ? queryActivityGraph(store, owner, repo, {
|
|
107
|
+
refs: [String(row.ref)], action: 'related', generation, max_nodes: 20, max_edges: 50, max_visited: 20, wall_ms: 10,
|
|
108
|
+
}) : undefined;
|
|
109
|
+
result.items.push(related ? { ...item, related_refs: [...new Set(related.nodes.filter(n => n.kind === 'event' && n.evidence_ref !== row.ref).map(n => n.evidence_ref))],
|
|
110
|
+
related_partial: related.partial, related_examined_edges: related.examined_edges } : item);
|
|
111
|
+
count++;
|
|
112
|
+
result.partial = rows.length > count;
|
|
113
|
+
result.next_cursor = result.partial ? token(rows[count - 1]) : null;
|
|
114
|
+
if (bytes(result) > cap) {
|
|
115
|
+
result.items.pop();
|
|
116
|
+
count--;
|
|
117
|
+
result.partial = true;
|
|
118
|
+
result.next_cursor = token(rows[count - 1]);
|
|
119
|
+
break;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
if (bytes(result) > cap || (rows.length && count === 0))
|
|
123
|
+
throw new Error('BUDGET_EXCEEDED');
|
|
124
|
+
if (result.partial)
|
|
125
|
+
result.warnings = ['response_budget_or_limit'];
|
|
126
|
+
if (bytes(result) > cap)
|
|
127
|
+
throw new Error('BUDGET_EXCEEDED');
|
|
128
|
+
return result;
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
export function readEvidence(store, owner, repo, input) {
|
|
132
|
+
store.authorize(owner, repo);
|
|
133
|
+
if (!Array.isArray(input.refs) || input.refs.length < 1 || input.refs.length > 20 || input.refs.some(ref => typeof ref !== 'string' || ref.length > 128))
|
|
134
|
+
throw new Error('BUDGET_EXCEEDED');
|
|
135
|
+
const cap = budget(input.max_bytes, 262144, 1048576), result = envelope(store, owner, repo);
|
|
136
|
+
const ranges = input.ranges ?? [];
|
|
137
|
+
if (!Array.isArray(ranges) || ranges.length > 20 || new Set(ranges.map(range => range.ref)).size !== ranges.length || ranges.some(range => !input.refs.includes(range.ref) || !Number.isSafeInteger(range.start) || range.start < 0 || (range.end !== undefined && (!Number.isSafeInteger(range.end) || range.end < range.start))))
|
|
138
|
+
throw new Error('ACTION_UNSUPPORTED');
|
|
139
|
+
const key = digest(ranges.length ? ['read', input.refs, ranges] : ['read', input.refs]);
|
|
140
|
+
const state = continuation(store, owner, repo, key, result.index.generation, input.cursor);
|
|
141
|
+
const token = (position, offset) => store.signReadCursor(owner, repo, { key, generation: result.index.generation, position, offset, expires_at: state.expires_at });
|
|
142
|
+
return store.withReadDatabase(owner, repo, db => {
|
|
143
|
+
for (let i = state.position; i < input.refs.length; i++) {
|
|
144
|
+
const ref = input.refs[i], row = db.prepare('SELECT * FROM evidence WHERE ref=?').get(ref);
|
|
145
|
+
const range = ranges.find(range => range.ref === ref);
|
|
146
|
+
const start = i === state.position && state.offset > 0 ? state.offset : range?.start ?? 0;
|
|
147
|
+
const error = !row ? 'REF_NOT_FOUND' : ['expired', 'purged'].includes(String(row.retention_state)) ? 'EVIDENCE_EXPIRED' : null;
|
|
148
|
+
if (error) {
|
|
149
|
+
result.items.push({ ref, error });
|
|
150
|
+
result.partial = i + 1 < input.refs.length;
|
|
151
|
+
result.next_cursor = result.partial ? token(i + 1, 0) : null;
|
|
152
|
+
if (bytes(result) > cap) {
|
|
153
|
+
result.items.pop();
|
|
154
|
+
result.partial = true;
|
|
155
|
+
result.next_cursor = token(i, 0);
|
|
156
|
+
break;
|
|
157
|
+
}
|
|
158
|
+
continue;
|
|
159
|
+
}
|
|
160
|
+
const text = String(row.text), total = Buffer.byteLength(text);
|
|
161
|
+
const endLimit = Math.min(range?.end ?? total, total);
|
|
162
|
+
const buffer = Buffer.from(text);
|
|
163
|
+
const boundary = (offset) => offset === total || (buffer[offset] & 0xc0) !== 0x80;
|
|
164
|
+
if (start > total || start > endLimit || !boundary(start) || !boundary(endLimit))
|
|
165
|
+
throw new Error(range ? 'ACTION_UNSUPPORTED' : 'CURSOR_INVALID');
|
|
166
|
+
const remaining = buffer.subarray(start, endLimit).toString('utf8');
|
|
167
|
+
const isCheckpoint = Boolean(db.prepare("SELECT 1 FROM activity_nodes WHERE kind='checkpoint' AND evidence_ref=?").get(ref));
|
|
168
|
+
const item = { ref, source_kind: isCheckpoint ? 'checkpoint' : 'history', evidence_class: isCheckpoint ? 'reported' : 'observed', content: '', hash: row.hash,
|
|
169
|
+
captured_range: { start, end: start }, total_bytes: total, redaction_state: row.redaction_state,
|
|
170
|
+
retention_state: row.partial ? 'partial' : row.retention_state, truncated: Boolean(row.partial) };
|
|
171
|
+
result.items.push(item);
|
|
172
|
+
const fit = (length) => {
|
|
173
|
+
const content = utf8Prefix(remaining, length), end = start + Buffer.byteLength(content);
|
|
174
|
+
item.content = content;
|
|
175
|
+
item.captured_range = { start, end };
|
|
176
|
+
item.truncated = Boolean(row.partial) || start > 0 || end < total;
|
|
177
|
+
result.partial = end < endLimit || i + 1 < input.refs.length;
|
|
178
|
+
result.next_cursor = null;
|
|
179
|
+
if (end < endLimit)
|
|
180
|
+
result.next_cursor = token(i, end);
|
|
181
|
+
else if (i + 1 < input.refs.length)
|
|
182
|
+
result.next_cursor = token(i + 1, 0);
|
|
183
|
+
return bytes(result) <= cap;
|
|
184
|
+
};
|
|
185
|
+
let low = 0, high = Buffer.byteLength(remaining);
|
|
186
|
+
while (low < high) {
|
|
187
|
+
const mid = Math.ceil((low + high) / 2);
|
|
188
|
+
if (fit(mid))
|
|
189
|
+
low = mid;
|
|
190
|
+
else
|
|
191
|
+
high = mid - 1;
|
|
192
|
+
}
|
|
193
|
+
const fits = fit(low);
|
|
194
|
+
if (!fits || (endLimit > start && !String(item.content).length)) {
|
|
195
|
+
result.items.pop();
|
|
196
|
+
result.partial = true;
|
|
197
|
+
result.next_cursor = token(i, start);
|
|
198
|
+
break;
|
|
199
|
+
}
|
|
200
|
+
if (start + Buffer.byteLength(String(item.content)) < endLimit)
|
|
201
|
+
break;
|
|
202
|
+
}
|
|
203
|
+
if (bytes(result) > cap || !result.items.length)
|
|
204
|
+
throw new Error('BUDGET_EXCEEDED');
|
|
205
|
+
return result;
|
|
206
|
+
});
|
|
207
|
+
}
|