@xopcai/xopc 0.0.100 → 0.0.102

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 (260) hide show
  1. package/dist/browser-ext/manifest.json +1 -1
  2. package/dist/extensions/feishu/src/outbound/media-load.js +1 -1
  3. package/dist/extensions/feishu/src/workflow-progress.js +1 -1
  4. package/dist/extensions/telegram/src/adapters/streaming-adapter.js +1 -1
  5. package/dist/extensions/telegram/src/inbound-processor.js +1 -1
  6. package/dist/extensions/telegram/src/plugin.js +1 -1
  7. package/dist/extensions/telegram/src/routing-integration.js +2 -2
  8. package/dist/extensions/telegram/src/update-offset-store.js +1 -1
  9. package/dist/extensions/telegram/src/workflow-progress.js +1 -1
  10. package/dist/extensions/telegram/xopc.extension.json +1 -1
  11. package/dist/extensions/weixin/src/api/api.js +7 -5
  12. package/dist/extensions/weixin/src/api/api.js.map +1 -1
  13. package/dist/extensions/weixin/src/auth/accounts.js +1 -1
  14. package/dist/extensions/weixin/src/cdn/upload.js +1 -1
  15. package/dist/extensions/weixin/src/media/data-url.js +1 -1
  16. package/dist/extensions/weixin/src/messaging/debug-mode.js +1 -1
  17. package/dist/extensions/weixin/src/messaging/inbound.js +1 -1
  18. package/dist/extensions/weixin/src/messaging/process-message.js +1 -1
  19. package/dist/extensions/weixin/src/plugin.js +1 -1
  20. package/dist/extensions/weixin/src/storage/sync-buf.js +1 -1
  21. package/dist/extensions/weixin/src/workflow-progress.js +1 -1
  22. package/dist/gateway/static/root/assets/{agents-C5jtBK-H.js → agents-DQ0mQxIA.js} +2 -2
  23. package/dist/gateway/static/root/assets/{apps-page-smJl-qf_.js → apps-page-Uv4kOmDE.js} +1 -1
  24. package/dist/gateway/static/root/assets/{attachment-preview-renderer-DBAxQXb-.js → attachment-preview-renderer-CybMSIoS.js} +2 -2
  25. package/dist/gateway/static/root/assets/{attachment-process-heavy-Csq3TrrP.js → attachment-process-heavy-RnGYy9bC.js} +2 -2
  26. package/dist/gateway/static/root/assets/{channels-settings-CH6KgFK6.js → channels-settings-BEtP53V6.js} +1 -1
  27. package/dist/gateway/static/root/assets/{channels-status-swr-CV70xvY7.js → channels-status-swr-CxTtS1hI.js} +1 -1
  28. package/dist/gateway/static/root/assets/{cron-api-CgBO8B7I.js → cron-api-CznlPIZT.js} +1 -1
  29. package/dist/gateway/static/root/assets/{cron-page-Be0cW1cR.js → cron-page-Dx6dE3eD.js} +1 -1
  30. package/dist/gateway/static/root/assets/{dist-P8HRGUuh.js → dist-DH4IuRV0.js} +1 -1
  31. package/dist/gateway/static/root/assets/{docx-preview-Dxj1EkCO.js → docx-preview-CajIk2FR.js} +1 -1
  32. package/dist/gateway/static/root/assets/{extension-debug-page-BowuycYu.js → extension-debug-page-BNkhvRQW.js} +1 -1
  33. package/dist/gateway/static/root/assets/{extension-page-27-Zpv8O.js → extension-page-CcIGQah2.js} +1 -1
  34. package/dist/gateway/static/root/assets/{extension-settings-page-DDFa_rIq.js → extension-settings-page-DuDCSb3J.js} +1 -1
  35. package/dist/gateway/static/root/assets/{field-primitives-BWLssxEb.js → field-primitives-BUwUiDf5.js} +1 -1
  36. package/dist/gateway/static/root/assets/{heartbeat-config-api-BVuYNYT_.js → heartbeat-config-api-BjFW1cl-.js} +1 -1
  37. package/dist/gateway/static/root/assets/{index-CRDAmr3W.js → index-DKgHC1Ta.js} +9 -9
  38. package/dist/gateway/static/root/assets/{logs-page-BDX8IrDQ.js → logs-page-CiG0m81R.js} +1 -1
  39. package/dist/gateway/static/root/assets/{note-detail-page-BmQ9KeiA.js → note-detail-page-DG3DVFTf.js} +2 -2
  40. package/dist/gateway/static/root/assets/{note-time-14I9Sgqh.js → note-time-DjlllCF3.js} +1 -1
  41. package/dist/gateway/static/root/assets/{notes-page-BMfP0FFf.js → notes-page-D40ZpS0Y.js} +1 -1
  42. package/dist/gateway/static/root/assets/{sessions-page-C3scyChq.js → sessions-page-BvtdbmW8.js} +1 -1
  43. package/dist/gateway/static/root/assets/{settings-advanced-gate-2ODuCjHP.js → settings-advanced-gate-BQamRjkV.js} +2 -2
  44. package/dist/gateway/static/root/assets/{settings-form-section-DUoGKAiW.js → settings-form-section-C3_sEFfD.js} +1 -1
  45. package/dist/gateway/static/root/assets/{settings-page-CttAfOWV.js → settings-page-D972_jaS.js} +2 -2
  46. package/dist/gateway/static/root/assets/{share-preview-page-B77xqiiI.js → share-preview-page-CKCTMo0_.js} +2 -2
  47. package/dist/gateway/static/root/assets/{skills-page-B_bAZGzB.js → skills-page-QwFCB4yV.js} +1 -1
  48. package/dist/gateway/static/root/assets/{theme-store-BgCPrmxU.js → theme-store-B6KXPHgK.js} +1 -1
  49. package/dist/gateway/static/root/assets/{utils-CCGejlcX.js → utils-DCm11_KW.js} +1 -1
  50. package/dist/gateway/static/root/assets/{voice-api-key-field-DZzzihcY.js → voice-api-key-field-CywiB7Dw.js} +1 -1
  51. package/dist/gateway/static/root/assets/{workflow-page.utils-D5RMAmT4.js → workflow-page.utils-g57BRfEd.js} +1 -1
  52. package/dist/gateway/static/root/assets/{workflows-page-DEYpC6Bv.js → workflows-page-Be7l_SWw.js} +1 -1
  53. package/dist/gateway/static/root/index.html +2 -2
  54. package/dist/package.js +1 -1
  55. package/dist/src/agent/agent-manager.js +23 -7
  56. package/dist/src/agent/agent-manager.js.map +1 -1
  57. package/dist/src/agent/agent-scope.js +1 -1
  58. package/dist/src/agent/bootstrap/load-bootstrap-files.js +1 -1
  59. package/dist/src/agent/context/workspace-seed.js +2 -2
  60. package/dist/src/agent/context/workspace-state.js +1 -1
  61. package/dist/src/agent/embedded/run-for-session.js +1 -0
  62. package/dist/src/agent/embedded/run-for-session.js.map +1 -1
  63. package/dist/src/agent/goals/goal-run-store.js +4 -4
  64. package/dist/src/agent/goals/persistent-goal-service.js +1 -1
  65. package/dist/src/agent/goals/post-turn.js +2 -2
  66. package/dist/src/agent/image/load-image-media.js +2 -2
  67. package/dist/src/agent/ipc/bus.js +1 -1
  68. package/dist/src/agent/ipc/inbox.js +2 -2
  69. package/dist/src/agent/ipc/socket.js +1 -1
  70. package/dist/src/agent/mcp/bundle-mcp-materialize.js +1 -1
  71. package/dist/src/agent/mcp/bundle-mcp-runtime.js +1 -1
  72. package/dist/src/agent/mcp/mcp-transport-config.js +1 -1
  73. package/dist/src/agent/mcp/mcp-transport.js +1 -1
  74. package/dist/src/agent/memory/builtin-memory-store.js +1 -1
  75. package/dist/src/agent/memory/dreaming/deep-promotion.js +1 -1
  76. package/dist/src/agent/memory/dreaming/events.js +1 -1
  77. package/dist/src/agent/memory/dreaming/last-run.js +1 -1
  78. package/dist/src/agent/memory/dreaming/light-sweep.js +1 -1
  79. package/dist/src/agent/memory/dreaming/preview.js +1 -1
  80. package/dist/src/agent/memory/dreaming/rem-patterns.js +1 -1
  81. package/dist/src/agent/memory/dreaming/short-term-store.js +1 -1
  82. package/dist/src/agent/memory/dreaming/utils.js +1 -1
  83. package/dist/src/agent/memory/memory-flush.js +1 -1
  84. package/dist/src/agent/memory/plugin-discovery.js +1 -1
  85. package/dist/src/agent/models/manager.js +1 -1
  86. package/dist/src/agent/prompt/service-prompt-builder.js +2 -2
  87. package/dist/src/agent/reply/post-compaction-context.js +1 -1
  88. package/dist/src/agent/reply/workspace-boundary-read.js +1 -1
  89. package/dist/src/agent/sandbox/path-policy.js +2 -2
  90. package/dist/src/agent/service.js +4 -4
  91. package/dist/src/agent/session/session-inspector.js +1 -1
  92. package/dist/src/agent/skills/config.js +1 -1
  93. package/dist/src/agent/skills/hub-hash.js +2 -2
  94. package/dist/src/agent/skills/hub-lock.js +1 -1
  95. package/dist/src/agent/skills/hub-pull.js +2 -2
  96. package/dist/src/agent/skills/index.js +1 -1
  97. package/dist/src/agent/skills/managed-store.js +1 -1
  98. package/dist/src/agent/skills/scanner.js +1 -1
  99. package/dist/src/agent/skills/skill-manage-ops.js +1 -1
  100. package/dist/src/agent/skills/skill-manager.js +1 -1
  101. package/dist/src/agent/tools/dreaming-tool.js +1 -1
  102. package/dist/src/agent/tools/factory.js +1 -1
  103. package/dist/src/agent/tools/image-generate-tool.js +1 -1
  104. package/dist/src/agent/tools/send-media.js +1 -1
  105. package/dist/src/agent/tools/skill-manage-tool.js +1 -1
  106. package/dist/src/agent/tools/workflow-tool.js +1 -1
  107. package/dist/src/agent/tools/write.js +1 -1
  108. package/dist/src/agent/workflow/catalog.js +1 -1
  109. package/dist/src/auth/credentials.js +3 -4
  110. package/dist/src/auth/credentials.js.map +1 -1
  111. package/dist/src/auth/profiles/store.js +1 -1
  112. package/dist/src/auth/sync-provider-auth.js +1 -1
  113. package/dist/src/browser/cache-dir-policy.js +1 -1
  114. package/dist/src/browser/cdp-local-launcher.js +2 -2
  115. package/dist/src/browser/providers/browser-ext-install.js +3 -3
  116. package/dist/src/browser/providers/cloakbrowser.js +4 -4
  117. package/dist/src/browser/providers/playwright-doctor.js +1 -1
  118. package/dist/src/browser/stealth.js +1 -1
  119. package/dist/src/channels/outbound/persist-store.js +1 -1
  120. package/dist/src/channels/pairing/allow-from-file.js +1 -1
  121. package/dist/src/channels/pairing/pairing-store.js +2 -2
  122. package/dist/src/chat-commands/agent-edit.js +2 -2
  123. package/dist/src/chat-commands/builtins/config.js +2 -2
  124. package/dist/src/chat-commands/context.js +1 -1
  125. package/dist/src/cli/bin.d.ts +1 -0
  126. package/dist/src/cli/bin.js +1 -0
  127. package/dist/src/cli/bin.js.map +1 -1
  128. package/dist/src/cli/commands/config.js +1 -1
  129. package/dist/src/cli/commands/doctor/checks/config-health.js +1 -1
  130. package/dist/src/cli/commands/doctor/checks/database-schema.js +1 -1
  131. package/dist/src/cli/commands/doctor/checks/provider-auth.js +1 -1
  132. package/dist/src/cli/commands/doctor/checks/state-integrity.js +1 -1
  133. package/dist/src/cli/commands/doctor/checks/workspace-status.js +1 -1
  134. package/dist/src/cli/commands/extension-dev.js +1 -1
  135. package/dist/src/cli/commands/extension-marketplace.js +1 -1
  136. package/dist/src/cli/commands/extension-pack.js +1 -1
  137. package/dist/src/cli/commands/gateway/logs.js +1 -1
  138. package/dist/src/cli/commands/image.js +1 -1
  139. package/dist/src/cli/commands/init.js +3 -3
  140. package/dist/src/cli/utils/init-workspace-core.js +2 -2
  141. package/dist/src/commands/agents.config.js +1 -1
  142. package/dist/src/config/agent-profile.js +1 -1
  143. package/dist/src/config/gateway-bind.js +1 -1
  144. package/dist/src/config/index.js +5 -5
  145. package/dist/src/config/loader.js +2 -2
  146. package/dist/src/config/models-json.js +2 -2
  147. package/dist/src/config/paths-state.js +1 -1
  148. package/dist/src/config/profile.js +2 -2
  149. package/dist/src/config/workspace-path.js +1 -1
  150. package/dist/src/cron/executor.js +2 -2
  151. package/dist/src/cron/persistence.js +1 -1
  152. package/dist/src/daemon/constants.js +1 -1
  153. package/dist/src/daemon/install-plan.js +2 -2
  154. package/dist/src/daemon/launchd.js +2 -2
  155. package/dist/src/daemon/schtasks.js +2 -2
  156. package/dist/src/daemon/systemd.js +2 -2
  157. package/dist/src/extensions/bundle-mcp.js +1 -1
  158. package/dist/src/extensions/discover-extensions.js +1 -1
  159. package/dist/src/extensions/health.js +1 -1
  160. package/dist/src/extensions/loader.js +1 -1
  161. package/dist/src/extensions/lockfile.js +2 -2
  162. package/dist/src/extensions/update.js +1 -1
  163. package/dist/src/gateway/agents-admin.js +3 -3
  164. package/dist/src/gateway/file-path-classifier.js +2 -2
  165. package/dist/src/gateway/hono/lib/config-payload.js +1 -1
  166. package/dist/src/gateway/hono/lib/extension-store.js +2 -2
  167. package/dist/src/gateway/hono/lib/static-ui.js +2 -2
  168. package/dist/src/gateway/hono/oauth-async.js +36 -3
  169. package/dist/src/gateway/hono/oauth-async.js.map +1 -1
  170. package/dist/src/gateway/hono/oauth.js +1 -1
  171. package/dist/src/gateway/hono/routes/agents.js +1 -1
  172. package/dist/src/gateway/hono/routes/auth-registry-extensions.js +1 -1
  173. package/dist/src/gateway/hono/routes/config-patch/misc.js +1 -1
  174. package/dist/src/gateway/hono/routes/dreaming.js +1 -1
  175. package/dist/src/gateway/hono/routes/host-fs.js +2 -2
  176. package/dist/src/gateway/hono/routes/models.js +1 -1
  177. package/dist/src/gateway/hono/routes/notes.js +9 -22
  178. package/dist/src/gateway/hono/routes/notes.js.map +1 -1
  179. package/dist/src/gateway/hono/routes/shares.js +1 -1
  180. package/dist/src/gateway/hono/routes/workspace.js +2 -2
  181. package/dist/src/gateway/lock.js +3 -3
  182. package/dist/src/gateway/ports.js +1 -1
  183. package/dist/src/gateway/service/agent-runner.js +2 -2
  184. package/dist/src/gateway/service/marketplace-service.js +2 -2
  185. package/dist/src/gateway/service.js +1 -1
  186. package/dist/src/gateway/workspace-fs-file-list.js +1 -1
  187. package/dist/src/infra/brew.js +1 -1
  188. package/dist/src/infra/http-proxy-env.d.ts +4 -0
  189. package/dist/src/infra/http-proxy-env.js +23 -0
  190. package/dist/src/infra/http-proxy-env.js.map +1 -0
  191. package/dist/src/infra/package-json.js +1 -1
  192. package/dist/src/infra/package-update-steps.js +1 -1
  193. package/dist/src/infra/path-env.js +2 -2
  194. package/dist/src/infra/restart.js +2 -2
  195. package/dist/src/infra/stable-node-path.js +1 -1
  196. package/dist/src/infra/update-check.js +1 -1
  197. package/dist/src/infra/update-global.js +1 -1
  198. package/dist/src/infra/update-lock.js +3 -3
  199. package/dist/src/infra/update-runner.js +1 -1
  200. package/dist/src/infra/update-startup.js +2 -2
  201. package/dist/src/infra/write-file-atomic.js +2 -2
  202. package/dist/src/media/store.js +1 -1
  203. package/dist/src/notes/note-attachment-sync.d.ts +2 -3
  204. package/dist/src/notes/note-attachment-sync.js +2 -27
  205. package/dist/src/notes/note-attachment-sync.js.map +1 -1
  206. package/dist/src/notes/note-index-meta.d.ts +6 -2
  207. package/dist/src/notes/note-index-meta.js +11 -16
  208. package/dist/src/notes/note-index-meta.js.map +1 -1
  209. package/dist/src/notes/note-markdown.d.ts +61 -0
  210. package/dist/src/notes/note-markdown.js +177 -0
  211. package/dist/src/notes/note-markdown.js.map +1 -0
  212. package/dist/src/notes/service.d.ts +9 -7
  213. package/dist/src/notes/service.js +163 -312
  214. package/dist/src/notes/service.js.map +1 -1
  215. package/dist/src/notes/store.js +4 -5
  216. package/dist/src/notes/store.js.map +1 -1
  217. package/dist/src/notes/types.d.ts +29 -58
  218. package/dist/src/providers/auth-runtime/auth-profile-store.js +1 -1
  219. package/dist/src/providers/index.js +2 -2
  220. package/dist/src/providers/model-registry.js +1 -1
  221. package/dist/src/session/init-session-turn.js +2 -2
  222. package/dist/src/session/resolve-session.js +3 -3
  223. package/dist/src/session/session-title.js +2 -2
  224. package/dist/src/session/store.js +2 -2
  225. package/dist/src/share/share-auto.js +2 -2
  226. package/dist/src/share/share-store.js +3 -3
  227. package/dist/src/share/share-thumbnail.js +2 -2
  228. package/dist/src/share/share-zip.js +1 -1
  229. package/dist/src/share/site-share-store.js +3 -3
  230. package/dist/src/share/site-static-serve.js +1 -1
  231. package/dist/src/storage/sqlite/connection.js +2 -2
  232. package/dist/src/storage/sqlite/memory-index-repository.js +2 -2
  233. package/dist/src/storage/sqlite/migrations/003_add_note_markdown_meta.sql +4 -0
  234. package/dist/src/storage/sqlite/migrations/discover.js +1 -1
  235. package/dist/src/storage/sqlite/migrations/runner.d.ts +2 -2
  236. package/dist/src/storage/sqlite/migrations/runner.js +5 -5
  237. package/dist/src/storage/sqlite/migrations/runner.js.map +1 -1
  238. package/dist/src/storage/sqlite/migrations/runner.ts +2 -2
  239. package/dist/src/storage/sqlite/notes-repository.js +18 -6
  240. package/dist/src/storage/sqlite/notes-repository.js.map +1 -1
  241. package/dist/src/storage/sqlite/schema.js +4 -4
  242. package/dist/src/storage/sqlite/schema.sql +5 -1
  243. package/dist/src/tui/clipboard-image.js +3 -3
  244. package/dist/src/tui/theme-manager.js +1 -1
  245. package/dist/src/tui/tui-keybindings-file.js +1 -1
  246. package/dist/src/tui/tui-scoped-models.js +2 -2
  247. package/dist/src/tui/tui-settings.js +1 -1
  248. package/dist/src/tui/tui.js +3 -3
  249. package/dist/src/tunnel/frpc-binary.js +3 -3
  250. package/dist/src/tunnel/frpc-config.js +1 -1
  251. package/dist/src/tunnel/frpc-extract.js +1 -1
  252. package/dist/src/tunnel/tunnel-state.js +1 -1
  253. package/dist/src/utils/logger/audit.js +1 -1
  254. package/dist/src/utils/logger/log-store.js +1 -1
  255. package/dist/src/utils/logger/rotation.js +1 -1
  256. package/dist/src/voice/tts/audio.js +1 -1
  257. package/dist/src/voice/tts/providers/edge-speech.js +2 -2
  258. package/dist/src/workflows/store/event-store.js +1 -1
  259. package/dist/src/workflows/store/run-store.js +1 -1
  260. package/package.json +3 -3
