Spaces:
Running
Running
Agent Manager commited on
Commit ·
4a68cda
1
Parent(s): 0d13486
Skip OpenCode title generation in managed runs
Browse files- server/default-agent.js +29 -2
- test/default-agent.js +8 -2
server/default-agent.js
CHANGED
|
@@ -309,7 +309,7 @@ async function runOpenCode(username, accessToken, prompt) {
|
|
| 309 |
try {
|
| 310 |
;({ stdout } = await execFileAsync(
|
| 311 |
OPENCODE_BIN,
|
| 312 |
-
|
| 313 |
{ env, timeout: RUN_TIMEOUT_MS, maxBuffer: 6 * 1024 * 1024 }
|
| 314 |
))
|
| 315 |
} catch (err) {
|
|
@@ -330,6 +330,23 @@ async function runOpenCode(username, accessToken, prompt) {
|
|
| 330 |
return parts.join('\n')
|
| 331 |
}
|
| 332 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 333 |
async function withRunSlot(run) {
|
| 334 |
if (activeRuns >= MAX_CONCURRENT_RUNS) {
|
| 335 |
await new Promise(resolve => runWaiters.push(resolve))
|
|
@@ -393,7 +410,17 @@ export function openCodeProcessSummary(err, secrets = []) {
|
|
| 393 |
if (counts.size) details.push(`events=${[...counts].map(([type, count]) => `${type}:${count}`).join(',')}`)
|
| 394 |
if (textParts) details.push(`text_parts=${textParts}`)
|
| 395 |
if (providerErrors.length) details.push(`provider=${providerErrors.join(' | ')}`)
|
| 396 |
-
const
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 397 |
if (stderrBytes) details.push(`stderr_bytes=${stderrBytes}`)
|
| 398 |
if (!details.length) details.push(`process_error=${err?.name || 'unknown'}`)
|
| 399 |
return safeError(details.join('; '), secrets)
|
|
|
|
| 309 |
try {
|
| 310 |
;({ stdout } = await execFileAsync(
|
| 311 |
OPENCODE_BIN,
|
| 312 |
+
openCodeArgs(dirs.workspace, prompt),
|
| 313 |
{ env, timeout: RUN_TIMEOUT_MS, maxBuffer: 6 * 1024 * 1024 }
|
| 314 |
))
|
| 315 |
} catch (err) {
|
|
|
|
| 330 |
return parts.join('\n')
|
| 331 |
}
|
| 332 |
|
| 333 |
+
export function openCodeArgs(workspace, prompt) {
|
| 334 |
+
return [
|
| 335 |
+
'run',
|
| 336 |
+
'--pure',
|
| 337 |
+
'--auto',
|
| 338 |
+
'--format', 'json',
|
| 339 |
+
'--print-logs',
|
| 340 |
+
'--log-level', 'INFO',
|
| 341 |
+
// Without an explicit title OpenCode makes a hidden preliminary model
|
| 342 |
+
// request before the actual task. That call can hang in headless workers.
|
| 343 |
+
'--title', 'Cowrite task',
|
| 344 |
+
'--model', OPENCODE_MODEL,
|
| 345 |
+
'--dir', workspace,
|
| 346 |
+
prompt,
|
| 347 |
+
]
|
| 348 |
+
}
|
| 349 |
+
|
| 350 |
async function withRunSlot(run) {
|
| 351 |
if (activeRuns >= MAX_CONCURRENT_RUNS) {
|
| 352 |
await new Promise(resolve => runWaiters.push(resolve))
|
|
|
|
| 410 |
if (counts.size) details.push(`events=${[...counts].map(([type, count]) => `${type}:${count}`).join(',')}`)
|
| 411 |
if (textParts) details.push(`text_parts=${textParts}`)
|
| 412 |
if (providerErrors.length) details.push(`provider=${providerErrors.join(' | ')}`)
|
| 413 |
+
const stderr = String(err?.stderr || '')
|
| 414 |
+
const stagePatterns = [
|
| 415 |
+
['creating instance', 'instance'],
|
| 416 |
+
['bootstrapping', 'bootstrap'],
|
| 417 |
+
['event connected', 'event_connected'],
|
| 418 |
+
['llm runtime selected', 'llm_selected'],
|
| 419 |
+
['message=process ', 'task_processing'],
|
| 420 |
+
]
|
| 421 |
+
const stages = stagePatterns.filter(([pattern]) => stderr.includes(pattern)).map(([, stage]) => stage)
|
| 422 |
+
if (stages.length) details.push(`stages=${stages.join(',')}`)
|
| 423 |
+
const stderrBytes = Buffer.byteLength(stderr)
|
| 424 |
if (stderrBytes) details.push(`stderr_bytes=${stderrBytes}`)
|
| 425 |
if (!details.length) details.push(`process_error=${err?.name || 'unknown'}`)
|
| 426 |
return safeError(details.join('; '), secrets)
|
test/default-agent.js
CHANGED
|
@@ -5,7 +5,7 @@ import path from 'node:path'
|
|
| 5 |
|
| 6 |
const data = fs.mkdtempSync(path.join(os.tmpdir(), 'cowrite-default-agent-'))
|
| 7 |
process.env.DATA_DIR = data
|
| 8 |
-
const { defaultAgentHandle, ensureDefaultAgent, openCodeProcessSummary, parseAgentAnswer, prepareOpenCode, safeError } = await import('../server/default-agent.js')
|
| 9 |
|
| 10 |
const agents = {
|
| 11 |
'alice-cowrite': { owner: 'someone-else' },
|
|
@@ -34,6 +34,11 @@ assert.equal(config.permission.websearch, 'allow')
|
|
| 34 |
assert.equal(config.permission.webfetch, 'deny')
|
| 35 |
assert.equal(config.provider['cowrite-hf'].options.apiKey, '{env:HF_OAUTH_TOKEN}', 'config references the in-memory token by environment name')
|
| 36 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 37 |
const secret = 'hf_oauth_secret-value_with-more'
|
| 38 |
assert.equal(safeError(new Error(`provider rejected ${secret}`), [secret]).includes('secret-value'), false)
|
| 39 |
assert.equal(safeError(new Error('provider rejected hf_oauth_tail-with_more')).includes('tail'), false)
|
|
@@ -46,11 +51,12 @@ const processSummary = openCodeProcessSummary({
|
|
| 46 |
JSON.stringify({ type: 'text', part: { text: 'private document output' } }),
|
| 47 |
JSON.stringify({ type: 'error', error: { data: { statusCode: 429, message: `retry ${secret}` } } }),
|
| 48 |
].join('\n'),
|
| 49 |
-
stderr: 'private stderr',
|
| 50 |
}, [secret])
|
| 51 |
assert.match(processSummary, /timeout_or_kill/)
|
| 52 |
assert.match(processSummary, /events=step_start:1,text:1,error:1/)
|
| 53 |
assert.match(processSummary, /provider=429 retry \[redacted\]/)
|
|
|
|
| 54 |
assert.doesNotMatch(processSummary, /private document output|private stderr|secret-value/)
|
| 55 |
|
| 56 |
assert.deepEqual(parseAgentAnswer('```json\n{"reply":"Done","suggestions":[],"dismiss":false}\n```'), {
|
|
|
|
| 5 |
|
| 6 |
const data = fs.mkdtempSync(path.join(os.tmpdir(), 'cowrite-default-agent-'))
|
| 7 |
process.env.DATA_DIR = data
|
| 8 |
+
const { defaultAgentHandle, ensureDefaultAgent, openCodeArgs, openCodeProcessSummary, parseAgentAnswer, prepareOpenCode, safeError } = await import('../server/default-agent.js')
|
| 9 |
|
| 10 |
const agents = {
|
| 11 |
'alice-cowrite': { owner: 'someone-else' },
|
|
|
|
| 34 |
assert.equal(config.permission.webfetch, 'deny')
|
| 35 |
assert.equal(config.provider['cowrite-hf'].options.apiKey, '{env:HF_OAUTH_TOKEN}', 'config references the in-memory token by environment name')
|
| 36 |
|
| 37 |
+
const commandArgs = openCodeArgs('/isolated/workspace', 'private prompt')
|
| 38 |
+
assert.deepEqual(commandArgs.slice(0, 4), ['run', '--pure', '--auto', '--format'])
|
| 39 |
+
assert.equal(commandArgs[commandArgs.indexOf('--title') + 1], 'Cowrite task')
|
| 40 |
+
assert.equal(commandArgs.at(-1), 'private prompt')
|
| 41 |
+
|
| 42 |
const secret = 'hf_oauth_secret-value_with-more'
|
| 43 |
assert.equal(safeError(new Error(`provider rejected ${secret}`), [secret]).includes('secret-value'), false)
|
| 44 |
assert.equal(safeError(new Error('provider rejected hf_oauth_tail-with_more')).includes('tail'), false)
|
|
|
|
| 51 |
JSON.stringify({ type: 'text', part: { text: 'private document output' } }),
|
| 52 |
JSON.stringify({ type: 'error', error: { data: { statusCode: 429, message: `retry ${secret}` } } }),
|
| 53 |
].join('\n'),
|
| 54 |
+
stderr: 'message="creating instance"\nmessage="llm runtime selected" private stderr',
|
| 55 |
}, [secret])
|
| 56 |
assert.match(processSummary, /timeout_or_kill/)
|
| 57 |
assert.match(processSummary, /events=step_start:1,text:1,error:1/)
|
| 58 |
assert.match(processSummary, /provider=429 retry \[redacted\]/)
|
| 59 |
+
assert.match(processSummary, /stages=instance,llm_selected/)
|
| 60 |
assert.doesNotMatch(processSummary, /private document output|private stderr|secret-value/)
|
| 61 |
|
| 62 |
assert.deepEqual(parseAgentAnswer('```json\n{"reply":"Done","suggestions":[],"dismiss":false}\n```'), {
|