@agfpd/iapeer 0.2.55 → 0.2.57

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.2.55",
3
+ "version": "0.2.57",
4
4
  "description": "Foundation core for the iapeer multi-agent ecosystem: identity, registry, storage, codec.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -77,7 +77,12 @@ describe('listTools', () => {
77
77
  const tools = listTools(readPeersIndex()) as any[]
78
78
  expect(tools.map(t => t.name).sort()).toEqual(['send_to_peer'])
79
79
  expect(tools.every(t => t._meta['anthropic/alwaysLoad'] === true)).toBe(true)
80
- expect(tools.find(t => t.name === 'send_to_peer').description).toContain('arthur')
80
+ const sendTool = tools.find(t => t.name === 'send_to_peer')
81
+ expect(sendTool.description).toContain('arthur')
82
+ expect(sendTool.description).toContain('wakes it and delivers there')
83
+ expect(sendTool.description).toContain('parallel to the default runtime')
84
+ expect(sendTool.inputSchema.properties.runtime.description).toContain('wakes it warm-on-demand')
85
+ expect(sendTool.inputSchema.properties.runtime.description).toContain('parallel session')
81
86
  })
82
87
  })
83
88
 
@@ -98,7 +98,7 @@ export function buildSendDescription(index: PeersIndex): string {
98
98
  }
99
99
  lines.push(
100
100
  '',
101
- "Use `personality`, not a runtime-prefixed identity. Set `runtime` only for a current-send override. Descriptions identify the peer's role; runtimes identify reachable delivery endpoints; intelligence identifies the nature of the peer (artificial / natural / absent). Inbound peer messages arrive as <iap from-personality=\"...\" from-runtime=\"...\" from-intelligence=\"...\"> blocks. The topic attribute is optional.",
101
+ "Use `personality`, not a runtime-prefixed identity. Set `runtime` to target a specific declared runtime for this send. If that runtime is warm-asleep, the daemon wakes it and delivers there; use this to intentionally bring up a second session of the same peer in parallel to the default runtime (for example codex alongside claude). Descriptions identify the peer's role; runtimes identify reachable delivery endpoints; intelligence identifies the nature of the peer (artificial / natural / absent). Inbound peer messages arrive as <iap from-personality=\"...\" from-runtime=\"...\" from-intelligence=\"...\"> blocks. The topic attribute is optional.",
102
102
  )
103
103
  return lines.join('\n')
104
104
  }
@@ -122,7 +122,7 @@ export function listTools(index: PeersIndex): unknown[] {
122
122
  runtime: {
123
123
  type: 'string',
124
124
  description:
125
- 'Optional delivery runtime override for this send. Runtime ids are short channel names such as claude, codex, telegram.',
125
+ 'Optional target runtime for this send. If the specified runtime is declared and wakeable but asleep, the daemon wakes it warm-on-demand and delivers there; choosing a non-default runtime can intentionally start that peer\'s parallel session. Runtime ids are short channel names such as claude, codex, telegram.',
126
126
  pattern: RUNTIME_RE_SOURCE,
127
127
  },
128
128
  message: {
@@ -1001,6 +1001,22 @@ export async function wakeOrSpawn(args: WakeArgs, deps: WakeDeps = {}): Promise<
1001
1001
  const firstMessage = composeFirstMessage(cwd, args.task, fresh)
1002
1002
  const result = await launch(spec, adapter, firstMessage, launchCfg)
1003
1003
  if (result.status === 'FAILED') {
1004
+ // enroll-on-FAILED (0.2.57): launch can return FAILED on a ready-gate false-negative
1005
+ // (model-did-not-process-task) while the tmux session + its process actually SURVIVE —
1006
+ // observed live (ttlverify, 13.06). Since 0.2.55 removed the self-TTL, such a remnant
1007
+ // would be an UNBOUNDED orphan: no .session → invisible to superviseTick's idle-reap,
1008
+ // and no wall-clock backstop. If the session is in fact ALIVE, enroll it so the
1009
+ // activity-aware idle-reap stays the UNIVERSAL bound (a genuinely wedged session is
1010
+ // idle-reaped after idleSecs; a false-negative-but-working one keeps working and is
1011
+ // reaped only when truly idle, and its next message reaches it via the has-session fast
1012
+ // path above). We do NOT flip to READY — the wake DID fail its ready-gate, the caller
1013
+ // must still hear FAILED; we only make the live remnant supervised. Best-effort: a write
1014
+ // hiccup must not mask the original FAILED reason.
1015
+ if (sessionAlive(sock, identity)) {
1016
+ try {
1017
+ writeSessionState(cfg, { identity, runtime, personality: args.personality, cwd, wokeAt: Date.now() })
1018
+ } catch { /* keep the original FAILED reason — enroll is best-effort */ }
1019
+ }
1004
1020
  return { status: 'FAILED', woke: false, runtime, reason: result.reason }
1005
1021
  }
1006
1022
  // The session is up and the message delivered. Recording supervise-state must not