oh-my-codex 0.21.13 → 0.21.15
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/Cargo.lock +6 -6
- package/Cargo.toml +1 -1
- package/README.md +17 -0
- package/dist/adapt/herdr/__tests__/wiring.test.js +21 -0
- package/dist/adapt/herdr/__tests__/wiring.test.js.map +1 -1
- package/dist/adapt/herdr/wiring.d.ts.map +1 -1
- package/dist/adapt/herdr/wiring.js +5 -0
- package/dist/adapt/herdr/wiring.js.map +1 -1
- package/dist/auth/__tests__/hotswap.test.js +28 -0
- package/dist/auth/__tests__/hotswap.test.js.map +1 -1
- package/dist/auth/hotswap.d.ts +1 -0
- package/dist/auth/hotswap.d.ts.map +1 -1
- package/dist/auth/hotswap.js +4 -1
- package/dist/auth/hotswap.js.map +1 -1
- package/dist/cli/__tests__/codex-plugin-layout.test.js +196 -0
- package/dist/cli/__tests__/codex-plugin-layout.test.js.map +1 -1
- package/dist/cli/__tests__/doctor-warning-copy.test.js +2 -1
- package/dist/cli/__tests__/doctor-warning-copy.test.js.map +1 -1
- package/dist/cli/__tests__/index.test.js +14 -1
- package/dist/cli/__tests__/index.test.js.map +1 -1
- package/dist/cli/__tests__/session-timeline.test.d.ts +2 -0
- package/dist/cli/__tests__/session-timeline.test.d.ts.map +1 -0
- package/dist/cli/__tests__/session-timeline.test.js +155 -0
- package/dist/cli/__tests__/session-timeline.test.js.map +1 -0
- package/dist/cli/__tests__/setup-hooks-shared-ownership.test.js +1 -0
- package/dist/cli/__tests__/setup-hooks-shared-ownership.test.js.map +1 -1
- package/dist/cli/doctor.d.ts.map +1 -1
- package/dist/cli/doctor.js +1 -0
- package/dist/cli/doctor.js.map +1 -1
- package/dist/cli/index.d.ts +1 -0
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +11 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/session-search.d.ts.map +1 -1
- package/dist/cli/session-search.js +7 -0
- package/dist/cli/session-search.js.map +1 -1
- package/dist/cli/session-timeline.d.ts +9 -0
- package/dist/cli/session-timeline.d.ts.map +1 -0
- package/dist/cli/session-timeline.js +174 -0
- package/dist/cli/session-timeline.js.map +1 -0
- package/dist/config/__tests__/codex-hooks.test.js +3 -1
- package/dist/config/__tests__/codex-hooks.test.js.map +1 -1
- package/dist/config/__tests__/generator-idempotent.test.js +1 -0
- package/dist/config/__tests__/generator-idempotent.test.js.map +1 -1
- package/dist/config/codex-hooks.d.ts +1 -1
- package/dist/config/codex-hooks.d.ts.map +1 -1
- package/dist/config/codex-hooks.js +5 -0
- package/dist/config/codex-hooks.js.map +1 -1
- package/dist/evals/astra-defaults/__tests__/collector.test.js +51 -23
- package/dist/evals/astra-defaults/__tests__/collector.test.js.map +1 -1
- package/dist/evals/astra-defaults/collector.d.ts +2 -0
- package/dist/evals/astra-defaults/collector.d.ts.map +1 -1
- package/dist/evals/astra-defaults/collector.js +19 -8
- package/dist/evals/astra-defaults/collector.js.map +1 -1
- package/dist/hooks/extensibility/__tests__/dispatcher.test.js +143 -0
- package/dist/hooks/extensibility/__tests__/dispatcher.test.js.map +1 -1
- package/dist/hooks/extensibility/dispatcher.d.ts.map +1 -1
- package/dist/hooks/extensibility/dispatcher.js +120 -28
- package/dist/hooks/extensibility/dispatcher.js.map +1 -1
- package/dist/hooks/extensibility/runtime.d.ts.map +1 -1
- package/dist/hooks/extensibility/runtime.js +1 -0
- package/dist/hooks/extensibility/runtime.js.map +1 -1
- package/dist/hooks/extensibility/types.d.ts +2 -0
- package/dist/hooks/extensibility/types.d.ts.map +1 -1
- package/dist/hooks/native/types.d.ts +1 -1
- package/dist/hooks/native/types.d.ts.map +1 -1
- package/dist/hooks/native/types.js.map +1 -1
- package/dist/notifications/__tests__/lifecycle-dedupe.test.js +200 -2
- package/dist/notifications/__tests__/lifecycle-dedupe.test.js.map +1 -1
- package/dist/notifications/lifecycle-dedupe.d.ts +10 -0
- package/dist/notifications/lifecycle-dedupe.d.ts.map +1 -1
- package/dist/notifications/lifecycle-dedupe.js +175 -19
- package/dist/notifications/lifecycle-dedupe.js.map +1 -1
- package/dist/scripts/__tests__/codex-native-hook.test.js +278 -6
- package/dist/scripts/__tests__/codex-native-hook.test.js.map +1 -1
- package/dist/scripts/__tests__/native-hook-protocol.test.d.ts +2 -0
- package/dist/scripts/__tests__/native-hook-protocol.test.d.ts.map +1 -0
- package/dist/scripts/__tests__/native-hook-protocol.test.js +329 -0
- package/dist/scripts/__tests__/native-hook-protocol.test.js.map +1 -0
- package/dist/scripts/__tests__/smoke-packed-install.test.js +7 -6
- package/dist/scripts/__tests__/smoke-packed-install.test.js.map +1 -1
- package/dist/scripts/codex-execution-surface.d.ts +1 -1
- package/dist/scripts/codex-execution-surface.d.ts.map +1 -1
- package/dist/scripts/codex-execution-surface.js.map +1 -1
- package/dist/scripts/codex-native-hook.d.ts +1 -1
- package/dist/scripts/codex-native-hook.d.ts.map +1 -1
- package/dist/scripts/codex-native-hook.js +124 -13
- package/dist/scripts/codex-native-hook.js.map +1 -1
- package/dist/scripts/hook-payload-guard.d.ts +1 -1
- package/dist/scripts/hook-payload-guard.d.ts.map +1 -1
- package/dist/scripts/hook-payload-guard.js +1 -0
- package/dist/scripts/hook-payload-guard.js.map +1 -1
- package/dist/scripts/smoke-packed-install.d.ts +2 -2
- package/dist/scripts/smoke-packed-install.d.ts.map +1 -1
- package/dist/scripts/smoke-packed-install.js +12 -1
- package/dist/scripts/smoke-packed-install.js.map +1 -1
- package/dist/session-history/__tests__/timeline.test.d.ts +2 -0
- package/dist/session-history/__tests__/timeline.test.d.ts.map +1 -0
- package/dist/session-history/__tests__/timeline.test.js +459 -0
- package/dist/session-history/__tests__/timeline.test.js.map +1 -0
- package/dist/session-history/export.d.ts +2 -3
- package/dist/session-history/export.d.ts.map +1 -1
- package/dist/session-history/export.js +5 -66
- package/dist/session-history/export.js.map +1 -1
- package/dist/session-history/timeline.d.ts +97 -0
- package/dist/session-history/timeline.d.ts.map +1 -0
- package/dist/session-history/timeline.js +325 -0
- package/dist/session-history/timeline.js.map +1 -0
- package/dist/session-history/transcript.d.ts +16 -0
- package/dist/session-history/transcript.d.ts.map +1 -0
- package/dist/session-history/transcript.js +111 -0
- package/dist/session-history/transcript.js.map +1 -0
- package/dist/subagents/tracker.d.ts +6 -0
- package/dist/subagents/tracker.d.ts.map +1 -1
- package/dist/subagents/tracker.js +32 -0
- package/dist/subagents/tracker.js.map +1 -1
- package/dist/team/__tests__/runtime.test.js +12 -3
- package/dist/team/__tests__/runtime.test.js.map +1 -1
- package/dist/team/__tests__/scaling.test.js +25 -0
- package/dist/team/__tests__/scaling.test.js.map +1 -1
- package/dist/team/__tests__/tmux-session.test.js +30 -0
- package/dist/team/__tests__/tmux-session.test.js.map +1 -1
- package/dist/team/tmux-session.d.ts.map +1 -1
- package/dist/team/tmux-session.js +9 -1
- package/dist/team/tmux-session.js.map +1 -1
- package/dist/wiki/__tests__/session-hooks.test.js +26 -0
- package/dist/wiki/__tests__/session-hooks.test.js.map +1 -1
- package/dist/wiki/lifecycle.d.ts.map +1 -1
- package/dist/wiki/lifecycle.js +6 -1
- package/dist/wiki/lifecycle.js.map +1 -1
- package/package.json +1 -1
- package/plugins/oh-my-codex/.codex-plugin/plugin.json +1 -1
- package/plugins/oh-my-codex/hooks/codex-native-hook.mjs +85 -18
- package/plugins/oh-my-codex/hooks/hooks.json +10 -0
- package/src/scripts/__tests__/codex-native-hook.test.ts +273 -5
- package/src/scripts/__tests__/native-hook-protocol.test.ts +362 -0
- package/src/scripts/__tests__/smoke-packed-install.test.ts +7 -6
- package/src/scripts/codex-execution-surface.ts +1 -0
- package/src/scripts/codex-native-hook.ts +130 -11
- package/src/scripts/hook-payload-guard.ts +1 -0
- package/src/scripts/smoke-packed-install.ts +12 -1
|
@@ -154,6 +154,7 @@ export interface HookDispatchResult {
|
|
|
154
154
|
export type HookDispatchSummary = HookDispatchResult;
|
|
155
155
|
export interface HookDispatchOptions {
|
|
156
156
|
cwd?: string;
|
|
157
|
+
pluginDiscoveryCwd?: string;
|
|
157
158
|
stateRoot?: string;
|
|
158
159
|
event?: HookEventEnvelope;
|
|
159
160
|
env?: NodeJS.ProcessEnv;
|
|
@@ -170,6 +171,7 @@ export interface HookValidateOptions {
|
|
|
170
171
|
}
|
|
171
172
|
export interface HookRuntimeDispatchInput {
|
|
172
173
|
cwd: string;
|
|
174
|
+
pluginDiscoveryCwd?: string;
|
|
173
175
|
event: HookEventEnvelope;
|
|
174
176
|
allowTeamWorkerSideEffects?: boolean;
|
|
175
177
|
sideEffectsEnabled?: boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/hooks/extensibility/types.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/hooks/extensibility/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* notifications only. PreToolUse is advisory-only except authority-decreasing
|
|
5
5
|
* cancel interception.
|
|
6
6
|
*/
|
|
7
|
-
export type CodexHookEventName = "SessionStart" | "PreToolUse" | "PostToolUse" | "UserPromptSubmit" | "PreCompact" | "PostCompact" | "Stop";
|
|
7
|
+
export type CodexHookEventName = "SessionStart" | "PreToolUse" | "PostToolUse" | "UserPromptSubmit" | "PreCompact" | "PostCompact" | "SubagentStop" | "Stop";
|
|
8
8
|
export type CodexHookPayload = Record<string, unknown>;
|
|
9
9
|
export interface NativeHookDispatchOptions {
|
|
10
10
|
cwd?: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/hooks/native/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,MAAM,kBAAkB,GAC1B,cAAc,GACd,YAAY,GACZ,aAAa,GACb,kBAAkB,GAClB,YAAY,GACZ,aAAa,GACb,MAAM,CAAC;AAEX,MAAM,MAAM,gBAAgB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAEvD,MAAM,WAAW,yBAAyB;IACxC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,yFAAyF;IACzF,mBAAmB,CAAC,EAAE;QACpB,QAAQ,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC;QAC3B,eAAe,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;KAC1D,CAAC;IACF,6BAA6B,CAAC,EAAE,CAC9B,GAAG,EAAE,MAAM,EACX,OAAO,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,EAAE,CAAA;KAAE,KAC3D,OAAO,CAAC,OAAO,CAAC,CAAC;CACvB;AAED,MAAM,WAAW,wBAAwB;IACvC,aAAa,EAAE,kBAAkB,GAAG,IAAI,CAAC;IACzC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,UAAU,EAAE,OAAO,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;CAC5C;AAED,sFAAsF;AACtF,eAAO,MAAM,+BAA+B,aAa1C,CAAC"}
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/hooks/native/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,MAAM,kBAAkB,GAC1B,cAAc,GACd,YAAY,GACZ,aAAa,GACb,kBAAkB,GAClB,YAAY,GACZ,aAAa,GACb,cAAc,GACd,MAAM,CAAC;AAEX,MAAM,MAAM,gBAAgB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAEvD,MAAM,WAAW,yBAAyB;IACxC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,yFAAyF;IACzF,mBAAmB,CAAC,EAAE;QACpB,QAAQ,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC;QAC3B,eAAe,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;KAC1D,CAAC;IACF,6BAA6B,CAAC,EAAE,CAC9B,GAAG,EAAE,MAAM,EACX,OAAO,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,EAAE,CAAA;KAAE,KAC3D,OAAO,CAAC,OAAO,CAAC,CAAC;CACvB;AAED,MAAM,WAAW,wBAAwB;IACvC,aAAa,EAAE,kBAAkB,GAAG,IAAI,CAAC;IACzC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,UAAU,EAAE,OAAO,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;CAC5C;AAED,sFAAsF;AACtF,eAAO,MAAM,+BAA+B,aAa1C,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/hooks/native/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/hooks/native/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAmCH,sFAAsF;AACtF,MAAM,CAAC,MAAM,+BAA+B,GAAG,IAAI,GAAG,CAAC;IACrD,yBAAyB;IACzB,iBAAiB;IACjB,iBAAiB;IACjB,iBAAiB;IACjB,cAAc;IACd,kBAAkB;IAClB,mBAAmB;IACnB,cAAc;IACd,gBAAgB;IAChB,cAAc;IACd,iBAAiB;IACjB,mBAAmB;CACpB,CAAC,CAAC"}
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { describe, it } from 'node:test';
|
|
2
2
|
import assert from 'node:assert/strict';
|
|
3
|
-
import {
|
|
3
|
+
import { spawn } from 'node:child_process';
|
|
4
|
+
import { existsSync } from 'node:fs';
|
|
5
|
+
import { mkdir, mkdtemp, readFile, rm, writeFile } from 'node:fs/promises';
|
|
4
6
|
import { tmpdir } from 'node:os';
|
|
5
7
|
import { join } from 'node:path';
|
|
6
|
-
import { shouldSendLifecycleNotification, recordLifecycleNotificationSent, shouldSendLifecycleHookBroadcast, recordLifecycleHookBroadcastSent, } from '../lifecycle-dedupe.js';
|
|
8
|
+
import { claimLifecycleHookBroadcast, completeLifecycleHookBroadcast, shouldSendLifecycleNotification, recordLifecycleNotificationSent, shouldSendLifecycleHookBroadcast, recordLifecycleHookBroadcastSent, renewLifecycleHookBroadcastClaim, releaseLifecycleHookBroadcast, } from '../lifecycle-dedupe.js';
|
|
7
9
|
function buildPayload(overrides = {}) {
|
|
8
10
|
return {
|
|
9
11
|
event: 'session-end',
|
|
@@ -16,7 +18,203 @@ function buildPayload(overrides = {}) {
|
|
|
16
18
|
...overrides,
|
|
17
19
|
};
|
|
18
20
|
}
|
|
21
|
+
async function waitForFiles(paths) {
|
|
22
|
+
const deadline = Date.now() + 10_000;
|
|
23
|
+
while (!paths.every(existsSync)) {
|
|
24
|
+
if (Date.now() >= deadline)
|
|
25
|
+
throw new Error(`Timed out waiting for files: ${paths.join(', ')}`);
|
|
26
|
+
await new Promise((resolve) => setTimeout(resolve, 5));
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
function waitForChildExit(child) {
|
|
30
|
+
return new Promise((resolve, reject) => {
|
|
31
|
+
child.once('error', reject);
|
|
32
|
+
child.once('close', (code, signal) => {
|
|
33
|
+
if (code === 0)
|
|
34
|
+
resolve();
|
|
35
|
+
else
|
|
36
|
+
reject(new Error(`Child process exited with ${code ?? signal}`));
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
}
|
|
19
40
|
describe('lifecycle notification dedupe', () => {
|
|
41
|
+
it('atomically claims duplicate child-hook deliveries and preserves other children across processes', async () => {
|
|
42
|
+
const stateDir = await mkdtemp(join(tmpdir(), 'omx-lifecycle-concurrent-hook-'));
|
|
43
|
+
const sessionId = 'parallel-session';
|
|
44
|
+
const statePath = join(stateDir, 'sessions', sessionId, 'lifecycle-notif-state.json');
|
|
45
|
+
const readyPath = join(stateDir, 'lock-ready');
|
|
46
|
+
const releasePath = join(stateDir, 'lock-release');
|
|
47
|
+
const trackerModuleUrl = new URL('../../subagents/tracker.js', import.meta.url).href;
|
|
48
|
+
const lifecycleModuleUrl = new URL('../lifecycle-dedupe.js', import.meta.url).href;
|
|
49
|
+
const holderSource = `
|
|
50
|
+
import { existsSync, writeFileSync } from 'node:fs';
|
|
51
|
+
import { withCrossProcessFileLockSync } from ${JSON.stringify(trackerModuleUrl)};
|
|
52
|
+
withCrossProcessFileLockSync(${JSON.stringify(statePath)}, () => {
|
|
53
|
+
writeFileSync(${JSON.stringify(readyPath)}, 'ready');
|
|
54
|
+
const waitArray = new Int32Array(new SharedArrayBuffer(4));
|
|
55
|
+
const deadline = Date.now() + 10_000;
|
|
56
|
+
while (!existsSync(${JSON.stringify(releasePath)}) && Date.now() < deadline) {
|
|
57
|
+
Atomics.wait(waitArray, 0, 0, 5);
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
`;
|
|
61
|
+
const holder = spawn(process.execPath, ['--input-type=module', '-e', holderSource], { stdio: 'ignore' });
|
|
62
|
+
const holderExit = waitForChildExit(holder);
|
|
63
|
+
const children = [];
|
|
64
|
+
try {
|
|
65
|
+
await waitForFiles([readyPath]);
|
|
66
|
+
const writers = [
|
|
67
|
+
{ writerId: 'child-a-first', childId: 'child-a' },
|
|
68
|
+
{ writerId: 'child-a-replay', childId: 'child-a' },
|
|
69
|
+
{ writerId: 'child-b-first', childId: 'child-b' },
|
|
70
|
+
];
|
|
71
|
+
for (const { writerId, childId } of writers) {
|
|
72
|
+
const startedPath = join(stateDir, `${writerId}.started`);
|
|
73
|
+
const donePath = join(stateDir, `${writerId}.done`);
|
|
74
|
+
const eventKey = `subagent-stop:${JSON.stringify([childId, 'turn-1'])}`;
|
|
75
|
+
const writerSource = `
|
|
76
|
+
import { writeFileSync } from 'node:fs';
|
|
77
|
+
import { claimLifecycleHookBroadcast } from ${JSON.stringify(lifecycleModuleUrl)};
|
|
78
|
+
writeFileSync(${JSON.stringify(startedPath)}, 'started');
|
|
79
|
+
const claim = claimLifecycleHookBroadcast(${JSON.stringify(stateDir)}, ${JSON.stringify(sessionId)}, ${JSON.stringify(eventKey)}, ${JSON.stringify(childId)});
|
|
80
|
+
writeFileSync(${JSON.stringify(donePath)}, JSON.stringify(claim));
|
|
81
|
+
`;
|
|
82
|
+
const child = spawn(process.execPath, ['--input-type=module', '-e', writerSource], { stdio: 'ignore' });
|
|
83
|
+
children.push({ child, childId, startedPath, donePath, exit: waitForChildExit(child) });
|
|
84
|
+
}
|
|
85
|
+
await waitForFiles(children.map(({ startedPath }) => startedPath));
|
|
86
|
+
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
87
|
+
assert.deepEqual(children.map(({ donePath }) => existsSync(donePath)), [false, false, false]);
|
|
88
|
+
await writeFile(releasePath, 'release');
|
|
89
|
+
await Promise.all(children.map(({ exit }) => exit));
|
|
90
|
+
await holderExit;
|
|
91
|
+
const results = await Promise.all(children.map(async (writer) => ({
|
|
92
|
+
...writer,
|
|
93
|
+
...JSON.parse(await readFile(writer.donePath, 'utf8')),
|
|
94
|
+
})));
|
|
95
|
+
assert.equal(results.filter(({ childId, acquired }) => childId === 'child-a' && acquired).length, 1);
|
|
96
|
+
assert.equal(results.filter(({ childId, acquired }) => childId === 'child-b' && acquired).length, 1);
|
|
97
|
+
const state = JSON.parse(await readFile(statePath, 'utf8'));
|
|
98
|
+
for (const childId of ['child-a', 'child-b']) {
|
|
99
|
+
const eventKey = `subagent-stop:${JSON.stringify([childId, 'turn-1'])}`;
|
|
100
|
+
assert.equal(state.hookEvents[eventKey].fingerprint, childId);
|
|
101
|
+
assert.ok(state.hookEvents[eventKey].claimId);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
finally {
|
|
105
|
+
await writeFile(releasePath, 'release').catch(() => { });
|
|
106
|
+
for (const { child } of children)
|
|
107
|
+
child.kill();
|
|
108
|
+
holder.kill();
|
|
109
|
+
await Promise.allSettled([...children.map(({ exit }) => exit), holderExit]);
|
|
110
|
+
await rm(stateDir, { recursive: true, force: true });
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
it('releases failed child-hook claims and records successful dispatches', async () => {
|
|
114
|
+
const stateDir = await mkdtemp(join(tmpdir(), 'omx-lifecycle-hook-claim-'));
|
|
115
|
+
const sessionId = 'claim-session';
|
|
116
|
+
const eventKey = 'subagent-stop:["child-a","turn-1"]';
|
|
117
|
+
const startMs = Date.parse('2026-04-12T00:00:00.000Z');
|
|
118
|
+
try {
|
|
119
|
+
const failedDispatchClaim = claimLifecycleHookBroadcast(stateDir, sessionId, eventKey, 'fingerprint', startMs);
|
|
120
|
+
assert.equal(failedDispatchClaim.acquired, true);
|
|
121
|
+
assert.ok(failedDispatchClaim.claimId);
|
|
122
|
+
assert.equal(claimLifecycleHookBroadcast(stateDir, sessionId, eventKey, 'fingerprint', startMs + 1).acquired, false);
|
|
123
|
+
releaseLifecycleHookBroadcast(stateDir, sessionId, eventKey, 'fingerprint', failedDispatchClaim.claimId, startMs + 2);
|
|
124
|
+
const retryClaim = claimLifecycleHookBroadcast(stateDir, sessionId, eventKey, 'fingerprint', startMs + 3);
|
|
125
|
+
assert.equal(retryClaim.acquired, true);
|
|
126
|
+
assert.ok(retryClaim.claimId);
|
|
127
|
+
completeLifecycleHookBroadcast(stateDir, sessionId, eventKey, 'fingerprint', retryClaim.claimId, startMs + 4);
|
|
128
|
+
assert.equal(claimLifecycleHookBroadcast(stateDir, sessionId, eventKey, 'fingerprint', startMs + 5).acquired, false);
|
|
129
|
+
assert.equal(shouldSendLifecycleHookBroadcast(stateDir, sessionId, eventKey, 'fingerprint', startMs + 5_003), false);
|
|
130
|
+
assert.equal(shouldSendLifecycleHookBroadcast(stateDir, sessionId, eventKey, 'fingerprint', startMs + 5_004), true);
|
|
131
|
+
assert.equal(claimLifecycleHookBroadcast(stateDir, sessionId, eventKey, 'fingerprint', startMs + 5_004).acquired, true);
|
|
132
|
+
}
|
|
133
|
+
finally {
|
|
134
|
+
await rm(stateDir, { recursive: true, force: true });
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
it('allows a replay to reclaim a child-hook receipt after an abandoned claim expires', async () => {
|
|
138
|
+
const stateDir = await mkdtemp(join(tmpdir(), 'omx-lifecycle-hook-claim-expiry-'));
|
|
139
|
+
const sessionId = 'claim-expiry-session';
|
|
140
|
+
const eventKey = 'subagent-stop:["child-a","turn-1"]';
|
|
141
|
+
const startMs = Date.parse('2026-04-12T00:00:00.000Z');
|
|
142
|
+
try {
|
|
143
|
+
const abandonedClaim = claimLifecycleHookBroadcast(stateDir, sessionId, eventKey, 'fingerprint', startMs);
|
|
144
|
+
assert.equal(abandonedClaim.acquired, true);
|
|
145
|
+
assert.equal(claimLifecycleHookBroadcast(stateDir, sessionId, eventKey, 'fingerprint', startMs + 119_999).acquired, false);
|
|
146
|
+
const recoveredClaim = claimLifecycleHookBroadcast(stateDir, sessionId, eventKey, 'fingerprint', startMs + 120_000);
|
|
147
|
+
assert.equal(recoveredClaim.acquired, true);
|
|
148
|
+
assert.ok(recoveredClaim.claimId);
|
|
149
|
+
releaseLifecycleHookBroadcast(stateDir, sessionId, eventKey, 'fingerprint', recoveredClaim.claimId, startMs + 120_001);
|
|
150
|
+
}
|
|
151
|
+
finally {
|
|
152
|
+
await rm(stateDir, { recursive: true, force: true });
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
it('fails closed on non-record lifecycle receipt state', async () => {
|
|
156
|
+
const stateDir = await mkdtemp(join(tmpdir(), 'omx-lifecycle-invalid-hook-state-'));
|
|
157
|
+
const sessionId = 'invalid-hook-state-session';
|
|
158
|
+
const statePath = join(stateDir, 'sessions', sessionId, 'lifecycle-notif-state.json');
|
|
159
|
+
try {
|
|
160
|
+
await mkdir(join(stateDir, 'sessions', sessionId), { recursive: true });
|
|
161
|
+
for (const contents of ['[]', JSON.stringify({ hookEvents: [] })]) {
|
|
162
|
+
await writeFile(statePath, contents);
|
|
163
|
+
assert.deepEqual(claimLifecycleHookBroadcast(stateDir, sessionId, 'subagent-stop:["child-a","turn-1"]', 'fingerprint'), { acquired: false, reason: 'unavailable' });
|
|
164
|
+
assert.equal(await readFile(statePath, 'utf8'), contents);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
finally {
|
|
168
|
+
await rm(stateDir, { recursive: true, force: true });
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
it('renews an active child-hook claim and rejects renewal by a stale owner', async () => {
|
|
172
|
+
const stateDir = await mkdtemp(join(tmpdir(), 'omx-lifecycle-hook-claim-renewal-'));
|
|
173
|
+
const sessionId = 'claim-renewal-session';
|
|
174
|
+
const eventKey = 'subagent-stop:["child-a","turn-1"]';
|
|
175
|
+
const startMs = Date.parse('2026-04-12T00:00:00.000Z');
|
|
176
|
+
try {
|
|
177
|
+
const claim = claimLifecycleHookBroadcast(stateDir, sessionId, eventKey, 'fingerprint', startMs);
|
|
178
|
+
assert.equal(claim.acquired, true);
|
|
179
|
+
assert.ok(claim.claimId);
|
|
180
|
+
assert.equal(renewLifecycleHookBroadcastClaim(stateDir, sessionId, eventKey, 'fingerprint', 'stale-claim', startMs + 60_000), false);
|
|
181
|
+
assert.equal(renewLifecycleHookBroadcastClaim(stateDir, sessionId, eventKey, 'fingerprint', claim.claimId, startMs + 60_000), true);
|
|
182
|
+
assert.deepEqual(claimLifecycleHookBroadcast(stateDir, sessionId, eventKey, 'fingerprint', startMs + 120_000), { acquired: false, reason: 'duplicate' });
|
|
183
|
+
const expiredClaim = claimLifecycleHookBroadcast(stateDir, sessionId, eventKey, 'fingerprint', startMs + 180_000);
|
|
184
|
+
assert.equal(expiredClaim.acquired, true);
|
|
185
|
+
assert.ok(expiredClaim.claimId);
|
|
186
|
+
releaseLifecycleHookBroadcast(stateDir, sessionId, eventKey, 'fingerprint', expiredClaim.claimId, startMs + 180_001);
|
|
187
|
+
}
|
|
188
|
+
finally {
|
|
189
|
+
await rm(stateDir, { recursive: true, force: true });
|
|
190
|
+
}
|
|
191
|
+
});
|
|
192
|
+
it('prunes expired child-turn keys while retaining live keys and other lifecycle state', async () => {
|
|
193
|
+
const stateDir = await mkdtemp(join(tmpdir(), 'omx-lifecycle-child-expiry-'));
|
|
194
|
+
try {
|
|
195
|
+
const payload = buildPayload();
|
|
196
|
+
const startMs = Date.parse('2026-04-12T00:00:00.000Z');
|
|
197
|
+
const oldKey = 'subagent-stop:["child-a","turn-1"]';
|
|
198
|
+
const liveKey = 'subagent-stop:["child-b","turn-1"]';
|
|
199
|
+
const nextKey = 'subagent-stop:["child-a","turn-2"]';
|
|
200
|
+
recordLifecycleNotificationSent(stateDir, payload, startMs);
|
|
201
|
+
recordLifecycleHookBroadcastSent(stateDir, payload.sessionId, 'stop', 'leader', startMs);
|
|
202
|
+
recordLifecycleHookBroadcastSent(stateDir, payload.sessionId, oldKey, 'old', startMs);
|
|
203
|
+
recordLifecycleHookBroadcastSent(stateDir, payload.sessionId, liveKey, 'live', startMs + 1_000);
|
|
204
|
+
recordLifecycleHookBroadcastSent(stateDir, payload.sessionId, nextKey, 'next', startMs + 5_000);
|
|
205
|
+
const state = JSON.parse(await readFile(join(stateDir, 'sessions', payload.sessionId, 'lifecycle-notif-state.json'), 'utf8'));
|
|
206
|
+
assert.equal(state.hookEvents[oldKey], undefined);
|
|
207
|
+
assert.equal(state.hookEvents[liveKey].fingerprint, 'live');
|
|
208
|
+
assert.equal(state.hookEvents[nextKey].fingerprint, 'next');
|
|
209
|
+
assert.equal(state.hookEvents.stop.fingerprint, 'leader');
|
|
210
|
+
assert.ok(state.events['session-end']);
|
|
211
|
+
assert.equal(shouldSendLifecycleHookBroadcast(stateDir, payload.sessionId, liveKey, 'live', startMs + 5_999), false);
|
|
212
|
+
assert.equal(shouldSendLifecycleHookBroadcast(stateDir, payload.sessionId, liveKey, 'live', startMs + 6_000), true);
|
|
213
|
+
}
|
|
214
|
+
finally {
|
|
215
|
+
await rm(stateDir, { recursive: true, force: true });
|
|
216
|
+
}
|
|
217
|
+
});
|
|
20
218
|
it('suppresses duplicate session lifecycle notifications for the same session transition', async () => {
|
|
21
219
|
const stateDir = await mkdtemp(join(tmpdir(), 'omx-lifecycle-dedupe-'));
|
|
22
220
|
try {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lifecycle-dedupe.test.js","sourceRoot":"","sources":["../../../src/notifications/__tests__/lifecycle-dedupe.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,MAAM,MAAM,oBAAoB,CAAC;AACxC,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,kBAAkB,CAAC;AACzD,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EACL,+BAA+B,EAC/B,+BAA+B,EAC/B,gCAAgC,EAChC,gCAAgC,GACjC,MAAM,wBAAwB,CAAC;AAGhC,SAAS,YAAY,CAAC,SAAS,GAAqC,EAAE;IACpE,OAAO;QACL,KAAK,EAAE,aAAa;QACpB,SAAS,EAAE,cAAc;QACzB,OAAO,EAAE,MAAM;QACf,SAAS,EAAE,0BAA0B;QACrC,WAAW,EAAE,cAAc;QAC3B,WAAW,EAAE,SAAS;QACtB,MAAM,EAAE,cAAc;QACtB,GAAG,SAAS;KACb,CAAC;AACJ,CAAC;AAED,QAAQ,CAAC,+BAA+B,EAAE,GAAG,EAAE;IAC7C,EAAE,CAAC,sFAAsF,EAAE,KAAK,IAAI,EAAE;QACpG,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,uBAAuB,CAAC,CAAC,CAAC;QACxE,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,YAAY,EAAE,CAAC;YAC/B,MAAM,CAAC,KAAK,CAAC,+BAA+B,CAAC,QAAQ,EAAE,OAAO,CAAC,EAAE,IAAI,CAAC,CAAC;YACvE,+BAA+B,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;YACnD,MAAM,CAAC,KAAK,CAAC,+BAA+B,CAAC,QAAQ,EAAE,OAAO,CAAC,EAAE,KAAK,CAAC,CAAC;QAC1E,CAAC;gBAAS,CAAC;YACT,MAAM,EAAE,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QACvD,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iDAAiD,EAAE,KAAK,IAAI,EAAE;QAC/D,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,8BAA8B,CAAC,CAAC,CAAC;QAC/E,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,YAAY,EAAE,CAAC;YAC/B,+BAA+B,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;YACnD,MAAM,CAAC,KAAK,CAAC,+BAA+B,CAAC,QAAQ,EAAE,YAAY,CAAC,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC;QACvG,CAAC;gBAAS,CAAC;YACT,MAAM,EAAE,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QACvD,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,oCAAoC,EAAE,KAAK,IAAI,EAAE;QAClD,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,+BAA+B,CAAC,CAAC,CAAC;QAChF,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,YAAY,CAAC,EAAE,SAAS,EAAE,WAAW,EAAE,KAAK,EAAE,eAAe,EAAE,CAAC,CAAC;YACjF,+BAA+B,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;YAEnD,MAAM,WAAW,GAAG,IAAI,CAAC,QAAQ,EAAE,UAAU,EAAE,WAAW,EAAE,4BAA4B,CAAC,CAAC;YAC1F,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC,CAE5D,CAAC;YAEF,MAAM,CAAC,KAAK,CAAC,OAAO,KAAK,CAAC,MAAM,EAAE,CAAC,eAAe,CAAC,EAAE,WAAW,EAAE,QAAQ,CAAC,CAAC;YAC5E,MAAM,CAAC,KAAK,CAAC,+BAA+B,CAAC,QAAQ,EAAE,YAAY,CAAC,EAAE,SAAS,EAAE,WAAW,EAAE,KAAK,EAAE,eAAe,EAAE,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC;QAClI,CAAC;gBAAS,CAAC;YACT,MAAM,EAAE,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QACvD,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,yEAAyE,EAAE,KAAK,IAAI,EAAE;QACvF,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,8BAA8B,CAAC,CAAC,CAAC;QAC/E,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,YAAY,CAAC,EAAE,KAAK,EAAE,eAAe,EAAE,CAAC,CAAC;YACzD,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,0BAA0B,CAAC,CAAC;YAEvD,+BAA+B,CAAC,QAAQ,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;YAE5D,MAAM,CAAC,KAAK,CAAC,+BAA+B,CAAC,QAAQ,EAAE,OAAO,EAAE,OAAO,GAAG,KAAK,CAAC,EAAE,KAAK,CAAC,CAAC;YACzF,MAAM,CAAC,KAAK,CAAC,+BAA+B,CAAC,QAAQ,EAAE,OAAO,EAAE,OAAO,GAAG,KAAK,CAAC,EAAE,IAAI,CAAC,CAAC;QAC1F,CAAC;gBAAS,CAAC;YACT,MAAM,EAAE,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QACvD,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,8DAA8D,EAAE,KAAK,IAAI,EAAE;QAC5E,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,4BAA4B,CAAC,CAAC,CAAC;QAC7E,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,YAAY,CAAC,EAAE,KAAK,EAAE,eAAe,EAAE,SAAS,EAAE,cAAc,EAAE,CAAC,CAAC;YACpF,MAAM,eAAe,GAAG,4BAA4B,CAAC;YAErD,+BAA+B,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;YACnD,MAAM,CAAC,KAAK,CACV,gCAAgC,CAAC,QAAQ,EAAE,OAAO,CAAC,SAAS,EAAE,kBAAkB,EAAE,eAAe,CAAC,EAClG,IAAI,CACL,CAAC;YAEF,gCAAgC,CAAC,QAAQ,EAAE,OAAO,CAAC,SAAS,EAAE,kBAAkB,EAAE,eAAe,CAAC,CAAC;YACnG,MAAM,CAAC,KAAK,CACV,gCAAgC,CAAC,QAAQ,EAAE,OAAO,CAAC,SAAS,EAAE,kBAAkB,EAAE,eAAe,CAAC,EAClG,KAAK,CACN,CAAC;YACF,MAAM,CAAC,KAAK,CAAC,+BAA+B,CAAC,QAAQ,EAAE,OAAO,CAAC,EAAE,KAAK,CAAC,CAAC;QAC1E,CAAC;gBAAS,CAAC;YACT,MAAM,EAAE,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QACvD,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
1
|
+
{"version":3,"file":"lifecycle-dedupe.test.js","sourceRoot":"","sources":["../../../src/notifications/__tests__/lifecycle-dedupe.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -4,5 +4,15 @@ export declare function createLifecycleBroadcastFingerprint(value: unknown): str
|
|
|
4
4
|
export declare function shouldSendLifecycleNotification(stateDir: string, payload: FullNotificationPayload, nowMs?: number): boolean;
|
|
5
5
|
export declare function recordLifecycleNotificationSent(stateDir: string, payload: FullNotificationPayload, nowMs?: number): void;
|
|
6
6
|
export declare function shouldSendLifecycleHookBroadcast(stateDir: string, sessionId: string | undefined, eventKey: string, fingerprint: string, nowMs?: number): boolean;
|
|
7
|
+
export declare function claimLifecycleHookBroadcast(stateDir: string, sessionId: string | undefined, eventKey: string, fingerprint: string, nowMs?: number): {
|
|
8
|
+
acquired: true;
|
|
9
|
+
claimId?: string;
|
|
10
|
+
} | {
|
|
11
|
+
acquired: false;
|
|
12
|
+
reason: 'duplicate' | 'unavailable';
|
|
13
|
+
};
|
|
14
|
+
export declare function renewLifecycleHookBroadcastClaim(stateDir: string, sessionId: string | undefined, eventKey: string, fingerprint: string, claimId: string, nowMs?: number): boolean;
|
|
15
|
+
export declare function completeLifecycleHookBroadcast(stateDir: string, sessionId: string | undefined, eventKey: string, fingerprint: string, claimId: string, nowMs?: number): void;
|
|
16
|
+
export declare function releaseLifecycleHookBroadcast(stateDir: string, sessionId: string | undefined, eventKey: string, fingerprint: string, claimId: string, nowMs?: number): void;
|
|
7
17
|
export declare function recordLifecycleHookBroadcastSent(stateDir: string, sessionId: string | undefined, eventKey: string, fingerprint: string, nowMs?: number): void;
|
|
8
18
|
//# sourceMappingURL=lifecycle-dedupe.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lifecycle-dedupe.d.ts","sourceRoot":"","sources":["../../src/notifications/lifecycle-dedupe.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"lifecycle-dedupe.d.ts","sourceRoot":"","sources":["../../src/notifications/lifecycle-dedupe.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,iBAAiB,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAC;AAiF7E,wBAAgB,iCAAiC,CAAC,KAAK,EAAE,iBAAiB,GAAG,OAAO,CAEnF;AA6FD,wBAAgB,mCAAmC,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAE1E;AAED,wBAAgB,+BAA+B,CAC7C,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,uBAAuB,EAChC,KAAK,GAAE,MAAmB,GACzB,OAAO,CAUT;AAED,wBAAgB,+BAA+B,CAC7C,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,uBAAuB,EAChC,KAAK,GAAE,MAAmB,GACzB,IAAI,CAUN;AAED,wBAAgB,gCAAgC,CAC9C,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,GAAG,SAAS,EAC7B,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,MAAM,EACnB,KAAK,GAAE,MAAmB,GACzB,OAAO,CAST;AAED,wBAAgB,2BAA2B,CACzC,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,GAAG,SAAS,EAC7B,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,MAAM,EACnB,KAAK,GAAE,MAAmB,GACzB;IAAE,QAAQ,EAAE,IAAI,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,QAAQ,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,WAAW,GAAG,aAAa,CAAA;CAAE,CAmCjG;AAED,wBAAgB,gCAAgC,CAC9C,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,GAAG,SAAS,EAC7B,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,MAAM,EACf,KAAK,GAAE,MAAmB,GACzB,OAAO,CAsBT;AAED,wBAAgB,8BAA8B,CAC5C,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,GAAG,SAAS,EAC7B,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,MAAM,EACf,KAAK,GAAE,MAAmB,GACzB,IAAI,CAsBN;AAED,wBAAgB,6BAA6B,CAC3C,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,GAAG,SAAS,EAC7B,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,MAAM,EACf,KAAK,GAAE,MAAmB,GACzB,IAAI,CAoBN;AAED,wBAAgB,gCAAgC,CAC9C,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,GAAG,SAAS,EAC7B,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,MAAM,EACnB,KAAK,GAAE,MAAmB,GACzB,IAAI,CASN"}
|
|
@@ -1,8 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { existsSync, mkdirSync, readFileSync } from 'node:fs';
|
|
2
3
|
import { dirname, join } from 'node:path';
|
|
4
|
+
import { withCrossProcessFileLockSync } from '../subagents/tracker.js';
|
|
3
5
|
const SESSION_ID_SAFE_PATTERN = /^[a-zA-Z0-9][a-zA-Z0-9_-]{0,255}$/;
|
|
4
6
|
const LIFECYCLE_DEDUPE_FILE = 'lifecycle-notif-state.json';
|
|
5
7
|
const LIFECYCLE_DEDUPE_WINDOW_MS = 5_000;
|
|
8
|
+
const LIFECYCLE_HOOK_CLAIM_WINDOW_MS = 120_000;
|
|
6
9
|
const DEDUPED_EVENTS = new Set(['session-start', 'session-stop', 'session-end']);
|
|
7
10
|
function normalizeFingerprint(payload) {
|
|
8
11
|
return JSON.stringify({
|
|
@@ -19,24 +22,40 @@ function getStatePath(stateDir, sessionId) {
|
|
|
19
22
|
}
|
|
20
23
|
return join(stateDir, LIFECYCLE_DEDUPE_FILE);
|
|
21
24
|
}
|
|
22
|
-
function
|
|
25
|
+
function isRecord(value) {
|
|
26
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
27
|
+
}
|
|
28
|
+
function readStateFromDisk(path) {
|
|
23
29
|
try {
|
|
24
30
|
if (!existsSync(path))
|
|
25
31
|
return {};
|
|
26
32
|
const parsed = JSON.parse(readFileSync(path, 'utf-8'));
|
|
27
|
-
|
|
33
|
+
if (!isRecord(parsed)
|
|
34
|
+
|| (parsed.events !== undefined && !isRecord(parsed.events))
|
|
35
|
+
|| (parsed.hookEvents !== undefined && !isRecord(parsed.hookEvents)))
|
|
36
|
+
return null;
|
|
37
|
+
return parsed;
|
|
28
38
|
}
|
|
29
39
|
catch {
|
|
30
|
-
return
|
|
40
|
+
return null;
|
|
31
41
|
}
|
|
32
42
|
}
|
|
33
|
-
function
|
|
43
|
+
function readState(path) {
|
|
44
|
+
return readStateFromDisk(path) ?? {};
|
|
45
|
+
}
|
|
46
|
+
function readBucketState(state, bucket) {
|
|
47
|
+
const bucketState = state[bucket];
|
|
48
|
+
return isRecord(bucketState) ? bucketState : {};
|
|
49
|
+
}
|
|
50
|
+
function writeState(path, state, publish) {
|
|
34
51
|
try {
|
|
35
52
|
mkdirSync(dirname(path), { recursive: true });
|
|
36
|
-
|
|
53
|
+
publish(JSON.stringify(state, null, 2));
|
|
54
|
+
return true;
|
|
37
55
|
}
|
|
38
56
|
catch {
|
|
39
57
|
// best effort
|
|
58
|
+
return false;
|
|
40
59
|
}
|
|
41
60
|
}
|
|
42
61
|
export function shouldDedupeLifecycleNotification(event) {
|
|
@@ -65,30 +84,52 @@ function shouldSendFingerprint(previous, fingerprint, nowMs) {
|
|
|
65
84
|
return false;
|
|
66
85
|
return nowMs - previousMs >= LIFECYCLE_DEDUPE_WINDOW_MS;
|
|
67
86
|
}
|
|
87
|
+
function pruneExpiredChildTurnEntries(hookEvents, nowMs) {
|
|
88
|
+
for (const [key, entry] of Object.entries(hookEvents)) {
|
|
89
|
+
if (!key.startsWith('subagent-stop:'))
|
|
90
|
+
continue;
|
|
91
|
+
if (entry?.claimId) {
|
|
92
|
+
const claimedAtMs = Date.parse(entry.claimedAt ?? '');
|
|
93
|
+
if (!Number.isFinite(claimedAtMs) || nowMs - claimedAtMs >= LIFECYCLE_HOOK_CLAIM_WINDOW_MS)
|
|
94
|
+
delete hookEvents[key];
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
const sentAtMs = Date.parse(entry?.sentAt ?? '');
|
|
98
|
+
if (!Number.isFinite(sentAtMs) || nowMs - sentAtMs >= LIFECYCLE_DEDUPE_WINDOW_MS)
|
|
99
|
+
delete hookEvents[key];
|
|
100
|
+
}
|
|
101
|
+
}
|
|
68
102
|
function shouldSendScopedLifecycleBroadcast(stateDir, sessionId, bucket, eventKey, fingerprint, nowMs = Date.now()) {
|
|
69
103
|
if (!sessionId || !stateDir)
|
|
70
104
|
return true;
|
|
71
105
|
const path = getStatePath(stateDir, sessionId);
|
|
72
106
|
const state = readState(path);
|
|
73
|
-
const bucketState = state
|
|
74
|
-
? state[bucket]
|
|
75
|
-
: {};
|
|
107
|
+
const bucketState = readBucketState(state, bucket);
|
|
76
108
|
return shouldSendFingerprint(bucketState?.[eventKey], fingerprint, nowMs);
|
|
77
109
|
}
|
|
78
110
|
function recordScopedLifecycleBroadcastSent(stateDir, sessionId, bucket, eventKey, fingerprint, nowMs = Date.now()) {
|
|
79
111
|
if (!sessionId || !stateDir)
|
|
80
112
|
return;
|
|
81
113
|
const path = getStatePath(stateDir, sessionId);
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
114
|
+
try {
|
|
115
|
+
withCrossProcessFileLockSync(path, (lock) => {
|
|
116
|
+
const state = readState(path);
|
|
117
|
+
const bucketState = readBucketState(state, bucket);
|
|
118
|
+
if (bucket === 'hookEvents') {
|
|
119
|
+
pruneExpiredChildTurnEntries(bucketState, nowMs);
|
|
120
|
+
}
|
|
121
|
+
bucketState[eventKey] = {
|
|
122
|
+
fingerprint,
|
|
123
|
+
sentAt: new Date(nowMs).toISOString(),
|
|
124
|
+
};
|
|
125
|
+
state[bucket] = bucketState;
|
|
126
|
+
lock.assertOwnership();
|
|
127
|
+
writeState(path, state, lock.publish);
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
catch {
|
|
131
|
+
// Lifecycle receipts are best effort; lock failure must not break hook dispatch.
|
|
132
|
+
}
|
|
92
133
|
}
|
|
93
134
|
export function createLifecycleBroadcastFingerprint(value) {
|
|
94
135
|
return stableSerialize(value);
|
|
@@ -106,6 +147,121 @@ export function recordLifecycleNotificationSent(stateDir, payload, nowMs = Date.
|
|
|
106
147
|
export function shouldSendLifecycleHookBroadcast(stateDir, sessionId, eventKey, fingerprint, nowMs = Date.now()) {
|
|
107
148
|
return shouldSendScopedLifecycleBroadcast(stateDir, sessionId, 'hookEvents', eventKey, fingerprint, nowMs);
|
|
108
149
|
}
|
|
150
|
+
export function claimLifecycleHookBroadcast(stateDir, sessionId, eventKey, fingerprint, nowMs = Date.now()) {
|
|
151
|
+
if (!sessionId || !stateDir)
|
|
152
|
+
return { acquired: true };
|
|
153
|
+
const path = getStatePath(stateDir, sessionId);
|
|
154
|
+
const claimId = randomUUID();
|
|
155
|
+
try {
|
|
156
|
+
return withCrossProcessFileLockSync(path, (lock) => {
|
|
157
|
+
const state = readStateFromDisk(path);
|
|
158
|
+
if (!state)
|
|
159
|
+
return { acquired: false, reason: 'unavailable' };
|
|
160
|
+
const hookEvents = readBucketState(state, 'hookEvents');
|
|
161
|
+
pruneExpiredChildTurnEntries(hookEvents, nowMs);
|
|
162
|
+
const previous = hookEvents[eventKey];
|
|
163
|
+
if (previous?.claimId) {
|
|
164
|
+
const claimedAtMs = Date.parse(previous.claimedAt ?? '');
|
|
165
|
+
if (Number.isFinite(claimedAtMs) && nowMs - claimedAtMs < LIFECYCLE_HOOK_CLAIM_WINDOW_MS) {
|
|
166
|
+
return { acquired: false, reason: 'duplicate' };
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
else if (!shouldSendFingerprint(previous, fingerprint, nowMs)) {
|
|
170
|
+
return { acquired: false, reason: 'duplicate' };
|
|
171
|
+
}
|
|
172
|
+
hookEvents[eventKey] = {
|
|
173
|
+
fingerprint,
|
|
174
|
+
claimId,
|
|
175
|
+
claimedAt: new Date(nowMs).toISOString(),
|
|
176
|
+
};
|
|
177
|
+
state.hookEvents = hookEvents;
|
|
178
|
+
lock.assertOwnership();
|
|
179
|
+
if (!writeState(path, state, lock.publish))
|
|
180
|
+
return { acquired: false, reason: 'unavailable' };
|
|
181
|
+
return { acquired: true, claimId };
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
catch {
|
|
185
|
+
// Do not run side-effecting hooks without an atomic child-completion claim.
|
|
186
|
+
return { acquired: false, reason: 'unavailable' };
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
export function renewLifecycleHookBroadcastClaim(stateDir, sessionId, eventKey, fingerprint, claimId, nowMs = Date.now()) {
|
|
190
|
+
if (!sessionId || !stateDir || !claimId)
|
|
191
|
+
return false;
|
|
192
|
+
const path = getStatePath(stateDir, sessionId);
|
|
193
|
+
try {
|
|
194
|
+
return withCrossProcessFileLockSync(path, (lock) => {
|
|
195
|
+
const state = readStateFromDisk(path);
|
|
196
|
+
if (!state)
|
|
197
|
+
return false;
|
|
198
|
+
const hookEvents = readBucketState(state, 'hookEvents');
|
|
199
|
+
const previous = hookEvents[eventKey];
|
|
200
|
+
if (previous?.claimId !== claimId || previous.fingerprint !== fingerprint)
|
|
201
|
+
return false;
|
|
202
|
+
hookEvents[eventKey] = {
|
|
203
|
+
...previous,
|
|
204
|
+
claimedAt: new Date(nowMs).toISOString(),
|
|
205
|
+
};
|
|
206
|
+
state.hookEvents = hookEvents;
|
|
207
|
+
lock.assertOwnership();
|
|
208
|
+
return writeState(path, state, lock.publish);
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
catch {
|
|
212
|
+
return false;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
export function completeLifecycleHookBroadcast(stateDir, sessionId, eventKey, fingerprint, claimId, nowMs = Date.now()) {
|
|
216
|
+
if (!sessionId || !stateDir || !claimId)
|
|
217
|
+
return;
|
|
218
|
+
const path = getStatePath(stateDir, sessionId);
|
|
219
|
+
try {
|
|
220
|
+
withCrossProcessFileLockSync(path, (lock) => {
|
|
221
|
+
const state = readState(path);
|
|
222
|
+
const hookEvents = readBucketState(state, 'hookEvents');
|
|
223
|
+
const previous = hookEvents[eventKey];
|
|
224
|
+
if (previous?.claimId !== claimId || previous.fingerprint !== fingerprint)
|
|
225
|
+
return;
|
|
226
|
+
hookEvents[eventKey] = {
|
|
227
|
+
fingerprint,
|
|
228
|
+
sentAt: new Date(nowMs).toISOString(),
|
|
229
|
+
};
|
|
230
|
+
state.hookEvents = hookEvents;
|
|
231
|
+
pruneExpiredChildTurnEntries(hookEvents, nowMs);
|
|
232
|
+
lock.assertOwnership();
|
|
233
|
+
writeState(path, state, lock.publish);
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
catch {
|
|
237
|
+
// Lifecycle receipts are best effort; lock failure must not break hook dispatch.
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
export function releaseLifecycleHookBroadcast(stateDir, sessionId, eventKey, fingerprint, claimId, nowMs = Date.now()) {
|
|
241
|
+
if (!sessionId || !stateDir || !claimId)
|
|
242
|
+
return;
|
|
243
|
+
const path = getStatePath(stateDir, sessionId);
|
|
244
|
+
try {
|
|
245
|
+
withCrossProcessFileLockSync(path, (lock) => {
|
|
246
|
+
const state = readState(path);
|
|
247
|
+
const hookEvents = readBucketState(state, 'hookEvents');
|
|
248
|
+
const previous = hookEvents[eventKey];
|
|
249
|
+
if (previous?.claimId !== claimId || previous.fingerprint !== fingerprint)
|
|
250
|
+
return;
|
|
251
|
+
delete hookEvents[eventKey];
|
|
252
|
+
if (Object.keys(hookEvents).length > 0)
|
|
253
|
+
state.hookEvents = hookEvents;
|
|
254
|
+
else
|
|
255
|
+
delete state.hookEvents;
|
|
256
|
+
pruneExpiredChildTurnEntries(hookEvents, nowMs);
|
|
257
|
+
lock.assertOwnership();
|
|
258
|
+
writeState(path, state, lock.publish);
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
catch {
|
|
262
|
+
// Lifecycle receipts are best effort; lock failure must not break hook dispatch.
|
|
263
|
+
}
|
|
264
|
+
}
|
|
109
265
|
export function recordLifecycleHookBroadcastSent(stateDir, sessionId, eventKey, fingerprint, nowMs = Date.now()) {
|
|
110
266
|
recordScopedLifecycleBroadcastSent(stateDir, sessionId, 'hookEvents', eventKey, fingerprint, nowMs);
|
|
111
267
|
}
|