@itsshadowai/refinery 0.1.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.
Files changed (99) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +228 -0
  3. package/coral/agents/claim-scout/coral-agent.toml +23 -0
  4. package/coral/agents/decision-synthesizer/coral-agent.toml +23 -0
  5. package/coral/agents/evidence-auditor/coral-agent.toml +23 -0
  6. package/coral/agents/memory-cartographer/coral-agent.toml +23 -0
  7. package/coral/agents/proposal-editor/coral-agent.toml +23 -0
  8. package/coral/agents/run-worker.sh +19 -0
  9. package/coral/refinery-config.toml +16 -0
  10. package/dist/adapters/codex-memory.d.ts +6 -0
  11. package/dist/adapters/codex-memory.js +264 -0
  12. package/dist/adapters/codex-memory.js.map +1 -0
  13. package/dist/cli.d.ts +2 -0
  14. package/dist/cli.js +671 -0
  15. package/dist/cli.js.map +1 -0
  16. package/dist/coral/client.d.ts +107 -0
  17. package/dist/coral/client.js +214 -0
  18. package/dist/coral/client.js.map +1 -0
  19. package/dist/coral/definitions.d.ts +25 -0
  20. package/dist/coral/definitions.js +53 -0
  21. package/dist/coral/definitions.js.map +1 -0
  22. package/dist/coral/mcp.d.ts +17 -0
  23. package/dist/coral/mcp.js +71 -0
  24. package/dist/coral/mcp.js.map +1 -0
  25. package/dist/coral/review-conductor.d.ts +120 -0
  26. package/dist/coral/review-conductor.js +1290 -0
  27. package/dist/coral/review-conductor.js.map +1 -0
  28. package/dist/coral/smoke.d.ts +1 -0
  29. package/dist/coral/smoke.js +265 -0
  30. package/dist/coral/smoke.js.map +1 -0
  31. package/dist/coral/topology.d.ts +5 -0
  32. package/dist/coral/topology.js +15 -0
  33. package/dist/coral/topology.js.map +1 -0
  34. package/dist/coral/worker.d.ts +34 -0
  35. package/dist/coral/worker.js +671 -0
  36. package/dist/coral/worker.js.map +1 -0
  37. package/dist/core/adapter.d.ts +93 -0
  38. package/dist/core/adapter.js +112 -0
  39. package/dist/core/adapter.js.map +1 -0
  40. package/dist/core/artifacts.d.ts +93 -0
  41. package/dist/core/artifacts.js +200 -0
  42. package/dist/core/artifacts.js.map +1 -0
  43. package/dist/core/deliberation.d.ts +89 -0
  44. package/dist/core/deliberation.js +385 -0
  45. package/dist/core/deliberation.js.map +1 -0
  46. package/dist/core/errors.d.ts +26 -0
  47. package/dist/core/errors.js +50 -0
  48. package/dist/core/errors.js.map +1 -0
  49. package/dist/core/intents.d.ts +5 -0
  50. package/dist/core/intents.js +34 -0
  51. package/dist/core/intents.js.map +1 -0
  52. package/dist/core/live-review.d.ts +93 -0
  53. package/dist/core/live-review.js +269 -0
  54. package/dist/core/live-review.js.map +1 -0
  55. package/dist/core/model-client.d.ts +19 -0
  56. package/dist/core/model-client.js +45 -0
  57. package/dist/core/model-client.js.map +1 -0
  58. package/dist/core/paths.d.ts +16 -0
  59. package/dist/core/paths.js +43 -0
  60. package/dist/core/paths.js.map +1 -0
  61. package/dist/core/review.d.ts +93 -0
  62. package/dist/core/review.js +102 -0
  63. package/dist/core/review.js.map +1 -0
  64. package/dist/core/specialists/claim-scout.d.ts +2 -0
  65. package/dist/core/specialists/claim-scout.js +26 -0
  66. package/dist/core/specialists/claim-scout.js.map +1 -0
  67. package/dist/core/specialists/decision-synthesizer.d.ts +2 -0
  68. package/dist/core/specialists/decision-synthesizer.js +35 -0
  69. package/dist/core/specialists/decision-synthesizer.js.map +1 -0
  70. package/dist/core/specialists/evidence-auditor.d.ts +2 -0
  71. package/dist/core/specialists/evidence-auditor.js +35 -0
  72. package/dist/core/specialists/evidence-auditor.js.map +1 -0
  73. package/dist/core/specialists/harness.d.ts +2 -0
  74. package/dist/core/specialists/harness.js +13 -0
  75. package/dist/core/specialists/harness.js.map +1 -0
  76. package/dist/core/specialists/index.d.ts +8 -0
  77. package/dist/core/specialists/index.js +8 -0
  78. package/dist/core/specialists/index.js.map +1 -0
  79. package/dist/core/specialists/memory-cartographer.d.ts +2 -0
  80. package/dist/core/specialists/memory-cartographer.js +33 -0
  81. package/dist/core/specialists/memory-cartographer.js.map +1 -0
  82. package/dist/core/specialists/prompt.d.ts +3 -0
  83. package/dist/core/specialists/prompt.js +25 -0
  84. package/dist/core/specialists/prompt.js.map +1 -0
  85. package/dist/core/specialists/proposal-editor.d.ts +2 -0
  86. package/dist/core/specialists/proposal-editor.js +37 -0
  87. package/dist/core/specialists/proposal-editor.js.map +1 -0
  88. package/dist/core/specialists/types.d.ts +20 -0
  89. package/dist/core/specialists/types.js +2 -0
  90. package/dist/core/specialists/types.js.map +1 -0
  91. package/dist/env.d.ts +11 -0
  92. package/dist/env.js +53 -0
  93. package/dist/env.js.map +1 -0
  94. package/dist/mcp.d.ts +9 -0
  95. package/dist/mcp.js +147 -0
  96. package/dist/mcp.js.map +1 -0
  97. package/package.json +50 -0
  98. package/skills/refinery/SKILL.md +117 -0
  99. package/skills/refinery/agents/openai.yaml +4 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Shadow AI
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,228 @@
1
+ # refinery
2
+
3
+ Refinery is a Codex-first memory review CLI. It reads bounded Codex memory
4
+ files, runs a dry-run Coral-coordinated specialist review, and emits proposal
5
+ artifacts that a coding agent or host app can inspect before applying changes
6
+ elsewhere.
7
+
8
+ Refinery does not approve proposals, apply edits, or own durable memory truth.
9
+ For the first useful version, Codex memories are the only built-in memory
10
+ surface.
11
+
12
+ ## Requirements
13
+
14
+ - Node.js >= 22.
15
+ - Codex memories enabled and available under `~/.codex/memories`, or another
16
+ explicitly provided directory named `memories`.
17
+ - Model credentials for live review, usually `OPENROUTER_API_KEY` or
18
+ `MODEL_API_KEY`.
19
+
20
+ ## Install
21
+
22
+ ```bash
23
+ npm install -g @itsshadowai/refinery
24
+ refinery init --json
25
+ refinery doctor --json
26
+ ```
27
+
28
+ `refinery init` creates global Refinery state under `~/.refinery` and installs
29
+ the bundled `$refinery` Codex skill into `${CODEX_HOME:-~/.codex}/skills/refinery`.
30
+ It preserves an existing installed skill unless `--force` is passed.
31
+
32
+ ## Commands
33
+
34
+ ```bash
35
+ # Verify the local Codex memory source is readable.
36
+ refinery doctor --json
37
+
38
+ # Verify the installed CLI version.
39
+ refinery version --json
40
+
41
+ # Verify a non-default Codex memory directory.
42
+ refinery doctor --memory-home /path/to/memories --json
43
+
44
+ # Run a dry-run stale-memory audit over Codex memories.
45
+ refinery review \
46
+ --project . \
47
+ --intent stale-audit \
48
+ --request "Find Codex memories that may be stale after recent repo moves." \
49
+ --json
50
+
51
+ # Seed a live Coral Console debate/critique session without writing run artifacts.
52
+ refinery console run \
53
+ --project . \
54
+ --intent stale-audit \
55
+ --json
56
+
57
+ # Inspect an existing run without invoking Coral or a model.
58
+ refinery trial inspect --run-dir ~/.refinery/runs/by-project/<project-key>/<run-id> --json
59
+ ```
60
+
61
+ The CLI always emits structured JSON for these commands. Failures use
62
+ `ok: false` with `error.code`, `error.message`, and `error.phase` when known.
63
+ Secrets are not emitted.
64
+
65
+ ## Memory Source
66
+
67
+ The built-in Codex memory reader is intentionally bounded. It accepts only a
68
+ directory named `memories`, normally `~/.codex/memories`, and does not crawl all
69
+ of `~/.codex`.
70
+
71
+ Indexed files:
72
+
73
+ - `MEMORY.md`
74
+ - `memory_summary.md`
75
+ - `rollout_summaries/*.md`
76
+ - `extensions/ad_hoc/**/*.md`
77
+
78
+ Records use opaque IDs such as `codex-source:<hash>` and
79
+ `codex-memory:<hash>`. Proposal targets should use those opaque IDs rather than
80
+ database IDs or file offsets.
81
+
82
+ ## Review
83
+
84
+ `refinery review` is dry-run only. It starts or targets Coral, creates bounded
85
+ proposal and critique threads, runs the five Refinery specialists, writes a run
86
+ directory, and returns proposed memory-maintenance actions.
87
+
88
+ The default workflow is debate/critique. Claim Scout extracts candidate memory
89
+ claims, Memory Cartographer maps nearby active memories, Evidence Auditor checks
90
+ support and provenance, Proposal Editor turns surviving claims into typed
91
+ proposal packets, and Decision Synthesizer resolves challenges into final
92
+ proposals, rejected candidates, and unresolved questions. Each specialist
93
+ message is persisted under that step's `messages/` artifact directory.
94
+
95
+ Supported review intents:
96
+
97
+ ```text
98
+ general-review
99
+ stale-audit
100
+ forget-candidates
101
+ update-candidates
102
+ conflict-audit
103
+ scope-audit
104
+ ```
105
+
106
+ Proposal actions:
107
+
108
+ ```text
109
+ create, update, supersede, merge, archive, retag, quarantine,
110
+ promote, demote, ttl_update, contradiction_review
111
+ ```
112
+
113
+ Proposal lifecycle states:
114
+
115
+ ```text
116
+ proposed, needs_review, accepted, rejected, deferred,
117
+ applied_externally, superseded, archived_for_audit
118
+ ```
119
+
120
+ New proposals default to `lifecycle: "proposed"`. Applying or rejecting them is
121
+ owned by the caller.
122
+
123
+ ## Trial Artifacts
124
+
125
+ Runtime state is globally organized by default:
126
+
127
+ ```text
128
+ ~/.refinery/
129
+ config/
130
+ credentials/
131
+ runs/
132
+ by-project/
133
+ <project-key>/
134
+ <run-id>/
135
+ input.json
136
+ manifest.json
137
+ metadata.json
138
+ proposals.json
139
+ rejected.json
140
+ claims.json
141
+ challenge-ledger.json
142
+ deliberation.json
143
+ review.json
144
+ coral.json
145
+ transcript.json
146
+ steps/
147
+ claim-scout/{input.json,output.raw.md,output.parsed.json}
148
+ memory-cartographer/{input.json,output.raw.md,output.parsed.json}
149
+ evidence-auditor/{input.json,output.raw.md,output.parsed.json}
150
+ proposal-editor/{input.json,output.raw.md,output.parsed.json}
151
+ decision-synthesizer/{input.json,output.raw.md,output.parsed.json}
152
+ ```
153
+
154
+ Failed reviews that reach a run directory write `status.json`, failed
155
+ `review.json`, and any available step error artifacts. Use `trial inspect` for a
156
+ stable summary instead of scraping file paths.
157
+
158
+ Use `--home ./.refinery` only when you intentionally want project-local
159
+ Refinery state. The default keeps run artifacts and future credentials/config
160
+ global while grouping runs by project key.
161
+
162
+ ## Coral Runtime
163
+
164
+ The default runtime is Coral. Refinery owns local executable agent manifests
165
+ under `coral/agents/*` and a repo-local config at `coral/refinery-config.toml`.
166
+ The CLI can also attach to an existing Coral server:
167
+
168
+ ```bash
169
+ refinery review \
170
+ --coral-url http://localhost:5555 \
171
+ --coral-no-start \
172
+ --json
173
+ ```
174
+
175
+ Caller-owned Coral sessions and threads are not torn down by Refinery.
176
+
177
+ ## Console Mode
178
+
179
+ `refinery console run` is a local development command for Coral Console
180
+ inspection. It reads the bounded Codex memory source, starts Coral when
181
+ `--coral-url` is not provided, creates a session and thread set, seeds the
182
+ default debate/critique workflow, prints the console URL and session
183
+ identifiers, and does not write run artifacts.
184
+
185
+ The default console topology is `debate-critique`. When Refinery starts the
186
+ Coral server, the command stays in the foreground so the Console remains
187
+ available until interrupted.
188
+
189
+ ## Development
190
+
191
+ ```bash
192
+ npm test
193
+ npm run build
194
+ npm link
195
+ refinery version --json
196
+ ```
197
+
198
+ The test suite covers the Codex memory adapter, Codex-first CLI contract, Coral
199
+ worker/conductor helpers, artifact inspection, model client, intents, MCP
200
+ specialist prompt tools, and specialist contracts.
201
+
202
+ ### Codex Skill
203
+
204
+ Use one Codex skill for Refinery memory work:
205
+
206
+ ```text
207
+ $refinery
208
+ ```
209
+
210
+ Example prompt:
211
+
212
+ ```text
213
+ Use $refinery to inspect the current project Codex memories with intent update-candidates, source-limit 1, source-char-limit 2500, and summarize the proposed edits.
214
+ ```
215
+
216
+ The companion skill should be installed once in the Codex global skill root:
217
+
218
+ ```text
219
+ ~/.codex/skills/refinery/SKILL.md
220
+ ```
221
+
222
+ Do not keep repo-local alternate copies of the Refinery skill; they create
223
+ duplicate suggestions and teach agents old invocation names. `$refinery`
224
+ defaults to live `refinery review`. For deterministic rehearsal only:
225
+
226
+ ```bash
227
+ refinery dev fixture memory-proposal --json
228
+ ```
@@ -0,0 +1,23 @@
1
+ edition = 4
2
+
3
+ [agent]
4
+ name = "refinery-claim-scout"
5
+ version = "0.1.0"
6
+ description = "Refinery Claim Scout specialist as a Coral executable agent."
7
+ summary = "Claim Scout extracts candidate memory claims from source evidence."
8
+ readme = "Executable Refinery specialist. Worker owns Coral wait/send and maps to src/core/specialists/claim-scout."
9
+
10
+ [agent.license]
11
+ type = "spdx"
12
+ expression = "MIT"
13
+
14
+ [options]
15
+ MODEL_NAME = { type = "string", default = "deepseek/deepseek-v4-pro" }
16
+ MODEL_BASE_URL = { type = "string", default = "https://openrouter.ai/api/v1" }
17
+ REASONING_EFFORT = { type = "string", default = "low" }
18
+ REFINERY_CORAL_MAX_TURNS = { type = "string", default = "1" }
19
+
20
+ [runtimes.executable]
21
+ path = "../run-worker.sh"
22
+ arguments = ["--specialist", "claim-scout"]
23
+ transport = "streamable_http"
@@ -0,0 +1,23 @@
1
+ edition = 4
2
+
3
+ [agent]
4
+ name = "refinery-decision-synthesizer"
5
+ version = "0.1.0"
6
+ description = "Refinery Decision Synthesizer specialist as a Coral executable agent."
7
+ summary = "Decision Synthesizer resolves challenges into final proposed edits, rejected candidates, and unresolved questions."
8
+ readme = "Executable Refinery specialist. Worker owns Coral wait/send and maps to src/core/specialists/decision-synthesizer."
9
+
10
+ [agent.license]
11
+ type = "spdx"
12
+ expression = "MIT"
13
+
14
+ [options]
15
+ MODEL_NAME = { type = "string", default = "deepseek/deepseek-v4-pro" }
16
+ MODEL_BASE_URL = { type = "string", default = "https://openrouter.ai/api/v1" }
17
+ REASONING_EFFORT = { type = "string", default = "low" }
18
+ REFINERY_CORAL_MAX_TURNS = { type = "string", default = "1" }
19
+
20
+ [runtimes.executable]
21
+ path = "../run-worker.sh"
22
+ arguments = ["--specialist", "decision-synthesizer"]
23
+ transport = "streamable_http"
@@ -0,0 +1,23 @@
1
+ edition = 4
2
+
3
+ [agent]
4
+ name = "refinery-evidence-auditor"
5
+ version = "0.1.0"
6
+ description = "Refinery Evidence Auditor specialist as a Coral executable agent."
7
+ summary = "Evidence Auditor checks support, provenance, truncation, lineage, and write risk."
8
+ readme = "Executable Refinery specialist. Worker owns Coral wait/send and maps to src/core/specialists/evidence-auditor."
9
+
10
+ [agent.license]
11
+ type = "spdx"
12
+ expression = "MIT"
13
+
14
+ [options]
15
+ MODEL_NAME = { type = "string", default = "deepseek/deepseek-v4-pro" }
16
+ MODEL_BASE_URL = { type = "string", default = "https://openrouter.ai/api/v1" }
17
+ REASONING_EFFORT = { type = "string", default = "low" }
18
+ REFINERY_CORAL_MAX_TURNS = { type = "string", default = "1" }
19
+
20
+ [runtimes.executable]
21
+ path = "../run-worker.sh"
22
+ arguments = ["--specialist", "evidence-auditor"]
23
+ transport = "streamable_http"
@@ -0,0 +1,23 @@
1
+ edition = 4
2
+
3
+ [agent]
4
+ name = "refinery-memory-cartographer"
5
+ version = "0.1.0"
6
+ description = "Refinery Memory Cartographer specialist as a Coral executable agent."
7
+ summary = "Memory Cartographer maps nearby memories, duplicates, conflicts, and supersession targets."
8
+ readme = "Executable Refinery specialist. Worker owns Coral wait/send and maps to src/core/specialists/memory-cartographer."
9
+
10
+ [agent.license]
11
+ type = "spdx"
12
+ expression = "MIT"
13
+
14
+ [options]
15
+ MODEL_NAME = { type = "string", default = "deepseek/deepseek-v4-pro" }
16
+ MODEL_BASE_URL = { type = "string", default = "https://openrouter.ai/api/v1" }
17
+ REASONING_EFFORT = { type = "string", default = "low" }
18
+ REFINERY_CORAL_MAX_TURNS = { type = "string", default = "1" }
19
+
20
+ [runtimes.executable]
21
+ path = "../run-worker.sh"
22
+ arguments = ["--specialist", "memory-cartographer"]
23
+ transport = "streamable_http"
@@ -0,0 +1,23 @@
1
+ edition = 4
2
+
3
+ [agent]
4
+ name = "refinery-proposal-editor"
5
+ version = "0.1.0"
6
+ description = "Refinery Proposal Editor specialist as a Coral executable agent."
7
+ summary = "Proposal Editor turns surviving claims into typed memory proposal packets."
8
+ readme = "Executable Refinery specialist. Worker owns Coral wait/send and maps to src/core/specialists/proposal-editor."
9
+
10
+ [agent.license]
11
+ type = "spdx"
12
+ expression = "MIT"
13
+
14
+ [options]
15
+ MODEL_NAME = { type = "string", default = "deepseek/deepseek-v4-pro" }
16
+ MODEL_BASE_URL = { type = "string", default = "https://openrouter.ai/api/v1" }
17
+ REASONING_EFFORT = { type = "string", default = "low" }
18
+ REFINERY_CORAL_MAX_TURNS = { type = "string", default = "1" }
19
+
20
+ [runtimes.executable]
21
+ path = "../run-worker.sh"
22
+ arguments = ["--specialist", "proposal-editor"]
23
+ transport = "streamable_http"
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
5
+ REPO_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"
6
+
7
+ if [[ -n "${REFINERY_NODE_BIN:-}" ]]; then
8
+ NODE_BIN="${REFINERY_NODE_BIN}"
9
+ elif [[ -x "${HOME}/.nvm/versions/node/v24.10.0/bin/node" ]]; then
10
+ NODE_BIN="${HOME}/.nvm/versions/node/v24.10.0/bin/node"
11
+ else
12
+ NODE_BIN="node"
13
+ fi
14
+
15
+ cd "${REPO_ROOT}"
16
+ if [[ -f "${REPO_ROOT}/dist/coral/worker.js" ]]; then
17
+ exec "${NODE_BIN}" dist/coral/worker.js "$@"
18
+ fi
19
+ exec "${NODE_BIN}" src/coral/worker.ts "$@"
@@ -0,0 +1,16 @@
1
+ [network]
2
+ bind_address = "127.0.0.1"
3
+ external_address = "127.0.0.1"
4
+ bind_port = 5555
5
+ allow_any_host = true
6
+
7
+ [auth]
8
+ keys = ["refinery-dev"]
9
+
10
+ [registry]
11
+ include_coral_home_agents = false
12
+ include_debug_agents = false
13
+ export_debug_agents = false
14
+ watch_local_agents = true
15
+ local_agent_rescan_timer = "10s"
16
+ local_agents = ["/Users/bambozlor/Lab/Research-Desk/refinery/coral/agents/*"]
@@ -0,0 +1,6 @@
1
+ import type { MemoryStoreAdapter } from "../core/adapter.ts";
2
+ export interface CodexMemoryAdapterOptions {
3
+ memoryHome?: string;
4
+ }
5
+ export declare function resolveCodexMemoryHome(memoryHome?: string): string;
6
+ export declare function createCodexMemoryAdapter(options?: CodexMemoryAdapterOptions): MemoryStoreAdapter;
@@ -0,0 +1,264 @@
1
+ import crypto from "node:crypto";
2
+ import fs from "node:fs";
3
+ import os from "node:os";
4
+ import path from "node:path";
5
+ import { RefineryError } from "../core/errors.js";
6
+ export function resolveCodexMemoryHome(memoryHome) {
7
+ return path.resolve(memoryHome ?? path.join(os.homedir(), ".codex", "memories"));
8
+ }
9
+ function hashId(prefix, parts) {
10
+ const hash = crypto.createHash("sha256");
11
+ for (const part of parts)
12
+ hash.update(part).update("\0");
13
+ return `${prefix}:${hash.digest("hex").slice(0, 16)}`;
14
+ }
15
+ function compactText(text, max = 4000) {
16
+ const compact = text.replace(/\s+/g, " ").trim();
17
+ if (compact.length <= max)
18
+ return compact;
19
+ return `${compact.slice(0, max - 3).trimEnd()}...`;
20
+ }
21
+ function assertSafeMemoryHome(memoryHome) {
22
+ if (path.basename(memoryHome) !== "memories") {
23
+ throw new RefineryError("CODEX_MEMORY_HOME_UNSAFE", "memoryHome must point to a directory named memories, such as ~/.codex/memories.", { phase: "adapter", details: { memoryHome } });
24
+ }
25
+ }
26
+ function ensureMemoryHome(memoryHome) {
27
+ if (!fs.existsSync(memoryHome) || !fs.statSync(memoryHome).isDirectory()) {
28
+ throw new RefineryError("CODEX_MEMORY_HOME_NOT_FOUND", `Codex memory home does not exist: ${memoryHome}`, { phase: "adapter", details: { memoryHome } });
29
+ }
30
+ }
31
+ function readIfExists(memoryHome, relPath) {
32
+ const absPath = path.join(memoryHome, relPath);
33
+ if (!fs.existsSync(absPath) || !fs.statSync(absPath).isFile())
34
+ return null;
35
+ return toDocument(memoryHome, absPath);
36
+ }
37
+ function walkMarkdown(dir) {
38
+ if (!fs.existsSync(dir) || !fs.statSync(dir).isDirectory())
39
+ return [];
40
+ const out = [];
41
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
42
+ const abs = path.join(dir, entry.name);
43
+ if (entry.isDirectory()) {
44
+ out.push(...walkMarkdown(abs));
45
+ }
46
+ else if (entry.isFile() && entry.name.endsWith(".md")) {
47
+ out.push(abs);
48
+ }
49
+ }
50
+ return out.sort();
51
+ }
52
+ function originKindForRelPath(relPath) {
53
+ if (relPath === "MEMORY.md")
54
+ return "memory-index";
55
+ if (relPath === "memory_summary.md")
56
+ return "memory-summary";
57
+ if (relPath === "raw_memories.md")
58
+ return "raw-memory";
59
+ if (relPath === "phase2_workspace_diff.md")
60
+ return "workspace-diff";
61
+ if (relPath.startsWith("rollout_summaries/"))
62
+ return "rollout-summary";
63
+ if (relPath.startsWith("extensions/ad_hoc/"))
64
+ return "ad-hoc-note";
65
+ return "other";
66
+ }
67
+ function sourceKindForOrigin(originKind) {
68
+ switch (originKind) {
69
+ case "memory-index":
70
+ return "codex-memory-index";
71
+ case "memory-summary":
72
+ return "codex-memory-summary";
73
+ case "rollout-summary":
74
+ return "codex-rollout-summary";
75
+ case "ad-hoc-note":
76
+ return "codex-ad-hoc-note";
77
+ case "raw-memory":
78
+ return "codex-raw-memory";
79
+ case "workspace-diff":
80
+ return "codex-workspace-diff";
81
+ case "other":
82
+ return "codex-memory-document";
83
+ }
84
+ }
85
+ function firstMetadataValue(text, field) {
86
+ const match = text.match(new RegExp(`^${field}:\\s*(.+)$`, "m"));
87
+ return match ? match[1].trim() : null;
88
+ }
89
+ function parseRolloutListMetadata(text) {
90
+ const match = text.match(/\(([^)]*thread_id=[^)]+)\)/);
91
+ if (!match)
92
+ return {};
93
+ const body = match[1];
94
+ const metadata = {};
95
+ for (const part of body.split(/,\s*/)) {
96
+ const eq = part.indexOf("=");
97
+ if (eq <= 0)
98
+ continue;
99
+ const key = part.slice(0, eq).trim();
100
+ const value = part.slice(eq + 1).trim();
101
+ if (key === "thread_id")
102
+ metadata.threadId = value;
103
+ else if (key === "updated_at")
104
+ metadata.updatedAt = value;
105
+ else if (key === "rollout_path")
106
+ metadata.rolloutPath = value;
107
+ else if (key === "cwd")
108
+ metadata.cwd = value;
109
+ else
110
+ metadata[key] = value;
111
+ }
112
+ return metadata;
113
+ }
114
+ function metadataFor(relPath, text, originKind) {
115
+ const metadata = {
116
+ originKind,
117
+ relPath,
118
+ };
119
+ if (originKind === "rollout-summary") {
120
+ metadata.threadId = firstMetadataValue(text, "thread_id");
121
+ metadata.updatedAt = firstMetadataValue(text, "updated_at");
122
+ metadata.rolloutPath = firstMetadataValue(text, "rollout_path");
123
+ metadata.cwd = firstMetadataValue(text, "cwd");
124
+ }
125
+ Object.assign(metadata, parseRolloutListMetadata(text));
126
+ return Object.fromEntries(Object.entries(metadata).filter(([, value]) => value !== null && value !== undefined));
127
+ }
128
+ function toDocument(memoryHome, absPath) {
129
+ const relPath = path.relative(memoryHome, absPath).split(path.sep).join("/");
130
+ const text = fs.readFileSync(absPath, "utf8");
131
+ const originKind = originKindForRelPath(relPath);
132
+ return {
133
+ relPath,
134
+ absPath,
135
+ text,
136
+ originKind,
137
+ sourceKind: sourceKindForOrigin(originKind),
138
+ metadata: metadataFor(relPath, text, originKind),
139
+ };
140
+ }
141
+ function loadDocuments(memoryHome) {
142
+ assertSafeMemoryHome(memoryHome);
143
+ ensureMemoryHome(memoryHome);
144
+ const docs = [];
145
+ for (const relPath of ["MEMORY.md", "memory_summary.md", "raw_memories.md", "phase2_workspace_diff.md"]) {
146
+ const doc = readIfExists(memoryHome, relPath);
147
+ if (doc)
148
+ docs.push(doc);
149
+ }
150
+ for (const abs of walkMarkdown(path.join(memoryHome, "rollout_summaries")))
151
+ docs.push(toDocument(memoryHome, abs));
152
+ for (const abs of walkMarkdown(path.join(memoryHome, "extensions/ad_hoc")))
153
+ docs.push(toDocument(memoryHome, abs));
154
+ return docs;
155
+ }
156
+ function documentToSource(doc) {
157
+ return {
158
+ id: hashId("codex-source", [doc.relPath, doc.text]),
159
+ kind: doc.sourceKind,
160
+ path: doc.relPath,
161
+ text: compactText(doc.text, 8000),
162
+ refs: [{ source_path: doc.relPath, origin_kind: doc.originKind }],
163
+ metadata: doc.metadata,
164
+ };
165
+ }
166
+ function headingForLine(lines, lineIndex) {
167
+ for (let i = lineIndex; i >= 0; i -= 1) {
168
+ const match = lines[i].match(/^#{1,6}\s+(.+)$/);
169
+ if (match)
170
+ return match[1].trim();
171
+ }
172
+ return null;
173
+ }
174
+ function inferMemoryType(originKind, heading, body) {
175
+ const text = `${heading ?? ""} ${body}`.toLowerCase();
176
+ if (text.includes("preference") || text.includes("when the user") || text.includes("should "))
177
+ return "operational";
178
+ if (text.includes("failure") || text.includes("fix:") || text.includes("symptom:"))
179
+ return "procedural";
180
+ if (originKind === "ad-hoc-note")
181
+ return "semantic";
182
+ return "semantic";
183
+ }
184
+ function documentToMemories(doc) {
185
+ const lines = doc.text.split(/\r?\n/);
186
+ const records = [];
187
+ lines.forEach((line, index) => {
188
+ const bullet = line.match(/^\s*-\s+(.+)$/);
189
+ if (!bullet)
190
+ return;
191
+ const body = bullet[1].trim();
192
+ if (!body)
193
+ return;
194
+ const heading = headingForLine(lines, index);
195
+ records.push({
196
+ id: hashId("codex-memory", [doc.relPath, String(index + 1), body]),
197
+ type: inferMemoryType(doc.originKind, heading, body),
198
+ scope: "project",
199
+ status: "active",
200
+ body,
201
+ confidence: null,
202
+ provenance: {
203
+ originKind: doc.originKind,
204
+ sourcePath: doc.relPath,
205
+ heading,
206
+ line: index + 1,
207
+ threadId: typeof doc.metadata.threadId === "string" ? doc.metadata.threadId : null,
208
+ updatedAt: typeof doc.metadata.updatedAt === "string" ? doc.metadata.updatedAt : null,
209
+ },
210
+ });
211
+ });
212
+ if (records.length === 0 && doc.originKind === "ad-hoc-note" && doc.text.trim()) {
213
+ records.push({
214
+ id: hashId("codex-memory", [doc.relPath, doc.text]),
215
+ type: "semantic",
216
+ scope: "project",
217
+ status: "active",
218
+ body: compactText(doc.text, 1600),
219
+ confidence: null,
220
+ provenance: {
221
+ originKind: doc.originKind,
222
+ sourcePath: doc.relPath,
223
+ heading: null,
224
+ line: 1,
225
+ },
226
+ });
227
+ }
228
+ return records;
229
+ }
230
+ function filterByQuery(items, query) {
231
+ const q = query.toLowerCase();
232
+ return items.filter((item) => [item.body, item.text, item.path].some((value) => typeof value === "string" && value.toLowerCase().includes(q)));
233
+ }
234
+ function limitItems(items, limit) {
235
+ return typeof limit === "number" && Number.isFinite(limit) && limit > 0 ? items.slice(0, limit) : items;
236
+ }
237
+ export function createCodexMemoryAdapter(options = {}) {
238
+ const memoryHome = resolveCodexMemoryHome(options.memoryHome);
239
+ assertSafeMemoryHome(memoryHome);
240
+ const readSources = () => loadDocuments(memoryHome).map(documentToSource);
241
+ const readMemories = () => loadDocuments(memoryHome).flatMap(documentToMemories);
242
+ return {
243
+ name: "codex-memory",
244
+ async listSourceEvidence(input) {
245
+ return limitItems(readSources(), input.limit);
246
+ },
247
+ async searchSourceEvidence(input) {
248
+ return limitItems(filterByQuery(readSources(), input.query), input.limit);
249
+ },
250
+ async getSourceEvidence(input) {
251
+ return readSources().find((source) => source.id === input.id) ?? null;
252
+ },
253
+ async listActiveMemories(input) {
254
+ return limitItems(readMemories(), input.limit);
255
+ },
256
+ async searchActiveMemories(input) {
257
+ return limitItems(filterByQuery(readMemories(), input.query), input.limit);
258
+ },
259
+ async getActiveMemory(input) {
260
+ return readMemories().find((memory) => memory.id === input.id) ?? null;
261
+ },
262
+ };
263
+ }
264
+ //# sourceMappingURL=codex-memory.js.map