nextclaw 0.16.2 → 0.16.4

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 (29) hide show
  1. package/dist/cli/index.js +772 -359
  2. package/package.json +10 -10
  3. package/ui-dist/assets/{ChannelsList-CKl1Zg8f.js → ChannelsList-Bbsn_9Sl.js} +1 -1
  4. package/ui-dist/assets/{ChatPage-BJgO27mk.js → ChatPage-B_RzI2dv.js} +24 -24
  5. package/ui-dist/assets/{DocBrowser-DYRBs4-z.js → DocBrowser-BqZmEG--.js} +1 -1
  6. package/ui-dist/assets/{LogoBadge-33Qlv3Hg.js → LogoBadge-CrFjMchW.js} +1 -1
  7. package/ui-dist/assets/{MarketplacePage-B8BZVtjV.js → MarketplacePage-LbazM1dh.js} +1 -1
  8. package/ui-dist/assets/{McpMarketplacePage-BRuE5fJJ.js → McpMarketplacePage-p3IcOlaC.js} +1 -1
  9. package/ui-dist/assets/{ModelConfig-BiFblwO-.js → ModelConfig-D_IusAAJ.js} +1 -1
  10. package/ui-dist/assets/{ProvidersList-9goRgHE4.js → ProvidersList-FcvdCX9J.js} +1 -1
  11. package/ui-dist/assets/{RemoteAccessPage-5vCxZPS6.js → RemoteAccessPage-BQaKeMrJ.js} +1 -1
  12. package/ui-dist/assets/{RuntimeConfig-BmDFHBdW.js → RuntimeConfig-DrzLjjo6.js} +1 -1
  13. package/ui-dist/assets/{SearchConfig-CJx5CKwG.js → SearchConfig-CihtJFdH.js} +1 -1
  14. package/ui-dist/assets/{SecretsConfig-B91efXoK.js → SecretsConfig-CY5A5VZ2.js} +1 -1
  15. package/ui-dist/assets/{SessionsConfig-CbFPVmx3.js → SessionsConfig-oVRDHPEI.js} +1 -1
  16. package/ui-dist/assets/{index-COJomMe9.js → index-BpRgylED.js} +2 -2
  17. package/ui-dist/assets/index-D51iFwm3.css +1 -0
  18. package/ui-dist/assets/{label-BnSDpjhL.js → label-DApqi6nE.js} +1 -1
  19. package/ui-dist/assets/{ncp-session-adapter-w8ZHprab.js → ncp-session-adapter-_ovs6_oM.js} +1 -1
  20. package/ui-dist/assets/{page-layout-B1RIu5-r.js → page-layout-D1eR51WI.js} +1 -1
  21. package/ui-dist/assets/{popover-ChzbCIfO.js → popover-DLEULa2y.js} +1 -1
  22. package/ui-dist/assets/{security-config-eYa6Ovfa.js → security-config-jZw4E7Zh.js} +1 -1
  23. package/ui-dist/assets/{skeleton-D4Eyop0R.js → skeleton-nPdy6tmR.js} +1 -1
  24. package/ui-dist/assets/{status-dot-CrCw5tkJ.js → status-dot-mdxhu605.js} +1 -1
  25. package/ui-dist/assets/{switch-C3vVTpfU.js → switch-D90AL-4F.js} +1 -1
  26. package/ui-dist/assets/{tabs-custom-Ilrgt6n1.js → tabs-custom-WdbYS5UC.js} +1 -1
  27. package/ui-dist/assets/{useConfirmDialog-BeaFLDO8.js → useConfirmDialog-Dy1a-YFD.js} +1 -1
  28. package/ui-dist/index.html +2 -2
  29. package/ui-dist/assets/index-BtAuUyww.css +0 -1
package/dist/cli/index.js CHANGED
@@ -7,23 +7,23 @@ import { registerRemoteCommands } from "@nextclaw/remote";
7
7
 
8
8
  // src/cli/runtime.ts
9
9
  import {
10
- loadConfig as loadConfig19,
10
+ loadConfig as loadConfig21,
11
11
  saveConfig as saveConfig11,
12
12
  getConfigPath as getConfigPath11,
13
13
  getDataDir as getDataDir10,
14
- getWorkspacePath as getWorkspacePath12,
14
+ getWorkspacePath as getWorkspacePath13,
15
15
  expandHome as expandHome2,
16
16
  MessageBus as MessageBus3,
17
17
  AgentLoop,
18
18
  ProviderManager as ProviderManager2,
19
- resolveConfigSecrets as resolveConfigSecrets5,
19
+ resolveConfigSecrets as resolveConfigSecrets7,
20
20
  APP_NAME as APP_NAME5,
21
21
  DEFAULT_WORKSPACE_DIR,
22
22
  DEFAULT_WORKSPACE_PATH
23
23
  } from "@nextclaw/core";
24
24
  import { RemoteRuntimeActions } from "@nextclaw/remote";
25
25
  import {
26
- getPluginChannelBindings as getPluginChannelBindings5,
26
+ getPluginChannelBindings as getPluginChannelBindings6,
27
27
  resolvePluginChannelMessageToolHints as resolvePluginChannelMessageToolHints3,
28
28
  setPluginRuntimeBridge as setPluginRuntimeBridge3
29
29
  } from "@nextclaw/openclaw-compat";
@@ -2749,7 +2749,8 @@ var CHANNEL_LABELS = {
2749
2749
  wecom: "WeCom",
2750
2750
  email: "Email",
2751
2751
  slack: "Slack",
2752
- qq: "QQ"
2752
+ qq: "QQ",
2753
+ weixin: "Weixin"
2753
2754
  };
2754
2755
  var RESERVED_PROVIDER_IDS2 = builtinProviderIds2();
2755
2756
  var ChannelCommands = class {
@@ -3985,12 +3986,10 @@ var DiagnosticsCommands = class {
3985
3986
  };
3986
3987
 
3987
3988
  // src/cli/commands/service.ts
3988
- import * as NextclawCore2 from "@nextclaw/core";
3989
+ import * as NextclawCore3 from "@nextclaw/core";
3989
3990
  import {
3990
- getPluginUiMetadataFromRegistry,
3991
3991
  resolvePluginChannelMessageToolHints as resolvePluginChannelMessageToolHints2,
3992
3992
  setPluginRuntimeBridge as setPluginRuntimeBridge2,
3993
- startPluginChannelGateways as startPluginChannelGateways2,
3994
3993
  stopPluginChannelGateways as stopPluginChannelGateways2
3995
3994
  } from "@nextclaw/openclaw-compat";
3996
3995
  import { appendFileSync, closeSync as closeSync2, cpSync as cpSync2, existsSync as existsSync11, mkdirSync as mkdirSync5, openSync as openSync2 } from "fs";
@@ -4254,221 +4253,6 @@ var ServiceMarketplaceInstaller = class {
4254
4253
  }
4255
4254
  };
4256
4255
 
