@alextis59/athena 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (201) hide show
  1. package/LICENSE +15 -0
  2. package/README.md +147 -0
  3. package/dist/agent/ask.d.ts +7 -0
  4. package/dist/agent/ask.d.ts.map +1 -0
  5. package/dist/agent/ask.js +35 -0
  6. package/dist/agent/ask.js.map +1 -0
  7. package/dist/agent/citations.d.ts +27 -0
  8. package/dist/agent/citations.d.ts.map +1 -0
  9. package/dist/agent/citations.js +190 -0
  10. package/dist/agent/citations.js.map +1 -0
  11. package/dist/agent/fake-provider.d.ts +9 -0
  12. package/dist/agent/fake-provider.d.ts.map +1 -0
  13. package/dist/agent/fake-provider.js +51 -0
  14. package/dist/agent/fake-provider.js.map +1 -0
  15. package/dist/agent/index.d.ts +10 -0
  16. package/dist/agent/index.d.ts.map +1 -0
  17. package/dist/agent/index.js +6 -0
  18. package/dist/agent/index.js.map +1 -0
  19. package/dist/agent/loop.d.ts +11 -0
  20. package/dist/agent/loop.d.ts.map +1 -0
  21. package/dist/agent/loop.js +156 -0
  22. package/dist/agent/loop.js.map +1 -0
  23. package/dist/agent/providers.d.ts +17 -0
  24. package/dist/agent/providers.d.ts.map +1 -0
  25. package/dist/agent/providers.js +171 -0
  26. package/dist/agent/providers.js.map +1 -0
  27. package/dist/agent/tools.d.ts +8 -0
  28. package/dist/agent/tools.d.ts.map +1 -0
  29. package/dist/agent/tools.js +233 -0
  30. package/dist/agent/tools.js.map +1 -0
  31. package/dist/agent/types.d.ts +84 -0
  32. package/dist/agent/types.d.ts.map +1 -0
  33. package/dist/agent/types.js +2 -0
  34. package/dist/agent/types.js.map +1 -0
  35. package/dist/cli/help.d.ts +2 -0
  36. package/dist/cli/help.d.ts.map +1 -0
  37. package/dist/cli/help.js +28 -0
  38. package/dist/cli/help.js.map +1 -0
  39. package/dist/cli/main.d.ts +3 -0
  40. package/dist/cli/main.d.ts.map +1 -0
  41. package/dist/cli/main.js +5 -0
  42. package/dist/cli/main.js.map +1 -0
  43. package/dist/cli/options.d.ts +21 -0
  44. package/dist/cli/options.d.ts.map +1 -0
  45. package/dist/cli/options.js +97 -0
  46. package/dist/cli/options.js.map +1 -0
  47. package/dist/cli/run.d.ts +21 -0
  48. package/dist/cli/run.d.ts.map +1 -0
  49. package/dist/cli/run.js +144 -0
  50. package/dist/cli/run.js.map +1 -0
  51. package/dist/config/index.d.ts +4 -0
  52. package/dist/config/index.d.ts.map +1 -0
  53. package/dist/config/index.js +3 -0
  54. package/dist/config/index.js.map +1 -0
  55. package/dist/config/loader.d.ts +14 -0
  56. package/dist/config/loader.d.ts.map +1 -0
  57. package/dist/config/loader.js +352 -0
  58. package/dist/config/loader.js.map +1 -0
  59. package/dist/config/schema.d.ts +6 -0
  60. package/dist/config/schema.d.ts.map +1 -0
  61. package/dist/config/schema.js +72 -0
  62. package/dist/config/schema.js.map +1 -0
  63. package/dist/config/types.d.ts +53 -0
  64. package/dist/config/types.d.ts.map +1 -0
  65. package/dist/config/types.js +2 -0
  66. package/dist/config/types.js.map +1 -0
  67. package/dist/doctor/doctor.d.ts +47 -0
  68. package/dist/doctor/doctor.d.ts.map +1 -0
  69. package/dist/doctor/doctor.js +159 -0
  70. package/dist/doctor/doctor.js.map +1 -0
  71. package/dist/embeddings/index.d.ts +117 -0
  72. package/dist/embeddings/index.d.ts.map +1 -0
  73. package/dist/embeddings/index.js +515 -0
  74. package/dist/embeddings/index.js.map +1 -0
  75. package/dist/errors.d.ts +19 -0
  76. package/dist/errors.d.ts.map +1 -0
  77. package/dist/errors.js +37 -0
  78. package/dist/errors.js.map +1 -0
  79. package/dist/index.d.ts +17 -0
  80. package/dist/index.d.ts.map +1 -0
  81. package/dist/index.js +9 -0
  82. package/dist/index.js.map +1 -0
  83. package/dist/indexer/discovery.d.ts +10 -0
  84. package/dist/indexer/discovery.d.ts.map +1 -0
  85. package/dist/indexer/discovery.js +38 -0
  86. package/dist/indexer/discovery.js.map +1 -0
  87. package/dist/indexer/hash.d.ts +3 -0
  88. package/dist/indexer/hash.d.ts.map +1 -0
  89. package/dist/indexer/hash.js +11 -0
  90. package/dist/indexer/hash.js.map +1 -0
  91. package/dist/indexer/index.d.ts +7 -0
  92. package/dist/indexer/index.d.ts.map +1 -0
  93. package/dist/indexer/index.js +5 -0
  94. package/dist/indexer/index.js.map +1 -0
  95. package/dist/indexer/kinds.d.ts +4 -0
  96. package/dist/indexer/kinds.d.ts.map +1 -0
  97. package/dist/indexer/kinds.js +38 -0
  98. package/dist/indexer/kinds.js.map +1 -0
  99. package/dist/indexer/run.d.ts +42 -0
  100. package/dist/indexer/run.d.ts.map +1 -0
  101. package/dist/indexer/run.js +563 -0
  102. package/dist/indexer/run.js.map +1 -0
  103. package/dist/logging/logger.d.ts +11 -0
  104. package/dist/logging/logger.d.ts.map +1 -0
  105. package/dist/logging/logger.js +18 -0
  106. package/dist/logging/logger.js.map +1 -0
  107. package/dist/mcp/index.d.ts +35 -0
  108. package/dist/mcp/index.d.ts.map +1 -0
  109. package/dist/mcp/index.js +392 -0
  110. package/dist/mcp/index.js.map +1 -0
  111. package/dist/parsers/chunking.d.ts +4 -0
  112. package/dist/parsers/chunking.d.ts.map +1 -0
  113. package/dist/parsers/chunking.js +112 -0
  114. package/dist/parsers/chunking.js.map +1 -0
  115. package/dist/parsers/index.d.ts +8 -0
  116. package/dist/parsers/index.d.ts.map +1 -0
  117. package/dist/parsers/index.js +5 -0
  118. package/dist/parsers/index.js.map +1 -0
  119. package/dist/parsers/line-parser.d.ts +13 -0
  120. package/dist/parsers/line-parser.d.ts.map +1 -0
  121. package/dist/parsers/line-parser.js +242 -0
  122. package/dist/parsers/line-parser.js.map +1 -0
  123. package/dist/parsers/normalize.d.ts +4 -0
  124. package/dist/parsers/normalize.d.ts.map +1 -0
  125. package/dist/parsers/normalize.js +17 -0
  126. package/dist/parsers/normalize.js.map +1 -0
  127. package/dist/parsers/parser.d.ts +14 -0
  128. package/dist/parsers/parser.d.ts.map +1 -0
  129. package/dist/parsers/parser.js +77 -0
  130. package/dist/parsers/parser.js.map +1 -0
  131. package/dist/parsers/pdf2llm.d.ts +14 -0
  132. package/dist/parsers/pdf2llm.d.ts.map +1 -0
  133. package/dist/parsers/pdf2llm.js +215 -0
  134. package/dist/parsers/pdf2llm.js.map +1 -0
  135. package/dist/parsers/types.d.ts +48 -0
  136. package/dist/parsers/types.d.ts.map +1 -0
  137. package/dist/parsers/types.js +2 -0
  138. package/dist/parsers/types.js.map +1 -0
  139. package/dist/retrieval/index.d.ts +4 -0
  140. package/dist/retrieval/index.d.ts.map +1 -0
  141. package/dist/retrieval/index.js +3 -0
  142. package/dist/retrieval/index.js.map +1 -0
  143. package/dist/retrieval/schemas.d.ts +127 -0
  144. package/dist/retrieval/schemas.d.ts.map +1 -0
  145. package/dist/retrieval/schemas.js +65 -0
  146. package/dist/retrieval/schemas.js.map +1 -0
  147. package/dist/retrieval/tools.d.ts +31 -0
  148. package/dist/retrieval/tools.d.ts.map +1 -0
  149. package/dist/retrieval/tools.js +803 -0
  150. package/dist/retrieval/tools.js.map +1 -0
  151. package/dist/retrieval/types.d.ts +104 -0
  152. package/dist/retrieval/types.d.ts.map +1 -0
  153. package/dist/retrieval/types.js +2 -0
  154. package/dist/retrieval/types.js.map +1 -0
  155. package/dist/server/index.d.ts +3 -0
  156. package/dist/server/index.d.ts.map +1 -0
  157. package/dist/server/index.js +2 -0
  158. package/dist/server/index.js.map +1 -0
  159. package/dist/server/server.d.ts +49 -0
  160. package/dist/server/server.d.ts.map +1 -0
  161. package/dist/server/server.js +517 -0
  162. package/dist/server/server.js.map +1 -0
  163. package/dist/shared/types.d.ts +98 -0
  164. package/dist/shared/types.d.ts.map +1 -0
  165. package/dist/shared/types.js +2 -0
  166. package/dist/shared/types.js.map +1 -0
  167. package/dist/storage/database.d.ts +18 -0
  168. package/dist/storage/database.d.ts.map +1 -0
  169. package/dist/storage/database.js +66 -0
  170. package/dist/storage/database.js.map +1 -0
  171. package/dist/storage/index.d.ts +8 -0
  172. package/dist/storage/index.d.ts.map +1 -0
  173. package/dist/storage/index.js +5 -0
  174. package/dist/storage/index.js.map +1 -0
  175. package/dist/storage/migrations.d.ts +15 -0
  176. package/dist/storage/migrations.d.ts.map +1 -0
  177. package/dist/storage/migrations.js +170 -0
  178. package/dist/storage/migrations.js.map +1 -0
  179. package/dist/storage/paths.d.ts +15 -0
  180. package/dist/storage/paths.d.ts.map +1 -0
  181. package/dist/storage/paths.js +27 -0
  182. package/dist/storage/paths.js.map +1 -0
  183. package/dist/storage/schema.d.ts +11 -0
  184. package/dist/storage/schema.d.ts.map +1 -0
  185. package/dist/storage/schema.js +40 -0
  186. package/dist/storage/schema.js.map +1 -0
  187. package/dist/ui/index.d.ts +2 -0
  188. package/dist/ui/index.d.ts.map +1 -0
  189. package/dist/ui/index.js +2 -0
  190. package/dist/ui/index.js.map +1 -0
  191. package/dist/ui/static-app.d.ts +2 -0
  192. package/dist/ui/static-app.d.ts.map +1 -0
  193. package/dist/ui/static-app.js +735 -0
  194. package/dist/ui/static-app.js.map +1 -0
  195. package/docs/adr/0001-sqlite-driver.md +23 -0
  196. package/docs/agentic-rag-specifications.md +659 -0
  197. package/docs/agentic-rag-study.md +1056 -0
  198. package/docs/implementation-plan.md +1425 -0
  199. package/docs/mcp-integration.md +154 -0
  200. package/docs/validation-corpus.md +71 -0
  201. package/package.json +79 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"static-app.js","sourceRoot":"","sources":["../../src/ui/static-app.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,aAAa;IAC3B,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QA2tBD,CAAC;AACT,CAAC"}
