@cocorograph/hub-agent 0.8.30 → 0.8.32
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 +1 -1
- package/src/sibling-inject.mjs +43 -14
package/package.json
CHANGED
package/src/sibling-inject.mjs
CHANGED
|
@@ -76,9 +76,22 @@ export const DELEGATION_LEDGER_RETENTION_MS = 24 * 60 * 60 * 1000
|
|
|
76
76
|
|
|
77
77
|
const execFile = promisify(execFileCallback)
|
|
78
78
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
79
|
+
/**
|
|
80
|
+
* `ps` の起動時刻を送受信双方で比較する契約形へ正規化する。
|
|
81
|
+
* 先頭・末尾の Unicode 空白だけを除去し、日時本体の内部空白は変更しない。
|
|
82
|
+
* 正本は Hub の `codex_agent.py:_normalize_process_started_at`。
|
|
83
|
+
*/
|
|
84
|
+
export function normalizeProcessStartedAt(raw) {
|
|
85
|
+
return raw.trim()
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export async function defaultPidStartedAt(pid, exec = execFile) {
|
|
89
|
+
// 送信側と同じ TZ/locale で比較可能な文字列を作る。契約の正本は Hub の codex_agent.py。
|
|
90
|
+
// launchd / WSL の環境は利用者のシェルと一致しないため、常駐サービスの環境は継承しない。
|
|
91
|
+
const { stdout } = await exec("ps", ["-o", "lstart=", "-p", String(pid)], {
|
|
92
|
+
env: { TZ: "UTC", LC_ALL: "C", LANG: "C" },
|
|
93
|
+
})
|
|
94
|
+
return normalizeProcessStartedAt(stdout)
|
|
82
95
|
}
|
|
83
96
|
|
|
84
97
|
const DELIVERY_UNCONFIRMED_HINT =
|
|
@@ -343,6 +356,13 @@ export class SiblingInjectBridge {
|
|
|
343
356
|
}
|
|
344
357
|
}
|
|
345
358
|
|
|
359
|
+
_acceptsDelegateEvent(cwd, sessionId, event) {
|
|
360
|
+
this._pruneDelegations()
|
|
361
|
+
if (event.phase !== "started") return true
|
|
362
|
+
const sessionKey = this._delegationSessionKey(cwd, sessionId)
|
|
363
|
+
return !this._terminalDelegations.get(sessionKey)?.has(event.delegation_id)
|
|
364
|
+
}
|
|
365
|
+
|
|
346
366
|
async _delegateProcessAlive(event) {
|
|
347
367
|
if (!Number.isInteger(event.pid) || event.pid <= 0 || !event.pid_started_at) return true
|
|
348
368
|
try {
|
|
@@ -352,7 +372,10 @@ export class SiblingInjectBridge {
|
|
|
352
372
|
if (err?.code !== "EPERM") return true
|
|
353
373
|
}
|
|
354
374
|
try {
|
|
355
|
-
return (
|
|
375
|
+
return (
|
|
376
|
+
normalizeProcessStartedAt(await this._pidStartedAt(event.pid)) ===
|
|
377
|
+
normalizeProcessStartedAt(event.pid_started_at)
|
|
378
|
+
)
|
|
356
379
|
} catch {
|
|
357
380
|
return true
|
|
358
381
|
}
|
|
@@ -598,17 +621,23 @@ export class SiblingInjectBridge {
|
|
|
598
621
|
...(pid_started_at !== undefined ? { pid_started_at } : {}),
|
|
599
622
|
timestamp: timestamp || new Date().toISOString(),
|
|
600
623
|
}
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
this._recordDelegateEvent(cwd, target_session_id, event)
|
|
606
|
-
result = { ok: true, delivered_to: r.session }
|
|
624
|
+
// 墓標確認から配送成功時の台帳反映まで await を挟まない同期区間に閉じる。
|
|
625
|
+
// 配送失敗した started は台帳へ残さず、終端後に遅着した started は配送前に拒否する。
|
|
626
|
+
if (!this._acceptsDelegateEvent(cwd, target_session_id, event)) {
|
|
627
|
+
result = { ok: false, error: "stale_delegate_event" }
|
|
607
628
|
} else {
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
629
|
+
const r = this.claudeBridge?.emitToStream
|
|
630
|
+
? this.claudeBridge.emitToStream(cwd, target_session_id, event)
|
|
631
|
+
: { status: "not_found" }
|
|
632
|
+
if (r.status === "delivered") {
|
|
633
|
+
this._recordDelegateEvent(cwd, target_session_id, event)
|
|
634
|
+
result = { ok: true, delivered_to: r.session }
|
|
635
|
+
} else {
|
|
636
|
+
result = {
|
|
637
|
+
ok: false,
|
|
638
|
+
error: AMBIGUOUS_TARGET_ERROR,
|
|
639
|
+
...(r.candidates ? { candidates: r.candidates } : {}),
|
|
640
|
+
}
|
|
612
641
|
}
|
|
613
642
|
}
|
|
614
643
|
}
|