@kontextmind/kxm 0.6.0 → 0.7.0
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/.kxm/agents/coordinator.yaml +9 -0
- package/.kxm/agents/critic-arch.yaml +13 -0
- package/.kxm/agents/critic-cli.yaml +13 -0
- package/.kxm/agents/implementer.yaml +13 -0
- package/.kxm/gates.yaml +8 -0
- package/.kxm/producers.yaml +22 -0
- package/.kxm/project.yaml +15 -0
- package/.kxm/roles/writer.yaml +5 -0
- package/.kxm/workflows/default.yaml +47 -0
- package/CHANGELOG.md +39 -7
- package/README.md +1 -0
- package/docs/README.md +2 -0
- package/docs/agent-skills.md +118 -0
- package/docs/architecture.md +1 -1
- package/docs/assignment-runner.md +21 -8
- package/docs/configuration.md +2 -2
- package/docs/kxm-handbook.md +3 -3
- package/docs/operator-pi-packages.md +63 -0
- package/docs/skills/repo-work-delivery.md +102 -0
- package/docs/test-matrix.md +4 -3
- package/docs/troubleshooting.md +19 -0
- package/docs/vnext/validation.md +9 -0
- package/docs/webhook-workflows.md +2 -2
- package/examples/README.md +1 -1
- package/package.json +16 -17
- package/plugins/kxm/.claude-plugin/plugin.json +1 -1
- package/plugins/kxm/README.md +1 -1
- package/plugins/kxm/dist/cli.js +6875 -3672
- package/plugins/kxm/dist/core.js +214 -34
- package/plugins/kxm/dist/extension.js +7721 -85
- package/plugins/kxm/dist/mcp-server.js +75 -21
- package/plugins/kxm/dist/runtime.js +5403 -1014
- package/plugins/kxm/dist/server.js +3008 -2268
- package/plugins/kxm/dist/vnext-runtime-supervisor.js +5751 -631
- package/plugins/kxm/package.json +1 -1
- package/plugins/kxm/skills/SUITE.md +5 -0
- package/plugins/kxm/skills/hints.json +73 -0
- package/plugins/kxm/skills/kxm/SKILL.md +30 -83
- package/plugins/kxm/skills/kxm-context-memory/SKILL.md +69 -0
- package/plugins/kxm/skills/kxm-definitions/SKILL.md +65 -0
- package/plugins/kxm/skills/kxm-harness-auth/SKILL.md +34 -0
- package/plugins/kxm/skills/kxm-harvest/SKILL.md +48 -0
- package/plugins/kxm/skills/kxm-hub-ops/SKILL.md +34 -0
- package/plugins/kxm/skills/kxm-insights/SKILL.md +48 -0
- package/plugins/kxm/skills/kxm-mind/SKILL.md +59 -0
- package/plugins/kxm/skills/kxm-peer/SKILL.md +110 -0
- package/plugins/kxm/skills/kxm-project-setup/SKILL.md +35 -0
- package/plugins/kxm/skills/kxm-projects/SKILL.md +43 -0
- package/plugins/kxm/skills/kxm-protocol/SKILL.md +66 -0
- package/plugins/kxm/skills/kxm-query/SKILL.md +45 -0
- package/plugins/kxm/skills/kxm-routing-improve/SKILL.md +30 -0
- package/plugins/kxm/skills/kxm-runs/SKILL.md +29 -0
- package/plugins/kxm/skills/kxm-setup/SKILL.md +55 -0
- package/plugins/kxm/skills/kxm-skill-lifecycle/SKILL.md +31 -0
- package/plugins/kxm/skills/kxm-tasks/SKILL.md +33 -0
- package/plugins/kxm/skills/kxm-triage/SKILL.md +47 -0
- package/plugins/kxm/skills/kxm-work/SKILL.md +44 -0
- package/plugins/kxm/skills/kxm-workflow/SKILL.md +45 -0
- package/plugins/kxm/src/autocomplete.ts +9 -3
- package/plugins/kxm/src/cli.ts +1077 -11
- package/plugins/kxm/src/commands.ts +150 -8
- package/plugins/kxm/src/config.ts +7 -4
- package/plugins/kxm/src/context-packet.ts +172 -0
- package/plugins/kxm/src/extension.ts +36 -1
- package/plugins/kxm/src/external-effects.ts +356 -7
- package/plugins/kxm/src/hub.ts +2 -4
- package/plugins/kxm/src/improve.ts +72 -0
- package/plugins/kxm/src/mcp-server.ts +1 -1
- package/plugins/kxm/src/model-inventory.ts +127 -0
- package/plugins/kxm/src/policy-draft.d.mts +55 -0
- package/plugins/kxm/src/policy-draft.mjs +565 -0
- package/plugins/kxm/src/price-calc.ts +17 -18
- package/plugins/kxm/src/prices.ts +32 -16
- package/plugins/kxm/src/producers.ts +71 -0
- package/plugins/kxm/src/restricted-yaml.d.mts +31 -0
- package/plugins/kxm/src/restricted-yaml.mjs +145 -0
- package/plugins/kxm/src/role.ts +375 -0
- package/plugins/kxm/src/routing.ts +99 -1
- package/plugins/kxm/src/session-work.ts +9 -2
- package/plugins/kxm/src/studio-layout.ts +660 -17
- package/plugins/kxm/src/suggest.ts +7 -13
- package/plugins/kxm/src/telemetry.ts +82 -0
- package/plugins/kxm/src/tui.ts +140 -0
- package/plugins/kxm/src/vnext-config.ts +15 -110
- package/plugins/kxm/src/vnext-engine.ts +198 -62
- package/plugins/kxm/src/vnext-harness.ts +263 -81
- package/plugins/kxm/src/vnext-oneshot-evidence.ts +85 -0
- package/plugins/kxm/src/vnext-oneshot-process.ts +149 -0
- package/plugins/kxm/src/vnext-oneshot-producer.ts +170 -233
- package/plugins/kxm/src/vnext-runtime-store.ts +35 -1
- package/plugins/kxm/src/vnext-runtime-supervisor.ts +120 -5
- package/plugins/kxm/src/vnext-runtime.ts +14 -0
- package/plugins/kxm/src/workflow-manager.ts +392 -0
- package/plugins/kxm/src/workflow-tui.ts +255 -0
- package/schemas/policy-draft/README.md +17 -0
- package/schemas/policy-draft/model.v2.schema.json +140 -0
- package/schemas/policy-draft/role.v2.schema.json +91 -0
- package/schemas/vnext/role.schema.json +76 -0
- package/schemas/vnext/run-event.schema.json +1 -0
- package/scripts/assignment-run.d.mts +1 -1
- package/scripts/assignment-run.mjs +44 -35
- package/scripts/check-generated.mjs +33 -9
- package/scripts/emit-codex-artifacts.mjs +255 -11
- package/scripts/harness-run.d.mts +12 -4
- package/scripts/harness-run.mjs +65 -17
- package/scripts/kxm-hub.mjs +6 -0
- package/scripts/native-critic.d.mts +5 -0
- package/scripts/native-critic.mjs +60 -0
- package/.kxm/config/README.md +0 -5
- package/.kxm/config/agents.json +0 -43
- package/.kxm/config/env.example +0 -56
- package/.kxm/config/update.example.yaml +0 -9
- package/.kxm/config/workflows/fix.json +0 -160
- package/.kxm/config/workflows/jira-development.json +0 -116
- package/.kxm/config/workflows/provenance-quorum.json +0 -150
- package/.kxm/config/workflows/v04-dogfood.json +0 -72
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* KXM Workflow TUI module for Pi Extension.
|
|
3
|
+
* Renders active workflow run, stage progress stepper, assigned roles, and model/spend metrics.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
7
|
+
import { join } from "node:path";
|
|
8
|
+
import { DatabaseSync } from "node:sqlite";
|
|
9
|
+
import { repoConfigDirectory } from "./config.ts";
|
|
10
|
+
import { readRoutingRecords, telemetryPath } from "./telemetry.ts";
|
|
11
|
+
|
|
12
|
+
export interface WorkflowProgressMetrics {
|
|
13
|
+
totalSpendUsd?: number | undefined;
|
|
14
|
+
inputTokens?: number | undefined;
|
|
15
|
+
outputTokens?: number | undefined;
|
|
16
|
+
cacheReadTokens?: number | undefined;
|
|
17
|
+
latencyMs?: number | undefined;
|
|
18
|
+
harness?: string | undefined;
|
|
19
|
+
model?: string | undefined;
|
|
20
|
+
effort?: string | undefined;
|
|
21
|
+
memoryRevision?: string | undefined;
|
|
22
|
+
transitions?: number | undefined;
|
|
23
|
+
maxTransitions?: number | undefined;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface WorkflowProgressState {
|
|
27
|
+
runId?: string | undefined;
|
|
28
|
+
workflowId: string;
|
|
29
|
+
status: "idle" | "running" | "waiting" | "passed" | "failed" | "completed" | "cancelled";
|
|
30
|
+
currentStage?: string | undefined;
|
|
31
|
+
currentRole?: string | undefined;
|
|
32
|
+
attempt?: number | undefined;
|
|
33
|
+
maxAttempts?: number | undefined;
|
|
34
|
+
stages: Array<{
|
|
35
|
+
id: string;
|
|
36
|
+
label?: string | undefined;
|
|
37
|
+
kind?: "agent" | "gate" | "wait" | "approval" | undefined;
|
|
38
|
+
status: "pending" | "running" | "passed" | "failed" | "waiting" | "cancelled";
|
|
39
|
+
role?: string | undefined;
|
|
40
|
+
attempts?: number | undefined;
|
|
41
|
+
}>;
|
|
42
|
+
metrics?: WorkflowProgressMetrics | undefined;
|
|
43
|
+
updatedAt?: string | undefined;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Load current active or latest workflow progress from SQLite state and telemetry.
|
|
48
|
+
*/
|
|
49
|
+
export function loadActiveWorkflowProgress(
|
|
50
|
+
repoRoot = process.cwd(),
|
|
51
|
+
targetRunId?: string,
|
|
52
|
+
): WorkflowProgressState | undefined {
|
|
53
|
+
const dbPath = join(repoConfigDirectory(repoRoot), "state", "kxm.db");
|
|
54
|
+
if (!existsSync(dbPath)) return undefined;
|
|
55
|
+
|
|
56
|
+
let db: DatabaseSync | undefined;
|
|
57
|
+
try {
|
|
58
|
+
db = new DatabaseSync(dbPath, { readOnly: true });
|
|
59
|
+
let row: { record: string } | undefined;
|
|
60
|
+
if (targetRunId) {
|
|
61
|
+
row = db.prepare("SELECT record FROM workflow_runs WHERE id = ?").get(targetRunId) as { record: string } | undefined;
|
|
62
|
+
} else {
|
|
63
|
+
row = db.prepare(
|
|
64
|
+
"SELECT record FROM workflow_runs ORDER BY CASE json_extract(record, '$.status') WHEN 'running' THEN 1 WHEN 'waiting' THEN 2 ELSE 3 END, json_extract(record, '$.updatedAt') DESC LIMIT 1",
|
|
65
|
+
).get() as { record: string } | undefined;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (!row) return undefined;
|
|
69
|
+
|
|
70
|
+
const raw = JSON.parse(row.record) as Record<string, unknown>;
|
|
71
|
+
const runId = (raw.id as string) || targetRunId;
|
|
72
|
+
const workflowId = (raw.definitionId as string) || (raw.workflowId as string) || "workflow";
|
|
73
|
+
const status = (raw.status as WorkflowProgressState["status"]) || "running";
|
|
74
|
+
const currentStage = (raw.currentStage as string) || undefined;
|
|
75
|
+
const updatedAt = (raw.updatedAt as string) || undefined;
|
|
76
|
+
|
|
77
|
+
const stages: WorkflowProgressState["stages"] = [];
|
|
78
|
+
const rawStages = Array.isArray(raw.stages) ? raw.stages : [];
|
|
79
|
+
for (const s of rawStages) {
|
|
80
|
+
if (s && typeof s === "object") {
|
|
81
|
+
stages.push({
|
|
82
|
+
id: String((s as Record<string, unknown>).id || ""),
|
|
83
|
+
label: (s as Record<string, unknown>).label as string | undefined,
|
|
84
|
+
kind: (s as Record<string, unknown>).kind as any,
|
|
85
|
+
status: ((s as Record<string, unknown>).status as any) || "pending",
|
|
86
|
+
role: (s as Record<string, unknown>).role as string | undefined,
|
|
87
|
+
attempts: typeof (s as Record<string, unknown>).attempts === "number" ? ((s as Record<string, unknown>).attempts as number) : undefined,
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
let metrics: WorkflowProgressMetrics | undefined;
|
|
93
|
+
try {
|
|
94
|
+
const logsDir = join(repoConfigDirectory(repoRoot), "logs");
|
|
95
|
+
const telemFile = telemetryPath(logsDir);
|
|
96
|
+
if (existsSync(telemFile)) {
|
|
97
|
+
const records = readRoutingRecords(telemFile);
|
|
98
|
+
const routingRunId = (routing: (typeof records)[number]["routing"]): string | undefined =>
|
|
99
|
+
"runId" in routing ? routing.runId : routing.workflowRunId;
|
|
100
|
+
let matching = records.filter((r) => routingRunId(r.routing) === runId);
|
|
101
|
+
if (matching.length === 0) {
|
|
102
|
+
const rawLines = readFileSync(telemFile, "utf8").split(/\r?\n/);
|
|
103
|
+
for (const line of rawLines) {
|
|
104
|
+
if (!line.trim()) continue;
|
|
105
|
+
try {
|
|
106
|
+
const p = JSON.parse(line) as Record<string, unknown>;
|
|
107
|
+
const rt = (p.routing || p) as any;
|
|
108
|
+
if (rt && (routingRunId(rt) === runId || p.runId === runId)) {
|
|
109
|
+
matching.push({ recordedAt: (p.timestamp as string) || new Date().toISOString(), routing: rt });
|
|
110
|
+
}
|
|
111
|
+
} catch {
|
|
112
|
+
// ignore
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
if (matching.length > 0) {
|
|
117
|
+
const latest = matching[matching.length - 1]!.routing;
|
|
118
|
+
let totalSpend = 0;
|
|
119
|
+
let totalIn = 0;
|
|
120
|
+
let totalOut = 0;
|
|
121
|
+
let totalCache = 0;
|
|
122
|
+
let totalLatency = 0;
|
|
123
|
+
for (const m of matching) {
|
|
124
|
+
const rt = m.routing;
|
|
125
|
+
if (typeof rt.costUsd === "number") totalSpend += rt.costUsd;
|
|
126
|
+
if ("tokensIn" in rt && typeof rt.tokensIn === "number") totalIn += rt.tokensIn;
|
|
127
|
+
else if ("tokens" in rt && rt.tokens && typeof (rt.tokens as { input?: unknown }).input === "number") totalIn += (rt.tokens as { input: number }).input;
|
|
128
|
+
if ("tokensOut" in rt && typeof rt.tokensOut === "number") totalOut += rt.tokensOut;
|
|
129
|
+
else if ("tokens" in rt && rt.tokens && typeof (rt.tokens as { output?: unknown }).output === "number") totalOut += (rt.tokens as { output: number }).output;
|
|
130
|
+
if ("cacheReadTokens" in rt && typeof rt.cacheReadTokens === "number") totalCache += rt.cacheReadTokens;
|
|
131
|
+
else if ("tokens" in rt && rt.tokens && typeof (rt.tokens as { cacheRead?: unknown }).cacheRead === "number") totalCache += (rt.tokens as { cacheRead: number }).cacheRead;
|
|
132
|
+
if ("latencyMs" in rt && typeof rt.latencyMs === "number") totalLatency += rt.latencyMs;
|
|
133
|
+
}
|
|
134
|
+
const latestRecord = latest as unknown as Record<string, unknown>;
|
|
135
|
+
const latestMetadata = latestRecord.providerMetadata && typeof latestRecord.providerMetadata === "object"
|
|
136
|
+
? latestRecord.providerMetadata as Record<string, unknown>
|
|
137
|
+
: undefined;
|
|
138
|
+
metrics = {
|
|
139
|
+
totalSpendUsd: totalSpend,
|
|
140
|
+
inputTokens: totalIn,
|
|
141
|
+
outputTokens: totalOut,
|
|
142
|
+
cacheReadTokens: totalCache,
|
|
143
|
+
latencyMs: totalLatency,
|
|
144
|
+
harness: typeof latestRecord.harness === "string" ? latestRecord.harness : (typeof latestMetadata?.harness === "string" ? latestMetadata.harness : undefined),
|
|
145
|
+
model: typeof latestRecord.effectiveModel === "string" ? latestRecord.effectiveModel : (typeof latestRecord.model === "string" ? latestRecord.model : (typeof latestRecord.requestedModel === "string" ? latestRecord.requestedModel : undefined)),
|
|
146
|
+
effort: typeof latestRecord.thinking === "string" ? latestRecord.thinking : (typeof latestRecord.reasoningEffort === "string" ? latestRecord.reasoningEffort : undefined),
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
} catch {
|
|
151
|
+
// Telemetry optional
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const activeStageObj = stages.find((s) => s.id === currentStage);
|
|
155
|
+
const currentRole = activeStageObj?.role || (raw.targetAgentName as string) || undefined;
|
|
156
|
+
const attempt = activeStageObj?.attempts || 1;
|
|
157
|
+
|
|
158
|
+
return {
|
|
159
|
+
runId,
|
|
160
|
+
workflowId,
|
|
161
|
+
status,
|
|
162
|
+
currentStage,
|
|
163
|
+
currentRole,
|
|
164
|
+
attempt,
|
|
165
|
+
stages,
|
|
166
|
+
metrics,
|
|
167
|
+
updatedAt,
|
|
168
|
+
};
|
|
169
|
+
} catch {
|
|
170
|
+
return undefined;
|
|
171
|
+
} finally {
|
|
172
|
+
try {
|
|
173
|
+
db?.close();
|
|
174
|
+
} catch {
|
|
175
|
+
// ignore
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Format stage progress indicator into a compact horizontal visual stepper.
|
|
182
|
+
* e.g.: [✔ implement] ──> [▶ review-arch] ──> [⧗ review-cli] ──> [○ verify]
|
|
183
|
+
*/
|
|
184
|
+
export function formatStageStepper(stages: WorkflowProgressState["stages"], currentStage?: string): string {
|
|
185
|
+
if (stages.length === 0) {
|
|
186
|
+
return currentStage ? `[▶ ${currentStage}]` : "[idle]";
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const parts = stages.map((s) => {
|
|
190
|
+
let icon = "○"; // pending
|
|
191
|
+
if (s.status === "passed") icon = "✔";
|
|
192
|
+
else if (s.id === currentStage || s.status === "running") icon = "▶";
|
|
193
|
+
else if (s.status === "waiting") icon = "⧗";
|
|
194
|
+
else if (s.status === "failed") icon = "✖";
|
|
195
|
+
else if (s.status === "cancelled") icon = "⊘";
|
|
196
|
+
return `[${icon} ${s.id}]`;
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
return parts.join(" ──> ");
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function formatTokenK(tokens?: number): string {
|
|
203
|
+
if (tokens === undefined || tokens === null) return "0";
|
|
204
|
+
if (tokens >= 1000) {
|
|
205
|
+
return `${(tokens / 1000).toFixed(1)}k`;
|
|
206
|
+
}
|
|
207
|
+
return String(tokens);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Render Pi Extension live widget lines (for `ctx.ui.setWidget("kxm-workflow", lines)`).
|
|
212
|
+
*/
|
|
213
|
+
export function renderWorkflowWidgetLines(state?: WorkflowProgressState): string[] {
|
|
214
|
+
if (!state || !state.runId) {
|
|
215
|
+
return [
|
|
216
|
+
"┌─ KXM Workflow ──────────────────────────────────────────────────────────────┐",
|
|
217
|
+
"│ No active workflow running. Use 'kxm workflow start <id>' │",
|
|
218
|
+
"└─────────────────────────────────────────────────────────────────────────────┘",
|
|
219
|
+
];
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
const shortRun = state.runId ? state.runId.slice(0, 16) : "-";
|
|
223
|
+
const header = `┌─ KXM Workflow: ${state.workflowId} (${shortRun}) ─`;
|
|
224
|
+
const topBorder = header + "─".repeat(Math.max(2, 79 - header.length)) + "┐";
|
|
225
|
+
|
|
226
|
+
const stepper = formatStageStepper(state.stages, state.currentStage);
|
|
227
|
+
const statusStr = state.status.toUpperCase();
|
|
228
|
+
const attemptStr = state.attempt ? ` (Attempt ${state.attempt})` : "";
|
|
229
|
+
const roleStr = state.currentRole ? ` · Role: ${state.currentRole}` : "";
|
|
230
|
+
|
|
231
|
+
const lines: string[] = [topBorder];
|
|
232
|
+
lines.push(`│ Stage: ${stepper}`);
|
|
233
|
+
lines.push(`│ Status: ${statusStr}${attemptStr}${roleStr}`);
|
|
234
|
+
|
|
235
|
+
if (state.metrics) {
|
|
236
|
+
const costStr = state.metrics.totalSpendUsd !== undefined ? `$${state.metrics.totalSpendUsd.toFixed(2)}` : "unmetered";
|
|
237
|
+
const routeStr = state.metrics.model ? `${state.metrics.harness ?? "pi"}:${state.metrics.model}` : "harness";
|
|
238
|
+
const effortStr = state.metrics.effort ? ` (${state.metrics.effort})` : "";
|
|
239
|
+
const inTokens = formatTokenK(state.metrics.inputTokens);
|
|
240
|
+
const outTokens = formatTokenK(state.metrics.outputTokens);
|
|
241
|
+
const cacheTokens = formatTokenK(state.metrics.cacheReadTokens);
|
|
242
|
+
lines.push(`│ Model: ${routeStr}${effortStr} · Spend: ${costStr}`);
|
|
243
|
+
lines.push(`│ Tokens: In: ${inTokens} | Out: ${outTokens} | Cache: ${cacheTokens}`);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
lines.push("└" + "─".repeat(78) + "┘");
|
|
247
|
+
return lines;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Render detailed full-width TUI banner / notification string.
|
|
252
|
+
*/
|
|
253
|
+
export function renderWorkflowTuiText(state?: WorkflowProgressState): string {
|
|
254
|
+
return renderWorkflowWidgetLines(state).join("\n");
|
|
255
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Policy-draft schemas (non-authoritative)
|
|
2
|
+
|
|
3
|
+
`kxm.model.v2` and `kxm.role.v2` here are **passive scaffolding**. They are not
|
|
4
|
+
operator settings, live registry identities, or admission.
|
|
5
|
+
|
|
6
|
+
- Live project models remain `kxm.model.v1` under `schemas/vnext/`.
|
|
7
|
+
- Live CLI roles remain `kxm.role.v1`.
|
|
8
|
+
- Runner admission remains `.kxm/roster.json` via the trusted control Git loader.
|
|
9
|
+
- This directory is not discovered as a vNext project resource.
|
|
10
|
+
|
|
11
|
+
Pure validation lives in `plugins/kxm/src/policy-draft.mjs`
|
|
12
|
+
(`validatePolicyDraft`). Callers must supply draft documents, evidence bytes,
|
|
13
|
+
and code-owned ceilings explicitly. The function does not read files, Git,
|
|
14
|
+
credentials, or cwd, and it does not dispatch or admit a route.
|
|
15
|
+
|
|
16
|
+
Roster `route` lists are configured preference order, not a measured ranking.
|
|
17
|
+
`status: candidate` is stored as candidate data and is never treated as admitted.
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://schemas.kxm.dev/policy-draft/model.v2.schema.json",
|
|
4
|
+
"title": "KXM policy-draft model route",
|
|
5
|
+
"description": "Passive kxm.model.v2 scaffolding. Not live registry selection, admission, or operator settings.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"required": ["schema", "harness", "model", "vendor", "status", "permissions"],
|
|
8
|
+
"additionalProperties": false,
|
|
9
|
+
"properties": {
|
|
10
|
+
"schema": { "const": "kxm.model.v2" },
|
|
11
|
+
"id": {
|
|
12
|
+
"type": "string",
|
|
13
|
+
"pattern": "^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$",
|
|
14
|
+
"minLength": 1,
|
|
15
|
+
"maxLength": 64
|
|
16
|
+
},
|
|
17
|
+
"harness": {
|
|
18
|
+
"type": "string",
|
|
19
|
+
"pattern": "^[a-z][a-z0-9]*(?:[-_][a-z0-9]+)*$",
|
|
20
|
+
"minLength": 1,
|
|
21
|
+
"maxLength": 64
|
|
22
|
+
},
|
|
23
|
+
"model": { "type": "string", "minLength": 1, "maxLength": 200 },
|
|
24
|
+
"vendor": { "type": "string", "minLength": 1, "maxLength": 64 },
|
|
25
|
+
"status": { "enum": ["admitted", "candidate", "retired"] },
|
|
26
|
+
"permissions": {
|
|
27
|
+
"type": "array",
|
|
28
|
+
"items": { "enum": ["edit", "read-only"] },
|
|
29
|
+
"minItems": 1,
|
|
30
|
+
"maxItems": 2,
|
|
31
|
+
"uniqueItems": true
|
|
32
|
+
},
|
|
33
|
+
"origin": {
|
|
34
|
+
"type": "object",
|
|
35
|
+
"required": ["source", "sha256"],
|
|
36
|
+
"additionalProperties": false,
|
|
37
|
+
"properties": {
|
|
38
|
+
"source": { "type": "string", "minLength": 1, "maxLength": 512 },
|
|
39
|
+
"sha256": { "type": "string", "pattern": "^[a-f0-9]{64}$" }
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
"thinking": { "type": "string", "minLength": 1, "maxLength": 64 },
|
|
43
|
+
"tags": {
|
|
44
|
+
"type": "array",
|
|
45
|
+
"items": {
|
|
46
|
+
"type": "string",
|
|
47
|
+
"pattern": "^[a-z][a-z0-9]*(?:[-_][a-z0-9]+)*$",
|
|
48
|
+
"minLength": 1,
|
|
49
|
+
"maxLength": 64
|
|
50
|
+
},
|
|
51
|
+
"uniqueItems": true,
|
|
52
|
+
"maxItems": 32
|
|
53
|
+
},
|
|
54
|
+
"capabilities": {
|
|
55
|
+
"type": "array",
|
|
56
|
+
"items": {
|
|
57
|
+
"type": "string",
|
|
58
|
+
"pattern": "^[a-z][a-z0-9]*(?:[-_][a-z0-9]+)*$",
|
|
59
|
+
"minLength": 1,
|
|
60
|
+
"maxLength": 64
|
|
61
|
+
},
|
|
62
|
+
"uniqueItems": true,
|
|
63
|
+
"maxItems": 32
|
|
64
|
+
},
|
|
65
|
+
"priority": { "type": "integer", "minimum": -10000, "maximum": 10000 },
|
|
66
|
+
"fallbacks": {
|
|
67
|
+
"type": "array",
|
|
68
|
+
"maxItems": 8,
|
|
69
|
+
"items": {
|
|
70
|
+
"oneOf": [
|
|
71
|
+
{
|
|
72
|
+
"type": "object",
|
|
73
|
+
"required": ["profile"],
|
|
74
|
+
"additionalProperties": false,
|
|
75
|
+
"properties": {
|
|
76
|
+
"profile": {
|
|
77
|
+
"type": "string",
|
|
78
|
+
"pattern": "^[a-z][a-z0-9]*(?:[-_][a-z0-9]+)*$",
|
|
79
|
+
"minLength": 1,
|
|
80
|
+
"maxLength": 64
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
"type": "object",
|
|
86
|
+
"required": ["tag"],
|
|
87
|
+
"additionalProperties": false,
|
|
88
|
+
"properties": {
|
|
89
|
+
"tag": {
|
|
90
|
+
"type": "string",
|
|
91
|
+
"pattern": "^[a-z][a-z0-9]*(?:[-_][a-z0-9]+)*$",
|
|
92
|
+
"minLength": 1,
|
|
93
|
+
"maxLength": 64
|
|
94
|
+
},
|
|
95
|
+
"capabilities": {
|
|
96
|
+
"type": "array",
|
|
97
|
+
"items": {
|
|
98
|
+
"type": "string",
|
|
99
|
+
"pattern": "^[a-z][a-z0-9]*(?:[-_][a-z0-9]+)*$",
|
|
100
|
+
"minLength": 1,
|
|
101
|
+
"maxLength": 64
|
|
102
|
+
},
|
|
103
|
+
"uniqueItems": true,
|
|
104
|
+
"maxItems": 32
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
"type": "object",
|
|
110
|
+
"required": ["provider", "model"],
|
|
111
|
+
"additionalProperties": false,
|
|
112
|
+
"properties": {
|
|
113
|
+
"provider": {
|
|
114
|
+
"type": "string",
|
|
115
|
+
"pattern": "^[a-z][a-z0-9]*(?:[-_][a-z0-9]+)*$",
|
|
116
|
+
"minLength": 1,
|
|
117
|
+
"maxLength": 64
|
|
118
|
+
},
|
|
119
|
+
"model": { "type": "string", "minLength": 1, "maxLength": 200 }
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
]
|
|
123
|
+
}
|
|
124
|
+
},
|
|
125
|
+
"limits": {
|
|
126
|
+
"type": "object",
|
|
127
|
+
"additionalProperties": false,
|
|
128
|
+
"properties": {
|
|
129
|
+
"contextTokens": { "type": "integer", "minimum": 1 },
|
|
130
|
+
"outputTokens": { "type": "integer", "minimum": 1 },
|
|
131
|
+
"timeoutMs": { "type": "integer", "minimum": 0, "maximum": 31536000000 }
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
},
|
|
135
|
+
"if": {
|
|
136
|
+
"properties": { "harness": { "const": "pi" } },
|
|
137
|
+
"required": ["harness"]
|
|
138
|
+
},
|
|
139
|
+
"then": { "required": ["origin"] }
|
|
140
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://schemas.kxm.dev/policy-draft/role.v2.schema.json",
|
|
4
|
+
"title": "KXM policy-draft role",
|
|
5
|
+
"description": "Passive kxm.role.v2 scaffolding. Specialty identity is separate from purpose. Roster order is configured preference, not a measured ranking. Not live admission or operator settings.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"required": ["schema", "purpose", "permission", "description", "roster"],
|
|
8
|
+
"additionalProperties": false,
|
|
9
|
+
"properties": {
|
|
10
|
+
"schema": { "const": "kxm.role.v2" },
|
|
11
|
+
"id": {
|
|
12
|
+
"type": "string",
|
|
13
|
+
"pattern": "^[a-z][a-z0-9]*(?:[-_][a-z0-9]+)*$",
|
|
14
|
+
"minLength": 1,
|
|
15
|
+
"maxLength": 64
|
|
16
|
+
},
|
|
17
|
+
"purpose": {
|
|
18
|
+
"enum": ["writer", "planner", "reviewer-arch", "reviewer-cli", "experiment"]
|
|
19
|
+
},
|
|
20
|
+
"permission": { "enum": ["edit", "read-only"] },
|
|
21
|
+
"description": { "type": "string", "minLength": 1, "maxLength": 2000 },
|
|
22
|
+
"roster": {
|
|
23
|
+
"type": "array",
|
|
24
|
+
"maxItems": 32,
|
|
25
|
+
"items": {
|
|
26
|
+
"type": "object",
|
|
27
|
+
"required": ["route"],
|
|
28
|
+
"additionalProperties": false,
|
|
29
|
+
"properties": {
|
|
30
|
+
"route": {
|
|
31
|
+
"type": "string",
|
|
32
|
+
"pattern": "^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$",
|
|
33
|
+
"minLength": 1,
|
|
34
|
+
"maxLength": 64
|
|
35
|
+
},
|
|
36
|
+
"effort": {
|
|
37
|
+
"enum": ["off", "minimal", "low", "medium", "high", "xhigh", "max"]
|
|
38
|
+
},
|
|
39
|
+
"mode": { "enum": ["headless", "interactive", "either"] }
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
"skills": {
|
|
44
|
+
"type": "array",
|
|
45
|
+
"items": { "type": "string", "minLength": 1, "maxLength": 128 },
|
|
46
|
+
"maxItems": 64
|
|
47
|
+
},
|
|
48
|
+
"tools": {
|
|
49
|
+
"type": "object",
|
|
50
|
+
"additionalProperties": false,
|
|
51
|
+
"properties": {
|
|
52
|
+
"preset": { "type": "string" },
|
|
53
|
+
"allow": { "type": "array", "items": { "type": "string" } },
|
|
54
|
+
"deny": { "type": "array", "items": { "type": "string" } }
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
"produces": {
|
|
58
|
+
"type": "array",
|
|
59
|
+
"items": {
|
|
60
|
+
"type": "object",
|
|
61
|
+
"required": ["template"],
|
|
62
|
+
"additionalProperties": false,
|
|
63
|
+
"properties": {
|
|
64
|
+
"template": { "type": "string" },
|
|
65
|
+
"schema": { "type": "string" }
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
"consumes": {
|
|
70
|
+
"type": "array",
|
|
71
|
+
"items": {
|
|
72
|
+
"type": "object",
|
|
73
|
+
"required": ["template"],
|
|
74
|
+
"additionalProperties": false,
|
|
75
|
+
"properties": {
|
|
76
|
+
"template": { "type": "string" },
|
|
77
|
+
"schema": { "type": "string" }
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
"policy": {
|
|
82
|
+
"type": "object",
|
|
83
|
+
"additionalProperties": false,
|
|
84
|
+
"properties": {
|
|
85
|
+
"vendorIndependenceRequired": { "type": "boolean" },
|
|
86
|
+
"maxTransitions": { "type": "integer", "minimum": 1 },
|
|
87
|
+
"requiresGateVerification": { "type": "boolean" }
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://schemas.kxm.dev/vnext/role.schema.json",
|
|
4
|
+
"title": "KXM role definition",
|
|
5
|
+
"description": "The filename determines the role identity unless explicitly declared.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"required": ["schema", "description"],
|
|
8
|
+
"properties": {
|
|
9
|
+
"schema": { "const": "kxm.role.v1" },
|
|
10
|
+
"id": { "$ref": "common.schema.json#/$defs/identifier" },
|
|
11
|
+
"description": { "type": "string", "minLength": 1, "maxLength": 2000 },
|
|
12
|
+
"skills": {
|
|
13
|
+
"type": "array",
|
|
14
|
+
"items": { "type": "string", "minLength": 1, "maxLength": 128 },
|
|
15
|
+
"maxItems": 64
|
|
16
|
+
},
|
|
17
|
+
"tools": {
|
|
18
|
+
"type": "object",
|
|
19
|
+
"properties": {
|
|
20
|
+
"preset": { "type": "string" },
|
|
21
|
+
"allow": { "type": "array", "items": { "type": "string" } },
|
|
22
|
+
"deny": { "type": "array", "items": { "type": "string" } }
|
|
23
|
+
},
|
|
24
|
+
"additionalProperties": false
|
|
25
|
+
},
|
|
26
|
+
"produces": {
|
|
27
|
+
"type": "array",
|
|
28
|
+
"items": {
|
|
29
|
+
"type": "object",
|
|
30
|
+
"required": ["template"],
|
|
31
|
+
"properties": {
|
|
32
|
+
"template": { "type": "string" },
|
|
33
|
+
"schema": { "type": "string" }
|
|
34
|
+
},
|
|
35
|
+
"additionalProperties": false
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
"consumes": {
|
|
39
|
+
"type": "array",
|
|
40
|
+
"items": {
|
|
41
|
+
"type": "object",
|
|
42
|
+
"required": ["template"],
|
|
43
|
+
"properties": {
|
|
44
|
+
"template": { "type": "string" },
|
|
45
|
+
"schema": { "type": "string" }
|
|
46
|
+
},
|
|
47
|
+
"additionalProperties": false
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
"roster": {
|
|
51
|
+
"type": "array",
|
|
52
|
+
"items": {
|
|
53
|
+
"type": "object",
|
|
54
|
+
"required": ["harness", "model"],
|
|
55
|
+
"properties": {
|
|
56
|
+
"harness": { "$ref": "common.schema.json#/$defs/identifier" },
|
|
57
|
+
"model": { "$ref": "common.schema.json#/$defs/modelSelector" },
|
|
58
|
+
"provider": { "type": "string" },
|
|
59
|
+
"effort": { "enum": ["low", "medium", "high", "xhigh"] },
|
|
60
|
+
"mode": { "enum": ["headless", "interactive", "either"] }
|
|
61
|
+
},
|
|
62
|
+
"additionalProperties": false
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
"policy": {
|
|
66
|
+
"type": "object",
|
|
67
|
+
"properties": {
|
|
68
|
+
"vendorIndependenceRequired": { "type": "boolean" },
|
|
69
|
+
"maxTransitions": { "type": "integer", "minimum": 1 },
|
|
70
|
+
"requiresGateVerification": { "type": "boolean" }
|
|
71
|
+
},
|
|
72
|
+
"additionalProperties": false
|
|
73
|
+
}
|
|
74
|
+
},
|
|
75
|
+
"additionalProperties": false
|
|
76
|
+
}
|
|
@@ -323,6 +323,7 @@
|
|
|
323
323
|
"properties": {
|
|
324
324
|
"workflowId": { "$ref": "common.schema.json#/$defs/identifier" },
|
|
325
325
|
"displayTitle": { "type": "string", "minLength": 1, "maxLength": 200 },
|
|
326
|
+
"prompt": { "type": "string", "maxLength": 64000 },
|
|
326
327
|
"promptHash": { "$ref": "common.schema.json#/$defs/sha256" },
|
|
327
328
|
"runPlanHash": { "$ref": "common.schema.json#/$defs/sha256" },
|
|
328
329
|
"capabilityHash": { "$ref": "common.schema.json#/$defs/sha256" },
|
|
@@ -33,7 +33,7 @@ export function resolveRequiredCritics(policy: unknown): Readonly<Record<string,
|
|
|
33
33
|
vendor: string;
|
|
34
34
|
route_id?: string;
|
|
35
35
|
}>>>;
|
|
36
|
-
export function getRosterPolicy(deps?: Record<string, unknown>):
|
|
36
|
+
export function getRosterPolicy(deps?: Record<string, unknown>): import("./roster-policy.mjs").RosterPolicy;
|
|
37
37
|
export const ASSIGNMENT_KINDS: readonly string[];
|
|
38
38
|
export const KIND_ROLES: Readonly<Record<string, string>>;
|
|
39
39
|
export const REVIEW_KINDS: readonly string[];
|