nextclaw 0.17.2 → 0.17.3

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.
Files changed (69) hide show
  1. package/dist/cli/index.js +375 -168
  2. package/package.json +11 -11
  3. package/resources/USAGE.md +32 -14
  4. package/ui-dist/assets/ChannelsList-uKmkpD25.js +8 -0
  5. package/ui-dist/assets/ChatPage-CslhBPfT.js +43 -0
  6. package/ui-dist/assets/{DocBrowser-DjcghYGO.js → DocBrowser-C7-1sXqo.js} +1 -1
  7. package/ui-dist/assets/DocBrowser-DQjtSsY3.js +1 -0
  8. package/ui-dist/assets/{DocBrowserContext-CLlq7rZQ.js → DocBrowserContext-DN5tjUoS.js} +1 -1
  9. package/ui-dist/assets/{LogoBadge-D_dOy5U3.js → LogoBadge-DDS1sU_U.js} +1 -1
  10. package/ui-dist/assets/MarketplacePage-BZQW70ti.js +1 -0
  11. package/ui-dist/assets/{MarketplacePage-BlIeNn3x.js → MarketplacePage-DE0QjYVv.js} +1 -1
  12. package/ui-dist/assets/{McpMarketplacePage-mz2_IX1O.js → McpMarketplacePage-CeLvv1xy.js} +1 -1
  13. package/ui-dist/assets/ModelConfig-D1JtGtQv.js +1 -0
  14. package/ui-dist/assets/ProviderScopedModelInput-SAJH6nkC.js +1 -0
  15. package/ui-dist/assets/{ProvidersList-B0RCb_Vg.js → ProvidersList-1rKi3aQT.js} +1 -1
  16. package/ui-dist/assets/{RemoteAccessPage-CcfQjLtx.js → RemoteAccessPage-bIAKxDky.js} +1 -1
  17. package/ui-dist/assets/RuntimeConfig-BTk9319O.js +1 -0
  18. package/ui-dist/assets/SearchConfig-EjeszXbv.js +1 -0
  19. package/ui-dist/assets/{SecretsConfig-DbiS3txa.js → SecretsConfig-cnAXvREZ.js} +1 -1
  20. package/ui-dist/assets/{SessionsConfig-CbIOcAp8.js → SessionsConfig-BIXiDaK2.js} +1 -1
  21. package/ui-dist/assets/{book-open-BLxSL7Dk.js → book-open-DvWqOode.js} +1 -1
  22. package/ui-dist/assets/chat-session-display-D4bYa0b8.js +1 -0
  23. package/ui-dist/assets/{chunk-JZWAC4HX-Bp0t5xoO.js → chunk-JZWAC4HX-CxfKRD7X.js} +1 -1
  24. package/ui-dist/assets/{config-C96FWufn.js → config-BeGwf2Ao.js} +1 -1
  25. package/ui-dist/assets/{createLucideIcon-B_U7Nq4F.js → createLucideIcon-C7MmdIX3.js} +1 -1
  26. package/ui-dist/assets/{dist-D9pHzW9z.js → dist-B6VMuIQN.js} +1 -1
  27. package/ui-dist/assets/{dist-BFY-GyT4.js → dist-RWNFhxvR.js} +1 -1
  28. package/ui-dist/assets/{external-link-BydIQTIH.js → external-link-U86Acd1t.js} +1 -1
  29. package/ui-dist/assets/{hash-Djdf0x1C.js → hash-D-OVfV3Z.js} +1 -1
  30. package/ui-dist/assets/i18n-hM3v-3YG.js +1 -0
  31. package/ui-dist/assets/{index-DqSv8Azv.js → index-8XNPYwJu.js} +3 -3
  32. package/ui-dist/assets/index-CpxuJa9o.css +1 -0
  33. package/ui-dist/assets/{label-Bvv4Mrea.js → label-CHJ1ATds.js} +1 -1
  34. package/ui-dist/assets/loader-circle-C8cpaL0w.js +1 -0
  35. package/ui-dist/assets/{logos-CGJJRI5_.js → logos-U1_qDA3U.js} +1 -1
  36. package/ui-dist/assets/{page-layout-6Nm4Cnvr.js → page-layout-Z1klaUFW.js} +1 -1
  37. package/ui-dist/assets/plus-CrkO1kob.js +1 -0
  38. package/ui-dist/assets/{popover-b9rSYI6X.js → popover-xWbqMnIN.js} +1 -1
  39. package/ui-dist/assets/{react-CDZz_StC.js → react-3YE87-lE.js} +1 -1
  40. package/ui-dist/assets/{refresh-ccw-BvSSnnCw.js → refresh-ccw-JQh1lwq-.js} +1 -1
  41. package/ui-dist/assets/{save-CAf0_-b9.js → save-4VRlzkii.js} +1 -1
  42. package/ui-dist/assets/search-EX-Papzl.js +1 -0
  43. package/ui-dist/assets/{security-config-DF66-l25.js → security-config-CGazBahs.js} +1 -1
  44. package/ui-dist/assets/{select-CEIMqc0H.js → select-DF-AUoie.js} +1 -1
  45. package/ui-dist/assets/skeleton-B0mmt1vo.js +1 -0
  46. package/ui-dist/assets/{status-dot-CmQI5Qq2.js → status-dot-Bq_8Ojvv.js} +1 -1
  47. package/ui-dist/assets/{switch-B7SxDXyR.js → switch-D7JF_RZ-.js} +1 -1
  48. package/ui-dist/assets/{tabs-custom-Dxt6EJJW.js → tabs-custom-CLksZ2bO.js} +1 -1
  49. package/ui-dist/assets/{trash-2-BnQ1PDTw.js → trash-2-VV8jvziy.js} +1 -1
  50. package/ui-dist/assets/{useConfirmDialog-B-vMOmhG.js → useConfirmDialog-D6HxybcM.js} +1 -1
  51. package/ui-dist/assets/{useMutation-Bi39Z9_J.js → useMutation-DBTWPbTg.js} +1 -1
  52. package/ui-dist/assets/x-B4sxJkGY.js +1 -0
  53. package/ui-dist/index.html +18 -18
  54. package/ui-dist/assets/ChannelsList-DekMP4a3.js +0 -8
  55. package/ui-dist/assets/ChatPage-Dgw4vlDt.js +0 -43
  56. package/ui-dist/assets/DocBrowser-CExjX5is.js +0 -1
  57. package/ui-dist/assets/MarketplacePage-DGfzg1LG.js +0 -1
  58. package/ui-dist/assets/ModelConfig-C_49_a9v.js +0 -1
  59. package/ui-dist/assets/RuntimeConfig-DBWzwoY-.js +0 -1
  60. package/ui-dist/assets/SearchConfig-jSdwlH4b.js +0 -1
  61. package/ui-dist/assets/chat-session-display-8yW6-mtm.js +0 -1
  62. package/ui-dist/assets/i18n-DAekxt_G.js +0 -1
  63. package/ui-dist/assets/index-CHEgQIiO.css +0 -1
  64. package/ui-dist/assets/loader-circle-CGXXikVG.js +0 -1
  65. package/ui-dist/assets/plus-CrW9BJDy.js +0 -1
  66. package/ui-dist/assets/provider-models-IJDA940D.js +0 -1
  67. package/ui-dist/assets/search-DgoXxocn.js +0 -1
  68. package/ui-dist/assets/skeleton-BiPUQkOD.js +0 -1
  69. package/ui-dist/assets/x-PBSiWt3l.js +0 -1
