@kontextmind/kxm 0.7.39 → 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/scripts/roster-policy.mjs +1 -1
- package/plugins/kxm/src/vnext-gate-hash.ts +0 -10
- /package/docs/{vnext → contracts}/effects-and-recovery.md +0 -0
- /package/docs/{vnext → contracts}/lifecycles.md +0 -0
- /package/docs/{vnext → contracts}/terminology.md +0 -0
- /package/examples/{vnext → project}/.kxm/agents/coordinator.yaml +0 -0
- /package/examples/{vnext → project}/.kxm/agents/critic-1.yaml +0 -0
- /package/examples/{vnext → project}/.kxm/agents/critic-2.yaml +0 -0
- /package/examples/{vnext → project}/.kxm/agents/implementer.yaml +0 -0
- /package/examples/{vnext → project}/.kxm/agents/planner.yaml +0 -0
- /package/examples/{vnext → project}/.kxm/agents/reproducer.yaml +0 -0
- /package/examples/{vnext → project}/.kxm/agents/reviewer.yaml +0 -0
- /package/examples/{vnext → project}/.kxm/gates.yaml +0 -0
- /package/examples/{vnext → project}/.kxm/models/critic-claude.yaml +0 -0
- /package/examples/{vnext → project}/.kxm/models/critic-gemini.yaml +0 -0
- /package/examples/{vnext → project}/.kxm/models/critic-grok.yaml +0 -0
- /package/examples/{vnext → project}/.kxm/models/implementation.yaml +0 -0
- /package/examples/{vnext → project}/.kxm/models/primary.yaml +0 -0
- /package/examples/{vnext → project}/.kxm/prices.yaml +0 -0
- /package/examples/{vnext → project}/.kxm/project/env.yaml +0 -0
- /package/examples/{vnext → project}/.kxm/project.yaml +0 -0
- /package/examples/{vnext → project}/.kxm/repo/repo.yaml +0 -0
- /package/examples/{vnext → project}/.kxm/workflows/default.yaml +0 -0
- /package/examples/{vnext → project}/.kxm/workflows/fix.yaml +0 -0
- /package/examples/{vnext → project}/.kxm/workflows/improve.yaml +0 -0
- /package/examples/{vnext → project}/records/assignment-result-recorded.json +0 -0
- /package/examples/{vnext → project}/records/assignment-result.json +0 -0
- /package/examples/{vnext → project}/records/context-candidate.json +0 -0
- /package/examples/{vnext → project}/records/delivery-manifest.json +0 -0
- /package/examples/{vnext → project}/records/effect-uncertainty-resolved-sync.json +0 -0
- /package/examples/{vnext → project}/records/effect-uncertainty-resolved.json +0 -0
- /package/examples/{vnext → project}/records/run-created.json +0 -0
- /package/examples/{vnext → project}/records/sync-event.json +0 -0
- /package/examples/{vnext → project}/repositories/api/.kxm/repo/env.yaml +0 -0
- /package/examples/{vnext → project}/repositories/api/.kxm/repo/repo.yaml +0 -0
- /package/examples/{vnext → project}/repositories/web/.kxm/repo/repo.yaml +0 -0
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { spawn, type ChildProcess } from "node:child_process";
|
|
2
2
|
import { createHash } from "node:crypto";
|
|
3
|
-
import type {
|
|
4
|
-
import type {
|
|
5
|
-
import { runtimeError } from "./
|
|
3
|
+
import type { KxmGateObservationInput } from "./engine-gate-records.ts";
|
|
4
|
+
import type { KxmGateErrorClass, KxmGateStopCause } from "./runtime-store.ts";
|
|
5
|
+
import { runtimeError } from "./runtime-store.ts";
|
|
6
6
|
import { assertCommandSeatbelt } from "./safety-integrity.ts";
|
|
7
7
|
|
|
8
8
|
const MAX_DIRECT_TIMER_MS = 2_147_483_647;
|
|
@@ -10,47 +10,47 @@ export const COMMAND_TERM_GRACE_MS = 2000;
|
|
|
10
10
|
export const COMMAND_FINAL_WAIT_MS = 2000;
|
|
11
11
|
export const COMMAND_LINGER_MS = 2000;
|
|
12
12
|
|
|
13
|
-
export interface
|
|
13
|
+
export interface KxmCommandDefinition {
|
|
14
14
|
readonly kind: "command";
|
|
15
15
|
readonly argv: readonly string[];
|
|
16
16
|
readonly timeoutMs: number;
|
|
17
17
|
readonly cwd?: "control";
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
export interface
|
|
20
|
+
export interface KxmCommandGateSeams {
|
|
21
21
|
timing?: { termGraceMs?: number; finalWaitMs?: number; lingerMs?: number } | undefined;
|
|
22
|
-
beforeSpawn?: ((definition:
|
|
22
|
+
beforeSpawn?: ((definition: KxmCommandDefinition) => void) | undefined;
|
|
23
23
|
afterSpawned?: ((pid: number) => void) | undefined;
|
|
24
|
-
afterStopRequested?: ((cause:
|
|
24
|
+
afterStopRequested?: ((cause: KxmGateStopCause) => void) | undefined;
|
|
25
25
|
afterChildClose?: (() => void) | undefined;
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
export const
|
|
28
|
+
export const kxmCommandGateSeams: KxmCommandGateSeams = {};
|
|
29
29
|
|
|
30
|
-
export type
|
|
31
|
-
| { kind: "no-start"; observation:
|
|
32
|
-
| { kind: "complete"; observation:
|
|
33
|
-
| { kind: "uncertain"; reason: string; observation:
|
|
30
|
+
export type KxmCommandOutcome =
|
|
31
|
+
| { kind: "no-start"; observation: KxmGateObservationInput }
|
|
32
|
+
| { kind: "complete"; observation: KxmGateObservationInput }
|
|
33
|
+
| { kind: "uncertain"; reason: string; observation: KxmGateObservationInput };
|
|
34
34
|
|
|
35
35
|
/**
|
|
36
36
|
* Post-spawn ChildProcess `error` is an uncertain stopCause `error`.
|
|
37
37
|
* The fold reason enum has no post-spawn spawn-error member, so the closed
|
|
38
38
|
* classification keeps errorClass `stream-error` rather than adding a store enum.
|
|
39
39
|
*/
|
|
40
|
-
export const POST_SPAWN_CHILD_ERROR_CLASS:
|
|
40
|
+
export const POST_SPAWN_CHILD_ERROR_CLASS: KxmGateErrorClass = "stream-error";
|
|
41
41
|
|
|
42
|
-
export interface
|
|
43
|
-
requestStop(cause:
|
|
42
|
+
export interface KxmCommandObserver {
|
|
43
|
+
requestStop(cause: KxmGateStopCause, errorClass?: KxmGateErrorClass): void;
|
|
44
44
|
beginBoundedCleanup(): void;
|
|
45
|
-
run(): Promise<
|
|
45
|
+
run(): Promise<KxmCommandOutcome>;
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
export function createCommandObserver(input: {
|
|
49
|
-
definition:
|
|
49
|
+
definition: KxmCommandDefinition;
|
|
50
50
|
cwd: string;
|
|
51
51
|
signal: AbortSignal;
|
|
52
52
|
onSpawned: (pid: number) => void;
|
|
53
|
-
}):
|
|
53
|
+
}): KxmCommandObserver {
|
|
54
54
|
return new CommandObserver(input);
|
|
55
55
|
}
|
|
56
56
|
|
|
@@ -70,8 +70,8 @@ class StreamDigest {
|
|
|
70
70
|
}
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
-
class CommandObserver implements
|
|
74
|
-
private readonly definition:
|
|
73
|
+
class CommandObserver implements KxmCommandObserver {
|
|
74
|
+
private readonly definition: KxmCommandDefinition;
|
|
75
75
|
private readonly cwd: string;
|
|
76
76
|
private readonly signal: AbortSignal;
|
|
77
77
|
private readonly onSpawned: (pid: number) => void;
|
|
@@ -88,9 +88,9 @@ class CommandObserver implements VnextCommandObserver {
|
|
|
88
88
|
private closeObserved = 0 as 0 | 1;
|
|
89
89
|
private exitCode: number | null = null;
|
|
90
90
|
private signalName: string | null = null;
|
|
91
|
-
private stopCause:
|
|
91
|
+
private stopCause: KxmGateStopCause = "none";
|
|
92
92
|
private signalsAttempted: "none" | "term" | "term-kill" = "none";
|
|
93
|
-
private errorClass:
|
|
93
|
+
private errorClass: KxmGateErrorClass | null = null;
|
|
94
94
|
private startedAt: string;
|
|
95
95
|
private finishedAt: string | null = null;
|
|
96
96
|
private readonly startedMono = process.hrtime.bigint();
|
|
@@ -100,11 +100,11 @@ class CommandObserver implements VnextCommandObserver {
|
|
|
100
100
|
private termTimer: NodeJS.Timeout | undefined;
|
|
101
101
|
private finalTimer: NodeJS.Timeout | undefined;
|
|
102
102
|
private lingerTimer: NodeJS.Timeout | undefined;
|
|
103
|
-
private resolveOutcome: ((outcome:
|
|
103
|
+
private resolveOutcome: ((outcome: KxmCommandOutcome) => void) | undefined;
|
|
104
104
|
private readonly abortListener: () => void;
|
|
105
105
|
|
|
106
106
|
constructor(input: {
|
|
107
|
-
definition:
|
|
107
|
+
definition: KxmCommandDefinition;
|
|
108
108
|
cwd: string;
|
|
109
109
|
signal: AbortSignal;
|
|
110
110
|
onSpawned: (pid: number) => void;
|
|
@@ -113,7 +113,7 @@ class CommandObserver implements VnextCommandObserver {
|
|
|
113
113
|
this.cwd = input.cwd;
|
|
114
114
|
this.signal = input.signal;
|
|
115
115
|
this.onSpawned = input.onSpawned;
|
|
116
|
-
const timing =
|
|
116
|
+
const timing = kxmCommandGateSeams.timing;
|
|
117
117
|
this.termGraceMs = clampTimer(timing?.termGraceMs ?? COMMAND_TERM_GRACE_MS);
|
|
118
118
|
this.finalWaitMs = clampTimer(timing?.finalWaitMs ?? COMMAND_FINAL_WAIT_MS);
|
|
119
119
|
this.lingerMs = clampTimer(timing?.lingerMs ?? COMMAND_LINGER_MS);
|
|
@@ -121,12 +121,12 @@ class CommandObserver implements VnextCommandObserver {
|
|
|
121
121
|
this.abortListener = () => this.requestStop("cancel");
|
|
122
122
|
}
|
|
123
123
|
|
|
124
|
-
requestStop(cause:
|
|
124
|
+
requestStop(cause: KxmGateStopCause, errorClass?: KxmGateErrorClass): void {
|
|
125
125
|
if (cause === "none") return;
|
|
126
126
|
if (this.stopCause === "none") {
|
|
127
127
|
this.stopCause = cause;
|
|
128
128
|
if (errorClass && this.errorClass === null) this.errorClass = errorClass;
|
|
129
|
-
|
|
129
|
+
kxmCommandGateSeams.afterStopRequested?.(cause);
|
|
130
130
|
this.clearTimer("linger");
|
|
131
131
|
this.beginSignalSequence();
|
|
132
132
|
return;
|
|
@@ -139,7 +139,7 @@ class CommandObserver implements VnextCommandObserver {
|
|
|
139
139
|
this.requestStop("error", "lost-close");
|
|
140
140
|
}
|
|
141
141
|
|
|
142
|
-
run(): Promise<
|
|
142
|
+
run(): Promise<KxmCommandOutcome> {
|
|
143
143
|
return new Promise((resolve) => {
|
|
144
144
|
this.resolveOutcome = resolve;
|
|
145
145
|
try {
|
|
@@ -161,7 +161,7 @@ class CommandObserver implements VnextCommandObserver {
|
|
|
161
161
|
return;
|
|
162
162
|
}
|
|
163
163
|
try {
|
|
164
|
-
|
|
164
|
+
kxmCommandGateSeams.beforeSpawn?.(this.definition);
|
|
165
165
|
} catch {
|
|
166
166
|
this.finishNoStart("validation");
|
|
167
167
|
return;
|
|
@@ -279,9 +279,9 @@ class CommandObserver implements VnextCommandObserver {
|
|
|
279
279
|
this.clearTimer("timeout");
|
|
280
280
|
child.stdout?.destroy();
|
|
281
281
|
child.stderr?.destroy();
|
|
282
|
-
|
|
282
|
+
dropKxmCommandStopCapability(this);
|
|
283
283
|
try {
|
|
284
|
-
|
|
284
|
+
kxmCommandGateSeams.afterChildClose?.();
|
|
285
285
|
} catch {
|
|
286
286
|
// test seam
|
|
287
287
|
}
|
|
@@ -306,7 +306,7 @@ class CommandObserver implements VnextCommandObserver {
|
|
|
306
306
|
this.requestStop("error", "recording-error");
|
|
307
307
|
}
|
|
308
308
|
try {
|
|
309
|
-
|
|
309
|
+
kxmCommandGateSeams.afterSpawned?.(this.observedPid);
|
|
310
310
|
} catch {
|
|
311
311
|
this.requestStop("error", "recording-error");
|
|
312
312
|
}
|
|
@@ -429,7 +429,7 @@ class CommandObserver implements VnextCommandObserver {
|
|
|
429
429
|
});
|
|
430
430
|
}
|
|
431
431
|
|
|
432
|
-
private uncertaintyReason(observation:
|
|
432
|
+
private uncertaintyReason(observation: KxmGateObservationInput): string {
|
|
433
433
|
// Requested primary cause is independent of the later observed exit signal.
|
|
434
434
|
// Signal-termination is only for an externally signalled exit with no earlier request.
|
|
435
435
|
if (this.stopCause === "timeout") return "timeout";
|
|
@@ -443,7 +443,7 @@ class CommandObserver implements VnextCommandObserver {
|
|
|
443
443
|
return "stop-error";
|
|
444
444
|
}
|
|
445
445
|
|
|
446
|
-
private snapshot():
|
|
446
|
+
private snapshot(): KxmGateObservationInput {
|
|
447
447
|
return this.baseObservation({
|
|
448
448
|
completeness: "complete",
|
|
449
449
|
spawned: this.spawnSeen,
|
|
@@ -466,11 +466,11 @@ class CommandObserver implements VnextCommandObserver {
|
|
|
466
466
|
}
|
|
467
467
|
|
|
468
468
|
private baseObservation(
|
|
469
|
-
facts: Omit<
|
|
469
|
+
facts: Omit<KxmGateObservationInput, "checkedCount" | "failedCount" | "elapsedMs" | "startedAt"> & {
|
|
470
470
|
elapsedMs?: number;
|
|
471
471
|
startedAt?: string;
|
|
472
472
|
},
|
|
473
|
-
):
|
|
473
|
+
): KxmGateObservationInput {
|
|
474
474
|
const elapsedNs = process.hrtime.bigint() - this.startedMono;
|
|
475
475
|
return {
|
|
476
476
|
...facts,
|
|
@@ -481,7 +481,7 @@ class CommandObserver implements VnextCommandObserver {
|
|
|
481
481
|
};
|
|
482
482
|
}
|
|
483
483
|
|
|
484
|
-
private handoffWith(outcome:
|
|
484
|
+
private handoffWith(outcome: KxmCommandOutcome): void {
|
|
485
485
|
if (this.handedOff) return;
|
|
486
486
|
this.handedOff = true;
|
|
487
487
|
this.canSignal = false;
|
|
@@ -491,7 +491,7 @@ class CommandObserver implements VnextCommandObserver {
|
|
|
491
491
|
this.clearTimer("final");
|
|
492
492
|
this.clearTimer("linger");
|
|
493
493
|
this.child?.unref();
|
|
494
|
-
|
|
494
|
+
dropKxmCommandStopCapability(this);
|
|
495
495
|
this.resolveOutcome?.(outcome);
|
|
496
496
|
}
|
|
497
497
|
|
|
@@ -502,11 +502,11 @@ class CommandObserver implements VnextCommandObserver {
|
|
|
502
502
|
}
|
|
503
503
|
}
|
|
504
504
|
|
|
505
|
-
function
|
|
505
|
+
function dropKxmCommandStopCapability(observer: CommandObserver): void {
|
|
506
506
|
observer.requestStop = () => undefined;
|
|
507
507
|
}
|
|
508
508
|
|
|
509
|
-
function isCompleteNormal(observation:
|
|
509
|
+
function isCompleteNormal(observation: KxmGateObservationInput): boolean {
|
|
510
510
|
return observation.completeness === "complete"
|
|
511
511
|
&& observation.spawned === 1
|
|
512
512
|
&& typeof observation.pid === "number"
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import type { JsonObject, JsonValue } from "./
|
|
1
|
+
import type { JsonObject, JsonValue } from "./project-config.ts";
|
|
2
2
|
|
|
3
|
-
export const
|
|
4
|
-
export const
|
|
3
|
+
export const KXM_COMPILED_WORKFLOW_SCHEMA = "kxm.compiled-workflow.v1";
|
|
4
|
+
export const KXM_RESERVED_STEP_KINDS = ["workflow"] as const;
|
|
5
5
|
|
|
6
6
|
const WORKFLOW_SCHEMA = "kxm.workflow.v1";
|
|
7
7
|
const STEP_ID_PATTERN = /^[a-z][a-z0-9]*(?:[-_][a-z0-9]+)*$/;
|
|
@@ -22,30 +22,30 @@ export type ReadonlyJsonValue =
|
|
|
22
22
|
| { readonly [key: string]: ReadonlyJsonValue };
|
|
23
23
|
export type ReadonlyJsonObject = { readonly [key: string]: ReadonlyJsonValue };
|
|
24
24
|
|
|
25
|
-
export type
|
|
26
|
-
export type
|
|
27
|
-
export type
|
|
25
|
+
export type KxmTerminalStatus = "completed" | "failed" | "cancelled";
|
|
26
|
+
export type KxmCompiledStepKind = "agent" | "moa" | "gate" | "approval" | "wait";
|
|
27
|
+
export type KxmRepositoryAccess = "none" | "read" | "write";
|
|
28
28
|
|
|
29
|
-
export type
|
|
29
|
+
export type KxmCompiledTransition =
|
|
30
30
|
| { readonly to: "step"; readonly target: string; readonly edge: "forward" | "back"; readonly maxTransitions?: number }
|
|
31
|
-
| { readonly to: "terminal"; readonly terminalStatus:
|
|
31
|
+
| { readonly to: "terminal"; readonly terminalStatus: KxmTerminalStatus; readonly maxTransitions?: number };
|
|
32
32
|
|
|
33
|
-
export interface
|
|
33
|
+
export interface KxmCompiledProducerPolicy {
|
|
34
34
|
readonly minimumProducers: number;
|
|
35
35
|
readonly eligibleAgents: readonly string[];
|
|
36
36
|
readonly acceptedStatuses: readonly ["passed"];
|
|
37
37
|
readonly degradation?: { readonly minimumProducers: number };
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
-
export interface
|
|
40
|
+
export interface KxmCompiledEvidence {
|
|
41
41
|
readonly key: string;
|
|
42
42
|
readonly kind: "assignment-result" | "gate" | "receipt" | "approval" | "artifact";
|
|
43
43
|
readonly minimum: number;
|
|
44
44
|
readonly reusableAcrossAttempts: boolean;
|
|
45
|
-
readonly producerPolicy?:
|
|
45
|
+
readonly producerPolicy?: KxmCompiledProducerPolicy;
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
-
export interface
|
|
48
|
+
export interface KxmCompiledAssignments {
|
|
49
49
|
readonly allowedAgents: readonly string[]; // declared, else [step.agent] when present, else []
|
|
50
50
|
readonly minimum: number;
|
|
51
51
|
readonly target: number;
|
|
@@ -56,13 +56,13 @@ export interface VnextCompiledAssignments {
|
|
|
56
56
|
readonly distinctBy: readonly ("provider" | "model" | "profile")[];
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
-
export interface
|
|
59
|
+
export interface KxmCompiledJoin {
|
|
60
60
|
readonly strategy: "all" | "all-settled" | "quorum" | "first-success";
|
|
61
61
|
readonly minimumPassed?: number;
|
|
62
62
|
readonly cancelRemaining?: boolean;
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
-
interface
|
|
65
|
+
interface KxmCompiledStepBase {
|
|
66
66
|
readonly id: string;
|
|
67
67
|
readonly index: number;
|
|
68
68
|
readonly description?: string;
|
|
@@ -70,26 +70,26 @@ interface VnextCompiledStepBase {
|
|
|
70
70
|
readonly maxAttempts: number;
|
|
71
71
|
readonly timeoutMs?: number;
|
|
72
72
|
readonly safeSpeculation: boolean;
|
|
73
|
-
readonly repositories: Readonly<Record<string,
|
|
73
|
+
readonly repositories: Readonly<Record<string, KxmRepositoryAccess>>;
|
|
74
74
|
readonly tools?: ReadonlyJsonObject;
|
|
75
75
|
readonly secrets: readonly ReadonlyJsonObject[];
|
|
76
76
|
readonly model?: ReadonlyJsonValue;
|
|
77
|
-
readonly requiredEvidence: readonly
|
|
77
|
+
readonly requiredEvidence: readonly KxmCompiledEvidence[];
|
|
78
78
|
readonly outcomes: readonly string[];
|
|
79
|
-
readonly transitions: Readonly<Record<string,
|
|
79
|
+
readonly transitions: Readonly<Record<string, KxmCompiledTransition>>;
|
|
80
80
|
readonly requiresPlanHash: boolean;
|
|
81
|
-
readonly assignments:
|
|
82
|
-
readonly join:
|
|
81
|
+
readonly assignments: KxmCompiledAssignments;
|
|
82
|
+
readonly join: KxmCompiledJoin;
|
|
83
83
|
}
|
|
84
84
|
|
|
85
|
-
export type
|
|
86
|
-
| (
|
|
87
|
-
| (
|
|
88
|
-
| (
|
|
89
|
-
| (
|
|
85
|
+
export type KxmCompiledStep =
|
|
86
|
+
| (KxmCompiledStepBase & { readonly kind: "agent" | "moa"; readonly agent: string })
|
|
87
|
+
| (KxmCompiledStepBase & { readonly kind: "gate"; readonly gate: string; readonly expect: "pass" | "fail" })
|
|
88
|
+
| (KxmCompiledStepBase & { readonly kind: "approval" })
|
|
89
|
+
| (KxmCompiledStepBase & { readonly kind: "wait"; readonly signal: string });
|
|
90
90
|
|
|
91
|
-
export interface
|
|
92
|
-
readonly schema: typeof
|
|
91
|
+
export interface KxmCompiledPlan {
|
|
92
|
+
readonly schema: typeof KXM_COMPILED_WORKFLOW_SCHEMA;
|
|
93
93
|
readonly workflowId: string;
|
|
94
94
|
readonly sourcePath?: string;
|
|
95
95
|
readonly coordinator: string;
|
|
@@ -104,13 +104,13 @@ export interface VnextCompiledPlan {
|
|
|
104
104
|
readonly hasBackEdges: boolean;
|
|
105
105
|
readonly entryStepId: string;
|
|
106
106
|
readonly order: readonly string[];
|
|
107
|
-
readonly steps: Readonly<Record<string,
|
|
107
|
+
readonly steps: Readonly<Record<string, KxmCompiledStep>>;
|
|
108
108
|
readonly reproOracle?: { readonly stageId: string; readonly evidenceKey: string };
|
|
109
109
|
readonly planHash?: { readonly stageId: string; readonly evidenceKey: string };
|
|
110
110
|
readonly requirePlanHash: readonly string[];
|
|
111
111
|
}
|
|
112
112
|
|
|
113
|
-
export interface
|
|
113
|
+
export interface KxmEngineCompileIssue {
|
|
114
114
|
readonly code: string;
|
|
115
115
|
readonly workflowId: string;
|
|
116
116
|
readonly stepId?: string;
|
|
@@ -118,18 +118,18 @@ export interface VnextEngineCompileIssue {
|
|
|
118
118
|
readonly message: string;
|
|
119
119
|
}
|
|
120
120
|
|
|
121
|
-
export class
|
|
122
|
-
readonly issues: readonly
|
|
121
|
+
export class KxmEngineCompileError extends Error {
|
|
122
|
+
readonly issues: readonly KxmEngineCompileIssue[];
|
|
123
123
|
|
|
124
|
-
constructor(issues: readonly
|
|
124
|
+
constructor(issues: readonly KxmEngineCompileIssue[]) {
|
|
125
125
|
const sorted = sortIssues(issues);
|
|
126
126
|
super(sorted.map((issue) => `${issue.workflowId}: ${issue.code}: ${issue.message}`).join("\n"));
|
|
127
|
-
this.name = "
|
|
127
|
+
this.name = "KxmEngineCompileError";
|
|
128
128
|
this.issues = sorted;
|
|
129
129
|
}
|
|
130
130
|
}
|
|
131
131
|
|
|
132
|
-
export interface
|
|
132
|
+
export interface KxmWorkflowCompileInput {
|
|
133
133
|
readonly id: string;
|
|
134
134
|
readonly value: JsonObject;
|
|
135
135
|
readonly logicalPath?: string;
|
|
@@ -137,10 +137,10 @@ export interface VnextWorkflowCompileInput {
|
|
|
137
137
|
|
|
138
138
|
interface IssueSink {
|
|
139
139
|
workflowId: string;
|
|
140
|
-
issues:
|
|
140
|
+
issues: KxmEngineCompileIssue[];
|
|
141
141
|
}
|
|
142
142
|
|
|
143
|
-
export function
|
|
143
|
+
export function compileKxmWorkflow(input: KxmWorkflowCompileInput): KxmCompiledPlan {
|
|
144
144
|
const sink: IssueSink = { workflowId: input.id, issues: [] };
|
|
145
145
|
const value = input.value;
|
|
146
146
|
if (value.schema !== WORKFLOW_SCHEMA) {
|
|
@@ -169,7 +169,7 @@ export function compileVnextWorkflow(input: VnextWorkflowCompileInput): VnextCom
|
|
|
169
169
|
const limitsObject = objectValue(value.limits);
|
|
170
170
|
const limits = compileLimits(limitsObject, sink);
|
|
171
171
|
const requirePlanHash = names(value.requirePlanHash);
|
|
172
|
-
const compiledSteps:
|
|
172
|
+
const compiledSteps: KxmCompiledStep[] = [];
|
|
173
173
|
let hasBackEdges = false;
|
|
174
174
|
|
|
175
175
|
for (const [index, step] of rawSteps.entries()) {
|
|
@@ -193,17 +193,17 @@ export function compileVnextWorkflow(input: VnextWorkflowCompileInput): VnextCom
|
|
|
193
193
|
}
|
|
194
194
|
}
|
|
195
195
|
|
|
196
|
-
if (sink.issues.length > 0) throw new
|
|
196
|
+
if (sink.issues.length > 0) throw new KxmEngineCompileError(sink.issues);
|
|
197
197
|
|
|
198
198
|
const order = compiledSteps.map((step) => step.id);
|
|
199
|
-
const steps = Object.create(null) as Record<string,
|
|
199
|
+
const steps = Object.create(null) as Record<string, KxmCompiledStep>;
|
|
200
200
|
for (const step of compiledSteps) steps[step.id] = step;
|
|
201
201
|
const entryStepId = order[0];
|
|
202
|
-
if (!entryStepId) throw new
|
|
202
|
+
if (!entryStepId) throw new KxmEngineCompileError(sink.issues);
|
|
203
203
|
const coordinator = stringValue(value.coordinator) ?? "coordinator";
|
|
204
204
|
const transitionBudget = limits.maxTransitions ?? order.length;
|
|
205
|
-
const plan:
|
|
206
|
-
schema:
|
|
205
|
+
const plan: KxmCompiledPlan = {
|
|
206
|
+
schema: KXM_COMPILED_WORKFLOW_SCHEMA,
|
|
207
207
|
workflowId: input.id,
|
|
208
208
|
...(input.logicalPath !== undefined ? { sourcePath: input.logicalPath } : {}),
|
|
209
209
|
coordinator,
|
|
@@ -223,7 +223,7 @@ export function compileVnextWorkflow(input: VnextWorkflowCompileInput): VnextCom
|
|
|
223
223
|
function compileLimits(
|
|
224
224
|
limits: JsonObject | undefined,
|
|
225
225
|
sink: IssueSink,
|
|
226
|
-
):
|
|
226
|
+
): KxmCompiledPlan["limits"] {
|
|
227
227
|
if (!limits) return {};
|
|
228
228
|
const compiled: {
|
|
229
229
|
maxTransitions?: number;
|
|
@@ -260,7 +260,7 @@ function compileStep(
|
|
|
260
260
|
stepIndex: ReadonlyMap<string, number>,
|
|
261
261
|
requirePlanHash: readonly string[],
|
|
262
262
|
sink: IssueSink,
|
|
263
|
-
):
|
|
263
|
+
): KxmCompiledStep | undefined {
|
|
264
264
|
const id = stringValue(step.id);
|
|
265
265
|
if (!id || !isStepId(id) || stepIndex.get(id) !== index) return undefined;
|
|
266
266
|
const kind = stringValue(step.kind);
|
|
@@ -299,7 +299,7 @@ function compileStep(
|
|
|
299
299
|
const requiredEvidence = compileEvidence(step, id, sink);
|
|
300
300
|
const transitions = compileTransitions(step, id, index, stepIndex, sink);
|
|
301
301
|
const outcomes = Object.keys(transitions).sort(compareCodeUnits);
|
|
302
|
-
const orderedTransitions = Object.create(null) as Record<string,
|
|
302
|
+
const orderedTransitions = Object.create(null) as Record<string, KxmCompiledTransition>;
|
|
303
303
|
for (const outcome of outcomes) {
|
|
304
304
|
const transition = transitions[outcome];
|
|
305
305
|
if (transition) orderedTransitions[outcome] = transition;
|
|
@@ -307,7 +307,7 @@ function compileStep(
|
|
|
307
307
|
|
|
308
308
|
const description = stringValue(step.description);
|
|
309
309
|
const instructions = stringValue(step.instructions);
|
|
310
|
-
const base:
|
|
310
|
+
const base: KxmCompiledStepBase = {
|
|
311
311
|
id,
|
|
312
312
|
index,
|
|
313
313
|
...(description !== undefined ? { description } : {}),
|
|
@@ -347,11 +347,11 @@ function compileAssignments(
|
|
|
347
347
|
stepId: string,
|
|
348
348
|
primaryAgentId: string | undefined,
|
|
349
349
|
sink: IssueSink,
|
|
350
|
-
):
|
|
350
|
+
): KxmCompiledAssignments {
|
|
351
351
|
const assignment = objectValue(step.assignments);
|
|
352
352
|
const declaredAgents = assignment ? names(assignment.allowedAgents) : [];
|
|
353
353
|
const allowedAgents = declaredAgents.length > 0 ? declaredAgents : primaryAgentId ? [primaryAgentId] : [];
|
|
354
|
-
// Mirrors validateWorkflow in
|
|
354
|
+
// Mirrors validateWorkflow in project-config.ts (minimum 1, target = minimum, maximum = target, maxParallel = maximum) for every kind. Do not resolve kind-conditional defaults here; the loader is the ceiling authority. Phase 7 changes both together.
|
|
355
355
|
const minimum = compileCountField(assignment?.minimum, 1, `${stepId}.assignments.minimum`, stepId, sink);
|
|
356
356
|
const target = compileCountField(assignment?.target, minimum, `${stepId}.assignments.target`, stepId, sink);
|
|
357
357
|
const maximum = compileCountField(assignment?.maximum, target, `${stepId}.assignments.maximum`, stepId, sink);
|
|
@@ -382,15 +382,15 @@ function compileAssignments(
|
|
|
382
382
|
};
|
|
383
383
|
}
|
|
384
384
|
|
|
385
|
-
function compileJoin(step: JsonObject, stepId: string, sink: IssueSink):
|
|
385
|
+
function compileJoin(step: JsonObject, stepId: string, sink: IssueSink): KxmCompiledJoin {
|
|
386
386
|
const join = objectValue(step.join);
|
|
387
387
|
if (!join) return { strategy: "all" };
|
|
388
388
|
const declared = stringValue(join.strategy);
|
|
389
389
|
const strategy = declared && JOIN_STRATEGIES.has(declared)
|
|
390
|
-
? declared as
|
|
390
|
+
? declared as KxmCompiledJoin["strategy"]
|
|
391
391
|
: "all";
|
|
392
392
|
const minimumPassed = compileOptionalCount(join.minimumPassed, `${stepId}.join.minimumPassed`, stepId, sink);
|
|
393
|
-
const compiled:
|
|
393
|
+
const compiled: KxmCompiledJoin = {
|
|
394
394
|
strategy,
|
|
395
395
|
...(minimumPassed !== undefined ? { minimumPassed } : {}),
|
|
396
396
|
...(typeof join.cancelRemaining === "boolean" ? { cancelRemaining: join.cancelRemaining } : {}),
|
|
@@ -398,8 +398,8 @@ function compileJoin(step: JsonObject, stepId: string, sink: IssueSink): VnextCo
|
|
|
398
398
|
return compiled;
|
|
399
399
|
}
|
|
400
400
|
|
|
401
|
-
function compileEvidence(step: JsonObject, stepId: string, sink: IssueSink):
|
|
402
|
-
const evidence:
|
|
401
|
+
function compileEvidence(step: JsonObject, stepId: string, sink: IssueSink): KxmCompiledEvidence[] {
|
|
402
|
+
const evidence: KxmCompiledEvidence[] = [];
|
|
403
403
|
const seen = new Set<string>();
|
|
404
404
|
for (const candidate of valuesOf(step, "requiredEvidence")) {
|
|
405
405
|
const requirement = objectValue(candidate);
|
|
@@ -415,9 +415,9 @@ function compileEvidence(step: JsonObject, stepId: string, sink: IssueSink): Vne
|
|
|
415
415
|
if (!kind || !EVIDENCE_KINDS.has(kind)) continue;
|
|
416
416
|
const minimum = compileCountField(requirement.minimum, 1, `${stepId}.${key}.minimum`, stepId, sink);
|
|
417
417
|
const producerPolicy = objectValue(requirement.producerPolicy);
|
|
418
|
-
const compiled:
|
|
418
|
+
const compiled: KxmCompiledEvidence = {
|
|
419
419
|
key,
|
|
420
|
-
kind: kind as
|
|
420
|
+
kind: kind as KxmCompiledEvidence["kind"],
|
|
421
421
|
minimum,
|
|
422
422
|
reusableAcrossAttempts: requirement.reusableAcrossAttempts === true,
|
|
423
423
|
...(producerPolicy ? { producerPolicy: compileProducerPolicy(producerPolicy, stepId, key, sink) } : {}),
|
|
@@ -432,7 +432,7 @@ function compileProducerPolicy(
|
|
|
432
432
|
stepId: string,
|
|
433
433
|
key: string,
|
|
434
434
|
sink: IssueSink,
|
|
435
|
-
):
|
|
435
|
+
): KxmCompiledProducerPolicy {
|
|
436
436
|
const minimumProducers = compileCountField(
|
|
437
437
|
policy.minimumProducers,
|
|
438
438
|
1,
|
|
@@ -461,9 +461,9 @@ function compileTransitions(
|
|
|
461
461
|
index: number,
|
|
462
462
|
stepIndex: ReadonlyMap<string, number>,
|
|
463
463
|
sink: IssueSink,
|
|
464
|
-
): Record<string,
|
|
464
|
+
): Record<string, KxmCompiledTransition> {
|
|
465
465
|
const outcomes = objectValue(step.on);
|
|
466
|
-
const compiled = Object.create(null) as Record<string,
|
|
466
|
+
const compiled = Object.create(null) as Record<string, KxmCompiledTransition>;
|
|
467
467
|
if (!outcomes || Object.keys(outcomes).length === 0) {
|
|
468
468
|
pushIssue(sink, { code: "transitions_missing", stepId, message: `${stepId} has no declared transitions` });
|
|
469
469
|
return compiled;
|
|
@@ -499,7 +499,7 @@ function compileTransitions(
|
|
|
499
499
|
}
|
|
500
500
|
compiled[outcome] = {
|
|
501
501
|
to: "terminal",
|
|
502
|
-
terminalStatus: status as
|
|
502
|
+
terminalStatus: status as KxmTerminalStatus,
|
|
503
503
|
...(maxTransitions !== undefined ? { maxTransitions } : {}),
|
|
504
504
|
};
|
|
505
505
|
continue;
|
|
@@ -557,7 +557,7 @@ function parseTransition(value: JsonValue): { target?: string; maxTransitions?:
|
|
|
557
557
|
function compileOracle(
|
|
558
558
|
field: "reproOracle" | "planHash",
|
|
559
559
|
oracle: JsonObject | undefined,
|
|
560
|
-
steps: readonly
|
|
560
|
+
steps: readonly KxmCompiledStep[],
|
|
561
561
|
sink: IssueSink,
|
|
562
562
|
): { stageId: string; evidenceKey: string } | undefined {
|
|
563
563
|
if (!oracle) return undefined;
|
|
@@ -578,12 +578,12 @@ function compileOracle(
|
|
|
578
578
|
return { stageId, evidenceKey };
|
|
579
579
|
}
|
|
580
580
|
|
|
581
|
-
function compileRepositories(value: JsonValue | undefined): Readonly<Record<string,
|
|
581
|
+
function compileRepositories(value: JsonValue | undefined): Readonly<Record<string, KxmRepositoryAccess>> {
|
|
582
582
|
const source = objectValue(value) ?? {};
|
|
583
|
-
const repositories: Record<string,
|
|
583
|
+
const repositories: Record<string, KxmRepositoryAccess> = {};
|
|
584
584
|
for (const [id, access] of Object.entries(source)) {
|
|
585
585
|
if (typeof access === "string" && REPOSITORY_ACCESS.has(access)) {
|
|
586
|
-
repositories[id] = access as
|
|
586
|
+
repositories[id] = access as KxmRepositoryAccess;
|
|
587
587
|
}
|
|
588
588
|
}
|
|
589
589
|
return repositories;
|
|
@@ -707,7 +707,7 @@ function compareOptional(left: string | undefined, right: string | undefined): n
|
|
|
707
707
|
return compareCodeUnits(left, right);
|
|
708
708
|
}
|
|
709
709
|
|
|
710
|
-
function sortIssues(issues: readonly
|
|
710
|
+
function sortIssues(issues: readonly KxmEngineCompileIssue[]): KxmEngineCompileIssue[] {
|
|
711
711
|
return [...issues].sort((left, right) => {
|
|
712
712
|
const byStep = compareOptional(left.stepId, right.stepId);
|
|
713
713
|
if (byStep !== 0) return byStep;
|
|
@@ -717,6 +717,6 @@ function sortIssues(issues: readonly VnextEngineCompileIssue[]): VnextEngineComp
|
|
|
717
717
|
});
|
|
718
718
|
}
|
|
719
719
|
|
|
720
|
-
function pushIssue(sink: IssueSink, issue: Omit<
|
|
720
|
+
function pushIssue(sink: IssueSink, issue: Omit<KxmEngineCompileIssue, "workflowId">): void {
|
|
721
721
|
sink.issues.push({ workflowId: sink.workflowId, ...issue });
|
|
722
722
|
}
|