@ycodium-ai/agent-fake 0.2.3130 → 0.2.3168

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.
Files changed (2) hide show
  1. package/dist/index.js +122 -59
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -89,6 +89,86 @@ var FAKE_CARD = {
89
89
  // src/fakeInstance.ts
90
90
  import * as NodeCrypto from "node:crypto";
91
91
 
92
+ // src/fakeEventBus.ts
93
+ var EventBus = class {
94
+ #queued = [];
95
+ #waiters = [];
96
+ #closed = false;
97
+ push(event) {
98
+ const waiter = this.#waiters.shift();
99
+ if (waiter) {
100
+ waiter({ done: false, value: event });
101
+ return;
102
+ }
103
+ this.#queued.push(event);
104
+ }
105
+ close() {
106
+ if (this.#closed) return;
107
+ this.#closed = true;
108
+ while (this.#waiters.length > 0) {
109
+ this.#waiters.shift()?.({ done: true, value: void 0 });
110
+ }
111
+ }
112
+ [Symbol.asyncIterator]() {
113
+ return {
114
+ next: () => {
115
+ const queued = this.#queued.shift();
116
+ if (queued !== void 0) return Promise.resolve({ done: false, value: queued });
117
+ if (this.#closed) return Promise.resolve({ done: true, value: void 0 });
118
+ return new Promise((resolve) => {
119
+ this.#waiters.push(resolve);
120
+ });
121
+ }
122
+ };
123
+ }
124
+ };
125
+
126
+ // src/fakeSessionLifecycle.ts
127
+ function runFakeTurn(run, settle, fail) {
128
+ const work = run();
129
+ return (fail === void 0 ? work : work.catch(fail)).finally(settle);
130
+ }
131
+ function createFakeSessionLifecycle(input) {
132
+ let disposal;
133
+ const closeBooked = (threadId, booked) => {
134
+ if (booked.closing !== void 0) return booked.closing;
135
+ const closing = Promise.withResolvers();
136
+ booked.closing = closing.promise;
137
+ void (async () => {
138
+ input.abort(threadId);
139
+ const [cleanup] = await Promise.allSettled([
140
+ (async () => input.stopPlaybook(threadId))(),
141
+ booked.turnWork?.catch(() => void 0)
142
+ ]);
143
+ if (cleanup.status === "rejected") throw cleanup.reason;
144
+ if (input.threads.get(threadId) !== booked) return;
145
+ input.exited(booked.session);
146
+ input.threads.delete(threadId);
147
+ input.forget(threadId);
148
+ })().then(closing.resolve, closing.reject);
149
+ return closing.promise;
150
+ };
151
+ return {
152
+ closeBooked,
153
+ dispose: () => {
154
+ if (disposal !== void 0) return disposal;
155
+ const disposing = Promise.withResolvers();
156
+ disposal = disposing.promise;
157
+ void (async () => {
158
+ const results = await Promise.allSettled(
159
+ [...input.threads].map(([threadId, booked]) => closeBooked(threadId, booked))
160
+ );
161
+ for (const result of results) {
162
+ if (result.status === "rejected") throw result.reason;
163
+ }
164
+ await input.disposePlaybook();
165
+ input.closeEvents();
166
+ })().then(disposing.resolve, disposing.reject);
167
+ return disposal;
168
+ }
169
+ };
170
+ }
171
+
92
172
  // src/laneScript.ts
93
173
  import * as NodeFS from "node:fs";
94
174
  import * as NodePath from "node:path";
@@ -563,12 +643,14 @@ function parseMcpSendInput(input) {
563
643
  async function runMcpCalls(input) {
564
644
  const lines = [];
565
645
  for (const call of input.calls) {
646
+ if (input.signal?.aborted === true) break;
566
647
  try {
567
648
  const result = await callMcpTool({
568
649
  fetch: input.fetch,
569
650
  server: input.server,
570
651
  name: call.tool,
571
- arguments: call.arguments
652
+ arguments: call.arguments,
653
+ ...input.signal === void 0 ? {} : { signal: input.signal }
572
654
  });
573
655
  lines.push(`mcp-send ${call.tool}: ${JSON.stringify(result)}`);
574
656
  } catch (error) {
@@ -1153,38 +1235,6 @@ async function runVisualizeTurn(input) {
1153
1235
 
1154
1236
  // src/fakeInstance.ts
1155
1237
  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
1238
  var FAKE_ASYNC_QUESTIONS = [
1189
1239
  {
1190
1240
  id: "q0",
@@ -1253,9 +1303,29 @@ var createFakeInstance = (config, host) => {
1253
1303
  payload,
1254
1304
  ...extra?.turnId === void 0 ? {} : { turnId: extra.turnId },
1255
1305
  ...extra?.itemId === void 0 ? {} : { itemId: extra.itemId },
1256
- ...extra?.requestId === void 0 ? {} : { requestId: extra.requestId }
1306
+ ...extra?.requestId === void 0 ? {} : { requestId: extra.requestId },
1307
+ ...extra?.originBindingGeneration === void 0 ? {} : { originBindingGeneration: extra.originBindingGeneration },
1308
+ ...extra?.originResumeCursor === void 0 ? {} : { originResumeCursor: extra.originResumeCursor }
1257
1309
  });
1258
1310
  };
1311
+ const lifecycle = createFakeSessionLifecycle({
1312
+ threads,
1313
+ abort: (threadId) => inflight.get(threadId)?.abort(),
1314
+ stopPlaybook: (threadId) => playbook?.stopSession(threadId),
1315
+ // 代次与续接游标取被关闭的那份 session,不在发送时重查当前会话。
1316
+ exited: (session) => emit(
1317
+ "session.exited",
1318
+ session.threadId,
1319
+ { reason: "stopped" },
1320
+ {
1321
+ ...session.bindingGeneration === void 0 ? {} : { originBindingGeneration: session.bindingGeneration },
1322
+ ...session.resumeCursor === void 0 ? {} : { originResumeCursor: session.resumeCursor }
1323
+ }
1324
+ ),
1325
+ forget: (threadId) => steeredInputs.delete(threadId),
1326
+ disposePlaybook: () => playbook?.dispose(),
1327
+ closeEvents: () => events.close()
1328
+ });
1259
1329
  const requireThread = (threadId) => {
1260
1330
  const booked = threads.get(threadId);
1261
1331
  if (!booked) throw new Error(`unknown thread ${threadId}`);
@@ -1283,12 +1353,12 @@ var createFakeInstance = (config, host) => {
1283
1353
  inflight.delete(booked.session.threadId);
1284
1354
  }
1285
1355
  };
1286
- const runMcpSendScript = async (booked, input) => {
1356
+ const runMcpSendScript = async (booked, input, signal) => {
1287
1357
  const calls = parseMcpSendInput(input);
1288
1358
  if (calls === null) return replyText;
1289
1359
  const server = builtinMcpServer(booked.mcpServers);
1290
1360
  if (server === null) return "mcp-send: no MCP server was handed to this session";
1291
- return runMcpCalls({ fetch: host.fetch, server, calls });
1361
+ return runMcpCalls({ fetch: host.fetch, server, calls, signal });
1292
1362
  };
1293
1363
  const runApprovalToolScript = async (threadId, turnId, signal) => {
1294
1364
  const isAborted = () => signal?.aborted === true;
@@ -1470,7 +1540,7 @@ var createFakeInstance = (config, host) => {
1470
1540
  return;
1471
1541
  }
1472
1542
  if (script === "mcp-send") {
1473
- const text = playbookText ?? await runMcpSendScript(booked, input);
1543
+ const text = playbookText ?? await runMcpSendScript(booked, input, signal);
1474
1544
  emit("content.delta", threadId, { streamKind: "assistant_text", delta: text }, { turnId });
1475
1545
  await yieldTick();
1476
1546
  finishTurn(booked, turnId, aborted() ? "interrupted" : "completed", [
@@ -1552,6 +1622,7 @@ var createFakeInstance = (config, host) => {
1552
1622
  startSession: async (input, signal) => {
1553
1623
  if (disposed) throw new Error("instance disposed");
1554
1624
  if (signal?.aborted) throw new Error("aborted");
1625
+ if (threads.get(input.threadId)?.closing !== void 0) throw new Error("session closing");
1555
1626
  if (failStartSession !== void 0) throw new Error(failStartSession);
1556
1627
  const cwd = input.cwd ?? host.workspacePath(input.threadId);
1557
1628
  if (cwd.trim() === "") throw new Error("workspace path missing");
@@ -1581,6 +1652,7 @@ var createFakeInstance = (config, host) => {
1581
1652
  sendTurn: async (input, signal) => {
1582
1653
  if (disposed) throw new Error("instance disposed");
1583
1654
  const booked = requireThread(input.threadId);
1655
+ if (booked.closing !== void 0) throw new Error("session closing");
1584
1656
  const running = inflight.get(input.threadId);
1585
1657
  const steered = acceptLaneSteer({
1586
1658
  running,
@@ -1611,17 +1683,15 @@ var createFakeInstance = (config, host) => {
1611
1683
  signal?.removeEventListener("abort", abortTurn);
1612
1684
  if (inflight.get(input.threadId)?.turnId === turnId) inflight.delete(input.threadId);
1613
1685
  };
1614
- if (script === "approval-tool" || script === "stream" || script === "mcp-send" || script === "visualize" || script === "lane") {
1615
- signal?.removeEventListener("abort", abortTurn);
1616
- void run().catch((error) => failTurn(booked, turnId, error)).finally(settle);
1617
- return result;
1618
- }
1619
- try {
1620
- await run();
1621
- return result;
1622
- } finally {
1623
- settle();
1624
- }
1686
+ const background = script === "approval-tool" || script === "stream" || script === "mcp-send" || script === "visualize" || script === "lane";
1687
+ if (background) signal?.removeEventListener("abort", abortTurn);
1688
+ booked.turnWork = runFakeTurn(
1689
+ run,
1690
+ settle,
1691
+ background ? (error) => failTurn(booked, turnId, error) : void 0
1692
+ );
1693
+ if (!background) await booked.turnWork;
1694
+ return result;
1625
1695
  },
1626
1696
  interruptTurn: async (threadId, turnId) => {
1627
1697
  const running = inflight.get(threadId);
@@ -1642,9 +1712,8 @@ var createFakeInstance = (config, host) => {
1642
1712
  host.log.info("respondToUserInput ignored");
1643
1713
  },
1644
1714
  stopSession: async (threadId) => {
1645
- inflight.get(threadId)?.abort();
1646
- threads.delete(threadId);
1647
- await playbook?.stopSession(threadId);
1715
+ const booked = threads.get(threadId);
1716
+ if (booked !== void 0) await lifecycle.closeBooked(threadId, booked);
1648
1717
  },
1649
1718
  readThread: async (threadId) => {
1650
1719
  const booked = requireThread(threadId);
@@ -1654,13 +1723,7 @@ var createFakeInstance = (config, host) => {
1654
1723
  usage: createFakeUsage({ instanceId: host.instanceId, script: parseUsageScript(config) }),
1655
1724
  dispose: async () => {
1656
1725
  disposed = true;
1657
- for (const running of inflight.values()) running.abort();
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();
1726
+ await lifecycle.dispose();
1664
1727
  }
1665
1728
  };
1666
1729
  return instance;
@@ -1678,7 +1741,7 @@ var executors = [
1678
1741
  ];
1679
1742
  var index_default = definePlugin({
1680
1743
  id: "agent-fake",
1681
- apiVersion: 14,
1744
+ apiVersion: 16,
1682
1745
  executors,
1683
1746
  activate(ctx) {
1684
1747
  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.3130",
3
+ "version": "0.2.3168",
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.3130",
24
+ "@ycodium-ai/plugin-api": "0.2.3168",
25
25
  "ycodium-executor-testing": "0.0.0"
26
26
  },
27
27
  "ycodium": {