package/dist/cli/index.js CHANGED
@@ -2486,41 +2486,79 @@ var ChannelCommands = class {
2486
2486
  function createCronService() {
2487
2487
  return new CronService(join(getDataDir(), "cron", "jobs.json"));
2488
2488
  }
2489
+ function readTrimmed(value) {
2490
+ if (typeof value !== "string") return;
2491
+ return value.trim() || void 0;
2492
+ }
2489
2493
  function toSchedule(opts) {
2490
- if (opts.every) return {
2491
- kind: "every",
2492
- everyMs: Number(opts.every) * 1e3
2493
- };
2494
- if (opts.cron) return {
2494
+ const every = readTrimmed(opts.every);
2495
+ const cron = readTrimmed(opts.cron);
2496
+ const at = readTrimmed(opts.at);
2497
+ if ([
2498
+ every,
2499
+ cron,
2500
+ at
2501
+ ].filter((value) => value !== void 0).length !== 1) return { error: "Error: Must specify exactly one of --every, --cron, or --at" };
2502
+ if (every) {
2503
+ const everySeconds = Number(every);
2504
+ if (!Number.isFinite(everySeconds) || everySeconds <= 0) return { error: "Error: --every must be a positive number of seconds" };
2505
+ return { schedule: {
2506
+ kind: "every",
2507
+ everyMs: everySeconds * 1e3
2508
+ } };
2509
+ }
2510
+ if (cron) return { schedule: {
2495
2511
  kind: "cron",
2496
- expr: String(opts.cron)
2497
- };
2498
- if (opts.at) return {
2512
+ expr: cron
2513
+ } };
2514
+ const atMs = Date.parse(String(at));
2515
+ if (!Number.isFinite(atMs)) return { error: "Error: --at must be a valid ISO datetime" };
2516
+ return { schedule: {
2499
2517
  kind: "at",
2500
- atMs: Date.parse(String(opts.at))
2501
- };
2502
- return null;
2518
+ atMs
2519
+ } };
2520
+ }
2521
+ function createCronCreateRequest(opts) {
2522
+ const name = readTrimmed(opts.name);
2523
+ const message = readTrimmed(opts.message);
2524
+ if (!name || !message) return { error: "Error: name and message are required" };
2525
+ const schedule = toSchedule(opts);
2526
+ if (!schedule.schedule) return { error: schedule.error ?? "Error: Must specify --every, --cron, or --at" };
2527
+ return { request: {
2528
+ name,
2529
+ message,
2530
+ schedule: schedule.schedule,
2531
+ agentId: readTrimmed(opts.agent),
2532
+ deliver: Boolean(opts.deliver),
2533
+ channel: readTrimmed(opts.channel),
2534
+ to: readTrimmed(opts.to),
2535
+ accountId: readTrimmed(opts.account)
2536
+ } };
2503
2537
  }
2504
2538
  var CronLocalService = class {
2505
2539
  list = (all) => {
2506
2540
  return createCronService().listJobs(all);
2507
2541
  };
2542
+ addRequest = (request) => {
2543
+ return createCronService().addJob({
2544
+ name: request.name,
2545
+ schedule: request.schedule,
2546
+ message: request.message,
2547
+ agentId: request.agentId ?? void 0,
2548
+ deliver: request.deliver === true,
2549
+ channel: request.channel ?? void 0,
2550
+ to: request.to ?? void 0,
2551
+ accountId: request.accountId ?? void 0,
2552
+ deleteAfterRun: request.deleteAfterRun === true
2553
+ });
2554
+ };
2508
2555
  add = (opts) => {
2509
- const schedule = toSchedule(opts);
2510
- if (!schedule) return {
2556
+ const request = createCronCreateRequest(opts);
2557
+ if (!request.request) return {
2511
2558
  job: null,
2512
- error: "Error: Must specify --every, --cron, or --at"
2559
+ error: request.error
2513
2560
  };
2514
- return { job: createCronService().addJob({
2515
- name: opts.name,
2516
- schedule,
2517
- message: opts.message,
2518
- agentId: opts.agent,
2519
- deliver: Boolean(opts.deliver),
2520
- channel: opts.channel,
2521
- to: opts.to,
2522
- accountId: opts.account
2523
- }) };
2561
+ return { job: this.addRequest(request.request) };
2524
2562
  };
2525
2563
  remove = (jobId) => {
2526
2564
  return createCronService().removeJob(jobId);
@@ -2550,9 +2588,11 @@ function printCronJobs(jobs) {
2550
2588
  //#region src/cli/commands/shared/ui-bridge-api.service.ts
2551
2589
  function resolveManagedApiBase() {
2552
2590
  const state = readServiceState();
2553
- if (!state?.apiUrl || !state.pid) return null;
2591
+ if (!state?.pid) return null;
2554
2592
  if (!isProcessRunning(state.pid)) return null;
2555
- return state.apiUrl.replace(/\/+$/, "");
2593
+ if (typeof state.uiUrl === "string" && state.uiUrl.trim().length > 0) return state.uiUrl.replace(/\/+$/, "");
2594
+ if (typeof state.apiUrl === "string" && state.apiUrl.trim().length > 0) return state.apiUrl.replace(/\/api\/?$/, "").replace(/\/+$/, "");
2595
+ return null;
2556
2596
  }
2557
2597
  var UiBridgeApiClient = class {
2558
2598
  cookie;
@@ -2590,6 +2630,10 @@ var UiBridgeApiClient = class {
2590
2630
  };
2591
2631
  //#endregion
2592
2632
  //#region src/cli/commands/cron.ts
2633
+ function readErrorMessage(error) {
2634
+ if (error instanceof Error && error.message.trim().length > 0) return error.message.trim();
2635
+ return String(error ?? "unknown error");
2636
+ }
2593
2637
  var CronCommands = class {
2594
2638
  constructor(local = new CronLocalService()) {
2595
2639
  this.local = local;
@@ -2610,12 +2654,26 @@ var CronCommands = class {
2610
2654
  printCronJobs(this.local.list(includeDisabled));
2611
2655
  };
2612
2656
  cronAdd = async (opts) => {
2613
- const result = this.local.add(opts);
2614
- if (!result.job) {
2615
- console.error(result.error ?? "Error: Failed to add job");
2657
+ const request = createCronCreateRequest(opts);
2658
+ if (!request.request) {
2659
+ console.error(request.error ?? "Error: Failed to add job");
2660
+ return;
2661
+ }
2662
+ const apiClient = this.createApiClient();
2663
+ if (apiClient) try {
2664
+ const data = await apiClient.request({
2665
+ path: "/api/cron",
2666
+ method: "POST",
2667
+ body: request.request
2668
+ });
2669
+ console.log(`✓ Added job '${data.job.name}' (${data.job.id})`);
2670
+ return;
2671
+ } catch (error) {
2672
+ console.error(`Error: ${readErrorMessage(error)}`);
2616
2673
  return;
2617
2674
  }
2618
- console.log(`✓ Added job '${result.job.name}' (${result.job.id})`);
2675
+ const job = this.local.addRequest(request.request);
2676
+ console.log(`✓ Added job '${job.name}' (${job.id})`);
2619
2677
  };
2620
2678
  cronRemove = async (jobId) => {
2621
2679
  const apiClient = this.createApiClient();
@@ -2627,7 +2685,10 @@ var CronCommands = class {
2627
2685
  console.log(`✓ Removed job ${jobId}`);
2628
2686
  return;
2629
2687
  }
2630
- } catch {}
2688
+ } catch (error) {
2689
+ console.error(`Error: ${readErrorMessage(error)}`);
2690
+ return;
2691
+ }
2631
2692
  if (this.local.remove(jobId)) console.log(`✓ Removed job ${jobId}`);
2632
2693
  else console.log(`Job ${jobId} not found`);
2633
2694
  };
@@ -2644,7 +2705,10 @@ var CronCommands = class {
2644
2705
  console.log(`✓ Job '${data.job.name}' ${opts.disable ? "disabled" : "enabled"}`);
2645
2706
  return;
2646
2707
  }
2647
- } catch {}
2708
+ } catch (error) {
2709
+ console.error(`Error: ${readErrorMessage(error)}`);
2710
+ return;
2711
+ }
2648
2712
  const job = this.local.enable(jobId, enabled);
2649
2713
  if (job) console.log(`✓ Job '${job.name}' ${opts.disable ? "disabled" : "enabled"}`);
2650
2714
  else console.log(`Job ${jobId} not found`);
@@ -2659,12 +2723,128 @@ var CronCommands = class {
2659
2723
  });
2660
2724
  console.log(data.executed ? "✓ Job executed" : `Failed to run job ${jobId}`);
2661
2725
  return;
2662
- } catch {}
2726
+ } catch (error) {
2727
+ console.error(`Error: ${readErrorMessage(error)}`);
2728
+ return;
2729
+ }
2663
2730
  const ok = await this.local.run(jobId, Boolean(opts.force));
2664
2731
  console.log(ok ? "✓ Job executed" : `Failed to run job ${jobId}`);
2665
2732
  };
2666
2733
  };
