pi-smart-compact 7.9.0 → 7.9.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,34 @@
1
1
  # Changelog
2
2
 
3
+ ## [7.9.1] - 2026-05-17
4
+
5
+ ### Fixed
6
+ - **`/smart-compact` race condition** — Added `waitForIdle()` to slash command handler. Previously the command could execute concurrently with agent streaming since extension commands bypass the input queue.
7
+ - **Tool `skipCompact` safety** — `ctx.compact()` internally calls `this.abort()`, which would kill the running agent loop if called from within a tool. Tool path now correctly keeps `skipCompact: true` and caches summary in `pendingRef` for the next natural compact.
8
+ - **Tool context-usage guard** — Tool now checks `getContextUsage()` before running and returns early with token info if context is too small.
9
+ - **Tool description enriched** — Better description and parameter hints so the agent knows when and how to call `smart_compact`.
10
+
11
+ ## [7.9.0] - 2026-05-17
12
+
13
+ ### Fixed
14
+ - **40 TypeScript strict-mode errors resolved** — `bunx tsc --noEmit` now passes cleanly with zero errors against latest `@earendil-works/pi-ai`, `pi-coding-agent`, `pi-tui` peer types.
15
+ - **`notify()` level mismatch** — `"success"` is not a valid level in `ExtensionUIContext.notify()`. All instances mapped to `"info"`.
16
+ - **`UserMessage.timestamp` mandatory** — 10 inline message objects across `explore.ts`, `synthesize.ts`, `verify.ts` were missing the required `timestamp: Date.now()` field.
17
+ - **`AgentToolResult.details` mandatory** — All tool return objects in `index.ts` now include explicit `details: undefined`.
18
+ - **`SelectList.selectedIndex` private accessor** — Replaced with `setSelectedIndex()` in `overlays.ts`.
19
+ - **`ThemeColor` union vs arbitrary string** — `theme.fg()` cast to `(c: string, t: string) => string` in overlays for dynamic color lookup.
20
+ - **`CacheAwareOptions` not assignable to `ProviderStreamOptions`** — Added explicit cast in `cache.ts`.
21
+ - **`blocks: unknown` after `Array.isArray`** — Explicit `unknown[]` typing in `extraction.ts` and `pruning.ts`.
22
+ - **`ExtensionContext` vs `ExtensionCommandContext`** — Proper `as unknown as` double-cast in `index.ts` for tool and hook contexts.
23
+ - **`apiKey?: string` (optional) used as `string`** — Extracted `apiKey` and `apiHeaders` as separate non-optional variables after guard check in `core.ts`.
24
+ - **`pendingRef.value` type narrowed to `never`** — TypeScript control flow after `pendingRef.value = null` prevented re-reading after `runSmartCompact`. Fixed with explicit cast.
25
+
26
+ ### Changed
27
+ - **`/smart-compact` command now uses `waitForIdle()`** — Prevents race condition when slash command is entered while agent is streaming. Agent finishes current turn before compaction starts.
28
+ - **Tool (`smart_compact`) keeps `skipCompact: true`** — Mid-turn compaction via `ctx.compact()` would abort the running agent loop (`this.abort()` internally). Tool prepares summary in `pendingRef` for the next natural compact to apply.
29
+ - **Tool description enriched** — Better description, parameter descriptions, and context-usage guard help the agent decide when to call smart_compact.
30
+ - **README.md updated** — version 7.9.0, module count 18, line count ~5,091, typecheck status now passing, added `logger.ts` and `type-guards.ts` to source table.
31
+
3
32
  ## [7.8.0] - 2026-05-17
4
33
 
5
34
  ### Fixed
package/README.md CHANGED
@@ -57,12 +57,12 @@ The extension keeps a short-lived pending compaction in memory, then hands that
57
57
 
58
58
  **Observed from the current codebase (`README`, `src/`, `test/`, `package.json`)**
59
59
 
60
- - **Package version:** `7.8.0`
60
+ - **Package version:** `7.9.1`
61
61
  - **Runtime entrypoint:** `dist/index.js`
62
62
  - **Source entrypoint:** `src/index.ts`
63
- - **Source modules:** 16 TypeScript files under `src/`
63
+ - **Source modules:** 18 TypeScript files under `src/`
64
64
  - **Tests:** 9 test files, **93 passing tests**
65
- - **Approx repo footprint:** ~5,053 lines across `src/` + `test/`
65
+ - **Approx repo footprint:** ~5,091 lines across `src/` + `test/`
66
66
  - **Documentation asset:** `docs/assets/pi-smart-compact.png`
67
67
  - **Published package files:** `dist/`, `docs/`, `README.md`, `LICENSE`, `CHANGELOG.md`
68
68
 
@@ -72,9 +72,9 @@ The extension keeps a short-lived pending compaction in memory, then hands that
72
72
  | --- | --- | --- |
73
73
  | `bun test` | ✅ Pass | 93/93 tests passing |
74
74
  | `bun run build` | ✅ Pass | Bundles `src/index.ts` to `dist/index.js` |
75
- | `bun run typecheck` | ⚠️ Fails | Current repo has TypeScript compatibility issues against the installed Pi typings / `.ts` import style |
75
+ | `bun run typecheck` | Pass | Strict TypeScript compatibility clean |
76
76
 
77
- So the project is currently **buildable and tested**, but **not fully typecheck-clean**.
77
+ So the project is currently **buildable, tested, and typecheck-clean**.
78
78
 
79
79
  ---
80
80
 
@@ -348,11 +348,13 @@ That pending summary is valid for **5 minutes** and is consumed by:
348
348
  | `src/utils/cache.ts` | Metrics, cache-aware LLM options, extraction cache |
349
349
  | `src/utils/damage.ts` | Post-compaction regression signal detection |
350
350
  | `src/utils/extraction.ts` | Deterministic extraction and open-loop detection |
351
+ | `src/utils/logger.ts` | Centralized logging with shared prefix |
351
352
  | `src/utils/fingerprint.ts` | Cross-session project fingerprinting |
352
353
  | `src/utils/helpers.ts` | Config loading, backups, batching, prompt helpers |
353
354
  | `src/utils/pruning.ts` | Redundancy pruning before compaction |
354
355
  | `src/utils/state.ts` | Compaction state persistence and delta logic |
355
356
  | `src/utils/tokens.ts` | Provider capabilities and token estimation |
357
+ | `src/utils/type-guards.ts` | Shared type guard functions |
356
358
 
357
359
  ### Tests
358
360
 
@@ -589,10 +591,9 @@ The current codebase includes these safeguards:
589
591
 
590
592
  To keep this README aligned with the repository's **actual** current state:
591
593
 
592
- 1. **`bun run typecheck` is currently failing.** The repo builds with Bun and passes tests, but strict TypeScript compatibility is not clean right now.
593
- 2. **One legacy test filename remains:** `test/semantic-compact.test.ts`.
594
- 3. **The package is published from `dist/`, not directly from `src/`.** Source and tests are not included in the package tarball.
595
- 4. **The extension depends on Pi runtime APIs and peer packages** (`@earendil-works/pi-ai`, `@earendil-works/pi-coding-agent`, `@earendil-works/pi-tui`, `typebox`).
594
+ 1. **One legacy test filename remains:** `test/semantic-compact.test.ts`.
595
+ 2. **The package is published from `dist/`**, not directly from `src/`. Source and tests are not included in the package tarball.
596
+ 3. **The extension depends on Pi runtime APIs and peer packages** (`@earendil-works/pi-ai`, `@earendil-works/pi-coding-agent`, `@earendil-works/pi-tui`, `typebox`).
596
597
 
597
598
  ---
598
599
 
@@ -629,7 +630,7 @@ rm -rf dist && mkdir dist && bun build ./src/index.ts --outdir ./dist --target b
629
630
  bun run typecheck
