c0de-agent 0.0.0 → 1.0.0

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 (330) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +101 -0
  3. package/dist/cli/commands/acp.d.ts +19 -0
  4. package/dist/cli/commands/acp.js +50 -0
  5. package/dist/cli/commands/chat.d.ts +13 -0
  6. package/dist/cli/commands/chat.js +26 -0
  7. package/dist/cli/commands/config.d.ts +17 -0
  8. package/dist/cli/commands/config.js +77 -0
  9. package/dist/cli/commands/init.d.ts +9 -0
  10. package/dist/cli/commands/init.js +14 -0
  11. package/dist/cli/commands/plugin.d.ts +11 -0
  12. package/dist/cli/commands/plugin.js +31 -0
  13. package/dist/cli/commands/serve.d.ts +18 -0
  14. package/dist/cli/commands/serve.js +35 -0
  15. package/dist/cli/commands/update.d.ts +24 -0
  16. package/dist/cli/commands/update.js +40 -0
  17. package/dist/cli/deps.d.ts +19 -0
  18. package/dist/cli/deps.js +57 -0
  19. package/dist/cli/index.d.ts +8 -0
  20. package/dist/cli/index.js +149 -0
  21. package/dist/cli/modes/acp.d.ts +18 -0
  22. package/dist/cli/modes/acp.js +57 -0
  23. package/dist/cli/modes/print.d.ts +16 -0
  24. package/dist/cli/modes/print.js +30 -0
  25. package/dist/cli/parser.d.ts +19 -0
  26. package/dist/cli/parser.js +28 -0
  27. package/dist/cli/utils/output.d.ts +11 -0
  28. package/dist/cli/utils/output.js +23 -0
  29. package/dist/cli/utils/prompt.d.ts +12 -0
  30. package/dist/cli/utils/prompt.js +18 -0
  31. package/dist/core/agent.d.ts +12 -0
  32. package/dist/core/agent.js +95 -0
  33. package/dist/core/agents/builtin.d.ts +6 -0
  34. package/dist/core/agents/builtin.js +76 -0
  35. package/dist/core/agents/discovery.d.ts +6 -0
  36. package/dist/core/agents/discovery.js +96 -0
  37. package/dist/core/agents/index.d.ts +6 -0
  38. package/dist/core/agents/index.js +4 -0
  39. package/dist/core/agents/parallel.d.ts +13 -0
  40. package/dist/core/agents/parallel.js +37 -0
  41. package/dist/core/agents/registry.d.ts +4 -0
  42. package/dist/core/agents/registry.js +23 -0
  43. package/dist/core/agents/types.d.ts +37 -0
  44. package/dist/core/agents/types.js +1 -0
  45. package/dist/core/compact.d.ts +11 -0
  46. package/dist/core/compact.js +31 -0
  47. package/dist/core/config.d.ts +7 -0
  48. package/dist/core/config.js +84 -0
  49. package/dist/core/context.d.ts +23 -0
  50. package/dist/core/context.js +93 -0
  51. package/dist/core/index.d.ts +13 -0
  52. package/dist/core/index.js +11 -0
  53. package/dist/core/loop.d.ts +25 -0
  54. package/dist/core/loop.js +651 -0
  55. package/dist/core/metrics.d.ts +27 -0
  56. package/dist/core/metrics.js +94 -0
  57. package/dist/core/prompt-registry.d.ts +21 -0
  58. package/dist/core/prompt-registry.js +200 -0
  59. package/dist/core/prompt.d.ts +11 -0
  60. package/dist/core/prompt.js +13 -0
  61. package/dist/core/secret.d.ts +12 -0
  62. package/dist/core/secret.js +51 -0
  63. package/dist/core/slash.d.ts +15 -0
  64. package/dist/core/slash.js +114 -0
  65. package/dist/core/steering.d.ts +8 -0
  66. package/dist/core/steering.js +15 -0
  67. package/dist/core/title.d.ts +42 -0
  68. package/dist/core/title.js +115 -0
  69. package/dist/core/tool-exec.d.ts +20 -0
  70. package/dist/core/tool-exec.js +64 -0
  71. package/dist/core/types.d.ts +95 -0
  72. package/dist/core/types.js +1 -0
  73. package/dist/core/worktree.d.ts +24 -0
  74. package/dist/core/worktree.js +68 -0
  75. package/dist/dap/index.d.ts +6 -0
  76. package/dist/dap/index.js +4 -0
  77. package/dist/dap/protocol.d.ts +40 -0
  78. package/dist/dap/protocol.js +132 -0
  79. package/dist/dap/session.d.ts +23 -0
  80. package/dist/dap/session.js +130 -0
  81. package/dist/dap/transport.d.ts +8 -0
  82. package/dist/dap/transport.js +30 -0
  83. package/dist/dap/types.d.ts +50 -0
  84. package/dist/dap/types.js +2 -0
  85. package/dist/db/client.d.ts +22 -0
  86. package/dist/db/client.js +39 -0
  87. package/dist/db/index.d.ts +6 -0
  88. package/dist/db/index.js +4 -0
  89. package/dist/db/migrate.d.ts +9 -0
  90. package/dist/db/migrate.js +15 -0
  91. package/dist/db/schema.d.ts +965 -0
  92. package/dist/db/schema.js +112 -0
  93. package/dist/db/types.d.ts +11 -0
  94. package/dist/db/types.js +1 -0
  95. package/dist/llm/index.d.ts +13 -0
  96. package/dist/llm/index.js +8 -0
  97. package/dist/llm/protocols/openai-compat.d.ts +139 -0
  98. package/dist/llm/protocols/openai-compat.js +214 -0
  99. package/dist/llm/protocols/utils/index.d.ts +2 -0
  100. package/dist/llm/protocols/utils/index.js +2 -0
  101. package/dist/llm/protocols/utils/lifecycle.d.ts +23 -0
  102. package/dist/llm/protocols/utils/lifecycle.js +46 -0
  103. package/dist/llm/protocols/utils/tool-stream.d.ts +46 -0
  104. package/dist/llm/protocols/utils/tool-stream.js +94 -0
  105. package/dist/llm/provider-error.d.ts +5 -0
  106. package/dist/llm/provider-error.js +33 -0
  107. package/dist/llm/provider.d.ts +32 -0
  108. package/dist/llm/provider.js +191 -0
  109. package/dist/llm/registry.d.ts +44 -0
  110. package/dist/llm/registry.js +107 -0
  111. package/dist/llm/retry.d.ts +37 -0
  112. package/dist/llm/retry.js +82 -0
  113. package/dist/llm/routing.d.ts +36 -0
  114. package/dist/llm/routing.js +53 -0
  115. package/dist/llm/schema/errors.d.ts +88 -0
  116. package/dist/llm/schema/errors.js +33 -0
  117. package/dist/llm/schema/events.d.ts +135 -0
  118. package/dist/llm/schema/events.js +35 -0
  119. package/dist/llm/schema/ids.d.ts +22 -0
  120. package/dist/llm/schema/ids.js +3 -0
  121. package/dist/llm/schema/index.d.ts +5 -0
  122. package/dist/llm/schema/index.js +5 -0
  123. package/dist/llm/schema/messages.d.ts +97 -0
  124. package/dist/llm/schema/messages.js +18 -0
  125. package/dist/llm/schema/options.d.ts +54 -0
  126. package/dist/llm/schema/options.js +54 -0
  127. package/dist/llm/token.d.ts +2 -0
  128. package/dist/llm/token.js +13 -0
  129. package/dist/llm/transport.d.ts +31 -0
  130. package/dist/llm/transport.js +165 -0
  131. package/dist/mcp/index.d.ts +1 -0
  132. package/dist/mcp/index.js +3 -0
  133. package/dist/plugins/builtin.d.ts +6 -0
  134. package/dist/plugins/builtin.js +54 -0
  135. package/dist/plugins/hooks.d.ts +3 -0
  136. package/dist/plugins/hooks.js +79 -0
  137. package/dist/plugins/index.d.ts +9 -0
  138. package/dist/plugins/index.js +8 -0
  139. package/dist/plugins/init.d.ts +23 -0
  140. package/dist/plugins/init.js +30 -0
  141. package/dist/plugins/lifecycle.d.ts +6 -0
  142. package/dist/plugins/lifecycle.js +74 -0
  143. package/dist/plugins/loader.d.ts +16 -0
  144. package/dist/plugins/loader.js +68 -0
  145. package/dist/plugins/logger.d.ts +4 -0
  146. package/dist/plugins/logger.js +31 -0
  147. package/dist/plugins/registry.d.ts +7 -0
  148. package/dist/plugins/registry.js +24 -0
  149. package/dist/plugins/types.d.ts +113 -0
  150. package/dist/plugins/types.js +1 -0
  151. package/dist/project/detect.d.ts +4 -0
  152. package/dist/project/detect.js +131 -0
  153. package/dist/project/index.d.ts +4 -0
  154. package/dist/project/index.js +2 -0
  155. package/dist/project/project.d.ts +23 -0
  156. package/dist/project/project.js +87 -0
  157. package/dist/project/resolve.d.ts +8 -0
  158. package/dist/project/resolve.js +81 -0
  159. package/dist/server/agent-manager.d.ts +32 -0
  160. package/dist/server/agent-manager.js +54 -0
  161. package/dist/server/app.d.ts +5 -0
  162. package/dist/server/app.js +75 -0
  163. package/dist/server/context.d.ts +20 -0
  164. package/dist/server/context.js +47 -0
  165. package/dist/server/dev.d.ts +11 -0
  166. package/dist/server/dev.js +83 -0
  167. package/dist/server/index.d.ts +11 -0
  168. package/dist/server/index.js +7 -0
  169. package/dist/server/middleware/auth.d.ts +15 -0
  170. package/dist/server/middleware/auth.js +29 -0
  171. package/dist/server/middleware/cors.d.ts +16 -0
  172. package/dist/server/middleware/cors.js +44 -0
  173. package/dist/server/middleware/error.d.ts +15 -0
  174. package/dist/server/middleware/error.js +11 -0
  175. package/dist/server/permission/interactive.d.ts +24 -0
  176. package/dist/server/permission/interactive.js +56 -0
  177. package/dist/server/permission/store.d.ts +30 -0
  178. package/dist/server/permission/store.js +47 -0
  179. package/dist/server/routes/agent.d.ts +4 -0
  180. package/dist/server/routes/agent.js +19 -0
  181. package/dist/server/routes/catalog.d.ts +42 -0
  182. package/dist/server/routes/catalog.js +194 -0
  183. package/dist/server/routes/chat.d.ts +8 -0
  184. package/dist/server/routes/chat.js +282 -0
  185. package/dist/server/routes/commands.d.ts +5 -0
  186. package/dist/server/routes/commands.js +17 -0
  187. package/dist/server/routes/config.d.ts +4 -0
  188. package/dist/server/routes/config.js +27 -0
  189. package/dist/server/routes/files.d.ts +4 -0
  190. package/dist/server/routes/files.js +183 -0
  191. package/dist/server/routes/filesystem.d.ts +29 -0
  192. package/dist/server/routes/filesystem.js +108 -0
  193. package/dist/server/routes/health.d.ts +3 -0
  194. package/dist/server/routes/health.js +13 -0
  195. package/dist/server/routes/permissions.d.ts +5 -0
  196. package/dist/server/routes/permissions.js +23 -0
  197. package/dist/server/routes/project.d.ts +4 -0
  198. package/dist/server/routes/project.js +51 -0
  199. package/dist/server/routes/provider.d.ts +16 -0
  200. package/dist/server/routes/provider.js +66 -0
  201. package/dist/server/routes/session.d.ts +4 -0
  202. package/dist/server/routes/session.js +130 -0
  203. package/dist/server/routes/tool.d.ts +4 -0
  204. package/dist/server/routes/tool.js +28 -0
  205. package/dist/server/routes/update.d.ts +14 -0
  206. package/dist/server/routes/update.js +49 -0
  207. package/dist/server/server.d.ts +43 -0
  208. package/dist/server/server.js +197 -0
  209. package/dist/server/types.d.ts +73 -0
  210. package/dist/server/types.js +2 -0
  211. package/dist/server/util/safe-path.d.ts +7 -0
  212. package/dist/server/util/safe-path.js +7 -0
  213. package/dist/session/archive.d.ts +15 -0
  214. package/dist/session/archive.js +129 -0
  215. package/dist/session/branch.d.ts +9 -0
  216. package/dist/session/branch.js +63 -0
  217. package/dist/session/compaction.d.ts +27 -0
  218. package/dist/session/compaction.js +192 -0
  219. package/dist/session/context.d.ts +16 -0
  220. package/dist/session/context.js +173 -0
  221. package/dist/session/index.d.ts +10 -0
  222. package/dist/session/index.js +10 -0
  223. package/dist/session/message.d.ts +22 -0
  224. package/dist/session/message.js +145 -0
  225. package/dist/session/session.d.ts +37 -0
  226. package/dist/session/session.js +144 -0
  227. package/dist/session/snapshot.d.ts +11 -0
  228. package/dist/session/snapshot.js +60 -0
  229. package/dist/session/squash.d.ts +9 -0
  230. package/dist/session/squash.js +88 -0
  231. package/dist/session/token.d.ts +10 -0
  232. package/dist/session/token.js +33 -0
  233. package/dist/session/types.d.ts +122 -0
  234. package/dist/session/types.js +1 -0
  235. package/dist/shared/index.d.ts +2 -0
  236. package/dist/shared/index.js +1 -0
  237. package/dist/shared/types/agent.d.ts +202 -0
  238. package/dist/shared/types/agent.js +1 -0
  239. package/dist/shared/types/base.d.ts +32 -0
  240. package/dist/shared/types/base.js +1 -0
  241. package/dist/shared/types/config.d.ts +104 -0
  242. package/dist/shared/types/config.js +1 -0
  243. package/dist/shared/types/index.d.ts +6 -0
  244. package/dist/shared/types/index.js +1 -0
  245. package/dist/shared/types/llm.d.ts +122 -0
  246. package/dist/shared/types/llm.js +1 -0
  247. package/dist/shared/types/message.d.ts +71 -0
  248. package/dist/shared/types/message.js +1 -0
  249. package/dist/shared/types/tool.d.ts +134 -0
  250. package/dist/shared/types/tool.js +1 -0
  251. package/dist/shared/utils/path.d.ts +15 -0
  252. package/dist/shared/utils/path.js +23 -0
  253. package/dist/shared/utils.d.ts +5 -0
  254. package/dist/shared/utils.js +10 -0
  255. package/dist/tools/builtin/bash.d.ts +12 -0
  256. package/dist/tools/builtin/bash.js +121 -0
  257. package/dist/tools/builtin/dap.d.ts +23 -0
  258. package/dist/tools/builtin/dap.js +231 -0
  259. package/dist/tools/builtin/edit.d.ts +12 -0
  260. package/dist/tools/builtin/edit.js +151 -0
  261. package/dist/tools/builtin/glob.d.ts +11 -0
  262. package/dist/tools/builtin/glob.js +120 -0
  263. package/dist/tools/builtin/grep.d.ts +6 -0
  264. package/dist/tools/builtin/grep.js +141 -0
  265. package/dist/tools/builtin/read.d.ts +6 -0
  266. package/dist/tools/builtin/read.js +73 -0
  267. package/dist/tools/builtin/resolvers.d.ts +12 -0
  268. package/dist/tools/builtin/resolvers.js +79 -0
  269. package/dist/tools/builtin/task.d.ts +9 -0
  270. package/dist/tools/builtin/task.js +100 -0
  271. package/dist/tools/builtin/write.d.ts +6 -0
  272. package/dist/tools/builtin/write.js +36 -0
  273. package/dist/tools/builtin/yield.d.ts +9 -0
  274. package/dist/tools/builtin/yield.js +40 -0
  275. package/dist/tools/executor.d.ts +9 -0
  276. package/dist/tools/executor.js +54 -0
  277. package/dist/tools/hashline/index.d.ts +2 -0
  278. package/dist/tools/hashline/index.js +3 -0
  279. package/dist/tools/hashline/patch.d.ts +48 -0
  280. package/dist/tools/hashline/patch.js +201 -0
  281. package/dist/tools/index.d.ts +30 -0
  282. package/dist/tools/index.js +55 -0
  283. package/dist/tools/permission.d.ts +13 -0
  284. package/dist/tools/permission.js +39 -0
  285. package/dist/tools/registry.d.ts +15 -0
  286. package/dist/tools/registry.js +54 -0
  287. package/dist/tools/resolver.d.ts +15 -0
  288. package/dist/tools/resolver.js +47 -0
  289. package/dist/tools/truncate.d.ts +8 -0
  290. package/dist/tools/truncate.js +50 -0
  291. package/dist/tools/types.d.ts +101 -0
  292. package/dist/tools/types.js +1 -0
  293. package/dist/tools/validate.d.ts +8 -0
  294. package/dist/tools/validate.js +130 -0
  295. package/dist/tools/websearch/fetch.d.ts +11 -0
  296. package/dist/tools/websearch/fetch.js +21 -0
  297. package/dist/tools/websearch/index.d.ts +19 -0
  298. package/dist/tools/websearch/index.js +78 -0
  299. package/dist/tools/websearch/providers/brave.d.ts +5 -0
  300. package/dist/tools/websearch/providers/brave.js +53 -0
  301. package/dist/tools/websearch/providers/duckduckgo.d.ts +3 -0
  302. package/dist/tools/websearch/providers/duckduckgo.js +75 -0
  303. package/dist/tools/websearch/providers/tavily.d.ts +11 -0
  304. package/dist/tools/websearch/providers/tavily.js +89 -0
  305. package/dist/tools/websearch/types.d.ts +51 -0
  306. package/dist/tools/websearch/types.js +18 -0
  307. package/dist/tools/websearch/websearch.d.ts +10 -0
  308. package/dist/tools/websearch/websearch.js +48 -0
  309. package/dist/update/index.d.ts +10 -0
  310. package/dist/update/index.js +6 -0
  311. package/dist/update/ipc.d.ts +15 -0
  312. package/dist/update/ipc.js +41 -0
  313. package/dist/update/scheduler.d.ts +34 -0
  314. package/dist/update/scheduler.js +57 -0
  315. package/dist/update/snapshot.d.ts +39 -0
  316. package/dist/update/snapshot.js +99 -0
  317. package/dist/update/updater.d.ts +48 -0
  318. package/dist/update/updater.js +83 -0
  319. package/dist/update/version.d.ts +18 -0
  320. package/dist/update/version.js +49 -0
  321. package/drizzle/0000_magical_nightshade.sql +55 -0
  322. package/drizzle/0001_chemical_doctor_faustus.sql +13 -0
  323. package/drizzle/0002_amusing_micromacro.sql +12 -0
  324. package/drizzle/0003_regular_purifiers.sql +2 -0
  325. package/drizzle/meta/0000_snapshot.json +448 -0
  326. package/drizzle/meta/0001_snapshot.json +539 -0
  327. package/drizzle/meta/0002_snapshot.json +633 -0
  328. package/drizzle/meta/0003_snapshot.json +645 -0
  329. package/drizzle/meta/_journal.json +34 -0
  330. package/package.json +100 -8