2667
2734
  //#endregion
2735
+ //#region src/cli/commands/ncp/ui-ncp-runtime-registry.ts
2736
+ const DEFAULT_UI_NCP_RUNTIME_KIND = "native";
2737
+ function normalizeRuntimeKind(value) {
2738
+ if (typeof value !== "string") return null;
2739
+ const normalized = value.trim().toLowerCase();
2740
+ return normalized.length > 0 ? normalized : null;
2741
+ }
2742
+ function readRequestedRuntimeKind(sessionMetadata) {
2743
+ return normalizeRuntimeKind(sessionMetadata.runtime) ?? normalizeRuntimeKind(sessionMetadata.session_type) ?? normalizeRuntimeKind(sessionMetadata.sessionType) ?? null;
2744
+ }
2745
+ var UiNcpRuntimeRegistry = class {
2746
+ registrations = /* @__PURE__ */ new Map();
2747
+ constructor(defaultKind = DEFAULT_UI_NCP_RUNTIME_KIND) {
2748
+ this.defaultKind = defaultKind;
2749
+ }
2750
+ register(registration) {
2751
+ const normalizedKind = normalizeRuntimeKind(registration.kind);
2752
+ if (!normalizedKind) throw new Error("ui ncp runtime kind must be a non-empty string");
2753
+ const token = Symbol(normalizedKind);
2754
+ this.registrations.set(normalizedKind, {
2755
+ ...registration,
2756
+ kind: normalizedKind,
2757
+ token
2758
+ });
2759
+ return toDisposable(() => {
2760
+ const current = this.registrations.get(normalizedKind);
2761
+ if (!current || current.token !== token) return;
2762
+ this.registrations.delete(normalizedKind);
2763
+ });
2764
+ }
2765
+ createRuntime(params) {
2766
+ const requestedKind = readRequestedRuntimeKind(params.sessionMetadata) ?? this.defaultKind;
2767
+ const registration = this.registrations.get(requestedKind);
2768
+ if (!registration) throw new Error(`ncp runtime unavailable: ${requestedKind}`);
2769
+ const nextSessionMetadata = {
2770
+ ...params.sessionMetadata,
2771
+ session_type: registration.kind
2772
+ };
2773
+ params.setSessionMetadata(nextSessionMetadata);
2774
+ return registration.createRuntime({
2775
+ ...params,
2776
+ sessionMetadata: nextSessionMetadata
2777
+ });
2778
+ }
2779
+ async listSessionTypes(params) {
2780
+ const options = await Promise.all([...this.registrations.values()].map(async (registration) => {
2781
+ const descriptor = await registration.describeSessionType?.(params);
2782
+ return {
2783
+ value: registration.kind,
2784
+ label: registration.label,
2785
+ ready: descriptor?.ready ?? true,
2786
+ reason: descriptor?.reason ?? null,
2787
+ reasonMessage: descriptor?.reasonMessage ?? null,
2788
+ recommendedModel: descriptor?.recommendedModel ?? null,
2789
+ cta: descriptor?.cta ?? null,
2790
+ ...descriptor?.supportedModels ? { supportedModels: descriptor.supportedModels } : {}
2791
+ };
2792
+ }));
2793
+ return {
2794
+ defaultType: this.defaultKind,
2795
+ options: options.sort((left, right) => {
2796
+ if (left.value === this.defaultKind) return -1;
2797
+ if (right.value === this.defaultKind) return 1;
2798
+ return left.value.localeCompare(right.value);
2799
+ })
2800
+ };
2801
+ }
2802
+ };
2803
+ //#endregion
2804
+ //#region src/cli/commands/agent/agent-runtime.ts
2805
+ function createUnusedRuntime(_params) {
2806
+ throw new Error("runtime creation is not available during runtime listing");
2807
+ }
2808
+ async function listAvailableAgentRuntimes(params) {
2809
+ const config = loadConfig();
2810
+ const pluginRegistry = loadPluginRegistry(config, getWorkspacePath(config.agents.defaults.workspace));
2811
+ logPluginDiagnostics(pluginRegistry);
2812
+ const extensionRegistry = toExtensionRegistry(pluginRegistry);
2813
+ const runtimeRegistry = new UiNcpRuntimeRegistry(DEFAULT_UI_NCP_RUNTIME_KIND);
2814
+ const runtimeSourceByKind = /* @__PURE__ */ new Map();
2815
+ runtimeRegistry.register({
2816
+ kind: DEFAULT_UI_NCP_RUNTIME_KIND,
2817
+ label: "Native",
2818
+ createRuntime: createUnusedRuntime
2819
+ });
2820
+ runtimeSourceByKind.set(DEFAULT_UI_NCP_RUNTIME_KIND, { source: "builtin" });
2821
+ for (const registration of extensionRegistry.ncpAgentRuntimes) {
2822
+ runtimeRegistry.register({
2823
+ kind: registration.kind,
2824
+ label: registration.label,
2825
+ createRuntime: registration.createRuntime,
2826
+ describeSessionType: registration.describeSessionType
2827
+ });
2828
+ runtimeSourceByKind.set(registration.kind, {
2829
+ source: "plugin",
2830
+ pluginId: registration.pluginId
2831
+ });
2832
+ }
2833
+ const listed = await runtimeRegistry.listSessionTypes(params);
2834
+ return {
2835
+ defaultRuntime: listed.defaultType,
2836
+ runtimes: listed.options.map((runtime) => {
2837
+ const source = runtimeSourceByKind.get(runtime.value);
2838
+ return {
2839
+ ...runtime,
2840
+ default: runtime.value === listed.defaultType,
2841
+ source: source?.source ?? "plugin",
2842
+ ...source?.pluginId ? { pluginId: source.pluginId } : {}
2843
+ };
2844
+ })
2845
+ };
2846
+ }
2847
+ //#endregion
2668
2848
  //#region src/cli/commands/agents.ts
