@mono-agent/agent-app 0.19.0 → 0.20.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 +304 -32
- package/dist/acp-bridge.d.ts.map +1 -1
- package/dist/acp-bridge.js +113 -54
- package/dist/acp-bridge.js.map +1 -1
- package/dist/acp-session-store.d.ts +12 -0
- package/dist/acp-session-store.d.ts.map +1 -0
- package/dist/acp-session-store.js +74 -0
- package/dist/acp-session-store.js.map +1 -0
- package/dist/agent-artifact-paths.d.ts +8 -0
- package/dist/agent-artifact-paths.d.ts.map +1 -0
- package/dist/agent-artifact-paths.js +11 -0
- package/dist/agent-artifact-paths.js.map +1 -0
- package/dist/agent-root-coordinator.d.ts +101 -0
- package/dist/agent-root-coordinator.d.ts.map +1 -0
- package/dist/agent-root-coordinator.js +470 -0
- package/dist/agent-root-coordinator.js.map +1 -0
- package/dist/app-controller-channels.d.ts +11 -1
- package/dist/app-controller-channels.d.ts.map +1 -1
- package/dist/app-controller-channels.js +145 -20
- package/dist/app-controller-channels.js.map +1 -1
- package/dist/app-controller-continuation.d.ts +2 -0
- package/dist/app-controller-continuation.d.ts.map +1 -1
- package/dist/app-controller-continuation.js.map +1 -1
- package/dist/app-controller-lifecycle.d.ts +5 -0
- package/dist/app-controller-lifecycle.d.ts.map +1 -1
- package/dist/app-controller-lifecycle.js +24 -5
- package/dist/app-controller-lifecycle.js.map +1 -1
- package/dist/app-controller-maintenance.d.ts +2 -0
- package/dist/app-controller-maintenance.d.ts.map +1 -1
- package/dist/app-controller-maintenance.js +32 -2
- package/dist/app-controller-maintenance.js.map +1 -1
- package/dist/app-controller-process-jobs.d.ts +46 -0
- package/dist/app-controller-process-jobs.d.ts.map +1 -0
- package/dist/app-controller-process-jobs.js +281 -0
- package/dist/app-controller-process-jobs.js.map +1 -0
- package/dist/app-controller-responder.d.ts +15 -0
- package/dist/app-controller-responder.d.ts.map +1 -1
- package/dist/app-controller-responder.js +146 -12
- package/dist/app-controller-responder.js.map +1 -1
- package/dist/app-controller-traceability.d.ts +6 -0
- package/dist/app-controller-traceability.d.ts.map +1 -1
- package/dist/app-controller-traceability.js +10 -0
- package/dist/app-controller-traceability.js.map +1 -1
- package/dist/app-controller-utils.d.ts +8 -0
- package/dist/app-controller-utils.d.ts.map +1 -1
- package/dist/app-controller-utils.js +24 -1
- package/dist/app-controller-utils.js.map +1 -1
- package/dist/app-controller.d.ts +40 -1
- package/dist/app-controller.d.ts.map +1 -1
- package/dist/app-controller.js +124 -39
- package/dist/app-controller.js.map +1 -1
- package/dist/background-log-maintenance.d.ts +85 -2
- package/dist/background-log-maintenance.d.ts.map +1 -1
- package/dist/background-log-maintenance.js +407 -136
- package/dist/background-log-maintenance.js.map +1 -1
- package/dist/background-runtime.d.ts.map +1 -1
- package/dist/background-runtime.js +22 -41
- package/dist/background-runtime.js.map +1 -1
- package/dist/background.d.ts +18 -26
- package/dist/background.d.ts.map +1 -1
- package/dist/background.js +83 -138
- package/dist/background.js.map +1 -1
- package/dist/channel-drivers/cron.d.ts +12 -1
- package/dist/channel-drivers/cron.d.ts.map +1 -1
- package/dist/channel-drivers/cron.js +121 -18
- package/dist/channel-drivers/cron.js.map +1 -1
- package/dist/channel-drivers/native-notify.d.ts +3 -0
- package/dist/channel-drivers/native-notify.d.ts.map +1 -1
- package/dist/channel-drivers/native-notify.js +2 -1
- package/dist/channel-drivers/native-notify.js.map +1 -1
- package/dist/channel-drivers/slack.d.ts.map +1 -1
- package/dist/channel-drivers/slack.js +41 -4
- 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 +46 -3
- package/dist/channel-drivers/telegram.js.map +1 -1
- package/dist/channel-drivers/tui.d.ts +13 -2
- package/dist/channel-drivers/tui.d.ts.map +1 -1
- package/dist/channel-drivers/tui.js +112 -3
- package/dist/channel-drivers/tui.js.map +1 -1
- package/dist/channels.d.ts +1 -1
- package/dist/channels.d.ts.map +1 -1
- package/dist/channels.js +4 -2
- package/dist/channels.js.map +1 -1
- package/dist/cli-args.d.ts +15 -1
- package/dist/cli-args.d.ts.map +1 -1
- package/dist/cli-args.js +82 -12
- package/dist/cli-args.js.map +1 -1
- package/dist/cli-background-command.d.ts +9 -2
- package/dist/cli-background-command.d.ts.map +1 -1
- package/dist/cli-background-command.js +174 -66
- package/dist/cli-background-command.js.map +1 -1
- package/dist/cli-commands.d.ts.map +1 -1
- package/dist/cli-commands.js +50 -12
- package/dist/cli-commands.js.map +1 -1
- package/dist/cli-help.d.ts.map +1 -1
- package/dist/cli-help.js +21 -1
- package/dist/cli-help.js.map +1 -1
- package/dist/config-reference.d.ts.map +1 -1
- package/dist/config-reference.js +99 -2
- package/dist/config-reference.js.map +1 -1
- package/dist/configured-agent.d.ts +44 -3
- package/dist/configured-agent.d.ts.map +1 -1
- package/dist/configured-agent.js +805 -156
- package/dist/configured-agent.js.map +1 -1
- package/dist/conversation-state-roots.d.ts +59 -0
- package/dist/conversation-state-roots.d.ts.map +1 -0
- package/dist/conversation-state-roots.js +248 -0
- package/dist/conversation-state-roots.js.map +1 -0
- package/dist/cron-control-store.d.ts +91 -0
- package/dist/cron-control-store.d.ts.map +1 -0
- package/dist/cron-control-store.js +1251 -0
- package/dist/cron-control-store.js.map +1 -0
- package/dist/cron-operator-service.d.ts +54 -0
- package/dist/cron-operator-service.d.ts.map +1 -0
- package/dist/cron-operator-service.js +352 -0
- package/dist/cron-operator-service.js.map +1 -0
- package/dist/doctor-session-history.d.ts +9 -0
- package/dist/doctor-session-history.d.ts.map +1 -0
- package/dist/doctor-session-history.js +313 -0
- package/dist/doctor-session-history.js.map +1 -0
- package/dist/doctor.d.ts +2 -1
- package/dist/doctor.d.ts.map +1 -1
- package/dist/doctor.js +320 -9
- package/dist/doctor.js.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/interaction-bridge.d.ts +1 -0
- package/dist/interaction-bridge.d.ts.map +1 -1
- package/dist/interaction-bridge.js +54 -6
- package/dist/interaction-bridge.js.map +1 -1
- package/dist/jobs-command.d.ts +17 -0
- package/dist/jobs-command.d.ts.map +1 -0
- package/dist/jobs-command.js +207 -0
- package/dist/jobs-command.js.map +1 -0
- package/dist/launchd-lifecycle-lock.d.ts +36 -0
- package/dist/launchd-lifecycle-lock.d.ts.map +1 -0
- package/dist/launchd-lifecycle-lock.js +124 -0
- package/dist/launchd-lifecycle-lock.js.map +1 -0
- package/dist/launchd-log-monitor-status.d.ts +7 -0
- package/dist/launchd-log-monitor-status.d.ts.map +1 -0
- package/dist/launchd-log-monitor-status.js +224 -0
- package/dist/launchd-log-monitor-status.js.map +1 -0
- package/dist/launchd-logs.d.ts +9 -1
- package/dist/launchd-logs.d.ts.map +1 -1
- package/dist/launchd-logs.js +69 -16
- package/dist/launchd-logs.js.map +1 -1
- package/dist/launchd-maintenance-command.d.ts +10 -0
- package/dist/launchd-maintenance-command.d.ts.map +1 -0
- package/dist/launchd-maintenance-command.js +2 -0
- package/dist/launchd-maintenance-command.js.map +1 -0
- package/dist/launchd-maintenance-entry.d.ts +30 -0
- package/dist/launchd-maintenance-entry.d.ts.map +1 -0
- package/dist/launchd-maintenance-entry.js +206 -0
- package/dist/launchd-maintenance-entry.js.map +1 -0
- package/dist/launchd-maintenance-gate.d.ts +28 -0
- package/dist/launchd-maintenance-gate.d.ts.map +1 -0
- package/dist/launchd-maintenance-gate.js +80 -0
- package/dist/launchd-maintenance-gate.js.map +1 -0
- package/dist/launchd-private-files.d.ts +3 -0
- package/dist/launchd-private-files.d.ts.map +1 -0
- package/dist/launchd-private-files.js +58 -0
- package/dist/launchd-private-files.js.map +1 -0
- package/dist/launchd.d.ts +56 -2
- package/dist/launchd.d.ts.map +1 -1
- package/dist/launchd.js +210 -5
- package/dist/launchd.js.map +1 -1
- package/dist/local-configuration.d.ts.map +1 -1
- package/dist/local-configuration.js +33 -7
- package/dist/local-configuration.js.map +1 -1
- package/dist/managed-launchd-maintenance-environment.d.ts +3 -0
- package/dist/managed-launchd-maintenance-environment.d.ts.map +1 -0
- package/dist/managed-launchd-maintenance-environment.js +12 -0
- package/dist/managed-launchd-maintenance-environment.js.map +1 -0
- package/dist/managed-runtime-launch-proof.d.ts +11 -0
- package/dist/managed-runtime-launch-proof.d.ts.map +1 -0
- package/dist/managed-runtime-launch-proof.js +59 -0
- package/dist/managed-runtime-launch-proof.js.map +1 -0
- package/dist/managed-runtime-maintenance-entry.d.ts +10 -0
- package/dist/managed-runtime-maintenance-entry.d.ts.map +1 -0
- package/dist/managed-runtime-maintenance-entry.js +244 -0
- package/dist/managed-runtime-maintenance-entry.js.map +1 -0
- package/dist/managed-web-logs.d.ts +29 -11
- package/dist/managed-web-logs.d.ts.map +1 -1
- package/dist/managed-web-logs.js +244 -121
- package/dist/managed-web-logs.js.map +1 -1
- package/dist/managed-web-maintenance-environment.d.ts +6 -0
- package/dist/managed-web-maintenance-environment.d.ts.map +1 -0
- package/dist/managed-web-maintenance-environment.js +19 -0
- package/dist/managed-web-maintenance-environment.js.map +1 -0
- package/dist/mcp-apps.d.ts +37 -0
- package/dist/mcp-apps.d.ts.map +1 -0
- package/dist/mcp-apps.js +1753 -0
- package/dist/mcp-apps.js.map +1 -0
- package/dist/memory-command.d.ts +12 -0
- package/dist/memory-command.d.ts.map +1 -1
- package/dist/memory-command.js +510 -1
- package/dist/memory-command.js.map +1 -1
- package/dist/modules/known-tools.d.ts +1 -1
- package/dist/modules/known-tools.d.ts.map +1 -1
- package/dist/modules/known-tools.js +2 -0
- package/dist/modules/known-tools.js.map +1 -1
- package/dist/posted-reply-history.d.ts.map +1 -1
- package/dist/posted-reply-history.js +12 -0
- package/dist/posted-reply-history.js.map +1 -1
- package/dist/proactive-notify.d.ts +4 -1
- package/dist/proactive-notify.d.ts.map +1 -1
- package/dist/proactive-notify.js +43 -5
- package/dist/proactive-notify.js.map +1 -1
- package/dist/process-jobs-config.d.ts +82 -0
- package/dist/process-jobs-config.d.ts.map +1 -0
- package/dist/process-jobs-config.js +423 -0
- package/dist/process-jobs-config.js.map +1 -0
- package/dist/process-jobs-context.d.ts +24 -0
- package/dist/process-jobs-context.d.ts.map +1 -0
- package/dist/process-jobs-context.js +135 -0
- package/dist/process-jobs-context.js.map +1 -0
- package/dist/process-jobs-root-registry.d.ts +115 -0
- package/dist/process-jobs-root-registry.d.ts.map +1 -0
- package/dist/process-jobs-root-registry.js +978 -0
- package/dist/process-jobs-root-registry.js.map +1 -0
- package/dist/process-jobs-runtime.d.ts +35 -0
- package/dist/process-jobs-runtime.d.ts.map +1 -0
- package/dist/process-jobs-runtime.js +177 -0
- package/dist/process-jobs-runtime.js.map +1 -0
- package/dist/process-jobs-service.d.ts +70 -0
- package/dist/process-jobs-service.d.ts.map +1 -0
- package/dist/process-jobs-service.js +1989 -0
- package/dist/process-jobs-service.js.map +1 -0
- package/dist/process-jobs-store.d.ts +151 -0
- package/dist/process-jobs-store.d.ts.map +1 -0
- package/dist/process-jobs-store.js +1290 -0
- package/dist/process-jobs-store.js.map +1 -0
- package/dist/reply-artifacts.d.ts +65 -0
- package/dist/reply-artifacts.d.ts.map +1 -0
- package/dist/reply-artifacts.js +1147 -0
- package/dist/reply-artifacts.js.map +1 -0
- package/dist/reply-part-budget.d.ts +16 -0
- package/dist/reply-part-budget.d.ts.map +1 -0
- package/dist/reply-part-budget.js +57 -0
- package/dist/reply-part-budget.js.map +1 -0
- package/dist/request-model-override.d.ts +19 -0
- package/dist/request-model-override.d.ts.map +1 -1
- package/dist/request-model-override.js +54 -1
- package/dist/request-model-override.js.map +1 -1
- package/dist/request-scoped-mcp.d.ts +31 -0
- package/dist/request-scoped-mcp.d.ts.map +1 -0
- package/dist/request-scoped-mcp.js +197 -0
- package/dist/request-scoped-mcp.js.map +1 -0
- package/dist/run-history.d.ts.map +1 -1
- package/dist/run-history.js +50 -254
- package/dist/run-history.js.map +1 -1
- package/dist/runtime-option-extensions.d.ts +21 -0
- package/dist/runtime-option-extensions.d.ts.map +1 -1
- package/dist/runtime-option-extensions.js +53 -0
- package/dist/runtime-option-extensions.js.map +1 -1
- package/dist/secure-file-replace.d.ts +3 -0
- package/dist/secure-file-replace.d.ts.map +1 -1
- package/dist/secure-file-replace.js +31 -7
- package/dist/secure-file-replace.js.map +1 -1
- package/dist/session-history.d.ts +146 -0
- package/dist/session-history.d.ts.map +1 -0
- package/dist/session-history.js +347 -0
- package/dist/session-history.js.map +1 -0
- package/dist/sessions.d.ts +45 -2
- package/dist/sessions.d.ts.map +1 -1
- package/dist/sessions.js +849 -30
- package/dist/sessions.js.map +1 -1
- package/dist/web-command.d.ts +12 -11
- package/dist/web-command.d.ts.map +1 -1
- package/dist/web-command.js +554 -80
- package/dist/web-command.js.map +1 -1
- package/dist/web-log-maintenance-command.d.ts +7 -0
- package/dist/web-log-maintenance-command.d.ts.map +1 -0
- package/dist/web-log-maintenance-command.js +21 -0
- package/dist/web-log-maintenance-command.js.map +1 -0
- package/dist/web-log-maintenance-status.d.ts +26 -0
- package/dist/web-log-maintenance-status.d.ts.map +1 -0
- package/dist/web-log-maintenance-status.js +171 -0
- package/dist/web-log-maintenance-status.js.map +1 -0
- package/dist/web-log-maintenance.d.ts +32 -0
- package/dist/web-log-maintenance.d.ts.map +1 -0
- package/dist/web-log-maintenance.js +163 -0
- package/dist/web-log-maintenance.js.map +1 -0
- package/dist/web-maintenance-paths.d.ts +12 -0
- package/dist/web-maintenance-paths.d.ts.map +1 -0
- package/dist/web-maintenance-paths.js +24 -0
- package/dist/web-maintenance-paths.js.map +1 -0
- package/dist/wizard/prompts.d.ts.map +1 -1
- package/dist/wizard/prompts.js +1 -0
- package/dist/wizard/prompts.js.map +1 -1
- package/dist/wizard/run.js +1 -1
- package/dist/wizard/run.js.map +1 -1
- package/package.json +15 -15
- package/schema/mono-agent.config.schema.json +102 -1
- package/skills/mono-agent-composer/references/config-blueprint.md +26 -2
- package/skills/mono-agent-composer/references/discovery-questions.md +2 -2
- package/skills/mono-agent-composer/references/feature-coverage.md +6 -4
- package/skills/mono-agent-composer/references/package-map.md +19 -3
- package/skills/mono-agent-composer/references/playbooks.md +3 -3
- package/skills/mono-agent-composer/references/validation.md +4 -2
package/dist/doctor.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { execFile as execFileCallback } from "node:child_process";
|
|
2
2
|
import { constants } from "node:fs";
|
|
3
|
-
import { access, lstat, mkdir, readdir, readFile, stat } from "node:fs/promises";
|
|
3
|
+
import { access, lstat, mkdir, readdir, readFile, realpath, stat } from "node:fs/promises";
|
|
4
4
|
import { homedir } from "node:os";
|
|
5
5
|
import { isIP } from "node:net";
|
|
6
|
-
import { join } from "node:path";
|
|
6
|
+
import { basename, join, relative, resolve, sep } from "node:path";
|
|
7
7
|
import { promisify } from "node:util";
|
|
8
8
|
// `BuiltinProvider`, not the root `KnownProvider`: since pi-ai 0.83.0 the
|
|
9
9
|
// latter also covers purely dynamic providers (e.g. "radius") that have no
|
|
@@ -28,6 +28,9 @@ import { CONTINUATION_RECORD_STORE_SCHEMA_VERSION, } from "./continuation-store.
|
|
|
28
28
|
import { applyRetention, isDurableGeneration, isOriginContextGroupCommit, isRecord, isRecordTransaction, isStoreFile, normalizeLegacyContinuationRecords, requiredDate, resolveRetention, } from "./continuation-store-policy.js";
|
|
29
29
|
import { MAX_LEGACY_STORE_BYTES, MAX_MANIFEST_BYTES, MAX_RECORD_BYTES, MAX_TRANSACTION_BYTES, } from "./continuation-store-types.js";
|
|
30
30
|
import { CONTINUATION_STATES, continuationDigest } from "./continuations.js";
|
|
31
|
+
import { isProcessJobState, PROCESS_JOB_STATES } from "@mono-agent/agent-contracts";
|
|
32
|
+
import { loadProcessJobsSettings } from "./process-jobs-config.js";
|
|
33
|
+
import { MAX_PROCESS_JOB_HEALTH_BYTES, PROCESS_JOB_HEALTH_FILE, PROCESS_JOB_QUARANTINE_DIRECTORY, PROCESS_JOB_RECORDS_DIRECTORY, PROCESS_JOB_SECRET_FILE, } from "./process-jobs-store.js";
|
|
31
34
|
import { formatInteractionBridgeUrl, loadInteractionSettings } from "./interaction-bridge.js";
|
|
32
35
|
import { FIRST_RUN_MEMORY_INITIALIZING_MARKER } from "./first-run-managed-memory.js";
|
|
33
36
|
import { DEFAULT_MEMORY_EMBEDDING_ENDPOINTS, probeMemoryEmbeddingSelection, } from "./memory-embedding-service.js";
|
|
@@ -37,8 +40,11 @@ import { checkManagedProjectSkills, managedProjectSkillsExist } from "./project-
|
|
|
37
40
|
import { configuredRuntimeFallbackModels, configuredRuntimeModels } from "./runtime-routes.js";
|
|
38
41
|
import { runtimeProvenanceDetail } from "./runtime-provenance.js";
|
|
39
42
|
import { loadSupermemoryPlugin } from "./supermemory-plugin.js";
|
|
40
|
-
import { DEFAULT_LAUNCHD_LOG_POLICY, inspectLaunchdLogs,
|
|
43
|
+
import { DEFAULT_LAUNCHD_LOG_POLICY, inspectLaunchdLogs, LAUNCHD_LOG_MONITOR_INTERVAL_SECONDS, launchdLogPathsForConfig, } from "./launchd-logs.js";
|
|
44
|
+
import { readLaunchdLogMonitorStatus } from "./launchd-log-monitor-status.js";
|
|
41
45
|
import { exporterSection, runsSection } from "./doctor-observability.js";
|
|
46
|
+
import { sessionToolHistorySection } from "./doctor-session-history.js";
|
|
47
|
+
import { runtimeRouteContainsUnsupportedHistoryTool } from "./app-controller-utils.js";
|
|
42
48
|
const execFile = promisify(execFileCallback);
|
|
43
49
|
const CONTINUATION_V2_ROLLBACK_GUARD = "UPGRADED-TO-RECORDS-V3";
|
|
44
50
|
const CONTINUATION_V2_ROLLBACK_GUARD_CONTENT = "This state directory uses continuation records v3. Older runtimes must not open records-v2.\n";
|
|
@@ -88,8 +94,13 @@ export async function validateMonoAgentFolder(options) {
|
|
|
88
94
|
sections.push(await contextSection(coreConfig, options.cwd));
|
|
89
95
|
sections.push(await memorySection(coreConfig, options.cwd, options.env, liveness, allowFilesystemWrites, options.preferAppPluginInstall === true));
|
|
90
96
|
sections.push(await toolsSection(coreConfig, options));
|
|
97
|
+
sections.push(await sessionToolHistorySection({
|
|
98
|
+
historyRoot: join(coreConfig.artifacts.dir, "..", "history"),
|
|
99
|
+
requestScopedToolSupported: !runtimeRouteContainsUnsupportedHistoryTool(coreConfig),
|
|
100
|
+
}));
|
|
91
101
|
sections.push(await webToolsSection(coreConfig, options, liveness));
|
|
92
102
|
sections.push(await continuationSection(coreConfig, options));
|
|
103
|
+
sections.push(await processJobsSection(coreConfig, options));
|
|
93
104
|
sections.push(await sandboxSection(coreConfig, options.sandboxEngine));
|
|
94
105
|
}
|
|
95
106
|
sections.push(await exporterSection(options, liveness));
|
|
@@ -120,9 +131,20 @@ export async function validateMonoAgentFolder(options) {
|
|
|
120
131
|
/** Read-only launchd log inventory used by both `validate` and its `doctor` alias. */
|
|
121
132
|
export async function launchdLogsSection(configPath) {
|
|
122
133
|
let inspection;
|
|
134
|
+
let monitorStatus;
|
|
123
135
|
try {
|
|
124
136
|
const paths = await launchdLogPathsForConfig(configPath);
|
|
125
137
|
inspection = await inspectLaunchdLogs(paths);
|
|
138
|
+
const stdoutName = basename(paths.stdoutPath);
|
|
139
|
+
const mainLabel = stdoutName.endsWith(".out.log")
|
|
140
|
+
? stdoutName.slice(0, -".out.log".length)
|
|
141
|
+
: "";
|
|
142
|
+
try {
|
|
143
|
+
monitorStatus = await readLaunchdLogMonitorStatus(mainLabel, paths);
|
|
144
|
+
}
|
|
145
|
+
catch {
|
|
146
|
+
monitorStatus = "unavailable";
|
|
147
|
+
}
|
|
126
148
|
}
|
|
127
149
|
catch {
|
|
128
150
|
return {
|
|
@@ -132,15 +154,16 @@ export async function launchdLogsSection(configPath) {
|
|
|
132
154
|
details: ["[WARN] Managed launchd log metadata could not be inspected safely."],
|
|
133
155
|
};
|
|
134
156
|
}
|
|
135
|
-
return launchdLogsSectionFromInspection(inspection);
|
|
157
|
+
return launchdLogsSectionFromInspection(inspection, monitorStatus);
|
|
136
158
|
}
|
|
137
159
|
/** Pure renderer kept separate so exact byte accounting is deterministic in tests. */
|
|
138
|
-
export function launchdLogsSectionFromInspection(inspection) {
|
|
160
|
+
export function launchdLogsSectionFromInspection(inspection, monitorStatus) {
|
|
139
161
|
const policy = DEFAULT_LAUNCHD_LOG_POLICY;
|
|
140
162
|
const details = [
|
|
141
|
-
`Policy: ${policy.maxBytes} bytes per file, ${policy.rotationCount} retained generations, checked every ${
|
|
163
|
+
`Policy: ${policy.maxBytes} bytes per file, ${policy.rotationCount} retained generations, checked every ${LAUNCHD_LOG_MONITOR_INTERVAL_SECONDS} seconds.`,
|
|
142
164
|
streamSizeDetail("stdout", inspection.stdout),
|
|
143
165
|
streamSizeDetail("stderr", inspection.stderr),
|
|
166
|
+
...monitorStatusDetails(monitorStatus),
|
|
144
167
|
...inspection.issues.map((issue) => `[WARN] ${issue}.`),
|
|
145
168
|
...oversizedLogDetails("stdout", inspection.stdout, policy.maxBytes),
|
|
146
169
|
...oversizedLogDetails("stderr", inspection.stderr, policy.maxBytes),
|
|
@@ -160,6 +183,19 @@ export function launchdLogsSectionFromInspection(inspection) {
|
|
|
160
183
|
details,
|
|
161
184
|
};
|
|
162
185
|
}
|
|
186
|
+
function monitorStatusDetails(status) {
|
|
187
|
+
if (status === "unavailable") {
|
|
188
|
+
return ["Monitor: owner-private status is unavailable or unsafe."];
|
|
189
|
+
}
|
|
190
|
+
if (status === undefined)
|
|
191
|
+
return ["Monitor: no observational snapshot has been recorded yet."];
|
|
192
|
+
return [
|
|
193
|
+
`Monitor last inspection: ${status.lastInspectionAt}.`,
|
|
194
|
+
`Monitor wake count: ${status.wakeCount}.`,
|
|
195
|
+
`Monitor last outcome: ${status.lastOutcome}.`,
|
|
196
|
+
`Monitor cooldown deadline: ${status.cooldownDeadline}.`,
|
|
197
|
+
];
|
|
198
|
+
}
|
|
163
199
|
function streamSizeDetail(label, stream) {
|
|
164
200
|
if (!stream.byteAccountingComplete) {
|
|
165
201
|
return `${label}: byte inventory unavailable because one or more paths could not be inspected safely.`;
|
|
@@ -1910,9 +1946,21 @@ async function probeSearxngEndpoint(endpoint) {
|
|
|
1910
1946
|
if (!response.ok)
|
|
1911
1947
|
return { ok: false, reason: `HTTP ${response.status}` };
|
|
1912
1948
|
const body = await response.json();
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1949
|
+
if (!Array.isArray(body.results)) {
|
|
1950
|
+
return { ok: false, reason: "response did not contain a results array" };
|
|
1951
|
+
}
|
|
1952
|
+
// An instance whose engines are all captcha'd or suspended answers 200 with
|
|
1953
|
+
// an empty result list, which the array check alone reported as healthy. The
|
|
1954
|
+
// empty array is not the signal on its own — this probe's query can
|
|
1955
|
+
// legitimately match very little — so only flag it alongside failed engines.
|
|
1956
|
+
const unresponsive = Array.isArray(body.unresponsive_engines) ? body.unresponsive_engines : [];
|
|
1957
|
+
if (body.results.length === 0 && unresponsive.length > 0) {
|
|
1958
|
+
const detail = unresponsive
|
|
1959
|
+
.map((entry) => (Array.isArray(entry) ? `${String(entry[0])}: ${String(entry[1])}` : String(entry)))
|
|
1960
|
+
.join("; ");
|
|
1961
|
+
return { ok: false, reason: `no results, and ${unresponsive.length === 1 ? "1 engine" : `${unresponsive.length} engines`} unresponsive — ${detail}` };
|
|
1962
|
+
}
|
|
1963
|
+
return { ok: true, reason: "" };
|
|
1916
1964
|
}
|
|
1917
1965
|
catch (error) {
|
|
1918
1966
|
return { ok: false, reason: error instanceof Error ? error.message : String(error) };
|
|
@@ -2002,6 +2050,269 @@ async function continuationSection(config, input) {
|
|
|
2002
2050
|
details,
|
|
2003
2051
|
};
|
|
2004
2052
|
}
|
|
2053
|
+
async function processJobsSection(config, input) {
|
|
2054
|
+
let settings;
|
|
2055
|
+
try {
|
|
2056
|
+
settings = await loadProcessJobsSettings({ cwd: input.cwd, configPath: input.configPath, env: input.env });
|
|
2057
|
+
}
|
|
2058
|
+
catch (error) {
|
|
2059
|
+
return {
|
|
2060
|
+
id: "process-jobs",
|
|
2061
|
+
label: "Background process jobs",
|
|
2062
|
+
status: "error",
|
|
2063
|
+
details: [`Process-job configuration is invalid: ${continuationReason(error)}`],
|
|
2064
|
+
};
|
|
2065
|
+
}
|
|
2066
|
+
if (!settings.enabled) {
|
|
2067
|
+
return {
|
|
2068
|
+
id: "process-jobs",
|
|
2069
|
+
label: "Background process jobs",
|
|
2070
|
+
status: "disabled",
|
|
2071
|
+
details: [
|
|
2072
|
+
"Background Exec/Bash jobs are opt-in (processJobs.enabled=false).",
|
|
2073
|
+
...(process.platform === "win32" ? ["Windows is unsupported; detached POSIX process-group ownership is required."] : []),
|
|
2074
|
+
],
|
|
2075
|
+
};
|
|
2076
|
+
}
|
|
2077
|
+
if (process.platform === "win32") {
|
|
2078
|
+
return {
|
|
2079
|
+
id: "process-jobs",
|
|
2080
|
+
label: "Background process jobs",
|
|
2081
|
+
status: "error",
|
|
2082
|
+
details: ["Process jobs are enabled but Windows is unsupported; detached POSIX process-group ownership is required."],
|
|
2083
|
+
};
|
|
2084
|
+
}
|
|
2085
|
+
const details = [
|
|
2086
|
+
`Owner-only local state: ${settings.stateDir}.`,
|
|
2087
|
+
`Concurrency: ${String(settings.maxConcurrent)} global, ${String(settings.maxActivePerConversation)} per conversation, ${String(settings.maxQueued)} queued.`,
|
|
2088
|
+
`Caps: runtime=${String(settings.maxRuntimeMs)}ms, queue-age=${String(settings.maxQueueAgeMs)}ms, output=${String(settings.maxOutputBytes)} bytes, chain-depth=${String(settings.maxChainDepth)}.`,
|
|
2089
|
+
`Runtime availability: Pi-native Exec/Bash only; configured primary is ${config.runtime.model.sdk}.`,
|
|
2090
|
+
];
|
|
2091
|
+
const inspection = await inspectProcessJobState(input.cwd, settings.stateDir);
|
|
2092
|
+
return {
|
|
2093
|
+
id: "process-jobs",
|
|
2094
|
+
label: "Background process jobs",
|
|
2095
|
+
status: inspection.status,
|
|
2096
|
+
details: [...details, ...inspection.details],
|
|
2097
|
+
};
|
|
2098
|
+
}
|
|
2099
|
+
async function inspectProcessJobState(cwd, stateDir) {
|
|
2100
|
+
const confinementError = await inspectProcessJobConfinement(cwd, stateDir);
|
|
2101
|
+
if (confinementError !== undefined) {
|
|
2102
|
+
return { status: "error", details: [confinementError] };
|
|
2103
|
+
}
|
|
2104
|
+
let root;
|
|
2105
|
+
try {
|
|
2106
|
+
root = await lstat(stateDir);
|
|
2107
|
+
}
|
|
2108
|
+
catch (error) {
|
|
2109
|
+
return continuationFsCode(error) === "ENOENT"
|
|
2110
|
+
? { status: "ok", details: ["State has not been initialized; the app will create it owner-only on first start."] }
|
|
2111
|
+
: { status: "error", details: [`Process-job state cannot be inspected: ${continuationReason(error)}`] };
|
|
2112
|
+
}
|
|
2113
|
+
if (!root.isDirectory() || root.isSymbolicLink()) {
|
|
2114
|
+
return { status: "error", details: ["Process-job state root must be a real directory, not a symlink."] };
|
|
2115
|
+
}
|
|
2116
|
+
const rootSecurity = processJobOwnershipError(root, "state directory", 0o700);
|
|
2117
|
+
if (rootSecurity !== undefined)
|
|
2118
|
+
return { status: "error", details: [rootSecurity] };
|
|
2119
|
+
const healthInspection = await inspectProcessJobHealth(stateDir);
|
|
2120
|
+
if (healthInspection !== undefined)
|
|
2121
|
+
return healthInspection;
|
|
2122
|
+
const quarantineInspection = await inspectProcessJobQuarantine(stateDir);
|
|
2123
|
+
if (quarantineInspection !== undefined)
|
|
2124
|
+
return quarantineInspection;
|
|
2125
|
+
const secretPath = join(stateDir, PROCESS_JOB_SECRET_FILE);
|
|
2126
|
+
try {
|
|
2127
|
+
const secret = await lstat(secretPath);
|
|
2128
|
+
if (!secret.isFile() || secret.isSymbolicLink() || Number(secret.nlink) !== 1 || Number(secret.size) > 256) {
|
|
2129
|
+
return { status: "error", details: ["Process-job operator secret must be one regular file."] };
|
|
2130
|
+
}
|
|
2131
|
+
const secretSecurity = processJobOwnershipError(secret, "operator secret", 0o600);
|
|
2132
|
+
if (secretSecurity !== undefined)
|
|
2133
|
+
return { status: "error", details: [secretSecurity] };
|
|
2134
|
+
}
|
|
2135
|
+
catch (error) {
|
|
2136
|
+
if (continuationFsCode(error) !== "ENOENT") {
|
|
2137
|
+
return { status: "error", details: [`Process-job operator secret cannot be inspected: ${continuationReason(error)}`] };
|
|
2138
|
+
}
|
|
2139
|
+
}
|
|
2140
|
+
const recordsPath = join(stateDir, PROCESS_JOB_RECORDS_DIRECTORY);
|
|
2141
|
+
let names;
|
|
2142
|
+
try {
|
|
2143
|
+
const records = await lstat(recordsPath);
|
|
2144
|
+
if (!records.isDirectory() || records.isSymbolicLink()) {
|
|
2145
|
+
return { status: "error", details: ["Process-job records path must be a real directory."] };
|
|
2146
|
+
}
|
|
2147
|
+
const recordsSecurity = processJobOwnershipError(records, "records directory", 0o700);
|
|
2148
|
+
if (recordsSecurity !== undefined)
|
|
2149
|
+
return { status: "error", details: [recordsSecurity] };
|
|
2150
|
+
names = (await readdir(recordsPath)).filter((name) => name.endsWith(".json"));
|
|
2151
|
+
}
|
|
2152
|
+
catch (error) {
|
|
2153
|
+
return continuationFsCode(error) === "ENOENT"
|
|
2154
|
+
? { status: "ok", details: ["No local process-job records have been written yet."] }
|
|
2155
|
+
: { status: "error", details: [`Process-job records cannot be inspected: ${continuationReason(error)}`] };
|
|
2156
|
+
}
|
|
2157
|
+
if (names.length > 10_000)
|
|
2158
|
+
return { status: "error", details: ["Process-job record count exceeds the compiled inspection bound of 10000."] };
|
|
2159
|
+
const counts = Object.fromEntries(PROCESS_JOB_STATES.map((state) => [state, 0]));
|
|
2160
|
+
for (const name of names) {
|
|
2161
|
+
const path = join(recordsPath, name);
|
|
2162
|
+
try {
|
|
2163
|
+
const info = await lstat(path);
|
|
2164
|
+
if (!info.isFile() || info.isSymbolicLink() || Number(info.nlink) !== 1)
|
|
2165
|
+
throw new Error("record is not one regular file");
|
|
2166
|
+
const security = processJobOwnershipError(info, `record ${name}`, 0o600);
|
|
2167
|
+
if (security !== undefined)
|
|
2168
|
+
throw new Error(security);
|
|
2169
|
+
if (Number(info.size) > 128 * 1024)
|
|
2170
|
+
throw new Error("record exceeds 131072 bytes");
|
|
2171
|
+
const raw = JSON.parse(await readFile(path, "utf8"));
|
|
2172
|
+
if (!isDoctorObject(raw) || !isProcessJobState(raw.state) || raw.jobId !== name.slice(0, -5)) {
|
|
2173
|
+
throw new Error("record identity or state is invalid");
|
|
2174
|
+
}
|
|
2175
|
+
counts[raw.state] = (counts[raw.state] ?? 0) + 1;
|
|
2176
|
+
}
|
|
2177
|
+
catch (error) {
|
|
2178
|
+
return { status: "error", details: [`Process-job record ${name} is unsafe or malformed: ${continuationReason(error)}`] };
|
|
2179
|
+
}
|
|
2180
|
+
}
|
|
2181
|
+
return {
|
|
2182
|
+
status: "ok",
|
|
2183
|
+
details: [
|
|
2184
|
+
`Local records: ${String(names.length)}.`,
|
|
2185
|
+
`States: ${PROCESS_JOB_STATES.map((state) => `${state}=${String(counts[state] ?? 0)}`).join(", ")}.`,
|
|
2186
|
+
],
|
|
2187
|
+
};
|
|
2188
|
+
}
|
|
2189
|
+
async function inspectProcessJobHealth(stateDir) {
|
|
2190
|
+
const path = join(stateDir, PROCESS_JOB_HEALTH_FILE);
|
|
2191
|
+
try {
|
|
2192
|
+
const info = await lstat(path);
|
|
2193
|
+
if (!info.isFile() || info.isSymbolicLink() || Number(info.nlink) !== 1) {
|
|
2194
|
+
throw new Error("runtime health marker is not one regular file");
|
|
2195
|
+
}
|
|
2196
|
+
const security = processJobOwnershipError(info, "runtime health marker", 0o600);
|
|
2197
|
+
if (security !== undefined)
|
|
2198
|
+
throw new Error(security);
|
|
2199
|
+
if (Number(info.size) > MAX_PROCESS_JOB_HEALTH_BYTES) {
|
|
2200
|
+
throw new Error(`runtime health marker exceeds ${String(MAX_PROCESS_JOB_HEALTH_BYTES)} bytes`);
|
|
2201
|
+
}
|
|
2202
|
+
const value = JSON.parse(await readFile(path, "utf8"));
|
|
2203
|
+
if (!isDoctorObject(value)
|
|
2204
|
+
|| Object.keys(value).sort().join(",") !== "detectedAt,operation,schemaVersion,state"
|
|
2205
|
+
|| value.schemaVersion !== 1
|
|
2206
|
+
|| value.state !== "degraded"
|
|
2207
|
+
|| typeof value.operation !== "string"
|
|
2208
|
+
|| !/^[a-z][a-z0-9_.-]{0,63}$/u.test(value.operation)
|
|
2209
|
+
|| typeof value.detectedAt !== "string"
|
|
2210
|
+
|| !Number.isFinite(Date.parse(value.detectedAt))) {
|
|
2211
|
+
throw new Error("runtime health marker is malformed");
|
|
2212
|
+
}
|
|
2213
|
+
return {
|
|
2214
|
+
status: "error",
|
|
2215
|
+
details: [
|
|
2216
|
+
`Process-job storage degraded during ${value.operation} at ${value.detectedAt}; new admission is closed until a clean restart recovery.`,
|
|
2217
|
+
],
|
|
2218
|
+
};
|
|
2219
|
+
}
|
|
2220
|
+
catch (error) {
|
|
2221
|
+
return continuationFsCode(error) === "ENOENT"
|
|
2222
|
+
? undefined
|
|
2223
|
+
: {
|
|
2224
|
+
status: "error",
|
|
2225
|
+
details: [`Process-job runtime health cannot be inspected: ${continuationReason(error)}`],
|
|
2226
|
+
};
|
|
2227
|
+
}
|
|
2228
|
+
}
|
|
2229
|
+
async function inspectProcessJobQuarantine(stateDir) {
|
|
2230
|
+
const quarantinePath = join(stateDir, PROCESS_JOB_QUARANTINE_DIRECTORY);
|
|
2231
|
+
let names;
|
|
2232
|
+
try {
|
|
2233
|
+
const directory = await lstat(quarantinePath);
|
|
2234
|
+
if (!directory.isDirectory() || directory.isSymbolicLink()) {
|
|
2235
|
+
return { status: "error", details: ["Process-job quarantine path must be a real directory."] };
|
|
2236
|
+
}
|
|
2237
|
+
const security = processJobOwnershipError(directory, "quarantine directory", 0o700);
|
|
2238
|
+
if (security !== undefined)
|
|
2239
|
+
return { status: "error", details: [security] };
|
|
2240
|
+
names = await readdir(quarantinePath);
|
|
2241
|
+
}
|
|
2242
|
+
catch (error) {
|
|
2243
|
+
return continuationFsCode(error) === "ENOENT"
|
|
2244
|
+
? undefined
|
|
2245
|
+
: { status: "error", details: [`Process-job quarantine cannot be inspected: ${continuationReason(error)}`] };
|
|
2246
|
+
}
|
|
2247
|
+
if (names.length > 10_000) {
|
|
2248
|
+
return { status: "error", details: ["Process-job quarantined transaction count exceeds 10000."] };
|
|
2249
|
+
}
|
|
2250
|
+
for (const name of names) {
|
|
2251
|
+
const path = join(quarantinePath, name);
|
|
2252
|
+
try {
|
|
2253
|
+
if (!/^transaction-\d{4}-\d{2}-\d{2}T[0-9.-]+Z-[0-9a-f-]{36}\.json$/iu.test(name)) {
|
|
2254
|
+
throw new Error("quarantine filename is invalid");
|
|
2255
|
+
}
|
|
2256
|
+
const info = await lstat(path);
|
|
2257
|
+
if (!info.isFile() || info.isSymbolicLink() || Number(info.nlink) !== 1) {
|
|
2258
|
+
throw new Error("quarantined transaction is not one regular file");
|
|
2259
|
+
}
|
|
2260
|
+
const security = processJobOwnershipError(info, `quarantined transaction ${name}`, 0o600);
|
|
2261
|
+
if (security !== undefined)
|
|
2262
|
+
throw new Error(security);
|
|
2263
|
+
if (Number(info.size) > 256 * 1024)
|
|
2264
|
+
throw new Error("quarantined transaction exceeds 262144 bytes");
|
|
2265
|
+
}
|
|
2266
|
+
catch (error) {
|
|
2267
|
+
return {
|
|
2268
|
+
status: "error",
|
|
2269
|
+
details: [`Process-job quarantined transaction ${name} is unsafe: ${continuationReason(error)}`],
|
|
2270
|
+
};
|
|
2271
|
+
}
|
|
2272
|
+
}
|
|
2273
|
+
return names.length === 0
|
|
2274
|
+
? undefined
|
|
2275
|
+
: {
|
|
2276
|
+
status: "error",
|
|
2277
|
+
details: [
|
|
2278
|
+
`Quarantined unreplayable process-job transactions: ${String(names.length)}.`,
|
|
2279
|
+
"The agent can continue without the affected transaction, but operator review is required.",
|
|
2280
|
+
],
|
|
2281
|
+
};
|
|
2282
|
+
}
|
|
2283
|
+
function processJobOwnershipError(info, label, expectedMode) {
|
|
2284
|
+
if (typeof process.getuid === "function" && Number(info.uid) !== process.getuid()) {
|
|
2285
|
+
return `Process-job ${label} is not owned by the current user.`;
|
|
2286
|
+
}
|
|
2287
|
+
if (process.platform !== "win32" && (Number(info.mode) & 0o777) !== expectedMode) {
|
|
2288
|
+
return `Process-job ${label} permissions must be ${expectedMode.toString(8)}.`;
|
|
2289
|
+
}
|
|
2290
|
+
return undefined;
|
|
2291
|
+
}
|
|
2292
|
+
async function inspectProcessJobConfinement(cwd, stateDir) {
|
|
2293
|
+
const lexicalRoot = resolve(cwd);
|
|
2294
|
+
const root = await realpath(lexicalRoot).catch(() => lexicalRoot);
|
|
2295
|
+
const rel = relative(root, resolve(stateDir));
|
|
2296
|
+
if (rel.length === 0 || rel === ".." || rel.startsWith(`..${sep}`)) {
|
|
2297
|
+
return "Process-job state must be a child directory inside the canonical agent root.";
|
|
2298
|
+
}
|
|
2299
|
+
let current = root;
|
|
2300
|
+
for (const component of rel.split(sep).filter(Boolean)) {
|
|
2301
|
+
current = join(current, component);
|
|
2302
|
+
try {
|
|
2303
|
+
const info = await lstat(current);
|
|
2304
|
+
if (!info.isDirectory() || info.isSymbolicLink()) {
|
|
2305
|
+
return `Process-job state component must be a real directory, not a symlink: ${current}.`;
|
|
2306
|
+
}
|
|
2307
|
+
}
|
|
2308
|
+
catch (error) {
|
|
2309
|
+
if (continuationFsCode(error) === "ENOENT")
|
|
2310
|
+
return undefined;
|
|
2311
|
+
return `Process-job state confinement cannot be inspected: ${continuationReason(error)}`;
|
|
2312
|
+
}
|
|
2313
|
+
}
|
|
2314
|
+
return undefined;
|
|
2315
|
+
}
|
|
2005
2316
|
async function inspectContinuationState(stateDir, retention) {
|
|
2006
2317
|
const details = [];
|
|
2007
2318
|
const retentionPolicy = resolveRetention(retention);
|