4257
- // src/cli/commands/service-plugin-runtime-bridge.ts
4258
- import { loadConfig as loadConfig14, resolveConfigSecrets as resolveConfigSecrets2, saveConfig as saveConfig9 } from "@nextclaw/core";
4259
- import { setPluginRuntimeBridge } from "@nextclaw/openclaw-compat";
4260
- function installPluginRuntimeBridge(params) {
4261
- const { runtimePool, runtimeConfigPath, pluginChannelBindings } = params;
4262
- setPluginRuntimeBridge({
4263
- loadConfig: () => toPluginConfigView(resolveConfigSecrets2(loadConfig14(), { configPath: runtimeConfigPath }), pluginChannelBindings),
4264
- writeConfigFile: async (nextConfigView) => {
4265
- if (!nextConfigView || typeof nextConfigView !== "object" || Array.isArray(nextConfigView)) {
4266
- throw new Error("plugin runtime writeConfigFile expects an object config");
4267
- }
4268
- const current = loadConfig14();
4269
- const next = mergePluginConfigView(current, nextConfigView, pluginChannelBindings);
4270
- saveConfig9(next);
4271
- },
4272
- dispatchReplyWithBufferedBlockDispatcher: async ({ ctx, dispatcherOptions }) => {
4273
- const request = resolvePluginRuntimeRequest(ctx);
4274
- if (!request) {
4275
- return;
4276
- }
4277
- try {
4278
- await dispatcherOptions.onReplyStart?.();
4279
- const response = await runtimePool.processDirect(request);
4280
- const replyText = typeof response === "string" ? response : String(response ?? "");
4281
- if (replyText.trim()) {
4282
- await dispatcherOptions.deliver({ text: replyText }, { kind: "final" });
4283
- }
4284
- } catch (error) {
4285
- dispatcherOptions.onError?.(error);
4286
- throw error;
4287
- }
4288
- }
4289
- });
4290
- }
4291
- function resolvePluginRuntimeRequest(ctx) {
4292
- const bodyForAgent = typeof ctx.BodyForAgent === "string" ? ctx.BodyForAgent : "";
4293
- const body = typeof ctx.Body === "string" ? ctx.Body : "";
4294
- const content = (bodyForAgent || body).trim();
4295
- const attachments = resolvePluginRuntimeAttachments(ctx);
4296
- if (!content && attachments.length === 0) {
4297
- return null;
4298
- }
4299
- const sessionKey = typeof ctx.SessionKey === "string" && ctx.SessionKey.trim().length > 0 ? ctx.SessionKey : void 0;
4300
- const channel = typeof ctx.OriginatingChannel === "string" && ctx.OriginatingChannel.trim().length > 0 ? ctx.OriginatingChannel : "cli";
4301
- const chatId = typeof ctx.OriginatingTo === "string" && ctx.OriginatingTo.trim().length > 0 ? ctx.OriginatingTo : typeof ctx.SenderId === "string" && ctx.SenderId.trim().length > 0 ? ctx.SenderId : "direct";
4302
- const agentId = typeof ctx.AgentId === "string" ? ctx.AgentId : void 0;
4303
- const modelOverride = resolveModelOverride(ctx);
4304
- const accountId = typeof ctx.AccountId === "string" && ctx.AccountId.trim().length > 0 ? ctx.AccountId : void 0;
4305
- return {
4306
- content,
4307
- sessionKey,
4308
- channel,
4309
- chatId,
4310
- agentId,
4311
- attachments,
4312
- metadata: {
4313
- ...accountId ? { account_id: accountId } : {},
4314
- ...modelOverride ? { model: modelOverride } : {}
4315
- }
4316
- };
4317
- }
4318
- function resolveModelOverride(ctx) {
4319
- if (typeof ctx.Model === "string" && ctx.Model.trim().length > 0) {
4320
- return ctx.Model.trim();
4321
- }
4322
- if (typeof ctx.AgentModel === "string" && ctx.AgentModel.trim().length > 0) {
4323
- return ctx.AgentModel.trim();
4324
- }
4325
- return void 0;
4326
- }
4327
- function readStringList(value) {
4328
- if (!Array.isArray(value)) {
4329
- return [];
4330
- }
4331
- return value.map((entry) => typeof entry === "string" ? entry.trim() : "").filter(Boolean);
4332
- }
4333
- function readOptionalString(value) {
4334
- if (typeof value !== "string") {
4335
- return void 0;
4336
- }
4337
- const trimmed = value.trim();
4338
- return trimmed || void 0;
4339
- }
4340
- function resolvePluginRuntimeAttachments(ctx) {
4341
- const mediaPaths = readStringList(ctx.MediaPaths);
4342
- const mediaUrls = readStringList(ctx.MediaUrls);
4343
- const fallbackPath = readOptionalString(ctx.MediaPath);
4344
- const fallbackUrl = readOptionalString(ctx.MediaUrl);
4345
- const mediaTypes = readStringList(ctx.MediaTypes);
4346
- const fallbackType = readOptionalString(ctx.MediaType);
4347
- const entryCount = Math.max(
4348
- mediaPaths.length,
4349
- mediaUrls.length,
4350
- fallbackPath ? 1 : 0,
4351
- fallbackUrl ? 1 : 0
4352
- );
4353
- const attachments = [];
4354
- for (let index = 0; index < entryCount; index += 1) {
4355
- const path2 = mediaPaths[index] ?? (index === 0 ? fallbackPath : void 0);
4356
- const rawUrl = mediaUrls[index] ?? (index === 0 ? fallbackUrl : void 0);
4357
- const url = rawUrl && rawUrl !== path2 ? rawUrl : void 0;
4358
- const mimeType = mediaTypes[index] ?? fallbackType;
4359
- if (!path2 && !url) {
4360
- continue;
4361
- }
4362
- attachments.push({
4363
- path: path2,
4364
- url,
4365
- mimeType,
4366
- source: "plugin-runtime",
4367
- status: path2 ? "ready" : "remote-only"
4368
- });
4369
- }
4370
- return attachments;
4371
- }
4372
-
4373
- // src/cli/commands/service-plugin-reload.ts
4374
- import { getWorkspacePath as getWorkspacePath7 } from "@nextclaw/core";
4375
- import {
4376
- getPluginChannelBindings as getPluginChannelBindings3,
4377
- startPluginChannelGateways,
4378
- stopPluginChannelGateways
4379
- } from "@nextclaw/openclaw-compat";
4380
-
4381
- // src/cli/commands/plugin-reload.ts
4382
- function buildPluginChannelBindingSignature(binding) {
4383
- return `${binding.pluginId}:${binding.channelId}`;
4384
- }
4385
- function buildSortedBindingSignatures(bindings) {
4386
- return bindings.map(buildPluginChannelBindingSignature).sort();
4387
- }
4388
- function buildSortedExtensionChannelIds(channels2) {
4389
- return channels2.map((registration) => registration.channel.id).filter((id) => typeof id === "string" && id.trim().length > 0).sort();
4390
- }
4391
- function areSortedStringListsEqual(left, right) {
4392
- if (left.length !== right.length) {
4393
- return false;
4394
- }
4395
- for (let index = 0; index < left.length; index += 1) {
4396
- if (left[index] !== right[index]) {
4397
- return false;
4398
- }
4399
- }
4400
- return true;
4401
- }
4402
- function readPluginIdFromPluginsEntryPath(path2) {
4403
- const prefix = "plugins.entries.";
4404
- if (!path2.startsWith(prefix)) {
4405
- return null;
4406
- }
4407
- const suffix = path2.slice(prefix.length);
4408
- const [pluginId] = suffix.split(".");
4409
- return pluginId?.trim() ? pluginId.trim() : null;
4410
- }
4411
- function shouldRestartChannelsForPluginReload(params) {
4412
- const currentBindingSignatures = buildSortedBindingSignatures(params.currentPluginChannelBindings);
4413
- const nextBindingSignatures = buildSortedBindingSignatures(params.nextPluginChannelBindings);
4414
- if (!areSortedStringListsEqual(currentBindingSignatures, nextBindingSignatures)) {
4415
- return true;
4416
- }
4417
- const currentExtensionChannelIds = buildSortedExtensionChannelIds(params.currentExtensionChannels);
4418
- const nextExtensionChannelIds = buildSortedExtensionChannelIds(params.nextExtensionChannels);
4419
- if (!areSortedStringListsEqual(currentExtensionChannelIds, nextExtensionChannelIds)) {
4420
- return true;
4421
- }
4422
- const channelPluginIds = /* @__PURE__ */ new Set();
4423
- for (const binding of params.currentPluginChannelBindings) {
4424
- channelPluginIds.add(binding.pluginId);
4425
- }
4426
- for (const binding of params.nextPluginChannelBindings) {
4427
- channelPluginIds.add(binding.pluginId);
4428
- }
4429
- for (const path2 of params.changedPaths) {
4430
- const pluginId = readPluginIdFromPluginsEntryPath(path2);
4431
- if (pluginId && channelPluginIds.has(pluginId)) {
4432
- return true;
4433
- }
4434
- }
4435
- return false;
4436
- }
4437
-
4438
- // src/cli/commands/service-plugin-reload.ts
4439
- async function reloadServicePlugins(params) {
4440
- const nextWorkspace = getWorkspacePath7(params.nextConfig.agents.defaults.workspace);
4441
- const nextPluginRegistry = loadPluginRegistry(params.nextConfig, nextWorkspace);
4442
- const nextExtensionRegistry = toExtensionRegistry(nextPluginRegistry);
4443
- const nextPluginChannelBindings = getPluginChannelBindings3(nextPluginRegistry);
4444
- const shouldRestartChannels = shouldRestartChannelsForPluginReload({
4445
- changedPaths: params.changedPaths,
4446
- currentPluginChannelBindings: params.pluginChannelBindings,
4447
- nextPluginChannelBindings,
4448
- currentExtensionChannels: params.extensionRegistry.channels,
4449
- nextExtensionChannels: nextExtensionRegistry.channels
4450
- });
4451
- logPluginDiagnostics(nextPluginRegistry);
4452
- let pluginGatewayHandles = params.pluginGatewayHandles;
4453
- if (shouldRestartChannels) {
4454
- await stopPluginChannelGateways(pluginGatewayHandles);
4455
- const startedPluginGateways = await startPluginChannelGateways({
4456
- registry: nextPluginRegistry,
4457
- config: params.nextConfig,
4458
- logger: params.pluginGatewayLogger
4459
- });
4460
- pluginGatewayHandles = startedPluginGateways.handles;
4461
- params.logPluginGatewayDiagnostics(startedPluginGateways.diagnostics);
4462
- }
4463
- return {
4464
- pluginRegistry: nextPluginRegistry,
4465
- extensionRegistry: nextExtensionRegistry,
4466
- pluginChannelBindings: nextPluginChannelBindings,
4467
- pluginGatewayHandles,
4468
- restartChannels: shouldRestartChannels
4469
- };
4470
- }
4471
-
4472
4256
  // src/cli/commands/service-startup-support.ts