2669
2849
  var AgentCommands = class {
2670
2850
  constructor(deps) {
@@ -2683,6 +2863,31 @@ var AgentCommands = class {
2683
2863
  console.log(` description: ${agent.description ?? "-"}`);
2684
2864
  console.log(` home: ${agent.workspace}`);
2685
2865
  console.log(` avatar: ${agent.avatar ?? "-"}`);
2866
+ console.log(` runtime: ${agent.runtime ?? "-"}`);
2867
+ }
2868
+ };
2869
+ agentsRuntimes = async (opts = {}) => {
2870
+ const describeMode = opts.probe ? "probe" : "observation";
2871
+ const listed = await listAvailableAgentRuntimes({ describeMode });
2872
+ if (opts.json) {
2873
+ console.log(JSON.stringify({
2874
+ defaultRuntime: listed.defaultRuntime,
2875
+ describeMode,
2876
+ runtimes: listed.runtimes
2877
+ }, null, 2));
2878
+ return;
2879
+ }
2880
+ for (const runtime of listed.runtimes) {
2881
+ const head = runtime.default ? `${runtime.value} (default)` : runtime.value;
2882
+ console.log(head);
2883
+ console.log(` label: ${runtime.label}`);
2884
+ console.log(` source: ${runtime.source}`);
2885
+ if (runtime.pluginId) console.log(` pluginId: ${runtime.pluginId}`);
2886
+ console.log(` ready: ${runtime.ready === false ? "no" : "yes"}`);
2887
+ console.log(` reason: ${runtime.reason ?? "-"}`);
2888
+ console.log(` reasonMessage: ${runtime.reasonMessage ?? "-"}`);
2889
+ console.log(` recommendedModel: ${runtime.recommendedModel ?? "-"}`);
2890
+ console.log(` supportedModels: ${runtime.supportedModels?.join(", ") ?? "-"}`);
2686
2891
  }
2687
2892
  };
2688
2893
  agentsNew = async (agentId, opts = {}) => {
@@ -2691,48 +2896,38 @@ var AgentCommands = class {
2691
2896
  displayName: opts.name,
2692
2897
  description: opts.description,
2693
2898
  avatar: opts.avatar,
2694
- home: opts.home
2899
+ home: opts.home,
2900
+ runtime: opts.runtime
2695
2901
  }, { initializeHomeDirectory: this.deps.initializeAgentHomeDirectory });
2696
2902
  if (opts.json) {
2697
- console.log(JSON.stringify({
2698
- agent: created,
2699
- restartRequired: true
2700
- }, null, 2));
2903
+ console.log(JSON.stringify({ agent: created }, null, 2));
2701
2904
  return;
2702
2905
  }
2703
- await this.deps.requestRestart({
2704
- reason: "agents-updated",
2705
- manualMessage: `Created agent '${created.id}'. Restart ${this.deps.appName} to apply agent runtime changes.`
2706
- });
2707
2906
  console.log(`✓ Created agent ${created.id}`);
2708
2907
  console.log(` name: ${created.displayName ?? "-"}`);
2709
2908
  console.log(` description: ${created.description ?? "-"}`);
2710
2909
  console.log(` home: ${created.workspace}`);
2711
2910
  console.log(` avatar: ${created.avatar ?? "-"}`);
2911
+ console.log(` runtime: ${created.runtime ?? created.engine ?? "-"}`);
2712
2912
  };
