@ycodium-ai/agent-fake 0.2.3935 → 0.2.3982
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 +272 -191
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
2
|
import { definePlugin } from "@ycodium-ai/plugin-api";
|
|
3
3
|
|
|
4
|
+
// ../../packages/plugin-api/src/version.ts
|
|
5
|
+
var PLUGIN_API_VERSION = 76;
|
|
6
|
+
|
|
4
7
|
// src/card.ts
|
|
5
8
|
var FAKE_SCRIPTS = [
|
|
6
9
|
"plain",
|
|
@@ -98,38 +101,32 @@ var FAKE_CARD = {
|
|
|
98
101
|
import * as NodeCrypto from "node:crypto";
|
|
99
102
|
|
|
100
103
|
// src/fakeEventBus.ts
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
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 });
|
|
110
113
|
}
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
this.#waiters.push(resolve);
|
|
128
|
-
});
|
|
129
|
-
}
|
|
130
|
-
};
|
|
131
|
-
}
|
|
132
|
-
};
|
|
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
|
+
}
|
|
133
130
|
|
|
134
131
|
// src/approvalToolScript.ts
|
|
135
132
|
function createApprovalToolScript({
|
|
@@ -299,7 +296,6 @@ function createFakeSessionLifecycle(input) {
|
|
|
299
296
|
if (result.status === "rejected") throw result.reason;
|
|
300
297
|
}
|
|
301
298
|
await input.disposePlaybook();
|
|
302
|
-
input.closeEvents();
|
|
303
299
|
})().then(disposing.resolve, disposing.reject);
|
|
304
300
|
return disposal;
|
|
305
301
|
}
|
|
@@ -885,115 +881,8 @@ function createPlaybookMcp(input) {
|
|
|
885
881
|
};
|
|
886
882
|
}
|
|
887
883
|
|
|
888
|
-
// src/
|
|
889
|
-
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
|
|
890
885
|
var isRecord2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
891
|
-
var isName = (value) => typeof value === "string" && value.length > 0;
|
|
892
|
-
var onlyKeys = (value, keys) => Object.keys(value).every((key) => keys.includes(key));
|
|
893
|
-
var parseTimeout = (value) => {
|
|
894
|
-
const timeoutMs = value === void 0 ? 3e4 : value;
|
|
895
|
-
return typeof timeoutMs === "number" && Number.isFinite(timeoutMs) && timeoutMs >= 0 ? timeoutMs : null;
|
|
896
|
-
};
|
|
897
|
-
function parseStep(value) {
|
|
898
|
-
if (!isRecord2(value)) return null;
|
|
899
|
-
if (Object.hasOwn(value, "expectInput"))
|
|
900
|
-
return onlyKeys(value, ["expectInput"]) && isName(value.expectInput) ? { expectInput: value.expectInput } : null;
|
|
901
|
-
if (Object.hasOwn(value, "awaitSteer")) {
|
|
902
|
-
const wait = value.awaitSteer;
|
|
903
|
-
if (!onlyKeys(value, ["awaitSteer"]) || !isRecord2(wait) || !onlyKeys(wait, ["timeoutMs", "continueOnTimeout"]) || Object.hasOwn(wait, "continueOnTimeout") && wait.continueOnTimeout !== true)
|
|
904
|
-
return null;
|
|
905
|
-
const timeoutMs = parseTimeout(wait.timeoutMs);
|
|
906
|
-
if (timeoutMs === null) return null;
|
|
907
|
-
const onTimeout = wait.continueOnTimeout === true ? { continueOnTimeout: true } : {};
|
|
908
|
-
return { awaitSteer: { timeoutMs, ...onTimeout } };
|
|
909
|
-
}
|
|
910
|
-
if (Object.hasOwn(value, "wait")) {
|
|
911
|
-
const wait = value.wait;
|
|
912
|
-
if (!onlyKeys(value, ["wait"]) || !isRecord2(wait) || !isName(wait.tool)) return null;
|
|
913
|
-
if (!onlyKeys(wait, ["server", "tool", "timeoutMs"]) || wait.server !== void 0 && !isName(wait.server))
|
|
914
|
-
return null;
|
|
915
|
-
const timeoutMs = parseTimeout(wait.timeoutMs);
|
|
916
|
-
return timeoutMs === null ? null : {
|
|
917
|
-
wait: {
|
|
918
|
-
tool: wait.tool,
|
|
919
|
-
timeoutMs,
|
|
920
|
-
...wait.server === void 0 ? {} : { server: wait.server }
|
|
921
|
-
}
|
|
922
|
-
};
|
|
923
|
-
}
|
|
924
|
-
if (Object.hasOwn(value, "waitFor")) {
|
|
925
|
-
const wait = value.waitFor;
|
|
926
|
-
if (!onlyKeys(value, ["waitFor"]) || !isRecord2(wait) || !isName(wait.tool)) return null;
|
|
927
|
-
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))
|
|
928
|
-
return null;
|
|
929
|
-
if (typeof wait.atLeast !== "number" || !Number.isInteger(wait.atLeast) || wait.atLeast < 0)
|
|
930
|
-
return null;
|
|
931
|
-
const timeoutMs = parseTimeout(wait.timeoutMs);
|
|
932
|
-
return timeoutMs === null ? null : {
|
|
933
|
-
waitFor: {
|
|
934
|
-
tool: wait.tool,
|
|
935
|
-
path: wait.path,
|
|
936
|
-
atLeast: wait.atLeast,
|
|
937
|
-
timeoutMs,
|
|
938
|
-
...wait.id === void 0 ? {} : { id: wait.id },
|
|
939
|
-
...wait.server === void 0 ? {} : { server: wait.server },
|
|
940
|
-
...wait.arguments === void 0 ? {} : { arguments: wait.arguments }
|
|
941
|
-
}
|
|
942
|
-
};
|
|
943
|
-
}
|
|
944
|
-
if (!onlyKeys(value, ["id", "server", "tool", "arguments"]) || !isName(value.tool) || value.server !== void 0 && !isName(value.server))
|
|
945
|
-
return null;
|
|
946
|
-
if (value.id !== void 0 && typeof value.id !== "string") return null;
|
|
947
|
-
if (value.arguments !== void 0 && !isRecord2(value.arguments)) return null;
|
|
948
|
-
return {
|
|
949
|
-
tool: value.tool,
|
|
950
|
-
...value.server === void 0 ? {} : { server: value.server },
|
|
951
|
-
...value.id === void 0 ? {} : { id: value.id },
|
|
952
|
-
...value.arguments === void 0 ? {} : { arguments: value.arguments }
|
|
953
|
-
};
|
|
954
|
-
}
|
|
955
|
-
function parsePlaybook(text) {
|
|
956
|
-
try {
|
|
957
|
-
const value = JSON.parse(text);
|
|
958
|
-
if (!isRecord2(value) || value.version !== 1) return { ok: false, reason: "version must be 1" };
|
|
959
|
-
if (!onlyKeys(value, ["version", "threads"]) || !Array.isArray(value.threads))
|
|
960
|
-
return { ok: false, reason: "threads must be an array" };
|
|
961
|
-
const threads = [];
|
|
962
|
-
for (const [index, line] of value.threads.entries()) {
|
|
963
|
-
if (!isRecord2(line) || !onlyKeys(line, ["match", "turns"]) || !isRecord2(line.match))
|
|
964
|
-
return { ok: false, reason: `threads[${index}] must have match and turns` };
|
|
965
|
-
const match = line.match;
|
|
966
|
-
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")
|
|
967
|
-
return { ok: false, reason: `threads[${index}].match needs threadId or title` };
|
|
968
|
-
if (!Array.isArray(line.turns))
|
|
969
|
-
return { ok: false, reason: `threads[${index}].turns must be an array` };
|
|
970
|
-
const turns = [];
|
|
971
|
-
for (const [turn, batch] of line.turns.entries()) {
|
|
972
|
-
if (!Array.isArray(batch))
|
|
973
|
-
return { ok: false, reason: `threads[${index}].turns[${turn}] must be an array` };
|
|
974
|
-
const steps = [];
|
|
975
|
-
for (const entry of batch) {
|
|
976
|
-
const step = parseStep(entry);
|
|
977
|
-
const last = steps.at(-1);
|
|
978
|
-
if (step === null || "expectInput" in step && steps.length > 0 || last !== void 0 && "awaitSteer" in last)
|
|
979
|
-
return { ok: false, reason: `threads[${index}].turns[${turn}] has an invalid step` };
|
|
980
|
-
steps.push(step);
|
|
981
|
-
}
|
|
982
|
-
turns.push(steps);
|
|
983
|
-
}
|
|
984
|
-
threads.push({
|
|
985
|
-
match: {
|
|
986
|
-
...typeof match.threadId === "string" ? { threadId: new RegExp(match.threadId) } : {},
|
|
987
|
-
...typeof match.title === "string" ? { title: match.title } : {}
|
|
988
|
-
},
|
|
989
|
-
turns
|
|
990
|
-
});
|
|
991
|
-
}
|
|
992
|
-
return { ok: true, threads };
|
|
993
|
-
} catch (error) {
|
|
994
|
-
return { ok: false, reason: String(error) };
|
|
995
|
-
}
|
|
996
|
-
}
|
|
997
886
|
var UnresolvedPlaceholder = class extends Error {
|
|
998
887
|
};
|
|
999
888
|
var splitPath = (path) => path.split(/\.(?![^\[]*\])/u);
|
|
@@ -1099,6 +988,127 @@ function stepResult(result) {
|
|
|
1099
988
|
return first.text;
|
|
1100
989
|
}
|
|
1101
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
|
+
}
|
|
1102
1112
|
function pickBatch(line, consumed, input) {
|
|
1103
1113
|
let earliest = true;
|
|
1104
1114
|
for (const [index, batch] of line.turns.entries()) {
|
|
@@ -1140,25 +1150,38 @@ var waitForTool = (step, input, clients) => poll(step.wait.timeoutMs, input, asy
|
|
|
1140
1150
|
(tool) => isRecord2(tool) && (tool.name === step.wait.tool || tool.name === `mcp__${step.wait.server ?? "ycodium"}__${step.wait.tool}`)
|
|
1141
1151
|
);
|
|
1142
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
|
+
}
|
|
1143
1166
|
async function waitForCount(step, args, input, clients) {
|
|
1144
1167
|
let ready;
|
|
1145
1168
|
const ok = await poll(step.waitFor.timeoutMs, input, async (signal) => {
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
"tools/call",
|
|
1149
|
-
{ name: step.waitFor.tool, arguments: args },
|
|
1150
|
-
signal
|
|
1151
|
-
);
|
|
1152
|
-
if (isRecord2(called) && called.isError === true) return false;
|
|
1153
|
-
const result = stepResult(called);
|
|
1154
|
-
const value = valueAt(result, splitPath(step.waitFor.path));
|
|
1155
|
-
const count = Array.isArray(value) ? value.length : typeof value === "number" ? value : value === void 0 || value === null ? 0 : 1;
|
|
1156
|
-
if (count < step.waitFor.atLeast) return false;
|
|
1157
|
-
ready = { result };
|
|
1158
|
-
return true;
|
|
1169
|
+
ready = await readCount(step, args, clients, signal);
|
|
1170
|
+
return ready !== void 0;
|
|
1159
1171
|
});
|
|
1160
1172
|
return ok ? ready : void 0;
|
|
1161
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
|
+
}
|
|
1162
1185
|
async function runCall(step, playback, input, clients) {
|
|
1163
1186
|
let args = step.arguments ?? {};
|
|
1164
1187
|
let failure;
|
|
@@ -1248,22 +1271,21 @@ function createPlaybookRunner(input) {
|
|
|
1248
1271
|
if (playback === void 0) {
|
|
1249
1272
|
const title = titles.get(turn.threadId);
|
|
1250
1273
|
const index = parsed.threads.findIndex(
|
|
1251
|
-
(
|
|
1274
|
+
(line2, index2) => !claimed.has(index2) && lineClaimsSession(line2, turn.threadId, title)
|
|
1252
1275
|
);
|
|
1253
1276
|
if (index >= 0) claimed.add(index);
|
|
1254
1277
|
playback = {
|
|
1255
1278
|
line: parsed.threads[index] ?? null,
|
|
1256
1279
|
consumed: /* @__PURE__ */ new Set(),
|
|
1280
|
+
owed: /* @__PURE__ */ new Set(),
|
|
1257
1281
|
results: /* @__PURE__ */ new Map(),
|
|
1258
1282
|
history: []
|
|
1259
1283
|
};
|
|
1260
1284
|
sessions.set(turn.threadId, playback);
|
|
1261
1285
|
}
|
|
1262
|
-
const
|
|
1263
|
-
const
|
|
1264
|
-
if (picked === void 0
|
|
1265
|
-
playback.consumed.add(picked);
|
|
1266
|
-
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;
|
|
1267
1289
|
let opened = clients.get(turn.threadId);
|
|
1268
1290
|
if (opened === void 0) {
|
|
1269
1291
|
opened = createPlaybookMcp({ fetch: turn.fetch, servers: turn.mcpServers });
|
|
@@ -1275,16 +1297,75 @@ function createPlaybookRunner(input) {
|
|
|
1275
1297
|
turn.emit(args[0], args[1], redactMcpUrls(args[2], turn.mcpServers), args[3]);
|
|
1276
1298
|
}
|
|
1277
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
|
+
};
|
|
1278
1317
|
const lines = [];
|
|
1279
|
-
const queue = [
|
|
1280
|
-
|
|
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()) {
|
|
1281
1362
|
if (turn.signal.aborted) break;
|
|
1282
1363
|
if ("expectInput" in step) continue;
|
|
1283
1364
|
if ("awaitSteer" in step) {
|
|
1284
1365
|
const ready = await turn.awaitSteer?.(step.awaitSteer.timeoutMs, turn.signal) ?? false;
|
|
1285
1366
|
if (turn.signal.aborted) break;
|
|
1286
1367
|
lines.push(`mcp-send awaitSteer${ready ? ": ready" : " failed: timeout"}`);
|
|
1287
|
-
const after = (playback.current ??
|
|
1368
|
+
const after = (playback.current ?? -1) + 1;
|
|
1288
1369
|
const following = playback.line?.turns[after];
|
|
1289
1370
|
if (!ready && step.awaitSteer.continueOnTimeout === true && following !== void 0 && !playback.consumed.has(after)) {
|
|
1290
1371
|
playback.consumed.add(after);
|
|
@@ -1301,9 +1382,12 @@ function createPlaybookRunner(input) {
|
|
|
1301
1382
|
if (turn.signal.aborted) break;
|
|
1302
1383
|
lines.push(`mcp-send wait ${step.wait.tool}${ready ? ": ready" : " failed: timeout"}`);
|
|
1303
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;
|
|
1304
1387
|
const missing = opened.missing(step.waitFor.server);
|
|
1305
1388
|
if (step.waitFor.server !== void 0 && missing !== void 0) {
|
|
1306
|
-
lines.push(`mcp-send ${
|
|
1389
|
+
lines.push(`mcp-send ${tool} failed: ${missing}`);
|
|
1390
|
+
if (required) haltRequired(step, missing);
|
|
1307
1391
|
continue;
|
|
1308
1392
|
}
|
|
1309
1393
|
let args;
|
|
@@ -1315,20 +1399,23 @@ function createPlaybookRunner(input) {
|
|
|
1315
1399
|
playback.history
|
|
1316
1400
|
);
|
|
1317
1401
|
} catch (error) {
|
|
1318
|
-
lines.push(`mcp-send ${
|
|
1402
|
+
lines.push(`mcp-send ${tool} failed: ${String(error)}`);
|
|
1403
|
+
if (required) haltRequired(step, String(error));
|
|
1319
1404
|
continue;
|
|
1320
1405
|
}
|
|
1321
1406
|
const ready = await waitForCount(step, args, safeTurn, opened);
|
|
1322
1407
|
if (turn.signal.aborted) break;
|
|
1323
|
-
const { id, tool, path: path2, atLeast } = step.waitFor;
|
|
1324
1408
|
if (ready !== void 0 && id !== void 0) playback.results.set(id, ready.result);
|
|
1325
1409
|
lines.push(
|
|
1326
1410
|
`mcp-send waitFor ${tool} ${path2} >= ${String(atLeast)}${ready ? ": ready" : " failed: timeout"}`
|
|
1327
1411
|
);
|
|
1412
|
+
if (ready === void 0 && required)
|
|
1413
|
+
haltRequired(step, `not reached within ${String(step.waitFor.timeoutMs)} ms`);
|
|
1328
1414
|
} else {
|
|
1329
1415
|
lines.push(await runCall(step, playback, safeTurn, opened));
|
|
1330
1416
|
}
|
|
1331
1417
|
}
|
|
1418
|
+
if (!ran) return null;
|
|
1332
1419
|
return redactMcpUrls(lines.join("\n"), turn.mcpServers);
|
|
1333
1420
|
}
|
|
1334
1421
|
};
|
|
@@ -1577,7 +1664,6 @@ var FAKE_MODELS = [
|
|
|
1577
1664
|
{ slug: "fake-large", name: "Fake Large" }
|
|
1578
1665
|
];
|
|
1579
1666
|
var createFakeInstance = (config, host) => {
|
|
1580
|
-
const events = new EventBus();
|
|
1581
1667
|
const threads = /* @__PURE__ */ new Map();
|
|
1582
1668
|
const script = parseScript(config);
|
|
1583
1669
|
const playbook = script === "mcp-send" ? createPlaybookRunner(config) : void 0;
|
|
@@ -1608,22 +1694,12 @@ var createFakeInstance = (config, host) => {
|
|
|
1608
1694
|
let disposed = false;
|
|
1609
1695
|
const nextId = (prefix) => `${prefix}-fake-${++seq}-${NodeCrypto.randomUUID()}`;
|
|
1610
1696
|
const now = () => (/* @__PURE__ */ new Date()).toISOString();
|
|
1611
|
-
const emit = (
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
createdAt: now(),
|
|
1618
|
-
type,
|
|
1619
|
-
payload,
|
|
1620
|
-
...extra?.turnId === void 0 ? {} : { turnId: extra.turnId },
|
|
1621
|
-
...extra?.itemId === void 0 ? {} : { itemId: extra.itemId },
|
|
1622
|
-
...extra?.requestId === void 0 ? {} : { requestId: extra.requestId },
|
|
1623
|
-
...extra?.originBindingGeneration === void 0 ? {} : { originBindingGeneration: extra.originBindingGeneration },
|
|
1624
|
-
...extra?.originResumeCursor === void 0 ? {} : { originResumeCursor: extra.originResumeCursor }
|
|
1625
|
-
});
|
|
1626
|
-
};
|
|
1697
|
+
const emit = createFakeEmitter({
|
|
1698
|
+
thread: (threadId) => threads.get(threadId),
|
|
1699
|
+
nextId: () => nextId("evt"),
|
|
1700
|
+
now,
|
|
1701
|
+
instanceId: host.instanceId
|
|
1702
|
+
});
|
|
1627
1703
|
const lifecycle = createFakeSessionLifecycle({
|
|
1628
1704
|
threads,
|
|
1629
1705
|
abort: (threadId) => inflight.get(threadId)?.abort(),
|
|
@@ -1631,19 +1707,17 @@ var createFakeInstance = (config, host) => {
|
|
|
1631
1707
|
// 代次与续接游标取被关闭的那份 session,不在发送时重查当前会话。
|
|
1632
1708
|
exited: (session) => {
|
|
1633
1709
|
if (stopWithoutExit) return;
|
|
1710
|
+
const context = threads.get(session.threadId);
|
|
1711
|
+
if (context) settleFakeTasks(context, session.threadId, emit);
|
|
1634
1712
|
emit(
|
|
1635
1713
|
"session.exited",
|
|
1636
1714
|
session.threadId,
|
|
1637
1715
|
{ reason: "stopped" },
|
|
1638
|
-
{
|
|
1639
|
-
...session.bindingGeneration === void 0 ? {} : { originBindingGeneration: session.bindingGeneration },
|
|
1640
|
-
...session.resumeCursor === void 0 ? {} : { originResumeCursor: session.resumeCursor }
|
|
1641
|
-
}
|
|
1716
|
+
session.resumeCursor === void 0 ? {} : { originResumeCursor: session.resumeCursor }
|
|
1642
1717
|
);
|
|
1643
1718
|
},
|
|
1644
1719
|
forget: (threadId) => steeredInputs.delete(threadId),
|
|
1645
|
-
disposePlaybook: () => playbook?.dispose()
|
|
1646
|
-
closeEvents: () => events.close()
|
|
1720
|
+
disposePlaybook: () => playbook?.dispose()
|
|
1647
1721
|
});
|
|
1648
1722
|
const requireThread = (threadId) => {
|
|
1649
1723
|
const booked = threads.get(threadId);
|
|
@@ -1866,6 +1940,8 @@ ${more ?? "playbook: steered input not handled"}`;
|
|
|
1866
1940
|
if (signal?.aborted) throw new Error("aborted");
|
|
1867
1941
|
if (threads.get(input.threadId)?.closing !== void 0) throw new Error("session closing");
|
|
1868
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);
|
|
1869
1945
|
const cwd = input.cwd ?? host.workspacePath(input.threadId);
|
|
1870
1946
|
if (cwd.trim() === "") throw new Error("workspace path missing");
|
|
1871
1947
|
const createdAt = now();
|
|
@@ -1881,7 +1957,13 @@ ${more ?? "playbook: steered input not handled"}`;
|
|
|
1881
1957
|
...input.discardResumeCursor === true || input.resumeCursor === void 0 ? {} : { resumeCursor: input.resumeCursor },
|
|
1882
1958
|
...input.bindingGeneration === void 0 ? {} : { bindingGeneration: input.bindingGeneration }
|
|
1883
1959
|
};
|
|
1884
|
-
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
|
+
});
|
|
1885
1967
|
playbook?.startSession(input);
|
|
1886
1968
|
emit(
|
|
1887
1969
|
"session.started",
|
|
@@ -1971,7 +2053,6 @@ ${more ?? "playbook: steered input not handled"}`;
|
|
|
1971
2053
|
const booked = requireThread(threadId);
|
|
1972
2054
|
return { threadId, turns: booked.turns };
|
|
1973
2055
|
},
|
|
1974
|
-
events,
|
|
1975
2056
|
usage: createFakeUsage({ instanceId: host.instanceId, script: parseUsageScript(config) }),
|
|
1976
2057
|
dispose: async () => {
|
|
1977
2058
|
disposed = true;
|
|
@@ -1994,7 +2075,7 @@ var executors = [
|
|
|
1994
2075
|
];
|
|
1995
2076
|
var index_default = definePlugin({
|
|
1996
2077
|
id: "agent-fake",
|
|
1997
|
-
apiVersion:
|
|
2078
|
+
apiVersion: PLUGIN_API_VERSION,
|
|
1998
2079
|
executors,
|
|
1999
2080
|
activate(ctx) {
|
|
2000
2081
|
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.3982",
|
|
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.3982",
|
|
25
25
|
"ycodium-executor-testing": "0.0.0"
|
|
26
26
|
},
|
|
27
27
|
"ycodium": {
|