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
@@ -0,0 +1,689 @@
1
+ import { z } from "zod";
2
+ import { DEFAULT_ALLOWED_GLOBS, DEFAULT_CODEX_CLI_APPROVAL_POLICY, DEFAULT_CODEX_CLI_SANDBOX_MODE, DEFAULT_SESSION_INSUFFICIENT_EVIDENCE_LIMIT, DEFAULT_SESSION_NO_PROGRESS_LIMIT, DEFAULT_SESSION_REPEATED_FAILURE_LIMIT, DEFAULT_TTY_SESSION_STARTUP_TIMEOUT_SEC, DEFAULT_TTY_SESSION_TURN_TIMEOUT_SEC, } from "../manifest/defaults.js";
3
+ import { codexCliSessionLifecyclePhaseSchema } from "./codex-cli-session-lifecycle.js";
4
+ export const researchSessionStatusSchema = z.enum([
5
+ "draft",
6
+ "running",
7
+ "awaiting_resume",
8
+ "halted",
9
+ "goal_achieved",
10
+ "failed",
11
+ ]);
12
+ export const RESUMABLE_RESEARCH_SESSION_STATUSES = [
13
+ "running",
14
+ "awaiting_resume",
15
+ "halted",
16
+ ];
17
+ export const researchSessionAgentSchema = z.object({
18
+ type: z.literal("codex_cli"),
19
+ command: z.string().min(1).default("codex"),
20
+ model: z.string().min(1).optional(),
21
+ approvalPolicy: z
22
+ .enum(["never", "on-failure", "on-request", "untrusted"])
23
+ .default(DEFAULT_CODEX_CLI_APPROVAL_POLICY),
24
+ sandboxMode: z
25
+ .enum(["read-only", "workspace-write", "danger-full-access"])
26
+ .default(DEFAULT_CODEX_CLI_SANDBOX_MODE),
27
+ ttySession: z
28
+ .object({
29
+ startupTimeoutSec: z.number().int().positive().default(DEFAULT_TTY_SESSION_STARTUP_TIMEOUT_SEC),
30
+ turnTimeoutSec: z.number().int().positive().default(DEFAULT_TTY_SESSION_TURN_TIMEOUT_SEC),
31
+ })
32
+ .default({
33
+ startupTimeoutSec: DEFAULT_TTY_SESSION_STARTUP_TIMEOUT_SEC,
34
+ turnTimeoutSec: DEFAULT_TTY_SESSION_TURN_TIMEOUT_SEC,
35
+ }),
36
+ });
37
+ export const researchSessionWorkspaceSchema = z.object({
38
+ strategy: z.literal("git_worktree"),
39
+ baseRef: z.string().min(1).optional(),
40
+ currentRef: z.string().min(1).optional(),
41
+ currentPath: z.string().min(1).optional(),
42
+ promoted: z.boolean().default(false),
43
+ promotedAt: z.string().datetime().optional(),
44
+ promotedRunId: z.string().min(1).optional(),
45
+ promotedDecisionId: z.string().min(1).optional(),
46
+ promotedCommitSha: z.string().min(1).optional(),
47
+ });
48
+ export const researchSessionContextSchema = z.object({
49
+ trackableGlobs: z.array(z.string().min(1)).min(1).default(DEFAULT_ALLOWED_GLOBS),
50
+ webSearch: z.boolean().default(true),
51
+ shellCommandAllowlistAdditions: z.array(z.string().min(1)).default([]),
52
+ shellCommandAllowlistRemovals: z.array(z.string().min(1)).default([]),
53
+ });
54
+ export const researchSessionStopPolicySchema = z.object({
55
+ repeatedFailures: z.number().int().min(1).default(DEFAULT_SESSION_REPEATED_FAILURE_LIMIT),
56
+ noMeaningfulProgress: z.number().int().min(1).default(DEFAULT_SESSION_NO_PROGRESS_LIMIT),
57
+ insufficientEvidence: z.number().int().min(1).default(DEFAULT_SESSION_INSUFFICIENT_EVIDENCE_LIMIT),
58
+ });
59
+ export const researchSessionGoalStepDraftSchema = z.object({
60
+ goal: z.string().optional(),
61
+ agentCommand: z.string().optional(),
62
+ repeatedFailures: z.string().optional(),
63
+ noMeaningfulProgress: z.string().optional(),
64
+ insufficientEvidence: z.string().optional(),
65
+ });
66
+ export const researchSessionAgentStepDraftSchema = z.object({
67
+ command: z.string().optional(),
68
+ model: z.string().optional(),
69
+ approvalPolicy: z.string().optional(),
70
+ sandboxMode: z.string().optional(),
71
+ startupTimeoutSec: z.string().optional(),
72
+ turnTimeoutSec: z.string().optional(),
73
+ });
74
+ export const researchSessionWorkspaceStepDraftSchema = z.object({
75
+ workingDirectory: z.string().optional(),
76
+ baseRef: z.string().optional(),
77
+ allowedGlobs: z.string().optional(),
78
+ });
79
+ export const researchSessionContextStepDraftSchema = z.object({
80
+ trackableGlobs: z.string().optional(),
81
+ webSearch: z.string().optional(),
82
+ shellCommandAllowlistAdditions: z.string().optional(),
83
+ shellCommandAllowlistRemovals: z.string().optional(),
84
+ });
85
+ export const researchSessionPermissionsFlowStateSchema = z.object({
86
+ workingDirectory: z.string().optional(),
87
+ webSearch: z.string().optional(),
88
+ shellCommandAllowlistAdditions: z.string().optional(),
89
+ shellCommandAllowlistRemovals: z.string().optional(),
90
+ approvalPolicy: z.string().optional(),
91
+ sandboxMode: z.string().optional(),
92
+ });
93
+ export const researchSessionStopRulesFlowStateSchema = z.object({
94
+ repeatedFailures: z.string().optional(),
95
+ noMeaningfulProgress: z.string().optional(),
96
+ insufficientEvidence: z.string().optional(),
97
+ });
98
+ export const researchSessionOutputsFlowStateSchema = z.object({
99
+ goal: z.string().optional(),
100
+ trackableGlobs: z.string().optional(),
101
+ baseRef: z.string().optional(),
102
+ agentCommand: z.string().optional(),
103
+ model: z.string().optional(),
104
+ startupTimeoutSec: z.string().optional(),
105
+ turnTimeoutSec: z.string().optional(),
106
+ });
107
+ export const researchSessionReviewSummaryFieldSchema = z.object({
108
+ label: z.string().min(1),
109
+ value: z.string(),
110
+ });
111
+ export const researchSessionReviewSectionFlowStateSchema = z.object({
112
+ index: z.string().min(1),
113
+ label: z.string().min(1),
114
+ step: z.enum(["permissions", "stopRules", "outputs"]),
115
+ fields: z.array(researchSessionReviewSummaryFieldSchema).default([]),
116
+ });
117
+ export const researchSessionReviewFlowStateSchema = z.object({
118
+ sections: z.array(researchSessionReviewSectionFlowStateSchema).default([]),
119
+ });
120
+ export const researchSessionFlowStateSchema = z.object({
121
+ permissions: researchSessionPermissionsFlowStateSchema.optional(),
122
+ stopRules: researchSessionStopRulesFlowStateSchema.optional(),
123
+ outputs: researchSessionOutputsFlowStateSchema.optional(),
124
+ review: researchSessionReviewFlowStateSchema.optional(),
125
+ });
126
+ export const researchSessionDraftStepSchema = z.enum([
127
+ "permissions",
128
+ "stopRules",
129
+ "outputs",
130
+ "review",
131
+ ]);
132
+ export const researchSessionDraftStateSchema = z.object({
133
+ currentStep: researchSessionDraftStepSchema.default("permissions"),
134
+ completedSteps: z.array(researchSessionDraftStepSchema).default([]),
135
+ returnToReview: z.boolean().default(false),
136
+ reviewConfirmed: z.boolean().default(false),
137
+ flowState: researchSessionFlowStateSchema.optional(),
138
+ goalStep: researchSessionGoalStepDraftSchema.optional(),
139
+ contextStep: researchSessionContextStepDraftSchema.optional(),
140
+ workspaceStep: researchSessionWorkspaceStepDraftSchema.optional(),
141
+ agentStep: researchSessionAgentStepDraftSchema.optional(),
142
+ });
143
+ export const researchSessionCycleOutcomeSchema = z.enum([
144
+ "accepted",
145
+ "rejected",
146
+ "needs_human",
147
+ "failed",
148
+ ]);
149
+ export const researchSessionProgressSignalSchema = z.object({
150
+ cycle: z.number().int().positive(),
151
+ outcome: researchSessionCycleOutcomeSchema,
152
+ changedFileCount: z.number().int().nonnegative().default(0),
153
+ diffLineCount: z.number().int().nonnegative().default(0),
154
+ repeatedDiff: z.boolean().default(false),
155
+ verificationDelta: z.number().optional(),
156
+ newArtifacts: z.array(z.string().min(1)).default([]),
157
+ meaningfulProgress: z.boolean(),
158
+ insufficientEvidence: z.boolean().default(false),
159
+ agentTieBreakerUsed: z.boolean().default(false),
160
+ agentSummary: z.string().min(1).optional(),
161
+ reasons: z.array(z.string().min(1)).default([]),
162
+ });
163
+ export const researchSessionProgressSchema = z.object({
164
+ completedCycles: z.number().int().nonnegative().default(0),
165
+ nextCycle: z.number().int().positive().default(1),
166
+ latestRunId: z.string().min(1).optional(),
167
+ latestDecisionId: z.string().min(1).optional(),
168
+ latestFrontierIds: z.array(z.string().min(1)).default([]),
169
+ repeatedFailureStreak: z.number().int().nonnegative().default(0),
170
+ noMeaningfulProgressStreak: z.number().int().nonnegative().default(0),
171
+ insufficientEvidenceStreak: z.number().int().nonnegative().default(0),
172
+ lastMeaningfulProgressCycle: z.number().int().positive().optional(),
173
+ lastCheckpointAt: z.string().datetime().optional(),
174
+ lastSignals: researchSessionProgressSignalSchema.optional(),
175
+ });
176
+ export const researchSessionStopConditionSchema = z.discriminatedUnion("type", [
177
+ z.object({
178
+ type: z.literal("none"),
179
+ }),
180
+ z.object({
181
+ type: z.literal("goal_achieved"),
182
+ summary: z.string().min(1),
183
+ achievedAtCycle: z.number().int().positive(),
184
+ }),
185
+ z.object({
186
+ type: z.literal("repeated_failures"),
187
+ count: z.number().int().min(1),
188
+ threshold: z.number().int().min(1),
189
+ }),
190
+ z.object({
191
+ type: z.literal("no_meaningful_progress"),
192
+ count: z.number().int().min(1),
193
+ threshold: z.number().int().min(1),
194
+ }),
195
+ z.object({
196
+ type: z.literal("insufficient_evidence"),
197
+ count: z.number().int().min(1),
198
+ threshold: z.number().int().min(1),
199
+ }),
200
+ z.object({
201
+ type: z.literal("operator_stop"),
202
+ note: z.string().min(1).optional(),
203
+ }),
204
+ z.object({
205
+ type: z.literal("unrecoverable_error"),
206
+ message: z.string().min(1),
207
+ stack: z.string().min(1).optional(),
208
+ }),
209
+ ]);
210
+ export const researchSessionResumeSchema = z.object({
211
+ resumable: z.boolean().default(true),
212
+ checkpointType: z.literal("completed_cycle_boundary"),
213
+ resumeFromCycle: z.number().int().positive(),
214
+ requiresUserConfirmation: z.boolean().default(false),
215
+ checkpointRunId: z.string().min(1).optional(),
216
+ checkpointDecisionId: z.string().min(1).optional(),
217
+ interruptionDetectedAt: z.string().datetime().optional(),
218
+ interruptedDuringCycle: z.number().int().positive().optional(),
219
+ note: z.string().min(1).optional(),
220
+ });
221
+ export const researchSessionTuiSelectedCandidateDecisionSchema = z.enum([
222
+ "resume",
223
+ "new_session",
224
+ ]);
225
+ export const researchSessionTuiSelectedCandidateConfirmationSchema = z.object({
226
+ required: z.literal(true),
227
+ decision: researchSessionTuiSelectedCandidateDecisionSchema.optional(),
228
+ });
229
+ export const researchSessionRecoveryClassificationSchema = z.enum([
230
+ "resumable",
231
+ "inspect_only",
232
+ "non_recoverable",
233
+ ]);
234
+ export const researchSessionRuntimeStateSchema = z.enum([
235
+ "active",
236
+ "stale",
237
+ "exited",
238
+ "missing",
239
+ ]);
240
+ export const researchSessionTuiSelectedCandidateCheckpointSchema = z.object({
241
+ completedCycles: z.number().int().nonnegative(),
242
+ latestRunId: z.string().min(1).optional(),
243
+ latestDecisionId: z.string().min(1).optional(),
244
+ lastCheckpointAt: z.string().datetime().optional(),
245
+ stopCondition: z.string().min(1),
246
+ });
247
+ export const researchSessionTuiSelectedCandidateLatestCycleSchema = z.object({
248
+ outcome: researchSessionCycleOutcomeSchema,
249
+ meaningfulProgress: z.boolean(),
250
+ insufficientEvidence: z.boolean(),
251
+ changedFileCount: z.number().int().nonnegative(),
252
+ diffLineCount: z.number().int().nonnegative(),
253
+ newArtifactCount: z.number().int().nonnegative(),
254
+ agentSummary: z.string().min(1).optional(),
255
+ });
256
+ export const researchSessionTuiSelectedCandidateRecoverySchema = z.object({
257
+ classification: researchSessionRecoveryClassificationSchema,
258
+ resumeAllowed: z.boolean(),
259
+ reason: z.string().min(1),
260
+ runtimeState: researchSessionRuntimeStateSchema,
261
+ codexPhase: codexCliSessionLifecyclePhaseSchema.optional(),
262
+ });
263
+ export const researchSessionTuiSelectedCandidateSummarySchema = z.object({
264
+ sessionId: z.string().min(1),
265
+ status: z.enum(RESUMABLE_RESEARCH_SESSION_STATUSES),
266
+ goal: z.string().min(1),
267
+ updatedAt: z.string().datetime(),
268
+ resumeFromCycle: z.number().int().positive(),
269
+ checkpoint: researchSessionTuiSelectedCandidateCheckpointSchema,
270
+ latestCycle: researchSessionTuiSelectedCandidateLatestCycleSchema.optional(),
271
+ recovery: researchSessionTuiSelectedCandidateRecoverySchema.optional(),
272
+ userConfirmation: researchSessionTuiSelectedCandidateConfirmationSchema,
273
+ });
274
+ const persistedResearchSessionMetadataSourceSchema = z.object({
275
+ sessionId: z.string().min(1),
276
+ goal: z.string().min(1),
277
+ workingDirectory: z.string().min(1),
278
+ status: researchSessionStatusSchema,
279
+ createdAt: z.string().datetime(),
280
+ updatedAt: z.string().datetime(),
281
+ progress: z.object({
282
+ completedCycles: z.number().int().nonnegative(),
283
+ lastCheckpointAt: z.string().datetime().optional(),
284
+ }),
285
+ resume: z.object({
286
+ resumeFromCycle: z.number().int().positive(),
287
+ }),
288
+ });
289
+ export const researchSessionMetadataSchema = z.object({
290
+ sessionId: z.string().min(1),
291
+ goal: z.string().min(1),
292
+ workingDirectory: z.string().min(1),
293
+ status: researchSessionStatusSchema,
294
+ createdAt: z.string().datetime(),
295
+ updatedAt: z.string().datetime(),
296
+ completedCycles: z.number().int().nonnegative(),
297
+ lastCheckpointAt: z.string().datetime().optional(),
298
+ resumeFromCycle: z.number().int().positive(),
299
+ });
300
+ export const researchSessionSubmittedSnapshotSchema = z.object({
301
+ sessionId: z.string().min(1),
302
+ goal: z.string().min(1),
303
+ workingDirectory: z.string().min(1),
304
+ status: researchSessionStatusSchema,
305
+ agent: researchSessionAgentSchema,
306
+ context: researchSessionContextSchema,
307
+ workspace: researchSessionWorkspaceSchema,
308
+ stopPolicy: researchSessionStopPolicySchema,
309
+ progress: researchSessionProgressSchema,
310
+ stopCondition: researchSessionStopConditionSchema,
311
+ resume: researchSessionResumeSchema,
312
+ draftState: researchSessionDraftStateSchema.optional(),
313
+ createdAt: z.string().datetime(),
314
+ updatedAt: z.string().datetime(),
315
+ endedAt: z.string().datetime().optional(),
316
+ });
317
+ export const researchSessionRecordSchema = z
318
+ .object({
319
+ sessionId: z.string().min(1),
320
+ goal: z.string().min(1),
321
+ workingDirectory: z.string().min(1),
322
+ status: researchSessionStatusSchema,
323
+ agent: researchSessionAgentSchema.default({
324
+ type: "codex_cli",
325
+ command: "codex",
326
+ approvalPolicy: DEFAULT_CODEX_CLI_APPROVAL_POLICY,
327
+ sandboxMode: DEFAULT_CODEX_CLI_SANDBOX_MODE,
328
+ ttySession: {
329
+ startupTimeoutSec: DEFAULT_TTY_SESSION_STARTUP_TIMEOUT_SEC,
330
+ turnTimeoutSec: DEFAULT_TTY_SESSION_TURN_TIMEOUT_SEC,
331
+ },
332
+ }),
333
+ context: researchSessionContextSchema.default({
334
+ trackableGlobs: DEFAULT_ALLOWED_GLOBS,
335
+ webSearch: true,
336
+ shellCommandAllowlistAdditions: [],
337
+ shellCommandAllowlistRemovals: [],
338
+ }),
339
+ workspace: researchSessionWorkspaceSchema.default({
340
+ strategy: "git_worktree",
341
+ promoted: false,
342
+ }),
343
+ stopPolicy: researchSessionStopPolicySchema.default({
344
+ repeatedFailures: DEFAULT_SESSION_REPEATED_FAILURE_LIMIT,
345
+ noMeaningfulProgress: DEFAULT_SESSION_NO_PROGRESS_LIMIT,
346
+ insufficientEvidence: DEFAULT_SESSION_INSUFFICIENT_EVIDENCE_LIMIT,
347
+ }),
348
+ progress: researchSessionProgressSchema.default({
349
+ completedCycles: 0,
350
+ nextCycle: 1,
351
+ latestFrontierIds: [],
352
+ repeatedFailureStreak: 0,
353
+ noMeaningfulProgressStreak: 0,
354
+ insufficientEvidenceStreak: 0,
355
+ }),
356
+ stopCondition: researchSessionStopConditionSchema.default({
357
+ type: "none",
358
+ }),
359
+ resume: researchSessionResumeSchema.default({
360
+ resumable: true,
361
+ checkpointType: "completed_cycle_boundary",
362
+ resumeFromCycle: 1,
363
+ requiresUserConfirmation: false,
364
+ }),
365
+ submittedSnapshot: researchSessionSubmittedSnapshotSchema.optional(),
366
+ draftState: researchSessionDraftStateSchema.optional(),
367
+ evidenceBundlePath: z.string().min(1).optional(),
368
+ createdAt: z.string().datetime(),
369
+ updatedAt: z.string().datetime(),
370
+ endedAt: z.string().datetime().optional(),
371
+ })
372
+ .superRefine((record, ctx) => {
373
+ if (record.progress.nextCycle !== record.progress.completedCycles + 1) {
374
+ ctx.addIssue({
375
+ code: z.ZodIssueCode.custom,
376
+ message: "progress.nextCycle must advance from the last completed cycle boundary",
377
+ path: ["progress", "nextCycle"],
378
+ });
379
+ }
380
+ if (record.resume.resumeFromCycle !== record.progress.nextCycle) {
381
+ ctx.addIssue({
382
+ code: z.ZodIssueCode.custom,
383
+ message: "resume.resumeFromCycle must match progress.nextCycle",
384
+ path: ["resume", "resumeFromCycle"],
385
+ });
386
+ }
387
+ if (record.submittedSnapshot &&
388
+ record.submittedSnapshot.sessionId !== record.sessionId) {
389
+ ctx.addIssue({
390
+ code: z.ZodIssueCode.custom,
391
+ message: "submittedSnapshot.sessionId must match the persisted sessionId",
392
+ path: ["submittedSnapshot", "sessionId"],
393
+ });
394
+ }
395
+ if (record.resume.interruptedDuringCycle !== undefined && record.resume.interruptedDuringCycle !== record.resume.resumeFromCycle) {
396
+ ctx.addIssue({
397
+ code: z.ZodIssueCode.custom,
398
+ message: "interruptedDuringCycle must point at the discarded in-flight cycle",
399
+ path: ["resume", "interruptedDuringCycle"],
400
+ });
401
+ }
402
+ if (record.progress.latestDecisionId && !record.progress.latestRunId) {
403
+ ctx.addIssue({
404
+ code: z.ZodIssueCode.custom,
405
+ message: "latestDecisionId requires latestRunId",
406
+ path: ["progress", "latestDecisionId"],
407
+ });
408
+ }
409
+ if (record.resume.checkpointDecisionId && !record.resume.checkpointRunId) {
410
+ ctx.addIssue({
411
+ code: z.ZodIssueCode.custom,
412
+ message: "checkpointDecisionId requires checkpointRunId",
413
+ path: ["resume", "checkpointDecisionId"],
414
+ });
415
+ }
416
+ if (record.resume.checkpointRunId && record.resume.checkpointRunId !== record.progress.latestRunId) {
417
+ ctx.addIssue({
418
+ code: z.ZodIssueCode.custom,
419
+ message: "checkpointRunId must match progress.latestRunId at the last completed cycle boundary",
420
+ path: ["resume", "checkpointRunId"],
421
+ });
422
+ }
423
+ if (record.resume.checkpointDecisionId && record.resume.checkpointDecisionId !== record.progress.latestDecisionId) {
424
+ ctx.addIssue({
425
+ code: z.ZodIssueCode.custom,
426
+ message: "checkpointDecisionId must match progress.latestDecisionId at the last completed cycle boundary",
427
+ path: ["resume", "checkpointDecisionId"],
428
+ });
429
+ }
430
+ if (record.progress.lastSignals && record.progress.lastSignals.cycle !== record.progress.completedCycles) {
431
+ ctx.addIssue({
432
+ code: z.ZodIssueCode.custom,
433
+ message: "lastSignals must describe the most recently completed cycle",
434
+ path: ["progress", "lastSignals", "cycle"],
435
+ });
436
+ }
437
+ if (record.progress.completedCycles > 0) {
438
+ if (!record.progress.latestRunId) {
439
+ ctx.addIssue({
440
+ code: z.ZodIssueCode.custom,
441
+ message: "completed sessions must capture the latest run id at the last completed cycle boundary",
442
+ path: ["progress", "latestRunId"],
443
+ });
444
+ }
445
+ if (!record.progress.lastCheckpointAt) {
446
+ ctx.addIssue({
447
+ code: z.ZodIssueCode.custom,
448
+ message: "completed sessions must capture lastCheckpointAt at the last completed cycle boundary",
449
+ path: ["progress", "lastCheckpointAt"],
450
+ });
451
+ }
452
+ if (!record.progress.lastSignals) {
453
+ ctx.addIssue({
454
+ code: z.ZodIssueCode.custom,
455
+ message: "completed sessions must capture progress.lastSignals for the most recent completed cycle",
456
+ path: ["progress", "lastSignals"],
457
+ });
458
+ }
459
+ if (!record.resume.checkpointRunId) {
460
+ ctx.addIssue({
461
+ code: z.ZodIssueCode.custom,
462
+ message: "completed sessions must capture resume.checkpointRunId for safe restart semantics",
463
+ path: ["resume", "checkpointRunId"],
464
+ });
465
+ }
466
+ }
467
+ if (record.status === "running") {
468
+ if (record.stopCondition.type !== "none") {
469
+ ctx.addIssue({
470
+ code: z.ZodIssueCode.custom,
471
+ message: "running sessions cannot declare a stop condition",
472
+ path: ["stopCondition"],
473
+ });
474
+ }
475
+ if (record.resume.requiresUserConfirmation) {
476
+ ctx.addIssue({
477
+ code: z.ZodIssueCode.custom,
478
+ message: "running sessions cannot require resume confirmation",
479
+ path: ["resume", "requiresUserConfirmation"],
480
+ });
481
+ }
482
+ }
483
+ if (record.status === "draft") {
484
+ if (record.stopCondition.type !== "none") {
485
+ ctx.addIssue({
486
+ code: z.ZodIssueCode.custom,
487
+ message: "draft sessions cannot declare a stop condition",
488
+ path: ["stopCondition"],
489
+ });
490
+ }
491
+ if (record.endedAt) {
492
+ ctx.addIssue({
493
+ code: z.ZodIssueCode.custom,
494
+ message: "draft sessions cannot include endedAt",
495
+ path: ["endedAt"],
496
+ });
497
+ }
498
+ }
499
+ if (record.status === "awaiting_resume") {
500
+ if (!record.resume.requiresUserConfirmation) {
501
+ ctx.addIssue({
502
+ code: z.ZodIssueCode.custom,
503
+ message: "awaiting_resume sessions must require user confirmation",
504
+ path: ["resume", "requiresUserConfirmation"],
505
+ });
506
+ }
507
+ if (!record.resume.interruptionDetectedAt) {
508
+ ctx.addIssue({
509
+ code: z.ZodIssueCode.custom,
510
+ message: "awaiting_resume sessions must record interruptionDetectedAt",
511
+ path: ["resume", "interruptionDetectedAt"],
512
+ });
513
+ }
514
+ }
515
+ if (record.status === "halted") {
516
+ if (!record.resume.requiresUserConfirmation) {
517
+ ctx.addIssue({
518
+ code: z.ZodIssueCode.custom,
519
+ message: "halted sessions must require user confirmation before resuming",
520
+ path: ["resume", "requiresUserConfirmation"],
521
+ });
522
+ }
523
+ if (!["repeated_failures", "no_meaningful_progress", "insufficient_evidence", "operator_stop"].includes(record.stopCondition.type)) {
524
+ ctx.addIssue({
525
+ code: z.ZodIssueCode.custom,
526
+ message: "halted sessions must record a halt stop condition",
527
+ path: ["stopCondition"],
528
+ });
529
+ }
530
+ }
531
+ if (record.status === "goal_achieved") {
532
+ if (record.stopCondition.type !== "goal_achieved") {
533
+ ctx.addIssue({
534
+ code: z.ZodIssueCode.custom,
535
+ message: "goal_achieved sessions must record a goal_achieved stop condition",
536
+ path: ["stopCondition"],
537
+ });
538
+ }
539
+ if (!record.evidenceBundlePath) {
540
+ ctx.addIssue({
541
+ code: z.ZodIssueCode.custom,
542
+ message: "goal_achieved sessions must include evidenceBundlePath",
543
+ path: ["evidenceBundlePath"],
544
+ });
545
+ }
546
+ if (!record.endedAt) {
547
+ ctx.addIssue({
548
+ code: z.ZodIssueCode.custom,
549
+ message: "goal_achieved sessions must include endedAt",
550
+ path: ["endedAt"],
551
+ });
552
+ }
553
+ }
554
+ if (record.status === "failed") {
555
+ if (record.stopCondition.type !== "unrecoverable_error") {
556
+ ctx.addIssue({
557
+ code: z.ZodIssueCode.custom,
558
+ message: "failed sessions must record an unrecoverable_error stop condition",
559
+ path: ["stopCondition"],
560
+ });
561
+ }
562
+ if (!record.endedAt) {
563
+ ctx.addIssue({
564
+ code: z.ZodIssueCode.custom,
565
+ message: "failed sessions must include endedAt",
566
+ path: ["endedAt"],
567
+ });
568
+ }
569
+ }
570
+ if (record.workspace.promoted && record.status !== "goal_achieved") {
571
+ ctx.addIssue({
572
+ code: z.ZodIssueCode.custom,
573
+ message: "only goal_achieved sessions may promote work back into the source workspace",
574
+ path: ["workspace", "promoted"],
575
+ });
576
+ }
577
+ if (record.workspace.promoted) {
578
+ if (!record.workspace.promotedAt) {
579
+ ctx.addIssue({
580
+ code: z.ZodIssueCode.custom,
581
+ message: "promoted workspaces must include promotedAt",
582
+ path: ["workspace", "promotedAt"],
583
+ });
584
+ }
585
+ if (!record.workspace.promotedRunId) {
586
+ ctx.addIssue({
587
+ code: z.ZodIssueCode.custom,
588
+ message: "promoted workspaces must include promotedRunId",
589
+ path: ["workspace", "promotedRunId"],
590
+ });
591
+ }
592
+ if (!record.workspace.promotedDecisionId) {
593
+ ctx.addIssue({
594
+ code: z.ZodIssueCode.custom,
595
+ message: "promoted workspaces must include promotedDecisionId",
596
+ path: ["workspace", "promotedDecisionId"],
597
+ });
598
+ }
599
+ if (!record.workspace.promotedCommitSha) {
600
+ ctx.addIssue({
601
+ code: z.ZodIssueCode.custom,
602
+ message: "promoted workspaces must include promotedCommitSha",
603
+ path: ["workspace", "promotedCommitSha"],
604
+ });
605
+ }
606
+ }
607
+ if (!record.workspace.promoted
608
+ && (record.workspace.promotedAt
609
+ || record.workspace.promotedRunId
610
+ || record.workspace.promotedDecisionId
611
+ || record.workspace.promotedCommitSha)) {
612
+ ctx.addIssue({
613
+ code: z.ZodIssueCode.custom,
614
+ message: "unpromoted workspaces cannot carry promotion metadata",
615
+ path: ["workspace"],
616
+ });
617
+ }
618
+ });
619
+ export function isResumableResearchSessionStatus(status) {
620
+ return RESUMABLE_RESEARCH_SESSION_STATUSES.includes(status);
621
+ }
622
+ export function buildResearchSessionTuiSelectedCandidateSummary(session, options = {}) {
623
+ const latestCycle = session.progress.lastSignals
624
+ ? {
625
+ outcome: session.progress.lastSignals.outcome,
626
+ meaningfulProgress: session.progress.lastSignals.meaningfulProgress,
627
+ insufficientEvidence: session.progress.lastSignals.insufficientEvidence ?? false,
628
+ changedFileCount: session.progress.lastSignals.changedFileCount,
629
+ diffLineCount: session.progress.lastSignals.diffLineCount,
630
+ newArtifactCount: session.progress.lastSignals.newArtifacts?.length ?? 0,
631
+ ...(session.progress.lastSignals.agentSummary
632
+ ? { agentSummary: session.progress.lastSignals.agentSummary }
633
+ : {}),
634
+ }
635
+ : undefined;
636
+ return researchSessionTuiSelectedCandidateSummarySchema.parse({
637
+ sessionId: session.sessionId,
638
+ status: session.status,
639
+ goal: session.goal,
640
+ updatedAt: session.updatedAt,
641
+ resumeFromCycle: session.resume.resumeFromCycle,
642
+ checkpoint: {
643
+ completedCycles: session.progress.completedCycles,
644
+ ...(session.progress.latestRunId ? { latestRunId: session.progress.latestRunId } : {}),
645
+ ...(session.progress.latestDecisionId
646
+ ? { latestDecisionId: session.progress.latestDecisionId }
647
+ : {}),
648
+ ...(session.progress.lastCheckpointAt
649
+ ? { lastCheckpointAt: session.progress.lastCheckpointAt }
650
+ : {}),
651
+ stopCondition: session.stopCondition.type,
652
+ },
653
+ ...(latestCycle ? { latestCycle } : {}),
654
+ ...(options.recovery ? { recovery: options.recovery } : {}),
655
+ userConfirmation: {
656
+ required: true,
657
+ },
658
+ });
659
+ }
660
+ export function buildResearchSessionMetadata(session) {
661
+ return researchSessionMetadataSchema.parse({
662
+ sessionId: session.sessionId,
663
+ goal: session.goal,
664
+ workingDirectory: session.workingDirectory,
665
+ status: session.status,
666
+ createdAt: session.createdAt,
667
+ updatedAt: session.updatedAt,
668
+ completedCycles: session.progress.completedCycles,
669
+ ...(session.progress.lastCheckpointAt
670
+ ? { lastCheckpointAt: session.progress.lastCheckpointAt }
671
+ : {}),
672
+ resumeFromCycle: session.resume.resumeFromCycle,
673
+ });
674
+ }
675
+ export function parsePersistedResearchSessionMetadata(value) {
676
+ const parsed = persistedResearchSessionMetadataSourceSchema.parse(value);
677
+ return researchSessionMetadataSchema.parse({
678
+ sessionId: parsed.sessionId,
679
+ goal: parsed.goal,
680
+ workingDirectory: parsed.workingDirectory,
681
+ status: parsed.status,
682
+ createdAt: parsed.createdAt,
683
+ updatedAt: parsed.updatedAt,
684
+ completedCycles: parsed.progress.completedCycles,
685
+ ...(parsed.progress.lastCheckpointAt ? { lastCheckpointAt: parsed.progress.lastCheckpointAt } : {}),
686
+ resumeFromCycle: parsed.resume.resumeFromCycle,
687
+ });
688
+ }
689
+ //# sourceMappingURL=research-session.js.map