trellis 2.0.13 → 2.1.2

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 (96) hide show
  1. package/dist/cli/index.js +1 -1
  2. package/dist/embeddings/index.js +1 -1
  3. package/dist/{index-7gvjxt27.js → index-2917tjd8.js} +1 -1
  4. package/package.json +2 -10
  5. package/dist/transformers.node-bx3q9d7k.js +0 -33130
  6. package/src/cli/index.ts +0 -3356
  7. package/src/core/agents/harness.ts +0 -380
  8. package/src/core/agents/index.ts +0 -18
  9. package/src/core/agents/types.ts +0 -90
  10. package/src/core/index.ts +0 -118
  11. package/src/core/kernel/middleware.ts +0 -44
  12. package/src/core/kernel/trellis-kernel.ts +0 -593
  13. package/src/core/ontology/builtins.ts +0 -248
  14. package/src/core/ontology/index.ts +0 -34
  15. package/src/core/ontology/registry.ts +0 -209
  16. package/src/core/ontology/types.ts +0 -124
  17. package/src/core/ontology/validator.ts +0 -382
  18. package/src/core/persist/backend.ts +0 -74
  19. package/src/core/persist/sqlite-backend.ts +0 -298
  20. package/src/core/plugins/index.ts +0 -17
  21. package/src/core/plugins/registry.ts +0 -322
  22. package/src/core/plugins/types.ts +0 -126
  23. package/src/core/query/datalog.ts +0 -188
  24. package/src/core/query/engine.ts +0 -370
  25. package/src/core/query/index.ts +0 -34
  26. package/src/core/query/parser.ts +0 -481
  27. package/src/core/query/types.ts +0 -200
  28. package/src/core/store/eav-store.ts +0 -467
  29. package/src/decisions/auto-capture.ts +0 -136
  30. package/src/decisions/hooks.ts +0 -163
  31. package/src/decisions/index.ts +0 -261
  32. package/src/decisions/types.ts +0 -103
  33. package/src/embeddings/auto-embed.ts +0 -248
  34. package/src/embeddings/chunker.ts +0 -327
  35. package/src/embeddings/index.ts +0 -48
  36. package/src/embeddings/model.ts +0 -112
  37. package/src/embeddings/search.ts +0 -305
  38. package/src/embeddings/store.ts +0 -313
  39. package/src/embeddings/types.ts +0 -92
  40. package/src/engine.ts +0 -1125
  41. package/src/garden/cluster.ts +0 -330
  42. package/src/garden/garden.ts +0 -306
  43. package/src/garden/index.ts +0 -29
  44. package/src/git/git-exporter.ts +0 -286
  45. package/src/git/git-importer.ts +0 -329
  46. package/src/git/git-reader.ts +0 -189
  47. package/src/git/index.ts +0 -22
  48. package/src/identity/governance.ts +0 -211
  49. package/src/identity/identity.ts +0 -224
  50. package/src/identity/index.ts +0 -30
  51. package/src/identity/signing-middleware.ts +0 -97
  52. package/src/index.ts +0 -29
  53. package/src/links/index.ts +0 -49
  54. package/src/links/lifecycle.ts +0 -400
  55. package/src/links/parser.ts +0 -484
  56. package/src/links/ref-index.ts +0 -186
  57. package/src/links/resolver.ts +0 -314
  58. package/src/links/types.ts +0 -108
  59. package/src/mcp/index.ts +0 -22
  60. package/src/mcp/server.ts +0 -1278
  61. package/src/semantic/csharp-parser.ts +0 -493
  62. package/src/semantic/go-parser.ts +0 -585
  63. package/src/semantic/index.ts +0 -34
  64. package/src/semantic/java-parser.ts +0 -456
  65. package/src/semantic/python-parser.ts +0 -659
  66. package/src/semantic/ruby-parser.ts +0 -446
  67. package/src/semantic/rust-parser.ts +0 -784
  68. package/src/semantic/semantic-merge.ts +0 -210
  69. package/src/semantic/ts-parser.ts +0 -681
  70. package/src/semantic/types.ts +0 -175
  71. package/src/sync/http-transport.ts +0 -144
  72. package/src/sync/index.ts +0 -43
  73. package/src/sync/memory-transport.ts +0 -66
  74. package/src/sync/multi-repo.ts +0 -200
  75. package/src/sync/reconciler.ts +0 -237
  76. package/src/sync/sync-engine.ts +0 -258
  77. package/src/sync/types.ts +0 -104
  78. package/src/sync/ws-transport.ts +0 -145
  79. package/src/ui/client.html +0 -695
  80. package/src/ui/server.ts +0 -419
  81. package/src/vcs/blob-store.ts +0 -124
  82. package/src/vcs/branch.ts +0 -150
  83. package/src/vcs/checkpoint.ts +0 -64
  84. package/src/vcs/decompose.ts +0 -469
  85. package/src/vcs/diff.ts +0 -409
  86. package/src/vcs/engine-context.ts +0 -26
  87. package/src/vcs/index.ts +0 -23
  88. package/src/vcs/issue.ts +0 -800
  89. package/src/vcs/merge.ts +0 -425
  90. package/src/vcs/milestone.ts +0 -124
  91. package/src/vcs/ops.ts +0 -59
  92. package/src/vcs/types.ts +0 -213
  93. package/src/vcs/vcs-middleware.ts +0 -81
  94. package/src/watcher/fs-watcher.ts +0 -255
  95. package/src/watcher/index.ts +0 -9
  96. package/src/watcher/ingestion.ts +0 -116
