@vortex-os/base 0.12.1 → 0.13.0

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.
@@ -57,8 +57,9 @@ function moduleDir(ctx, moduleName) {
57
57
  import { existsSync, readFileSync } from "fs";
58
58
  import { join as join2 } from "path";
59
59
  var DEFAULT_CONFIG = {
60
- autoRecord: { sessionStart: true, worklog: true, decision: true, ambientRecall: true, archive: true, vectorize: true, vectorizeAutoDownload: true, commitFrameworkChanges: true, handoff: true, handoffRetentionDays: 7, reindex: true, backfill: true, failures: true },
60
+ autoRecord: { sessionStart: true, worklog: true, decision: true, ambientRecall: true, archive: true, archiveCommit: true, archiveAtEnd: true, archiveRawRetentionDays: 30, vectorize: true, vectorizeAutoDownload: true, commitFrameworkChanges: true, handoff: true, handoffRetentionDays: 7, reindex: true, backfill: true, failures: true },
61
61
  updates: { check: "session" },
62
+ sync: { autoPush: false },
62
63
  environments: []
63
64
  };
64
65
  function vortexConfigPath(ctx) {
@@ -104,6 +105,12 @@ function loadVortexConfig(ctx) {
104
105
  const reindex = rawAuto.reindex === void 0 ? true : rawAuto.reindex === true;
105
106
  const backfill = rawAuto.backfill === void 0 ? true : rawAuto.backfill === true;
106
107
  const failures = rawAuto.failures === void 0 ? true : rawAuto.failures === true;
108
+ const archiveCommit = rawAuto.archiveCommit === void 0 ? true : rawAuto.archiveCommit === true;
109
+ const archiveAtEnd = rawAuto.archiveAtEnd === void 0 ? true : rawAuto.archiveAtEnd === true;
110
+ const rawRetention = rawAuto.archiveRawRetentionDays;
111
+ const archiveRawRetentionDays = typeof rawRetention === "number" && Number.isFinite(rawRetention) && rawRetention >= 0 ? Math.floor(rawRetention) : rawRetention === false ? 0 : DEFAULT_CONFIG.autoRecord.archiveRawRetentionDays;
112
+ const rawSync = raw.sync && typeof raw.sync === "object" && !Array.isArray(raw.sync) ? raw.sync : {};
113
+ const autoPush = rawSync.autoPush === true;
107
114
  const rawDays = rawAuto.handoffRetentionDays;
108
115
  const handoffRetentionDays = typeof rawDays === "number" && Number.isFinite(rawDays) && rawDays > 0 ? Math.floor(rawDays) : DEFAULT_CONFIG.autoRecord.handoffRetentionDays;
109
116
  return {
@@ -116,9 +123,13 @@ function loadVortexConfig(ctx) {
116
123
  handoffRetentionDays,
117
124
  reindex,
118
125
  backfill,
119
- failures
126
+ failures,
127
+ archiveCommit,
128
+ archiveAtEnd,
129
+ archiveRawRetentionDays
120
130
  },
121
131
  updates: { check },
132
+ sync: { autoPush },
122
133
  environments
123
134
  };
124
135
  } catch {
@@ -298,4 +309,4 @@ export {
298
309
  atomicWriteFile,
299
310
  dist_exports
300
311
  };
301
- //# sourceMappingURL=chunk-T53UWSTR.js.map
312
+ //# sourceMappingURL=chunk-IZIAMK3L.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../core/src/frontmatter.ts","../../core/src/paths.ts","../../core/src/config.ts","../../core/src/index.ts","../../core/src/types.ts","../../core/src/privacy.ts","../../core/src/safe-fs.ts"],"sourcesContent":["import { parse as parseYaml, stringify as stringifyYaml } from \"yaml\";\nimport type { FrontmatterDoc, Privacy } from \"./types.js\";\n\nconst FENCE = /^---\\r?\\n([\\s\\S]*?)\\r?\\n---\\r?\\n?/;\nconst BOM = \"\";\n\n/**\n * Parse a markdown source into its YAML frontmatter and body. If no frontmatter\n * fence is present, returns an empty frontmatter object and the source unchanged.\n *\n * A leading UTF-8 BOM is stripped before matching. Windows-authored files\n * frequently begin with ``, which would otherwise prevent the fence\n * regex from anchoring to `---` at byte 0.\n */\nexport function parseFrontmatter<T = Record<string, unknown>>(\n source: string,\n): FrontmatterDoc<T> {\n const cleaned = source.startsWith(BOM) ? source.slice(1) : source;\n const match = cleaned.match(FENCE);\n if (!match) {\n return {\n frontmatter: {} as T & { privacy?: Privacy },\n body: cleaned,\n };\n }\n const yaml = match[1] ?? \"\";\n const body = cleaned.slice(match[0].length);\n let parsed: T & { privacy?: Privacy };\n try {\n const value: unknown = parseYaml(yaml);\n // Frontmatter must be a mapping. Scalars, arrays, and null (e.g. a fence\n // holding only a string, a list, or nothing) are normalized to an empty\n // object so the result keeps its record shape and downstream code that\n // assumes key access cannot crash.\n parsed =\n typeof value === \"object\" && value !== null && !Array.isArray(value)\n ? (value as T & { privacy?: Privacy })\n : ({} as T & { privacy?: Privacy });\n } catch {\n // The fence was present but its YAML body did not parse — often the\n // result of copy/paste artifacts (a stray `---` mid-document, headings\n // accidentally pasted inside the fence). Treat as no frontmatter so\n // callers can still read the body; the fence content is discarded.\n parsed = {} as T & { privacy?: Privacy };\n }\n return { frontmatter: parsed, body };\n}\n\n/**\n * Serialize a `FrontmatterDoc` back into markdown text. If the frontmatter is\n * empty, the body is returned unchanged (no empty fence is emitted).\n */\nexport function serializeFrontmatter<T = Record<string, unknown>>(\n doc: FrontmatterDoc<T>,\n): string {\n const keys = Object.keys(doc.frontmatter ?? {});\n if (keys.length === 0) return doc.body;\n const yaml = stringifyYaml(doc.frontmatter).trimEnd();\n return `---\\n${yaml}\\n---\\n${doc.body}`;\n}\n","import { resolve, join } from \"node:path\";\nimport type { ModuleContext } from \"./types.js\";\n\n/**\n * Build a `ModuleContext` for the given VortEX repository root. The root is\n * resolved to an absolute path; standard subdirectories are derived by\n * convention and are not guaranteed to exist on disk.\n */\nexport function makeContext(repoRoot: string): ModuleContext {\n const root = resolve(repoRoot);\n return {\n repoRoot: root,\n agentDir: join(root, \".agent\"),\n dataDir: join(root, \"data\"),\n modulesDir: join(root, \"modules\"),\n pluginsDir: join(root, \"plugins\"),\n };\n}\n\n/**\n * Resolve the directory of a named module within the repository.\n */\nexport function moduleDir(ctx: ModuleContext, moduleName: string): string {\n return join(ctx.modulesDir, moduleName);\n}\n","import { existsSync, readFileSync } from \"node:fs\";\r\nimport { join } from \"node:path\";\r\nimport type { ModuleContext } from \"./types.js\";\r\n\r\n/**\r\n * Instance configuration for VortEX, read from `<agentDir>/vortex.json`.\r\n *\r\n * The config exists to give the user control over the **auto-maintained**\r\n * operational behaviors (see `AI-RULES.md` → \"Default behaviors\") without\r\n * touching code: toggle any auto-record off, and declare how this machine's\r\n * environment is detected. A fresh instance has no config file — everything\r\n * is on, no environment — so the framework works out of the box and the file\r\n * is purely opt-in tuning.\r\n */\r\n\r\n/** Which auto-maintained operational records are enabled. Default: all on. */\r\nexport interface AutoRecordConfig {\r\n readonly sessionStart: boolean;\r\n readonly worklog: boolean;\r\n readonly decision: boolean;\r\n readonly ambientRecall: boolean;\r\n /**\r\n * Catch-up: at session start, ingest conversation transcripts that have not\r\n * been archived yet (the host's own machine only). Text is stored\r\n * immediately; vectorization is deferred to recall/rebuild so start stays\r\n * fast. Off → no automatic ingest; the archive only grows when explicitly\r\n * rebuilt.\r\n */\r\n readonly archive: boolean;\r\n /**\r\n * Auto-commit the conversation archive. After catch-up writes new transcripts\r\n * into `data/_session-archive/`, commit exactly that pathspec — and nothing\r\n * else in the working tree — so archived conversations never linger as the\r\n * \"N uncommitted change(s)\" noise in the next session-start report. Runs at\r\n * session start, session end, and `vortex catch-up`; gated additionally on a\r\n * healthy repo (no in-progress merge/rebase/lock, not a detached HEAD).\r\n * Best-effort: any git failure is a silent skip. On by default; off → the\r\n * archive still grows but you commit it yourself.\r\n */\r\n readonly archiveCommit: boolean;\r\n /**\r\n * Run catch-up (ingest + the auto-commit above) at session END too, not only\r\n * at start. The ending session itself is usually still skipped (its\r\n * transcript was just written, so the live-session guard catches it; if it\r\n * is ingested after a long idle pause, later transcript writes advance the\r\n * source mtime and the next catch-up re-ingests — the archive self-heals),\r\n * but every OTHER session closed earlier lands in the archive the moment you\r\n * wrap up, instead of waiting for the next session start. Crash-safe: if the\r\n * end hook never fires, the next start catches up as before. On by default.\r\n */\r\n readonly archiveAtEnd: boolean;\r\n /**\r\n * Days to keep the RAW transcript copies (`_session-archive/raw/`) before the\r\n * sweep deletes them from the local working tree. Raw files are byte-for-byte\r\n * duplicates kept for debugging/fidelity; recall, reindex, and cross-machine\r\n * sync all use only the NORMALIZED files (which are kept forever), so this\r\n * deletion loses nothing. Default 30; `0` disables the sweep entirely. A\r\n * malformed value falls back to the default.\r\n */\r\n readonly archiveRawRetentionDays: number;\r\n /**\r\n * Auto-vectorize: at session start (after catch-up), embed any\r\n * newly-archived sessions and memories so semantic recall — including the\r\n * ambient \"did we discuss this?\" path — stays current without a manual\r\n * rebuild. **Gated on the recall index already existing** (`_indexes/\r\n * memory.sqlite`): until the user has set recall up once (which downloads the\r\n * embedding model), this is a silent no-op, so it never triggers a download\r\n * on its own. On by default; off → vectors only update on an explicit\r\n * `/recall` / rebuild.\r\n */\r\n readonly vectorize: boolean;\r\n /**\r\n * Auto-download the embedding model the FIRST time recall is set up. When\r\n * `@vortex-os/memory-extended` is installed but the recall index does not\r\n * exist yet, session start kicks off a **background** worker that downloads\r\n * the model (~470 MB, once) and builds the index — so installing the add-on\r\n * is itself the opt-in and search turns on without a manual `/recall`. The\r\n * download never blocks session start (it runs detached). On by default; set\r\n * `false` — or env `VORTEX_VECTORIZE_AUTO_DOWNLOAD=0` — to keep the first\r\n * download manual (metered connections, CI). Independent of `vectorize`: if\r\n * `vectorize` is off, nothing runs regardless.\r\n */\r\n readonly vectorizeAutoDownload: boolean;\r\n /**\r\n * Auto-commit framework bookkeeping. When `vortex update` refreshes\r\n * framework-owned files it also rewrites the ownership manifest\r\n * (`data/.vortex/ownership.json`); on its own that leaves an uncommitted\r\n * \"trail\" that the next session reports as carried-over work, even though it\r\n * is plumbing the user never touched. With this on, `vortex update` commits\r\n * exactly the framework files it changed (the manifest + the templates it\r\n * refreshed) — and nothing else in the working tree — so an update leaves a\r\n * clean tree. Best-effort: a non-git folder, or any git failure, is a silent\r\n * no-op (the command still succeeds). On by default; set `false` to keep the\r\n * commit manual (e.g. you prefer to review and commit framework changes\r\n * yourself). Conflicts (`<file>.new`) are never auto-committed — those still\r\n * wait for you to merge.\r\n */\r\n readonly commitFrameworkChanges: boolean;\r\n /**\r\n * Use the dedicated session hand-off store (`data/_handoff/`) — a forward-\r\n * looking baton kept separate from the worklog. On by default. When off,\r\n * hand-offs fall back to the legacy `## 다음 작업` worklog section and no\r\n * auto-archiving runs.\r\n */\r\n readonly handoff: boolean;\r\n /**\r\n * Days a hand-off stays \"active\" before session start sweeps it to\r\n * `_handoff/_archive/` (deterministic, age-based — git keeps the history).\r\n * Default 7 (covers a week-long absence); set lower to tidy faster, higher to\r\n * keep more around. A non-positive / malformed value falls back to the default.\r\n */\r\n readonly handoffRetentionDays: number;\r\n /**\r\n * Auto-reindex the on-demand memory index (`_memory/_INDEX.md`) at session\r\n * start when it looks stale (a memory file is newer than the index).\r\n * Mechanical and idempotent — it only rewrites `_INDEX.md` from each memory's\r\n * frontmatter, never a memory's content, and writes only when the rendered\r\n * index actually changes (then refreshes the index's mtime so the stale flag\r\n * clears). On by default; off → the start report just flags the stale index\r\n * for the agent to fix instead of fixing it automatically.\r\n */\r\n readonly reindex: boolean;\r\n /**\r\n * Auto-backfill a missed worklog. When a past day had commits but no worklog,\r\n * the in-session agent reconstructs and writes it at the next session start\r\n * (zero-worklog days only, capped, labeled as reconstructed — see AI-RULES.md\r\n * \"Default behaviors\"). Gated by `worklog` as well: turning `worklog` off\r\n * suppresses backfill too. On by default; off → the start report still flags\r\n * the gap but the agent leaves it for you.\r\n */\r\n readonly backfill: boolean;\r\n /**\r\n * The failure ledger (`data/_failures/`) — the self-improvement loop's\r\n * evidence store. With this on, the agent records noticed failures\r\n * (append-only, one file per occurrence) and the session-start report\r\n * surfaces recurring keys with their escalation-ladder stage (2nd occurrence\r\n * → the covering rule's gate is mandatory; 3rd+ → propose a deterministic\r\n * guard — always a proposal, never auto-installed). On by default; off → no\r\n * recording ritual and no report section ( `vortex failure` still works when\r\n * invoked explicitly).\r\n */\r\n readonly failures: boolean;\r\n}\r\n\r\n/**\r\n * One environment label plus the signal that selects it. Rules are evaluated\r\n * in order; the first match wins. Generalizes the operator's home/work\r\n * `Test-Path` pattern into a portable, declarative form.\r\n */\r\nexport interface EnvironmentRule {\r\n readonly label: string;\r\n /** Match when this absolute path exists on the machine. */\r\n readonly pathExists?: string;\r\n /** Match when the OS hostname equals this (case-insensitive). */\r\n readonly hostname?: string;\r\n /**\r\n * Match when an environment variable is set. A bare string matches on\r\n * presence; the object form additionally requires a specific value.\r\n */\r\n readonly envVar?: string | { readonly name: string; readonly equals?: string };\r\n}\r\n\r\n/**\r\n * Update-awareness behavior. `check` controls the once-per-session check for a\r\n * newer published `@vortex-os/base`:\r\n * - `\"session\"` (default): check the npm registry once at session start and,\r\n * when a newer version exists, surface it (the agent then asks before any\r\n * install — nothing is installed automatically). Offline → silent skip.\r\n * - `\"off\"`: no automatic check, no network contact for updates.\r\n * On either setting the local \"templates not yet applied\" notice (no network)\r\n * and the on-demand `vortex check-updates` still work.\r\n */\r\nexport interface UpdatesConfig {\r\n readonly check: \"session\" | \"off\";\r\n}\r\n\r\n/**\r\n * Git-sync behavior. `autoPush` is the ONE exception to the \"push stays\r\n * explicit\" default, and only when you opt in: after an archive auto-commit\r\n * (session start/end, `vortex catch-up`), fast-forward-pull then `git push`.\r\n * IMPORTANT semantics: git cannot push a single commit — a push sends EVERY\r\n * unpushed commit on the current branch, including your own manual ones. Off\r\n * by default; only an explicit `true` enables it (fail-closed: any other\r\n * value, typo included, keeps push manual).\r\n */\r\nexport interface SyncConfig {\r\n readonly autoPush: boolean;\r\n}\r\n\r\nexport interface VortexConfig {\r\n readonly autoRecord: AutoRecordConfig;\r\n readonly updates: UpdatesConfig;\r\n readonly sync: SyncConfig;\r\n readonly environments: readonly EnvironmentRule[];\r\n}\r\n\r\nconst DEFAULT_CONFIG: VortexConfig = {\r\n autoRecord: { sessionStart: true, worklog: true, decision: true, ambientRecall: true, archive: true, archiveCommit: true, archiveAtEnd: true, archiveRawRetentionDays: 30, vectorize: true, vectorizeAutoDownload: true, commitFrameworkChanges: true, handoff: true, handoffRetentionDays: 7, reindex: true, backfill: true, failures: true },\r\n updates: { check: \"session\" },\r\n sync: { autoPush: false },\r\n environments: [],\r\n};\r\n\r\n/** Path of the instance config file: `<agentDir>/vortex.json`. */\r\nexport function vortexConfigPath(ctx: ModuleContext): string {\r\n return join(ctx.agentDir, \"vortex.json\");\r\n}\r\n\r\n/**\r\n * Validate one raw environment rule, returning a clean `EnvironmentRule` or\r\n * `null` when it is unusable. A rule must be a plain object with a string\r\n * `label`; `hostname`/`pathExists` are accepted only as strings (dropped\r\n * otherwise) and `envVar` only as a string or an object with a string `name`\r\n * (with an optional string `equals`). Dropping malformed rules keeps a bad\r\n * config from throwing later in `resolveEnvironment`.\r\n */\r\nfunction parseEnvironmentRule(raw: unknown): EnvironmentRule | null {\r\n if (typeof raw !== \"object\" || raw === null || Array.isArray(raw)) return null;\r\n const candidate = raw as Record<string, unknown>;\r\n if (typeof candidate.label !== \"string\") return null;\r\n const rule: { -readonly [K in keyof EnvironmentRule]: EnvironmentRule[K] } = {\r\n label: candidate.label,\r\n };\r\n if (typeof candidate.pathExists === \"string\") rule.pathExists = candidate.pathExists;\r\n if (typeof candidate.hostname === \"string\") rule.hostname = candidate.hostname;\r\n if (typeof candidate.envVar === \"string\") {\r\n rule.envVar = candidate.envVar;\r\n } else if (typeof candidate.envVar === \"object\" && candidate.envVar !== null) {\r\n const ev = candidate.envVar as Record<string, unknown>;\r\n if (typeof ev.name === \"string\") {\r\n rule.envVar =\r\n typeof ev.equals === \"string\" ? { name: ev.name, equals: ev.equals } : { name: ev.name };\r\n }\r\n }\r\n return rule;\r\n}\r\n\r\n/**\r\n * Load the instance config, merged over defaults. A missing, unreadable, or\r\n * invalid file yields defaults (everything on, no environments) rather than\r\n * throwing — config is opt-in tuning, never a prerequisite. Environment rules\r\n * are validated individually; malformed ones are dropped so that a partly bad\r\n * config still resolves rather than throwing in `resolveEnvironment`.\r\n */\r\nexport function loadVortexConfig(ctx: ModuleContext): VortexConfig {\r\n const path = vortexConfigPath(ctx);\r\n if (!existsSync(path)) return DEFAULT_CONFIG;\r\n try {\r\n const raw = JSON.parse(readFileSync(path, \"utf8\")) as Partial<VortexConfig>;\r\n const environments = Array.isArray(raw.environments)\r\n ? raw.environments\r\n .map(parseEnvironmentRule)\r\n .filter((rule): rule is EnvironmentRule => rule !== null)\r\n : [];\r\n // Resolve `updates.check`, normalizing and FAILING CLOSED toward no network:\r\n // - missing / a malformed `updates` shape → the default (\"session\", on);\r\n // - an explicit \"session\" (any case/whitespace) stays on;\r\n // - ANY other explicit value (incl. \"off\", \"OFF\", \" off \", a typo, or a\r\n // non-string) DISABLES the check — the only reason to set a non-default\r\n // value is to turn it off, so a mistyped opt-out must not silently keep\r\n // phoning home.\r\n const rawUpdates =\r\n raw.updates && typeof raw.updates === \"object\" && !Array.isArray(raw.updates)\r\n ? (raw.updates as unknown as Record<string, unknown>)\r\n : {};\r\n const rawCheck = rawUpdates.check;\r\n const check: UpdatesConfig[\"check\"] =\r\n rawCheck === undefined\r\n ? \"session\"\r\n : typeof rawCheck === \"string\" && rawCheck.trim().toLowerCase() === \"session\"\r\n ? \"session\"\r\n : \"off\";\r\n // `vectorizeAutoDownload` fails CLOSED toward NOT downloading: absent → on\r\n // (the default opt-in); explicit `true` → on; ANY other explicit value\r\n // (false, \"off\", a typo, a non-boolean) → off, so a mistyped opt-out never\r\n // silently keeps the ~470 MB auto-download on.\r\n const rawAuto =\r\n raw.autoRecord && typeof raw.autoRecord === \"object\" && !Array.isArray(raw.autoRecord)\r\n ? (raw.autoRecord as unknown as Record<string, unknown>)\r\n : {};\r\n const vectorizeAutoDownload =\r\n rawAuto.vectorizeAutoDownload === undefined ? true : rawAuto.vectorizeAutoDownload === true;\r\n // `commitFrameworkChanges` fails CLOSED toward NOT committing: absent → on\r\n // (the default), explicit `true` → on, ANY other explicit value (false,\r\n // \"off\", a typo, a non-boolean) → off — so a mistyped opt-out never leaves\r\n // the framework silently committing into the user's repo.\r\n const commitFrameworkChanges =\r\n rawAuto.commitFrameworkChanges === undefined ? true : rawAuto.commitFrameworkChanges === true;\r\n // `handoff` fails CLOSED toward the legacy worklog-section behavior: absent →\r\n // on (the default), explicit `true` → on, ANY other explicit value → off.\r\n const handoff = rawAuto.handoff === undefined ? true : rawAuto.handoff === true;\r\n // `reindex` / `backfill` fail CLOSED toward NOT auto-writing: absent → on\r\n // (the default), explicit `true` → on, ANY other explicit value (false,\r\n // \"off\", a typo, a non-boolean) → off — so a mistyped opt-out never leaves\r\n // the framework auto-writing the memory index or a backfilled worklog.\r\n const reindex = rawAuto.reindex === undefined ? true : rawAuto.reindex === true;\r\n const backfill = rawAuto.backfill === undefined ? true : rawAuto.backfill === true;\r\n // `failures` (the failure ledger) fails CLOSED toward NOT auto-writing,\r\n // same shape as reindex/backfill: absent → on, explicit `true` → on, ANY\r\n // other explicit value → off.\r\n const failures = rawAuto.failures === undefined ? true : rawAuto.failures === true;\r\n // `archiveCommit` / `archiveAtEnd` fail CLOSED toward NOT auto-writing into\r\n // the user's git history / NOT running work at session end: absent → on\r\n // (the default), explicit `true` → on, ANY other explicit value → off.\r\n const archiveCommit =\r\n rawAuto.archiveCommit === undefined ? true : rawAuto.archiveCommit === true;\r\n const archiveAtEnd = rawAuto.archiveAtEnd === undefined ? true : rawAuto.archiveAtEnd === true;\r\n // `archiveRawRetentionDays`: a finite number ≥ 0 is honored (floored; 0 =\r\n // sweep off). Anything else falls back to the default — EXCEPT explicit\r\n // `false`, which reads as \"don't delete\" and maps to 0, because the risky\r\n // side of this option is deletion, so an opt-out spelled as a boolean must\r\n // not silently fall back to the deleting default.\r\n const rawRetention = rawAuto.archiveRawRetentionDays;\r\n const archiveRawRetentionDays =\r\n typeof rawRetention === \"number\" && Number.isFinite(rawRetention) && rawRetention >= 0\r\n ? Math.floor(rawRetention)\r\n : rawRetention === false\r\n ? 0\r\n : DEFAULT_CONFIG.autoRecord.archiveRawRetentionDays;\r\n // `sync.autoPush` fails CLOSED toward NOT pushing (the network-visible,\r\n // hardest-to-undo action here): only an explicit `true` enables it.\r\n const rawSync =\r\n raw.sync && typeof raw.sync === \"object\" && !Array.isArray(raw.sync)\r\n ? (raw.sync as unknown as Record<string, unknown>)\r\n : {};\r\n const autoPush = rawSync.autoPush === true;\r\n // `handoffRetentionDays`: a positive finite number is honored (floored);\r\n // anything else falls back to the default, so a typo never disables pruning\r\n // silently or sets a nonsensical window.\r\n const rawDays = rawAuto.handoffRetentionDays;\r\n const handoffRetentionDays =\r\n typeof rawDays === \"number\" && Number.isFinite(rawDays) && rawDays > 0\r\n ? Math.floor(rawDays)\r\n : DEFAULT_CONFIG.autoRecord.handoffRetentionDays;\r\n return {\r\n autoRecord: {\r\n ...DEFAULT_CONFIG.autoRecord,\r\n ...(raw.autoRecord ?? {}),\r\n vectorizeAutoDownload,\r\n commitFrameworkChanges,\r\n handoff,\r\n handoffRetentionDays,\r\n reindex,\r\n backfill,\r\n failures,\r\n archiveCommit,\r\n archiveAtEnd,\r\n archiveRawRetentionDays,\r\n },\r\n updates: { check },\r\n sync: { autoPush },\r\n environments,\r\n };\r\n } catch {\r\n return DEFAULT_CONFIG;\r\n }\r\n}\r\n\r\n/**\r\n * Resolve the active environment label from the config rules (first match\r\n * wins), or null when none match. Pure — the caller injects the signals\r\n * (hostname, env vars, a path-existence probe) so this stays testable and\r\n * free of direct OS access.\r\n */\r\nexport function resolveEnvironment(\r\n config: VortexConfig,\r\n signals: {\r\n readonly hostname?: string;\r\n readonly env?: Record<string, string | undefined>;\r\n readonly pathExists?: (p: string) => boolean;\r\n },\r\n): string | null {\r\n const host = signals.hostname?.toLowerCase();\r\n const env = signals.env ?? {};\r\n const pathExists = signals.pathExists ?? (() => false);\r\n for (const rule of config.environments) {\r\n if (rule.pathExists && pathExists(rule.pathExists)) return rule.label;\r\n if (rule.hostname && host && rule.hostname.toLowerCase() === host) return rule.label;\r\n if (rule.envVar) {\r\n if (typeof rule.envVar === \"string\") {\r\n if (env[rule.envVar] !== undefined) return rule.label;\r\n } else {\r\n const v = env[rule.envVar.name];\r\n if (v !== undefined && (rule.envVar.equals === undefined || v === rule.envVar.equals)) {\r\n return rule.label;\r\n }\r\n }\r\n }\r\n }\r\n return null;\r\n}\r\n","export { Privacy } from \"./types.js\";\r\nexport type { FrontmatterDoc, ModuleContext } from \"./types.js\";\r\nexport { parseFrontmatter, serializeFrontmatter } from \"./frontmatter.js\";\r\nexport { isVisibleAt, maxPrivacy, normalizePrivacy } from \"./privacy.js\";\r\nexport { makeContext, moduleDir } from \"./paths.js\";\r\nexport { loadVortexConfig, resolveEnvironment, vortexConfigPath } from \"./config.js\";\r\nexport type { AutoRecordConfig, EnvironmentRule, UpdatesConfig, VortexConfig } from \"./config.js\";\r\nexport { atomicWriteFile, exclusiveCreateFile, validateDataRelativePath } from \"./safe-fs.js\";\r\nexport type { ValidateDataRelativePathOptions } from \"./safe-fs.js\";\r\n","/**\n * Three-tier privacy classification used across VortEX.\n *\n * - `public` — safe to share with anyone, including in published repositories.\n * - `internal` — visible to the operator and their organization; not for public release.\n * - `personal` — personal data; never shared, never published.\n */\nexport const Privacy = {\n Public: \"public\",\n Internal: \"internal\",\n Personal: \"personal\",\n} as const;\n\nexport type Privacy = (typeof Privacy)[keyof typeof Privacy];\n\n/**\n * Parsed markdown document with separated YAML frontmatter and body.\n */\nexport interface FrontmatterDoc<T = Record<string, unknown>> {\n frontmatter: T & { privacy?: Privacy };\n body: string;\n}\n\n/**\n * Resolved paths for a VortEX repository root.\n *\n * Every module receives a `ModuleContext` from the host (CLI, plugin runtime,\n * or test harness) and resolves its own paths against it. Modules must not\n * derive paths by string manipulation against an assumed layout.\n */\nexport interface ModuleContext {\n repoRoot: string;\n agentDir: string;\n dataDir: string;\n modulesDir: string;\n pluginsDir: string;\n}\n","import type { Privacy } from \"./types.js\";\n\nconst ORDER: Record<Privacy, number> = {\n public: 0,\n internal: 1,\n personal: 2,\n};\n\n/**\n * Returns true if a document with `docPrivacy` is visible to a viewer authorized\n * at `viewerPrivacy`. Viewers see content at or below their own level.\n *\n * - `public` viewer sees → public only\n * - `internal` viewer sees → public + internal\n * - `personal` viewer sees → all three\n */\nexport function isVisibleAt(docPrivacy: Privacy, viewerPrivacy: Privacy): boolean {\n return ORDER[docPrivacy] <= ORDER[viewerPrivacy];\n}\n\n/**\n * Returns the more-restrictive of two privacy levels.\n * Useful when combining content from multiple sources for sharing.\n */\nexport function maxPrivacy(a: Privacy, b: Privacy): Privacy {\n return ORDER[a] >= ORDER[b] ? a : b;\n}\n\n/**\n * Coerce an unknown value (e.g. user input or untyped frontmatter) into a\n * valid `Privacy`. Falls back to `internal` by default — the safest default\n * for unclassified content.\n */\nexport function normalizePrivacy(value: unknown, fallback: Privacy = \"internal\"): Privacy {\n if (value === \"public\" || value === \"internal\" || value === \"personal\") {\n return value;\n }\n return fallback;\n}\n","import { rename, writeFile } from \"node:fs/promises\";\nimport { isAbsolute, relative, resolve, sep } from \"node:path\";\n\n/**\n * Path-safety primitives shared by every VortEX write path.\n *\n * Two concerns are deliberately separated:\n *\n * - {@link validateDataRelativePath} is the *gate* — it turns an\n * untrusted, possibly-LLM-chosen `data/`-relative path into a vetted\n * absolute path under `dataDir`, or throws. It must run before ANY write\n * so a traversal/absolute/drive-qualified/system-dir path can never reach\n * the filesystem.\n * - {@link exclusiveCreateFile} is the *write* — an exclusive create that\n * refuses to overwrite, so an accidental collision surfaces as an error\n * rather than silent data loss.\n *\n * The validator is the security boundary. The proactive-curator's placement\n * decisions come from an LLM; the CLI accept path takes a serialized payload\n * from an agent. Neither is trusted to stay inside `data/` on its own.\n */\n\n/**\n * Directories at the top of `data/` that VortEX maintains structurally and\n * that the curate value loop must never write into. Mirrors the\n * proactive-curator's own `SYSTEM_META_DIRS`, plus the leading-`_` rule\n * (any `_*` first segment is reserved) enforced separately below.\n */\nconst SYSTEM_META_DIRS = new Set([\n \"worklog\",\n \"decision-log\",\n \"runbooks\",\n \"hubs\",\n \"_memory\",\n \"_templates\",\n \"_proactive-curator\",\n // Framework metadata carve-out: `data/.vortex/` holds the update-lifecycle\n // ownership manifest and template backups. It is NOT user space — the curate\n // value loop (and any LLM-chosen write path) must never target it, even\n // though it does not start with `_`. (The leading-`_` rule below does not\n // cover a leading-dot dir, so it is listed explicitly.)\n \".vortex\",\n]);\n\n/** Match a drive-qualified prefix such as `C:` or `c:/...` (Windows). */\nconst DRIVE_QUALIFIED = /^[a-zA-Z]:/;\n\n/** Control characters (U+0000–U+001F) are never valid in a path we accept. */\n// eslint-disable-next-line no-control-regex\nconst CONTROL_CHARS = /[\\u0000-\\u001F]/;\n\n/**\n * Options for {@link validateDataRelativePath}. Shaped so a future\n * symlink-aware mode can be added WITHOUT changing any caller: today every\n * caller passes no options (or `{}`) and gets the lexical check. When the\n * realpath mode lands it becomes `{ resolveSymlinks: true }` and callers that\n * want it opt in; the default stays lexical-only.\n *\n * The symlink/realpath ancestor check is intentionally DEFERRED for v1 — the\n * lexical containment check (resolve + `path.relative`) already blocks\n * traversal, absolute, and drive-qualified paths. A symlink *inside* `data/`\n * pointing outside it is the remaining gap; closing it needs an async\n * `realpath` walk, which this synchronous API leaves room for.\n */\nexport interface ValidateDataRelativePathOptions {\n /**\n * Reserved for the deferred realpath/symlink-ancestor mode. Currently\n * ignored — accepted now so enabling it later is not a breaking change.\n */\n readonly resolveSymlinks?: boolean;\n}\n\n/**\n * Validate an untrusted `data/`-relative path and return the vetted absolute\n * path under `dataDir`, or throw a clear {@link Error}.\n *\n * Rejection rules (in order — the cheap lexical checks run before the\n * resolve so a hostile input never even reaches `path.resolve`):\n *\n * 1. Not a non-empty string.\n * 2. Contains a control character (U+0000–U+001F).\n * 3. After normalizing `\\` → `/`: empty, `.`, drive-qualified (`C:`),\n * absolute, or has a leading separator.\n * 4. Any segment is empty (collapsed `//`), `.`, or `..` (traversal).\n * 5. The final segment (the filename) is empty.\n * 6. The first segment is a system/meta dir (worklog, decision-log,\n * runbooks, hubs, _memory, _templates, _proactive-curator) or starts\n * with `_` (any reserved/meta dir).\n *\n * Then it resolves against `dataDir` and confirms containment using\n * `path.relative` (NOT a string prefix — `data` vs `data-evil` would fool a\n * prefix check). On Windows the relative check is done case-insensitively.\n *\n * @param dataDir Absolute path of the instance's `data/` directory.\n * @param rel Untrusted `data/`-relative path (LLM- or agent-supplied).\n * @returns The validated absolute path, guaranteed under `dataDir`.\n */\nexport function validateDataRelativePath(\n dataDir: string,\n rel: string,\n // Options are accepted for forward-compatibility (deferred symlink mode);\n // unused today. Underscore-prefixed so noUnusedParameters stays happy.\n _options: ValidateDataRelativePathOptions = {},\n): string {\n if (typeof rel !== \"string\" || rel.length === 0) {\n throw new Error(\"Invalid data-relative path: must be a non-empty string.\");\n }\n if (CONTROL_CHARS.test(rel)) {\n throw new Error(\"Invalid data-relative path: contains control characters.\");\n }\n\n const normalized = rel.replace(/\\\\/g, \"/\");\n\n if (normalized === \"\" || normalized === \".\") {\n throw new Error(`Invalid data-relative path: \"${rel}\" is empty or '.'.`);\n }\n if (DRIVE_QUALIFIED.test(normalized)) {\n throw new Error(`Invalid data-relative path: \"${rel}\" is drive-qualified (must be data-relative).`);\n }\n if (isAbsolute(normalized) || normalized.startsWith(\"/\")) {\n throw new Error(`Invalid data-relative path: \"${rel}\" is absolute (must be data-relative).`);\n }\n\n const segments = normalized.split(\"/\");\n for (const segment of segments) {\n if (segment === \"\" || segment === \".\" || segment === \"..\") {\n throw new Error(\n `Invalid data-relative path: \"${rel}\" contains an empty, '.', or '..' segment (path traversal).`,\n );\n }\n }\n\n // The final segment is the filename; it must not itself be a separator-\n // bearing value (defensive — split already removed separators) and must\n // be non-empty (covered by the segment loop, asserted here for clarity).\n const filename = segments[segments.length - 1]!;\n if (filename.length === 0 || filename.includes(\"/\") || filename.includes(\"\\\\\")) {\n throw new Error(`Invalid data-relative path: \"${rel}\" has an invalid filename.`);\n }\n\n const first = segments[0]!;\n if (SYSTEM_META_DIRS.has(first) || first.startsWith(\"_\")) {\n throw new Error(\n `Invalid data-relative path: \"${rel}\" targets a reserved system/meta directory (\"${first}\"). ` +\n \"The curate value loop writes user documents only — not worklog/decision-log/runbooks/hubs or any _* directory.\",\n );\n }\n\n const absPath = resolve(dataDir, normalized);\n const rootResolved = resolve(dataDir);\n\n // Containment via path.relative — a true ancestor relationship, not a\n // string prefix (which would accept a sibling like `<dataDir>-evil`).\n let relToData = relative(rootResolved, absPath);\n let relCompare = relToData;\n let upPrefix = \"..\" + sep;\n if (sep === \"\\\\\") {\n // Windows: filesystem is case-insensitive — compare case-folded so a\n // drive-letter/segment casing difference does not read as \"escaped\".\n relCompare = relToData.toLowerCase();\n upPrefix = upPrefix.toLowerCase();\n }\n if (\n relCompare === \"..\" ||\n relCompare.startsWith(upPrefix) ||\n isAbsolute(relToData)\n ) {\n throw new Error(`Invalid data-relative path: \"${rel}\" resolves outside the data directory.`);\n }\n\n return absPath;\n}\n\n/**\n * Create a file exclusively (refusing to overwrite). Writes `body` with the\n * `wx` flag so an existing target throws `EEXIST`, which is re-thrown as a\n * clear \"refusing to overwrite\" error. Use for the create-file action where\n * clobbering an existing document would be silent data loss.\n *\n * The caller is responsible for ensuring the parent directory exists (the\n * create-file path in the curator does its own `mkdir` after validation).\n *\n * @param absPath Absolute target path (already validated by\n * {@link validateDataRelativePath}).\n * @param body File contents (UTF-8).\n */\nexport async function exclusiveCreateFile(absPath: string, body: string): Promise<void> {\n try {\n await writeFile(absPath, body, { encoding: \"utf8\", flag: \"wx\" });\n } catch (e) {\n if ((e as NodeJS.ErrnoException).code === \"EEXIST\") {\n throw new Error(\n `Refusing to overwrite existing file: ${absPath}. ` +\n \"create-file is an exclusive create; to add to an existing document use append-section instead.\",\n );\n }\n throw e;\n }\n}\n\n/**\n * Write `body` to `absPath` atomically: stage to a sibling temp file, then\n * `rename` over the target. A reader of `absPath` therefore sees either the old\n * bytes or the new bytes in full — never a partially-written file. The temp\n * file is a sibling (same directory ⇒ same filesystem) so the rename is atomic;\n * `counter` disambiguates concurrent writers in the same process.\n *\n * This is the write primitive for the update lifecycle's transaction model —\n * template replacements and the ownership manifest must never leave a half-\n * written file on a crash mid-update. The caller ensures the parent directory\n * exists.\n *\n * @param absPath Absolute target path.\n * @param body File contents (UTF-8).\n */\nlet atomicWriteCounter = 0;\nexport async function atomicWriteFile(absPath: string, body: string): Promise<void> {\n const tmp = `${absPath}.tmp-${process.pid}-${atomicWriteCounter++}`;\n try {\n await writeFile(tmp, body, { encoding: \"utf8\" });\n await rename(tmp, absPath);\n } catch (e) {\n // Best-effort cleanup of the temp file if the rename never happened.\n try {\n const { rm } = await import(\"node:fs/promises\");\n await rm(tmp, { force: true });\n } catch {\n // ignore — the temp file is named distinctly and harmless if it lingers\n }\n throw e;\n }\n}\n"],"mappings":"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","names":["join","resolve"]}
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  loadVortexConfig,
3
3
  makeContext
4
- } from "./chunk-T53UWSTR.js";
4
+ } from "./chunk-IZIAMK3L.js";
5
5
 
6
6
  // ../plugins/session-rituals/dist/guard.js
7
7
  import { existsSync, readFileSync } from "fs";
@@ -125,7 +125,7 @@ async function runGuardCli(argv, out, err) {
125
125
  if (loadVortexConfig(ctx).autoRecord.failures) {
126
126
  const filePath = toolInput.file_path;
127
127
  const fileNote = typeof filePath === "string" && filePath ? ` (target: ${filePath})` : "";
128
- const { recordGuardDenial } = await import("./failures-PMURLMVB.js");
128
+ const { recordGuardDenial } = await import("./failures-DBXJKFYX.js");
129
129
  await recordGuardDenial(ctx.dataDir, `write guard denied a literal control character in ${finding.field}${fileNote}`);
130
130
  }
131
131
  }
@@ -163,4 +163,4 @@ export {
163
163
  runGuardCli,
164
164
  resolveInstanceRoot
165
165
  };
166
- //# sourceMappingURL=chunk-2FVNWW77.js.map
166
+ //# sourceMappingURL=chunk-JKQAB5OK.js.map
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  GUARD_WRITE_COMMAND,
3
3
  GUARD_WRITE_MATCHER
4
- } from "./chunk-2FVNWW77.js";
4
+ } from "./chunk-JKQAB5OK.js";
5
5
 
