@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
|
@@ -26,6 +26,45 @@ export function getConversationDirName(
|
|
|
26
26
|
return `${getConversationDirTimestamp(createdAtMs)}_${id}`;
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
+
/**
|
|
30
|
+
* Parse a canonical conversation directory name (`<ISO-with-dashes>_<id>`)
|
|
31
|
+
* back into its components. Returns null for names that don't match the
|
|
32
|
+
* canonical format — callers should treat null as "skip this entry"
|
|
33
|
+
* rather than as an error.
|
|
34
|
+
*
|
|
35
|
+
* Inverse of {@link getConversationDirName}. Does NOT parse the legacy
|
|
36
|
+
* `<id>_<ISO-with-dashes>` format.
|
|
37
|
+
*/
|
|
38
|
+
export function parseConversationDirName(
|
|
39
|
+
name: string,
|
|
40
|
+
): { conversationId: string; createdAtMs: number } | null {
|
|
41
|
+
// Canonical format: YYYY-MM-DDTHH-MM-SS.sssZ_<uuid>
|
|
42
|
+
// The timestamp portion has 4 hyphens (date + time-component separators)
|
|
43
|
+
// and ends in `Z`. Anchor the regex to enforce that.
|
|
44
|
+
const match = name.match(
|
|
45
|
+
/^(\d{4}-\d{2}-\d{2})T(\d{2})-(\d{2})-(\d{2}\.\d{1,9}Z)_(.+)$/,
|
|
46
|
+
);
|
|
47
|
+
if (!match) return null;
|
|
48
|
+
const [, datePart, hh, mm, ssAndMs, conversationId] = match;
|
|
49
|
+
const iso = `${datePart}T${hh}:${mm}:${ssAndMs}`;
|
|
50
|
+
const ms = Date.parse(iso);
|
|
51
|
+
if (Number.isNaN(ms)) return null;
|
|
52
|
+
if (!conversationId) return null;
|
|
53
|
+
// Reject conversation IDs that are path-traversal-shaped or contain
|
|
54
|
+
// path separators. These are never valid conversation IDs and would
|
|
55
|
+
// be a defense-in-depth concern if parsed.conversationId is later used
|
|
56
|
+
// to construct filesystem paths.
|
|
57
|
+
if (
|
|
58
|
+
conversationId === "." ||
|
|
59
|
+
conversationId === ".." ||
|
|
60
|
+
conversationId.includes("/") ||
|
|
61
|
+
conversationId.includes("\\")
|
|
62
|
+
) {
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
return { conversationId, createdAtMs: ms };
|
|
66
|
+
}
|
|
67
|
+
|
|
29
68
|
/**
|
|
30
69
|
* Return the absolute path to a conversation's timestamp-first disk-view
|
|
31
70
|
* directory.
|
|
@@ -57,15 +57,45 @@ export function ensureGroupMigration(): void {
|
|
|
57
57
|
}
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
-
// 3. Seed system groups (
|
|
60
|
+
// 3. Seed system groups (four: pinned, scheduled, background, all)
|
|
61
|
+
const now = Math.floor(Date.now() / 1000);
|
|
61
62
|
rawExec(`
|
|
62
|
-
INSERT OR IGNORE INTO conversation_groups (id, name, sort_position, is_system_group)
|
|
63
|
+
INSERT OR IGNORE INTO conversation_groups (id, name, sort_position, is_system_group, created_at, updated_at)
|
|
63
64
|
VALUES
|
|
64
|
-
('system:pinned', 'Pinned', 0, TRUE),
|
|
65
|
-
('system:scheduled', 'Scheduled', 1, TRUE),
|
|
66
|
-
('system:background', 'Background', 2, TRUE)
|
|
65
|
+
('system:pinned', 'Pinned', 0, TRUE, ${now}, ${now}),
|
|
66
|
+
('system:scheduled', 'Scheduled', 1, TRUE, ${now}, ${now}),
|
|
67
|
+
('system:background', 'Background', 2, TRUE, ${now}, ${now}),
|
|
68
|
+
('system:all', 'Recents', 3, TRUE, ${now}, ${now})
|
|
67
69
|
`);
|
|
68
70
|
|
|
71
|
+
// One-time migration: move system:all to sortPosition 3 (from 999999).
|
|
72
|
+
// Bump custom groups at position 3+ up by 1 to make room. Wrapped in a
|
|
73
|
+
// transaction so a crash between the shift and the sentinel can't cause
|
|
74
|
+
// repeated drift on restart.
|
|
75
|
+
const sortShiftDone = rawGet<{ id: string }>(
|
|
76
|
+
"SELECT id FROM conversation_groups WHERE id = '_sort_shift_complete'",
|
|
77
|
+
);
|
|
78
|
+
if (!sortShiftDone) {
|
|
79
|
+
try {
|
|
80
|
+
rawExec("BEGIN");
|
|
81
|
+
rawRun(
|
|
82
|
+
"UPDATE conversation_groups SET sort_position = sort_position + 1 WHERE is_system_group = 0 AND sort_position >= 3",
|
|
83
|
+
);
|
|
84
|
+
rawRun(
|
|
85
|
+
"UPDATE conversation_groups SET sort_position = 3 WHERE id = 'system:all' AND sort_position != 3",
|
|
86
|
+
);
|
|
87
|
+
rawRun(
|
|
88
|
+
`INSERT OR IGNORE INTO conversation_groups (id, name, sort_position, is_system_group, created_at, updated_at)
|
|
89
|
+
VALUES ('_sort_shift_complete', '_sort_shift_complete', -1, TRUE, ${now}, ${now})`,
|
|
90
|
+
);
|
|
91
|
+
rawExec("COMMIT");
|
|
92
|
+
} catch (err) {
|
|
93
|
+
rawExec("ROLLBACK");
|
|
94
|
+
log.error({ err }, "Sort-position shift transaction failed, rolled back");
|
|
95
|
+
throw err;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
69
99
|
// 4. One-time backfill (guard: persistent marker prevents re-running on restart)
|
|
70
100
|
//
|
|
71
101
|
// The backfill sets group_id on existing conversations based on their attributes.
|
|
@@ -118,7 +148,7 @@ export function ensureGroupMigration(): void {
|
|
|
118
148
|
// Step B: Scheduled -> system:scheduled (schedule/reminder source or has schedule_job_id)
|
|
119
149
|
rawExec(`
|
|
120
150
|
UPDATE conversations SET group_id = 'system:scheduled'
|
|
121
|
-
WHERE (source IN ('schedule', 'reminder') OR schedule_job_id IS NOT NULL)
|
|
151
|
+
WHERE (source IN ('schedule', 'reminder') OR schedule_job_id IS NOT NULL OR conversation_type = 'scheduled')
|
|
122
152
|
AND group_id IS NULL
|
|
123
153
|
`);
|
|
124
154
|
|
|
@@ -153,5 +183,35 @@ export function ensureGroupMigration(): void {
|
|
|
153
183
|
}
|
|
154
184
|
}
|
|
155
185
|
|
|
186
|
+
// 5. One-time backfill: assign all ungrouped conversations to system:all
|
|
187
|
+
//
|
|
188
|
+
// Separate from the initial backfill above because system:all is added later.
|
|
189
|
+
// Uses its own sentinel so it runs exactly once, even on existing installations
|
|
190
|
+
// where the original backfill already completed.
|
|
191
|
+
const allBackfillDone = rawGet<{ id: string }>(
|
|
192
|
+
"SELECT id FROM conversation_groups WHERE id = '_backfill_all_complete'",
|
|
193
|
+
);
|
|
194
|
+
|
|
195
|
+
if (!allBackfillDone) {
|
|
196
|
+
try {
|
|
197
|
+
rawExec("BEGIN");
|
|
198
|
+
|
|
199
|
+
rawExec(`
|
|
200
|
+
UPDATE conversations SET group_id = 'system:all' WHERE group_id IS NULL
|
|
201
|
+
`);
|
|
202
|
+
|
|
203
|
+
rawExec(`
|
|
204
|
+
INSERT OR IGNORE INTO conversation_groups (id, name, sort_position, is_system_group)
|
|
205
|
+
VALUES ('_backfill_all_complete', '_backfill_all_complete', -1, TRUE)
|
|
206
|
+
`);
|
|
207
|
+
|
|
208
|
+
rawExec("COMMIT");
|
|
209
|
+
} catch (err) {
|
|
210
|
+
rawExec("ROLLBACK");
|
|
211
|
+
log.error({ err }, "system:all backfill transaction failed, rolled back");
|
|
212
|
+
throw err;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
156
216
|
migrated = true;
|
|
157
217
|
}
|
|
@@ -14,7 +14,22 @@ const log = getLogger("conversation-store");
|
|
|
14
14
|
* Build an FTS5 MATCH query string from natural text by extracting tokens.
|
|
15
15
|
* Used for messages_fts full-text search over conversation content.
|
|
16
16
|
*/
|
|
17
|
-
export function buildFtsMatchQuery(
|
|
17
|
+
export function buildFtsMatchQuery(
|
|
18
|
+
text: string,
|
|
19
|
+
opts?: { allowFts5Syntax?: boolean },
|
|
20
|
+
): string | null {
|
|
21
|
+
// If the query already contains FTS5 operators, pass it through directly
|
|
22
|
+
// so callers (e.g. the archive recall tool) can use exact-phrase, AND, OR,
|
|
23
|
+
// NOT, NEAR syntax. Only enabled when the caller explicitly opts in —
|
|
24
|
+
// user-facing search should always go through normal tokenization to avoid
|
|
25
|
+
// FTS5 boolean semantics leaking into sidebar/global search.
|
|
26
|
+
if (
|
|
27
|
+
opts?.allowFts5Syntax &&
|
|
28
|
+
/\bAND\b|\bOR\b|\bNOT\b|\bNEAR\s*\(|"[^"]+"/.test(text)
|
|
29
|
+
) {
|
|
30
|
+
return text.trim();
|
|
31
|
+
}
|
|
32
|
+
|
|
18
33
|
const tokens = text
|
|
19
34
|
.toLowerCase()
|
|
20
35
|
.split(/[^a-z0-9_]+/g)
|
|
@@ -22,7 +37,8 @@ export function buildFtsMatchQuery(text: string): string | null {
|
|
|
22
37
|
.filter((token) => token.length >= 2);
|
|
23
38
|
if (tokens.length === 0) return null;
|
|
24
39
|
const unique = [...new Set(tokens)].slice(0, 24);
|
|
25
|
-
|
|
40
|
+
// Space-separated quoted tokens are implicit AND in FTS5.
|
|
41
|
+
return unique.map((token) => `"${token.replace(/"/g, '""')}"`).join(" ");
|
|
26
42
|
}
|
|
27
43
|
|
|
28
44
|
export function listConversations(
|
|
@@ -34,23 +50,49 @@ export function listConversations(
|
|
|
34
50
|
ensureGroupMigration();
|
|
35
51
|
const db = getDb();
|
|
36
52
|
const where = backgroundOnly
|
|
37
|
-
? sql`${conversations.conversationType}
|
|
38
|
-
: sql`${conversations.conversationType} NOT IN ('background', 'private')`;
|
|
53
|
+
? sql`${conversations.conversationType} IN ('background', 'scheduled') AND (${conversations.source} IS NULL OR ${conversations.source} != 'subagent')`
|
|
54
|
+
: sql`${conversations.conversationType} NOT IN ('background', 'private', 'scheduled')`;
|
|
39
55
|
const query = db
|
|
40
56
|
.select()
|
|
41
57
|
.from(conversations)
|
|
42
58
|
.where(where)
|
|
43
|
-
.orderBy(
|
|
59
|
+
.orderBy(
|
|
60
|
+
desc(
|
|
61
|
+
sql`COALESCE(${conversations.lastMessageAt}, ${conversations.updatedAt})`,
|
|
62
|
+
),
|
|
63
|
+
)
|
|
44
64
|
.limit(limit ?? 100)
|
|
45
65
|
.offset(offset);
|
|
46
66
|
return query.all().map(parseConversation);
|
|
47
67
|
}
|
|
48
68
|
|
|
69
|
+
export function listPinnedConversations(): ConversationRow[] {
|
|
70
|
+
ensureDisplayOrderMigration();
|
|
71
|
+
ensureGroupMigration();
|
|
72
|
+
const db = getDb();
|
|
73
|
+
const query = db
|
|
74
|
+
.select()
|
|
75
|
+
.from(conversations)
|
|
76
|
+
.where(
|
|
77
|
+
and(
|
|
78
|
+
sql`${conversations.conversationType} NOT IN ('background', 'private', 'scheduled')`,
|
|
79
|
+
sql`is_pinned = 1`,
|
|
80
|
+
),
|
|
81
|
+
)
|
|
82
|
+
.orderBy(
|
|
83
|
+
sql`COALESCE(display_order, 999999) ASC`,
|
|
84
|
+
desc(
|
|
85
|
+
sql`COALESCE(${conversations.lastMessageAt}, ${conversations.updatedAt})`,
|
|
86
|
+
),
|
|
87
|
+
);
|
|
88
|
+
return query.all().map(parseConversation);
|
|
89
|
+
}
|
|
90
|
+
|
|
49
91
|
export function countConversations(backgroundOnly = false): number {
|
|
50
92
|
const db = getDb();
|
|
51
93
|
const where = backgroundOnly
|
|
52
|
-
? sql`${conversations.conversationType}
|
|
53
|
-
: sql`${conversations.conversationType} NOT IN ('background', 'private')`;
|
|
94
|
+
? sql`${conversations.conversationType} IN ('background', 'scheduled') AND (${conversations.source} IS NULL OR ${conversations.source} != 'subagent')`
|
|
95
|
+
: sql`${conversations.conversationType} NOT IN ('background', 'private', 'scheduled')`;
|
|
54
96
|
const [{ total }] = db
|
|
55
97
|
.select({ total: count() })
|
|
56
98
|
.from(conversations)
|
|
@@ -65,9 +107,13 @@ export function getLatestConversation(): ConversationRow | null {
|
|
|
65
107
|
.select()
|
|
66
108
|
.from(conversations)
|
|
67
109
|
.where(
|
|
68
|
-
sql`${conversations.conversationType} NOT IN ('background', 'private')`,
|
|
110
|
+
sql`${conversations.conversationType} NOT IN ('background', 'private', 'scheduled')`,
|
|
111
|
+
)
|
|
112
|
+
.orderBy(
|
|
113
|
+
desc(
|
|
114
|
+
sql`COALESCE(${conversations.lastMessageAt}, ${conversations.updatedAt})`,
|
|
115
|
+
),
|
|
69
116
|
)
|
|
70
|
-
.orderBy(desc(conversations.updatedAt))
|
|
71
117
|
.limit(1)
|
|
72
118
|
.get();
|
|
73
119
|
return row ? parseConversation(row) : null;
|
|
@@ -172,7 +218,7 @@ export function searchConversations(
|
|
|
172
218
|
FROM messages_fts f
|
|
173
219
|
JOIN messages m ON m.id = f.message_id
|
|
174
220
|
JOIN conversations c ON c.id = m.conversation_id
|
|
175
|
-
WHERE messages_fts MATCH ? AND c.conversation_type NOT IN ('background', 'private')
|
|
221
|
+
WHERE messages_fts MATCH ? AND c.conversation_type NOT IN ('background', 'private', 'scheduled')
|
|
176
222
|
LIMIT 1000
|
|
177
223
|
`,
|
|
178
224
|
ftsMatch,
|
|
@@ -197,7 +243,7 @@ export function searchConversations(
|
|
|
197
243
|
SELECT DISTINCT m.conversation_id
|
|
198
244
|
FROM messages m
|
|
199
245
|
JOIN conversations c ON c.id = m.conversation_id
|
|
200
|
-
WHERE m.content LIKE ? ESCAPE '\\' AND c.conversation_type NOT IN ('background', 'private')
|
|
246
|
+
WHERE m.content LIKE ? ESCAPE '\\' AND c.conversation_type NOT IN ('background', 'private', 'scheduled')
|
|
201
247
|
LIMIT 1000
|
|
202
248
|
`,
|
|
203
249
|
likePattern,
|
|
@@ -211,7 +257,7 @@ export function searchConversations(
|
|
|
211
257
|
.from(conversations)
|
|
212
258
|
.where(
|
|
213
259
|
and(
|
|
214
|
-
sql`${conversations.conversationType} NOT IN ('background', 'private')`,
|
|
260
|
+
sql`${conversations.conversationType} NOT IN ('background', 'private', 'scheduled')`,
|
|
215
261
|
sql`${conversations.title} LIKE ${titlePattern} ESCAPE '\\'`,
|
|
216
262
|
),
|
|
217
263
|
)
|