2713
2913
  agentsUpdate = async (agentId, opts = {}) => {
2714
2914
  const updated = updateAgentProfile({
2715
2915
  id: agentId,
2716
2916
  displayName: opts.name,
2717
2917
  description: opts.description,
2718
- avatar: opts.avatar
2918
+ avatar: opts.avatar,
2919
+ runtime: opts.runtime
2719
2920
  });
2720
2921
  if (opts.json) {
2721
- console.log(JSON.stringify({
2722
- agent: updated,
2723
- restartRequired: true
2724
- }, null, 2));
2922
+ console.log(JSON.stringify({ agent: updated }, null, 2));
2725
2923
  return;
2726
2924
  }
2727
- await this.deps.requestRestart({
2728
- reason: "agents-updated",
2729
- manualMessage: `Updated agent '${updated.id}'. Restart ${this.deps.appName} to apply agent runtime changes.`
2730
- });
2731
2925
  console.log(`✓ Updated agent ${updated.id}`);
2732
2926
  console.log(` name: ${updated.displayName ?? "-"}`);
2733
2927
  console.log(` description: ${updated.description ?? "-"}`);
2734
2928
  console.log(` home: ${updated.workspace}`);
2735
2929
  console.log(` avatar: ${updated.avatar ?? "-"}`);
2930
+ console.log(` runtime: ${updated.runtime ?? updated.engine ?? "-"}`);
2736
2931
  };
