open-multi-agent-kit 0.79.3 → 0.80.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (124) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/MATURITY.md +2 -2
  3. package/README.md +86 -39
  4. package/ROADMAP.md +20 -17
  5. package/dist/brand/night-city.theme.json +122 -24
  6. package/dist/brand/rust-forge.theme.json +1 -1
  7. package/dist/commands/chat/native-root-loop.d.ts +27 -3
  8. package/dist/commands/chat/native-root-loop.js +375 -49
  9. package/dist/commands/dag.js +45 -0
  10. package/dist/commands/init/content.d.ts +1 -1
  11. package/dist/commands/init/content.js +3 -3
  12. package/dist/commands/init.js +3 -3
  13. package/dist/commands/verify.js +59 -1
  14. package/dist/contracts/dag.d.ts +9 -1
  15. package/dist/evidence/graph-ontology-relation-gate.d.ts +10 -0
  16. package/dist/evidence/graph-ontology-relation-gate.js +46 -0
  17. package/dist/evidence/headroom-replay-verifier.d.ts +19 -0
  18. package/dist/evidence/headroom-replay-verifier.js +86 -0
  19. package/dist/memory/local-graph-memory-store.d.ts +75 -0
  20. package/dist/memory/local-graph-memory-store.js +570 -0
  21. package/dist/orchestration/dag.d.ts +22 -1
  22. package/dist/orchestration/evidence-gate.js +12 -0
  23. package/dist/orchestration/executor.js +108 -15
  24. package/dist/orchestration/loop-controller-headroom.d.ts +6 -0
  25. package/dist/orchestration/loop-controller-headroom.js +33 -0
  26. package/dist/orchestration/scheduler.d.ts +2 -0
  27. package/dist/orchestration/scheduler.js +3 -0
  28. package/dist/orchestration/task-graph.d.ts +17 -0
  29. package/dist/orchestration/task-graph.js +38 -0
  30. package/dist/providers/codex-cli-runner.js +17 -2
  31. package/dist/runtime/agent-runtime.d.ts +32 -3
  32. package/dist/runtime/agent-runtime.js +20 -7
  33. package/dist/runtime/authority-matrix.d.ts +23 -0
  34. package/dist/runtime/authority-matrix.js +159 -0
  35. package/dist/runtime/chat-advisory-runtime.js +3 -0
  36. package/dist/runtime/codex-cli-runtime.js +2 -0
  37. package/dist/runtime/codex-runtime.d.ts +4 -1
  38. package/dist/runtime/codex-runtime.js +34 -12
  39. package/dist/runtime/context-broker-converter.js +32 -2
  40. package/dist/runtime/context-broker.js +18 -0
  41. package/dist/runtime/contracts/evidence.d.ts +52 -0
  42. package/dist/runtime/contracts/evidence.js +128 -1
  43. package/dist/runtime/contracts/shared.d.ts +27 -0
  44. package/dist/runtime/data-retention-gate.d.ts +42 -0
  45. package/dist/runtime/data-retention-gate.js +123 -0
  46. package/dist/runtime/deepseek-runtime.d.ts +5 -1
  47. package/dist/runtime/deepseek-runtime.js +13 -7
  48. package/dist/runtime/external-cli-adapter.js +20 -7
  49. package/dist/runtime/freedomd-evidence-envelope.d.ts +57 -0
  50. package/dist/runtime/freedomd-evidence-envelope.js +110 -0
  51. package/dist/runtime/freedomd-exception.d.ts +32 -0
  52. package/dist/runtime/freedomd-exception.js +95 -0
  53. package/dist/runtime/freedomd-incidents.d.ts +23 -0
  54. package/dist/runtime/freedomd-incidents.js +103 -0
  55. package/dist/runtime/freedomd-policy.d.ts +35 -0
  56. package/dist/runtime/freedomd-policy.js +114 -0
  57. package/dist/runtime/freedomd-router.d.ts +66 -0
  58. package/dist/runtime/freedomd-router.js +433 -0
  59. package/dist/runtime/headroom-aware-loop-decision.d.ts +21 -0
  60. package/dist/runtime/headroom-aware-loop-decision.js +67 -0
  61. package/dist/runtime/headroom-loop-risk.d.ts +19 -0
  62. package/dist/runtime/headroom-loop-risk.js +43 -0
  63. package/dist/runtime/headroom-policy.d.ts +14 -1
  64. package/dist/runtime/headroom-policy.js +74 -12
  65. package/dist/runtime/kimi-api-runtime.d.ts +4 -1
  66. package/dist/runtime/kimi-api-runtime.js +35 -11
  67. package/dist/runtime/kimi-print-runtime.js +21 -0
  68. package/dist/runtime/kimi-wire-protocol-runtime.js +17 -1
  69. package/dist/runtime/local-llm-runtime.d.ts +5 -1
  70. package/dist/runtime/local-llm-runtime.js +14 -23
  71. package/dist/runtime/private-stderr.d.ts +13 -0
  72. package/dist/runtime/private-stderr.js +69 -0
  73. package/dist/runtime/prompt-envelope.d.ts +1 -1
  74. package/dist/runtime/provider-sovereignty.d.ts +81 -0
  75. package/dist/runtime/provider-sovereignty.js +285 -0
  76. package/dist/runtime/runtime-backed-task-runner.d.ts +1 -0
  77. package/dist/runtime/runtime-backed-task-runner.js +282 -10
  78. package/dist/runtime/runtime-bootstrap.d.ts +12 -0
  79. package/dist/runtime/runtime-bootstrap.js +56 -3
  80. package/dist/runtime/runtime-failure-classifier.d.ts +15 -0
  81. package/dist/runtime/runtime-failure-classifier.js +107 -0
  82. package/dist/runtime/runtime-health-probes.d.ts +30 -0
  83. package/dist/runtime/runtime-health-probes.js +102 -0
  84. package/dist/runtime/runtime-router.d.ts +6 -2
  85. package/dist/runtime/runtime-router.js +575 -53
  86. package/dist/runtime/structured-compaction.d.ts +107 -0
  87. package/dist/runtime/structured-compaction.js +414 -0
  88. package/dist/runtime/tool-dispatch-contracts.d.ts +7 -5
  89. package/dist/runtime/tool-dispatch-contracts.js +15 -4
  90. package/dist/runtime/tool-plane.d.ts +1 -1
  91. package/dist/runtime/tool-plane.js +49 -1
  92. package/dist/runtime/worker-manifest.js +1 -1
  93. package/dist/schema/evidence-bundle.schema.d.ts +8 -8
  94. package/dist/schema/proof-bundle.schema.d.ts +2 -2
  95. package/dist/util/hash.d.ts +2 -0
  96. package/dist/util/hash.js +13 -0
  97. package/dist/util/session.js +6 -5
  98. package/docs/2026-06-16/critical-issues.md +19 -0
  99. package/docs/2026-06-16/improvements.md +15 -0
  100. package/docs/2026-06-16/init-checklist.md +25 -0
  101. package/docs/2026-06-16/plan.md +20 -0
  102. package/docs/ARCHITECTURE_ANALYSIS_CODEGRAPH.md +2 -1
  103. package/docs/GSTACK_MIGRATION.md +1 -1
  104. package/docs/algorithm-hardening-playbook.md +219 -0
  105. package/docs/claims.md +1 -1
  106. package/docs/getting-started.md +1 -1
  107. package/docs/native-root-runtime-hardening.md +42 -32
  108. package/docs/post-0793-hardening-roadmap.md +123 -0
  109. package/docs/provider-maturity.md +18 -16
  110. package/docs/versioning.md +3 -3
  111. package/docs/what-is-omk.md +1 -1
  112. package/package.json +8 -4
  113. package/readmeasset/.npmignore +2 -0
  114. package/readmeasset/ASSET_INDEX.md +2 -0
  115. package/readmeasset/ASSET_PROVENANCE.md +40 -20
  116. package/readmeasset/omk-adaptorch-ouroboros-supermemory.svg +1 -1
  117. package/readmeasset/omk-control-surfaces.svg +1 -1
  118. package/readmeasset/omk-core-loop.svg +1 -1
  119. package/readmeasset/omk-freedomd-control-plane.svg +59 -0
  120. package/readmeasset/omk-freedomd-control-plane.webp +0 -0
  121. package/readmeasset/omk-init-control-loop.svg +1 -1
  122. package/readmeasset/omk-logo-mark.svg +1 -1
  123. package/readmeasset/omk-parallel-subagents.svg +1 -1
  124. package/readmeasset/omk-release-assertions.svg +1 -1
