ralph-research 0.1.2 → 0.1.3

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 (127) hide show
  1. package/README.md +127 -101
  2. package/dist/adapters/fs/json-file-research-project-defaults-store.d.ts +8 -0
  3. package/dist/adapters/fs/json-file-research-project-defaults-store.js +30 -0
  4. package/dist/adapters/fs/json-file-research-project-defaults-store.js.map +1 -0
  5. package/dist/adapters/fs/json-file-research-session-repository.d.ts +24 -0
  6. package/dist/adapters/fs/json-file-research-session-repository.js +199 -0
  7. package/dist/adapters/fs/json-file-research-session-repository.js.map +1 -0
  8. package/dist/adapters/fs/manifest-loader.js +8 -1
  9. package/dist/adapters/fs/manifest-loader.js.map +1 -1
  10. package/dist/adapters/proposer/codex-cli-proposer.d.ts +16 -0
  11. package/dist/adapters/proposer/codex-cli-proposer.js +106 -0
  12. package/dist/adapters/proposer/codex-cli-proposer.js.map +1 -0
  13. package/dist/adapters/proposer/codex-cli-session-driver.d.ts +64 -0
  14. package/dist/adapters/proposer/codex-cli-session-driver.js +182 -0
  15. package/dist/adapters/proposer/codex-cli-session-driver.js.map +1 -0
  16. package/dist/adapters/proposer/codex-cli-session-manager.d.ts +79 -0
  17. package/dist/adapters/proposer/codex-cli-session-manager.js +248 -0
  18. package/dist/adapters/proposer/codex-cli-session-manager.js.map +1 -0
  19. package/dist/adapters/proposer/codex-cli-session-outcome-extractor.d.ts +3 -0
  20. package/dist/adapters/proposer/codex-cli-session-outcome-extractor.js +94 -0
  21. package/dist/adapters/proposer/codex-cli-session-outcome-extractor.js.map +1 -0
  22. package/dist/adapters/proposer/proposer-factory.d.ts +22 -0
  23. package/dist/adapters/proposer/proposer-factory.js +19 -0
  24. package/dist/adapters/proposer/proposer-factory.js.map +1 -0
  25. package/dist/app/services/codex-cli-session-lifecycle-service.d.ts +116 -0
  26. package/dist/app/services/codex-cli-session-lifecycle-service.js +186 -0
  27. package/dist/app/services/codex-cli-session-lifecycle-service.js.map +1 -0
  28. package/dist/app/services/research-project-defaults-service.d.ts +18 -0
  29. package/dist/app/services/research-project-defaults-service.js +175 -0
  30. package/dist/app/services/research-project-defaults-service.js.map +1 -0
  31. package/dist/app/services/research-session-draft-service.d.ts +121 -0
  32. package/dist/app/services/research-session-draft-service.js +846 -0
  33. package/dist/app/services/research-session-draft-service.js.map +1 -0
  34. package/dist/app/services/research-session-entry-flow-summary-mapper.d.ts +12 -0
  35. package/dist/app/services/research-session-entry-flow-summary-mapper.js +33 -0
  36. package/dist/app/services/research-session-entry-flow-summary-mapper.js.map +1 -0
  37. package/dist/app/services/research-session-interactive-service.d.ts +35 -0
  38. package/dist/app/services/research-session-interactive-service.js +295 -0
  39. package/dist/app/services/research-session-interactive-service.js.map +1 -0
  40. package/dist/app/services/research-session-launch-service.d.ts +46 -0
  41. package/dist/app/services/research-session-launch-service.js +389 -0
  42. package/dist/app/services/research-session-launch-service.js.map +1 -0
  43. package/dist/app/services/research-session-orchestrator-service.d.ts +140 -0
  44. package/dist/app/services/research-session-orchestrator-service.js +614 -0
  45. package/dist/app/services/research-session-orchestrator-service.js.map +1 -0
  46. package/dist/app/services/research-session-recovery-service.d.ts +30 -0
  47. package/dist/app/services/research-session-recovery-service.js +110 -0
  48. package/dist/app/services/research-session-recovery-service.js.map +1 -0
  49. package/dist/app/services/research-session-wizard-controller.d.ts +51 -0
  50. package/dist/app/services/research-session-wizard-controller.js +220 -0
  51. package/dist/app/services/research-session-wizard-controller.js.map +1 -0
  52. package/dist/app/services/run-cycle-service.d.ts +2 -0
  53. package/dist/app/services/run-cycle-service.js +2 -0
  54. package/dist/app/services/run-cycle-service.js.map +1 -1
  55. package/dist/cli/commands/inspect.js +2 -0
  56. package/dist/cli/commands/inspect.js.map +1 -1
  57. package/dist/cli/commands/launch.d.ts +16 -0
  58. package/dist/cli/commands/launch.js +68 -0
  59. package/dist/cli/commands/launch.js.map +1 -0
  60. package/dist/cli/commands/proposer-display.d.ts +2 -0
  61. package/dist/cli/commands/proposer-display.js +18 -0
  62. package/dist/cli/commands/proposer-display.js.map +1 -0
  63. package/dist/cli/commands/resume.d.ts +14 -0
  64. package/dist/cli/commands/resume.js +134 -0
  65. package/dist/cli/commands/resume.js.map +1 -0
  66. package/dist/cli/commands/run.d.ts +1 -1
  67. package/dist/cli/commands/run.js +2 -2
  68. package/dist/cli/commands/run.js.map +1 -1
  69. package/dist/cli/commands/status.js +4 -0
  70. package/dist/cli/commands/status.js.map +1 -1
  71. package/dist/cli/main.js +2 -29
  72. package/dist/cli/main.js.map +1 -1
  73. package/dist/cli/program.d.ts +15 -0
  74. package/dist/cli/program.js +54 -0
  75. package/dist/cli/program.js.map +1 -0
  76. package/dist/cli/tui/research-session-shell.d.ts +22 -0
  77. package/dist/cli/tui/research-session-shell.js +719 -0
  78. package/dist/cli/tui/research-session-shell.js.map +1 -0
  79. package/dist/core/engine/cycle-runner.d.ts +4 -0
  80. package/dist/core/engine/cycle-runner.js +15 -9
  81. package/dist/core/engine/cycle-runner.js.map +1 -1
  82. package/dist/core/manifest/admission.d.ts +1 -0
  83. package/dist/core/manifest/admission.js +50 -0
  84. package/dist/core/manifest/admission.js.map +1 -1
  85. package/dist/core/manifest/defaults.d.ts +21 -0
  86. package/dist/core/manifest/defaults.js +21 -0
  87. package/dist/core/manifest/defaults.js.map +1 -1
  88. package/dist/core/manifest/schema.d.ts +170 -0
  89. package/dist/core/manifest/schema.js +21 -1
  90. package/dist/core/manifest/schema.js.map +1 -1
  91. package/dist/core/model/codex-cli-cycle-session.d.ts +4 -0
  92. package/dist/core/model/codex-cli-cycle-session.js +2 -0
  93. package/dist/core/model/codex-cli-cycle-session.js.map +1 -0
  94. package/dist/core/model/codex-cli-session-lifecycle.d.ts +131 -0
  95. package/dist/core/model/codex-cli-session-lifecycle.js +237 -0
  96. package/dist/core/model/codex-cli-session-lifecycle.js.map +1 -0
  97. package/dist/core/model/codex-cli-session-outcome.d.ts +121 -0
  98. package/dist/core/model/codex-cli-session-outcome.js +70 -0
  99. package/dist/core/model/codex-cli-session-outcome.js.map +1 -0
  100. package/dist/core/model/research-project-defaults.d.ts +48 -0
  101. package/dist/core/model/research-project-defaults.js +46 -0
  102. package/dist/core/model/research-project-defaults.js.map +1 -0
  103. package/dist/core/model/research-session.d.ts +1143 -0
  104. package/dist/core/model/research-session.js +689 -0
  105. package/dist/core/model/research-session.js.map +1 -0
  106. package/dist/core/model/run-record.d.ts +56 -6
  107. package/dist/core/model/run-record.js +28 -0
  108. package/dist/core/model/run-record.js.map +1 -1
  109. package/dist/core/ports/research-project-defaults-store.d.ts +5 -0
  110. package/dist/core/ports/research-project-defaults-store.js +2 -0
  111. package/dist/core/ports/research-project-defaults-store.js.map +1 -0
  112. package/dist/core/ports/research-session-repository.d.ts +25 -0
  113. package/dist/core/ports/research-session-repository.js +2 -0
  114. package/dist/core/ports/research-session-repository.js.map +1 -0
  115. package/dist/core/state/research-session-recovery-classifier.d.ts +24 -0
  116. package/dist/core/state/research-session-recovery-classifier.js +236 -0
  117. package/dist/core/state/research-session-recovery-classifier.js.map +1 -0
  118. package/dist/core/state/research-session-resume-candidate.d.ts +8 -0
  119. package/dist/core/state/research-session-resume-candidate.js +62 -0
  120. package/dist/core/state/research-session-resume-candidate.js.map +1 -0
  121. package/dist/core/state/research-session-state-machine.d.ts +62 -0
  122. package/dist/core/state/research-session-state-machine.js +443 -0
  123. package/dist/core/state/research-session-state-machine.js.map +1 -0
  124. package/dist/mcp/server.d.ts +4 -0
  125. package/dist/mcp/server.js +192 -1
  126. package/dist/mcp/server.js.map +1 -1
  127. package/package.json +1 -1
