openvisio-agent 0.11.0 → 0.11.1
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/package.json +1 -1
- package/src/watch.mjs +38 -10
package/package.json
CHANGED
package/src/watch.mjs
CHANGED
|
@@ -81,7 +81,7 @@ const CODE_CHARTER = [
|
|
|
81
81
|
|
|
82
82
|
const CODE_FULL = [
|
|
83
83
|
'THIS CYCLE: call get_marching_orders and poll_inbox to see assigned tickets + mentions, then act on them.',
|
|
84
|
-
'
|
|
84
|
+
'DO NOT post a promise or pre-work acknowledgement. Start the repository work immediately. Your first task/channel update must contain either a verified result (branch, commit, PR, tests) or a concrete blocker you actually encountered.',
|
|
85
85
|
'For real code work (an assigned ticket, or a mention asking for changes), run the full flow end-to-end:',
|
|
86
86
|
' 1. GET THE CODE: use verified thread/history/recall context first, locate the target repo under your workspace root, and read its AGENTS.md. Reuse an existing clone; clone only if absent. Check `git status` before changing anything and preserve unrelated user work. Update from the remote only when it is safe. Do this yourself; never ask the user for a path you can discover.',
|
|
87
87
|
' 2. BRANCH: git checkout -B agent/<short-task-slug>. NEVER work on, commit to, or push main/master.',
|
|
@@ -348,8 +348,20 @@ function createCodexRunner({ mcpUrl, mcpHeaders, workdir, canCode, maxCycleMs, l
|
|
|
348
348
|
...(m ? ['--model', m] : []),
|
|
349
349
|
...(mcpOverride ? ['-c', mcpOverride] : []),
|
|
350
350
|
full]
|
|
351
|
-
let child = null, done = false
|
|
352
|
-
const finish = (o) => { if (done) return; done = true; clearTimeout(timer); resolve(o) }
|
|
351
|
+
let child = null, done = false, didCode = false, didMessage = false, outputText = '', jsonlBuffer = ''
|
|
352
|
+
const finish = (o) => { if (done) return; done = true; clearTimeout(timer); resolve({ ...o, didCode, didMessage, outputText }) }
|
|
353
|
+
const inspectLine = (line) => {
|
|
354
|
+
const s = line.trim()
|
|
355
|
+
if (!s) return
|
|
356
|
+
if (/command_execution|file_change|apply_patch|shell_command|exec_command/i.test(s)) didCode = true
|
|
357
|
+
if (/post_message|comment_ticket/i.test(s)) didMessage = true
|
|
358
|
+
try {
|
|
359
|
+
const event = JSON.parse(s)
|
|
360
|
+
const item = event.item ?? event
|
|
361
|
+
if (item?.type === 'agent_message' && typeof item.text === 'string') outputText += ' ' + item.text
|
|
362
|
+
} catch { /* non-JSON diagnostic */ }
|
|
363
|
+
if (debug) log(' · ' + s.slice(0, 220))
|
|
364
|
+
}
|
|
353
365
|
const timer = setTimeout(() => {
|
|
354
366
|
log('codex cycle TIMED OUT after ' + Math.round(maxCycleMs / 1000) + 's — killing')
|
|
355
367
|
try { child && child.kill() } catch { /* gone */ }
|
|
@@ -357,15 +369,20 @@ function createCodexRunner({ mcpUrl, mcpHeaders, workdir, canCode, maxCycleMs, l
|
|
|
357
369
|
}, maxCycleMs)
|
|
358
370
|
log('running codex cycle…' + (m ? ' [' + m + ']' : ''))
|
|
359
371
|
try {
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
372
|
+
// Always inspect Codex JSONL so a successful process exit cannot be
|
|
373
|
+
// mistaken for completed work. Keep it out of normal logs unless debug.
|
|
374
|
+
child = spawn(bin, args, { cwd, stdio: ['ignore', 'pipe', 'inherit'] })
|
|
375
|
+
if (child.stdout) child.stdout.on('data', (d) => {
|
|
376
|
+
jsonlBuffer += String(d)
|
|
377
|
+
const lines = jsonlBuffer.split('\n')
|
|
378
|
+
jsonlBuffer = lines.pop() ?? ''
|
|
379
|
+
for (const line of lines) inspectLine(line)
|
|
363
380
|
})
|
|
364
381
|
} catch (e) {
|
|
365
382
|
log('codex spawn failed: ' + (e && e.message ? e.message : e) + ' — is Codex installed and signed in? (`npm i -g @openai/codex`, then `codex login`)')
|
|
366
383
|
return finish({ type: 'result', subtype: 'spawn-failed' })
|
|
367
384
|
}
|
|
368
|
-
child.on('
|
|
385
|
+
child.on('close', (code) => { inspectLine(jsonlBuffer); jsonlBuffer = ''; log('codex cycle done (' + (code === 0 ? 'ok' : 'exit ' + code) + ')'); finish({ type: 'result', subtype: code === 0 ? 'ok' : 'error' }) })
|
|
369
386
|
child.on('error', (e) => { log('codex error: ' + (e && e.message ? e.message : e)); finish({ type: 'result', subtype: 'error' }) })
|
|
370
387
|
})
|
|
371
388
|
}
|
|
@@ -561,7 +578,14 @@ function loopBackendWs({ wsUrl, apiKey, identifier, slug, claude, agent, mcpConf
|
|
|
561
578
|
emitStatus('working')
|
|
562
579
|
const heartbeat = targets.length ? setInterval(() => emitStatus('working'), 9000) : null
|
|
563
580
|
try {
|
|
564
|
-
await runCycle(prompt, useModel)
|
|
581
|
+
const result = await runCycle(prompt, useModel)
|
|
582
|
+
// Codex can exit successfully after posting only "I'll do it". For coding
|
|
583
|
+
// cycles, treat that as incomplete and give it one focused recovery turn.
|
|
584
|
+
// Do not retry a real blocker, which would only create duplicate noise.
|
|
585
|
+
if (agent === 'codex' && kind === 'full' && result?.subtype === 'ok' && result.didMessage && !result.didCode && !/\b(?:blocked|cannot|can't|missing|need access|permission|unclear)\b/i.test(result.outputText || '')) {
|
|
586
|
+
log('coding cycle only acknowledged the task; running one completion recovery')
|
|
587
|
+
await runCycle('You posted an acknowledgement but performed no repository work. Do not post another acknowledgement. Resume the same assigned task now: inspect the workspace, make the required changes, verify them, commit and push an agent/* branch, open the PR, then post exactly one result update with evidence. If a real blocker appears, report that blocker once.', codeModel)
|
|
588
|
+
}
|
|
565
589
|
} finally {
|
|
566
590
|
if (heartbeat) clearInterval(heartbeat)
|
|
567
591
|
for (const c of targets) { try { handle && handle.sendStatus(c, 'done') } catch { /* noop */ } statusTargets.delete(c) }
|
|
@@ -580,7 +604,8 @@ function loopBackendWs({ wsUrl, apiKey, identifier, slug, claude, agent, mcpConf
|
|
|
580
604
|
|
|
581
605
|
// Route obvious repository work to the coding lane. Everything else, including
|
|
582
606
|
// chat and board movement, stays on the lightweight coordination model.
|
|
583
|
-
const needsCode = (value) => /\b(?:code|coding|implement|implementation|fix|bug|debug|refactor|test|tests|build|compile|repository|repo|github|git|branch|commit|pull request|pr|endpoint|api|component|function|class|database|migration|schema|deploy|release|package|npm|typescript|javascript|python|swift|rust|golang|css|html|file|files)\b/i.test(String(value || ''))
|
|
607
|
+
const needsCode = (value) => /\b(?:code|coding|implement|implementation|create|make|design|fix|bug|debug|refactor|test|tests|build|compile|repository|repo|github|git|branch|commit|pull request|pr|endpoint|api|component|feature|page|screen|ui|function|class|database|migration|schema|deploy|release|package|npm|typescript|javascript|python|swift|rust|golang|css|html|file|files|documentation)\b/i.test(String(value || ''))
|
|
608
|
+
const coordinationOnly = (value) => /\b(?:move|moving|status|column|assign|reassign|unassign|comment|reply|message|triage|prioriti[sz]e|label|rename|close|reopen)\b/i.test(String(value || '')) && !needsCode(value)
|
|
584
609
|
|
|
585
610
|
// Engineers change the model under the hood from chat: "/model", "/model sonnet",
|
|
586
611
|
// "use model haiku", "switch model to opus". Returns {report} | {set} | {invalid}.
|
|
@@ -633,7 +658,10 @@ function loopBackendWs({ wsUrl, apiKey, identifier, slug, claude, agent, mcpConf
|
|
|
633
658
|
log('task ' + k.slice(5) + ' #' + (t.id != null ? t.id : '?') + ' (agent ' + agentId + ') “' + (t.title || '') + '”')
|
|
634
659
|
const desc = t.description ? ' — ' + String(t.description).replace(/\s+/g, ' ').slice(0, 400) : ''
|
|
635
660
|
const taskText = [t.title, t.description, t.type, t.kind, Array.isArray(t.labels) ? t.labels.join(' ') : t.labels].filter(Boolean).join(' ')
|
|
636
|
-
|
|
661
|
+
// An assigned task is presumed to require execution. Only explicit board or
|
|
662
|
+
// messaging work stays on the cheap lane; vague verbs such as "create",
|
|
663
|
+
// "make", or "improve" must not strand a coding task in coordination.
|
|
664
|
+
const kind = canCode && !coordinationOnly(taskText) ? 'full' : 'coord'
|
|
637
665
|
void drain(kind, `A task was just ${k === 'task:created' ? 'created and assigned' : 'assigned'} to an agent in this workspace: task #${t.id != null ? t.id : '?'}: "${t.title || ''}"${desc} (agent_id ${agentId}). Call get_marching_orders to confirm it is assigned to YOU. If it is yours, acknowledge it once, complete it with the tools appropriate to this ${kind === 'full' ? 'coding' : 'coordination'} lane, move the ticket when appropriate, and report the verified result. If it is not yours, do nothing and stop.`)
|
|
638
666
|
} else if (k === 'agent:mention') {
|
|
639
667
|
const cid = raw.channel_id != null ? raw.channel_id : (raw.channelId != null ? raw.channelId : null)
|