@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,42 @@
1
+ const GENERIC_TOOL_NAMES = new Set(["tool call", "acp_tool", "tool"]);
2
+ export function isGenericToolName(name) {
3
+ const normalized = name?.trim().toLowerCase() ?? "";
4
+ return normalized.length === 0 || GENERIC_TOOL_NAMES.has(normalized);
5
+ }
6
+ export function isEmptyToolInput(value) {
7
+ if (value == null)
8
+ return true;
9
+ if (typeof value === "string") {
10
+ const trimmed = value.trim();
11
+ return trimmed.length === 0 || trimmed === "{}";
12
+ }
13
+ if (Array.isArray(value))
14
+ return value.length === 0;
15
+ if (typeof value === "object")
16
+ return Object.keys(value).length === 0;
17
+ return false;
18
+ }
19
+ export function preferToolName(current, incoming) {
20
+ const incomingName = incoming?.trim() ?? "";
21
+ const currentName = current?.trim() ?? "";
22
+ if (!incomingName)
23
+ return currentName || "acp_tool";
24
+ if (isGenericToolName(incomingName) && currentName && !isGenericToolName(currentName)) {
25
+ return currentName;
26
+ }
27
+ return incomingName;
28
+ }
29
+ export function toolNameFromInput(input) {
30
+ if (!isRecord(input))
31
+ return undefined;
32
+ for (const key of ["name", "toolName", "tool"]) {
33
+ const value = input[key];
34
+ if (typeof value === "string" && value.trim() && !isGenericToolName(value)) {
35
+ return value.trim();
36
+ }
37
+ }
38
+ return undefined;
39
+ }
40
+ function isRecord(value) {
41
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
42
+ }
@@ -0,0 +1,6 @@
1
+ import type { AgentEvent } from "../core/types";
2
+ import { type AiChatItem } from "./chat-items";
3
+ export declare function applyAgentEventToChatItems(items: AiChatItem[], event: AgentEvent, now?: () => string): AiChatItem[];
4
+ export declare function isVisibleAgentStatus(status: string): boolean;
5
+ export declare function markApprovalResponse(items: AiChatItem[], requestId: string, optionId: string): AiChatItem[];
6
+ export declare function markQuestionResponse(items: AiChatItem[], requestId: string): AiChatItem[];
@@ -0,0 +1,256 @@
1
+ import { APP_TOOL_MCP_SERVER_NAME } from "../tools/mcp-server-registry";
2
+ import { createChatItemId } from "./chat-items";
3
+ import { isEmptyToolInput, isGenericToolName, preferToolName, } from "./chat-tool-identity";
4
+ export function applyAgentEventToChatItems(items, event, now = () => new Date().toISOString()) {
5
+ const next = [...items];
6
+ const createdAt = now();
7
+ const source = event.source ? { source: { ...event.source } } : {};
8
+ switch (event.type) {
9
+ case "text": {
10
+ settleStreamingThinking(next);
11
+ const last = next.at(-1);
12
+ if (last?.type === "message" && last.role === "assistant") {
13
+ next[next.length - 1] = {
14
+ ...last,
15
+ ...source,
16
+ text: `${last.text}${event.text}`,
17
+ };
18
+ return next;
19
+ }
20
+ next.push({
21
+ id: createChatItemId("message", next.length + 1),
22
+ type: "message",
23
+ role: "assistant",
24
+ text: event.text,
25
+ createdAt,
26
+ ...source,
27
+ });
28
+ return next;
29
+ }
30
+ case "thinking": {
31
+ const last = next.at(-1);
32
+ if (last?.type === "thinking" && last.state === "streaming") {
33
+ next[next.length - 1] = {
34
+ ...last,
35
+ text: `${last.text}${event.text}`,
36
+ kind: event.kind ?? last.kind,
37
+ ...source,
38
+ };
39
+ return next;
40
+ }
41
+ next.push({
42
+ id: createChatItemId("thinking", next.length + 1),
43
+ type: "thinking",
44
+ text: event.text,
45
+ kind: event.kind,
46
+ state: "streaming",
47
+ createdAt,
48
+ ...source,
49
+ });
50
+ return next;
51
+ }
52
+ case "tool.start": {
53
+ settleStreamingThinking(next);
54
+ const index = findToolItemIndex(next, event);
55
+ const input = preferToolInput(undefined, event.input);
56
+ if (index >= 0) {
57
+ const current = next[index];
58
+ if (current?.type === "tool") {
59
+ next[index] = {
60
+ ...current,
61
+ toolId: pairedToolId(current, event.id),
62
+ name: preferToolName(current.name, event.name),
63
+ server: event.server ?? current.server,
64
+ state: "running",
65
+ input: preferToolInput(current.input, event.input),
66
+ ...source,
67
+ };
68
+ }
69
+ }
70
+ else {
71
+ next.push({
72
+ id: event.id,
73
+ type: "tool",
74
+ toolId: event.id,
75
+ name: preferToolName(undefined, event.name),
76
+ server: event.server,
77
+ state: "running",
78
+ input,
79
+ createdAt,
80
+ ...source,
81
+ });
82
+ }
83
+ return next;
84
+ }
85
+ case "tool.end": {
86
+ settleStreamingThinking(next);
87
+ const index = findToolItemIndex(next, event);
88
+ const output = event.error != null
89
+ ? stringifyUnknown(event.error)
90
+ : stringifyUnknown(event.output);
91
+ if (index >= 0) {
92
+ const current = next[index];
93
+ if (current?.type === "tool") {
94
+ next[index] = {
95
+ ...current,
96
+ toolId: pairedToolId(current, event.id),
97
+ name: preferToolName(current.name, event.name),
98
+ server: event.server ?? current.server,
99
+ state: event.error != null ? "error" : "completed",
100
+ input: preferToolInput(current.input, event.input),
101
+ output,
102
+ ...source,
103
+ };
104
+ }
105
+ return next;
106
+ }
107
+ next.push({
108
+ id: event.id,
109
+ type: "tool",
110
+ toolId: event.id,
111
+ name: preferToolName(undefined, event.name),
112
+ server: event.server,
113
+ state: event.error != null ? "error" : "completed",
114
+ input: preferToolInput(undefined, event.input),
115
+ output,
116
+ createdAt,
117
+ ...source,
118
+ });
119
+ return next;
120
+ }
121
+ case "permission.request": {
122
+ settleStreamingThinking(next);
123
+ next.push({
124
+ id: `approval-${event.request.id}`,
125
+ type: "approval",
126
+ request: event.request,
127
+ status: "pending",
128
+ createdAt,
129
+ ...source,
130
+ });
131
+ return next;
132
+ }
133
+ case "question.request": {
134
+ settleStreamingThinking(next);
135
+ next.push({
136
+ id: `question-${event.request.id}`,
137
+ type: "question",
138
+ request: event.request,
139
+ status: "pending",
140
+ createdAt,
141
+ ...source,
142
+ });
143
+ return next;
144
+ }
145
+ case "status": {
146
+ if (!isVisibleAgentStatus(event.status))
147
+ return next;
148
+ settleStreamingThinking(next);
149
+ next.push({
150
+ id: createChatItemId("status", next.length + 1),
151
+ type: "status",
152
+ text: event.status,
153
+ createdAt,
154
+ ...source,
155
+ });
156
+ return next;
157
+ }
158
+ case "error": {
159
+ const settled = next.map((item) => item.type === "thinking" && item.state === "streaming"
160
+ ? { ...item, state: "done" }
161
+ : item);
162
+ settled.push({
163
+ id: createChatItemId("error", next.length + 1),
164
+ type: "error",
165
+ text: event.error.message,
166
+ createdAt,
167
+ ...source,
168
+ });
169
+ return settled;
170
+ }
171
+ case "completed": {
172
+ return next.map((item) => item.type === "thinking" && item.state === "streaming"
173
+ ? { ...item, state: "done", ...source }
174
+ : item);
175
+ }
176
+ case "commands.update":
177
+ return next;
178
+ default:
179
+ return next;
180
+ }
181
+ }
182
+ export function isVisibleAgentStatus(status) {
183
+ const normalized = status.trim().toLowerCase();
184
+ return !(/^usage updated(?:\s*:\s*[\d,]+\s*\/\s*[\d,]+)?$/.test(normalized) ||
185
+ normalized === "session updated" ||
186
+ /^available commands updated(?:\s*\(\d+\))?$/.test(normalized));
187
+ }
188
+ export function markApprovalResponse(items, requestId, optionId) {
189
+ return items.map((item) => {
190
+ if (item.type !== "approval" || item.request.id !== requestId)
191
+ return item;
192
+ const denied = optionId.startsWith("deny");
193
+ return {
194
+ ...item,
195
+ status: denied ? "rejected" : "approved",
196
+ responseOptionId: optionId,
197
+ };
198
+ });
199
+ }
200
+ export function markQuestionResponse(items, requestId) {
201
+ return items.map((item) => item.type === "question" && item.request.id === requestId
202
+ ? { ...item, status: "answered" }
203
+ : item);
204
+ }
205
+ function settleStreamingThinking(items) {
206
+ for (const [index, item] of items.entries()) {
207
+ if (item.type === "thinking" && item.state === "streaming") {
208
+ items[index] = { ...item, state: "done" };
209
+ }
210
+ }
211
+ }
212
+ function stringifyUnknown(value) {
213
+ if (value == null)
214
+ return undefined;
215
+ if (typeof value === "string")
216
+ return value;
217
+ try {
218
+ return JSON.stringify(value);
219
+ }
220
+ catch {
221
+ return String(value);
222
+ }
223
+ }
224
+ function preferToolInput(current, incoming) {
225
+ if (isEmptyToolInput(incoming))
226
+ return current;
227
+ return stringifyUnknown(incoming) ?? current;
228
+ }
229
+ function pairedToolId(current, incomingId) {
230
+ if (current.id === incomingId || current.toolId === incomingId) {
231
+ return current.toolId;
232
+ }
233
+ return incomingId;
234
+ }
235
+ function findToolItemIndex(items, event) {
236
+ const exact = items.findIndex((item) => item.type === "tool" &&
237
+ (item.toolId === event.id || item.id === event.id));
238
+ if (exact >= 0)
239
+ return exact;
240
+ const incomingApp = event.server === APP_TOOL_MCP_SERVER_NAME;
241
+ const incomingGeneric = !incomingApp &&
242
+ isGenericToolName(event.name) &&
243
+ isEmptyToolInput(event.input);
244
+ if (incomingApp) {
245
+ return items.findIndex((item) => item.type === "tool" &&
246
+ item.server !== APP_TOOL_MCP_SERVER_NAME &&
247
+ isGenericToolName(item.name) &&
248
+ item.id === item.toolId);
249
+ }
250
+ if (incomingGeneric) {
251
+ return items.findIndex((item) => item.type === "tool" &&
252
+ item.server === APP_TOOL_MCP_SERVER_NAME &&
253
+ item.id === item.toolId);
254
+ }
255
+ return -1;
256
+ }
@@ -0,0 +1 @@
1
+ export declare const LIVE_RUNTIME_UNAVAILABLE_REASON = "The local agent server is not connected. Start it, then set its URL, port, and token in Settings.";
@@ -0,0 +1 @@
1
+ export const LIVE_RUNTIME_UNAVAILABLE_REASON = "The local agent server is not connected. Start it, then set its URL, port, and token in Settings.";
@@ -0,0 +1,8 @@
1
+ import type { AcpAgentId } from "../settings/acp-agents";
2
+ export type ComposerAgentSelection = {
3
+ agent: AcpAgentId;
4
+ runtimePreference: "acp" | "codex-native" | "fake";
5
+ label: string;
6
+ };
7
+ export declare function composerAgentLabel(agent?: string, runtimePreference?: string): string;
8
+ export declare function parseAgentCommand(raw: string): ComposerAgentSelection | "status" | "unknown";
@@ -0,0 +1,38 @@
1
+ const KNOWN = {
2
+ codex: { agent: "codex", runtimePreference: "acp", label: "Codex ACP" },
3
+ "codex-acp": {
4
+ agent: "codex",
5
+ runtimePreference: "acp",
6
+ label: "Codex ACP",
7
+ },
8
+ cursor: { agent: "cursor", runtimePreference: "acp", label: "Cursor ACP" },
9
+ "cursor-acp": {
10
+ agent: "cursor",
11
+ runtimePreference: "acp",
12
+ label: "Cursor ACP",
13
+ },
14
+ native: {
15
+ agent: "codex",
16
+ runtimePreference: "codex-native",
17
+ label: "Codex Native",
18
+ },
19
+ "codex-native": {
20
+ agent: "codex",
21
+ runtimePreference: "codex-native",
22
+ label: "Codex Native",
23
+ },
24
+ fake: { agent: "codex", runtimePreference: "fake", label: "Fake" },
25
+ };
26
+ export function composerAgentLabel(agent, runtimePreference) {
27
+ if (runtimePreference === "fake")
28
+ return "Fake";
29
+ if (runtimePreference === "codex-native")
30
+ return "Codex Native";
31
+ return agent === "cursor" ? "Cursor ACP" : "Codex ACP";
32
+ }
33
+ export function parseAgentCommand(raw) {
34
+ const name = raw.trim().toLowerCase();
35
+ if (!name)
36
+ return "status";
37
+ return KNOWN[name] ?? "unknown";
38
+ }
@@ -0,0 +1,18 @@
1
+ import type { AppSlashCommandRegistry } from "@lapis-notes/api/agent-skills";
2
+ import type { NativeAgentCommand, SkillSnapshot } from "../skills/types";
3
+ import type { DiscoveredCommandFile } from "./discovery";
4
+ import type { EffectiveSlashCommand } from "./types";
5
+ export declare class SlashCommandCatalog {
6
+ #private;
7
+ private readonly extensions?;
8
+ constructor(extensions?: AppSlashCommandRegistry | undefined);
9
+ setFileCommandLoader(loader: (scopeDir: string) => Promise<void>): void;
10
+ refreshFileCommands(scopeDir: string): Promise<void>;
11
+ rebuildSkillCommands(snapshot: SkillSnapshot): void;
12
+ replaceFileCommands(commands: readonly DiscoveredCommandFile[], overlays?: readonly DiscoveredCommandFile[]): void;
13
+ replaceNativeCommands(agentBindingId: string, commands: readonly NativeAgentCommand[]): void;
14
+ clearNativeCommands(agentBindingId: string): void;
15
+ get(name: string, agentBindingId?: string): EffectiveSlashCommand | undefined;
16
+ list(agentBindingId?: string): EffectiveSlashCommand[];
17
+ native(agentBindingId: string | undefined, name: string): NativeAgentCommand | undefined;
18
+ }
@@ -0,0 +1,115 @@
1
+ import { reservedSlashCommandNames, RESERVED_SLASH_COMMANDS } from "./reserved";
2
+ export class SlashCommandCatalog {
3
+ extensions;
4
+ #skillCommands = [];
5
+ #fileCommands = [];
6
+ #reservedOverlays = new Map();
7
+ #loadFiles;
8
+ #native = new Map();
9
+ constructor(extensions) {
10
+ this.extensions = extensions;
11
+ }
12
+ setFileCommandLoader(loader) {
13
+ this.#loadFiles = loader;
14
+ }
15
+ async refreshFileCommands(scopeDir) {
16
+ await this.#loadFiles?.(scopeDir);
17
+ }
18
+ rebuildSkillCommands(snapshot) {
19
+ const reserved = reservedSlashCommandNames();
20
+ const extensionNames = new Set((this.extensions?.list() ?? []).map((item) => item.command.name));
21
+ this.#skillCommands = snapshot.skills
22
+ .filter((skill) => skill.userInvocable)
23
+ .map((skill) => {
24
+ const collision = reserved.has(skill.name) || extensionNames.has(skill.name);
25
+ return {
26
+ name: skill.name,
27
+ description: skill.description,
28
+ argumentHint: skill.argumentHint,
29
+ source: "skill",
30
+ dispatch: { kind: "skill", skill: skill.name },
31
+ disabled: collision,
32
+ };
33
+ })
34
+ .filter((command) => !("disabled" in command && command.disabled));
35
+ }
36
+ replaceFileCommands(commands, overlays = []) {
37
+ this.#fileCommands = commands.map((command) => ({
38
+ name: command.name,
39
+ description: command.description,
40
+ argumentHint: command.argumentHint,
41
+ aliases: command.aliases,
42
+ source: command.source,
43
+ dispatch: command.dispatch,
44
+ }));
45
+ this.#reservedOverlays = new Map(overlays.map((command) => [
46
+ command.name,
47
+ {
48
+ description: command.description,
49
+ argumentHint: command.argumentHint,
50
+ aliases: command.aliases,
51
+ },
52
+ ]));
53
+ }
54
+ replaceNativeCommands(agentBindingId, commands) {
55
+ this.#native.set(agentBindingId, commands.map((command) => ({ ...command })));
56
+ }
57
+ clearNativeCommands(agentBindingId) {
58
+ this.#native.delete(agentBindingId);
59
+ }
60
+ get(name, agentBindingId) {
61
+ return this.list(agentBindingId).find((command) => command.name === name || command.aliases?.includes(name));
62
+ }
63
+ list(agentBindingId) {
64
+ const reserved = reservedSlashCommandNames();
65
+ const reservedCommands = RESERVED_SLASH_COMMANDS.map((command) => {
66
+ const overlay = this.#reservedOverlays.get(command.name);
67
+ return overlay ? { ...command, ...overlay } : command;
68
+ });
69
+ const extension = (this.extensions?.list() ?? [])
70
+ .filter((item) => !reserved.has(item.command.name))
71
+ .map((item) => ({
72
+ name: item.command.name,
73
+ description: item.command.description,
74
+ argumentHint: item.command.argumentHint,
75
+ aliases: item.command.aliases,
76
+ source: "extension",
77
+ dispatch: item.command.dispatch,
78
+ }));
79
+ const extensionNames = new Set(extension.map((command) => command.name));
80
+ const files = this.#fileCommands.filter((command) => !reserved.has(command.name) && !extensionNames.has(command.name));
81
+ const fileNames = new Set(files.map((command) => command.name));
82
+ const skills = this.#skillCommands.filter((command) => !reserved.has(command.name) &&
83
+ !extensionNames.has(command.name) &&
84
+ !fileNames.has(command.name));
85
+ const claimed = new Set([
86
+ ...reserved,
87
+ ...extensionNames,
88
+ ...fileNames,
89
+ ...skills.map((command) => command.name),
90
+ ]);
91
+ const native = (agentBindingId ? this.#native.get(agentBindingId) : undefined)
92
+ ?.filter((command) => !claimed.has(sanitizeName(command.name)))
93
+ .map((command) => ({
94
+ name: sanitizeName(command.name),
95
+ description: command.description ?? "Current agent command",
96
+ argumentHint: command.argumentHint,
97
+ source: "native-agent",
98
+ dispatch: {
99
+ kind: "native-agent",
100
+ nativeName: command.name,
101
+ },
102
+ }));
103
+ return [...reservedCommands, ...extension, ...files, ...skills, ...(native ?? [])];
104
+ }
105
+ native(agentBindingId, name) {
106
+ if (!agentBindingId)
107
+ return undefined;
108
+ return this.#native
109
+ .get(agentBindingId)
110
+ ?.find((command) => sanitizeName(command.name) === sanitizeName(name));
111
+ }
112
+ }
113
+ function sanitizeName(name) {
114
+ return name.trim().replace(/^\/+/u, "").toLowerCase();
115
+ }
@@ -0,0 +1,36 @@
1
+ import type { Vault } from "@lapis-notes/api";
2
+ import type { AppSlashCommandDispatch } from "@lapis-notes/api/agent-skills";
3
+ import { type ParsedCommandFile } from "./markdown";
4
+ import type { SlashCommandSource } from "./types";
5
+ import type { UserAgentsCommandStore } from "./user-agents";
6
+ export type CommandFileSource = "folder" | "vault" | "user";
7
+ export interface DiscoveredCommandFile {
8
+ name: string;
9
+ description: string;
10
+ argumentHint?: string;
11
+ aliases?: string[];
12
+ source: CommandFileSource;
13
+ path?: string;
14
+ kind: ParsedCommandFile["kind"];
15
+ dispatch: AppSlashCommandDispatch;
16
+ }
17
+ export interface CommandDiagnostic {
18
+ path: string;
19
+ message: string;
20
+ }
21
+ export interface DiscoveredCommands {
22
+ commands: DiscoveredCommandFile[];
23
+ overlays: DiscoveredCommandFile[];
24
+ diagnostics: CommandDiagnostic[];
25
+ }
26
+ export interface CommandDiscoveryOptions {
27
+ vault: Vault;
28
+ userAgents?: UserAgentsCommandStore;
29
+ }
30
+ export declare class CommandDiscovery {
31
+ #private;
32
+ private readonly options;
33
+ constructor(options: CommandDiscoveryOptions);
34
+ discover(scopeDir: string): Promise<DiscoveredCommands>;
35
+ }
36
+ export declare function fileCommandSource(source: CommandFileSource): SlashCommandSource;
@@ -0,0 +1,151 @@
1
+ import { commandFileDispatch, commandNameFromFilename, CommandParseError, parseCommandMarkdown, } from "./markdown";
2
+ import { reservedSlashCommandNames } from "./reserved";
3
+ const SOURCE_RANK = {
4
+ folder: 3,
5
+ vault: 2,
6
+ user: 1,
7
+ };
8
+ function commandGlob(prefix) {
9
+ const root = prefix.replace(/\/$/u, "");
10
+ return `${root}/*.md`;
11
+ }
12
+ function folderPrefix(scopeDir) {
13
+ return scopeDir ? `${scopeDir}/.agents/commands` : ".agents/commands";
14
+ }
15
+ export class CommandDiscovery {
16
+ options;
17
+ constructor(options) {
18
+ this.options = options;
19
+ }
20
+ async discover(scopeDir) {
21
+ const diagnostics = [];
22
+ const collected = [];
23
+ await this.#collectVault("folder", folderPrefix(scopeDir), collected, diagnostics);
24
+ await this.#collectVault("vault", ".agents/commands", collected, diagnostics);
25
+ await this.#collectUser(collected, diagnostics);
26
+ return mergeCommandFiles(collected, diagnostics);
27
+ }
28
+ async #collectVault(source, prefix, collected, diagnostics) {
29
+ for (const file of this.options.vault.getFilesByGlob(commandGlob(prefix))) {
30
+ if (file.path.includes("/")) {
31
+ const parent = file.path.slice(0, file.path.lastIndexOf("/"));
32
+ if (parent !== prefix)
33
+ continue;
34
+ }
35
+ const filename = file.path.split("/").pop() ?? file.path;
36
+ const name = commandNameFromFilename(filename);
37
+ if (!name) {
38
+ diagnostics.push({
39
+ path: file.path,
40
+ message: "Command file name is not a valid slash command.",
41
+ });
42
+ continue;
43
+ }
44
+ try {
45
+ const parsed = parseCommandMarkdown(await this.options.vault.cachedRead(file), file.path, name);
46
+ collected.push({
47
+ name: parsed.name,
48
+ description: parsed.description,
49
+ argumentHint: parsed.argumentHint,
50
+ aliases: parsed.aliases,
51
+ source,
52
+ path: file.path,
53
+ kind: parsed.kind,
54
+ dispatch: commandFileDispatch(parsed),
55
+ });
56
+ }
57
+ catch (error) {
58
+ diagnostics.push({
59
+ path: file.path,
60
+ message: error instanceof CommandParseError
61
+ ? error.message
62
+ : "Command could not be parsed.",
63
+ });
64
+ }
65
+ }
66
+ }
67
+ async #collectUser(collected, diagnostics) {
68
+ const store = this.options.userAgents;
69
+ if (!store)
70
+ return;
71
+ for (const file of await store.list()) {
72
+ const path = store.root
73
+ ? `${store.root.replace(/\/$/u, "")}/${file.name}.md`
74
+ : `${file.name}.md`;
75
+ try {
76
+ const parsed = parseCommandMarkdown(file.content, path, file.name);
77
+ collected.push({
78
+ name: parsed.name,
79
+ description: parsed.description,
80
+ argumentHint: parsed.argumentHint,
81
+ aliases: parsed.aliases,
82
+ source: "user",
83
+ kind: parsed.kind,
84
+ dispatch: commandFileDispatch(parsed),
85
+ });
86
+ }
87
+ catch (error) {
88
+ diagnostics.push({
89
+ path,
90
+ message: error instanceof CommandParseError
91
+ ? error.message
92
+ : "Command could not be parsed.",
93
+ });
94
+ }
95
+ }
96
+ }
97
+ }
98
+ function mergeCommandFiles(collected, diagnostics) {
99
+ const reserved = reservedSlashCommandNames();
100
+ const overlays = [];
101
+ const prompts = new Map();
102
+ const seen = new Map();
103
+ const ranked = [...collected].sort((left, right) => SOURCE_RANK[right.source] - SOURCE_RANK[left.source]);
104
+ for (const command of ranked) {
105
+ const path = command.path ?? command.name;
106
+ if (reserved.has(command.name)) {
107
+ if (command.kind !== "host") {
108
+ diagnostics.push({
109
+ path,
110
+ message: `Reserved command name cannot be overridden: ${command.name}`,
111
+ });
112
+ continue;
113
+ }
114
+ const existingOverlay = overlays.find((item) => item.name === command.name);
115
+ if (!existingOverlay)
116
+ overlays.push(command);
117
+ continue;
118
+ }
119
+ if (command.kind === "host") {
120
+ diagnostics.push({
121
+ path,
122
+ message: `kind: host requires a reserved command name: ${command.name}`,
123
+ });
124
+ continue;
125
+ }
126
+ const level = seen.get(command.source) ?? new Set();
127
+ if (level.has(command.name)) {
128
+ diagnostics.push({
129
+ path,
130
+ message: `Duplicate command name at the same precedence: ${command.name}`,
131
+ });
132
+ prompts.delete(command.name);
133
+ continue;
134
+ }
135
+ level.add(command.name);
136
+ seen.set(command.source, level);
137
+ const existing = prompts.get(command.name);
138
+ if (existing && SOURCE_RANK[existing.source] >= SOURCE_RANK[command.source]) {
139
+ continue;
140
+ }
141
+ prompts.set(command.name, command);
142
+ }
143
+ return {
144
+ commands: [...prompts.values()].sort((left, right) => left.name.localeCompare(right.name)),
145
+ overlays,
146
+ diagnostics,
147
+ };
148
+ }
149
+ export function fileCommandSource(source) {
150
+ return source;
151
+ }