@ycodium-ai/agent-fake 0.2.2106 → 0.2.2126

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 +151 -6
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -37,10 +37,17 @@ var FAKE_CARD = {
37
37
  },
38
38
  credentialSources: [],
39
39
  diagnostics: [],
40
- configSchema: { type: "object", additionalProperties: false },
40
+ configSchema: {
41
+ type: "object",
42
+ additionalProperties: false,
43
+ properties: {
44
+ script: { type: "string", enum: ["plain", "approval-tool"] }
45
+ }
46
+ },
41
47
  modelConnectivity: {
42
48
  dialects: ["openai-chat-completions"],
43
- acceptsExternalEndpoints: true
49
+ // 假手不 spawn、也不出网,没有「把地址和钥匙注给谁」这回事。
50
+ acceptsExternalEndpoints: false
44
51
  }
45
52
  };
46
53
 
@@ -78,9 +85,16 @@ var EventBus = class {
78
85
  };
79
86
  }
80
87
  };
81
- var createFakeInstance = (_config, host) => {
88
+ function parseScript(input) {
89
+ const envelope = input;
90
+ const script = envelope?.config?.script;
91
+ return script === "approval-tool" ? "approval-tool" : "plain";
92
+ }
93
+ var createFakeInstance = (config, host) => {
82
94
  const events = new EventBus();
83
95
  const threads = /* @__PURE__ */ new Map();
96
+ const script = parseScript(config);
97
+ const pendingDecisions = /* @__PURE__ */ new Map();
84
98
  let seq = 0;
85
99
  let inflight = null;
86
100
  let disposed = false;
@@ -95,7 +109,9 @@ var createFakeInstance = (_config, host) => {
95
109
  createdAt: now(),
96
110
  type,
97
111
  payload,
98
- ...extra?.turnId === void 0 ? {} : { turnId: extra.turnId }
112
+ ...extra?.turnId === void 0 ? {} : { turnId: extra.turnId },
113
+ ...extra?.itemId === void 0 ? {} : { itemId: extra.itemId },
114
+ ...extra?.requestId === void 0 ? {} : { requestId: extra.requestId }
99
115
  });
100
116
  };
