@lapis-notes/ai 0.1.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 (266) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/LICENSE.md +8 -0
  3. package/README.md +20 -0
  4. package/dist/ai-plugin.d.ts +88 -0
  5. package/dist/ai-plugin.js +742 -0
  6. package/dist/bootstrap/build.d.ts +15 -0
  7. package/dist/bootstrap/build.js +28 -0
  8. package/dist/bootstrap/folder-instructions.d.ts +4 -0
  9. package/dist/bootstrap/folder-instructions.js +49 -0
  10. package/dist/bootstrap/format.d.ts +2 -0
  11. package/dist/bootstrap/format.js +46 -0
  12. package/dist/bootstrap/instructions.d.ts +1 -0
  13. package/dist/bootstrap/instructions.js +8 -0
  14. package/dist/bootstrap/types.d.ts +36 -0
  15. package/dist/bootstrap/types.js +3 -0
  16. package/dist/catalog/ai-catalog-panel.svelte +742 -0
  17. package/dist/catalog/ai-catalog-panel.svelte.d.ts +13 -0
  18. package/dist/catalog/ai-catalog-view-type.d.ts +1 -0
  19. package/dist/catalog/ai-catalog-view-type.js +1 -0
  20. package/dist/catalog/ai-catalog-view.d.ts +22 -0
  21. package/dist/catalog/ai-catalog-view.js +48 -0
  22. package/dist/catalog/filter.d.ts +3 -0
  23. package/dist/catalog/filter.js +24 -0
  24. package/dist/catalog/inventory.d.ts +21 -0
  25. package/dist/catalog/inventory.js +255 -0
  26. package/dist/catalog/tree.d.ts +12 -0
  27. package/dist/catalog/tree.js +60 -0
  28. package/dist/catalog/types.d.ts +44 -0
  29. package/dist/catalog/types.js +1 -0
  30. package/dist/chat/ai-approval-card.svelte +54 -0
  31. package/dist/chat/ai-approval-card.svelte.d.ts +9 -0
  32. package/dist/chat/ai-chat-panel.svelte +1288 -0
  33. package/dist/chat/ai-chat-panel.svelte.d.ts +50 -0
  34. package/dist/chat/ai-inventory-result.css +55 -0
  35. package/dist/chat/ai-inventory-result.svelte +63 -0
  36. package/dist/chat/ai-inventory-result.svelte.d.ts +5 -0
  37. package/dist/chat/ai-question-card.svelte +132 -0
  38. package/dist/chat/ai-question-card.svelte.d.ts +9 -0
  39. package/dist/chat/ai-view-bootstrap.d.ts +13 -0
  40. package/dist/chat/ai-view-bootstrap.js +86 -0
  41. package/dist/chat/ai-view-panel.svelte +89 -0
  42. package/dist/chat/ai-view-panel.svelte.d.ts +13 -0
  43. package/dist/chat/ai-view-type.d.ts +1 -0
  44. package/dist/chat/ai-view-type.js +1 -0
  45. package/dist/chat/ai-view.d.ts +60 -0
  46. package/dist/chat/ai-view.js +121 -0
  47. package/dist/chat/chat-controller.svelte.d.ts +82 -0
  48. package/dist/chat/chat-controller.svelte.js +1926 -0
  49. package/dist/chat/chat-items.d.ts +88 -0
  50. package/dist/chat/chat-items.js +9 -0
  51. package/dist/chat/chat-mentions.d.ts +15 -0
  52. package/dist/chat/chat-mentions.js +77 -0
  53. package/dist/chat/chat-result-views.d.ts +5 -0
  54. package/dist/chat/chat-result-views.js +15 -0
  55. package/dist/chat/chat-session.d.ts +28 -0
  56. package/dist/chat/chat-session.js +50 -0
  57. package/dist/chat/chat-status.d.ts +1 -0
  58. package/dist/chat/chat-status.js +3 -0
  59. package/dist/chat/chat-time.d.ts +18 -0
  60. package/dist/chat/chat-time.js +90 -0
  61. package/dist/chat/chat-tool-display.d.ts +17 -0
  62. package/dist/chat/chat-tool-display.js +213 -0
  63. package/dist/chat/chat-tool-identity.d.ts +4 -0
  64. package/dist/chat/chat-tool-identity.js +42 -0
  65. package/dist/chat/chat-trace.d.ts +6 -0
  66. package/dist/chat/chat-trace.js +256 -0
  67. package/dist/chat/live-runtime-unavailable.d.ts +1 -0
  68. package/dist/chat/live-runtime-unavailable.js +1 -0
  69. package/dist/commands/agent.d.ts +8 -0
  70. package/dist/commands/agent.js +38 -0
  71. package/dist/commands/catalog.d.ts +18 -0
  72. package/dist/commands/catalog.js +115 -0
  73. package/dist/commands/discovery.d.ts +36 -0
  74. package/dist/commands/discovery.js +151 -0
  75. package/dist/commands/groups.d.ts +16 -0
  76. package/dist/commands/groups.js +121 -0
  77. package/dist/commands/inspect.d.ts +18 -0
  78. package/dist/commands/inspect.js +29 -0
  79. package/dist/commands/markdown.d.ts +21 -0
  80. package/dist/commands/markdown.js +117 -0
  81. package/dist/commands/parser.d.ts +4 -0
  82. package/dist/commands/parser.js +24 -0
  83. package/dist/commands/reserved.d.ts +4 -0
  84. package/dist/commands/reserved.js +107 -0
  85. package/dist/commands/router.d.ts +17 -0
  86. package/dist/commands/router.js +170 -0
  87. package/dist/commands/seed.d.ts +4 -0
  88. package/dist/commands/seed.js +8 -0
  89. package/dist/commands/types.d.ts +54 -0
  90. package/dist/commands/types.js +1 -0
  91. package/dist/commands/user-agents-node.d.ts +3 -0
  92. package/dist/commands/user-agents-node.js +52 -0
  93. package/dist/commands/user-agents.d.ts +22 -0
  94. package/dist/commands/user-agents.js +40 -0
  95. package/dist/conversations/approval-grants.d.ts +20 -0
  96. package/dist/conversations/approval-grants.js +75 -0
  97. package/dist/conversations/binding-state.d.ts +6 -0
  98. package/dist/conversations/binding-state.js +35 -0
  99. package/dist/conversations/context-handoff.d.ts +25 -0
  100. package/dist/conversations/context-handoff.js +207 -0
  101. package/dist/conversations/conversation-index-coordinator.d.ts +18 -0
  102. package/dist/conversations/conversation-index-coordinator.js +81 -0
  103. package/dist/conversations/conversation-index.d.ts +16 -0
  104. package/dist/conversations/conversation-index.js +125 -0
  105. package/dist/conversations/conversation-locator.d.ts +2 -0
  106. package/dist/conversations/conversation-locator.js +20 -0
  107. package/dist/conversations/conversation-palette.d.ts +18 -0
  108. package/dist/conversations/conversation-palette.js +29 -0
  109. package/dist/conversations/conversation-repository.d.ts +39 -0
  110. package/dist/conversations/conversation-repository.js +213 -0
  111. package/dist/conversations/handoff-summary.d.ts +48 -0
  112. package/dist/conversations/handoff-summary.js +269 -0
  113. package/dist/conversations/hashes.d.ts +5 -0
  114. package/dist/conversations/hashes.js +40 -0
  115. package/dist/conversations/history-creation-scope.d.ts +12 -0
  116. package/dist/conversations/history-creation-scope.js +44 -0
  117. package/dist/conversations/jsonl.d.ts +10 -0
  118. package/dist/conversations/jsonl.js +41 -0
  119. package/dist/conversations/memory-transcript-store.d.ts +15 -0
  120. package/dist/conversations/memory-transcript-store.js +119 -0
  121. package/dist/conversations/paths.d.ts +14 -0
  122. package/dist/conversations/paths.js +92 -0
  123. package/dist/conversations/redaction.d.ts +13 -0
  124. package/dist/conversations/redaction.js +94 -0
  125. package/dist/conversations/reveal-scope.d.ts +3 -0
  126. package/dist/conversations/reveal-scope.js +8 -0
  127. package/dist/conversations/scope-resolver.d.ts +11 -0
  128. package/dist/conversations/scope-resolver.js +18 -0
  129. package/dist/conversations/scope-tree.d.ts +10 -0
  130. package/dist/conversations/scope-tree.js +71 -0
  131. package/dist/conversations/transcript-projection.d.ts +9 -0
  132. package/dist/conversations/transcript-projection.js +409 -0
  133. package/dist/conversations/transcript-store.d.ts +24 -0
  134. package/dist/conversations/transcript-store.js +27 -0
  135. package/dist/conversations/types.d.ts +224 -0
  136. package/dist/conversations/types.js +12 -0
  137. package/dist/conversations/validation.d.ts +4 -0
  138. package/dist/conversations/validation.js +383 -0
  139. package/dist/conversations/vault-transcript-store.d.ts +22 -0
  140. package/dist/conversations/vault-transcript-store.js +219 -0
  141. package/dist/core/event-queue.d.ts +6 -0
  142. package/dist/core/event-queue.js +39 -0
  143. package/dist/core/types.d.ts +241 -0
  144. package/dist/core/types.js +50 -0
  145. package/dist/history/ai-history-panel.svelte +937 -0
  146. package/dist/history/ai-history-panel.svelte.d.ts +17 -0
  147. package/dist/history/ai-history-view-type.d.ts +1 -0
  148. package/dist/history/ai-history-view-type.js +1 -0
  149. package/dist/history/ai-history-view.d.ts +26 -0
  150. package/dist/history/ai-history-view.js +51 -0
  151. package/dist/history/conversation-history-tree.d.ts +15 -0
  152. package/dist/history/conversation-history-tree.js +71 -0
  153. package/dist/host/agent-workspace.d.ts +6 -0
  154. package/dist/host/agent-workspace.js +16 -0
  155. package/dist/host/create-host-runtimes.d.ts +2 -0
  156. package/dist/host/create-host-runtimes.js +14 -0
  157. package/dist/host/desktop-process-host.d.ts +6 -0
  158. package/dist/host/desktop-process-host.js +74 -0
  159. package/dist/host/host-runtime-availability.d.ts +1 -0
  160. package/dist/host/host-runtime-availability.js +3 -0
  161. package/dist/host/process-host.d.ts +31 -0
  162. package/dist/host/process-host.js +6 -0
  163. package/dist/index.d.ts +69 -0
  164. package/dist/index.js +49 -0
  165. package/dist/jsonl/ai-jsonl-preview.d.ts +27 -0
  166. package/dist/jsonl/ai-jsonl-preview.js +68 -0
  167. package/dist/jsonl/ai-jsonl-view-type.d.ts +1 -0
  168. package/dist/jsonl/ai-jsonl-view-type.js +1 -0
  169. package/dist/jsonl/ai-jsonl-view.d.ts +16 -0
  170. package/dist/jsonl/ai-jsonl-view.js +57 -0
  171. package/dist/jsonl/ai-jsonl-view.svelte +260 -0
  172. package/dist/jsonl/ai-jsonl-view.svelte.d.ts +9 -0
  173. package/dist/memory/candidate-extractor.d.ts +25 -0
  174. package/dist/memory/candidate-extractor.js +191 -0
  175. package/dist/memory/forget-confirmation.d.ts +3 -0
  176. package/dist/memory/forget-confirmation.js +45 -0
  177. package/dist/memory/hashes.d.ts +5 -0
  178. package/dist/memory/hashes.js +37 -0
  179. package/dist/memory/memory-ingestion-coordinator.d.ts +19 -0
  180. package/dist/memory/memory-ingestion-coordinator.js +108 -0
  181. package/dist/memory/memory-record-store.d.ts +62 -0
  182. package/dist/memory/memory-record-store.js +369 -0
  183. package/dist/memory/memory-scheduler.d.ts +21 -0
  184. package/dist/memory/memory-scheduler.js +143 -0
  185. package/dist/memory/memory-tools.d.ts +61 -0
  186. package/dist/memory/memory-tools.js +99 -0
  187. package/dist/memory/native-memory-service.d.ts +72 -0
  188. package/dist/memory/native-memory-service.js +1256 -0
  189. package/dist/memory/paths.d.ts +13 -0
  190. package/dist/memory/paths.js +60 -0
  191. package/dist/memory/runtime-consolidation-provider.d.ts +18 -0
  192. package/dist/memory/runtime-consolidation-provider.js +154 -0
  193. package/dist/memory/types.d.ts +139 -0
  194. package/dist/memory/types.js +1 -0
  195. package/dist/providers/acp-model-provider.d.ts +11 -0
  196. package/dist/providers/acp-model-provider.js +99 -0
  197. package/dist/providers/codex-model-catalog.d.ts +7 -0
  198. package/dist/providers/codex-model-catalog.js +179 -0
  199. package/dist/providers/codex-model-provider.d.ts +12 -0
  200. package/dist/providers/codex-model-provider.js +42 -0
  201. package/dist/providers/model-provider.d.ts +26 -0
  202. package/dist/providers/model-provider.js +26 -0
  203. package/dist/public-components.d.ts +3 -0
  204. package/dist/public-components.js +3 -0
  205. package/dist/registry/runtime-registry.d.ts +11 -0
  206. package/dist/registry/runtime-registry.js +71 -0
  207. package/dist/registry/select-runtime.d.ts +9 -0
  208. package/dist/registry/select-runtime.js +31 -0
  209. package/dist/runtime-adapters.d.ts +16 -0
  210. package/dist/runtime-adapters.js +9 -0
  211. package/dist/runtimes/acp/acp-event-mapper.d.ts +56 -0
  212. package/dist/runtimes/acp/acp-event-mapper.js +237 -0
  213. package/dist/runtimes/acp/acp-runtime.d.ts +46 -0
  214. package/dist/runtimes/acp/acp-runtime.js +165 -0
  215. package/dist/runtimes/acp/desktop-acp-backend.d.ts +16 -0
  216. package/dist/runtimes/acp/desktop-acp-backend.js +243 -0
  217. package/dist/runtimes/codex/app-server-protocol.d.ts +28 -0
  218. package/dist/runtimes/codex/app-server-protocol.js +274 -0
  219. package/dist/runtimes/codex/codex-runtime.d.ts +24 -0
  220. package/dist/runtimes/codex/codex-runtime.js +318 -0
  221. package/dist/runtimes/fake/fake-runtime.d.ts +51 -0
  222. package/dist/runtimes/fake/fake-runtime.js +257 -0
  223. package/dist/sessions/plugin-data.d.ts +8 -0
  224. package/dist/sessions/plugin-data.js +29 -0
  225. package/dist/sessions/session-store.d.ts +47 -0
  226. package/dist/sessions/session-store.js +117 -0
  227. package/dist/settings/acp-agents.d.ts +8 -0
  228. package/dist/settings/acp-agents.js +11 -0
  229. package/dist/settings/ai-settings-tab.d.ts +8 -0
  230. package/dist/settings/ai-settings-tab.js +249 -0
  231. package/dist/settings/ai-settings.d.ts +43 -0
  232. package/dist/settings/ai-settings.js +147 -0
  233. package/dist/settings/app-tool-setting-rows.d.ts +16 -0
  234. package/dist/settings/app-tool-setting-rows.js +30 -0
  235. package/dist/settings/register-ai-settings.d.ts +3 -0
  236. package/dist/settings/register-ai-settings.js +393 -0
  237. package/dist/skills/activation.d.ts +2 -0
  238. package/dist/skills/activation.js +20 -0
  239. package/dist/skills/bundled/lapis-notes.d.ts +3 -0
  240. package/dist/skills/bundled/lapis-notes.js +52 -0
  241. package/dist/skills/bundled/research.d.ts +6 -0
  242. package/dist/skills/bundled/research.js +29 -0
  243. package/dist/skills/discovery.d.ts +21 -0
  244. package/dist/skills/discovery.js +227 -0
  245. package/dist/skills/manifest.d.ts +3 -0
  246. package/dist/skills/manifest.js +39 -0
  247. package/dist/skills/parser.d.ts +12 -0
  248. package/dist/skills/parser.js +124 -0
  249. package/dist/skills/registry.d.ts +21 -0
  250. package/dist/skills/registry.js +89 -0
  251. package/dist/skills/seed.d.ts +5 -0
  252. package/dist/skills/seed.js +18 -0
  253. package/dist/skills/skill-tools.d.ts +8 -0
  254. package/dist/skills/skill-tools.js +113 -0
  255. package/dist/skills/types.d.ts +67 -0
  256. package/dist/skills/types.js +1 -0
  257. package/dist/styles.css +514 -0
  258. package/dist/tools/app-tool-host.d.ts +47 -0
  259. package/dist/tools/app-tool-host.js +419 -0
  260. package/dist/tools/desktop-app-tool-bridge.d.ts +23 -0
  261. package/dist/tools/desktop-app-tool-bridge.js +223 -0
  262. package/dist/tools/mcp-server-registry.d.ts +7 -0
  263. package/dist/tools/mcp-server-registry.js +30 -0
  264. package/manifest.json +34 -0
  265. package/package.json +83 -0
  266. package/styles.css +1 -0
