token-optimizer-opencode 1.0.8 → 1.0.13

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 (54) hide show
  1. package/dist/continuity/matcher.d.ts +15 -0
  2. package/dist/continuity/matcher.d.ts.map +1 -1
  3. package/dist/continuity/matcher.js +30 -1
  4. package/dist/continuity/matcher.js.map +1 -1
  5. package/dist/continuity/restore.d.ts +8 -1
  6. package/dist/continuity/restore.d.ts.map +1 -1
  7. package/dist/continuity/restore.js +43 -1
  8. package/dist/continuity/restore.js.map +1 -1
  9. package/dist/continuity/resume-lean.d.ts +126 -0
  10. package/dist/continuity/resume-lean.d.ts.map +1 -0
  11. package/dist/continuity/resume-lean.js +437 -0
  12. package/dist/continuity/resume-lean.js.map +1 -0
  13. package/dist/dashboard/generator.d.ts.map +1 -1
  14. package/dist/dashboard/generator.js +232 -36
  15. package/dist/dashboard/generator.js.map +1 -1
  16. package/dist/index.d.ts.map +1 -1
  17. package/dist/index.js +58 -4
  18. package/dist/index.js.map +1 -1
  19. package/dist/nudges/fresh-session-nudge.d.ts +72 -0
  20. package/dist/nudges/fresh-session-nudge.d.ts.map +1 -0
  21. package/dist/nudges/fresh-session-nudge.js +190 -0
  22. package/dist/nudges/fresh-session-nudge.js.map +1 -0
  23. package/dist/nudges/verbosity-steer.d.ts +28 -0
  24. package/dist/nudges/verbosity-steer.d.ts.map +1 -0
  25. package/dist/nudges/verbosity-steer.js +61 -0
  26. package/dist/nudges/verbosity-steer.js.map +1 -0
  27. package/dist/pricing.d.ts +58 -0
  28. package/dist/pricing.d.ts.map +1 -0
  29. package/dist/pricing.js +307 -0
  30. package/dist/pricing.js.map +1 -0
  31. package/dist/savings.baseline.test.d.ts +2 -0
  32. package/dist/savings.baseline.test.d.ts.map +1 -0
  33. package/dist/savings.baseline.test.js +100 -0
  34. package/dist/savings.baseline.test.js.map +1 -0
  35. package/dist/savings.d.ts +41 -3
  36. package/dist/savings.d.ts.map +1 -1
  37. package/dist/savings.js +296 -86
  38. package/dist/savings.js.map +1 -1
  39. package/dist/storage/trends.d.ts +61 -0
  40. package/dist/storage/trends.d.ts.map +1 -1
  41. package/dist/storage/trends.js +149 -0
  42. package/dist/storage/trends.js.map +1 -1
  43. package/dist/util/context-window.d.ts.map +1 -1
  44. package/dist/util/context-window.js +2 -1
  45. package/dist/util/context-window.js.map +1 -1
  46. package/dist/util/env.d.ts +2 -0
  47. package/dist/util/env.d.ts.map +1 -1
  48. package/dist/util/env.js +4 -0
  49. package/dist/util/env.js.map +1 -1
  50. package/package.json +1 -1
  51. package/dist/nudges/tool-call-warn.d.ts +0 -7
  52. package/dist/nudges/tool-call-warn.d.ts.map +0 -1
  53. package/dist/nudges/tool-call-warn.js +0 -20
  54. package/dist/nudges/tool-call-warn.js.map +0 -1
@@ -8,6 +8,21 @@ export interface CheckpointMatch {
8
8
  * Used as the floor input to the checkpoint_restore savings estimate. */
9
9
  rawBytes: number;
10
10
  }
