@ycodium-ai/agent-fake 0.2.3976 → 0.2.3990
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 +269 -191
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { definePlugin } from "@ycodium-ai/plugin-api";
|
|
3
3
|
|
|
4
4
|
// ../../packages/plugin-api/src/version.ts
|
|
5
|
-
var PLUGIN_API_VERSION =
|
|
5
|
+
var PLUGIN_API_VERSION = 77;
|
|
6
6
|
|
|
7
7
|
// src/card.ts
|
|
8
8
|
var FAKE_SCRIPTS = [
|
|
@@ -101,38 +101,32 @@ var FAKE_CARD = {
|
|
|
101
101
|
import * as NodeCrypto from "node:crypto";
|
|
102
102
|
|
|
103
103
|
// src/fakeEventBus.ts
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
}
|
|
114
|
-
this.#queued.push(event);
|
|
115
|
-
}
|
|
116
|
-
close() {
|
|
117
|
-
if (this.#closed) return;
|
|
118
|
-
this.#closed = true;
|
|
119
|
-
while (this.#waiters.length > 0) {
|
|
120
|
-
this.#waiters.shift()?.({ done: true, value: void 0 });
|
|
104
|
+
function createFakeEmitter(input) {
|
|
105
|
+
return (type, threadId, payload, extra) => {
|
|
106
|
+
const context = input.thread(threadId);
|
|
107
|
+
if (!context) return;
|
|
108
|
+
if (type.startsWith("task.")) {
|
|
109
|
+
const task = payload;
|
|
110
|
+
if (type === "task.completed" || ["completed", "failed", "cancelled", "interrupted", "idle"].includes(task.status ?? ""))
|
|
111
|
+
context.tasks.delete(task.taskId);
|
|
112
|
+
else context.tasks.set(task.taskId, { ...context.tasks.get(task.taskId), ...task });
|
|
121
113
|
}
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
114
|
+
context.events.emit({
|
|
115
|
+
eventId: input.nextId(),
|
|
116
|
+
provider: "fake",
|
|
117
|
+
providerInstanceId: input.instanceId,
|
|
118
|
+
threadId,
|
|
119
|
+
createdAt: input.now(),
|
|
120
|
+
type,
|
|
121
|
+
payload,
|
|
122
|
+
...extra
|
|
123
|
+
});
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
function settleFakeTasks(context, threadId, emit) {
|
|
127
|
+
for (const [taskId, task] of context.tasks)
|
|
128
|
+
emit("task.completed", threadId, { ...task, taskId, status: "stopped" });
|
|
129
|
+
}
|
|
136
130
|
|
|
137
131
|
// src/approvalToolScript.ts
|
|
138
132
|
function createApprovalToolScript({
|
|
@@ -302,7 +296,6 @@ function createFakeSessionLifecycle(input) {
|
|
|
302
296
|
if (result.status === "rejected") throw result.reason;
|
|
303
297
|
}
|
|
304
298
|
await input.disposePlaybook();
|
|
305
|
-
input.closeEvents();
|
|
306
299
|
})().then(disposing.resolve, disposing.reject);
|
|
307
300
|
return disposal;
|
|
308
301
|
}
|
|
@@ -888,115 +881,8 @@ function createPlaybookMcp(input) {
|
|
|
888
881
|
};
|
|
889
882
|
}
|
|
890
883
|
|
|
891
|
-
// src/
|
|
892
|
-
var lineClaimsSession = (line, threadId, title) => (line.match.threadId === void 0 || line.match.threadId.test(threadId)) && (line.match.title === void 0 || title !== void 0 && title.includes(line.match.title));
|
|
884
|
+
// src/playbookResolve.ts
|
|
893
885
|
var isRecord2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
894
|
-
var isName = (value) => typeof value === "string" && value.length > 0;
|
|
895
|
-
var onlyKeys = (value, keys) => Object.keys(value).every((key) => keys.includes(key));
|
|
896
|
-
var parseTimeout = (value) => {
|
|
897
|
-
const timeoutMs = value === void 0 ? 3e4 : value;
|
|
898
|
-
return typeof timeoutMs === "number" && Number.isFinite(timeoutMs) && timeoutMs >= 0 ? timeoutMs : null;
|
|
899
|
-
};
|
|
900
|
-
function parseStep(value) {
|
|
901
|
-
if (!isRecord2(value)) return null;
|
|
902
|
-
if (Object.hasOwn(value, "expectInput"))
|
|
903
|
-
return onlyKeys(value, ["expectInput"]) && isName(value.expectInput) ? { expectInput: value.expectInput } : null;
|
|
904
|
-
if (Object.hasOwn(value, "awaitSteer")) {
|
|
905
|
-
const wait = value.awaitSteer;
|
|
906
|
-
if (!onlyKeys(value, ["awaitSteer"]) || !isRecord2(wait) || !onlyKeys(wait, ["timeoutMs", "continueOnTimeout"]) || Object.hasOwn(wait, "continueOnTimeout") && wait.continueOnTimeout !== true)
|
|
907
|
-
return null;
|
|
908
|
-
const timeoutMs = parseTimeout(wait.timeoutMs);
|
|
909
|
-
if (timeoutMs === null) return null;
|
|
910
|
-
const onTimeout = wait.continueOnTimeout === true ? { continueOnTimeout: true } : {};
|
|
911
|
-
return { awaitSteer: { timeoutMs, ...onTimeout } };
|
|
912
|
-
}
|
|
913
|
-
if (Object.hasOwn(value, "wait")) {
|
|
914
|
-
const wait = value.wait;
|
|
915
|
-
if (!onlyKeys(value, ["wait"]) || !isRecord2(wait) || !isName(wait.tool)) return null;
|
|
916
|
-
if (!onlyKeys(wait, ["server", "tool", "timeoutMs"]) || wait.server !== void 0 && !isName(wait.server))
|
|
917
|
-
return null;
|
|
918
|
-
const timeoutMs = parseTimeout(wait.timeoutMs);
|
|
919
|
-
return timeoutMs === null ? null : {
|
|
920
|
-
wait: {
|
|
921
|
-
tool: wait.tool,
|
|
922
|
-
timeoutMs,
|
|
923
|
-
...wait.server === void 0 ? {} : { server: wait.server }
|
|
924
|
-
}
|
|
925
|
-
};
|
|
926
|
-
}
|
|
927
|
-
if (Object.hasOwn(value, "waitFor")) {
|
|
928
|
-
const wait = value.waitFor;
|
|
929
|
-
if (!onlyKeys(value, ["waitFor"]) || !isRecord2(wait) || !isName(wait.tool)) return null;
|
|
930
|
-
if (!onlyKeys(wait, ["id", "server", "tool", "arguments", "path", "atLeast", "timeoutMs"]) || !isName(wait.path) || wait.id !== void 0 && !isName(wait.id) || wait.arguments !== void 0 && !isRecord2(wait.arguments) || wait.server !== void 0 && !isName(wait.server))
|
|
931
|
-
return null;
|
|
932
|
-
if (typeof wait.atLeast !== "number" || !Number.isInteger(wait.atLeast) || wait.atLeast < 0)
|
|
933
|
-
return null;
|
|
934
|
-
const timeoutMs = parseTimeout(wait.timeoutMs);
|
|
935
|
-
return timeoutMs === null ? null : {
|
|
936
|
-
waitFor: {
|
|
937
|
-
tool: wait.tool,
|
|
938
|
-
path: wait.path,
|
|
939
|
-
atLeast: wait.atLeast,
|
|
940
|
-
timeoutMs,
|
|
941
|
-
...wait.id === void 0 ? {} : { id: wait.id },
|
|
942
|
-
...wait.server === void 0 ? {} : { server: wait.server },
|
|
943
|
-
...wait.arguments === void 0 ? {} : { arguments: wait.arguments }
|
|
944
|
-
}
|
|
945
|
-
};
|
|
946
|
-
}
|
|
947
|
-
if (!onlyKeys(value, ["id", "server", "tool", "arguments"]) || !isName(value.tool) || value.server !== void 0 && !isName(value.server))
|
|
948
|
-
return null;
|
|
949
|
-
if (value.id !== void 0 && typeof value.id !== "string") return null;
|
|
950
|
-
if (value.arguments !== void 0 && !isRecord2(value.arguments)) return null;
|
|
951
|
-
return {
|
|
952
|
-
tool: value.tool,
|
|
953
|
-
...value.server === void 0 ? {} : { server: value.server },
|
|
954
|
-
...value.id === void 0 ? {} : { id: value.id },
|
|
955
|
-
...value.arguments === void 0 ? {} : { arguments: value.arguments }
|
|
956
|
-
};
|
|
957
|
-
}
|
|
958
|
-
function parsePlaybook(text) {
|
|
959
|
-
try {
|
|
960
|
-
const value = JSON.parse(text);
|
|
961
|
-
if (!isRecord2(value) || value.version !== 1) return { ok: false, reason: "version must be 1" };
|
|
962
|
-
if (!onlyKeys(value, ["version", "threads"]) || !Array.isArray(value.threads))
|
|
963
|
-
return { ok: false, reason: "threads must be an array" };
|
|
964
|
-
const threads = [];
|
|
965
|
-
for (const [index, line] of value.threads.entries()) {
|
|
966
|
-
if (!isRecord2(line) || !onlyKeys(line, ["match", "turns"]) || !isRecord2(line.match))
|
|
967
|
-
return { ok: false, reason: `threads[${index}] must have match and turns` };
|
|
968
|
-
const match = line.match;
|
|
969
|
-
if (!onlyKeys(match, ["threadId", "title"]) || match.threadId === void 0 && match.title === void 0 || match.threadId !== void 0 && typeof match.threadId !== "string" || match.title !== void 0 && typeof match.title !== "string")
|
|
970
|
-
return { ok: false, reason: `threads[${index}].match needs threadId or title` };
|
|
971
|
-
if (!Array.isArray(line.turns))
|
|
972
|
-
return { ok: false, reason: `threads[${index}].turns must be an array` };
|
|
973
|
-
const turns = [];
|
|
974
|
-
for (const [turn, batch] of line.turns.entries()) {
|
|
975
|
-
if (!Array.isArray(batch))
|
|
976
|
-
return { ok: false, reason: `threads[${index}].turns[${turn}] must be an array` };
|
|
977
|
-
const steps = [];
|
|
978
|
-
for (const entry of batch) {
|
|
979
|
-
const step = parseStep(entry);
|
|
980
|
-
const last = steps.at(-1);
|
|
981
|
-
if (step === null || "expectInput" in step && steps.length > 0 || last !== void 0 && "awaitSteer" in last)
|
|
982
|
-
return { ok: false, reason: `threads[${index}].turns[${turn}] has an invalid step` };
|
|
983
|
-
steps.push(step);
|
|
984
|
-
}
|
|
985
|
-
turns.push(steps);
|
|
986
|
-
}
|
|
987
|
-
threads.push({
|
|
988
|
-
match: {
|
|
989
|
-
...typeof match.threadId === "string" ? { threadId: new RegExp(match.threadId) } : {},
|
|
990
|
-
...typeof match.title === "string" ? { title: match.title } : {}
|
|
991
|
-
},
|
|
992
|
-
turns
|
|
993
|
-
});
|
|
994
|
-
}
|
|
995
|
-
return { ok: true, threads };
|
|
996
|
-
} catch (error) {
|
|
997
|
-
return { ok: false, reason: String(error) };
|
|
998
|
-
}
|
|
999
|
-
}
|
|
1000
886
|
var UnresolvedPlaceholder = class extends Error {
|
|
1001
887
|
};
|
|
1002
888
|
var splitPath = (path) => path.split(/\.(?![^\[]*\])/u);
|
|
@@ -1102,6 +988,127 @@ function stepResult(result) {
|
|
|
1102
988
|
return first.text;
|
|
1103
989
|
}
|
|
1104
990
|
}
|
|
991
|
+
|
|
992
|
+
// src/playbook.ts
|
|
993
|
+
var lineClaimsSession = (line, threadId, title) => (line.match.threadId === void 0 || line.match.threadId.test(threadId)) && (line.match.title === void 0 || title !== void 0 && title.includes(line.match.title));
|
|
994
|
+
var isName = (value) => typeof value === "string" && value.length > 0;
|
|
995
|
+
var onlyKeys = (value, keys) => Object.keys(value).every((key) => keys.includes(key));
|
|
996
|
+
var parseTimeout = (value) => {
|
|
997
|
+
const timeoutMs = value === void 0 ? 3e4 : value;
|
|
998
|
+
return typeof timeoutMs === "number" && Number.isFinite(timeoutMs) && timeoutMs >= 0 ? timeoutMs : null;
|
|
999
|
+
};
|
|
1000
|
+
function parseStep(value) {
|
|
1001
|
+
if (!isRecord2(value)) return null;
|
|
1002
|
+
if (Object.hasOwn(value, "expectInput"))
|
|
1003
|
+
return onlyKeys(value, ["expectInput"]) && isName(value.expectInput) ? { expectInput: value.expectInput } : null;
|
|
1004
|
+
if (Object.hasOwn(value, "awaitSteer")) {
|
|
1005
|
+
const wait = value.awaitSteer;
|
|
1006
|
+
if (!onlyKeys(value, ["awaitSteer"]) || !isRecord2(wait) || !onlyKeys(wait, ["timeoutMs", "continueOnTimeout"]) || Object.hasOwn(wait, "continueOnTimeout") && wait.continueOnTimeout !== true)
|
|
1007
|
+
return null;
|
|
1008
|
+
const timeoutMs = parseTimeout(wait.timeoutMs);
|
|
1009
|
+
if (timeoutMs === null) return null;
|
|
1010
|
+
const onTimeout = wait.continueOnTimeout === true ? { continueOnTimeout: true } : {};
|
|
1011
|
+
return { awaitSteer: { timeoutMs, ...onTimeout } };
|
|
1012
|
+
}
|
|
1013
|
+
if (Object.hasOwn(value, "wait")) {
|
|
1014
|
+
const wait = value.wait;
|
|
1015
|
+
if (!onlyKeys(value, ["wait"]) || !isRecord2(wait) || !isName(wait.tool)) return null;
|
|
1016
|
+
if (!onlyKeys(wait, ["server", "tool", "timeoutMs"]) || wait.server !== void 0 && !isName(wait.server))
|
|
1017
|
+
return null;
|
|
1018
|
+
const timeoutMs = parseTimeout(wait.timeoutMs);
|
|
1019
|
+
return timeoutMs === null ? null : {
|
|
1020
|
+
wait: {
|
|
1021
|
+
tool: wait.tool,
|
|
1022
|
+
timeoutMs,
|
|
1023
|
+
...wait.server === void 0 ? {} : { server: wait.server }
|
|
1024
|
+
}
|
|
1025
|
+
};
|
|
1026
|
+
}
|
|
1027
|
+
if (Object.hasOwn(value, "waitFor")) {
|
|
1028
|
+
const wait = value.waitFor;
|
|
1029
|
+
if (!onlyKeys(value, ["waitFor"]) || !isRecord2(wait) || !isName(wait.tool)) return null;
|
|
1030
|
+
if (!onlyKeys(wait, [
|
|
1031
|
+
"id",
|
|
1032
|
+
"server",
|
|
1033
|
+
"tool",
|
|
1034
|
+
"arguments",
|
|
1035
|
+
"path",
|
|
1036
|
+
"atLeast",
|
|
1037
|
+
"timeoutMs",
|
|
1038
|
+
"turnGate",
|
|
1039
|
+
"continueOnTimeout"
|
|
1040
|
+
]) || wait.continueOnTimeout !== void 0 && wait.continueOnTimeout !== true || wait.turnGate !== void 0 && wait.turnGate !== true || !isName(wait.path) || wait.id !== void 0 && !isName(wait.id) || wait.arguments !== void 0 && !isRecord2(wait.arguments) || wait.server !== void 0 && !isName(wait.server))
|
|
1041
|
+
return null;
|
|
1042
|
+
if (typeof wait.atLeast !== "number" || !Number.isInteger(wait.atLeast) || wait.atLeast < 0)
|
|
1043
|
+
return null;
|
|
1044
|
+
const timeoutMs = parseTimeout(wait.timeoutMs);
|
|
1045
|
+
return timeoutMs === null ? null : {
|
|
1046
|
+
waitFor: {
|
|
1047
|
+
tool: wait.tool,
|
|
1048
|
+
path: wait.path,
|
|
1049
|
+
atLeast: wait.atLeast,
|
|
1050
|
+
timeoutMs,
|
|
1051
|
+
...wait.id === void 0 ? {} : { id: wait.id },
|
|
1052
|
+
...wait.server === void 0 ? {} : { server: wait.server },
|
|
1053
|
+
...wait.arguments === void 0 ? {} : { arguments: wait.arguments },
|
|
1054
|
+
...wait.turnGate === true ? { turnGate: true } : {},
|
|
1055
|
+
...wait.continueOnTimeout === true ? { continueOnTimeout: true } : {}
|
|
1056
|
+
}
|
|
1057
|
+
};
|
|
1058
|
+
}
|
|
1059
|
+
if (!onlyKeys(value, ["id", "server", "tool", "arguments"]) || !isName(value.tool) || value.server !== void 0 && !isName(value.server))
|
|
1060
|
+
return null;
|
|
1061
|
+
if (value.id !== void 0 && typeof value.id !== "string") return null;
|
|
1062
|
+
if (value.arguments !== void 0 && !isRecord2(value.arguments)) return null;
|
|
1063
|
+
return {
|
|
1064
|
+
tool: value.tool,
|
|
1065
|
+
...value.server === void 0 ? {} : { server: value.server },
|
|
1066
|
+
...value.id === void 0 ? {} : { id: value.id },
|
|
1067
|
+
...value.arguments === void 0 ? {} : { arguments: value.arguments }
|
|
1068
|
+
};
|
|
1069
|
+
}
|
|
1070
|
+
function parsePlaybook(text) {
|
|
1071
|
+
try {
|
|
1072
|
+
const value = JSON.parse(text);
|
|
1073
|
+
if (!isRecord2(value) || value.version !== 1) return { ok: false, reason: "version must be 1" };
|
|
1074
|
+
if (!onlyKeys(value, ["version", "threads"]) || !Array.isArray(value.threads))
|
|
1075
|
+
return { ok: false, reason: "threads must be an array" };
|
|
1076
|
+
const threads = [];
|
|
1077
|
+
for (const [index, line] of value.threads.entries()) {
|
|
1078
|
+
if (!isRecord2(line) || !onlyKeys(line, ["match", "turns"]) || !isRecord2(line.match))
|
|
1079
|
+
return { ok: false, reason: `threads[${index}] must have match and turns` };
|
|
1080
|
+
const match = line.match;
|
|
1081
|
+
if (!onlyKeys(match, ["threadId", "title"]) || match.threadId === void 0 && match.title === void 0 || match.threadId !== void 0 && typeof match.threadId !== "string" || match.title !== void 0 && typeof match.title !== "string")
|
|
1082
|
+
return { ok: false, reason: `threads[${index}].match needs threadId or title` };
|
|
1083
|
+
if (!Array.isArray(line.turns))
|
|
1084
|
+
return { ok: false, reason: `threads[${index}].turns must be an array` };
|
|
1085
|
+
const turns = [];
|
|
1086
|
+
for (const [turn, batch] of line.turns.entries()) {
|
|
1087
|
+
if (!Array.isArray(batch))
|
|
1088
|
+
return { ok: false, reason: `threads[${index}].turns[${turn}] must be an array` };
|
|
1089
|
+
const steps = [];
|
|
1090
|
+
for (const entry of batch) {
|
|
1091
|
+
const step = parseStep(entry);
|
|
1092
|
+
const last = steps.at(-1);
|
|
1093
|
+
if (step === null || "expectInput" in step && steps.length > 0 || last !== void 0 && "awaitSteer" in last)
|
|
1094
|
+
return { ok: false, reason: `threads[${index}].turns[${turn}] has an invalid step` };
|
|
1095
|
+
steps.push(step);
|
|
1096
|
+
}
|
|
1097
|
+
turns.push(steps);
|
|
1098
|
+
}
|
|
1099
|
+
threads.push({
|
|
1100
|
+
match: {
|
|
1101
|
+
...typeof match.threadId === "string" ? { threadId: new RegExp(match.threadId) } : {},
|
|
1102
|
+
...typeof match.title === "string" ? { title: match.title } : {}
|
|
1103
|
+
},
|
|
1104
|
+
turns
|
|
1105
|
+
});
|
|
1106
|
+
}
|
|
1107
|
+
return { ok: true, threads };
|
|
1108
|
+
} catch (error) {
|
|
1109
|
+
return { ok: false, reason: String(error) };
|
|
1110
|
+
}
|
|
1111
|
+
}
|
|
1105
1112
|
function pickBatch(line, consumed, input) {
|
|
1106
1113
|
let earliest = true;
|
|
1107
1114
|
for (const [index, batch] of line.turns.entries()) {
|
|
@@ -1143,25 +1150,38 @@ var waitForTool = (step, input, clients) => poll(step.wait.timeoutMs, input, asy
|
|
|
1143
1150
|
(tool) => isRecord2(tool) && (tool.name === step.wait.tool || tool.name === `mcp__${step.wait.server ?? "ycodium"}__${step.wait.tool}`)
|
|
1144
1151
|
);
|
|
1145
1152
|
});
|
|
1153
|
+
async function readCount(step, args, clients, signal) {
|
|
1154
|
+
const called = await clients.request(
|
|
1155
|
+
step.waitFor.server,
|
|
1156
|
+
"tools/call",
|
|
1157
|
+
{ name: step.waitFor.tool, arguments: args },
|
|
1158
|
+
signal
|
|
1159
|
+
);
|
|
1160
|
+
if (isRecord2(called) && called.isError === true) return void 0;
|
|
1161
|
+
const result = stepResult(called);
|
|
1162
|
+
const value = valueAt(result, splitPath(step.waitFor.path));
|
|
1163
|
+
const count = Array.isArray(value) ? value.length : typeof value === "number" ? value : value === void 0 || value === null ? 0 : 1;
|
|
1164
|
+
return count < step.waitFor.atLeast ? void 0 : { result };
|
|
1165
|
+
}
|
|
1146
1166
|
async function waitForCount(step, args, input, clients) {
|
|
1147
1167
|
let ready;
|
|
1148
1168
|
const ok = await poll(step.waitFor.timeoutMs, input, async (signal) => {
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
"tools/call",
|
|
1152
|
-
{ name: step.waitFor.tool, arguments: args },
|
|
1153
|
-
signal
|
|
1154
|
-
);
|
|
1155
|
-
if (isRecord2(called) && called.isError === true) return false;
|
|
1156
|
-
const result = stepResult(called);
|
|
1157
|
-
const value = valueAt(result, splitPath(step.waitFor.path));
|
|
1158
|
-
const count = Array.isArray(value) ? value.length : typeof value === "number" ? value : value === void 0 || value === null ? 0 : 1;
|
|
1159
|
-
if (count < step.waitFor.atLeast) return false;
|
|
1160
|
-
ready = { result };
|
|
1161
|
-
return true;
|
|
1169
|
+
ready = await readCount(step, args, clients, signal);
|
|
1170
|
+
return ready !== void 0;
|
|
1162
1171
|
});
|
|
1163
1172
|
return ok ? ready : void 0;
|
|
1164
1173
|
}
|
|
1174
|
+
var REQUIRED_WAIT_TOOLS = /* @__PURE__ */ new Set([
|
|
1175
|
+
"work_history",
|
|
1176
|
+
"work_list_comments",
|
|
1177
|
+
"work_list_items"
|
|
1178
|
+
]);
|
|
1179
|
+
var GATE_PROBE_TIMEOUT_MS = 5e3;
|
|
1180
|
+
var REQUIRED_WAIT_FAILED = "playbook required wait failed:";
|
|
1181
|
+
function turnGateOf(batch) {
|
|
1182
|
+
const first = batch.find((step) => !("expectInput" in step));
|
|
1183
|
+
return first !== void 0 && "waitFor" in first && first.waitFor.turnGate === true ? first : void 0;
|
|
1184
|
+
}
|
|
1165
1185
|
async function runCall(step, playback, input, clients) {
|
|
1166
1186
|
let args = step.arguments ?? {};
|
|
1167
1187
|
let failure;
|
|
@@ -1251,22 +1271,21 @@ function createPlaybookRunner(input) {
|
|
|
1251
1271
|
if (playback === void 0) {
|
|
1252
1272
|
const title = titles.get(turn.threadId);
|
|
1253
1273
|
const index = parsed.threads.findIndex(
|
|
1254
|
-
(
|
|
1274
|
+
(line2, index2) => !claimed.has(index2) && lineClaimsSession(line2, turn.threadId, title)
|
|
1255
1275
|
);
|
|
1256
1276
|
if (index >= 0) claimed.add(index);
|
|
1257
1277
|
playback = {
|
|
1258
1278
|
line: parsed.threads[index] ?? null,
|
|
1259
1279
|
consumed: /* @__PURE__ */ new Set(),
|
|
1280
|
+
owed: /* @__PURE__ */ new Set(),
|
|
1260
1281
|
results: /* @__PURE__ */ new Map(),
|
|
1261
1282
|
history: []
|
|
1262
1283
|
};
|
|
1263
1284
|
sessions.set(turn.threadId, playback);
|
|
1264
1285
|
}
|
|
1265
|
-
const
|
|
1266
|
-
const
|
|
1267
|
-
if (picked === void 0
|
|
1268
|
-
playback.consumed.add(picked);
|
|
1269
|
-
playback.current = picked;
|
|
1286
|
+
const { line } = playback;
|
|
1287
|
+
const picked = line === null ? void 0 : pickBatch(line, playback.consumed, turn.input ?? "");
|
|
1288
|
+
if (picked === void 0 && playback.owed.size === 0) return null;
|
|
1270
1289
|
let opened = clients.get(turn.threadId);
|
|
1271
1290
|
if (opened === void 0) {
|
|
1272
1291
|
opened = createPlaybookMcp({ fetch: turn.fetch, servers: turn.mcpServers });
|
|
@@ -1278,16 +1297,75 @@ function createPlaybookRunner(input) {
|
|
|
1278
1297
|
turn.emit(args[0], args[1], redactMcpUrls(args[2], turn.mcpServers), args[3]);
|
|
1279
1298
|
}
|
|
1280
1299
|
};
|
|
1300
|
+
const gateMet = async (gate) => {
|
|
1301
|
+
try {
|
|
1302
|
+
const args = resolveArguments(
|
|
1303
|
+
gate.waitFor.arguments ?? {},
|
|
1304
|
+
turn.threadId,
|
|
1305
|
+
playback.results,
|
|
1306
|
+
playback.history
|
|
1307
|
+
);
|
|
1308
|
+
const signal = AbortSignal.any([
|
|
1309
|
+
turn.signal,
|
|
1310
|
+
AbortSignal.timeout(Math.min(GATE_PROBE_TIMEOUT_MS, gate.waitFor.timeoutMs))
|
|
1311
|
+
]);
|
|
1312
|
+
return await readCount(gate, args, opened, signal) !== void 0;
|
|
1313
|
+
} catch {
|
|
1314
|
+
return false;
|
|
1315
|
+
}
|
|
1316
|
+
};
|
|
1281
1317
|
const lines = [];
|
|
1282
|
-
const queue = [
|
|
1283
|
-
|
|
1318
|
+
const queue = [];
|
|
1319
|
+
const pickedBatch = picked === void 0 ? void 0 : line?.turns[picked];
|
|
1320
|
+
if (picked !== void 0 && pickedBatch !== void 0) {
|
|
1321
|
+
const gate = turnGateOf(pickedBatch);
|
|
1322
|
+
const aheadPending = [...line?.turns.keys() ?? []].some(
|
|
1323
|
+
(index) => index < picked && !playback.consumed.has(index)
|
|
1324
|
+
);
|
|
1325
|
+
if (gate !== void 0 && aheadPending && !await gateMet(gate)) {
|
|
1326
|
+
if (turn.signal.aborted) return "";
|
|
1327
|
+
playback.owed.add(picked);
|
|
1328
|
+
return `mcp-send deferred batch ${String(picked)}: ${gate.waitFor.tool} ${gate.waitFor.path} < ${String(gate.waitFor.atLeast)}`;
|
|
1329
|
+
}
|
|
1330
|
+
playback.consumed.add(picked);
|
|
1331
|
+
playback.owed.delete(picked);
|
|
1332
|
+
playback.current = picked;
|
|
1333
|
+
queue.push(...pickedBatch);
|
|
1334
|
+
}
|
|
1335
|
+
let ran = pickedBatch !== void 0;
|
|
1336
|
+
let halted = false;
|
|
1337
|
+
const next = async () => {
|
|
1338
|
+
while (queue.length === 0 && !halted && !turn.signal.aborted) {
|
|
1339
|
+
const [owed] = [...playback.owed].sort((left, right) => left - right);
|
|
1340
|
+
const batch = owed === void 0 ? void 0 : line?.turns[owed];
|
|
1341
|
+
if (owed === void 0 || batch === void 0) break;
|
|
1342
|
+
const gate = turnGateOf(batch);
|
|
1343
|
+
if (gate !== void 0 && !await gateMet(gate)) break;
|
|
1344
|
+
playback.owed.delete(owed);
|
|
1345
|
+
playback.consumed.add(owed);
|
|
1346
|
+
playback.current = owed;
|
|
1347
|
+
queue.push(...batch);
|
|
1348
|
+
ran = true;
|
|
1349
|
+
}
|
|
1350
|
+
return queue.shift();
|
|
1351
|
+
};
|
|
1352
|
+
const haltRequired = (step, reason) => {
|
|
1353
|
+
const { tool, path: path2, atLeast } = step.waitFor;
|
|
1354
|
+
const who = line?.match.title ?? line?.match.threadId?.source ?? "";
|
|
1355
|
+
lines.push(
|
|
1356
|
+
`${REQUIRED_WAIT_FAILED} session ${turn.threadId} line ${who} batch ${String(playback.current)}: ${tool} ${path2} >= ${String(atLeast)} ${reason}`
|
|
1357
|
+
);
|
|
1358
|
+
halted = true;
|
|
1359
|
+
queue.length = 0;
|
|
1360
|
+
};
|
|
1361
|
+
for (let step = await next(); step !== void 0; step = await next()) {
|
|
1284
1362
|
if (turn.signal.aborted) break;
|
|
1285
1363
|
if ("expectInput" in step) continue;
|
|
1286
1364
|
if ("awaitSteer" in step) {
|
|
1287
1365
|
const ready = await turn.awaitSteer?.(step.awaitSteer.timeoutMs, turn.signal) ?? false;
|
|
1288
1366
|
if (turn.signal.aborted) break;
|
|
1289
1367
|
lines.push(`mcp-send awaitSteer${ready ? ": ready" : " failed: timeout"}`);
|
|
1290
|
-
const after = (playback.current ??
|
|
1368
|
+
const after = (playback.current ?? -1) + 1;
|
|
1291
1369
|
const following = playback.line?.turns[after];
|
|
1292
1370
|
if (!ready && step.awaitSteer.continueOnTimeout === true && following !== void 0 && !playback.consumed.has(after)) {
|
|
1293
1371
|
playback.consumed.add(after);
|
|
@@ -1304,9 +1382,12 @@ function createPlaybookRunner(input) {
|
|
|
1304
1382
|
if (turn.signal.aborted) break;
|
|
1305
1383
|
lines.push(`mcp-send wait ${step.wait.tool}${ready ? ": ready" : " failed: timeout"}`);
|
|
1306
1384
|
} else if ("waitFor" in step) {
|
|
1385
|
+
const { id, tool, path: path2, atLeast } = step.waitFor;
|
|
1386
|
+
const required = REQUIRED_WAIT_TOOLS.has(tool) && step.waitFor.continueOnTimeout !== true;
|
|
1307
1387
|
const missing = opened.missing(step.waitFor.server);
|
|
1308
1388
|
if (step.waitFor.server !== void 0 && missing !== void 0) {
|
|
1309
|
-
lines.push(`mcp-send ${
|
|
1389
|
+
lines.push(`mcp-send ${tool} failed: ${missing}`);
|
|
1390
|
+
if (required) haltRequired(step, missing);
|
|
1310
1391
|
continue;
|
|
1311
1392
|
}
|
|
1312
1393
|
let args;
|
|
@@ -1318,20 +1399,23 @@ function createPlaybookRunner(input) {
|
|
|
1318
1399
|
playback.history
|
|
1319
1400
|
);
|
|
1320
1401
|
} catch (error) {
|
|
1321
|
-
lines.push(`mcp-send ${
|
|
1402
|
+
lines.push(`mcp-send ${tool} failed: ${String(error)}`);
|
|
1403
|
+
if (required) haltRequired(step, String(error));
|
|
1322
1404
|
continue;
|
|
1323
1405
|
}
|
|
1324
1406
|
const ready = await waitForCount(step, args, safeTurn, opened);
|
|
1325
1407
|
if (turn.signal.aborted) break;
|
|
1326
|
-
const { id, tool, path: path2, atLeast } = step.waitFor;
|
|
1327
1408
|
if (ready !== void 0 && id !== void 0) playback.results.set(id, ready.result);
|
|
1328
1409
|
lines.push(
|
|
1329
1410
|
`mcp-send waitFor ${tool} ${path2} >= ${String(atLeast)}${ready ? ": ready" : " failed: timeout"}`
|
|
1330
1411
|
);
|
|
1412
|
+
if (ready === void 0 && required)
|
|
1413
|
+
haltRequired(step, `not reached within ${String(step.waitFor.timeoutMs)} ms`);
|
|
1331
1414
|
} else {
|
|
1332
1415
|
lines.push(await runCall(step, playback, safeTurn, opened));
|
|
1333
1416
|
}
|
|
1334
1417
|
}
|
|
1418
|
+
if (!ran) return null;
|
|
1335
1419
|
return redactMcpUrls(lines.join("\n"), turn.mcpServers);
|
|
1336
1420
|
}
|
|
1337
1421
|
};
|
|
@@ -1580,7 +1664,6 @@ var FAKE_MODELS = [
|
|
|
1580
1664
|
{ slug: "fake-large", name: "Fake Large" }
|
|
1581
1665
|
];
|
|
1582
1666
|
var createFakeInstance = (config, host) => {
|
|
1583
|
-
const events = new EventBus();
|
|
1584
1667
|
const threads = /* @__PURE__ */ new Map();
|
|
1585
1668
|
const script = parseScript(config);
|
|
1586
1669
|
const playbook = script === "mcp-send" ? createPlaybookRunner(config) : void 0;
|
|
@@ -1611,22 +1694,12 @@ var createFakeInstance = (config, host) => {
|
|
|
1611
1694
|
let disposed = false;
|
|
1612
1695
|
const nextId = (prefix) => `${prefix}-fake-${++seq}-${NodeCrypto.randomUUID()}`;
|
|
1613
1696
|
const now = () => (/* @__PURE__ */ new Date()).toISOString();
|
|
1614
|
-
const emit = (
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
createdAt: now(),
|
|
1621
|
-
type,
|
|
1622
|
-
payload,
|
|
1623
|
-
...extra?.turnId === void 0 ? {} : { turnId: extra.turnId },
|
|
1624
|
-
...extra?.itemId === void 0 ? {} : { itemId: extra.itemId },
|
|
1625
|
-
...extra?.requestId === void 0 ? {} : { requestId: extra.requestId },
|
|
1626
|
-
...extra?.originBindingGeneration === void 0 ? {} : { originBindingGeneration: extra.originBindingGeneration },
|
|
1627
|
-
...extra?.originResumeCursor === void 0 ? {} : { originResumeCursor: extra.originResumeCursor }
|
|
1628
|
-
});
|
|
1629
|
-
};
|
|
1697
|
+
const emit = createFakeEmitter({
|
|
1698
|
+
thread: (threadId) => threads.get(threadId),
|
|
1699
|
+
nextId: () => nextId("evt"),
|
|
1700
|
+
now,
|
|
1701
|
+
instanceId: host.instanceId
|
|
1702
|
+
});
|
|
1630
1703
|
const lifecycle = createFakeSessionLifecycle({
|
|
1631
1704
|
threads,
|
|
1632
1705
|
abort: (threadId) => inflight.get(threadId)?.abort(),
|
|
@@ -1634,19 +1707,17 @@ var createFakeInstance = (config, host) => {
|
|
|
1634
1707
|
// 代次与续接游标取被关闭的那份 session,不在发送时重查当前会话。
|
|
1635
1708
|
exited: (session) => {
|
|
1636
1709
|
if (stopWithoutExit) return;
|
|
1710
|
+
const context = threads.get(session.threadId);
|
|
1711
|
+
if (context) settleFakeTasks(context, session.threadId, emit);
|
|
1637
1712
|
emit(
|
|
1638
1713
|
"session.exited",
|
|
1639
1714
|
session.threadId,
|
|
1640
1715
|
{ reason: "stopped" },
|
|
1641
|
-
{
|
|
1642
|
-
...session.bindingGeneration === void 0 ? {} : { originBindingGeneration: session.bindingGeneration },
|
|
1643
|
-
...session.resumeCursor === void 0 ? {} : { originResumeCursor: session.resumeCursor }
|
|
1644
|
-
}
|
|
1716
|
+
session.resumeCursor === void 0 ? {} : { originResumeCursor: session.resumeCursor }
|
|
1645
1717
|
);
|
|
1646
1718
|
},
|
|
1647
1719
|
forget: (threadId) => steeredInputs.delete(threadId),
|
|
1648
|
-
disposePlaybook: () => playbook?.dispose()
|
|
1649
|
-
closeEvents: () => events.close()
|
|
1720
|
+
disposePlaybook: () => playbook?.dispose()
|
|
1650
1721
|
});
|
|
1651
1722
|
const requireThread = (threadId) => {
|
|
1652
1723
|
const booked = threads.get(threadId);
|
|
@@ -1869,6 +1940,8 @@ ${more ?? "playbook: steered input not handled"}`;
|
|
|
1869
1940
|
if (signal?.aborted) throw new Error("aborted");
|
|
1870
1941
|
if (threads.get(input.threadId)?.closing !== void 0) throw new Error("session closing");
|
|
1871
1942
|
if (failStartSession !== void 0) throw new Error(failStartSession);
|
|
1943
|
+
const previous = threads.get(input.threadId);
|
|
1944
|
+
if (previous) await lifecycle.closeBooked(input.threadId, previous);
|
|
1872
1945
|
const cwd = input.cwd ?? host.workspacePath(input.threadId);
|
|
1873
1946
|
if (cwd.trim() === "") throw new Error("workspace path missing");
|
|
1874
1947
|
const createdAt = now();
|
|
@@ -1884,7 +1957,13 @@ ${more ?? "playbook: steered input not handled"}`;
|
|
|
1884
1957
|
...input.discardResumeCursor === true || input.resumeCursor === void 0 ? {} : { resumeCursor: input.resumeCursor },
|
|
1885
1958
|
...input.bindingGeneration === void 0 ? {} : { bindingGeneration: input.bindingGeneration }
|
|
1886
1959
|
};
|
|
1887
|
-
threads.set(input.threadId, {
|
|
1960
|
+
threads.set(input.threadId, {
|
|
1961
|
+
tasks: /* @__PURE__ */ new Map(),
|
|
1962
|
+
events: input.events,
|
|
1963
|
+
session,
|
|
1964
|
+
turns: [],
|
|
1965
|
+
mcpServers: input.mcpServers
|
|
1966
|
+
});
|
|
1888
1967
|
playbook?.startSession(input);
|
|
1889
1968
|
emit(
|
|
1890
1969
|
"session.started",
|
|
@@ -1974,7 +2053,6 @@ ${more ?? "playbook: steered input not handled"}`;
|
|
|
1974
2053
|
const booked = requireThread(threadId);
|
|
1975
2054
|
return { threadId, turns: booked.turns };
|
|
1976
2055
|
},
|
|
1977
|
-
events,
|
|
1978
2056
|
usage: createFakeUsage({ instanceId: host.instanceId, script: parseUsageScript(config) }),
|
|
1979
2057
|
dispose: async () => {
|
|
1980
2058
|
disposed = true;
|
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.3990",
|
|
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.3990",
|
|
25
25
|
"ycodium-executor-testing": "0.0.0"
|
|
26
26
|
},
|
|
27
27
|
"ycodium": {
|