@inneranimalmedia/agentsam-sdk 2.6.2 → 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/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/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 +3 -2
- 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/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/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 +117 -9
- 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/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/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 +118 -37
- package/src/commands/resume.js +9 -5
- package/src/commands/runtime-status.js +154 -0
- package/src/commands/shell.js +466 -55
- package/src/commands/status.js +82 -7
- package/src/commands/tunnel.js +228 -195
- package/src/commands/whoami.js +78 -6
- 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 -51
- 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 +8 -2
- package/src/lib/agent-instructions.js +3 -0
- package/src/lib/auth.js +7 -3
- 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 +37 -18
- package/src/lib/deploy/local-studio.js +6 -5
- 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 +73 -11
- 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 +9 -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 +24 -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 +11 -4
- package/src/ui/wireframes.js +166 -0
- package/test/account-session.test.mjs +28 -0
- package/test/apps-scaffold-contract.test.mjs +16 -1
- 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/shell.test.mjs +149 -4
- package/test/smoke.mjs +2 -2
- package/test/tools-search.test.mjs +33 -0
- package/test/unit/website-assets-bindings.test.mjs +60 -0
- package/test/whoami-resume.test.mjs +82 -3
- package/examples/gorilla-shell/README.md +0 -15
- package/src/lib/gorilla-template.js +0 -54
- 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,152 @@
|
|
|
1
|
+
function trimSlash(value) {
|
|
2
|
+
return String(value || '').replace(/\/+$/, '');
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
function uniquePhysicalQueueNames(topology) {
|
|
6
|
+
return [...new Set(Object.values(topology?.routes || {}).filter(Boolean))];
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export class CloudflareQueueBindingAdapter {
|
|
10
|
+
constructor({ bindings = {} } = {}) {
|
|
11
|
+
this.bindings = bindings;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
resolve(queue) {
|
|
15
|
+
const binding = this.bindings[queue] ?? this.bindings.default ?? null;
|
|
16
|
+
if (!binding || typeof binding.send !== 'function') {
|
|
17
|
+
throw new Error(`cloudflare_queue_binding_missing:${queue}`);
|
|
18
|
+
}
|
|
19
|
+
return binding;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
async publish(queue, job, options = {}) {
|
|
23
|
+
await this.resolve(queue).send(job, options.send_options);
|
|
24
|
+
return { provider: 'cloudflare-binding', queue, accepted: 1 };
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async publishBatch(queue, jobs) {
|
|
28
|
+
const binding = this.resolve(queue);
|
|
29
|
+
if (typeof binding.sendBatch === 'function') {
|
|
30
|
+
await binding.sendBatch(jobs.map((body) => ({ body })));
|
|
31
|
+
} else {
|
|
32
|
+
await Promise.all(jobs.map((job) => binding.send(job)));
|
|
33
|
+
}
|
|
34
|
+
return { provider: 'cloudflare-binding', queue, accepted: jobs.length };
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export class CloudflareQueueApiAdapter {
|
|
39
|
+
constructor({
|
|
40
|
+
cloudflareAccountId,
|
|
41
|
+
apiToken,
|
|
42
|
+
fetchImpl = fetch,
|
|
43
|
+
apiBase = 'https://api.cloudflare.com/client/v4',
|
|
44
|
+
} = {}) {
|
|
45
|
+
if (!cloudflareAccountId) throw new TypeError('cloudflareAccountId is required');
|
|
46
|
+
if (!apiToken) throw new TypeError('apiToken is required');
|
|
47
|
+
this.cloudflareAccountId = cloudflareAccountId;
|
|
48
|
+
this.apiToken = apiToken;
|
|
49
|
+
this.fetchImpl = fetchImpl;
|
|
50
|
+
this.apiBase = trimSlash(apiBase);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
async request(path, init = {}) {
|
|
54
|
+
const response = await this.fetchImpl(`${this.apiBase}${path}`, {
|
|
55
|
+
...init,
|
|
56
|
+
headers: {
|
|
57
|
+
authorization: `Bearer ${this.apiToken}`,
|
|
58
|
+
'content-type': 'application/json',
|
|
59
|
+
...(init.headers || {}),
|
|
60
|
+
},
|
|
61
|
+
});
|
|
62
|
+
const body = await response.json().catch(() => null);
|
|
63
|
+
if (!response.ok || body?.success === false) {
|
|
64
|
+
const error = new Error(body?.errors?.[0]?.message || `cloudflare_http_${response.status}`);
|
|
65
|
+
error.code = body?.errors?.[0]?.code ? String(body.errors[0].code) : `cloudflare_http_${response.status}`;
|
|
66
|
+
error.status = response.status;
|
|
67
|
+
error.response = body;
|
|
68
|
+
throw error;
|
|
69
|
+
}
|
|
70
|
+
return body?.result ?? body;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
async listQueues() {
|
|
74
|
+
const path = `/accounts/${encodeURIComponent(this.cloudflareAccountId)}/queues`;
|
|
75
|
+
const result = await this.request(path, { method: 'GET' });
|
|
76
|
+
return Array.isArray(result) ? result : [];
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
async createQueue(queueName) {
|
|
80
|
+
const path = `/accounts/${encodeURIComponent(this.cloudflareAccountId)}/queues`;
|
|
81
|
+
return this.request(path, {
|
|
82
|
+
method: 'POST',
|
|
83
|
+
body: JSON.stringify({ queue_name: queueName }),
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
async attachWorkerConsumer(queueId, {
|
|
88
|
+
script_name,
|
|
89
|
+
batch_size = 10,
|
|
90
|
+
max_wait_time_ms = 5_000,
|
|
91
|
+
max_retries = 3,
|
|
92
|
+
retry_delay = 5,
|
|
93
|
+
max_concurrency,
|
|
94
|
+
} = {}) {
|
|
95
|
+
if (!script_name) throw new TypeError('script_name is required');
|
|
96
|
+
const settings = {
|
|
97
|
+
batch_size,
|
|
98
|
+
max_wait_time_ms,
|
|
99
|
+
max_retries,
|
|
100
|
+
retry_delay,
|
|
101
|
+
};
|
|
102
|
+
if (Number.isInteger(max_concurrency) && max_concurrency > 0) {
|
|
103
|
+
settings.max_concurrency = max_concurrency;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const path = `/accounts/${encodeURIComponent(this.cloudflareAccountId)}/queues/${encodeURIComponent(queueId)}/consumers`;
|
|
107
|
+
return this.request(path, {
|
|
108
|
+
method: 'POST',
|
|
109
|
+
body: JSON.stringify({
|
|
110
|
+
type: 'worker',
|
|
111
|
+
script_name,
|
|
112
|
+
settings,
|
|
113
|
+
}),
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
async ensureTopology(topology, options = {}) {
|
|
118
|
+
const existing = await this.listQueues();
|
|
119
|
+
const byName = new Map(existing.map((queue) => [queue.queue_name ?? queue.name, queue]));
|
|
120
|
+
const created = [];
|
|
121
|
+
const present = [];
|
|
122
|
+
|
|
123
|
+
for (const queueName of uniquePhysicalQueueNames(topology)) {
|
|
124
|
+
let queue = byName.get(queueName);
|
|
125
|
+
if (!queue) {
|
|
126
|
+
queue = await this.createQueue(queueName);
|
|
127
|
+
created.push(queueName);
|
|
128
|
+
byName.set(queueName, queue);
|
|
129
|
+
} else {
|
|
130
|
+
present.push(queueName);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const consumer = options.consumers?.[queueName];
|
|
134
|
+
if (consumer?.script_name) {
|
|
135
|
+
const queueId = queue.queue_id ?? queue.id;
|
|
136
|
+
if (!queueId) throw new Error(`cloudflare_queue_id_missing:${queueName}`);
|
|
137
|
+
await this.attachWorkerConsumer(queueId, consumer);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return {
|
|
142
|
+
provider: 'cloudflare-api',
|
|
143
|
+
created,
|
|
144
|
+
existing: present,
|
|
145
|
+
queues: [...byName.values()],
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
async publish() {
|
|
150
|
+
throw new Error('cloudflare_api_adapter_publish_not_supported_use_queue_binding_or_http_producer');
|
|
151
|
+
}
|
|
152
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
export class MemoryQueueAdapter {
|
|
2
|
+
constructor() {
|
|
3
|
+
this.queues = new Map();
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
#queue(name) {
|
|
7
|
+
if (!this.queues.has(name)) this.queues.set(name, []);
|
|
8
|
+
return this.queues.get(name);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
async publish(queue, job) {
|
|
12
|
+
this.#queue(queue).push(structuredClone(job));
|
|
13
|
+
return { provider: 'memory', queue, accepted: 1 };
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
async publishBatch(queue, jobs) {
|
|
17
|
+
this.#queue(queue).push(...jobs.map((job) => structuredClone(job)));
|
|
18
|
+
return { provider: 'memory', queue, accepted: jobs.length };
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async pull(queue, max = 1) {
|
|
22
|
+
return this.#queue(queue).splice(0, Math.max(0, Number(max) || 1));
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
async ensureTopology(topology) {
|
|
26
|
+
const names = new Set(Object.values(topology.routes || {}));
|
|
27
|
+
for (const name of names) this.#queue(name);
|
|
28
|
+
return {
|
|
29
|
+
provider: 'memory',
|
|
30
|
+
created: [...names],
|
|
31
|
+
existing: [],
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
depth(queue) {
|
|
36
|
+
return this.#queue(queue).length;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
|
|
3
|
+
export const JOB_STATUSES = Object.freeze([
|
|
4
|
+
'queued',
|
|
5
|
+
'claimed',
|
|
6
|
+
'running',
|
|
7
|
+
'waiting',
|
|
8
|
+
'batch_waiting',
|
|
9
|
+
'awaiting_input',
|
|
10
|
+
'completed',
|
|
11
|
+
'failed',
|
|
12
|
+
'cancelled',
|
|
13
|
+
]);
|
|
14
|
+
|
|
15
|
+
export const TERMINAL_JOB_STATUSES = Object.freeze([
|
|
16
|
+
'completed',
|
|
17
|
+
'failed',
|
|
18
|
+
'cancelled',
|
|
19
|
+
]);
|
|
20
|
+
|
|
21
|
+
export const JOB_PRIORITIES = Object.freeze(['low', 'normal', 'high', 'urgent']);
|
|
22
|
+
|
|
23
|
+
export const EXECUTOR_KINDS = Object.freeze([
|
|
24
|
+
'inline',
|
|
25
|
+
'queue',
|
|
26
|
+
'workflow',
|
|
27
|
+
'execos',
|
|
28
|
+
'worker_rpc',
|
|
29
|
+
'openai_batch',
|
|
30
|
+
'gemini_batch',
|
|
31
|
+
'external',
|
|
32
|
+
]);
|
|
33
|
+
|
|
34
|
+
export const DEFAULT_RETRY_POLICY = Object.freeze({
|
|
35
|
+
max_attempts: 3,
|
|
36
|
+
backoff: 'exponential',
|
|
37
|
+
initial_delay_ms: 1_000,
|
|
38
|
+
max_delay_ms: 60_000,
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
function clean(value) {
|
|
42
|
+
return value == null ? '' : String(value).trim();
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function assertEnum(value, allowed, field) {
|
|
46
|
+
if (!allowed.includes(value)) {
|
|
47
|
+
throw new TypeError(`${field} must be one of: ${allowed.join(', ')}`);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function createJobEnvelope(input = {}) {
|
|
52
|
+
const kind = clean(input.kind);
|
|
53
|
+
if (!kind) throw new TypeError('kind is required');
|
|
54
|
+
|
|
55
|
+
const accountId = clean(input.account_id);
|
|
56
|
+
if (!accountId) throw new TypeError('account_id is required');
|
|
57
|
+
|
|
58
|
+
const now = input.created_at ?? Math.floor(Date.now() / 1000);
|
|
59
|
+
const priority = input.priority ?? 'normal';
|
|
60
|
+
assertEnum(priority, JOB_PRIORITIES, 'priority');
|
|
61
|
+
|
|
62
|
+
const executor = input.executor ?? 'queue';
|
|
63
|
+
assertEnum(executor, EXECUTOR_KINDS, 'executor');
|
|
64
|
+
|
|
65
|
+
return {
|
|
66
|
+
schema_version: '1',
|
|
67
|
+
id: clean(input.id) || `job_${randomUUID().replaceAll('-', '')}`,
|
|
68
|
+
account_id: accountId,
|
|
69
|
+
kind,
|
|
70
|
+
logical_queue: clean(input.logical_queue) || null,
|
|
71
|
+
executor,
|
|
72
|
+
priority,
|
|
73
|
+
status: input.status ?? 'queued',
|
|
74
|
+
parent_job_id: clean(input.parent_job_id) || null,
|
|
75
|
+
conversation_id: clean(input.conversation_id) || null,
|
|
76
|
+
agent_id: clean(input.agent_id) || null,
|
|
77
|
+
source_run_id: clean(input.source_run_id) || null,
|
|
78
|
+
idempotency_key: clean(input.idempotency_key) || null,
|
|
79
|
+
payload: input.payload ?? null,
|
|
80
|
+
payload_ref: clean(input.payload_ref) || null,
|
|
81
|
+
metadata: input.metadata && typeof input.metadata === 'object' ? input.metadata : {},
|
|
82
|
+
retry: {
|
|
83
|
+
...DEFAULT_RETRY_POLICY,
|
|
84
|
+
...(input.retry && typeof input.retry === 'object' ? input.retry : {}),
|
|
85
|
+
},
|
|
86
|
+
attempt: Number.isInteger(input.attempt) ? input.attempt : 0,
|
|
87
|
+
created_at: now,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export function assertJobEnvelope(job) {
|
|
92
|
+
if (!job || typeof job !== 'object') throw new TypeError('job envelope must be an object');
|
|
93
|
+
if (job.schema_version !== '1') throw new TypeError('unsupported job schema_version');
|
|
94
|
+
if (!clean(job.id)) throw new TypeError('job.id is required');
|
|
95
|
+
if (!clean(job.account_id)) throw new TypeError('job.account_id is required');
|
|
96
|
+
if (!clean(job.kind)) throw new TypeError('job.kind is required');
|
|
97
|
+
assertEnum(job.priority ?? 'normal', JOB_PRIORITIES, 'job.priority');
|
|
98
|
+
assertEnum(job.executor ?? 'queue', EXECUTOR_KINDS, 'job.executor');
|
|
99
|
+
assertEnum(job.status ?? 'queued', JOB_STATUSES, 'job.status');
|
|
100
|
+
return job;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export function createReceipt(job, input = {}) {
|
|
104
|
+
assertJobEnvelope(job);
|
|
105
|
+
const status = input.status ?? 'completed';
|
|
106
|
+
assertEnum(status, JOB_STATUSES, 'receipt.status');
|
|
107
|
+
|
|
108
|
+
return {
|
|
109
|
+
schema_version: '1',
|
|
110
|
+
receipt_id: clean(input.receipt_id) || `receipt_${randomUUID().replaceAll('-', '')}`,
|
|
111
|
+
job_id: job.id,
|
|
112
|
+
account_id: job.account_id,
|
|
113
|
+
kind: job.kind,
|
|
114
|
+
status,
|
|
115
|
+
executor: input.executor ?? job.executor,
|
|
116
|
+
provider: clean(input.provider) || null,
|
|
117
|
+
provider_job_id: clean(input.provider_job_id) || null,
|
|
118
|
+
result: input.result ?? null,
|
|
119
|
+
result_ref: clean(input.result_ref) || null,
|
|
120
|
+
error: input.error ?? null,
|
|
121
|
+
attempt: Number.isInteger(input.attempt) ? input.attempt : job.attempt,
|
|
122
|
+
started_at: input.started_at ?? null,
|
|
123
|
+
completed_at: input.completed_at ?? Math.floor(Date.now() / 1000),
|
|
124
|
+
metadata: input.metadata && typeof input.metadata === 'object' ? input.metadata : {},
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export function isTerminalJobStatus(status) {
|
|
129
|
+
return TERMINAL_JOB_STATUSES.includes(status);
|
|
130
|
+
}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { createJobEnvelope, assertJobEnvelope } from './contracts.js';
|
|
2
|
+
import { buildQueueTopology, resolvePhysicalQueue } from './topology.js';
|
|
3
|
+
import { routeWork, shouldRequireProvisionApproval } from './policy.js';
|
|
4
|
+
import { JobDispatcher } from './dispatch.js';
|
|
5
|
+
|
|
6
|
+
export class QueueControl {
|
|
7
|
+
constructor({
|
|
8
|
+
adapter,
|
|
9
|
+
topology = buildQueueTopology(),
|
|
10
|
+
routingPolicy = {},
|
|
11
|
+
approval = null,
|
|
12
|
+
dispatcher = new JobDispatcher(),
|
|
13
|
+
} = {}) {
|
|
14
|
+
if (!adapter) throw new TypeError('adapter is required');
|
|
15
|
+
this.adapter = adapter;
|
|
16
|
+
this.topology = topology;
|
|
17
|
+
this.routingPolicy = routingPolicy;
|
|
18
|
+
this.approval = approval;
|
|
19
|
+
this.dispatcher = dispatcher;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
register(kind, handler) {
|
|
23
|
+
this.dispatcher.register(kind, handler);
|
|
24
|
+
return this;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
plan(task) {
|
|
28
|
+
const route = routeWork(task, this.routingPolicy);
|
|
29
|
+
return {
|
|
30
|
+
...route,
|
|
31
|
+
physical_queue: route.logical_queue === 'interactive'
|
|
32
|
+
? null
|
|
33
|
+
: resolvePhysicalQueue(this.topology, route.logical_queue),
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
async enqueue(task, input = {}) {
|
|
38
|
+
const plan = this.plan(task);
|
|
39
|
+
const job = createJobEnvelope({
|
|
40
|
+
...input,
|
|
41
|
+
kind: task.kind,
|
|
42
|
+
payload: input.payload ?? task.payload ?? null,
|
|
43
|
+
account_id: input.account_id ?? task.account_id,
|
|
44
|
+
logical_queue: plan.logical_queue,
|
|
45
|
+
executor: plan.executor,
|
|
46
|
+
priority: input.priority ?? task.priority ?? 'normal',
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
if (plan.mode === 'interactive') {
|
|
50
|
+
return { job, plan, published: false };
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
assertJobEnvelope(job);
|
|
54
|
+
const physicalQueue = resolvePhysicalQueue(this.topology, plan.logical_queue);
|
|
55
|
+
const receipt = await this.adapter.publish(physicalQueue, job, {
|
|
56
|
+
logical_queue: plan.logical_queue,
|
|
57
|
+
});
|
|
58
|
+
return { job, plan, published: true, publish_receipt: receipt ?? null };
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
async enqueueMany(tasks, common = {}) {
|
|
62
|
+
const grouped = new Map();
|
|
63
|
+
|
|
64
|
+
for (const task of tasks) {
|
|
65
|
+
const plan = this.plan(task);
|
|
66
|
+
if (plan.mode === 'interactive') {
|
|
67
|
+
throw new Error('enqueueMany does not accept interactive tasks');
|
|
68
|
+
}
|
|
69
|
+
const job = createJobEnvelope({
|
|
70
|
+
...common,
|
|
71
|
+
account_id: common.account_id ?? task.account_id,
|
|
72
|
+
kind: task.kind,
|
|
73
|
+
payload: task.payload ?? null,
|
|
74
|
+
logical_queue: plan.logical_queue,
|
|
75
|
+
executor: plan.executor,
|
|
76
|
+
priority: task.priority ?? common.priority ?? 'normal',
|
|
77
|
+
});
|
|
78
|
+
const queue = resolvePhysicalQueue(this.topology, plan.logical_queue);
|
|
79
|
+
const bucket = grouped.get(queue) ?? [];
|
|
80
|
+
bucket.push(job);
|
|
81
|
+
grouped.set(queue, bucket);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const receipts = [];
|
|
85
|
+
for (const [queue, jobs] of grouped) {
|
|
86
|
+
const receipt = this.adapter.publishBatch
|
|
87
|
+
? await this.adapter.publishBatch(queue, jobs)
|
|
88
|
+
: await Promise.all(jobs.map((job) => this.adapter.publish(queue, job)));
|
|
89
|
+
receipts.push({ queue, count: jobs.length, receipt });
|
|
90
|
+
}
|
|
91
|
+
return receipts;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
async provision(options = {}) {
|
|
95
|
+
if (typeof this.adapter.ensureTopology !== 'function') {
|
|
96
|
+
throw new Error('adapter_does_not_support_provisioning');
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (shouldRequireProvisionApproval('provision', this.routingPolicy)) {
|
|
100
|
+
if (typeof this.approval !== 'function') {
|
|
101
|
+
const error = new Error('queue_provision_approval_required');
|
|
102
|
+
error.code = 'approval_required';
|
|
103
|
+
throw error;
|
|
104
|
+
}
|
|
105
|
+
const allowed = await this.approval({
|
|
106
|
+
action: 'provision',
|
|
107
|
+
topology: this.topology,
|
|
108
|
+
options,
|
|
109
|
+
});
|
|
110
|
+
if (!allowed) {
|
|
111
|
+
const error = new Error('queue_provision_denied');
|
|
112
|
+
error.code = 'approval_denied';
|
|
113
|
+
throw error;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return this.adapter.ensureTopology(this.topology, options);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
async consume(messages, context = {}) {
|
|
121
|
+
const results = [];
|
|
122
|
+
for (const message of messages) {
|
|
123
|
+
const job = message?.body ?? message;
|
|
124
|
+
assertJobEnvelope(job);
|
|
125
|
+
try {
|
|
126
|
+
const result = await this.dispatcher.dispatch(job, context);
|
|
127
|
+
if (typeof message?.ack === 'function') message.ack();
|
|
128
|
+
results.push({ job_id: job.id, ok: true, result });
|
|
129
|
+
} catch (error) {
|
|
130
|
+
if (typeof message?.retry === 'function') message.retry();
|
|
131
|
+
results.push({
|
|
132
|
+
job_id: job.id,
|
|
133
|
+
ok: false,
|
|
134
|
+
error: String(error?.message || error),
|
|
135
|
+
code: error?.code ?? null,
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return results;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
export class JobDispatcher {
|
|
2
|
+
#handlers = new Map();
|
|
3
|
+
|
|
4
|
+
register(kind, handler) {
|
|
5
|
+
if (!kind || typeof handler !== 'function') {
|
|
6
|
+
throw new TypeError('register(kind, handler) requires a kind and function');
|
|
7
|
+
}
|
|
8
|
+
this.#handlers.set(kind, handler);
|
|
9
|
+
return this;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
unregister(kind) {
|
|
13
|
+
return this.#handlers.delete(kind);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
resolve(kind) {
|
|
17
|
+
if (this.#handlers.has(kind)) return this.#handlers.get(kind);
|
|
18
|
+
|
|
19
|
+
let best = null;
|
|
20
|
+
for (const [registered, handler] of this.#handlers.entries()) {
|
|
21
|
+
if (!registered.endsWith('*')) continue;
|
|
22
|
+
const prefix = registered.slice(0, -1);
|
|
23
|
+
if (kind.startsWith(prefix) && (!best || prefix.length > best.prefix.length)) {
|
|
24
|
+
best = { prefix, handler };
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return best?.handler ?? null;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
async dispatch(job, context = {}) {
|
|
31
|
+
const handler = this.resolve(job.kind);
|
|
32
|
+
if (!handler) {
|
|
33
|
+
const error = new Error(`job_handler_not_found:${job.kind}`);
|
|
34
|
+
error.code = 'job_handler_not_found';
|
|
35
|
+
throw error;
|
|
36
|
+
}
|
|
37
|
+
return handler(job, context);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
kinds() {
|
|
41
|
+
return [...this.#handlers.keys()];
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
const DEFAULT_KIND_ROUTES = Object.freeze({
|
|
2
|
+
'deploy.': { logical_queue: 'deployments', executor: 'workflow' },
|
|
3
|
+
'infra.': { logical_queue: 'deployments', executor: 'workflow' },
|
|
4
|
+
'cad.': { logical_queue: 'cad', executor: 'queue' },
|
|
5
|
+
'code.index': { logical_queue: 'indexing', executor: 'queue' },
|
|
6
|
+
'repository.index': { logical_queue: 'indexing', executor: 'queue' },
|
|
7
|
+
'cms.': { logical_queue: 'cms', executor: 'queue' },
|
|
8
|
+
'commerce.': { logical_queue: 'cms', executor: 'queue' },
|
|
9
|
+
'webhook.': { logical_queue: 'webhooks', executor: 'queue' },
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
function firstKindMatch(kind, routes) {
|
|
13
|
+
for (const [prefix, route] of Object.entries(routes)) {
|
|
14
|
+
if (kind === prefix || kind.startsWith(prefix)) return route;
|
|
15
|
+
}
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function routeWork(task = {}, policy = {}) {
|
|
20
|
+
const kind = String(task.kind || '').trim();
|
|
21
|
+
if (!kind) throw new TypeError('task.kind is required');
|
|
22
|
+
|
|
23
|
+
if (task.interactive === true || task.requires_immediate_response === true) {
|
|
24
|
+
return {
|
|
25
|
+
mode: 'interactive',
|
|
26
|
+
logical_queue: 'interactive',
|
|
27
|
+
executor: 'inline',
|
|
28
|
+
reason: 'interactive_requirement',
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const aiCalls = Number(task.estimated_ai_calls || 0);
|
|
33
|
+
const independent = task.independent_items === true || Number(task.item_count || 0) > 1;
|
|
34
|
+
const latency = task.latency ?? 'normal';
|
|
35
|
+
const batchThreshold = Number(policy.batch_ai_min_calls ?? 50);
|
|
36
|
+
|
|
37
|
+
if (
|
|
38
|
+
task.ai_provider &&
|
|
39
|
+
aiCalls >= batchThreshold &&
|
|
40
|
+
independent &&
|
|
41
|
+
['deferred', 'background', 'hours'].includes(latency)
|
|
42
|
+
) {
|
|
43
|
+
const provider = String(task.ai_provider).toLowerCase();
|
|
44
|
+
if (provider === 'openai' || provider === 'google' || provider === 'gemini') {
|
|
45
|
+
return {
|
|
46
|
+
mode: 'batch_ai',
|
|
47
|
+
logical_queue: 'batch_ai',
|
|
48
|
+
executor: provider === 'openai' ? 'openai_batch' : 'gemini_batch',
|
|
49
|
+
reason: 'bulk_independent_ai_work',
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const configuredRoutes = {
|
|
55
|
+
...DEFAULT_KIND_ROUTES,
|
|
56
|
+
...(policy.kind_routes || {}),
|
|
57
|
+
};
|
|
58
|
+
const matched = firstKindMatch(kind, configuredRoutes);
|
|
59
|
+
if (matched) {
|
|
60
|
+
return {
|
|
61
|
+
mode: 'background',
|
|
62
|
+
...matched,
|
|
63
|
+
reason: 'kind_policy',
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (task.machine_required === true) {
|
|
68
|
+
return {
|
|
69
|
+
mode: 'background',
|
|
70
|
+
logical_queue: 'jobs',
|
|
71
|
+
executor: 'execos',
|
|
72
|
+
reason: 'machine_requirement',
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return {
|
|
77
|
+
mode: 'background',
|
|
78
|
+
logical_queue: 'jobs',
|
|
79
|
+
executor: 'queue',
|
|
80
|
+
reason: 'default_durable_work',
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export function shouldRequireProvisionApproval(action, policy = {}) {
|
|
85
|
+
const normalized = String(action || '');
|
|
86
|
+
if (policy.allow_infrastructure_mutation === true) return false;
|
|
87
|
+
return ['provision', 'configure', 'delete'].includes(normalized);
|
|
88
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
export const DEFAULT_LOGICAL_QUEUES = Object.freeze({
|
|
2
|
+
interactive: { lane: 'interactive', durability: 'best_effort' },
|
|
3
|
+
jobs: { lane: 'general', durability: 'durable' },
|
|
4
|
+
deployments: { lane: 'infra', durability: 'durable' },
|
|
5
|
+
cad: { lane: 'compute', durability: 'durable' },
|
|
6
|
+
indexing: { lane: 'compute', durability: 'durable' },
|
|
7
|
+
cms: { lane: 'business', durability: 'durable' },
|
|
8
|
+
webhooks: { lane: 'events', durability: 'durable' },
|
|
9
|
+
batch_ai: { lane: 'ai', durability: 'durable' },
|
|
10
|
+
dead_letter: { lane: 'failure', durability: 'durable' },
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
export function buildQueueTopology({
|
|
14
|
+
namespace = 'agentsam',
|
|
15
|
+
environment = 'dev',
|
|
16
|
+
mode = 'compact',
|
|
17
|
+
logicalQueues = DEFAULT_LOGICAL_QUEUES,
|
|
18
|
+
} = {}) {
|
|
19
|
+
if (!['compact', 'segmented'].includes(mode)) {
|
|
20
|
+
throw new TypeError('mode must be compact or segmented');
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const prefix = [namespace, environment].filter(Boolean).join('-');
|
|
24
|
+
const physical = {};
|
|
25
|
+
const routes = {};
|
|
26
|
+
|
|
27
|
+
if (mode === 'compact') {
|
|
28
|
+
physical.jobs = {
|
|
29
|
+
name: `${prefix}-jobs`,
|
|
30
|
+
dead_letter: `${prefix}-dlq`,
|
|
31
|
+
};
|
|
32
|
+
for (const key of Object.keys(logicalQueues)) {
|
|
33
|
+
if (key === 'interactive') continue;
|
|
34
|
+
routes[key] = key === 'dead_letter' ? physical.jobs.dead_letter : physical.jobs.name;
|
|
35
|
+
}
|
|
36
|
+
} else {
|
|
37
|
+
for (const [key, config] of Object.entries(logicalQueues)) {
|
|
38
|
+
if (key === 'interactive') continue;
|
|
39
|
+
const name = key === 'dead_letter'
|
|
40
|
+
? `${prefix}-dlq`
|
|
41
|
+
: `${prefix}-${config.lane || key}`;
|
|
42
|
+
physical[key] = {
|
|
43
|
+
name,
|
|
44
|
+
dead_letter: key === 'dead_letter' ? null : `${prefix}-dlq`,
|
|
45
|
+
};
|
|
46
|
+
routes[key] = name;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return {
|
|
51
|
+
schema_version: '1',
|
|
52
|
+
mode,
|
|
53
|
+
namespace,
|
|
54
|
+
environment,
|
|
55
|
+
logical_queues: logicalQueues,
|
|
56
|
+
physical,
|
|
57
|
+
routes,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function resolvePhysicalQueue(topology, logicalQueue) {
|
|
62
|
+
if (!topology?.routes) throw new TypeError('topology.routes is required');
|
|
63
|
+
const value = topology.routes[logicalQueue];
|
|
64
|
+
if (!value) throw new Error(`logical_queue_not_routed:${logicalQueue}`);
|
|
65
|
+
return value;
|
|
66
|
+
}
|