@kontextmind/kxm 0.7.44 → 0.7.45

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.44",
14
+ "version": "0.7.45",
15
15
  "category": "development",
16
16
  "tags": ["kxm", "multi-agent", "workflows", "mcp"]
17
17
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kontextmind/kxm",
3
- "version": "0.7.44",
3
+ "version": "0.7.45",
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.44",
5
+ "version": "0.7.45",
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",
@@ -17121,7 +17121,7 @@ async function deliverInboxNotification(messageId, delivered, notify) {
17121
17121
  }
17122
17122
 
17123
17123
  // plugins/kxm/src/mcp-server.ts
17124
- var VERSION = "0.7.44";
17124
+ var VERSION = "0.7.45";
17125
17125
  var inbox = /* @__PURE__ */ new Map();
17126
17126
  var notifiedInbox = /* @__PURE__ */ new Set();
17127
17127
  var meshClient;
@@ -21865,7 +21865,8 @@ function openKxmRuntimeContext(projectRoot, options) {
21865
21865
  projectRoot: bundle.projectRoot,
21866
21866
  projectId: registration.projectId,
21867
21867
  homeRuntimeId: registration.homeRuntimeId,
21868
- eventStore
21868
+ eventStore,
21869
+ ...options.budgetClock ? { budgetClock: options.budgetClock } : {}
21869
21870
  };
21870
21871
  } catch (error) {
21871
21872
  registry.close();
@@ -25576,7 +25577,11 @@ function declaredRunDurationBudget(context, runId) {
25576
25577
  return void 0;
25577
25578
  }
25578
25579
  }
