@agfpd/iapeer 0.2.77 → 0.2.79

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.
Files changed (113) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +2 -2
  3. package/package.json +21 -2
  4. package/src/cli/index.ts +95 -78
  5. package/src/connect/index.ts +4 -4
  6. package/src/core/constants.ts +3 -3
  7. package/src/core/normalize.ts +2 -2
  8. package/src/create/index.ts +1 -1
  9. package/src/daemon/index.ts +5 -5
  10. package/src/daemon/main.ts +9 -9
  11. package/src/enable/index.ts +19 -18
  12. package/src/enable/provisionCommand.ts +10 -10
  13. package/src/identity/index.ts +2 -2
  14. package/src/identity/profileStandard.ts +8 -8
  15. package/src/init/index.ts +10 -10
  16. package/src/install/index.ts +2 -2
  17. package/src/install/signing.ts +3 -4
  18. package/src/launch/adapters/claude.ts +7 -7
  19. package/src/launch/adapters/codex.ts +5 -6
  20. package/src/launch/canary.ts +8 -8
  21. package/src/launch/cmdlog.ts +3 -3
  22. package/src/launch/composeSystemPrompt.ts +2 -1
  23. package/src/launch/index.ts +5 -6
  24. package/src/launch/invocation.ts +1 -1
  25. package/src/launch/launchd.ts +14 -15
  26. package/src/launch/launchdRun.ts +6 -6
  27. package/src/launch/nativeMemory.ts +9 -9
  28. package/src/launch/ptyHost.ts +1 -1
  29. package/src/launch/readyGateModel.ts +3 -3
  30. package/src/launch/types.ts +7 -7
  31. package/src/lifecycle/eventlog.ts +2 -2
  32. package/src/lifecycle/index.ts +21 -21
  33. package/src/lifecycle/queue.ts +1 -1
  34. package/src/onboard/index.ts +19 -13
  35. package/src/onboard/memory.ts +5 -5
  36. package/src/onboard/steps.ts +8 -9
  37. package/src/provision/index.ts +2 -3
  38. package/src/runtime/deploy.ts +2 -2
  39. package/src/runtime/index.ts +4 -4
  40. package/src/shadow/gate.ts +6 -6
  41. package/src/shadow/index.ts +2 -2
  42. package/src/status/index.ts +11 -11
  43. package/src/storage/index.ts +1 -1
  44. package/src/supervisor/daemon.ts +20 -13
  45. package/src/transport/index.ts +11 -11
  46. package/src/update/index.ts +4 -4
  47. package/src/cli/cli.test.ts +0 -603
  48. package/src/cli/listTui.test.ts +0 -71
  49. package/src/codec/codec.test.ts +0 -271
  50. package/src/connect/connect.test.ts +0 -163
  51. package/src/core/constants.test.ts +0 -32
  52. package/src/core/normalize.test.ts +0 -98
  53. package/src/create/create.test.ts +0 -143
  54. package/src/daemon/daemon-http.test.ts +0 -114
  55. package/src/daemon/daemon.test.ts +0 -267
  56. package/src/daemon/main.test.ts +0 -413
  57. package/src/enable/enable.test.ts +0 -95
  58. package/src/enable/provisionCommand.test.ts +0 -107
  59. package/src/identity/identity.test.ts +0 -279
  60. package/src/identity/profileStandard.test.ts +0 -204
  61. package/src/init/init.test.ts +0 -215
  62. package/src/init/runtime-resolve.test.ts +0 -49
  63. package/src/install/install.test.ts +0 -66
  64. package/src/install/signing.test.ts +0 -84
  65. package/src/launch/bootdeliver.test.ts +0 -108
  66. package/src/launch/bootstrap.test.ts +0 -56
  67. package/src/launch/canary.test.ts +0 -346
  68. package/src/launch/cmdlog.test.ts +0 -94
  69. package/src/launch/codexHooksTrust.test.ts +0 -248
  70. package/src/launch/composeSystemPrompt.layers.test.ts +0 -445
  71. package/src/launch/composeSystemPrompt.test.ts +0 -145
  72. package/src/launch/hostComposerOccupied.test.ts +0 -63
  73. package/src/launch/invocation.test.ts +0 -174
  74. package/src/launch/launch.test.ts +0 -359
  75. package/src/launch/launchd.test.ts +0 -499
  76. package/src/launch/nativeMemory.test.ts +0 -193
  77. package/src/launch/ptyHost.test.ts +0 -38
  78. package/src/launch/readyGateModel.test.ts +0 -72
  79. package/src/launch/sockdir.test.ts +0 -74
  80. package/src/lifecycle/eventlog.test.ts +0 -114
  81. package/src/lifecycle/lifecycle.test.ts +0 -1056
  82. package/src/lifecycle/queue.test.ts +0 -185
  83. package/src/onboard/memory.test.ts +0 -157
  84. package/src/onboard/onboard.test.ts +0 -39
  85. package/src/onboard/steps.test.ts +0 -201
  86. package/src/provision/provision.test.ts +0 -288
  87. package/src/registry/registry.test.ts +0 -432
  88. package/src/runtime/runtime.test.ts +0 -226
  89. package/src/runtime/update.test.ts +0 -192
  90. package/src/shadow/enumerate-skip.test.ts +0 -67
  91. package/src/shadow/gate.test.ts +0 -286
  92. package/src/shadow/install.test.ts +0 -153
  93. package/src/shadow/shadow.test.ts +0 -42
  94. package/src/status/status.test.ts +0 -208
  95. package/src/storage/peers-home.test.ts +0 -34
  96. package/src/storage/rotatelog.test.ts +0 -44
  97. package/src/storage/storage.test.ts +0 -65
  98. package/src/supervisor/attached-marker.test.ts +0 -84
  99. package/src/supervisor/boot.test.ts +0 -66
  100. package/src/supervisor/client.attach.test.ts +0 -107
  101. package/src/supervisor/daemon.test.ts +0 -213
  102. package/src/supervisor/deliver.test.ts +0 -58
  103. package/src/supervisor/panelog.test.ts +0 -95
  104. package/src/supervisor/protocol.test.ts +0 -222
  105. package/src/supervisor/selfargv.test.ts +0 -22
  106. package/src/supervisor/spinner-strip.test.ts +0 -52
  107. package/src/transport/compactDone.test.ts +0 -144
  108. package/src/transport/composerQueue.test.ts +0 -204
  109. package/src/transport/hostControl.test.ts +0 -87
  110. package/src/transport/hostDeliver.test.ts +0 -137
  111. package/src/transport/hostLive.test.ts +0 -50
  112. package/src/transport/routesend-wake.test.ts +0 -357
  113. package/src/update/update.test.ts +0 -208