6
6
  // ../plugins/session-rituals/dist/statusline.js
7
7
  import { execFileSync } from "child_process";
@@ -498,4 +498,4 @@ export {
498
498
  ensureStatusline,
499
499
  runStatuslineCli
500
500
  };
501
- //# sourceMappingURL=chunk-P7IMUUNY.js.map
501
+ //# sourceMappingURL=chunk-OFID33QA.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  parseFrontmatter
3
- } from "./chunk-T53UWSTR.js";
3
+ } from "./chunk-IZIAMK3L.js";
4
4
 
5
5
  // ../plugins/session-rituals/dist/failures.js
6
6
  import { mkdir, readdir, readFile, stat, writeFile } from "fs/promises";
@@ -289,4 +289,4 @@ export {
289
289
  recordGuardDenial,
290
290
  runFailureCli
291
291
  };
292
- //# sourceMappingURL=chunk-UV76ZEDC.js.map
292
+ //# sourceMappingURL=chunk-T4TJZUPP.js.map
@@ -8,8 +8,8 @@ import {
8
8
  recordGuardDenial,
9
9
  runFailureCli,
10
10
  scanFailures
11
- } from "./chunk-UV76ZEDC.js";
12
- import "./chunk-T53UWSTR.js";
11
+ } from "./chunk-T4TJZUPP.js";
12
+ import "./chunk-IZIAMK3L.js";
13
13
  import "./chunk-PZ5AY32C.js";