package/README.md CHANGED
@@ -1,45 +1,112 @@
1
1
  # ralph-research
2
2
 
3
- Local-first runtime for recursive research improvement.
3
+ Local-first runtime for recursive research improvement over real artifacts.
4
4
 
5
- `ralph-research` runs a bounded improvement loop over a real artifact:
5
+ `ralph-research` ships an actual CLI and stdio MCP server that run a bounded loop:
6
6
 
7
- 1. define a metric
7
+ 1. load a manifest
8
8
  2. generate one candidate change
9
- 3. evaluate it
10
- 4. keep only verified improvements
9
+ 3. evaluate it with trusted signals
10
+ 4. persist the run, decision, and frontier state
11
+ 5. promote only verified improvements
11
12
 
12
- The v0.1 focus is a writing workflow that is runnable in under five minutes on a local machine.
13
+ The current product bar is reliability, not breadth. The bundled success path is the `writing` template, while the runtime itself is manifest-driven and reusable for other local workflows.
13
14
 
14
- ## Quickstart
15
+ ## Trust Signals
15
16
 
16
- ### Zero-config demo
17
+ - Actual shipped surfaces: CLI binary `rrx` and stdio MCP server
18
+ - Development verification commands: `npm test`, `npm run typecheck`, `npm run build`
19
+ - Persisted runtime evidence: runs, decisions, frontier, and lock metadata
20
+ - Recovery semantics are enforced by code and persisted state, not described only in prompts
21
+ - Supported onboarding path is intentionally narrower than the full manifest surface
22
+
23
+ ## What It Is
24
+
25
+ - A Node/TypeScript runtime with a real CLI: `rrx`
26
+ - A stdio MCP server backed by the same service layer as the CLI
27
+ - A Git-aware candidate execution loop with persisted run, decision, and frontier state
28
+ - A local-first system designed to be resumed, inspected, and trusted after interruptions
29
+
30
+ ## What It Is Not
31
+
32
+ - Not a no-config autonomous agent for arbitrary domains out of the box
33
+ - Not a hosted service
34
+ - Not a prompt-only protocol with undocumented runtime behavior
35
+ - Not broader than the shipped contract: one bundled template (`writing`) and three MCP tools
36
+
37
+ ## Quick Decision Guide
38
+
39
+ | If you want to... | Use |
40
+ | --- | --- |
41
+ | Check whether a repo is runnable | `rrx validate` then `rrx doctor` |
42
+ | Materialize the bundled example project | `rrx init --template writing` |
43
+ | Run a disposable end-to-end demo | `rrx demo writing` |
44
+ | Launch the v1 goal-driven orchestrator | `rrx "improve the holdout top-3 model"` |
45
+ | Launch the v1 goal-driven orchestrator explicitly | `rrx launch "improve the holdout top-3 model"` |
46
+ | Resume a persisted TUI research session | `rrx resume latest` |
47
+ | Execute one cycle | `rrx run --json` |
48
+ | Resume the latest recoverable run | `rrx run` |
49
+ | Force a fresh run id | `rrx run --fresh` |
50
+ | Inspect runtime and recovery state | `rrx status --json` |
51
+ | Inspect why one run was accepted or rejected | `rrx inspect <runId> --json` |
52
+ | Review the current accepted frontier | `rrx frontier --json` |
53
+ | Serve the same contract over MCP stdio | `rrx serve-mcp --stdio` |
54
+
55
+ ## Five-Minute Start
56
+
57
+ ### Option A: disposable demo
17
58
 
