@kontextmind/kxm 0.7.123 → 0.7.125
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/workflows/implement-only.yaml +20 -0
- package/.kxm/workflows/review-arch-only.yaml +20 -0
- package/.kxm/workflows/review-cli-only.yaml +20 -0
- package/CHANGELOG.md +33 -3
- package/docs/contributing/assignment-runner.md +15 -4
- package/docs/reference/cli-reference.md +10 -6
- package/docs/reference/config-reference.md +2 -1
- package/docs/reference/workflow-catalog.md +10 -0
- package/docs/reference/workflow-definitions.md +1 -1
- package/package.json +1 -1
- package/plugins/kxm/.claude-plugin/plugin.json +1 -1
- package/plugins/kxm/dist/cli.js +83 -13
- package/plugins/kxm/dist/mcp-server.js +1 -1
- package/plugins/kxm/dist/runtime-supervisor.js +209 -72
- package/plugins/kxm/dist/runtime.js +233 -94
- package/plugins/kxm/package.json +1 -1
- package/plugins/kxm/src/cli/land.ts +2 -0
- package/plugins/kxm/src/cli/project.ts +14 -2
- package/plugins/kxm/src/cli.ts +62 -8
- package/plugins/kxm/src/engine-fold.ts +15 -7
- package/plugins/kxm/src/engine-plan.ts +10 -1
- package/plugins/kxm/src/engine.ts +105 -2
- package/plugins/kxm/src/mcp-server.ts +1 -1
- package/plugins/kxm/src/oneshot-evidence.ts +44 -0
- package/plugins/kxm/src/oneshot-producer.ts +5 -5
- package/plugins/kxm/src/runtime-service.ts +10 -0
- package/plugins/kxm/src/runtime-store.ts +1 -0
- package/plugins/kxm/src/runtime-supervisor.ts +2 -0
- package/schemas/drive-receipt.schema.json +1 -0
- package/schemas/project.schema.json +2 -1
- package/scripts/pr-land.mjs +68 -19
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
schema: kxm.workflow.v1
|
|
2
|
+
description: One implementer step with a drive receipt. Replaces just impl.
|
|
3
|
+
coordinator: coordinator
|
|
4
|
+
limits:
|
|
5
|
+
maxTransitions: 2
|
|
6
|
+
steps:
|
|
7
|
+
- id: implement
|
|
8
|
+
kind: agent
|
|
9
|
+
agent: implementer
|
|
10
|
+
repositories:
|
|
11
|
+
control: write
|
|
12
|
+
maxAttempts: 1
|
|
13
|
+
timeoutMs: 3600000
|
|
14
|
+
on:
|
|
15
|
+
passed:
|
|
16
|
+
target: $terminal
|
|
17
|
+
terminalStatus: completed
|
|
18
|
+
failed:
|
|
19
|
+
target: $terminal
|
|
20
|
+
terminalStatus: failed
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
schema: kxm.workflow.v1
|
|
2
|
+
description: One read-only architecture critic step. Replaces just review-arch.
|
|
3
|
+
coordinator: coordinator
|
|
4
|
+
limits:
|
|
5
|
+
maxTransitions: 2
|
|
6
|
+
steps:
|
|
7
|
+
- id: critic-arch
|
|
8
|
+
kind: agent
|
|
9
|
+
agent: critic-arch
|
|
10
|
+
repositories:
|
|
11
|
+
control: read
|
|
12
|
+
maxAttempts: 1
|
|
13
|
+
timeoutMs: 1800000
|
|
14
|
+
on:
|
|
15
|
+
passed:
|
|
16
|
+
target: $terminal
|
|
17
|
+
terminalStatus: completed
|
|
18
|
+
failed:
|
|
19
|
+
target: $terminal
|
|
20
|
+
terminalStatus: failed
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
schema: kxm.workflow.v1
|
|
2
|
+
description: One read-only CLI critic step. Replaces just review-cli.
|
|
3
|
+
coordinator: coordinator
|
|
4
|
+
limits:
|
|
5
|
+
maxTransitions: 2
|
|
6
|
+
steps:
|
|
7
|
+
- id: critic-cli
|
|
8
|
+
kind: agent
|
|
9
|
+
agent: critic-cli
|
|
10
|
+
repositories:
|
|
11
|
+
control: read
|
|
12
|
+
maxAttempts: 1
|
|
13
|
+
timeoutMs: 1800000
|
|
14
|
+
on:
|
|
15
|
+
passed:
|
|
16
|
+
target: $terminal
|
|
17
|
+
terminalStatus: completed
|
|
18
|
+
failed:
|
|
19
|
+
target: $terminal
|
|
20
|
+
terminalStatus: failed
|
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,23 @@ All notable user-facing changes are documented here. The project follows [Semant
|
|
|
6
6
|
|
|
7
7
|
### Added
|
|
8
8
|
|
|
9
|
+
- **A live agent step uses a configurable one-shot timeout, and a cancelling run recovers when its child has already exited.**
|
|
10
|
+
The bound is the step `timeoutMs`, or the project `limits.agentStepTimeoutMs`
|
|
11
|
+
when the step omits it (minimum 60 seconds, default one hour). A wider step
|
|
12
|
+
is refused at prerequisites (`step_unsupported`, field `timeoutMs`). The
|
|
13
|
+
effective value is written on the one-shot evidence. A `cancelling` run whose
|
|
14
|
+
executing attempt's child already exited settles `executing_unrecorded`.
|
|
15
|
+
Admission is released when a drive closes with a handoff, so a later drive
|
|
16
|
+
is admitted, and `runs status` names the attempt. `implement-only`,
|
|
17
|
+
`review-arch-only`, and `review-cli-only` are one-step workflows, driven with
|
|
18
|
+
`kxm lane run <unit> --workflow <id> --brief <file>`. See
|
|
19
|
+
[kxm lane](docs/reference/cli-reference.md#kxm-lane),
|
|
20
|
+
[runs status](docs/reference/cli-reference.md#kxm-runs-status),
|
|
21
|
+
[project limits](docs/reference/config-reference.md#kxmprojectyaml-kxmprojectv1),
|
|
22
|
+
[step fields](docs/reference/config-reference.md#step-fields),
|
|
23
|
+
[developer workflows](docs/reference/workflow-catalog.md#repository-developer-workflows),
|
|
24
|
+
and the [transport recipes](docs/contributing/assignment-runner.md#transport-only-recipes).
|
|
25
|
+
|
|
9
26
|
- **`kxm land` verifies, regenerates docs, and squash-merges the current branch.**
|
|
10
27
|
The stages are `verify`, `docs`, `push`, `pr`, `rebase`, `unblock`, `merge`,
|
|
11
28
|
`release`, and `milestone`. `npm run verify` is the first stage and is not
|
|
@@ -30,9 +47,9 @@ All notable user-facing changes are documented here. The project follows [Semant
|
|
|
30
47
|
and `cancel`. The `just worktree` and `just worktree-drop` recipes now call
|
|
31
48
|
those verbs. The refusals a user can see are `lane_exists`, `lane_missing`,
|
|
32
49
|
`lane_base_unresolved`, `lane_dirty`, `lane_run_open`, `brief_unreadable` and
|
|
33
|
-
`brief_and_prompt`. A live writer step through `kxm lane run` is
|
|
34
|
-
|
|
35
|
-
|
|
50
|
+
`brief_and_prompt`. A live writer step through `kxm lane run` is bounded by
|
|
51
|
+
the step's `timeoutMs` or the project's `limits.agentStepTimeoutMs`, default
|
|
52
|
+
one hour. See the
|
|
36
53
|
[CLI reference](docs/reference/cli-reference.md#kxm-lane).
|
|
37
54
|
|
|
38
55
|
- **The tailnet docs site is built and served with `kxm docs build` and `kxm docs serve`.**
|
|
@@ -121,6 +138,11 @@ All notable user-facing changes are documented here. The project follows [Semant
|
|
|
121
138
|
|
|
122
139
|
### Changed
|
|
123
140
|
|
|
141
|
+
- **Usage errors under `--json` print a `usage_error` envelope and exit 2.**
|
|
142
|
+
A missing required option, unknown command, or other Commander usage error
|
|
143
|
+
writes `kxm.cli-result.v1` to stdout with `command`, `error`, and `detail`.
|
|
144
|
+
Text mode still prints the Commander line on stderr.
|
|
145
|
+
|
|
124
146
|
- **Windows Validate legs run again on GitHub-hosted `windows-latest`.** The PR gate
|
|
125
147
|
still requires only `Validate (linux, Node 22.19.0)` and `Validate (linux, Node 24)`
|
|
126
148
|
on ARC `kontextmind-doks`. Windows Node 22.19.0 and 24 report `validate:pr` but are
|
|
@@ -386,6 +408,14 @@ All notable user-facing changes are documented here. The project follows [Semant
|
|
|
386
408
|
|
|
387
409
|
### Fixed
|
|
388
410
|
|
|
411
|
+
- **`kxm land` names the pull request from the first commit subject and matches the Release run by time.**
|
|
412
|
+
`--title` sets the title; otherwise the subject of the first commit on the
|
|
413
|
+
branch is used, and a missing subject refuses `land_pr_title_missing`. The
|
|
414
|
+
title is never the branch name. The squash commit title stays `<title> (#n)`.
|
|
415
|
+
After Auto-Release succeeds, the Release run is the first `release.yml` run
|
|
416
|
+
created after that Auto-Release run, with no title filter. Both run ids are
|
|
417
|
+
recorded and printed, and each wait logs one JSON line every two minutes.
|
|
418
|
+
|
|
389
419
|
- **A local `kxm workflow add` writes only what the project loader accepts, where it reads
|
|
390
420
|
it.** Local scope needs a KXM project (`project_not_found` outside one, creating nothing)
|
|
391
421
|
and writes to the project root's `.kxm/workflows/` from any subdirectory. Before writing,
|
|
@@ -313,10 +313,21 @@ It writes `recording-resolved.json` in the record directory. It never changes
|
|
|
313
313
|
|
|
314
314
|
## Transport-only recipes
|
|
315
315
|
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
`
|
|
319
|
-
|
|
316
|
+
Drive a one-step workflow in a lane. Each command writes a drive receipt and
|
|
317
|
+
the checkout fingerprint. `just impl`, `just plan`, `just review-arch`,
|
|
318
|
+
`just review-cli`, and `just impl-bg` are retired in favor of these. The
|
|
319
|
+
recipes stay in the justfile until one real unit has been driven this way.
|
|
320
|
+
|
|
321
|
+
```bash
|
|
322
|
+
kxm lane run <unit> --workflow implement-only --brief <file>
|
|
323
|
+
kxm lane run <unit> --workflow review-arch-only --brief <file>
|
|
324
|
+
kxm lane run <unit> --workflow review-cli-only --brief <file>
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
`just dispatch` still sends one `kxm.harness-request.v1` envelope through
|
|
328
|
+
`scripts/harness-run.mjs` and prints a `kxm.harness-result.v2` envelope. It
|
|
329
|
+
mints no assignment, witness or acceptance proof, so its output cannot be
|
|
330
|
+
accepted. The retired recipes did the same.
|
|
320
331
|
|
|
321
332
|
### Preflight refusals
|
|
322
333
|
|
|
@@ -86,6 +86,7 @@ kxm -V
|
|
|
86
86
|
### JSON results
|
|
87
87
|
|
|
88
88
|
- Every JSON result carries a `schema` field. Most commands print `kxm.cli-result.v1` with `ok` and `command` fields plus command-specific keys.
|
|
89
|
+
- A usage error under `--json` (unknown command or option, missing argument, or missing required option) prints one `kxm.cli-result.v1` object on stdout and exits 2. `ok` is false, `error` is `usage_error`, `command` is the command path, and `detail` is the Commander message. Text mode still prints that message on stderr.
|
|
89
90
|
- Gate-style commands print a `kxm.worker-result.v1` envelope, which adds `worker`, `createdAt`, `outcome` (`passed`, `warning`, or `failed`), and `summary`: `gate validate`, `gate artifacts-exist`, `gate degrade`, `gate signal`, `workflow signal`, `gate github watch`, and `agent worker --dry-run`. Outside `--dry-run`, the `gate` subcommands and `workflow signal` also append the envelope to `.kxm/logs/telemetry.jsonl`.
|
|
90
91
|
- `session brief` prints `kxm.session-brief.v1` and `tenant status` prints `kxm.tenant-status.v1`.
|
|
91
92
|
- The `command` field is not always the words you typed: `hub stop` and `session stop` report `stop`, `session token` reports `auth token`, `routes admit` and `routes disable` report `routes admitted` and `routes disabled`, `models inventory-refresh` reports `models inventory refresh`, `workflow export` reports `retrospective export`, `gate degrade` reports `workflow degrade`, `gate signal` and `workflow signal` report `signal`, `gate github watch` reports `github watch`, `agent worker` reports `worker`, and `improve report` reports `improve`.
|
|
@@ -135,7 +136,7 @@ For this page, 63 `--dry-run` invocations (every command in the first list, the
|
|
|
135
136
|
|
|
136
137
|
Exit 1 covers an `ok: false` result, an unreachable hub for `hub view`, permission expansions for `trust check`, a stopped supervisor for `runtime status`, missing local state, and a planning-only `init`.
|
|
137
138
|
|
|
138
|
-
Exit 2 covers an unknown command or option, a missing argument or required option, a value KXM rejects before acting, `--workspace` where unsupported, conflicting flags, a group run without a subcommand, a removed command (`removed_command`), and `--dry-run` on a command that cannot plan (`dry_run_unsupported`).
|
|
139
|
+
Exit 2 covers an unknown command or option, a missing argument or required option, a value KXM rejects before acting, `--workspace` where unsupported, conflicting flags, a group run without a subcommand, a removed command (`removed_command`), and `--dry-run` on a command that cannot plan (`dry_run_unsupported`). With `--json`, Commander usage errors are `usage_error` envelopes on stdout and exit 2.
|
|
139
140
|
|
|
140
141
|
## Where commands read and write
|
|
141
142
|
|
|
@@ -1431,7 +1432,7 @@ Refusals (exit 1): `lane_missing`, `lane_dirty`, `lane_run_open`, `lane_git_fail
|
|
|
1431
1432
|
|
|
1432
1433
|
Creates the lane when the record is absent (same rules as `create`), refuses `lane_run_open` when the last run is not settled, then runs the same path as `kxm run --lane <unit> --brief <file>` and `kxm runs drive <runId> --lane <unit>`. `--wait` and `--timeout-ms` are passed through. The run id is stored on the record. Prints the run envelope and the drive result. That open-run check may start the Runtime supervisor; `--dry-run` only attaches to a supervisor that is already running. `kxm lane status` never starts the supervisor.
|
|
1433
1434
|
|
|
1434
|
-
`--brief` is required. `--workflow` defaults to the lane project's `defaultWorkflow`. `--base` applies only when the lane is created. A live
|
|
1435
|
+
`--brief` is required. `--workflow` defaults to the lane project's `defaultWorkflow`. `--base` applies only when the lane is created. A live agent step uses the step `timeoutMs` when it is set, otherwise the project `limits.agentStepTimeoutMs` (default 3,600,000). This repository's `implement-only`, `review-arch-only`, and `review-cli-only` workflows are the lane forms of the retired transport recipes.
|
|
1435
1436
|
|
|
1436
1437
|
Refusals (exit 1): `brief_unreadable`, `lane_unit_invalid`, `lane_exists`, `lane_base_unresolved`, `lane_run_open`, `lane_git_failed`, `lane_not_project`, `lanes_unreadable`.
|
|
1437
1438
|
|
|
@@ -1440,7 +1441,7 @@ Refusals (exit 1): `brief_unreadable`, `lane_unit_invalid`, `lane_exists`, `lane
|
|
|
1440
1441
|
Land the current branch. `npm run verify` is the first stage and is not replaced. The command spawns `scripts/pr-land.mjs` from the project root and streams one JSON line per stage. With no `--stage`, the stages run in order and stop at the first refusal. `--dry-run` prints each stage's plan and does not mutate. Squash is the only merge. A required review is reported and not bypassed.
|
|
1441
1442
|
|
|
1442
1443
|
```text
|
|
1443
|
-
kxm land [--pr <n>] [--stage <name>] [--body-file <path>]
|
|
1444
|
+
kxm land [--pr <n>] [--stage <name>] [--title <text>] [--body-file <path>]
|
|
1444
1445
|
```
|
|
1445
1446
|
|
|
1446
1447
|
```bash
|
|
@@ -1451,6 +1452,7 @@ kxm land --stage verify --dry-run
|
|
|
1451
1452
|
|---|---|---|---|
|
|
1452
1453
|
| `--pr` | `<n>` | the open pull request for this branch | Pull request number to reuse |
|
|
1453
1454
|
| `--stage` | `<name>` | all stages | One of `verify`, `docs`, `push`, `pr`, `rebase`, `unblock`, `merge`, `release`, `milestone` |
|
|
1455
|
+
| `--title` | `<text>` | subject of the first commit on the branch | Pull request title passed to `gh pr create`. Never the branch name |
|
|
1454
1456
|
| `--body-file` | `<path>` | none | Body file for `gh pr create`. Required when creating a pull request |
|
|
1455
1457
|
|
|
1456
1458
|
Outside a KXM project the command refuses `project_required` (exit 1). Unknown arguments and an unknown stage exit 2.
|
|
@@ -1462,14 +1464,16 @@ Outside a KXM project the command refuses `project_required` (exit 1). Unknown a
|
|
|
1462
1464
|
| `verify` | Refuses `land_dirty_tree` unless `git status --porcelain` is empty. Skips when `.kxm/logs/land-verify-<tree>.json` for `HEAD^{tree}` is younger than 30 minutes. Otherwise runs `npm run verify` and writes that receipt. |
|
|
1463
1465
|
| `docs` | Runs `plans/kxm-roadmap/update-dashboard.mjs` when that file exists. Commits changes under `docs/roadmap/`, `plans/kxm-roadmap/`, or `docs/architecture/` as `docs(roadmap): regenerate after verify`. Any other path, including `state.json`, is `land_docs_failed`. When the generator is absent the stage passes with `docs: skipped (generator absent)`. |
|
|
1464
1466
|
| `push` | `git push -u origin <branch>`. After a rebase in this run, the push uses `--force-with-lease`. |
|
|
1465
|
-
| `pr` | Reuses the branch's open pull request, or creates one with `gh pr create --body-file`. |
|
|
1467
|
+
| `pr` | Reuses the branch's open pull request, or creates one with `gh pr create --body-file`. The title is `--title` when that option is set; otherwise it is the subject of the first commit (`git log --reverse --format=%s origin/main..HEAD`). `--dry-run` prints that title. |
|
|
1466
1468
|
| `rebase` | When `mergeStateStatus` is `BEHIND` or `DIRTY`: fetch, rebase onto `origin/main`, and resolve only three conflicts (take `plugins/kxm/dist` from main and rebuild; union CHANGELOG Unreleased bullets, ours first; union the tracker "Landed in this tree" list, newest first). Runs `docs` again when the tree changed, re-verifies unless `git merge-tree --write-tree origin/main HEAD` was clean, and pushes with the lease. Five rounds, then `land_conflict_manual`. |
|
|
1467
1469
|
| `unblock` | Reads `statusCheckRollup` and `reviewDecision`. Reruns one failed check with `gh run rerun --failed`. A second failure or `REVIEW_REQUIRED` is `land_blocked`. |
|
|
1468
1470
|
| `merge` | Enables auto-merge with `enablePullRequestAutoMerge` and `mergeMethod: SQUASH`. When the response contains "clean status", squash-merges with `PUT /repos/{owner}/{repo}/pulls/{n}/merge` and commit title `<title> (#n)`. Polls `gh pr view --json state` every 30 seconds for up to 20 minutes. |
|
|
1469
|
-
| `release` | Records the newest `v*` tag before the merge, waits for the Auto-Release run whose title contains the pull request title, requires a newer tag from `git ls-remote --tags origin`, waits for the
|
|
1471
|
+
| `release` | Records the newest `v*` tag before the merge, waits for the Auto-Release run whose title contains the pull request title, requires a newer tag from `git ls-remote --tags origin`, then waits for the first `release.yml` run whose `createdAt` is after that Auto-Release run, with no title filter. Both run ids are written to `.kxm/logs/land-release-context.json` and printed in the stage detail (`PUBLISHED <version> auto-release <id> release <id>`). Each wait prints one JSON line every 2 minutes (`waiting auto-release.yml 4m`). The npm poll is `npm view @kontextmind/kxm@<version> version` every 30 seconds for 10 minutes. |
|
|
1470
1472
|
| `milestone` | Compares `plans/kxm-roadmap/state.json` from before and after `docs`. When a phase goes from an open task to all tasks `done`, or the pull request body contains a `Milestone:` line, prints `deep_review_required: true` and exits 0. The review is the `/reanalyze-roadmap` skill, not this command. An absent state file passes with skipped. |
|
|
1471
1473
|
|
|
1472
|
-
Refusals (exit 1): `project_required`, `land_dirty_tree`, `land_verify_failed`, `land_docs_failed`, `land_push_rejected`, `land_pr_body_missing`, `land_conflict_manual`, `land_blocked`, `land_merge_failed`, `land_release_failed`, `land_publish_timeout`, `land_milestone_failed`.
|
|
1474
|
+
Refusals (exit 1): `project_required`, `land_dirty_tree`, `land_verify_failed`, `land_docs_failed`, `land_push_rejected`, `land_pr_body_missing`, `land_pr_title_missing`, `land_conflict_manual`, `land_blocked`, `land_merge_failed`, `land_release_failed`, `land_publish_timeout`, `land_milestone_failed`.
|
|
1475
|
+
|
|
1476
|
+
`land_pr_title_missing`: the `pr` stage was not given `--title`, and the first commit subject on `origin/main..HEAD` is empty.
|
|
1473
1477
|
|
|
1474
1478
|
## `kxm assign`
|
|
1475
1479
|
|
|
@@ -165,6 +165,7 @@ makes a directory a KXM project. Parser: `loadKxmProject` in
|
|
|
165
165
|
| `limits.maxConcurrentRuns` | Integer, 1 to 128 | Optional, `1` | Runtime admission. A changed bound is refused (`scheduler_policy_conflict`) while admitted or queued runs still use the previous one. |
|
|
166
166
|
| `limits.maxRunDurationMs` | Integer, 0 to 31,536,000,000 | Optional | Runtime. Combined with the workflow's own value; the smaller one wins. |
|
|
167
167
|
| `limits.maxAgentTimeMs` | Integer, 0 to 31,536,000,000 | Optional | Runtime refuses to drive any run while it is set (`limit_unsupported`); leave it out |
|
|
168
|
+
| `limits.agentStepTimeoutMs` | Integer, 60,000 to 31,536,000,000 | Optional, 3,600,000 | Wall clock for one live agent or moa step when the step omits `timeoutMs`. The supervisor passes it to the one-shot producer. A step `timeoutMs` narrower than this wins; a wider step value is refused (`step_unsupported`, field `timeoutMs`) |
|
|
168
169
|
|
|
169
170
|
The Runtime binds one project `id` to one control root per state root, so a
|
|
170
171
|
second checkout with the same ID is refused with `project_home_conflict`.
|
|
@@ -555,7 +556,7 @@ Duration budgets use the wall clock. `maxRunDurationMs` counts from the time the
|
|
|
555
556
|
| `signal` | Identifier | Required for `wait` | Compiled and diffed; the Runtime does not match signals to wait steps yet |
|
|
556
557
|
| `model` | Model selector | Optional | Intersected with each allowed agent's own model ceiling; an empty intersection is `model_selector_incompatible`. Live route resolution ignores it. The Runtime refuses it on gate steps. |
|
|
557
558
|
| `maxAttempts` | Integer, 1 to 20 | Optional, `1` | Entering the step again after this many attempts fails the run (`budget_step_attempts`) |
|
|
558
|
-
| `timeoutMs` | Integer, 0 to 31,536,000,000 | Optional | Not `0`. Gate steps: refused for `artifacts-exist` gates or below the gate's `timeoutMs`.
|
|
559
|
+
| `timeoutMs` | Integer, 0 to 31,536,000,000 | Optional | Not `0`. Gate steps: refused for `artifacts-exist` gates or below the gate's `timeoutMs`. Agent and moa steps: the one-shot spawn uses this value when it is at most `limits.agentStepTimeoutMs`; a wider value is refused (`step_unsupported`, field `timeoutMs`). When the step omits it, the project limit applies (default 3,600,000). The process default of 120 seconds is only the last resort when neither is set |
|
|
559
560
|
| `repositories` | Map of repository ID to `none`, `read`, or `write` | Optional | IDs must be declared (`repository_unknown`); may not exceed the agent's ceiling (`repository_scope_expansion`) |
|
|
560
561
|
| `tools` | `{preset, allow, deny}` | Optional | Must keep the agent's preset and denials and allow only tools the agent allows (`tool_scope_expansion`). The Runtime refuses steps that declare `tools`. |
|
|
561
562
|
| `secrets` | `[{ref, as, required}]` | Optional | Only refs the agent grants (`secret_scope_expansion`). The Runtime refuses steps that declare `secrets`. |
|
|
@@ -1090,6 +1090,16 @@ This cross-reference points each software and security workflow at the KXM pages
|
|
|
1090
1090
|
|
|
1091
1091
|
`kxm run land --dry-run --json` may be refused by live prerequisites until gate-only workflows are supported. The command `kxm land` runs the same stages without a run receipt.
|
|
1092
1092
|
|
|
1093
|
+
## Repository developer workflows
|
|
1094
|
+
|
|
1095
|
+
These three files live in this repository's `.kxm/workflows`. `kxm init` does not write them.
|
|
1096
|
+
|
|
1097
|
+
| Workflow | Replaces |
|
|
1098
|
+
|---|---|
|
|
1099
|
+
| `implement-only` | `just impl`. The recipe is retired once this lands. |
|
|
1100
|
+
| `review-arch-only` | `just review-arch`. The recipe is retired once this lands. |
|
|
1101
|
+
| `review-cli-only` | `just review-cli`. The recipe is retired once this lands. |
|
|
1102
|
+
|
|
1093
1103
|
## Related
|
|
1094
1104
|
|
|
1095
1105
|
- [Workflow definitions](workflow-definitions.md): write a webhook or Runtime workflow for these stages
|
|
@@ -271,7 +271,7 @@ A Runtime workflow is a YAML file whose name is its ID. The [configuration file
|
|
|
271
271
|
| Transitions | `on` is required on every step. `$terminal` needs `terminalStatus` (`completed`, `failed` or `cancelled`). Each back-edge needs its own `maxTransitions`, plus `limits.maxTransitions` | [Transitions and outcomes](config-reference.md#transitions-and-outcomes) |
|
|
272
272
|
| Gate outcome keys | An `expect: pass` gate produces `passed` or `implementation-failure`; an `expect: fail` gate produces `passed` or `repro-missing`. Declare both produced outcomes | [Transitions and outcomes](config-reference.md#transitions-and-outcomes) |
|
|
273
273
|
| `gate_outcome_impossible` | Refused at load when a gate step declares an outcome it never produces, such as `failed`, and omits one it produces. `gate_outcome_renamed` catches underscore spellings | [Transitions and outcomes](config-reference.md#transitions-and-outcomes) |
|
|
274
|
-
| Agent steps | The model returns a JSON `outcome` from the step's declared keys; anything else becomes `failed`, so declare `failed` | [
|
|
274
|
+
| Agent steps | The model returns a JSON `outcome` from the step's declared keys; anything else becomes `failed`, so declare `failed`. `timeoutMs` bounds the one-shot spawn for `agent` and `moa` steps, and must not be wider than the project `limits.agentStepTimeoutMs` | [Step fields](config-reference.md#step-fields) |
|
|
275
275
|
| Not executed yet | Some valid fields make the Runtime hand the run off (`step_unsupported`, `gate_unsupported`, `limit_unsupported`) instead of executing | [Steps the Runtime does not execute yet](config-reference.md#steps-the-runtime-does-not-execute-yet) |
|
|
276
276
|
|
|
277
277
|
`kxm workflow add bug-fix --template implement-and-verify` writes a valid starting file; `dual-critic-review` and `spec-and-plan` are also available. IDs are flat filename-derived slugs, not catalog paths such as `software-engineering/bug-fix`. Installation validates YAML/schema/transitions before writing, including under `--dry-run`. `kxm run <workflow>` creates a run and reports live prerequisites and separate `runs drive/status/receipt` commands. Current live one-shot profiles are read-only, so a writer template can validate but cannot execute live; `task run` refuses incompatible work before creating a run or changing the task. Simulation remains explicitly available and is not proof of implementation. See [`kxm run`](cli-reference.md#kxm-run) and [`kxm runs`](cli-reference.md#kxm-runs).
|
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.125",
|
|
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",
|
package/plugins/kxm/dist/cli.js
CHANGED
|
@@ -21375,13 +21375,16 @@ function objectValue3(value) {
|
|
|
21375
21375
|
function kxmProjectAdmissionLimits(bundle) {
|
|
21376
21376
|
const limits = objectValue3(bundle.project.value.limits);
|
|
21377
21377
|
const maxConcurrentRuns = typeof limits?.maxConcurrentRuns === "number" && Number.isInteger(limits.maxConcurrentRuns) && limits.maxConcurrentRuns >= 1 ? limits.maxConcurrentRuns : 1;
|
|
21378
|
+
const declared = limits?.agentStepTimeoutMs;
|
|
21379
|
+
const agentStepTimeoutMs = typeof declared === "number" && Number.isInteger(declared) && declared >= KXM_MIN_AGENT_STEP_TIMEOUT_MS ? declared : KXM_DEFAULT_AGENT_STEP_TIMEOUT_MS;
|
|
21378
21380
|
return {
|
|
21379
21381
|
maxConcurrentRuns,
|
|
21380
21382
|
...typeof limits?.maxRunDurationMs === "number" ? { maxRunDurationMs: limits.maxRunDurationMs } : {},
|
|
21381
|
-
...typeof limits?.maxAgentTimeMs === "number" ? { maxAgentTimeMs: limits.maxAgentTimeMs } : {}
|
|
21383
|
+
...typeof limits?.maxAgentTimeMs === "number" ? { maxAgentTimeMs: limits.maxAgentTimeMs } : {},
|
|
21384
|
+
agentStepTimeoutMs
|
|
21382
21385
|
};
|
|
21383
21386
|
}
|
|
21384
|
-
var RUNTIME_EPOCH_NS;
|
|
21387
|
+
var KXM_DEFAULT_AGENT_STEP_TIMEOUT_MS, KXM_MIN_AGENT_STEP_TIMEOUT_MS, RUNTIME_EPOCH_NS;
|
|
21385
21388
|
var init_runtime_service = __esm({
|
|
21386
21389
|
"plugins/kxm/src/runtime-service.ts"() {
|
|
21387
21390
|
"use strict";
|
|
@@ -21391,6 +21394,8 @@ var init_runtime_service = __esm({
|
|
|
21391
21394
|
init_engine_plan();
|
|
21392
21395
|
init_runtime_owner();
|
|
21393
21396
|
init_runtime_store();
|
|
21397
|
+
KXM_DEFAULT_AGENT_STEP_TIMEOUT_MS = 36e5;
|
|
21398
|
+
KXM_MIN_AGENT_STEP_TIMEOUT_MS = 6e4;
|
|
21394
21399
|
RUNTIME_EPOCH_NS = process.hrtime.bigint();
|
|
21395
21400
|
}
|
|
21396
21401
|
});
|
|
@@ -21400,6 +21405,7 @@ var TEXT_LIMIT, ARGV_LIMIT, RECORD_LIMIT;
|
|
|
21400
21405
|
var init_oneshot_evidence = __esm({
|
|
21401
21406
|
"plugins/kxm/src/oneshot-evidence.ts"() {
|
|
21402
21407
|
"use strict";
|
|
21408
|
+
init_bindings();
|
|
21403
21409
|
TEXT_LIMIT = 4 * 1024 * 1024;
|
|
21404
21410
|
ARGV_LIMIT = 64 * 1024;
|
|
21405
21411
|
RECORD_LIMIT = 16 * 1024 * 1024;
|
|
@@ -23195,6 +23201,15 @@ function resolveProducerRoute(projectRoot, step, agentId) {
|
|
|
23195
23201
|
const model = selector.slice(slash + 1);
|
|
23196
23202
|
return { provider, model, selector };
|
|
23197
23203
|
}
|
|
23204
|
+
function unsupportedAgentStepTimeout(step, limitMs) {
|
|
23205
|
+
if (step.kind !== "agent" && step.kind !== "moa") return void 0;
|
|
23206
|
+
if (step.timeoutMs === void 0 || step.timeoutMs <= limitMs) return void 0;
|
|
23207
|
+
return {
|
|
23208
|
+
reason: "step_unsupported",
|
|
23209
|
+
field: "timeoutMs",
|
|
23210
|
+
detail: "step timeoutMs is wider than project limits.agentStepTimeoutMs"
|
|
23211
|
+
};
|
|
23212
|
+
}
|
|
23198
23213
|
function kxmLiveRunPrerequisites(bundle, workflowId, projectRoot) {
|
|
23199
23214
|
const workflow = bundle.workflows.get(workflowId);
|
|
23200
23215
|
if (!workflow) throw runtimeError("run_workflow_unknown", workflowId, `workflow ${workflowId} does not exist in this project`);
|
|
@@ -23213,7 +23228,8 @@ function kxmLiveRunPrerequisites(bundle, workflowId, projectRoot) {
|
|
|
23213
23228
|
for (const transition2 of Object.values(step.transitions)) {
|
|
23214
23229
|
if (transition2.to === "step") pending.push(transition2.target);
|
|
23215
23230
|
}
|
|
23216
|
-
const
|
|
23231
|
+
const limitMs = envelope.projectLimits.agentStepTimeoutMs ?? KXM_DEFAULT_AGENT_STEP_TIMEOUT_MS;
|
|
23232
|
+
const unsupported = step.kind === "gate" ? unsupportedGateStep(plan, step, envelope, { projectRoot }) ?? starterGatePrerequisite(step, envelope.gates, projectRoot) : unsupportedAgentStepTimeout(step, limitMs) ?? unsupportedStep(plan, step, "oneshot");
|
|
23217
23233
|
if (unsupported) {
|
|
23218
23234
|
prerequisites.push({ ...unsupported, stepId });
|
|
23219
23235
|
continue;
|
|
@@ -23533,6 +23549,7 @@ var init_engine = __esm({
|
|
|
23533
23549
|
init_context_packet();
|
|
23534
23550
|
init_engine_compile();
|
|
23535
23551
|
init_harness();
|
|
23552
|
+
init_oneshot_evidence();
|
|
23536
23553
|
init_engine_fold();
|
|
23537
23554
|
init_engine_plan();
|
|
23538
23555
|
init_dispatch_context();
|
|
@@ -23556,7 +23573,6 @@ var init_oneshot_producer = __esm({
|
|
|
23556
23573
|
"plugins/kxm/src/oneshot-producer.ts"() {
|
|
23557
23574
|
"use strict";
|
|
23558
23575
|
init_oneshot_evidence();
|
|
23559
|
-
init_bindings();
|
|
23560
23576
|
init_oneshot_process();
|
|
23561
23577
|
init_oneshot_process();
|
|
23562
23578
|
init_harness();
|
|
@@ -26990,7 +27006,10 @@ function formatDriveStatusLine(runStatus, drive) {
|
|
|
26990
27006
|
}
|
|
26991
27007
|
function formatRunStatusLine(run, drive) {
|
|
26992
27008
|
const reason = typeof drive?.receipt?.settlement?.reason === "string" ? drive.receipt.settlement.reason : "";
|
|
26993
|
-
const
|
|
27009
|
+
const handoff = drive?.receipt?.settlement?.handoff;
|
|
27010
|
+
const attemptId = typeof handoff?.attemptId === "string" ? handoff.attemptId : "";
|
|
27011
|
+
const handoffReason = typeof handoff?.reason === "string" ? handoff.reason : "";
|
|
27012
|
+
const statusLabel = run.status === "cancelled" ? formatCancelledStatus(reason) : run.status === "cancelling" && attemptId.length > 0 && handoffReason.length > 0 ? `cancelling (attempt ${attemptId}, ${handoffReason})` : run.status;
|
|
26994
27013
|
return `run ${run.runId}: ${statusLabel} (workflow ${run.workflowId}, updated ${run.updatedAt})`;
|
|
26995
27014
|
}
|
|
26996
27015
|
async function cmdKxmRunStatus(runtime, runId) {
|
|
@@ -37166,6 +37185,7 @@ function cmdLand(runtime, options = {}) {
|
|
|
37166
37185
|
const args = [script];
|
|
37167
37186
|
if (options.pr) args.push("--pr", options.pr);
|
|
37168
37187
|
if (options.stage) args.push("--stage", options.stage);
|
|
37188
|
+
if (options.title) args.push("--title", options.title);
|
|
37169
37189
|
if (options.bodyFile) args.push("--body-file", options.bodyFile);
|
|
37170
37190
|
if (runtime.json) args.push("--json");
|
|
37171
37191
|
if (runtime.dryRun) args.push("--dry-run");
|
|
@@ -51562,7 +51582,7 @@ async function dispatchAgentCliCommand(runtime, toolName, rawArgs) {
|
|
|
51562
51582
|
}
|
|
51563
51583
|
}
|
|
51564
51584
|
}
|
|
51565
|
-
function createProgram(ctx, result) {
|
|
51585
|
+
function createProgram(ctx, result, argv) {
|
|
51566
51586
|
const bind = (action) => {
|
|
51567
51587
|
return async function commandAction(...args) {
|
|
51568
51588
|
const command = args.at(-1) instanceof Command ? args.at(-1) : this;
|
|
@@ -51572,7 +51592,11 @@ function createProgram(ctx, result) {
|
|
|
51572
51592
|
const program2 = new Command(CLI_NAME2);
|
|
51573
51593
|
program2.description("KXM local-first orchestration CLI").version(readInstalledKxmVersion(findKxmRepoRoot(import.meta.url)), "-V, --version", "Print the installed kxm version").exitOverride().configureOutput({
|
|
51574
51594
|
writeOut: (text) => ctx.io.stdout(text),
|
|
51575
|
-
|
|
51595
|
+
// Subcommands inherit this object by reference (copyInheritedSettings).
|
|
51596
|
+
writeErr: (text) => {
|
|
51597
|
+
if (hasJsonFlag(argv)) return;
|
|
51598
|
+
ctx.io.stderr(text);
|
|
51599
|
+
}
|
|
51576
51600
|
}).helpCommand("help", "Show help");
|
|
51577
51601
|
addGlobalOptions(program2);
|
|
51578
51602
|
program2.hook("preAction", (_program, actionCommand) => {
|
|
@@ -51617,10 +51641,11 @@ function createProgram(ctx, result) {
|
|
|
51617
51641
|
...options.timeoutMs !== void 0 ? { timeoutMs: options.timeoutMs } : {}
|
|
51618
51642
|
});
|
|
51619
51643
|
});
|
|
51620
|
-
addGlobalOptions(program2.command("land").description("Land the current branch: verify, regenerate docs, push, pull request, rebase, unblock, squash-merge, release, and milestone")).option("--pr <n>", "Existing pull request number").option("--stage <name>", "Run one stage: verify, docs, push, pr, rebase, unblock, merge, release, or milestone").option("--body-file <path>", "Pull request body file used when creating a pull request").action(async function landAction(options) {
|
|
51644
|
+
addGlobalOptions(program2.command("land").description("Land the current branch: verify, regenerate docs, push, pull request, rebase, unblock, squash-merge, release, and milestone")).option("--pr <n>", "Existing pull request number").option("--stage <name>", "Run one stage: verify, docs, push, pr, rebase, unblock, merge, release, or milestone").option("--title <text>", "Pull request title. Default is the subject of the first commit on the branch").option("--body-file <path>", "Pull request body file used when creating a pull request").action(async function landAction(options) {
|
|
51621
51645
|
result.code = await cmdLand(runtimeFrom(ctx, this), {
|
|
51622
51646
|
...options.pr !== void 0 ? { pr: options.pr } : {},
|
|
51623
51647
|
...options.stage !== void 0 ? { stage: options.stage } : {},
|
|
51648
|
+
...options.title !== void 0 ? { title: options.title } : {},
|
|
51624
51649
|
...options.bodyFile !== void 0 ? { bodyFile: options.bodyFile } : {}
|
|
51625
51650
|
});
|
|
51626
51651
|
});
|
|
@@ -52097,10 +52122,55 @@ function createProgram(ctx, result) {
|
|
|
52097
52122
|
});
|
|
52098
52123
|
return program2;
|
|
52099
52124
|
}
|
|
52100
|
-
function
|
|
52125
|
+
function hasJsonFlag(argv) {
|
|
52126
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
52127
|
+
const arg = argv[index];
|
|
52128
|
+
if (arg === "--") break;
|
|
52129
|
+
if (arg === "--json") return true;
|
|
52130
|
+
}
|
|
52131
|
+
return false;
|
|
52132
|
+
}
|
|
52133
|
+
function commandWords(argv) {
|
|
52134
|
+
const words = [];
|
|
52135
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
52136
|
+
const arg = argv[index];
|
|
52137
|
+
if (arg === "--") break;
|
|
52138
|
+
if (!arg.startsWith("-")) {
|
|
52139
|
+
words.push(arg);
|
|
52140
|
+
continue;
|
|
52141
|
+
}
|
|
52142
|
+
if (arg.includes("=") || arg === "--json" || arg === "--dry-run" || arg === "-h" || arg === "--help" || arg === "-V" || arg === "--version") continue;
|
|
52143
|
+
const next = argv[index + 1];
|
|
52144
|
+
if (next !== void 0 && !next.startsWith("-")) index += 1;
|
|
52145
|
+
}
|
|
52146
|
+
return words;
|
|
52147
|
+
}
|
|
52148
|
+
function commandPathFromArgv(program2, argv) {
|
|
52149
|
+
const names3 = [];
|
|
52150
|
+
let current = program2;
|
|
52151
|
+
for (const word of commandWords(argv)) {
|
|
52152
|
+
const next = current.commands.find((command) => command.name() === word || command.aliases().includes(word));
|
|
52153
|
+
if (!next) break;
|
|
52154
|
+
names3.push(next.name());
|
|
52155
|
+
current = next;
|
|
52156
|
+
}
|
|
52157
|
+
return names3.join(" ");
|
|
52158
|
+
}
|
|
52159
|
+
function mapCommanderError(error, argv, program2, io) {
|
|
52101
52160
|
if (error.exitCode === 0) return 0;
|
|
52102
|
-
|
|
52103
|
-
|
|
52161
|
+
const code = USAGE_ERROR_CODES.has(error.code) ? 2 : error.exitCode || 1;
|
|
52162
|
+
if (code === 2 && hasJsonFlag(argv)) {
|
|
52163
|
+
const command = commandPathFromArgv(program2, argv);
|
|
52164
|
+
io.stdout(`${JSON.stringify({
|
|
52165
|
+
schema: CLI_RESULT_SCHEMA,
|
|
52166
|
+
ok: false,
|
|
52167
|
+
command: command || "kxm",
|
|
52168
|
+
error: "usage_error",
|
|
52169
|
+
detail: error.message
|
|
52170
|
+
})}
|
|
52171
|
+
`);
|
|
52172
|
+
}
|
|
52173
|
+
return code;
|
|
52104
52174
|
}
|
|
52105
52175
|
var MESH_REMOVED_TEXT = "kxm mesh was removed. Use kxm init, kxm hub start|view|stop, and node scripts/smoke-multi-pi.mjs (KXM_SMOKE=1).";
|
|
52106
52176
|
function removedMeshInvocation(argv) {
|
|
@@ -52141,12 +52211,12 @@ async function runCli(argv, env = process.env, io = { stdout: (text) => process.
|
|
|
52141
52211
|
return 2;
|
|
52142
52212
|
}
|
|
52143
52213
|
const result = { code: 0 };
|
|
52144
|
-
const program2 = createProgram({ env, io, cwd }, result);
|
|
52214
|
+
const program2 = createProgram({ env, io, cwd }, result, argv);
|
|
52145
52215
|
try {
|
|
52146
52216
|
await program2.parseAsync(argv, { from: "user" });
|
|
52147
52217
|
return result.code;
|
|
52148
52218
|
} catch (error) {
|
|
52149
|
-
if (error instanceof CommanderError) return mapCommanderError(error);
|
|
52219
|
+
if (error instanceof CommanderError) return mapCommanderError(error, argv, program2, io);
|
|
52150
52220
|
if (error instanceof DryRunRefused) return error.code;
|
|
52151
52221
|
throw error;
|
|
52152
52222
|
}
|
|
@@ -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.125";
|
|
17317
17317
|
var CONFIGURE_PLUGIN = "/plugin configure kxm@kxm";
|
|
17318
17318
|
var inbox = /* @__PURE__ */ new Map();
|
|
17319
17319
|
var notifiedInbox = /* @__PURE__ */ new Set();
|