@raidiant/notifai 11.4.1 → 11.6.0-beta.2

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 (107) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/README.md +7 -3
  3. package/dist/codex-tool-messages.d.ts +17 -0
  4. package/dist/codex-tool-messages.js +142 -0
  5. package/dist/codex-tool-messages.js.map +1 -0
  6. package/dist/commands-acknowledge.js +11 -0
  7. package/dist/commands-acknowledge.js.map +1 -1
  8. package/dist/commands-ask.js +22 -3
  9. package/dist/commands-ask.js.map +1 -1
  10. package/dist/commands-doctor.js +149 -39
  11. package/dist/commands-doctor.js.map +1 -1
  12. package/dist/commands-harness-context.js +3 -0
  13. package/dist/commands-harness-context.js.map +1 -1
  14. package/dist/commands-hook-attend.d.ts +2 -0
  15. package/dist/commands-hook-attend.js +87 -22
  16. package/dist/commands-hook-attend.js.map +1 -1
  17. package/dist/commands-hook-diagnostics.d.ts +11 -1
  18. package/dist/commands-hook-diagnostics.js +40 -1
  19. package/dist/commands-hook-diagnostics.js.map +1 -1
  20. package/dist/commands-hook-install.js +162 -21
  21. package/dist/commands-hook-install.js.map +1 -1
  22. package/dist/commands-hook-run.js +245 -8
  23. package/dist/commands-hook-run.js.map +1 -1
  24. package/dist/commands-hook-shape.d.ts +4 -3
  25. package/dist/commands-hook-shape.js +5 -4
  26. package/dist/commands-hook-shape.js.map +1 -1
  27. package/dist/commands-send-support.js +3 -0
  28. package/dist/commands-send-support.js.map +1 -1
  29. package/dist/commands-send.js +29 -0
  30. package/dist/commands-send.js.map +1 -1
  31. package/dist/harnesses.d.ts +14 -10
  32. package/dist/harnesses.js +39 -9
  33. package/dist/harnesses.js.map +1 -1
  34. package/dist/hermes-attendant.d.ts +38 -0
  35. package/dist/hermes-attendant.js +333 -0
  36. package/dist/hermes-attendant.js.map +1 -0
  37. package/dist/hermes-plugin.d.ts +11 -0
  38. package/dist/hermes-plugin.js +299 -0
  39. package/dist/hermes-plugin.js.map +1 -0
  40. package/dist/hook-events.d.ts +9 -2
  41. package/dist/hook-events.js +16 -6
  42. package/dist/hook-events.js.map +1 -1
  43. package/dist/hook-lifecycle.d.ts +1 -1
  44. package/dist/hook-lifecycle.js +27 -10
  45. package/dist/hook-lifecycle.js.map +1 -1
  46. package/dist/hook-session-state.d.ts +4 -1
  47. package/dist/hook-session-state.js +16 -4
  48. package/dist/hook-session-state.js.map +1 -1
  49. package/dist/hook-types.d.ts +23 -0
  50. package/dist/install-hooks.js +51 -4
  51. package/dist/install-hooks.js.map +1 -1
  52. package/dist/invocation-context.d.ts +1 -0
  53. package/dist/invocation-context.js +3 -0
  54. package/dist/invocation-context.js.map +1 -1
  55. package/dist/openclaw-continuation-bridge.d.ts +7 -0
  56. package/dist/openclaw-continuation-bridge.js +64 -0
  57. package/dist/openclaw-continuation-bridge.js.map +1 -0
  58. package/dist/openclaw-gateway-readiness.d.ts +1 -0
  59. package/dist/openclaw-gateway-readiness.js +24 -0
  60. package/dist/openclaw-gateway-readiness.js.map +1 -0
  61. package/dist/openclaw-generation.d.ts +21 -0
  62. package/dist/openclaw-generation.js +115 -0
  63. package/dist/openclaw-generation.js.map +1 -0
  64. package/dist/openclaw-message-bridge.d.ts +8 -0
  65. package/dist/openclaw-message-bridge.js +93 -0
  66. package/dist/openclaw-message-bridge.js.map +1 -0
  67. package/dist/openclaw-pending.d.ts +8 -0
  68. package/dist/openclaw-pending.js +47 -0
  69. package/dist/openclaw-pending.js.map +1 -0
  70. package/dist/openclaw-plugin.d.ts +22 -4
  71. package/dist/openclaw-plugin.js +1075 -90
  72. package/dist/openclaw-plugin.js.map +1 -1
  73. package/dist/openclaw-session-access.d.ts +12 -0
  74. package/dist/openclaw-session-access.js +79 -0
  75. package/dist/openclaw-session-access.js.map +1 -0
  76. package/dist/program.js +13 -8
  77. package/dist/program.js.map +1 -1
  78. package/dist/session-activation.js +5 -4
  79. package/dist/session-activation.js.map +1 -1
  80. package/dist/session-attendant-probe.d.ts +11 -0
  81. package/dist/session-attendant-probe.js +41 -0
  82. package/dist/session-attendant-probe.js.map +1 -1
  83. package/dist/session-attendant-state.d.ts +5 -0
  84. package/dist/session-attendant-state.js +14 -0
  85. package/dist/session-attendant-state.js.map +1 -1
  86. package/dist/session-attendant.d.ts +6 -0
  87. package/dist/session-attendant.js +12 -0
  88. package/dist/session-attendant.js.map +1 -1
  89. package/dist/session-delivery.d.ts +2 -0
  90. package/dist/session-delivery.js +2 -0
  91. package/dist/session-delivery.js.map +1 -1
  92. package/dist/session-handoff.d.ts +1 -1
  93. package/dist/session-labels.d.ts +2 -0
  94. package/dist/session-labels.js +5 -0
  95. package/dist/session-labels.js.map +1 -1
  96. package/dist/session-message-handoff.d.ts +5 -1
  97. package/dist/session-message-handoff.js +3 -1
  98. package/dist/session-message-handoff.js.map +1 -1
  99. package/dist/skill-source/manifest.json +6 -6
  100. package/dist/skill-source/notifai/SKILL.md +63 -59
  101. package/dist/skill-source/notifai/references/harness-setup.md +67 -18
  102. package/dist/skill-source/notifai/references/notes-and-edits.md +12 -1
  103. package/dist/skill-source/notifai/references/updates.md +7 -6
  104. package/dist/update-handoff.d.ts +1 -1
  105. package/dist/update-handoff.js +2 -1
  106. package/dist/update-handoff.js.map +1 -1
  107. package/package.json +3 -2
