pi-smart-compact 9.6.0 → 9.6.1

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/ARCHITECTURE.md CHANGED
@@ -23,6 +23,7 @@ section is about lifecycle.
23
23
  | --- | --- |
24
24
  | `/smart-compact` | Manual command. Explainable target-first preflight or direct args. Bypasses the adaptive pressure gate, not yield/verification gates. |
25
25
  | `session_before_compact` | Auto hook. Returns/stages a pending summary or runs under pressure; durable commit waits for matching `session_compact`. |
26
+ | `session_compact_failed` | Pi 0.85 failure hook. Clears extension-owned staged state and records one error/cancellation outcome; older hosts simply never emit it. |
26
27
  | `agent_settled` | Opt-in pressure monitor. Requests `ctx.compact()` only; it never runs EESV or consumes/stages pending state. |
27
28
  | `smart_compact` tool | Agent-callable. Prepares a pending summary; never compacts mid-turn. |
28
29
  | `smart_recall` tool | Searches only the current project's bounded context graph; same session/branch ranks first. |
@@ -36,7 +37,8 @@ token/percentage, queue, in-flight, and per-session cooldown guards, then asks
36
37
  Pi for a normal host compaction. Pi re-enters `session_before_compact`, which
37
38
  reuses an already tool-staged summary or runs EESV exactly once under the
38
39
  host's signal and timeout. The matching `session_compact` event remains the
39
- only durable commit authority. This keeps proactive triggering out of the
40
+ only durable commit authority; `session_compact_failed` discards the staged
41
+ candidate without committing it. This keeps proactive triggering out of the
40
42
  pending/commit state machine and preserves branch-provenance checks.
41
43
 
42
44
  ### Host dependency boundary
@@ -72,7 +74,7 @@ threads a typed context through ten stages:
72
74
  | 1 | prepare | `app/steps/prepare.ts` | config + provider caps + budgets; auth remains lazy |
73
75
  | 2 | window | `app/steps/window.ts` | pick the prefix using a calibrated final-summary allowance |
74
76
  | 3 | recover | `app/steps/recover.ts` | restore log-truncated messages |
75
- | 4 | tier | `app/steps/tier.ts` | choose none / light / full |
77
+ | 4 | tier | `app/steps/tier.ts` | admission gate + none / light / full pressure label; mode owns strategy |
76
78
  | 5 | extract | `app/steps/extract.ts` | prune + deterministic extraction + cache |
77
79
  | 6 | synthesize | `app/steps/synthesize.ts` | single-pass or EESV |
78
80
  | 7 | verify | `app/steps/verify.ts` | structural verify + repair; high-risk outcomes require successful tool evidence |
@@ -99,8 +101,8 @@ RcBase
99
101
  → StatedRc (after state)
100
102
  ```
101
103
 
102
- Each stage adds a `_prepared` / `_windowed` / … discriminator field that is
103
- **never read at runtime** it exists only to carry the type-level proof.
104
+ Each stage adds a `_prepared` / `_windowed` / … discriminator field that
105
+ carries the type-level proof and is checked by `advance()` at runtime.
104
106
  Mutation is preserved: a step mutates its input object and casts it to the
105
107
  next stage (no per-step copy of ~30 fields). The final alias
106
108
  `RunContext = StatedRc` keeps post-`buildState` consumers readable.
@@ -486,7 +488,7 @@ The code is organized into six layers, each with a single responsibility.
486
488
  | `app/steps/prepare.ts` | resolve config, provider caps, budgets, and cancellation; stage auth resolves lazily |
487
489
  | `app/steps/window.ts` | pick the prefix using provider-calibrated synthesis and deterministic post-processing bounds |
488
490
  | `app/steps/recover.ts` | recover full content for log-truncated messages |
489
- | `app/steps/tier.ts` | choose compaction tier (none / light / full) |
491
+ | `app/steps/tier.ts` | admission gate + context-pressure label (none / light / full); modes own execution depth |
490
492
  | `app/steps/extract.ts` | pruning + deterministic extraction with incremental cache |
491
493
  | `app/steps/synthesize.ts` | single-pass / EESV synthesis |
492
494
  | `app/steps/verify.ts` | structural verification + repair with tool-result trust boundaries |
package/CHANGELOG.md CHANGED
@@ -2,6 +2,26 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [9.6.1] - 2026-09-04
6
+
7
+ ### Changed
8
+
9
+ - The package now declares its emitted artifact as ESM, and release audit pins that metadata in both source and packed manifests.
10
+ - Internal `light` / `full` tiers are documented as admission/pressure labels; Fast, Balanced, and Thorough remain the controls that change synthesis strategy.
11
+ - The Pi compatibility workspace now declares `@earendil-works/pi-server`, and Bun declarations are updated to 1.4.0.
12
+
13
+ ### Fixed
14
+
15
+ - Empty, truncated, partial, duplicate, or otherwise malformed batch-summary responses are rejected before cache insertion, so deterministic fallback is visible in telemetry and a retry can reach the provider.
16
+ - Pi 0.85 `session_compact_failed` events now clear correlated extension candidates, progress UI, and deferred backup state while recording exactly one error/cancellation outcome.
17
+ - Result overlays support Pi 0.85's split scrollbar track/thumb styling while retaining the Pi 0.84 API fallback.
18
+ - Runtime pipeline transitions now reject a skipped stage even when a fresh object carries no earlier stage marker.
19
+
20
+ ### Performance
21
+
22
+ - File-reference extraction uses a semantics-preserving linear scanner instead of a regex with quadratic backtracking on long dotless tokens.
23
+ - Summary verification uses bounded path-suffix and ownership indexes, removing repeated reference × path normalization without unbounded suffix allocation.
24
+
5
25
  ## [9.6.0] - 2026-08-31
6
26
 
7
27
  ### Added
package/README.md CHANGED
@@ -143,6 +143,7 @@ prompt.
143
143
  | --- | --- |
144
144
  | `/smart-compact` | Explicit manual run. Opens a target-first preflight or accepts direct args, dry-run, focus, and budgets. |
145
145
  | `session_before_compact` | Auto path. Returns/stages a verification-scored summary under pressure; durable state waits for matching `session_compact`. |
146
+ | `session_compact_failed` | Pi 0.85 cleanup path. Discards extension-owned staged state and records a failed/cancelled outcome; it is inert on older hosts. |
146
147
  | `smart_compact` tool | Agent path. Produces a pending summary for Pi's next natural compact; does not compact mid-turn. Can be hidden from the agent. |
147
148
  | `/smart-compact loops` | Project-level open-loop manager: resolve/reopen, priority, pin/unpin. |
148
149
  | `/smart-compact settings` | Unified TUI for branch overrides and every `smartCompact` global setting. Manual `/smart-compact` always remains available. |
@@ -445,7 +446,7 @@ strategy is:
445
446
  ```