@@ -0,0 +1,107 @@
1
+ import type { ContextCapsule } from "./context-capsule.js";
2
+ export interface StructuredCompactionContractV1 {
3
+ readonly schemaVersion: "omk.structured-compaction.v1";
4
+ readonly requiredSections: readonly string[];
5
+ readonly minTaskPrefixChars: number;
6
+ readonly safetyMarkers: readonly string[];
7
+ }
8
+ export interface StructuredCompactionContractV2 {
9
+ readonly schemaVersion: "omk.structured-compaction.v2";
10
+ readonly node: {
11
+ readonly id: string;
12
+ readonly role: string;
13
+ };
14
+ readonly routing: {
15
+ readonly provider: string;
16
+ readonly risk: string;
17
+ readonly sandboxMode: string;
18
+ readonly readOnly: boolean;
19
+ readonly approvalPolicy: string;
20
+ };
21
+ readonly evidence: {
22
+ readonly required: readonly {
23
+ readonly gate: string;
24
+ readonly ref?: string;
25
+ }[];
26
+ };
27
+ readonly capabilities: readonly string[];
28
+ readonly safety: {
29
+ readonly evidenceRequired: boolean;
30
+ readonly preserve: readonly string[];
31
+ };
32
+ }
33
+ export type StructuredCompactionContract = StructuredCompactionContractV1 | StructuredCompactionContractV2;
34
+ export interface StructuredCompactionValidationResult {
35
+ readonly ok: boolean;
36
+ readonly missing: readonly string[];
37
+ readonly contract: StructuredCompactionContract;
38
+ }
39
+ export interface StructuredCompactionBuildOptions {
40
+ /** Approximate target budget for the generated fallback text. */
41
+ readonly maxTokens?: number;
42
+ /** Apply secret redaction to all non-contract free text. Default true. */
43
+ readonly redact?: boolean;
44
+ readonly maxMemoryFacts?: number;
45
+ readonly maxDependencySummaries?: number;
46
+ readonly estimator?: TokenEstimationOptions;
47
+ }
48
+ export interface TokenEstimationCalibration {
49
+ readonly multiplier?: number;
50
+ readonly bias?: number;
51
+ }
52
+ export interface TokenEstimationOptions {
53
+ readonly provider?: string;
54
+ readonly model?: string;
55
+ readonly calibration?: TokenEstimationCalibration;
56
+ }
57
+ export interface CompactionQualityInput {
58
+ readonly applied: boolean;
59
+ readonly validated: boolean;
60
+ readonly beforeTokens?: number | null;
61
+ readonly afterTokens?: number | null;
62
+ readonly missingSections?: readonly string[];
63
+ }
64
+ export interface CompactionQualityScore {
65
+ readonly qualityScore: number;
66
+ readonly compressionRatio: number | null;
67
+ readonly contractScore: number;
68
+ readonly compressionScore: number;
69
+ readonly evidenceScore: number;
70
+ readonly safetyScore: number;
71
+ readonly capabilityScore: number;
72
+ }
73
+ export declare const DEFAULT_STRUCTURED_COMPACTION_CONTRACT: StructuredCompactionContractV1;
74
+ export declare const STRUCTURED_COMPACTION_V2_SCHEMA_VERSION: "omk.structured-compaction.v2";
75
+ export interface CompactionQualityGateInput {
76
+ readonly applied: boolean;
77
+ readonly validated: boolean;
78
+ readonly qualityScore: number;
79
+ readonly contractScore: number;
80
+ readonly risk?: string;
81
+ readonly capabilities?: {
82
+ readonly write?: boolean;
83
+ readonly patch?: boolean;
84
+ readonly shell?: boolean;
85
+ readonly merge?: boolean;
86
+ };
87
+ }
88
+ export type CompactionGateDecision = "not-attempted" | "not-applied" | "accept" | "accept-with-warning" | "reject";
89
+ export interface CompactionQualityGateResult {
90
+ readonly gateDecision: CompactionGateDecision;
91
+ readonly warning?: string;
92
+ readonly threshold: number;
93
+ }
94
+ export declare function resolveCompactionQualityThreshold(input: {
95
+ readonly risk?: string;
96
+ readonly capabilities?: CompactionQualityGateInput["capabilities"];
97
+ }): number;
98
+ export declare function evaluateCompactionQualityGate(input: CompactionQualityGateInput): CompactionQualityGateResult;
99
+ export declare function validateStructuredCompaction(compactedText: string, capsule: ContextCapsule, contract?: StructuredCompactionContractV1): StructuredCompactionValidationResult;
100
+ export declare function buildTypedStructuredCompactionContract(capsule: ContextCapsule): StructuredCompactionContractV2;
101
+ export declare function renderTypedStructuredCompactionContract(contract: StructuredCompactionContractV2): string;
102
+ export declare function parseTypedStructuredCompactionContract(compactedText: string): StructuredCompactionContractV2 | null;
103
+ export declare function structuredCompactionGuardNote(result: StructuredCompactionValidationResult): string;
104
+ export declare function structuredCompactionInstruction(contract?: StructuredCompactionContract): string;
105
+ export declare function buildStructuredCompactionText(capsule: ContextCapsule, contract?: StructuredCompactionContractV1, options?: StructuredCompactionBuildOptions): string;
106
+ export declare function estimateTextTokens(text: string, options?: TokenEstimationOptions): number;
107
+ export declare function computeCompactionQualityScore(input: CompactionQualityInput): CompactionQualityScore;
@@ -0,0 +1,414 @@
1
+ import { maskSensitiveText } from "../util/secret-mask.js";
2
+ export const DEFAULT_STRUCTURED_COMPACTION_CONTRACT = {
3
+ schemaVersion: "omk.structured-compaction.v1",
4
+ requiredSections: [
5
+ "task",
6
+ "node routing",
7
+ "evidence requirements",
8
+ "safety constraints",
9
+ "capabilities",
10
+ ],
11
+ minTaskPrefixChars: 40,
12
+ safetyMarkers: ["preserve", "safety", "constraints", "evidence required", "capabilities"],
13
+ };
14
+ export const STRUCTURED_COMPACTION_V2_SCHEMA_VERSION = "omk.structured-compaction.v2";
15
+ export function resolveCompactionQualityThreshold(input) {
16
+ const risk = (input.risk ?? "").toLowerCase();
17
+ const caps = input.capabilities ?? {};
18
+ if (caps.merge === true || caps.shell === true)
19
+ return 0.85;
20
+ if (caps.write === true || caps.patch === true)
21
+ return 0.75;
22
+ if (risk.includes("shell") || risk.includes("merge") || risk.includes("release"))
23
+ return 0.85;
24
+ if (risk.includes("write") || risk.includes("patch"))
25
+ return 0.75;
26
+ return 0.60;
27
+ }
28
+ function isStrictGuardrailMode() {
29
+ const raw = process.env.OMK_STRICT_GUARDRAIL ?? "";
30
+ const normalized = raw.trim().toLowerCase();
31
+ return normalized === "1" || normalized === "true" || normalized === "on";
32
+ }
33
+ export function evaluateCompactionQualityGate(input) {
34
+ if (!input.applied) {
35
+ return { gateDecision: input.validated ? "not-applied" : "not-attempted", threshold: resolveCompactionQualityThreshold(input) };
36
+ }
37
+ const threshold = resolveCompactionQualityThreshold(input);
38
+ if (input.qualityScore >= threshold) {
39
+ return { gateDecision: "accept", threshold };
40
+ }
41
+ if (input.qualityScore >= Math.max(0, threshold - 0.20) && input.contractScore === 1) {
42
+ return { gateDecision: "accept-with-warning", warning: "low compaction quality", threshold };
43
+ }
44
+ if (!isStrictGuardrailMode()) {
45
+ return { gateDecision: "accept-with-warning", warning: "low compaction quality accepted under default freedom mode", threshold };
46
+ }
47
+ return {
48
+ gateDecision: "reject",
49
+ warning: "compaction quality below safety threshold",
50
+ threshold,
51
+ };
52
+ }
53
+ export function validateStructuredCompaction(compactedText, capsule, contract = DEFAULT_STRUCTURED_COMPACTION_CONTRACT) {
54
+ const typed = parseTypedStructuredCompactionContract(compactedText);
55
+ if (typed)
56
+ return validateTypedStructuredCompaction(compactedText, capsule, typed);
57
+ return validateLegacyStructuredCompaction(compactedText, capsule, contract);
58
+ }
59
+ function validateLegacyStructuredCompaction(compactedText, capsule, contract) {
60
+ const lower = compactedText.toLowerCase();
61
+ const missing = [];
62
+ if (contract.requiredSections.includes("task")) {
63
+ const taskLower = capsule.task.toLowerCase().trim();
64
+ const prefixLength = Math.min(contract.minTaskPrefixChars, taskLower.length);
65
+ if (taskLower.length > 0 && !lower.includes(taskLower.slice(0, prefixLength))) {
66
+ missing.push("task");
67
+ }
68
+ }
69
+ const routing = capsule.node.routing;
70
+ if (routing && contract.requiredSections.includes("node routing")) {
71
+ if (routing.provider && !lower.includes(routing.provider.toLowerCase()))
72
+ missing.push("node routing provider");
73
+ if (routing.risk && !lower.includes(routing.risk.toLowerCase()))
74
+ missing.push("node routing risk");
75
+ if (routing.sandboxMode && !lower.includes(routing.sandboxMode.toLowerCase()))
76
+ missing.push("node routing sandboxMode");
77
+ if (routing.readOnly === true && !lower.includes("read-only") && !lower.includes("readonly")) {
78
+ missing.push("node routing readOnly");
79
+ }
80
+ }
81
+ if (contract.requiredSections.includes("evidence requirements")) {
82
+ const requiredGates = capsule.evidenceRequirements
83
+ .filter((e) => e.required)
84
+ .map((e) => e.gate.toLowerCase());
85
+ if (requiredGates.length > 0 && !requiredGates.some((gate) => lower.includes(gate))) {
86
+ missing.push("evidence requirements");
87
+ }
88
+ }
89
+ if (contract.requiredSections.includes("safety constraints")) {
90
+ const systemLower = capsule.system.toLowerCase();
91
+ const hasSafetyMarker = contract.safetyMarkers.some((marker) => lower.includes(marker));
92
+ const hasSystemOverlap = systemLower.length > 0 && lower.includes(systemLower.slice(0, Math.min(60, systemLower.length)));
93
+ if (!hasSafetyMarker && !hasSystemOverlap)
94
+ missing.push("safety constraints");
95
+ }
96
+ if (contract.requiredSections.includes("capabilities")) {
97
+ const assignedCaps = new Set(routing?.assignedProviderCapabilities?.map((c) => c.toLowerCase()) ?? []);
98
+ if (assignedCaps.size > 0 && !Array.from(assignedCaps).some((cap) => lower.includes(cap))) {
99
+ missing.push("capabilities");
100
+ }
101
+ }
102
+ return { ok: missing.length === 0, missing, contract };
103
+ }
104
+ export function buildTypedStructuredCompactionContract(capsule) {
105
+ const routing = capsule.node.routing;
106
+ const requiredEvidence = capsule.evidenceRequirements
107
+ .filter((e) => e.required)
108
+ .map((e) => (e.ref ? { gate: e.gate, ref: e.ref } : { gate: e.gate }));
109
+ return {
110
+ schemaVersion: STRUCTURED_COMPACTION_V2_SCHEMA_VERSION,
111
+ node: {
112
+ id: capsule.nodeId,
113
+ role: String(capsule.node.role ?? "worker"),
114
+ },
115
+ routing: {
116
+ provider: routing?.provider ?? "auto",
117
+ risk: routing?.risk ?? "read",
118
+ sandboxMode: routing?.sandboxMode ?? "unknown",
119
+ readOnly: routing?.readOnly === true,
120
+ approvalPolicy: routing?.approvalPolicy ?? "unknown",
121
+ },
122
+ evidence: {
123
+ required: requiredEvidence,
124
+ },
125
+ capabilities: routing?.assignedProviderCapabilities ?? [],
126
+ safety: {
127
+ evidenceRequired: routing?.evidenceRequired === true || requiredEvidence.length > 0,
128
+ preserve: DEFAULT_STRUCTURED_COMPACTION_CONTRACT.requiredSections,
129
+ },
130
+ };
131
+ }
132
+ export function renderTypedStructuredCompactionContract(contract) {
133
+ return [
134
+ "```json omk.structured-compaction.v2",
135
+ JSON.stringify(contract, null, 2),
136
+ "```",
137
+ ].join("\n");
138
+ }
139
+ export function parseTypedStructuredCompactionContract(compactedText) {
140
+ const match = compactedText.match(/```json omk\.structured-compaction\.v2\s*\n([\s\S]*?)\n```/i);
141
+ if (!match)
142
+ return null;
143
+ try {
144
+ const parsed = JSON.parse(match[1]);
145
+ if (!isRecord(parsed) || parsed.schemaVersion !== STRUCTURED_COMPACTION_V2_SCHEMA_VERSION)
146
+ return null;
147
+ if (!isRecord(parsed.node) || typeof parsed.node.id !== "string" || typeof parsed.node.role !== "string")
148
+ return null;
149
+ if (!isRecord(parsed.routing))
150
+ return null;
151
+ if (!isRecord(parsed.evidence) || !Array.isArray(parsed.evidence.required))
152
+ return null;
153
+ if (!Array.isArray(parsed.capabilities))
154
+ return null;
155
+ if (!isRecord(parsed.safety) || !Array.isArray(parsed.safety.preserve))
156
+ return null;
157
+ return parsed;
158
+ }
159
+ catch {
160
+ return null;
161
+ }
162
+ }
163
+ function validateTypedStructuredCompaction(compactedText, capsule, contract) {
164
+ const missing = [];
165
+ const expected = buildTypedStructuredCompactionContract(capsule);
166
+ const lower = compactedText.toLowerCase();
167
+ for (const section of DEFAULT_STRUCTURED_COMPACTION_CONTRACT.requiredSections) {
168
+ if (!lower.includes(`## ${section}`))
169
+ missing.push(section);
170
+ }
171
+ if (contract.node.id !== expected.node.id)
172
+ missing.push("typed node id");
173
+ if (contract.node.role !== expected.node.role)
174
+ missing.push("typed node role");
175
+ if (contract.routing.provider !== expected.routing.provider)
176
+ missing.push("typed routing provider");
177
+ if (contract.routing.risk !== expected.routing.risk)
178
+ missing.push("typed routing risk");
179
+ if (contract.routing.sandboxMode !== expected.routing.sandboxMode)
180
+ missing.push("typed routing sandboxMode");
181
+ if (contract.routing.readOnly !== expected.routing.readOnly)
182
+ missing.push("typed routing readOnly");
183
+ if (contract.routing.approvalPolicy !== expected.routing.approvalPolicy)
184
+ missing.push("typed routing approvalPolicy");
185
+ if (!sameStringSet(contract.capabilities, expected.capabilities))
186
+ missing.push("typed capabilities");
187
+ if (!sameEvidence(contract.evidence.required, expected.evidence.required))
188
+ missing.push("typed evidence requirements");
189
+ if (contract.safety.evidenceRequired !== expected.safety.evidenceRequired)
190
+ missing.push("typed safety evidenceRequired");
191
+ if (!DEFAULT_STRUCTURED_COMPACTION_CONTRACT.requiredSections.every((section) => contract.safety.preserve.includes(section))) {
192
+ missing.push("typed safety preserve");
193
+ }
194
+ return { ok: missing.length === 0, missing, contract };
195
+ }
196
+ export function structuredCompactionGuardNote(result) {
197
+ return `Headroom compaction removed required sections: ${result.missing.join(", ")}; using original capsule.`;
198
+ }
199
+ export function structuredCompactionInstruction(contract = DEFAULT_STRUCTURED_COMPACTION_CONTRACT) {
200
+ if (contract.schemaVersion === STRUCTURED_COMPACTION_V2_SCHEMA_VERSION) {
201
+ return [
202
+ `${contract.schemaVersion}: the original context capsule was compacted before runtime dispatch.`,
203
+ "The typed JSON contract below is authoritative for routing, evidence, safety, and capabilities.",
204
+ "Preserve task, node routing, evidence requirements, safety constraints, and capability grants below.",
205
+ ].join(" ");
206
+ }
207
+ return [
208
+ `${contract.schemaVersion}: the original context capsule was compacted before runtime dispatch.`,
209
+ `Required sections: ${contract.requiredSections.join(", ")}.`,
210
+ "Preserve task, node routing, evidence requirements, safety constraints, and capability grants below.",
211
+ ].join(" ");
212
+ }
213
+ export function buildStructuredCompactionText(capsule, contract = DEFAULT_STRUCTURED_COMPACTION_CONTRACT, options = {}) {
214
+ const typedContract = buildTypedStructuredCompactionContract(capsule);
215
+ const routing = capsule.node.routing;
216
+ const redact = options.redact !== false;
217
+ const safe = (value, maxChars = 1_200) => truncateChars(redactText(String(value ?? ""), redact), maxChars);
218
+ const requiredEvidence = capsule.evidenceRequirements
219
+ .filter((e) => e.required)
220
+ .map((e) => `${safe(e.gate, 120)}${e.ref ? `:${safe(e.ref, 240)}` : ""}`)
221
+ .join(", ") || "none";
222
+ const capabilities = routing?.assignedProviderCapabilities?.map((cap) => safe(cap, 120)).join(", ") || "none";
223
+ const hardSections = [
224
+ structuredCompactionInstruction(typedContract),
225
+ renderTypedStructuredCompactionContract(typedContract),
226
+ "",
227
+ "## task",
228
+ safe(capsule.task, 1_600),
229
+ "",
230
+ "## node routing",
231
+ [
232
+ `node=${safe(capsule.nodeId, 160)}`,
233
+ `role=${safe(capsule.node.role, 120)}`,
234
+ `provider=${safe(routing?.provider ?? "auto", 120)}`,
235
+ `risk=${safe(routing?.risk ?? "read", 120)}`,
236
+ `sandboxMode=${safe(routing?.sandboxMode ?? "unknown", 120)}`,
237
+ `readOnly=${routing?.readOnly === true ? "read-only" : "write-capable"}`,
238
+ `approvalPolicy=${safe(routing?.approvalPolicy ?? "unknown", 120)}`,
239
+ ].join("; "),
240
+ "",
241
+ "## evidence requirements",
242
+ requiredEvidence,
243
+ "",
244
+ "## safety constraints",
245
+ [
246
+ "preserve safety constraints",
247
+ routing?.evidenceRequired ? "evidence required" : "evidence optional",
248
+ `goal=${safe(capsule.goal, 400)}`,
249
+ safe(capsule.system, 700),
250
+ ].filter(Boolean).join("; "),
251
+ "",
252
+ "## capabilities",
253
+ capabilities,
254
+ ].join("\n");
255
+ const targetTokens = Math.max(256, Math.floor(options.maxTokens ?? (capsule.budget.maxInputTokens - capsule.budget.reservedOutputTokens)));
256
+ const estimator = options.estimator;
257
+ let remainingTokens = Math.max(0, targetTokens - estimateTextTokens(hardSections, estimator));
258
+ const selected = selectCandidateSections(capsule, remainingTokens, options, redact);
259
+ return [hardSections, ...selected].join("\n");
260
+ }
261
+ function selectCandidateSections(capsule, budgetTokens, options, redact) {
262
+ const candidates = [];
263
+ const maxDeps = Math.max(0, options.maxDependencySummaries ?? 5);
264
+ for (const [index, summary] of capsule.dependencySummaries.slice(0, maxDeps).entries()) {
265
+ candidates.push({
266
+ heading: "dependency summaries",
267
+ text: redactText(summary, redact),
268
+ priority: 0.78 - index * 0.02,
269
+ });
270
+ }
271
+ const maxMemoryFacts = Math.max(0, options.maxMemoryFacts ?? capsule.budget.maxMemoryFacts);
272
+ for (const [index, fact] of capsule.graphMemory.slice(0, maxMemoryFacts).entries()) {
273
+ const text = redactText(`${fact.key}=${fact.value}`, redact);
274
+ candidates.push({
275
+ heading: "graph memory",
276
+ text,
277
+ priority: memoryPriority(fact.kind) - index * 0.01,
278
+ });
279
+ }
280
+ for (const [index, file] of capsule.relevantFiles.slice(0, 5).entries()) {
281
+ candidates.push({
282
+ heading: "file summaries",
283
+ text: redactText(`${file.path}:${file.startLine}-${file.endLine}\n${truncateChars(file.content, 1_000)}`, redact),
284
+ priority: 0.62 - index * 0.02,
285
+ });
286
+ }
287
+ for (const [index, attempt] of capsule.priorAttempts.slice(0, 5).entries()) {
288
+ candidates.push({
289
+ heading: "prior attempts",
290
+ text: redactText(`attempt=${attempt.attempt} provider=${attempt.provider} status=${attempt.status} durationMs=${attempt.durationMs ?? "?"} failure=${attempt.failureSummary ?? "none"}`, redact),
291
+ priority: 0.58 - index * 0.02,
292
+ });
293
+ }
294
+ const selected = {};
295
+ const sorted = candidates
296
+ .map((candidate) => ({ ...candidate, cost: Math.max(1, estimateTextTokens(candidate.text, options.estimator)) }))
297
+ .sort((a, b) => (b.priority / b.cost) - (a.priority / a.cost));
298
+ let remaining = budgetTokens;
299
+ for (const candidate of sorted) {
300
+ if (remaining <= 0)
301
+ break;
302
+ const cost = Math.max(1, estimateTextTokens(candidate.text, options.estimator));
303
+ const text = cost <= remaining
304
+ ? candidate.text
305
+ : truncateToTokenBudget(candidate.text, remaining);
306
+ if (!text.trim())
307
+ continue;
308
+ selected[candidate.heading] ??= [];
309
+ selected[candidate.heading].push(text);
310
+ remaining -= estimateTextTokens(text, options.estimator);
311
+ }
312
+ const headings = ["dependency summaries", "graph memory", "file summaries", "prior attempts"];
313
+ return headings.map((heading) => {
314
+ const lines = selected[heading];
315
+ return ["", `## ${heading}`, lines?.length ? lines.join(" | ") : "none"].join("\n");
316
+ });
317
+ }
318
+ function memoryPriority(kind) {
319
+ switch (kind) {
320
+ case "failure_pattern":
321
+ case "provider_behavior":
322
+ return 0.72;
323
+ case "architecture_decision":
324
+ case "api_contract":
325
+ return 0.66;
326
+ default:
327
+ return 0.54;
328
+ }
329
+ }
330
+ export function estimateTextTokens(text, options = {}) {
331
+ const base = Math.ceil(text.length / 4);
332
+ if (base <= 0)
333
+ return 0;
334
+ const hangulChars = text.match(/[\u3131-\u318E\uAC00-\uD7A3]/g)?.length ?? 0;
335
+ const hangulRatio = hangulChars / Math.max(1, text.length);
336
+ const hasJsonOrCode = /```|[{}[\]\s\S]*?:[\s\S]*?[}]|\b(function|class|interface|const|let|import|export)\b/.test(text);
337
+ const languageFactor = hangulRatio > 0.1 ? 1.25 : 1;
338
+ const structureFactor = hasJsonOrCode ? 1.10 : 1;
339
+ const multiplier = options.calibration?.multiplier ?? 1;
340
+ const bias = options.calibration?.bias ?? 0;
341
+ return Math.max(0, Math.ceil(base * languageFactor * structureFactor * multiplier + bias));
342
+ }
343
+ export function computeCompactionQualityScore(input) {
344
+ if (!input.applied) {
345
+ return {
346
+ qualityScore: 0,
347
+ compressionRatio: null,
348
+ contractScore: 0,
349
+ compressionScore: 0,
350
+ evidenceScore: 0,
351
+ safetyScore: 0,
352
+ capabilityScore: 0,
353
+ };
354
+ }
355
+ const before = Math.max(1, input.beforeTokens ?? 0);
356
+ const after = Math.max(0, input.afterTokens ?? 0);
357
+ const compressionRatio = after / before;
358
+ const compressionScore = clamp01(1 - compressionRatio);
359
+ const missing = new Set(input.missingSections ?? []);
360
+ const contractScore = input.validated ? 1 : 0;
361
+ const evidenceScore = missing.has("evidence requirements") || missing.has("typed evidence requirements") ? 0 : 1;
362
+ const safetyScore = missing.has("safety constraints") || missing.has("typed safety evidenceRequired") || missing.has("typed safety preserve") ? 0 : 1;
363
+ const capabilityScore = missing.has("capabilities") || missing.has("typed capabilities") ? 0 : 1;
364
+ const qualityScore = clamp01(0.35 * contractScore
365
+ + 0.25 * compressionScore
366
+ + 0.15 * evidenceScore
367
+ + 0.15 * safetyScore
368
+ + 0.10 * capabilityScore);
369
+ return {
370
+ qualityScore,
371
+ compressionRatio,
372
+ contractScore,
373
+ compressionScore,
374
+ evidenceScore,
375
+ safetyScore,
376
+ capabilityScore,
377
+ };
378
+ }
379
+ function clamp01(value) {
380
+ return Math.max(0, Math.min(1, value));
381
+ }
382
+ function truncateToTokenBudget(text, budgetTokens) {
383
+ const maxChars = Math.max(0, budgetTokens * 4);
384
+ return truncateChars(text, maxChars);
385
+ }
386
+ function truncateChars(text, maxChars) {
387
+ if (maxChars <= 0)
388
+ return "";
389
+ if (text.length <= maxChars)
390
+ return text;
391
+ if (maxChars <= 16)
392
+ return text.slice(0, maxChars);
393
+ return `${text.slice(0, maxChars - 16)}…[truncated]`;
394
+ }
395
+ function redactText(text, redact) {
396
+ return redact ? maskSensitiveText(text) : text;
397
+ }
398
+ function sameStringSet(a, b) {
399
+ const left = new Set(a);
400
+ const right = new Set(b);
401
+ if (left.size !== right.size)
402
+ return false;
403
+ for (const value of left)
404
+ if (!right.has(value))
405
+ return false;
406
+ return true;
407
+ }
408
+ function sameEvidence(a, b) {
409
+ const key = (item) => `${item.gate}\u0000${item.ref ?? ""}`;
410
+ return sameStringSet(a.map(key), b.map(key));
411
+ }
412
+ function isRecord(value) {
413
+ return typeof value === "object" && value !== null;
414
+ }
@@ -8,8 +8,8 @@ export interface ToolDispatchResult<R = unknown> {
8
8
  readonly value?: R;
9
9
  readonly reason?: unknown;
10
10
  }
11
- /** Shadow = record only; enforce = a block/ask(non-TTY) verdict rejects the call. */
12
- export type ToolAuthorityMode = "shadow" | "enforce";
11
+ /** Shadow = record only; warn = visible diagnostic; enforce = a block/ask(non-TTY) verdict rejects the call. */
12
+ export type ToolAuthorityMode = "shadow" | "warn" | "enforce";
13
13
  /**
14
14
  * Per-call verdict recorded at the dispatch checkpoint. Carries only coarse,
15
15
  * non-secret signals (op class, authority levels, policy) — never tool args.
@@ -68,10 +68,12 @@ export interface ToolAuthorityWiring {
68
68
  readonly resolveWritePath?: (call: OmkToolCall) => string | undefined;
69
69
  }
70
70
  /**
71
- * Resolve the global enforcement opt-in from the environment. Default OFF means
72
- * the gate runs in shadow mode (record only). Set `OMK_TOOL_AUTHORITY_ENFORCE=1`
73
- * to enable fail-closed enforcement at the dispatch checkpoint.
71
+ * Resolve staged authority mode from the environment. Default OFF means the gate
72
+ * runs in shadow mode (record only). `warn` emits visible diagnostics without
73
+ * blocking. `enforce` fail-closes block/ask(non-TTY) verdicts.
74
74
  */
75
+ export declare function resolveToolAuthorityMode(env?: Record<string, string | undefined>): ToolAuthorityMode;
76
+ /** Backward-compatible boolean resolver for existing dispatch call sites. */
75
77
  export declare function resolveToolAuthorityEnforcement(env?: Record<string, string | undefined>): boolean;
76
78
  /** Error used to reject a tool call rejected by the authority gate (enforce mode). */
77
79
  export declare class ToolAuthorityBlockedError extends Error {
@@ -3,12 +3,23 @@ import { decideToolAuthority, decideToolAuthorityV2, mapToolNameToOp, } from "..
3
3
  import { assertWritable } from "./sandbox-profile.js";
4
4
  const ENFORCE_PATTERN = /^(1|true|yes|on)$/i;
5
5
  /**
6
- * Resolve the global enforcement opt-in from the environment. Default OFF means
7
- * the gate runs in shadow mode (record only). Set `OMK_TOOL_AUTHORITY_ENFORCE=1`
8
- * to enable fail-closed enforcement at the dispatch checkpoint.
6
+ * Resolve staged authority mode from the environment. Default OFF means the gate
7
+ * runs in shadow mode (record only). `warn` emits visible diagnostics without
8
+ * blocking. `enforce` fail-closes block/ask(non-TTY) verdicts.
9
9
  */
10
+ export function resolveToolAuthorityMode(env = process.env) {
11
+ const rawMode = (env.OMK_TOOL_AUTHORITY_MODE ?? "").trim().toLowerCase();
12
+ if (rawMode === "enforce" || rawMode === "warn" || rawMode === "shadow")
13
+ return rawMode;
14
+ if (ENFORCE_PATTERN.test((env.OMK_TOOL_AUTHORITY_ENFORCE ?? "").trim()))
15
+ return "enforce";
16
+ if (ENFORCE_PATTERN.test((env.OMK_TOOL_AUTHORITY_WARN ?? "").trim()))
17
+ return "warn";
18
+ return "shadow";
19
+ }
20
+ /** Backward-compatible boolean resolver for existing dispatch call sites. */
10
21
  export function resolveToolAuthorityEnforcement(env = process.env) {
11
- return ENFORCE_PATTERN.test((env.OMK_TOOL_AUTHORITY_ENFORCE ?? "").trim());
22
+ return resolveToolAuthorityMode(env) === "enforce";
12
23
  }
13
24
  /** Build a redacted reason string from non-secret authority signals only. */
14
25
  function redactedAuthorityReason(op, decision, wiring) {
@@ -2,7 +2,7 @@ import type { OmkRuntimeScope } from "../util/resource-profile.js";
2
2
  import { type OmkToolPrefixSpec } from "./tool-registry-contract.js";
3
3
  export interface OmkToolPlaneDiagnostic {
4
4
  readonly level: "warning" | "error";
5
- readonly code: "mcp_config_parse_failed" | "mcp_config_read_failed" | "runtime_mcp_config_parse_failed" | "runtime_mcp_required_unavailable";
5
+ readonly code: "mcp_config_parse_failed" | "mcp_config_read_failed" | "runtime_mcp_config_parse_failed" | "runtime_mcp_required_unavailable" | "skill_reference_unknown" | "skill_config_read_failed" | "hook_reference_unknown" | "hook_config_read_failed";
6
6
  readonly path?: string;
7
7
  readonly message: string;
8
8
  }
@@ -7,7 +7,9 @@ export async function buildOmkToolPlaneManifest(input) {
7
7
  const requiresRuntimeMcp = input.requiresRuntimeMcp === true;
8
8
  const resolved = await resolveRuntimeMcpConfigFile(input.mcpScope, input.mcpAllowlist);
9
9
  const runtimeRead = resolved.mcpConfigFile ? await readMcpServerNames(resolved.mcpConfigFile) : { servers: [], diagnostics: [] };
10
- const diagnostics = [...resolved.diagnostics, ...runtimeRead.diagnostics];
10
+ const skillDiagnostics = await validateSkillReferences(input.skills ?? []);
11
+ const hookDiagnostics = await validateHookReferences(input.hooks ?? []);
12
+ const diagnostics = [...resolved.diagnostics, ...runtimeRead.diagnostics, ...skillDiagnostics, ...hookDiagnostics];
11
13
  if (requiresRuntimeMcp && (diagnostics.some((diagnostic) => diagnostic.level === "error") || !resolved.mcpConfigFile || runtimeRead.servers.length === 0)) {
12
14
  throw new Error(formatRequiredRuntimeMcpError(diagnostics, resolved.mcpConfigFile, runtimeRead.servers));
13
15
  }
@@ -97,6 +99,52 @@ function mcpDiagnosticMessage(err) {
97
99
  }
98
100
  return "invalid MCP config";
99
101
  }
102
+ async function validateSkillReferences(skills) {
103
+ if (skills.length === 0)
104
+ return [];
105
+ const diagnostics = [];
106
+ const projectRoot = resolve(await getProjectRootAsync());
107
+ for (const skill of skills) {
108
+ const safeName = skill.replace(/[^a-zA-Z0-9_-]/g, "");
109
+ if (safeName !== skill) {
110
+ diagnostics.push({ level: "warning", code: "skill_reference_unknown", message: `skill name contains unsafe characters: ${skill}` });
111
+ continue;
112
+ }
113
+ const paths = [
114
+ resolve(projectRoot, ".agents/skills", safeName, "SKILL.md"),
115
+ resolve(projectRoot, ".kimi/skills", safeName, "SKILL.md"),
116
+ resolve(getUserHome(), ".omk/agent/skills", safeName, "SKILL.md"),
117
+ ];
118
+ const found = await Promise.all(paths.map((p) => readFile(p, "utf-8").then(() => true).catch(() => false)));
119
+ if (!found.some(Boolean)) {
120
+ diagnostics.push({ level: "warning", code: "skill_reference_unknown", message: `skill not found in project or user skill paths: ${skill}` });
121
+ }
122
+ }
123
+ return diagnostics;
124
+ }
125
+ async function validateHookReferences(hooks) {
126
+ if (hooks.length === 0)
127
+ return [];
128
+ const diagnostics = [];
129
+ const projectRoot = resolve(await getProjectRootAsync());
130
+ for (const hook of hooks) {
131
+ const safeName = hook.replace(/[^a-zA-Z0-9_.-]/g, "");
132
+ if (safeName !== hook) {
133
+ diagnostics.push({ level: "warning", code: "hook_reference_unknown", message: `hook name contains unsafe characters: ${hook}` });
134
+ continue;
135
+ }
136
+ const paths = [
137
+ resolve(projectRoot, ".agents/hooks", safeName),
138
+ resolve(projectRoot, ".kimi/hooks", safeName),
139
+ resolve(getUserHome(), ".omk/agent/hooks", safeName),
140
+ ];
141
+ const found = await Promise.all(paths.map((p) => readFile(p, "utf-8").then(() => true).catch(() => false)));
142
+ if (!found.some(Boolean)) {
143
+ diagnostics.push({ level: "warning", code: "hook_reference_unknown", message: `hook not found in project or user hook paths: ${hook}` });
144
+ }
145
+ }
146
+ return diagnostics;
147
+ }
100
148
  function formatRequiredRuntimeMcpError(diagnostics, mcpConfigFile, servers) {
101
149
  const errors = diagnostics
102
150
  .filter((diagnostic) => diagnostic.level === "error")
@@ -138,7 +138,7 @@ function deriveWorkerCapabilities(node, toolPlane) {
138
138
  const role = node.role?.toLowerCase() ?? "";
139
139
  const gates = node.outputs?.map((output) => output.gate).filter(Boolean) ?? [];
140
140
  const assigned = new Set(routing?.assignedProviderCapabilities ?? []);
141
- const readOnly = routing?.readOnly === true;
141
+ const readOnly = routing?.readOnly === true || routing?.freedomd?.degradedMode === "read-only-local-review";
142
142
  const merge = !readOnly && (assigned.has("merge") || role === "merger" || role === "integrator" || role === "orchestrator");
143
143
  const write = !readOnly && (assigned.has("write") || merge || role === "coder" || role === "executor" || role === "refactorer");
144
144
  const shell = !readOnly && (assigned.has("shell") || routing?.requiresToolCalling === true || gates.includes("command-pass") || gates.includes("test-pass"));