@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.
- package/CHANGELOG.md +28 -0
- package/README.md +7 -3
- package/dist/codex-tool-messages.d.ts +17 -0
- package/dist/codex-tool-messages.js +142 -0
- package/dist/codex-tool-messages.js.map +1 -0
- package/dist/commands-acknowledge.js +11 -0
- package/dist/commands-acknowledge.js.map +1 -1
- package/dist/commands-ask.js +22 -3
- package/dist/commands-ask.js.map +1 -1
- package/dist/commands-doctor.js +149 -39
- package/dist/commands-doctor.js.map +1 -1
- package/dist/commands-harness-context.js +3 -0
- package/dist/commands-harness-context.js.map +1 -1
- package/dist/commands-hook-attend.d.ts +2 -0
- package/dist/commands-hook-attend.js +87 -22
- package/dist/commands-hook-attend.js.map +1 -1
- package/dist/commands-hook-diagnostics.d.ts +11 -1
- package/dist/commands-hook-diagnostics.js +40 -1
- package/dist/commands-hook-diagnostics.js.map +1 -1
- package/dist/commands-hook-install.js +162 -21
- package/dist/commands-hook-install.js.map +1 -1
- package/dist/commands-hook-run.js +245 -8
- package/dist/commands-hook-run.js.map +1 -1
- package/dist/commands-hook-shape.d.ts +4 -3
- package/dist/commands-hook-shape.js +5 -4
- package/dist/commands-hook-shape.js.map +1 -1
- package/dist/commands-send-support.js +3 -0
- package/dist/commands-send-support.js.map +1 -1
- package/dist/commands-send.js +29 -0
- package/dist/commands-send.js.map +1 -1
- package/dist/harnesses.d.ts +14 -10
- package/dist/harnesses.js +39 -9
- package/dist/harnesses.js.map +1 -1
- package/dist/hermes-attendant.d.ts +38 -0
- package/dist/hermes-attendant.js +333 -0
- package/dist/hermes-attendant.js.map +1 -0
- package/dist/hermes-plugin.d.ts +11 -0
- package/dist/hermes-plugin.js +299 -0
- package/dist/hermes-plugin.js.map +1 -0
- package/dist/hook-events.d.ts +9 -2
- package/dist/hook-events.js +16 -6
- package/dist/hook-events.js.map +1 -1
- package/dist/hook-lifecycle.d.ts +1 -1
- package/dist/hook-lifecycle.js +27 -10
- package/dist/hook-lifecycle.js.map +1 -1
- package/dist/hook-session-state.d.ts +4 -1
- package/dist/hook-session-state.js +16 -4
- package/dist/hook-session-state.js.map +1 -1
- package/dist/hook-types.d.ts +23 -0
- package/dist/install-hooks.js +51 -4
- package/dist/install-hooks.js.map +1 -1
- package/dist/invocation-context.d.ts +1 -0
- package/dist/invocation-context.js +3 -0
- package/dist/invocation-context.js.map +1 -1
- package/dist/openclaw-continuation-bridge.d.ts +7 -0
- package/dist/openclaw-continuation-bridge.js +64 -0
- package/dist/openclaw-continuation-bridge.js.map +1 -0
- package/dist/openclaw-gateway-readiness.d.ts +1 -0
- package/dist/openclaw-gateway-readiness.js +24 -0
- package/dist/openclaw-gateway-readiness.js.map +1 -0
- package/dist/openclaw-generation.d.ts +21 -0
- package/dist/openclaw-generation.js +115 -0
- package/dist/openclaw-generation.js.map +1 -0
- package/dist/openclaw-message-bridge.d.ts +8 -0
- package/dist/openclaw-message-bridge.js +93 -0
- package/dist/openclaw-message-bridge.js.map +1 -0
- package/dist/openclaw-pending.d.ts +8 -0
- package/dist/openclaw-pending.js +47 -0
- package/dist/openclaw-pending.js.map +1 -0
- package/dist/openclaw-plugin.d.ts +22 -4
- package/dist/openclaw-plugin.js +1075 -90
- package/dist/openclaw-plugin.js.map +1 -1
- package/dist/openclaw-session-access.d.ts +12 -0
- package/dist/openclaw-session-access.js +79 -0
- package/dist/openclaw-session-access.js.map +1 -0
- package/dist/program.js +13 -8
- package/dist/program.js.map +1 -1
- package/dist/session-activation.js +5 -4
- package/dist/session-activation.js.map +1 -1
- package/dist/session-attendant-probe.d.ts +11 -0
- package/dist/session-attendant-probe.js +41 -0
- package/dist/session-attendant-probe.js.map +1 -1
- package/dist/session-attendant-state.d.ts +5 -0
- package/dist/session-attendant-state.js +14 -0
- package/dist/session-attendant-state.js.map +1 -1
- package/dist/session-attendant.d.ts +6 -0
- package/dist/session-attendant.js +12 -0
- package/dist/session-attendant.js.map +1 -1
- package/dist/session-delivery.d.ts +2 -0
- package/dist/session-delivery.js +2 -0
- package/dist/session-delivery.js.map +1 -1
- package/dist/session-handoff.d.ts +1 -1
- package/dist/session-labels.d.ts +2 -0
- package/dist/session-labels.js +5 -0
- package/dist/session-labels.js.map +1 -1
- package/dist/session-message-handoff.d.ts +5 -1
- package/dist/session-message-handoff.js +3 -1
- package/dist/session-message-handoff.js.map +1 -1
- package/dist/skill-source/manifest.json +6 -6
- package/dist/skill-source/notifai/SKILL.md +63 -59
- package/dist/skill-source/notifai/references/harness-setup.md +67 -18
- package/dist/skill-source/notifai/references/notes-and-edits.md +12 -1
- package/dist/skill-source/notifai/references/updates.md +7 -6
- package/dist/update-handoff.d.ts +1 -1
- package/dist/update-handoff.js +2 -1
- package/dist/update-handoff.js.map +1 -1
- 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
|