@@ -0,0 +1,57 @@
1
+ import { TextFileView, mountComponent, } from "@lapis-notes/api";
2
+ import AiJsonlViewSurface from "./ai-jsonl-view.svelte";
3
+ import { AiJsonlViewType } from "./ai-jsonl-view-type";
4
+ export { AiJsonlViewType } from "./ai-jsonl-view-type";
5
+ export class AiJsonlView extends TextFileView {
6
+ #component = null;
7
+ getViewType() {
8
+ return AiJsonlViewType;
9
+ }
10
+ getViewData() {
11
+ return this.data;
12
+ }
13
+ setViewData(data, clear = false) {
14
+ void clear;
15
+ this.data = data;
16
+ if (this.#component)
17
+ this.#component.props.data = data;
18
+ }
19
+ clear() {
20
+ this.setViewData("", true);
21
+ }
22
+ load() {
23
+ if (!this.containerEl)
24
+ return;
25
+ this.unload();
26
+ this.containerEl.replaceChildren();
27
+ this.containerEl.classList.add("text-view", "ai-jsonl-file-view");
28
+ this.actions = [];
29
+ this.#component = mountComponent(AiJsonlViewSurface, {
30
+ target: this.containerEl,
31
+ props: {
32
+ app: this.app,
33
+ data: this.data,
34
+ filePath: this.file?.path ?? "",
35
+ },
36
+ });
37
+ }
38
+ unload() {
39
+ this.#component?.destroy();
40
+ this.#component = null;
41
+ }
42
+ canAcceptExtension(extension) {
43
+ return extension.trim().replace(/^\./u, "").toLowerCase() === "jsonl";
44
+ }
45
+ getDisplayText() {
46
+ return this.file?.name ?? "JSONL preview";
47
+ }
48
+ getIcon() {
49
+ return "messages-square";
50
+ }
51
+ onOpen() {
52
+ return Promise.resolve();
53
+ }
54
+ onClose() {
55
+ return Promise.resolve();
56
+ }
57
+ }
@@ -0,0 +1,260 @@
1
+ <script lang="ts">
2
+ import type { App } from "@lapis-notes/api";
3
+ import { MarkdownEmbed } from "@lapis-notes/markdown/embed";
4
+ import * as Chat from "@lapismd/design-core/ai/chat";
5
+ import { Reasoning } from "@lapismd/design-core/ai/experimental";
6
+ import { CodeBlock } from "@lapismd/design-core/shadcn/code-block";
7
+ import * as Empty from "@lapismd/design-core/shadcn/empty";
8
+ import FileJsonIcon from "@lucide/svelte/icons/file-json";
9
+ import type { ToolCallItem } from "@lapismd/design-core/ai/chat";
10
+ import { mentionTokensFromText } from "../chat/chat-mentions";
11
+ import { formatChatTimestamp, groupChatItemsByDate } from "../chat/chat-time";
12
+ import {
13
+ isOneLineAlert,
14
+ presentToolPayload,
15
+ toolCallStatus,
16
+ toolCallTarget,
17
+ type AiChatToolItem,
18
+ } from "../chat/chat-tool-display";
19
+ import type { AgentBindingRecord } from "../conversations/types";
20
+ import { createAiJsonlPreview } from "./ai-jsonl-preview";
21
+
22
+ let {
23
+ app,
24
+ data,
25
+ filePath,
26
+ }: { app: App; data: string; filePath: string } = $props();
27
+
28
+ const preview = $derived(createAiJsonlPreview(filePath, data));
29
+ const timeline = $derived(
30
+ preview.kind === "transcript"
31
+ ? groupChatItemsByDate(preview.items)
32
+ : [],
33
+ );
34
+ const isEmpty = $derived(
35
+ (preview.kind === "transcript" && preview.entries.length === 0) ||
36
+ (preview.kind === "agents" && preview.records.length === 0) ||
37
+ (preview.kind === "records" && preview.records.length === 0),
38
+ );
39
+
40
+ function toolCallProps(item: AiChatToolItem): ToolCallItem {
41
+ const hint = { toolName: item.name, input: item.input };
42
+ const presentedError =
43
+ item.state === "error" ? presentToolPayload(item.output, hint) : undefined;
44
+ return {
45
+ id: item.toolId,
46
+ name: item.name,
47
+ status: toolCallStatus(item.state),
48
+ errorMessage:
49
+ presentedError && isOneLineAlert(presentedError)
50
+ ? presentedError.code
51
+ : undefined,
52
+ target: toolCallTarget(item.input, item.server),
53
+ data: { input: item.input, output: item.output },
54
+ detail: item.input || item.output ? toolDetail : undefined,
55
+ };
56
+ }
57
+
58
+ function agentRecordLabel(record: AgentBindingRecord): string {
59
+ switch (record.type) {
60
+ case "binding.created":
61
+ return `Agent binding created · ${record.agent ?? record.runtime}`;
62
+ case "usage.updated":
63
+ return `Context usage updated · ${record.usage.used.toLocaleString()} of ${record.usage.limit.toLocaleString()}`;
64
+ case "binding.context.updated":
65
+ return `Context checkpoint updated · ${record.cause}`;
66
+ case "binding.config.updated":
67
+ return "Agent configuration updated";
68
+ case "handoff.summary.created":
69
+ return "Handoff summary created";
70
+ }
71
+ }
72
+
73
+ function recordJson(value: unknown): string {
74
+ return JSON.stringify(value, null, 2);
75
+ }
76
+ </script>
77
+
78
+ {#snippet emptyComposer()}{/snippet}
79
+
80
+ {#snippet emptyState()}
81
+ <Empty.Root>
82
+ <Empty.Header>
83
+ <Empty.Media variant="icon"><FileJsonIcon aria-hidden="true" /></Empty.Media>
84
+ <Empty.Title>Empty JSONL file</Empty.Title>
85
+ <Empty.Description>Records will appear here when they are appended.</Empty.Description>
86
+ </Empty.Header>
87
+ </Empty.Root>
88
+ {/snippet}
89
+
90
+ {#snippet toolDetail(call: ToolCallItem)}
91
+ {@const payload = call.data as { input?: string; output?: string }}
92
+ <div class="ai-jsonl-view__tool-detail">
93
+ {#if payload.input}
94
+ <CodeBlock
95
+ code={presentToolPayload(payload.input)?.code ?? payload.input}
96
+ language={presentToolPayload(payload.input)?.language ?? "plaintext"}
97
+ title="Input"
98
+ width="100%"
99
+ size="sm"
100
+ isWrapped
101
+ />
102
+ {/if}
103
+ {#if payload.output}
104
+ <CodeBlock
105
+ code={presentToolPayload(payload.output)?.code ?? payload.output}
106
+ language={presentToolPayload(payload.output)?.language ?? "plaintext"}
107
+ title="Output"
108
+ width="100%"
109
+ size="sm"
110
+ isWrapped
111
+ />
112
+ {/if}
113
+ </div>
114
+ {/snippet}
115
+
116
+ <div
117
+ class="ai-jsonl-view"
118
+ data-ui-component="ai-jsonl-view"
119
+ data-preview-kind={preview.kind}
120
+ data-file-path={filePath}
121
+ >
122
+ <Chat.Layout
123
+ density="compact"
124
+ {isEmpty}
125
+ {emptyState}
126
+ composer={emptyComposer}
127
+ aria-label="AI JSONL preview"
128
+ >
129
+ <Chat.MessageList
130
+ density="compact"
131
+ {isEmpty}
132
+ {emptyState}
133
+ aria-label="JSONL records"
134
+ >
135
+ {#if preview.kind === "error"}
136
+ <Chat.Message sender="assistant">
137
+ <Chat.MessageBubble>{preview.message}</Chat.MessageBubble>
138
+ {#snippet metadata()}
139
+ <Chat.MessageMetadata status="error" />
140
+ {/snippet}
141
+ </Chat.Message>
142
+ {:else}
143
+ {#each preview.warnings as warning}
144
+ <Chat.SystemMessage>
145
+ Line {warning.line}: {warning.message}
146
+ </Chat.SystemMessage>
147
+ {/each}
148
+ {/if}
149
+
150
+ {#if preview.kind === "transcript"}
151
+ {#each timeline as entry (entry.kind === "item" ? entry.item.id : entry.id)}
152
+ {#if entry.kind === "divider"}
153
+ <Chat.SystemMessage variant="divider">{entry.label}</Chat.SystemMessage>
154
+ {:else if entry.kind === "tools"}
155
+ <Chat.ToolCalls
156
+ calls={entry.items.map(toolCallProps)}
157
+ defaultExpanded={false}
158
+ />
159
+ {:else if entry.item.type === "message"}
160
+ {@const message = entry.item}
161
+ <Chat.Message sender={message.role}>
162
+ <Chat.MessageBubble>
163
+ {#if message.role === "assistant"}
164
+ <MarkdownEmbed
165
+ {app}
166
+ value={message.text}
167
+ htmlPolicy="safe"
168
+ class="ai-jsonl-view__markdown"
169
+ />
170
+ {:else}
171
+ <Chat.TokenizedText
172
+ text={message.text}
173
+ tokens={mentionTokensFromText(message.text)}
174
+ />
175
+ {/if}
176
+ </Chat.MessageBubble>
177
+ {#snippet metadata()}
178
+ {#if message.createdAt}
179
+ <Chat.MessageMetadata timestamp={formatChatTimestamp(message.createdAt)} />
180
+ {/if}
181
+ {/snippet}
182
+ </Chat.Message>
183
+ {:else if entry.item.type === "thinking"}
184
+ <Reasoning
185
+ preview={entry.item.text}
186
+ label={entry.item.kind === "plan" ? "Plan" : "Thinking"}
187
+ >
188
+ {entry.item.text}
189
+ </Reasoning>
190
+ {:else if entry.item.type === "error"}
191
+ <Chat.Message sender="assistant">
192
+ <Chat.MessageBubble>{entry.item.text}</Chat.MessageBubble>
193
+ {#snippet metadata()}
194
+ <Chat.MessageMetadata
195
+ timestamp={entry.item.createdAt
196
+ ? formatChatTimestamp(entry.item.createdAt)
197
+ : undefined}
198
+ status="error"
199
+ />
200
+ {/snippet}
201
+ </Chat.Message>
202
+ {:else if entry.item.type === "approval"}
203
+ <Chat.SystemMessage>
204
+ Approval {entry.item.status}: {entry.item.request.title}
205
+ </Chat.SystemMessage>
206
+ {:else if entry.item.type === "question"}
207
+ <Chat.SystemMessage>
208
+ Question {entry.item.status}: {entry.item.request.title}
209
+ </Chat.SystemMessage>
210
+ {:else}
211
+ <Chat.SystemMessage>{entry.item.text}</Chat.SystemMessage>
212
+ {/if}
213
+ {/each}
214
+ {:else if preview.kind === "agents"}
215
+ {#each preview.records as record (record.id)}
216
+ <section class="ai-jsonl-view__record" data-record-type={record.type}>
217
+ <Chat.SystemMessage>
218
+ {agentRecordLabel(record)} · {formatChatTimestamp(record.createdAt)}
219
+ </Chat.SystemMessage>
220
+ {#if record.type === "handoff.summary.created"}
221
+ <Chat.Message sender="assistant">
222
+ <Chat.MessageBubble>
223
+ <MarkdownEmbed
224
+ {app}
225
+ value={record.summary}
226
+ htmlPolicy="safe"
227
+ class="ai-jsonl-view__markdown"
228
+ />
229
+ </Chat.MessageBubble>
230
+ </Chat.Message>
231
+ {/if}
232
+ <CodeBlock
233
+ code={recordJson(record)}
234
+ language="json"
235
+ width="100%"
236
+ size="sm"
237
+ isCollapsible
238
+ collapsibleThreshold={8}
239
+ hasLineNumbers
240
+ />
241
+ </section>
242
+ {/each}
243
+ {:else if preview.kind === "records"}
244
+ {#each preview.records as record (record.line)}
245
+ <section class="ai-jsonl-view__record">
246
+ <Chat.SystemMessage variant="divider">Line {record.line}</Chat.SystemMessage>
247
+ <CodeBlock
248
+ code={recordJson(record.value)}
249
+ language="json"
250
+ width="100%"
251
+ size="sm"
252
+ isWrapped
253
+ hasLineNumbers
254
+ />
255
+ </section>
256
+ {/each}
257
+ {/if}
258
+ </Chat.MessageList>
259
+ </Chat.Layout>
260
+ </div>
@@ -0,0 +1,9 @@
1
+ import type { App } from "@lapis-notes/api";
2
+ type $$ComponentProps = {
3
+ app: App;
4
+ data: string;
5
+ filePath: string;
6
+ };
7
+ declare const AiJsonlView: import("svelte").Component<$$ComponentProps, {}, "">;
8
+ type AiJsonlView = ReturnType<typeof AiJsonlView>;
9
+ export default AiJsonlView;
@@ -0,0 +1,25 @@
1
+ import type { AppDatabase } from "@lapis-notes/api";
2
+ import type { ConversationSnapshot, TranscriptEntry } from "../conversations/types";
3
+ import type { DurableMemoryKind } from "./types";
4
+ export type CandidateExtractionResult = {
5
+ examined: number;
6
+ staged: number;
7
+ review: number;
8
+ rejectedSecrets: number;
9
+ };
10
+ export type CandidateExtractionOptions = {
11
+ entries?: TranscriptEntry[];
12
+ yieldToApp?: () => Promise<void>;
13
+ };
14
+ export declare function extractDeterministicCandidates(snapshot: ConversationSnapshot, database: AppDatabase, options?: CandidateExtractionOptions): Promise<CandidateExtractionResult>;
15
+ declare function normalizeClaim(text: string): string;
16
+ declare function supersessionKey(text: string, kind: DurableMemoryKind): string | undefined;
17
+ declare function extractTriggers(claim: string): string[];
18
+ export declare function isSecretBearingMemoryText(text: string): boolean;
19
+ export declare const __candidateExtractorInternals: {
20
+ containsSecret: typeof isSecretBearingMemoryText;
21
+ extractTriggers: typeof extractTriggers;
22
+ normalizeClaim: typeof normalizeClaim;
23
+ supersessionKey: typeof supersessionKey;
24
+ };
25
+ export {};
@@ -0,0 +1,191 @@
1
+ import { deterministicMemoryId, sha256Text, transcriptEntryHash } from "./hashes";
2
+ import { conversationMemoryScope } from "./paths";
3
+ const SECRET_PATTERNS = [
4
+ /-----BEGIN [A-Z ]*PRIVATE KEY-----/iu,
5
+ /\b(?:api[_ -]?key|password|passwd|secret|access[_ -]?token)\s*[:=]\s*\S+/iu,
6
+ /\b(?:sk|ghp|github_pat|xox[baprs])[-_][A-Za-z0-9_-]{16,}\b/u,
7
+ ];
8
+ const EXPLICIT_CUE = /\b(?:remember(?:\s+that)?|i\s+prefer|we\s+decided|always\s+for\s+this\s+project)\b/iu;
9
+ const STABLE_CUE = /\b(?:prefer|uses?|must|should|decided|convention|workflow|constraint)\b/iu;
10
+ const STOP_WORDS = new Set([
11
+ "a",
12
+ "an",
13
+ "and",
14
+ "are",
15
+ "as",
16
+ "at",
17
+ "be",
18
+ "for",
19
+ "i",
20
+ "in",
21
+ "is",
22
+ "it",
23
+ "of",
24
+ "on",
25
+ "or",
26
+ "that",
27
+ "the",
28
+ "this",
29
+ "to",
30
+ "use",
31
+ "uses",
32
+ "we",
33
+ "with",
34
+ ]);
35
+ export async function extractDeterministicCandidates(snapshot, database, options = {}) {
36
+ let examined = 0;
37
+ let staged = 0;
38
+ let review = 0;
39
+ let rejectedSecrets = 0;
40
+ const entries = options.entries ?? snapshot.transcript;
41
+ for (let index = 0; index < entries.length; index += 1) {
42
+ const entry = entries[index];
43
+ if (index > 0 && index % 200 === 0) {
44
+ await options.yieldToApp?.();
45
+ }
46
+ if (entry.type !== "message")
47
+ continue;
48
+ const originClass = entry.role === "user" ? "owner" : "agent";
49
+ if (!STABLE_CUE.test(entry.text) && !EXPLICIT_CUE.test(entry.text))
50
+ continue;
51
+ examined += 1;
52
+ if (isSecretBearingMemoryText(entry.text)) {
53
+ rejectedSecrets += 1;
54
+ continue;
55
+ }
56
+ const explicit = originClass === "owner" && EXPLICIT_CUE.test(entry.text);
57
+ const input = await candidateFromEntry(snapshot, entry, originClass, explicit);
58
+ const existing = (await database.queryMemoryCandidates({
59
+ scopeKind: input.candidate.scopeKind,
60
+ scopePath: input.candidate.scopePath,
61
+ limit: 10_000,
62
+ })).find((candidate) => candidate.candidate.id === input.candidate.id);
63
+ const origins = dedupeOrigins([
64
+ ...(existing?.origins ?? []),
65
+ ...input.origins,
66
+ ]);
67
+ const conversations = new Set(origins.map((origin) => origin.conversationId));
68
+ const days = new Set(origins.map((origin) => new Date(origin.observedAt).toISOString().slice(0, 10)));
69
+ const qualifiesRecurring = conversations.size >= 2 && days.size >= 2;
70
+ const state = originClass === "agent"
71
+ ? "review"
72
+ : explicit || qualifiesRecurring
73
+ ? "staged"
74
+ : "review";
75
+ await database.upsertMemoryCandidate({
76
+ candidate: {
77
+ ...(existing?.candidate ?? input.candidate),
78
+ lastSeenAt: Math.max(existing?.candidate.lastSeenAt ?? 0, input.candidate.lastSeenAt),
79
+ recurrenceCount: origins.length,
80
+ conversationCount: conversations.size,
81
+ state: existing?.candidate.state === "promoted"
82
+ ? "promoted"
83
+ : state,
84
+ },
85
+ origins,
86
+ });
87
+ if (state === "staged")
88
+ staged += 1;
89
+ else
90
+ review += 1;
91
+ }
92
+ return { examined, staged, review, rejectedSecrets };
93
+ }
94
+ async function candidateFromEntry(snapshot, entry, originClass, explicit) {
95
+ const scope = candidateScope(snapshot.location.scopeDir, entry.text);
96
+ const normalizedClaim = normalizeClaim(entry.text);
97
+ const claimHash = await sha256Text(normalizedClaim.toLocaleLowerCase());
98
+ const scopePath = scope.kind === "project" ? scope.projectDir : "";
99
+ const kind = candidateKind(entry.text);
100
+ const id = await deterministicMemoryId("candidate", scope.kind, scopePath, kind, claimHash);
101
+ const observedAt = Date.parse(entry.createdAt);
102
+ return {
103
+ candidate: {
104
+ id,
105
+ scopeKind: scope.kind,
106
+ scopePath,
107
+ kind,
108
+ normalizedClaim,
109
+ claimHash,
110
+ ...(supersessionKey(entry.text, kind)
111
+ ? { supersessionKey: supersessionKey(entry.text, kind) }
112
+ : {}),
113
+ originClass,
114
+ importance: explicit ? 4 : 3,
115
+ triggers: extractTriggers(normalizedClaim),
116
+ state: originClass === "agent" ? "review" : explicit ? "staged" : "review",
117
+ firstSeenAt: observedAt,
118
+ lastSeenAt: observedAt,
119
+ recurrenceCount: 1,
120
+ conversationCount: 1,
121
+ },
122
+ origins: [
123
+ {
124
+ candidateId: id,
125
+ conversationId: snapshot.location.conversationId,
126
+ entryId: entry.id,
127
+ entryHash: await transcriptEntryHash(entry),
128
+ observedAt,
129
+ },
130
+ ],
131
+ };
132
+ }
133
+ function candidateScope(scopeDir, text) {
134
+ if (/\b(?:this|the)\s+project\b/iu.test(text) && scopeDir) {
135
+ return conversationMemoryScope(scopeDir);
136
+ }
137
+ if (/\b(?:i\s+prefer|my\s+preference)\b/iu.test(text)) {
138
+ return { kind: "user" };
139
+ }
140
+ return scopeDir
141
+ ? conversationMemoryScope(scopeDir)
142
+ : { kind: "workspace" };
143
+ }
144
+ function normalizeClaim(text) {
145
+ return text
146
+ .replace(/^\s*(?:please\s+)?remember(?:\s+that)?\s+/iu, "")
147
+ .replace(/^\s*i\s+prefer\s+/iu, "Prefer ")
148
+ .replace(/^\s*we\s+decided\s+(?:that\s+|to\s+)?/iu, "")
149
+ .replace(/^\s*always\s+for\s+this\s+project[,;:]?\s*/iu, "")
150
+ .replace(/\s+/gu, " ")
151
+ .trim()
152
+ .replace(/[.!?]+$/u, "");
153
+ }
154
+ function candidateKind(text) {
155
+ if (/\bprefer(?:ence)?\b/iu.test(text))
156
+ return "preference";
157
+ if (/\bdecided\b/iu.test(text))
158
+ return "decision";
159
+ if (/\b(?:must|never|always|constraint)\b/iu.test(text))
160
+ return "constraint";
161
+ if (/\b(?:workflow|procedure|steps?|run|execute)\b/iu.test(text)) {
162
+ return "procedure";
163
+ }
164
+ return "fact";
165
+ }
166
+ function supersessionKey(text, kind) {
167
+ const normalized = normalizeClaim(text).toLocaleLowerCase();
168
+ const subject = /^(.{2,80}?)\s+(?:is|are|uses?|must|should)\b/u.exec(normalized)?.[1];
169
+ if (!subject)
170
+ return undefined;
171
+ return `${kind}:${subject.replace(/[^\p{L}\p{N}]+/gu, "-").replace(/^-|-$/gu, "")}`;
172
+ }
173
+ function extractTriggers(claim) {
174
+ const tokens = claim.toLocaleLowerCase().match(/[\p{L}\p{N}_-]+/gu) ?? [];
175
+ return [...new Set(tokens.filter((token) => token.length > 2 && !STOP_WORDS.has(token)))].slice(0, 8);
176
+ }
177
+ export function isSecretBearingMemoryText(text) {
178
+ return SECRET_PATTERNS.some((pattern) => pattern.test(text));
179
+ }
180
+ function dedupeOrigins(origins) {
181
+ return [...new Map(origins.map((origin) => [
182
+ `${origin.conversationId}\u0000${origin.entryId}`,
183
+ origin,
184
+ ])).values()].sort((left, right) => `${left.conversationId}\u0000${left.entryId}`.localeCompare(`${right.conversationId}\u0000${right.entryId}`));
185
+ }
186
+ export const __candidateExtractorInternals = {
187
+ containsSecret: isSecretBearingMemoryText,
188
+ extractTriggers,
189
+ normalizeClaim,
190
+ supersessionKey,
191
+ };
@@ -0,0 +1,3 @@
1
+ import { type App } from "@lapis-notes/api";
2
+ import type { MemoryForgetPreview } from "./types";
3
+ export declare function confirmForgetDerivedMemory(app: App, preview: MemoryForgetPreview): Promise<boolean>;
@@ -0,0 +1,45 @@
1
+ import { Modal, Setting } from "@lapis-notes/api";
2
+ export function confirmForgetDerivedMemory(app, preview) {
3
+ return new Promise((resolve) => {
4
+ let settled = false;
5
+ const finish = (confirmed) => {
6
+ if (settled)
7
+ return;
8
+ settled = true;
9
+ resolve(confirmed);
10
+ modal.close();
11
+ };
12
+ const modal = new (class extends Modal {
13
+ onOpen() {
14
+ this.setTitle("Forget derived conversation memory");
15
+ const document = this.contentEl.ownerDocument;
16
+ const description = document.createElement("p");
17
+ description.textContent = [
18
+ `Remove ${preview.episodeRefs.length} episodic reference${preview.episodeRefs.length === 1 ? "" : "s"}`,
19
+ `${preview.candidateIds.length} candidate${preview.candidateIds.length === 1 ? "" : "s"}`,
20
+ `and retract ${preview.memoryIds.length} curated record${preview.memoryIds.length === 1 ? "" : "s"}.`,
21
+ "The immutable conversation transcript is not deleted.",
22
+ ].join(", ");
23
+ this.contentEl.replaceChildren(description);
24
+ new Setting(this.contentEl)
25
+ .addButton((button) => {
26
+ button.setButtonText("Cancel").onClick(() => finish(false));
27
+ })
28
+ .addButton((button) => {
29
+ button
30
+ .setWarning()
31
+ .setButtonText("Forget derived memory")
32
+ .onClick(() => finish(true));
33
+ });
34
+ }
35
+ onClose() {
36
+ if (!settled) {
37
+ settled = true;
38
+ resolve(false);
39
+ }
40
+ this.contentEl.replaceChildren();
41
+ }
42
+ })(app);
43
+ modal.open();
44
+ });
45
+ }
@@ -0,0 +1,5 @@
1
+ import type { TranscriptEntry } from "../conversations/types";
2
+ export declare function sha256Text(value: string): Promise<string>;
3
+ export declare function canonicalJson(value: unknown): string;
4
+ export declare function transcriptEntryHash(entry: TranscriptEntry): Promise<string>;
5
+ export declare function deterministicMemoryId(namespace: string, ...parts: string[]): Promise<string>;
@@ -0,0 +1,37 @@
1
+ function canonicalize(value) {
2
+ if (value === null || typeof value === "boolean" || typeof value === "string") {
3
+ return JSON.stringify(value);
4
+ }
5
+ if (typeof value === "number") {
6
+ if (!Number.isFinite(value))
7
+ throw new Error("Cannot hash a non-finite number");
8
+ return JSON.stringify(value);
9
+ }
10
+ if (Array.isArray(value)) {
11
+ return `[${value.map(canonicalize).join(",")}]`;
12
+ }
13
+ if (typeof value === "object") {
14
+ const record = value;
15
+ return `{${Object.keys(record)
16
+ .filter((key) => record[key] !== undefined)
17
+ .sort()
18
+ .map((key) => `${JSON.stringify(key)}:${canonicalize(record[key])}`)
19
+ .join(",")}}`;
20
+ }
21
+ throw new Error(`Cannot hash ${typeof value}`);
22
+ }
23
+ export async function sha256Text(value) {
24
+ const digest = await globalThis.crypto.subtle.digest("SHA-256", new TextEncoder().encode(value));
25
+ return [...new Uint8Array(digest)]
26
+ .map((byte) => byte.toString(16).padStart(2, "0"))
27
+ .join("");
28
+ }
29
+ export function canonicalJson(value) {
30
+ return canonicalize(value);
31
+ }
32
+ export function transcriptEntryHash(entry) {
33
+ return sha256Text(canonicalJson(entry));
34
+ }
35
+ export async function deterministicMemoryId(namespace, ...parts) {
36
+ return `${namespace}-${(await sha256Text(parts.join("\u0000"))).slice(0, 32)}`;
37
+ }
@@ -0,0 +1,19 @@
1
+ import type { ConversationRepository, ConversationRepositoryChange } from "../conversations/conversation-repository";
2
+ import type { ConversationLocation } from "../conversations/types";
3
+ import type { MemoryService } from "./types";
4
+ export type MemoryIngestionErrorHandler = (operation: "ingest" | "rebuild", location: ConversationLocation | undefined, error: unknown) => void;
5
+ export declare class MemoryIngestionCoordinator {
6
+ #private;
7
+ private readonly repository;
8
+ private readonly memory;
9
+ private readonly onError;
10
+ private readonly delay;
11
+ constructor(repository: ConversationRepository, memory: MemoryService, onError?: MemoryIngestionErrorHandler, delay?: number);
12
+ handleRepositoryChange(change: ConversationRepositoryChange): void;
13
+ handleVaultChange(path: string, oldPath?: string): void;
14
+ startCatchUp(): void;
15
+ flush(): Promise<void>;
16
+ dispose(): void;
17
+ private schedule;
18
+ private hasTerminalTranscriptBoundary;
19
+ }