14
14
  export {
15
15
  FAILURES_DIR,
@@ -22,4 +22,4 @@ export {
22
22
  runFailureCli,
23
23
  scanFailures
24
24
  };
25
- //# sourceMappingURL=failures-PMURLMVB.js.map
25
+ //# sourceMappingURL=failures-DBXJKFYX.js.map
@@ -7,8 +7,8 @@ import {
7
7
  resolveInstanceRoot,
8
8
  runGuardCli,
9
9
  scanToolInput
10
- } from "./chunk-2FVNWW77.js";
11
- import "./chunk-T53UWSTR.js";
10
+ } from "./chunk-JKQAB5OK.js";
11
+ import "./chunk-IZIAMK3L.js";
12
12
  import "./chunk-PZ5AY32C.js";
13
13
  export {
14
14
  GUARD_WRITE_COMMAND,
@@ -20,4 +20,4 @@ export {
20
20
  runGuardCli,
21
21
  scanToolInput
22
22
  };
23
- //# sourceMappingURL=guard-IMJR6ET7.js.map
23
+ //# sourceMappingURL=guard-S6NGMDXR.js.map
package/dist/index.d.ts CHANGED
@@ -108,6 +108,37 @@ interface AutoRecordConfig {
108
108
  * rebuilt.
109
109
  */
110
110
  readonly archive: boolean;
111
+ /**
112
+ * Auto-commit the conversation archive. After catch-up writes new transcripts
113
+ * into `data/_session-archive/`, commit exactly that pathspec — and nothing
114
+ * else in the working tree — so archived conversations never linger as the
115
+ * "N uncommitted change(s)" noise in the next session-start report. Runs at
116
+ * session start, session end, and `vortex catch-up`; gated additionally on a
117
+ * healthy repo (no in-progress merge/rebase/lock, not a detached HEAD).
118
+ * Best-effort: any git failure is a silent skip. On by default; off → the
119
+ * archive still grows but you commit it yourself.
120
+ */
121
+ readonly archiveCommit: boolean;
122
+ /**
123
+ * Run catch-up (ingest + the auto-commit above) at session END too, not only
124
+ * at start. The ending session itself is usually still skipped (its
125
+ * transcript was just written, so the live-session guard catches it; if it
126
+ * is ingested after a long idle pause, later transcript writes advance the
127
+ * source mtime and the next catch-up re-ingests — the archive self-heals),
128
+ * but every OTHER session closed earlier lands in the archive the moment you
129
+ * wrap up, instead of waiting for the next session start. Crash-safe: if the
130
+ * end hook never fires, the next start catches up as before. On by default.
131
+ */
132
+ readonly archiveAtEnd: boolean;
133
+ /**
134
+ * Days to keep the RAW transcript copies (`_session-archive/raw/`) before the
135
+ * sweep deletes them from the local working tree. Raw files are byte-for-byte
136
+ * duplicates kept for debugging/fidelity; recall, reindex, and cross-machine
137
+ * sync all use only the NORMALIZED files (which are kept forever), so this
138
+ * deletion loses nothing. Default 30; `0` disables the sweep entirely. A
139
+ * malformed value falls back to the default.
140
+ */
141
+ readonly archiveRawRetentionDays: number;
111
142
  /**
112
143
  * Auto-vectorize: at session start (after catch-up), embed any
113
144
  * newly-archived sessions and memories so semantic recall — including the
@@ -224,9 +255,22 @@ interface EnvironmentRule {
224
255
  interface UpdatesConfig {
225
256
  readonly check: "session" | "off";
226
257
  }
258
+ /**
259
+ * Git-sync behavior. `autoPush` is the ONE exception to the "push stays
260
+ * explicit" default, and only when you opt in: after an archive auto-commit
261
+ * (session start/end, `vortex catch-up`), fast-forward-pull then `git push`.
262
+ * IMPORTANT semantics: git cannot push a single commit — a push sends EVERY
263
+ * unpushed commit on the current branch, including your own manual ones. Off
264
+ * by default; only an explicit `true` enables it (fail-closed: any other
265
+ * value, typo included, keeps push manual).
266
+ */
267
+ interface SyncConfig {
268
+ readonly autoPush: boolean;
269
+ }
227
270
  interface VortexConfig {
228
271
  readonly autoRecord: AutoRecordConfig;
229
272
  readonly updates: UpdatesConfig;
273
+ readonly sync: SyncConfig;
230
274
  readonly environments: readonly EnvironmentRule[];
231
275
  }
232
276
  /** Path of the instance config file: `<agentDir>/vortex.json`. */
@@ -2649,6 +2693,121 @@ interface RitualRegistryOptions {
2649
2693
  */
2650
2694
  declare function createRitualRegistry(options?: RitualRegistryOptions): CommandRegistry;
2651
2695
 
2696
+ /**
2697
+ * Start-of-session "catch-up": fold conversation transcripts into the local
2698
+ * search archive without the user ever having to wrap up a session.
2699
+ *
2700
+ * Two sources, one pass:
2701
+ * - **local (a)** — this machine's own transcripts that are not archived yet,
2702
+ * read from every detected agent host's transcript store (Claude Code,
2703
+ * Codex, Gemini) and scoped to the current project. Because all hosts are
2704
+ * swept on every start, a single Claude Code session-start also folds in the
2705
+ * Codex/Gemini sessions you ran in the same project, into one archive.
2706
+ * - **pulled (b)** — transcripts created on another machine that arrived as
2707
+ * normalized text via git sync. Their text is present but this machine's
2708
+ * DB (local, derived, gitignored) has never indexed them.
2709
+ *
2710
+ * Text only — vectorization is deferred to recall/rebuild so session start
2711
+ * stays fast. The whole step is gated by `autoRecord.archive` at the call site
2712
+ * and is best-effort: callers should treat a thrown archive backend (e.g. the
2713
+ * native sqlite module not built) as "skip", never as a fatal start error.
2714
+ */
2715
+ interface CatchUpResult {
2716
+ /** Local transcripts newly archived this run (source a). */
2717
+ readonly ingestedLocal: number;
2718
+ /** Normalized transcripts from another machine newly indexed (source b). */
2719
+ readonly indexedPulled: number;
2720
+ /** Per-session ingest errors (source a). */
2721
+ readonly errors: number;
2722
+ }
2723
+ interface CatchUpOptions {
2724
+ /** Restrict local ingest to one project's transcripts. Default: `ctx.repoRoot`. */
2725
+ readonly cwd?: string;
2726
+ /**
2727
+ * Transcript adapters for local ingest. Default: all CLI hosts — Claude Code,
2728
+ * Codex, and Gemini. Each adapter's `detect()` returns false when its host is
2729
+ * absent, so registering all three is ~free on a machine that only uses one.
2730
+ * (Claude Desktop is opt-in — it needs the `classic-level` dependency — so it
2731
+ * is not in the default set; a caller can add it.) Tests inject fakes (or a
2732
+ * sandbox `env.home`) so the scan never touches the real home directory.
2733
+ */
2734
+ readonly adapters?: sessionArchive.IngestParams["adapters"];
2735
+ /** Adapter environment override (e.g. a sandbox HOME). Tests use this. */
2736
+ readonly env?: sessionArchive.IngestParams["env"];
2737
+ }
2738
+ declare function catchUpSessions(ctx: ModuleContext, opts?: CatchUpOptions): Promise<CatchUpResult>;
2739
+
2740
+ /**
2741
+ * Run a git command, returning its stdout; git's own stderr is suppressed so a
2742
+ * plain non-git folder never leaks `fatal: not a git repository` into hook
2743
+ * output. Callers treat a non-zero exit (throw) as "no git" / "step skipped".
2744
+ */
2745
+ declare function gitOut(cwd: string, gitArgs: readonly string[]): string;
2746
+ /**
2747
+ * Detect an interrupted git operation — a near-certain crashed-session signal:
2748
+ * a merge / rebase / cherry-pick / revert / bisect left mid-flight, or a stray
2749
+ * index lock. Uses `git rev-parse --git-path` so the marker resolves correctly
2750
+ * even when `.git` is a FILE (a linked worktree or submodule), not a directory.
2751
+ * Returns the FIRST matching marker name, or null when the tree is clean / not
2752
+ * a git repo. Exported for tests. (Tier-1 carryover; decision-log 2026-06-04.)
2753
+ */
2754
+ declare function detectInterruptedGitOp(repoRoot: string): string | null;
2755
+ /** Why the auto-commit step did not commit (when `committed` is false). */
2756
+ type ArchiveCommitSkip = "off" | "git-busy" | "detached-head" | "nothing-to-commit" | "not-a-git-repo" | "git-error";
2757
+ /** Why the opt-in push did not push (when `pushed` is false). */
2758
+ type ArchivePushSkip = "off" | "no-commit" | "no-remote" | "no-upstream" | "diverged" | "git-error";
2759
+ interface ArchiveSyncResult {
2760
+ /** False when the run was skipped wholesale (lock held by a concurrent run). */
2761
+ readonly ran: boolean;
2762
+ readonly lockSkipped: boolean;
2763
+ /** Ingest outcome; null when the add-on is absent or the ingest failed. */
2764
+ readonly catchUp: CatchUpResult | null;
2765
+ /** Raw transcript copies deleted by the retention sweep this run. */
2766
+ readonly rawPruned: number;
2767
+ /** The retention window the sweep used (0 = sweep disabled). */
2768
+ readonly retentionDays: number;
2769
+ readonly committed: boolean;
2770
+ readonly commitSkipped?: ArchiveCommitSkip;
2771
+ readonly pushed: boolean;
2772
+ readonly pushSkipped?: ArchivePushSkip;
2773
+ }
2774
+ /** Repo-relative POSIX path of `data/_session-archive` (for pathspecs / report). */
2775
+ declare function archiveRelPath(ctx: ModuleContext): string;
2776
+ /**
2777
+ * Delete raw transcript copies older than `retentionDays` from
2778
+ * `<dataDir>/_session-archive/raw/`. Files only — directories stay, and the
2779
+ * normalized tree is never touched. Returns the number of files removed.
2780
+ * Exported for tests.
2781
+ */
2782
+ declare function sweepRawArchive(dataDir: string, retentionDays: number, nowMs?: number): number;
2783
+ /**
2784
+ * Clean up atomic-write temp files (`*.tmp-<pid>`). Two distinct sweeps:
2785
+ *
2786
+ * - **Committed subtrees (`raw/`, `normalized/`) — delete on sight, no age
2787
+ * gate.** The archive store stages its temps in `.tmp/` (outside these
2788
+ * subtrees) precisely so that NOTHING legitimate ever writes a temp here; a
2789
+ * match can only be a stray, and removing it before `git add` guarantees a
2790
+ * temp can never be committed under any crash timing.
2791
+ * - **The `.tmp/` staging dir — age-gated housekeeping.** A crash between
2792
+ * write and rename strands the temp here, which is harmless (never
2793
+ * committed); anything older than the lock TTL is clearly abandoned and is
2794
+ * removed to keep the dir from accumulating.
2795
+ *
2796
+ * Exported for tests.
2797
+ */
2798
+ declare function sweepOrphanTempFiles(dataDir: string, nowMs?: number): number;
2799
+ /**
2800
+ * Run the archive sync (ingest → sweep → commit → optional push) under the
2801
+ * repo-level lock. `phase` only affects which config gates the CALLER applies
2802
+ * (start/end honor `autoRecord.archive` / `archiveAtEnd`; "manual" = the
2803
+ * explicit `vortex catch-up`, which always ingests) — the steps themselves
2804
+ * behave identically.
2805
+ */
2806
+ declare function runArchiveSync(ctx: ModuleContext, config: VortexConfig, _phase: "start" | "end" | "manual", opts?: {
2807
+ /** Catch-up overrides — tests inject a sandbox home/fake adapters here. */
2808
+ readonly catchUp?: CatchUpOptions;
2809
+ }): Promise<ArchiveSyncResult>;
2810
+
2652
2811
  interface CliIo {
2653
2812
  /** Write a chunk to stdout. Default: `process.stdout.write`. */
2654
2813
  readonly stdout?: (s: string) => void;
@@ -2698,15 +2857,6 @@ declare function argvToSlash(argv: readonly string[]): string;
2698
2857
  * `process.exit`, so callers/tests stay in control.
2699
2858
  */
2700
2859
  declare function runVortexCli(argv: readonly string[], io?: CliIo): Promise<number>;
2701
- /**
2702
- * Detect an interrupted git operation — a near-certain crashed-session signal:
2703
- * a merge / rebase / cherry-pick / revert / bisect left mid-flight, or a stray
2704
- * index lock. Uses `git rev-parse --git-path` so the marker resolves correctly
2705
- * even when `.git` is a FILE (a linked worktree or submodule), not a directory.
2706
- * Returns the FIRST matching marker name, or null when the tree is clean / not a
2707
- * git repo. Exported for tests. (Tier-1 carryover; decision-log 2026-06-04.)
2708
- */
2709
- declare function detectInterruptedGitOp(repoRoot: string): string | null;
2710
2860
  /**
2711
2861
  * Assemble the Tier-1 carryover signal for the session-start report. The
2712
2862
  * interrupted-op check and the uncommitted count are computed INDEPENDENTLY: a
@@ -3304,6 +3454,20 @@ declare function renderSessionStartReport(report: SessionStartHookReport, extras
3304
3454
  readonly indexedPulled: number;
3305
3455
  readonly errors: number;
3306
3456
  };
3457
+ /**
3458
+ * Archive-sync outcome beyond the ingest itself: the pathspec auto-commit
3459
+ * of `data/_session-archive/` (so archived conversations never count as
3460
+ * carried-over noise), the raw-copy retention sweep, and the opt-in push.
3461
+ * `pushSkipped` is surfaced ONLY when auto-push is enabled but could not
3462
+ * push (a quiet heads-up; "off" — the default — renders nothing).
3463
+ */
3464
+ readonly archiveSync?: {
3465
+ readonly committed: boolean;
3466
+ readonly pushed: boolean;
3467
+ readonly pushSkipped?: string;
3468
+ readonly rawPruned: number;
3469
+ readonly retentionDays: number;
3470
+ };
3307
3471
  readonly vectorized?: {
3308
3472
  readonly memories: number;
3309
3473
  readonly sessionChunks: number;
@@ -3400,50 +3564,6 @@ declare function ensureWorklogEntry(ctx: ModuleContext, opts?: {
3400
3564
  readonly body?: string;
3401
3565
  }): Promise<EnsureWorklogResult>;
3402
3566
 
3403
- /**
3404
- * Start-of-session "catch-up": fold conversation transcripts into the local
3405
- * search archive without the user ever having to wrap up a session.
3406
- *
3407
- * Two sources, one pass:
3408
- * - **local (a)** — this machine's own transcripts that are not archived yet,
3409
- * read from every detected agent host's transcript store (Claude Code,
3410
- * Codex, Gemini) and scoped to the current project. Because all hosts are
3411
- * swept on every start, a single Claude Code session-start also folds in the
3412
- * Codex/Gemini sessions you ran in the same project, into one archive.
3413
- * - **pulled (b)** — transcripts created on another machine that arrived as
3414
- * normalized text via git sync. Their text is present but this machine's
3415
- * DB (local, derived, gitignored) has never indexed them.
3416
- *
3417
- * Text only — vectorization is deferred to recall/rebuild so session start
3418
- * stays fast. The whole step is gated by `autoRecord.archive` at the call site
3419
- * and is best-effort: callers should treat a thrown archive backend (e.g. the
3420
- * native sqlite module not built) as "skip", never as a fatal start error.
3421
- */
3422
- interface CatchUpResult {
3423
- /** Local transcripts newly archived this run (source a). */
3424
- readonly ingestedLocal: number;
3425
- /** Normalized transcripts from another machine newly indexed (source b). */
3426
- readonly indexedPulled: number;
3427
- /** Per-session ingest errors (source a). */
3428
- readonly errors: number;
3429
- }
3430
- interface CatchUpOptions {
3431
- /** Restrict local ingest to one project's transcripts. Default: `ctx.repoRoot`. */
3432
- readonly cwd?: string;
3433
- /**
3434
- * Transcript adapters for local ingest. Default: all CLI hosts — Claude Code,
3435
- * Codex, and Gemini. Each adapter's `detect()` returns false when its host is
3436
- * absent, so registering all three is ~free on a machine that only uses one.
3437
- * (Claude Desktop is opt-in — it needs the `classic-level` dependency — so it
3438
- * is not in the default set; a caller can add it.) Tests inject fakes (or a
3439
- * sandbox `env.home`) so the scan never touches the real home directory.
3440
- */
3441
- readonly adapters?: sessionArchive.IngestParams["adapters"];
3442
- /** Adapter environment override (e.g. a sandbox HOME). Tests use this. */
3443
- readonly env?: sessionArchive.IngestParams["env"];
3444
- }
3445
- declare function catchUpSessions(ctx: ModuleContext, opts?: CatchUpOptions): Promise<CatchUpResult>;
3446
-
3447
3567
  /**
3448
3568
  * `vortex statusline` — a Claude Code statusLine renderer.
3449
3569
  *
@@ -4384,6 +4504,9 @@ declare function checkBaseUpdate(ctx: ModuleContext): UpdateCheckResult;
4384
4504
 
4385
4505
  type index_d_AgendaReport = AgendaReport;
4386
4506
  type index_d_AmbientRecallFactoryOptions = AmbientRecallFactoryOptions;
4507
+ type index_d_ArchiveCommitSkip = ArchiveCommitSkip;
4508
+ type index_d_ArchivePushSkip = ArchivePushSkip;
4509
+ type index_d_ArchiveSyncResult = ArchiveSyncResult;
4387
4510
  type index_d_CatchUpOptions = CatchUpOptions;
4388
4511
  type index_d_CatchUpResult = CatchUpResult;
4389
4512
  type index_d_ClaudeSettings = ClaudeSettings;
@@ -4455,6 +4578,7 @@ type index_d_WorklogAppendResult = WorklogAppendResult;
4455
4578
  declare const index_d_agendaCommand: typeof agendaCommand;
4456
4579
  declare const index_d_aggregateHandoff: typeof aggregateHandoff;
4457
4580
  declare const index_d_applyGlobalSetup: typeof applyGlobalSetup;
4581
+ declare const index_d_archiveRelPath: typeof archiveRelPath;
4458
4582
  declare const index_d_argvToSlash: typeof argvToSlash;
4459
4583
  declare const index_d_autoReindexMemory: typeof autoReindexMemory;
4460
4584
  declare const index_d_buildDenyDecision: typeof buildDenyDecision;
@@ -4488,6 +4612,7 @@ declare const index_d_findControlChar: typeof findControlChar;
4488
4612
  declare const index_d_formatTokens: typeof formatTokens;
4489
4613
  declare const index_d_formatWindow: typeof formatWindow;
4490
4614
  declare const index_d_gapWindowSinceArg: typeof gapWindowSinceArg;
4615
+ declare const index_d_gitOut: typeof gitOut;
4491
4616
  declare const index_d_globalMemoryPath: typeof globalMemoryPath;
4492
4617
  declare const index_d_globalSettingsHasHook: typeof globalSettingsHasHook;
4493
4618
  declare const index_d_globalSettingsPath: typeof globalSettingsPath;
@@ -4523,6 +4648,7 @@ declare const index_d_renderStatusline: typeof renderStatusline;
4523
4648
  declare const index_d_repairOwnershipManifest: typeof repairOwnershipManifest;
4524
4649
  declare const index_d_resolveInstanceRoot: typeof resolveInstanceRoot;
4525
4650
  declare const index_d_resolveRepoRoot: typeof resolveRepoRoot;
4651
+ declare const index_d_runArchiveSync: typeof runArchiveSync;
4526
4652
  declare const index_d_runCurateAccept: typeof runCurateAccept;
4527
4653
  declare const index_d_runCurateCandidates: typeof runCurateCandidates;
4528
4654
  declare const index_d_runCurateDecline: typeof runCurateDecline;
@@ -4540,13 +4666,15 @@ declare const index_d_serializeSettings: typeof serializeSettings;
4540
4666
  declare const index_d_sessionStartCommand: typeof sessionStartCommand;
4541
4667
  declare const index_d_sniffEffortFromTranscript: typeof sniffEffortFromTranscript;
4542
4668
  declare const index_d_statuslineCommand: typeof statuslineCommand;
4669
+ declare const index_d_sweepOrphanTempFiles: typeof sweepOrphanTempFiles;
4670
+ declare const index_d_sweepRawArchive: typeof sweepRawArchive;
4543
4671
  declare const index_d_templateDestRelPath: typeof templateDestRelPath;
4544
4672
  declare const index_d_upsertGlobalBlock: typeof upsertGlobalBlock;
4545
4673
  declare const index_d_validateCuratePayload: typeof validateCuratePayload;
4546
4674
  declare const index_d_vortexCommand: typeof vortexCommand;
4547
4675
  declare const index_d_writeOwnershipManifest: typeof writeOwnershipManifest;
4548
4676
  declare namespace index_d {
4549
- export { type index_d_AgendaReport as AgendaReport, type index_d_AmbientRecallFactoryOptions as AmbientRecallFactoryOptions, type index_d_CatchUpOptions as CatchUpOptions, type index_d_CatchUpResult as CatchUpResult, type index_d_ClaudeSettings as ClaudeSettings, type index_d_CliIo as CliIo, type index_d_CollectAgendaOptions as CollectAgendaOptions, type index_d_CurateAcceptResult as CurateAcceptResult, type index_d_CurateActionKind as CurateActionKind, type index_d_CurateAnyProposal as CurateAnyProposal, type index_d_CurateCandidate as CurateCandidate, type index_d_CurateCandidatesResult as CurateCandidatesResult, type index_d_CurateDeclineResult as CurateDeclineResult, type index_d_CurateOptions as CurateOptions, type index_d_CuratePayload as CuratePayload, type index_d_CuratePayloadValidation as CuratePayloadValidation, type index_d_CuratePreviewResult as CuratePreviewResult, type index_d_CurateResult as CurateResult, index_d_DEFAULT_GAP_WINDOW_DAYS as DEFAULT_GAP_WINDOW_DAYS, type index_d_EnsureHooksResult as EnsureHooksResult, type index_d_EnsureWorklogResult as EnsureWorklogResult, index_d_FAILURES_DIR as FAILURES_DIR, type index_d_FailureEntry as FailureEntry, type index_d_FailureGroup as FailureGroup, type index_d_FailureReportSlice as FailureReportSlice, type index_d_FailureScan as FailureScan, index_d_GUARD_DENIAL_KEY as GUARD_DENIAL_KEY, index_d_GUARD_WRITE_COMMAND as GUARD_WRITE_COMMAND, index_d_GUARD_WRITE_MATCHER as GUARD_WRITE_MATCHER, type index_d_GitPullResult as GitPullResult, type index_d_GuardFinding as GuardFinding, index_d_HANDOFF_ARCHIVE_DIR as HANDOFF_ARCHIVE_DIR, index_d_HANDOFF_DIR as HANDOFF_DIR, type index_d_HandoffCreateResult as HandoffCreateResult, type index_d_HandoffSummary as HandoffSummary, type index_d_HandoffWriteResult as HandoffWriteResult, type index_d_LadderStage as LadderStage, type index_d_NewDecisionResult as NewDecisionResult, index_d_OWNERSHIP_SCHEMA as OWNERSHIP_SCHEMA, type index_d_OpenDecision as OpenDecision, type index_d_OpenTask as OpenTask, type index_d_OwnershipDiagnosis as OwnershipDiagnosis, type index_d_OwnershipEntry as OwnershipEntry, type index_d_OwnershipManifest as OwnershipManifest, type index_d_RecallOptions as RecallOptions, type index_d_RecentWorklog as RecentWorklog, type index_d_RecordFailureInput as RecordFailureInput, type index_d_RecordFailureResult as RecordFailureResult, type index_d_ReindexResult as ReindexResult, type index_d_RitualRegistryOptions as RitualRegistryOptions, index_d_SESSION_END_COMMAND as SESSION_END_COMMAND, index_d_SESSION_START_COMMAND as SESSION_START_COMMAND, type index_d_SessionStartHookReport as SessionStartHookReport, type index_d_SessionStartReport as SessionStartReport, type index_d_StatuslineData as StatuslineData, type index_d_StatuslineInstallResult as StatuslineInstallResult, type index_d_StatuslineProbes as StatuslineProbes, type index_d_UpdateCheckResult as UpdateCheckResult, type index_d_UpdateFileAction as UpdateFileAction, type index_d_UpdateFileActionKind as UpdateFileActionKind, type index_d_VortexHelpResult as VortexHelpResult, type index_d_VortexInitResult as VortexInitResult, type index_d_VortexPlannedResult as VortexPlannedResult, type index_d_VortexResult as VortexResult, type index_d_VortexSyncResult as VortexSyncResult, type index_d_VortexSyncStep as VortexSyncStep, type index_d_VortexSyncStepId as VortexSyncStepId, type index_d_VortexSyncStepStatus as VortexSyncStepStatus, type index_d_VortexUpdateResult as VortexUpdateResult, type index_d_WorklogAppendResult as WorklogAppendResult, index_d_agendaCommand as agendaCommand, index_d_aggregateHandoff as aggregateHandoff, index_d_applyGlobalSetup as applyGlobalSetup, index_d_argvToSlash as argvToSlash, index_d_autoReindexMemory as autoReindexMemory, index_d_buildDenyDecision as buildDenyDecision, index_d_buildInstallCommand as buildInstallCommand, index_d_buildOwnershipManifest as buildOwnershipManifest, index_d_buildRegistry as buildRegistry, index_d_catchUpSessions as catchUpSessions, index_d_checkBaseUpdate as checkBaseUpdate, index_d_collectAgenda as collectAgenda, index_d_collectCarryover as collectCarryover, index_d_collectSessionStartReport as collectSessionStartReport, index_d_collectStatuslineProbes as collectStatuslineProbes, index_d_compareSemver as compareSemver, index_d_computeCurateFingerprint as computeCurateFingerprint, index_d_countUncommitted as countUncommitted, index_d_createAmbientRecaller as createAmbientRecaller, index_d_createHandoffSkeleton as createHandoffSkeleton, index_d_createRitualRegistry as createRitualRegistry, index_d_curateCommand as curateCommand, index_d_decisionCommand as decisionCommand, index_d_detectInterruptedGitOp as detectInterruptedGitOp, index_d_detectWorklogGaps as detectWorklogGaps, index_d_effortMeter as effortMeter, index_d_ensureStatusline as ensureStatusline, index_d_ensureVortexHooks as ensureVortexHooks, index_d_ensureWorklogEntry as ensureWorklogEntry, index_d_extractNextUp as extractNextUp, index_d_extractOpenTasks as extractOpenTasks, index_d_failureReportSlice as failureReportSlice, index_d_findControlChar as findControlChar, index_d_formatTokens as formatTokens, index_d_formatWindow as formatWindow, index_d_gapWindowSinceArg as gapWindowSinceArg, index_d_globalMemoryPath as globalMemoryPath, index_d_globalSettingsHasHook as globalSettingsHasHook, index_d_globalSettingsPath as globalSettingsPath, index_d_globalStatePath as globalStatePath, index_d_guardWriteDecision as guardWriteDecision, index_d_handoffCommand as handoffCommand, index_d_inspectGlobalSetup as inspectGlobalSetup, index_d_inspectOwnership as inspectOwnership, index_d_isInstanceRoot as isInstanceRoot, index_d_isNewer as isNewer, index_d_isStableUpdate as isStableUpdate, index_d_isValidFailureKey as isValidFailureKey, index_d_ladderStage as ladderStage, index_d_logCommand as logCommand, index_d_makeBar as makeBar, index_d_ownershipManifestPath as ownershipManifestPath, index_d_parseAdoptArgs as parseAdoptArgs, index_d_parseSettings as parseSettings, index_d_parseStatuslineInput as parseStatuslineInput, index_d_pruneHandoffs as pruneHandoffs, index_d_queryNpmLatest as queryNpmLatest, index_d_readGlobalInstancePointer as readGlobalInstancePointer, index_d_readInstalledBaseVersion as readInstalledBaseVersion, index_d_recallCommand as recallCommand, index_d_recordFailure as recordFailure, index_d_recordGlobalSetupDecline as recordGlobalSetupDecline, index_d_recordGuardDenial as recordGuardDenial, index_d_reindexCommand as reindexCommand, index_d_renderAgenda as renderAgenda, index_d_renderGlobalBlock as renderGlobalBlock, index_d_renderSessionStartReport as renderSessionStartReport, index_d_renderStatusline as renderStatusline, index_d_repairOwnershipManifest as repairOwnershipManifest, index_d_resolveInstanceRoot as resolveInstanceRoot, index_d_resolveRepoRoot as resolveRepoRoot, index_d_runCurateAccept as runCurateAccept, index_d_runCurateCandidates as runCurateCandidates, index_d_runCurateDecline as runCurateDecline, index_d_runCuratePreview as runCuratePreview, index_d_runFailureCli as runFailureCli, index_d_runGuardCli as runGuardCli, index_d_runStatuslineCli as runStatuslineCli, index_d_runTemplatesUpdate as runTemplatesUpdate, index_d_runVortexCli as runVortexCli, index_d_safeSegment as safeSegment, index_d_scanFailures as scanFailures, index_d_scanHandoffs as scanHandoffs, index_d_scanToolInput as scanToolInput, index_d_serializeSettings as serializeSettings, index_d_sessionStartCommand as sessionStartCommand, index_d_sniffEffortFromTranscript as sniffEffortFromTranscript, index_d_statuslineCommand as statuslineCommand, index_d_templateDestRelPath as templateDestRelPath, index_d_upsertGlobalBlock as upsertGlobalBlock, index_d_validateCuratePayload as validateCuratePayload, index_d_vortexCommand as vortexCommand, index_d_writeOwnershipManifest as writeOwnershipManifest };
4677
+ export { type index_d_AgendaReport as AgendaReport, type index_d_AmbientRecallFactoryOptions as AmbientRecallFactoryOptions, type index_d_ArchiveCommitSkip as ArchiveCommitSkip, type index_d_ArchivePushSkip as ArchivePushSkip, type index_d_ArchiveSyncResult as ArchiveSyncResult, type index_d_CatchUpOptions as CatchUpOptions, type index_d_CatchUpResult as CatchUpResult, type index_d_ClaudeSettings as ClaudeSettings, type index_d_CliIo as CliIo, type index_d_CollectAgendaOptions as CollectAgendaOptions, type index_d_CurateAcceptResult as CurateAcceptResult, type index_d_CurateActionKind as CurateActionKind, type index_d_CurateAnyProposal as CurateAnyProposal, type index_d_CurateCandidate as CurateCandidate, type index_d_CurateCandidatesResult as CurateCandidatesResult, type index_d_CurateDeclineResult as CurateDeclineResult, type index_d_CurateOptions as CurateOptions, type index_d_CuratePayload as CuratePayload, type index_d_CuratePayloadValidation as CuratePayloadValidation, type index_d_CuratePreviewResult as CuratePreviewResult, type index_d_CurateResult as CurateResult, index_d_DEFAULT_GAP_WINDOW_DAYS as DEFAULT_GAP_WINDOW_DAYS, type index_d_EnsureHooksResult as EnsureHooksResult, type index_d_EnsureWorklogResult as EnsureWorklogResult, index_d_FAILURES_DIR as FAILURES_DIR, type index_d_FailureEntry as FailureEntry, type index_d_FailureGroup as FailureGroup, type index_d_FailureReportSlice as FailureReportSlice, type index_d_FailureScan as FailureScan, index_d_GUARD_DENIAL_KEY as GUARD_DENIAL_KEY, index_d_GUARD_WRITE_COMMAND as GUARD_WRITE_COMMAND, index_d_GUARD_WRITE_MATCHER as GUARD_WRITE_MATCHER, type index_d_GitPullResult as GitPullResult, type index_d_GuardFinding as GuardFinding, index_d_HANDOFF_ARCHIVE_DIR as HANDOFF_ARCHIVE_DIR, index_d_HANDOFF_DIR as HANDOFF_DIR, type index_d_HandoffCreateResult as HandoffCreateResult, type index_d_HandoffSummary as HandoffSummary, type index_d_HandoffWriteResult as HandoffWriteResult, type index_d_LadderStage as LadderStage, type index_d_NewDecisionResult as NewDecisionResult, index_d_OWNERSHIP_SCHEMA as OWNERSHIP_SCHEMA, type index_d_OpenDecision as OpenDecision, type index_d_OpenTask as OpenTask, type index_d_OwnershipDiagnosis as OwnershipDiagnosis, type index_d_OwnershipEntry as OwnershipEntry, type index_d_OwnershipManifest as OwnershipManifest, type index_d_RecallOptions as RecallOptions, type index_d_RecentWorklog as RecentWorklog, type index_d_RecordFailureInput as RecordFailureInput, type index_d_RecordFailureResult as RecordFailureResult, type index_d_ReindexResult as ReindexResult, type index_d_RitualRegistryOptions as RitualRegistryOptions, index_d_SESSION_END_COMMAND as SESSION_END_COMMAND, index_d_SESSION_START_COMMAND as SESSION_START_COMMAND, type index_d_SessionStartHookReport as SessionStartHookReport, type index_d_SessionStartReport as SessionStartReport, type index_d_StatuslineData as StatuslineData, type index_d_StatuslineInstallResult as StatuslineInstallResult, type index_d_StatuslineProbes as StatuslineProbes, type index_d_UpdateCheckResult as UpdateCheckResult, type index_d_UpdateFileAction as UpdateFileAction, type index_d_UpdateFileActionKind as UpdateFileActionKind, type index_d_VortexHelpResult as VortexHelpResult, type index_d_VortexInitResult as VortexInitResult, type index_d_VortexPlannedResult as VortexPlannedResult, type index_d_VortexResult as VortexResult, type index_d_VortexSyncResult as VortexSyncResult, type index_d_VortexSyncStep as VortexSyncStep, type index_d_VortexSyncStepId as VortexSyncStepId, type index_d_VortexSyncStepStatus as VortexSyncStepStatus, type index_d_VortexUpdateResult as VortexUpdateResult, type index_d_WorklogAppendResult as WorklogAppendResult, index_d_agendaCommand as agendaCommand, index_d_aggregateHandoff as aggregateHandoff, index_d_applyGlobalSetup as applyGlobalSetup, index_d_archiveRelPath as archiveRelPath, index_d_argvToSlash as argvToSlash, index_d_autoReindexMemory as autoReindexMemory, index_d_buildDenyDecision as buildDenyDecision, index_d_buildInstallCommand as buildInstallCommand, index_d_buildOwnershipManifest as buildOwnershipManifest, index_d_buildRegistry as buildRegistry, index_d_catchUpSessions as catchUpSessions, index_d_checkBaseUpdate as checkBaseUpdate, index_d_collectAgenda as collectAgenda, index_d_collectCarryover as collectCarryover, index_d_collectSessionStartReport as collectSessionStartReport, index_d_collectStatuslineProbes as collectStatuslineProbes, index_d_compareSemver as compareSemver, index_d_computeCurateFingerprint as computeCurateFingerprint, index_d_countUncommitted as countUncommitted, index_d_createAmbientRecaller as createAmbientRecaller, index_d_createHandoffSkeleton as createHandoffSkeleton, index_d_createRitualRegistry as createRitualRegistry, index_d_curateCommand as curateCommand, index_d_decisionCommand as decisionCommand, index_d_detectInterruptedGitOp as detectInterruptedGitOp, index_d_detectWorklogGaps as detectWorklogGaps, index_d_effortMeter as effortMeter, index_d_ensureStatusline as ensureStatusline, index_d_ensureVortexHooks as ensureVortexHooks, index_d_ensureWorklogEntry as ensureWorklogEntry, index_d_extractNextUp as extractNextUp, index_d_extractOpenTasks as extractOpenTasks, index_d_failureReportSlice as failureReportSlice, index_d_findControlChar as findControlChar, index_d_formatTokens as formatTokens, index_d_formatWindow as formatWindow, index_d_gapWindowSinceArg as gapWindowSinceArg, index_d_gitOut as gitOut, index_d_globalMemoryPath as globalMemoryPath, index_d_globalSettingsHasHook as globalSettingsHasHook, index_d_globalSettingsPath as globalSettingsPath, index_d_globalStatePath as globalStatePath, index_d_guardWriteDecision as guardWriteDecision, index_d_handoffCommand as handoffCommand, index_d_inspectGlobalSetup as inspectGlobalSetup, index_d_inspectOwnership as inspectOwnership, index_d_isInstanceRoot as isInstanceRoot, index_d_isNewer as isNewer, index_d_isStableUpdate as isStableUpdate, index_d_isValidFailureKey as isValidFailureKey, index_d_ladderStage as ladderStage, index_d_logCommand as logCommand, index_d_makeBar as makeBar, index_d_ownershipManifestPath as ownershipManifestPath, index_d_parseAdoptArgs as parseAdoptArgs, index_d_parseSettings as parseSettings, index_d_parseStatuslineInput as parseStatuslineInput, index_d_pruneHandoffs as pruneHandoffs, index_d_queryNpmLatest as queryNpmLatest, index_d_readGlobalInstancePointer as readGlobalInstancePointer, index_d_readInstalledBaseVersion as readInstalledBaseVersion, index_d_recallCommand as recallCommand, index_d_recordFailure as recordFailure, index_d_recordGlobalSetupDecline as recordGlobalSetupDecline, index_d_recordGuardDenial as recordGuardDenial, index_d_reindexCommand as reindexCommand, index_d_renderAgenda as renderAgenda, index_d_renderGlobalBlock as renderGlobalBlock, index_d_renderSessionStartReport as renderSessionStartReport, index_d_renderStatusline as renderStatusline, index_d_repairOwnershipManifest as repairOwnershipManifest, index_d_resolveInstanceRoot as resolveInstanceRoot, index_d_resolveRepoRoot as resolveRepoRoot, index_d_runArchiveSync as runArchiveSync, index_d_runCurateAccept as runCurateAccept, index_d_runCurateCandidates as runCurateCandidates, index_d_runCurateDecline as runCurateDecline, index_d_runCuratePreview as runCuratePreview, index_d_runFailureCli as runFailureCli, index_d_runGuardCli as runGuardCli, index_d_runStatuslineCli as runStatuslineCli, index_d_runTemplatesUpdate as runTemplatesUpdate, index_d_runVortexCli as runVortexCli, index_d_safeSegment as safeSegment, index_d_scanFailures as scanFailures, index_d_scanHandoffs as scanHandoffs, index_d_scanToolInput as scanToolInput, index_d_serializeSettings as serializeSettings, index_d_sessionStartCommand as sessionStartCommand, index_d_sniffEffortFromTranscript as sniffEffortFromTranscript, index_d_statuslineCommand as statuslineCommand, index_d_sweepOrphanTempFiles as sweepOrphanTempFiles, index_d_sweepRawArchive as sweepRawArchive, index_d_templateDestRelPath as templateDestRelPath, index_d_upsertGlobalBlock as upsertGlobalBlock, index_d_validateCuratePayload as validateCuratePayload, index_d_vortexCommand as vortexCommand, index_d_writeOwnershipManifest as writeOwnershipManifest };
4550
4678
  }
4551
4679
 
4552
4680
  export { index_d$9 as aiCodingPitfalls, index_d$d as core, index_d$a as dataLint, index_d$5 as decisionLog, index_d$4 as indexGenerator, index_d$2 as linkRewriter, index_d$b as memorySystem, index_d$1 as proactiveCurator, index_d$7 as reportGenerator, index_d$3 as runbooks, index_d as sessionRituals, index_d$c as slashCommands, index_d$8 as toolRules, index_d$6 as worklog };