@raidiant/notifai 11.0.6 → 11.2.0

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 (90) hide show
  1. package/CHANGELOG.md +49 -0
  2. package/README.md +6 -0
  3. package/dist/agent-update-notice.d.ts +7 -0
  4. package/dist/agent-update-notice.js +77 -0
  5. package/dist/agent-update-notice.js.map +1 -0
  6. package/dist/cli-release.d.ts +3 -3
  7. package/dist/cli-release.js +12 -16
  8. package/dist/cli-release.js.map +1 -1
  9. package/dist/codex-wake.d.ts +44 -63
  10. package/dist/codex-wake.js +71 -174
  11. package/dist/codex-wake.js.map +1 -1
  12. package/dist/commands-ask.js +3 -1
  13. package/dist/commands-ask.js.map +1 -1
  14. package/dist/commands-close.js +28 -14
  15. package/dist/commands-close.js.map +1 -1
  16. package/dist/commands-core.d.ts +1 -1
  17. package/dist/commands-doctor.js +36 -19
  18. package/dist/commands-doctor.js.map +1 -1
  19. package/dist/commands-guidance.d.ts +1 -1
  20. package/dist/commands-guidance.js +15 -2
  21. package/dist/commands-guidance.js.map +1 -1
  22. package/dist/commands-hook-install.js +146 -54
  23. package/dist/commands-hook-install.js.map +1 -1
  24. package/dist/commands-hook-run.js +21 -13
  25. package/dist/commands-hook-run.js.map +1 -1
  26. package/dist/commands-hook-shape.js +3 -2
  27. package/dist/commands-hook-shape.js.map +1 -1
  28. package/dist/commands-update-check.d.ts +6 -0
  29. package/dist/commands-update-check.js +86 -0
  30. package/dist/commands-update-check.js.map +1 -0
  31. package/dist/commands-update-skill.d.ts +5 -0
  32. package/dist/commands-update-skill.js +36 -0
  33. package/dist/commands-update-skill.js.map +1 -0
  34. package/dist/commands-update.js +29 -1
  35. package/dist/commands-update.js.map +1 -1
  36. package/dist/commands.d.ts +2 -0
  37. package/dist/commands.js +2 -0
  38. package/dist/commands.js.map +1 -1
  39. package/dist/credentials.js +7 -4
  40. package/dist/credentials.js.map +1 -1
  41. package/dist/harnesses.d.ts +1 -1
  42. package/dist/harnesses.js +2 -2
  43. package/dist/harnesses.js.map +1 -1
  44. package/dist/hook-adapter.d.ts +7 -1
  45. package/dist/hook-adapter.js +36 -3
  46. package/dist/hook-adapter.js.map +1 -1
  47. package/dist/hook-events.d.ts +69 -2
  48. package/dist/hook-events.js +75 -13
  49. package/dist/hook-events.js.map +1 -1
  50. package/dist/hook-lifecycle.js +44 -15
  51. package/dist/hook-lifecycle.js.map +1 -1
  52. package/dist/hook-question-retirement.d.ts +9 -0
  53. package/dist/hook-question-retirement.js +38 -3
  54. package/dist/hook-question-retirement.js.map +1 -1
  55. package/dist/hook-question-state.d.ts +14 -5
  56. package/dist/hook-question-state.js +55 -24
  57. package/dist/hook-question-state.js.map +1 -1
  58. package/dist/hook-types.d.ts +6 -0
  59. package/dist/install-hooks.d.ts +70 -12
  60. package/dist/install-hooks.js +352 -198
  61. package/dist/install-hooks.js.map +1 -1
  62. package/dist/native-skills.d.ts +2 -0
  63. package/dist/native-skills.js +3 -1
  64. package/dist/native-skills.js.map +1 -1
  65. package/dist/openclaw-plugin.d.ts +0 -1
  66. package/dist/openclaw-plugin.js +0 -7
  67. package/dist/openclaw-plugin.js.map +1 -1
  68. package/dist/opencode-plugin.d.ts +9 -0
  69. package/dist/opencode-plugin.js +19 -2
  70. package/dist/opencode-plugin.js.map +1 -1
  71. package/dist/program.d.ts +3 -1
  72. package/dist/program.js +21 -6
  73. package/dist/program.js.map +1 -1
  74. package/dist/readiness.d.ts +9 -1
  75. package/dist/readiness.js +11 -3
  76. package/dist/readiness.js.map +1 -1
  77. package/dist/session-activation.d.ts +2 -2
  78. package/dist/session-activation.js +8 -7
  79. package/dist/session-activation.js.map +1 -1
  80. package/dist/session-labels.js +6 -6
  81. package/dist/session-labels.js.map +1 -1
  82. package/dist/skill-source/manifest.json +8 -4
  83. package/dist/skill-source/notifai/SKILL.md +14 -16
  84. package/dist/skill-source/notifai/references/harness-setup.md +23 -14
  85. package/dist/skill-source/notifai/references/updates.md +74 -0
  86. package/dist/ui/theme.d.ts +1 -1
  87. package/dist/update-handoff.d.ts +29 -0
  88. package/dist/update-handoff.js +66 -0
  89. package/dist/update-handoff.js.map +1 -0
  90. package/package.json +2 -2
