@mono-agent/agent-app 0.19.1 → 0.20.1

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 (317) hide show
  1. package/README.md +307 -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 +13 -1
  18. package/dist/app-controller-channels.d.ts.map +1 -1
  19. package/dist/app-controller-channels.js +152 -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 +4 -0
  29. package/dist/app-controller-maintenance.d.ts.map +1 -1
  30. package/dist/app-controller-maintenance.js +34 -2
  31. package/dist/app-controller-maintenance.js.map +1 -1
  32. package/dist/app-controller-memory.d.ts +2 -0
  33. package/dist/app-controller-memory.d.ts.map +1 -1
  34. package/dist/app-controller-memory.js +6 -3
  35. package/dist/app-controller-memory.js.map +1 -1
  36. package/dist/app-controller-process-jobs.d.ts +49 -0
  37. package/dist/app-controller-process-jobs.d.ts.map +1 -0
  38. package/dist/app-controller-process-jobs.js +294 -0
  39. package/dist/app-controller-process-jobs.js.map +1 -0
  40. package/dist/app-controller-responder.d.ts +17 -0
  41. package/dist/app-controller-responder.d.ts.map +1 -1
  42. package/dist/app-controller-responder.js +164 -13
  43. package/dist/app-controller-responder.js.map +1 -1
  44. package/dist/app-controller-traceability.d.ts +8 -0
  45. package/dist/app-controller-traceability.d.ts.map +1 -1
  46. package/dist/app-controller-traceability.js +22 -1
  47. package/dist/app-controller-traceability.js.map +1 -1
  48. package/dist/app-controller-types.d.ts +1 -1
  49. package/dist/app-controller-types.d.ts.map +1 -1
  50. package/dist/app-controller-utils.d.ts +8 -0
  51. package/dist/app-controller-utils.d.ts.map +1 -1
  52. package/dist/app-controller-utils.js +24 -1
  53. package/dist/app-controller-utils.js.map +1 -1
  54. package/dist/app-controller.d.ts +45 -1
  55. package/dist/app-controller.d.ts.map +1 -1
  56. package/dist/app-controller.js +135 -39
  57. package/dist/app-controller.js.map +1 -1
  58. package/dist/background-log-maintenance.d.ts +85 -2
  59. package/dist/background-log-maintenance.d.ts.map +1 -1
  60. package/dist/background-log-maintenance.js +407 -136
  61. package/dist/background-log-maintenance.js.map +1 -1
  62. package/dist/background-runtime.d.ts.map +1 -1
  63. package/dist/background-runtime.js +22 -41
  64. package/dist/background-runtime.js.map +1 -1
  65. package/dist/background.d.ts +18 -26
  66. package/dist/background.d.ts.map +1 -1
  67. package/dist/background.js +105 -138
  68. package/dist/background.js.map +1 -1
  69. package/dist/channel-drivers/cron.d.ts +12 -1
  70. package/dist/channel-drivers/cron.d.ts.map +1 -1
  71. package/dist/channel-drivers/cron.js +121 -18
  72. package/dist/channel-drivers/cron.js.map +1 -1
  73. package/dist/channel-drivers/native-notify.d.ts +3 -0
  74. package/dist/channel-drivers/native-notify.d.ts.map +1 -1
  75. package/dist/channel-drivers/native-notify.js +2 -1
  76. package/dist/channel-drivers/native-notify.js.map +1 -1
  77. package/dist/channel-drivers/slack.d.ts.map +1 -1
  78. package/dist/channel-drivers/slack.js +41 -4
  79. package/dist/channel-drivers/slack.js.map +1 -1
  80. package/dist/channel-drivers/telegram.d.ts.map +1 -1
  81. package/dist/channel-drivers/telegram.js +46 -3
  82. package/dist/channel-drivers/telegram.js.map +1 -1
  83. package/dist/channel-drivers/tui.d.ts +13 -2
  84. package/dist/channel-drivers/tui.d.ts.map +1 -1
  85. package/dist/channel-drivers/tui.js +112 -3
  86. package/dist/channel-drivers/tui.js.map +1 -1
  87. package/dist/channels.d.ts +1 -1
  88. package/dist/channels.d.ts.map +1 -1
  89. package/dist/channels.js +4 -2
  90. package/dist/channels.js.map +1 -1
  91. package/dist/cli-args.d.ts +15 -1
  92. package/dist/cli-args.d.ts.map +1 -1
  93. package/dist/cli-args.js +82 -12
  94. package/dist/cli-args.js.map +1 -1
  95. package/dist/cli-background-command.d.ts +9 -2
  96. package/dist/cli-background-command.d.ts.map +1 -1
  97. package/dist/cli-background-command.js +179 -66
  98. package/dist/cli-background-command.js.map +1 -1
  99. package/dist/cli-commands.d.ts.map +1 -1
  100. package/dist/cli-commands.js +50 -12
  101. package/dist/cli-commands.js.map +1 -1
  102. package/dist/cli-help.d.ts.map +1 -1
  103. package/dist/cli-help.js +21 -1
  104. package/dist/cli-help.js.map +1 -1
  105. package/dist/config-reference.d.ts.map +1 -1
  106. package/dist/config-reference.js +105 -2
  107. package/dist/config-reference.js.map +1 -1
  108. package/dist/configured-agent.d.ts +64 -21
  109. package/dist/configured-agent.d.ts.map +1 -1
  110. package/dist/configured-agent.js +840 -156
  111. package/dist/configured-agent.js.map +1 -1
  112. package/dist/conversation-state-roots.d.ts +59 -0
  113. package/dist/conversation-state-roots.d.ts.map +1 -0
  114. package/dist/conversation-state-roots.js +248 -0
  115. package/dist/conversation-state-roots.js.map +1 -0
  116. package/dist/cron-control-store.d.ts +91 -0
  117. package/dist/cron-control-store.d.ts.map +1 -0
  118. package/dist/cron-control-store.js +1251 -0
  119. package/dist/cron-control-store.js.map +1 -0
  120. package/dist/cron-operator-service.d.ts +54 -0
  121. package/dist/cron-operator-service.d.ts.map +1 -0
  122. package/dist/cron-operator-service.js +352 -0
  123. package/dist/cron-operator-service.js.map +1 -0
  124. package/dist/doctor-session-history.d.ts +9 -0
  125. package/dist/doctor-session-history.d.ts.map +1 -0
  126. package/dist/doctor-session-history.js +313 -0
  127. package/dist/doctor-session-history.js.map +1 -0
  128. package/dist/doctor.d.ts +2 -1
  129. package/dist/doctor.d.ts.map +1 -1
  130. package/dist/doctor.js +356 -9
  131. package/dist/doctor.js.map +1 -1
  132. package/dist/index.d.ts +4 -0
  133. package/dist/index.d.ts.map +1 -1
  134. package/dist/index.js +2 -0
  135. package/dist/index.js.map +1 -1
  136. package/dist/interaction-bridge.d.ts +1 -0
  137. package/dist/interaction-bridge.d.ts.map +1 -1
  138. package/dist/interaction-bridge.js +54 -6
  139. package/dist/interaction-bridge.js.map +1 -1
  140. package/dist/jobs-command.d.ts +17 -0
  141. package/dist/jobs-command.d.ts.map +1 -0
  142. package/dist/jobs-command.js +207 -0
  143. package/dist/jobs-command.js.map +1 -0
  144. package/dist/launchd-lifecycle-lock.d.ts +36 -0
  145. package/dist/launchd-lifecycle-lock.d.ts.map +1 -0
  146. package/dist/launchd-lifecycle-lock.js +124 -0
  147. package/dist/launchd-lifecycle-lock.js.map +1 -0
  148. package/dist/launchd-log-monitor-status.d.ts +7 -0
  149. package/dist/launchd-log-monitor-status.d.ts.map +1 -0
  150. package/dist/launchd-log-monitor-status.js +224 -0
  151. package/dist/launchd-log-monitor-status.js.map +1 -0
  152. package/dist/launchd-logs.d.ts +9 -1
  153. package/dist/launchd-logs.d.ts.map +1 -1
  154. package/dist/launchd-logs.js +69 -16
  155. package/dist/launchd-logs.js.map +1 -1
  156. package/dist/launchd-maintenance-command.d.ts +10 -0
  157. package/dist/launchd-maintenance-command.d.ts.map +1 -0
  158. package/dist/launchd-maintenance-command.js +2 -0
  159. package/dist/launchd-maintenance-command.js.map +1 -0
  160. package/dist/launchd-maintenance-entry.d.ts +30 -0
  161. package/dist/launchd-maintenance-entry.d.ts.map +1 -0
  162. package/dist/launchd-maintenance-entry.js +206 -0
  163. package/dist/launchd-maintenance-entry.js.map +1 -0
  164. package/dist/launchd-maintenance-gate.d.ts +28 -0
  165. package/dist/launchd-maintenance-gate.d.ts.map +1 -0
  166. package/dist/launchd-maintenance-gate.js +80 -0
  167. package/dist/launchd-maintenance-gate.js.map +1 -0
  168. package/dist/launchd-private-files.d.ts +3 -0
  169. package/dist/launchd-private-files.d.ts.map +1 -0
  170. package/dist/launchd-private-files.js +58 -0
  171. package/dist/launchd-private-files.js.map +1 -0
  172. package/dist/launchd.d.ts +56 -2
  173. package/dist/launchd.d.ts.map +1 -1
  174. package/dist/launchd.js +210 -5
  175. package/dist/launchd.js.map +1 -1
  176. package/dist/local-configuration.d.ts.map +1 -1
  177. package/dist/local-configuration.js +34 -7
  178. package/dist/local-configuration.js.map +1 -1
  179. package/dist/managed-launchd-maintenance-environment.d.ts +3 -0
  180. package/dist/managed-launchd-maintenance-environment.d.ts.map +1 -0
  181. package/dist/managed-launchd-maintenance-environment.js +12 -0
  182. package/dist/managed-launchd-maintenance-environment.js.map +1 -0
  183. package/dist/managed-runtime-launch-proof.d.ts +11 -0
  184. package/dist/managed-runtime-launch-proof.d.ts.map +1 -0
  185. package/dist/managed-runtime-launch-proof.js +59 -0
  186. package/dist/managed-runtime-launch-proof.js.map +1 -0
  187. package/dist/managed-runtime-maintenance-entry.d.ts +10 -0
  188. package/dist/managed-runtime-maintenance-entry.d.ts.map +1 -0
  189. package/dist/managed-runtime-maintenance-entry.js +244 -0
  190. package/dist/managed-runtime-maintenance-entry.js.map +1 -0
  191. package/dist/managed-web-logs.d.ts +29 -11
  192. package/dist/managed-web-logs.d.ts.map +1 -1
  193. package/dist/managed-web-logs.js +244 -121
  194. package/dist/managed-web-logs.js.map +1 -1
  195. package/dist/managed-web-maintenance-environment.d.ts +6 -0
  196. package/dist/managed-web-maintenance-environment.d.ts.map +1 -0
  197. package/dist/managed-web-maintenance-environment.js +19 -0
  198. package/dist/managed-web-maintenance-environment.js.map +1 -0
  199. package/dist/mcp-apps.d.ts +37 -0
  200. package/dist/mcp-apps.d.ts.map +1 -0
  201. package/dist/mcp-apps.js +1753 -0
  202. package/dist/mcp-apps.js.map +1 -0
  203. package/dist/memory-command.d.ts +12 -0
  204. package/dist/memory-command.d.ts.map +1 -1
  205. package/dist/memory-command.js +510 -1
  206. package/dist/memory-command.js.map +1 -1
  207. package/dist/modules/known-tools.d.ts +1 -1
  208. package/dist/modules/known-tools.d.ts.map +1 -1
  209. package/dist/modules/known-tools.js +2 -0
  210. package/dist/modules/known-tools.js.map +1 -1
  211. package/dist/posted-reply-history.d.ts.map +1 -1
  212. package/dist/posted-reply-history.js +12 -0
  213. package/dist/posted-reply-history.js.map +1 -1
  214. package/dist/proactive-notify.d.ts +4 -1
  215. package/dist/proactive-notify.d.ts.map +1 -1
  216. package/dist/proactive-notify.js +43 -5
  217. package/dist/proactive-notify.js.map +1 -1
  218. package/dist/process-jobs-config.d.ts +84 -0
  219. package/dist/process-jobs-config.d.ts.map +1 -0
  220. package/dist/process-jobs-config.js +427 -0
  221. package/dist/process-jobs-config.js.map +1 -0
  222. package/dist/process-jobs-context.d.ts +24 -0
  223. package/dist/process-jobs-context.d.ts.map +1 -0
  224. package/dist/process-jobs-context.js +135 -0
  225. package/dist/process-jobs-context.js.map +1 -0
  226. package/dist/process-jobs-protection.d.ts +33 -0
  227. package/dist/process-jobs-protection.d.ts.map +1 -0
  228. package/dist/process-jobs-protection.js +74 -0
  229. package/dist/process-jobs-protection.js.map +1 -0
  230. package/dist/process-jobs-root-registry.d.ts +115 -0
  231. package/dist/process-jobs-root-registry.d.ts.map +1 -0
  232. package/dist/process-jobs-root-registry.js +978 -0
  233. package/dist/process-jobs-root-registry.js.map +1 -0
  234. package/dist/process-jobs-runtime.d.ts +38 -0
  235. package/dist/process-jobs-runtime.d.ts.map +1 -0
  236. package/dist/process-jobs-runtime.js +178 -0
  237. package/dist/process-jobs-runtime.js.map +1 -0
  238. package/dist/process-jobs-service.d.ts +70 -0
  239. package/dist/process-jobs-service.d.ts.map +1 -0
  240. package/dist/process-jobs-service.js +1989 -0
  241. package/dist/process-jobs-service.js.map +1 -0
  242. package/dist/process-jobs-store.d.ts +151 -0
  243. package/dist/process-jobs-store.d.ts.map +1 -0
  244. package/dist/process-jobs-store.js +1290 -0
  245. package/dist/process-jobs-store.js.map +1 -0
  246. package/dist/reply-artifacts.d.ts +65 -0
  247. package/dist/reply-artifacts.d.ts.map +1 -0
  248. package/dist/reply-artifacts.js +1147 -0
  249. package/dist/reply-artifacts.js.map +1 -0
  250. package/dist/reply-part-budget.d.ts +16 -0
  251. package/dist/reply-part-budget.d.ts.map +1 -0
  252. package/dist/reply-part-budget.js +57 -0
  253. package/dist/reply-part-budget.js.map +1 -0
  254. package/dist/request-model-override.d.ts +19 -0
  255. package/dist/request-model-override.d.ts.map +1 -1
  256. package/dist/request-model-override.js +54 -1
  257. package/dist/request-model-override.js.map +1 -1
  258. package/dist/request-scoped-mcp.d.ts +31 -0
  259. package/dist/request-scoped-mcp.d.ts.map +1 -0
  260. package/dist/request-scoped-mcp.js +197 -0
  261. package/dist/request-scoped-mcp.js.map +1 -0
  262. package/dist/run-history.d.ts.map +1 -1
  263. package/dist/run-history.js +50 -254
  264. package/dist/run-history.js.map +1 -1
  265. package/dist/runtime-option-extensions.d.ts +23 -0
  266. package/dist/runtime-option-extensions.d.ts.map +1 -1
  267. package/dist/runtime-option-extensions.js +55 -0
  268. package/dist/runtime-option-extensions.js.map +1 -1
  269. package/dist/runtime-routes.d.ts +7 -1
  270. package/dist/runtime-routes.d.ts.map +1 -1
  271. package/dist/runtime-routes.js +25 -0
  272. package/dist/runtime-routes.js.map +1 -1
  273. package/dist/secure-file-replace.d.ts +3 -0
  274. package/dist/secure-file-replace.d.ts.map +1 -1
  275. package/dist/secure-file-replace.js +31 -7
  276. package/dist/secure-file-replace.js.map +1 -1
  277. package/dist/session-history.d.ts +146 -0
  278. package/dist/session-history.d.ts.map +1 -0
  279. package/dist/session-history.js +347 -0
  280. package/dist/session-history.js.map +1 -0
  281. package/dist/sessions.d.ts +45 -2
  282. package/dist/sessions.d.ts.map +1 -1
  283. package/dist/sessions.js +849 -30
  284. package/dist/sessions.js.map +1 -1
  285. package/dist/web-command.d.ts +12 -11
  286. package/dist/web-command.d.ts.map +1 -1
  287. package/dist/web-command.js +554 -80
  288. package/dist/web-command.js.map +1 -1
  289. package/dist/web-log-maintenance-command.d.ts +7 -0
  290. package/dist/web-log-maintenance-command.d.ts.map +1 -0
  291. package/dist/web-log-maintenance-command.js +21 -0
  292. package/dist/web-log-maintenance-command.js.map +1 -0
  293. package/dist/web-log-maintenance-status.d.ts +26 -0
  294. package/dist/web-log-maintenance-status.d.ts.map +1 -0
  295. package/dist/web-log-maintenance-status.js +171 -0
  296. package/dist/web-log-maintenance-status.js.map +1 -0
  297. package/dist/web-log-maintenance.d.ts +32 -0
  298. package/dist/web-log-maintenance.d.ts.map +1 -0
  299. package/dist/web-log-maintenance.js +163 -0
  300. package/dist/web-log-maintenance.js.map +1 -0
  301. package/dist/web-maintenance-paths.d.ts +12 -0
  302. package/dist/web-maintenance-paths.d.ts.map +1 -0
  303. package/dist/web-maintenance-paths.js +24 -0
  304. package/dist/web-maintenance-paths.js.map +1 -0
  305. package/dist/wizard/prompts.d.ts.map +1 -1
  306. package/dist/wizard/prompts.js +1 -0
  307. package/dist/wizard/prompts.js.map +1 -1
  308. package/dist/wizard/run.js +1 -1
  309. package/dist/wizard/run.js.map +1 -1
  310. package/package.json +15 -15
  311. package/schema/mono-agent.config.schema.json +106 -1
  312. package/skills/mono-agent-composer/references/config-blueprint.md +27 -2
  313. package/skills/mono-agent-composer/references/discovery-questions.md +2 -2
  314. package/skills/mono-agent-composer/references/feature-coverage.md +6 -4
  315. package/skills/mono-agent-composer/references/package-map.md +19 -3
  316. package/skills/mono-agent-composer/references/playbooks.md +3 -3
  317. 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,11 @@ 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 { attestProcessJobsRootRegistrySnapshot, failedProcessJobsRootRegistryProtection, loadProcessJobsRootRegistryProtection, } from "./process-jobs-root-registry.js";