18
59
  ```bash
19
60
  npx ralph-research demo writing
20
61
  ```
21
62
 
22
- This creates a temporary writing repo, runs one accepted cycle, and prints the path plus the run id. The v0.1 demo supports the bundled `writing` template only.
63
+ This creates a temporary Git repo, runs one accepted cycle, and prints the temp path plus the run id.
23
64
 
24
- ### Template flow
65
+ ### Option B: initialize a local repo
25
66
 
26
67
  ```bash
27
68
  npx ralph-research init --template writing
69
+ npx ralph-research doctor
28
70
  npx ralph-research run --json
29
- npx ralph-research run --until-target --until-no-improve 3 --json
71
+ npx ralph-research status --json
30
72
  npx ralph-research inspect run-0001 --json
31
73
  ```
32
74
 
33
- This path is the v0.1 success bar: `init -> run -> inspect` should work quickly and produce an acceptance reason you can inspect. The bundled template set is currently `writing` only.
75
+ This is the current truth contract for the bundled template: `init -> run -> inspect` should succeed quickly on a local machine.
34
76
 
35
- ## Core Concepts
77
+ `rrx "goal"` now creates or refreshes the launch draft session and drops into the v1 TUI shell. Initial launch does not start an autonomous research cycle until the shell tells it to continue.
78
+
79
+ When you submit the review step, the shell materializes a real research session and hands control to the selected agent runtime. Once that interactive run returns, `launch-draft` is removed. The remaining persisted session is the only runtime record you need to inspect or resume.
80
+
81
+ Resume semantics are intentionally narrow:
36
82
 
37
- - `Manifest`: `ralph.yaml` defines the research program.
38
- - `Metric`: how candidate quality is measured.
39
- - `Frontier`: the currently accepted best candidate set.
40
- - `Ratchet`: the acceptance policy that decides whether the frontier advances.
41
- - `Proposer`: how a bounded candidate change is generated.
42
- - `Judge`: how qualitative outputs are compared when numeric metrics are not enough.
83
+ - `rrx resume <sessionId>` only works for sessions that ended after a completed cycle checkpoint
84
+ - interrupted sessions are resumable because the runtime has durable evidence for the next cycle boundary
85
+ - a clean agent exit without `goal_achieved` or a completed checkpoint is treated as terminal and is not resumable
86
+
87
+ ## Runtime Model
88
+
89
+ The runtime is manifest-driven. `ralph.yaml` defines the project, proposer, experiment, metrics, ratchet, and storage root. The service layer then:
90
+
91
+ - loads and validates the manifest
92
+ - acquires a durable lock
93
+ - classifies recovery against the latest persisted run
94
+ - executes or resumes a candidate
95
+ - writes run, decision, and frontier state under the storage root
96
+
97
+ See [docs/operation-model.md](docs/operation-model.md) for the full lifecycle and recovery model.
98
+
99
+ ## Current Scope
100
+
101
+ - Bundled template: `writing`
102
+ - Default template metric: local command metric, no API key required
103
+ - Optional judge path: pairwise LLM judge packs
104
+ - MCP tools:
105
+ - `run_research_cycle`
106
+ - `get_research_status`
107
+ - `get_frontier`
108
+
109
+ The runtime supports broader manifests than the bundled template demonstrates, but the shipped onboarding path is intentionally narrow until those flows are equally reliable.
43
110
 
44
111
  ## Writing Template
45
112
 
@@ -49,19 +116,51 @@ The bundled writing template is self-contained:
49
116
  - `scripts/propose.mjs`: bounded rewrite
50
117
  - `scripts/experiment.mjs`: output materialization
51
118
  - `scripts/metric.mjs`: local heuristic metric
