@kontextmind/kxm 0.7.40 → 0.7.44
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 +45 -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-hub.mjs +36 -0
- 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
|
@@ -2,24 +2,24 @@ import { createHash, randomUUID } from "node:crypto";
|
|
|
2
2
|
import { existsSync, lstatSync, mkdirSync, readFileSync, realpathSync, writeFileSync } from "node:fs";
|
|
3
3
|
import { dirname, join, resolve } from "node:path";
|
|
4
4
|
import { DatabaseSync } from "./sqlite.ts";
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
5
|
+
import { KxmConfigError, validateDriveReceipt, validateRunEvent, kxmCanonicalJson, type JsonValue, type KxmConfigIssue, type KxmConfigOptions } from "./project-config.ts";
|
|
6
|
+
import { kxmUserStateRoot } from "./bindings.ts";
|
|
7
7
|
|
|
8
8
|
/* ------------------------------------------------------------------ *
|
|
9
9
|
* Runtime registry (per-user, platform state root)
|
|
10
10
|
* ------------------------------------------------------------------ */
|
|
11
11
|
|
|
12
|
-
export interface
|
|
12
|
+
export interface KxmRuntimePaths {
|
|
13
13
|
stateRoot: string;
|
|
14
14
|
runtimeDir: string;
|
|
15
15
|
registryDb: string;
|
|
16
16
|
projectsDir: string;
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
export function
|
|
19
|
+
export function kxmRuntimePaths(options: { stateRoot?: string; env?: NodeJS.ProcessEnv; homeDir?: string } = {}): KxmRuntimePaths {
|
|
20
20
|
const stateRoot = options.stateRoot
|
|
21
21
|
? resolve(options.stateRoot)
|
|
22
|
-
:
|
|
22
|
+
: kxmUserStateRoot({ ...(options.env ? { env: options.env } : {}), ...(options.homeDir ? { homeDir: options.homeDir } : {}) });
|
|
23
23
|
const runtimeDir = join(stateRoot, "runtime");
|
|
24
24
|
return {
|
|
25
25
|
stateRoot,
|
|
@@ -29,12 +29,12 @@ export function vnextRuntimePaths(options: { stateRoot?: string; env?: NodeJS.Pr
|
|
|
29
29
|
};
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
function runtimeIssue(phase:
|
|
32
|
+
function runtimeIssue(phase: KxmConfigIssue["phase"], code: string, file: string, message: string): KxmConfigIssue {
|
|
33
33
|
return { phase, code, file, message };
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
export function runtimeError(code: string, file: string, message: string):
|
|
37
|
-
return new
|
|
36
|
+
export function runtimeError(code: string, file: string, message: string): KxmConfigError {
|
|
37
|
+
return new KxmConfigError([runtimeIssue("semantic", code, file, message)]);
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
export function projectRuntimeKey(projectRoot: string): string {
|
|
@@ -65,12 +65,12 @@ import {
|
|
|
65
65
|
type DatabaseSchemaSpec,
|
|
66
66
|
} from "./database.ts";
|
|
67
67
|
|
|
68
|
-
export type
|
|
68
|
+
export type KxmDatabaseSchema = DatabaseSchemaSpec;
|
|
69
69
|
export { openDatabase };
|
|
70
70
|
|
|
71
71
|
/* ----------------------------- registry ---------------------------- */
|
|
72
72
|
|
|
73
|
-
export interface
|
|
73
|
+
export interface KxmSupervisorRecord {
|
|
74
74
|
runtimeId: string;
|
|
75
75
|
pid: number;
|
|
76
76
|
port: number;
|
|
@@ -80,7 +80,7 @@ export interface VnextSupervisorRecord {
|
|
|
80
80
|
state: "starting" | "running" | "stopping" | "stopped";
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
-
export interface
|
|
83
|
+
export interface KxmProjectRegistration {
|
|
84
84
|
projectId: string;
|
|
85
85
|
projectRoot: string;
|
|
86
86
|
projectKey: string;
|
|
@@ -89,7 +89,7 @@ export interface VnextProjectRegistration {
|
|
|
89
89
|
registeredAt: string;
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
-
export const
|
|
92
|
+
export const KXM_REGISTRY_SCHEMA_VERSION = 1;
|
|
93
93
|
|
|
94
94
|
const REGISTRY_TABLES = {
|
|
95
95
|
supervisor: ["singleton_id", "runtime_id", "pid", "port", "token_hash", "started_at", "heartbeat_at", "state"],
|
|
@@ -117,7 +117,7 @@ CREATE TABLE projects (
|
|
|
117
117
|
) STRICT;
|
|
118
118
|
`;
|
|
119
119
|
|
|
120
|
-
export class
|
|
120
|
+
export class KxmRuntimeRegistry {
|
|
121
121
|
readonly path: string;
|
|
122
122
|
private readonly database: DatabaseSync;
|
|
123
123
|
|
|
@@ -125,7 +125,7 @@ export class VnextRuntimeRegistry {
|
|
|
125
125
|
this.path = resolve(path);
|
|
126
126
|
this.database = openDatabase(this.path, "runtime registry", {
|
|
127
127
|
schema: REGISTRY_SCHEMA,
|
|
128
|
-
version:
|
|
128
|
+
version: KXM_REGISTRY_SCHEMA_VERSION,
|
|
129
129
|
tables: REGISTRY_TABLES,
|
|
130
130
|
});
|
|
131
131
|
}
|
|
@@ -135,7 +135,7 @@ export class VnextRuntimeRegistry {
|
|
|
135
135
|
}
|
|
136
136
|
|
|
137
137
|
/** Atomically claim or refresh the supervisor singleton. Returns the record that now owns it. */
|
|
138
|
-
claimSupervisor(record: { runtimeId: string; pid: number; port: number; tokenHash: string; now: string }): { claimed: boolean; record:
|
|
138
|
+
claimSupervisor(record: { runtimeId: string; pid: number; port: number; tokenHash: string; now: string }): { claimed: boolean; record: KxmSupervisorRecord } {
|
|
139
139
|
this.database.exec("BEGIN IMMEDIATE");
|
|
140
140
|
try {
|
|
141
141
|
const existing = this.readSupervisorRow();
|
|
@@ -161,7 +161,7 @@ export class VnextRuntimeRegistry {
|
|
|
161
161
|
}
|
|
162
162
|
|
|
163
163
|
/** Take over a dead supervisor's singleton, preserving its logical runtime identity and project home bindings. */
|
|
164
|
-
takeoverSupervisor(record: { runtimeId: string; pid: number; port: number; tokenHash: string; now: string; observedDeadPid: number; observedHeartbeatAt: string }):
|
|
164
|
+
takeoverSupervisor(record: { runtimeId: string; pid: number; port: number; tokenHash: string; now: string; observedDeadPid: number; observedHeartbeatAt: string }): KxmSupervisorRecord {
|
|
165
165
|
this.database.exec("BEGIN IMMEDIATE");
|
|
166
166
|
try {
|
|
167
167
|
// The logical runtime id is stable across process restarts: the new
|
|
@@ -203,9 +203,9 @@ export class VnextRuntimeRegistry {
|
|
|
203
203
|
return Number(result.changes);
|
|
204
204
|
}
|
|
205
205
|
|
|
206
|
-
private readSupervisorRow():
|
|
206
|
+
private readSupervisorRow(): KxmSupervisorRecord | undefined {
|
|
207
207
|
const row = this.database.prepare("SELECT runtime_id, pid, port, token_hash, started_at, heartbeat_at, state FROM supervisor WHERE singleton_id = 1").get() as
|
|
208
|
-
| { runtime_id: string; pid: number; port: number; token_hash: string; started_at: string; heartbeat_at: string; state:
|
|
208
|
+
| { runtime_id: string; pid: number; port: number; token_hash: string; started_at: string; heartbeat_at: string; state: KxmSupervisorRecord["state"] }
|
|
209
209
|
| undefined;
|
|
210
210
|
return row
|
|
211
211
|
? {
|
|
@@ -220,12 +220,12 @@ export class VnextRuntimeRegistry {
|
|
|
220
220
|
: undefined;
|
|
221
221
|
}
|
|
222
222
|
|
|
223
|
-
supervisor():
|
|
223
|
+
supervisor(): KxmSupervisorRecord | undefined {
|
|
224
224
|
return this.readSupervisorRow();
|
|
225
225
|
}
|
|
226
226
|
|
|
227
227
|
/** Register or revalidate a project's home binding. Home Runtime is immutable. */
|
|
228
|
-
registerProject(registration: { projectId: string; projectRoot: string; homeRuntimeId: string; configRevision?: string; now: string }):
|
|
228
|
+
registerProject(registration: { projectId: string; projectRoot: string; homeRuntimeId: string; configRevision?: string; now: string }): KxmProjectRegistration {
|
|
229
229
|
const projectRoot = resolve(registration.projectRoot);
|
|
230
230
|
const projectKey = projectRuntimeKey(projectRoot);
|
|
231
231
|
this.database.exec("BEGIN IMMEDIATE");
|
|
@@ -246,7 +246,7 @@ export class VnextRuntimeRegistry {
|
|
|
246
246
|
this.database.exec("ROLLBACK");
|
|
247
247
|
throw runtimeError("project_home_conflict", ".kxm/project.yaml", problems.join("; "));
|
|
248
248
|
}
|
|
249
|
-
const result:
|
|
249
|
+
const result: KxmProjectRegistration = {
|
|
250
250
|
projectId: existing.project_id,
|
|
251
251
|
projectRoot: existing.project_root,
|
|
252
252
|
projectKey: existing.project_key,
|
|
@@ -276,7 +276,7 @@ export class VnextRuntimeRegistry {
|
|
|
276
276
|
}
|
|
277
277
|
}
|
|
278
278
|
|
|
279
|
-
project(projectId: string):
|
|
279
|
+
project(projectId: string): KxmProjectRegistration | undefined {
|
|
280
280
|
const row = this.database.prepare("SELECT project_id, project_root, project_key, home_runtime_id, config_revision, registered_at FROM projects WHERE project_id = ?").get(projectId) as
|
|
281
281
|
| { project_id: string; project_root: string; project_key: string; home_runtime_id: string; config_revision: string | null; registered_at: string }
|
|
282
282
|
| undefined;
|
|
@@ -292,7 +292,7 @@ export class VnextRuntimeRegistry {
|
|
|
292
292
|
: undefined;
|
|
293
293
|
}
|
|
294
294
|
|
|
295
|
-
projectByRoot(projectRoot: string):
|
|
295
|
+
projectByRoot(projectRoot: string): KxmProjectRegistration | undefined {
|
|
296
296
|
const key = projectRuntimeKey(projectRoot);
|
|
297
297
|
const row = this.database.prepare("SELECT project_id, project_root, project_key, home_runtime_id, config_revision, registered_at FROM projects WHERE project_key = ?").get(key) as
|
|
298
298
|
| { project_id: string; project_root: string; project_key: string; home_runtime_id: string; config_revision: string | null; registered_at: string }
|
|
@@ -312,18 +312,18 @@ export class VnextRuntimeRegistry {
|
|
|
312
312
|
|
|
313
313
|
/* --------------------------- event store --------------------------- */
|
|
314
314
|
|
|
315
|
-
export type
|
|
315
|
+
export type KxmRunStatus = "created" | "preparing" | "running" | "waiting" | "blocked_uncertain" | "cancelling" | "cancelled" | "completed" | "failed";
|
|
316
316
|
|
|
317
|
-
export const
|
|
318
|
-
export const
|
|
317
|
+
export const KXM_RUN_EVENT_SCHEMA = "kxm.run-event.v1";
|
|
318
|
+
export const KXM_ABSENT_MEMORY_REVISION = "ctxrev_absent";
|
|
319
319
|
|
|
320
|
-
export interface
|
|
320
|
+
export interface KxmRunRecord {
|
|
321
321
|
runId: string;
|
|
322
322
|
projectId: string;
|
|
323
323
|
homeRuntimeId: string;
|
|
324
324
|
workflowId: string;
|
|
325
325
|
promptSha256: string;
|
|
326
|
-
status:
|
|
326
|
+
status: KxmRunStatus;
|
|
327
327
|
configRevision: string;
|
|
328
328
|
memoryRevision: string;
|
|
329
329
|
executorPolicyRevision: string;
|
|
@@ -332,8 +332,8 @@ export interface VnextRunRecord {
|
|
|
332
332
|
updatedAt: string;
|
|
333
333
|
}
|
|
334
334
|
|
|
335
|
-
export interface
|
|
336
|
-
schema: typeof
|
|
335
|
+
export interface KxmRunEvent {
|
|
336
|
+
schema: typeof KXM_RUN_EVENT_SCHEMA;
|
|
337
337
|
eventId: string;
|
|
338
338
|
eventType: string;
|
|
339
339
|
projectId: string;
|
|
@@ -351,36 +351,36 @@ export interface VnextRunEvent {
|
|
|
351
351
|
payload: Record<string, unknown>;
|
|
352
352
|
}
|
|
353
353
|
|
|
354
|
-
export interface
|
|
354
|
+
export interface KxmRunPlanRow {
|
|
355
355
|
runId: string;
|
|
356
356
|
runPlanHash: string;
|
|
357
357
|
envelope: string;
|
|
358
358
|
pinnedSequence: number;
|
|
359
359
|
}
|
|
360
360
|
|
|
361
|
-
export interface
|
|
361
|
+
export interface KxmRunStateRow {
|
|
362
362
|
runId: string;
|
|
363
363
|
lastSequence: number;
|
|
364
364
|
state: string;
|
|
365
365
|
}
|
|
366
366
|
|
|
367
|
-
export interface
|
|
367
|
+
export interface KxmDriveReceiptHandoff {
|
|
368
368
|
reason: string;
|
|
369
369
|
field?: string;
|
|
370
370
|
stepId?: string;
|
|
371
371
|
detail: string;
|
|
372
372
|
}
|
|
373
373
|
|
|
374
|
-
export interface
|
|
374
|
+
export interface KxmDriveReceiptSettlement {
|
|
375
375
|
kind: "terminal" | "handoff" | "unsettled";
|
|
376
376
|
status: string;
|
|
377
377
|
reason: string;
|
|
378
|
-
handoff?:
|
|
378
|
+
handoff?: KxmDriveReceiptHandoff;
|
|
379
379
|
error?: { class: string; component: string; retryable: boolean };
|
|
380
380
|
}
|
|
381
381
|
|
|
382
|
-
export interface
|
|
383
|
-
schema: typeof
|
|
382
|
+
export interface KxmDriveReceipt {
|
|
383
|
+
schema: typeof KXM_DRIVE_RECEIPT_SCHEMA;
|
|
384
384
|
driveId: string;
|
|
385
385
|
runId: string;
|
|
386
386
|
projectId: string;
|
|
@@ -391,16 +391,16 @@ export interface VnextDriveReceipt {
|
|
|
391
391
|
closedAt: string;
|
|
392
392
|
lastSequence: number;
|
|
393
393
|
logHash: string;
|
|
394
|
-
settlement:
|
|
395
|
-
budget:
|
|
394
|
+
settlement: KxmDriveReceiptSettlement;
|
|
395
|
+
budget: KxmDriveReceiptBudget | null;
|
|
396
396
|
producer: { id: string; closed: boolean };
|
|
397
397
|
}
|
|
398
398
|
|
|
399
|
-
export type
|
|
400
|
-
|
|
|
399
|
+
export type KxmDriveReceiptListItem =
|
|
400
|
+
| KxmDriveReceipt
|
|
401
401
|
| { readonly driveId: string; readonly divergence: string };
|
|
402
402
|
|
|
403
|
-
export interface
|
|
403
|
+
export interface KxmDriveReceiptBudget {
|
|
404
404
|
budgetMs: number;
|
|
405
405
|
source: "workflow" | "project" | "both";
|
|
406
406
|
elapsedMs: number;
|
|
@@ -408,14 +408,14 @@ export interface VnextDriveReceiptBudget {
|
|
|
408
408
|
}
|
|
409
409
|
|
|
410
410
|
/** sha256 over `eventId:sequence` for events 1..lastSequence, in sequence order. */
|
|
411
|
-
export function
|
|
411
|
+
export function hashKxmDriveLog(events: readonly { eventId: string; sequence: number }[]): string {
|
|
412
412
|
const body = events.map((event) => `${event.eventId}:${event.sequence}`).join("\n");
|
|
413
413
|
return `sha256:${createHash("sha256").update(body, "utf8").digest("hex")}`;
|
|
414
414
|
}
|
|
415
415
|
|
|
416
|
-
export function
|
|
417
|
-
receipt:
|
|
418
|
-
events: readonly
|
|
416
|
+
export function verifyKxmDriveReceipt(
|
|
417
|
+
receipt: KxmDriveReceipt,
|
|
418
|
+
events: readonly KxmRunEvent[],
|
|
419
419
|
foldedStatus: string,
|
|
420
420
|
binding: { runId: string; driveId: string },
|
|
421
421
|
): { verified: boolean; divergence?: string } {
|
|
@@ -432,7 +432,7 @@ export function verifyVnextDriveReceipt(
|
|
|
432
432
|
reasons.push(`lastSequence ${receipt.lastSequence} != log ${currentLast}`);
|
|
433
433
|
}
|
|
434
434
|
const prefix = events.filter((event) => event.sequence >= 1 && event.sequence <= receipt.lastSequence);
|
|
435
|
-
if (
|
|
435
|
+
if (hashKxmDriveLog(prefix) !== receipt.logHash) {
|
|
436
436
|
reasons.push("logHash mismatch");
|
|
437
437
|
}
|
|
438
438
|
if (receipt.settlement.status !== foldedStatus) {
|
|
@@ -442,7 +442,7 @@ export function verifyVnextDriveReceipt(
|
|
|
442
442
|
return { verified: false, divergence: reasons.join("; ") };
|
|
443
443
|
}
|
|
444
444
|
|
|
445
|
-
export interface
|
|
445
|
+
export interface KxmAttemptCapabilityRow {
|
|
446
446
|
attemptId: string;
|
|
447
447
|
runId: string;
|
|
448
448
|
assignmentId: string;
|
|
@@ -453,15 +453,15 @@ export interface VnextAttemptCapabilityRow {
|
|
|
453
453
|
state: "issued" | "settled" | "revoked";
|
|
454
454
|
}
|
|
455
455
|
|
|
456
|
-
export type
|
|
457
|
-
export type
|
|
458
|
-
export type
|
|
459
|
-
export type
|
|
460
|
-
export type
|
|
461
|
-
export type
|
|
462
|
-
export type
|
|
456
|
+
export type KxmGateKind = "command" | "artifacts-exist";
|
|
457
|
+
export type KxmGateExpect = "pass" | "fail";
|
|
458
|
+
export type KxmGateCompleteness = "complete" | "incomplete" | "no-start";
|
|
459
|
+
export type KxmGateStopCause = "none" | "timeout" | "cancel" | "error";
|
|
460
|
+
export type KxmGateSignalsAttempted = "none" | "term" | "term-kill";
|
|
461
|
+
export type KxmGateErrorClass = "validation" | "spawn-error" | "stream-error" | "stop-error" | "recording-error" | "lost-close";
|
|
462
|
+
export type KxmGateEvidenceOutcome = "passed" | "implementation-failure" | "repro-missing";
|
|
463
463
|
|
|
464
|
-
export interface
|
|
464
|
+
export interface KxmGateAttemptRow {
|
|
465
465
|
attemptId: string;
|
|
466
466
|
runId: string;
|
|
467
467
|
projectId: string;
|
|
@@ -471,8 +471,8 @@ export interface VnextGateAttemptRow {
|
|
|
471
471
|
assignmentId: string;
|
|
472
472
|
effectId: string;
|
|
473
473
|
gateId: string;
|
|
474
|
-
gateKind:
|
|
475
|
-
expect:
|
|
474
|
+
gateKind: KxmGateKind;
|
|
475
|
+
expect: KxmGateExpect;
|
|
476
476
|
gateDefinitionHash: string;
|
|
477
477
|
registryHash: string;
|
|
478
478
|
runPlanHash: string;
|
|
@@ -482,7 +482,7 @@ export interface VnextGateAttemptRow {
|
|
|
482
482
|
contentHash: string;
|
|
483
483
|
}
|
|
484
484
|
|
|
485
|
-
export interface
|
|
485
|
+
export interface KxmGateObservationRow {
|
|
486
486
|
observationId: string;
|
|
487
487
|
attemptId: string;
|
|
488
488
|
runId: string;
|
|
@@ -492,16 +492,16 @@ export interface VnextGateObservationRow {
|
|
|
492
492
|
stepAttempt: number;
|
|
493
493
|
assignmentId: string;
|
|
494
494
|
effectId: string;
|
|
495
|
-
completeness:
|
|
495
|
+
completeness: KxmGateCompleteness;
|
|
496
496
|
spawned: 0 | 1;
|
|
497
497
|
pid: number | null;
|
|
498
498
|
exitCode: number | null;
|
|
499
499
|
signal: string | null;
|
|
500
500
|
exitObserved: 0 | 1;
|
|
501
501
|
closeObserved: 0 | 1;
|
|
502
|
-
stopCause:
|
|
503
|
-
signalsAttempted:
|
|
504
|
-
errorClass:
|
|
502
|
+
stopCause: KxmGateStopCause;
|
|
503
|
+
signalsAttempted: KxmGateSignalsAttempted;
|
|
504
|
+
errorClass: KxmGateErrorClass | null;
|
|
505
505
|
stdoutSha256: string | null;
|
|
506
506
|
stdoutBytes: number | null;
|
|
507
507
|
stdoutComplete: 0 | 1 | null;
|
|
@@ -517,7 +517,7 @@ export interface VnextGateObservationRow {
|
|
|
517
517
|
contentHash: string;
|
|
518
518
|
}
|
|
519
519
|
|
|
520
|
-
export interface
|
|
520
|
+
export interface KxmGateEvidenceRow {
|
|
521
521
|
evidenceId: string;
|
|
522
522
|
attemptId: string;
|
|
523
523
|
observationId: string;
|
|
@@ -530,19 +530,19 @@ export interface VnextGateEvidenceRow {
|
|
|
530
530
|
effectId: string;
|
|
531
531
|
evidenceKey: string | null;
|
|
532
532
|
kind: "gate";
|
|
533
|
-
expect:
|
|
534
|
-
outcome:
|
|
533
|
+
expect: KxmGateExpect;
|
|
534
|
+
outcome: KxmGateEvidenceOutcome;
|
|
535
535
|
settledEventId: string;
|
|
536
536
|
contentHash: string;
|
|
537
537
|
}
|
|
538
538
|
|
|
539
|
-
export interface
|
|
539
|
+
export interface KxmGateRowCounts {
|
|
540
540
|
attempts: number;
|
|
541
541
|
observations: number;
|
|
542
542
|
evidence: number;
|
|
543
543
|
}
|
|
544
544
|
|
|
545
|
-
export interface
|
|
545
|
+
export interface KxmCommandRecord {
|
|
546
546
|
commandId: string;
|
|
547
547
|
runId: string;
|
|
548
548
|
kind: string;
|
|
@@ -550,8 +550,8 @@ export interface VnextCommandRecord {
|
|
|
550
550
|
recordedAt: string;
|
|
551
551
|
}
|
|
552
552
|
|
|
553
|
-
export const
|
|
554
|
-
export const
|
|
553
|
+
export const KXM_EVENT_STORE_SCHEMA_VERSION = 4;
|
|
554
|
+
export const KXM_DRIVE_RECEIPT_SCHEMA = "kxm.drive-receipt.v1";
|
|
555
555
|
export const DRIVE_RECEIPT_MAX_BYTES = 8 * 1024;
|
|
556
556
|
|
|
557
557
|
const EVENT_STORE_TABLES = {
|
|
@@ -757,7 +757,7 @@ const EVENT_STORE_MIGRATIONS = [
|
|
|
757
757
|
},
|
|
758
758
|
];
|
|
759
759
|
|
|
760
|
-
export class
|
|
760
|
+
export class KxmRunEventStore {
|
|
761
761
|
readonly path: string;
|
|
762
762
|
private readonly database: DatabaseSync;
|
|
763
763
|
|
|
@@ -765,7 +765,7 @@ export class VnextRunEventStore {
|
|
|
765
765
|
this.path = resolve(path);
|
|
766
766
|
this.database = openDatabase(this.path, "run event store", {
|
|
767
767
|
schema: EVENT_STORE_SCHEMA,
|
|
768
|
-
version:
|
|
768
|
+
version: KXM_EVENT_STORE_SCHEMA_VERSION,
|
|
769
769
|
tables: EVENT_STORE_TABLES,
|
|
770
770
|
migrations: EVENT_STORE_MIGRATIONS,
|
|
771
771
|
});
|
|
@@ -814,7 +814,7 @@ export class VnextRunEventStore {
|
|
|
814
814
|
return withDatabaseTransaction(this.database, work);
|
|
815
815
|
}
|
|
816
816
|
|
|
817
|
-
command(commandId: string):
|
|
817
|
+
command(commandId: string): KxmCommandRecord | undefined {
|
|
818
818
|
const row = this.database.prepare("SELECT command_id, run_id, kind, result, recorded_at FROM commands WHERE command_id = ?").get(commandId) as
|
|
819
819
|
| { command_id: string; run_id: string; kind: string; result: string; recorded_at: string }
|
|
820
820
|
| undefined;
|
|
@@ -823,12 +823,12 @@ export class VnextRunEventStore {
|
|
|
823
823
|
: undefined;
|
|
824
824
|
}
|
|
825
825
|
|
|
826
|
-
insertCommand(record:
|
|
826
|
+
insertCommand(record: KxmCommandRecord): void {
|
|
827
827
|
this.database.prepare("INSERT INTO commands (command_id, run_id, kind, result, recorded_at) VALUES (?, ?, ?, ?, ?)")
|
|
828
828
|
.run(record.commandId, record.runId, record.kind, JSON.stringify(record.result), record.recordedAt);
|
|
829
829
|
}
|
|
830
830
|
|
|
831
|
-
insertRun(record:
|
|
831
|
+
insertRun(record: KxmRunRecord): void {
|
|
832
832
|
this.database.prepare(`
|
|
833
833
|
INSERT INTO runs (run_id, project_id, home_runtime_id, workflow_id, prompt_sha256, status, config_revision, memory_revision, executor_policy_revision, tool_policy_revision, created_at, updated_at)
|
|
834
834
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
@@ -848,11 +848,11 @@ export class VnextRunEventStore {
|
|
|
848
848
|
);
|
|
849
849
|
}
|
|
850
850
|
|
|
851
|
-
updateRunStatus(runId: string, status:
|
|
851
|
+
updateRunStatus(runId: string, status: KxmRunStatus, updatedAt: string): void {
|
|
852
852
|
this.database.prepare("UPDATE runs SET status = ?, updated_at = ? WHERE run_id = ?").run(status, updatedAt, runId);
|
|
853
853
|
}
|
|
854
854
|
|
|
855
|
-
run(runId: string):
|
|
855
|
+
run(runId: string): KxmRunRecord | undefined {
|
|
856
856
|
const row = this.database.prepare(`
|
|
857
857
|
SELECT run_id, project_id, home_runtime_id, workflow_id, prompt_sha256, status, config_revision, memory_revision, executor_policy_revision, tool_policy_revision, created_at, updated_at
|
|
858
858
|
FROM runs WHERE run_id = ?
|
|
@@ -863,7 +863,7 @@ export class VnextRunEventStore {
|
|
|
863
863
|
home_runtime_id: string;
|
|
864
864
|
workflow_id: string;
|
|
865
865
|
prompt_sha256: string;
|
|
866
|
-
status:
|
|
866
|
+
status: KxmRunStatus;
|
|
867
867
|
config_revision: string;
|
|
868
868
|
memory_revision: string;
|
|
869
869
|
executor_policy_revision: string;
|
|
@@ -875,7 +875,7 @@ export class VnextRunEventStore {
|
|
|
875
875
|
return row ? runFromRow(row) : undefined;
|
|
876
876
|
}
|
|
877
877
|
|
|
878
|
-
runsForProject(projectId: string, limit = 50):
|
|
878
|
+
runsForProject(projectId: string, limit = 50): KxmRunRecord[] {
|
|
879
879
|
const rows = this.database.prepare(`
|
|
880
880
|
SELECT run_id, project_id, home_runtime_id, workflow_id, prompt_sha256, status, config_revision, memory_revision, executor_policy_revision, tool_policy_revision, created_at, updated_at
|
|
881
881
|
FROM runs WHERE project_id = ? ORDER BY created_at DESC, run_id DESC LIMIT ?
|
|
@@ -888,7 +888,7 @@ export class VnextRunEventStore {
|
|
|
888
888
|
return row.max_sequence + 1;
|
|
889
889
|
}
|
|
890
890
|
|
|
891
|
-
appendEvent(event:
|
|
891
|
+
appendEvent(event: KxmRunEvent): void {
|
|
892
892
|
validateRunEvent(event, "run-event");
|
|
893
893
|
const run = this.run(event.runId);
|
|
894
894
|
if (!run) throw runtimeError("run_unknown", event.runId, "run does not exist in this event store");
|
|
@@ -918,7 +918,7 @@ export class VnextRunEventStore {
|
|
|
918
918
|
);
|
|
919
919
|
}
|
|
920
920
|
|
|
921
|
-
events(runId: string, afterSequence = 0, limit = 200):
|
|
921
|
+
events(runId: string, afterSequence = 0, limit = 200): KxmRunEvent[] {
|
|
922
922
|
const rows = this.database.prepare(`
|
|
923
923
|
SELECT project_id, run_id, sequence, event_id, event_type, command_id, occurred_at, recorded_at, monotonic_ns, config_revision, memory_revision, executor_policy_revision, tool_policy_revision, payload, schema, home_runtime_id
|
|
924
924
|
FROM events WHERE run_id = ? AND sequence > ? ORDER BY sequence ASC LIMIT ?
|
|
@@ -937,7 +937,7 @@ export class VnextRunEventStore {
|
|
|
937
937
|
executor_policy_revision: string;
|
|
938
938
|
tool_policy_revision: string;
|
|
939
939
|
payload: string;
|
|
940
|
-
schema: typeof
|
|
940
|
+
schema: typeof KXM_RUN_EVENT_SCHEMA;
|
|
941
941
|
home_runtime_id: string;
|
|
942
942
|
}>;
|
|
943
943
|
return rows.map((row) => ({
|
|
@@ -960,12 +960,12 @@ export class VnextRunEventStore {
|
|
|
960
960
|
}));
|
|
961
961
|
}
|
|
962
962
|
|
|
963
|
-
insertRunPlan(row:
|
|
963
|
+
insertRunPlan(row: KxmRunPlanRow): void {
|
|
964
964
|
this.database.prepare("INSERT INTO run_plans (run_id, run_plan_hash, envelope, pinned_sequence) VALUES (?, ?, ?, ?)")
|
|
965
965
|
.run(row.runId, row.runPlanHash, row.envelope, row.pinnedSequence);
|
|
966
966
|
}
|
|
967
967
|
|
|
968
|
-
runPlan(runId: string):
|
|
968
|
+
runPlan(runId: string): KxmRunPlanRow | undefined {
|
|
969
969
|
const row = this.database.prepare("SELECT run_id, run_plan_hash, envelope, pinned_sequence FROM run_plans WHERE run_id = ?").get(runId) as
|
|
970
970
|
| { run_id: string; run_plan_hash: string; envelope: string; pinned_sequence: number }
|
|
971
971
|
| undefined;
|
|
@@ -974,23 +974,23 @@ export class VnextRunEventStore {
|
|
|
974
974
|
: undefined;
|
|
975
975
|
}
|
|
976
976
|
|
|
977
|
-
runState(runId: string):
|
|
977
|
+
runState(runId: string): KxmRunStateRow | undefined {
|
|
978
978
|
const row = this.database.prepare("SELECT run_id, last_sequence, state FROM run_state WHERE run_id = ?").get(runId) as
|
|
979
979
|
| { run_id: string; last_sequence: number; state: string }
|
|
980
980
|
| undefined;
|
|
981
981
|
return row ? { runId: row.run_id, lastSequence: row.last_sequence, state: row.state } : undefined;
|
|
982
982
|
}
|
|
983
983
|
|
|
984
|
-
upsertRunState(row:
|
|
984
|
+
upsertRunState(row: KxmRunStateRow): void {
|
|
985
985
|
this.database.prepare(`
|
|
986
986
|
INSERT INTO run_state (run_id, last_sequence, state) VALUES (?, ?, ?)
|
|
987
987
|
ON CONFLICT(run_id) DO UPDATE SET last_sequence = excluded.last_sequence, state = excluded.state
|
|
988
988
|
`).run(row.runId, row.lastSequence, row.state);
|
|
989
989
|
}
|
|
990
990
|
|
|
991
|
-
insertDriveReceipt(receipt:
|
|
991
|
+
insertDriveReceipt(receipt: KxmDriveReceipt): { inserted: boolean } {
|
|
992
992
|
validateDriveReceipt(receipt, "drive-receipt");
|
|
993
|
-
const canonical =
|
|
993
|
+
const canonical = kxmCanonicalJson(receipt as unknown as JsonValue);
|
|
994
994
|
if (Buffer.byteLength(canonical, "utf8") > DRIVE_RECEIPT_MAX_BYTES) {
|
|
995
995
|
throw runtimeError("drive_receipt_invalid", receipt.driveId, `drive receipt exceeds ${DRIVE_RECEIPT_MAX_BYTES} bytes`);
|
|
996
996
|
}
|
|
@@ -1011,14 +1011,14 @@ export class VnextRunEventStore {
|
|
|
1011
1011
|
return { inserted: Number(result.changes) === 1 };
|
|
1012
1012
|
}
|
|
1013
1013
|
|
|
1014
|
-
driveReceipt(driveId: string):
|
|
1014
|
+
driveReceipt(driveId: string): KxmDriveReceipt | undefined {
|
|
1015
1015
|
const row = this.database.prepare("SELECT receipt FROM drive_receipts WHERE drive_id = ?").get(driveId) as
|
|
1016
1016
|
| { receipt: string }
|
|
1017
1017
|
| undefined;
|
|
1018
1018
|
return row ? parseDriveReceipt(row.receipt) : undefined;
|
|
1019
1019
|
}
|
|
1020
1020
|
|
|
1021
|
-
driveReceiptsForRun(runId: string, limit = 20):
|
|
1021
|
+
driveReceiptsForRun(runId: string, limit = 20): KxmDriveReceiptListItem[] {
|
|
1022
1022
|
const rows = this.database.prepare(`
|
|
1023
1023
|
SELECT drive_id, receipt FROM drive_receipts WHERE run_id = ? ORDER BY closed_at DESC, drive_id DESC LIMIT ?
|
|
1024
1024
|
`).all(runId, limit) as Array<{ drive_id: string; receipt: string }>;
|
|
@@ -1031,21 +1031,21 @@ export class VnextRunEventStore {
|
|
|
1031
1031
|
});
|
|
1032
1032
|
}
|
|
1033
1033
|
|
|
1034
|
-
insertCapability(row:
|
|
1034
|
+
insertCapability(row: KxmAttemptCapabilityRow): void {
|
|
1035
1035
|
this.database.prepare(`
|
|
1036
1036
|
INSERT INTO attempt_capabilities (attempt_id, run_id, assignment_id, step_id, step_attempt, producer_id, capability_hash, state)
|
|
1037
1037
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
1038
1038
|
`).run(row.attemptId, row.runId, row.assignmentId, row.stepId, row.stepAttempt, row.producerId, row.capabilityHash, row.state);
|
|
1039
1039
|
}
|
|
1040
1040
|
|
|
1041
|
-
capabilityByHash(capabilityHash: string):
|
|
1041
|
+
capabilityByHash(capabilityHash: string): KxmAttemptCapabilityRow | undefined {
|
|
1042
1042
|
return capabilityFromRow(this.database.prepare(`
|
|
1043
1043
|
SELECT attempt_id, run_id, assignment_id, step_id, step_attempt, producer_id, capability_hash, state
|
|
1044
1044
|
FROM attempt_capabilities WHERE capability_hash = ?
|
|
1045
1045
|
`).get(capabilityHash) as CapabilitySqlRow | undefined);
|
|
1046
1046
|
}
|
|
1047
1047
|
|
|
1048
|
-
capabilityByAttempt(attemptId: string):
|
|
1048
|
+
capabilityByAttempt(attemptId: string): KxmAttemptCapabilityRow | undefined {
|
|
1049
1049
|
return capabilityFromRow(this.database.prepare(`
|
|
1050
1050
|
SELECT attempt_id, run_id, assignment_id, step_id, step_attempt, producer_id, capability_hash, state
|
|
1051
1051
|
FROM attempt_capabilities WHERE attempt_id = ?
|
|
@@ -1059,7 +1059,7 @@ export class VnextRunEventStore {
|
|
|
1059
1059
|
}
|
|
1060
1060
|
}
|
|
1061
1061
|
|
|
1062
|
-
insertGateAttempt(row:
|
|
1062
|
+
insertGateAttempt(row: KxmGateAttemptRow): void {
|
|
1063
1063
|
assertGateAttemptRow(row);
|
|
1064
1064
|
this.assertEventType(row.runId, row.intentEventId, ["effect.intent_recorded"]);
|
|
1065
1065
|
const capability = this.capabilityByAttempt(row.attemptId);
|
|
@@ -1085,7 +1085,7 @@ export class VnextRunEventStore {
|
|
|
1085
1085
|
);
|
|
1086
1086
|
}
|
|
1087
1087
|
|
|
1088
|
-
insertGateObservation(row:
|
|
1088
|
+
insertGateObservation(row: KxmGateObservationRow): void {
|
|
1089
1089
|
assertGateObservationRow(row);
|
|
1090
1090
|
this.assertEventType(row.runId, row.recordedEventId, ["effect.observed", "effect.blocked_uncertain"]);
|
|
1091
1091
|
const attempt = this.gateAttempt(row.attemptId);
|
|
@@ -1108,7 +1108,7 @@ export class VnextRunEventStore {
|
|
|
1108
1108
|
);
|
|
1109
1109
|
}
|
|
1110
1110
|
|
|
1111
|
-
insertGateEvidence(row:
|
|
1111
|
+
insertGateEvidence(row: KxmGateEvidenceRow): void {
|
|
1112
1112
|
assertGateEvidenceRow(row);
|
|
1113
1113
|
this.assertEventType(row.runId, row.settledEventId, ["effect.settled"]);
|
|
1114
1114
|
const attempt = this.gateAttempt(row.attemptId);
|
|
@@ -1130,7 +1130,7 @@ export class VnextRunEventStore {
|
|
|
1130
1130
|
);
|
|
1131
1131
|
}
|
|
1132
1132
|
|
|
1133
|
-
gateAttempt(attemptId: string):
|
|
1133
|
+
gateAttempt(attemptId: string): KxmGateAttemptRow | undefined {
|
|
1134
1134
|
const row = this.database.prepare(`
|
|
1135
1135
|
SELECT attempt_id, run_id, project_id, home_runtime_id, step_id, step_attempt, assignment_id, effect_id,
|
|
1136
1136
|
gate_id, gate_kind, expect, gate_definition_hash, registry_hash, run_plan_hash, control_project_key,
|
|
@@ -1140,7 +1140,7 @@ export class VnextRunEventStore {
|
|
|
1140
1140
|
return row ? gateAttemptFromSql(row) : undefined;
|
|
1141
1141
|
}
|
|
1142
1142
|
|
|
1143
|
-
gateAttemptsForRun(runId: string):
|
|
1143
|
+
gateAttemptsForRun(runId: string): KxmGateAttemptRow[] {
|
|
1144
1144
|
const rows = this.database.prepare(`
|
|
1145
1145
|
SELECT attempt_id, run_id, project_id, home_runtime_id, step_id, step_attempt, assignment_id, effect_id,
|
|
1146
1146
|
gate_id, gate_kind, expect, gate_definition_hash, registry_hash, run_plan_hash, control_project_key,
|
|
@@ -1150,7 +1150,7 @@ export class VnextRunEventStore {
|
|
|
1150
1150
|
return rows.map(gateAttemptFromSql);
|
|
1151
1151
|
}
|
|
1152
1152
|
|
|
1153
|
-
gateAttemptsForProject(projectId: string):
|
|
1153
|
+
gateAttemptsForProject(projectId: string): KxmGateAttemptRow[] {
|
|
1154
1154
|
const rows = this.database.prepare(`
|
|
1155
1155
|
SELECT attempt_id, run_id, project_id, home_runtime_id, step_id, step_attempt, assignment_id, effect_id,
|
|
1156
1156
|
gate_id, gate_kind, expect, gate_definition_hash, registry_hash, run_plan_hash, control_project_key,
|
|
@@ -1160,14 +1160,14 @@ export class VnextRunEventStore {
|
|
|
1160
1160
|
return rows.map(gateAttemptFromSql);
|
|
1161
1161
|
}
|
|
1162
1162
|
|
|
1163
|
-
issuedOrRevokedCapabilities():
|
|
1163
|
+
issuedOrRevokedCapabilities(): KxmAttemptCapabilityRow[] {
|
|
1164
1164
|
const rows = this.database.prepare(`
|
|
1165
1165
|
SELECT attempt_id, run_id, assignment_id, step_id, step_attempt, producer_id, capability_hash, state
|
|
1166
1166
|
FROM attempt_capabilities
|
|
1167
1167
|
WHERE state IN ('issued','revoked')
|
|
1168
1168
|
ORDER BY run_id ASC, step_attempt ASC, attempt_id ASC
|
|
1169
1169
|
`).all() as CapabilitySqlRow[];
|
|
1170
|
-
const mapped:
|
|
1170
|
+
const mapped: KxmAttemptCapabilityRow[] = [];
|
|
1171
1171
|
for (const row of rows) {
|
|
1172
1172
|
const capability = capabilityFromRow(row);
|
|
1173
1173
|
if (!capability) {
|
|
@@ -1178,7 +1178,7 @@ export class VnextRunEventStore {
|
|
|
1178
1178
|
return mapped;
|
|
1179
1179
|
}
|
|
1180
1180
|
|
|
1181
|
-
gateObservationForAttempt(attemptId: string):
|
|
1181
|
+
gateObservationForAttempt(attemptId: string): KxmGateObservationRow | undefined {
|
|
1182
1182
|
const row = this.database.prepare(`
|
|
1183
1183
|
SELECT observation_id, attempt_id, run_id, project_id, home_runtime_id, step_id, step_attempt, assignment_id,
|
|
1184
1184
|
effect_id, completeness, spawned, pid, exit_code, signal, exit_observed, close_observed, stop_cause,
|
|
@@ -1189,7 +1189,7 @@ export class VnextRunEventStore {
|
|
|
1189
1189
|
return row ? gateObservationFromSql(row) : undefined;
|
|
1190
1190
|
}
|
|
1191
1191
|
|
|
1192
|
-
gateObservationsForRun(runId: string):
|
|
1192
|
+
gateObservationsForRun(runId: string): KxmGateObservationRow[] {
|
|
1193
1193
|
const rows = this.database.prepare(`
|
|
1194
1194
|
SELECT observation_id, attempt_id, run_id, project_id, home_runtime_id, step_id, step_attempt, assignment_id,
|
|
1195
1195
|
effect_id, completeness, spawned, pid, exit_code, signal, exit_observed, close_observed, stop_cause,
|
|
@@ -1200,7 +1200,7 @@ export class VnextRunEventStore {
|
|
|
1200
1200
|
return rows.map(gateObservationFromSql);
|
|
1201
1201
|
}
|
|
1202
1202
|
|
|
1203
|
-
gateEvidenceForRun(runId: string):
|
|
1203
|
+
gateEvidenceForRun(runId: string): KxmGateEvidenceRow[] {
|
|
1204
1204
|
const rows = this.database.prepare(`
|
|
1205
1205
|
SELECT evidence_id, attempt_id, observation_id, run_id, project_id, home_runtime_id, step_id, step_attempt,
|
|
1206
1206
|
assignment_id, effect_id, evidence_key, kind, expect, outcome, settled_event_id, content_hash
|
|
@@ -1209,7 +1209,7 @@ export class VnextRunEventStore {
|
|
|
1209
1209
|
return rows.map(gateEvidenceFromSql);
|
|
1210
1210
|
}
|
|
1211
1211
|
|
|
1212
|
-
gateEvidenceForAttempt(attemptId: string):
|
|
1212
|
+
gateEvidenceForAttempt(attemptId: string): KxmGateEvidenceRow | undefined {
|
|
1213
1213
|
const row = this.database.prepare(`
|
|
1214
1214
|
SELECT evidence_id, attempt_id, observation_id, run_id, project_id, home_runtime_id, step_id, step_attempt,
|
|
1215
1215
|
assignment_id, effect_id, evidence_key, kind, expect, outcome, settled_event_id, content_hash
|
|
@@ -1218,7 +1218,7 @@ export class VnextRunEventStore {
|
|
|
1218
1218
|
return row ? gateEvidenceFromSql(row) : undefined;
|
|
1219
1219
|
}
|
|
1220
1220
|
|
|
1221
|
-
gateRowCounts(runId: string):
|
|
1221
|
+
gateRowCounts(runId: string): KxmGateRowCounts {
|
|
1222
1222
|
const attempts = this.database.prepare("SELECT COUNT(*) AS n FROM gate_attempts WHERE run_id = ?").get(runId) as { n: number };
|
|
1223
1223
|
const observations = this.database.prepare("SELECT COUNT(*) AS n FROM gate_observations WHERE run_id = ?").get(runId) as { n: number };
|
|
1224
1224
|
const evidence = this.database.prepare("SELECT COUNT(*) AS n FROM gate_evidence WHERE run_id = ?").get(runId) as { n: number };
|
|
@@ -1235,10 +1235,10 @@ export class VnextRunEventStore {
|
|
|
1235
1235
|
}
|
|
1236
1236
|
}
|
|
1237
1237
|
|
|
1238
|
-
function parseDriveReceipt(raw: string):
|
|
1238
|
+
function parseDriveReceipt(raw: string): KxmDriveReceipt {
|
|
1239
1239
|
const parsed = JSON.parse(raw) as unknown;
|
|
1240
1240
|
validateDriveReceipt(parsed, "drive-receipt");
|
|
1241
|
-
return parsed as
|
|
1241
|
+
return parsed as KxmDriveReceipt;
|
|
1242
1242
|
}
|
|
1243
1243
|
|
|
1244
1244
|
function runFromRow(row: {
|
|
@@ -1247,14 +1247,14 @@ function runFromRow(row: {
|
|
|
1247
1247
|
home_runtime_id: string;
|
|
1248
1248
|
workflow_id: string;
|
|
1249
1249
|
prompt_sha256: string;
|
|
1250
|
-
status:
|
|
1250
|
+
status: KxmRunStatus;
|
|
1251
1251
|
config_revision: string;
|
|
1252
1252
|
memory_revision: string;
|
|
1253
1253
|
executor_policy_revision: string;
|
|
1254
1254
|
tool_policy_revision: string;
|
|
1255
1255
|
created_at: string;
|
|
1256
1256
|
updated_at: string;
|
|
1257
|
-
}):
|
|
1257
|
+
}): KxmRunRecord {
|
|
1258
1258
|
if (typeof row.memory_revision !== "string" || row.memory_revision.length === 0) {
|
|
1259
1259
|
throw runtimeError("runtime_schema_shape_invalid", row.run_id, "memory_revision is required");
|
|
1260
1260
|
}
|
|
@@ -1275,35 +1275,35 @@ function runFromRow(row: {
|
|
|
1275
1275
|
}
|
|
1276
1276
|
|
|
1277
1277
|
/** Compute the immutable identity for a new run. */
|
|
1278
|
-
export function
|
|
1278
|
+
export function newKxmRunId(): string {
|
|
1279
1279
|
return `run_${randomUUID().replaceAll("-", "")}`;
|
|
1280
1280
|
}
|
|
1281
1281
|
|
|
1282
|
-
export function
|
|
1282
|
+
export function newKxmEventId(): string {
|
|
1283
1283
|
return `evt_${randomUUID().replaceAll("-", "")}`;
|
|
1284
1284
|
}
|
|
1285
1285
|
|
|
1286
|
-
export function
|
|
1286
|
+
export function newKxmCommandId(): string {
|
|
1287
1287
|
return `cmd_${randomUUID().replaceAll("-", "")}`;
|
|
1288
1288
|
}
|
|
1289
1289
|
|
|
1290
|
-
export function
|
|
1290
|
+
export function newKxmAssignmentId(): string {
|
|
1291
1291
|
return `asg_${randomUUID().replaceAll("-", "")}`;
|
|
1292
1292
|
}
|
|
1293
1293
|
|
|
1294
|
-
export function
|
|
1294
|
+
export function newKxmAttemptId(): string {
|
|
1295
1295
|
return `att_${randomUUID().replaceAll("-", "")}`;
|
|
1296
1296
|
}
|
|
1297
1297
|
|
|
1298
|
-
export function
|
|
1298
|
+
export function newKxmEffectId(): string {
|
|
1299
1299
|
return `eff_${randomUUID().replaceAll("-", "")}`;
|
|
1300
1300
|
}
|
|
1301
1301
|
|
|
1302
|
-
export function
|
|
1302
|
+
export function newKxmObservationId(): string {
|
|
1303
1303
|
return `obs_${randomUUID().replaceAll("-", "")}`;
|
|
1304
1304
|
}
|
|
1305
1305
|
|
|
1306
|
-
export function
|
|
1306
|
+
export function newKxmEvidenceId(): string {
|
|
1307
1307
|
return `gev_${randomUUID().replaceAll("-", "")}`;
|
|
1308
1308
|
}
|
|
1309
1309
|
|
|
@@ -1330,11 +1330,11 @@ export function gateRowContentHash(table: "gate_attempts" | "gate_observations"
|
|
|
1330
1330
|
const copy = { ...(row as Record<string, unknown>) };
|
|
1331
1331
|
delete copy.contentHash;
|
|
1332
1332
|
void table;
|
|
1333
|
-
return `sha256:${createHash("sha256").update(
|
|
1333
|
+
return `sha256:${createHash("sha256").update(kxmCanonicalJson(copy as JsonValue), "utf8").digest("hex")}`;
|
|
1334
1334
|
}
|
|
1335
1335
|
|
|
1336
|
-
export type
|
|
1337
|
-
|
|
1336
|
+
export type KxmGateObservationFacts = Pick<
|
|
1337
|
+
KxmGateObservationRow,
|
|
1338
1338
|
| "completeness"
|
|
1339
1339
|
| "spawned"
|
|
1340
1340
|
| "pid"
|
|
@@ -1356,8 +1356,8 @@ export type VnextGateObservationFacts = Pick<
|
|
|
1356
1356
|
>;
|
|
1357
1357
|
|
|
1358
1358
|
export function assertClosedGateObservation(
|
|
1359
|
-
kind:
|
|
1360
|
-
row:
|
|
1359
|
+
kind: KxmGateKind,
|
|
1360
|
+
row: KxmGateObservationFacts,
|
|
1361
1361
|
id = "observation",
|
|
1362
1362
|
): void {
|
|
1363
1363
|
if (row.completeness === "incomplete") return;
|
|
@@ -1435,10 +1435,10 @@ export function assertClosedGateObservation(
|
|
|
1435
1435
|
}
|
|
1436
1436
|
|
|
1437
1437
|
export function computeGateEvidenceOutcome(
|
|
1438
|
-
kind:
|
|
1439
|
-
expect:
|
|
1440
|
-
observation:
|
|
1441
|
-
):
|
|
1438
|
+
kind: KxmGateKind,
|
|
1439
|
+
expect: KxmGateExpect,
|
|
1440
|
+
observation: KxmGateObservationFacts,
|
|
1441
|
+
): KxmGateEvidenceOutcome {
|
|
1442
1442
|
assertClosedGateObservation(kind, observation);
|
|
1443
1443
|
if (observation.completeness !== "complete") {
|
|
1444
1444
|
throw gateRowInvalid("observation", "evaluated settlement requires a complete observation");
|
|
@@ -1451,7 +1451,7 @@ export function computeGateEvidenceOutcome(
|
|
|
1451
1451
|
return observation.exitCode === 0 ? "repro-missing" : "passed";
|
|
1452
1452
|
}
|
|
1453
1453
|
|
|
1454
|
-
function gateRowInvalid(file: string, message: string):
|
|
1454
|
+
function gateRowInvalid(file: string, message: string): KxmConfigError {
|
|
1455
1455
|
return runtimeError("gate_row_invalid", file, message);
|
|
1456
1456
|
}
|
|
1457
1457
|
|
|
@@ -1485,7 +1485,7 @@ function assertNullInt(value: unknown, id: string, label: string): void {
|
|
|
1485
1485
|
}
|
|
1486
1486
|
}
|
|
1487
1487
|
|
|
1488
|
-
function assertGateAttemptRow(row:
|
|
1488
|
+
function assertGateAttemptRow(row: KxmGateAttemptRow): void {
|
|
1489
1489
|
assertExactRowKeys(row, GATE_ATTEMPT_KEYS, row.attemptId);
|
|
1490
1490
|
assertId(row.attemptId, "att_", row.attemptId, "attemptId");
|
|
1491
1491
|
assertId(row.effectId, "eff_", row.attemptId, "effectId");
|
|
@@ -1509,7 +1509,7 @@ function assertGateAttemptRow(row: VnextGateAttemptRow): void {
|
|
|
1509
1509
|
}
|
|
1510
1510
|
}
|
|
1511
1511
|
|
|
1512
|
-
function assertGateObservationRow(row:
|
|
1512
|
+
function assertGateObservationRow(row: KxmGateObservationRow): void {
|
|
1513
1513
|
assertExactRowKeys(row, GATE_OBSERVATION_KEYS, row.observationId);
|
|
1514
1514
|
assertId(row.observationId, "obs_", row.observationId, "observationId");
|
|
1515
1515
|
assertId(row.attemptId, "att_", row.observationId, "attemptId");
|
|
@@ -1543,7 +1543,7 @@ function assertGateObservationRow(row: VnextGateObservationRow): void {
|
|
|
1543
1543
|
}
|
|
1544
1544
|
}
|
|
1545
1545
|
|
|
1546
|
-
function assertGateEvidenceRow(row:
|
|
1546
|
+
function assertGateEvidenceRow(row: KxmGateEvidenceRow): void {
|
|
1547
1547
|
assertExactRowKeys(row, GATE_EVIDENCE_KEYS, row.evidenceId);
|
|
1548
1548
|
assertId(row.evidenceId, "gev_", row.evidenceId, "evidenceId");
|
|
1549
1549
|
assertId(row.attemptId, "att_", row.evidenceId, "attemptId");
|
|
@@ -1561,7 +1561,7 @@ function assertGateEvidenceRow(row: VnextGateEvidenceRow): void {
|
|
|
1561
1561
|
|
|
1562
1562
|
function assertSharedIdentity(
|
|
1563
1563
|
row: { runId: string; projectId: string; homeRuntimeId: string; stepId: string; stepAttempt: number; assignmentId: string; effectId: string },
|
|
1564
|
-
attempt:
|
|
1564
|
+
attempt: KxmGateAttemptRow,
|
|
1565
1565
|
id: string,
|
|
1566
1566
|
): void {
|
|
1567
1567
|
if (
|
|
@@ -1587,8 +1587,8 @@ type GateAttemptSql = {
|
|
|
1587
1587
|
assignment_id: string;
|
|
1588
1588
|
effect_id: string;
|
|
1589
1589
|
gate_id: string;
|
|
1590
|
-
gate_kind:
|
|
1591
|
-
expect:
|
|
1590
|
+
gate_kind: KxmGateKind;
|
|
1591
|
+
expect: KxmGateExpect;
|
|
1592
1592
|
gate_definition_hash: string;
|
|
1593
1593
|
registry_hash: string;
|
|
1594
1594
|
run_plan_hash: string;
|
|
@@ -1598,7 +1598,7 @@ type GateAttemptSql = {
|
|
|
1598
1598
|
content_hash: string;
|
|
1599
1599
|
};
|
|
1600
1600
|
|
|
1601
|
-
function gateAttemptFromSql(row: GateAttemptSql):
|
|
1601
|
+
function gateAttemptFromSql(row: GateAttemptSql): KxmGateAttemptRow {
|
|
1602
1602
|
return {
|
|
1603
1603
|
attemptId: row.attempt_id,
|
|
1604
1604
|
runId: row.run_id,
|
|
@@ -1631,16 +1631,16 @@ type GateObservationSql = {
|
|
|
1631
1631
|
step_attempt: number;
|
|
1632
1632
|
assignment_id: string;
|
|
1633
1633
|
effect_id: string;
|
|
1634
|
-
completeness:
|
|
1634
|
+
completeness: KxmGateCompleteness;
|
|
1635
1635
|
spawned: number;
|
|
1636
1636
|
pid: number | null;
|
|
1637
1637
|
exit_code: number | null;
|
|
1638
1638
|
signal: string | null;
|
|
1639
1639
|
exit_observed: number;
|
|
1640
1640
|
close_observed: number;
|
|
1641
|
-
stop_cause:
|
|
1642
|
-
signals_attempted:
|
|
1643
|
-
error_class:
|
|
1641
|
+
stop_cause: KxmGateStopCause;
|
|
1642
|
+
signals_attempted: KxmGateSignalsAttempted;
|
|
1643
|
+
error_class: KxmGateErrorClass | null;
|
|
1644
1644
|
stdout_sha256: string | null;
|
|
1645
1645
|
stdout_bytes: number | null;
|
|
1646
1646
|
stdout_complete: number | null;
|
|
@@ -1656,7 +1656,7 @@ type GateObservationSql = {
|
|
|
1656
1656
|
content_hash: string;
|
|
1657
1657
|
};
|
|
1658
1658
|
|
|
1659
|
-
function gateObservationFromSql(row: GateObservationSql):
|
|
1659
|
+
function gateObservationFromSql(row: GateObservationSql): KxmGateObservationRow {
|
|
1660
1660
|
return {
|
|
1661
1661
|
observationId: row.observation_id,
|
|
1662
1662
|
attemptId: row.attempt_id,
|
|
@@ -1706,13 +1706,13 @@ type GateEvidenceSql = {
|
|
|
1706
1706
|
effect_id: string;
|
|
1707
1707
|
evidence_key: string | null;
|
|
1708
1708
|
kind: "gate";
|
|
1709
|
-
expect:
|
|
1710
|
-
outcome:
|
|
1709
|
+
expect: KxmGateExpect;
|
|
1710
|
+
outcome: KxmGateEvidenceOutcome;
|
|
1711
1711
|
settled_event_id: string;
|
|
1712
1712
|
content_hash: string;
|
|
1713
1713
|
};
|
|
1714
1714
|
|
|
1715
|
-
function gateEvidenceFromSql(row: GateEvidenceSql):
|
|
1715
|
+
function gateEvidenceFromSql(row: GateEvidenceSql): KxmGateEvidenceRow {
|
|
1716
1716
|
return {
|
|
1717
1717
|
evidenceId: row.evidence_id,
|
|
1718
1718
|
attemptId: row.attempt_id,
|
|
@@ -1741,10 +1741,10 @@ type CapabilitySqlRow = {
|
|
|
1741
1741
|
step_attempt: number;
|
|
1742
1742
|
producer_id: string;
|
|
1743
1743
|
capability_hash: string;
|
|
1744
|
-
state:
|
|
1744
|
+
state: KxmAttemptCapabilityRow["state"];
|
|
1745
1745
|
};
|
|
1746
1746
|
|
|
1747
|
-
function capabilityFromRow(row: CapabilitySqlRow | undefined):
|
|
1747
|
+
function capabilityFromRow(row: CapabilitySqlRow | undefined): KxmAttemptCapabilityRow | undefined {
|
|
1748
1748
|
return row
|
|
1749
1749
|
? {
|
|
1750
1750
|
attemptId: row.attempt_id,
|