@raidiant/notifai 1.0.1 → 2.0.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 (63) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/README.md +17 -7
  3. package/dist/client.d.ts +5 -1
  4. package/dist/client.js +2 -0
  5. package/dist/client.js.map +1 -1
  6. package/dist/commands.d.ts +10 -1
  7. package/dist/commands.js +151 -54
  8. package/dist/commands.js.map +1 -1
  9. package/dist/config.d.ts +2 -2
  10. package/dist/config.js +5 -9
  11. package/dist/config.js.map +1 -1
  12. package/dist/credentials.d.ts +54 -3
  13. package/dist/credentials.js +237 -18
  14. package/dist/credentials.js.map +1 -1
  15. package/dist/file-lock.js +15 -6
  16. package/dist/file-lock.js.map +1 -1
  17. package/dist/hook-adapter.d.ts +6 -2
  18. package/dist/hook-adapter.js +110 -29
  19. package/dist/hook-adapter.js.map +1 -1
  20. package/dist/hooks.d.ts +15 -1
  21. package/dist/hooks.js +183 -12
  22. package/dist/hooks.js.map +1 -1
  23. package/dist/install-hooks.d.ts +50 -21
  24. package/dist/install-hooks.js +141 -57
  25. package/dist/install-hooks.js.map +1 -1
  26. package/dist/logging.d.ts +1 -1
  27. package/dist/logging.js +4 -0
  28. package/dist/logging.js.map +1 -1
  29. package/dist/main.js +14 -3
  30. package/dist/main.js.map +1 -1
  31. package/dist/native-skills.js +5 -10
  32. package/dist/native-skills.js.map +1 -1
  33. package/dist/opencode-plugin.d.ts +7 -2
  34. package/dist/opencode-plugin.js +142 -4
  35. package/dist/opencode-plugin.js.map +1 -1
  36. package/dist/platform.d.ts +73 -0
  37. package/dist/platform.js +143 -0
  38. package/dist/platform.js.map +1 -0
  39. package/dist/ui/config-view.js +5 -1
  40. package/dist/ui/config-view.js.map +1 -1
  41. package/package.json +11 -10
  42. package/src/client.test.ts +53 -0
  43. package/src/client.ts +24 -0
  44. package/src/commands.test.ts +351 -30
  45. package/src/commands.ts +207 -49
  46. package/src/config.ts +11 -9
  47. package/src/credentials.test.ts +291 -0
  48. package/src/credentials.ts +274 -17
  49. package/src/file-lock.ts +34 -9
  50. package/src/hook-adapter.test.ts +136 -0
  51. package/src/hook-adapter.ts +120 -28
  52. package/src/hooks.test.ts +175 -1
  53. package/src/hooks.ts +241 -12
  54. package/src/install-hooks.test.ts +174 -33
  55. package/src/install-hooks.ts +186 -54
  56. package/src/logging.ts +4 -0
  57. package/src/main.ts +17 -2
  58. package/src/native-skills.ts +5 -10
  59. package/src/opencode-plugin.ts +148 -3
  60. package/src/platform.test.ts +152 -0
  61. package/src/platform.ts +173 -0
  62. package/src/skill.test.ts +32 -1
  63. package/src/ui/config-view.ts +5 -5
