@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,13 @@
1
+ declare const MAX_DURABLE_FIELD_BYTES: number;
2
+ export type DurableSanitizationOptions = {
3
+ vaultRoot?: string;
4
+ workspaceRoot?: string;
5
+ knownSecrets?: Iterable<string>;
6
+ };
7
+ export type SanitizedDurableField = {
8
+ text?: string;
9
+ redacted: boolean;
10
+ truncated: boolean;
11
+ };
12
+ export declare function sanitizeDurableField(value: unknown, options?: DurableSanitizationOptions): SanitizedDurableField;
13
+ export { MAX_DURABLE_FIELD_BYTES };
@@ -0,0 +1,94 @@
1
+ const MAX_DURABLE_FIELD_BYTES = 64 * 1024;
2
+ const REDACTED = "[REDACTED]";
3
+ const TRUNCATION_MARKER = "\n...[truncated]";
4
+ const SENSITIVE_KEY = /^(?:authorization|cookie|credentials?|env(?:ironment)?|password|private[_-]?key|secret|token|access[_-]?token|refresh[_-]?token|api[_-]?key)$/iu;
5
+ function sanitizeStructure(value, state) {
6
+ if (Array.isArray(value)) {
7
+ return value.map((item) => sanitizeStructure(item, state));
8
+ }
9
+ if (value && typeof value === "object") {
10
+ const output = {};
11
+ for (const [key, nested] of Object.entries(value)) {
12
+ if (SENSITIVE_KEY.test(key)) {
13
+ output[key] = REDACTED;
14
+ state.redacted = true;
15
+ }
16
+ else {
17
+ output[key] = sanitizeStructure(nested, state);
18
+ }
19
+ }
20
+ return output;
21
+ }
22
+ return value;
23
+ }
24
+ function stringify(value) {
25
+ if (value == null)
26
+ return undefined;
27
+ if (typeof value === "string")
28
+ return value;
29
+ try {
30
+ return JSON.stringify(value);
31
+ }
32
+ catch {
33
+ return String(value);
34
+ }
35
+ }
36
+ function replaceAllLiteral(value, needle, replacement) {
37
+ return needle ? value.split(needle).join(replacement) : value;
38
+ }
39
+ function truncateUtf8(value) {
40
+ const encoded = new TextEncoder().encode(value);
41
+ if (encoded.byteLength <= MAX_DURABLE_FIELD_BYTES) {
42
+ return { text: value, truncated: false };
43
+ }
44
+ const marker = new TextEncoder().encode(TRUNCATION_MARKER);
45
+ const slice = encoded.slice(0, MAX_DURABLE_FIELD_BYTES - marker.byteLength);
46
+ return {
47
+ text: `${new TextDecoder().decode(slice)}${TRUNCATION_MARKER}`,
48
+ truncated: true,
49
+ };
50
+ }
51
+ export function sanitizeDurableField(value, options = {}) {
52
+ const state = { redacted: false };
53
+ const structured = sanitizeStructure(value, state);
54
+ const initialText = stringify(structured);
55
+ if (initialText == null) {
56
+ return { redacted: state.redacted, truncated: false };
57
+ }
58
+ let text = initialText;
59
+ const replacements = [
60
+ [options.vaultRoot, "<vault>"],
61
+ [options.workspaceRoot, "<workspace>"],
62
+ ];
63
+ for (const [root, replacement] of replacements) {
64
+ if (root && text.includes(root)) {
65
+ text = replaceAllLiteral(text, root, replacement);
66
+ state.redacted = true;
67
+ }
68
+ }
69
+ for (const secret of options.knownSecrets ?? []) {
70
+ if (secret && text.includes(secret)) {
71
+ text = replaceAllLiteral(text, secret, REDACTED);
72
+ state.redacted = true;
73
+ }
74
+ }
75
+ const patterns = [
76
+ /-----BEGIN [^-]+ PRIVATE KEY-----[\s\S]*?-----END [^-]+ PRIVATE KEY-----/giu,
77
+ /\bBearer\s+[A-Za-z0-9._~+/=-]+/giu,
78
+ /\b(?:api[_-]?key|password|secret|token)\s*[:=]\s*[^\s,;]+/giu,
79
+ /\bCookie\s*:\s*[^\r\n]+/giu,
80
+ ];
81
+ for (const pattern of patterns) {
82
+ const replaced = text.replace(pattern, REDACTED);
83
+ if (replaced !== text)
84
+ state.redacted = true;
85
+ text = replaced;
86
+ }
87
+ const truncated = truncateUtf8(text);
88
+ return {
89
+ text: truncated.text,
90
+ redacted: state.redacted,
91
+ truncated: truncated.truncated,
92
+ };
93
+ }
94
+ export { MAX_DURABLE_FIELD_BYTES };
@@ -0,0 +1,3 @@
1
+ import type { App } from "@lapis-notes/api";
2
+ export declare const FILE_EXPLORER_REVEAL_PATH_COMMAND = "lapis-file-explorer:reveal-path";
3
+ export declare function revealConversationScope(app: App | undefined, scopeDir: string): void;
@@ -0,0 +1,8 @@
1
+ export const FILE_EXPLORER_REVEAL_PATH_COMMAND = "lapis-file-explorer:reveal-path";
2
+ export function revealConversationScope(app, scopeDir) {
3
+ if (!app)
4
+ return;
5
+ void app.commands
6
+ .executeCommand(FILE_EXPLORER_REVEAL_PATH_COMMAND, scopeDir || "/")
7
+ .catch(() => undefined);
8
+ }
@@ -0,0 +1,11 @@
1
+ import type { TFile } from "@lapis-notes/api/vault";
2
+ export type ConversationScopeResolution = {
3
+ scopeDir: string;
4
+ source: "explicit" | "active-file" | "vault-root";
5
+ };
6
+ export declare class ConversationScopeResolver {
7
+ resolve(input: {
8
+ explicitFolder?: string | null;
9
+ activeFile?: Pick<TFile, "path"> | null;
10
+ }): ConversationScopeResolution;
11
+ }
@@ -0,0 +1,18 @@
1
+ import { conversationScopeForActiveFile, normalizeConversationScope, } from "./paths";
2
+ export class ConversationScopeResolver {
3
+ resolve(input) {
4
+ if (input.explicitFolder != null) {
5
+ return {
6
+ scopeDir: normalizeConversationScope(input.explicitFolder),
7
+ source: "explicit",
8
+ };
9
+ }
10
+ if (input.activeFile?.path) {
11
+ return {
12
+ scopeDir: conversationScopeForActiveFile(input.activeFile.path),
13
+ source: "active-file",
14
+ };
15
+ }
16
+ return { scopeDir: "", source: "vault-root" };
17
+ }
18
+ }
@@ -0,0 +1,10 @@
1
+ import type { ConversationListEntry } from "./transcript-store";
2
+ export declare function isScopeInTree(contextDir: string, scopeDir: string): boolean;
3
+ export declare function scopeDepthFromContext(contextDir: string, scopeDir: string): number;
4
+ export declare function formatDirectoryContextLabel(scopeDir: string): string;
5
+ export declare function relativeScopeLabel(contextDir: string, scopeDir: string): string;
6
+ export declare function conversationsInScopeTree(entries: ConversationListEntry[], contextDir: string): ConversationListEntry[];
7
+ export declare function groupConversationsByRelativeScope(entries: ConversationListEntry[], contextDir: string): {
8
+ heading: string;
9
+ items: ConversationListEntry[];
10
+ }[];
@@ -0,0 +1,71 @@
1
+ import { normalizeConversationScope } from "./paths";
2
+ export function isScopeInTree(contextDir, scopeDir) {
3
+ const context = normalizeConversationScope(contextDir);
4
+ const scope = normalizeConversationScope(scopeDir);
5
+ if (!context)
6
+ return true;
7
+ return scope === context || scope.startsWith(`${context}/`);
8
+ }
9
+ export function scopeDepthFromContext(contextDir, scopeDir) {
10
+ const context = normalizeConversationScope(contextDir);
11
+ const scope = normalizeConversationScope(scopeDir);
12
+ if (!context) {
13
+ return scope ? scope.split("/").length : 0;
14
+ }
15
+ if (scope === context)
16
+ return 0;
17
+ if (!scope.startsWith(`${context}/`))
18
+ return Number.POSITIVE_INFINITY;
19
+ return scope.slice(context.length + 1).split("/").length;
20
+ }
21
+ export function formatDirectoryContextLabel(scopeDir) {
22
+ const scope = normalizeConversationScope(scopeDir);
23
+ return scope || "Vault";
24
+ }
25
+ export function relativeScopeLabel(contextDir, scopeDir) {
26
+ const context = normalizeConversationScope(contextDir);
27
+ const scope = normalizeConversationScope(scopeDir);
28
+ if (!scope)
29
+ return "Vault";
30
+ if (!context)
31
+ return scope;
32
+ if (scope === context)
33
+ return formatDirectoryContextLabel(context);
34
+ if (scope.startsWith(`${context}/`))
35
+ return scope.slice(context.length + 1);
36
+ return scope;
37
+ }
38
+ export function conversationsInScopeTree(entries, contextDir) {
39
+ return entries
40
+ .filter((entry) => {
41
+ if (entry.metadata?.status === "archived")
42
+ return false;
43
+ if (entry.unavailableReason)
44
+ return false;
45
+ return isScopeInTree(contextDir, entry.location.scopeDir);
46
+ })
47
+ .sort((left, right) => {
48
+ const depthDelta = scopeDepthFromContext(contextDir, left.location.scopeDir) -
49
+ scopeDepthFromContext(contextDir, right.location.scopeDir);
50
+ if (depthDelta !== 0)
51
+ return depthDelta;
52
+ const leftUpdated = left.metadata?.updatedAt ?? "";
53
+ const rightUpdated = right.metadata?.updatedAt ?? "";
54
+ if (leftUpdated !== rightUpdated) {
55
+ return rightUpdated.localeCompare(leftUpdated);
56
+ }
57
+ return (left.metadata?.title ?? left.location.conversationId).localeCompare(right.metadata?.title ?? right.location.conversationId);
58
+ });
59
+ }
60
+ export function groupConversationsByRelativeScope(entries, contextDir) {
61
+ const groups = [];
62
+ for (const entry of entries) {
63
+ const heading = relativeScopeLabel(contextDir, entry.location.scopeDir);
64
+ const last = groups.at(-1);
65
+ if (last?.heading === heading)
66
+ last.items.push(entry);
67
+ else
68
+ groups.push({ heading, items: [entry] });
69
+ }
70
+ return groups;
71
+ }
@@ -0,0 +1,9 @@
1
+ import type { AiChatItem } from "../chat/chat-items";
2
+ import type { DurableSanitizationOptions } from "./redaction";
3
+ import { type TranscriptEntry } from "./types";
4
+ export type TranscriptProjectionOptions = DurableSanitizationOptions & {
5
+ agentBindingId?: string;
6
+ now?: () => string;
7
+ };
8
+ export declare function projectChatItemsToTranscript(items: AiChatItem[], options?: TranscriptProjectionOptions): TranscriptEntry[];
9
+ export declare function projectTranscriptToChatItems(entries: TranscriptEntry[]): AiChatItem[];
@@ -0,0 +1,409 @@
1
+ import { sanitizeDurableField } from "./redaction";
2
+ import { CONVERSATION_SCHEMA_VERSION } from "./types";
3
+ function isRuntimeProvenance(value) {
4
+ return Boolean(value &&
5
+ typeof value === "object" &&
6
+ "sessionId" in value &&
7
+ "runId" in value);
8
+ }
9
+ function runtimeProvenance(value) {
10
+ return isRuntimeProvenance(value) ? { source: { ...value } } : {};
11
+ }
12
+ function createdAt(item, now) {
13
+ return item.createdAt ?? now();
14
+ }
15
+ export function projectChatItemsToTranscript(items, options = {}) {
16
+ const now = options.now ?? (() => new Date().toISOString());
17
+ return items.flatMap((item) => {
18
+ const common = {
19
+ schemaVersion: CONVERSATION_SCHEMA_VERSION,
20
+ createdAt: createdAt(item, now),
21
+ ...(options.agentBindingId
22
+ ? { agentBindingId: options.agentBindingId }
23
+ : {}),
24
+ ...runtimeProvenance(item.source),
25
+ };
26
+ switch (item.type) {
27
+ case "message":
28
+ return [
29
+ {
30
+ ...common,
31
+ id: item.id,
32
+ type: "message",
33
+ role: item.role,
34
+ text: item.text,
35
+ provenance: {
36
+ originClass: item.role === "user" ? "owner" : "agent",
37
+ sourceKind: item.role === "user" ? "user-message" : "assistant-message",
38
+ },
39
+ },
40
+ ];
41
+ case "thinking":
42
+ if (item.state !== "done" ||
43
+ (item.kind !== "summary" && item.kind !== "plan")) {
44
+ return [];
45
+ }
46
+ return [
47
+ {
48
+ ...common,
49
+ id: item.id,
50
+ type: "thinking.summary",
51
+ text: item.text,
52
+ kind: item.kind,
53
+ provenance: {
54
+ originClass: "agent",
55
+ sourceKind: "runtime-output",
56
+ },
57
+ },
58
+ ];
59
+ case "tool": {
60
+ if (item.state === "running")
61
+ return [];
62
+ const input = sanitizeDurableField(item.input, options);
63
+ const output = sanitizeDurableField(item.output, options);
64
+ return [
65
+ {
66
+ ...common,
67
+ id: item.id,
68
+ type: "tool",
69
+ toolId: item.toolId,
70
+ name: item.name,
71
+ server: item.server,
72
+ state: item.state,
73
+ input: input.text,
74
+ output: output.text,
75
+ redacted: input.redacted || output.redacted || undefined,
76
+ truncated: input.truncated || output.truncated || undefined,
77
+ provenance: {
78
+ originClass: "untrusted",
79
+ sourceKind: "runtime-output",
80
+ },
81
+ },
82
+ ];
83
+ }
84
+ case "approval": {
85
+ const toolInput = sanitizeDurableField(item.request.tool?.input, options);
86
+ const entries = [
87
+ {
88
+ ...common,
89
+ id: `${item.id}:request`,
90
+ type: "approval.request",
91
+ requestId: item.request.id,
92
+ kind: item.request.kind,
93
+ title: item.request.title,
94
+ tool: item.request.tool
95
+ ? { name: item.request.tool.name, input: toolInput.text }
96
+ : undefined,
97
+ options: item.request.options.map((option) => ({ ...option })),
98
+ redacted: toolInput.redacted || undefined,
99
+ truncated: toolInput.truncated || undefined,
100
+ provenance: {
101
+ originClass: "untrusted",
102
+ sourceKind: "runtime-output",
103
+ },
104
+ },
105
+ ];
106
+ if (item.responseOptionId) {
107
+ const option = item.request.options.find((candidate) => candidate.id === item.responseOptionId);
108
+ entries.push({
109
+ ...common,
110
+ id: `${item.id}:response`,
111
+ type: "approval.response",
112
+ requestId: item.request.id,
113
+ option: {
114
+ id: item.responseOptionId,
115
+ label: option?.label ?? item.responseOptionId,
116
+ },
117
+ provenance: {
118
+ originClass: "owner",
119
+ sourceKind: "owner-response",
120
+ },
121
+ });
122
+ }
123
+ return entries;
124
+ }
125
+ case "question": {
126
+ const entries = [
127
+ {
128
+ ...common,
129
+ id: `${item.id}:request`,
130
+ type: "question.request",
131
+ requestId: item.request.id,
132
+ title: item.request.title,
133
+ questions: structuredClone(item.request.questions),
134
+ provenance: {
135
+ originClass: "agent",
136
+ sourceKind: "runtime-output",
137
+ },
138
+ },
139
+ ];
140
+ if (item.status !== "pending") {
141
+ entries.push({
142
+ ...common,
143
+ id: `${item.id}:response`,
144
+ type: "question.response",
145
+ requestId: item.request.id,
146
+ status: item.status,
147
+ provenance: {
148
+ originClass: "owner",
149
+ sourceKind: "owner-response",
150
+ },
151
+ });
152
+ }
153
+ return entries;
154
+ }
155
+ case "status":
156
+ return [
157
+ {
158
+ ...common,
159
+ id: item.id,
160
+ type: "system.notice",
161
+ text: sanitizeDurableField(item.text, options).text ?? "",
162
+ ...(item.layout === "report" || item.layout === "inventory"
163
+ ? { layout: item.layout }
164
+ : {}),
165
+ ...(item.layout === "inventory" && item.inventory
166
+ ? { inventory: item.inventory }
167
+ : {}),
168
+ provenance: {
169
+ originClass: "system",
170
+ sourceKind: "app-system",
171
+ },
172
+ },
173
+ ];
174
+ case "error":
175
+ return [
176
+ {
177
+ ...common,
178
+ id: item.id,
179
+ type: "error",
180
+ message: sanitizeDurableField(item.text, options).text ?? "",
181
+ retryable: true,
182
+ provenance: {
183
+ originClass: "system",
184
+ sourceKind: "app-system",
185
+ },
186
+ },
187
+ ];
188
+ case "command":
189
+ return [
190
+ {
191
+ ...common,
192
+ id: item.id,
193
+ type: "command",
194
+ command: item.command,
195
+ origin: item.origin,
196
+ arguments: item.arguments,
197
+ status: item.status,
198
+ provenance: {
199
+ originClass: "system",
200
+ sourceKind: "app-system",
201
+ },
202
+ },
203
+ ];
204
+ case "skill-activation":
205
+ return [
206
+ {
207
+ ...common,
208
+ id: item.id,
209
+ type: "skill-activation",
210
+ skillId: item.skillId,
211
+ skillName: item.skillName,
212
+ version: item.version,
213
+ origin: item.origin,
214
+ arguments: item.arguments,
215
+ provenance: {
216
+ originClass: item.origin === "user"
217
+ ? "owner"
218
+ : item.origin === "model"
219
+ ? "agent"
220
+ : "system",
221
+ sourceKind: item.origin === "user"
222
+ ? "owner-response"
223
+ : item.origin === "model"
224
+ ? "runtime-output"
225
+ : "app-system",
226
+ },
227
+ },
228
+ ];
229
+ }
230
+ });
231
+ }
232
+ export function projectTranscriptToChatItems(entries) {
233
+ const items = [];
234
+ const approvals = new Map();
235
+ const questions = new Map();
236
+ for (const entry of entries) {
237
+ switch (entry.type) {
238
+ case "message":
239
+ items.push({
240
+ id: entry.id,
241
+ type: "message",
242
+ role: entry.role,
243
+ text: entry.text,
244
+ createdAt: entry.createdAt,
245
+ agentBindingId: entry.agentBindingId,
246
+ });
247
+ break;
248
+ case "thinking.summary":
249
+ items.push({
250
+ id: entry.id,
251
+ type: "thinking",
252
+ text: entry.text,
253
+ kind: entry.kind,
254
+ state: "done",
255
+ createdAt: entry.createdAt,
256
+ agentBindingId: entry.agentBindingId,
257
+ });
258
+ break;
259
+ case "tool":
260
+ items.push({
261
+ id: entry.id,
262
+ type: "tool",
263
+ toolId: entry.toolId,
264
+ name: entry.name,
265
+ server: entry.server,
266
+ state: entry.state === "cancelled" ? "error" : entry.state,
267
+ input: entry.input,
268
+ output: entry.output,
269
+ createdAt: entry.createdAt,
270
+ agentBindingId: entry.agentBindingId,
271
+ });
272
+ break;
273
+ case "approval.request":
274
+ approvals.set(entry.requestId, items.length);
275
+ items.push({
276
+ id: entry.id.replace(/:request$/u, ""),
277
+ type: "approval",
278
+ request: {
279
+ id: entry.requestId,
280
+ kind: entry.kind,
281
+ title: entry.title,
282
+ tool: entry.tool,
283
+ options: structuredClone(entry.options),
284
+ },
285
+ status: "pending",
286
+ createdAt: entry.createdAt,
287
+ agentBindingId: entry.agentBindingId,
288
+ });
289
+ break;
290
+ case "approval.response": {
291
+ const index = approvals.get(entry.requestId);
292
+ if (index !== undefined) {
293
+ const current = items[index];
294
+ if (current?.type === "approval") {
295
+ items[index] = {
296
+ ...current,
297
+ status: entry.option.id.startsWith("deny")
298
+ ? "rejected"
299
+ : "approved",
300
+ responseOptionId: entry.option.id,
301
+ };
302
+ }
303
+ }
304
+ break;
305
+ }
306
+ case "question.request":
307
+ questions.set(entry.requestId, items.length);
308
+ items.push({
309
+ id: entry.id.replace(/:request$/u, ""),
310
+ type: "question",
311
+ request: {
312
+ id: entry.requestId,
313
+ title: entry.title,
314
+ questions: structuredClone(entry.questions),
315
+ },
316
+ status: "pending",
317
+ createdAt: entry.createdAt,
318
+ agentBindingId: entry.agentBindingId,
319
+ });
320
+ break;
321
+ case "question.response": {
322
+ const index = questions.get(entry.requestId);
323
+ if (index !== undefined) {
324
+ const current = items[index];
325
+ if (current?.type === "question") {
326
+ items[index] = { ...current, status: entry.status };
327
+ }
328
+ }
329
+ break;
330
+ }
331
+ case "system.notice":
332
+ items.push({
333
+ id: entry.id,
334
+ type: "status",
335
+ text: entry.text,
336
+ ...(entry.layout === "report" || entry.layout === "inventory"
337
+ ? { layout: entry.layout }
338
+ : {}),
339
+ ...(entry.layout === "inventory" && entry.inventory
340
+ ? { inventory: entry.inventory }
341
+ : {}),
342
+ createdAt: entry.createdAt,
343
+ agentBindingId: entry.agentBindingId,
344
+ });
345
+ break;
346
+ case "error":
347
+ items.push({
348
+ id: entry.id,
349
+ type: "error",
350
+ text: entry.message,
351
+ createdAt: entry.createdAt,
352
+ agentBindingId: entry.agentBindingId,
353
+ });
354
+ break;
355
+ case "command":
356
+ items.push({
357
+ id: entry.id,
358
+ type: "command",
359
+ command: entry.command,
360
+ origin: entry.origin,
361
+ arguments: entry.arguments,
362
+ status: entry.status,
363
+ text: `/${entry.command}${entry.arguments ? ` ${entry.arguments}` : ""}`,
364
+ createdAt: entry.createdAt,
365
+ agentBindingId: entry.agentBindingId,
366
+ });
367
+ break;
368
+ case "skill-activation":
369
+ items.push({
370
+ id: entry.id,
371
+ type: "skill-activation",
372
+ skillId: entry.skillId,
373
+ skillName: entry.skillName,
374
+ version: entry.version,
375
+ origin: entry.origin,
376
+ arguments: entry.arguments,
377
+ text: `Skill ${entry.skillName} (${entry.version})`,
378
+ createdAt: entry.createdAt,
379
+ agentBindingId: entry.agentBindingId,
380
+ });
381
+ break;
382
+ case "agent.switch":
383
+ case "agent.config":
384
+ break;
385
+ case "cancelled": {
386
+ if (entry.interactionType === "approval" && entry.requestId) {
387
+ const index = approvals.get(entry.requestId);
388
+ if (index !== undefined) {
389
+ const current = items[index];
390
+ if (current?.type === "approval") {
391
+ items[index] = { ...current, status: "cancelled" };
392
+ }
393
+ }
394
+ }
395
+ if (entry.interactionType === "question" && entry.requestId) {
396
+ const index = questions.get(entry.requestId);
397
+ if (index !== undefined) {
398
+ const current = items[index];
399
+ if (current?.type === "question") {
400
+ items[index] = { ...current, status: "cancelled" };
401
+ }
402
+ }
403
+ }
404
+ break;
405
+ }
406
+ }
407
+ }
408
+ return items;
409
+ }
@@ -0,0 +1,24 @@
1
+ import type { AgentBindingRecord, ConversationLocation, ConversationMetadata, ConversationSnapshot, TranscriptEntry } from "./types";
2
+ export type ConversationListEntry = {
3
+ location: ConversationLocation;
4
+ metadata?: ConversationMetadata;
5
+ unavailableReason?: string;
6
+ /** Disposable search-index context. Never written to portable source files. */
7
+ preview?: string;
8
+ };
9
+ export interface TranscriptStore {
10
+ create(location: ConversationLocation, metadata: ConversationMetadata): Promise<ConversationSnapshot>;
11
+ read(location: ConversationLocation): Promise<ConversationSnapshot>;
12
+ list(scopeDir: string): Promise<ConversationListEntry[]>;
13
+ listAll(): Promise<ConversationListEntry[]>;
14
+ writeMetadata(location: ConversationLocation, metadata: ConversationMetadata): Promise<void>;
15
+ appendAgentRecords(location: ConversationLocation, records: AgentBindingRecord[]): Promise<void>;
16
+ appendTranscriptEntries(location: ConversationLocation, entries: TranscriptEntry[]): Promise<void>;
17
+ delete(location: ConversationLocation): Promise<void>;
18
+ }
19
+ export declare function conversationStorageKey(location: ConversationLocation): string;
20
+ export declare function cloneConversationSnapshot(snapshot: ConversationSnapshot): ConversationSnapshot;
21
+ export declare class ConversationWriteQueue {
22
+ private readonly pending;
23
+ run<T>(key: string, operation: () => Promise<T>): Promise<T>;
24
+ }