dsh-context-compression-improved 0.5.1 → 0.5.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/.gitattributes +1 -0
  2. package/CHANGELOG.ja.md +144 -83
  3. package/CHANGELOG.ko.md +143 -82
  4. package/CHANGELOG.md +278 -212
  5. package/CHANGELOG.zh.md +131 -77
  6. package/docs/installation.md +103 -103
  7. package/docs/installation.zh.md +100 -100
  8. package/package.json +1 -1
  9. package/packages/selector/cordis.patch.yml +5 -6
  10. package/packages/selector/lib/advisor-state.js +4 -231
  11. package/packages/selector/lib/client.d.ts +0 -24
  12. package/packages/selector/lib/client.js +6 -501
  13. package/packages/selector/lib/index.d.ts +4 -10
  14. package/packages/selector/lib/index.js +16 -234
  15. package/packages/selector/lib/pruner.d.ts +13 -248
  16. package/packages/selector/lib/pruner.js +148 -552
  17. package/packages/selector/src/client/EstimatorControls.tsx +0 -101
  18. package/packages/selector/src/client/index.ts +0 -17
  19. package/packages/selector/src/client/locales.ts +0 -38
  20. package/packages/selector/src/client/preset-options.ts +3 -2
  21. package/packages/selector/src/client/settings-section.tsx +8 -17
  22. package/packages/selector/src/index.ts +24 -271
  23. package/packages/selector/src/profiles.ts +4 -27
  24. package/packages/selector/src/pruner/state.ts +2 -25
  25. package/packages/selector/src/pruner.ts +75 -403
  26. package/packages/selector/src/runtime/audit.ts +27 -21
  27. package/packages/selector/src/runtime/config.ts +6 -32
  28. package/packages/selector/src/runtime/tokenpilot/advisor-prompt.ts +188 -188
  29. package/packages/selector/src/runtime/tokenpilot/advisor-state.ts +149 -133
  30. package/packages/selector/src/runtime/tokenpilot/advisor.ts +419 -419
  31. package/packages/selector/src/runtime/tokenpilot/benefit.ts +200 -0
  32. package/packages/selector/src/runtime/types.ts +0 -17
  33. package/packages/selector/tests/advisor-report.host.spec.ts +223 -223
  34. package/packages/selector/tests/preset-options-write.client.spec.ts +7 -23
  35. package/packages/selector/tests/public/package-contract.client.spec.ts +20 -0
  36. package/packages/selector/tests/runtime/advice-never-withholds.host.spec.ts +232 -0
  37. package/packages/selector/tests/runtime/advisor-invariant.spec.ts +272 -272
  38. package/packages/selector/tests/runtime/advisor.spec.ts +226 -226
  39. package/packages/selector/tests/runtime/audit.spec.ts +35 -21
  40. package/packages/selector/tests/runtime/char-basis.spec.ts +30 -30
  41. package/packages/selector/tests/runtime/deprecated-preset-options.spec.ts +96 -0
  42. package/packages/selector/tests/runtime/tokenpilot/benefit.spec.ts +217 -0
  43. package/packages/selector/tests/runtime/tokenpilot/profile-baseline.spec.ts +4 -5
  44. package/packages/selector/tests/settings-seat.client.spec.ts +45 -14
  45. package/scripts/toolclass-corpus-replay.mjs +281 -281
  46. package/packages/selector/src/client/ReviewOverlay.tsx +0 -320
  47. package/packages/selector/src/client/review-scope.ts +0 -16
  48. package/packages/selector/src/runtime/tokenpilot/proposal.ts +0 -267
  49. package/packages/selector/src/runtime/tokenpilot/review-queue.ts +0 -231
  50. package/packages/selector/src/runtime/tokenpilot/review-registry.ts +0 -117
  51. package/packages/selector/src/runtime/tokenpilot/review-storage.ts +0 -122
  52. package/packages/selector/tests/review-overlay.client.spec.tsx +0 -118
  53. package/packages/selector/tests/review-routes-registry.host.spec.ts +0 -142
  54. package/packages/selector/tests/review-routes.host.spec.ts +0 -290
  55. package/packages/selector/tests/runtime/tokenpilot/proposal.spec.ts +0 -393
  56. package/packages/selector/tests/runtime/tokenpilot/pruner-review.spec.ts +0 -382
  57. package/packages/selector/tests/runtime/tokenpilot/review-queue.spec.ts +0 -168