446
447
 
447
448
  `settled` requires a Pi host that emits `agent_settled`; the release boundary is
448
- verified against Pi 0.84.0 and 0.84.1.
449
+ verified against Pi 0.84.x–0.85.x.
449
450
  The settled handler never runs EESV or mutates pending state itself: after
450
451
  checking finite context pressure, idle/queue state, per-session in-flight
451
452
  deduplication, and cooldown, it asks Pi to compact. The existing
@@ -508,7 +509,7 @@ the run fails closed before staging or apply.
508
509
  | `agentToolAccess` | `"inherit" \| "enabled" \| "disabled"` | `"inherit"` | Respect Pi's active tools by default, or explicitly expose/hide `smart_compact`; manual command is unaffected |
509
510
  | `autoTrigger` | `boolean` | `true` | Allow smart compaction in Pi's native hook and the selected trigger strategy |
510
511
  | `showStatus` | `boolean` | `true` | Show the policy status line (e.g. "manual only") in Pi's footer; set `false` for a clean footer |
511
- | `autoTriggerStrategy` | `native-hook \| settled` | `native-hook` | `settled` additionally requests Pi's normal compact flow after an idle high-pressure agent run; verified with Pi 0.84.0+ |
512
+ | `autoTriggerStrategy` | `native-hook \| settled` | `native-hook` | `settled` additionally requests Pi's normal compact flow after an idle high-pressure agent run; verified with Pi 0.84.x–0.85.x |
512
513
  | `autoTriggerTimeoutMs` | `number` | `120000` | Requested auto cancellation deadline; the host hook clamps it to 60s and four LLM calls, shows live phase progress, then safely unwinds to native recovery |
513
514
  | `minContextPercent` | `number` | `60` | Auto/tool context gate; manual `/smart-compact` warns and bypasses it |
514
515
  | `backupEnabled` | `boolean` | `true` | Prepare a scrubbed pre-compaction backup; write it only after confirmed apply |
@@ -25,9 +25,9 @@
25
25
  *
26
26
  * Implementation note: mutation is preserved. Each step mutates the input
27
27
  * object and returns it cast to the next stage. The `_brand` field on each
28
- * extension is the only thing that distinguishes stages structurally it is
29
- * never read at runtime. This lets us avoid copying ~30 fields per step
30
- * while still getting compile-time stage tracking.
28
+ * extension distinguishes stages structurally and is checked by `advance()`
29
+ * at runtime. This lets us avoid copying ~30 fields per step while retaining
30
+ * both compile-time and runtime stage tracking.
31
31
  *
32
32
  * The final `RunContext = StatedRc` alias keeps backwards-compatible imports
33
33
  * working for the `applyCompaction` / metrics paths that ran post-`buildState`.
