@vellumai/assistant 0.6.0 → 0.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +4 -0
- package/ARCHITECTURE.md +68 -15
- package/Dockerfile +2 -2
- package/bun.lock +6 -2
- package/docker-entrypoint.sh +42 -1
- package/docs/architecture/integrations.md +1 -1
- package/docs/architecture/memory.md +21 -24
- package/node_modules/@vellumai/ces-contracts/src/handles.ts +7 -9
- package/openapi.yaml +539 -4
- package/package.json +5 -1
- package/src/__tests__/anthropic-provider.test.ts +160 -95
- package/src/__tests__/app-dir-path-guard.test.ts +1 -0
- package/src/__tests__/app-executors.test.ts +47 -1
- package/src/__tests__/app-source-watcher.test.ts +159 -0
- package/src/__tests__/assistant-event-hub.test.ts +30 -0
- package/src/__tests__/checker.test.ts +138 -172
- package/src/__tests__/cli-command-risk-guard.test.ts +1 -1
- package/src/__tests__/config-schema.test.ts +5 -0
- package/src/__tests__/context-overflow-approval.test.ts +5 -5
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +4 -6
- package/src/__tests__/conversation-agent-loop.test.ts +4 -51
- package/src/__tests__/conversation-analysis-routes.test.ts +169 -0
- package/src/__tests__/conversation-directories-parse.test.ts +105 -0
- package/src/__tests__/conversation-history-web-search.test.ts +1 -1
- package/src/__tests__/conversation-runtime-assembly.test.ts +653 -832
- package/src/__tests__/conversation-runtime-workspace.test.ts +1 -93
- package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +17 -4
- package/src/__tests__/conversation-wipe.test.ts +2 -6
- package/src/__tests__/conversation-workspace-cache-state.test.ts +6 -12
- package/src/__tests__/conversation-workspace-injection.test.ts +25 -26
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +1 -1
- package/src/__tests__/copy-composer-tc-templates.test.ts +335 -0
- package/src/__tests__/credential-execution-approval-bridge.test.ts +0 -2
- package/src/__tests__/date-context.test.ts +76 -210
- package/src/__tests__/db-schedule-syntax-migration.test.ts +16 -1
- package/src/__tests__/file-list-tool.test.ts +219 -0
- package/src/__tests__/first-greeting.test.ts +1 -1
- package/src/__tests__/heartbeat-service.test.ts +180 -3
- package/src/__tests__/identity-routes.test.ts +328 -0
- package/src/__tests__/init-feature-flag-overrides.test.ts +167 -0
- package/src/__tests__/injection-block.test.ts +24 -0
- package/src/__tests__/inline-command-runner.test.ts +7 -5
- package/src/__tests__/install-skill-routing.test.ts +7 -6
- package/src/__tests__/jobs-store-qdrant-breaker.test.ts +15 -14
- package/src/__tests__/list-messages-tool-merge.test.ts +300 -0
- package/src/__tests__/llm-context-normalization.test.ts +18 -18
- package/src/__tests__/llm-context-route-provider.test.ts +101 -0
- package/src/__tests__/llm-request-log-turn-query.test.ts +162 -0
- package/src/__tests__/log-export-workspace.test.ts +257 -100
- package/src/__tests__/managed-credential-catalog-cli.test.ts +12 -14
- package/src/__tests__/mcp-abort-signal.test.ts +5 -0
- package/src/__tests__/mcp-client-auth.test.ts +5 -0
- package/src/__tests__/memory-recall-log-store.test.ts +132 -0
- package/src/__tests__/migration-export-streaming.test.ts +304 -0
- package/src/__tests__/migration-import-commit-http.test.ts +11 -10
- package/src/__tests__/mock-fetch.ts +87 -0
- package/src/__tests__/navigate-settings-tab.test.ts +14 -1
- package/src/__tests__/notification-broadcaster.test.ts +65 -0
- package/src/__tests__/notification-decision-recipient-context.test.ts +282 -0
- package/src/__tests__/onboarding-template-contract.test.ts +63 -14
- package/src/__tests__/parser.test.ts +32 -0
- package/src/__tests__/permission-checker-host-gate.test.ts +452 -0
- package/src/__tests__/permission-controls-v2-flag.test.ts +55 -0
- package/src/__tests__/permission-mode-sse.test.ts +418 -0
- package/src/__tests__/permission-mode-store.test.ts +277 -0
- package/src/__tests__/permission-mode.test.ts +101 -0
- package/src/__tests__/pkb-autoinject.test.ts +96 -0
- package/src/__tests__/platform-bash-auto-approve.test.ts +359 -0
- package/src/__tests__/profiler-routes.test.ts +502 -0
- package/src/__tests__/profiler-run-store.test.ts +441 -0
- package/src/__tests__/proxy-approval-callback.test.ts +4 -75
- package/src/__tests__/registry.test.ts +1 -1
- package/src/__tests__/require-fresh-approval.test.ts +0 -2
- package/src/__tests__/sandbox-diagnostics.test.ts +1 -32
- package/src/__tests__/sandbox-host-parity.test.ts +5 -4
- package/src/__tests__/scheduler-reuse-conversation.test.ts +368 -0
- package/src/__tests__/scrub-corrupted-image-attachments.test.ts +278 -0
- package/src/__tests__/search-skills-unified.test.ts +4 -3
- package/src/__tests__/send-endpoint-busy.test.ts +42 -3
- package/src/__tests__/set-permission-mode.test.ts +274 -0
- package/src/__tests__/skill-load-feature-flag.test.ts +12 -0
- package/src/__tests__/skill-memory.test.ts +2 -783
- package/src/__tests__/strip-memory-injections.test.ts +187 -0
- package/src/__tests__/subagent-detail.test.ts +84 -0
- package/src/__tests__/subagent-disposal.test.ts +308 -0
- package/src/__tests__/subagent-manager-notify.test.ts +19 -10
- package/src/__tests__/subagent-notify-parent.test.ts +390 -0
- package/src/__tests__/subagent-role-registry.test.ts +108 -0
- package/src/__tests__/subagent-tool-filtering.test.ts +71 -0
- package/src/__tests__/subagent-tools.test.ts +464 -4
- package/src/__tests__/system-prompt-ask-mode.test.ts +139 -0
- package/src/__tests__/task-memory-cleanup.test.ts +12 -12
- package/src/__tests__/terminal-sandbox.test.ts +1 -1
- package/src/__tests__/terminal-tools.test.ts +16 -29
- package/src/__tests__/test-preload.ts +18 -0
- package/src/__tests__/tool-domain-event-publisher.test.ts +0 -1
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +1 -8
- package/src/__tests__/tool-executor.test.ts +4 -27
- package/src/__tests__/tool-side-effects-slack-dm.test.ts +1 -0
- package/src/__tests__/top-level-renderer.test.ts +10 -13
- package/src/__tests__/transport-hints-queue.test.ts +77 -0
- package/src/__tests__/trust-store.test.ts +4 -4
- package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +116 -2
- package/src/__tests__/workspace-migration-028-recover-conversations-from-disk-view.test.ts +387 -0
- package/src/__tests__/workspace-migration-030-seed-pkb-autoinject.test.ts +168 -0
- package/src/__tests__/workspace-policy.test.ts +2 -7
- package/src/agent/loop.ts +6 -29
- package/src/approvals/guardian-request-resolvers.ts +24 -0
- package/src/avatar/traits-png-sync.ts +3 -3
- package/src/channels/types.ts +5 -0
- package/src/cli/__tests__/run-assistant-command.ts +56 -0
- package/src/cli/__tests__/unknown-command.test.ts +33 -0
- package/src/cli/commands/__tests__/email-download.test.ts +245 -0
- package/src/cli/commands/__tests__/email-list.test.ts +192 -0
- package/src/cli/commands/__tests__/email-register.test.ts +186 -0
- package/src/cli/commands/__tests__/email-send.test.ts +291 -0
- package/src/cli/commands/__tests__/email-status.test.ts +181 -0
- package/src/cli/commands/__tests__/email-unregister.test.ts +139 -0
- package/src/cli/commands/__tests__/routes.test.ts +562 -0
- package/src/cli/commands/conversations.ts +1 -8
- package/src/cli/commands/default-action.ts +68 -1
- package/src/cli/commands/email.ts +584 -835
- package/src/cli/commands/memory.ts +1 -34
- package/src/cli/commands/notifications.ts +7 -2
- package/src/cli/commands/oauth/__tests__/connect.test.ts +27 -0
- package/src/cli/commands/oauth/connect.ts +25 -5
- package/src/cli/commands/platform/__tests__/connect.test.ts +1 -1
- package/src/cli/commands/platform/__tests__/disconnect.test.ts +1 -1
- package/src/cli/commands/platform/__tests__/status.test.ts +1 -1
- package/src/cli/commands/routes.ts +396 -0
- package/src/cli/commands/skills.ts +130 -20
- package/src/cli/program.ts +11 -2
- package/src/cli.ts +1 -120
- package/src/config/assistant-feature-flags.ts +59 -55
- package/src/config/bundled-skills/app-builder/SKILL.md +91 -5
- package/src/config/bundled-skills/gmail/SKILL.md +13 -8
- package/src/config/bundled-skills/gmail/TOOLS.json +1 -1
- package/src/config/bundled-skills/gmail/tools/gmail-sender-digest.ts +2 -1
- package/src/config/bundled-skills/messaging/SKILL.md +7 -0
- package/src/config/bundled-skills/schedule/SKILL.md +22 -2
- package/src/config/bundled-skills/schedule/TOOLS.json +8 -0
- package/src/config/bundled-skills/settings/TOOLS.json +1 -1
- package/src/config/bundled-skills/settings/tools/avatar-get.ts +3 -13
- package/src/config/bundled-skills/settings/tools/avatar-remove.ts +2 -4
- package/src/config/bundled-skills/settings/tools/avatar-update.ts +5 -2
- package/src/config/bundled-skills/settings/tools/navigate-settings-tab.ts +8 -3
- package/src/config/bundled-skills/slack/SKILL.md +2 -0
- package/src/config/bundled-skills/subagent/SKILL.md +43 -3
- package/src/config/bundled-skills/subagent/TOOLS.json +29 -4
- package/src/config/env-registry.ts +63 -0
- package/src/config/feature-flag-registry.json +17 -1
- package/src/config/schema.ts +8 -0
- package/src/config/schemas/filing.ts +51 -0
- package/src/config/schemas/heartbeat.ts +15 -12
- package/src/config/schemas/memory-lifecycle.ts +12 -0
- package/src/config/schemas/security.ts +14 -0
- package/src/config/schemas/services.ts +8 -0
- package/src/credential-execution/approval-bridge.ts +0 -1
- package/src/credential-execution/managed-catalog.ts +3 -7
- package/src/daemon/app-source-watcher.ts +93 -0
- package/src/daemon/config-watcher.ts +85 -3
- package/src/daemon/context-overflow-approval.ts +0 -1
- package/src/daemon/conversation-agent-loop-handlers.ts +20 -0
- package/src/daemon/conversation-agent-loop.ts +179 -65
- package/src/daemon/conversation-attachments.ts +0 -1
- package/src/daemon/conversation-history.ts +4 -19
- package/src/daemon/conversation-lifecycle.ts +8 -14
- package/src/daemon/conversation-messaging.ts +3 -0
- package/src/daemon/conversation-process.ts +30 -8
- package/src/daemon/conversation-queue-manager.ts +8 -0
- package/src/daemon/conversation-runtime-assembly.ts +359 -308
- package/src/daemon/conversation-surfaces.ts +65 -0
- package/src/daemon/conversation-tool-setup.ts +44 -17
- package/src/daemon/conversation-workspace.ts +1 -2
- package/src/daemon/conversation.ts +19 -3
- package/src/daemon/date-context.ts +26 -53
- package/src/daemon/first-greeting.ts +1 -1
- package/src/daemon/handlers/conversations.ts +5 -7
- package/src/daemon/handlers/shared.test.ts +143 -0
- package/src/daemon/handlers/shared.ts +70 -5
- package/src/daemon/handlers/skills.ts +11 -18
- package/src/daemon/lifecycle.ts +220 -158
- package/src/daemon/message-types/conversations.ts +29 -6
- package/src/daemon/message-types/messages.ts +9 -2
- package/src/daemon/message-types/notifications.ts +12 -0
- package/src/daemon/message-types/schedules.ts +1 -0
- package/src/daemon/message-types/settings.ts +18 -0
- package/src/daemon/profiler-run-store.ts +557 -0
- package/src/daemon/server.ts +87 -10
- package/src/daemon/shutdown-handlers.ts +5 -0
- package/src/daemon/tool-side-effects.ts +23 -3
- package/src/daemon/transport-hints.ts +33 -0
- package/src/export/transcript-formatter.ts +148 -0
- package/src/filing/filing-service.ts +228 -0
- package/src/heartbeat/heartbeat-service.ts +96 -7
- package/src/index.ts +1 -1
- package/src/mcp/client.ts +6 -0
- package/src/mcp/mcp-oauth-provider.ts +149 -27
- package/src/memory/admin.ts +33 -32
- package/src/memory/app-store.ts +69 -0
- package/src/memory/conversation-bootstrap.ts +1 -1
- package/src/memory/conversation-crud.ts +151 -117
- package/src/memory/conversation-directories.ts +39 -0
- package/src/memory/conversation-group-migration.ts +66 -6
- package/src/memory/conversation-queries.ts +58 -12
- package/src/memory/conversation-title-service.ts +1 -0
- package/src/memory/db-init.ts +182 -376
- package/src/memory/embedding-local.ts +1 -1
- package/src/memory/graph/bootstrap.ts +75 -66
- package/src/memory/graph/capability-seed.ts +167 -17
- package/src/memory/graph/consolidation.ts +38 -4
- package/src/memory/graph/conversation-graph-memory.ts +133 -104
- package/src/memory/graph/extraction-job.ts +9 -4
- package/src/memory/graph/extraction.ts +66 -23
- package/src/memory/graph/graph-memory-state-store.ts +37 -0
- package/src/memory/graph/graph-search.ts +29 -15
- package/src/memory/graph/injection.ts +38 -8
- package/src/memory/graph/inspect.ts +12 -3
- package/src/memory/graph/retriever.ts +365 -262
- package/src/memory/graph/store.test.ts +48 -0
- package/src/memory/graph/store.ts +150 -11
- package/src/memory/graph/tool-handlers.ts +84 -209
- package/src/memory/graph/tools.ts +8 -52
- package/src/memory/graph/types.ts +24 -0
- package/src/memory/group-crud.ts +25 -9
- package/src/memory/job-handlers/cleanup.ts +44 -1
- package/src/memory/jobs-store.ts +70 -60
- package/src/memory/jobs-worker.ts +44 -28
- package/src/memory/llm-request-log-store.ts +96 -12
- package/src/memory/memory-recall-log-store.ts +49 -5
- package/src/memory/migrations/203-drop-memory-items-tables.ts +33 -1
- package/src/memory/migrations/206-memory-graph-node-edits.ts +19 -0
- package/src/memory/migrations/206-scrub-corrupted-image-attachments.ts +131 -0
- package/src/memory/migrations/207-conversation-graph-memory-state.ts +20 -0
- package/src/memory/migrations/208-conversations-last-message-at.ts +35 -0
- package/src/memory/migrations/209-strip-thinking-from-consolidated.ts +85 -0
- package/src/memory/migrations/210-schedule-reuse-conversation.ts +13 -0
- package/src/memory/migrations/211-memory-recall-logs-query-context.ts +21 -0
- package/src/memory/migrations/212-llm-request-logs-created-at-index.ts +19 -0
- package/src/memory/migrations/index.ts +8 -0
- package/src/memory/migrations/registry.ts +8 -0
- package/src/memory/schema/conversations.ts +14 -0
- package/src/memory/schema/infrastructure.ts +8 -1
- package/src/memory/schema/memory-core.ts +0 -51
- package/src/memory/schema/memory-graph.ts +15 -0
- package/src/memory/task-memory-cleanup.ts +30 -11
- package/src/messaging/provider.ts +1 -1
- package/src/notifications/broadcaster.ts +6 -0
- package/src/notifications/conversation-pairing.ts +12 -4
- package/src/notifications/copy-composer.ts +86 -0
- package/src/notifications/decision-engine.ts +35 -0
- package/src/notifications/emit-signal.ts +14 -0
- package/src/notifications/signal.ts +11 -0
- package/src/oauth/platform-connection.test.ts +2 -2
- package/src/oauth/seed-providers.ts +1 -0
- package/src/permissions/checker.ts +15 -4
- package/src/permissions/defaults.ts +7 -8
- package/src/permissions/permission-mode-store.ts +180 -0
- package/src/permissions/permission-mode.ts +31 -0
- package/src/permissions/prompter.ts +0 -2
- package/src/permissions/workspace-policy.ts +9 -0
- package/src/platform/client.ts +1 -1
- package/src/prompts/system-prompt.ts +59 -7
- package/src/prompts/templates/BOOTSTRAP-REFERENCE.md +100 -0
- package/src/prompts/templates/BOOTSTRAP.md +76 -162
- package/src/prompts/templates/HEARTBEAT.md +3 -1
- package/src/prompts/templates/SOUL.md +30 -9
- package/src/prompts/templates/UPDATES.md +8 -0
- package/src/providers/anthropic/client.ts +107 -219
- package/src/runtime/assistant-event-hub.ts +22 -0
- package/src/runtime/auth/route-policy.ts +23 -0
- package/src/runtime/auth/token-service.ts +8 -0
- package/src/runtime/http-server.ts +32 -2
- package/src/runtime/http-types.ts +12 -1
- package/src/runtime/migrations/vbundle-builder.ts +389 -3
- package/src/runtime/migrations/vbundle-importer.ts +8 -6
- package/src/runtime/routes/__tests__/user-route-dispatcher.test.ts +378 -0
- package/src/runtime/routes/app-management-routes.ts +1 -11
- package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +26 -0
- package/src/runtime/routes/archive-utils.ts +29 -0
- package/src/runtime/routes/avatar-routes.ts +2 -9
- package/src/runtime/routes/btw-routes.ts +14 -1
- package/src/runtime/routes/conversation-analysis-routes.ts +185 -0
- package/src/runtime/routes/conversation-management-routes.ts +1 -14
- package/src/runtime/routes/conversation-query-routes.ts +49 -3
- package/src/runtime/routes/conversation-routes.ts +270 -44
- package/src/runtime/routes/group-routes.ts +22 -8
- package/src/runtime/routes/heartbeat-routes.ts +4 -10
- package/src/runtime/routes/identity-routes.ts +53 -18
- package/src/runtime/routes/llm-context-normalization.ts +14 -10
- package/src/runtime/routes/log-export/AGENTS.md +104 -0
- package/src/runtime/routes/log-export/__tests__/workspace-allowlist-error-contract.test.ts +103 -0
- package/src/runtime/routes/log-export/__tests__/workspace-allowlist.test.ts +716 -0
- package/src/runtime/routes/log-export/workspace-allowlist.ts +458 -0
- package/src/runtime/routes/log-export-routes.ts +41 -278
- package/src/runtime/routes/memory-item-routes.test.ts +168 -233
- package/src/runtime/routes/migration-routes.ts +18 -7
- package/src/runtime/routes/profiler-routes.ts +350 -0
- package/src/runtime/routes/schedule-routes.ts +27 -12
- package/src/runtime/routes/settings-routes.ts +95 -8
- package/src/runtime/routes/subagents-routes.ts +28 -7
- package/src/runtime/routes/user-route-dispatcher.ts +223 -0
- package/src/runtime/routes/user-routes.ts +41 -0
- package/src/runtime/routes/workspace-routes.ts +0 -1
- package/src/schedule/schedule-store.ts +30 -0
- package/src/schedule/scheduler.ts +45 -18
- package/src/skills/catalog-install.ts +10 -2
- package/src/skills/inline-command-runner.ts +12 -14
- package/src/skills/managed-store.ts +2 -2
- package/src/skills/skill-memory.ts +1 -293
- package/src/subagent/index.ts +13 -3
- package/src/subagent/manager.ts +308 -29
- package/src/subagent/types.ts +68 -0
- package/src/tasks/task-runner.ts +4 -4
- package/src/tools/apps/executors.ts +29 -4
- package/src/tools/filesystem/list.ts +93 -0
- package/src/tools/permission-checker.ts +78 -18
- package/src/tools/registry.ts +4 -0
- package/src/tools/schedule/create.ts +3 -0
- package/src/tools/schedule/list.ts +1 -0
- package/src/tools/schedule/update.ts +6 -0
- package/src/tools/secret-detection-handler.ts +0 -1
- package/src/tools/shared/filesystem/errors.ts +5 -0
- package/src/tools/shared/filesystem/file-ops-service.ts +90 -2
- package/src/tools/shared/filesystem/types.ts +17 -0
- package/src/tools/shared/shell-output.ts +31 -2
- package/src/tools/skills/sandbox-runner.ts +3 -6
- package/src/tools/subagent/abort.ts +12 -2
- package/src/tools/subagent/message.ts +9 -2
- package/src/tools/subagent/notify-parent.ts +79 -0
- package/src/tools/subagent/read.ts +29 -8
- package/src/tools/subagent/resolve.ts +21 -0
- package/src/tools/subagent/spawn.ts +2 -0
- package/src/tools/subagent/status.ts +11 -1
- package/src/tools/system/avatar-generator.ts +3 -3
- package/src/tools/system/register.ts +23 -0
- package/src/tools/system/set-permission-mode.ts +103 -0
- package/src/tools/terminal/parser.ts +30 -5
- package/src/tools/terminal/safe-env.ts +16 -1
- package/src/tools/terminal/sandbox-diagnostics.ts +4 -4
- package/src/tools/terminal/sandbox.ts +4 -1
- package/src/tools/terminal/shell.ts +3 -5
- package/src/tools/tool-manifest.ts +6 -0
- package/src/tools/types.ts +2 -3
- package/src/util/logger.ts +1 -1
- package/src/util/platform.ts +50 -17
- package/src/watcher/provider-types.ts +1 -1
- package/src/workspace/migrations/023-move-config-files-to-workspace.ts +2 -2
- package/src/workspace/migrations/024-move-runtime-files-to-workspace.ts +2 -2
- package/src/workspace/migrations/028-recover-conversations-from-disk-view.ts +270 -0
- package/src/workspace/migrations/029-seed-pkb.ts +85 -0
- package/src/workspace/migrations/030-seed-pkb-autoinject.ts +73 -0
- package/src/workspace/migrations/registry.ts +6 -0
- package/src/workspace/top-level-renderer.ts +5 -9
- package/src/__tests__/cli-memory.test.ts +0 -377
- package/src/__tests__/clipboard.test.ts +0 -88
- package/src/cli/cli-memory.ts +0 -179
- package/src/util/clipboard.ts +0 -34
|
@@ -238,7 +238,7 @@ export const Header: FunctionComponent<HeaderProps> = ({ title, count }) => (
|
|
|
238
238
|
|
|
239
239
|
file_write("{workspaceDir}/data/apps/project-tracker/src/styles.css", `.app { padding: var(--v-spacing-lg); }
|
|
240
240
|
.header { display: flex; justify-content: space-between; align-items: center; }
|
|
241
|
-
.badge { background: var(--v-accent); color: white; padding: var(--v-spacing-xs) var(--v-spacing-sm); border-radius: var(--v-radius-pill); }`)
|
|
241
|
+
.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); }`)
|
|
242
242
|
|
|
243
243
|
# After all files are written, compile and refresh:
|
|
244
244
|
app_refresh(app_id)
|
|
@@ -290,9 +290,12 @@ Available design tokens:
|
|
|
290
290
|
| **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` |
|
|
291
291
|
| **Animation** | `--v-duration-fast` (0.15s) / `-standard` (0.25s) / `-slow` (0.4s) |
|
|
292
292
|
| **Palettes** | `--v-slate-{950..50}`, `--v-emerald-*`, `--v-violet-*`, `--v-indigo-*`, `--v-rose-*`, `--v-amber-*` |
|
|
293
|
+
| **Constant** | `--v-aux-white` (always `#FFFFFF` in both modes — use for text on filled/accent backgrounds) |
|
|
293
294
|
|
|
294
295
|
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`.
|
|
295
296
|
|
|
297
|
+
**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.
|
|
298
|
+
|
|
296
299
|
**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.
|
|
297
300
|
|
|
298
301
|
**Theme detection in JavaScript:**
|
|
@@ -445,9 +448,91 @@ Important:
|
|
|
445
448
|
- All operations are async - use `async/await`
|
|
446
449
|
- Wrap all calls in `try/catch`
|
|
447
450
|
|
|
451
|
+
#### Custom route handlers (user-defined routes)
|
|
452
|
+
|
|
453
|
+
When the app needs server-side persistence, custom API logic, or workspace file access, use **user-defined routes**. Route handlers are TypeScript or JavaScript files that live in the workspace `routes/` directory and are served under the `/v1/x/` URL path.
|
|
454
|
+
|
|
455
|
+
**Common use cases:** CRUD storage, file-based persistence, search/aggregation, external API proxying, webhook receivers.
|
|
456
|
+
|
|
457
|
+
**Handler file convention:**
|
|
458
|
+
|
|
459
|
+
Each handler file exports named functions for the HTTP methods it supports (`GET`, `POST`, `PUT`, `PATCH`, `DELETE`). Handlers use the standard Web API `Request`/`Response` signature.
|
|
460
|
+
|
|
461
|
+
```
|
|
462
|
+
{workspaceDir}/routes/
|
|
463
|
+
items.ts # Handles /v1/x/items
|
|
464
|
+
items/
|
|
465
|
+
[id].ts # Not supported — use query params instead
|
|
466
|
+
index.ts # Also handles /v1/x/items (index convention)
|
|
467
|
+
```
|
|
468
|
+
|
|
469
|
+
**Example handler — JSON file persistence:**
|
|
470
|
+
|
|
471
|
+
```typescript
|
|
472
|
+
// routes/items.ts
|
|
473
|
+
import { readFileSync, writeFileSync, mkdirSync, existsSync } from "node:fs";
|
|
474
|
+
import { join } from "node:path";
|
|
475
|
+
|
|
476
|
+
export const description = "Item CRUD — stores records as a JSON file";
|
|
477
|
+
|
|
478
|
+
const DATA_DIR = join(process.env.VELLUM_WORKSPACE_DIR!, "data");
|
|
479
|
+
const DATA_FILE = join(DATA_DIR, "items.json");
|
|
480
|
+
|
|
481
|
+
function loadItems(): Array<Record<string, unknown>> {
|
|
482
|
+
mkdirSync(DATA_DIR, { recursive: true });
|
|
483
|
+
if (!existsSync(DATA_FILE)) return [];
|
|
484
|
+
return JSON.parse(readFileSync(DATA_FILE, "utf-8"));
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
function saveItems(items: Array<Record<string, unknown>>): void {
|
|
488
|
+
mkdirSync(DATA_DIR, { recursive: true });
|
|
489
|
+
writeFileSync(DATA_FILE, JSON.stringify(items, null, 2));
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
export function GET(): Response {
|
|
493
|
+
return Response.json(loadItems());
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
export async function POST(request: Request): Promise<Response> {
|
|
497
|
+
const body = await request.json();
|
|
498
|
+
const items = loadItems();
|
|
499
|
+
const item = { id: crypto.randomUUID(), ...body, createdAt: new Date().toISOString() };
|
|
500
|
+
items.push(item);
|
|
501
|
+
saveItems(items);
|
|
502
|
+
return Response.json(item, { status: 201 });
|
|
503
|
+
}
|
|
504
|
+
```
|
|
505
|
+
|
|
506
|
+
**Calling routes from the app frontend:**
|
|
507
|
+
|
|
508
|
+
Apps call custom routes via `fetch()` using the `/v1/x/` prefix. The assistant's runtime HTTP server requires the `/v1/` namespace for all API requests.
|
|
509
|
+
|
|
510
|
+
```typescript
|
|
511
|
+
// In a TSX component or HTML script
|
|
512
|
+
const res = await fetch("/v1/x/items");
|
|
513
|
+
const items = await res.json();
|
|
514
|
+
|
|
515
|
+
// Create a new item
|
|
516
|
+
await fetch("/v1/x/items", {
|
|
517
|
+
method: "POST",
|
|
518
|
+
headers: { "Content-Type": "application/json" },
|
|
519
|
+
body: JSON.stringify({ name: "New item", status: "active" }),
|
|
520
|
+
});
|
|
521
|
+
```
|
|
522
|
+
|
|
523
|
+
**Key rules:**
|
|
524
|
+
|
|
525
|
+
- Always create the route handler files via `file_write` before calling `app_refresh`
|
|
526
|
+
- Export an optional `description` string for CLI discoverability (`assistant routes list`)
|
|
527
|
+
- Handlers have full Node.js API access — `fs`, `path`, `crypto`, etc.
|
|
528
|
+
- Handlers get a 30-second timeout per request
|
|
529
|
+
- Files are hot-reloaded on change (mtime-based cache)
|
|
530
|
+
- Use `.ts` (preferred) or `.js` extensions
|
|
531
|
+
- Route resolution: `routes/foo.ts` → `/v1/x/foo`, `routes/bar/index.ts` → `/v1/x/bar`
|
|
532
|
+
|
|
448
533
|
#### Client-side state management
|
|
449
534
|
|
|
450
|
-
`localStorage` and `sessionStorage` are available for ephemeral UI state (filters, view modes, collapsed state, preferences, form drafts). Use
|
|
535
|
+
`localStorage` and `sessionStorage` are available for ephemeral UI state (filters, view modes, collapsed state, preferences, form drafts). Use custom routes for persistent app records, `localStorage` for UI preferences.
|
|
451
536
|
|
|
452
537
|
<!-- feature:app-builder-multifile:alt -->
|
|
453
538
|
|
|
@@ -464,7 +549,8 @@ let allRecords = [];
|
|
|
464
549
|
|
|
465
550
|
async function loadRecords() {
|
|
466
551
|
try {
|
|
467
|
-
|
|
552
|
+
const res = await fetch("/v1/x/records");
|
|
553
|
+
allRecords = await res.json();
|
|
468
554
|
render();
|
|
469
555
|
} catch (err) {
|
|
470
556
|
console.error("Failed to load:", err);
|
|
@@ -553,7 +639,7 @@ Every app must meet these baselines:
|
|
|
553
639
|
|
|
554
640
|
## Presentation Slide Design
|
|
555
641
|
|
|
556
|
-
Slides are a different domain from apps. Skip app-specific patterns (contextual headers, search/filter, toast notifications, form validation,
|
|
642
|
+
Slides are a different domain from apps. Skip app-specific patterns (contextual headers, search/filter, toast notifications, form validation, custom routes). Slides are static content — build navigation and layouts with custom HTML/CSS.
|
|
557
643
|
|
|
558
644
|
**Key principles:**
|
|
559
645
|
|
|
@@ -566,7 +652,7 @@ Slides are a different domain from apps. Skip app-specific patterns (contextual
|
|
|
566
652
|
|
|
567
653
|
## Error Handling
|
|
568
654
|
|
|
569
|
-
- All `
|
|
655
|
+
- All `fetch()` calls to custom routes must be wrapped in `try/catch` with user-friendly feedback.
|
|
570
656
|
- Never let a failed operation silently pass - always show a toast or inline error.
|
|
571
657
|
- If the page loads with no data, show a designed empty state (`.v-empty-state`).
|
|
572
658
|
- For forms, show validation errors inline next to the relevant field.
|
|
@@ -110,22 +110,27 @@ When a user asks to declutter, clean up, or organize their email - start scannin
|
|
|
110
110
|
|
|
111
111
|
### Workflow
|
|
112
112
|
|
|
113
|
-
1. **Scan**: Call `gmail_sender_digest`. Default query targets promotions from the last 90 days.
|
|
113
|
+
1. **Scan**: Call `gmail_sender_digest`. Default query targets promotions currently in the inbox from the last 90 days (`in:inbox category:promotions newer_than:90d`). Counts shown in the table reflect only what is currently in the inbox — these are the emails that will be archived.
|
|
114
114
|
2. **Present**: Show results as a `ui_show` table with `selectionMode: "multiple"`:
|
|
115
115
|
- **Columns (exactly 3)**: Sender, Emails Found, Unsub?
|
|
116
116
|
- **Unsub? cell values**: Use rich cell format: `{ "text": "Yes", "icon": "checkmark.circle.fill", "iconColor": "success" }` when `has_unsubscribe` is true, `{ "text": "No", "icon": "minus.circle", "iconColor": "muted" }` when false.
|
|
117
117
|
- **Pre-select all rows** (`selected: true`) - users deselect what they want to keep
|
|
118
118
|
- **Caption**: Include two parts separated by a newline: (1) data scope, e.g. "Newsletters, notifications, and outreach from last 90 days. Deselect anything you want to keep." (adjusted to match the query used), and (2) the Unsub? column legend: "Unsub? - \"Yes\" means these emails contain an unsubscribe link, so I can opt you out automatically. \"No\" means no unsubscribe link was found - these will be archived but you may continue receiving them."
|
|
119
119
|
- **Action buttons (exactly 2)**: "Archive & Unsubscribe" (primary), "Archive Only" (secondary). **NEVER offer Delete, Trash, or any destructive action.**
|
|
120
|
-
3. **
|
|
120
|
+
3. **Embed scan_id in button data**: When constructing the action buttons in `ui_show`, include the `scan_id` from the `gmail_sender_digest` result in each button's `data` field. This ensures `scan_id` is forwarded automatically when the user clicks — the LLM does not need to recall it from earlier context:
|
|
121
|
+
```json
|
|
122
|
+
{ "id": "archive_unsubscribe", "label": "Archive & Unsubscribe", "style": "primary", "data": { "scan_id": "<scan_id value here>" } }
|
|
123
|
+
```
|
|
124
|
+
4. **Wait for user action**: Stop and wait. Do NOT proceed to archiving or unsubscribing until the user clicks one of the action buttons on the table. When the user clicks an action button you will receive a surface action message containing `action data: { scan_id, selectedIds }`:
|
|
125
|
+
- `selectedIds` are **sender IDs** (the `id` values from the scan result rows, base64-encoded email addresses) — NOT Gmail message IDs. Always use them as `sender_ids` with `scan_id`, never as `message_ids`.
|
|
121
126
|
- **Dismiss the table immediately** with `ui_dismiss` - it collapses to a completion chip
|
|
122
127
|
- **Show a `task_progress` card** with steps for each phase (e.g., "Archiving 89 senders (2,400 emails)", "Unsubscribing from 72 senders"). Update each step from `in_progress` → `completed` as each phase finishes.
|
|
123
128
|
- When all senders are processed, set the progress card's `status: "completed"`.
|
|
124
|
-
|
|
125
|
-
- **Archive all at once**: Call `gmail_archive` **once** with `scan_id`
|
|
129
|
+
5. **Act on selection** - batch, don't loop:
|
|
130
|
+
- **Archive all at once**: Call `gmail_archive` **once** with `scan_id` (from action data) + `sender_ids` set to all `selectedIds` from the action data. The tool resolves message IDs server-side and batches the Gmail API calls internally - never loop sender-by-sender. **Never** pass `selectedIds` as `message_ids` — they are sender IDs, not Gmail message IDs.
|
|
126
131
|
- **Unsubscribe in bulk**: If the action is "Archive & Unsubscribe", call `gmail_unsubscribe` for each sender that has `has_unsubscribe: true` - but emit **all** unsubscribe tool calls in a **single assistant response** (parallel tool use) rather than one-at-a-time across separate turns.
|
|
127
|
-
|
|
128
|
-
|
|
132
|
+
6. **Accurate summary**: The scan counts are exact - the `message_count` shown in the table matches the number of messages archived. Format: "Cleaned up [total_archived] emails from [sender_count] senders. Unsubscribed from [unsub_count]."
|
|
133
|
+
7. **Ongoing protection offer**: After reporting results, offer auto-archive filters:
|
|
129
134
|
- "Want me to set up auto-archive filters so future emails from these senders skip your inbox?"
|
|
130
135
|
- If yes, call `gmail_filters` with `action: "create"` for each sender with `from` set to the sender's email and `remove_label_ids: ["INBOX"]`.
|
|
131
136
|
- Then offer a recurring declutter schedule: "Want me to scan for new clutter monthly?" If yes, use `schedule_create` to set up a monthly declutter check.
|
|
@@ -156,9 +161,9 @@ Scan tools (`gmail_sender_digest`, `gmail_outreach_scan`) return a `scan_id` tha
|
|
|
156
161
|
|
|
157
162
|
Before composing any email that references a date or time:
|
|
158
163
|
|
|
159
|
-
1. Check the
|
|
164
|
+
1. Check the `timestamp:` field in the `<turn_context>` block for today's date and timezone
|
|
160
165
|
2. Verify that "tomorrow" means the day after today's date, "next week" means the upcoming Monday–Friday, etc.
|
|
161
|
-
3. If the email references a date from another message, cross-check it against the
|
|
166
|
+
3. If the email references a date from another message, cross-check it against the turn context to ensure it's in the future
|
|
162
167
|
|
|
163
168
|
## Confidence Scores
|
|
164
169
|
|
|
@@ -490,7 +490,7 @@
|
|
|
490
490
|
"properties": {
|
|
491
491
|
"query": {
|
|
492
492
|
"type": "string",
|
|
493
|
-
"description": "Gmail search query (default 'category:promotions newer_than:90d')"
|
|
493
|
+
"description": "Gmail search query (default 'in:inbox category:promotions newer_than:90d')"
|
|
494
494
|
},
|
|
495
495
|
"max_messages": {
|
|
496
496
|
"type": "number",
|
|
@@ -49,7 +49,8 @@ export async function run(
|
|
|
49
49
|
_context: ToolContext,
|
|
50
50
|
): Promise<ToolExecutionResult> {
|
|
51
51
|
const account = input.account as string | undefined;
|
|
52
|
-
const query =
|
|
52
|
+
const query =
|
|
53
|
+
(input.query as string) ?? "in:inbox category:promotions newer_than:90d";
|
|
53
54
|
const maxMessages = Math.min(
|
|
54
55
|
(input.max_messages as number) ?? 5000,
|
|
55
56
|
MAX_MESSAGES_CAP,
|
|
@@ -148,6 +148,7 @@ Telegram is supported as a messaging provider with limited capabilities compared
|
|
|
148
148
|
- `send_notification` is provided by the **notifications** skill (always active) -- use it when the user asks for an alert/notification (for example "send this as a desktop notification").
|
|
149
149
|
- Use `messaging_send` when the user asks to send a message into a specific chat/email destination.
|
|
150
150
|
- `send_notification` channel routing is LLM-driven; `preferred_channels` are hints, not hard channel forcing.
|
|
151
|
+
- Before using `messaging_send` or `send_notification`, look up the recipient's contact record with `contact_search` to inform tone and content (see **Recipient Context** below).
|
|
151
152
|
|
|
152
153
|
## Personalized Drafting
|
|
153
154
|
|
|
@@ -157,6 +158,12 @@ If no style items exist and the user asks you to draft a message, suggest runnin
|
|
|
157
158
|
|
|
158
159
|
> "I can analyze your sent messages to learn your writing style so drafts sound like you. Want me to do that?"
|
|
159
160
|
|
|
161
|
+
## Recipient Context
|
|
162
|
+
|
|
163
|
+
Before composing or sending a message to someone, look up their contact record with `contact_search` using their name or channel address. If the contact has notes (e.g. relationship context, communication preferences, response expectations), use that context to inform the message's tone, level of detail, and content. This ensures outbound messages are personalized to the recipient — not just the sender's style.
|
|
164
|
+
|
|
165
|
+
If no contact record exists, proceed without recipient context.
|
|
166
|
+
|
|
160
167
|
## Confidence Scores
|
|
161
168
|
|
|
162
169
|
Medium and high risk tools require a confidence score between 0 and 1:
|
|
@@ -87,6 +87,14 @@ The `mode` parameter controls what happens when a schedule fires:
|
|
|
87
87
|
|
|
88
88
|
Use `notify` for simple reminders ("remind me to take medicine at 9am") and `execute` for tasks that need assistant action ("check my calendar at 8am and send me a digest").
|
|
89
89
|
|
|
90
|
+
## Conversation Reuse
|
|
91
|
+
|
|
92
|
+
By default, each schedule run creates a new conversation. For recurring schedules that benefit from accumulating context across runs (e.g. polling-style jobs, daily digests that reference prior results), set `reuse_conversation: true`. When enabled, subsequent runs reuse the conversation from the last successful run instead of creating a new one.
|
|
93
|
+
|
|
94
|
+
- Only applies to **recurring** schedules; ignored for one-shot schedules.
|
|
95
|
+
- If the prior conversation has been deleted, a new one is created automatically.
|
|
96
|
+
- On the first run (no prior conversation), a new conversation is created as usual.
|
|
97
|
+
|
|
90
98
|
## Routing (notify mode)
|
|
91
99
|
|
|
92
100
|
Control how notify-mode schedules are delivered at trigger time with `routing_intent`:
|
|
@@ -101,9 +109,20 @@ Optionally pass `routing_hints` (a JSON object) to influence routing decisions (
|
|
|
101
109
|
|
|
102
110
|
- **Default to `all_channels`** for most notifications. Users usually want to be notified wherever they are.
|
|
103
111
|
- **Use `single_channel`** only when the user explicitly specifies a single channel (e.g. "remind me on Telegram").
|
|
104
|
-
- **
|
|
112
|
+
- **Determine the originating channel** for routing hints using this priority:
|
|
113
|
+
1. **`source_channel`** from `<turn_context>` — use directly if present. This is the authoritative channel name.
|
|
114
|
+
2. **`interface` fallback** — if `source_channel` is absent (common for guardian/direct users), map the `interface` value to a channel name:
|
|
115
|
+
| `interface` value | Channel name |
|
|
116
|
+
| --- | --- |
|
|
117
|
+
| `macos`, `ios` | `vellum` |
|
|
118
|
+
| `telegram` | `telegram` |
|
|
119
|
+
| `slack` | `slack` |
|
|
120
|
+
| `cli` | _(omit — no routable channel)_ |
|
|
121
|
+
3. If neither field is present or the interface is `cli`, omit `preferred_channels`.
|
|
122
|
+
|
|
123
|
+
When a channel is determined, include it as a routing hint:
|
|
105
124
|
```
|
|
106
|
-
routing_hints: { preferred_channels: ["
|
|
125
|
+
routing_hints: { preferred_channels: ["<resolved channel>"] }
|
|
107
126
|
routing_intent: "all_channels"
|
|
108
127
|
```
|
|
109
128
|
|
|
@@ -119,6 +138,7 @@ Use `syntax` + `expression` to specify the schedule type explicitly, or just `ex
|
|
|
119
138
|
|
|
120
139
|
## Tips
|
|
121
140
|
|
|
141
|
+
- **When the user specifies a name for the schedule, use it exactly as given.** Do not paraphrase, embellish, or generate a descriptive name.
|
|
122
142
|
- Use `schedule_create` for both recurring automation ("every day at 9am") and one-time reminders ("remind me at 3pm").
|
|
123
143
|
- For task tracking ("add to my tasks", "add to my queue"), use task_list_add instead.
|
|
124
144
|
- `fire_at` must be a strict ISO 8601 timestamp with timezone offset or Z (e.g. `2025-03-15T09:00:00-05:00`).
|
|
@@ -56,6 +56,10 @@
|
|
|
56
56
|
"type": "boolean",
|
|
57
57
|
"description": "When true, suppress completion notifications for this schedule. The job still runs and produces output, but no notification or conversation message is sent on completion. Useful for high-frequency recurring jobs that report findings separately. Defaults to false."
|
|
58
58
|
},
|
|
59
|
+
"reuse_conversation": {
|
|
60
|
+
"type": "boolean",
|
|
61
|
+
"description": "When true, reuse the same conversation across recurring schedule runs instead of creating a new one each time. Useful for polling-style schedules that accumulate context over time. Ignored for one-shot schedules. Defaults to false."
|
|
62
|
+
},
|
|
59
63
|
"activity": {
|
|
60
64
|
"type": "string",
|
|
61
65
|
"description": "Brief non-technical explanation of why this tool is being called"
|
|
@@ -147,6 +151,10 @@
|
|
|
147
151
|
"type": "boolean",
|
|
148
152
|
"description": "When true, suppress completion notifications for this schedule. Useful for high-frequency jobs that report findings separately."
|
|
149
153
|
},
|
|
154
|
+
"reuse_conversation": {
|
|
155
|
+
"type": "boolean",
|
|
156
|
+
"description": "When true, reuse the same conversation across recurring schedule runs instead of creating a new one each time. Useful for polling-style schedules that accumulate context over time. Ignored for one-shot schedules."
|
|
157
|
+
},
|
|
150
158
|
"activity": {
|
|
151
159
|
"type": "string",
|
|
152
160
|
"description": "Brief non-technical explanation of why this tool is being called"
|
|
@@ -7,27 +7,17 @@ import type {
|
|
|
7
7
|
ToolContext,
|
|
8
8
|
ToolExecutionResult,
|
|
9
9
|
} from "../../../../tools/types.js";
|
|
10
|
-
import {
|
|
10
|
+
import { getAvatarDir, getAvatarImagePath } from "../../../../util/platform.js";
|
|
11
11
|
|
|
12
12
|
export async function run(
|
|
13
13
|
_input: Record<string, unknown>,
|
|
14
14
|
_context: ToolContext,
|
|
15
15
|
): Promise<ToolExecutionResult> {
|
|
16
|
-
const avatarPath =
|
|
17
|
-
getWorkspaceDir(),
|
|
18
|
-
"data",
|
|
19
|
-
"avatar",
|
|
20
|
-
"avatar-image.png",
|
|
21
|
-
);
|
|
16
|
+
const avatarPath = getAvatarImagePath();
|
|
22
17
|
|
|
23
18
|
if (!existsSync(avatarPath)) {
|
|
24
19
|
// Check for native character traits and regenerate the static PNG
|
|
25
|
-
const traitsPath = join(
|
|
26
|
-
getWorkspaceDir(),
|
|
27
|
-
"data",
|
|
28
|
-
"avatar",
|
|
29
|
-
"character-traits.json",
|
|
30
|
-
);
|
|
20
|
+
const traitsPath = join(getAvatarDir(), "character-traits.json");
|
|
31
21
|
if (existsSync(traitsPath)) {
|
|
32
22
|
try {
|
|
33
23
|
const traits = JSON.parse(readFileSync(traitsPath, "utf-8"));
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { existsSync, unlinkSync } from "node:fs";
|
|
2
|
-
import { join } from "node:path";
|
|
3
2
|
|
|
4
3
|
import { buildAssistantEvent } from "../../../../runtime/assistant-event.js";
|
|
5
4
|
import { assistantEventHub } from "../../../../runtime/assistant-event-hub.js";
|
|
@@ -9,7 +8,7 @@ import type {
|
|
|
9
8
|
ToolExecutionResult,
|
|
10
9
|
} from "../../../../tools/types.js";
|
|
11
10
|
import { getLogger } from "../../../../util/logger.js";
|
|
12
|
-
import {
|
|
11
|
+
import { getAvatarImagePath } from "../../../../util/platform.js";
|
|
13
12
|
import { updateIdentityAvatarSection } from "./identity-avatar.js";
|
|
14
13
|
|
|
15
14
|
const log = getLogger("avatar-remove");
|
|
@@ -18,8 +17,7 @@ export async function run(
|
|
|
18
17
|
_input: Record<string, unknown>,
|
|
19
18
|
_context: ToolContext,
|
|
20
19
|
): Promise<ToolExecutionResult> {
|
|
21
|
-
const
|
|
22
|
-
const avatarPath = join(avatarDir, "avatar-image.png");
|
|
20
|
+
const avatarPath = getAvatarImagePath();
|
|
23
21
|
|
|
24
22
|
if (!existsSync(avatarPath)) {
|
|
25
23
|
return {
|
|
@@ -9,14 +9,17 @@ import type {
|
|
|
9
9
|
ToolExecutionResult,
|
|
10
10
|
} from "../../../../tools/types.js";
|
|
11
11
|
import { getLogger } from "../../../../util/logger.js";
|
|
12
|
-
import {
|
|
12
|
+
import {
|
|
13
|
+
getAvatarImagePath,
|
|
14
|
+
getWorkspaceDir,
|
|
15
|
+
} from "../../../../util/platform.js";
|
|
13
16
|
import { updateIdentityAvatarSection } from "./identity-avatar.js";
|
|
14
17
|
|
|
15
18
|
const log = getLogger("avatar-update");
|
|
16
19
|
|
|
17
20
|
/** Canonical path where the custom avatar PNG is stored. */
|
|
18
21
|
function getAvatarPath(): string {
|
|
19
|
-
return
|
|
22
|
+
return getAvatarImagePath();
|
|
20
23
|
}
|
|
21
24
|
|
|
22
25
|
export async function run(
|
|
@@ -10,21 +10,26 @@ const SETTINGS_TABS = [
|
|
|
10
10
|
"Sounds",
|
|
11
11
|
"Permissions & Privacy",
|
|
12
12
|
"Billing",
|
|
13
|
-
"
|
|
13
|
+
"Archive",
|
|
14
14
|
"Schedules",
|
|
15
15
|
"Developer",
|
|
16
16
|
] as const;
|
|
17
17
|
|
|
18
18
|
type SettingsTab = (typeof SETTINGS_TABS)[number];
|
|
19
19
|
|
|
20
|
+
const LEGACY_TAB_ALIASES: Record<string, SettingsTab> = {
|
|
21
|
+
"Archived Conversations": "Archive",
|
|
22
|
+
};
|
|
23
|
+
|
|
20
24
|
export async function run(
|
|
21
25
|
input: Record<string, unknown>,
|
|
22
26
|
context: ToolContext,
|
|
23
27
|
): Promise<ToolExecutionResult> {
|
|
24
|
-
const
|
|
28
|
+
const rawTab = input.tab as string;
|
|
29
|
+
const tab = LEGACY_TAB_ALIASES[rawTab] ?? rawTab;
|
|
25
30
|
if (!SETTINGS_TABS.includes(tab as SettingsTab)) {
|
|
26
31
|
return {
|
|
27
|
-
content: `Error: unknown tab "${
|
|
32
|
+
content: `Error: unknown tab "${rawTab}". Valid tabs: ${SETTINGS_TABS.join(
|
|
28
33
|
", ",
|
|
29
34
|
)}`,
|
|
30
35
|
isError: true,
|
|
@@ -68,6 +68,8 @@ When you need to send a DM or look up a Slack user by name, check contacts first
|
|
|
68
68
|
|
|
69
69
|
1. **Before calling `users.list`**: Use `contact_search` with `query: "<name>"` and `channel_type: "slack"`. If a matching contact has `externalUserId` (Slack user ID) and `externalChatId` (DM channel ID), skip the API lookups and use those IDs directly with `chat.postMessage`.
|
|
70
70
|
|
|
71
|
+
When `contact_search` returns notes for the recipient, use them to inform the message's tone, formality, and content. Contact notes capture relationship context and communication preferences that should shape how you write to this person.
|
|
72
|
+
|
|
71
73
|
2. **After resolving via API**: When you had to call `users.list` or `conversations.open` to resolve a user, save the contact with `contact_upsert` so you can find them by name next time. External Slack IDs (user ID, DM channel ID) are cached automatically by the messaging layer and should not be passed through `contact_upsert`.
|
|
72
74
|
|
|
73
75
|
## Privacy Rules
|
|
@@ -6,6 +6,9 @@ metadata:
|
|
|
6
6
|
emoji: "🤖"
|
|
7
7
|
vellum:
|
|
8
8
|
display-name: "Subagent"
|
|
9
|
+
activation-hints:
|
|
10
|
+
- "Run tasks in parallel, delegate work to background agents, or do multiple things at once"
|
|
11
|
+
- "Spawn a researcher, coder, or planner agent for independent work"
|
|
9
12
|
---
|
|
10
13
|
|
|
11
14
|
Subagent orchestration -- spawn background agents to work on tasks in parallel.
|
|
@@ -14,9 +17,43 @@ Subagent orchestration -- spawn background agents to work on tasks in parallel.
|
|
|
14
17
|
|
|
15
18
|
Subagents follow this status flow: `pending` -> `running` -> `completed` / `failed` / `aborted`
|
|
16
19
|
|
|
17
|
-
- **Spawn**: Use `subagent_spawn` with a label and
|
|
18
|
-
- **
|
|
19
|
-
- **
|
|
20
|
+
- **Spawn**: Use `subagent_spawn` with a label, objective, and role. The subagent runs autonomously.
|
|
21
|
+
- **Mid-run communication**: Subagents can send notifications to the parent via `notify_parent` while still running -- useful for sharing interim findings or signaling that they are blocked.
|
|
22
|
+
- **Auto-notification**: The parent conversation is automatically notified when a subagent reaches a terminal status (completed/failed/aborted). Do NOT poll `subagent_status`.
|
|
23
|
+
- **Read output**: Use `subagent_read` after the subagent reaches a terminal status to retrieve its full output.
|
|
24
|
+
|
|
25
|
+
## Roles
|
|
26
|
+
|
|
27
|
+
Each subagent is spawned with a role that determines its tool access. Choose the most restrictive role that can accomplish the task.
|
|
28
|
+
|
|
29
|
+
| Role | Tools | When to use |
|
|
30
|
+
|---|---|---|
|
|
31
|
+
| `general` | Full tool access | Task genuinely needs unrestricted capabilities (rare -- prefer a specialized role) |
|
|
32
|
+
| `researcher` | `web_search`, `web_fetch`, `file_read`, `file_list`, `recall`, `notify_parent` | Information gathering, web research, codebase exploration, reading documentation |
|
|
33
|
+
| `coder` | `bash`, `file_read`, `file_write`, `file_edit`, `web_search`, `recall`, `notify_parent` | Code changes, file editing, running commands, build/test tasks |
|
|
34
|
+
| `planner` | `file_read`, `file_list`, `web_search`, `web_fetch`, `recall`, `notify_parent` | Analysis, planning, synthesizing information, reviewing approaches |
|
|
35
|
+
|
|
36
|
+
All specialized roles (`researcher`, `coder`, `planner`) include `notify_parent` for mid-run communication with the parent.
|
|
37
|
+
|
|
38
|
+
## Parent Communication
|
|
39
|
+
|
|
40
|
+
Subagents use `notify_parent` to send messages to the parent conversation while still running. Each notification has an urgency level:
|
|
41
|
+
|
|
42
|
+
- **`info`** -- Progress updates, minor findings. The parent is informed but does not need to act.
|
|
43
|
+
- **`important`** -- Key findings, significant results. The parent should review when convenient.
|
|
44
|
+
- **`blocked`** -- The subagent needs guidance or a decision from the parent to continue.
|
|
45
|
+
|
|
46
|
+
Use notifications judiciously -- one per major finding or milestone. Do not send a notification for every small step.
|
|
47
|
+
|
|
48
|
+
## Naming
|
|
49
|
+
|
|
50
|
+
Subagents can be referenced by label instead of UUID. The `label` parameter is accepted on `subagent_message`, `subagent_status`, `subagent_read`, and `subagent_abort` as an alternative to `subagent_id`. Label lookup is case-insensitive.
|
|
51
|
+
|
|
52
|
+
Use descriptive labels when spawning subagents (e.g., "research-auth-libraries", "implement-login-form") so they are easy to reference later.
|
|
53
|
+
|
|
54
|
+
## Reading Output
|
|
55
|
+
|
|
56
|
+
`subagent_read` returns the subagent's assistant text output. Use the `last_n` parameter to retrieve only the most recent N assistant messages instead of the full history. This is useful for large outputs where you only need the final result.
|
|
20
57
|
|
|
21
58
|
## Ownership
|
|
22
59
|
|
|
@@ -29,5 +66,8 @@ Set `send_result_to_user: false` when spawning a subagent whose result is for in
|
|
|
29
66
|
## Tips
|
|
30
67
|
|
|
31
68
|
- Do NOT poll `subagent_status` in a loop. You will be notified automatically when a subagent completes.
|
|
69
|
+
- Use roles to scope tool access and minimize blast radius. Default to the most restrictive role that works.
|
|
70
|
+
- Spawn a `researcher` and `coder` in parallel for research-then-implement workflows -- the researcher gathers context while the coder starts on the known parts.
|
|
71
|
+
- Use `notify_parent` for interim findings instead of waiting for completion. This lets the parent act on partial results early.
|
|
32
72
|
- Use `subagent_message` to send follow-up instructions to a running subagent.
|
|
33
73
|
- Use `subagent_abort` to cancel a subagent that is no longer needed.
|
|
@@ -25,6 +25,11 @@
|
|
|
25
25
|
"type": "boolean",
|
|
26
26
|
"description": "Whether to present the subagent's result to the user when it completes. Defaults to true. Set to false for internal/silent processing."
|
|
27
27
|
},
|
|
28
|
+
"role": {
|
|
29
|
+
"type": "string",
|
|
30
|
+
"enum": ["general", "researcher", "coder", "planner"],
|
|
31
|
+
"description": "Agent specialization that controls tool access. 'researcher': read-only (web, files, memory). 'coder': file and bash access. 'planner': read-only analysis. 'general': full access (default)."
|
|
32
|
+
},
|
|
28
33
|
"activity": {
|
|
29
34
|
"type": "string",
|
|
30
35
|
"description": "Brief non-technical explanation of why this tool is being called"
|
|
@@ -47,6 +52,10 @@
|
|
|
47
52
|
"type": "string",
|
|
48
53
|
"description": "Optional subagent ID to query. If omitted, returns all subagents for this conversation."
|
|
49
54
|
},
|
|
55
|
+
"label": {
|
|
56
|
+
"type": "string",
|
|
57
|
+
"description": "The label of the subagent (alternative to subagent_id). Case-insensitive."
|
|
58
|
+
},
|
|
50
59
|
"activity": {
|
|
51
60
|
"type": "string",
|
|
52
61
|
"description": "Brief non-technical explanation of why this tool is being called"
|
|
@@ -59,7 +68,7 @@
|
|
|
59
68
|
},
|
|
60
69
|
{
|
|
61
70
|
"name": "subagent_abort",
|
|
62
|
-
"description": "Abort a running subagent by ID.",
|
|
71
|
+
"description": "Abort a running subagent by ID or label.",
|
|
63
72
|
"category": "orchestration",
|
|
64
73
|
"risk": "low",
|
|
65
74
|
"input_schema": {
|
|
@@ -69,12 +78,16 @@
|
|
|
69
78
|
"type": "string",
|
|
70
79
|
"description": "The ID of the subagent to abort."
|
|
71
80
|
},
|
|
81
|
+
"label": {
|
|
82
|
+
"type": "string",
|
|
83
|
+
"description": "The label of the subagent (alternative to subagent_id). Case-insensitive."
|
|
84
|
+
},
|
|
72
85
|
"activity": {
|
|
73
86
|
"type": "string",
|
|
74
87
|
"description": "Brief non-technical explanation of why this tool is being called"
|
|
75
88
|
}
|
|
76
89
|
},
|
|
77
|
-
"required": [
|
|
90
|
+
"required": []
|
|
78
91
|
},
|
|
79
92
|
"executor": "tools/subagent-abort.ts",
|
|
80
93
|
"execution_target": "host"
|
|
@@ -91,6 +104,10 @@
|
|
|
91
104
|
"type": "string",
|
|
92
105
|
"description": "The ID of the subagent to send a message to."
|
|
93
106
|
},
|
|
107
|
+
"label": {
|
|
108
|
+
"type": "string",
|
|
109
|
+
"description": "The label of the subagent (alternative to subagent_id). Case-insensitive."
|
|
110
|
+
},
|
|
94
111
|
"content": {
|
|
95
112
|
"type": "string",
|
|
96
113
|
"description": "The message content to send to the subagent."
|
|
@@ -100,7 +117,7 @@
|
|
|
100
117
|
"description": "Brief non-technical explanation of why this tool is being called"
|
|
101
118
|
}
|
|
102
119
|
},
|
|
103
|
-
"required": ["
|
|
120
|
+
"required": ["content"]
|
|
104
121
|
},
|
|
105
122
|
"executor": "tools/subagent-message.ts",
|
|
106
123
|
"execution_target": "host"
|
|
@@ -117,12 +134,20 @@
|
|
|
117
134
|
"type": "string",
|
|
118
135
|
"description": "The ID of the subagent whose output to read."
|
|
119
136
|
},
|
|
137
|
+
"label": {
|
|
138
|
+
"type": "string",
|
|
139
|
+
"description": "The label of the subagent (alternative to subagent_id). Case-insensitive."
|
|
140
|
+
},
|
|
141
|
+
"last_n": {
|
|
142
|
+
"type": "integer",
|
|
143
|
+
"description": "Number of recent assistant messages to return. Omit to return all messages (current behavior)."
|
|
144
|
+
},
|
|
120
145
|
"activity": {
|
|
121
146
|
"type": "string",
|
|
122
147
|
"description": "Brief non-technical explanation of why this tool is being called"
|
|
123
148
|
}
|
|
124
149
|
},
|
|
125
|
-
"required": [
|
|
150
|
+
"required": []
|
|
126
151
|
},
|
|
127
152
|
"executor": "tools/subagent-read.ts",
|
|
128
153
|
"execution_target": "host"
|