task-while 0.0.2 → 0.0.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 (41) hide show
  1. package/README.md +32 -34
  2. package/package.json +2 -2
  3. package/src/adapters/fs/harness-store.ts +84 -0
  4. package/src/agents/claude.ts +159 -9
  5. package/src/agents/codex.ts +68 -4
  6. package/src/agents/event-log.ts +160 -15
  7. package/src/batch/discovery.ts +1 -1
  8. package/src/commands/batch.ts +63 -164
  9. package/src/commands/run-branch-helpers.ts +81 -0
  10. package/src/commands/run-providers.ts +77 -0
  11. package/src/commands/run.ts +121 -177
  12. package/src/core/create-runtime-ports.ts +118 -0
  13. package/src/core/runtime.ts +15 -36
  14. package/src/harness/in-memory-store.ts +45 -0
  15. package/src/harness/kernel.ts +226 -0
  16. package/src/harness/state.ts +47 -0
  17. package/src/harness/store.ts +26 -0
  18. package/src/harness/workflow-builders.ts +87 -0
  19. package/src/harness/workflow-program.ts +86 -0
  20. package/src/ports/agent.ts +17 -0
  21. package/src/ports/code-host.ts +23 -0
  22. package/src/programs/batch.ts +139 -0
  23. package/src/programs/run-direct.ts +209 -0
  24. package/src/programs/run-pr-transitions.ts +81 -0
  25. package/src/programs/run-pr.ts +290 -0
  26. package/src/programs/shared-steps.ts +252 -0
  27. package/src/schedulers/scheduler.ts +208 -0
  28. package/src/session/session.ts +127 -0
  29. package/src/workflow/config.ts +15 -0
  30. package/src/core/engine-helpers.ts +0 -114
  31. package/src/core/engine-outcomes.ts +0 -166
  32. package/src/core/engine.ts +0 -223
  33. package/src/core/orchestrator-helpers.ts +0 -52
  34. package/src/core/orchestrator-integrate-resume.ts +0 -149
  35. package/src/core/orchestrator-review-resume.ts +0 -228
  36. package/src/core/orchestrator-task-attempt.ts +0 -257
  37. package/src/core/orchestrator.ts +0 -99
  38. package/src/runtime/fs-runtime.ts +0 -209
  39. package/src/workflow/direct-preset.ts +0 -44
  40. package/src/workflow/preset.ts +0 -86
  41. package/src/workflow/pull-request-preset.ts +0 -312
