@agfpd/iapeer 0.2.82 → 0.2.83

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.82",
3
+ "version": "0.2.83",
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",
@@ -592,7 +592,7 @@ export function resolveWakeMode(
592
592
  : { resume: false, cause: 'idle-reaped-human-no-resume' }
593
593
  }
594
594
  // executor: RESUME is an INTENTIONAL "continue THIS work" signal — it happens ONLY when
595
- // the incoming message carries a topic that matches the session's last topic. A DIFFERENT
595
+ // the incoming message carries a topic that matches the session's topic set. A DIFFERENT
596
596
  // topic (new work) OR NO topic at all → FRESH. Rationale: a topicless ping after an idle
597
597
  // gap is not a request to resume specific work; defaulting it to resume revived stale
598
598
  // context (e.g. a 3-day-old session). The sender opts INTO continuity by passing the topic.
@@ -1160,11 +1160,19 @@ export interface SuperviseOutcome {
1160
1160
  export interface SuperviseDeps {
1161
1161
  env?: NodeJS.ProcessEnv
1162
1162
  nowMs?: number
1163
+ /** Activity proxy seam (default: the runtime adapter's transcript/session mtime). Tests inject a
1164
+ * controlled value to exercise the idle-age floor without a real transcript on disk. */
1165
+ newestActivityMtime?: (runtime: string, cwd: string) => number | null
1166
+ /** Liveness seam (default: the real `sessionAlive` = pty-host OR tmux-fallback). Tests inject a
1167
+ * fixed verdict so the idle path is exercised hermetically — no live tmux/pty session needed. */
1168
+ sessionAlive?: (sock: string, identity: string) => boolean
1163
1169
  }
1164
1170
 
1165
1171
  export function superviseTick(cfg: LifecycleConfig, deps: SuperviseDeps = {}): SuperviseOutcome[] {
1166
1172
  const env = deps.env ?? process.env
1167
1173
  const nowMs = deps.nowMs ?? Date.now()
1174
+ const activityMtime = deps.newestActivityMtime ?? ((rt: string, c: string) => getAdapter(rt as Runtime).newestActivityMtime(c))
1175
+ const isAlive = deps.sessionAlive ?? sessionAlive
1168
1176
  const verbose = superviseLogVerbose(env)
1169
1177
  // tmux command-log volume cap (killer-hunt socket-command layer, launch/cmdlog.ts):
1170
1178
  // a -v server log under a busy TUI grows ~7 KB/s, so each tick tail-keeps any log
@@ -1186,7 +1194,7 @@ export function superviseTick(cfg: LifecycleConfig, deps: SuperviseDeps = {}): S
1186
1194
  continue
1187
1195
  }
1188
1196
  const sock = buildSocketPath(s.runtime, s.personality, cfg.sockDir)
1189
- if (!sessionAlive(sock, s.identity)) {
1197
+ if (!isAlive(sock, s.identity)) {
1190
1198
  // A DELIBERATE stop is not a death: the stop verb parks clean
1191
1199
  // (.stopped + .idle-reaped) and kills the session itself. Catch-up branch for
1192
1200
  // a stop that raced this tick (or a pre-fix stop): drop the state quietly —
@@ -1235,14 +1243,19 @@ export function superviseTick(cfg: LifecycleConfig, deps: SuperviseDeps = {}): S
1235
1243
  continue
1236
1244
  }
1237
1245
  // Idle accounting via the runtime adapter's activity proxy (claude transcript
1238
- // / codex session mtime); fall back to wokeAt so a session that produced zero
1239
- // further turns is still reaped, and when no adapter exists yet (codex/telegram
1240
- // pre-workflow-#2 — session-states are claude-only for now anyway).
1246
+ // / codex session mtime), FLOORED at wokeAt: a session cannot be idle longer than
1247
+ // it has been awake. A wake that produced no model turn (e.g. an attach-only resume)
1248
+ // leaves the proxy at its PRE-wake value; the old `?? wokeAt` floored only the NULL
1249
+ // case, so a stale-but-non-null proxy (older than this wake) made a freshly-woken
1250
+ // session look idle for its entire prior life → idle-reaped on the very next tick,
1251
+ // out from under an attaching operator. max(), not ??, is the fix — and it still
1252
+ // reaps a session that produced zero further turns (mt=wokeAt → reaped idleSecs
1253
+ // after the wake), and falls back to wokeAt when no adapter exists for the runtime.
1241
1254
  let mt = s.wokeAt
1242
1255
  try {
1243
- mt = getAdapter(s.runtime).newestActivityMtime(s.cwd) ?? s.wokeAt
1256
+ mt = Math.max(activityMtime(s.runtime, s.cwd) ?? 0, s.wokeAt)
1244
1257
  } catch {
1245
- /* no adapter for this runtime yet → wokeAt fallback */
1258
+ /* no adapter for this runtime yet → wokeAt baseline */
1246
1259
  }
1247
1260
  const ageSecs = Math.floor((nowMs - mt) / 1000)
1248
1261
  // wake_policy:ephemeral M2 — die-after-reply: an ARMED ephemeral session (the
@@ -494,7 +494,7 @@ export function deliverViaTmux(target: DeliveryTarget, envelope: string, cwd?: s
494
494
  } while (monotonicMs() < graceDeadline)
495
495
  return err(
496
496
  `peer "${target.personality}" (${target.runtime}) is listed live but did not accept the message ` +
497
- `(no input-clear, no transcript advance within ${livenessGraceMs()}ms) — treated as dead; message NOT delivered`,
497
+ `(no input-clear, no transcript advance within ${livenessGraceMs()}ms) — live but unresponsive to this delivery (not dead); message NOT delivered`,
498
498
  )
499
499
  }
500
500
  sleepSync(PASTE_SETTLE_MS)
@@ -579,7 +579,7 @@ async function deliverViaHost(
579
579
  } while (monotonicMs() < graceDeadline)
580
580
  return err(
581
581
  `hosted peer "${target.personality}" (${target.runtime}) is listed live but did not accept the message ` +
582
- `(no transcript advance within ${graceMs}ms) — treated as dead; message NOT delivered`,
582
+ `(no transcript advance within ${graceMs}ms) — live but unresponsive to this delivery (not dead); message NOT delivered`,
583
583
  )
584
584
  }
585
585