open-multi-agent-kit 0.78.0 → 0.78.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.
- package/CHANGELOG.md +56 -15
- package/MATURITY.md +6 -2
- package/README.md +125 -26
- package/ROADMAP.md +36 -28
- package/dist/cli/register-basic-commands.js +3 -2
- package/dist/cli/register-mcp-dag-cron-screenshot-commands.js +2 -0
- package/dist/cli/register-spec-agent-goal-commands.js +45 -0
- package/dist/cli/register-tool-commands.js +11 -0
- package/dist/cli/register-workflow-commands.js +1 -0
- package/dist/cli/registry/tooling.js +3 -2
- package/dist/cli/release-promotion-gate.d.ts +14 -0
- package/dist/cli/release-promotion-gate.js +71 -0
- package/dist/cli/v2/release-commands.d.ts +29 -0
- package/dist/cli/v2/release-commands.js +95 -0
- package/dist/commands/chat/core.js +5 -0
- package/dist/commands/chat/native-root-loop.js +74 -1
- package/dist/commands/chat/slash/commands/session.js +19 -1
- package/dist/commands/dag-from-spec.d.ts +1 -0
- package/dist/commands/dag-from-spec.js +61 -1
- package/dist/commands/goal-interview.d.ts +18 -0
- package/dist/commands/goal-interview.js +396 -0
- package/dist/commands/graph.d.ts +62 -0
- package/dist/commands/graph.js +182 -0
- package/dist/commands/merge.d.ts +1 -0
- package/dist/commands/merge.js +88 -0
- package/dist/commands/parallel/core.js +3 -3
- package/dist/commands/provider.js +5 -3
- package/dist/commands/star.js +6 -1
- package/dist/commands/summary.d.ts +4 -1
- package/dist/commands/summary.js +103 -1
- package/dist/commands/team.d.ts +1 -0
- package/dist/commands/team.js +38 -0
- package/dist/contracts/interview.d.ts +106 -0
- package/dist/contracts/interview.js +9 -0
- package/dist/contracts/provider-health.d.ts +42 -0
- package/dist/contracts/provider-health.js +9 -0
- package/dist/evidence/index.d.ts +4 -0
- package/dist/evidence/index.js +2 -0
- package/dist/evidence/proof-trust-cli.d.ts +8 -0
- package/dist/evidence/proof-trust-cli.js +27 -0
- package/dist/evidence/proof-trust.d.ts +14 -0
- package/dist/evidence/proof-trust.js +381 -0
- package/dist/evidence/regression-proof-matrix.d.ts +42 -0
- package/dist/evidence/regression-proof-matrix.js +72 -0
- package/dist/goal/intent-frame.d.ts +30 -0
- package/dist/goal/intent-frame.js +39 -9
- package/dist/goal/interview-assimilation.d.ts +13 -0
- package/dist/goal/interview-assimilation.js +383 -0
- package/dist/goal/interview-question-bank.d.ts +11 -0
- package/dist/goal/interview-question-bank.js +225 -0
- package/dist/goal/interview-scoring.d.ts +31 -0
- package/dist/goal/interview-scoring.js +187 -0
- package/dist/goal/interview-session.d.ts +25 -0
- package/dist/goal/interview-session.js +116 -0
- package/dist/input/input-envelope.d.ts +22 -0
- package/dist/input/input-envelope.js +1 -0
- package/dist/memory/local-graph-memory-store.d.ts +15 -0
- package/dist/memory/local-graph-memory-store.js +176 -0
- package/dist/memory/memory-store.d.ts +18 -0
- package/dist/memory/memory-store.js +18 -0
- package/dist/orchestration/adaptorch-topology.d.ts +59 -0
- package/dist/orchestration/adaptorch-topology.js +194 -0
- package/dist/orchestration/capability-routing.d.ts +23 -0
- package/dist/orchestration/capability-routing.js +56 -0
- package/dist/orchestration/dag-compiler-types.d.ts +3 -0
- package/dist/orchestration/dag-compiler.js +14 -1
- package/dist/orchestration/parallel-orchestrator.d.ts +6 -0
- package/dist/orchestration/parallel-orchestrator.js +31 -0
- package/dist/providers/provider-health.d.ts +39 -0
- package/dist/providers/provider-health.js +161 -0
- package/dist/runtime/advanced-control-loop.d.ts +60 -0
- package/dist/runtime/advanced-control-loop.js +136 -0
- package/dist/runtime/agent-runtime.d.ts +10 -0
- package/dist/runtime/blast-radius.d.ts +10 -0
- package/dist/runtime/blast-radius.js +14 -0
- package/dist/runtime/context-broker.d.ts +13 -4
- package/dist/runtime/context-broker.js +14 -1
- package/dist/runtime/contracts/evidence.d.ts +87 -0
- package/dist/runtime/contracts/evidence.js +7 -0
- package/dist/runtime/contracts/router-v2.d.ts +44 -0
- package/dist/runtime/contracts/router-v2.js +4 -0
- package/dist/runtime/contracts/weakness-remediation.d.ts +67 -0
- package/dist/runtime/contracts/weakness-remediation.js +36 -0
- package/dist/runtime/headroom-policy.d.ts +37 -0
- package/dist/runtime/headroom-policy.js +122 -0
- package/dist/runtime/kimi-api-runtime.js +59 -1
- package/dist/runtime/ouroboros-policy.d.ts +57 -0
- package/dist/runtime/ouroboros-policy.js +134 -0
- package/dist/runtime/proof-bundle-trust.d.ts +74 -0
- package/dist/runtime/proof-bundle-trust.js +100 -0
- package/dist/runtime/provider-maturity-gate.d.ts +41 -0
- package/dist/runtime/provider-maturity-gate.js +101 -0
- package/dist/runtime/public-surface.d.ts +93 -0
- package/dist/runtime/public-surface.js +146 -0
- package/dist/runtime/router-v2-scoring.d.ts +11 -0
- package/dist/runtime/router-v2-scoring.js +151 -0
- package/dist/runtime/runtime-backed-task-runner.js +9 -1
- package/dist/runtime/tool-dispatch-contracts.d.ts +57 -1
- package/dist/runtime/tool-dispatch-contracts.js +79 -3
- package/dist/runtime/weakness-remediation-index.d.ts +27 -0
- package/dist/runtime/weakness-remediation-index.js +37 -0
- package/dist/safety/tool-authority-gate.d.ts +62 -0
- package/dist/safety/tool-authority-gate.js +108 -0
- package/dist/schema/proof-bundle.schema.d.ts +26 -26
- package/dist/schema/provider.schema.d.ts +4 -4
- package/dist/util/clipboard-image.d.ts +49 -0
- package/dist/util/clipboard-image.js +263 -0
- package/dist/util/first-run-star.d.ts +9 -0
- package/dist/util/first-run-star.js +42 -1
- package/dist/util/terminal-input.d.ts +20 -0
- package/dist/util/terminal-input.js +32 -0
- package/dist/util/update-check.d.ts +6 -1
- package/dist/util/update-check.js +35 -1
- package/docs/2026-06-08/critical-issues.md +20 -0
- package/docs/2026-06-08/improvements.md +14 -0
- package/docs/2026-06-08/init-checklist.md +25 -0
- package/docs/2026-06-08/plan.md +20 -0
- package/docs/2026-06-09/critical-issues.md +20 -0
- package/docs/2026-06-09/improvements.md +14 -0
- package/docs/2026-06-09/init-checklist.md +25 -0
- package/docs/2026-06-09/plan.md +20 -0
- package/docs/getting-started.md +31 -3
- package/docs/github-organic-promotion.md +127 -0
- package/docs/integrations/ouroboros.md +96 -0
- package/docs/native-root-runtime-algorithms.md +301 -0
- package/docs/provider-maturity.md +1 -1
- package/docs/versioning.md +3 -3
- package/package.json +4 -3
- package/readmeasset/ASSET_INDEX.md +1 -0
- package/templates/skills/agents/omk-agent-reach-websearch/SKILL.md +55 -0
- package/templates/skills/kimi/omk-agent-reach-websearch/SKILL.md +55 -0
- package/dist/native/linux-x64/omk-safety +0 -0
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HeadroomPolicy — proactive context compaction trigger.
|
|
3
|
+
*
|
|
4
|
+
* Industrial control-loop policy: given current token usage and model
|
|
5
|
+
* context window, decide whether to compact BEFORE utilization reaches
|
|
6
|
+
* a configurable threshold (default 90%).
|
|
7
|
+
*
|
|
8
|
+
* Prefers the external `headroom` CLI compressor when available;
|
|
9
|
+
* falls back to the built-in `optimizeContextBudget` when not.
|
|
10
|
+
*
|
|
11
|
+
* Deterministic except for the injectable `runHeadroom` runner.
|
|
12
|
+
*/
|
|
13
|
+
// ─── Defaults ────────────────────────────────────────────────────────────────
|
|
14
|
+
const DEFAULT_THRESHOLD = 0.90;
|
|
15
|
+
const MIN_THRESHOLD = 0.50;
|
|
16
|
+
const MAX_THRESHOLD = 0.99;
|
|
17
|
+
// ─── Threshold resolver ──────────────────────────────────────────────────────
|
|
18
|
+
export function resolveHeadroomThreshold(env = process.env) {
|
|
19
|
+
const raw = env.OMK_HEADROOM_THRESHOLD;
|
|
20
|
+
if (raw === undefined || raw === "")
|
|
21
|
+
return DEFAULT_THRESHOLD;
|
|
22
|
+
const parsed = Number(raw);
|
|
23
|
+
if (!Number.isFinite(parsed))
|
|
24
|
+
return DEFAULT_THRESHOLD;
|
|
25
|
+
return Math.min(MAX_THRESHOLD, Math.max(MIN_THRESHOLD, parsed));
|
|
26
|
+
}
|
|
27
|
+
// ─── Enabled check ───────────────────────────────────────────────────────────
|
|
28
|
+
export function isHeadroomEnabled(env = process.env) {
|
|
29
|
+
const raw = env.OMK_HEADROOM;
|
|
30
|
+
if (raw === undefined || raw === "")
|
|
31
|
+
return true;
|
|
32
|
+
const normalized = raw.trim().toLowerCase();
|
|
33
|
+
if (normalized === "off" || normalized === "0" || normalized === "false")
|
|
34
|
+
return false;
|
|
35
|
+
return true;
|
|
36
|
+
}
|
|
37
|
+
// ─── Evaluate ────────────────────────────────────────────────────────────────
|
|
38
|
+
export function evaluateHeadroom(input) {
|
|
39
|
+
const env = input.env ?? process.env;
|
|
40
|
+
const enabled = isHeadroomEnabled(env);
|
|
41
|
+
const threshold = resolveHeadroomThreshold(env);
|
|
42
|
+
const { usedTokens, contextWindow } = input;
|
|
43
|
+
if (!enabled) {
|
|
44
|
+
return {
|
|
45
|
+
shouldCompact: false,
|
|
46
|
+
utilization: 0,
|
|
47
|
+
threshold,
|
|
48
|
+
usedTokens,
|
|
49
|
+
contextWindow,
|
|
50
|
+
reason: "headroom disabled via OMK_HEADROOM",
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
if (contextWindow <= 0) {
|
|
54
|
+
return {
|
|
55
|
+
shouldCompact: false,
|
|
56
|
+
utilization: 0,
|
|
57
|
+
threshold,
|
|
58
|
+
usedTokens,
|
|
59
|
+
contextWindow,
|
|
60
|
+
reason: "context window size unknown (0)",
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
const utilization = usedTokens / contextWindow;
|
|
64
|
+
const shouldCompact = utilization >= threshold;
|
|
65
|
+
return {
|
|
66
|
+
shouldCompact,
|
|
67
|
+
utilization,
|
|
68
|
+
threshold,
|
|
69
|
+
usedTokens,
|
|
70
|
+
contextWindow,
|
|
71
|
+
reason: shouldCompact
|
|
72
|
+
? `utilization ${(utilization * 100).toFixed(1)}% >= threshold ${(threshold * 100).toFixed(1)}%`
|
|
73
|
+
: `utilization ${(utilization * 100).toFixed(1)}% below threshold ${(threshold * 100).toFixed(1)}%`,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
// ─── Compaction runner ───────────────────────────────────────────────────────
|
|
77
|
+
const HEADROOM_CLI_TIMEOUT_MS = 15_000;
|
|
78
|
+
async function defaultRunHeadroom(text) {
|
|
79
|
+
try {
|
|
80
|
+
const { runShell } = await import("../util/shell.js");
|
|
81
|
+
const result = await runShell("headroom", ["compact", "--stdin"], {
|
|
82
|
+
timeout: HEADROOM_CLI_TIMEOUT_MS,
|
|
83
|
+
input: text,
|
|
84
|
+
});
|
|
85
|
+
if (result.failed || result.exitCode !== 0)
|
|
86
|
+
return null;
|
|
87
|
+
const output = result.stdout.trim();
|
|
88
|
+
return output.length > 0 ? output : null;
|
|
89
|
+
}
|
|
90
|
+
catch {
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
export async function maybeCompactWithHeadroom(args) {
|
|
95
|
+
if (!args.decision.shouldCompact) {
|
|
96
|
+
return { compacted: false, via: "none" };
|
|
97
|
+
}
|
|
98
|
+
// Try headroom first
|
|
99
|
+
if (args.text) {
|
|
100
|
+
try {
|
|
101
|
+
const runner = args.runHeadroom ?? defaultRunHeadroom;
|
|
102
|
+
const result = await runner(args.text);
|
|
103
|
+
if (result !== null) {
|
|
104
|
+
return { compacted: true, via: "headroom" };
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
catch {
|
|
108
|
+
// Headroom threw — fall through to fallback
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
// Fall back to built-in optimizer
|
|
112
|
+
if (args.fallback) {
|
|
113
|
+
try {
|
|
114
|
+
await args.fallback();
|
|
115
|
+
return { compacted: true, via: "fallback" };
|
|
116
|
+
}
|
|
117
|
+
catch {
|
|
118
|
+
// Fallback also failed — graceful degradation
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return { compacted: false, via: "none" };
|
|
122
|
+
}
|
|
@@ -4,9 +4,47 @@
|
|
|
4
4
|
*
|
|
5
5
|
* Calls https://api.moonshot.cn/v1/chat/completions directly.
|
|
6
6
|
*/
|
|
7
|
+
import { readFileSync, existsSync } from "node:fs";
|
|
8
|
+
import { resolve } from "node:path";
|
|
7
9
|
import { capsuleToTask } from "./context-broker-converter.js";
|
|
8
10
|
import { buildProviderToolPayload } from "./provider-tool-contracts.js";
|
|
9
11
|
import { repairToolCalls } from "./tool-call-repair.js";
|
|
12
|
+
/**
|
|
13
|
+
* Detect "Image file: <path>" patterns in the prompt text (inserted by /paste
|
|
14
|
+
* or Ctrl+V clipboard image) and load the referenced images as base64 data URIs
|
|
15
|
+
* for multimodal API calls.
|
|
16
|
+
*/
|
|
17
|
+
function extractInlineImageParts(prompt) {
|
|
18
|
+
const results = [];
|
|
19
|
+
// Match "Image file: .omk/screenshots/.../screenshot-xxx.png" lines
|
|
20
|
+
const pattern = /^Image file:\s+(.+\.(?:png|jpg|jpeg|webp|gif))\s*$/gim;
|
|
21
|
+
let match;
|
|
22
|
+
while ((match = pattern.exec(prompt)) !== null) {
|
|
23
|
+
const filePath = match[1].trim();
|
|
24
|
+
const absPath = resolve(filePath);
|
|
25
|
+
if (!existsSync(absPath))
|
|
26
|
+
continue;
|
|
27
|
+
try {
|
|
28
|
+
const buf = readFileSync(absPath);
|
|
29
|
+
if (buf.length === 0 || buf.length > 20 * 1024 * 1024)
|
|
30
|
+
continue;
|
|
31
|
+
// Detect mime type from magic bytes
|
|
32
|
+
let mimeType = "image/png";
|
|
33
|
+
if (buf[0] === 0xff && buf[1] === 0xd8)
|
|
34
|
+
mimeType = "image/jpeg";
|
|
35
|
+
else if (buf[0] === 0x52 && buf[1] === 0x49)
|
|
36
|
+
mimeType = "image/webp";
|
|
37
|
+
else if (buf[0] === 0x47 && buf[1] === 0x49)
|
|
38
|
+
mimeType = "image/gif";
|
|
39
|
+
const base64 = buf.toString("base64");
|
|
40
|
+
results.push({ dataUri: `data:${mimeType};base64,${base64}` });
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
// Skip unreadable files
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return results;
|
|
47
|
+
}
|
|
10
48
|
function mapToolCalls(apiToolCalls, context) {
|
|
11
49
|
const repaired = repairToolCalls({
|
|
12
50
|
declaredCalls: (apiToolCalls ?? []).map((tc) => ({
|
|
@@ -155,7 +193,27 @@ export class KimiApiRuntime {
|
|
|
155
193
|
if (task.context.system) {
|
|
156
194
|
messages.push({ role: "system", content: task.context.system });
|
|
157
195
|
}
|
|
158
|
-
|
|
196
|
+
// Build multimodal content when attachments are present or when
|
|
197
|
+
// the prompt contains "Image file: <path>" references (from /paste or
|
|
198
|
+
// Ctrl+V clipboard image). This makes clipboard-pasted images send as
|
|
199
|
+
// image_url content parts to OpenAI-compatible multimodal endpoints.
|
|
200
|
+
const attachments = task.attachments ?? [];
|
|
201
|
+
const inlineImages = extractInlineImageParts(task.prompt);
|
|
202
|
+
if (attachments.length > 0 || inlineImages.length > 0) {
|
|
203
|
+
const parts = [{ type: "text", text: task.prompt }];
|
|
204
|
+
for (const attachment of attachments) {
|
|
205
|
+
if (attachment.dataUri) {
|
|
206
|
+
parts.push({ type: "image_url", image_url: { url: attachment.dataUri } });
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
for (const image of inlineImages) {
|
|
210
|
+
parts.push({ type: "image_url", image_url: { url: image.dataUri } });
|
|
211
|
+
}
|
|
212
|
+
messages.push({ role: "user", content: parts });
|
|
213
|
+
}
|
|
214
|
+
else {
|
|
215
|
+
messages.push({ role: "user", content: task.prompt });
|
|
216
|
+
}
|
|
159
217
|
const providerTools = task.capabilities.toolCalling
|
|
160
218
|
? buildProviderToolPayload(task.tools.available)
|
|
161
219
|
: buildProviderToolPayload([]);
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ouroboros routing policy for OMK.
|
|
3
|
+
*
|
|
4
|
+
* Resolves whether to prefer the embedded Ouroboros spec-first flow
|
|
5
|
+
* for goal/spec/orchestration intents. Detection is non-fatal and
|
|
6
|
+
* never triggers network access or implicit installs.
|
|
7
|
+
*/
|
|
8
|
+
export type OuroborosMode = "always" | "auto" | "off";
|
|
9
|
+
/**
|
|
10
|
+
* Read the OMK_OUROBOROS env var and normalise it into a mode.
|
|
11
|
+
*
|
|
12
|
+
* - unset / anything other than the known tokens → "always" (default)
|
|
13
|
+
* - "auto" → "auto"
|
|
14
|
+
* - "off" / "0" / "false" (case-insensitive) → "off"
|
|
15
|
+
*/
|
|
16
|
+
export declare function resolveOuroborosMode(env?: NodeJS.ProcessEnv): OuroborosMode;
|
|
17
|
+
export interface OuroborosAvailability {
|
|
18
|
+
available: boolean;
|
|
19
|
+
via: "mcp" | "binary" | "none";
|
|
20
|
+
detail: string;
|
|
21
|
+
}
|
|
22
|
+
interface DetectOpts {
|
|
23
|
+
env?: NodeJS.ProcessEnv;
|
|
24
|
+
mcpConfigPath?: string;
|
|
25
|
+
which?: (cmd: string) => Promise<string | null>;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Check whether Ouroboros is reachable without network or installs.
|
|
29
|
+
*
|
|
30
|
+
* Strategy (non-fatal):
|
|
31
|
+
* 1. Look for an `ouroboros` key in ~/.omk/agent/mcp.json (global)
|
|
32
|
+
* and/or .omk/mcp.json (project-scoped).
|
|
33
|
+
* 2. Optionally check for an `ouroboros` binary via an injectable which().
|
|
34
|
+
*
|
|
35
|
+
* Any I/O error silently yields `{ available: false, via: "none" }`.
|
|
36
|
+
*/
|
|
37
|
+
export declare function detectOuroborosAvailable(opts?: DetectOpts): Promise<OuroborosAvailability>;
|
|
38
|
+
export interface OuroborosDecision {
|
|
39
|
+
use: boolean;
|
|
40
|
+
mode: OuroborosMode;
|
|
41
|
+
availability: OuroborosAvailability;
|
|
42
|
+
reason: string;
|
|
43
|
+
}
|
|
44
|
+
interface DecisionInput {
|
|
45
|
+
intent: string;
|
|
46
|
+
env?: NodeJS.ProcessEnv;
|
|
47
|
+
detect?: () => Promise<OuroborosAvailability>;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Decide whether the current run should route through Ouroboros.
|
|
51
|
+
*
|
|
52
|
+
* - use=true only when mode≠off AND available AND intent matches.
|
|
53
|
+
* - When mode=always but unavailable → use=false with fallback reason.
|
|
54
|
+
* - Never throws.
|
|
55
|
+
*/
|
|
56
|
+
export declare function resolveOuroborosDecision(input: DecisionInput): Promise<OuroborosDecision>;
|
|
57
|
+
export {};
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ouroboros routing policy for OMK.
|
|
3
|
+
*
|
|
4
|
+
* Resolves whether to prefer the embedded Ouroboros spec-first flow
|
|
5
|
+
* for goal/spec/orchestration intents. Detection is non-fatal and
|
|
6
|
+
* never triggers network access or implicit installs.
|
|
7
|
+
*/
|
|
8
|
+
import { readFile } from "node:fs/promises";
|
|
9
|
+
import { join } from "node:path";
|
|
10
|
+
import { homedir } from "node:os";
|
|
11
|
+
/**
|
|
12
|
+
* Read the OMK_OUROBOROS env var and normalise it into a mode.
|
|
13
|
+
*
|
|
14
|
+
* - unset / anything other than the known tokens → "always" (default)
|
|
15
|
+
* - "auto" → "auto"
|
|
16
|
+
* - "off" / "0" / "false" (case-insensitive) → "off"
|
|
17
|
+
*/
|
|
18
|
+
export function resolveOuroborosMode(env = process.env) {
|
|
19
|
+
const raw = (env.OMK_OUROBOROS ?? "").trim().toLowerCase();
|
|
20
|
+
if (raw === "off" || raw === "0" || raw === "false")
|
|
21
|
+
return "off";
|
|
22
|
+
if (raw === "auto")
|
|
23
|
+
return "auto";
|
|
24
|
+
return "always";
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Check whether Ouroboros is reachable without network or installs.
|
|
28
|
+
*
|
|
29
|
+
* Strategy (non-fatal):
|
|
30
|
+
* 1. Look for an `ouroboros` key in ~/.omk/agent/mcp.json (global)
|
|
31
|
+
* and/or .omk/mcp.json (project-scoped).
|
|
32
|
+
* 2. Optionally check for an `ouroboros` binary via an injectable which().
|
|
33
|
+
*
|
|
34
|
+
* Any I/O error silently yields `{ available: false, via: "none" }`.
|
|
35
|
+
*/
|
|
36
|
+
export async function detectOuroborosAvailable(opts) {
|
|
37
|
+
// --- binary check (fast, optional) ---
|
|
38
|
+
if (opts?.which) {
|
|
39
|
+
try {
|
|
40
|
+
const bin = await opts.which("ouroboros");
|
|
41
|
+
if (bin) {
|
|
42
|
+
return { available: true, via: "binary", detail: `found binary: ${bin}` };
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
// swallow
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
// --- MCP config check (non-fatal) ---
|
|
50
|
+
const home = homedir();
|
|
51
|
+
const candidates = [
|
|
52
|
+
opts?.mcpConfigPath ?? join(home, ".omk", "agent", "mcp.json"),
|
|
53
|
+
join(process.cwd(), ".omk", "mcp.json"),
|
|
54
|
+
];
|
|
55
|
+
for (const configPath of candidates) {
|
|
56
|
+
try {
|
|
57
|
+
const raw = await readFile(configPath, "utf-8");
|
|
58
|
+
const parsed = JSON.parse(raw);
|
|
59
|
+
if (isRecord(parsed) && isRecord(parsed.mcpServers)) {
|
|
60
|
+
if ("ouroboros" in parsed.mcpServers) {
|
|
61
|
+
return {
|
|
62
|
+
available: true,
|
|
63
|
+
via: "mcp",
|
|
64
|
+
detail: `ouroboros server found in ${configPath}`,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
// file missing or malformed → continue
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return { available: false, via: "none", detail: "ouroboros not detected" };
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Intent tokens (lowercased) that qualify for Ouroboros routing.
|
|
77
|
+
*/
|
|
78
|
+
const OUROBOROS_INTENT_KEYWORDS = [
|
|
79
|
+
"goal",
|
|
80
|
+
"plan",
|
|
81
|
+
"spec",
|
|
82
|
+
"seed",
|
|
83
|
+
"interview",
|
|
84
|
+
"orchestrate",
|
|
85
|
+
"feature",
|
|
86
|
+
"build",
|
|
87
|
+
"implement",
|
|
88
|
+
// Korean equivalents
|
|
89
|
+
"계획",
|
|
90
|
+
"스펙",
|
|
91
|
+
"구현",
|
|
92
|
+
"기획",
|
|
93
|
+
];
|
|
94
|
+
/**
|
|
95
|
+
* Decide whether the current run should route through Ouroboros.
|
|
96
|
+
*
|
|
97
|
+
* - use=true only when mode≠off AND available AND intent matches.
|
|
98
|
+
* - When mode=always but unavailable → use=false with fallback reason.
|
|
99
|
+
* - Never throws.
|
|
100
|
+
*/
|
|
101
|
+
export async function resolveOuroborosDecision(input) {
|
|
102
|
+
const mode = resolveOuroborosMode(input.env);
|
|
103
|
+
const detect = input.detect ?? (() => detectOuroborosAvailable({ env: input.env }));
|
|
104
|
+
let availability;
|
|
105
|
+
try {
|
|
106
|
+
availability = await detect();
|
|
107
|
+
}
|
|
108
|
+
catch {
|
|
109
|
+
availability = { available: false, via: "none", detail: "detection threw" };
|
|
110
|
+
}
|
|
111
|
+
if (mode === "off") {
|
|
112
|
+
return { use: false, mode, availability, reason: "ouroboros-mode-off" };
|
|
113
|
+
}
|
|
114
|
+
if (!availability.available) {
|
|
115
|
+
return {
|
|
116
|
+
use: false,
|
|
117
|
+
mode,
|
|
118
|
+
availability,
|
|
119
|
+
reason: "ouroboros-unavailable-fallback-native",
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
if (!isGoalLikeIntent(input.intent)) {
|
|
123
|
+
return { use: false, mode, availability, reason: "intent-not-goal-like" };
|
|
124
|
+
}
|
|
125
|
+
return { use: true, mode, availability, reason: "ouroboros-routing-active" };
|
|
126
|
+
}
|
|
127
|
+
// ── Helpers ─────────────────────────────────────────────────────────
|
|
128
|
+
function isGoalLikeIntent(intent) {
|
|
129
|
+
const lowered = intent.toLocaleLowerCase();
|
|
130
|
+
return OUROBOROS_INTENT_KEYWORDS.some((kw) => lowered.includes(kw));
|
|
131
|
+
}
|
|
132
|
+
function isRecord(v) {
|
|
133
|
+
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
134
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Proof Bundle Trust Score — Phase 2 of OMK Weakness Remediation.
|
|
3
|
+
*
|
|
4
|
+
* Evaluates a curated proof bundle across 8 dimensions and produces
|
|
5
|
+
* a trust score T_b, a permission level, and a pass/fail verdict
|
|
6
|
+
* against τ_proof.
|
|
7
|
+
*/
|
|
8
|
+
import type { ClaimPermissionLevel, EvidenceVerdict } from "./contracts/evidence.js";
|
|
9
|
+
/** The 8 scored dimensions of a proof bundle. */
|
|
10
|
+
export interface ProofBundleScores {
|
|
11
|
+
/** Schema conformance of evidence items [0, 1]. */
|
|
12
|
+
readonly schema: number;
|
|
13
|
+
/** Hash integrity / tamper evidence [0, 1]. */
|
|
14
|
+
readonly hashes: number;
|
|
15
|
+
/** Command trace coverage and correctness [0, 1]. */
|
|
16
|
+
readonly commands: number;
|
|
17
|
+
/** Stdout / stderr capture completeness [0, 1]. */
|
|
18
|
+
readonly stdout: number;
|
|
19
|
+
/** Decision record quality and count [0, 1]. */
|
|
20
|
+
readonly decisions: number;
|
|
21
|
+
/** Evidence item confidence and verdict strength [0, 1]. */
|
|
22
|
+
readonly evidence: number;
|
|
23
|
+
/** Acknowledged limitations documented [0, 1]. */
|
|
24
|
+
readonly limitations: number;
|
|
25
|
+
/** Replay reproducibility score [0, 1]. */
|
|
26
|
+
readonly replay: number;
|
|
27
|
+
}
|
|
28
|
+
/** Result of evaluating a proof bundle. */
|
|
29
|
+
export interface TrustScoreResult {
|
|
30
|
+
/** Computed trust score T_b ∈ [0, 1]. */
|
|
31
|
+
readonly score: number;
|
|
32
|
+
/** Permission level derived from score thresholds. */
|
|
33
|
+
readonly permissionLevel: ClaimPermissionLevel;
|
|
34
|
+
/** Whether score meets τ_proof. */
|
|
35
|
+
readonly passed: boolean;
|
|
36
|
+
/** Individual dimension contributions. */
|
|
37
|
+
readonly breakdown: ProofBundleScores;
|
|
38
|
+
}
|
|
39
|
+
/** Engine that evaluates proof bundle trust. */
|
|
40
|
+
export interface ProofBundleTrustEngine {
|
|
41
|
+
/**
|
|
42
|
+
* Evaluate a proof bundle from its 8 dimension scores.
|
|
43
|
+
*
|
|
44
|
+
* Formula:
|
|
45
|
+
* T_b = 0.15·schema + 0.15·hashes + 0.15·commands + 0.10·stdout
|
|
46
|
+
* + 0.15·decisions + 0.15·evidence + 0.05·limitations + 0.10·replay
|
|
47
|
+
*/
|
|
48
|
+
evaluate(scores: ProofBundleScores): TrustScoreResult;
|
|
49
|
+
/** Derive dimension scores from a raw evidence verdict and coverage. */
|
|
50
|
+
deriveScores(verdict: EvidenceVerdict, coveragePercent: number, options?: DeriveScoresOptions): ProofBundleScores;
|
|
51
|
+
}
|
|
52
|
+
/** Options for automatic score derivation. */
|
|
53
|
+
export interface DeriveScoresOptions {
|
|
54
|
+
/** Override schema conformance (default inferred from verdict). */
|
|
55
|
+
readonly schema?: number;
|
|
56
|
+
/** Override hash integrity (default 1.0). */
|
|
57
|
+
readonly hashes?: number;
|
|
58
|
+
/** Override command trace score (default inferred from coverage). */
|
|
59
|
+
readonly commands?: number;
|
|
60
|
+
/** Override stdout completeness (default inferred from coverage). */
|
|
61
|
+
readonly stdout?: number;
|
|
62
|
+
/** Override decision record score (default inferred from verdict). */
|
|
63
|
+
readonly decisions?: number;
|
|
64
|
+
/** Override evidence strength (default inferred from verdict). */
|
|
65
|
+
readonly evidence?: number;
|
|
66
|
+
/** Override limitations documentation (default 0.5). */
|
|
67
|
+
readonly limitations?: number;
|
|
68
|
+
/** Override replay score (default inferred from verdict). */
|
|
69
|
+
readonly replay?: number;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Create a ProofBundleTrustEngine with default weights and thresholds.
|
|
73
|
+
*/
|
|
74
|
+
export declare function createProofBundleTrustEngine(): ProofBundleTrustEngine;
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Proof Bundle Trust Score — Phase 2 of OMK Weakness Remediation.
|
|
3
|
+
*
|
|
4
|
+
* Evaluates a curated proof bundle across 8 dimensions and produces
|
|
5
|
+
* a trust score T_b, a permission level, and a pass/fail verdict
|
|
6
|
+
* against τ_proof.
|
|
7
|
+
*/
|
|
8
|
+
import { TAU_PROOF } from "./contracts/weakness-remediation.js";
|
|
9
|
+
// ── Constants ───────────────────────────────────────────────────
|
|
10
|
+
const WEIGHT_SCHEMA = 0.15;
|
|
11
|
+
const WEIGHT_HASHES = 0.15;
|
|
12
|
+
const WEIGHT_COMMANDS = 0.15;
|
|
13
|
+
const WEIGHT_STDOUT = 0.10;
|
|
14
|
+
const WEIGHT_DECISIONS = 0.15;
|
|
15
|
+
const WEIGHT_EVIDENCE = 0.15;
|
|
16
|
+
const WEIGHT_LIMITATIONS = 0.05;
|
|
17
|
+
const WEIGHT_REPLAY = 0.10;
|
|
18
|
+
const STRONG_PUBLIC_THRESHOLD = 0.90;
|
|
19
|
+
const QUALIFIED_PUBLIC_THRESHOLD = 0.75;
|
|
20
|
+
const INTERNAL_CLAIM_THRESHOLD = 0.60;
|
|
21
|
+
// ── Helpers ─────────────────────────────────────────────────────
|
|
22
|
+
function clamp01(n) {
|
|
23
|
+
return Math.max(0, Math.min(1, n));
|
|
24
|
+
}
|
|
25
|
+
function permissionLevelFromScore(score) {
|
|
26
|
+
if (score >= STRONG_PUBLIC_THRESHOLD) {
|
|
27
|
+
return "strong-public-claim";
|
|
28
|
+
}
|
|
29
|
+
if (score >= QUALIFIED_PUBLIC_THRESHOLD) {
|
|
30
|
+
return "qualified-public-claim";
|
|
31
|
+
}
|
|
32
|
+
if (score >= INTERNAL_CLAIM_THRESHOLD) {
|
|
33
|
+
return "internal-claim-only";
|
|
34
|
+
}
|
|
35
|
+
return "no-claim";
|
|
36
|
+
}
|
|
37
|
+
function verdictToBaseScore(verdict) {
|
|
38
|
+
switch (verdict) {
|
|
39
|
+
case "pass":
|
|
40
|
+
return 1.0;
|
|
41
|
+
case "partial":
|
|
42
|
+
return 0.65;
|
|
43
|
+
case "pending":
|
|
44
|
+
return 0.35;
|
|
45
|
+
case "fail":
|
|
46
|
+
return 0.0;
|
|
47
|
+
default:
|
|
48
|
+
return 0.0;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
// ── Engine Factory ──────────────────────────────────────────────
|
|
52
|
+
/**
|
|
53
|
+
* Create a ProofBundleTrustEngine with default weights and thresholds.
|
|
54
|
+
*/
|
|
55
|
+
export function createProofBundleTrustEngine() {
|
|
56
|
+
return {
|
|
57
|
+
evaluate(scores) {
|
|
58
|
+
const clamped = {
|
|
59
|
+
schema: clamp01(scores.schema),
|
|
60
|
+
hashes: clamp01(scores.hashes),
|
|
61
|
+
commands: clamp01(scores.commands),
|
|
62
|
+
stdout: clamp01(scores.stdout),
|
|
63
|
+
decisions: clamp01(scores.decisions),
|
|
64
|
+
evidence: clamp01(scores.evidence),
|
|
65
|
+
limitations: clamp01(scores.limitations),
|
|
66
|
+
replay: clamp01(scores.replay),
|
|
67
|
+
};
|
|
68
|
+
const score = WEIGHT_SCHEMA * clamped.schema +
|
|
69
|
+
WEIGHT_HASHES * clamped.hashes +
|
|
70
|
+
WEIGHT_COMMANDS * clamped.commands +
|
|
71
|
+
WEIGHT_STDOUT * clamped.stdout +
|
|
72
|
+
WEIGHT_DECISIONS * clamped.decisions +
|
|
73
|
+
WEIGHT_EVIDENCE * clamped.evidence +
|
|
74
|
+
WEIGHT_LIMITATIONS * clamped.limitations +
|
|
75
|
+
WEIGHT_REPLAY * clamped.replay;
|
|
76
|
+
const finalScore = clamp01(score);
|
|
77
|
+
const permissionLevel = permissionLevelFromScore(finalScore);
|
|
78
|
+
return Object.freeze({
|
|
79
|
+
score: finalScore,
|
|
80
|
+
permissionLevel,
|
|
81
|
+
passed: finalScore >= TAU_PROOF,
|
|
82
|
+
breakdown: clamped,
|
|
83
|
+
});
|
|
84
|
+
},
|
|
85
|
+
deriveScores(verdict, coveragePercent, options = {}) {
|
|
86
|
+
const base = verdictToBaseScore(verdict);
|
|
87
|
+
const cov = clamp01(coveragePercent / 100);
|
|
88
|
+
return Object.freeze({
|
|
89
|
+
schema: clamp01(options.schema ?? base),
|
|
90
|
+
hashes: clamp01(options.hashes ?? 1.0),
|
|
91
|
+
commands: clamp01(options.commands ?? cov),
|
|
92
|
+
stdout: clamp01(options.stdout ?? cov),
|
|
93
|
+
decisions: clamp01(options.decisions ?? base),
|
|
94
|
+
evidence: clamp01(options.evidence ?? base),
|
|
95
|
+
limitations: clamp01(options.limitations ?? 0.5),
|
|
96
|
+
replay: clamp01(options.replay ?? base),
|
|
97
|
+
});
|
|
98
|
+
},
|
|
99
|
+
};
|
|
100
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Provider Maturity Gate — Phase 3 of OMK Weakness Remediation.
|
|
3
|
+
*
|
|
4
|
+
* Evaluates a provider/runtime across 8 adapter test dimensions and
|
|
5
|
+
* produces a maturity score M_p, an authority class, and a pass/fail
|
|
6
|
+
* verdict.
|
|
7
|
+
*/
|
|
8
|
+
import type { AdapterTestKind, AdapterTestResult, ProviderAuthorityClass } from "./contracts/evidence.js";
|
|
9
|
+
/** Maturity evaluation result for a single provider. */
|
|
10
|
+
export interface MaturityResult {
|
|
11
|
+
/** Computed maturity score M_p ∈ [0, 1]. */
|
|
12
|
+
readonly score: number;
|
|
13
|
+
/** Authority class derived from score and sub-score constraints. */
|
|
14
|
+
readonly authorityClass: ProviderAuthorityClass;
|
|
15
|
+
/** Whether the provider meets minimum viability. */
|
|
16
|
+
readonly passed: boolean;
|
|
17
|
+
/** Sub-scores keyed by adapter test kind. */
|
|
18
|
+
readonly subScores: Readonly<Record<AdapterTestKind, number>>;
|
|
19
|
+
}
|
|
20
|
+
/** Engine that evaluates provider maturity. */
|
|
21
|
+
export interface ProviderMaturityGate {
|
|
22
|
+
/**
|
|
23
|
+
* Evaluate provider maturity from adapter test results.
|
|
24
|
+
*
|
|
25
|
+
* Formula:
|
|
26
|
+
* M_p = 0.10·s_auth + 0.10·s_read + 0.15·s_write + 0.10·s_shell
|
|
27
|
+
* + 0.15·s_mcp + 0.15·s_merge + 0.15·s_evidence + 0.10·s_fallback
|
|
28
|
+
*/
|
|
29
|
+
evaluate(results: readonly AdapterTestResult[]): MaturityResult;
|
|
30
|
+
/** Look up a single sub-score by test kind (defaults to 0). */
|
|
31
|
+
getSubScore(results: readonly AdapterTestResult[], kind: AdapterTestKind): number;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Create a ProviderMaturityGate with default weights and thresholds.
|
|
35
|
+
*/
|
|
36
|
+
export interface ProviderMaturityTable {
|
|
37
|
+
lookup(providerId: string): MaturityResult | undefined;
|
|
38
|
+
register(providerId: string, result: MaturityResult): void;
|
|
39
|
+
}
|
|
40
|
+
export declare function createProviderMaturityTable(): ProviderMaturityTable;
|
|
41
|
+
export declare function createProviderMaturityGate(): ProviderMaturityGate;
|