open-multi-agent-kit 0.96.1 → 0.96.2

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 (167) hide show
  1. package/CHANGELOG.md +41 -0
  2. package/README.md +12 -2
  3. package/dist/cli/args.d.ts.map +1 -1
  4. package/dist/cli/args.js +1 -0
  5. package/dist/cli/args.js.map +1 -1
  6. package/dist/commands/resource-doctor-cli.d.ts +22 -0
  7. package/dist/commands/resource-doctor-cli.d.ts.map +1 -0
  8. package/dist/commands/resource-doctor-cli.js +98 -0
  9. package/dist/commands/resource-doctor-cli.js.map +1 -0
  10. package/dist/core/agent-session.d.ts +37 -1
  11. package/dist/core/agent-session.d.ts.map +1 -1
  12. package/dist/core/agent-session.js +289 -5
  13. package/dist/core/agent-session.js.map +1 -1
  14. package/dist/core/completion-sound.d.ts +109 -0
  15. package/dist/core/completion-sound.d.ts.map +1 -0
  16. package/dist/core/completion-sound.js +165 -0
  17. package/dist/core/completion-sound.js.map +1 -0
  18. package/dist/core/context-budget-system-prompt-items.d.ts.map +1 -1
  19. package/dist/core/context-budget-system-prompt-items.js +2 -2
  20. package/dist/core/context-budget-system-prompt-items.js.map +1 -1
  21. package/dist/core/host-resource-probe.d.ts +63 -0
  22. package/dist/core/host-resource-probe.d.ts.map +1 -0
  23. package/dist/core/host-resource-probe.js +126 -0
  24. package/dist/core/host-resource-probe.js.map +1 -0
  25. package/dist/core/host-resource-snapshot.d.ts +54 -0
  26. package/dist/core/host-resource-snapshot.d.ts.map +1 -0
  27. package/dist/core/host-resource-snapshot.js +139 -0
  28. package/dist/core/host-resource-snapshot.js.map +1 -0
  29. package/dist/core/prompt-settlement.d.ts +81 -0
  30. package/dist/core/prompt-settlement.d.ts.map +1 -0
  31. package/dist/core/prompt-settlement.js +84 -0
  32. package/dist/core/prompt-settlement.js.map +1 -0
  33. package/dist/core/provider-resilience.d.ts.map +1 -1
  34. package/dist/core/provider-resilience.js +1 -1
  35. package/dist/core/provider-resilience.js.map +1 -1
  36. package/dist/core/provider-retry.d.ts +7 -0
  37. package/dist/core/provider-retry.d.ts.map +1 -1
  38. package/dist/core/provider-retry.js +24 -0
  39. package/dist/core/provider-retry.js.map +1 -1
  40. package/dist/core/provider-usage.d.ts +15 -2
  41. package/dist/core/provider-usage.d.ts.map +1 -1
  42. package/dist/core/provider-usage.js +110 -2
  43. package/dist/core/provider-usage.js.map +1 -1
  44. package/dist/core/reasoning-router-resolver.d.ts.map +1 -1
  45. package/dist/core/reasoning-router-resolver.js +1 -1
  46. package/dist/core/reasoning-router-resolver.js.map +1 -1
  47. package/dist/core/resource-admission-config.d.ts +55 -0
  48. package/dist/core/resource-admission-config.d.ts.map +1 -0
  49. package/dist/core/resource-admission-config.js +97 -0
  50. package/dist/core/resource-admission-config.js.map +1 -0
  51. package/dist/core/resource-admission.d.ts +81 -0
  52. package/dist/core/resource-admission.d.ts.map +1 -0
  53. package/dist/core/resource-admission.js +128 -0
  54. package/dist/core/resource-admission.js.map +1 -0
  55. package/dist/core/resource-governor-format.d.ts +50 -0
  56. package/dist/core/resource-governor-format.d.ts.map +1 -0
  57. package/dist/core/resource-governor-format.js +113 -0
  58. package/dist/core/resource-governor-format.js.map +1 -0
  59. package/dist/core/resource-governor-settings.d.ts +56 -0
  60. package/dist/core/resource-governor-settings.d.ts.map +1 -0
  61. package/dist/core/resource-governor-settings.js +89 -0
  62. package/dist/core/resource-governor-settings.js.map +1 -0
  63. package/dist/core/resource-observation-journal.d.ts +74 -0
  64. package/dist/core/resource-observation-journal.d.ts.map +1 -0
  65. package/dist/core/resource-observation-journal.js +198 -0
  66. package/dist/core/resource-observation-journal.js.map +1 -0
  67. package/dist/core/resource-safety-gate.d.ts +54 -0
  68. package/dist/core/resource-safety-gate.d.ts.map +1 -0
  69. package/dist/core/resource-safety-gate.js +51 -0
  70. package/dist/core/resource-safety-gate.js.map +1 -0
  71. package/dist/core/run-journal.d.ts.map +1 -1
  72. package/dist/core/run-journal.js +13 -1
  73. package/dist/core/run-journal.js.map +1 -1
  74. package/dist/core/run-resource-lease.d.ts +73 -0
  75. package/dist/core/run-resource-lease.d.ts.map +1 -0
  76. package/dist/core/run-resource-lease.js +78 -0
  77. package/dist/core/run-resource-lease.js.map +1 -0
  78. package/dist/core/session-bash-service.d.ts +12 -0
  79. package/dist/core/session-bash-service.d.ts.map +1 -1
  80. package/dist/core/session-bash-service.js +14 -0
  81. package/dist/core/session-bash-service.js.map +1 -1
  82. package/dist/core/session-system-prompt.d.ts +2 -0
  83. package/dist/core/session-system-prompt.d.ts.map +1 -1
  84. package/dist/core/session-system-prompt.js +2 -0
  85. package/dist/core/session-system-prompt.js.map +1 -1
  86. package/dist/core/session-termination.d.ts +7 -2
  87. package/dist/core/session-termination.d.ts.map +1 -1
  88. package/dist/core/session-termination.js +29 -1
  89. package/dist/core/session-termination.js.map +1 -1
  90. package/dist/core/settings-manager.d.ts +12 -0
  91. package/dist/core/settings-manager.d.ts.map +1 -1
  92. package/dist/core/settings-manager.js +36 -0
  93. package/dist/core/settings-manager.js.map +1 -1
  94. package/dist/core/slash-commands.d.ts.map +1 -1
  95. package/dist/core/slash-commands.js +1 -0
  96. package/dist/core/slash-commands.js.map +1 -1
  97. package/dist/core/subagent-lane-launcher.d.ts +64 -0
  98. package/dist/core/subagent-lane-launcher.d.ts.map +1 -0
  99. package/dist/core/subagent-lane-launcher.js +107 -0
  100. package/dist/core/subagent-lane-launcher.js.map +1 -0
  101. package/dist/core/system-cpu-sampler.d.ts +45 -0
  102. package/dist/core/system-cpu-sampler.d.ts.map +1 -0
  103. package/dist/core/system-cpu-sampler.js +95 -0
  104. package/dist/core/system-cpu-sampler.js.map +1 -0
  105. package/dist/core/system-prompt.d.ts.map +1 -1
  106. package/dist/core/system-prompt.js +5 -8
  107. package/dist/core/system-prompt.js.map +1 -1
  108. package/dist/core/workload-classifier.d.ts +32 -0
  109. package/dist/core/workload-classifier.d.ts.map +1 -0
  110. package/dist/core/workload-classifier.js +66 -0
  111. package/dist/core/workload-classifier.js.map +1 -0
  112. package/dist/core/workload-family-matcher.d.ts +3 -0
  113. package/dist/core/workload-family-matcher.d.ts.map +1 -0
  114. package/dist/core/workload-family-matcher.js +126 -0
  115. package/dist/core/workload-family-matcher.js.map +1 -0
  116. package/dist/core/workload-permit-pool.d.ts +78 -0
  117. package/dist/core/workload-permit-pool.d.ts.map +1 -0
  118. package/dist/core/workload-permit-pool.js +138 -0
  119. package/dist/core/workload-permit-pool.js.map +1 -0
  120. package/dist/core/workload-shard-executor.d.ts +68 -0
  121. package/dist/core/workload-shard-executor.d.ts.map +1 -0
  122. package/dist/core/workload-shard-executor.js +156 -0
  123. package/dist/core/workload-shard-executor.js.map +1 -0
  124. package/dist/core/workload-shard-plan.d.ts +81 -0
  125. package/dist/core/workload-shard-plan.d.ts.map +1 -0
  126. package/dist/core/workload-shard-plan.js +183 -0
  127. package/dist/core/workload-shard-plan.js.map +1 -0
  128. package/dist/core/workload-shard-runner.d.ts +11 -0
  129. package/dist/core/workload-shard-runner.d.ts.map +1 -0
  130. package/dist/core/workload-shard-runner.js +98 -0
  131. package/dist/core/workload-shard-runner.js.map +1 -0
  132. package/dist/core/workload-shard-store.d.ts +44 -0
  133. package/dist/core/workload-shard-store.d.ts.map +1 -0
  134. package/dist/core/workload-shard-store.js +189 -0
  135. package/dist/core/workload-shard-store.js.map +1 -0
  136. package/dist/core/workload-sharders.d.ts +47 -0
  137. package/dist/core/workload-sharders.d.ts.map +1 -0
  138. package/dist/core/workload-sharders.js +162 -0
  139. package/dist/core/workload-sharders.js.map +1 -0
  140. package/dist/core/workload-shell-scan.d.ts +12 -0
  141. package/dist/core/workload-shell-scan.d.ts.map +1 -0
  142. package/dist/core/workload-shell-scan.js +79 -0
  143. package/dist/core/workload-shell-scan.js.map +1 -0
  144. package/dist/main.d.ts.map +1 -1
  145. package/dist/main.js +6 -0
  146. package/dist/main.js.map +1 -1
  147. package/dist/modes/interactive/interactive-mode.d.ts +4 -0
  148. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  149. package/dist/modes/interactive/interactive-mode.js +81 -0
  150. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  151. package/docs/providers.md +1 -1
  152. package/docs/sdk.md +4 -0
  153. package/docs/settings.md +64 -2
  154. package/docs/skills.md +16 -0
  155. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  156. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  157. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  158. package/examples/extensions/gondolin/package-lock.json +2 -2
  159. package/examples/extensions/gondolin/package.json +1 -1
  160. package/examples/extensions/sandbox/package-lock.json +2 -2
  161. package/examples/extensions/sandbox/package.json +1 -1
  162. package/examples/extensions/subagent/agent-capability-router.test.ts +7 -0
  163. package/examples/extensions/subagent/domain-profiles.ts +0 -1
  164. package/examples/extensions/with-deps/package-lock.json +2 -2
  165. package/examples/extensions/with-deps/package.json +1 -1
  166. package/npm-shrinkwrap.json +18 -18
  167. package/package.json +7 -9