@@ -1,4 +1,4 @@
1
- import { d as ContextCompressionSettingsSchema, o as resolveReviewPruner, t as getAdvisorState, u as CONTEXT_COMPRESSION_SETTINGS_NAMESPACE } from "./advisor-state.js";
1
+ import { o as CONTEXT_COMPRESSION_SETTINGS_NAMESPACE, s as ContextCompressionSettingsSchema, t as getAdvisorState } from "./advisor-state.js";
2
2
  import z from "@deepseek-ai/schemastery";
3
3
  import "@deepseek-ai/dsh-settings";
4
4
  import { AsyncLocalStorage } from "node:async_hooks";
@@ -505,28 +505,12 @@ function restoreMethod(presets, snapshot) {
505
505
  //#region src/index.ts
506
506
  const CONTEXT_COMPRESSION_NAMESPACE = CONTEXT_COMPRESSION_SETTINGS_NAMESPACE;
507
507
  const ESTIMATOR_CATALOG_ROUTES = ["/endpoint/dsh-context-compression-improved/estimator-catalog", "/api/dsh-context-compression-improved/estimator-catalog"];
508
- const REVIEW_QUEUE_ROUTES = ["/endpoint/dsh-context-compression-improved/review-queue", "/api/dsh-context-compression-improved/review-queue"];
509
- const REVIEW_DECIDE_ROUTES = ["/endpoint/dsh-context-compression-improved/review-decide", "/api/dsh-context-compression-improved/review-decide"];
510
508
  const ADVISOR_REPORT_ROUTES = ["/endpoint/dsh-context-compression-improved/advisor-report", "/api/dsh-context-compression-improved/advisor-report"];
511
- /**
512
- * Resolve the review pipeline for the top-level routes.
513
- *
514
- * A top-level `toolResultPruner` service wins when a deployment actually mounts
515
- * one, but in production every pruner lives inside an agent preset's isolated
516
- * group, so the registry is the path that resolves. Without the fallback the
517
- * queue route answered 503 "review pipeline unavailable" on every request while
518
- * the review pipeline itself was running normally.
519
- */
520
- function reviewPrunerOf(readService) {
521
- const candidate = readService("toolResultPruner");
522
- if (typeof candidate?.listReviewProposals === "function" && typeof candidate?.decideReviewProposal === "function") return candidate;
523
- return resolveReviewPruner();
524
- }
525
509
  function sessionFor(readService, sessionId) {
526
510
  const agents = readService("agents");
527
511
  return typeof agents?.get === "function" ? agents.get(sessionId)?.session : void 0;
528
512
  }
529
- function reviewJson(res, status, body) {
513
+ function jsonResponse(res, status, body) {
530
514
  const resTyped = res;
531
515
  resTyped.writeHead(status, {
532
516
  "content-type": "application/json; charset=utf-8",
@@ -534,217 +518,9 @@ function reviewJson(res, status, body) {
534
518
  });
535
519
  resTyped.end(JSON.stringify(body));
536
520
  }
537
- function readRequestBody(req) {
538
- return new Promise((resolve, reject) => {
539
- const typed = req;
540
- let data = "";
541
- try {
542
- typed.on?.("data", (chunk) => {
543
- data += String(chunk ?? "");
544
- if (data.length > 65536) {
545
- data = "";
546
- resolve("");
547
- }
548
- });
549
- typed.on?.("end", () => resolve(data));
550
- typed.on?.("error", reject);
551
- } catch (error) {
552
- reject(error instanceof Error ? error : new Error(String(error)));
553
- }
554
- });
555
- }
556
- /**
557
- * Serve the review pipeline's two HTTP routes (best effort, mirroring the
558
- * estimator-catalog registration):
559
- *
560
- * - `GET .../review-queue?sessionId=…` → the session's pending proposals with
561
- * their benefit numbers. Sanitized by construction: the queue never holds
562
- * message content, and the response carries ids/seqs/counts only (digests
563
- * stay in the runtime — the client cannot need them).
564
- * - `POST .../review-decide` `{sessionId, proposalId, decision}` → one human
565
- * decision. Invalid body → 400; unknown/not-pending proposal → 404; review
566
- * mode off for the session → 503.
567
- */
568
- function registerReviewQueueRoutes(ctx) {
569
- const readService = (name) => {
570
- try {
571
- return ctx.get(name);
572
- } catch {
573
- return;
574
- }
575
- };
576
- const log = (level, message, ...args) => {
577
- console[level](message, ...args);
578
- };
579
- const registered = () => {
580
- log("info", "context-compression review queue routes registered: %s / %s", REVIEW_QUEUE_ROUTES.join(", "), REVIEW_DECIDE_ROUTES.join(", "));
581
- };
582
- const getHandler = (req, res) => {
583
- const pruner = reviewPrunerOf(readService);
584
- if (pruner === void 0 || pruner.listAllReviewProposals === void 0) {
585
- reviewJson(res, 503, {
586
- ok: false,
587
- error: "review pipeline unavailable"
588
- });
589
- return;
590
- }
591
- let sessionId = "";
592
- try {
593
- sessionId = new URL(String(req.url ?? ""), "http://localhost").searchParams.get("sessionId") ?? "";
594
- } catch {
595
- sessionId = "";
596
- }
597
- const sanitize = (sid, proposal) => ({
598
- sessionId: sid,
599
- id: proposal.id,
600
- kind: proposal.kind,
601
- items: proposal.items.map((item) => ({
602
- seq: item.seq,
603
- kind: item.kind,
604
- component: item.component,
605
- tokensBefore: item.tokensBefore,
606
- tokensAfter: item.tokensAfter
607
- })),
608
- benefit: {
609
- recoveredTokens: proposal.benefit.recoveredTokens,
610
- penaltyTokens: proposal.benefit.penaltyTokens,
611
- ...proposal.benefit.paybackTurns === void 0 ? {} : { paybackTurns: proposal.benefit.paybackTurns },
612
- ...proposal.benefit.expectedSaving === void 0 ? {} : { expectedSaving: proposal.benefit.expectedSaving }
613
- },
614
- enqueuedTurn: proposal.enqueuedTurn,
615
- lastTurnIndex: proposal.lastTurnIndex
616
- });
617
- if (sessionId === "") {
618
- const pending = pruner.listAllReviewProposals().flatMap((entry) => entry.proposals.map((proposal) => sanitize(entry.sessionId, proposal)));
619
- reviewJson(res, 200, {
620
- ok: true,
621
- total: pending.length,
622
- pending
623
- });
624
- return;
625
- }
626
- const session = sessionFor(readService, sessionId);
627
- if (session === void 0) {
628
- reviewJson(res, 404, {
629
- ok: false,
630
- error: "unknown session"
631
- });
632
- return;
633
- }
634
- const pending = pruner.listReviewProposals(session).map((proposal) => sanitize(sessionId, proposal));
635
- const summary = pruner.reviewSummary?.(session);
636
- reviewJson(res, 200, {
637
- ok: true,
638
- sessionId,
639
- total: pending.length,
640
- pending,
641
- ...summary === void 0 ? {} : { summary }
642
- });
643
- };
644
- const decideHandler = async (req, res) => {
645
- const pruner = reviewPrunerOf(readService);
646
- if (pruner === void 0) {
647
- reviewJson(res, 503, {
648
- ok: false,
649
- error: "review pipeline unavailable"
650
- });
651
- return;
652
- }
653
- let body;
654
- try {
655
- body = JSON.parse(await readRequestBody(req));
656
- } catch {
657
- body = void 0;
658
- }
659
- if (typeof body !== "object" || body === null) {
660
- reviewJson(res, 400, {
661
- ok: false,
662
- error: "invalid JSON body"
663
- });
664
- return;
665
- }
666
- const record = body;
667
- if (typeof record.sessionId !== "string" || record.sessionId === "" || typeof record.proposalId !== "string" || record.proposalId === "") {
668
- reviewJson(res, 400, {
669
- ok: false,
670
- error: "sessionId and proposalId are required"
671
- });
672
- return;
673
- }
674
- if (record.decision !== "approved" && record.decision !== "rejected" && record.decision !== "ignored") {
675
- reviewJson(res, 400, {
676
- ok: false,
677
- error: "decision must be approved, rejected, or ignored"
678
- });
679
- return;
680
- }
681
- const session = sessionFor(readService, record.sessionId);
682
- if (session === void 0) {
683
- reviewJson(res, 404, {
684
- ok: false,
685
- error: "unknown session"
686
- });
687
- return;
688
- }
689
- const outcome = pruner.decideReviewProposal(session, record.proposalId, record.decision);
690
- if (outcome === void 0) {
691
- reviewJson(res, 503, {
692
- ok: false,
693
- error: "review mode is off for this session"
694
- });
695
- return;
696
- }
697
- if (!outcome.ok) {
698
- reviewJson(res, 404, {
699
- ok: false,
700
- error: outcome.reason
701
- });
702
- return;
703
- }
704
- reviewJson(res, 200, {
705
- ok: true,
706
- sessionId: record.sessionId,
707
- proposalId: record.proposalId,
708
- decision: record.decision
709
- });
710
- };
711
- const register = (webServer) => {
712
- const disposers = [...[...REVIEW_QUEUE_ROUTES].map((path) => ({
713
- path,
714
- handler: getHandler
715
- })), ...[...REVIEW_DECIDE_ROUTES].map((path) => ({
716
- path,
717
- handler: (req, res) => {
718
- decideHandler(req, res);
719
- }
720
- }))].map((entry) => webServer.register({
721
- kind: "exact",
722
- path: entry.path,
723
- handler: entry.handler
724
- })).filter((off) => typeof off === "function");
725
- ctx.effect(() => () => {
726
- for (const off of disposers) off();
727
- }, "contextCompressionSelector.review routes");
728
- registered();
729
- };
730
- const active = asWebServer(readService("webServer"));
731
- if (active !== void 0) {
732
- register(active);
733
- return;
734
- }
735
- ctx.inject(["webServer"], (injected) => {
736
- const webServer = asWebServer(injected.webServer);
737
- if (webServer === void 0) {
738
- log("warn", "context-compression webServer exposes no register() — review routes not registered");
739
- return;
740
- }
741
- register(webServer);
742
- });
743
- log("warn", "context-compression webServer not active yet — review routes pending: %s", REVIEW_QUEUE_ROUTES.join(", "));
744
- }
745
521
  /**
746
522
  * Serve the advisory advisor's read-only report route (same registration
747
- * skeleton as the review routes):
523
+ * skeleton as the estimator-catalog route):
748
524
  *
749
525
  * `GET .../advisor-report?sessionId=…` → the session's prefix-decay figure,
750
526
  * the todolist-bound task summary, and the score distribution. Content-free
@@ -766,7 +542,7 @@ function registerAdvisorReportRoute(ctx) {
766
542
  };
767
543
  const getHandler = (req, res) => {
768
544
  if (typeof readService("agents")?.get !== "function") {
769
- reviewJson(res, 503, {
545
+ jsonResponse(res, 503, {
770
546
  ok: false,
771
547
  error: "advisor report unavailable"
772
548
  });
@@ -777,7 +553,7 @@ function registerAdvisorReportRoute(ctx) {
777
553
  sessionId = new URL(String(req.url ?? ""), "http://localhost").searchParams.get("sessionId") ?? "";
778
554
  } catch {}
779
555
  if (sessionId === "") {
780
- reviewJson(res, 400, {
556
+ jsonResponse(res, 400, {
781
557
  ok: false,
782
558
  error: "sessionId is required"
783
559
  });
@@ -785,14 +561,14 @@ function registerAdvisorReportRoute(ctx) {
785
561
  }
786
562
  const session = sessionFor(readService, sessionId);
787
563
  if (session === void 0) {
788
- reviewJson(res, 404, {
564
+ jsonResponse(res, 404, {
789
565
  ok: false,
790
566
  error: "unknown session"
791
567
  });
792
568
  return;
793
569
  }
794
570
  const state = getAdvisorState(session);
795
- reviewJson(res, 200, {
571
+ jsonResponse(res, 200, {
796
572
  ok: true,
797
573
  sessionId,
798
574
  advisor: {
@@ -805,7 +581,15 @@ function registerAdvisorReportRoute(ctx) {
805
581
  score: entry.score,
806
582
  turn: entry.turn
807
583
  })),
808
- lowRelevanceSeqs: [...state.recertified.keys()]
584
+ lowRelevanceSeqs: [...state.recertified.keys()],
585
+ lastAdvice: state.lastAdvice === void 0 ? null : {
586
+ band: state.lastAdvice.band,
587
+ turn: state.lastAdvice.turn,
588
+ itemSeqs: state.lastAdvice.itemSeqs,
589
+ recoveredTokens: state.lastAdvice.recoveredTokens,
590
+ penaltyTokens: state.lastAdvice.penaltyTokens,
591
+ paybackTurns: state.lastAdvice.paybackTurns ?? null
592
+ }
809
593
  }
810
594
  });
811
595
  };
@@ -942,7 +726,6 @@ const SHARED_SETTINGS = Symbol.for("dsh-context-compression-improved/settings-re
942
726
  const Config = z.object({
943
727
  presetOverlay: z.boolean().default(false),
944
728
  estimatorCatalogRoute: z.boolean().default(false),
945
- reviewQueueRoute: z.boolean().default(false),
946
729
  advisorReportRoute: z.boolean().default(false)
947
730
  });
948
731
  /** Register the persisted default read by the currently mounted root pruner. */
@@ -952,7 +735,6 @@ function apply(ctx, config = {}) {
952
735
  acquireSettingsRegistration(settingsCtx);
953
736
  });
954
737
  if (config.estimatorCatalogRoute === true) registerEstimatorCatalogRoute(ctx);
955
- if (config.reviewQueueRoute === true) registerReviewQueueRoutes(ctx);
956
738
  if (config.advisorReportRoute === true) registerAdvisorReportRoute(ctx);
957
739
  if (config.presetOverlay !== true) return;
958
740
  ctx.inject(["agentPresets"], (presetsCtx) => {
@@ -96,18 +96,6 @@ interface PresetOptions {
96
96
  readonly sampleLimit: number;
97
97
  readonly minTokens: number;
98
98
  };
99
- /**
100
- * Human-gated review pipeline (beta): edge/high-impact candidates queue for
101
- * manual approval and execute in one merged batch at the next turn boundary
102
- * instead of the automatic path (R4).
103
- */
104
- readonly reviewMode: boolean;
105
- /** Turn-boundary patience: pending review proposals older than this many turns auto-expire (R4). */
106
- readonly reviewTimeoutTurns: number;
107
- /** Cache-hit discount rate α in the benefit model; expectedSaving = α·R·Ŝ − (1−α)·tail. */
108
- readonly cacheHitDiscountAlpha: number;
109
- /** Candidates whose tokenBefore reaches this threshold bypass payback triage and always enter review (R4). */
110
- readonly reviewHighImpactTokens: number;
111
99
  }
112
100
  /** Common user-authored Custom stages shared by persisted policy versions. */
113
101
  interface CustomCompressionPolicyFields<HistoryPolicy> {
@@ -157,11 +145,6 @@ interface PresetOptionsSettings {
157
145
  readonly prefixStabilizer?: boolean;
158
146
  readonly readState?: boolean;
159
147
  readonly estimatorMode?: '' | 'host' | 'direct';
160
- /** Review-mode overrides (beta); see PresetOptions.reviewMode. */
161
- readonly reviewMode?: boolean;
162
- readonly reviewTimeoutTurns?: number;
163
- readonly cacheHitDiscountAlpha?: number;
164
- readonly reviewHighImpactTokens?: number;
165
148
  /**
166
149
  * Estimator endpoint fields. Persisted-settings only: they never enter the
167
150
  * frozen CompressionPolicy, which is emitted verbatim by policy-resolved
@@ -414,157 +397,7 @@ interface EstimatorFailures {
414
397
  cooldownUntil: number;
415
398
  }
416
399
  //#endregion
417
- //#region src/runtime/tokenpilot/proposal.d.ts
418
- interface BenefitEstimate {
419
- /** Net reclaimed tokens across the batch; may be ≤ 0 when a batch is not worth it. */
420
- readonly recoveredTokens: number;
421
- /** The one-time cache-refill penalty the merged mutation pays: (1−α)·tailTokens. */
422
- readonly penaltyTokens: number;
423
- /** Turns of discounted recovery needed to recoup the penalty; `undefined` when α·R ≤ 0. */
424
- readonly paybackTurns?: number;
425
- /** Discounted net benefit over the remaining session; omitted when Ŝ is unknown. */
426
- readonly expectedSaving?: number;
427
- }
428
- /** Human-facing reduction kind carried by every review proposal. */
429
- type ProposalKind = 'estimator' | 'dedup' | 'read-state';
430
- /** One frozen item inside a review proposal: metadata and digest, never content. */
431
- interface ProposalItem {
432
- readonly seq: number;
433
- readonly component: string;
434
- readonly kind: ProposalKind;
435
- readonly tokensBefore: number;
436
- readonly tokensAfter: number;
437
- /** Content sha-256 frozen at enqueue time and re-checked at the apply point. */
438
- readonly digest: string;
439
- }
440
- /** Proposal fields derivable at classification time; queue fields attach at enqueue. */
441
- interface ProposalSkeleton {
442
- readonly id: string;
443
- readonly kind: ProposalKind;
444
- readonly items: readonly ProposalItem[];
445
- readonly benefit: BenefitEstimate;
446
- }
447
- //#endregion
448
- //#region src/runtime/tokenpilot/review-queue.d.ts
449
- /** Human-side proposal status. */
450
- type ReviewProposalStatus = 'pending' | 'approved' | 'rejected' | 'ignored' | 'expired';
451
- /** Execution-side receipt built only from real mutation evidence (never estimated). */
452
- interface ReviewReceipt {
453
- readonly status: 'applied' | 'deferred';
454
- /** Reason code for deferred receipts, aligned with the upstream naming. */
455
- readonly reasonCode?: string;
456
- /** Predicted recovery from the benefit model at enqueue time. */
457
- readonly estimatedTokens: number;
458
- /** Measured recovery of the landed mutation; present only on applied. */
459
- readonly appliedTokens?: number;
460
- /** Canonical ISO timestamp of the execution evidence. */
461
- readonly updatedAt: string;
462
- }
463
- /** One queued proposal: persisted metadata, never content. */
464
- interface ReviewProposalRecord {
465
- readonly id: string;
466
- readonly sessionId: string;
467
- readonly kind: ProposalKind;
468
- readonly items: readonly {
469
- readonly seq: number;
470
- readonly component: string;
471
- readonly kind: ProposalKind;
472
- readonly tokensBefore: number;
473
- readonly tokensAfter: number;
474
- readonly digest: string;
475
- }[];
476
- readonly benefit: {
477
- readonly recoveredTokens: number;
478
- readonly penaltyTokens: number;
479
- readonly paybackTurns?: number;
480
- readonly expectedSaving?: number;
481
- };
482
- status: ReviewProposalStatus;
483
- readonly enqueuedTurn: number;
484
- lastTurnIndex: number;
485
- }
486
- /** Whole-session record: one durable KV value per session. */
487
- interface ReviewSessionRecord {
488
- readonly version: 1;
489
- readonly proposals: readonly ReviewProposalRecord[];
490
- }
491
- /** A settled proposal: the live record plus its execution receipt. */
492
- interface ReviewReceiptRecord extends ReviewProposalRecord {
493
- readonly receipt: ReviewReceipt;
494
- }
495
- /** Minimal KV face the queue persists through. */
496
- interface ReviewQueueStore {
497
- load(sessionId: string): ReviewSessionRecord | undefined;
498
- save(sessionId: string, record: ReviewSessionRecord): void;
499
- /** Session ids with live records; optional (aggregate reads degrade to none). */
500
- ids?(): readonly string[];
501
- }
502
- interface ReviewQueueOptions {
503
- /** Pending proposals older than this many turns (since lastTurnIndex) expire. */
504
- readonly timeoutTurns: number;
505
- }
506
- /** Decision outcomes for one decide call. */
507
- type DecideOutcome = {
508
- readonly ok: true;
509
- } | {
510
- readonly ok: false;
511
- readonly reason: 'unknown-proposal' | 'not-pending';
512
- };
513
- declare class ReviewQueue {
514
- private readonly store;
515
- private readonly options;
516
- constructor(store: ReviewQueueStore, options: ReviewQueueOptions);
517
- /**
518
- * Fail-open store access: a throwing seam must never break the compression
519
- * pipeline. Reads degrade to "no stored record"; writes degrade to losing
520
- * durability for that call (the store itself is expected to warn).
521
- */
522
- private safeLoad;
523
- private safeSave;
524
- private sessionRecord;
525
- /**
526
- * Queue one classified proposal. A repeated classification of the same
527
- * content re-does nothing but refresh the patience clock, so re-enqueue
528
- * cannot duplicate a live proposal.
529
- * @returns `false` when an identical live proposal already exists.
530
- */
531
- enqueue(sessionId: string, skeleton: ProposalSkeleton, turnIndex: number): boolean;
532
- /** Live pending proposals of one session, oldest enqueue first. */
533
- listPending(sessionId: string): readonly ReviewProposalRecord[];
534
- /** Approved proposals waiting for the next turn-boundary batch. */
535
- listApproved(sessionId: string): readonly ReviewProposalRecord[];
536
- /**
537
- * Transition one pending proposal. Idempotent: deciding an unknown id or a
538
- * non-pending proposal changes nothing and reports the miss.
539
- */
540
- decide(sessionId: string, id: string, decision: 'approved' | 'rejected' | 'ignored'): DecideOutcome;
541
- /**
542
- * Expire every pending proposal whose patience has run out at this turn
543
- * boundary. Expired proposals are removed from the store (the summary view
544
- * aggregates them from the audit log instead).
545
- * @returns the expired proposals, for the caller's audit emission.
546
- */
547
- expireTurn(sessionId: string, turnIndex: number): readonly ReviewProposalRecord[];
548
- /**
549
- * Settle an approved proposal with its execution receipt and retire it from
550
- * the live store. The caller is responsible for auditing the receipt; the
551
- * queue only records which proposal left and why.
552
- */
553
- recordReceipt(sessionId: string, id: string, receipt: ReviewReceipt): ReviewReceiptRecord | undefined;
554
- }
555
- //#endregion
556
400
  //#region src/pruner/state.d.ts
557
- /** Four-state per-session outcome counters behind the floating-window summary row. */
558
- interface ReviewSessionSummary {
559
- /** Rewrites that landed through the automatic path while review mode served this session. */
560
- autoApplied: number;
561
- /** Approved proposals whose merged batch executed with an applied receipt. */
562
- reviewApplied: number;
563
- /** Pending proposals that expired unhandled at a turn boundary. */
564
- expired: number;
565
- /** Approved proposals voided at the apply point (digest mismatch et al). */
566
- voided: number;
567
- }
568
401
  /** Mutable per-session state bag used inside {@link ToolResultPruner}. */
569
402
  interface PrunerState {
570
403
  /** Resolved immutable deployment configuration. */
@@ -591,22 +424,12 @@ interface PrunerState {
591
424
  readonly tailTrimBoundaryAttempts: WeakMap<Session, object>;
592
425
  /** Last effective policy audit key emitted for each Session. */
593
426
  readonly policyResolutionAudits: WeakMap<Session, string>;
594
- /**
595
- * TokenPilot-inspired R4: shared review-queue store. Starts as the in-memory
596
- * fail-open fallback; swapped to the storageDomain-backed adapter when (and
597
- * if) that seam opens successfully.
598
- */
599
- reviewStore: ReviewQueueStore;
600
- /** Per-session review queue carrying the frozen timeout policy. */
601
- readonly reviewQueues: WeakMap<Session, ReviewQueue>;
602
- /** Last observed turn index per Session: the monotonic clock for review expiries. */
603
- readonly reviewClocks: WeakMap<Session, number>;
427
+ /** Last observed turn index per Session: the monotonic clock for advisory records. */
428
+ readonly turnClocks: WeakMap<Session, number>;
604
429
  /** Estimator-reported remaining turns Ŝ per Session; advisory only. */
605
430
  readonly estimatorRemainingTurns: WeakMap<Session, number>;
606
431
  /** Per-session advisor side channel, constructed once with the advisor overrides. */
607
432
  readonly advisorChannels: WeakMap<Session, SideChannel>;
608
- /** Four-state outcome counters per Session (floating-window summary row). */
609
- readonly reviewSummaries: WeakMap<Session, ReviewSessionSummary>;
610
433
  }
611
434
  //#endregion
612
435
  //#region src/runtime/custom-policy.d.ts
@@ -930,78 +753,20 @@ declare class ToolResultPruner extends Service {
930
753
  */
931
754
  private postflightAdvisorPass;
932
755
  /**
933
- * The per-session review queue, or `undefined` while review mode is off
934
- * (every review path must then behave exactly like before).
935
- */
936
- private reviewQueueFor;
937
- /**
938
- * Monotonic per-session turn clock for review patience and expiry. Bumped by
939
- * the agent loop payloads (`pre-step` / `turn-stopping`); passes without a
940
- * turn coordinate reuse the last observed value.
941
- */
942
- private reviewClock;
943
- private auditReviewOutcome;
944
- /**
945
- * Review-mode triage hook: classify one pass's planned replacements and
946
- * withhold the review bucket from landing, enqueuing it for human approval
947
- * instead. With review mode off (or nothing planned) this is the identity.
948
- *
949
- * The digest freezes each candidate's ORIGINAL surface content, so the apply
950
- * point can prove "what is removed now is what was approved then".
951
- */
952
- private triageForReview;
953
- /**
954
- * Execute every approved proposal of one session as ONE merged replacement
955
- * batch at the current turn boundary, following the upstream applied-receipt
956
- * discipline: applied receipts are built only from real mutation evidence —
957
- * estimates never cross into applied savings.
958
- *
959
- * Per proposal: every item's frozen digest is re-checked against the current
960
- * surface content; any mismatch voids the whole proposal (deferred with a
961
- * reason code) instead of deleting something the user never approved.
962
- * Fail-open: any unexpected error only logs and leaves the queue intact.
963
- */
964
- applyApprovedProposals(session: Session): void;
965
- /**
966
- * Current surface content at one seq: the newest covering replacement's
967
- * blocks when the seq was rewritten, otherwise the original event's blocks.
968
- */
969
- private surfaceContentAt;
970
- /**
971
- * The execution-point digest check: `undefined` when every item's frozen
972
- * digest still matches the current surface content, otherwise the aligned
973
- * reason code explaining the void.
974
- */
975
- private reviewProposalVoided;
976
- private reviewSummaryFor;
977
- /** Live pending review proposals of one session; empty when review mode is off. */
978
- listReviewProposals(session: Session): readonly ReviewProposalRecord[];
979
- /**
980
- * Every session's live pending proposals, for the floating window's
981
- * aggregate badge (the client carries no session id of its own).
982
- */
983
- listAllReviewProposals(): readonly {
984
- readonly sessionId: string;
985
- readonly proposals: readonly ReviewProposalRecord[];
986
- }[];
987
- /** Four-state outcome counters of one session (floating-window summary row). */
988
- reviewSummary(session: Session): ReviewSessionSummary;
989
- /**
990
- * Record one human decision. Returns the outcome, or `undefined` when
991
- * review mode is off for this session (the route maps that to 503).
756
+ * Monotonic per-session turn clock for advisory records. Bumped by the agent
757
+ * loop payloads (`pre-step`); passes without a turn coordinate reuse the last
758
+ * observed value.
992
759
  */
993
- decideReviewProposal(session: Session, proposalId: string, decision: 'approved' | 'rejected' | 'ignored'): {
994
- ok: true;
995
- } | {
996
- ok: false;
997
- reason: 'unknown-proposal' | 'not-pending';
998
- } | undefined;
760
+ private turnClock;
999
761
  /**
1000
- * Expire stale pending proposals at one turn boundary and audit each.
1001
- * Public because tests drive it directly; the turn-stopping handler calls
1002
- * it with the loop's own turn index.
762
+ * Advisory benefit-model hook what the retired human-gated review pipeline
763
+ * left behind. It is the IDENTITY on the landing path: every plan it is given
764
+ * comes back unchanged, because a reduction must never block automatic
765
+ * processing. The model's band is published as a `reduction-advice` audit and
766
+ * snapshotted onto the advisor state for the read-only report route, so the
767
+ * cache-accounting insight survives without a gate.
1003
768
  */
1004
- expireReviewProposals(session: Session, turnIndex?: number): readonly ReviewProposalRecord[];
769
+ private adviseReplacements;
1005
770
  private activePolicy;
1006
771
  private contextWindowForRequest;
1007
772
  private runRequestBoundary;