52
- - `prompts/judge.md`: pairwise judge prompt you can upgrade to later
119
+ - `prompts/judge.md`: pairwise judge prompt starter
120
+
121
+ `templates/writing/ralph.yaml` uses a local command metric by default, so the first run works without model credentials.
122
+
123
+ ## Progressive Runs
53
124
 
54
- The default template uses a local command metric so the first run does not require API keys. When you are ready, replace the numeric metric with an `llm_judge` extractor and use the included pairwise prompt as a starting point.
125
+ `rrx run` executes one cycle by default and auto-resumes the latest recoverable run when one exists.
126
+
127
+ Progressive stop modes are opt-in:
128
+
129
+ - `--fresh`: start a new `runId` instead of auto-resuming the latest recoverable run
130
+ - `--until-target`: keep iterating until `manifest.stopping.target` is met
131
+ - `--until-no-improve N`: stop after `N` consecutive cycles without frontier improvement
132
+ - `--cycles N` with a progressive flag: treat `N` as a max-cycle cap instead of an exact count
133
+
134
+ The bundled `writing` template ships with `stopping.target` commented out, so enable that block in `ralph.yaml` before using `--until-target`.
135
+
136
+ ```bash
137
+ npx ralph-research run --until-target --until-no-improve 3 --json
138
+ ```
139
+
140
+ ## More Docs
141
+
142
+ - [docs/operation-model.md](docs/operation-model.md): lifecycle, persisted state, recovery classes
143
+ - [docs/playbook.md](docs/playbook.md): situation-to-command operator guide
144
+ - [docs/examples.md](docs/examples.md): quickstart and manifest examples pulled from shipped templates and fixtures
145
+ - [docs/examples-catalog.md](docs/examples-catalog.md): broader scenario catalog grounded in shipped templates and test fixtures
146
+ - [docs/comparison.md](docs/comparison.md): why this runtime is narrower and more stateful than prompt-only loop systems
147
+ - [docs/faq.md](docs/faq.md): common runtime, recovery, and inspection questions
148
+ - [docs/knowledge/INDEX.md](docs/knowledge/INDEX.md): project knowledge log
55
149
 
56
150
  ## CLI
57
151
 
58
152
  ```text
153
+ rrx "improve the holdout top-3 model"
154
+ rrx launch "improve the holdout top-3 model"
155
+ rrx resume latest
59
156
  rrx validate
60
157
  rrx doctor
61
158
  rrx init --template writing
62
159
  rrx demo writing
63
160
  rrx run
161
+ rrx run --fresh
64
162
  rrx run --until-target
163
+ rrx run --until-no-improve 3
65
164
  rrx run --until-target --until-no-improve 3
66
165
  rrx status
67
166
  rrx frontier
@@ -71,87 +170,14 @@ rrx reject <runId>
71
170
  rrx serve-mcp --stdio
72
171
  ```
73
172
 
74
- `rrx run --cycles N` still executes a finite loop. Progressive modes are opt-in:
75
-
76
- - `--until-target`: keep iterating until `manifest.stopping.target` is satisfied
77
- - `--until-no-improve N`: stop after `N` consecutive cycles without a frontier improvement
78
- - `--cycles N` with a progressive flag: treat `N` as a max-cycle cap instead of an exact count
79
-
80
- `rrx status` now reports both the persisted latest run snapshot and the runtime view derived from the lock heartbeat, so `running (alive)` is distinguished from `stale (resumable)` and the output includes heartbeat and last-progress timestamps when available.
81
-
82
- ## Stopping Targets
83
-
84
- Use `stopping.target` when the workflow contract is "keep going until metric X reaches threshold Y":
85
-
86
- ```yaml
87
- metrics:
88
- catalog:
89
- - id: exact_rate
90
- kind: numeric
91
- direction: maximize
92
- extractor:
93
- type: command
94
- command: "python scripts/metric.py"
95
- parser: plain_number
96
-
97
- frontier:
98
- strategy: single_best
99
- primaryMetric: exact_rate
100
-
101
- ratchet:
102
- type: epsilon_improve
103
- metric: exact_rate
104
- epsilon: 0
105
-
106
- stopping:
107
- target:
108
- metric: exact_rate
109
- op: ">="
110
- value: 0.8
111
- ```
112
-
113
- ## Metric Diagnostics
114
-
115
- If a metric script can explain why a candidate was zeroed or downgraded, prefer JSON output plus `parser: json_path` so the reason survives into `run`, `decision`, and `inspect` output:
116
-
117
- ```yaml
118
- metrics:
119
- catalog:
120
- - id: exact_rate
121
- kind: numeric
122
- direction: maximize
123
- extractor:
124
- type: command
125
- command: "python scripts/metric.py"
126
- parser: json_path
127
- valuePath: $.value
128
- ```
129
-
130
- ```json
131
- {
132
- "value": 0,
133
- "metricId": "overfit_safe_exact_rate",
134
- "reasons": ["all_missing_features", "normalized_order_leak"]
135
- }
136
- ```
137
-
138
- When `project.workspace=git`, rrx now warns if proposer, experiment, or metric command files are dirty in the working tree, because detached candidate worktrees only see committed baseline content.
139
-
140
- ## MCP
141
-
142
- The bundled MCP server currently supports stdio transport and exposes three thin tools backed by the same service layer as the CLI:
143
-
144
- - `run_research_cycle`
145
- - `get_research_status`
146
- - `get_frontier`
147
-
148
- ## Design Principles
173
+ ## Core Concepts
149
174
 
