@kontextmind/kxm 0.7.111 → 0.7.115
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/implementer.yaml +1 -1
- package/.kxm/roles/writer.yaml +1 -1
- package/CHANGELOG.md +6 -0
- package/docs/contributing/assignment-runner.md +3 -3
- package/docs/contributing/ci-and-release.md +18 -12
- package/package.json +1 -1
- package/plugins/kxm/.claude-plugin/plugin.json +1 -1
- package/plugins/kxm/dist/mcp-server.js +1 -1
- package/plugins/kxm/package.json +1 -1
- package/plugins/kxm/src/mcp-server.ts +1 -1
- package/scripts/assignment-run.d.mts +7 -0
- package/scripts/assignment-run.mjs +128 -1
- package/scripts/harness-run.mjs +2 -2
- package/scripts/native-critic.mjs +7 -7
package/.kxm/roles/writer.yaml
CHANGED
|
@@ -5,7 +5,7 @@ id: writer
|
|
|
5
5
|
# Effort default: medium for implementation (AGENTS.md). agy ids bake in the
|
|
6
6
|
# effort tier, so those entries carry no effort field.
|
|
7
7
|
roster:
|
|
8
|
-
- model: xai/grok-4.
|
|
8
|
+
- model: xai/grok-4.7
|
|
9
9
|
effort: medium
|
|
10
10
|
enabled: true
|
|
11
11
|
- model: openrouter/qwen/qwen3-coder-plus
|
package/CHANGELOG.md
CHANGED
|
@@ -81,6 +81,12 @@ All notable user-facing changes are documented here. The project follows [Semant
|
|
|
81
81
|
|
|
82
82
|
### Changed
|
|
83
83
|
|
|
84
|
+
- **Windows Validate legs run again on GitHub-hosted `windows-latest`.** The PR gate
|
|
85
|
+
still requires only `Validate (linux, Node 22.19.0)` and `Validate (linux, Node 24)`
|
|
86
|
+
on ARC `kontextmind-doks`. Windows Node 22.19.0 and 24 report `validate:pr` but are
|
|
87
|
+
not protect-main required checks. Nightly complete coverage and release stay on
|
|
88
|
+
Linux. See [CI and release](docs/contributing/ci-and-release.md).
|
|
89
|
+
|
|
84
90
|
- **The rotation surface drive uses is the one setup writes.** Guide setup appends only
|
|
85
91
|
reviewed selectors to `.kxm/routes.yaml`. It skips Google guide candidates: Google's
|
|
86
92
|
route is the Pi `antigravity` provider, which a Runtime drive cannot reach yet, so a
|
|
@@ -33,7 +33,7 @@ permissions:
|
|
|
33
33
|
|
|
34
34
|
| Role | Admitted route (harness / model) | Vendor | Permission |
|
|
35
35
|
|---|---|---|---|
|
|
36
|
-
| `writer` | `grok` / `grok-4.
|
|
36
|
+
| `writer` | `grok` / `grok-4.7`; relief: `pi` / `openrouter/qwen/qwen3-coder-plus` | `xai`; `alibaba` | `edit` |
|
|
37
37
|
| `planner` | `claude` / `fable` | `anthropic` | `read-only` |
|
|
38
38
|
| `reviewer-arch` | `claude` / `fable` | `anthropic` | `read-only` |
|
|
39
39
|
| `reviewer-cli` | `codex` / `gpt-5.6-sol` | `openai` | `read-only` |
|
|
@@ -115,7 +115,7 @@ Write a closed `kxm.assignment.v1` manifest. Unknown keys are refused.
|
|
|
115
115
|
"assignment_id": "asg-writer-1",
|
|
116
116
|
"kind": "implement",
|
|
117
117
|
"harness": "grok",
|
|
118
|
-
"model": "grok-4.
|
|
118
|
+
"model": "grok-4.7",
|
|
119
119
|
"effort": "medium",
|
|
120
120
|
"permission": "edit",
|
|
121
121
|
"cwd": "/abs/kxm-fix-improve-sources",
|
|
@@ -247,7 +247,7 @@ then is that `BLOCK` resolved.
|
|
|
247
247
|
├── output-schema.json # the structured-output schema given to the harness
|
|
248
248
|
├── pre-dispatch.json # written before spawn (kxm.assignment-dispatch.v1)
|
|
249
249
|
├── completion.json # kxm.assignment-completion.v1, or refusal.json
|
|
250
|
-
├── routing-record.json #
|
|
250
|
+
├── routing-record.json # export with `node scripts/assignment-run.mjs routing-export --task-dir <abs>` and pass the JSONL to `kxm improve report --file` / `kxm routing report --file`
|
|
251
251
|
├── telemetry.jsonl # usage, latency and cost
|
|
252
252
|
├── runner-errors.jsonl # bounded failure codes
|
|
253
253
|
├── witness/ # latest.json and history/<receipt-id>.json
|
|
@@ -24,10 +24,11 @@ flowchart LR
|
|
|
24
24
|
MAIN -->|"daily 04:00 UTC"| NIGHT[Nightly<br/>complete suite]
|
|
25
25
|
```
|
|
26
26
|
|
|
27
|
-
All workflows live in `.github/workflows/`.
|
|
28
|
-
ARC runner scale set
|
|
29
|
-
|
|
30
|
-
workflow and that test
|
|
27
|
+
All workflows live in `.github/workflows/`. Linux jobs run on the organization's
|
|
28
|
+
ARC runner scale set. Validate also runs two Windows legs on GitHub-hosted
|
|
29
|
+
`windows-latest`. `test/core/ci-contract.test.ts` pins the runner selectors, job
|
|
30
|
+
names, coverage floors and release triggers. Change a workflow and that test
|
|
31
|
+
together.
|
|
31
32
|
|
|
32
33
|
## What runs where
|
|
33
34
|
|
|
@@ -64,11 +65,14 @@ Three differences matter when a check fails on one side only:
|
|
|
64
65
|
|
|
65
66
|
### Validate matrix and required checks
|
|
66
67
|
|
|
67
|
-
The Validate job runs on Node 22.19.0 and Node 24, on Linux
|
|
68
|
-
three-minute job timeout.
|
|
69
|
-
|
|
70
|
-
`
|
|
71
|
-
|
|
68
|
+
The Validate job runs on Node 22.19.0 and Node 24, on Linux and Windows. Linux
|
|
69
|
+
uses the ARC scale set `kontextmind-doks` with a three-minute job timeout.
|
|
70
|
+
Windows uses GitHub-hosted `windows-latest` with a fifteen-minute timeout so
|
|
71
|
+
`npm ci` can finish. The branch ruleset requires the job names
|
|
72
|
+
`Validate (linux, Node 22.19.0)` and `Validate (linux, Node 24)` only; the
|
|
73
|
+
Windows names are reported but not required, so a Windows-only failure does not
|
|
74
|
+
block merge. Renaming a required Linux job or the matrix means updating the
|
|
75
|
+
ruleset in the same change. A newer push cancels an older pull request run;
|
|
72
76
|
runs on `main` are never cancelled.
|
|
73
77
|
|
|
74
78
|
### CI jobs stay queued while a runner is online
|
|
@@ -106,9 +110,11 @@ autoscaler. Do not relabel `km-gh-rn01` or push an empty commit as a routing
|
|
|
106
110
|
workaround.
|
|
107
111
|
|
|
108
112
|
> [!NOTE]
|
|
109
|
-
> Windows
|
|
110
|
-
>
|
|
111
|
-
>
|
|
113
|
+
> Windows Validate uses GitHub-hosted `windows-latest`, not a self-hosted
|
|
114
|
+
> homelab runner and not the ARC scale set. Do not add a `kontextmind-doks`
|
|
115
|
+
> label to a Windows runner. Nightly complete coverage and release stay on
|
|
116
|
+
> Linux. Windows-specific fixtures (for example the `pi.cmd` worker launch in
|
|
117
|
+
> `test/core/worker.test.ts`) also run in the hosted Windows Validate legs.
|
|
112
118
|
|
|
113
119
|
### The docs-only classifier
|
|
114
120
|
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json",
|
|
3
3
|
"name": "kxm",
|
|
4
4
|
"displayName": "KXM",
|
|
5
|
-
"version": "0.7.
|
|
5
|
+
"version": "0.7.115",
|
|
6
6
|
"description": "Headless multi-agent orchestration, durable workflows, and a live operator dashboard for Pi and Claude Code",
|
|
7
7
|
"author": {
|
|
8
8
|
"name": "KontextMind",
|
|
@@ -17313,7 +17313,7 @@ function sessionTokenFixHint(policy) {
|
|
|
17313
17313
|
}
|
|
17314
17314
|
|
|
17315
17315
|
// plugins/kxm/src/mcp-server.ts
|
|
17316
|
-
var VERSION = "0.7.
|
|
17316
|
+
var VERSION = "0.7.115";
|
|
17317
17317
|
var CONFIGURE_PLUGIN = "/plugin configure kxm@kxm";
|
|
17318
17318
|
var inbox = /* @__PURE__ */ new Map();
|
|
17319
17319
|
var notifiedInbox = /* @__PURE__ */ new Set();
|
package/plugins/kxm/package.json
CHANGED
|
@@ -11,7 +11,7 @@ import { deliverInboxNotification } from "./inbox.ts";
|
|
|
11
11
|
import type { HubEvent, MessageRecord } from "./protocol.ts";
|
|
12
12
|
import { sessionTokenFixHint } from "./session-token-hint.ts";
|
|
13
13
|
|
|
14
|
-
const VERSION = "0.7.
|
|
14
|
+
const VERSION = "0.7.115";
|
|
15
15
|
const CONFIGURE_PLUGIN = "/plugin configure kxm@kxm";
|
|
16
16
|
const inbox = new Map<string, MessageRecord>();
|
|
17
17
|
const notifiedInbox = new Set<string>();
|
|
@@ -339,6 +339,13 @@ export function changeReport(request: { taskDir: string }, deps?: Record<string,
|
|
|
339
339
|
ranking: "not-ranked";
|
|
340
340
|
orchestration_usage: "unavailable";
|
|
341
341
|
};
|
|
342
|
+
export const ROUTING_EXPORT_SCHEMA: "kxm.assignment-routing-export.v1";
|
|
343
|
+
export function exportAssignmentRoutingRecords(request: { taskDir: string }, deps?: Record<string, unknown>): {
|
|
344
|
+
schema: "kxm.assignment-routing-export.v1";
|
|
345
|
+
task_id: string;
|
|
346
|
+
records: Array<Record<string, any>>;
|
|
347
|
+
skipped: Array<{ assignment_id: string | null; reason: "acceptance_record_invalid" | "no_routing_record" | "routing_record_invalid" | "manifest_invalid" }>;
|
|
348
|
+
};
|
|
342
349
|
export function writeCurrentPlan(request: {
|
|
343
350
|
taskDir: string; plan: string; sha256: string; baseCommit: string; expectedGeneration: number; settledDecisions?: string[];
|
|
344
351
|
}, deps?: Record<string, unknown>): {
|
|
@@ -2330,7 +2330,7 @@ export async function observeAssignment(outputDir, deps = {}) {
|
|
|
2330
2330
|
return appended;
|
|
2331
2331
|
}
|
|
2332
2332
|
|
|
2333
|
-
const CLI_USAGE = "usage: assignment-run.mjs run --manifest <absolute-path> | observe --record-dir <absolute-path> | witness --record-dir <absolute-path> | accept --task-dir <absolute-path> --commit <commit> --record-dir <absolute-path> --critic <absolute-path> --critic <absolute-path> [--observed-pr <id>] [--observed-ci <id>] | attribute --task-dir <absolute-path> --record-dir <absolute-path> --class <orchestration|model|environment|unclassified> --explanation-file <absolute-path> | observe-cost --task-dir <absolute-path> --input <absolute-path> | plan-current --task-dir <absolute-path> --plan <absolute-path> --sha256 <hex64> --base-commit <commit> --expected-generation <n> | change-report --task-dir <absolute-path>";
|
|
2333
|
+
const CLI_USAGE = "usage: assignment-run.mjs run --manifest <absolute-path> | observe --record-dir <absolute-path> | witness --record-dir <absolute-path> | accept --task-dir <absolute-path> --commit <commit> --record-dir <absolute-path> --critic <absolute-path> --critic <absolute-path> [--observed-pr <id>] [--observed-ci <id>] | attribute --task-dir <absolute-path> --record-dir <absolute-path> --class <orchestration|model|environment|unclassified> --explanation-file <absolute-path> | observe-cost --task-dir <absolute-path> --input <absolute-path> | plan-current --task-dir <absolute-path> --plan <absolute-path> --sha256 <hex64> --base-commit <commit> --expected-generation <n> | change-report --task-dir <absolute-path> | routing-export --task-dir <absolute-path>";
|
|
2334
2334
|
|
|
2335
2335
|
const WITNESS_RECEIPT_KEYS = Object.freeze([
|
|
2336
2336
|
"schema",
|
|
@@ -4293,8 +4293,135 @@ export function writeCurrentPlan(request, deps = {}) {
|
|
|
4293
4293
|
} catch (error) { throw observationFailure(error, "plan_ref_invalid"); }
|
|
4294
4294
|
}
|
|
4295
4295
|
|
|
4296
|
+
export const ROUTING_EXPORT_SCHEMA = "kxm.assignment-routing-export.v1";
|
|
4297
|
+
|
|
4298
|
+
function contractExportable(contract) {
|
|
4299
|
+
return Boolean(contract)
|
|
4300
|
+
&& typeof contract === "object"
|
|
4301
|
+
&& !Array.isArray(contract)
|
|
4302
|
+
&& typeof contract.boundary === "string"
|
|
4303
|
+
&& Array.isArray(contract.deliverables)
|
|
4304
|
+
&& contract.witness
|
|
4305
|
+
&& typeof contract.witness === "object"
|
|
4306
|
+
&& !Array.isArray(contract.witness)
|
|
4307
|
+
&& typeof contract.witness.id === "string"
|
|
4308
|
+
&& Array.isArray(contract.deferred);
|
|
4309
|
+
}
|
|
4310
|
+
|
|
4311
|
+
export function exportAssignmentRoutingRecords(request, deps = {}) {
|
|
4312
|
+
const io = ioDeps(deps);
|
|
4313
|
+
const taskDir = observationTask(request.taskDir, io);
|
|
4314
|
+
const taskId = basename(taskDir);
|
|
4315
|
+
const skipped = [];
|
|
4316
|
+
const acceptedIds = new Set();
|
|
4317
|
+
const acceptedPath = join(taskDir, ACCEPTED_FILENAME);
|
|
4318
|
+
if (lstatOrNull(acceptedPath, io.lstatSync)) {
|
|
4319
|
+
try {
|
|
4320
|
+
const value = privateRecord(acceptedPath, io).value;
|
|
4321
|
+
const valid = value.schema === ACCEPTED_SCHEMA
|
|
4322
|
+
&& value.task_id === taskId
|
|
4323
|
+
&& typeof value.writer?.assignment_id === "string"
|
|
4324
|
+
&& Array.isArray(value.critics);
|
|
4325
|
+
if (!valid) throw new Error("acceptance invalid");
|
|
4326
|
+
acceptedIds.add(value.writer.assignment_id);
|
|
4327
|
+
for (const critic of value.critics) {
|
|
4328
|
+
if (typeof critic?.assignment_id === "string") acceptedIds.add(critic.assignment_id);
|
|
4329
|
+
}
|
|
4330
|
+
} catch {
|
|
4331
|
+
skipped.push({ assignment_id: null, reason: "acceptance_record_invalid" });
|
|
4332
|
+
}
|
|
4333
|
+
}
|
|
4334
|
+
const collected = [];
|
|
4335
|
+
const dirs = listDirectAssignmentDirs(taskDir, io).sort((left, right) => left.assignment_id < right.assignment_id ? -1 : left.assignment_id > right.assignment_id ? 1 : 0);
|
|
4336
|
+
for (const { assignment_id, path } of dirs) {
|
|
4337
|
+
const routingPath = join(path, "routing-record.json");
|
|
4338
|
+
if (!lstatOrNull(routingPath, io.lstatSync)) {
|
|
4339
|
+
skipped.push({ assignment_id, reason: "no_routing_record" });
|
|
4340
|
+
continue;
|
|
4341
|
+
}
|
|
4342
|
+
let record;
|
|
4343
|
+
try {
|
|
4344
|
+
record = parseRoutingRecord(privateRecord(routingPath, io).value);
|
|
4345
|
+
if (record.workflowRunId !== assignment_id) throw new Error("workflowRunId");
|
|
4346
|
+
} catch {
|
|
4347
|
+
skipped.push({ assignment_id, reason: "routing_record_invalid" });
|
|
4348
|
+
continue;
|
|
4349
|
+
}
|
|
4350
|
+
let contract;
|
|
4351
|
+
try {
|
|
4352
|
+
const manifest = privateRecord(join(path, "manifest.json"), io).value;
|
|
4353
|
+
contract = manifest.contract;
|
|
4354
|
+
if (!contractExportable(contract)) throw new Error("contract");
|
|
4355
|
+
} catch {
|
|
4356
|
+
skipped.push({ assignment_id, reason: "manifest_invalid" });
|
|
4357
|
+
continue;
|
|
4358
|
+
}
|
|
4359
|
+
collected.push({ id: assignment_id, record, contract });
|
|
4360
|
+
}
|
|
4361
|
+
const supersededBy = new Map();
|
|
4362
|
+
for (const entry of collected) {
|
|
4363
|
+
const reworkOf = entry.record.providerMetadata?.rework_of;
|
|
4364
|
+
if (typeof reworkOf === "string" && !supersededBy.has(reworkOf)) supersededBy.set(reworkOf, entry.id);
|
|
4365
|
+
}
|
|
4366
|
+
const records = [];
|
|
4367
|
+
for (const entry of collected) {
|
|
4368
|
+
const kind = entry.record.stageId;
|
|
4369
|
+
const role = entry.record.agentRole;
|
|
4370
|
+
const askSha256 = sha256Bytes(Buffer.from(JSON.stringify(["kxm.assignment-ask.v1", kind, role])));
|
|
4371
|
+
const objectiveSha256 = sha256Bytes(Buffer.from(JSON.stringify([
|
|
4372
|
+
"kxm.assignment-brief.v1",
|
|
4373
|
+
kind,
|
|
4374
|
+
entry.contract.boundary,
|
|
4375
|
+
entry.contract.deliverables,
|
|
4376
|
+
entry.contract.witness.id,
|
|
4377
|
+
entry.contract.deferred,
|
|
4378
|
+
])));
|
|
4379
|
+
const finalOutcome = acceptedIds.has(entry.id) ? "accepted" : supersededBy.has(entry.id) ? "failed" : "pending";
|
|
4380
|
+
try {
|
|
4381
|
+
records.push(parseRoutingRecord({
|
|
4382
|
+
...entry.record,
|
|
4383
|
+
workflowRunId: taskId,
|
|
4384
|
+
finalOutcome,
|
|
4385
|
+
providerMetadata: {
|
|
4386
|
+
...entry.record.providerMetadata,
|
|
4387
|
+
workflowId: "assignment-runner",
|
|
4388
|
+
askSha256,
|
|
4389
|
+
objectiveSha256,
|
|
4390
|
+
stepWrites: WRITER_KINDS.includes(kind),
|
|
4391
|
+
assignmentId: entry.id,
|
|
4392
|
+
...(finalOutcome === "failed" ? { supersededBy: supersededBy.get(entry.id) } : {}),
|
|
4393
|
+
},
|
|
4394
|
+
}));
|
|
4395
|
+
} catch {
|
|
4396
|
+
skipped.push({ assignment_id: entry.id, reason: "routing_record_invalid" });
|
|
4397
|
+
}
|
|
4398
|
+
}
|
|
4399
|
+
return { schema: ROUTING_EXPORT_SCHEMA, task_id: taskId, records, skipped };
|
|
4400
|
+
}
|
|
4401
|
+
|
|
4296
4402
|
export async function main(argv = process.argv, io = { stdin: process.stdin, stdout: process.stdout, stderr: process.stderr }) {
|
|
4297
4403
|
const args = argv.slice(2).filter((arg) => arg !== "--");
|
|
4404
|
+
if (args[0] === "routing-export") {
|
|
4405
|
+
try {
|
|
4406
|
+
const values = {};
|
|
4407
|
+
for (let i = 1; i < args.length; i += 2) {
|
|
4408
|
+
if (args[i] !== "--task-dir" || values[args[i]] !== undefined || args[i + 1] === undefined) throw failClosed("invalid report/plan arguments", "manifest_invalid");
|
|
4409
|
+
values[args[i]] = args[i + 1];
|
|
4410
|
+
}
|
|
4411
|
+
if (values["--task-dir"] === undefined) throw failClosed("missing report/plan arguments", "manifest_invalid");
|
|
4412
|
+
const result = exportAssignmentRoutingRecords({ taskDir: values["--task-dir"] });
|
|
4413
|
+
for (const record of result.records) io.stdout.write(`${JSON.stringify(record)}\n`);
|
|
4414
|
+
io.stderr.write(`${JSON.stringify({ schema: result.schema, task_id: result.task_id, records: result.records.length, skipped: result.skipped })}\n`);
|
|
4415
|
+
process.exitCode = 0;
|
|
4416
|
+
return result;
|
|
4417
|
+
} catch (error) {
|
|
4418
|
+
const failure = observationFailure(error);
|
|
4419
|
+
io.stderr.write(`${failure.runnerCode}: assignment routing export refused\n`);
|
|
4420
|
+
process.exitCode = 1;
|
|
4421
|
+
throw failure;
|
|
4422
|
+
}
|
|
4423
|
+
}
|
|
4424
|
+
|
|
4298
4425
|
if (["change-report", "plan-current"].includes(args[0])) {
|
|
4299
4426
|
try {
|
|
4300
4427
|
const flags = args[0] === "change-report" ? ["--task-dir"] : ["--task-dir", "--plan", "--sha256", "--base-commit", "--expected-generation"];
|
package/scripts/harness-run.mjs
CHANGED
|
@@ -137,7 +137,7 @@ export const ROUTES = Object.freeze({
|
|
|
137
137
|
provider: "xai",
|
|
138
138
|
roles: Object.freeze(["writer"]),
|
|
139
139
|
permissions: Object.freeze(["edit"]),
|
|
140
|
-
models: Object.freeze(["grok-4.
|
|
140
|
+
models: Object.freeze(["grok-4.7"]),
|
|
141
141
|
efforts: Object.freeze(["low", "medium", "high"]),
|
|
142
142
|
auth: Object.freeze({ args: ["models"], loginHint: "grok (OAuth to auth.x.ai)" }),
|
|
143
143
|
},
|
|
@@ -153,7 +153,7 @@ export const ROUTES = Object.freeze({
|
|
|
153
153
|
provider: "anthropic",
|
|
154
154
|
roles: Object.freeze(["planner", "reviewer-arch"]),
|
|
155
155
|
permissions: Object.freeze(["read-only"]),
|
|
156
|
-
models: Object.freeze(["fable"]),
|
|
156
|
+
models: Object.freeze(["fable", "opus"]),
|
|
157
157
|
efforts: Object.freeze(["low", "medium", "high", "xhigh", "max"]),
|
|
158
158
|
auth: Object.freeze({ args: ["auth", "status"], loginHint: "claude auth login" }),
|
|
159
159
|
},
|
|
@@ -8,13 +8,13 @@ import { buildArgv } from "./harness-run.mjs";
|
|
|
8
8
|
|
|
9
9
|
// Launch intent only: these labels are not observed model identity or admission.
|
|
10
10
|
export function nativeCriticLaunch(harness) {
|
|
11
|
-
if (!["fable", "astra"].includes(harness)) {
|
|
12
|
-
throw new Error("usage: native-critic.mjs <fable|astra> <prompt>");
|
|
11
|
+
if (!["fable", "astra", "opus"].includes(harness)) {
|
|
12
|
+
throw new Error("usage: native-critic.mjs <fable|astra|opus> <prompt>");
|
|
13
13
|
}
|
|
14
|
-
const model = harness === "fable" ? "fable" : "gpt-6-astra";
|
|
15
|
-
const command = harness === "fable" ? "claude" : "codex";
|
|
16
|
-
const args = harness === "fable"
|
|
17
|
-
? ["-p", "--model", model, "--output-format", "json", "--no-session-persistence", "--tools", "Read,Glob,Grep", "--setting-sources", "user"]
|
|
14
|
+
const model = harness === "fable" ? "fable" : harness === "opus" ? "opus" : "gpt-6-astra";
|
|
15
|
+
const command = harness === "fable" || harness === "opus" ? "claude" : "codex";
|
|
16
|
+
const args = harness === "fable" || harness === "opus"
|
|
17
|
+
? ["-p", "--model", model, "--effort", "medium", "--output-format", "json", "--no-session-persistence", "--tools", "Read,Glob,Grep", "--setting-sources", "user"]
|
|
18
18
|
: buildArgv({ harness: "codex", model, effort: "low", permission: "read-only", prompt_file: "-" });
|
|
19
19
|
return { model, command, args };
|
|
20
20
|
}
|
|
@@ -36,7 +36,7 @@ async function main() {
|
|
|
36
36
|
mkdirSync(outDir, { recursive: true });
|
|
37
37
|
const client = new HubClient({ serverUrl, authToken: env.KXM_AUTH_TOKEN, name: `${harness}-critic`, purpose: "independent CLI critic", project: env.KXM_PROJECT || ".kxm", model });
|
|
38
38
|
const prompt = promptParts.join(" ");
|
|
39
|
-
const agent = await client.start(() => {});
|
|
39
|
+
const agent = await client.start(() => { });
|
|
40
40
|
const result = await new Promise((resolve) => {
|
|
41
41
|
const child = spawn(command, args, { env, stdio: ["pipe", "pipe", "pipe"], shell: false });
|
|
42
42
|
let stdout = "", stderr = "";
|