34
+ import { resolveProcessJobsProtectionPosture, } from "./process-jobs-protection.js";
35
+ 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
36
  import { formatInteractionBridgeUrl, loadInteractionSettings } from "./interaction-bridge.js";
32
37
  import { FIRST_RUN_MEMORY_INITIALIZING_MARKER } from "./first-run-managed-memory.js";
33
38
  import { DEFAULT_MEMORY_EMBEDDING_ENDPOINTS, probeMemoryEmbeddingSelection, } from "./memory-embedding-service.js";
@@ -37,8 +42,11 @@ import { checkManagedProjectSkills, managedProjectSkillsExist } from "./project-
37
42
  import { configuredRuntimeFallbackModels, configuredRuntimeModels } from "./runtime-routes.js";
38
43
  import { runtimeProvenanceDetail } from "./runtime-provenance.js";
39
44
  import { loadSupermemoryPlugin } from "./supermemory-plugin.js";
40
- import { DEFAULT_LAUNCHD_LOG_POLICY, inspectLaunchdLogs, LAUNCHD_LOG_MAINTENANCE_INTERVAL_SECONDS, launchdLogPathsForConfig, } from "./launchd-logs.js";
45
+ import { DEFAULT_LAUNCHD_LOG_POLICY, inspectLaunchdLogs, LAUNCHD_LOG_MONITOR_INTERVAL_SECONDS, launchdLogPathsForConfig, } from "./launchd-logs.js";
46
+ import { readLaunchdLogMonitorStatus } from "./launchd-log-monitor-status.js";
41
47
  import { exporterSection, runsSection } from "./doctor-observability.js";
