@vicvic-im/agent-bot 0.1.4 → 0.1.5

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.
package/dist/cli.cjs CHANGED
@@ -363,7 +363,7 @@ var acp = __toESM(require("@agentclientprotocol/sdk"), 1);
363
363
  var EMPTY_USAGE = { used: 0, size: 0, cost: null };
364
364
 
365
365
  // src/version.ts
366
- var VBOT_VERSION = "0.1.4";
366
+ var VBOT_VERSION = "0.1.5";
367
367
 
368
368
  // src/agents/acp.ts
369
369
  var AgentAbortError = class extends Error {
@@ -426,6 +426,11 @@ var AcpAgent = class {
426
426
  const label = options.find((entry) => entry.value === value)?.label ?? value;
427
427
  return { value, label, options };
428
428
  }
429
+ // 不跑 prompt 也能读模型:session/new|resume|load 的 configOptions 里带 model 项。
430
+ async warmup(savedSessionId) {
431
+ await this.start();
432
+ await this.ensureSession(savedSessionId);
433
+ }
429
434
  resetSession() {
430
435
  this.session = null;
431
436
  }
@@ -762,7 +767,7 @@ var HELP_ROWS = [
762
767
  ["/stop", "Abort the current request"],
763
768
  ["/status", "Show agent, session and queue status"],
764
769
  ["/agent [name]", "Show or switch agent backend"],
765
- ["/model [name]", "Show or switch model"],
770
+ ["/model [name]", "Show or switch model (/model list for all)"],
766
771
  ["/cost", "Show context usage and cost"],
767
772
  ["/version", "Show vbot version"],
768
773
  ["/help", "Show this help"]
@@ -770,6 +775,7 @@ var HELP_ROWS = [
770
775
  var HELP_FOOTER = "Other /commands are sent to the agent as prompts.";
771
776
  var NEW_BUSY_TEXT = "Cannot reset the session while a request is running. Use /stop first.";
772
777
  var MODEL_BUSY_TEXT = "Cannot switch model while a request is running. Use /stop first.";
778
+ var INLINE_OPTION_LIMIT = 8;
773
779
  var HELP_TEXT = [
774
780
  table(
775
781
  ["Command", "Description"],
@@ -833,9 +839,11 @@ function compactPath(path) {
833
839
  function modelText(runtime) {
834
840
  const current = runtime.agent().currentModel();
835
841
  if (current) return current.label;
842
+ const cached = runtime.state().model;
843
+ if (cached) return `${cached.label} (last known)`;
836
844
  const configured = runtime.config().agent.model;
837
845
  if (configured) return `${configured} (configured, not applied yet)`;
838
- return "unknown (session not started)";
846
+ return "unknown (send /model list)";
839
847
  }
840
848
  function handleSlashCommand(runtime, text, send) {
841
849
  const [rawCommand, ...args] = text.split(/\s+/);
@@ -925,7 +933,11 @@ function handleAgent(runtime, args, send) {
925
933
  async function handleModel(runtime, args, send) {
926
934
  const model = args.join(" ").trim();
927
935
  if (!model) {
928
- send(modelReport(runtime));
936
+ await reportModel(runtime, send, false);
937
+ return;
938
+ }
939
+ if (model.toLowerCase() === "list") {
940
+ await reportModel(runtime, send, true);
929
941
  return;
930
942
  }
931
943
  if (runtime.queue().status().busy) {
@@ -940,7 +952,12 @@ async function handleModel(runtime, args, send) {
940
952
  runtime.saveConfig();
941
953
  return accepted;
942
954
  });
943
- const applied = runtime.agent().currentModel()?.label ?? model;
955
+ const applied = runtime.agent().currentModel();
956
+ if (applied) {
957
+ runtime.state().model = { value: applied.value, label: applied.label };
958
+ runtime.saveState();
959
+ }
960
+ const label = applied?.label ?? model;
944
961
  if (!supported) {
945
962
  send(
946
963
  `Model saved: ${model}. This agent does not expose a runtime model option; it will be used on the next session if supported.`
@@ -948,34 +965,83 @@ async function handleModel(runtime, args, send) {
948
965
  return;
949
966
  }
950
967
  send(
951
- hadSession ? `Model switched to: ${applied}` : `Model set to: ${applied}. It will apply when the session starts.`
968
+ hadSession ? `Model switched to: ${label}` : `Model set to: ${label}. It will apply when the session starts.`
952
969
  );
953
970
  }
954
- function modelReport(runtime) {
955
- const current = runtime.agent().currentModel();
956
- if (!current) {
957
- const configured = runtime.config().agent.model;
958
- const hint = configured ? `Configured: ${configured}
959
-
960
- The agent has not reported a model yet; it becomes visible after the first message.` : "This agent does not expose a runtime model option, or the session has not started.";
961
- return `Current: unknown
962
-
963
- ${hint}`;
971
+ async function reportModel(runtime, send, wantList) {
972
+ const live = runtime.agent().currentModel();
973
+ if (live) {
974
+ send(renderModel(live, wantList));
975
+ return;
976
+ }
977
+ const cached = runtime.state().model;
978
+ if (cached && !wantList) {
979
+ send(renderCachedModel(cached));
980
+ return;
981
+ }
982
+ const name = runtime.config().agent.name;
983
+ send(`Reading ${wantList ? "model list" : "model"} from ${name}... (starting the agent)`);
984
+ try {
985
+ await runtime.serialize(
986
+ () => runtime.agent().warmup(runtime.state().acpSessionId ?? void 0)
987
+ );
988
+ } catch (error) {
989
+ const reason = error instanceof Error ? error.message : String(error);
990
+ send(`Could not read the model from ${name}: ${reason}`);
991
+ if (cached) send(renderCachedModel(cached));
992
+ return;
993
+ }
994
+ const refreshed = runtime.agent().currentModel();
995
+ if (refreshed) {
996
+ runtime.state().model = { value: refreshed.value, label: refreshed.label };
997
+ runtime.saveState();
998
+ send(renderModel(refreshed, wantList));
999
+ return;
1000
+ }
1001
+ send(cached ? renderCachedModel(cached) : unknownModelText(runtime));
1002
+ }
1003
+ function renderModel(model, wantList) {
1004
+ const lines = [`Current: ${model.label}`];
1005
+ const total = model.options.length;
1006
+ if (total === 0) {
1007
+ lines.push("", "This agent does not expose a model list.");
1008
+ return lines.join("\n");
964
1009
  }
965
- const lines = [`Current: ${current.label}`];
966
- if (current.options.length > 0) {
1010
+ if (wantList || total <= INLINE_OPTION_LIMIT) {
967
1011
  lines.push(
968
1012
  "",
969
1013
  table(
970
1014
  ["Model", ""],
971
- current.options.map((option) => [
1015
+ model.options.map((option) => [
972
1016
  option.label,
973
- option.value === current.value ? "\u2190 current" : ""
1017
+ option.value === model.value ? "\u2190 current" : ""
974
1018
  ])
975
1019
  )
976
1020
  );
977
- }
978
- lines.push("", "Switch: /model <name>");
1021
+ lines.push("", "Switch: /model <name>");
1022
+ return lines.join("\n");
1023
+ }
1024
+ lines.push(
1025
+ "",
1026
+ table(["Model", ""], [[model.label, "\u2190 current"]]),
1027
+ "",
1028
+ `${total} models available \xB7 Switch: /model <name> \xB7 List: /model list`
1029
+ );
1030
+ return lines.join("\n");
1031
+ }
1032
+ function renderCachedModel(cached) {
1033
+ return [
1034
+ `Current: ${cached.label} (last known)`,
1035
+ "",
1036
+ "Cached from the last session; the agent has not started in this process yet.",
1037
+ "Send /model list to read the live value from the agent."
1038
+ ].join("\n");
1039
+ }
1040
+ function unknownModelText(runtime) {
1041
+ const configured = runtime.config().agent.model;
1042
+ const lines = ["Current: unknown"];
1043
+ if (configured) lines.push("", `Configured: ${configured}`);
1044
+ lines.push("", "This agent does not expose a runtime model option.");
979
1045
  return lines.join("\n");
980
1046
  }
981
1047
  function statusText(runtime) {
@@ -1052,6 +1118,7 @@ async function switchAgent(ctx, name) {
1052
1118
  ctx.state.agent = name;
1053
1119
  ctx.state.acpSessionId = null;
1054
1120
  ctx.state.usage = { ...EMPTY_USAGE };
1121
+ ctx.state.model = null;
1055
1122
  ctx.saveConfig();
1056
1123
  ctx.saveState();
1057
1124
  await previous.close().catch(() => void 0);
@@ -1088,9 +1155,16 @@ function emptyState(token, agent, cwd) {
1088
1155
  cwd,
1089
1156
  createdAt: now,
1090
1157
  lastUsedAt: now,
1091
- usage: { ...EMPTY_USAGE }
1158
+ usage: { ...EMPTY_USAGE },
1159
+ model: null
1092
1160
  };
1093
1161
  }
1162
+ function parseCachedModel(value) {
1163
+ if (!value || typeof value !== "object") return null;
1164
+ const record = value;
1165
+ if (typeof record.value !== "string" || typeof record.label !== "string") return null;
1166
+ return { value: record.value, label: record.label };
1167
+ }
1094
1168
  function loadState(token) {
1095
1169
  const path = statePath(token);
1096
1170
  if (!(0, import_node_fs4.existsSync)(path)) return null;
@@ -1105,7 +1179,8 @@ function loadState(token) {
1105
1179
  cwd: typeof parsed.cwd === "string" ? parsed.cwd : process.cwd(),
1106
1180
  createdAt: typeof parsed.createdAt === "string" ? parsed.createdAt : (/* @__PURE__ */ new Date()).toISOString(),
1107
1181
  lastUsedAt: typeof parsed.lastUsedAt === "string" ? parsed.lastUsedAt : (/* @__PURE__ */ new Date()).toISOString(),
1108
- usage: parsed.usage && typeof parsed.usage === "object" ? { ...EMPTY_USAGE, ...parsed.usage } : { ...EMPTY_USAGE }
1182
+ usage: parsed.usage && typeof parsed.usage === "object" ? { ...EMPTY_USAGE, ...parsed.usage } : { ...EMPTY_USAGE },
1183
+ model: parseCachedModel(parsed.model)
1109
1184
  };
1110
1185
  } catch {
1111
1186
  return null;
@@ -1501,6 +1576,8 @@ async function processPrompt(ctx, item, signal) {
1501
1576
  });
1502
1577
  ctx.state.acpSessionId = result.sessionId;
1503
1578
  ctx.state.usage = result.usage;
1579
+ const model = ctx.getAgent().currentModel();
1580
+ if (model) ctx.state.model = { value: model.value, label: model.label };
1504
1581
  ctx.saveState();
1505
1582
  for (const chunk of splitMessage(result.text || "(no output)")) {
1506
1583
  await item.reply(chunk);