@raidiant/notifai 9.3.0 → 10.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 (101) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/dist/client.d.ts +3 -1
  3. package/dist/client.js +2 -0
  4. package/dist/client.js.map +1 -1
  5. package/dist/commands-agent-sessions.d.ts +6 -0
  6. package/dist/commands-agent-sessions.js +51 -0
  7. package/dist/commands-agent-sessions.js.map +1 -0
  8. package/dist/commands-config.js +11 -4
  9. package/dist/commands-config.js.map +1 -1
  10. package/dist/commands-core.d.ts +3 -0
  11. package/dist/commands-core.js.map +1 -1
  12. package/dist/commands-doctor.js +42 -4
  13. package/dist/commands-doctor.js.map +1 -1
  14. package/dist/commands-harness-context.d.ts +15 -2
  15. package/dist/commands-harness-context.js +32 -1
  16. package/dist/commands-harness-context.js.map +1 -1
  17. package/dist/commands-hooks.d.ts +4 -3
  18. package/dist/commands-hooks.js +17 -11
  19. package/dist/commands-hooks.js.map +1 -1
  20. package/dist/commands-send-support.js +18 -1
  21. package/dist/commands-send-support.js.map +1 -1
  22. package/dist/commands-send.js +2 -2
  23. package/dist/commands-send.js.map +1 -1
  24. package/dist/commands-sounds.d.ts +12 -0
  25. package/dist/commands-sounds.js +35 -0
  26. package/dist/commands-sounds.js.map +1 -0
  27. package/dist/commands.d.ts +2 -0
  28. package/dist/commands.js +2 -0
  29. package/dist/commands.js.map +1 -1
  30. package/dist/config-schema.js +5 -2
  31. package/dist/config-schema.js.map +1 -1
  32. package/dist/config.d.ts +3 -4
  33. package/dist/config.js +5 -2
  34. package/dist/config.js.map +1 -1
  35. package/dist/credentials.js +4 -1
  36. package/dist/credentials.js.map +1 -1
  37. package/dist/harnesses.d.ts +49 -7
  38. package/dist/harnesses.js +90 -24
  39. package/dist/harnesses.js.map +1 -1
  40. package/dist/hooks.d.ts +2 -2
  41. package/dist/hooks.js +2 -2
  42. package/dist/hooks.js.map +1 -1
  43. package/dist/install-hooks.d.ts +10 -11
  44. package/dist/install-hooks.js +3 -4
  45. package/dist/install-hooks.js.map +1 -1
  46. package/dist/invocation-context.d.ts +2 -2
  47. package/dist/orca-session-title.d.ts +10 -0
  48. package/dist/orca-session-title.js +98 -0
  49. package/dist/orca-session-title.js.map +1 -0
  50. package/dist/program.d.ts +3 -1
  51. package/dist/program.js +28 -5
  52. package/dist/program.js.map +1 -1
  53. package/dist/send.js +11 -14
  54. package/dist/send.js.map +1 -1
  55. package/dist/session-labels.d.ts +18 -2
  56. package/dist/session-labels.js +79 -8
  57. package/dist/session-labels.js.map +1 -1
  58. package/dist/sound-ref.d.ts +23 -0
  59. package/dist/sound-ref.js +28 -0
  60. package/dist/sound-ref.js.map +1 -0
  61. package/package.json +3 -3
  62. package/src/client.test.ts +12 -0
  63. package/src/client.ts +8 -0
  64. package/src/commands-agent-sessions.ts +72 -0
  65. package/src/commands-config.ts +10 -4
  66. package/src/commands-core.ts +3 -0
  67. package/src/commands-doctor.ts +48 -4
  68. package/src/commands-harness-context.ts +48 -2
  69. package/src/commands-hooks.ts +36 -18
  70. package/src/commands-send-support.ts +23 -1
  71. package/src/commands-send.ts +2 -2
  72. package/src/commands-sounds.test.ts +106 -0
  73. package/src/commands-sounds.ts +49 -0
  74. package/src/commands.test.ts +344 -11
  75. package/src/commands.ts +5 -0
  76. package/src/config-schema.test.ts +1 -0
  77. package/src/config-schema.ts +5 -2
  78. package/src/config.test.ts +42 -20
  79. package/src/config.ts +6 -5
  80. package/src/credentials.test.ts +20 -0
  81. package/src/credentials.ts +4 -1
  82. package/src/fixtures/hermes-0.20.6-classic-cli-local.json +50 -0
  83. package/src/harness-integration.test.ts +212 -0
  84. package/src/harnesses.test.ts +107 -8
  85. package/src/harnesses.ts +111 -30
  86. package/src/hooks.test.ts +4 -3
  87. package/src/hooks.ts +4 -4
  88. package/src/install-hooks.test.ts +5 -4
  89. package/src/install-hooks.ts +19 -21
  90. package/src/invocation-context.ts +2 -2
  91. package/src/orca-session-title.test.ts +54 -0
  92. package/src/orca-session-title.ts +103 -0
  93. package/src/program.test.ts +27 -0
  94. package/src/program.ts +38 -4
  95. package/src/send.ts +10 -19
  96. package/src/session-labels.test.ts +58 -9
  97. package/src/session-labels.ts +95 -12
  98. package/src/skill.test.ts +14 -3
  99. package/src/sound-ref.test.ts +31 -0
  100. package/src/sound-ref.ts +34 -0
  101. package/src/ui/config-view.test.ts +1 -0
