@botcord/daemon 0.2.77 → 0.2.78

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.
@@ -22,6 +22,12 @@ export interface DiscoveredAgentCredential {
22
22
  * in that case.
23
23
  */
24
24
  runtime?: string;
25
+ /** Runtime model id/alias selected for this agent. */
26
+ runtimeModel?: string;
27
+ /** Runtime reasoning effort selected for this agent. */
28
+ reasoningEffort?: string;
29
+ /** Kimi-style thinking toggle selected for this agent. */
30
+ thinking?: boolean;
25
31
  /** Working directory cached alongside `runtime`. */
26
32
  cwd?: string;
27
33
  /** OpenClaw gateway profile name from credentials (only meaningful for openclaw-acp). */
@@ -96,6 +96,12 @@ export function discoverAgentCredentials(opts = {}) {
96
96
  entry.displayName = creds.displayName;
97
97
  if (creds.runtime)
98
98
  entry.runtime = creds.runtime;
99
+ if (creds.runtimeModel)
100
+ entry.runtimeModel = creds.runtimeModel;
101
+ if (creds.reasoningEffort)
102
+ entry.reasoningEffort = creds.reasoningEffort;
103
+ if (typeof creds.thinking === "boolean")
104
+ entry.thinking = creds.thinking;
99
105
  if (creds.cwd)
100
106
  entry.cwd = creds.cwd;
101
107
  if (creds.openclawGateway)
@@ -3,6 +3,12 @@ import type { DaemonConfig, OpenclawGatewayProfile } from "./config.js";
3
3
  /** Per-agent metadata cached from credentials, used by `buildManagedRoutes`. */
4
4
  export interface AgentRuntimeMeta {
5
5
  runtime?: string;
6
+ /** Runtime model id/alias selected for this agent. */
7
+ runtimeModel?: string;
8
+ /** Runtime reasoning effort selected for this agent. */
9
+ reasoningEffort?: string;
10
+ /** Kimi-style thinking toggle selected for this agent. */
11
+ thinking?: boolean;
6
12
  cwd?: string;
7
13
  /** OpenClaw gateway profile name to lookup in the registry. */
8
14
  openclawGateway?: string;
@@ -4,6 +4,7 @@ import path from "node:path";
4
4
  import { resolveAgentIds } from "./config.js";
5
5
  import { agentWorkspaceDir } from "./agent-workspace.js";
6
6
  import { log as daemonLog } from "./log.js";
7
+ import { buildRuntimeSelectionExtraArgs, mergeRuntimeExtraArgs, } from "./runtime-route-options.js";
7
8
  function expandHome(p) {
8
9
  if (p === "~")
9
10
  return homedir();
@@ -259,10 +260,10 @@ export function buildManagedRoutes(agentIds, agentRuntimes, defaultRoute, opencl
259
260
  match: { accountId: agentId },
260
261
  runtime,
261
262
  cwd: meta.cwd || agentWorkspaceDir(agentId),
262
- // Inherit defaultRoute's extraArgs so synthesized per-agent routes
263
- // pick up operator-wide flags (e.g. `--permission-mode bypassPermissions`)
264
- // that would otherwise apply only to agents listed in `cfg.routes[]`.
265
- ...(defaultRoute.extraArgs ? { extraArgs: defaultRoute.extraArgs.slice() } : {}),
263
+ ...(() => {
264
+ const extraArgs = mergeRuntimeExtraArgs(defaultRoute.extraArgs, buildRuntimeSelectionExtraArgs(runtime, meta));
265
+ return extraArgs ? { extraArgs } : {};
266
+ })(),
266
267
  };
267
268
  if (runtime === "openclaw-acp") {
268
269
  // Per RFC §3.4: prefer credentials, fall back to defaultRoute.gateway.
package/dist/daemon.d.ts CHANGED
@@ -121,6 +121,9 @@ export interface BootBackfillResult {
121
121
  credentialPathByAgentId: Map<string, string>;
122
122
  agentRuntimes: Record<string, {
123
123
  runtime?: string;
124
+ runtimeModel?: string;
125
+ reasoningEffort?: string;
126
+ thinking?: boolean;
124
127
  cwd?: string;
125
128
  openclawGateway?: string;
126
129
  openclawAgent?: string;
package/dist/daemon.js CHANGED
@@ -527,9 +527,19 @@ export function backfillBootAgents(agents, opts) {
527
527
  for (const a of agents) {
528
528
  if (a.credentialsFile)
529
529
  credentialPathByAgentId.set(a.agentId, a.credentialsFile);
530
- if (a.runtime || a.cwd || a.openclawGateway || a.openclawAgent || a.hermesProfile) {
530
+ if (a.runtime ||
531
+ a.runtimeModel ||
532
+ a.reasoningEffort ||
533
+ typeof a.thinking === "boolean" ||
534
+ a.cwd ||
535
+ a.openclawGateway ||
536
+ a.openclawAgent ||
537
+ a.hermesProfile) {
531
538
  agentRuntimes[a.agentId] = {
532
539
  ...(a.runtime ? { runtime: a.runtime } : {}),
540
+ ...(a.runtimeModel ? { runtimeModel: a.runtimeModel } : {}),
541
+ ...(a.reasoningEffort ? { reasoningEffort: a.reasoningEffort } : {}),
542
+ ...(typeof a.thinking === "boolean" ? { thinking: a.thinking } : {}),
533
543
  ...(a.cwd ? { cwd: a.cwd } : {}),
534
544
  ...(a.openclawGateway ? { openclawGateway: a.openclawGateway } : {}),
535
545
  ...(a.openclawAgent ? { openclawAgent: a.openclawAgent } : {}),
@@ -202,6 +202,11 @@ export class DeepseekTuiAdapter {
202
202
  auto_approve: opts.trustLevel !== "public",
203
203
  archived: false,
204
204
  };
205
+ const selection = parseDeepseekRuntimeSelection(opts.extraArgs);
206
+ if (selection.model)
207
+ body.model = selection.model;
208
+ if (selection.reasoningEffort)
209
+ body.reasoning_effort = selection.reasoningEffort;
205
210
  if (opts.systemContext)
206
211
  body.system_prompt = opts.systemContext;
207
212
  const res = await this.requestJson(`${baseUrl}/v1/threads`, {
@@ -236,16 +241,22 @@ export class DeepseekTuiAdapter {
236
241
  });
237
242
  let turnId = "";
238
243
  try {
244
+ const selection = parseDeepseekRuntimeSelection(opts.extraArgs);
245
+ const body = {
246
+ prompt: opts.text,
247
+ mode: "agent",
248
+ allow_shell: opts.trustLevel !== "public",
249
+ trust_mode: opts.trustLevel !== "public",
250
+ auto_approve: opts.trustLevel !== "public",
251
+ };
252
+ if (selection.model)
253
+ body.model = selection.model;
254
+ if (selection.reasoningEffort)
255
+ body.reasoning_effort = selection.reasoningEffort;
239
256
  const started = await this.requestJson(`${baseUrl}/v1/threads/${encodeURIComponent(threadId)}/turns`, {
240
257
  method: "POST",
241
258
  headers,
242
- body: JSON.stringify({
243
- prompt: opts.text,
244
- mode: "agent",
245
- allow_shell: opts.trustLevel !== "public",
246
- trust_mode: opts.trustLevel !== "public",
247
- auto_approve: opts.trustLevel !== "public",
248
- }),
259
+ body: JSON.stringify(body),
249
260
  signal,
250
261
  });
251
262
  turnId = stringField(started?.turn, "id") ?? stringField(started, "turn_id") ?? "";
@@ -457,6 +468,43 @@ function parseSseFrame(raw) {
457
468
  function authHeaders(token) {
458
469
  return token ? { authorization: `Bearer ${token}` } : {};
459
470
  }
471
+ function parseDeepseekRuntimeSelection(extraArgs) {
472
+ const out = {};
473
+ if (!extraArgs?.length)
474
+ return out;
475
+ for (let i = 0; i < extraArgs.length; i += 1) {
476
+ const arg = extraArgs[i];
477
+ if (arg === "--model") {
478
+ const value = nextArgValue(extraArgs, i);
479
+ if (value !== undefined) {
480
+ out.model = value;
481
+ i += 1;
482
+ }
483
+ }
484
+ else if (arg.startsWith("--model=")) {
485
+ out.model = arg.slice("--model=".length);
486
+ }
487
+ else if (arg === "--reasoning-effort") {
488
+ const value = nextArgValue(extraArgs, i);
489
+ if (value !== undefined) {
490
+ out.reasoningEffort = value;
491
+ i += 1;
492
+ }
493
+ }
494
+ else if (arg.startsWith("--reasoning-effort=")) {
495
+ out.reasoningEffort = arg.slice("--reasoning-effort=".length);
496
+ }
497
+ }
498
+ return out;
499
+ }
500
+ function nextArgValue(args, index) {
501
+ const next = args[index + 1];
502
+ if (typeof next !== "string")
503
+ return undefined;
504
+ if (!next.startsWith("-"))
505
+ return next;
506
+ return /^-\d/.test(next) ? next : undefined;
507
+ }
460
508
  function poolKey(opts) {
461
509
  return opts.accountId || "default";
462
510
  }
@@ -107,6 +107,8 @@ export declare function collectRuntimeSnapshot(opts?: {
107
107
  export declare function attachRuntimeHealth(snapshot: ListRuntimesResult, live: GatewayRuntimeSnapshot): ListRuntimesResult;
108
108
  /** Maximum number of `endpoints[]` entries persisted per runtime (RFC §3.8.2). */
109
109
  export declare const RUNTIME_ENDPOINTS_CAP = 32;
110
+ export declare const RUNTIME_MODELS_CAP = 128;
111
+ export declare const RUNTIME_PARAMETERS_CAP = 64;
110
112
  /** Injection seam for L2 + L3 endpoint probes — kept testable + side-effect-free. */
111
113
  export type WsEndpointProbeFn = (args: {
112
114
  url: string;
package/dist/provision.js CHANGED
@@ -18,6 +18,8 @@ import { hermesProfileHomeDir, isValidHermesProfileName, listHermesProfiles, } f
18
18
  import { log as daemonLog } from "./log.js";
19
19
  import { discoverAgentCredentials } from "./agent-discovery.js";
20
20
  import { resolveMemoryDir } from "./working-memory.js";
21
+ import { discoverRuntimeModelCatalog } from "./runtime-models.js";
22
+ import { buildRuntimeSelectionExtraArgs, mergeRuntimeExtraArgs, } from "./runtime-route-options.js";
21
23
  /**
22
24
  * Build a dispatcher function that routes a `ControlFrame` to the right
23
25
  * handler. Returned function signature matches
@@ -783,6 +785,9 @@ function upsertManagedRouteForCredentials(credentials, cfg, gateway) {
783
785
  runtime: credentials.runtime ?? cfg.defaultRoute.adapter,
784
786
  cwd: credentials.cwd ?? agentWorkspaceDir(credentials.agentId),
785
787
  };
788
+ const extraArgs = mergeRuntimeExtraArgs(cfg.defaultRoute.extraArgs, buildRuntimeSelectionExtraArgs(synthRoute.runtime, credentials));
789
+ if (extraArgs)
790
+ synthRoute.extraArgs = extraArgs;
786
791
  if (synthRoute.runtime === "openclaw-acp") {
787
792
  const profile = (cfg.openclawGateways ?? []).find((g) => g.name === credentials.openclawGateway);
788
793
  if (profile) {
@@ -887,6 +892,13 @@ async function materializeCredentials(params, cfg, ctx, explicitCwd) {
887
892
  record.tokenExpiresAt = c.tokenExpiresAt;
888
893
  if (runtime)
889
894
  record.runtime = runtime;
895
+ const runtimeSelection = pickRuntimeSelection(params);
896
+ if (runtimeSelection.runtimeModel)
897
+ record.runtimeModel = runtimeSelection.runtimeModel;
898
+ if (runtimeSelection.reasoningEffort)
899
+ record.reasoningEffort = runtimeSelection.reasoningEffort;
900
+ if (typeof runtimeSelection.thinking === "boolean")
901
+ record.thinking = runtimeSelection.thinking;
890
902
  record.cwd = cwd;
891
903
  const openclawSel = pickOpenclawSelection(params);
892
904
  if (openclawSel.gateway)
@@ -922,6 +934,13 @@ async function materializeCredentials(params, cfg, ctx, explicitCwd) {
922
934
  };
923
935
  if (runtime)
924
936
  record.runtime = runtime;
937
+ const runtimeSelection = pickRuntimeSelection(params);
938
+ if (runtimeSelection.runtimeModel)
939
+ record.runtimeModel = runtimeSelection.runtimeModel;
940
+ if (runtimeSelection.reasoningEffort)
941
+ record.reasoningEffort = runtimeSelection.reasoningEffort;
942
+ if (typeof runtimeSelection.thinking === "boolean")
943
+ record.thinking = runtimeSelection.thinking;
925
944
  record.cwd = cwd;
926
945
  const openclawSel = pickOpenclawSelection(params);
927
946
  if (openclawSel.gateway)
@@ -1449,6 +1468,12 @@ export function collectRuntimeSnapshot(opts = {}) {
1449
1468
  record.version = entry.result.version;
1450
1469
  if (entry.result.path)
1451
1470
  record.path = entry.result.path;
1471
+ const catalog = discoverRuntimeModelCatalog(entry);
1472
+ const models = catalog.models;
1473
+ if (models?.length)
1474
+ record.models = models.slice(0, RUNTIME_MODELS_CAP);
1475
+ if (catalog.parameters?.length)
1476
+ record.parameters = catalog.parameters.slice(0, RUNTIME_PARAMETERS_CAP);
1452
1477
  // Gateway's probe surface doesn't expose an `error` string today — it
1453
1478
  // already swallows throws into `{available: false}`. We leave the wire
1454
1479
  // field blank in that case and let callers treat `!available` as reason
@@ -1502,6 +1527,8 @@ export function attachRuntimeHealth(snapshot, live) {
1502
1527
  }
1503
1528
  /** Maximum number of `endpoints[]` entries persisted per runtime (RFC §3.8.2). */
1504
1529
  export const RUNTIME_ENDPOINTS_CAP = 32;
1530
+ export const RUNTIME_MODELS_CAP = 128;
1531
+ export const RUNTIME_PARAMETERS_CAP = 64;
1505
1532
  export function classifyOpenclawAuthError(message) {
1506
1533
  const text = (message ?? "").toLowerCase();
1507
1534
  if (!text)
@@ -2096,6 +2123,12 @@ function readAgentRuntimesFromCredentials(agentIds) {
2096
2123
  const entry = {};
2097
2124
  if (creds.runtime)
2098
2125
  entry.runtime = creds.runtime;
2126
+ if (creds.runtimeModel)
2127
+ entry.runtimeModel = creds.runtimeModel;
2128
+ if (creds.reasoningEffort)
2129
+ entry.reasoningEffort = creds.reasoningEffort;
2130
+ if (typeof creds.thinking === "boolean")
2131
+ entry.thinking = creds.thinking;
2099
2132
  if (creds.cwd)
2100
2133
  entry.cwd = creds.cwd;
2101
2134
  if (creds.openclawGateway)
@@ -2104,8 +2137,16 @@ function readAgentRuntimesFromCredentials(agentIds) {
2104
2137
  entry.openclawAgent = creds.openclawAgent;
2105
2138
  if (creds.hermesProfile)
2106
2139
  entry.hermesProfile = creds.hermesProfile;
2107
- if (entry.runtime || entry.cwd || entry.openclawGateway || entry.openclawAgent || entry.hermesProfile)
2140
+ if (entry.runtime ||
2141
+ entry.runtimeModel ||
2142
+ entry.reasoningEffort ||
2143
+ typeof entry.thinking === "boolean" ||
2144
+ entry.cwd ||
2145
+ entry.openclawGateway ||
2146
+ entry.openclawAgent ||
2147
+ entry.hermesProfile) {
2108
2148
  out[id] = entry;
2149
+ }
2109
2150
  }
2110
2151
  catch {
2111
2152
  // best-effort — skip agents with unreadable credentials
@@ -2309,6 +2350,30 @@ function pickRuntime(params) {
2309
2350
  }
2310
2351
  return undefined;
2311
2352
  }
2353
+ function pickRuntimeSelection(params) {
2354
+ const out = {};
2355
+ const runtimeModel = pickString(params.runtimeModel, params.credentials?.runtimeModel);
2356
+ const reasoningEffort = pickString(params.reasoningEffort, params.credentials?.reasoningEffort);
2357
+ if (runtimeModel)
2358
+ out.runtimeModel = runtimeModel;
2359
+ if (reasoningEffort)
2360
+ out.reasoningEffort = reasoningEffort;
2361
+ if (typeof params.thinking === "boolean") {
2362
+ out.thinking = params.thinking;
2363
+ }
2364
+ else if (typeof params.credentials?.thinking === "boolean") {
2365
+ out.thinking = params.credentials.thinking;
2366
+ }
2367
+ return out;
2368
+ }
2369
+ function pickString(...values) {
2370
+ for (const value of values) {
2371
+ const trimmed = value?.trim();
2372
+ if (trimmed)
2373
+ return trimmed;
2374
+ }
2375
+ return undefined;
2376
+ }
2312
2377
  function assertKnownRuntime(runtime) {
2313
2378
  const mod = getAdapterModule(runtime);
2314
2379
  if (!mod) {
@@ -0,0 +1,17 @@
1
+ import type { RuntimeModelProbe, RuntimeParameterProbe } from "@botcord/protocol-core";
2
+ import type { RuntimeProbeEntry } from "./adapters/runtimes.js";
3
+ export interface RuntimeModelDiscovery {
4
+ models?: RuntimeModelProbe[];
5
+ parameters?: RuntimeParameterProbe[];
6
+ }
7
+ export declare function discoverRuntimeModelCatalog(entry: RuntimeProbeEntry): RuntimeModelDiscovery;
8
+ export declare function discoverRuntimeModels(entry: RuntimeProbeEntry): RuntimeModelProbe[] | undefined;
9
+ export declare function discoverRuntimeParameters(entry: RuntimeProbeEntry): RuntimeParameterProbe[] | undefined;
10
+ export declare function discoverClaudeModels(): RuntimeModelProbe[];
11
+ export declare function discoverCodexModels(command: string | undefined): RuntimeModelProbe[] | undefined;
12
+ export declare function parseCodexModelCatalog(raw: string): RuntimeModelProbe[] | undefined;
13
+ export declare function discoverDeepseekModels(command: string | undefined): RuntimeModelProbe[] | undefined;
14
+ export declare function parseDeepseekModelList(raw: string): RuntimeModelProbe[] | undefined;
15
+ export declare function discoverKimiModels(): RuntimeModelProbe[] | undefined;
16
+ export declare function parseKimiConfigModels(raw: string): RuntimeModelProbe[] | undefined;
17
+ export declare function parseKimiRuntimeParameters(raw: string): RuntimeParameterProbe[];