4473
4257
  var pluginGatewayLogger = {
4474
4258
  info: (message) => console.log(`[plugins] ${message}`),
@@ -4626,7 +4410,7 @@ function tryClaimRemoteOwnershipLock(params) {
4626
4410
  function claimManagedRemoteRuntimeOwnership(params) {
4627
4411
  const lockPath = params.lockPath ?? resolveRemoteOwnershipLockPath();
4628
4412
  const currentPid = params.currentPid ?? process.pid;
4629
- const now = params.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
4413
+ const now3 = params.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
4630
4414
  const isProcessRunningFn = params.isProcessRunningFn ?? isProcessRunning;
4631
4415
  const readServiceStateFn = params.readServiceStateFn ?? readServiceState;
4632
4416
  const managedConflict = detectManagedRemoteOwnershipConflict({
@@ -4646,7 +4430,7 @@ function claimManagedRemoteRuntimeOwnership(params) {
4646
4430
  claim: {
4647
4431
  pid: currentPid,
4648
4432
  localOrigin: params.localOrigin,
4649
- claimedAt: now()
4433
+ claimedAt: now3()
4650
4434
  },
4651
4435
  currentPid,
4652
4436
  isProcessRunningFn
@@ -4714,11 +4498,11 @@ function writeReadyManagedServiceState(params) {
4714
4498
  }
4715
4499
 
4716
4500
  // src/cli/commands/remote-access-host.ts
4717
- import { getConfigPath as getConfigPath8, loadConfig as loadConfig16 } from "@nextclaw/core";
4501
+ import { getConfigPath as getConfigPath8, loadConfig as loadConfig15 } from "@nextclaw/core";
4718
4502
  import { readPlatformSessionTokenState as readPlatformSessionTokenState2 } from "@nextclaw/remote";
4719
4503
 
4720
4504
  // src/cli/commands/remote-access-service-control.ts
4721
- import { getConfigPath as getConfigPath7, loadConfig as loadConfig15 } from "@nextclaw/core";
4505
+ import { getConfigPath as getConfigPath7, loadConfig as loadConfig14 } from "@nextclaw/core";
4722
4506
  import { spawn as spawn2 } from "child_process";
4723
4507
  var FORCED_PUBLIC_UI_HOST = "0.0.0.0";
4724
4508
  function resolveRemoteServiceView(currentUi) {
@@ -4807,7 +4591,7 @@ async function controlManagedService(action, deps) {
4807
4591
  return { accepted: true, action, message: "Managed service restarted." };
4808
4592
  }
4809
4593
  function resolveManagedUiOverrides() {
4810
- const config2 = loadConfig15(getConfigPath7());
4594
+ const config2 = loadConfig14(getConfigPath7());
4811
4595
  const resolved = resolveUiConfig(config2, {
4812
4596
  enabled: true,
4813
4597
  host: FORCED_PUBLIC_UI_HOST,
@@ -4907,7 +4691,7 @@ var RemoteAccessHost = class {
4907
4691
  this.deps = deps;
4908
4692
  }
4909
4693
  getStatus() {
4910
- const config2 = loadConfig16(getConfigPath8());
4694
+ const config2 = loadConfig15(getConfigPath8());
4911
4695
  const status = this.deps.remoteCommands.getStatusView();
4912
4696
  const account = this.readAccountView({
4913
4697
  token: normalizeOptionalString4(config2.providers.nextclaw?.apiKey),
@@ -4944,7 +4728,7 @@ var RemoteAccessHost = class {
4944
4728
  };
4945
4729
  }
4946
4730
  async pollBrowserAuth(input) {
4947
- const config2 = loadConfig16(getConfigPath8());
4731
+ const config2 = loadConfig15(getConfigPath8());
4948
4732
  const result = await this.deps.platformAuthCommands.pollBrowserAuth({
4949
4733
  apiBase: normalizeOptionalString4(input.apiBase) ?? normalizeOptionalString4(config2.remote.platformApiBase) ?? normalizeOptionalString4(config2.providers.nextclaw?.apiBase) ?? void 0,
4950
4734
  sessionId: input.sessionId
@@ -5064,7 +4848,7 @@ import { resolve as resolve11 } from "path";
5064
4848
  import {
5065
4849
  buildAssetContentPath
5066
4850
  } from "@nextclaw/ncp-agent-runtime";
5067
- function readOptionalString2(value) {
4851
+ function readOptionalString(value) {
5068
4852
  if (typeof value !== "string") {
5069
4853
  return null;
5070
4854
  }
@@ -5072,7 +4856,7 @@ function readOptionalString2(value) {
5072
4856
  return trimmed.length > 0 ? trimmed : null;
5073
4857
  }
5074
4858
  function readOptionalBase64Bytes(value) {
5075
- const base64 = readOptionalString2(value);
4859
+ const base64 = readOptionalString(value);
5076
4860
  if (!base64) {
5077
4861
  return null;
5078
4862
  }
@@ -5124,9 +4908,9 @@ var AssetPutTool = class {
5124
4908
  }
5125
4909
  };
5126
4910
  async execute(args) {
5127
- const path2 = readOptionalString2(args?.path);
5128
- const fileName = readOptionalString2(args?.fileName);
5129
- const mimeType = readOptionalString2(args?.mimeType);
4911
+ const path2 = readOptionalString(args?.path);
4912
+ const fileName = readOptionalString(args?.fileName);
4913
+ const mimeType = readOptionalString(args?.mimeType);
5130
4914
  const bytes = readOptionalBase64Bytes(args?.bytesBase64);
5131
4915
  let record;
5132
4916
  if (path2) {
@@ -5171,8 +4955,8 @@ var AssetExportTool = class {
5171
4955
  required: ["assetUri", "targetPath"]
5172
4956
  };
5173
4957
  async execute(args) {
5174
- const assetUri = readOptionalString2(args?.assetUri);
5175
- const targetPath = readOptionalString2(args?.targetPath);
4958
+ const assetUri = readOptionalString(args?.assetUri);
4959
+ const targetPath = readOptionalString(args?.targetPath);
5176
4960
  if (!assetUri || !targetPath) {
5177
4961
  throw new Error("asset_export requires assetUri and targetPath.");
5178
4962
  }
@@ -5202,7 +4986,7 @@ var AssetStatTool = class {
5202
4986
  required: ["assetUri"]
5203
4987
  };
5204
4988
  async execute(args) {
5205
- const assetUri = readOptionalString2(args?.assetUri);
4989
+ const assetUri = readOptionalString(args?.assetUri);
5206
4990
  if (!assetUri) {
5207
4991
  throw new Error("asset_stat requires assetUri.");
5208
4992
  }
@@ -5236,7 +5020,7 @@ import {
5236
5020
  buildToolCatalogEntries,
5237
5021
  ContextBuilder,
5238
5022
  InputBudgetPruner,
5239
- getWorkspacePath as getWorkspacePath8,
5023
+ getWorkspacePath as getWorkspacePath7,
5240
5024
  parseThinkingLevel as parseThinkingLevel2,
5241
5025
  resolveThinkingLevel
5242
5026
  } from "@nextclaw/core";
@@ -5895,7 +5679,7 @@ function resolvePrimaryAgentProfile(config2) {
5895
5679
  const profile = config2.agents.list.find((entry) => entry.id.trim() === configuredDefaultAgentId);
5896
5680
  return {
5897
5681
  agentId: configuredDefaultAgentId,
5898
- workspace: getWorkspacePath8(profile?.workspace ?? config2.agents.defaults.workspace),
5682
+ workspace: getWorkspacePath7(profile?.workspace ?? config2.agents.defaults.workspace),
5899
5683
  model: profile?.model ?? config2.agents.defaults.model,
5900
5684
  maxIterations: profile?.maxToolIterations ?? config2.agents.defaults.maxToolIterations,
5901
5685
  contextTokens: profile?.contextTokens ?? config2.agents.defaults.contextTokens,
@@ -6749,7 +6533,6 @@ function createUiNcpAgentHandle(params) {
6749
6533
  basePath: "/api/ncp/agent",
6750
6534
  agentClientEndpoint: createAgentClientFromServer(params.backend),
6751
6535
  streamProvider: params.backend,
6752
- sessionApi: params.backend,
6753
6536
  listSessionTypes: (describeParams) => {
6754
6537
  params.refreshPluginRuntimeRegistrations();
6755
6538
  return params.runtimeRegistry.listSessionTypes(describeParams);
@@ -6807,9 +6590,96 @@ async function createUiNcpAgent(params) {
6807
6590
  });
6808
6591
  }
6809
6592
 
6593
+ // src/cli/commands/ncp/ui-session-service.ts
6594
+ function applyLimit(items, limit) {
6595
+ if (!Number.isFinite(limit) || typeof limit !== "number" || limit <= 0) {
6596
+ return items;
6597
+ }
6598
+ return items.slice(0, Math.trunc(limit));
6599
+ }
6600
+ function now() {
6601
+ return (/* @__PURE__ */ new Date()).toISOString();
6602
+ }
6603
+ function toSessionSummary(params) {
6604
+ return {
6605
+ sessionId: params.sessionId,
6606
+ messageCount: params.messages.length,
6607
+ updatedAt: params.updatedAt,
6608
+ status: "idle",
6609
+ ...params.metadata ? { metadata: structuredClone(params.metadata) } : {}
6610
+ };
6611
+ }
6612
+ function buildUpdatedMetadata(params) {
6613
+ if (params.patch.metadata === null) {
6614
+ return {};
6615
+ }
6616
+ if (params.patch.metadata) {
6617
+ return structuredClone(params.patch.metadata);
6618
+ }
6619
+ return structuredClone(params.existingMetadata ?? {});
6620
+ }
6621
+ var UiSessionService = class {
6622
+ sessionStore;
6623
+ constructor(sessionManager) {
6624
+ this.sessionStore = new NextclawAgentSessionStore(sessionManager);
6625
+ }
6626
+ async listSessions(options) {
6627
+ const sessions = await this.sessionStore.listSessions();
6628
+ return applyLimit(
6629
+ sessions.map(
6630
+ (session) => toSessionSummary({
6631
+ sessionId: session.sessionId,
6632
+ messages: session.messages,
6633
+ updatedAt: session.updatedAt,
6634
+ metadata: session.metadata
6635
+ })
6636
+ ),
6637
+ options?.limit
6638
+ );
6639
+ }
6640
+ async listSessionMessages(sessionId, options) {
6641
+ const session = await this.sessionStore.getSession(sessionId);
6642
+ if (!session) {
6643
+ return [];
6644
+ }
6645
+ return applyLimit(session.messages.map((message) => structuredClone(message)), options?.limit);
6646
+ }
6647
+ async getSession(sessionId) {
6648
+ const session = await this.sessionStore.getSession(sessionId);
6649
+ if (!session) {
6650
+ return null;
6651
+ }
6652
+ return toSessionSummary({
6653
+ sessionId,
6654
+ messages: session.messages,
6655
+ updatedAt: session.updatedAt,
6656
+ metadata: session.metadata
6657
+ });
6658
+ }
6659
+ async updateSession(sessionId, patch) {
6660
+ const session = await this.sessionStore.getSession(sessionId);
6661
+ if (!session) {
6662
+ return null;
6663
+ }
6664
+ await this.sessionStore.saveSession({
6665
+ sessionId,
6666
+ messages: session.messages.map((message) => structuredClone(message)),
6667
+ updatedAt: now(),
6668
+ metadata: buildUpdatedMetadata({
6669
+ existingMetadata: session.metadata,
6670
+ patch
6671
+ })
6672
+ });
6673
+ return await this.getSession(sessionId);
6674
+ }
6675
+ async deleteSession(sessionId) {
6676
+ await this.sessionStore.deleteSession(sessionId);
6677
+ }
6678
+ };
6679
+
6810
6680
  // src/cli/commands/service-gateway-context.ts
6811
6681
  import * as NextclawCore from "@nextclaw/core";
6812
- import { getPluginChannelBindings as getPluginChannelBindings4, resolvePluginChannelMessageToolHints } from "@nextclaw/openclaw-compat";
6682
+ import { getPluginChannelBindings as getPluginChannelBindings3, resolvePluginChannelMessageToolHints } from "@nextclaw/openclaw-compat";
6813
6683
  import { join as join6 } from "path";
6814
6684
 
6815
6685
  // src/cli/gateway/controller.ts
@@ -7157,7 +7027,7 @@ var ConfigReloader = class {
7157
7027
  console.log("Config reload: MCP servers reloaded.");
7158
7028
  }
7159
7029
  if (plan.restartChannels || reloadPluginsResult?.restartChannels) {
7160
- await this.reloadChannels(nextConfig);
7030
+ await this.reloadChannels(nextConfig, { start: true });
7161
7031
  console.log("Config reload: channels restarted.");
7162
7032
  }
7163
7033
  if (plan.reloadProviders) {
@@ -7207,7 +7077,10 @@ var ConfigReloader = class {
7207
7077
  await this.runReload(reason ?? "gateway tool");
7208
7078
  return "Config reload triggered";
7209
7079
  }
7210
- async reloadChannels(nextConfig) {
7080
+ async rebuildChannels(nextConfig, options = {}) {
7081
+ await this.reloadChannels(nextConfig, { start: options.start ?? true });
7082
+ }
7083
+ async reloadChannels(nextConfig, options) {
7211
7084
  if (this.reloadTask) {
7212
7085
  await this.reloadTask;
7213
7086
  return;
@@ -7221,7 +7094,9 @@ var ConfigReloader = class {
7221
7094
  this.options.sessionManager,
7222
7095
  this.options.getExtensionChannels?.() ?? []
7223
7096
  );
7224
- await this.channels.startAll();
7097
+ if (options.start) {
7098
+ await this.channels.startAll();
7099
+ }
7225
7100
  })();
7226
7101
  try {
7227
7102
  await this.reloadTask;
@@ -7273,7 +7148,7 @@ import {
7273
7148
  createAssistantStreamDeltaControlMessage,
7274
7149
  createAssistantStreamResetControlMessage,
7275
7150
  AgentRouteResolver,
7276
- getWorkspacePath as getWorkspacePath9,
7151
+ getWorkspacePath as getWorkspacePath8,
7277
7152
  parseAgentScopedSessionKey
7278
7153
  } from "@nextclaw/core";
7279
7154
  function normalizeAgentId(value) {
@@ -7318,7 +7193,7 @@ function resolveAgentProfiles(config2) {
7318
7193
  }
7319
7194
  return Array.from(unique.values()).map((entry) => ({
7320
7195
  id: entry.id,
7321
- workspace: getWorkspacePath9(entry.workspace ?? defaults.workspace),
7196
+ workspace: getWorkspacePath8(entry.workspace ?? defaults.workspace),
7322
7197
  model: entry.model ?? defaults.model,
7323
7198
  engine: normalizeEngineKind(entry.engine ?? defaults.engine),
7324
7199
  engineConfig: entry.engineConfig ?? toRecord(defaults.engineConfig),
@@ -7586,7 +7461,7 @@ var GatewayAgentRuntimePool = class {
7586
7461
  const normalizedAgentId = normalizeAgentId(agentId);
7587
7462
  return this.resolvedProfiles.find((profile) => profile.id === normalizedAgentId) ?? this.resolvedProfiles.find((profile) => profile.id === this.defaultAgentId) ?? this.resolvedProfiles[0] ?? {
7588
7463
  id: this.defaultAgentId,
7589
- workspace: getWorkspacePath9(this.options.config.agents.defaults.workspace),
7464
+ workspace: getWorkspacePath8(this.options.config.agents.defaults.workspace),
7590
7465
  model: this.options.config.agents.defaults.model,
7591
7466
  maxIterations: this.options.config.agents.defaults.maxToolIterations,
7592
7467
  contextTokens: this.options.config.agents.defaults.contextTokens,
@@ -7808,31 +7683,36 @@ var {
7808
7683
  CronService: CronService2,
7809
7684
  getConfigPath: getConfigPath9,
7810
7685
  getDataDir: getDataDir8,
7811
- getWorkspacePath: getWorkspacePath10,
7686
+ getWorkspacePath: getWorkspacePath9,
7812
7687
  HeartbeatService,
7813
- loadConfig: loadConfig17,
7688
+ loadConfig: loadConfig16,
7814
7689
  MessageBus,
7815
7690
  ProviderManager,
7816
- resolveConfigSecrets: resolveConfigSecrets3,
7817
- saveConfig: saveConfig10,
7691
+ resolveConfigSecrets: resolveConfigSecrets2,
7692
+ saveConfig: saveConfig9,
7818
7693
  SessionManager
7819
7694
  } = NextclawCore;
7820
7695
  function createGatewayShellContext(params) {
7821
7696
  const runtimeConfigPath = getConfigPath9();
7822
- const config2 = resolveConfigSecrets3(loadConfig17(), { configPath: runtimeConfigPath });
7823
- const workspace = getWorkspacePath10(config2.agents.defaults.workspace);
7697
+ const config2 = resolveConfigSecrets2(loadConfig16(), { configPath: runtimeConfigPath });
7698
+ const workspace = getWorkspacePath9(config2.agents.defaults.workspace);
7824
7699
  const cronStorePath = join6(getDataDir8(), "cron", "jobs.json");
7700
+ const sessionManager = measureStartupSync(
7701
+ "service.gateway_shell_context.session_manager",
7702
+ () => new SessionManager(workspace)
7703
+ );
7825
7704
  const cron2 = new CronService2(cronStorePath);
7826
7705
  const uiConfig = resolveUiConfig(config2, params.uiOverrides);
7827
7706
  const uiStaticDir = params.uiStaticDir === void 0 ? resolveUiStaticDir() : params.uiStaticDir;
7828
7707
  const remoteModule = createManagedRemoteModuleForUi({
7829
- loadConfig: () => resolveConfigSecrets3(loadConfig17(), { configPath: runtimeConfigPath }),
7708
+ loadConfig: () => resolveConfigSecrets2(loadConfig16(), { configPath: runtimeConfigPath }),
7830
7709
  uiConfig
7831
7710
  });
7832
7711
  return {
7833
7712
  runtimeConfigPath,
7834
7713
  config: config2,
7835
7714
  workspace,
7715
+ sessionManager,
7836
7716
  cron: cron2,
7837
7717
  uiConfig,
7838
7718
  uiStaticDir,
@@ -7848,17 +7728,18 @@ function createGatewayStartupContext(params) {
7848
7728
  state.runtimeConfigPath = shellContext.runtimeConfigPath;
7849
7729
  state.config = shellContext.config;
7850
7730
  state.workspace = shellContext.workspace;
7731
+ state.sessionManager = shellContext.sessionManager;
7851
7732
  state.cron = shellContext.cron;
7852
7733
  state.uiConfig = shellContext.uiConfig;
7853
7734
  state.uiStaticDir = shellContext.uiStaticDir;
7854
7735
  state.remoteModule = shellContext.remoteModule;
7855
- state.pluginRegistry = measureStartupSync(
7736
+ state.pluginRegistry = params.initialPluginRegistry ?? measureStartupSync(
7856
7737
  "service.gateway_context.load_plugin_registry",
7857
7738
  () => loadPluginRegistry(state.config, state.workspace)
7858
7739
  );
7859
7740
  state.pluginChannelBindings = measureStartupSync(
7860
7741
  "service.gateway_context.get_plugin_channel_bindings",
7861
- () => getPluginChannelBindings4(state.pluginRegistry)
7742
+ () => getPluginChannelBindings3(state.pluginRegistry)
7862
7743
  );
7863
7744
  state.extensionRegistry = measureStartupSync(
7864
7745
  "service.gateway_context.to_extension_registry",
@@ -7874,10 +7755,6 @@ function createGatewayStartupContext(params) {
7874
7755
  config: state.config
7875
7756
  })
7876
7757
  );
7877
- state.sessionManager = measureStartupSync(
7878
- "service.gateway_context.session_manager",
7879
- () => new SessionManager(state.workspace)
7880
- );
7881
7758
  if (!provider) {
7882
7759
  console.warn(
7883
7760
  "Warning: No API key configured. The gateway is running, but agent replies are disabled until provider config is set."
@@ -7898,7 +7775,7 @@ function createGatewayStartupContext(params) {
7898
7775
  sessionManager: state.sessionManager,
7899
7776
  providerManager: state.providerManager,
7900
7777
  makeProvider: (nextConfig) => params.makeProvider(nextConfig, { allowMissing: true }) ?? params.makeMissingProvider(nextConfig),
7901
- loadConfig: () => resolveConfigSecrets3(loadConfig17(), { configPath: state.runtimeConfigPath }),
7778
+ loadConfig: () => resolveConfigSecrets2(loadConfig16(), { configPath: state.runtimeConfigPath }),
7902
7779
  resolveChannelConfig: (nextConfig) => resolveChannelConfigView(nextConfig, state.pluginChannelBindings),
7903
7780
  getExtensionChannels: () => state.extensionRegistry.channels,
7904
7781
  onRestartRequired: (paths) => {
@@ -7911,7 +7788,7 @@ function createGatewayStartupContext(params) {
7911
7788
  })
7912
7789
  );
7913
7790
  state.applyLiveConfigReload = async () => {
7914
- await state.reloader.applyReloadPlan(resolveConfigSecrets3(loadConfig17(), { configPath: state.runtimeConfigPath }));
7791
+ await state.reloader.applyReloadPlan(resolveConfigSecrets2(loadConfig16(), { configPath: state.runtimeConfigPath }));
7915
7792
  };
7916
7793
  state.gatewayController = measureStartupSync(
7917
7794
  "service.gateway_context.gateway_controller",
@@ -7920,7 +7797,7 @@ function createGatewayStartupContext(params) {
7920
7797
  cron: state.cron,
7921
7798
  sessionManager: state.sessionManager,
7922
7799
  getConfigPath: getConfigPath9,
7923
- saveConfig: saveConfig10,
7800
+ saveConfig: saveConfig9,
7924
7801
  requestRestart: async (options) => {
7925
7802
  await params.requestRestart({
7926
7803
  reason: options?.reason ?? "gateway tool restart",
@@ -7949,7 +7826,7 @@ function createGatewayStartupContext(params) {
7949
7826
  resolveMessageToolHints: ({ channel, accountId }) => resolvePluginChannelMessageToolHints({
7950
7827
  registry: state.pluginRegistry,
7951
7828
  channel,
7952
- cfg: resolveConfigSecrets3(loadConfig17(), { configPath: state.runtimeConfigPath }),
7829
+ cfg: resolveConfigSecrets2(loadConfig16(), { configPath: state.runtimeConfigPath }),
7953
7830
  accountId
7954
7831
  })
7955
7832
  })
@@ -7969,7 +7846,9 @@ function createGatewayStartupContext(params) {
7969
7846
  }
7970
7847
 
7971
7848
  // src/cli/commands/service-gateway-startup.ts
7972
- import { startUiServer } from "@nextclaw/server";
7849
+ import {
7850
+ startUiServer
7851
+ } from "@nextclaw/server";
7973
7852
 
7974
7853
  // src/cli/commands/service-deferred-ncp-agent.ts
7975
7854
  var DEFAULT_BASE_PATH = "/api/ncp/agent";
@@ -8039,7 +7918,6 @@ function createDeferredUiNcpAgent(basePath = DEFAULT_BASE_PATH) {
8039
7918
  activeAgent = null;
8040
7919
  agent.basePath = basePath;
8041
7920
  agent.streamProvider = void 0;
8042
- agent.sessionApi = void 0;
8043
7921
  agent.listSessionTypes = void 0;
8044
7922
  agent.assetApi = void 0;
8045
7923
  };
@@ -8049,7 +7927,6 @@ function createDeferredUiNcpAgent(basePath = DEFAULT_BASE_PATH) {
8049
7927
  activeAgent = nextAgent;
8050
7928
  agent.basePath = nextAgent.basePath ?? basePath;
8051
7929
  agent.streamProvider = nextAgent.streamProvider;
8052
- agent.sessionApi = nextAgent.sessionApi;
8053
7930
  agent.listSessionTypes = nextAgent.listSessionTypes;
8054
7931
  agent.assetApi = nextAgent.assetApi;
8055
7932
  },
@@ -8083,8 +7960,10 @@ async function startUiShell(params) {
8083
7960
  cronService: params.cronService,
8084
7961
  marketplace: params.marketplace,
8085
7962
  remoteAccess: params.remoteAccess,
7963
+ getBootstrapStatus: params.getBootstrapStatus,
8086
7964
  getPluginChannelBindings: params.getPluginChannelBindings,
8087
7965
  getPluginUiMetadata: params.getPluginUiMetadata,
7966
+ ncpSessionService: params.ncpSessionService,
8088
7967
  ncpAgent: deferredNcpAgent.agent
8089
7968
  });
8090
7969
  publishUiEvent = uiServer.publish;
@@ -8131,6 +8010,9 @@ async function startDeferredGatewayStartup(params) {
8131
8010
  console.error(`UI NCP agent startup failed: ${error instanceof Error ? error.message : String(error)}`);
8132
8011
  }
8133
8012
  }
8013
+ if (params.hydrateCapabilities) {
8014
+ await measureStartupAsync("service.deferred_startup.hydrate_capabilities", params.hydrateCapabilities);
8015
+ }
8134
8016
  await measureStartupAsync("service.deferred_startup.start_plugin_gateways", params.startPluginGateways);
8135
8017
  await measureStartupAsync("service.deferred_startup.start_channels", params.startChannels);
8136
8018
  await measureStartupAsync("service.deferred_startup.wake_restart_sentinel", params.wakeFromRestartSentinel);
@@ -8152,18 +8034,570 @@ async function runGatewayRuntimeLoop(params) {
8152
8034
  }
8153
8035
  }
8154
8036
 
8155
- // src/cli/commands/service-ui-shell-grace.ts
8156
- import { setTimeout as delay } from "timers/promises";
8157
- var DEFAULT_UI_SHELL_GRACE_MS = 3e3;
8158
- async function waitForUiShellGraceWindow(uiStartup) {
8159
- if (!uiStartup) {
8160
- return;
8161
- }
8037
+ // src/cli/commands/plugin-registry-loader.ts
8038
+ import {
8039
+ discoverPluginStatusReport,
8040
+ loadOpenClawPluginsProgressively
8041
+ } from "@nextclaw/openclaw-compat";
8042
+ function createPluginLogger() {
8043
+ return {
8044
+ info: (message) => console.log(message),
8045
+ warn: (message) => console.warn(message),
8046
+ error: (message) => console.error(message),
8047
+ debug: (message) => console.debug(message)
8048
+ };
8049
+ }
8050
+ function withDevFirstPartyPluginPaths(config2) {
8051
+ const workspaceExtensionsDir = resolveDevFirstPartyPluginDir(process.env.NEXTCLAW_DEV_FIRST_PARTY_PLUGIN_DIR);
8052
+ return {
8053
+ workspaceExtensionsDir,
8054
+ configWithDevPluginPaths: applyDevFirstPartyPluginLoadPaths(config2, workspaceExtensionsDir)
8055
+ };
8056
+ }
8057
+ async function loadPluginRegistryProgressively(config2, workspaceDir, options = {}) {
8058
+ const { workspaceExtensionsDir, configWithDevPluginPaths } = withDevFirstPartyPluginPaths(config2);
8059
+ const excludedRoots = resolveDevFirstPartyPluginInstallRoots(config2, workspaceExtensionsDir);
8060
+ return await loadOpenClawPluginsProgressively({
8061
+ config: configWithDevPluginPaths,
8062
+ workspaceDir,
8063
+ excludeRoots: excludedRoots,
8064
+ ...buildReservedPluginLoadOptions(),
8065
+ onPluginProcessed: options.onPluginProcessed,
8066
+ logger: createPluginLogger()
8067
+ });
8068
+ }
8069
+ function discoverPluginRegistryStatus(config2, workspaceDir) {
8070
+ const { configWithDevPluginPaths } = withDevFirstPartyPluginPaths(config2);
8071
+ return discoverPluginStatusReport({
8072
+ config: configWithDevPluginPaths,
8073
+ workspaceDir
8074
+ });
8075
+ }
8076
+ function createEmptyPluginRegistry() {
8077
+ return {
8078
+ plugins: [],
8079
+ tools: [],
8080
+ channels: [],
8081
+ providers: [],
8082
+ engines: [],
8083
+ ncpAgentRuntimes: [],
8084
+ diagnostics: [],
8085
+ resolvedTools: []
8086
+ };
8087
+ }
8088
+
8089
+ // src/cli/commands/service-gateway-bootstrap.ts
8090
+ import * as NextclawCore2 from "@nextclaw/core";
8091
+ import {
8092
+ getPluginUiMetadataFromRegistry as getPluginUiMetadataFromRegistry2,
8093
+ startPluginChannelGateways as startPluginChannelGateways2
8094
+ } from "@nextclaw/openclaw-compat";
8095
+
8096
+ // src/cli/commands/service-bootstrap-status.ts
8097
+ function now2() {
8098
+ return (/* @__PURE__ */ new Date()).toISOString();
8099
+ }
8100
+ var ServiceBootstrapStatusStore = class {
8101
+ state = {
8102
+ phase: "kernel-starting",
8103
+ pluginHydration: {
8104
+ state: "pending",
8105
+ loadedPluginCount: 0,
8106
+ totalPluginCount: 0
8107
+ },
8108
+ channels: {
8109
+ state: "pending",
8110
+ enabled: []
8111
+ },
8112
+ remote: {
8113
+ state: "pending"
8114
+ }
8115
+ };
8116
+ getStatus() {
8117
+ return {
8118
+ ...this.state,
8119
+ pluginHydration: { ...this.state.pluginHydration },
8120
+ channels: {
8121
+ ...this.state.channels,
8122
+ enabled: [...this.state.channels.enabled]
8123
+ },
8124
+ remote: { ...this.state.remote }
8125
+ };
8126
+ }
8127
+ markShellReady() {
8128
+ this.state.phase = "shell-ready";
8129
+ this.state.shellReadyAt = this.state.shellReadyAt ?? now2();
8130
+ }
8131
+ markPluginHydrationPending() {
8132
+ this.state.pluginHydration = {
8133
+ ...this.state.pluginHydration,
8134
+ state: "pending",
8135
+ loadedPluginCount: 0,
8136
+ totalPluginCount: 0
8137
+ };
8138
+ }
8139
+ markPluginHydrationRunning(params) {
8140
+ this.state.phase = "hydrating-capabilities";
8141
+ this.state.pluginHydration = {
8142
+ ...this.state.pluginHydration,
8143
+ state: "running",
8144
+ loadedPluginCount: 0,
8145
+ totalPluginCount: params.totalPluginCount,
8146
+ startedAt: this.state.pluginHydration.startedAt ?? now2(),
8147
+ completedAt: void 0,
8148
+ error: void 0
8149
+ };
8150
+ }
8151
+ markPluginHydrationProgress(params) {
8152
+ this.state.pluginHydration = {
8153
+ ...this.state.pluginHydration,
8154
+ state: "running",
8155
+ loadedPluginCount: params.loadedPluginCount,
8156
+ totalPluginCount: params.totalPluginCount ?? this.state.pluginHydration.totalPluginCount
8157
+ };
8158
+ }
8159
+ markPluginHydrationReady(params) {
8160
+ this.state.pluginHydration = {
8161
+ ...this.state.pluginHydration,
8162
+ state: "ready",
8163
+ loadedPluginCount: params.loadedPluginCount,
8164
+ totalPluginCount: params.totalPluginCount,
8165
+ completedAt: now2(),
8166
+ error: void 0
8167
+ };
8168
+ }
8169
+ markPluginHydrationError(error) {
8170
+ this.state.phase = "error";
8171
+ this.state.lastError = error;
8172
+ this.state.pluginHydration = {
8173
+ ...this.state.pluginHydration,
8174
+ state: "error",
8175
+ completedAt: now2(),
8176
+ error
8177
+ };
8178
+ }
8179
+ markChannelsPending() {
8180
+ this.state.channels = {
8181
+ state: "pending",
8182
+ enabled: []
8183
+ };
8184
+ }
8185
+ markChannelsReady(enabled) {
8186
+ this.state.channels = {
8187
+ state: "ready",
8188
+ enabled: [...enabled]
8189
+ };
8190
+ if (this.state.pluginHydration.state === "ready") {
8191
+ this.state.phase = "ready";
8192
+ this.state.lastError = void 0;
8193
+ }
8194
+ }
8195
+ markChannelsError(error) {
8196
+ this.state.phase = "error";
8197
+ this.state.lastError = error;
8198
+ this.state.channels = {
8199
+ state: "error",
8200
+ enabled: [...this.state.channels.enabled],
8201
+ error
8202
+ };
8203
+ }
8204
+ setRemoteState(state, message) {
8205
+ this.state.remote = {
8206
+ state,
8207
+ ...message ? { message } : {}
8208
+ };
8209
+ }
8210
+ markReady() {
8211
+ this.state.phase = "ready";
8212
+ this.state.lastError = void 0;
8213
+ }
8214
+ markError(error) {
8215
+ this.state.phase = "error";
8216
+ this.state.lastError = error;
8217
+ }
8218
+ };
8219
+
8220
+ // src/cli/commands/service-capability-hydration.ts
8221
+ import { getWorkspacePath as getWorkspacePath10, loadConfig as loadConfig17, resolveConfigSecrets as resolveConfigSecrets3 } from "@nextclaw/core";
8222
+ import {
8223
+ getPluginChannelBindings as getPluginChannelBindings4,
8224
+ getPluginUiMetadataFromRegistry
8225
+ } from "@nextclaw/openclaw-compat";
8226
+
8227
+ // src/cli/commands/plugin-reload.ts
8228
+ function buildPluginChannelBindingSignature(binding) {
8229
+ return `${binding.pluginId}:${binding.channelId}`;
8230
+ }
8231
+ function buildSortedBindingSignatures(bindings) {
8232
+ return bindings.map(buildPluginChannelBindingSignature).sort();
8233
+ }
8234
+ function buildSortedExtensionChannelIds(channels2) {
8235
+ return channels2.map((registration) => registration.channel.id).filter((id) => typeof id === "string" && id.trim().length > 0).sort();
8236
+ }
8237
+ function areSortedStringListsEqual(left, right) {
8238
+ if (left.length !== right.length) {
8239
+ return false;
8240
+ }
8241
+ for (let index = 0; index < left.length; index += 1) {
8242
+ if (left[index] !== right[index]) {
8243
+ return false;
8244
+ }
8245
+ }
8246
+ return true;
8247
+ }
8248
+ function readPluginIdFromPluginsEntryPath(path2) {
8249
+ const prefix = "plugins.entries.";
8250
+ if (!path2.startsWith(prefix)) {
8251
+ return null;
8252
+ }
8253
+ const suffix = path2.slice(prefix.length);
8254
+ const [pluginId] = suffix.split(".");
8255
+ return pluginId?.trim() ? pluginId.trim() : null;
8256
+ }
8257
+ function shouldRestartChannelsForPluginReload(params) {
8258
+ const currentBindingSignatures = buildSortedBindingSignatures(params.currentPluginChannelBindings);
8259
+ const nextBindingSignatures = buildSortedBindingSignatures(params.nextPluginChannelBindings);
8260
+ if (!areSortedStringListsEqual(currentBindingSignatures, nextBindingSignatures)) {
8261
+ return true;
8262
+ }
8263
+ const currentExtensionChannelIds = buildSortedExtensionChannelIds(params.currentExtensionChannels);
8264
+ const nextExtensionChannelIds = buildSortedExtensionChannelIds(params.nextExtensionChannels);
8265
+ if (!areSortedStringListsEqual(currentExtensionChannelIds, nextExtensionChannelIds)) {
8266
+ return true;
8267
+ }
8268
+ const channelPluginIds = /* @__PURE__ */ new Set();
8269
+ for (const binding of params.currentPluginChannelBindings) {
8270
+ channelPluginIds.add(binding.pluginId);
8271
+ }
8272
+ for (const binding of params.nextPluginChannelBindings) {
8273
+ channelPluginIds.add(binding.pluginId);
8274
+ }
8275
+ for (const path2 of params.changedPaths) {
8276
+ const pluginId = readPluginIdFromPluginsEntryPath(path2);
8277
+ if (pluginId && channelPluginIds.has(pluginId)) {
8278
+ return true;
8279
+ }
8280
+ }
8281
+ return false;
8282
+ }
8283
+
8284
+ // src/cli/commands/service-ui-shell-grace.ts
8285
+ import { setTimeout as delay } from "timers/promises";
8286
+ var DEFAULT_UI_SHELL_GRACE_MS = 3e3;
8287
+ async function waitForUiShellGraceWindow(uiStartup) {
8288
+ if (!uiStartup) {
8289
+ return;
8290
+ }
8162
8291
  await measureStartupAsync("service.ui_shell_grace_window", async () => {
8163
8292
  await delay(DEFAULT_UI_SHELL_GRACE_MS);
8164
8293
  });
8165
8294
  }
8166
8295
 
8296
+ // src/cli/commands/service-capability-hydration.ts
8297
+ function countEnabledPlugins(config2, workspaceDir) {
8298
+ return discoverPluginRegistryStatus(config2, workspaceDir).plugins.filter((plugin) => plugin.enabled).length;
8299
+ }
8300
+ async function hydrateServiceCapabilities(params) {
8301
+ await waitForUiShellGraceWindow(params.uiStartup);
8302
+ const nextConfig = resolveConfigSecrets3(loadConfig17(), { configPath: params.gateway.runtimeConfigPath });
8303
+ const nextWorkspace = getWorkspacePath10(nextConfig.agents.defaults.workspace);
8304
+ const totalPluginCount = countEnabledPlugins(nextConfig, nextWorkspace);
8305
+ let loadedPluginCount = 0;
8306
+ params.bootstrapStatus.markPluginHydrationRunning({
8307
+ totalPluginCount
8308
+ });
8309
+ params.bootstrapStatus.markChannelsPending();
8310
+ try {
8311
+ const nextPluginRegistry = await loadPluginRegistryProgressively(nextConfig, nextWorkspace, {
8312
+ onPluginProcessed: ({ loadedPluginCount: nextCount }) => {
8313
+ loadedPluginCount = nextCount;
8314
+ params.bootstrapStatus.markPluginHydrationProgress({
8315
+ loadedPluginCount: nextCount,
8316
+ totalPluginCount
8317
+ });
8318
+ }
8319
+ });
8320
+ logPluginDiagnostics(nextPluginRegistry);
8321
+ const nextExtensionRegistry = toExtensionRegistry(nextPluginRegistry);
8322
+ const nextPluginChannelBindings = getPluginChannelBindings4(nextPluginRegistry);
8323
+ const nextPluginUiMetadata = getPluginUiMetadataFromRegistry(nextPluginRegistry);
8324
+ const shouldRebuildChannels = shouldRestartChannelsForPluginReload({
8325
+ changedPaths: [],
8326
+ currentPluginChannelBindings: params.state.pluginChannelBindings,
8327
+ nextPluginChannelBindings,
8328
+ currentExtensionChannels: params.state.extensionRegistry.channels,
8329
+ nextExtensionChannels: nextExtensionRegistry.channels
8330
+ });
8331
+ params.state.pluginRegistry = nextPluginRegistry;
8332
+ params.state.extensionRegistry = nextExtensionRegistry;
8333
+ params.state.pluginChannelBindings = nextPluginChannelBindings;
8334
+ params.state.pluginUiMetadata = nextPluginUiMetadata;
8335
+ params.gateway.runtimePool.applyExtensionRegistry(nextExtensionRegistry);
8336
+ params.gateway.runtimePool.applyRuntimeConfig(nextConfig);
8337
+ params.getLiveUiNcpAgent()?.applyExtensionRegistry?.(nextExtensionRegistry);
8338
+ if (shouldRebuildChannels) {
8339
+ await params.gateway.reloader.rebuildChannels(nextConfig, { start: false });
8340
+ }
8341
+ params.uiStartup?.publish({ type: "config.updated", payload: { path: "channels" } });
8342
+ params.uiStartup?.publish({ type: "config.updated", payload: { path: "plugins" } });
8343
+ params.bootstrapStatus.markPluginHydrationReady({
8344
+ loadedPluginCount: loadedPluginCount || totalPluginCount,
8345
+ totalPluginCount
8346
+ });
8347
+ } catch (error) {
8348
+ const message = error instanceof Error ? error.message : String(error);
8349
+ params.bootstrapStatus.markPluginHydrationError(message);
8350
+ throw error;
8351
+ }
8352
+ }
8353
+
8354
+ // src/cli/commands/service-plugin-runtime-bridge.ts
8355
+ import { loadConfig as loadConfig18, resolveConfigSecrets as resolveConfigSecrets4, saveConfig as saveConfig10 } from "@nextclaw/core";
8356
+ import { setPluginRuntimeBridge } from "@nextclaw/openclaw-compat";
8357
+ function installPluginRuntimeBridge(params) {
8358
+ const { runtimePool, runtimeConfigPath, getPluginChannelBindings: getPluginChannelBindings7 } = params;
8359
+ setPluginRuntimeBridge({
8360
+ loadConfig: () => toPluginConfigView(resolveConfigSecrets4(loadConfig18(), { configPath: runtimeConfigPath }), getPluginChannelBindings7()),
8361
+ writeConfigFile: async (nextConfigView) => {
8362
+ if (!nextConfigView || typeof nextConfigView !== "object" || Array.isArray(nextConfigView)) {
8363
+ throw new Error("plugin runtime writeConfigFile expects an object config");
8364
+ }
8365
+ const current = loadConfig18();
8366
+ const next = mergePluginConfigView(current, nextConfigView, getPluginChannelBindings7());
8367
+ saveConfig10(next);
8368
+ },
8369
+ dispatchReplyWithBufferedBlockDispatcher: async ({ ctx, dispatcherOptions }) => {
8370
+ const request = resolvePluginRuntimeRequest(ctx);
8371
+ if (!request) {
8372
+ return;
8373
+ }
8374
+ try {
8375
+ await dispatcherOptions.onReplyStart?.();
8376
+ const response = await runtimePool.processDirect(request);
8377
+ const replyText = typeof response === "string" ? response : String(response ?? "");
8378
+ if (replyText.trim()) {
8379
+ await dispatcherOptions.deliver({ text: replyText }, { kind: "final" });
8380
+ }
8381
+ } catch (error) {
8382
+ dispatcherOptions.onError?.(error);
8383
+ throw error;
8384
+ }
8385
+ }
8386
+ });
8387
+ }
8388
+ function resolvePluginRuntimeRequest(ctx) {
8389
+ const bodyForAgent = typeof ctx.BodyForAgent === "string" ? ctx.BodyForAgent : "";
8390
+ const body = typeof ctx.Body === "string" ? ctx.Body : "";
8391
+ const content = (bodyForAgent || body).trim();
8392
+ const attachments = resolvePluginRuntimeAttachments(ctx);
8393
+ if (!content && attachments.length === 0) {
8394
+ return null;
8395
+ }
8396
+ const sessionKey = typeof ctx.SessionKey === "string" && ctx.SessionKey.trim().length > 0 ? ctx.SessionKey : void 0;
8397
+ const channel = typeof ctx.OriginatingChannel === "string" && ctx.OriginatingChannel.trim().length > 0 ? ctx.OriginatingChannel : "cli";
8398
+ const chatId = typeof ctx.OriginatingTo === "string" && ctx.OriginatingTo.trim().length > 0 ? ctx.OriginatingTo : typeof ctx.SenderId === "string" && ctx.SenderId.trim().length > 0 ? ctx.SenderId : "direct";
8399
+ const agentId = typeof ctx.AgentId === "string" ? ctx.AgentId : void 0;
8400
+ const modelOverride = resolveModelOverride(ctx);
8401
+ const accountId = typeof ctx.AccountId === "string" && ctx.AccountId.trim().length > 0 ? ctx.AccountId : void 0;
8402
+ return {
8403
+ content,
8404
+ sessionKey,
8405
+ channel,
8406
+ chatId,
8407
+ agentId,
8408
+ attachments,
8409
+ metadata: {
8410
+ ...accountId ? { account_id: accountId } : {},
8411
+ ...modelOverride ? { model: modelOverride } : {}
8412
+ }
8413
+ };
8414
+ }
8415
+ function resolveModelOverride(ctx) {
8416
+ if (typeof ctx.Model === "string" && ctx.Model.trim().length > 0) {
8417
+ return ctx.Model.trim();
8418
+ }
8419
+ if (typeof ctx.AgentModel === "string" && ctx.AgentModel.trim().length > 0) {
8420
+ return ctx.AgentModel.trim();
8421
+ }
8422
+ return void 0;
8423
+ }
8424
+ function readStringList(value) {
8425
+ if (!Array.isArray(value)) {
8426
+ return [];
8427
+ }
8428
+ return value.map((entry) => typeof entry === "string" ? entry.trim() : "").filter(Boolean);
8429
+ }
8430
+ function readOptionalString2(value) {
8431
+ if (typeof value !== "string") {
8432
+ return void 0;
8433
+ }
8434
+ const trimmed = value.trim();
8435
+ return trimmed || void 0;
8436
+ }
8437
+ function resolvePluginRuntimeAttachments(ctx) {
8438
+ const mediaPaths = readStringList(ctx.MediaPaths);
8439
+ const mediaUrls = readStringList(ctx.MediaUrls);
8440
+ const fallbackPath = readOptionalString2(ctx.MediaPath);
8441
+ const fallbackUrl = readOptionalString2(ctx.MediaUrl);
8442
+ const mediaTypes = readStringList(ctx.MediaTypes);
8443
+ const fallbackType = readOptionalString2(ctx.MediaType);
8444
+ const entryCount = Math.max(
8445
+ mediaPaths.length,
8446
+ mediaUrls.length,
8447
+ fallbackPath ? 1 : 0,
8448
+ fallbackUrl ? 1 : 0
8449
+ );
8450
+ const attachments = [];
8451
+ for (let index = 0; index < entryCount; index += 1) {
8452
+ const path2 = mediaPaths[index] ?? (index === 0 ? fallbackPath : void 0);
8453
+ const rawUrl = mediaUrls[index] ?? (index === 0 ? fallbackUrl : void 0);
8454
+ const url = rawUrl && rawUrl !== path2 ? rawUrl : void 0;
8455
+ const mimeType = mediaTypes[index] ?? fallbackType;
8456
+ if (!path2 && !url) {
8457
+ continue;
8458
+ }
8459
+ attachments.push({
8460
+ path: path2,
8461
+ url,
8462
+ mimeType,
8463
+ source: "plugin-runtime",
8464
+ status: path2 ? "ready" : "remote-only"
8465
+ });
8466
+ }
8467
+ return attachments;
8468
+ }
8469
+
8470
+ // src/cli/commands/service-plugin-reload.ts
8471
+ import { getWorkspacePath as getWorkspacePath11 } from "@nextclaw/core";
8472
+ import {
8473
+ getPluginChannelBindings as getPluginChannelBindings5,
8474
+ startPluginChannelGateways,
8475
+ stopPluginChannelGateways
8476
+ } from "@nextclaw/openclaw-compat";
8477
+ async function reloadServicePlugins(params) {
8478
+ const nextWorkspace = getWorkspacePath11(params.nextConfig.agents.defaults.workspace);
8479
+ const nextPluginRegistry = loadPluginRegistry(params.nextConfig, nextWorkspace);
8480
+ const nextExtensionRegistry = toExtensionRegistry(nextPluginRegistry);
8481
+ const nextPluginChannelBindings = getPluginChannelBindings5(nextPluginRegistry);
8482
+ const shouldRestartChannels = shouldRestartChannelsForPluginReload({
8483
+ changedPaths: params.changedPaths,
8484
+ currentPluginChannelBindings: params.pluginChannelBindings,
8485
+ nextPluginChannelBindings,
8486
+ currentExtensionChannels: params.extensionRegistry.channels,
8487
+ nextExtensionChannels: nextExtensionRegistry.channels
8488
+ });
8489
+ logPluginDiagnostics(nextPluginRegistry);
8490
+ let pluginGatewayHandles = params.pluginGatewayHandles;
8491
+ if (shouldRestartChannels) {
8492
+ await stopPluginChannelGateways(pluginGatewayHandles);
8493
+ const startedPluginGateways = await startPluginChannelGateways({
8494
+ registry: nextPluginRegistry,
8495
+ config: params.nextConfig,
8496
+ logger: params.pluginGatewayLogger
8497
+ });
8498
+ pluginGatewayHandles = startedPluginGateways.handles;
8499
+ params.logPluginGatewayDiagnostics(startedPluginGateways.diagnostics);
8500
+ }
8501
+ return {
8502
+ pluginRegistry: nextPluginRegistry,
8503
+ extensionRegistry: nextExtensionRegistry,
8504
+ pluginChannelBindings: nextPluginChannelBindings,
8505
+ pluginGatewayHandles,
8506
+ restartChannels: shouldRestartChannels
8507
+ };
8508
+ }
8509
+
8510
+ // src/cli/commands/service-gateway-bootstrap.ts
8511
+ var { loadConfig: loadConfig19, resolveConfigSecrets: resolveConfigSecrets5 } = NextclawCore2;
8512
+ function createBootstrapStatus(remoteEnabled) {
8513
+ const bootstrapStatus = new ServiceBootstrapStatusStore();
8514
+ bootstrapStatus.markPluginHydrationPending();
8515
+ bootstrapStatus.markChannelsPending();
8516
+ bootstrapStatus.setRemoteState(remoteEnabled ? "pending" : "disabled");
8517
+ return bootstrapStatus;
8518
+ }
8519
+ function createGatewayRuntimeState(gateway) {
8520
+ return {
8521
+ pluginRegistry: gateway.pluginRegistry,
8522
+ extensionRegistry: gateway.extensionRegistry,
8523
+ pluginChannelBindings: gateway.pluginChannelBindings,
8524
+ pluginUiMetadata: getPluginUiMetadataFromRegistry2(gateway.pluginRegistry),
8525
+ pluginGatewayHandles: []
8526
+ };
8527
+ }
8528
+ function configureGatewayPluginRuntime(params) {
8529
+ params.gateway.reloader.setApplyAgentRuntimeConfig((nextConfig) => params.gateway.runtimePool.applyRuntimeConfig(nextConfig));
8530
+ params.gateway.reloader.setReloadPlugins(async ({ config: nextConfig, changedPaths }) => {
8531
+ const result = await reloadServicePlugins({
8532
+ nextConfig,
8533
+ changedPaths,
8534
+ pluginRegistry: params.state.pluginRegistry,
8535
+ extensionRegistry: params.state.extensionRegistry,
8536
+ pluginChannelBindings: params.state.pluginChannelBindings,
8537
+ pluginGatewayHandles: params.state.pluginGatewayHandles,
8538
+ pluginGatewayLogger,
8539
+ logPluginGatewayDiagnostics
8540
+ });
8541
+ params.state.pluginRegistry = result.pluginRegistry;
8542
+ params.state.extensionRegistry = result.extensionRegistry;
8543
+ params.state.pluginChannelBindings = result.pluginChannelBindings;
8544
+ params.state.pluginUiMetadata = getPluginUiMetadataFromRegistry2(result.pluginRegistry);
8545
+ params.state.pluginGatewayHandles = result.pluginGatewayHandles;
8546
+ params.gateway.runtimePool.applyExtensionRegistry(result.extensionRegistry);
8547
+ params.getLiveUiNcpAgent()?.applyExtensionRegistry?.(result.extensionRegistry);
8548
+ params.gateway.runtimePool.applyRuntimeConfig(nextConfig);
8549
+ if (result.restartChannels) {
8550
+ console.log("Config reload: plugin channel gateways restarted.");
8551
+ }
8552
+ return { restartChannels: result.restartChannels };
8553
+ });
8554
+ params.gateway.reloader.setReloadMcp(async ({ config: nextConfig }) => {
8555
+ await params.getLiveUiNcpAgent()?.applyMcpConfig?.(nextConfig);
8556
+ });
8557
+ installPluginRuntimeBridge({
8558
+ runtimePool: params.gateway.runtimePool,
8559
+ runtimeConfigPath: params.gateway.runtimeConfigPath,
8560
+ getPluginChannelBindings: () => params.state.pluginChannelBindings
8561
+ });
8562
+ }
8563
+ function createDeferredGatewayStartupHooks(params) {
8564
+ return {
8565
+ hydrateCapabilities: async () => {
8566
+ await hydrateServiceCapabilities({
8567
+ uiStartup: params.uiStartup,
8568
+ gateway: params.gateway,
8569
+ state: params.state,
8570
+ bootstrapStatus: params.bootstrapStatus,
8571
+ getLiveUiNcpAgent: params.getLiveUiNcpAgent
8572
+ });
8573
+ },
8574
+ startPluginGateways: async () => {
8575
+ const startedPluginGateways = await startPluginChannelGateways2({
8576
+ registry: params.state.pluginRegistry,
8577
+ config: resolveConfigSecrets5(loadConfig19(), { configPath: params.gateway.runtimeConfigPath }),
8578
+ logger: pluginGatewayLogger
8579
+ });
8580
+ params.state.pluginGatewayHandles = startedPluginGateways.handles;
8581
+ logPluginGatewayDiagnostics(startedPluginGateways.diagnostics);
8582
+ },
8583
+ startChannels: async () => {
8584
+ await params.gateway.reloader.getChannels().startAll();
8585
+ const enabledChannels = params.gateway.reloader.getChannels().enabledChannels;
8586
+ if (enabledChannels.length > 0) {
8587
+ console.log(`\u2713 Channels enabled: ${enabledChannels.join(", ")}`);
8588
+ } else {
8589
+ console.log("Warning: No channels enabled");
8590
+ }
8591
+ params.bootstrapStatus.markChannelsReady(enabledChannels);
8592
+ params.bootstrapStatus.markReady();
8593
+ },
8594
+ wakeFromRestartSentinel: params.wakeFromRestartSentinel,
8595
+ onNcpAgentReady: (ncpAgent) => {
8596
+ params.setLiveUiNcpAgent(ncpAgent);
8597
+ }
8598
+ };
8599
+ }
8600
+
8167
8601
  // src/cli/commands/service.ts
8168
8602
  var {
8169
8603
  APP_NAME: APP_NAME3,
@@ -8171,16 +8605,16 @@ var {
8171
8605
  getConfigPath: getConfigPath10,
8172
8606
  getProvider,
8173
8607
  getProviderName,
8174
- getWorkspacePath: getWorkspacePath11,
8608
+ getWorkspacePath: getWorkspacePath12,
8175
8609
  LiteLLMProvider,
8176
- loadConfig: loadConfig18,
8610
+ loadConfig: loadConfig20,
8177
8611
  MessageBus: MessageBus2,
8178
- resolveConfigSecrets: resolveConfigSecrets4,
8612
+ resolveConfigSecrets: resolveConfigSecrets6,
8179
8613
  SessionManager: SessionManager2,
8180
8614
  parseAgentScopedSessionKey: parseAgentScopedSessionKey2
8181
- } = NextclawCore2;
8615
+ } = NextclawCore3;
8182
8616
  function createSkillsLoader(workspace) {
8183
- const ctor = NextclawCore2.SkillsLoader;
8617
+ const ctor = NextclawCore3.SkillsLoader;
8184
8618
  if (!ctor) {
8185
8619
  return null;
8186
8620
  }
@@ -8203,9 +8637,9 @@ var ServiceCommands = class {
8203
8637
  const applyLiveConfigReload = async () => {
8204
8638
  await this.applyLiveConfigReload?.();
8205
8639
  };
8206
- let pluginChannelBindings = [];
8207
- let pluginUiMetadata = [];
8208
- let pluginGatewayHandles = [];
8640
+ let runtimeState = null;
8641
+ const bootstrapStatus = createBootstrapStatus(shellContext.config.remote.enabled);
8642
+ const ncpSessionService = new UiSessionService(shellContext.sessionManager);
8209
8643
  const marketplaceInstaller = new ServiceMarketplaceInstaller({
8210
8644
  applyLiveConfigReload,
8211
8645
  runCliSubcommand: (args) => this.runCliSubcommand(args),
@@ -8223,18 +8657,20 @@ var ServiceCommands = class {
8223
8657
  uiConfig: shellContext.uiConfig,
8224
8658
  uiStaticDir: shellContext.uiStaticDir,
8225
8659
  cronService: shellContext.cron,
8226
- getConfig: () => resolveConfigSecrets4(loadConfig18(), { configPath: shellContext.runtimeConfigPath }),
8660
+ getConfig: () => resolveConfigSecrets6(loadConfig20(), { configPath: shellContext.runtimeConfigPath }),
8227
8661
  configPath: getConfigPath10(),
8228
8662
  productVersion: getPackageVersion(),
8229
- getPluginChannelBindings: () => pluginChannelBindings,
8230
- getPluginUiMetadata: () => pluginUiMetadata,
8663
+ getPluginChannelBindings: () => runtimeState?.pluginChannelBindings ?? [],
8664
+ getPluginUiMetadata: () => runtimeState?.pluginUiMetadata ?? [],
8231
8665
  marketplace: { apiBaseUrl: process.env.NEXTCLAW_MARKETPLACE_API_BASE, installer: marketplaceInstaller },
8232
8666
  remoteAccess,
8667
+ getBootstrapStatus: () => bootstrapStatus.getStatus(),
8233
8668
  openBrowserWindow: shellContext.uiConfig.open,
8234
- applyLiveConfigReload
8669
+ applyLiveConfigReload,
8670
+ ncpSessionService
8235
8671
  })
8236
8672
  );
8237
- await waitForUiShellGraceWindow(uiStartup);
8673
+ bootstrapStatus.markShellReady();
8238
8674
  await waitForNextTick();
8239
8675
  const gateway = measureStartupSync(
8240
8676
  "service.create_gateway_startup_context",
@@ -8243,51 +8679,23 @@ var ServiceCommands = class {
8243
8679
  uiOverrides: options.uiOverrides,
8244
8680
  allowMissingProvider: options.allowMissingProvider,
8245
8681
  uiStaticDir: options.uiStaticDir,
8682
+ initialPluginRegistry: createEmptyPluginRegistry(),
8246
8683
  makeProvider: (config2, providerOptions) => providerOptions?.allowMissing === true ? this.makeProvider(config2, { allowMissing: true }) : this.makeProvider(config2),
8247
8684
  makeMissingProvider: (config2) => this.makeMissingProvider(config2),
8248
8685
  requestRestart: (params) => this.deps.requestRestart(params)
8249
8686
  })
8250
8687
  );
8251
8688
  this.applyLiveConfigReload = gateway.applyLiveConfigReload;
8252
- let { pluginRegistry, extensionRegistry } = gateway;
8253
- pluginChannelBindings = gateway.pluginChannelBindings;
8254
- pluginUiMetadata = getPluginUiMetadataFromRegistry(pluginRegistry);
8689
+ const gatewayRuntimeState = createGatewayRuntimeState(gateway);
8690
+ runtimeState = gatewayRuntimeState;
8255
8691
  uiStartup?.publish({ type: "config.updated", payload: { path: "channels" } });
8256
8692
  uiStartup?.publish({ type: "config.updated", payload: { path: "plugins" } });
8257
- gateway.reloader.setApplyAgentRuntimeConfig((nextConfig) => gateway.runtimePool.applyRuntimeConfig(nextConfig));
8258
- gateway.reloader.setReloadPlugins(async ({ config: nextConfig, changedPaths }) => {
8259
- const result = await reloadServicePlugins({
8260
- nextConfig,
8261
- changedPaths,
8262
- pluginRegistry,
8263
- extensionRegistry,
8264
- pluginChannelBindings,
8265
- pluginGatewayHandles,
8266
- pluginGatewayLogger,
8267
- logPluginGatewayDiagnostics
8268
- });
8269
- pluginRegistry = result.pluginRegistry;
8270
- extensionRegistry = result.extensionRegistry;
8271
- pluginChannelBindings = result.pluginChannelBindings;
8272
- pluginGatewayHandles = result.pluginGatewayHandles;
8273
- gateway.runtimePool.applyExtensionRegistry(result.extensionRegistry);
8274
- this.liveUiNcpAgent?.applyExtensionRegistry?.(result.extensionRegistry);
8275
- gateway.runtimePool.applyRuntimeConfig(nextConfig);
8276
- if (result.restartChannels) {
8277
- console.log("Config reload: plugin channel gateways restarted.");
8278
- }
8279
- return { restartChannels: result.restartChannels };
8280
- });
8281
- gateway.reloader.setReloadMcp(async ({ config: nextConfig }) => {
8282
- await this.liveUiNcpAgent?.applyMcpConfig?.(nextConfig);
8283
- });
8284
- installPluginRuntimeBridge({
8285
- runtimePool: gateway.runtimePool,
8286
- runtimeConfigPath: gateway.runtimeConfigPath,
8287
- pluginChannelBindings
8693
+ configureGatewayPluginRuntime({
8694
+ gateway,
8695
+ state: gatewayRuntimeState,
8696
+ getLiveUiNcpAgent: () => this.liveUiNcpAgent
8288
8697
  });
8289
- if (gateway.reloader.getChannels().enabledChannels.length) console.log(`\u2713 Channels enabled: ${gateway.reloader.getChannels().enabledChannels.join(", ")}`);
8290
- else console.log("Warning: No channels enabled");
8698
+ console.log("\u2713 Capability hydration: scheduled in background");
8291
8699
  await measureStartupAsync(
8292
8700
  "service.start_gateway_support_services",
8293
8701
  async () => await startGatewaySupportServices({
@@ -8298,6 +8706,19 @@ var ServiceCommands = class {
8298
8706
  startHeartbeat: () => gateway.heartbeat.start()
8299
8707
  })
8300
8708
  );
8709
+ const deferredGatewayStartupHooks = createDeferredGatewayStartupHooks({
8710
+ uiStartup,
8711
+ gateway,
8712
+ state: gatewayRuntimeState,
8713
+ bootstrapStatus,
8714
+ getLiveUiNcpAgent: () => this.liveUiNcpAgent,
8715
+ setLiveUiNcpAgent: (ncpAgent) => {
8716
+ this.liveUiNcpAgent = ncpAgent;
8717
+ },
8718
+ wakeFromRestartSentinel: async () => {
8719
+ await this.wakeFromRestartSentinel({ bus: gateway.bus, sessionManager: gateway.sessionManager });
8720
+ }
8721
+ });
8301
8722
  logStartupTrace("service.start_gateway.runtime_loop_begin");
8302
8723
  await runGatewayRuntimeLoop({
8303
8724
  runtimePool: gateway.runtimePool,
@@ -8308,34 +8729,26 @@ var ServiceCommands = class {
8308
8729
  providerManager: gateway.providerManager,
8309
8730
  cronService: gateway.cron,
8310
8731
  gatewayController: gateway.gatewayController,
8311
- getConfig: () => resolveConfigSecrets4(loadConfig18(), { configPath: gateway.runtimeConfigPath }),
8312
- getExtensionRegistry: () => extensionRegistry,
8732
+ getConfig: () => resolveConfigSecrets6(loadConfig20(), { configPath: gateway.runtimeConfigPath }),
8733
+ getExtensionRegistry: () => gatewayRuntimeState.extensionRegistry,
8313
8734
  resolveMessageToolHints: ({ channel, accountId }) => resolvePluginChannelMessageToolHints2({
8314
- registry: pluginRegistry,
8735
+ registry: gatewayRuntimeState.pluginRegistry,
8315
8736
  channel,
8316
- cfg: resolveConfigSecrets4(loadConfig18(), { configPath: gateway.runtimeConfigPath }),
8737
+ cfg: resolveConfigSecrets6(loadConfig20(), { configPath: gateway.runtimeConfigPath }),
8317
8738
  accountId
8318
8739
  }),
8319
- startPluginGateways: async () => {
8320
- const startedPluginGateways = await startPluginChannelGateways2({
8321
- registry: pluginRegistry,
8322
- config: gateway.config,
8323
- logger: pluginGatewayLogger
8324
- });
8325
- pluginGatewayHandles = startedPluginGateways.handles;
8326
- logPluginGatewayDiagnostics(startedPluginGateways.diagnostics);
8327
- },
8328
- startChannels: async () => {
8329
- await gateway.reloader.getChannels().startAll();
8330
- },
8331
- wakeFromRestartSentinel: async () => {
8332
- await this.wakeFromRestartSentinel({ bus: gateway.bus, sessionManager: gateway.sessionManager });
8333
- },
8334
- onNcpAgentReady: (ncpAgent) => {
8335
- this.liveUiNcpAgent = ncpAgent;
8336
- }
8740
+ hydrateCapabilities: deferredGatewayStartupHooks.hydrateCapabilities,
8741
+ startPluginGateways: deferredGatewayStartupHooks.startPluginGateways,
8742
+ startChannels: deferredGatewayStartupHooks.startChannels,
8743
+ wakeFromRestartSentinel: deferredGatewayStartupHooks.wakeFromRestartSentinel,
8744
+ onNcpAgentReady: deferredGatewayStartupHooks.onNcpAgentReady
8337
8745
  }),
8338
8746
  onDeferredStartupError: (error) => {
8747
+ const message = error instanceof Error ? error.message : String(error);
8748
+ bootstrapStatus.markError(message);
8749
+ if (bootstrapStatus.getStatus().pluginHydration.state === "running") {
8750
+ bootstrapStatus.markPluginHydrationError(message);
8751
+ }
8339
8752
  console.error(`Deferred startup failed: ${error instanceof Error ? error.message : String(error)}`);
8340
8753
  },
8341
8754
  cleanup: async () => {
@@ -8343,7 +8756,7 @@ var ServiceCommands = class {
8343
8756
  this.liveUiNcpAgent = null;
8344
8757
  await uiStartup?.deferredNcpAgent.close();
8345
8758
  await gateway.remoteModule?.stop();
8346
- await stopPluginChannelGateways2(pluginGatewayHandles);
8759
+ await stopPluginChannelGateways2(runtimeState?.pluginGatewayHandles ?? []);
8347
8760
  setPluginRuntimeBridge2(null);
8348
8761
  }
8349
8762
  });
@@ -8478,7 +8891,7 @@ var ServiceCommands = class {
8478
8891
  });
8479
8892
  }
8480
8893
  async runForeground(options) {
8481
- const config2 = loadConfig18();
8894
+ const config2 = loadConfig20();
8482
8895
  const uiConfig = resolveUiConfig(config2, options.uiOverrides);
8483
8896
  const uiUrl = resolveUiApiBase(uiConfig.host, uiConfig.port);
8484
8897
  if (options.open) {
@@ -8491,7 +8904,7 @@ var ServiceCommands = class {
8491
8904
  });
8492
8905
  }
8493
8906
  async startService(options) {
8494
- const config2 = loadConfig18();
8907
+ const config2 = loadConfig20();
8495
8908
  const uiConfig = resolveUiConfig(config2, options.uiOverrides);
8496
8909
  const uiUrl = resolveUiApiBase(uiConfig.host, uiConfig.port);
8497
8910
  const apiUrl = `${uiUrl}/api`;
@@ -8940,7 +9353,7 @@ var ServiceCommands = class {
8940
9353
  console.log(` - If you need to stop the service, run: ${APP_NAME3} stop`);
8941
9354
  }
8942
9355
  installBuiltinMarketplaceSkill(slug, force) {
8943
- const workspace = getWorkspacePath11(loadConfig18().agents.defaults.workspace);
9356
+ const workspace = getWorkspacePath12(loadConfig20().agents.defaults.workspace);
8944
9357
  const destination = join7(workspace, "skills", slug);
8945
9358
  const destinationSkillFile = join7(destination, "SKILL.md");
8946
9359
  if (existsSync11(destinationSkillFile) && !force) {
@@ -9204,7 +9617,7 @@ function resolveSkillsInstallWorkdir(params) {
9204
9617
  if (params.explicitWorkdir) {
9205
9618
  return expandHome2(params.explicitWorkdir);
9206
9619
  }
9207
- return getWorkspacePath12(params.configuredWorkspace);
9620
+ return getWorkspacePath13(params.configuredWorkspace);
9208
9621
  }
9209
9622
  var CliRuntime = class {
9210
9623
  logo;
@@ -9449,7 +9862,7 @@ var CliRuntime = class {
9449
9862
  const force = Boolean(options.force);
9450
9863
  const configPath = getConfigPath11();
9451
9864
  const createdConfig = initializeConfigIfMissing(configPath);
9452
- const config2 = loadConfig19();
9865
+ const config2 = loadConfig21();
9453
9866
  const workspaceSetting = config2.agents.defaults.workspace;
9454
9867
  const workspacePath = !workspaceSetting || workspaceSetting === DEFAULT_WORKSPACE_PATH ? join9(getDataDir10(), DEFAULT_WORKSPACE_DIR) : expandHome2(workspaceSetting);
9455
9868
  const workspaceExisted = existsSync13(workspacePath);
@@ -9576,15 +9989,15 @@ ${this.logo} ${APP_NAME5} is ready! (${source})`);
9576
9989
  }
9577
9990
  async agent(opts) {
9578
9991
  const configPath = getConfigPath11();
9579
- const config2 = resolveConfigSecrets5(loadConfig19(), { configPath });
9580
- const workspace = getWorkspacePath12(config2.agents.defaults.workspace);
9992
+ const config2 = resolveConfigSecrets7(loadConfig21(), { configPath });
9993
+ const workspace = getWorkspacePath13(config2.agents.defaults.workspace);
9581
9994
  const pluginRegistry = loadPluginRegistry(config2, workspace);
9582
9995
  const extensionRegistry = toExtensionRegistry(pluginRegistry);
9583
9996
  logPluginDiagnostics(pluginRegistry);
9584
- const pluginChannelBindings = getPluginChannelBindings5(pluginRegistry);
9997
+ const pluginChannelBindings = getPluginChannelBindings6(pluginRegistry);
9585
9998
  setPluginRuntimeBridge3({
9586
9999
  loadConfig: () => toPluginConfigView(
9587
- resolveConfigSecrets5(loadConfig19(), { configPath }),
10000
+ resolveConfigSecrets7(loadConfig21(), { configPath }),
9588
10001
  pluginChannelBindings
9589
10002
  ),
9590
10003
  writeConfigFile: async (nextConfigView) => {
@@ -9593,7 +10006,7 @@ ${this.logo} ${APP_NAME5} is ready! (${source})`);
9593
10006
  "plugin runtime writeConfigFile expects an object config"
9594
10007
  );
9595
10008
  }
9596
- const current = loadConfig19();
10009
+ const current = loadConfig21();
9597
10010
  const next = mergePluginConfigView(
9598
10011
  current,
9599
10012
  nextConfigView,
@@ -9625,7 +10038,7 @@ ${this.logo} ${APP_NAME5} is ready! (${source})`);
9625
10038
  resolveMessageToolHints: ({ channel, accountId }) => resolvePluginChannelMessageToolHints3({
9626
10039
  registry: pluginRegistry,
9627
10040
  channel,
9628
- cfg: resolveConfigSecrets5(loadConfig19(), { configPath }),
10041
+ cfg: resolveConfigSecrets7(loadConfig21(), { configPath }),
9629
10042
  accountId
9630
10043
  })
9631
10044
  });
@@ -9820,7 +10233,7 @@ ${this.logo} ${APP_NAME5} is ready! (${source})`);
9820
10233
  await this.diagnosticsCommands.doctor(opts);
9821
10234
  }
9822
10235
  async skillsInstall(options) {
9823
- const config2 = loadConfig19();
10236
+ const config2 = loadConfig21();
9824
10237
  const workdir = resolveSkillsInstallWorkdir({
9825
10238
  explicitWorkdir: options.workdir,
9826
10239
  configuredWorkspace: config2.agents.defaults.workspace