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,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Phase 5 — Release Promotion Gate (Algorithm 8)
|
|
3
|
+
*
|
|
4
|
+
* Computes a release viability score R_v from ten normalized input
|
|
5
|
+
* dimensions and derives a verdict: block, pre-release, or stable.
|
|
6
|
+
*/
|
|
7
|
+
import { type ReleasePromotionInputs, type ReleasePromotionResult } from "../runtime/contracts/weakness-remediation.js";
|
|
8
|
+
/** Gate engine contract. */
|
|
9
|
+
export interface ReleasePromotionGate {
|
|
10
|
+
/** Evaluate inputs and return scored result with verdict. */
|
|
11
|
+
evaluate(inputs: ReleasePromotionInputs): ReleasePromotionResult;
|
|
12
|
+
}
|
|
13
|
+
/** Factory that creates the default release promotion gate. */
|
|
14
|
+
export declare function createReleasePromotionGate(): ReleasePromotionGate;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Phase 5 — Release Promotion Gate (Algorithm 8)
|
|
3
|
+
*
|
|
4
|
+
* Computes a release viability score R_v from ten normalized input
|
|
5
|
+
* dimensions and derives a verdict: block, pre-release, or stable.
|
|
6
|
+
*/
|
|
7
|
+
import { RELEASE_GATE_WEIGHTS, } from "../runtime/contracts/weakness-remediation.js";
|
|
8
|
+
/** Factory that creates the default release promotion gate. */
|
|
9
|
+
export function createReleasePromotionGate() {
|
|
10
|
+
return {
|
|
11
|
+
evaluate(inputs) {
|
|
12
|
+
const w = RELEASE_GATE_WEIGHTS;
|
|
13
|
+
const demoRun = inputs.demoRun ?? false;
|
|
14
|
+
const maturity = inputs.maturity ?? inputs.providerMinimum ?? 0;
|
|
15
|
+
const rawScore = w.ci * inputs.ci +
|
|
16
|
+
w.build * (inputs.build ?? 0) +
|
|
17
|
+
w.types * (inputs.types ?? 0) +
|
|
18
|
+
w.tests * (inputs.tests ?? 0) +
|
|
19
|
+
w.install * inputs.freshInstallSmoke +
|
|
20
|
+
w.demo * (demoRun ? 1 : 0) +
|
|
21
|
+
w.proof * inputs.proofMedian +
|
|
22
|
+
w.maturity * maturity +
|
|
23
|
+
w.docs * inputs.docs -
|
|
24
|
+
w.regression * inputs.regressionSeverity;
|
|
25
|
+
const score = clamp01(rawScore);
|
|
26
|
+
const reasons = [];
|
|
27
|
+
const blocked = inputs.ci === 0 || inputs.freshInstallSmoke === 0 || !demoRun;
|
|
28
|
+
if (blocked) {
|
|
29
|
+
if (inputs.ci === 0) {
|
|
30
|
+
reasons.push("CI score is 0 (blocking)");
|
|
31
|
+
}
|
|
32
|
+
if (inputs.freshInstallSmoke === 0) {
|
|
33
|
+
reasons.push("Fresh install smoke is 0 (blocking)");
|
|
34
|
+
}
|
|
35
|
+
if (!demoRun) {
|
|
36
|
+
reasons.push("Minimal verified demo run failed or missing (blocking)");
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
let verdict;
|
|
40
|
+
if (blocked) {
|
|
41
|
+
verdict = "block";
|
|
42
|
+
}
|
|
43
|
+
else if (score >= 0.90 && inputs.proofMedian >= 0.85 && maturity >= 0.80) {
|
|
44
|
+
verdict = "stable";
|
|
45
|
+
reasons.push(`Score ${formatScore(score)} meets stable threshold (≥0.90) with proof≥0.85 and maturity≥0.80`);
|
|
46
|
+
}
|
|
47
|
+
else if (score >= 0.75 && inputs.proofMedian >= 0.75) {
|
|
48
|
+
verdict = "pre-release";
|
|
49
|
+
reasons.push(`Score ${formatScore(score)} meets pre-release threshold (≥0.75) with proof≥0.75`);
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
verdict = "block";
|
|
53
|
+
reasons.push(`Score ${formatScore(score)} below pre-release threshold (≥0.75) or proof below 0.75`);
|
|
54
|
+
}
|
|
55
|
+
return {
|
|
56
|
+
score,
|
|
57
|
+
verdict,
|
|
58
|
+
blocked,
|
|
59
|
+
reasons,
|
|
60
|
+
};
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
function clamp01(n) {
|
|
65
|
+
if (Number.isNaN(n))
|
|
66
|
+
return 0;
|
|
67
|
+
return Math.max(0, Math.min(1, n));
|
|
68
|
+
}
|
|
69
|
+
function formatScore(n) {
|
|
70
|
+
return n.toFixed(4);
|
|
71
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Section 21 — CLI v2 Release Commands (Clipanion)
|
|
3
|
+
*
|
|
4
|
+
* `omk release check` — Evaluate release promotion gate
|
|
5
|
+
* `omk release promote` — Promote release if gate passes (stub)
|
|
6
|
+
*/
|
|
7
|
+
import { Command, type Cli } from "clipanion";
|
|
8
|
+
type ClipanionRegistrar = Pick<Cli, "register">;
|
|
9
|
+
export declare class ReleaseCheckCommand extends Command {
|
|
10
|
+
static paths: string[][];
|
|
11
|
+
static usage: import("clipanion").Usage;
|
|
12
|
+
ci: string;
|
|
13
|
+
schema: string;
|
|
14
|
+
docs: string;
|
|
15
|
+
proof: string;
|
|
16
|
+
provider: string;
|
|
17
|
+
regression: string;
|
|
18
|
+
install: string;
|
|
19
|
+
semver: string;
|
|
20
|
+
json: boolean;
|
|
21
|
+
execute(): Promise<number>;
|
|
22
|
+
}
|
|
23
|
+
export declare class ReleasePromoteCommand extends Command {
|
|
24
|
+
static paths: string[][];
|
|
25
|
+
static usage: import("clipanion").Usage;
|
|
26
|
+
execute(): Promise<number>;
|
|
27
|
+
}
|
|
28
|
+
export declare function registerReleaseCommandsV2(cli: ClipanionRegistrar): void;
|
|
29
|
+
export {};
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Section 21 — CLI v2 Release Commands (Clipanion)
|
|
3
|
+
*
|
|
4
|
+
* `omk release check` — Evaluate release promotion gate
|
|
5
|
+
* `omk release promote` — Promote release if gate passes (stub)
|
|
6
|
+
*/
|
|
7
|
+
import { Command, Option } from "clipanion";
|
|
8
|
+
import { createReleasePromotionGate } from "../release-promotion-gate.js";
|
|
9
|
+
// ──────────────────────────────────────────────
|
|
10
|
+
// Release Check
|
|
11
|
+
// ──────────────────────────────────────────────
|
|
12
|
+
export class ReleaseCheckCommand extends Command {
|
|
13
|
+
static paths = [["release", "check"]];
|
|
14
|
+
static usage = Command.Usage({
|
|
15
|
+
description: "Evaluate release promotion gate",
|
|
16
|
+
examples: [["Check release readiness", "omk release check"]],
|
|
17
|
+
});
|
|
18
|
+
ci = Option.String("--ci", "1", { description: "CI score (0–1)" });
|
|
19
|
+
schema = Option.String("--schema", "1", { description: "Schema score (0–1)" });
|
|
20
|
+
docs = Option.String("--docs", "1", { description: "Docs score (0–1)" });
|
|
21
|
+
proof = Option.String("--proof", "1", { description: "Proof median (0–1)" });
|
|
22
|
+
provider = Option.String("--provider", "1", { description: "Provider minimum (0–1)" });
|
|
23
|
+
regression = Option.String("--regression", "0", { description: "Regression severity (0–1)" });
|
|
24
|
+
install = Option.String("--install", "1", { description: "Fresh install smoke (0–1)" });
|
|
25
|
+
semver = Option.String("--semver", "1", { description: "Semver score (0–1)" });
|
|
26
|
+
json = Option.Boolean("--json", false, { description: "JSON output" });
|
|
27
|
+
async execute() {
|
|
28
|
+
const gate = createReleasePromotionGate();
|
|
29
|
+
const inputs = {
|
|
30
|
+
ci: Number.parseFloat(this.ci),
|
|
31
|
+
schema: Number.parseFloat(this.schema),
|
|
32
|
+
docs: Number.parseFloat(this.docs),
|
|
33
|
+
proofMedian: Number.parseFloat(this.proof),
|
|
34
|
+
providerMinimum: Number.parseFloat(this.provider),
|
|
35
|
+
regressionSeverity: Number.parseFloat(this.regression),
|
|
36
|
+
freshInstallSmoke: Number.parseFloat(this.install),
|
|
37
|
+
semver: Number.parseFloat(this.semver),
|
|
38
|
+
};
|
|
39
|
+
const result = gate.evaluate(inputs);
|
|
40
|
+
if (this.json) {
|
|
41
|
+
this.context.stdout.write(JSON.stringify(result, null, 2) + "\n");
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
this.context.stdout.write("Release Promotion Gate\n");
|
|
45
|
+
this.context.stdout.write(`Score: ${result.score.toFixed(4)}\n`);
|
|
46
|
+
this.context.stdout.write(`Verdict: ${result.verdict}\n`);
|
|
47
|
+
this.context.stdout.write(`Blocked: ${result.blocked}\n`);
|
|
48
|
+
if (result.reasons.length > 0) {
|
|
49
|
+
this.context.stdout.write("Reasons:\n");
|
|
50
|
+
for (const reason of result.reasons) {
|
|
51
|
+
this.context.stdout.write(` - ${reason}\n`);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return result.verdict === "block" ? 1 : 0;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
// ──────────────────────────────────────────────
|
|
59
|
+
// Release Promote
|
|
60
|
+
// ──────────────────────────────────────────────
|
|
61
|
+
export class ReleasePromoteCommand extends Command {
|
|
62
|
+
static paths = [["release", "promote"]];
|
|
63
|
+
static usage = Command.Usage({
|
|
64
|
+
description: "Promote release if gate passes",
|
|
65
|
+
examples: [["Promote release", "omk release promote"]],
|
|
66
|
+
});
|
|
67
|
+
async execute() {
|
|
68
|
+
const gate = createReleasePromotionGate();
|
|
69
|
+
const inputs = {
|
|
70
|
+
ci: Number.parseFloat(process.env.OMK_RELEASE_CI ?? "1"),
|
|
71
|
+
schema: Number.parseFloat(process.env.OMK_RELEASE_SCHEMA ?? "1"),
|
|
72
|
+
docs: Number.parseFloat(process.env.OMK_RELEASE_DOCS ?? "1"),
|
|
73
|
+
proofMedian: Number.parseFloat(process.env.OMK_RELEASE_PROOF ?? "1"),
|
|
74
|
+
providerMinimum: Number.parseFloat(process.env.OMK_RELEASE_PROVIDER ?? "1"),
|
|
75
|
+
regressionSeverity: Number.parseFloat(process.env.OMK_RELEASE_REGRESSION ?? "0"),
|
|
76
|
+
freshInstallSmoke: Number.parseFloat(process.env.OMK_RELEASE_INSTALL ?? "1"),
|
|
77
|
+
semver: Number.parseFloat(process.env.OMK_RELEASE_SEMVER ?? "1"),
|
|
78
|
+
};
|
|
79
|
+
const result = gate.evaluate(inputs);
|
|
80
|
+
this.context.stdout.write(JSON.stringify(result, null, 2) + "\n");
|
|
81
|
+
if (result.verdict === "block") {
|
|
82
|
+
this.context.stderr.write("Release promotion blocked.\n");
|
|
83
|
+
return 1;
|
|
84
|
+
}
|
|
85
|
+
this.context.stdout.write("Release promotion approved.\n");
|
|
86
|
+
return 0;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
// ──────────────────────────────────────────────
|
|
90
|
+
// Registration
|
|
91
|
+
// ──────────────────────────────────────────────
|
|
92
|
+
export function registerReleaseCommandsV2(cli) {
|
|
93
|
+
cli.register(ReleaseCheckCommand);
|
|
94
|
+
cli.register(ReleasePromoteCommand);
|
|
95
|
+
}
|
|
@@ -245,6 +245,11 @@ export async function chatCommand(options) {
|
|
|
245
245
|
catch {
|
|
246
246
|
// Update prompts are advisory and must not block chat startup.
|
|
247
247
|
}
|
|
248
|
+
// NOTE: the star/update prompts above use @inquirer/prompts (raw mode + their
|
|
249
|
+
// own readline) and can leave interactive stdin paused. The native root loop
|
|
250
|
+
// re-validates and resumes a paused TTY via resumeInteractiveInput() right
|
|
251
|
+
// before it builds its readline (see runNativeOmkRootLoop), so the first run
|
|
252
|
+
// after `omk init` does not exit immediately with "Session ended".
|
|
248
253
|
}
|
|
249
254
|
// ── tmux cockpit layout: keep prompt pane clean; move cockpit/HUD telemetry to the side pane ──
|
|
250
255
|
const explicitTmux = layout === "tmux";
|
|
@@ -2,6 +2,9 @@ import { style } from "../../util/theme.js";
|
|
|
2
2
|
import { runShell } from "../../util/shell.js";
|
|
3
3
|
import { createDag } from "../../orchestration/dag.js";
|
|
4
4
|
import { applyCapabilityInjectionToRouting, buildCapabilityInjection, } from "../../runtime/capability-injection.js";
|
|
5
|
+
import { capabilityScopesFromRouting } from "../../orchestration/capability-routing.js";
|
|
6
|
+
import { decideToolAuthority, } from "../../safety/tool-authority-gate.js";
|
|
7
|
+
import { resolveToolAuthorityEnforcement } from "../../runtime/tool-dispatch-contracts.js";
|
|
5
8
|
import { compileBloatToNlp, } from "../../runtime/debloat-nlp.js";
|
|
6
9
|
import { buildPromptEnvelope, renderPromptEnvelope, } from "../../runtime/prompt-envelope.js";
|
|
7
10
|
import { buildTaskRunContext } from "../../runtime/worker-manifest.js";
|
|
@@ -10,6 +13,7 @@ import { persistInputEnvelope } from "../../input/input-artifacts.js";
|
|
|
10
13
|
import { buildDagCompileResult } from "../../orchestration/dag-compiler.js";
|
|
11
14
|
import { persistDagCompileArtifacts } from "../../orchestration/dag-artifacts.js";
|
|
12
15
|
import { TerminalOwner } from "../../util/terminal-owner.js";
|
|
16
|
+
import { resumeInteractiveInput } from "../../util/terminal-input.js";
|
|
13
17
|
import { executeHarnessRun } from "../../harness/execute-harness-run.js";
|
|
14
18
|
import { normalizeProviderId, readProviderRegistry } from "../../providers/model-registry.js";
|
|
15
19
|
import { renderProviderModelTable } from "../../providers/model-table.js";
|
|
@@ -192,6 +196,53 @@ function bootstrapProviderPolicy(bootstrap) {
|
|
|
192
196
|
return "auto";
|
|
193
197
|
}
|
|
194
198
|
}
|
|
199
|
+
function nativeTurnRiskToToolOp(risk) {
|
|
200
|
+
switch (risk) {
|
|
201
|
+
case "merge":
|
|
202
|
+
return "merge";
|
|
203
|
+
case "shell":
|
|
204
|
+
return "shell";
|
|
205
|
+
case "write":
|
|
206
|
+
return "write";
|
|
207
|
+
default:
|
|
208
|
+
return "read";
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Shadow-only tool-authority observability at the live turn dispatch
|
|
213
|
+
* checkpoint. The kimi runner executes tools inside a spawned CLI, so per-tool
|
|
214
|
+
* enforcement lives in dispatchToolCallsByContract; here we only compute and
|
|
215
|
+
* record the turn-level verdict for the trace. Default output is byte-identical
|
|
216
|
+
* (emitted only under OMK_DEBUG / OMK_TOOL_AUTHORITY_TRACE); this path never
|
|
217
|
+
* blocks dispatch.
|
|
218
|
+
*/
|
|
219
|
+
function recordNativeTurnToolAuthority(input) {
|
|
220
|
+
const traceEnabled = input.env.OMK_DEBUG === "1" ||
|
|
221
|
+
/^(1|true|yes|on)$/i.test(input.env.OMK_TOOL_AUTHORITY_TRACE ?? "");
|
|
222
|
+
if (!traceEnabled)
|
|
223
|
+
return;
|
|
224
|
+
const routing = input.node.routing;
|
|
225
|
+
const scopes = capabilityScopesFromRouting(routing);
|
|
226
|
+
const op = nativeTurnRiskToToolOp(routing?.risk);
|
|
227
|
+
const enforce = resolveToolAuthorityEnforcement(input.env);
|
|
228
|
+
const decision = decideToolAuthority({
|
|
229
|
+
op,
|
|
230
|
+
writeAuthority: scopes.writeAuthority,
|
|
231
|
+
shellAuthority: scopes.shellAuthority,
|
|
232
|
+
approvalPolicy: nativeApprovalPolicy(routing?.approvalPolicy ?? routing?.executionPrompt),
|
|
233
|
+
sandboxMode: routing?.sandboxMode === "read-only" ? "read-only" : "workspace-write",
|
|
234
|
+
tty: Boolean(process.stdout.isTTY),
|
|
235
|
+
});
|
|
236
|
+
const line = ` tool-authority(${enforce ? "enforce" : "shadow"}): node=${input.node.id} ` +
|
|
237
|
+
`op=${op} decision=${decision} write=${scopes.writeAuthority} ` +
|
|
238
|
+
`shell=${scopes.shellAuthority} sandbox=${routing?.sandboxMode ?? "auto"}\n`;
|
|
239
|
+
if (input.renderer) {
|
|
240
|
+
input.renderer.emit({ type: "control:output", text: style.phosphorDim(line) });
|
|
241
|
+
}
|
|
242
|
+
else {
|
|
243
|
+
process.stderr.write(style.phosphorDim(line));
|
|
244
|
+
}
|
|
245
|
+
}
|
|
195
246
|
function emitNativeTurnRoute(input) {
|
|
196
247
|
if (!input.heartbeatEnabled)
|
|
197
248
|
return;
|
|
@@ -463,6 +514,7 @@ async function executeNativeRootTurn(input) {
|
|
|
463
514
|
const routing = input.node.routing;
|
|
464
515
|
const activity = describeNativeTurnActivity(input.node);
|
|
465
516
|
emitNativeTurnRoute(input);
|
|
517
|
+
recordNativeTurnToolAuthority({ node: input.node, env: input.env, renderer: input.renderer });
|
|
466
518
|
let heartbeatPrinted = false;
|
|
467
519
|
let heartbeatLineClosed = false;
|
|
468
520
|
const heartbeat = input.heartbeatEnabled
|
|
@@ -598,6 +650,7 @@ async function executeNativeRootHarnessTurn(input) {
|
|
|
598
650
|
skillNames: node.routing?.skills ?? input.skillNames,
|
|
599
651
|
hookNames: node.routing?.hooks ?? input.hookNames,
|
|
600
652
|
});
|
|
653
|
+
recordNativeTurnToolAuthority({ node, env: input.env, renderer: input.renderer });
|
|
601
654
|
},
|
|
602
655
|
onNodeComplete: (node, result) => {
|
|
603
656
|
completed.push({ node, result });
|
|
@@ -666,6 +719,13 @@ export async function runNativeOmkRootLoop(input) {
|
|
|
666
719
|
else
|
|
667
720
|
process.stdout.write(`${text}\n`);
|
|
668
721
|
};
|
|
722
|
+
// Defensive stdin re-validation before the chat readline takes ownership.
|
|
723
|
+
// The first-run GitHub-star / update prompts (@inquirer/prompts) can take
|
|
724
|
+
// over raw mode and leave the shared interactive stdin paused; a paused TTY
|
|
725
|
+
// makes the readline below see an immediate EOF/'close' and exit the loop
|
|
726
|
+
// with "Session ended". This only resumes an explicitly-paused TTY and never
|
|
727
|
+
// touches non-TTY stdin (its EOF/exit behavior must stay unchanged).
|
|
728
|
+
resumeInteractiveInput(process.stdin);
|
|
669
729
|
const { createInterface } = await import("readline");
|
|
670
730
|
const rl = createInterface({
|
|
671
731
|
input: process.stdin,
|
|
@@ -731,6 +791,7 @@ export async function runNativeOmkRootLoop(input) {
|
|
|
731
791
|
let running = true;
|
|
732
792
|
let readlineClosed = false;
|
|
733
793
|
let activeTurnAbort;
|
|
794
|
+
let pendingPastedImageLine;
|
|
734
795
|
const queuedLines = [];
|
|
735
796
|
let pendingLineResolve;
|
|
736
797
|
const resolveNextLine = (value) => {
|
|
@@ -786,7 +847,7 @@ export async function runNativeOmkRootLoop(input) {
|
|
|
786
847
|
const userInput = await readPromptLine();
|
|
787
848
|
if (userInput === undefined)
|
|
788
849
|
break;
|
|
789
|
-
|
|
850
|
+
let line = userInput.trim();
|
|
790
851
|
if (!line)
|
|
791
852
|
continue;
|
|
792
853
|
renderer?.emit({ type: "input:submitted", text: line });
|
|
@@ -795,6 +856,10 @@ export async function runNativeOmkRootLoop(input) {
|
|
|
795
856
|
break;
|
|
796
857
|
}
|
|
797
858
|
const parsedSlash = parseSlashInput(line);
|
|
859
|
+
if (!parsedSlash && pendingPastedImageLine) {
|
|
860
|
+
line = `${pendingPastedImageLine}\n${line}`;
|
|
861
|
+
pendingPastedImageLine = undefined;
|
|
862
|
+
}
|
|
798
863
|
const inputEnvelope = buildNativeInputEnvelope({
|
|
799
864
|
loopInput: input,
|
|
800
865
|
state,
|
|
@@ -836,6 +901,14 @@ export async function runNativeOmkRootLoop(input) {
|
|
|
836
901
|
try {
|
|
837
902
|
await terminalOwner.withChildProcess(rl, async () => {
|
|
838
903
|
const result = await runSlashHandler(handler, parsedSlash, slashContext);
|
|
904
|
+
if (parsedSlash.command === "/paste" && result.ok) {
|
|
905
|
+
const pasteLine = result.text?.trim();
|
|
906
|
+
if (pasteLine) {
|
|
907
|
+
pendingPastedImageLine = pendingPastedImageLine
|
|
908
|
+
? `${pendingPastedImageLine}\n${pasteLine}`
|
|
909
|
+
: pasteLine;
|
|
910
|
+
}
|
|
911
|
+
}
|
|
839
912
|
if (result.exit)
|
|
840
913
|
running = false;
|
|
841
914
|
});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { style } from "../../../../util/theme.js";
|
|
2
2
|
import { readTodos } from "../../../../util/todo-sync.js";
|
|
3
3
|
import { commandLine, formatScopedNames, section } from "../format.js";
|
|
4
|
-
import { okSlashResult } from "../result.js";
|
|
4
|
+
import { errorSlashResult, okSlashResult } from "../result.js";
|
|
5
5
|
export function buildSessionSlashCommands() {
|
|
6
6
|
return [
|
|
7
7
|
{
|
|
@@ -22,6 +22,23 @@ export function buildSessionSlashCommands() {
|
|
|
22
22
|
examples: ["/help"],
|
|
23
23
|
handler: () => okSlashResult({ text: renderSlashHelp() }),
|
|
24
24
|
},
|
|
25
|
+
{
|
|
26
|
+
name: "/paste",
|
|
27
|
+
aliases: [],
|
|
28
|
+
group: "session",
|
|
29
|
+
summary: "Paste image from clipboard",
|
|
30
|
+
usage: "/paste",
|
|
31
|
+
examples: ["/paste"],
|
|
32
|
+
handler: async (ctx) => {
|
|
33
|
+
const { pasteClipboardImage } = await import("../../../../util/clipboard-image.js");
|
|
34
|
+
const result = pasteClipboardImage(ctx.input.root);
|
|
35
|
+
if (!result.ok)
|
|
36
|
+
return errorSlashResult(result.error ?? "No image in clipboard");
|
|
37
|
+
if (!result.relativePath)
|
|
38
|
+
return errorSlashResult("Clipboard image saved without a relative path");
|
|
39
|
+
return okSlashResult({ text: `Image file: ${result.relativePath}` });
|
|
40
|
+
},
|
|
41
|
+
},
|
|
25
42
|
{
|
|
26
43
|
name: "/status",
|
|
27
44
|
aliases: ["/s"],
|
|
@@ -76,6 +93,7 @@ function renderSlashHelp() {
|
|
|
76
93
|
commandLine("/theme", "<system24|green-rain|neon-grid|rust-forge|plain|high-contrast>", "Set session theme"),
|
|
77
94
|
commandLine("/view", "<summary|graph|evidence|tool-plane|events>", "Set control-plane view"),
|
|
78
95
|
commandLine("/animation", "<off|low|auto|full>", "Set animation policy"),
|
|
96
|
+
commandLine("/paste", "", "Paste image from clipboard"),
|
|
79
97
|
commandLine("/status", "", "Session status"),
|
|
80
98
|
commandLine("/clear", "/cls", "Clear screen"),
|
|
81
99
|
commandLine("/runs", "", "Recent run history"),
|
|
@@ -3,7 +3,9 @@ import { join } from "path";
|
|
|
3
3
|
import { writeFile, mkdir } from "fs/promises";
|
|
4
4
|
import { getProjectRoot, pathExists, getRunPath, getRunsDir } from "../util/fs.js";
|
|
5
5
|
import { header, label } from "../util/theme.js";
|
|
6
|
-
import { createDag } from "../orchestration/dag.js";
|
|
6
|
+
import { createDag, getExecutionWaves } from "../orchestration/dag.js";
|
|
7
|
+
import { createOmkJsonEnvelope } from "../util/json-envelope.js";
|
|
8
|
+
import { emitJson } from "../util/cli-contract.js";
|
|
7
9
|
function inferRole(description, explicitRole) {
|
|
8
10
|
if (explicitRole)
|
|
9
11
|
return explicitRole;
|
|
@@ -243,6 +245,61 @@ export async function loadSpecDag(specDir, options = {}) {
|
|
|
243
245
|
}
|
|
244
246
|
return tasksToDag(tasks, { parallel: options.parallel });
|
|
245
247
|
}
|
|
248
|
+
/**
|
|
249
|
+
* Project the in-memory DAG artifact into the envelope `data` shape. The inner
|
|
250
|
+
* dag artifact (nodes) is preserved verbatim; edges/batches/stats are derived
|
|
251
|
+
* from the existing dependsOn graph (no new data sources).
|
|
252
|
+
*/
|
|
253
|
+
function buildDagJsonData(inputId, dag) {
|
|
254
|
+
const edges = dag.nodes.flatMap((node) => node.dependsOn.map((from) => ({ from, to: node.id })));
|
|
255
|
+
const batches = getExecutionWaves(dag).map((wave) => wave.map((node) => node.id));
|
|
256
|
+
return {
|
|
257
|
+
inputId,
|
|
258
|
+
nodes: dag.nodes,
|
|
259
|
+
edges,
|
|
260
|
+
batches,
|
|
261
|
+
stats: { nodes: dag.nodes.length, edges: edges.length, batches: batches.length },
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* JSON path for `omk dag from-spec --json`.
|
|
266
|
+
* Emits EXACTLY ONE omk.contract.v1 envelope to stdout (no banner, no ANSI) and
|
|
267
|
+
* never throws on a missing/empty tasks.md, so JSON stdout stays one envelope.
|
|
268
|
+
*/
|
|
269
|
+
async function emitDagFromSpecJson(targetDir, options) {
|
|
270
|
+
const started = Date.now();
|
|
271
|
+
let dag;
|
|
272
|
+
try {
|
|
273
|
+
dag = await loadSpecDag(targetDir, { parallel: options.parallel });
|
|
274
|
+
}
|
|
275
|
+
catch (err) {
|
|
276
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
277
|
+
const empty = { nodes: [] };
|
|
278
|
+
emitJson(createOmkJsonEnvelope({
|
|
279
|
+
command: "dag",
|
|
280
|
+
status: "not-applicable",
|
|
281
|
+
ok: false,
|
|
282
|
+
data: buildDagJsonData(targetDir, empty),
|
|
283
|
+
warnings: [
|
|
284
|
+
{ code: "RUN_ARTIFACT_MISSING", message, recoverable: true, severity: "warning" },
|
|
285
|
+
],
|
|
286
|
+
durationMs: Date.now() - started,
|
|
287
|
+
}));
|
|
288
|
+
return empty;
|
|
289
|
+
}
|
|
290
|
+
if (options.output) {
|
|
291
|
+
await mkdir(getRunsDir(getProjectRoot()), { recursive: true });
|
|
292
|
+
await writeFile(options.output, JSON.stringify(dag, null, 2));
|
|
293
|
+
}
|
|
294
|
+
emitJson(createOmkJsonEnvelope({
|
|
295
|
+
command: "dag",
|
|
296
|
+
status: "passed",
|
|
297
|
+
ok: true,
|
|
298
|
+
data: buildDagJsonData(targetDir, dag),
|
|
299
|
+
durationMs: Date.now() - started,
|
|
300
|
+
}));
|
|
301
|
+
return dag;
|
|
302
|
+
}
|
|
246
303
|
export async function dagFromSpecCommand(specDir, options = {}) {
|
|
247
304
|
const root = getProjectRoot();
|
|
248
305
|
let targetDir = specDir;
|
|
@@ -266,6 +323,9 @@ export async function dagFromSpecCommand(specDir, options = {}) {
|
|
|
266
323
|
targetDir = getRunPath(options.run, undefined, root);
|
|
267
324
|
}
|
|
268
325
|
}
|
|
326
|
+
if (options.json === true || process.argv.includes("--json")) {
|
|
327
|
+
return emitDagFromSpecJson(targetDir, { output: options.output, parallel: options.parallel });
|
|
328
|
+
}
|
|
269
329
|
const dag = await loadSpecDag(targetDir, { parallel: options.parallel });
|
|
270
330
|
const dagJson = JSON.stringify(dag, null, 2);
|
|
271
331
|
if (options.output) {
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
interface GoalInterviewOptions {
|
|
2
|
+
goalId?: string;
|
|
3
|
+
mode?: string;
|
|
4
|
+
depth?: string;
|
|
5
|
+
maxQuestions?: string;
|
|
6
|
+
answers?: string;
|
|
7
|
+
image?: string;
|
|
8
|
+
writeSpec?: boolean;
|
|
9
|
+
json?: boolean;
|
|
10
|
+
}
|
|
11
|
+
interface GoalRefineOptions {
|
|
12
|
+
fromInterview?: string;
|
|
13
|
+
plan?: boolean;
|
|
14
|
+
json?: boolean;
|
|
15
|
+
}
|
|
16
|
+
export declare function goalInterviewCommand(input: string | undefined, options: GoalInterviewOptions): Promise<void>;
|
|
17
|
+
export declare function goalRefineCommand(goalId: string, options: GoalRefineOptions): Promise<void>;
|
|
18
|
+
export {};
|