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.
- package/README.md +32 -34
- package/package.json +2 -2
- package/src/adapters/fs/harness-store.ts +84 -0
- package/src/agents/claude.ts +159 -9
- package/src/agents/codex.ts +68 -4
- package/src/agents/event-log.ts +160 -15
- package/src/batch/discovery.ts +1 -1
- package/src/commands/batch.ts +63 -164
- package/src/commands/run-branch-helpers.ts +81 -0
- package/src/commands/run-providers.ts +77 -0
- package/src/commands/run.ts +121 -177
- package/src/core/create-runtime-ports.ts +118 -0
- package/src/core/runtime.ts +15 -36
- package/src/harness/in-memory-store.ts +45 -0
- package/src/harness/kernel.ts +226 -0
- package/src/harness/state.ts +47 -0
- package/src/harness/store.ts +26 -0
- package/src/harness/workflow-builders.ts +87 -0
- package/src/harness/workflow-program.ts +86 -0
- package/src/ports/agent.ts +17 -0
- package/src/ports/code-host.ts +23 -0
- package/src/programs/batch.ts +139 -0
- package/src/programs/run-direct.ts +209 -0
- package/src/programs/run-pr-transitions.ts +81 -0
- package/src/programs/run-pr.ts +290 -0
- package/src/programs/shared-steps.ts +252 -0
- package/src/schedulers/scheduler.ts +208 -0
- package/src/session/session.ts +127 -0
- package/src/workflow/config.ts +15 -0
- package/src/core/engine-helpers.ts +0 -114
- package/src/core/engine-outcomes.ts +0 -166
- package/src/core/engine.ts +0 -223
- package/src/core/orchestrator-helpers.ts +0 -52
- package/src/core/orchestrator-integrate-resume.ts +0 -149
- package/src/core/orchestrator-review-resume.ts +0 -228
- package/src/core/orchestrator-task-attempt.ts +0 -257
- package/src/core/orchestrator.ts +0 -99
- package/src/runtime/fs-runtime.ts +0 -209
- package/src/workflow/direct-preset.ts +0 -44
- package/src/workflow/preset.ts +0 -86
- package/src/workflow/pull-request-preset.ts +0 -312
package/src/commands/run.ts
CHANGED
|
@@ -1,33 +1,16 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { createCodexProvider } from '../agents/codex'
|
|
3
|
-
import {
|
|
4
|
-
createClaudeEventHandler,
|
|
5
|
-
createCodexEventHandler,
|
|
6
|
-
} from '../agents/event-log'
|
|
1
|
+
import { createFsHarnessStore } from '../adapters/fs/harness-store'
|
|
7
2
|
import { providerOptionsEqual } from '../agents/provider-options'
|
|
8
|
-
import {
|
|
3
|
+
import { createRuntimePorts } from '../core/create-runtime-ports'
|
|
9
4
|
import { buildTaskTopology } from '../core/task-topology'
|
|
10
|
-
import {
|
|
5
|
+
import { runKernel } from '../harness/kernel'
|
|
6
|
+
import { createRunDirectProgram } from '../programs/run-direct'
|
|
7
|
+
import { createRunPrProgram } from '../programs/run-pr'
|
|
8
|
+
import { createRuntimePaths } from '../runtime/path-layout'
|
|
9
|
+
import { createRunGraphScheduler } from '../schedulers/scheduler'
|
|
10
|
+
import { runSession } from '../session/session'
|
|
11
11
|
import { openTaskSource } from '../task-sources/registry'
|
|
12
|
-
import {
|
|
13
|
-
|
|
14
|
-
type WorkflowConfig,
|
|
15
|
-
type WorkflowProvider,
|
|
16
|
-
type WorkflowRoleConfig,
|
|
17
|
-
} from '../workflow/config'
|
|
18
|
-
import {
|
|
19
|
-
createDirectWorkflowPreset,
|
|
20
|
-
createPullRequestWorkflowPreset,
|
|
21
|
-
type WorkflowRuntime,
|
|
22
|
-
} from '../workflow/preset'
|
|
23
|
-
import { createCodexRemoteReviewerProvider } from '../workflow/remote-reviewer'
|
|
24
|
-
|
|
25
|
-
import type {
|
|
26
|
-
ImplementerProvider,
|
|
27
|
-
RemoteReviewerProvider,
|
|
28
|
-
ReviewerProvider,
|
|
29
|
-
WorkflowRoleProviders,
|
|
30
|
-
} from '../agents/types'
|
|
12
|
+
import { loadWorkflowConfig, type WorkflowConfig } from '../workflow/config'
|
|
13
|
+
|
|
31
14
|
import type { WorkspaceContext } from '../types'
|
|
32
15
|
|
|
33
16
|
export interface RunCommandOptions {
|
|
@@ -36,110 +19,56 @@ export interface RunCommandOptions {
|
|
|
36
19
|
verbose?: boolean
|
|
37
20
|
}
|
|
38
21
|
|
|
39
|
-
export
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
export interface ResolveWorkflowRuntimeInput {
|
|
48
|
-
config: WorkflowConfig
|
|
49
|
-
context: WorkspaceContext
|
|
50
|
-
options: RunCommandOptions
|
|
22
|
+
export interface RunCommandResult {
|
|
23
|
+
summary: {
|
|
24
|
+
blockedTasks: number
|
|
25
|
+
completedTasks: number
|
|
26
|
+
finalStatus: 'blocked' | 'completed' | 'in_progress' | 'replan_required'
|
|
27
|
+
replanTasks: number
|
|
28
|
+
totalTasks: number
|
|
29
|
+
}
|
|
51
30
|
}
|
|
52
31
|
|
|
53
|
-
export
|
|
54
|
-
|
|
55
|
-
|
|
32
|
+
export async function runCommand(
|
|
33
|
+
context: WorkspaceContext,
|
|
34
|
+
options: RunCommandOptions = {},
|
|
35
|
+
): Promise<RunCommandResult> {
|
|
36
|
+
const config =
|
|
37
|
+
options.config ?? (await loadWorkflowConfig(context.workspaceRoot))
|
|
56
38
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
39
|
+
const taskSource = await openTaskSource(config.task.source, {
|
|
40
|
+
featureDir: context.featureDir,
|
|
41
|
+
featureId: context.featureId,
|
|
42
|
+
workspaceRoot: context.workspaceRoot,
|
|
43
|
+
})
|
|
60
44
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
ImplementerProvider & ReviewerProvider
|
|
68
|
-
>()
|
|
69
|
-
return (role: WorkflowRoleConfig) => {
|
|
70
|
-
const cached = cache.get(role.provider)
|
|
71
|
-
if (cached) {
|
|
72
|
-
return cached
|
|
73
|
-
}
|
|
74
|
-
let provider: ImplementerProvider & ReviewerProvider
|
|
75
|
-
if (role.provider === 'claude') {
|
|
76
|
-
const onEvent = createClaudeEventHandler(verbose)
|
|
77
|
-
provider = createClaudeProvider({
|
|
78
|
-
...(role.effort ? { effort: role.effort } : {}),
|
|
79
|
-
...(role.model ? { model: role.model } : {}),
|
|
80
|
-
workspaceRoot: context.workspaceRoot,
|
|
81
|
-
...(onEvent ? { onEvent } : {}),
|
|
82
|
-
})
|
|
83
|
-
} else {
|
|
84
|
-
const onEvent = createCodexEventHandler(verbose)
|
|
85
|
-
provider = createCodexProvider({
|
|
86
|
-
...(role.effort ? { effort: role.effort } : {}),
|
|
87
|
-
...(role.model ? { model: role.model } : {}),
|
|
88
|
-
workspaceRoot: context.workspaceRoot,
|
|
89
|
-
...(onEvent ? { onEvent } : {}),
|
|
90
|
-
})
|
|
91
|
-
}
|
|
92
|
-
cache.set(role.provider, provider)
|
|
93
|
-
return provider
|
|
94
|
-
}
|
|
95
|
-
}
|
|
45
|
+
const ports = createRuntimePorts({
|
|
46
|
+
config,
|
|
47
|
+
context,
|
|
48
|
+
taskSource,
|
|
49
|
+
verbose: options.verbose,
|
|
50
|
+
})
|
|
96
51
|
|
|
97
|
-
|
|
98
|
-
const cache = new Map<WorkflowProvider, RemoteReviewerProvider>()
|
|
99
|
-
return (providerName: WorkflowProvider) => {
|
|
100
|
-
const cached = cache.get(providerName)
|
|
101
|
-
if (cached) {
|
|
102
|
-
return cached
|
|
103
|
-
}
|
|
104
|
-
if (providerName === 'claude') {
|
|
105
|
-
throw new Error(
|
|
106
|
-
'claude remote reviewer is not implemented in pull-request mode',
|
|
107
|
-
)
|
|
108
|
-
}
|
|
109
|
-
const provider = createCodexRemoteReviewerProvider()
|
|
110
|
-
cache.set(providerName, provider)
|
|
111
|
-
return provider
|
|
112
|
-
}
|
|
113
|
-
}
|
|
52
|
+
await ports.git.requireCleanWorktree()
|
|
114
53
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
input.context,
|
|
120
|
-
input.options.verbose,
|
|
54
|
+
const topology = buildTaskTopology(
|
|
55
|
+
taskSource,
|
|
56
|
+
context.featureId,
|
|
57
|
+
config.task.maxIterations,
|
|
121
58
|
)
|
|
122
|
-
|
|
123
|
-
const
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
reviewer,
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
return {
|
|
135
|
-
roles,
|
|
136
|
-
preset: createPullRequestWorkflowPreset({
|
|
137
|
-
reviewer,
|
|
138
|
-
}),
|
|
139
|
-
}
|
|
59
|
+
|
|
60
|
+
const isPullRequest = config.workflow.mode === 'pull-request'
|
|
61
|
+
const implementerRole = config.workflow.roles.implementer
|
|
62
|
+
const reviewerRole = config.workflow.roles.reviewer
|
|
63
|
+
|
|
64
|
+
if (isPullRequest && reviewerRole.provider === 'claude') {
|
|
65
|
+
throw new Error(
|
|
66
|
+
'claude remote reviewer is not implemented in pull-request mode',
|
|
67
|
+
)
|
|
140
68
|
}
|
|
141
69
|
|
|
142
70
|
if (
|
|
71
|
+
!isPullRequest &&
|
|
143
72
|
implementerRole.provider === reviewerRole.provider &&
|
|
144
73
|
!providerOptionsEqual(implementerRole, reviewerRole)
|
|
145
74
|
) {
|
|
@@ -148,71 +77,86 @@ function resolveWorkflowRuntime(
|
|
|
148
77
|
)
|
|
149
78
|
}
|
|
150
79
|
|
|
151
|
-
const
|
|
152
|
-
const
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
reviewer,
|
|
156
|
-
}
|
|
80
|
+
const protocol = isPullRequest ? 'run-pr' : 'run-direct'
|
|
81
|
+
const store = createFsHarnessStore(
|
|
82
|
+
createRuntimePaths(context.featureDir).runtimeDir,
|
|
83
|
+
)
|
|
157
84
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
85
|
+
const implementer = ports.resolveAgent(implementerRole)
|
|
86
|
+
const reviewer = ports.resolveAgent(reviewerRole)
|
|
87
|
+
|
|
88
|
+
const program =
|
|
89
|
+
protocol === 'run-pr'
|
|
90
|
+
? createRunPrProgram({
|
|
91
|
+
implementer,
|
|
92
|
+
maxIterations: config.task.maxIterations,
|
|
93
|
+
ports,
|
|
94
|
+
reviewer,
|
|
95
|
+
verifyCommands: config.verify.commands,
|
|
96
|
+
workspaceRoot: context.workspaceRoot,
|
|
97
|
+
})
|
|
98
|
+
: createRunDirectProgram({
|
|
99
|
+
implementer,
|
|
100
|
+
maxIterations: config.task.maxIterations,
|
|
101
|
+
ports,
|
|
102
|
+
reviewer,
|
|
103
|
+
verifyCommands: config.verify.commands,
|
|
104
|
+
workspaceRoot: context.workspaceRoot,
|
|
105
|
+
})
|
|
165
106
|
|
|
166
|
-
export async function loadWorkflowExecution(
|
|
167
|
-
context: WorkspaceContext,
|
|
168
|
-
options: RunCommandOptions = {},
|
|
169
|
-
): Promise<WorkflowExecution> {
|
|
170
|
-
const config =
|
|
171
|
-
options.config ?? (await loadWorkflowConfig(context.workspaceRoot))
|
|
172
|
-
const workflow = resolveWorkflowRuntime({
|
|
173
|
-
config,
|
|
174
|
-
context,
|
|
175
|
-
options,
|
|
176
|
-
})
|
|
177
|
-
const taskSource = await openTaskSource(config.task.source, {
|
|
178
|
-
featureDir: context.featureDir,
|
|
179
|
-
featureId: context.featureId,
|
|
180
|
-
workspaceRoot: context.workspaceRoot,
|
|
181
|
-
})
|
|
182
|
-
const runtime = createOrchestratorRuntime({
|
|
183
|
-
featureDir: context.featureDir,
|
|
184
|
-
taskSource,
|
|
185
|
-
workspaceRoot: context.workspaceRoot,
|
|
186
|
-
})
|
|
187
|
-
await runtime.git.requireCleanWorktree()
|
|
188
|
-
const graph = buildTaskTopology(
|
|
189
|
-
taskSource,
|
|
190
|
-
context.featureId,
|
|
191
|
-
config.task.maxIterations,
|
|
192
|
-
)
|
|
193
107
|
const untilTaskHandle = options.untilTaskId
|
|
194
108
|
? taskSource.resolveTaskSelector(options.untilTaskId)
|
|
195
109
|
: undefined
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
110
|
+
|
|
111
|
+
const scheduler = createRunGraphScheduler({
|
|
112
|
+
protocol,
|
|
113
|
+
store,
|
|
114
|
+
graph: topology.tasks.map((t) => ({
|
|
115
|
+
dependsOn: t.dependsOn,
|
|
116
|
+
subjectId: t.handle,
|
|
117
|
+
})),
|
|
200
118
|
...(untilTaskHandle ? { untilTaskHandle } : {}),
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
for await (const event of runSession({
|
|
122
|
+
config: {},
|
|
123
|
+
scheduler,
|
|
124
|
+
kernel: {
|
|
125
|
+
run: (subjectId) =>
|
|
126
|
+
runKernel({
|
|
127
|
+
config: { verify: config.verify, workflow: config.workflow },
|
|
128
|
+
program,
|
|
129
|
+
protocol,
|
|
130
|
+
store,
|
|
131
|
+
subjectId,
|
|
132
|
+
}),
|
|
133
|
+
},
|
|
134
|
+
})) {
|
|
135
|
+
void event
|
|
201
136
|
}
|
|
202
137
|
|
|
138
|
+
const sets = await scheduler.rebuild()
|
|
139
|
+
const totalTasks = topology.tasks.length
|
|
140
|
+
const completedTasks = sets.done.size
|
|
141
|
+
const blockedTasks = sets.blocked.size
|
|
142
|
+
const replanTasks = sets.replan.size
|
|
143
|
+
|
|
144
|
+
const finalStatus =
|
|
145
|
+
replanTasks > 0
|
|
146
|
+
? ('replan_required' as const)
|
|
147
|
+
: blockedTasks > 0
|
|
148
|
+
? ('blocked' as const)
|
|
149
|
+
: completedTasks === totalTasks
|
|
150
|
+
? ('completed' as const)
|
|
151
|
+
: ('in_progress' as const)
|
|
152
|
+
|
|
203
153
|
return {
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
154
|
+
summary: {
|
|
155
|
+
blockedTasks,
|
|
156
|
+
completedTasks,
|
|
157
|
+
finalStatus,
|
|
158
|
+
replanTasks,
|
|
159
|
+
totalTasks,
|
|
208
160
|
},
|
|
209
161
|
}
|
|
210
162
|
}
|
|
211
|
-
|
|
212
|
-
export async function runCommand(
|
|
213
|
-
context: WorkspaceContext,
|
|
214
|
-
options: RunCommandOptions = {},
|
|
215
|
-
) {
|
|
216
|
-
const execution = await loadWorkflowExecution(context, options)
|
|
217
|
-
return execution.execute()
|
|
218
|
-
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createClaudeProvider,
|
|
3
|
+
type ClaudeAgentClientOptions,
|
|
4
|
+
} from '../agents/claude'
|
|
5
|
+
import {
|
|
6
|
+
createCodexProvider,
|
|
7
|
+
type CodexAgentClientOptions,
|
|
8
|
+
} from '../agents/codex'
|
|
9
|
+
import {
|
|
10
|
+
createClaudeEventHandler,
|
|
11
|
+
createCodexEventHandler,
|
|
12
|
+
} from '../agents/event-log'
|
|
13
|
+
import { GitRuntime } from '../runtime/git'
|
|
14
|
+
import { GitHubRuntime } from '../runtime/github'
|
|
15
|
+
import { createRuntimePaths } from '../runtime/path-layout'
|
|
16
|
+
|
|
17
|
+
import type { ImplementerProvider } from '../agents/types'
|
|
18
|
+
import type { AgentPort } from '../ports/agent'
|
|
19
|
+
import type { TaskSourceSession } from '../task-sources/types'
|
|
20
|
+
import type { WorkspaceContext } from '../types'
|
|
21
|
+
import type { WorkflowConfig } from '../workflow/config'
|
|
22
|
+
import type { AgentRoleConfig, RuntimePorts } from './runtime'
|
|
23
|
+
|
|
24
|
+
interface InvokeCapable {
|
|
25
|
+
invokeStructured: <T>(input: {
|
|
26
|
+
outputSchema: Record<string, unknown>
|
|
27
|
+
prompt: string
|
|
28
|
+
}) => Promise<T>
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function hasInvokeStructured(
|
|
32
|
+
provider: ImplementerProvider,
|
|
33
|
+
): provider is ImplementerProvider & InvokeCapable {
|
|
34
|
+
return (
|
|
35
|
+
'invokeStructured' in provider &&
|
|
36
|
+
typeof provider.invokeStructured === 'function'
|
|
37
|
+
)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface CreateRuntimePortsInput {
|
|
41
|
+
config: WorkflowConfig
|
|
42
|
+
context: WorkspaceContext
|
|
43
|
+
taskSource: TaskSourceSession
|
|
44
|
+
verbose?: boolean | undefined
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function createRuntimePorts(
|
|
48
|
+
input: CreateRuntimePortsInput,
|
|
49
|
+
): RuntimePorts {
|
|
50
|
+
const { context, taskSource, verbose } = input
|
|
51
|
+
const runtimePaths = createRuntimePaths(context.featureDir)
|
|
52
|
+
const git = new GitRuntime(context.workspaceRoot, runtimePaths.runtimeDir)
|
|
53
|
+
const codeHost = new GitHubRuntime(context.workspaceRoot)
|
|
54
|
+
const agentCache = new Map<string, AgentPort>()
|
|
55
|
+
|
|
56
|
+
function resolveAgent(role: AgentRoleConfig): AgentPort {
|
|
57
|
+
const key = `${role.provider}:${role.model ?? ''}:${role.effort ?? ''}`
|
|
58
|
+
const cached = agentCache.get(key)
|
|
59
|
+
if (cached) {
|
|
60
|
+
return cached
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
let provider: ImplementerProvider
|
|
64
|
+
if (role.provider === 'claude') {
|
|
65
|
+
const onEvent = createClaudeEventHandler(verbose)
|
|
66
|
+
provider = createClaudeProvider({
|
|
67
|
+
...(role.effort
|
|
68
|
+
? {
|
|
69
|
+
effort: role.effort as NonNullable<
|
|
70
|
+
ClaudeAgentClientOptions['effort']
|
|
71
|
+
>,
|
|
72
|
+
}
|
|
73
|
+
: {}),
|
|
74
|
+
...(role.model ? { model: role.model } : {}),
|
|
75
|
+
workspaceRoot: context.workspaceRoot,
|
|
76
|
+
...(onEvent ? { onEvent } : {}),
|
|
77
|
+
})
|
|
78
|
+
} else {
|
|
79
|
+
const onEvent = createCodexEventHandler(verbose)
|
|
80
|
+
provider = createCodexProvider({
|
|
81
|
+
...(role.effort
|
|
82
|
+
? {
|
|
83
|
+
effort: role.effort as NonNullable<
|
|
84
|
+
CodexAgentClientOptions['effort']
|
|
85
|
+
>,
|
|
86
|
+
}
|
|
87
|
+
: {}),
|
|
88
|
+
...(role.model ? { model: role.model } : {}),
|
|
89
|
+
workspaceRoot: context.workspaceRoot,
|
|
90
|
+
...(onEvent ? { onEvent } : {}),
|
|
91
|
+
})
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (!hasInvokeStructured(provider)) {
|
|
95
|
+
throw new Error(
|
|
96
|
+
`Provider ${role.provider} does not support invokeStructured`,
|
|
97
|
+
)
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const invoke = provider.invokeStructured.bind(provider)
|
|
101
|
+
const agent: AgentPort = {
|
|
102
|
+
name: provider.name,
|
|
103
|
+
async execute(invocation) {
|
|
104
|
+
return invoke(invocation)
|
|
105
|
+
},
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
agentCache.set(key, agent)
|
|
109
|
+
return agent
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return {
|
|
113
|
+
codeHost,
|
|
114
|
+
git,
|
|
115
|
+
resolveAgent,
|
|
116
|
+
taskSource,
|
|
117
|
+
}
|
|
118
|
+
}
|
package/src/core/runtime.ts
CHANGED
|
@@ -1,39 +1,6 @@
|
|
|
1
|
+
import type { AgentPort } from '../ports/agent'
|
|
2
|
+
import type { CodeHostPort } from '../ports/code-host'
|
|
1
3
|
import type { TaskSourceSession } from '../task-sources/types'
|
|
2
|
-
import type {
|
|
3
|
-
FinalReport,
|
|
4
|
-
ImplementArtifact,
|
|
5
|
-
IntegrateArtifact,
|
|
6
|
-
ReviewArtifact,
|
|
7
|
-
TaskGraph,
|
|
8
|
-
WorkflowEvent,
|
|
9
|
-
WorkflowState,
|
|
10
|
-
} from '../types'
|
|
11
|
-
|
|
12
|
-
export interface AttemptArtifactKey {
|
|
13
|
-
attempt: number
|
|
14
|
-
generation: number
|
|
15
|
-
taskHandle: string
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export interface WorkflowStore {
|
|
19
|
-
appendEvent: (event: WorkflowEvent) => Promise<void>
|
|
20
|
-
loadGraph: () => Promise<null | TaskGraph>
|
|
21
|
-
loadImplementArtifact: (
|
|
22
|
-
key: AttemptArtifactKey,
|
|
23
|
-
) => Promise<ImplementArtifact | null>
|
|
24
|
-
loadReviewArtifact: (
|
|
25
|
-
key: AttemptArtifactKey,
|
|
26
|
-
) => Promise<null | ReviewArtifact>
|
|
27
|
-
loadState: () => Promise<null | WorkflowState>
|
|
28
|
-
readReport: () => Promise<FinalReport | null>
|
|
29
|
-
reset: () => Promise<void>
|
|
30
|
-
saveGraph: (graph: TaskGraph) => Promise<void>
|
|
31
|
-
saveImplementArtifact: (artifact: ImplementArtifact) => Promise<void>
|
|
32
|
-
saveIntegrateArtifact: (artifact: IntegrateArtifact) => Promise<void>
|
|
33
|
-
saveReport: (report: FinalReport) => Promise<void>
|
|
34
|
-
saveReviewArtifact: (artifact: ReviewArtifact) => Promise<void>
|
|
35
|
-
saveState: (state: WorkflowState) => Promise<void>
|
|
36
|
-
}
|
|
37
4
|
|
|
38
5
|
export interface GitCheckoutBranchOptions {
|
|
39
6
|
create?: boolean
|
|
@@ -167,9 +134,21 @@ export interface SquashMergePullRequestInput {
|
|
|
167
134
|
subject: string
|
|
168
135
|
}
|
|
169
136
|
|
|
137
|
+
export interface AgentRoleConfig {
|
|
138
|
+
effort?: string | undefined
|
|
139
|
+
model?: string | undefined
|
|
140
|
+
provider: 'claude' | 'codex'
|
|
141
|
+
}
|
|
142
|
+
|
|
170
143
|
export interface OrchestratorRuntime {
|
|
171
144
|
git: GitPort
|
|
172
145
|
github: GitHubPort
|
|
173
|
-
|
|
146
|
+
taskSource: TaskSourceSession
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export interface RuntimePorts {
|
|
150
|
+
codeHost: CodeHostPort
|
|
151
|
+
git: GitPort
|
|
152
|
+
resolveAgent: (role: AgentRoleConfig) => AgentPort
|
|
174
153
|
taskSource: TaskSourceSession
|
|
175
154
|
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { Artifact, TaskState, TransitionRecord } from './state'
|
|
2
|
+
import type { HarnessStore } from './store'
|
|
3
|
+
|
|
4
|
+
const storeKey = (protocol: string, subjectId: string) =>
|
|
5
|
+
`${protocol}:${subjectId}`
|
|
6
|
+
|
|
7
|
+
export function createInMemoryHarnessStore(): HarnessStore {
|
|
8
|
+
const states = new Map<string, TaskState>()
|
|
9
|
+
const artifacts = new Map<string, Artifact>()
|
|
10
|
+
const transitions = new Map<string, TransitionRecord[]>()
|
|
11
|
+
|
|
12
|
+
return {
|
|
13
|
+
async appendTransition(protocol, subjectId, record) {
|
|
14
|
+
const k = storeKey(protocol, subjectId)
|
|
15
|
+
const list = transitions.get(k) ?? []
|
|
16
|
+
list.push(structuredClone(record))
|
|
17
|
+
transitions.set(k, list)
|
|
18
|
+
},
|
|
19
|
+
async listArtifacts(protocol, subjectId) {
|
|
20
|
+
const prefix = `${storeKey(protocol, subjectId)}:`
|
|
21
|
+
return [...artifacts.entries()]
|
|
22
|
+
.filter(([k]) => k.startsWith(prefix))
|
|
23
|
+
.map(([, v]) => structuredClone(v))
|
|
24
|
+
},
|
|
25
|
+
async loadArtifact(protocol, subjectId, artifactId) {
|
|
26
|
+
const artifact = artifacts.get(
|
|
27
|
+
`${storeKey(protocol, subjectId)}:${artifactId}`,
|
|
28
|
+
)
|
|
29
|
+
return artifact ? structuredClone(artifact) : null
|
|
30
|
+
},
|
|
31
|
+
async loadState(protocol, subjectId) {
|
|
32
|
+
const state = states.get(storeKey(protocol, subjectId))
|
|
33
|
+
return state ? structuredClone(state) : null
|
|
34
|
+
},
|
|
35
|
+
async saveArtifact(protocol, subjectId, artifact) {
|
|
36
|
+
artifacts.set(
|
|
37
|
+
`${storeKey(protocol, subjectId)}:${artifact.id}`,
|
|
38
|
+
structuredClone(artifact),
|
|
39
|
+
)
|
|
40
|
+
},
|
|
41
|
+
async saveState(protocol, subjectId, state) {
|
|
42
|
+
states.set(storeKey(protocol, subjectId), structuredClone(state))
|
|
43
|
+
},
|
|
44
|
+
}
|
|
45
|
+
}
|