openvisio-agent 0.18.12 → 0.18.13
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/bin/cli.mjs +0 -0
- package/package.json +1 -1
- package/scripts/certify.mjs +1 -0
- package/src/codex-mcp-proxy.mjs +8 -2
- package/src/events.mjs +11 -0
- package/src/watch.mjs +3 -4
package/bin/cli.mjs
CHANGED
|
File without changes
|
package/package.json
CHANGED
package/scripts/certify.mjs
CHANGED
|
@@ -68,6 +68,7 @@ const assertions = [
|
|
|
68
68
|
['reply delivery keys survive reconnects', watcher.includes('deliveredReplies: [...deliveredReplies]') && watcher.includes('deliveredReplies.has(deliveryKey)')],
|
|
69
69
|
['tickets and guarded replies use the behavior-tested serial queue', watcher.includes('createCycleQueue({') && watcher.includes('queues[laneName].enqueue') && cycleQueue.includes('const pending = new Map()')],
|
|
70
70
|
['queued assignments are checked again before model execution', watcher.includes('queued ticket no longer actionable; skipped before model start')],
|
|
71
|
+
['feature titles cannot be mistaken for coordination commands', watcher.includes('taskIsCoordinationOnly(taskText)') && events.includes('Uploading files as messages') && events.includes('explicitCoordination')],
|
|
71
72
|
['reply runner has no coding workspace or coding charter', watcher.includes("workdir: '', systemPrompt: CHAT_CHARTER") && opencodeConfig.includes("'*': 'deny'")],
|
|
72
73
|
['MCP requests have a deadline including response bodies', mcpHttp.includes('controller.abort()') && mcpHttp.includes('const body = await res.text()')],
|
|
73
74
|
['BYO memory uses real ticket and thread identities', watcher.includes('createByoMemoryGraph') && watcher.includes('memory.context(memoryRefs)') && memory.includes('sameRef(r.projectId, refs.projectId)') && memory.includes('sameRef(r.threadId, refs.threadId)')],
|
package/src/codex-mcp-proxy.mjs
CHANGED
|
@@ -24,8 +24,14 @@ export function runCodexMcpProxy() {
|
|
|
24
24
|
if (!url || !apiKey || !identifier) throw new Error('OpenVisio Codex MCP bridge is missing its watcher environment.')
|
|
25
25
|
const client = createMcpHttpClient({ url, apiKey, identifier, clientVersion: 'openvisio-agent-codex-proxy' })
|
|
26
26
|
const reply = (id, result) => process.stdout.write(JSON.stringify({ jsonrpc: '2.0', id, result }) + '\n')
|
|
27
|
-
const fail = (id, error) => {
|
|
27
|
+
const fail = (id, error, request) => {
|
|
28
28
|
const message = String(error?.message || error || 'MCP bridge error').split(apiKey).join('[redacted]')
|
|
29
|
+
if (request?.method === 'tools/call') {
|
|
30
|
+
const safeArgs = { ...(request.params?.arguments || {}) }
|
|
31
|
+
delete safeArgs.agent_api_key
|
|
32
|
+
delete safeArgs.agent_identifier
|
|
33
|
+
process.stderr.write(`OpenVisio MCP bridge call failed: ${String(request.params?.name || 'unknown')} ${JSON.stringify(safeArgs).slice(0, 1000)} — ${message.slice(0, 1000)}\n`)
|
|
34
|
+
}
|
|
29
35
|
process.stdout.write(JSON.stringify({ jsonrpc: '2.0', id, error: { code: -32000, message: message.slice(0, 1000) } }) + '\n')
|
|
30
36
|
}
|
|
31
37
|
const lines = createInterface({ input: process.stdin, crlfDelay: Infinity })
|
|
@@ -53,7 +59,7 @@ export function runCodexMcpProxy() {
|
|
|
53
59
|
process.stdout.write(JSON.stringify({ jsonrpc: '2.0', id, error: { code: -32601, message: 'Method not found' } }) + '\n')
|
|
54
60
|
}
|
|
55
61
|
} catch (error) {
|
|
56
|
-
if (id != null) fail(id, error)
|
|
62
|
+
if (id != null) fail(id, error, request)
|
|
57
63
|
}
|
|
58
64
|
})
|
|
59
65
|
}
|
package/src/events.mjs
CHANGED
|
@@ -471,6 +471,17 @@ export function conversationNeedsCode(value) {
|
|
|
471
471
|
return action && target
|
|
472
472
|
}
|
|
473
473
|
|
|
474
|
+
// Coordination tickets are imperative board/chat actions, not implementation
|
|
475
|
+
// work that merely mentions words such as "message", "status", or "label" in
|
|
476
|
+
// its feature title. Requiring the coordination verb at the start prevents
|
|
477
|
+
// tickets like "Uploading files as messages" from being routed away from the
|
|
478
|
+
// coding lane.
|
|
479
|
+
export function taskIsCoordinationOnly(value) {
|
|
480
|
+
const text = String(value || '').trim()
|
|
481
|
+
const explicitCoordination = /^(?:please\s+)?(?:move|moving|assign|reassign|unassign|comment(?:\s+on)?|reply(?:\s+to)?|send\s+(?:a\s+)?message|triage|prioriti[sz]e|rename|close|reopen|(?:add|remove|change|update)\s+(?:the\s+)?(?:status|column|label))\b/i.test(text)
|
|
482
|
+
return explicitCoordination && !conversationNeedsCode(text)
|
|
483
|
+
}
|
|
484
|
+
|
|
474
485
|
// A mention event means this agent's name appeared somewhere, not necessarily
|
|
475
486
|
// that the request was addressed to it. Reject a later-agent hand-off before a
|
|
476
487
|
// model starts, while keeping explicitly shared requests addressed to both.
|
package/src/watch.mjs
CHANGED
|
@@ -11,7 +11,7 @@ import { join, dirname } from 'node:path'
|
|
|
11
11
|
import { fileURLToPath } from 'node:url'
|
|
12
12
|
import { OV_DIR, DEFAULT_WORKSPACE, readConfig, writeJson, configPath, onPath, fail, ok, info, slugify, stripSlash, chmodSafe } from './lib.mjs'
|
|
13
13
|
import { connectAgentWs, assertWebSocket } from './ws.mjs'
|
|
14
|
-
import { agentAddedByName, agentStateRequest, buildTaskCompletionReport, claudeEventEvidence, classifyConversationTarget, codexEventEvidence, codexPolicyBlock, combineRuntimeWorkEvidence, conversationNeedsCode, mentionDedupeKeys, missingRuntimeWorkEvidence, normalizeRenderedMessageText, opencodeEventEvidence, renderedAgentMessages, shouldSuppressCodexDiagnostic, taskAgentId, taskFromEvent, taskIsAwaitingReview, taskIsCompleted, ticketDisplaySlug } from './events.mjs'
|
|
14
|
+
import { agentAddedByName, agentStateRequest, buildTaskCompletionReport, claudeEventEvidence, classifyConversationTarget, codexEventEvidence, codexPolicyBlock, combineRuntimeWorkEvidence, conversationNeedsCode, mentionDedupeKeys, missingRuntimeWorkEvidence, normalizeRenderedMessageText, opencodeEventEvidence, renderedAgentMessages, shouldSuppressCodexDiagnostic, taskAgentId, taskFromEvent, taskIsAwaitingReview, taskIsCompleted, taskIsCoordinationOnly, ticketDisplaySlug } from './events.mjs'
|
|
15
15
|
import { createByoMemoryGraph } from './memory.mjs'
|
|
16
16
|
import { repositoryHasPrPushAuthorization } from './pr-push.mjs'
|
|
17
17
|
import { createMcpHttpClient } from './mcp-http.mjs'
|
|
@@ -1479,7 +1479,6 @@ function loopBackendWs({ backend, wsUrl, apiKey, identifier, slug, claude, agent
|
|
|
1479
1479
|
|
|
1480
1480
|
// Route only concrete repository requests to the coding lane. Conversation
|
|
1481
1481
|
// ownership is classified separately before this is consulted.
|
|
1482
|
-
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 || '')) && !conversationNeedsCode(value)
|
|
1483
1482
|
|
|
1484
1483
|
// Engineers change the model under the hood from chat: "/model", "/model sonnet",
|
|
1485
1484
|
// "use model haiku", "switch model to opus". Returns {report} | {set} | {invalid}.
|
|
@@ -1567,13 +1566,13 @@ function loopBackendWs({ backend, wsUrl, apiKey, identifier, slug, claude, agent
|
|
|
1567
1566
|
const ticketSlug = ticketDisplaySlug(ticket)
|
|
1568
1567
|
memory.remember({ key: `ticket:${projectId}:${ticketId}`, kind: 'ticket', state: 'queued', summary: title, refs: { projectId, ticketId }, meta: { sourceEvent: kind } })
|
|
1569
1568
|
const taskText = [title, ticket.description, ticket.type, ticket.kind].filter(Boolean).join(' ')
|
|
1570
|
-
const cycleKind = canCode && !
|
|
1569
|
+
const cycleKind = canCode && !taskIsCoordinationOnly(taskText) ? 'full' : 'coord'
|
|
1571
1570
|
log(kind + ' verified ticket #' + ticketId + ' “' + title + '” -> ' + cycleKind + ' lane')
|
|
1572
1571
|
const activityChannel = await projectStatusChannel(projectId)
|
|
1573
1572
|
if (activityChannel != null) sendStatus(activityChannel, 'thinking')
|
|
1574
1573
|
if (cycleKind === 'full') { pendingCompletionReports.add(key); trimSeen(pendingCompletionReports); persistReplay() }
|
|
1575
1574
|
const humanTicketRef = ticketSlug ? `ticket ${ticketSlug}` : `the ticket “${title}”`
|
|
1576
|
-
void drain(cycleKind, `Authoritative get_ticket verification confirms ${humanTicketRef} is open and assigned to YOU. Internal tool identity: project_id ${projectId}, ticket_id ${ticketId}. Numeric ids are MCP arguments only and must never appear in human-facing text; use ${ticketSlug || 'the ticket title'} instead. Ticket details: ${JSON.stringify({ slug: ticketSlug, title, description: ticket.description, priority: ticket.priority, typeId: ticket.type_id ?? ticket.typeId })}. This assignment has no source thread: do not call post_message yourself. Use list_task_types and update_ticket to move it active, complete and verify the work, open the PR when applicable, then update/move the ticket with evidence. For coding work, the watcher will publish exactly one verified completion result in the project channel.`, activityChannel == null ? [] : [activityChannel], { projectId, ticketId, channelId: activityChannel })
|
|
1575
|
+
void drain(cycleKind, `Authoritative get_ticket verification confirms ${humanTicketRef} is open and assigned to YOU. Internal tool identity: project_id ${projectId}, ticket_id ${ticketId}. For every get_ticket call pass exactly { project_id: ${projectId}, ticket_id: ${ticketId} }; for list_task_types pass exactly { project_id: ${projectId} }. Numeric ids are MCP arguments only and must never appear in human-facing text; use ${ticketSlug || 'the ticket title'} instead. Ticket details: ${JSON.stringify({ slug: ticketSlug, title, description: ticket.description, priority: ticket.priority, typeId: ticket.type_id ?? ticket.typeId })}. This assignment has no source thread: do not call post_message yourself. Use list_task_types and update_ticket to move it active, complete and verify the work, open the PR when applicable, then update/move the ticket with evidence. For coding work, the watcher will publish exactly one verified completion result in the project channel.`, activityChannel == null ? [] : [activityChannel], { projectId, ticketId, channelId: activityChannel })
|
|
1577
1576
|
} catch (e) {
|
|
1578
1577
|
log(kind + ' ticket verification failed for #' + ticketId + ': ' + (e?.message || e))
|
|
1579
1578
|
}
|