101
117
  const requireThread = (threadId) => {
@@ -114,6 +130,121 @@ var createFakeInstance = (_config, host) => {
114
130
  emit("turn.completed", booked.session.threadId, { state }, { turnId });
115
131
  inflight = null;
116
132
  };
133
+ const runApprovalToolScript = async (threadId, turnId, signal) => {
134
+ const isAborted = () => signal?.aborted === true;
135
+ const fakeReply = "\u597D";
136
+ emit(
137
+ "content.delta",
138
+ threadId,
139
+ {
140
+ streamKind: "reasoning_text",
141
+ delta: "\u5148\u60F3\u6E05\u695A\u6B65\u9AA4\uFF1A\u7B2C\u4E00\u6B65 ls \u5F53\u524D\u76EE\u5F55\uFF1B\u7B2C\u4E8C\u6B65\u8BFB README.md \u7684\u7B2C\u4E00\u884C\uFF1B\u6700\u540E\u53EA\u56DE\u4E00\u4E2A\u5B57\u3002"
142
+ },
143
+ { turnId }
144
+ );
145
+ await yieldTick();
146
+ if (isAborted()) return null;
147
+ const approve = async (request) => {
148
+ emit(
149
+ "item.started",
150
+ threadId,
151
+ {
152
+ itemType: request.itemType,
153
+ status: "inProgress",
154
+ title: request.title,
155
+ data: request.args
156
+ },
157
+ { turnId, itemId: request.itemId }
158
+ );
159
+ emit(
160
+ "request.opened",
161
+ threadId,
162
+ {
163
+ requestType: request.requestType,
164
+ toolName: request.toolName,
165
+ detail: request.detail,
166
+ args: request.args
167
+ },
168
+ { turnId, requestId: request.requestId }
169
+ );
170
+ const decision = await new Promise((resolve) => {
171
+ let settled = false;
172
+ const settle = (value) => {
173
+ if (settled) return;
174
+ settled = true;
175
+ signal?.removeEventListener("abort", onAbort);
176
+ pendingDecisions.delete(request.requestId);
177
+ resolve(value);
178
+ };
179
+ const onAbort = () => settle("cancel");
180
+ signal?.addEventListener("abort", onAbort);
181
+ if (isAborted()) {
182
+ settle("cancel");
183
+ return;
184
+ }
185
+ pendingDecisions.set(request.requestId, settle);
186
+ });
187
+ emit(
188
+ "request.resolved",
189
+ threadId,
190
+ {
191
+ requestType: request.requestType,
192
+ decision: decision === "allow" ? "accept" : decision === "deny" ? "decline" : "cancel"
193
+ },
194
+ { turnId, requestId: request.requestId }
195
+ );
196
+ if (decision === "allow") {
197
+ emit(
198
+ "content.delta",
199
+ threadId,
200
+ { streamKind: "command_output", delta: request.output },
201
+ { turnId, itemId: request.itemId }
202
+ );
203
+ }
204
+ emit(
205
+ "item.completed",
206
+ threadId,
207
+ {
208
+ itemType: request.itemType,
209
+ status: decision === "allow" ? "completed" : "declined",
210
+ title: request.title,
211
+ data: { ...request.args, ...decision === "allow" ? { output: request.output } : {} }
212
+ },
213
+ { turnId, itemId: request.itemId }
214
+ );
215
+ return decision;
216
+ };
217
+ const first = await approve({
218
+ itemId: nextId("item"),
219
+ requestId: nextId("req"),
220
+ requestType: "exec_command_approval",
221
+ toolName: "shell",
222
+ detail: "ls",
223
+ args: { command: "ls" },
224
+ itemType: "command_execution",
225
+ title: "ls",
226
+ output: "README.md\n"
227
+ });
228
+ if (first === "cancel") return null;
229
+ if (first === "allow") {
230
+ const second = await approve({
231
+ itemId: nextId("item"),
232
+ requestId: nextId("req"),
233
+ requestType: "file_read_approval",
234
+ toolName: "Read",
235
+ detail: "README.md",
236
+ args: { file_path: "README.md" },
237
+ itemType: "dynamic_tool_call",
238
+ title: "Read README.md",
239
+ output: "# hands-probe-workspace\n"
240
+ });
241
+ if (second === "cancel") return null;
242
+ }
243
+ if (isAborted()) return null;
244
+ emit("content.delta", threadId, { streamKind: "assistant_text", delta: fakeReply }, { turnId });
245
+ await yieldTick();
246
+ return isAborted() ? null : fakeReply;
247
+ };
117
248
  const instance = {
118
249
  probe: async () => ({
119
250
  installed: true,
@@ -167,6 +298,13 @@ var createFakeInstance = (_config, host) => {
167
298
  finishTurn(booked, turnId, "interrupted", []);
168
299
  return { threadId: input.threadId, turnId };
169
300
  }
301
+ if (script === "approval-tool") {
302
+ const finalText = await runApprovalToolScript(input.threadId, turnId, signal);
303
+ finishTurn(booked, turnId, aborted ? "interrupted" : "completed", [
304
+ ...finalText === null ? [] : [{ kind: "assistant", text: finalText }]
305
+ ]);
306
+ return { threadId: input.threadId, turnId };
307
+ }
170
308
  emit(
171
309
  "content.delta",
172
310
  input.threadId,
@@ -194,8 +332,13 @@ var createFakeInstance = (_config, host) => {
194
332
  host.log.info("interrupt turn", { threadId, turnId: inflight.turnId });
195
333
  inflight.abort();
196
334
  },
197
- respondToRequest: async () => {
198
- host.log.info("respondToRequest ignored");
335
+ respondToRequest: async (threadId, requestId, decision) => {
336
+ host.log.info("respondToRequest", { threadId, requestId, decision });
337
+ const resolve = pendingDecisions.get(requestId);
338
+ if (resolve !== void 0) {
339
+ pendingDecisions.delete(requestId);
340
+ resolve(decision === "allow" ? "allow" : "deny");
341
+ }
199
342
  },
200
343
  respondToUserInput: async () => {
201
344
  host.log.info("respondToUserInput ignored");
@@ -213,6 +356,8 @@ var createFakeInstance = (_config, host) => {
213
356
  disposed = true;
214
357
  inflight?.abort();
215
358
  inflight = null;
359
+ for (const settle of pendingDecisions.values()) settle("cancel");
360
+ pendingDecisions.clear();
216
361
  threads.clear();
217
362
  events.close();
218
363
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ycodium-ai/agent-fake",
3
- "version": "0.2.2106",
3
+ "version": "0.2.2126",
4
4
  "description": "Minimal fake executor for plugin authors and host orchestration tests.",
5
5
  "keywords": [
6
6
  "ycodium",
@@ -20,7 +20,7 @@
20
20
  "@types/node": "24.12.4",
21
21
  "esbuild": "0.28.1",
22
22
  "vite-plus": "0.2.2",
23
- "@ycodium-ai/plugin-api": "0.2.2106"
23
+ "@ycodium-ai/plugin-api": "0.2.2126"
24
24
  },
25
25
  "ycodium": {
26
26
  "plugin": "./dist/index.js",