task-while 0.0.4 → 0.0.5
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 +8 -3
- package/bin/task-while.mjs +0 -0
- package/package.json +18 -19
- package/src/agents/claude.ts +35 -27
- package/src/agents/codex.ts +31 -16
- package/src/agents/provider-options.ts +17 -0
- package/src/agents/timeout.ts +41 -0
- package/src/batch/config.ts +2 -0
- package/src/batch/provider.ts +2 -0
- package/src/commands/batch.ts +2 -0
- package/src/commands/run-providers.ts +7 -6
- package/src/core/create-runtime-ports.ts +4 -1
- package/src/core/runtime.ts +1 -0
package/README.md
CHANGED
|
@@ -40,7 +40,7 @@ pnpm exec task-while run
|
|
|
40
40
|
|
|
41
41
|
## Configuration
|
|
42
42
|
|
|
43
|
-
`while.yaml` configures the `run` workflow only. When it is absent, the CLI runs `task.source: spec-kit`, `task.maxIterations: 5`, and `workflow.mode: direct` with `codex` for both roles. Each workflow role accepts provider-specific `model` and `
|
|
43
|
+
`while.yaml` configures the `run` workflow only. When it is absent, the CLI runs `task.source: spec-kit`, `task.maxIterations: 5`, and `workflow.mode: direct` with `codex` for both roles. Each workflow role accepts provider-specific `model`, `effort`, and optional `timeout` in milliseconds.
|
|
44
44
|
|
|
45
45
|
```yaml
|
|
46
46
|
task:
|
|
@@ -53,20 +53,23 @@ workflow:
|
|
|
53
53
|
implementer:
|
|
54
54
|
model: gpt-5-codex
|
|
55
55
|
effort: high
|
|
56
|
+
timeout: 900000
|
|
56
57
|
reviewer:
|
|
57
58
|
model: gpt-5-codex
|
|
58
59
|
effort: high
|
|
60
|
+
timeout: 900000
|
|
59
61
|
```
|
|
60
62
|
|
|
61
63
|
Current status:
|
|
62
64
|
|
|
63
65
|
- `workflow.roles.<role>.provider` accepts `codex` or `claude`; when omitted it defaults to `codex`, including roles that only set `model` and/or `effort`
|
|
66
|
+
- `workflow.roles.<role>.timeout` is optional and sets a per-turn timeout in milliseconds for local agent runs; valid values are positive integers up to `2147483647`
|
|
64
67
|
- `codex` `effort` accepts `minimal`, `low`, `medium`, `high`, or `xhigh`
|
|
65
68
|
- `claude` `effort` accepts `low`, `medium`, `high`, or `max`
|
|
66
69
|
- `workflow.mode: direct` requires `implementer` and `reviewer` to use identical `model` and `effort` when they share the same provider
|
|
67
70
|
- `workflow.mode: direct` uses a local reviewer
|
|
68
71
|
- `workflow.mode: pull-request` pushes a task branch, polls GitHub PR review from `chatgpt-codex-connector[bot]`, then squash-merges on approval
|
|
69
|
-
- in `workflow.mode: pull-request`, reviewer `provider` still selects the remote reviewer, but any local reviewer `model` and `
|
|
72
|
+
- in `workflow.mode: pull-request`, reviewer `provider` still selects the remote reviewer, but any local reviewer `model`, `effort`, and `timeout` values are ignored
|
|
70
73
|
- `workflow.mode: pull-request` currently supports only `codex` as the remote reviewer provider
|
|
71
74
|
- `task.maxIterations` uses the same configured limit for every task in the selected source session; run workflow retries share a single per-task budget across phases
|
|
72
75
|
|
|
@@ -135,6 +138,7 @@ Batch config example:
|
|
|
135
138
|
provider: claude
|
|
136
139
|
model: claude-sonnet-4-6
|
|
137
140
|
effort: max
|
|
141
|
+
timeout: 300000
|
|
138
142
|
glob:
|
|
139
143
|
- 'src/**/*.{ts,tsx}'
|
|
140
144
|
prompt: |
|
|
@@ -157,10 +161,11 @@ Batch behavior:
|
|
|
157
161
|
- `glob` is optional and defaults to `**/*`
|
|
158
162
|
- `glob` is resolved relative to the directory that contains `batch.yaml`
|
|
159
163
|
- `provider`, `prompt`, and `schema` are required
|
|
160
|
-
- `model` and `
|
|
164
|
+
- `model`, `effort`, and `timeout` are optional and are forwarded to the selected provider client
|
|
161
165
|
- batch `provider` accepts `codex` or `claude`
|
|
162
166
|
- batch `codex` `effort` accepts `minimal`, `low`, `medium`, `high`, or `xhigh`
|
|
163
167
|
- batch `claude` `effort` accepts `low`, `medium`, `high`, or `max`
|
|
168
|
+
- batch `timeout` is an optional per-file timeout in milliseconds; valid values are positive integers up to `2147483647`
|
|
164
169
|
- each run scans files under the `batch.yaml` directory and filters them by `glob`
|
|
165
170
|
- structured results are written beside the YAML file in `results.json`
|
|
166
171
|
- internal harness state is written under `.while/harness/` beside the YAML file
|
package/bin/task-while.mjs
CHANGED
|
File without changes
|
package/package.json
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "task-while",
|
|
3
|
-
"version": "0.0.
|
|
4
|
-
"packageManager": "pnpm@10.32.1",
|
|
3
|
+
"version": "0.0.5",
|
|
5
4
|
"description": "Git-first task orchestrator for task-source workspaces",
|
|
6
5
|
"author": "Zhang Yu",
|
|
7
6
|
"license": "MIT",
|
|
@@ -32,23 +31,9 @@
|
|
|
32
31
|
"engines": {
|
|
33
32
|
"node": ">=24"
|
|
34
33
|
},
|
|
35
|
-
"scripts": {
|
|
36
|
-
"dev": "tsx src/index.ts",
|
|
37
|
-
"format": "prettier . --write",
|
|
38
|
-
"format:check": "prettier . --check",
|
|
39
|
-
"lint": "eslint .",
|
|
40
|
-
"lint:fix": "eslint . --fix",
|
|
41
|
-
"smoke:codex": "tsx fixtures/smoke/codex-provider.ts",
|
|
42
|
-
"smoke:e2e:codex": "tsx fixtures/smoke/codex-e2e.ts",
|
|
43
|
-
"smoke:github-pr-snapshot": "tsx fixtures/smoke/github-pr-snapshot.ts",
|
|
44
|
-
"typecheck": "tsc --noEmit",
|
|
45
|
-
"test": "vitest run",
|
|
46
|
-
"test:watch": "vitest",
|
|
47
|
-
"coverage": "vitest run --coverage"
|
|
48
|
-
},
|
|
49
34
|
"dependencies": {
|
|
50
|
-
"@anthropic-ai/claude-agent-sdk": "^0.2.
|
|
51
|
-
"@openai/codex-sdk": "^0.
|
|
35
|
+
"@anthropic-ai/claude-agent-sdk": "^0.2.104",
|
|
36
|
+
"@openai/codex-sdk": "^0.120.0",
|
|
52
37
|
"ajv": "^8.18.0",
|
|
53
38
|
"arg": "^5.0.2",
|
|
54
39
|
"execa": "^8.0.1",
|
|
@@ -68,5 +53,19 @@
|
|
|
68
53
|
"prettier": "^3.8.1",
|
|
69
54
|
"typescript": "^5.9.3",
|
|
70
55
|
"vitest": "4.1.0"
|
|
56
|
+
},
|
|
57
|
+
"scripts": {
|
|
58
|
+
"dev": "tsx src/index.ts",
|
|
59
|
+
"format": "prettier . --write",
|
|
60
|
+
"format:check": "prettier . --check",
|
|
61
|
+
"lint": "eslint .",
|
|
62
|
+
"lint:fix": "eslint . --fix",
|
|
63
|
+
"smoke:codex": "tsx fixtures/smoke/codex-provider.ts",
|
|
64
|
+
"smoke:e2e:codex": "tsx fixtures/smoke/codex-e2e.ts",
|
|
65
|
+
"smoke:github-pr-snapshot": "tsx fixtures/smoke/github-pr-snapshot.ts",
|
|
66
|
+
"typecheck": "tsc --noEmit",
|
|
67
|
+
"test": "vitest run",
|
|
68
|
+
"test:watch": "vitest",
|
|
69
|
+
"coverage": "vitest run --coverage"
|
|
71
70
|
}
|
|
72
|
-
}
|
|
71
|
+
}
|
package/src/agents/claude.ts
CHANGED
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
validateImplementOutput,
|
|
7
7
|
validateReviewOutput,
|
|
8
8
|
} from '../schema/index'
|
|
9
|
+
import { withAbortTimeout } from './timeout'
|
|
9
10
|
|
|
10
11
|
import type { Options as ClaudeQueryOptions } from '@anthropic-ai/claude-agent-sdk'
|
|
11
12
|
|
|
@@ -278,34 +279,41 @@ export class ClaudeAgentClient
|
|
|
278
279
|
|
|
279
280
|
public async invokeStructured<T>(input: ClaudeStructuredInput): Promise<T> {
|
|
280
281
|
const { query } = await import('@anthropic-ai/claude-agent-sdk')
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
282
|
+
return withAbortTimeout(
|
|
283
|
+
this.name,
|
|
284
|
+
this.options.timeout,
|
|
285
|
+
async (controller) => {
|
|
286
|
+
const queryOptions = {
|
|
287
|
+
allowDangerouslySkipPermissions: true,
|
|
288
|
+
cwd: this.options.workspaceRoot,
|
|
289
|
+
permissionMode: 'bypassPermissions',
|
|
290
|
+
outputFormat: {
|
|
291
|
+
schema: input.outputSchema,
|
|
292
|
+
type: 'json_schema',
|
|
293
|
+
},
|
|
294
|
+
...(this.options.onEvent
|
|
295
|
+
? {
|
|
296
|
+
agentProgressSummaries: true,
|
|
297
|
+
includePartialMessages: true,
|
|
298
|
+
}
|
|
299
|
+
: {
|
|
300
|
+
includePartialMessages: false,
|
|
301
|
+
}),
|
|
302
|
+
...(this.options.model ? { model: this.options.model } : {}),
|
|
303
|
+
...(this.options.effort ? { effort: this.options.effort } : {}),
|
|
304
|
+
...(controller ? { abortController: controller } : {}),
|
|
305
|
+
} satisfies ClaudeQueryOptions
|
|
306
|
+
|
|
307
|
+
const messages = query({
|
|
308
|
+
options: queryOptions,
|
|
309
|
+
prompt: input.prompt,
|
|
310
|
+
})
|
|
305
311
|
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
312
|
+
return this.collectStructuredOutput(
|
|
313
|
+
messages as AsyncIterable<QueryMessage>,
|
|
314
|
+
) as Promise<T>
|
|
315
|
+
},
|
|
316
|
+
)
|
|
309
317
|
}
|
|
310
318
|
|
|
311
319
|
public async review(input: ReviewAgentInput) {
|
package/src/agents/codex.ts
CHANGED
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
validateImplementOutput,
|
|
7
7
|
validateReviewOutput,
|
|
8
8
|
} from '../schema/index'
|
|
9
|
+
import { withAbortTimeout } from './timeout'
|
|
9
10
|
|
|
10
11
|
import type { CodexProviderOptions } from './provider-options'
|
|
11
12
|
import type {
|
|
@@ -143,6 +144,7 @@ export interface CodexRunStreamedResult {
|
|
|
143
144
|
|
|
144
145
|
export interface CodexThreadRunOptions {
|
|
145
146
|
outputSchema: Record<string, unknown>
|
|
147
|
+
signal?: AbortSignal
|
|
146
148
|
}
|
|
147
149
|
|
|
148
150
|
export interface CodexThreadLike {
|
|
@@ -190,9 +192,11 @@ export class CodexAgentClient implements ImplementerProvider, ReviewerProvider {
|
|
|
190
192
|
private async collectStreamedTurn<T>(
|
|
191
193
|
thread: CodexThreadLike,
|
|
192
194
|
input: CodexStructuredInput,
|
|
195
|
+
signal?: AbortSignal,
|
|
193
196
|
): Promise<T> {
|
|
194
197
|
const streamedTurn = await thread.runStreamed!(input.prompt, {
|
|
195
198
|
outputSchema: input.outputSchema,
|
|
199
|
+
...(signal ? { signal } : {}),
|
|
196
200
|
})
|
|
197
201
|
let finalResponse = ''
|
|
198
202
|
|
|
@@ -259,23 +263,34 @@ export class CodexAgentClient implements ImplementerProvider, ReviewerProvider {
|
|
|
259
263
|
const thread = client.startThread(startThreadOptions)
|
|
260
264
|
|
|
261
265
|
if (this.options.onEvent && thread.runStreamed) {
|
|
262
|
-
return this.
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
const turn = await thread.run(input.prompt, {
|
|
266
|
-
outputSchema: input.outputSchema,
|
|
267
|
-
})
|
|
268
|
-
const response = turn.finalResponse.trim()
|
|
269
|
-
if (!response) {
|
|
270
|
-
throw new Error('Codex agent client returned empty finalResponse')
|
|
271
|
-
}
|
|
272
|
-
try {
|
|
273
|
-
return JSON.parse(response) as T
|
|
274
|
-
} catch (error) {
|
|
275
|
-
throw new Error('Codex agent client returned non-JSON finalResponse', {
|
|
276
|
-
cause: error,
|
|
277
|
-
})
|
|
266
|
+
return withAbortTimeout(this.name, this.options.timeout, (controller) =>
|
|
267
|
+
this.collectStreamedTurn<T>(thread, input, controller?.signal),
|
|
268
|
+
)
|
|
278
269
|
}
|
|
270
|
+
return withAbortTimeout(
|
|
271
|
+
this.name,
|
|
272
|
+
this.options.timeout,
|
|
273
|
+
async (controller) => {
|
|
274
|
+
const turn = await thread.run(input.prompt, {
|
|
275
|
+
outputSchema: input.outputSchema,
|
|
276
|
+
...(controller ? { signal: controller.signal } : {}),
|
|
277
|
+
})
|
|
278
|
+
const response = turn.finalResponse.trim()
|
|
279
|
+
if (!response) {
|
|
280
|
+
throw new Error('Codex agent client returned empty finalResponse')
|
|
281
|
+
}
|
|
282
|
+
try {
|
|
283
|
+
return JSON.parse(response) as T
|
|
284
|
+
} catch (error) {
|
|
285
|
+
throw new Error(
|
|
286
|
+
'Codex agent client returned non-JSON finalResponse',
|
|
287
|
+
{
|
|
288
|
+
cause: error,
|
|
289
|
+
},
|
|
290
|
+
)
|
|
291
|
+
}
|
|
292
|
+
},
|
|
293
|
+
)
|
|
279
294
|
}
|
|
280
295
|
|
|
281
296
|
public async review(input: ReviewAgentInput) {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { z } from 'zod'
|
|
2
2
|
|
|
3
3
|
const modelSchema = z.string().trim().min(1)
|
|
4
|
+
const timeoutSchema = z.number().int().positive().max(2_147_483_647)
|
|
4
5
|
|
|
5
6
|
export const codexEffortSchema = z.enum([
|
|
6
7
|
'minimal',
|
|
@@ -16,6 +17,7 @@ export const codexProviderOptionsSchema = z
|
|
|
16
17
|
.object({
|
|
17
18
|
effort: codexEffortSchema.optional(),
|
|
18
19
|
model: modelSchema.optional(),
|
|
20
|
+
timeout: timeoutSchema.optional(),
|
|
19
21
|
})
|
|
20
22
|
.strict()
|
|
21
23
|
|
|
@@ -23,6 +25,7 @@ export const claudeProviderOptionsSchema = z
|
|
|
23
25
|
.object({
|
|
24
26
|
effort: claudeEffortSchema.optional(),
|
|
25
27
|
model: modelSchema.optional(),
|
|
28
|
+
timeout: timeoutSchema.optional(),
|
|
26
29
|
})
|
|
27
30
|
.strict()
|
|
28
31
|
|
|
@@ -43,3 +46,17 @@ export function providerOptionsEqual(
|
|
|
43
46
|
left.effort === right.effort
|
|
44
47
|
)
|
|
45
48
|
}
|
|
49
|
+
|
|
50
|
+
export function providerOptionsCacheKey(options: {
|
|
51
|
+
effort?: string | undefined
|
|
52
|
+
model?: string | undefined
|
|
53
|
+
provider: 'claude' | 'codex'
|
|
54
|
+
timeout?: number | undefined
|
|
55
|
+
}) {
|
|
56
|
+
return [
|
|
57
|
+
options.provider,
|
|
58
|
+
options.model ?? '',
|
|
59
|
+
options.effort ?? '',
|
|
60
|
+
String(options.timeout ?? ''),
|
|
61
|
+
].join(':')
|
|
62
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
export class AgentTimeoutError extends Error {
|
|
2
|
+
public constructor(
|
|
3
|
+
public readonly provider: string,
|
|
4
|
+
public readonly timeout: number,
|
|
5
|
+
options?: ErrorOptions,
|
|
6
|
+
) {
|
|
7
|
+
super(`${provider} agent timed out after ${timeout}ms`, options)
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export async function withAbortTimeout<T>(
|
|
12
|
+
provider: string,
|
|
13
|
+
timeout: number | undefined,
|
|
14
|
+
run: (controller: AbortController | undefined) => Promise<T>,
|
|
15
|
+
): Promise<T> {
|
|
16
|
+
if (!timeout) {
|
|
17
|
+
return run(undefined)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const controller = new AbortController()
|
|
21
|
+
const state = { timedOut: false }
|
|
22
|
+
const timer = setTimeout(() => {
|
|
23
|
+
state.timedOut = true
|
|
24
|
+
controller.abort()
|
|
25
|
+
}, timeout)
|
|
26
|
+
|
|
27
|
+
try {
|
|
28
|
+
const result = await run(controller)
|
|
29
|
+
if (state.timedOut) {
|
|
30
|
+
throw new AgentTimeoutError(provider, timeout)
|
|
31
|
+
}
|
|
32
|
+
return result
|
|
33
|
+
} catch (error) {
|
|
34
|
+
if (state.timedOut) {
|
|
35
|
+
throw new AgentTimeoutError(provider, timeout, { cause: error })
|
|
36
|
+
}
|
|
37
|
+
throw error
|
|
38
|
+
} finally {
|
|
39
|
+
clearTimeout(timer)
|
|
40
|
+
}
|
|
41
|
+
}
|
package/src/batch/config.ts
CHANGED
|
@@ -97,6 +97,7 @@ export async function loadBatchConfig(
|
|
|
97
97
|
provider: 'claude',
|
|
98
98
|
...(parsedConfig.model ? { model: parsedConfig.model } : {}),
|
|
99
99
|
...(parsedConfig.effort ? { effort: parsedConfig.effort } : {}),
|
|
100
|
+
...(parsedConfig.timeout ? { timeout: parsedConfig.timeout } : {}),
|
|
100
101
|
}
|
|
101
102
|
}
|
|
102
103
|
|
|
@@ -105,5 +106,6 @@ export async function loadBatchConfig(
|
|
|
105
106
|
provider: 'codex',
|
|
106
107
|
...(parsedConfig.model ? { model: parsedConfig.model } : {}),
|
|
107
108
|
...(parsedConfig.effort ? { effort: parsedConfig.effort } : {}),
|
|
109
|
+
...(parsedConfig.timeout ? { timeout: parsedConfig.timeout } : {}),
|
|
108
110
|
}
|
|
109
111
|
}
|
package/src/batch/provider.ts
CHANGED
|
@@ -70,6 +70,7 @@ export function createBatchStructuredOutputProvider(
|
|
|
70
70
|
new CodexAgentClient({
|
|
71
71
|
...(input.effort ? { effort: input.effort } : {}),
|
|
72
72
|
...(input.model ? { model: input.model } : {}),
|
|
73
|
+
...(input.timeout ? { timeout: input.timeout } : {}),
|
|
73
74
|
...(onEvent ? { onEvent } : {}),
|
|
74
75
|
workspaceRoot: input.workspaceRoot,
|
|
75
76
|
}),
|
|
@@ -81,6 +82,7 @@ export function createBatchStructuredOutputProvider(
|
|
|
81
82
|
new ClaudeAgentClient({
|
|
82
83
|
...(input.effort ? { effort: input.effort } : {}),
|
|
83
84
|
...(input.model ? { model: input.model } : {}),
|
|
85
|
+
...(input.timeout ? { timeout: input.timeout } : {}),
|
|
84
86
|
...(onEvent ? { onEvent } : {}),
|
|
85
87
|
workspaceRoot: input.workspaceRoot,
|
|
86
88
|
}),
|
package/src/commands/batch.ts
CHANGED
|
@@ -101,6 +101,7 @@ function createProvider(
|
|
|
101
101
|
provider: 'codex',
|
|
102
102
|
...(config.effort ? { effort: config.effort } : {}),
|
|
103
103
|
...(config.model ? { model: config.model } : {}),
|
|
104
|
+
...(config.timeout ? { timeout: config.timeout } : {}),
|
|
104
105
|
...(verbose === undefined ? {} : { verbose }),
|
|
105
106
|
workspaceRoot: config.configDir,
|
|
106
107
|
})
|
|
@@ -110,6 +111,7 @@ function createProvider(
|
|
|
110
111
|
provider: 'claude',
|
|
111
112
|
...(config.effort ? { effort: config.effort } : {}),
|
|
112
113
|
...(config.model ? { model: config.model } : {}),
|
|
114
|
+
...(config.timeout ? { timeout: config.timeout } : {}),
|
|
113
115
|
...(verbose === undefined ? {} : { verbose }),
|
|
114
116
|
workspaceRoot: config.configDir,
|
|
115
117
|
})
|
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
createClaudeEventHandler,
|
|
5
5
|
createCodexEventHandler,
|
|
6
6
|
} from '../agents/event-log'
|
|
7
|
+
import { providerOptionsCacheKey } from '../agents/provider-options'
|
|
7
8
|
import { createCodexRemoteReviewerProvider } from '../workflow/remote-reviewer'
|
|
8
9
|
|
|
9
10
|
import type {
|
|
@@ -26,12 +27,10 @@ export function createProviderResolver(
|
|
|
26
27
|
context: WorkspaceContext,
|
|
27
28
|
verbose: boolean | undefined,
|
|
28
29
|
): ProviderResolver {
|
|
29
|
-
const cache = new Map<
|
|
30
|
-
WorkflowProvider,
|
|
31
|
-
ImplementerProvider & ReviewerProvider
|
|
32
|
-
>()
|
|
30
|
+
const cache = new Map<string, ImplementerProvider & ReviewerProvider>()
|
|
33
31
|
return (role: WorkflowRoleConfig) => {
|
|
34
|
-
const
|
|
32
|
+
const cacheKey = providerOptionsCacheKey(role)
|
|
33
|
+
const cached = cache.get(cacheKey)
|
|
35
34
|
if (cached) {
|
|
36
35
|
return cached
|
|
37
36
|
}
|
|
@@ -41,6 +40,7 @@ export function createProviderResolver(
|
|
|
41
40
|
provider = createClaudeProvider({
|
|
42
41
|
...(role.effort ? { effort: role.effort } : {}),
|
|
43
42
|
...(role.model ? { model: role.model } : {}),
|
|
43
|
+
...(role.timeout ? { timeout: role.timeout } : {}),
|
|
44
44
|
workspaceRoot: context.workspaceRoot,
|
|
45
45
|
...(onEvent ? { onEvent } : {}),
|
|
46
46
|
})
|
|
@@ -49,11 +49,12 @@ export function createProviderResolver(
|
|
|
49
49
|
provider = createCodexProvider({
|
|
50
50
|
...(role.effort ? { effort: role.effort } : {}),
|
|
51
51
|
...(role.model ? { model: role.model } : {}),
|
|
52
|
+
...(role.timeout ? { timeout: role.timeout } : {}),
|
|
52
53
|
workspaceRoot: context.workspaceRoot,
|
|
53
54
|
...(onEvent ? { onEvent } : {}),
|
|
54
55
|
})
|
|
55
56
|
}
|
|
56
|
-
cache.set(
|
|
57
|
+
cache.set(cacheKey, provider)
|
|
57
58
|
return provider
|
|
58
59
|
}
|
|
59
60
|
}
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
createClaudeEventHandler,
|
|
11
11
|
createCodexEventHandler,
|
|
12
12
|
} from '../agents/event-log'
|
|
13
|
+
import { providerOptionsCacheKey } from '../agents/provider-options'
|
|
13
14
|
import { GitRuntime } from '../runtime/git'
|
|
14
15
|
import { GitHubRuntime } from '../runtime/github'
|
|
15
16
|
import { createRuntimePaths } from '../runtime/path-layout'
|
|
@@ -54,7 +55,7 @@ export function createRuntimePorts(
|
|
|
54
55
|
const agentCache = new Map<string, AgentPort>()
|
|
55
56
|
|
|
56
57
|
function resolveAgent(role: AgentRoleConfig): AgentPort {
|
|
57
|
-
const key =
|
|
58
|
+
const key = providerOptionsCacheKey(role)
|
|
58
59
|
const cached = agentCache.get(key)
|
|
59
60
|
if (cached) {
|
|
60
61
|
return cached
|
|
@@ -72,6 +73,7 @@ export function createRuntimePorts(
|
|
|
72
73
|
}
|
|
73
74
|
: {}),
|
|
74
75
|
...(role.model ? { model: role.model } : {}),
|
|
76
|
+
...(role.timeout ? { timeout: role.timeout } : {}),
|
|
75
77
|
workspaceRoot: context.workspaceRoot,
|
|
76
78
|
...(onEvent ? { onEvent } : {}),
|
|
77
79
|
})
|
|
@@ -86,6 +88,7 @@ export function createRuntimePorts(
|
|
|
86
88
|
}
|
|
87
89
|
: {}),
|
|
88
90
|
...(role.model ? { model: role.model } : {}),
|
|
91
|
+
...(role.timeout ? { timeout: role.timeout } : {}),
|
|
89
92
|
workspaceRoot: context.workspaceRoot,
|
|
90
93
|
...(onEvent ? { onEvent } : {}),
|
|
91
94
|
})
|