@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
package/src/util/platform.ts
CHANGED
|
@@ -25,16 +25,6 @@ export function getPlatformName(): string {
|
|
|
25
25
|
return process.platform;
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
/**
|
|
29
|
-
* Returns the platform-specific clipboard copy command, or null if
|
|
30
|
-
* clipboard access is not supported on the current platform.
|
|
31
|
-
*/
|
|
32
|
-
export function getClipboardCommand(): string | null {
|
|
33
|
-
if (isMacOS()) return "pbcopy";
|
|
34
|
-
if (isLinux()) return "xclip -selection clipboard";
|
|
35
|
-
return null;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
28
|
/**
|
|
39
29
|
* Normalize an assistant ID to its canonical form for DB operations.
|
|
40
30
|
*
|
|
@@ -105,13 +95,25 @@ export function getInterfacesDir(): string {
|
|
|
105
95
|
|
|
106
96
|
/**
|
|
107
97
|
* Returns the sounds directory (~/.vellum/workspace/data/sounds).
|
|
108
|
-
* Custom sound files and sound configuration live here.
|
|
109
|
-
* can be large, so this directory is excluded from diagnostic exports.
|
|
98
|
+
* Custom sound files and sound configuration live here.
|
|
110
99
|
*/
|
|
111
100
|
export function getSoundsDir(): string {
|
|
112
101
|
return join(getWorkspaceDir(), "data", "sounds");
|
|
113
102
|
}
|
|
114
103
|
|
|
104
|
+
/** Returns the avatar directory ($VELLUM_WORKSPACE_DIR/data/avatar). */
|
|
105
|
+
export function getAvatarDir(): string {
|
|
106
|
+
return join(getWorkspaceDir(), "data", "avatar");
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/** Canonical filename for the custom avatar PNG. */
|
|
110
|
+
export const AVATAR_IMAGE_FILENAME = "avatar-image.png";
|
|
111
|
+
|
|
112
|
+
/** Returns the canonical avatar image path (~/.vellum/workspace/data/avatar/avatar-image.png). */
|
|
113
|
+
export function getAvatarImagePath(): string {
|
|
114
|
+
return join(getAvatarDir(), AVATAR_IMAGE_FILENAME);
|
|
115
|
+
}
|
|
116
|
+
|
|
115
117
|
/**
|
|
116
118
|
* Returns the TCP port the daemon should listen on for iOS clients.
|
|
117
119
|
* Hardcoded default: 8765.
|
|
@@ -218,7 +220,7 @@ export function getHistoryPath(): string {
|
|
|
218
220
|
* overrides, device approval lists — live here.
|
|
219
221
|
*
|
|
220
222
|
* This directory is:
|
|
221
|
-
* - Outside the workspace
|
|
223
|
+
* - Outside the workspace
|
|
222
224
|
* - Outside the sandbox write boundary (tools cannot modify it)
|
|
223
225
|
* - Skipped in containerized mode (credentials via CES, trust via gateway)
|
|
224
226
|
*/
|
|
@@ -271,10 +273,6 @@ export function getEmbedWorkerPidPath(): string {
|
|
|
271
273
|
* When the VELLUM_WORKSPACE_DIR env var is set, returns that value (used in
|
|
272
274
|
* containerized deployments where the workspace is a separate volume).
|
|
273
275
|
* Otherwise falls back to ~/.vellum/workspace.
|
|
274
|
-
*
|
|
275
|
-
* WARNING: The entire workspace directory is included in diagnostic log exports
|
|
276
|
-
* ("Send logs to Vellum"). Do not store secrets, API keys, or sensitive
|
|
277
|
-
* credentials here — use the credential store or ~/.vellum/protected/ instead.
|
|
278
276
|
*/
|
|
279
277
|
export function getWorkspaceDir(): string {
|
|
280
278
|
const override = getWorkspaceDirOverride();
|
|
@@ -315,6 +313,11 @@ export function getWorkspaceHooksDir(): string {
|
|
|
315
313
|
return join(getWorkspaceDir(), "hooks");
|
|
316
314
|
}
|
|
317
315
|
|
|
316
|
+
/** Returns $VELLUM_WORKSPACE_DIR/routes — user-defined HTTP route handlers. */
|
|
317
|
+
export function getWorkspaceRoutesDir(): string {
|
|
318
|
+
return join(getWorkspaceDir(), "routes");
|
|
319
|
+
}
|
|
320
|
+
|
|
318
321
|
/** Returns ~/.vellum/workspace/deprecated — transitional files slated for removal. */
|
|
319
322
|
export function getDeprecatedDir(): string {
|
|
320
323
|
return join(getWorkspaceDir(), "deprecated");
|
|
@@ -330,6 +333,35 @@ export function getWorkspacePromptPath(file: string): string {
|
|
|
330
333
|
return join(getWorkspaceDir(), file);
|
|
331
334
|
}
|
|
332
335
|
|
|
336
|
+
// ── Profiler filesystem layout ──────────────────────────────────────────
|
|
337
|
+
// Managed profiler runs live under <workspace>/data/profiler/. These
|
|
338
|
+
// helpers enforce a single canonical layout so every runtime caller
|
|
339
|
+
// resolves the same paths.
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* Returns the profiler root directory (<workspace>/data/profiler).
|
|
343
|
+
* All profiler state (runs directory, global metadata) lives here.
|
|
344
|
+
*/
|
|
345
|
+
export function getProfilerRootDir(): string {
|
|
346
|
+
return join(getDataDir(), "profiler");
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
/**
|
|
350
|
+
* Returns the profiler runs directory (<workspace>/data/profiler/runs).
|
|
351
|
+
* Each completed or active profiler run gets its own sub-directory here.
|
|
352
|
+
*/
|
|
353
|
+
export function getProfilerRunsDir(): string {
|
|
354
|
+
return join(getProfilerRootDir(), "runs");
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
/**
|
|
358
|
+
* Returns the directory for a specific profiler run by ID
|
|
359
|
+
* (<workspace>/data/profiler/runs/<runId>).
|
|
360
|
+
*/
|
|
361
|
+
export function getProfilerRunDir(runId: string): string {
|
|
362
|
+
return join(getProfilerRunsDir(), runId);
|
|
363
|
+
}
|
|
364
|
+
|
|
333
365
|
export function ensureDataDir(): void {
|
|
334
366
|
const root = vellumRoot();
|
|
335
367
|
const workspace = getWorkspaceDir();
|
|
@@ -342,6 +374,7 @@ export function ensureDataDir(): void {
|
|
|
342
374
|
join(workspace, "signals"),
|
|
343
375
|
join(workspace, "hooks"),
|
|
344
376
|
join(workspace, "skills"),
|
|
377
|
+
join(workspace, "routes"),
|
|
345
378
|
join(workspace, "embedding-models"),
|
|
346
379
|
join(workspace, "conversations"),
|
|
347
380
|
join(workspace, "logs"),
|
|
@@ -28,7 +28,7 @@ export interface WatcherProvider {
|
|
|
28
28
|
id: string;
|
|
29
29
|
/** Human-readable name. */
|
|
30
30
|
displayName: string;
|
|
31
|
-
/** Credential service required (e.g. '
|
|
31
|
+
/** Credential service required (e.g. 'google'). */
|
|
32
32
|
requiredCredentialService: string;
|
|
33
33
|
|
|
34
34
|
/**
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Previously, dictation-profiles.json, email-guardrails.json, and
|
|
5
5
|
* active-call-leases.json lived directly under getRootDir() (~/.vellum/).
|
|
6
|
-
* This migration moves them into the workspace directory so they
|
|
7
|
-
*
|
|
6
|
+
* This migration moves them into the workspace directory so they follow
|
|
7
|
+
* the workspace convention for organizational consistency.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import { existsSync, renameSync, unlinkSync } from "node:fs";
|
|
@@ -40,8 +40,8 @@ function getRootDir(): string {
|
|
|
40
40
|
const FILE_MOVES: Array<{ name: string; subdir?: string }> = [
|
|
41
41
|
{ name: "daemon-stderr.log", subdir: "logs" },
|
|
42
42
|
{ name: "daemon-startup.lock" },
|
|
43
|
-
// .env stays at root — it contains secrets (API keys) and
|
|
44
|
-
//
|
|
43
|
+
// .env stays at root — it contains secrets (API keys) and should not
|
|
44
|
+
// be in the sandbox working directory.
|
|
45
45
|
{ name: "embed-worker.pid" },
|
|
46
46
|
];
|
|
47
47
|
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workspace migration 028: Recover conversations from disk-view directories.
|
|
3
|
+
*
|
|
4
|
+
* If the SQLite database was recreated empty but the disk-view directories
|
|
5
|
+
* under `workspace/conversations/` still exist, this migration reads each
|
|
6
|
+
* conversation's `meta.json` and `messages.jsonl` and re-inserts the rows
|
|
7
|
+
* into the database.
|
|
8
|
+
*
|
|
9
|
+
* Idempotent: conversations already present in the DB are skipped.
|
|
10
|
+
* Malformed files are skipped with warnings — they do not crash the migration.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { randomUUID } from "node:crypto";
|
|
14
|
+
import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
|
|
15
|
+
import { join } from "node:path";
|
|
16
|
+
|
|
17
|
+
import { eq } from "drizzle-orm";
|
|
18
|
+
|
|
19
|
+
import { getDb } from "../../memory/db.js";
|
|
20
|
+
import { conversations, messages } from "../../memory/schema/conversations.js";
|
|
21
|
+
import { getLogger } from "../../util/logger.js";
|
|
22
|
+
import type { WorkspaceMigration } from "./types.js";
|
|
23
|
+
|
|
24
|
+
const log = getLogger("workspace-migrations");
|
|
25
|
+
|
|
26
|
+
interface DiskMeta {
|
|
27
|
+
id: string;
|
|
28
|
+
title?: string;
|
|
29
|
+
type?: string;
|
|
30
|
+
channel?: string;
|
|
31
|
+
createdAt?: string;
|
|
32
|
+
updatedAt?: string;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
interface DiskToolCall {
|
|
36
|
+
name?: string;
|
|
37
|
+
input?: unknown;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
interface DiskToolResult {
|
|
41
|
+
content?: unknown;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
interface DiskMessageRecord {
|
|
45
|
+
role: string;
|
|
46
|
+
ts?: string;
|
|
47
|
+
content?: string;
|
|
48
|
+
toolCalls?: DiskToolCall[];
|
|
49
|
+
toolResults?: DiskToolResult[];
|
|
50
|
+
attachments?: unknown[];
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function parseEpochMs(isoString: string | undefined): number | null {
|
|
54
|
+
if (!isoString) return null;
|
|
55
|
+
const ms = new Date(isoString).getTime();
|
|
56
|
+
return Number.isNaN(ms) ? null : ms;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function buildContentBlocks(record: DiskMessageRecord): unknown[] {
|
|
60
|
+
const blocks: unknown[] = [];
|
|
61
|
+
|
|
62
|
+
if (record.content) {
|
|
63
|
+
blocks.push({ type: "text", text: record.content });
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (Array.isArray(record.toolCalls)) {
|
|
67
|
+
for (const tc of record.toolCalls) {
|
|
68
|
+
blocks.push({
|
|
69
|
+
type: "tool_use",
|
|
70
|
+
id: randomUUID(),
|
|
71
|
+
name: tc.name ?? "unknown",
|
|
72
|
+
input: tc.input ?? {},
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (Array.isArray(record.toolResults)) {
|
|
78
|
+
for (const tr of record.toolResults) {
|
|
79
|
+
blocks.push({
|
|
80
|
+
type: "tool_result",
|
|
81
|
+
tool_use_id: "",
|
|
82
|
+
content:
|
|
83
|
+
typeof tr.content === "string"
|
|
84
|
+
? tr.content
|
|
85
|
+
: JSON.stringify(tr.content),
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// content column is NOT NULL — ensure at least one block
|
|
91
|
+
if (blocks.length === 0) {
|
|
92
|
+
blocks.push({ type: "text", text: "" });
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return blocks;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export const recoverConversationsFromDiskViewMigration: WorkspaceMigration = {
|
|
99
|
+
id: "028-recover-conversations-from-disk-view",
|
|
100
|
+
description:
|
|
101
|
+
"Recover conversations from disk-view directories into the database",
|
|
102
|
+
|
|
103
|
+
run(workspaceDir: string): void {
|
|
104
|
+
const conversationsDir = join(workspaceDir, "conversations");
|
|
105
|
+
if (!existsSync(conversationsDir)) return;
|
|
106
|
+
|
|
107
|
+
const db = getDb();
|
|
108
|
+
|
|
109
|
+
let entries: string[];
|
|
110
|
+
try {
|
|
111
|
+
entries = readdirSync(conversationsDir);
|
|
112
|
+
} catch (err) {
|
|
113
|
+
log.warn(`Failed to read conversations directory: ${err}`);
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
let recovered = 0;
|
|
118
|
+
let skipped = 0;
|
|
119
|
+
let errors = 0;
|
|
120
|
+
|
|
121
|
+
for (const entry of entries) {
|
|
122
|
+
const dirPath = join(conversationsDir, entry);
|
|
123
|
+
|
|
124
|
+
// Skip non-directories
|
|
125
|
+
try {
|
|
126
|
+
if (!statSync(dirPath).isDirectory()) {
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
} catch {
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Read and parse meta.json
|
|
134
|
+
const metaPath = join(dirPath, "meta.json");
|
|
135
|
+
if (!existsSync(metaPath)) {
|
|
136
|
+
log.warn(
|
|
137
|
+
`Skipping ${entry}: missing meta.json`,
|
|
138
|
+
);
|
|
139
|
+
skipped++;
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
let meta: DiskMeta;
|
|
144
|
+
try {
|
|
145
|
+
meta = JSON.parse(readFileSync(metaPath, "utf-8")) as DiskMeta;
|
|
146
|
+
} catch (err) {
|
|
147
|
+
log.warn(
|
|
148
|
+
`Skipping ${entry}: malformed meta.json: ${err}`,
|
|
149
|
+
);
|
|
150
|
+
skipped++;
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (!meta.id) {
|
|
155
|
+
log.warn(
|
|
156
|
+
`Skipping ${entry}: meta.json missing id`,
|
|
157
|
+
);
|
|
158
|
+
skipped++;
|
|
159
|
+
continue;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Check if conversation already exists in DB (idempotency)
|
|
163
|
+
const existing = db
|
|
164
|
+
.select()
|
|
165
|
+
.from(conversations)
|
|
166
|
+
.where(eq(conversations.id, meta.id))
|
|
167
|
+
.get();
|
|
168
|
+
|
|
169
|
+
if (existing) {
|
|
170
|
+
skipped++;
|
|
171
|
+
continue;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// Parse messages.jsonl
|
|
175
|
+
const messagesPath = join(dirPath, "messages.jsonl");
|
|
176
|
+
const messageRecords: DiskMessageRecord[] = [];
|
|
177
|
+
|
|
178
|
+
if (existsSync(messagesPath)) {
|
|
179
|
+
try {
|
|
180
|
+
const raw = readFileSync(messagesPath, "utf-8");
|
|
181
|
+
for (const line of raw.split("\n")) {
|
|
182
|
+
const trimmed = line.trim();
|
|
183
|
+
if (!trimmed) continue;
|
|
184
|
+
try {
|
|
185
|
+
messageRecords.push(
|
|
186
|
+
JSON.parse(trimmed) as DiskMessageRecord,
|
|
187
|
+
);
|
|
188
|
+
} catch {
|
|
189
|
+
log.warn(
|
|
190
|
+
`Skipping malformed JSONL line in ${entry}/messages.jsonl`,
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
} catch (err) {
|
|
195
|
+
log.warn(
|
|
196
|
+
`Failed to read messages.jsonl for ${entry}: ${err}`,
|
|
197
|
+
);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// Compute timestamps
|
|
202
|
+
const createdAt = parseEpochMs(meta.createdAt) ?? Date.now();
|
|
203
|
+
const updatedAt = parseEpochMs(meta.updatedAt) ?? createdAt;
|
|
204
|
+
|
|
205
|
+
// Insert conversation + messages in a transaction
|
|
206
|
+
try {
|
|
207
|
+
db.transaction((tx) => {
|
|
208
|
+
tx.insert(conversations)
|
|
209
|
+
.values({
|
|
210
|
+
id: meta.id,
|
|
211
|
+
title: meta.title ?? null,
|
|
212
|
+
createdAt,
|
|
213
|
+
updatedAt,
|
|
214
|
+
conversationType: meta.type ?? "standard",
|
|
215
|
+
originChannel: meta.channel ?? null,
|
|
216
|
+
source: "user",
|
|
217
|
+
memoryScopeId: "default",
|
|
218
|
+
isAutoTitle: 1,
|
|
219
|
+
totalInputTokens: 0,
|
|
220
|
+
totalOutputTokens: 0,
|
|
221
|
+
totalEstimatedCost: 0,
|
|
222
|
+
contextSummary: null,
|
|
223
|
+
contextCompactedMessageCount: 0,
|
|
224
|
+
contextCompactedAt: null,
|
|
225
|
+
originInterface: null,
|
|
226
|
+
forkParentConversationId: null,
|
|
227
|
+
forkParentMessageId: null,
|
|
228
|
+
scheduleJobId: null,
|
|
229
|
+
})
|
|
230
|
+
.run();
|
|
231
|
+
|
|
232
|
+
for (const record of messageRecords) {
|
|
233
|
+
const contentBlocks = buildContentBlocks(record);
|
|
234
|
+
const msgCreatedAt =
|
|
235
|
+
parseEpochMs(record.ts) ?? createdAt;
|
|
236
|
+
|
|
237
|
+
tx.insert(messages)
|
|
238
|
+
.values({
|
|
239
|
+
id: randomUUID(),
|
|
240
|
+
conversationId: meta.id,
|
|
241
|
+
role: record.role,
|
|
242
|
+
content: JSON.stringify(contentBlocks),
|
|
243
|
+
createdAt: msgCreatedAt,
|
|
244
|
+
metadata: null,
|
|
245
|
+
})
|
|
246
|
+
.run();
|
|
247
|
+
}
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
recovered++;
|
|
251
|
+
} catch (err) {
|
|
252
|
+
log.warn(
|
|
253
|
+
`Failed to insert conversation ${meta.id} (${entry}): ${err}`,
|
|
254
|
+
);
|
|
255
|
+
errors++;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
if (recovered > 0 || errors > 0) {
|
|
260
|
+
log.info(
|
|
261
|
+
`Recover conversations from disk-view: recovered=${recovered}, skipped=${skipped}, errors=${errors}`,
|
|
262
|
+
);
|
|
263
|
+
}
|
|
264
|
+
},
|
|
265
|
+
|
|
266
|
+
// No-op: deleting recovered conversation data from the database would cause
|
|
267
|
+
// data loss — the disk-view files are the only remaining copy after the
|
|
268
|
+
// original DB was lost.
|
|
269
|
+
down(_workspaceDir: string): void {},
|
|
270
|
+
};
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, rmdirSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
|
|
4
|
+
import type { WorkspaceMigration } from "./types.js";
|
|
5
|
+
|
|
6
|
+
const INDEX_TEMPLATE = `_ Lines starting with _ are comments - they won't appear in the injected context
|
|
7
|
+
|
|
8
|
+
# Knowledge Base
|
|
9
|
+
|
|
10
|
+
**Remember aggressively.** When you learn ANY fact — a preference, a name, a date, a habit, a plan, an opinion — call \`remember\` immediately. Don't filter, don't judge importance. Remembering too much costs nothing. Forgetting something that mattered costs trust.
|
|
11
|
+
|
|
12
|
+
## Always Loaded
|
|
13
|
+
- essentials.md — Core facts, patterns, and biographical info
|
|
14
|
+
- threads.md — Active commitments, follow-ups, and projects in progress
|
|
15
|
+
- buffer.md — Inbox of recently learned facts (filed periodically)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
## Topics
|
|
19
|
+
`;
|
|
20
|
+
|
|
21
|
+
const ESSENTIALS_TEMPLATE = `_ Lines starting with _ are comments - they won't appear in the injected context
|
|
22
|
+
|
|
23
|
+
# Essentials
|
|
24
|
+
|
|
25
|
+
_ The most important facts — things you'd be embarrassed to forget.
|
|
26
|
+
_ This file is always loaded into every conversation. Keep it focused.
|
|
27
|
+
_ Promote facts here from topic files when they come up constantly.
|
|
28
|
+
_ Demote facts to topic files when they stop being essential.
|
|
29
|
+
`;
|
|
30
|
+
|
|
31
|
+
const THREADS_TEMPLATE = `_ Lines starting with _ are comments - they won't appear in the injected context
|
|
32
|
+
|
|
33
|
+
# Active Threads
|
|
34
|
+
|
|
35
|
+
_ Commitments, follow-ups, and projects in progress.
|
|
36
|
+
_ This file is always loaded into every conversation.
|
|
37
|
+
_ Remove items when they're completed or no longer relevant.
|
|
38
|
+
`;
|
|
39
|
+
|
|
40
|
+
export const seedPkbMigration: WorkspaceMigration = {
|
|
41
|
+
id: "029-seed-pkb",
|
|
42
|
+
description: "Create pkb/ knowledge base directory with seed files",
|
|
43
|
+
|
|
44
|
+
down(workspaceDir: string): void {
|
|
45
|
+
// Best-effort: only remove empty directories. Never delete user content.
|
|
46
|
+
const pkbDir = join(workspaceDir, "pkb");
|
|
47
|
+
if (!existsSync(pkbDir)) return;
|
|
48
|
+
|
|
49
|
+
try {
|
|
50
|
+
// Try removing subdirectories first, then the root. rmdirSync fails
|
|
51
|
+
// on non-empty directories, which is exactly what we want.
|
|
52
|
+
for (const sub of ["archive"]) {
|
|
53
|
+
try {
|
|
54
|
+
rmdirSync(join(pkbDir, sub));
|
|
55
|
+
} catch {
|
|
56
|
+
// Non-empty or doesn't exist — skip
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
rmdirSync(pkbDir);
|
|
60
|
+
} catch {
|
|
61
|
+
// Non-empty — leave it alone
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
|
|
65
|
+
run(workspaceDir: string): void {
|
|
66
|
+
const pkbDir = join(workspaceDir, "pkb");
|
|
67
|
+
mkdirSync(pkbDir, { recursive: true });
|
|
68
|
+
mkdirSync(join(pkbDir, "archive"), { recursive: true });
|
|
69
|
+
|
|
70
|
+
// Seed files only if they don't already exist (idempotent)
|
|
71
|
+
const seeds: Array<[string, string]> = [
|
|
72
|
+
["INDEX.md", INDEX_TEMPLATE],
|
|
73
|
+
["essentials.md", ESSENTIALS_TEMPLATE],
|
|
74
|
+
["threads.md", THREADS_TEMPLATE],
|
|
75
|
+
["buffer.md", ""],
|
|
76
|
+
];
|
|
77
|
+
|
|
78
|
+
for (const [filename, content] of seeds) {
|
|
79
|
+
const filePath = join(pkbDir, filename);
|
|
80
|
+
if (!existsSync(filePath)) {
|
|
81
|
+
writeFileSync(filePath, content, "utf-8");
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
};
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { existsSync, readFileSync, unlinkSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
|
|
4
|
+
import type { WorkspaceMigration } from "./types.js";
|
|
5
|
+
|
|
6
|
+
const AUTOINJECT_TEMPLATE = `_ Lines starting with _ are comments - they won't appear in the injected context
|
|
7
|
+
_ List one PKB filename per line. These files are loaded into every conversation.
|
|
8
|
+
_ Remove a line to stop autoinjecting that file. Add new filenames to inject more.
|
|
9
|
+
|
|
10
|
+
INDEX.md
|
|
11
|
+
essentials.md
|
|
12
|
+
threads.md
|
|
13
|
+
buffer.md
|
|
14
|
+
`;
|
|
15
|
+
|
|
16
|
+
const INDEX_ENTRY = "- _autoinject.md — Controls which files are loaded into every conversation";
|
|
17
|
+
|
|
18
|
+
export const seedPkbAutoinjectMigration: WorkspaceMigration = {
|
|
19
|
+
id: "030-seed-pkb-autoinject",
|
|
20
|
+
description: "Seed pkb/_autoinject.md for configurable PKB autoinjection",
|
|
21
|
+
|
|
22
|
+
run(workspaceDir: string): void {
|
|
23
|
+
const pkbDir = join(workspaceDir, "pkb");
|
|
24
|
+
if (!existsSync(pkbDir)) return;
|
|
25
|
+
|
|
26
|
+
// Seed _autoinject.md if it doesn't already exist
|
|
27
|
+
const autoinjectPath = join(pkbDir, "_autoinject.md");
|
|
28
|
+
if (!existsSync(autoinjectPath)) {
|
|
29
|
+
writeFileSync(autoinjectPath, AUTOINJECT_TEMPLATE, "utf-8");
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Append _autoinject.md entry to INDEX.md if not already present
|
|
33
|
+
const indexPath = join(pkbDir, "INDEX.md");
|
|
34
|
+
if (existsSync(indexPath)) {
|
|
35
|
+
try {
|
|
36
|
+
const indexContent = readFileSync(indexPath, "utf-8");
|
|
37
|
+
if (!indexContent.includes("_autoinject.md")) {
|
|
38
|
+
// Insert after the last "Always Loaded" entry (buffer.md line)
|
|
39
|
+
const bufferLine = "- buffer.md";
|
|
40
|
+
const bufferIdx = indexContent.indexOf(bufferLine);
|
|
41
|
+
if (bufferIdx !== -1) {
|
|
42
|
+
const endOfBufferLine = indexContent.indexOf("\n", bufferIdx);
|
|
43
|
+
const insertAt =
|
|
44
|
+
endOfBufferLine === -1 ? indexContent.length : endOfBufferLine;
|
|
45
|
+
const updated =
|
|
46
|
+
indexContent.slice(0, insertAt) +
|
|
47
|
+
"\n" +
|
|
48
|
+
INDEX_ENTRY +
|
|
49
|
+
indexContent.slice(insertAt);
|
|
50
|
+
writeFileSync(indexPath, updated, "utf-8");
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
} catch {
|
|
54
|
+
// INDEX.md unreadable — skip
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
|
|
59
|
+
down(workspaceDir: string): void {
|
|
60
|
+
const autoinjectPath = join(workspaceDir, "pkb", "_autoinject.md");
|
|
61
|
+
if (!existsSync(autoinjectPath)) return;
|
|
62
|
+
|
|
63
|
+
try {
|
|
64
|
+
const content = readFileSync(autoinjectPath, "utf-8");
|
|
65
|
+
// Only delete if content matches the template (preserve user edits)
|
|
66
|
+
if (content === AUTOINJECT_TEMPLATE) {
|
|
67
|
+
unlinkSync(autoinjectPath);
|
|
68
|
+
}
|
|
69
|
+
} catch {
|
|
70
|
+
// Unreadable — leave it alone
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
};
|
|
@@ -24,6 +24,9 @@ import { moveRuntimeFilesToWorkspaceMigration } from "./024-move-runtime-files-t
|
|
|
24
24
|
import { removeOauthAppSetupSkillsMigration } from "./025-remove-oauth-app-setup-skills.js";
|
|
25
25
|
import { backfillInstallMetaMigration } from "./026-backfill-install-meta.js";
|
|
26
26
|
import { removeOrphanedOptimizedImagesCacheMigration } from "./027-remove-orphaned-optimized-images-cache.js";
|
|
27
|
+
import { recoverConversationsFromDiskViewMigration } from "./028-recover-conversations-from-disk-view.js";
|
|
28
|
+
import { seedPkbMigration } from "./029-seed-pkb.js";
|
|
29
|
+
import { seedPkbAutoinjectMigration } from "./030-seed-pkb-autoinject.js";
|
|
27
30
|
import { migrateToWorkspaceVolumeMigration } from "./migrate-to-workspace-volume.js";
|
|
28
31
|
import type { WorkspaceMigration } from "./types.js";
|
|
29
32
|
|
|
@@ -59,4 +62,7 @@ export const WORKSPACE_MIGRATIONS: WorkspaceMigration[] = [
|
|
|
59
62
|
removeOauthAppSetupSkillsMigration,
|
|
60
63
|
backfillInstallMetaMigration,
|
|
61
64
|
removeOrphanedOptimizedImagesCacheMigration,
|
|
65
|
+
recoverConversationsFromDiskViewMigration,
|
|
66
|
+
seedPkbMigration,
|
|
67
|
+
seedPkbAutoinjectMigration,
|
|
62
68
|
];
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import type { TopLevelSnapshot } from "./top-level-scanner.js";
|
|
2
2
|
|
|
3
3
|
export interface WorkspaceTopLevelRenderOptions {
|
|
4
|
-
|
|
5
|
-
currentConversationAttachmentsPath?: string | null;
|
|
4
|
+
conversationAttachmentsPath?: string | null;
|
|
6
5
|
}
|
|
7
6
|
|
|
8
7
|
/**
|
|
@@ -15,19 +14,16 @@ export function renderWorkspaceTopLevelContext(
|
|
|
15
14
|
snapshot: TopLevelSnapshot,
|
|
16
15
|
options: WorkspaceTopLevelRenderOptions = {},
|
|
17
16
|
): string {
|
|
18
|
-
const lines: string[] = ["<
|
|
17
|
+
const lines: string[] = ["<workspace>"];
|
|
19
18
|
lines.push(`Root: ${snapshot.rootPath}`);
|
|
20
19
|
lines.push(`Directories: ${snapshot.directories.join(", ")}`);
|
|
21
20
|
lines.push(`Files: ${snapshot.files.join(", ")}`);
|
|
22
|
-
if (options.
|
|
23
|
-
lines.push(`Current conversation
|
|
24
|
-
}
|
|
25
|
-
if (options.currentConversationAttachmentsPath) {
|
|
26
|
-
lines.push(`Attachment files: ${options.currentConversationAttachmentsPath}`);
|
|
21
|
+
if (options.conversationAttachmentsPath) {
|
|
22
|
+
lines.push(`Current conversation attachments: ${options.conversationAttachmentsPath}`);
|
|
27
23
|
}
|
|
28
24
|
if (snapshot.truncated) {
|
|
29
25
|
lines.push("(list truncated — more entries exist)");
|
|
30
26
|
}
|
|
31
|
-
lines.push("</
|
|
27
|
+
lines.push("</workspace>");
|
|
32
28
|
return lines.join("\n");
|
|
33
29
|
}
|