48
+ import { sessionToolHistorySection } from "./doctor-session-history.js";
49
+ import { runtimeRouteContainsUnsupportedHistoryTool } from "./app-controller-utils.js";
42
50
  const execFile = promisify(execFileCallback);
43
51
  const CONTINUATION_V2_ROLLBACK_GUARD = "UPGRADED-TO-RECORDS-V3";
44
52
  const CONTINUATION_V2_ROLLBACK_GUARD_CONTENT = "This state directory uses continuation records v3. Older runtimes must not open records-v2.\n";
@@ -88,8 +96,13 @@ export async function validateMonoAgentFolder(options) {
88
96
  sections.push(await contextSection(coreConfig, options.cwd));
89
97
  sections.push(await memorySection(coreConfig, options.cwd, options.env, liveness, allowFilesystemWrites, options.preferAppPluginInstall === true));
90
98
  sections.push(await toolsSection(coreConfig, options));
99
+ sections.push(await sessionToolHistorySection({
100
+ historyRoot: join(coreConfig.artifacts.dir, "..", "history"),
101
+ requestScopedToolSupported: !runtimeRouteContainsUnsupportedHistoryTool(coreConfig),
102
+ }));
91
103
  sections.push(await webToolsSection(coreConfig, options, liveness));
92
104
  sections.push(await continuationSection(coreConfig, options));
105
+ sections.push(await processJobsSection(coreConfig, options));
93
106
  sections.push(await sandboxSection(coreConfig, options.sandboxEngine));
94
107
  }