package/CHANGELOG.md CHANGED
@@ -8,6 +8,56 @@ 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
+ ## [2.0.0](https://github.com/Raidiant-io/notifai/compare/v1.1.0...v2.0.0) (2026-08-13)
12
+
13
+
14
+ ### ⚠ BREAKING CHANGES
15
+
16
+ * **cli:** hide the service URL, move personal prefs, and shorten setup closes
17
+
18
+ ### Features
19
+
20
+ * **cli:** pack a bounded consented slice of the local log ([6fb3d68](https://github.com/Raidiant-io/notifai/commit/6fb3d68cd0057ab901c031c6012318fe464949a9))
21
+ * **cli:** send a test notification by kind, not by inventing a sound ([b90c8ed](https://github.com/Raidiant-io/notifai/commit/b90c8edcbf2ba98db09414701a309cef88aa1cc4))
22
+ * **cli:** support Windows and Linux harness workflows ([779ab6b](https://github.com/Raidiant-io/notifai/commit/779ab6b075900a7a431b809ddcb715fd80836688))
23
+ * compose native skills setup into init ([de6318a](https://github.com/Raidiant-io/notifai/commit/de6318a71166ef75774a217176f1e020116e177b))
24
+ * **skill:** make agents set Notifai up and skip sound/level unless overriding ([9ffd87c](https://github.com/Raidiant-io/notifai/commit/9ffd87c0c79e539f8b92e94ac453407d58bca30f))
25
+
26
+
27
+ ### Bug Fixes
28
+
29
+ * **cli:** cover Windows terminal behavior ([cda7abd](https://github.com/Raidiant-io/notifai/commit/cda7abd134744b2cac44204dd3eb1b20b9038edc))
30
+ * **cli:** drop unfinished import changes from the skill rewrite ([2fd49b2](https://github.com/Raidiant-io/notifai/commit/2fd49b2ba4d4f218138da5dbc3455f1ac07adaf6))
31
+ * **cli:** hide the service URL, move personal prefs, and shorten setup closes ([5b9fe52](https://github.com/Raidiant-io/notifai/commit/5b9fe52d9dc5296e5c3b328a82682a36719d891b))
32
+ * **cli:** install hooks at one scope, not project and global ([f4cd237](https://github.com/Raidiant-io/notifai/commit/f4cd2376b57d9104fbcdcca83990ad9dfe09b296))
33
+ * **cli:** isolate Codex hooks and reply routing ([#1](https://github.com/Raidiant-io/notifai/issues/1)) ([d060b6a](https://github.com/Raidiant-io/notifai/commit/d060b6a18b5937d82f74619a6c1bc711243be26c))
34
+ * **cli:** keep Codex hook install on ~/.codex, ignore CODEX_HOME ([3f75f13](https://github.com/Raidiant-io/notifai/commit/3f75f131c24534d3813a336bb72422f78e24a972))
35
+ * **cli:** offer labeled hooks install and uninstall in the interactive app ([f93d0ae](https://github.com/Raidiant-io/notifai/commit/f93d0ae1c981694de6abb317f93f1647dd1d64fa))
36
+ * **cli:** preserve Codex hook trust on reinstall ([#3](https://github.com/Raidiant-io/notifai/issues/3)) ([5e2c2f4](https://github.com/Raidiant-io/notifai/commit/5e2c2f4d618276573f810a989f41a89ab1053564))
37
+ * **cli:** report observed setup receipts ([99d04bb](https://github.com/Raidiant-io/notifai/commit/99d04bbd487681db3023468fbd88b77a1ba13ceb))
38
+ * **cli:** skip readiness work that cannot change the next action ([7fc2295](https://github.com/Raidiant-io/notifai/commit/7fc2295e4a98b07f0116bad17ea93b8f98b265d1))
39
+ * **cli:** stabilize Linux portability checks ([1462693](https://github.com/Raidiant-io/notifai/commit/146269306ae3dfc91f3559cce802460d3e058925))
40
+ * **cli:** write Codex hooks in the layer's existing representation ([7589862](https://github.com/Raidiant-io/notifai/commit/758986296009c1d50bed66a79a5863ea977aa87e))
41
+ * **cli:** write Codex hooks to config.toml, not hooks.json ([3cc6bb8](https://github.com/Raidiant-io/notifai/commit/3cc6bb8bc9bb592ee4abce3f1421d7c0c6f56d32))
42
+ * make CLI package installable from npm ([67bd6be](https://github.com/Raidiant-io/notifai/commit/67bd6be77d648a12ff0d3036a5853b6a4391bebb))
43
+
44
+ ## [1.1.0](https://github.com/Raidiant-io/notifai/compare/v1.0.1...v1.1.0) (2026-08-13)
45
+
46
+
47
+ ### Features
48
+
49
+ * **cli:** support Windows and Linux harness workflows ([779ab6b](https://github.com/Raidiant-io/notifai/commit/779ab6b075900a7a431b809ddcb715fd80836688))
50
+
51
+
52
+ ### Bug Fixes
53
+
54
+ * **cli:** cover Windows terminal behavior ([cda7abd](https://github.com/Raidiant-io/notifai/commit/cda7abd134744b2cac44204dd3eb1b20b9038edc))
55
+ * **cli:** isolate Codex hooks and reply routing ([#1](https://github.com/Raidiant-io/notifai/issues/1)) ([d060b6a](https://github.com/Raidiant-io/notifai/commit/d060b6a18b5937d82f74619a6c1bc711243be26c))
56
+ * **cli:** keep Codex hook install on ~/.codex, ignore CODEX_HOME ([3f75f13](https://github.com/Raidiant-io/notifai/commit/3f75f131c24534d3813a336bb72422f78e24a972))
57
+ * **cli:** preserve Codex hook trust on reinstall ([#3](https://github.com/Raidiant-io/notifai/issues/3)) ([5e2c2f4](https://github.com/Raidiant-io/notifai/commit/5e2c2f4d618276573f810a989f41a89ab1053564))
58
+ * **cli:** report observed setup receipts ([99d04bb](https://github.com/Raidiant-io/notifai/commit/99d04bbd487681db3023468fbd88b77a1ba13ceb))
59
+ * **cli:** stabilize Linux portability checks ([1462693](https://github.com/Raidiant-io/notifai/commit/146269306ae3dfc91f3559cce802460d3e058925))
60
+
11
61
  ## [Unreleased]
12
62
 
13
63
  ## [1.0.1] - 2026-08-13
package/README.md CHANGED
@@ -56,6 +56,10 @@ For a harness that cannot resume an idle agent turn, `notifai send
56
56
 
57
57
  ## Agent harnesses
58
58
 
59
+ The CLI and managed hook adapters support macOS, Linux, and Windows. Receiving
60
+ still requires the current iPhone or macOS Companion App; native Windows and
61
+ Linux Companion Apps are not shipped yet.
62
+
59
63
  ```sh
60
64
  notifai hooks install
61
65
  ```
@@ -67,13 +71,19 @@ or add one it did not see. This is what lets an agent's question reach
67
71
  your phone without the agent having to cooperate — no question detection,
68
72
  no state in a context window that compaction will eat.
69
73
 
70
- Every harness definition calls one stable user-level adapter at
71
- `~/.notifai/bin/hook-adapter`. Node, package manager, CLI version, checkout,
72
- XDG directories, and notification preferences are resolved behind it, so
73
- upgrades and configuration changes do not rewrite trusted hook definitions.
74
- The first migration may require one Codex `/hooks` approval; later repairs keep
75
- the same identity. Codex's Stop definition omits its timeout so the host default
76
- remains authoritative; prompt-submit and session-end retain fixed short limits.
74
+ Every harness definition calls one stable user-level adapter under the account
75
+ home (`~/.notifai/bin/hook-adapter` on macOS/Linux and the corresponding user
76
+ profile path on Windows). Node, package manager, CLI version, checkout,
77
+ configuration directories, and notification preferences are resolved behind
78
+ it, so upgrades and configuration changes do not rewrite trusted hook
79
+ definitions. Windows invokes its JavaScript adapter through the registered Node
80
+ executable; macOS/Linux retain the POSIX adapter and its stable command bytes.
81
+ Claude Code Stop returns immediately into the live inbox route on macOS/Linux;
82
+ on Windows, where upstream exposes no inbox socket, Stop stays open and returns
83
+ the accepted answer through the harness continuation. The first migration may
84
+ require one Codex `/hooks` approval; later repairs keep the same identity.
85
+ Codex's Stop definition omits its timeout so the host default remains
86
+ authoritative; prompt-submit and session-end retain fixed short limits.
77
87
 
78
88
  `notifai doctor` compares Codex trust on a best-effort basis against Codex's
79
89
  current persisted representation. Notifai never writes that trust store;
package/dist/client.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { AccountAccessResponse, BeginPairingResponse, CapabilityDocument, CreateMediaUploadRequestT, CreateMediaUploadResponse, EvidenceSnapshot, ListDevicesResponse, ListRepliesResponse, Platform, PollPairingResponse, SubmissionReceipt, SubmitNotificationRequestT } from '@raidiant/notifai-protocol';
1
+ import type { AccountAccessResponse, BeginPairingResponse, CapabilityDocument, CreateMediaUploadRequestT, CreateMediaUploadResponse, EvidenceSnapshot, GetAgentAcknowledgementResponse, ListDevicesResponse, ListRepliesResponse, Platform, PollPairingResponse, PutAgentAcknowledgementRequestT, PutAgentAcknowledgementResponse, SubmissionReceipt, SubmitNotificationRequestT } from '@raidiant/notifai-protocol';
2
2
  import type { Logger } from './logging.js';
3
3
  export declare class ApiCallError extends Error {
4
4
  status: number;
@@ -34,6 +34,10 @@ export interface ApiClient {
34
34
  }): Promise<ListRepliesResponse>;
35
35
  /** Retire a question and return the replies committed before the close fence. */
36
36
  closeReplies(requestId: string): Promise<ListRepliesResponse>;
37
+ putAgentAcknowledgement(requestId: string, body: PutAgentAcknowledgementRequestT): Promise<PutAgentAcknowledgementResponse>;
38
+ agentAcknowledgement(requestId: string, options: {
39
+ waitSeconds: number;
40
+ }): Promise<GetAgentAcknowledgementResponse>;
37
41
  createMediaUpload(body: CreateMediaUploadRequestT): Promise<CreateMediaUploadResponse>;
38
42
  uploadMedia(grant: CreateMediaUploadResponse, bytes: Uint8Array): Promise<void>;
39
43
  health(): Promise<boolean>;
package/dist/client.js CHANGED
@@ -129,6 +129,8 @@ export function createClient(baseUrl, bearer, options = {}) {
129
129
  evidence: (requestId) => call('GET', `/api/v1/notifications/${requestId}`),
130
130
  replies: (requestId, { waitSeconds, afterSeq }) => call('GET', `/api/v1/notifications/${encodeURIComponent(requestId)}/replies?wait_seconds=${waitSeconds}&after_seq=${afterSeq}`, undefined, waitSeconds),
131
131
  closeReplies: (requestId) => call('POST', `/api/v1/notifications/${encodeURIComponent(requestId)}/replies/close`),
132
+ putAgentAcknowledgement: (requestId, body) => call('PUT', `/api/v1/notifications/${encodeURIComponent(requestId)}/agent-acknowledgement`, body),
133
+ agentAcknowledgement: (requestId, { waitSeconds }) => call('GET', `/api/v1/notifications/${encodeURIComponent(requestId)}/agent-acknowledgement?wait_seconds=${waitSeconds}`, undefined, waitSeconds),
132
134
  createMediaUpload: (body) => call('POST', '/api/v1/media', body),
133
135
  uploadMedia: async (grant, bytes) => {
134
136
  let response;
@@ -1 +1 @@
1
- {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
@@ -61,6 +61,8 @@ export interface CommandDeps {
61
61
  hookAdapterHome?: string;
62
62
  /** Test seam; production uses this process's Node and CLI paths. */
63
63
  hookInstallTarget?: HookAdapterTarget;
64
+ /** Test seam; production uses process.platform for adapter format and quoting. */
65
+ hookPlatform?: NodeJS.Platform;
64
66
  /** Test seam; production uses fetch against base_url. */
65
67
  clientFactory?: (baseUrl: string, bearer: string | null, options?: ClientOptions) => ApiClient;
66
68
  /** Test seam for bounded polling without wall-clock sleeps. */
@@ -228,8 +230,15 @@ export declare function buildQuestions(flags: AskFlags, question: string | undef
228
230
  * for `ask_grace_seconds` before it reaches any device.
229
231
  */
230
232
  export declare function askCommand(deps: CommandDeps, question: string | undefined, flags: AskFlags): number;
233
+ /** Record the one Agent Acknowledgement associated with a replied-to request. */
234
+ export declare function acknowledgeCommand(deps: CommandDeps, requestId: string, flags: {
235
+ text?: string;
236
+ json?: boolean;
237
+ }): Promise<number>;
231
238
  /** Retire a question so a late answer is rejected rather than silently lost. */
232
- export declare function closeCommand(deps: CommandDeps, requestId: string): Promise<number>;
239
+ export declare function closeCommand(deps: CommandDeps, requestId: string, flags?: {
240
+ json?: boolean;
241
+ }): Promise<number>;
233
242
  export interface HooksInstallFlags {
234
243
  harness?: string;
235
244
  global?: boolean;
package/dist/commands.js CHANGED
@@ -1,20 +1,19 @@
1
1
  import { createHash, randomBytes } from 'node:crypto';
2
- import { spawn } from 'node:child_process';
3
2
  import { existsSync, lstatSync, mkdirSync, readFileSync, realpathSync, rmSync, unlinkSync, writeFileSync } from 'node:fs';
4
3
  import os from 'node:os';
5
4
  import path from 'node:path';
6
5
  import { parse as parseToml, stringify as stringifyToml } from 'smol-toml';
7
- import { CAPABILITIES_V1, QUESTION_TEXT_MAX_LENGTH, REPLY_MAX_QUESTIONS, REPLY_MAX_WINDOW_SECONDS, validateDraft, } from '@raidiant/notifai-protocol';
6
+ import { AGENT_ACKNOWLEDGEMENT_MAX_LENGTH, CAPABILITIES_V1, QUESTION_TEXT_MAX_LENGTH, REPLY_MAX_QUESTIONS, REPLY_MAX_WINDOW_SECONDS, validateDraft, } from '@raidiant/notifai-protocol';
8
7
  import { sha256Hex } from '@raidiant/notifai-protocol/node';
9
8
  import { ApiCallError, NetworkError, createClient, isRetryableReplyPollError, } from './client.js';
10
- import { BOOLEAN_CONFIG_KEYS, CONFIG_KEYS, LOG_LEVELS, NUMERIC_CONFIG_KEYS, configBounds, configDefaultValue, findProjectConfigPath, personalProjectConfigPath, globalConfigPath, loadConfig, sessionConfigPath, } from './config.js';
9
+ import { BOOLEAN_CONFIG_KEYS, CONFIG_KEYS, LOG_LEVELS, NUMERIC_CONFIG_KEYS, configBounds, configDefaultValue, findProjectConfigPath, personalProjectConfigPath, globalConfigPath, loadConfig, sessionConfigPath, stateDir, } from './config.js';
11
10
  import { acceptedValues, configInfo } from './config-schema.js';
12
11
  import { packageVersion, skillsSource } from './release.js';
13
12
  import { atomicWriteFileSync } from './atomic-file.js';
14
13
  import { withTargetFileLock } from './file-lock.js';
15
14
  import { renderConfigExplain, renderConfigList, renderConfigPlain, } from './ui/config-view.js';
16
15
  import { firstBlocker, } from './readiness.js';
17
- import { WAITER_CEILING_SECONDS, handleSessionEnd, handleStop, handleUserPromptSubmit, parseHookInput, pruneAbandonedSessions, readLiveProjectSessionPointers, readMatchingProjectSessionPointer, readProjectSession, readProjectSessionPointer, readSessionState, registerQuestion, } from './hooks.js';
16
+ import { WAITER_CEILING_SECONDS, clearAcknowledgementObligation, handleSessionEnd, handleStop, handleUserPromptSubmit, parseHookInput, pruneAbandonedSessions, readLiveProjectSessionPointers, readMatchingProjectSessionPointer, readProjectSession, readProjectSessionPointer, readSessionState, registerQuestion, } from './hooks.js';
18
17
  import { LOG_EVENTS, activeLogPath, archiveLogPaths, logConfigResolved, logSettingsFrom, logsDiskUsage, nullLogger, readLogRecords, renderRecord, } from './logging.js';
19
18
  import { BLOCKING_STOP_TIMEOUT_SECONDS, CLAUDE_ASYNC_STOP_TIMEOUT_SECONDS, HARNESSES, applyPlan, buildCursorHookConfig, buildHookConfig, collapseUnusedCodexRepresentation, codexLayerDir, codexLayerPaths, codexRepresentationProblems, codexTrustProblems, codexProjectRoot, detectHarness, detectedHarnesses, findInstallations, handlerEvent, hookDefinitionFiles, inspectCodexLayer, loadCursorSettings, loadSettings, mergeCursorHooks, mergeHooks, removeCursorHooks, removeHooks, settingsFile, } from './install-hooks.js';
20
19
  import { HARNESS_CAPABILITIES, HARNESS_LABELS } from './harnesses.js';
@@ -23,6 +22,7 @@ import { codexWakeRoute, inspectCodexResume, } from './codex-wake.js';
23
22
  import { inspectHookAdapter, installHookAdapter, isNpxAdapterTarget, } from './hook-adapter.js';
24
23
  import { isOurOpencodePlugin, opencodePluginSource, } from './opencode-plugin.js';
25
24
  import { CHOICE_USAGE, buildDraft, formatReceipt, parseChoices, receiptExitCode, slugify, } from './send.js';
25
+ import { openUrl } from './platform.js';
26
26
  /** The invocation's logger, or one that discards everything. */
27
27
  export function log(deps) {
28
28
  return deps.logger ?? nullLogger();
@@ -422,6 +422,7 @@ export async function sendCommand(deps, flags) {
422
422
  title: flags.title,
423
423
  overall: receipt.overall,
424
424
  replayed: receipt.replayed,
425
+ agent_acknowledgement_required: receipt.agent_acknowledgement_required,
425
426
  deliveries: receipt.deliveries.length,
426
427
  wait_seconds: waitSeconds,
427
428
  exit: receiptExit,
@@ -452,6 +453,9 @@ export async function sendCommand(deps, flags) {
452
453
  type: 'reply_result',
453
454
  request_id: receipt.request_id,
454
455
  replies: [],
456
+ agent_acknowledgement_required: receipt.agent_acknowledgement_required,
457
+ agent_acknowledgement: null,
458
+ acknowledgement_command: null,
455
459
  degraded: false,
456
460
  })
457
461
  : JSON.stringify(receipt, null, 2));
@@ -467,17 +471,16 @@ export async function sendCommand(deps, flags) {
467
471
  });
468
472
  recordReplies(deps, receipt.request_id, result.response.replies);
469
473
  if (flags.json) {
470
- deps.io.out(JSON.stringify({
471
- type: 'reply_result',
472
- request_id: receipt.request_id,
473
- replies: result.response.replies,
474
- degraded: result.degraded,
475
- }));
474
+ deps.io.out(JSON.stringify(replyResultJson(result.response, result.degraded)));
476
475
  }
477
- else if (result.response.replies.length > 0)
476
+ else if (result.response.replies.length > 0) {
478
477
  printReplies(deps, result.response.replies);
479
- else
478
+ printAcknowledgementStatus(deps, result.response);
479
+ }
480
+ else {
480
481
  printNoReply(deps, receipt.request_id, result.response.reply_expires_at);
482
+ printAcknowledgementStatus(deps, result.response);
483
+ }
481
484
  if (result.degraded) {
482
485
  log(deps).error('cli.error', {
483
486
  kind: 'network',
@@ -601,15 +604,17 @@ export async function repliesCommand(deps, requestedId, flags) {
601
604
  });
602
605
  recordReplies(deps, requestId, result.response.replies);
603
606
  if (flags.json) {
604
- jsonBodies.push({ ...result.response, degraded: result.degraded });
607
+ jsonBodies.push(replyResultJson(result.response, result.degraded));
605
608
  }
606
609
  else if (result.response.replies.length > 0) {
607
610
  if (flags.pending === true)
608
611
  deps.io.out(`pending request ${requestId}`);
609
612
  printReplies(deps, result.response.replies);
613
+ printAcknowledgementStatus(deps, result.response);
610
614
  }
611
615
  else {
612
616
  printNoReply(deps, requestId, result.response.reply_expires_at);
617
+ printAcknowledgementStatus(deps, result.response);
613
618
  }
614
619
  anyReplies ||= result.response.replies.length > 0;
615
620
  if (result.degraded)
@@ -687,7 +692,13 @@ export async function waitForReply(client, requestId, options) {
687
692
  throw lastTransientError;
688
693
  return {
689
694
  response: lastResponse ??
690
- { request_id: requestId, reply_expires_at: null, replies: [] },
695
+ {
696
+ request_id: requestId,
697
+ reply_expires_at: null,
698
+ agent_acknowledgement_required: false,
699
+ agent_acknowledgement: null,
700
+ replies: [],
701
+ },
691
702
  timedOut: true,
692
703
  // A poll succeeded at some point, so we do not throw — but the last thing
693
704
  // we know is that we could not reach the server. Reporting that as a plain
@@ -723,6 +734,39 @@ function degradedWaitWarning(requestId) {
723
734
  function isNonNegativeInteger(value) {
724
735
  return Number.isInteger(value) && value >= 0;
725
736
  }
737
+ function acknowledgementCommand(requestId, required, acknowledgement, hasReply = true) {
738
+ return required && acknowledgement === null && hasReply
739
+ ? `notifai acknowledge ${requestId} --text <text>`
740
+ : null;
741
+ }
742
+ function replyResultJson(response, degraded) {
743
+ return {
744
+ type: 'reply_result',
745
+ request_id: response.request_id,
746
+ reply_expires_at: response.reply_expires_at,
747
+ replies: response.replies,
748
+ agent_acknowledgement_required: response.agent_acknowledgement_required,
749
+ agent_acknowledgement: response.agent_acknowledgement,
750
+ acknowledgement_command: acknowledgementCommand(response.request_id, response.agent_acknowledgement_required, response.agent_acknowledgement, response.replies.length > 0),
751
+ degraded,
752
+ };
753
+ }
754
+ function printAcknowledgementStatus(deps, response) {
755
+ if (!response.agent_acknowledgement_required) {
756
+ deps.io.out('Agent Acknowledgement: not required for this request.');
757
+ return;
758
+ }
759
+ if (response.agent_acknowledgement !== null) {
760
+ deps.io.out(`Agent Acknowledgement: recorded at ${response.agent_acknowledgement.created_at}: ${response.agent_acknowledgement.text}`);
761
+ return;
762
+ }
763
+ if (response.replies.length > 0) {
764
+ deps.io.out('Agent Acknowledgement required.');
765
+ deps.io.out(`next: Run \`${acknowledgementCommand(response.request_id, true, null)}\` with concrete text saying what you will do because of the reply.`);
766
+ return;
767
+ }
768
+ deps.io.out('Agent Acknowledgement: required after a user reply; no reply is recorded yet.');
769
+ }
726
770
  function recordReplies(deps, requestId, replies) {
727
771
  const logger = log(deps);
728
772
  for (const reply of replies) {
@@ -1128,6 +1172,8 @@ function stopWakeRoute(deps, harness, sessionId, cwd) {
1128
1172
  if (sessionId === undefined)
1129
1173
  return undefined;
1130
1174
  if (harness === 'claude-code') {
1175
+ if ((deps.hookPlatform ?? process.platform) === 'win32')
1176
+ return undefined;
1131
1177
  return claudeWakeRoute({
1132
1178
  sessionId,
1133
1179
  cwd,
@@ -1316,7 +1362,7 @@ export function askCommand(deps, question, flags) {
1316
1362
  const { active, contested } = resolveActiveHarness(deps.env, deps.cwd, now);
1317
1363
  let sessionId;
1318
1364
  if (active !== null) {
1319
- const installations = findInstallations(deps.cwd, deps.env, deps.hookAdapterHome);
1365
+ const installations = findInstallations(deps.cwd, deps.env, deps.hookAdapterHome, deps.hookPlatform);
1320
1366
  const activeInstalled = installations.some((installation) => installation.harness === active.harness);
1321
1367
  if (!activeInstalled) {
1322
1368
  for (const line of diagnoseActiveHarnessSession(active, 'not-installed', installations, contested)) {
@@ -1518,11 +1564,11 @@ function activeQuestionRouteProblems(deps, active, installations) {
1518
1564
  }
1519
1565
  }
1520
1566
  }
1521
- for (const problem of inspectHookAdapter(deps.hookAdapterHome).problems) {
1567
+ for (const problem of inspectHookAdapter(deps.hookAdapterHome, deps.hookPlatform).problems) {
1522
1568
  problems.push(problem);
1523
1569
  }
1524
1570
  for (const installation of matching)
1525
- problems.push(...stopShapeProblems(installation));
1571
+ problems.push(...stopShapeProblems(installation, deps.hookPlatform));
1526
1572
  problems.push(...codexTrustProblems(matching, deps.env));
1527
1573
  return [...new Set(problems)];
1528
1574
  }
@@ -1574,7 +1620,7 @@ function diagnoseActiveHarnessSession(active, problem, installations, contested
1574
1620
  * checked after a prompt before assuming that a new session is required.
1575
1621
  */
1576
1622
  function diagnoseMissingSession(deps) {
1577
- const installations = findInstallations(deps.cwd, deps.env, deps.hookAdapterHome);
1623
+ const installations = findInstallations(deps.cwd, deps.env, deps.hookAdapterHome, deps.hookPlatform);
1578
1624
  if (installations.length === 0) {
1579
1625
  return [
1580
1626
  'Could not tell which harness session this is: no Notifai hooks are installed for this project.',
@@ -1613,15 +1659,76 @@ function hookActivationAdvice(installations) {
1613
1659
  }
1614
1660
  return `${advice.join('. ')}.`;
1615
1661
  }
1662
+ /** Record the one Agent Acknowledgement associated with a replied-to request. */
1663
+ export async function acknowledgeCommand(deps, requestId, flags) {
1664
+ const text = flags.text?.trim() ?? '';
1665
+ if (text.length === 0) {
1666
+ deps.io.err('--text is required and must contain non-whitespace text.');
1667
+ return EXIT.usage;
1668
+ }
1669
+ if (text.length > AGENT_ACKNOWLEDGEMENT_MAX_LENGTH) {
1670
+ deps.io.err(`--text must be at most ${AGENT_ACKNOWLEDGEMENT_MAX_LENGTH} characters after trimming.`);
1671
+ return EXIT.usage;
1672
+ }
1673
+ const logger = log(deps);
1674
+ const config = loadLoggedConfig(deps, { cwd: deps.cwd, env: deps.env });
1675
+ logger.info('acknowledgement.attempted', {
1676
+ request_id: requestId,
1677
+ text,
1678
+ characters: text.length,
1679
+ });
1680
+ const authed = authedClient(deps, config);
1681
+ if (!authed)
1682
+ return EXIT.auth;
1683
+ try {
1684
+ const result = await authed.client.putAgentAcknowledgement(requestId, { text });
1685
+ const output = {
1686
+ request_id: requestId,
1687
+ outcome: result.status,
1688
+ acknowledgement: result.agent_acknowledgement,
1689
+ agent_acknowledgement_required: true,
1690
+ };
1691
+ logger.info('acknowledgement.outcome', {
1692
+ request_id: requestId,
1693
+ outcome: result.status,
1694
+ text: result.agent_acknowledgement.text,
1695
+ created_at: result.agent_acknowledgement.created_at,
1696
+ agent_acknowledgement_required: true,
1697
+ });
1698
+ const sessionId = readProjectSession(deps.cwd, deps.env, (deps.now ?? Date.now)());
1699
+ if (sessionId !== null) {
1700
+ clearAcknowledgementObligation(sessionId, deps.env, requestId);
1701
+ }
1702
+ if (flags.json)
1703
+ deps.io.out(JSON.stringify(output, null, 2));
1704
+ else {
1705
+ deps.io.out(`Agent Acknowledgement ${result.status} for ${requestId} at ${result.agent_acknowledgement.created_at}.`);
1706
+ }
1707
+ return EXIT.ok;
1708
+ }
1709
+ catch (err) {
1710
+ return reportError(deps, err, { operation: 'agent_acknowledgement', request_id: requestId });
1711
+ }
1712
+ }
1616
1713
  /** Retire a question so a late answer is rejected rather than silently lost. */
1617
- export async function closeCommand(deps, requestId) {
1714
+ export async function closeCommand(deps, requestId, flags = {}) {
1618
1715
  const config = loadLoggedConfig(deps, { cwd: deps.cwd, env: deps.env });
1619
1716
  const authed = authedClient(deps, config);
1620
1717
  if (!authed)
1621
1718
  return EXIT.auth;
1622
1719
  try {
1623
- await authed.client.closeReplies(requestId);
1624
- deps.io.out(`Closed the reply window for ${requestId}.`);
1720
+ const response = await authed.client.closeReplies(requestId);
1721
+ if (flags.json) {
1722
+ deps.io.out(JSON.stringify({
1723
+ ...response,
1724
+ acknowledgement_command: acknowledgementCommand(response.request_id, response.agent_acknowledgement_required, response.agent_acknowledgement, response.replies.length > 0),
1725
+ }, null, 2));
1726
+ }
1727
+ else {
1728
+ deps.io.out(`Closed the reply window for ${requestId}.`);
1729
+ if (response.replies.length > 0)
1730
+ printAcknowledgementStatus(deps, response);
1731
+ }
1625
1732
  return EXIT.ok;
1626
1733
  }
1627
1734
  catch (err) {
@@ -1687,16 +1794,18 @@ export function hooksInstallCommand(deps, flags) {
1687
1794
  return EXIT.usage;
1688
1795
  const adapterTarget = resolveHookAdapterTarget(deps, flags);
1689
1796
  const scriptPath = flags.scriptPath ?? fileHookInstallTarget(adapterTarget)?.scriptPath ?? process.argv[1] ?? 'notifai';
1797
+ const hookPlatform = deps.hookPlatform ?? process.platform;
1798
+ const nodePath = adapterTarget.execPath;
1690
1799
  let adapterPath;
1691
1800
  try {
1692
- adapterPath = installHookAdapter(adapterTarget, deps.hookAdapterHome).path;
1801
+ adapterPath = installHookAdapter(adapterTarget, deps.hookAdapterHome, hookPlatform).path;
1693
1802
  }
1694
1803
  catch (err) {
1695
1804
  deps.io.err(`Could not prepare the stable hook adapter: ${String(err)}`);
1696
1805
  return EXIT.failed;
1697
1806
  }
1698
1807
  const wantGlobal = flags.global === true;
1699
- const existing = findInstallations(deps.cwd, deps.env, deps.hookAdapterHome).filter((installation) => installation.harness === harness);
1808
+ const existing = findInstallations(deps.cwd, deps.env, deps.hookAdapterHome, deps.hookPlatform).filter((installation) => installation.harness === harness);
1700
1809
  const otherScope = existing.filter((installation) => installation.global !== wantGlobal);
1701
1810
  if (!wantGlobal && otherScope.some((installation) => installation.global)) {
1702
1811
  const globalFile = otherScope.find((installation) => installation.global)?.file;
@@ -1708,13 +1817,15 @@ export function hooksInstallCommand(deps, flags) {
1708
1817
  return EXIT.failed;
1709
1818
  }
1710
1819
  }
1711
- const file = settingsFile(harness, wantGlobal, deps.cwd, deps.env);
1820
+ const file = settingsFile(harness, wantGlobal, deps.cwd, deps.env, hookPlatform);
1712
1821
  // OpenCode's adapter is a generated plugin module rather than a handler
1713
1822
  // merged into a settings document, so it owns the whole file.
1714
1823
  if (harness === 'opencode') {
1715
1824
  return installOpencodePlugin(deps, file, {
1716
1825
  adapterPath,
1717
1826
  timeoutSeconds: BLOCKING_STOP_TIMEOUT_SECONDS,
1827
+ platform: hookPlatform,
1828
+ nodePath,
1718
1829
  });
1719
1830
  }
1720
1831
  if (harness === 'cursor') {
@@ -1724,6 +1835,8 @@ export function hooksInstallCommand(deps, flags) {
1724
1835
  const result = mergeCursorHooks(document, buildCursorHookConfig({
1725
1836
  adapterPath,
1726
1837
  harness: 'cursor',
1838
+ platform: hookPlatform,
1839
+ nodePath,
1727
1840
  }), scriptPath);
1728
1841
  applyPlan(file, result.document);
1729
1842
  return result;
@@ -1745,6 +1858,8 @@ export function hooksInstallCommand(deps, flags) {
1745
1858
  const result = mergeHooks(document, buildHookConfig({
1746
1859
  adapterPath,
1747
1860
  harness,
1861
+ platform: hookPlatform,
1862
+ nodePath,
1748
1863
  }), scriptPath);
1749
1864
  applyPlan(file, result.document);
1750
1865
  return result;
@@ -1818,7 +1933,7 @@ export function hooksUninstallCommand(deps, flags) {
1818
1933
  if (!harness)
1819
1934
  return EXIT.usage;
1820
1935
  const scriptPath = flags.scriptPath ?? process.argv[1] ?? 'notifai';
1821
- const file = settingsFile(harness, flags.global ?? false, deps.cwd, deps.env);
1936
+ const file = settingsFile(harness, flags.global ?? false, deps.cwd, deps.env, deps.hookPlatform);
1822
1937
  if (harness === 'opencode') {
1823
1938
  try {
1824
1939
  return withTargetFileLock(file, () => {
@@ -2515,9 +2630,7 @@ function setupProofPath(deps) {
2515
2630
  // the resolved absolute path is still a stable local identity for it.
2516
2631
  }
2517
2632
  const digest = createHash('sha256').update(projectDir).digest('hex').slice(0, 32);
2518
- const xdg = deps.env['XDG_STATE_HOME'];
2519
- const base = xdg && xdg !== '' ? xdg : path.join(os.homedir(), '.local', 'state');
2520
- return path.join(base, 'notifai', 'setup-proofs', `${digest}.json`);
2633
+ return path.join(stateDir(deps.env), 'setup-proofs', `${digest}.json`);
2521
2634
  }
2522
2635
  function readSetupProof(deps) {
2523
2636
  const file = setupProofPath(deps);
@@ -2816,6 +2929,7 @@ async function runSetupProof(deps) {
2816
2929
  const observed = observedCompanionReceipt(snapshot, proof.device_id);
2817
2930
  if (observed) {
2818
2931
  spinner?.stop(`Receipt observed from ${observed.delivery.device_name}`);
2932
+ deps.io.out(`Companion Receipt (the app's delivery confirmation) observed from ${observed.delivery.device_name}.`);
2819
2933
  return 'closed';
2820
2934
  }
2821
2935
  lastError = null;
@@ -3559,7 +3673,7 @@ function remedyLine(state) {
3559
3673
  * is, is each check's own call (`HookCheck`).
3560
3674
  */
3561
3675
  function hookStates(deps) {
3562
- const installations = findInstallations(deps.cwd, deps.env, deps.hookAdapterHome);
3676
+ const installations = findInstallations(deps.cwd, deps.env, deps.hookAdapterHome, deps.hookPlatform);
3563
3677
  const active = activeHarnessSession(deps.env, deps.cwd, (deps.now ?? Date.now)());
3564
3678
  const config = loadLoggedConfig(deps, { cwd: deps.cwd, env: deps.env });
3565
3679
  const settings = {
@@ -3647,12 +3761,12 @@ function hookStates(deps) {
3647
3761
  * hashes into `trusted_hash`, and an untrusted handler is simply not run.
3648
3762
  * - Everything else blocks its turn and needs a ceiling above the wait.
3649
3763
  */
3650
- function stopShapeProblems(installation) {
3764
+ function stopShapeProblems(installation, platform = process.platform) {
3651
3765
  if (installation.harness === 'codex')
3652
3766
  return [];
3653
3767
  const problems = [];
3654
3768
  for (const handler of installation.handlers.filter((entry) => handlerEvent(entry.command) === 'stop')) {
3655
- if (installation.harness === 'claude-code') {
3769
+ if (installation.harness === 'claude-code' && platform !== 'win32') {
3656
3770
  if (handler.async !== true) {
3657
3771
  problems.push(`${installation.file} declares a blocking Stop handler; the Claude Code wake route needs \`async: true\` so the turn ends while the waiter runs`);
3658
3772
  }
@@ -3669,7 +3783,7 @@ function stopShapeProblems(installation) {
3669
3783
  }
3670
3784
  function hookChecks(deps) {
3671
3785
  const checks = [];
3672
- const installations = findInstallations(deps.cwd, deps.env, deps.hookAdapterHome);
3786
+ const installations = findInstallations(deps.cwd, deps.env, deps.hookAdapterHome, deps.hookPlatform);
3673
3787
  // Not having hooks is a setup someone chose, not a fault: `send` works
3674
3788
  // without them. A setup that cannot work is what deserves to go red.
3675
3789
  if (installations.length === 0) {
@@ -3766,7 +3880,7 @@ function hookChecks(deps) {
3766
3880
  : `${stale.join('; ')} — rerun \`notifai hooks install\` to drop ${stale.length === 1 ? 'it' : 'them'}`,
3767
3881
  });
3768
3882
  const adapterProblems = installations.flatMap((installation) => (installation.problems ?? []).map((problem) => `${installation.file}: ${problem}`));
3769
- const sharedAdapter = inspectHookAdapter(deps.hookAdapterHome);
3883
+ const sharedAdapter = inspectHookAdapter(deps.hookAdapterHome, deps.hookPlatform);
3770
3884
  adapterProblems.push(...sharedAdapter.problems);
3771
3885
  if (adapterProblems.length > 0) {
3772
3886
  checks.push({
@@ -3794,7 +3908,7 @@ function hookChecks(deps) {
3794
3908
  },
3795
3909
  }),
3796
3910
  });
3797
- const shapeProblems = installations.flatMap((installation) => stopShapeProblems(installation).map((problem) => `${problem} — run \`notifai hooks install --harness ${installation.harness}${installation.global ? ' --global' : ''}\``));
3911
+ const shapeProblems = installations.flatMap((installation) => stopShapeProblems(installation, deps.hookPlatform).map((problem) => `${problem} — run \`notifai hooks install --harness ${installation.harness}${installation.global ? ' --global' : ''}\``));
3798
3912
  checks.push({
3799
3913
  name: 'hooks (stop shape)',
3800
3914
  ok: shapeProblems.length === 0,
@@ -3963,7 +4077,7 @@ function wakeRouteCheck(deps, active, activeInstallations) {
3963
4077
  if (active.harness === 'claude-code') {
3964
4078
  const readiness = inspectClaudeInbox({
3965
4079
  pid: deps.claudeSourcePid ?? claudeSessionPid(deps.env),
3966
- platform: process.platform,
4080
+ platform: deps.hookPlatform ?? process.platform,
3967
4081
  readDescriptor: deps.claudeWake?.readDescriptor ?? systemClaudeWakeAdapters(deps.env).readDescriptor,
3968
4082
  socketExists: (socketPath) => existsSync(socketPath),
3969
4083
  });
@@ -3978,7 +4092,7 @@ function wakeRouteCheck(deps, active, activeInstallations) {
3978
4092
  }
3979
4093
  if (active.harness === 'codex') {
3980
4094
  const readiness = inspectCodexResume(deps.env, {
3981
- platform: process.platform,
4095
+ platform: deps.hookPlatform ?? process.platform,
3982
4096
  directoryExists: (directory) => existsSync(directory),
3983
4097
  });
3984
4098
  return {
@@ -4133,24 +4247,7 @@ export function realIo(env = process.env) {
4133
4247
  return log.error(message);
4134
4248
  }
4135
4249
  },
4136
- openUrl: (url) => {
4137
- try {
4138
- if (process.platform === 'darwin') {
4139
- spawn('open', [url], { stdio: 'ignore', detached: true }).unref();
4140
- }
4141
- else if (process.platform === 'win32') {
4142
- // `start` is a cmd builtin; spawn('start', …) cannot work on Windows.
4143
- // Empty title argument keeps URLs with special characters intact.
4144
- spawn('cmd', ['/c', 'start', '', url], { stdio: 'ignore', detached: true }).unref();
4145
- }
4146
- else {
4147
- spawn('xdg-open', [url], { stdio: 'ignore', detached: true }).unref();
4148
- }
4149
- }
4150
- catch {
4151
- // Browser opening is best-effort; the URL is printed anyway.
4152
- }
4153
- },
4250
+ openUrl,
4154
4251
  };
4155
4252
  }
4156
4253
  //# sourceMappingURL=commands.js.map