@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.
Files changed (303) hide show
  1. package/README.md +304 -32
  2. package/dist/acp-bridge.d.ts.map +1 -1
  3. package/dist/acp-bridge.js +113 -54
  4. package/dist/acp-bridge.js.map +1 -1
  5. package/dist/acp-session-store.d.ts +12 -0
  6. package/dist/acp-session-store.d.ts.map +1 -0
  7. package/dist/acp-session-store.js +74 -0
  8. package/dist/acp-session-store.js.map +1 -0
  9. package/dist/agent-artifact-paths.d.ts +8 -0
  10. package/dist/agent-artifact-paths.d.ts.map +1 -0
  11. package/dist/agent-artifact-paths.js +11 -0
  12. package/dist/agent-artifact-paths.js.map +1 -0
  13. package/dist/agent-root-coordinator.d.ts +101 -0
  14. package/dist/agent-root-coordinator.d.ts.map +1 -0
  15. package/dist/agent-root-coordinator.js +470 -0
  16. package/dist/agent-root-coordinator.js.map +1 -0
  17. package/dist/app-controller-channels.d.ts +11 -1
  18. package/dist/app-controller-channels.d.ts.map +1 -1
  19. package/dist/app-controller-channels.js +145 -20
  20. package/dist/app-controller-channels.js.map +1 -1
  21. package/dist/app-controller-continuation.d.ts +2 -0
  22. package/dist/app-controller-continuation.d.ts.map +1 -1
  23. package/dist/app-controller-continuation.js.map +1 -1
  24. package/dist/app-controller-lifecycle.d.ts +5 -0
  25. package/dist/app-controller-lifecycle.d.ts.map +1 -1
  26. package/dist/app-controller-lifecycle.js +24 -5
  27. package/dist/app-controller-lifecycle.js.map +1 -1
  28. package/dist/app-controller-maintenance.d.ts +2 -0
  29. package/dist/app-controller-maintenance.d.ts.map +1 -1
  30. package/dist/app-controller-maintenance.js +32 -2
  31. package/dist/app-controller-maintenance.js.map +1 -1
  32. package/dist/app-controller-process-jobs.d.ts +46 -0
  33. package/dist/app-controller-process-jobs.d.ts.map +1 -0
  34. package/dist/app-controller-process-jobs.js +281 -0
  35. package/dist/app-controller-process-jobs.js.map +1 -0
  36. package/dist/app-controller-responder.d.ts +15 -0
  37. package/dist/app-controller-responder.d.ts.map +1 -1
  38. package/dist/app-controller-responder.js +146 -12
  39. package/dist/app-controller-responder.js.map +1 -1
  40. package/dist/app-controller-traceability.d.ts +6 -0
  41. package/dist/app-controller-traceability.d.ts.map +1 -1
  42. package/dist/app-controller-traceability.js +10 -0
  43. package/dist/app-controller-traceability.js.map +1 -1
  44. package/dist/app-controller-utils.d.ts +8 -0
  45. package/dist/app-controller-utils.d.ts.map +1 -1
  46. package/dist/app-controller-utils.js +24 -1
  47. package/dist/app-controller-utils.js.map +1 -1
  48. package/dist/app-controller.d.ts +40 -1
  49. package/dist/app-controller.d.ts.map +1 -1
  50. package/dist/app-controller.js +124 -39
  51. package/dist/app-controller.js.map +1 -1
  52. package/dist/background-log-maintenance.d.ts +85 -2
  53. package/dist/background-log-maintenance.d.ts.map +1 -1
  54. package/dist/background-log-maintenance.js +407 -136
  55. package/dist/background-log-maintenance.js.map +1 -1
  56. package/dist/background-runtime.d.ts.map +1 -1
  57. package/dist/background-runtime.js +22 -41
  58. package/dist/background-runtime.js.map +1 -1
  59. package/dist/background.d.ts +18 -26
  60. package/dist/background.d.ts.map +1 -1
  61. package/dist/background.js +83 -138
  62. package/dist/background.js.map +1 -1
  63. package/dist/channel-drivers/cron.d.ts +12 -1
  64. package/dist/channel-drivers/cron.d.ts.map +1 -1
  65. package/dist/channel-drivers/cron.js +121 -18
  66. package/dist/channel-drivers/cron.js.map +1 -1
  67. package/dist/channel-drivers/native-notify.d.ts +3 -0
  68. package/dist/channel-drivers/native-notify.d.ts.map +1 -1
  69. package/dist/channel-drivers/native-notify.js +2 -1
  70. package/dist/channel-drivers/native-notify.js.map +1 -1
  71. package/dist/channel-drivers/slack.d.ts.map +1 -1
  72. package/dist/channel-drivers/slack.js +41 -4
  73. package/dist/channel-drivers/slack.js.map +1 -1
  74. package/dist/channel-drivers/telegram.d.ts.map +1 -1
  75. package/dist/channel-drivers/telegram.js +46 -3
  76. package/dist/channel-drivers/telegram.js.map +1 -1
  77. package/dist/channel-drivers/tui.d.ts +13 -2
  78. package/dist/channel-drivers/tui.d.ts.map +1 -1
  79. package/dist/channel-drivers/tui.js +112 -3
  80. package/dist/channel-drivers/tui.js.map +1 -1
  81. package/dist/channels.d.ts +1 -1
  82. package/dist/channels.d.ts.map +1 -1
  83. package/dist/channels.js +4 -2
  84. package/dist/channels.js.map +1 -1
  85. package/dist/cli-args.d.ts +15 -1
  86. package/dist/cli-args.d.ts.map +1 -1
  87. package/dist/cli-args.js +82 -12
  88. package/dist/cli-args.js.map +1 -1
  89. package/dist/cli-background-command.d.ts +9 -2
  90. package/dist/cli-background-command.d.ts.map +1 -1
  91. package/dist/cli-background-command.js +174 -66
  92. package/dist/cli-background-command.js.map +1 -1
  93. package/dist/cli-commands.d.ts.map +1 -1
  94. package/dist/cli-commands.js +50 -12
  95. package/dist/cli-commands.js.map +1 -1
  96. package/dist/cli-help.d.ts.map +1 -1
  97. package/dist/cli-help.js +21 -1
  98. package/dist/cli-help.js.map +1 -1
  99. package/dist/config-reference.d.ts.map +1 -1
  100. package/dist/config-reference.js +99 -2
  101. package/dist/config-reference.js.map +1 -1
  102. package/dist/configured-agent.d.ts +44 -3
  103. package/dist/configured-agent.d.ts.map +1 -1
  104. package/dist/configured-agent.js +805 -156
  105. package/dist/configured-agent.js.map +1 -1
  106. package/dist/conversation-state-roots.d.ts +59 -0
  107. package/dist/conversation-state-roots.d.ts.map +1 -0
  108. package/dist/conversation-state-roots.js +248 -0
  109. package/dist/conversation-state-roots.js.map +1 -0
  110. package/dist/cron-control-store.d.ts +91 -0
  111. package/dist/cron-control-store.d.ts.map +1 -0
  112. package/dist/cron-control-store.js +1251 -0
  113. package/dist/cron-control-store.js.map +1 -0
  114. package/dist/cron-operator-service.d.ts +54 -0
  115. package/dist/cron-operator-service.d.ts.map +1 -0
  116. package/dist/cron-operator-service.js +352 -0
  117. package/dist/cron-operator-service.js.map +1 -0
  118. package/dist/doctor-session-history.d.ts +9 -0
  119. package/dist/doctor-session-history.d.ts.map +1 -0
  120. package/dist/doctor-session-history.js +313 -0
  121. package/dist/doctor-session-history.js.map +1 -0
  122. package/dist/doctor.d.ts +2 -1
  123. package/dist/doctor.d.ts.map +1 -1
  124. package/dist/doctor.js +320 -9
  125. package/dist/doctor.js.map +1 -1
  126. package/dist/index.d.ts +4 -0
  127. package/dist/index.d.ts.map +1 -1
  128. package/dist/index.js +2 -0
  129. package/dist/index.js.map +1 -1
  130. package/dist/interaction-bridge.d.ts +1 -0
  131. package/dist/interaction-bridge.d.ts.map +1 -1
  132. package/dist/interaction-bridge.js +54 -6
  133. package/dist/interaction-bridge.js.map +1 -1
  134. package/dist/jobs-command.d.ts +17 -0
  135. package/dist/jobs-command.d.ts.map +1 -0
  136. package/dist/jobs-command.js +207 -0
  137. package/dist/jobs-command.js.map +1 -0
  138. package/dist/launchd-lifecycle-lock.d.ts +36 -0
  139. package/dist/launchd-lifecycle-lock.d.ts.map +1 -0
  140. package/dist/launchd-lifecycle-lock.js +124 -0
  141. package/dist/launchd-lifecycle-lock.js.map +1 -0
  142. package/dist/launchd-log-monitor-status.d.ts +7 -0
  143. package/dist/launchd-log-monitor-status.d.ts.map +1 -0
  144. package/dist/launchd-log-monitor-status.js +224 -0
  145. package/dist/launchd-log-monitor-status.js.map +1 -0
  146. package/dist/launchd-logs.d.ts +9 -1
  147. package/dist/launchd-logs.d.ts.map +1 -1
  148. package/dist/launchd-logs.js +69 -16
  149. package/dist/launchd-logs.js.map +1 -1
  150. package/dist/launchd-maintenance-command.d.ts +10 -0
  151. package/dist/launchd-maintenance-command.d.ts.map +1 -0
  152. package/dist/launchd-maintenance-command.js +2 -0
  153. package/dist/launchd-maintenance-command.js.map +1 -0
  154. package/dist/launchd-maintenance-entry.d.ts +30 -0
  155. package/dist/launchd-maintenance-entry.d.ts.map +1 -0
  156. package/dist/launchd-maintenance-entry.js +206 -0
  157. package/dist/launchd-maintenance-entry.js.map +1 -0
  158. package/dist/launchd-maintenance-gate.d.ts +28 -0
  159. package/dist/launchd-maintenance-gate.d.ts.map +1 -0
  160. package/dist/launchd-maintenance-gate.js +80 -0
  161. package/dist/launchd-maintenance-gate.js.map +1 -0
  162. package/dist/launchd-private-files.d.ts +3 -0
  163. package/dist/launchd-private-files.d.ts.map +1 -0
  164. package/dist/launchd-private-files.js +58 -0
  165. package/dist/launchd-private-files.js.map +1 -0
  166. package/dist/launchd.d.ts +56 -2
  167. package/dist/launchd.d.ts.map +1 -1
  168. package/dist/launchd.js +210 -5
  169. package/dist/launchd.js.map +1 -1
  170. package/dist/local-configuration.d.ts.map +1 -1
  171. package/dist/local-configuration.js +33 -7
  172. package/dist/local-configuration.js.map +1 -1
  173. package/dist/managed-launchd-maintenance-environment.d.ts +3 -0
  174. package/dist/managed-launchd-maintenance-environment.d.ts.map +1 -0
  175. package/dist/managed-launchd-maintenance-environment.js +12 -0
  176. package/dist/managed-launchd-maintenance-environment.js.map +1 -0
  177. package/dist/managed-runtime-launch-proof.d.ts +11 -0
  178. package/dist/managed-runtime-launch-proof.d.ts.map +1 -0
  179. package/dist/managed-runtime-launch-proof.js +59 -0
  180. package/dist/managed-runtime-launch-proof.js.map +1 -0
  181. package/dist/managed-runtime-maintenance-entry.d.ts +10 -0
  182. package/dist/managed-runtime-maintenance-entry.d.ts.map +1 -0
  183. package/dist/managed-runtime-maintenance-entry.js +244 -0
  184. package/dist/managed-runtime-maintenance-entry.js.map +1 -0
  185. package/dist/managed-web-logs.d.ts +29 -11
  186. package/dist/managed-web-logs.d.ts.map +1 -1
  187. package/dist/managed-web-logs.js +244 -121
  188. package/dist/managed-web-logs.js.map +1 -1
  189. package/dist/managed-web-maintenance-environment.d.ts +6 -0
  190. package/dist/managed-web-maintenance-environment.d.ts.map +1 -0
  191. package/dist/managed-web-maintenance-environment.js +19 -0
  192. package/dist/managed-web-maintenance-environment.js.map +1 -0
  193. package/dist/mcp-apps.d.ts +37 -0
  194. package/dist/mcp-apps.d.ts.map +1 -0
  195. package/dist/mcp-apps.js +1753 -0
  196. package/dist/mcp-apps.js.map +1 -0
  197. package/dist/memory-command.d.ts +12 -0
  198. package/dist/memory-command.d.ts.map +1 -1
  199. package/dist/memory-command.js +510 -1
  200. package/dist/memory-command.js.map +1 -1
  201. package/dist/modules/known-tools.d.ts +1 -1
  202. package/dist/modules/known-tools.d.ts.map +1 -1
  203. package/dist/modules/known-tools.js +2 -0
  204. package/dist/modules/known-tools.js.map +1 -1
  205. package/dist/posted-reply-history.d.ts.map +1 -1
  206. package/dist/posted-reply-history.js +12 -0
  207. package/dist/posted-reply-history.js.map +1 -1
  208. package/dist/proactive-notify.d.ts +4 -1
  209. package/dist/proactive-notify.d.ts.map +1 -1
  210. package/dist/proactive-notify.js +43 -5
  211. package/dist/proactive-notify.js.map +1 -1
  212. package/dist/process-jobs-config.d.ts +82 -0
  213. package/dist/process-jobs-config.d.ts.map +1 -0
  214. package/dist/process-jobs-config.js +423 -0
  215. package/dist/process-jobs-config.js.map +1 -0
  216. package/dist/process-jobs-context.d.ts +24 -0
  217. package/dist/process-jobs-context.d.ts.map +1 -0
  218. package/dist/process-jobs-context.js +135 -0
  219. package/dist/process-jobs-context.js.map +1 -0
  220. package/dist/process-jobs-root-registry.d.ts +115 -0
  221. package/dist/process-jobs-root-registry.d.ts.map +1 -0
  222. package/dist/process-jobs-root-registry.js +978 -0
  223. package/dist/process-jobs-root-registry.js.map +1 -0
  224. package/dist/process-jobs-runtime.d.ts +35 -0
  225. package/dist/process-jobs-runtime.d.ts.map +1 -0
  226. package/dist/process-jobs-runtime.js +177 -0
  227. package/dist/process-jobs-runtime.js.map +1 -0
  228. package/dist/process-jobs-service.d.ts +70 -0
  229. package/dist/process-jobs-service.d.ts.map +1 -0
  230. package/dist/process-jobs-service.js +1989 -0
  231. package/dist/process-jobs-service.js.map +1 -0
  232. package/dist/process-jobs-store.d.ts +151 -0
  233. package/dist/process-jobs-store.d.ts.map +1 -0
  234. package/dist/process-jobs-store.js +1290 -0
  235. package/dist/process-jobs-store.js.map +1 -0
  236. package/dist/reply-artifacts.d.ts +65 -0
  237. package/dist/reply-artifacts.d.ts.map +1 -0
  238. package/dist/reply-artifacts.js +1147 -0
  239. package/dist/reply-artifacts.js.map +1 -0
  240. package/dist/reply-part-budget.d.ts +16 -0
  241. package/dist/reply-part-budget.d.ts.map +1 -0
  242. package/dist/reply-part-budget.js +57 -0
  243. package/dist/reply-part-budget.js.map +1 -0
  244. package/dist/request-model-override.d.ts +19 -0
  245. package/dist/request-model-override.d.ts.map +1 -1
  246. package/dist/request-model-override.js +54 -1
  247. package/dist/request-model-override.js.map +1 -1
  248. package/dist/request-scoped-mcp.d.ts +31 -0
  249. package/dist/request-scoped-mcp.d.ts.map +1 -0
  250. package/dist/request-scoped-mcp.js +197 -0
  251. package/dist/request-scoped-mcp.js.map +1 -0
  252. package/dist/run-history.d.ts.map +1 -1
  253. package/dist/run-history.js +50 -254
  254. package/dist/run-history.js.map +1 -1
  255. package/dist/runtime-option-extensions.d.ts +21 -0
  256. package/dist/runtime-option-extensions.d.ts.map +1 -1
  257. package/dist/runtime-option-extensions.js +53 -0
  258. package/dist/runtime-option-extensions.js.map +1 -1
  259. package/dist/secure-file-replace.d.ts +3 -0
  260. package/dist/secure-file-replace.d.ts.map +1 -1
  261. package/dist/secure-file-replace.js +31 -7
  262. package/dist/secure-file-replace.js.map +1 -1
  263. package/dist/session-history.d.ts +146 -0
  264. package/dist/session-history.d.ts.map +1 -0
  265. package/dist/session-history.js +347 -0
  266. package/dist/session-history.js.map +1 -0
  267. package/dist/sessions.d.ts +45 -2
  268. package/dist/sessions.d.ts.map +1 -1
  269. package/dist/sessions.js +849 -30
  270. package/dist/sessions.js.map +1 -1
  271. package/dist/web-command.d.ts +12 -11
  272. package/dist/web-command.d.ts.map +1 -1
  273. package/dist/web-command.js +554 -80
  274. package/dist/web-command.js.map +1 -1
  275. package/dist/web-log-maintenance-command.d.ts +7 -0
  276. package/dist/web-log-maintenance-command.d.ts.map +1 -0
  277. package/dist/web-log-maintenance-command.js +21 -0
  278. package/dist/web-log-maintenance-command.js.map +1 -0
  279. package/dist/web-log-maintenance-status.d.ts +26 -0
  280. package/dist/web-log-maintenance-status.d.ts.map +1 -0
  281. package/dist/web-log-maintenance-status.js +171 -0
  282. package/dist/web-log-maintenance-status.js.map +1 -0
  283. package/dist/web-log-maintenance.d.ts +32 -0
  284. package/dist/web-log-maintenance.d.ts.map +1 -0
  285. package/dist/web-log-maintenance.js +163 -0
  286. package/dist/web-log-maintenance.js.map +1 -0
  287. package/dist/web-maintenance-paths.d.ts +12 -0
  288. package/dist/web-maintenance-paths.d.ts.map +1 -0
  289. package/dist/web-maintenance-paths.js +24 -0
  290. package/dist/web-maintenance-paths.js.map +1 -0
  291. package/dist/wizard/prompts.d.ts.map +1 -1
  292. package/dist/wizard/prompts.js +1 -0
  293. package/dist/wizard/prompts.js.map +1 -1
  294. package/dist/wizard/run.js +1 -1
  295. package/dist/wizard/run.js.map +1 -1
  296. package/package.json +15 -15
  297. package/schema/mono-agent.config.schema.json +102 -1
  298. package/skills/mono-agent-composer/references/config-blueprint.md +26 -2
  299. package/skills/mono-agent-composer/references/discovery-questions.md +2 -2
  300. package/skills/mono-agent-composer/references/feature-coverage.md +6 -4
  301. package/skills/mono-agent-composer/references/package-map.md +19 -3
  302. package/skills/mono-agent-composer/references/playbooks.md +3 -3
  303. 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, LAUNCHD_LOG_MAINTENANCE_INTERVAL_SECONDS, launchdLogPathsForConfig, } from "./launchd-logs.js";
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 ${LAUNCHD_LOG_MAINTENANCE_INTERVAL_SECONDS} seconds.`,
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
- return Array.isArray(body.results)
1914
- ? { ok: true, reason: "" }
1915
- : { ok: false, reason: "response did not contain a results array" };
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);