630
631
  ```
631
632
 
632
- At the moment, this command is expected to report errors.
633
+ At the moment, this command passes cleanly.
633
634
 
634
635
  ### Typical local path inside Pi
635
636
 
@@ -2,7 +2,7 @@
2
2
  * Constants, prompts, and profile defaults.
3
3
  */
4
4
  import type { CompressionProfile, ProfileConfig } from "./types.ts";
5
- export declare const VERSION = "7.9.0";
5
+ export declare const VERSION = "7.9.1";
6
6
  export declare const CHARS_PER_TOKEN = 3.8;
7
7
  export declare const COMPACT_SYSTEM_PREFIX: string;
8
8
  export declare const PROFILES: Record<CompressionProfile, ProfileConfig>;
@@ -1 +1 @@
1
- {"version":3,"file":"core.d.ts","sourceRoot":"","sources":["../src/core.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,iCAAiC,CAAC;AAE/E,OAAO,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,KAAK,EACV,kBAAkB,EAAE,iBAAiB,EAEtC,MAAM,YAAY,CAAC;AAsBpB,yEAAyE;AACzE,MAAM,WAAW,mBAAmB;IAClC,GAAG,EAAE,uBAAuB,CAAC;IAC7B,YAAY,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC;IACzB,QAAQ,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC;IACrB,OAAO,EAAE,kBAAkB,CAAC;IAC5B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,UAAU,EAAE;QAAE,KAAK,EAAE,iBAAiB,GAAG,IAAI,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC;IACnE,SAAS,EAAE;QAAE,KAAK,EAAE,OAAO,CAAA;KAAE,CAAC;IAC9B,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,wBAAsB,eAAe,CAAC,IAAI,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC,CA8V9E"}
1
+ {"version":3,"file":"core.d.ts","sourceRoot":"","sources":["../src/core.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,iCAAiC,CAAC;AAE/E,OAAO,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,KAAK,EACV,kBAAkB,EAAE,iBAAiB,EAEtC,MAAM,YAAY,CAAC;AAsBpB,yEAAyE;AACzE,MAAM,WAAW,mBAAmB;IAClC,GAAG,EAAE,uBAAuB,CAAC;IAC7B,YAAY,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC;IACzB,QAAQ,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC;IACrB,OAAO,EAAE,kBAAkB,CAAC;IAC5B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,UAAU,EAAE;QAAE,KAAK,EAAE,iBAAiB,GAAG,IAAI,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC;IACnE,SAAS,EAAE;QAAE,KAAK,EAAE,OAAO,CAAA;KAAE,CAAC;IAC9B,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,wBAAsB,eAAe,CAAC,IAAI,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC,CAgW9E"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,YAAY,EAA2B,MAAM,iCAAiC,CAAC;AAwC7F,MAAM,CAAC,OAAO,UAAU,qBAAqB,CAAC,EAAE,EAAE,YAAY,QA0H7D"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,YAAY,EAA2B,MAAM,iCAAiC,CAAC;AAwC7F,MAAM,CAAC,OAAO,UAAU,qBAAqB,CAAC,EAAE,EAAE,YAAY,QA2I7D"}
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  // @bun
2
2
  // src/constants.ts
3
- var VERSION = "7.9.0";
3
+ var VERSION = "7.9.1";
4
4
  var CHARS_PER_TOKEN = 3.8;
5
5
  var COMPACT_SYSTEM_PREFIX = "You are an expert conversation summarizer for a coding agent. " + "Produce structured markdown summaries. " + "Follow output format exactly. " + "Use EXACT names \u2014 never paraphrase code identifiers. " + "Trust deterministic extraction data over intuition.";
6
6
  var PROFILES = {
@@ -197,6 +197,33 @@ function debug(msg, ...args) {
197
197
  }
198
198
 
199
199
  // src/utils/helpers.ts
200
+ function validateSmartCompactConfig(sc) {
201
+ const VALID_PROFILES = ["light", "balanced", "aggressive"];
202
+ if ("profile" in sc && !VALID_PROFILES.includes(sc.profile)) {
203
+ warn("smart-compact config: invalid profile '" + sc.profile + "', expected light|balanced|aggressive. Using default 'balanced'.");
204
+ delete sc.profile;
205
+ }
206
+ if ("autoTrigger" in sc && typeof sc.autoTrigger !== "boolean") {
207
+ warn("smart-compact config: autoTrigger must be boolean, got " + typeof sc.autoTrigger);
208
+ delete sc.autoTrigger;
209
+ }
210
+ if ("backupEnabled" in sc && typeof sc.backupEnabled !== "boolean") {
211
+ warn("smart-compact config: backupEnabled must be boolean, got " + typeof sc.backupEnabled);
212
+ delete sc.backupEnabled;
213
+ }
214
+ if ("summaryModel" in sc && sc.summaryModel !== null && typeof sc.summaryModel !== "string") {
215
+ warn("smart-compact config: summaryModel must be string|null, got " + typeof sc.summaryModel);
216
+ delete sc.summaryModel;
217
+ }
218
+ if ("segmentationModel" in sc && sc.segmentationModel !== null && typeof sc.segmentationModel !== "string") {
219
+ warn("smart-compact config: segmentationModel must be string|null, got " + typeof sc.segmentationModel);
220
+ delete sc.segmentationModel;
221
+ }
222
+ if ("profiles" in sc && (typeof sc.profiles !== "object" || sc.profiles === null || Array.isArray(sc.profiles))) {
223
+ warn("smart-compact config: profiles must be an object, got " + typeof sc.profiles);
224
+ delete sc.profiles;
225
+ }
226
+ }
200
227
  var _cfg = null;
201
228
  var _cfgMtime = 0;
202
229
  function loadConfig() {
@@ -207,6 +234,7 @@ function loadConfig() {
207
234
  return _cfg;
208
235
  const raw = JSON.parse(fs.readFileSync(p, "utf-8"));
209
236
  const sc = raw[CONFIG_KEY] ?? raw[CONFIG_KEY_ALT] ?? {};
237
+ validateSmartCompactConfig(sc);
210
238
  const merged = { ...DEFAULT_CONFIG, ...sc };
211
239
  if (sc.profiles)
212
240
  merged.profiles = { ...PROFILES, ...sc.profiles };
@@ -372,6 +400,24 @@ function buildExtractionContext(extraction, forRange) {
372
400
  ].join(`