@@ -1,7 +1,9 @@
1
1
  import { createLogger } from "../utils/logger/index.js";
2
2
  import { init_logger } from "../utils/logger.js";
3
3
  import { getDefaultModelSync, init_providers, resolveModel } from "../providers/index.js";
4
- import { attachmentIdFromTarget, buildNoteAttachmentRef } from "./attachment-ref.js";
4
+ import { buildNoteAttachmentRef } from "./attachment-ref.js";
5
+ import { parseNoteMarkdown } from "./note-markdown.js";
6
+ import { NotesStore } from "./store.js";
5
7
  import { partitionAttachmentsByReference } from "./note-attachment-sync.js";
6
8
  import { randomUUID } from "node:crypto";
7
9
  import { complete } from "@earendil-works/pi-ai";
@@ -9,278 +11,136 @@ import { complete } from "@earendil-works/pi-ai";
9
11
  init_providers();
10
12
  init_logger();
11
13
  const log = createLogger("NotesService");
12
- function inferKind(text, hasAttachments, attachments) {
14
+ const MAX_SNAPSHOTS_PER_NOTE = 50;
15
+ const SNAPSHOT_THROTTLE_MS = 6e4;
16
+ function inferKind(markdown, hasAttachments, attachments) {
13
17
  if (hasAttachments && attachments?.length && attachments.every((item) => item.type === "audio")) return "voice";
14
18
  if (hasAttachments) return "media";
15
- if (!text) return "thought";
16
- const lower = text.toLowerCase();
17
- if (/^(todo|task|remind|buy|call|email|meet|finish|submit|send)\b/i.test(lower) || /\b(明天|今天|记得|别忘|待办|提醒)\b/.test(text)) return "todo";
18
- if (/^https?:\/\//.test(text.trim())) return "bookmark";
19
+ if (!markdown) return "thought";
20
+ const lower = markdown.toLowerCase();
21
+ if (/^(todo|task|remind|buy|call|email|meet|finish|submit|send)\b/i.test(lower) || /\b(明天|今天|记得|别忘|待办|提醒)\b/.test(markdown)) return "todo";
22
+ if (/^https?:\/\//.test(markdown.trim())) return "bookmark";
19
23
  return "thought";
20
24
  }
21
- function deriveDefaultTitle(text) {
22
- const normalizedText = text?.trim().replace(/\s+/g, " ");
23
- if (!normalizedText) return void 0;
24
- return Array.from(normalizedText).slice(0, 10).join("");
25
- }
26
- function createBlockId() {
27
- return `block_${Date.now()}_${randomUUID().slice(0, 8)}`;
28
- }
29
- const IMAGE_MARKDOWN = /^!\[([^\]]*)\]\(([^)]+)\)$/;
30
- function noteTextToBlocks(text, noteId) {
31
- if (!text?.trim()) return void 0;
32
- const now = Date.now();
33
- return text.split(/\n{2,}/).map((part) => {
34
- const trimmed = part.trim();
35
- if (noteId) {
36
- const imageMatch = trimmed.match(IMAGE_MARKDOWN);
37
- if (imageMatch) {
38
- const attachmentId = attachmentIdFromTarget(imageMatch[2], noteId);
39
- if (attachmentId) return {
40
- id: createBlockId(),
41
- type: "image",
42
- attachmentId,
43
- alt: imageMatch[1] || void 0,
44
- createdAt: now,
45
- updatedAt: now
46
- };
47
- }
48
- }
49
- return {
50
- id: createBlockId(),
51
- type: "paragraph",
52
- text: trimmed,
53
- createdAt: now,
54
- updatedAt: now
55
- };
56
- });
57
- }
58
- function blocksToPlainText(blocks, noteId) {
59
- if (!blocks?.length) return void 0;
60
- return blocks.map((block) => {
61
- if (block.type === "divider") return "---";
62
- if (block.type === "todo") return `${block.checked ? "[x]" : "[ ]"} ${block.text}`;
63
- if (block.type === "image") {
64
- if (noteId) return `![${block.alt ?? ""}](${buildNoteAttachmentRef(noteId, block.attachmentId)})`;
65
- return block.alt ?? "";
66
- }
67
- return block.text;
68
- }).filter((text) => text.trim().length > 0).join("\n\n");
69
- }
70
- function createAiOrganizedBlocks(blocks, instruction) {
71
- const now = Date.now();
72
- const lines = (blocksToPlainText(blocks) || "").split(/\n+/).map((line) => line.trim()).filter(Boolean);
73
- const wantsTodos = /待办|todo|task|行动|提醒/i.test(instruction);
74
- const wantsSummary = /摘要|总结|summary|压缩/i.test(instruction);
75
- if (wantsTodos) {
76
- const candidates = lines.filter((line) => /要|需|记得|todo|task|完成|提交|联系|跟进|提醒/i.test(line));
77
- return (candidates.length ? candidates : lines).slice(0, 12).map((line) => ({
78
- id: createBlockId(),
79
- type: "todo",
80
- text: line.replace(/^[-*\d.\s\[\]x]+/i, "").trim(),
81
- checked: false,
82
- createdAt: now,
83
- updatedAt: now
84
- }));
85
- }
86
- if (wantsSummary) {
87
- const summary = lines.join(" ").slice(0, 220);
88
- return [{
89
- id: createBlockId(),
90
- type: "paragraph",
91
- text: summary,
92
- createdAt: now,
93
- updatedAt: now
94
- }];
95
- }
96
- const titleText = lines[0]?.slice(0, 40) || "整理后的笔记";
97
- const bodyLines = lines.slice(1).length ? lines.slice(1) : lines;
98
- return [{
99
- id: createBlockId(),
100
- type: "heading",
101
- text: titleText,
102
- level: 2,
103
- createdAt: now,
104
- updatedAt: now
105
- }, ...bodyLines.map((line) => ({
106
- id: createBlockId(),
107
- type: "bulletList",
108
- text: line.replace(/^[-*\d.\s]+/, "").trim(),
109
- indent: 0,
110
- createdAt: now,
111
- updatedAt: now
112
- }))];
25
+ function deriveDefaultTitle(markdown) {
26
+ const normalized = parseNoteMarkdown(markdown ?? "").plainText.trim().replace(/\s+/g, " ");
27
+ return normalized ? Array.from(normalized).slice(0, 10).join("") : void 0;
113
28
  }
114
- function splitMeaningfulLines(text) {
115
- return (text ?? "").split(/[\n。!?!?;;]+/).map((line) => line.trim().replace(/^[-*\d.\s]+/, "")).filter((line) => line.length > 0);
29
+ function splitMeaningfulLines(markdown) {
30
+ return parseNoteMarkdown(markdown ?? "").plainText.split(/\n|。|;|;/).map((line) => line.trim()).filter(Boolean);
116
31
  }
117
- function summarizeIdea(text) {
118
- const joined = splitMeaningfulLines(text).join(" ");
119
- return Array.from(joined || "这个想法").slice(0, 90).join("");
32
+ function summarizeIdea(markdown) {
33
+ return splitMeaningfulLines(markdown).slice(0, 3).join("").slice(0, 140);
120
34
  }
121
35
  function inferCatalysisStage(note) {
122
- const text = `${note.title ?? ""}\n${note.text ?? ""}`;
123
- if (/发布|上线|分享|推广|launch|ship/i.test(text)) return "shipped";
124
- if (/验证|实验|用户|指标|反馈|validate|experiment/i.test(text)) return "validating";
125
- if (/实现|开发|MVP|原型|prototype|build/i.test(text)) return "developing";
126
- if (text.trim().length > 80) return "incubating";
127
- return "seed";
36
+ const text = `${note.title ?? ""}\n${note.markdown}`;
37
+ if (/上线|发布|launch|ship/i.test(text)) return "shipped";
38
+ if (/验证|访谈|用户|测试|validate/i.test(text)) return "validating";
39
+ if (/开发|实现|构建|build|mvp/i.test(text)) return "developing";
40
+ if (/想法|机会|问题|idea|seed/i.test(text)) return "seed";
41
+ return "incubating";
128
42
  }
129
43
  function buildCatalysisReport(note) {
130
- const generatedAt = Date.now();
131
- const summary = summarizeIdea(note.text ?? note.title);
44
+ const summary = summarizeIdea(note.markdown || note.title);
132
45
  const title = note.title?.trim() || summary.slice(0, 28) || "未命名想法";
133
- const hasUserSignal = /用户|客户|读者|创作者|团队|个人|开发者|founder|creator|user/i.test(summary);
134
- const hasProductSignal = /产品|工具|平台|workflow|agent|AI|自动|系统|应用/i.test(summary);
135
- const confidence = Math.min(.86, Math.max(.42, .48 + (hasUserSignal ? .16 : 0) + (hasProductSignal ? .18 : 0) + Math.min(summary.length, 120) / 600));
46
+ const actions = [
47
+ {
48
+ kind: "research",
49
+ text: `验证「${title}」的真实需求和目标用户`
50
+ },
51
+ {
52
+ kind: "task",
53
+ text: `拆解「${title}」的最小可行下一步`
54
+ },
55
+ {
56
+ kind: "chat",
57
+ text: `继续和 AI 讨论「${title}」的方案`
58
+ }
59
+ ];
136
60
  return {
137
61
  originalNoteId: note.id,
138
- generatedAt,
62
+ generatedAt: Date.now(),
139
63
  title,
140
- valueHypothesis: `如果把「${summary}」推进成一个可体验的小成果,它最可能的价值是帮助用户更快完成判断、表达或行动。`,
141
- targetUsers: hasUserSignal ? ["笔记作者自己", "有类似场景的目标用户"] : ["笔记作者自己", "未来可能被这个想法帮助的人"],
64
+ valueHypothesis: summary || "这条笔记可能包含一个待澄清的想法。",
65
+ targetUsers: ["待确认的核心用户"],
142
66
  keyQuestions: [
143
- "这个想法最想解决的具体痛点是什么?",
144
- "第一个可验证的用户场景是什么?",
145
- "什么样的最小成果能证明它值得继续投入?"
67
+ "用户为什么现在需要它?",
68
+ "最小可验证版本是什么?",
69
+ "如何判断它值得继续投入?"
146
70
  ],
147
71
  mvpPath: [
148
- "把想法改写成一句清晰的问题陈述。",
149
- "列出 1 个目标用户和 1 个高频使用场景。",
150
- "产出一个最小原型、提纲或行动清单。"
151
- ],
152
- risks: ["想法还停留在概念层,缺少明确使用场景。", "下一步过大时容易变成长期搁置的项目。"],
153
- nextActions: [
154
- {
155
- kind: "chat",
156
- text: "和 AI 继续深聊这个想法,收敛成问题陈述。"
157
- },
158
- {
159
- kind: "research",
160
- text: "补充 3 个相似产品、案例或用户反馈。"
161
- },
162
- {
163
- kind: "task",
164
- text: "写下今天能完成的一个最小推进动作。"
165
- }
72
+ "定义目标用户",
73
+ "写出最小场景",
74
+ "完成一次验证"
166
75
  ],
167
- confidence: Number(confidence.toFixed(2))
76
+ risks: ["需求不够明确", "执行路径过大"],
77
+ nextActions: actions,
78
+ confidence: summary ? .58 : .35
168
79
  };
169
80
  }
170
- function stripJsonCodeFence(raw) {
171
- return raw.trim().replace(/^```(?:json)?\s*/i, "").replace(/\s*```$/i, "").trim();
172
- }
173
- function extractJsonObject(raw) {
174
- if (!raw.trim()) return null;
175
- const text = stripJsonCodeFence(raw);
176
- try {
177
- const data = JSON.parse(text);
178
- return data && typeof data === "object" && !Array.isArray(data) ? data : null;
179
- } catch {
180
- const start = text.indexOf("{");
181
- const end = text.lastIndexOf("}");
182
- if (start < 0 || end <= start) return null;
183
- try {
184
- const data = JSON.parse(text.slice(start, end + 1));
185
- return data && typeof data === "object" && !Array.isArray(data) ? data : null;
186
- } catch {
187
- return null;
188
- }
189
- }
190
- }
191
- function extractAssistantText(result) {
192
- if (!Array.isArray(result.content)) return "";
193
- return result.content.filter((block) => {
194
- return !!block && typeof block === "object" && block.type === "text";
195
- }).map((block) => block.text).join("").trim();
196
- }
197
- function stringArray(value, fallback, limit) {
198
- if (!Array.isArray(value)) return fallback;
199
- const items = value.map((item) => typeof item === "string" ? item.trim() : "").filter(Boolean).slice(0, limit);
200
- return items.length ? items : fallback;
201
- }
202
- function catalysisActions(value, fallback) {
203
- if (!Array.isArray(value)) return fallback;
204
- const allowed = new Set([
205
- "task",
206
- "workflow",
207
- "research",
208
- "share",
209
- "chat"
210
- ]);
211
- const items = [];
212
- for (const item of value) {
213
- if (!item || typeof item !== "object") continue;
214
- const row = item;
215
- const kind = typeof row.kind === "string" && allowed.has(row.kind) ? row.kind : "task";
216
- const text = typeof row.text === "string" ? row.text.trim() : "";
217
- if (text) items.push({
218
- kind,
219
- text
220
- });
221
- }
222
- return items.length ? items.slice(0, 5) : fallback;
223
- }
224
81
  function normalizeAiCatalysisReport(note, data) {
225
82
  const fallback = buildCatalysisReport(note);
226
- const confidenceRaw = typeof data.confidence === "number" ? data.confidence : fallback.confidence;
227
- const confidence = Math.min(1, Math.max(0, confidenceRaw));
83
+ const stringArray = (value, fallbackValue) => Array.isArray(value) ? value.filter((item) => typeof item === "string").slice(0, 12) : fallbackValue;
84
+ const actions = Array.isArray(data.nextActions) ? data.nextActions.map((item) => {
85
+ if (!item || typeof item !== "object") return null;
86
+ const row = item;
87
+ const kind = row.kind === "task" || row.kind === "workflow" || row.kind === "research" || row.kind === "share" || row.kind === "chat" ? row.kind : "task";
88
+ return typeof row.text === "string" && row.text.trim() ? {
89
+ kind,
90
+ text: row.text.trim()
91
+ } : null;
92
+ }).filter((item) => Boolean(item)).slice(0, 12) : fallback.nextActions;
228
93
  return {
229
94
  originalNoteId: note.id,
230
95
  generatedAt: Date.now(),
231
- title: typeof data.title === "string" && data.title.trim() ? data.title.trim().slice(0, 80) : fallback.title,
96
+ title: typeof data.title === "string" && data.title.trim() ? data.title.trim() : fallback.title,
232
97
  valueHypothesis: typeof data.valueHypothesis === "string" && data.valueHypothesis.trim() ? data.valueHypothesis.trim() : fallback.valueHypothesis,
233
- targetUsers: stringArray(data.targetUsers, fallback.targetUsers, 5),
234
- keyQuestions: stringArray(data.keyQuestions, fallback.keyQuestions, 6),
235
- mvpPath: stringArray(data.mvpPath, fallback.mvpPath, 6),
236
- risks: stringArray(data.risks, fallback.risks, 5),
237
- nextActions: catalysisActions(data.nextActions, fallback.nextActions),
238
- confidence: Number(confidence.toFixed(2))
98
+ targetUsers: stringArray(data.targetUsers, fallback.targetUsers),
99
+ keyQuestions: stringArray(data.keyQuestions, fallback.keyQuestions),
100
+ mvpPath: stringArray(data.mvpPath, fallback.mvpPath),
101
+ risks: stringArray(data.risks, fallback.risks),
102
+ nextActions: actions,
103
+ confidence: typeof data.confidence === "number" ? Math.max(0, Math.min(1, data.confidence)) : fallback.confidence
239
104
  };
240
105
  }
241
106
  function buildCatalysisPrompt(note) {
242
- return `你是一个帮助用户把想法推进成成果的个人 AI Agent。请基于这条 Note 做“想法催化”,帮助用户进入:想法 → 创造 → 分享 → 反馈 的循环。\n\n要求:\n- 使用自然、具体的中文。\n- 不要空泛鼓励,要给出可执行路径。\n- 输出必须是单个 JSON 对象,不要 Markdown,不要代码块。\n- 字段必须包含:title, valueHypothesis, targetUsers, keyQuestions, mvpPath, risks, nextActions, confidence。\n- nextActions 每项格式为 {"kind":"task|workflow|research|share|chat","text":"..."}。\n- confidence 是 0 到 1 的数字。\n\nNote 标题:${note.title?.trim() || "未命名笔记"}\n\nNote 正文:\n${((note.text ?? "").trim() || "(无正文)").slice(0, 6e3)}`;
107
+ return `请把下面的 Markdown 笔记催化成可执行的想法报告,输出 JSON:\n{\n "title": string,\n "valueHypothesis": string,\n "targetUsers": string[],\n "keyQuestions": string[],\n "mvpPath": string[],\n "risks": string[],\n "nextActions": [{"text": string, "kind": "task"|"workflow"|"research"|"share"|"chat"}],\n "confidence": number\n}\n\n标题:${note.title?.trim() || "未命名笔记"}\n\nMarkdown:\n${note.markdown.trim() || "(无正文)"}`;
243
108
  }
244
109
  async function buildAiCatalysisReport(note, config) {
245
- const data = extractJsonObject(extractAssistantText(await complete(resolveModel(getDefaultModelSync(config)), { messages: [{
110
+ const response = await complete(resolveModel(getDefaultModelSync(config) ?? "openai/gpt-4o-mini"), { messages: [{
246
111
  role: "user",
247
112
  content: buildCatalysisPrompt(note),
248
113
  timestamp: Date.now()
249
- }] }, {
250
- maxTokens: 1800,
251
- temperature: .2
252
- })));
253
- if (!data) throw new Error("Catalysis model did not return valid JSON");
254
- return normalizeAiCatalysisReport(note, data);
114
+ }] }, { temperature: .2 });
115
+ let responseText = "";
116
+ if (Array.isArray(response.content)) {
117
+ for (const part of response.content) if (part && typeof part === "object" && part.type === "text") responseText += String(part.text || "");
118
+ }
119
+ const raw = responseText.trim().replace(/^```json\s*/i, "").replace(/```$/i, "").trim();
120
+ return normalizeAiCatalysisReport(note, JSON.parse(raw));
255
121
  }
256
- const SNAPSHOT_THROTTLE_MS = 6e4;
257
- const MAX_SNAPSHOTS_PER_NOTE = 30;
258
122
  var NotesService = class {
259
- store;
260
123
  lastSnapshotAt = /* @__PURE__ */ new Map();
261
- constructor(store) {
124
+ constructor(store = new NotesStore()) {
262
125
  this.store = store;
263
126
  }
264
127
  async initialize() {
265
128
  await this.store.initialize();
266
- log.debug("NotesService initialized");
267
129
  }
268
- async quickCapture(text, source) {
269
- const now = Date.now();
130
+ async quickCapture(markdown, source) {
270
131
  const id = randomUUID();
271
- const blocks = noteTextToBlocks(text, id);
132
+ const now = Date.now();
272
133
  const note = {
273
134
  id,
274
- title: deriveDefaultTitle(text),
275
- kind: inferKind(text),
135
+ title: deriveDefaultTitle(markdown),
136
+ kind: inferKind(markdown),
276
137
  status: "inbox",
277
- text,
278
- blocks,
138
+ markdown,
279
139
  createdAt: now,
280
140
  updatedAt: now,
281
141
  capturedVia: source,
282
- localVersion: 0,
283
- remoteVersion: 1
142
+ remoteVersion: 1,
143
+ localVersion: 1
284
144
  };
285
145
  await this.store.addNote(note);
286
146
  log.debug({
@@ -290,24 +150,25 @@ var NotesService = class {
290
150
  return note;
291
151
  }
292
152
  async createNote(params) {
293
- const now = Date.now();
294
153
  const id = randomUUID();
295
- const blocks = params.blocks ?? noteTextToBlocks(params.text, id);
296
- const text = params.text ?? blocksToPlainText(blocks, id);
154
+ const now = Date.now();
155
+ const markdown = params.markdown ?? "";
297
156
  const note = {
298
157
  id,
299
- title: params.title,
300
- kind: params.kind || inferKind(text),
158
+ title: params.title ?? deriveDefaultTitle(markdown),
159
+ kind: params.kind ?? inferKind(markdown),
301
160
  status: "inbox",
302
- text,
303
- blocks,
161
+ markdown,
162
+ attachments: [],
304
163
  createdAt: now,
305
164
  updatedAt: now,
306
165
  capturedVia: params.capturedVia,
307
166
  tags: params.tags,
308
167
  pinned: params.pinned,
309
- localVersion: 0,
310
- remoteVersion: 1
168
+ groupId: params.groupId,
169
+ taskMeta: params.taskMeta,
170
+ remoteVersion: 1,
171
+ localVersion: 1
311
172
  };
312
173
  await this.store.addNote(note);
313
174
  log.debug({
@@ -322,25 +183,16 @@ var NotesService = class {
322
183
  async updateNote(id, patch, trigger = "edit") {
323
184
  const existing = await this.store.getNote(id);
324
185
  if (!existing) return null;
325
- const contentTouched = patch.text !== void 0 || patch.blocks !== void 0 || patch.title !== void 0;
186
+ const contentTouched = patch.markdown !== void 0 || patch.title !== void 0;
326
187
  if (contentTouched) await this.maybeSaveSnapshot(existing, trigger);
327
188
  const normalizedPatch = { ...patch };
328
- if (patch.blocks) normalizedPatch.text = patch.text ?? blocksToPlainText(patch.blocks, existing.id);
329
- else if (typeof patch.text === "string") normalizedPatch.blocks = patch.blocks ?? noteTextToBlocks(patch.text, existing.id);
330
- normalizedPatch.remoteVersion = (existing.remoteVersion ?? 0) + 1;
331
- if (contentTouched) {
332
- const merged = {
333
- ...existing,
334
- ...normalizedPatch,
335
- id: existing.id,
336
- createdAt: existing.createdAt,
337
- updatedAt: Date.now()
338
- };
339
- const reconciled = await this.reconcileAttachments(merged);
340
- normalizedPatch.attachments = reconciled.attachments;
341
- normalizedPatch.kind = reconciled.kind;
189
+ if (patch.markdown !== void 0) normalizedPatch.kind = patch.kind ?? inferKind(patch.markdown, Boolean(existing.attachments?.length), existing.attachments);
190
+ if (contentTouched || patch.attachments) {
191
+ normalizedPatch.remoteVersion = (existing.remoteVersion ?? 1) + 1;
192
+ normalizedPatch.localVersion = patch.localVersion ?? (existing.localVersion ?? 1) + 1;
342
193
  }
343
- return this.store.updateNote(id, normalizedPatch);
194
+ const updatedRaw = await this.store.updateNote(id, normalizedPatch);
195
+ return updatedRaw ? this.reconcileAttachments(updatedRaw) : null;
344
196
  }
345
197
  async reconcileAttachments(note) {
346
198
  const { kept, removed } = partitionAttachmentsByReference(note);
@@ -351,10 +203,12 @@ var NotesService = class {
351
203
  removedIds: removed.map((attachment) => attachment.id)
352
204
  }, "Pruned orphan note attachments");
353
205
  const hasAttachments = kept.length > 0;
354
- return {
206
+ return await this.store.updateNote(note.id, {
207
+ attachments: kept,
208
+ kind: inferKind(note.markdown, hasAttachments, kept)
209
+ }) ?? {
355
210
  ...note,
356
- attachments: hasAttachments ? kept : void 0,
357
- kind: inferKind(note.text, hasAttachments, kept)
211
+ attachments: kept
358
212
  };
359
213
  }
360
214
  async syncNote(id, patch, baseRemoteVersion) {
@@ -363,8 +217,7 @@ var NotesService = class {
363
217
  note: null,
364
218
  conflict: false
365
219
  };
366
- const currentRemoteVersion = existing.remoteVersion ?? 0;
367
- if (baseRemoteVersion !== void 0 && baseRemoteVersion < currentRemoteVersion) return {
220
+ if (baseRemoteVersion !== void 0 && existing.remoteVersion !== void 0 && baseRemoteVersion < existing.remoteVersion) return {
368
221
  note: existing,
369
222
  conflict: true
370
223
  };
@@ -373,18 +226,29 @@ var NotesService = class {
373
226
  conflict: false
374
227
  };
375
228
  }
376
- async createAiEditPatch(id, instruction, blocks) {
229
+ async createAiEditPatch(id, instruction, markdownOverride) {
377
230
  const note = await this.store.getNote(id);
378
231
  if (!note) return null;
379
- const organizedBlocks = createAiOrganizedBlocks(blocks?.length ? blocks : note.blocks ?? noteTextToBlocks(note.text, note.id) ?? [], instruction);
232
+ const source = markdownOverride ?? note.markdown;
233
+ const parsed = parseNoteMarkdown(source, id);
234
+ const wantsTodos = /待办|todo|task|行动|提醒/i.test(instruction);
235
+ const wantsSummary = /摘要|总结|summary|压缩/i.test(instruction);
236
+ let generated = "";
237
+ if (wantsTodos) {
238
+ const lines = source.split(/\n|。|;|;/).map((line) => line.trim()).filter(Boolean);
239
+ generated = (lines.length ? lines : [parsed.plainText]).slice(0, 12).map((line) => `- [ ] ${line.replace(/^[-*\d.\s\[\]x]+/i, "").trim()}`).join("\n");
240
+ } else if (wantsSummary) generated = `> [!SUMMARY]\n> ${parsed.plainText.slice(0, 220)}`;
241
+ else generated = `${source.trimEnd()}\n\n## AI 整理\n\n${instruction.trim()}`;
380
242
  return {
381
243
  message: "AI edit patch generated",
382
244
  patch: {
383
245
  id: randomUUID(),
384
- summary: `已根据「${instruction.slice(0, 40)}」生成可预览的块级整理建议`,
246
+ summary: instruction,
385
247
  operations: [{
386
- type: "replaceBlocks",
387
- blocks: organizedBlocks
248
+ type: "replaceRange",
249
+ from: 0,
250
+ to: source.length,
251
+ markdown: generated || source
388
252
  }]
389
253
  }
390
254
  };
@@ -402,27 +266,24 @@ var NotesService = class {
402
266
  }, "AI note catalysis failed; using local fallback report");
403
267
  report = buildCatalysisReport(note);
404
268
  }
405
- const existingDeep = note.aiDeep;
406
- const catalysis = {
407
- ...existingDeep?.catalysis,
408
- status: "catalyzed",
409
- stage: inferCatalysisStage(note),
410
- lastCatalyzedAt: report.generatedAt,
411
- confidence: report.confidence,
412
- report
413
- };
414
269
  const updated = await this.updateNote(id, {
270
+ aiDeep: {
271
+ ...note.aiDeep,
272
+ processedAt: Date.now(),
273
+ catalysis: {
274
+ status: "catalyzed",
275
+ stage: inferCatalysisStage(note),
276
+ lastCatalyzedAt: Date.now(),
277
+ confidence: report.confidence,
278
+ report,
279
+ ...note.aiDeep?.catalysis
280
+ }
281
+ },
415
282
  ai: {
416
283
  ...note.ai,
417
284
  intent: note.ai?.intent ?? "idea",
418
285
  summary: note.ai?.summary ?? report.valueHypothesis
419
286
  },
420
- aiDeep: {
421
- ...existingDeep,
422
- processedAt: report.generatedAt,
423
- insights: report.valueHypothesis,
424
- catalysis
425
- },
426
287
  status: note.status === "inbox" ? "processed" : note.status
427
288
  }, "ai_edit");
428
289
  return updated ? {
@@ -433,22 +294,20 @@ var NotesService = class {
433
294
  async recordCatalysisFeedback(id, feedback) {
434
295
  const note = await this.store.getNote(id);
435
296
  if (!note) return null;
436
- const now = Date.now();
437
297
  return this.updateNote(id, { aiDeep: {
438
298
  ...note.aiDeep,
439
- processedAt: now,
299
+ processedAt: Date.now(),
440
300
  catalysis: {
441
301
  status: note.aiDeep?.catalysis?.status ?? "catalyzed",
442
302
  ...note.aiDeep?.catalysis,
443
303
  feedback
444
304
  }
445
- } }, "ai_edit");
305
+ } });
446
306
  }
447
307
  async linkNoteThread(id, sessionKey) {
448
308
  const note = await this.store.getNote(id);
449
309
  if (!note) return null;
450
310
  const existingKeys = note.aiDeep?.catalysis?.linkedSessionKeys ?? [];
451
- const linkedSessionKeys = Array.from(new Set([sessionKey, ...existingKeys]));
452
311
  return this.updateNote(id, { aiDeep: {
453
312
  ...note.aiDeep,
454
313
  processedAt: Date.now(),
@@ -456,32 +315,28 @@ var NotesService = class {
456
315
  status: note.aiDeep?.catalysis?.status ?? "none",
457
316
  ...note.aiDeep?.catalysis,
458
317
  sourceSessionKey: note.aiDeep?.catalysis?.sourceSessionKey ?? sessionKey,
459
- linkedSessionKeys
318
+ linkedSessionKeys: Array.from(new Set([...existingKeys, sessionKey]))
460
319
  }
461
320
  } }, "sync");
462
321
  }
463
322
  async listNoteThreads(id) {
464
323
  const note = await this.store.getNote(id);
465
324
  if (!note) return null;
466
- const keys = [note.aiDeep?.catalysis?.sourceSessionKey, ...note.aiDeep?.catalysis?.linkedSessionKeys ?? []].filter((key) => typeof key === "string" && key.length > 0);
467
- return Array.from(new Set(keys));
325
+ return Array.from(new Set([note.aiDeep?.catalysis?.sourceSessionKey, ...note.aiDeep?.catalysis?.linkedSessionKeys ?? []].filter((key) => Boolean(key))));
468
326
  }
469
327
  async appendTextToNote(id, content, heading = "AI 讨论沉淀") {
470
328
  const note = await this.store.getNote(id);
471
329
  if (!note) return null;
472
330
  const trimmed = content.trim();
473
331
  if (!trimmed) return note;
474
- const currentText = note.text?.trimEnd() ?? "";
475
- const nextText = `${currentText}${currentText ? "\n\n" : ""}## ${heading}\n\n${trimmed}`;
476
- return this.updateNote(id, { text: nextText }, "ai_edit");
332
+ const current = note.markdown.trimEnd();
333
+ const nextMarkdown = `${current}${current ? "\n\n" : ""}## ${heading}\n\n${trimmed}\n`;
334
+ return this.updateNote(id, { markdown: nextMarkdown }, "ai_edit");
477
335
  }
478
336
  async deleteNote(id) {
479
- const deleted = await this.store.deleteNote(id);
480
- if (deleted) {
481
- await this.store.deleteAllSnapshots(id);
482
- this.lastSnapshotAt.delete(id);
483
- }
484
- return deleted;
337
+ const removed = await this.store.deleteNote(id);
338
+ if (removed) await this.store.deleteAllSnapshots(id);
339
+ return removed;
485
340
  }
486
341
  async listNotes(query = {}) {
487
342
  return this.store.listNotes(query);
@@ -492,7 +347,7 @@ var NotesService = class {
492
347
  const { relativePath, size } = await this.store.saveAttachment(noteId, file.name, file.buffer);
493
348
  const attachment = {
494
349
  id: randomUUID(),
495
- type: inferAttachmentType(file.mimeType),
350
+ type: file.mimeType.startsWith("image/") ? "image" : file.mimeType.startsWith("audio/") ? "audio" : file.mimeType.startsWith("video/") ? "video" : "file",
496
351
  mimeType: file.mimeType,
497
352
  fileName: file.name,
498
353
  size,
@@ -526,17 +381,17 @@ var NotesService = class {
526
381
  }
527
382
  async restoreNoteSnapshot(noteId, timestamp) {
528
383
  const snapshot = await this.store.getSnapshot(noteId, timestamp);
529
- if (!snapshot) return null;
530
384
  const existing = await this.store.getNote(noteId);
531
- if (!existing) return null;
532
- await this.store.saveSnapshot(existing, "restore");
385
+ if (!snapshot || !existing) return null;
386
+ await this.maybeSaveSnapshot(existing, "restore");
533
387
  this.lastSnapshotAt.set(noteId, Date.now());
534
388
  await this.store.pruneSnapshots(noteId, MAX_SNAPSHOTS_PER_NOTE);
535
389
  return this.store.updateNote(noteId, {
536
390
  title: snapshot.title,
537
- text: snapshot.text,
538
- blocks: snapshot.blocks,
539
- tags: snapshot.tags
391
+ markdown: snapshot.markdown,
392
+ tags: snapshot.tags,
393
+ kind: snapshot.kind,
394
+ status: snapshot.status
540
395
  });
541
396
  }
542
397
  async flush() {
@@ -545,18 +400,19 @@ var NotesService = class {
545
400
  async moveToGroup(noteId, groupId) {
546
401
  return this.updateNote(noteId, { groupId: groupId ?? void 0 });
547
402
  }
548
- async createTask(title, source, options) {
403
+ async createTask(title, source, opts = {}) {
549
404
  return this.createNote({
550
405
  title,
406
+ markdown: `- [ ] ${title}`,
551
407
  kind: "task",
552
408
  capturedVia: source,
553
- groupId: options?.groupId,
409
+ groupId: opts.groupId,
554
410
  taskMeta: {
555
411
  done: false,
556
- dueAt: options?.dueAt,
557
- priority: options?.priority,
558
- sourceSessionKey: options?.sourceSessionKey,
559
- sourceNoteId: options?.sourceNoteId
412
+ dueAt: opts.dueAt,
413
+ priority: opts.priority,
414
+ sourceSessionKey: opts.sourceSessionKey,
415
+ sourceNoteId: opts.sourceNoteId
560
416
  }
561
417
  });
562
418
  }
@@ -564,12 +420,13 @@ var NotesService = class {
564
420
  const note = await this.store.getNote(noteId);
565
421
  if (!note || note.kind !== "task") return null;
566
422
  const done = !note.taskMeta?.done;
423
+ const markdown = note.markdown.replace(/^- \[[ xX]\]/m, `- [${done ? "x" : " "}]`);
567
424
  return this.updateNote(noteId, {
425
+ markdown,
568
426
  taskMeta: {
569
427
  ...note.taskMeta,
570
428
  done
571
- },
572
- status: done ? "archived" : "processed"
429
+ }
573
430
  });
574
431
  }
575
432
  async updateTaskMeta(noteId, patch) {
@@ -598,13 +455,7 @@ var NotesService = class {
598
455
  await this.store.pruneSnapshots(note.id, MAX_SNAPSHOTS_PER_NOTE);
599
456
  }
600
457
  };
601
- function inferAttachmentType(mimeType) {
602
- if (mimeType.startsWith("image/")) return "image";
603
- if (mimeType.startsWith("video/")) return "video";
604
- if (mimeType.startsWith("audio/")) return "audio";
605
- return "file";
606
- }
607
458
  //#endregion
608
- export { NotesService };
459
+ export { NotesService, buildNoteAttachmentRef };
609
460
 
610
461
  //# sourceMappingURL=service.js.map