@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
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { kxmCanonicalJson, type JsonValue } from "./project-config.ts";
|
|
2
|
+
import { kxmSha256 } from "./engine-plan.ts";
|
|
3
|
+
|
|
4
|
+
export function gateDefinitionHash(id: string, definition: JsonValue): string {
|
|
5
|
+
return kxmSha256(kxmCanonicalJson({ id, ...(definition as object) } as JsonValue));
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function gateRegistryHash(registryValue: JsonValue): string {
|
|
9
|
+
return kxmSha256(kxmCanonicalJson(registryValue));
|
|
10
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { spawnSync } from "node:child_process";
|
|
2
2
|
import { existsSync } from "node:fs";
|
|
3
3
|
import { win32 as win32Path } from "node:path";
|
|
4
|
-
import { defaultSpawn } from "./
|
|
4
|
+
import { defaultSpawn } from "./oneshot-process.ts";
|
|
5
5
|
|
|
6
6
|
export const DEFAULT_HARNESS = "pi";
|
|
7
7
|
export type HarnessMode = "headless" | "either";
|
package/plugins/kxm/src/hub.ts
CHANGED
|
@@ -2166,7 +2166,7 @@ export function createMeshHub(options: MeshHubOptions = {}): MeshHub {
|
|
|
2166
2166
|
const current = requireAgent(request, agentId);
|
|
2167
2167
|
requireProjectAuth(request, current.project);
|
|
2168
2168
|
const presenceOnly = url.searchParams.get("presenceOnly") === "true";
|
|
2169
|
-
if (presenceOnly && current.model !== "
|
|
2169
|
+
if (presenceOnly && current.model !== "tui") {
|
|
2170
2170
|
throw new ProtocolError(403, "presence-only streams are reserved for metadata observers", "presence_stream_forbidden");
|
|
2171
2171
|
}
|
|
2172
2172
|
response.writeHead(200, {
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* first candidate whose harness is installed AND authenticated wins. No
|
|
8
8
|
* candidate is admitted without an authenticated harness (fail closed).
|
|
9
9
|
*
|
|
10
|
-
* This module writes only current
|
|
10
|
+
* This module writes only current KXM project resources:
|
|
11
11
|
* - `.kxm/agents/<role-slug>.yaml` (kxm.agent.v1)
|
|
12
12
|
* - `.kxm/workflows/<slug>.yaml` (kxm.workflow.v1)
|
|
13
13
|
* It never writes retired legacy authority (`.kxm/config`, retired
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
import { existsSync, mkdirSync, writeFileSync } from "node:fs";
|
|
19
19
|
import { dirname, join } from "node:path";
|
|
20
20
|
import { stringify } from "yaml";
|
|
21
|
-
import { NATIVE_HARNESS_PROVIDERS, type HarnessInventory } from "./
|
|
21
|
+
import { NATIVE_HARNESS_PROVIDERS, type HarnessInventory } from "./harness.ts";
|
|
22
22
|
|
|
23
23
|
export interface GuideCandidate {
|
|
24
24
|
readonly vendor: string;
|
|
@@ -482,7 +482,7 @@ function workflowDocument(workflow: GuideWorkflow): Record<string, unknown> {
|
|
|
482
482
|
}
|
|
483
483
|
|
|
484
484
|
/**
|
|
485
|
-
* Render the planned
|
|
485
|
+
* Render the planned KXM resource files (`.kxm/agents/*.yaml`,
|
|
486
486
|
* `.kxm/workflows/*.yaml`). Pure: no disk access.
|
|
487
487
|
*/
|
|
488
488
|
export function renderGuideSetupFiles(projectRoot: string, plan: GuideSetupPlan): GuideSetupFile[] {
|
|
@@ -2,60 +2,60 @@ import { randomUUID } from "node:crypto";
|
|
|
2
2
|
import { existsSync } from "node:fs";
|
|
3
3
|
import { basename, join } from "node:path";
|
|
4
4
|
import {
|
|
5
|
-
|
|
5
|
+
KxmConfigError,
|
|
6
6
|
discoverGitRoot,
|
|
7
|
-
|
|
7
|
+
loadKxmProject,
|
|
8
8
|
assertNoRegisteredGates,
|
|
9
|
-
|
|
9
|
+
planKxmInitialization,
|
|
10
10
|
type JsonObject,
|
|
11
|
-
type
|
|
12
|
-
type
|
|
13
|
-
type
|
|
14
|
-
} from "./
|
|
11
|
+
type KxmConfigIssue,
|
|
12
|
+
type KxmConfigOptions,
|
|
13
|
+
type KxmInitializationPlan,
|
|
14
|
+
} from "./project-config.ts";
|
|
15
15
|
import {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
type
|
|
21
|
-
type
|
|
22
|
-
} from "./
|
|
16
|
+
planKxmLocalBindings,
|
|
17
|
+
readKxmLocalBindings,
|
|
18
|
+
withKxmLocalBindingLock,
|
|
19
|
+
writeKxmLocalBindings,
|
|
20
|
+
type KxmLocalBindingLock,
|
|
21
|
+
type KxmLocalBindingStoreOptions,
|
|
22
|
+
} from "./bindings.ts";
|
|
23
23
|
import {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
type
|
|
32
|
-
type
|
|
33
|
-
} from "./
|
|
24
|
+
commitKxmInitTransaction,
|
|
25
|
+
hasKxmInitTransaction,
|
|
26
|
+
inspectKxmInitTransaction,
|
|
27
|
+
planKxmTemplateRepair,
|
|
28
|
+
prepareAndApplyKxmCreate,
|
|
29
|
+
prepareAndApplyKxmRepair,
|
|
30
|
+
resumeKxmInitTransaction,
|
|
31
|
+
type KxmRepairRuntimeOptions,
|
|
32
|
+
type KxmTemplateRepairPlan,
|
|
33
|
+
} from "./repair.ts";
|
|
34
34
|
import {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
type
|
|
38
|
-
} from "./
|
|
35
|
+
CURRENT_KXM_TEMPLATE_VARIANT,
|
|
36
|
+
renderKxmTemplate,
|
|
37
|
+
type KxmTemplateVariant,
|
|
38
|
+
} from "./template.ts";
|
|
39
39
|
|
|
40
|
-
export interface
|
|
40
|
+
export interface KxmInitOptions extends KxmConfigOptions {
|
|
41
41
|
projectId?: string;
|
|
42
42
|
projectName?: string;
|
|
43
43
|
dryRun?: boolean;
|
|
44
44
|
localStateRoot?: string;
|
|
45
45
|
/** Test/future-template injection; the CLI always uses the current built-in variant. */
|
|
46
|
-
templateVariant?:
|
|
46
|
+
templateVariant?: KxmTemplateVariant;
|
|
47
47
|
/** Fault injection used by crash-recovery tests. */
|
|
48
48
|
testFaultAt?: "prepared" | "first-resource" | "provenance" | "verified";
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
-
export interface
|
|
51
|
+
export interface KxmInitResult {
|
|
52
52
|
action: "planned" | "created" | "joined" | "repaired" | "resumed" | "validated";
|
|
53
|
-
plan:
|
|
53
|
+
plan: KxmInitializationPlan;
|
|
54
54
|
projectRoot?: string;
|
|
55
55
|
configRevision?: string;
|
|
56
56
|
localBindingFile?: string;
|
|
57
57
|
bindingsChanged?: boolean;
|
|
58
|
-
repairPlan?:
|
|
58
|
+
repairPlan?: KxmTemplateRepairPlan;
|
|
59
59
|
resumePending?: boolean;
|
|
60
60
|
transactionKind?: "create" | "repair";
|
|
61
61
|
files: readonly string[];
|
|
@@ -63,14 +63,14 @@ export interface VnextInitResult {
|
|
|
63
63
|
|
|
64
64
|
const PROJECT_ID = /^prj_[A-Za-z0-9][A-Za-z0-9_-]{5,127}$/;
|
|
65
65
|
|
|
66
|
-
function initIssue(code: string, file: string, message: string):
|
|
66
|
+
function initIssue(code: string, file: string, message: string): KxmConfigIssue {
|
|
67
67
|
return { phase: "discovery", code, file, message };
|
|
68
68
|
}
|
|
69
69
|
|
|
70
70
|
function normalizedProjectName(root: string, requested?: string): string {
|
|
71
71
|
const name = (requested ?? basename(root) ?? "KXM Project").trim();
|
|
72
72
|
if (name.length < 1 || name.length > 120 || /[\u0000\r\n]/.test(name)) {
|
|
73
|
-
throw new
|
|
73
|
+
throw new KxmConfigError([initIssue("project_name_invalid", ".kxm/project.yaml", "project name must contain 1-120 characters on one line")]);
|
|
74
74
|
}
|
|
75
75
|
return name;
|
|
76
76
|
}
|
|
@@ -78,12 +78,12 @@ function normalizedProjectName(root: string, requested?: string): string {
|
|
|
78
78
|
function generatedProjectId(requested?: string): string {
|
|
79
79
|
const id = requested?.trim() || `prj_${randomUUID().replaceAll("-", "")}`;
|
|
80
80
|
if (!PROJECT_ID.test(id) || id.length > 144) {
|
|
81
|
-
throw new
|
|
81
|
+
throw new KxmConfigError([initIssue("project_id_invalid", ".kxm/project.yaml", "project ID must satisfy the kxm.project.v1 opaque ID grammar and use the prj_ prefix")]);
|
|
82
82
|
}
|
|
83
83
|
return id;
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
-
function configOptions(options:
|
|
86
|
+
function configOptions(options: KxmInitOptions, repositoryBindings: Readonly<Record<string, string>>): KxmConfigOptions {
|
|
87
87
|
assertNoRegisteredGates(options);
|
|
88
88
|
return {
|
|
89
89
|
...(options.schemasDir === undefined ? {} : { schemasDir: options.schemasDir }),
|
|
@@ -94,17 +94,17 @@ function configOptions(options: VnextInitOptions, repositoryBindings: Readonly<R
|
|
|
94
94
|
}
|
|
95
95
|
|
|
96
96
|
function repairOptions(
|
|
97
|
-
options:
|
|
97
|
+
options: KxmInitOptions,
|
|
98
98
|
repositoryBindings: Readonly<Record<string, string>>,
|
|
99
|
-
):
|
|
99
|
+
): KxmRepairRuntimeOptions {
|
|
100
100
|
return {
|
|
101
101
|
...configOptions(options, repositoryBindings),
|
|
102
|
-
templateVariant: options.templateVariant ??
|
|
102
|
+
templateVariant: options.templateVariant ?? CURRENT_KXM_TEMPLATE_VARIANT,
|
|
103
103
|
...(options.testFaultAt === undefined ? {} : { testFaultAt: options.testFaultAt }),
|
|
104
104
|
};
|
|
105
105
|
}
|
|
106
106
|
|
|
107
|
-
function bindingStoreOptions(options:
|
|
107
|
+
function bindingStoreOptions(options: KxmInitOptions): KxmLocalBindingStoreOptions {
|
|
108
108
|
return {
|
|
109
109
|
...(options.localStateRoot === undefined ? {} : { stateRoot: options.localStateRoot }),
|
|
110
110
|
...(options.schemasDir === undefined ? {} : { schemasDir: options.schemasDir }),
|
|
@@ -119,17 +119,17 @@ function memberRepositoryIds(project: JsonObject): Set<string> { const reposito
|
|
|
119
119
|
}));
|
|
120
120
|
}
|
|
121
121
|
|
|
122
|
-
function completedPlan(projectRoot: string, options:
|
|
123
|
-
return
|
|
122
|
+
function completedPlan(projectRoot: string, options: KxmConfigOptions): KxmInitializationPlan {
|
|
123
|
+
return planKxmInitialization(projectRoot, options);
|
|
124
124
|
}
|
|
125
125
|
|
|
126
126
|
function finalizeBindingUpdate(
|
|
127
|
-
bundle: ReturnType<typeof
|
|
128
|
-
persisted: ReturnType<typeof
|
|
127
|
+
bundle: ReturnType<typeof loadKxmProject>,
|
|
128
|
+
persisted: ReturnType<typeof readKxmLocalBindings>,
|
|
129
129
|
allBindings: Readonly<Record<string, string>>,
|
|
130
130
|
explicitBindings: Readonly<Record<string, string>>,
|
|
131
|
-
storeOptions:
|
|
132
|
-
mutationLock:
|
|
131
|
+
storeOptions: KxmLocalBindingStoreOptions,
|
|
132
|
+
mutationLock: KxmLocalBindingLock | undefined,
|
|
133
133
|
dryRun: boolean,
|
|
134
134
|
): { localBindingFile?: string; bindingsChanged?: boolean } {
|
|
135
135
|
const memberIds = validateBindingIdentities(bundle, persisted, explicitBindings);
|
|
@@ -139,20 +139,20 @@ function finalizeBindingUpdate(
|
|
|
139
139
|
const memberBindings = Object.fromEntries(Object.entries(allBindings)
|
|
140
140
|
.filter(([repositoryId]) => memberIds.has(repositoryId)));
|
|
141
141
|
const projectId = String(bundle.project.value.id);
|
|
142
|
-
const bindingPlan =
|
|
142
|
+
const bindingPlan = planKxmLocalBindings(bundle.projectRoot, projectId, memberBindings, storeOptions);
|
|
143
143
|
if (dryRun) return { localBindingFile: bindingPlan.file, bindingsChanged: bindingPlan.written };
|
|
144
144
|
if (bindingPlan.written && !mutationLock) throw new Error("project mutation lock is required to persist repository bindings");
|
|
145
145
|
const result = bindingPlan.written
|
|
146
|
-
?
|
|
146
|
+
? writeKxmLocalBindings(bundle.projectRoot, projectId, memberBindings, storeOptions, mutationLock)
|
|
147
147
|
: bindingPlan;
|
|
148
148
|
return { localBindingFile: result.file, bindingsChanged: result.written };
|
|
149
149
|
}
|
|
150
150
|
|
|
151
151
|
function plannedRepairResult(
|
|
152
|
-
plan:
|
|
152
|
+
plan: KxmInitializationPlan,
|
|
153
153
|
projectRoot: string,
|
|
154
|
-
repairPlan?:
|
|
155
|
-
):
|
|
154
|
+
repairPlan?: KxmTemplateRepairPlan,
|
|
155
|
+
): KxmInitResult {
|
|
156
156
|
return {
|
|
157
157
|
action: "planned",
|
|
158
158
|
plan,
|
|
@@ -163,23 +163,23 @@ function plannedRepairResult(
|
|
|
163
163
|
}
|
|
164
164
|
|
|
165
165
|
function validateBindingIdentities(
|
|
166
|
-
bundle: ReturnType<typeof
|
|
167
|
-
persisted: ReturnType<typeof
|
|
166
|
+
bundle: ReturnType<typeof loadKxmProject>,
|
|
167
|
+
persisted: ReturnType<typeof readKxmLocalBindings>,
|
|
168
168
|
explicit: Readonly<Record<string, string>>,
|
|
169
169
|
): Set<string> {
|
|
170
170
|
const projectId = String(bundle.project.value.id);
|
|
171
171
|
if (persisted && persisted.projectId !== projectId) {
|
|
172
|
-
throw new
|
|
172
|
+
throw new KxmConfigError([initIssue("local_binding_project_id_mismatch", "Runtime-local repository bindings", "binding record belongs to a different project identity")]);
|
|
173
173
|
}
|
|
174
174
|
const memberIds = memberRepositoryIds(bundle.project.value);
|
|
175
175
|
for (const repositoryId of Object.keys(persisted?.repositories ?? {})) {
|
|
176
176
|
if (!memberIds.has(repositoryId)) {
|
|
177
|
-
throw new
|
|
177
|
+
throw new KxmConfigError([initIssue("local_binding_repository_invalid", "Runtime-local repository bindings", `persisted binding ${repositoryId} is not a member repository`)]);
|
|
178
178
|
}
|
|
179
179
|
}
|
|
180
180
|
for (const repositoryId of Object.keys(explicit)) {
|
|
181
181
|
if (!memberIds.has(repositoryId)) {
|
|
182
|
-
throw new
|
|
182
|
+
throw new KxmConfigError([initIssue("local_binding_repository_invalid", "Runtime-local repository bindings", `explicit binding ${repositoryId} is not a member repository`)]);
|
|
183
183
|
}
|
|
184
184
|
}
|
|
185
185
|
return memberIds;
|
|
@@ -190,15 +190,15 @@ function validateBindingIdentities(
|
|
|
190
190
|
* provenance-backed conflict-free template repair, join with local member
|
|
191
191
|
* bindings, or return a non-mutating migration/conflict plan.
|
|
192
192
|
*/
|
|
193
|
-
function
|
|
193
|
+
function initializeKxmProjectAtGitRoot(
|
|
194
194
|
start: string,
|
|
195
195
|
gitRoot: string,
|
|
196
|
-
options:
|
|
197
|
-
mutationLock?:
|
|
198
|
-
):
|
|
196
|
+
options: KxmInitOptions,
|
|
197
|
+
mutationLock?: KxmLocalBindingLock,
|
|
198
|
+
): KxmInitResult {
|
|
199
199
|
const storeOptions = bindingStoreOptions(options);
|
|
200
200
|
const persisted = existsSync(join(gitRoot, ".kxm", "project.yaml"))
|
|
201
|
-
?
|
|
201
|
+
? readKxmLocalBindings(gitRoot, storeOptions)
|
|
202
202
|
: undefined;
|
|
203
203
|
const repositoryBindings = Object.fromEntries([
|
|
204
204
|
...Object.entries(persisted?.repositories ?? {}),
|
|
@@ -207,13 +207,13 @@ function initializeVnextProjectAtGitRoot(
|
|
|
207
207
|
const loaderOptions = configOptions(options, repositoryBindings);
|
|
208
208
|
const transactionOptions = repairOptions(options, repositoryBindings);
|
|
209
209
|
|
|
210
|
-
if (
|
|
211
|
-
const operation =
|
|
212
|
-
const plan =
|
|
210
|
+
if (hasKxmInitTransaction(gitRoot)) {
|
|
211
|
+
const operation = inspectKxmInitTransaction(gitRoot, options.schemasDir);
|
|
212
|
+
const plan = planKxmInitialization(start, loaderOptions);
|
|
213
213
|
if (!operation) {
|
|
214
214
|
if (options.dryRun) return { action: "planned", plan, projectRoot: gitRoot, resumePending: true, files: [] };
|
|
215
215
|
if (!mutationLock) throw new Error("project mutation lock is required to clean an empty transaction");
|
|
216
|
-
|
|
216
|
+
resumeKxmInitTransaction(gitRoot, transactionOptions);
|
|
217
217
|
} else if (options.dryRun) {
|
|
218
218
|
return {
|
|
219
219
|
action: "planned",
|
|
@@ -226,13 +226,13 @@ function initializeVnextProjectAtGitRoot(
|
|
|
226
226
|
} else {
|
|
227
227
|
if (!mutationLock) throw new Error("project mutation lock is required to resume initialization");
|
|
228
228
|
if (options.projectId !== undefined && options.projectId !== operation.projectId) {
|
|
229
|
-
throw new
|
|
229
|
+
throw new KxmConfigError([initIssue("resume_project_id_mismatch", "--project-id", "requested project ID differs from the pinned initialization transaction")]);
|
|
230
230
|
}
|
|
231
231
|
if (options.projectName !== undefined && options.projectName !== operation.projectName) {
|
|
232
|
-
throw new
|
|
232
|
+
throw new KxmConfigError([initIssue("resume_project_name_mismatch", "--name", "requested project name differs from the pinned initialization transaction")]);
|
|
233
233
|
}
|
|
234
234
|
if (operation.kind === "repair" && Object.keys(options.repositoryBindings ?? {}).length > 0) {
|
|
235
|
-
const currentBundle =
|
|
235
|
+
const currentBundle = loadKxmProject(gitRoot, loaderOptions);
|
|
236
236
|
finalizeBindingUpdate(
|
|
237
237
|
currentBundle,
|
|
238
238
|
persisted,
|
|
@@ -243,7 +243,7 @@ function initializeVnextProjectAtGitRoot(
|
|
|
243
243
|
false,
|
|
244
244
|
);
|
|
245
245
|
}
|
|
246
|
-
const result =
|
|
246
|
+
const result = resumeKxmInitTransaction(gitRoot, transactionOptions);
|
|
247
247
|
if (!result) throw new Error("initialization transaction disappeared while holding the project lock");
|
|
248
248
|
const bindingResult = finalizeBindingUpdate(
|
|
249
249
|
result.bundle,
|
|
@@ -254,7 +254,7 @@ function initializeVnextProjectAtGitRoot(
|
|
|
254
254
|
mutationLock,
|
|
255
255
|
false,
|
|
256
256
|
);
|
|
257
|
-
|
|
257
|
+
commitKxmInitTransaction(gitRoot, options.schemasDir);
|
|
258
258
|
return {
|
|
259
259
|
action: "resumed",
|
|
260
260
|
plan: completedPlan(gitRoot, loaderOptions),
|
|
@@ -267,24 +267,24 @@ function initializeVnextProjectAtGitRoot(
|
|
|
267
267
|
}
|
|
268
268
|
}
|
|
269
269
|
|
|
270
|
-
const plan =
|
|
270
|
+
const plan = planKxmInitialization(start, loaderOptions);
|
|
271
271
|
if (plan.mode === "migrate") {
|
|
272
272
|
return { action: "planned", plan, ...(plan.projectRoot ? { projectRoot: plan.projectRoot } : {}), files: [] };
|
|
273
273
|
}
|
|
274
274
|
|
|
275
275
|
if (plan.mode === "repair") {
|
|
276
276
|
const projectRoot = plan.projectRoot ?? gitRoot;
|
|
277
|
-
let templateRepair:
|
|
277
|
+
let templateRepair: KxmTemplateRepairPlan | undefined;
|
|
278
278
|
try {
|
|
279
|
-
templateRepair =
|
|
279
|
+
templateRepair = planKxmTemplateRepair(projectRoot, transactionOptions);
|
|
280
280
|
} catch (error) {
|
|
281
|
-
if (!(error instanceof
|
|
281
|
+
if (!(error instanceof KxmConfigError)) throw error;
|
|
282
282
|
return plannedRepairResult({ ...plan, issues: [...plan.issues, ...error.issues] }, projectRoot);
|
|
283
283
|
}
|
|
284
284
|
if (!templateRepair?.canApply) return plannedRepairResult(plan, projectRoot, templateRepair);
|
|
285
285
|
if (options.dryRun) return plannedRepairResult(plan, projectRoot, templateRepair);
|
|
286
286
|
if (Object.keys(options.repositoryBindings ?? {}).length > 0) {
|
|
287
|
-
const blockedRepair:
|
|
287
|
+
const blockedRepair: KxmTemplateRepairPlan = {
|
|
288
288
|
...templateRepair,
|
|
289
289
|
canApply: false,
|
|
290
290
|
issues: [...templateRepair.issues, initIssue(
|
|
@@ -296,7 +296,7 @@ function initializeVnextProjectAtGitRoot(
|
|
|
296
296
|
return plannedRepairResult(plan, projectRoot, blockedRepair);
|
|
297
297
|
}
|
|
298
298
|
if (!mutationLock) throw new Error("project mutation lock is required to apply template repair");
|
|
299
|
-
const repaired =
|
|
299
|
+
const repaired = prepareAndApplyKxmRepair(templateRepair, transactionOptions);
|
|
300
300
|
const bindingResult = finalizeBindingUpdate(
|
|
301
301
|
repaired.bundle,
|
|
302
302
|
persisted,
|
|
@@ -306,7 +306,7 @@ function initializeVnextProjectAtGitRoot(
|
|
|
306
306
|
mutationLock,
|
|
307
307
|
false,
|
|
308
308
|
);
|
|
309
|
-
|
|
309
|
+
commitKxmInitTransaction(projectRoot, options.schemasDir);
|
|
310
310
|
return {
|
|
311
311
|
action: "repaired",
|
|
312
312
|
plan: completedPlan(projectRoot, loaderOptions),
|
|
@@ -320,8 +320,8 @@ function initializeVnextProjectAtGitRoot(
|
|
|
320
320
|
|
|
321
321
|
if (plan.mode === "ready") {
|
|
322
322
|
const projectRoot = plan.projectRoot ?? gitRoot;
|
|
323
|
-
const bundle =
|
|
324
|
-
const templateRepair =
|
|
323
|
+
const bundle = loadKxmProject(projectRoot, loaderOptions);
|
|
324
|
+
const templateRepair = planKxmTemplateRepair(projectRoot, transactionOptions);
|
|
325
325
|
if (templateRepair?.changesRequired) {
|
|
326
326
|
if (!templateRepair.canApply || options.dryRun) return plannedRepairResult(plan, projectRoot, templateRepair);
|
|
327
327
|
if (!mutationLock) throw new Error("project mutation lock is required to apply template repair");
|
|
@@ -334,7 +334,7 @@ function initializeVnextProjectAtGitRoot(
|
|
|
334
334
|
mutationLock,
|
|
335
335
|
false,
|
|
336
336
|
);
|
|
337
|
-
const repaired =
|
|
337
|
+
const repaired = prepareAndApplyKxmRepair(templateRepair, transactionOptions);
|
|
338
338
|
finalizeBindingUpdate(
|
|
339
339
|
repaired.bundle,
|
|
340
340
|
persisted,
|
|
@@ -344,7 +344,7 @@ function initializeVnextProjectAtGitRoot(
|
|
|
344
344
|
mutationLock,
|
|
345
345
|
false,
|
|
346
346
|
);
|
|
347
|
-
|
|
347
|
+
commitKxmInitTransaction(projectRoot, options.schemasDir);
|
|
348
348
|
return {
|
|
349
349
|
action: "repaired",
|
|
350
350
|
plan: completedPlan(projectRoot, loaderOptions),
|
|
@@ -386,19 +386,19 @@ function initializeVnextProjectAtGitRoot(
|
|
|
386
386
|
}
|
|
387
387
|
|
|
388
388
|
if (plan.projectRoot !== gitRoot) {
|
|
389
|
-
throw new
|
|
389
|
+
throw new KxmConfigError([initIssue("git_root_required", ".", "kxm init must run inside the authoritative Git worktree")]);
|
|
390
390
|
}
|
|
391
391
|
const projectId = generatedProjectId(options.projectId);
|
|
392
392
|
const projectName = normalizedProjectName(gitRoot, options.projectName);
|
|
393
|
-
const rendered =
|
|
393
|
+
const rendered = renderKxmTemplate(projectId, projectName, options.templateVariant ?? CURRENT_KXM_TEMPLATE_VARIANT);
|
|
394
394
|
const files = [...rendered.files.keys()];
|
|
395
395
|
if (options.dryRun) return { action: "planned", plan, projectRoot: gitRoot, files };
|
|
396
396
|
if (!mutationLock) throw new Error("project mutation lock is required to create configuration");
|
|
397
397
|
if (existsSync(join(gitRoot, ".kxm"))) {
|
|
398
|
-
throw new
|
|
398
|
+
throw new KxmConfigError([initIssue("workspace_changed", ".kxm", "workspace changed after planning; existing .kxm state was not overwritten")]);
|
|
399
399
|
}
|
|
400
|
-
const created =
|
|
401
|
-
|
|
400
|
+
const created = prepareAndApplyKxmCreate(gitRoot, rendered, transactionOptions);
|
|
401
|
+
commitKxmInitTransaction(gitRoot, options.schemasDir);
|
|
402
402
|
return {
|
|
403
403
|
action: "created",
|
|
404
404
|
plan: completedPlan(gitRoot, loaderOptions),
|
|
@@ -408,23 +408,23 @@ function initializeVnextProjectAtGitRoot(
|
|
|
408
408
|
};
|
|
409
409
|
}
|
|
410
410
|
|
|
411
|
-
export function
|
|
411
|
+
export function initializeKxmProject(start = process.cwd(), options: KxmInitOptions = {}): KxmInitResult {
|
|
412
412
|
assertNoRegisteredGates(options);
|
|
413
413
|
const gitRoot = discoverGitRoot(start);
|
|
414
414
|
if (!gitRoot) {
|
|
415
|
-
throw new
|
|
415
|
+
throw new KxmConfigError([initIssue("git_root_required", ".", "kxm init must run inside the authoritative Git worktree")]);
|
|
416
416
|
}
|
|
417
417
|
if (options.projectId !== undefined) generatedProjectId(options.projectId);
|
|
418
418
|
if (options.projectName !== undefined) normalizedProjectName(gitRoot, options.projectName);
|
|
419
|
-
if (options.dryRun) return
|
|
419
|
+
if (options.dryRun) return initializeKxmProjectAtGitRoot(start, gitRoot, options);
|
|
420
420
|
|
|
421
|
-
const preflight =
|
|
421
|
+
const preflight = initializeKxmProjectAtGitRoot(start, gitRoot, { ...options, dryRun: true });
|
|
422
422
|
const mutationRequired = preflight.resumePending === true
|
|
423
423
|
|| preflight.plan.mode === "create"
|
|
424
424
|
|| (preflight.repairPlan?.canApply === true
|
|
425
425
|
&& !(preflight.plan.mode === "repair" && Object.keys(options.repositoryBindings ?? {}).length > 0))
|
|
426
426
|
|| (Object.keys(options.repositoryBindings ?? {}).length > 0 && preflight.plan.mode === "ready");
|
|
427
|
-
if (!mutationRequired) return
|
|
427
|
+
if (!mutationRequired) return initializeKxmProjectAtGitRoot(start, gitRoot, options);
|
|
428
428
|
const storeOptions = bindingStoreOptions(options);
|
|
429
|
-
return
|
|
429
|
+
return withKxmLocalBindingLock(gitRoot, storeOptions, (lock) => initializeKxmProjectAtGitRoot(start, gitRoot, options, lock));
|
|
430
430
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from "node:fs";
|
|
2
2
|
import { dirname, join } from "node:path";
|
|
3
|
-
import { canonicalHostPath, sameHostPath } from "./
|
|
3
|
+
import { canonicalHostPath, sameHostPath } from "./bindings.ts";
|
|
4
4
|
|
|
5
5
|
export interface InstallProbe {
|
|
6
6
|
moduleDir: string;
|
|
@@ -6,10 +6,10 @@ import {
|
|
|
6
6
|
KxmUpdateConfigError,
|
|
7
7
|
type KxmUpdateConfig,
|
|
8
8
|
} from "./kxm-update.ts";
|
|
9
|
-
import {
|
|
9
|
+
import { kxmUserStateRoot } from "./bindings.ts";
|
|
10
10
|
|
|
11
11
|
export function loadKxmUpdateConfig(env: NodeJS.ProcessEnv = process.env): KxmUpdateConfig {
|
|
12
|
-
const path = join(
|
|
12
|
+
const path = join(kxmUserStateRoot({ env }), "update.yaml");
|
|
13
13
|
if (!existsSync(path)) return { schema: KXM_UPDATE_SCHEMA, auto: false, source: "github" };
|
|
14
14
|
let parsed: unknown;
|
|
15
15
|
try {
|
|
@@ -45,7 +45,7 @@ export interface MeshTuiPlan {
|
|
|
45
45
|
export type MeshTuiOpenMessage = Pick<MessageRecord, "id" | "status" | "fromName" | "toName" | "delivery" | "createdAt" | "correlationId">;
|
|
46
46
|
|
|
47
47
|
export interface LocalMeshSnapshot {
|
|
48
|
-
source?: "legacy" | "
|
|
48
|
+
source?: "legacy" | "runtime" | "both" | undefined;
|
|
49
49
|
agents: AgentRecord[];
|
|
50
50
|
openMessages: MeshTuiOpenMessage[];
|
|
51
51
|
openMessageTotal: number;
|
|
@@ -205,12 +205,12 @@ export function resolveKxmSnapshotPaths(cwd: string, env: NodeJS.ProcessEnv = pr
|
|
|
205
205
|
return { dataPath, stateDir };
|
|
206
206
|
}
|
|
207
207
|
|
|
208
|
-
function
|
|
208
|
+
function resolveKxmStateRoot(
|
|
209
209
|
stateDir: string,
|
|
210
|
-
options?: { env?: NodeJS.ProcessEnv;
|
|
210
|
+
options?: { env?: NodeJS.ProcessEnv; kxmStateRoot?: string },
|
|
211
211
|
): string | undefined {
|
|
212
|
-
if (options?.
|
|
213
|
-
return resolve(options.
|
|
212
|
+
if (options?.kxmStateRoot && existsSync(options.kxmStateRoot)) {
|
|
213
|
+
return resolve(options.kxmStateRoot);
|
|
214
214
|
}
|
|
215
215
|
if (existsSync(join(stateDir, "runtime", "registry.db")) || existsSync(join(stateDir, "runtime", "projects"))) {
|
|
216
216
|
return stateDir;
|
|
@@ -239,7 +239,7 @@ function resolveVnextStateRoot(
|
|
|
239
239
|
export function loadLocalMeshSnapshot(
|
|
240
240
|
dataPath: string,
|
|
241
241
|
stateDir: string,
|
|
242
|
-
options?: { env?: NodeJS.ProcessEnv; projectRoot?: string;
|
|
242
|
+
options?: { env?: NodeJS.ProcessEnv; projectRoot?: string; kxmStateRoot?: string },
|
|
243
243
|
): LocalMeshSnapshot {
|
|
244
244
|
let hasLegacy = false;
|
|
245
245
|
let agents: AgentRecord[] = [];
|
|
@@ -264,20 +264,20 @@ export function loadLocalMeshSnapshot(
|
|
|
264
264
|
}
|
|
265
265
|
}
|
|
266
266
|
|
|
267
|
-
//
|
|
268
|
-
let
|
|
269
|
-
const
|
|
270
|
-
let
|
|
271
|
-
const
|
|
272
|
-
if (
|
|
273
|
-
const runtimeDir = join(
|
|
267
|
+
// KXM discovery
|
|
268
|
+
let hasKxm = false;
|
|
269
|
+
const kxmRuns: MeshTuiRun[] = [];
|
|
270
|
+
let kxmRunTotal = 0;
|
|
271
|
+
const kxmStateRoot = resolveKxmStateRoot(stateDir, options);
|
|
272
|
+
if (kxmStateRoot) {
|
|
273
|
+
const runtimeDir = join(kxmStateRoot, "runtime");
|
|
274
274
|
const registryDbPath = join(runtimeDir, "registry.db");
|
|
275
275
|
const projectsDir = join(runtimeDir, "projects");
|
|
276
276
|
|
|
277
277
|
const projectKeys = new Set<string>();
|
|
278
278
|
|
|
279
279
|
if (existsSync(registryDbPath)) {
|
|
280
|
-
|
|
280
|
+
hasKxm = true;
|
|
281
281
|
try {
|
|
282
282
|
const regDb = new DatabaseSync(registryDbPath, { readOnly: true });
|
|
283
283
|
try {
|
|
@@ -305,7 +305,7 @@ export function loadLocalMeshSnapshot(
|
|
|
305
305
|
for (const key of projectKeys) {
|
|
306
306
|
const eventDbPath = join(projectsDir, key, "run-events.db");
|
|
307
307
|
if (existsSync(eventDbPath)) {
|
|
308
|
-
|
|
308
|
+
hasKxm = true;
|
|
309
309
|
try {
|
|
310
310
|
const eventDb = new DatabaseSync(eventDbPath, { readOnly: true });
|
|
311
311
|
try {
|
|
@@ -322,9 +322,9 @@ export function loadLocalMeshSnapshot(
|
|
|
322
322
|
updated_at: string;
|
|
323
323
|
}>;
|
|
324
324
|
const countRow = eventDb.prepare("SELECT COUNT(*) AS total FROM runs").get() as { total: number } | undefined;
|
|
325
|
-
|
|
325
|
+
kxmRunTotal += Number(countRow?.total ?? runRows.length);
|
|
326
326
|
for (const r of runRows) {
|
|
327
|
-
|
|
327
|
+
kxmRuns.push({
|
|
328
328
|
id: r.run_id,
|
|
329
329
|
status: r.status,
|
|
330
330
|
definitionId: r.workflow_id,
|
|
@@ -360,7 +360,7 @@ export function loadLocalMeshSnapshot(
|
|
|
360
360
|
// Combine runs
|
|
361
361
|
const combinedRuns = [
|
|
362
362
|
...legacyRuns.map((run) => summarizeMeshRun(run)),
|
|
363
|
-
...
|
|
363
|
+
...kxmRuns,
|
|
364
364
|
];
|
|
365
365
|
const seenIds = new Set<string>();
|
|
366
366
|
const uniqueRuns: MeshTuiRun[] = [];
|
|
@@ -376,14 +376,14 @@ export function loadLocalMeshSnapshot(
|
|
|
376
376
|
return bt - at;
|
|
377
377
|
});
|
|
378
378
|
const runs = uniqueRuns.slice(0, 16);
|
|
379
|
-
const runTotal = legacyRunTotal +
|
|
379
|
+
const runTotal = legacyRunTotal + kxmRunTotal;
|
|
380
380
|
|
|
381
381
|
// Source determination
|
|
382
|
-
let source: "legacy" | "
|
|
383
|
-
if (hasLegacy &&
|
|
382
|
+
let source: "legacy" | "runtime" | "both";
|
|
383
|
+
if (hasLegacy && hasKxm) {
|
|
384
384
|
source = "both";
|
|
385
|
-
} else if (
|
|
386
|
-
source = "
|
|
385
|
+
} else if (hasKxm) {
|
|
386
|
+
source = "runtime";
|
|
387
387
|
} else {
|
|
388
388
|
source = "legacy";
|
|
389
389
|
}
|
|
@@ -8,7 +8,7 @@ import { AGENT_COMMANDS_MAP, enforceToolPolicy, getMcpTools, reconcileInbox } fr
|
|
|
8
8
|
import { deliverInboxNotification } from "./inbox.ts";
|
|
9
9
|
import type { HubEvent, MessageRecord } from "./protocol.ts";
|
|
10
10
|
|
|
11
|
-
const VERSION = "0.7.
|
|
11
|
+
const VERSION = "0.7.44";
|
|
12
12
|
const inbox = new Map<string, MessageRecord>();
|
|
13
13
|
const notifiedInbox = new Set<string>();
|
|
14
14
|
let meshClient: HubClient | undefined;
|