@@ -0,0 +1,23 @@
1
+ # ADR 0001: SQLite Driver
2
+
3
+ Date: 2026-07-03
4
+
5
+ ## Decision
6
+
7
+ Athena uses `better-sqlite3` for the initial local SQLite storage layer.
8
+
9
+ ## Rationale
10
+
11
+ - Athena's index updates and retrieval reads are local, short-lived CLI/server
12
+ operations where synchronous transactions are simple and deterministic.
13
+ - `better-sqlite3` provides mature SQLite and FTS5 support without the
14
+ `node:sqlite` ExperimentalWarning emitted by the current Node 22 runtime.
15
+ - The storage boundary is isolated in `src/storage/`, so a future migration to a
16
+ different SQLite binding or vector extension can be handled behind the same
17
+ migration and transaction helpers.
18
+
19
+ ## Consequences
20
+
21
+ - `better-sqlite3` is a runtime dependency.
22
+ - Tests can inspect schema, indexes, and rollback behavior without a separate
23
+ database service.
@@ -0,0 +1,659 @@
1
+ # Agentic RAG Tool Specifications
2
+
3
+ Date: 2026-07-03
4
+
5
+ Related study: [Agentic RAG Tool Design Study](./agentic-rag-study.md)
6
+
7
+ ## Purpose
8
+
9
+ Athena is a local-first documentation RAG tool. It indexes documentation in the
10
+ current folder, launches a local chat UI, and answers questions with exact
11
+ citations into the original sources.
12
+
13
+ This specification converts the study into concrete product and engineering
14
+ requirements.
15
+
16
+ ## Naming
17
+
18
+ - Project name: `athena`.
19
+ - npm package name: `@alextis59/athena`.
20
+ - CLI binary/command name: `athena`.
21
+ - Package-run form: `npx @alextis59/athena`.
22
+ - Local cache directory: `.athena/`.
23
+
24
+ All product documentation and command examples should treat `athena` as the
25
+ canonical project and command name. `npx @alextis59/athena` is only the
26
+ no-install npm invocation form.
27
+
28
+ ## Final Architecture Decisions
29
+
30
+ 1. Athena owns the agentic loop.
31
+ No agent SDK handles tool selection, tool execution, step control, memory
32
+ policy, streaming, or citation validation.
33
+
34
+ 2. `pdf-2-llm` is the canonical PDF parser.
35
+ PDF ingestion uses the published npm package `pdf-2-llm`, not a local
36
+ workspace-only dependency.
37
+
38
+ 3. Local-first is the default.
39
+ Indexes, embeddings, and chat UI should run locally unless the user
40
+ explicitly opts into remote model or embedding providers.
41
+
42
+ 4. Citations are first-class data.
43
+ Every substantive answer must be grounded in source ranges or PDF page
44
+ regions that the UI can open.
45
+
46
+ 5. External agents are integrations.
47
+ OpenCode, MCP clients, LangChain, LlamaIndex, Vercel AI SDK, and similar
48
+ tools may integrate with Athena, but none of them should own Athena's core
49
+ agent behavior.
50
+
51
+ ## Functional Requirements
52
+
53
+ ### FR-1: Single Command Startup
54
+
55
+ Athena must support a command that can be run inside any documentation folder:
56
+
57
+ ```bash
58
+ athena
59
+ ```
60
+
61
+ For ad hoc execution without installing the binary first, users may run
62
+ `npx @alextis59/athena`.
63
+
64
+ The command must:
65
+
66
+ - discover supported documentation files;
67
+ - create or update a local `.athena/index.sqlite` cache;
68
+ - skip unchanged files;
69
+ - start a local web UI;
70
+ - expose a chat interface connected to the local index.
71
+
72
+ ### FR-2: Index-Only Mode
73
+
74
+ Athena must support indexing without starting the UI:
75
+
76
+ ```bash
77
+ athena index
78
+ ```
79
+
80
+ This command must exit non-zero when parsing, database migration, or embedding
81
+ generation fails.
82
+
83
+ ### FR-3: Serve Existing Index
84
+
85
+ Athena must support serving a previously built index:
86
+
87
+ ```bash
88
+ athena serve
89
+ ```
90
+
91
+ If the index is missing, the command should either fail with a clear message or
92
+ offer an explicit `--build` mode.
93
+
94
+ ### FR-4: One-Shot Ask Mode
95
+
96
+ Athena should support terminal questions:
97
+
98
+ ```bash
99
+ athena ask "How do I configure OAuth?"
100
+ ```
101
+
102
+ The answer must include citations in terminal-readable form.
103
+
104
+ ### FR-5: Doctor Mode
105
+
106
+ Athena should expose a diagnostic command:
107
+
108
+ ```bash
109
+ athena doctor
110
+ ```
111
+
112
+ It should report:
113
+
114
+ - database status;
115
+ - schema version;
116
+ - indexed source count;
117
+ - embedding provider status;
118
+ - `pdf-2-llm` package availability;
119
+ - parser warnings summary;
120
+ - model cache status;
121
+ - UI port availability.
122
+
123
+ ### FR-6: MCP Server Mode
124
+
125
+ Athena should expose an optional MCP server:
126
+
127
+ ```bash
128
+ athena mcp
129
+ ```
130
+
131
+ The MCP server must expose citation-aware retrieval tools only. It must not
132
+ allow external agents to bypass Athena's source validation.
133
+
134
+ ## Supported Inputs
135
+
136
+ ### Initial File Types
137
+
138
+ Athena must support:
139
+
140
+ - Markdown: `.md`, `.mdx`;
141
+ - text: `.txt`;
142
+ - reStructuredText: `.rst`;
143
+ - AsciiDoc: `.adoc`;
144
+ - HTML: `.html`, `.htm`;
145
+ - PDF: `.pdf`;
146
+ - extensionless documentation files such as `README`, `CHANGELOG`,
147
+ `CONTRIBUTING`, and `LICENSE`.
148
+
149
+ ### Default Exclusions
150
+
151
+ Athena must exclude:
152
+
153
+ - `.git/**`;
154
+ - `node_modules/**`;
155
+ - `.athena/**`;
156
+ - `dist/**`;
157
+ - `build/**`;
158
+ - `coverage/**`;
159
+ - binary files not explicitly supported.
160
+
161
+ ## Configuration
162
+
163
+ Athena should load optional config from:
164
+
165
+ - `athena.config.ts`;
166
+ - `athena.config.mjs`;
167
+ - `athena.config.json`.
168
+
169
+ Example:
170
+
171
+ ```ts
172
+ import { defineConfig } from "@alextis59/athena";
173
+
174
+ export default defineConfig({
175
+ index: {
176
+ include: ["**/*.md", "**/*.mdx", "**/*.pdf", "**/*.html"],
177
+ exclude: ["node_modules/**", "dist/**", ".git/**"]
178
+ },
179
+ embeddings: {
180
+ provider: "transformers-js",
181
+ model: "mixedbread-ai/mxbai-embed-xsmall-v1",
182
+ dimensions: 384,
183
+ pooling: "mean",
184
+ normalize: true
185
+ },
186
+ pdf: {
187
+ parser: "pdf-2-llm",
188
+ pageAnchors: true,
189
+ csvSidecars: true,
190
+ timeoutMs: 30_000,
191
+ minConfidenceForAutoCitation: 0.65
192
+ },
193
+ server: {
194
+ host: "127.0.0.1",
195
+ port: 4317
196
+ }
197
+ });
198
+ ```
199
+
200
+ ## PDF Ingestion Specification
201
+
202
+ ### Package
203
+
204
+ Athena must use the public npm package:
205
+
206
+ ```bash
207
+ npm install pdf-2-llm
208
+ ```
209
+
210
+ The Node parser import should use:
211
+
212
+ ```ts
213
+ import { convertPdfToMarkdown } from "pdf-2-llm/node";
214
+ ```
215
+
216
+ ### PDF Parser Output
217
+
218
+ Athena must preserve these `pdf-2-llm` outputs when available:
219
+
220
+ - Markdown;
221
+ - source-map entries;
222
+ - page regions;
223
+ - document IR;
224
+ - table sidecars;
225
+ - extracted assets;
226
+ - warning codes;
227
+ - diagnostics;
228
+ - confidence scores.
229
+
230
+ ### PDF Storage Requirements
231
+
232
+ For every parsed PDF, Athena must store:
233
+
234
+ - original file path;
235
+ - source hash;
236
+ - parser package version;
237
+ - parser options hash;
238
+ - generated Markdown;
239
+ - source-map metadata;
240
+ - document IR metadata;
241
+ - warning summary;
242
+ - confidence summary;
243
+ - sidecar asset references.
244
+
245
+ ### PDF Citation Requirements
246
+
247
+ PDF citations must include:
248
+
249
+ - source file path;
250
+ - page number;
251
+ - page region coordinates when available;
252
+ - generated Markdown offset or line range;
253
+ - confidence score when available;
254
+ - warning codes that affect the cited region.
255
+
256
+ Example:
257
+
258
+ ```json
259
+ {
260
+ "kind": "pdf",
261
+ "path": "manual.pdf",
262
+ "page": 17,
263
+ "regions": [
264
+ { "x": 72, "y": 144, "width": 440, "height": 88 }
265
+ ],
266
+ "markdownOffsetStart": 1204,
267
+ "markdownOffsetEnd": 1422,
268
+ "confidence": 0.91,
269
+ "warningCodes": []
270
+ }
271
+ ```
272
+
273
+ ### PDF Warning Behavior
274
+
275
+ Athena must not silently hide extraction uncertainty.
276
+
277
+ If a cited PDF region has warnings or low confidence, the UI should display that
278
+ state near the citation.
279
+
280
+ If confidence is below the configured auto-citation threshold, Athena should
281
+ either avoid using that chunk as evidence or mark the citation as uncertain.
282
+
283
+ ## Agent Loop Specification
284
+
285
+ ### Ownership
286
+
287
+ Athena must implement its own agent loop. No SDK should own the loop.
288
+
289
+ The loop must be responsible for:
290
+
291
+ - building model messages;
292
+ - exposing tool schemas;
293
+ - receiving tool calls;
294
+ - validating tool call inputs;
295
+ - executing tools;
296
+ - appending tool results;
297
+ - enforcing max steps;
298
+ - streaming partial output;
299
+ - compacting conversation state;
300
+ - validating final citations.
301
+
302
+ ### Model Provider Interface
303
+
304
+ Model providers must be adapters behind a small interface:
305
+
306
+ ```ts
307
+ export interface ModelProvider {
308
+ streamResponse(input: {
309
+ model: string;
310
+ messages: ModelMessage[];
311
+ tools: ToolDef[];
312
+ temperature?: number;
313
+ }): AsyncIterable<ModelEvent>;
314
+ }
315
+ ```
316
+
317
+ Provider adapters may use vendor SDKs internally if useful, but those SDKs must
318
+ not own Athena's agent loop.
319
+
320
+ ### Step Limit
321
+
322
+ The default max step count should be 6.
323
+
324
+ If the model exceeds the limit, Athena must stop the turn and return a clear
325
+ message that the answer could not be completed within the tool budget.
326
+
327
+ ### Citation Validation
328
+
329
+ Before finalizing an answer, Athena must validate that cited claims reference
330
+ retrieved source ranges or PDF regions.
331
+
332
+ The validator must reject:
333
+
334
+ - citations pointing to unknown paths;
335
+ - line ranges outside file bounds;
336
+ - PDF page numbers outside the document;
337
+ - PDF regions missing source-map backing when source-map backing is required;
338
+ - citations to chunks that were not retrieved or read during the turn.
339
+
340
+ ## Agent Tools
341
+
342
+ ### `searchDocs`
343
+
344
+ Hybrid search over indexed documentation.
345
+
346
+ Input:
347
+
348
+ ```json
349
+ {
350
+ "query": "string",
351
+ "filters": {
352
+ "pathPrefix": "string",
353
+ "kind": "markdown | pdf | html | text"
354
+ },
355
+ "limit": 10
356
+ }
357
+ ```
358
+
359
+ Output:
360
+
361
+ ```json
362
+ {
363
+ "results": [
364
+ {
365
+ "chunkId": 123,
366
+ "path": "docs/api.md",
367
+ "headingPath": "Authentication > Tokens",
368
+ "snippet": "...",
369
+ "score": 0.82,
370
+ "locator": { "startLine": 10, "endLine": 22 }
371
+ }
372
+ ]
373
+ }
374
+ ```
375
+
376
+ ### `readSourceRange`
377
+
378
+ Read an exact text-like source range.
379
+
380
+ Input:
381
+
382
+ ```json
383
+ {
384
+ "path": "docs/api.md",
385
+ "startLine": 10,
386
+ "endLine": 40
387
+ }
388
+ ```
389
+
390
+ ### `readPdfSource`
391
+
392
+ Read converted PDF content and source-map metadata.
393
+
394
+ Input:
395
+
396
+ ```json
397
+ {
398
+ "path": "manual.pdf",
399
+ "page": 17,
400
+ "regionId": "..."
401
+ }
402
+ ```
403
+
404
+ ### `grepDocs`
405
+
406
+ Exact string or regex search.
407
+
408
+ Input:
409
+
410
+ ```json
411
+ {
412
+ "pattern": "AUTH_TOKEN",
413
+ "caseSensitive": false,
414
+ "limit": 20
415
+ }
416
+ ```
417
+
418
+ ### `listDocs`
419
+
420
+ Inspect indexed sources.
421
+
422
+ Input:
423
+
424
+ ```json
425
+ {
426
+ "pathPrefix": "docs/",
427
+ "kind": "pdf"
428
+ }
429
+ ```
430
+
431
+ ### `relatedChunks`
432
+
433
+ Fetch neighboring chunks.
434
+
435
+ Input:
436
+
437
+ ```json
438
+ {
439
+ "chunkId": 123,
440
+ "before": 1,
441
+ "after": 2
442
+ }
443
+ ```
444
+
445
+ ## Retrieval Specification
446
+
447
+ Athena must support hybrid retrieval:
448
+
449
+ 1. lexical search with SQLite FTS5;
450
+ 2. semantic vector search;
451
+ 3. exact grep for identifiers;
452
+ 4. result merging and deduplication;
453
+ 5. optional reranking;
454
+ 6. neighboring context expansion;
455
+ 7. warning/confidence-aware PDF scoring.
456
+
457
+ Ranking should boost:
458
+
459
+ - exact identifier matches;
460
+ - heading matches;
461
+ - source title matches;
462
+ - recently read related chunks;
463
+ - high-confidence PDF regions.
464
+
465
+ Ranking should penalize:
466
+
467
+ - low-confidence PDF regions;
468
+ - chunks with parser warnings;
469
+ - overly large generic chunks;
470
+ - stale chunks from superseded parser/index versions.
471
+
472
+ ## Embeddings Specification
473
+
474
+ ### Default Provider
475
+
476
+ The default embedding provider should be local:
477
+
478
+ ```yaml
479
+ provider: transformers-js
480
+ model: mixedbread-ai/mxbai-embed-xsmall-v1
481
+ dimensions: 384
482
+ pooling: mean
483
+ normalize: true
484
+ ```
485
+
486
+ ### Optional Providers
487
+
488
+ Athena should support provider adapters for:
489
+
490
+ - Ollama;
491
+ - OpenAI;
492
+ - Voyage;
493
+ - Cohere;
494
+ - Gemini.
495
+
496
+ Remote providers must require explicit user opt-in before document text is sent
497
+ over the network.
498
+
499
+ ### Cache Keys
500
+
501
+ Embedding cache keys must include:
502
+
503
+ - chunk text hash;
504
+ - provider name;
505
+ - model name;
506
+ - dimensions;
507
+ - pooling;
508
+ - normalization;
509
+ - quantization settings;
510
+ - model revision or digest when available;
511
+ - chunking strategy version.
512
+
513
+ ## Storage Specification
514
+
515
+ Athena must store index state in `.athena/index.sqlite`.
516
+
517
+ Required tables:
518
+
519
+ - `sources`;
520
+ - `documents`;
521
+ - `chunks`;
522
+ - `chunks_fts`;
523
+ - `embeddings`;
524
+ - `runs`;
525
+ - `parser_artifacts`;
526
+ - `conversation_turns`;
527
+ - `answer_citations`.
528
+
529
+ The schema must be migration-based and versioned.
530
+
531
+ Source records must include:
532
+
533
+ - path;
534
+ - file kind;
535
+ - hash;
536
+ - file size;
537
+ - mtime;
538
+ - parser name;
539
+ - parser version;
540
+ - parser options hash;
541
+ - indexed timestamp.
542
+
543
+ Chunk records must include:
544
+
545
+ - source ID;
546
+ - document ID;
547
+ - ordinal;
548
+ - heading path;
549
+ - text;
550
+ - normalized text;
551
+ - token estimate;
552
+ - locator JSON;
553
+ - extraction confidence;
554
+ - warning codes.
555
+
556
+ ## Chat UI Specification
557
+
558
+ The local UI must include:
559
+
560
+ - chat panel;
561
+ - source citation list;
562
+ - source preview panel;
563
+ - retrieval trace drawer;
564
+ - index status indicator;
565
+ - parser warning badges;
566
+ - source filter controls.
567
+
568
+ Citation interactions:
569
+
570
+ - text citations open exact line ranges;
571
+ - PDF citations open generated Markdown and PDF page-region metadata;
572
+ - uncertain citations show warning/confidence state;
573
+ - copied answers preserve citation labels.
574
+
575
+ ## Follow-Up Memory Specification
576
+
577
+ Athena should persist compact conversation memory:
578
+
579
+ - user question;
580
+ - final answer summary;
581
+ - citations;
582
+ - timestamp;
583
+ - optional user-pinned sources.
584
+
585
+ Athena should not replay full intermediate tool traces into future model context
586
+ unless the user asks for debug/audit details.
587
+
588
+ ## Security and Privacy
589
+
590
+ Defaults:
591
+
592
+ - bind the UI to `127.0.0.1`;
593
+ - store indexes locally;
594
+ - use local embeddings;
595
+ - avoid remote document upload;
596
+ - avoid automatic external agent execution.
597
+
598
+ Remote model or embedding providers must require explicit configuration.
599
+
600
+ MCP mode must be read-only by default.
601
+
602
+ ## MVP Acceptance Criteria
603
+
604
+ ### MVP-1: Markdown RAG
605
+
606
+ Done when:
607
+
608
+ - `athena index` indexes Markdown files;
609
+ - unchanged files are skipped on the second run;
610
+ - `athena serve` starts a local UI;
611
+ - a question can be answered from Markdown;
612
+ - answer citations open exact line ranges.
613
+
614
+ ### MVP-2: Local Embeddings
615
+
616
+ Done when:
617
+
618
+ - default local embeddings are generated;
619
+ - vectors are cached;
620
+ - lexical and vector search are merged;
621
+ - changing the embedding model invalidates only affected vectors.
622
+
623
+ ### MVP-3: PDF RAG
624
+
625
+ Done when:
626
+
627
+ - `pdf-2-llm` is installed from npm;
628
+ - PDFs are converted through `pdf-2-llm/node`;
629
+ - generated Markdown and source maps are stored;
630
+ - PDF citations include page and region metadata;
631
+ - low-confidence regions are visible in the UI.
632
+
633
+ ### MVP-4: Agent Loop
634
+
635
+ Done when:
636
+
637
+ - the custom loop can call tools for up to a configured max step count;
638
+ - no agent SDK controls tool execution;
639
+ - invalid tool inputs are rejected;
640
+ - final answer citation validation runs every turn;
641
+ - unsupported answers are refused or marked as unsupported.
642
+
643
+ ### MVP-5: Evaluation Harness
644
+
645
+ Done when:
646
+
647
+ - parser/index tests run locally;
648
+ - retrieval tests compare FTS-only, vector-only, and hybrid results;
649
+ - answer tests validate citation requirements;
650
+ - PDF source-map tests verify page and region locators.
651
+
652
+ ## Open Questions
653
+
654
+ - Which model provider should be the first chat-generation adapter?
655
+ - Should the initial UI be a small custom app or a minimal server-rendered page?
656
+ - Should vector search use `sqlite-vec` immediately or begin with a simple
657
+ in-process vector scan for MVP scale?
658
+ - What exact `pdf-2-llm` output fields should be considered stable public API?
659
+ - Should MCP mode launch from the same index server or as a separate process?