25579
- function runDurationOverrunPayload(context, runId, nowIso = (/* @__PURE__ */ new Date()).toISOString()) {
25580
+ function budgetNowIso(context) {
25581
+ if (process.env.KXM_DETERMINISTIC_TEST_CLOCK === "1" && context.budgetClock) return context.budgetClock();
25582
+ return (/* @__PURE__ */ new Date()).toISOString();
25583
+ }
25584
+ function runDurationOverrunPayload(context, runId, nowIso = budgetNowIso(context)) {
25580
25585
  const declared = declaredRunDurationBudget(context, runId);
25581
25586
  if (!declared) return void 0;
25582
25587
  const run = context.eventStore.run(runId);
@@ -25639,7 +25644,7 @@ function armRunDurationBudgetTimer(context, runId) {
25639
25644
  const fire = () => {
25640
25645
  timer = void 0;
25641
25646
  if (cleared || isKxmRuntimeContextClosed(context)) return;
25642
- const nowIso = (/* @__PURE__ */ new Date()).toISOString();
25647
+ const nowIso = budgetNowIso(context);
25643
25648
  const elapsedMs = elapsedMsBetween(state.runningSince, nowIso);
25644
25649
  if (elapsedMs < declared.budgetMs) {
25645
25650
  arm(declared.budgetMs - elapsedMs);
@@ -22308,7 +22308,8 @@ function openKxmRuntimeContext(projectRoot, options) {
22308
22308
  projectRoot: bundle.projectRoot,
22309
22309
  projectId: registration.projectId,
22310
22310
  homeRuntimeId: registration.homeRuntimeId,
22311
- eventStore
22311
+ eventStore,
22312
+ ...options.budgetClock ? { budgetClock: options.budgetClock } : {}
22312
22313
  };
22313
22314
  } catch (error) {
22314
22315
  registry.close();
@@ -26088,7 +26089,11 @@ function declaredRunDurationBudget(context, runId) {
26088
26089
  return void 0;
26089
26090
  }
26090
26091
  }
26091
- function runDurationOverrunPayload(context, runId, nowIso2 = (/* @__PURE__ */ new Date()).toISOString()) {
26092
+ function budgetNowIso(context) {
26093
+ if (process.env.KXM_DETERMINISTIC_TEST_CLOCK === "1" && context.budgetClock) return context.budgetClock();
26094
+ return (/* @__PURE__ */ new Date()).toISOString();
26095
+ }
26096
+ function runDurationOverrunPayload(context, runId, nowIso2 = budgetNowIso(context)) {
26092
26097
  const declared = declaredRunDurationBudget(context, runId);
26093
26098
  if (!declared) return void 0;
26094
26099
  const run = context.eventStore.run(runId);
@@ -26151,7 +26156,7 @@ function armRunDurationBudgetTimer(context, runId) {
26151
26156
  const fire = () => {
26152
26157
  timer = void 0;
26153
26158
  if (cleared || isKxmRuntimeContextClosed(context)) return;
26154
- const nowIso2 = (/* @__PURE__ */ new Date()).toISOString();
26159
+ const nowIso2 = budgetNowIso(context);
26155
26160
  const elapsedMs = elapsedMsBetween(state.runningSince, nowIso2);
26156
26161
  if (elapsedMs < declared.budgetMs) {
26157
26162
  arm(declared.budgetMs - elapsedMs);
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-plugin",
3
- "version": "0.7.44",
3
+ "version": "0.7.45",
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "engines": {
@@ -2352,10 +2352,25 @@ function declaredRunDurationBudget(context: KxmRuntimeContext, runId: string): K
2352
2352
  }
2353
2353
  }
2354
2354
 
2355
+ /**
2356
+ * "Now" for run-duration budget accounting.
2357
+ *
2358
+ * Budgets are always measured from the log-derived `runningSince`; this only
2359
+ * supplies the other side of that subtraction. An injected `budgetClock` is
2360
+ * honoured only under `KXM_DETERMINISTIC_TEST_CLOCK=1`, so deterministic engine
2361
+ * tests can pin a budget boundary while no production path — including the
2362
+ * supervisor, which never passes a clock — can postpone a cancellation by
2363
+ * feeding the engine a stale time.
2364
+ */
2365
+ function budgetNowIso(context: KxmRuntimeContext): string {
2366
+ if (process.env.KXM_DETERMINISTIC_TEST_CLOCK === "1" && context.budgetClock) return context.budgetClock();
2367
+ return new Date().toISOString();
2368
+ }
2369
+
2355
2370
  function runDurationOverrunPayload(
2356
2371
  context: KxmRuntimeContext,
2357
2372
  runId: string,
2358
- nowIso = new Date().toISOString(),
2373
+ nowIso = budgetNowIso(context),
2359
2374
  ): { budgetMs: number; elapsedMs: number; source: KxmRunDurationBudget["source"] } | undefined {
2360
2375
  const declared = declaredRunDurationBudget(context, runId);
2361
2376
  if (!declared) return undefined;
@@ -2432,7 +2447,7 @@ function armRunDurationBudgetTimer(context: KxmRuntimeContext, runId: string): (
2432
2447
  const fire = (): void => {
2433
2448
  timer = undefined;
2434
2449
  if (cleared || isKxmRuntimeContextClosed(context)) return;
2435
- const nowIso = new Date().toISOString();
2450
+ const nowIso = budgetNowIso(context);
2436
2451
  const elapsedMs = elapsedMsBetween(state.runningSince!, nowIso);
2437
2452
  if (elapsedMs < declared.budgetMs) {
2438
2453
  arm(declared.budgetMs - elapsedMs);
@@ -8,7 +8,7 @@ import { AGENT_COMMANDS_MAP, enforceToolPolicy, getMcpTools, reconcileInbox } fr
8
8
  import { deliverInboxNotification } from "./inbox.ts";
9
9
  import type { HubEvent, MessageRecord } from "./protocol.ts";
10
10
 
11
- const VERSION = "0.7.44";
11
+ const VERSION = "0.7.45";
12
12
  const inbox = new Map<string, MessageRecord>();
13
13
  const notifiedInbox = new Set<string>();
14
14
  let meshClient: HubClient | undefined;
@@ -234,6 +234,18 @@ export interface KxmRuntimeContext {
234
234
  projectId: string;
235
235
  homeRuntimeId: string;
236
236
  eventStore: KxmRunEventStore;
237
+ /**
238
+ * Optional clock for **run-duration budget accounting only**. Budgets are
239
+ * measured from the log's `runningSince`; this answers "what time is it now"
240
+ * at the far end of that subtraction so engine tests can exercise a budget
241
+ * boundary without racing a loaded event loop.
242
+ *
243
+ * It is honoured only when `KXM_DETERMINISTIC_TEST_CLOCK=1` is set (see
244
+ * `budgetNowIso` in `engine.ts`), it is never consulted for event timestamps,
245
+ * and the Runtime supervisor does not pass one — so no production path can
246
+ * freeze a budget by supplying a stale clock.
247
+ */
248
+ readonly budgetClock?: () => string;
237
249
  }
238
250
 
239
251
  const closedRuntimeContexts = new WeakSet<KxmRuntimeContext>();
@@ -267,7 +279,12 @@ export function registerKxmRuntimeCloseHook(context: KxmRuntimeContext, hook: ()
267
279
  */
268
280
  export function openKxmRuntimeContext(
269
281
  projectRoot: string,
270
- options: KxmConfigOptions & { stateRoot?: string; now?: string; homeRuntimeId: string },
282
+ options: KxmConfigOptions & {
283
+ stateRoot?: string;
284
+ now?: string;
285
+ homeRuntimeId: string;
286
+ budgetClock?: () => string;
287
+ },
271
288
  ): KxmRuntimeContext {
272
289
  const paths = kxmRuntimePaths(options.stateRoot !== undefined ? { stateRoot: options.stateRoot } : {});
273
290
  const registry = new KxmRuntimeRegistry(paths.registryDb);
@@ -289,6 +306,7 @@ export function openKxmRuntimeContext(
289
306
  projectId: registration.projectId,
290
307
  homeRuntimeId: registration.homeRuntimeId,
291
308
  eventStore,
309
+ ...(options.budgetClock ? { budgetClock: options.budgetClock } : {}),
292
310
  };
293
311
  } catch (error) {
294
312
  registry.close();