150
- - local-first execution
151
- - bounded changes
152
- - recoverable state transitions
153
- - trusted signal before automation
154
- - inspectable accept/reject decisions
175
+ - `Manifest`: `ralph.yaml` defines the research program
176
+ - `Metric`: how candidate quality is measured
177
+ - `Frontier`: the currently accepted best candidate set
178
+ - `Ratchet`: the acceptance policy that decides whether the frontier advances
179
+ - `Proposer`: how a bounded candidate change is generated
180
+ - `Judge`: how qualitative outputs are compared when numeric metrics are not enough
155
181
 
156
182
  ## Development
157
183
 
@@ -0,0 +1,8 @@
1
+ import { type ResearchProjectDefaultsRecord } from "../../core/model/research-project-defaults.js";
2
+ import type { ResearchProjectDefaultsStore } from "../../core/ports/research-project-defaults-store.js";
3
+ export declare class JsonFileResearchProjectDefaultsStore implements ResearchProjectDefaultsStore {
4
+ private readonly filePath;
5
+ constructor(filePath: string);
6
+ save(record: ResearchProjectDefaultsRecord): Promise<void>;
7
+ load(): Promise<ResearchProjectDefaultsRecord | null>;
8
+ }
@@ -0,0 +1,30 @@
1
+ import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
2
+ import { dirname, resolve } from "node:path";
3
+ import { researchProjectDefaultsRecordSchema, } from "../../core/model/research-project-defaults.js";
4
+ import { isMissingFileError } from "../../shared/fs-errors.js";
5
+ export class JsonFileResearchProjectDefaultsStore {
6
+ filePath;
7
+ constructor(filePath) {
8
+ this.filePath = resolve(filePath);
9
+ }
10
+ async save(record) {
11
+ const parsed = researchProjectDefaultsRecordSchema.parse(record);
12
+ const tempPath = `${this.filePath}.tmp-${process.pid}-${Date.now()}`;
13
+ await mkdir(dirname(this.filePath), { recursive: true });
14
+ await writeFile(tempPath, `${JSON.stringify(parsed, null, 2)}\n`, "utf8");
15
+ await rename(tempPath, this.filePath);
16
+ }
17
+ async load() {
18
+ try {
19
+ const raw = await readFile(this.filePath, "utf8");
20
+ return researchProjectDefaultsRecordSchema.parse(JSON.parse(raw));
21
+ }
22
+ catch (error) {
23
+ if (isMissingFileError(error)) {
24
+ return null;
25
+ }
26
+ throw error;
27
+ }
28
+ }
29
+ }
30
+ //# sourceMappingURL=json-file-research-project-defaults-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"json-file-research-project-defaults-store.js","sourceRoot":"","sources":["../../../src/adapters/fs/json-file-research-project-defaults-store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACtE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAE7C,OAAO,EACL,mCAAmC,GAEpC,MAAM,+CAA+C,CAAC;AAEvD,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAE/D,MAAM,OAAO,oCAAoC;IAC9B,QAAQ,CAAS;IAElC,YAAmB,QAAgB;QACjC,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;IACpC,CAAC;IAEM,KAAK,CAAC,IAAI,CAAC,MAAqC;QACrD,MAAM,MAAM,GAAG,mCAAmC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QACjE,MAAM,QAAQ,GAAG,GAAG,IAAI,CAAC,QAAQ,QAAQ,OAAO,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC;QAErE,MAAM,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACzD,MAAM,SAAS,CAAC,QAAQ,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QAC1E,MAAM,MAAM,CAAC,QAAQ,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;IACxC,CAAC;IAEM,KAAK,CAAC,IAAI;QACf,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;YAClD,OAAO,mCAAmC,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC;QACpE,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,kBAAkB,CAAC,KAAK,CAAC,EAAE,CAAC;gBAC9B,OAAO,IAAI,CAAC;YACd,CAAC;YACD,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC;CACF"}
@@ -0,0 +1,24 @@
1
+ import { type ResearchSessionMetadata, type ResearchSessionRecord } from "../../core/model/research-session.js";
2
+ import type { PersistedResearchSessionBundle, ResearchSessionQuery, ResearchSessionRepository } from "../../core/ports/research-session-repository.js";
3
+ export declare class JsonFileResearchSessionRepository implements ResearchSessionRepository {
4
+ private readonly sessionsRoot;
5
+ constructor(sessionsRoot: string);
6
+ saveSession(record: ResearchSessionRecord): Promise<void>;
7
+ loadSession(sessionId: string): Promise<ResearchSessionRecord | null>;
8
+ deleteSession(sessionId: string): Promise<void>;
9
+ loadSessionMetadata(sessionId: string): Promise<ResearchSessionMetadata | null>;
10
+ loadPersistedSession(sessionId: string): Promise<PersistedResearchSessionBundle | null>;
11
+ querySessions(query?: ResearchSessionQuery): Promise<ResearchSessionRecord[]>;
12
+ querySessionMetadata(query?: ResearchSessionQuery): Promise<ResearchSessionMetadata[]>;
13
+ private loadAllSessions;
14
+ private loadAllSessionMetadata;
15
+ private listSessionDirectories;
16
+ private getPath;
17
+ private getLifecyclePath;
18
+ private getSessionsRoot;
19
+ private readSessionRecord;
20
+ private readSessionMetadata;
21
+ private readLifecycleRecord;
22
+ private assertDirectoryMatchesSession;
23
+ private assertLifecycleMatchesSession;
24
+ }
@@ -0,0 +1,199 @@
1
+ import { mkdir, readFile, readdir, rename, rm, stat, writeFile } from "node:fs/promises";
2
+ import { dirname, join, resolve } from "node:path";
3
+ import { parseCodexCliSessionLifecycleRecord, } from "../../core/model/codex-cli-session-lifecycle.js";
4
+ import { parsePersistedResearchSessionMetadata, researchSessionRecordSchema, } from "../../core/model/research-session.js";
5
+ import { isMissingFileError } from "../../shared/fs-errors.js";
6
+ export class JsonFileResearchSessionRepository {
7
+ sessionsRoot;
8
+ constructor(sessionsRoot) {
9
+ this.sessionsRoot = resolve(sessionsRoot);
10
+ }
11
+ async saveSession(record) {
12
+ const parsed = researchSessionRecordSchema.parse(record);
13
+ const path = this.getPath(parsed.sessionId);
14
+ const tempPath = `${path}.tmp-${process.pid}-${Date.now()}`;
15
+ await mkdir(dirname(path), { recursive: true });
16
+ await writeFile(tempPath, `${JSON.stringify(parsed, null, 2)}\n`, "utf8");
17
+ await rename(tempPath, path);
18
+ }
19
+ async loadSession(sessionId) {
20
+ return this.readSessionRecord(sessionId);
21
+ }
22
+ async deleteSession(sessionId) {
23
+ const path = join(this.getSessionsRoot(), sessionId);
24
+ await rm(path, { recursive: true, force: true });
25
+ }
26
+ async loadSessionMetadata(sessionId) {
27
+ return this.readSessionMetadata(sessionId);
28
+ }
29
+ async loadPersistedSession(sessionId) {
30
+ const session = await this.readSessionRecord(sessionId);
31
+ if (!session) {
32
+ return null;
33
+ }
34
+ const lifecyclePath = this.getLifecyclePath(sessionId);
35
+ const lifecycle = await this.readLifecycleRecord(sessionId);
36
+ return {
37
+ session,
38
+ lifecycle,
39
+ codexSessionReference: lifecycle
40
+ ? {
41
+ codexSessionId: lifecycle.identity.codexSessionId,
42
+ lifecyclePath,
43
+ }
44
+ : null,
45
+ };
46
+ }
47
+ async querySessions(query = {}) {
48
+ const records = await this.loadAllSessions();
49
+ const filtered = records.filter((record) => {
50
+ if (query.workingDirectory && record.workingDirectory !== query.workingDirectory) {
51
+ return false;
52
+ }
53
+ if (query.statuses && !query.statuses.includes(record.status)) {
54
+ return false;
55
+ }
56
+ return true;
57
+ });
58
+ if (query.limit === undefined) {
59
+ return filtered;
60
+ }
61
+ return filtered.slice(0, query.limit);
62
+ }
63
+ async querySessionMetadata(query = {}) {
64
+ const records = await this.loadAllSessionMetadata();
65
+ const filtered = records.filter((record) => {
66
+ if (query.workingDirectory && record.workingDirectory !== query.workingDirectory) {
67
+ return false;
68
+ }
69
+ if (query.statuses && !query.statuses.includes(record.status)) {
70
+ return false;
71
+ }
72
+ return true;
73
+ });
74
+ if (query.limit === undefined) {
75
+ return filtered;
76
+ }
77
+ return filtered.slice(0, query.limit);
78
+ }
79
+ async loadAllSessions() {
80
+ const directories = await this.listSessionDirectories();
81
+ const records = [];
82
+ for (const directoryName of directories) {
83
+ const record = await this.readSessionRecord(directoryName);
84
+ if (!record) {
85
+ continue;
86
+ }
87
+ this.assertDirectoryMatchesSession(record, directoryName);
88
+ records.push(record);
89
+ }
90
+ return records.sort((left, right) => left.sessionId.localeCompare(right.sessionId));
91
+ }
92
+ async loadAllSessionMetadata() {
93
+ const directories = await this.listSessionDirectories();
94
+ const records = [];
95
+ for (const directoryName of directories) {
96
+ const record = await this.readSessionMetadata(directoryName);
97
+ if (!record) {
98
+ continue;
99
+ }
100
+ this.assertDirectoryMatchesSession(record, directoryName);
101
+ records.push(record);
102
+ }
103
+ return records.sort((left, right) => left.sessionId.localeCompare(right.sessionId));
104
+ }
105
+ async listSessionDirectories() {
106
+ try {
107
+ const entries = await readdir(this.getSessionsRoot(), { withFileTypes: true });
108
+ const directories = [];
109
+ for (const entry of entries) {
110
+ if (!entry.isDirectory()) {
111
+ continue;
112
+ }
113
+ const path = join(this.getSessionsRoot(), entry.name, "session.json");
114
+ const fileStats = await stat(path).catch((error) => {
115
+ if (isMissingFileError(error)) {
116
+ return null;
117
+ }
118
+ throw error;
119
+ });
120
+ if (!fileStats?.isFile()) {
121
+ continue;
122
+ }
123
+ directories.push(entry.name);
124
+ }
125
+ return directories.sort((left, right) => left.localeCompare(right));
126
+ }
127
+ catch (error) {
128
+ if (isMissingFileError(error)) {
129
+ return [];
130
+ }
131
+ throw error;
132
+ }
133
+ }
134
+ getPath(sessionId) {
135
+ return join(this.getSessionsRoot(), sessionId, "session.json");
136
+ }
137
+ getLifecyclePath(sessionId) {
138
+ return join(this.getSessionsRoot(), sessionId, "codex-session.json");
139
+ }
140
+ getSessionsRoot() {
141
+ return this.sessionsRoot;
142
+ }
143
+ async readSessionRecord(sessionId) {
144
+ const path = this.getPath(sessionId);
145
+ try {
146
+ const raw = await readFile(path, "utf8");
147
+ const record = researchSessionRecordSchema.parse(JSON.parse(raw));
148
+ this.assertDirectoryMatchesSession(record, sessionId);
149
+ return record;
150
+ }
151
+ catch (error) {
152
+ if (isMissingFileError(error)) {
153
+ return null;
154
+ }
155
+ throw error;
156
+ }
157
+ }
158
+ async readSessionMetadata(sessionId) {
159
+ const path = this.getPath(sessionId);
160
+ try {
161
+ const raw = await readFile(path, "utf8");
162
+ const record = parsePersistedResearchSessionMetadata(JSON.parse(raw));
163
+ this.assertDirectoryMatchesSession(record, sessionId);
164
+ return record;
165
+ }
166
+ catch (error) {
167
+ if (isMissingFileError(error)) {
168
+ return null;
169
+ }
170
+ throw error;
171
+ }
172
+ }
173
+ async readLifecycleRecord(sessionId) {
174
+ const path = this.getLifecyclePath(sessionId);
175
+ try {
176
+ const raw = await readFile(path, "utf8");
177
+ const record = parseCodexCliSessionLifecycleRecord(raw);
178
+ this.assertLifecycleMatchesSession(record, sessionId);
179
+ return record;
180
+ }
181
+ catch (error) {
182
+ if (isMissingFileError(error)) {
183
+ return null;
184
+ }
185
+ throw error;
186
+ }
187
+ }
188
+ assertDirectoryMatchesSession(record, directoryName) {
189
+ if (record.sessionId !== directoryName) {
190
+ throw new Error(`Research session directory name "${directoryName}" must match record sessionId "${record.sessionId}"`);
191
+ }
192
+ }
193
+ assertLifecycleMatchesSession(record, directoryName) {
194
+ if (record.sessionId !== directoryName) {
195
+ throw new Error(`Research session directory name "${directoryName}" must match lifecycle sessionId "${record.sessionId}"`);
196
+ }
197
+ }
198
+ }
199
+ //# sourceMappingURL=json-file-research-session-repository.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"json-file-research-session-repository.js","sourceRoot":"","sources":["../../../src/adapters/fs/json-file-research-session-repository.ts"],"names":[],"mappings":"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"}
@@ -2,7 +2,7 @@ import { readFile } from "node:fs/promises";
2
2
  import { resolve } from "node:path";
