@vellumai/assistant 0.8.7 → 0.8.8-dev.202606052332.17fc8ea
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/Dockerfile +20 -4
- package/bun.lock +2 -2
- package/docker-entrypoint.sh +4 -2
- package/docker-init-apt-root.sh +3 -1
- package/docker-kata-apt-env.sh +3 -1
- package/docker-kata-runtime-family.sh +12 -0
- package/docs/architecture/memory.md +1 -1
- package/examples/plugins/echo/README.md +61 -66
- package/examples/plugins/echo/hooks/post-tool-use.ts +18 -0
- package/examples/plugins/echo/hooks/stop.ts +16 -0
- package/examples/plugins/echo/hooks/user-prompt-submit.ts +18 -0
- package/examples/plugins/echo/package.json +1 -2
- package/examples/plugins/echo/src/emit.ts +19 -0
- package/node_modules/@vellumai/skill-host-contracts/src/server-message.ts +3 -3
- package/node_modules/@vellumai/skill-host-contracts/src/skill-host.ts +7 -6
- package/openapi.yaml +3378 -335
- package/package.json +2 -2
- package/scripts/generate-openapi.ts +68 -41
- package/src/__tests__/agent-loop-exit-reason.test.ts +35 -93
- package/src/__tests__/agent-loop-provider-error-recording.test.ts +1 -1
- package/src/__tests__/agent-loop.test.ts +37 -87
- package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +2 -0
- package/src/__tests__/annotate-activity-metadata.test.ts +262 -0
- package/src/__tests__/annotate-risk-options.test.ts +2 -3
- package/src/__tests__/anthropic-provider.test.ts +95 -2
- package/src/__tests__/app-control-flow.test.ts +1 -1
- package/src/__tests__/app-dir-path-guard.test.ts +1 -0
- package/src/__tests__/approval-routes-http.test.ts +4 -1
- package/src/__tests__/assistant-event-hub.test.ts +25 -0
- package/src/__tests__/assistant-events-sse-shed.test.ts +8 -0
- package/src/__tests__/{conversation-stream-state.test.ts → assistant-stream-state.test.ts} +252 -91
- package/src/__tests__/auth-fallback-events-store.test.ts +116 -0
- package/src/__tests__/background-workers-disk-pressure.test.ts +6 -0
- package/src/__tests__/btw-routes.test.ts +62 -3
- package/src/__tests__/build-persisted-content.test.ts +184 -0
- package/src/__tests__/catalog-files.test.ts +1 -1
- package/src/__tests__/channel-approval-routes.test.ts +1 -1
- package/src/__tests__/channel-approvals.test.ts +1 -1
- package/src/__tests__/clawhub-files.test.ts +1 -1
- package/src/__tests__/compaction-circuit.test.ts +258 -0
- package/src/__tests__/compaction-direct.test.ts +132 -0
- package/src/__tests__/compaction.benchmark.test.ts +0 -30
- package/src/__tests__/config-watcher.test.ts +1 -1
- package/src/__tests__/conversation-abort-tool-results.test.ts +57 -19
- package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +6 -5
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +10 -7
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +316 -1143
- package/src/__tests__/conversation-agent-loop.test.ts +638 -1655
- package/src/__tests__/conversation-analysis-routes.test.ts +6 -0
- package/src/__tests__/conversation-clean-command.test.ts +5 -2
- package/src/__tests__/conversation-history-web-search.test.ts +11 -1
- package/src/__tests__/conversation-pairing.test.ts +4 -31
- package/src/__tests__/conversation-process-app-control-preactivation.test.ts +6 -0
- package/src/__tests__/conversation-provider-retry-repair.test.ts +30 -10
- package/src/__tests__/conversation-queue.test.ts +2 -0
- package/src/__tests__/conversation-routes-disk-view.test.ts +3 -0
- package/src/__tests__/conversation-routes-slash-commands.test.ts +6 -5
- package/src/__tests__/conversation-runtime-assembly.test.ts +310 -300
- package/src/__tests__/conversation-runtime-workspace.test.ts +105 -45
- package/src/__tests__/conversation-slash-commands.test.ts +8 -42
- package/src/__tests__/conversation-slash-queue.test.ts +6 -1
- package/src/__tests__/conversation-starter-routes.test.ts +14 -6
- package/src/__tests__/conversation-surfaces-action-delivery.test.ts +84 -0
- package/src/__tests__/conversation-sync-tags.test.ts +27 -15
- package/src/__tests__/conversation-title-service.test.ts +135 -2
- package/src/__tests__/conversation-workspace-cache-state.test.ts +17 -16
- package/src/__tests__/conversation-workspace-injection.test.ts +67 -2
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +7 -6
- package/src/__tests__/conversations-import-system-filter.test.ts +101 -0
- package/src/__tests__/cross-provider-web-search.test.ts +214 -1
- package/src/__tests__/db-acp-history.test.ts +101 -0
- package/src/__tests__/db-schedule-syntax-migration.test.ts +5 -0
- package/src/__tests__/dm-persistence.test.ts +5 -1
- package/src/__tests__/dynamic-page-surface.test.ts +31 -0
- package/src/__tests__/empty-response-hook.test.ts +304 -0
- package/src/__tests__/feature-flag-test-helpers.ts +2 -2
- package/src/__tests__/file-write-tool.test.ts +63 -0
- package/src/__tests__/gateway-only-guard.test.ts +12 -2
- package/src/__tests__/gemini-image-service.test.ts +13 -0
- package/src/__tests__/guardian-grant-minting.test.ts +1 -1
- package/src/__tests__/guardian-routing-invariants.test.ts +2 -4
- package/src/__tests__/handlers-user-message-approval-consumption.test.ts +1 -1
- package/src/__tests__/heartbeat-disk-pressure.test.ts +1 -0
- package/src/__tests__/heartbeat-service.test.ts +1 -0
- package/src/__tests__/helpers/mock-provider.ts +110 -0
- package/src/__tests__/helpers/native-web-search-harness.ts +129 -0
- package/src/__tests__/history-repair-hook.test.ts +1 -0
- package/src/__tests__/host-app-control-routes.test.ts +1 -1
- package/src/__tests__/host-cu-routes-targeted.test.ts +3 -3
- package/src/__tests__/identity-intro-cache.test.ts +12 -100
- package/src/__tests__/identity-routes.test.ts +248 -7
- package/src/__tests__/inbound-slack-persistence.test.ts +5 -1
- package/src/__tests__/injector-background-turn.test.ts +3 -9
- package/src/__tests__/injector-chain.test.ts +139 -275
- package/src/__tests__/injector-disk-pressure.test.ts +75 -41
- package/src/__tests__/injector-document-comments.test.ts +3 -3
- package/src/__tests__/injector-pkb-v2-silenced.test.ts +30 -22
- package/src/__tests__/injector-v3-suppression.test.ts +31 -37
- package/src/__tests__/internal-telemetry-routes.test.ts +109 -0
- package/src/__tests__/list-messages-hidden-metadata.test.ts +38 -0
- package/src/__tests__/list-messages-page-latest.test.ts +60 -0
- package/src/__tests__/list-messages-tool-merge.test.ts +20 -0
- package/src/__tests__/llm-usage-store.test.ts +223 -1
- package/src/__tests__/memory-retrieval-hook.test.ts +297 -0
- package/src/__tests__/memory-v2-static-injector.test.ts +103 -35
- package/src/__tests__/native-web-search.test.ts +191 -0
- package/src/__tests__/onboarding-template-contract.test.ts +2 -0
- package/src/__tests__/openai-image-service.test.ts +17 -0
- package/src/__tests__/openai-provider.test.ts +31 -1
- package/src/__tests__/{overflow-reduce-pipeline.test.ts → overflow-reduction-loop.test.ts} +64 -284
- package/src/__tests__/persist-unsendable-image.test.ts +215 -0
- package/src/__tests__/persistence-secret-redaction.test.ts +1 -0
- package/src/__tests__/pkb-autoinject.test.ts +2 -5
- package/src/__tests__/plugin-api-shim.test.ts +3 -6
- package/src/__tests__/plugin-bootstrap.test.ts +14 -40
- package/src/__tests__/plugin-registry.test.ts +3 -76
- package/src/__tests__/plugin-types.test.ts +0 -193
- package/src/__tests__/process-message-display-content.test.ts +6 -2
- package/src/__tests__/reaction-persistence.test.ts +1 -1
- package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +5 -1
- package/src/__tests__/resolve-trust-class.test.ts +4 -4
- package/src/__tests__/runtime-events-sse-reconnect.test.ts +60 -23
- package/src/__tests__/schedule-routes.test.ts +603 -2
- package/src/__tests__/schedule-store.test.ts +41 -0
- package/src/__tests__/schedule-tools.test.ts +35 -0
- package/src/__tests__/send-endpoint-busy.test.ts +4 -1
- package/src/__tests__/server-history-render.test.ts +314 -1
- package/src/__tests__/skill-feature-flags-integration.test.ts +33 -0
- package/src/__tests__/skillssh-files.test.ts +1 -1
- package/src/__tests__/subagent-call-site-routing.test.ts +1 -1
- package/src/__tests__/subagent-fork-notifications.test.ts +1 -3
- package/src/__tests__/subagent-fork-spawn.test.ts +1 -1
- package/src/__tests__/subagent-manager-notify.test.ts +1 -3
- package/src/__tests__/subagent-notify-parent.test.ts +1 -3
- package/src/__tests__/subagent-spawn-tool-fork.test.ts +1 -1
- package/src/__tests__/system-prompt.test.ts +20 -0
- package/src/__tests__/task-scheduler.test.ts +162 -1
- package/src/__tests__/terminal-tools.test.ts +6 -1
- package/src/__tests__/title-generate-hook.test.ts +319 -0
- package/src/__tests__/tool-error-hook.test.ts +278 -0
- package/src/__tests__/tool-preview-lifecycle.test.ts +468 -5
- package/src/__tests__/tool-result-metadata-plumbing.test.ts +1 -0
- package/src/__tests__/tool-result-truncate-hook.test.ts +127 -0
- package/src/__tests__/tool-result-truncation.test.ts +0 -2
- package/src/__tests__/ui-choice-copy-surfaces.test.ts +254 -0
- package/src/__tests__/ui-work-result-surface.test.ts +159 -0
- package/src/__tests__/usage-routes.test.ts +285 -1
- package/src/__tests__/user-plugin-loader.test.ts +54 -286
- package/src/__tests__/voice-session-bridge.test.ts +6 -3
- package/src/__tests__/web-search-backend-failure.test.ts +166 -0
- package/src/acp/__tests__/agent-process.test.ts +161 -0
- package/src/acp/__tests__/client-handler.test.ts +40 -0
- package/src/acp/__tests__/helpers/acp-history-db.ts +82 -0
- package/src/acp/__tests__/helpers/exec-file-stub.ts +101 -0
- package/src/acp/__tests__/prepare-agent-env.test.ts +137 -0
- package/src/acp/__tests__/session-manager-persistence.test.ts +95 -28
- package/src/acp/__tests__/session-manager-resume.test.ts +736 -0
- package/src/acp/agent-process.ts +61 -1
- package/src/acp/auto-install.test.ts +196 -0
- package/src/acp/auto-install.ts +177 -0
- package/src/acp/client-handler.ts +31 -0
- package/src/acp/feature-gate.test.ts +48 -0
- package/src/acp/feature-gate.ts +34 -0
- package/src/acp/prepare-agent-env.ts +83 -29
- package/src/acp/resolve-agent.test.ts +320 -7
- package/src/acp/resolve-agent.ts +182 -18
- package/src/acp/resume-hint.ts +25 -0
- package/src/acp/session-manager.ts +495 -73
- package/src/acp/types.ts +8 -0
- package/src/agent/compaction-circuit.ts +60 -102
- package/src/agent/loop.ts +362 -485
- package/src/api/events/assistant-thinking-delta.ts +33 -0
- package/src/api/events/tool-output-chunk.ts +45 -0
- package/src/api/events/tool-use-preview-start.ts +32 -0
- package/src/api/events/trace-event.ts +69 -0
- package/src/api/index.ts +48 -13
- package/src/api/responses/conversation-message.ts +374 -0
- package/src/approvals/guardian-request-resolvers.ts +1 -1
- package/src/avatar/__tests__/avatar-store.test.ts +34 -29
- package/src/background-wake/next-wake.ts +1 -0
- package/src/cli/commands/__tests__/notifications.test.ts +58 -14
- package/src/cli/commands/notifications.ts +112 -60
- package/src/config/__tests__/feature-flag-registry-guard.test.ts +2 -2
- package/src/config/acp-defaults.test.ts +10 -0
- package/src/config/acp-defaults.ts +6 -0
- package/src/config/assistant-feature-flags.ts +22 -11
- package/src/config/bundled-skills/acp/SKILL.md +83 -31
- package/src/config/bundled-skills/acp/TOOLS.json +4 -4
- package/src/config/bundled-skills/app-builder/SKILL.md +224 -398
- package/src/config/bundled-skills/app-builder/TOOLS.json +29 -0
- package/src/config/bundled-skills/app-builder/references/DESIGN_SYSTEM.md +48 -0
- package/src/config/bundled-skills/app-builder/references/RESPONSIVE.md +57 -0
- package/src/config/bundled-skills/app-builder/references/SLIDES.md +38 -0
- package/src/config/bundled-skills/app-builder/references/examples/README.md +17 -0
- package/src/config/bundled-skills/app-builder/references/examples/expense-tracker.md +515 -0
- package/src/config/bundled-skills/app-builder/references/examples/focus-timer.md +342 -0
- package/src/config/bundled-skills/app-builder/references/examples/habit-tracker.md +490 -0
- package/src/config/bundled-skills/app-builder/tools/app-list.ts +62 -0
- package/src/config/bundled-skills/document-editor/SKILL.md +28 -23
- package/src/config/bundled-skills/document-editor/TOOLS.json +1 -1
- package/src/config/bundled-skills/messaging/SKILL.md +0 -7
- package/src/config/bundled-tool-registry.ts +2 -0
- package/src/config/feature-flag-cache.ts +3 -3
- package/src/config/feature-flag-registry.json +48 -7
- package/src/config/schemas/__tests__/memory-v2.test.ts +1 -0
- package/src/config/schemas/__tests__/memory-v3.test.ts +25 -0
- package/src/config/schemas/heartbeat.ts +9 -0
- package/src/config/schemas/llm.ts +1 -0
- package/src/config/schemas/memory-v2.ts +8 -0
- package/src/config/schemas/memory-v3.ts +8 -0
- package/src/config/schemas/platform.ts +8 -0
- package/src/config/seed-inference-profiles.ts +2 -2
- package/src/config/skills.ts +13 -0
- package/src/context/compactor.ts +1 -1
- package/src/context/strip-injections.ts +128 -0
- package/src/context/token-estimator.ts +23 -0
- package/src/context/tool-result-truncation.ts +0 -23
- package/src/context/window-manager.ts +5 -7
- package/src/credential-execution/executable-discovery.ts +16 -0
- package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +6 -0
- package/src/daemon/__tests__/inference-profile-notification.test.ts +153 -0
- package/src/daemon/__tests__/native-web-search-metadata.test.ts +10 -8
- package/src/daemon/assistant-attachments.ts +1 -1
- package/src/daemon/config-watcher.ts +2 -2
- package/src/daemon/context-overflow-reducer.ts +0 -1
- package/src/daemon/conversation-agent-loop-handlers.ts +594 -153
- package/src/daemon/conversation-agent-loop.ts +301 -997
- package/src/daemon/conversation-history.ts +5 -4
- package/src/daemon/conversation-lifecycle.ts +3 -4
- package/src/daemon/conversation-messaging.ts +7 -6
- package/src/daemon/conversation-process.ts +11 -16
- package/src/daemon/conversation-registry.ts +159 -0
- package/src/daemon/conversation-runtime-assembly.ts +218 -398
- package/src/daemon/conversation-slash.ts +6 -25
- package/src/daemon/conversation-store.ts +9 -90
- package/src/daemon/conversation-surfaces.ts +222 -4
- package/src/daemon/conversation-tool-setup.ts +2 -29
- package/src/daemon/conversation-workspace.ts +17 -0
- package/src/daemon/conversation.ts +32 -20
- package/src/daemon/external-plugins-bootstrap.ts +17 -18
- package/src/daemon/handlers/config-a2a.ts +51 -36
- package/src/daemon/handlers/config-slack-channel.ts +20 -14
- package/src/daemon/handlers/config-telegram.ts +16 -2
- package/src/daemon/handlers/conversations.ts +3 -1
- package/src/daemon/handlers/shared.ts +156 -84
- package/src/daemon/handlers/skills.ts +42 -10
- package/src/daemon/lifecycle.ts +25 -0
- package/src/daemon/message-types/apps.ts +1 -29
- package/src/daemon/message-types/messages.ts +9 -57
- package/src/daemon/message-types/skills.ts +2 -0
- package/src/daemon/message-types/surfaces.ts +136 -3
- package/src/daemon/now-scratchpad.ts +21 -0
- package/src/daemon/orphan-reaper.test.ts +210 -0
- package/src/daemon/orphan-reaper.ts +240 -0
- package/src/daemon/overflow-reduction-loop.ts +230 -0
- package/src/daemon/persist-unsendable-image.ts +117 -0
- package/src/daemon/process-message.ts +1 -3
- package/src/daemon/server.ts +2 -0
- package/src/daemon/trace-emitter.ts +6 -4
- package/src/daemon/trust-context.ts +19 -0
- package/src/daemon/wake-target-adapter.ts +3 -1
- package/src/heartbeat/__tests__/heartbeat-service.test.ts +3 -0
- package/src/heartbeat/heartbeat-run-store.ts +23 -1
- package/src/heartbeat/heartbeat-service.ts +26 -0
- package/src/home/home-greeting-cache.ts +24 -1
- package/src/ipc/__tests__/browser-ipc.test.ts +1 -1
- package/src/ipc/__tests__/ui-request-route.test.ts +3 -3
- package/src/ipc/gateway-client.test.ts +2 -2
- package/src/ipc/gateway-client.ts +3 -3
- package/src/ipc/skill-routes/__tests__/memory.test.ts +15 -0
- package/src/ipc/skill-routes/memory.ts +4 -2
- package/src/media/gemini-image-service.ts +15 -0
- package/src/media/openai-image-service.ts +14 -0
- package/src/media/types.ts +34 -0
- package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +56 -0
- package/src/memory/auth-fallback-events-store.ts +94 -0
- package/src/memory/conversation-starter-checkpoints.ts +1 -0
- package/src/memory/conversation-title-service.ts +65 -41
- package/src/memory/db-init.ts +6 -0
- package/src/memory/graph/__tests__/conversation-graph-memory-registry.test.ts +119 -0
- package/src/memory/graph/conversation-graph-memory.ts +65 -0
- package/src/memory/job-handlers/conversation-starters.ts +13 -2
- package/src/memory/jobs-store.ts +33 -0
- package/src/memory/jobs-worker.ts +32 -5
- package/src/memory/llm-usage-store.ts +224 -50
- package/src/memory/migrations/222-strip-placeholder-sentinels-from-messages.ts +6 -5
- package/src/memory/migrations/270-schedule-source-conversation.ts +13 -0
- package/src/memory/migrations/271-create-auth-fallback-events.ts +21 -0
- package/src/memory/migrations/272-acp-session-history-cwd.ts +36 -0
- package/src/memory/migrations/index.ts +3 -0
- package/src/memory/pkb/autoinject.ts +61 -0
- package/src/memory/pkb/context.ts +50 -0
- package/src/memory/pkb/types.ts +14 -0
- package/src/memory/schedule-attribution-sql.ts +104 -0
- package/src/memory/schema/acp.ts +4 -0
- package/src/memory/schema/infrastructure.ts +16 -0
- package/src/memory/usage-grouped-buckets.ts +6 -1
- package/src/memory/v2/__tests__/consolidation-job.test.ts +4 -4
- package/src/memory/v2/consolidation-job.ts +14 -5
- package/src/notifications/conversation-pairing.ts +8 -15
- package/src/notifications/decision-engine.ts +6 -3
- package/src/notifications/home-feed-side-effect.ts +12 -1
- package/src/permissions/prompter.ts +4 -0
- package/src/plugin-api/constants.ts +4 -0
- package/src/plugin-api/index.ts +7 -5
- package/src/plugin-api/types.ts +151 -1
- package/src/plugins/defaults/compaction/compact.ts +59 -0
- package/src/plugins/defaults/compaction/package.json +1 -1
- package/src/plugins/defaults/compaction/register.ts +8 -19
- package/src/plugins/defaults/empty-response/hooks/stop.ts +126 -0
- package/src/plugins/defaults/empty-response/register.ts +8 -13
- package/src/plugins/defaults/index.ts +2 -18
- package/src/plugins/defaults/memory-retrieval/hooks/post-compact.ts +95 -0
- package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit-temp.ts +216 -0
- package/src/plugins/defaults/memory-retrieval/injector-chain.ts +35 -0
- package/src/plugins/defaults/{injectors/register.ts → memory-retrieval/injectors.ts} +288 -81
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/assign.test.ts +4 -4
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/health.test.ts +16 -0
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/live-integration.test.ts +4 -4
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/maintain-job.test.ts +5 -5
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/orchestrate.test.ts +48 -12
- package/src/plugins/defaults/memory-v3-shadow/__tests__/provider-blocks.test.ts +13 -0
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/reconcile.test.ts +2 -2
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/render-injection.test.ts +1 -1
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/router.test.ts +104 -32
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/selection-log-store.test.ts +8 -8
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/selector.test.ts +96 -30
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/shadow-plugin.test.ts +34 -16
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/assign.ts +5 -5
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/capabilities.ts +2 -2
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/health.ts +0 -0
- package/src/plugins/defaults/memory-v3-shadow/hooks/post-compact.ts +14 -0
- package/src/plugins/defaults/memory-v3-shadow/hooks/user-prompt-submit.ts +19 -0
- package/src/plugins/defaults/memory-v3-shadow/injector.ts +75 -0
- package/src/plugins/defaults/memory-v3-shadow/llm-retry.ts +32 -0
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/maintain-job.ts +8 -8
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/orchestrate.ts +26 -14
- package/src/plugins/defaults/{llm-call → memory-v3-shadow}/package.json +2 -2
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/page-content.ts +2 -2
- package/src/plugins/defaults/memory-v3-shadow/provider-blocks.ts +26 -0
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/reconcile.ts +3 -3
- package/src/plugins/defaults/memory-v3-shadow/register.ts +26 -0
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/render-injection.ts +1 -1
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/router.ts +51 -45
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/selection-log-store.ts +4 -4
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/selector.ts +61 -46
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/shadow-plugin.ts +69 -99
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/tree.ts +1 -1
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/types.ts +8 -0
- package/src/plugins/defaults/title-generate/hooks/stop.ts +75 -0
- package/src/plugins/defaults/title-generate/hooks/user-prompt-submit.ts +35 -0
- package/src/plugins/defaults/title-generate/package.json +1 -1
- package/src/plugins/defaults/title-generate/register.ts +18 -18
- package/src/plugins/defaults/tool-error/hooks/post-tool-use.ts +118 -0
- package/src/plugins/defaults/tool-error/package.json +1 -1
- package/src/plugins/defaults/tool-error/register.ts +9 -21
- package/src/plugins/defaults/tool-result-truncate/hooks/post-tool-use.ts +32 -0
- package/src/plugins/defaults/tool-result-truncate/register.ts +10 -21
- package/src/plugins/defaults/tool-result-truncate/terminal.ts +37 -18
- package/src/plugins/external-api.ts +2 -2
- package/src/plugins/pipeline.ts +6 -305
- package/src/plugins/registry.ts +10 -55
- package/src/plugins/types.ts +62 -797
- package/src/plugins/user-loader.ts +30 -127
- package/src/proactive-artifact/aux-message-injector.ts +4 -4
- package/src/proactive-artifact/job.test.ts +8 -13
- package/src/prompts/__tests__/system-prompt.test.ts +42 -0
- package/src/prompts/templates/BOOTSTRAP-ACTIVATION-RAIL.md +64 -0
- package/src/prompts/templates/BOOTSTRAP.md +2 -2
- package/src/prompts/templates/system-sections.ts +15 -0
- package/src/providers/anthropic/client.ts +37 -29
- package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +112 -0
- package/src/providers/openai/chat-completions-provider.ts +44 -0
- package/src/providers/openrouter/client.ts +1 -0
- package/src/providers/placeholder-sentinels.ts +35 -0
- package/src/runtime/__tests__/agent-wake.test.ts +10 -6
- package/src/runtime/__tests__/interactive-ui.test.ts +1 -1
- package/src/runtime/agent-wake.ts +2 -5
- package/src/runtime/assistant-event-hub.ts +37 -7
- package/src/runtime/{conversation-stream-state.ts → assistant-stream-state.ts} +132 -58
- package/src/runtime/channel-approvals.ts +1 -1
- package/src/runtime/http-router.ts +16 -21
- package/src/runtime/http-types.ts +16 -70
- package/src/runtime/interactive-ui.ts +1 -1
- package/src/runtime/pending-interactions.ts +1 -0
- package/src/runtime/routes/__tests__/acp-routes.test.ts +283 -55
- package/src/runtime/routes/__tests__/consolidation-routes.test.ts +265 -2
- package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +1 -1
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +31 -1
- package/src/runtime/routes/__tests__/memory-v2-routes.test.ts +6 -2
- package/src/runtime/routes/__tests__/surface-action-routes.test.ts +5 -4
- package/src/runtime/routes/__tests__/surface-content-routes.test.ts +4 -1
- package/src/runtime/routes/__tests__/tts-routes.test.ts +6 -2
- package/src/runtime/routes/acp-routes.test.ts +89 -25
- package/src/runtime/routes/acp-routes.ts +81 -29
- package/src/runtime/routes/app-management-routes.ts +6 -117
- package/src/runtime/routes/app-routes.ts +13 -15
- package/src/runtime/routes/approval-routes.ts +1 -1
- package/src/runtime/routes/attachment-routes.ts +26 -15
- package/src/runtime/routes/avatar-routes.ts +26 -0
- package/src/runtime/routes/browser-routes.ts +1 -1
- package/src/runtime/routes/browser-tabs-routes.ts +6 -10
- package/src/runtime/routes/btw-routes.ts +29 -23
- package/src/runtime/routes/consolidation-routes.ts +120 -20
- package/src/runtime/routes/conversation-cli-routes.ts +1 -1
- package/src/runtime/routes/conversation-list-routes.ts +1 -1
- package/src/runtime/routes/conversation-query-routes.ts +3 -1
- package/src/runtime/routes/conversation-routes.ts +372 -185
- package/src/runtime/routes/conversation-starter-routes.ts +13 -7
- package/src/runtime/routes/conversations-import-routes.ts +24 -7
- package/src/runtime/routes/documents-routes.ts +4 -0
- package/src/runtime/routes/domain-routes.ts +51 -37
- package/src/runtime/routes/epoch-millis-range.ts +34 -0
- package/src/runtime/routes/events-routes.ts +28 -34
- package/src/runtime/routes/gateway-log-routes.ts +26 -4
- package/src/runtime/routes/heartbeat-routes.ts +32 -12
- package/src/runtime/routes/host-app-control-routes.ts +1 -1
- package/src/runtime/routes/host-cu-routes.ts +1 -1
- package/src/runtime/routes/identity-intro-cache.ts +11 -34
- package/src/runtime/routes/identity-routes.ts +224 -18
- package/src/runtime/routes/image-generation-routes.ts +40 -2
- package/src/runtime/routes/inbound-message-handler.ts +1 -1
- package/src/runtime/routes/index.ts +2 -0
- package/src/runtime/routes/integrations/a2a.ts +12 -10
- package/src/runtime/routes/integrations/slack/__tests__/channel.test.ts +16 -0
- package/src/runtime/routes/integrations/slack/channel.ts +4 -0
- package/src/runtime/routes/integrations/slack/share.ts +27 -6
- package/src/runtime/routes/integrations/telegram.ts +6 -0
- package/src/runtime/routes/integrations/twilio.ts +42 -0
- package/src/runtime/routes/internal-telemetry-routes.ts +88 -0
- package/src/runtime/routes/log-export-routes.ts +8 -0
- package/src/runtime/routes/memory-v2-routes.ts +15 -8
- package/src/runtime/routes/memory-v3-routes.ts +66 -34
- package/src/runtime/routes/oauth-apps.ts +66 -12
- package/src/runtime/routes/oauth-providers.ts +44 -5
- package/src/runtime/routes/platform-routes.ts +81 -5
- package/src/runtime/routes/playground/__tests__/force-compact.test.ts +6 -4
- package/src/runtime/routes/playground/force-compact.ts +1 -1
- package/src/runtime/routes/playground/helpers.ts +1 -1
- package/src/runtime/routes/rename-conversation-routes.ts +5 -0
- package/src/runtime/routes/schedule-routes.ts +152 -42
- package/src/runtime/routes/secret-routes.ts +14 -2
- package/src/runtime/routes/skills-routes.ts +43 -14
- package/src/runtime/routes/surface-conversation-resolver.ts +4 -3
- package/src/runtime/routes/tool-call-confirmation-enrichment.test.ts +161 -0
- package/src/runtime/routes/tool-call-confirmation-enrichment.ts +107 -0
- package/src/runtime/routes/trust-rules-routes.ts +26 -2
- package/src/runtime/routes/tts-routes.ts +35 -0
- package/src/runtime/routes/types.ts +66 -8
- package/src/runtime/routes/usage-routes.ts +47 -39
- package/src/runtime/routes/webhook-routes.ts +41 -2
- package/src/runtime/routes/work-items-routes.ts +2 -4
- package/src/runtime/routes/workspace-routes.ts +4 -0
- package/src/runtime/services/__tests__/analyze-conversation.test.ts +6 -0
- package/src/runtime/services/analyze-conversation.ts +2 -2
- package/src/runtime/services/conversation-serializer.ts +1 -1
- package/src/schedule/schedule-store.ts +20 -1
- package/src/schedule/schedule-usage-store.ts +83 -0
- package/src/schedule/scheduler.ts +12 -5
- package/src/signals/cancel.ts +2 -4
- package/src/skills/catalog-files.ts +2 -2
- package/src/skills/catalog-install.ts +3 -0
- package/src/skills/categories-cache.ts +118 -0
- package/src/skills/clawhub-files.ts +1 -2
- package/src/skills/skillssh-files.ts +1 -2
- package/src/subagent/manager.ts +17 -5
- package/src/telemetry/types.ts +29 -1
- package/src/telemetry/usage-telemetry-reporter.test.ts +112 -3
- package/src/telemetry/usage-telemetry-reporter.ts +57 -2
- package/src/tools/acp/context.ts +20 -0
- package/src/tools/acp/list-agents.test.ts +7 -1
- package/src/tools/acp/spawn.test.ts +158 -55
- package/src/tools/acp/spawn.ts +47 -72
- package/src/tools/acp/steer.test.ts +105 -8
- package/src/tools/acp/steer.ts +48 -17
- package/src/tools/apps/executors.ts +13 -8
- package/src/tools/executor.ts +1 -53
- package/src/tools/filesystem/write.ts +34 -0
- package/src/tools/network/__tests__/web-search-metadata.test.ts +7 -1
- package/src/tools/network/__tests__/web-search.test.ts +11 -3
- package/src/tools/network/web-search-error.test.ts +248 -0
- package/src/tools/network/web-search-error.ts +267 -0
- package/src/tools/network/web-search.ts +207 -48
- package/src/tools/schedule/create.ts +2 -0
- package/src/tools/subagent/spawn.ts +2 -4
- package/src/tools/terminal/safe-env.ts +10 -1
- package/src/tools/ui-surface/definitions.ts +34 -5
- package/src/tts/__tests__/provider-catalog-consistency.test.ts +85 -1
- package/src/tts/provider-catalog.ts +76 -1
- package/src/util/mutex.ts +47 -0
- package/src/workspace/git-service.ts +1 -42
- package/src/workspace/migrations/051-seed-conversation-summarization-callsite.ts +4 -5
- package/src/workspace/migrations/095-bump-heartbeat-interval-30m-to-60m.ts +51 -0
- package/src/workspace/migrations/096-reduce-quality-profile-effort.ts +72 -0
- package/src/workspace/migrations/097-enable-adaptive-thinking-managed-profiles.ts +117 -0
- package/src/workspace/migrations/registry.ts +6 -0
- package/docs/plugins.md +0 -836
- package/examples/plugins/echo/register.ts +0 -184
- package/src/__tests__/bootstrap-turn-cleanup.test.ts +0 -44
- package/src/__tests__/circuit-breaker-pipeline.test.ts +0 -405
- package/src/__tests__/compaction-pipeline.test.ts +0 -210
- package/src/__tests__/compaction-timeout-recovery.test.ts +0 -251
- package/src/__tests__/empty-response-pipeline.test.ts +0 -423
- package/src/__tests__/llm-call-pipeline.test.ts +0 -287
- package/src/__tests__/memory-retrieval-pipeline.test.ts +0 -418
- package/src/__tests__/persistence-pipeline.test.ts +0 -503
- package/src/__tests__/pipeline-runner.test.ts +0 -564
- package/src/__tests__/title-generate-pipeline.test.ts +0 -211
- package/src/__tests__/token-estimate-pipeline.test.ts +0 -479
- package/src/__tests__/tool-error-pipeline.test.ts +0 -241
- package/src/__tests__/tool-execute-pipeline.test.ts +0 -417
- package/src/__tests__/tool-result-truncate-pipeline.test.ts +0 -341
- package/src/daemon/bootstrap-turn-cleanup.ts +0 -45
- package/src/gallery/default-gallery.ts +0 -1359
- package/src/gallery/gallery-manifest.ts +0 -28
- package/src/home/feature-gate.ts +0 -22
- package/src/memory/v3/provider-blocks.ts +0 -16
- package/src/plugins/defaults/circuit-breaker/middlewares/circuitBreaker.ts +0 -93
- package/src/plugins/defaults/circuit-breaker/package.json +0 -15
- package/src/plugins/defaults/circuit-breaker/register.ts +0 -39
- package/src/plugins/defaults/compaction/middlewares/compaction.ts +0 -25
- package/src/plugins/defaults/compaction/terminal.ts +0 -73
- package/src/plugins/defaults/empty-response/middlewares/emptyResponse.ts +0 -22
- package/src/plugins/defaults/empty-response/terminal.ts +0 -106
- package/src/plugins/defaults/injectors/package.json +0 -15
- package/src/plugins/defaults/llm-call/middlewares/llmCall.ts +0 -17
- package/src/plugins/defaults/llm-call/register.ts +0 -45
- package/src/plugins/defaults/memory-retrieval/middlewares/memoryRetrieval.ts +0 -17
- package/src/plugins/defaults/memory-retrieval/package.json +0 -15
- package/src/plugins/defaults/memory-retrieval/register.ts +0 -181
- package/src/plugins/defaults/overflow-reduce/middlewares/overflowReduce.ts +0 -126
- package/src/plugins/defaults/overflow-reduce/package.json +0 -15
- package/src/plugins/defaults/overflow-reduce/register.ts +0 -42
- package/src/plugins/defaults/persistence/middlewares/persistence.ts +0 -19
- package/src/plugins/defaults/persistence/package.json +0 -15
- package/src/plugins/defaults/persistence/register.ts +0 -38
- package/src/plugins/defaults/persistence/terminal.ts +0 -83
- package/src/plugins/defaults/title-generate/terminal.ts +0 -31
- package/src/plugins/defaults/token-estimate/middlewares/tokenEstimate.ts +0 -23
- package/src/plugins/defaults/token-estimate/package.json +0 -15
- package/src/plugins/defaults/token-estimate/register.ts +0 -34
- package/src/plugins/defaults/token-estimate/terminal.ts +0 -40
- package/src/plugins/defaults/tool-error/middlewares/toolError.ts +0 -21
- package/src/plugins/defaults/tool-error/terminal.ts +0 -47
- package/src/plugins/defaults/tool-execute/middlewares/toolExecute.ts +0 -23
- package/src/plugins/defaults/tool-execute/package.json +0 -15
- package/src/plugins/defaults/tool-execute/register.ts +0 -49
- package/src/plugins/defaults/tool-result-truncate/middlewares/toolResultTruncate.ts +0 -23
- package/src/plugins/defaults/tool-result-truncate/types.ts +0 -22
- package/src/skills/category-inference.ts +0 -111
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/capabilities.test.ts +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/core.test.ts +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/fixtures/eval-turns.json +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/fixtures/live-turns.json +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/needle.test.ts +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/snapshot.test.ts +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/tree.test.ts +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/types.test.ts +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/working-set-eviction.test.ts +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/working-set-skeleton.test.ts +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/core.ts +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/README.md +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/assignments.json +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/core.json +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/leaves/domain-a/topic-x.md +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/leaves/domain-a/topic-y.md +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/leaves/domain-b/topic-z.md +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/needle.ts +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/snapshot.ts +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/working-set.ts +0 -0
|
@@ -1,529 +1,355 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: app-builder
|
|
3
|
-
description: Build
|
|
4
|
-
compatibility: "Designed for Vellum personal assistants"
|
|
3
|
+
description: Build and edit small, personal visual tools and artifacts — dashboards, trackers, calculators, data visualizations, charts, simple landing pages, and slide decks the user wants for THEMSELVES. This is the right skill whenever the user asks to "visualize this," "make a chart," or "build an artifact" for their own use, or to edit an app they already built here. Do NOT reach for a ui_show dynamic_page to fake an artifact — build a real persistent app here. NOT for complex, multi-user, or shippable products — those go to a real project folder with a coding agent (see Scope below).
|
|
5
4
|
metadata:
|
|
6
|
-
emoji: "
|
|
5
|
+
emoji: "🛠️"
|
|
7
6
|
vellum:
|
|
8
7
|
display-name: "App Builder"
|
|
9
8
|
activation-hints:
|
|
10
|
-
- "User asks to build
|
|
11
|
-
- "User asks to visualize
|
|
12
|
-
- "
|
|
9
|
+
- "User asks to build a dashboard, tracker, calculator, data visualization, chart, simple landing page, or slide deck for their own use"
|
|
10
|
+
- "User asks to visualize something, make a chart, or build an artifact — build a real persistent app here, never a ui_show dynamic_page"
|
|
11
|
+
- "User asks to change, fix, restyle, or extend an app they already built in the sandbox — open it and iterate"
|
|
12
|
+
avoid-when:
|
|
13
|
+
- "User wants a complex app, a multi-user app, or something to publish, deploy, or hand off to others — route to a local project folder + coding agent instead (see Scope)"
|
|
13
14
|
---
|
|
14
15
|
|
|
15
|
-
You
|
|
16
|
+
You build small, personal visual tools — dashboards, trackers, calculators, data visualizations, simple landing pages, and slide decks. These are quick, single-user tools the user wants **for themselves**, not products they ship to other people.
|
|
16
17
|
|
|
17
|
-
|
|
18
|
+
Load `frontend-design` first (`skill_load("frontend-design")`), then move fast: think, plan in one pass, pick a striking visual direction following that skill, and build it immediately. Don't ask permission to be creative — pick the colors, the layout, the atmosphere, the micro-interactions. Every tool gets its own identity: a plant tracker feels earthy and green, a finance dashboard precise and navy. They should feel designed, not generated.
|
|
18
19
|
|
|
19
|
-
**
|
|
20
|
+
**Design quality is delegated to the `frontend-design` skill. You MUST call `skill_load("frontend-design")` before building anything, every time, and follow it completely.** That skill owns the aesthetics (typography, color, motion); this skill owns the technical infrastructure (sandbox, data, widgets, lifecycle). Skipping the load gives generic, templated UI, which is a failed build.
|
|
20
21
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
## Filesystem Layout
|
|
24
|
-
|
|
25
|
-
Apps live under `{workspaceDir}/data/apps/`. Each app has a slug-based layout:
|
|
26
|
-
|
|
27
|
-
```
|
|
28
|
-
{workspaceDir}/data/apps/
|
|
29
|
-
<slug>.json # App metadata
|
|
30
|
-
<slug>/ # App directory (contains all app files)
|
|
31
|
-
index.html # Legacy single-file entry point (do not create for new apps)
|
|
32
|
-
pages/ # Legacy additional pages (do not create for new apps)
|
|
33
|
-
records/ # Data records (one JSON file per record)
|
|
34
|
-
src/ # Source files (multi-file TSX apps, formatVersion: 2)
|
|
35
|
-
dist/ # Compiled output (multi-file TSX apps)
|
|
36
|
-
<slug>.preview # Preview image (auto-generated)
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
### Metadata JSON (`<slug>.json`)
|
|
40
|
-
|
|
41
|
-
Fields: `id`, `name`, `description`, `icon`, `schemaJson`, `createdAt`, `updatedAt`, `formatVersion`, `dirName`.
|
|
42
|
-
|
|
43
|
-
**Important:** Legacy `htmlDefinition` and `pages` content is NOT stored in the metadata JSON — it lives as separate files inside the app directory (`index.html` and `pages/`). Do not create new single-file apps or new `pages/` directories.
|
|
44
|
-
|
|
45
|
-
### Records
|
|
46
|
-
|
|
47
|
-
Each record is a JSON file at `<slug>/records/<uuid>.json` with shape:
|
|
48
|
-
|
|
49
|
-
```json
|
|
50
|
-
{ "id": "<uuid>", "appId": "<app-id>", "data": { ... }, "createdAt": "...", "updatedAt": "..." }
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
### Multi-file TSX Apps
|
|
54
|
-
|
|
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
|
-
|
|
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.
|
|
22
|
+
---
|
|
68
23
|
|
|
69
|
-
|
|
24
|
+
## Scope — what belongs here, what doesn't
|
|
70
25
|
|
|
71
|
-
|
|
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.
|
|
26
|
+
**Build here** (the default — lean toward it): a tool the user wants for themselves. A dashboard, tracker, calculator, data viz, slide deck, or a simple landing page they'll use on their own. Personal and self-contained.
|
|
74
27
|
|
|
75
|
-
**
|
|
28
|
+
**Does NOT belong here:** anything complex, multi-user, or meant to be **published, deployed, handed off, or shipped to other people**. Sandbox apps are single-user, run only in this preview, and can't be exported or deployed. They're the wrong home for a real product.
|
|
76
29
|
|
|
77
|
-
|
|
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.
|
|
30
|
+
When a request is for a shippable/complex app, don't build in the sandbox. Instead:
|
|
79
31
|
|
|
80
|
-
**
|
|
32
|
+
1. **Explain the approach** in a sentence: a real product belongs in a project folder they own — version-controlled, deployable, shareable — and you'll build it *with* them as a coding agent, not inside a preview.
|
|
33
|
+
2. **Establish a project folder** (propose a path, or use one they name).
|
|
34
|
+
3. **Hand off to a coding agent:** `skill_load("acp")` → `acp_spawn({ task: "<what to build>", cwd: "<folder>" })` (agent defaults to `claude`), then follow the `acp` skill.
|
|
81
35
|
|
|
82
|
-
|
|
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.
|
|
36
|
+
Triage on intent, not artifact type. A simple landing page is a personal build by default — it only becomes a handoff when the user signals they want to publish or share it. When the signal is weak, lean personal and just build. If you genuinely can't tell, ask exactly **one** short question.
|
|
85
37
|
|
|
86
|
-
**
|
|
38
|
+
**Editing an existing sandbox app? Skip scope entirely** — that's iteration. Resolve the app (see below), open it, and go to *Iteration*.
|
|
87
39
|
|
|
88
|
-
|
|
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.
|
|
40
|
+
### Resolving an app the user mentions
|
|
91
41
|
|
|
92
|
-
|
|
42
|
+
`app_open` takes an `app_id`, not a name:
|
|
93
43
|
|
|
94
|
-
|
|
44
|
+
1. If the `app_id` is already in your context, use it.
|
|
45
|
+
2. Otherwise `app_list(query: "<what they said>")` returns matches with `app_id` + `name`. `app_list()` with no query lists everything.
|
|
46
|
+
3. One match → open it. Multiple → list them and ask which. None → say so, show what exists, offer to build it.
|
|
95
47
|
|
|
96
|
-
|
|
48
|
+
---
|
|
97
49
|
|
|
98
|
-
|
|
50
|
+
## Filesystem layout
|
|
99
51
|
|
|
100
|
-
|
|
52
|
+
Apps live under `/workspace/data/apps/`:
|
|
101
53
|
|
|
102
|
-
|
|
54
|
+
```
|
|
55
|
+
/workspace/data/apps/
|
|
56
|
+
<slug>.json # App metadata
|
|
57
|
+
<slug>/
|
|
58
|
+
src/ # Source files (TSX) — what you write
|
|
59
|
+
dist/ # Compiled output — auto-generated by app_refresh
|
|
60
|
+
records/ # Data records (one JSON file per record)
|
|
61
|
+
<slug>.preview # Preview image (auto-generated)
|
|
62
|
+
```
|
|
103
63
|
|
|
104
|
-
|
|
64
|
+
Metadata fields: `id`, `name`, `description`, `icon`, `schemaJson`, `createdAt`, `updatedAt`, `formatVersion`, `dirName`. Records: `{ "id", "appId", "data": {...}, "createdAt", "updatedAt" }` — the system auto-adds everything but `data`.
|
|
105
65
|
|
|
106
|
-
|
|
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.
|
|
66
|
+
All new apps use `formatVersion: 2` (multi-file TSX). No root-level `index.html` or `pages/` — those are legacy.
|
|
109
67
|
|
|
110
|
-
|
|
68
|
+
⚠️ Correct source path is `/workspace/data/apps/<slug>/src/`. Never `/workspace/apps/`.
|
|
111
69
|
|
|
112
|
-
|
|
70
|
+
---
|
|
113
71
|
|
|
114
|
-
|
|
72
|
+
## Responsive & design system
|
|
115
73
|
|
|
116
|
-
|
|
74
|
+
Every app works phone (~360px) to desktop (~1400px+). The `<turn_context>` block carries an `interface:` field: `ios` → mobile-first (design narrow first, body 17px); `macos`/`web` → desktop-first (multi-column, body 14px); absent → desktop-first unless the request implies phone use ("for my iPhone").
|
|
117
75
|
|
|
118
|
-
|
|
76
|
+
**Universal baseline — every build, regardless of interface:**
|
|
77
|
+
- Viewport meta: `width=device-width, initial-scale=1, viewport-fit=cover`. Never `user-scalable=no` (blocks accessibility zoom).
|
|
78
|
+
- Pad the root with `env(safe-area-inset-*)` so content clears the notch: `padding-top: max(var(--v-spacing-lg), env(safe-area-inset-top))`, mirrored for the other sides.
|
|
79
|
+
- Full-height containers use `100dvh`, not `100vh`.
|
|
80
|
+
- Form controls (`input`/`textarea`/`select`) must be `font-size: 16px`+ or iOS Safari zooms on focus. Add `inputmode` (`numeric`/`decimal`/`email`/`tel`/`url`).
|
|
81
|
+
- Interactive elements ≥44×44pt (`.v-button` already complies; custom controls set `min-height: 44px`). Gate hover behind `@media (hover: hover)`.
|
|
82
|
+
- Fluid widths only — `%`, `fr`, `minmax`, `clamp()`, never fixed `px` on containers. Size chart containers in `vw`/`%`. At narrow widths, collapse tables into stacked label-value cards.
|
|
119
83
|
|
|
120
|
-
|
|
84
|
+
**Mobile-first extras (`interface: ios`):** body `--v-font-size-lg` (17px); one column by default, multi-column only above `@media (min-width: 720px)`; bottom-anchor the primary action (`position: sticky; bottom: env(safe-area-inset-bottom)`); bottom sheets instead of side modals.
|
|
121
85
|
|
|
122
|
-
|
|
86
|
+
Full detail when reachable: `{baseDir}/references/RESPONSIVE.md`.
|
|
123
87
|
|
|
124
|
-
|
|
88
|
+
A design-system CSS and widget library are **auto-injected** (inside a `@layer`, so your own styles always win). Use the `--v-*` variables and `.v-*` classes below — they switch light/dark automatically, no manual dark-mode CSS needed. **Always use `window.vellum.widgets.*` chart functions** instead of hand-coded SVG/CSS charts.
|
|
125
89
|
|
|
126
|
-
|
|
127
|
-
assistant inference session list
|
|
128
|
-
```
|
|
90
|
+
**Design tokens** (use these, don't invent hex values):
|
|
129
91
|
|
|
130
|
-
|
|
92
|
+
| Category | Tokens |
|
|
93
|
+
| --- | --- |
|
|
94
|
+
| Backgrounds | `--v-bg`, `--v-surface`, `--v-surface-border` |
|
|
95
|
+
| Text | `--v-text`, `--v-text-secondary`, `--v-text-muted` |
|
|
96
|
+
| Accent | `--v-accent`, `--v-accent-hover` |
|
|
97
|
+
| Status | `--v-success`, `--v-danger`, `--v-warning` |
|
|
98
|
+
| Spacing | `--v-spacing-xxs`(2) `-xs`(4) `-sm`(8) `-md`(12) `-lg`(16) `-xl`(24) `-xxl`(32) `-xxxl`(48) |
|
|
99
|
+
| Radius | `--v-radius-xs`(2) `-sm`(4) `-md`(8) `-lg`(12) `-xl`(16) `-pill`(999) |
|
|
100
|
+
| Shadows | `--v-shadow-sm/md/lg` |
|
|
101
|
+
| Typography | `--v-font-family`, `--v-font-mono`, `--v-font-size-xs`(10) `-sm`(11) `-base`(14) `-lg`(17) `-xl`(22) `-2xl`(26) |
|
|
102
|
+
| Animation | `--v-duration-fast`(.15s) `-standard`(.25s) `-slow`(.4s) |
|
|
103
|
+
| Palettes | `--v-slate/emerald/violet/indigo/rose/amber-{950..50}` |
|
|
104
|
+
| Constant | `--v-aux-white` (always `#FFF` both modes — text on filled/accent backgrounds) |
|
|
131
105
|
|
|
132
|
-
|
|
133
|
-
assistant config get llm.activeProfile
|
|
134
|
-
```
|
|
106
|
+
**Utility classes:** `.v-button` (`.secondary`/`.danger`/`.ghost`), `.v-card`, `.v-list`/`.v-list-item`, `.v-badge` (`.success`/`.warning`/`.danger`), `.v-input-row`, `.v-empty-state`, `.v-toggle`.
|
|
135
107
|
|
|
136
|
-
|
|
108
|
+
**Theme in JS:** `window.vellum.theme.mode` (`'light'`/`'dark'`); listen on `window.addEventListener("vellum-theme-change", e => e.detail.mode)`.
|
|
137
109
|
|
|
138
|
-
|
|
110
|
+
For a **custom branded look**, write complete CSS with hardcoded colors + `@media (prefers-color-scheme: dark)` — don't mix `--v-*` auto-switching vars with hardcoded colors in the same element.
|
|
139
111
|
|
|
140
|
-
|
|
141
|
-
ui_show({
|
|
142
|
-
surface_type: "confirmation",
|
|
143
|
-
title: "Use quality model for this app?",
|
|
144
|
-
data: {
|
|
145
|
-
message: "The current model profile is `<profile>`. App building works best with `quality-optimized` because it makes better design decisions, writes cleaner components, and produces more visually polished results.",
|
|
146
|
-
detail: "Choose whether to switch for this build or keep the current profile and build now.",
|
|
147
|
-
confirmLabel: "Switch for this build",
|
|
148
|
-
cancelLabel: "Keep current profile"
|
|
149
|
-
},
|
|
150
|
-
display: "inline",
|
|
151
|
-
await_action: true
|
|
152
|
-
})
|
|
153
|
-
```
|
|
112
|
+
⚠️ Never hardcode `color: white` / `#fff` — use `var(--v-aux-white)` on filled/accent backgrounds, `var(--v-text)` / `var(--v-text-secondary)` on surfaces. Hardcoded white goes invisible on light surfaces.
|
|
154
113
|
|
|
155
|
-
|
|
114
|
+
Full detail when reachable: `{baseDir}/references/DESIGN_SYSTEM.md`. Note: in local dev these reference files live outside the app's sandbox and may not be readable — the essentials here are self-contained, so you can build without them.
|
|
156
115
|
|
|
157
|
-
|
|
116
|
+
### Widget library (auto-injected)
|
|
158
117
|
|
|
159
|
-
|
|
160
|
-
assistant inference session open quality-optimized --ttl 1h
|
|
161
|
-
```
|
|
118
|
+
CSS classes for standard patterns: `.v-metric-card`/`.v-metric-grid` (big-number stats), `.v-data-table` (sortable, sticky header, `th[data-sortable]`), `.v-tabs`, `.v-accordion`, `.v-search-bar`, `.v-timeline`, `.v-action-list` (rows with per-item actions), `.v-card-grid`, `.v-progress-bar`, `.v-status-badge` (`.success`/`.error`/`.warning`/`.info`), `.v-stat-row`/`.v-stat`, `.v-tag-group`, `.v-avatar-row`. Landing-page components: `.v-hero`/`.v-hero-badge`/`.v-hero-subtitle`, `.v-section-header`/`.v-section-label`, `.v-feature-grid`/`.v-feature-card`, `.v-pullquote`, `.v-comparison` (`.before`/`.after`), `.v-page`, `.v-gradient-text`, `.v-animate-in`. Domain widgets: `.v-weather-card`, `.v-stock-ticker`, `.v-receipt`, `.v-invoice`, `.v-itinerary`, `.v-boarding-pass`.
|
|
162
119
|
|
|
163
|
-
|
|
120
|
+
JS utilities at `window.vellum.widgets.*`:
|
|
164
121
|
|
|
122
|
+
```javascript
|
|
123
|
+
// Charts — ALWAYS use these, never hand-code SVG/CSS charts (they handle bounds, scaling, dark mode)
|
|
124
|
+
vellum.widgets.sparkline("el-id", [10,25,15,30], { width:200, height:40, color:"var(--v-success)", fill:true });
|
|
125
|
+
vellum.widgets.barChart("el-id", [{label:"Jan",value:120},{label:"Feb",value:180,color:"var(--v-success)"}], { width:400, height:200, showValues:true, horizontal:false });
|
|
126
|
+
vellum.widgets.lineChart("el-id", [{label:"Mon",value:42},{label:"Tue",value:58}], { width:400, height:200, showDots:true, showGrid:true });
|
|
127
|
+
vellum.widgets.progressRing("el-id", 75, { size:100, strokeWidth:8, color:"var(--v-success)", label:"75%" });
|
|
128
|
+
// Formatting
|
|
129
|
+
vellum.widgets.formatCurrency(1234.56, "USD"); // "$1,234.56"
|
|
130
|
+
vellum.widgets.formatDate("2025-01-15", "relative"); // "3d ago" ("short" → "1/15/25")
|
|
131
|
+
vellum.widgets.formatNumber(1234567, { compact:true }); // "1.2M"
|
|
132
|
+
// Behaviors
|
|
133
|
+
vellum.widgets.sortTable("table-id"); // wire th[data-sortable]
|
|
134
|
+
vellum.widgets.filterTable("table-id", "input-id"); // live text search
|
|
135
|
+
vellum.widgets.tabs("tabs-id"); vellum.widgets.accordion("acc-id", { allowMultiple:true });
|
|
136
|
+
vellum.widgets.toast("Saved!", "success", 4000); // success | error | warning | info
|
|
137
|
+
vellum.widgets.countdown("el", "2025-12-31T00:00:00Z", { onComplete:()=>{} });
|
|
165
138
|
```
|
|
166
|
-
assistant config get llm.profiles
|
|
167
|
-
assistant inference session open <profile-name> --ttl 1h
|
|
168
|
-
```
|
|
169
|
-
|
|
170
|
-
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.
|
|
171
|
-
|
|
172
|
-
**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.
|
|
173
|
-
|
|
174
|
-
If `assistant inference session` isn't available on this binary, proceed without it.
|
|
175
|
-
|
|
176
|
-
### 1. Gather Requirements
|
|
177
|
-
|
|
178
|
-
**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:
|
|
179
139
|
|
|
180
|
-
|
|
181
|
-
2. Pick a distinctive visual direction following the `frontend-design` skill
|
|
182
|
-
3. Design a clean data schema
|
|
183
|
-
4. Build the complete, polished app with animations, interactions, and empty states
|
|
140
|
+
Use custom HTML for novel/creative UIs (games, art tools); widgets for standard patterns; mix freely. Full list: `{baseDir}/references/WIDGETS.md`.
|
|
184
141
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
**Build all new apps as multi-file TSX projects.** They give you component reuse, TypeScript safety, and cleaner organization.
|
|
188
|
-
|
|
189
|
-
**Only ask questions when the request is genuinely ambiguous** - e.g., "build me an app" with no indication of what kind. Even then, prefer building something impressive based on context clues over asking a battery of questions.
|
|
142
|
+
---
|
|
190
143
|
|
|
191
|
-
|
|
144
|
+
## Build workflow
|
|
192
145
|
|
|
193
|
-
|
|
146
|
+
### 0. Preflight — optional profile switch
|
|
194
147
|
|
|
195
|
-
|
|
148
|
+
App builds are multi-step and benefit from a stronger model. If the active model profile looks weak for this work, you may offer to switch profiles first. Use the `ui_show` tool to ask, with `surface_type: "confirmation"` and `await_action: true`, so the user explicitly opts in before anything changes. Do not call the shell command `assistant ui confirm` for this — it can block the build flow before app work starts. If the user declines, just proceed on the current profile.
|
|
196
149
|
|
|
197
|
-
|
|
150
|
+
### 1 — Plan and build, fast
|
|
198
151
|
|
|
199
|
-
|
|
152
|
+
Think (what's the tool, who's the single user), plan in one pass (visual direction, minimal schema, core layout), then build. No wireframes, no mockups, no color questions. Make the creative calls yourself. Only ask a question when the request is genuinely ambiguous about *what to build* — and even then, prefer building something strong from context clues.
|
|
200
153
|
|
|
201
|
-
|
|
202
|
-
- Define `properties` for each field
|
|
203
|
-
- Supported types: `string`, `number`, `boolean`
|
|
204
|
-
- Add a `required` array for mandatory fields
|
|
205
|
-
- Keep schemas reasonably flat - encode complex nested data as JSON strings when needed
|
|
154
|
+
### 2 — Design the data schema (only if it persists data)
|
|
206
155
|
|
|
207
|
-
|
|
156
|
+
A JSON Schema for a single record. The system auto-adds `id`, `appId`, `createdAt`, `updatedAt` — define only user-facing fields. Keep it flat (`string`, `number`, `boolean`); encode nested data as JSON strings.
|
|
208
157
|
|
|
209
158
|
```json
|
|
210
159
|
{
|
|
211
160
|
"type": "object",
|
|
212
161
|
"properties": {
|
|
213
|
-
"title":
|
|
214
|
-
"status": {
|
|
215
|
-
"type": "string",
|
|
216
|
-
"enum": ["backlog", "in-progress", "review", "done"]
|
|
217
|
-
},
|
|
218
|
-
"priority": {
|
|
219
|
-
"type": "string",
|
|
220
|
-
"enum": ["low", "medium", "high", "critical"]
|
|
221
|
-
},
|
|
222
|
-
"description": { "type": "string" },
|
|
223
|
-
"tags": { "type": "string" }
|
|
162
|
+
"title": { "type": "string" },
|
|
163
|
+
"status": { "type": "string", "enum": ["todo", "doing", "done"] }
|
|
224
164
|
},
|
|
225
|
-
"required": ["title"
|
|
165
|
+
"required": ["title"]
|
|
226
166
|
}
|
|
227
167
|
```
|
|
228
168
|
|
|
229
|
-
|
|
169
|
+
Calculators, single-page tools, landing pages, and slide decks skip this — pass an empty `schema_json` or omit it.
|
|
230
170
|
|
|
231
|
-
|
|
171
|
+
### 3 — Create the app (scaffold, then expand)
|
|
232
172
|
|
|
233
|
-
|
|
173
|
+
⚠️ **`app_create` is ONE-SHOT per build.** Call it exactly once. After it returns an `app_id`, all further changes go through `file_write` / `file_edit` + `app_refresh`. To start over: `app_delete(app_id)` first, then a fresh `app_create`.
|
|
234
174
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
**Project structure:**
|
|
175
|
+
Apps are multi-file Preact + TSX projects; esbuild bundles automatically. Structure:
|
|
238
176
|
|
|
239
177
|
```
|
|
240
178
|
src/
|
|
241
|
-
index.html #
|
|
242
|
-
main.tsx
|
|
243
|
-
components/
|
|
244
|
-
|
|
245
|
-
RecordList.tsx
|
|
246
|
-
...
|
|
247
|
-
styles.css # Global styles (imported from TSX)
|
|
179
|
+
index.html # Minimal shell that loads the bundle
|
|
180
|
+
main.tsx # Renders <App /> into #app
|
|
181
|
+
components/App.tsx # Top-level component
|
|
182
|
+
styles.css # Global styles (import from TSX)
|
|
248
183
|
```
|
|
249
184
|
|
|
250
|
-
**Preact usage:**
|
|
251
|
-
|
|
252
185
|
```tsx
|
|
253
186
|
import { render } from "preact";
|
|
254
|
-
import { useState, useEffect } from "preact/hooks";
|
|
255
187
|
import { App } from "./components/App";
|
|
256
|
-
|
|
188
|
+
import "./styles.css";
|
|
257
189
|
render(<App />, document.getElementById("app")!);
|
|
258
190
|
```
|
|
259
191
|
|
|
260
|
-
|
|
192
|
+
**Scaffold-then-expand** is the pattern for every non-trivial app. Cramming all files into one `app_create` blows the response token budget mid-emit:
|
|
261
193
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
interface Props {
|
|
266
|
-
title: string;
|
|
267
|
-
count: number;
|
|
268
|
-
}
|
|
194
|
+
1. **`app_create`** with a **4-file scaffold**: `src/index.html`, `src/main.tsx`, a **placeholder** `src/components/App.tsx` (`<div>Loading...</div>`), and an **empty** `src/styles.css`. The placeholders make the first compile clean — a 2-file scaffold leaves broken imports.
|
|
195
|
+
2. **`file_write`** each real file, one per tool call, overwriting the placeholders and adding components.
|
|
196
|
+
3. **`app_refresh`** ONCE at the end to compile.
|
|
269
197
|
|
|
270
|
-
|
|
271
|
-
return (
|
|
272
|
-
<header>
|
|
273
|
-
<h1>{title}</h1>
|
|
274
|
-
<span className="badge">{count}</span>
|
|
275
|
-
</header>
|
|
276
|
-
);
|
|
277
|
-
};
|
|
278
|
-
```
|
|
198
|
+
**Allowed packages** (esbuild-resolved, no CDN): `date-fns`, `chart.js`, `lodash-es`, `zod`, `clsx`, `lucide` (use `lucide`, NOT `lucide-react`).
|
|
279
199
|
|
|
280
|
-
**
|
|
200
|
+
**Constraints:** Preact not React. No CDN imports. No external fonts/images (system fonts, inline CSS/SVG). Responsive only, no fixed-pixel widths. The WebView blocks navigation — `href` and form `action` don't work.
|
|
281
201
|
|
|
282
|
-
|
|
202
|
+
⚠️ `compile_errors` in the `app_create` response is NOT a retry signal — the response also has an `app_id`, so the app was created. Proceed. Calling `app_create` again makes a duplicate.
|
|
283
203
|
|
|
284
|
-
|
|
204
|
+
#### `app_create` accepts EXACTLY these 7 keys — nothing else
|
|
285
205
|
|
|
286
|
-
|
|
287
|
-
const [items, setItems] = useState<Item[]>([]);
|
|
288
|
-
|
|
289
|
-
useEffect(() => {
|
|
290
|
-
window.vellum.fetch("/v1/x/items")
|
|
291
|
-
.then((res) => (res.ok ? res.json() : Promise.reject(res.status)))
|
|
292
|
-
.then(setItems)
|
|
293
|
-
.catch(console.error);
|
|
294
|
-
}, []);
|
|
295
|
-
```
|
|
206
|
+
`name` (required), `description`, `schema_json`, `source_files`, `preview`, `auto_open`, `change_summary`.
|
|
296
207
|
|
|
297
|
-
|
|
208
|
+
Anything else fails with `Invalid input for tool "app_create": Unknown parameter "X"`. The retired keys models still reach for:
|
|
298
209
|
|
|
299
|
-
|
|
210
|
+
- **`html`** — old single-file shortcut. Put your HTML inside `source_files["src/index.html"]`.
|
|
211
|
+
- **`pages`** — retired. Multi-page apps use TSX components under `src/components/`.
|
|
212
|
+
- **`icon`** — NOT a top-level param. An emoji icon goes in `preview.icon` (e.g. `preview: { title: "Bean Coffee", icon: "☕" }`). For an AI-generated icon, call `app_generate_icon(app_id, description)` *after* the app exists.
|
|
213
|
+
- **A file path as a top-level key** (e.g. `"src/components/Header.tsx"`) — these go inside `source_files`, or in a `file_write` after `app_create`.
|
|
300
214
|
|
|
301
|
-
|
|
215
|
+
If a prior session in your context shows `app_create({ html })` or `app_create({ pages })`, that example is outdated — ignore it.
|
|
302
216
|
|
|
303
217
|
```
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
<body><div id="app"></div></body>
|
|
315
|
-
</html>`,
|
|
316
|
-
"src/main.tsx": `import { render } from 'preact';
|
|
317
|
-
import { App } from './components/App';
|
|
318
|
-
import './styles.css';
|
|
319
|
-
|
|
320
|
-
render(<App />, document.getElementById('app')!);`,
|
|
321
|
-
"src/components/App.tsx": `import { FunctionComponent } from 'preact';
|
|
322
|
-
import { useState, useEffect } from 'preact/hooks';
|
|
323
|
-
import { Header } from './Header';
|
|
324
|
-
|
|
325
|
-
export const App: FunctionComponent = () => {
|
|
326
|
-
const [records, setRecords] = useState([]);
|
|
327
|
-
|
|
328
|
-
useEffect(() => {
|
|
329
|
-
window.vellum.fetch("/v1/x/projects")
|
|
330
|
-
.then((res) => res.ok ? res.json() : Promise.reject(res.status))
|
|
331
|
-
.then(setRecords)
|
|
332
|
-
.catch(console.error);
|
|
333
|
-
}, []);
|
|
334
|
-
|
|
335
|
-
return (
|
|
336
|
-
<div className="app">
|
|
337
|
-
<Header title="Project Tracker" count={records.length} />
|
|
338
|
-
{/* ... */}
|
|
339
|
-
</div>
|
|
340
|
-
);
|
|
341
|
-
};`,
|
|
342
|
-
"src/components/Header.tsx": `import { FunctionComponent } from 'preact';
|
|
343
|
-
|
|
344
|
-
interface HeaderProps {
|
|
345
|
-
title: string;
|
|
346
|
-
count: number;
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
export const Header: FunctionComponent<HeaderProps> = ({ title, count }) => (
|
|
350
|
-
<header className="header">
|
|
351
|
-
<h1>{title}</h1>
|
|
352
|
-
<span className="badge">{count} items</span>
|
|
353
|
-
</header>
|
|
354
|
-
);`,
|
|
355
|
-
"src/styles.css": `.app { padding: var(--v-spacing-lg); }
|
|
356
|
-
.header { display: flex; justify-content: space-between; align-items: center; }
|
|
357
|
-
.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); }`
|
|
358
|
-
}
|
|
359
|
-
})
|
|
218
|
+
// ❌ Wrong // ✅ Right
|
|
219
|
+
app_create({ app_create({
|
|
220
|
+
name: "Landing", name: "Landing",
|
|
221
|
+
html: "<!DOCTYPE...>" // INVALID source_files: {
|
|
222
|
+
}) "src/index.html": "<!DOCTYPE...>",
|
|
223
|
+
"src/main.tsx": "...",
|
|
224
|
+
"src/components/App.tsx": "...",
|
|
225
|
+
"src/styles.css": ""
|
|
226
|
+
}
|
|
227
|
+
})
|
|
360
228
|
```
|
|
361
229
|
|
|
362
|
-
**
|
|
363
|
-
|
|
364
|
-
- No CDN imports - use esbuild-resolved packages from the allowlist above
|
|
365
|
-
- Preact for UI (not React) - `import { render } from 'preact'`
|
|
366
|
-
- TypeScript encouraged for all `.tsx`/`.ts` files
|
|
367
|
-
- No external fonts, images, or resources - use system fonts and CSS/SVG for visuals
|
|
368
|
-
- Design responsively. Apps render at fluid, user-resizable widths — avoid fixed-pixel layouts
|
|
369
|
-
- The WebView blocks all navigation - links and form `action` attributes won't work
|
|
230
|
+
**Key notes:** `preview` — always include, `title` required (plus optional `subtitle`, `description`, `icon`, up to 3 `metrics`). `auto_open` — **always pass `false`** so you don't get a duplicate preview card (Step 5 owns surfacing). `change_summary` — conventional commit message.
|
|
370
231
|
|
|
371
|
-
|
|
232
|
+
### 4 — Compile
|
|
372
233
|
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
**Use `--v-*` variables and `.v-*` classes** - they handle light/dark mode automatically. No manual dark mode CSS needed.
|
|
376
|
-
|
|
377
|
-
Available design tokens:
|
|
378
|
-
|
|
379
|
-
| Category | Tokens |
|
|
380
|
-
| --------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
381
|
-
| **Backgrounds** | `--v-bg`, `--v-surface`, `--v-surface-border` |
|
|
382
|
-
| **Text** | `--v-text`, `--v-text-secondary`, `--v-text-muted` |
|
|
383
|
-
| **Accent** | `--v-accent`, `--v-accent-hover` |
|
|
384
|
-
| **Status** | `--v-success`, `--v-danger`, `--v-warning` |
|
|
385
|
-
| **Spacing** | `--v-spacing-xxs` (2px) / `-xs` (4px) / `-sm` (8px) / `-md` (12px) / `-lg` (16px) / `-xl` (24px) / `-xxl` (32px) / `-xxxl` (48px) |
|
|
386
|
-
| **Radius** | `--v-radius-xs` (2px) / `-sm` (4px) / `-md` (8px) / `-lg` (12px) / `-xl` (16px) / `-pill` (999px) |
|
|
387
|
-
| **Shadows** | `--v-shadow-sm`, `--v-shadow-md`, `--v-shadow-lg` |
|
|
388
|
-
| **Typography** | `--v-font-family`, `--v-font-mono`, `--v-font-size-xs` (10px) / `-sm` (11px) / `-base` (14px) / `-lg` (17px) / `-xl` (22px) / `-2xl` (26px), `--v-line-height` |
|
|
389
|
-
| **Animation** | `--v-duration-fast` (0.15s) / `-standard` (0.25s) / `-slow` (0.4s) |
|
|
390
|
-
| **Palettes** | `--v-slate-{950..50}`, `--v-emerald-*`, `--v-violet-*`, `--v-indigo-*`, `--v-rose-*`, `--v-amber-*` |
|
|
391
|
-
| **Constant** | `--v-aux-white` (always `#FFFFFF` in both modes — use for text on filled/accent backgrounds) |
|
|
392
|
-
|
|
393
|
-
Utility classes: `.v-button` (`.secondary`/`.danger`/`.ghost`), `.v-card`, `.v-list`/`.v-list-item`, `.v-badge` (`.success`/`.warning`/`.danger`), `.v-input-row`, `.v-empty-state`, `.v-toggle`.
|
|
394
|
-
|
|
395
|
-
**Never hardcode `color: white` or `color: #fff`.** Use `var(--v-aux-white)` for text on filled/accent backgrounds, or `var(--v-text)` / `var(--v-text-secondary)` for text on surface backgrounds. Hardcoded white causes invisible text on light surfaces.
|
|
396
|
-
|
|
397
|
-
**Custom themes:** When the user wants a specific branded look, write complete CSS with hardcoded colors and `@media (prefers-color-scheme: dark)` for dark variants. Don't mix `--v-*` auto-switching variables with hardcoded colors in the same element.
|
|
398
|
-
|
|
399
|
-
**Theme detection in JavaScript:**
|
|
400
|
-
|
|
401
|
-
```javascript
|
|
402
|
-
console.log(window.vellum.theme.mode); // 'light' or 'dark'
|
|
403
|
-
window.addEventListener("vellum-theme-change", (e) => {
|
|
404
|
-
console.log("Theme:", e.detail.mode);
|
|
405
|
-
});
|
|
234
|
+
```
|
|
235
|
+
app_refresh(app_id)
|
|
406
236
|
```
|
|
407
237
|
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
A CSS/JS widget library is auto-injected alongside the design system. Use `.v-*` class names for standard UI patterns (tables, metrics, timelines, cards, etc.) and `window.vellum.widgets.*` JS utilities for charts, data formatting, and interactive behaviors. **ALWAYS use `vellum.widgets.*` chart functions** instead of hand-coding SVG/CSS charts.
|
|
411
|
-
|
|
412
|
-
For the full widget reference (class names, JS APIs, chart functions, formatting utilities), see **[Widget Component Library](references/WIDGETS.md)**.
|
|
413
|
-
|
|
414
|
-
#### Data bridge API (deprecated)
|
|
415
|
-
|
|
416
|
-
> **Prefer custom route handlers** for new apps. The data bridge (`window.vellum.data`) only works for assistants that run on the same machine as the desktop app, which will also be deprecated soon.
|
|
417
|
-
|
|
418
|
-
The native WebView can read and write app records via `window.vellum.data`. All methods return Promises.
|
|
419
|
-
|
|
420
|
-
- `window.vellum.data.query()` - Returns all records: `{ id, appId, data, createdAt, updatedAt }[]`
|
|
421
|
-
- `window.vellum.data.create(data)` - Creates a record. Returns the created record.
|
|
422
|
-
- `window.vellum.data.update(recordId, data)` - Updates a record by ID. Returns updated record.
|
|
423
|
-
- `window.vellum.data.delete(recordId)` - Deletes a record by ID. Returns void.
|
|
424
|
-
|
|
425
|
-
Important:
|
|
426
|
-
|
|
427
|
-
- Call `query()` on page load to populate initial state
|
|
428
|
-
- User fields live in `record.data` (e.g., `record.data.title`)
|
|
429
|
-
- Record IDs are UUID strings
|
|
430
|
-
- All operations are async - use `async/await`
|
|
431
|
-
- Wrap all calls in `try/catch`
|
|
432
|
-
|
|
433
|
-
#### Custom route handlers (user-defined routes)
|
|
434
|
-
|
|
435
|
-
When the app needs server-side persistence, custom API logic, or workspace file access, use **user-defined routes**. Route handlers are TypeScript/JavaScript files in the workspace `routes/` directory, served under `/v1/x/`. Call them from the frontend via `window.vellum.fetch("/v1/x/...")`. **Never use raw `fetch()` for `/v1/x/` routes** — it will fail in the sandboxed origin.
|
|
436
|
-
|
|
437
|
-
For handler conventions, examples, key rules, and frontend usage patterns, see **[Custom Route Handlers](references/CUSTOM_ROUTES.md)**.
|
|
238
|
+
Call it ONCE, after ALL file writes — batching is required. If it fails, the response has error details; fix with `file_edit`, then `app_refresh` again.
|
|
438
239
|
|
|
439
|
-
|
|
240
|
+
### 5 — Show the preview card
|
|
440
241
|
|
|
441
|
-
|
|
242
|
+
```
|
|
243
|
+
app_open(app_id, open_mode: "preview")
|
|
244
|
+
```
|
|
442
245
|
|
|
443
|
-
|
|
246
|
+
⚠️ Don't skip this — without it the user has no Open button, just your text. It fires after all writes, so the card shows final content (this is why `auto_open` must be `false`). Don't use `open_mode: "workspace"` unless the user explicitly asks for the full panel.
|
|
444
247
|
|
|
445
|
-
|
|
248
|
+
### 6 — Iteration
|
|
446
249
|
|
|
447
|
-
|
|
448
|
-
- `description`: One-sentence summary
|
|
449
|
-
- `schema_json`: JSON schema as string
|
|
450
|
-
- `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.
|
|
451
|
-
- `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).
|
|
452
|
-
- `preview`: Always include - `title` (required), `subtitle`, `description`, `icon` (image URL preferred, emoji fallback), `metrics` (up to 3 key-value pills)
|
|
250
|
+
Editing an existing app means reusing its `app_id` — never `app_create`. Resolve it from name if needed (see *Resolving an app*), open it so the live result is visible, then:
|
|
453
251
|
|
|
454
|
-
|
|
252
|
+
- **`file_edit`** — targeted changes (styles, fixes, small features), full path `/workspace/data/apps/<slug>/src/...`
|
|
253
|
+
- **`file_write`** — new files or full rewrites
|
|
254
|
+
- **Rename / metadata** — edit `/workspace/data/apps/<slug>.json` directly. Not a new app.
|
|
255
|
+
- **Full rebrand** — still iteration, edit the existing files.
|
|
455
256
|
|
|
456
|
-
|
|
257
|
+
Then `app_refresh(app_id)` ONCE. If the change is substantial, `app_open(app_id, open_mode: "preview")` for a fresh card; for small tweaks the existing card stays valid.
|
|
457
258
|
|
|
458
|
-
|
|
259
|
+
> ⚠️ **`skill_load("app-builder")` is required before every `app_*` call** (including the first `app_create`). The skill can auto-unload between turns; without the reload, `app_refresh` / `app_open` error with "not currently active." It's idempotent — call it every time.
|
|
459
260
|
|
|
460
|
-
|
|
261
|
+
---
|
|
461
262
|
|
|
462
|
-
|
|
463
|
-
- **`file_write`** - for creating new files or full rewrites.
|
|
464
|
-
- **`app_refresh`** - call ONCE after all file changes are complete to trigger compilation and surface refresh.
|
|
465
|
-
- For metadata changes (`name`, `description`, `schemaJson`, etc.), edit the `<slug>.json` file directly with `file_edit`, then call `app_refresh`.
|
|
263
|
+
## Using your assistant's tools and data
|
|
466
264
|
|
|
467
|
-
|
|
265
|
+
The point of these apps is to put **the user's own data and the assistant's capabilities** behind a real interface. Apps reach the assistant backend through custom routes.
|
|
468
266
|
|
|
469
|
-
|
|
267
|
+
**Call routes with `window.vellum.fetch("/v1/x/...")` — never raw `fetch()`.** Raw fetch fails in the sandboxed origin. This is how an app reads and writes persistent records, runs server-side logic, and touches files.
|
|
470
268
|
|
|
471
|
-
|
|
269
|
+
```tsx
|
|
270
|
+
async function loadRecords() {
|
|
271
|
+
const res = await window.vellum.fetch("/v1/x/my-route");
|
|
272
|
+
if (!res.ok) { window.vellum.widgets.toast("Couldn't load", "error"); return []; }
|
|
273
|
+
return res.json();
|
|
274
|
+
}
|
|
275
|
+
```
|
|
472
276
|
|
|
473
|
-
|
|
277
|
+
Always wrap calls in `try/catch`, check `res.ok` before parsing, and surface failures with a toast or inline error — never fail silently:
|
|
474
278
|
|
|
279
|
+
```tsx
|
|
280
|
+
useEffect(() => {
|
|
281
|
+
window.vellum.fetch("/v1/x/items")
|
|
282
|
+
.then(res => res.ok ? res.json() : Promise.reject(res.status))
|
|
283
|
+
.then(setItems)
|
|
284
|
+
.catch(() => window.vellum.widgets.toast("Couldn't load", "error"));
|
|
285
|
+
}, []);
|
|
475
286
|
```
|
|
476
|
-
|
|
287
|
+
|
|
288
|
+
**Writing a route handler.** Routes are `.ts`/`.js` files in `{workspaceDir}/routes/`, served at `/v1/x/<filename>` (`routes/items.ts` → `/v1/x/items`; `routes/bar/index.ts` → `/v1/x/bar`). Write them with `file_write` **before** `app_refresh`. Each exports named functions per HTTP method (`GET`/`POST`/`PUT`/`PATCH`/`DELETE`), receiving the Web `Request` and an optional `context`. Full Node API access (`fs`, `path`, `crypto`), 30s timeout, hot-reloaded on change. No `[id].ts` dynamic segments — use query params.
|
|
289
|
+
|
|
290
|
+
```typescript
|
|
291
|
+
// routes/items.ts
|
|
292
|
+
import { readFileSync, writeFileSync, mkdirSync, existsSync } from "node:fs";
|
|
293
|
+
import { join } from "node:path";
|
|
294
|
+
export const description = "Item CRUD — JSON file storage"; // optional, for `assistant routes list`
|
|
295
|
+
const FILE = join(process.env.VELLUM_WORKSPACE_DIR!, "data", "items.json");
|
|
296
|
+
const load = () => existsSync(FILE) ? JSON.parse(readFileSync(FILE, "utf-8")) : [];
|
|
297
|
+
const save = (x:unknown[]) => { mkdirSync(join(process.env.VELLUM_WORKSPACE_DIR!,"data"),{recursive:true}); writeFileSync(FILE, JSON.stringify(x,null,2)); };
|
|
298
|
+
|
|
299
|
+
export function GET(): Response { return Response.json(load()); }
|
|
300
|
+
export async function POST(req: Request): Promise<Response> {
|
|
301
|
+
const item = { id: crypto.randomUUID(), ...(await req.json()), createdAt: new Date().toISOString() };
|
|
302
|
+
const items = load(); items.push(item); save(items);
|
|
303
|
+
return Response.json(item, { status: 201 });
|
|
304
|
+
}
|
|
477
305
|
```
|
|
478
306
|
|
|
479
|
-
|
|
307
|
+
The optional `context` arg exposes daemon singletons — e.g. `context.assistantEventHub.publish({...})` to push real-time events to connected clients (UI updates, navigation, notifications). It's immutable. Full guide + copyable examples (Focus Timer, Habit Tracker, Expense Tracker): `{baseDir}/references/CUSTOM_ROUTES.md`, `{baseDir}/references/examples/`.
|
|
308
|
+
|
|
309
|
+
**Persistence options:** `localStorage` for ephemeral UI state (filters, view modes, drafts); custom routes for persistent records and server-side logic. (`window.vellum.data.*` is deprecated — only for editing pre-existing legacy apps.)
|
|
480
310
|
|
|
481
|
-
|
|
311
|
+
---
|
|
482
312
|
|
|
483
|
-
|
|
313
|
+
## Interaction standards
|
|
484
314
|
|
|
485
|
-
- **Feedback for every action
|
|
486
|
-
- **
|
|
487
|
-
- **
|
|
488
|
-
- **Loading states
|
|
489
|
-
- **
|
|
315
|
+
- **Feedback for every action** — `vellum.widgets.toast()` after creates, deletes, updates, errors.
|
|
316
|
+
- **Confirm destructive actions** — `window.vellum.confirm(title, message)` (returns `Promise<boolean>`) before deleting or resetting.
|
|
317
|
+
- **Validate forms** before submit, show errors inline, disable submit during async.
|
|
318
|
+
- **Loading states** — skeleton or spinner, never a blank screen.
|
|
319
|
+
- **Designed empty states** — `.v-empty-state` when there's no data.
|
|
490
320
|
|
|
491
|
-
|
|
321
|
+
### Keep the assistant aware
|
|
492
322
|
|
|
493
|
-
|
|
323
|
+
Wire `window.vellum.sendAction()` during the build so the assistant sees meaningful interactions. **Reactive** hooks trigger a response (form submissions, selections worth explaining); **silent** hooks (`state_update`) accumulate context without interrupting (tab changes, filter changes). Examples in `{baseDir}/references/INTERACTION_HOOKS.md`.
|
|
494
324
|
|
|
495
|
-
|
|
325
|
+
### Actionable UI & links
|
|
496
326
|
|
|
497
|
-
-
|
|
498
|
-
- Layout variety - 3+ different types per deck, never consecutive same-type
|
|
499
|
-
- 8 layout types: Title, Stats, Bullets, Quote, Comparison, Timeline, Visual/Immersive, Closing/CTA
|
|
500
|
-
- Bold backgrounds - dark, gradient, or strongly tinted
|
|
501
|
-
- Max 6 bullets per slide, max 3 sentences body text
|
|
502
|
-
- Never go below 15px for any visible text
|
|
327
|
+
For triage/bulk-action UIs: render a `dynamic_page` with selectable items + action buttons → user selects and clicks → UI sends `surfaceAction` with action ID + selected IDs → execute tools, `ui_update`, toast. Use `window.vellum.confirm()` for destructive actions. Make items clickable with `vellum.openLink(url, metadata)` (include `metadata.provider` and `metadata.type`).
|
|
503
328
|
|
|
504
|
-
|
|
329
|
+
---
|
|
505
330
|
|
|
506
|
-
|
|
507
|
-
- Never let a failed operation silently pass - always show a toast or inline error.
|
|
508
|
-
- If the page loads with no data, show a designed empty state (`.v-empty-state`).
|
|
509
|
-
- For forms, show validation errors inline next to the relevant field.
|
|
331
|
+
## Slides
|
|
510
332
|
|
|
511
|
-
|
|
333
|
+
Slide decks are a different domain — skip app patterns (contextual headers, search/filter, toasts, form validation, custom routes). Build navigation and layouts with custom HTML/CSS. Templates and principles in `{baseDir}/references/SLIDES.md`.
|
|
512
334
|
|
|
513
|
-
|
|
335
|
+
---
|
|
514
336
|
|
|
515
|
-
|
|
337
|
+
## SKILL COMPLETE WHEN
|
|
516
338
|
|
|
517
|
-
|
|
339
|
+
- [ ] Request was scoped: personal build (sandbox) or complex/shippable (handed off to a project folder + coding agent)
|
|
340
|
+
- [ ] **Sandbox path:** `app_create` returned an `app_id`; all files written via `file_write`; `app_refresh` ran ONCE clean; `app_open(open_mode: "preview")` rendered the card; user told what was built (3-6 bullets); iterations reflected live
|
|
341
|
+
- [ ] **Handoff path:** project folder established; coding agent spawned via `acp_spawn({ task, cwd })`; user told work continues in the folder
|
|
518
342
|
|
|
519
|
-
|
|
343
|
+
---
|
|
520
344
|
|
|
521
|
-
|
|
522
|
-
2. Render a `dynamic_page` with selectable items and action buttons
|
|
523
|
-
3. User selects + clicks action - UI sends `surfaceAction` with action ID and selected IDs
|
|
524
|
-
4. Execute tools, update UI with `ui_update`, show feedback via `widgets.toast()`
|
|
525
|
-
5. Use `window.vellum.confirm()` for destructive actions
|
|
345
|
+
## Reference files
|
|
526
346
|
|
|
527
|
-
|
|
347
|
+
Read with `file_read` using the `{baseDir}/references/...` paths (`{baseDir}` resolves to this skill's directory):
|
|
528
348
|
|
|
529
|
-
|
|
349
|
+
- `RESPONSIVE.md` — mobile vs desktop, universal baseline, safe areas
|
|
350
|
+
- `DESIGN_SYSTEM.md` — token table, utility classes, theme detection
|
|
351
|
+
- `WIDGETS.md` — widget classes, chart utilities, formatting helpers
|
|
352
|
+
- `CUSTOM_ROUTES.md` — server-side persistence and custom API routes
|
|
353
|
+
- `examples/` — complete copyable example apps
|
|
354
|
+
- `INTERACTION_HOOKS.md` — sendAction patterns, reactive vs silent
|
|
355
|
+
- `SLIDES.md` — presentation slide design
|