@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.
Files changed (285) hide show
  1. package/AGENTS.md +4 -0
  2. package/ARCHITECTURE.md +68 -15
  3. package/Dockerfile +2 -2
  4. package/bun.lock +6 -2
  5. package/docker-entrypoint.sh +32 -1
  6. package/docs/architecture/integrations.md +1 -1
  7. package/docs/architecture/memory.md +21 -24
  8. package/openapi.yaml +538 -3
  9. package/package.json +5 -1
  10. package/src/__tests__/anthropic-provider.test.ts +160 -95
  11. package/src/__tests__/app-dir-path-guard.test.ts +1 -0
  12. package/src/__tests__/app-executors.test.ts +47 -1
  13. package/src/__tests__/app-source-watcher.test.ts +159 -0
  14. package/src/__tests__/checker.test.ts +38 -6
  15. package/src/__tests__/config-schema.test.ts +5 -0
  16. package/src/__tests__/conversation-agent-loop-overflow.test.ts +4 -6
  17. package/src/__tests__/conversation-agent-loop.test.ts +4 -51
  18. package/src/__tests__/conversation-history-web-search.test.ts +1 -1
  19. package/src/__tests__/conversation-runtime-assembly.test.ts +653 -832
  20. package/src/__tests__/conversation-runtime-workspace.test.ts +1 -93
  21. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +17 -4
  22. package/src/__tests__/conversation-wipe.test.ts +2 -6
  23. package/src/__tests__/conversation-workspace-cache-state.test.ts +6 -12
  24. package/src/__tests__/conversation-workspace-injection.test.ts +25 -26
  25. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +1 -1
  26. package/src/__tests__/copy-composer-tc-templates.test.ts +335 -0
  27. package/src/__tests__/date-context.test.ts +76 -210
  28. package/src/__tests__/db-schedule-syntax-migration.test.ts +16 -1
  29. package/src/__tests__/file-list-tool.test.ts +219 -0
  30. package/src/__tests__/first-greeting.test.ts +1 -1
  31. package/src/__tests__/heartbeat-service.test.ts +180 -3
  32. package/src/__tests__/identity-routes.test.ts +328 -0
  33. package/src/__tests__/injection-block.test.ts +24 -0
  34. package/src/__tests__/install-skill-routing.test.ts +7 -6
  35. package/src/__tests__/jobs-store-qdrant-breaker.test.ts +15 -14
  36. package/src/__tests__/list-messages-tool-merge.test.ts +300 -0
  37. package/src/__tests__/llm-context-normalization.test.ts +18 -18
  38. package/src/__tests__/llm-context-route-provider.test.ts +101 -0
  39. package/src/__tests__/llm-request-log-turn-query.test.ts +162 -0
  40. package/src/__tests__/log-export-workspace.test.ts +72 -105
  41. package/src/__tests__/mcp-abort-signal.test.ts +5 -0
  42. package/src/__tests__/mcp-client-auth.test.ts +5 -0
  43. package/src/__tests__/memory-recall-log-store.test.ts +132 -0
  44. package/src/__tests__/migration-export-streaming.test.ts +304 -0
  45. package/src/__tests__/migration-import-commit-http.test.ts +11 -10
  46. package/src/__tests__/mock-fetch.ts +87 -0
  47. package/src/__tests__/notification-decision-recipient-context.test.ts +282 -0
  48. package/src/__tests__/onboarding-template-contract.test.ts +62 -14
  49. package/src/__tests__/parser.test.ts +32 -0
  50. package/src/__tests__/permission-checker-host-gate.test.ts +452 -0
  51. package/src/__tests__/permission-controls-v2-flag.test.ts +55 -0
  52. package/src/__tests__/permission-mode-sse.test.ts +418 -0
  53. package/src/__tests__/permission-mode-store.test.ts +277 -0
  54. package/src/__tests__/permission-mode.test.ts +101 -0
  55. package/src/__tests__/platform-bash-auto-approve.test.ts +359 -0
  56. package/src/__tests__/profiler-routes.test.ts +502 -0
  57. package/src/__tests__/profiler-run-store.test.ts +441 -0
  58. package/src/__tests__/proxy-approval-callback.test.ts +4 -75
  59. package/src/__tests__/registry.test.ts +1 -1
  60. package/src/__tests__/sandbox-host-parity.test.ts +5 -4
  61. package/src/__tests__/scheduler-reuse-conversation.test.ts +368 -0
  62. package/src/__tests__/scrub-corrupted-image-attachments.test.ts +278 -0
  63. package/src/__tests__/search-skills-unified.test.ts +4 -3
  64. package/src/__tests__/send-endpoint-busy.test.ts +42 -3
  65. package/src/__tests__/set-permission-mode.test.ts +274 -0
  66. package/src/__tests__/skill-load-feature-flag.test.ts +12 -0
  67. package/src/__tests__/skill-memory.test.ts +2 -783
  68. package/src/__tests__/strip-memory-injections.test.ts +187 -0
  69. package/src/__tests__/subagent-detail.test.ts +84 -0
  70. package/src/__tests__/subagent-disposal.test.ts +308 -0
  71. package/src/__tests__/subagent-manager-notify.test.ts +19 -10
  72. package/src/__tests__/subagent-notify-parent.test.ts +390 -0
  73. package/src/__tests__/subagent-role-registry.test.ts +108 -0
  74. package/src/__tests__/subagent-tool-filtering.test.ts +71 -0
  75. package/src/__tests__/subagent-tools.test.ts +464 -4
  76. package/src/__tests__/system-prompt-ask-mode.test.ts +139 -0
  77. package/src/__tests__/task-memory-cleanup.test.ts +12 -12
  78. package/src/__tests__/terminal-tools.test.ts +17 -27
  79. package/src/__tests__/test-preload.ts +4 -0
  80. package/src/__tests__/tool-executor.test.ts +4 -26
  81. package/src/__tests__/tool-side-effects-slack-dm.test.ts +1 -0
  82. package/src/__tests__/top-level-renderer.test.ts +10 -13
  83. package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +116 -2
  84. package/src/__tests__/workspace-migration-028-recover-conversations-from-disk-view.test.ts +387 -0
  85. package/src/agent/loop.ts +6 -0
  86. package/src/approvals/guardian-request-resolvers.ts +24 -0
  87. package/src/avatar/traits-png-sync.ts +3 -3
  88. package/src/cli/__tests__/run-assistant-command.ts +29 -0
  89. package/src/cli/commands/__tests__/email-download.test.ts +245 -0
  90. package/src/cli/commands/__tests__/email-list.test.ts +192 -0
  91. package/src/cli/commands/__tests__/email-register.test.ts +186 -0
  92. package/src/cli/commands/__tests__/email-send.test.ts +291 -0
  93. package/src/cli/commands/__tests__/email-status.test.ts +181 -0
  94. package/src/cli/commands/__tests__/email-unregister.test.ts +139 -0
  95. package/src/cli/commands/__tests__/routes.test.ts +562 -0
  96. package/src/cli/commands/conversations.ts +1 -8
  97. package/src/cli/commands/email.ts +584 -835
  98. package/src/cli/commands/memory.ts +1 -34
  99. package/src/cli/commands/notifications.ts +7 -2
  100. package/src/cli/commands/oauth/connect.ts +14 -5
  101. package/src/cli/commands/routes.ts +396 -0
  102. package/src/cli/commands/skills.ts +130 -20
  103. package/src/cli/program.ts +2 -0
  104. package/src/cli.ts +1 -120
  105. package/src/config/bundled-skills/app-builder/SKILL.md +4 -1
  106. package/src/config/bundled-skills/gmail/SKILL.md +2 -2
  107. package/src/config/bundled-skills/messaging/SKILL.md +7 -0
  108. package/src/config/bundled-skills/schedule/SKILL.md +22 -2
  109. package/src/config/bundled-skills/schedule/TOOLS.json +8 -0
  110. package/src/config/bundled-skills/settings/tools/avatar-get.ts +3 -13
  111. package/src/config/bundled-skills/settings/tools/avatar-remove.ts +2 -4
  112. package/src/config/bundled-skills/settings/tools/avatar-update.ts +5 -2
  113. package/src/config/bundled-skills/slack/SKILL.md +2 -0
  114. package/src/config/bundled-skills/subagent/SKILL.md +43 -3
  115. package/src/config/bundled-skills/subagent/TOOLS.json +29 -4
  116. package/src/config/env-registry.ts +63 -0
  117. package/src/config/feature-flag-registry.json +17 -1
  118. package/src/config/schema.ts +8 -0
  119. package/src/config/schemas/filing.ts +51 -0
  120. package/src/config/schemas/heartbeat.ts +15 -12
  121. package/src/config/schemas/memory-lifecycle.ts +12 -0
  122. package/src/config/schemas/security.ts +14 -0
  123. package/src/daemon/app-source-watcher.ts +93 -0
  124. package/src/daemon/config-watcher.ts +79 -1
  125. package/src/daemon/conversation-agent-loop-handlers.ts +20 -0
  126. package/src/daemon/conversation-agent-loop.ts +158 -65
  127. package/src/daemon/conversation-history.ts +4 -19
  128. package/src/daemon/conversation-lifecycle.ts +8 -14
  129. package/src/daemon/conversation-process.ts +13 -7
  130. package/src/daemon/conversation-runtime-assembly.ts +300 -306
  131. package/src/daemon/conversation-tool-setup.ts +44 -14
  132. package/src/daemon/conversation-workspace.ts +1 -2
  133. package/src/daemon/conversation.ts +18 -0
  134. package/src/daemon/date-context.ts +26 -53
  135. package/src/daemon/first-greeting.ts +1 -1
  136. package/src/daemon/handlers/conversations.ts +4 -7
  137. package/src/daemon/handlers/shared.test.ts +143 -0
  138. package/src/daemon/handlers/shared.ts +63 -5
  139. package/src/daemon/handlers/skills.ts +11 -18
  140. package/src/daemon/lifecycle.ts +199 -157
  141. package/src/daemon/message-types/conversations.ts +25 -6
  142. package/src/daemon/message-types/messages.ts +9 -1
  143. package/src/daemon/message-types/schedules.ts +1 -0
  144. package/src/daemon/message-types/settings.ts +6 -0
  145. package/src/daemon/profiler-run-store.ts +557 -0
  146. package/src/daemon/server.ts +89 -9
  147. package/src/daemon/shutdown-handlers.ts +5 -0
  148. package/src/daemon/tool-side-effects.ts +23 -3
  149. package/src/export/transcript-formatter.ts +148 -0
  150. package/src/filing/filing-service.ts +228 -0
  151. package/src/heartbeat/heartbeat-service.ts +96 -7
  152. package/src/mcp/client.ts +6 -0
  153. package/src/mcp/mcp-oauth-provider.ts +149 -27
  154. package/src/memory/admin.ts +33 -32
  155. package/src/memory/app-store.ts +69 -0
  156. package/src/memory/conversation-bootstrap.ts +1 -1
  157. package/src/memory/conversation-crud.ts +136 -107
  158. package/src/memory/conversation-group-migration.ts +1 -1
  159. package/src/memory/conversation-queries.ts +58 -12
  160. package/src/memory/conversation-title-service.ts +1 -0
  161. package/src/memory/db-init.ts +182 -376
  162. package/src/memory/graph/bootstrap.ts +75 -66
  163. package/src/memory/graph/capability-seed.ts +167 -15
  164. package/src/memory/graph/consolidation.ts +38 -4
  165. package/src/memory/graph/conversation-graph-memory.ts +133 -104
  166. package/src/memory/graph/extraction-job.ts +9 -4
  167. package/src/memory/graph/extraction.ts +66 -23
  168. package/src/memory/graph/graph-memory-state-store.ts +37 -0
  169. package/src/memory/graph/graph-search.ts +29 -15
  170. package/src/memory/graph/injection.ts +38 -8
  171. package/src/memory/graph/inspect.ts +12 -3
  172. package/src/memory/graph/retriever.ts +365 -262
  173. package/src/memory/graph/store.test.ts +48 -0
  174. package/src/memory/graph/store.ts +150 -11
  175. package/src/memory/graph/tool-handlers.ts +84 -209
  176. package/src/memory/graph/tools.ts +8 -52
  177. package/src/memory/graph/types.ts +24 -0
  178. package/src/memory/job-handlers/cleanup.ts +44 -1
  179. package/src/memory/jobs-store.ts +70 -60
  180. package/src/memory/jobs-worker.ts +44 -28
  181. package/src/memory/llm-request-log-store.ts +96 -12
  182. package/src/memory/memory-recall-log-store.ts +49 -5
  183. package/src/memory/migrations/203-drop-memory-items-tables.ts +33 -1
  184. package/src/memory/migrations/206-memory-graph-node-edits.ts +19 -0
  185. package/src/memory/migrations/206-scrub-corrupted-image-attachments.ts +131 -0
  186. package/src/memory/migrations/207-conversation-graph-memory-state.ts +20 -0
  187. package/src/memory/migrations/208-conversations-last-message-at.ts +35 -0
  188. package/src/memory/migrations/209-strip-thinking-from-consolidated.ts +85 -0
  189. package/src/memory/migrations/210-schedule-reuse-conversation.ts +13 -0
  190. package/src/memory/migrations/211-memory-recall-logs-query-context.ts +21 -0
  191. package/src/memory/migrations/212-llm-request-logs-created-at-index.ts +19 -0
  192. package/src/memory/migrations/index.ts +8 -0
  193. package/src/memory/migrations/registry.ts +8 -0
  194. package/src/memory/schema/conversations.ts +14 -0
  195. package/src/memory/schema/infrastructure.ts +8 -1
  196. package/src/memory/schema/memory-core.ts +0 -51
  197. package/src/memory/schema/memory-graph.ts +15 -0
  198. package/src/memory/task-memory-cleanup.ts +30 -11
  199. package/src/notifications/copy-composer.ts +86 -0
  200. package/src/notifications/decision-engine.ts +35 -0
  201. package/src/permissions/checker.ts +12 -1
  202. package/src/permissions/permission-mode-store.ts +180 -0
  203. package/src/permissions/permission-mode.ts +31 -0
  204. package/src/permissions/workspace-policy.ts +9 -0
  205. package/src/prompts/system-prompt.ts +59 -7
  206. package/src/prompts/templates/BOOTSTRAP-REFERENCE.md +100 -0
  207. package/src/prompts/templates/BOOTSTRAP.md +70 -165
  208. package/src/prompts/templates/HEARTBEAT.md +3 -1
  209. package/src/prompts/templates/SOUL.md +25 -4
  210. package/src/prompts/templates/UPDATES.md +8 -0
  211. package/src/providers/anthropic/client.ts +107 -219
  212. package/src/runtime/auth/route-policy.ts +23 -0
  213. package/src/runtime/http-server.ts +32 -2
  214. package/src/runtime/http-types.ts +12 -1
  215. package/src/runtime/migrations/vbundle-builder.ts +389 -3
  216. package/src/runtime/migrations/vbundle-importer.ts +8 -6
  217. package/src/runtime/routes/__tests__/user-route-dispatcher.test.ts +378 -0
  218. package/src/runtime/routes/app-management-routes.ts +1 -11
  219. package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +26 -0
  220. package/src/runtime/routes/archive-utils.ts +29 -0
  221. package/src/runtime/routes/avatar-routes.ts +2 -9
  222. package/src/runtime/routes/btw-routes.ts +14 -1
  223. package/src/runtime/routes/conversation-analysis-routes.ts +173 -0
  224. package/src/runtime/routes/conversation-management-routes.ts +1 -14
  225. package/src/runtime/routes/conversation-query-routes.ts +49 -3
  226. package/src/runtime/routes/conversation-routes.ts +264 -44
  227. package/src/runtime/routes/heartbeat-routes.ts +4 -10
  228. package/src/runtime/routes/identity-routes.ts +53 -18
  229. package/src/runtime/routes/llm-context-normalization.ts +14 -10
  230. package/src/runtime/routes/log-export-routes.ts +23 -275
  231. package/src/runtime/routes/memory-item-routes.test.ts +168 -233
  232. package/src/runtime/routes/migration-routes.ts +18 -7
  233. package/src/runtime/routes/profiler-routes.ts +350 -0
  234. package/src/runtime/routes/schedule-routes.ts +27 -12
  235. package/src/runtime/routes/settings-routes.ts +95 -8
  236. package/src/runtime/routes/subagents-routes.ts +28 -7
  237. package/src/runtime/routes/user-route-dispatcher.ts +223 -0
  238. package/src/runtime/routes/user-routes.ts +41 -0
  239. package/src/runtime/routes/workspace-routes.ts +0 -1
  240. package/src/schedule/schedule-store.ts +30 -0
  241. package/src/schedule/scheduler.ts +45 -18
  242. package/src/skills/catalog-install.ts +10 -2
  243. package/src/skills/managed-store.ts +2 -2
  244. package/src/skills/skill-memory.ts +1 -293
  245. package/src/subagent/index.ts +13 -3
  246. package/src/subagent/manager.ts +308 -29
  247. package/src/subagent/types.ts +68 -0
  248. package/src/tasks/task-runner.ts +4 -4
  249. package/src/tools/apps/executors.ts +29 -4
  250. package/src/tools/filesystem/list.ts +93 -0
  251. package/src/tools/permission-checker.ts +78 -0
  252. package/src/tools/registry.ts +4 -0
  253. package/src/tools/schedule/create.ts +3 -0
  254. package/src/tools/schedule/list.ts +1 -0
  255. package/src/tools/schedule/update.ts +6 -0
  256. package/src/tools/shared/filesystem/errors.ts +5 -0
  257. package/src/tools/shared/filesystem/file-ops-service.ts +90 -2
  258. package/src/tools/shared/filesystem/types.ts +17 -0
  259. package/src/tools/shared/shell-output.ts +31 -2
  260. package/src/tools/subagent/abort.ts +12 -2
  261. package/src/tools/subagent/message.ts +9 -2
  262. package/src/tools/subagent/notify-parent.ts +79 -0
  263. package/src/tools/subagent/read.ts +29 -8
  264. package/src/tools/subagent/resolve.ts +21 -0
  265. package/src/tools/subagent/spawn.ts +2 -0
  266. package/src/tools/subagent/status.ts +11 -1
  267. package/src/tools/system/avatar-generator.ts +3 -3
  268. package/src/tools/system/register.ts +23 -0
  269. package/src/tools/system/set-permission-mode.ts +103 -0
  270. package/src/tools/terminal/parser.ts +30 -5
  271. package/src/tools/terminal/safe-env.ts +16 -1
  272. package/src/tools/tool-manifest.ts +6 -0
  273. package/src/tools/types.ts +2 -0
  274. package/src/util/logger.ts +1 -1
  275. package/src/util/platform.ts +50 -17
  276. package/src/workspace/migrations/023-move-config-files-to-workspace.ts +2 -2
  277. package/src/workspace/migrations/024-move-runtime-files-to-workspace.ts +2 -2
  278. package/src/workspace/migrations/028-recover-conversations-from-disk-view.ts +270 -0
  279. package/src/workspace/migrations/029-seed-pkb.ts +84 -0
  280. package/src/workspace/migrations/registry.ts +4 -0
  281. package/src/workspace/top-level-renderer.ts +5 -9
  282. package/src/__tests__/cli-memory.test.ts +0 -377
  283. package/src/__tests__/clipboard.test.ts +0 -88
  284. package/src/cli/cli-memory.ts +0 -179
  285. 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
- currentConversationPath?: string | null;
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[] = ["<workspace_top_level>"];
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.currentConversationPath) {
23
- lines.push(`Current conversation folder: ${options.currentConversationPath}`);
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("</workspace_top_level>");
27
+ lines.push("</workspace>");
32
28
  return lines.join("\n");
33
29
  }