@@ -0,0 +1,18 @@
1
+ /**
2
+ * websearch 工具的统一类型、常量与工具函数。
3
+ *
4
+ * 来源:oh-my-pi(packages/coding-agent/src/web/search/)多后端 strategy 架构,
5
+ * 裁剪适配 c0de-agent 的 data+functions 范式(无 class)。
6
+ * 详见 docs/superpowers/specs/2026-06-30-websearch-tool-design.md §2-3。
7
+ */
8
+ /** 工具参数校验常量。 */
9
+ const DEFAULT_NUM_RESULTS = 8;
10
+ const MAX_NUM_RESULTS = 20;
11
+ const MIN_NUM_RESULTS = 1;
12
+ /** clamp 结果数到 [MIN, MAX];undefined/NaN 回退到 fallback。 */
13
+ function clampNumResults(n, fallback = DEFAULT_NUM_RESULTS) {
14
+ if (n === undefined || Number.isNaN(n))
15
+ return fallback;
16
+ return Math.min(MAX_NUM_RESULTS, Math.max(MIN_NUM_RESULTS, Math.trunc(n)));
17
+ }
18
+ export { clampNumResults, DEFAULT_NUM_RESULTS, MAX_NUM_RESULTS, MIN_NUM_RESULTS };
@@ -0,0 +1,10 @@
1
+ import type { WebSearchConfig } from '../../shared/types/config.js';
2
+ import type { ToolDef } from '../../shared/types/tool.js';
3
+ import { setFetchOverride } from './index.js';
4
+ export { setFetchOverride };
5
+ /**
6
+ * websearch 工具工厂。config 由工厂闭包捕获(对齐 runSubAgent/debugSpawn 依赖反转模式)。
7
+ * permission: auto(只读网络搜索,不改本地状态)。timeout 30s。
8
+ * 详见 docs/superpowers/specs/2026-06-30-websearch-tool-design.md §5。
9
+ */
10
+ export declare function createWebSearchTool(config: WebSearchConfig): ToolDef;
@@ -0,0 +1,48 @@
1
+ import { formatForLLM, runWebSearch, setFetchOverride } from './index.js';
2
+ export { setFetchOverride };
3
+ const NO_RESULTS = 'No search results found. Try a different query.';
4
+ /**
5
+ * websearch 工具工厂。config 由工厂闭包捕获(对齐 runSubAgent/debugSpawn 依赖反转模式)。
6
+ * permission: auto(只读网络搜索,不改本地状态)。timeout 30s。
7
+ * 详见 docs/superpowers/specs/2026-06-30-websearch-tool-design.md §5。
8
+ */
9
+ export function createWebSearchTool(config) {
10
+ return {
11
+ name: 'websearch',
12
+ description: 'Search the web for up-to-date information beyond knowledge cutoff. ' +
13
+ 'Returns titled sources with snippets (and a synthesized answer when the ' +
14
+ 'backend supports it). Use for current events, recent releases, and docs. ' +
15
+ `The current year is ${new Date().getFullYear()}.`,
16
+ parameters: {
17
+ type: 'object',
18
+ properties: {
19
+ query: { type: 'string', description: 'Search query' },
20
+ numResults: {
21
+ type: 'number',
22
+ description: 'Number of results (default 8, max 20)',
23
+ },
24
+ recency: {
25
+ type: 'string',
26
+ enum: ['day', 'week', 'month', 'year'],
27
+ description: 'Time filter for results',
28
+ },
29
+ },
30
+ required: ['query'],
31
+ },
32
+ permission: 'auto',
33
+ timeout: 30_000,
34
+ execute: async (input, ctx) => {
35
+ const { query, numResults, recency } = input;
36
+ try {
37
+ const response = await runWebSearch({ query, numResults, recency }, config, ctx.abort);
38
+ if (!response.answer && response.sources.length === 0) {
39
+ return { _tag: 'success', output: NO_RESULTS };
40
+ }
41
+ return { _tag: 'success', output: formatForLLM(response) };
42
+ }
43
+ catch (err) {
44
+ return { _tag: 'error', error: err instanceof Error ? err.message : String(err) };
45
+ }
46
+ },
47
+ };
48
+ }
@@ -0,0 +1,10 @@
1
+ export type { HandoffServer } from './ipc.js';
2
+ export { createHandoffServer, requestHandoff } from './ipc.js';
3
+ export type { UpdateScheduler, UpdateSchedulerOptions } from './scheduler.js';
4
+ export { createUpdateScheduler } from './scheduler.js';
5
+ export type { SerializedEntry, SerializedSession, SessionSnapshot } from './snapshot.js';
6
+ export { orderSessionsByParent, restoreSessions, serializeSessions } from './snapshot.js';
7
+ export type { HotUpdateOptions, HotUpdateResult, SpawnFn } from './updater.js';
8
+ export { cleanupSnapshot, performHotUpdate } from './updater.js';
9
+ export type { UpdateCheckResult } from './version.js';
10
+ export { checkForUpdate, compareSemver } from './version.js';
@@ -0,0 +1,6 @@
1
+ // 热更新 + 会话迁移包(spec §18)。
2
+ export { createHandoffServer, requestHandoff } from './ipc.js';
3
+ export { createUpdateScheduler } from './scheduler.js';
4
+ export { orderSessionsByParent, restoreSessions, serializeSessions } from './snapshot.js';
5
+ export { cleanupSnapshot, performHotUpdate } from './updater.js';
6
+ export { checkForUpdate, compareSemver } from './version.js';
@@ -0,0 +1,15 @@
1
+ /**
2
+ * 端口接管 IPC(spec §18.3):新实例检测到端口被旧实例占用时,通过 HTTP
3
+ * `/handoff` 端点请求旧实例 graceful shutdown(序列化状态后退出),从而
4
+ * 接管端口。用普通 HTTP 而非 unix socket,便于跨平台。
5
+ */
6
+ type HandoffServer = {
7
+ port: number;
8
+ close: () => Promise<void>;
9
+ };
10
+ /** 旧实例启动时创建 handoff 端点。收到 POST /handoff 即触发 onHandoff(序列化 + 退出)。 */
11
+ declare function createHandoffServer(onHandoff: () => Promise<void>): Promise<HandoffServer>;
12
+ /** 新实例请求旧实例让出端口(graceful shutdown)。失败抛错。 */
13
+ declare function requestHandoff(port: number, host?: string): Promise<void>;
14
+ export type { HandoffServer };
15
+ export { createHandoffServer, requestHandoff };
@@ -0,0 +1,41 @@
1
+ import { createServer } from 'node:http';
2
+ /** 旧实例启动时创建 handoff 端点。收到 POST /handoff 即触发 onHandoff(序列化 + 退出)。 */
3
+ function createHandoffServer(onHandoff) {
4
+ return new Promise((resolve, reject) => {
5
+ const handler = async (req, res) => {
6
+ if (req.method === 'POST' && req.url === '/handoff') {
7
+ try {
8
+ await onHandoff();
9
+ res.writeHead(200, { 'content-type': 'application/json' });
10
+ res.end(JSON.stringify({ ok: true }));
11
+ }
12
+ catch (error) {
13
+ res.writeHead(500, { 'content-type': 'application/json' });
14
+ res.end(JSON.stringify({ ok: false, error: String(error) }));
15
+ }
16
+ return;
17
+ }
18
+ res.writeHead(404);
19
+ res.end();
20
+ };
21
+ const server = createServer(handler);
22
+ server.on('error', reject);
23
+ server.listen(0, '127.0.0.1', () => {
24
+ const addr = server.address();
25
+ const port = typeof addr === 'object' && addr ? addr.port : 0;
26
+ resolve({
27
+ port,
28
+ close: () => new Promise((res) => {
29
+ server.close(() => res());
30
+ }),
31
+ });
32
+ });
33
+ });
34
+ }
35
+ /** 新实例请求旧实例让出端口(graceful shutdown)。失败抛错。 */
36
+ async function requestHandoff(port, host = '127.0.0.1') {
37
+ const res = await fetch(`http://${host}:${port}/handoff`, { method: 'POST' });
38
+ if (!res.ok)
39
+ throw new Error(`handoff failed: HTTP ${res.status}`);
40
+ }
41
+ export { createHandoffServer, requestHandoff };
@@ -0,0 +1,34 @@
1
+ import type { UpdateCheckResult } from './version.js';
2
+ /**
3
+ * 后台版本检查调度器(spec §18.1 步骤 1)。
4
+ *
5
+ * 启动时延迟 initialDelayMs 后执行首次 checkForUpdate,之后按 intervalMs
6
+ * 周期性检查。结果缓存到内存,供 /api/update 路由读取(避免每次请求都打
7
+ * npm registry)。发现新版本通过 onUpdate 回调通知上层(server.ts 转发到
8
+ * 前端 SSE 或前端轮询读取)。
9
+ *
10
+ * checkFn 抛错时不传播,缓存降级为 hasUpdate:false(与 checkForUpdate 自身
11
+ * 的容错语义一致)。
12
+ */
13
+ type UpdateSchedulerOptions = {
14
+ checkFn: () => Promise<UpdateCheckResult>;
15
+ /** 周期检查间隔(毫秒),默认 1 小时。 */
16
+ intervalMs?: number;
17
+ /** 首次检查延迟(毫秒),默认 10 秒。 */
18
+ initialDelayMs?: number;
19
+ /** 每次成功检查后回调(含 hasUpdate:false);异常路径不调用。 */
20
+ onUpdate?: (result: UpdateCheckResult) => void;
21
+ };
22
+ type UpdateScheduler = {
23
+ /** 启动调度(首次延迟 + 周期检查)。重复 start 是 no-op。 */
24
+ start(): void;
25
+ /** 停止调度;已缓存的 lastResult 保留。 */
26
+ stop(): void;
27
+ /** 立即触发一次检查并更新缓存;异常降级为 hasUpdate:false。 */
28
+ checkNow(): Promise<UpdateCheckResult>;
29
+ /** 当前缓存结果;未检查过返回 null。 */
30
+ getLastResult(): UpdateCheckResult | null;
31
+ };
32
+ declare function createUpdateScheduler(opts: UpdateSchedulerOptions): UpdateScheduler;
33
+ export type { UpdateScheduler, UpdateSchedulerOptions };
34
+ export { createUpdateScheduler };
@@ -0,0 +1,57 @@
1
+ const DEFAULT_INTERVAL_MS = 60 * 60 * 1000;
2
+ const DEFAULT_INITIAL_DELAY_MS = 10_000;
3
+ function createUpdateScheduler(opts) {
4
+ const intervalMs = opts.intervalMs ?? DEFAULT_INTERVAL_MS;
5
+ const initialDelayMs = opts.initialDelayMs ?? DEFAULT_INITIAL_DELAY_MS;
6
+ // 直接引用全局 timer;vitest useFakeTimers() 会替换全局,start() 时读取即拿到 mock。
7
+ let intervalId = null;
8
+ let initialId = null;
9
+ let lastResult = null;
10
+ let started = false;
11
+ async function runOnce() {
12
+ let r;
13
+ try {
14
+ r = await opts.checkFn();
15
+ }
16
+ catch {
17
+ // 异常降级:缓存 hasUpdate:false,但不触发 onUpdate(避免误报)。
18
+ lastResult = { hasUpdate: false, currentVersion: '0.0.0', latestVersion: '0.0.0' };
19
+ return lastResult;
20
+ }
21
+ lastResult = r;
22
+ if (opts.onUpdate)
23
+ opts.onUpdate(r);
24
+ return r;
25
+ }
26
+ return {
27
+ start() {
28
+ if (started)
29
+ return;
30
+ started = true;
31
+ // 首次延迟:到达后执行 runOnce,再挂周期 interval。
32
+ // 不能直接 setInterval——initialDelayMs 与 intervalMs 通常不同。
33
+ initialId = setTimeout(() => {
34
+ void runOnce();
35
+ intervalId = setInterval(() => void runOnce(), intervalMs);
36
+ }, initialDelayMs);
37
+ },
38
+ stop() {
39
+ if (initialId !== null) {
40
+ clearTimeout(initialId);
41
+ initialId = null;
42
+ }
43
+ if (intervalId !== null) {
44
+ clearInterval(intervalId);
45
+ intervalId = null;
46
+ }
47
+ started = false;
48
+ },
49
+ checkNow() {
50
+ return runOnce();
51
+ },
52
+ getLastResult() {
53
+ return lastResult;
54
+ },
55
+ };
56
+ }
57
+ export { createUpdateScheduler };
@@ -0,0 +1,39 @@
1
+ import type { DB } from '../db/client.js';
2
+ /** 序列化后的会话行(Date → epoch ms,纯 JSON 安全)。 */
3
+ type SerializedSession = {
4
+ id: string;
5
+ title: string;
6
+ parentId: string | null;
7
+ projectId: string | null;
8
+ branchPoint: number | null;
9
+ metadata: unknown;
10
+ createdAt: number;
11
+ updatedAt: number;
12
+ };
13
+ /** 序列化后的会话条目行(Date → epoch ms)。 */
14
+ type SerializedEntry = {
15
+ id: string;
16
+ sessionId: string;
17
+ tag: string;
18
+ role: string | null;
19
+ content: unknown;
20
+ toolName: string | null;
21
+ tokenCount: number;
22
+ createdAt: number;
23
+ };
24
+ /** 热更新迁移快照(spec §18.2)。 */
25
+ type SessionSnapshot = {
26
+ version: string;
27
+ sessions: SerializedSession[];
28
+ entries: SerializedEntry[];
29
+ config: unknown;
30
+ timestamp: number;
31
+ };
32
+ /** 按 parentId 拓扑序排列 sessions,使父会话先于子会话插入(满足自引用 FK)。 */
33
+ declare function orderSessionsByParent(list: SerializedSession[]): SerializedSession[];
34
+ /** 从 DB 导出所有会话与条目为可序列化快照。 */
35
+ declare function serializeSessions(handle: DB, config?: unknown): Promise<SessionSnapshot>;
36
+ /** 把快照导入 DB(保留原始 id 与时间戳;父会话先插入以满足 FK)。 */
37
+ declare function restoreSessions(handle: DB, snapshot: SessionSnapshot): Promise<void>;
38
+ export type { SerializedEntry, SerializedSession, SessionSnapshot };
39
+ export { orderSessionsByParent, restoreSessions, serializeSessions };
@@ -0,0 +1,99 @@
1
+ import { sessionEntries, sessions } from '../db/schema.js';
2
+ const CURRENT_SNAPSHOT_VERSION = '0.1.0';
3
+ function toDateMs(v) {
4
+ if (v instanceof Date)
5
+ return v.getTime();
6
+ if (typeof v === 'string' || typeof v === 'number')
7
+ return new Date(v).getTime();
8
+ return Date.now();
9
+ }
10
+ function toSerializedSession(row) {
11
+ return {
12
+ id: row.id,
13
+ title: row.title,
14
+ parentId: row.parentId,
15
+ projectId: row.projectId,
16
+ branchPoint: row.branchPoint,
17
+ metadata: row.metadata,
18
+ createdAt: toDateMs(row.createdAt),
19
+ updatedAt: toDateMs(row.updatedAt),
20
+ };
21
+ }
22
+ function toSerializedEntry(row) {
23
+ return {
24
+ id: row.id,
25
+ sessionId: row.sessionId,
26
+ tag: row.tag,
27
+ role: row.role,
28
+ content: row.content,
29
+ toolName: row.toolName,
30
+ tokenCount: row.tokenCount ?? 0,
31
+ createdAt: toDateMs(row.createdAt),
32
+ };
33
+ }
34
+ /** 按 parentId 拓扑序排列 sessions,使父会话先于子会话插入(满足自引用 FK)。 */
35
+ function orderSessionsByParent(list) {
36
+ const byId = new Map(list.map((s) => [s.id, s]));
37
+ const ordered = [];
38
+ const seen = new Set();
39
+ const visit = (s) => {
40
+ if (seen.has(s.id))
41
+ return;
42
+ const parent = s.parentId ? byId.get(s.parentId) : undefined;
43
+ if (parent && !seen.has(parent.id))
44
+ visit(parent);
45
+ seen.add(s.id);
46
+ ordered.push(s);
47
+ };
48
+ for (const s of list)
49
+ visit(s);
50
+ return ordered;
51
+ }
52
+ /** 从 DB 导出所有会话与条目为可序列化快照。 */
53
+ async function serializeSessions(handle, config) {
54
+ const [sRows, eRows] = await Promise.all([
55
+ handle.db.select().from(sessions),
56
+ handle.db.select().from(sessionEntries),
57
+ ]);
58
+ return {
59
+ version: CURRENT_SNAPSHOT_VERSION,
60
+ sessions: sRows.map(toSerializedSession),
61
+ entries: eRows.map(toSerializedEntry),
62
+ config: config ?? null,
63
+ timestamp: Date.now(),
64
+ };
65
+ }
66
+ /** 把快照导入 DB(保留原始 id 与时间戳;父会话先插入以满足 FK)。 */
67
+ async function restoreSessions(handle, snapshot) {
68
+ for (const s of orderSessionsByParent(snapshot.sessions)) {
69
+ await handle.db
70
+ .insert(sessions)
71
+ .values({
72
+ id: s.id,
73
+ title: s.title,
74
+ parentId: s.parentId,
75
+ projectId: s.projectId,
76
+ branchPoint: s.branchPoint,
77
+ metadata: s.metadata,
78
+ createdAt: new Date(s.createdAt),
79
+ updatedAt: new Date(s.updatedAt),
80
+ })
81
+ .onConflictDoNothing();
82
+ }
83
+ for (const e of snapshot.entries) {
84
+ await handle.db
85
+ .insert(sessionEntries)
86
+ .values({
87
+ id: e.id,
88
+ sessionId: e.sessionId,
89
+ tag: e.tag,
90
+ role: e.role,
91
+ content: e.content,
92
+ toolName: e.toolName,
93
+ tokenCount: e.tokenCount,
94
+ createdAt: new Date(e.createdAt),
95
+ })
96
+ .onConflictDoNothing();
97
+ }
98
+ }
99
+ export { orderSessionsByParent, restoreSessions, serializeSessions };
@@ -0,0 +1,48 @@
1
+ import type { SessionSnapshot } from './snapshot.js';
2
+ /** 热更新执行结果(spec §18)。 */
3
+ type HotUpdateResult = {
4
+ _tag: 'success';
5
+ snapshotPath: string;
6
+ } | {
7
+ _tag: 'install_failed';
8
+ error: string;
9
+ snapshotPath: string;
10
+ } | {
11
+ _tag: 'spawn_failed';
12
+ error: string;
13
+ snapshotPath: string;
14
+ };
15
+ /**
16
+ * 启动新实例的函数签名。
17
+ * @param snapshotPath 快照文件路径(旧实例已写入)
18
+ * @param argv 传给新进程的完整参数(含 restore flag + 可选 handoff flag)
19
+ */
20
+ type SpawnFn = (snapshotPath: string, argv: string[]) => Promise<void>;
21
+ type HotUpdateOptions = {
22
+ /** 自更新安装函数;默认 `npm install -g <pkg>`。 */
23
+ installFn?: (pkg: string) => Promise<void>;
24
+ /** 启动新实例的函数;默认 detached spawn 当前进程入口。 */
25
+ spawnNewInstanceFn?: SpawnFn;
26
+ packageName?: string;
27
+ /** 快照写入路径;默认临时目录。 */
28
+ snapshotPath?: string;
29
+ /** 新实例接收快照的参数前缀(默认 --restore)。 */
30
+ restoreFlag?: string;
31
+ /** 旧实例 handoff 端口;提供时新实例启动后请求旧实例 graceful shutdown。 */
32
+ handoffPort?: number;
33
+ };
34
+ /**
35
+ * 执行热更新(spec §18.1-18.2 流程):
36
+ * 1. 序列化当前会话状态到快照文件
37
+ * 2. 全局自更新(npm install -g)
38
+ * 3. detached 启动新版本实例,传入快照路径(新实例 restore + 端口接管)
39
+ *
40
+ * 注:旧实例的 graceful shutdown / 端口让渡由新实例启动后通过 IPC 协调
41
+ * (见 ipc.ts),本函数只负责"序列化 + 安装 + 接力"。若提供 handoffPort,
42
+ * argv 会带上 --handoff-port,新实例据此 requestHandoff 通知旧实例退出。
43
+ */
44
+ declare function performHotUpdate(snapshot: SessionSnapshot, opts?: HotUpdateOptions): Promise<HotUpdateResult>;
45
+ /** 清理快照临时目录(热更新完成或放弃后调用)。 */
46
+ declare function cleanupSnapshot(snapshotPath: string): Promise<void>;
47
+ export type { HotUpdateOptions, HotUpdateResult, SpawnFn };
48
+ export { cleanupSnapshot, performHotUpdate };
@@ -0,0 +1,83 @@
1
+ import { spawn } from 'node:child_process';
2
+ import { mkdtemp, rm, writeFile } from 'node:fs/promises';
3
+ import { tmpdir } from 'node:os';
4
+ import { join } from 'node:path';
5
+ const DEFAULT_PACKAGE = 'c0de-agent';
6
+ /** 默认自更新:npm install -g <pkg>。 */
7
+ function defaultInstall(pkg) {
8
+ return new Promise((resolve, reject) => {
9
+ const child = spawn('npm', ['install', '-g', pkg], { stdio: 'ignore' });
10
+ child.on('error', reject);
11
+ child.on('exit', (code) => {
12
+ if (code === 0)
13
+ resolve();
14
+ else
15
+ reject(new Error(`npm install -g ${pkg} exited with ${code}`));
16
+ });
17
+ });
18
+ }
19
+ /** 默认启动新实例:detached spawn,argv 全部透传给新进程。 */
20
+ function defaultSpawn(_snapshotPath, argv) {
21
+ return new Promise((resolve, reject) => {
22
+ try {
23
+ const child = spawn(process.argv0, [process.argv[1] ?? '', ...argv], {
24
+ detached: true,
25
+ stdio: 'ignore',
26
+ });
27
+ child.unref();
28
+ resolve();
29
+ }
30
+ catch (error) {
31
+ reject(error instanceof Error ? error : new Error(String(error)));
32
+ }
33
+ });
34
+ }
35
+ /**
36
+ * 执行热更新(spec §18.1-18.2 流程):
37
+ * 1. 序列化当前会话状态到快照文件
38
+ * 2. 全局自更新(npm install -g)
39
+ * 3. detached 启动新版本实例,传入快照路径(新实例 restore + 端口接管)
40
+ *
41
+ * 注:旧实例的 graceful shutdown / 端口让渡由新实例启动后通过 IPC 协调
42
+ * (见 ipc.ts),本函数只负责"序列化 + 安装 + 接力"。若提供 handoffPort,
43
+ * argv 会带上 --handoff-port,新实例据此 requestHandoff 通知旧实例退出。
44
+ */
45
+ async function performHotUpdate(snapshot, opts = {}) {
46
+ const pkg = opts.packageName ?? DEFAULT_PACKAGE;
47
+ const dir = await mkdtemp(join(tmpdir(), 'c0de-update-'));
48
+ const snapshotPath = opts.snapshotPath ?? join(dir, 'snapshot.json');
49
+ await writeFile(snapshotPath, JSON.stringify(snapshot), 'utf8');
50
+ const install = opts.installFn ?? defaultInstall;
51
+ try {
52
+ await install(pkg);
53
+ }
54
+ catch (error) {
55
+ return {
56
+ _tag: 'install_failed',
57
+ error: error instanceof Error ? error.message : String(error),
58
+ snapshotPath,
59
+ };
60
+ }
61
+ const restoreFlag = opts.restoreFlag ?? '--restore';
62
+ const argv = [restoreFlag, snapshotPath];
63
+ if (opts.handoffPort !== undefined) {
64
+ argv.push('--handoff-port', String(opts.handoffPort));
65
+ }
66
+ const spawnNew = opts.spawnNewInstanceFn ?? defaultSpawn;
67
+ try {
68
+ await spawnNew(snapshotPath, argv);
69
+ }
70
+ catch (error) {
71
+ return {
72
+ _tag: 'spawn_failed',
73
+ error: error instanceof Error ? error.message : String(error),
74
+ snapshotPath,
75
+ };
76
+ }
77
+ return { _tag: 'success', snapshotPath };
78
+ }
79
+ /** 清理快照临时目录(热更新完成或放弃后调用)。 */
80
+ async function cleanupSnapshot(snapshotPath) {
81
+ await rm(join(snapshotPath, '..'), { recursive: true, force: true }).catch(() => { });
82
+ }
83
+ export { cleanupSnapshot, performHotUpdate };
@@ -0,0 +1,18 @@
1
+ type UpdateCheckResult = {
2
+ hasUpdate: boolean;
3
+ currentVersion: string;
4
+ latestVersion: string;
5
+ releaseNotes?: string;
6
+ };
7
+ type CheckOptions = {
8
+ fetchImpl?: typeof fetch;
9
+ packageName?: string;
10
+ currentVersion?: string;
11
+ registryUrl?: string;
12
+ };
13
+ /** 语义化版本比较:a < b → -1,a == b → 0,a > b → 1。忽略前导 v 和 prerelease。 */
14
+ declare function compareSemver(a: string, b: string): number;
15
+ /** 查询 npm registry 判断是否有新版本。网络/解析失败时返回 hasUpdate:false,绝不抛错。 */
16
+ declare function checkForUpdate(opts?: CheckOptions): Promise<UpdateCheckResult>;
17
+ export type { CheckOptions, UpdateCheckResult };
18
+ export { checkForUpdate, compareSemver };
@@ -0,0 +1,49 @@
1
+ // 版本检查(spec §18.1):查询 npm registry 比对当前版本。
2
+ const DEFAULT_PACKAGE = 'c0de-agent';
3
+ const DEFAULT_VERSION = '0.1.0';
4
+ const DEFAULT_REGISTRY = 'https://registry.npmjs.org';
5
+ /** 取 semver 的数值核心(去前导 v 与 prerelease)。 */
6
+ function semverCore(v) {
7
+ return (v.replace(/^v/, '').split('-')[0] ?? '').split('.').map(Number);
8
+ }
9
+ /** 语义化版本比较:a < b → -1,a == b → 0,a > b → 1。忽略前导 v 和 prerelease。 */
10
+ function compareSemver(a, b) {
11
+ const pa = semverCore(a);
12
+ const pb = semverCore(b);
13
+ for (let i = 0; i < 3; i++) {
14
+ const x = pa[i] ?? 0;
15
+ const y = pb[i] ?? 0;
16
+ if (x < y)
17
+ return -1;
18
+ if (x > y)
19
+ return 1;
20
+ }
21
+ return 0;
22
+ }
23
+ /** 查询 npm registry 判断是否有新版本。网络/解析失败时返回 hasUpdate:false,绝不抛错。 */
24
+ async function checkForUpdate(opts = {}) {
25
+ const pkg = opts.packageName ?? DEFAULT_PACKAGE;
26
+ const current = opts.currentVersion ?? DEFAULT_VERSION;
27
+ const registry = opts.registryUrl ?? DEFAULT_REGISTRY;
28
+ const fetchImpl = opts.fetchImpl ?? fetch;
29
+ const result = {
30
+ hasUpdate: false,
31
+ currentVersion: current,
32
+ latestVersion: current,
33
+ };
34
+ try {
35
+ const res = await fetchImpl(`${registry}/${pkg}/latest`);
36
+ if (!res.ok)
37
+ return result;
38
+ const data = (await res.json());
39
+ if (!data.version)
40
+ return result;
41
+ result.latestVersion = data.version;
42
+ result.hasUpdate = compareSemver(current, data.version) < 0;
43
+ }
44
+ catch {
45
+ // 离线/网络错误:保守地报告无更新
46
+ }
47
+ return result;
48
+ }
49
+ export { checkForUpdate, compareSemver };
@@ -0,0 +1,55 @@
1
+ CREATE TABLE "compaction_archives" (
2
+ "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
3
+ "session_id" uuid NOT NULL,
4
+ "compaction_id" uuid NOT NULL,
5
+ "archive_type" text NOT NULL,
6
+ "original_entries" jsonb NOT NULL,
7
+ "file_snapshots" jsonb DEFAULT '[]'::jsonb,
8
+ "summary" text NOT NULL,
9
+ "token_count" integer,
10
+ "searchable_text" text,
11
+ "created_at" timestamp with time zone DEFAULT now() NOT NULL
12
+ );
13
+ --> statement-breakpoint
14
+ CREATE TABLE "file_snapshots" (
15
+ "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
16
+ "session_id" uuid NOT NULL,
17
+ "entry_id" uuid,
18
+ "file_path" text NOT NULL,
19
+ "content" text NOT NULL,
20
+ "content_hash" text NOT NULL,
21
+ "token_count" integer DEFAULT 0,
22
+ "version" integer DEFAULT 1,
23
+ "created_at" timestamp with time zone DEFAULT now() NOT NULL
24
+ );
25
+ --> statement-breakpoint
26
+ CREATE TABLE "session_entries" (
27
+ "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
28
+ "session_id" uuid NOT NULL,
29
+ "tag" text NOT NULL,
30
+ "role" text,
31
+ "content" jsonb NOT NULL,
32
+ "tool_name" text,
33
+ "token_count" integer DEFAULT 0,
34
+ "created_at" timestamp with time zone DEFAULT now() NOT NULL
35
+ );
36
+ --> statement-breakpoint
37
+ CREATE TABLE "sessions" (
38
+ "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
39
+ "title" text NOT NULL,
40
+ "parent_id" uuid,
41
+ "branch_point" integer,
42
+ "metadata" jsonb DEFAULT '{}'::jsonb NOT NULL,
43
+ "created_at" timestamp with time zone DEFAULT now() NOT NULL,
44
+ "updated_at" timestamp with time zone DEFAULT now() NOT NULL
45
+ );
46
+ --> statement-breakpoint
47
+ ALTER TABLE "compaction_archives" ADD CONSTRAINT "compaction_archives_session_id_sessions_id_fk" FOREIGN KEY ("session_id") REFERENCES "public"."sessions"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
48
+ ALTER TABLE "file_snapshots" ADD CONSTRAINT "file_snapshots_session_id_sessions_id_fk" FOREIGN KEY ("session_id") REFERENCES "public"."sessions"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
49
+ ALTER TABLE "session_entries" ADD CONSTRAINT "session_entries_session_id_sessions_id_fk" FOREIGN KEY ("session_id") REFERENCES "public"."sessions"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
50
+ ALTER TABLE "sessions" ADD CONSTRAINT "sessions_parent_id_sessions_id_fk" FOREIGN KEY ("parent_id") REFERENCES "public"."sessions"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
51
+ CREATE INDEX "idx_archives_session" ON "compaction_archives" USING btree ("session_id");--> statement-breakpoint
52
+ CREATE INDEX "idx_snapshots_session_path" ON "file_snapshots" USING btree ("session_id","file_path");--> statement-breakpoint
53
+ CREATE UNIQUE INDEX "idx_snapshots_latest" ON "file_snapshots" USING btree ("session_id","file_path","version");--> statement-breakpoint
54
+ CREATE INDEX "idx_entries_session" ON "session_entries" USING btree ("session_id","created_at");--> statement-breakpoint
55
+ CREATE INDEX "idx_sessions_parent" ON "sessions" USING btree ("parent_id");
@@ -0,0 +1,13 @@
1
+ CREATE TABLE "projects" (
2
+ "id" text PRIMARY KEY NOT NULL,
3
+ "worktree" text NOT NULL,
4
+ "vcs" text,
5
+ "name" text,
6
+ "git_remote" text,
7
+ "created_at" timestamp with time zone DEFAULT now() NOT NULL,
8
+ "updated_at" timestamp with time zone DEFAULT now() NOT NULL
9
+ );
10
+ --> statement-breakpoint
11
+ ALTER TABLE "sessions" ADD COLUMN "project_id" text;--> statement-breakpoint
12
+ ALTER TABLE "sessions" ADD CONSTRAINT "sessions_project_id_projects_id_fk" FOREIGN KEY ("project_id") REFERENCES "public"."projects"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint
13
+ CREATE INDEX "idx_sessions_project" ON "sessions" USING btree ("project_id");