@pella-labs/pinakes 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 (174) hide show
  1. package/README.md +208 -0
  2. package/dist/cli/audit.d.ts +30 -0
  3. package/dist/cli/audit.d.ts.map +1 -0
  4. package/dist/cli/audit.js +49 -0
  5. package/dist/cli/audit.js.map +1 -0
  6. package/dist/cli/export.d.ts +32 -0
  7. package/dist/cli/export.d.ts.map +1 -0
  8. package/dist/cli/export.js +73 -0
  9. package/dist/cli/export.js.map +1 -0
  10. package/dist/cli/import.d.ts +24 -0
  11. package/dist/cli/import.d.ts.map +1 -0
  12. package/dist/cli/import.js +96 -0
  13. package/dist/cli/import.js.map +1 -0
  14. package/dist/cli/index.d.ts +3 -0
  15. package/dist/cli/index.d.ts.map +1 -0
  16. package/dist/cli/index.js +172 -0
  17. package/dist/cli/index.js.map +1 -0
  18. package/dist/cli/purge.d.ts +23 -0
  19. package/dist/cli/purge.d.ts.map +1 -0
  20. package/dist/cli/purge.js +57 -0
  21. package/dist/cli/purge.js.map +1 -0
  22. package/dist/cli/rebuild.d.ts +54 -0
  23. package/dist/cli/rebuild.d.ts.map +1 -0
  24. package/dist/cli/rebuild.js +113 -0
  25. package/dist/cli/rebuild.js.map +1 -0
  26. package/dist/cli/serve.d.ts +49 -0
  27. package/dist/cli/serve.d.ts.map +1 -0
  28. package/dist/cli/serve.js +296 -0
  29. package/dist/cli/serve.js.map +1 -0
  30. package/dist/cli/status.d.ts +39 -0
  31. package/dist/cli/status.d.ts.map +1 -0
  32. package/dist/cli/status.js +108 -0
  33. package/dist/cli/status.js.map +1 -0
  34. package/dist/db/client.d.ts +109 -0
  35. package/dist/db/client.d.ts.map +1 -0
  36. package/dist/db/client.js +175 -0
  37. package/dist/db/client.js.map +1 -0
  38. package/dist/db/repository.d.ts +82 -0
  39. package/dist/db/repository.d.ts.map +1 -0
  40. package/dist/db/repository.js +173 -0
  41. package/dist/db/repository.js.map +1 -0
  42. package/dist/db/schema.d.ts +990 -0
  43. package/dist/db/schema.d.ts.map +1 -0
  44. package/dist/db/schema.js +259 -0
  45. package/dist/db/schema.js.map +1 -0
  46. package/dist/db/types.d.ts +28 -0
  47. package/dist/db/types.d.ts.map +1 -0
  48. package/dist/db/types.js +11 -0
  49. package/dist/db/types.js.map +1 -0
  50. package/dist/gaps/detector.d.ts +67 -0
  51. package/dist/gaps/detector.d.ts.map +1 -0
  52. package/dist/gaps/detector.js +160 -0
  53. package/dist/gaps/detector.js.map +1 -0
  54. package/dist/gate/budget.d.ts +90 -0
  55. package/dist/gate/budget.d.ts.map +1 -0
  56. package/dist/gate/budget.js +145 -0
  57. package/dist/gate/budget.js.map +1 -0
  58. package/dist/ingest/chokidar.d.ts +33 -0
  59. package/dist/ingest/chokidar.d.ts.map +1 -0
  60. package/dist/ingest/chokidar.js +152 -0
  61. package/dist/ingest/chokidar.js.map +1 -0
  62. package/dist/ingest/ingester.d.ts +117 -0
  63. package/dist/ingest/ingester.d.ts.map +1 -0
  64. package/dist/ingest/ingester.js +312 -0
  65. package/dist/ingest/ingester.js.map +1 -0
  66. package/dist/ingest/manifest.d.ts +87 -0
  67. package/dist/ingest/manifest.d.ts.map +1 -0
  68. package/dist/ingest/manifest.js +223 -0
  69. package/dist/ingest/manifest.js.map +1 -0
  70. package/dist/ingest/memory-store.d.ts +55 -0
  71. package/dist/ingest/memory-store.d.ts.map +1 -0
  72. package/dist/ingest/memory-store.js +94 -0
  73. package/dist/ingest/memory-store.js.map +1 -0
  74. package/dist/ingest/parse/chunk.d.ts +15 -0
  75. package/dist/ingest/parse/chunk.d.ts.map +1 -0
  76. package/dist/ingest/parse/chunk.js +88 -0
  77. package/dist/ingest/parse/chunk.js.map +1 -0
  78. package/dist/ingest/parse/markdown.d.ts +64 -0
  79. package/dist/ingest/parse/markdown.d.ts.map +1 -0
  80. package/dist/ingest/parse/markdown.js +152 -0
  81. package/dist/ingest/parse/markdown.js.map +1 -0
  82. package/dist/ingest/queue.d.ts +21 -0
  83. package/dist/ingest/queue.d.ts.map +1 -0
  84. package/dist/ingest/queue.js +24 -0
  85. package/dist/ingest/queue.js.map +1 -0
  86. package/dist/ingest/source.d.ts +42 -0
  87. package/dist/ingest/source.d.ts.map +1 -0
  88. package/dist/ingest/source.js +19 -0
  89. package/dist/ingest/source.js.map +1 -0
  90. package/dist/mcp/envelope.d.ts +73 -0
  91. package/dist/mcp/envelope.d.ts.map +1 -0
  92. package/dist/mcp/envelope.js +46 -0
  93. package/dist/mcp/envelope.js.map +1 -0
  94. package/dist/mcp/tools/execute.d.ts +55 -0
  95. package/dist/mcp/tools/execute.d.ts.map +1 -0
  96. package/dist/mcp/tools/execute.js +232 -0
  97. package/dist/mcp/tools/execute.js.map +1 -0
  98. package/dist/mcp/tools/search.d.ts +53 -0
  99. package/dist/mcp/tools/search.d.ts.map +1 -0
  100. package/dist/mcp/tools/search.js +114 -0
  101. package/dist/mcp/tools/search.js.map +1 -0
  102. package/dist/observability/audit.d.ts +25 -0
  103. package/dist/observability/audit.d.ts.map +1 -0
  104. package/dist/observability/audit.js +38 -0
  105. package/dist/observability/audit.js.map +1 -0
  106. package/dist/observability/logger.d.ts +4 -0
  107. package/dist/observability/logger.d.ts.map +1 -0
  108. package/dist/observability/logger.js +56 -0
  109. package/dist/observability/logger.js.map +1 -0
  110. package/dist/observability/metrics.d.ts +38 -0
  111. package/dist/observability/metrics.d.ts.map +1 -0
  112. package/dist/observability/metrics.js +64 -0
  113. package/dist/observability/metrics.js.map +1 -0
  114. package/dist/retrieval/embedder.d.ts +130 -0
  115. package/dist/retrieval/embedder.d.ts.map +1 -0
  116. package/dist/retrieval/embedder.js +278 -0
  117. package/dist/retrieval/embedder.js.map +1 -0
  118. package/dist/retrieval/fts.d.ts +42 -0
  119. package/dist/retrieval/fts.d.ts.map +1 -0
  120. package/dist/retrieval/fts.js +46 -0
  121. package/dist/retrieval/fts.js.map +1 -0
  122. package/dist/retrieval/hybrid.d.ts +43 -0
  123. package/dist/retrieval/hybrid.d.ts.map +1 -0
  124. package/dist/retrieval/hybrid.js +120 -0
  125. package/dist/retrieval/hybrid.js.map +1 -0
  126. package/dist/retrieval/vec.d.ts +39 -0
  127. package/dist/retrieval/vec.d.ts.map +1 -0
  128. package/dist/retrieval/vec.js +50 -0
  129. package/dist/retrieval/vec.js.map +1 -0
  130. package/dist/sandbox/bindings/budget.d.ts +10 -0
  131. package/dist/sandbox/bindings/budget.d.ts.map +1 -0
  132. package/dist/sandbox/bindings/budget.js +44 -0
  133. package/dist/sandbox/bindings/budget.js.map +1 -0
  134. package/dist/sandbox/bindings/install.d.ts +23 -0
  135. package/dist/sandbox/bindings/install.d.ts.map +1 -0
  136. package/dist/sandbox/bindings/install.js +15 -0
  137. package/dist/sandbox/bindings/install.js.map +1 -0
  138. package/dist/sandbox/bindings/kg.d.ts +29 -0
  139. package/dist/sandbox/bindings/kg.d.ts.map +1 -0
  140. package/dist/sandbox/bindings/kg.js +323 -0
  141. package/dist/sandbox/bindings/kg.js.map +1 -0
  142. package/dist/sandbox/bindings/logger.d.ts +11 -0
  143. package/dist/sandbox/bindings/logger.d.ts.map +1 -0
  144. package/dist/sandbox/bindings/logger.js +33 -0
  145. package/dist/sandbox/bindings/logger.js.map +1 -0
  146. package/dist/sandbox/bindings/write.d.ts +34 -0
  147. package/dist/sandbox/bindings/write.d.ts.map +1 -0
  148. package/dist/sandbox/bindings/write.js +195 -0
  149. package/dist/sandbox/bindings/write.js.map +1 -0
  150. package/dist/sandbox/executor.d.ts +68 -0
  151. package/dist/sandbox/executor.d.ts.map +1 -0
  152. package/dist/sandbox/executor.js +280 -0
  153. package/dist/sandbox/executor.js.map +1 -0
  154. package/dist/sandbox/helpers.d.ts +26 -0
  155. package/dist/sandbox/helpers.d.ts.map +1 -0
  156. package/dist/sandbox/helpers.js +131 -0
  157. package/dist/sandbox/helpers.js.map +1 -0
  158. package/dist/sandbox/pool.d.ts +63 -0
  159. package/dist/sandbox/pool.d.ts.map +1 -0
  160. package/dist/sandbox/pool.js +98 -0
  161. package/dist/sandbox/pool.js.map +1 -0
  162. package/dist/sandbox/vendored-codemode.d.ts +99 -0
  163. package/dist/sandbox/vendored-codemode.d.ts.map +1 -0
  164. package/dist/sandbox/vendored-codemode.js +471 -0
  165. package/dist/sandbox/vendored-codemode.js.map +1 -0
  166. package/dist/server.d.ts +3 -0
  167. package/dist/server.d.ts.map +1 -0
  168. package/dist/server.js +74 -0
  169. package/dist/server.js.map +1 -0
  170. package/dist/spike.d.ts +15 -0
  171. package/dist/spike.d.ts.map +1 -0
  172. package/dist/spike.js +90 -0
  173. package/dist/spike.js.map +1 -0
  174. package/package.json +60 -0