11
+ /**
12
+ * Make raw checkpoint body safe to inject after a [RECOVERED DATA ...] sentinel.
13
+ *
14
+ * The body is prior-conversation text and may be attacker-influenced (a pasted
15
+ * file, fetched web content, a crafted message). Strip C0 control chars and
16
+ * DEFANG any forged RECOVERED-DATA sentinel / role-prefix so the body cannot
17
+ * "close" the data fence and smuggle the following lines in as live instructions.
18
+ *
19
+ * Mirrors Python's _neutralize_recovered_body() in measure.py exactly:
20
+ * - Strip C0 control chars except TAB and LF.
21
+ * - Defang forged "[RECOVERED..." sentinels: leading bracket becomes paren.
22
+ * - Bracket role-prefix lines (system:, user:, assistant:, etc.) so they
23
+ * cannot read as a new turn or system instruction.
24
+ */
25
+ export declare function neutralizeRecoveredBody(text: string): string;
11
26
  export declare function findBestCheckpoint(userPrompt: string, checkpoints: Array<{
12
27
  session_id: string;
13
28
  content: string;
@@ -1 +1 @@
1
- {"version":3,"file":"matcher.d.ts","sourceRoot":"","sources":["../../src/continuity/matcher.ts"],"names":[],"mappings":"AAwBA,wBAAgB,cAAc,CAAC,UAAU,EAAE,MAAM,EAAE,iBAAiB,EAAE,MAAM,GAAG,MAAM,CAcpF;AAED,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb;8EAC0E;IAC1E,QAAQ,EAAE,MAAM,CAAC;CAClB;AAUD,wBAAgB,kBAAkB,CAChC,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,KAAK,CAAC;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,CAAC,EAC7F,SAAS,EAAE,MAAM,EACjB,QAAQ,GAAE,MAAa,GACtB,eAAe,GAAG,IAAI,CAsBxB"}
1
+ {"version":3,"file":"matcher.d.ts","sourceRoot":"","sources":["../../src/continuity/matcher.ts"],"names":[],"mappings":"AAwBA,wBAAgB,cAAc,CAAC,UAAU,EAAE,MAAM,EAAE,iBAAiB,EAAE,MAAM,GAAG,MAAM,CAcpF;AAED,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb;8EAC0E;IAC1E,QAAQ,EAAE,MAAM,CAAC;CAClB;AAUD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAY5D;AAED,wBAAgB,kBAAkB,CAChC,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,KAAK,CAAC;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,CAAC,EAC7F,SAAS,EAAE,MAAM,EACjB,QAAQ,GAAE,MAAa,GACtB,eAAe,GAAG,IAAI,CA0BxB"}
@@ -43,6 +43,31 @@ function safeSlice(str, maxChars) {
43
43
  end--;
44
44
  return str.slice(0, end) + "\n[... truncated]";
45
45
  }
46
+ /**
47
+ * Make raw checkpoint body safe to inject after a [RECOVERED DATA ...] sentinel.
48
+ *
49
+ * The body is prior-conversation text and may be attacker-influenced (a pasted
50
+ * file, fetched web content, a crafted message). Strip C0 control chars and
51
+ * DEFANG any forged RECOVERED-DATA sentinel / role-prefix so the body cannot
52
+ * "close" the data fence and smuggle the following lines in as live instructions.
53
+ *
54
+ * Mirrors Python's _neutralize_recovered_body() in measure.py exactly:
55
+ * - Strip C0 control chars except TAB and LF.
56
+ * - Defang forged "[RECOVERED..." sentinels: leading bracket becomes paren.
57
+ * - Bracket role-prefix lines (system:, user:, assistant:, etc.) so they
58
+ * cannot read as a new turn or system instruction.
59
+ */
60
+ export function neutralizeRecoveredBody(text) {
61
+ if (!text)
62
+ return "";
63
+ // Strip C0 control chars except tab (\x09) and newline (\x0a).
64
+ text = text.replace(/[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/g, " ");
65
+ // Defang forged open/close sentinels: "[RECOVERED...", "[/RECOVERED...", etc.
66
+ text = text.replace(/\[(\s*\/?\s*RECOVERED\b)/gi, "($1");
67
+ // Defang role-prefix lines that could read as a new turn / system instruction.
68
+ text = text.replace(/^(\s*)(system|assistant|user|human|developer|tool|instructions?)(\s*:)/gim, "$1[$2]$3");
69
+ return text;
70
+ }
46
71
  export function findBestCheckpoint(userPrompt, checkpoints, threshold, maxChars = 2000) {
47
72
  let best = null;
48
73
  let bestScore = 0;
@@ -50,8 +75,12 @@ export function findBestCheckpoint(userPrompt, checkpoints, threshold, maxChars
50
75
  const score = scoreRelevance(userPrompt, cp.content);
51
76
  if (score > bestScore && score >= threshold) {
52
77
  bestScore = score;
78
+ // Neutralize forged sentinels / control chars before injecting.
79
+ // The raw checkpoint body is replayed prior-conversation text and is
80
+ // attacker-influenceable; it must not be able to break the data fence.
81
+ const safeContent = neutralizeRecoveredBody(safeSlice(cp.content, maxChars));
53
82
  best = {
54
- content: safeSlice(cp.content, maxChars),
83
+ content: safeContent,
55
84
  score,
56
85
  sessionId: cp.session_id,
57
86
  mode: cp.mode,
@@ -1 +1 @@
1
- {"version":3,"file":"matcher.js","sourceRoot":"","sources":["../../src/continuity/matcher.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"matcher.js","sourceRoot":"","sources":["../../src/continuity/matcher.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,11 @@
1
1
  import { type CheckpointMatch } from "./matcher.js";
2
2
  import type { TokenOptimizerConfig } from "../util/env.js";
3
- export declare function restoreCheckpoint(dataDir: string, userPrompt: string, currentSessionId: string, config: TokenOptimizerConfig): CheckpointMatch | null;
3
+ import type { TrendsStore } from "../storage/trends.js";
4
+ export declare function restoreCheckpoint(dataDir: string, userPrompt: string, currentSessionId: string, config: TokenOptimizerConfig,
5
+ /** Optional: when supplied and the prompt shows resume-intent, the lean
6
+ * reconstruction path fires and savings are credited to trends.db. */
7
+ trendsStore?: TrendsStore,
8
+ /** Working directory for same-project scoping. Comes from ctx.project.path
9
+ * or process.cwd() at call time (the caller decides). */
10
+ cwd?: string): CheckpointMatch | null;
4
11
  //# sourceMappingURL=restore.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"restore.d.ts","sourceRoot":"","sources":["../../src/continuity/restore.ts"],"names":[],"mappings":"AAGA,OAAO,EAAsB,KAAK,eAAe,EAAE,MAAM,cAAc,CAAC;AAExE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAE3D,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,MAAM,EAClB,gBAAgB,EAAE,MAAM,EACxB,MAAM,EAAE,oBAAoB,GAC3B,eAAe,GAAG,IAAI,CA6FxB"}
1
+ {"version":3,"file":"restore.d.ts","sourceRoot":"","sources":["../../src/continuity/restore.ts"],"names":[],"mappings":"AAGA,OAAO,EAAsB,KAAK,eAAe,EAAE,MAAM,cAAc,CAAC;AAExE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAM3D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAExD,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,MAAM,EAClB,gBAAgB,EAAE,MAAM,EACxB,MAAM,EAAE,oBAAoB;AAC5B;uEACuE;AACvE,WAAW,CAAC,EAAE,WAAW;AACzB;0DAC0D;AAC1D,GAAG,CAAC,EAAE,MAAM,GACX,eAAe,GAAG,IAAI,CAsIxB"}
@@ -3,7 +3,14 @@ import { join } from "node:path";
3
3
  import { Database } from "bun:sqlite";
4
4
  import { findBestCheckpoint } from "./matcher.js";
5
5
  import { sanitizeSessionId } from "../storage/session-store.js";
6
- export function restoreCheckpoint(dataDir, userPrompt, currentSessionId, config) {
6
+ import { resumeIntent, buildResumeLeanBlock, logResumeLeanSavings, } from "./resume-lean.js";
7
+ export function restoreCheckpoint(dataDir, userPrompt, currentSessionId, config,
8
+ /** Optional: when supplied and the prompt shows resume-intent, the lean
9
+ * reconstruction path fires and savings are credited to trends.db. */
10
+ trendsStore,
11
+ /** Working directory for same-project scoping. Comes from ctx.project.path
12
+ * or process.cwd() at call time (the caller decides). */
13
+ cwd) {
7
14
  if (!config.features.continuity)
8
15
  return null;
9
16
  const sessDir = join(dataDir, "token-optimizer", "sessions");
@@ -12,6 +19,41 @@ export function restoreCheckpoint(dataDir, userPrompt, currentSessionId, config)
12
19
  // DB filenames are the sanitized session id, so compare like-for-like or a
13
20
  // session whose id contains special chars would fail to exclude its own file.
14
21
  const safeCurrentId = sanitizeSessionId(currentSessionId);
22
+ // ---------------------------------------------------------------------------
23
+ // Resume-lean path: natural-language "continue prior work" → full lean block.
24
+ // Fires BEFORE the lightweight keyword-hint path so a deliberate "continue
25
+ // the token-optimizer work" request returns a full reconstruction, not a hint.
26
+ // Falls through to the lightweight hint when no same-project match is found.
27
+ // ---------------------------------------------------------------------------
28
+ if (config.features.continuity && cwd && resumeIntent(userPrompt)) {
29
+ try {
30
+ const [leanBlock, targetSid] = buildResumeLeanBlock(userPrompt, dataDir, safeCurrentId, cwd, config.checkpointRetentionDays, config.checkpointRetentionMax);
31
+ if (leanBlock && targetSid) {
32
+ // Credit the avoided cold-resume cost. Best-effort: never breaks injection.
33
+ if (trendsStore) {
34
+ const leanRawBytes = Buffer.byteLength(leanBlock, "utf8");
35
+ logResumeLeanSavings(trendsStore, targetSid, leanBlock, leanRawBytes);
36
+ }
37
+ // Wrap as a CheckpointMatch so the caller can inject it uniformly.
38
+ return {
39
+ content: leanBlock,
40
+ score: 1.0,
41
+ sessionId: targetSid,
42
+ mode: "resume_lean",
43
+ rawBytes: Buffer.byteLength(leanBlock, "utf8"),
44
+ };
45
+ }
46
+ // No same-project match. When cwd is known, do NOT fall through to the
47
+ // (un-gated) lightweight hint — that could surface a DIFFERENT project's
48
+ // checkpoint for an explicit "continue our work" request. Return null
49
+ // instead. Only fall through when cwd is absent (best-effort mode).
50
+ if (cwd)
51
+ return null;
52
+ }
53
+ catch {
54
+ // Best-effort: never break normal continuity over the lean path.
55
+ }
56
+ }
15
57
  const cutoff = config.checkpointRetentionDays <= 0
16
58
  ? 0
17
59
  : Date.now() / 1000 - config.checkpointRetentionDays * 86400;
@@ -1 +1 @@
1
- {"version":3,"file":"restore.js","sourceRoot":"","sources":["../../src/continuity/restore.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"restore.js","sourceRoot":"","sources":["../../src/continuity/restore.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACpE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,EAAE,kBAAkB,EAAwB,MAAM,cAAc,CAAC;AACxE,OAAO,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AAEhE,OAAO,EACL,YAAY,EACZ,oBAAoB,EACpB,oBAAoB,GACrB,MAAM,kBAAkB,CAAC;AAG1B,MAAM,UAAU,iBAAiB,CAC/B,OAAe,EACf,UAAkB,EAClB,gBAAwB,EACxB,MAA4B;AAC5B;uEACuE;AACvE,WAAyB;AACzB;0DAC0D;AAC1D,GAAY;IAEZ,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,UAAU;QAAE,OAAO,IAAI,CAAC;IAE7C,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,EAAE,iBAAiB,EAAE,UAAU,CAAC,CAAC;IAC7D,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC;QAAE,OAAO,IAAI,CAAC;IAEtC,2EAA2E;IAC3E,8EAA8E;IAC9E,MAAM,aAAa,GAAG,iBAAiB,CAAC,gBAAgB,CAAC,CAAC;IAE1D,8EAA8E;IAC9E,8EAA8E;IAC9E,2EAA2E;IAC3E,+EAA+E;IAC/E,6EAA6E;IAC7E,8EAA8E;IAC9E,IAAI,MAAM,CAAC,QAAQ,CAAC,UAAU,IAAI,GAAG,IAAI,YAAY,CAAC,UAAU,CAAC,EAAE,CAAC;QAClE,IAAI,CAAC;YACH,MAAM,CAAC,SAAS,EAAE,SAAS,CAAC,GAAG,oBAAoB,CACjD,UAAU,EACV,OAAO,EACP,aAAa,EACb,GAAG,EACH,MAAM,CAAC,uBAAuB,EAC9B,MAAM,CAAC,sBAAsB,CAC9B,CAAC;YACF,IAAI,SAAS,IAAI,SAAS,EAAE,CAAC;gBAC3B,4EAA4E;gBAC5E,IAAI,WAAW,EAAE,CAAC;oBAChB,MAAM,YAAY,GAAG,MAAM,CAAC,UAAU,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;oBAC1D,oBAAoB,CAAC,WAAW,EAAE,SAAS,EAAE,SAAS,EAAE,YAAY,CAAC,CAAC;gBACxE,CAAC;gBACD,mEAAmE;gBACnE,OAAO;oBACL,OAAO,EAAE,SAAS;oBAClB,KAAK,EAAE,GAAG;oBACV,SAAS,EAAE,SAAS;oBACpB,IAAI,EAAE,aAAa;oBACnB,QAAQ,EAAE,MAAM,CAAC,UAAU,CAAC,SAAS,EAAE,MAAM,CAAC;iBAC/C,CAAC;YACJ,CAAC;YACD,uEAAuE;YACvE,yEAAyE;YACzE,sEAAsE;YACtE,oEAAoE;YACpE,IAAI,GAAG;gBAAE,OAAO,IAAI,CAAC;QACvB,CAAC;QAAC,MAAM,CAAC;YACP,iEAAiE;QACnE,CAAC;IACH,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,CAAC,uBAAuB,IAAI,CAAC;QAChD,CAAC,CAAC,CAAC;QACH,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,GAAG,MAAM,CAAC,uBAAuB,GAAG,KAAK,CAAC;IAC/D,MAAM,cAAc,GAKf,EAAE,CAAC;IAER,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC;QACtC,gEAAgE;QAChE,0EAA0E;QAC1E,qEAAqE;QACrE,kCAAkC;QAClC,MAAM,MAAM,GAAG,QAAQ;aACpB,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;aAChC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;YACT,IAAI,OAAO,GAAG,CAAC,CAAC;YAChB,IAAI,CAAC;gBAAC,OAAO,GAAG,QAAQ,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;YAAC,CAAC;YAAC,MAAM,CAAC,CAAC,yBAAyB,CAAC,CAAC;YACzF,OAAO,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC;QACxB,CAAC,CAAC;aACD,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC;QAEzC,2EAA2E;QAC3E,0EAA0E;QAC1E,2EAA2E;QAC3E,uBAAuB;QACvB,MAAM,aAAa,GACjB,MAAM,CAAC,uBAAuB,GAAG,CAAC;YAChC,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,MAAM,CAAC,uBAAuB,GAAG,KAAK,GAAG,IAAI;YAC5D,CAAC,CAAC,CAAC,CAAC;QACR,MAAM,KAAK,GAAkB,EAAE,CAAC;QAChC,KAAK,MAAM,IAAI,IAAI,MAAM,EAAE,CAAC;YAC1B,MAAM,GAAG,GAAG,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;YACtC,MAAM,OAAO,GACX,aAAa,GAAG,CAAC,IAAI,IAAI,CAAC,OAAO,GAAG,CAAC,IAAI,IAAI,CAAC,OAAO,GAAG,aAAa,CAAC;YACxE,IAAI,OAAO,IAAI,GAAG,KAAK,aAAa,EAAE,CAAC;gBACrC,IAAI,CAAC;oBAAC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;gBAAC,CAAC;gBAAC,MAAM,CAAC,CAAC,iBAAiB,CAAC,CAAC;gBACnF,SAAS;YACX,CAAC;YACD,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACnB,CAAC;QAED,MAAM,OAAO,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,MAAM,CAAC,sBAAsB,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAE9E,KAAK,MAAM,IAAI,IAAI,OAAO,EAAE,CAAC;YAC3B,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;YAC1C,IAAI,SAAS,KAAK,aAAa;gBAAE,SAAS;YAE1C,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;YACnC,IAAI,EAAE,GAAoB,IAAI,CAAC;YAC/B,IAAI,CAAC;gBACH,EAAE,GAAG,IAAI,QAAQ,CAAC,MAAM,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;gBAC9C,EAAE,CAAC,IAAI,CAAC,yBAAyB,CAAC,CAAC;gBACnC,MAAM,IAAI,GAAG,EAAE;qBACZ,KAAK,CACJ,qHAAqH,CACtH;qBACA,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,sBAAsB,CAK1C,CAAC;gBACH,cAAc,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC;YAC/B,CAAC;YAAC,MAAM,CAAC;gBACP,0BAA0B;YAC5B,CAAC;oBAAS,CAAC;gBACT,EAAE,EAAE,KAAK,EAAE,CAAC;YACd,CAAC;QACH,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;IAED,IAAI,cAAc,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAE7C,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,GAAG,CAAC,CAAC,UAAU,CAAC,CAAC;IAC3D,MAAM,UAAU,GAAG,cAAc,CAAC,KAAK,CAAC,CAAC,EAAE,MAAM,CAAC,sBAAsB,CAAC,CAAC;IAE1E,OAAO,kBAAkB,CAAC,UAAU,EAAE,UAAU,EAAE,MAAM,CAAC,kBAAkB,EAAE,MAAM,CAAC,kBAAkB,CAAC,CAAC;AAC1G,CAAC"}
@@ -0,0 +1,126 @@
1
+ /**
2
+ * Cold-resume-lean: natural-language auto-resume for OpenCode.
3
+ *
4
+ * When the user's first message shows resume intent ("continue the token-optimizer
5
+ * work", "what we discussed last session"), inject a FULL lean reconstruction of
6
+ * the right same-project prior session — no command, no id.
7
+ *
8
+ * Token-free: pure SQLite + in-memory reads; no LLM, no subprocess.
9
+ *
10
+ * Port from Python: skills/token-optimizer/scripts/measure.py
11
+ * Functions ported: _resume_intent, _RESUME_INTENT_RE, _RESUME_TOPIC_STOPWORDS,
12
+ * _resume_topic_score, _checkpoint_in_project, _continuity_resume_block,
13
+ * build_lean_resume_context, _resume_lean_already_credited, _log_resume_lean_savings.
14
+ *
15
+ * Key structural difference from Python: opencode stores checkpoints in per-session
16
+ * SQLite DBs (session_store.ts checkpoints table) — NOT in JSON sidecar files. The
17
+ * sidecar fields (active_task, continuation, open_questions, recent_reads, git,
18
+ * quality) don't exist; we reconstruct from what IS available: active_files[]
19
+ * (JSON), decisions[] (JSON), and the content text. The "thin tier" is therefore
20
+ * more common here. Savings use tokens_cache_write (closest proxy to Python's
21
+ * cache_create tokens) or rawBytes fallback.
22
+ */
23
+ import type { TrendsStore } from "../storage/trends.js";
24
+ /**
25
+ * Matches natural-language cues that the user wants to pick up prior work.
26
+ * Kept tight to avoid firing on incidental "continue to the next file".
27
+ * MUST NOT match bare "continue" without a contextual modifier.
28
+ */
29
+ export declare const RESUME_INTENT_RE: RegExp;
30
+ /** True when the prompt asks to continue/recall prior work. */
31
+ export declare function resumeIntent(text: string): boolean;
32
+ /**
33
+ * Generic glue words that carry no topic once the resume cue is removed.
34
+ * Keeping them would let "session"/"work" falsely match every checkpoint.
35
+ *
36
+ * Note: opencode's scoreRelevance in matcher.ts already strips STOP_WORDS and
37
+ * uses pure keyword precision, but it would short-circuit on "continue"/"resume"
38
+ * inflating every checkpoint to a high score. We compute residual precision
39
+ * against the checkpoint's stored content instead.
40
+ */
41
+ export declare const RESUME_TOPIC_STOPWORDS: Set<string>;
42
+ /**
43
+ * Precision of the prompt's RESIDUAL topic words (after removing resume cues)
44
+ * against a checkpoint's content text.
45
+ *
46
+ * Unlike scoreRelevance (matcher.ts), this does NOT short-circuit on bare
47
+ * "continue"/"resume" cues, so a named topic ("the keepwarm one") scores
48
+ * higher than a vague "continue last session" → residual empty → score 0.0.
49
+ *
50
+ * @param prompt The user's first message.
51
+ * @param content The checkpoint's full content string from the DB.
52
+ */
53
+ export declare function resumeTopicScore(prompt: string, content: string): number;
54
+ /**
55
+ * True when a checkpoint's working set lives under the current project dir.
56
+ *
57
+ * In opencode, active_files is a JSON-encoded string[] column from the
58
+ * checkpoints table. We use it as the "recent_reads + modified_files"
59
+ * equivalent. Path-prefix based, no DB join needed.
60
+ */
61
+ export declare function checkpointInProject(activeFilesJson: string, cwd: string): boolean;
62
+ /** Topic bar: above this, the prompt names a topic (keyword winner); below it, most-recent. */
63
+ export declare const RESUME_TOPIC_BAR: number;
64
+ /**
65
+ * A DB row from the checkpoints table, enriched with the session DB file path
66
+ * and mtime (for recency ordering).
67
+ */
68
+ interface CheckpointRow {
69
+ session_id: string;
70
+ trigger: string;
71
+ mode: string;
72
+ quality_score: number | null;
73
+ fill_pct: number | null;
74
+ active_files: string;
75
+ decisions: string;
76
+ content: string;
77
+ created_at: number;
78
+ /** Path of the session DB file that holds this checkpoint (for dedup) */
79
+ dbPath: string;
80
+ }
81
+ /**
82
+ * Build a LEAN context block from a checkpoint row.
83
+ *
84
+ * Faithful tier (checkpoint present): active files, decisions, topic summary,
85
+ * quality, mode. Thin tier (no decisions / empty content): clearly flagged.
86
+ * Fenced as RECOVERED DATA so a fresh session treats it as context, not instructions.
87
+ *
88
+ * DEVIATION from Python: Python's sidecar has rich fields (active_task,
89
+ * continuation, open_questions, recent_reads, git). opencode's checkpoint DB
90
+ * stores (active_files[], decisions[], content text, mode, quality_score,
91
+ * fill_pct). We surface what we have; the "thin tier" is hit more often here.
92
+ */
93
+ export declare function buildLeanResumeContext(cp: CheckpointRow, sessionId: string, maxChars?: number): string;
94
+ /**
95
+ * When the user asks to continue prior work, return a FULL lean reconstruction
96
+ * of the right same-project session, or "" to fall through to the lightweight
97
+ * hint (or no-op when no match).
98
+ *
99
+ * Selection ("both", per spec):
100
+ * - best residual score >= RESUME_TOPIC_BAR → keyword winner (recency breaks ties)
101
+ * - else → most-recent same-project checkpoint
102
+ *
103
+ * Returns [block, targetSessionId] or ["", ""] on no match.
104
+ */
105
+ export declare function buildResumeLeanBlock(userPrompt: string, dataDir: string, currentSessionId: string, cwd: string, retentionDays?: number, maxCandidates?: number): [string, string];
106
+ /**
107
+ * Credit the cold-resume cost avoided by reconstructing a session lean instead
108
+ * of a full --resume cold-rewrite.
109
+ *
110
+ * Avoided cost (in priority order, matching Python's _log_resume_lean_savings):
111
+ * 1. tokens_cache_write from session_log (the real cold-rewrite cost, closest
112
+ * proxy to Python's cache_create_1h_tokens + cache_create_5m_tokens).
113
+ * 2. checkpointRawBytes / CHARS_PER_TOKEN (conservative byte-size proxy).
114
+ * 3. If neither is available: credit 0. NO generous heuristic (e.g. lean*10).
115
+ * Per PRIME DIRECTIVE: never-overcount wins every tradeoff.
116
+ *
117
+ * Cross-session dedup: calls TrendsStore.hasRecentSavingsEvent to ensure the
118
+ * same cold session is credited at most once per 6h window, even if reopened
119
+ * from two different fresh sessions. Mirrors Python's _resume_lean_already_credited
120
+ * which dedups on the TARGET session_uuid within 6h.
121
+ *
122
+ * Idempotent per target session within ~6h. Best-effort: never breaks injection.
123
+ */
124
+ export declare function logResumeLeanSavings(trendsStore: TrendsStore, targetSessionId: string, leanBlock: string, checkpointRawBytes?: number): void;
125
+ export {};
126
+ //# sourceMappingURL=resume-lean.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resume-lean.d.ts","sourceRoot":"","sources":["../../src/continuity/resume-lean.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAKH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAMxD;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,QAqB5B,CAAC;AAEF,+DAA+D;AAC/D,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAElD;AAMD;;;;;;;;GAQG;AACH,eAAO,MAAM,sBAAsB,aAKjC,CAAC;AAEH;;;;;;;;;;GAUG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CA4BxE;AAMD;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,eAAe,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAgCjF;AAMD,+FAA+F;AAC/F,eAAO,MAAM,gBAAgB,QAE5B,CAAC;AAcF;;;GAGG;AACH,UAAU,aAAa;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,yEAAyE;IACzE,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,sBAAsB,CACpC,EAAE,EAAE,aAAa,EACjB,SAAS,EAAE,MAAM,EACjB,QAAQ,GAAE,MAAuB,GAChC,MAAM,CAqFR;AAsFD;;;;;;;;;;GAUG;AACH,wBAAgB,oBAAoB,CAClC,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,MAAM,EACf,gBAAgB,EAAE,MAAM,EACxB,GAAG,EAAE,MAAM,EACX,aAAa,GAAE,MAAU,EACzB,aAAa,GAAE,MAAW,GACzB,CAAC,MAAM,EAAE,MAAM,CAAC,CAyClB;AAaD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,oBAAoB,CAClC,WAAW,EAAE,WAAW,EACxB,eAAe,EAAE,MAAM,EACvB,SAAS,EAAE,MAAM,EACjB,kBAAkB,GAAE,MAAU,GAC7B,IAAI,CAwCN"}