373
401
  `);
374
402
  }
403
+ function inferSessionType(extraction, report) {
404
+ if (report?.sessionType)
405
+ return report.sessionType;
406
+ const hasModifications = extraction.modifiedFiles.length > 0;
407
+ const hasUnresolvedErrors = extraction.errors.some((e) => !e.resolved);
408
+ const hasResolvedErrors = extraction.errors.some((e) => e.resolved);
409
+ const hasReadsOnly = extraction.readFiles.length > 2 && !hasModifications;
410
+ const hasDecisions = extraction.decisions.length > 0;
411
+ if (hasUnresolvedErrors && (hasModifications || hasResolvedErrors))
412
+ return "debugging";
413
+ if (hasReadsOnly && !hasDecisions)
414
+ return "review";
415
+ if (hasDecisions && !hasModifications && !hasUnresolvedErrors)
416
+ return "discussion";
417
+ if (hasModifications)
418
+ return "implementation";
419
+ return "implementation";
420
+ }
375
421
  function buildExplorationContext(report) {
376
422
  if (!report.mainGoal && !report.crossReferences.length && !report.enrichedConstraints.length)
377
423
  return "";
@@ -649,15 +695,28 @@ function loadCachedExtraction(sessionId) {
649
695
  }
650
696
  }
651
697
  function mergeExtractions(base, delta, baseMsgCount) {
698
+ const offsetErrors = delta.errors.map((e) => ({ ...e, index: e.index + baseMsgCount }));
699
+ const offsetDecisions = delta.decisions.map((d) => ({ ...d, index: d.index + baseMsgCount }));
700
+ const offsetConstraints = delta.constraints.map((c) => ({ ...c, index: c.index + baseMsgCount }));
701
+ const offsetTopics = delta.topics.map((t) => ({
702
+ ...t,
703
+ startIndex: t.startIndex + baseMsgCount,
704
+ endIndex: t.endIndex + baseMsgCount
705
+ }));
706
+ const offsetTimeline = delta.timeline.map((t) => ({ ...t, index: t.index + baseMsgCount }));
707
+ const offsetModifiedFiles = delta.modifiedFiles.map((f) => ({
708
+ ...f,
709
+ lastModifiedIndex: f.lastModifiedIndex + baseMsgCount
710
+ }));
652
711
  return {
653
- modifiedFiles: [...new Map([...base.modifiedFiles, ...delta.modifiedFiles].map((f) => [f.path, f])).values()],
712
+ modifiedFiles: [...new Map([...base.modifiedFiles, ...offsetModifiedFiles].map((f) => [f.path, f])).values()],
654
713
  readFiles: [...new Set([...base.readFiles, ...delta.readFiles])],
655
714
  deletedFiles: [...new Set([...base.deletedFiles, ...delta.deletedFiles])],
656
- errors: [...base.errors, ...delta.errors],
657
- decisions: [...base.decisions, ...delta.decisions],
658
- constraints: [...base.constraints, ...delta.constraints],
659
- topics: [...base.topics, ...delta.topics],
660
- timeline: [...base.timeline, ...delta.timeline],
715
+ errors: [...base.errors, ...offsetErrors],
716
+ decisions: [...base.decisions, ...offsetDecisions],
717
+ constraints: [...base.constraints, ...offsetConstraints],
718
+ topics: [...base.topics, ...offsetTopics],
719
+ timeline: [...base.timeline, ...offsetTimeline],
661
720
  mainGoal: delta.mainGoal ?? base.mainGoal,
662
721
  lastUserMessages: delta.lastUserMessages.length > 0 ? delta.lastUserMessages : base.lastUserMessages,
663
722
  lastErrors: delta.lastErrors.length > 0 ? delta.lastErrors : base.lastErrors,
@@ -782,7 +841,8 @@ function catalogErrors(msgs, _tcIdx) {
782
841
  for (const err of errors) {
783
842
  for (let j = err.index + 1;j < Math.min(msgs.length, err.index + 6); j++) {
784
843
  if (msgs[j]?.role === "assistant") {
785
- const blocks = Array.isArray(msgs[j]?.content) ? msgs[j].content : [];
844
+ const rawBlocks = msgs[j]?.content;
845
+ const blocks = Array.isArray(rawBlocks) ? rawBlocks : [];
786
846
  for (const b of blocks) {
787
847
  if (isToolCallBlock(b) && b.name === err.tool) {
788
848
  err.retryAttempted = true;
@@ -1123,7 +1183,7 @@ function buildCompactionState(extraction, openLoops, report, nextActions, critic
1123
1183
  })),
1124
1184
  nextActions,
1125
1185
  criticalContext,
1126
- sessionType: report?.sessionType ?? "implementation",
1186
+ sessionType: inferSessionType(extraction, report),
1127
1187
  compactionVersion: VERSION,
1128
1188
  updatedAt: Date.now()
1129
1189
  };
@@ -1312,7 +1372,8 @@ function pruneRedundant(msgs) {
1312
1372
  for (let idx = 0;idx < msgs.length; idx++) {
1313
1373
  if (msgs[idx].role !== "assistant")
1314
1374
  continue;
1315
- const blocks = Array.isArray(msgs[idx].content) ? msgs[idx].content : [];
1375
+ const rawBlocks = msgs[idx].content;
1376
+ const blocks = Array.isArray(rawBlocks) ? rawBlocks : [];
1316
1377
  const hasToolCall = blocks.some((b) => isToolCallBlock(b));
1317
1378
  if (hasToolCall)
1318
1379
  continue;
@@ -1390,22 +1451,73 @@ var FRAMEWORK_SIGNALS = [
1390
1451
  function getFingerprintPath(projectId) {
1391
1452
  return path5.join(FINGERPRINT_DIR, projectId + ".json");
1392
1453
  }
1454
+ var NOISE_PATH_RE = /(?:node_modules|[/\\]\.pi[/\\]agent|[/\\]\.cache|[/\\]\.npm|[/\\]\.bun|[/\\]\.git[/\\])/;
1455
+ function isProjectPath(filePath) {
1456
+ return !NOISE_PATH_RE.test(filePath);
1457
+ }
1458
+ function hashProjectId(seed) {
1459
+ return "proj-" + crypto3.createHash("sha256").update(seed).digest("hex").slice(0, 12);
1460
+ }
1461
+ function deriveFromAbsolutePaths(paths) {
1462
+ const segments = paths.map((p) => p.replace(/^\/+/, "").split("/").filter(Boolean)).filter((s) => s.length >= 3);
1463
+ if (segments.length < 2)
1464
+ return deriveFromRelativePaths(paths);
1465
+ const dirCounts = new Map;
1466
+ for (const segs of segments) {
1467
+ for (let depth = 3;depth <= segs.length; depth++) {
1468
+ const prefix = segs.slice(0, depth).join("/");
1469
+ dirCounts.set(prefix, (dirCounts.get(prefix) ?? 0) + 1);
1470
+ }
1471
+ }
1472
+ const threshold = Math.ceil(segments.length * 0.5);
1473
+ const candidates = [...dirCounts.entries()].filter(([, count]) => count >= threshold).sort((a, b) => {
1474
+ const depthDiff = b[0].split("/").length - a[0].split("/").length;
1475
+ if (depthDiff !== 0)
1476
+ return depthDiff;
1477
+ return b[1] - a[1];
1478
+ });
1479
+ if (candidates.length)
1480
+ return hashProjectId(candidates[0][0]);
1481
+ const sorted = [...segments].sort((a, b) => a.length - b.length);
1482
+ let commonDepth = 0;
1483
+ for (let d = 0;d < sorted[0].length; d++) {
1484
+ if (segments.every((s) => s[d] === sorted[0][d]))
1485
+ commonDepth = d + 1;
1486
+ else
1487
+ break;
1488
+ }
1489
+ return hashProjectId(sorted[0].slice(0, Math.max(commonDepth, 3)).join("/"));
1490
+ }
1491
+ function deriveFromRelativePaths(paths) {
1492
+ const topEntries = [...new Set(paths.map((p) => p.split("/").filter(Boolean)[0]).filter(Boolean))].sort();
1493
+ const dir2Counts = new Map;
1494
+ for (const p of paths) {
1495
+ const segs = p.split("/").filter(Boolean);
1496
+ if (segs.length >= 2) {
1497
+ const d2 = segs.slice(0, 2).join("/");
1498
+ dir2Counts.set(d2, (dir2Counts.get(d2) ?? 0) + 1);
1499
+ }
1500
+ }
1501
+ const stableDirs = [...dir2Counts.entries()].sort((a, b) => b[1] - a[1]).slice(0, 8).map(([d]) => d).sort();
1502
+ const fingerprint = topEntries.join(",") + "|" + stableDirs.join(",");
1503
+ return hashProjectId(fingerprint);
1504
+ }
1393
1505
  function deriveProjectId(extraction) {
1394
1506
  const allPaths = [
1395
1507
  ...extraction.modifiedFiles.map((f) => f.path),
1396
1508
  ...extraction.readFiles
1397
- ];
1509
+ ].filter(isProjectPath);
1398
1510
  if (!allPaths.length)
1399
1511
  return "unknown";
1400
- const roots = new Map;
1401
- for (const p of allPaths) {
1402
- const parts = p.split("/");
1403
- const root = parts.length > 1 ? parts.slice(0, Math.min(2, parts.length - 1)).join("/") : "root";
1404
- roots.set(root, (roots.get(root) ?? 0) + 1);
1512
+ const absolutePaths = allPaths.filter((p) => p.startsWith("/"));
1513
+ const relativePaths = allPaths.filter((p) => !p.startsWith("/"));
1514
+ if (absolutePaths.length >= 2) {
1515
+ return deriveFromAbsolutePaths(absolutePaths);
1516
+ }
1517
+ if (relativePaths.length >= 2) {
1518
+ return deriveFromRelativePaths(relativePaths);
1405
1519
  }
1406
- const topRoot = [...roots.entries()].sort((a, b) => b[1] - a[1])[0]?.[0] ?? "unknown";
1407
- const hash = crypto3.createHash("sha256").update(topRoot).digest("hex").slice(0, 12);
1408
- return "proj-" + hash;
1520
+ return hashProjectId(allPaths.sort().join("|"));
1409
1521
  }
1410
1522
  function detectLanguage(extraction) {
1411
1523
  const extCounts = new Map;
@@ -1832,7 +1944,7 @@ async function exploreConversation(llmMessages, extraction, model, auth, prevSum
1832
1944
  }
1833
1945
  const probeResp = await trackedComplete("explore", model, {
1834
1946
  systemPrompt: COMPACT_SYSTEM_PREFIX,
1835
- messages: [{ role: "user", content: [{ type: "text", text: userContent }] }],
1947
+ messages: [{ role: "user", content: [{ type: "text", text: userContent }], timestamp: Date.now() }],
1836
1948
  tools: EXPLORATION_TOOLS
1837
1949
  }, cacheOpts({ apiKey: auth.apiKey, headers: auth.headers, signal }, model.provider));
1838
1950
  const toolCalls = probeResp.content.filter((c) => c.type === "toolCall");
@@ -1929,7 +2041,7 @@ User steering: ` + userNote : "");
1929
2041
  try {
1930
2042
  const resp = await trackedComplete("explore-retry", model, {
1931
2043
  systemPrompt: COMPACT_SYSTEM_PREFIX,
1932
- messages: [{ role: "user", content: [{ type: "text", text: retryPrompt }] }]
2044
+ messages: [{ role: "user", content: [{ type: "text", text: retryPrompt }], timestamp: Date.now() }]
1933
2045
  }, cacheOpts({ apiKey: auth.apiKey, headers: auth.headers, maxTokens: Math.min(4096, getProviderCaps(model.provider).maxOutputTokens), signal }, model.provider));
1934
2046
  const text = resp.content.filter((c) => c.type === "text").map((c) => c.text).join("").trim();
1935
2047
  return parseExplorationReport(text, llmMessages);
@@ -1967,7 +2079,7 @@ Output ONLY JSON: {"mainGoal":"...","sessionType":"implementation|review|debuggi
1967
2079
  try {
1968
2080
  const resp = await trackedComplete("explore-direct", model, {
1969
2081
  systemPrompt: COMPACT_SYSTEM_PREFIX,
1970
- messages: [{ role: "user", content: [{ type: "text", text: prompt }] }]
2082
+ messages: [{ role: "user", content: [{ type: "text", text: prompt }], timestamp: Date.now() }]
1971
2083
  }, cacheOpts({ apiKey: auth.apiKey, headers: auth.headers, maxTokens: Math.min(4096, getProviderCaps(model.provider).maxOutputTokens), signal }, model.provider));
1972
2084
  const text = resp.content.filter((c) => c.type === "text").map((c) => c.text).join(`
1973
2085
  `).trim();
@@ -2042,7 +2154,7 @@ function chunkLlmMessages(msgs, boundaries, pc) {
2042
2154
  async function singlePassCompact(convText, extraction, report, prevContext, model, auth, signal) {
2043
2155
  const extractionCtx = buildExtractionContext(extraction);
2044
2156
  const explorationCtx = report ? buildExplorationContext(report) : "";
2045
- const sessionType = report?.sessionType ?? "implementation";
2157
+ const sessionType = inferSessionType(extraction, report);
2046
2158
  const sessionInstruction = SESSION_TYPE_INSTRUCTIONS[sessionType] ?? SESSION_TYPE_INSTRUCTIONS.implementation;
2047
2159
  const adaptedPrefix = SINGLE_PASS_PREFIX + `
2048
2160
  Session-specific instructions:
@@ -2051,8 +2163,8 @@ Session-specific instructions:
2051
2163
  const resp = await trackedComplete("single-pass", model, {
2052
2164
  systemPrompt: COMPACT_SYSTEM_PREFIX,
2053
2165
  messages: [
2054
- { role: "user", content: [{ type: "text", text: adaptedPrefix }] },
2055
- { role: "user", content: [{ type: "text", text: dynamicSuffix }] }
2166
+ { role: "user", content: [{ type: "text", text: adaptedPrefix }], timestamp: Date.now() },
2167
+ { role: "user", content: [{ type: "text", text: dynamicSuffix }], timestamp: Date.now() }
2056
2168
  ]
2057
2169
  }, cacheOpts({ apiKey: auth.apiKey, headers: auth.headers, maxTokens: getProviderCaps(model.provider).maxOutputTokens, signal }, model.provider));
2058
2170
  const summary = resp.content.filter((c) => c.type === "text").map((c) => c.text).join(`
@@ -2082,8 +2194,8 @@ async function summarizeBatch(batch, extraction, model, auth, signal) {
2082
2194
  const resp = await trackedComplete("batch", model, {
2083
2195
  systemPrompt: COMPACT_SYSTEM_PREFIX,
2084
2196
  messages: [
2085
- { role: "user", content: [{ type: "text", text: BATCH_PROMPT_PREFIX }] },
2086
- { role: "user", content: [{ type: "text", text: dynamicSuffix }] }
2197
+ { role: "user", content: [{ type: "text", text: BATCH_PROMPT_PREFIX }], timestamp: Date.now() },
2198
+ { role: "user", content: [{ type: "text", text: dynamicSuffix }], timestamp: Date.now() }
2087
2199
  ]
2088
2200
  }, cacheOpts({ apiKey: auth.apiKey, headers: auth.headers, maxTokens: Math.min(4096, getProviderCaps(model.provider).maxOutputTokens), signal }, model.provider));
2089
2201
  const output = resp.content.filter((c) => c.type === "text").map((c) => c.text).join(`
@@ -2123,8 +2235,8 @@ async function assembleLLM(summaries, extraction, report, model, auth, budget, p
2123
2235
  const resp = await trackedComplete("assemble", model, {
2124
2236
  systemPrompt: COMPACT_SYSTEM_PREFIX,
2125
2237
  messages: [
2126
- { role: "user", content: [{ type: "text", text: ASSEMBLY_PROMPT_PREFIX }] },
2127
- { role: "user", content: [{ type: "text", text: dynamicSuffix }] }
2238
+ { role: "user", content: [{ type: "text", text: ASSEMBLY_PROMPT_PREFIX }], timestamp: Date.now() },
2239
+ { role: "user", content: [{ type: "text", text: dynamicSuffix }], timestamp: Date.now() }
2128
2240
  ]
2129
2241
  }, cacheOpts({ apiKey: auth.apiKey, headers: auth.headers, maxTokens: Math.min(budget, getProviderCaps(model.provider).maxOutputTokens), signal }, model.provider));
2130
2242
  return resp.content.filter((c) => c.type === "text").map((c) => c.text).join(`
@@ -2337,7 +2449,7 @@ Return the COMPLETE updated summary with missing items integrated. Keep the same
2337
2449
  try {
2338
2450
  const resp = await trackedComplete("patch", model, {
2339
2451
  systemPrompt: COMPACT_SYSTEM_PREFIX,
2340
- messages: [{ role: "user", content: [{ type: "text", text: patchPrompt }] }]
2452
+ messages: [{ role: "user", content: [{ type: "text", text: patchPrompt }], timestamp: Date.now() }]
2341
2453
  }, cacheOpts({ apiKey: auth.apiKey, headers: auth.headers, maxTokens: 8192, signal }));
2342
2454
  const patched = resp.content.filter((c) => c.type === "text").map((c) => c.text).join(`
2343
2455
  `).trim();
@@ -2398,7 +2510,7 @@ async function selectModel(ctx, opts) {
2398
2510
  scrollInfo: (t) => theme.fg("dim", t),
2399
2511
  noMatch: (t) => theme.fg("warning", t)
2400
2512
  });
2401
- sel.selectedIndex = opts.defaultModelIndex;
2513
+ sel.setSelectedIndex(opts.defaultModelIndex);
2402
2514
  sel.onSelect = (item) => done(item.value);
2403
2515
  sel.onCancel = () => done(null);
2404
2516
  c.addChild(sel);
@@ -2447,7 +2559,7 @@ async function selectProfile(ctx, selectedModel, opts) {
2447
2559
  scrollInfo: (t) => theme.fg("dim", t),
2448
2560
  noMatch: (t) => theme.fg("warning", t)
2449
2561
  });
2450
- sel.selectedIndex = 1;
2562
+ sel.setSelectedIndex(1);
2451
2563
  sel.onSelect = (item) => done(item.value);
2452
2564
  sel.onCancel = () => done(null);
2453
2565
  c.addChild(sel);
@@ -2472,8 +2584,7 @@ function showProgressOverlay(ctx, state) {
2472
2584
  const progress = Math.round(state.phase / 4 * 100);
2473
2585
  const name = phaseNames[state.phase - 1] ?? "?";
2474
2586
  const detail = state.detail ? " (" + state.detail + ")" : "";
2475
- const type = state.phase >= 4 ? "success" : "info";
2476
- ctx.ui.notify("EESV [" + progress + "%] Phase " + state.phase + "/4: " + name + detail, type);
2587
+ ctx.ui.notify("EESV [" + progress + "%] Phase " + state.phase + "/4: " + name + detail, state.phase >= 4 ? "info" : "info");
2477
2588
  }
2478
2589
  async function showResultScreen(ctx, details, extraction) {
2479
2590
  await ctx.ui.custom((tui, theme, _kb, done) => {
@@ -2615,6 +2726,8 @@ async function runSmartCompact(opts) {
2615
2726
  ctx.ui.notify("Auth failed", "error");
2616
2727
  return;
2617
2728
  }
2729
+ const apiKey = auth.apiKey;
2730
+ const apiHeaders = auth.headers;
2618
2731
  const usage = ctx.getContextUsage();
2619
2732
  const totalTokens = usage?.tokens ?? 0;
2620
2733
  if (!totalTokens || totalTokens < MIN_TOKEN_THRESHOLD) {
@@ -2624,7 +2737,7 @@ async function runSmartCompact(opts) {
2624
2737
  return;
2625
2738
  }
2626
2739
  const notify = (msg, type = "info") => {
2627
- ctx.ui.notify(msg, type);
2740
+ ctx.ui.notify(msg, type === "success" ? "info" : type);
2628
2741
  };
2629
2742
  const ctrl = new AbortController;
2630
2743
  const signal = ctrl.signal;
@@ -2654,7 +2767,7 @@ async function runSmartCompact(opts) {
2654
2767
  isRunning.value = false;
2655
2768
  return;
2656
2769
  }
2657
- const firstKeptId = msgs[keepFrom]?.id ?? msgs[msgs.length - 1]?.id ?? "";
2770
+ const firstKeptId = msgs[keepFrom]?.id ?? msgs[msgs.length - 1]?.id;
2658
2771
  if (!autoTriggered) {
2659
2772
  showProgressOverlay(ctx, { phase: 1, phaseName: "Extract", detail: "Preparing...", model: modelLabel, profile });
2660
2773
  }
@@ -2698,7 +2811,7 @@ async function runSmartCompact(opts) {
2698
2811
  if (!autoTriggered)
2699
2812
  showProgressOverlay(ctx, { phase: 2, phaseName: "Explore", detail: "Single-pass (" + convTokens.toLocaleString() + "t)", model: modelLabel, profile, extraction });
2700
2813
  try {
2701
- const r = await singlePassCompact(convText, extraction, null, prevContext + projectCtx, summaryModel, { apiKey: auth.apiKey, headers: auth.headers }, signal);
2814
+ const r = await singlePassCompact(convText, extraction, null, prevContext + projectCtx, summaryModel, { apiKey, headers: apiHeaders }, signal);
2702
2815
  finalSummary = r.summary;
2703
2816
  method = "single-pass";
2704
2817
  llmCalls = r.llmCalls;
@@ -2763,7 +2876,7 @@ async function runSmartCompact(opts) {
2763
2876
  const concurrency = caps.concurrencyLimit;
2764
2877
  if (totalBatches <= 1) {
2765
2878
  try {
2766
- summaries.push(...await summarizeBatch(batches[0], extraction, summaryModel, { apiKey: auth.apiKey, headers: auth.headers }, signal));
2879
+ summaries.push(...await summarizeBatch(batches[0], extraction, summaryModel, { apiKey, headers: apiHeaders }, signal));
2767
2880
  } catch (err) {
2768
2881
  summaries.push(...batches[0].map((ch) => ({
2769
2882
  topic: ch.topic,
@@ -2786,7 +2899,7 @@ async function runSmartCompact(opts) {
2786
2899
  const wavePromises = waveBatches.map(async (batch, i) => {
2787
2900
  const idx = wave + i;
2788
2901
  try {
2789
- results[idx] = await summarizeBatch(batch, extraction, summaryModel, { apiKey: auth.apiKey, headers: auth.headers }, signal);
2902
+ results[idx] = await summarizeBatch(batch, extraction, summaryModel, { apiKey, headers: apiHeaders }, signal);
2790
2903
  } catch (err) {
2791
2904
  errors[idx] = err instanceof Error ? err : new Error(String(err));
2792
2905
  results[idx] = batch.map((ch) => ({
@@ -2818,7 +2931,7 @@ async function runSmartCompact(opts) {
2818
2931
  showProgressOverlay(ctx, { phase: 3, phaseName: "Synthesize", detail: "Assembling...", model: modelLabel, profile, extraction, totalBatches: batches.length });
2819
2932
  let assemblyCalls = 1;
2820
2933
  try {
2821
- const r = await assembleLLM(summaries, extraction, explorationReport, summaryModel, { apiKey: auth.apiKey, headers: auth.headers }, pc.summaryBudgetTokens, prevContext, signal);
2934
+ const r = await assembleLLM(summaries, extraction, explorationReport, summaryModel, { apiKey, headers: apiHeaders }, pc.summaryBudgetTokens, prevContext, signal);
2822
2935
  if (r?.startsWith("##"))
2823
2936
  finalSummary = r;
2824
2937
  else
@@ -2842,7 +2955,7 @@ async function runSmartCompact(opts) {
2842
2955
  if (!recheck.ok && recheck.score < 75) {
2843
2956
  notify("Phase 4 Verify: deterministic patch insufficient (score=" + recheck.score + "), trying LLM patch", "warning");
2844
2957
  try {
2845
- finalSummary = await patchSummary(finalSummary, recheck.gaps, summaryModel, { apiKey: auth.apiKey, headers: auth.headers }, signal);
2958
+ finalSummary = await patchSummary(finalSummary, recheck.gaps, summaryModel, { apiKey, headers: apiHeaders }, signal);
2846
2959
  llmCalls++;
2847
2960
  } catch (err) {
2848
2961
  warn("LLM patch failed", err);
@@ -2941,7 +3054,7 @@ async function runSmartCompact(opts) {
2941
3054
  customInstructions: "Use pre-computed smart summary from /smart-compact",
2942
3055
  onComplete: () => {
2943
3056
  if (!autoTriggered)
2944
- ctx.ui.notify("Applied \u2713", "success");
3057
+ ctx.ui.notify("Applied \u2713", "info");
2945
3058
  },
2946
3059
  onError: (e) => {
2947
3060
  if (!autoTriggered)
@@ -2996,6 +3109,7 @@ function smartCompactExtension(pi) {
2996
3109
  return m.length ? m : null;
2997
3110
  },
2998
3111
  handler: async (args, ctx) => {
3112
+ await ctx.waitForIdle();
2999
3113
  try {
3000
3114
  const tokens = args.trim().split(/\s+/).filter(Boolean);
3001
3115
  const flags = tokens.map((t) => t.toLowerCase());
@@ -3072,11 +3186,11 @@ function smartCompactExtension(pi) {
3072
3186
  return;
3073
3187
  if (!isRunning.value) {
3074
3188
  await runSmartCompact({ ctx, summaryModel: sumModel, segModel: segModel ?? sumModel, profile: config.profile, pendingRef, isRunning, autoTriggered: true });
3075
- if (pendingRef.value) {
3076
- const c = pendingRef.value;
3189
+ const pending = pendingRef.value;
3190
+ if (pending) {
3077
3191
  pendingRef.value = null;
3078
3192
  pendingRef.createdAt = 0;
3079
- return { compaction: { summary: c.summary, firstKeptEntryId: c.firstKeptEntryId, tokensBefore: c.tokensBefore, details: c.details } };
3193
+ return { compaction: { summary: pending.summary, firstKeptEntryId: pending.firstKeptEntryId, tokensBefore: pending.tokensBefore, details: pending.details } };
3080
3194
  }
3081
3195
  }
3082
3196
  } catch (e) {
@@ -3086,15 +3200,15 @@ function smartCompactExtension(pi) {
3086
3200
  pi.registerTool({
3087
3201
  name: "smart_compact",
3088
3202
  label: "Smart Compact",
3089
- description: "EESV smart compaction v" + VERSION + " with deterministic extraction, exploration, and verification.",
3203
+ description: "EESV smart compaction v" + VERSION + " with deterministic extraction, exploration, and verification. Compacts the conversation into a structured summary preserving goals, decisions, open loops, modified files, and critical context. Call this when the conversation is getting long \u2014 the tool internally checks context usage and skips if not needed. Prefer this over default compact.",
3090
3204
  promptSnippet: "Smart compaction",
3091
- promptGuidelines: ["Use for long conversations.", "Prefer over default compact."],
3205
+ promptGuidelines: ["Use for long conversations. Prefer over default compact."],
3092
3206
  parameters: {
3093
3207
  type: "object",
3094
3208
  properties: {
3095
- profile: { type: "string", description: "light, balanced, or aggressive" },
3096
- verbose: { type: "boolean" },
3097
- dry_run: { type: "boolean" }
3209
+ profile: { type: "string", description: "light, balanced, or aggressive. Default: balanced." },
3210
+ verbose: { type: "boolean", description: "Show detailed pipeline output." },
3211
+ dry_run: { type: "boolean", description: "Run the pipeline but skip applying the compaction." }
3098
3212
  }
3099
3213
  },
3100
3214
  async execute(_id, params, _sig, _onUp, ctx) {
@@ -3103,25 +3217,29 @@ function smartCompactExtension(pi) {
3103
3217
  const dryRun = !!params.dry_run;
3104
3218
  const config = loadConfig();
3105
3219
  const resolvedProfile = profile ?? config.profile;
3220
+ const cmdCtx = ctx;
3221
+ const usage = ctx.getContextUsage?.();
3222
+ const totalTokens = usage?.tokens ?? 0;
3223
+ if (!totalTokens || totalTokens < MIN_TOKEN_THRESHOLD) {
3224
+ const pct = ctx.model && totalTokens ? Math.round(totalTokens / ctx.model.contextWindow * 100) : 0;
3225
+ return { content: [{ type: "text", text: "Context is not large enough for compaction (" + totalTokens.toLocaleString() + " tokens, " + pct + "%). No action needed." }], details: undefined };
3226
+ }
3106
3227
  const cur = "model" in ctx ? ctx.model : undefined;
3107
- const { segModel, sumModel } = resolveModels(ctx, cur, config);
3228
+ const { segModel, sumModel } = resolveModels(cmdCtx, cur, config);
3108
3229
  if (!sumModel) {
3109
- return { content: [{ type: "text", text: "Error: Could not resolve model." }] };
3230
+ return { content: [{ type: "text", text: "Error: Could not resolve model." }], details: undefined };
3110
3231
  }
3111
3232
  try {
3112
3233
  const toolStart = Date.now();
3113
- await runSmartCompact({ ctx, summaryModel: sumModel, segModel: segModel ?? sumModel, profile: resolvedProfile, verbose, dryRun, pendingRef, isRunning, autoTriggered: true, skipCompact: true });
3234
+ await runSmartCompact({ ctx: cmdCtx, summaryModel: sumModel, segModel: segModel ?? sumModel, profile: resolvedProfile, verbose, dryRun, pendingRef, isRunning, autoTriggered: true, skipCompact: true });
3114
3235
  const toolSecs = ((Date.now() - toolStart) / 1000).toFixed(1);
3115
3236
  if (pendingRef.value) {
3116
- return { content: [{ type: "text", text: "Smart summary generated (" + resolvedProfile + "). Tokens: " + (pendingRef.value.tokensBefore ?? "?") + " -> " + (pendingRef.value.summary?.length ?? 0) + " chars (" + toolSecs + `s).
3117
-
3118
- Now run tree compact to apply \u2014 the session_before_compact hook will use this summary.
3119
- TTL: ` + Math.round(PENDING_TTL_MS / 60000) + " minutes." }] };
3237
+ return { content: [{ type: "text", text: "Smart summary prepared (" + resolvedProfile + "). Tokens: " + (pendingRef.value.tokensBefore ?? 0).toLocaleString() + " \u2014 summary cached for " + Math.round(PENDING_TTL_MS / 60000) + " min. The next /compact will use this summary automatically." }], details: undefined };
3120
3238
  }
3121
- return { content: [{ type: "text", text: "Compaction finished (" + resolvedProfile + ") but no summary was generated." }] };
3239
+ return { content: [{ type: "text", text: "Compaction finished (" + resolvedProfile + ") but no summary was generated." }], details: undefined };
3122
3240
  } catch (error) {
3123
3241
  const msg = error instanceof Error ? error.message : String(error);
3124
- return { content: [{ type: "text", text: "Compaction error: " + msg }] };
3242
+ return { content: [{ type: "text", text: "Compaction error: " + msg }], details: undefined };
3125
3243
  }
3126
3244
  }
3127
3245
  });
@@ -1 +1 @@
1
- {"version":3,"file":"overlays.d.ts","sourceRoot":"","sources":["../../src/ui/overlays.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,iCAAiC,CAAC;AAI/E,OAAO,KAAK,EACV,kBAAkB,EAAE,WAAW,EAAE,aAAa,EAC9C,mBAAmB,EAAE,oBAAoB,EAC1C,MAAM,aAAa,CAAC;AAIrB,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,SAAK,GAAG,MAAM,CAM7F;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,SAAK,GAAG,MAAM,CAO5G;AAED,wBAAsB,WAAW,CAC/B,GAAG,EAAE,uBAAuB,EAC5B,IAAI,EAAE;IAAE,aAAa,EAAE,MAAM,CAAC;IAAC,cAAc,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,iBAAiB,EAAE,MAAM,CAAA;CAAE,GACvG,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CA8C7B;AAED,wBAAsB,aAAa,CACjC,GAAG,EAAE,uBAAuB,EAC5B,aAAa,EAAE,WAAW,EAC1B,IAAI,EAAE;IAAE,aAAa,EAAE,MAAM,CAAC;IAAC,cAAc,EAAE,MAAM,CAAA;CAAE,GACtD,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC,CA4CpC;AAED,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,uBAAuB,EAAE,KAAK,EAAE,aAAa,GAAG,IAAI,CAO5F;AAED,wBAAsB,gBAAgB,CACpC,GAAG,EAAE,uBAAuB,EAC5B,OAAO,EAAE,mBAAmB,EAC5B,UAAU,EAAE,oBAAoB,GAC/B,OAAO,CAAC,IAAI,CAAC,CAqJf;AAED,wBAAsB,aAAa,CACjC,GAAG,EAAE,uBAAuB,EAC5B,IAAI,EAAE;IAAE,aAAa,EAAE,MAAM,CAAC;IAAC,cAAc,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,iBAAiB,EAAE,MAAM,CAAA;CAAE,GACvG,OAAO,CAAC;IAAE,KAAK,EAAE,WAAW,CAAC;IAAC,OAAO,EAAE,kBAAkB,CAAA;CAAE,GAAG,IAAI,CAAC,CAMrE"}
1
+ {"version":3,"file":"overlays.d.ts","sourceRoot":"","sources":["../../src/ui/overlays.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,iCAAiC,CAAC;AAI/E,OAAO,KAAK,EACV,kBAAkB,EAAE,WAAW,EAAE,aAAa,EAC9C,mBAAmB,EAAE,oBAAoB,EAC1C,MAAM,aAAa,CAAC;AAIrB,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,SAAK,GAAG,MAAM,CAS7F;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,SAAK,GAAG,MAAM,CAO5G;AAED,wBAAsB,WAAW,CAC/B,GAAG,EAAE,uBAAuB,EAC5B,IAAI,EAAE;IAAE,aAAa,EAAE,MAAM,CAAC;IAAC,cAAc,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,iBAAiB,EAAE,MAAM,CAAA;CAAE,GACvG,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CA8C7B;AAED,wBAAsB,aAAa,CACjC,GAAG,EAAE,uBAAuB,EAC5B,aAAa,EAAE,WAAW,EAC1B,IAAI,EAAE;IAAE,aAAa,EAAE,MAAM,CAAC;IAAC,cAAc,EAAE,MAAM,CAAA;CAAE,GACtD,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC,CA4CpC;AAED,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,uBAAuB,EAAE,KAAK,EAAE,aAAa,GAAG,IAAI,CAM5F;AAED,wBAAsB,gBAAgB,CACpC,GAAG,EAAE,uBAAuB,EAC5B,OAAO,EAAE,mBAAmB,EAC5B,UAAU,EAAE,oBAAoB,GAC/B,OAAO,CAAC,IAAI,CAAC,CAsJf;AAED,wBAAsB,aAAa,CACjC,GAAG,EAAE,uBAAuB,EAC5B,IAAI,EAAE;IAAE,aAAa,EAAE,MAAM,CAAC;IAAC,cAAc,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,iBAAiB,EAAE,MAAM,CAAA;CAAE,GACvG,OAAO,CAAC;IAAE,KAAK,EAAE,WAAW,CAAC;IAAC,OAAO,EAAE,kBAAkB,CAAA;CAAE,GAAG,IAAI,CAAC,CAMrE"}
@@ -22,6 +22,17 @@ export declare function getMetricsSummary(): {
22
22
  export declare function trackedComplete(phase: LLMCallMetric["phase"], model: Model<Api>, reqBody: Context, opts: CacheAwareOptions): Promise<AssistantMessage>;
23
23
  export declare function saveCachedExtraction(sessionId: string, extraction: StructuredExtraction, msgCount: number): void;
24
24
  export declare function loadCachedExtraction(sessionId: string): CachedExtraction | null;
25
+ /**
26
+ * Merge a delta extraction into a base extraction, offsetting all
27
+ * index-bearing fields so they align with the global message array.
28
+ *
29
+ * When `extractStructured` is called on `msgs.slice(cached.lastMessageIndex + 1)`
30
+ * the delta's indexes start at 0 in the slice — but in the full conversation
31
+ * they start at `baseMsgCount` (= `cached.messageCount` = `cached.lastMessageIndex + 1`).
32
+ *
33
+ * Without this offset, incremental extraction produces corrupted indexes that
34
+ * break timeline ordering, topic segmentation, and downstream verification.
35
+ */
25
36
  export declare function mergeExtractions(base: StructuredExtraction, delta: StructuredExtraction, baseMsgCount: number): StructuredExtraction;
26
37
  export declare function appendMetricsLog(sessionId: string): void;
27
38
  //# sourceMappingURL=cache.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../../src/utils/cache.ts"],"names":[],"mappings":"AAAA;;GAEG;AAKH,OAAO,KAAK,EAAE,aAAa,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAG5G,OAAO,EAAY,KAAK,KAAK,EAAE,KAAK,GAAG,EAAE,KAAK,gBAAgB,EAAE,KAAK,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAO5G,wBAAgB,mBAAmB,IAAI,MAAM,CAK5C;AAED,wBAAgB,qBAAqB,IAAI,IAAI,CAE5C;AAGD,wBAAgB,SAAS,CAAC,IAAI,EAAE,iBAAiB,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,iBAAiB,GAAG;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,CAOhH;AAKD,wBAAgB,YAAY,IAAI,IAAI,CAAyB;AAC7D,wBAAgB,YAAY,CAAC,CAAC,EAAE,aAAa,GAAG,IAAI,CAA4F;AAChJ,wBAAgB,UAAU,IAAI,aAAa,EAAE,CAA0B;AAEvE,wBAAgB,iBAAiB,IAAI;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,CAYpK;AAGD,wBAAsB,eAAe,CACnC,KAAK,EAAE,aAAa,CAAC,OAAO,CAAC,EAC7B,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,EACjB,OAAO,EAAE,OAAO,EAChB,IAAI,EAAE,iBAAiB,GACtB,OAAO,CAAC,gBAAgB,CAAC,CA2B3B;AAQD,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,oBAAoB,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAQhH;AAED,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,MAAM,GAAG,gBAAgB,GAAG,IAAI,CAQ/E;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,oBAAoB,EAAE,KAAK,EAAE,oBAAoB,EAAE,YAAY,EAAE,MAAM,GAAG,oBAAoB,CAepI;AAGD,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAOxD"}
1
+ {"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../../src/utils/cache.ts"],"names":[],"mappings":"AAAA;;GAEG;AAKH,OAAO,KAAK,EAAE,aAAa,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAG5G,OAAO,EAAY,KAAK,KAAK,EAAE,KAAK,GAAG,EAAE,KAAK,gBAAgB,EAAE,KAAK,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAO5G,wBAAgB,mBAAmB,IAAI,MAAM,CAK5C;AAED,wBAAgB,qBAAqB,IAAI,IAAI,CAE5C;AAGD,wBAAgB,SAAS,CAAC,IAAI,EAAE,iBAAiB,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,iBAAiB,GAAG;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,CAOhH;AAKD,wBAAgB,YAAY,IAAI,IAAI,CAAyB;AAC7D,wBAAgB,YAAY,CAAC,CAAC,EAAE,aAAa,GAAG,IAAI,CAA4F;AAChJ,wBAAgB,UAAU,IAAI,aAAa,EAAE,CAA0B;AAEvE,wBAAgB,iBAAiB,IAAI;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,CAYpK;AAGD,wBAAsB,eAAe,CACnC,KAAK,EAAE,aAAa,CAAC,OAAO,CAAC,EAC7B,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,EACjB,OAAO,EAAE,OAAO,EAChB,IAAI,EAAE,iBAAiB,GACtB,OAAO,CAAC,gBAAgB,CAAC,CA2B3B;AAQD,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,oBAAoB,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAQhH;AAED,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,MAAM,GAAG,gBAAgB,GAAG,IAAI,CAQ/E;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,oBAAoB,EAAE,KAAK,EAAE,oBAAoB,EAAE,YAAY,EAAE,MAAM,GAAG,oBAAoB,CA8BpI;AAGD,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAOxD"}
@@ -1 +1 @@
1
- {"version":3,"file":"extraction.d.ts","sourceRoot":"","sources":["../../src/utils/extraction.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,oBAAoB,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAK7F,oCAAoC;AACpC,MAAM,MAAM,aAAa,GAAG,GAAG,CAAC,MAAM,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAEhH,wBAAgB,WAAW,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,CAQpD;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,UAAU,EAAE,GAAG,aAAa,CAapE;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,UAAU,EAAE,EAAE,MAAM,CAAC,EAAE,aAAa,GAAG;IAAE,QAAQ,EAAE,oBAAoB,CAAC,eAAe,CAAC,CAAC;IAAC,IAAI,EAAE,MAAM,EAAE,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,CAAA;CAAE,CAiC/J;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,UAAU,EAAE,EAAE,MAAM,CAAC,EAAE,aAAa,GAAG,oBAAoB,CAAC,QAAQ,CAAC,CA2CxG;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,UAAU,EAAE,EAAE,MAAM,CAAC,EAAE,aAAa,GAAG,oBAAoB,CAAC,WAAW,CAAC,CAyB9G;AAYD,wBAAgB,eAAe,CAAC,IAAI,EAAE,UAAU,EAAE,GAAG,oBAAoB,CAAC,aAAa,CAAC,CAcvF;AAED,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,UAAU,EAAE,EAAE,EAAE,EAAE,aAAa,EAAE,OAAO,SAAK,EAAE,MAAM,CAAC,EAAE,aAAa,GAAG,oBAAoB,CAAC,QAAQ,CAAC,CA8ClJ;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,UAAU,EAAE,EAAE,MAAM,EAAE,oBAAoB,CAAC,QAAQ,CAAC,GAAG,oBAAoB,CAAC,UAAU,CAAC,CAc1H;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,UAAU,EAAE,GAAG,MAAM,GAAG,IAAI,CAOjE;AAED,kEAAkE;AAClE,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,UAAU,EAAE,EAAE,UAAU,EAAE,oBAAoB,GAAG,QAAQ,EAAE,CAoFjG;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,UAAU,EAAE,EAAE,EAAE,EAAE,aAAa,GAAG,oBAAoB,CAgB7F"}
1
+ {"version":3,"file":"extraction.d.ts","sourceRoot":"","sources":["../../src/utils/extraction.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,oBAAoB,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAK7F,oCAAoC;AACpC,MAAM,MAAM,aAAa,GAAG,GAAG,CAAC,MAAM,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAEhH,wBAAgB,WAAW,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,CAQpD;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,UAAU,EAAE,GAAG,aAAa,CAapE;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,UAAU,EAAE,EAAE,MAAM,CAAC,EAAE,aAAa,GAAG;IAAE,QAAQ,EAAE,oBAAoB,CAAC,eAAe,CAAC,CAAC;IAAC,IAAI,EAAE,MAAM,EAAE,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,CAAA;CAAE,CAiC/J;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,UAAU,EAAE,EAAE,MAAM,CAAC,EAAE,aAAa,GAAG,oBAAoB,CAAC,QAAQ,CAAC,CA4CxG;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,UAAU,EAAE,EAAE,MAAM,CAAC,EAAE,aAAa,GAAG,oBAAoB,CAAC,WAAW,CAAC,CAyB9G;AAYD,wBAAgB,eAAe,CAAC,IAAI,EAAE,UAAU,EAAE,GAAG,oBAAoB,CAAC,aAAa,CAAC,CAcvF;AAED,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,UAAU,EAAE,EAAE,EAAE,EAAE,aAAa,EAAE,OAAO,SAAK,EAAE,MAAM,CAAC,EAAE,aAAa,GAAG,oBAAoB,CAAC,QAAQ,CAAC,CA8ClJ;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,UAAU,EAAE,EAAE,MAAM,EAAE,oBAAoB,CAAC,QAAQ,CAAC,GAAG,oBAAoB,CAAC,UAAU,CAAC,CAc1H;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,UAAU,EAAE,GAAG,MAAM,GAAG,IAAI,CAOjE;AAED,kEAAkE;AAClE,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,UAAU,EAAE,EAAE,UAAU,EAAE,oBAAoB,GAAG,QAAQ,EAAE,CAoFjG;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,UAAU,EAAE,EAAE,EAAE,EAAE,aAAa,GAAG,oBAAoB,CAgB7F"}
@@ -13,8 +13,18 @@ export interface ProjectFingerprint {
13
13
  updatedAt: number;
14
14
  }
15
15
  /**
16
- * Generate a project ID from file paths in the extraction.
17
- * Uses the most common root directory as a heuristic.
16
+ * Generate a stable project ID from file paths in the extraction.
17
+ *
18
+ * Strategy (in priority order):
19
+ * 1. **Noise filtering** — exclude node_modules, .cache, .pi/agent, etc.
20
+ * 2. **Absolute paths** (≥ 2) — deep ancestor with majority vote
21
+ * 3. **Relative paths** (≥ 2) — directory structure fingerprint
22
+ * 4. **Fallback** — hash all paths together
23
+ *
24
+ * ⚠️ Breaking change: this replaces the previous shallow-root algorithm
25
+ * that caused cross-project contamination
26
+ * (e.g. hash("root") → proj-4813494d, hash("/Users") → proj-a2a0ee2c).
27
+ * Old fingerprint/state files will be orphaned and expire via TTL.
18
28
  */
19
29
  export declare function deriveProjectId(extraction: StructuredExtraction): string;
20
30
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"fingerprint.d.ts","sourceRoot":"","sources":["../../src/utils/fingerprint.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAGxD,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;CACnB;AAuCD;;;GAGG;AACH,wBAAgB,eAAe,CAAC,UAAU,EAAE,oBAAoB,GAAG,MAAM,CAkBxE;AAoDD;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,SAAS,EAAE,MAAM,GAAG,kBAAkB,GAAG,IAAI,CASnF;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CACpC,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,oBAAoB,GAC/B,IAAI,CAuBN;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,WAAW,EAAE,kBAAkB,GAAG,IAAI,GAAG,MAAM,CAQlF"}
1
+ {"version":3,"file":"fingerprint.d.ts","sourceRoot":"","sources":["../../src/utils/fingerprint.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAGxD,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;CACnB;AA8ID;;;;;;;;;;;;;GAaG;AACH,wBAAgB,eAAe,CAAC,UAAU,EAAE,oBAAoB,GAAG,MAAM,CAuBxE;AAoDD;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,SAAS,EAAE,MAAM,GAAG,kBAAkB,GAAG,IAAI,CASnF;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CACpC,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,oBAAoB,GAC/B,IAAI,CAuBN;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,WAAW,EAAE,kBAAkB,GAAG,IAAI,GAAG,MAAM,CAQlF"}
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * General helpers: config, backup, batching, preprocessing.
3
3
  */
4
- import type { CompactConfig, ChunkSummary, LlmChunk, StructuredExtraction, ExplorationReport, SessionMessageEntry } from "../types.ts";
4
+ import type { CompactConfig, ChunkSummary, LlmChunk, StructuredExtraction, ExplorationReport, SessionType, SessionMessageEntry } from "../types.ts";
5
5
  export declare function loadConfig(): CompactConfig;
6
6
  export declare function backupConversation(convText: string, sessionId: string): string | null;
7
7
  export declare function getPreviousCompactionContext(branch: unknown[]): string;
@@ -18,5 +18,20 @@ export declare function buildExtractionContext(extraction: StructuredExtraction,
18
18
  start: number;
19
19
  end: number;
20
20
  }): string;
21
+ /**
22
+ * Infer session type from extraction data when exploration report is absent.
23
+ *
24
+ * Previously defaulted blindly to "implementation", which caused review-only
25
+ * and discussion-only sessions to be summarized with the wrong prompt strategy.
26
+ *
27
+ * Heuristic priority:
28
+ * 1. If exploration report provides a classification → trust it
29
+ * 2. Active errors + code changes → debugging
30
+ * 3. Reads only, no modifications → review
31
+ * 4. Decisions but no code changes → discussion
32
+ * 5. Code modifications → implementation
33
+ * 6. Fallback → implementation (most common agent activity)
34
+ */
35
+ export declare function inferSessionType(extraction: StructuredExtraction, report: ExplorationReport | null): SessionType;
21
36
  export declare function buildExplorationContext(report: ExplorationReport): string;
22
37
  //# sourceMappingURL=helpers.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../src/utils/helpers.ts"],"names":[],"mappings":"AAAA;;GAEG;AAKH,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,QAAQ,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAOvI,wBAAgB,UAAU,IAAI,aAAa,CAiB1C;AAED,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAUrF;AAED,wBAAgB,4BAA4B,CAAC,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,CAQtE;AAID,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,mBAAmB,EAAE,EAAE,aAAa,EAAE,MAAM,GAAG,MAAM,CA0B5F;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAKhE;AAED,wBAAgB,aAAa,CAAC,MAAM,EAAE,QAAQ,EAAE,EAAE,SAAS,EAAE,MAAM,GAAG,QAAQ,EAAE,EAAE,CASjF;AAqCD,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,YAAY,EAAE,EAAE,YAAY,CAAC,EAAE,MAAM;;;;;EAYnF;AAED,wBAAgB,sBAAsB,CAAC,UAAU,EAAE,oBAAoB,EAAE,QAAQ,CAAC,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAU1H;AAED,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,iBAAiB,GAAG,MAAM,CAYzE"}
1
+ {"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../src/utils/helpers.ts"],"names":[],"mappings":"AAAA;;GAEG;AAKH,OAAO,KAAK,EAAE,aAAa,EAAsB,YAAY,EAAE,QAAQ,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,WAAW,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AA0CxK,wBAAgB,UAAU,IAAI,aAAa,CAkB1C;AAED,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAUrF;AAED,wBAAgB,4BAA4B,CAAC,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,CAQtE;AAID,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,mBAAmB,EAAE,EAAE,aAAa,EAAE,MAAM,GAAG,MAAM,CA0B5F;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAKhE;AAED,wBAAgB,aAAa,CAAC,MAAM,EAAE,QAAQ,EAAE,EAAE,SAAS,EAAE,MAAM,GAAG,QAAQ,EAAE,EAAE,CASjF;AAqCD,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,YAAY,EAAE,EAAE,YAAY,CAAC,EAAE,MAAM;;;;;EAYnF;AAED,wBAAgB,sBAAsB,CAAC,UAAU,EAAE,oBAAoB,EAAE,QAAQ,CAAC,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAU1H;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,gBAAgB,CAC9B,UAAU,EAAE,oBAAoB,EAChC,MAAM,EAAE,iBAAiB,GAAG,IAAI,GAC/B,WAAW,CAeb;AAED,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,iBAAiB,GAAG,MAAM,CAYzE"}
@@ -1 +1 @@
1
- {"version":3,"file":"pruning.d.ts","sourceRoot":"","sources":["../../src/utils/pruning.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAK9C,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,UAAU,EAAE,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,OAAO,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACnD;AAQD;;GAEG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,UAAU,EAAE,GAAG,aAAa,CAwGhE"}
1
+ {"version":3,"file":"pruning.d.ts","sourceRoot":"","sources":["../../src/utils/pruning.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAK9C,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,UAAU,EAAE,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,OAAO,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACnD;AAQD;;GAEG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,UAAU,EAAE,GAAG,aAAa,CAyGhE"}
@@ -1 +1 @@
1
- {"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../../src/utils/state.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,KAAK,EAAE,oBAAoB,EAAE,QAAQ,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAUtG;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,eAAe,GAAG,IAAI,CAKnF;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG,eAAe,GAAG,IAAI,CAe7E;AAED,wBAAgB,oBAAoB,CAClC,UAAU,EAAE,oBAAoB,EAChC,SAAS,EAAE,QAAQ,EAAE,EACrB,MAAM,EAAE,iBAAiB,GAAG,IAAI,EAChC,WAAW,EAAE,MAAM,EAAE,EACrB,eAAe,EAAE,MAAM,EAAE,GACxB,eAAe,CAgDjB;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,GAAG,MAAM,CAsBrF;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,4CAA4C;IAC5C,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,+DAA+D;IAC/D,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,oCAAoC;IACpC,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,2CAA2C;IAC3C,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,qBAAqB;IACrB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,2CAA2C;IAC3C,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,sDAAsD;IACtD,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,4BAA4B;IAC5B,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,oBAAoB;IACpB,WAAW,EAAE,OAAO,CAAC;IACrB,+BAA+B;IAC/B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,eAAe,EAAE,OAAO,EAAE,eAAe,GAAG,eAAe,CAmD7F;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,eAAe,GAAG,MAAM,CA+BjE;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,eAAe,GAAG,MAAM,CA2BlF;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,CAO5D;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,CAIhE"}
1
+ {"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../../src/utils/state.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,KAAK,EAAE,oBAAoB,EAAE,QAAQ,EAAE,eAAe,EAAE,iBAAiB,EAAe,MAAM,aAAa,CAAC;AAWnH;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,eAAe,GAAG,IAAI,CAKnF;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG,eAAe,GAAG,IAAI,CAe7E;AAED,wBAAgB,oBAAoB,CAClC,UAAU,EAAE,oBAAoB,EAChC,SAAS,EAAE,QAAQ,EAAE,EACrB,MAAM,EAAE,iBAAiB,GAAG,IAAI,EAChC,WAAW,EAAE,MAAM,EAAE,EACrB,eAAe,EAAE,MAAM,EAAE,GACxB,eAAe,CAgDjB;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,GAAG,MAAM,CAsBrF;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,4CAA4C;IAC5C,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,+DAA+D;IAC/D,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,oCAAoC;IACpC,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,2CAA2C;IAC3C,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,qBAAqB;IACrB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,2CAA2C;IAC3C,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,sDAAsD;IACtD,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,4BAA4B;IAC5B,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,oBAAoB;IACpB,WAAW,EAAE,OAAO,CAAC;IACrB,+BAA+B;IAC/B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,eAAe,EAAE,OAAO,EAAE,eAAe,GAAG,eAAe,CAmD7F;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,eAAe,GAAG,MAAM,CA+BjE;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,eAAe,GAAG,MAAM,CA2BlF;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,CAO5D;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,CAIhE"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-smart-compact",
3
- "version": "7.9.0",
3
+ "version": "7.9.2",
4
4
  "description": "EESV smart compaction extension for Pi Coding Agent — deterministic extraction, exploration, synthesis, verification with redundancy pruning, project fingerprinting, and damage detection.",
5
5
  "license": "MIT",
6
6
  "main": "./dist/index.js",
@@ -40,7 +40,7 @@
40
40
  "CHANGELOG.md"
41
41
  ],
42
42
  "scripts": {
43
- "build": "rm -rf dist && mkdir dist && bun build ./src/index.ts --outdir ./dist --target bun --external '@earendil-works/*' --external 'typebox' && (bunx tsc --emitDeclarationOnly --outDir dist || true)",
43
+ "build": "rm -rf dist && mkdir dist && bun build ./src/index.ts --outdir ./dist --target bun --external '@earendil-works/*' --external 'typebox' && bunx tsc --emitDeclarationOnly --outDir dist",
44
44
  "test": "bun test",
45
45
  "typecheck": "bunx tsc --noEmit"
46
46
  },