acp-kernel 0.0.44 → 0.0.45-pr.160.14

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.
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  createInitialState
3
- } from "../chunk-DPH62BGM.js";
3
+ } from "../chunk-UX4LINT7.js";
4
4
 
5
5
  // src/persist/store.ts
6
6
  import { createHash } from "crypto";
@@ -356,7 +356,8 @@ function mergeCompressionState(parsed) {
356
356
  stats: { ...fresh.stats, ...parsed.stats ?? {} },
357
357
  nextBlockId: parsed.nextBlockId ?? fresh.nextBlockId,
358
358
  nextRunId: parsed.nextRunId ?? fresh.nextRunId,
359
- tokenSnapshot: parsed.tokenSnapshot ?? fresh.tokenSnapshot
359
+ tokenSnapshot: parsed.tokenSnapshot ?? fresh.tokenSnapshot,
360
+ absorbed: parsed.absorbed ?? fresh.absorbed
360
361
  };
361
362
  }
362
363
  export {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/persist/store.ts","../../src/persist/state-merge.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\nimport fs from \"node:fs\";\nimport fsp from \"node:fs/promises\";\nimport path from \"node:path\";\n\n/** Logger callback. Downstream routes it wherever it wants (file, stderr). */\nexport type PersistLogger = (level: \"info\" | \"warn\" | \"error\", msg: string) => void;\n\n/**\n * On-disk record shape. The store owns the envelope (version stamp, save\n * time, id) so atomicity and discovery can rely on it; the payload schema\n * is entirely downstream-owned. `payload` must be JSON-serializable.\n */\nexport interface PersistedEnvelope<T> {\n version: number;\n savedAt: number;\n id: string;\n payload: T;\n}\n\n/** A legacy (pre-envelope) record adopted on load. `version`/`savedAt`\n * preserve the source record's own stamps when present. */\nexport interface LegacyAdoption<T> {\n id: string;\n payload: T;\n version?: number;\n savedAt?: number;\n}\n\nexport interface StateStoreOptions<T> {\n /**\n * Storage root. The kernel deliberately has NO default location: the\n * downstream decides where state lives (CLI dir, XDG data dir, plugin\n * dir, temp dir in tests). All records live under this directory.\n */\n dir: string;\n /**\n * Schema version stamped into every envelope. Owned by the downstream;\n * bump it when the payload shape changes. The store itself never\n * rejects a record over its version — migration policy belongs to the\n * reader.\n */\n version: number;\n /** Debounce window for scheduleSave, ms. Default 500. */\n debounceMs?: number;\n /** Default true. When false, all writes are silent no-ops and loads\n * return empty results. */\n enabled?: boolean;\n log?: PersistLogger;\n /**\n * Relative path (under `dir`) for a record, possibly namespaced into\n * subdirectories (e.g. `openai/host-hash.json`). Default: flat\n * `<sha256(id)[:24]>.json`.\n *\n * Because the path may depend on payload fields the store only learns\n * at write time, single-record loads resolve namespaced files only\n * after loadAll() has discovered them (or the store itself wrote\n * them). The flat default name is always checked as a fallback, so\n * downstreams using a custom relPath should call loadAll() at boot.\n */\n relPath?: (id: string, payload: T) => string;\n /**\n * Adopt records written by an older, pre-envelope schema. Receives the\n * parsed JSON of any file that failed the envelope-shape check; return\n * an adoption to load it as an envelope, or null to skip it. Adopted\n * records are re-persisted in the current envelope format on the next\n * dirty write — files migrate organically, and old files keep loading\n * (same policy billion-context's proxy used for its v1→v3 migration).\n */\n legacy?: (parsed: unknown) => LegacyAdoption<T> | null;\n /**\n * Payload validation on load. Return false to skip a record (foreign\n * schema, corrupt content). Default: envelope-shape check only\n * (string id, non-null payload).\n */\n validate?: (envelope: PersistedEnvelope<T>) => boolean;\n}\n\n/**\n * Crash-safe, debounce-coalescing JSON state store. Mechanism only — lifted\n * from billion-context's proxy SessionStore and generalized:\n *\n * - atomic writes: temp file + rename, so a crash mid-write never leaves a\n * truncated record (readers see either the old or the new file)\n * - rename retries on Windows transient locks (EPERM/EBUSY/EACCES)\n * - per-id serialization so concurrent writeNow calls never interleave\n * temp-file names or reorders writes\n * - debounced scheduleSave coalesces bursts into one write; the record is\n * built at WRITE time from a builder, so late mutations are picked up\n * - loadAll skips `.tmp-*` orphans, corrupt JSON, and records whose\n * filename does not match their id — one bad file never blocks boot\n *\n * The store never deletes files. Session cleanup is a downstream policy\n * decision (kernel position: persisted state should not be deleted\n * opportunistically).\n */\nexport class StateStore<T> {\n readonly enabled: boolean;\n private readonly dir: string;\n private readonly version: number;\n private readonly debounceMs: number;\n private readonly log: PersistLogger;\n private readonly legacyFn?: (parsed: unknown) => LegacyAdoption<T> | null;\n private readonly relPathFn?: (id: string, payload: T) => string;\n private readonly validateFn: (envelope: PersistedEnvelope<T>) => boolean;\n private readonly timers = new Map<string, NodeJS.Timeout>();\n private readonly pending = new Map<string, () => T>();\n private readonly writeChains = new Map<string, Promise<void>>();\n /** id → absolute path, populated by writes and loadAll. */\n private readonly discovered = new Map<string, string>();\n /** Monotonic counter for unique temp filenames within a process. */\n private tmpSeq = 0;\n\n constructor(opts: StateStoreOptions<T>) {\n this.dir = opts.dir;\n this.version = opts.version;\n this.debounceMs = opts.debounceMs ?? 500;\n this.enabled = opts.enabled ?? true;\n this.log = opts.log ?? ((_level, _msg) => {});\n this.relPathFn = opts.relPath;\n this.legacyFn = opts.legacy;\n this.validateFn = opts.validate ?? defaultValidate;\n }\n\n /** Debounced save. Coalesces bursts; the builder runs at write time, so\n * the freshest state is always persisted. Never throws. */\n scheduleSave(id: string, build: () => T): void {\n if (!this.enabled) return;\n this.pending.set(id, build);\n const existing = this.timers.get(id);\n if (existing) return;\n const timer = setTimeout(() => {\n this.timers.delete(id);\n const builder = this.pending.get(id);\n this.pending.delete(id);\n if (!builder) return;\n // Errors are logged inside writeInner; swallowing here keeps a\n // failed timer write from becoming an unhandledRejection.\n void this.writeNow(id, builder).catch(() => {});\n }, this.debounceMs);\n timer.unref?.();\n this.timers.set(id, timer);\n }\n\n /** Immediate save, serialized per id. Rejects on write failure; a\n * failing write never breaks the chain for the next one. */\n async writeNow(id: string, build: () => T): Promise<void> {\n if (!this.enabled) return;\n // The previous promise may reject (disk full, EPERM) — catch so our\n // chain doesn't break, then run our own write.\n const prev = this.writeChains.get(id) ?? Promise.resolve();\n const next = prev.catch(() => {}).then(() => this.writeInner(id, build));\n this.writeChains.set(id, next);\n // Clean up the chain entry once settled so the Map doesn't grow. The\n // .catch() is load-bearing: .finally() returns a derived promise that\n // nobody else holds — when the chain rejects it would surface as an\n // unhandledRejection and crash the host on default Node settings.\n next\n .finally(() => {\n if (this.writeChains.get(id) === next) this.writeChains.delete(id);\n })\n .catch(() => {});\n return next;\n }\n\n /** Synchronous flush for one id. Used where the caller cannot await\n * (memory eviction, sync shutdown paths). Cancels any pending debounce\n * timer. Returns true on success, false on failure — callers that use\n * the result to drop in-memory state must NOT drop it on failure. */\n flushSync(id: string, build: () => T): boolean {\n if (!this.enabled) return true;\n const timer = this.timers.get(id);\n if (timer) {\n clearTimeout(timer);\n this.timers.delete(id);\n this.pending.delete(id);\n }\n let payload: T;\n try {\n payload = build();\n } catch (e) {\n this.log(\"error\", `[persist] builder failed for ${id}: ${errText(e)}`);\n return false;\n }\n const file = this.resolvePath(id, payload);\n const data = JSON.stringify(this.envelope(id, payload));\n try {\n fs.mkdirSync(path.dirname(file), { recursive: true });\n } catch (e) {\n this.log(\"warn\", `[persist] could not create dir ${path.dirname(file)}: ${errText(e)}`);\n }\n const tmp = this.tempPath(file);\n try {\n fs.writeFileSync(tmp, data, \"utf8\");\n // renameSync can throw EPERM/EBUSY on Windows when the dest is\n // briefly held (AV scanner, indexer, SMB). Retry briefly —\n // transient locks usually release within ms.\n let lastErr: unknown;\n for (let attempt = 0; attempt < 3; attempt++) {\n try {\n fs.renameSync(tmp, file);\n lastErr = undefined;\n break;\n } catch (e) {\n lastErr = e;\n const code = (e as NodeJS.ErrnoException).code;\n if (code !== \"EPERM\" && code !== \"EBUSY\" && code !== \"EACCES\") break;\n // brief sync backoff (Atomics.wait is the sync sleep)\n Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 20 * (attempt + 1));\n }\n }\n if (lastErr) throw lastErr;\n return true;\n } catch (e) {\n this.log(\"error\", `[persist] flushSync failed for ${id}: ${errText(e)}`);\n try {\n fs.unlinkSync(tmp);\n } catch {\n // best-effort cleanup of the orphan temp\n }\n return false;\n }\n }\n\n /** Load one record. Checks the discovered path (from a prior\n * write/loadAll), an optional relative-path hint, and the flat default\n * name. Returns null when absent, disabled, corrupt, or rejected by\n * validate. The hint covers namespaced records the store has not\n * discovered (e.g. an evicted session re-requested with its meta,\n * where the path depends on data the store cannot reconstruct from\n * the id alone). */\n loadSync(id: string, hint?: string): PersistedEnvelope<T> | null {\n if (!this.enabled) return null;\n const candidates = [\n this.discovered.get(id),\n hint ? path.join(this.dir, hint) : undefined,\n path.join(this.dir, flatFileNameFor(id)),\n ];\n for (const file of candidates) {\n if (!file) continue;\n const envelope = this.readEnvelope(file);\n if (envelope && envelope.id === id) return envelope;\n }\n return null;\n }\n\n /** Load every record under dir. Populates the discovery map (enables\n * loadSync for namespaced relPaths). Skips corrupt files, `.tmp-*`\n * orphans, and records whose filename does not match their id — one\n * bad file never blocks boot. Never throws. */\n async loadAll(): Promise<Map<string, PersistedEnvelope<T>>> {\n const out = new Map<string, PersistedEnvelope<T>>();\n if (!this.enabled) return out;\n const files = await this.walkJsonFiles(this.dir);\n for (const file of files) {\n const envelope = this.readEnvelope(file);\n if (!envelope) continue;\n const expected =\n path.basename(this.relPathOf(envelope.id, envelope.payload)) === path.basename(file) ||\n flatFileNameFor(envelope.id) === path.basename(file);\n if (!expected) {\n this.log(\"warn\", `[persist] skipping ${rel(file, this.dir)}: filename does not match record id`);\n continue;\n }\n this.discovered.set(envelope.id, file);\n out.set(envelope.id, envelope);\n }\n return out;\n }\n\n /** Whether a debounced write is pending for an id. */\n hasPending(id: string): boolean {\n return this.timers.has(id);\n }\n\n /** Ids with a pending debounced write. */\n pendingIds(): string[] {\n return [...this.timers.keys()];\n }\n\n /** Flush every pending debounced write immediately, then drain in-flight\n * writes. For graceful shutdown (SIGTERM/SIGINT). Never rejects. */\n async flushAll(): Promise<void> {\n const ids = this.pendingIds();\n for (const timer of this.timers.values()) clearTimeout(timer);\n this.timers.clear();\n const builders = new Map<string, () => T>();\n for (const id of ids) {\n const build = this.pending.get(id);\n if (build) builders.set(id, build);\n this.pending.delete(id);\n }\n await Promise.all(\n [...builders.entries()].map(([id, build]) =>\n this.writeNow(id, build).catch((e) => {\n this.log(\"error\", `[persist] shutdown flush failed for ${id}: ${errText(e)}`);\n }),\n ),\n );\n // Drain writes whose timer fired earlier but are still mid-flight.\n await Promise.allSettled([...this.writeChains.values()]);\n }\n\n /** Cancel all pending debounced writes without flushing (tests). */\n cancelAll(): void {\n for (const timer of this.timers.values()) clearTimeout(timer);\n this.timers.clear();\n this.pending.clear();\n }\n\n private async writeInner(id: string, build: () => T): Promise<void> {\n let payload: T;\n try {\n payload = build();\n } catch (e) {\n this.log(\"error\", `[persist] builder failed for ${id}: ${errText(e)}`);\n throw e;\n }\n const file = this.resolvePath(id, payload);\n try {\n await fsp.mkdir(path.dirname(file), { recursive: true });\n } catch (e) {\n this.log(\"warn\", `[persist] could not create dir ${path.dirname(file)}: ${errText(e)}`);\n }\n const tmp = this.tempPath(file);\n try {\n await fsp.writeFile(tmp, JSON.stringify(this.envelope(id, payload)), \"utf8\");\n await renameWithRetry(tmp, file);\n this.discovered.set(id, file);\n } catch (e) {\n // Wrap so a failure cleans up the .tmp orphan instead of leaving\n // it for the next write to collide with.\n await fsp.unlink(tmp).catch(() => {});\n this.log(\"error\", `[persist] write failed for ${id}: ${errText(e)}`);\n throw e;\n }\n }\n\n private envelope(id: string, payload: T): PersistedEnvelope<T> {\n return { version: this.version, savedAt: Date.now(), id, payload };\n }\n\n /** Absolute path for a record: custom relPath (guarded against path\n * escape) or the flat hash default. */\n private resolvePath(id: string, payload: T): string {\n return path.join(this.dir, this.relPathOf(id, payload));\n }\n\n private relPathOf(id: string, payload: T): string {\n const custom = this.relPathFn?.(id, payload);\n if (!custom) return flatFileNameFor(id);\n const rel = path.normalize(custom);\n if (path.isAbsolute(rel) || rel.split(/[\\\\/]+/).includes(\"..\")) {\n this.log(\"warn\", `[persist] relPath for ${id} escapes dir; using flat name`);\n return flatFileNameFor(id);\n }\n return rel;\n }\n\n /** Unique temp file next to the destination (same dir ⇒ same volume ⇒\n * rename is atomic). Prefixed `.tmp-` so loadAll skips orphans. */\n private tempPath(dest: string): string {\n const seq = this.tmpSeq++;\n return path.join(path.dirname(dest), `.tmp-${path.basename(dest, \".json\")}-${process.pid}-${seq}`);\n }\n\n /** Parse + validate one file. Corrupt or invalid records return null\n * (logged) instead of throwing — load paths must never block boot. */\n private readEnvelope(file: string): PersistedEnvelope<T> | null {\n let parsed: unknown;\n try {\n parsed = JSON.parse(fs.readFileSync(file, \"utf8\"));\n } catch (e) {\n // A missing candidate path is an expected miss (loadSync probes);\n // only genuinely unreadable/corrupt files get logged.\n if ((e as NodeJS.ErrnoException).code !== \"ENOENT\") {\n this.log(\"warn\", `[persist] skipping corrupt file ${rel(file, this.dir)}: ${errText(e)}`);\n }\n return null;\n }\n if (isEnvelopeLike(parsed)) {\n if (!this.validateFn(parsed as PersistedEnvelope<T>)) {\n this.log(\"warn\", `[persist] skipping invalid record ${rel(file, this.dir)}`);\n return null;\n }\n return parsed as PersistedEnvelope<T>;\n }\n const adopted = this.adoptLegacy(parsed);\n if (adopted) return adopted;\n this.log(\"warn\", `[persist] skipping invalid record ${rel(file, this.dir)}`);\n return null;\n }\n\n /** Wrap a legacy (pre-envelope) record as an envelope via the `legacy`\n * hook, then validate the adopted payload like any other. */\n private adoptLegacy(parsed: unknown): PersistedEnvelope<T> | null {\n const adoption = this.legacyFn ? this.legacyFn(parsed) : null;\n if (!adoption || typeof adoption.id !== \"string\" || adoption.id.length === 0 || adoption.payload == null) {\n return null;\n }\n const source = parsed as Partial<PersistedEnvelope<T>>;\n const envelope: PersistedEnvelope<T> = {\n version: adoption.version ?? source.version ?? this.version,\n savedAt: adoption.savedAt ?? source.savedAt ?? 0,\n id: adoption.id,\n payload: adoption.payload,\n };\n if (!this.validateFn(envelope)) return null;\n return envelope;\n }\n\n /** Iterative recursive walk (no readdir-recursive dependency), skipping\n * `.tmp-*` names and non-.json files. */\n private async walkJsonFiles(root: string): Promise<string[]> {\n const out: string[] = [];\n const queue: string[] = [root];\n while (queue.length > 0) {\n const dir = queue.pop()!;\n let dirents: fs.Dirent[];\n try {\n dirents = await fsp.readdir(dir, { withFileTypes: true });\n } catch {\n continue; // missing dir → no records yet\n }\n for (const d of dirents) {\n if (d.name.startsWith(\".tmp-\")) continue;\n const full = path.join(dir, d.name);\n if (d.isDirectory()) queue.push(full);\n else if (d.isFile() && d.name.endsWith(\".json\")) out.push(full);\n }\n }\n return out;\n }\n}\n\nfunction defaultValidate<T>(envelope: PersistedEnvelope<T>): boolean {\n return typeof envelope.id === \"string\" && envelope.id.length > 0 && envelope.payload != null;\n}\n\nfunction isEnvelopeLike(value: unknown): value is PersistedEnvelope<unknown> {\n if (!value || typeof value !== \"object\") return false;\n const v = value as Partial<PersistedEnvelope<unknown>>;\n return typeof v.id === \"string\" && \"payload\" in v;\n}\n\n/** Deterministic flat filename: `<sha256(id)[:24]>.json`. Truncated hash —\n * 96 bits keeps collisions unreachable for realistic id counts, and short\n * names stay greppable. */\nexport function flatFileNameFor(id: string): string {\n return createHash(\"sha256\").update(id, \"utf8\").digest(\"hex\").slice(0, 24) + \".json\";\n}\n\n/** fs.rename with brief retries on Windows transient locks (EPERM/EBUSY/\n * EACCES from AV scan, search indexer, SMB). A short delay + retry almost\n * always succeeds; other errors are real and rethrown immediately. */\nasync function renameWithRetry(src: string, dest: string): Promise<void> {\n let lastErr: unknown;\n for (let attempt = 0; attempt < 3; attempt++) {\n try {\n await fsp.rename(src, dest);\n return;\n } catch (e) {\n lastErr = e;\n const code = (e as NodeJS.ErrnoException).code;\n if (code !== \"EPERM\" && code !== \"EBUSY\" && code !== \"EACCES\") throw e;\n const { promise, resolve } = Promise.withResolvers<void>();\n setTimeout(resolve, 20 * (attempt + 1));\n await promise;\n }\n }\n throw lastErr;\n}\n\nfunction errText(e: unknown): string {\n return e instanceof Error ? e.message : String(e);\n}\n\n\nfunction rel(p: string, base: string): string {\n const r = path.relative(base, p);\n return r && !r.startsWith(\"..\") ? r : p;\n}\n","import type { CompressionState } from \"../types.js\";\nimport { createInitialState } from \"../state.js\";\n\n/**\n * Forward-compat: merge a parsed state with a fresh one so fields added in\n * later kernel versions get sane defaults instead of `undefined`. Nested\n * groups (nudge, stats) are shallow-merged per field so older snapshots\n * keep their values while newer counters default in.\n *\n * Lifted verbatim in behavior from billion-context's proxy mergeState.\n */\nexport function mergeCompressionState(parsed: CompressionState): CompressionState {\n const fresh = createInitialState();\n return {\n blocks: parsed.blocks ?? fresh.blocks,\n messageRefs: parsed.messageRefs ?? fresh.messageRefs,\n nudge: { ...fresh.nudge, ...(parsed.nudge ?? {}) },\n stats: { ...fresh.stats, ...(parsed.stats ?? {}) },\n nextBlockId: parsed.nextBlockId ?? fresh.nextBlockId,\n nextRunId: parsed.nextRunId ?? fresh.nextRunId,\n tokenSnapshot: parsed.tokenSnapshot ?? fresh.tokenSnapshot,\n };\n}\n"],"mappings":"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","names":["rel"]}
