@vellumai/assistant 0.8.1 → 0.8.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/ARCHITECTURE.md +13 -19
- package/Dockerfile +75 -1
- package/bun.lock +11 -1
- package/docker-entrypoint.sh +17 -0
- package/docker-init-apt-root.sh +167 -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 +642 -5
- 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-loop-exit-reason.test.ts +272 -0
- package/src/__tests__/agent-loop-provider-error-recording.test.ts +195 -0
- 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 +147 -0
- package/src/__tests__/config-get-vision-flag.test.ts +136 -0
- package/src/__tests__/config-loader-backfill.test.ts +115 -18
- 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 +31 -65
- 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 +59 -1
- 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-media-retry.test.ts +19 -8
- 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 +102 -13
- 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__/date-context.test.ts +45 -0
- 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 +151 -55
- package/src/__tests__/filing-service.test.ts +140 -0
- package/src/__tests__/get-skill-detail-audit.test.ts +0 -4
- package/src/__tests__/guardian-action-no-hardcoded-copy.test.ts +0 -1
- package/src/__tests__/guardian-dispatch.test.ts +1 -0
- package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +43 -62
- package/src/__tests__/heartbeat-service.test.ts +24 -164
- package/src/__tests__/helpers/channel-test-adapter.ts +0 -2
- 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 +507 -10
- package/src/__tests__/host-proxy-preactivation.test.ts +200 -13
- 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-background-turn.test.ts +153 -0
- package/src/__tests__/injector-chain.test.ts +15 -8
- package/src/__tests__/install-skill-routing.test.ts +155 -37
- package/src/__tests__/lifecycle-memory-v2-seed.test.ts +99 -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-callsite-catalog.test.ts +25 -0
- package/src/__tests__/llm-catalog-parity.test.ts +58 -13
- package/src/__tests__/llm-request-log-agent-loop-exit-reason.test.ts +116 -0
- package/src/__tests__/llm-request-log-error-payload.test.ts +138 -0
- package/src/__tests__/llm-request-log-source-clickhouse.test.ts +36 -0
- package/src/__tests__/llm-request-log-source-factory.test.ts +29 -53
- package/src/__tests__/llm-resolver.test.ts +255 -2
- package/src/__tests__/llm-usage-store.test.ts +114 -0
- package/src/__tests__/managed-profile-guard.test.ts +41 -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__/notification-decision-fallback.test.ts +0 -91
- package/src/__tests__/notification-decision-strategy.test.ts +14 -31
- package/src/__tests__/notification-deep-link.test.ts +15 -0
- package/src/__tests__/notification-guardian-path.test.ts +1 -2
- package/src/__tests__/notification-platform-adapter.test.ts +5 -4
- package/src/__tests__/notification-telegram-adapter.test.ts +1 -0
- package/src/__tests__/notification-vellum-adapter.test.ts +113 -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 +242 -3
- package/src/__tests__/openai-responses-cutover-guard.test.ts +17 -9
- package/src/__tests__/openrouter-provider-only.test.ts +51 -3
- package/src/__tests__/openrouter-token-estimation.test.ts +34 -25
- 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} +7 -2
- 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 +158 -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} +33 -31
- 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__/{secret-routes-managed-proxy.test.ts → secret-routes-platform-proxy.test.ts} +1 -1
- 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 +670 -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-087-memory-router-balanced-profile.test.ts +228 -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/a2a/__tests__/agent-card.test.ts +98 -0
- package/src/a2a/__tests__/e2e-a2a-channel.test.ts +597 -0
- package/src/a2a/__tests__/protocol-helpers.test.ts +113 -0
- package/src/a2a/__tests__/task-store.test.ts +246 -0
- package/src/a2a/agent-card.ts +58 -0
- package/src/a2a/feature-gate.ts +8 -0
- package/src/a2a/protocol-constants.ts +21 -0
- package/src/a2a/protocol-errors.ts +50 -0
- package/src/a2a/protocol-types.ts +162 -0
- package/src/a2a/task-store.ts +168 -0
- package/src/acp/resolve-agent.ts +1 -1
- package/src/agent/image-optimize.ts +13 -5
- package/src/agent/loop.ts +167 -18
- package/src/calls/voice-session-bridge.ts +61 -42
- package/src/channels/config.ts +9 -0
- package/src/channels/types.ts +122 -0
- package/src/cli/__tests__/unknown-command.test.ts +24 -0
- package/src/cli/commands/__tests__/changelog.test.ts +304 -319
- package/src/cli/{__tests__ → commands/__tests__}/notifications.test.ts +201 -28
- package/src/cli/commands/__tests__/schedules.test.ts +960 -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 +388 -346
- package/src/cli/commands/plugins.ts +252 -0
- package/src/cli/commands/schedules.ts +683 -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__/search-plugins.test.ts +261 -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 +303 -0
- package/src/cli/lib/list-installed-plugins.ts +137 -0
- package/src/cli/lib/search-plugins.ts +163 -0
- package/src/cli/lib/uninstall-plugin.ts +82 -0
- package/src/cli/lib/unknown-command.ts +111 -0
- package/src/cli/program.ts +52 -2
- package/src/config/assistant-feature-flags.ts +24 -54
- package/src/config/bundled-skills/app-builder/SKILL.md +140 -22
- 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/phone-calls/SKILL.md +1 -1
- 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/call-site-defaults.ts +105 -0
- package/src/config/feature-flag-registry.json +41 -9
- package/src/config/llm-resolver.ts +52 -1
- package/src/config/loader.ts +64 -38
- package/src/config/schema.ts +9 -10
- package/src/config/schemas/__tests__/llm-request-logs.test.ts +36 -0
- package/src/config/schemas/__tests__/memory-v2.test.ts +3 -3
- package/src/config/schemas/channels.ts +17 -0
- package/src/config/schemas/compaction.ts +28 -0
- package/src/config/schemas/conversations.ts +10 -0
- package/src/config/schemas/heartbeat.ts +23 -0
- package/src/config/schemas/llm-request-logs.ts +31 -7
- package/src/config/schemas/llm.ts +1 -0
- package/src/config/schemas/memory-retrieval.ts +18 -0
- package/src/config/schemas/memory-retrospective.ts +1 -1
- package/src/config/schemas/memory-v2.ts +4 -4
- package/src/config/schemas/memory.ts +3 -1
- package/src/config/schemas/tools.ts +14 -0
- package/src/config/seed-inference-profiles.ts +99 -29
- package/src/config/skills.ts +3 -96
- package/src/context/compactor.ts +1107 -0
- package/src/context/token-estimator.ts +34 -36
- 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 +33 -18
- 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-handlers.ts +78 -0
- package/src/daemon/conversation-agent-loop.ts +198 -11
- 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 +25 -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 -8
- package/src/daemon/date-context.ts +40 -0
- package/src/daemon/external-plugins-bootstrap.ts +217 -181
- package/src/daemon/first-greeting.ts +22 -2
- package/src/daemon/guardian-action-generators.ts +1 -125
- package/src/daemon/handlers/__tests__/config-a2a-complete.test.ts +248 -0
- package/src/daemon/handlers/__tests__/config-a2a-invite.test.ts +154 -0
- package/src/daemon/handlers/__tests__/config-a2a-redeem.test.ts +133 -0
- package/src/daemon/handlers/__tests__/config-a2a.test.ts +95 -0
- package/src/daemon/handlers/config-a2a.ts +289 -0
- package/src/daemon/handlers/config-model.ts +6 -5
- package/src/daemon/handlers/config-slack-channel.ts +15 -3
- package/src/daemon/handlers/conversations.ts +1 -0
- 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 +153 -27
- package/src/daemon/host-proxy-preactivation.ts +85 -18
- package/src/daemon/lifecycle.ts +89 -91
- package/src/daemon/meet-host-supervisor.ts +5 -4
- package/src/daemon/memory-v2-startup.ts +85 -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/notifications.ts +21 -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 +11 -54
- package/src/daemon/pkb-reminder-builder.ts +5 -20
- package/src/daemon/plugin-source-watcher.ts +146 -0
- package/src/daemon/process-message.ts +24 -3
- package/src/daemon/server.ts +11 -2
- package/src/daemon/skill-memory-refresh.ts +33 -0
- package/src/daemon/wake-target-adapter.ts +2 -0
- package/src/documents/document-store.ts +221 -3
- package/src/embedded/plugin-api.ts +40 -0
- package/src/export/__tests__/transcript-formatter.test.ts +121 -0
- package/src/export/transcript-formatter.ts +54 -20
- package/src/filing/filing-service.ts +39 -0
- package/src/heartbeat/__tests__/heartbeat-service.test.ts +135 -6
- package/src/heartbeat/heartbeat-run-store.ts +2 -1
- package/src/heartbeat/heartbeat-service.ts +73 -189
- package/src/home/__tests__/feed-types.test.ts +80 -0
- package/src/home/feed-types.ts +36 -2
- package/src/home/post-connect-feed.ts +1 -0
- package/src/index.ts +18 -1
- package/src/ipc/cli-client.ts +147 -45
- 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 +483 -0
- package/src/memory/__tests__/jobs-worker-v2-graph-trigger-embed.test.ts +113 -0
- package/src/memory/__tests__/memory-retrospective-enqueue.test.ts +2 -50
- package/src/memory/__tests__/memory-retrospective-job.test.ts +87 -4
- 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 +197 -11
- package/src/memory/conversation-title-service.ts +26 -4
- package/src/memory/db-init.ts +12 -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 +150 -12
- package/src/memory/graph/conversation-graph-memory.ts +49 -21
- package/src/memory/graph/tools.ts +9 -40
- package/src/memory/indexer.ts +34 -29
- package/src/memory/invite-store.ts +53 -0
- 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 +24 -12
- package/src/memory/llm-request-log-source.ts +19 -52
- package/src/memory/llm-request-log-store.ts +92 -1
- package/src/memory/llm-usage-store.ts +125 -5
- package/src/memory/memory-retrospective-enqueue.ts +1 -20
- package/src/memory/memory-retrospective-job.ts +33 -6
- 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/250-provider-connection-base-url-and-models.ts +28 -0
- package/src/memory/migrations/251-a2a-tasks.ts +49 -0
- package/src/memory/migrations/252-llm-request-log-agent-loop-exit-reason.ts +32 -0
- package/src/memory/migrations/index.ts +9 -0
- package/src/memory/migrations/registry.ts +16 -0
- package/src/memory/onboarding-events-store.ts +106 -0
- package/src/memory/schema/a2a.ts +15 -0
- package/src/memory/schema/bookmarks.ts +0 -2
- package/src/memory/schema/calls.ts +1 -0
- package/src/memory/schema/index.ts +1 -0
- package/src/memory/schema/inference.ts +3 -3
- package/src/memory/schema/infrastructure.ts +13 -0
- package/src/memory/turn-events-store.ts +127 -2
- package/src/memory/v2/__tests__/activation-store.test.ts +25 -23
- package/src/memory/v2/__tests__/activation.test.ts +0 -8
- package/src/memory/v2/__tests__/cli-command-store.test.ts +404 -0
- package/src/memory/v2/__tests__/frontmatter-sweep.test.ts +25 -4
- package/src/memory/v2/__tests__/injection.test.ts +288 -11
- 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/__tests__/static-context.test.ts +12 -1
- package/src/memory/v2/activation-store.ts +14 -16
- package/src/memory/v2/cli-command-content.ts +19 -0
- package/src/memory/v2/cli-command-store.ts +304 -0
- package/src/memory/v2/frontmatter-sweep.ts +7 -1
- package/src/memory/v2/injection.ts +81 -26
- package/src/memory/v2/migration.ts +49 -19
- package/src/memory/v2/page-index.ts +63 -8
- 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/memory/v2/static-context.ts +4 -4
- package/src/memory/v2/types.ts +23 -0
- package/src/messaging/providers/a2a/__tests__/deliver.test.ts +274 -0
- package/src/messaging/providers/a2a/deliver.ts +156 -0
- package/src/messaging/providers/gmail/client.ts +9 -2
- package/src/messaging/providers/index.ts +11 -2
- 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/__tests__/broadcaster.test.ts +203 -0
- package/src/notifications/__tests__/decision-engine.test.ts +283 -0
- package/src/notifications/__tests__/deterministic-checks.test.ts +286 -0
- package/src/notifications/__tests__/emit-signal-home-feed.test.ts +1 -0
- package/src/notifications/__tests__/home-feed-side-effect.test.ts +430 -7
- package/src/notifications/adapters/macos.ts +12 -2
- package/src/notifications/broadcaster.ts +29 -4
- package/src/notifications/conversation-pairing.ts +2 -1
- package/src/notifications/copy-composer.ts +17 -64
- package/src/notifications/decision-engine.ts +113 -45
- package/src/notifications/deterministic-checks.ts +96 -0
- package/src/notifications/emit-signal.ts +21 -1
- package/src/notifications/home-feed-side-effect.ts +138 -5
- package/src/notifications/signal.ts +3 -5
- package/src/notifications/types.ts +8 -0
- package/src/oauth/connection-resolver.ts +8 -4
- package/src/oauth/platform-connection.test.ts +43 -3
- package/src/oauth/platform-connection.ts +19 -6
- 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 +74 -22
- 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 +187 -42
- 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 +40 -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 +10 -43
- package/src/prompts/__tests__/task-progress-hint-section.test.ts +95 -0
- package/src/prompts/normalize-onboarding.ts +27 -0
- package/src/prompts/sections.ts +302 -0
- package/src/prompts/system-prompt.ts +63 -174
- package/src/prompts/templates/BOOTSTRAP.md +17 -1
- package/src/prompts/templates/system-sections.ts +164 -0
- package/src/providers/__tests__/inference.test.ts +24 -7
- package/src/providers/anthropic/client.ts +28 -28
- package/src/providers/call-site-routing.ts +24 -6
- package/src/providers/connection-resolution.ts +68 -11
- package/src/providers/inference/__tests__/adapter-factory-openai-compatible.test.ts +74 -0
- package/src/providers/inference/__tests__/connections-openai-compatible.test.ts +175 -0
- package/src/providers/inference/__tests__/connections-status-label.test.ts +15 -0
- package/src/providers/inference/adapter-factory.ts +32 -6
- package/src/providers/inference/auth.ts +12 -0
- package/src/providers/inference/backfill.ts +14 -1
- package/src/providers/inference/connections.ts +159 -34
- package/src/providers/inference/resolve-auth.ts +14 -4
- package/src/providers/model-catalog.ts +249 -12
- package/src/providers/model-intents.ts +3 -3
- package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +235 -0
- package/src/providers/openai/chat-completions-provider.ts +169 -8
- package/src/providers/openrouter/client.ts +49 -4
- package/src/providers/{managed-proxy → platform-proxy}/constants.ts +4 -2
- 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 +38 -0
- package/src/providers/provider-send-message.ts +27 -12
- package/src/providers/registry.ts +52 -15
- package/src/providers/retry.ts +47 -1
- package/src/runtime/__tests__/agent-wake.test.ts +152 -0
- package/src/runtime/agent-wake.ts +103 -15
- package/src/runtime/auth/route-policy.ts +21 -1
- package/src/runtime/btw-sidechain.ts +2 -0
- package/src/runtime/http-server.ts +7 -16
- package/src/runtime/http-types.ts +19 -47
- 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__/consolidation-routes.test.ts +258 -0
- package/src/runtime/routes/__tests__/conversation-management-routes.test.ts +5 -1
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +172 -23
- package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +275 -44
- package/src/runtime/routes/__tests__/llm-call-sites-routes.test.ts +12 -0
- 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 +126 -0
- package/src/runtime/routes/consolidation-routes.ts +100 -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 +99 -35
- package/src/runtime/routes/conversation-routes.ts +97 -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 +8 -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 +199 -22
- package/src/runtime/routes/integrations/a2a.ts +235 -0
- 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/llm-call-sites-routes.ts +11 -1
- 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 +98 -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 +17 -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/memory/register.ts +1 -9
- 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 +107 -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/087-memory-router-balanced-profile.ts +91 -0
- package/src/workspace/migrations/registry.ts +10 -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/__tests__/guardian-action-conversation-turn.test.ts +0 -441
- package/src/context/__tests__/compact-prompt.test.ts +0 -63
- package/src/context/prompts/compact.md +0 -26
- package/src/memory/graph/__tests__/remember-description.test.ts +0 -55
- package/src/prompts/__tests__/build-cli-reference-section.test.ts +0 -37
- package/src/runtime/guardian-action-conversation-turn.ts +0 -99
|
@@ -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
|
/**
|
|
@@ -57,6 +62,20 @@ import { log } from "./logger.js";
|
|
|
57
62
|
*/
|
|
58
63
|
export async function buildCliProgram(): Promise<Command> {
|
|
59
64
|
await initFeatureFlagOverrides({ retryBackoffsMs: [], callTimeoutMs: 200 });
|
|
65
|
+
return buildCliProgramTree();
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Synchronously build the CLI program tree without pre-populating the
|
|
70
|
+
* feature-flag cache. Use this from inside the daemon, where flags are
|
|
71
|
+
* already initialized — calling `buildCliProgram` from there would round-trip
|
|
72
|
+
* to the gateway unnecessarily.
|
|
73
|
+
*
|
|
74
|
+
* Same shape as `buildCliProgram` minus the async feature-flag init: registers
|
|
75
|
+
* the full subcommand set (conditionally gated on email / external-plugins
|
|
76
|
+
* flags via `getConfigReadOnly()`) and installs the workspace-preAction hook.
|
|
77
|
+
*/
|
|
78
|
+
export function buildCliProgramTree(): Command {
|
|
60
79
|
const program = new Command();
|
|
61
80
|
|
|
62
81
|
program
|
|
@@ -65,6 +84,13 @@ export async function buildCliProgram(): Promise<Command> {
|
|
|
65
84
|
.version(APP_VERSION)
|
|
66
85
|
.allowExcessArguments(true);
|
|
67
86
|
|
|
87
|
+
// Color Commander-emitted error output red (unknown options, missing args,
|
|
88
|
+
// cmd.error() calls). Plain success output and --help text are untouched.
|
|
89
|
+
// The `red` helper is a no-op when stderr isn't a TTY or NO_COLOR is set.
|
|
90
|
+
program.configureOutput({
|
|
91
|
+
outputError: (str, write) => write(red(str)),
|
|
92
|
+
});
|
|
93
|
+
|
|
68
94
|
program.addHelpText(
|
|
69
95
|
"after",
|
|
70
96
|
`
|
|
@@ -107,12 +133,17 @@ Examples:
|
|
|
107
133
|
registerOAuthCommand(program);
|
|
108
134
|
registerPendingCommand(program);
|
|
109
135
|
registerPlatformCommand(program);
|
|
136
|
+
if (isExternalPluginsEnabled(getConfigReadOnly())) {
|
|
137
|
+
registerPluginsCommand(program);
|
|
138
|
+
}
|
|
110
139
|
registerRoutesCommand(program);
|
|
140
|
+
registerSchedulesCommand(program);
|
|
111
141
|
registerSequenceCommand(program);
|
|
112
142
|
registerStatusCommand(program);
|
|
113
143
|
registerSkillsCommand(program);
|
|
114
144
|
registerSttCommand(program);
|
|
115
145
|
registerTaskCommand(program);
|
|
146
|
+
registerTelemetryCommand(program);
|
|
116
147
|
registerTrustCommand(program);
|
|
117
148
|
registerTtsCommand(program);
|
|
118
149
|
registerUiCommand(program);
|
|
@@ -126,9 +157,28 @@ Examples:
|
|
|
126
157
|
// remain available even without a workspace.
|
|
127
158
|
// Workspace-independent commands are exempt:
|
|
128
159
|
// completions — pure shell-script generation, no workspace files needed
|
|
129
|
-
|
|
160
|
+
// status — diagnostic; should run even when the workspace is broken
|
|
161
|
+
// changelog — pure read-only network surface backed by GitHub Releases;
|
|
162
|
+
// its on-disk cache is best-effort and tolerates a missing
|
|
163
|
+
// workspace dir (see changelog.ts:writeCache)
|
|
164
|
+
const workspaceExemptCommands = new Set([
|
|
165
|
+
"completions",
|
|
166
|
+
"status",
|
|
167
|
+
"changelog",
|
|
168
|
+
]);
|
|
169
|
+
// An action command's `.name()` returns the leaf (e.g. "show" for
|
|
170
|
+
// `changelog show <ver>`), so we walk up the parent chain to see whether
|
|
171
|
+
// any ancestor — typically the top-level subcommand — is exempt.
|
|
172
|
+
const isExemptFromWorkspaceCheck = (command: Command): boolean => {
|
|
173
|
+
let current: Command | null | undefined = command;
|
|
174
|
+
while (current && current !== program) {
|
|
175
|
+
if (workspaceExemptCommands.has(current.name())) return true;
|
|
176
|
+
current = current.parent;
|
|
177
|
+
}
|
|
178
|
+
return false;
|
|
179
|
+
};
|
|
130
180
|
program.hook("preAction", (_thisCommand, actionCommand) => {
|
|
131
|
-
if (
|
|
181
|
+
if (isExemptFromWorkspaceCheck(actionCommand)) {
|
|
132
182
|
return;
|
|
133
183
|
}
|
|
134
184
|
const workspaceDir = getWorkspaceDir();
|
|
@@ -110,20 +110,22 @@ function parseRegistryToDefaults(parsed: unknown): FeatureFlagDefaultsRegistry {
|
|
|
110
110
|
}
|
|
111
111
|
|
|
112
112
|
// ---------------------------------------------------------------------------
|
|
113
|
-
// Override loading — reads from gateway IPC socket
|
|
113
|
+
// Override loading — reads from gateway IPC socket
|
|
114
114
|
// ---------------------------------------------------------------------------
|
|
115
115
|
|
|
116
116
|
/**
|
|
117
|
-
* Module-level cache of feature flag override values. Populated
|
|
118
|
-
*
|
|
117
|
+
* Module-level cache of feature flag override values. Populated by
|
|
118
|
+
* `initFeatureFlagOverrides()` at startup, invalidated by
|
|
119
|
+
* `clearFeatureFlagOverridesCache()`.
|
|
119
120
|
*/
|
|
120
121
|
let cachedOverrides: Record<string, boolean> | null = null;
|
|
121
122
|
|
|
122
123
|
/**
|
|
123
|
-
* True when `cachedOverrides` was populated by the gateway IPC fetch
|
|
124
|
-
* preseeded by a test
|
|
125
|
-
*
|
|
126
|
-
*
|
|
124
|
+
* True when `cachedOverrides` was populated by the gateway IPC fetch or
|
|
125
|
+
* preseeded by a test via `_setOverridesForTesting()`. Guards
|
|
126
|
+
* `initFeatureFlagOverrides()` from clobbering an existing populated cache
|
|
127
|
+
* when called a second time (e.g. by a CLI entry point after the daemon
|
|
128
|
+
* has already initialized).
|
|
127
129
|
*/
|
|
128
130
|
let cachedOverridesFromGateway = false;
|
|
129
131
|
|
|
@@ -247,59 +249,30 @@ function loadOverrides(): Record<string, boolean> {
|
|
|
247
249
|
return cachedOverrides ?? {};
|
|
248
250
|
}
|
|
249
251
|
|
|
250
|
-
// ---------------------------------------------------------------------------
|
|
251
|
-
// Remote values — platform-pushed flags cached in a local JSON file
|
|
252
|
-
// ---------------------------------------------------------------------------
|
|
253
|
-
|
|
254
252
|
/**
|
|
255
|
-
*
|
|
256
|
-
*
|
|
257
|
-
*/
|
|
258
|
-
let cachedRemoteValues: Record<string, boolean> | null = null;
|
|
259
|
-
|
|
260
|
-
/**
|
|
261
|
-
* Load remote values with module-level caching.
|
|
253
|
+
* Invalidate the cached overrides so the next call to
|
|
254
|
+
* `isAssistantFeatureFlagEnabled` re-reads from the gateway.
|
|
262
255
|
*
|
|
263
|
-
*
|
|
264
|
-
* server-side), so this only returns the injected test cache. In production,
|
|
265
|
-
* remote values flow through the overrides cache.
|
|
266
|
-
*/
|
|
267
|
-
function loadRemoteValues(): Record<string, boolean> {
|
|
268
|
-
return cachedRemoteValues ?? {};
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
/**
|
|
272
|
-
* Invalidate the cached override and remote values so the next call to
|
|
273
|
-
* `isAssistantFeatureFlagEnabled` re-reads from the source.
|
|
274
|
-
*
|
|
275
|
-
* Called by the config watcher when the feature-flags file changes.
|
|
256
|
+
* Used by tests between cases to reset module state.
|
|
276
257
|
*/
|
|
277
258
|
export function clearFeatureFlagOverridesCache(): void {
|
|
278
259
|
cachedOverrides = null;
|
|
279
260
|
cachedOverridesFromGateway = false;
|
|
280
|
-
cachedRemoteValues = null;
|
|
281
261
|
}
|
|
282
262
|
|
|
283
263
|
/**
|
|
284
264
|
* Directly inject override values into the module-level cache.
|
|
285
265
|
*
|
|
286
|
-
* **Test-only** — bypasses
|
|
287
|
-
* flag state without
|
|
288
|
-
* use `clearFeatureFlagOverridesCache()` instead and let
|
|
289
|
-
* re-read from the
|
|
290
|
-
*
|
|
291
|
-
* Forces `cachedRemoteValues` to an empty record (not `null`) so the resolver
|
|
292
|
-
* does not fall through to reading `feature-flags-remote.json` from disk. This
|
|
293
|
-
* matters because a developer's local remote-cache file can leak platform-set
|
|
294
|
-
* values into the test environment (e.g. `email-channel: true`), defeating
|
|
295
|
-
* test isolation.
|
|
266
|
+
* **Test-only** — bypasses the gateway IPC fetch so unit tests can control
|
|
267
|
+
* flag state without standing up a real gateway. Production code should
|
|
268
|
+
* never call this; use `clearFeatureFlagOverridesCache()` instead and let
|
|
269
|
+
* the resolver re-read from the gateway.
|
|
296
270
|
*/
|
|
297
271
|
export function _setOverridesForTesting(
|
|
298
272
|
overrides: Record<string, boolean>,
|
|
299
273
|
): void {
|
|
300
274
|
cachedOverrides = { ...overrides };
|
|
301
275
|
cachedOverridesFromGateway = true;
|
|
302
|
-
cachedRemoteValues = {};
|
|
303
276
|
}
|
|
304
277
|
|
|
305
278
|
// ---------------------------------------------------------------------------
|
|
@@ -310,9 +283,11 @@ export function _setOverridesForTesting(
|
|
|
310
283
|
* Resolve whether an assistant feature flag is enabled.
|
|
311
284
|
*
|
|
312
285
|
* Resolution order:
|
|
313
|
-
* 1. Override from gateway IPC
|
|
314
|
-
*
|
|
315
|
-
*
|
|
286
|
+
* 1. Override from the gateway IPC fetch (includes platform-pushed remote
|
|
287
|
+
* values, which the gateway merges server-side: persisted > remote >
|
|
288
|
+
* registry)
|
|
289
|
+
* 2. Registry `defaultEnabled` (for declared assistant-scope keys)
|
|
290
|
+
* 3. `true` (for undeclared keys with no override)
|
|
316
291
|
*/
|
|
317
292
|
export function isAssistantFeatureFlagEnabled(
|
|
318
293
|
key: string,
|
|
@@ -322,18 +297,13 @@ export function isAssistantFeatureFlagEnabled(
|
|
|
322
297
|
const declared = defaults[key];
|
|
323
298
|
const overrides = loadOverrides();
|
|
324
299
|
|
|
325
|
-
// 1. Check overrides from gateway
|
|
300
|
+
// 1. Check overrides from the gateway IPC cache.
|
|
326
301
|
const explicit = overrides[key];
|
|
327
302
|
if (typeof explicit === "boolean") return explicit;
|
|
328
303
|
|
|
329
|
-
// 2.
|
|
330
|
-
const remote = loadRemoteValues();
|
|
331
|
-
const remoteValue = remote[key];
|
|
332
|
-
if (typeof remoteValue === "boolean") return remoteValue;
|
|
333
|
-
|
|
334
|
-
// 3. For declared keys, use the registry default
|
|
304
|
+
// 2. For declared keys, use the registry default.
|
|
335
305
|
if (declared) return declared.defaultEnabled;
|
|
336
306
|
|
|
337
|
-
//
|
|
307
|
+
// 3. Undeclared keys with no override default to enabled.
|
|
338
308
|
return true;
|
|
339
309
|
}
|
|
@@ -54,8 +54,114 @@ Each record is a JSON file at `<slug>/records/<uuid>.json` with shape:
|
|
|
54
54
|
|
|
55
55
|
All new apps use `formatVersion: 2`: source files live under `src/` and compiled output lives under `dist/`. The build system compiles TSX to JS automatically when `app_refresh` is called.
|
|
56
56
|
|
|
57
|
+
## Responsive Baseline & Mobile-First Mode
|
|
58
|
+
|
|
59
|
+
Every app must be responsive across the full width range — phone (~360px) to desktop (~1400px+). The conversation context's `<turn_context>` block carries an `interface:` field. Visual interfaces are `macos`, `ios`, and `web`; the field doesn't toggle responsiveness on or off — it shifts the **design priority**. Non-visual values like `phone` represent voice channels that can't render apps at all and don't need to be considered here.
|
|
60
|
+
|
|
61
|
+
- **`interface: ios`** (or any future mobile-web / android identifier) — mobile-first build. Design the narrow viewport first and progressively enhance upward at wider widths.
|
|
62
|
+
- **`interface: macos` / `web`** — desktop-first build. Design the larger composition first; the narrow-width fallback must still meet the universal baseline below but doesn't need to feel like a native mobile app.
|
|
63
|
+
- **Field absent or ambiguous** — default to desktop-first unless the user's request itself implies phone use ("for my iPhone home screen", "a tap-tracker I'll use on the go").
|
|
64
|
+
|
|
65
|
+
### Universal baseline (every build, regardless of interface)
|
|
66
|
+
|
|
67
|
+
These rules aren't mobile-specific — they're touch / responsive a11y baselines that any user-resizable WebView needs.
|
|
68
|
+
|
|
69
|
+
**Viewport & safe areas**
|
|
70
|
+
|
|
71
|
+
- Viewport meta: `<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">`. Never set `user-scalable=no` — it blocks accessibility zoom.
|
|
72
|
+
- Pad the root container with `env(safe-area-inset-*)` so content clears the notch / home indicator when the app is opened on a notched device: `padding-top: max(var(--v-spacing-lg), env(safe-area-inset-top))`, mirrored for `-bottom`/`-left`/`-right`. On desktop the env vars resolve to `0` and the `max()` falls through to the design-system value — no-op.
|
|
73
|
+
- Use `100dvh` (dynamic viewport height), not `100vh`, for full-height containers. `100vh` creates a scroll-jump on every mobile browser regardless of build mode.
|
|
74
|
+
|
|
75
|
+
**Form controls**
|
|
76
|
+
|
|
77
|
+
- `<input>`, `<textarea>`, `<select>` must be `font-size: 16px` or larger, or iOS Safari will zoom on focus and break the layout. This applies to every build — anyone may open a desktop-built app on their phone.
|
|
78
|
+
- Add `inputmode` to text fields with structured input: `numeric` for integers, `decimal` for amounts, `email`, `tel`, `url`. Add matching `autocomplete` and `autocapitalize` hints where appropriate.
|
|
79
|
+
|
|
80
|
+
**Touch & hover**
|
|
81
|
+
|
|
82
|
+
- Interactive elements (buttons, list rows, nav items, toggles, icon buttons) must be ≥44×44pt. `.v-button` already meets this; for custom controls, set `min-height: 44px` explicitly.
|
|
83
|
+
- Gate hover affordances behind `@media (hover: hover)` so they don't stick on touch devices visiting a desktop-built app.
|
|
84
|
+
- Disable text selection on app chrome (headers, nav, buttons) with `user-select: none; -webkit-user-select: none` so long-press doesn't pop the iOS selection menu over interactive elements.
|
|
85
|
+
|
|
86
|
+
**Layout fluidity**
|
|
87
|
+
|
|
88
|
+
- Fluid widths only — no fixed-pixel layouts. Use `%`, `fr`, `minmax`, `clamp()` instead of `px` on container widths.
|
|
89
|
+
- Horizontal-scroll tables don't work on narrow screens. At narrow widths, collapse rows into stacked cards with labels and values arranged vertically. (Mobile-first builds can use cards everywhere; desktop-first builds can keep the table at wide widths and switch to cards below a breakpoint.)
|
|
90
|
+
- `vellum.widgets.*` chart containers should be sized in `vw`/`%`, not fixed `px`. Prefer simpler chart types (sparkline, bar) at narrow widths — dense multi-series charts lose detail.
|
|
91
|
+
|
|
92
|
+
### Mobile-first priorities (`interface: ios` or future mobile identifier)
|
|
93
|
+
|
|
94
|
+
These are the **design priority differences** that mobile-first builds adopt on top of the universal baseline. They reflect "narrow viewport is the primary experience, wider widths progressively enhance."
|
|
95
|
+
|
|
96
|
+
**Typography**
|
|
97
|
+
|
|
98
|
+
- Default body text to `--v-font-size-lg` (17px), not `--v-font-size-base` (14px) — the desktop base is too small to read comfortably on a phone. At wider widths the same 17px reads fine.
|
|
99
|
+
|
|
100
|
+
**Spacing**
|
|
101
|
+
|
|
102
|
+
- Bump default vertical rhythm one step (e.g. `--v-spacing-md` → `--v-spacing-lg` between cards and sections) so users can comfortably scroll-stop on each item.
|
|
103
|
+
|
|
104
|
+
**Layout**
|
|
105
|
+
|
|
106
|
+
- One column as the **default**, not as a narrow-width fallback. `flex-direction: column` first; opt into a multi-column grid only above a width breakpoint (`@media (min-width: 720px)`). No side rails, no two-pane master/detail, no fixed-width sidebars in the default view.
|
|
107
|
+
- Bottom-anchor the primary action (e.g. "Add", "Save") so the thumb can reach it: `position: sticky; bottom: env(safe-area-inset-bottom)` over the scrolling list. On wider widths you may re-flow it back inline.
|
|
108
|
+
- Replace side modals and popovers with bottom sheets that animate up from the bottom edge.
|
|
109
|
+
|
|
110
|
+
**Interaction**
|
|
111
|
+
|
|
112
|
+
- Skip the Tab/Enter/Esc keyboard pattern from "Interaction Standards" as the primary affordance — on mobile, focus comes from taps, submit from the soft keyboard's `return`, dismissal from a swipe down on bottom sheets. Keyboard support is still allowed (external-keyboard users exist on iPad) but isn't the design driver.
|
|
113
|
+
|
|
114
|
+
### Desktop-first priorities (`interface: macos` / `web`)
|
|
115
|
+
|
|
116
|
+
The default behaviour the rest of this skill describes — multi-column composition, hover-rich affordances, denser information, side modals, inline primary actions. The universal baseline above is the floor: the narrow-width view must still work and follow the touch / responsive a11y rules, but it doesn't need to feel native to mobile.
|
|
117
|
+
|
|
118
|
+
Everything else in this skill applies unchanged.
|
|
119
|
+
|
|
57
120
|
## Workflow
|
|
58
121
|
|
|
122
|
+
### 0. Preflight — Pin to a high-quality model
|
|
123
|
+
|
|
124
|
+
App building is design-heavy judgment work — color palettes, layout decisions, component architecture, micro-interactions. A stronger model produces meaningfully better apps: more creative visual directions, cleaner component boundaries, fewer generic patterns. Before building, check whether the conversation is already pinned to the quality profile:
|
|
125
|
+
|
|
126
|
+
```
|
|
127
|
+
assistant inference session list
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
If no session is active, check the current default profile:
|
|
131
|
+
|
|
132
|
+
```
|
|
133
|
+
assistant config get llm.default.profile
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
If the profile is already `quality-optimized`, skip the rest of this step and proceed to Step 1.
|
|
137
|
+
|
|
138
|
+
**If the active profile is `balanced`, `cost-optimized`, or any non-quality profile, you MUST ask the user for permission before switching. Do NOT open an inference session without explicit user confirmation.** Use `assistant ui confirm`:
|
|
139
|
+
|
|
140
|
+
```
|
|
141
|
+
assistant ui confirm --message "App building works best with a high-quality model — it makes better design decisions, writes cleaner components, and produces more visually polished results. Switch to the quality profile for this build? (You can switch back after.)"
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
If `assistant ui confirm` isn't available on this binary, ask the user directly in conversation instead. **Either way, wait for the user's answer before proceeding.**
|
|
145
|
+
|
|
146
|
+
**Only if the user confirms**, open an inference session:
|
|
147
|
+
|
|
148
|
+
```
|
|
149
|
+
assistant inference session open quality-optimized --ttl 1h
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
If `quality-optimized` isn't a profile name on this workspace, list the available profiles and open against the highest-quality one:
|
|
153
|
+
|
|
154
|
+
```
|
|
155
|
+
assistant config get llm.profiles
|
|
156
|
+
assistant inference session open <profile-name> --ttl 1h
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
The `--ttl 1h` gives comfortable headroom for a typical app build without leaving a forever-pinned session if the close in Step 6 is skipped.
|
|
160
|
+
|
|
161
|
+
**If the user declines, do not switch profiles.** Proceed with the current profile — the build still works, the model just won't be pinned. Skip the close in Step 6 too.
|
|
162
|
+
|
|
163
|
+
If `assistant inference session` isn't available on this binary, proceed without it.
|
|
164
|
+
|
|
59
165
|
### 1. Gather Requirements
|
|
60
166
|
|
|
61
167
|
**Default: just build.** When a user says "build me a habit tracker," don't ask what colors they want or how many fields to include. Immediately:
|
|
@@ -177,27 +283,31 @@ useEffect(() => {
|
|
|
177
283
|
}, []);
|
|
178
284
|
```
|
|
179
285
|
|
|
180
|
-
**File workflow:**
|
|
286
|
+
**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
287
|
|
|
182
288
|
**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
289
|
|
|
184
|
-
**Example - creating a multi-file project
|
|
290
|
+
**Example - creating a multi-file project:**
|
|
185
291
|
|
|
186
292
|
```
|
|
187
|
-
|
|
293
|
+
app_create({
|
|
294
|
+
name: "Project Tracker",
|
|
295
|
+
description: "Track projects with status and priority",
|
|
296
|
+
schema_json: '{"type":"object","properties":{"title":{"type":"string"},"status":{"type":"string"}},"required":["title"]}',
|
|
297
|
+
preview: { title: "Project Tracker", icon: "📋" },
|
|
298
|
+
source_files: {
|
|
299
|
+
"src/index.html": `<!DOCTYPE html>
|
|
188
300
|
<html lang="en">
|
|
189
301
|
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
190
302
|
<title>Project Tracker</title></head>
|
|
191
303
|
<body><div id="app"></div></body>
|
|
192
|
-
</html
|
|
193
|
-
|
|
194
|
-
file_write("{workspaceDir}/data/apps/project-tracker/src/main.tsx", `import { render } from 'preact';
|
|
304
|
+
</html>`,
|
|
305
|
+
"src/main.tsx": `import { render } from 'preact';
|
|
195
306
|
import { App } from './components/App';
|
|
196
307
|
import './styles.css';
|
|
197
308
|
|
|
198
|
-
render(<App />, document.getElementById('app')!)
|
|
199
|
-
|
|
200
|
-
file_write("{workspaceDir}/data/apps/project-tracker/src/components/App.tsx", `import { FunctionComponent } from 'preact';
|
|
309
|
+
render(<App />, document.getElementById('app')!);`,
|
|
310
|
+
"src/components/App.tsx": `import { FunctionComponent } from 'preact';
|
|
201
311
|
import { useState, useEffect } from 'preact/hooks';
|
|
202
312
|
import { Header } from './Header';
|
|
203
313
|
|
|
@@ -217,9 +327,8 @@ export const App: FunctionComponent = () => {
|
|
|
217
327
|
{/* ... */}
|
|
218
328
|
</div>
|
|
219
329
|
);
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
file_write("{workspaceDir}/data/apps/project-tracker/src/components/Header.tsx", `import { FunctionComponent } from 'preact';
|
|
330
|
+
};`,
|
|
331
|
+
"src/components/Header.tsx": `import { FunctionComponent } from 'preact';
|
|
223
332
|
|
|
224
333
|
interface HeaderProps {
|
|
225
334
|
title: string;
|
|
@@ -231,14 +340,12 @@ export const Header: FunctionComponent<HeaderProps> = ({ title, count }) => (
|
|
|
231
340
|
<h1>{title}</h1>
|
|
232
341
|
<span className="badge">{count} items</span>
|
|
233
342
|
</header>
|
|
234
|
-
)
|
|
235
|
-
|
|
236
|
-
file_write("{workspaceDir}/data/apps/project-tracker/src/styles.css", `.app { padding: var(--v-spacing-lg); }
|
|
343
|
+
);`,
|
|
344
|
+
"src/styles.css": `.app { padding: var(--v-spacing-lg); }
|
|
237
345
|
.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)
|
|
346
|
+
.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); }`
|
|
347
|
+
}
|
|
348
|
+
})
|
|
242
349
|
```
|
|
243
350
|
|
|
244
351
|
**Technical constraints (multi-file):**
|
|
@@ -329,10 +436,11 @@ Call `app_create` with:
|
|
|
329
436
|
- `name`: Short descriptive name
|
|
330
437
|
- `description`: One-sentence summary
|
|
331
438
|
- `schema_json`: JSON schema as string
|
|
332
|
-
- `
|
|
439
|
+
- `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.
|
|
440
|
+
- `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
441
|
- `preview`: Always include - `title` (required), `subtitle`, `description`, `icon` (image URL preferred, emoji fallback), `metrics` (up to 3 key-value pills)
|
|
334
442
|
|
|
335
|
-
Do not pass `html` or `pages` to `app_create`; those single-file shortcuts are retired.
|
|
443
|
+
Do not pass `html` or `pages` to `app_create`; those single-file shortcuts are retired.
|
|
336
444
|
|
|
337
445
|
The app is NOT opened in a workspace panel automatically - users open it via the 'Open App' button on the inline card.
|
|
338
446
|
|
|
@@ -349,6 +457,16 @@ After making all file changes, call `app_refresh(app_id)` once to compile and re
|
|
|
349
457
|
|
|
350
458
|
Apps should have multiple source files under `src/` (`styles.css`, components, helpers, etc.). Import CSS and modules from TSX so esbuild includes them in the compiled output.
|
|
351
459
|
|
|
460
|
+
### 6. Close the inference session
|
|
461
|
+
|
|
462
|
+
If you opened an inference session in Step 0, close it now:
|
|
463
|
+
|
|
464
|
+
```
|
|
465
|
+
assistant inference session close
|
|
466
|
+
```
|
|
467
|
+
|
|
468
|
+
If you skipped the open in Step 0 (because the user declined, the CLI didn't have the command, or the profile was already quality), skip this step too.
|
|
469
|
+
|
|
352
470
|
## Interaction Standards
|
|
353
471
|
|
|
354
472
|
Every app must meet these baselines:
|
|
@@ -357,7 +475,7 @@ Every app must meet these baselines:
|
|
|
357
475
|
- **Confirmation for destructive actions:** Use `window.vellum.confirm(title, message)` before deleting or resetting. Returns `Promise<boolean>`.
|
|
358
476
|
- **Form validation:** Validate before submit, show errors inline, disable submit during async operations.
|
|
359
477
|
- **Loading states:** Never show a blank screen while data loads. Use skeleton shimmer or spinners.
|
|
360
|
-
- **Keyboard navigation:** `Tab` between elements, `Enter` to submit, `Escape` to close/cancel.
|
|
478
|
+
- **Keyboard navigation:** `Tab` between elements, `Enter` to submit, `Escape` to close/cancel. *(De-prioritised on mobile-first builds — see [Responsive Baseline & Mobile-First Mode](#responsive-baseline--mobile-first-mode).)*
|
|
361
479
|
|
|
362
480
|
## Presentation Slide Design
|
|
363
481
|
|