@@ -1,467 +0,0 @@
1
- /**
2
- * EAV-based Datalog Engine with Path-Aware Ingestor
3
- *
4
- * Core types and fact storage for schema-agnostic data processing.
5
- * Inlined from trellis-core for single-package publish.
6
- *
7
- * @module trellis/core
8
- */
9
-
10
- export type Atom = string | number | boolean | Date | EntityRef;
11
- export type EntityRef = string;
12
-
13
- export interface Fact {
14
- e: string; // entity
15
- a: string; // attribute (JSONPath)
16
- v: Atom; // value
17
- }
18
-
19
- export interface Link {
20
- e1: string; // source entity
21
- a: string; // relationship attribute
22
- e2: string; // target entity
23
- }
24
-
25
- export interface CatalogEntry {
26
- attribute: string;
27
- type: 'string' | 'number' | 'boolean' | 'date' | 'mixed';
28
- cardinality: 'one' | 'many';
29
- distinctCount: number;
30
- examples: Atom[];
31
- min?: number;
32
- max?: number;
33
- }
34
-
35
- export interface QueryTraceEntry {
36
- goal: string;
37
- bindingsCount: number;
38
- durationMs: number;
39
- }
40
-
41
- export interface QueryResult {
42
- bindings: Record<string, Atom>[];
43
- executionTime: number;
44
- plan?: string;
45
- trace?: QueryTraceEntry[];
46
- }
47
-
48
- /**
49
- * Path-aware JSON flattener
50
- * Converts nested JSON into attribute-value pairs with dot notation
51
- */
52
- export function* flatten(obj: any, base = ''): Generator<[string, any]> {
53
- if (Array.isArray(obj)) {
54
- for (const v of obj) {
55
- yield* flatten(v, base);
56
- }
57
- } else if (obj && typeof obj === 'object') {
58
- for (const [k, v] of Object.entries(obj)) {
59
- yield* flatten(v, base ? `${base}.${k}` : k);
60
- }
61
- } else {
62
- yield [base, obj];
63
- }
64
- }
65
-
66
- /**
67
- * Convert JSON entity to EAV facts
68
- */
69
- export function jsonEntityFacts(
70
- entityId: string,
71
- root: any,
72
- type: string,
73
- ): Fact[] {
74
- const facts: Fact[] = [{ e: entityId, a: 'type', v: type }];
75
-
76
- for (const [a, v] of flatten(root)) {
77
- if (v === undefined || v === null) continue;
78
-
79
- if (Array.isArray(v)) {
80
- for (const el of v) {
81
- facts.push({ e: entityId, a, v: el as any });
82
- }
83
- } else if (typeof v === 'object') {
84
- // Handled by flatten recursion
85
- } else {
86
- facts.push({ e: entityId, a, v: v as any });
87
- }
88
- }
89
-
90
- return facts;
91
- }
92
-
93
- /**
94
- * In-memory EAV triple store
95
- */
96
- export class EAVStore {
97
- private facts: Fact[] = [];
98
- private links: Link[] = [];
99
- private catalog: Map<string, CatalogEntry> = new Map();
100
-
101
- // Indexes for fast lookups
102
- private eavIndex: Map<string, Map<string, Set<number>>> = new Map();
103
- private aevIndex: Map<string, Map<string, Set<number>>> = new Map();
104
- private aveIndex: Map<string, Map<Atom, Set<number>>> = new Map();
105
-
106
- // Link indexes for graph queries
107
- private linkIndex: Map<string, Map<string, Set<string>>> = new Map(); // e1 -> a -> e2s
108
- private linkReverseIndex: Map<string, Map<string, Set<string>>> = new Map(); // e2 -> a -> e1s
109
- private linkAttrIndex: Map<string, Set<[string, string]>> = new Map(); // a -> [(e1, e2)]
110
-
111
- // Distinct value tracking
112
- private distinct = new Map<string, Set<string>>(); // attr -> set of valueKey
113
-
114
- addFacts(facts: Fact[]): void {
115
- for (let i = 0; i < facts.length; i++) {
116
- const fact = facts[i];
117
- if (fact) {
118
- this.facts.push(fact);
119
- this.updateIndexes(fact, this.facts.length - 1);
120
- this.updateCatalog(fact);
121
- }
122
- }
123
- }
124
-
125
- addLinks(links: Link[]): void {
126
- for (const link of links) {
127
- this.links.push(link);
128
- this.updateLinkIndexes(link);
129
- }
130
- }
131
-
132
- deleteFacts(factsToDelete: Fact[]): void {
133
- for (const fact of factsToDelete) {
134
- // Find the fact index
135
- const valueKey = this.valueKey(fact.v);
136
- const indices = this.aveIndex.get(fact.a)?.get(valueKey);
137
- if (!indices) continue;
138
-
139
- let foundIdx = -1;
140
- for (const idx of indices) {
141
- const storedFact = this.facts[idx];
142
- if (storedFact && storedFact.e === fact.e && storedFact.a === fact.a) {
143
- foundIdx = idx;
144
- break;
145
- }
146
- }
147
-
148
- if (foundIdx !== -1) {
149
- // Remove from main facts (set to null to maintain indices if needed, or just remove and rebuild)
150
- // For simplicity and to keep indices valid without shifting, we'll set to undefined
151
- this.facts[foundIdx] = undefined as any;
152
-
153
- // Remove from indexes
154
- this.eavIndex.get(fact.e)?.get(fact.a)?.delete(foundIdx);
155
- this.aevIndex.get(fact.a)?.get(fact.e)?.delete(foundIdx);
156
- this.aveIndex.get(fact.a)?.get(valueKey)?.delete(foundIdx);
157
-
158
- // Update catalog (approximate, since we don't fully rebuild it)
159
- const entry = this.catalog.get(fact.a);
160
- if (entry) {
161
- // If we had a specific count, we'd decrement, but distinctCount needs re-check
162
- // For now we'll just leave it or let it be refreshed later
163
- }
164
- }
165
- }
166
- }
167
-
168
- deleteLinks(linksToDelete: Link[]): void {
169
- for (const link of linksToDelete) {
170
- // Find and remove from main links list
171
- const initialLen = this.links.length;
172
- this.links = this.links.filter(
173
- (l) => !(l.e1 === link.e1 && l.a === link.a && l.e2 === link.e2),
174
- );
175
-
176
- if (this.links.length < initialLen) {
177
- // Remove from indexes
178
- this.linkIndex.get(link.e1)?.get(link.a)?.delete(link.e2);
179
- this.linkReverseIndex.get(link.e2)?.get(link.a)?.delete(link.e1);
180
-
181
- const attrPairs = this.linkAttrIndex.get(link.a);
182
- if (attrPairs) {
183
- for (const pair of attrPairs) {
184
- if (pair[0] === link.e1 && pair[1] === link.e2) {
185
- attrPairs.delete(pair);
186
- break;
187
- }
188
- }
189
- }
190
- }
191
- }
192
- }
193
-
194
- private updateIndexes(fact: Fact, index: number): void {
195
- // EAV index: entity -> attribute -> fact indices
196
- if (!this.eavIndex.has(fact.e)) {
197
- this.eavIndex.set(fact.e, new Map());
198
- }
199
- if (!this.eavIndex.get(fact.e)!.has(fact.a)) {
200
- this.eavIndex.get(fact.e)!.set(fact.a, new Set());
201
- }
202
- this.eavIndex.get(fact.e)!.get(fact.a)!.add(index);
203
-
204
- // AEV index: attribute -> entity -> fact indices
205
- if (!this.aevIndex.has(fact.a)) {
206
- this.aevIndex.set(fact.a, new Map());
207
- }
208
- if (!this.aevIndex.get(fact.a)!.has(fact.e)) {
209
- this.aevIndex.get(fact.a)!.set(fact.e, new Set());
210
- }
211
- this.aevIndex.get(fact.a)!.get(fact.e)!.add(index);
212
-
213
- // AVE index: attribute -> value -> fact indices
214
- if (!this.aveIndex.has(fact.a)) {
215
- this.aveIndex.set(fact.a, new Map());
216
- }
217
- const valueKey = this.valueKey(fact.v);
218
- if (!this.aveIndex.get(fact.a)!.has(valueKey)) {
219
- this.aveIndex.get(fact.a)!.set(valueKey, new Set());
220
- }
221
- this.aveIndex.get(fact.a)!.get(valueKey)!.add(index);
222
- }
223
-
224
- private updateLinkIndexes(link: Link): void {
225
- // Forward index: e1 -> a -> e2s
226
- if (!this.linkIndex.has(link.e1)) {
227
- this.linkIndex.set(link.e1, new Map());
228
- }
229
- const e1Attrs = this.linkIndex.get(link.e1)!;
230
- if (!e1Attrs.has(link.a)) {
231
- e1Attrs.set(link.a, new Set());
232
- }
233
- e1Attrs.get(link.a)!.add(link.e2);
234
-
235
- // Reverse index: e2 -> a -> e1s
236
- if (!this.linkReverseIndex.has(link.e2)) {
237
- this.linkReverseIndex.set(link.e2, new Map());
238
- }
239
- const e2Attrs = this.linkReverseIndex.get(link.e2)!;
240
- if (!e2Attrs.has(link.a)) {
241
- e2Attrs.set(link.a, new Set());
242
- }
243
- e2Attrs.get(link.a)!.add(link.e1);
244
-
245
- // Attribute index: a -> [(e1, e2)]
246
- if (!this.linkAttrIndex.has(link.a)) {
247
- this.linkAttrIndex.set(link.a, new Set());
248
- }
249
- this.linkAttrIndex.get(link.a)!.add([link.e1, link.e2]);
250
- }
251
-
252
- private valueKey(v: Atom): string {
253
- if (v instanceof Date) return `date:${v.toISOString()}`;
254
- return `${typeof v}:${v}`;
255
- }
256
-
257
- private updateCatalog(fact: Fact): void {
258
- const entry = this.catalog.get(fact.a) || {
259
- attribute: fact.a,
260
- type: this.inferType(fact.v),
261
- cardinality: 'one',
262
- distinctCount: 0,
263
- examples: [],
264
- };
265
-
266
- // Update type (may become 'mixed')
267
- const factType = this.inferType(fact.v);
268
- if (entry.type !== factType && entry.type !== 'mixed') {
269
- entry.type = 'mixed';
270
- }
271
-
272
- // Update cardinality (if we see multiple values for same entity+attribute)
273
- const entityAttrs = this.eavIndex.get(fact.e)?.get(fact.a);
274
- if (entityAttrs && entityAttrs.size > 1) {
275
- entry.cardinality = 'many';
276
- }
277
-
278
- // Update distinct count
279
- const k = this.valueKey(fact.v);
280
- const s =
281
- this.distinct.get(fact.a) ||
282
- (this.distinct.set(fact.a, new Set()), this.distinct.get(fact.a)!);
283
- s.add(k);
284
- entry.distinctCount = s.size;
285
-
286
- // Update examples (keep first 5)
287
- if (entry.examples.length < 5 && !entry.examples.includes(fact.v)) {
288
- entry.examples.push(fact.v);
289
- }
290
-
291
- // Update numeric ranges
292
- if (typeof fact.v === 'number') {
293
- entry.min = Math.min(entry.min ?? fact.v, fact.v);
294
- entry.max = Math.max(entry.max ?? fact.v, fact.v);
295
- }
296
-
297
- this.catalog.set(fact.a, entry);
298
- }
299
-
300
- private inferType(
301
- v: Atom,
302
- ): 'string' | 'number' | 'boolean' | 'date' | 'mixed' {
303
- if (typeof v === 'string') return 'string';
304
- if (typeof v === 'number') return 'number';
305
- if (typeof v === 'boolean') return 'boolean';
306
- if (v instanceof Date) return 'date';
307
- return 'mixed';
308
- }
309
-
310
- // Query methods
311
- getFactsByEntity(entity: string): Fact[] {
312
- const indices = this.eavIndex.get(entity);
313
- if (!indices) return [];
314
-
315
- const result: Fact[] = [];
316
- for (const attrIndices of indices.values()) {
317
- for (const idx of attrIndices) {
318
- const fact = this.facts[idx];
319
- if (fact) {
320
- result.push(fact);
321
- }
322
- }
323
- }
324
- return result;
325
- }
326
-
327
- getFactsByAttribute(attribute: string): Fact[] {
328
- const indices = this.aevIndex.get(attribute);
329
- if (!indices) return [];
330
-
331
- const result: Fact[] = [];
332
- for (const entityIndices of indices.values()) {
333
- for (const idx of entityIndices) {
334
- const fact = this.facts[idx];
335
- if (fact) {
336
- result.push(fact);
337
- }
338
- }
339
- }
340
- return result;
341
- }
342
-
343
- getFactsByValue(attribute: string, value: Atom): Fact[] {
344
- const indices = this.aveIndex.get(attribute)?.get(this.valueKey(value));
345
- if (!indices) return [];
346
-
347
- return Array.from(indices)
348
- .map((idx) => this.facts[idx])
349
- .filter((fact): fact is Fact => fact !== undefined);
350
- }
351
-
352
- getCatalog(): CatalogEntry[] {
353
- return Array.from(this.catalog.values());
354
- }
355
-
356
- getCatalogEntry(attribute: string): CatalogEntry | undefined {
357
- return this.catalog.get(attribute);
358
- }
359
-
360
- // Statistics
361
- getAllFacts(): Fact[] {
362
- return this.facts.filter((f): f is Fact => f !== undefined);
363
- }
364
-
365
- getAllLinks(): Link[] {
366
- return [...this.links];
367
- }
368
-
369
- getLinksByEntity(entity: string): Link[] {
370
- const results: Link[] = [];
371
- const forwardLinks = this.linkIndex.get(entity);
372
- if (forwardLinks) {
373
- for (const [attr, targets] of forwardLinks) {
374
- for (const target of targets) {
375
- results.push({ e1: entity, a: attr, e2: target });
376
- }
377
- }
378
- }
379
- const reverseLinks = this.linkReverseIndex.get(entity);
380
- if (reverseLinks) {
381
- for (const [attr, sources] of reverseLinks) {
382
- for (const source of sources) {
383
- results.push({ e1: source, a: attr, e2: entity });
384
- }
385
- }
386
- }
387
- return results;
388
- }
389
-
390
- getLinksByAttribute(attribute: string): Link[] {
391
- const results: Link[] = [];
392
- const links = this.linkAttrIndex.get(attribute);
393
- if (links) {
394
- for (const [e1, e2] of links) {
395
- results.push({ e1, a: attribute, e2 });
396
- }
397
- }
398
- return results;
399
- }
400
-
401
- getLinksByEntityAndAttribute(entity: string, attribute: string): Link[] {
402
- const results: Link[] = [];
403
- const attrs = this.linkIndex.get(entity);
404
- if (attrs) {
405
- const targets = attrs.get(attribute);
406
- if (targets) {
407
- for (const target of targets) {
408
- results.push({ e1: entity, a: attribute, e2: target });
409
- }
410
- }
411
- }
412
- return results;
413
- }
414
-
415
- getStats() {
416
- return {
417
- totalFacts: this.facts.length,
418
- totalLinks: this.links.length,
419
- uniqueEntities: this.eavIndex.size,
420
- uniqueAttributes: this.aevIndex.size,
421
- catalogEntries: this.catalog.size,
422
- };
423
- }
424
-
425
- /**
426
- * Creates a serializable snapshot of the current store state.
427
- */
428
- snapshot(): { facts: Fact[]; links: Link[]; catalog: CatalogEntry[] } {
429
- return {
430
- facts: this.facts.filter((f) => f !== undefined),
431
- links: [...this.links],
432
- catalog: this.getCatalog(),
433
- };
434
- }
435
-
436
- /**
437
- * Restores the store state from a snapshot and rebuilds all indexes.
438
- */
439
- restore(snapshot: {
440
- facts: Fact[];
441
- links: Link[];
442
- catalog: CatalogEntry[];
443
- }): void {
444
- // Clear current state
445
- this.facts = [];
446
- this.links = [];
447
- this.catalog.clear();
448
- this.eavIndex.clear();
449
- this.aevIndex.clear();
450
- this.aveIndex.clear();
451
- this.linkIndex.clear();
452
- this.linkReverseIndex.clear();
453
- this.linkAttrIndex.clear();
454
- this.distinct.clear();
455
-
456
- // Re-add data
457
- this.addFacts(snapshot.facts);
458
- this.addLinks(snapshot.links);
459
-
460
- // Explicitly restore catalog if provided (though addFacts rebuilds it partially)
461
- if (snapshot.catalog) {
462
- for (const entry of snapshot.catalog) {
463
- this.catalog.set(entry.attribute, entry);
464
- }
465
- }
466
- }
467
- }
@@ -1,136 +0,0 @@
1
- /**
2
- * Decision Auto-Capture Middleware
3
- *
4
- * Wraps MCP tool handlers to automatically emit vcs:decisionRecord ops
5
- * for every tool invocation. Pre/post hooks can enrich the trace with
6
- * rationale, alternatives, and prompt context.
7
- */
8
-
9
- import type { HookRegistry } from './hooks.js';
10
- import type { DecisionInput, DecisionContext } from './types.js';
11
-
12
- // ---------------------------------------------------------------------------
13
- // Types
14
- // ---------------------------------------------------------------------------
15
-
16
- /** A generic MCP tool handler: receives params, returns a result. */
17
- export type ToolHandler = (params: Record<string, unknown>) => Promise<unknown>;
18
-
19
- /** Callback invoked after auto-capture builds the DecisionInput. */
20
- export type DecisionRecorder = (decision: DecisionInput) => Promise<void>;
21
-
22
- export interface AutoCaptureOpts {
23
- /** The hook registry for pre/post enrichment. */
24
- hooks: HookRegistry;
25
- /** Called to persist the decision as a VcsOp. */
26
- recorder: DecisionRecorder;
27
- /** Tool names to exclude from auto-capture (e.g. read-only queries). */
28
- exclude?: Set<string>;
29
- }
30
-
31
- // ---------------------------------------------------------------------------
32
- // Wrapper
33
- // ---------------------------------------------------------------------------
34
-
35
- /**
36
- * Wrap an MCP tool handler for automatic decision trace capture.
37
- */
38
- export function wrapToolHandler(
39
- toolName: string,
40
- handler: ToolHandler,
41
- opts: AutoCaptureOpts,
42
- ): ToolHandler {
43
- return async (params: Record<string, unknown>) => {
44
- // Skip excluded tools
45
- if (opts.exclude?.has(toolName)) {
46
- return handler(params);
47
- }
48
-
49
- // Run pre-hooks
50
- const preContext: DecisionContext = await opts.hooks.runPreHooks(
51
- toolName,
52
- params,
53
- );
54
-
55
- // Execute the actual tool
56
- const result = await handler(params);
57
-
58
- // Run post-hooks
59
- const enrichment = await opts.hooks.runPostHooks(
60
- toolName,
61
- params,
62
- result,
63
- preContext,
64
- );
65
-
66
- // Build the decision input
67
- const decision: DecisionInput = {
68
- toolName,
69
- input: sanitizeInput(params),
70
- outputSummary: summarize(result),
71
- context: preContext.prompt ?? preContext.conversationId,
72
- rationale: enrichment.rationale,
73
- alternatives: enrichment.alternatives,
74
- confidence: enrichment.confidence,
75
- relatedEntities: enrichment.relatedEntities,
76
- custom: {
77
- ...preContext.custom,
78
- ...enrichment.custom,
79
- agentModel: preContext.agentModel,
80
- },
81
- };
82
-
83
- // Record asynchronously — don't block tool response
84
- opts.recorder(decision).catch(() => {
85
- // Silently ignore recording failures
86
- });
87
-
88
- return result;
89
- };
90
- }
91
-
92
- // ---------------------------------------------------------------------------
93
- // Helpers
94
- // ---------------------------------------------------------------------------
95
-
96
- /**
97
- * Remove potentially large/sensitive fields from tool input before storing.
98
- */
99
- function sanitizeInput(
100
- params: Record<string, unknown>,
101
- ): Record<string, unknown> {
102
- const sanitized: Record<string, unknown> = {};
103
- for (const [key, value] of Object.entries(params)) {
104
- if (typeof value === 'string' && value.length > 2000) {
105
- sanitized[key] = value.slice(0, 2000) + '…';
106
- } else {
107
- sanitized[key] = value;
108
- }
109
- }
110
- return sanitized;
111
- }
112
-
113
- /**
114
- * Summarize a tool result to a concise string (max 500 chars).
115
- */
116
- function summarize(result: unknown): string {
117
- if (result === null || result === undefined) return '';
118
-
119
- // MCP-style { content: [{ type: 'text', text: '...' }] }
120
- if (typeof result === 'object' && result !== null && 'content' in result) {
121
- const content = (result as any).content;
122
- if (Array.isArray(content)) {
123
- const texts = content
124
- .filter((c: any) => c.type === 'text')
125
- .map((c: any) => c.text)
126
- .join('\n');
127
- return texts.length > 500 ? texts.slice(0, 500) + '…' : texts;
128
- }
129
- }
130
-
131
- const str =
132
- typeof result === 'string' ? result : JSON.stringify(result, null, 0);
133
- return str.length > 500 ? str.slice(0, 500) + '…' : str;
134
- }
135
-
136
- export { summarize as _summarizeForTest };