borgmcp 2.6.0 → 2.7.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.
- package/README.md +1 -1
- package/dist/codex-launch.d.ts +2 -2
- package/dist/codex-launch.d.ts.map +1 -1
- package/dist/codex-launch.js +5 -6
- package/dist/codex-launch.js.map +1 -1
- package/dist/log-stream.js +1 -1
- package/dist/log-stream.js.map +1 -1
- package/dist/opencode-drone.d.ts +3 -2
- package/dist/opencode-drone.d.ts.map +1 -1
- package/dist/opencode-drone.js +23 -28
- package/dist/opencode-drone.js.map +1 -1
- package/dist/opencode-wake-copy.js +1 -1
- package/dist/opencode-wake-copy.js.map +1 -1
- package/dist/regen-format.d.ts +4 -6
- package/dist/regen-format.d.ts.map +1 -1
- package/dist/regen-format.js +8 -11
- package/dist/regen-format.js.map +1 -1
- package/dist/repository-cube-init.d.ts.map +1 -1
- package/dist/repository-cube-init.js +21 -14
- package/dist/repository-cube-init.js.map +1 -1
- package/dist/stream-status.js +1 -1
- package/dist/stream-status.js.map +1 -1
- package/dist/tool-manifest.js +2 -2
- package/dist/tool-manifest.js.map +1 -1
- package/docs/EXTRACTION_PROVENANCE.md +3 -3
- package/docs/RELEASING.md +11 -1
- package/package.json +1 -1
- package/src/codex-launch.ts +5 -6
- package/src/log-stream.ts +1 -1
- package/src/opencode-drone.ts +22 -34
- package/src/opencode-wake-copy.ts +1 -1
- package/src/regen-format.ts +8 -11
- package/src/repository-cube-init.ts +24 -16
- package/src/stream-status.ts +1 -1
- package/src/tool-manifest.ts +2 -2
package/src/opencode-drone.ts
CHANGED
|
@@ -67,7 +67,6 @@ export type OpenCodeDeliveryState =
|
|
|
67
67
|
interface OpenCodeDelivery {
|
|
68
68
|
entryId: string;
|
|
69
69
|
text: string;
|
|
70
|
-
messageId: string;
|
|
71
70
|
allowSubmit: boolean;
|
|
72
71
|
state: Exclude<OpenCodeDeliveryState, 'failed'>;
|
|
73
72
|
resolve: (delivered: boolean) => void;
|
|
@@ -212,22 +211,16 @@ async function listSessionMessages(id: string): Promise<OCMessage[]> {
|
|
|
212
211
|
return JSON.parse(body);
|
|
213
212
|
}
|
|
214
213
|
|
|
215
|
-
async function
|
|
216
|
-
sessionId
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
if (status !== 200) {
|
|
224
|
-
throw new Error(`OpenCode message request failed (${status})`);
|
|
225
|
-
}
|
|
226
|
-
const message = JSON.parse(body) as OCMessage;
|
|
227
|
-
if (message.info?.id !== messageId || message.info.role !== 'user') {
|
|
228
|
-
throw new Error('OpenCode returned the wrong injected message');
|
|
214
|
+
async function findInjectedMessage(sessionId: string, text: string): Promise<string | null> {
|
|
215
|
+
const messages = await listSessionMessages(sessionId);
|
|
216
|
+
for (let index = messages.length - 1; index >= 0; index--) {
|
|
217
|
+
const message = messages[index];
|
|
218
|
+
if (message?.info?.role !== 'user' || typeof message.info.id !== 'string') continue;
|
|
219
|
+
if (message.parts?.some((part) => part.type === 'text' && part.text === text)) {
|
|
220
|
+
return message.info.id;
|
|
221
|
+
}
|
|
229
222
|
}
|
|
230
|
-
return
|
|
223
|
+
return null;
|
|
231
224
|
}
|
|
232
225
|
|
|
233
226
|
async function promptSession(id: string, bodyObj: Record<string, unknown>): Promise<number> {
|
|
@@ -443,11 +436,6 @@ async function resolveInjectionSession(): Promise<OCSession | null> {
|
|
|
443
436
|
return bound;
|
|
444
437
|
}
|
|
445
438
|
|
|
446
|
-
function openCodeMessageId(entryId: string): string {
|
|
447
|
-
const digest = createHash('sha256').update(entryId).digest('hex');
|
|
448
|
-
return `msg_borg_${digest}`;
|
|
449
|
-
}
|
|
450
|
-
|
|
451
439
|
function rememberBounded(
|
|
452
440
|
entries: Map<string, string>,
|
|
453
441
|
entryId: string,
|
|
@@ -476,8 +464,10 @@ async function deliverOpenCodeEntry(
|
|
|
476
464
|
let target: OCSession | null = null;
|
|
477
465
|
|
|
478
466
|
// Before the one allowed POST, retries are safe: no submission has happened.
|
|
479
|
-
//
|
|
480
|
-
//
|
|
467
|
+
// OpenCode must generate the message ID: its run loop treats IDs as
|
|
468
|
+
// lexicographically ordered, so arbitrary caller IDs can persist without
|
|
469
|
+
// ever becoming the active user turn. The unique inbox text correlates the
|
|
470
|
+
// generated message across confirmation and process-replay instead.
|
|
481
471
|
for (let attempt = 0; attempt < OPEN_CODE_DELIVERY_RETRY_DELAYS_MS.length; attempt++) {
|
|
482
472
|
if (state !== owner || !owner.connected) return 'failed';
|
|
483
473
|
if (attempt > 0) {
|
|
@@ -498,7 +488,7 @@ async function deliverOpenCodeEntry(
|
|
|
498
488
|
}
|
|
499
489
|
|
|
500
490
|
try {
|
|
501
|
-
if (await
|
|
491
|
+
if (await findInjectedMessage(target.id, delivery.text)) {
|
|
502
492
|
return 'delivered';
|
|
503
493
|
}
|
|
504
494
|
} catch (err) {
|
|
@@ -510,14 +500,12 @@ async function deliverOpenCodeEntry(
|
|
|
510
500
|
continue;
|
|
511
501
|
}
|
|
512
502
|
|
|
513
|
-
//
|
|
514
|
-
//
|
|
515
|
-
// exact message. A transport failure is ambiguous and follows the same
|
|
503
|
+
// prompt_async is not idempotent. Submit at most once, then only poll for
|
|
504
|
+
// the exact text. A transport failure is ambiguous and follows the same
|
|
516
505
|
// confirmation-only path.
|
|
517
506
|
let status: number | null = null;
|
|
518
507
|
try {
|
|
519
508
|
status = await promptSession(target.id, {
|
|
520
|
-
messageID: delivery.messageId,
|
|
521
509
|
parts: [{ type: 'text', text: delivery.text }],
|
|
522
510
|
});
|
|
523
511
|
} catch (err) {
|
|
@@ -543,7 +531,7 @@ async function deliverOpenCodeEntry(
|
|
|
543
531
|
delivery.state = 'delivered-unconfirmed';
|
|
544
532
|
}
|
|
545
533
|
try {
|
|
546
|
-
if (await
|
|
534
|
+
if (await findInjectedMessage(target.id, delivery.text)) {
|
|
547
535
|
return 'delivered';
|
|
548
536
|
}
|
|
549
537
|
} catch (err) {
|
|
@@ -583,7 +571,7 @@ async function processOpenCodeDeliveries(owner: OpenCodeDroneState): Promise<voi
|
|
|
583
571
|
} else if (outcome === 'delivered-unconfirmed') {
|
|
584
572
|
owner.failedEntries.delete(delivery.entryId);
|
|
585
573
|
rememberBounded(owner.unconfirmedEntries, delivery.entryId, delivery.text);
|
|
586
|
-
delivery.resolve(
|
|
574
|
+
delivery.resolve(false);
|
|
587
575
|
} else {
|
|
588
576
|
owner.unconfirmedEntries.delete(delivery.entryId);
|
|
589
577
|
rememberBounded(owner.failedEntries, delivery.entryId, delivery.text);
|
|
@@ -642,8 +630,9 @@ export async function injectInitialKickoff(launch: OpenCodeLaunchKickoff): Promi
|
|
|
642
630
|
|
|
643
631
|
/**
|
|
644
632
|
* Queue one durable inbox entry for delivery into the bound OpenCode session.
|
|
645
|
-
* The
|
|
646
|
-
* can confirm an earlier ambiguous submission without
|
|
633
|
+
* The durable inbox text identifies the OpenCode-generated user message, so
|
|
634
|
+
* retries and replay can confirm an earlier ambiguous submission without
|
|
635
|
+
* supplying an ordering-breaking caller message ID or running it twice.
|
|
647
636
|
*/
|
|
648
637
|
export function injectOpenCodeEntry(
|
|
649
638
|
text: string,
|
|
@@ -670,7 +659,7 @@ export function injectOpenCodeEntry(
|
|
|
670
659
|
rememberBounded(owner.failedEntries, entryId, text);
|
|
671
660
|
return Promise.resolve(false);
|
|
672
661
|
}
|
|
673
|
-
return Promise.resolve(
|
|
662
|
+
return Promise.resolve(false);
|
|
674
663
|
}
|
|
675
664
|
|
|
676
665
|
const active = owner.activeDeliveries.get(entryId);
|
|
@@ -690,7 +679,6 @@ export function injectOpenCodeEntry(
|
|
|
690
679
|
const delivery: OpenCodeDelivery = {
|
|
691
680
|
entryId,
|
|
692
681
|
text,
|
|
693
|
-
messageId: openCodeMessageId(entryId),
|
|
694
682
|
allowSubmit,
|
|
695
683
|
state: 'queued',
|
|
696
684
|
resolve: resolveDelivery,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export const OPENCODE_WAKE_PATH_GUIDANCE =
|
|
2
2
|
'OpenCode wakes through HTTP entry injection into its session through the local HTTP API. ' +
|
|
3
3
|
'Borg writes each entry to the durable inbox before one injection attempt and ' +
|
|
4
|
-
'confirms delivery by
|
|
4
|
+
'confirms delivery by the unique persisted entry text; no inbox Monitor or secondary wake loop is used. ' +
|
|
5
5
|
'If injection is rejected or cannot be ' +
|
|
6
6
|
'confirmed, the durable entry remains available and `borg_stream-status` reports ' +
|
|
7
7
|
'it as failed or delivered-unconfirmed; run `borg_read-log unread_only=true` and ' +
|
package/src/regen-format.ts
CHANGED
|
@@ -27,9 +27,8 @@ import { OPENCODE_WAKE_PATH_GUIDANCE } from './opencode-wake-copy.js';
|
|
|
27
27
|
* one of `startup` / `resume` / `clear` / `compact`. The `borg-regen`
|
|
28
28
|
* SessionStart hook uses this to detect a `/clear` re-orientation, which is
|
|
29
29
|
* the FIRST time the hook is the SOLE orientation path (the launch kickoff
|
|
30
|
-
* prompt is gone)
|
|
31
|
-
*
|
|
32
|
-
* an operational re-arm.
|
|
30
|
+
* prompt is gone), so the re-injected orientation must instruct an operational
|
|
31
|
+
* Monitor re-arm.
|
|
33
32
|
*
|
|
34
33
|
* Best-effort + total: empty input (manual / TTY run with no stdin),
|
|
35
34
|
* malformed JSON, a missing `source`, or a non-string `source` all return
|
|
@@ -60,9 +59,8 @@ export type AgentKind = 'claude' | 'codex' | 'opencode';
|
|
|
60
59
|
*
|
|
61
60
|
* Agent-branched on the existing env-agnostic signal (BORG_SESSION-style
|
|
62
61
|
* `isCodexRemoteWakeEnabled`), NOT on a mutable server-recorded field:
|
|
63
|
-
* - claude: arm the inbox-file tail Monitor
|
|
64
|
-
*
|
|
65
|
-
* healthy or indeterminate; short only while explicitly broken).
|
|
62
|
+
* - claude: arm the inbox-file tail Monitor, drain unread entries on every
|
|
63
|
+
* wake, and re-arm the Monitor after an exit or whenever none is armed.
|
|
66
64
|
* - codex: Borg's activity stream reaches the app-server remote-control inbox
|
|
67
65
|
* channel; each wake is followed by an unread-log drain. Manual full regen
|
|
68
66
|
* + drain is a degraded fallback when remote control is unavailable.
|
|
@@ -98,9 +96,8 @@ export function wakePathArming(
|
|
|
98
96
|
return [
|
|
99
97
|
'Arm your wake path before working:',
|
|
100
98
|
`1. **Inbox Monitor** (wake path) — run a persistent Monitor on \`${monitorCommand}\` so cube posts wake you in real time.`,
|
|
101
|
-
'2. **
|
|
102
|
-
'3. **
|
|
103
|
-
'4. **Recovery tick** — drain `borg_read-log unread_only=true`. If empty, check wake status, set deadline, resume prior work. No full-regen or liveness post on empty; safety probes may still wake.',
|
|
99
|
+
'2. **On every wake** — drain `borg_read-log unread_only=true`. If empty, resume prior work without a full regen or liveness post; safety probes may still wake.',
|
|
100
|
+
'3. **Monitor recovery** — re-arm the Monitor when its exit notification wakes you, and whenever you notice no Monitor is armed.',
|
|
104
101
|
].join('\n');
|
|
105
102
|
}
|
|
106
103
|
|
|
@@ -165,8 +162,8 @@ export function formatLeanOrientation(args: {
|
|
|
165
162
|
? '\n_(`/clear` cleared your conversation; Codex remote-control wake remains active. Follow the required Codex wake path below.)_\n'
|
|
166
163
|
: agentKind === 'claude'
|
|
167
164
|
? [
|
|
168
|
-
'\n_(`/clear` cleared Claude\'s
|
|
169
|
-
'_Quiet-clear fallback: if a later turn follows silence, inspect `borg_stream-status` + `borg_roster`; call `borg_regen mode="full"`, drain `borg_read-log unread_only=true`, then re-arm
|
|
165
|
+
'\n_(`/clear` cleared Claude\'s conversation — re-arm the inbox Monitor now.)_',
|
|
166
|
+
'_Quiet-clear fallback: if a later turn follows silence, inspect `borg_stream-status` + `borg_roster`; call `borg_regen mode="full"`, drain `borg_read-log unread_only=true`, then re-arm the Monitor._\n',
|
|
170
167
|
].join('\n')
|
|
171
168
|
: ''
|
|
172
169
|
: '';
|
|
@@ -158,7 +158,7 @@ function renderResult(
|
|
|
158
158
|
`Next: borg assimilate --host ${shellEscape(input.serverOrigin)}`,
|
|
159
159
|
);
|
|
160
160
|
} else {
|
|
161
|
-
lines.push('Continuing with role and seat setup
|
|
161
|
+
lines.push('Continuing with role and seat setup…');
|
|
162
162
|
}
|
|
163
163
|
resultWriter(`${lines.join('\n')}\n`);
|
|
164
164
|
}
|
|
@@ -176,6 +176,7 @@ export async function initializeRepositoryCube(input: {
|
|
|
176
176
|
input.flags.template !== undefined || input.flags.noTemplate === true || input.flags.yes === true;
|
|
177
177
|
const creationOptionsUnused = association !== null && creationOptionsRequested;
|
|
178
178
|
if (association) {
|
|
179
|
+
deps.write(`Checking for this repository's cube on ${input.serverOrigin}…\n`);
|
|
179
180
|
const cube = await deps.getCube(association.cubeId);
|
|
180
181
|
const response: CreateCubeResponse = {
|
|
181
182
|
result: 'resolved',
|
|
@@ -241,6 +242,7 @@ export async function initializeRepositoryCube(input: {
|
|
|
241
242
|
return { kind: 'success', creation: { response, cube }, existing: true };
|
|
242
243
|
};
|
|
243
244
|
|
|
245
|
+
deps.write(`Checking for this repository's cube on ${input.serverOrigin}…\n`);
|
|
244
246
|
const serverAssociation = await deps.resolveAssociation(repository, input.context.derivedName);
|
|
245
247
|
if (serverAssociation.result === 'resolved') {
|
|
246
248
|
return saveResolvedAssociation(serverAssociation, {
|
|
@@ -263,9 +265,14 @@ export async function initializeRepositoryCube(input: {
|
|
|
263
265
|
return { kind: 'stop', code: 1 };
|
|
264
266
|
}
|
|
265
267
|
if (!deps.isTTY() || input.flags.yes) {
|
|
268
|
+
const retryCommand = input.mode === 'cube-init'
|
|
269
|
+
? `borg server cube init --host ${shellEscape(input.serverOrigin)}`
|
|
270
|
+
: `borg assimilate --host ${shellEscape(input.serverOrigin)}`;
|
|
266
271
|
deps.write(
|
|
267
|
-
|
|
268
|
-
'
|
|
272
|
+
`Found existing cube '${matches[0].name}' on ${input.serverOrigin}.\n` +
|
|
273
|
+
'Linking a repository to an existing cube requires one interactive confirmation.\n' +
|
|
274
|
+
`Run ${retryCommand} --cube-name ${shellEscape(matches[0].name)} once in an interactive terminal to link it; scripted runs work from then on.\n` +
|
|
275
|
+
'No cube, repository binding, or drone was created.\n',
|
|
269
276
|
);
|
|
270
277
|
return { kind: 'stop', code: 1 };
|
|
271
278
|
}
|
|
@@ -276,16 +283,17 @@ export async function initializeRepositoryCube(input: {
|
|
|
276
283
|
` server: ${input.serverOrigin}\n`,
|
|
277
284
|
);
|
|
278
285
|
while (true) {
|
|
279
|
-
const answer = await ask(deps, 'Link this repository to that cube? [
|
|
286
|
+
const answer = await ask(deps, 'Link this repository to that cube? [Y/n]: ', 'adoption');
|
|
280
287
|
if ('stop' in answer) return { kind: 'stop', code: answer.stop };
|
|
281
288
|
const confirmation = answer.value.trim().toLowerCase();
|
|
282
|
-
if (confirmation === 'y' || confirmation === 'yes') break;
|
|
283
|
-
if (confirmation === '
|
|
289
|
+
if (confirmation === '' || confirmation === 'y' || confirmation === 'yes') break;
|
|
290
|
+
if (confirmation === 'n' || confirmation === 'no') {
|
|
284
291
|
deps.write('No cube, repository binding, or drone was created.\n');
|
|
285
292
|
return { kind: 'stop', code: 0 };
|
|
286
293
|
}
|
|
287
294
|
deps.write('Enter y or n.\n');
|
|
288
295
|
}
|
|
296
|
+
deps.write(`Linking this repository to cube '${matches[0].name}'…\n`);
|
|
289
297
|
const associated = await deps.associateCube({
|
|
290
298
|
cubeId: matches[0].id,
|
|
291
299
|
workingRepoName: input.context.derivedName,
|
|
@@ -299,7 +307,11 @@ export async function initializeRepositoryCube(input: {
|
|
|
299
307
|
if (confirmed.result !== 'resolved' || confirmed.cube_id !== associated.cube_id) {
|
|
300
308
|
throw new RepositoryAssociationConfirmationError();
|
|
301
309
|
}
|
|
302
|
-
return await saveResolvedAssociation(confirmed, {
|
|
310
|
+
return await saveResolvedAssociation(confirmed, {
|
|
311
|
+
adopted: true,
|
|
312
|
+
creationOptionsUnused: input.flags.template !== undefined ||
|
|
313
|
+
input.flags.noTemplate === true,
|
|
314
|
+
});
|
|
303
315
|
} catch (error) {
|
|
304
316
|
if (error instanceof RepositoryAssociationSaveError) throw error;
|
|
305
317
|
throw new RepositoryAssociationConfirmationError();
|
|
@@ -380,16 +392,12 @@ export async function initializeRepositoryCube(input: {
|
|
|
380
392
|
template ??= 'software-dev';
|
|
381
393
|
|
|
382
394
|
if (deps.isTTY() && !input.flags.yes) {
|
|
383
|
-
deps.write(
|
|
384
|
-
`Create this cube?\n` +
|
|
385
|
-
` Name: ${name}\n` +
|
|
386
|
-
` Template: ${presentation(template).label}\n` +
|
|
387
|
-
` Repository: ${input.context.root}\n` +
|
|
388
|
-
` Server: ${input.serverOrigin}\n`,
|
|
389
|
-
);
|
|
390
395
|
let confirmed = false;
|
|
391
396
|
while (!confirmed) {
|
|
392
|
-
const answer = await ask(
|
|
397
|
+
const answer = await ask(
|
|
398
|
+
deps,
|
|
399
|
+
`Create cube '${name}' (${presentation(template).label}) on ${input.serverOrigin}? [Y/n]: `,
|
|
400
|
+
);
|
|
393
401
|
if ('stop' in answer) return { kind: 'stop', code: answer.stop };
|
|
394
402
|
const confirmation = answer.value.trim().toLowerCase();
|
|
395
403
|
if (confirmation === '' || confirmation === 'y' || confirmation === 'yes') {
|
|
@@ -404,7 +412,7 @@ export async function initializeRepositoryCube(input: {
|
|
|
404
412
|
}
|
|
405
413
|
}
|
|
406
414
|
|
|
407
|
-
deps.write(
|
|
415
|
+
deps.write(`Creating cube '${name}'…\n`);
|
|
408
416
|
const workingRepoName = input.context.derivedName;
|
|
409
417
|
let creation: RepositoryCubeCreation;
|
|
410
418
|
try {
|
package/src/stream-status.ts
CHANGED
|
@@ -385,7 +385,7 @@ export function formatWakePathPrefix(inputs: {
|
|
|
385
385
|
return [
|
|
386
386
|
`## ⚠ Wake path broken — arm Monitor NOW`,
|
|
387
387
|
``,
|
|
388
|
-
`No process is tailing this drone's inbox file. SSE delivery is healthy (entries reach disk), but Claude Code has no event source to wake on. Until you arm a Monitor, this session
|
|
388
|
+
`No process is tailing this drone's inbox file. SSE delivery is healthy (entries reach disk), but Claude Code has no event source to wake on. Until you arm a Monitor, this session has no wake path and will miss live coordination from other drones:`,
|
|
389
389
|
``,
|
|
390
390
|
`> Monitor command: \`${monitorCommand(inboxPath, monitorStateRoot)}\` — persistent, 1h timeout, description "borg inbox for ${droneLabel} on cube ${cubeName}".`,
|
|
391
391
|
``,
|
package/src/tool-manifest.ts
CHANGED
|
@@ -172,7 +172,7 @@ export const TOOL_MANIFEST: ToolManifestEntry[] = [
|
|
|
172
172
|
{
|
|
173
173
|
name: 'borg_roster',
|
|
174
174
|
description:
|
|
175
|
-
"List all currently connected drones in your cube, with each drone's label, role, and last-seen time. Optional `since` argument adds a sender-side liveness column — pass either an activity_log entry id (e.g., from a dispatch you posted) or an ISO-8601 timestamp; each drone is marked `awake` if they've posted a log entry after that point, otherwise `stale-since-X`. Useful for confirming a dispatch reached its named recipients (catches the silent-wake-path-failure class where SSE delivered but the drone's
|
|
175
|
+
"List all currently connected drones in your cube, with each drone's label, role, and last-seen time. Optional `since` argument adds a sender-side liveness column — pass either an activity_log entry id (e.g., from a dispatch you posted) or an ISO-8601 timestamp; each drone is marked `awake` if they've posted a log entry after that point, otherwise `stale-since-X`. Useful for confirming a dispatch reached its named recipients (catches the silent-wake-path-failure class where SSE delivered but the drone's inbox Monitor never woke it).",
|
|
176
176
|
inputSchema: {
|
|
177
177
|
type: 'object',
|
|
178
178
|
properties: {
|
|
@@ -188,7 +188,7 @@ export const TOOL_MANIFEST: ToolManifestEntry[] = [
|
|
|
188
188
|
{
|
|
189
189
|
name: 'borg_stream-status',
|
|
190
190
|
description:
|
|
191
|
-
"Diagnostic probe of the local SSE log-stream consumer: returns `connected`, `lastContentEventAt`, `lastWireActivityAt`, `lastHeartbeatAt`, `lastPersistedEventId`, `reconnectAttempts`, plus a wake-path check that flags if SSE is attached but no inbox
|
|
191
|
+
"Diagnostic probe of the local SSE log-stream consumer: returns `connected`, `lastContentEventAt`, `lastWireActivityAt`, `lastHeartbeatAt`, `lastPersistedEventId`, `reconnectAttempts`, plus a wake-path check that flags if SSE is attached but no inbox Monitor is watching the file (the silent failure where incoming entries reach disk but do not wake the drone). Read-only in-process state; does NOT re-open the stream. Use when troubleshooting wake-ups or verifying the stream is alive.",
|
|
192
192
|
inputSchema: {
|
|
193
193
|
type: 'object',
|
|
194
194
|
properties: {},
|