openvisio-agent 0.3.3 → 0.3.4

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/watch.mjs +35 -9
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openvisio-agent",
3
- "version": "0.3.3",
3
+ "version": "0.3.4",
4
4
  "description": "Connect your coding agent (Claude Code) to an OpenVisio team — MCP tools + optional autonomy — in one command. No shell scripts.",
5
5
  "type": "module",
6
6
  "bin": {
package/src/watch.mjs CHANGED
@@ -154,27 +154,53 @@ function loopBackendWs({ wsUrl, apiKey, identifier, claude, mcpConfig, workdir,
154
154
 
155
155
  let busy = false
156
156
  let queued = null // 'full' | 'fast' — a cycle requested while one was running
157
+ // Context lines from the events themselves (the WS payload already carries the
158
+ // channel + message / task), so the agent acts on THEM directly instead of
159
+ // hoping poll_inbox re-surfaces the same item. Accumulated across coalesced
160
+ // events and drained into the next cycle's prompt.
161
+ const pending = []
157
162
 
158
- async function drain(kind) {
163
+ async function drain(kind, context) {
164
+ if (context) pending.push(context)
159
165
  if (busy) { queued = (queued === 'full' || kind === 'full') ? 'full' : 'fast'; log('busy — queued a ' + kind + ' follow-up cycle'); return }
160
166
  busy = true
161
- log('running ' + kind + ' cycle…')
167
+ const ctx = pending.splice(0) // take everything accumulated so far
168
+ const base = kind === 'full' ? fullPrompt : fastPrompt
169
+ const prompt = ctx.length ? ctx.join('\n') + '\n\n' + base : base
170
+ log('running ' + kind + ' cycle…' + (ctx.length ? ' (' + ctx.length + ' event' + (ctx.length === 1 ? '' : 's') + ')' : ''))
162
171
  try {
163
- await runCycle(kind === 'full' ? fullPrompt : fastPrompt)
172
+ await runCycle(prompt)
164
173
  } finally {
165
174
  busy = false
166
- if (queued) { const next = queued; queued = null; void drain(next) }
175
+ if (queued || pending.length) { const next = queued || 'fast'; queued = null; void drain(next) }
167
176
  }
168
177
  }
169
178
 
179
+ // Distill an event's WS payload into a directive the model can act on without
180
+ // any extra lookup.
181
+ const senderName = (m) => {
182
+ const s = m && (m.sender || m.user || m.author)
183
+ if (!s) return ''
184
+ return (`${s.first_name || ''} ${s.last_name || ''}`.trim() || s.name || s.email || '')
185
+ }
186
+
170
187
  function onEvent(k, d) {
188
+ const raw = d && typeof d === 'object' ? d : {}
171
189
  if (k === 'task:assigned') {
172
- const t = d && d.task
173
- log('task:assigned ' + (t ? '#' + t.id + ' “' + (t.title || '') + '”' : ''))
174
- void drain('full')
190
+ const t = raw.task || {}
191
+ log('task:assigned ' + (t.id != null ? '#' + t.id + ' “' + (t.title || '') + '”' : ''))
192
+ const desc = t.description ? ' — ' + String(t.description).replace(/\s+/g, ' ').slice(0, 400) : ''
193
+ void drain('full', t.id != null ? `You were ASSIGNED task #${t.id}: "${t.title || ''}"${desc}. Handle it, then comment_ticket with a short summary.` : undefined)
175
194
  } else if (k === 'agent:mention') {
176
- log('agent:mention in channel ' + (d && d.channel_id != null ? d.channel_id : '?'))
177
- void drain('fast')
195
+ const cid = raw.channel_id != null ? raw.channel_id : (raw.channelId != null ? raw.channelId : null)
196
+ const msg = raw.message && typeof raw.message === 'object' ? raw.message : {}
197
+ const text = String(msg.content || msg.body || msg.text || '').replace(/\s+/g, ' ').slice(0, 600)
198
+ const who = senderName(msg)
199
+ log('agent:mention in channel ' + (cid != null ? cid : '?'))
200
+ const ctx = cid != null
201
+ ? `You were @mentioned in OpenVisio channel ${cid}${who ? ` by ${who}` : ''}: "${text}". Reply DIRECTLY in that channel now with post_message (channel_id ${cid}), 1-3 sentences addressing it. Do NOT depend on poll_inbox to find it — you already have it here. You MAY still call poll_inbox to catch OTHER pending items, but you MUST answer this one.`
202
+ : undefined
203
+ void drain('fast', ctx)
178
204
  } else {
179
205
  log('event ' + k)
180
206
  }