@@ -1 +1 @@
1
- {"version":3,"file":"run-context.d.ts","sourceRoot":"","sources":["../../src/app/run-context.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"run-context.d.ts","sourceRoot":"","sources":["../../src/app/run-context.ts"],"names":[],"mappings":"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"}
@@ -1,11 +1,12 @@
1
1
  /**
2
- * Step 4: select compaction tier.
2
+ * Step 4: apply the admission gate and record context-pressure tier.
3
3
  *
4
4
  * Stage: `RecoveredRc` → `TieredRc | null`.
5
5
  *
6
- * Returns `null` for tier="none" so the orchestrator can short-circuit. Only
7
- * "light" and "full" tiers reach later stages, which is enforced statically
8
- * by the `ActiveTier` type on `TieredRc.tier`.
6
+ * Returns `null` for tier="none" so the orchestrator can short-circuit. The
7
+ * "light" / "full" value is a telemetry and UI pressure label; Fast,
8
+ * Balanced, and Thorough modes independently control downstream strategy.
9
+ * `ActiveTier` statically proves that an admitted run cannot carry "none".
9
10
  */
10
11
  import type { RecoveredRc, TieredRc } from "../run-context.ts";
11
12
  export declare function selectTier(rc: RecoveredRc): TieredRc | null;
@@ -1 +1 @@
1
- {"version":3,"file":"tier.d.ts","sourceRoot":"","sources":["../../../src/app/steps/tier.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAc,MAAM,mBAAmB,CAAC;AAQ3E,wBAAgB,UAAU,CAAC,EAAE,EAAE,WAAW,GAAG,QAAQ,GAAG,IAAI,CA+B3D"}
1
+ {"version":3,"file":"tier.d.ts","sourceRoot":"","sources":["../../../src/app/steps/tier.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAc,MAAM,mBAAmB,CAAC;AAQ3E,wBAAgB,UAAU,CAAC,EAAE,EAAE,WAAW,GAAG,QAAQ,GAAG,IAAI,CA+B3D"}
@@ -8,7 +8,7 @@ import type { CompressionProfile, ProfileConfig } from "./types.ts";
8
8
  * `package.json#version`. Do not hand-edit this line for releases; bump
9
9
  * package.json and run `bun run sync-version`.
10
10
  */
11
- export declare const VERSION = "9.6.0";
11
+ export declare const VERSION = "9.6.1";
12
12
  export declare const CHARS_PER_TOKEN = 3.8;
13
13
  export declare const MIN_COMPACTION_SAVING_RATIO = 0.1;
14
14
  export declare const ESTIMATOR_ROUNDING_TOLERANCE_TOKENS = 1;
@@ -1 +1 @@
1
- {"version":3,"file":"tool-semantics.d.ts","sourceRoot":"","sources":["../../src/domain/tool-semantics.ts"],"names":[],"mappings":"AA0CA;;;GAGG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAQjE;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AA8GD,mFAAmF;AACnF,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,OAAO,GAAG,mBAAmB,CAiC7E;AAED,MAAM,MAAM,SAAS,GAAG,SAAS,GAAG,UAAU,GAAG,UAAU,GAAG,OAAO,CAAC;AACtE,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,QAAQ,GAAG,MAAM,GAAG,QAAQ,GAAG,QAAQ,GAAG,SAAS,GAAG,SAAS,CAAC;AAErG,uFAAuF;AACvF,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,CAQvD;AAOD;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,OAAO,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,aAAa,CAarF;AAqBD,sFAAsF;AACtF,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAW9F;AAED,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,EAC1D,KAAK,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,GAC1D,OAAO,CAET;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,OAAO,GAAG,SAAS,CAQrD"}
1
+ {"version":3,"file":"tool-semantics.d.ts","sourceRoot":"","sources":["../../src/domain/tool-semantics.ts"],"names":[],"mappings":"AA0CA;;;GAGG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAQjE;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AA8GD,mFAAmF;AACnF,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,OAAO,GAAG,mBAAmB,CAiC7E;AAED,MAAM,MAAM,SAAS,GAAG,SAAS,GAAG,UAAU,GAAG,UAAU,GAAG,OAAO,CAAC;AACtE,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,QAAQ,GAAG,MAAM,GAAG,QAAQ,GAAG,QAAQ,GAAG,SAAS,GAAG,SAAS,CAAC;AAErG,uFAAuF;AACvF,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,CAQvD;AAOD;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,OAAO,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,aAAa,CAarF;AAqCD,sFAAsF;AACtF,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAW9F;AAED,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,EAC1D,KAAK,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,GAC1D,OAAO,CAET;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,OAAO,GAAG,SAAS,CAQrD"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAEL,KAAK,YAAY,EAElB,MAAM,iCAAiC,CAAC;AAuDzC,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAgEtE,MAAM,CAAC,OAAO,UAAU,qBAAqB,CAAC,EAAE,EAAE,YAAY,QAgW7D"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAEL,KAAK,YAAY,EAElB,MAAM,iCAAiC,CAAC;AAuDzC,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAoFtE,MAAM,CAAC,OAAO,UAAU,qBAAqB,CAAC,EAAE,EAAE,YAAY,QAoY7D"}
package/dist/index.js CHANGED
@@ -7,7 +7,7 @@ import {
7
7
  } from "@earendil-works/pi-coding-agent";
8
8
 
9
9
  // src/constants.ts
10
- var VERSION = "9.6.0";
10
+ var VERSION = "9.6.1";
11
11
  var CHARS_PER_TOKEN = 3.8;
12
12
  var MIN_COMPACTION_SAVING_RATIO = 0.1;
13
13
  var ESTIMATOR_ROUNDING_TOLERANCE_TOKENS = 1;
@@ -1455,38 +1455,117 @@ function buildPathNeedles(filePath) {
1455
1455
  }
1456
1456
  return needles;
1457
1457
  }
1458
- function buildUniquePathNeedles(filePath, allPaths) {
1459
- const normalized = allPaths.map(normalizePath);
1458
+ var MAX_INDEXED_SUFFIX_CHARS = 1024;
1459
+ function buildPathNeedleOwnershipIndex(allPaths) {
1460
+ const owners = new Map;
1461
+ const normalizedPaths = allPaths.map(normalizePath);
1462
+ let hasUnindexedSuffixes = false;
1463
+ for (const normalized of normalizedPaths) {
1464
+ const suffixes = new Set([normalized]);
1465
+ for (let index = 0;index < normalized.length; index++) {
1466
+ if (normalized[index] !== "/")
1467
+ continue;
1468
+ if (normalized.length - index - 1 <= MAX_INDEXED_SUFFIX_CHARS) {
1469
+ suffixes.add(normalized.slice(index + 1));
1470
+ } else {
1471
+ hasUnindexedSuffixes = true;
1472
+ }
1473
+ }
1474
+ for (const suffix of suffixes) {
1475
+ owners.set(suffix, (owners.get(suffix) ?? 0) + 1);
1476
+ }
1477
+ }
1478
+ return { counts: owners, normalizedPaths, hasUnindexedSuffixes };
1479
+ }
1480
+ function buildUniquePathNeedlesFromIndex(filePath, owners) {
1460
1481
  return buildPathNeedles(filePath).filter((needle) => {
1461
- let owners = 0;
1462
- for (const candidate of normalized) {
1482
+ if (!owners.hasUnindexedSuffixes)
1483
+ return owners.counts.get(needle) === 1;
1484
+ let count = 0;
1485
+ for (const candidate of owners.normalizedPaths) {
1463
1486
  if (candidate === needle || candidate.endsWith("/" + needle))
1464
- owners++;
1465
- if (owners > 1)
1487
+ count++;
1488
+ if (count > 1)
1466
1489
  return false;
1467
1490
  }
1468
- return owners === 1;
1491
+ return count === 1;
1469
1492
  });
1470
1493
  }
1471
- function isKnownPathReference(ref, knownPaths) {
1472
- const normalizedRef = normalizePath(ref).replace(/^\/+/, "");
1473
- if (!normalizedRef)
1474
- return false;
1475
- const pathShaped = normalizedRef.includes("/");
1476
- return knownPaths.some((path3) => {
1494
+ var PATH_CANDIDATE_CHAR_RE = /[\w./-]/;
1495
+ function buildKnownPathReferenceIndex(knownPaths) {
1496
+ const segmentSuffixes = new Set;
1497
+ const boundarySuffixes = new Set;
1498
+ const normalizedPaths = [];
1499
+ let hasUnindexedSuffixes = false;
1500
+ for (const path3 of knownPaths) {
1477
1501
  const normalizedPath = normalizePath(path3).replace(/^\/+/, "");
1502
+ if (!normalizedPath)
1503
+ continue;
1504
+ normalizedPaths.push(normalizedPath);
1505
+ segmentSuffixes.add(normalizedPath);
1506
+ for (let index = 0;index < normalizedPath.length; index++) {
1507
+ if (normalizedPath[index] === "/") {
1508
+ if (normalizedPath.length - index - 1 <= MAX_INDEXED_SUFFIX_CHARS) {
1509
+ segmentSuffixes.add(normalizedPath.slice(index + 1));
1510
+ } else {
1511
+ hasUnindexedSuffixes = true;
1512
+ }
1513
+ }
1514
+ if (index > 0 && !PATH_CANDIDATE_CHAR_RE.test(normalizedPath[index - 1])) {
1515
+ if (normalizedPath.length - index <= MAX_INDEXED_SUFFIX_CHARS) {
1516
+ boundarySuffixes.add(normalizedPath.slice(index));
1517
+ } else {
1518
+ hasUnindexedSuffixes = true;
1519
+ }
1520
+ }
1521
+ }
1522
+ }
1523
+ return {
1524
+ segmentSuffixes,
1525
+ sortedSegmentSuffixes: [...segmentSuffixes].sort(),
1526
+ boundarySuffixes,
1527
+ normalizedPaths,
1528
+ hasUnindexedSuffixes
1529
+ };
1530
+ }
1531
+ function matchesKnownPathReference(normalizedRef, normalizedPaths) {
1532
+ const pathShaped = normalizedRef.includes("/");
1533
+ return normalizedPaths.some((normalizedPath) => {
1478
1534
  if (normalizedPath === normalizedRef || normalizedPath.endsWith("/" + normalizedRef))
1479
1535
  return true;
1480
1536
  if (normalizedPath.endsWith(normalizedRef)) {
1481
1537
  const boundary = normalizedPath[normalizedPath.length - normalizedRef.length - 1];
1482
- if (boundary && !/[\w./-]/.test(boundary))
1538
+ if (boundary && !PATH_CANDIDATE_CHAR_RE.test(boundary))
1483
1539
  return true;
1484
1540
  }
1485
1541
  if (!pathShaped)
1486
1542
  return false;
1487
- return normalizedPath.split("/").some((_, index, parts) => parts.slice(index).join("/").startsWith(normalizedRef + "/"));
1543
+ return normalizedPath.startsWith(normalizedRef + "/") || normalizedPath.includes("/" + normalizedRef + "/");
1488
1544
  });
1489
1545
  }
1546
+ function sortedHasPrefix(values, prefix) {
1547
+ let low = 0;
1548
+ let high = values.length;
1549
+ while (low < high) {
1550
+ const middle = low + high >>> 1;
1551
+ if (values[middle] < prefix)
1552
+ low = middle + 1;
1553
+ else
1554
+ high = middle;
1555
+ }
1556
+ return values[low]?.startsWith(prefix) ?? false;
1557
+ }
1558
+ function isKnownPathReferenceInIndex(ref, index) {
1559
+ const normalizedRef = normalizePath(ref).replace(/^\/+/, "");
1560
+ if (!normalizedRef)
1561
+ return false;
1562
+ if (index.segmentSuffixes.has(normalizedRef) || index.boundarySuffixes.has(normalizedRef)) {
1563
+ return true;
1564
+ }
1565
+ if (normalizedRef.includes("/") && sortedHasPrefix(index.sortedSegmentSuffixes, normalizedRef + "/"))
1566
+ return true;
1567
+ return index.hasUnindexedSuffixes ? matchesKnownPathReference(normalizedRef, index.normalizedPaths) : false;
1568
+ }
1490
1569
 
1491
1570
  // src/domain/tool-semantics.ts
1492
1571
  var PATH_KEYS = [
@@ -1704,8 +1783,12 @@ function classifyToolOperation(args, toolName) {
1704
1783
  function stableValue(value) {
1705
1784
  if (Array.isArray(value))
1706
1785
  return value.map(stableValue);
1707
- if (!value || typeof value !== "object")
1786
+ if (value == null || typeof value === "string" || typeof value === "number" || typeof value === "boolean")
1708
1787
  return value;
1788
+ if (typeof value === "bigint")
1789
+ return value.toString();
1790
+ if (typeof value !== "object")
1791
+ return;
1709
1792
  return Object.fromEntries(Object.entries(value).sort(([left], [right]) => left.localeCompare(right)).map(([key, item]) => [key, stableValue(item)]));
1710
1793
  }
1711
1794
  function commandIdentity(args) {
@@ -2091,7 +2174,12 @@ function buildExplorationContext(report) {
2091
2174
  // src/utils/file-ref-detect.ts
2092
2175
  var CODE_EXT_RE = /\.(ts|tsx|js|jsx|mjs|cjs|rs|py|go|java|rb|cs|cpp|c|h|hpp|swift|kt|scala|php|css|scss|html|json|yaml|yml|toml|md|mdx|sh|sql|tf|ini|env|lock|gradle|xml)$/i;
2093
2176
  var VERSION_RE = /^v?\d+(?:\.\d+)+(?:[-+][\w.-]+)?$/i;
2094
- var FILE_REF_CANDIDATE_RE = /[\w.\/-]+\.[\w]+/g;
2177
+ function isAsciiWordCode(code) {
2178
+ return code >= 48 && code <= 57 || code >= 65 && code <= 90 || code === 95 || code >= 97 && code <= 122;
2179
+ }
2180
+ function isCandidateCode(code) {
2181
+ return isAsciiWordCode(code) || code === 45 || code === 46 || code === 47;
2182
+ }
2095
2183
  function isLikelyFileRef(candidate) {
2096
2184
  if (candidate.startsWith("//") || VERSION_RE.test(candidate))
2097
2185
  return false;
@@ -2102,13 +2190,32 @@ function isLikelyFileRef(candidate) {
2102
2190
  return CODE_EXT_RE.test(candidate);
2103
2191
  }
2104
2192
  function extractFileRefs(summary) {
2105
- const matcher = new RegExp(FILE_REF_CANDIDATE_RE.source, FILE_REF_CANDIDATE_RE.flags);
2106
2193
  const refs = [];
2107
- for (const match of summary.matchAll(matcher)) {
2108
- if (/[\\/]/.test(summary[(match.index ?? 0) + match[0].length] ?? ""))
2194
+ let cursor = 0;
2195
+ while (cursor < summary.length) {
2196
+ while (cursor < summary.length && !isCandidateCode(summary.charCodeAt(cursor)))
2197
+ cursor++;
2198
+ const runStart = cursor;
2199
+ while (cursor < summary.length && isCandidateCode(summary.charCodeAt(cursor)))
2200
+ cursor++;
2201
+ const runEnd = cursor;
2202
+ let extensionDot = -1;
2203
+ for (let index = runStart + 1;index + 1 < runEnd; index++) {
2204
+ if (summary.charCodeAt(index) === 46 && isAsciiWordCode(summary.charCodeAt(index + 1))) {
2205
+ extensionDot = index;
2206
+ }
2207
+ }
2208
+ if (extensionDot < 0)
2209
+ continue;
2210
+ let matchEnd = extensionDot + 2;
2211
+ while (matchEnd < runEnd && isAsciiWordCode(summary.charCodeAt(matchEnd))) {
2212
+ matchEnd++;
2213
+ }
2214
+ const candidate = summary.slice(runStart, matchEnd);
2215
+ if (/[\\/]/.test(summary[matchEnd] ?? ""))
2109
2216
  continue;
2110
- if (isLikelyFileRef(match[0]))
2111
- refs.push(match[0]);
2217
+ if (isLikelyFileRef(candidate))
2218
+ refs.push(candidate);
2112
2219
  }
2113
2220
  return refs;
2114
2221
  }
@@ -4947,12 +5054,9 @@ function advance(rc, stage) {
4947
5054
  const marker = stage;
4948
5055
  const index = STAGE_ORDER.indexOf(marker);
4949
5056
  const record = rc;
4950
- const hasStageHistory = STAGE_ORDER.some((candidate) => record[candidate] === true);
4951
- if (hasStageHistory) {
4952
- for (let prior = 0;prior < index; prior++) {
4953
- if (record[STAGE_ORDER[prior]] !== true) {
4954
- throw new Error("Pipeline stage out of order: " + marker + " requires " + STAGE_ORDER[prior]);
4955
- }
5057
+ for (let prior = 0;prior < index; prior++) {
5058
+ if (record[STAGE_ORDER[prior]] !== true) {
5059
+ throw new Error("Pipeline stage out of order: " + marker + " requires " + STAGE_ORDER[prior]);
4956
5060
  }
4957
5061
  }
4958
5062
  for (const field of STAGE_REQUIRED_FIELDS[marker]) {
@@ -6298,12 +6402,15 @@ async function showResultScreen(ctx, details, extraction, services, opts = {}) {
6298
6402
  c.addChild(new Text2(theme.fg("text", opts.summary), 2, 0));
6299
6403
  c.addChild(new Text2("", 0, 0));
6300
6404
  }
6405
+ const scrollbarStyle = (text) => theme.fg("borderMuted", text);
6301
6406
  const scroll = new ScrollView(c, {
6302
6407
  follow: "none",
6303
6408
  primary: true,
6304
6409
  overscroll: "contain",
6305
6410
  scrollbar: "auto",
6306
- scrollbarStyle: (text) => theme.fg("borderMuted", text)
6411
+ scrollbarStyle,
6412
+ scrollbarTrackStyle: scrollbarStyle,
6413
+ scrollbarThumbStyle: scrollbarStyle
6307
6414
  });
6308
6415
  const footer = new Container2;
6309
6416
  footer.addChild(new DynamicBorder2((s) => theme.fg("accent", s)));
@@ -7501,8 +7608,12 @@ function boundedExplorationValue(value, depth = 0) {
7501
7608
  if (typeof value === "string") {
7502
7609
  return value.length > TRUNC.PREVIEW_XL ? value.slice(0, TRUNC.PREVIEW_XL) + "\u2026" : value;
7503
7610
  }
7504
- if (value == null || typeof value !== "object")
7611
+ if (value == null || typeof value === "number" || typeof value === "boolean")
7505
7612
  return value;
7613
+ if (typeof value === "bigint")
7614
+ return value.toString();
7615
+ if (typeof value !== "object")
7616
+ return;
7506
7617
  if (depth >= 3)
7507
7618
  return "[bounded]";
7508
7619
  if (Array.isArray(value))
@@ -7511,7 +7622,7 @@ function boundedExplorationValue(value, depth = 0) {
7511
7622
  }
7512
7623
  function serializeExplorationResult(value, scrubber) {
7513
7624
  const safe = boundedExplorationValue(scrubber.scrubValue(value).value);
7514
- const serialized = JSON.stringify(safe);
7625
+ const serialized = JSON.stringify(safe) ?? "null";
7515
7626
  if (serialized.length <= MAX_EXPLORER_OUTPUT_CHARS)
7516
7627
  return serialized;
7517
7628
  let excerptChars = Math.max(0, Math.floor((MAX_EXPLORER_OUTPUT_CHARS - 160) / 2));
@@ -8160,16 +8271,75 @@ function batchFieldPattern(name) {
8160
8271
  let pattern = batchFieldPatterns.get(name);
8161
8272
  if (!pattern) {
8162
8273
  const escaped = name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
8163
- pattern = new RegExp("\\*\\*" + escaped + "\\*\\*:\\s*(.+?)(?:\\n|$)", "i");
8274
+ pattern = new RegExp("(?:^|\\n)\\*\\*" + escaped + "\\*\\*:\\s*(.+?)(?:\\n|$)", "i");
8164
8275
  batchFieldPatterns.set(name, pattern);
8165
8276
  }
8166
8277
  return pattern;
8167
8278
  }
8279
+
8280
+ class BatchSummaryFormatError extends Error {
8281
+ name = "BatchSummaryFormatError";
8282
+ constructor(reason) {
8283
+ super("Malformed batch summary response: " + reason);
8284
+ }
8285
+ }
8286
+ var BATCH_REQUIRED_FIELDS = [
8287
+ "Priority",
8288
+ "Summary",
8289
+ "Decisions",
8290
+ "Modified",
8291
+ "Deleted",
8292
+ "Read"
8293
+ ];
8294
+ function assertCompleteBatchResponse(stopReason, sectionMap, duplicateIds, batchSize) {
8295
+ const reason = String(stopReason ?? "");
8296
+ if (reason !== "stop" && reason !== "endTurn") {
8297
+ throw new BatchSummaryFormatError("non-terminal stop reason " + (reason || "unknown"));
8298
+ }
8299
+ if (duplicateIds.size > 0) {
8300
+ throw new BatchSummaryFormatError("duplicate chunk id(s): " + [...duplicateIds].sort((a, b) => a - b).join(", "));
8301
+ }
8302
+ const unexpected = [...sectionMap.keys()].filter((id) => id < 1 || id > batchSize);
8303
+ if (unexpected.length > 0) {
8304
+ throw new BatchSummaryFormatError("unexpected chunk id(s): " + unexpected.sort((a, b) => a - b).join(", "));
8305
+ }
8306
+ const missingSections = [];
8307
+ for (let id = 1;id <= batchSize; id++) {
8308
+ if (!sectionMap.has(id))
8309
+ missingSections.push(id);
8310
+ }
8311
+ if (missingSections.length > 0) {
8312
+ throw new BatchSummaryFormatError("missing chunk section(s): " + missingSections.join(", "));
8313
+ }
8314
+ for (let id = 1;id <= batchSize; id++) {
8315
+ const section = sectionMap.get(id) ?? "";
8316
+ const missingFields = BATCH_REQUIRED_FIELDS.filter((field) => !batchFieldPattern(field).test(section));
8317
+ if (missingFields.length > 0) {
8318
+ throw new BatchSummaryFormatError("chunk " + id + " missing field(s): " + missingFields.join(", "));
8319
+ }
8320
+ }
8321
+ const missing = [];
8322
+ for (let id = 1;id <= batchSize; id++) {
8323
+ const section = sectionMap.get(id);
8324
+ const summary = section?.match(batchFieldPattern("Summary"))?.[1].trim();
8325
+ if (!summary || summary.toLowerCase() === "none")
8326
+ missing.push(id);
8327
+ }
8328
+ if (missing.length > 0) {
8329
+ throw new BatchSummaryFormatError("missing usable Summary for chunk(s): " + missing.join(", "));
8330
+ }
8331
+ }
8168
8332
  function boundedToolArgs(value, depth = 0) {
8169
8333
  if (typeof value === "string")
8170
8334
  return value.length > TRUNC.DETAIL ? value.slice(0, TRUNC.DETAIL) + "\u2026" : value;
8171
- if (value == null || typeof value !== "object" || depth >= 2)
8335
+ if (value == null || typeof value === "number" || typeof value === "boolean")
8172
8336
  return value;
8337
+ if (typeof value === "bigint")
8338
+ return value.toString();
8339
+ if (typeof value !== "object")
8340
+ return;
8341
+ if (depth >= 2)
8342
+ return "[bounded]";
8173
8343
  if (Array.isArray(value))
8174
8344
  return value.slice(0, 8).map((item) => boundedToolArgs(item, depth + 1));
8175
8345
  return Object.fromEntries(Object.entries(value).slice(0, 12).map(([key, item]) => [key, boundedToolArgs(item, depth + 1)]));
@@ -8483,13 +8653,19 @@ async function summarizeBatch(batch, extraction, model, auth, signal, services,
8483
8653
  const output = resp.content.filter((c) => c.type === "text").map((c) => c.text).join(`
8484
8654
  `);
8485
8655
  const sectionMap = new Map;
8656
+ const duplicateIds = new Set;
8486
8657
  const sections = output.split(/^### /m).filter((s) => s.trim());
8487
8658
  for (const sec of sections) {
8488
8659
  const m = sec.match(/^CHUNK\s+(\d+):\s*(.*?)\n/i);
8489
8660
  if (m) {
8490
- sectionMap.set(parseInt(m[1], 10), sec);
8661
+ const id = parseInt(m[1], 10);
8662
+ if (sectionMap.has(id))
8663
+ duplicateIds.add(id);
8664
+ else
8665
+ sectionMap.set(id, sec);
8491
8666
  }
8492
8667
  }
8668
+ assertCompleteBatchResponse(resp.stopReason, sectionMap, duplicateIds, batch.length);
8493
8669
  const result = batch.map((ch, i) => {
8494
8670
  const id = i + 1;
8495
8671
  const sec = sectionMap.get(id) ?? "";
@@ -9737,8 +9913,9 @@ function verifyFileReferences(summary, extraction, continuity, evidence, collect
9737
9913
  ...(continuity?.unresolvedErrors ?? []).flatMap((error2) => error2.files),
9738
9914
  ...(continuity?.openLoops ?? []).flatMap((loop) => loop.files)
9739
9915
  ]));
9916
+ const knownFileIndex = buildKnownPathReferenceIndex(knownFiles);
9740
9917
  for (const ref of new Set(extractFileRefs(summary))) {
9741
- const grounded = isKnownPathReference(ref, knownFiles) || Boolean(evidence.sourceMessages && sourceSupportsFileReference(ref, evidence.sourceMessages));
9918
+ const grounded = isKnownPathReferenceInIndex(ref, knownFileIndex) || Boolean(evidence.sourceMessages && sourceSupportsFileReference(ref, evidence.sourceMessages));
9742
9919
  if (!grounded)
9743
9920
  addGap(accumulator, { kind: "fabricated-file", ref }, 4);
9744
9921
  }
@@ -9756,9 +9933,10 @@ function verifyProgressConsistency(parsed, extraction, collected, paths, accumul
9756
9933
  }, 12);
9757
9934
  }
9758
9935
  const doneRefs = new Set(extractFileRefs(done).map(normalizePath));
9936
+ const modifiedPathOwners = buildPathNeedleOwnershipIndex(paths.modified);
9759
9937
  for (const file of extraction.modifiedFiles) {
9760
- const needles = buildUniquePathNeedles(file.path, paths.modified);
9761
- if (!needles.some((needle) => doneRefs.has(normalizePath(needle))))
9938
+ const needles = buildUniquePathNeedlesFromIndex(file.path, modifiedPathOwners);
9939
+ if (!needles.some((needle) => doneRefs.has(needle)))
9762
9940
  continue;
9763
9941
  const unresolved = collected.unresolved.find((error2) => {
9764
9942
  const firstLine = error2.message.split(/\r?\n/, 1)[0] ?? "";
@@ -14767,19 +14945,29 @@ function smartCompactExtension(pi) {
14767
14945
  const settledAutoTrigger = createSettledAutoTrigger();
14768
14946
  const policy = createSmartCompactPolicy(pi);
14769
14947
  const nativeContinuity = createNativeContinuityBridge();
14948
+ const applyFailureWrites = new Map;
14770
14949
  const recordApplyFailure = (pending, reason) => {
14771
14950
  if (!pending.metricsSnapshot)
14772
- return;
14951
+ return null;
14773
14952
  const cancelled = reason === "aborted" || reason === "shutdown";
14774
- appendMetricsSnapshot(pending.sessionId, {
14953
+ const write = appendMetricsSnapshot(pending.sessionId, {
14775
14954
  ...pending.metricsSnapshot,
14776
14955
  status: cancelled ? "cancelled" : "error",
14777
14956
  failureKind: cancelled ? "cancelled" : reason === "evicted" ? "internal" : "persistence",
14778
14957
  fallbackReason: "native-apply:" + reason
14779
14958
  });
14959
+ applyFailureWrites.set(pending.runId, write);
14960
+ write.finally(() => {
14961
+ if (applyFailureWrites.get(pending.runId) === write) {
14962
+ applyFailureWrites.delete(pending.runId);
14963
+ }
14964
+ });
14965
+ return write;
14780
14966
  };
14781
14967
  const commitCandidates = createCompactionCommitStore({
14782
- onDiscard: recordApplyFailure
14968
+ onDiscard: (pending, reason) => {
14969
+ recordApplyFailure(pending, reason);
14970
+ }
14783
14971
  });
14784
14972
  const onNativeApplyError = (runId) => Boolean(commitCandidates.discard(runId, "apply-error"));
14785
14973
  const activateOnlineDamage = (pending) => {
@@ -14954,6 +15142,24 @@ function smartCompactExtension(pi) {
14954
15142
  if (state)
14955
15143
  nativeContinuity.stage({ projectId, sessionId, branchHeadId }, renderContinuityCapsule(state));
14956
15144
  });
15145
+ const compactFailedEvents = pi;
15146
+ compactFailedEvents.on("session_compact_failed", async (event, ctx) => {
15147
+ if (!event.fromExtension)
15148
+ return;
15149
+ const sessionId = resolveSessionId(ctx);
15150
+ clearCompactProgress(ctx);
15151
+ const reason = event.aborted ? "aborted" : "apply-error";
15152
+ const discarded = commitCandidates.clearSession(sessionId, reason);
15153
+ const metricWrites = discarded.flatMap((pending) => {
15154
+ const write = applyFailureWrites.get(pending.runId);
15155
+ return write ? [write] : [];
15156
+ });
15157
+ if (metricWrites.length > 0)
15158
+ await Promise.all(metricWrites);
15159
+ if (discarded.length > 0) {
15160
+ warn("Discarded " + discarded.length + " staged smart compaction after native apply " + (event.aborted ? "abort" : "failure") + (event.errorMessage ? ": " + event.errorMessage : ""));
15161
+ }
15162
+ });
14957
15163
  pi.on("before_agent_start", async (_event, ctx) => {
14958
15164
  const scope = resolveGraphScope(ctx);
14959
15165
  if (!scope?.branchHeadId)
@@ -15014,7 +15220,7 @@ function smartCompactExtension(pi) {
15014
15220
  });
15015
15221
  }
15016
15222
  export {
15017
- resolveModels,
15223
+ smartCompactExtension as default,
15018
15224
  findModelById,
15019
- smartCompactExtension as default
15225
+ resolveModels
15020
15226
  };