package/dist/harnesses.js CHANGED
@@ -1,35 +1,101 @@
1
- /** Every harness Notifai ships. This is the sole source of the harness union. */
2
- export const HARNESSES = ['claude-code', 'codex', 'cursor', 'opencode'];
1
+ /** Every harness Notifai can name in Source Context when authoritatively known. */
2
+ export const SOURCE_CONTEXT_HARNESSES = ['claude-code', 'codex', 'cursor', 'opencode', 'hermes'];
3
+ /**
4
+ * Harnesses whose lifecycle Notifai can install and manage.
5
+ *
6
+ * Source Context is the broader vocabulary. Managed hook installation is a
7
+ * narrower, independently proven cell: Hermes can appear in Source Context
8
+ * without being hook-installable.
9
+ */
10
+ export const HOOK_INSTALLABLE_HARNESSES = ['claude-code', 'codex', 'cursor', 'opencode'];
11
+ export function isHookInstallableHarness(value) {
12
+ return HOOK_INSTALLABLE_HARNESSES.includes(value);
13
+ }
3
14
  export const HARNESS_LABELS = {
4
15
  'claude-code': 'Claude Code',
5
16
  codex: 'Codex',
6
17
  cursor: 'Cursor',
7
18
  opencode: 'OpenCode',
19
+ hermes: 'Hermes',
20
+ };
21
+ const CLAUDE_CODE_CAPABILITY = {
22
+ stopContinuation: 'decision-block',
23
+ deliveryRoutes: ['hook-continuation', 'inbox-socket', 'cold-resume', 'hold-for-next-turn'],
24
+ deliveryContract: 'the Stop hook returns at once and waits out of band through the complete answer window, then posts the answer into this same session over its own inbox socket; a session that has stopped is resumed only once a liveness probe proves it stopped',
25
+ };
26
+ const CODEX_CAPABILITY = {
27
+ stopContinuation: 'decision-block',
28
+ deliveryRoutes: ['hook-continuation', 'cold-resume', 'hold-for-next-turn'],
29
+ deliveryContract: 'live Stop continuation while the turn is held through the complete answer window; crash recovery may resume only a stopped thread behind its writer lock',
30
+ };
31
+ const CURSOR_CAPABILITY = {
32
+ stopContinuation: 'unsupported',
33
+ deliveryRoutes: ['unsupported'],
34
+ deliveryContract: 'the hook can return a live follow-up, but the invoking shell exposes no exact conversation id; asynchronous ask is unsupported',
35
+ };
36
+ const OPENCODE_CAPABILITY = {
37
+ stopContinuation: 'unsupported',
38
+ deliveryRoutes: ['unsupported'],
39
+ deliveryContract: 'no proven answer continuation after session.idle; use a blocking reply command',
40
+ };
41
+ export const HERMES_QUESTION_ROUTING_UNAVAILABLE = {
42
+ stopContinuation: 'unsupported',
43
+ deliveryRoutes: ['unsupported'],
44
+ deliveryContract: 'Hermes asynchronous ask has no proven continuation owner on this integration surface. Use a blocking `notifai send --reply` question',
8
45
  };
