@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.
@@ -13,9 +13,9 @@ import {
13
13
  safeSegment,
14
14
  sniffEffortFromTranscript,
15
15
  statuslineCommand
16
- } from "./chunk-P7IMUUNY.js";
17
- import "./chunk-2FVNWW77.js";
18
- import "./chunk-T53UWSTR.js";
16
+ } from "./chunk-OFID33QA.js";
17
+ import "./chunk-JKQAB5OK.js";
18
+ import "./chunk-IZIAMK3L.js";
19
19
  import "./chunk-PZ5AY32C.js";
20
20
  export {
21
21
  LOCAL_BIN_RELPATH,
@@ -33,4 +33,4 @@ export {
33
33
  sniffEffortFromTranscript,
34
34
  statuslineCommand
35
35
  };
36
- //# sourceMappingURL=statusline-JTSL5CCH.js.map
36
+ //# sourceMappingURL=statusline-6BGWOOLA.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vortex-os/base",
3
- "version": "0.12.1",
3
+ "version": "0.13.0",
4
4
  "description": "Base entry point for VortEX — a Multi-Agent Personal AI Work OS framework",
5
5
  "license": "MIT",
6
6
  "author": "vortex-os-project",
@@ -4,10 +4,16 @@
4
4
  "worklog": true,
5
5
  "decision": true,
6
6
  "ambientRecall": true,
7
- "archive": true
7
+ "archive": true,
8
+ "archiveCommit": true,
9
+ "archiveAtEnd": true,
10
+ "archiveRawRetentionDays": 30
8
11
  },
9
12
  "updates": {
10
13
  "check": "session"
11
14
  },
15
+ "sync": {
16
+ "autoPush": false
17
+ },
12
18
  "environments": []
