@agfpd/iapeer 0.4.125 → 0.4.126

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agfpd/iapeer",
3
- "version": "0.4.125",
3
+ "version": "0.4.126",
4
4
  "description": "Run Claude Code and Codex CLI as local background agents that message each other, wake on demand, and share one memory.",
5
5
  "license": "Apache-2.0",
6
6
  "author": "agfpd",
@@ -108,6 +108,9 @@ export const opencodeAdapter: RuntimeAdapter = {
108
108
  const warnings = hasExplicitModel(config)
109
109
  ? []
110
110
  : [`opencode-implicit-provider: no explicit "model" in ${p.config} — OpenCode's own default provider receives the doctrine and messages`]
111
+ // Handed to the plugin too, so they reach runtime.ready / state.json / fleet runtimes[].warnings like
112
+ // the self-check findings (not only lifecycle.log).
113
+ if (warnings.length) out.IAPEER_OPENCODE_WARNINGS = JSON.stringify(warnings)
111
114
  return { env: out, readyToken: launchID, warnings }
112
115
  },
113
116
 
@@ -194,13 +194,17 @@ export const launch: LaunchFn = async (
194
194
  // Structural runtimes (opencode) prepare launch artifacts + extra env BEFORE spawn. The identity
195
195
  // ABI stays authoritative: prepared env never overrides PEER_PERSONALITY/RUNTIME/IDENTITY.
196
196
  let readyToken: string | undefined
197
+ const loggedWarnings = new Set<string>()
197
198
  if (adapter.prepareLaunch) {
198
199
  try {
199
200
  const prepared = adapter.prepareLaunch(spec, invocation.env, cfg)
200
201
  const { PEER_PERSONALITY, PEER_RUNTIME, PEER_IDENTITY } = invocation.env
201
202
  invocation.env = { ...invocation.env, ...prepared.env, PEER_PERSONALITY, PEER_RUNTIME, PEER_IDENTITY }
202
203
  readyToken = prepared.readyToken
203
- for (const w of prepared.warnings ?? []) appendLifecycleEvent(cfg.exitLogDir, { ev: 'launch-warning', identity, warning: w }, { env })
204
+ for (const w of prepared.warnings ?? []) {
205
+ loggedWarnings.add(w)
206
+ appendLifecycleEvent(cfg.exitLogDir, { ev: 'launch-warning', identity, warning: w }, { env })
207
+ }
204
208
  } catch (e) {
205
209
  return fail(identity, `launch preparation failed: ${e instanceof Error ? e.message : String(e)}`)
206
210
  }
@@ -241,7 +245,9 @@ export const launch: LaunchFn = async (
241
245
  if (adapter.structuralReady && readyToken) {
242
246
  const r = await adapter.structuralReady(spec, readyToken, Date.now() + cfg.bootDeadlineSecs * 1000, () => hostSessionAlive(identity, env), env)
243
247
  if (!r.ok) return fail(identity, `never-became-ready (structural: ${r.reason ?? 'unknown'})`)
244
- for (const w of r.warnings ?? []) appendLifecycleEvent(cfg.exitLogDir, { ev: 'launch-warning', identity, warning: w }, { env })
248
+ for (const w of r.warnings ?? []) {
249
+ if (!loggedWarnings.has(w)) appendLifecycleEvent(cfg.exitLogDir, { ev: 'launch-warning', identity, warning: w }, { env })
250
+ }
245
251
  if (firstMessage.trim().length === 0) return ready(identity)
246
252
  if (!adapter.deliver) return fail(identity, 'structural runtime has no deliver hook')
247
253
  const d = await adapter.deliver({ personality: spec.personality, identity }, firstMessage, env)
@@ -119,17 +119,20 @@ export function sweepInflight(stateDir: string, identity: string, deps: Inflight
119
119
  }
120
120
  }
121
121
  // A session works its input in order: a FINISHED turn for a later message means every earlier one
122
- // was handled too (bounds the ledger when a turn-end record is missed).
123
- let finishedUpTo = -Infinity
122
+ // OF THE SAME SESSION was handled too (bounds the ledger when a turn-end record is missed). Strictly
123
+ // per session token: a turn finished in a NEW session says nothing about a dead session's queue —
124
+ // that cross-session shortcut silently dropped A and B in 0.4.122 (linus OC07 regression).
125
+ const finishedUpTo = new Map<string, number>()
124
126
  const finished = new Set<string>()
127
+ const sessionKey = (e: InflightEntry) => e.sessionToken ?? '∅'
125
128
  for (const e of entries) {
126
129
  if (safe(deps.completed, e, false) || (!deps.completed && safe(deps.processed, e, false))) {
127
130
  finished.add(e.id)
128
- finishedUpTo = Math.max(finishedUpTo, e.ts)
131
+ finishedUpTo.set(sessionKey(e), Math.max(finishedUpTo.get(sessionKey(e)) ?? -Infinity, e.ts))
129
132
  }
130
133
  }
131
134
  for (const e of entries) {
132
- if (finished.has(e.id) || e.ts < finishedUpTo) continue
135
+ if (finished.has(e.id) || e.ts < (finishedUpTo.get(sessionKey(e)) ?? -Infinity)) continue
133
136
  const done = safe(deps.processed, e, false)
134
137
  if (done) {
135
138
  // Answered but its turn did not finish: live → still working; gone → INTERRUPTED (side effects
@@ -320,6 +320,10 @@ async function server(input) {
320
320
  }
321
321
  async function selfCheck() {
322
322
  const warnings = []
323
+ try {
324
+ const fromCore = JSON.parse(env.IAPEER_OPENCODE_WARNINGS || '[]') // launch-time facts (e.g. implicit provider)
325
+ if (Array.isArray(fromCore)) for (const w of fromCore) if (typeof w === 'string') warnings.push(w)
326
+ } catch {}
323
327
  try {
324
328
  const st = await client.mcp.status()
325
329
  for (const [name, s] of Object.entries((st && st.data) || {})) {