@agfpd/iapeer 0.4.95 → 0.4.96
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/docs/19-notices.md +17 -7
- package/package.json +1 -1
- package/src/daemon/mutewatch.ts +12 -6
- package/src/transport/index.ts +34 -5
package/docs/19-notices.md
CHANGED
|
@@ -210,13 +210,23 @@ The daemon sweeps on its own timer (`DEFAULT_MUTEWATCH_INTERVAL_MS`, 20 s — de
|
|
|
210
210
|
is the product here, and the 60 s supervise tick would put the worst case at the budget with
|
|
211
211
|
nothing to spare). A sweep only opens files whose mtime moved since the last pass.
|
|
212
212
|
|
|
213
|
-
|
|
214
|
-
**
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
213
|
+
Attribution does not derive the path from `cwd`. It reads the cwd each file states **about
|
|
214
|
+
itself** (claude: the `cwd` field on every line; codex: `session_meta.payload.cwd`) and matches
|
|
215
|
+
it against the registry, case-insensitively. A file whose cwd belongs to no peer is ignored —
|
|
216
|
+
a human's own session never notifies.
|
|
217
|
+
|
|
218
|
+
The reason is that one rule must serve both runtimes: the claude slug is a naming convention we
|
|
219
|
+
do not own, and a codex rollout carries no cwd in its path at all (`YYYY/MM/DD/rollout-*`), so
|
|
220
|
+
there is nothing to derive from. Reading the file's own statement works for both.
|
|
221
|
+
|
|
222
|
+
> **Correction (15.07.2026).** This section previously justified the choice by claiming the host
|
|
223
|
+
> carries BOTH `-Users-macmini-Projects-IAPeer` and `-Users-macmini-Projects-iapeer` as two
|
|
224
|
+
> directories for one case-insensitive path. **That was false.** `readdir` lists exactly one
|
|
225
|
+
> (`…-IAPeer`), and both spellings `stat` to the same inode — the filesystem is case-insensitive,
|
|
226
|
+
> so a slug of differing case resolves correctly, and the confirm path in `transport` derives its
|
|
227
|
+
> slug this way and works. The claim came from reading a `grep -c` result of 2 whose second hit was
|
|
228
|
+
> `…-Projects-iapeer-memory`: a count was trusted instead of the entries. The design choice is
|
|
229
|
+
> unchanged; its stated reason is.
|
|
220
230
|
|
|
221
231
|
## 7. What is proven, and what is not
|
|
222
232
|
|
package/package.json
CHANGED
package/src/daemon/mutewatch.ts
CHANGED
|
@@ -31,12 +31,18 @@
|
|
|
31
31
|
// binary but never observed live here), so an unknown future variant still detects.
|
|
32
32
|
// Codex DOES carry resets_at per window → its notice states the reset time.
|
|
33
33
|
//
|
|
34
|
-
// PATHS ARE
|
|
35
|
-
//
|
|
36
|
-
//
|
|
37
|
-
//
|
|
38
|
-
//
|
|
39
|
-
//
|
|
34
|
+
// PATHS ARE NOT DERIVED FROM cwd — but NOT for the reason first claimed here. The original note
|
|
35
|
+
// said this host carries BOTH `-Users-macmini-Projects-IAPeer` and `-Users-macmini-Projects-iapeer`
|
|
36
|
+
// as two directories. That was FALSE, and measured to be false on 15.07: readdir lists exactly one
|
|
37
|
+
// (`…-IAPeer`), and both spellings stat to the SAME inode — the filesystem is case-insensitive, so
|
|
38
|
+
// a slug whose case differs from the on-disk name resolves fine. (The claim came from a grep -c of
|
|
39
|
+
// 2, whose second hit was `…-Projects-iapeer-memory`. The count was read instead of the entries.)
|
|
40
|
+
//
|
|
41
|
+
// The real reason stands on its own: the slug mirrors the cwd STRING as the process was launched,
|
|
42
|
+
// which is a claude-specific naming convention we do not own, and codex has no cwd in its path at
|
|
43
|
+
// all (YYYY/MM/DD/rollout-*). One attribution rule that works for both runtimes beats two derivation
|
|
44
|
+
// schemes: scan the session files that CHANGED, read the cwd each file states about ITSELF (claude:
|
|
45
|
+
// the `cwd` field on every line; codex: session_meta.payload.cwd), and match that against the
|
|
40
46
|
// registry. A file whose cwd is no peer's is ignored — a human's own session never notifies.
|
|
41
47
|
|
|
42
48
|
import { existsSync, openSync, closeSync, fstatSync, readSync, readdirSync, statSync } from 'node:fs'
|
package/src/transport/index.ts
CHANGED
|
@@ -182,11 +182,40 @@ const LIVENESS_POLL_MS = 100
|
|
|
182
182
|
// `IAP_HOST_LIVENESS_GRACE_MS` overrides (operator calibration; tests set 0 for an immediate
|
|
183
183
|
// deterministic fail). Prefer a false-FAIL (sender retries) over a false-OK (silent loss, forbidden).
|
|
184
184
|
const CONFIRM_GRACE_MS = 4000
|
|
185
|
-
|
|
186
|
-
|
|
185
|
+
|
|
186
|
+
// ATTACHMENT grace. The "sub-second" assumption above holds only WITHOUT attachments. Measured
|
|
187
|
+
// 15.07.2026 on a live att=4 delivery (1.3 MB of PNGs) into an IDLE receiver: the record was
|
|
188
|
+
// stamped 19:34:59.269, the confirm polled the right file until 19:35:03.4 and did not see it,
|
|
189
|
+
// and the receiver's first output came at 19:35:21.7. The receiver writes the user-record when it
|
|
190
|
+
// BEGINS the turn, and loading/encoding the images pushed that start ~22 s out — so a 4 s grace
|
|
191
|
+
// expired on a message that had landed. Every att>0 delivery to a claude receiver false-FAILED,
|
|
192
|
+
// the sender retried on the lie, and the message duplicated in the receiver's context.
|
|
193
|
+
//
|
|
194
|
+
// 60 s is a CONSERVATIVE BOUND FROM THAT MEASUREMENT (≤22 s observed, ×3 headroom), not a
|
|
195
|
+
// calibrated number: the exact paste→disk latency is still unmeasured (see docs/10). It costs
|
|
196
|
+
// nothing on the happy path — the confirm returns the moment the record lands — and costs the
|
|
197
|
+
// sender up to a minute only when an attachment message genuinely fails, which is rare.
|
|
198
|
+
const CONFIRM_GRACE_ATTACHMENTS_MS = 60_000
|
|
199
|
+
|
|
200
|
+
function envNonNegative(raw: string | undefined): number | null {
|
|
187
201
|
const n = raw === undefined ? NaN : Number(raw)
|
|
188
|
-
|
|
189
|
-
|
|
202
|
+
return Number.isFinite(n) && n >= 0 ? n : null
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/** Does this envelope carry attachments — i.e. will the receiver have to ingest files before it
|
|
206
|
+
* records anything? Same marker `confirmNeedles` splits on, so the two cannot disagree. */
|
|
207
|
+
export function envelopeHasAttachments(envelope: string): boolean {
|
|
208
|
+
return envelope.includes('<attachments>') && envelope.includes('</attachments>')
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
function confirmGraceMs(hasAttachments: boolean): number {
|
|
212
|
+
// An EXPLICIT operator/test override wins for both modes — tests set 0 for a deterministic
|
|
213
|
+
// immediate fail, and an operator calibrating one number must not be silently overridden by
|
|
214
|
+
// the attachment branch.
|
|
215
|
+
const explicit = envNonNegative(process.env.IAP_HOST_LIVENESS_GRACE_MS)
|
|
216
|
+
if (explicit !== null) return explicit
|
|
217
|
+
if (!hasAttachments) return CONFIRM_GRACE_MS
|
|
218
|
+
return envNonNegative(process.env.IAP_HOST_LIVENESS_GRACE_ATTACHMENTS_MS) ?? CONFIRM_GRACE_ATTACHMENTS_MS
|
|
190
219
|
}
|
|
191
220
|
|
|
192
221
|
// Launchd-revive retry — a MISS on a launchd-managed target the daemon can't wake (H4)
|
|
@@ -328,7 +357,7 @@ async function deliverViaHost(
|
|
|
328
357
|
// false-OK (silent loss the contract forbids).
|
|
329
358
|
const confirm = seam.confirmLanded ?? ((payload: string) => transcriptCarriesEnvelope(baseline, payload, { env }))
|
|
330
359
|
const sleep = seam.sleep ?? ((ms: number) => new Promise<void>(r => setTimeout(r, ms)))
|
|
331
|
-
const graceMs = confirmGraceMs()
|
|
360
|
+
const graceMs = confirmGraceMs(envelopeHasAttachments(envelope))
|
|
332
361
|
const graceDeadline = monotonicMs() + graceMs
|
|
333
362
|
while (true) {
|
|
334
363
|
if (confirm(envelope)) return ok(undefined)
|