openvisio-agent 0.18.9 → 0.18.11
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/scripts/certify.mjs +5 -2
- package/src/codex-config.mjs +17 -0
- package/src/watch.mjs +4 -8
package/package.json
CHANGED
package/scripts/certify.mjs
CHANGED
|
@@ -25,6 +25,7 @@ const events = readFileSync(join(root, 'src', 'events.mjs'), 'utf8')
|
|
|
25
25
|
const memory = readFileSync(join(root, 'src', 'memory.mjs'), 'utf8')
|
|
26
26
|
const mcpHttp = readFileSync(join(root, 'src', 'mcp-http.mjs'), 'utf8')
|
|
27
27
|
const opencodeConfig = readFileSync(join(root, 'src', 'opencode-config.mjs'), 'utf8')
|
|
28
|
+
const codexConfig = readFileSync(join(root, 'src', 'codex-config.mjs'), 'utf8')
|
|
28
29
|
const prPush = readFileSync(join(root, 'src', 'pr-push.mjs'), 'utf8')
|
|
29
30
|
const cli = readFileSync(join(root, 'bin', 'cli.mjs'), 'utf8')
|
|
30
31
|
const websocket = readFileSync(join(root, 'src', 'ws.mjs'), 'utf8')
|
|
@@ -59,7 +60,9 @@ const assertions = [
|
|
|
59
60
|
['generic coding pickup messages are absent', !watcher.includes("I've picked this up and will return here with the verified result")],
|
|
60
61
|
['human-facing ticket references require slugs, never database ids', watcher.includes('TICKET SLUGS, NEVER DATABASE IDS') && watcher.includes('ticketDisplaySlug(task)') && events.includes('export function ticketDisplaySlug') && !events.includes('I finished ticket #${task.id}')],
|
|
61
62
|
['rendered backend replies are checked before every guarded thread post', watcher.includes("callMcpTool('list_message_thread'") && watcher.includes('renderedAgentMessages(live') && watcher.includes('same-content-rendered')],
|
|
62
|
-
['Codex cannot race the watcher with post_message', watcher.includes("disabledMcpTools: ['post_message']") &&
|
|
63
|
+
['Codex cannot race the watcher with post_message', watcher.includes("disabledMcpTools: ['post_message']") && codexConfig.includes('disabled_tools = [')],
|
|
64
|
+
['Codex cannot continue without OpenVisio MCP tools', watcher.includes('buildCodexMcpOverride') && codexConfig.includes('required = true') && codexConfig.includes('startup_timeout_sec = 30')],
|
|
65
|
+
['Codex discovers deferred OpenVisio actions before claiming they are missing', watcher.includes('Codex may defer MCP actions') && watcher.includes('use tool_search to find that exact action') && watcher.includes('initial-list miss is NOT evidence that the server is disconnected')],
|
|
63
66
|
['reply delivery keys survive reconnects', watcher.includes('deliveredReplies: [...deliveredReplies]') && watcher.includes('deliveredReplies.has(deliveryKey)')],
|
|
64
67
|
['tickets and guarded replies use the behavior-tested serial queue', watcher.includes('createCycleQueue({') && watcher.includes('queues[laneName].enqueue') && cycleQueue.includes('const pending = new Map()')],
|
|
65
68
|
['queued assignments are checked again before model execution', watcher.includes('queued ticket no longer actionable; skipped before model start')],
|
|
@@ -89,7 +92,7 @@ const assertions = [
|
|
|
89
92
|
['OpenCode emits structured events for runtime evidence', watcher.includes("'--format', 'json'") && watcher.includes('opencodeEventEvidence(event)')],
|
|
90
93
|
['OpenCode API-key MCP disables OAuth probing', opencodeConfig.includes("oauth: false") && opencodeConfig.includes('timeout: 15_000')],
|
|
91
94
|
['OpenCode identities use isolated configs outside the code workspace', watcher.includes('opencodeRuntimeLayout({ cfgKey, workdir })') && watcher.includes("'--dir', workspace") && watcher.includes('OPENCODE_CONFIG: opencodeConfigPath') && watcher.includes('OPENCODE_CONFIG_CONTENT: JSON.stringify(opencodeConfig)')],
|
|
92
|
-
['OpenCode backend prompts forbid relay and resource-discovery tools', watcher.includes('BACKEND MCP RULE') &&
|
|
95
|
+
['OpenCode backend prompts forbid relay and resource-discovery tools', watcher.includes('BACKEND MCP RULE') && ['get_marching_orders', 'poll_inbox', 'get_resource', 'list_mcp_resources', 'list_mcp_resource_templates'].every((name) => watcher.includes(name))],
|
|
93
96
|
['OpenCode tool failures retain sanitized diagnostics', events.includes('toolError: toolError.replace') && watcher.includes("opencode tool '") && watcher.includes("split(redactKey).join('[redacted]')")],
|
|
94
97
|
['all runtime acknowledgements cannot satisfy coding completion', watcher.includes('missingRuntimeWorkEvidence(result') && watcher.includes('claudeEventEvidence(o, turnToolUses)') && watcher.includes('releaseTaskForRetry(activeTaskRef, prompt)')],
|
|
95
98
|
['Codex prose cannot masquerade as repository evidence', watcher.includes('codexEventEvidence(event)') && events.includes("item.type === 'agent_message'") && events.includes("event.type === 'item.completed'")],
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
const tomlString = (value) => JSON.stringify(String(value))
|
|
2
|
+
|
|
3
|
+
// Codex treats remote MCP servers as optional unless `required` is set. An
|
|
4
|
+
// optional server may fail during startup while the model turn continues without
|
|
5
|
+
// its tools, which makes an otherwise connected agent incorrectly claim that it
|
|
6
|
+
// cannot inspect OpenVisio. Fail the subprocess instead so the watcher can retry
|
|
7
|
+
// the event and report a real runtime failure without delivering model guesswork.
|
|
8
|
+
export function buildCodexMcpOverride({ mcpUrl, mcpHeaders, disabledMcpTools = [] } = {}) {
|
|
9
|
+
if (!mcpUrl) return ''
|
|
10
|
+
const headerEntries = Object.entries(mcpHeaders || {})
|
|
11
|
+
.map(([key, value]) => `${tomlString(key)} = ${tomlString(value)}`)
|
|
12
|
+
.join(', ')
|
|
13
|
+
const disabled = Array.isArray(disabledMcpTools) ? disabledMcpTools.filter(Boolean) : []
|
|
14
|
+
const headerConfig = headerEntries ? `, http_headers = { ${headerEntries} }` : ''
|
|
15
|
+
const disabledToolConfig = disabled.length ? `, disabled_tools = [${disabled.map(tomlString).join(', ')}]` : ''
|
|
16
|
+
return `mcp_servers={ openvisio-team = { url = ${tomlString(mcpUrl)}, required = true, startup_timeout_sec = 30${headerConfig}${disabledToolConfig} } }`
|
|
17
|
+
}
|
package/src/watch.mjs
CHANGED
|
@@ -15,6 +15,7 @@ import { createByoMemoryGraph } from './memory.mjs'
|
|
|
15
15
|
import { repositoryHasPrPushAuthorization } from './pr-push.mjs'
|
|
16
16
|
import { createMcpHttpClient } from './mcp-http.mjs'
|
|
17
17
|
import { buildOpencodeConfig, opencodeRuntimeLayout } from './opencode-config.mjs'
|
|
18
|
+
import { buildCodexMcpOverride } from './codex-config.mjs'
|
|
18
19
|
import { createCycleQueue } from './cycle-queue.mjs'
|
|
19
20
|
import { modelProcessOptions, stopModelProcess } from './process-lifecycle.mjs'
|
|
20
21
|
|
|
@@ -438,8 +439,6 @@ function createOpencodeRunner({ mcpUrl, mcpHeaders, cfgKey, workdir, canCode, ma
|
|
|
438
439
|
function createCodexRunner({ mcpUrl, mcpHeaders, workdir, canCode, maxCycleMs, log, debug, model, onTool, systemPrompt }) {
|
|
439
440
|
const bin = onPath('codex') || 'codex'
|
|
440
441
|
const cwd = workdir || OV_DIR
|
|
441
|
-
const tomlString = (v) => JSON.stringify(String(v))
|
|
442
|
-
const headerEntries = Object.entries(mcpHeaders || {}).map(([k, v]) => `${JSON.stringify(k)} = ${tomlString(v)}`).join(', ')
|
|
443
442
|
let cancelActive = null
|
|
444
443
|
|
|
445
444
|
function runCycle(prompt, cycleModel, cycleOptions = {}) {
|
|
@@ -447,10 +446,7 @@ function createCodexRunner({ mcpUrl, mcpHeaders, workdir, canCode, maxCycleMs, l
|
|
|
447
446
|
const m = cycleModel || model
|
|
448
447
|
const full = systemPrompt ? systemPrompt + '\n\n' + prompt : prompt
|
|
449
448
|
const disabledMcpTools = Array.isArray(cycleOptions.disabledMcpTools) ? cycleOptions.disabledMcpTools.filter(Boolean) : []
|
|
450
|
-
const
|
|
451
|
-
const mcpOverride = mcpUrl
|
|
452
|
-
? `mcp_servers={ openvisio-team = { url = ${tomlString(mcpUrl)}${headerEntries ? `, http_headers = { ${headerEntries} }` : ''}${disabledToolConfig} } }`
|
|
453
|
-
: ''
|
|
449
|
+
const mcpOverride = buildCodexMcpOverride({ mcpUrl, mcpHeaders, disabledMcpTools })
|
|
454
450
|
const args = ['exec', '--ignore-user-config', '--skip-git-repo-check', '--ephemeral', '--json', '--color', 'never',
|
|
455
451
|
...(canCode ? ['--approve-for-me'] : ['--sandbox', 'read-only']),
|
|
456
452
|
...(m ? ['--model', m] : []),
|
|
@@ -769,7 +765,7 @@ function loopBackendWs({ backend, wsUrl, apiKey, identifier, slug, claude, agent
|
|
|
769
765
|
const canCode = !!workdir
|
|
770
766
|
// The Mastra bridge authenticates per-CALL: every openvisio-team tool needs
|
|
771
767
|
// agent_identifier + agent_api_key as arguments. Hand them over up front.
|
|
772
|
-
const credNote = `AUTH: the openvisio-team tools REQUIRE two arguments on EVERY call — agent_identifier: "${identifier}" and agent_api_key: "${apiKey}". Include BOTH on every openvisio-team tool call.
|
|
768
|
+
const credNote = `AUTH: the openvisio-team tools REQUIRE two arguments on EVERY call — agent_identifier: "${identifier}" and agent_api_key: "${apiKey}". Include BOTH on every openvisio-team tool call. Codex may defer MCP actions instead of placing them in the initial tool list. If a native tool_search is available and an expected openvisio-team action is not initially visible, use tool_search to find that exact action, then call it; an initial-list miss is NOT evidence that the server is disconnected. Do not ask a teammate to reconnect or provide a slug until tool_search and the applicable live lookup have actually failed. On backend MCP, discover and update work with list_agents, list_projects, list_tasks, list_task_types, get_ticket, update_ticket, list_channels, list_message_thread, and list_activity as applicable. Ticket comments are optional: never invent or call comment_ticket unless that exact tool appears or tool_search finds it. get_marching_orders, poll_inbox, get_resource, list_mcp_resources, and list_mcp_resource_templates are NOT team-action tools here; never call them. Tools may be namespaced — call whichever names actually appear. The credentials are given here; do NOT hunt for them. Bash/git/gh ARE for code work; this rule only forbids searching for keys.`
|
|
773
769
|
// The STATIC charter + creds are the session system prompt (cached, billed once),
|
|
774
770
|
// NOT re-sent in every cycle's user message — the big token saving.
|
|
775
771
|
const systemPrompt = (canCode ? CODE_CHARTER : CHAT_CHARTER) + '\n\n' + credNote
|
|
@@ -786,7 +782,7 @@ function loopBackendWs({ backend, wsUrl, apiKey, identifier, slug, claude, agent
|
|
|
786
782
|
const codexPushGuide = agent === 'codex' && canCode
|
|
787
783
|
? '\n\nCODEX PR DELIVERY: when the repository exists in the local workspace, use that clone for branch creation, edits, tests, and commits; do not inspect or mutate it through linked-codebase MCP tools. To publish the local agent/* branch, run `openvisio-agent push-pr-branch` from the repository, then open the PR with `gh pr create`. The helper can only push HEAD to the matching agent/* branch on the exact authorized origin. If it reports OPENVISIO_PR_PUSH_AUTH_REQUIRED, do not retry or route around it. Report the one-time command `openvisio-agent authorize-pr-push` as the blocker. Use list_codebases/create_codebase_branch/create_codebase_commit/create_pull_request only as a fallback when the repository cannot be obtained locally.'
|
|
788
784
|
: ''
|
|
789
|
-
const backendToolRule = 'BACKEND MCP RULE: the event and watcher already provide the work source. Never call get_marching_orders, poll_inbox, get_resource, list_mcp_resources,
|
|
785
|
+
const backendToolRule = 'BACKEND MCP RULE: the event and watcher already provide the work source. Never call get_marching_orders, poll_inbox, get_resource, list_mcp_resources, or list_mcp_resource_templates; they are not backend team actions. Codex can defer MCP actions: native tool_search is allowed and must be used to find an expected openvisio-team action that is absent from the initial list. An initial-list miss does not mean the MCP server is unavailable. For live discovery use list_agents, list_projects, list_tasks, list_task_types, list_activity, get_ticket, and list_channels as applicable. Never call comment_ticket unless that exact optional tool is present or tool_search finds it.'
|
|
790
786
|
const fullPrompt = (canCode ? CODE_FULL + codexPushGuide : 'Handle the supplied verified backend ticket with the available OpenVisio tools. Update or comment on the ticket as requested, do not claim repository work in chat-only mode, and stop after the verified action.') + '\n\n' + backendToolRule
|
|
791
787
|
const fastPrompt = (canCode ? CODE_FAST : CYCLE_FAST) + '\n\n' + backendToolRule
|
|
792
788
|
const coordinatePrompt = COORDINATE + '\n\n' + backendToolRule
|