3
3
  import { parse } from "yaml";
4
4
  import { ZodError } from "zod";
5
- import { compileManifestAdmission } from "../../core/manifest/admission.js";
5
+ import { compileManifestAdmission, compileRawManifestAdmission } from "../../core/manifest/admission.js";
6
6
  import { DEFAULT_MANIFEST_FILENAME, RalphManifestSchema } from "../../core/manifest/schema.js";
7
7
  export class ManifestLoadError extends Error {
8
8
  causeValue;
@@ -29,6 +29,13 @@ export async function loadManifestFromFile(path = DEFAULT_MANIFEST_FILENAME, opt
29
29
  throw new ManifestLoadError(`Failed to parse YAML from ${resolvedPath}`, error);
30
30
  }
31
31
  try {
32
+ const rawAdmissionIssues = compileRawManifestAdmission(parsedYaml);
33
+ if (rawAdmissionIssues.length > 0) {
34
+ throw new ManifestLoadError(`Manifest admission failed for ${resolvedPath}`, {
35
+ executable: false,
36
+ issues: rawAdmissionIssues,
37
+ });
38
+ }
32
39
  const manifest = RalphManifestSchema.parse(parsedYaml);
33
40
  const admission = await compileManifestAdmission(manifest, options);
34
41
  if (!admission.executable) {
@@ -1 +1 @@
1
- {"version":3,"file":"manifest-loader.js","sourceRoot":"","sources":["../../../src/adapters/fs/manifest-loader.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,OAAO,EAAE,KAAK,EAAE,MAAM,MAAM,CAAC;AAC7B,OAAO,EAAE,QAAQ,EAAE,MAAM,KAAK,CAAC;AAE/B,OAAO,EAAE,wBAAwB,EAAE,MAAM,kCAAkC,CAAC;AAC5E,OAAO,EAAE,yBAAyB,EAAE,mBAAmB,EAAsB,MAAM,+BAA+B,CAAC;AAQnH,MAAM,OAAO,iBAAkB,SAAQ,KAAK;IAC1B,UAAU,CAAW;IAErC,YAAmB,OAAe,EAAE,UAAoB;QACtD,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,mBAAmB,CAAC;QAChC,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;IAC/B,CAAC;CACF;AAMD,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,IAAI,GAAG,yBAAyB,EAChC,UAA+B,EAAE;IAEjC,MAAM,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEnC,IAAI,OAAe,CAAC;IACpB,IAAI,CAAC;QACH,OAAO,GAAG,MAAM,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC;IACjD,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,IAAI,iBAAiB,CAAC,8BAA8B,YAAY,EAAE,EAAE,KAAK,CAAC,CAAC;IACnF,CAAC;IAED,IAAI,UAAmB,CAAC;IACxB,IAAI,CAAC;QACH,UAAU,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC;IAC9B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,IAAI,iBAAiB,CAAC,6BAA6B,YAAY,EAAE,EAAE,KAAK,CAAC,CAAC;IAClF,CAAC;IAED,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,mBAAmB,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;QACvD,MAAM,SAAS,GAAG,MAAM,wBAAwB,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QAEpE,IAAI,CAAC,SAAS,CAAC,UAAU,EAAE,CAAC;YAC1B,MAAM,IAAI,iBAAiB,CAAC,iCAAiC,YAAY,EAAE,EAAE;gBAC3E,UAAU,EAAE,KAAK;gBACjB,MAAM,EAAE,SAAS,CAAC,MAAM;aACzB,CAAC,CAAC;QACL,CAAC;QAED,OAAO;YACL,IAAI,EAAE,YAAY;YAClB,QAAQ;YACR,mBAAmB,EAAE,SAAS,CAAC,mBAAmB;SACnD,CAAC;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,KAAK,YAAY,iBAAiB,EAAE,CAAC;YACvC,MAAM,KAAK,CAAC;QACd,CAAC;QAED,IAAI,KAAK,YAAY,QAAQ,EAAE,CAAC;YAC9B,MAAM,IAAI,iBAAiB,CAAC,kCAAkC,YAAY,EAAE,EAAE,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;QACjG,CAAC;QAED,MAAM,IAAI,iBAAiB,CAAC,kCAAkC,YAAY,EAAE,EAAE,KAAK,CAAC,CAAC;IACvF,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"manifest-loader.js","sourceRoot":"","sources":["../../../src/adapters/fs/manifest-loader.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,OAAO,EAAE,KAAK,EAAE,MAAM,MAAM,CAAC;AAC7B,OAAO,EAAE,QAAQ,EAAE,MAAM,KAAK,CAAC;AAE/B,OAAO,EAAE,wBAAwB,EAAE,2BAA2B,EAAE,MAAM,kCAAkC,CAAC;AACzG,OAAO,EAAE,yBAAyB,EAAE,mBAAmB,EAAsB,MAAM,+BAA+B,CAAC;AAQnH,MAAM,OAAO,iBAAkB,SAAQ,KAAK;IAC1B,UAAU,CAAW;IAErC,YAAmB,OAAe,EAAE,UAAoB;QACtD,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,mBAAmB,CAAC;QAChC,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;IAC/B,CAAC;CACF;AAMD,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,IAAI,GAAG,yBAAyB,EAChC,UAA+B,EAAE;IAEjC,MAAM,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEnC,IAAI,OAAe,CAAC;IACpB,IAAI,CAAC;QACH,OAAO,GAAG,MAAM,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC;IACjD,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,IAAI,iBAAiB,CAAC,8BAA8B,YAAY,EAAE,EAAE,KAAK,CAAC,CAAC;IACnF,CAAC;IAED,IAAI,UAAmB,CAAC;IACxB,IAAI,CAAC;QACH,UAAU,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC;IAC9B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,IAAI,iBAAiB,CAAC,6BAA6B,YAAY,EAAE,EAAE,KAAK,CAAC,CAAC;IAClF,CAAC;IAED,IAAI,CAAC;QACH,MAAM,kBAAkB,GAAG,2BAA2B,CAAC,UAAU,CAAC,CAAC;QACnE,IAAI,kBAAkB,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAClC,MAAM,IAAI,iBAAiB,CAAC,iCAAiC,YAAY,EAAE,EAAE;gBAC3E,UAAU,EAAE,KAAK;gBACjB,MAAM,EAAE,kBAAkB;aAC3B,CAAC,CAAC;QACL,CAAC;QAED,MAAM,QAAQ,GAAG,mBAAmB,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;QACvD,MAAM,SAAS,GAAG,MAAM,wBAAwB,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QAEpE,IAAI,CAAC,SAAS,CAAC,UAAU,EAAE,CAAC;YAC1B,MAAM,IAAI,iBAAiB,CAAC,iCAAiC,YAAY,EAAE,EAAE;gBAC3E,UAAU,EAAE,KAAK;gBACjB,MAAM,EAAE,SAAS,CAAC,MAAM;aACzB,CAAC,CAAC;QACL,CAAC;QAED,OAAO;YACL,IAAI,EAAE,YAAY;YAClB,QAAQ;YACR,mBAAmB,EAAE,SAAS,CAAC,mBAAmB;SACnD,CAAC;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,KAAK,YAAY,iBAAiB,EAAE,CAAC;YACvC,MAAM,KAAK,CAAC;QACd,CAAC;QAED,IAAI,KAAK,YAAY,QAAQ,EAAE,CAAC;YAC9B,MAAM,IAAI,iBAAiB,CAAC,kCAAkC,YAAY,EAAE,EAAE,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;QACjG,CAAC;QAED,MAAM,IAAI,iBAAiB,CAAC,kCAAkC,YAAY,EAAE,EAAE,KAAK,CAAC,CAAC;IACvF,CAAC;AACH,CAAC"}
@@ -0,0 +1,16 @@
1
+ import type { CodexCliProposerConfig } from "../../core/manifest/schema.js";
2
+ import { CodexCliSessionManager } from "./codex-cli-session-manager.js";
3
+ import type { ProposalExecutionInput, ProposalExecutionResult, ProposerRunner } from "./proposer-factory.js";
4
+ export interface CodexCliSessionProposerDependencies {
5
+ createSessionManager?: () => CodexCliSessionManager;
6
+ createSessionId?: () => string;
7
+ now?: () => Date;
8
+ }
9
+ export declare class CodexCliSessionProposer implements ProposerRunner {
10
+ private readonly config;
11
+ private readonly sessionManager;
12
+ private readonly createSessionId;
13
+ private readonly now;
14
+ constructor(config: CodexCliProposerConfig, dependencies?: CodexCliSessionProposerDependencies);
15
+ run(input: ProposalExecutionInput): Promise<ProposalExecutionResult>;
16
+ }