package/CHANGELOG.md CHANGED
@@ -8,6 +8,55 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
8
8
  This project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html)
9
9
  and [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/).
10
10
 
11
+ ## [11.2.0](https://github.com/Raidiant-io/notifai/compare/v11.1.1...v11.2.0) (2026-09-20)
12
+
13
+
14
+ ### Features
15
+
16
+ * **cli:** offer weekly updates with session-aware guidance ([#175](https://github.com/Raidiant-io/notifai/issues/175)) ([b4e56a2](https://github.com/Raidiant-io/notifai/commit/b4e56a2f45865720a3dc5478268fa726e880393c))
17
+
18
+
19
+ ### Dependencies
20
+
21
+ * The following workspace dependencies were updated
22
+ * dependencies
23
+ * @raidiant/notifai-protocol bumped from 7.0.2 to 7.0.3
24
+
25
+ ## [11.1.1](https://github.com/Raidiant-io/notifai/compare/v11.1.0...v11.1.1) (2026-09-20)
26
+
27
+
28
+ ### Bug Fixes
29
+
30
+ * **cli:** recover publication after registry processing delays ([#173](https://github.com/Raidiant-io/notifai/issues/173)) ([0ce6e0b](https://github.com/Raidiant-io/notifai/commit/0ce6e0be40e46be42441e0e22600937ef1b8be0a))
31
+
32
+
33
+ ### Dependencies
34
+
35
+ * The following workspace dependencies were updated
36
+ * dependencies
37
+ * @raidiant/notifai-protocol bumped from 7.0.1 to 7.0.2
38
+
39
+ ## [11.1.0](https://github.com/Raidiant-io/notifai/compare/v11.0.6...v11.1.0) (2026-09-20)
40
+
41
+
42
+ ### Features
43
+
44
+ * **cli:** deliver Codex answers through the thread's durable queue ([#169](https://github.com/Raidiant-io/notifai/issues/169)) ([4941c3f](https://github.com/Raidiant-io/notifai/commit/4941c3f60e7b82e679e3a90ca3ef187ec10004a0))
45
+
46
+
47
+ ### Bug Fixes
48
+
49
+ * **cli:** avoid slow PowerShell module loading for Windows credentials ([#172](https://github.com/Raidiant-io/notifai/issues/172)) ([cd35b10](https://github.com/Raidiant-io/notifai/commit/cd35b1040ddde59e7360beb2d5ba031e02b04c93))
50
+ * **cli:** make setup and queued-question retirement reliable ([#163](https://github.com/Raidiant-io/notifai/issues/163)) ([e4cee77](https://github.com/Raidiant-io/notifai/commit/e4cee77d2aaaf965544763a23cc0d3c4a636210c))
51
+ * **cli:** preserve harness configuration during setup and cleanup ([#164](https://github.com/Raidiant-io/notifai/issues/164)) ([f8f82e6](https://github.com/Raidiant-io/notifai/commit/f8f82e62241d5ab841130070070a55cf50ae98dd))
52
+
53
+
54
+ ### Dependencies
55
+
56
+ * The following workspace dependencies were updated
57
+ * dependencies
58
+ * @raidiant/notifai-protocol bumped from 7.0.0 to 7.0.1
59
+
11
60
  ## [11.0.6](https://github.com/Raidiant-io/notifai/compare/v11.0.5...v11.0.6) (2026-09-06)
12
61
 
13
62
 
package/README.md CHANGED
@@ -18,6 +18,12 @@ bin, `npx --yes @raidiant/notifai@<version>` is supported. Pin the version.
18
18
  `hooks install` then writes that same pinned npx invocation into the harness
19
19
  adapter. This is slower than a real install and is not the default path.
20
20
 
21
+ Run `notifai doctor` or `notifai update --check --json` to check for a newer
22
+ CLI and inspect release notes, guidance, and session requirements. Enabled
23
+ Projects give root agents a quiet update offer at most once every seven days
24
+ across the machine. A release does not install itself or restart an Agent
25
+ Session automatically. The package includes its changelog.
26
+
21
27
  Use `notifai update` for later global updates. It repairs the installation the
22
28
  shell actually selects and keeps the stable hook adapter on that same CLI. It
23
29
  fails instead of moving the command to a different PATH slot when it cannot
@@ -0,0 +1,7 @@
1
+ /** One optional notice per machine-local state directory, across roots and harnesses. */
2
+ export declare function agentUpdateNotice(options: {
3
+ env: NodeJS.ProcessEnv;
4
+ now: number;
5
+ updateCommand: string;
6
+ fetchImpl?: typeof fetch;
7
+ }): Promise<string | undefined>;
@@ -0,0 +1,77 @@
1
+ import { lstatSync, readFileSync } from 'node:fs';
2
+ import path from 'node:path';
3
+ import { atomicWriteFileSync } from './atomic-file.js';
4
+ import { stateDir } from './config.js';
5
+ import { withFileLock } from './file-lock.js';
6
+ import { latestPublishedCliVersion, newerPublishedCli, shouldConsultCliRegistry, thisCliVersion } from './cli-release.js';
7
+ import { CLI_UPDATE_AVAILABLE } from './cli-contract.js';
8
+ const CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000;
9
+ const NOTICE_INTERVAL_MS = 7 * CHECK_INTERVAL_MS;
10
+ function readState(file) {
11
+ try {
12
+ const stat = lstatSync(file);
13
+ if (!stat.isFile() || stat.size > 1024)
14
+ throw new Error('invalid update notice state');
15
+ const value = JSON.parse(readFileSync(file, 'utf8'));
16
+ if (typeof value !== 'object' || value === null)
17
+ throw new Error('invalid update notice state');
18
+ const state = value;
19
+ for (const timestamp of [state.checked_at, state.notified_at]) {
20
+ if (timestamp !== undefined && (!Number.isSafeInteger(timestamp) || timestamp < 0)) {
21
+ throw new Error('invalid update notice timestamp');
22
+ }
23
+ }
24
+ return state;
25
+ }
26
+ catch (error) {
27
+ if (error.code === 'ENOENT')
28
+ return {};
29
+ throw error;
30
+ }
31
+ }
32
+ function recent(timestamp, now, interval) {
33
+ // A clock rollback must not create another notice.
34
+ return timestamp !== undefined && now - timestamp < interval;
35
+ }
36
+ /** One optional notice per machine-local state directory, across roots and harnesses. */
37
+ export async function agentUpdateNotice(options) {
38
+ if (!shouldConsultCliRegistry({ env: options.env }))
39
+ return undefined;
40
+ const file = path.join(stateDir(options.env), 'cli-update-notice.json');
41
+ const save = (state) => atomicWriteFileSync(file, `${JSON.stringify(state)}\n`);
42
+ try {
43
+ // Claim the lookup before doing network I/O. Another process neither waits
44
+ // for the registry nor emits a duplicate. Only short disk work holds a lock.
45
+ const claimed = withFileLock(`${file}.lock`, () => {
46
+ const state = readState(file);
47
+ if (recent(state.notified_at, options.now, NOTICE_INTERVAL_MS) ||
48
+ recent(state.checked_at, options.now, CHECK_INTERVAL_MS))
49
+ return false;
50
+ save({ ...state, checked_at: options.now });
51
+ return true;
52
+ }, { waitMs: 50 });
53
+ if (!claimed)
54
+ return undefined;
55
+ const latest = await latestPublishedCliVersion(options.fetchImpl, { useCache: false });
56
+ if (newerPublishedCli(thisCliVersion(), latest) === null)
57
+ return undefined;
58
+ const notify = withFileLock(`${file}.lock`, () => {
59
+ const state = readState(file);
60
+ if (recent(state.notified_at, options.now, NOTICE_INTERVAL_MS))
61
+ return false;
62
+ save({ ...state, notified_at: options.now });
63
+ return true;
64
+ }, { waitMs: 50 });
65
+ if (!notify)
66
+ return undefined;
67
+ return `${CLI_UPDATE_AVAILABLE} Run \`notifai update --check --json\`, read the release notes, and briefly offer to perform the update at a natural pause. ` +
68
+ `When authorized, use \`${options.updateCommand}\` and follow the new CLI guidance and session handoff. ` +
69
+ 'Follow the User\'s instructions about updating; keep working if deferred. ' +
70
+ 'Do not send a Notification Request just for this notice. It appears at most once every seven days on this machine.';
71
+ }
72
+ catch {
73
+ // Update discovery must never interrupt lifecycle guidance or routing.
74
+ return undefined;
75
+ }
76
+ }
77
+ //# sourceMappingURL=agent-update-notice.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent-update-notice.js","sourceRoot":"","sources":["../src/agent-update-notice.ts"],"names":[],"mappings":"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"}
@@ -1,9 +1,9 @@
1
1
  export declare function shouldConsultCliRegistry(input: {
2
- interactive?: boolean;
3
- json?: boolean;
4
2
  env?: NodeJS.ProcessEnv;
5
3
  }): boolean;
6
- export declare function latestPublishedCliVersion(fetchImpl?: typeof fetch): Promise<string | null>;
4
+ export declare function latestPublishedCliVersion(fetchImpl?: typeof fetch, options?: {
5
+ useCache?: boolean;
6
+ }): Promise<string | null>;
7
7
  export declare function newerPublishedCli(local: string | null, latest: string | null): string | null;
8
8
  export declare function thisCliVersion(): string | null;
9
9
  export declare function resetLatestPublishedCliVersionForTest(): void;
@@ -1,28 +1,23 @@
1
1
  import { packageVersion } from './release.js';
2
2
  import { cliDistTagsUrl } from './cli-contract.js';
3
- import { compareVersions } from './version.js';
3
+ import { compareVersions, isSemVer } from './version.js';
4
4
  /**
5
5
  * The newest published CLI, read from the registry that publishes it.
6
6
  *
7
7
  * Which version is newest is a registry fact. Doctor consults it as a
8
- * best-effort, cached, short-timeout hint: never fatal, and never on a
9
- * non-interactive or CI invocation — those callers already get structured
10
- * support state from the server when they are signed in.
8
+ * best-effort, cached, short-timeout hint in human and structured diagnostics.
9
+ * Automatic lifecycle notices have their own persistent throttle.
11
10
  */
12
11
  const DIST_TAGS_URL = cliDistTagsUrl();
13
12
  const REQUEST_TIMEOUT_MS = 2_000;
14
13
  const CACHE_TTL_MS = 60 * 60 * 1000;
15
14
  let cached = null;
16
15
  export function shouldConsultCliRegistry(input) {
17
- if (input.json === true)
18
- return false;
19
- if (input.interactive !== true)
20
- return false;
21
16
  const ci = input.env?.['CI'];
22
17
  return ci !== '1' && ci !== 'true';
23
18
  }
24
- export async function latestPublishedCliVersion(fetchImpl = fetch) {
25
- if (cached !== null && Date.now() - cached.at < CACHE_TTL_MS)
19
+ export async function latestPublishedCliVersion(fetchImpl = fetch, options = {}) {
20
+ if (options.useCache !== false && cached !== null && Date.now() - cached.at < CACHE_TTL_MS)
26
21
  return cached.value;
27
22
  try {
28
23
  const response = await fetchImpl(DIST_TAGS_URL, {
@@ -30,19 +25,20 @@ export async function latestPublishedCliVersion(fetchImpl = fetch) {
30
25
  signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),
31
26
  });
32
27
  if (!response.ok)
33
- return cached?.value ?? null;
28
+ return null;
34
29
  const body = await response.json();
35
30
  if (typeof body !== 'object' || body === null)
36
- return cached?.value ?? null;
31
+ return null;
37
32
  const latest = body.latest;
38
- if (typeof latest !== 'string' || compareVersions(latest, '0.0.0') === 'unparseable') {
39
- return cached?.value ?? null;
33
+ if (typeof latest !== 'string' || !isSemVer(latest)) {
34
+ return null;
40
35
  }
41
- cached = { value: latest, at: Date.now() };
36
+ if (options.useCache !== false)
37
+ cached = { value: latest, at: Date.now() };
42
38
  return latest;
43
39
  }
44
40
  catch {
45
- return cached?.value ?? null;
41
+ return null;
46
42
  }
47
43
  }
48
44
  export function newerPublishedCli(local, latest) {
@@ -1 +1 @@
1
- {"version":3,"file":"cli-release.js","sourceRoot":"","sources":["../src/cli-release.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AAC7C,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAA;AAClD,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAA;AAE9C;;;;;;;GAOG;AACH,MAAM,aAAa,GAAG,cAAc,EAAE,CAAA;AACtC,MAAM,kBAAkB,GAAG,KAAK,CAAA;AAChC,MAAM,YAAY,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAA;AAEnC,IAAI,MAAM,GAAgD,IAAI,CAAA;AAE9D,MAAM,UAAU,wBAAwB,CAAC,KAIxC;IACC,IAAI,KAAK,CAAC,IAAI,KAAK,IAAI;QAAE,OAAO,KAAK,CAAA;IACrC,IAAI,KAAK,CAAC,WAAW,KAAK,IAAI;QAAE,OAAO,KAAK,CAAA;IAC5C,MAAM,EAAE,GAAG,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAA;IAC5B,OAAO,EAAE,KAAK,GAAG,IAAI,EAAE,KAAK,MAAM,CAAA;AACpC,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,yBAAyB,CAC7C,YAA0B,KAAK;IAE/B,IAAI,MAAM,KAAK,IAAI,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,MAAM,CAAC,EAAE,GAAG,YAAY;QAAE,OAAO,MAAM,CAAC,KAAK,CAAA;IACjF,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,MAAM,SAAS,CAAC,aAAa,EAAE;YAC9C,OAAO,EAAE,EAAE,MAAM,EAAE,kBAAkB,EAAE;YACvC,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,kBAAkB,CAAC;SAChD,CAAC,CAAA;QACF,IAAI,CAAC,QAAQ,CAAC,EAAE;YAAE,OAAO,MAAM,EAAE,KAAK,IAAI,IAAI,CAAA;QAC9C,MAAM,IAAI,GAAY,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAA;QAC3C,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,IAAI;YAAE,OAAO,MAAM,EAAE,KAAK,IAAI,IAAI,CAAA;QAC3E,MAAM,MAAM,GAAI,IAA6B,CAAC,MAAM,CAAA;QACpD,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,eAAe,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,aAAa,EAAE,CAAC;YACrF,OAAO,MAAM,EAAE,KAAK,IAAI,IAAI,CAAA;QAC9B,CAAC;QACD,MAAM,GAAG,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,CAAA;QAC1C,OAAO,MAAM,CAAA;IACf,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,MAAM,EAAE,KAAK,IAAI,IAAI,CAAA;IAC9B,CAAC;AACH,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,KAAoB,EAAE,MAAqB;IAC3E,IAAI,KAAK,KAAK,IAAI,IAAI,MAAM,KAAK,IAAI;QAAE,OAAO,IAAI,CAAA;IAClD,OAAO,eAAe,CAAC,MAAM,EAAE,KAAK,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAA;AACnE,CAAC;AAED,MAAM,UAAU,cAAc;IAC5B,OAAO,cAAc,EAAE,CAAA;AACzB,CAAC;AAED,MAAM,UAAU,qCAAqC;IACnD,MAAM,GAAG,IAAI,CAAA;AACf,CAAC"}
1
+ {"version":3,"file":"cli-release.js","sourceRoot":"","sources":["../src/cli-release.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AAC7C,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAA;AAClD,OAAO,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AAExD;;;;;;GAMG;AACH,MAAM,aAAa,GAAG,cAAc,EAAE,CAAA;AACtC,MAAM,kBAAkB,GAAG,KAAK,CAAA;AAChC,MAAM,YAAY,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAA;AAEnC,IAAI,MAAM,GAAgD,IAAI,CAAA;AAE9D,MAAM,UAAU,wBAAwB,CAAC,KAExC;IACC,MAAM,EAAE,GAAG,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAA;IAC5B,OAAO,EAAE,KAAK,GAAG,IAAI,EAAE,KAAK,MAAM,CAAA;AACpC,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,yBAAyB,CAC7C,YAA0B,KAAK,EAC/B,UAAkC,EAAE;IAEpC,IAAI,OAAO,CAAC,QAAQ,KAAK,KAAK,IAAI,MAAM,KAAK,IAAI,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,MAAM,CAAC,EAAE,GAAG,YAAY;QAAE,OAAO,MAAM,CAAC,KAAK,CAAA;IAC/G,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,MAAM,SAAS,CAAC,aAAa,EAAE;YAC9C,OAAO,EAAE,EAAE,MAAM,EAAE,kBAAkB,EAAE;YACvC,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,kBAAkB,CAAC;SAChD,CAAC,CAAA;QACF,IAAI,CAAC,QAAQ,CAAC,EAAE;YAAE,OAAO,IAAI,CAAA;QAC7B,MAAM,IAAI,GAAY,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAA;QAC3C,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,IAAI;YAAE,OAAO,IAAI,CAAA;QAC1D,MAAM,MAAM,GAAI,IAA6B,CAAC,MAAM,CAAA;QACpD,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;YACpD,OAAO,IAAI,CAAA;QACb,CAAC;QACD,IAAI,OAAO,CAAC,QAAQ,KAAK,KAAK;YAAE,MAAM,GAAG,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,CAAA;QAC1E,OAAO,MAAM,CAAA;IACf,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAA;IACb,CAAC;AACH,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,KAAoB,EAAE,MAAqB;IAC3E,IAAI,KAAK,KAAK,IAAI,IAAI,MAAM,KAAK,IAAI;QAAE,OAAO,IAAI,CAAA;IAClD,OAAO,eAAe,CAAC,MAAM,EAAE,KAAK,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAA;AACnE,CAAC;AAED,MAAM,UAAU,cAAc;IAC5B,OAAO,cAAc,EAAE,CAAA;AACzB,CAAC;AAED,MAAM,UAAU,qCAAqC;IACnD,MAAM,GAAG,IAAI,CAAA;AACf,CAAC"}
@@ -1,86 +1,67 @@
1
1
  import { type EscalationDeliveryRoute } from './hook-types.js';
2
- /** Codex's per-thread writer lock directory, relative to `$CODEX_HOME`. */
3
- export declare const CODEX_THREAD_LOCK_DIR = "thread-writer-locks";
4
- export type CodexWakeObservation =
5
- /** A live writer owns this thread. Resuming it would create a divergent ghost. */
6
- {
7
- state: 'live';
8
- }
9
- /** No process holds the thread's writer lock; a cold resume can own it. */
10
- | {
11
- state: 'stopped';
12
- } | {
13
- state: 'unknown';
14
- reason: string;
15
- };
16
- export interface CodexWakeAdapters {
17
- /** Non-blocking writer-lock probe. Never holds the lock beyond the probe. */
18
- probeThreadWriter(lockPath: string): CodexWakeObservation;
19
- /** Is the harness process that will read this hook's stdout still running? */
20
- sourceAlive(pid: number): boolean;
21
- resume(threadId: string, cwd: string, context: string): Promise<void>;
22
- }
23
2
  /**
24
- * The one cross-process oracle for "who owns this Codex thread".
3
+ * The per-thread durable inbox `codex queue` writes to, relative to `$CODEX_HOME`.
25
4
  *
26
- * Codex's thread store takes a non-blocking `flock(LOCK_EX)` on a zero-byte
27
- * file per thread and holds it for as long as that process owns the thread —
28
- * verified against both a hosted app-server thread sitting idle and an
29
- * in-process `codex exec` run (2026-08-12). Because the kernel holds it, a
30
- * crash, SIGKILL, or reboot releases it instantly, and the leftover file proves
31
- * nothing: only the lock does. Codex fails closed itself with `-32600` when a
32
- * second writer contends, but that is the backstop; this is the gate.
5
+ * Named for diagnostics only. Notifai never opens it: the schema is private to
6
+ * Codex, versioned by its own migration table, and the supported writer is the
7
+ * CLI. Reading it here would couple Notifai to an internal shape it does not own.
33
8
  */
34
- export declare function observeCodexThread(threadId: string, env: NodeJS.ProcessEnv, adapters: Pick<CodexWakeAdapters, 'probeThreadWriter'>): CodexWakeObservation;
35
- export declare function codexThreadLockPath(threadId: string, env: NodeJS.ProcessEnv): string;
36
- export declare function codexThreadLockDirectory(env: NodeJS.ProcessEnv): string;
37
- export type CodexResumeReadiness = {
9
+ export declare const CODEX_QUEUE_STORE_FILE = "queue_1.sqlite";
10
+ export interface CodexWakeAdapters {
11
+ /** Write one message into the thread's durable inbox via `codex queue`. */
12
+ queue(threadId: string, cwd: string, context: string): Promise<void>;
13
+ }
14
+ export type CodexQueueReadiness = {
38
15
  state: 'ready';
39
- lockDirectory: string;
16
+ threadId: string;
40
17
  } | {
41
18
  state: 'unavailable';
42
19
  reason: string;
43
20
  };
21
+ export declare function codexHome(env: NodeJS.ProcessEnv): string;
22
+ export declare function codexQueueStorePath(env: NodeJS.ProcessEnv): string;
44
23
  /**
45
- * Whether a Codex thread whose Stop hook has already returned could still be
46
- * cold-resumed, decided without taking any lock or spawning anything.
24
+ * Whether this Stop hook could queue an answer into its own thread.
47
25
  *
48
- * Two things have to hold: this platform must implement the non-blocking
49
- * `flock` the probe is built on, and `$CODEX_HOME` must actually have a
50
- * thread-writer-lock directory — its absence is not evidence that a thread is
51
- * unowned, so the gate fails closed and the answer waits for the next turn.
52
- * Neither affects the ordinary path, where the hook's own stdout continues the
53
- * held turn.
26
+ * The only precondition the route actually has is an exact thread id: queueing
27
+ * needs no writer lock, no daemon, no socket and no platform syscall, and it
28
+ * succeeds against a live, busy or stopped thread alike. The absence of the
29
+ * queue store is deliberately *not* a blocker — Codex creates it on first
30
+ * write — so this reports readiness, never a store-existence gate.
54
31
  */
55
- export declare function inspectCodexResume(env: NodeJS.ProcessEnv, options: {
56
- platform: NodeJS.Platform;
57
- directoryExists: (directory: string) => boolean;
58
- }): CodexResumeReadiness;
32
+ export declare function inspectCodexQueue(threadId: string | undefined, _env: NodeJS.ProcessEnv): CodexQueueReadiness;
59
33
  /**
60
- * Codex's answer-delivery route: the blocking Stop hook, then nothing else.
34
+ * Codex's answer-delivery route: the thread's own durable inbox.
35
+ *
36
+ * `codex queue --thread <id> --message <text>` is not an IPC call into a live
37
+ * process. It writes one FIFO item into a per-thread inbox in `$CODEX_HOME`,
38
+ * and whichever client owns that thread drains it: a live idle session within
39
+ * seconds, a busy one at its next turn boundary, a stopped one the next time it
40
+ * is opened. The answer arrives as an ordinary user turn, firing
41
+ * `UserPromptSubmit` with the queued text.
42
+ *
43
+ * That is why this route replaced the writer-lock probe rather than joining it.
44
+ * The probe existed so a cold resume could never ghost-write a thread another
45
+ * process owned; queueing cannot ghost-write, because the message is delivered
46
+ * *by* the owner and simply waits when there is none. There is no race to lose,
47
+ * and nothing here depends on a BSD `O_EXLOCK`.
61
48
  *
62
- * The default is the hook continuation itself. While the Codex process that
63
- * invoked this hook is alive it is reading this process's stdout, and
64
- * `{"decision":"block"}` there creates a real new user turn — no launch
65
- * ownership, no control plane, no probe required. That path must never depend
66
- * on the lock probe: printing to a dead pipe cannot ghost-write anything, so
67
- * an unreadable `$CODEX_HOME` is not a reason to withhold a continuation.
49
+ * Two limits are load-bearing and are honoured here:
68
50
  *
69
- * Only when that process is gone does the answer need another last meter, and
70
- * then exactly one is safe: a cold `codex exec resume` of a thread no live
71
- * writer owns. Anything else — a live writer, an unprobeable platform, a thread
72
- * id we cannot name — is journaled and replayed at the session's next Stop.
73
- * That accepted gap is the honest floor of the Codex adapter: a session that
74
- * went idle after its Stop returned, which Notifai did not launch, cannot be
75
- * woken in place.
51
+ * 1. **Exit 0 is not delivery.** `codex queue` reports the same success against
52
+ * an exited session as a live one, so this route never claims `delivered`.
53
+ * It reports `queued`, and Notifai's own `UserPromptSubmit` hook is what
54
+ * observes actual consumption.
55
+ * 2. **Never queue and cold-resume the same answer.** `codex exec resume <id>
56
+ * "<prompt>"` drains the pending queue *and* runs the prompt, delivering it
57
+ * twice. This module therefore has no resume path at all: the routes must be
58
+ * mutually exclusive per answer, and the simplest way to guarantee that is
59
+ * for only one of them to exist.
76
60
  */
77
61
  export declare function codexWakeRoute(options: {
78
62
  threadId: string;
79
63
  cwd: string;
80
- sourcePid: number;
81
64
  env?: NodeJS.ProcessEnv;
82
65
  adapters?: CodexWakeAdapters;
83
- /** False for a detached recovery owner whose stdout no harness will read. */
84
- continuationActive?: boolean;
85
66
  }): EscalationDeliveryRoute;
86
67
  export declare function systemCodexWakeAdapters(env?: NodeJS.ProcessEnv): CodexWakeAdapters;