@vicvic-im/agent-bot 0.1.2 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -2
- package/dist/agents/acp.d.ts +2 -1
- package/dist/agents/acp.d.ts.map +1 -1
- package/dist/agents/types.d.ts +16 -0
- package/dist/agents/types.d.ts.map +1 -1
- package/dist/bin-server.cjs +201 -71
- package/dist/bin-server.cjs.map +1 -1
- package/dist/bin-server.js +1 -1
- package/dist/{chunk-6CK4XIJB.js → chunk-CDBYOHVL.js} +2 -2
- package/dist/{chunk-X5QSWCDH.js → chunk-ELLWQDOY.js} +202 -74
- package/dist/chunk-ELLWQDOY.js.map +1 -0
- package/dist/cli.cjs +201 -73
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +2 -2
- package/dist/core/dispatcher.d.ts +3 -1
- package/dist/core/dispatcher.d.ts.map +1 -1
- package/dist/index.cjs +201 -73
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +2 -2
- package/dist/server.cjs +201 -73
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +1 -1
- package/dist/version.d.ts +1 -1
- package/package.json +10 -10
- package/dist/chunk-X5QSWCDH.js.map +0 -1
- /package/dist/{chunk-6CK4XIJB.js.map → chunk-CDBYOHVL.js.map} +0 -0
package/dist/index.cjs
CHANGED
|
@@ -360,6 +360,9 @@ var acp = __toESM(require("@agentclientprotocol/sdk"), 1);
|
|
|
360
360
|
// src/agents/types.ts
|
|
361
361
|
var EMPTY_USAGE = { used: 0, size: 0, cost: null };
|
|
362
362
|
|
|
363
|
+
// src/version.ts
|
|
364
|
+
var VBOT_VERSION = "0.1.3";
|
|
365
|
+
|
|
363
366
|
// src/agents/acp.ts
|
|
364
367
|
var AgentAbortError = class extends Error {
|
|
365
368
|
constructor() {
|
|
@@ -376,6 +379,14 @@ var AgentStartError = class extends Error {
|
|
|
376
379
|
stderr;
|
|
377
380
|
};
|
|
378
381
|
var STDERR_LIMIT = 4e3;
|
|
382
|
+
function flattenSelectOptions(options) {
|
|
383
|
+
return options.flatMap(
|
|
384
|
+
(entry) => "group" in entry ? entry.options.map((option) => ({ value: option.value, label: option.name })) : [{ value: entry.value, label: entry.name }]
|
|
385
|
+
);
|
|
386
|
+
}
|
|
387
|
+
function isModelOption(option) {
|
|
388
|
+
return option.type === "select" && (option.category === "model" || option.id === "model");
|
|
389
|
+
}
|
|
379
390
|
var AcpAgent = class {
|
|
380
391
|
constructor(name, config) {
|
|
381
392
|
this.name = name;
|
|
@@ -405,6 +416,14 @@ var AcpAgent = class {
|
|
|
405
416
|
sessionId() {
|
|
406
417
|
return this.session;
|
|
407
418
|
}
|
|
419
|
+
currentModel() {
|
|
420
|
+
const option = this.configOptions.find(isModelOption);
|
|
421
|
+
if (!option || option.type !== "select") return null;
|
|
422
|
+
const value = String(option.currentValue);
|
|
423
|
+
const options = flattenSelectOptions(option.options);
|
|
424
|
+
const label = options.find((entry) => entry.value === value)?.label ?? value;
|
|
425
|
+
return { value, label, options };
|
|
426
|
+
}
|
|
408
427
|
resetSession() {
|
|
409
428
|
this.session = null;
|
|
410
429
|
}
|
|
@@ -472,7 +491,7 @@ var AcpAgent = class {
|
|
|
472
491
|
const init = await this.ctx.request(acp.methods.agent.initialize, {
|
|
473
492
|
protocolVersion: 1,
|
|
474
493
|
clientCapabilities: {},
|
|
475
|
-
clientInfo: { name: "vbot", version:
|
|
494
|
+
clientInfo: { name: "vbot", version: VBOT_VERSION }
|
|
476
495
|
});
|
|
477
496
|
this.initCaps = init.agentCapabilities;
|
|
478
497
|
} catch (error) {
|
|
@@ -591,15 +610,14 @@ var AcpAgent = class {
|
|
|
591
610
|
this.pendingModel = model;
|
|
592
611
|
return true;
|
|
593
612
|
}
|
|
594
|
-
const option = this.configOptions.find(
|
|
595
|
-
(entry) => entry.type === "select" && (entry.id === "model" || entry.category === "model")
|
|
596
|
-
);
|
|
613
|
+
const option = this.configOptions.find(isModelOption);
|
|
597
614
|
if (!option) return false;
|
|
598
|
-
await this.ctx.request(acp.methods.agent.session.setConfigOption, {
|
|
615
|
+
const response = await this.ctx.request(acp.methods.agent.session.setConfigOption, {
|
|
599
616
|
sessionId: this.session,
|
|
600
617
|
configId: option.id,
|
|
601
618
|
value: model
|
|
602
619
|
});
|
|
620
|
+
if (response?.configOptions?.length) this.configOptions = response.configOptions;
|
|
603
621
|
this.pendingModel = null;
|
|
604
622
|
return true;
|
|
605
623
|
}
|
|
@@ -735,21 +753,29 @@ var BotQueue = class {
|
|
|
735
753
|
}
|
|
736
754
|
};
|
|
737
755
|
|
|
738
|
-
// src/version.ts
|
|
739
|
-
var VBOT_VERSION = "0.1.2";
|
|
740
|
-
|
|
741
756
|
// src/core/dispatcher.ts
|
|
742
|
-
var
|
|
743
|
-
|
|
744
|
-
/
|
|
745
|
-
/
|
|
746
|
-
/
|
|
747
|
-
/
|
|
748
|
-
/
|
|
749
|
-
/
|
|
750
|
-
/
|
|
751
|
-
|
|
752
|
-
|
|
757
|
+
var import_node_os2 = require("os");
|
|
758
|
+
var HELP_ROWS = [
|
|
759
|
+
["/new", "Start a new session"],
|
|
760
|
+
["/stop", "Abort the current request"],
|
|
761
|
+
["/status", "Show agent, session and queue status"],
|
|
762
|
+
["/agent [name]", "Show or switch agent backend"],
|
|
763
|
+
["/model [name]", "Show or switch model"],
|
|
764
|
+
["/cost", "Show context usage and cost"],
|
|
765
|
+
["/version", "Show vbot version"],
|
|
766
|
+
["/help", "Show this help"]
|
|
767
|
+
];
|
|
768
|
+
var HELP_FOOTER = "Other /commands are sent to the agent as prompts.";
|
|
769
|
+
var NEW_BUSY_TEXT = "Cannot reset the session while a request is running. Use /stop first.";
|
|
770
|
+
var MODEL_BUSY_TEXT = "Cannot switch model while a request is running. Use /stop first.";
|
|
771
|
+
var HELP_TEXT = [
|
|
772
|
+
table(
|
|
773
|
+
["Command", "Description"],
|
|
774
|
+
HELP_ROWS.map(([command, description]) => [command, description])
|
|
775
|
+
),
|
|
776
|
+
"",
|
|
777
|
+
HELP_FOOTER
|
|
778
|
+
].join("\n");
|
|
753
779
|
function createDispatcher(runtime) {
|
|
754
780
|
return (text, reply) => {
|
|
755
781
|
const send = (message) => {
|
|
@@ -763,11 +789,57 @@ function createDispatcher(runtime) {
|
|
|
763
789
|
});
|
|
764
790
|
};
|
|
765
791
|
}
|
|
792
|
+
function escapeCell(value) {
|
|
793
|
+
return value.replace(/\|/g, "\\|").replace(/\r?\n/g, " ");
|
|
794
|
+
}
|
|
795
|
+
function table(headers, rows) {
|
|
796
|
+
const head = `| ${headers.map(escapeCell).join(" | ")} |`;
|
|
797
|
+
const separator = `| ${headers.map(() => "---").join(" | ")} |`;
|
|
798
|
+
const body = rows.map((row) => `| ${row.map(escapeCell).join(" | ")} |`);
|
|
799
|
+
return [head, separator, ...body].join("\n");
|
|
800
|
+
}
|
|
801
|
+
function formatNumber(value) {
|
|
802
|
+
if (!Number.isFinite(value)) return String(value);
|
|
803
|
+
const negative = value < 0;
|
|
804
|
+
const digits = Math.abs(Math.trunc(value)).toString();
|
|
805
|
+
let out = "";
|
|
806
|
+
for (let i = 0; i < digits.length; i += 1) {
|
|
807
|
+
if (i > 0 && (digits.length - i) % 3 === 0) out += ",";
|
|
808
|
+
out += digits[i];
|
|
809
|
+
}
|
|
810
|
+
return negative ? "-" + out : out;
|
|
811
|
+
}
|
|
812
|
+
function percentOf(used, size) {
|
|
813
|
+
if (size <= 0) return null;
|
|
814
|
+
return `${(used / size * 100).toFixed(1)}%`;
|
|
815
|
+
}
|
|
816
|
+
function usageText(runtime) {
|
|
817
|
+
const { used, size } = runtime.state().usage;
|
|
818
|
+
if (size <= 0) return "not reported";
|
|
819
|
+
const base = `${formatNumber(used)} / ${formatNumber(size)}`;
|
|
820
|
+
const percent = percentOf(used, size);
|
|
821
|
+
return percent ? `${base} (${percent})` : base;
|
|
822
|
+
}
|
|
823
|
+
function compactPath(path) {
|
|
824
|
+
const home = (0, import_node_os2.homedir)();
|
|
825
|
+
if (!home) return path;
|
|
826
|
+
if (path === home) return "~";
|
|
827
|
+
const prefix = home + "/";
|
|
828
|
+
if (path.startsWith(prefix)) return "~" + path.slice(home.length);
|
|
829
|
+
return path;
|
|
830
|
+
}
|
|
831
|
+
function modelText(runtime) {
|
|
832
|
+
const current = runtime.agent().currentModel();
|
|
833
|
+
if (current) return current.label;
|
|
834
|
+
const configured = runtime.config().agent.model;
|
|
835
|
+
if (configured) return `${configured} (configured, not applied yet)`;
|
|
836
|
+
return "unknown (session not started)";
|
|
837
|
+
}
|
|
766
838
|
function handleSlashCommand(runtime, text, send) {
|
|
767
839
|
const [rawCommand, ...args] = text.split(/\s+/);
|
|
768
840
|
switch (rawCommand.toLowerCase()) {
|
|
769
841
|
case "/new":
|
|
770
|
-
handleNew(runtime, send);
|
|
842
|
+
void handleNew(runtime, send);
|
|
771
843
|
return null;
|
|
772
844
|
case "/stop":
|
|
773
845
|
handleStop(runtime, send);
|
|
@@ -779,7 +851,7 @@ function handleSlashCommand(runtime, text, send) {
|
|
|
779
851
|
handleAgent(runtime, args, send);
|
|
780
852
|
return null;
|
|
781
853
|
case "/model":
|
|
782
|
-
handleModel(runtime, args, send);
|
|
854
|
+
void handleModel(runtime, args, send);
|
|
783
855
|
return null;
|
|
784
856
|
case "/cost":
|
|
785
857
|
send(costText(runtime));
|
|
@@ -794,12 +866,18 @@ function handleSlashCommand(runtime, text, send) {
|
|
|
794
866
|
return text.slice(1);
|
|
795
867
|
}
|
|
796
868
|
}
|
|
797
|
-
function handleNew(runtime, send) {
|
|
798
|
-
runtime.
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
runtime.
|
|
869
|
+
async function handleNew(runtime, send) {
|
|
870
|
+
if (runtime.queue().status().busy) {
|
|
871
|
+
send(NEW_BUSY_TEXT);
|
|
872
|
+
return;
|
|
873
|
+
}
|
|
874
|
+
await runtime.serialize(async () => {
|
|
875
|
+
runtime.agent().resetSession();
|
|
876
|
+
const state = runtime.state();
|
|
877
|
+
state.acpSessionId = null;
|
|
878
|
+
state.usage = { used: 0, size: 0, cost: null };
|
|
879
|
+
runtime.saveState();
|
|
880
|
+
});
|
|
803
881
|
send("Session reset. Starting a new conversation.");
|
|
804
882
|
}
|
|
805
883
|
function handleStop(runtime, send) {
|
|
@@ -815,12 +893,18 @@ function handleStop(runtime, send) {
|
|
|
815
893
|
}
|
|
816
894
|
function handleAgent(runtime, args, send) {
|
|
817
895
|
const name = args.join(" ").trim();
|
|
896
|
+
const current = runtime.config().agent.name;
|
|
818
897
|
if (!name) {
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
898
|
+
send(
|
|
899
|
+
table(
|
|
900
|
+
["Agent", "Backend", ""],
|
|
901
|
+
AGENT_NAMES.map((entry) => [
|
|
902
|
+
entry,
|
|
903
|
+
AGENTS[entry].label,
|
|
904
|
+
entry === current ? "\u2190 current" : ""
|
|
905
|
+
])
|
|
906
|
+
)
|
|
907
|
+
);
|
|
824
908
|
return;
|
|
825
909
|
}
|
|
826
910
|
if (!isAgentName(name)) {
|
|
@@ -836,56 +920,95 @@ ${list}`);
|
|
|
836
920
|
(error) => send(`Error: ${error instanceof Error ? error.message : String(error)}`)
|
|
837
921
|
);
|
|
838
922
|
}
|
|
839
|
-
function handleModel(runtime, args, send) {
|
|
923
|
+
async function handleModel(runtime, args, send) {
|
|
840
924
|
const model = args.join(" ").trim();
|
|
841
925
|
if (!model) {
|
|
842
|
-
send(
|
|
926
|
+
send(modelReport(runtime));
|
|
927
|
+
return;
|
|
928
|
+
}
|
|
929
|
+
if (runtime.queue().status().busy) {
|
|
930
|
+
send(MODEL_BUSY_TEXT);
|
|
843
931
|
return;
|
|
844
932
|
}
|
|
845
933
|
const config = runtime.config();
|
|
846
934
|
const hadSession = runtime.agent().sessionId() !== null;
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
}
|
|
935
|
+
const supported = await runtime.serialize(async () => {
|
|
936
|
+
const accepted = await runtime.agent().setModel(model);
|
|
937
|
+
config.agent.model = model;
|
|
938
|
+
runtime.saveConfig();
|
|
939
|
+
return accepted;
|
|
940
|
+
});
|
|
941
|
+
const applied = runtime.agent().currentModel()?.label ?? model;
|
|
942
|
+
if (!supported) {
|
|
856
943
|
send(
|
|
857
|
-
|
|
944
|
+
`Model saved: ${model}. This agent does not expose a runtime model option; it will be used on the next session if supported.`
|
|
858
945
|
);
|
|
859
|
-
|
|
860
|
-
|
|
946
|
+
return;
|
|
947
|
+
}
|
|
948
|
+
send(
|
|
949
|
+
hadSession ? `Model switched to: ${applied}` : `Model set to: ${applied}. It will apply when the session starts.`
|
|
861
950
|
);
|
|
862
951
|
}
|
|
952
|
+
function modelReport(runtime) {
|
|
953
|
+
const current = runtime.agent().currentModel();
|
|
954
|
+
if (!current) {
|
|
955
|
+
const configured = runtime.config().agent.model;
|
|
956
|
+
const hint = configured ? `Configured: ${configured}
|
|
957
|
+
|
|
958
|
+
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.";
|
|
959
|
+
return `Current: unknown
|
|
960
|
+
|
|
961
|
+
${hint}`;
|
|
962
|
+
}
|
|
963
|
+
const lines = [`Current: ${current.label}`];
|
|
964
|
+
if (current.options.length > 0) {
|
|
965
|
+
lines.push(
|
|
966
|
+
"",
|
|
967
|
+
table(
|
|
968
|
+
["Model", ""],
|
|
969
|
+
current.options.map((option) => [
|
|
970
|
+
option.label,
|
|
971
|
+
option.value === current.value ? "\u2190 current" : ""
|
|
972
|
+
])
|
|
973
|
+
)
|
|
974
|
+
);
|
|
975
|
+
}
|
|
976
|
+
lines.push("", "Switch: /model <name>");
|
|
977
|
+
return lines.join("\n");
|
|
978
|
+
}
|
|
863
979
|
function statusText(runtime) {
|
|
864
980
|
const config = runtime.config();
|
|
865
981
|
const agent = runtime.agent();
|
|
866
982
|
const state = runtime.state();
|
|
867
983
|
const queue = runtime.queue().status();
|
|
868
|
-
const
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
984
|
+
const { used, size } = state.usage;
|
|
985
|
+
const summary = [
|
|
986
|
+
`**${config.agent.name}**`,
|
|
987
|
+
queue.busy ? "busy" : "idle",
|
|
988
|
+
size > 0 ? `${formatNumber(used)} / ${formatNumber(size)}` : null
|
|
989
|
+
].filter(Boolean).join(" \xB7 ");
|
|
990
|
+
const rows = [
|
|
991
|
+
["Agent", `${config.agent.name} (${AGENTS[config.agent.name].label})`],
|
|
992
|
+
["Session", agent.sessionId() ?? "none"],
|
|
993
|
+
["Work dir", compactPath(config.agent.cwd)],
|
|
994
|
+
["Model", modelText(runtime)],
|
|
995
|
+
["Status", queue.busy ? "busy" : "idle"],
|
|
996
|
+
["Queue", String(queue.queued)],
|
|
997
|
+
["Usage", usageText(runtime)],
|
|
998
|
+
["Capabilities", capabilitiesText(agent.capabilities())]
|
|
877
999
|
];
|
|
878
|
-
return
|
|
1000
|
+
return [summary, "", table(["Field", "Value"], rows)].join("\n");
|
|
879
1001
|
}
|
|
880
1002
|
function costText(runtime) {
|
|
881
1003
|
const usage = runtime.state().usage;
|
|
882
|
-
const
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
1004
|
+
const cost = usage.cost ? `${usage.cost.amount} ${usage.cost.currency}` : "not reported by this agent";
|
|
1005
|
+
return table(
|
|
1006
|
+
["Field", "Value"],
|
|
1007
|
+
[
|
|
1008
|
+
["Context", usageText(runtime)],
|
|
1009
|
+
["Cost", cost]
|
|
1010
|
+
]
|
|
1011
|
+
);
|
|
889
1012
|
}
|
|
890
1013
|
function capabilitiesText(caps) {
|
|
891
1014
|
const enabled = [
|
|
@@ -904,10 +1027,10 @@ var import_node_path4 = require("path");
|
|
|
904
1027
|
// src/core/state.ts
|
|
905
1028
|
var import_node_crypto = require("crypto");
|
|
906
1029
|
var import_node_fs3 = require("fs");
|
|
907
|
-
var
|
|
1030
|
+
var import_node_os3 = require("os");
|
|
908
1031
|
var import_node_path3 = require("path");
|
|
909
1032
|
function vbotHome() {
|
|
910
|
-
return process.env.VBOT_HOME?.trim() || (0, import_node_path3.join)((0,
|
|
1033
|
+
return process.env.VBOT_HOME?.trim() || (0, import_node_path3.join)((0, import_node_os3.homedir)(), ".vbot");
|
|
911
1034
|
}
|
|
912
1035
|
function stateDir() {
|
|
913
1036
|
return (0, import_node_path3.join)(vbotHome(), "state");
|
|
@@ -1281,13 +1404,17 @@ async function startBot(configPath) {
|
|
|
1281
1404
|
saveConfig: () => saveConfig(config, configPath)
|
|
1282
1405
|
};
|
|
1283
1406
|
holder.queue = new BotQueue((item, signal) => gate(() => processPrompt(ctx, item, signal)));
|
|
1284
|
-
const runtime = createRuntime(
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
switching
|
|
1289
|
-
|
|
1290
|
-
|
|
1407
|
+
const runtime = createRuntime(
|
|
1408
|
+
ctx,
|
|
1409
|
+
(task) => gate(task),
|
|
1410
|
+
(name) => {
|
|
1411
|
+
if (switching) return Promise.reject(new Error("Agent switch already in progress."));
|
|
1412
|
+
switching = true;
|
|
1413
|
+
return gate(() => switchAgent(ctx, name)).finally(() => {
|
|
1414
|
+
switching = false;
|
|
1415
|
+
});
|
|
1416
|
+
}
|
|
1417
|
+
);
|
|
1291
1418
|
const dispatch = createDispatcher(runtime);
|
|
1292
1419
|
let notifySignal = () => void 0;
|
|
1293
1420
|
const wire = new VicvicWire({
|
|
@@ -1348,7 +1475,7 @@ async function runBot(configPath) {
|
|
|
1348
1475
|
const bot = await startBot(configPath);
|
|
1349
1476
|
await bot.finished;
|
|
1350
1477
|
}
|
|
1351
|
-
function createRuntime(ctx, switchAgent2) {
|
|
1478
|
+
function createRuntime(ctx, gate, switchAgent2) {
|
|
1352
1479
|
return {
|
|
1353
1480
|
config: () => ctx.config,
|
|
1354
1481
|
agent: () => ctx.getAgent(),
|
|
@@ -1356,6 +1483,7 @@ function createRuntime(ctx, switchAgent2) {
|
|
|
1356
1483
|
state: () => ctx.state,
|
|
1357
1484
|
saveState: () => ctx.saveState(),
|
|
1358
1485
|
saveConfig: () => ctx.saveConfig(),
|
|
1486
|
+
serialize: (task) => gate(task),
|
|
1359
1487
|
switchAgent: switchAgent2
|
|
1360
1488
|
};
|
|
1361
1489
|
}
|