1
+ {"version":3,"sources":["../../src/persist/store.ts","../../src/persist/state-merge.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\nimport fs from \"node:fs\";\nimport fsp from \"node:fs/promises\";\nimport path from \"node:path\";\n\n/** Logger callback. Downstream routes it wherever it wants (file, stderr). */\nexport type PersistLogger = (level: \"info\" | \"warn\" | \"error\", msg: string) => void;\n\n/**\n * On-disk record shape. The store owns the envelope (version stamp, save\n * time, id) so atomicity and discovery can rely on it; the payload schema\n * is entirely downstream-owned. `payload` must be JSON-serializable.\n */\nexport interface PersistedEnvelope<T> {\n version: number;\n savedAt: number;\n id: string;\n payload: T;\n}\n\n/** A legacy (pre-envelope) record adopted on load. `version`/`savedAt`\n * preserve the source record's own stamps when present. */\nexport interface LegacyAdoption<T> {\n id: string;\n payload: T;\n version?: number;\n savedAt?: number;\n}\n\nexport interface StateStoreOptions<T> {\n /**\n * Storage root. The kernel deliberately has NO default location: the\n * downstream decides where state lives (CLI dir, XDG data dir, plugin\n * dir, temp dir in tests). All records live under this directory.\n */\n dir: string;\n /**\n * Schema version stamped into every envelope. Owned by the downstream;\n * bump it when the payload shape changes. The store itself never\n * rejects a record over its version — migration policy belongs to the\n * reader.\n */\n version: number;\n /** Debounce window for scheduleSave, ms. Default 500. */\n debounceMs?: number;\n /** Default true. When false, all writes are silent no-ops and loads\n * return empty results. */\n enabled?: boolean;\n log?: PersistLogger;\n /**\n * Relative path (under `dir`) for a record, possibly namespaced into\n * subdirectories (e.g. `openai/host-hash.json`). Default: flat\n * `<sha256(id)[:24]>.json`.\n *\n * Because the path may depend on payload fields the store only learns\n * at write time, single-record loads resolve namespaced files only\n * after loadAll() has discovered them (or the store itself wrote\n * them). The flat default name is always checked as a fallback, so\n * downstreams using a custom relPath should call loadAll() at boot.\n */\n relPath?: (id: string, payload: T) => string;\n /**\n * Adopt records written by an older, pre-envelope schema. Receives the\n * parsed JSON of any file that failed the envelope-shape check; return\n * an adoption to load it as an envelope, or null to skip it. Adopted\n * records are re-persisted in the current envelope format on the next\n * dirty write — files migrate organically, and old files keep loading\n * (same policy billion-context's proxy used for its v1→v3 migration).\n */\n legacy?: (parsed: unknown) => LegacyAdoption<T> | null;\n /**\n * Payload validation on load. Return false to skip a record (foreign\n * schema, corrupt content). Default: envelope-shape check only\n * (string id, non-null payload).\n */\n validate?: (envelope: PersistedEnvelope<T>) => boolean;\n}\n\n/**\n * Crash-safe, debounce-coalescing JSON state store. Mechanism only — lifted\n * from billion-context's proxy SessionStore and generalized:\n *\n * - atomic writes: temp file + rename, so a crash mid-write never leaves a\n * truncated record (readers see either the old or the new file)\n * - rename retries on Windows transient locks (EPERM/EBUSY/EACCES)\n * - per-id serialization so concurrent writeNow calls never interleave\n * temp-file names or reorders writes\n * - debounced scheduleSave coalesces bursts into one write; the record is\n * built at WRITE time from a builder, so late mutations are picked up\n * - loadAll skips `.tmp-*` orphans, corrupt JSON, and records whose\n * filename does not match their id — one bad file never blocks boot\n *\n * The store never deletes files. Session cleanup is a downstream policy\n * decision (kernel position: persisted state should not be deleted\n * opportunistically).\n */\nexport class StateStore<T> {\n readonly enabled: boolean;\n private readonly dir: string;\n private readonly version: number;\n private readonly debounceMs: number;\n private readonly log: PersistLogger;\n private readonly legacyFn?: (parsed: unknown) => LegacyAdoption<T> | null;\n private readonly relPathFn?: (id: string, payload: T) => string;\n private readonly validateFn: (envelope: PersistedEnvelope<T>) => boolean;\n private readonly timers = new Map<string, NodeJS.Timeout>();\n private readonly pending = new Map<string, () => T>();\n private readonly writeChains = new Map<string, Promise<void>>();\n /** id → absolute path, populated by writes and loadAll. */\n private readonly discovered = new Map<string, string>();\n /** Monotonic counter for unique temp filenames within a process. */\n private tmpSeq = 0;\n\n constructor(opts: StateStoreOptions<T>) {\n this.dir = opts.dir;\n this.version = opts.version;\n this.debounceMs = opts.debounceMs ?? 500;\n this.enabled = opts.enabled ?? true;\n this.log = opts.log ?? ((_level, _msg) => {});\n this.relPathFn = opts.relPath;\n this.legacyFn = opts.legacy;\n this.validateFn = opts.validate ?? defaultValidate;\n }\n\n /** Debounced save. Coalesces bursts; the builder runs at write time, so\n * the freshest state is always persisted. Never throws. */\n scheduleSave(id: string, build: () => T): void {\n if (!this.enabled) return;\n this.pending.set(id, build);\n const existing = this.timers.get(id);\n if (existing) return;\n const timer = setTimeout(() => {\n this.timers.delete(id);\n const builder = this.pending.get(id);\n this.pending.delete(id);\n if (!builder) return;\n // Errors are logged inside writeInner; swallowing here keeps a\n // failed timer write from becoming an unhandledRejection.\n void this.writeNow(id, builder).catch(() => {});\n }, this.debounceMs);\n timer.unref?.();\n this.timers.set(id, timer);\n }\n\n /** Immediate save, serialized per id. Rejects on write failure; a\n * failing write never breaks the chain for the next one. */\n async writeNow(id: string, build: () => T): Promise<void> {\n if (!this.enabled) return;\n // The previous promise may reject (disk full, EPERM) — catch so our\n // chain doesn't break, then run our own write.\n const prev = this.writeChains.get(id) ?? Promise.resolve();\n const next = prev.catch(() => {}).then(() => this.writeInner(id, build));\n this.writeChains.set(id, next);\n // Clean up the chain entry once settled so the Map doesn't grow. The\n // .catch() is load-bearing: .finally() returns a derived promise that\n // nobody else holds — when the chain rejects it would surface as an\n // unhandledRejection and crash the host on default Node settings.\n next\n .finally(() => {\n if (this.writeChains.get(id) === next) this.writeChains.delete(id);\n })\n .catch(() => {});\n return next;\n }\n\n /** Synchronous flush for one id. Used where the caller cannot await\n * (memory eviction, sync shutdown paths). Cancels any pending debounce\n * timer. Returns true on success, false on failure — callers that use\n * the result to drop in-memory state must NOT drop it on failure. */\n flushSync(id: string, build: () => T): boolean {\n if (!this.enabled) return true;\n const timer = this.timers.get(id);\n if (timer) {\n clearTimeout(timer);\n this.timers.delete(id);\n this.pending.delete(id);\n }\n let payload: T;\n try {\n payload = build();\n } catch (e) {\n this.log(\"error\", `[persist] builder failed for ${id}: ${errText(e)}`);\n return false;\n }\n const file = this.resolvePath(id, payload);\n const data = JSON.stringify(this.envelope(id, payload));\n try {\n fs.mkdirSync(path.dirname(file), { recursive: true });\n } catch (e) {\n this.log(\"warn\", `[persist] could not create dir ${path.dirname(file)}: ${errText(e)}`);\n }\n const tmp = this.tempPath(file);\n try {\n fs.writeFileSync(tmp, data, \"utf8\");\n // renameSync can throw EPERM/EBUSY on Windows when the dest is\n // briefly held (AV scanner, indexer, SMB). Retry briefly —\n // transient locks usually release within ms.\n let lastErr: unknown;\n for (let attempt = 0; attempt < 3; attempt++) {\n try {\n fs.renameSync(tmp, file);\n lastErr = undefined;\n break;\n } catch (e) {\n lastErr = e;\n const code = (e as NodeJS.ErrnoException).code;\n if (code !== \"EPERM\" && code !== \"EBUSY\" && code !== \"EACCES\") break;\n // brief sync backoff (Atomics.wait is the sync sleep)\n Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 20 * (attempt + 1));\n }\n }\n if (lastErr) throw lastErr;\n return true;\n } catch (e) {\n this.log(\"error\", `[persist] flushSync failed for ${id}: ${errText(e)}`);\n try {\n fs.unlinkSync(tmp);\n } catch {\n // best-effort cleanup of the orphan temp\n }\n return false;\n }\n }\n\n /** Load one record. Checks the discovered path (from a prior\n * write/loadAll), an optional relative-path hint, and the flat default\n * name. Returns null when absent, disabled, corrupt, or rejected by\n * validate. The hint covers namespaced records the store has not\n * discovered (e.g. an evicted session re-requested with its meta,\n * where the path depends on data the store cannot reconstruct from\n * the id alone). */\n loadSync(id: string, hint?: string): PersistedEnvelope<T> | null {\n if (!this.enabled) return null;\n const candidates = [\n this.discovered.get(id),\n hint ? path.join(this.dir, hint) : undefined,\n path.join(this.dir, flatFileNameFor(id)),\n ];\n for (const file of candidates) {\n if (!file) continue;\n const envelope = this.readEnvelope(file);\n if (envelope && envelope.id === id) return envelope;\n }\n return null;\n }\n\n /** Load every record under dir. Populates the discovery map (enables\n * loadSync for namespaced relPaths). Skips corrupt files, `.tmp-*`\n * orphans, and records whose filename does not match their id — one\n * bad file never blocks boot. Never throws. */\n async loadAll(): Promise<Map<string, PersistedEnvelope<T>>> {\n const out = new Map<string, PersistedEnvelope<T>>();\n if (!this.enabled) return out;\n const files = await this.walkJsonFiles(this.dir);\n for (const file of files) {\n const envelope = this.readEnvelope(file);\n if (!envelope) continue;\n const expected =\n path.basename(this.relPathOf(envelope.id, envelope.payload)) === path.basename(file) ||\n flatFileNameFor(envelope.id) === path.basename(file);\n if (!expected) {\n this.log(\"warn\", `[persist] skipping ${rel(file, this.dir)}: filename does not match record id`);\n continue;\n }\n this.discovered.set(envelope.id, file);\n out.set(envelope.id, envelope);\n }\n return out;\n }\n\n /** Whether a debounced write is pending for an id. */\n hasPending(id: string): boolean {\n return this.timers.has(id);\n }\n\n /** Ids with a pending debounced write. */\n pendingIds(): string[] {\n return [...this.timers.keys()];\n }\n\n /** Flush every pending debounced write immediately, then drain in-flight\n * writes. For graceful shutdown (SIGTERM/SIGINT). Never rejects. */\n async flushAll(): Promise<void> {\n const ids = this.pendingIds();\n for (const timer of this.timers.values()) clearTimeout(timer);\n this.timers.clear();\n const builders = new Map<string, () => T>();\n for (const id of ids) {\n const build = this.pending.get(id);\n if (build) builders.set(id, build);\n this.pending.delete(id);\n }\n await Promise.all(\n [...builders.entries()].map(([id, build]) =>\n this.writeNow(id, build).catch((e) => {\n this.log(\"error\", `[persist] shutdown flush failed for ${id}: ${errText(e)}`);\n }),\n ),\n );\n // Drain writes whose timer fired earlier but are still mid-flight.\n await Promise.allSettled([...this.writeChains.values()]);\n }\n\n /** Cancel all pending debounced writes without flushing (tests). */\n cancelAll(): void {\n for (const timer of this.timers.values()) clearTimeout(timer);\n this.timers.clear();\n this.pending.clear();\n }\n\n private async writeInner(id: string, build: () => T): Promise<void> {\n let payload: T;\n try {\n payload = build();\n } catch (e) {\n this.log(\"error\", `[persist] builder failed for ${id}: ${errText(e)}`);\n throw e;\n }\n const file = this.resolvePath(id, payload);\n try {\n await fsp.mkdir(path.dirname(file), { recursive: true });\n } catch (e) {\n this.log(\"warn\", `[persist] could not create dir ${path.dirname(file)}: ${errText(e)}`);\n }\n const tmp = this.tempPath(file);\n try {\n await fsp.writeFile(tmp, JSON.stringify(this.envelope(id, payload)), \"utf8\");\n await renameWithRetry(tmp, file);\n this.discovered.set(id, file);\n } catch (e) {\n // Wrap so a failure cleans up the .tmp orphan instead of leaving\n // it for the next write to collide with.\n await fsp.unlink(tmp).catch(() => {});\n this.log(\"error\", `[persist] write failed for ${id}: ${errText(e)}`);\n throw e;\n }\n }\n\n private envelope(id: string, payload: T): PersistedEnvelope<T> {\n return { version: this.version, savedAt: Date.now(), id, payload };\n }\n\n /** Absolute path for a record: custom relPath (guarded against path\n * escape) or the flat hash default. */\n private resolvePath(id: string, payload: T): string {\n return path.join(this.dir, this.relPathOf(id, payload));\n }\n\n private relPathOf(id: string, payload: T): string {\n const custom = this.relPathFn?.(id, payload);\n if (!custom) return flatFileNameFor(id);\n const rel = path.normalize(custom);\n if (path.isAbsolute(rel) || rel.split(/[\\\\/]+/).includes(\"..\")) {\n this.log(\"warn\", `[persist] relPath for ${id} escapes dir; using flat name`);\n return flatFileNameFor(id);\n }\n return rel;\n }\n\n /** Unique temp file next to the destination (same dir ⇒ same volume ⇒\n * rename is atomic). Prefixed `.tmp-` so loadAll skips orphans. */\n private tempPath(dest: string): string {\n const seq = this.tmpSeq++;\n return path.join(path.dirname(dest), `.tmp-${path.basename(dest, \".json\")}-${process.pid}-${seq}`);\n }\n\n /** Parse + validate one file. Corrupt or invalid records return null\n * (logged) instead of throwing — load paths must never block boot. */\n private readEnvelope(file: string): PersistedEnvelope<T> | null {\n let parsed: unknown;\n try {\n parsed = JSON.parse(fs.readFileSync(file, \"utf8\"));\n } catch (e) {\n // A missing candidate path is an expected miss (loadSync probes);\n // only genuinely unreadable/corrupt files get logged.\n if ((e as NodeJS.ErrnoException).code !== \"ENOENT\") {\n this.log(\"warn\", `[persist] skipping corrupt file ${rel(file, this.dir)}: ${errText(e)}`);\n }\n return null;\n }\n if (isEnvelopeLike(parsed)) {\n if (!this.validateFn(parsed as PersistedEnvelope<T>)) {\n this.log(\"warn\", `[persist] skipping invalid record ${rel(file, this.dir)}`);\n return null;\n }\n return parsed as PersistedEnvelope<T>;\n }\n const adopted = this.adoptLegacy(parsed);\n if (adopted) return adopted;\n this.log(\"warn\", `[persist] skipping invalid record ${rel(file, this.dir)}`);\n return null;\n }\n\n /** Wrap a legacy (pre-envelope) record as an envelope via the `legacy`\n * hook, then validate the adopted payload like any other. */\n private adoptLegacy(parsed: unknown): PersistedEnvelope<T> | null {\n const adoption = this.legacyFn ? this.legacyFn(parsed) : null;\n if (!adoption || typeof adoption.id !== \"string\" || adoption.id.length === 0 || adoption.payload == null) {\n return null;\n }\n const source = parsed as Partial<PersistedEnvelope<T>>;\n const envelope: PersistedEnvelope<T> = {\n version: adoption.version ?? source.version ?? this.version,\n savedAt: adoption.savedAt ?? source.savedAt ?? 0,\n id: adoption.id,\n payload: adoption.payload,\n };\n if (!this.validateFn(envelope)) return null;\n return envelope;\n }\n\n /** Iterative recursive walk (no readdir-recursive dependency), skipping\n * `.tmp-*` names and non-.json files. */\n private async walkJsonFiles(root: string): Promise<string[]> {\n const out: string[] = [];\n const queue: string[] = [root];\n while (queue.length > 0) {\n const dir = queue.pop()!;\n let dirents: fs.Dirent[];\n try {\n dirents = await fsp.readdir(dir, { withFileTypes: true });\n } catch {\n continue; // missing dir → no records yet\n }\n for (const d of dirents) {\n if (d.name.startsWith(\".tmp-\")) continue;\n const full = path.join(dir, d.name);\n if (d.isDirectory()) queue.push(full);\n else if (d.isFile() && d.name.endsWith(\".json\")) out.push(full);\n }\n }\n return out;\n }\n}\n\nfunction defaultValidate<T>(envelope: PersistedEnvelope<T>): boolean {\n return typeof envelope.id === \"string\" && envelope.id.length > 0 && envelope.payload != null;\n}\n\nfunction isEnvelopeLike(value: unknown): value is PersistedEnvelope<unknown> {\n if (!value || typeof value !== \"object\") return false;\n const v = value as Partial<PersistedEnvelope<unknown>>;\n return typeof v.id === \"string\" && \"payload\" in v;\n}\n\n/** Deterministic flat filename: `<sha256(id)[:24]>.json`. Truncated hash —\n * 96 bits keeps collisions unreachable for realistic id counts, and short\n * names stay greppable. */\nexport function flatFileNameFor(id: string): string {\n return createHash(\"sha256\").update(id, \"utf8\").digest(\"hex\").slice(0, 24) + \".json\";\n}\n\n/** fs.rename with brief retries on Windows transient locks (EPERM/EBUSY/\n * EACCES from AV scan, search indexer, SMB). A short delay + retry almost\n * always succeeds; other errors are real and rethrown immediately. */\nasync function renameWithRetry(src: string, dest: string): Promise<void> {\n let lastErr: unknown;\n for (let attempt = 0; attempt < 3; attempt++) {\n try {\n await fsp.rename(src, dest);\n return;\n } catch (e) {\n lastErr = e;\n const code = (e as NodeJS.ErrnoException).code;\n if (code !== \"EPERM\" && code !== \"EBUSY\" && code !== \"EACCES\") throw e;\n const { promise, resolve } = Promise.withResolvers<void>();\n setTimeout(resolve, 20 * (attempt + 1));\n await promise;\n }\n }\n throw lastErr;\n}\n\nfunction errText(e: unknown): string {\n return e instanceof Error ? e.message : String(e);\n}\n\n\nfunction rel(p: string, base: string): string {\n const r = path.relative(base, p);\n return r && !r.startsWith(\"..\") ? r : p;\n}\n","import type { CompressionState } from \"../types.js\";\nimport { createInitialState } from \"../state.js\";\n\n/**\n * Forward-compat: merge a parsed state with a fresh one so fields added in\n * later kernel versions get sane defaults instead of `undefined`. Nested\n * groups (nudge, stats) are shallow-merged per field so older snapshots\n * keep their values while newer counters default in.\n *\n * Lifted verbatim in behavior from billion-context's proxy mergeState.\n */\nexport function mergeCompressionState(parsed: CompressionState): CompressionState {\n const fresh = createInitialState();\n return {\n blocks: parsed.blocks ?? fresh.blocks,\n messageRefs: parsed.messageRefs ?? fresh.messageRefs,\n nudge: { ...fresh.nudge, ...(parsed.nudge ?? {}) },\n stats: { ...fresh.stats, ...(parsed.stats ?? {}) },\n nextBlockId: parsed.nextBlockId ?? fresh.nextBlockId,\n nextRunId: parsed.nextRunId ?? fresh.nextRunId,\n tokenSnapshot: parsed.tokenSnapshot ?? fresh.tokenSnapshot,\n absorbed: parsed.absorbed ?? fresh.absorbed,\n };\n}\n"],"mappings":"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","names":["rel"]}
@@ -1 +1 @@
1
- {"version":3,"file":"state-merge.d.ts","sourceRoot":"","sources":["../../src/persist/state-merge.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAGpD;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,gBAAgB,GAAG,gBAAgB,CAWhF"}
1
+ {"version":3,"file":"state-merge.d.ts","sourceRoot":"","sources":["../../src/persist/state-merge.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAGpD;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,gBAAgB,GAAG,gBAAgB,CAYhF"}
@@ -1 +1 @@
1
- {"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../src/state.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAErE,wBAAgB,kBAAkB,IAAI,gBAAgB,CAgBrD;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,gBAAgB,GAAG,MAAM,CAI/D;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,gBAAgB,GAAG,MAAM,CAI7D;AAED,wBAAgB,SAAS,CACvB,KAAK,EAAE,gBAAgB,EACvB,OAAO,EAAE,MAAM,GACd,gBAAgB,GAAG,SAAS,CAE9B;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,gBAAgB,GAAG,gBAAgB,EAAE,CAExE;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,gBAAgB,GAAG,GAAG,CAAC,MAAM,CAAC,CAOtE;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,gBAAgB,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAMxE;AAED,wBAAgB,eAAe,CAC7B,KAAK,EAAE,gBAAgB,EACvB,kBAAkB,EAAE,MAAM,GACzB,IAAI,CAQN"}
1
+ {"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../src/state.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAErE,wBAAgB,kBAAkB,IAAI,gBAAgB,CAiBrD;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,gBAAgB,GAAG,MAAM,CAI/D;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,gBAAgB,GAAG,MAAM,CAI7D;AAED,wBAAgB,SAAS,CACvB,KAAK,EAAE,gBAAgB,EACvB,OAAO,EAAE,MAAM,GACd,gBAAgB,GAAG,SAAS,CAE9B;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,gBAAgB,GAAG,gBAAgB,EAAE,CAExE;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,gBAAgB,GAAG,GAAG,CAAC,MAAM,CAAC,CAOtE;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,gBAAgB,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAMxE;AAED,wBAAgB,eAAe,CAC7B,KAAK,EAAE,gBAAgB,EACvB,kBAAkB,EAAE,MAAM,GACzB,IAAI,CAQN"}
@@ -1 +1 @@
1
- {"version":3,"file":"sync.d.ts","sourceRoot":"","sources":["../src/sync.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAEhE,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,gBAAgB,CAAC;IACxB,WAAW,EAAE,MAAM,EAAE,CAAC;CACvB;AAED,wBAAgB,UAAU,CACxB,QAAQ,EAAE,WAAW,EAAE,EACvB,KAAK,EAAE,gBAAgB,GACtB,UAAU,CAqEZ"}
1
+ {"version":3,"file":"sync.d.ts","sourceRoot":"","sources":["../src/sync.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAEhE,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,gBAAgB,CAAC;IACxB,WAAW,EAAE,MAAM,EAAE,CAAC;CACvB;AAED,wBAAgB,UAAU,CACxB,QAAQ,EAAE,WAAW,EAAE,EACvB,KAAK,EAAE,gBAAgB,GACtB,UAAU,CAsEZ"}
package/dist/types.d.ts CHANGED
@@ -49,6 +49,31 @@ export interface NudgeState {
49
49
  export interface CompressionStats {
50
50
  tokensCompressed: number;
51
51
  compressionCount: number;
52
+ /** Cumulative tokens reclaimed by absorb (instant tool-result hiding). Optional for pre-absorb persisted states. */
53
+ absorbedTokens?: number;
54
+ }
55
+ /** One instant tool-result absorption: the original tool-call + tool-result
56
+ * pair is hidden from view once recorded; the model's absorb call (carrying
57
+ * the distilled summary) remains as the durable record. */
58
+ export interface AbsorbRecord {
59
+ toolCallId: string;
60
+ callMessageId: string;
61
+ resultMessageId: string;
62
+ absorbCallId?: string;
63
+ summary: string;
64
+ tokensReclaimed: number;
65
+ createdAt: number;
66
+ }
67
+ export interface AbsorbConfig {
68
+ enabled: boolean;
69
+ /** Model-facing absorb tool name (adapters may rename, e.g. acp_absorb). */
70
+ toolName: string;
71
+ /** Only tool results >= this many tokens get the forced absorb prompt. 0 = all. */
72
+ minToolTokens: number;
73
+ /** Only prompt when context usage >= this fraction of modelContextLimit. 0 = size gate alone. */
74
+ contextThresholdPct: number;
75
+ /** Tool-name patterns (glob suffix allowed) never absorbable, independent of protectedTools. */
76
+ excludeTools: string[];
52
77
  }
53
78
  export interface CompressionState {
54
79
  blocks: CompressionBlock[];
@@ -61,6 +86,8 @@ export interface CompressionState {
61
86
  tokenSnapshot: Record<string, number>;
62
87
  nudge: NudgeState;
63
88
  stats: CompressionStats;
89
+ /** Instant tool-result absorption records. Optional: pre-absorb persisted states lack it. */
90
+ absorbed?: AbsorbRecord[];
64
91
  nextBlockId: number;
65
92
  nextRunId: number;
66
93
  }
@@ -113,6 +140,8 @@ export interface Config {
113
140
  preserveRecentMessages: number;
114
141
  preserveRecentTokens: number;
115
142
  modelContextLimit: number;
143
+ /** Instant tool-result absorption (see absorb.ts). Absent/disabled = feature off. */
144
+ absorb?: AbsorbConfig;
116
145
  messageFilters?: import("./filter/types.js").MessageFiltersConfig;
117
146
  }
118
147
  export type CompressMode = "range" | "message";
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "acp-kernel",
3
- "version": "0.0.44",
3
+ "version": "0.0.45-pr.160.14",
4
4
  "description": "Framework-agnostic context-compression engine (model-driven, 3-tier LSM). Pure core: no host dependency.",
5
5
  "license": "MIT",
6
6
  "author": "ranxianglei",
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/state.ts"],"sourcesContent":["import type { CompressionBlock, CompressionState } from \"./types.js\";\n\nexport function createInitialState(): CompressionState {\n return {\n blocks: [],\n messageRefs: { byRaw: {}, byRef: {} },\n tokenSnapshot: {},\n nudge: {\n lastPerMessageNudgeTokens: 0,\n lastNudgeShownTokens: 0,\n baselineTokens: 0,\n anchors: {},\n lastShownByTier: {},\n },\n stats: { tokensCompressed: 0, compressionCount: 0 },\n nextBlockId: 1,\n nextRunId: 1,\n };\n}\n\nexport function allocateBlockId(state: CompressionState): string {\n const id = state.nextBlockId;\n state.nextBlockId = Math.max(1, id) + 1;\n return `b${id}`;\n}\n\nexport function allocateRunId(state: CompressionState): string {\n const id = state.nextRunId;\n state.nextRunId = Math.max(1, id) + 1;\n return `r${id}`;\n}\n\nexport function blockById(\n state: CompressionState,\n blockId: string,\n): CompressionBlock | undefined {\n return state.blocks.find((block) => block.blockId === blockId);\n}\n\nexport function activeBlocks(state: CompressionState): CompressionBlock[] {\n return state.blocks.filter((block) => block.active);\n}\n\nexport function coveredMessageIds(state: CompressionState): Set<string> {\n const covered = new Set<string>();\n for (const block of state.blocks) {\n if (!block.active) continue;\n for (const id of block.effectiveMessageIds) covered.add(id);\n }\n return covered;\n}\n\nexport function highestActiveTier(state: CompressionState): 0 | 1 | 2 | 3 {\n let highest: 0 | 1 | 2 | 3 = 0;\n for (const block of state.blocks) {\n if (block.active && block.tier > highest) highest = block.tier;\n }\n return highest;\n}\n\nexport function advanceSurvival(\n state: CompressionState,\n promotionThreshold: number,\n): void {\n for (const block of state.blocks) {\n if (!block.active) continue;\n block.survivedCount += 1;\n if (block.survivedCount >= promotionThreshold) {\n block.generation = \"old\";\n }\n }\n}\n"],"mappings":";AAEO,SAAS,qBAAuC;AACrD,SAAO;AAAA,IACL,QAAQ,CAAC;AAAA,IACT,aAAa,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,EAAE;AAAA,IACpC,eAAe,CAAC;AAAA,IAChB,OAAO;AAAA,MACL,2BAA2B;AAAA,MAC3B,sBAAsB;AAAA,MACtB,gBAAgB;AAAA,MAChB,SAAS,CAAC;AAAA,MACV,iBAAiB,CAAC;AAAA,IACpB;AAAA,IACA,OAAO,EAAE,kBAAkB,GAAG,kBAAkB,EAAE;AAAA,IAClD,aAAa;AAAA,IACb,WAAW;AAAA,EACb;AACF;AAEO,SAAS,gBAAgB,OAAiC;AAC/D,QAAM,KAAK,MAAM;AACjB,QAAM,cAAc,KAAK,IAAI,GAAG,EAAE,IAAI;AACtC,SAAO,IAAI,EAAE;AACf;AAEO,SAAS,cAAc,OAAiC;AAC7D,QAAM,KAAK,MAAM;AACjB,QAAM,YAAY,KAAK,IAAI,GAAG,EAAE,IAAI;AACpC,SAAO,IAAI,EAAE;AACf;AAEO,SAAS,UACd,OACA,SAC8B;AAC9B,SAAO,MAAM,OAAO,KAAK,CAAC,UAAU,MAAM,YAAY,OAAO;AAC/D;AAEO,SAAS,aAAa,OAA6C;AACxE,SAAO,MAAM,OAAO,OAAO,CAAC,UAAU,MAAM,MAAM;AACpD;AAEO,SAAS,kBAAkB,OAAsC;AACtE,QAAM,UAAU,oBAAI,IAAY;AAChC,aAAW,SAAS,MAAM,QAAQ;AAChC,QAAI,CAAC,MAAM,OAAQ;AACnB,eAAW,MAAM,MAAM,oBAAqB,SAAQ,IAAI,EAAE;AAAA,EAC5D;AACA,SAAO;AACT;AAEO,SAAS,kBAAkB,OAAwC;AACxE,MAAI,UAAyB;AAC7B,aAAW,SAAS,MAAM,QAAQ;AAChC,QAAI,MAAM,UAAU,MAAM,OAAO,QAAS,WAAU,MAAM;AAAA,EAC5D;AACA,SAAO;AACT;AAEO,SAAS,gBACd,OACA,oBACM;AACN,aAAW,SAAS,MAAM,QAAQ;AAChC,QAAI,CAAC,MAAM,OAAQ;AACnB,UAAM,iBAAiB;AACvB,QAAI,MAAM,iBAAiB,oBAAoB;AAC7C,YAAM,aAAa;AAAA,IACrB;AAAA,EACF;AACF;","names":[]}