@wrongstack/acp 0.293.0 → 0.295.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/README.md +22 -2
- package/dist/agent/protocol-contract.d.ts +210 -0
- package/dist/agent/protocol-contract.d.ts.map +1 -0
- package/dist/agent/protocol-handler.d.ts +6 -189
- package/dist/agent/protocol-handler.d.ts.map +1 -1
- package/dist/agent/server-agent-turn.d.ts +7 -1
- package/dist/agent/server-agent-turn.d.ts.map +1 -1
- package/dist/agent/stdio-transport.d.ts +12 -1
- package/dist/agent/stdio-transport.d.ts.map +1 -1
- package/dist/agent/tools-registry.d.ts +1 -1
- package/dist/agent/tools-registry.d.ts.map +1 -1
- package/dist/agent/wrongstack-acp-agent.d.ts +2 -0
- package/dist/agent/wrongstack-acp-agent.d.ts.map +1 -1
- package/dist/agent.js +172 -22
- package/dist/agent.js.map +4 -4
- package/dist/client/acp-session.d.ts +13 -1
- package/dist/client/acp-session.d.ts.map +1 -1
- package/dist/client/index.d.ts +11 -9
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/terminal-server.d.ts +5 -0
- package/dist/client/terminal-server.d.ts.map +1 -1
- package/dist/client/tool-translator.d.ts +1 -1
- package/dist/client/tool-translator.d.ts.map +1 -1
- package/dist/client/trust-boundary-permission.d.ts +16 -0
- package/dist/client/trust-boundary-permission.d.ts.map +1 -0
- package/dist/client/websocket-transport.d.ts +6 -0
- package/dist/client/websocket-transport.d.ts.map +1 -1
- package/dist/client.js +429 -218
- package/dist/client.js.map +4 -4
- package/dist/index.d.ts +29 -29
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2555 -2250
- package/dist/index.js.map +4 -4
- package/dist/integration/acp-subagent-runner.d.ts +3 -3
- package/dist/integration/acp-subagent-runner.d.ts.map +1 -1
- package/dist/legacy.d.ts +8 -0
- package/dist/legacy.d.ts.map +1 -0
- package/dist/legacy.js +6 -0
- package/dist/legacy.js.map +7 -0
- package/dist/sdk.d.ts +10 -8
- package/dist/sdk.d.ts.map +1 -1
- package/dist/sdk.js +22 -0
- package/dist/sdk.js.map +3 -3
- package/dist/v1.d.ts +3 -0
- package/dist/v1.d.ts.map +1 -0
- package/dist/v1.js +12 -0
- package/dist/v1.js.map +7 -0
- package/dist/version.d.ts +2 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/wrongstack-acp-agent.js +107 -16
- package/dist/wrongstack-acp-agent.js.map +4 -4
- package/package.json +10 -2
package/dist/agent.js
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
// src/agent/stdio-transport.ts
|
|
2
|
-
import { expectDefined, writeErr } from "@wrongstack/core";
|
|
2
|
+
import { expectDefined, writeErr } from "@wrongstack/core/utils";
|
|
3
|
+
var DEFAULT_MAX_FRAME_CHARS = 20 * 1024 * 1024;
|
|
4
|
+
var DEFAULT_MAX_QUEUED_MESSAGES = 1e3;
|
|
5
|
+
function positiveLimit(value, fallback) {
|
|
6
|
+
return Number.isFinite(value) && (value ?? 0) > 0 ? Math.floor(value) : fallback;
|
|
7
|
+
}
|
|
3
8
|
var StdioTransport = class {
|
|
4
9
|
stdin = process.stdin;
|
|
5
10
|
stdout = process.stdout;
|
|
@@ -9,7 +14,14 @@ var StdioTransport = class {
|
|
|
9
14
|
closed = false;
|
|
10
15
|
resolveRead = null;
|
|
11
16
|
messageQueue = [];
|
|
12
|
-
|
|
17
|
+
maxFrameChars;
|
|
18
|
+
maxQueuedMessages;
|
|
19
|
+
constructor(opts = {}) {
|
|
20
|
+
this.maxFrameChars = positiveLimit(opts.maxFrameChars, DEFAULT_MAX_FRAME_CHARS);
|
|
21
|
+
this.maxQueuedMessages = positiveLimit(
|
|
22
|
+
opts.maxQueuedMessages,
|
|
23
|
+
DEFAULT_MAX_QUEUED_MESSAGES
|
|
24
|
+
);
|
|
13
25
|
this.stdin.resume();
|
|
14
26
|
this.stdin.setEncoding("utf8");
|
|
15
27
|
this.stdin.on("data", (chunk) => this.onData(chunk));
|
|
@@ -45,13 +57,34 @@ var StdioTransport = class {
|
|
|
45
57
|
this.stdin.pause();
|
|
46
58
|
this.resolveRead?.(null);
|
|
47
59
|
this.resolveRead = null;
|
|
60
|
+
this.buffer = "";
|
|
61
|
+
this.messageQueue.length = 0;
|
|
62
|
+
this.handlers.clear();
|
|
48
63
|
}
|
|
49
64
|
onData(chunk) {
|
|
50
65
|
this.buffer += chunk;
|
|
51
66
|
const lines = this.buffer.split("\n");
|
|
52
67
|
this.buffer = lines.pop() ?? "";
|
|
68
|
+
if (this.buffer.length > this.maxFrameChars) {
|
|
69
|
+
this.stderr.write(
|
|
70
|
+
`[wstack-acp frame error] pending frame exceeds ${this.maxFrameChars} characters
|
|
71
|
+
`,
|
|
72
|
+
"utf8"
|
|
73
|
+
);
|
|
74
|
+
this.close();
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
53
77
|
for (const raw of lines) {
|
|
54
78
|
if (!raw.trim()) continue;
|
|
79
|
+
if (raw.length > this.maxFrameChars) {
|
|
80
|
+
this.stderr.write(
|
|
81
|
+
`[wstack-acp frame error] frame exceeds ${this.maxFrameChars} characters
|
|
82
|
+
`,
|
|
83
|
+
"utf8"
|
|
84
|
+
);
|
|
85
|
+
this.close();
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
55
88
|
try {
|
|
56
89
|
this.dispatch(JSON.parse(raw));
|
|
57
90
|
} catch (err) {
|
|
@@ -66,6 +99,15 @@ var StdioTransport = class {
|
|
|
66
99
|
this.resolveRead = null;
|
|
67
100
|
resolve(msg);
|
|
68
101
|
} else {
|
|
102
|
+
if (this.messageQueue.length >= this.maxQueuedMessages) {
|
|
103
|
+
this.stderr.write(
|
|
104
|
+
`[wstack-acp queue error] pending message queue exceeds ${this.maxQueuedMessages} entries
|
|
105
|
+
`,
|
|
106
|
+
"utf8"
|
|
107
|
+
);
|
|
108
|
+
this.close();
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
69
111
|
this.messageQueue.push(msg);
|
|
70
112
|
}
|
|
71
113
|
for (const handler of this.handlers) {
|
|
@@ -78,9 +120,7 @@ var StdioTransport = class {
|
|
|
78
120
|
}
|
|
79
121
|
}
|
|
80
122
|
handleClose() {
|
|
81
|
-
this.
|
|
82
|
-
this.resolveRead?.(null);
|
|
83
|
-
this.resolveRead = null;
|
|
123
|
+
this.close();
|
|
84
124
|
}
|
|
85
125
|
failAll(err) {
|
|
86
126
|
this.stderr.write(`[wstack-acp stdin error] ${err.message}
|
|
@@ -206,11 +246,28 @@ function toolToPriority(tool) {
|
|
|
206
246
|
// src/types/acp-v1.ts
|
|
207
247
|
var ACP_PROTOCOL_VERSION = 1;
|
|
208
248
|
|
|
209
|
-
// src/
|
|
249
|
+
// src/version.ts
|
|
250
|
+
import { createRequire } from "node:module";
|
|
251
|
+
var require2 = createRequire(import.meta.url);
|
|
252
|
+
function readPackageVersion() {
|
|
253
|
+
try {
|
|
254
|
+
const packageJson = require2("../package.json");
|
|
255
|
+
if (typeof packageJson.version === "string" && packageJson.version.length > 0) {
|
|
256
|
+
return packageJson.version;
|
|
257
|
+
}
|
|
258
|
+
} catch {
|
|
259
|
+
}
|
|
260
|
+
return "dev";
|
|
261
|
+
}
|
|
262
|
+
var ACP_PACKAGE_VERSION = readPackageVersion();
|
|
263
|
+
|
|
264
|
+
// src/agent/protocol-contract.ts
|
|
210
265
|
function toWire(msg) {
|
|
211
266
|
return msg;
|
|
212
267
|
}
|
|
213
|
-
var WRONGSTACK_VERSION =
|
|
268
|
+
var WRONGSTACK_VERSION = ACP_PACKAGE_VERSION;
|
|
269
|
+
|
|
270
|
+
// src/agent/protocol-handler.ts
|
|
214
271
|
var WRONGSTACK_AUTH_METHODS = [
|
|
215
272
|
{
|
|
216
273
|
id: "wrongstack-auth",
|
|
@@ -221,6 +278,7 @@ var WRONGSTACK_AUTH_METHODS = [
|
|
|
221
278
|
}
|
|
222
279
|
];
|
|
223
280
|
var DEFAULT_MODE_ID = "code";
|
|
281
|
+
var DEFAULT_MAX_SESSIONS = 64;
|
|
224
282
|
var DEFAULT_MODES = [
|
|
225
283
|
{
|
|
226
284
|
id: DEFAULT_MODE_ID,
|
|
@@ -238,6 +296,8 @@ var ACPProtocolHandler = class {
|
|
|
238
296
|
agentName;
|
|
239
297
|
replayFor;
|
|
240
298
|
seedFor;
|
|
299
|
+
disposeFor;
|
|
300
|
+
maxSessions;
|
|
241
301
|
store;
|
|
242
302
|
initialized = false;
|
|
243
303
|
clientCapabilities = {};
|
|
@@ -258,6 +318,8 @@ var ACPProtocolHandler = class {
|
|
|
258
318
|
this.agentName = opts.agentName ?? "wrongstack";
|
|
259
319
|
this.replayFor = opts.replayFor;
|
|
260
320
|
this.seedFor = opts.seedFor;
|
|
321
|
+
this.disposeFor = opts.disposeFor;
|
|
322
|
+
this.maxSessions = Number.isFinite(opts.maxSessions) && (opts.maxSessions ?? 0) > 0 ? Math.floor(opts.maxSessions) : DEFAULT_MAX_SESSIONS;
|
|
261
323
|
this.store = opts.store;
|
|
262
324
|
if (typeof this.transport.onMessage === "function") {
|
|
263
325
|
this.transport.onMessage((m) => this.maybeResolvePending(m));
|
|
@@ -315,8 +377,9 @@ var ACPProtocolHandler = class {
|
|
|
315
377
|
}
|
|
316
378
|
/** Abort all active turns and drop session state. */
|
|
317
379
|
close() {
|
|
318
|
-
for (const [, session] of this.sessions) {
|
|
380
|
+
for (const [sessionId, session] of this.sessions) {
|
|
319
381
|
session.abort.abort();
|
|
382
|
+
this.disposeSession(sessionId);
|
|
320
383
|
}
|
|
321
384
|
this.sessions.clear();
|
|
322
385
|
for (const [, p] of this.pendingOut) {
|
|
@@ -325,6 +388,12 @@ var ACPProtocolHandler = class {
|
|
|
325
388
|
}
|
|
326
389
|
this.pendingOut.clear();
|
|
327
390
|
}
|
|
391
|
+
disposeSession(sessionId) {
|
|
392
|
+
try {
|
|
393
|
+
this.disposeFor?.(sessionId);
|
|
394
|
+
} catch {
|
|
395
|
+
}
|
|
396
|
+
}
|
|
328
397
|
// ────────────────────────────────────────────────────────────────────
|
|
329
398
|
// Requests
|
|
330
399
|
// ────────────────────────────────────────────────────────────────────
|
|
@@ -444,6 +513,10 @@ var ACPProtocolHandler = class {
|
|
|
444
513
|
return false;
|
|
445
514
|
}
|
|
446
515
|
async handleSessionNew(id, params) {
|
|
516
|
+
if (this.sessions.size >= this.maxSessions) {
|
|
517
|
+
await this.sendError(id, -32e3, `active session limit reached (${this.maxSessions})`);
|
|
518
|
+
return false;
|
|
519
|
+
}
|
|
447
520
|
const p = params ?? {};
|
|
448
521
|
const cwd = typeof p.cwd === "string" ? p.cwd : this.defaultCwd;
|
|
449
522
|
const sessionId = `sess_${this.allocId()}`;
|
|
@@ -494,6 +567,10 @@ var ACPProtocolHandler = class {
|
|
|
494
567
|
if (!existing && sessionId && this.store) {
|
|
495
568
|
const persisted = await this.store.load(sessionId);
|
|
496
569
|
if (persisted) {
|
|
570
|
+
if (this.sessions.size >= this.maxSessions) {
|
|
571
|
+
await this.sendError(id, -32e3, `active session limit reached (${this.maxSessions})`);
|
|
572
|
+
return false;
|
|
573
|
+
}
|
|
497
574
|
const restored = {
|
|
498
575
|
id: sessionId,
|
|
499
576
|
cwd: persisted.cwd ?? loadCwd ?? this.defaultCwd,
|
|
@@ -589,7 +666,10 @@ var ACPProtocolHandler = class {
|
|
|
589
666
|
return false;
|
|
590
667
|
}
|
|
591
668
|
session.abort.abort();
|
|
592
|
-
if (sessionId)
|
|
669
|
+
if (sessionId) {
|
|
670
|
+
this.sessions.delete(sessionId);
|
|
671
|
+
this.disposeSession(sessionId);
|
|
672
|
+
}
|
|
593
673
|
await this.transport.send(toWire({
|
|
594
674
|
jsonrpc: "2.0",
|
|
595
675
|
id,
|
|
@@ -611,6 +691,7 @@ var ACPProtocolHandler = class {
|
|
|
611
691
|
const session = this.sessions.get(sessionId);
|
|
612
692
|
session.abort.abort();
|
|
613
693
|
this.sessions.delete(sessionId);
|
|
694
|
+
this.disposeSession(sessionId);
|
|
614
695
|
await this.transport.send(toWire({
|
|
615
696
|
jsonrpc: "2.0",
|
|
616
697
|
id,
|
|
@@ -626,6 +707,10 @@ var ACPProtocolHandler = class {
|
|
|
626
707
|
await this.sendError(id, -32e3, `session not found: ${sourceId}`);
|
|
627
708
|
return false;
|
|
628
709
|
}
|
|
710
|
+
if (this.sessions.size >= this.maxSessions) {
|
|
711
|
+
await this.sendError(id, -32e3, `active session limit reached (${this.maxSessions})`);
|
|
712
|
+
return false;
|
|
713
|
+
}
|
|
629
714
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
630
715
|
const sessionId = `sess_${this.allocId()}`;
|
|
631
716
|
const forked = {
|
|
@@ -1094,7 +1179,7 @@ var WsBridgeTransport = class {
|
|
|
1094
1179
|
// src/agent/wrongstack-acp-agent.ts
|
|
1095
1180
|
import { fileURLToPath } from "node:url";
|
|
1096
1181
|
import { createServer } from "node:http";
|
|
1097
|
-
import { writeErr as writeErr2 } from "@wrongstack/core";
|
|
1182
|
+
import { writeErr as writeErr2 } from "@wrongstack/core/utils";
|
|
1098
1183
|
var WrongStackACPServer = class {
|
|
1099
1184
|
transport;
|
|
1100
1185
|
handler;
|
|
@@ -1113,6 +1198,7 @@ var WrongStackACPServer = class {
|
|
|
1113
1198
|
agentName: opts.agentName,
|
|
1114
1199
|
...opts.replayFor ? { replayFor: opts.replayFor } : {},
|
|
1115
1200
|
...opts.seedFor ? { seedFor: opts.seedFor } : {},
|
|
1201
|
+
...opts.disposeFor ? { disposeFor: opts.disposeFor } : {},
|
|
1116
1202
|
...opts.store ? { store: opts.store } : {}
|
|
1117
1203
|
});
|
|
1118
1204
|
}
|
|
@@ -1134,13 +1220,17 @@ var WrongStackACPServer = class {
|
|
|
1134
1220
|
this.transport.sendStartupMarker();
|
|
1135
1221
|
}
|
|
1136
1222
|
this.running = true;
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1223
|
+
try {
|
|
1224
|
+
while (this.running) {
|
|
1225
|
+
const msg = await this.transport.read();
|
|
1226
|
+
if (!msg) break;
|
|
1227
|
+
const terminal = await this.handler.handleMessage(msg);
|
|
1228
|
+
if (terminal) break;
|
|
1229
|
+
}
|
|
1230
|
+
} finally {
|
|
1231
|
+
this.handler.close();
|
|
1232
|
+
this.transport.close();
|
|
1142
1233
|
}
|
|
1143
|
-
this.transport.close();
|
|
1144
1234
|
}
|
|
1145
1235
|
async startHttp(port) {
|
|
1146
1236
|
const host = this.options.host ?? "127.0.0.1";
|
|
@@ -1255,6 +1345,7 @@ var WrongStackACPServer = class {
|
|
|
1255
1345
|
/** Stop the server. */
|
|
1256
1346
|
stop() {
|
|
1257
1347
|
this.running = false;
|
|
1348
|
+
this.handler.close();
|
|
1258
1349
|
this.transport.close();
|
|
1259
1350
|
if (this.httpServer) {
|
|
1260
1351
|
this.httpServer.close();
|
|
@@ -1283,8 +1374,11 @@ function makeACPServerAgentTurn(opts) {
|
|
|
1283
1374
|
const agents = /* @__PURE__ */ new Map();
|
|
1284
1375
|
const timeouts = /* @__PURE__ */ new Map();
|
|
1285
1376
|
const history = /* @__PURE__ */ new Map();
|
|
1377
|
+
const historyBytes = /* @__PURE__ */ new Map();
|
|
1286
1378
|
const pendingSeed = /* @__PURE__ */ new Set();
|
|
1287
1379
|
const timeoutMs = opts.timeoutMs ?? 5 * 6e4;
|
|
1380
|
+
const maxHistoryEntries = finitePositiveLimit(opts.maxHistoryEntries, 1e3);
|
|
1381
|
+
const maxHistoryBytes = finitePositiveLimit(opts.maxHistoryBytes, 8 * 1024 * 1024);
|
|
1288
1382
|
const turn = async (input, emit, api) => {
|
|
1289
1383
|
let agent = agents.get(input.sessionId);
|
|
1290
1384
|
if (!agent) {
|
|
@@ -1348,13 +1442,36 @@ function makeACPServerAgentTurn(opts) {
|
|
|
1348
1442
|
}
|
|
1349
1443
|
const userText = promptToText(input.prompt);
|
|
1350
1444
|
const hist = history.get(input.sessionId) ?? [];
|
|
1445
|
+
let retainedHistoryBytes = historyBytes.get(input.sessionId) ?? 0;
|
|
1351
1446
|
if (userText) {
|
|
1352
|
-
|
|
1447
|
+
const update = {
|
|
1448
|
+
sessionUpdate: "user_message_chunk",
|
|
1449
|
+
content: { type: "text", text: userText }
|
|
1450
|
+
};
|
|
1451
|
+
hist.push(update);
|
|
1452
|
+
retainedHistoryBytes += replayEntryBytes(update);
|
|
1353
1453
|
}
|
|
1354
1454
|
if (text) {
|
|
1355
|
-
|
|
1455
|
+
const update = {
|
|
1456
|
+
sessionUpdate: "agent_message_chunk",
|
|
1457
|
+
content: { type: "text", text }
|
|
1458
|
+
};
|
|
1459
|
+
hist.push(update);
|
|
1460
|
+
retainedHistoryBytes += replayEntryBytes(update);
|
|
1461
|
+
}
|
|
1462
|
+
retainedHistoryBytes = trimHistory(
|
|
1463
|
+
hist,
|
|
1464
|
+
retainedHistoryBytes,
|
|
1465
|
+
maxHistoryEntries,
|
|
1466
|
+
maxHistoryBytes
|
|
1467
|
+
);
|
|
1468
|
+
if (hist.length > 0) {
|
|
1469
|
+
history.set(input.sessionId, hist);
|
|
1470
|
+
historyBytes.set(input.sessionId, retainedHistoryBytes);
|
|
1471
|
+
} else {
|
|
1472
|
+
history.delete(input.sessionId);
|
|
1473
|
+
historyBytes.delete(input.sessionId);
|
|
1356
1474
|
}
|
|
1357
|
-
if (hist.length > 0) history.set(input.sessionId, hist);
|
|
1358
1475
|
const plan = extractPlan(result);
|
|
1359
1476
|
if (plan.length > 0) {
|
|
1360
1477
|
emit({
|
|
@@ -1388,13 +1505,46 @@ function makeACPServerAgentTurn(opts) {
|
|
|
1388
1505
|
for (const u of unsub) u();
|
|
1389
1506
|
}
|
|
1390
1507
|
};
|
|
1391
|
-
const replay = (sessionId) =>
|
|
1508
|
+
const replay = (sessionId) => [
|
|
1509
|
+
...history.get(sessionId) ?? []
|
|
1510
|
+
];
|
|
1392
1511
|
const seed = (sessionId, incoming) => {
|
|
1393
1512
|
if (incoming.length === 0) return;
|
|
1394
|
-
|
|
1513
|
+
const seeded = [...incoming];
|
|
1514
|
+
const retainedBytes = trimHistory(
|
|
1515
|
+
seeded,
|
|
1516
|
+
seeded.reduce((total, entry) => total + replayEntryBytes(entry), 0),
|
|
1517
|
+
maxHistoryEntries,
|
|
1518
|
+
maxHistoryBytes
|
|
1519
|
+
);
|
|
1520
|
+
history.set(sessionId, seeded);
|
|
1521
|
+
historyBytes.set(sessionId, retainedBytes);
|
|
1395
1522
|
pendingSeed.add(sessionId);
|
|
1396
1523
|
};
|
|
1397
|
-
|
|
1524
|
+
const dispose = (sessionId) => {
|
|
1525
|
+
const timer = timeouts.get(sessionId);
|
|
1526
|
+
if (timer) clearTimeout(timer);
|
|
1527
|
+
timeouts.delete(sessionId);
|
|
1528
|
+
agents.delete(sessionId);
|
|
1529
|
+
history.delete(sessionId);
|
|
1530
|
+
historyBytes.delete(sessionId);
|
|
1531
|
+
pendingSeed.delete(sessionId);
|
|
1532
|
+
};
|
|
1533
|
+
return Object.assign(turn, { replay, seed, dispose });
|
|
1534
|
+
}
|
|
1535
|
+
function finitePositiveLimit(value, fallback) {
|
|
1536
|
+
return Number.isFinite(value) && (value ?? 0) > 0 ? Math.floor(value) : fallback;
|
|
1537
|
+
}
|
|
1538
|
+
function trimHistory(entries, retainedBytes, maxEntries, maxBytes) {
|
|
1539
|
+
while (entries.length > maxEntries || retainedBytes > maxBytes) {
|
|
1540
|
+
const removed = entries.shift();
|
|
1541
|
+
if (!removed) break;
|
|
1542
|
+
retainedBytes -= replayEntryBytes(removed);
|
|
1543
|
+
}
|
|
1544
|
+
return Math.max(0, retainedBytes);
|
|
1545
|
+
}
|
|
1546
|
+
function replayEntryBytes(entry) {
|
|
1547
|
+
return Buffer.byteLength(JSON.stringify(entry), "utf8");
|
|
1398
1548
|
}
|
|
1399
1549
|
function seedAgentContext(agent, history) {
|
|
1400
1550
|
const state = agent.ctx?.state;
|