@kontextmind/kxm 0.7.40 → 0.7.43
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/.claude-plugin/marketplace.json +1 -1
- package/CHANGELOG.md +41 -7
- package/docs/README.md +5 -3
- package/docs/agent-skills.md +1 -1
- package/docs/architecture.md +1 -1
- package/docs/configuration.md +28 -5
- package/docs/{vnext → contracts}/README.md +7 -7
- package/docs/{vnext → contracts}/architecture.md +2 -2
- package/docs/{vnext → contracts}/migration.md +25 -25
- package/docs/{vnext → contracts}/routing.md +2 -2
- package/docs/{vnext → contracts}/synchronization.md +1 -1
- package/docs/{vnext → contracts}/validation.md +7 -7
- package/docs/getting-started.md +1 -1
- package/docs/kb/qa-authentik-authentication.md +47 -0
- package/docs/kb/qa-extension-install-and-hub-bootstrap.md +68 -0
- package/docs/kb/qa-hub-on-a-public-host.md +31 -0
- package/docs/kb/qa-sqlite-vs-duckdb.md +18 -0
- package/docs/kb/qa-what-the-hub-stores.md +47 -0
- package/docs/packages.md +91 -0
- package/docs/templates/architecture.md +1 -1
- package/docs/test-matrix.md +6 -6
- package/docs/tui-components.md +131 -0
- package/examples/README.md +2 -2
- package/examples/{vnext → project}/.kxm/agents/critic-3.yaml +1 -1
- package/examples/{vnext → project}/README.md +4 -4
- package/package.json +18 -8
- package/packages/core/tui/CHANGELOG.md +19 -0
- package/packages/core/tui/LICENSE +21 -0
- package/packages/core/tui/README.md +45 -0
- package/packages/core/tui/dist/index.js +1439 -0
- package/packages/core/tui/src/adapters/pi.ts +73 -0
- package/packages/core/tui/src/adapters/terminal.ts +106 -0
- package/packages/core/tui/src/exports/index.ts +30 -0
- package/packages/core/tui/src/services/registry.ts +274 -0
- package/packages/core/tui/src/tui/keys.ts +130 -0
- package/packages/core/tui/src/tui/layout.ts +117 -0
- package/packages/core/tui/src/tui/panel.ts +435 -0
- package/packages/core/tui/src/tui/panelComponent.ts +208 -0
- package/packages/core/tui/src/tui/render.ts +309 -0
- package/packages/core/tui/src/tui/theme.ts +145 -0
- package/packages/core/tui/src/types/surface.ts +407 -0
- package/plugins/kxm/.claude-plugin/plugin.json +1 -1
- package/plugins/kxm/dist/cli.js +681 -553
- package/plugins/kxm/dist/extension.js +18 -18
- package/plugins/kxm/dist/mcp-server.js +1 -1
- package/plugins/kxm/dist/{vnext-runtime-supervisor.js → runtime-supervisor.js} +553 -553
- package/plugins/kxm/dist/runtime.js +608 -608
- package/plugins/kxm/dist/server.js +13 -13
- package/plugins/kxm/package.json +2 -2
- package/plugins/kxm/skills/kxm-project-setup/SKILL.md +2 -2
- package/plugins/kxm/skills/kxm-runs/SKILL.md +3 -3
- package/plugins/kxm/skills/kxm-session/SKILL.md +1 -1
- package/plugins/kxm/skills/kxm-workflow/SKILL.md +1 -1
- package/plugins/kxm/src/autocomplete.ts +4 -4
- package/plugins/kxm/src/{vnext-bindings.ts → bindings.ts} +48 -48
- package/plugins/kxm/src/cli/hub.ts +3 -3
- package/plugins/kxm/src/cli/{vnext.ts → project.ts} +106 -106
- package/plugins/kxm/src/cli/roles.ts +9 -9
- package/plugins/kxm/src/cli/system.ts +2 -2
- package/plugins/kxm/src/cli/tasks.ts +8 -8
- package/plugins/kxm/src/cli/workflows.ts +8 -8
- package/plugins/kxm/src/cli.ts +47 -47
- package/plugins/kxm/src/config.ts +69 -14
- package/plugins/kxm/src/database.ts +4 -4
- package/plugins/kxm/src/{vnext-engine-artifacts.ts → engine-artifacts.ts} +7 -7
- package/plugins/kxm/src/{vnext-engine-command.ts → engine-command.ts} +40 -40
- package/plugins/kxm/src/{vnext-engine-compile.ts → engine-compile.ts} +64 -64
- package/plugins/kxm/src/{vnext-engine-evidence.ts → engine-evidence.ts} +26 -26
- package/plugins/kxm/src/{vnext-engine-fold.ts → engine-fold.ts} +99 -99
- package/plugins/kxm/src/{vnext-engine-gate-records.ts → engine-gate-records.ts} +105 -105
- package/plugins/kxm/src/{vnext-engine-plan.ts → engine-plan.ts} +69 -69
- package/plugins/kxm/src/{vnext-engine.ts → engine.ts} +502 -502
- package/plugins/kxm/src/gate-hash.ts +10 -0
- package/plugins/kxm/src/{vnext-harness.ts → harness.ts} +1 -1
- package/plugins/kxm/src/hub.ts +1 -1
- package/plugins/kxm/src/init-guide-setup.ts +3 -3
- package/plugins/kxm/src/{vnext-init.ts → init.ts} +93 -93
- package/plugins/kxm/src/kxm-install-kind.ts +1 -1
- package/plugins/kxm/src/kxm-update-config.ts +2 -2
- package/plugins/kxm/src/local-snapshot.ts +23 -23
- package/plugins/kxm/src/mcp-server.ts +1 -1
- package/plugins/kxm/src/{vnext-migrate.ts → migrate.ts} +123 -123
- package/plugins/kxm/src/model-inventory.ts +1 -1
- package/plugins/kxm/src/{vnext-oneshot-evidence.ts → oneshot-evidence.ts} +2 -2
- package/plugins/kxm/src/{vnext-oneshot-process.ts → oneshot-process.ts} +4 -4
- package/plugins/kxm/src/{vnext-oneshot-producer.ts → oneshot-producer.ts} +24 -24
- package/plugins/kxm/src/{vnext-permission.ts → permission.ts} +86 -86
- package/plugins/kxm/src/{vnext-pi-producer.ts → pi-producer.ts} +13 -13
- package/plugins/kxm/src/{vnext-config.ts → project-config.ts} +177 -177
- package/plugins/kxm/src/{vnext-repair.ts → repair.ts} +159 -159
- package/plugins/kxm/src/restricted-yaml.d.mts +3 -3
- package/plugins/kxm/src/restricted-yaml.mjs +4 -4
- package/plugins/kxm/src/{vnext-runtime-owner.ts → runtime-owner.ts} +35 -35
- package/plugins/kxm/src/{vnext-runtime.ts → runtime-service.ts} +136 -136
- package/plugins/kxm/src/{vnext-runtime-store.ts → runtime-store.ts} +145 -145
- package/plugins/kxm/src/{vnext-runtime-supervisor.ts → runtime-supervisor.ts} +80 -80
- package/plugins/kxm/src/runtime.ts +6 -6
- package/plugins/kxm/src/session-work.ts +3 -3
- package/plugins/kxm/src/studio-layout.ts +6 -6
- package/plugins/kxm/src/{vnext-template.ts → template.ts} +33 -33
- package/plugins/kxm/src/tui.ts +10 -24
- package/schemas/{vnext/README.md → README.md} +3 -3
- package/schemas/{vnext/agent.schema.json → agent.schema.json} +1 -1
- package/schemas/{vnext/assignment-result.schema.json → assignment-result.schema.json} +1 -1
- package/schemas/{vnext/backup-manifest.schema.json → backup-manifest.schema.json} +1 -1
- package/schemas/{vnext/candidate.schema.json → candidate.schema.json} +1 -1
- package/schemas/{vnext/common.schema.json → common.schema.json} +2 -2
- package/schemas/{vnext/context-candidate.schema.json → context-candidate.schema.json} +1 -1
- package/schemas/{vnext/context-packet.schema.json → context-packet.schema.json} +1 -1
- package/schemas/{vnext/delivery-manifest.schema.json → delivery-manifest.schema.json} +1 -1
- package/schemas/{vnext/drive-receipt.schema.json → drive-receipt.schema.json} +2 -2
- package/schemas/{vnext/environment.schema.json → environment.schema.json} +1 -1
- package/schemas/{vnext/gate-registry.schema.json → gate-registry.schema.json} +1 -1
- package/schemas/{vnext/handoff-manifest.schema.json → handoff-manifest.schema.json} +1 -1
- package/schemas/{vnext/init-operation.schema.json → init-operation.schema.json} +1 -1
- package/schemas/{vnext/local-repository-bindings.schema.json → local-repository-bindings.schema.json} +1 -1
- package/schemas/{vnext/memory-record.schema.json → memory-record.schema.json} +1 -1
- package/schemas/{vnext/migration-decision.schema.json → migration-decision.schema.json} +1 -1
- package/schemas/{vnext/migration-plan.schema.json → migration-plan.schema.json} +1 -1
- package/schemas/{vnext/migration-receipt.schema.json → migration-receipt.schema.json} +1 -1
- package/schemas/{vnext/model.schema.json → model.schema.json} +1 -1
- package/schemas/{vnext/modes.schema.json → modes.schema.json} +1 -1
- package/schemas/{vnext/permission-diff.schema.json → permission-diff.schema.json} +1 -1
- package/schemas/policy-draft/README.md +2 -2
- package/schemas/{vnext/prices.schema.json → prices.schema.json} +1 -1
- package/schemas/{vnext/project.schema.json → project.schema.json} +1 -1
- package/schemas/{vnext/repository.schema.json → repository.schema.json} +1 -1
- package/schemas/{vnext/role.schema.json → role.schema.json} +1 -1
- package/schemas/{vnext/run-event.schema.json → run-event.schema.json} +1 -1
- package/schemas/{vnext/session-brief.schema.json → session-brief.schema.json} +2 -2
- package/schemas/{vnext/sync-event.schema.json → sync-event.schema.json} +1 -1
- package/schemas/{vnext/template-provenance.schema.json → template-provenance.schema.json} +1 -1
- package/schemas/{vnext/workflow.schema.json → workflow.schema.json} +1 -1
- package/scripts/build-package.mjs +59 -0
- package/scripts/build-runtime.mjs +3 -2
- package/scripts/check-generated.mjs +2 -1
- package/scripts/check-versions.mjs +12 -1
- package/scripts/docker-install-smoke.mjs +268 -0
- package/scripts/emit-codex-artifacts.mjs +1 -1
- package/scripts/kxm-bump-version.mjs +53 -3
- package/scripts/kxm-runtime-supervisor.mjs +3 -3
- package/scripts/package-surfaces.mjs +39 -0
- package/plugins/kxm/src/vnext-gate-hash.ts +0 -10
- /package/docs/{vnext → contracts}/effects-and-recovery.md +0 -0
- /package/docs/{vnext → contracts}/lifecycles.md +0 -0
- /package/docs/{vnext → contracts}/terminology.md +0 -0
- /package/examples/{vnext → project}/.kxm/agents/coordinator.yaml +0 -0
- /package/examples/{vnext → project}/.kxm/agents/critic-1.yaml +0 -0
- /package/examples/{vnext → project}/.kxm/agents/critic-2.yaml +0 -0
- /package/examples/{vnext → project}/.kxm/agents/implementer.yaml +0 -0
- /package/examples/{vnext → project}/.kxm/agents/planner.yaml +0 -0
- /package/examples/{vnext → project}/.kxm/agents/reproducer.yaml +0 -0
- /package/examples/{vnext → project}/.kxm/agents/reviewer.yaml +0 -0
- /package/examples/{vnext → project}/.kxm/gates.yaml +0 -0
- /package/examples/{vnext → project}/.kxm/models/critic-claude.yaml +0 -0
- /package/examples/{vnext → project}/.kxm/models/critic-gemini.yaml +0 -0
- /package/examples/{vnext → project}/.kxm/models/critic-grok.yaml +0 -0
- /package/examples/{vnext → project}/.kxm/models/implementation.yaml +0 -0
- /package/examples/{vnext → project}/.kxm/models/primary.yaml +0 -0
- /package/examples/{vnext → project}/.kxm/prices.yaml +0 -0
- /package/examples/{vnext → project}/.kxm/project/env.yaml +0 -0
- /package/examples/{vnext → project}/.kxm/project.yaml +0 -0
- /package/examples/{vnext → project}/.kxm/repo/repo.yaml +0 -0
- /package/examples/{vnext → project}/.kxm/workflows/default.yaml +0 -0
- /package/examples/{vnext → project}/.kxm/workflows/fix.yaml +0 -0
- /package/examples/{vnext → project}/.kxm/workflows/improve.yaml +0 -0
- /package/examples/{vnext → project}/records/assignment-result-recorded.json +0 -0
- /package/examples/{vnext → project}/records/assignment-result.json +0 -0
- /package/examples/{vnext → project}/records/context-candidate.json +0 -0
- /package/examples/{vnext → project}/records/delivery-manifest.json +0 -0
- /package/examples/{vnext → project}/records/effect-uncertainty-resolved-sync.json +0 -0
- /package/examples/{vnext → project}/records/effect-uncertainty-resolved.json +0 -0
- /package/examples/{vnext → project}/records/run-created.json +0 -0
- /package/examples/{vnext → project}/records/sync-event.json +0 -0
- /package/examples/{vnext → project}/repositories/api/.kxm/repo/env.yaml +0 -0
- /package/examples/{vnext → project}/repositories/api/.kxm/repo/repo.yaml +0 -0
- /package/examples/{vnext → project}/repositories/web/.kxm/repo/repo.yaml +0 -0
|
@@ -1,23 +1,23 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
|
-
import {
|
|
2
|
+
import { kxmCanonicalJson, kxmPortablePath, type JsonObject, type JsonValue } from "./project-config.ts";
|
|
3
3
|
import {
|
|
4
|
-
|
|
5
|
-
type
|
|
6
|
-
type
|
|
7
|
-
type
|
|
8
|
-
type
|
|
9
|
-
type
|
|
10
|
-
type
|
|
11
|
-
type
|
|
12
|
-
} from "./
|
|
4
|
+
KXM_COMPILED_WORKFLOW_SCHEMA,
|
|
5
|
+
type KxmCompiledAssignments,
|
|
6
|
+
type KxmCompiledEvidence,
|
|
7
|
+
type KxmCompiledJoin,
|
|
8
|
+
type KxmCompiledPlan,
|
|
9
|
+
type KxmCompiledStep,
|
|
10
|
+
type KxmCompiledTransition,
|
|
11
|
+
type KxmRepositoryAccess,
|
|
12
|
+
} from "./engine-compile.ts";
|
|
13
13
|
import {
|
|
14
|
-
|
|
14
|
+
KXM_ABSENT_MEMORY_REVISION,
|
|
15
15
|
runtimeError,
|
|
16
|
-
type
|
|
17
|
-
type
|
|
18
|
-
} from "./
|
|
16
|
+
type KxmRunEventStore,
|
|
17
|
+
type KxmRunRecord,
|
|
18
|
+
} from "./runtime-store.ts";
|
|
19
19
|
|
|
20
|
-
export const
|
|
20
|
+
export const KXM_RUN_PLAN_SCHEMA = "kxm.run-plan.v2";
|
|
21
21
|
const SUPPORTED_KINDS = new Set(["agent", "moa", "gate", "approval", "wait"]);
|
|
22
22
|
const TERMINAL = new Set(["completed", "failed", "cancelled"]);
|
|
23
23
|
const JOIN_STRATEGIES = new Set(["all", "all-settled", "quorum", "first-success"]);
|
|
@@ -58,19 +58,19 @@ const JOIN_OPTIONAL = ["minimumPassed", "cancelRemaining"] as const;
|
|
|
58
58
|
const EVIDENCE_REQUIRED = ["key", "kind", "minimum", "reusableAcrossAttempts"] as const;
|
|
59
59
|
const EVIDENCE_OPTIONAL = ["producerPolicy"] as const;
|
|
60
60
|
|
|
61
|
-
export type
|
|
61
|
+
export type KxmGateDefinition =
|
|
62
62
|
| { readonly kind: "command"; readonly argv: readonly string[]; readonly timeoutMs: number; readonly cwd?: "control" }
|
|
63
63
|
| { readonly kind: "artifacts-exist"; readonly paths: readonly string[] }
|
|
64
64
|
| { readonly kind: "reserved" };
|
|
65
65
|
|
|
66
|
-
export interface
|
|
66
|
+
export interface KxmPinnedGates {
|
|
67
67
|
readonly registry: { readonly schema: typeof GATE_REGISTRY_SCHEMA; readonly hash: string } | null;
|
|
68
|
-
readonly definitions: Readonly<Record<string,
|
|
68
|
+
readonly definitions: Readonly<Record<string, KxmGateDefinition>>;
|
|
69
69
|
readonly controlRoot: { readonly repositoryId: "control"; readonly projectKey: string };
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
-
export interface
|
|
73
|
-
readonly schema: typeof
|
|
72
|
+
export interface KxmRunPlanEnvelope {
|
|
73
|
+
readonly schema: typeof KXM_RUN_PLAN_SCHEMA;
|
|
74
74
|
readonly runId: string;
|
|
75
75
|
readonly projectId: string;
|
|
76
76
|
readonly homeRuntimeId: string;
|
|
@@ -86,20 +86,20 @@ export interface VnextRunPlanEnvelope {
|
|
|
86
86
|
readonly maxRunDurationMs?: number;
|
|
87
87
|
readonly maxAgentTimeMs?: number;
|
|
88
88
|
};
|
|
89
|
-
readonly plan:
|
|
90
|
-
readonly gates:
|
|
89
|
+
readonly plan: KxmCompiledPlan;
|
|
90
|
+
readonly gates: KxmPinnedGates;
|
|
91
91
|
}
|
|
92
92
|
|
|
93
|
-
export function
|
|
93
|
+
export function kxmSha256(input: string): string {
|
|
94
94
|
return `sha256:${createHash("sha256").update(input, "utf8").digest("hex")}`;
|
|
95
95
|
}
|
|
96
96
|
|
|
97
|
-
export function
|
|
98
|
-
return
|
|
97
|
+
export function hashKxmRunPlanEnvelope(envelope: KxmRunPlanEnvelope): string {
|
|
98
|
+
return kxmSha256(kxmCanonicalJson(envelope as unknown as JsonValue));
|
|
99
99
|
}
|
|
100
100
|
|
|
101
|
-
export function
|
|
102
|
-
const steps = Object.create(null) as Record<string,
|
|
101
|
+
export function freezeKxmCompiledPlan(plan: KxmCompiledPlan): KxmCompiledPlan {
|
|
102
|
+
const steps = Object.create(null) as Record<string, KxmCompiledStep>;
|
|
103
103
|
for (const id of plan.order) {
|
|
104
104
|
const step = plan.steps[id];
|
|
105
105
|
if (!step) throw runtimeError("run_plan_corrupt", plan.workflowId, `compiled plan is missing step ${id}`);
|
|
@@ -113,14 +113,14 @@ export function freezeVnextCompiledPlan(plan: VnextCompiledPlan): VnextCompiledP
|
|
|
113
113
|
});
|
|
114
114
|
}
|
|
115
115
|
|
|
116
|
-
function freezeStep(step:
|
|
117
|
-
const transitions = Object.create(null) as Record<string,
|
|
116
|
+
function freezeStep(step: KxmCompiledStep): KxmCompiledStep {
|
|
117
|
+
const transitions = Object.create(null) as Record<string, KxmCompiledTransition>;
|
|
118
118
|
for (const outcome of step.outcomes) {
|
|
119
119
|
const transition = step.transitions[outcome];
|
|
120
120
|
if (!transition) throw runtimeError("run_plan_corrupt", step.id, `compiled plan is missing transition ${outcome}`);
|
|
121
121
|
transitions[outcome] = { ...transition };
|
|
122
122
|
}
|
|
123
|
-
const repositories = Object.create(null) as Record<string,
|
|
123
|
+
const repositories = Object.create(null) as Record<string, KxmRepositoryAccess>;
|
|
124
124
|
for (const [id, access] of Object.entries(step.repositories)) repositories[id] = access;
|
|
125
125
|
return deepFreeze({
|
|
126
126
|
...step,
|
|
@@ -138,7 +138,7 @@ function freezeStep(step: VnextCompiledStep): VnextCompiledStep {
|
|
|
138
138
|
});
|
|
139
139
|
}
|
|
140
140
|
|
|
141
|
-
export function
|
|
141
|
+
export function parseKxmRunPlanEnvelope(raw: string, run: KxmRunRecord, expectedHash: string): KxmRunPlanEnvelope {
|
|
142
142
|
let parsed: unknown;
|
|
143
143
|
try {
|
|
144
144
|
parsed = JSON.parse(raw) as unknown;
|
|
@@ -146,22 +146,22 @@ export function parseVnextRunPlanEnvelope(raw: string, run: VnextRunRecord, expe
|
|
|
146
146
|
throw runtimeError("run_plan_corrupt", run.runId, "run plan envelope is not JSON");
|
|
147
147
|
}
|
|
148
148
|
const envelope = parseEnvelope(parsed, run);
|
|
149
|
-
const hash =
|
|
149
|
+
const hash = hashKxmRunPlanEnvelope(envelope);
|
|
150
150
|
if (hash !== expectedHash) {
|
|
151
151
|
throw runtimeError("run_plan_corrupt", run.runId, "run plan envelope hash does not match");
|
|
152
152
|
}
|
|
153
153
|
return envelope;
|
|
154
154
|
}
|
|
155
155
|
|
|
156
|
-
export function
|
|
156
|
+
export function rehydrateKxmCompiledPlanFromStore(store: KxmRunEventStore, run: KxmRunRecord): KxmCompiledPlan {
|
|
157
157
|
return loadPinnedEnvelope(store, run).plan;
|
|
158
158
|
}
|
|
159
159
|
|
|
160
|
-
export function
|
|
160
|
+
export function loadKxmRunPlanEnvelope(store: KxmRunEventStore, run: KxmRunRecord): KxmRunPlanEnvelope {
|
|
161
161
|
return loadPinnedEnvelope(store, run);
|
|
162
162
|
}
|
|
163
163
|
|
|
164
|
-
function loadPinnedEnvelope(store:
|
|
164
|
+
function loadPinnedEnvelope(store: KxmRunEventStore, run: KxmRunRecord): KxmRunPlanEnvelope {
|
|
165
165
|
const row = store.runPlan(run.runId);
|
|
166
166
|
if (!row) throw runtimeError("run_plan_missing", run.runId, "run has no pinned plan");
|
|
167
167
|
const events = store.events(run.runId, 0, row.pinnedSequence);
|
|
@@ -170,17 +170,17 @@ function loadPinnedEnvelope(store: VnextRunEventStore, run: VnextRunRecord): Vne
|
|
|
170
170
|
if (typeof eventHash !== "string" || eventHash !== row.runPlanHash) {
|
|
171
171
|
throw runtimeError("run_plan_corrupt", run.runId, "pinned runPlanHash does not match the plan row");
|
|
172
172
|
}
|
|
173
|
-
return
|
|
173
|
+
return parseKxmRunPlanEnvelope(row.envelope, run, row.runPlanHash);
|
|
174
174
|
}
|
|
175
175
|
|
|
176
176
|
export function absentMemoryRevision(): string {
|
|
177
|
-
return
|
|
177
|
+
return KXM_ABSENT_MEMORY_REVISION;
|
|
178
178
|
}
|
|
179
179
|
|
|
180
|
-
function parseEnvelope(parsed: unknown, run:
|
|
180
|
+
function parseEnvelope(parsed: unknown, run: KxmRunRecord): KxmRunPlanEnvelope {
|
|
181
181
|
const value = asObject(parsed, run.runId, "run plan envelope");
|
|
182
182
|
assertExactKeys(value, ENVELOPE_REQUIRED, [], run.runId, "run plan envelope");
|
|
183
|
-
if (value.schema !==
|
|
183
|
+
if (value.schema !== KXM_RUN_PLAN_SCHEMA) {
|
|
184
184
|
throw runtimeError("run_plan_corrupt", run.runId, "schema is not kxm.run-plan.v2");
|
|
185
185
|
}
|
|
186
186
|
const runId = asString(value.runId, run.runId, "runId");
|
|
@@ -208,14 +208,14 @@ function parseEnvelope(parsed: unknown, run: VnextRunRecord): VnextRunPlanEnvelo
|
|
|
208
208
|
}
|
|
209
209
|
const limitsValue = asObject(value.projectLimits, run.runId, "projectLimits");
|
|
210
210
|
assertExactKeys(limitsValue, PROJECT_LIMIT_REQUIRED, PROJECT_LIMIT_OPTIONAL, run.runId, "projectLimits");
|
|
211
|
-
const projectLimits:
|
|
211
|
+
const projectLimits: KxmRunPlanEnvelope["projectLimits"] = {
|
|
212
212
|
maxConcurrentRuns: asCount(limitsValue.maxConcurrentRuns, run.runId, "projectLimits.maxConcurrentRuns"),
|
|
213
213
|
...(limitsValue.maxRunDurationMs !== undefined ? { maxRunDurationMs: asDuration(limitsValue.maxRunDurationMs, run.runId, "projectLimits.maxRunDurationMs") } : {}),
|
|
214
214
|
...(limitsValue.maxAgentTimeMs !== undefined ? { maxAgentTimeMs: asDuration(limitsValue.maxAgentTimeMs, run.runId, "projectLimits.maxAgentTimeMs") } : {}),
|
|
215
215
|
};
|
|
216
|
-
const plan =
|
|
216
|
+
const plan = freezeKxmCompiledPlan(parseCompiledPlan(value.plan, run.runId));
|
|
217
217
|
return {
|
|
218
|
-
schema:
|
|
218
|
+
schema: KXM_RUN_PLAN_SCHEMA,
|
|
219
219
|
runId,
|
|
220
220
|
projectId,
|
|
221
221
|
homeRuntimeId,
|
|
@@ -227,7 +227,7 @@ function parseEnvelope(parsed: unknown, run: VnextRunRecord): VnextRunPlanEnvelo
|
|
|
227
227
|
};
|
|
228
228
|
}
|
|
229
229
|
|
|
230
|
-
function parseGates(raw: unknown, plan:
|
|
230
|
+
function parseGates(raw: unknown, plan: KxmCompiledPlan, runId: string): KxmPinnedGates {
|
|
231
231
|
const value = asObject(raw, runId, "gates");
|
|
232
232
|
assertExactKeys(value, GATES_REQUIRED, [], runId, "gates");
|
|
233
233
|
const referenced = new Set<string>();
|
|
@@ -251,14 +251,14 @@ function parseGates(raw: unknown, plan: VnextCompiledPlan, runId: string): Vnext
|
|
|
251
251
|
throw runtimeError("run_plan_corrupt", runId, `definition ${gateId} is not referenced`);
|
|
252
252
|
}
|
|
253
253
|
}
|
|
254
|
-
const definitions = Object.create(null) as Record<string,
|
|
254
|
+
const definitions = Object.create(null) as Record<string, KxmGateDefinition>;
|
|
255
255
|
for (const gateId of defined) {
|
|
256
256
|
definitions[gateId] = parseGateDefinition(definitionsValue[gateId], gateId, runId);
|
|
257
257
|
}
|
|
258
258
|
return { registry, definitions, controlRoot };
|
|
259
259
|
}
|
|
260
260
|
|
|
261
|
-
function parseRegistryPin(raw: unknown, runId: string, required: boolean):
|
|
261
|
+
function parseRegistryPin(raw: unknown, runId: string, required: boolean): KxmPinnedGates["registry"] {
|
|
262
262
|
if (raw === null) {
|
|
263
263
|
if (required) throw runtimeError("run_plan_corrupt", runId, "gate steps require a non-null registry pin");
|
|
264
264
|
return null;
|
|
@@ -275,7 +275,7 @@ function parseRegistryPin(raw: unknown, runId: string, required: boolean): Vnext
|
|
|
275
275
|
return { schema: GATE_REGISTRY_SCHEMA, hash };
|
|
276
276
|
}
|
|
277
277
|
|
|
278
|
-
function parseControlRoot(raw: unknown, runId: string):
|
|
278
|
+
function parseControlRoot(raw: unknown, runId: string): KxmPinnedGates["controlRoot"] {
|
|
279
279
|
const value = asObject(raw, runId, "gates.controlRoot");
|
|
280
280
|
assertExactKeys(value, CONTROL_ROOT_REQUIRED, [], runId, "gates.controlRoot");
|
|
281
281
|
const repositoryId = asString(value.repositoryId, runId, "gates.controlRoot.repositoryId");
|
|
@@ -285,7 +285,7 @@ function parseControlRoot(raw: unknown, runId: string): VnextPinnedGates["contro
|
|
|
285
285
|
return { repositoryId: "control", projectKey: asString(value.projectKey, runId, "gates.controlRoot.projectKey") };
|
|
286
286
|
}
|
|
287
287
|
|
|
288
|
-
export function parseGateDefinition(raw: unknown, gateId: string, runId: string):
|
|
288
|
+
export function parseGateDefinition(raw: unknown, gateId: string, runId: string): KxmGateDefinition {
|
|
289
289
|
const value = asObject(raw, runId, `gates.definitions.${gateId}`);
|
|
290
290
|
const kind = asString(value.kind, runId, `gates.definitions.${gateId}.kind`);
|
|
291
291
|
if (kind === "command") {
|
|
@@ -327,7 +327,7 @@ export function parseGateDefinition(raw: unknown, gateId: string, runId: string)
|
|
|
327
327
|
throw runtimeError("run_plan_corrupt", runId, `gates.definitions.${gateId}.paths are not unique`);
|
|
328
328
|
}
|
|
329
329
|
for (const path of paths) {
|
|
330
|
-
if (path === "." || !
|
|
330
|
+
if (path === "." || !kxmPortablePath(path) || path.startsWith("/") || path.includes("\\")) {
|
|
331
331
|
throw runtimeError("run_plan_corrupt", runId, `gates.definitions.${gateId} path is not a portable relative path`);
|
|
332
332
|
}
|
|
333
333
|
}
|
|
@@ -340,10 +340,10 @@ export function parseGateDefinition(raw: unknown, gateId: string, runId: string)
|
|
|
340
340
|
throw runtimeError("run_plan_corrupt", runId, `gates.definitions.${gateId} kind is invalid`);
|
|
341
341
|
}
|
|
342
342
|
|
|
343
|
-
function freezeGates(gates:
|
|
344
|
-
const definitions = Object.create(null) as Record<string,
|
|
343
|
+
function freezeGates(gates: KxmPinnedGates): KxmPinnedGates {
|
|
344
|
+
const definitions = Object.create(null) as Record<string, KxmGateDefinition>;
|
|
345
345
|
for (const [id, definition] of Object.entries(gates.definitions)) {
|
|
346
|
-
definitions[id] = deepFreeze({ ...definition, ...(definition.kind === "command" ? { argv: [...definition.argv] } : {}), ...(definition.kind === "artifacts-exist" ? { paths: [...definition.paths] } : {}) } as
|
|
346
|
+
definitions[id] = deepFreeze({ ...definition, ...(definition.kind === "command" ? { argv: [...definition.argv] } : {}), ...(definition.kind === "artifacts-exist" ? { paths: [...definition.paths] } : {}) } as KxmGateDefinition);
|
|
347
347
|
}
|
|
348
348
|
return deepFreeze({
|
|
349
349
|
registry: gates.registry ? { ...gates.registry } : null,
|
|
@@ -352,10 +352,10 @@ function freezeGates(gates: VnextPinnedGates): VnextPinnedGates {
|
|
|
352
352
|
});
|
|
353
353
|
}
|
|
354
354
|
|
|
355
|
-
function parseCompiledPlan(raw: unknown, runId: string):
|
|
355
|
+
function parseCompiledPlan(raw: unknown, runId: string): KxmCompiledPlan {
|
|
356
356
|
const value = asObject(raw, runId, "compiled plan");
|
|
357
357
|
assertExactKeys(value, PLAN_REQUIRED, PLAN_OPTIONAL, runId, "compiled plan");
|
|
358
|
-
if (value.schema !==
|
|
358
|
+
if (value.schema !== KXM_COMPILED_WORKFLOW_SCHEMA) {
|
|
359
359
|
throw runtimeError("run_plan_corrupt", runId, "compiled plan schema is not kxm.compiled-workflow.v1");
|
|
360
360
|
}
|
|
361
361
|
const order = asStringArray(value.order, runId, "order");
|
|
@@ -364,7 +364,7 @@ function parseCompiledPlan(raw: unknown, runId: string): VnextCompiledPlan {
|
|
|
364
364
|
if (!sameSet(stepIds, order)) {
|
|
365
365
|
throw runtimeError("run_plan_corrupt", runId, "compiled plan step keys do not equal order");
|
|
366
366
|
}
|
|
367
|
-
const steps = Object.create(null) as Record<string,
|
|
367
|
+
const steps = Object.create(null) as Record<string, KxmCompiledStep>;
|
|
368
368
|
for (const stepId of order) {
|
|
369
369
|
steps[stepId] = parseStep(stepsValue[stepId], stepId, runId);
|
|
370
370
|
}
|
|
@@ -374,7 +374,7 @@ function parseCompiledPlan(raw: unknown, runId: string): VnextCompiledPlan {
|
|
|
374
374
|
}
|
|
375
375
|
const limitsObject = asObject(value.limits, runId, "limits");
|
|
376
376
|
assertExactKeys(limitsObject, [], LIMIT_OPTIONAL, runId, "limits");
|
|
377
|
-
const limits:
|
|
377
|
+
const limits: KxmCompiledPlan["limits"] = {
|
|
378
378
|
...(limitsObject.maxTransitions !== undefined ? { maxTransitions: asCount(limitsObject.maxTransitions, runId, "limits.maxTransitions") } : {}),
|
|
379
379
|
...(limitsObject.maxRunDurationMs !== undefined ? { maxRunDurationMs: asDuration(limitsObject.maxRunDurationMs, runId, "limits.maxRunDurationMs") } : {}),
|
|
380
380
|
...(limitsObject.maxAgentTimeMs !== undefined ? { maxAgentTimeMs: asDuration(limitsObject.maxAgentTimeMs, runId, "limits.maxAgentTimeMs") } : {}),
|
|
@@ -385,8 +385,8 @@ function parseCompiledPlan(raw: unknown, runId: string): VnextCompiledPlan {
|
|
|
385
385
|
for (const stageId of requirePlanHash) {
|
|
386
386
|
if (!steps[stageId]) throw runtimeError("run_plan_corrupt", runId, `requirePlanHash references unknown step ${stageId}`);
|
|
387
387
|
}
|
|
388
|
-
const plan:
|
|
389
|
-
schema:
|
|
388
|
+
const plan: KxmCompiledPlan = {
|
|
389
|
+
schema: KXM_COMPILED_WORKFLOW_SCHEMA,
|
|
390
390
|
workflowId: asString(value.workflowId, runId, "workflowId"),
|
|
391
391
|
...(value.sourcePath !== undefined ? { sourcePath: asStringAllowEmpty(value.sourcePath, runId, "sourcePath") } : {}),
|
|
392
392
|
coordinator: asString(value.coordinator, runId, "coordinator"),
|
|
@@ -403,7 +403,7 @@ function parseCompiledPlan(raw: unknown, runId: string): VnextCompiledPlan {
|
|
|
403
403
|
return plan;
|
|
404
404
|
}
|
|
405
405
|
|
|
406
|
-
function parseStep(raw: unknown, stepId: string, runId: string):
|
|
406
|
+
function parseStep(raw: unknown, stepId: string, runId: string): KxmCompiledStep {
|
|
407
407
|
const value = asObject(raw, runId, `step ${stepId}`);
|
|
408
408
|
const kind = asString(value.kind, runId, `${stepId}.kind`);
|
|
409
409
|
if (!SUPPORTED_KINDS.has(kind)) {
|
|
@@ -431,17 +431,17 @@ function parseStep(raw: unknown, stepId: string, runId: string): VnextCompiledSt
|
|
|
431
431
|
if (!sameSet(Object.keys(transitionsValue), outcomes)) {
|
|
432
432
|
throw runtimeError("run_plan_corrupt", runId, `step ${stepId} transition keys do not equal outcomes`);
|
|
433
433
|
}
|
|
434
|
-
const transitions = Object.create(null) as Record<string,
|
|
434
|
+
const transitions = Object.create(null) as Record<string, KxmCompiledTransition>;
|
|
435
435
|
for (const outcome of outcomes) {
|
|
436
436
|
transitions[outcome] = parseTransition(transitionsValue[outcome], stepId, outcome, runId);
|
|
437
437
|
}
|
|
438
438
|
const repositoriesValue = asObject(value.repositories, runId, `${stepId}.repositories`);
|
|
439
|
-
const repositories = Object.create(null) as Record<string,
|
|
439
|
+
const repositories = Object.create(null) as Record<string, KxmRepositoryAccess>;
|
|
440
440
|
for (const [repositoryId, access] of Object.entries(repositoriesValue)) {
|
|
441
441
|
if (typeof access !== "string" || !REPOSITORY_ACCESS.has(access)) {
|
|
442
442
|
throw runtimeError("run_plan_corrupt", runId, `step ${stepId} repository ${repositoryId} has invalid access`);
|
|
443
443
|
}
|
|
444
|
-
repositories[repositoryId] = access as
|
|
444
|
+
repositories[repositoryId] = access as KxmRepositoryAccess;
|
|
445
445
|
}
|
|
446
446
|
const base = {
|
|
447
447
|
id,
|
|
@@ -480,7 +480,7 @@ function parseStep(raw: unknown, stepId: string, runId: string): VnextCompiledSt
|
|
|
480
480
|
return { ...base, kind: "approval" };
|
|
481
481
|
}
|
|
482
482
|
|
|
483
|
-
function parseTransition(raw: unknown, stepId: string, outcome: string, runId: string):
|
|
483
|
+
function parseTransition(raw: unknown, stepId: string, outcome: string, runId: string): KxmCompiledTransition {
|
|
484
484
|
const value = asObject(raw, runId, `${stepId}.${outcome}`);
|
|
485
485
|
const to = asString(value.to, runId, `${stepId}.${outcome}.to`);
|
|
486
486
|
const maxTransitions = value.maxTransitions !== undefined
|
|
@@ -514,7 +514,7 @@ function parseTransition(raw: unknown, stepId: string, outcome: string, runId: s
|
|
|
514
514
|
throw runtimeError("run_plan_corrupt", runId, `step ${stepId} transition ${outcome} is not a discriminated transition`);
|
|
515
515
|
}
|
|
516
516
|
|
|
517
|
-
function parseAssignments(raw: unknown, stepId: string, runId: string):
|
|
517
|
+
function parseAssignments(raw: unknown, stepId: string, runId: string): KxmCompiledAssignments {
|
|
518
518
|
const value = asObject(raw, runId, `${stepId}.assignments`);
|
|
519
519
|
assertExactKeys(value, ASSIGNMENT_REQUIRED, ASSIGNMENT_OPTIONAL, runId, `${stepId}.assignments`);
|
|
520
520
|
const distinctBy = asStringArray(value.distinctBy, runId, `${stepId}.assignments.distinctBy`);
|
|
@@ -535,7 +535,7 @@ function parseAssignments(raw: unknown, stepId: string, runId: string): VnextCom
|
|
|
535
535
|
};
|
|
536
536
|
}
|
|
537
537
|
|
|
538
|
-
function parseJoin(raw: unknown, stepId: string, runId: string):
|
|
538
|
+
function parseJoin(raw: unknown, stepId: string, runId: string): KxmCompiledJoin {
|
|
539
539
|
const value = asObject(raw, runId, `${stepId}.join`);
|
|
540
540
|
assertExactKeys(value, JOIN_REQUIRED, JOIN_OPTIONAL, runId, `${stepId}.join`);
|
|
541
541
|
const strategy = asString(value.strategy, runId, `${stepId}.join.strategy`);
|
|
@@ -543,13 +543,13 @@ function parseJoin(raw: unknown, stepId: string, runId: string): VnextCompiledJo
|
|
|
543
543
|
throw runtimeError("run_plan_corrupt", runId, `step ${stepId} join strategy is invalid`);
|
|
544
544
|
}
|
|
545
545
|
return {
|
|
546
|
-
strategy: strategy as
|
|
546
|
+
strategy: strategy as KxmCompiledJoin["strategy"],
|
|
547
547
|
...(value.minimumPassed !== undefined ? { minimumPassed: asCount(value.minimumPassed, runId, `${stepId}.join.minimumPassed`) } : {}),
|
|
548
548
|
...(value.cancelRemaining !== undefined ? { cancelRemaining: asBoolean(value.cancelRemaining, runId, `${stepId}.join.cancelRemaining`) } : {}),
|
|
549
549
|
};
|
|
550
550
|
}
|
|
551
551
|
|
|
552
|
-
function parseEvidence(raw: unknown, stepId: string, index: number, runId: string):
|
|
552
|
+
function parseEvidence(raw: unknown, stepId: string, index: number, runId: string): KxmCompiledEvidence {
|
|
553
553
|
const value = asObject(raw, runId, `${stepId}.requiredEvidence[${index}]`);
|
|
554
554
|
assertExactKeys(value, EVIDENCE_REQUIRED, EVIDENCE_OPTIONAL, runId, `${stepId}.requiredEvidence[${index}]`);
|
|
555
555
|
const kind = asString(value.kind, runId, `${stepId}.requiredEvidence[${index}].kind`);
|
|
@@ -558,14 +558,14 @@ function parseEvidence(raw: unknown, stepId: string, index: number, runId: strin
|
|
|
558
558
|
}
|
|
559
559
|
return {
|
|
560
560
|
key: asString(value.key, runId, `${stepId}.requiredEvidence[${index}].key`),
|
|
561
|
-
kind: kind as
|
|
561
|
+
kind: kind as KxmCompiledEvidence["kind"],
|
|
562
562
|
minimum: asCount(value.minimum, runId, `${stepId}.requiredEvidence[${index}].minimum`),
|
|
563
563
|
reusableAcrossAttempts: asBoolean(value.reusableAcrossAttempts, runId, `${stepId}.requiredEvidence[${index}].reusableAcrossAttempts`),
|
|
564
564
|
...(value.producerPolicy !== undefined ? { producerPolicy: parseProducerPolicy(value.producerPolicy, stepId, runId) } : {}),
|
|
565
565
|
};
|
|
566
566
|
}
|
|
567
567
|
|
|
568
|
-
function parseProducerPolicy(raw: unknown, stepId: string, runId: string): NonNullable<
|
|
568
|
+
function parseProducerPolicy(raw: unknown, stepId: string, runId: string): NonNullable<KxmCompiledEvidence["producerPolicy"]> {
|
|
569
569
|
const value = asObject(raw, runId, `${stepId}.producerPolicy`);
|
|
570
570
|
assertExactKeys(value, ["minimumProducers", "eligibleAgents", "acceptedStatuses"], ["degradation"], runId, `${stepId}.producerPolicy`);
|
|
571
571
|
const accepted = value.acceptedStatuses;
|
|
@@ -588,7 +588,7 @@ function parseProducerPolicy(raw: unknown, stepId: string, runId: string): NonNu
|
|
|
588
588
|
|
|
589
589
|
function parseOracle(
|
|
590
590
|
raw: unknown,
|
|
591
|
-
steps: Readonly<Record<string,
|
|
591
|
+
steps: Readonly<Record<string, KxmCompiledStep>>,
|
|
592
592
|
runId: string,
|
|
593
593
|
label: string,
|
|
594
594
|
): { stageId: string; evidenceKey: string } {
|