9
46
  /**
10
- * Exhaustive runtime contract. Adding a harness without deciding every
11
- * continuation joint is a type error, not a silently degraded notification.
47
+ * The one Hermes integration instance proven by the pinned vertical probe.
48
+ *
49
+ * This is evidence-bearing capability data, not an adapter. Unsupported and
50
+ * deferred surfaces intentionally have no empty implementation object here.
12
51
  */
13
- export const HARNESS_CAPABILITIES = {
14
- 'claude-code': {
15
- stopContinuation: 'decision-block',
16
- deliveryRoutes: ['hook-continuation', 'inbox-socket', 'cold-resume', 'hold-for-next-turn'],
17
- deliveryContract: 'the Stop hook returns at once and waits out of band through the complete answer window, then posts the answer into this same session over its own inbox socket; a session that has stopped is resumed only once a liveness probe proves it stopped',
18
- },
19
- codex: {
20
- stopContinuation: 'decision-block',
21
- deliveryRoutes: ['hook-continuation', 'cold-resume', 'hold-for-next-turn'],
22
- deliveryContract: 'live Stop continuation while the turn is held through the complete answer window; crash recovery may resume only a stopped thread behind its writer lock',
23
- },
24
- cursor: {
25
- stopContinuation: 'unsupported',
26
- deliveryRoutes: ['unsupported'],
27
- deliveryContract: 'the hook can return a live follow-up, but the invoking shell exposes no exact conversation id; asynchronous ask is unsupported',
28
- },
29
- opencode: {
30
- stopContinuation: 'unsupported',
31
- deliveryRoutes: ['unsupported'],
32
- deliveryContract: 'no proven answer continuation after session.idle; use a blocking reply command',
52
+ export const HERMES_CLASSIC_CLI_LOCAL_CAPABILITY = {
53
+ instance: {
54
+ harness: 'hermes',
55
+ surface: 'classic-cli',
56
+ terminalBackend: 'local',
33
57
  },
58
+ setup: 'unsupported',
59
+ activation: 'unsupported',
60
+ sourceContext: 'hermes-session-id-and-invocation-cwd',
61
+ continuation: 'unsupported',
62
+ };
63
+ /**
64
+ * Resolve only the Hermes instance whose complete marker envelope was proven.
65
+ *
66
+ * `HERMES_SESSION_ID` is also bridged by deferred gateway, TUI, API, ACP, and
67
+ * remote-backend paths. Their first-party routing/source/backend markers keep
68
+ * those cells distinct. Missing source/backend values are the pinned classic
69
+ * CLI defaults; a gateway key is never part of that instance.
70
+ */
71
+ export function hermesClassicCliLocalInstance(env) {
72
+ const sessionId = (env['HERMES_SESSION_ID'] ?? '').trim();
73
+ const sessionKey = (env['HERMES_SESSION_KEY'] ?? '').trim();
74
+ const platform = (env['HERMES_SESSION_PLATFORM'] ?? '').trim().toLowerCase();
75
+ const source = (env['HERMES_SESSION_SOURCE'] ?? '').trim().toLowerCase();
76
+ const terminalBackend = (env['TERMINAL_ENV'] ?? '').trim().toLowerCase();
77
+ if (sessionId === '' || sessionKey !== '' || platform !== '')
78
+ return null;
79
+ if (source !== '' && source !== 'cli')
80
+ return null;
81
+ if (terminalBackend !== '' && terminalBackend !== 'local')
82
+ return null;
83
+ return HERMES_CLASSIC_CLI_LOCAL_CAPABILITY.instance;
84
+ }
85
+ /**
86
+ * Question-routing contract for hook-installable harnesses.
87
+ *
88
+ * Hermes is deliberately absent: a send-only surface must not occupy this
89
+ * table with a fake managed-hook continuation route.
90
+ */
91
+ export const HARNESS_CAPABILITIES = {
92
+ 'claude-code': CLAUDE_CODE_CAPABILITY,
93
+ codex: CODEX_CAPABILITY,
94
+ cursor: CURSOR_CAPABILITY,
95
+ opencode: OPENCODE_CAPABILITY,
34
96
  };
97
+ /** Question-routing capability for the active harness integration. */
98
+ export function questionRoutingCapability(harness) {
99
+ return HARNESS_CAPABILITIES[harness];
100
+ }
35
101
  //# sourceMappingURL=harnesses.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"harnesses.js","sourceRoot":"","sources":["../src/harnesses.ts"],"names":[],"mappings":"AAAA,iFAAiF;AACjF,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,aAAa,EAAE,OAAO,EAAE,QAAQ,EAAE,UAAU,CAAU,CAAA;AAIhF,MAAM,CAAC,MAAM,cAAc,GAA4B;IACrD,aAAa,EAAE,aAAa;IAC5B,KAAK,EAAE,OAAO;IACd,MAAM,EAAE,QAAQ;IAChB,QAAQ,EAAE,UAAU;CACrB,CAAA;AAqBD;;;GAGG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAuC;IACtE,aAAa,EAAE;QACb,gBAAgB,EAAE,gBAAgB;QAClC,cAAc,EAAE,CAAC,mBAAmB,EAAE,cAAc,EAAE,aAAa,EAAE,oBAAoB,CAAC;QAC1F,gBAAgB,EACd,oPAAoP;KACvP;IACD,KAAK,EAAE;QACL,gBAAgB,EAAE,gBAAgB;QAClC,cAAc,EAAE,CAAC,mBAAmB,EAAE,aAAa,EAAE,oBAAoB,CAAC;QAC1E,gBAAgB,EACd,0JAA0J;KAC7J;IACD,MAAM,EAAE;QACN,gBAAgB,EAAE,aAAa;QAC/B,cAAc,EAAE,CAAC,aAAa,CAAC;QAC/B,gBAAgB,EACd,gIAAgI;KACnI;IACD,QAAQ,EAAE;QACR,gBAAgB,EAAE,aAAa;QAC/B,cAAc,EAAE,CAAC,aAAa,CAAC;QAC/B,gBAAgB,EACd,gFAAgF;KACnF;CACF,CAAA"}
1
+ {"version":3,"file":"harnesses.js","sourceRoot":"","sources":["../src/harnesses.ts"],"names":[],"mappings":"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"}
package/dist/hooks.d.ts CHANGED
@@ -2,7 +2,7 @@ import type { LifecycleEndState, MediaItemT, NotificationDraftT, QuestionT, Repl
2
2
  import { type ApiClient } from './client.js';
3
3
  import { type CliConfig } from './config.js';
4
4
  import type { Logger } from './logging.js';
5
- import { type DeliveryRoute, type Harness } from './harnesses.js';
5
+ import { type DeliveryRoute, type HookInstallableHarness } from './harnesses.js';
6
6
  /**
7
7
  * Harness hook handlers.
8
8
  *
@@ -373,7 +373,7 @@ export declare const GATE_REASONS: readonly ["no-session", "no-question", "answe
373
373
  * emitted once; documenting them taught agents to filter for silence.
374
374
  */
375
375
  export type GateReason = (typeof GATE_REASONS)[number];
376
- export type HookHarness = Harness;
376
+ export type HookHarness = HookInstallableHarness;
377
377
  /**
378
378
  * Everything retirement needs. Narrower than a HookContext on purpose:
379
379
  * `notifai ask` supersedes the previous question and it is a plain command with
package/dist/hooks.js CHANGED
@@ -9,7 +9,7 @@ import { buildDraft } from './send.js';
9
9
  import { atomicWriteFileSync } from './atomic-file.js';
10
10
  import { withFileLock } from './file-lock.js';
11
11
  import { inferInvocationContext } from './invocation-context.js';
12
- import { HARNESS_CAPABILITIES, HARNESSES, } from './harnesses.js';
12
+ import { HARNESS_CAPABILITIES, HOOK_INSTALLABLE_HARNESSES, } from './harnesses.js';
13
13
  /**
14
14
  * A retirement is only worth syncing while the question it retires could still
15
15
  * be answered, so this has to outlive the longest reply window a user can
@@ -474,7 +474,7 @@ function storedProjectSessionPointer(candidate) {
474
474
  return null;
475
475
  if (typeof entry.updated_at !== 'number')
476
476
  return null;
477
- if (!HARNESSES.includes(entry.harness))
477
+ if (!HOOK_INSTALLABLE_HARNESSES.includes(entry.harness))
478
478
  return null;
479
479
  return {
480
480
  sessionId: entry.session_id,