@kontextmind/kxm 0.7.69 → 0.7.71

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.69",
14
+ "version": "0.7.71",
15
15
  "category": "development",
16
16
  "tags": ["kxm", "multi-agent", "workflows", "mcp"]
17
17
  }
package/CHANGELOG.md CHANGED
@@ -6,6 +6,13 @@ All notable user-facing changes are documented here. The project follows [Semant
6
6
 
7
7
  ### Added
8
8
 
9
+ - **`kxm peer send --allow-offline` queues to a registered offline peer.**
10
+ `POST /v1/messages` accepts `allowOffline: true` (also `kxm_send.allowOffline`): a
11
+ registered agent in the same project is stored `queued` instead of `target_not_found`,
12
+ real-time `publish` is skipped, and the existing reconnect cursor delivers the message
13
+ once on resumption. Unknown names still fail closed. TTL expiry is unchanged; queued
14
+ messages are not evidence unless `workflowContext` was hub-authorized at send.
15
+
9
16
  - **Per-tenant hosting operations, and a backup that covers the whole state set.**
10
17
  `docs/operations.md` gained a *Per-tenant hosted deployment* section (one tenant = one box
11
18
  = one hub; loopback-only hub and supervisor; the tenant's proxy owns TLS and the browser
@@ -22,6 +22,7 @@ npm run verify
22
22
  | All delivery modes, message fields, hop limits, and validation | `test/core/hub-api.test.ts`, `test/core/protocol.test.ts` |
23
23
  | Queue, acknowledgement, visibility, reply, and authorization | `test/core/hub-api.test.ts`, `test/core/hub.test.ts` |
24
24
  | Queued/delivered replay after recipient restart reuses one message record | `test/core/hub.test.ts`, `test/core/extension.test.ts`, `test/core/mcp.test.ts` |
25
+ | Known-offline peer send with `allowOffline` queues, delivers once on resumption, and expires unread by TTL | `test/core/hub-api.test.ts` |
25
26
  | One-to-three-peer fanout, recoverable local timeouts/aborts, exact retries, and partial-error collection | `test/core/client.test.ts`, `test/core/hub-api.test.ts`, `test/core/extension.test.ts`, `test/core/mcp.test.ts` |
26
27
  | TTL expiry, sender cancellation, and terminal retention | `test/core/hub-api.test.ts` |
27
28
  | Terminal inbound cleanup and next-request activation | `test/core/extension.test.ts`, `test/core/mcp.test.ts` |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kontextmind/kxm",
3
- "version": "0.7.69",
3
+ "version": "0.7.71",
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.69",
5
+ "version": "0.7.71",
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",
@@ -18350,9 +18350,7 @@ function findKxmRepoRoot(fromUrl = import.meta.url) {
18350
18350
 
18351
18351
  // plugins/kxm/src/client.ts
18352
18352
  import { createHash as createHash3 } from "node:crypto";
18353
-
18354
- // plugins/kxm/src/workflow.ts
18355
- import { createHash as createHash2 } from "node:crypto";
18353
+ import { hostname } from "node:os";
18356
18354
 
18357
18355
  // plugins/kxm/src/protocol.ts
18358
18356
  import { randomUUID } from "node:crypto";
@@ -18361,6 +18359,7 @@ var MIN_MESSAGE_TTL_MS = 1e3;
18361
18359
  var MAX_MESSAGE_TTL_MS = 7 * 24 * 60 * 6e4;
18362
18360
  var DEFAULT_MESSAGE_RETENTION_MS = 7 * 24 * 60 * 6e4;
18363
18361
  var MAX_BODY_BYTES = 256 * 1024;
18362
+ var MAX_AGENT_HOST_CHARS = 64;
18364
18363
  var ProtocolError = class extends Error {
18365
18364
  statusCode;
18366
18365
  code;
@@ -18453,6 +18452,7 @@ function validateTerminalReceipt(value) {
18453
18452
  }
18454
18453
 
18455
18454
  // plugins/kxm/src/workflow.ts
18455
+ import { createHash as createHash2 } from "node:crypto";
18456
18456
  var PROMOTABLE_JOURNAL_CATEGORIES = ["skill-candidate", "hypothesis", "experiment"];
18457
18457
  var WORKFLOW_TERMINAL_TARGET = "$terminal";
18458
18458
  function normalizeOutcomeValue(value, field) {
@@ -19200,6 +19200,13 @@ var MeshWaitError = class extends Error {
19200
19200
  this.waitStatus = waitStatus;
19201
19201
  }
19202
19202
  };
19203
+ function defaultHostLabel() {
19204
+ try {
19205
+ return hostname().trim().slice(0, MAX_AGENT_HOST_CHARS) || void 0;
19206
+ } catch {
19207
+ return void 0;
19208
+ }
19209
+ }
19203
19210
  function completedFanoutResult(target, message) {
19204
19211
  if (message.status === "queued" || message.status === "delivered") {
19205
19212
  throw new Error(`message ${message.id} is not complete`);
@@ -19289,8 +19296,11 @@ var HubClient = class {
19289
19296
  this.onEvent = void 0;
19290
19297
  this.eventLoop = void 0;
19291
19298
  }
19292
- async listAgents() {
19293
- const result = await this.request("/v1/agents");
19299
+ /** Online peers of this client's project. `includeOffline` also returns
19300
+ * registered members whose lease the hub has already retired. */
19301
+ async listAgents(options = {}) {
19302
+ const path4 = options.includeOffline ? "/v1/agents?includeOffline=true" : "/v1/agents";
19303
+ const result = await this.request(path4);
19294
19304
  return result.agents;
19295
19305
  }
19296
19306
  async send(options) {
@@ -19530,7 +19540,8 @@ var HubClient = class {
19530
19540
  name: this.options.name,
19531
19541
  purpose: this.options.purpose,
19532
19542
  project: this.options.project,
19533
- model: this.options.model
19543
+ model: this.options.model,
19544
+ host: this.options.host ?? defaultHostLabel()
19534
19545
  })
19535
19546
  }, false);
19536
19547
  this.agent = registration.agent;
@@ -19770,14 +19781,19 @@ var AGENT_COMMANDS = [
19770
19781
  group: "peer",
19771
19782
  verb: "list",
19772
19783
  label: "List hub peers",
19773
- description: "List online peer agents in this project's hub pool, including their names and purposes.",
19784
+ description: "List peer agents in this project's hub pool with their names, purposes, host label, and hub-clocked presence (online, stale, offline). Registered offline peers are listed only when includeOffline is set.",
19774
19785
  parameters: {
19775
19786
  type: "object",
19776
- properties: {},
19787
+ properties: {
19788
+ includeOffline: {
19789
+ type: "boolean",
19790
+ description: "Also list registered peers whose hub lease has expired"
19791
+ }
19792
+ },
19777
19793
  additionalProperties: false
19778
19794
  },
19779
- async execute(client) {
19780
- return { agents: await client.listAgents() };
19795
+ async execute(client, args) {
19796
+ return { agents: await client.listAgents({ includeOffline: args.includeOffline === true }) };
19781
19797
  }
19782
19798
  },
19783
19799
  {
@@ -19817,7 +19833,12 @@ var AGENT_COMMANDS = [
19817
19833
  required: ["runId", "stageId", "requirementKey", "attempt"],
19818
19834
  additionalProperties: false
19819
19835
  },
19820
- ttlMs: { type: "number", minimum: 1e3, maximum: 6048e5, description: "Message TTL in milliseconds" }
19836
+ ttlMs: { type: "number", minimum: 1e3, maximum: 6048e5, description: "Message TTL in milliseconds" },
19837
+ allowOffline: {
19838
+ type: "boolean",
19839
+ default: false,
19840
+ description: "Queue the request if the target is a registered offline agent in this project"
19841
+ }
19821
19842
  },
19822
19843
  required: ["target", "content"],
19823
19844
  additionalProperties: false
@@ -19834,7 +19855,8 @@ var AGENT_COMMANDS = [
19834
19855
  ...correlationId ? { correlationId } : {},
19835
19856
  ...idempotencyKey ? { idempotencyKey } : {},
19836
19857
  ...workflowContext ? { workflowContext } : {},
19837
- ...typeof args.ttlMs === "number" ? { ttlMs: args.ttlMs } : {}
19858
+ ...typeof args.ttlMs === "number" ? { ttlMs: args.ttlMs } : {},
19859
+ ...args.allowOffline === true ? { allowOffline: true } : {}
19838
19860
  });
19839
19861
  return { messageId: message.id, status: message.status, target: message.toName };
19840
19862
  }
@@ -26182,8 +26204,8 @@ function printWorker(runtime, worker, payload, text, outcome) {
26182
26204
  }
26183
26205
  function hostMode(runtime) {
26184
26206
  try {
26185
- const hostname = new URL(runtime.serverUrl).hostname;
26186
- if (hostname === "127.0.0.1" || hostname === "localhost" || hostname === "::1") return "local";
26207
+ const hostname2 = new URL(runtime.serverUrl).hostname;
26208
+ if (hostname2 === "127.0.0.1" || hostname2 === "localhost" || hostname2 === "::1") return "local";
26187
26209
  } catch {
26188
26210
  }
26189
26211
  return "hub";
@@ -43081,6 +43103,13 @@ function meshTuiTheme(color) {
43081
43103
  function visibleAgents(snapshot) {
43082
43104
  return snapshot.agents.filter((agent) => agent.model !== "tui");
43083
43105
  }
43106
+ function presenceCell(agent, theme, width = 7) {
43107
+ const presence = agent.presence ?? "n/a";
43108
+ const cell = pad(presence, width);
43109
+ if (presence === "online") return theme.success(cell);
43110
+ if (presence === "stale") return theme.warning(cell);
43111
+ return theme.dim(cell);
43112
+ }
43084
43113
  function panelMetric(snapshot, panel) {
43085
43114
  if (panel === "agents") {
43086
43115
  const agents = visibleAgents(snapshot);
@@ -43131,7 +43160,7 @@ function listLines(snapshot, view, theme) {
43131
43160
  const now = Date.parse(snapshot.fetchedAt);
43132
43161
  const mark = (index) => cursor(theme, index === view.selected, "list", view.pane);
43133
43162
  if (view.tab === "agents") {
43134
- return visibleAgents(snapshot).map((agent, index) => `${mark(index)}${pad(agent.name, 14)} ${agent.online ? theme.success(pad("yes", 3)) : theme.dim(pad("no", 3))} ${pad(agent.model ?? "-", 18)} ${pad(age(agent.lastSeenAt, now), 4)}`);
43163
+ return visibleAgents(snapshot).map((agent, index) => `${mark(index)}${pad(agent.name, 14)} ${presenceCell(agent, theme)} ${pad(agent.host ?? "-", 12)} ${pad(agent.model ?? "-", 14)} ${pad(age(agent.lastSeenAt, now), 4)}`);
43135
43164
  }
43136
43165
  if (view.tab === "tasks" || view.tab === "workflows") {
43137
43166
  const runs = view.tab === "tasks" ? snapshot.runs.filter((run) => run.status === "running" || run.status === "waiting") : snapshot.runs;
@@ -43169,9 +43198,10 @@ function detailLines(snapshot, view, theme) {
43169
43198
  const related = snapshot.openMessages.filter((message) => message.fromName === agent.name || message.toName === agent.name);
43170
43199
  return [
43171
43200
  theme.accent(agent.name),
43172
- `${agent.online ? theme.success("online") : theme.dim("offline")} ${agent.model ?? "-"}`,
43201
+ `${presenceCell(agent, theme, (agent.presence ?? "n/a").length)} ${agent.model ?? "-"}`,
43202
+ `host ${agent.host ?? "-"}`,
43173
43203
  agent.purpose,
43174
- `seen ${age(agent.lastSeenAt, now)} ago`,
43204
+ `seen ${age(agent.lastSeenAt, now)} ago \xB7 lease ${agent.leaseExpiresAt?.slice(11, 19) ?? "n/a"} UTC`,
43175
43205
  "",
43176
43206
  theme.dim("Open work"),
43177
43207
  ...related.length === 0 ? [theme.dim("none")] : related.map((message) => `${message.status} ${message.fromName} \u2192 ${message.toName} ${age(message.createdAt, now)}`)
@@ -43480,7 +43510,7 @@ async function runMeshTui(input) {
43480
43510
  }
43481
43511
  if (!useOpsStream && identity) {
43482
43512
  try {
43483
- const listed = await input.fetchImpl(`${base}/v1/agents`, { headers: headers(identity) });
43513
+ const listed = await input.fetchImpl(`${base}/v1/agents?includeOffline=true`, { headers: headers(identity) });
43484
43514
  if (listed.ok) {
43485
43515
  const body = await readJson(listed);
43486
43516
  const byId = new Map(local.agents.map((agent) => [agent.id, agent]));
@@ -47738,10 +47768,10 @@ function createProgram(ctx, result) {
47738
47768
  });
47739
47769
  const peer = addGlobalOptions(program2.command("peer").description("Peer agent messaging and coordination"));
47740
47770
  peer.helpCommand("help", "Show peer help");
47741
- addGlobalOptions(peer.command("list").description("List online peer agents in this project's hub pool")).option("--payload <json>", "JSON payload").action(async function peerListAction(opts) {
47771
+ addGlobalOptions(peer.command("list").description("List peer agents in this project's hub pool with host and presence")).option("--include-offline", "Also list registered peers whose hub lease has expired").option("--payload <json>", "JSON payload").action(async function peerListAction(opts) {
47742
47772
  result.code = await dispatchAgentCliCommand(runtimeFrom(ctx, this), "kxm_list", opts ?? {});
47743
47773
  });
47744
- addGlobalOptions(peer.command("send [target] [content]").description("Send a focused request to a peer agent")).option("--target <name>", "Peer name or agent ID").option("--content <text>", "Focused request content").option("--delivery <mode>", "steer, followUp, or nextTurn").option("--correlation-id <id>", "Task grouping ID").option("--idempotency-key <key>", "Deduplication key").option("--workflow-context <json>", "Workflow context JSON").option("--ttl-ms <ms>", "Message TTL in milliseconds").option("--payload <json>", "JSON payload").action(async function peerSendAction(target, content, opts) {
47774
+ addGlobalOptions(peer.command("send [target] [content]").description("Send a focused request to a peer agent")).option("--target <name>", "Peer name or agent ID").option("--content <text>", "Focused request content").option("--delivery <mode>", "steer, followUp, or nextTurn").option("--correlation-id <id>", "Task grouping ID").option("--idempotency-key <key>", "Deduplication key").option("--workflow-context <json>", "Workflow context JSON").option("--ttl-ms <ms>", "Message TTL in milliseconds").option("--allow-offline", "Queue the request if the target is registered but offline").option("--payload <json>", "JSON payload").action(async function peerSendAction(target, content, opts) {
47745
47775
  const options = { ...opts, ...target ? { target } : {}, ...content ? { content } : {} };
47746
47776
  result.code = await dispatchAgentCliCommand(runtimeFrom(ctx, this), "kxm_send", options);
47747
47777
  });
@@ -1,11 +1,13 @@
1
1
  // plugins/kxm/src/client.ts
2
2
  import { createHash } from "node:crypto";
3
+ import { hostname } from "node:os";
3
4
 
4
5
  // plugins/kxm/src/protocol.ts
5
6
  var DEFAULT_MESSAGE_TTL_MS = 24 * 60 * 6e4;
6
7
  var MAX_MESSAGE_TTL_MS = 7 * 24 * 60 * 6e4;
7
8
  var DEFAULT_MESSAGE_RETENTION_MS = 7 * 24 * 60 * 6e4;
8
9
  var MAX_BODY_BYTES = 256 * 1024;
10
+ var MAX_AGENT_HOST_CHARS = 64;
9
11
 
10
12
  // plugins/kxm/src/workflow.ts
11
13
  function canonicalWorkflowEvidenceKey(value) {
@@ -21,6 +23,13 @@ var MeshWaitError = class extends Error {
21
23
  this.waitStatus = waitStatus;
22
24
  }
23
25
  };
26
+ function defaultHostLabel() {
27
+ try {
28
+ return hostname().trim().slice(0, MAX_AGENT_HOST_CHARS) || void 0;
29
+ } catch {
30
+ return void 0;
31
+ }
32
+ }
24
33
  function completedFanoutResult(target, message) {
25
34
  if (message.status === "queued" || message.status === "delivered") {
26
35
  throw new Error(`message ${message.id} is not complete`);
@@ -110,8 +119,11 @@ var HubClient = class {
110
119
  this.onEvent = void 0;
111
120
  this.eventLoop = void 0;
112
121
  }
113
- async listAgents() {
114
- const result = await this.request("/v1/agents");
122
+ /** Online peers of this client's project. `includeOffline` also returns
123
+ * registered members whose lease the hub has already retired. */
124
+ async listAgents(options = {}) {
125
+ const path = options.includeOffline ? "/v1/agents?includeOffline=true" : "/v1/agents";
126
+ const result = await this.request(path);
115
127
  return result.agents;
116
128
  }
117
129
  async send(options) {
@@ -351,7 +363,8 @@ var HubClient = class {
351
363
  name: this.options.name,
352
364
  purpose: this.options.purpose,
353
365
  project: this.options.project,
354
- model: this.options.model
366
+ model: this.options.model,
367
+ host: this.options.host ?? defaultHostLabel()
355
368
  })
356
369
  }, false);
357
370
  this.agent = registration.agent;
@@ -12,6 +12,17 @@ var DEFAULT_RATE_LIMIT_MAX = 600;
12
12
  var DEFAULT_RATE_LIMIT_WINDOW_MS = 6e4;
13
13
  var MAX_BODY_BYTES = 256 * 1024;
14
14
  var MAX_CONTENT_CHARS = 32e3;
15
+ var MAX_AGENT_HOST_CHARS = 64;
16
+ function agentPresenceView(agent, staleAfterMs = DEFAULT_STALE_AFTER_MS, now = Date.now()) {
17
+ const lastSeenMs = Date.parse(agent.lastSeenAt);
18
+ const leaseExpiresAtMs = (Number.isFinite(lastSeenMs) ? lastSeenMs : 0) + staleAfterMs;
19
+ const leaseExpiresAt = new Date(leaseExpiresAtMs).toISOString();
20
+ if (!agent.online) return { leaseExpiresAt, presence: "offline" };
21
+ return { leaseExpiresAt, presence: now < leaseExpiresAtMs ? "online" : "stale" };
22
+ }
23
+ function toAgentRecord(agent, staleAfterMs, now) {
24
+ return { ...agent, ...agentPresenceView(agent, staleAfterMs, now) };
25
+ }
15
26
  var ProtocolError = class extends Error {
16
27
  statusCode;
17
28
  code;
@@ -945,14 +956,19 @@ var AGENT_COMMANDS = [
945
956
  group: "peer",
946
957
  verb: "list",
947
958
  label: "List hub peers",
948
- description: "List online peer agents in this project's hub pool, including their names and purposes.",
959
+ description: "List peer agents in this project's hub pool with their names, purposes, host label, and hub-clocked presence (online, stale, offline). Registered offline peers are listed only when includeOffline is set.",
949
960
  parameters: {
950
961
  type: "object",
951
- properties: {},
962
+ properties: {
963
+ includeOffline: {
964
+ type: "boolean",
965
+ description: "Also list registered peers whose hub lease has expired"
966
+ }
967
+ },
952
968
  additionalProperties: false
953
969
  },
954
- async execute(client) {
955
- return { agents: await client.listAgents() };
970
+ async execute(client, args) {
971
+ return { agents: await client.listAgents({ includeOffline: args.includeOffline === true }) };
956
972
  }
957
973
  },
958
974
  {
@@ -992,7 +1008,12 @@ var AGENT_COMMANDS = [
992
1008
  required: ["runId", "stageId", "requirementKey", "attempt"],
993
1009
  additionalProperties: false
994
1010
  },
995
- ttlMs: { type: "number", minimum: 1e3, maximum: 6048e5, description: "Message TTL in milliseconds" }
1011
+ ttlMs: { type: "number", minimum: 1e3, maximum: 6048e5, description: "Message TTL in milliseconds" },
1012
+ allowOffline: {
1013
+ type: "boolean",
1014
+ default: false,
1015
+ description: "Queue the request if the target is a registered offline agent in this project"
1016
+ }
996
1017
  },
997
1018
  required: ["target", "content"],
998
1019
  additionalProperties: false
@@ -1009,7 +1030,8 @@ var AGENT_COMMANDS = [
1009
1030
  ...correlationId ? { correlationId } : {},
1010
1031
  ...idempotencyKey ? { idempotencyKey } : {},
1011
1032
  ...workflowContext ? { workflowContext } : {},
1012
- ...typeof args.ttlMs === "number" ? { ttlMs: args.ttlMs } : {}
1033
+ ...typeof args.ttlMs === "number" ? { ttlMs: args.ttlMs } : {},
1034
+ ...args.allowOffline === true ? { allowOffline: true } : {}
1013
1035
  });
1014
1036
  return { messageId: message.id, status: message.status, target: message.toName };
1015
1037
  }
@@ -2005,6 +2027,7 @@ export {
2005
2027
  DEFAULT_RATE_LIMIT_WINDOW_MS,
2006
2028
  DEFAULT_STALE_AFTER_MS,
2007
2029
  LOG_LEVEL_PRIORITY,
2030
+ MAX_AGENT_HOST_CHARS,
2008
2031
  MAX_BODY_BYTES,
2009
2032
  MAX_CONTENT_CHARS,
2010
2033
  MAX_CONTEXT_ITEM_IDS,
@@ -2020,6 +2043,7 @@ export {
2020
2043
  TERMINAL_RECEIPT_SCHEMA,
2021
2044
  WORKER_RESULT_SCHEMA,
2022
2045
  WORKER_SCHEMA,
2046
+ agentPresenceView,
2023
2047
  agentWorker,
2024
2048
  behavioralConfigHash,
2025
2049
  clearSessionTokenFromDisk,
@@ -2060,6 +2084,7 @@ export {
2060
2084
  rotateLogFiles,
2061
2085
  sessionTokenPath,
2062
2086
  timingSafeStringCompare,
2087
+ toAgentRecord,
2063
2088
  validateTerminalReceipt,
2064
2089
  workerResult
2065
2090
  };
@@ -14534,6 +14534,7 @@ import { dirname, join, resolve } from "node:path";
14534
14534
 
14535
14535
  // plugins/kxm/src/client.ts
14536
14536
  import { createHash } from "node:crypto";
14537
+ import { hostname } from "node:os";
14537
14538
 
14538
14539
  // plugins/kxm/src/protocol.ts
14539
14540
  var DEFAULT_MESSAGE_TTL_MS = 24 * 60 * 6e4;
@@ -14541,6 +14542,7 @@ var MAX_MESSAGE_TTL_MS = 7 * 24 * 60 * 6e4;
14541
14542
  var DEFAULT_MESSAGE_RETENTION_MS = 7 * 24 * 60 * 6e4;
14542
14543
  var MAX_BODY_BYTES = 256 * 1024;
14543
14544
  var MAX_CONTENT_CHARS = 32e3;
14545
+ var MAX_AGENT_HOST_CHARS = 64;
14544
14546
 
14545
14547
  // plugins/kxm/src/workflow.ts
14546
14548
  function canonicalWorkflowEvidenceKey(value) {
@@ -14556,6 +14558,13 @@ var MeshWaitError = class extends Error {
14556
14558
  this.waitStatus = waitStatus;
14557
14559
  }
14558
14560
  };
14561
+ function defaultHostLabel() {
14562
+ try {
14563
+ return hostname().trim().slice(0, MAX_AGENT_HOST_CHARS) || void 0;
14564
+ } catch {
14565
+ return void 0;
14566
+ }
14567
+ }
14559
14568
  function completedFanoutResult(target, message) {
14560
14569
  if (message.status === "queued" || message.status === "delivered") {
14561
14570
  throw new Error(`message ${message.id} is not complete`);
@@ -14645,8 +14654,11 @@ var HubClient = class {
14645
14654
  this.onEvent = void 0;
14646
14655
  this.eventLoop = void 0;
14647
14656
  }
14648
- async listAgents() {
14649
- const result2 = await this.request("/v1/agents");
14657
+ /** Online peers of this client's project. `includeOffline` also returns
14658
+ * registered members whose lease the hub has already retired. */
14659
+ async listAgents(options = {}) {
14660
+ const path = options.includeOffline ? "/v1/agents?includeOffline=true" : "/v1/agents";
14661
+ const result2 = await this.request(path);
14650
14662
  return result2.agents;
14651
14663
  }
14652
14664
  async send(options) {
@@ -14886,7 +14898,8 @@ var HubClient = class {
14886
14898
  name: this.options.name,
14887
14899
  purpose: this.options.purpose,
14888
14900
  project: this.options.project,
14889
- model: this.options.model
14901
+ model: this.options.model,
14902
+ host: this.options.host ?? defaultHostLabel()
14890
14903
  })
14891
14904
  }, false);
14892
14905
  this.agent = registration.agent;
@@ -15014,14 +15027,19 @@ var AGENT_COMMANDS = [
15014
15027
  group: "peer",
15015
15028
  verb: "list",
15016
15029
  label: "List hub peers",
15017
- description: "List online peer agents in this project's hub pool, including their names and purposes.",
15030
+ description: "List peer agents in this project's hub pool with their names, purposes, host label, and hub-clocked presence (online, stale, offline). Registered offline peers are listed only when includeOffline is set.",
15018
15031
  parameters: {
15019
15032
  type: "object",
15020
- properties: {},
15033
+ properties: {
15034
+ includeOffline: {
15035
+ type: "boolean",
15036
+ description: "Also list registered peers whose hub lease has expired"
15037
+ }
15038
+ },
15021
15039
  additionalProperties: false
15022
15040
  },
15023
- async execute(client) {
15024
- return { agents: await client.listAgents() };
15041
+ async execute(client, args) {
15042
+ return { agents: await client.listAgents({ includeOffline: args.includeOffline === true }) };
15025
15043
  }
15026
15044
  },
15027
15045
  {
@@ -15061,7 +15079,12 @@ var AGENT_COMMANDS = [
15061
15079
  required: ["runId", "stageId", "requirementKey", "attempt"],
15062
15080
  additionalProperties: false
15063
15081
  },
15064
- ttlMs: { type: "number", minimum: 1e3, maximum: 6048e5, description: "Message TTL in milliseconds" }
15082
+ ttlMs: { type: "number", minimum: 1e3, maximum: 6048e5, description: "Message TTL in milliseconds" },
15083
+ allowOffline: {
15084
+ type: "boolean",
15085
+ default: false,
15086
+ description: "Queue the request if the target is a registered offline agent in this project"
15087
+ }
15065
15088
  },
15066
15089
  required: ["target", "content"],
15067
15090
  additionalProperties: false
@@ -15078,7 +15101,8 @@ var AGENT_COMMANDS = [
15078
15101
  ...correlationId ? { correlationId } : {},
15079
15102
  ...idempotencyKey ? { idempotencyKey } : {},
15080
15103
  ...workflowContext ? { workflowContext } : {},
15081
- ...typeof args.ttlMs === "number" ? { ttlMs: args.ttlMs } : {}
15104
+ ...typeof args.ttlMs === "number" ? { ttlMs: args.ttlMs } : {},
15105
+ ...args.allowOffline === true ? { allowOffline: true } : {}
15082
15106
  });
15083
15107
  return { messageId: message.id, status: message.status, target: message.toName };
15084
15108
  }
@@ -15755,12 +15755,14 @@ var StdioServerTransport = class {
15755
15755
 
15756
15756
  // plugins/kxm/src/client.ts
15757
15757
  import { createHash } from "node:crypto";
15758
+ import { hostname } from "node:os";
15758
15759
 
15759
15760
  // plugins/kxm/src/protocol.ts
15760
15761
  var DEFAULT_MESSAGE_TTL_MS = 24 * 60 * 6e4;
15761
15762
  var MAX_MESSAGE_TTL_MS = 7 * 24 * 60 * 6e4;
15762
15763
  var DEFAULT_MESSAGE_RETENTION_MS = 7 * 24 * 60 * 6e4;
15763
15764
  var MAX_BODY_BYTES = 256 * 1024;
15765
+ var MAX_AGENT_HOST_CHARS = 64;
15764
15766
 
15765
15767
  // plugins/kxm/src/workflow.ts
15766
15768
  function canonicalWorkflowEvidenceKey(value) {
@@ -15776,6 +15778,13 @@ var MeshWaitError = class extends Error {
15776
15778
  this.waitStatus = waitStatus;
15777
15779
  }
15778
15780
  };
15781
+ function defaultHostLabel() {
15782
+ try {
15783
+ return hostname().trim().slice(0, MAX_AGENT_HOST_CHARS) || void 0;
15784
+ } catch {
15785
+ return void 0;
15786
+ }
15787
+ }
15779
15788
  function completedFanoutResult(target, message) {
15780
15789
  if (message.status === "queued" || message.status === "delivered") {
15781
15790
  throw new Error(`message ${message.id} is not complete`);
@@ -15865,8 +15874,11 @@ var HubClient = class {
15865
15874
  this.onEvent = void 0;
15866
15875
  this.eventLoop = void 0;
15867
15876
  }
15868
- async listAgents() {
15869
- const result = await this.request("/v1/agents");
15877
+ /** Online peers of this client's project. `includeOffline` also returns
15878
+ * registered members whose lease the hub has already retired. */
15879
+ async listAgents(options = {}) {
15880
+ const path = options.includeOffline ? "/v1/agents?includeOffline=true" : "/v1/agents";
15881
+ const result = await this.request(path);
15870
15882
  return result.agents;
15871
15883
  }
15872
15884
  async send(options) {
@@ -16106,7 +16118,8 @@ var HubClient = class {
16106
16118
  name: this.options.name,
16107
16119
  purpose: this.options.purpose,
16108
16120
  project: this.options.project,
16109
- model: this.options.model
16121
+ model: this.options.model,
16122
+ host: this.options.host ?? defaultHostLabel()
16110
16123
  })
16111
16124
  }, false);
16112
16125
  this.agent = registration.agent;
@@ -16325,14 +16338,19 @@ var AGENT_COMMANDS = [
16325
16338
  group: "peer",
16326
16339
  verb: "list",
16327
16340
  label: "List hub peers",
16328
- description: "List online peer agents in this project's hub pool, including their names and purposes.",
16341
+ description: "List peer agents in this project's hub pool with their names, purposes, host label, and hub-clocked presence (online, stale, offline). Registered offline peers are listed only when includeOffline is set.",
16329
16342
  parameters: {
16330
16343
  type: "object",
16331
- properties: {},
16344
+ properties: {
16345
+ includeOffline: {
16346
+ type: "boolean",
16347
+ description: "Also list registered peers whose hub lease has expired"
16348
+ }
16349
+ },
16332
16350
  additionalProperties: false
16333
16351
  },
16334
- async execute(client) {
16335
- return { agents: await client.listAgents() };
16352
+ async execute(client, args) {
16353
+ return { agents: await client.listAgents({ includeOffline: args.includeOffline === true }) };
16336
16354
  }
16337
16355
  },
16338
16356
  {
@@ -16372,7 +16390,12 @@ var AGENT_COMMANDS = [
16372
16390
  required: ["runId", "stageId", "requirementKey", "attempt"],
16373
16391
  additionalProperties: false
16374
16392
  },
16375
- ttlMs: { type: "number", minimum: 1e3, maximum: 6048e5, description: "Message TTL in milliseconds" }
16393
+ ttlMs: { type: "number", minimum: 1e3, maximum: 6048e5, description: "Message TTL in milliseconds" },
16394
+ allowOffline: {
16395
+ type: "boolean",
16396
+ default: false,
16397
+ description: "Queue the request if the target is a registered offline agent in this project"
16398
+ }
16376
16399
  },
16377
16400
  required: ["target", "content"],
16378
16401
  additionalProperties: false
@@ -16389,7 +16412,8 @@ var AGENT_COMMANDS = [
16389
16412
  ...correlationId ? { correlationId } : {},
16390
16413
  ...idempotencyKey ? { idempotencyKey } : {},
16391
16414
  ...workflowContext ? { workflowContext } : {},
16392
- ...typeof args.ttlMs === "number" ? { ttlMs: args.ttlMs } : {}
16415
+ ...typeof args.ttlMs === "number" ? { ttlMs: args.ttlMs } : {},
16416
+ ...args.allowOffline === true ? { allowOffline: true } : {}
16393
16417
  });
16394
16418
  return { messageId: message.id, status: message.status, target: message.toName };
16395
16419
  }
@@ -17121,7 +17145,7 @@ async function deliverInboxNotification(messageId, delivered, notify) {
17121
17145
  }
17122
17146
 
17123
17147
  // plugins/kxm/src/mcp-server.ts
17124
- var VERSION = "0.7.69";
17148
+ var VERSION = "0.7.71";
17125
17149
  var inbox = /* @__PURE__ */ new Map();
17126
17150
  var notifiedInbox = /* @__PURE__ */ new Set();
17127
17151
  var meshClient;