@kontextmind/kxm 0.7.98 → 0.7.100

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.
@@ -11,7 +11,7 @@
11
11
  "name": "kxm",
12
12
  "source": "./plugins/kxm",
13
13
  "description": "Durable workflows, peer agents, and kxm tui",
14
- "version": "0.7.98",
14
+ "version": "0.7.100",
15
15
  "category": "development",
16
16
  "tags": ["kxm", "multi-agent", "workflows", "mcp"]
17
17
  }
package/CHANGELOG.md CHANGED
@@ -319,6 +319,18 @@ All notable user-facing changes are documented here. The project follows [Semant
319
319
 
320
320
  ### Fixed
321
321
 
322
+ - **A local `kxm workflow add` writes only what the project loader accepts, where it reads
323
+ it.** Local scope needs a KXM project (`project_not_found` outside one, creating nothing)
324
+ and writes to the project root's `.kxm/workflows/` from any subdirectory. Before writing,
325
+ also under `--dry-run`, the project loader checks the project with the new document; if
326
+ it would not load, the command exits 2 with `workflow_invalid`, lists the issues and
327
+ writes nothing. A `--file` in the shape written through 0.7.92 is refused, and
328
+ `--overwrite` repairs a file left in that shape. See
329
+ `docs/reference/cli-reference.md#kxm-workflow-add`.
330
+ - **`kxm workflow add --pick <global-id>` copies the global definition.** In local scope,
331
+ picking a global definition wrote the one-step scaffold under its id and reported
332
+ success. It now writes the global definition's content, with `--description` replacing
333
+ its description, and the loader check refuses one the project cannot load.
322
334
  - **Live `kxm runs drive` can author on an audited writer profile.** A write-repository
323
335
  step on pi (`-a`, with extensions, skills, and the session off) or grok
324
336
  (`--always-approve`, with subagents and web search off) runs against the checkout.
@@ -365,6 +377,13 @@ All notable user-facing changes are documented here. The project follows [Semant
365
377
  - **Workflow prompts no longer point agents at `.kxm/config`**, a path KXM refuses.
366
378
  - **`kxm gate signal` and `kxm workflow wait` inside a KXM project reach the hub for hub
367
379
  runs.** They go to the local Runtime only for a run its store holds.
380
+ - **`kxm peer inbox` lists the requests waiting for a named CLI agent.** It returned
381
+ `{"messages":[]}` every time. The hub now serves `GET /v1/agents/:id/inbox`
382
+ (agent-authenticated, project-scoped): the caller's queued and delivered requests,
383
+ oldest first, acknowledging nothing. Run with a stable `KXM_AGENT_NAME` (for example
384
+ `codex`) to list requests peers queued for it while it was offline, then answer them
385
+ with `kxm peer reply`. The Pi extension's `kxm_inbox` tool now refuses instead of
386
+ returning an empty list, because Pi activates each inbound request as a turn itself.
368
387
 
369
388
  - **The Claude plugin's SessionStart hook is one bundled, read-only, project-scoped
370
389
  script.** The two shell hooks it replaces (`kxm session brief --status` and
@@ -61,6 +61,8 @@ to run one file is in [Develop KXM](development.md#run-one-file-or-one-test).
61
61
  | Dispatch context: only committed, pinned memory and hash-verified promoted skills reach an agent; the rest is a `dispatch_context_*` gap | `engine.test.ts` ("dispatch context: agents receive only committed, pinned memory and verified skills; anything else is withheld with a gap and the step still completes") |
62
62
  | `kxm run` prints the simulated drive command for the new run | `cli.test.ts` ("kxm run prints the simulated drive command for the created run") |
63
63
  | `kxm workflow add --template` writes workflows that validate and plan; an impossible gate outcome is refused | `cli-experience.test.ts` ("workflow add templates validate and plan a run, and a gate outcome the step can never produce is refused") |
64
+ | `kxm workflow add` writes a local workflow only where the loader reads it and only if the project still loads; outside a project it refuses | `role-and-workflow-manager.test.ts` ("workflow add writes only what the project loader accepts, at the project root, and loadKxmProject still loads") |
65
+ | `kxm workflow add --pick <global-id>` copies the global definition into the project, not the scaffold, and the loader check refuses one that does not fit | `role-and-workflow-manager.test.ts` ("workflow add --pick <global-id> copies that global definition into the project, and refuses one the project loader rejects") |
64
66
  | `default.yaml` and the 13-step `fix.yaml` compile deterministically; back edges need budgets | `engine-compile.test.ts` |
65
67
  | Artifact gate: non-empty regular files pass; missing, empty, non-file and escaping paths fail | `artifacts-exist.test.ts` |
66
68
  | Vision gate: strict verdicts, admitted routes only, unreadable images fail closed | `vision-gate.test.ts` |
@@ -210,7 +210,7 @@ CLI (run with the recipient's `KXM_AGENT_NAME`):
210
210
  kxm peer reply msg_779f5e0f22e04ac1af6078589a874971 "The plan is sound. Add a test for a zero discount."
211
211
  ```
212
212
 
213
- Only the recipient can reply, and only once. From the CLI, `kxm peer inbox` always returns an empty list, because a one-shot command has no long-running inbox. Use `kxm dash --screen inbox` to see pending requests.
213
+ Only the recipient can reply, and only once. From the CLI, `kxm peer inbox` lists the requests still waiting for the agent named by `KXM_AGENT_NAME`, including ones queued while it was offline, without acknowledging them. The default `cli-<pid>` name is a new agent on every call, so its list is empty.
214
214
 
215
215
  ## Choose a delivery mode
216
216
 
@@ -291,7 +291,7 @@ Errors about `workflowContext` are covered in [Peer provenance and quorum gates]
291
291
  | A request stays `delivered` | The recipient's turn, tool, or provider call is still running, or a Claude Code session restarted after acknowledging it. | Wait, or cancel and send it again with a new idempotency key. |
292
292
  | `kxm_fanout` returns `pending` | The local wait ended before a reply. | Use the returned message IDs with `kxm_get`, or repeat the exact call. |
293
293
  | Claude Code never sees requests | Channel mode is off or blocked by policy. | Use `kxm_inbox` and `kxm_reply`. |
294
- | `kxm peer inbox` is always empty | The CLI has no long-running inbox. | Use `kxm dash --screen inbox`. |
294
+ | `kxm peer inbox` is always empty | `KXM_AGENT_NAME` is unset, so each call registers a new `cli-<pid>` agent that nobody has addressed. | Set `KXM_AGENT_NAME` to the name peers send to. |
295
295
 
296
296
  For hub-level problems, see [Troubleshoot KXM](../operations/troubleshooting.md).
297
297
 
@@ -91,7 +91,7 @@ kxm -V
91
91
  - 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`.
92
92
  - A result with `ok: false` is written to stderr in both text and JSON mode; everything else goes to stdout. `runtime status` is the exception: when the supervisor is down it prints `ok: true, running: false` on stdout and exits 1.
93
93
  - `peer` subcommands and `workflow checkpoint|record|wait` print the hub's result object as returned, tagged with `schema` but without `ok` or `command`. Their failures print `{"ok":false,"error":"command_failed","detail":"..."}`. Text mode prints the same JSON.
94
- - Several error paths ignore `--json` and print one plain line on stderr: argument checks in `agent worker`, `session start`, `workflow start`, `gate degrade`, `gate signal`, and `gate github watch`; every error from `config`, `role`, `workflow definitions|add|remove|modify` (except the three `workflow add --template` refusals, which honor `--json`), `goal`, `task`, `memory`, `skills` (other than `skills create --dry-run`), `suggest`, `studio`, and `completion`. Check the exit code before parsing stdout.
94
+ - Several error paths ignore `--json` and print one plain line on stderr: argument checks in `agent worker`, `session start`, `workflow start`, `gate degrade`, `gate signal`, and `gate github watch`; every error from `config`, `role`, `workflow definitions|add|remove|modify` (except the `workflow add` refusals listed under it, which honor `--json`), `goal`, `task`, `memory`, `skills` (other than `skills create --dry-run`), `suggest`, `studio`, and `completion`. Check the exit code before parsing stdout.
95
95
  - Every `context` subcommand exits 1 with a Node.js stack trace and no JSON when the hub cannot be reached.
96
96
  - Output is redacted. Values of environment variables whose names contain `TOKEN`, `SECRET`, `KEY`, or `PASSWORD` are replaced with `[redacted]`, and 64-character hex strings are replaced unless they appear in a known digest field such as `configRevision` or `sha256`. `session brief` and `auth token` print the session token itself; treat their output as a credential.
97
97
 
@@ -1961,7 +1961,7 @@ WORKFLOW DEFINITIONS:
1961
1961
  kxm workflow add [workflowId] [--template <name> | --file <path> | --pick [selection]] [--description <text>] [--scope global|local] [--overwrite]
1962
1962
  ```
1963
1963
 
1964
- Add a workflow definition to global or local configuration. With `--template <name>`, the named built-in template is written under the workflow ID. Without a workflow ID, or with `--pick`, you choose from the built-in templates and, for local scope, existing global definitions. With `--file`, the YAML file is copied as-is. Otherwise a one-step scaffold is written: one `implementer` agent step with write access to `control` that ends the run `completed` on `passed` and `failed` on `failed`.
1964
+ Add a workflow definition to global or local configuration. With `--template <name>`, the named built-in template is written under the workflow ID. Without a workflow ID, or with `--pick`, you choose from the built-in templates and, for local scope, existing global definitions; a global definition with a template's ID is not offered. The choice is written under its own ID with its content: the template, or a copy of the global definition's file, with `--description` replacing its description. With `--file`, the YAML file is copied as-is once it passes the local check below. Otherwise a one-step scaffold is written: one `implementer` agent step with write access to `control` that ends the run `completed` on `passed` and `failed` on `failed`.
1965
1965
 
1966
1966
  | Option | Argument | Default | Description |
1967
1967
  |---|---|---|---|
@@ -1975,7 +1975,8 @@ Add a workflow definition to global or local configuration. With `--template <na
1975
1975
  - Templates: `implement-and-verify` runs the `implementer` agent, then the project's `test` gate, and a failing gate (`implementation-failure`) sends the work back to `implement` at most twice. `dual-critic-review` adds two review steps between them, both run as the `coordinator` agent with read access; point `review-arch` and `review-cli` at your own agents for independent critics. `spec-and-plan` plans and then reviews the plan, both as `coordinator`, reading the repository only.
1976
1976
  - The templates and the scaffold are valid `kxm.workflow.v1` definitions that use only what `kxm init` creates: the `coordinator` and `implementer` agents, the `control` repository, and the `test` gate. Each was checked with `kxm init --json` and `kxm run <id> --dry-run` for this page. A new file under `.kxm/workflows/` is a permission expansion that `kxm trust check` asks you to review before you commit it.
1977
1977
  - Writes `<scope dir>/workflows/<id>.yaml`. `kxm run` loads only `.kxm/workflows/`, so a `--scope global` definition is not runnable until it is copied into a project. `--dry-run` plans the write and writes nothing.
1978
- - `--template` refusals exit 2 and honor `--json`: `workflow_template_unknown` (the text names the three templates), `workflow_id_required` (no workflow ID), and `workflow_add_conflict` (combined with `--file` or `--pick`). An existing definition without `--overwrite` exits 1 with a plain `workflow add failed: workflow_already_exists: ...` line, also under `--dry-run`.
1978
+ - Local scope belongs to a KXM project: the file lands in the project root's `.kxm/workflows/` from any subdirectory, and outside a project the command refuses with `project_not_found` and creates nothing. Before writing, the project loader checks the project with the new document in place of any file of that ID, using the parser, schema and bundle rules `kxm run` uses. If the project would not load, the command refuses with `workflow_invalid`, lists each issue and writes nothing, also under `--dry-run`. So `--file` or a picked global definition with a top-level `id` or a `role:` step, the shape `workflow add` wrote through 0.7.92, is refused, and `--overwrite` replaces a file left in that shape. Global scope is not checked, because no loader reads it.
1979
+ - Refusals exit 2 and honor `--json`: `workflow_template_unknown` (the text names the three templates), `workflow_id_required` (no workflow ID), `workflow_add_conflict` (`--template` combined with `--file` or `--pick`), `project_not_found`, and `workflow_invalid` (with `issues`, each `{phase, code, file, message}`). An existing definition without `--overwrite` exits 1 with a plain `workflow add failed: workflow_already_exists: ...` line, also under `--dry-run`.
1979
1980
  - JSON keys: `workflowId`, `id`, `filePath`, `scope`.
1980
1981
 
1981
1982
  Start a first workflow from a template:
@@ -2397,18 +2398,21 @@ kxm peer fanout --targets reviewer critic --content "Independent review of PR 42
2397
2398
  kxm peer inbox
2398
2399
  ```
2399
2400
 
2400
- List inbound peer requests. From the CLI this always returns `{"messages":[]}`: the inbox lives in a long-running harness session, which a one-shot CLI call does not have. Use `kxm dash --screen inbox` to see pending requests.
2401
+ List the inbound peer requests addressed to this agent that still need a reply, oldest first, read from the hub (`GET /v1/agents/<agentId>/inbox`). Only a stable `KXM_AGENT_NAME` has an inbox: a registered name that is offline keeps its agent ID, so a later call as the same name lists what peers queued for it with `peer send --allow-offline`. The default `cli-<pid>` is a new agent on every call, and its list is always empty. Answer each request with `peer reply` under the same name.
2401
2402
 
2402
2403
  | Option | Argument | Default | Description |
2403
2404
  |---|---|---|---|
2404
2405
  | `--payload` | `<json>` | none | JSON payload |
2405
2406
 
2407
+ - Reads only. Listing acknowledges nothing, so a request stays `queued` for push delivery. Output key: `messages`, each a full message record (`id`, `fromName`, `content`, `status` `queued` or `delivered`, `expiresAt`).
2408
+ - The call registers the name while it runs, so it exits 1 (`command_failed`, `agent name already active in project`) when another session holds that name online.
2409
+
2406
2410
  ```bash
2407
- kxm peer inbox --json
2411
+ KXM_AGENT_NAME=codex kxm peer inbox --json
2408
2412
  ```
2409
2413
 
2410
2414
  ```text
2411
- {"schema":"kxm.cli-result.v1","messages":[]}
2415
+ {"schema":"kxm.cli-result.v1","messages":[{"id":"msg_1f0c…","project":"kxm","from":"agt_9d2e…","fromName":"reviewer","to":"agt_4b7a…","toName":"codex","content":"Review the retry loop in src/sync.ts","delivery":"followUp","hops":0,"maxHops":5,"seq":1,"createdAt":"2026-09-23T21:40:00.000Z","expiresAt":"2026-09-24T21:40:00.000Z","status":"queued"}]}
2412
2416
  ```
2413
2417
 
2414
2418
  ### `kxm peer reply`
@@ -3590,7 +3594,6 @@ These are behaviors of the current build that differ from what the help text or
3590
3594
 
3591
3595
  - `kxm routing benchmark` prints constant placeholder figures.
3592
3596
  - `kxm task sync` does not contact GitHub or Jira.
3593
- - `kxm peer inbox` always returns an empty list from the CLI.
3594
3597
  - `kxm context` subcommands crash with a stack trace when the hub is unreachable, and they ignore the persisted hub credential.
3595
3598
  - `kxm completion <shell>` generates a command list that includes a nonexistent `plan` command, omits `models`, `routes`, `explain`, and `ssh`, lists a nonexistent `goal get`, and omits `runs drive`, `runs receipt`, `runtime sync-retry`, and `improve report`.
3596
3599
  - `kxm backup` JSON shows `manifestSha256` redacted.
@@ -889,8 +889,10 @@ compiles and pins the plan, then drives it live, or without models with
889
889
  reads). Both are valid `kxm.workflow.v1` definitions that use only what
890
890
  `kxm init` creates: the `coordinator` and `implementer` agents, the `control`
891
891
  repository, and the `test` gate. The templates route gate failures on
892
- `implementation-failure`. Review the new file with `kxm trust check` before
893
- committing it.
892
+ `implementation-failure`. A local add is checked by the project loader first:
893
+ if the project would not load with the new file, it is refused with
894
+ `workflow_invalid` and nothing is written. Review the new file with
895
+ `kxm trust check` before committing it.
894
896
 
895
897
  ## `.kxm/gates.yaml` (`kxm.gate-registry.v1`)
896
898
 
@@ -49,6 +49,7 @@ Operations routes carry no message bodies. `kxm dash` and `kxm tenant status` us
49
49
  | `POST` | `/v1/agents/register` | Project | Register or resume `name` in `project` with `purpose`, optional `model` and `host`. Returns the agent and a new `agentKey` (201 new, 200 resumed) |
50
50
  | `GET` | `/v1/agents` | Agent | Agents in your project; `?includeOffline=true` adds registered offline agents |
51
51
  | `POST` | `/v1/agents/<agentId>/heartbeat` | Agent | Renew your presence lease (clients send one every 10 seconds) |
52
+ | `GET` | `/v1/agents/<agentId>/inbox` | Agent | Your open inbound requests (`queued` or `delivered`), oldest first, as `{ "messages": [...] }`. Expires overdue requests first; acknowledges nothing and moves no delivery cursor |
52
53
  | `DELETE` | `/v1/agents/<agentId>` | Agent | Mark yourself offline (204) |
53
54
 
54
55
  Key errors: `duplicate_agent_name` (409, the name is online in the project), `invalid_agent_identity` (401). Presence (`online`, `stale`, `offline`) is computed from the hub's clock with a 30-second lease; agents never report their own presence.
@@ -24,7 +24,7 @@ flowchart LR
24
24
  | `kxm_fanout` | Peers | Yes | `kxm peer fanout` |
25
25
  | `kxm_await` | Peers | No | `kxm peer await` |
26
26
  | `kxm_cancel` | Peers | Yes | `kxm peer cancel` |
27
- | `kxm_inbox` | Peers | No | `kxm peer inbox` (always empty from the CLI) |
27
+ | `kxm_inbox` | Peers | No | `kxm peer inbox` (lists a named CLI agent's open requests; refused in Pi) |
28
28
  | `kxm_reply` | Peers | Yes | `kxm peer reply` |
29
29
  | `kxm_workflow_list` | Workflows | No | None; `kxm workflow list` reads the local database instead |
30
30
  | `kxm_workflow_get` | Workflows | No | None; `kxm workflow get` reads the local database instead |
@@ -162,8 +162,8 @@ Returns the message record with `status: "cancelled"`. Cancelling an already can
162
162
  Lists inbound requests that still need a reply. It takes no parameters.
163
163
 
164
164
  - **Claude Code:** returns `{ "messages": [...] }` from the session's inbox, which fills from the hub's event stream while the session is registered. Before returning, it re-reads each request and drops those already answered, cancelled or expired. This is pull mode; see [Pushed channel mode and pull mode](../../plugins/kxm/README.md#pushed-channel-mode-and-pull-mode).
165
- - **Pi:** always returns an empty list. The extension turns each inbound request into a model turn instead.
166
- - **CLI:** always returns an empty list, because a one-shot command holds no inbox.
165
+ - **Pi:** refuses. The extension turns each inbound request into a model turn itself, and that turn's final response is the reply, so listing would offer requests its own queue is about to activate.
166
+ - **CLI:** `kxm peer inbox` reads the agent's open requests from the hub (`GET /v1/agents/<agentId>/inbox`), acknowledging nothing. Run it with a stable `KXM_AGENT_NAME` to see requests queued for that name while it was offline; the default `cli-<pid>` is a new agent on every call, so its list is empty.
167
167
 
168
168
  ### `kxm_reply`
169
169
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kontextmind/kxm",
3
- "version": "0.7.98",
3
+ "version": "0.7.100",
4
4
  "description": "KXM local-first multi-agent orchestration and operator dashboard",
5
5
  "type": "module",
6
6
  "author": "KontextMind",
@@ -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.98",
5
+ "version": "0.7.100",
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",
@@ -9508,7 +9508,10 @@ var init_commands = __esm({
9508
9508
  await reconcileInbox(client, context.inbox, context.notifiedInbox);
9509
9509
  return { messages: [...context.inbox.values()] };
9510
9510
  }
9511
- return { messages: [] };
9511
+ if (context?.hubInbox) return { messages: await client.listInbox() };
9512
+ throw new Error(
9513
+ "kxm_inbox is not available in this session: it activates each inbound request as a turn, and that turn's final response is the reply"
9514
+ );
9512
9515
  }
9513
9516
  },
9514
9517
  {