@mono-agent/agent-app 0.11.5 → 0.13.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 +54 -17
- package/dist/adapter-send-tools.d.ts.map +1 -1
- package/dist/adapter-send-tools.js +126 -34
- package/dist/adapter-send-tools.js.map +1 -1
- package/dist/app-controller-lifecycle.d.ts.map +1 -1
- package/dist/app-controller-lifecycle.js +2 -6
- package/dist/app-controller-lifecycle.js.map +1 -1
- package/dist/app-controller-maintenance.d.ts.map +1 -1
- package/dist/app-controller-maintenance.js +2 -1
- package/dist/app-controller-maintenance.js.map +1 -1
- package/dist/app-controller-memory-health.d.ts +1 -1
- package/dist/app-controller-memory-health.d.ts.map +1 -1
- package/dist/app-controller-memory-health.js +5 -1
- package/dist/app-controller-memory-health.js.map +1 -1
- package/dist/app-controller-responder.d.ts.map +1 -1
- package/dist/app-controller-responder.js +8 -3
- package/dist/app-controller-responder.js.map +1 -1
- package/dist/app-controller-traceability.d.ts.map +1 -1
- package/dist/app-controller-traceability.js +15 -1
- package/dist/app-controller-traceability.js.map +1 -1
- package/dist/app-controller.d.ts +14 -1
- package/dist/app-controller.d.ts.map +1 -1
- package/dist/app-controller.js +61 -9
- package/dist/app-controller.js.map +1 -1
- package/dist/audit-runs.d.ts.map +1 -1
- package/dist/audit-runs.js +12 -2
- package/dist/audit-runs.js.map +1 -1
- package/dist/background-runtime.d.ts +26 -0
- package/dist/background-runtime.d.ts.map +1 -1
- package/dist/background-runtime.js +874 -46
- package/dist/background-runtime.js.map +1 -1
- package/dist/background.d.ts +12 -2
- package/dist/background.d.ts.map +1 -1
- package/dist/background.js +195 -40
- package/dist/background.js.map +1 -1
- package/dist/channel-drivers/live.d.ts +3 -2
- package/dist/channel-drivers/live.d.ts.map +1 -1
- package/dist/channel-drivers/live.js +3 -2
- package/dist/channel-drivers/live.js.map +1 -1
- package/dist/channel-drivers/slack.d.ts.map +1 -1
- package/dist/channel-drivers/slack.js +6 -0
- package/dist/channel-drivers/slack.js.map +1 -1
- package/dist/channel-drivers/telegram.d.ts.map +1 -1
- package/dist/channel-drivers/telegram.js +20 -4
- package/dist/channel-drivers/telegram.js.map +1 -1
- package/dist/channel-drivers/tui.d.ts.map +1 -1
- package/dist/channel-drivers/tui.js +24 -0
- package/dist/channel-drivers/tui.js.map +1 -1
- package/dist/channel-runtime-controls.d.ts +23 -0
- package/dist/channel-runtime-controls.d.ts.map +1 -0
- package/dist/channel-runtime-controls.js +63 -0
- package/dist/channel-runtime-controls.js.map +1 -0
- package/dist/channel-status-display.d.ts +15 -0
- package/dist/channel-status-display.d.ts.map +1 -0
- package/dist/channel-status-display.js +48 -0
- package/dist/channel-status-display.js.map +1 -0
- package/dist/cli-args.d.ts +19 -22
- package/dist/cli-args.d.ts.map +1 -1
- package/dist/cli-args.js +115 -68
- package/dist/cli-args.js.map +1 -1
- package/dist/cli-background-command.d.ts +1 -0
- package/dist/cli-background-command.d.ts.map +1 -1
- package/dist/cli-background-command.js +72 -16
- package/dist/cli-background-command.js.map +1 -1
- package/dist/cli-commands.d.ts +4 -2
- package/dist/cli-commands.d.ts.map +1 -1
- package/dist/cli-commands.js +51 -47
- package/dist/cli-commands.js.map +1 -1
- package/dist/cli-help.d.ts +19 -1
- package/dist/cli-help.d.ts.map +1 -1
- package/dist/cli-help.js +217 -70
- package/dist/cli-help.js.map +1 -1
- package/dist/cli-init-command.d.ts.map +1 -1
- package/dist/cli-init-command.js +4 -18
- package/dist/cli-init-command.js.map +1 -1
- package/dist/cli-install-skill-command.d.ts.map +1 -1
- package/dist/cli-install-skill-command.js +32 -3
- package/dist/cli-install-skill-command.js.map +1 -1
- package/dist/cli-runs-command.d.ts +13 -0
- package/dist/cli-runs-command.d.ts.map +1 -0
- package/dist/cli-runs-command.js +70 -0
- package/dist/cli-runs-command.js.map +1 -0
- package/dist/cli-sandbox-command.d.ts +1 -1
- package/dist/cli-sandbox-command.d.ts.map +1 -1
- package/dist/cli-sandbox-command.js +14 -2
- package/dist/cli-sandbox-command.js.map +1 -1
- package/dist/cli-validate-config-command.d.ts +24 -5
- package/dist/cli-validate-config-command.d.ts.map +1 -1
- package/dist/cli-validate-config-command.js +120 -41
- package/dist/cli-validate-config-command.js.map +1 -1
- package/dist/config-reference.d.ts +1 -1
- package/dist/config-reference.d.ts.map +1 -1
- package/dist/config-reference.js +63 -1
- package/dist/config-reference.js.map +1 -1
- package/dist/configured-agent.js +3 -1
- package/dist/configured-agent.js.map +1 -1
- package/dist/docs-mcp-pairing.d.ts +35 -0
- package/dist/docs-mcp-pairing.d.ts.map +1 -0
- package/dist/docs-mcp-pairing.js +311 -0
- package/dist/docs-mcp-pairing.js.map +1 -0
- package/dist/doctor.d.ts +6 -0
- package/dist/doctor.d.ts.map +1 -1
- package/dist/doctor.js +6 -5
- package/dist/doctor.js.map +1 -1
- package/dist/install-skill.d.ts +2 -0
- package/dist/install-skill.d.ts.map +1 -1
- package/dist/install-skill.js +10 -4
- package/dist/install-skill.js.map +1 -1
- package/dist/interaction-bridge.d.ts +2 -0
- package/dist/interaction-bridge.d.ts.map +1 -1
- package/dist/interaction-bridge.js +46 -1
- package/dist/interaction-bridge.js.map +1 -1
- package/dist/launchd.d.ts +18 -0
- package/dist/launchd.d.ts.map +1 -1
- package/dist/launchd.js +69 -0
- package/dist/launchd.js.map +1 -1
- package/dist/local-configuration.d.ts +2 -2
- package/dist/local-configuration.d.ts.map +1 -1
- package/dist/local-configuration.js +10 -10
- package/dist/local-configuration.js.map +1 -1
- package/dist/managed-runtime-publication.d.ts +22 -0
- package/dist/managed-runtime-publication.d.ts.map +1 -0
- package/dist/managed-runtime-publication.js +91 -0
- package/dist/managed-runtime-publication.js.map +1 -0
- package/dist/managed-startup.d.ts +8 -0
- package/dist/managed-startup.d.ts.map +1 -0
- package/dist/managed-startup.js +12 -0
- package/dist/managed-startup.js.map +1 -0
- package/dist/memory-retrieval.d.ts.map +1 -1
- package/dist/memory-retrieval.js +31 -18
- package/dist/memory-retrieval.js.map +1 -1
- package/dist/metrics.d.ts.map +1 -1
- package/dist/metrics.js +12 -2
- package/dist/metrics.js.map +1 -1
- package/dist/modules/known-tools.d.ts +2 -2
- package/dist/modules/known-tools.d.ts.map +1 -1
- package/dist/modules/known-tools.js +2 -1
- package/dist/modules/known-tools.js.map +1 -1
- package/dist/notify-runtime.d.ts +26 -0
- package/dist/notify-runtime.d.ts.map +1 -0
- package/dist/notify-runtime.js +27 -0
- package/dist/notify-runtime.js.map +1 -0
- package/dist/notify-tool.d.ts +51 -0
- package/dist/notify-tool.d.ts.map +1 -0
- package/dist/notify-tool.js +182 -0
- package/dist/notify-tool.js.map +1 -0
- package/dist/posted-reply-history.d.ts.map +1 -1
- package/dist/posted-reply-history.js +7 -0
- package/dist/posted-reply-history.js.map +1 -1
- package/dist/project-skills.d.ts +1 -1
- package/dist/project-skills.d.ts.map +1 -1
- package/dist/project-skills.js +9 -7
- package/dist/project-skills.js.map +1 -1
- package/dist/recipes/base.d.ts +15 -0
- package/dist/recipes/base.d.ts.map +1 -0
- package/dist/recipes/base.js +51 -0
- package/dist/recipes/base.js.map +1 -0
- package/dist/recipes/catalog.d.ts +4 -0
- package/dist/recipes/catalog.d.ts.map +1 -0
- package/dist/recipes/catalog.js +525 -0
- package/dist/recipes/catalog.js.map +1 -0
- package/dist/recipes/index.d.ts +11 -0
- package/dist/recipes/index.d.ts.map +1 -0
- package/dist/recipes/index.js +14 -0
- package/dist/recipes/index.js.map +1 -0
- package/dist/recipes/types.d.ts +70 -0
- package/dist/recipes/types.d.ts.map +1 -0
- package/dist/recipes/types.js +15 -0
- package/dist/recipes/types.js.map +1 -0
- package/dist/request-model-override.d.ts +3 -2
- package/dist/request-model-override.d.ts.map +1 -1
- package/dist/request-model-override.js +13 -7
- package/dist/request-model-override.js.map +1 -1
- package/dist/run-history.d.ts.map +1 -1
- package/dist/run-history.js +35 -22
- package/dist/run-history.js.map +1 -1
- package/dist/runtime-provenance.d.ts.map +1 -1
- package/dist/runtime-provenance.js +10 -5
- package/dist/runtime-provenance.js.map +1 -1
- package/dist/sessions-command.d.ts +53 -0
- package/dist/sessions-command.d.ts.map +1 -0
- package/dist/sessions-command.js +309 -0
- package/dist/sessions-command.js.map +1 -0
- package/dist/setup.d.ts +29 -0
- package/dist/setup.d.ts.map +1 -0
- package/dist/setup.js +97 -0
- package/dist/setup.js.map +1 -0
- package/dist/tui-command.d.ts +1 -1
- package/dist/tui-command.d.ts.map +1 -1
- package/dist/tui-command.js +6 -5
- package/dist/tui-command.js.map +1 -1
- package/dist/web-command.d.ts +109 -43
- package/dist/web-command.d.ts.map +1 -1
- package/dist/web-command.js +1108 -237
- package/dist/web-command.js.map +1 -1
- package/dist/wizard/from-flags.d.ts +0 -1
- package/dist/wizard/from-flags.d.ts.map +1 -1
- package/dist/wizard/from-flags.js +1 -8
- package/dist/wizard/from-flags.js.map +1 -1
- package/dist/wizard/index.d.ts +1 -1
- package/dist/wizard/index.d.ts.map +1 -1
- package/dist/wizard/index.js +1 -1
- package/dist/wizard/index.js.map +1 -1
- package/dist/wizard/presets.d.ts +3 -12
- package/dist/wizard/presets.d.ts.map +1 -1
- package/dist/wizard/presets.js +3 -15
- package/dist/wizard/presets.js.map +1 -1
- package/dist/wizard/prompts.d.ts.map +1 -1
- package/dist/wizard/prompts.js +2 -1
- package/dist/wizard/prompts.js.map +1 -1
- package/dist/wizard/run.d.ts.map +1 -1
- package/dist/wizard/run.js +9 -9
- package/dist/wizard/run.js.map +1 -1
- package/package.json +15 -15
- package/schema/mono-agent.config.schema.json +70 -2
- package/skills/mono-agent-composer/SKILL.md +14 -6
- package/skills/mono-agent-composer/agents/openai.yaml +1 -1
- package/skills/mono-agent-composer/references/config-blueprint.md +30 -9
- package/skills/mono-agent-composer/references/discovery-questions.md +10 -5
- package/skills/mono-agent-composer/references/feature-coverage.md +14 -12
- package/skills/mono-agent-composer/references/package-map.md +10 -3
- package/skills/mono-agent-composer/references/playbooks.md +8 -8
- package/skills/mono-agent-composer/references/validation.md +4 -2
package/dist/background.js
CHANGED
|
@@ -7,12 +7,15 @@ import process from "node:process";
|
|
|
7
7
|
import { listRecordedRuns, listTraceSources } from "@mono-agent/observability";
|
|
8
8
|
import { describeSensitiveDataExportWarning, phoenixAppBaseUrl, resolveAppTraceRegistryDir, resolveAppTraceStaleAfterMs, } from "./app-config.js";
|
|
9
9
|
import { formatChannelFactValue } from "./channel-fact-format.js";
|
|
10
|
+
import { formatHumanChannelSections } from "./channel-status-display.js";
|
|
11
|
+
import { hasCompletedManagedStartup } from "./managed-startup.js";
|
|
10
12
|
import { bootout, bootstrap, buildLaunchdMaintenancePlistXml, buildPlistXml, defaultPathEnv, deriveLaunchdLabel, deriveLaunchdMaintenanceLabel, isLoaded, launchdServiceInfo, launchdPathsFor, MANAGED_LAUNCHD_LOG_MAINTENANCE_ENV, makeLaunchctlRunner, } from "./launchd.js";
|
|
11
13
|
import { selectBackgroundOperationalEnvironment } from "./background-environment.js";
|
|
12
14
|
import { beginLaunchdLogMaintenanceIntent, clearLaunchdLogMaintenanceIntent, inspectLaunchdLogs, LAUNCHD_LOG_MAINTENANCE_INTERVAL_SECONDS, markLaunchdLogMaintenanceRestoring, markLaunchdLogMaintenanceStopped, markLaunchdLogMaintenanceStopping, readLaunchdLogMaintenanceIntent, rotateStoppedLaunchdLogs, } from "./launchd-logs.js";
|
|
13
15
|
import { ensureManagedBackgroundRuntime, MANAGED_BACKGROUND_WORKER_ENV, } from "./background-runtime.js";
|
|
14
16
|
import { backgroundSnapshotFromMetadata, captureBackgroundSnapshot, encodeBackgroundSnapshot, sameBackgroundSnapshot, } from "./background-snapshot.js";
|
|
15
17
|
import { resolveConfiguredManagedRuntimePackages } from "./managed-runtime-packages.js";
|
|
18
|
+
import { acquireManagedRuntimePublicationBarrier } from "./managed-runtime-publication.js";
|
|
16
19
|
import { processIncarnationFromJson, } from "./process-incarnation.js";
|
|
17
20
|
import { acquireOwnerPrivateLock, validateOwnerPrivateLockInputs } from "./owner-private-lock.js";
|
|
18
21
|
import { buildRunsHealthDisplay, RUNS_HEALTH_MAX_RUNS } from "./runs-health.js";
|
|
@@ -146,6 +149,10 @@ export function defaultBackgroundDeps() {
|
|
|
146
149
|
env: target.configurationEnvironment,
|
|
147
150
|
}),
|
|
148
151
|
acquireLifecycleLock: acquireFilesystemLifecycleLock,
|
|
152
|
+
acquireRuntimePublicationBarrier: (target) => acquireManagedRuntimePublicationBarrier({
|
|
153
|
+
label: target.label,
|
|
154
|
+
managedRoot: dirname(target.paths.logDir),
|
|
155
|
+
}),
|
|
149
156
|
probeTui: probeTuiEndpoint,
|
|
150
157
|
stdout: (text) => void process.stdout.write(text),
|
|
151
158
|
stderr: (text) => void process.stderr.write(text),
|
|
@@ -156,12 +163,13 @@ export function defaultBackgroundDeps() {
|
|
|
156
163
|
}),
|
|
157
164
|
};
|
|
158
165
|
}
|
|
159
|
-
const
|
|
160
|
-
|
|
166
|
+
const DEFAULT_CONTROL_POLL = { timeoutMs: 18_000, intervalMs: 400 };
|
|
167
|
+
const DEFAULT_READINESS_POLL = { timeoutMs: 60_000, intervalMs: 400 };
|
|
168
|
+
export async function startBackground(target, deps, poll) {
|
|
161
169
|
return (await ensureBackgroundReady(target, deps, poll)).ok ? 0 : 1;
|
|
162
170
|
}
|
|
163
171
|
/** Restart is behaviourally identical: ensure a single fresh running instance. */
|
|
164
|
-
export async function restartBackground(target, deps, poll
|
|
172
|
+
export async function restartBackground(target, deps, poll) {
|
|
165
173
|
return (await ensureBackgroundReady(target, deps, poll)).ok ? 0 : 1;
|
|
166
174
|
}
|
|
167
175
|
/**
|
|
@@ -169,7 +177,7 @@ export async function restartBackground(target, deps, poll = DEFAULT_POLL) {
|
|
|
169
177
|
* retaining one lifecycle lock. This closes the force-restart gap in which a
|
|
170
178
|
* concurrent start could previously enter while the session store was purged.
|
|
171
179
|
*/
|
|
172
|
-
export async function forceRestartBackground(target, deps, whileStopped, poll
|
|
180
|
+
export async function forceRestartBackground(target, deps, whileStopped, poll) {
|
|
173
181
|
const release = await deps.acquireLifecycleLock(target);
|
|
174
182
|
if (release === undefined) {
|
|
175
183
|
deps.stderr(ui.errorLine(`Another lifecycle command is already active for ${target.label}.`));
|
|
@@ -177,11 +185,12 @@ export async function forceRestartBackground(target, deps, whileStopped, poll =
|
|
|
177
185
|
return 1;
|
|
178
186
|
}
|
|
179
187
|
try {
|
|
180
|
-
const
|
|
188
|
+
const controlPoll = poll ?? DEFAULT_CONTROL_POLL;
|
|
189
|
+
const stopCode = await stopBackgroundUnlocked(target, deps, controlPoll);
|
|
181
190
|
if (stopCode !== 0)
|
|
182
191
|
return stopCode;
|
|
183
192
|
await whileStopped();
|
|
184
|
-
return (await ensureBackgroundReadyUnlocked(target, deps, poll)).ok ? 0 : 1;
|
|
193
|
+
return (await ensureBackgroundReadyUnlocked(target, deps, controlPoll, poll ?? DEFAULT_READINESS_POLL)).ok ? 0 : 1;
|
|
185
194
|
}
|
|
186
195
|
finally {
|
|
187
196
|
await release().catch((error) => {
|
|
@@ -195,7 +204,7 @@ export async function forceRestartBackground(target, deps, whileStopped, poll =
|
|
|
195
204
|
* intentionally stopped service; the sole exception is recovery authorized by
|
|
196
205
|
* its own durable lifecycle intent after a prior maintainer died post-bootout.
|
|
197
206
|
*/
|
|
198
|
-
export async function maintainLaunchdLogs(target, deps, poll =
|
|
207
|
+
export async function maintainLaunchdLogs(target, deps, poll = DEFAULT_CONTROL_POLL) {
|
|
199
208
|
const release = await deps.acquireLifecycleLock(target);
|
|
200
209
|
if (release === undefined) {
|
|
201
210
|
// Controller start/restart/stop owns the same lock. Skipping this periodic
|
|
@@ -398,11 +407,11 @@ function maintenanceErrorMessage(error) {
|
|
|
398
407
|
}
|
|
399
408
|
/**
|
|
400
409
|
* Ensure the canonical per-config LaunchAgent is running and return the fresh
|
|
401
|
-
* trace source that proved it
|
|
410
|
+
* trace source that proved it completed the full startup lifecycle. This is the shared
|
|
402
411
|
* lifecycle boundary for CLI start/restart and remote configuration handoffs;
|
|
403
412
|
* callers must not open a console when the result is not ok.
|
|
404
413
|
*/
|
|
405
|
-
export async function ensureBackgroundReady(target, deps, poll
|
|
414
|
+
export async function ensureBackgroundReady(target, deps, poll) {
|
|
406
415
|
const release = await deps.acquireLifecycleLock(target);
|
|
407
416
|
if (release === undefined) {
|
|
408
417
|
deps.stderr(ui.errorLine(`Another lifecycle command is already active for ${target.label}.`));
|
|
@@ -410,7 +419,7 @@ export async function ensureBackgroundReady(target, deps, poll = DEFAULT_POLL) {
|
|
|
410
419
|
return { ok: false, action: "start", reason: "ownership" };
|
|
411
420
|
}
|
|
412
421
|
try {
|
|
413
|
-
return await ensureBackgroundReadyUnlocked(target, deps, poll);
|
|
422
|
+
return await ensureBackgroundReadyUnlocked(target, deps, poll ?? DEFAULT_CONTROL_POLL, poll ?? DEFAULT_READINESS_POLL);
|
|
414
423
|
}
|
|
415
424
|
finally {
|
|
416
425
|
await release().catch((error) => {
|
|
@@ -418,18 +427,58 @@ export async function ensureBackgroundReady(target, deps, poll = DEFAULT_POLL) {
|
|
|
418
427
|
});
|
|
419
428
|
}
|
|
420
429
|
}
|
|
421
|
-
async function ensureBackgroundReadyUnlocked(target, deps,
|
|
430
|
+
async function ensureBackgroundReadyUnlocked(target, deps, controlPoll, readinessPoll) {
|
|
422
431
|
if (target.expectedSnapshot === undefined) {
|
|
423
432
|
deps.stderr(ui.errorLine("Refusing to launch a managed worker without an approved background snapshot."));
|
|
424
433
|
deps.stderr(ui.hint("No LaunchAgent changes were made. Retry from the wizard or `mono-agent start`."));
|
|
425
434
|
return { ok: false, action: "start", reason: "snapshot" };
|
|
426
435
|
}
|
|
427
|
-
const uid = deps.getuid();
|
|
428
436
|
if (!(await snapshotStillMatches(target, deps))) {
|
|
429
437
|
reportSnapshotDrift(target, deps);
|
|
430
438
|
return { ok: false, action: "start", reason: "snapshot" };
|
|
431
439
|
}
|
|
440
|
+
let publicationBarrier;
|
|
441
|
+
try {
|
|
442
|
+
publicationBarrier = await (deps.acquireRuntimePublicationBarrier
|
|
443
|
+
?? ((input) => acquireManagedRuntimePublicationBarrier({
|
|
444
|
+
label: input.label,
|
|
445
|
+
managedRoot: dirname(input.paths.logDir),
|
|
446
|
+
})))(target);
|
|
447
|
+
}
|
|
448
|
+
catch (error) {
|
|
449
|
+
reportLifecycleException(target, deps, "establish the managed runtime publication barrier", error);
|
|
450
|
+
return { ok: false, action: "start", reason: "ownership" };
|
|
451
|
+
}
|
|
452
|
+
if (publicationBarrier === undefined) {
|
|
453
|
+
deps.stderr(ui.errorLine(`Another runtime publication is already active for ${target.label}.`));
|
|
454
|
+
return { ok: false, action: "start", reason: "ownership" };
|
|
455
|
+
}
|
|
456
|
+
return await ensureBackgroundReadyWithPublicationBarrier(target, deps, controlPoll, readinessPoll, publicationBarrier);
|
|
457
|
+
}
|
|
458
|
+
async function ensureBackgroundReadyWithPublicationBarrier(target, deps, controlPoll, readinessPoll, publicationBarrier) {
|
|
459
|
+
let barrierReleased = false;
|
|
460
|
+
const releaseBarrier = async () => {
|
|
461
|
+
if (barrierReleased)
|
|
462
|
+
return;
|
|
463
|
+
await publicationBarrier.release();
|
|
464
|
+
barrierReleased = true;
|
|
465
|
+
};
|
|
466
|
+
try {
|
|
467
|
+
return await ensureBackgroundReadyAfterPublicationBarrier(target, deps, controlPoll, readinessPoll, releaseBarrier);
|
|
468
|
+
}
|
|
469
|
+
finally {
|
|
470
|
+
if (!barrierReleased) {
|
|
471
|
+
await releaseBarrier().catch((error) => {
|
|
472
|
+
deps.stderr(ui.errorLine(`Could not release runtime publication barrier for ${target.label}: ${error instanceof Error ? error.message : String(error)}`));
|
|
473
|
+
});
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
async function ensureBackgroundReadyAfterPublicationBarrier(target, deps, controlPoll, readinessPoll, releaseBarrier) {
|
|
478
|
+
const uid = deps.getuid();
|
|
432
479
|
let launchTarget;
|
|
480
|
+
const runtimeStartedAt = deps.now();
|
|
481
|
+
deps.stdout(ui.hint("Verifying the durable managed runtime…"));
|
|
433
482
|
try {
|
|
434
483
|
const additionalPackages = await deps.resolveManagedRuntimePackages?.(target) ?? [];
|
|
435
484
|
const runtime = await deps.ensureManagedRuntime({
|
|
@@ -441,7 +490,10 @@ async function ensureBackgroundReadyUnlocked(target, deps, poll) {
|
|
|
441
490
|
...target,
|
|
442
491
|
cliPath: runtime.cliPath,
|
|
443
492
|
nodePath: runtime.nodePath,
|
|
493
|
+
managedRuntimeLaunchProof: runtime.launchProof,
|
|
444
494
|
};
|
|
495
|
+
deps.stdout(ui.hint(`Managed runtime ready (${runtimeVerificationLabel(runtime.verificationMode)}, `
|
|
496
|
+
+ `${formatLifecycleDuration(deps.now() - runtimeStartedAt)}).`));
|
|
445
497
|
}
|
|
446
498
|
catch (error) {
|
|
447
499
|
reportLifecycleException(target, deps, "install and verify the durable managed runtime", error);
|
|
@@ -475,6 +527,7 @@ async function ensureBackgroundReadyUnlocked(target, deps, poll) {
|
|
|
475
527
|
}
|
|
476
528
|
let outcome;
|
|
477
529
|
let prepared = false;
|
|
530
|
+
deps.stdout(ui.hint("Replacing the managed worker…"));
|
|
478
531
|
try {
|
|
479
532
|
let interruptedMaintenance = await deps.readLaunchdLogMaintenanceIntent(launchTarget.paths);
|
|
480
533
|
if (interruptedMaintenance?.phase === "stopping" || interruptedMaintenance?.phase === "restoring") {
|
|
@@ -483,7 +536,7 @@ async function ensureBackgroundReadyUnlocked(target, deps, poll) {
|
|
|
483
536
|
throw new Error("Interrupted launchd-log maintenance lost its writer PID before durable stopped-writer proof; refusing restart.");
|
|
484
537
|
}
|
|
485
538
|
}
|
|
486
|
-
outcome = await bootstrapOrRestart(launchTarget, deps, uid,
|
|
539
|
+
outcome = await bootstrapOrRestart(launchTarget, deps, uid, controlPoll, async () => {
|
|
487
540
|
const stoppedIntent = interruptedMaintenance;
|
|
488
541
|
if (stoppedIntent?.phase === "stopped") {
|
|
489
542
|
interruptedMaintenance = await deps.markLaunchdLogMaintenanceStopping(launchTarget.paths, stoppedIntent);
|
|
@@ -504,6 +557,9 @@ async function ensureBackgroundReadyUnlocked(target, deps, poll) {
|
|
|
504
557
|
await writePlists(launchTarget, deps);
|
|
505
558
|
sinceMs = deps.now();
|
|
506
559
|
prepared = true;
|
|
560
|
+
// The replacement plist now contains the finalized-runtime proof. Let
|
|
561
|
+
// launchd respawn only after the stopped-window commit is complete.
|
|
562
|
+
await releaseBarrier();
|
|
507
563
|
});
|
|
508
564
|
}
|
|
509
565
|
catch (error) {
|
|
@@ -519,18 +575,20 @@ async function ensureBackgroundReadyUnlocked(target, deps, poll) {
|
|
|
519
575
|
let ready;
|
|
520
576
|
try {
|
|
521
577
|
ready = await pollInstanceReady(launchTarget, deps, {
|
|
522
|
-
...
|
|
578
|
+
...readinessPoll,
|
|
523
579
|
sinceMs,
|
|
524
580
|
...(launchTarget.requireTui === true ? { requireTui: true } : {}),
|
|
525
581
|
});
|
|
526
582
|
}
|
|
527
583
|
catch (error) {
|
|
528
|
-
|
|
584
|
+
reportReadinessException(deps, error);
|
|
585
|
+
const stopped = await cleanUpUnreadyBackground(launchTarget, deps, controlPoll);
|
|
586
|
+
reportReadinessCleanup(launchTarget, deps, stopped);
|
|
529
587
|
return { ok: false, action, reason: "readiness" };
|
|
530
588
|
}
|
|
531
589
|
if (ready === undefined) {
|
|
532
590
|
if (!(await snapshotStillMatches(launchTarget, deps))) {
|
|
533
|
-
const stopped = await stopBackgroundUnlocked(launchTarget, deps,
|
|
591
|
+
const stopped = await stopBackgroundUnlocked(launchTarget, deps, controlPoll);
|
|
534
592
|
reportSnapshotDrift(launchTarget, deps);
|
|
535
593
|
if (stopped === 0) {
|
|
536
594
|
deps.stderr(ui.style.dim("The drifted LaunchAgent was stopped before returning control.") + "\n");
|
|
@@ -540,13 +598,27 @@ async function ensureBackgroundReadyUnlocked(target, deps, poll) {
|
|
|
540
598
|
}
|
|
541
599
|
return { ok: false, action, reason: "snapshot" };
|
|
542
600
|
}
|
|
543
|
-
reportTimeout(
|
|
601
|
+
reportTimeout(deps);
|
|
602
|
+
const stopped = await cleanUpUnreadyBackground(launchTarget, deps, controlPoll);
|
|
603
|
+
reportReadinessCleanup(launchTarget, deps, stopped);
|
|
544
604
|
return { ok: false, action, reason: "timeout" };
|
|
545
605
|
}
|
|
546
606
|
const completedAction = outcome.restarted ? "restarted" : "started";
|
|
547
607
|
printInstanceInfo(ready, launchTarget, deps, completedAction);
|
|
548
608
|
return { ok: true, action: completedAction, source: ready };
|
|
549
609
|
}
|
|
610
|
+
function runtimeVerificationLabel(mode) {
|
|
611
|
+
switch (mode) {
|
|
612
|
+
case "fast-reuse": return "warm reuse";
|
|
613
|
+
case "full-reuse": return "full verification";
|
|
614
|
+
case "installed": return "installed";
|
|
615
|
+
case "repaired": return "repaired";
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
function formatLifecycleDuration(milliseconds) {
|
|
619
|
+
const safe = Math.max(0, milliseconds);
|
|
620
|
+
return safe < 1_000 ? `${Math.round(safe)} ms` : `${(safe / 1_000).toFixed(1)} s`;
|
|
621
|
+
}
|
|
550
622
|
async function prepareLaunchdDirectories(target, deps) {
|
|
551
623
|
// launchd will not create the log file's parent directory, so make both dirs
|
|
552
624
|
// before writing the plist that references them.
|
|
@@ -558,6 +630,9 @@ async function writePlists(target, deps) {
|
|
|
558
630
|
if (target.expectedSnapshot === undefined) {
|
|
559
631
|
throw new Error("A managed LaunchAgent requires an approved background snapshot.");
|
|
560
632
|
}
|
|
633
|
+
if (target.managedRuntimeLaunchProof === undefined) {
|
|
634
|
+
throw new Error("A managed LaunchAgent requires a finalized runtime launch proof.");
|
|
635
|
+
}
|
|
561
636
|
const maintenance = maintenancePathsForTarget(target);
|
|
562
637
|
const mainXml = buildPlistXml({
|
|
563
638
|
label: target.label,
|
|
@@ -567,6 +642,7 @@ async function writePlists(target, deps) {
|
|
|
567
642
|
cwd: target.cwd,
|
|
568
643
|
...(target.envFile === undefined ? {} : { envFile: target.envFile }),
|
|
569
644
|
expectedBackgroundSnapshot: encodeBackgroundSnapshot(target.expectedSnapshot),
|
|
645
|
+
expectedManagedRuntimeLaunch: target.managedRuntimeLaunchProof,
|
|
570
646
|
stdoutPath: target.paths.stdoutPath,
|
|
571
647
|
stderrPath: target.paths.stderrPath,
|
|
572
648
|
environment: target.environment,
|
|
@@ -586,7 +662,7 @@ async function writePlists(target, deps) {
|
|
|
586
662
|
await deps.writeFile(target.paths.plistPath, mainXml);
|
|
587
663
|
await deps.writeFile(maintenance.plistPath, maintenanceXml);
|
|
588
664
|
}
|
|
589
|
-
async function ensureOwnerPrivateLaunchdDirectory(path) {
|
|
665
|
+
export async function ensureOwnerPrivateLaunchdDirectory(path) {
|
|
590
666
|
const parent = dirname(path);
|
|
591
667
|
const parentDetails = await lstat(parent);
|
|
592
668
|
assertOwnerDirectory(parentDetails, parent, "LaunchAgent parent");
|
|
@@ -624,7 +700,7 @@ async function ensureOwnerPrivateLaunchdDirectory(path) {
|
|
|
624
700
|
await handle.close();
|
|
625
701
|
}
|
|
626
702
|
}
|
|
627
|
-
async function writeOwnerPrivateLaunchdFile(path, data) {
|
|
703
|
+
export async function writeOwnerPrivateLaunchdFile(path, data) {
|
|
628
704
|
let existing;
|
|
629
705
|
try {
|
|
630
706
|
existing = await lstat(path);
|
|
@@ -913,7 +989,7 @@ function lifecycleFailure(result, detail) {
|
|
|
913
989
|
function uniquePids(values) {
|
|
914
990
|
return [...new Set(values.filter((value) => value !== undefined && value > 0))];
|
|
915
991
|
}
|
|
916
|
-
export async function stopBackground(target, deps, poll =
|
|
992
|
+
export async function stopBackground(target, deps, poll = DEFAULT_CONTROL_POLL) {
|
|
917
993
|
const release = await deps.acquireLifecycleLock(target);
|
|
918
994
|
if (release === undefined) {
|
|
919
995
|
deps.stderr(ui.errorLine(`Another lifecycle command is already active for ${target.label}.`));
|
|
@@ -1015,13 +1091,27 @@ async function stopBackgroundUnlocked(target, deps, poll) {
|
|
|
1015
1091
|
deps.stdout(ui.keyValue([["config", target.configPath]]));
|
|
1016
1092
|
return 0;
|
|
1017
1093
|
}
|
|
1018
|
-
export async function statusBackground(target, deps) {
|
|
1094
|
+
export async function statusBackground(target, deps, options = {}) {
|
|
1019
1095
|
const result = await deps.listTraceSources({ registryDir: target.registryDir, staleAfterMs: target.staleAfterMs });
|
|
1020
1096
|
const classified = await Promise.all(result.sources.map(async (source) => ({
|
|
1021
1097
|
source,
|
|
1022
1098
|
matches: await matchesConfig(source, target.configPath),
|
|
1023
1099
|
})));
|
|
1024
1100
|
const current = classified.find((entry) => entry.matches)?.source;
|
|
1101
|
+
// Only surface other instances that are live or crashed — cleanly stopped
|
|
1102
|
+
// manifests linger in the registry and would just be noise.
|
|
1103
|
+
const others = classified
|
|
1104
|
+
.filter((entry) => !entry.matches && entry.source.health !== "stopped")
|
|
1105
|
+
.map((entry) => entry.source);
|
|
1106
|
+
if (options.json === true) {
|
|
1107
|
+
const instance = current === undefined ? null : await assembleInstanceStatus(current, target, deps);
|
|
1108
|
+
deps.stdout(`${JSON.stringify({
|
|
1109
|
+
ok: current?.health === "running",
|
|
1110
|
+
instance,
|
|
1111
|
+
others: others.map(assembleOtherInstanceStatus),
|
|
1112
|
+
})}\n`);
|
|
1113
|
+
return current?.health === "running" ? 0 : 1;
|
|
1114
|
+
}
|
|
1025
1115
|
if (current === undefined) {
|
|
1026
1116
|
deps.stdout(ui.style.dim(`No running mono-agent instance for ${target.configPath}.`) + "\n");
|
|
1027
1117
|
deps.stdout(ui.hint(`Start it with: mono-agent start${commandFlags(target)}`));
|
|
@@ -1030,11 +1120,6 @@ export async function statusBackground(target, deps) {
|
|
|
1030
1120
|
writeInstanceDetail(current, target, deps);
|
|
1031
1121
|
await writeRunsHealthDetail(current, deps);
|
|
1032
1122
|
}
|
|
1033
|
-
// Only surface other instances that are live or crashed — cleanly stopped
|
|
1034
|
-
// manifests linger in the registry and would just be noise.
|
|
1035
|
-
const others = classified
|
|
1036
|
-
.filter((entry) => !entry.matches && entry.source.health !== "stopped")
|
|
1037
|
-
.map((entry) => entry.source);
|
|
1038
1123
|
if (others.length > 0) {
|
|
1039
1124
|
deps.stdout("\n" + ui.rule("Other mono-agent instances"));
|
|
1040
1125
|
for (const source of others) {
|
|
@@ -1043,6 +1128,62 @@ export async function statusBackground(target, deps) {
|
|
|
1043
1128
|
}
|
|
1044
1129
|
return current?.health === "running" ? 0 : 1;
|
|
1045
1130
|
}
|
|
1131
|
+
/**
|
|
1132
|
+
* The machine-readable instance record for `status --json`. It mirrors the
|
|
1133
|
+
* fields the human `writeInstanceDetail`/`writeRunsHealthDetail` render, reading
|
|
1134
|
+
* only the already-computed trace-source manifest (content-free, safe to publish)
|
|
1135
|
+
* and the same runs-health probe the human path performs — no extra probes.
|
|
1136
|
+
*/
|
|
1137
|
+
async function assembleInstanceStatus(source, target, deps) {
|
|
1138
|
+
const metadata = source.metadata ?? {};
|
|
1139
|
+
const observability = metadata.observability;
|
|
1140
|
+
const sandbox = metadata.sandbox;
|
|
1141
|
+
const session = metadata.session;
|
|
1142
|
+
const channels = metadata.channels;
|
|
1143
|
+
return {
|
|
1144
|
+
sourceId: source.sourceId,
|
|
1145
|
+
label: source.label,
|
|
1146
|
+
launchdLabel: target.label,
|
|
1147
|
+
pid: source.pid ?? null,
|
|
1148
|
+
health: source.health,
|
|
1149
|
+
status: source.status,
|
|
1150
|
+
configPath: target.configPath,
|
|
1151
|
+
startedAt: source.startedAt,
|
|
1152
|
+
updatedAt: source.updatedAt,
|
|
1153
|
+
...(source.artifactDir === undefined ? {} : { artifactDir: source.artifactDir }),
|
|
1154
|
+
...(source.transports === undefined ? {} : { transports: source.transports }),
|
|
1155
|
+
logs: { stdout: target.paths.stdoutPath, stderr: target.paths.stderrPath },
|
|
1156
|
+
...(isPlainRecord(observability) ? { observability } : {}),
|
|
1157
|
+
...(isPlainRecord(sandbox) ? { sandbox } : {}),
|
|
1158
|
+
...(isPlainRecord(session) ? { session } : {}),
|
|
1159
|
+
...(isPlainRecord(channels) ? { channels } : {}),
|
|
1160
|
+
...(source.memoryHealth === undefined ? {} : { memoryHealth: source.memoryHealth }),
|
|
1161
|
+
runsHealth: await assembleRunsHealthStatus(source, deps),
|
|
1162
|
+
};
|
|
1163
|
+
}
|
|
1164
|
+
async function assembleRunsHealthStatus(source, deps) {
|
|
1165
|
+
if (source.artifactDir === undefined || source.artifactDir.trim().length === 0) {
|
|
1166
|
+
return null;
|
|
1167
|
+
}
|
|
1168
|
+
const { totalRuns, warnings } = await deps.listRecordedRuns({
|
|
1169
|
+
artifactDir: source.artifactDir,
|
|
1170
|
+
maxRuns: RUNS_HEALTH_MAX_RUNS,
|
|
1171
|
+
scope: "agent",
|
|
1172
|
+
});
|
|
1173
|
+
return { totalRuns, warnings };
|
|
1174
|
+
}
|
|
1175
|
+
function assembleOtherInstanceStatus(source) {
|
|
1176
|
+
return {
|
|
1177
|
+
sourceId: source.sourceId,
|
|
1178
|
+
label: source.label,
|
|
1179
|
+
health: source.health,
|
|
1180
|
+
pid: source.pid ?? null,
|
|
1181
|
+
...(source.configPath === undefined ? {} : { configPath: source.configPath }),
|
|
1182
|
+
};
|
|
1183
|
+
}
|
|
1184
|
+
function isPlainRecord(value) {
|
|
1185
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
1186
|
+
}
|
|
1046
1187
|
export async function tailLogs(target, deps, options) {
|
|
1047
1188
|
const args = [
|
|
1048
1189
|
"-n",
|
|
@@ -1146,9 +1287,27 @@ function reportLifecycleException(target, deps, operation, error) {
|
|
|
1146
1287
|
deps.stderr(ui.style.dim("The committed agent files were preserved.") + "\n");
|
|
1147
1288
|
reportLaunchRecovery(target, deps);
|
|
1148
1289
|
}
|
|
1149
|
-
function
|
|
1290
|
+
function reportReadinessException(deps, error) {
|
|
1291
|
+
deps.stderr(ui.errorLine(`Failed to read the worker readiness trace: ${error instanceof Error ? error.message : String(error)}`));
|
|
1292
|
+
deps.stderr(ui.style.dim("The committed agent files were preserved.") + "\n");
|
|
1293
|
+
}
|
|
1294
|
+
function reportTimeout(deps) {
|
|
1150
1295
|
deps.stderr(ui.errorLine("mono-agent did not report ready within the timeout."));
|
|
1151
|
-
deps.stderr(ui.style.dim("The committed agent files were preserved.
|
|
1296
|
+
deps.stderr(ui.style.dim("The committed agent files were preserved.") + "\n");
|
|
1297
|
+
}
|
|
1298
|
+
async function cleanUpUnreadyBackground(target, deps, poll) {
|
|
1299
|
+
try {
|
|
1300
|
+
return await stopBackgroundUnlocked(target, { ...deps, stdout: () => undefined }, poll) === 0;
|
|
1301
|
+
}
|
|
1302
|
+
catch (error) {
|
|
1303
|
+
deps.stderr(ui.errorLine(`Failed while trying to stop the unready LaunchAgent: ${error instanceof Error ? error.message : String(error)}`));
|
|
1304
|
+
return false;
|
|
1305
|
+
}
|
|
1306
|
+
}
|
|
1307
|
+
function reportReadinessCleanup(target, deps, stopped) {
|
|
1308
|
+
deps.stderr(stopped
|
|
1309
|
+
? ui.style.dim("The unready LaunchAgent and scheduled maintenance were stopped and their definitions removed before returning control.") + "\n"
|
|
1310
|
+
: ui.style.yellow("The unready LaunchAgent could not be proven stopped; a worker or maintenance helper may still be running.") + "\n");
|
|
1152
1311
|
reportLaunchRecovery(target, deps);
|
|
1153
1312
|
}
|
|
1154
1313
|
function reportLaunchRecovery(target, deps) {
|
|
@@ -1190,10 +1349,10 @@ function writeInstanceDetail(source, target, deps) {
|
|
|
1190
1349
|
deps.stdout(` ${line}\n`);
|
|
1191
1350
|
}
|
|
1192
1351
|
}
|
|
1193
|
-
const
|
|
1194
|
-
|
|
1195
|
-
deps.stdout(ui.rule(
|
|
1196
|
-
for (const line of
|
|
1352
|
+
const channelSections = formatChannels(source);
|
|
1353
|
+
for (const section of channelSections) {
|
|
1354
|
+
deps.stdout(ui.rule(section.title));
|
|
1355
|
+
for (const line of section.lines) {
|
|
1197
1356
|
deps.stdout(`${line}\n`);
|
|
1198
1357
|
}
|
|
1199
1358
|
}
|
|
@@ -1247,7 +1406,7 @@ async function matchesConfig(source, configPath) {
|
|
|
1247
1406
|
return await canonicalBackgroundConfigPath(process.cwd(), source.configPath) === configPath;
|
|
1248
1407
|
}
|
|
1249
1408
|
function isReady(source, requireTui) {
|
|
1250
|
-
if (source.health !== "running" ||
|
|
1409
|
+
if (source.health !== "running" || !hasCompletedManagedStartup(source))
|
|
1251
1410
|
return false;
|
|
1252
1411
|
if (source.memoryHealth?.status === "unhealthy")
|
|
1253
1412
|
return false;
|
|
@@ -1268,10 +1427,6 @@ function channelRecords(source) {
|
|
|
1268
1427
|
return Object.values(channels)
|
|
1269
1428
|
.filter((value) => value !== null && typeof value === "object");
|
|
1270
1429
|
}
|
|
1271
|
-
function metadataReason(source) {
|
|
1272
|
-
const reason = source.metadata?.reason;
|
|
1273
|
-
return typeof reason === "string" ? reason : undefined;
|
|
1274
|
-
}
|
|
1275
1430
|
function startedAtMs(source) {
|
|
1276
1431
|
const parsed = Date.parse(source.startedAt);
|
|
1277
1432
|
return Number.isFinite(parsed) ? parsed : 0;
|
|
@@ -1539,10 +1694,10 @@ function formatChannels(source) {
|
|
|
1539
1694
|
if (channels === null || typeof channels !== "object") {
|
|
1540
1695
|
return [];
|
|
1541
1696
|
}
|
|
1542
|
-
return Object.entries(channels).map(([id, value]) => {
|
|
1697
|
+
return formatHumanChannelSections(Object.entries(channels).map(([id, value]) => {
|
|
1543
1698
|
const { kind, text } = describeChannel(value);
|
|
1544
|
-
return
|
|
1545
|
-
});
|
|
1699
|
+
return { id, kind, text };
|
|
1700
|
+
}));
|
|
1546
1701
|
}
|
|
1547
1702
|
export function describeChannel(value) {
|
|
1548
1703
|
if (value === null || typeof value !== "object") {
|