@ycodium-ai/agent-fake 0.2.3369 → 0.2.3389
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 +20 -7
- package/dist/index.js +42 -8
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -61,14 +61,25 @@ Replay recorded conversations or drive multi-thread acceptance tests with
|
|
|
61
61
|
machine. Use an absolute path. This option is outside the settings form and applies only to
|
|
62
62
|
`mcp-send`. Each turn runs the next batch, ignoring its input, including host wake-ups and follow-ups.
|
|
63
63
|
|
|
64
|
-
A playbook instance accepts a steer
|
|
65
|
-
running
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
64
|
+
A playbook instance accepts a steer only where the playbook says so: while the batch the thread is
|
|
65
|
+
running ends with `awaitSteer`. Then a second `sendTurn` carrying the running turn's id returns that
|
|
66
|
+
turn, and when the current batch finishes the next batch runs inside the same turn, one steer at a
|
|
67
|
+
time in arrival order (each still subject to `expectInput`). A playbook derived from a recording has
|
|
68
|
+
`awaitSteer` exactly where the recorded agent took a steer, so a steer elsewhere would shift every
|
|
69
|
+
later batch by one; it is refused instead, and the host starts it as its own turn, as in the
|
|
70
|
+
recording. A steer the playbook has no batch for is logged as `playbook has no step for a steered
|
|
71
|
+
input` and noted in the reply. A steer the running batch does not await, or whose target turn has
|
|
72
|
+
already ended or is not the running turn, is refused with an error carrying
|
|
73
|
+
`code: "provider-rejected"`, so the host puts the message back in the queue. A plain
|
|
70
74
|
`mcp-send` instance without a playbook still refuses a second turn while one is running.
|
|
71
75
|
|
|
76
|
+
`{ "awaitSteer": { "timeoutMs": 60000, "continueOnTimeout": true } }` treats a steer that never
|
|
77
|
+
arrives as already folded into the turn: on timeout it runs the next batch inside the same turn
|
|
78
|
+
instead of ending it. Derived playbooks set it on member-machine sessions, where a steer can reach
|
|
79
|
+
the host before the member's turn starts and be merged into that turn's input. The next batch then
|
|
80
|
+
relies on its own `waitFor` steps; a steer that arrives later is refused like any other and starts
|
|
81
|
+
a turn with no matching batch.
|
|
82
|
+
|
|
72
83
|
This example assigns one script by thread ID and another by title:
|
|
73
84
|
|
|
74
85
|
```json
|
|
@@ -132,7 +143,9 @@ A `wait` polls `tools/list` on that MCP server every 250 ms for the tool name or
|
|
|
132
143
|
`mcp-send wait <tool>: ready` or `mcp-send wait <tool> failed: timeout`, then continues, with no
|
|
133
144
|
tool items. A `waitFor`, such as
|
|
134
145
|
`{ "waitFor": { "tool": "message_inbox_peek", "path": "items", "atLeast": 2 } }`, calls that tool
|
|
135
|
-
with its optional `arguments` (none by default;
|
|
146
|
+
with its optional `arguments` (none by default; `{{thread}}` and `{{step:…}}` placeholders are
|
|
147
|
+
resolved as for calls, and an unresolvable one replies `mcp-send <tool> failed: <error>` without
|
|
148
|
+
polling) every 250 ms on one session until the value at the dot `path` of its result is
|
|
136
149
|
an array at least `atLeast` long, or a number at least `atLeast`; any other value found there (for
|
|
137
150
|
example one entry picked by a selector such as `items[title=<encodeURIComponent value>]`) counts
|
|
138
151
|
as 1, and a tool error counts as not yet. Dots inside a selector's brackets do not split the path.
|
package/dist/index.js
CHANGED
|
@@ -897,10 +897,12 @@ function parseStep(value) {
|
|
|
897
897
|
return onlyKeys(value, ["expectInput"]) && isName(value.expectInput) ? { expectInput: value.expectInput } : null;
|
|
898
898
|
if (Object.hasOwn(value, "awaitSteer")) {
|
|
899
899
|
const wait = value.awaitSteer;
|
|
900
|
-
if (!onlyKeys(value, ["awaitSteer"]) || !isRecord2(wait) || !onlyKeys(wait, ["timeoutMs"]))
|
|
900
|
+
if (!onlyKeys(value, ["awaitSteer"]) || !isRecord2(wait) || !onlyKeys(wait, ["timeoutMs", "continueOnTimeout"]) || Object.hasOwn(wait, "continueOnTimeout") && wait.continueOnTimeout !== true)
|
|
901
901
|
return null;
|
|
902
902
|
const timeoutMs = parseTimeout(wait.timeoutMs);
|
|
903
|
-
|
|
903
|
+
if (timeoutMs === null) return null;
|
|
904
|
+
const onTimeout = wait.continueOnTimeout === true ? { continueOnTimeout: true } : {};
|
|
905
|
+
return { awaitSteer: { timeoutMs, ...onTimeout } };
|
|
904
906
|
}
|
|
905
907
|
if (Object.hasOwn(value, "wait")) {
|
|
906
908
|
const wait = value.wait;
|
|
@@ -1135,13 +1137,13 @@ var waitForTool = (step, input, clients) => poll(step.wait.timeoutMs, input, asy
|
|
|
1135
1137
|
(tool) => isRecord2(tool) && (tool.name === step.wait.tool || tool.name === `mcp__${step.wait.server ?? "ycodium"}__${step.wait.tool}`)
|
|
1136
1138
|
);
|
|
1137
1139
|
});
|
|
1138
|
-
async function waitForCount(step, input, clients) {
|
|
1140
|
+
async function waitForCount(step, args, input, clients) {
|
|
1139
1141
|
let ready;
|
|
1140
1142
|
const ok = await poll(step.waitFor.timeoutMs, input, async (signal) => {
|
|
1141
1143
|
const called = await clients.request(
|
|
1142
1144
|
step.waitFor.server,
|
|
1143
1145
|
"tools/call",
|
|
1144
|
-
{ name: step.waitFor.tool, arguments:
|
|
1146
|
+
{ name: step.waitFor.tool, arguments: args },
|
|
1145
1147
|
signal
|
|
1146
1148
|
);
|
|
1147
1149
|
if (isRecord2(called) && called.isError === true) return false;
|
|
@@ -1207,6 +1209,16 @@ function createPlaybookRunner(input) {
|
|
|
1207
1209
|
const clients = /* @__PURE__ */ new Map();
|
|
1208
1210
|
let loaded;
|
|
1209
1211
|
return {
|
|
1212
|
+
/**
|
|
1213
|
+
* 这个会话正在跑的那一批以 awaitSteer 结尾时才收插话:剧本照录像在收到插话的地方放 awaitSteer,
|
|
1214
|
+
* 别处的插话录像里是另起一轮的,收了就会把后面的批次往前错一格。
|
|
1215
|
+
*/
|
|
1216
|
+
expectsSteer: (threadId) => {
|
|
1217
|
+
const playback = sessions.get(threadId);
|
|
1218
|
+
const batch = playback?.current === void 0 ? void 0 : playback.line?.turns[playback.current];
|
|
1219
|
+
const last = batch?.at(-1);
|
|
1220
|
+
return last !== void 0 && "awaitSteer" in last;
|
|
1221
|
+
},
|
|
1210
1222
|
startSession: (session) => {
|
|
1211
1223
|
titles.set(session.threadId, session.title);
|
|
1212
1224
|
},
|
|
@@ -1248,6 +1260,7 @@ function createPlaybookRunner(input) {
|
|
|
1248
1260
|
const batch = picked === void 0 ? void 0 : playback.line?.turns[picked];
|
|
1249
1261
|
if (picked === void 0 || batch === void 0) return null;
|
|
1250
1262
|
playback.consumed.add(picked);
|
|
1263
|
+
playback.current = picked;
|
|
1251
1264
|
let opened = clients.get(turn.threadId);
|
|
1252
1265
|
if (opened === void 0) {
|
|
1253
1266
|
opened = createPlaybookMcp({ fetch: turn.fetch, servers: turn.mcpServers });
|
|
@@ -1260,13 +1273,21 @@ function createPlaybookRunner(input) {
|
|
|
1260
1273
|
}
|
|
1261
1274
|
};
|
|
1262
1275
|
const lines = [];
|
|
1263
|
-
|
|
1276
|
+
const queue = [...batch];
|
|
1277
|
+
for (let step = queue.shift(); step !== void 0; step = queue.shift()) {
|
|
1264
1278
|
if (turn.signal.aborted) break;
|
|
1265
1279
|
if ("expectInput" in step) continue;
|
|
1266
1280
|
if ("awaitSteer" in step) {
|
|
1267
1281
|
const ready = await turn.awaitSteer?.(step.awaitSteer.timeoutMs, turn.signal) ?? false;
|
|
1268
1282
|
if (turn.signal.aborted) break;
|
|
1269
1283
|
lines.push(`mcp-send awaitSteer${ready ? ": ready" : " failed: timeout"}`);
|
|
1284
|
+
const after = (playback.current ?? picked) + 1;
|
|
1285
|
+
const following = playback.line?.turns[after];
|
|
1286
|
+
if (!ready && step.awaitSteer.continueOnTimeout === true && following !== void 0 && !playback.consumed.has(after)) {
|
|
1287
|
+
playback.consumed.add(after);
|
|
1288
|
+
playback.current = after;
|
|
1289
|
+
queue.push(...following);
|
|
1290
|
+
}
|
|
1270
1291
|
} else if ("wait" in step) {
|
|
1271
1292
|
const missing = opened.missing(step.wait.server);
|
|
1272
1293
|
if (step.wait.server !== void 0 && missing !== void 0) {
|
|
@@ -1282,7 +1303,19 @@ function createPlaybookRunner(input) {
|
|
|
1282
1303
|
lines.push(`mcp-send ${step.waitFor.tool} failed: ${missing}`);
|
|
1283
1304
|
continue;
|
|
1284
1305
|
}
|
|
1285
|
-
|
|
1306
|
+
let args;
|
|
1307
|
+
try {
|
|
1308
|
+
args = resolveArguments(
|
|
1309
|
+
step.waitFor.arguments ?? {},
|
|
1310
|
+
turn.threadId,
|
|
1311
|
+
playback.results,
|
|
1312
|
+
playback.history
|
|
1313
|
+
);
|
|
1314
|
+
} catch (error) {
|
|
1315
|
+
lines.push(`mcp-send ${step.waitFor.tool} failed: ${String(error)}`);
|
|
1316
|
+
continue;
|
|
1317
|
+
}
|
|
1318
|
+
const ready = await waitForCount(step, args, safeTurn, opened);
|
|
1286
1319
|
if (turn.signal.aborted) break;
|
|
1287
1320
|
const { id, tool, path: path2, atLeast } = step.waitFor;
|
|
1288
1321
|
if (ready !== void 0 && id !== void 0) playback.results.set(id, ready.result);
|
|
@@ -1863,7 +1896,8 @@ ${more ?? "playbook: steered input not handled"}`;
|
|
|
1863
1896
|
const steerable = script === "lane" || playbook !== void 0;
|
|
1864
1897
|
const steered = acceptLaneSteer({
|
|
1865
1898
|
running,
|
|
1866
|
-
|
|
1899
|
+
// 剧本只在正在跑的那一批写明 awaitSteer 时收插话;其余照下面「目标轮已收口」一样拒,宿主另起一轮。
|
|
1900
|
+
enabled: script === "lane" || (playbook?.expectsSteer(input.threadId) ?? false),
|
|
1867
1901
|
expectedTurnId: input.expectedTurnId,
|
|
1868
1902
|
text: input.input,
|
|
1869
1903
|
threadId: input.threadId,
|
|
@@ -1956,7 +1990,7 @@ var executors = [
|
|
|
1956
1990
|
];
|
|
1957
1991
|
var index_default = definePlugin({
|
|
1958
1992
|
id: "agent-fake",
|
|
1959
|
-
apiVersion:
|
|
1993
|
+
apiVersion: 37,
|
|
1960
1994
|
executors,
|
|
1961
1995
|
activate(ctx) {
|
|
1962
1996
|
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.3389",
|
|
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.3389",
|
|
25
25
|
"ycodium-executor-testing": "0.0.0"
|
|
26
26
|
},
|
|
27
27
|
"ycodium": {
|