@@ -18,12 +18,12 @@ export const PEERS_SCHEMA_VERSION = 2
18
18
  // 450 (was 250): self-documenting API-peer descriptions (notifier timer/watcher)
19
19
  // must fit "who the peer is + registration format + a live example" — dense full
20
20
  // texts run to ~408 chars; 250 cut them mid-word so the caller could not compose
21
- // the call. Bumped with Arthur's sanction (2026-06-08). NB: this is COMPILE-TIME
21
+ // the call. NB: this is COMPILE-TIME
22
22
  // baked — the live daemon re-clamps descriptions on read (registry parsePeerRecord),
23
23
  // so the running router keeps the OLD limit until restarted onto the new binary.
24
24
  export const MAX_DESCRIPTION_LEN = 450
25
25
 
26
- // Contract vocabulary (docs/Идентичность, Артур 05.06): the nature of the
26
+ // Contract vocabulary (docs/Идентичность): the nature of the
27
27
  // intelligence expressing itself through a runtime.
28
28
  // artificial — AI agent · natural — human · absent — programmatic source
29
29
  export const INTELLIGENCE_VALUES = ['artificial', 'natural', 'absent'] as const
@@ -127,7 +127,7 @@ export const DEFAULT_SOCK_DIR = '/tmp'
127
127
  // agree — a site that hardcodes DEFAULT_SOCK_DIR would look in /tmp while a sandbox
128
128
  // (IAPEER_SOCK_DIR set) created the session elsewhere → a false "offline".
129
129
  //
130
- // IAPEER_ROOT IMPLIES SOCKET ISOLATION (boris's e2e find 10.06): an alt-root used
130
+ // IAPEER_ROOT IMPLIES SOCKET ISOLATION: an alt-root used
131
131
  // to inherit the GLOBAL /tmp, so a sandboxed `list` saw PROD sessions live by name
132
132
  // collision, and a sandboxed stop/start would have HIT a prod session. A set root
133
133
  // now derives `<root>/socks` unless IAPEER_SOCK_DIR explicitly says otherwise; the
@@ -11,7 +11,7 @@
11
11
  // and system `uconv`/`iconv` are forbidden by the zone (OS-nondeterministic) and
12
12
  // absent here. `transliteration` is the one pure-JS engine that reproduces ICU
13
13
  // across WRITING SYSTEMS — CJK → pinyin WITH syllable spacing (项目 → "Xiang Mu"),
14
- // Cyrillic (Артур → "Artur"), Greek (Ω → "O"), Latin diacritics (Köln → "Koln") —
14
+ // Cyrillic (Привет → "Privet"), Greek (Ω → "O"), Latin diacritics (Köln → "Koln") —
15
15
  // and it bundles its own CLDR/unidecode-derived tables, so it is deterministic
16
16
  // across OSes (exactly the property the zone's iconv ban is protecting). It is the
17
17
  // deterministic CLDR-EQUIVALENT of ICU, NOT a literal ICU engine.
