@agfpd/iapeer 0.4.128 → 0.4.129

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.
@@ -336,7 +336,9 @@ nothing to spare). A sweep only opens files whose mtime moved since the last pas
336
336
 
337
337
  Attribution does not derive the path from `cwd`. It reads the cwd each file states **about
338
338
  itself** (claude: the `cwd` field on every line; codex: `session_meta.payload.cwd`) and matches
339
- it against the registry, case-insensitively. A file whose cwd belongs to no peer is ignored —
339
+ it against the registry, case-insensitively — the peer whose cwd is that path or its NEAREST
340
+ ancestor (a peer that `cd`-ed into a subdirectory stays itself, 18.10.2026; ancestors shallower
341
+ than `/<a>/<b>/<c>` match only exactly). A file whose cwd belongs to no peer is ignored —
340
342
  a human's own session never notifies.
341
343
 
342
344
  The reason is that one rule must serve both runtimes: the claude slug is a naming convention we
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agfpd/iapeer",
3
- "version": "0.4.128",
3
+ "version": "0.4.129",
4
4
  "description": "Run Claude Code and Codex CLI as local background agents that message each other, wake on demand, and share one memory.",
5
5
  "license": "Apache-2.0",
6
6
  "author": "agfpd",
@@ -557,6 +557,28 @@ export function muteWatchTick(deps: MuteWatchDeps, sinceMs: number): { raised: n
557
557
  return { raised, sinceMs: startedMs }
558
558
  }
559
559
 
560
+ /**
561
+ * personality ← a session's self-stated cwd: the peer whose registered cwd is that path OR its
562
+ * NEAREST ancestor. A session records the cwd it is IN at each line, and a peer that `cd`s into a
563
+ * subdirectory of its project (observed 18.10.2026: `…/oc-test-nemo/.iapeer`) would otherwise fall
564
+ * out of attribution — no notice, no limit-park. Longest prefix wins, so a peer nested inside
565
+ * another peer's tree is attributed to itself. Keys are normalized (trailing slash off, lowercase).
566
+ */
567
+ export function peerByCwdPrefix(byCwd: Map<string, string>, cwd: string): string | undefined {
568
+ const exact = cwd.replace(/\/+$/, '').toLowerCase()
569
+ let p = exact
570
+ while (p) {
571
+ const hit = byCwd.get(p)
572
+ // An ANCESTOR hit counts only below /<a>/<b>/<c> depth: a peer whose cwd is $HOME (or shallower)
573
+ // must not swallow every session on the host — such a peer attributes by exact match only.
574
+ if (hit && (p === exact || p.split('/').length > 4)) return hit
575
+ const i = p.lastIndexOf('/')
576
+ if (i <= 0) return undefined
577
+ p = p.slice(0, i)
578
+ }
579
+ return undefined
580
+ }
581
+
560
582
  export interface StartMuteWatchOptions {
561
583
  env?: NodeJS.ProcessEnv
562
584
  intervalMs?: number
@@ -604,7 +626,7 @@ export function startMuteWatch(cfg: LifecycleConfig, board: NoticeBoard, opts: S
604
626
  now,
605
627
  onError: opts.onError,
606
628
  onLimit: opts.onLimit,
607
- peerByCwd: (cwd: string) => byCwd.get(cwd.replace(/\/+$/, '').toLowerCase()),
629
+ peerByCwd: (cwd: string) => peerByCwdPrefix(byCwd, cwd),
608
630
  opencodeJournals: () =>
609
631
  rows
610
632
  .filter(r => (r.runtimes ?? []).includes('opencode'))