@vellumai/assistant 0.8.1 → 0.8.2
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/ARCHITECTURE.md +2 -7
- package/Dockerfile +75 -1
- package/bun.lock +11 -1
- package/docker-entrypoint.sh +5 -0
- package/docker-init-apt-root.sh +94 -0
- package/docker-kata-apt-env.sh +39 -0
- package/docs/plugins.md +88 -47
- package/docs/skills.md +9 -7
- package/examples/plugins/echo/README.md +27 -27
- package/examples/plugins/echo/package.json +3 -0
- package/examples/plugins/echo/register.ts +31 -31
- package/node_modules/@vellumai/slack-text/src/index.test.ts +114 -14
- package/node_modules/@vellumai/slack-text/src/index.ts +82 -18
- package/openapi.yaml +325 -3
- package/package.json +3 -1
- package/scripts/generate-openapi.ts +83 -10
- package/scripts/sync-llm-catalog.ts +2 -2
- package/scripts/sync-web-search-catalog.ts +47 -25
- package/src/__tests__/agent-image-optimize.test.ts +11 -3
- package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +131 -0
- package/src/__tests__/anthropic-provider.test.ts +45 -0
- package/src/__tests__/app-builder-tool-scripts.test.ts +9 -3
- package/src/__tests__/app-executors.test.ts +220 -4
- package/src/__tests__/auto-analysis-end-to-end.test.ts +35 -0
- package/src/__tests__/bundled-asset.test.ts +6 -6
- package/src/__tests__/channel-availability-routes.test.ts +206 -0
- package/src/__tests__/channel-delivery-store.test.ts +289 -1
- package/src/__tests__/circuit-breaker-pipeline.test.ts +0 -1
- package/src/__tests__/clawhub.test.ts +75 -16
- package/src/__tests__/compactor-tail-resolution.test.ts +41 -0
- package/src/__tests__/config-schema.test.ts +21 -0
- package/src/__tests__/config-set-route.test.ts +80 -0
- package/src/__tests__/config-sounds-sync.test.ts +97 -0
- package/src/__tests__/config-watcher-skill-reseed.test.ts +453 -0
- package/src/__tests__/context-search-conversations-source.test.ts +117 -2
- package/src/__tests__/context-search-memory-v2-source.test.ts +0 -1
- package/src/__tests__/context-search-workspace-source.test.ts +7 -0
- package/src/__tests__/context-token-estimator.test.ts +1 -0
- package/src/__tests__/conversation-abort-tool-results.test.ts +4 -1
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +1 -0
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +92 -92
- package/src/__tests__/conversation-agent-loop.test.ts +2 -0
- package/src/__tests__/conversation-error.test.ts +42 -3
- package/src/__tests__/conversation-fork-crud.test.ts +82 -0
- package/src/__tests__/conversation-inference-profile-route.test.ts +40 -4
- package/src/__tests__/conversation-lifecycle.test.ts +173 -0
- package/src/__tests__/conversation-message-sync-tags.test.ts +97 -0
- package/src/__tests__/conversation-pairing.test.ts +54 -0
- package/src/__tests__/conversation-process-callsite.test.ts +4 -1
- package/src/__tests__/conversation-provider-retry-repair.test.ts +5 -1
- package/src/__tests__/conversation-queue.test.ts +4 -1
- package/src/__tests__/conversation-runtime-assembly.test.ts +76 -9
- package/src/__tests__/conversation-slash-queue.test.ts +59 -1
- package/src/__tests__/conversation-slash-unknown.test.ts +4 -1
- package/src/__tests__/conversation-surfaces-table-action.test.ts +360 -0
- package/src/__tests__/conversation-sync-tags.test.ts +235 -0
- package/src/__tests__/conversation-workspace-injection.test.ts +5 -1
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +5 -1
- package/src/__tests__/credential-security-invariants.test.ts +3 -2
- package/src/__tests__/db-slack-external-content-normalization.test.ts +301 -0
- package/src/__tests__/delete-managed-skill-tool.test.ts +55 -13
- package/src/__tests__/disk-pressure-tools.test.ts +1 -0
- package/src/__tests__/dm-backfill.test.ts +121 -10
- package/src/__tests__/document-tool-security.test.ts +258 -0
- package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +0 -1
- package/src/__tests__/edit-propagation.test.ts +33 -0
- package/src/__tests__/empty-response-pipeline.test.ts +0 -4
- package/src/__tests__/external-plugin-loader.test.ts +60 -36
- package/src/__tests__/filing-service.test.ts +140 -0
- package/src/__tests__/get-skill-detail-audit.test.ts +0 -4
- package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +43 -62
- package/src/__tests__/helpers/tar-fixtures.ts +39 -0
- package/src/__tests__/helpers/wait-for.ts +21 -0
- package/src/__tests__/history-repair-pipeline.test.ts +0 -3
- package/src/__tests__/history-repair.test.ts +73 -0
- package/src/__tests__/host-app-control-proxy.test.ts +266 -10
- package/src/__tests__/image-credentials.test.ts +1 -1
- package/src/__tests__/inbound-slack-persistence.test.ts +2 -0
- package/src/__tests__/inference-no-mode-boot-e2e.test.ts +1 -1
- package/src/__tests__/inference-profile-reaper.test.ts +4 -2
- package/src/__tests__/inference-profile-session-handler.test.ts +18 -6
- package/src/__tests__/inference-profile-session-ipc.test.ts +17 -5
- package/src/__tests__/injector-chain.test.ts +10 -8
- package/src/__tests__/install-skill-routing.test.ts +155 -37
- package/src/__tests__/lifecycle-memory-v2-seed.test.ts +92 -3
- package/src/__tests__/list-messages-page-latest.test.ts +55 -0
- package/src/__tests__/llm-call-pipeline.test.ts +0 -3
- package/src/__tests__/llm-catalog-parity.test.ts +55 -13
- package/src/__tests__/llm-request-log-source-clickhouse.test.ts +34 -0
- package/src/__tests__/llm-request-log-source-factory.test.ts +29 -53
- package/src/__tests__/llm-usage-store.test.ts +114 -0
- package/src/__tests__/managed-profile-guard.test.ts +31 -29
- package/src/__tests__/managed-skill-lifecycle.test.ts +109 -18
- package/src/__tests__/managed-store.test.ts +84 -192
- package/src/__tests__/media-generate-image.test.ts +1 -1
- package/src/__tests__/memory-retrieval-pipeline.test.ts +0 -2
- package/src/__tests__/messages-after-tiebreaker.test.ts +122 -0
- package/src/__tests__/oauth-commands-routes.test.ts +168 -16
- package/src/__tests__/oauth-provider-profiles.test.ts +9 -0
- package/src/__tests__/openai-provider.test.ts +24 -0
- package/src/__tests__/openai-responses-cutover-guard.test.ts +17 -9
- package/src/__tests__/overflow-reduce-pipeline.test.ts +0 -2
- package/src/__tests__/persistence-pipeline.test.ts +0 -2
- package/src/__tests__/{managed-proxy-context.test.ts → platform-proxy-context.test.ts} +1 -1
- package/src/__tests__/platform.test.ts +2 -0
- package/src/__tests__/plugin-api-shim.test.ts +125 -0
- package/src/__tests__/plugin-bootstrap.test.ts +10 -36
- package/src/__tests__/plugin-external-api.test.ts +68 -0
- package/src/__tests__/plugin-registry.test.ts +0 -77
- package/src/__tests__/plugin-route-contribution.test.ts +0 -1
- package/src/__tests__/plugin-skill-contribution.test.ts +0 -2
- package/src/__tests__/plugin-tool-contribution.test.ts +16 -15
- package/src/__tests__/plugin-types.test.ts +3 -13
- package/src/__tests__/process-message-background-slack.test.ts +8 -1
- package/src/__tests__/process-message-display-content.test.ts +421 -0
- package/src/__tests__/provider-catalog-visibility.test.ts +142 -0
- package/src/__tests__/provider-error-scenarios.test.ts +111 -0
- package/src/__tests__/{provider-managed-proxy-integration.test.ts → provider-platform-proxy-integration.test.ts} +8 -8
- package/src/__tests__/scaffold-managed-skill-tool.test.ts +65 -13
- package/src/__tests__/schedule-routes.test.ts +50 -3
- package/src/__tests__/schedule-store.test.ts +94 -0
- package/src/__tests__/scheduler-reuse-conversation.test.ts +54 -7
- package/src/__tests__/schema-transforms.test.ts +20 -0
- package/src/__tests__/search-skills-unified.test.ts +0 -5
- package/src/__tests__/server-history-render.test.ts +43 -0
- package/src/__tests__/skill-load-feature-flag.test.ts +0 -12
- package/src/__tests__/skill-load-tool.test.ts +27 -89
- package/src/__tests__/skill-memory.test.ts +23 -3
- package/src/__tests__/skills-file-content-endpoint.test.ts +9 -38
- package/src/__tests__/skills-files-catalog-fallback.test.ts +0 -3
- package/src/__tests__/skills-install-extract.test.ts +49 -38
- package/src/__tests__/skills-install-staging.test.ts +159 -0
- package/src/__tests__/skills-uninstall.test.ts +9 -41
- package/src/__tests__/skills.test.ts +51 -58
- package/src/__tests__/slack-channel-config.test.ts +9 -0
- package/src/__tests__/subagent-tool-filtering.test.ts +50 -0
- package/src/__tests__/system-prompt.test.ts +737 -63
- package/src/__tests__/terminal-tools.test.ts +28 -1
- package/src/__tests__/thread-backfill.test.ts +557 -27
- package/src/__tests__/title-generate-pipeline.test.ts +0 -13
- package/src/__tests__/token-estimate-pipeline.test.ts +0 -3
- package/src/__tests__/tool-error-pipeline.test.ts +0 -3
- package/src/__tests__/tool-execute-pipeline.test.ts +0 -5
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +1 -1
- package/src/__tests__/tool-executor.test.ts +16 -4
- package/src/__tests__/tool-result-truncate-pipeline.test.ts +0 -12
- package/src/__tests__/turn-events-store.test.ts +256 -0
- package/src/__tests__/twilio-routes.test.ts +4 -0
- package/src/__tests__/user-plugin-loader.test.ts +0 -7
- package/src/__tests__/voice-session-bridge.test.ts +198 -0
- package/src/__tests__/web-search-catalog-parity.test.ts +32 -10
- package/src/__tests__/workspace-migration-057-repair-stale-gemini-model-ids.test.ts +115 -3
- package/src/__tests__/workspace-migration-072-seed-reply-suggestion-callsite.test.ts +50 -0
- package/src/__tests__/workspace-migration-073-repair-recall-callsite-empty-profile.test.ts +153 -0
- package/src/__tests__/workspace-migration-085-memory-v2-bm25-b-reembed-disabled-v2-pages.test.ts +220 -0
- package/src/__tests__/workspace-migration-086-revert-stale-gemini-mis-rewrites.test.ts +269 -0
- package/src/__tests__/workspace-migration-remove-legacy-skills-index.test.ts +309 -0
- package/src/__tests__/workspace-migrations-runner.test.ts +111 -3
- package/src/acp/resolve-agent.ts +1 -1
- package/src/agent/image-optimize.ts +13 -5
- package/src/calls/voice-session-bridge.ts +61 -42
- package/src/channels/types.ts +108 -0
- package/src/cli/__tests__/unknown-command.test.ts +24 -0
- package/src/cli/commands/__tests__/changelog.test.ts +304 -319
- package/src/cli/commands/__tests__/schedules.test.ts +491 -0
- package/src/cli/commands/changelog.ts +106 -42
- package/src/cli/commands/conversations.ts +102 -17
- package/src/cli/commands/default-action.ts +10 -53
- package/src/cli/commands/notifications.ts +329 -317
- package/src/cli/commands/plugins.ts +185 -0
- package/src/cli/commands/schedules.ts +391 -0
- package/src/cli/commands/telemetry.ts +40 -0
- package/src/cli/lib/__tests__/cli-colors.test.ts +48 -0
- package/src/cli/lib/__tests__/confirm-prompt.test.ts +159 -0
- package/src/cli/lib/__tests__/install-from-github.test.ts +355 -0
- package/src/cli/lib/__tests__/list-installed-plugins.test.ts +154 -0
- package/src/cli/lib/__tests__/uninstall-plugin.test.ts +124 -0
- package/src/cli/lib/__tests__/unknown-command.test.ts +106 -0
- package/src/cli/lib/cli-colors.ts +12 -0
- package/src/cli/lib/confirm-prompt.ts +79 -0
- package/src/cli/lib/install-from-github.ts +304 -0
- package/src/cli/lib/list-installed-plugins.ts +137 -0
- package/src/cli/lib/uninstall-plugin.ts +82 -0
- package/src/cli/lib/unknown-command.ts +111 -0
- package/src/cli/program.ts +38 -2
- package/src/config/bundled-skills/app-builder/SKILL.md +23 -21
- package/src/config/bundled-skills/app-builder/TOOLS.json +7 -0
- package/src/config/bundled-skills/computer-use/TOOLS.json +15 -52
- package/src/config/bundled-skills/document/SKILL.md +23 -3
- package/src/config/bundled-skills/document/TOOLS.json +53 -0
- package/src/config/bundled-skills/document/tools/document-delete.ts +12 -0
- package/src/config/bundled-skills/document/tools/document-list.ts +12 -0
- package/src/config/bundled-skills/document/tools/document-read.ts +12 -0
- package/src/config/bundled-skills/skill-management/SKILL.md +2 -2
- package/src/config/bundled-skills/skill-management/TOOLS.json +7 -7
- package/src/config/bundled-tool-registry.ts +6 -0
- package/src/config/feature-flag-registry.json +41 -1
- package/src/config/loader.ts +64 -38
- package/src/config/schema.ts +7 -10
- package/src/config/schemas/__tests__/llm-request-logs.test.ts +36 -0
- package/src/config/schemas/channels.ts +8 -0
- package/src/config/schemas/compaction.ts +28 -0
- package/src/config/schemas/heartbeat.ts +9 -0
- package/src/config/schemas/llm-request-logs.ts +31 -7
- package/src/config/schemas/llm.ts +3 -0
- package/src/config/schemas/memory-retrieval.ts +18 -0
- package/src/config/schemas/tools.ts +14 -0
- package/src/config/skills.ts +3 -96
- package/src/context/compactor.ts +1047 -0
- package/src/context/token-estimator.ts +2 -2
- package/src/context/window-manager.ts +197 -1520
- package/src/credential-execution/managed-catalog.ts +37 -0
- package/src/credential-health/credential-health-service.ts +280 -19
- package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +34 -0
- package/src/daemon/__tests__/conversation-tool-setup-exclude.test.ts +138 -0
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +74 -0
- package/src/daemon/approval-generators.ts +8 -6
- package/src/daemon/config-watcher.ts +94 -31
- package/src/daemon/conversation-agent-loop.ts +169 -9
- package/src/daemon/conversation-error.ts +171 -37
- package/src/daemon/conversation-lifecycle.ts +53 -40
- package/src/daemon/conversation-messaging.ts +25 -6
- package/src/daemon/conversation-process.ts +49 -12
- package/src/daemon/conversation-runtime-assembly.ts +16 -1
- package/src/daemon/conversation-slash.ts +12 -5
- package/src/daemon/conversation-store.ts +11 -4
- package/src/daemon/conversation-tool-setup.ts +39 -7
- package/src/daemon/conversation.ts +33 -1
- package/src/daemon/external-plugins-bootstrap.ts +217 -181
- package/src/daemon/first-greeting.ts +22 -2
- package/src/daemon/handlers/config-model.ts +6 -5
- package/src/daemon/handlers/config-slack-channel.ts +15 -3
- package/src/daemon/handlers/shared.ts +14 -5
- package/src/daemon/handlers/skills.ts +111 -108
- package/src/daemon/history-repair.ts +28 -1
- package/src/daemon/host-app-control-proxy.ts +98 -23
- package/src/daemon/lifecycle.ts +45 -35
- package/src/daemon/meet-host-supervisor.ts +5 -4
- package/src/daemon/memory-v2-startup.ts +49 -0
- package/src/daemon/message-protocol.ts +1 -0
- package/src/daemon/message-types/conversations.ts +25 -0
- package/src/daemon/message-types/messages.ts +61 -0
- package/src/daemon/message-types/subagents.ts +1 -0
- package/src/daemon/message-types/sync.ts +1 -0
- package/src/daemon/pkb-reminder-builder.test.ts +1 -1
- package/src/daemon/pkb-reminder-builder.ts +1 -1
- package/src/daemon/plugin-source-watcher.ts +146 -0
- package/src/daemon/process-message.ts +21 -3
- package/src/daemon/server.ts +11 -2
- package/src/daemon/skill-memory-refresh.ts +29 -0
- package/src/documents/document-store.ts +221 -3
- package/src/embedded/plugin-api.ts +40 -0
- package/src/filing/filing-service.ts +39 -0
- package/src/heartbeat/__tests__/heartbeat-service.test.ts +91 -6
- package/src/heartbeat/heartbeat-run-store.ts +2 -1
- package/src/heartbeat/heartbeat-service.ts +41 -0
- package/src/home/__tests__/feed-types.test.ts +40 -0
- package/src/home/feed-types.ts +22 -0
- package/src/home/post-connect-feed.ts +1 -0
- package/src/index.ts +18 -1
- package/src/live-voice/__tests__/live-voice-stt.test.ts +57 -0
- package/src/mcp/client.ts +20 -4
- package/src/media/image-credentials.ts +3 -3
- package/src/memory/__tests__/bookmark-crud.test.ts +33 -27
- package/src/memory/__tests__/conversation-queries.test.ts +263 -0
- package/src/memory/__tests__/jobs-worker-v2-graph-trigger-embed.test.ts +113 -0
- package/src/memory/__tests__/memory-retrospective-startup-cleanup.test.ts +119 -14
- package/src/memory/__tests__/message-content.test.ts +35 -0
- package/src/memory/bookmark-crud.ts +42 -10
- package/src/memory/context-search/sources/conversations.ts +62 -2
- package/src/memory/context-search/sources/workspace.ts +4 -0
- package/src/memory/conversation-crud.ts +63 -19
- package/src/memory/conversation-queries.ts +110 -10
- package/src/memory/db-init.ts +6 -0
- package/src/memory/delivery-crud.ts +152 -5
- package/src/memory/embedding-backend.ts +4 -4
- package/src/memory/external-conversation-store.ts +66 -5
- package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +66 -9
- package/src/memory/graph/conversation-graph-memory.ts +31 -15
- package/src/memory/graph/tools.ts +3 -3
- package/src/memory/indexer.ts +34 -29
- package/src/memory/jobs/__tests__/embed-concept-page.test.ts +73 -0
- package/src/memory/jobs/embed-concept-page.ts +20 -11
- package/src/memory/jobs-worker.ts +6 -1
- package/src/memory/llm-request-log-source-clickhouse.ts +17 -10
- package/src/memory/llm-request-log-source.ts +19 -52
- package/src/memory/llm-usage-store.ts +125 -5
- package/src/memory/memory-retrospective-startup-cleanup.ts +72 -5
- package/src/memory/message-content.ts +1 -1
- package/src/memory/migrations/109-external-conversation-bindings.ts +15 -4
- package/src/memory/migrations/229-delete-private-conversations.test.ts +38 -1
- package/src/memory/migrations/229-delete-private-conversations.ts +7 -0
- package/src/memory/migrations/247-external-conversation-binding-thread-id.ts +78 -0
- package/src/memory/migrations/248-create-onboarding-events.ts +21 -0
- package/src/memory/migrations/249-normalize-slack-external-content.ts +240 -0
- package/src/memory/migrations/index.ts +6 -0
- package/src/memory/migrations/registry.ts +8 -0
- package/src/memory/onboarding-events-store.ts +106 -0
- package/src/memory/schema/bookmarks.ts +0 -2
- package/src/memory/schema/calls.ts +1 -0
- package/src/memory/schema/inference.ts +1 -3
- package/src/memory/schema/infrastructure.ts +12 -0
- package/src/memory/turn-events-store.ts +127 -2
- package/src/memory/v2/__tests__/activation.test.ts +0 -8
- package/src/memory/v2/__tests__/injection.test.ts +98 -8
- package/src/memory/v2/__tests__/migration.test.ts +87 -0
- package/src/memory/v2/__tests__/page-index.test.ts +83 -0
- package/src/memory/v2/__tests__/prompts-router.test.ts +58 -6
- package/src/memory/v2/__tests__/qdrant.test.ts +66 -3
- package/src/memory/v2/__tests__/router.test.ts +15 -0
- package/src/memory/v2/__tests__/skill-store.test.ts +387 -8
- package/src/memory/v2/injection.ts +32 -6
- package/src/memory/v2/migration.ts +49 -19
- package/src/memory/v2/page-index.ts +35 -5
- package/src/memory/v2/prompts/router.ts +11 -8
- package/src/memory/v2/prompts/sweep.ts +2 -2
- package/src/memory/v2/qdrant.ts +135 -7
- package/src/memory/v2/router.ts +9 -8
- package/src/memory/v2/skill-store.ts +120 -35
- package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +45 -5
- package/src/messaging/providers/slack/__tests__/download.test.ts +231 -0
- package/src/messaging/providers/slack/adapter.ts +43 -5
- package/src/messaging/providers/slack/client.ts +27 -0
- package/src/messaging/providers/slack/deep-link.ts +65 -0
- package/src/messaging/providers/slack/download.ts +104 -0
- package/src/messaging/providers/slack/message-metadata.test.ts +32 -0
- package/src/messaging/providers/slack/message-metadata.ts +27 -0
- package/src/messaging/providers/slack/render-transcript.test.ts +134 -0
- package/src/messaging/providers/slack/render-transcript.ts +69 -5
- package/src/messaging/providers/slack/types.ts +20 -1
- package/src/notifications/conversation-pairing.ts +2 -1
- package/src/notifications/decision-engine.ts +2 -1
- package/src/notifications/emit-signal.ts +20 -1
- package/src/notifications/home-feed-side-effect.ts +54 -0
- package/src/notifications/signal.ts +3 -1
- package/src/oauth/connection-resolver.ts +8 -4
- package/src/oauth/platform-connection.ts +6 -2
- package/src/oauth/seed-providers.ts +10 -1
- package/src/permissions/checker.ts +2 -0
- package/src/permissions/ipc-risk-types.ts +1 -0
- package/src/permissions/question-prompter.test.ts +416 -0
- package/src/permissions/question-prompter.ts +294 -0
- package/src/platform/client.test.ts +1 -1
- package/src/platform/client.ts +1 -1
- package/src/plugin-api/constants.ts +26 -0
- package/src/plugin-api/index.ts +34 -1
- package/src/plugin-api/types.ts +104 -22
- package/src/plugins/defaults/circuit-breaker.ts +0 -5
- package/src/plugins/defaults/compaction.ts +0 -4
- package/src/plugins/defaults/empty-response.ts +0 -2
- package/src/plugins/defaults/history-repair.ts +0 -2
- package/src/plugins/defaults/injectors.ts +36 -3
- package/src/plugins/defaults/llm-call.ts +0 -2
- package/src/plugins/defaults/memory-retrieval.ts +0 -1
- package/src/plugins/defaults/overflow-reduce.ts +0 -1
- package/src/plugins/defaults/persistence.ts +0 -2
- package/src/plugins/defaults/title-generate.ts +0 -5
- package/src/plugins/defaults/token-estimate.ts +0 -2
- package/src/plugins/defaults/tool-error.ts +0 -7
- package/src/plugins/defaults/tool-execute.ts +0 -2
- package/src/plugins/defaults/tool-result-truncate.ts +0 -4
- package/src/plugins/ensure-plugin-api-shim.ts +96 -0
- package/src/plugins/external-api.ts +104 -0
- package/src/plugins/external-plugin-loader.ts +105 -32
- package/src/plugins/feature-gate.ts +22 -0
- package/src/plugins/pipeline.ts +37 -0
- package/src/plugins/registry.ts +48 -80
- package/src/plugins/types.ts +31 -26
- package/src/plugins/user-loader.ts +21 -2
- package/src/proactive-artifact/aux-message-injector.ts +11 -0
- package/src/proactive-artifact/job.test.ts +37 -5
- package/src/prompts/__tests__/system-prompt.test.ts +12 -0
- package/src/prompts/__tests__/task-progress-hint-section.test.ts +99 -0
- package/src/prompts/normalize-onboarding.ts +27 -0
- package/src/prompts/sections.ts +302 -0
- package/src/prompts/system-prompt.ts +63 -166
- package/src/prompts/templates/BOOTSTRAP.md +17 -1
- package/src/prompts/templates/system-sections.ts +173 -0
- package/src/providers/__tests__/inference.test.ts +22 -7
- package/src/providers/anthropic/client.ts +28 -28
- package/src/providers/connection-resolution.ts +7 -0
- package/src/providers/inference/adapter-factory.ts +41 -4
- package/src/providers/inference/connections.ts +74 -29
- package/src/providers/inference/resolve-auth.ts +12 -4
- package/src/providers/model-catalog.ts +294 -12
- package/src/providers/openai/chat-completions-provider.ts +10 -2
- package/src/providers/openrouter/client.ts +7 -0
- package/src/providers/{managed-proxy → platform-proxy}/constants.ts +4 -1
- package/src/providers/{managed-proxy → platform-proxy}/context.ts +3 -3
- package/src/providers/provider-availability.ts +17 -2
- package/src/providers/provider-catalog-visibility.ts +36 -0
- package/src/providers/registry.ts +22 -14
- package/src/providers/retry.ts +47 -1
- package/src/runtime/__tests__/agent-wake.test.ts +152 -0
- package/src/runtime/agent-wake.ts +42 -14
- package/src/runtime/auth/route-policy.ts +8 -1
- package/src/runtime/btw-sidechain.ts +2 -0
- package/src/runtime/http-types.ts +19 -0
- package/src/runtime/migrations/origin-mode.ts +1 -1
- package/src/runtime/pending-interactions.ts +1 -0
- package/src/runtime/routes/__tests__/bookmark-routes.test.ts +17 -0
- package/src/runtime/routes/__tests__/conversation-management-routes.test.ts +5 -1
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +107 -20
- package/src/runtime/routes/__tests__/question-routes.test.ts +395 -0
- package/src/runtime/routes/__tests__/tts-routes.test.ts +64 -1
- package/src/runtime/routes/acp-routes-list.test.ts +143 -0
- package/src/runtime/routes/acp-routes.ts +5 -3
- package/src/runtime/routes/auth-routes.ts +1 -1
- package/src/runtime/routes/bookmark-routes.ts +5 -3
- package/src/runtime/routes/btw-routes.ts +5 -1
- package/src/runtime/routes/channel-availability-routes.ts +121 -0
- package/src/runtime/routes/conversation-cli-routes.ts +44 -3
- package/src/runtime/routes/conversation-list-routes.ts +3 -20
- package/src/runtime/routes/conversation-management-routes.ts +17 -42
- package/src/runtime/routes/conversation-query-routes.ts +40 -35
- package/src/runtime/routes/conversation-routes.ts +90 -11
- package/src/runtime/routes/documents-routes.ts +25 -86
- package/src/runtime/routes/group-routes.ts +5 -0
- package/src/runtime/routes/inbound-conversation.ts +28 -8
- package/src/runtime/routes/inbound-message-handler.ts +236 -41
- package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +111 -0
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +32 -1
- package/src/runtime/routes/inbound-stages/edit-intercept.ts +17 -4
- package/src/runtime/routes/index.ts +6 -0
- package/src/runtime/routes/inference-profile-session-handler.ts +17 -44
- package/src/runtime/routes/inference-profile-session-reaper.ts +7 -21
- package/src/runtime/routes/inference-provider-connection-routes.ts +65 -21
- package/src/runtime/routes/integrations/slack/share.ts +4 -52
- package/src/runtime/routes/integrations/slack/token.ts +43 -0
- package/src/runtime/routes/integrations/twilio.ts +6 -13
- package/src/runtime/routes/notification-routes.ts +1 -1
- package/src/runtime/routes/oauth-commands-routes.ts +105 -15
- package/src/runtime/routes/oauth-lifecycle-routes.ts +43 -0
- package/src/runtime/routes/question-routes.ts +259 -0
- package/src/runtime/routes/rename-conversation-routes.ts +2 -33
- package/src/runtime/routes/schedule-routes.ts +4 -7
- package/src/runtime/routes/subagents-routes.ts +57 -18
- package/src/runtime/routes/telemetry-routes.ts +27 -0
- package/src/runtime/routes/tts-routes.ts +27 -2
- package/src/runtime/routes/workspace-routes.test.ts +43 -0
- package/src/runtime/routes/workspace-routes.ts +28 -0
- package/src/runtime/services/conversation-serializer.ts +39 -7
- package/src/runtime/sync/resource-sync-events.ts +93 -1
- package/src/schedule/schedule-store.ts +27 -2
- package/src/schedule/scheduler.ts +9 -1
- package/src/security/__tests__/untrusted-content.test.ts +86 -0
- package/src/security/untrusted-content.ts +93 -8
- package/src/skills/catalog-files.ts +1 -1
- package/src/skills/catalog-install.ts +233 -116
- package/src/skills/clawhub.ts +70 -13
- package/src/skills/managed-store.ts +4 -119
- package/src/skills/skillssh-registry.ts +27 -48
- package/src/subagent/manager.ts +15 -7
- package/src/telemetry/types.ts +113 -1
- package/src/telemetry/usage-telemetry-reporter.test.ts +312 -5
- package/src/telemetry/usage-telemetry-reporter.ts +113 -7
- package/src/tools/apps/executors.ts +58 -7
- package/src/tools/ask-question/ask-question-tool.test.ts +509 -0
- package/src/tools/ask-question/ask-question-tool.ts +304 -0
- package/src/tools/browser/browser-execution.ts +15 -11
- package/src/tools/computer-use/definitions.ts +3 -3
- package/src/tools/credentials/vault.ts +1 -1
- package/src/tools/document/document-tool.ts +124 -1
- package/src/tools/filesystem/edit.ts +1 -1
- package/src/tools/filesystem/list.ts +1 -1
- package/src/tools/filesystem/read.ts +1 -1
- package/src/tools/filesystem/write.ts +5 -2
- package/src/tools/host-filesystem/transfer.ts +1 -1
- package/src/tools/host-terminal/host-shell.ts +1 -1
- package/src/tools/permission-checker.ts +1 -1
- package/src/tools/registry.ts +17 -7
- package/src/tools/schedule/create.ts +2 -2
- package/src/tools/schema-transforms.ts +7 -2
- package/src/tools/side-effects.ts +1 -0
- package/src/tools/skills/delete-managed.ts +4 -4
- package/src/tools/skills/execute.ts +1 -1
- package/src/tools/skills/scaffold-managed.ts +3 -2
- package/src/tools/subagent/notify-parent.ts +1 -1
- package/src/tools/system/request-permission.ts +2 -2
- package/src/tools/terminal/safe-env.ts +60 -1
- package/src/tools/tool-manifest.ts +2 -0
- package/src/tools/types.ts +72 -21
- package/src/tools/ui-surface/definitions.ts +6 -5
- package/src/tts/__tests__/provider-adapters.test.ts +76 -2
- package/src/tts/providers/elevenlabs-provider.ts +75 -1
- package/src/types/onboarding-context.ts +2 -0
- package/src/util/errors.ts +17 -0
- package/src/util/platform.ts +10 -0
- package/src/watcher/__tests__/engine.test.ts +22 -0
- package/src/watcher/engine.ts +6 -2
- package/src/workspace/migrations/057-repair-stale-gemini-model-ids.ts +80 -15
- package/src/workspace/migrations/072-seed-reply-suggestion-callsite.ts +35 -22
- package/src/workspace/migrations/073-repair-recall-callsite-empty-profile.ts +3 -1
- package/src/workspace/migrations/083-system-prompt-prefix-to-file.ts +191 -0
- package/src/workspace/migrations/084-remove-legacy-skills-index.ts +276 -0
- package/src/workspace/migrations/085-memory-v2-bm25-b-reembed-disabled-v2-pages.ts +137 -0
- package/src/workspace/migrations/086-revert-stale-gemini-mis-rewrites.ts +198 -0
- package/src/workspace/migrations/registry.ts +8 -0
- package/src/workspace/migrations/runner.ts +39 -9
- package/src/workspace/migrations/types.ts +4 -0
- package/examples/plugins/echo/bun.lock +0 -25
- package/src/__tests__/context-window-manager.test.ts +0 -2481
- package/src/context/__tests__/compact-prompt.test.ts +0 -63
- package/src/context/prompts/compact.md +0 -26
- package/src/prompts/__tests__/build-cli-reference-section.test.ts +0 -37
- /package/src/__tests__/{secret-routes-managed-proxy.test.ts → secret-routes-platform-proxy.test.ts} +0 -0
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Enumerate plugins materialized under `<workspaceDir>/plugins/`.
|
|
3
|
+
*
|
|
4
|
+
* The CLI command `assistant plugins list` is a thin wrapper. Downstream
|
|
5
|
+
* callers (the daemon's diagnostics surface, a future TUI, scripted
|
|
6
|
+
* audits) can call {@link listInstalledPlugins} directly without going
|
|
7
|
+
* through commander.
|
|
8
|
+
*
|
|
9
|
+
* Designed to be lenient: a malformed `package.json` is reported as an
|
|
10
|
+
* error on that one entry rather than failing the whole listing. The
|
|
11
|
+
* daemon makes the same call on boot via `external-plugin-loader.ts`
|
|
12
|
+
* and we want both surfaces to agree on what's present.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import {
|
|
16
|
+
existsSync,
|
|
17
|
+
readdirSync,
|
|
18
|
+
readFileSync,
|
|
19
|
+
statSync,
|
|
20
|
+
} from "node:fs";
|
|
21
|
+
import { join } from "node:path";
|
|
22
|
+
|
|
23
|
+
import { getWorkspacePluginsDir } from "../../util/platform.js";
|
|
24
|
+
|
|
25
|
+
/** Minimal manifest fields surfaced to the CLI. */
|
|
26
|
+
export interface PluginPackageMetadata {
|
|
27
|
+
readonly name?: string;
|
|
28
|
+
readonly version?: string;
|
|
29
|
+
readonly description?: string;
|
|
30
|
+
readonly peerDependencies?: Record<string, string>;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** One installed plugin entry. */
|
|
34
|
+
export interface InstalledPluginInfo {
|
|
35
|
+
/** Directory name under `<workspaceDir>/plugins/`. */
|
|
36
|
+
readonly name: string;
|
|
37
|
+
/** Absolute path to the plugin directory. */
|
|
38
|
+
readonly target: string;
|
|
39
|
+
/** Parsed `package.json` content, when present and parseable. */
|
|
40
|
+
readonly packageJson: PluginPackageMetadata | null;
|
|
41
|
+
/**
|
|
42
|
+
* Non-fatal issues with this entry (missing `package.json`, malformed
|
|
43
|
+
* JSON, unexpected type, etc.). Empty when the entry parses cleanly.
|
|
44
|
+
*/
|
|
45
|
+
readonly issues: readonly string[];
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** Options accepted by {@link listInstalledPlugins}. */
|
|
49
|
+
export interface ListInstalledPluginsOptions {
|
|
50
|
+
/** Override the workspace plugins directory. Falls back to {@link getWorkspacePluginsDir}. */
|
|
51
|
+
readonly workspacePluginsDir?: string;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Return one entry per directory under the workspace plugins directory,
|
|
56
|
+
* sorted alphabetically by name. Hidden entries (`.`-prefixed) and
|
|
57
|
+
* non-directory entries are skipped silently — the daemon's loader does
|
|
58
|
+
* the same. Returns `[]` if the plugins directory does not exist.
|
|
59
|
+
*/
|
|
60
|
+
export function listInstalledPlugins(
|
|
61
|
+
opts: ListInstalledPluginsOptions = {},
|
|
62
|
+
): InstalledPluginInfo[] {
|
|
63
|
+
const pluginsDir = opts.workspacePluginsDir ?? getWorkspacePluginsDir();
|
|
64
|
+
if (!existsSync(pluginsDir)) return [];
|
|
65
|
+
|
|
66
|
+
const entries = readdirSync(pluginsDir, { withFileTypes: true })
|
|
67
|
+
.filter((e) => !e.name.startsWith("."))
|
|
68
|
+
.filter((e) => {
|
|
69
|
+
if (e.isDirectory()) return true;
|
|
70
|
+
if (!e.isSymbolicLink()) return false;
|
|
71
|
+
// Resolve the symlink and only keep it if it points to a directory.
|
|
72
|
+
try {
|
|
73
|
+
return statSync(join(pluginsDir, e.name)).isDirectory();
|
|
74
|
+
} catch {
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
})
|
|
78
|
+
.map((e) => e.name)
|
|
79
|
+
.sort();
|
|
80
|
+
|
|
81
|
+
return entries.map((name) => readPluginEntry(pluginsDir, name));
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function readPluginEntry(
|
|
85
|
+
pluginsDir: string,
|
|
86
|
+
name: string,
|
|
87
|
+
): InstalledPluginInfo {
|
|
88
|
+
const target = join(pluginsDir, name);
|
|
89
|
+
const pkgJsonPath = join(target, "package.json");
|
|
90
|
+
const issues: string[] = [];
|
|
91
|
+
|
|
92
|
+
if (!existsSync(pkgJsonPath)) {
|
|
93
|
+
issues.push("missing package.json");
|
|
94
|
+
return { name, target, packageJson: null, issues };
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
let raw: string;
|
|
98
|
+
try {
|
|
99
|
+
raw = readFileSync(pkgJsonPath, "utf8");
|
|
100
|
+
} catch (err) {
|
|
101
|
+
issues.push(
|
|
102
|
+
`package.json unreadable: ${err instanceof Error ? err.message : String(err)}`,
|
|
103
|
+
);
|
|
104
|
+
return { name, target, packageJson: null, issues };
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
let parsed: unknown;
|
|
108
|
+
try {
|
|
109
|
+
parsed = JSON.parse(raw);
|
|
110
|
+
} catch (err) {
|
|
111
|
+
issues.push(
|
|
112
|
+
`package.json invalid JSON: ${err instanceof Error ? err.message : String(err)}`,
|
|
113
|
+
);
|
|
114
|
+
return { name, target, packageJson: null, issues };
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
118
|
+
issues.push("package.json is not an object");
|
|
119
|
+
return { name, target, packageJson: null, issues };
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const meta = parsed as Record<string, unknown>;
|
|
123
|
+
const packageJson: PluginPackageMetadata = {
|
|
124
|
+
name: typeof meta.name === "string" ? meta.name : undefined,
|
|
125
|
+
version: typeof meta.version === "string" ? meta.version : undefined,
|
|
126
|
+
description:
|
|
127
|
+
typeof meta.description === "string" ? meta.description : undefined,
|
|
128
|
+
peerDependencies:
|
|
129
|
+
typeof meta.peerDependencies === "object" &&
|
|
130
|
+
meta.peerDependencies !== null &&
|
|
131
|
+
!Array.isArray(meta.peerDependencies)
|
|
132
|
+
? (meta.peerDependencies as Record<string, string>)
|
|
133
|
+
: undefined,
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
return { name, target, packageJson, issues };
|
|
137
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Remove a plugin previously materialized under `<workspaceDir>/plugins/`.
|
|
3
|
+
*
|
|
4
|
+
* Symmetric to {@link ./install-from-github.installPlugin}. The CLI
|
|
5
|
+
* command `assistant plugins uninstall <name>` is a thin wrapper that
|
|
6
|
+
* supplies the live workspace directory and formats the result.
|
|
7
|
+
*
|
|
8
|
+
* The operation is destructive — a successful return means the plugin
|
|
9
|
+
* directory and everything beneath it have been removed from disk.
|
|
10
|
+
* Callers needing a confirmation prompt should run it before invoking
|
|
11
|
+
* this function (the CLI command does this via `--force`).
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { existsSync, rmSync, statSync } from "node:fs";
|
|
15
|
+
import { join } from "node:path";
|
|
16
|
+
|
|
17
|
+
import { getWorkspacePluginsDir } from "../../util/platform.js";
|
|
18
|
+
import {
|
|
19
|
+
InvalidPluginNameError,
|
|
20
|
+
sanitizePluginName,
|
|
21
|
+
} from "./install-from-github.js";
|
|
22
|
+
|
|
23
|
+
/** Plugin is not present in the workspace plugins directory. */
|
|
24
|
+
export class PluginNotInstalledError extends Error {
|
|
25
|
+
constructor(
|
|
26
|
+
readonly pluginName: string,
|
|
27
|
+
readonly target: string,
|
|
28
|
+
) {
|
|
29
|
+
super(`Plugin "${pluginName}" is not installed at ${target}.`);
|
|
30
|
+
this.name = "PluginNotInstalledError";
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** Options accepted by {@link uninstallPlugin}. */
|
|
35
|
+
export interface UninstallPluginOptions {
|
|
36
|
+
readonly name: string;
|
|
37
|
+
/** Override the workspace plugins directory. Falls back to {@link getWorkspacePluginsDir}. */
|
|
38
|
+
readonly workspacePluginsDir?: string;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** Result of a successful uninstall. */
|
|
42
|
+
export interface UninstallPluginResult {
|
|
43
|
+
readonly name: string;
|
|
44
|
+
/** Absolute path that was removed. */
|
|
45
|
+
readonly target: string;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Validate the name, confirm the plugin exists, then recursively remove
|
|
50
|
+
* the install target. Throws {@link InvalidPluginNameError} if the name
|
|
51
|
+
* fails sanitization or {@link PluginNotInstalledError} if no directory
|
|
52
|
+
* (or symlink to a directory) is present at the resolved target.
|
|
53
|
+
*
|
|
54
|
+
* The name check is performed up front so an attacker-supplied
|
|
55
|
+
* `../../etc/passwd` style argument never reaches `rmSync` — even
|
|
56
|
+
* though commander typically prevents it at the argv level, defense in
|
|
57
|
+
* depth.
|
|
58
|
+
*/
|
|
59
|
+
export function uninstallPlugin(
|
|
60
|
+
opts: UninstallPluginOptions,
|
|
61
|
+
): UninstallPluginResult {
|
|
62
|
+
const name = sanitizePluginName(opts.name);
|
|
63
|
+
const pluginsDir = opts.workspacePluginsDir ?? getWorkspacePluginsDir();
|
|
64
|
+
const target = join(pluginsDir, name);
|
|
65
|
+
|
|
66
|
+
if (!existsSync(target)) {
|
|
67
|
+
throw new PluginNotInstalledError(name, target);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// `existsSync` follows symlinks; guard against a stray file with the
|
|
71
|
+
// plugin's name (which would be surprising rather than dangerous —
|
|
72
|
+
// we'd refuse to delete it).
|
|
73
|
+
const stats = statSync(target);
|
|
74
|
+
if (!stats.isDirectory()) {
|
|
75
|
+
throw new PluginNotInstalledError(name, target);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
rmSync(target, { recursive: true, force: true });
|
|
79
|
+
return { name, target };
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export { InvalidPluginNameError };
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import type { Command } from "commander";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Result of detecting an unknown top-level subcommand. `suggestion` is set
|
|
5
|
+
* when a near-match is found (Levenshtein distance ≤ 40% of the longer name).
|
|
6
|
+
*/
|
|
7
|
+
export interface UnknownCommandHit {
|
|
8
|
+
readonly token: string;
|
|
9
|
+
readonly suggestion?: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Collect every top-level subcommand name and alias registered on `program`.
|
|
14
|
+
*/
|
|
15
|
+
export function knownCommandNames(program: Command): Set<string> {
|
|
16
|
+
const names = new Set<string>();
|
|
17
|
+
for (const cmd of program.commands) {
|
|
18
|
+
names.add(cmd.name());
|
|
19
|
+
for (const alias of cmd.aliases()) {
|
|
20
|
+
names.add(alias);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return names;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Pre-parse scan: returns the first positional argv token that doesn't match
|
|
28
|
+
* any known subcommand/alias, or null when the args look valid.
|
|
29
|
+
*
|
|
30
|
+
* Commander processes `--help` / `--version` before any action or hook runs,
|
|
31
|
+
* so `assistant invalid --help` would otherwise dump the root help instead of
|
|
32
|
+
* surfacing the unknown command. Callers should run this before `parse()` so
|
|
33
|
+
* the error wins over the help short-circuit.
|
|
34
|
+
*
|
|
35
|
+
* The first non-flag token is treated as the subcommand candidate. Flags are
|
|
36
|
+
* skipped wholesale; the root program has no value-taking options today.
|
|
37
|
+
*/
|
|
38
|
+
export function detectUnknownCommand(
|
|
39
|
+
program: Command,
|
|
40
|
+
argv: readonly string[],
|
|
41
|
+
): UnknownCommandHit | null {
|
|
42
|
+
const firstPositional = argv.find((token) => !token.startsWith("-"));
|
|
43
|
+
if (!firstPositional) return null;
|
|
44
|
+
|
|
45
|
+
const known = knownCommandNames(program);
|
|
46
|
+
if (known.has(firstPositional)) return null;
|
|
47
|
+
|
|
48
|
+
const suggestion = findClosestCommand(firstPositional, [...known]);
|
|
49
|
+
return suggestion ? { token: firstPositional, suggestion } : { token: firstPositional };
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Format the unknown-command error as a multi-line message. Kept as a pure
|
|
54
|
+
* function so it can be reused by `default-action`'s in-parse path (when the
|
|
55
|
+
* user runs `assistant invalid` with no `--help`) and by the pre-parse path.
|
|
56
|
+
*/
|
|
57
|
+
export function formatUnknownCommandMessage(hit: UnknownCommandHit): string {
|
|
58
|
+
const lines = [`unknown command '${hit.token}'`];
|
|
59
|
+
if (hit.suggestion) {
|
|
60
|
+
lines.push(`(Did you mean '${hit.suggestion}'?)`);
|
|
61
|
+
}
|
|
62
|
+
lines.push(`Run 'assistant --help' to see a list of available commands.`);
|
|
63
|
+
return lines.join("\n");
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Find the closest matching command name using Levenshtein distance.
|
|
68
|
+
* Returns the best match if the distance is ≤ 40% of the longer string's
|
|
69
|
+
* length, otherwise returns undefined.
|
|
70
|
+
*/
|
|
71
|
+
export function findClosestCommand(
|
|
72
|
+
input: string,
|
|
73
|
+
candidates: readonly string[],
|
|
74
|
+
): string | undefined {
|
|
75
|
+
let best: string | undefined;
|
|
76
|
+
let bestDist = Infinity;
|
|
77
|
+
const lowered = input.toLowerCase();
|
|
78
|
+
|
|
79
|
+
for (const name of candidates) {
|
|
80
|
+
const dist = levenshtein(lowered, name.toLowerCase());
|
|
81
|
+
if (dist < bestDist) {
|
|
82
|
+
bestDist = dist;
|
|
83
|
+
best = name;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const maxLen = Math.max(input.length, best?.length ?? 0);
|
|
88
|
+
if (best && bestDist <= Math.ceil(maxLen * 0.4)) {
|
|
89
|
+
return best;
|
|
90
|
+
}
|
|
91
|
+
return undefined;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function levenshtein(a: string, b: string): number {
|
|
95
|
+
const m = a.length;
|
|
96
|
+
const n = b.length;
|
|
97
|
+
const dp: number[][] = Array.from({ length: m + 1 }, () =>
|
|
98
|
+
Array(n + 1).fill(0),
|
|
99
|
+
);
|
|
100
|
+
for (let i = 0; i <= m; i++) dp[i][0] = i;
|
|
101
|
+
for (let j = 0; j <= n; j++) dp[0][j] = j;
|
|
102
|
+
for (let i = 1; i <= m; i++) {
|
|
103
|
+
for (let j = 1; j <= n; j++) {
|
|
104
|
+
dp[i][j] =
|
|
105
|
+
a[i - 1] === b[j - 1]
|
|
106
|
+
? dp[i - 1][j - 1]
|
|
107
|
+
: 1 + Math.min(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return dp[m][n];
|
|
111
|
+
}
|
package/src/cli/program.ts
CHANGED
|
@@ -5,6 +5,7 @@ import { Command } from "commander";
|
|
|
5
5
|
import { initFeatureFlagOverrides } from "../config/assistant-feature-flags.js";
|
|
6
6
|
import { getConfigReadOnly } from "../config/loader.js";
|
|
7
7
|
import { isEmailEnabled } from "../email/feature-gate.js";
|
|
8
|
+
import { isExternalPluginsEnabled } from "../plugins/feature-gate.js";
|
|
8
9
|
import { getWorkspaceDir } from "../util/platform.js";
|
|
9
10
|
import { APP_VERSION } from "../version.js";
|
|
10
11
|
import { registerAttachmentCommand } from "./commands/attachment.js";
|
|
@@ -37,18 +38,22 @@ import { registerNotificationsCommand } from "./commands/notifications.js";
|
|
|
37
38
|
import { registerOAuthCommand } from "./commands/oauth/index.js";
|
|
38
39
|
import { registerPendingCommand } from "./commands/pending.js";
|
|
39
40
|
import { registerPlatformCommand } from "./commands/platform/index.js";
|
|
41
|
+
import { registerPluginsCommand } from "./commands/plugins.js";
|
|
40
42
|
import { registerRoutesCommand } from "./commands/routes.js";
|
|
43
|
+
import { registerSchedulesCommand } from "./commands/schedules.js";
|
|
41
44
|
import { registerSequenceCommand } from "./commands/sequence.js";
|
|
42
45
|
import { registerSkillsCommand } from "./commands/skills.js";
|
|
43
46
|
import { registerStatusCommand } from "./commands/status.js";
|
|
44
47
|
import { registerSttCommand } from "./commands/stt.js";
|
|
45
48
|
import { registerTaskCommand } from "./commands/task.js";
|
|
49
|
+
import { registerTelemetryCommand } from "./commands/telemetry.js";
|
|
46
50
|
import { registerTrustCommand } from "./commands/trust.js";
|
|
47
51
|
import { registerTtsCommand } from "./commands/tts.js";
|
|
48
52
|
import { registerUiCommand } from "./commands/ui.js";
|
|
49
53
|
import { registerUsageCommand } from "./commands/usage.js";
|
|
50
54
|
import { registerWatchersCommand } from "./commands/watchers.js";
|
|
51
55
|
import { registerWebhooksCommand } from "./commands/webhooks.js";
|
|
56
|
+
import { red } from "./lib/cli-colors.js";
|
|
52
57
|
import { log } from "./logger.js";
|
|
53
58
|
|
|
54
59
|
/**
|
|
@@ -65,6 +70,13 @@ export async function buildCliProgram(): Promise<Command> {
|
|
|
65
70
|
.version(APP_VERSION)
|
|
66
71
|
.allowExcessArguments(true);
|
|
67
72
|
|
|
73
|
+
// Color Commander-emitted error output red (unknown options, missing args,
|
|
74
|
+
// cmd.error() calls). Plain success output and --help text are untouched.
|
|
75
|
+
// The `red` helper is a no-op when stderr isn't a TTY or NO_COLOR is set.
|
|
76
|
+
program.configureOutput({
|
|
77
|
+
outputError: (str, write) => write(red(str)),
|
|
78
|
+
});
|
|
79
|
+
|
|
68
80
|
program.addHelpText(
|
|
69
81
|
"after",
|
|
70
82
|
`
|
|
@@ -107,12 +119,17 @@ Examples:
|
|
|
107
119
|
registerOAuthCommand(program);
|
|
108
120
|
registerPendingCommand(program);
|
|
109
121
|
registerPlatformCommand(program);
|
|
122
|
+
if (isExternalPluginsEnabled(getConfigReadOnly())) {
|
|
123
|
+
registerPluginsCommand(program);
|
|
124
|
+
}
|
|
110
125
|
registerRoutesCommand(program);
|
|
126
|
+
registerSchedulesCommand(program);
|
|
111
127
|
registerSequenceCommand(program);
|
|
112
128
|
registerStatusCommand(program);
|
|
113
129
|
registerSkillsCommand(program);
|
|
114
130
|
registerSttCommand(program);
|
|
115
131
|
registerTaskCommand(program);
|
|
132
|
+
registerTelemetryCommand(program);
|
|
116
133
|
registerTrustCommand(program);
|
|
117
134
|
registerTtsCommand(program);
|
|
118
135
|
registerUiCommand(program);
|
|
@@ -126,9 +143,28 @@ Examples:
|
|
|
126
143
|
// remain available even without a workspace.
|
|
127
144
|
// Workspace-independent commands are exempt:
|
|
128
145
|
// completions — pure shell-script generation, no workspace files needed
|
|
129
|
-
|
|
146
|
+
// status — diagnostic; should run even when the workspace is broken
|
|
147
|
+
// changelog — pure read-only network surface backed by GitHub Releases;
|
|
148
|
+
// its on-disk cache is best-effort and tolerates a missing
|
|
149
|
+
// workspace dir (see changelog.ts:writeCache)
|
|
150
|
+
const workspaceExemptCommands = new Set([
|
|
151
|
+
"completions",
|
|
152
|
+
"status",
|
|
153
|
+
"changelog",
|
|
154
|
+
]);
|
|
155
|
+
// An action command's `.name()` returns the leaf (e.g. "show" for
|
|
156
|
+
// `changelog show <ver>`), so we walk up the parent chain to see whether
|
|
157
|
+
// any ancestor — typically the top-level subcommand — is exempt.
|
|
158
|
+
const isExemptFromWorkspaceCheck = (command: Command): boolean => {
|
|
159
|
+
let current: Command | null | undefined = command;
|
|
160
|
+
while (current && current !== program) {
|
|
161
|
+
if (workspaceExemptCommands.has(current.name())) return true;
|
|
162
|
+
current = current.parent;
|
|
163
|
+
}
|
|
164
|
+
return false;
|
|
165
|
+
};
|
|
130
166
|
program.hook("preAction", (_thisCommand, actionCommand) => {
|
|
131
|
-
if (
|
|
167
|
+
if (isExemptFromWorkspaceCheck(actionCommand)) {
|
|
132
168
|
return;
|
|
133
169
|
}
|
|
134
170
|
const workspaceDir = getWorkspaceDir();
|
|
@@ -177,27 +177,31 @@ useEffect(() => {
|
|
|
177
177
|
}, []);
|
|
178
178
|
```
|
|
179
179
|
|
|
180
|
-
**File workflow:**
|
|
180
|
+
**File workflow:** Pass all source files inline via the `source_files` parameter of `app_create`. This writes and compiles the real app in a single call — no scaffold placeholder, no separate `file_write` or `app_refresh` needed for initial creation. For subsequent edits, use `file_edit`/`file_write` then call `app_refresh` once.
|
|
181
181
|
|
|
182
182
|
**Allowed third-party packages:** `date-fns`, `chart.js`, `lodash-es`, `zod`, `clsx`, `lucide`. Import them directly - esbuild resolves them at build time. No CDN imports. Note: `lucide` is the vanilla JS icon library (not `lucide-react`). Use its `createElement` or `createIcons` API, or manually inline SVG - do not import JSX icon components.
|
|
183
183
|
|
|
184
|
-
**Example - creating a multi-file project
|
|
184
|
+
**Example - creating a multi-file project:**
|
|
185
185
|
|
|
186
186
|
```
|
|
187
|
-
|
|
187
|
+
app_create({
|
|
188
|
+
name: "Project Tracker",
|
|
189
|
+
description: "Track projects with status and priority",
|
|
190
|
+
schema_json: '{"type":"object","properties":{"title":{"type":"string"},"status":{"type":"string"}},"required":["title"]}',
|
|
191
|
+
preview: { title: "Project Tracker", icon: "📋" },
|
|
192
|
+
source_files: {
|
|
193
|
+
"src/index.html": `<!DOCTYPE html>
|
|
188
194
|
<html lang="en">
|
|
189
195
|
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
190
196
|
<title>Project Tracker</title></head>
|
|
191
197
|
<body><div id="app"></div></body>
|
|
192
|
-
</html
|
|
193
|
-
|
|
194
|
-
file_write("{workspaceDir}/data/apps/project-tracker/src/main.tsx", `import { render } from 'preact';
|
|
198
|
+
</html>`,
|
|
199
|
+
"src/main.tsx": `import { render } from 'preact';
|
|
195
200
|
import { App } from './components/App';
|
|
196
201
|
import './styles.css';
|
|
197
202
|
|
|
198
|
-
render(<App />, document.getElementById('app')!)
|
|
199
|
-
|
|
200
|
-
file_write("{workspaceDir}/data/apps/project-tracker/src/components/App.tsx", `import { FunctionComponent } from 'preact';
|
|
203
|
+
render(<App />, document.getElementById('app')!);`,
|
|
204
|
+
"src/components/App.tsx": `import { FunctionComponent } from 'preact';
|
|
201
205
|
import { useState, useEffect } from 'preact/hooks';
|
|
202
206
|
import { Header } from './Header';
|
|
203
207
|
|
|
@@ -217,9 +221,8 @@ export const App: FunctionComponent = () => {
|
|
|
217
221
|
{/* ... */}
|
|
218
222
|
</div>
|
|
219
223
|
);
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
file_write("{workspaceDir}/data/apps/project-tracker/src/components/Header.tsx", `import { FunctionComponent } from 'preact';
|
|
224
|
+
};`,
|
|
225
|
+
"src/components/Header.tsx": `import { FunctionComponent } from 'preact';
|
|
223
226
|
|
|
224
227
|
interface HeaderProps {
|
|
225
228
|
title: string;
|
|
@@ -231,14 +234,12 @@ export const Header: FunctionComponent<HeaderProps> = ({ title, count }) => (
|
|
|
231
234
|
<h1>{title}</h1>
|
|
232
235
|
<span className="badge">{count} items</span>
|
|
233
236
|
</header>
|
|
234
|
-
)
|
|
235
|
-
|
|
236
|
-
file_write("{workspaceDir}/data/apps/project-tracker/src/styles.css", `.app { padding: var(--v-spacing-lg); }
|
|
237
|
+
);`,
|
|
238
|
+
"src/styles.css": `.app { padding: var(--v-spacing-lg); }
|
|
237
239
|
.header { display: flex; justify-content: space-between; align-items: center; }
|
|
238
|
-
.badge { background: var(--v-accent); color: var(--v-aux-white); padding: var(--v-spacing-xs) var(--v-spacing-sm); border-radius: var(--v-radius-pill); }`
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
app_refresh(app_id)
|
|
240
|
+
.badge { background: var(--v-accent); color: var(--v-aux-white); padding: var(--v-spacing-xs) var(--v-spacing-sm); border-radius: var(--v-radius-pill); }`
|
|
241
|
+
}
|
|
242
|
+
})
|
|
242
243
|
```
|
|
243
244
|
|
|
244
245
|
**Technical constraints (multi-file):**
|
|
@@ -329,10 +330,11 @@ Call `app_create` with:
|
|
|
329
330
|
- `name`: Short descriptive name
|
|
330
331
|
- `description`: One-sentence summary
|
|
331
332
|
- `schema_json`: JSON schema as string
|
|
332
|
-
- `
|
|
333
|
+
- `source_files`: Map of relative file paths to contents (e.g. `{"src/main.tsx": "...", "src/styles.css": "..."}`). **Always include this** with the complete app source — it writes, compiles, and opens the real app in a single call.
|
|
334
|
+
- `auto_open`: (optional, defaults to `true`) Shows an inline preview card in chat after the app is built. Only fires when real source files are provided (not for scaffold-only apps).
|
|
333
335
|
- `preview`: Always include - `title` (required), `subtitle`, `description`, `icon` (image URL preferred, emoji fallback), `metrics` (up to 3 key-value pills)
|
|
334
336
|
|
|
335
|
-
Do not pass `html` or `pages` to `app_create`; those single-file shortcuts are retired.
|
|
337
|
+
Do not pass `html` or `pages` to `app_create`; those single-file shortcuts are retired.
|
|
336
338
|
|
|
337
339
|
The app is NOT opened in a workspace panel automatically - users open it via the 'Open App' button on the inline card.
|
|
338
340
|
|
|
@@ -64,6 +64,13 @@
|
|
|
64
64
|
},
|
|
65
65
|
"required": ["title"]
|
|
66
66
|
},
|
|
67
|
+
"source_files": {
|
|
68
|
+
"type": "object",
|
|
69
|
+
"description": "Map of file paths to file contents to write under the app directory. Supply the real app source here instead of making separate file_write calls. Paths are relative to the app directory (e.g. 'src/main.tsx', 'src/components/App.tsx', 'src/styles.css'). When src/main.tsx is included, no scaffold placeholder is created and the app compiles immediately with real content.",
|
|
70
|
+
"additionalProperties": {
|
|
71
|
+
"type": "string"
|
|
72
|
+
}
|
|
73
|
+
},
|
|
67
74
|
"change_summary": {
|
|
68
75
|
"type": "string",
|
|
69
76
|
"description": "Short summary of what changed, using git conventional commit format (e.g. 'feat: add coin flip app', 'fix: correct button alignment'). Used as the version history entry."
|