@ycodium-ai/agent-fake 0.2.3158 → 0.2.3180
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/index.js +132 -59
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -57,6 +57,8 @@ var FAKE_CARD = {
|
|
|
57
57
|
},
|
|
58
58
|
streamChunks: { type: "integer", minimum: 1 },
|
|
59
59
|
streamIntervalMs: { type: "integer", minimum: 0 },
|
|
60
|
+
// 仅探针与测试故意违约:stop 收完后台轮但不发 session.exited,事件流仍打开;默认关闭,不进设置表单。
|
|
61
|
+
stopWithoutExit: { type: "boolean" },
|
|
60
62
|
// lane 的 hold 最多等插话多久;不进设置表单。
|
|
61
63
|
holdMs: { type: "integer", minimum: 0 },
|
|
62
64
|
// stream 脚本循环切片的源文本;性能探针用它换上可控的长回复,不进设置表单。
|
|
@@ -89,6 +91,90 @@ var FAKE_CARD = {
|
|
|
89
91
|
// src/fakeInstance.ts
|
|
90
92
|
import * as NodeCrypto from "node:crypto";
|
|
91
93
|
|
|
94
|
+
// src/fakeEventBus.ts
|
|
95
|
+
var EventBus = class {
|
|
96
|
+
#queued = [];
|
|
97
|
+
#waiters = [];
|
|
98
|
+
#closed = false;
|
|
99
|
+
push(event) {
|
|
100
|
+
const waiter = this.#waiters.shift();
|
|
101
|
+
if (waiter) {
|
|
102
|
+
waiter({ done: false, value: event });
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
this.#queued.push(event);
|
|
106
|
+
}
|
|
107
|
+
close() {
|
|
108
|
+
if (this.#closed) return;
|
|
109
|
+
this.#closed = true;
|
|
110
|
+
while (this.#waiters.length > 0) {
|
|
111
|
+
this.#waiters.shift()?.({ done: true, value: void 0 });
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
[Symbol.asyncIterator]() {
|
|
115
|
+
return {
|
|
116
|
+
next: () => {
|
|
117
|
+
const queued = this.#queued.shift();
|
|
118
|
+
if (queued !== void 0) return Promise.resolve({ done: false, value: queued });
|
|
119
|
+
if (this.#closed) return Promise.resolve({ done: true, value: void 0 });
|
|
120
|
+
return new Promise((resolve) => {
|
|
121
|
+
this.#waiters.push(resolve);
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
// src/fakeSessionLifecycle.ts
|
|
129
|
+
var parseStopWithoutExit = (input) => {
|
|
130
|
+
const envelope = input;
|
|
131
|
+
return envelope?.config?.stopWithoutExit === true;
|
|
132
|
+
};
|
|
133
|
+
function runFakeTurn(run, settle, fail) {
|
|
134
|
+
const work = run();
|
|
135
|
+
return (fail === void 0 ? work : work.catch(fail)).finally(settle);
|
|
136
|
+
}
|
|
137
|
+
function createFakeSessionLifecycle(input) {
|
|
138
|
+
let disposal;
|
|
139
|
+
const closeBooked = (threadId, booked) => {
|
|
140
|
+
if (booked.closing !== void 0) return booked.closing;
|
|
141
|
+
const closing = Promise.withResolvers();
|
|
142
|
+
booked.closing = closing.promise;
|
|
143
|
+
void (async () => {
|
|
144
|
+
input.abort(threadId);
|
|
145
|
+
const [cleanup] = await Promise.allSettled([
|
|
146
|
+
(async () => input.stopPlaybook(threadId))(),
|
|
147
|
+
booked.turnWork?.catch(() => void 0)
|
|
148
|
+
]);
|
|
149
|
+
if (cleanup.status === "rejected") throw cleanup.reason;
|
|
150
|
+
if (input.threads.get(threadId) !== booked) return;
|
|
151
|
+
input.exited(booked.session);
|
|
152
|
+
input.threads.delete(threadId);
|
|
153
|
+
input.forget(threadId);
|
|
154
|
+
})().then(closing.resolve, closing.reject);
|
|
155
|
+
return closing.promise;
|
|
156
|
+
};
|
|
157
|
+
return {
|
|
158
|
+
closeBooked,
|
|
159
|
+
dispose: () => {
|
|
160
|
+
if (disposal !== void 0) return disposal;
|
|
161
|
+
const disposing = Promise.withResolvers();
|
|
162
|
+
disposal = disposing.promise;
|
|
163
|
+
void (async () => {
|
|
164
|
+
const results = await Promise.allSettled(
|
|
165
|
+
[...input.threads].map(([threadId, booked]) => closeBooked(threadId, booked))
|
|
166
|
+
);
|
|
167
|
+
for (const result of results) {
|
|
168
|
+
if (result.status === "rejected") throw result.reason;
|
|
169
|
+
}
|
|
170
|
+
await input.disposePlaybook();
|
|
171
|
+
input.closeEvents();
|
|
172
|
+
})().then(disposing.resolve, disposing.reject);
|
|
173
|
+
return disposal;
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
|
|
92
178
|
// src/laneScript.ts
|
|
93
179
|
import * as NodeFS from "node:fs";
|
|
94
180
|
import * as NodePath from "node:path";
|
|
@@ -563,12 +649,14 @@ function parseMcpSendInput(input) {
|
|
|
563
649
|
async function runMcpCalls(input) {
|
|
564
650
|
const lines = [];
|
|
565
651
|
for (const call of input.calls) {
|
|
652
|
+
if (input.signal?.aborted === true) break;
|
|
566
653
|
try {
|
|
567
654
|
const result = await callMcpTool({
|
|
568
655
|
fetch: input.fetch,
|
|
569
656
|
server: input.server,
|
|
570
657
|
name: call.tool,
|
|
571
|
-
arguments: call.arguments
|
|
658
|
+
arguments: call.arguments,
|
|
659
|
+
...input.signal === void 0 ? {} : { signal: input.signal }
|
|
572
660
|
});
|
|
573
661
|
lines.push(`mcp-send ${call.tool}: ${JSON.stringify(result)}`);
|
|
574
662
|
} catch (error) {
|
|
@@ -1153,38 +1241,6 @@ async function runVisualizeTurn(input) {
|
|
|
1153
1241
|
|
|
1154
1242
|
// src/fakeInstance.ts
|
|
1155
1243
|
var yieldTick = () => Promise.resolve();
|
|
1156
|
-
var EventBus = class {
|
|
1157
|
-
#queued = [];
|
|
1158
|
-
#waiters = [];
|
|
1159
|
-
#closed = false;
|
|
1160
|
-
push(event) {
|
|
1161
|
-
const waiter = this.#waiters.shift();
|
|
1162
|
-
if (waiter) {
|
|
1163
|
-
waiter({ done: false, value: event });
|
|
1164
|
-
return;
|
|
1165
|
-
}
|
|
1166
|
-
this.#queued.push(event);
|
|
1167
|
-
}
|
|
1168
|
-
close() {
|
|
1169
|
-
if (this.#closed) return;
|
|
1170
|
-
this.#closed = true;
|
|
1171
|
-
while (this.#waiters.length > 0) {
|
|
1172
|
-
this.#waiters.shift()?.({ done: true, value: void 0 });
|
|
1173
|
-
}
|
|
1174
|
-
}
|
|
1175
|
-
[Symbol.asyncIterator]() {
|
|
1176
|
-
return {
|
|
1177
|
-
next: () => {
|
|
1178
|
-
const queued = this.#queued.shift();
|
|
1179
|
-
if (queued !== void 0) return Promise.resolve({ done: false, value: queued });
|
|
1180
|
-
if (this.#closed) return Promise.resolve({ done: true, value: void 0 });
|
|
1181
|
-
return new Promise((resolve) => {
|
|
1182
|
-
this.#waiters.push(resolve);
|
|
1183
|
-
});
|
|
1184
|
-
}
|
|
1185
|
-
};
|
|
1186
|
-
}
|
|
1187
|
-
};
|
|
1188
1244
|
var FAKE_ASYNC_QUESTIONS = [
|
|
1189
1245
|
{
|
|
1190
1246
|
id: "q0",
|
|
@@ -1232,6 +1288,7 @@ var createFakeInstance = (config, host) => {
|
|
|
1232
1288
|
const playbook = script === "mcp-send" ? createPlaybookRunner(config) : void 0;
|
|
1233
1289
|
const replyText = parseReplyText(config);
|
|
1234
1290
|
const failStartSession = parseFailStartSession(config);
|
|
1291
|
+
const stopWithoutExit = parseStopWithoutExit(config);
|
|
1235
1292
|
const streamOptions = parseStreamOptions(config);
|
|
1236
1293
|
const previewHoldMs = parsePreviewHoldMs(config);
|
|
1237
1294
|
const laneOptions = laneTurnOptions(config, streamOptions);
|
|
@@ -1253,9 +1310,32 @@ var createFakeInstance = (config, host) => {
|
|
|
1253
1310
|
payload,
|
|
1254
1311
|
...extra?.turnId === void 0 ? {} : { turnId: extra.turnId },
|
|
1255
1312
|
...extra?.itemId === void 0 ? {} : { itemId: extra.itemId },
|
|
1256
|
-
...extra?.requestId === void 0 ? {} : { requestId: extra.requestId }
|
|
1313
|
+
...extra?.requestId === void 0 ? {} : { requestId: extra.requestId },
|
|
1314
|
+
...extra?.originBindingGeneration === void 0 ? {} : { originBindingGeneration: extra.originBindingGeneration },
|
|
1315
|
+
...extra?.originResumeCursor === void 0 ? {} : { originResumeCursor: extra.originResumeCursor }
|
|
1257
1316
|
});
|
|
1258
1317
|
};
|
|
1318
|
+
const lifecycle = createFakeSessionLifecycle({
|
|
1319
|
+
threads,
|
|
1320
|
+
abort: (threadId) => inflight.get(threadId)?.abort(),
|
|
1321
|
+
stopPlaybook: (threadId) => playbook?.stopSession(threadId),
|
|
1322
|
+
// 代次与续接游标取被关闭的那份 session,不在发送时重查当前会话。
|
|
1323
|
+
exited: (session) => {
|
|
1324
|
+
if (stopWithoutExit) return;
|
|
1325
|
+
emit(
|
|
1326
|
+
"session.exited",
|
|
1327
|
+
session.threadId,
|
|
1328
|
+
{ reason: "stopped" },
|
|
1329
|
+
{
|
|
1330
|
+
...session.bindingGeneration === void 0 ? {} : { originBindingGeneration: session.bindingGeneration },
|
|
1331
|
+
...session.resumeCursor === void 0 ? {} : { originResumeCursor: session.resumeCursor }
|
|
1332
|
+
}
|
|
1333
|
+
);
|
|
1334
|
+
},
|
|
1335
|
+
forget: (threadId) => steeredInputs.delete(threadId),
|
|
1336
|
+
disposePlaybook: () => playbook?.dispose(),
|
|
1337
|
+
closeEvents: () => events.close()
|
|
1338
|
+
});
|
|
1259
1339
|
const requireThread = (threadId) => {
|
|
1260
1340
|
const booked = threads.get(threadId);
|
|
1261
1341
|
if (!booked) throw new Error(`unknown thread ${threadId}`);
|
|
@@ -1283,12 +1363,12 @@ var createFakeInstance = (config, host) => {
|
|
|
1283
1363
|
inflight.delete(booked.session.threadId);
|
|
1284
1364
|
}
|
|
1285
1365
|
};
|
|
1286
|
-
const runMcpSendScript = async (booked, input) => {
|
|
1366
|
+
const runMcpSendScript = async (booked, input, signal) => {
|
|
1287
1367
|
const calls = parseMcpSendInput(input);
|
|
1288
1368
|
if (calls === null) return replyText;
|
|
1289
1369
|
const server = builtinMcpServer(booked.mcpServers);
|
|
1290
1370
|
if (server === null) return "mcp-send: no MCP server was handed to this session";
|
|
1291
|
-
return runMcpCalls({ fetch: host.fetch, server, calls });
|
|
1371
|
+
return runMcpCalls({ fetch: host.fetch, server, calls, signal });
|
|
1292
1372
|
};
|
|
1293
1373
|
const runApprovalToolScript = async (threadId, turnId, signal) => {
|
|
1294
1374
|
const isAborted = () => signal?.aborted === true;
|
|
@@ -1470,7 +1550,7 @@ var createFakeInstance = (config, host) => {
|
|
|
1470
1550
|
return;
|
|
1471
1551
|
}
|
|
1472
1552
|
if (script === "mcp-send") {
|
|
1473
|
-
const text = playbookText ?? await runMcpSendScript(booked, input);
|
|
1553
|
+
const text = playbookText ?? await runMcpSendScript(booked, input, signal);
|
|
1474
1554
|
emit("content.delta", threadId, { streamKind: "assistant_text", delta: text }, { turnId });
|
|
1475
1555
|
await yieldTick();
|
|
1476
1556
|
finishTurn(booked, turnId, aborted() ? "interrupted" : "completed", [
|
|
@@ -1552,6 +1632,7 @@ var createFakeInstance = (config, host) => {
|
|
|
1552
1632
|
startSession: async (input, signal) => {
|
|
1553
1633
|
if (disposed) throw new Error("instance disposed");
|
|
1554
1634
|
if (signal?.aborted) throw new Error("aborted");
|
|
1635
|
+
if (threads.get(input.threadId)?.closing !== void 0) throw new Error("session closing");
|
|
1555
1636
|
if (failStartSession !== void 0) throw new Error(failStartSession);
|
|
1556
1637
|
const cwd = input.cwd ?? host.workspacePath(input.threadId);
|
|
1557
1638
|
if (cwd.trim() === "") throw new Error("workspace path missing");
|
|
@@ -1581,6 +1662,7 @@ var createFakeInstance = (config, host) => {
|
|
|
1581
1662
|
sendTurn: async (input, signal) => {
|
|
1582
1663
|
if (disposed) throw new Error("instance disposed");
|
|
1583
1664
|
const booked = requireThread(input.threadId);
|
|
1665
|
+
if (booked.closing !== void 0) throw new Error("session closing");
|
|
1584
1666
|
const running = inflight.get(input.threadId);
|
|
1585
1667
|
const steered = acceptLaneSteer({
|
|
1586
1668
|
running,
|
|
@@ -1611,17 +1693,15 @@ var createFakeInstance = (config, host) => {
|
|
|
1611
1693
|
signal?.removeEventListener("abort", abortTurn);
|
|
1612
1694
|
if (inflight.get(input.threadId)?.turnId === turnId) inflight.delete(input.threadId);
|
|
1613
1695
|
};
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
settle();
|
|
1624
|
-
}
|
|
1696
|
+
const background = script === "approval-tool" || script === "stream" || script === "mcp-send" || script === "visualize" || script === "lane";
|
|
1697
|
+
if (background) signal?.removeEventListener("abort", abortTurn);
|
|
1698
|
+
booked.turnWork = runFakeTurn(
|
|
1699
|
+
run,
|
|
1700
|
+
settle,
|
|
1701
|
+
background ? (error) => failTurn(booked, turnId, error) : void 0
|
|
1702
|
+
);
|
|
1703
|
+
if (!background) await booked.turnWork;
|
|
1704
|
+
return result;
|
|
1625
1705
|
},
|
|
1626
1706
|
interruptTurn: async (threadId, turnId) => {
|
|
1627
1707
|
const running = inflight.get(threadId);
|
|
@@ -1642,9 +1722,8 @@ var createFakeInstance = (config, host) => {
|
|
|
1642
1722
|
host.log.info("respondToUserInput ignored");
|
|
1643
1723
|
},
|
|
1644
1724
|
stopSession: async (threadId) => {
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
await playbook?.stopSession(threadId);
|
|
1725
|
+
const booked = threads.get(threadId);
|
|
1726
|
+
if (booked !== void 0) await lifecycle.closeBooked(threadId, booked);
|
|
1648
1727
|
},
|
|
1649
1728
|
readThread: async (threadId) => {
|
|
1650
1729
|
const booked = requireThread(threadId);
|
|
@@ -1654,13 +1733,7 @@ var createFakeInstance = (config, host) => {
|
|
|
1654
1733
|
usage: createFakeUsage({ instanceId: host.instanceId, script: parseUsageScript(config) }),
|
|
1655
1734
|
dispose: async () => {
|
|
1656
1735
|
disposed = true;
|
|
1657
|
-
|
|
1658
|
-
inflight.clear();
|
|
1659
|
-
for (const settle of pendingDecisions.values()) settle("cancel");
|
|
1660
|
-
pendingDecisions.clear();
|
|
1661
|
-
threads.clear();
|
|
1662
|
-
await playbook?.dispose();
|
|
1663
|
-
events.close();
|
|
1736
|
+
await lifecycle.dispose();
|
|
1664
1737
|
}
|
|
1665
1738
|
};
|
|
1666
1739
|
return instance;
|
|
@@ -1678,7 +1751,7 @@ var executors = [
|
|
|
1678
1751
|
];
|
|
1679
1752
|
var index_default = definePlugin({
|
|
1680
1753
|
id: "agent-fake",
|
|
1681
|
-
apiVersion:
|
|
1754
|
+
apiVersion: 16,
|
|
1682
1755
|
executors,
|
|
1683
1756
|
activate(ctx) {
|
|
1684
1757
|
ctx.log.info("fake executor ready");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ycodium-ai/agent-fake",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.3180",
|
|
4
4
|
"description": "Minimal fake executor for plugin authors and host orchestration tests.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ycodium",
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"@types/node": "24.12.4",
|
|
22
22
|
"esbuild": "0.28.1",
|
|
23
23
|
"vite-plus": "0.2.2",
|
|
24
|
-
"@ycodium-ai/plugin-api": "0.2.
|
|
24
|
+
"@ycodium-ai/plugin-api": "0.2.3180",
|
|
25
25
|
"ycodium-executor-testing": "0.0.0"
|
|
26
26
|
},
|
|
27
27
|
"ycodium": {
|