@@ -0,0 +1,113 @@
1
+ import { effectiveAvailableMemoryBytes } from "./host-resource-snapshot.js";
2
+ const REASON_LABELS = {
3
+ "resource.memory.low": "memory low",
4
+ "resource.memory.critical": "memory critical",
5
+ "resource.disk.low": "disk low",
6
+ "resource.disk.critical": "disk critical",
7
+ "resource.cpu.busy": "CPU busy",
8
+ "resource.heap.high": "heap high",
9
+ "resource.heap.critical": "heap critical",
10
+ "resource.probe.partial": "partial probe",
11
+ "resource.probe.timeout": "probe timeout",
12
+ };
13
+ /** Humanize reason codes for the §19.1 summary line ("memory low, CPU busy"). */
14
+ export function describeResourceReasons(reasons) {
15
+ if (reasons.length === 0) {
16
+ return "none";
17
+ }
18
+ return reasons.map((reason) => REASON_LABELS[reason] ?? reason).join(", ");
19
+ }
20
+ /** §19.1 summary block shared by `/resource` and the doctor's human output. */
21
+ export function formatResourceSummaryLines(mode, decision) {
22
+ return [
23
+ `mode: ${mode}`,
24
+ `pressure: ${decision.pressure}`,
25
+ `action: ${decision.action}`,
26
+ `tool concurrency: ${decision.maxToolConcurrency}`,
27
+ `subagent lanes: ${decision.maxParallelLanes}`,
28
+ `heavy processes: ${decision.maxHeavyProcesses}`,
29
+ `reasons: ${describeResourceReasons(decision.reasons)}`,
30
+ ];
31
+ }
32
+ /**
33
+ * Verbose probe details for explicit local diagnostics only (§19.1 verbose,
34
+ * §11.3: raw values never enter tool results or model prompts).
35
+ */
36
+ export function formatResourceProbeLines(snapshot) {
37
+ const heapRatio = snapshot.heapLimitBytes > 0 ? snapshot.heapUsedBytes / snapshot.heapLimitBytes : null;
38
+ const lines = [
39
+ `observed at: ${snapshot.observedAt}`,
40
+ `environment: ${snapshot.environment} (${snapshot.platform}/${snapshot.arch}, ${snapshot.logicalCpuCount} CPUs)`,
41
+ `effective available memory: ${formatMiB(effectiveAvailableMemoryBytes(snapshot))}`,
42
+ ` process available: ${formatMiB(snapshot.processAvailableMemoryBytes)}`,
43
+ ` constrained limit: ${formatMiB(snapshot.constrainedMemoryBytes)}`,
44
+ ` host free: ${formatMiB(snapshot.hostFreeMemoryBytes)} of ${formatMiB(snapshot.hostTotalMemoryBytes)}`,
45
+ `workspace disk available: ${formatMiB(snapshot.workspaceAvailableBytes)}`,
46
+ `heap: ${formatMiB(snapshot.heapUsedBytes)} of ${formatMiB(snapshot.heapLimitBytes)}${heapRatio === null ? "" : ` (${(heapRatio * 100).toFixed(1)}%)`}`,
47
+ `system CPU: ${snapshot.systemCpuPercent === null ? "unknown" : `${snapshot.systemCpuPercent.toFixed(1)}%`}`,
48
+ ];
49
+ if (snapshot.diagnostics.length > 0) {
50
+ lines.push(`diagnostics: ${snapshot.diagnostics.join(", ")}`);
51
+ }
52
+ return lines;
53
+ }
54
+ /** Policy view: mode, §6.7 thresholds, §7.2 caps, and §18.1 validation errors. */
55
+ export function formatResourcePolicyLines(resolved) {
56
+ const { thresholds, caps } = resolved.admission;
57
+ const lines = [
58
+ `mode: ${resolved.mode}`,
59
+ `probe deadline: ${resolved.maxProbeMs ?? 300} ms, CPU sample: ${resolved.cpuSampleMs ?? 180} ms`,
60
+ `memory MiB (constrained/critical): ${thresholds.constrainedAvailableMemoryMiB}/${thresholds.criticalAvailableMemoryMiB}`,
61
+ `disk MiB (constrained/critical): ${thresholds.constrainedDiskFreeMiB}/${thresholds.criticalDiskFreeMiB}`,
62
+ `heap ratio (constrained/critical): ${thresholds.constrainedHeapRatio}/${thresholds.criticalHeapRatio}`,
63
+ `busy CPU percent: ${thresholds.busyCpuPercent}`,
64
+ `caps normal: tools ${caps.normal.maxToolConcurrency}, lanes ${caps.normal.maxParallelLanes}, heavy ${caps.normal.maxHeavyProcesses}`,
65
+ `caps constrained: tools ${caps.constrained.maxToolConcurrency}, lanes ${caps.constrained.maxParallelLanes}, heavy ${caps.constrained.maxHeavyProcesses}`,
66
+ `caps critical: tools ${caps.critical.maxToolConcurrency}, lanes ${caps.critical.maxParallelLanes}, heavy ${caps.critical.maxHeavyProcesses}`,
67
+ ];
68
+ for (const error of resolved.errors) {
69
+ lines.push(`error: ${error}`);
70
+ }
71
+ return lines;
72
+ }
73
+ export function buildResourceDoctorReport(input) {
74
+ const { snapshot, decision, resolved } = input;
75
+ const heapRatio = snapshot.heapLimitBytes > 0 ? snapshot.heapUsedBytes / snapshot.heapLimitBytes : null;
76
+ return {
77
+ schemaVersion: 1,
78
+ command: "resource_doctor",
79
+ mode: resolved.mode,
80
+ pressure: decision.pressure,
81
+ action: decision.action,
82
+ caps: {
83
+ maxToolConcurrency: decision.maxToolConcurrency,
84
+ maxParallelLanes: decision.maxParallelLanes,
85
+ maxHeavyProcesses: decision.maxHeavyProcesses,
86
+ },
87
+ reasons: decision.reasons,
88
+ probeDurationMs: Math.round(input.probeDurationMs),
89
+ snapshot: {
90
+ environment: snapshot.environment,
91
+ logicalCpuCount: snapshot.logicalCpuCount,
92
+ effectiveAvailableMemoryMiB: toMiB(effectiveAvailableMemoryBytes(snapshot)),
93
+ processAvailableMemoryMiB: toMiB(snapshot.processAvailableMemoryBytes),
94
+ constrainedMemoryMiB: toMiB(snapshot.constrainedMemoryBytes),
95
+ hostFreeMemoryMiB: toMiB(snapshot.hostFreeMemoryBytes),
96
+ hostTotalMemoryMiB: toMiB(snapshot.hostTotalMemoryBytes),
97
+ workspaceAvailableMiB: toMiB(snapshot.workspaceAvailableBytes),
98
+ heapUsedRatio: heapRatio === null ? null : Number(heapRatio.toFixed(4)),
99
+ systemCpuPercent: snapshot.systemCpuPercent,
100
+ diagnostics: snapshot.diagnostics,
101
+ },
102
+ settingsErrors: resolved.errors,
103
+ };
104
+ }
105
+ const MIB = 1024 * 1024;
106
+ function toMiB(bytes) {
107
+ return bytes === null ? null : Math.round(bytes / MIB);
108
+ }
109
+ function formatMiB(bytes) {
110
+ const mib = toMiB(bytes);
111
+ return mib === null ? "unknown" : `${mib.toLocaleString()} MiB`;
112
+ }
113
+ //# sourceMappingURL=resource-governor-format.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resource-governor-format.js","sourceRoot":"","sources":["../../src/core/resource-governor-format.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,6BAA6B,EAAE,MAAM,6BAA6B,CAAC;AAI5E,MAAM,aAAa,GAAuC;IACzD,qBAAqB,EAAE,YAAY;IACnC,0BAA0B,EAAE,iBAAiB;IAC7C,mBAAmB,EAAE,UAAU;IAC/B,wBAAwB,EAAE,eAAe;IACzC,mBAAmB,EAAE,UAAU;IAC/B,oBAAoB,EAAE,WAAW;IACjC,wBAAwB,EAAE,eAAe;IACzC,wBAAwB,EAAE,eAAe;IACzC,wBAAwB,EAAE,eAAe;CACzC,CAAC;AAEF,kFAAiF;AACjF,MAAM,UAAU,uBAAuB,CAAC,OAAsC,EAAU;IACvF,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC1B,OAAO,MAAM,CAAC;IACf,CAAC;IACD,OAAO,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,MAAM,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAAA,CAC3E;AAED,gFAA+E;AAC/E,MAAM,UAAU,0BAA0B,CAAC,IAA0B,EAAE,QAAmC,EAAY;IACrH,OAAO;QACN,SAAS,IAAI,EAAE;QACf,aAAa,QAAQ,CAAC,QAAQ,EAAE;QAChC,WAAW,QAAQ,CAAC,MAAM,EAAE;QAC5B,qBAAqB,QAAQ,CAAC,kBAAkB,EAAE;QAClD,mBAAmB,QAAQ,CAAC,gBAAgB,EAAE;QAC9C,oBAAoB,QAAQ,CAAC,iBAAiB,EAAE;QAChD,YAAY,uBAAuB,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE;KACvD,CAAC;AAAA,CACF;AAED;;;GAGG;AACH,MAAM,UAAU,wBAAwB,CAAC,QAA8B,EAAY;IAClF,MAAM,SAAS,GAAG,QAAQ,CAAC,cAAc,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,aAAa,GAAG,QAAQ,CAAC,cAAc,CAAC,CAAC,CAAC,IAAI,CAAC;IACxG,MAAM,KAAK,GAAG;QACb,gBAAgB,QAAQ,CAAC,UAAU,EAAE;QACrC,gBAAgB,QAAQ,CAAC,WAAW,KAAK,QAAQ,CAAC,QAAQ,IAAI,QAAQ,CAAC,IAAI,KAAK,QAAQ,CAAC,eAAe,QAAQ;QAChH,+BAA+B,SAAS,CAAC,6BAA6B,CAAC,QAAQ,CAAC,CAAC,EAAE;QACnF,wBAAwB,SAAS,CAAC,QAAQ,CAAC,2BAA2B,CAAC,EAAE;QACzE,wBAAwB,SAAS,CAAC,QAAQ,CAAC,sBAAsB,CAAC,EAAE;QACpE,gBAAgB,SAAS,CAAC,QAAQ,CAAC,mBAAmB,CAAC,OAAO,SAAS,CAAC,QAAQ,CAAC,oBAAoB,CAAC,EAAE;QACxG,6BAA6B,SAAS,CAAC,QAAQ,CAAC,uBAAuB,CAAC,EAAE;QAC1E,SAAS,SAAS,CAAC,QAAQ,CAAC,aAAa,CAAC,OAAO,SAAS,CAAC,QAAQ,CAAC,cAAc,CAAC,GAAG,SAAS,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,SAAS,GAAG,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,EAAE;QACvJ,eAAe,QAAQ,CAAC,gBAAgB,KAAK,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,QAAQ,CAAC,gBAAgB,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,EAAE;KAC5G,CAAC;IACF,IAAI,QAAQ,CAAC,WAAW,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACrC,KAAK,CAAC,IAAI,CAAC,gBAAgB,QAAQ,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAC/D,CAAC;IACD,OAAO,KAAK,CAAC;AAAA,CACb;AAED,qFAAkF;AAClF,MAAM,UAAU,yBAAyB,CAAC,QAA0C,EAAY;IAC/F,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,GAAG,QAAQ,CAAC,SAAS,CAAC;IAChD,MAAM,KAAK,GAAG;QACb,SAAS,QAAQ,CAAC,IAAI,EAAE;QACxB,mBAAmB,QAAQ,CAAC,UAAU,IAAI,GAAG,oBAAoB,QAAQ,CAAC,WAAW,IAAI,GAAG,KAAK;QACjG,sCAAsC,UAAU,CAAC,6BAA6B,IAAI,UAAU,CAAC,0BAA0B,EAAE;QACzH,oCAAoC,UAAU,CAAC,sBAAsB,IAAI,UAAU,CAAC,mBAAmB,EAAE;QACzG,sCAAsC,UAAU,CAAC,oBAAoB,IAAI,UAAU,CAAC,iBAAiB,EAAE;QACvG,qBAAqB,UAAU,CAAC,cAAc,EAAE;QAChD,sBAAsB,IAAI,CAAC,MAAM,CAAC,kBAAkB,WAAW,IAAI,CAAC,MAAM,CAAC,gBAAgB,WAAW,IAAI,CAAC,MAAM,CAAC,iBAAiB,EAAE;QACrI,2BAA2B,IAAI,CAAC,WAAW,CAAC,kBAAkB,WAAW,IAAI,CAAC,WAAW,CAAC,gBAAgB,WAAW,IAAI,CAAC,WAAW,CAAC,iBAAiB,EAAE;QACzJ,wBAAwB,IAAI,CAAC,QAAQ,CAAC,kBAAkB,WAAW,IAAI,CAAC,QAAQ,CAAC,gBAAgB,WAAW,IAAI,CAAC,QAAQ,CAAC,iBAAiB,EAAE;KAC7I,CAAC;IACF,KAAK,MAAM,KAAK,IAAI,QAAQ,CAAC,MAAM,EAAE,CAAC;QACrC,KAAK,CAAC,IAAI,CAAC,UAAU,KAAK,EAAE,CAAC,CAAC;IAC/B,CAAC;IACD,OAAO,KAAK,CAAC;AAAA,CACb;AAgCD,MAAM,UAAU,yBAAyB,CAAC,KAKzC,EAAwB;IACxB,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,GAAG,KAAK,CAAC;IAC/C,MAAM,SAAS,GAAG,QAAQ,CAAC,cAAc,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,aAAa,GAAG,QAAQ,CAAC,cAAc,CAAC,CAAC,CAAC,IAAI,CAAC;IACxG,OAAO;QACN,aAAa,EAAE,CAAC;QAChB,OAAO,EAAE,iBAAiB;QAC1B,IAAI,EAAE,QAAQ,CAAC,IAAI;QACnB,QAAQ,EAAE,QAAQ,CAAC,QAAQ;QAC3B,MAAM,EAAE,QAAQ,CAAC,MAAM;QACvB,IAAI,EAAE;YACL,kBAAkB,EAAE,QAAQ,CAAC,kBAAkB;YAC/C,gBAAgB,EAAE,QAAQ,CAAC,gBAAgB;YAC3C,iBAAiB,EAAE,QAAQ,CAAC,iBAAiB;SAC7C;QACD,OAAO,EAAE,QAAQ,CAAC,OAAO;QACzB,eAAe,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,eAAe,CAAC;QAClD,QAAQ,EAAE;YACT,WAAW,EAAE,QAAQ,CAAC,WAAW;YACjC,eAAe,EAAE,QAAQ,CAAC,eAAe;YACzC,2BAA2B,EAAE,KAAK,CAAC,6BAA6B,CAAC,QAAQ,CAAC,CAAC;YAC3E,yBAAyB,EAAE,KAAK,CAAC,QAAQ,CAAC,2BAA2B,CAAC;YACtE,oBAAoB,EAAE,KAAK,CAAC,QAAQ,CAAC,sBAAsB,CAAC;YAC5D,iBAAiB,EAAE,KAAK,CAAC,QAAQ,CAAC,mBAAmB,CAAC;YACtD,kBAAkB,EAAE,KAAK,CAAC,QAAQ,CAAC,oBAAoB,CAAC;YACxD,qBAAqB,EAAE,KAAK,CAAC,QAAQ,CAAC,uBAAuB,CAAC;YAC9D,aAAa,EAAE,SAAS,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;YACvE,gBAAgB,EAAE,QAAQ,CAAC,gBAAgB;YAC3C,WAAW,EAAE,QAAQ,CAAC,WAAW;SACjC;QACD,cAAc,EAAE,QAAQ,CAAC,MAAM;KAC/B,CAAC;AAAA,CACF;AAED,MAAM,GAAG,GAAG,IAAI,GAAG,IAAI,CAAC;AAExB,SAAS,KAAK,CAAC,KAAoB,EAAiB;IACnD,OAAO,KAAK,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,GAAG,CAAC,CAAC;AAAA,CACvD;AAED,SAAS,SAAS,CAAC,KAAoB,EAAU;IAChD,MAAM,GAAG,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC;IACzB,OAAO,GAAG,KAAK,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,cAAc,EAAE,MAAM,CAAC;AAAA,CAChE","sourcesContent":["import type { HostResourceSnapshot } from \"./host-resource-snapshot.ts\";\nimport { effectiveAvailableMemoryBytes } from \"./host-resource-snapshot.ts\";\nimport type { ResourceAdmissionDecision, ResourceGovernorMode, ResourceReasonCode } from \"./resource-admission.ts\";\nimport type { ResolvedResourceGovernorSettings } from \"./resource-governor-settings.ts\";\n\nconst REASON_LABELS: Record<ResourceReasonCode, string> = {\n\t\"resource.memory.low\": \"memory low\",\n\t\"resource.memory.critical\": \"memory critical\",\n\t\"resource.disk.low\": \"disk low\",\n\t\"resource.disk.critical\": \"disk critical\",\n\t\"resource.cpu.busy\": \"CPU busy\",\n\t\"resource.heap.high\": \"heap high\",\n\t\"resource.heap.critical\": \"heap critical\",\n\t\"resource.probe.partial\": \"partial probe\",\n\t\"resource.probe.timeout\": \"probe timeout\",\n};\n\n/** Humanize reason codes for the §19.1 summary line (\"memory low, CPU busy\"). */\nexport function describeResourceReasons(reasons: readonly ResourceReasonCode[]): string {\n\tif (reasons.length === 0) {\n\t\treturn \"none\";\n\t}\n\treturn reasons.map((reason) => REASON_LABELS[reason] ?? reason).join(\", \");\n}\n\n/** §19.1 summary block shared by `/resource` and the doctor's human output. */\nexport function formatResourceSummaryLines(mode: ResourceGovernorMode, decision: ResourceAdmissionDecision): string[] {\n\treturn [\n\t\t`mode: ${mode}`,\n\t\t`pressure: ${decision.pressure}`,\n\t\t`action: ${decision.action}`,\n\t\t`tool concurrency: ${decision.maxToolConcurrency}`,\n\t\t`subagent lanes: ${decision.maxParallelLanes}`,\n\t\t`heavy processes: ${decision.maxHeavyProcesses}`,\n\t\t`reasons: ${describeResourceReasons(decision.reasons)}`,\n\t];\n}\n\n/**\n * Verbose probe details for explicit local diagnostics only (§19.1 verbose,\n * §11.3: raw values never enter tool results or model prompts).\n */\nexport function formatResourceProbeLines(snapshot: HostResourceSnapshot): string[] {\n\tconst heapRatio = snapshot.heapLimitBytes > 0 ? snapshot.heapUsedBytes / snapshot.heapLimitBytes : null;\n\tconst lines = [\n\t\t`observed at: ${snapshot.observedAt}`,\n\t\t`environment: ${snapshot.environment} (${snapshot.platform}/${snapshot.arch}, ${snapshot.logicalCpuCount} CPUs)`,\n\t\t`effective available memory: ${formatMiB(effectiveAvailableMemoryBytes(snapshot))}`,\n\t\t` process available: ${formatMiB(snapshot.processAvailableMemoryBytes)}`,\n\t\t` constrained limit: ${formatMiB(snapshot.constrainedMemoryBytes)}`,\n\t\t` host free: ${formatMiB(snapshot.hostFreeMemoryBytes)} of ${formatMiB(snapshot.hostTotalMemoryBytes)}`,\n\t\t`workspace disk available: ${formatMiB(snapshot.workspaceAvailableBytes)}`,\n\t\t`heap: ${formatMiB(snapshot.heapUsedBytes)} of ${formatMiB(snapshot.heapLimitBytes)}${heapRatio === null ? \"\" : ` (${(heapRatio * 100).toFixed(1)}%)`}`,\n\t\t`system CPU: ${snapshot.systemCpuPercent === null ? \"unknown\" : `${snapshot.systemCpuPercent.toFixed(1)}%`}`,\n\t];\n\tif (snapshot.diagnostics.length > 0) {\n\t\tlines.push(`diagnostics: ${snapshot.diagnostics.join(\", \")}`);\n\t}\n\treturn lines;\n}\n\n/** Policy view: mode, §6.7 thresholds, §7.2 caps, and §18.1 validation errors. */\nexport function formatResourcePolicyLines(resolved: ResolvedResourceGovernorSettings): string[] {\n\tconst { thresholds, caps } = resolved.admission;\n\tconst lines = [\n\t\t`mode: ${resolved.mode}`,\n\t\t`probe deadline: ${resolved.maxProbeMs ?? 300} ms, CPU sample: ${resolved.cpuSampleMs ?? 180} ms`,\n\t\t`memory MiB (constrained/critical): ${thresholds.constrainedAvailableMemoryMiB}/${thresholds.criticalAvailableMemoryMiB}`,\n\t\t`disk MiB (constrained/critical): ${thresholds.constrainedDiskFreeMiB}/${thresholds.criticalDiskFreeMiB}`,\n\t\t`heap ratio (constrained/critical): ${thresholds.constrainedHeapRatio}/${thresholds.criticalHeapRatio}`,\n\t\t`busy CPU percent: ${thresholds.busyCpuPercent}`,\n\t\t`caps normal: tools ${caps.normal.maxToolConcurrency}, lanes ${caps.normal.maxParallelLanes}, heavy ${caps.normal.maxHeavyProcesses}`,\n\t\t`caps constrained: tools ${caps.constrained.maxToolConcurrency}, lanes ${caps.constrained.maxParallelLanes}, heavy ${caps.constrained.maxHeavyProcesses}`,\n\t\t`caps critical: tools ${caps.critical.maxToolConcurrency}, lanes ${caps.critical.maxParallelLanes}, heavy ${caps.critical.maxHeavyProcesses}`,\n\t];\n\tfor (const error of resolved.errors) {\n\t\tlines.push(`error: ${error}`);\n\t}\n\treturn lines;\n}\n\n/** Bounded JSON schema for `omk doctor resources --json` (§19.2). */\nexport interface ResourceDoctorReport {\n\treadonly schemaVersion: 1;\n\treadonly command: \"resource_doctor\";\n\treadonly mode: ResourceGovernorMode;\n\treadonly pressure: ResourceAdmissionDecision[\"pressure\"];\n\treadonly action: ResourceAdmissionDecision[\"action\"];\n\treadonly caps: {\n\t\treadonly maxToolConcurrency: number;\n\t\treadonly maxParallelLanes: number;\n\t\treadonly maxHeavyProcesses: number;\n\t};\n\treadonly reasons: readonly ResourceReasonCode[];\n\treadonly probeDurationMs: number;\n\treadonly snapshot: {\n\t\treadonly environment: HostResourceSnapshot[\"environment\"];\n\t\treadonly logicalCpuCount: number;\n\t\treadonly effectiveAvailableMemoryMiB: number | null;\n\t\treadonly processAvailableMemoryMiB: number | null;\n\t\treadonly constrainedMemoryMiB: number | null;\n\t\treadonly hostFreeMemoryMiB: number | null;\n\t\treadonly hostTotalMemoryMiB: number | null;\n\t\treadonly workspaceAvailableMiB: number | null;\n\t\treadonly heapUsedRatio: number | null;\n\t\treadonly systemCpuPercent: number | null;\n\t\treadonly diagnostics: readonly string[];\n\t};\n\treadonly settingsErrors: readonly string[];\n}\n\nexport function buildResourceDoctorReport(input: {\n\treadonly snapshot: HostResourceSnapshot;\n\treadonly decision: ResourceAdmissionDecision;\n\treadonly resolved: ResolvedResourceGovernorSettings;\n\treadonly probeDurationMs: number;\n}): ResourceDoctorReport {\n\tconst { snapshot, decision, resolved } = input;\n\tconst heapRatio = snapshot.heapLimitBytes > 0 ? snapshot.heapUsedBytes / snapshot.heapLimitBytes : null;\n\treturn {\n\t\tschemaVersion: 1,\n\t\tcommand: \"resource_doctor\",\n\t\tmode: resolved.mode,\n\t\tpressure: decision.pressure,\n\t\taction: decision.action,\n\t\tcaps: {\n\t\t\tmaxToolConcurrency: decision.maxToolConcurrency,\n\t\t\tmaxParallelLanes: decision.maxParallelLanes,\n\t\t\tmaxHeavyProcesses: decision.maxHeavyProcesses,\n\t\t},\n\t\treasons: decision.reasons,\n\t\tprobeDurationMs: Math.round(input.probeDurationMs),\n\t\tsnapshot: {\n\t\t\tenvironment: snapshot.environment,\n\t\t\tlogicalCpuCount: snapshot.logicalCpuCount,\n\t\t\teffectiveAvailableMemoryMiB: toMiB(effectiveAvailableMemoryBytes(snapshot)),\n\t\t\tprocessAvailableMemoryMiB: toMiB(snapshot.processAvailableMemoryBytes),\n\t\t\tconstrainedMemoryMiB: toMiB(snapshot.constrainedMemoryBytes),\n\t\t\thostFreeMemoryMiB: toMiB(snapshot.hostFreeMemoryBytes),\n\t\t\thostTotalMemoryMiB: toMiB(snapshot.hostTotalMemoryBytes),\n\t\t\tworkspaceAvailableMiB: toMiB(snapshot.workspaceAvailableBytes),\n\t\t\theapUsedRatio: heapRatio === null ? null : Number(heapRatio.toFixed(4)),\n\t\t\tsystemCpuPercent: snapshot.systemCpuPercent,\n\t\t\tdiagnostics: snapshot.diagnostics,\n\t\t},\n\t\tsettingsErrors: resolved.errors,\n\t};\n}\n\nconst MIB = 1024 * 1024;\n\nfunction toMiB(bytes: number | null): number | null {\n\treturn bytes === null ? null : Math.round(bytes / MIB);\n}\n\nfunction formatMiB(bytes: number | null): string {\n\tconst mib = toMiB(bytes);\n\treturn mib === null ? \"unknown\" : `${mib.toLocaleString()} MiB`;\n}\n"]}
@@ -0,0 +1,56 @@
1
+ import type { ResourceAdmissionConfig, ResourceGovernorMode } from "./resource-admission.ts";
2
+ export { buildResourceDoctorReport, describeResourceReasons, formatResourcePolicyLines, formatResourceProbeLines, formatResourceSummaryLines, type ResourceDoctorReport, } from "./resource-governor-format.ts";
3
+ /**
4
+ * Resource governor settings surface (OMK v0.97.x roadmap §18, M1/PR2).
5
+ *
6
+ * Pure translation from the additive `resourceGovernor` settings block (and
7
+ * the §18.2 `OMK_RESOURCE_GOVERNOR` env override) to the layer-1 admission
8
+ * config plus probe options. Validation follows §18.1: explicit errors over
9
+ * silent clamps, and invalid input fails closed to the defaults.
10
+ *
11
+ * Resolved values feed the `/resource` command, `omk doctor resources`, and
12
+ * the M2 prompt preflight: `observe` records decisions only, while
13
+ * `adaptive`/`strict` let the run lease throttle per-run tool concurrency.
14
+ * Lane and heavy-process caps become authoritative in later milestones.
15
+ */
16
+ /** Additive settings block (§18, §29.1: absent block keeps v0.96.1 behavior). */
17
+ export interface ResourceGovernorSettings {
18
+ enabled?: boolean;
19
+ mode?: ResourceGovernorMode;
20
+ maxProbeMs?: number;
21
+ cpuSampleMs?: number;
22
+ constrainedAvailableMemoryMiB?: number;
23
+ criticalAvailableMemoryMiB?: number;
24
+ constrainedDiskFreeMiB?: number;
25
+ criticalDiskFreeMiB?: number;
26
+ constrainedHeapRatio?: number;
27
+ criticalHeapRatio?: number;
28
+ busyCpuPercent?: number;
29
+ normalMaxToolConcurrency?: number;
30
+ constrainedMaxToolConcurrency?: number;
31
+ criticalMaxToolConcurrency?: number;
32
+ normalMaxParallelLanes?: number;
33
+ constrainedMaxParallelLanes?: number;
34
+ criticalMaxParallelLanes?: number;
35
+ normalMaxHeavyProcesses?: number;
36
+ constrainedMaxHeavyProcesses?: number;
37
+ criticalMaxHeavyProcesses?: number;
38
+ }
39
+ export declare const RESOURCE_GOVERNOR_MODE_ENV = "OMK_RESOURCE_GOVERNOR";
40
+ export declare const DEFAULT_RESOURCE_GOVERNOR_MODE: ResourceGovernorMode;
41
+ export interface ResolvedResourceGovernorSettings {
42
+ readonly mode: ResourceGovernorMode;
43
+ /** Only present when the setting is valid; the probe applies its own defaults otherwise. */
44
+ readonly maxProbeMs?: number;
45
+ readonly cpuSampleMs?: number;
46
+ readonly admission: ResourceAdmissionConfig;
47
+ /** §18.1 explicit validation errors; non-empty means defaults were applied for the failed area. */
48
+ readonly errors: readonly string[];
49
+ }
50
+ /**
51
+ * Resolve settings + environment into governor mode, probe options, and the
52
+ * admission config. Never throws; every invalid field produces an explicit
53
+ * error and falls back to the corresponding default (§18.1 fail-closed).
54
+ */
55
+ export declare function resolveResourceGovernorSettings(settings?: ResourceGovernorSettings, env?: NodeJS.ProcessEnv): ResolvedResourceGovernorSettings;
56
+ //# sourceMappingURL=resource-governor-settings.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resource-governor-settings.d.ts","sourceRoot":"","sources":["../../src/core/resource-governor-settings.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACX,uBAAuB,EAEvB,oBAAoB,EACpB,MAAM,yBAAyB,CAAC;AAIjC,OAAO,EACN,yBAAyB,EACzB,uBAAuB,EACvB,yBAAyB,EACzB,wBAAwB,EACxB,0BAA0B,EAC1B,KAAK,oBAAoB,GACzB,MAAM,+BAA+B,CAAC;AAEvC;;;;;;;;;;;;GAYG;AAEH,mFAAiF;AACjF,MAAM,WAAW,wBAAwB;IACxC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,IAAI,CAAC,EAAE,oBAAoB,CAAC;IAE5B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB,6BAA6B,CAAC,EAAE,MAAM,CAAC;IACvC,0BAA0B,CAAC,EAAE,MAAM,CAAC;IACpC,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,cAAc,CAAC,EAAE,MAAM,CAAC;IAExB,wBAAwB,CAAC,EAAE,MAAM,CAAC;IAClC,6BAA6B,CAAC,EAAE,MAAM,CAAC;IACvC,0BAA0B,CAAC,EAAE,MAAM,CAAC;IACpC,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,2BAA2B,CAAC,EAAE,MAAM,CAAC;IACrC,wBAAwB,CAAC,EAAE,MAAM,CAAC;IAClC,uBAAuB,CAAC,EAAE,MAAM,CAAC;IACjC,4BAA4B,CAAC,EAAE,MAAM,CAAC;IACtC,yBAAyB,CAAC,EAAE,MAAM,CAAC;CACnC;AAED,eAAO,MAAM,0BAA0B,0BAA0B,CAAC;AAClE,eAAO,MAAM,8BAA8B,EAAE,oBAAgC,CAAC;AAG9E,MAAM,WAAW,gCAAgC;IAChD,QAAQ,CAAC,IAAI,EAAE,oBAAoB,CAAC;IACpC,4FAA4F;IAC5F,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,SAAS,EAAE,uBAAuB,CAAC;IAC5C,oGAAmG;IACnG,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;CACnC;AAED;;;;GAIG;AACH,wBAAgB,+BAA+B,CAC9C,QAAQ,CAAC,EAAE,wBAAwB,EACnC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAClC,gCAAgC,CA6ClC","sourcesContent":["import type {\n\tResourceAdmissionConfig,\n\tResourceAdmissionConfigOverrides,\n\tResourceGovernorMode,\n} from \"./resource-admission.ts\";\nimport { resolveResourceAdmissionConfig } from \"./resource-admission.ts\";\nimport { MAX_CPU_SAMPLE_MS, MIN_CPU_SAMPLE_MS } from \"./system-cpu-sampler.ts\";\n\nexport {\n\tbuildResourceDoctorReport,\n\tdescribeResourceReasons,\n\tformatResourcePolicyLines,\n\tformatResourceProbeLines,\n\tformatResourceSummaryLines,\n\ttype ResourceDoctorReport,\n} from \"./resource-governor-format.ts\";\n\n/**\n * Resource governor settings surface (OMK v0.97.x roadmap §18, M1/PR2).\n *\n * Pure translation from the additive `resourceGovernor` settings block (and\n * the §18.2 `OMK_RESOURCE_GOVERNOR` env override) to the layer-1 admission\n * config plus probe options. Validation follows §18.1: explicit errors over\n * silent clamps, and invalid input fails closed to the defaults.\n *\n * Resolved values feed the `/resource` command, `omk doctor resources`, and\n * the M2 prompt preflight: `observe` records decisions only, while\n * `adaptive`/`strict` let the run lease throttle per-run tool concurrency.\n * Lane and heavy-process caps become authoritative in later milestones.\n */\n\n/** Additive settings block (§18, §29.1: absent block keeps v0.96.1 behavior). */\nexport interface ResourceGovernorSettings {\n\tenabled?: boolean; // default: true; false forces mode \"off\"\n\tmode?: ResourceGovernorMode; // default: \"observe\" (v0.97.0-alpha rollout stage, §7.4)\n\n\tmaxProbeMs?: number; // default: 300; async probe deadline, valid 50..5000 (§18.1)\n\tcpuSampleMs?: number; // default: 180; CPU two-sample interval, valid 150..250 (§6.3)\n\n\tconstrainedAvailableMemoryMiB?: number; // default: 1536 (§6.7)\n\tcriticalAvailableMemoryMiB?: number; // default: 512\n\tconstrainedDiskFreeMiB?: number; // default: 4096\n\tcriticalDiskFreeMiB?: number; // default: 1024\n\tconstrainedHeapRatio?: number; // default: 0.75\n\tcriticalHeapRatio?: number; // default: 0.85\n\tbusyCpuPercent?: number; // default: 85\n\n\tnormalMaxToolConcurrency?: number; // default: 4 (§7.2)\n\tconstrainedMaxToolConcurrency?: number; // default: 2\n\tcriticalMaxToolConcurrency?: number; // default: 1\n\tnormalMaxParallelLanes?: number; // default: 4\n\tconstrainedMaxParallelLanes?: number; // default: 2\n\tcriticalMaxParallelLanes?: number; // default: 1\n\tnormalMaxHeavyProcesses?: number; // default: 2\n\tconstrainedMaxHeavyProcesses?: number; // default: 1\n\tcriticalMaxHeavyProcesses?: number; // default: 1\n}\n\nexport const RESOURCE_GOVERNOR_MODE_ENV = \"OMK_RESOURCE_GOVERNOR\";\nexport const DEFAULT_RESOURCE_GOVERNOR_MODE: ResourceGovernorMode = \"observe\";\nconst RESOURCE_GOVERNOR_MODES: ReadonlySet<string> = new Set([\"off\", \"observe\", \"adaptive\", \"strict\"]);\n\nexport interface ResolvedResourceGovernorSettings {\n\treadonly mode: ResourceGovernorMode;\n\t/** Only present when the setting is valid; the probe applies its own defaults otherwise. */\n\treadonly maxProbeMs?: number;\n\treadonly cpuSampleMs?: number;\n\treadonly admission: ResourceAdmissionConfig;\n\t/** §18.1 explicit validation errors; non-empty means defaults were applied for the failed area. */\n\treadonly errors: readonly string[];\n}\n\n/**\n * Resolve settings + environment into governor mode, probe options, and the\n * admission config. Never throws; every invalid field produces an explicit\n * error and falls back to the corresponding default (§18.1 fail-closed).\n */\nexport function resolveResourceGovernorSettings(\n\tsettings?: ResourceGovernorSettings,\n\tenv: NodeJS.ProcessEnv = process.env,\n): ResolvedResourceGovernorSettings {\n\tconst errors: string[] = [];\n\tconst mode = resolveMode(settings, env, errors);\n\n\tconst maxProbeMs = validateRange(settings?.maxProbeMs, \"maxProbeMs\", 50, 5000, errors);\n\tconst cpuSampleMs = validateRange(\n\t\tsettings?.cpuSampleMs,\n\t\t\"cpuSampleMs\",\n\t\tMIN_CPU_SAMPLE_MS,\n\t\tMAX_CPU_SAMPLE_MS,\n\t\terrors,\n\t);\n\n\tconst overrides: ResourceAdmissionConfigOverrides = {\n\t\tthresholds: pruneUndefined({\n\t\t\tconstrainedAvailableMemoryMiB: settings?.constrainedAvailableMemoryMiB,\n\t\t\tcriticalAvailableMemoryMiB: settings?.criticalAvailableMemoryMiB,\n\t\t\tconstrainedDiskFreeMiB: settings?.constrainedDiskFreeMiB,\n\t\t\tcriticalDiskFreeMiB: settings?.criticalDiskFreeMiB,\n\t\t\tconstrainedHeapRatio: settings?.constrainedHeapRatio,\n\t\t\tcriticalHeapRatio: settings?.criticalHeapRatio,\n\t\t\tbusyCpuPercent: settings?.busyCpuPercent,\n\t\t}),\n\t\tcaps: {\n\t\t\tnormal: pruneUndefined({\n\t\t\t\tmaxToolConcurrency: settings?.normalMaxToolConcurrency,\n\t\t\t\tmaxParallelLanes: settings?.normalMaxParallelLanes,\n\t\t\t\tmaxHeavyProcesses: settings?.normalMaxHeavyProcesses,\n\t\t\t}),\n\t\t\tconstrained: pruneUndefined({\n\t\t\t\tmaxToolConcurrency: settings?.constrainedMaxToolConcurrency,\n\t\t\t\tmaxParallelLanes: settings?.constrainedMaxParallelLanes,\n\t\t\t\tmaxHeavyProcesses: settings?.constrainedMaxHeavyProcesses,\n\t\t\t}),\n\t\t\tcritical: pruneUndefined({\n\t\t\t\tmaxToolConcurrency: settings?.criticalMaxToolConcurrency,\n\t\t\t\tmaxParallelLanes: settings?.criticalMaxParallelLanes,\n\t\t\t\tmaxHeavyProcesses: settings?.criticalMaxHeavyProcesses,\n\t\t\t}),\n\t\t},\n\t};\n\tconst admission = resolveResourceAdmissionConfig(overrides);\n\terrors.push(...admission.errors);\n\n\treturn { mode, maxProbeMs, cpuSampleMs, admission: admission.config, errors };\n}\n\nfunction resolveMode(\n\tsettings: ResourceGovernorSettings | undefined,\n\tenv: NodeJS.ProcessEnv,\n\terrors: string[],\n): ResourceGovernorMode {\n\t// §18.2/§30.1: the env override is the operator kill switch and wins.\n\tconst envMode = env[RESOURCE_GOVERNOR_MODE_ENV]?.trim();\n\tif (envMode !== undefined && envMode !== \"\") {\n\t\tif (RESOURCE_GOVERNOR_MODES.has(envMode)) {\n\t\t\treturn envMode as ResourceGovernorMode;\n\t\t}\n\t\terrors.push(`resourceGovernor.mode: invalid ${RESOURCE_GOVERNOR_MODE_ENV} value ${JSON.stringify(envMode)}`);\n\t}\n\tif (settings?.enabled === false) {\n\t\treturn \"off\";\n\t}\n\tconst configured = settings?.mode;\n\tif (configured !== undefined) {\n\t\tif (RESOURCE_GOVERNOR_MODES.has(configured)) {\n\t\t\treturn configured;\n\t\t}\n\t\terrors.push(`resourceGovernor.mode: invalid value ${JSON.stringify(configured)}`);\n\t}\n\treturn DEFAULT_RESOURCE_GOVERNOR_MODE;\n}\n\nfunction validateRange(\n\tvalue: number | undefined,\n\tfield: string,\n\tmin: number,\n\tmax: number,\n\terrors: string[],\n): number | undefined {\n\tif (value === undefined) {\n\t\treturn undefined;\n\t}\n\tif (!Number.isSafeInteger(value) || value < min || value > max) {\n\t\terrors.push(`resourceGovernor.${field}: must be an integer within [${min}, ${max}], got ${String(value)}`);\n\t\treturn undefined;\n\t}\n\treturn value;\n}\n\nfunction pruneUndefined<T extends Record<string, number | undefined>>(record: T): Partial<T> {\n\tconst pruned: Record<string, number> = {};\n\tfor (const [key, value] of Object.entries(record)) {\n\t\tif (value !== undefined) {\n\t\t\tpruned[key] = value;\n\t\t}\n\t}\n\treturn pruned as Partial<T>;\n}\n"]}
@@ -0,0 +1,89 @@
1
+ import { resolveResourceAdmissionConfig } from "./resource-admission.js";
2
+ import { MAX_CPU_SAMPLE_MS, MIN_CPU_SAMPLE_MS } from "./system-cpu-sampler.js";
3
+ export { buildResourceDoctorReport, describeResourceReasons, formatResourcePolicyLines, formatResourceProbeLines, formatResourceSummaryLines, } from "./resource-governor-format.js";
4
+ export const RESOURCE_GOVERNOR_MODE_ENV = "OMK_RESOURCE_GOVERNOR";
5
+ export const DEFAULT_RESOURCE_GOVERNOR_MODE = "observe";
6
+ const RESOURCE_GOVERNOR_MODES = new Set(["off", "observe", "adaptive", "strict"]);
7
+ /**
8
+ * Resolve settings + environment into governor mode, probe options, and the
9
+ * admission config. Never throws; every invalid field produces an explicit
10
+ * error and falls back to the corresponding default (§18.1 fail-closed).
11
+ */
12
+ export function resolveResourceGovernorSettings(settings, env = process.env) {
13
+ const errors = [];
14
+ const mode = resolveMode(settings, env, errors);
15
+ const maxProbeMs = validateRange(settings?.maxProbeMs, "maxProbeMs", 50, 5000, errors);
16
+ const cpuSampleMs = validateRange(settings?.cpuSampleMs, "cpuSampleMs", MIN_CPU_SAMPLE_MS, MAX_CPU_SAMPLE_MS, errors);
17
+ const overrides = {
18
+ thresholds: pruneUndefined({
19
+ constrainedAvailableMemoryMiB: settings?.constrainedAvailableMemoryMiB,
20
+ criticalAvailableMemoryMiB: settings?.criticalAvailableMemoryMiB,
21
+ constrainedDiskFreeMiB: settings?.constrainedDiskFreeMiB,
22
+ criticalDiskFreeMiB: settings?.criticalDiskFreeMiB,
23
+ constrainedHeapRatio: settings?.constrainedHeapRatio,
24
+ criticalHeapRatio: settings?.criticalHeapRatio,
25
+ busyCpuPercent: settings?.busyCpuPercent,
26
+ }),
27
+ caps: {
28
+ normal: pruneUndefined({
29
+ maxToolConcurrency: settings?.normalMaxToolConcurrency,
30
+ maxParallelLanes: settings?.normalMaxParallelLanes,
31
+ maxHeavyProcesses: settings?.normalMaxHeavyProcesses,
32
+ }),
33
+ constrained: pruneUndefined({
34
+ maxToolConcurrency: settings?.constrainedMaxToolConcurrency,
35
+ maxParallelLanes: settings?.constrainedMaxParallelLanes,
36
+ maxHeavyProcesses: settings?.constrainedMaxHeavyProcesses,
37
+ }),
38
+ critical: pruneUndefined({
39
+ maxToolConcurrency: settings?.criticalMaxToolConcurrency,
40
+ maxParallelLanes: settings?.criticalMaxParallelLanes,
41
+ maxHeavyProcesses: settings?.criticalMaxHeavyProcesses,
42
+ }),
43
+ },
44
+ };
45
+ const admission = resolveResourceAdmissionConfig(overrides);
46
+ errors.push(...admission.errors);
47
+ return { mode, maxProbeMs, cpuSampleMs, admission: admission.config, errors };
48
+ }
49
+ function resolveMode(settings, env, errors) {
50
+ // §18.2/§30.1: the env override is the operator kill switch and wins.
51
+ const envMode = env[RESOURCE_GOVERNOR_MODE_ENV]?.trim();
52
+ if (envMode !== undefined && envMode !== "") {
53
+ if (RESOURCE_GOVERNOR_MODES.has(envMode)) {
54
+ return envMode;
55
+ }
56
+ errors.push(`resourceGovernor.mode: invalid ${RESOURCE_GOVERNOR_MODE_ENV} value ${JSON.stringify(envMode)}`);
57
+ }
58
+ if (settings?.enabled === false) {
59
+ return "off";
60
+ }
61
+ const configured = settings?.mode;
62
+ if (configured !== undefined) {
63
+ if (RESOURCE_GOVERNOR_MODES.has(configured)) {
64
+ return configured;
65
+ }
66
+ errors.push(`resourceGovernor.mode: invalid value ${JSON.stringify(configured)}`);
67
+ }
68
+ return DEFAULT_RESOURCE_GOVERNOR_MODE;
69
+ }
70
+ function validateRange(value, field, min, max, errors) {
71
+ if (value === undefined) {
72
+ return undefined;
73
+ }
74
+ if (!Number.isSafeInteger(value) || value < min || value > max) {
75
+ errors.push(`resourceGovernor.${field}: must be an integer within [${min}, ${max}], got ${String(value)}`);
76
+ return undefined;
77
+ }
78
+ return value;
79
+ }
80
+ function pruneUndefined(record) {
81
+ const pruned = {};
82
+ for (const [key, value] of Object.entries(record)) {
83
+ if (value !== undefined) {
84
+ pruned[key] = value;
85
+ }
86
+ }
87
+ return pruned;
88
+ }
89
+ //# sourceMappingURL=resource-governor-settings.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resource-governor-settings.js","sourceRoot":"","sources":["../../src/core/resource-governor-settings.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n\tResourceAdmissionConfig,\n\tResourceAdmissionConfigOverrides,\n\tResourceGovernorMode,\n} from \"./resource-admission.ts\";\nimport { resolveResourceAdmissionConfig } from \"./resource-admission.ts\";\nimport { MAX_CPU_SAMPLE_MS, MIN_CPU_SAMPLE_MS } from \"./system-cpu-sampler.ts\";\n\nexport {\n\tbuildResourceDoctorReport,\n\tdescribeResourceReasons,\n\tformatResourcePolicyLines,\n\tformatResourceProbeLines,\n\tformatResourceSummaryLines,\n\ttype ResourceDoctorReport,\n} from \"./resource-governor-format.ts\";\n\n/**\n * Resource governor settings surface (OMK v0.97.x roadmap §18, M1/PR2).\n *\n * Pure translation from the additive `resourceGovernor` settings block (and\n * the §18.2 `OMK_RESOURCE_GOVERNOR` env override) to the layer-1 admission\n * config plus probe options. Validation follows §18.1: explicit errors over\n * silent clamps, and invalid input fails closed to the defaults.\n *\n * Resolved values feed the `/resource` command, `omk doctor resources`, and\n * the M2 prompt preflight: `observe` records decisions only, while\n * `adaptive`/`strict` let the run lease throttle per-run tool concurrency.\n * Lane and heavy-process caps become authoritative in later milestones.\n */\n\n/** Additive settings block (§18, §29.1: absent block keeps v0.96.1 behavior). */\nexport interface ResourceGovernorSettings {\n\tenabled?: boolean; // default: true; false forces mode \"off\"\n\tmode?: ResourceGovernorMode; // default: \"observe\" (v0.97.0-alpha rollout stage, §7.4)\n\n\tmaxProbeMs?: number; // default: 300; async probe deadline, valid 50..5000 (§18.1)\n\tcpuSampleMs?: number; // default: 180; CPU two-sample interval, valid 150..250 (§6.3)\n\n\tconstrainedAvailableMemoryMiB?: number; // default: 1536 (§6.7)\n\tcriticalAvailableMemoryMiB?: number; // default: 512\n\tconstrainedDiskFreeMiB?: number; // default: 4096\n\tcriticalDiskFreeMiB?: number; // default: 1024\n\tconstrainedHeapRatio?: number; // default: 0.75\n\tcriticalHeapRatio?: number; // default: 0.85\n\tbusyCpuPercent?: number; // default: 85\n\n\tnormalMaxToolConcurrency?: number; // default: 4 (§7.2)\n\tconstrainedMaxToolConcurrency?: number; // default: 2\n\tcriticalMaxToolConcurrency?: number; // default: 1\n\tnormalMaxParallelLanes?: number; // default: 4\n\tconstrainedMaxParallelLanes?: number; // default: 2\n\tcriticalMaxParallelLanes?: number; // default: 1\n\tnormalMaxHeavyProcesses?: number; // default: 2\n\tconstrainedMaxHeavyProcesses?: number; // default: 1\n\tcriticalMaxHeavyProcesses?: number; // default: 1\n}\n\nexport const RESOURCE_GOVERNOR_MODE_ENV = \"OMK_RESOURCE_GOVERNOR\";\nexport const DEFAULT_RESOURCE_GOVERNOR_MODE: ResourceGovernorMode = \"observe\";\nconst RESOURCE_GOVERNOR_MODES: ReadonlySet<string> = new Set([\"off\", \"observe\", \"adaptive\", \"strict\"]);\n\nexport interface ResolvedResourceGovernorSettings {\n\treadonly mode: ResourceGovernorMode;\n\t/** Only present when the setting is valid; the probe applies its own defaults otherwise. */\n\treadonly maxProbeMs?: number;\n\treadonly cpuSampleMs?: number;\n\treadonly admission: ResourceAdmissionConfig;\n\t/** §18.1 explicit validation errors; non-empty means defaults were applied for the failed area. */\n\treadonly errors: readonly string[];\n}\n\n/**\n * Resolve settings + environment into governor mode, probe options, and the\n * admission config. Never throws; every invalid field produces an explicit\n * error and falls back to the corresponding default (§18.1 fail-closed).\n */\nexport function resolveResourceGovernorSettings(\n\tsettings?: ResourceGovernorSettings,\n\tenv: NodeJS.ProcessEnv = process.env,\n): ResolvedResourceGovernorSettings {\n\tconst errors: string[] = [];\n\tconst mode = resolveMode(settings, env, errors);\n\n\tconst maxProbeMs = validateRange(settings?.maxProbeMs, \"maxProbeMs\", 50, 5000, errors);\n\tconst cpuSampleMs = validateRange(\n\t\tsettings?.cpuSampleMs,\n\t\t\"cpuSampleMs\",\n\t\tMIN_CPU_SAMPLE_MS,\n\t\tMAX_CPU_SAMPLE_MS,\n\t\terrors,\n\t);\n\n\tconst overrides: ResourceAdmissionConfigOverrides = {\n\t\tthresholds: pruneUndefined({\n\t\t\tconstrainedAvailableMemoryMiB: settings?.constrainedAvailableMemoryMiB,\n\t\t\tcriticalAvailableMemoryMiB: settings?.criticalAvailableMemoryMiB,\n\t\t\tconstrainedDiskFreeMiB: settings?.constrainedDiskFreeMiB,\n\t\t\tcriticalDiskFreeMiB: settings?.criticalDiskFreeMiB,\n\t\t\tconstrainedHeapRatio: settings?.constrainedHeapRatio,\n\t\t\tcriticalHeapRatio: settings?.criticalHeapRatio,\n\t\t\tbusyCpuPercent: settings?.busyCpuPercent,\n\t\t}),\n\t\tcaps: {\n\t\t\tnormal: pruneUndefined({\n\t\t\t\tmaxToolConcurrency: settings?.normalMaxToolConcurrency,\n\t\t\t\tmaxParallelLanes: settings?.normalMaxParallelLanes,\n\t\t\t\tmaxHeavyProcesses: settings?.normalMaxHeavyProcesses,\n\t\t\t}),\n\t\t\tconstrained: pruneUndefined({\n\t\t\t\tmaxToolConcurrency: settings?.constrainedMaxToolConcurrency,\n\t\t\t\tmaxParallelLanes: settings?.constrainedMaxParallelLanes,\n\t\t\t\tmaxHeavyProcesses: settings?.constrainedMaxHeavyProcesses,\n\t\t\t}),\n\t\t\tcritical: pruneUndefined({\n\t\t\t\tmaxToolConcurrency: settings?.criticalMaxToolConcurrency,\n\t\t\t\tmaxParallelLanes: settings?.criticalMaxParallelLanes,\n\t\t\t\tmaxHeavyProcesses: settings?.criticalMaxHeavyProcesses,\n\t\t\t}),\n\t\t},\n\t};\n\tconst admission = resolveResourceAdmissionConfig(overrides);\n\terrors.push(...admission.errors);\n\n\treturn { mode, maxProbeMs, cpuSampleMs, admission: admission.config, errors };\n}\n\nfunction resolveMode(\n\tsettings: ResourceGovernorSettings | undefined,\n\tenv: NodeJS.ProcessEnv,\n\terrors: string[],\n): ResourceGovernorMode {\n\t// §18.2/§30.1: the env override is the operator kill switch and wins.\n\tconst envMode = env[RESOURCE_GOVERNOR_MODE_ENV]?.trim();\n\tif (envMode !== undefined && envMode !== \"\") {\n\t\tif (RESOURCE_GOVERNOR_MODES.has(envMode)) {\n\t\t\treturn envMode as ResourceGovernorMode;\n\t\t}\n\t\terrors.push(`resourceGovernor.mode: invalid ${RESOURCE_GOVERNOR_MODE_ENV} value ${JSON.stringify(envMode)}`);\n\t}\n\tif (settings?.enabled === false) {\n\t\treturn \"off\";\n\t}\n\tconst configured = settings?.mode;\n\tif (configured !== undefined) {\n\t\tif (RESOURCE_GOVERNOR_MODES.has(configured)) {\n\t\t\treturn configured;\n\t\t}\n\t\terrors.push(`resourceGovernor.mode: invalid value ${JSON.stringify(configured)}`);\n\t}\n\treturn DEFAULT_RESOURCE_GOVERNOR_MODE;\n}\n\nfunction validateRange(\n\tvalue: number | undefined,\n\tfield: string,\n\tmin: number,\n\tmax: number,\n\terrors: string[],\n): number | undefined {\n\tif (value === undefined) {\n\t\treturn undefined;\n\t}\n\tif (!Number.isSafeInteger(value) || value < min || value > max) {\n\t\terrors.push(`resourceGovernor.${field}: must be an integer within [${min}, ${max}], got ${String(value)}`);\n\t\treturn undefined;\n\t}\n\treturn value;\n}\n\nfunction pruneUndefined<T extends Record<string, number | undefined>>(record: T): Partial<T> {\n\tconst pruned: Record<string, number> = {};\n\tfor (const [key, value] of Object.entries(record)) {\n\t\tif (value !== undefined) {\n\t\t\tpruned[key] = value;\n\t\t}\n\t}\n\treturn pruned as Partial<T>;\n}\n"]}
@@ -0,0 +1,74 @@
1
+ import type { CompletionSoundResult } from "./completion-sound.ts";
2
+ import type { PromptSettledEvent } from "./prompt-settlement.ts";
3
+ import type { ResourceAdmissionDecision } from "./resource-admission.ts";
4
+ import type { WorkloadClassification } from "./workload-classifier.ts";
5
+ /**
6
+ * Local-only resource observation journal (roadmap §15.1/§15.2/§20.2).
7
+ *
8
+ * A dedicated JSONL artifact under `.omk/runs/<promptRunId>/` — deliberately
9
+ * NOT the hash-chained run journal: §15.2 restricts raw host facts to a
10
+ * separate local-only artifact, and extending the run journal's closed audit
11
+ * union would invalidate old readers. Shard plan/transition entries stay in
12
+ * `workload-shards.jsonl` (they already journal there); this file carries
13
+ * the remaining §20.2 entries.
14
+ *
15
+ * Privacy (§15.2, §20.3, §30): facts are flat primitives only — pressure
16
+ * levels, caps, digests, durations, reason codes. Never raw memory/cpu
17
+ * numbers, hostnames, usernames, or command text. Observability is
18
+ * fail-open: a write/read failure degrades to diagnostics, never breaks the
19
+ * run (probe failure is non-fatal by contract).
20
+ */
21
+ export declare const RESOURCE_OBSERVATION_JOURNAL_FILE = "resource-observations.jsonl";
22
+ /** §20.2 journal entry kinds handled by this artifact. */
23
+ export declare const RESOURCE_OBSERVATION_KINDS: readonly ["resource_snapshot_v1", "resource_admission_v1", "resource_lease_acquired_v1", "resource_lease_released_v1", "workload_classification_v1", "workload_permit_wait_v1", "workload_permit_acquired_v1", "workload_permit_released_v1", "prompt_settled_v1", "completion_sound_result_v1"];
24
+ export type ResourceObservationKind = (typeof RESOURCE_OBSERVATION_KINDS)[number];
25
+ export type ResourceObservationFacts = Readonly<Record<string, string | number | boolean | null>>;
26
+ export interface ResourceObservationRecord {
27
+ readonly schemaVersion: 1;
28
+ readonly seq: number;
29
+ readonly kind: ResourceObservationKind;
30
+ readonly at: string;
31
+ readonly promptRunId: string;
32
+ readonly facts: ResourceObservationFacts;
33
+ }
34
+ /** §15.1 protocol observation projection of one journal record. */
35
+ export interface ResourceProtocolObservation {
36
+ readonly kind: "resource_snapshot.v1" | "resource_admission.v1" | "workload_classification.v1" | "workload_permit.v1" | "prompt_settled.v1" | "notification_result.v1";
37
+ readonly facts: ResourceObservationFacts;
38
+ }
39
+ /**
40
+ * Project a journal record onto the §15.1 protocol observation surface.
41
+ * Lease entries are local-only operational detail and project to null.
42
+ */
43
+ export declare function toProtocolObservation(record: ResourceObservationRecord): ResourceProtocolObservation | null;
44
+ /** §15.2: probe health and digest only — raw host values never leave the process. */
45
+ export declare function snapshotObservationFacts(snapshot: {
46
+ readonly systemCpuPercent: number | null;
47
+ readonly processAvailableMemoryBytes: number | null;
48
+ readonly hostFreeMemoryBytes: number | null;
49
+ readonly heapLimitBytes: number;
50
+ }): ResourceObservationFacts;
51
+ /** §15.2 example shape: pressure, action, and the three caps. */
52
+ export declare function admissionObservationFacts(decision: ResourceAdmissionDecision): ResourceObservationFacts;
53
+ export declare function classificationObservationFacts(classification: WorkloadClassification): ResourceObservationFacts;
54
+ export declare function settledObservationFacts(event: PromptSettledEvent): ResourceObservationFacts;
55
+ export declare function soundObservationFacts(result: CompletionSoundResult): ResourceObservationFacts;
56
+ export interface ResourceObservationLoadResult {
57
+ readonly records: readonly ResourceObservationRecord[];
58
+ readonly diagnostics: readonly string[];
59
+ }
60
+ export declare class ResourceObservationJournal {
61
+ private readonly journalPath;
62
+ private readonly promptRunId;
63
+ private readonly now;
64
+ private seq;
65
+ private disabled;
66
+ constructor(journalPath: string, promptRunId: string, now?: () => Date);
67
+ static open(cwd: string, promptRunId: string, now?: () => Date): ResourceObservationJournal;
68
+ get filePath(): string;
69
+ /** Fail-open append: returns false (and stops trying) instead of throwing. */
70
+ record(kind: ResourceObservationKind, facts: ResourceObservationFacts): boolean;
71
+ /** Fail-open load: malformed lines become diagnostics, never throws. */
72
+ load(): ResourceObservationLoadResult;
73
+ }
74
+ //# sourceMappingURL=resource-observation-journal.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resource-observation-journal.d.ts","sourceRoot":"","sources":["../../src/core/resource-observation-journal.ts"],"names":[],"mappings":"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","sourcesContent":["import * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport type { CompletionSoundResult } from \"./completion-sound.ts\";\nimport type { PromptSettledEvent } from \"./prompt-settlement.ts\";\nimport type { ResourceAdmissionDecision } from \"./resource-admission.ts\";\nimport type { WorkloadClassification } from \"./workload-classifier.ts\";\n\n/**\n * Local-only resource observation journal (roadmap §15.1/§15.2/§20.2).\n *\n * A dedicated JSONL artifact under `.omk/runs/<promptRunId>/` — deliberately\n * NOT the hash-chained run journal: §15.2 restricts raw host facts to a\n * separate local-only artifact, and extending the run journal's closed audit\n * union would invalidate old readers. Shard plan/transition entries stay in\n * `workload-shards.jsonl` (they already journal there); this file carries\n * the remaining §20.2 entries.\n *\n * Privacy (§15.2, §20.3, §30): facts are flat primitives only — pressure\n * levels, caps, digests, durations, reason codes. Never raw memory/cpu\n * numbers, hostnames, usernames, or command text. Observability is\n * fail-open: a write/read failure degrades to diagnostics, never breaks the\n * run (probe failure is non-fatal by contract).\n */\n\nexport const RESOURCE_OBSERVATION_JOURNAL_FILE = \"resource-observations.jsonl\";\n\n/** §20.2 journal entry kinds handled by this artifact. */\nexport const RESOURCE_OBSERVATION_KINDS = [\n\t\"resource_snapshot_v1\",\n\t\"resource_admission_v1\",\n\t\"resource_lease_acquired_v1\",\n\t\"resource_lease_released_v1\",\n\t\"workload_classification_v1\",\n\t\"workload_permit_wait_v1\",\n\t\"workload_permit_acquired_v1\",\n\t\"workload_permit_released_v1\",\n\t\"prompt_settled_v1\",\n\t\"completion_sound_result_v1\",\n] as const;\n\nexport type ResourceObservationKind = (typeof RESOURCE_OBSERVATION_KINDS)[number];\n\nexport type ResourceObservationFacts = Readonly<Record<string, string | number | boolean | null>>;\n\nexport interface ResourceObservationRecord {\n\treadonly schemaVersion: 1;\n\treadonly seq: number;\n\treadonly kind: ResourceObservationKind;\n\treadonly at: string;\n\treadonly promptRunId: string;\n\treadonly facts: ResourceObservationFacts;\n}\n\n/** §15.1 protocol observation projection of one journal record. */\nexport interface ResourceProtocolObservation {\n\treadonly kind:\n\t\t| \"resource_snapshot.v1\"\n\t\t| \"resource_admission.v1\"\n\t\t| \"workload_classification.v1\"\n\t\t| \"workload_permit.v1\"\n\t\t| \"prompt_settled.v1\"\n\t\t| \"notification_result.v1\";\n\treadonly facts: ResourceObservationFacts;\n}\n\nconst PROTOCOL_KIND_BY_JOURNAL_KIND: Readonly<\n\tPartial<Record<ResourceObservationKind, ResourceProtocolObservation[\"kind\"]>>\n> = {\n\tresource_snapshot_v1: \"resource_snapshot.v1\",\n\tresource_admission_v1: \"resource_admission.v1\",\n\tworkload_classification_v1: \"workload_classification.v1\",\n\tworkload_permit_wait_v1: \"workload_permit.v1\",\n\tworkload_permit_acquired_v1: \"workload_permit.v1\",\n\tworkload_permit_released_v1: \"workload_permit.v1\",\n\tprompt_settled_v1: \"prompt_settled.v1\",\n\tcompletion_sound_result_v1: \"notification_result.v1\",\n};\n\n/**\n * Project a journal record onto the §15.1 protocol observation surface.\n * Lease entries are local-only operational detail and project to null.\n */\nexport function toProtocolObservation(record: ResourceObservationRecord): ResourceProtocolObservation | null {\n\tconst kind = PROTOCOL_KIND_BY_JOURNAL_KIND[record.kind];\n\treturn kind === undefined ? null : { kind, facts: record.facts };\n}\n\n/** §15.2: probe health and digest only — raw host values never leave the process. */\nexport function snapshotObservationFacts(snapshot: {\n\treadonly systemCpuPercent: number | null;\n\treadonly processAvailableMemoryBytes: number | null;\n\treadonly hostFreeMemoryBytes: number | null;\n\treadonly heapLimitBytes: number;\n}): ResourceObservationFacts {\n\treturn {\n\t\tcpuProbe: snapshot.systemCpuPercent !== null,\n\t\tmemoryProbe: snapshot.processAvailableMemoryBytes !== null || snapshot.hostFreeMemoryBytes !== null,\n\t\theapLimitKnown: snapshot.heapLimitBytes > 0,\n\t};\n}\n\n/** §15.2 example shape: pressure, action, and the three caps. */\nexport function admissionObservationFacts(decision: ResourceAdmissionDecision): ResourceObservationFacts {\n\treturn {\n\t\tdecisionId: decision.decisionId,\n\t\tsnapshotDigest: decision.snapshotDigest,\n\t\tpressure: decision.pressure,\n\t\taction: decision.action,\n\t\tmaxToolConcurrency: decision.maxToolConcurrency,\n\t\tmaxParallelLanes: decision.maxParallelLanes,\n\t\tmaxHeavyProcesses: decision.maxHeavyProcesses,\n\t};\n}\n\nexport function classificationObservationFacts(classification: WorkloadClassification): ResourceObservationFacts {\n\treturn {\n\t\tworkloadClass: classification.workloadClass,\n\t\tcommandFamily: classification.commandFamily,\n\t\tcomplexity: classification.complexity,\n\t\tsafeToAutoShard: classification.safeToAutoShard,\n\t\treasonCodes: classification.reasonCodes.slice(0, 8).join(\",\"),\n\t};\n}\n\nexport function settledObservationFacts(event: PromptSettledEvent): ResourceObservationFacts {\n\treturn {\n\t\toutcome: event.outcome,\n\t\tdurationMs: event.durationMs,\n\t\t...(event.terminationKind !== undefined ? { terminationKind: event.terminationKind } : {}),\n\t};\n}\n\nexport function soundObservationFacts(result: CompletionSoundResult): ResourceObservationFacts {\n\treturn {\n\t\tbackend: result.backend,\n\t\tattempted: result.attempted,\n\t\tsuccess: result.success,\n\t\t...(result.diagnostic !== undefined ? { diagnostic: result.diagnostic.slice(0, 120) } : {}),\n\t};\n}\n\nconst MAX_LINE_BYTES = 8 * 1024;\nconst MAX_RECORDS = 10_000;\n\nexport interface ResourceObservationLoadResult {\n\treadonly records: readonly ResourceObservationRecord[];\n\treadonly diagnostics: readonly string[];\n}\n\nexport class ResourceObservationJournal {\n\tprivate readonly journalPath: string;\n\tprivate readonly promptRunId: string;\n\tprivate readonly now: () => Date;\n\tprivate seq = 0;\n\tprivate disabled = false;\n\n\tconstructor(journalPath: string, promptRunId: string, now: () => Date = () => new Date()) {\n\t\tthis.journalPath = journalPath;\n\t\tthis.promptRunId = promptRunId;\n\t\tthis.now = now;\n\t}\n\n\tstatic open(cwd: string, promptRunId: string, now?: () => Date): ResourceObservationJournal {\n\t\treturn new ResourceObservationJournal(\n\t\t\tpath.join(cwd, \".omk\", \"runs\", promptRunId, RESOURCE_OBSERVATION_JOURNAL_FILE),\n\t\t\tpromptRunId,\n\t\t\tnow,\n\t\t);\n\t}\n\n\tget filePath(): string {\n\t\treturn this.journalPath;\n\t}\n\n\t/** Fail-open append: returns false (and stops trying) instead of throwing. */\n\trecord(kind: ResourceObservationKind, facts: ResourceObservationFacts): boolean {\n\t\tif (this.disabled || this.seq >= MAX_RECORDS) return false;\n\t\tconst entry: ResourceObservationRecord = {\n\t\t\tschemaVersion: 1,\n\t\t\tseq: this.seq + 1,\n\t\t\tkind,\n\t\t\tat: this.now().toISOString(),\n\t\t\tpromptRunId: this.promptRunId,\n\t\t\tfacts,\n\t\t};\n\t\tconst line = `${JSON.stringify(entry)}\\n`;\n\t\tif (line.length > MAX_LINE_BYTES) return false;\n\t\ttry {\n\t\t\tfs.mkdirSync(path.dirname(this.journalPath), { recursive: true });\n\t\t\tfs.appendFileSync(this.journalPath, line, \"utf8\");\n\t\t\tthis.seq = entry.seq;\n\t\t\treturn true;\n\t\t} catch {\n\t\t\t// Observability must never break the run; stop retrying this run.\n\t\t\tthis.disabled = true;\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/** Fail-open load: malformed lines become diagnostics, never throws. */\n\tload(): ResourceObservationLoadResult {\n\t\tlet raw: string;\n\t\ttry {\n\t\t\traw = fs.readFileSync(this.journalPath, \"utf8\");\n\t\t} catch {\n\t\t\treturn { records: [], diagnostics: [] };\n\t\t}\n\t\tconst records: ResourceObservationRecord[] = [];\n\t\tconst diagnostics: string[] = [];\n\t\tconst lines = raw.split(\"\\n\");\n\t\tfor (let index = 0; index < lines.length && records.length < MAX_RECORDS; index++) {\n\t\t\tconst line = lines[index].trim();\n\t\t\tif (line === \"\") continue;\n\t\t\tconst parsed = parseObservationLine(line);\n\t\t\tif (parsed === null) {\n\t\t\t\tdiagnostics.push(`line ${index + 1}: invalid observation record`);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\trecords.push(parsed);\n\t\t}\n\t\treturn { records, diagnostics };\n\t}\n}\n\nconst KIND_SET: ReadonlySet<string> = new Set(RESOURCE_OBSERVATION_KINDS);\n\nfunction parseObservationLine(line: string): ResourceObservationRecord | null {\n\tif (line.length > MAX_LINE_BYTES) return null;\n\tlet value: unknown;\n\ttry {\n\t\tvalue = JSON.parse(line);\n\t} catch {\n\t\treturn null;\n\t}\n\tif (typeof value !== \"object\" || value === null || Array.isArray(value)) return null;\n\tconst record = value as Record<string, unknown>;\n\tif (record.schemaVersion !== 1) return null;\n\tif (typeof record.seq !== \"number\" || !Number.isSafeInteger(record.seq) || record.seq < 1) return null;\n\tif (typeof record.kind !== \"string\" || !KIND_SET.has(record.kind)) return null;\n\tif (typeof record.at !== \"string\" || typeof record.promptRunId !== \"string\") return null;\n\tif (typeof record.facts !== \"object\" || record.facts === null || Array.isArray(record.facts)) return null;\n\tfor (const factValue of Object.values(record.facts)) {\n\t\tconst kind = typeof factValue;\n\t\tif (factValue !== null && kind !== \"string\" && kind !== \"number\" && kind !== \"boolean\") return null;\n\t}\n\treturn record as unknown as ResourceObservationRecord;\n}\n"]}
@@ -0,0 +1,198 @@
1
+ import * as fs from "node:fs";
2
+ import * as path from "node:path";
3
+ /**
4
+ * Local-only resource observation journal (roadmap §15.1/§15.2/§20.2).
5
+ *
6
+ * A dedicated JSONL artifact under `.omk/runs/<promptRunId>/` — deliberately
7
+ * NOT the hash-chained run journal: §15.2 restricts raw host facts to a
8
+ * separate local-only artifact, and extending the run journal's closed audit
9
+ * union would invalidate old readers. Shard plan/transition entries stay in
10
+ * `workload-shards.jsonl` (they already journal there); this file carries
11
+ * the remaining §20.2 entries.
12
+ *
13
+ * Privacy (§15.2, §20.3, §30): facts are flat primitives only — pressure
14
+ * levels, caps, digests, durations, reason codes. Never raw memory/cpu
15
+ * numbers, hostnames, usernames, or command text. Observability is
16
+ * fail-open: a write/read failure degrades to diagnostics, never breaks the
17
+ * run (probe failure is non-fatal by contract).
18
+ */
19
+ export const RESOURCE_OBSERVATION_JOURNAL_FILE = "resource-observations.jsonl";
20
+ /** §20.2 journal entry kinds handled by this artifact. */
21
+ export const RESOURCE_OBSERVATION_KINDS = [
22
+ "resource_snapshot_v1",
23
+ "resource_admission_v1",
24
+ "resource_lease_acquired_v1",
25
+ "resource_lease_released_v1",
26
+ "workload_classification_v1",
27
+ "workload_permit_wait_v1",
28
+ "workload_permit_acquired_v1",
29
+ "workload_permit_released_v1",
30
+ "prompt_settled_v1",
31
+ "completion_sound_result_v1",
32
+ ];
33
+ const PROTOCOL_KIND_BY_JOURNAL_KIND = {
34
+ resource_snapshot_v1: "resource_snapshot.v1",
35
+ resource_admission_v1: "resource_admission.v1",
36
+ workload_classification_v1: "workload_classification.v1",
37
+ workload_permit_wait_v1: "workload_permit.v1",
38
+ workload_permit_acquired_v1: "workload_permit.v1",
39
+ workload_permit_released_v1: "workload_permit.v1",
40
+ prompt_settled_v1: "prompt_settled.v1",
41
+ completion_sound_result_v1: "notification_result.v1",
42
+ };
43
+ /**
44
+ * Project a journal record onto the §15.1 protocol observation surface.
45
+ * Lease entries are local-only operational detail and project to null.
46
+ */
47
+ export function toProtocolObservation(record) {
48
+ const kind = PROTOCOL_KIND_BY_JOURNAL_KIND[record.kind];
49
+ return kind === undefined ? null : { kind, facts: record.facts };
50
+ }
51
+ /** §15.2: probe health and digest only — raw host values never leave the process. */
52
+ export function snapshotObservationFacts(snapshot) {
53
+ return {
54
+ cpuProbe: snapshot.systemCpuPercent !== null,
55
+ memoryProbe: snapshot.processAvailableMemoryBytes !== null || snapshot.hostFreeMemoryBytes !== null,
56
+ heapLimitKnown: snapshot.heapLimitBytes > 0,
57
+ };
58
+ }
59
+ /** §15.2 example shape: pressure, action, and the three caps. */
60
+ export function admissionObservationFacts(decision) {
61
+ return {
62
+ decisionId: decision.decisionId,
63
+ snapshotDigest: decision.snapshotDigest,
64
+ pressure: decision.pressure,
65
+ action: decision.action,
66
+ maxToolConcurrency: decision.maxToolConcurrency,
67
+ maxParallelLanes: decision.maxParallelLanes,
68
+ maxHeavyProcesses: decision.maxHeavyProcesses,
69
+ };
70
+ }
71
+ export function classificationObservationFacts(classification) {
72
+ return {
73
+ workloadClass: classification.workloadClass,
74
+ commandFamily: classification.commandFamily,
75
+ complexity: classification.complexity,
76
+ safeToAutoShard: classification.safeToAutoShard,
77
+ reasonCodes: classification.reasonCodes.slice(0, 8).join(","),
78
+ };
79
+ }
80
+ export function settledObservationFacts(event) {
81
+ return {
82
+ outcome: event.outcome,
83
+ durationMs: event.durationMs,
84
+ ...(event.terminationKind !== undefined ? { terminationKind: event.terminationKind } : {}),
85
+ };
86
+ }
87
+ export function soundObservationFacts(result) {
88
+ return {
89
+ backend: result.backend,
90
+ attempted: result.attempted,
91
+ success: result.success,
92
+ ...(result.diagnostic !== undefined ? { diagnostic: result.diagnostic.slice(0, 120) } : {}),
93
+ };
94
+ }
95
+ const MAX_LINE_BYTES = 8 * 1024;
96
+ const MAX_RECORDS = 10_000;
97
+ export class ResourceObservationJournal {
98
+ journalPath;
99
+ promptRunId;
100
+ now;
101
+ seq = 0;
102
+ disabled = false;
103
+ constructor(journalPath, promptRunId, now = () => new Date()) {
104
+ this.journalPath = journalPath;
105
+ this.promptRunId = promptRunId;
106
+ this.now = now;
107
+ }
108
+ static open(cwd, promptRunId, now) {
109
+ return new ResourceObservationJournal(path.join(cwd, ".omk", "runs", promptRunId, RESOURCE_OBSERVATION_JOURNAL_FILE), promptRunId, now);
110
+ }
111
+ get filePath() {
112
+ return this.journalPath;
113
+ }
114
+ /** Fail-open append: returns false (and stops trying) instead of throwing. */
115
+ record(kind, facts) {
116
+ if (this.disabled || this.seq >= MAX_RECORDS)
117
+ return false;
118
+ const entry = {
119
+ schemaVersion: 1,
120
+ seq: this.seq + 1,
121
+ kind,
122
+ at: this.now().toISOString(),
123
+ promptRunId: this.promptRunId,
124
+ facts,
125
+ };
126
+ const line = `${JSON.stringify(entry)}\n`;
127
+ if (line.length > MAX_LINE_BYTES)
128
+ return false;
129
+ try {
130
+ fs.mkdirSync(path.dirname(this.journalPath), { recursive: true });
131
+ fs.appendFileSync(this.journalPath, line, "utf8");
132
+ this.seq = entry.seq;
133
+ return true;
134
+ }
135
+ catch {
136
+ // Observability must never break the run; stop retrying this run.
137
+ this.disabled = true;
138
+ return false;
139
+ }
140
+ }
141
+ /** Fail-open load: malformed lines become diagnostics, never throws. */
142
+ load() {
143
+ let raw;
144
+ try {
145
+ raw = fs.readFileSync(this.journalPath, "utf8");
146
+ }
147
+ catch {
148
+ return { records: [], diagnostics: [] };
149
+ }
150
+ const records = [];
151
+ const diagnostics = [];
152
+ const lines = raw.split("\n");
153
+ for (let index = 0; index < lines.length && records.length < MAX_RECORDS; index++) {
154
+ const line = lines[index].trim();
155
+ if (line === "")
156
+ continue;
157
+ const parsed = parseObservationLine(line);
158
+ if (parsed === null) {
159
+ diagnostics.push(`line ${index + 1}: invalid observation record`);
160
+ continue;
161
+ }
162
+ records.push(parsed);
163
+ }
164
+ return { records, diagnostics };
165
+ }
166
+ }
167
+ const KIND_SET = new Set(RESOURCE_OBSERVATION_KINDS);
168
+ function parseObservationLine(line) {
169
+ if (line.length > MAX_LINE_BYTES)
170
+ return null;
171
+ let value;
172
+ try {
173
+ value = JSON.parse(line);
174
+ }
175
+ catch {
176
+ return null;
177
+ }
178
+ if (typeof value !== "object" || value === null || Array.isArray(value))
179
+ return null;
180
+ const record = value;
181
+ if (record.schemaVersion !== 1)
182
+ return null;
183
+ if (typeof record.seq !== "number" || !Number.isSafeInteger(record.seq) || record.seq < 1)
184
+ return null;
185
+ if (typeof record.kind !== "string" || !KIND_SET.has(record.kind))
186
+ return null;
187
+ if (typeof record.at !== "string" || typeof record.promptRunId !== "string")
188
+ return null;
189
+ if (typeof record.facts !== "object" || record.facts === null || Array.isArray(record.facts))
190
+ return null;
191
+ for (const factValue of Object.values(record.facts)) {
192
+ const kind = typeof factValue;
193
+ if (factValue !== null && kind !== "string" && kind !== "number" && kind !== "boolean")
194
+ return null;
195
+ }
196
+ return record;
197
+ }
198
+ //# sourceMappingURL=resource-observation-journal.js.map