@@ -75,7 +75,7 @@ function slug(value: string): string {
75
75
  * derivation/comparison routes through (basename(cwd) → personality, PEER_*
76
76
  * comparisons, profile re-validation).
77
77
  *
78
- * IDEMPOTENT on already-valid names: normalize("boris") === "boris",
78
+ * IDEMPOTENT on already-valid names: normalize("alice") === "alice",
79
79
  * normalize("notifier-timer") === "notifier-timer" — required because
80
80
  * validatePersonality re-normalizes stored profile names.
81
81
  *
@@ -11,7 +11,7 @@
11
11
  // NO-CLOBBER: making the folder is mkdir-recursive (never deletes); init is
12
12
  // idempotent (profile kept, .mcp.json merged, doctrine never overwritten). The one
13
13
  // hard refusal: a target that already holds a DIFFERENT peer's profile — creating
14
- // "maria" into a folder that is already "boris" would either silently adopt boris or
14
+ // "alice" into a folder that is already "bob" would either silently adopt bob or
15
15
  // split an identity, so it fails loudly instead.
16
16
  //
17
17
  // INFRA (telegram human via operator-add / notifier function via a declared set):
@@ -79,7 +79,7 @@ export function resolveCallerFromHeader(value: string | undefined, index: PeersI
79
79
  // The roster of known peers is DELIBERATELY NOT in this description. It is rendered
80
80
  // ONCE, in the peer's system prompt (composeSystemPrompt → Layer 3 "## Known peers").
81
81
  // Embedding it here ALSO duplicated the full list into every agent's context on every
82
- // turn — a live consumer (boris) confirmed seeing the identical roster twice. The
82
+ // turn — confirmed live: an agent saw the identical roster twice. The
83
83
  // description stays light: purpose + parameter semantics only; the model gets the
84
84
  // roster from its system prompt. Likewise the MCP server-level `instructions` field is
85
85
  // left unset (createMcpServer) — it would re-inject doctrine into the client system
@@ -211,7 +211,7 @@ export async function callTool(
211
211
  // Ф-#8a: ONE durable outcome line per delivery attempt (delivery.log, sibling
212
212
  // to lifecycle.log) — metadata only, never the body. Both branches of the
213
213
  // routeSend result are recorded, so a suspected loss is reconstructable from
214
- // disk (the gap the 09.06 investigation hit). No-op when no dir is wired
214
+ // disk (the gap a delivery-loss investigation hit). No-op when no dir is wired
215
215
  // (library/test daemons); appendDeliveryEvent itself never throws.
216
216
  appendDeliveryEvent(deps.deliveryLogDir, {
217
217
  ev: 'delivery',
@@ -221,8 +221,8 @@ export async function callTool(
221
221
  ok: String(sent.ok),
222
222
  via: sent.ok ? `${sent.value.delivered_to.runtime}-${sent.value.delivered_to.personality}` : undefined,
223
223
  woke: sent.ok ? String(sent.value.woke) : undefined,
224
- // M3 queue observability (boris acceptance (a)): a queued accept is marked
225
- // and carries the queue depth at accept time — a stuck queue is visible.
224
+ // M3 queue observability: a queued accept is marked and carries the queue
225
+ // depth at accept time — a stuck queue is visible.
226
226
  queued: sent.ok && sent.value.queued ? 'true' : undefined,
227
227
  qkind: sent.ok ? sent.value.queuedBy : undefined,
228
228
  qd: sent.ok ? sent.value.queueDepth : undefined,
@@ -345,7 +345,7 @@ export interface StartDaemonOptions extends StorageOptions {
345
345
  * `Authorization: Bearer <token>` or it is rejected 401 BEFORE any MCP dispatch.
346
346
  * Unset → no auth (the same-uid unix-socket base / loopback default). This is the
347
347
  * structural layer for closing H8 on the TCP loopback listener — kept OFF until
348
- * Артур enables it (the production main reads IAPEER_BEARER_TOKEN). Loopback is
348
+ * explicitly enabled (the production main reads IAPEER_BEARER_TOKEN). Loopback is
349
349
  * broader than same-uid, so a token gates it without changing the on-wire MCP.
350
350
  */
351
351
  bearerToken?: string
@@ -118,10 +118,10 @@ export function makeArmEphemeralOnDelivered(cfg: LifecycleConfig): (caller: Reso
118
118
  * makeEphemeralRouteDeps): record a LIVE-delivered topic as the target identity's
119
119
  * `.topic` marker, so the marker tracks the topic the session LAST WORKED ON — the
120
120
  * semantics resolveWakeMode's executor discriminator actually wants — not merely the
121
- * topic it woke with. Live defect 11.06 (boris): an executor woke topicless at 13:13,
122
- * worked a delivered topic all day, was stop-parked clean, and the topic-matching
123
- * resume-wake at 17:06 compared against the STALE wake-time marker → cause=
124
- * idle-reaped-new-topic → FRESH; the day's context was lost despite the clean park.
121
+ * topic it woke with. Defect: an executor could wake topicless, work a delivered
122
+ * topic for a session, be stop-parked clean, and a later topic-matching resume-wake
123
+ * compared against the STALE wake-time marker → cause=idle-reaped-new-topic → FRESH;
124
+ * the session's context was lost despite the clean park.
125
125
  * Same-topic deliveries are a no-op (no marker churn, no log spam) — the lifecycle.log
126
126
  * line `ev=topic-note` therefore marks exactly the moments the session's working
127
127
  * topic SHIFTED, which is what a fresh-vs-resume postmortem needs. Best-effort: a
@@ -307,7 +307,7 @@ export async function startConfiguredDaemon(opts: ConfiguredDaemonOptions = {}):
307
307
  ephemeral,
308
308
  // Live-delivered topic → the target's .topic marker (see makeNoteLiveTopic):
309
309
  // the wake resolver's executor discriminator then compares against the topic
310
- // the session last WORKED ON, not the one it woke with (defect 11.06).
310
+ // the session last WORKED ON, not the one it woke with.
311
311
  noteLiveTopic,
312
312
  // Busy-human-composer queue: fast queued ack, async drain, fail all pending
313
313
  // on daemon close/update so a queued envelope is never silently lost.
@@ -371,7 +371,7 @@ export interface InstallDaemonPlistOptions {
371
371
  export function buildDaemonPlistSpec(opts: InstallDaemonPlistOptions = {}): LaunchdPlistSpec {
372
372
  const env = opts.env ?? process.env
373
373
  const home = env.HOME?.trim() || homedir()
374
- const defaultPath = `${home}/.bun/bin:${home}/.local/bin:/opt/homebrew/bin:/usr/bin:/bin`
374
+ const defaultPath = `${home}/.bun/bin:${home}/.local/bin:/opt/homebrew/bin:/opt/homebrew/sbin:/usr/local/bin:/usr/bin:/bin`
375
375
  const logDir = pluginLogsDir('iapeer', { env })
376
376
  const environment: Record<string, string> = {
377
377
  PATH: opts.path ?? env.PATH ?? defaultPath,
@@ -417,9 +417,9 @@ export interface InstallDaemonPlistResult {
417
417
  * ~/Library/LaunchAgents/com.agfpd.iapeer.plist (or IAPEER_LAUNCHAGENTS_DIR),
418
418
  * returning {path, changed}. Writes the FILE only — does NOT bootstrap it (load
419
419
  * is a deliberate operator step). IDEMPOTENT BY CONTENT: writes only when the
420
- * rendered plist differs from what is already on disk (owner-caught 11.06: an
421
- * unconditional rewrite on every deploy spams the owner's Background Task
422
- * Management notification for zero benefit). Collision guard: refuses to
420
+ * rendered plist differs from what is already on disk (an unconditional rewrite
421
+ * on every deploy spams the owner's Background Task Management notification for
422
+ * zero benefit). Collision guard: refuses to
423
423
  * overwrite an existing com.agfpd.iapeer.plist that is not foundation-owned
424
424
  * (lacks the sentinel) — the same ownership proof the peer-plist installer uses.
425
425
  * com.agfpd.* is the foundation-exclusive daemon namespace, so this never
@@ -1,9 +1,9 @@
1
1
  // enable — per-peer capability install (contract Установка §3 + Per-рантайм
2
2
  // install-асимметрия; verb `enable <capability> [peer]`). The init→enable
3
- // capability story: a peer gains a capability (peer-voice / MergeMind) FROM our
4
- // marketplace. iapeer ORCHESTRATES — install + enable + call `setup` ONLY if the
5
- // plugin declares it — and deliberately does NOT resolve the plugin's dep-graph
6
- // (`requires`/`setup` internals are the plugin's job, → Стандарт iapeer плагина).
3
+ // capability story: a peer gains a capability FROM our marketplace. iapeer
4
+ // ORCHESTRATES — install + enable + call `setup` ONLY if the plugin declares it
5
+ // — and deliberately does NOT resolve the plugin's dep-graph (`requires`/`setup`
6
+ // internals are the plugin's job, → Стандарт iapeer плагина).
7
7
  //
8
8
  // Per-рантайм install-асимметрия (contract table):
9
9
  // claude → `plugin install <p>@agfpd --scope project` run IN the peer's cwd; the
@@ -15,9 +15,10 @@
15
15
  // against the real host under fleet-guard without an isolated CODEX_HOME.
16
16
  //
17
17
  // SIMPLE vs СЛОЖНЫЙ plugin (contract): a plugin MAY ship `iapeer.json` at its root
18
- // declaring `setup` (a multi-step installer) and `requires`. SIMPLE (peer-voice — no
19
- // iapeer.json/no setup): install + enable → works. СЛОЖНЫЙ (MergeMind): install +
20
- // enable → call `setup`. iapeer reads the manifest and calls setup ONLY if declared.
18
+ // declaring `setup` (a multi-step installer) and `requires`. SIMPLE (no
19
+ // iapeer.json/no setup): install + enable → works. СЛОЖНЫЙ (a multi-step
20
+ // capability plugin): install + enable → call `setup`. iapeer reads the manifest
21
+ // and calls setup ONLY if declared.
21
22
 
22
23
  import { spawnSync } from 'child_process'
23
24
  import { existsSync, readFileSync, realpathSync } from 'fs'
@@ -369,10 +370,10 @@ export interface CwdEnableOptions {
369
370
  }
370
371
 
371
372
  /** One install attempt for one runtime, WITH the stale-snapshot resilience the
372
- * contract requires (просьба iapeer-memory): a failed attempt refreshes the
373
- * runtime's local marketplace snapshot (claude `marketplace update` / codex
374
- * `marketplace upgrade`) and retries ONCE — a plugin registered in the
375
- * marketplace after the host's last pull reads as «unknown plugin» until then. */
373
+ * contract requires: a failed attempt refreshes the runtime's local marketplace
374
+ * snapshot (claude `marketplace update` / codex `marketplace upgrade`) and
375
+ * retries ONCE — a plugin registered in the marketplace after the host's last
376
+ * pull reads as «unknown plugin» until then. */
376
377
  function enableRuntimeWithRetry(
377
378
  rt: CapabilityRuntime,
378
379
  plugin: string,
@@ -399,8 +400,8 @@ function enableRuntimeWithRetry(
399
400
  * codex global) + enable + `setup` ONLY if declared — with marketplace ensure
400
401
  * (marketplaceRef given + not registered → add) and the stale-snapshot retry.
401
402
  * Consumer: enableCapability (the peer-resolved generic `iapeer enable` verb) —
402
- * ONE home of the install forms. (The memory-plugin birth-hook and verb consumers
403
- * were removed 11.06 — the provider plugin form is retired, ADR-017.)
403
+ * ONE home of the install forms. (The provider birth-hook and verb consumers were
404
+ * removed — the provider plugin form is retired, ADR-017.)
404
405
  */
405
406
  export function enableCapabilityForCwd(o: CwdEnableOptions): EnableResult {
406
407
  const env = o.env ?? process.env
@@ -473,9 +474,9 @@ export function enableCapability(opts: EnableOptions): EnableResult {
473
474
  }
474
475
 
475
476
  // ─────────────────────────────────────────────────────────────────────────────
476
- // Removal forms — REMOVED 11.06 with the memory-plugin verb (their only caller;
477
- // ADR-017 retired the provider plugin form). Historical verb names, verified
478
- // live 10.06, for a future generic `disable`: claude `plugin uninstall <id>
479
- // --scope project` (run in the peer cwd), codex `plugin remove <id>`
480
- // (host-global). See git history ≤v0.2.41 for the implementations.
477
+ // Removal forms — REMOVED with the provider verb (their only caller; ADR-017
478
+ // retired the provider plugin form). Historical verb names, for a future generic
479
+ // `disable`: claude `plugin uninstall <id> --scope project` (run in the peer
480
+ // cwd), codex `plugin remove <id>` (host-global). See git history ≤v0.2.41 for
481
+ // the implementations.
481
482
  // ─────────────────────────────────────────────────────────────────────────────
@@ -1,18 +1,18 @@
1
- // Provision-command executor — the v1.2 inversion joint (контракт «Слот памяти»
2
- // §Provision провайдера; ADR-009, решение Артура 10.06, схема сверена с
3
- // iapeer-memory 11.06). The slot declares provision/unprovision COMMANDS; the
4
- // core shells into them at the lifecycle joints and knows NOTHING about surface
5
- // forms. The four contract requirements live here:
1
+ // Provision-command executor — the v1.2 inversion joint (contract «Слот памяти»
2
+ // §Provision провайдера; ADR-009). The slot declares provision/unprovision
3
+ // COMMANDS; the core shells into them at the lifecycle joints and knows NOTHING
4
+ // about surface forms. The four contract requirements live here:
6
5
  // 1. argv-form {command, args[]} with PER-ARG placeholder substitution —
7
- // spawned WITHOUT a shell (injection/quoting class; бэктик-грабли 10.06).
6
+ // spawned WITHOUT a shell (injection/quoting class — a backtick in an arg
7
+ // must never reach a shell).
8
8
  // 2. `command` is ABSOLUTE (parser refuses relative — launchd minimal PATH).
9
9
  // 3. Timeout 120 s; best-effort semantics live at the CALL SITES (LOUD warn,
10
10
  // the birth/remove flow continues) — this module only reports structurally.
11
11
  // 4. {occasion} vocabulary (финален, contract §Provision): birth | sweep-on |
12
12
  // off-peer | off-all | remove — ref-counting of host-global surfaces is the
13
- // PROVIDER's business. Since the memory-plugin verb removal (11.06, ADR-017)
14
- // the CORE emits only `birth` and `remove`; the sweep occasions remain part
15
- // of the provider-command interface (their CLI may use them internally).
13
+ // PROVIDER's business. Since the provider-verb removal (ADR-017) the CORE
14
+ // emits only `birth` and `remove`; the sweep occasions remain part of the
15
+ // provider-command interface (their CLI may use them internally).
16
16
  // Idempotency is the provider's obligation by construction; the provider holds
17
17
  // its own lock against concurrent provisions.
18
18
 
@@ -20,7 +20,7 @@ import { spawnSync } from 'child_process'
20
20
  import { accessSync, constants as FS } from 'fs'
21
21
  import type { MemoryProviderProvision } from '../status/index.ts'
22
22
 
23
- /** The agreed occasion vocabulary (финален, сверено с iapeer-memory). */
23
+ /** The agreed occasion vocabulary (финален). */
24
24
  export type ProvisionOccasion = 'birth' | 'sweep-on' | 'off-peer' | 'off-all' | 'remove'
25
25
 
26
26
  export const PROVISION_TIMEOUT_MS = 120_000
@@ -481,12 +481,12 @@ export function ensurePeerProfile(options: EnsurePeerProfileOptions): PeerProfil
481
481
  const mergedRuntimes = uniqueRuntimes([...existing.runtimes, ...discoveredRuntimes])
482
482
  ensureLocalRuntimeScopes(cwd, mergedRuntimes)
483
483
  // RE-PROVISION parity with the new-profile branch: an EXISTING infra peer (a
484
- // migration / re-provision — e.g. `iapeer create arthur --runtime telegram` to move
484
+ // migration / re-provision — e.g. `iapeer create alice --runtime telegram` to move
485
485
  // a live telegram human onto the foundation) must ALSO get its always-on plist
486
486
  // installed and its intelligence set to the runtime's foundation default
487
487
  // (telegram→natural, notifier→absent). The old code only merged runtimes here, so
488
488
  // re-provisioning an infra peer wrote NO plist (bootstrap → refused-foreign on the
489
- // missing plist) and left a stale legacy intelligence — the arthur cutover had to
489
+ // missing plist) and left a stale legacy intelligence — the cutover had to
490
490
  // install the plist + flip vocab by hand. Install BEFORE the write so a
491
491
  // collision-guard refusal (a foreign plist still sitting at the label) fails loudly
492
492
  // and leaves no half-updated profile. Idempotent for our own (sentinel) plist.
@@ -11,7 +11,7 @@
11
11
  // (telegram/capability passport — PASSPORT ONLY: aliases are misfiled there and the
12
12
  // contract relocates them to `expansion.aliases`, the runtime-agnostic plugin-config
13
13
  // section owned by the chat-runtime — telegram-runtime today, future discord/matrix
14
- // read the SAME section per the 25.05 expansion design), notifier (notifier-runtime),
14
+ // read the SAME section per the expansion design), notifier (notifier-runtime),
15
15
  // expansion (chat-runtime aliases), <plugin> sections — each plugin writes only its
16
16
  // own field via atomic merge. Plugin-config sections are NOT projected into the
17
17
  // registry index (private config is read from the LOCAL profile by its owner).
@@ -130,12 +130,12 @@ export function validateProfileStandard(raw: unknown, cwd: string): ProfileIssue
130
130
  message: 'legacy top-level `aliases` — contract relocates to expansion.aliases (/alias-* namespace); owner migration pending',
131
131
  })
132
132
  }
133
- // aliases misfiled in the PASSPORT (interfaces.telegram) — the 07.06 interim home.
134
- // Taxonomy revision (11.06, Артур): aliases are PRIVATE plugin config (one reader,
135
- // the chat-runtime at expansion), not a public passport attribute — the contract
136
- // relocates them to the runtime-agnostic `expansion.aliases` section (25.05 замысел:
137
- // future discord/matrix runtimes read the SAME aliases). Transition observability:
138
- // this warn goes silent once the owner migration moves the data.
133
+ // aliases misfiled in the PASSPORT (interfaces.telegram) — an interim home.
134
+ // Taxonomy: aliases are PRIVATE plugin config (one reader, the chat-runtime at
135
+ // expansion), not a public passport attribute — the contract relocates them to the
136
+ // runtime-agnostic `expansion.aliases` section (future discord/matrix runtimes read
137
+ // the SAME aliases). Transition observability: this warn goes silent once the owner
138
+ // migration moves the data.
139
139
  const interfacesObj =
140
140
  obj.interfaces && typeof obj.interfaces === 'object' && !Array.isArray(obj.interfaces)
141
141
  ? (obj.interfaces as Record<string, unknown>)
@@ -254,7 +254,7 @@ export function reconcileIndex(options: PeersUpdateOptions = {}): ReconcileEntry
254
254
  * Self-heal the index: REPLACE every registry record with the projection from its
255
255
  * local profile (the source of truth). Unlike upsertPeer (which UNIONs runtimes so a
256
256
  * boot never drops one), reindex is a deliberate REWRITE — it is how a local profile's
257
- * narrowed runtimes (e.g. arthur dropped to [telegram]) propagate to the index. A peer
257
+ * narrowed runtimes (e.g. dropped to [telegram]) propagate to the index. A peer
258
258
  * whose local profile is missing is left untouched (and surfaced in `missing`). The
259
259
  * legacy-safe intelligenceRaw is preserved when the nature is unchanged (never silently
260
260
  * migrate the live vocab — the human→natural incident class).
package/src/init/index.ts CHANGED
@@ -3,7 +3,7 @@
3
3
  // + registry + infra plist) and adds the per-peer ECOSYSTEM wiring a peer needs to
4
4
  // talk: the HTTP-MCP transport config + the local doctrine template.
5
5
  //
6
- // THE BIG SIMPLIFICATION (install-gate snapped LIVE 08.06): a peer gets send_to_peer
6
+ // THE BIG SIMPLIFICATION (install-gate, proven live): a peer gets send_to_peer
7
7
  // purely from a project-scope `.mcp.json` pointing at the host-wide HTTP-MCP daemon —
8
8
  // NO plugin install, NO /reload-plugins, NO GC version-snapshots (the whole legacy
9
9
  // persistent-peer install-gate class is gone). Verified: a cold-start claude session
@@ -120,8 +120,8 @@ export function writeClaudeMcpConfig(cwd: string, personality: string, daemonUrl
120
120
  //
121
121
  // codex's CLI does NOT import tools from an OPEN streamable-HTTP MCP server — it marks
122
122
  // it authStatus=unsupported (and BLOCKS on startup) unless an auth scheme is configured
123
- // (codex bug #21532 / #4707). The token-free fix (PROVEN LIVE, codex 0.136, owner
124
- // decision 08.06 — Артур rejected a real host-wide bearer as a localhost crutch): set a
123
+ // (codex bug #21532 / #4707). The token-free fix (PROVEN LIVE, codex 0.136; a real
124
+ // host-wide bearer was rejected as a localhost crutch): set a
125
125
  // FIXED, NON-SECRET bearer (CODEX_DUMMY_BEARER). Setting `bearer_token_env_var` flips
126
126
  // authStatus to `bearer_token` purely from the config FACT — codex does NOT require the
127
127
  // server to validate the token. The daemon stays OPEN: it ignores `Authorization` and
@@ -183,16 +183,16 @@ export function writeCodexMcpConfig(daemonUrl: string, options: StorageOptions =
183
183
  * Idempotently ensure codex's startup update-check DIALOG is disabled host-wide:
184
184
  * `check_for_update_on_startup = false` as a TOP-LEVEL key in config.toml.
185
185
  *
186
- * Why (incident 11.06, codex-linus): the interactive "✨ Update available! …
187
- * Press enter to continue" dialog is a BOOT GATE — a daemon-woken codex session
188
- * sits on it until a human attaches. The wake reported never-became-ready after
189
- * 4+ minutes and a notifier alert of the EMERGENCY contour (mining farm) was
190
- * lost; the session came alive only from a manual attach. Warm-on-demand peers
186
+ * Why: the interactive "✨ Update available! … Press enter to continue" dialog is
187
+ * a BOOT GATE — a daemon-woken codex session sits on it until a human attaches.
188
+ * Observed: the wake never became ready after 4+ minutes and a notifier alert of
189
+ * the EMERGENCY contour was lost; the session came alive only from a manual
190
+ * attach. Warm-on-demand peers
191
191
  * must reach ready hands-free — codex updates stay operator-driven (`codex
192
192
  * update` / brew), never a boot-time question to a headless session.
193
193
  *
194
194
  * Mechanics: the key is a documented top-level config.toml option (present in
195
- * the binary's own config schema; suppression PROVEN LIVE 11.06 on codex 0.138
195
+ * the binary's own config schema; suppression PROVEN LIVE on codex 0.138
196
196
  * with the 0.139 update pending — control boot showed the dialog, the keyed
197
197
  * boot went straight past it). A TOML top-level key must precede the first
198
198
  * table header, so the key is PREPENDED — appending would land it inside the
@@ -346,7 +346,7 @@ export async function initPeer(opts: InitPeerOptions): Promise<InitPeerResult> {
346
346
  let codexMcpConfigPath: string | undefined
347
347
  if (provisioned.runtime === 'codex' || hasCodexMarker(provisioned.cwd)) {
348
348
  codexMcpConfigPath = writeCodexMcpConfig(daemonUrl, { env }).path
349
- // Boot-gate hygiene (incident 11.06): a codex peer must never sit on the
349
+ // Boot-gate hygiene: a codex peer must never sit on the
350
350
  // interactive update dialog at wake — disable the startup check host-wide.
351
351
  ensureCodexUpdateCheckDisabled({ env })
352
352
  }
@@ -9,8 +9,8 @@
9
9
  //
10
10
  // macOS TCC: a stable PATH is NOT enough to keep grants through updates — TCC keys
11
11
  // on the code requirement, and an ad-hoc bun-compiled binary's requirement is its
12
- // CDHash (changes every build → re-prompts; live-proven 10.06, Артур's DX
13
- // requirement). signInstalledBinary (signing.ts) re-signs each install with the
12
+ // CDHash (changes every build → re-prompts; live-proven).
13
+ // signInstalledBinary (signing.ts) re-signs each install with the
14
14
  // stable local identity so the designated requirement — and the grants — survive.
15
15
 
16
16
  import { copyFileSync, existsSync, mkdirSync, renameSync, statSync } from 'fs'
@@ -1,6 +1,5 @@
1
1
  // Stable code-signing for the installed binary — TCC grants must SURVIVE updates
2
- // (Артур's DX requirement 10.06: «1 раз при установке, потом обновления не должны
3
- // опять триггерить»).
2
+ // (grant once at install; subsequent updates must not re-trigger the TCC prompts).
4
3
  //
5
4
  // ROOT CAUSE (proven on the host): `bun build --compile` output is ad-hoc
6
5
  // linker-signed (Identifier=a.out, no cert chain) — its only stable identity is
@@ -8,7 +7,7 @@
8
7
  // requirement; for an ad-hoc binary that collapses to the cdhash → every update
9
8
  // is a NEW TCC subject → re-prompts.
10
9
  //
11
- // FIX (proven live, /tmp experiment 10.06): a LOCAL self-signed code-signing
10
+ // FIX (proven live): a LOCAL self-signed code-signing
12
11
  // identity ("iapeer Local Codesign", created once at install) re-signs the binary
13
12
  // after every build. Two different binaries signed by it carry the IDENTICAL
14
13
  // designated requirement:
@@ -26,7 +25,7 @@ import { tmpdir } from 'os'
26
25
  import { join } from 'path'
27
26
  import { spawnSync } from 'child_process'
28
27
 
29
- /** CROSS-PRODUCT CONTRACT (agreed with iapeer-memory, 10.06): this CN is the
28
+ /** CROSS-PRODUCT CONTRACT: this CN is the
30
29
  * SHARED signing identity of the whole agfpd stack. Each product signs with its
31
30
  * OWN --identifier (foundation: com.agfpd.iapeer; memory: com.agfpd.iapeer-memory),
32
31
  * so TCC subjects stay separate while the host carries ONE key (one keychain
@@ -91,8 +91,8 @@ export const claudeAdapter: RuntimeAdapter = {
91
91
  usesDoctrine: true,
92
92
 
93
93
  /**
94
- * Delivery markers for submitIntoTui (07.06 refactor — moved out of transport's
95
- * PROMPT_GLYPHS union into the adapter; docs/Рантайм claude "Доставка"):
94
+ * Delivery markers for submitIntoTui (moved out of transport's PROMPT_GLYPHS
95
+ * union into the adapter; docs/Рантайм claude "Доставка"):
96
96
  * - promptGlyphs ['❯'] — the same U+276F input-prompt glyph as isInputReady;
97
97
  * submitIntoTui finds the prompt row by it. claude-only (codex uses '›'), so
98
98
  * a stray codex glyph in a claude pane no longer false-matches.
@@ -151,10 +151,10 @@ export const claudeAdapter: RuntimeAdapter = {
151
151
  * - RESUME COMPACT-PICKER ('Resume from summary …') → CURSOR-VERIFIED two-step.
152
152
  * This menu's cursor DEFAULTS to "1. Resume from summary (recommended)",
153
153
  * and selecting it COMPACTS the session (claude implements that choice as an
154
- * internal /compact) — the owner's invariant is «на resume не должно быть
155
- * компакта». The original blind ['Down','Enter'] burst (0e67e1f) put BOTH
156
- * keys into one pty chunk; under a heavy resume (boris 10.06, 313k-token
157
- * session) the TUI dropped the Down and the Enter confirmed the DEFAULT →
154
+ * internal /compact) — but a resume must NOT trigger a compact. The original
155
+ * blind ['Down','Enter'] burst (0e67e1f) put BOTH keys into one pty chunk;
156
+ * under a heavy resume (a 313k-token session) the TUI dropped the Down and
157
+ * the Enter confirmed the DEFAULT →
158
158
  * silent /compact (proven: pane log shows the picker frame with ❯ on
159
159
  * option 1 followed by "❯ /compact"; transcript compactMetadata
160
160
  * trigger=manual). Fix: ONE key per boot-iteration — Down while the cursor
@@ -256,7 +256,7 @@ export const claudeAdapter: RuntimeAdapter = {
256
256
  * + docs/TUI-взаимодействие):
257
257
  * - interrupt → ['Escape'] (claude interrupts the current turn with ONE Escape;
258
258
  * the session + context stay intact — distinct from the `stop` verb which halts
259
- * the session). The "кнопка заткнуть бредящего" without losing context.
259
+ * the session). Interrupts a runaway turn without losing context.
260
260
  * - compact → type '/compact' then Enter (claude's context-compaction slash).
261
261
  * - anything else → null (unsupported → explicit refusal upstream).
262
262
  */
@@ -154,8 +154,7 @@ export const codexAdapter: RuntimeAdapter = {
154
154
  usesDoctrine: true,
155
155
 
156
156
  /**
157
- * Delivery markers for submitIntoTui (07.06 refactor; docs/Рантайм codex
158
- * "Доставка"):
157
+ * Delivery markers for submitIntoTui (docs/Рантайм codex "Доставка"):
159
158
  * - promptGlyphs ['›'] — codex's input-row arrow (the same stable invariant as
160
159
  * isInputReady's '› ' check; the exact glyph the prompt row starts with).
161
160
  * codex-only, so a stray claude '❯' no longer false-matches.
@@ -234,12 +233,12 @@ export const codexAdapter: RuntimeAdapter = {
234
233
  * the input arrow is the stable ready invariant — not any single tip.)
235
234
  *
236
235
  * DELIBERATELY no 'OpenAI Codex' splash check (the watcher.codexInputReady
237
- * heritage requirement, removed 12.06): `resume --last` replays the prior
236
+ * heritage requirement, since removed): `resume --last` replays the prior
238
237
  * transcript ABOVE the composer, and a history taller than the pane scrolls
239
238
  * the splash box off the top of capture-pane — the predicate then stays false
240
239
  * for the entire boot deadline while the composer sits ready, failing the wake
241
- * `never-became-ready` and LOSING the triggering message (live incidents
242
- * 11.06 19:21Z + 12.06 12:07Z, codex-linus). A replayed history message also
240
+ * `never-became-ready` and LOSING the triggering message (observed live with a
241
+ * tall replayed history). A replayed history message also
243
242
  * renders with a leading '› ', so this can fire a frame before the composer
244
243
  * activates — benign: the paste/Enter queue in the pty and land in the
245
244
  * composer, and the ready-gate still verifies a real model turn (activity
@@ -302,7 +301,7 @@ export const codexAdapter: RuntimeAdapter = {
302
301
  * interrupts the turn — NOT a double. (Codex's own footer says "esc to
303
302
  * interrupt"; one Escape yields "■ Conversation interrupted". The contract's
304
303
  * open "×1/×2?" is resolved to ×1, same as claude.) Session + context intact.
305
- * - compact → type '/compact' then Enter. SNAPPED LIVE 12.06 (codex-cli
304
+ * - compact → type '/compact' then Enter. SNAPPED LIVE (codex-cli
306
305
  * 0.138.0): the slash EXISTS now — autocomplete documents it («summarize
307
306
  * conversation to prevent hitting the context limit») and a live run
308
307
  * yields «• Context compacted». The 0.136-era "codex has no /compact"
@@ -1,15 +1,15 @@
1
1
  // Server-death canary — the SERVER-level catcher for the exit-cause gap the
2
2
  // pane-died hook structurally cannot close (контракт: pane-died needs a living
3
3
  // tmux event loop; SIGKILL/OOM to the tmux SERVER runs no hook). Raised from the
4
- // deferred backlog after the SECOND live case of the class (iapeer-memory 10.06
5
- // 01:43, boris 10.06 11:41 — both: a healthy session with active Bash
6
- // subprocessing, the whole server gone silently, zero system traces).
4
+ // deferred backlog after the SECOND live case of the class (both: a healthy
5
+ // session with active Bash subprocessing, the whole server gone silently, zero
6
+ // system traces).
7
7
  //
8
8
  // Mechanism: one tiny detached `sh` per LIVE tmux server, holding a blocking
9
9
  // client `tmux -S <sock> wait-for iap-canary-<identity>` — a process OUTSIDE the
10
10
  // dying server, connected via its socket, so the server's death (any cause,
11
11
  // including SIGKILL) is observed the moment the connection drops. Protocol (v2 —
12
- // death #4 10.06 15:42Z proved v1's exit-code trust wrong twice over: the killer
12
+ // a live death proved v1's exit-code trust wrong twice over: the killer
13
13
  // took the SERVER and the canary CLIENT together, AND a TERMed tmux client
14
14
  // returns rc=0 — indistinguishable from a clean channel signal — so the rc-based
15
15
  // `0 || ≥128 → silent` guard silenced a real death):
@@ -26,11 +26,11 @@
26
26
  // into exits.log (the per-peer death-cause home, next to pane-died's
27
27
  // `ev=session-exit`) + a forensics snapshot (vm_stat / swap / top-RSS ps /
28
28
  // fresh DiagnosticReports) captured within seconds — the evidence the 60 s
29
- // supervise tick can never recover ("системных следов ноль" was the recurring
29
+ // supervise tick can never recover (zero system traces was the recurring
30
30
  // investigation outcome). PID identity matters: a wake/attach can replace the
31
31
  // tmux server on the same socket before the 2 s grace elapses; probing only
32
32
  // the socket would mistake the successor for the dead original and lose the
33
- // canary record (live canary-gap 11.06).
33
+ // canary record (a live canary-gap).
34
34
  // The raw wait_rc still ATTRIBUTES the death (cause=server-vanished /
35
35
  // signaled-server-gone / client-killed-server-gone).
36
36
  // Residual blind spot (structural): a killer that SIGKILLs the sh recorder
@@ -103,7 +103,7 @@ export function canaryScript(o: CanaryScriptOptions): string {
103
103
  `'${o.tmuxBin}' -S '${o.sock}' wait-for '${ch}'`,
104
104
  `rc=$?`,
105
105
  // NO wait-for exit code is trusted as "deliberate" by itself — PROVEN live
106
- // (death-#4 postmortem): a TERM to the tmux CLIENT returns rc=0, identical
106
+ // (a death postmortem): a TERM to the tmux CLIENT returns rc=0, identical
107
107
  // to a clean channel signal, so an external killer sweeping server+client
108
108
  // rides the clean-looking code straight past any rc-based guard (v1's
109
109
  // `rc=0 || rc>=128 → silent` was exactly that hole). v2 contract instead:
@@ -217,7 +217,7 @@ export function signalCanaryClean(sock: string, identity: string): void {
217
217
  /** The pgrep/pkill ERE matching BOTH canary processes of ONE identity — the sh
218
218
  * wrapper (its -c script quotes the channel: `…'iap-canary-<id>'…`) and the
219
219
  * tmux client (argv ends with the bare channel). Anchored so an identity can
220
- * never match another identity's prefix (claude-iapeer ≠ claude-iapeer-memory). */
220
+ * never match another identity's prefix (claude-alice ≠ claude-alice-bob). */
221
221
  export function canaryProcessPattern(identity: string): string {
222
222
  return `${canaryChannel(identity)}([^a-z0-9-]|$)`
223
223
  }
@@ -1,6 +1,6 @@
1
1
  // tmux command-log — the SOCKET-COMMAND observability layer of the killer hunt.
2
2
  //
3
- // Why (death class 11.06, boris's session-deaths track): a tmux server killed by a
3
+ // Why (a session-death class): a tmux server killed by a
4
4
  // CLIENT COMMAND (`kill-server` / `kill-session` over the unix socket) dies with
5
5
  // zero signals — structurally invisible to ALL THREE existing layers: the eslogger
6
6
  // trap (no kill(2) anywhere; kernel-originated SIGHUP to the panes is also proven
@@ -11,7 +11,7 @@
11
11
  // the attribution (client pid + exact command + timestamp) that decides the class
12
12
  // binarily: "killed by client X" vs "no command ⇒ internal exit".
13
13
  //
14
- // Sensitivity (boris's requirement): the -v log carries FULL send-keys payloads
14
+ // Sensitivity: the -v log carries FULL send-keys payloads
15
15
  // (every delivered envelope, plaintext) — tmux has no commands-only level and the
16
16
  // server writes the file itself (name tmux-server-<pid>.log in the STARTING
17
17
  // client's cwd — no FIFO/pipe interposition possible). Containment instead:
@@ -63,7 +63,7 @@ export function prepareCmdLogDir(eventLogDir: string, identity: string): string
63
63
 
64
64
  /** Clamp every file the freshly-started server/client dropped into the dir to
65
65
  * 0600 (the dir's 0700 already blocks traversal; this is the belt to that
66
- * suspender — boris's acceptance criterion: no file wider than 0600). */
66
+ * suspender — the acceptance criterion: no file wider than 0600). */
67
67
  export function hardenCmdLogDir(dir: string): void {
68
68
  try {
69
69
  for (const name of readdirSync(dir)) {
@@ -158,7 +158,8 @@ function renderRegistry(peers: readonly PublicPeerSummary[]): string {
158
158
  // • Layer 4: every non-IAPEER `<DOMAIN>.md` pair at the .iapeer/ root (operator /
159
159
  // plugin user-settings; SPAWNER_INSTRUCTIONS.md, … flow in organically).
160
160
  // • Layer 5: every `<STEM>.md` pair in .iapeer/fragments/ (primitive-owned,
161
- // machine-regenerated doctrine fragments — iapeer-memory's guide + note index).
161
+ // machine-regenerated doctrine fragments — e.g. a memory provider's guide +
162
+ // note index).
162
163
  // Each block contributes its GLOBAL half then its LOCAL half (general → specific),
163
164
  // EACH as its own marked file-section (a 0-byte / newline-only file → no section,
164
165
  // no dangling marker). Blocks are appended in the order given (the gatherer sorts
@@ -143,8 +143,8 @@ function ready(identity: string): LaunchResult {
143
143
  // Exit-cause observability — capture WHY a session's process died, AT THE MOMENT
144
144
  // of death. The daemon's 60 s supervise tick only learns of a death post-factum
145
145
  // (reaped-gone), by which time the exit code/signal — and often the whole tmux
146
- // server — is gone (the boris-fresh-style blind spot one level deeper than the
147
- // supervise log). A tmux `pane-died` hook closes it: it fires the instant the
146
+ // server — is gone (a blind spot one level deeper than the supervise log). A
147
+ // tmux `pane-died` hook closes it: it fires the instant the
148
148
  // pane's leader process exits (with `remain-on-exit on` retaining the dead pane so
149
149
  // `#{pane_dead_status}`/`#{pane_dead_signal}` are populated), logs one logfmt line,
150
150
  // then kill-sessions the now-dead pane so the daemon's `has-session` death
@@ -394,14 +394,13 @@ export const launch: LaunchFn = async (
394
394
  // `run-shell -b -d <maxAgeSecs>` → `kill-session`, intended to bound a zombie
395
395
  // even if no supervise sweep runs. It was a BLIND wall-clock kill armed ONCE
396
396
  // at birth and never re-armed, so it guillotined HEALTHY long-lived sessions
397
- // at exactly birth+maxAge (4 h) — the recurrent boris/iapeer "mystery deaths".
397
+ // at exactly birth+maxAge (4 h) — the recurrent "mystery deaths".
398
398
  // By construction any session that survives to maxAge is active: idle ones are
399
399
  // already idle-reaped at idleSecs (1 h) by the activity-aware supervisor — so
400
400
  // the blind kill could ONLY ever destroy live work (its sole "useful" case,
401
401
  // daemon dead the whole interval, leaves an inert orphan nothing can deliver
402
402
  // to, self-healed by idle-reap on daemon restart). The activity-aware idle-reap
403
- // (superviseTick) is now the SOLE lifecycle bound. See ADR «миграция …» / vault
404
- // note «Четырёхчасовой TTL-backstop …». (alwaysOn infra never armed it.)
403
+ // (superviseTick) is now the SOLE lifecycle bound. (alwaysOn infra never armed it.)
405
404
 
406
405
  // (5) Router runtime (telegram): no TUI input surface — there is no boot dialog
407
406
  // and no ready marker to gate on. The process is up; return READY.
@@ -488,7 +487,7 @@ export const launch: LaunchFn = async (
488
487
  }
489
488
  }
490
489
  if (!delivered) {
491
- // Surface WHAT the pane showed when the gate gave up — the 12.06 codex-resume
490
+ // Surface WHAT the pane showed when the gate gave up — a codex-resume
492
491
  // incident (splash scrolled off, composer ready, predicate false for 240 s) was
493
492
  // undiagnosable from the bare reason alone. Last two non-empty lines, quotes
494
493
  // flattened (the reason lands inside a logfmt-quoted err field), clipped.
@@ -50,7 +50,7 @@ export function buildLaunchInvocation(
50
50
  PEER_PERSONALITY: spec.personality,
51
51
  PEER_RUNTIME: spec.runtime,
52
52
  PEER_IDENTITY: spec.identity,
53
- PATH: env.PATH ?? `${homedir()}/.bun/bin:${homedir()}/.local/bin:/opt/homebrew/bin:/usr/bin:/bin`,
53
+ PATH: env.PATH ?? `${homedir()}/.bun/bin:${homedir()}/.local/bin:/opt/homebrew/bin:/opt/homebrew/sbin:/usr/local/bin:/usr/bin:/bin`,
54
54
  }
55
55
  // Strip the claude-code-internal session namespace from the inherited env. When a launch is
56
56
  // INITIATED from inside a running claude session (an operator CLI, or a claude peer shelling out),