13
19
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "schema": "vortex-template-index/1",
3
- "baseVersion": "0.12.1",
3
+ "baseVersion": "0.13.0",
4
4
  "files": [
5
5
  {
6
6
  "templateId": "commands/agenda.md",
@@ -30,7 +30,7 @@
30
30
  {
31
31
  "templateId": "config/vortex.json",
32
32
  "path": "config/vortex.json",
33
- "sha256": "71d629ce0038534dc3921a1c5e507897ec8754df50a76ad4e9da93327e84b4d6"
33
+ "sha256": "95a0be5c447beefcf274108e45ed76644222bf6c16ab85cbb9fe8e77ab6778d2"
34
34
  },
35
35
  {
36
36
  "templateId": "routers/.cursorrules",
@@ -45,7 +45,7 @@
45
45
  {
46
46
  "templateId": "routers/AI-RULES.md",
47
47
  "path": "routers/AI-RULES.md",
48
- "sha256": "44f41b61ef2e25f833d6cb58e27f7e43883c5256a482935ae68bf8f839d0479a"
48
+ "sha256": "e434c141420559a23aff3161813a0ef6148e6c2f9fa6422659c02969a9c036b8"
49
49
  },
50
50
  {
51
51
  "templateId": "routers/CLAUDE.md",
@@ -34,9 +34,10 @@ Operating principle: **auto-maintain the operational memory; propose changes to
34
34
  - **Session hand-off.** At wind-down — or whenever the user asks to hand off / continue later ("hand off", "wrap up so I can resume", "leave it for next time", "인수인계") — write a hand-off with `/handoff` (or `npx vortex handoff "<title>"`): **one file per session** under `data/_handoff/`, carrying the concrete next steps as unchecked `- [ ]` items, plus the current state and the pointers to resume from (files, commit hashes, decisions). A hand-off is a **forward-looking baton kept SEPARATE from the worklog** (the permanent "what happened" record): put next steps + how-to-resume here, leave the narrative in the worklog. One session that spans several topics writes **ONE** hand-off covering all of them — never a per-topic split, never a "hand-off" worklog file, and never carry another session's items forward (concurrent sessions each leave their own). The next session's start report surfaces the active hand-off(s) automatically (multiple are normal), and hand-offs older than the retention window (default 7 days) are swept to `_handoff/_archive/` on their own — so you never tidy them by hand.
35
35
  - **Decisions.** When the user makes a *substantive* decision ("let's go with X"), record it in the Decision Log. Real decisions, not casual asides.
36
36
  - **Failure ledger (the self-improvement loop).** When one of your own failures becomes visible — the user corrects you or pushes back on a mistake, a verification you ran disproves your work, a cross-check finds a real defect — record it: `npx vortex failure record --key <root-cause-key> --what "<one line>" [--signal user_pushback|tool_error|test_failure|cross_check_miss|guard_denied|self_detected] [--severity low|medium|high] [--rule <memory-slug-if-one-already-covers-it>] [--evidence <path>]`. The **key names the root cause, in stable kebab-case** — the same cause must reuse the same key (check `npx vortex failure list` when unsure), because the count is what drives escalation. The command returns the occurrence count and the **ladder stage**; relay its one-line note. The ladder: **1st** occurrence = recorded; **2nd** while a rule already covers it = that rule's write-time gate is **mandatory** for the rest of the session; **3rd+** = **propose** a deterministic defense to the user (a hook, a pre-commit check, a wrapper script — whatever removes the failure class mechanically), never self-install it. Promoting a lesson into `data/_memory/` requires: evidence links to the ledger entries, a replay check ("would this rule have prevented the recorded cases?"), and a conflict check against existing memories — then propose it as a diff for approval. The session-start report surfaces recurring keys automatically, so escalation never depends on anyone's memory. Mistakes by the user are NOT failures to ledger; this loop is about the agent's own repeated misses. **Write-guard denials record themselves** (the hook appends the ledger entry, with a per-incident dedup) — when a write is denied, fix the write and relay what happened, but do NOT record it again by hand.
37
+ - **Conversation archive sync.** Finished conversation transcripts are folded into `data/_session-archive/` and **auto-committed** (only that pathspec — your other changes are never swept in) at session start AND session end, so archived conversations never linger as "uncommitted changes" noise. The session you are in right now can't archive itself (its transcript is still being written) — it lands at the next start/end, which is normal. When the user asks to sync/push their instance, run `npx vortex catch-up` FIRST so the day's closed sessions are archived and committed before you push. Raw transcript copies (`_session-archive/raw/` — byte-for-byte duplicates kept for debugging; recall and cross-machine sync use only the normalized files, which are kept forever) are deleted locally after `autoRecord.archiveRawRetentionDays` (default 30) days. Sessions where the user never typed anything are not archived at all.
37
38
  - **Ambient recall** (read-only). When the user references earlier work, run `/recall <the reference>` and weave a confident hit into one sentence. One nudge, not a list; drop it if waved off. Conversations are vectorized automatically at session start. With the `@vortex-os/memory-extended` add-on installed, the first session also sets recall up on its own — downloading the local search model (~470 MB, once) in the **background** — so recall and these ambient lookups just work with no prompt and no manual step (the session-start report notes the one-time download while it runs; relay that). To keep that download manual instead (metered line, CI), set `autoRecord.vectorizeAutoDownload: false` (or env `VORTEX_VECTORIZE_AUTO_DOWNLOAD=0`) and set it up once with `/recall` or `vortex vectorize`.
38
39
 
39
- These are **append-only and transparent**: do them without a yes/no prompt, but surface a brief, non-blocking note so the user always sees what was recorded. Everything lands in git and is reversible; disable any of them via `.agent/vortex.json` (`autoRecord.*`). `git push` stays **explicit**, never automatic.
40
+ These are **append-only and transparent**: do them without a yes/no prompt, but surface a brief, non-blocking note so the user always sees what was recorded. Everything lands in git and is reversible; disable any of them via `.agent/vortex.json` (`autoRecord.*`). `git push` stays **explicit**, never automatic — with ONE opt-in exception: `sync.autoPush: true` in `.agent/vortex.json` lets the archive sync push after its auto-commit, for users who treat the instance repo as continuously synced. Know what that means before recommending it: git cannot push one commit in isolation, so an auto-push sends **every** unpushed commit on the branch, including the user's own.
40
41
 
41
42
  **Propose (ask first — the one place friction is intentional):**
42
43
  - **Knowledge-base documentation.** Changes to the user's topic documentation (the `data/<topic>/` tree and hubs) are **proposed, never auto-written**: "you've done X, Y, Z on this — update the existing doc, or create a new folder?" The user owns the shape of their knowledge base; the agent never silently restructures it.
@@ -127,7 +128,7 @@ These slash-commands are installed into `.claude/commands/` by `vortex init` (th
127
128
  | `/resume` | After a crash / abrupt close with no handoff: back-traces git state + the last session transcript to find what was in progress and whether it's wrapped up. Read-only; cheap signals first, transcript only if needed. |
128
129
  | `/reindex [dir]` | Regenerate `_INDEX.md` for a category (or all). Idempotent. |
129
130
 
130
- You can also run any of these directly: `npx vortex <command> [args]`. CLI-only (no slash command): `vortex failure record|list` — the failure ledger behind the self-improvement loop (see "Default behaviors"); `vortex statusline install [--lite]` — optional colored Claude Code status bar; `vortex guard write` — the write-guard hook body `vortex init` wires (denies literal control bytes in file writes; remove the PreToolUse group in `.claude/settings.json` to opt out).
131
+ You can also run any of these directly: `npx vortex <command> [args]`. CLI-only (no slash command): `vortex catch-up` — archive the day's closed conversations now (auto-commit included; run it before a manual sync/push, see "Default behaviors" → Conversation archive sync); `vortex failure record|list` — the failure ledger behind the self-improvement loop (see "Default behaviors"); `vortex statusline install [--lite]` — optional colored Claude Code status bar; `vortex guard write` — the write-guard hook body `vortex init` wires (denies literal control bytes in file writes; remove the PreToolUse group in `.claude/settings.json` to opt out).
131
132
 
132
133
  ## Optional add-ons
133
134
 
@@ -1 +0,0 @@
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-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\nexport interface VortexConfig {\r\n readonly autoRecord: AutoRecordConfig;\r\n readonly updates: UpdatesConfig;\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, vectorize: true, vectorizeAutoDownload: true, commitFrameworkChanges: true, handoff: true, handoffRetentionDays: 7, reindex: true, backfill: true, failures: true },\r\n updates: { check: \"session\" },\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 // `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 },\r\n updates: { check },\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"]}