2737
2932
  agentsRemove = async (agentId, opts = {}) => {
2738
2933
  if (agentId.trim().toLowerCase() === BUILTIN_MAIN_AGENT_ID) throw new Error(`agent id '${BUILTIN_MAIN_AGENT_ID}' is reserved`);
@@ -2740,15 +2935,10 @@ var AgentCommands = class {
2740
2935
  if (opts.json) {
2741
2936
  console.log(JSON.stringify({
2742
2937
  removed: true,
2743
- agentId,
2744
- restartRequired: true
2938
+ agentId
2745
2939
  }, null, 2));
2746
2940
  return;
2747
2941
  }
2748
- await this.deps.requestRestart({
2749
- reason: "agents-updated",
2750
- manualMessage: `Removed agent '${agentId}'. Restart ${this.deps.appName} to apply agent runtime changes.`
2751
- });
2752
2942
  console.log(`✓ Removed agent ${agentId}`);
2753
2943
  };
2754
2944
  toAgentListEntry = (agent) => {
@@ -2758,6 +2948,7 @@ var AgentCommands = class {
2758
2948
  description: agent.description ?? null,
2759
2949
  avatar: agent.avatar ?? null,
2760
2950
  workspace: agent.workspace,
2951
+ runtime: agent.runtime ?? agent.engine ?? null,
2761
2952
  builtIn: agent.builtIn === true
2762
2953
  };
2763
2954
  };
@@ -4184,13 +4375,14 @@ async function startGatewaySupportServices(params) {
4184
4375
  }
4185
4376
  function watchCronStoreFile(params) {
4186
4377
  const cronStorePath = resolve(params.cronStorePath);
4187
- chokidar.watch(cronStorePath, {
4378
+ const watcher = chokidar.watch(cronStorePath, {
4188
4379
  ignoreInitial: true,
4189
4380
  awaitWriteFinish: {
4190
4381
  stabilityThreshold: 200,
4191
4382
  pollInterval: 50
4192
4383
  }
4193
- }).on("all", (event, changedPath) => {
4384
+ });
4385
+ watcher.on("all", (event, changedPath) => {
4194
4386
  if (resolve(changedPath) !== cronStorePath) return;
4195
4387
  if (event === "add" || event === "change" || event === "unlink") try {
4196
4388
  params.reloadCronStore();
@@ -4198,6 +4390,85 @@ function watchCronStoreFile(params) {
4198
4390
  console.error(`Cron store reload failed (${event}): ${String(error)}`);
4199
4391
  }
4200
4392
  });
4393
+ return watcher;
4394
+ }
4395
+ function watchServiceConfigFile(params) {
4396
+ const configPath = resolve(params.configPath);
4397
+ const watcher = chokidar.watch(configPath, {
4398
+ ignoreInitial: true,
4399
+ awaitWriteFinish: {
4400
+ stabilityThreshold: 200,
4401
+ pollInterval: 50
4402
+ }
4403
+ });
4404
+ params.watcherRegistry.remember(watcher);
4405
+ watcher.on("all", (event, changedPath) => {
4406
+ if (resolve(changedPath) !== configPath) return;
4407
+ if (event === "add") {
4408
+ params.scheduleReload("config add");
4409
+ return;
4410
+ }
4411
+ if (event === "change") {
4412
+ params.scheduleReload("config change");
4413
+ return;
4414
+ }
4415
+ if (event === "unlink") params.scheduleReload("config unlink");
4416
+ });
4417
+ }
4418
+ var ServiceFileWatcherRegistry = class {
4419
+ watchers = [];
4420
+ remember = (watcher) => {
4421
+ this.watchers.push(watcher);
4422
+ };
4423
+ clear = async () => {
4424
+ const watchers = this.watchers.splice(0);
4425
+ await Promise.allSettled(watchers.map(async (watcher) => {
4426
+ try {
4427
+ await watcher.close();
4428
+ } catch {}
4429
+ }));
4430
+ };
4431
+ };
4432
+ function writeLocalServiceDiscoveryState(uiConfig, pid = process.pid) {
4433
+ const uiUrl = resolveUiApiBase(uiConfig.host, uiConfig.port);
4434
+ const apiUrl = `${uiUrl}/api`;
4435
+ const existing = readServiceState();
4436
+ writeServiceState({
4437
+ pid,
4438
+ startedAt: existing?.pid === pid && typeof existing.startedAt === "string" ? existing.startedAt : (/* @__PURE__ */ new Date()).toISOString(),
4439
+ uiUrl,
4440
+ apiUrl,
4441
+ uiHost: uiConfig.host,
4442
+ uiPort: uiConfig.port,
4443
+ logPath: existing?.logPath ?? resolveServiceLogPath(),
4444
+ startupState: existing?.startupState ?? "ready",
4445
+ startupLastProbeError: existing?.startupLastProbeError ?? null,
4446
+ startupTimeoutMs: existing?.startupTimeoutMs,
4447
+ startupCheckedAt: (/* @__PURE__ */ new Date()).toISOString(),
4448
+ ...existing?.remote ? { remote: existing.remote } : {}
4449
+ });
4450
+ }
4451
+ function clearOwnedServiceState(pid = process.pid) {
4452
+ if (readServiceState()?.pid === pid) clearServiceState();
4453
+ }
4454
+ function finalizeLocalUiStartup(params) {
4455
+ const { setUiEventPublisher, uiConfig, uiStartup } = params;
4456
+ setUiEventPublisher(uiStartup?.publish);
4457
+ if (uiStartup) writeLocalServiceDiscoveryState(uiConfig);
4458
+ }
4459
+ async function startGatewayRuntimeSupport(params) {
4460
+ const { cronJobs, cronStorePath, reloadCronStore, remoteModule, startCron, startHeartbeat, watchConfigFile, watcherRegistry } = params;
4461
+ await startGatewaySupportServices({
4462
+ cronJobs,
4463
+ remoteModule,
4464
+ watchConfigFile,
4465
+ startCron,
4466
+ startHeartbeat
4467
+ });
4468
+ watcherRegistry.remember(watchCronStoreFile({
4469
+ cronStorePath,
4470
+ reloadCronStore
4471
+ }));
4201
4472
  }
4202
4473
  //#endregion
4203
4474
  //#region src/cli/commands/remote-support/remote-access-service-control.ts
@@ -4998,6 +5269,10 @@ var SpawnChildSessionTool = class extends Tool {
4998
5269
  type: "string",
4999
5270
  description: "Optional model override used only for the child session."
5000
5271
  },
5272
+ runtime: {
5273
+ type: "string",
5274
+ description: "Optional runtime override used only for the child session, for example native or codex."
5275
+ },
5001
5276
  agentId: {
5002
5277
  type: "string",
5003
5278
  description: "Optional target agent id for the child session. Omit to use the default agent for that child."
@@ -5020,6 +5295,7 @@ var SpawnChildSessionTool = class extends Tool {
5020
5295
  task,
5021
5296
  title: readOptionalString$4(params, "label"),
5022
5297
  model: readOptionalString$4(params, "model"),
5298
+ runtime: readOptionalString$4(params, "runtime"),
5023
5299
  handoffDepth: this.handoffDepth,
5024
5300
  agentId: readOptionalString$4(params, "agentId")
5025
5301
  });
@@ -5141,6 +5417,10 @@ var SessionSpawnTool = class extends Tool {
5141
5417
  type: "string",
5142
5418
  description: "Optional model override for the new session."
5143
5419
  },
5420
+ runtime: {
5421
+ type: "string",
5422
+ description: "Optional runtime override for the new session, for example native or codex."
5423
+ },
5144
5424
  agentId: {
5145
5425
  type: "string",
5146
5426
  description: "Optional target agent id for the new session. Omit to use the default agent."
@@ -5160,7 +5440,8 @@ var SessionSpawnTool = class extends Tool {
5160
5440
  title: readOptionalString$3(params.title),
5161
5441
  sourceSessionMetadata: this.sourceSessionMetadata,
5162
5442
  agentId: readOptionalString$3(params.agentId),
5163
- model: readOptionalString$3(params.model)
5443
+ model: readOptionalString$3(params.model),
5444
+ runtime: readOptionalString$3(params.runtime)
5164
5445
  });
5165
5446
  return {
5166
5447
  kind: "nextclaw.session",
@@ -5429,6 +5710,7 @@ function prependRequestedSkills(content, requestedSkillNames) {
5429
5710
  function buildSessionOrchestrationSection() {
5430
5711
  return [
5431
5712
  "## Session Orchestration",
5713
+ "- Before passing a non-default `runtime` to `spawn`, `sessions_spawn`, or agent creation/update flows, inspect the installed runtime kinds with `nextclaw agents runtimes --json`.",
5432
5714
  "- `spawn` creates a child session, starts the delegated task there immediately, and returns a running child-session handle right away instead of waiting for the child to finish.",
5433
5715
  "- When that child reaches its final reply, `spawn` writes the completed result back into the original tool call and resumes the current session with the child's result.",
5434
5716
  "- Use `spawn` when the work is a subtask of the current flow and the user expects this session to pause now and then continue after that child finishes.",
@@ -5946,6 +6228,7 @@ function summarizeTask(task) {
5946
6228
  function cloneInheritedMetadata(sourceMetadata) {
5947
6229
  const nextMetadata = {};
5948
6230
  for (const key of [
6231
+ "runtime",
5949
6232
  "session_type",
5950
6233
  "preferred_model",
5951
6234
  "preferred_thinking",
@@ -5970,10 +6253,11 @@ function resolveSessionTitle(params) {
5970
6253
  return readOptionalString$2(params.title) ?? summarizeTask(params.task);
5971
6254
  }
5972
6255
  function resolveSessionType(params) {
5973
- return readOptionalString$2(params.sessionType) ?? readOptionalString$2(params.metadata.session_type) ?? DEFAULT_SESSION_TYPE;
6256
+ return readOptionalString$2(params.runtime) ?? readOptionalString$2(params.metadata.runtime) ?? readOptionalString$2(params.sessionType) ?? readOptionalString$2(params.metadata.session_type) ?? DEFAULT_SESSION_TYPE;
5974
6257
  }
5975
6258
  function applySessionOverrides(params) {
5976
6259
  params.metadata.session_type = params.sessionType;
6260
+ params.metadata.runtime = params.sessionType;
5977
6261
  params.metadata[SESSION_METADATA_LABEL_KEY] = params.title;
5978
6262
  params.metadata[CHILD_SESSION_LIFECYCLE_METADATA_KEY] = params.lifecycle;
5979
6263
  if (params.parentSessionId) {
@@ -6013,6 +6297,7 @@ var SessionCreationService = class {
6013
6297
  const parentSessionId = readOptionalString$2(params.parentSessionId);
6014
6298
  const requestId = readOptionalString$2(params.requestId);
6015
6299
  const sessionType = resolveSessionType({
6300
+ runtime: params.runtime,
6016
6301
  sessionType: params.sessionType,
6017
6302
  metadata
6018
6303
  });
@@ -6036,7 +6321,7 @@ var SessionCreationService = class {
6036
6321
  sessionId,
6037
6322
  agentId: resolvedAgentId,
6038
6323
  sessionType,
6039
- runtimeFamily: "native",
6324
+ runtimeFamily: sessionType === DEFAULT_SESSION_TYPE ? "native" : "external",
6040
6325
  ...parentSessionId ? { parentSessionId } : {},
6041
6326
  ...requestId ? { spawnedByRequestId: requestId } : {},
6042
6327
  lifecycle: DEFAULT_LIFECYCLE,
@@ -6189,7 +6474,7 @@ var SessionRequestBroker = class {
6189
6474
  this.onSessionUpdated = onSessionUpdated;
6190
6475
  }
6191
6476
  spawnChildSessionAndRequest = async (params) => {
6192
- const { sourceSessionId, sourceToolCallId, sourceSessionMetadata, task, title, model, handoffDepth, sessionType, thinkingLevel, projectRoot, agentId } = params;
6477
+ const { sourceSessionId, sourceToolCallId, sourceSessionMetadata, task, title, model, runtime, handoffDepth, sessionType, thinkingLevel, projectRoot, agentId } = params;
6193
6478
  const requestId = randomUUID();
6194
6479
  const childSession = this.sessionCreationService.createChildSession({
6195
6480
  parentSessionId: sourceSessionId,
@@ -6198,6 +6483,7 @@ var SessionRequestBroker = class {
6198
6483
  sourceSessionMetadata,
6199
6484
  agentId,
6200
6485
  model,
6486
+ runtime,
6201
6487
  thinkingLevel,
6202
6488
  sessionType,
6203
6489
  projectRoot,
@@ -6499,75 +6785,6 @@ var SessionRequestDeliveryService = class {
6499
6785
  };
6500
6786
  };
6501
6787
  //#endregion
6502
- //#region src/cli/commands/ncp/ui-ncp-runtime-registry.ts
6503
- const DEFAULT_UI_NCP_RUNTIME_KIND = "native";
6504
- function normalizeRuntimeKind(value) {
6505
- if (typeof value !== "string") return null;
6506
- const normalized = value.trim().toLowerCase();
6507
- return normalized.length > 0 ? normalized : null;
6508
- }
6509
- function readRequestedRuntimeKind(sessionMetadata) {
6510
- return normalizeRuntimeKind(sessionMetadata.session_type) ?? normalizeRuntimeKind(sessionMetadata.sessionType) ?? null;
6511
- }
6512
- var UiNcpRuntimeRegistry = class {
6513
- registrations = /* @__PURE__ */ new Map();
6514
- constructor(defaultKind = DEFAULT_UI_NCP_RUNTIME_KIND) {
6515
- this.defaultKind = defaultKind;
6516
- }
6517
- register(registration) {
6518
- const normalizedKind = normalizeRuntimeKind(registration.kind);
6519
- if (!normalizedKind) throw new Error("ui ncp runtime kind must be a non-empty string");
6520
- const token = Symbol(normalizedKind);
6521
- this.registrations.set(normalizedKind, {
6522
- ...registration,
6523
- kind: normalizedKind,
6524
- token
6525
- });
6526
- return toDisposable(() => {
6527
- const current = this.registrations.get(normalizedKind);
6528
- if (!current || current.token !== token) return;
6529
- this.registrations.delete(normalizedKind);
6530
- });
6531
- }
6532
- createRuntime(params) {
6533
- const requestedKind = readRequestedRuntimeKind(params.sessionMetadata) ?? this.defaultKind;
6534
- const registration = this.registrations.get(requestedKind);
6535
- if (!registration) throw new Error(`ncp runtime unavailable: ${requestedKind}`);
6536
- const nextSessionMetadata = {
6537
- ...params.sessionMetadata,
6538
- session_type: registration.kind
6539
- };
6540
- params.setSessionMetadata(nextSessionMetadata);
6541
- return registration.createRuntime({
6542
- ...params,
6543
- sessionMetadata: nextSessionMetadata
6544
- });
6545
- }
6546
- async listSessionTypes(params) {
6547
- const options = await Promise.all([...this.registrations.values()].map(async (registration) => {
6548
- const descriptor = await registration.describeSessionType?.(params);
6549
- return {
6550
- value: registration.kind,
6551
- label: registration.label,
6552
- ready: descriptor?.ready ?? true,
6553
- reason: descriptor?.reason ?? null,
6554
- reasonMessage: descriptor?.reasonMessage ?? null,
6555
- recommendedModel: descriptor?.recommendedModel ?? null,
6556
- cta: descriptor?.cta ?? null,
6557
- ...descriptor?.supportedModels ? { supportedModels: descriptor.supportedModels } : {}
6558
- };
6559
- }));
6560
- return {
6561
- defaultType: this.defaultKind,
6562
- options: options.sort((left, right) => {
6563
- if (left.value === this.defaultKind) return -1;
6564
- if (right.value === this.defaultKind) return 1;
6565
- return left.value.localeCompare(right.value);
6566
- })
6567
- };
6568
- }
6569
- };
6570
- //#endregion
6571
6788
  //#region src/cli/commands/ncp/runtime/ui-ncp-agent-handle.ts
6572
6789
  function createUiNcpAgentHandle(params) {
6573
6790
  return {
@@ -8664,11 +8881,13 @@ function createSkillsLoader(workspace) {
8664
8881
  var ServiceCommands = class {
8665
8882
  applyLiveConfigReload = null;
8666
8883
  liveUiNcpAgent = null;
8884
+ fileWatchers = new ServiceFileWatcherRegistry();
8667
8885
  constructor(deps) {
8668
8886
  this.deps = deps;
8669
8887
  }
8670
8888
  startGateway = async (options = {}) => {
8671
8889
  logStartupTrace("service.start_gateway.begin");
8890
+ await this.fileWatchers.clear();
8672
8891
  this.applyLiveConfigReload = null;
8673
8892
  this.liveUiNcpAgent = null;
8674
8893
  const shellContext = measureStartupSync("service.create_gateway_shell_context", () => createGatewayShellContext({
@@ -8712,7 +8931,11 @@ var ServiceCommands = class {
8712
8931
  ncpSessionService: ncpSessionRealtimeBridge.sessionService,
8713
8932
  initializeAgentHomeDirectory: this.deps.initializeAgentHomeDirectory
8714
8933
  }));
8715
- ncpSessionRealtimeBridge.setUiEventPublisher(uiStartup?.publish);
8934
+ finalizeLocalUiStartup({
8935
+ uiStartup,
8936
+ setUiEventPublisher: (publish) => ncpSessionRealtimeBridge.setUiEventPublisher(publish),
8937
+ uiConfig: shellContext.uiConfig
8938
+ });
8716
8939
  bootstrapStatus.markShellReady();
8717
8940
  await setImmediate();
8718
8941
  const gateway = measureStartupSync("service.create_gateway_startup_context", () => createGatewayStartupContext({
@@ -8746,17 +8969,20 @@ var ServiceCommands = class {
8746
8969
  publishUiEvent: uiStartup?.publish
8747
8970
  });
8748
8971
  console.log("✓ Capability hydration: scheduled in background");
8749
- await measureStartupAsync("service.start_gateway_support_services", async () => await startGatewaySupportServices({
8972
+ await measureStartupAsync("service.start_gateway_support_services", async () => await startGatewayRuntimeSupport({
8750
8973
  cronJobs: gateway.cron.status().jobs,
8751
8974
  remoteModule: gateway.remoteModule,
8752
- watchConfigFile: () => this.watchConfigFile(gateway.reloader),
8975
+ watchConfigFile: () => watchServiceConfigFile({
8976
+ configPath: resolve(getConfigPath$1()),
8977
+ watcherRegistry: this.fileWatchers,
8978
+ scheduleReload: (reason) => gateway.reloader.scheduleReload(reason)
8979
+ }),
8753
8980
  startCron: () => gateway.cron.start(),
8754
- startHeartbeat: () => gateway.heartbeat.start()
8755
- }));
8756
- watchCronStoreFile({
8981
+ startHeartbeat: () => gateway.heartbeat.start(),
8757
8982
  cronStorePath: resolve(join(NextclawCore.getDataDir(), "cron", "jobs.json")),
8758
- reloadCronStore: () => gateway.cron.reloadFromStore()
8759
- });
8983
+ reloadCronStore: () => gateway.cron.reloadFromStore(),
8984
+ watcherRegistry: this.fileWatchers
8985
+ }));
8760
8986
  const deferredGatewayStartupHooks = createDeferredGatewayStartupHooks({
8761
8987
  uiStartup,
8762
8988
  gateway,
@@ -8804,6 +9030,8 @@ var ServiceCommands = class {
8804
9030
  console.error(`Deferred startup failed: ${error instanceof Error ? error.message : String(error)}`);
8805
9031
  },
8806
9032
  cleanup: async () => {
9033
+ clearOwnedServiceState();
9034
+ await this.fileWatchers.clear();
8807
9035
  this.applyLiveConfigReload = null;
8808
9036
  this.liveUiNcpAgent = null;
8809
9037
  ncpSessionRealtimeBridge.clear();
@@ -8819,27 +9047,6 @@ var ServiceCommands = class {
8819
9047
  if (typeof value !== "string") return;
8820
9048
  return value.trim() || void 0;
8821
9049
  };
8822
- watchConfigFile = (reloader) => {
8823
- const configPath = resolve(getConfigPath$1());
8824
- chokidar.watch(configPath, {
8825
- ignoreInitial: true,
8826
- awaitWriteFinish: {
8827
- stabilityThreshold: 200,
8828
- pollInterval: 50
8829
- }
8830
- }).on("all", (event, changedPath) => {
8831
- if (resolve(changedPath) !== configPath) return;
8832
- if (event === "add") {
8833
- reloader.scheduleReload("config add");
8834
- return;
8835
- }
8836
- if (event === "change") {
8837
- reloader.scheduleReload("config change");
8838
- return;
8839
- }
8840
- if (event === "unlink") reloader.scheduleReload("config unlink");
8841
- });
8842
- };
8843
9050
  resolveMostRecentRoutableSessionKey = (sessionManager) => {
8844
9051
  let best = null;
8845
9052
  for (const session of sessionManager.listSessions()) {
@@ -9552,11 +9759,7 @@ var CliRuntime = class {
9552
9759
  this.mcpCommands = measureStartupSync("cli.runtime.mcp_commands", () => new McpCommands());
9553
9760
  this.secretsCommands = measureStartupSync("cli.runtime.secrets_commands", () => new SecretsCommands({ requestRestart: (params) => this.requestRestart(params) }));
9554
9761
  this.pluginCommands = measureStartupSync("cli.runtime.plugin_commands", () => new PluginCommands());
9555
- this.agentCommands = measureStartupSync("cli.runtime.agent_commands", () => new AgentCommands({
9556
- requestRestart: (params) => this.requestRestart(params),
9557
- initializeAgentHomeDirectory: (homeDirectory) => this.workspaceManager.createWorkspaceTemplates(homeDirectory),
9558
- appName: APP_NAME
9559
- }));
9762
+ this.agentCommands = measureStartupSync("cli.runtime.agent_commands", () => new AgentCommands({ initializeAgentHomeDirectory: (homeDirectory) => this.workspaceManager.createWorkspaceTemplates(homeDirectory) }));
9560
9763
  this.channelCommands = measureStartupSync("cli.runtime.channel_commands", () => new ChannelCommands({
9561
9764
  logo: this.logo,
9562
9765
  getBridgeDir: () => this.workspaceManager.getBridgeDir(),
@@ -9959,6 +10162,9 @@ var CliRuntime = class {
9959
10162
  agentsList = (opts = {}) => {
9960
10163
  this.agentCommands.agentsList(opts);
9961
10164
  };
10165
+ agentsRuntimes = async (opts = {}) => {
10166
+ await this.agentCommands.agentsRuntimes(opts);
10167
+ };
9962
10168
  agentsNew = async (agentId, opts = {}) => {
9963
10169
  await this.agentCommands.agentsNew(agentId, opts);
9964
10170
  };
@@ -10120,8 +10326,9 @@ var CliRuntime = class {
10120
10326
  function registerAgentsCommands(program, runtime) {
10121
10327
  const agents = program.command("agents").description("Manage agents");
10122
10328
  agents.command("list").description("List available agents").option("--json", "Output JSON", false).action((opts) => runtime.agentsList(opts));
10123
- agents.command("new <agentId>").description("Create a new agent").option("--name <name>", "Agent display name").option("--description <description>", "Agent description").option("--avatar <avatar>", "Remote avatar URL or local image path").option("--home <path>", "Agent home directory").option("--json", "Output JSON", false).action(async (agentId, opts) => runtime.agentsNew(agentId, opts));
10124
- agents.command("update <agentId>").description("Update an existing agent").option("--name <name>", "Agent display name").option("--description <description>", "Agent description").option("--avatar <avatar>", "Remote avatar URL or local image path").option("--json", "Output JSON", false).action(async (agentId, opts) => runtime.agentsUpdate(agentId, opts));
10329
+ agents.command("runtimes").description("List available agent runtimes").option("--probe", "Actively probe runtime readiness", false).option("--json", "Output JSON", false).action(async (opts) => runtime.agentsRuntimes(opts));
10330
+ agents.command("new <agentId>").description("Create a new agent").option("--name <name>", "Agent display name").option("--description <description>", "Agent description").option("--avatar <avatar>", "Remote avatar URL or local image path").option("--home <path>", "Agent home directory").option("--runtime <runtime>", "Agent runtime kind, for example native or codex").option("--json", "Output JSON", false).action(async (agentId, opts) => runtime.agentsNew(agentId, opts));
10331
+ agents.command("update <agentId>").description("Update an existing agent").option("--name <name>", "Agent display name").option("--description <description>", "Agent description").option("--avatar <avatar>", "Remote avatar URL or local image path").option("--runtime <runtime>", "Agent runtime kind, for example native or codex").option("--json", "Output JSON", false).action(async (agentId, opts) => runtime.agentsUpdate(agentId, opts));
10125
10332
  agents.command("remove <agentId>").description("Remove an agent").option("--json", "Output JSON", false).action(async (agentId, opts) => runtime.agentsRemove(agentId, opts));
10126
10333
  }
10127
10334
  //#endregion