@wrongstack/webui 0.1.9 → 0.2.0
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/assets/index-C1jTV4WR.js +609 -0
- package/dist/assets/index-CRAjL4S_.css +1 -0
- package/dist/index.css +25 -0
- package/dist/index.css.map +1 -1
- package/dist/index.html +2 -2
- package/dist/index.js +4920 -4595
- package/dist/index.js.map +1 -1
- package/dist/server/entry.js +98 -33
- package/dist/server/entry.js.map +1 -1
- package/dist/server/index.js +98 -33
- package/dist/server/index.js.map +1 -1
- package/package.json +4 -4
- package/dist/assets/index-DfJLWoIU.css +0 -1
- package/dist/assets/index-Dt6Hlw_3.js +0 -607
package/dist/server/index.js
CHANGED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
import * as fs from "fs/promises";
|
|
3
3
|
import * as os from "os";
|
|
4
4
|
import * as path from "path";
|
|
5
|
-
import { WebSocketServer, WebSocket } from "ws";
|
|
6
5
|
import {
|
|
7
6
|
Agent,
|
|
8
7
|
AutoCompactionMiddleware,
|
|
@@ -13,6 +12,7 @@ import {
|
|
|
13
12
|
DefaultErrorHandler,
|
|
14
13
|
DefaultLogger,
|
|
15
14
|
DefaultMemoryStore,
|
|
15
|
+
DefaultModeStore,
|
|
16
16
|
DefaultModelsRegistry,
|
|
17
17
|
DefaultPathResolver,
|
|
18
18
|
DefaultPermissionPolicy,
|
|
@@ -21,25 +21,22 @@ import {
|
|
|
21
21
|
DefaultSecretVault,
|
|
22
22
|
DefaultSessionStore,
|
|
23
23
|
DefaultSkillLoader,
|
|
24
|
-
DefaultModeStore,
|
|
25
24
|
DefaultSystemPromptBuilder,
|
|
26
25
|
DefaultTokenCounter,
|
|
27
26
|
EventBus,
|
|
28
27
|
HybridCompactor,
|
|
29
28
|
ProviderRegistry,
|
|
30
|
-
ToolRegistry,
|
|
31
29
|
TOKENS,
|
|
32
|
-
|
|
30
|
+
ToolRegistry,
|
|
33
31
|
atomicWrite,
|
|
32
|
+
createDefaultPipelines,
|
|
34
33
|
migratePlaintextSecrets,
|
|
35
34
|
resolveWstackPaths
|
|
36
35
|
} from "@wrongstack/core";
|
|
37
|
-
import {
|
|
38
|
-
|
|
39
|
-
makeProviderFromConfig
|
|
40
|
-
} from "@wrongstack/providers";
|
|
36
|
+
import { buildProviderFactoriesFromRegistry, makeProviderFromConfig } from "@wrongstack/providers";
|
|
37
|
+
import { forgetTool, rememberTool } from "@wrongstack/tools";
|
|
41
38
|
import { builtinTools } from "@wrongstack/tools/builtin";
|
|
42
|
-
import {
|
|
39
|
+
import { WebSocket, WebSocketServer } from "ws";
|
|
43
40
|
async function bootConfig() {
|
|
44
41
|
const cwd = process.cwd();
|
|
45
42
|
const pathResolver = new DefaultPathResolver(cwd);
|
|
@@ -98,11 +95,14 @@ async function startWebUI(opts = {}) {
|
|
|
98
95
|
container.bind(TOKENS.RetryPolicy, () => new DefaultRetryPolicy());
|
|
99
96
|
container.bind(TOKENS.ErrorHandler, () => new DefaultErrorHandler());
|
|
100
97
|
container.bind(TOKENS.ModelsRegistry, () => modelsRegistry);
|
|
101
|
-
container.bind(
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
98
|
+
container.bind(
|
|
99
|
+
TOKENS.PermissionPolicy,
|
|
100
|
+
() => new DefaultPermissionPolicy({
|
|
101
|
+
trustFile: wpaths.projectTrust,
|
|
102
|
+
yolo: false,
|
|
103
|
+
promptDelegate: void 0
|
|
104
|
+
})
|
|
105
|
+
);
|
|
106
106
|
const providerRegistry = new ProviderRegistry();
|
|
107
107
|
try {
|
|
108
108
|
const factories = await buildProviderFactoriesFromRegistry({
|
|
@@ -253,10 +253,24 @@ async function startWebUI(opts = {}) {
|
|
|
253
253
|
mode: modeId
|
|
254
254
|
};
|
|
255
255
|
}
|
|
256
|
-
const
|
|
257
|
-
|
|
256
|
+
const verifyClient = (info) => {
|
|
257
|
+
const origin = info.origin;
|
|
258
|
+
if (!origin) return true;
|
|
259
|
+
try {
|
|
260
|
+
const { hostname } = new URL(origin);
|
|
261
|
+
return hostname === "localhost" || hostname === "127.0.0.1" || hostname === "::1";
|
|
262
|
+
} catch {
|
|
263
|
+
return false;
|
|
264
|
+
}
|
|
265
|
+
};
|
|
266
|
+
const wssPrimary = new WebSocketServer({
|
|
267
|
+
port: wsPort,
|
|
268
|
+
host: wsHost,
|
|
269
|
+
verifyClient
|
|
270
|
+
});
|
|
271
|
+
const wssSecondary = wsHost === "127.0.0.1" ? new WebSocketServer({ port: wsPort, host: "::1", verifyClient }) : null;
|
|
258
272
|
const clients = /* @__PURE__ */ new Map();
|
|
259
|
-
let
|
|
273
|
+
let runLock = null;
|
|
260
274
|
console.log(
|
|
261
275
|
`[WebUI] WebSocket server running on ws://${wsHost}:${wsPort}` + (wssSecondary ? ` (and ws://[::1]:${wsPort})` : "")
|
|
262
276
|
);
|
|
@@ -270,6 +284,9 @@ async function startWebUI(opts = {}) {
|
|
|
270
284
|
events.on("provider.text_delta", (e) => {
|
|
271
285
|
broadcast({ type: "provider.text_delta", payload: { text: e.text, messageId: "current" } });
|
|
272
286
|
});
|
|
287
|
+
events.on("provider.thinking_delta", (e) => {
|
|
288
|
+
broadcast({ type: "provider.thinking_delta", payload: { text: e.text } });
|
|
289
|
+
});
|
|
273
290
|
events.on("tool.started", (e) => {
|
|
274
291
|
broadcast({
|
|
275
292
|
type: "tool.started",
|
|
@@ -391,10 +408,20 @@ async function startWebUI(opts = {}) {
|
|
|
391
408
|
switch (msg.type) {
|
|
392
409
|
case "user_message": {
|
|
393
410
|
const content = msg.payload.content;
|
|
394
|
-
|
|
395
|
-
|
|
411
|
+
if (runLock) {
|
|
412
|
+
send(ws, {
|
|
413
|
+
type: "error",
|
|
414
|
+
payload: {
|
|
415
|
+
phase: "user_message",
|
|
416
|
+
message: "Agent is already processing a request. Wait for the current run to finish."
|
|
417
|
+
}
|
|
418
|
+
});
|
|
419
|
+
break;
|
|
420
|
+
}
|
|
421
|
+
runLock = new AbortController();
|
|
422
|
+
const thisRun = runLock;
|
|
396
423
|
try {
|
|
397
|
-
const result = await agent.run(content, { signal:
|
|
424
|
+
const result = await agent.run(content, { signal: thisRun.signal });
|
|
398
425
|
send(ws, {
|
|
399
426
|
type: "run.result",
|
|
400
427
|
payload: {
|
|
@@ -417,12 +444,14 @@ async function startWebUI(opts = {}) {
|
|
|
417
444
|
}
|
|
418
445
|
});
|
|
419
446
|
} finally {
|
|
420
|
-
|
|
447
|
+
if (runLock === thisRun) {
|
|
448
|
+
runLock = null;
|
|
449
|
+
}
|
|
421
450
|
}
|
|
422
451
|
break;
|
|
423
452
|
}
|
|
424
453
|
case "abort":
|
|
425
|
-
|
|
454
|
+
runLock?.abort();
|
|
426
455
|
broadcast({ type: "error", payload: { phase: "abort", message: "User aborted" } });
|
|
427
456
|
break;
|
|
428
457
|
case "ping":
|
|
@@ -468,10 +497,7 @@ async function startWebUI(opts = {}) {
|
|
|
468
497
|
return String(c);
|
|
469
498
|
}
|
|
470
499
|
};
|
|
471
|
-
const sysTokens = context.systemPrompt.reduce(
|
|
472
|
-
(acc, b) => acc + estimate(b.text ?? ""),
|
|
473
|
-
0
|
|
474
|
-
);
|
|
500
|
+
const sysTokens = context.systemPrompt.reduce((acc, b) => acc + estimate(b.text ?? ""), 0);
|
|
475
501
|
const tools = toolRegistry.list();
|
|
476
502
|
const toolBreakdown = tools.map((t) => {
|
|
477
503
|
const schema = t.inputSchema ?? {};
|
|
@@ -511,7 +537,11 @@ async function startWebUI(opts = {}) {
|
|
|
511
537
|
total,
|
|
512
538
|
systemPrompt: sysTokens,
|
|
513
539
|
tools: { total: toolTokens, count: tools.length, breakdown: toolBreakdown },
|
|
514
|
-
messages: {
|
|
540
|
+
messages: {
|
|
541
|
+
total: msgTokens,
|
|
542
|
+
count: context.messages.length,
|
|
543
|
+
breakdown: messageBreakdown
|
|
544
|
+
}
|
|
515
545
|
}
|
|
516
546
|
});
|
|
517
547
|
break;
|
|
@@ -786,7 +816,11 @@ async function startWebUI(opts = {}) {
|
|
|
786
816
|
const { text, scope } = msg.payload;
|
|
787
817
|
try {
|
|
788
818
|
const removed = await memoryStore.forget(text, scope ?? "project-memory");
|
|
789
|
-
sendResult(
|
|
819
|
+
sendResult(
|
|
820
|
+
ws,
|
|
821
|
+
removed > 0,
|
|
822
|
+
removed > 0 ? `Removed ${removed} entr${removed === 1 ? "y" : "ies"}` : "No matching entries"
|
|
823
|
+
);
|
|
790
824
|
} catch (err) {
|
|
791
825
|
sendResult(ws, false, err instanceof Error ? err.message : String(err));
|
|
792
826
|
}
|
|
@@ -819,7 +853,11 @@ async function startWebUI(opts = {}) {
|
|
|
819
853
|
} catch (err) {
|
|
820
854
|
send(ws, {
|
|
821
855
|
type: "skills.list",
|
|
822
|
-
payload: {
|
|
856
|
+
payload: {
|
|
857
|
+
skills: [],
|
|
858
|
+
enabled: true,
|
|
859
|
+
error: err instanceof Error ? err.message : String(err)
|
|
860
|
+
}
|
|
823
861
|
});
|
|
824
862
|
}
|
|
825
863
|
break;
|
|
@@ -894,7 +932,8 @@ async function startWebUI(opts = {}) {
|
|
|
894
932
|
for (const e of entries) {
|
|
895
933
|
if (results.length >= 600) return;
|
|
896
934
|
if (e.name.startsWith(".") && e.name !== ".wrongstack" && e.name !== ".env.example") {
|
|
897
|
-
if (e.name !== ".gitignore" && e.name !== ".eslintrc" && e.name !== ".prettierrc")
|
|
935
|
+
if (e.name !== ".gitignore" && e.name !== ".eslintrc" && e.name !== ".prettierrc")
|
|
936
|
+
continue;
|
|
898
937
|
}
|
|
899
938
|
const childRel = rel ? `${rel}/${e.name}` : e.name;
|
|
900
939
|
if (e.isDirectory()) {
|
|
@@ -948,7 +987,11 @@ async function startWebUI(opts = {}) {
|
|
|
948
987
|
} catch (err) {
|
|
949
988
|
send(ws, {
|
|
950
989
|
type: "modes.list",
|
|
951
|
-
payload: {
|
|
990
|
+
payload: {
|
|
991
|
+
modes: [],
|
|
992
|
+
activeId: "default",
|
|
993
|
+
error: err instanceof Error ? err.message : String(err)
|
|
994
|
+
}
|
|
952
995
|
});
|
|
953
996
|
}
|
|
954
997
|
break;
|
|
@@ -964,6 +1007,22 @@ async function startWebUI(opts = {}) {
|
|
|
964
1007
|
await modeStore.setActiveMode(id);
|
|
965
1008
|
}
|
|
966
1009
|
modeId = id;
|
|
1010
|
+
const modePrompt2 = id === "default" ? "" : (await modeStore.getMode(id))?.prompt ?? "";
|
|
1011
|
+
const freshBuilder = new DefaultSystemPromptBuilder({
|
|
1012
|
+
memoryStore,
|
|
1013
|
+
skillLoader,
|
|
1014
|
+
modeStore,
|
|
1015
|
+
modeId: id,
|
|
1016
|
+
modePrompt: modePrompt2,
|
|
1017
|
+
modelCapabilities
|
|
1018
|
+
});
|
|
1019
|
+
context.systemPrompt = await freshBuilder.build({
|
|
1020
|
+
cwd: projectRoot,
|
|
1021
|
+
projectRoot,
|
|
1022
|
+
tools: toolRegistry.list(),
|
|
1023
|
+
provider: config.provider,
|
|
1024
|
+
model: config.model
|
|
1025
|
+
});
|
|
967
1026
|
sendResult(ws, true, `Switched to mode "${id}"`);
|
|
968
1027
|
broadcast({
|
|
969
1028
|
type: "session.start",
|
|
@@ -1125,7 +1184,9 @@ async function startWebUI(opts = {}) {
|
|
|
1125
1184
|
baseUrl: payload.baseUrl
|
|
1126
1185
|
};
|
|
1127
1186
|
if (payload.apiKey) {
|
|
1128
|
-
newProv.apiKeys = [
|
|
1187
|
+
newProv.apiKeys = [
|
|
1188
|
+
{ label: "default", apiKey: payload.apiKey, createdAt: (/* @__PURE__ */ new Date()).toISOString() }
|
|
1189
|
+
];
|
|
1129
1190
|
newProv.activeKey = "default";
|
|
1130
1191
|
}
|
|
1131
1192
|
providers[payload.id] = newProv;
|
|
@@ -1152,7 +1213,11 @@ async function startWebUI(opts = {}) {
|
|
|
1152
1213
|
const shutdown = async () => {
|
|
1153
1214
|
console.log("[WebUI] Shutting down...");
|
|
1154
1215
|
try {
|
|
1155
|
-
await session.append({
|
|
1216
|
+
await session.append({
|
|
1217
|
+
type: "session_end",
|
|
1218
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1219
|
+
usage: tokenCounter.total()
|
|
1220
|
+
});
|
|
1156
1221
|
await session.close();
|
|
1157
1222
|
} catch (e) {
|
|
1158
1223
|
console.warn("[WebUI] Error closing session:", e);
|