@@ -0,0 +1,73 @@
1
+ /**
2
+ * MCP tool response envelope.
3
+ *
4
+ * CLAUDE.md §API Rules #5 — the envelope is immutable. Adding a field
5
+ * requires updating this file and regenerating the tool schemas. The LLM
6
+ * consumer parses this shape, so drift here breaks prompts in the wild.
7
+ */
8
+ export type Scope = 'project' | 'personal' | 'both';
9
+ export interface EnvelopeMeta {
10
+ /** User-requested `max_tokens` (default 5000, max 20000). */
11
+ tokens_budgeted: number;
12
+ /** Actual token count of the final serialized response body. */
13
+ tokens_used: number;
14
+ /** True if the budget gate truncated results or emitted a sentinel. */
15
+ results_truncated: boolean;
16
+ /** Scope this call ran against. */
17
+ scope: Scope;
18
+ /** Wall-clock time from tool entry to envelope build. */
19
+ query_time_ms: number;
20
+ /**
21
+ * Files whose on-disk `source_sha` no longer matches the indexed copy.
22
+ * Always empty in Phase 1 (in-memory store, no staleness concept).
23
+ * Phase 2+ populates this after the staleness check.
24
+ */
25
+ stale_files: string[];
26
+ }
27
+ /**
28
+ * The canonical response wrapper for both `kg_search` and `kg_execute`.
29
+ *
30
+ * `result` is the tool-specific payload. `meta` is always present and
31
+ * always has every field populated. `logs` is optional — present when the
32
+ * tool invoked `logger.log()` inside the sandbox, or when ingestion emitted
33
+ * warnings worth surfacing.
34
+ *
35
+ * Errors go inside `result` (e.g. `{ result: { error: "..." } }`) rather
36
+ * than being thrown or marked with `isError: true`, per CLAUDE.md
37
+ * §API Rules #8 — Claude Code has a bug where protocol-level errors display
38
+ * as "Error: undefined", and stuffing the error into the payload is the
39
+ * only way to keep it visible.
40
+ */
41
+ export interface Envelope<T> {
42
+ result: T;
43
+ meta: EnvelopeMeta;
44
+ logs?: string[];
45
+ }
46
+ /**
47
+ * Build an envelope from its parts. Kept as a helper so the field order is
48
+ * stable (JSON stringification cares) and so the caller cannot forget a
49
+ * required meta field.
50
+ */
51
+ export declare function buildEnvelope<T>(params: {
52
+ result: T;
53
+ tokensBudgeted: number;
54
+ tokensUsed: number;
55
+ resultsTruncated: boolean;
56
+ scope: Scope;
57
+ queryTimeMs: number;
58
+ staleFiles?: string[];
59
+ logs?: string[];
60
+ }): Envelope<T>;
61
+ /**
62
+ * Helper for measuring query wall-clock time. Captures a start timestamp
63
+ * on construction and returns the elapsed ms when `end()` is called. Using
64
+ * `performance.now()` instead of `Date.now()` for sub-ms resolution — the
65
+ * cold-start benchmark gate in Phase 1 tests #6 is measuring p50 at the
66
+ * 100ms scale, so millisecond precision matters.
67
+ */
68
+ export declare class QueryTimer {
69
+ private readonly startMs;
70
+ constructor();
71
+ end(): number;
72
+ }
73
+ //# sourceMappingURL=envelope.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"envelope.d.ts","sourceRoot":"","sources":["../../src/mcp/envelope.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,MAAM,KAAK,GAAG,SAAS,GAAG,UAAU,GAAG,MAAM,CAAC;AAEpD,MAAM,WAAW,YAAY;IAC3B,6DAA6D;IAC7D,eAAe,EAAE,MAAM,CAAC;IACxB,gEAAgE;IAChE,WAAW,EAAE,MAAM,CAAC;IACpB,uEAAuE;IACvE,iBAAiB,EAAE,OAAO,CAAC;IAC3B,mCAAmC;IACnC,KAAK,EAAE,KAAK,CAAC;IACb,yDAAyD;IACzD,aAAa,EAAE,MAAM,CAAC;IACtB;;;;OAIG;IACH,WAAW,EAAE,MAAM,EAAE,CAAC;CACvB;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,WAAW,QAAQ,CAAC,CAAC;IACzB,MAAM,EAAE,CAAC,CAAC;IACV,IAAI,EAAE,YAAY,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;CACjB;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAAE,MAAM,EAAE;IACvC,MAAM,EAAE,CAAC,CAAC;IACV,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,KAAK,EAAE,KAAK,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;CACjB,GAAG,QAAQ,CAAC,CAAC,CAAC,CAgBd;AAED;;;;;;GAMG;AACH,qBAAa,UAAU;IACrB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;;IAMjC,GAAG,IAAI,MAAM;CAGd"}
@@ -0,0 +1,46 @@
1
+ /**
2
+ * MCP tool response envelope.
3
+ *
4
+ * CLAUDE.md §API Rules #5 — the envelope is immutable. Adding a field
5
+ * requires updating this file and regenerating the tool schemas. The LLM
6
+ * consumer parses this shape, so drift here breaks prompts in the wild.
7
+ */
8
+ /**
9
+ * Build an envelope from its parts. Kept as a helper so the field order is
10
+ * stable (JSON stringification cares) and so the caller cannot forget a
11
+ * required meta field.
12
+ */
13
+ export function buildEnvelope(params) {
14
+ const envelope = {
15
+ result: params.result,
16
+ meta: {
17
+ tokens_budgeted: params.tokensBudgeted,
18
+ tokens_used: params.tokensUsed,
19
+ results_truncated: params.resultsTruncated,
20
+ scope: params.scope,
21
+ query_time_ms: params.queryTimeMs,
22
+ stale_files: params.staleFiles ?? [],
23
+ },
24
+ };
25
+ if (params.logs && params.logs.length > 0) {
26
+ envelope.logs = params.logs;
27
+ }
28
+ return envelope;
29
+ }
30
+ /**
31
+ * Helper for measuring query wall-clock time. Captures a start timestamp
32
+ * on construction and returns the elapsed ms when `end()` is called. Using
33
+ * `performance.now()` instead of `Date.now()` for sub-ms resolution — the
34
+ * cold-start benchmark gate in Phase 1 tests #6 is measuring p50 at the
35
+ * 100ms scale, so millisecond precision matters.
36
+ */
37
+ export class QueryTimer {
38
+ startMs;
39
+ constructor() {
40
+ this.startMs = performance.now();
41
+ }
42
+ end() {
43
+ return Math.round(performance.now() - this.startMs);
44
+ }
45
+ }
46
+ //# sourceMappingURL=envelope.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"envelope.js","sourceRoot":"","sources":["../../src/mcp/envelope.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AA2CH;;;;GAIG;AACH,MAAM,UAAU,aAAa,CAAI,MAShC;IACC,MAAM,QAAQ,GAAgB;QAC5B,MAAM,EAAE,MAAM,CAAC,MAAM;QACrB,IAAI,EAAE;YACJ,eAAe,EAAE,MAAM,CAAC,cAAc;YACtC,WAAW,EAAE,MAAM,CAAC,UAAU;YAC9B,iBAAiB,EAAE,MAAM,CAAC,gBAAgB;YAC1C,KAAK,EAAE,MAAM,CAAC,KAAK;YACnB,aAAa,EAAE,MAAM,CAAC,WAAW;YACjC,WAAW,EAAE,MAAM,CAAC,UAAU,IAAI,EAAE;SACrC;KACF,CAAC;IACF,IAAI,MAAM,CAAC,IAAI,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC1C,QAAQ,CAAC,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC;IAC9B,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED;;;;;;GAMG;AACH,MAAM,OAAO,UAAU;IACJ,OAAO,CAAS;IAEjC;QACE,IAAI,CAAC,OAAO,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC;IACnC,CAAC;IAED,GAAG;QACD,OAAO,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC;IACtD,CAAC;CACF"}
@@ -0,0 +1,55 @@
1
+ import { z } from 'zod';
2
+ import type { Repository } from '../../db/repository.js';
3
+ import type { DbBundle } from '../../db/client.js';
4
+ import type { QuickJSExecutor } from '../../sandbox/executor.js';
5
+ import type { Embedder } from '../../retrieval/embedder.js';
6
+ export declare const kgExecuteInputShape: {
7
+ code: z.ZodString;
8
+ max_tokens: z.ZodOptional<z.ZodNumber>;
9
+ timeout_ms: z.ZodOptional<z.ZodNumber>;
10
+ scope: z.ZodOptional<z.ZodEnum<{
11
+ project: "project";
12
+ personal: "personal";
13
+ both: "both";
14
+ }>>;
15
+ };
16
+ export declare const kgExecuteToolConfig: {
17
+ readonly title: "Run JS in the knowledge-graph sandbox";
18
+ readonly description: string;
19
+ readonly inputSchema: {
20
+ code: z.ZodString;
21
+ max_tokens: z.ZodOptional<z.ZodNumber>;
22
+ timeout_ms: z.ZodOptional<z.ZodNumber>;
23
+ scope: z.ZodOptional<z.ZodEnum<{
24
+ project: "project";
25
+ personal: "personal";
26
+ both: "both";
27
+ }>>;
28
+ };
29
+ };
30
+ export interface KgExecuteDeps {
31
+ repository: Repository;
32
+ executor: QuickJSExecutor;
33
+ bundle: DbBundle;
34
+ embedder: Embedder;
35
+ wikiRoot?: string;
36
+ personalBundle?: DbBundle;
37
+ personalWikiRoot?: string;
38
+ }
39
+ /**
40
+ * Build the `kg_execute` handler. Phase 3 path: uses `executeWithBindings`
41
+ * with the full `kg.project.*` surface via the warm pool.
42
+ */
43
+ export declare function makeKgExecuteHandler(deps: KgExecuteDeps): (args: {
44
+ code: string;
45
+ max_tokens?: number;
46
+ timeout_ms?: number;
47
+ scope?: "project" | "personal" | "both";
48
+ }) => Promise<{
49
+ content: [{
50
+ type: "text";
51
+ text: string;
52
+ }];
53
+ isError?: boolean;
54
+ }>;
55
+ //# sourceMappingURL=execute.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"execute.d.ts","sourceRoot":"","sources":["../../../src/mcp/tools/execute.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AACjE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,6BAA6B,CAAC;AAmD5D,eAAO,MAAM,mBAAmB;;;;;;;;;CAuC/B,CAAC;AAEF,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;CAWtB,CAAC;AAEX,MAAM,WAAW,aAAa;IAC5B,UAAU,EAAE,UAAU,CAAC;IACvB,QAAQ,EAAE,eAAe,CAAC;IAC1B,MAAM,EAAE,QAAQ,CAAC;IACjB,QAAQ,EAAE,QAAQ,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,QAAQ,CAAC;IAC1B,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,aAAa,IACxC,MAAM;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,SAAS,GAAG,UAAU,GAAG,MAAM,CAAC;CACzC,KAAG,OAAO,CAAC;IAAE,OAAO,EAAE,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC,CA8F9E"}
@@ -0,0 +1,232 @@
1
+ import { z } from 'zod';
2
+ import { countEnvelopeTokens, countTokens, computeInternalBudget, fitResults } from '../../gate/budget.js';
3
+ import { buildEnvelope, QueryTimer } from '../envelope.js';
4
+ /**
5
+ * `kg_execute` — code-mode tool. The LLM writes a short JS snippet, we run
6
+ * it inside the QuickJS sandbox against the `kg` bindings, and return the
7
+ * (budget-shaped) result.
8
+ *
9
+ * Phase 3: full `kg.project.*` binding surface via warm pool. Backward-compat
10
+ * `kg.search()`/`kg.get()` aliases remain.
11
+ */
12
+ const KG_EXECUTE_TYPES = `
13
+ declare const kg: {
14
+ /** Substring search (backward compat). */
15
+ search(query: string): Array<{ id: string; text: string; source_uri: string }>;
16
+ /** Chunk lookup by id (backward compat). */
17
+ get(id: string): { id: string; text: string; source_uri: string } | null;
18
+ project: {
19
+ /** FTS5 full-text search with bm25 ranking. */
20
+ fts(query: string, opts?: { limit?: number }): Array<{ id: string; text: string; source_uri: string; node_id: string; rank: number; title: string | null; section_path: string }>;
21
+ /** Vector similarity search. Returns [] if no pre-computed embedding available. */
22
+ vec(query: string, opts?: { limit?: number }): Array<{ id: string; text: string; source_uri: string; node_id: string; distance: number; title: string | null; section_path: string }>;
23
+ /** Hybrid FTS + vector search with RRF fusion. Falls back to FTS-only if no embedding cached. */
24
+ hybrid(query: string, opts?: { limit?: number; rrf_k?: number }): Array<{ id: string; text: string; source_uri: string; node_id: string; score: number; snippet?: string; title: string | null; section_path: string }>;
25
+ /** Table of contents — list all nodes for LLM-driven browsing. Use this to scan titles, then get(id) to read specific sections. */
26
+ index(opts?: { kind?: string; source_uri?: string; limit?: number }): Array<{ id: string; title: string | null; source_uri: string; section_path: string; kind: string; token_count: number }>;
27
+ /** Node lookup by id — full section content. */
28
+ get(id: string): { id: string; source_uri: string; section_path: string; kind: string; title: string | null; content: string; token_count: number } | null;
29
+ /** K-hop graph traversal. */
30
+ neighbors(id: string, opts?: { depth?: number; edge_kinds?: string[] }): Array<{ id: string; source_uri: string; kind: string; title: string | null; depth: number }>;
31
+ log: {
32
+ /** Recent log entries. */
33
+ recent(n?: number, opts?: { kind?: string }): Array<{ id: number; ts: number; kind: string; source_uri: string | null; payload: unknown }>;
34
+ };
35
+ /** Concept gaps (stub, Phase 6). */
36
+ gaps(opts?: { resolved?: boolean; limit?: number }): never[];
37
+ /** Create or overwrite a wiki page. Path is relative to wiki root, .md extension enforced. */
38
+ write(path: string, content: string): { path: string; bytes: number };
39
+ /** Append a timestamped entry to log.md. */
40
+ append(entry: string): { path: string; bytes: number };
41
+ /** Delete a wiki page. Path is relative to wiki root. */
42
+ remove(path: string): { path: string; removed: true };
43
+ };
44
+ };
45
+ declare const budget: { fit(items: unknown[], maxTokens?: number): unknown[] };
46
+ declare const logger: { log(...args: unknown[]): void };
47
+ `.trim();
48
+ export const kgExecuteInputShape = {
49
+ code: z
50
+ .string()
51
+ .min(1)
52
+ .describe('JavaScript to run inside the sandbox. The sandbox is QuickJS with ' +
53
+ '`eval`, `Function`, `fetch`, `require`, `process`, and `constructor` ' +
54
+ 'removed. You have `kg.project.*` for FTS, graph traversal, log ' +
55
+ 'queries, and wiki writes (write/append/remove), plus `budget.fit()` ' +
56
+ 'for token-aware truncation and `logger.log()` captured into ' +
57
+ '`response.logs`. Return a value.\n' +
58
+ KG_EXECUTE_TYPES),
59
+ max_tokens: z
60
+ .number()
61
+ .int()
62
+ .positive()
63
+ .max(20_000)
64
+ .optional()
65
+ .describe('Maximum total tokens the response can contain. Default 5000, max 20000.'),
66
+ timeout_ms: z
67
+ .number()
68
+ .int()
69
+ .positive()
70
+ .max(10_000)
71
+ .optional()
72
+ .describe('Hard wall-clock execution timeout for the sandbox. Default 2000ms, ' +
73
+ 'max 10000ms. If exceeded, the response contains an `error` string.'),
74
+ scope: z
75
+ .enum(['project', 'personal', 'both'])
76
+ .optional()
77
+ .describe('Which KG bindings to inject. "project" (default) installs kg.project, ' +
78
+ '"personal" installs kg.personal, "both" installs both namespaces.'),
79
+ };
80
+ export const kgExecuteToolConfig = {
81
+ title: 'Run JS in the knowledge-graph sandbox',
82
+ description: 'Run a short JavaScript snippet inside the KG sandbox. Use `kg.project.index()` ' +
83
+ 'to browse the wiki table of contents, then `kg.project.get(id)` to read ' +
84
+ 'specific sections. `kg.project.hybrid()` for search, ' +
85
+ '`kg.project.neighbors()` for graph traversal, `kg.project.log.recent()` for ' +
86
+ 'event log. Legacy `kg.search()`/`kg.get()` also available. ' +
87
+ '64MB memory cap, 2s default timeout. No network, no eval.\n\n' +
88
+ KG_EXECUTE_TYPES,
89
+ inputSchema: kgExecuteInputShape,
90
+ };
91
+ /**
92
+ * Build the `kg_execute` handler. Phase 3 path: uses `executeWithBindings`
93
+ * with the full `kg.project.*` surface via the warm pool.
94
+ */
95
+ export function makeKgExecuteHandler(deps) {
96
+ return async (args) => {
97
+ const timer = new QueryTimer();
98
+ const maxTokens = args.max_tokens ?? 5000;
99
+ const scope = args.scope ?? 'project';
100
+ // Check that personal scope is available if requested
101
+ if ((scope === 'personal' || scope === 'both') && !deps.personalBundle) {
102
+ const envelope = buildEnvelope({
103
+ result: {
104
+ error: 'personal scope requested but no personal KG is configured — ' +
105
+ 'set KG_PROFILE_PATH or pass --profile-path',
106
+ },
107
+ tokensBudgeted: maxTokens,
108
+ tokensUsed: 0,
109
+ resultsTruncated: false,
110
+ scope,
111
+ queryTimeMs: timer.end(),
112
+ });
113
+ return wrapText(envelope);
114
+ }
115
+ const logs = [];
116
+ const writeCounter = { value: 0 };
117
+ // Build per-scope binding deps. Privacy invariant: only include
118
+ // the scope(s) the caller requested.
119
+ const bindingDeps = { maxTokens, logs };
120
+ if (scope === 'project' || scope === 'both') {
121
+ bindingDeps.project = {
122
+ repository: deps.repository,
123
+ bundle: deps.bundle,
124
+ scope: 'project',
125
+ embedder: deps.embedder,
126
+ wikiRoot: deps.wikiRoot,
127
+ writeCounter,
128
+ };
129
+ }
130
+ if ((scope === 'personal' || scope === 'both') && deps.personalBundle) {
131
+ bindingDeps.personal = {
132
+ repository: deps.repository,
133
+ bundle: deps.personalBundle,
134
+ scope: 'personal',
135
+ embedder: deps.embedder,
136
+ wikiRoot: deps.personalWikiRoot,
137
+ writeCounter,
138
+ };
139
+ }
140
+ const result = await deps.executor.executeWithBindings(args.code, bindingDeps, args.timeout_ms);
141
+ // Executor errors are in-payload per CLAUDE.md §API Rules #8.
142
+ if (result.error) {
143
+ const envelope = buildEnvelope({
144
+ result: { error: result.error },
145
+ tokensBudgeted: maxTokens,
146
+ tokensUsed: 0,
147
+ resultsTruncated: false,
148
+ scope,
149
+ queryTimeMs: timer.end(),
150
+ logs: result.logs,
151
+ });
152
+ const json = JSON.stringify(envelope);
153
+ envelope.meta.tokens_used = countEnvelopeTokens(json);
154
+ return wrapText(envelope);
155
+ }
156
+ // Apply the budget gate. If the result is an array, fitResults does
157
+ // the greedy truncation we want. If the result is a scalar or object,
158
+ // we check its total size and replace it with a truncation notice
159
+ // rather than emitting a too-large sentinel (which only makes sense
160
+ // for rank-ordered results).
161
+ const raw = result.result;
162
+ const { shapedResult, truncated, tokensUsed, tokensBudgeted } = shapeForBudget(raw, maxTokens);
163
+ const envelope = buildEnvelope({
164
+ result: shapedResult,
165
+ tokensBudgeted,
166
+ tokensUsed,
167
+ resultsTruncated: truncated,
168
+ scope,
169
+ queryTimeMs: timer.end(),
170
+ logs: result.logs,
171
+ });
172
+ const json = JSON.stringify(envelope);
173
+ envelope.meta.tokens_used = countEnvelopeTokens(json);
174
+ return wrapText(envelope);
175
+ };
176
+ }
177
+ /**
178
+ * Budget-shape the raw result of a sandbox execution. For array results
179
+ * we reuse `fitResults` with a synthetic id/uri extractor. For non-array
180
+ * results we measure the whole thing and replace it with a truncation
181
+ * notice if it overflows.
182
+ */
183
+ function shapeForBudget(raw, maxTokens) {
184
+ const budget = computeInternalBudget(maxTokens);
185
+ if (Array.isArray(raw)) {
186
+ let arrayIdx = 0;
187
+ const fit = fitResults(raw, maxTokens, (item) => JSON.stringify(item), (item) => {
188
+ // Try to pull a stable id out of the item if it has one,
189
+ // otherwise fall back to an array-index pseudo-id.
190
+ const obj = item;
191
+ if (obj && typeof obj.id === 'string')
192
+ return obj.id;
193
+ return `[${arrayIdx++}]`;
194
+ }, (item) => {
195
+ const obj = item;
196
+ return obj && typeof obj.source_uri === 'string' ? obj.source_uri : '';
197
+ });
198
+ return {
199
+ shapedResult: fit.kept,
200
+ truncated: fit.truncated,
201
+ tokensUsed: fit.tokensUsed,
202
+ tokensBudgeted: fit.tokensBudgeted,
203
+ };
204
+ }
205
+ // Scalar, object, null, etc. — measure the whole thing.
206
+ const json = JSON.stringify(raw);
207
+ const cost = countTokens(json ?? 'null');
208
+ if (cost <= budget) {
209
+ return {
210
+ shapedResult: raw,
211
+ truncated: false,
212
+ tokensUsed: cost,
213
+ tokensBudgeted: budget,
214
+ };
215
+ }
216
+ // Too large for a scalar result. Replace with an error string so the
217
+ // LLM can retry with a higher max_tokens or re-query more narrowly.
218
+ return {
219
+ shapedResult: {
220
+ error: `result body is ${cost} tokens, which exceeds the internal budget ` +
221
+ `of ${budget} (max_tokens=${maxTokens}). Re-query with a higher ` +
222
+ `max_tokens or narrow your code to return fewer fields.`,
223
+ },
224
+ truncated: true,
225
+ tokensUsed: countTokens(JSON.stringify({ error: 'placeholder' })),
226
+ tokensBudgeted: budget,
227
+ };
228
+ }
229
+ function wrapText(envelope) {
230
+ return { content: [{ type: 'text', text: JSON.stringify(envelope) }] };
231
+ }
232
+ //# sourceMappingURL=execute.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"execute.js","sourceRoot":"","sources":["../../../src/mcp/tools/execute.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,53 @@
1
+ import { z } from 'zod';
2
+ import type { Repository } from '../../db/repository.js';
3
+ import type { DbBundle } from '../../db/client.js';
4
+ import type { Embedder } from '../../retrieval/embedder.js';
5
+ /**
6
+ * `kg_search` — fast-path hybrid search against the knowledge graph.
7
+ *
8
+ * Phase 5: supports all three scopes. For `scope='both'`, results from
9
+ * both KGs are merged and tagged with `source_scope`.
10
+ */
11
+ export declare const kgSearchInputShape: {
12
+ query: z.ZodString;
13
+ max_tokens: z.ZodOptional<z.ZodNumber>;
14
+ scope: z.ZodOptional<z.ZodEnum<{
15
+ project: "project";
16
+ personal: "personal";
17
+ both: "both";
18
+ }>>;
19
+ };
20
+ export declare const kgSearchToolConfig: {
21
+ readonly title: "Search the knowledge graph";
22
+ readonly description: string;
23
+ readonly inputSchema: {
24
+ query: z.ZodString;
25
+ max_tokens: z.ZodOptional<z.ZodNumber>;
26
+ scope: z.ZodOptional<z.ZodEnum<{
27
+ project: "project";
28
+ personal: "personal";
29
+ both: "both";
30
+ }>>;
31
+ };
32
+ };
33
+ export interface KgSearchDeps {
34
+ repository: Repository;
35
+ embedder: Embedder;
36
+ bundle: DbBundle;
37
+ personalBundle?: DbBundle;
38
+ }
39
+ /**
40
+ * Build the `kg_search` handler. Supports project, personal, and both scopes.
41
+ */
42
+ export declare function makeKgSearchHandler(deps: KgSearchDeps): (args: {
43
+ query: string;
44
+ max_tokens?: number;
45
+ scope?: "project" | "personal" | "both";
46
+ }) => Promise<{
47
+ content: [{
48
+ type: "text";
49
+ text: string;
50
+ }];
51
+ isError?: boolean;
52
+ }>;
53
+ //# sourceMappingURL=search.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../../src/mcp/tools/search.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,6BAA6B,CAAC;AAM5D;;;;;GAKG;AAEH,eAAO,MAAM,kBAAkB;;;;;;;;CA6B9B,CAAC;AAEF,eAAO,MAAM,kBAAkB;;;;;;;;;;;;CASrB,CAAC;AAEX,MAAM,WAAW,YAAY;IAC3B,UAAU,EAAE,UAAU,CAAC;IACvB,QAAQ,EAAE,QAAQ,CAAC;IACnB,MAAM,EAAE,QAAQ,CAAC;IACjB,cAAc,CAAC,EAAE,QAAQ,CAAC;CAC3B;AAaD;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,YAAY,IACtC,MAAM;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,SAAS,GAAG,UAAU,GAAG,MAAM,CAAC;CACzC,KAAG,OAAO,CAAC;IAAE,OAAO,EAAE,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC,CA4E9E"}
@@ -0,0 +1,114 @@
1
+ import { z } from 'zod';
2
+ import { hybridSearch } from '../../retrieval/hybrid.js';
3
+ import { fitResults, countEnvelopeTokens } from '../../gate/budget.js';
4
+ import { buildEnvelope, QueryTimer } from '../envelope.js';
5
+ import { nextReader } from '../../db/client.js';
6
+ /**
7
+ * `kg_search` — fast-path hybrid search against the knowledge graph.
8
+ *
9
+ * Phase 5: supports all three scopes. For `scope='both'`, results from
10
+ * both KGs are merged and tagged with `source_scope`.
11
+ */
12
+ export const kgSearchInputShape = {
13
+ query: z
14
+ .string()
15
+ .min(1)
16
+ .describe('The search term. Hybrid FTS5 + vector search against every indexed ' +
17
+ 'chunk of the wiki, ranked by Reciprocal Rank Fusion. ' +
18
+ 'Examples: "hashPassword", "bcrypt", "auth flow".'),
19
+ max_tokens: z
20
+ .number()
21
+ .int()
22
+ .positive()
23
+ .max(20_000)
24
+ .optional()
25
+ .describe('Maximum total tokens the response can contain. Defaults to 5000, max 20000. ' +
26
+ 'The server applies a ~10% safety margin internally. If results would ' +
27
+ 'exceed the budget, they are greedy-truncated in rank order and ' +
28
+ '`meta.results_truncated` is set to true.'),
29
+ scope: z
30
+ .enum(['project', 'personal', 'both'])
31
+ .optional()
32
+ .describe('Which KG to query. "project" (default) searches the project wiki, ' +
33
+ '"personal" searches the personal wiki, "both" merges results from ' +
34
+ 'both with `source_scope` tagging on each result.'),
35
+ };
36
+ export const kgSearchToolConfig = {
37
+ title: 'Search the knowledge graph',
38
+ description: 'Hybrid FTS + vector search the knowledge graph and return matching ' +
39
+ 'chunks ranked by Reciprocal Rank Fusion. Each result includes `title` ' +
40
+ 'and `section_path` for quick triage without reading full content. ' +
41
+ 'Prefer this for short lookups; use `kg_execute` when you need to chain ' +
42
+ 'filters, browse the wiki index, or combine multiple queries in one call.',
43
+ inputSchema: kgSearchInputShape,
44
+ };
45
+ /**
46
+ * Build the `kg_search` handler. Supports project, personal, and both scopes.
47
+ */
48
+ export function makeKgSearchHandler(deps) {
49
+ return async (args) => {
50
+ const timer = new QueryTimer();
51
+ const maxTokens = args.max_tokens ?? 5000;
52
+ const scope = args.scope ?? 'project';
53
+ // Check personal scope availability
54
+ if ((scope === 'personal' || scope === 'both') && !deps.personalBundle) {
55
+ const envelope = buildEnvelope({
56
+ result: {
57
+ error: 'personal scope requested but no personal KG is configured — ' +
58
+ 'set KG_PROFILE_PATH or pass --profile-path',
59
+ },
60
+ tokensBudgeted: maxTokens,
61
+ tokensUsed: 0,
62
+ resultsTruncated: false,
63
+ scope,
64
+ queryTimeMs: timer.end(),
65
+ });
66
+ return wrapText(envelope);
67
+ }
68
+ let allHits = [];
69
+ if (scope === 'project' || scope === 'both') {
70
+ const reader = nextReader(deps.bundle);
71
+ const hits = await hybridSearch(reader, 'project', args.query, deps.embedder);
72
+ const tagged = hits.map((h) => ({
73
+ id: h.id, text: h.text, source_uri: h.source_uri, score: h.score,
74
+ confidence: h.confidence, title: h.title, section_path: h.section_path,
75
+ ...(scope === 'both' ? { source_scope: 'project' } : {}),
76
+ }));
77
+ allHits.push(...tagged);
78
+ }
79
+ if ((scope === 'personal' || scope === 'both') && deps.personalBundle) {
80
+ const reader = nextReader(deps.personalBundle);
81
+ const hits = await hybridSearch(reader, 'personal', args.query, deps.embedder);
82
+ const tagged = hits.map((h) => ({
83
+ id: h.id, text: h.text, source_uri: h.source_uri, score: h.score,
84
+ confidence: h.confidence, title: h.title, section_path: h.section_path,
85
+ ...(scope === 'both' ? { source_scope: 'personal' } : {}),
86
+ }));
87
+ allHits.push(...tagged);
88
+ }
89
+ // For scope='both', re-sort merged results by score descending
90
+ if (scope === 'both') {
91
+ allHits.sort((a, b) => b.score - a.score);
92
+ }
93
+ const fit = fitResults(allHits, maxTokens, (h) => JSON.stringify(h), (h) => h.id, (h) => h.source_uri);
94
+ const envelope = buildEnvelope({
95
+ result: fit.kept.map((item) => {
96
+ if ('too_large' in item)
97
+ return item;
98
+ return item;
99
+ }),
100
+ tokensBudgeted: fit.tokensBudgeted,
101
+ tokensUsed: 0,
102
+ resultsTruncated: fit.truncated,
103
+ scope,
104
+ queryTimeMs: timer.end(),
105
+ });
106
+ const json = JSON.stringify(envelope);
107
+ envelope.meta.tokens_used = countEnvelopeTokens(json);
108
+ return wrapText(envelope);
109
+ };
110
+ }
111
+ function wrapText(envelope) {
112
+ return { content: [{ type: 'text', text: JSON.stringify(envelope) }] };
113
+ }
114
+ //# sourceMappingURL=search.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search.js","sourceRoot":"","sources":["../../../src/mcp/tools/search.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ import type { Database as BetterSqliteDatabase } from 'better-sqlite3';
2
+ /**
3
+ * Audit log writer with scope-split JSONL mirror.
4
+ *
5
+ * Per CLAUDE.md §Security #7:
6
+ * - scope='project' → kg_audit table in project .kg/kg.db + .kg/audit.jsonl
7
+ * - scope='personal' or 'both' → kg_audit table in ~/.kg/kg.db + ~/.kg/audit.jsonl
8
+ * - NEVER write personal query text to a path inside the project repo
9
+ */
10
+ export interface AuditEntry {
11
+ toolName: string;
12
+ scopeRequested: string;
13
+ callerCtx?: string;
14
+ responseTokens?: number;
15
+ error?: string;
16
+ }
17
+ /**
18
+ * Write an audit row to the appropriate DB and mirror to JSONL.
19
+ *
20
+ * @param writer The DB writer for the scope-appropriate bundle.
21
+ * @param jsonlPath The path for the JSONL mirror file.
22
+ * @param entry Audit entry data.
23
+ */
24
+ export declare function writeAuditRow(writer: BetterSqliteDatabase, jsonlPath: string | undefined, entry: AuditEntry): void;
25
+ //# sourceMappingURL=audit.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audit.d.ts","sourceRoot":"","sources":["../../src/observability/audit.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,IAAI,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAIvE;;;;;;;GAOG;AAEH,MAAM,WAAW,UAAU;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAC3B,MAAM,EAAE,oBAAoB,EAC5B,SAAS,EAAE,MAAM,GAAG,SAAS,EAC7B,KAAK,EAAE,UAAU,GAChB,IAAI,CAmCN"}