@@ -0,0 +1,333 @@
1
+ /**
2
+ * The local classic CLI plugin owns this child for one exact Hermes session.
3
+ * Its pipe is an in-process writer bridge, not a second Hermes connection:
4
+ * the plugin checks its attached CLI's session id again at every injection.
5
+ */
6
+ import { existsSync } from 'node:fs';
7
+ import path from 'node:path';
8
+ import readline from 'node:readline';
9
+ import { log, makeClient } from './commands-core.js';
10
+ import { waitForReply } from './commands-send-support.js';
11
+ import { loadConfig } from './config.js';
12
+ import { hermesPluginCurrent } from './hermes-plugin.js';
13
+ import { acquireClaimFile, claimHolderMayRun, readClaimFile, releaseClaimFile } from './hook-question-lock.js';
14
+ import { beginSessionIncarnation, lifecycleStamp, readSessionIncarnation, readSessionState, refreshSessionMarkers, rotateSessionIncarnation, sessionNotified, } from './hook-session-state.js';
15
+ import { handleSessionEnd, runEscalationWaiter } from './hook-lifecycle.js';
16
+ import { currentProcessIdentity, processStartTime } from './process-identity.js';
17
+ import { projectBinding, projectEnabled } from './project-enablement.js';
18
+ import { runSessionAttendant, systemAttendantClock } from './session-attendant.js';
19
+ import { attendantClaimPath, attendantStatusPath, readAttendantLease, writeAttendantStatus } from './session-attendant-state.js';
20
+ import { handOffSessionMessages } from './session-message-handoff.js';
21
+ const STATE_MAX_AGE_MS = 5_000;
22
+ const INITIAL_FRAME_TIMEOUT_MS = 5_000;
23
+ /** Framed, bounded IPC to the plugin thread that owns PluginContext. */
24
+ export class HermesWriterBridge {
25
+ input;
26
+ output;
27
+ now;
28
+ lines;
29
+ helloValue = null;
30
+ currentSession = null;
31
+ activity = 'idle';
32
+ stateAt = 0;
33
+ closed = false;
34
+ ended = false;
35
+ nextId = 0;
36
+ pending = new Map();
37
+ onHello = null;
38
+ constructor(input, output, now = Date.now) {
39
+ this.input = input;
40
+ this.output = output;
41
+ this.now = now;
42
+ this.lines = readline.createInterface({ input, crlfDelay: Infinity });
43
+ this.lines.on('line', line => this.receive(line));
44
+ this.lines.on('close', () => this.close());
45
+ }
46
+ async hello() {
47
+ if (this.helloValue !== null || this.closed)
48
+ return this.helloValue;
49
+ return new Promise(resolve => {
50
+ const timer = setTimeout(() => {
51
+ this.onHello = null;
52
+ resolve(null);
53
+ }, INITIAL_FRAME_TIMEOUT_MS);
54
+ this.onHello = value => {
55
+ clearTimeout(timer);
56
+ resolve(value);
57
+ };
58
+ });
59
+ }
60
+ receive(line) {
61
+ if (line.length > 256 * 1024 || this.closed)
62
+ return;
63
+ let frame;
64
+ try {
65
+ frame = JSON.parse(line);
66
+ }
67
+ catch {
68
+ return;
69
+ }
70
+ if (frame.type === 'hello' && this.helloValue === null &&
71
+ typeof frame.session_id === 'string' && typeof frame.cwd === 'string' &&
72
+ typeof frame.pid === 'number') {
73
+ this.helloValue = frame;
74
+ this.onHello?.(this.helloValue);
75
+ this.onHello = null;
76
+ return;
77
+ }
78
+ if (this.helloValue === null)
79
+ return;
80
+ if (frame.type === 'state' && typeof frame.session_id === 'string' &&
81
+ (frame.activity === 'idle' || frame.activity === 'working')) {
82
+ this.currentSession = frame.session_id;
83
+ this.activity = frame.activity;
84
+ this.stateAt = this.now();
85
+ }
86
+ else if (frame.type === 'end' && frame.session_id === this.helloValue.session_id) {
87
+ this.ended = true;
88
+ this.currentSession = null;
89
+ for (const settle of this.pending.values())
90
+ settle(null);
91
+ this.pending.clear();
92
+ }
93
+ else if (frame.type === 'result' && typeof frame.id === 'number' &&
94
+ typeof frame.accepted === 'boolean') {
95
+ this.pending.get(frame.id)?.(frame);
96
+ this.pending.delete(frame.id);
97
+ }
98
+ }
99
+ close() {
100
+ if (this.closed)
101
+ return;
102
+ this.closed = true;
103
+ this.onHello?.(null);
104
+ this.onHello = null;
105
+ for (const settle of this.pending.values())
106
+ settle(null);
107
+ this.pending.clear();
108
+ this.lines.close();
109
+ this.input.destroy();
110
+ }
111
+ probe(sessionId) {
112
+ if (this.ended)
113
+ return { state: 'ended', reason: 'session-end-hook' };
114
+ if (this.closed || process.ppid !== this.helloValue?.pid)
115
+ return { state: 'ended', reason: 'harness-gone' };
116
+ if (this.currentSession !== null && this.currentSession !== sessionId) {
117
+ return { state: 'ended', reason: 'session-replaced' };
118
+ }
119
+ if (this.currentSession !== sessionId || this.now() - this.stateAt > STATE_MAX_AGE_MS) {
120
+ return { state: 'uncertain', reason: 'plugin-writer-unconfirmed' };
121
+ }
122
+ return { state: 'running', activity: this.activity };
123
+ }
124
+ async write(sessionId, text, begin, guard) {
125
+ if (this.probe(sessionId).state !== 'running')
126
+ return { status: 'unavailable', reason: 'Hermes plugin writer is not current' };
127
+ if (!begin())
128
+ return { status: 'cancelled' };
129
+ if (!guard.writable())
130
+ return { status: 'aborted', reason: 'claim or lease lapsed before Hermes injection' };
131
+ const budget = Math.floor(Math.min(5_000, guard.remainingMs()));
132
+ if (!Number.isFinite(budget) || budget <= 0) {
133
+ return { status: 'aborted', reason: 'claim or lease lapsed before Hermes injection' };
134
+ }
135
+ const id = ++this.nextId;
136
+ const result = new Promise(resolve => this.pending.set(id, resolve));
137
+ try {
138
+ // The plugin checks this again immediately before calling inject_message.
139
+ // A queued pipe write must not outlive the delivery claim.
140
+ this.output.write(`${JSON.stringify({ type: 'write', id, session_id: sessionId, text,
141
+ deadline_ms: Date.now() + budget })}\n`);
142
+ }
143
+ catch (error) {
144
+ this.pending.delete(id);
145
+ return { status: 'failed', reason: 'Hermes writer pipe failed', error };
146
+ }
147
+ let timer;
148
+ const reply = await Promise.race([
149
+ result,
150
+ new Promise(resolve => { timer = setTimeout(() => resolve(null), budget); }),
151
+ ]);
152
+ if (timer !== undefined)
153
+ clearTimeout(timer);
154
+ this.pending.delete(id);
155
+ if (reply === null)
156
+ return { status: 'failed', reason: 'Hermes injection was not confirmed', error: new Error('writer response missing') };
157
+ if (!reply.accepted)
158
+ return { status: 'aborted', reason: reply.reason ?? 'Hermes rejected the exact session' };
159
+ return { status: 'written', route: 'session-queue' };
160
+ }
161
+ }
162
+ /** The only Hermes answer route: the plugin's attached classic CLI instance. */
163
+ export function hermesAnswerRoute(bridge, sessionId) {
164
+ return {
165
+ kind: 'session-queue',
166
+ deliver: async (event) => {
167
+ if (event.writeGuard === undefined)
168
+ return { acknowledgement: 'held', notes: [] };
169
+ const result = await bridge.write(sessionId, event.context, () => event.commitDelivery(), event.writeGuard);
170
+ if (result.status === 'written')
171
+ return {
172
+ acknowledgement: 'delivered', notes: [],
173
+ log: { route: 'session-queue', stage: 'plugin-injection' },
174
+ };
175
+ return {
176
+ acknowledgement: 'held', notes: [],
177
+ log: { reason: result.status === 'failed' ? 'write-unconfirmed' : result.status === 'aborted' ? 'write-aborted' : result.status },
178
+ };
179
+ },
180
+ };
181
+ }
182
+ function hermesGates(deps, cwd, sessionId) {
183
+ try {
184
+ const config = loadConfig({ cwd, env: deps.env, sessionId });
185
+ if (!projectEnabled(projectBinding(cwd, deps.env, config.project.value))) {
186
+ return { ok: false, reason: 'project-disabled' };
187
+ }
188
+ }
189
+ catch {
190
+ return { ok: false, reason: 'enablement-unavailable' };
191
+ }
192
+ if (!hermesPluginCurrent(deps.hookAdapterHome, deps.env)) {
193
+ return { ok: false, reason: 'hermes-plugin-removed-or-replaced' };
194
+ }
195
+ return { ok: true };
196
+ }
197
+ export async function hermesAttendCommand(deps, input, output) {
198
+ const bridge = new HermesWriterBridge(input, output);
199
+ const hello = await bridge.hello();
200
+ if (hello === null || hello.pid !== process.ppid ||
201
+ hello.session_id === '' || hello.session_id !== deps.env['HERMES_SESSION_ID'] ||
202
+ !path.isAbsolute(hello.cwd) || !existsSync(hello.cwd)) {
203
+ bridge.close();
204
+ return 0;
205
+ }
206
+ const { session_id: sessionId, cwd } = hello;
207
+ const gate = () => hermesGates(deps, cwd, sessionId);
208
+ if (!gate().ok) {
209
+ bridge.close();
210
+ return 0;
211
+ }
212
+ const start = processStartTime(hello.pid);
213
+ const writer = currentProcessIdentity();
214
+ if (start === null || writer === null) {
215
+ bridge.close();
216
+ return 0;
217
+ }
218
+ const logger = log(deps);
219
+ logger.bind({ cmd: 'hook hermes-attend', session: sessionId });
220
+ const claimFile = attendantClaimPath(sessionId, deps.env);
221
+ const record = beginSessionIncarnation(sessionId, deps.env, {
222
+ stamp: lifecycleStamp(), harnessProcess: { pid: hello.pid, start }, clearEarlierEnd: true,
223
+ });
224
+ let token = acquireClaimFile(claimFile, { incarnation: record.incarnation }, Date.now());
225
+ const claimDeadline = systemAttendantClock.monotonic() + 6_000;
226
+ while (token === null && systemAttendantClock.monotonic() < claimDeadline) {
227
+ const holder = readClaimFile(claimFile);
228
+ if (holder?.['incarnation'] === record.incarnation && claimHolderMayRun(holder))
229
+ break;
230
+ await systemAttendantClock.sleep(250, new AbortController().signal);
231
+ token = acquireClaimFile(claimFile, { incarnation: record.incarnation }, Date.now());
232
+ }
233
+ if (token === null) {
234
+ bridge.close();
235
+ return 0;
236
+ }
237
+ let client;
238
+ const connect = () => {
239
+ if (client !== undefined)
240
+ return client;
241
+ const credential = deps.store.load();
242
+ client = credential
243
+ ? makeClient(deps, credential.baseUrl, `Bearer nfm_${credential.machineId}.${credential.secret}`)
244
+ : null;
245
+ return client;
246
+ };
247
+ let observer = null;
248
+ const observeQuestions = () => {
249
+ const probe = bridge.probe(sessionId);
250
+ if (observer !== null || probe.state !== 'running' || probe.activity !== 'idle')
251
+ return;
252
+ const state = readSessionState(sessionId, deps.env);
253
+ if (state.harness !== 'hermes' ||
254
+ ((state.pending?.length ?? 0) === 0 && state.accepted === undefined))
255
+ return;
256
+ const api = connect();
257
+ if (api === null)
258
+ return;
259
+ const now = deps.now ?? Date.now;
260
+ const sleep = deps.sleep ?? ((ms) => new Promise(resolve => setTimeout(resolve, ms)));
261
+ const ctx = {
262
+ client: api, config: loadConfig({ cwd, env: deps.env, sessionId }), env: deps.env,
263
+ now, sleep, harness: 'hermes', log: logger,
264
+ waitForFirstReply: async (requestId, timeoutSeconds) => {
265
+ const result = await waitForReply(api, requestId, { timeoutSeconds, afterSeq: 0, now, sleep });
266
+ return { replies: result.response.replies, timedOut: result.timedOut, degraded: result.degraded };
267
+ },
268
+ answerClaims: {
269
+ lease: () => readAttendantLease(sessionId, deps.env), writer,
270
+ monotonic: () => systemAttendantClock.monotonic(),
271
+ },
272
+ };
273
+ observer = runEscalationWaiter(ctx, {
274
+ sessionId, envelope: { session_id: sessionId, cwd },
275
+ route: hermesAnswerRoute(bridge, sessionId), recordStop: false,
276
+ }).then(() => undefined).catch(err => {
277
+ logger.error('hook.end', { hook: 'hermes-question', outcome: 'failed', reason: String(err) });
278
+ }).finally(() => { observer = null; });
279
+ };
280
+ try {
281
+ await runSessionAttendant({
282
+ sessionId,
283
+ incarnation: record.incarnation,
284
+ incarnationNow: () => readSessionIncarnation(sessionId, deps.env)?.incarnation ?? null,
285
+ rotateIncarnation: expected => {
286
+ const next = rotateSessionIncarnation(sessionId, deps.env, expected);
287
+ if (next === null)
288
+ return null;
289
+ releaseClaimFile(claimFile, token);
290
+ token = acquireClaimFile(claimFile, { incarnation: next.incarnation }, Date.now());
291
+ return token === null ? null : next.incarnation;
292
+ },
293
+ probe: () => bridge.probe(sessionId),
294
+ claimHeld: () => token !== null && readClaimFile(claimFile)?.['token'] === token,
295
+ notified: () => sessionNotified(sessionId, deps.env),
296
+ gates: gate,
297
+ client: connect,
298
+ serverSupportsAttendance: async (api) => (await api.compatibility()).server_capabilities.includes('session_attendance'),
299
+ acceptsMessages: true,
300
+ publishProbeStatus: true,
301
+ onProbe: probe => { if (probe.state === 'running' && probe.activity === 'idle')
302
+ observeQuestions(); },
303
+ onMessages: (messages, attendant) => {
304
+ const api = connect();
305
+ if (api === null)
306
+ return Promise.resolve('done');
307
+ return handOffSessionMessages(messages, attendant, {
308
+ sequencer: {
309
+ sessionId, env: deps.env, client: api,
310
+ monotonic: () => systemAttendantClock.monotonic(),
311
+ wall: () => systemAttendantClock.wall(),
312
+ sleep: ms => systemAttendantClock.sleep(ms, new AbortController().signal),
313
+ writer, log: logger,
314
+ },
315
+ write: (text, begin, guard) => bridge.write(sessionId, text, begin, guard),
316
+ });
317
+ },
318
+ clock: systemAttendantClock,
319
+ logger,
320
+ writeStatus: status => writeAttendantStatus(sessionId, deps.env, status),
321
+ heartbeat: () => refreshSessionMarkers(sessionId, deps.env, Date.now(), [claimFile, attendantStatusPath(sessionId, deps.env)]),
322
+ });
323
+ }
324
+ finally {
325
+ bridge.close();
326
+ handleSessionEnd(deps.env, { session_id: sessionId, cwd }, Date.now(), false);
327
+ await observer;
328
+ if (token !== null)
329
+ releaseClaimFile(claimFile, token);
330
+ }
331
+ return 0;
332
+ }
333
+ //# sourceMappingURL=hermes-attendant.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hermes-attendant.js","sourceRoot":"","sources":["../src/hermes-attendant.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ export declare const HERMES_PLUGIN_ID = "notifai";
2
+ export declare const HERMES_PLUGIN_MARKER = "# notifai managed hermes plugin v1";
3
+ export declare function hermesPluginDir(env?: NodeJS.ProcessEnv): string;
4
+ export declare function hermesPluginSource(adapterPath: string, nodePath?: string): string;
5
+ export declare function isOurHermesPlugin(dir: string): boolean;
6
+ export declare function hermesVersionSupported(env?: NodeJS.ProcessEnv): boolean;
7
+ export declare function hermesPluginListed(env?: NodeJS.ProcessEnv): boolean;
8
+ export declare function preflightHermesPlugin(env?: NodeJS.ProcessEnv): void;
9
+ export declare function installHermesPlugin(adapterPath: string, env?: NodeJS.ProcessEnv, nodePath?: string): string;
10
+ export declare function uninstallHermesPlugin(env?: NodeJS.ProcessEnv): boolean;
11
+ export declare function hermesPluginCurrent(adapterHome: string | undefined, env: NodeJS.ProcessEnv): boolean;
@@ -0,0 +1,299 @@
1
+ import { spawnSync } from 'node:child_process';
2
+ import { existsSync, lstatSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
3
+ import os from 'node:os';
4
+ import path from 'node:path';
5
+ import { pathToFileURL } from 'node:url';
6
+ import { hookAdapterPath } from './hook-adapter.js';
7
+ import { accountHome } from './platform.js';
8
+ export const HERMES_PLUGIN_ID = 'notifai';
9
+ export const HERMES_PLUGIN_MARKER = '# notifai managed hermes plugin v1';
10
+ export function hermesPluginDir(env = process.env) {
11
+ const home = env['HERMES_HOME']?.trim() || path.join(accountHome(env, process.platform), '.hermes');
12
+ return path.join(home, 'plugins', HERMES_PLUGIN_ID);
13
+ }
14
+ export function hermesPluginSource(adapterPath, nodePath) {
15
+ const command = nodePath === undefined ? [adapterPath] : [nodePath, adapterPath];
16
+ return `${HERMES_PLUGIN_MARKER}
17
+ """Notifai activation and Session Attendant for a local Hermes CLI session."""
18
+ import atexit
19
+ import json
20
+ import os
21
+ import select
22
+ import subprocess
23
+ import threading
24
+ import time
25
+
26
+ COMMAND = ${JSON.stringify(command)}
27
+ _attendants = {}
28
+ _lock = threading.RLock()
29
+
30
+ def _attached_session(ctx):
31
+ cli = getattr(ctx._manager, "_cli_ref", None)
32
+ return str(getattr(cli, "session_id", "") or "") if cli is not None else ""
33
+
34
+ def _run_attendant(ctx, session_id, cwd, stopped):
35
+ proc = None
36
+ wall_anchor = time.time_ns()
37
+ mono_anchor = time.monotonic_ns()
38
+ try:
39
+ proc = subprocess.Popen(COMMAND + ["hook", "hermes-attend", "--owner", "notifai", "--harness", "hermes"],
40
+ cwd=cwd, stdin=subprocess.PIPE, stdout=subprocess.PIPE,
41
+ stderr=subprocess.DEVNULL, text=True, bufsize=1)
42
+ with _lock:
43
+ _attendants[session_id] = (stopped, proc)
44
+ def send(frame):
45
+ with _lock:
46
+ if proc.stdin is None or proc.stdin.closed:
47
+ return False
48
+ try:
49
+ proc.stdin.write(json.dumps(frame, ensure_ascii=False) + "\\n")
50
+ proc.stdin.flush()
51
+ return True
52
+ except (OSError, ValueError):
53
+ return False
54
+ if not send({"type": "hello", "session_id": session_id, "cwd": cwd, "pid": os.getpid()}):
55
+ return
56
+ pending = bytearray()
57
+ while not stopped.is_set() and proc.poll() is None:
58
+ with _lock:
59
+ current = _attached_session(ctx)
60
+ working = bool(getattr(ctx._manager._cli_ref, "_agent_running", False)) if current == session_id else False
61
+ if current != session_id:
62
+ break
63
+ if not send({"type": "state", "session_id": session_id,
64
+ "activity": "working" if working else "idle"}):
65
+ break
66
+ readable, _, _ = select.select([proc.stdout], [], [], 1.0)
67
+ if not readable:
68
+ continue
69
+ chunk = os.read(proc.stdout.fileno(), 65536)
70
+ if not chunk:
71
+ break
72
+ pending.extend(chunk)
73
+ if len(pending) > 2 * 1024 * 1024:
74
+ break
75
+ while b"\\n" in pending:
76
+ raw, _, rest = pending.partition(b"\\n")
77
+ pending = bytearray(rest)
78
+ try:
79
+ frame = json.loads(raw.decode("utf-8"))
80
+ except (UnicodeDecodeError, ValueError):
81
+ continue
82
+ if frame.get("type") != "write" or not isinstance(frame.get("id"), int):
83
+ continue
84
+ accepted = False
85
+ with _lock:
86
+ wall_now = time.time_ns()
87
+ mono_now = time.monotonic_ns()
88
+ # Fail closed after a backward wall-clock step. Node's
89
+ # deadline is wall time because its monotonic origin may
90
+ # differ from Python's on the same macOS host.
91
+ clock_stable = wall_now - wall_anchor + 100_000_000 >= mono_now - mono_anchor
92
+ deadline = frame.get("deadline_ms")
93
+ if (not stopped.is_set() and frame.get("session_id") == session_id and
94
+ _attached_session(ctx) == session_id and
95
+ type(deadline) is int and wall_now < deadline * 1_000_000 and clock_stable):
96
+ try:
97
+ accepted = bool(ctx.inject_message(frame.get("text", "")))
98
+ except Exception:
99
+ pass
100
+ send({"type": "result", "id": frame["id"], "accepted": accepted,
101
+ "reason": "session-replaced" if not accepted else ""})
102
+ send({"type": "end", "session_id": session_id})
103
+ except (OSError, ValueError):
104
+ pass
105
+ finally:
106
+ with _lock:
107
+ if _attendants.get(session_id, (None, None))[0] is stopped:
108
+ _attendants.pop(session_id, None)
109
+ if proc is not None:
110
+ try:
111
+ if proc.stdin is not None:
112
+ proc.stdin.close()
113
+ proc.wait(timeout=4)
114
+ except (OSError, subprocess.TimeoutExpired):
115
+ proc.terminate()
116
+ try:
117
+ proc.wait(timeout=2)
118
+ except subprocess.TimeoutExpired:
119
+ proc.kill()
120
+
121
+ def _start_attendant(ctx, session_id, cwd):
122
+ if os.name == "nt":
123
+ return
124
+ with _lock:
125
+ if _attached_session(ctx) != session_id or os.environ.get("HERMES_SESSION_ID") != session_id:
126
+ return
127
+ if session_id in _attendants:
128
+ return
129
+ stopped = threading.Event()
130
+ _attendants[session_id] = (stopped, None)
131
+ threading.Thread(target=_run_attendant, args=(ctx, session_id, cwd, stopped),
132
+ daemon=True, name="notifai-hermes-attendant").start()
133
+
134
+ def _stop_attendant(session_id):
135
+ with _lock:
136
+ item = _attendants.get(session_id)
137
+ if item is None:
138
+ return
139
+ stopped, proc = item
140
+ stopped.set()
141
+ if proc is not None and proc.stdin is not None and not proc.stdin.closed:
142
+ try:
143
+ proc.stdin.write(json.dumps({"type": "end", "session_id": session_id}) + "\\n")
144
+ proc.stdin.flush()
145
+ except (OSError, ValueError):
146
+ pass
147
+
148
+ def _stop_all():
149
+ for session_id in list(_attendants):
150
+ _stop_attendant(session_id)
151
+
152
+ def _activation(info):
153
+ platform = str(info.get("platform") or "")
154
+ if platform not in ("cli", "subagent"):
155
+ return ""
156
+ if os.environ.get("HERMES_SESSION_SOURCE", "").lower() not in ("", "cli"):
157
+ return ""
158
+ # Hermes may import gateway.run inside a classic CLI and set its process
159
+ # marker there. The attached CLI object and exact session are the proof.
160
+ if os.environ.get("HERMES_TUI_ACTIVE_SESSION_FILE"):
161
+ return ""
162
+ if os.environ.get("TERMINAL_ENV", "").lower() not in ("", "local"):
163
+ return ""
164
+ if os.environ.get("HERMES_SESSION_KEY") or os.environ.get("HERMES_SESSION_PLATFORM"):
165
+ return ""
166
+ session_id = str(info.get("session_id") or "").strip()
167
+ if not session_id:
168
+ return ""
169
+ cwd = str(info.get("cwd") or "").strip()
170
+ if not cwd:
171
+ # A set but invalid TERMINAL_CWD cannot fall back to the launch dir.
172
+ if os.environ.get("TERMINAL_CWD", "").strip():
173
+ return ""
174
+ try:
175
+ cwd = os.getcwd()
176
+ except OSError:
177
+ return ""
178
+ if not os.path.isdir(cwd):
179
+ return ""
180
+ worker = platform == "subagent"
181
+ event = "subagent-start" if worker else "session-start"
182
+ envelope = {"session_id": session_id, "cwd": cwd,
183
+ "hook_event_name": "SubagentStart" if worker else "SessionStart"}
184
+ try:
185
+ result = subprocess.run(COMMAND + ["hook", event, "--owner", "notifai", "--harness", "hermes"],
186
+ input=json.dumps(envelope), text=True, capture_output=True,
187
+ timeout=10, check=False)
188
+ except (OSError, subprocess.TimeoutExpired):
189
+ return ""
190
+ content = result.stdout.strip() if result.returncode == 0 else ""
191
+ if content and not worker:
192
+ _start_attendant(_ctx, session_id, cwd)
193
+ return content
194
+
195
+ def register(ctx):
196
+ global _ctx
197
+ _ctx = ctx
198
+ ctx.register_system_prompt_section("notifai.activation", _activation,
199
+ position="after_memory", max_chars=4000)
200
+ ctx.register_hook("on_session_finalize", lambda session_id=None, **_: _stop_attendant(str(session_id or "")))
201
+ ctx.register_hook("on_session_reset", lambda session_id=None, **_: _stop_all())
202
+ atexit.register(_stop_all)
203
+ `;
204
+ }
205
+ export function isOurHermesPlugin(dir) {
206
+ try {
207
+ if (!lstatSync(dir).isDirectory() ||
208
+ !lstatSync(path.join(dir, '__init__.py')).isFile() ||
209
+ !lstatSync(path.join(dir, 'plugin.yaml')).isFile())
210
+ return false;
211
+ return readFileSync(path.join(dir, '__init__.py'), 'utf8').startsWith(HERMES_PLUGIN_MARKER) &&
212
+ /^name: notifai$/m.test(readFileSync(path.join(dir, 'plugin.yaml'), 'utf8'));
213
+ }
214
+ catch {
215
+ return false;
216
+ }
217
+ }
218
+ function hermesCommand(args, env) {
219
+ const result = spawnSync('hermes', ['plugins', ...args], {
220
+ env, encoding: 'utf8', timeout: 30_000, maxBuffer: 256 * 1024,
221
+ });
222
+ if (result.error || result.status !== 0) {
223
+ throw new Error(`Hermes plugin command failed: ${result.error?.message ?? result.stderr?.trim() ?? result.stdout?.trim() ?? 'unknown error'}`);
224
+ }
225
+ return result.stdout;
226
+ }
227
+ export function hermesVersionSupported(env = process.env) {
228
+ const result = spawnSync('hermes', ['--version'], {
229
+ env, encoding: 'utf8', timeout: 10_000, maxBuffer: 64 * 1024,
230
+ });
231
+ return result.status === 0 && /^Hermes Agent v0\.21\.5\b/m.test(result.stdout);
232
+ }
233
+ export function hermesPluginListed(env = process.env) {
234
+ return hermesCommand(['list', '--plain', '--no-bundled'], env)
235
+ .split(/\r?\n/)
236
+ .some(line => /^enabled\s+\S+\s+\S+\s+notifai\s*$/.test(line));
237
+ }
238
+ export function preflightHermesPlugin(env = process.env) {
239
+ const installed = hermesPluginDir(env);
240
+ if (existsSync(installed) && !isOurHermesPlugin(installed)) {
241
+ throw new Error('Hermes already has a foreign plugin named notifai; leave it unchanged');
242
+ }
243
+ if (!hermesVersionSupported(env)) {
244
+ throw new Error('Hermes v0.21.5 is required for the proven Notifai plugin integration');
245
+ }
246
+ if (!existsSync(installed) && hermesCommand(['list', '--plain'], env)
247
+ .split(/\r?\n/)
248
+ .some(line => /^(?:enabled|disabled|not enabled)\s+\S+\s+\S+\s+notifai\s*$/.test(line))) {
249
+ throw new Error('Hermes already discovers a foreign plugin named notifai; leave it unchanged');
250
+ }
251
+ }
252
+ export function installHermesPlugin(adapterPath, env = process.env, nodePath) {
253
+ const installed = hermesPluginDir(env);
254
+ preflightHermesPlugin(env);
255
+ const source = hermesPluginSource(adapterPath, nodePath);
256
+ if (existsSync(installed) && readFileSync(path.join(installed, '__init__.py'), 'utf8') === source) {
257
+ hermesCommand(['enable', HERMES_PLUGIN_ID], env);
258
+ }
259
+ else {
260
+ const temp = mkdtempSync(path.join(os.tmpdir(), 'notifai-hermes-plugin-'));
261
+ try {
262
+ writeFileSync(path.join(temp, 'plugin.yaml'), 'name: notifai\nversion: "1.0.0"\ndescription: "Notifai activation and Session Attendant"\nprovides_hooks:\n - on_session_finalize\n - on_session_reset\n');
263
+ writeFileSync(path.join(temp, '__init__.py'), source);
264
+ const git = (args) => {
265
+ const result = spawnSync('git', args, { cwd: temp, env, encoding: 'utf8', timeout: 10_000 });
266
+ if (result.error || result.status !== 0)
267
+ throw new Error(`Could not prepare Hermes plugin: ${result.error?.message ?? result.stderr?.trim() ?? 'git failed'}`);
268
+ };
269
+ git(['init', '-q']);
270
+ git(['add', 'plugin.yaml', '__init__.py']);
271
+ git(['-c', 'user.name=Notifai', '-c', 'user.email=notifai@example.invalid', 'commit', '-qm', 'plugin']);
272
+ hermesCommand(['install', pathToFileURL(temp).href, '--enable', '--no-deps', ...(existsSync(installed) ? ['--force'] : [])], env);
273
+ }
274
+ finally {
275
+ rmSync(temp, { recursive: true, force: true });
276
+ }
277
+ }
278
+ if (!isOurHermesPlugin(installed) || !hermesPluginListed(env)) {
279
+ throw new Error('Hermes did not report an enabled Notifai plugin after installation');
280
+ }
281
+ return installed;
282
+ }
283
+ export function uninstallHermesPlugin(env = process.env) {
284
+ const installed = hermesPluginDir(env);
285
+ if (!existsSync(installed))
286
+ return false;
287
+ if (!isOurHermesPlugin(installed))
288
+ throw new Error('Hermes plugin named notifai is foreign; leave it unchanged');
289
+ hermesCommand(['remove', HERMES_PLUGIN_ID], env);
290
+ if (existsSync(installed))
291
+ throw new Error('Hermes still has the Notifai plugin after removal');
292
+ return true;
293
+ }
294
+ export function hermesPluginCurrent(adapterHome, env) {
295
+ const dir = hermesPluginDir(env);
296
+ return isOurHermesPlugin(dir) && readFileSync(path.join(dir, '__init__.py'), 'utf8') ===
297
+ hermesPluginSource(hookAdapterPath(adapterHome), process.platform === 'win32' ? process.execPath : undefined);
298
+ }
299
+ //# sourceMappingURL=hermes-plugin.js.map