95
108
  sections.push(await exporterSection(options, liveness));
@@ -120,9 +133,20 @@ export async function validateMonoAgentFolder(options) {
120
133
  /** Read-only launchd log inventory used by both `validate` and its `doctor` alias. */
121
134
  export async function launchdLogsSection(configPath) {
122
135
  let inspection;
136
+ let monitorStatus;
123
137
  try {
124
138
  const paths = await launchdLogPathsForConfig(configPath);
125
139
  inspection = await inspectLaunchdLogs(paths);
140
+ const stdoutName = basename(paths.stdoutPath);
141
+ const mainLabel = stdoutName.endsWith(".out.log")
142
+ ? stdoutName.slice(0, -".out.log".length)
143
+ : "";
144
+ try {
145
+ monitorStatus = await readLaunchdLogMonitorStatus(mainLabel, paths);
146
+ }
147
+ catch {
148
+ monitorStatus = "unavailable";
149
+ }
126
150
  }
127
151
  catch {
128
152
  return {
@@ -132,15 +156,16 @@ export async function launchdLogsSection(configPath) {
132
156
  details: ["[WARN] Managed launchd log metadata could not be inspected safely."],
133
157
  };
134
158
  }
135
- return launchdLogsSectionFromInspection(inspection);
159
+ return launchdLogsSectionFromInspection(inspection, monitorStatus);
136
160
  }
137
161
  /** Pure renderer kept separate so exact byte accounting is deterministic in tests. */
138
- export function launchdLogsSectionFromInspection(inspection) {
162
+ export function launchdLogsSectionFromInspection(inspection, monitorStatus) {
139
163
  const policy = DEFAULT_LAUNCHD_LOG_POLICY;
140
164
  const details = [
141
- `Policy: ${policy.maxBytes} bytes per file, ${policy.rotationCount} retained generations, checked every ${LAUNCHD_LOG_MAINTENANCE_INTERVAL_SECONDS} seconds.`,
165
+ `Policy: ${policy.maxBytes} bytes per file, ${policy.rotationCount} retained generations, checked every ${LAUNCHD_LOG_MONITOR_INTERVAL_SECONDS} seconds.`,
142
166
  streamSizeDetail("stdout", inspection.stdout),
143
167
  streamSizeDetail("stderr", inspection.stderr),
168
+ ...monitorStatusDetails(monitorStatus),
144
169
  ...inspection.issues.map((issue) => `[WARN] ${issue}.`),
145
170
  ...oversizedLogDetails("stdout", inspection.stdout, policy.maxBytes),
146
171
  ...oversizedLogDetails("stderr", inspection.stderr, policy.maxBytes),
@@ -160,6 +185,19 @@ export function launchdLogsSectionFromInspection(inspection) {
160
185
  details,
161
186
  };
162
187
  }
188
+ function monitorStatusDetails(status) {
189
+ if (status === "unavailable") {
190
+ return ["Monitor: owner-private status is unavailable or unsafe."];
191
+ }
192
+ if (status === undefined)
193
+ return ["Monitor: no observational snapshot has been recorded yet."];
194
+ return [
195
+ `Monitor last inspection: ${status.lastInspectionAt}.`,
196
+ `Monitor wake count: ${status.wakeCount}.`,
197
+ `Monitor last outcome: ${status.lastOutcome}.`,
198
+ `Monitor cooldown deadline: ${status.cooldownDeadline}.`,
199
+ ];
200
+ }
163
201
  function streamSizeDetail(label, stream) {
164
202
  if (!stream.byteAccountingComplete) {
165
203
  return `${label}: byte inventory unavailable because one or more paths could not be inspected safely.`;
@@ -1910,9 +1948,21 @@ async function probeSearxngEndpoint(endpoint) {
1910
1948
  if (!response.ok)
1911
1949
  return { ok: false, reason: `HTTP ${response.status}` };
1912
1950
  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" };
1951
+ if (!Array.isArray(body.results)) {
1952
+ return { ok: false, reason: "response did not contain a results array" };
1953
+ }
1954
+ // An instance whose engines are all captcha'd or suspended answers 200 with
1955
+ // an empty result list, which the array check alone reported as healthy. The
1956
+ // empty array is not the signal on its own — this probe's query can
1957
+ // legitimately match very little — so only flag it alongside failed engines.
1958
+ const unresponsive = Array.isArray(body.unresponsive_engines) ? body.unresponsive_engines : [];
1959
+ if (body.results.length === 0 && unresponsive.length > 0) {
1960
+ const detail = unresponsive
1961
+ .map((entry) => (Array.isArray(entry) ? `${String(entry[0])}: ${String(entry[1])}` : String(entry)))
1962
+ .join("; ");
1963
+ return { ok: false, reason: `no results, and ${unresponsive.length === 1 ? "1 engine" : `${unresponsive.length} engines`} unresponsive — ${detail}` };
1964
+ }
1965
+ return { ok: true, reason: "" };
1916
1966
  }
1917
1967
  catch (error) {
1918
1968
  return { ok: false, reason: error instanceof Error ? error.message : String(error) };
@@ -2002,6 +2052,303 @@ async function continuationSection(config, input) {
2002
2052
  details,
2003
2053
  };
2004
2054
  }
2055
+ async function processJobsSection(config, input) {
2056
+ let settings;
2057
+ try {
2058
+ settings = await loadProcessJobsSettings({ cwd: input.cwd, configPath: input.configPath, env: input.env });
2059
+ }
2060
+ catch (error) {
2061
+ return {
2062
+ id: "process-jobs",
2063
+ label: "Background process jobs",
2064
+ status: "error",
2065
+ details: [`Process-job configuration is invalid: ${continuationReason(error)}`],
2066
+ };
2067
+ }
2068
+ let unsafeWarning;
2069
+ if (settings.unsafeAllowUnprotectedState) {
2070
+ try {
2071
+ let registry = await loadProcessJobsRootRegistryProtection(input.cwd, config.runtime.workspace);
2072
+ if (registry.kind !== "failed") {
2073
+ try {
2074
+ registry = await attestProcessJobsRootRegistrySnapshot(registry, config.runtime.workspace);
2075
+ }
2076
+ catch {
2077
+ registry = failedProcessJobsRootRegistryProtection(registry.agentRoot);
2078
+ }
2079
+ }
2080
+ const posture = resolveProcessJobsProtectionPosture({ settings, registry, coreConfig: config });
2081
+ if (posture.kind === "unavailable") {
2082
+ return {
2083
+ id: "process-jobs",
2084
+ label: "Background process jobs",
2085
+ status: "error",
2086
+ details: ["Process-job private-state protection is unavailable."],
2087
+ };
2088
+ }
2089
+ unsafeWarning = posture.warning;
2090
+ }
2091
+ catch (error) {
2092
+ return {
2093
+ id: "process-jobs",
2094
+ label: "Background process jobs",
2095
+ status: "error",
2096
+ details: [`Process-job configuration is invalid: ${continuationReason(error)}`],
2097
+ };
2098
+ }
2099
+ }
2100
+ if (!settings.enabled) {
2101
+ return {
2102
+ id: "process-jobs",
2103
+ label: "Background process jobs",
2104
+ status: unsafeWarning === undefined ? "disabled" : "ok",
2105
+ details: [
2106
+ "Background Exec/Bash jobs are opt-in (processJobs.enabled=false).",
2107
+ ...(unsafeWarning === undefined ? [] : [unsafeWarning]),
2108
+ ...(process.platform === "win32" ? ["Windows is unsupported; detached POSIX process-group ownership is required."] : []),
2109
+ ],
2110
+ };
2111
+ }
2112
+ if (process.platform === "win32") {
2113
+ return {
2114
+ id: "process-jobs",
2115
+ label: "Background process jobs",
2116
+ status: "error",
2117
+ details: ["Process jobs are enabled but Windows is unsupported; detached POSIX process-group ownership is required."],
2118
+ };
2119
+ }
2120
+ const details = [
2121
+ ...(unsafeWarning === undefined ? [] : [unsafeWarning]),
2122
+ `Owner-only local state: ${settings.stateDir}.`,
2123
+ `Concurrency: ${String(settings.maxConcurrent)} global, ${String(settings.maxActivePerConversation)} per conversation, ${String(settings.maxQueued)} queued.`,
2124
+ `Caps: runtime=${String(settings.maxRuntimeMs)}ms, queue-age=${String(settings.maxQueueAgeMs)}ms, output=${String(settings.maxOutputBytes)} bytes, chain-depth=${String(settings.maxChainDepth)}.`,
2125
+ `Runtime availability: Pi-native Exec/Bash only; configured primary is ${config.runtime.model.sdk}.`,
2126
+ ];
2127
+ const inspection = await inspectProcessJobState(input.cwd, settings.stateDir);
2128
+ return {
2129
+ id: "process-jobs",
2130
+ label: "Background process jobs",
2131
+ status: inspection.status,
2132
+ details: [...details, ...inspection.details],
2133
+ };
2134
+ }
2135
+ async function inspectProcessJobState(cwd, stateDir) {
2136
+ const confinementError = await inspectProcessJobConfinement(cwd, stateDir);
2137
+ if (confinementError !== undefined) {
2138
+ return { status: "error", details: [confinementError] };
2139
+ }
2140
+ let root;
2141
+ try {
2142
+ root = await lstat(stateDir);
2143
+ }
2144
+ catch (error) {
2145
+ return continuationFsCode(error) === "ENOENT"
2146
+ ? { status: "ok", details: ["State has not been initialized; the app will create it owner-only on first start."] }
2147
+ : { status: "error", details: [`Process-job state cannot be inspected: ${continuationReason(error)}`] };
2148
+ }
2149
+ if (!root.isDirectory() || root.isSymbolicLink()) {
2150
+ return { status: "error", details: ["Process-job state root must be a real directory, not a symlink."] };
2151
+ }
2152
+ const rootSecurity = processJobOwnershipError(root, "state directory", 0o700);
2153
+ if (rootSecurity !== undefined)
2154
+ return { status: "error", details: [rootSecurity] };
2155
+ const healthInspection = await inspectProcessJobHealth(stateDir);
2156
+ if (healthInspection !== undefined)
2157
+ return healthInspection;
2158
+ const quarantineInspection = await inspectProcessJobQuarantine(stateDir);
2159
+ if (quarantineInspection !== undefined)
2160
+ return quarantineInspection;
2161
+ const secretPath = join(stateDir, PROCESS_JOB_SECRET_FILE);
2162
+ try {
2163
+ const secret = await lstat(secretPath);
2164
+ if (!secret.isFile() || secret.isSymbolicLink() || Number(secret.nlink) !== 1 || Number(secret.size) > 256) {
2165
+ return { status: "error", details: ["Process-job operator secret must be one regular file."] };
2166
+ }
2167
+ const secretSecurity = processJobOwnershipError(secret, "operator secret", 0o600);
2168
+ if (secretSecurity !== undefined)
2169
+ return { status: "error", details: [secretSecurity] };
2170
+ }
2171
+ catch (error) {
2172
+ if (continuationFsCode(error) !== "ENOENT") {
2173
+ return { status: "error", details: [`Process-job operator secret cannot be inspected: ${continuationReason(error)}`] };
2174
+ }
2175
+ }
2176
+ const recordsPath = join(stateDir, PROCESS_JOB_RECORDS_DIRECTORY);
2177
+ let names;
2178
+ try {
2179
+ const records = await lstat(recordsPath);
2180
+ if (!records.isDirectory() || records.isSymbolicLink()) {
2181
+ return { status: "error", details: ["Process-job records path must be a real directory."] };
2182
+ }
2183
+ const recordsSecurity = processJobOwnershipError(records, "records directory", 0o700);
2184
+ if (recordsSecurity !== undefined)
2185
+ return { status: "error", details: [recordsSecurity] };
2186
+ names = (await readdir(recordsPath)).filter((name) => name.endsWith(".json"));
2187
+ }
2188
+ catch (error) {
2189
+ return continuationFsCode(error) === "ENOENT"
2190
+ ? { status: "ok", details: ["No local process-job records have been written yet."] }
2191
+ : { status: "error", details: [`Process-job records cannot be inspected: ${continuationReason(error)}`] };
2192
+ }
2193
+ if (names.length > 10_000)
2194
+ return { status: "error", details: ["Process-job record count exceeds the compiled inspection bound of 10000."] };
2195
+ const counts = Object.fromEntries(PROCESS_JOB_STATES.map((state) => [state, 0]));
2196
+ for (const name of names) {
2197
+ const path = join(recordsPath, name);
2198
+ try {
2199
+ const info = await lstat(path);
2200
+ if (!info.isFile() || info.isSymbolicLink() || Number(info.nlink) !== 1)
2201
+ throw new Error("record is not one regular file");
2202
+ const security = processJobOwnershipError(info, `record ${name}`, 0o600);
2203
+ if (security !== undefined)
2204
+ throw new Error(security);
2205
+ if (Number(info.size) > 128 * 1024)
2206
+ throw new Error("record exceeds 131072 bytes");
2207
+ const raw = JSON.parse(await readFile(path, "utf8"));
2208
+ if (!isDoctorObject(raw) || !isProcessJobState(raw.state) || raw.jobId !== name.slice(0, -5)) {
2209
+ throw new Error("record identity or state is invalid");
2210
+ }
2211
+ counts[raw.state] = (counts[raw.state] ?? 0) + 1;
2212
+ }
2213
+ catch (error) {
2214
+ return { status: "error", details: [`Process-job record ${name} is unsafe or malformed: ${continuationReason(error)}`] };
2215
+ }
2216
+ }
2217
+ return {
2218
+ status: "ok",
2219
+ details: [
2220
+ `Local records: ${String(names.length)}.`,
2221
+ `States: ${PROCESS_JOB_STATES.map((state) => `${state}=${String(counts[state] ?? 0)}`).join(", ")}.`,
2222
+ ],
2223
+ };
2224
+ }
2225
+ async function inspectProcessJobHealth(stateDir) {
2226
+ const path = join(stateDir, PROCESS_JOB_HEALTH_FILE);
2227
+ try {
2228
+ const info = await lstat(path);
2229
+ if (!info.isFile() || info.isSymbolicLink() || Number(info.nlink) !== 1) {
2230
+ throw new Error("runtime health marker is not one regular file");
2231
+ }
2232
+ const security = processJobOwnershipError(info, "runtime health marker", 0o600);
2233
+ if (security !== undefined)
2234
+ throw new Error(security);
2235
+ if (Number(info.size) > MAX_PROCESS_JOB_HEALTH_BYTES) {
2236
+ throw new Error(`runtime health marker exceeds ${String(MAX_PROCESS_JOB_HEALTH_BYTES)} bytes`);
2237
+ }
2238
+ const value = JSON.parse(await readFile(path, "utf8"));
2239
+ if (!isDoctorObject(value)
2240
+ || Object.keys(value).sort().join(",") !== "detectedAt,operation,schemaVersion,state"
2241
+ || value.schemaVersion !== 1
2242
+ || value.state !== "degraded"
2243
+ || typeof value.operation !== "string"
2244
+ || !/^[a-z][a-z0-9_.-]{0,63}$/u.test(value.operation)
2245
+ || typeof value.detectedAt !== "string"
2246
+ || !Number.isFinite(Date.parse(value.detectedAt))) {
2247
+ throw new Error("runtime health marker is malformed");
2248
+ }
2249
+ return {
2250
+ status: "error",
2251
+ details: [
2252
+ `Process-job storage degraded during ${value.operation} at ${value.detectedAt}; new admission is closed until a clean restart recovery.`,
2253
+ ],
2254
+ };
2255
+ }
2256
+ catch (error) {
2257
+ return continuationFsCode(error) === "ENOENT"
2258
+ ? undefined
2259
+ : {
2260
+ status: "error",
2261
+ details: [`Process-job runtime health cannot be inspected: ${continuationReason(error)}`],
2262
+ };
2263
+ }
2264
+ }
2265
+ async function inspectProcessJobQuarantine(stateDir) {
2266
+ const quarantinePath = join(stateDir, PROCESS_JOB_QUARANTINE_DIRECTORY);
2267
+ let names;
2268
+ try {
2269
+ const directory = await lstat(quarantinePath);
2270
+ if (!directory.isDirectory() || directory.isSymbolicLink()) {
2271
+ return { status: "error", details: ["Process-job quarantine path must be a real directory."] };
2272
+ }
2273
+ const security = processJobOwnershipError(directory, "quarantine directory", 0o700);
2274
+ if (security !== undefined)
2275
+ return { status: "error", details: [security] };
2276
+ names = await readdir(quarantinePath);
2277
+ }
2278
+ catch (error) {
2279
+ return continuationFsCode(error) === "ENOENT"
2280
+ ? undefined
2281
+ : { status: "error", details: [`Process-job quarantine cannot be inspected: ${continuationReason(error)}`] };
2282
+ }
2283
+ if (names.length > 10_000) {
2284
+ return { status: "error", details: ["Process-job quarantined transaction count exceeds 10000."] };
2285
+ }
2286
+ for (const name of names) {
2287
+ const path = join(quarantinePath, name);
2288
+ try {
2289
+ if (!/^transaction-\d{4}-\d{2}-\d{2}T[0-9.-]+Z-[0-9a-f-]{36}\.json$/iu.test(name)) {
2290
+ throw new Error("quarantine filename is invalid");
2291
+ }
2292
+ const info = await lstat(path);
2293
+ if (!info.isFile() || info.isSymbolicLink() || Number(info.nlink) !== 1) {
2294
+ throw new Error("quarantined transaction is not one regular file");
2295
+ }
2296
+ const security = processJobOwnershipError(info, `quarantined transaction ${name}`, 0o600);
2297
+ if (security !== undefined)
2298
+ throw new Error(security);
2299
+ if (Number(info.size) > 256 * 1024)
2300
+ throw new Error("quarantined transaction exceeds 262144 bytes");
2301
+ }
2302
+ catch (error) {
2303
+ return {
2304
+ status: "error",
2305
+ details: [`Process-job quarantined transaction ${name} is unsafe: ${continuationReason(error)}`],
2306
+ };
2307
+ }
2308
+ }
2309
+ return names.length === 0
2310
+ ? undefined
2311
+ : {
2312
+ status: "error",
2313
+ details: [
2314
+ `Quarantined unreplayable process-job transactions: ${String(names.length)}.`,
2315
+ "The agent can continue without the affected transaction, but operator review is required.",
2316
+ ],
2317
+ };
2318
+ }
2319
+ function processJobOwnershipError(info, label, expectedMode) {
2320
+ if (typeof process.getuid === "function" && Number(info.uid) !== process.getuid()) {
2321
+ return `Process-job ${label} is not owned by the current user.`;
2322
+ }
2323
+ if (process.platform !== "win32" && (Number(info.mode) & 0o777) !== expectedMode) {
2324
+ return `Process-job ${label} permissions must be ${expectedMode.toString(8)}.`;
2325
+ }
2326
+ return undefined;
2327
+ }
2328
+ async function inspectProcessJobConfinement(cwd, stateDir) {
2329
+ const lexicalRoot = resolve(cwd);
2330
+ const root = await realpath(lexicalRoot).catch(() => lexicalRoot);
2331
+ const rel = relative(root, resolve(stateDir));
2332
+ if (rel.length === 0 || rel === ".." || rel.startsWith(`..${sep}`)) {
2333
+ return "Process-job state must be a child directory inside the canonical agent root.";
2334
+ }
2335
+ let current = root;
2336
+ for (const component of rel.split(sep).filter(Boolean)) {
2337
+ current = join(current, component);
2338
+ try {
2339
+ const info = await lstat(current);
2340
+ if (!info.isDirectory() || info.isSymbolicLink()) {
2341
+ return `Process-job state component must be a real directory, not a symlink: ${current}.`;
2342
+ }
2343
+ }
2344
+ catch (error) {
2345
+ if (continuationFsCode(error) === "ENOENT")
2346
+ return undefined;
2347
+ return `Process-job state confinement cannot be inspected: ${continuationReason(error)}`;
2348
+ }
2349
+ }
2350
+ return undefined;
2351
+ }
2005
2352
  async function inspectContinuationState(stateDir, retention) {
2006
2353
  const details = [];
2007
2354
  const retentionPolicy = resolveRetention(retention);