@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
|
@@ -3,50 +3,50 @@ import { existsSync, readdirSync, readFileSync } from "node:fs";
|
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
import type { ContextItem } from "./context.ts";
|
|
5
5
|
import {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
KxmConfigError,
|
|
7
|
+
loadKxmProject,
|
|
8
|
+
kxmCanonicalJson,
|
|
9
9
|
type JsonObject,
|
|
10
10
|
type JsonValue,
|
|
11
|
-
type
|
|
12
|
-
type
|
|
13
|
-
} from "./
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
11
|
+
type KxmConfigOptions,
|
|
12
|
+
type KxmProjectBundle,
|
|
13
|
+
} from "./project-config.ts";
|
|
14
|
+
import { foldKxmRunState, isTerminalRunStatus, KXM_RUN_STATE_SCHEMA, kxmFoldPanelAttemptIds, type KxmRunState } from "./engine-fold.ts";
|
|
15
|
+
import { verifyKxmGateEvidence } from "./engine-evidence.ts";
|
|
16
|
+
import { loadKxmRunPlanEnvelope } from "./engine-plan.ts";
|
|
17
17
|
import {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
} from "./
|
|
18
|
+
registerKxmRuntimeHandle,
|
|
19
|
+
resolveKxmGateHold,
|
|
20
|
+
unregisterKxmRuntimeHandle,
|
|
21
|
+
kxmAttemptControllers,
|
|
22
|
+
} from "./runtime-owner.ts";
|
|
23
23
|
import {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
24
|
+
KXM_ABSENT_MEMORY_REVISION,
|
|
25
|
+
KXM_RUN_EVENT_SCHEMA,
|
|
26
|
+
KxmRunEventStore,
|
|
27
|
+
KxmRuntimeRegistry,
|
|
28
|
+
newKxmCommandId,
|
|
29
|
+
newKxmEventId,
|
|
30
|
+
newKxmRunId,
|
|
31
31
|
runtimeError,
|
|
32
|
-
|
|
33
|
-
type
|
|
34
|
-
type
|
|
35
|
-
type
|
|
36
|
-
} from "./
|
|
32
|
+
kxmRuntimePaths,
|
|
33
|
+
type KxmRunEvent,
|
|
34
|
+
type KxmRunRecord,
|
|
35
|
+
type KxmRunStatus,
|
|
36
|
+
} from "./runtime-store.ts";
|
|
37
37
|
|
|
38
38
|
/* ------------------------------------------------------------------ *
|
|
39
39
|
* Policy revisions derived deterministically from the bundle
|
|
40
40
|
* ------------------------------------------------------------------ */
|
|
41
41
|
|
|
42
|
-
export interface
|
|
42
|
+
export interface KxmPolicyRevisions {
|
|
43
43
|
configRevision: string;
|
|
44
44
|
memoryRevision: string;
|
|
45
45
|
executorPolicyRevision: string;
|
|
46
46
|
toolPolicyRevision: string;
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
-
export interface
|
|
49
|
+
export interface KxmMemoryRevisionOptions {
|
|
50
50
|
promotedState?: readonly ContextItem[];
|
|
51
51
|
memoryDir?: string;
|
|
52
52
|
skillsDir?: string;
|
|
@@ -65,7 +65,7 @@ function objectValue(value: JsonValue | undefined): JsonObject | undefined {
|
|
|
65
65
|
}
|
|
66
66
|
|
|
67
67
|
/** Executor policy: the union of executor identities referenced anywhere in the bundle. */
|
|
68
|
-
export function
|
|
68
|
+
export function kxmExecutorPolicyRevision(bundle: KxmProjectBundle): string {
|
|
69
69
|
const executors = new Set<string>();
|
|
70
70
|
const defaultExecutor = bundle.project.value.defaultExecutor;
|
|
71
71
|
if (typeof defaultExecutor === "string") executors.add(defaultExecutor);
|
|
@@ -73,11 +73,11 @@ export function vnextExecutorPolicyRevision(bundle: VnextProjectBundle): string
|
|
|
73
73
|
const executor = agent.value.executor;
|
|
74
74
|
if (typeof executor === "string") executors.add(executor);
|
|
75
75
|
}
|
|
76
|
-
return sha256Of(
|
|
76
|
+
return sha256Of(kxmCanonicalJson([...executors].sort() as JsonValue));
|
|
77
77
|
}
|
|
78
78
|
|
|
79
79
|
/** Tool policy: canonical agent/step tool ceilings across the bundle. */
|
|
80
|
-
export function
|
|
80
|
+
export function kxmToolPolicyRevision(bundle: KxmProjectBundle): string {
|
|
81
81
|
const policies: JsonObject[] = [];
|
|
82
82
|
for (const agent of [...bundle.agents.values()].sort((left, right) => String(left.id).localeCompare(String(right.id)))) {
|
|
83
83
|
policies.push({ agent: agent.id ?? "unknown", tools: (objectValue(agent.value.tools) ?? null) as JsonValue });
|
|
@@ -91,7 +91,7 @@ export function vnextToolPolicyRevision(bundle: VnextProjectBundle): string {
|
|
|
91
91
|
}
|
|
92
92
|
}
|
|
93
93
|
}
|
|
94
|
-
return sha256Of(
|
|
94
|
+
return sha256Of(kxmCanonicalJson({ policies, gateRegistry: bundle.gateRegistry?.value ?? null }));
|
|
95
95
|
}
|
|
96
96
|
|
|
97
97
|
function collectMemoryFiles(dir: string, baseDir: string, ignoreSubdirs: Set<string> = new Set()): { relPath: string; fullPath: string }[] {
|
|
@@ -116,9 +116,9 @@ function collectMemoryFiles(dir: string, baseDir: string, ignoreSubdirs: Set<str
|
|
|
116
116
|
* Git-authored set (.kxm/memory, excluding candidates; .kxm/skills/promoted) plus
|
|
117
117
|
* the promoted-state snapshot.
|
|
118
118
|
*/
|
|
119
|
-
export function
|
|
120
|
-
bundle:
|
|
121
|
-
options:
|
|
119
|
+
export function computeKxmMemoryRevision(
|
|
120
|
+
bundle: KxmProjectBundle,
|
|
121
|
+
options: KxmMemoryRevisionOptions = {},
|
|
122
122
|
): string {
|
|
123
123
|
const hash = createHash("sha256");
|
|
124
124
|
|
|
@@ -151,7 +151,7 @@ export function computeVnextMemoryRevision(
|
|
|
151
151
|
.sort((left, right) => compareCodeUnits(left.id, right.id));
|
|
152
152
|
for (const item of currentItems) {
|
|
153
153
|
hash.update(`state:${item.id}\0`, "utf8");
|
|
154
|
-
hash.update(
|
|
154
|
+
hash.update(kxmCanonicalJson(item as unknown as JsonValue), "utf8");
|
|
155
155
|
hash.update("\0", "utf8");
|
|
156
156
|
}
|
|
157
157
|
}
|
|
@@ -161,10 +161,10 @@ export function computeVnextMemoryRevision(
|
|
|
161
161
|
|
|
162
162
|
export function checkMemoryRevisionDrift(
|
|
163
163
|
pinnedRevision: string,
|
|
164
|
-
bundle:
|
|
165
|
-
options?:
|
|
164
|
+
bundle: KxmProjectBundle,
|
|
165
|
+
options?: KxmMemoryRevisionOptions,
|
|
166
166
|
): { drifted: boolean; currentRevision: string; pinnedRevision: string } {
|
|
167
|
-
const currentRevision =
|
|
167
|
+
const currentRevision = computeKxmMemoryRevision(bundle, options);
|
|
168
168
|
return {
|
|
169
169
|
drifted: currentRevision !== pinnedRevision,
|
|
170
170
|
currentRevision,
|
|
@@ -172,30 +172,30 @@ export function checkMemoryRevisionDrift(
|
|
|
172
172
|
};
|
|
173
173
|
}
|
|
174
174
|
|
|
175
|
-
export function
|
|
176
|
-
bundle:
|
|
177
|
-
options?:
|
|
178
|
-
):
|
|
175
|
+
export function kxmPolicyRevisions(
|
|
176
|
+
bundle: KxmProjectBundle,
|
|
177
|
+
options?: KxmMemoryRevisionOptions,
|
|
178
|
+
): KxmPolicyRevisions {
|
|
179
179
|
return {
|
|
180
180
|
configRevision: bundle.configRevision,
|
|
181
|
-
memoryRevision:
|
|
182
|
-
executorPolicyRevision:
|
|
183
|
-
toolPolicyRevision:
|
|
181
|
+
memoryRevision: computeKxmMemoryRevision(bundle, options),
|
|
182
|
+
executorPolicyRevision: kxmExecutorPolicyRevision(bundle),
|
|
183
|
+
toolPolicyRevision: kxmToolPolicyRevision(bundle),
|
|
184
184
|
};
|
|
185
185
|
}
|
|
186
186
|
|
|
187
|
-
export function
|
|
187
|
+
export function kxmDeclaredRepositoryIds(bundle: KxmProjectBundle): string[] {
|
|
188
188
|
return [...bundle.repositories.keys()].sort();
|
|
189
189
|
}
|
|
190
190
|
|
|
191
|
-
export function
|
|
191
|
+
export function kxmDeclaredExecutorIds(bundle: KxmProjectBundle): string[] {
|
|
192
192
|
return [...new Set([
|
|
193
193
|
typeof bundle.project.value.defaultExecutor === "string" ? bundle.project.value.defaultExecutor : undefined,
|
|
194
194
|
...[...bundle.agents.values()].map((agent) => agent.value.executor).filter((value): value is string => typeof value === "string"),
|
|
195
195
|
].filter((value): value is string => value !== undefined))].sort();
|
|
196
196
|
}
|
|
197
197
|
|
|
198
|
-
export function
|
|
198
|
+
export function kxmProjectAdmissionLimits(bundle: KxmProjectBundle): {
|
|
199
199
|
maxConcurrentRuns: number;
|
|
200
200
|
maxRunDurationMs?: number;
|
|
201
201
|
maxAgentTimeMs?: number;
|
|
@@ -215,35 +215,35 @@ export function vnextProjectAdmissionLimits(bundle: VnextProjectBundle): {
|
|
|
215
215
|
* Run acceptance and lifecycle
|
|
216
216
|
* ------------------------------------------------------------------ */
|
|
217
217
|
|
|
218
|
-
export interface
|
|
218
|
+
export interface KxmRunAcceptanceRequest {
|
|
219
219
|
commandId?: string;
|
|
220
220
|
workflowId: string;
|
|
221
221
|
prompt: string;
|
|
222
222
|
}
|
|
223
223
|
|
|
224
|
-
export interface
|
|
224
|
+
export interface KxmRunAcceptance {
|
|
225
225
|
accepted: boolean;
|
|
226
226
|
idempotent: boolean;
|
|
227
|
-
run:
|
|
228
|
-
event:
|
|
227
|
+
run: KxmRunRecord;
|
|
228
|
+
event: KxmRunEvent;
|
|
229
229
|
}
|
|
230
230
|
|
|
231
|
-
export interface
|
|
232
|
-
registry:
|
|
231
|
+
export interface KxmRuntimeContext {
|
|
232
|
+
registry: KxmRuntimeRegistry;
|
|
233
233
|
projectRoot: string;
|
|
234
234
|
projectId: string;
|
|
235
235
|
homeRuntimeId: string;
|
|
236
|
-
eventStore:
|
|
236
|
+
eventStore: KxmRunEventStore;
|
|
237
237
|
}
|
|
238
238
|
|
|
239
|
-
const closedRuntimeContexts = new WeakSet<
|
|
240
|
-
const runtimeCloseHooks = new WeakMap<
|
|
239
|
+
const closedRuntimeContexts = new WeakSet<KxmRuntimeContext>();
|
|
240
|
+
const runtimeCloseHooks = new WeakMap<KxmRuntimeContext, Set<() => void>>();
|
|
241
241
|
|
|
242
|
-
export function
|
|
242
|
+
export function isKxmRuntimeContextClosed(context: KxmRuntimeContext): boolean {
|
|
243
243
|
return closedRuntimeContexts.has(context);
|
|
244
244
|
}
|
|
245
245
|
|
|
246
|
-
export function
|
|
246
|
+
export function registerKxmRuntimeCloseHook(context: KxmRuntimeContext, hook: () => void): () => void {
|
|
247
247
|
if (closedRuntimeContexts.has(context)) {
|
|
248
248
|
hook();
|
|
249
249
|
return () => undefined;
|
|
@@ -265,14 +265,14 @@ export function registerVnextRuntimeCloseHook(context: VnextRuntimeContext, hook
|
|
|
265
265
|
* active runtime id; direct callers must supply one explicitly. An existing
|
|
266
266
|
* registration always wins and is revalidated immutably.
|
|
267
267
|
*/
|
|
268
|
-
export function
|
|
268
|
+
export function openKxmRuntimeContext(
|
|
269
269
|
projectRoot: string,
|
|
270
|
-
options:
|
|
271
|
-
):
|
|
272
|
-
const paths =
|
|
273
|
-
const registry = new
|
|
270
|
+
options: KxmConfigOptions & { stateRoot?: string; now?: string; homeRuntimeId: string },
|
|
271
|
+
): KxmRuntimeContext {
|
|
272
|
+
const paths = kxmRuntimePaths(options.stateRoot !== undefined ? { stateRoot: options.stateRoot } : {});
|
|
273
|
+
const registry = new KxmRuntimeRegistry(paths.registryDb);
|
|
274
274
|
try {
|
|
275
|
-
const bundle =
|
|
275
|
+
const bundle = loadKxmProject(projectRoot, options);
|
|
276
276
|
const projectId = String(bundle.project.value.id);
|
|
277
277
|
const registration = registry.registerProject({
|
|
278
278
|
projectId,
|
|
@@ -281,8 +281,8 @@ export function openVnextRuntimeContext(
|
|
|
281
281
|
configRevision: bundle.configRevision,
|
|
282
282
|
now: options.now ?? new Date().toISOString(),
|
|
283
283
|
});
|
|
284
|
-
const eventStore = new
|
|
285
|
-
|
|
284
|
+
const eventStore = new KxmRunEventStore(join(paths.projectsDir, registration.projectKey, "run-events.db"));
|
|
285
|
+
registerKxmRuntimeHandle(eventStore.path);
|
|
286
286
|
return {
|
|
287
287
|
registry,
|
|
288
288
|
projectRoot: bundle.projectRoot,
|
|
@@ -296,7 +296,7 @@ export function openVnextRuntimeContext(
|
|
|
296
296
|
}
|
|
297
297
|
}
|
|
298
298
|
|
|
299
|
-
export function
|
|
299
|
+
export function closeKxmRuntimeContext(context: KxmRuntimeContext): void {
|
|
300
300
|
if (closedRuntimeContexts.has(context)) return;
|
|
301
301
|
closedRuntimeContexts.add(context);
|
|
302
302
|
const hooks = runtimeCloseHooks.get(context);
|
|
@@ -310,7 +310,7 @@ export function closeVnextRuntimeContext(context: VnextRuntimeContext): void {
|
|
|
310
310
|
}
|
|
311
311
|
}
|
|
312
312
|
}
|
|
313
|
-
|
|
313
|
+
unregisterKxmRuntimeHandle(context.eventStore.path);
|
|
314
314
|
context.eventStore.close();
|
|
315
315
|
context.registry.close();
|
|
316
316
|
}
|
|
@@ -318,17 +318,17 @@ export function closeVnextRuntimeContext(context: VnextRuntimeContext): void {
|
|
|
318
318
|
const RUNTIME_EPOCH_NS = process.hrtime.bigint();
|
|
319
319
|
|
|
320
320
|
/** Intra-process monotonic nanoseconds as a decimal string, ordered only by sequence. */
|
|
321
|
-
export function
|
|
321
|
+
export function kxmMonotonicNs(): string {
|
|
322
322
|
return (process.hrtime.bigint() - RUNTIME_EPOCH_NS).toString();
|
|
323
323
|
}
|
|
324
324
|
|
|
325
|
-
export function
|
|
325
|
+
export function kxmIncrementMonotonicNs(value: string): string {
|
|
326
326
|
return (BigInt(value) + 1n).toString();
|
|
327
327
|
}
|
|
328
328
|
|
|
329
|
-
export function
|
|
329
|
+
export function kxmEventBase(context: KxmRuntimeContext, run: KxmRunRecord, now: string, monotonicNs: string, commandId?: string): Omit<KxmRunEvent, "eventId" | "eventType" | "sequence" | "payload"> {
|
|
330
330
|
return {
|
|
331
|
-
schema:
|
|
331
|
+
schema: KXM_RUN_EVENT_SCHEMA,
|
|
332
332
|
projectId: context.projectId,
|
|
333
333
|
runId: run.runId,
|
|
334
334
|
homeRuntimeId: run.homeRuntimeId,
|
|
@@ -343,7 +343,7 @@ export function vnextEventBase(context: VnextRuntimeContext, run: VnextRunRecord
|
|
|
343
343
|
};
|
|
344
344
|
}
|
|
345
345
|
|
|
346
|
-
export interface
|
|
346
|
+
export interface KxmRunAcceptanceOptions {
|
|
347
347
|
now?: string;
|
|
348
348
|
monotonicNs?: string;
|
|
349
349
|
memoryRevision?: string;
|
|
@@ -353,21 +353,21 @@ export interface VnextRunAcceptanceOptions {
|
|
|
353
353
|
}
|
|
354
354
|
|
|
355
355
|
/** Accept a run idempotently: a repeated commandId returns the prior acceptance. */
|
|
356
|
-
export function
|
|
357
|
-
context:
|
|
358
|
-
bundle:
|
|
359
|
-
request:
|
|
360
|
-
options:
|
|
361
|
-
):
|
|
362
|
-
const commandId = request.commandId ??
|
|
356
|
+
export function acceptKxmRun(
|
|
357
|
+
context: KxmRuntimeContext,
|
|
358
|
+
bundle: KxmProjectBundle,
|
|
359
|
+
request: KxmRunAcceptanceRequest,
|
|
360
|
+
options: KxmRunAcceptanceOptions = {},
|
|
361
|
+
): KxmRunAcceptance {
|
|
362
|
+
const commandId = request.commandId ?? newKxmCommandId();
|
|
363
363
|
const now = options.now ?? new Date().toISOString();
|
|
364
|
-
const monotonicNs = options.monotonicNs ??
|
|
364
|
+
const monotonicNs = options.monotonicNs ?? kxmMonotonicNs();
|
|
365
365
|
|
|
366
366
|
const workflow = bundle.workflows.get(request.workflowId);
|
|
367
367
|
if (!workflow) {
|
|
368
368
|
throw runtimeError("run_workflow_unknown", ".kxm/workflows", `workflow ${request.workflowId} does not exist in this project`);
|
|
369
369
|
}
|
|
370
|
-
const revisions =
|
|
370
|
+
const revisions = kxmPolicyRevisions(bundle, options);
|
|
371
371
|
const memoryRevision = options.memoryRevision ?? revisions.memoryRevision;
|
|
372
372
|
const promptSha256 = `sha256:${createHash("sha256").update(request.prompt, "utf8").digest("hex")}`;
|
|
373
373
|
|
|
@@ -387,17 +387,17 @@ export function acceptVnextRun(
|
|
|
387
387
|
return { accepted: true, idempotent: true, run, event: events[0]! };
|
|
388
388
|
}
|
|
389
389
|
|
|
390
|
-
const runId =
|
|
390
|
+
const runId = newKxmRunId();
|
|
391
391
|
const payload: Record<string, unknown> = {
|
|
392
392
|
workflowId: request.workflowId,
|
|
393
393
|
status: "created",
|
|
394
394
|
promptHash: promptSha256,
|
|
395
|
-
repositoryIds:
|
|
396
|
-
executorIds:
|
|
395
|
+
repositoryIds: kxmDeclaredRepositoryIds(bundle),
|
|
396
|
+
executorIds: kxmDeclaredExecutorIds(bundle),
|
|
397
397
|
};
|
|
398
|
-
const event:
|
|
399
|
-
schema:
|
|
400
|
-
eventId:
|
|
398
|
+
const event: KxmRunEvent = {
|
|
399
|
+
schema: KXM_RUN_EVENT_SCHEMA,
|
|
400
|
+
eventId: newKxmEventId(),
|
|
401
401
|
eventType: "run.created",
|
|
402
402
|
projectId: context.projectId,
|
|
403
403
|
runId,
|
|
@@ -413,7 +413,7 @@ export function acceptVnextRun(
|
|
|
413
413
|
toolPolicyRevision: revisions.toolPolicyRevision,
|
|
414
414
|
payload,
|
|
415
415
|
};
|
|
416
|
-
const run:
|
|
416
|
+
const run: KxmRunRecord = {
|
|
417
417
|
runId,
|
|
418
418
|
projectId: context.projectId,
|
|
419
419
|
homeRuntimeId: context.homeRuntimeId,
|
|
@@ -445,14 +445,14 @@ export function acceptVnextRun(
|
|
|
445
445
|
* Projection
|
|
446
446
|
* ------------------------------------------------------------------ */
|
|
447
447
|
|
|
448
|
-
export function
|
|
448
|
+
export function foldStoredKxmRun(context: KxmRuntimeContext, run: KxmRunRecord): KxmRunState {
|
|
449
449
|
const events = context.eventStore.events(run.runId, 0, 1_000_000);
|
|
450
450
|
const planRow = context.eventStore.runPlan(run.runId);
|
|
451
451
|
if (!planRow && events.some((event) => event.eventType.startsWith("step.") || event.eventType.startsWith("effect.") || event.payload.status === "preparing" || event.payload.status === "running" || event.payload.status === "cancelling")) {
|
|
452
452
|
throw runtimeError("run_plan_missing", run.runId, "run reached preparing or later without a pinned plan");
|
|
453
453
|
}
|
|
454
|
-
const envelope = planRow ?
|
|
455
|
-
const state =
|
|
454
|
+
const envelope = planRow ? loadKxmRunPlanEnvelope(context.eventStore, run) : undefined;
|
|
455
|
+
const state = foldKxmRunState(run, envelope?.plan, events, envelope
|
|
456
456
|
? {
|
|
457
457
|
observationLookup: (attemptId, observationId) => {
|
|
458
458
|
const row = context.eventStore.gateObservationForAttempt(attemptId);
|
|
@@ -467,7 +467,7 @@ export function foldStoredVnextRun(context: VnextRuntimeContext, run: VnextRunRe
|
|
|
467
467
|
projectLimits: envelope.projectLimits,
|
|
468
468
|
}
|
|
469
469
|
: {});
|
|
470
|
-
if (envelope)
|
|
470
|
+
if (envelope) verifyKxmGateEvidence(context.eventStore, run, envelope, events, state);
|
|
471
471
|
assertStoredProjection(context, run);
|
|
472
472
|
return state;
|
|
473
473
|
}
|
|
@@ -481,22 +481,22 @@ function storedProjectionSchema(stateJson: string): string | undefined {
|
|
|
481
481
|
}
|
|
482
482
|
}
|
|
483
483
|
|
|
484
|
-
function assertStoredProjection(context:
|
|
484
|
+
function assertStoredProjection(context: KxmRuntimeContext, run: KxmRunRecord): void {
|
|
485
485
|
const stored = context.eventStore.runState(run.runId);
|
|
486
486
|
if (!stored) return;
|
|
487
487
|
const schema = storedProjectionSchema(stored.state);
|
|
488
|
-
if (schema !==
|
|
488
|
+
if (schema !== KXM_RUN_STATE_SCHEMA) {
|
|
489
489
|
throw runtimeError("run_projection_divergent", run.runId, "stored run_state does not match the folded projection");
|
|
490
490
|
}
|
|
491
491
|
}
|
|
492
492
|
|
|
493
493
|
/** Fold a stored run into its current status with evidence verification. */
|
|
494
|
-
export function
|
|
495
|
-
return
|
|
494
|
+
export function readKxmRunStatus(context: KxmRuntimeContext, run: KxmRunRecord): KxmRunStatus {
|
|
495
|
+
return foldStoredKxmRun(context, run).status;
|
|
496
496
|
}
|
|
497
497
|
|
|
498
|
-
function persistProjection(context:
|
|
499
|
-
const canonical =
|
|
498
|
+
function persistProjection(context: KxmRuntimeContext, run: KxmRunRecord, state: KxmRunState, now: string): KxmRunRecord {
|
|
499
|
+
const canonical = kxmCanonicalJson(state as unknown as JsonValue);
|
|
500
500
|
const stored = context.eventStore.runState(run.runId);
|
|
501
501
|
if (stored && stored.state !== canonical) {
|
|
502
502
|
throw runtimeError("run_projection_divergent", run.runId, "stored run_state does not match the folded projection");
|
|
@@ -514,10 +514,10 @@ function persistProjection(context: VnextRuntimeContext, run: VnextRunRecord, st
|
|
|
514
514
|
}
|
|
515
515
|
|
|
516
516
|
/** Rebuild the stored run row from the event log; returns the projected record. */
|
|
517
|
-
export function
|
|
517
|
+
export function rebuildKxmRunProjection(context: KxmRuntimeContext, runId: string): KxmRunRecord {
|
|
518
518
|
const stored = context.eventStore.run(runId);
|
|
519
519
|
if (!stored) throw runtimeError("run_unknown", runId, "run does not exist in this event store");
|
|
520
|
-
const state =
|
|
520
|
+
const state = foldStoredKxmRun(context, stored);
|
|
521
521
|
const events = context.eventStore.events(runId, 0, 1_000_000);
|
|
522
522
|
if (events.length === 0) throw runtimeError("run_events_corrupt", runId, "run has no events");
|
|
523
523
|
const last = events[events.length - 1]!;
|
|
@@ -528,28 +528,28 @@ export function rebuildVnextRunProjection(context: VnextRuntimeContext, runId: s
|
|
|
528
528
|
* persisting. GET paths must not write (no run_state upsert, no status
|
|
529
529
|
* update, no run_projection_divergent throw) — the event log stays the sole
|
|
530
530
|
* source of truth and the folded status is returned as-is. */
|
|
531
|
-
export function
|
|
531
|
+
export function projectKxmRunReadOnly(context: KxmRuntimeContext, runId: string): KxmRunRecord {
|
|
532
532
|
const stored = context.eventStore.run(runId);
|
|
533
533
|
if (!stored) throw runtimeError("run_unknown", runId, "run does not exist in this event store");
|
|
534
|
-
const state =
|
|
534
|
+
const state = foldStoredKxmRun(context, stored);
|
|
535
535
|
const events = context.eventStore.events(runId, 0, 1_000_000);
|
|
536
536
|
if (events.length === 0) throw runtimeError("run_events_corrupt", runId, "run has no events");
|
|
537
537
|
const last = events[events.length - 1]!;
|
|
538
538
|
return { ...stored, status: state.status, updatedAt: last.occurredAt };
|
|
539
539
|
}
|
|
540
540
|
|
|
541
|
-
export function
|
|
541
|
+
export function persistKxmRunState(context: KxmRuntimeContext, runId: string, state: KxmRunState, lastSequence: number): void {
|
|
542
542
|
const stored = context.eventStore.runState(runId);
|
|
543
543
|
if (stored) {
|
|
544
544
|
const schema = storedProjectionSchema(stored.state);
|
|
545
|
-
if (schema !==
|
|
545
|
+
if (schema !== KXM_RUN_STATE_SCHEMA) {
|
|
546
546
|
throw runtimeError("run_projection_divergent", runId, "stored run_state does not match the folded projection");
|
|
547
547
|
}
|
|
548
548
|
}
|
|
549
549
|
context.eventStore.upsertRunState({
|
|
550
550
|
runId,
|
|
551
551
|
lastSequence,
|
|
552
|
-
state:
|
|
552
|
+
state: kxmCanonicalJson(state as unknown as JsonValue),
|
|
553
553
|
});
|
|
554
554
|
}
|
|
555
555
|
|
|
@@ -557,14 +557,14 @@ export function persistVnextRunState(context: VnextRuntimeContext, runId: string
|
|
|
557
557
|
* Cancel
|
|
558
558
|
* ------------------------------------------------------------------ */
|
|
559
559
|
|
|
560
|
-
export interface
|
|
561
|
-
run:
|
|
560
|
+
export interface KxmRunCancelResult {
|
|
561
|
+
run: KxmRunRecord;
|
|
562
562
|
idempotent: boolean;
|
|
563
|
-
events:
|
|
563
|
+
events: KxmRunEvent[];
|
|
564
564
|
}
|
|
565
565
|
|
|
566
|
-
export function
|
|
567
|
-
context:
|
|
566
|
+
export function cancelKxmRun(
|
|
567
|
+
context: KxmRuntimeContext,
|
|
568
568
|
runId: string,
|
|
569
569
|
options: {
|
|
570
570
|
commandId?: string;
|
|
@@ -573,10 +573,10 @@ export function cancelVnextRun(
|
|
|
573
573
|
reason?: string;
|
|
574
574
|
budget?: { budgetMs: number; elapsedMs: number; source: "workflow" | "project" | "both" };
|
|
575
575
|
} = {},
|
|
576
|
-
):
|
|
577
|
-
const commandId = options.commandId ??
|
|
576
|
+
): KxmRunCancelResult {
|
|
577
|
+
const commandId = options.commandId ?? newKxmCommandId();
|
|
578
578
|
const now = options.now ?? new Date().toISOString();
|
|
579
|
-
const monotonicNs = options.monotonicNs ??
|
|
579
|
+
const monotonicNs = options.monotonicNs ?? kxmMonotonicNs();
|
|
580
580
|
const abortControllers: AbortController[] = [];
|
|
581
581
|
|
|
582
582
|
const result = context.eventStore.transaction(() => {
|
|
@@ -587,11 +587,11 @@ export function cancelVnextRun(
|
|
|
587
587
|
}
|
|
588
588
|
const run = context.eventStore.run(prior.runId);
|
|
589
589
|
if (!run) throw runtimeError("run_command_corrupt", commandId, "idempotent command references a missing run");
|
|
590
|
-
return { run, idempotent: true, events: [] as
|
|
590
|
+
return { run, idempotent: true, events: [] as KxmRunEvent[] };
|
|
591
591
|
}
|
|
592
592
|
const run = context.eventStore.run(runId);
|
|
593
593
|
if (!run) throw runtimeError("run_unknown", runId, "run does not exist in this event store");
|
|
594
|
-
const folded =
|
|
594
|
+
const folded = foldStoredKxmRun(context, run);
|
|
595
595
|
if (isTerminalRunStatus(folded.status) || folded.status === "cancelling") {
|
|
596
596
|
context.eventStore.insertCommand({
|
|
597
597
|
commandId,
|
|
@@ -600,27 +600,27 @@ export function cancelVnextRun(
|
|
|
600
600
|
result: { runId, status: folded.status },
|
|
601
601
|
recordedAt: now,
|
|
602
602
|
});
|
|
603
|
-
return { run: { ...run, status: folded.status }, idempotent: true, events: [] as
|
|
603
|
+
return { run: { ...run, status: folded.status }, idempotent: true, events: [] as KxmRunEvent[] };
|
|
604
604
|
}
|
|
605
605
|
|
|
606
|
-
const events:
|
|
606
|
+
const events: KxmRunEvent[] = [];
|
|
607
607
|
let sequence = context.eventStore.nextSequence(runId);
|
|
608
608
|
let nextMono = monotonicNs;
|
|
609
|
-
const push = (eventType: string, payload: Record<string, unknown>):
|
|
610
|
-
const event:
|
|
611
|
-
...
|
|
612
|
-
eventId:
|
|
609
|
+
const push = (eventType: string, payload: Record<string, unknown>): KxmRunEvent => {
|
|
610
|
+
const event: KxmRunEvent = {
|
|
611
|
+
...kxmEventBase(context, run, now, nextMono, commandId),
|
|
612
|
+
eventId: newKxmEventId(),
|
|
613
613
|
eventType,
|
|
614
614
|
sequence: sequence++,
|
|
615
615
|
payload,
|
|
616
616
|
};
|
|
617
|
-
nextMono =
|
|
617
|
+
nextMono = kxmIncrementMonotonicNs(nextMono);
|
|
618
618
|
events.push(event);
|
|
619
619
|
return event;
|
|
620
620
|
};
|
|
621
621
|
|
|
622
622
|
const cancelReason = options.reason ?? "operator_cancel";
|
|
623
|
-
const activeAttempt = Boolean(folded.currentStep?.attemptId) ||
|
|
623
|
+
const activeAttempt = Boolean(folded.currentStep?.attemptId) || kxmFoldPanelAttemptIds(folded.currentStep).length > 0;
|
|
624
624
|
const cancelPayload: Record<string, unknown> = {
|
|
625
625
|
actor: { kind: "runtime", id: context.homeRuntimeId },
|
|
626
626
|
reason: cancelReason,
|
|
@@ -633,11 +633,11 @@ export function cancelVnextRun(
|
|
|
633
633
|
}
|
|
634
634
|
push("run.cancel_requested", cancelPayload);
|
|
635
635
|
|
|
636
|
-
let status:
|
|
636
|
+
let status: KxmRunStatus = "cancelled";
|
|
637
637
|
if (folded.status === "running" && activeAttempt) {
|
|
638
638
|
status = "cancelling";
|
|
639
639
|
push("run.status_changed", { status: "cancelling", reason: cancelReason });
|
|
640
|
-
const attemptIds = new Set(
|
|
640
|
+
const attemptIds = new Set(kxmFoldPanelAttemptIds(folded.currentStep));
|
|
641
641
|
if (folded.currentStep?.attemptId) attemptIds.add(folded.currentStep.attemptId);
|
|
642
642
|
for (const attemptId of attemptIds) {
|
|
643
643
|
const capability = context.eventStore.capabilityByAttempt(attemptId);
|
|
@@ -645,7 +645,7 @@ export function cancelVnextRun(
|
|
|
645
645
|
context.eventStore.settleCapability(attemptId, "revoked");
|
|
646
646
|
}
|
|
647
647
|
}
|
|
648
|
-
for (const owned of
|
|
648
|
+
for (const owned of kxmAttemptControllers(context.eventStore.path, runId)) {
|
|
649
649
|
abortControllers.push(owned.controller);
|
|
650
650
|
}
|
|
651
651
|
} else if (folded.status === "blocked_uncertain") {
|
|
@@ -656,9 +656,9 @@ export function cancelVnextRun(
|
|
|
656
656
|
if (capability && (capability.state === "issued" || capability.state === "revoked")) {
|
|
657
657
|
context.eventStore.settleCapability(attemptId, "revoked");
|
|
658
658
|
}
|
|
659
|
-
|
|
659
|
+
resolveKxmGateHold(context.eventStore.path, runId, attemptId);
|
|
660
660
|
}
|
|
661
|
-
for (const owned of
|
|
661
|
+
for (const owned of kxmAttemptControllers(context.eventStore.path, runId)) {
|
|
662
662
|
abortControllers.push(owned.controller);
|
|
663
663
|
}
|
|
664
664
|
push("run.status_changed", { status: "cancelled", reason: cancelReason });
|
|
@@ -671,8 +671,8 @@ export function cancelVnextRun(
|
|
|
671
671
|
}
|
|
672
672
|
|
|
673
673
|
for (const event of events) context.eventStore.appendEvent(event);
|
|
674
|
-
const nextState =
|
|
675
|
-
|
|
674
|
+
const nextState = foldStoredKxmRun(context, run);
|
|
675
|
+
persistKxmRunState(context, runId, nextState, events[events.length - 1]!.sequence);
|
|
676
676
|
context.eventStore.updateRunStatus(runId, status, now);
|
|
677
677
|
context.eventStore.insertCommand({
|
|
678
678
|
commandId,
|
|
@@ -693,7 +693,7 @@ export function cancelVnextRun(
|
|
|
693
693
|
* ------------------------------------------------------------------ */
|
|
694
694
|
|
|
695
695
|
/** Runs pin revisions at acceptance; drift is reported, never silently adopted. */
|
|
696
|
-
export function
|
|
696
|
+
export function kxmRunRevisionDrift(run: KxmRunRecord, bundle: KxmProjectBundle): { configDrift: boolean; currentRevision: string; pinnedRevision: string } {
|
|
697
697
|
return {
|
|
698
698
|
configDrift: run.configRevision !== bundle.configRevision,
|
|
699
699
|
currentRevision: bundle.configRevision,
|
|
@@ -701,6 +701,6 @@ export function vnextRunRevisionDrift(run: VnextRunRecord, bundle: VnextProjectB
|
|
|
701
701
|
};
|
|
702
702
|
}
|
|
703
703
|
|
|
704
|
-
export function
|
|
705
|
-
if (!(error instanceof
|
|
704
|
+
export function assertKxmConfigError(error: unknown): asserts error is KxmConfigError {
|
|
705
|
+
if (!(error instanceof KxmConfigError)) throw error;
|
|
706
706
|
}
|