@agfpd/iapeer 0.2.47 → 0.2.48

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.47",
3
+ "version": "0.2.48",
4
4
  "description": "Foundation core for the iapeer multi-agent ecosystem: identity, registry, storage, codec.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1251,20 +1251,29 @@ export function superviseTick(cfg: LifecycleConfig, deps: SuperviseDeps = {}): S
1251
1251
  * consumed BEFORE the relaunch so a relaunch failure does not loop on the same eager mark
1252
1252
  * (it then fresh-wakes lazily on its next message — branch 3a — never lost). NB: a /new'd
1253
1253
  * peer is expected to carry an initial_prompt; without one the first turn delivers nothing.
1254
+ *
1255
+ * Runtime is DELIBERATELY NOT pinned to the dead session's runtime: the relaunch passes
1256
+ * no runtime so resolveWakeRuntime falls through to the registry default (H5 — registry
1257
+ * is the only source of wake-runtime truth). Pinning broke the fleet-switch procedure
1258
+ * (`default-runtime <rt>` + `self-fresh`): the peer silently resurrected on the OLD
1259
+ * runtime, ignoring the just-flipped default (live incident 12.06, claude-iapeer). A
1260
+ * session deliberately living on a non-default runtime has explicit paths back (wake
1261
+ * with an explicit runtime); an owner /new returns the peer to its declared default.
1254
1262
  */
1255
1263
  export async function processEagerRelaunches(
1256
1264
  cfg: LifecycleConfig,
1257
1265
  outcomes: SuperviseOutcome[],
1258
- deps: WakeDeps = {},
1266
+ deps: WakeDeps & { wakeFn?: (args: WakeArgs, deps: WakeDeps) => Promise<WakeResult> } = {},
1259
1267
  ): Promise<WakeResult[]> {
1268
+ const wake = deps.wakeFn ?? wakeOrSpawn
1260
1269
  const results: WakeResult[] = []
1261
1270
  for (const o of outcomes) {
1262
- if (o.action !== 'needs-eager-fresh' || !o.personality || !o.runtime) continue
1271
+ if (o.action !== 'needs-eager-fresh' || !o.personality) continue
1263
1272
  clearNewEager(cfg, o.identity) // consume the eager mark — the relaunch is fresh by construction
1264
1273
  try {
1265
1274
  results.push(
1266
- await wakeOrSpawn(
1267
- { personality: o.personality, runtime: o.runtime, task: '', resume: false },
1275
+ await wake(
1276
+ { personality: o.personality, task: '', resume: false },
1268
1277
  { cfg, env: deps.env },
1269
1278
  ),
1270
1279
  )
@@ -621,6 +621,38 @@ describe('superviseTick death-cause accounting (TARGET)', () => {
621
621
  }
622
622
  })
623
623
 
624
+ test('eager relaunch does NOT pin the dead session runtime — re-resolves from the registry (H5 fleet-switch)', async () => {
625
+ // Live incident 12.06: `default-runtime codex` + `self-fresh` resurrected the peer
626
+ // on the OLD runtime — the relaunch passed the dead session's runtime EXPLICITLY,
627
+ // so resolveWakeRuntime never consulted the just-flipped registry default.
628
+ const { env, cfg, root, laDir } = deadSessionEnv('rt')
629
+ try {
630
+ setNewEager(cfg, 'claude-rt')
631
+ const outcomes = superviseTick(cfg, { env })
632
+ const o = outcomes.find(x => x.identity === 'claude-rt')
633
+ expect(o?.action).toBe('needs-eager-fresh')
634
+ const seen: Array<{ personality: string; runtime?: string; resume?: boolean; task: string }> = []
635
+ const { processEagerRelaunches } = await import('./index.ts')
636
+ await processEagerRelaunches(cfg, outcomes, {
637
+ env,
638
+ wakeFn: async args => {
639
+ seen.push({ personality: args.personality, runtime: args.runtime, resume: args.resume, task: args.task })
640
+ return { status: 'READY', woke: true }
641
+ },
642
+ })
643
+ expect(seen.length).toBe(1)
644
+ expect(seen[0].personality).toBe('rt')
645
+ expect(seen[0].runtime).toBeUndefined() // ← the fix: wakeOrSpawn re-resolves from the registry
646
+ expect(seen[0].resume).toBe(false)
647
+ expect(seen[0].task).toBe('')
648
+ // the mark was consumed by the relaunch
649
+ expect(hasNewEager(cfg, 'claude-rt')).toBe(false)
650
+ } finally {
651
+ rmSync(root, { recursive: true, force: true })
652
+ rmSync(laDir, { recursive: true, force: true })
653
+ }
654
+ })
655
+
624
656
  test('a DEAD session with NO .new-eager → reaped-gone, NO .idle-reaped written (crash leaves no marker)', () => {
625
657
  const { env, cfg, root, laDir } = deadSessionEnv('w')
626
658
  try {