@vellumai/assistant 0.6.0 → 0.6.1
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 +32 -1
- package/docs/architecture/integrations.md +1 -1
- package/docs/architecture/memory.md +21 -24
- package/openapi.yaml +538 -3
- 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__/checker.test.ts +38 -6
- package/src/__tests__/config-schema.test.ts +5 -0
- 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-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__/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__/injection-block.test.ts +24 -0
- 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 +72 -105
- 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__/notification-decision-recipient-context.test.ts +282 -0
- package/src/__tests__/onboarding-template-contract.test.ts +62 -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__/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__/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-tools.test.ts +17 -27
- package/src/__tests__/test-preload.ts +4 -0
- package/src/__tests__/tool-executor.test.ts +4 -26
- 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__/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/agent/loop.ts +6 -0
- package/src/approvals/guardian-request-resolvers.ts +24 -0
- package/src/avatar/traits-png-sync.ts +3 -3
- package/src/cli/__tests__/run-assistant-command.ts +29 -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/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/connect.ts +14 -5
- package/src/cli/commands/routes.ts +396 -0
- package/src/cli/commands/skills.ts +130 -20
- package/src/cli/program.ts +2 -0
- package/src/cli.ts +1 -120
- package/src/config/bundled-skills/app-builder/SKILL.md +4 -1
- package/src/config/bundled-skills/gmail/SKILL.md +2 -2
- 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/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/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/daemon/app-source-watcher.ts +93 -0
- package/src/daemon/config-watcher.ts +79 -1
- package/src/daemon/conversation-agent-loop-handlers.ts +20 -0
- package/src/daemon/conversation-agent-loop.ts +158 -65
- package/src/daemon/conversation-history.ts +4 -19
- package/src/daemon/conversation-lifecycle.ts +8 -14
- package/src/daemon/conversation-process.ts +13 -7
- package/src/daemon/conversation-runtime-assembly.ts +300 -306
- package/src/daemon/conversation-tool-setup.ts +44 -14
- package/src/daemon/conversation-workspace.ts +1 -2
- package/src/daemon/conversation.ts +18 -0
- package/src/daemon/date-context.ts +26 -53
- package/src/daemon/first-greeting.ts +1 -1
- package/src/daemon/handlers/conversations.ts +4 -7
- package/src/daemon/handlers/shared.test.ts +143 -0
- package/src/daemon/handlers/shared.ts +63 -5
- package/src/daemon/handlers/skills.ts +11 -18
- package/src/daemon/lifecycle.ts +199 -157
- package/src/daemon/message-types/conversations.ts +25 -6
- package/src/daemon/message-types/messages.ts +9 -1
- package/src/daemon/message-types/schedules.ts +1 -0
- package/src/daemon/message-types/settings.ts +6 -0
- package/src/daemon/profiler-run-store.ts +557 -0
- package/src/daemon/server.ts +89 -9
- package/src/daemon/shutdown-handlers.ts +5 -0
- package/src/daemon/tool-side-effects.ts +23 -3
- 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/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 +136 -107
- package/src/memory/conversation-group-migration.ts +1 -1
- 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/graph/bootstrap.ts +75 -66
- package/src/memory/graph/capability-seed.ts +167 -15
- 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/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/notifications/copy-composer.ts +86 -0
- package/src/notifications/decision-engine.ts +35 -0
- package/src/permissions/checker.ts +12 -1
- package/src/permissions/permission-mode-store.ts +180 -0
- package/src/permissions/permission-mode.ts +31 -0
- package/src/permissions/workspace-policy.ts +9 -0
- package/src/prompts/system-prompt.ts +59 -7
- package/src/prompts/templates/BOOTSTRAP-REFERENCE.md +100 -0
- package/src/prompts/templates/BOOTSTRAP.md +70 -165
- package/src/prompts/templates/HEARTBEAT.md +3 -1
- package/src/prompts/templates/SOUL.md +25 -4
- package/src/prompts/templates/UPDATES.md +8 -0
- package/src/providers/anthropic/client.ts +107 -219
- package/src/runtime/auth/route-policy.ts +23 -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 +173 -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 +264 -44
- 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-routes.ts +23 -275
- 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/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 -0
- 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/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/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/tool-manifest.ts +6 -0
- package/src/tools/types.ts +2 -0
- package/src/util/logger.ts +1 -1
- package/src/util/platform.ts +50 -17
- 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 +84 -0
- package/src/workspace/migrations/registry.ts +4 -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
|
@@ -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,84 @@
|
|
|
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
|
+
## Topics
|
|
18
|
+
`;
|
|
19
|
+
|
|
20
|
+
const ESSENTIALS_TEMPLATE = `_ Lines starting with _ are comments - they won't appear in the injected context
|
|
21
|
+
|
|
22
|
+
# Essentials
|
|
23
|
+
|
|
24
|
+
_ The most important facts — things you'd be embarrassed to forget.
|
|
25
|
+
_ This file is always loaded into every conversation. Keep it focused.
|
|
26
|
+
_ Promote facts here from topic files when they come up constantly.
|
|
27
|
+
_ Demote facts to topic files when they stop being essential.
|
|
28
|
+
`;
|
|
29
|
+
|
|
30
|
+
const THREADS_TEMPLATE = `_ Lines starting with _ are comments - they won't appear in the injected context
|
|
31
|
+
|
|
32
|
+
# Active Threads
|
|
33
|
+
|
|
34
|
+
_ Commitments, follow-ups, and projects in progress.
|
|
35
|
+
_ This file is always loaded into every conversation.
|
|
36
|
+
_ Remove items when they're completed or no longer relevant.
|
|
37
|
+
`;
|
|
38
|
+
|
|
39
|
+
export const seedPkbMigration: WorkspaceMigration = {
|
|
40
|
+
id: "029-seed-pkb",
|
|
41
|
+
description: "Create pkb/ knowledge base directory with seed files",
|
|
42
|
+
|
|
43
|
+
down(workspaceDir: string): void {
|
|
44
|
+
// Best-effort: only remove empty directories. Never delete user content.
|
|
45
|
+
const pkbDir = join(workspaceDir, "pkb");
|
|
46
|
+
if (!existsSync(pkbDir)) return;
|
|
47
|
+
|
|
48
|
+
try {
|
|
49
|
+
// Try removing subdirectories first, then the root. rmdirSync fails
|
|
50
|
+
// on non-empty directories, which is exactly what we want.
|
|
51
|
+
for (const sub of ["archive"]) {
|
|
52
|
+
try {
|
|
53
|
+
rmdirSync(join(pkbDir, sub));
|
|
54
|
+
} catch {
|
|
55
|
+
// Non-empty or doesn't exist — skip
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
rmdirSync(pkbDir);
|
|
59
|
+
} catch {
|
|
60
|
+
// Non-empty — leave it alone
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
|
|
64
|
+
run(workspaceDir: string): void {
|
|
65
|
+
const pkbDir = join(workspaceDir, "pkb");
|
|
66
|
+
mkdirSync(pkbDir, { recursive: true });
|
|
67
|
+
mkdirSync(join(pkbDir, "archive"), { recursive: true });
|
|
68
|
+
|
|
69
|
+
// Seed files only if they don't already exist (idempotent)
|
|
70
|
+
const seeds: Array<[string, string]> = [
|
|
71
|
+
["INDEX.md", INDEX_TEMPLATE],
|
|
72
|
+
["essentials.md", ESSENTIALS_TEMPLATE],
|
|
73
|
+
["threads.md", THREADS_TEMPLATE],
|
|
74
|
+
["buffer.md", ""],
|
|
75
|
+
];
|
|
76
|
+
|
|
77
|
+
for (const [filename, content] of seeds) {
|
|
78
|
+
const filePath = join(pkbDir, filename);
|
|
79
|
+
if (!existsSync(filePath)) {
|
|
80
|
+
writeFileSync(filePath, content, "utf-8");
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
};
|
|
@@ -24,6 +24,8 @@ 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";
|
|
27
29
|
import { migrateToWorkspaceVolumeMigration } from "./migrate-to-workspace-volume.js";
|
|
28
30
|
import type { WorkspaceMigration } from "./types.js";
|
|
29
31
|
|
|
@@ -59,4 +61,6 @@ export const WORKSPACE_MIGRATIONS: WorkspaceMigration[] = [
|
|
|
59
61
|
removeOauthAppSetupSkillsMigration,
|
|
60
62
|
backfillInstallMetaMigration,
|
|
61
63
|
removeOrphanedOptimizedImagesCacheMigration,
|
|
64
|
+
recoverConversationsFromDiskViewMigration,
|
|
65
|
+
seedPkbMigration,
|
|
62
66
|
];
|
|
@@ -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
|
}
|