@agfpd/iapeer 0.4.21 → 0.4.22

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/CHANGELOG.md CHANGED
@@ -10,6 +10,21 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
10
10
 
11
11
  ## [Unreleased]
12
12
 
13
+ ## [0.4.22] - 2026-06-22
14
+
15
+ ### Added
16
+
17
+ - **`iapeer live-runtime <peer>`** — prints the peer's CURRENT live runtime: the
18
+ freshest-pane-log among its **pid-alive** supervisor sessions (a peer can be alive on
19
+ more than one runtime at once — a `/codex` flip can leave the old session running
20
+ alongside the new). This is the authoritative, real-time signal for "what runtime is
21
+ this peer running right now" — NOT `default_runtime` (only the wake-default) and NOT a
22
+ `.session` wake-record (alive-filtered only on a supervise-tick latency). Exit 0 + the
23
+ runtime on stdout when one is live; exit 1 + no output when none is alive. For external
24
+ consumers (e.g. telegram-runtime's typing/tool-use indicator) that must resolve the live
25
+ runtime of a peer running on a non-default runtime, instead of inferring it. Exposed as
26
+ `resolveLiveRuntime` (transport).
27
+
13
28
  ## [0.4.21] - 2026-06-22
14
29
 
15
30
  ### Fixed
@@ -325,7 +340,8 @@ Initial public release — the foundation core of the iapeer multi-agent ecosyst
325
340
  - **Heterogeneous peers** — AI agents (Claude, Codex), humans (Telegram), and services
326
341
  (timer = cron, watcher = event) are all first-class and addressed the same way.
327
342
 
328
- [Unreleased]: https://github.com/agfpd/iapeer/compare/v0.4.21...HEAD
343
+ [Unreleased]: https://github.com/agfpd/iapeer/compare/v0.4.22...HEAD
344
+ [0.4.22]: https://github.com/agfpd/iapeer/compare/v0.4.21...v0.4.22
329
345
  [0.4.21]: https://github.com/agfpd/iapeer/compare/v0.4.20...v0.4.21
330
346
  [0.4.20]: https://github.com/agfpd/iapeer/compare/v0.4.19...v0.4.20
331
347
  [0.4.19]: https://github.com/agfpd/iapeer/compare/v0.4.18...v0.4.19
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agfpd/iapeer",
3
- "version": "0.4.21",
3
+ "version": "0.4.22",
4
4
  "description": "Foundation core for the iapeer multi-agent ecosystem: identity, registry, storage, codec.",
5
5
  "license": "Apache-2.0",
6
6
  "author": "agfpd",
package/src/cli/index.ts CHANGED
@@ -1139,6 +1139,7 @@ const VERBS: ReadonlyArray<{ sig: string; desc: string }> = [
1139
1139
  desc: 'backbone host-phase: marketplace → notifier → telegram (human peer) → memory → voice (all default YES). --accept-risk (or IAPEER_ACCEPT_RISK=1) accepts the security warning non-interactively',
1140
1140
  },
1141
1141
  { sig: 'status', desc: 'host snapshot: version, daemon health, memory + voice slots (<provider> | none)' },
1142
+ { sig: 'live-runtime <peer>', desc: 'print the peer\'s CURRENT live runtime (freshest pane-log among pid-alive sessions; NOT default_runtime). Exit 1 + no output if none alive' },
1142
1143
  { sig: 'install-runtime <runtime> [--package pkg] [--npx]', desc: 'npx-install a runtime package + deploy its declared peer-set' },
1143
1144
  { sig: 'update-runtime <runtime> | --all [--force]', desc: "version-gate → re-install + re-provision declared set → restart the runtime's peers" },
1144
1145
  { sig: 'init [cwd] [--runtime r] [--description d]', desc: 'onboard the CURRENT folder as a peer (name = folder name; identity + MCP + doctrine)' },
@@ -1501,6 +1502,21 @@ export async function runCli(argv: string[], env: NodeJS.ProcessEnv = process.en
1501
1502
  out(formatHostStatus(s))
1502
1503
  return s.daemon.healthy ? 0 : 1
1503
1504
  }
1505
+ case 'live-runtime': {
1506
+ // The AUTHORITATIVE current live runtime of a peer (machine-readable, for external
1507
+ // consumers like telegram-runtime's typing indicator). Prints the runtime the peer
1508
+ // is RUNNING right now — the freshest-pane-log among its PID-alive supervisor
1509
+ // sessions — NOT default_runtime (the wake-default) and NOT a .session wake-record.
1510
+ // A peer can be alive on >1 runtime (a /codex flip leaves both); this picks the
1511
+ // currently-active. Exit 0 + the runtime on stdout when one is live; exit 1 + no
1512
+ // output when none is alive (queryable: `rt=$(iapeer live-runtime <p>) || handle-down`).
1513
+ if (!positionals[0]) return argErr(errOut, 'live-runtime needs a peer name — usage: iapeer live-runtime <peer>')
1514
+ const { resolveLiveRuntime } = await import('../transport/index.ts')
1515
+ const rt = resolveLiveRuntime(positionals[0])
1516
+ if (!rt) return 1 // no live session — no output, non-zero
1517
+ out(`${rt}\n`)
1518
+ return 0
1519
+ }
1504
1520
  case 'install-runtime': {
1505
1521
  // §6 onboard a runtime END-TO-END: npx-install the package (auto-resolved from
1506
1522
  // the built-in runtime→package registry, or --package; self-deploys bin +
@@ -279,6 +279,33 @@ function defaultPaneLogMtime(address: string): number | null {
279
279
  }
280
280
  }
281
281
 
282
+ /**
283
+ * The CURRENT live runtime of a peer — the AUTHORITATIVE, real-time signal for "what
284
+ * runtime is this peer running right now" (NOT default_runtime, which is only the wake
285
+ * DEFAULT, and NOT a `.session` file, a wake-record cleaned on a supervise-tick latency).
286
+ *
287
+ * Resolution: among the peer's PID-ALIVE supervisor sessions (listHostedPeers — the same
288
+ * source delivery uses; `sessionAlive` = pidfile exists AND kill-0), pick the one with the
289
+ * FRESHEST pane-log. A peer can be alive on >1 runtime at once (a `/codex` flip can leave
290
+ * the old session running alongside the new), so the freshest-pane-log among the alive set
291
+ * is the currently-active surface. The alive-filter is what fixes the flip-race: a
292
+ * just-died runtime's pane-log / .sock / .session all LINGER, but its pid is dead → it is
293
+ * excluded here. Returns null when no session is alive.
294
+ *
295
+ * Deps are injectable for hermetic tests (the defaults read the real run-dir + pane-logs).
296
+ */
297
+ export function resolveLiveRuntime(
298
+ personality: string,
299
+ deps: { aliveRuntimes?: (p: string) => string[]; paneLogMtime?: (address: string) => number | null } = {},
300
+ ): string | null {
301
+ const aliveOf = deps.aliveRuntimes ?? ((p: string) => listHostedPeers().filter(h => h.personality === p).map(h => h.runtime))
302
+ const paneMt = deps.paneLogMtime ?? defaultPaneLogMtime
303
+ const alive = aliveOf(personality)
304
+ if (alive.length <= 1) return alive[0] ?? null
305
+ // >1 alive → the freshest pane-log mtime (the currently-rendering turn surface).
306
+ return alive.reduce((best, rt) => ((paneMt(`${rt}-${personality}`) ?? 0) > (paneMt(`${best}-${personality}`) ?? 0) ? rt : best))
307
+ }
308
+
282
309
 
283
310
  // ─── Compact done gate (dialogue control must report FACT, not keystroke delivery) ──
284
311