@inneranimalmedia/agentsam-sdk 2.6.1 → 2.6.3
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/AGENTSAM.md +45 -1
- package/README.md +80 -0
- package/apps/README.md +34 -0
- package/apps/cad-creator/README.md +728 -0
- package/apps/cad-creator/shared/cad/package.json +19 -0
- package/apps/cad-creator/shared/cad/src/domain/operations.ts +7 -0
- package/apps/cad-creator/shared/cad/src/domain/project-contract.d.ts +3 -0
- package/apps/cad-creator/shared/cad/src/domain/project-contract.js +75 -0
- package/apps/cad-creator/shared/cad/src/domain/project-tools.d.ts +1 -0
- package/apps/cad-creator/shared/cad/src/domain/project-tools.js +24 -0
- package/apps/cad-creator/shared/cad/src/domain/validation.ts +191 -0
- package/apps/cad-creator/shared/cad/src/index.ts +4 -0
- package/apps/cad-creator/shared/cad/src/robotics/types.ts +232 -0
- package/apps/cad-creator/shared/cad/src/types.ts +507 -0
- package/apps/cad-creator/shared/cad/tsconfig.json +14 -0
- package/docs/AUTH_IDENTITY_CONTRACT.md +25 -27
- package/docs/AUTORAG.md +58 -0
- package/docs/BRAND_INTELLIGENCE.md +30 -0
- package/docs/CLI_SHELL.md +4 -2
- package/docs/CMS_CONTEXTUAL_COMPOSER.md +38 -0
- package/docs/MERKLE.md +4 -0
- package/docs/PROJECT_CONFIG.md +3 -0
- package/docs/REPOSITORY_KNOWLEDGE.md +2 -0
- package/docs/SDK_WORKER.md +4 -3
- package/docs/THEME_REFINERY.md +120 -0
- package/docs/architecture/PROVIDER-TOOLS-AND-COMPACTION.md +60 -0
- package/docs/architecture/STORAGE.md +87 -0
- package/docs/cad-project-tools.md +69 -0
- package/docs/cms-scaffolding-pattern-reference.md +73 -0
- package/docs/plans/AGENTSAM-INTERACTIVE-UI-REQUIREMENTS-codex-tui-20260919.md +113 -0
- package/docs/plans/AGENTSAM-INTERACTIVE-UI-REQUIREMENTS.md +85 -0
- package/docs/plans/AGENTSAM-MCP-CLIENT-2026-09-19.md +181 -0
- package/docs/plans/ASBD-PORTABLE-WORKGRAPH-UI-DEFERRED.md +34 -0
- package/docs/plans/ASTRA-BRIEF-2026-09-18.md +258 -0
- package/docs/plans/MULTI-AGENT-SPRINTS-2026-09-19.md +373 -0
- package/docs/plans/NEXT-AGENT-HANDOFF.md +167 -0
- package/docs/plans/RECONCILIATION-2026-09-19.md +178 -0
- package/docs/plugins.md +43 -0
- package/docs/tools/README.md +57 -0
- package/docs/tools/agent-and-orchestration.md +49 -0
- package/docs/tools/browser-and-research.md +46 -0
- package/docs/tools/cloudflare-platform.md +32 -0
- package/docs/tools/cms.md +25 -0
- package/docs/tools/communications.md +41 -0
- package/docs/tools/database-and-storage.md +62 -0
- package/docs/tools/design-and-media.md +64 -0
- package/docs/tools/filesystem-and-code.md +55 -0
- package/docs/tools/github.md +56 -0
- package/docs/tools/integrations-and-misc.md +16 -0
- package/docs/tools/memory.md +21 -0
- package/docs/tools/terminal-and-execution.md +29 -0
- package/docs/tools/tickets-and-platform.md +30 -0
- package/migrations/d1/0001_agentsam_plugin_runtime.sql +177 -0
- package/migrations/d1/0002_agentsam_ticket_ownership.sql +8 -0
- package/migrations/d1/0002_dynamic_model_policies.sql +23 -0
- package/migrations/d1/0003_expand_tool_handler_families.sql +18 -0
- package/migrations/d1/0004_project_aware_retrieval_lanes.sql +24 -0
- package/migrations/d1/0005_portable_knowledge_identity.sql +104 -0
- package/migrations/d1/0006_repair_ops_trail_account_scope.sql +14 -0
- package/migrations/d1/0007_repair_ops_trail_agent_run_account.sql +13 -0
- package/migrations/d1/0008_backfill_knowledge_run_legacy_tenant.sql +14 -0
- package/migrations/d1/0009_normalize_cloudflare_plugin_installations.sql +137 -0
- package/migrations/runtime/0001_cli_runtime.sql +67 -0
- package/migrations/runtime/0002_project_sessions.sql +15 -0
- package/migrations/runtime/0003_provider_continuations.sql +8 -0
- package/package.json +92 -12
- package/packages/agentsam-brand/README.md +21 -0
- package/packages/agentsam-brand/package.json +22 -0
- package/packages/agentsam-brand/src/contract.js +59 -0
- package/packages/agentsam-brand/src/extract.js +126 -0
- package/packages/agentsam-brand/src/goap-actions.js +73 -0
- package/packages/agentsam-brand/src/index.js +48 -0
- package/packages/agentsam-brand/src/plan.js +115 -0
- package/packages/agentsam-brand/src/resolve.js +139 -0
- package/packages/agentsam-brand/src/scan.js +250 -0
- package/packages/agentsam-brand/test/brand-scan.test.mjs +106 -0
- package/packages/agentsam-contracts/README.md +23 -0
- package/packages/agentsam-contracts/package.json +32 -0
- package/packages/agentsam-contracts/src/adapter.ts +25 -0
- package/packages/agentsam-contracts/src/agent.ts +35 -0
- package/packages/agentsam-contracts/src/artifacts.ts +33 -0
- package/packages/agentsam-contracts/src/authority.ts +29 -0
- package/packages/agentsam-contracts/src/context.ts +16 -0
- package/packages/agentsam-contracts/src/errors.ts +46 -0
- package/packages/agentsam-contracts/src/events.ts +47 -0
- package/packages/agentsam-contracts/src/execution.ts +50 -0
- package/packages/agentsam-contracts/src/hooks.ts +40 -0
- package/packages/agentsam-contracts/src/identity.ts +16 -0
- package/packages/agentsam-contracts/src/index.ts +14 -0
- package/packages/agentsam-contracts/src/models.ts +9 -0
- package/packages/agentsam-contracts/src/providers.ts +33 -0
- package/packages/agentsam-contracts/src/repository.ts +64 -0
- package/packages/agentsam-contracts/src/tools.ts +98 -0
- package/packages/agentsam-contracts/test/contracts.test.mjs +16 -0
- package/packages/agentsam-contracts/test/errors-catalog.test.mjs +25 -0
- package/packages/agentsam-contracts/test/repository.test.mjs +18 -0
- package/packages/agentsam-contracts/tsconfig.json +12 -0
- package/packages/agentsam-errors/package.json +17 -0
- package/packages/agentsam-errors/src/adapters/anthropic.js +15 -0
- package/packages/agentsam-errors/src/adapters/cad.js +109 -0
- package/packages/agentsam-errors/src/adapters/cloudflare.js +21 -0
- package/packages/agentsam-errors/src/adapters/cursor.js +4 -0
- package/packages/agentsam-errors/src/adapters/device.js +38 -0
- package/packages/agentsam-errors/src/adapters/docker.js +28 -0
- package/packages/agentsam-errors/src/adapters/gcp.js +14 -0
- package/packages/agentsam-errors/src/adapters/github.js +22 -0
- package/packages/agentsam-errors/src/adapters/google.js +17 -0
- package/packages/agentsam-errors/src/adapters/index.js +55 -0
- package/packages/agentsam-errors/src/adapters/internal.js +40 -0
- package/packages/agentsam-errors/src/adapters/oauth.js +24 -0
- package/packages/agentsam-errors/src/adapters/openai.js +17 -0
- package/packages/agentsam-errors/src/adapters/process.js +35 -0
- package/packages/agentsam-errors/src/adapters/provider.js +114 -0
- package/packages/agentsam-errors/src/envelope.js +186 -0
- package/packages/agentsam-errors/src/error.js +24 -0
- package/packages/agentsam-errors/src/fingerprint.js +35 -0
- package/packages/agentsam-errors/src/index.js +10 -0
- package/packages/agentsam-errors/src/normalize.js +30 -0
- package/packages/agentsam-errors/src/redaction.js +52 -0
- package/packages/agentsam-errors/src/render.js +16 -0
- package/packages/agentsam-errors/src/retry.js +10 -0
- package/packages/agentsam-errors/src/transport.js +52 -0
- package/packages/agentsam-errors/src/vocabulary.js +320 -0
- package/packages/agentsam-errors/test/envelope.test.mjs +70 -0
- package/packages/agentsam-errors/test/providers.test.mjs +99 -0
- package/packages/agentsam-errors/test/redaction.test.mjs +21 -0
- package/packages/agentsam-errors/test/runtime-cad-device.test.mjs +76 -0
- package/packages/agentsam-errors/test/transport.test.mjs +46 -0
- package/packages/agentsam-knowledge/package.json +15 -0
- package/packages/agentsam-knowledge/src/autorag/index.js +64 -0
- package/packages/agentsam-knowledge/src/backends/index.js +26 -0
- package/packages/agentsam-knowledge/src/index.js +3 -0
- package/packages/agentsam-knowledge/src/providers/index.js +95 -0
- package/packages/agentsam-knowledge/test/autorag.test.mjs +42 -0
- package/packages/agentsam-nav/README.md +40 -0
- package/packages/agentsam-nav/dist/Nav.d.ts +43 -0
- package/packages/agentsam-nav/dist/NavCollapsible.d.ts +10 -0
- package/packages/agentsam-nav/dist/NavContent.d.ts +2 -0
- package/packages/agentsam-nav/dist/NavFooter.d.ts +2 -0
- package/packages/agentsam-nav/dist/NavGroup.d.ts +3 -0
- package/packages/agentsam-nav/dist/NavHeader.d.ts +2 -0
- package/packages/agentsam-nav/dist/NavMenu.d.ts +22 -0
- package/packages/agentsam-nav/dist/NavProvider.d.ts +35 -0
- package/packages/agentsam-nav/dist/NavTrigger.d.ts +2 -0
- package/packages/agentsam-nav/dist/Surfaces.d.ts +41 -0
- package/packages/agentsam-nav/dist/browser.d.ts +11 -0
- package/packages/agentsam-nav/dist/browser.global.js +132 -0
- package/packages/agentsam-nav/dist/browser.js +613 -0
- package/packages/agentsam-nav/dist/contracts.d.ts +76 -0
- package/packages/agentsam-nav/dist/index.d.ts +11 -0
- package/packages/agentsam-nav/dist/index.js +614 -0
- package/packages/agentsam-nav/dist/legacy.css +83 -0
- package/packages/agentsam-nav/dist/shell.css +113 -0
- package/packages/agentsam-nav/dist/theme.css +113 -0
- package/packages/agentsam-nav/dist/utils.d.ts +1 -0
- package/packages/agentsam-nav/package.json +43 -0
- package/packages/agentsam-queue-control/README.md +107 -0
- package/packages/agentsam-queue-control/package.json +31 -0
- package/packages/agentsam-queue-control/schemas/job-envelope.schema.json +56 -0
- package/packages/agentsam-queue-control/src/adapters/cloudflare.js +152 -0
- package/packages/agentsam-queue-control/src/adapters/memory.js +38 -0
- package/packages/agentsam-queue-control/src/contracts.js +130 -0
- package/packages/agentsam-queue-control/src/control-plane.js +141 -0
- package/packages/agentsam-queue-control/src/dispatch.js +43 -0
- package/packages/agentsam-queue-control/src/index.js +7 -0
- package/packages/agentsam-queue-control/src/policy.js +88 -0
- package/packages/agentsam-queue-control/src/topology.js +66 -0
- package/packages/agentsam-queue-control/test/queue-control.test.mjs +100 -0
- package/packages/agentsam-repository/src/goap.js +718 -0
- package/packages/agentsam-repository/src/goap.js.bak-20260922011251 +670 -0
- package/packages/agentsam-repository/src/index.js +5 -0
- package/packages/agentsam-repository/src/merkle/cloudflare-persistence.js +2 -2
- package/packages/agentsam-repository/src/tickets.js +184 -0
- package/packages/agentsam-repository/src/work-tracking.js +199 -0
- package/packages/agentsam-repository/src/workspace-state.js +176 -0
- package/packages/agentsam-repository/test/goap-tickets-workspace-state.test.mjs +152 -0
- package/packages/connectors/cloudflare/src/index.js +297 -15
- package/packages/connectors/cloudflare/src/owner.js +43 -40
- package/packages/connectors/cloudflare/src/routes.js +167 -26
- package/packages/connectors/cloudflare/tests/connector.test.mjs +112 -2
- package/packages/heuristic-theme/README.md +11 -0
- package/packages/heuristic-theme/package.json +25 -0
- package/packages/heuristic-theme/src/index.js +2 -0
- package/packages/heuristic-theme/src/registry.js +36 -0
- package/packages/heuristic-theme/src/theme.js +41 -0
- package/packages/heuristic-theme/test/theme.test.mjs +26 -0
- package/packages/identity/.agentsam/features/oauth-login-portal/agentsam.feature.json +64 -0
- package/packages/identity/.agentsam/features/oauth-login-portal/providers.json +96 -0
- package/packages/identity/.agentsam/features/oauth-login-portal/resources.json +47 -0
- package/packages/identity/.agentsam/features/oauth-login-portal/routes.json +74 -0
- package/packages/identity/.agentsam/features/oauth-login-portal/ui.json +35 -0
- package/packages/identity/package.json +4 -3
- package/packages/identity/src/adapters/cloudflare-d1/ids.js +4 -0
- package/packages/identity/src/adapters/cloudflare-d1/index.js +39 -1
- package/packages/identity/src/contracts/auth-config.js +23 -40
- package/packages/identity/src/contracts/provider.js +3 -2
- package/packages/identity/src/frontend/auth-portal/pages/login.html +7 -0
- package/packages/identity/src/index.js +2 -0
- package/packages/identity/src/oauth/credentials.js +13 -0
- package/packages/identity/src/oauth/iam-platform.js +5 -5
- package/packages/identity/src/providers/cloudflare/index.js +19 -0
- package/packages/identity/src/providers/cloudflare/mapper.js +21 -0
- package/packages/identity/src/providers/cloudflare/oauth.js +48 -0
- package/packages/identity/src/providers/cloudflare/profile.js +28 -0
- package/packages/identity/src/providers/index.js +18 -2
- package/packages/identity/src/server/identity-service.js +13 -3
- package/packages/identity/src/server/worker-router.js +63 -8
- package/packages/identity/tests/auth-config.test.mjs +14 -23
- package/packages/identity/tests/identity-service.test.mjs +15 -0
- package/packages/providers/completeful/README.md +38 -0
- package/packages/providers/completeful/package.json +29 -0
- package/packages/providers/completeful/src/client.js +162 -0
- package/packages/providers/completeful/src/events.js +49 -0
- package/packages/providers/completeful/src/index.js +22 -0
- package/packages/providers/completeful/src/tools.js +579 -0
- package/packages/providers/completeful/src/webhooks.js +55 -0
- package/packages/providers/completeful/test/provider.test.mjs +196 -0
- package/packages/theme-church-site/README.md +10 -0
- package/packages/theme-church-site/package.json +30 -0
- package/packages/theme-church-site/src/harvest.json +14 -0
- package/packages/theme-church-site/src/index.js +84 -0
- package/packages/theme-church-site/test/theme.test.mjs +19 -0
- package/packages/theme-companions-site/README.md +10 -0
- package/packages/theme-companions-site/package.json +30 -0
- package/packages/theme-companions-site/src/harvest.json +12 -0
- package/packages/theme-companions-site/src/index.js +82 -0
- package/packages/theme-companions-site/test/theme.test.mjs +19 -0
- package/packages/theme-floors-site/README.md +10 -0
- package/packages/theme-floors-site/package.json +30 -0
- package/packages/theme-floors-site/src/harvest.json +14 -0
- package/packages/theme-floors-site/src/index.js +82 -0
- package/packages/theme-floors-site/test/theme.test.mjs +19 -0
- package/packages/theme-fuelnfree-site/README.md +10 -0
- package/packages/theme-fuelnfree-site/package.json +30 -0
- package/packages/theme-fuelnfree-site/src/harvest.json +13 -0
- package/packages/theme-fuelnfree-site/src/index.js +81 -0
- package/packages/theme-fuelnfree-site/test/theme.test.mjs +19 -0
- package/packages/theme-handyman-site/README.md +10 -0
- package/packages/theme-handyman-site/package.json +30 -0
- package/packages/theme-handyman-site/src/harvest.json +10 -0
- package/packages/theme-handyman-site/src/index.js +83 -0
- package/packages/theme-handyman-site/test/theme.test.mjs +19 -0
- package/packages/theme-insurance-site/README.md +10 -0
- package/packages/theme-insurance-site/package.json +30 -0
- package/packages/theme-insurance-site/src/harvest.json +10 -0
- package/packages/theme-insurance-site/src/index.js +83 -0
- package/packages/theme-insurance-site/test/theme.test.mjs +19 -0
- package/packages/theme-shinshu-site/README.md +10 -0
- package/packages/theme-shinshu-site/package.json +30 -0
- package/packages/theme-shinshu-site/src/harvest.json +12 -0
- package/packages/theme-shinshu-site/src/index.js +83 -0
- package/packages/theme-shinshu-site/test/theme.test.mjs +19 -0
- package/packages/work-graph/README.md +49 -0
- package/packages/work-graph/package.json +19 -0
- package/packages/work-graph/prototype/README.md +32 -0
- package/packages/work-graph/prototype/data/demo-workgraph.js +47 -0
- package/packages/work-graph/prototype/renderers/timeline-model.js +21 -0
- package/packages/work-graph/prototype/renderers/workgraph-runtime.js +38 -0
- package/packages/work-graph/prototype-gnantt.html +655 -0
- package/packages/work-graph/src/adapters/business/projects.js +23 -0
- package/packages/work-graph/src/adapters/engineering/git.js +22 -0
- package/packages/work-graph/src/adapters/engineering/mcp.js +22 -0
- package/packages/work-graph/src/index.js +76 -0
- package/packages/work-graph/src/renderers/gantt-model.js +11 -0
- package/packages/work-graph/src/renderers/timeline.js +16 -0
- package/packages/work-graph/test/work-graph.test.mjs +37 -0
- package/protocol/README.md +12 -0
- package/protocol/capabilities/cloudflare-cpu-audit-input.schema.json +30 -6
- package/protocol/capabilities/cloudflare-cpu-profile-input.schema.json +18 -4
- package/protocol/capabilities/cloudflare-wrangler-native-input.schema.json +59 -10
- package/protocol/capabilities/knowledge-search-input.schema.json +35 -0
- package/protocol/capabilities/manifest.json +163 -1
- package/protocol/capabilities/repository-audit-input.schema.json +39 -6
- package/protocol/capabilities/repository-snapshot-input.schema.json +15 -3
- package/protocol/capabilities/terminal-exec-input.schema.json +55 -0
- package/protocol/commerce/catalog.v1.json +50 -0
- package/protocol/errors/error-catalog.json +164 -0
- package/protocol/errors/error-envelope.schema.json +544 -0
- package/protocol/features/README.md +57 -0
- package/protocol/features/agentsam.feature.v1.schema.json +290 -0
- package/protocol/features/catalog.json +65 -0
- package/protocol/features/examples/provider-resend.feature.json +17 -0
- package/protocol/go/agentsam-go-runtime.v1.json +17 -0
- package/protocol/knowledge/LIFECYCLE.md +140 -0
- package/protocol/knowledge/README.md +4 -0
- package/protocol/presets/catalog.json +128 -19
- package/protocol/rpc/v1/errors.proto +24 -0
- package/protocol/theme-refinery/d1-reconciliation.md +48 -0
- package/protocol/theme-refinery/product-metadata.schema.json +27 -0
- package/protocol/theme-refinery/receipts/products-registry-batch-20260924.json +318 -0
- package/protocol/theme-refinery/receipts/theme-harvest-20260924.json +472 -0
- package/protocol/theme-refinery/reconciliation-receipt.schema.json +83 -0
- package/services/cad/server.py +249 -2
- package/src/agent/capability-adapter.js +15 -5
- package/src/agent/index.js +2 -1
- package/src/agent/model-turn.js +51 -0
- package/src/agent/responses-runner.js +304 -38
- package/src/capabilities/index.js +1 -0
- package/src/capabilities/terminal-exec.js +78 -0
- package/src/cli.js +129 -31
- package/src/cloudflare/index.js +6 -0
- package/src/cloudflare/runtime-status.js +393 -0
- package/src/cloudflare/wrangler.js +17 -1
- package/src/commands/account-auth.js +1 -1
- package/src/commands/app.js +106 -0
- package/src/commands/autorag.js +77 -0
- package/src/commands/brand.js +293 -0
- package/src/commands/cad.js +1 -0
- package/src/commands/connections.js +126 -29
- package/src/commands/context-economics.js +8 -0
- package/src/commands/db.js +7 -0
- package/src/commands/deploy.js +2 -2
- package/src/commands/env.js +5 -4
- package/src/commands/eval.js +209 -14
- package/src/commands/go.js +365 -0
- package/src/commands/goap.js +176 -0
- package/src/commands/identity-init.js +27 -3
- package/src/commands/identity-protocol.js +195 -0
- package/src/commands/knowledge.js +64 -5
- package/src/commands/mcp.js +341 -0
- package/src/commands/merkle-persist.js +3 -1
- package/src/commands/merkle.js +18 -3
- package/src/commands/models.js +109 -23
- package/src/commands/plugins.js +175 -0
- package/src/commands/preferences.js +28 -8
- package/src/commands/product.js +53 -10
- package/src/commands/providers.js +389 -0
- package/src/commands/resume.js +9 -5
- package/src/commands/runtime-status.js +154 -0
- package/src/commands/shell.js +469 -54
- package/src/commands/status.js +82 -7
- package/src/commands/tunnel.js +228 -195
- package/src/commands/whoami.js +115 -22
- package/src/context/budget.js +6 -0
- package/src/context/index.js +2 -1
- package/src/context/project-card.js +75 -0
- package/src/errors/contract.js +34 -211
- package/src/errors/diagnostic.js +95 -50
- package/src/errors/index.js +6 -13
- package/src/eval/index.js +6 -0
- package/src/eval/live.js +439 -0
- package/src/features/resolve.js +242 -0
- package/src/go/build.js +108 -0
- package/src/go/cloudflare.js +207 -0
- package/src/go/contract.js +139 -0
- package/src/go/discover.js +96 -0
- package/src/go/index.js +13 -0
- package/src/go/receipts.js +77 -0
- package/src/go/registry.js +223 -0
- package/src/go/verify.js +31 -0
- package/src/index.js +100 -1
- package/src/knowledge/config.js +13 -5
- package/src/knowledge/engine.js +23 -8
- package/src/knowledge/index.js +1 -0
- package/src/knowledge/lane-registry.js +46 -0
- package/src/knowledge/providers/gemini.js +48 -8
- package/src/knowledge/service/client.js +13 -1
- package/src/knowledge/service/grpc-codec.js +156 -53
- package/src/knowledge/service/job-engine.js +133 -11
- package/src/knowledge/service/job-worker.js +34 -5
- package/src/knowledge/service/server.js +10 -1
- package/src/knowledge/store-discovery.js +77 -0
- package/src/lib/account-session.js +100 -25
- package/src/lib/agent-instructions.js +3 -0
- package/src/lib/auth.js +318 -51
- package/src/lib/cad/blender.js +42 -3
- package/src/lib/cad/d1-project-store.js +43 -0
- package/src/lib/cad/discovery.js +616 -0
- package/src/lib/cad/docker-executor.js +352 -0
- package/src/lib/cad/freecad.js +253 -0
- package/src/lib/cad/index.js +40 -0
- package/src/lib/cad/openscad.js +198 -0
- package/src/lib/cad/project-cli.d.ts +2 -0
- package/src/lib/cad/project-cli.js +21 -0
- package/src/lib/cad/project-contract.d.ts +3 -0
- package/src/lib/cad/project-contract.js +1 -0
- package/src/lib/cad/project-recipe.js +30 -0
- package/src/lib/cad/project-runtime.d.ts +2 -0
- package/src/lib/cad/project-runtime.js +53 -0
- package/src/lib/cad/project-store.js +25 -0
- package/src/lib/cad/project-workflows.js +31 -0
- package/src/lib/cad-docker.js +21 -2
- package/src/lib/cli-preferences.js +17 -2
- package/src/lib/core-client.js +97 -33
- package/src/lib/deploy/local-studio.js +6 -5
- package/src/lib/detect-context.js +14 -16
- package/src/lib/dockerize.js +3 -0
- package/src/lib/execution-approvals.js +1 -0
- package/src/lib/identity-scaffold.js +4 -1
- package/src/lib/knowledge-docker.js +9 -1
- package/src/lib/local-sessions.js +124 -34
- package/src/lib/local-status.js +35 -14
- package/src/lib/project-config.js +9 -0
- package/src/lib/provider-credentials.js +233 -55
- package/src/lib/scaffold/resolve-cloudflare-account.js +37 -0
- package/src/lib/scaffold/wizards/cms.js +2 -8
- package/src/lib/scaffold/wizards/worker-api.js +2 -8
- package/src/lib/slash-commands.js +10 -2
- package/src/local/migrations.js +109 -1
- package/src/local/runtime-store.js +31 -5
- package/src/local/sqlite.js +36 -1
- package/src/mcp/authority.js +348 -0
- package/src/mcp/client-adapters.js +431 -0
- package/src/mcp/client.js +172 -0
- package/src/mcp/index.js +47 -0
- package/src/mcp/telemetry.js +103 -0
- package/src/models/catalog.js +329 -51
- package/src/models/discovery.js +59 -4
- package/src/models/index.js +2 -0
- package/src/models/inventory-core.js +182 -0
- package/src/models/policy.js +20 -0
- package/src/plugins/cloudflare.js +103 -0
- package/src/plugins/completeful.js +69 -0
- package/src/plugins/contracts.js +86 -0
- package/src/plugins/index.js +11 -0
- package/src/plugins/registry.js +207 -0
- package/src/plugins/resource-scope.js +45 -0
- package/src/plugins/runtime.js +118 -0
- package/src/plugins/vectorize.js +31 -0
- package/src/progression/engine.js +171 -0
- package/src/progression/index.js +1 -0
- package/src/providers/cloudflare-chat.js +1 -1
- package/src/providers/gemini-generate-content.js +2 -1
- package/src/providers/openai-responses.js +45 -16
- package/src/providers/tool-schema.js +85 -0
- package/src/repository/index.js +29 -0
- package/src/rpc/generated/errors_pb.js +961 -1
- package/src/security/local-vault.js +415 -0
- package/src/security/process.js +20 -4
- package/src/status/actions.js +192 -0
- package/src/tools/hydrate.js +5 -1
- package/src/tools/index.js +7 -0
- package/src/tools/redact.js +64 -0
- package/src/tools/registry.js +83 -0
- package/src/tools/runtime.js +257 -0
- package/src/tools/search.js +21 -8
- package/src/ui/ansi.js +117 -3
- package/src/ui/cli/activity.js +76 -7
- package/src/ui/cli/footer.js +110 -9
- package/src/ui/cli/help.js +14 -5
- package/src/ui/wireframes.js +166 -0
- package/test/account-session.test.mjs +76 -11
- package/test/apps-scaffold-contract.test.mjs +58 -0
- package/test/capabilities.test.mjs +1 -1
- package/test/cli/eval-live.test.mjs +219 -0
- package/test/cli/go.test.mjs +71 -0
- package/test/cli/local-vault-and-provider-setup.test.mjs +268 -0
- package/test/cli/mcp-client.test.mjs +245 -0
- package/test/cli/runtime-ui.test.mjs +69 -2
- package/test/cli/status-actions.test.mjs +88 -0
- package/test/cli/wireframes-go-registry.test.mjs +54 -0
- package/test/cloudflare-connector.test.mjs +3 -6
- package/test/cloudflare-runtime.test.mjs +8 -1
- package/test/context.test.mjs +15 -0
- package/test/error-diagnostics.test.mjs +9 -0
- package/test/fixtures/cad/courtyard-house.json +208 -0
- package/test/go/cloudflare-probe.test.mjs +53 -0
- package/test/integration/app-command.test.mjs +55 -0
- package/test/integration/cad-discovery.test.mjs +158 -0
- package/test/integration/cad-docker-executor.test.mjs +249 -0
- package/test/integration/cad-execution.test.mjs +184 -0
- package/test/integration/cad-project-app.test.mjs +31 -0
- package/test/integration/cad-project-d1.test.mjs +25 -0
- package/test/integration/cad-project-native.test.mjs +24 -0
- package/test/integration/cad-project-workflow.test.mjs +9 -0
- package/test/integration/cad-project.test.mjs +82 -0
- package/test/integration/cli-help.test.mjs +4 -0
- package/test/integration/cms-site-tenancy-contract.test.mjs +256 -0
- package/test/integration/core-client.integration.test.mjs +55 -0
- package/test/integration/error-runtime-rpc.test.mjs +71 -0
- package/test/integration/features-resolve.test.mjs +128 -0
- package/test/integration/install-script.test.mjs +107 -0
- package/test/integration/inventory-core.test.mjs +53 -0
- package/test/integration/plugins-cloudflare.integration.test.mjs +28 -0
- package/test/integration/provider-env-cli.test.mjs +1 -1
- package/test/integration/runtime-migrations.test.mjs +6 -1
- package/test/integration/runtime-status.integration.test.mjs +211 -0
- package/test/integration/scaffold-cloudflare.test.mjs +46 -0
- package/test/integration/storage-architecture.test.mjs +154 -0
- package/test/integration/store-discovery.test.mjs +44 -0
- package/test/integration/terminal-exec.integration.test.mjs +86 -0
- package/test/integration/tool-runtime.test.mjs +124 -0
- package/test/integration/tool-schema.test.mjs +59 -0
- package/test/knowledge-service.test.mjs +4 -0
- package/test/local-sessions.test.mjs +11 -2
- package/test/local-studio-deploy.test.mjs +1 -1
- package/test/model-catalog.test.mjs +28 -1
- package/test/models.test.mjs +159 -5
- package/test/openai-responses.test.mjs +60 -0
- package/test/provider-credentials.test.mjs +15 -5
- package/test/responses-runner.test.mjs +321 -6
- package/test/sdk-worker-contract.test.mjs +5 -1
- package/test/shell.test.mjs +149 -4
- package/test/smoke.mjs +4 -3
- package/test/tools-search.test.mjs +33 -0
- package/test/unit/website-assets-bindings.test.mjs +60 -0
- package/test/whoami-resume.test.mjs +87 -7
- package/examples/gorilla-shell/README.md +0 -15
- package/src/lib/gorilla-template.js +0 -54
- package/src/lib/prompt-byok.js +0 -57
- package/src/lib/save-sdk-token.js +0 -19
- package/templates/gorilla-shell/App.tsx +0 -639
- package/templates/gorilla-shell/README.md +0 -34
- package/templates/gorilla-shell/index.html +0 -15
- package/templates/gorilla-shell/main.jsx +0 -9
- package/templates/gorilla-shell/package.json +0 -19
- package/templates/gorilla-shell/vite.config.js +0 -21
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import test from 'node:test';
|
|
3
|
+
import {
|
|
4
|
+
classifyAnthropicFailure,
|
|
5
|
+
classifyCloudflareFailure,
|
|
6
|
+
classifyCursorFailure,
|
|
7
|
+
classifyGeminiFailure,
|
|
8
|
+
classifyGitHubFailure,
|
|
9
|
+
classifyGcpFailure,
|
|
10
|
+
classifyOpenAIFailure,
|
|
11
|
+
} from '../src/index.js';
|
|
12
|
+
|
|
13
|
+
test('missing OpenAI key is user configuration, not an OpenAI outage', () => {
|
|
14
|
+
const error = classifyOpenAIFailure({ credential_state: 'missing' });
|
|
15
|
+
assert.equal(error.reason, 'provider_credential_missing');
|
|
16
|
+
assert.equal(error.source.kind, 'user');
|
|
17
|
+
assert.equal(error.resolution_owner, 'user');
|
|
18
|
+
assert.equal(error.provider, 'openai');
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
test('OpenAI rejected key and exhausted budget stay distinct', () => {
|
|
22
|
+
const rejected = classifyOpenAIFailure({ status: 401, code: 'invalid_api_key', message: 'Incorrect API key' });
|
|
23
|
+
const budget = classifyOpenAIFailure({ status: 429, code: 'project_spend_limit_exceeded', message: 'budget reached' });
|
|
24
|
+
assert.equal(rejected.reason, 'provider_credential_invalid');
|
|
25
|
+
assert.equal(rejected.source.kind, 'provider');
|
|
26
|
+
assert.equal(rejected.resolution_owner, 'user');
|
|
27
|
+
assert.equal(budget.reason, 'provider_budget_exhausted');
|
|
28
|
+
assert.equal(budget.retryable, false);
|
|
29
|
+
assert.equal(budget.remediation.action, 'add_budget');
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
test('the same provider budget response is internal when the provider account is AgentSam-owned', () => {
|
|
33
|
+
const error = classifyOpenAIFailure(
|
|
34
|
+
{ status: 429, code: 'project_spend_limit_exceeded', message: 'budget reached' },
|
|
35
|
+
{ credential_owner: 'agentsam' },
|
|
36
|
+
);
|
|
37
|
+
assert.equal(error.reason, 'provider_budget_exhausted');
|
|
38
|
+
assert.equal(error.source.kind, 'provider');
|
|
39
|
+
assert.equal(error.source.name, 'openai');
|
|
40
|
+
assert.equal(error.resolution_owner, 'agentsam');
|
|
41
|
+
assert.equal(error.severity, 'blocking_internal');
|
|
42
|
+
assert.equal(error.remediation.action, 'inspect_platform');
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
test('organization-owned provider account failures route to the organization administrator', () => {
|
|
46
|
+
const error = classifyGeminiFailure(
|
|
47
|
+
{ status_name: 'RESOURCE_EXHAUSTED', message: 'Quota exceeded' },
|
|
48
|
+
{ account_owner: 'organization' },
|
|
49
|
+
);
|
|
50
|
+
assert.equal(error.reason, 'provider_quota_exhausted');
|
|
51
|
+
assert.equal(error.resolution_owner, 'organization_admin');
|
|
52
|
+
assert.equal(error.remediation.action, 'contact_organization_admin');
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
test('provider invalid request can be attributed to AgentSam when AgentSam authored it', () => {
|
|
56
|
+
const error = classifyOpenAIFailure({ status: 400, code: 'invalid_request_error', message: 'invalid generated request' }, { request_origin: 'agentsam' });
|
|
57
|
+
assert.equal(error.reason, 'provider_request_invalid');
|
|
58
|
+
assert.equal(error.source.kind, 'provider');
|
|
59
|
+
assert.equal(error.resolution_owner, 'agentsam');
|
|
60
|
+
assert.equal(error.severity, 'blocking_internal');
|
|
61
|
+
assert.equal(error.remediation.action, 'inspect_platform');
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
test('Anthropic overload is transient provider ownership', () => {
|
|
65
|
+
const error = classifyAnthropicFailure({ status: 529, type: 'overloaded_error', message: 'Overloaded' });
|
|
66
|
+
assert.equal(error.reason, 'provider_overloaded');
|
|
67
|
+
assert.equal(error.retryable, true);
|
|
68
|
+
assert.equal(error.resolution_owner, 'provider');
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
test('Gemini resource exhaustion is classified separately from invalid API key', () => {
|
|
72
|
+
const quota = classifyGeminiFailure({ status: 429, status_name: 'RESOURCE_EXHAUSTED', message: 'Quota exceeded' });
|
|
73
|
+
const key = classifyGeminiFailure({ status: 400, message: 'API key not valid. Please pass a valid API key.' });
|
|
74
|
+
assert.equal(quota.reason, 'provider_quota_exhausted');
|
|
75
|
+
assert.equal(quota.resolution_owner, 'user');
|
|
76
|
+
assert.equal(key.reason, 'provider_credential_invalid');
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
test('Cursor uses the generic provider contract rather than a parallel vocabulary', () => {
|
|
80
|
+
const error = classifyCursorFailure({ status: 503, message: 'Service unavailable' });
|
|
81
|
+
assert.equal(error.reason, 'provider_unavailable');
|
|
82
|
+
assert.equal(error.provider, 'cursor');
|
|
83
|
+
assert.equal(error.retryable, true);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
test('GitHub differentiates installation, scopes and rate limits', () => {
|
|
87
|
+
assert.equal(classifyGitHubFailure({ installation_state: 'missing' }).reason, 'github_installation_missing');
|
|
88
|
+
assert.equal(classifyGitHubFailure({ status: 403, message: 'Resource not accessible by integration' }).reason, 'github_scope_insufficient');
|
|
89
|
+
assert.equal(classifyGitHubFailure({ status: 403, message: 'API rate limit exceeded', headers: { 'x-ratelimit-remaining': '0' } }).reason, 'github_rate_limited');
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
test('Cloudflare and GCP retain infrastructure-specific actionable reasons', () => {
|
|
93
|
+
const tunnel = classifyCloudflareFailure({ status: 530, cloudflare_code: 1033, message: 'tunnel unavailable' });
|
|
94
|
+
const quota = classifyGcpFailure({ status_name: 'RESOURCE_EXHAUSTED', message: 'Compute Engine quota exceeded' }, { resource: { type: 'vm', id: 'vm_1' } });
|
|
95
|
+
assert.equal(tunnel.reason, 'tunnel_connector_unavailable');
|
|
96
|
+
assert.equal(tunnel.remediation.action, 'reconnect_service');
|
|
97
|
+
assert.equal(quota.reason, 'gcp_quota_exhausted');
|
|
98
|
+
assert.equal(quota.remediation.action, 'increase_quota');
|
|
99
|
+
});
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import test from 'node:test';
|
|
3
|
+
import { redactErrorValue, redactString } from '../src/index.js';
|
|
4
|
+
|
|
5
|
+
test('redaction never leaks machine credential values', () => {
|
|
6
|
+
const leaked = redactString('key=aak_liveMachineKey12345678 secret=sk-testKey1234567890');
|
|
7
|
+
assert.ok(!leaked.includes('aak_liveMachineKey12345678'), 'agent API key must be redacted');
|
|
8
|
+
assert.ok(!leaked.includes('sk-testKey1234567890'), 'provider key must be redacted');
|
|
9
|
+
assert.ok(leaked.includes('[REDACTED]'), 'redaction marker must be present');
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
test('redaction strips credential-named object keys at any depth', () => {
|
|
13
|
+
const value = redactErrorValue({
|
|
14
|
+
provider: 'openai',
|
|
15
|
+
credentials: { api_key: 'sk-testKey1234567890', mode: 'byok' },
|
|
16
|
+
nested: { list: [{ authorization: 'Bearer abcdef123456' }] },
|
|
17
|
+
});
|
|
18
|
+
assert.equal(value.credentials, '[REDACTED]', 'credential-named objects redact wholesale');
|
|
19
|
+
assert.equal(value.nested.list[0].authorization, '[REDACTED]');
|
|
20
|
+
assert.equal(value.provider, 'openai');
|
|
21
|
+
});
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import test from 'node:test';
|
|
3
|
+
import {
|
|
4
|
+
classifyBlenderFailure,
|
|
5
|
+
classifyDeviceFailure,
|
|
6
|
+
classifyDockerFailure,
|
|
7
|
+
classifyFreeCadFailure,
|
|
8
|
+
classifyInternalFailure,
|
|
9
|
+
classifyMeshyFailure,
|
|
10
|
+
classifyOpenScadFailure,
|
|
11
|
+
classifyProcessFailure,
|
|
12
|
+
} from '../src/index.js';
|
|
13
|
+
|
|
14
|
+
test('Docker daemon absence is user-fixable runtime configuration', () => {
|
|
15
|
+
const error = classifyDockerFailure({ stderr: 'Cannot connect to the Docker daemon. Is the docker daemon running?' }, { stage: 'run' });
|
|
16
|
+
assert.equal(error.reason, 'docker_daemon_unavailable');
|
|
17
|
+
assert.equal(error.source.kind, 'runtime');
|
|
18
|
+
assert.equal(error.resolution_owner, 'user');
|
|
19
|
+
assert.equal(error.remediation.action, 'restart_runtime');
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
test('process timeout and missing executable remain separate', () => {
|
|
23
|
+
const timeout = classifyProcessFailure({ timeout: true, code: 'PROCESS_TIMEOUT' });
|
|
24
|
+
const missing = classifyProcessFailure({ spawn_failed: true, code: 'ENOENT' }, { tool: 'git' });
|
|
25
|
+
assert.equal(timeout.reason, 'execution_timeout');
|
|
26
|
+
assert.equal(timeout.retryable, true);
|
|
27
|
+
assert.equal(missing.reason, 'process_spawn_failed');
|
|
28
|
+
assert.equal(missing.tool, 'git');
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
test('FreeCAD native exception hierarchy survives normalization', () => {
|
|
32
|
+
assert.equal(classifyFreeCadFailure({ exception_type: 'Part::BooleanException' }, { stage: 'boolean' }).reason, 'cad_boolean_failed');
|
|
33
|
+
assert.equal(classifyFreeCadFailure({ exception_type: 'Part::NullShapeException' }).reason, 'cad_null_shape');
|
|
34
|
+
assert.equal(classifyFreeCadFailure({ exception_type: 'Base::CADKernelError' }).reason, 'cad_kernel_failed');
|
|
35
|
+
assert.equal(classifyFreeCadFailure({ exception_type: 'Base::MemoryException' }).reason, 'cad_memory_exhausted');
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
test('OpenSCAD parser and geometry failures are distinct', () => {
|
|
39
|
+
assert.equal(classifyOpenScadFailure({ stderr: 'ERROR: Parser error: syntax error' }, { stage: 'parse' }).reason, 'cad_source_parse_failed');
|
|
40
|
+
assert.equal(classifyOpenScadFailure({ stderr: 'WARNING: No top level geometry to render' }, { stage: 'build' }).reason, 'cad_geometry_invalid');
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
test('Blender discovery and render environment failures are actionable', () => {
|
|
44
|
+
const missing = classifyBlenderFailure({ installed: false }, { stage: 'discovery' });
|
|
45
|
+
const gpu = classifyBlenderFailure({ message: 'No compatible GPU available' }, { stage: 'render' });
|
|
46
|
+
assert.equal(missing.reason, 'cad_tool_not_installed');
|
|
47
|
+
assert.equal(missing.remediation.action, 'install_dependency');
|
|
48
|
+
assert.equal(gpu.reason, 'cad_gpu_unavailable');
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
test('Meshy is a CAD-domain provider and preserves provider quota semantics', () => {
|
|
52
|
+
const error = classifyMeshyFailure({ status: 429, message: 'quota exceeded' });
|
|
53
|
+
assert.equal(error.domain, 'cad');
|
|
54
|
+
assert.equal(error.tool, 'meshy');
|
|
55
|
+
assert.equal(error.reason, 'provider_quota_exhausted');
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
test('device user failures and InnerAnimalMedia control-plane defects cannot be confused', () => {
|
|
59
|
+
const unenrolled = classifyDeviceFailure({ state: 'not_enrolled' });
|
|
60
|
+
const internal = classifyDeviceFailure({ state: 'identity_resolution_failed', trace_id: 'tr_1' });
|
|
61
|
+
assert.equal(unenrolled.reason, 'device_not_enrolled');
|
|
62
|
+
assert.equal(unenrolled.resolution_owner, 'user');
|
|
63
|
+
assert.equal(unenrolled.remediation.action, 'reenroll_device');
|
|
64
|
+
assert.equal(internal.reason, 'identity_resolution_failed');
|
|
65
|
+
assert.equal(internal.source.kind, 'agentsam');
|
|
66
|
+
assert.equal(internal.source.name, 'inneranimalmedia');
|
|
67
|
+
assert.equal(internal.resolution_owner, 'agentsam');
|
|
68
|
+
assert.equal(internal.severity, 'blocking_internal');
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
test('explicit internal failures are always AgentSam-owned', () => {
|
|
72
|
+
const error = classifyInternalFailure({ code: 'ASSERTION', message: 'bad invariant' }, { reason: 'internal_invariant_violation', system: 'agentsam-sdk' });
|
|
73
|
+
assert.equal(error.resolution_owner, 'agentsam');
|
|
74
|
+
assert.equal(error.source.name, 'agentsam-sdk');
|
|
75
|
+
assert.equal(error.remediation.action, 'inspect_platform');
|
|
76
|
+
});
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import test from 'node:test';
|
|
3
|
+
import {
|
|
4
|
+
createErrorEnvelope,
|
|
5
|
+
fromHttpError,
|
|
6
|
+
retryDelayMs,
|
|
7
|
+
shouldRetry,
|
|
8
|
+
toHttpError,
|
|
9
|
+
} from '../src/index.js';
|
|
10
|
+
|
|
11
|
+
test('HTTP transport preserves the complete canonical envelope', () => {
|
|
12
|
+
const error = createErrorEnvelope({
|
|
13
|
+
reason: 'provider_rate_limited',
|
|
14
|
+
message: 'Slow down',
|
|
15
|
+
source: { kind: 'provider', name: 'openai' },
|
|
16
|
+
provider: 'openai',
|
|
17
|
+
provider_code: 'slow_down',
|
|
18
|
+
request_id: 'req_1',
|
|
19
|
+
retry_after_ms: 2000,
|
|
20
|
+
domain: 'provider',
|
|
21
|
+
});
|
|
22
|
+
const http = toHttpError(error);
|
|
23
|
+
const decoded = fromHttpError({ status: http.status, body: http.body, headers: http.headers });
|
|
24
|
+
assert.deepEqual(decoded, error);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
test('fallback HTTP failures still normalize without provider-specific code', () => {
|
|
28
|
+
const error = fromHttpError({ status: 503, body: { message: 'upstream down' }, provider: 'example' });
|
|
29
|
+
assert.equal(error.code, 'UNAVAILABLE');
|
|
30
|
+
assert.equal(error.reason, 'transport_unreachable');
|
|
31
|
+
assert.equal(error.retryable, true);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
test('retry policy honors provider Retry-After before exponential fallback', () => {
|
|
35
|
+
const explicit = createErrorEnvelope({ reason: 'provider_rate_limited', message: 'wait', retry_after_ms: 2500 });
|
|
36
|
+
const fallback = createErrorEnvelope({ reason: 'provider_unavailable', message: 'down' });
|
|
37
|
+
assert.equal(shouldRetry(explicit), true);
|
|
38
|
+
assert.equal(retryDelayMs(explicit, 4), 2500);
|
|
39
|
+
assert.equal(retryDelayMs(fallback, 3, { baseMs: 500 }), 2000);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
test('fingerprints exclude volatile request ids and messages', () => {
|
|
43
|
+
const a = createErrorEnvelope({ reason: 'provider_unavailable', message: 'first', source: { kind: 'provider', name: 'openai' }, provider: 'openai', request_id: 'req_a' });
|
|
44
|
+
const b = createErrorEnvelope({ reason: 'provider_unavailable', message: 'different wording', source: { kind: 'provider', name: 'openai' }, provider: 'openai', request_id: 'req_b' });
|
|
45
|
+
assert.equal(a.fingerprint, b.fingerprint);
|
|
46
|
+
});
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@inneranimalmedia/agentsam-knowledge",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"description": "Portable AutoRAG discovery, adapter registries, probes, and company routing contracts for AgentSam.",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": "./src/index.js",
|
|
9
|
+
"./autorag": "./src/autorag/index.js",
|
|
10
|
+
"./providers": "./src/providers/index.js",
|
|
11
|
+
"./backends": "./src/backends/index.js"
|
|
12
|
+
},
|
|
13
|
+
"scripts": { "test": "node --test test/*.test.mjs" },
|
|
14
|
+
"engines": { "node": ">=22 <25" }
|
|
15
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { execFileSync } from 'node:child_process';
|
|
4
|
+
import { createProviderRegistry } from '../providers/index.js';
|
|
5
|
+
import { createBackendRegistry } from '../backends/index.js';
|
|
6
|
+
|
|
7
|
+
const clean = value => String(value || '').trim();
|
|
8
|
+
const unique = values => [...new Set(values.filter(Boolean))];
|
|
9
|
+
function git(root, args) { try { return execFileSync('git', args, { cwd: root, encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }).trim() || null; } catch { return null; } }
|
|
10
|
+
function findScopes(root) {
|
|
11
|
+
const candidates = ['packages', 'src', 'services', 'apps', 'docs', 'README.md', 'schema', 'migrations'];
|
|
12
|
+
return candidates.filter(candidate => fs.existsSync(path.join(root, candidate)));
|
|
13
|
+
}
|
|
14
|
+
export async function discoverAutoRag({ root = process.cwd(), env = process.env, companyAdapter = null, fetchImpl } = {}) {
|
|
15
|
+
const isGit = Boolean(git(root, ['rev-parse', '--is-inside-work-tree']));
|
|
16
|
+
const providerRegistry = createProviderRegistry({ gemini: { apiKey: env.GEMINI_API_KEY, fetchImpl }, openai: { apiKey: env.OPENAI_API_KEY, fetchImpl }, ollama: { endpoint: env.OLLAMA_HOST || 'http://127.0.0.1:11434', fetchImpl } });
|
|
17
|
+
const providers = await providerRegistry.capabilities();
|
|
18
|
+
const docker = git(root, ['--version']) && (() => { try { return Boolean(execFileSync('docker', ['--version'], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] })); } catch { return false; } })();
|
|
19
|
+
const repositoryId = git(root, ['config', '--get', 'remote.origin.url']) || `local:${path.basename(root)}`;
|
|
20
|
+
return Object.freeze({
|
|
21
|
+
repository: { root, name: path.basename(root), identity: repositoryId, branch: git(root, ['branch', '--show-current']), revision: git(root, ['rev-parse', 'HEAD']), git: isGit, merkle: fs.existsSync(path.join(root, '.agentsam', 'merkle.json')), semantic_merkle: fs.existsSync(path.join(root, '.agentsam', 'merkle.json')) },
|
|
22
|
+
scopes: findScopes(root),
|
|
23
|
+
capabilities: { sqlite: true, git: isGit, merkle: isGit, structural_indexing: true, local_knowledge_engine: true, repository_intelligence: fs.existsSync(path.join(root, 'packages', 'agentsam-repository')), docker, providers, backends: createBackendRegistry().capabilities(), company_graph: Boolean(companyAdapter?.discover) ? await companyAdapter.discover() : { available: false } },
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
export function recommendAutoRag({ discovery, purpose = 'code', include, provider = 'none', backend = 'local_exact', semantic = false } = {}) {
|
|
27
|
+
const scopes = include?.length ? unique(include) : purpose === 'code' ? discovery.scopes.filter(scope => ['packages', 'src'].includes(scope)).slice(0, 2) : discovery.scopes.filter(scope => /^(docs|README\.md|schema|migrations)$/.test(scope)).slice(0, 3);
|
|
28
|
+
return Object.freeze({ purpose, repositories: [discovery.repository.identity], scope: scopes.length ? scopes : ['.'], evidence: purpose === 'code' ? ['git', 'merkle', 'semantic-metadata', 'ast', 'lexical'] : ['git', 'merkle', 'lexical'], embedding: semantic ? provider : 'none', control_plane: 'local_sqlite', backend, probe: { max_files: 25, max_chunks: 100, max_semantic_queries: 1, paid_embeddings: false }, company_registration: 'disabled' });
|
|
29
|
+
}
|
|
30
|
+
export function safeAutoRagConfig({ existing = {}, recommendation, repositoryId, projectKey } = {}) {
|
|
31
|
+
const config = structuredClone(existing);
|
|
32
|
+
config.version = Math.max(Number(config.version || 1), 2);
|
|
33
|
+
config.repository_id = config.repository_id || repositoryId;
|
|
34
|
+
config.project_key = config.project_key || projectKey || config.repository_id;
|
|
35
|
+
config.scope = { name: config.scope?.name || recommendation.purpose, include: recommendation.scope, exclude: config.scope?.exclude || [] };
|
|
36
|
+
config.chunking = config.chunking || { max_chars: 4000 };
|
|
37
|
+
config.embedding = recommendation.embedding === 'none' ? { provider: 'none', model: 'none', revision: '1', dimensions: 0, parameters: {} } : (config.embedding || { provider: recommendation.embedding, model: recommendation.embedding === 'ollama' ? 'nomic-embed-text' : recommendation.embedding === 'openai' ? 'text-embedding-3-small' : 'gemini-embedding-2', revision: '1', dimensions: 768, parameters: { task: 'code retrieval' } });
|
|
38
|
+
config.storage = config.storage || { driver: 'sqlite' };
|
|
39
|
+
config.lane = { id: config.lane?.id || `${recommendation.purpose}-local`, backend: recommendation.backend, resource: config.lane?.resource || null, binding: config.lane?.binding || null, index: config.lane?.index || null, metric: config.lane?.metric || 'cosine' };
|
|
40
|
+
return config;
|
|
41
|
+
}
|
|
42
|
+
export async function runAutoRagProbe({ root, config, engine, store, embedder = null, semantic = false, maxFiles = 25, maxChunks = 100, query = 'repository overview' } = {}) {
|
|
43
|
+
if (!engine?.planIndex || !engine?.runIndex || !engine?.retrieve) throw new TypeError('autorag_probe_engine_required');
|
|
44
|
+
if (semantic && !embedder) throw new Error('autorag_probe_semantic_adapter_required');
|
|
45
|
+
// Probes are isolated to a dedicated generation scope. A failed or successful
|
|
46
|
+
// probe therefore cannot replace a healthy normal indexing generation.
|
|
47
|
+
const probeConfig = structuredClone(config);
|
|
48
|
+
probeConfig.scope.name = `${config.scope.name}--probe`;
|
|
49
|
+
const plan = await engine.planIndex({ root, config: probeConfig, store, embed: semantic, limits: { maxFiles, maxChunks } });
|
|
50
|
+
if (plan.receipt.files > maxFiles || plan.receipt.chunks > maxChunks) throw new Error('autorag_probe_limits_not_enforced');
|
|
51
|
+
const result = await engine.runIndex({ root, config: probeConfig, store, embed: semantic, embedder, maxInputs: semantic ? maxChunks : 0, maxCharacters: 200000, limits: { maxFiles, maxChunks }, probe: true });
|
|
52
|
+
const contextPack = await engine.retrieve({ store, config: probeConfig, text: query, semantic, embedder, topK: 3, tokenBudget: 1200, generationId: result.generation_id });
|
|
53
|
+
return Object.freeze({ type: 'agentsam_autorag_probe_receipt', stages: ['discovery', 'source_read', 'chunk', 'structural_index', ...(semantic ? ['embed', 'vector_upsert'] : []), 'verify', 'query'], repository: config.repository_id, scope: config.scope, provider: config.embedding.provider, model: config.embedding.model, dimensions: config.embedding.dimensions, backend: config.lane?.backend || 'local_exact', files: result.files, chunks: result.chunks, generation: result.generation_id, git: result.git, merkle_root: result.merkle_root || null, published: result.published, query_result: contextPack.hits.slice(0, 1), paid_embeddings: false });
|
|
54
|
+
}
|
|
55
|
+
export function selectIntentRoute(routes, { repositoryId, accountId, intent } = {}) {
|
|
56
|
+
const matches = (routes || []).filter(route => route?.is_active !== false && route.intent_key === intent);
|
|
57
|
+
return matches.find(route => clean(route.repository_id) === clean(repositoryId) && clean(repositoryId)) || matches.find(route => clean(route.account_id) === clean(accountId) && !clean(route.repository_id)) || matches.find(route => !clean(route.account_id) && !clean(route.repository_id)) || null;
|
|
58
|
+
}
|
|
59
|
+
export async function routeCompanyQuestion({ question, companyAdapter, accountId, repositoryId, intent = 'code_question', limits = { repositories: 5, lanes: 3 } } = {}) {
|
|
60
|
+
if (!companyAdapter?.candidateRepositories) return { mode: 'local', repositories: repositoryId ? [repositoryId] : [], reason: 'company_graph_unavailable' };
|
|
61
|
+
const route = await companyAdapter.route?.({ accountId, repositoryId, intent }) || null;
|
|
62
|
+
const repositories = (await companyAdapter.candidateRepositories({ question, accountId, repositoryId, intent, route })).slice(0, limits.repositories);
|
|
63
|
+
return { mode: 'company_graph', intent, route, repositories, lane_limit: limits.lanes };
|
|
64
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
const clean = value => String(value || '').trim();
|
|
2
|
+
function profileGuard(profile, supported) {
|
|
3
|
+
if (!supported.includes(profile?.backend)) throw new Error(`backend_profile_mismatch:${profile?.backend}`);
|
|
4
|
+
if (!Number.isInteger(profile?.dimensions) || profile.dimensions < 1) throw new Error('backend_dimensions_required');
|
|
5
|
+
}
|
|
6
|
+
function bindingBackend(id, required) {
|
|
7
|
+
return Object.freeze({
|
|
8
|
+
id, capabilities: () => ({ id, explicit_resource_required: true, supported: true }),
|
|
9
|
+
prepare(profile, context = {}) { profileGuard(profile, [id]); for (const key of required) if (!clean(context[key] || profile[key])) throw new Error(`backend_${id}_${key}_required`); return { id, dimensions: profile.dimensions }; },
|
|
10
|
+
async upsert(records, context = {}) { const backend = context.backend; if (!backend?.upsert) throw new Error(`backend_runtime_unavailable:${id}`); return backend.upsert(records); },
|
|
11
|
+
async delete(ids, context = {}) { const backend = context.backend; if (!backend?.delete) throw new Error(`backend_runtime_unavailable:${id}`); return backend.delete(ids); },
|
|
12
|
+
async query(vector, options, context = {}) { const backend = context.backend; if (!backend?.query) throw new Error(`backend_runtime_unavailable:${id}`); return backend.query(vector, options); },
|
|
13
|
+
async verify(ids, context = {}) { const backend = context.backend; if (!backend?.verify) throw new Error(`backend_runtime_unavailable:${id}`); return backend.verify(ids); },
|
|
14
|
+
health: async context => ({ id, operational: Boolean(context?.backend) }),
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
export function createBackendRegistry() {
|
|
18
|
+
const local = Object.freeze({
|
|
19
|
+
id: 'local_exact', capabilities: () => ({ id: 'local_exact', exact: true, external_resource_required: false }),
|
|
20
|
+
prepare(profile) { profileGuard(profile, ['local_exact']); return { id: 'local_exact', dimensions: profile.dimensions }; },
|
|
21
|
+
async upsert(records, context = {}) { if (!context.store?.cachePut) throw new Error('backend_runtime_unavailable:local_exact'); for (const record of records) await context.store.cachePut(record.id, record.vector); return { upserted: records.length }; },
|
|
22
|
+
async delete() { return { deleted: 0 }; }, async query() { throw new Error('backend_query_owned_by_knowledge_store'); }, async verify() { return { verified: true }; }, health: async () => ({ id: 'local_exact', operational: true }),
|
|
23
|
+
});
|
|
24
|
+
const adapters = new Map([['local_exact', local], ['postgres_pgvector', bindingBackend('postgres_pgvector', ['resource'])], ['supabase_pgvector', bindingBackend('supabase_pgvector', ['resource'])], ['cloudflare_vectorize', bindingBackend('cloudflare_vectorize', ['binding', 'index'])]]);
|
|
25
|
+
return Object.freeze({ ids: () => [...adapters.keys()], get(id) { const item = adapters.get(clean(id)); if (!item) throw new Error(`backend_unsupported:${id}`); return item; }, capabilities: () => [...adapters.values()].map(adapter => adapter.capabilities()) });
|
|
26
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
const clean = value => String(value || '').trim();
|
|
2
|
+
const finiteVector = (vector, dimensions) => {
|
|
3
|
+
if (!Array.isArray(vector) || vector.length !== dimensions || vector.some(value => typeof value !== 'number' || !Number.isFinite(value))) {
|
|
4
|
+
throw new Error(`provider_vector_dimension_mismatch:${Array.isArray(vector) ? vector.length : 'invalid'}!=${dimensions}`);
|
|
5
|
+
}
|
|
6
|
+
return vector;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
function httpAdapter({ id, env, models, request, credentials = env }) {
|
|
10
|
+
return Object.freeze({
|
|
11
|
+
id,
|
|
12
|
+
capabilities: () => ({ id, operational: Boolean(credentials()), models, credentials: credentials() ? 'available' : 'missing' }),
|
|
13
|
+
validate(profile) {
|
|
14
|
+
if (clean(profile?.provider) !== id) throw new Error(`provider_profile_mismatch:${id}`);
|
|
15
|
+
if (!models.includes(clean(profile?.model))) throw new Error(`provider_model_unsupported:${id}:${profile?.model}`);
|
|
16
|
+
if (!Number.isInteger(profile?.dimensions) || profile.dimensions < 1) throw new Error('provider_dimensions_required');
|
|
17
|
+
if (!credentials()) throw new Error(`provider_credentials_unavailable:${id}`);
|
|
18
|
+
},
|
|
19
|
+
dimensions: profile => profile?.dimensions,
|
|
20
|
+
async embedDocuments(inputs, profile, context = {}) { this.validate(profile); return Promise.all(inputs.map(input => request(String(input), profile, { ...context, kind: 'document' }))); },
|
|
21
|
+
async embedQuery(input, profile, context = {}) { this.validate(profile); return request(String(input), profile, { ...context, kind: 'query' }); },
|
|
22
|
+
health: async () => ({ id, operational: Boolean(credentials()), models, credentials: credentials() ? 'available' : 'missing' }),
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function createFixtureProvider({ dimensions = 3 } = {}) {
|
|
27
|
+
const vector = text => {
|
|
28
|
+
const bytes = Buffer.from(String(text)); const values = Array.from({ length: dimensions }, () => 0);
|
|
29
|
+
for (let i = 0; i < bytes.length; i++) values[i % dimensions] += (bytes[i] % 23) + 1;
|
|
30
|
+
const norm = Math.hypot(...values) || 1;
|
|
31
|
+
return values.map(value => value / norm);
|
|
32
|
+
};
|
|
33
|
+
return Object.freeze({
|
|
34
|
+
id: 'fixture', capabilities: () => ({ id: 'fixture', operational: true, deterministic: true, models: ['deterministic'] }),
|
|
35
|
+
validate(profile) { if (profile?.provider !== 'fixture') throw new Error('provider_profile_mismatch:fixture'); if (profile.dimensions !== dimensions) throw new Error(`provider_vector_dimension_mismatch:${profile.dimensions}!=${dimensions}`); },
|
|
36
|
+
dimensions: profile => profile?.dimensions,
|
|
37
|
+
async embedDocuments(inputs, profile) { this.validate(profile); return inputs.map(vector); },
|
|
38
|
+
async embedQuery(input, profile) { this.validate(profile); return vector(input); },
|
|
39
|
+
health: async () => ({ id: 'fixture', operational: true }),
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function createGeminiProvider({ apiKey = process.env.GEMINI_API_KEY, fetchImpl = globalThis.fetch } = {}) {
|
|
44
|
+
return httpAdapter({ id: 'gemini', env: 'GEMINI_API_KEY', models: ['gemini-embedding-2'], credentials: () => apiKey,
|
|
45
|
+
request: async (text, profile, context) => {
|
|
46
|
+
const prefix = context.kind === 'query' ? `task: ${profile.parameters?.task || 'code retrieval'} | query: ` : 'title: none | text: ';
|
|
47
|
+
const response = await fetchImpl(`https://generativelanguage.googleapis.com/v1beta/models/${profile.model}:embedContent`, { method: 'POST', headers: { 'content-type': 'application/json', 'x-goog-api-key': apiKey }, body: JSON.stringify({ model: `models/${profile.model}`, content: { parts: [{ text: prefix + text }] }, outputDimensionality: profile.dimensions }) });
|
|
48
|
+
if (!response.ok) throw new Error(`provider_request_failed:gemini:${response.status}`);
|
|
49
|
+
return finiteVector((await response.json()).embedding?.values, profile.dimensions);
|
|
50
|
+
} });
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function createOpenAIProvider({ apiKey = process.env.OPENAI_API_KEY, fetchImpl = globalThis.fetch } = {}) {
|
|
54
|
+
return httpAdapter({ id: 'openai', env: 'OPENAI_API_KEY', models: ['text-embedding-3-small', 'text-embedding-3-large'], credentials: () => apiKey,
|
|
55
|
+
request: async (text, profile) => {
|
|
56
|
+
const response = await fetchImpl('https://api.openai.com/v1/embeddings', { method: 'POST', headers: { 'content-type': 'application/json', authorization: `Bearer ${apiKey}` }, body: JSON.stringify({ model: profile.model, input: text, dimensions: profile.dimensions }) });
|
|
57
|
+
if (!response.ok) throw new Error(`provider_request_failed:openai:${response.status}`);
|
|
58
|
+
return finiteVector((await response.json()).data?.[0]?.embedding, profile.dimensions);
|
|
59
|
+
} });
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function createWorkersAiProvider({ binding } = {}) {
|
|
63
|
+
return Object.freeze({
|
|
64
|
+
id: 'workers-ai', capabilities: () => ({ id: 'workers-ai', operational: Boolean(binding?.run), execution: 'authenticated-runtime' }),
|
|
65
|
+
validate(profile) { if (profile?.provider !== 'workers-ai') throw new Error('provider_profile_mismatch:workers-ai'); if (!binding?.run) throw new Error('provider_credentials_unavailable:workers-ai'); },
|
|
66
|
+
dimensions: profile => profile?.dimensions,
|
|
67
|
+
async embedDocuments(inputs, profile) { this.validate(profile); return Promise.all(inputs.map(input => binding.run(profile.model, { text: [input] }).then(result => finiteVector(result?.data?.[0] || result?.[0], profile.dimensions)))); },
|
|
68
|
+
async embedQuery(input, profile) { return (await this.embedDocuments([input], profile))[0]; },
|
|
69
|
+
health: async () => ({ id: 'workers-ai', operational: Boolean(binding?.run), execution: 'authenticated-runtime' }),
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function createOllamaProvider({ endpoint = process.env.OLLAMA_HOST || 'http://127.0.0.1:11434', fetchImpl = globalThis.fetch } = {}) {
|
|
74
|
+
return httpAdapter({ id: 'ollama', env: 'OLLAMA_HOST', models: ['nomic-embed-text', 'mxbai-embed-large'], credentials: () => endpoint,
|
|
75
|
+
request: async (text, profile) => {
|
|
76
|
+
const response = await fetchImpl(new URL('/api/embed', endpoint), { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ model: profile.model, input: text }) });
|
|
77
|
+
if (!response.ok) throw new Error(`provider_request_failed:ollama:${response.status}`);
|
|
78
|
+
return finiteVector((await response.json()).embeddings?.[0], profile.dimensions);
|
|
79
|
+
} });
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export function createProviderRegistry(options = {}) {
|
|
83
|
+
const adapters = new Map([
|
|
84
|
+
['fixture', createFixtureProvider(options.fixture)],
|
|
85
|
+
['gemini', createGeminiProvider(options.gemini)],
|
|
86
|
+
['openai', createOpenAIProvider(options.openai)],
|
|
87
|
+
['workers-ai', createWorkersAiProvider(options.workersAi)],
|
|
88
|
+
['ollama', createOllamaProvider(options.ollama)],
|
|
89
|
+
]);
|
|
90
|
+
return Object.freeze({
|
|
91
|
+
ids: () => [...adapters.keys()],
|
|
92
|
+
get(id) { const adapter = adapters.get(clean(id)); if (!adapter) throw new Error(`provider_unsupported:${id}`); return adapter; },
|
|
93
|
+
capabilities: async () => Promise.all([...adapters.values()].map(async adapter => adapter.health())),
|
|
94
|
+
});
|
|
95
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import test from 'node:test';
|
|
3
|
+
import { createFixtureProvider, createProviderRegistry, createBackendRegistry, recommendAutoRag, safeAutoRagConfig, selectIntentRoute, routeCompanyQuestion } from '../src/index.js';
|
|
4
|
+
|
|
5
|
+
test('provider registry is explicit, deterministic, and fails closed', async () => {
|
|
6
|
+
const registry = createProviderRegistry({ fixture: { dimensions: 3 }, gemini: { apiKey: null }, openai: { apiKey: null } });
|
|
7
|
+
assert.deepEqual(registry.ids(), ['fixture', 'gemini', 'openai', 'workers-ai', 'ollama']);
|
|
8
|
+
const fixture = createFixtureProvider({ dimensions: 3 });
|
|
9
|
+
const profile = { provider: 'fixture', model: 'deterministic', dimensions: 3 };
|
|
10
|
+
assert.equal((await fixture.embedQuery('hello', profile)).length, 3);
|
|
11
|
+
assert.throws(() => registry.get('not-a-provider'), /provider_unsupported/);
|
|
12
|
+
assert.equal((await registry.capabilities()).find(item => item.id === 'gemini').operational, false);
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
test('backend registry requires explicit resources and dimensions', () => {
|
|
16
|
+
const registry = createBackendRegistry();
|
|
17
|
+
assert.deepEqual(registry.get('local_exact').prepare({ backend: 'local_exact', dimensions: 3 }), { id: 'local_exact', dimensions: 3 });
|
|
18
|
+
assert.throws(() => registry.get('cloudflare_vectorize').prepare({ backend: 'cloudflare_vectorize', dimensions: 3 }), /binding_required/);
|
|
19
|
+
assert.throws(() => registry.get('unknown'), /backend_unsupported/);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
test('recommendation preserves local defaults and never serializes credentials', () => {
|
|
23
|
+
const discovery = { repository: { identity: 'local:demo' }, scopes: ['src', 'docs'] };
|
|
24
|
+
const recommendation = recommendAutoRag({ discovery, purpose: 'code' });
|
|
25
|
+
const config = safeAutoRagConfig({ recommendation, repositoryId: 'local:demo' });
|
|
26
|
+
assert.deepEqual(config.scope.include, ['src']);
|
|
27
|
+
assert.equal(config.embedding.provider, 'none');
|
|
28
|
+
assert.equal(config.lane.backend, 'local_exact');
|
|
29
|
+
assert.equal(JSON.stringify(config).includes('API_KEY'), false);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
test('routing is repository first, then account, then portable default', async () => {
|
|
33
|
+
const routes = [
|
|
34
|
+
{ intent_key: 'code_question', account_id: '', repository_id: '', id: 'default' },
|
|
35
|
+
{ intent_key: 'code_question', account_id: 'account', repository_id: '', id: 'account' },
|
|
36
|
+
{ intent_key: 'code_question', account_id: 'account', repository_id: 'repo', id: 'repository' },
|
|
37
|
+
];
|
|
38
|
+
assert.equal(selectIntentRoute(routes, { intent: 'code_question', accountId: 'account', repositoryId: 'repo' }).id, 'repository');
|
|
39
|
+
assert.equal(selectIntentRoute(routes, { intent: 'code_question', accountId: 'account', repositoryId: 'other' }).id, 'account');
|
|
40
|
+
const routed = await routeCompanyQuestion({ question: 'where is auth?', repositoryId: 'repo', companyAdapter: { candidateRepositories: async () => ['repo', 'related', 'unrelated'] } });
|
|
41
|
+
assert.deepEqual(routed.repositories, ['repo', 'related', 'unrelated']);
|
|
42
|
+
});
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# @inneranimalmedia/agentsam-nav
|
|
2
|
+
|
|
3
|
+
One navigation context, two independently mountable surfaces. The package owns navigation presentation and transient interaction state. The host owns routing, accounts, projects, conversations, persistence, and all network access.
|
|
4
|
+
|
|
5
|
+
```tsx
|
|
6
|
+
import { Nav } from '@inneranimalmedia/agentsam-nav';
|
|
7
|
+
import '@inneranimalmedia/agentsam-nav/theme.css';
|
|
8
|
+
|
|
9
|
+
<Nav.Provider value={navigation} theme="dark" accentColor="#8B5CF6" peekable defaultOpen={false}>
|
|
10
|
+
<Nav.Sidenav />
|
|
11
|
+
<main>
|
|
12
|
+
<Nav.Topbar rightSlot={<YourWorkspaceControls />} />
|
|
13
|
+
{children}
|
|
14
|
+
</main>
|
|
15
|
+
</Nav.Provider>
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Either surface can be omitted. `Nav.Topbar` accepts `children` to replace its composition, or `contextSlot`, `modeSlot`, and `rightSlot`. Named parts include `TopbarLogo`, `ProjectContext`, `TopbarSpacer`, `FilesResources`, `ConversationActions`, `ShareButton`, `AccountSwitcher`, `OverflowMenu`, and `ModeSwitcher`. `Nav.Scope` applies the same appearance to a consumer's layout.
|
|
19
|
+
|
|
20
|
+
## Configuration
|
|
21
|
+
|
|
22
|
+
`value` supplies `brand`, `mode`, `project`, `conversation`, `projects`, `conversations`, `account`, `accounts`, `destinations`, menu action arrays, and callbacks. Every identity has a stable `id`. Routing uses consumer callbacks and has no router dependency. `brand` contains `name`, `logo`, and `home`. Destinations accept an icon node, label, and either `href` or `onSelect`.
|
|
23
|
+
|
|
24
|
+
Menus use accessible keyboard navigation, collision handling, Escape dismissal, and restored trigger focus. Mobile navigation is a modal drawer. Hover expansion also supports focus. Persistence is deliberately external. `useNav()` exposes shell state and actions for custom controls.
|
|
25
|
+
|
|
26
|
+
## Appearance contract
|
|
27
|
+
|
|
28
|
+
`theme` is `dark`, `light`, or `system`. `accentColor` controls focus and selected control outlines. `tokens` customizes semantic roles rather than arbitrary selectors: `canvas`, `sidebar`, `surface`, `popover`, `text`, `muted`, `border`, `hover`, `selected`, `danger`, `font`, `label-size`, `meta-size`, `row-height`, `radius`, `menu-radius`, `shadow`, `width`, and `rail-width`.
|
|
29
|
+
|
|
30
|
+
Defaults: 56px topbar/rail, 264px expanded sidenav, 14px labels, 12px metadata, 36px rows, 8px controls, 14px menus. Touch controls expand to 44px. Each surface and portal carries its own theme, so a light commerce host can embed a dark AgentSam shell without changing the host document. Supply complete, contrast-checked token sets when changing colorways. Brand identity, theme, accent, and route configuration remain separate.
|
|
31
|
+
|
|
32
|
+
## Plain JavaScript hosts
|
|
33
|
+
|
|
34
|
+
`mountNav` from the `/browser` export mounts React portals into host elements while retaining one provider. Pass `sidenav` and/or `topbar` DOM elements plus provider options. The return value supplies `update(options)` and `unmount()`. The optional `browser.global.js` bundle exposes `AgentSamNav.mountNav` and includes its own React runtime for non-React hosts. Load `theme.css` once. Do not use the bundled runtime inside an existing React tree; use the normal peer-dependent React exports there.
|
|
35
|
+
|
|
36
|
+
## Boundaries
|
|
37
|
+
|
|
38
|
+
No composer, model picker, plugin registry, message renderer, upload transport, browser, terminal, editor, or execution runtime is included. Their controls can be supplied as slots. Menu actions are callbacks; the package does not pretend unavailable operations work. The legacy low-level exports remain available; their stylesheet is `/legacy.css`.
|
|
39
|
+
|
|
40
|
+
Build with `npm run build`; validate with `npm test` and `npm pack`. Distribution contains JavaScript, declarations, scoped CSS, and the browser adapter. Registry publication is a separate release action.
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { type HTMLAttributes } from 'react';
|
|
2
|
+
import { NavCollapsible, NavCollapsibleContent, NavCollapsibleTrigger } from './NavCollapsible.js';
|
|
3
|
+
import { NavContent } from './NavContent.js';
|
|
4
|
+
import { NavFooter } from './NavFooter.js';
|
|
5
|
+
import { NavGroup, NavGroupLabel } from './NavGroup.js';
|
|
6
|
+
import { NavHeader } from './NavHeader.js';
|
|
7
|
+
import { NavMenu, NavMenuButton, NavMenuChevron, NavMenuItem, NavMenuSub, NavMenuSubButton } from './NavMenu.js';
|
|
8
|
+
import { NavProvider } from './NavProvider.js';
|
|
9
|
+
import { NavTrigger } from './NavTrigger.js';
|
|
10
|
+
import { NavScope, NavSidenav, NavTopbar, NavTopbarLogo, NavTopbarSpacer, NavProjectContext, NavAccountSwitcher, NavFilesResources, NavPinnedSummary, NavConversationActions, NavShareButton, NavOverflowMenu, NavModeSwitcher } from './Surfaces.js';
|
|
11
|
+
declare function NavRoot({ className, children, ...props }: HTMLAttributes<HTMLElement>): import("react").JSX.Element;
|
|
12
|
+
export declare const Nav: typeof NavRoot & {
|
|
13
|
+
Scope: typeof NavScope;
|
|
14
|
+
Sidenav: typeof NavSidenav;
|
|
15
|
+
Topbar: typeof NavTopbar;
|
|
16
|
+
TopbarLogo: typeof NavTopbarLogo;
|
|
17
|
+
TopbarSpacer: typeof NavTopbarSpacer;
|
|
18
|
+
ProjectContext: typeof NavProjectContext;
|
|
19
|
+
AccountSwitcher: typeof NavAccountSwitcher;
|
|
20
|
+
FilesResources: typeof NavFilesResources;
|
|
21
|
+
PinnedSummary: typeof NavPinnedSummary;
|
|
22
|
+
ConversationActions: typeof NavConversationActions;
|
|
23
|
+
ShareButton: typeof NavShareButton;
|
|
24
|
+
OverflowMenu: typeof NavOverflowMenu;
|
|
25
|
+
ModeSwitcher: typeof NavModeSwitcher;
|
|
26
|
+
Provider: typeof NavProvider;
|
|
27
|
+
Header: typeof NavHeader;
|
|
28
|
+
Content: typeof NavContent;
|
|
29
|
+
Footer: typeof NavFooter;
|
|
30
|
+
Group: typeof NavGroup;
|
|
31
|
+
GroupLabel: typeof NavGroupLabel;
|
|
32
|
+
Menu: typeof NavMenu;
|
|
33
|
+
MenuItem: typeof NavMenuItem;
|
|
34
|
+
MenuButton: typeof NavMenuButton;
|
|
35
|
+
MenuSub: typeof NavMenuSub;
|
|
36
|
+
MenuSubButton: typeof NavMenuSubButton;
|
|
37
|
+
MenuChevron: typeof NavMenuChevron;
|
|
38
|
+
Collapsible: typeof NavCollapsible;
|
|
39
|
+
CollapsibleTrigger: typeof NavCollapsibleTrigger;
|
|
40
|
+
CollapsibleContent: typeof NavCollapsibleContent;
|
|
41
|
+
Trigger: typeof NavTrigger;
|
|
42
|
+
};
|
|
43
|
+
export {};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type ButtonHTMLAttributes, type HTMLAttributes, type ReactElement, type ReactNode } from 'react';
|
|
2
|
+
export declare function NavCollapsible({ children, defaultOpen }: {
|
|
3
|
+
children: ReactNode;
|
|
4
|
+
defaultOpen?: boolean;
|
|
5
|
+
}): import("react").JSX.Element;
|
|
6
|
+
export declare function NavCollapsibleTrigger({ render, children, className, ...props }: ButtonHTMLAttributes<HTMLButtonElement> & {
|
|
7
|
+
render?: ReactElement;
|
|
8
|
+
children?: ReactNode;
|
|
9
|
+
}): import("react").JSX.Element;
|
|
10
|
+
export declare function NavCollapsibleContent({ className, ...props }: HTMLAttributes<HTMLDivElement>): import("react").JSX.Element;
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { HTMLAttributes } from 'react';
|
|
2
|
+
export declare function NavGroup({ className, ...props }: HTMLAttributes<HTMLElement>): import("react").JSX.Element;
|
|
3
|
+
export declare function NavGroupLabel({ className, ...props }: HTMLAttributes<HTMLParagraphElement>): import("react").JSX.Element;
|