@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/server.cjs CHANGED
@@ -250,7 +250,7 @@ var acp = __toESM(require("@agentclientprotocol/sdk"), 1);
250
250
  var EMPTY_USAGE = { used: 0, size: 0, cost: null };
251
251
 
252
252
  // src/version.ts
253
- var VBOT_VERSION = "0.1.4";
253
+ var VBOT_VERSION = "0.1.5";
254
254
 
255
255
  // src/agents/acp.ts
256
256
  var AgentAbortError = class extends Error {
@@ -313,6 +313,11 @@ var AcpAgent = class {
313
313
  const label = options.find((entry) => entry.value === value)?.label ?? value;
314
314
  return { value, label, options };
315
315
  }
316
+ // 不跑 prompt 也能读模型:session/new|resume|load 的 configOptions 里带 model 项。
317
+ async warmup(savedSessionId) {
318
+ await this.start();
319
+ await this.ensureSession(savedSessionId);
320
+ }
316
321
  resetSession() {
317
322
  this.session = null;
318
323
  }
@@ -649,7 +654,7 @@ var HELP_ROWS = [
649
654
  ["/stop", "Abort the current request"],
650
655
  ["/status", "Show agent, session and queue status"],
651
656
  ["/agent [name]", "Show or switch agent backend"],
652
- ["/model [name]", "Show or switch model"],
657
+ ["/model [name]", "Show or switch model (/model list for all)"],
653
658
  ["/cost", "Show context usage and cost"],
654
659
  ["/version", "Show vbot version"],
655
660
  ["/help", "Show this help"]
@@ -657,6 +662,7 @@ var HELP_ROWS = [
657
662
  var HELP_FOOTER = "Other /commands are sent to the agent as prompts.";
658
663
  var NEW_BUSY_TEXT = "Cannot reset the session while a request is running. Use /stop first.";
659
664
  var MODEL_BUSY_TEXT = "Cannot switch model while a request is running. Use /stop first.";
665
+ var INLINE_OPTION_LIMIT = 8;
660
666
  var HELP_TEXT = [
661
667
  table(
662
668
  ["Command", "Description"],
@@ -720,9 +726,11 @@ function compactPath(path) {
720
726
  function modelText(runtime) {
721
727
  const current = runtime.agent().currentModel();
722
728
  if (current) return current.label;
729
+ const cached = runtime.state().model;
730
+ if (cached) return `${cached.label} (last known)`;
723
731
  const configured = runtime.config().agent.model;
724
732
  if (configured) return `${configured} (configured, not applied yet)`;
725
- return "unknown (session not started)";
733
+ return "unknown (send /model list)";
726
734
  }
727
735
  function handleSlashCommand(runtime, text, send) {
728
736
  const [rawCommand, ...args] = text.split(/\s+/);
@@ -812,7 +820,11 @@ function handleAgent(runtime, args, send) {
812
820
  async function handleModel(runtime, args, send) {
813
821
  const model = args.join(" ").trim();
814
822
  if (!model) {
815
- send(modelReport(runtime));
823
+ await reportModel(runtime, send, false);
824
+ return;
825
+ }
826
+ if (model.toLowerCase() === "list") {
827
+ await reportModel(runtime, send, true);
816
828
  return;
817
829
  }
818
830
  if (runtime.queue().status().busy) {
@@ -827,7 +839,12 @@ async function handleModel(runtime, args, send) {
827
839
  runtime.saveConfig();
828
840
  return accepted;
829
841
  });
830
- const applied = runtime.agent().currentModel()?.label ?? model;
842
+ const applied = runtime.agent().currentModel();
843
+ if (applied) {
844
+ runtime.state().model = { value: applied.value, label: applied.label };
845
+ runtime.saveState();
846
+ }
847
+ const label = applied?.label ?? model;
831
848
  if (!supported) {
832
849
  send(
833
850
  `Model saved: ${model}. This agent does not expose a runtime model option; it will be used on the next session if supported.`
@@ -835,34 +852,83 @@ async function handleModel(runtime, args, send) {
835
852
  return;
836
853
  }
837
854
  send(
838
- hadSession ? `Model switched to: ${applied}` : `Model set to: ${applied}. It will apply when the session starts.`
855
+ hadSession ? `Model switched to: ${label}` : `Model set to: ${label}. It will apply when the session starts.`
839
856
  );
840
857
  }
841
- function modelReport(runtime) {
842
- const current = runtime.agent().currentModel();
843
- if (!current) {
844
- const configured = runtime.config().agent.model;
845
- const hint = configured ? `Configured: ${configured}
846
-
847
- 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.";
848
- return `Current: unknown
849
-
850
- ${hint}`;
858
+ async function reportModel(runtime, send, wantList) {
859
+ const live = runtime.agent().currentModel();
860
+ if (live) {
861
+ send(renderModel(live, wantList));
862
+ return;
863
+ }
864
+ const cached = runtime.state().model;
865
+ if (cached && !wantList) {
866
+ send(renderCachedModel(cached));
867
+ return;
868
+ }
869
+ const name = runtime.config().agent.name;
870
+ send(`Reading ${wantList ? "model list" : "model"} from ${name}... (starting the agent)`);
871
+ try {
872
+ await runtime.serialize(
873
+ () => runtime.agent().warmup(runtime.state().acpSessionId ?? void 0)
874
+ );
875
+ } catch (error) {
876
+ const reason = error instanceof Error ? error.message : String(error);
877
+ send(`Could not read the model from ${name}: ${reason}`);
878
+ if (cached) send(renderCachedModel(cached));
879
+ return;
880
+ }
881
+ const refreshed = runtime.agent().currentModel();
882
+ if (refreshed) {
883
+ runtime.state().model = { value: refreshed.value, label: refreshed.label };
884
+ runtime.saveState();
885
+ send(renderModel(refreshed, wantList));
886
+ return;
887
+ }
888
+ send(cached ? renderCachedModel(cached) : unknownModelText(runtime));
889
+ }
890
+ function renderModel(model, wantList) {
891
+ const lines = [`Current: ${model.label}`];
892
+ const total = model.options.length;
893
+ if (total === 0) {
894
+ lines.push("", "This agent does not expose a model list.");
895
+ return lines.join("\n");
851
896
  }
852
- const lines = [`Current: ${current.label}`];
853
- if (current.options.length > 0) {
897
+ if (wantList || total <= INLINE_OPTION_LIMIT) {
854
898
  lines.push(
855
899
  "",
856
900
  table(
857
901
  ["Model", ""],
858
- current.options.map((option) => [
902
+ model.options.map((option) => [
859
903
  option.label,
860
- option.value === current.value ? "\u2190 current" : ""
904
+ option.value === model.value ? "\u2190 current" : ""
861
905
  ])
862
906
  )
863
907
  );
864
- }
865
- lines.push("", "Switch: /model <name>");
908
+ lines.push("", "Switch: /model <name>");
909
+ return lines.join("\n");
910
+ }
911
+ lines.push(
912
+ "",
913
+ table(["Model", ""], [[model.label, "\u2190 current"]]),
914
+ "",
915
+ `${total} models available \xB7 Switch: /model <name> \xB7 List: /model list`
916
+ );
917
+ return lines.join("\n");
918
+ }
919
+ function renderCachedModel(cached) {
920
+ return [
921
+ `Current: ${cached.label} (last known)`,
922
+ "",
923
+ "Cached from the last session; the agent has not started in this process yet.",
924
+ "Send /model list to read the live value from the agent."
925
+ ].join("\n");
926
+ }
927
+ function unknownModelText(runtime) {
928
+ const configured = runtime.config().agent.model;
929
+ const lines = ["Current: unknown"];
930
+ if (configured) lines.push("", `Configured: ${configured}`);
931
+ lines.push("", "This agent does not expose a runtime model option.");
866
932
  return lines.join("\n");
867
933
  }
868
934
  function statusText(runtime) {
@@ -939,6 +1005,7 @@ async function switchAgent(ctx, name) {
939
1005
  ctx.state.agent = name;
940
1006
  ctx.state.acpSessionId = null;
941
1007
  ctx.state.usage = { ...EMPTY_USAGE };
1008
+ ctx.state.model = null;
942
1009
  ctx.saveConfig();
943
1010
  ctx.saveState();
944
1011
  await previous.close().catch(() => void 0);
@@ -975,9 +1042,16 @@ function emptyState(token, agent, cwd) {
975
1042
  cwd,
976
1043
  createdAt: now,
977
1044
  lastUsedAt: now,
978
- usage: { ...EMPTY_USAGE }
1045
+ usage: { ...EMPTY_USAGE },
1046
+ model: null
979
1047
  };
980
1048
  }
1049
+ function parseCachedModel(value) {
1050
+ if (!value || typeof value !== "object") return null;
1051
+ const record = value;
1052
+ if (typeof record.value !== "string" || typeof record.label !== "string") return null;
1053
+ return { value: record.value, label: record.label };
1054
+ }
981
1055
  function loadState(token) {
982
1056
  const path = statePath(token);
983
1057
  if (!(0, import_node_fs2.existsSync)(path)) return null;
@@ -992,7 +1066,8 @@ function loadState(token) {
992
1066
  cwd: typeof parsed.cwd === "string" ? parsed.cwd : process.cwd(),
993
1067
  createdAt: typeof parsed.createdAt === "string" ? parsed.createdAt : (/* @__PURE__ */ new Date()).toISOString(),
994
1068
  lastUsedAt: typeof parsed.lastUsedAt === "string" ? parsed.lastUsedAt : (/* @__PURE__ */ new Date()).toISOString(),
995
- usage: parsed.usage && typeof parsed.usage === "object" ? { ...EMPTY_USAGE, ...parsed.usage } : { ...EMPTY_USAGE }
1069
+ usage: parsed.usage && typeof parsed.usage === "object" ? { ...EMPTY_USAGE, ...parsed.usage } : { ...EMPTY_USAGE },
1070
+ model: parseCachedModel(parsed.model)
996
1071
  };
997
1072
  } catch {
998
1073
  return null;
@@ -1388,6 +1463,8 @@ async function processPrompt(ctx, item, signal) {
1388
1463
  });
1389
1464
  ctx.state.acpSessionId = result.sessionId;
1390
1465
  ctx.state.usage = result.usage;
1466
+ const model = ctx.getAgent().currentModel();
1467
+ if (model) ctx.state.model = { value: model.value, label: model.label };
1391
1468
  ctx.saveState();
1392
1469
  for (const chunk of splitMessage(result.text || "(no output)")) {
1393
1470
  await item.reply(chunk);