@@ -1,228 +0,0 @@
1
- import {
2
- isPullRequestWorkflowPreset,
3
- type WorkflowRuntime,
4
- } from '../workflow/preset'
5
- import {
6
- recordCommitFailure,
7
- recordIntegrateResult,
8
- recordReviewApproved,
9
- recordReviewFailure,
10
- recordReviewResult,
11
- } from './engine'
12
- import { shouldPassZeroGate } from './engine-helpers'
13
- import {
14
- appendEvent,
15
- now,
16
- persistCommittedArtifacts,
17
- persistState,
18
- } from './orchestrator-helpers'
19
-
20
- import type {
21
- FinalReport,
22
- IntegrateArtifact,
23
- ReviewArtifact,
24
- TaskGraph,
25
- WorkflowState,
26
- } from '../types'
27
- import type { OrchestratorRuntime } from './runtime'
28
-
29
- export interface ResumePullRequestReviewInput {
30
- graph: TaskGraph
31
- runtime: OrchestratorRuntime
32
- state: WorkflowState
33
- workflow: WorkflowRuntime
34
- }
35
-
36
- export interface ResumePullRequestReviewResult {
37
- report: FinalReport
38
- state: WorkflowState
39
- }
40
-
41
- export async function resumePullRequestReview(
42
- input: ResumePullRequestReviewInput,
43
- ): Promise<null | ResumePullRequestReviewResult> {
44
- const preset = input.workflow.preset
45
- if (!isPullRequestWorkflowPreset(preset) || !input.state.currentTaskHandle) {
46
- return null
47
- }
48
-
49
- const taskHandle = input.state.currentTaskHandle
50
- const taskState = input.state.tasks[taskHandle]
51
- if (taskState?.status !== 'running' || taskState.stage !== 'review') {
52
- return null
53
- }
54
-
55
- const artifactKey = {
56
- attempt: taskState.attempt,
57
- generation: taskState.generation,
58
- taskHandle,
59
- }
60
- const implementArtifact =
61
- await input.runtime.store.loadImplementArtifact(artifactKey)
62
-
63
- if (!implementArtifact) {
64
- const reason = `Cannot resume review for ${taskHandle} without a persisted implement artifact`
65
- const nextState = recordReviewFailure(
66
- input.graph,
67
- input.state,
68
- taskHandle,
69
- reason,
70
- )
71
- await appendEvent(input.runtime, {
72
- attempt: taskState.attempt,
73
- detail: reason,
74
- generation: taskState.generation,
75
- taskHandle,
76
- timestamp: now(),
77
- type: 'review_failed',
78
- })
79
- const report = await persistState(input.runtime, input.graph, nextState)
80
- return {
81
- report,
82
- state: nextState,
83
- }
84
- }
85
-
86
- const commitMessage = input.runtime.taskSource.buildCommitSubject(taskHandle)
87
- let review
88
- let reviewPhaseKind: 'approved' | 'rejected'
89
-
90
- try {
91
- const completionCriteria =
92
- await input.runtime.taskSource.getCompletionCriteria(taskHandle)
93
- const reviewPhase = await preset.review({
94
- attempt: taskState.attempt,
95
- commitMessage,
96
- completionCriteria,
97
- runtime: input.runtime,
98
- taskHandle,
99
- })
100
- reviewPhaseKind = reviewPhase.kind
101
- review = reviewPhase.review
102
- if (review.taskHandle !== taskHandle) {
103
- throw new Error(
104
- `Review taskHandle mismatch: expected ${taskHandle}, received ${review.taskHandle}`,
105
- )
106
- }
107
- } catch (error) {
108
- const reason = error instanceof Error ? error.message : String(error)
109
- const nextState = recordReviewFailure(
110
- input.graph,
111
- input.state,
112
- taskHandle,
113
- reason,
114
- )
115
- await appendEvent(input.runtime, {
116
- attempt: taskState.attempt,
117
- detail: reason,
118
- generation: taskState.generation,
119
- taskHandle,
120
- timestamp: now(),
121
- type: 'review_failed',
122
- })
123
- const report = await persistState(input.runtime, input.graph, nextState)
124
- return {
125
- report,
126
- state: nextState,
127
- }
128
- }
129
-
130
- const reviewArtifact: ReviewArtifact = {
131
- attempt: taskState.attempt,
132
- createdAt: now(),
133
- generation: taskState.generation,
134
- result: review,
135
- taskHandle,
136
- }
137
- await input.runtime.store.saveReviewArtifact(reviewArtifact)
138
- await appendEvent(input.runtime, {
139
- attempt: taskState.attempt,
140
- detail: review.summary,
141
- generation: taskState.generation,
142
- taskHandle,
143
- timestamp: now(),
144
- type: 'review_completed',
145
- })
146
-
147
- if (reviewPhaseKind === 'approved' && shouldPassZeroGate({ review })) {
148
- let nextState = recordReviewApproved(input.state, taskHandle, review)
149
- await appendEvent(input.runtime, {
150
- attempt: taskState.attempt,
151
- generation: taskState.generation,
152
- taskHandle,
153
- timestamp: now(),
154
- type: 'integrate_started',
155
- })
156
- let report = await persistState(input.runtime, input.graph, nextState)
157
-
158
- let integrateResult
159
- try {
160
- integrateResult = await input.workflow.preset.integrate({
161
- commitMessage,
162
- runtime: input.runtime,
163
- taskHandle,
164
- })
165
- } catch (error) {
166
- const reason = error instanceof Error ? error.message : String(error)
167
- nextState = recordCommitFailure(
168
- input.graph,
169
- nextState,
170
- taskHandle,
171
- reason,
172
- )
173
- await appendEvent(input.runtime, {
174
- attempt: taskState.attempt,
175
- detail: reason,
176
- generation: taskState.generation,
177
- taskHandle,
178
- timestamp: now(),
179
- type: 'integrate_failed',
180
- })
181
- report = await persistState(input.runtime, input.graph, nextState)
182
- return {
183
- report,
184
- state: nextState,
185
- }
186
- }
187
-
188
- const integrateArtifact: IntegrateArtifact = {
189
- attempt: taskState.attempt,
190
- createdAt: now(),
191
- generation: taskState.generation,
192
- result: integrateResult.result,
193
- taskHandle,
194
- }
195
- nextState = recordIntegrateResult(input.graph, nextState, taskHandle, {
196
- commitSha: integrateResult.result.commitSha,
197
- review,
198
- })
199
- report = await persistState(input.runtime, input.graph, nextState)
200
- await appendEvent(input.runtime, {
201
- attempt: taskState.attempt,
202
- detail: integrateResult.result.summary,
203
- generation: taskState.generation,
204
- taskHandle,
205
- timestamp: now(),
206
- type: 'integrate_completed',
207
- })
208
- await input.runtime.store.saveIntegrateArtifact(integrateArtifact)
209
- await persistCommittedArtifacts(input.runtime, {
210
- commitSha: integrateResult.result.commitSha,
211
- implementArtifact,
212
- reviewArtifact,
213
- })
214
- return {
215
- report,
216
- state: nextState,
217
- }
218
- }
219
-
220
- const nextState = recordReviewResult(input.graph, input.state, taskHandle, {
221
- review,
222
- })
223
- const report = await persistState(input.runtime, input.graph, nextState)
224
- return {
225
- report,
226
- state: nextState,
227
- }
228
- }
@@ -1,257 +0,0 @@
1
- import {
2
- recordCommitFailure,
3
- recordImplementFailure,
4
- recordImplementSuccess,
5
- recordIntegrateResult,
6
- recordReviewApproved,
7
- recordReviewFailure,
8
- recordReviewResult,
9
- startAttempt,
10
- } from './engine'
11
- import { shouldPassZeroGate } from './engine-helpers'
12
- import {
13
- appendEvent,
14
- now,
15
- persistCommittedArtifacts,
16
- persistState,
17
- } from './orchestrator-helpers'
18
-
19
- import type {
20
- FinalReport,
21
- ImplementArtifact,
22
- IntegrateArtifact,
23
- ReviewArtifact,
24
- TaskGraph,
25
- WorkflowState,
26
- } from '../types'
27
- import type { ReviewPhaseResult, WorkflowRuntime } from '../workflow/preset'
28
- import type { OrchestratorRuntime } from './runtime'
29
-
30
- export interface ExecuteTaskAttemptInput {
31
- graph: TaskGraph
32
- runtime: OrchestratorRuntime
33
- state: WorkflowState
34
- taskHandle: string
35
- workflow: WorkflowRuntime
36
- }
37
-
38
- export interface ExecuteTaskAttemptResult {
39
- report: FinalReport
40
- state: WorkflowState
41
- }
42
-
43
- export async function executeTaskAttempt(
44
- input: ExecuteTaskAttemptInput,
45
- ): Promise<ExecuteTaskAttemptResult> {
46
- let state = startAttempt(input.graph, input.state, input.taskHandle)
47
- await appendEvent(input.runtime, {
48
- attempt: state.tasks[input.taskHandle]!.attempt,
49
- generation: state.tasks[input.taskHandle]!.generation,
50
- taskHandle: input.taskHandle,
51
- timestamp: now(),
52
- type: 'attempt_started',
53
- })
54
- let report = await persistState(input.runtime, input.graph, state)
55
- const taskState = state.tasks[input.taskHandle]!
56
- const taskHandle = input.taskHandle
57
- const commitMessage = input.runtime.taskSource.buildCommitSubject(taskHandle)
58
- let implementArtifact: ImplementArtifact | null = null
59
- let reviewArtifact: null | ReviewArtifact = null
60
- let implement
61
- try {
62
- const prompt = await input.runtime.taskSource.buildImplementPrompt({
63
- attempt: taskState.attempt,
64
- generation: taskState.generation,
65
- lastFindings: taskState.lastFindings,
66
- taskHandle,
67
- })
68
- implement = await input.workflow.roles.implementer.implement({
69
- attempt: taskState.attempt,
70
- generation: taskState.generation,
71
- lastFindings: taskState.lastFindings,
72
- prompt,
73
- taskHandle,
74
- })
75
- if (implement.taskHandle !== taskHandle) {
76
- throw new Error(
77
- `Implement taskHandle mismatch: expected ${taskHandle}, received ${implement.taskHandle}`,
78
- )
79
- }
80
- } catch (error) {
81
- const reason = error instanceof Error ? error.message : String(error)
82
- state = recordImplementFailure(input.graph, state, input.taskHandle, reason)
83
- await appendEvent(input.runtime, {
84
- attempt: taskState.attempt,
85
- detail: reason,
86
- generation: taskState.generation,
87
- taskHandle: input.taskHandle,
88
- timestamp: now(),
89
- type: 'implement_failed',
90
- })
91
- report = await persistState(input.runtime, input.graph, state)
92
- return { report, state }
93
- }
94
- implementArtifact = {
95
- attempt: taskState.attempt,
96
- createdAt: now(),
97
- generation: taskState.generation,
98
- result: implement,
99
- taskHandle,
100
- }
101
- await input.runtime.store.saveImplementArtifact(implementArtifact)
102
- state = recordImplementSuccess(state, input.taskHandle)
103
- await appendEvent(input.runtime, {
104
- attempt: taskState.attempt,
105
- generation: taskState.generation,
106
- taskHandle: input.taskHandle,
107
- timestamp: now(),
108
- type: 'implement_succeeded',
109
- })
110
- await appendEvent(input.runtime, {
111
- attempt: taskState.attempt,
112
- generation: taskState.generation,
113
- taskHandle: input.taskHandle,
114
- timestamp: now(),
115
- type: 'review_started',
116
- })
117
- report = await persistState(input.runtime, input.graph, state)
118
- let review
119
- let reviewPhaseKind: 'approved' | 'rejected'
120
- try {
121
- let reviewPhase: ReviewPhaseResult
122
- if (input.workflow.preset.mode === 'direct') {
123
- const actualChangedFiles =
124
- await input.runtime.git.getChangedFilesSinceHead()
125
- const prompt = await input.runtime.taskSource.buildReviewPrompt({
126
- actualChangedFiles,
127
- attempt: taskState.attempt,
128
- generation: taskState.generation,
129
- implement,
130
- lastFindings: taskState.lastFindings,
131
- taskHandle,
132
- })
133
- reviewPhase = await input.workflow.preset.review({
134
- actualChangedFiles,
135
- attempt: taskState.attempt,
136
- commitMessage,
137
- generation: taskState.generation,
138
- implement,
139
- lastFindings: taskState.lastFindings,
140
- prompt,
141
- taskHandle,
142
- })
143
- } else {
144
- const completionCriteria =
145
- await input.runtime.taskSource.getCompletionCriteria(taskHandle)
146
- reviewPhase = await input.workflow.preset.review({
147
- attempt: taskState.attempt,
148
- commitMessage,
149
- completionCriteria,
150
- runtime: input.runtime,
151
- taskHandle,
152
- })
153
- }
154
- reviewPhaseKind = reviewPhase.kind
155
- review = reviewPhase.review
156
- if (review.taskHandle !== taskHandle) {
157
- throw new Error(
158
- `Review taskHandle mismatch: expected ${taskHandle}, received ${review.taskHandle}`,
159
- )
160
- }
161
- } catch (error) {
162
- const reason = error instanceof Error ? error.message : String(error)
163
- state = recordReviewFailure(input.graph, state, input.taskHandle, reason)
164
- await appendEvent(input.runtime, {
165
- attempt: taskState.attempt,
166
- detail: reason,
167
- generation: taskState.generation,
168
- taskHandle: input.taskHandle,
169
- timestamp: now(),
170
- type: 'review_failed',
171
- })
172
- report = await persistState(input.runtime, input.graph, state)
173
- return { report, state }
174
- }
175
- reviewArtifact = {
176
- attempt: taskState.attempt,
177
- createdAt: now(),
178
- generation: taskState.generation,
179
- result: review,
180
- taskHandle,
181
- }
182
- await input.runtime.store.saveReviewArtifact(reviewArtifact)
183
- await appendEvent(input.runtime, {
184
- attempt: taskState.attempt,
185
- detail: review.summary,
186
- generation: taskState.generation,
187
- taskHandle: input.taskHandle,
188
- timestamp: now(),
189
- type: 'review_completed',
190
- })
191
-
192
- if (reviewPhaseKind === 'approved' && shouldPassZeroGate({ review })) {
193
- state = recordReviewApproved(state, input.taskHandle, review)
194
- await appendEvent(input.runtime, {
195
- attempt: taskState.attempt,
196
- generation: taskState.generation,
197
- taskHandle: input.taskHandle,
198
- timestamp: now(),
199
- type: 'integrate_started',
200
- })
201
- report = await persistState(input.runtime, input.graph, state)
202
-
203
- let integrateResult
204
- try {
205
- integrateResult = await input.workflow.preset.integrate({
206
- commitMessage,
207
- runtime: input.runtime,
208
- taskHandle,
209
- })
210
- } catch (error) {
211
- const reason = error instanceof Error ? error.message : String(error)
212
- state = recordCommitFailure(input.graph, state, input.taskHandle, reason)
213
- await appendEvent(input.runtime, {
214
- attempt: taskState.attempt,
215
- detail: reason,
216
- generation: taskState.generation,
217
- taskHandle: input.taskHandle,
218
- timestamp: now(),
219
- type: 'integrate_failed',
220
- })
221
- report = await persistState(input.runtime, input.graph, state)
222
- return { report, state }
223
- }
224
- const integrateArtifact: IntegrateArtifact = {
225
- attempt: taskState.attempt,
226
- createdAt: now(),
227
- generation: taskState.generation,
228
- result: integrateResult.result,
229
- taskHandle,
230
- }
231
- state = recordIntegrateResult(input.graph, state, taskHandle, {
232
- commitSha: integrateResult.result.commitSha,
233
- review,
234
- })
235
- report = await persistState(input.runtime, input.graph, state)
236
- await appendEvent(input.runtime, {
237
- attempt: taskState.attempt,
238
- detail: integrateResult.result.summary,
239
- generation: taskState.generation,
240
- taskHandle: input.taskHandle,
241
- timestamp: now(),
242
- type: 'integrate_completed',
243
- })
244
- await input.runtime.store.saveIntegrateArtifact(integrateArtifact)
245
- await persistCommittedArtifacts(input.runtime, {
246
- commitSha: integrateResult.result.commitSha,
247
- implementArtifact,
248
- reviewArtifact,
249
- })
250
- return { report, state }
251
- }
252
- state = recordReviewResult(input.graph, state, input.taskHandle, {
253
- review,
254
- })
255
- report = await persistState(input.runtime, input.graph, state)
256
- return { report, state }
257
- }
@@ -1,99 +0,0 @@
1
- import {
2
- alignStateWithGraph,
3
- createInitialWorkflowState,
4
- selectNextRunnableTask,
5
- } from './engine'
6
- import { persistState } from './orchestrator-helpers'
7
- import { resumePullRequestIntegrate } from './orchestrator-integrate-resume'
8
- import { resumePullRequestReview } from './orchestrator-review-resume'
9
- import { executeTaskAttempt } from './orchestrator-task-attempt'
10
-
11
- import type { FinalReport, TaskGraph, WorkflowState } from '../types'
12
- import type { WorkflowRuntime } from '../workflow/preset'
13
- import type { OrchestratorRuntime } from './runtime'
14
-
15
- export interface WorkflowRunResult {
16
- state: WorkflowState
17
- summary: FinalReport['summary']
18
- }
19
-
20
- export interface RunWorkflowInput {
21
- graph: TaskGraph
22
- runtime: OrchestratorRuntime
23
- untilTaskHandle?: string
24
- workflow: WorkflowRuntime
25
- }
26
-
27
- export async function runWorkflow(
28
- input: RunWorkflowInput,
29
- ): Promise<WorkflowRunResult> {
30
- const workflow = input.workflow
31
- const isPullRequestMode = workflow.preset.mode === 'pull-request'
32
- await input.runtime.store.saveGraph(input.graph)
33
- const storedState = await input.runtime.store.loadState()
34
- let state = alignStateWithGraph(
35
- input.graph,
36
- storedState ?? createInitialWorkflowState(input.graph),
37
- {
38
- preserveRunningIntegrate: isPullRequestMode,
39
- preserveRunningReview: isPullRequestMode,
40
- },
41
- )
42
- let report = await persistState(input.runtime, input.graph, state)
43
-
44
- while (
45
- report.summary.finalStatus !== 'blocked' &&
46
- report.summary.finalStatus !== 'replan_required'
47
- ) {
48
- if (
49
- input.untilTaskHandle &&
50
- state.tasks[input.untilTaskHandle]?.status === 'done'
51
- ) {
52
- break
53
- }
54
-
55
- const resumedIntegrate = await resumePullRequestIntegrate({
56
- graph: input.graph,
57
- runtime: input.runtime,
58
- state,
59
- workflow,
60
- })
61
- if (resumedIntegrate) {
62
- report = resumedIntegrate.report
63
- state = resumedIntegrate.state
64
- continue
65
- }
66
-
67
- const resumedReview = await resumePullRequestReview({
68
- graph: input.graph,
69
- runtime: input.runtime,
70
- state,
71
- workflow,
72
- })
73
- if (resumedReview) {
74
- report = resumedReview.report
75
- state = resumedReview.state
76
- continue
77
- }
78
-
79
- const task = selectNextRunnableTask(input.graph, state)
80
- if (!task) {
81
- break
82
- }
83
-
84
- const next = await executeTaskAttempt({
85
- graph: input.graph,
86
- runtime: input.runtime,
87
- state,
88
- taskHandle: task,
89
- workflow,
90
- })
91
- report = next.report
92
- state = next.state
93
- }
94
-
95
- return {
96
- state,
97
- summary: report.summary,
98
- }
99
- }