@ycodium-ai/agent-fake 0.2.2938 → 0.2.2942

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 (3) hide show
  1. package/README.md +1 -1
  2. package/dist/index.js +106 -3
  3. package/package.json +2 -2
package/README.md CHANGED
@@ -4,7 +4,7 @@ A minimal executor plugin. It starts no CLI and replays a fixed event script:
4
4
 
5
5
  - `startSession` emits `session.started`
6
6
  - `sendTurn` emits `turn.started`, one `content.delta`, then `turn.completed` (the host binds the turn by `turn.started`)
7
- - `config.script` picks the script: `plain` (default), `approval-tool` (two approvals before the reply), `async-question` (on a thread's first turn, the reply plus one non-blocking `user-input.requested` with `responseMode: "message"`), `stream` (a Markdown answer in `config.streamChunks` deltas, default 200, one every `config.streamIntervalMs`, default 30; the UI probe `interaction-table.ts` measures streaming with it), or `mcp-send` (a turn whose input is a JSON object is passed as the arguments of one real `send_message` call through the built-in MCP server the host handed to `startSession`, the same path a real agent takes; the reply is the tool result, and any other input gets the plain reply)
7
+ - `config.script` picks the script: `plain` (default), `approval-tool` (two approvals before the reply), `async-question` (on a thread's first turn, the reply plus one non-blocking `user-input.requested` with `responseMode: "message"`), `stream` (a Markdown answer in `config.streamChunks` deltas, default 200, one every `config.streamIntervalMs`, default 30; the UI probe `interaction-table.ts` measures streaming with it), `mcp-send` (a turn whose input is a JSON object is passed as the arguments of one real `send_message` call through the built-in MCP server the host handed to `startSession`, the same path a real agent takes; the reply is the tool result, and any other input gets the plain reply), or `visualize` (emits one in-progress builtin tool row, holds for `config.previewHoldMs` milliseconds so the input preview can paint, then one real `visualize_visualize` call on that same server; the tool row’s result is the MCP confirmation text, and the reply is “The figure is in the card above.”)
8
8
  - `interruptTurn` (or an aborted `AbortSignal`) ends the in-flight turn as `interrupted`
9
9
  - `readThread` returns the bookkeeping the instance recorded
10
10
  - `probe` reports two models, `fake` (default) and `fake-large`
package/dist/index.js CHANGED
@@ -42,7 +42,7 @@ var FAKE_CARD = {
42
42
  properties: {
43
43
  script: {
44
44
  type: "string",
45
- enum: ["plain", "approval-tool", "async-question", "stream", "mcp-send"]
45
+ enum: ["plain", "approval-tool", "async-question", "stream", "mcp-send", "visualize"]
46
46
  },
47
47
  streamChunks: { type: "integer", minimum: 1 },
48
48
  streamIntervalMs: { type: "integer", minimum: 0 },
@@ -251,6 +251,88 @@ async function callMcpTool(input) {
251
251
  return called.message?.result;
252
252
  }
253
253
 
254
+ // src/visualizeScript.ts
255
+ var WIRE_NAME = "mcp__ycodium__visualize_visualize";
256
+ var TITLE = "Monthly sales";
257
+ var FRAGMENT = "<section><h1>Monthly sales</h1><p>Jan 12 \xB7 Feb 18 \xB7 Mar 15</p></section>";
258
+ var REPLY = "The figure is in the card above.";
259
+ function parsePreviewHoldMs(input) {
260
+ const config = input?.config;
261
+ const raw = config?.previewHoldMs;
262
+ if (typeof raw !== "number" || !Number.isFinite(raw) || raw < 0) return 0;
263
+ return Math.min(3e4, Math.floor(raw));
264
+ }
265
+ function confirmationText(result) {
266
+ const content = result?.content;
267
+ if (!Array.isArray(content)) return "";
268
+ const chunks = [];
269
+ for (const entry of content) {
270
+ const text = entry?.text;
271
+ if (typeof text === "string" && text.length > 0) chunks.push(text);
272
+ }
273
+ return chunks.join("\n");
274
+ }
275
+ function hold(ms, signal) {
276
+ if (ms === 0 || signal.aborted) return Promise.resolve();
277
+ return new Promise((resolve) => {
278
+ let timer;
279
+ const finish = () => {
280
+ if (timer !== void 0) clearTimeout(timer);
281
+ signal.removeEventListener("abort", finish);
282
+ resolve();
283
+ };
284
+ timer = setTimeout(finish, ms);
285
+ signal.addEventListener("abort", finish, { once: true });
286
+ });
287
+ }
288
+ async function runVisualizeTurn(input) {
289
+ const args = JSON.stringify({ action: "create", title: TITLE, fragment: FRAGMENT });
290
+ const tool = (status, result) => ({
291
+ itemType: "dynamic_tool_call",
292
+ status,
293
+ title: WIRE_NAME,
294
+ data: {
295
+ toolName: WIRE_NAME,
296
+ argumentText: args,
297
+ ...result === void 0 ? {} : { result }
298
+ }
299
+ });
300
+ input.emit("item.started", input.threadId, tool("inProgress"), {
301
+ turnId: input.turnId,
302
+ itemId: input.itemId
303
+ });
304
+ await hold(input.holdMs, input.signal);
305
+ if (input.signal.aborted) return null;
306
+ const server = builtinMcpServer(input.mcpServers);
307
+ if (server === null) {
308
+ const failure = "visualize: no MCP server was handed to this session";
309
+ input.emit("item.completed", input.threadId, tool("completed", failure), {
310
+ turnId: input.turnId,
311
+ itemId: input.itemId
312
+ });
313
+ return failure;
314
+ }
315
+ let resultText;
316
+ try {
317
+ resultText = confirmationText(
318
+ await callMcpTool({
319
+ fetch: input.fetch,
320
+ server,
321
+ name: "visualize_visualize",
322
+ arguments: { action: "create", title: TITLE, fragment: FRAGMENT }
323
+ })
324
+ );
325
+ } catch (error) {
326
+ resultText = `visualize failed: ${String(error)}`;
327
+ }
328
+ if (input.signal.aborted) return null;
329
+ input.emit("item.completed", input.threadId, tool("completed", resultText), {
330
+ turnId: input.turnId,
331
+ itemId: input.itemId
332
+ });
333
+ return resultText.length > 0 ? REPLY : "visualize failed: empty tool result";
334
+ }
335
+
254
336
  // src/fakeInstance.ts
255
337
  var yieldTick = () => Promise.resolve();
256
338
  var EventBus = class {
@@ -309,7 +391,7 @@ var FAKE_ASYNC_QUESTIONS = [
309
391
  function parseScript(input) {
310
392
  const envelope = input;
311
393
  const script = envelope?.config?.script;
312
- return script === "approval-tool" || script === "async-question" || script === "stream" || script === "mcp-send" ? script : "plain";
394
+ return script === "approval-tool" || script === "async-question" || script === "stream" || script === "mcp-send" || script === "visualize" ? script : "plain";
313
395
  }
314
396
  var FAKE_STREAM_TEXT = [
315
397
  "Here is the plan for the change, step by step.\n\n",
@@ -353,6 +435,7 @@ var createFakeInstance = (config, host) => {
353
435
  const replyText = parseReplyText(config);
354
436
  const failStartSession = parseFailStartSession(config);
355
437
  const streamOptions = parseStreamOptions(config);
438
+ const previewHoldMs = parsePreviewHoldMs(config);
356
439
  const pendingDecisions = /* @__PURE__ */ new Map();
357
440
  let seq = 0;
358
441
  const inflight = /* @__PURE__ */ new Map();
@@ -560,6 +643,26 @@ var createFakeInstance = (config, host) => {
560
643
  ]);
561
644
  return;
562
645
  }
646
+ if (script === "visualize") {
647
+ const text = await runVisualizeTurn({
648
+ threadId,
649
+ turnId,
650
+ itemId: nextId("item"),
651
+ holdMs: previewHoldMs,
652
+ signal,
653
+ fetch: host.fetch,
654
+ mcpServers: booked.mcpServers,
655
+ emit
656
+ });
657
+ if (text === null || aborted()) {
658
+ finishTurn(booked, turnId, "interrupted", []);
659
+ return;
660
+ }
661
+ emit("content.delta", threadId, { streamKind: "assistant_text", delta: text }, { turnId });
662
+ await yieldTick();
663
+ finishTurn(booked, turnId, "completed", [{ kind: "assistant", text }]);
664
+ return;
665
+ }
563
666
  if (script === "mcp-send") {
564
667
  const text = await runMcpSendScript(booked, input);
565
668
  emit("content.delta", threadId, { streamKind: "assistant_text", delta: text }, { turnId });
@@ -663,7 +766,7 @@ var createFakeInstance = (config, host) => {
663
766
  signal?.removeEventListener("abort", abortTurn);
664
767
  if (inflight.get(input.threadId)?.turnId === turnId) inflight.delete(input.threadId);
665
768
  };
666
- if (script === "approval-tool" || script === "stream" || script === "mcp-send") {
769
+ if (script === "approval-tool" || script === "stream" || script === "mcp-send" || script === "visualize") {
667
770
  signal?.removeEventListener("abort", abortTurn);
668
771
  void run().catch((error) => failTurn(booked, turnId, error)).finally(settle);
669
772
  return result;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ycodium-ai/agent-fake",
3
- "version": "0.2.2938",
3
+ "version": "0.2.2942",
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.2938",
24
+ "@ycodium-ai/plugin-api": "0.2.2942",
25
25
  "ycodium-executor-testing": "0.0.0"
26
26
  },
27
27
  "ycodium": {