@theokit/sdk 2.28.0 → 2.30.0
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/CHANGELOG.md +34 -0
- package/dist/a2a/index.cjs +373 -17
- package/dist/a2a/index.cjs.map +1 -1
- package/dist/a2a/index.js +373 -17
- package/dist/a2a/index.js.map +1 -1
- package/dist/{cron-BR1NCSk1.d.cts → cron-BNHJywtl.d.ts} +482 -18
- package/dist/{cron-DgEQCJ2i.d.ts → cron-t4oKI2Is.d.cts} +482 -18
- package/dist/cron.cjs +1143 -692
- package/dist/cron.cjs.map +1 -1
- package/dist/cron.d.cts +2 -2
- package/dist/cron.d.ts +2 -2
- package/dist/cron.js +1146 -695
- package/dist/cron.js.map +1 -1
- package/dist/{errors-DLMNb4Ka.d.cts → errors-DZpCGlYv.d.cts} +1 -1
- package/dist/{errors-CbY3pxY7.d.ts → errors-D_Bfo30u.d.ts} +1 -1
- package/dist/errors.d.cts +2 -2
- package/dist/eval.cjs +914 -508
- package/dist/eval.cjs.map +1 -1
- package/dist/eval.js +915 -509
- package/dist/eval.js.map +1 -1
- package/dist/index.cjs +1038 -590
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +13 -135
- package/dist/index.d.ts +13 -135
- package/dist/index.js +1037 -588
- package/dist/index.js.map +1 -1
- package/dist/internal/persistence/conversation-storage-fs.d.cts +4 -0
- package/dist/internal/persistence/conversation-storage-fs.d.ts +4 -0
- package/dist/internal/persistence/conversation-storage-memory.d.cts +4 -0
- package/dist/internal/persistence/conversation-storage-memory.d.ts +4 -0
- package/dist/internal/persistence/objective-coerce.d.cts +9 -0
- package/dist/internal/persistence/objective-coerce.d.ts +9 -0
- package/dist/internal/runtime/lifecycle/wrap-completion-check-run.d.ts +30 -0
- package/dist/internal/runtime/local-agent/local-agent-goal-extensions.d.ts +80 -0
- package/dist/internal/runtime/objective/objective-store.d.ts +33 -0
- package/dist/{run-CdWiihyU.d.cts → run-CLXKMRgq.d.cts} +71 -2
- package/dist/{run-CdWiihyU.d.ts → run-CLXKMRgq.d.ts} +71 -2
- package/dist/types/agent.d.ts +32 -1
- package/dist/types/conversation-storage.d.ts +23 -0
- package/dist/types/cron.d.ts +30 -13
- package/dist/types/goal-events.d.ts +7 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/objective.d.ts +45 -0
- package/dist/types/run-events.d.ts +12 -1
- package/dist/types/run.d.ts +58 -0
- package/package.json +3 -3
package/dist/cron.cjs
CHANGED
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
var zod = require('zod');
|
|
4
4
|
var module$1 = require('module');
|
|
5
5
|
var crypto = require('crypto');
|
|
6
|
-
var promises = require('fs/promises');
|
|
7
|
-
var path = require('path');
|
|
8
6
|
var fs = require('fs');
|
|
7
|
+
var path = require('path');
|
|
8
|
+
var promises = require('fs/promises');
|
|
9
9
|
var async_hooks = require('async_hooks');
|
|
10
10
|
var os = require('os');
|
|
11
11
|
var child_process = require('child_process');
|
|
@@ -783,6 +783,155 @@ var init_cwd_mutex = __esm({
|
|
|
783
783
|
tails = /* @__PURE__ */ new Map();
|
|
784
784
|
}
|
|
785
785
|
});
|
|
786
|
+
function safePathJoin(base, ...parts) {
|
|
787
|
+
if (base === "") {
|
|
788
|
+
throw new Error("safePathJoin: base must be non-empty");
|
|
789
|
+
}
|
|
790
|
+
rejectNulAndControlChars(base, "base");
|
|
791
|
+
for (const part of parts) {
|
|
792
|
+
rejectNulAndControlChars(part, "path segment");
|
|
793
|
+
}
|
|
794
|
+
const baseResolved = path.resolve(base);
|
|
795
|
+
const target = path.resolve(base, ...parts);
|
|
796
|
+
if (target !== baseResolved && !target.startsWith(baseResolved + path.sep)) {
|
|
797
|
+
throw new PathTraversalError(parts.join("/"), target);
|
|
798
|
+
}
|
|
799
|
+
return target;
|
|
800
|
+
}
|
|
801
|
+
function rejectNulAndControlChars(input, role) {
|
|
802
|
+
for (let i = 0; i < input.length; i++) {
|
|
803
|
+
const code = input.charCodeAt(i);
|
|
804
|
+
if (code === 0 || code >= 1 && code <= 31 || code === 127) {
|
|
805
|
+
const label = code === 0 ? "<nul-byte>" : `<control-char-0x${code.toString(16)}>`;
|
|
806
|
+
throw new PathTraversalError(`${role}: ${input}`, label);
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
function assertNoSymlinkEscape(path$1, base) {
|
|
811
|
+
rejectNulAndControlChars(path$1, "path");
|
|
812
|
+
rejectNulAndControlChars(base, "base");
|
|
813
|
+
let baseResolved;
|
|
814
|
+
try {
|
|
815
|
+
baseResolved = fs.realpathSync(base);
|
|
816
|
+
} catch {
|
|
817
|
+
baseResolved = path.resolve(base);
|
|
818
|
+
}
|
|
819
|
+
const resolved = realpathOfDeepestExisting(path$1);
|
|
820
|
+
if (resolved === void 0) return;
|
|
821
|
+
if (resolved !== baseResolved && !resolved.startsWith(baseResolved + path.sep)) {
|
|
822
|
+
throw new PathTraversalError(`symlink ${path$1}`, resolved);
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
function realpathOfDeepestExisting(path$1) {
|
|
826
|
+
try {
|
|
827
|
+
return fs.realpathSync(path$1);
|
|
828
|
+
} catch {
|
|
829
|
+
}
|
|
830
|
+
try {
|
|
831
|
+
const stat6 = fs.lstatSync(path$1);
|
|
832
|
+
if (stat6.isSymbolicLink()) {
|
|
833
|
+
const target = fs.readlinkSync(path$1);
|
|
834
|
+
const parentReal = realpathOfDeepestExisting(path.dirname(path$1));
|
|
835
|
+
const parentBase = parentReal ?? path.dirname(path$1);
|
|
836
|
+
return path.resolve(parentBase, target);
|
|
837
|
+
}
|
|
838
|
+
} catch {
|
|
839
|
+
}
|
|
840
|
+
let cursor = path.dirname(path$1);
|
|
841
|
+
let suffix = path$1.slice(cursor.length);
|
|
842
|
+
while (cursor !== path.dirname(cursor)) {
|
|
843
|
+
try {
|
|
844
|
+
const real = fs.realpathSync(cursor);
|
|
845
|
+
return path.resolve(real, `.${suffix}`);
|
|
846
|
+
} catch {
|
|
847
|
+
suffix = path$1.slice(path.dirname(cursor).length);
|
|
848
|
+
cursor = path.dirname(cursor);
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
return void 0;
|
|
852
|
+
}
|
|
853
|
+
function validateArtifactPath(input) {
|
|
854
|
+
rejectKnownPrefixVectors(input);
|
|
855
|
+
const normalized = decodeAndNormalize(input);
|
|
856
|
+
rejectParentTraversal(input, normalized);
|
|
857
|
+
}
|
|
858
|
+
function rejectKnownPrefixVectors(input) {
|
|
859
|
+
if (input.includes("\0")) {
|
|
860
|
+
throw new PathTraversalError(input, "<nul-byte>");
|
|
861
|
+
}
|
|
862
|
+
if (input.startsWith("/") || input.startsWith("~")) {
|
|
863
|
+
throw new PathTraversalError(input, input);
|
|
864
|
+
}
|
|
865
|
+
if (/^[A-Za-z]:[\\/]?/.test(input)) {
|
|
866
|
+
throw new PathTraversalError(input, input);
|
|
867
|
+
}
|
|
868
|
+
}
|
|
869
|
+
function decodeAndNormalize(input) {
|
|
870
|
+
let decoded = input;
|
|
871
|
+
for (let i = 0; i < 2; i += 1) {
|
|
872
|
+
try {
|
|
873
|
+
const next = decodeURIComponent(decoded);
|
|
874
|
+
if (next === decoded) break;
|
|
875
|
+
decoded = next;
|
|
876
|
+
} catch {
|
|
877
|
+
throw new PathTraversalError(input, "<malformed-url-encoding>");
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
return decoded.replace(/\\/g, "/");
|
|
881
|
+
}
|
|
882
|
+
function rejectParentTraversal(input, normalized) {
|
|
883
|
+
for (const segment of normalized.split("/")) {
|
|
884
|
+
if (segment === ".." || segment === "..%00") {
|
|
885
|
+
throw new PathTraversalError(input, normalized);
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
if (normalized.includes("..")) {
|
|
889
|
+
throw new PathTraversalError(input, normalized);
|
|
890
|
+
}
|
|
891
|
+
}
|
|
892
|
+
function sanitizeIdentifier(input, options) {
|
|
893
|
+
const maxLen = options?.maxLen ?? 64;
|
|
894
|
+
if (input.length === 0 || input.length > maxLen) {
|
|
895
|
+
throw new ConfigurationError(`Identifier length out of range (1-${maxLen}): "${input}"`, {
|
|
896
|
+
code: "invalid_identifier"
|
|
897
|
+
});
|
|
898
|
+
}
|
|
899
|
+
rejectNulAndControlChars(input, "identifier");
|
|
900
|
+
if (!IDENTIFIER_PATTERN.test(input)) {
|
|
901
|
+
throw new ConfigurationError(`Identifier contains invalid characters: "${input}"`, {
|
|
902
|
+
code: "invalid_identifier"
|
|
903
|
+
});
|
|
904
|
+
}
|
|
905
|
+
return input.toLowerCase();
|
|
906
|
+
}
|
|
907
|
+
function safeFilenameForId(id, options) {
|
|
908
|
+
if (id.length === 0) {
|
|
909
|
+
throw new ConfigurationError("Filename id must be a non-empty string", {
|
|
910
|
+
code: "invalid_filename_id"
|
|
911
|
+
});
|
|
912
|
+
}
|
|
913
|
+
const maxLen = options?.maxLen ?? 128;
|
|
914
|
+
const lower = id.toLowerCase();
|
|
915
|
+
if (lower.length <= maxLen && IDENTIFIER_PATTERN.test(lower)) {
|
|
916
|
+
return lower;
|
|
917
|
+
}
|
|
918
|
+
return `h-${crypto.createHash("sha256").update(id).digest("hex").slice(0, 16)}`;
|
|
919
|
+
}
|
|
920
|
+
var PathTraversalError, IDENTIFIER_PATTERN;
|
|
921
|
+
var init_path_guard = __esm({
|
|
922
|
+
"src/internal/security/path-guard.ts"() {
|
|
923
|
+
init_errors();
|
|
924
|
+
PathTraversalError = class extends ConfigurationError {
|
|
925
|
+
name = "PathTraversalError";
|
|
926
|
+
constructor(input, resolvedPath) {
|
|
927
|
+
super(`Path traversal attempt: ${input} \u2192 ${resolvedPath}`, {
|
|
928
|
+
code: "path_traversal"
|
|
929
|
+
});
|
|
930
|
+
}
|
|
931
|
+
};
|
|
932
|
+
IDENTIFIER_PATTERN = /^[a-z0-9][a-z0-9\-_]*$/i;
|
|
933
|
+
}
|
|
934
|
+
});
|
|
786
935
|
function detectNetworkFsName(typeMagic) {
|
|
787
936
|
return NETWORK_FS_MAGIC.get(typeMagic) ?? null;
|
|
788
937
|
}
|
|
@@ -847,6 +996,80 @@ var init_atomic_write = __esm({
|
|
|
847
996
|
}
|
|
848
997
|
});
|
|
849
998
|
|
|
999
|
+
// src/internal/security/index.ts
|
|
1000
|
+
var init_security = __esm({
|
|
1001
|
+
"src/internal/security/index.ts"() {
|
|
1002
|
+
init_path_guard();
|
|
1003
|
+
init_redact();
|
|
1004
|
+
}
|
|
1005
|
+
});
|
|
1006
|
+
|
|
1007
|
+
// src/internal/persistence/file-lock.ts
|
|
1008
|
+
async function getProperLockfile() {
|
|
1009
|
+
if (cached !== void 0) return cached;
|
|
1010
|
+
try {
|
|
1011
|
+
const mod = await import('proper-lockfile');
|
|
1012
|
+
if (!validateLockModule(mod)) {
|
|
1013
|
+
if (!warnedStructural) {
|
|
1014
|
+
warnedStructural = true;
|
|
1015
|
+
process.stderr.write(
|
|
1016
|
+
"[theokit-sdk] proper-lockfile: imported module does NOT expose the expected `lock`/`unlock` API surface. This may indicate a supply-chain compromise or an incompatible major version. Falling back to in-process mutex (no cross-process safety). Reinstall with: pnpm add proper-lockfile@^11\n"
|
|
1017
|
+
);
|
|
1018
|
+
}
|
|
1019
|
+
cached = null;
|
|
1020
|
+
return cached;
|
|
1021
|
+
}
|
|
1022
|
+
cached = mod;
|
|
1023
|
+
} catch {
|
|
1024
|
+
cached = null;
|
|
1025
|
+
}
|
|
1026
|
+
return cached;
|
|
1027
|
+
}
|
|
1028
|
+
function validateLockModule(mod) {
|
|
1029
|
+
if (mod === null || mod === void 0 || typeof mod !== "object") return false;
|
|
1030
|
+
const m = mod;
|
|
1031
|
+
return typeof m.lock === "function" && typeof m.unlock === "function";
|
|
1032
|
+
}
|
|
1033
|
+
async function withFileLock(path, fn, options) {
|
|
1034
|
+
const lib = await getProperLockfile();
|
|
1035
|
+
if (lib === null) {
|
|
1036
|
+
if (!warnedMissing) {
|
|
1037
|
+
warnedMissing = true;
|
|
1038
|
+
process.stderr.write(
|
|
1039
|
+
"[theokit-sdk] proper-lockfile not installed; cross-process file lock unavailable. Install with: pnpm add proper-lockfile\n"
|
|
1040
|
+
);
|
|
1041
|
+
}
|
|
1042
|
+
return withCwdMutex(`file-lock:${path}`, fn);
|
|
1043
|
+
}
|
|
1044
|
+
return withCwdMutex(`file-lock:${path}`, async () => {
|
|
1045
|
+
const release = await lib.lock(path, {
|
|
1046
|
+
// EC-1: companion lockfile, target path may not exist yet.
|
|
1047
|
+
lockfilePath: `${path}.lock`,
|
|
1048
|
+
realpath: false,
|
|
1049
|
+
stale: 3e4,
|
|
1050
|
+
retries: {
|
|
1051
|
+
retries: 5,
|
|
1052
|
+
factor: 1.5,
|
|
1053
|
+
minTimeout: 100,
|
|
1054
|
+
maxTimeout: 5e3
|
|
1055
|
+
}
|
|
1056
|
+
});
|
|
1057
|
+
try {
|
|
1058
|
+
return await fn();
|
|
1059
|
+
} finally {
|
|
1060
|
+
await release();
|
|
1061
|
+
}
|
|
1062
|
+
});
|
|
1063
|
+
}
|
|
1064
|
+
var cached, warnedMissing, warnedStructural;
|
|
1065
|
+
var init_file_lock = __esm({
|
|
1066
|
+
"src/internal/persistence/file-lock.ts"() {
|
|
1067
|
+
init_cwd_mutex();
|
|
1068
|
+
warnedMissing = false;
|
|
1069
|
+
warnedStructural = false;
|
|
1070
|
+
}
|
|
1071
|
+
});
|
|
1072
|
+
|
|
850
1073
|
// src/internal/runtime/context/yaml-frontmatter.ts
|
|
851
1074
|
function parseSimpleYaml(text) {
|
|
852
1075
|
const fields = {};
|
|
@@ -1055,37 +1278,402 @@ var init_hooks_source = __esm({
|
|
|
1055
1278
|
warned = /* @__PURE__ */ new Set();
|
|
1056
1279
|
}
|
|
1057
1280
|
});
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
function currentToolWhitelist() {
|
|
1062
|
-
return toolWhitelistStore.getStore();
|
|
1281
|
+
function sessionFilePath(cwd, agentId) {
|
|
1282
|
+
const safe2 = sanitizeIdentifier(agentId, { maxLen: 128 });
|
|
1283
|
+
return safePathJoin(cwd, ".theokit", "agents", safe2, "messages.jsonl");
|
|
1063
1284
|
}
|
|
1064
|
-
function
|
|
1065
|
-
const
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
return
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
};
|
|
1285
|
+
async function readJsonlLines(cwd, agentId) {
|
|
1286
|
+
const path = sessionFilePath(cwd, agentId);
|
|
1287
|
+
try {
|
|
1288
|
+
const raw = await promises.readFile(path, "utf8");
|
|
1289
|
+
return raw.split("\n").filter((line) => line.length > 0);
|
|
1290
|
+
} catch {
|
|
1291
|
+
return [];
|
|
1072
1292
|
}
|
|
1073
|
-
return { allowed: true };
|
|
1074
1293
|
}
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1294
|
+
function warnMalformed(agentId, line) {
|
|
1295
|
+
process.stderr.write(
|
|
1296
|
+
`[theokit-sdk] skipping malformed line in messages.jsonl (${agentId}): ${line.slice(0, 80)}...
|
|
1297
|
+
`
|
|
1298
|
+
);
|
|
1299
|
+
}
|
|
1300
|
+
function hydrateSessionLine(parsed) {
|
|
1301
|
+
if (typeof parsed.text !== "string" || parsed.role === void 0) return void 0;
|
|
1302
|
+
if (parsed.role === "user" || parsed.role === "assistant") {
|
|
1303
|
+
return { role: parsed.role, text: parsed.text };
|
|
1079
1304
|
}
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1305
|
+
if (parsed.role === "tool_call" || parsed.role === "tool_result") {
|
|
1306
|
+
const label = parsed.role === "tool_call" ? "tool call" : "tool result";
|
|
1307
|
+
return { role: "assistant", text: `[${label}] ${parsed.text}` };
|
|
1308
|
+
}
|
|
1309
|
+
return void 0;
|
|
1310
|
+
}
|
|
1311
|
+
async function readSessionFile(cwd, agentId) {
|
|
1312
|
+
const lines = await readJsonlLines(cwd, agentId);
|
|
1313
|
+
const messages = [];
|
|
1314
|
+
for (const line of lines) {
|
|
1315
|
+
try {
|
|
1316
|
+
const msg = hydrateSessionLine(JSON.parse(line));
|
|
1317
|
+
if (msg !== void 0) messages.push(msg);
|
|
1318
|
+
} catch {
|
|
1319
|
+
warnMalformed(agentId, line);
|
|
1320
|
+
}
|
|
1321
|
+
}
|
|
1322
|
+
return messages;
|
|
1323
|
+
}
|
|
1324
|
+
async function readAllPersistedMessages(cwd, agentId) {
|
|
1325
|
+
const lines = await readJsonlLines(cwd, agentId);
|
|
1326
|
+
const messages = [];
|
|
1327
|
+
for (const line of lines) {
|
|
1328
|
+
try {
|
|
1329
|
+
const parsed = JSON.parse(line);
|
|
1330
|
+
if (parsed.role !== void 0 && VALID_ROLES.has(parsed.role) && typeof parsed.text === "string") {
|
|
1331
|
+
messages.push({
|
|
1332
|
+
role: parsed.role,
|
|
1333
|
+
text: parsed.text,
|
|
1334
|
+
at: typeof parsed.at === "number" ? parsed.at : Date.now()
|
|
1335
|
+
});
|
|
1336
|
+
}
|
|
1337
|
+
} catch {
|
|
1338
|
+
warnMalformed(agentId, line);
|
|
1339
|
+
}
|
|
1340
|
+
}
|
|
1341
|
+
return messages;
|
|
1342
|
+
}
|
|
1343
|
+
async function appendAnyPersistedMessage(cwd, agentId, record) {
|
|
1344
|
+
await appendPersistedMessages(cwd, agentId, [record]);
|
|
1345
|
+
}
|
|
1346
|
+
async function appendPersistedMessages(cwd, agentId, records) {
|
|
1347
|
+
if (records.length === 0) return;
|
|
1348
|
+
const path$1 = sessionFilePath(cwd, agentId);
|
|
1349
|
+
const payload = records.map((r) => `${redactSecrets(JSON.stringify(r))}
|
|
1350
|
+
`).join("");
|
|
1351
|
+
const dir = path.dirname(path$1);
|
|
1352
|
+
let written = false;
|
|
1353
|
+
const attempt = async () => {
|
|
1354
|
+
await promises.mkdir(dir, { recursive: true });
|
|
1355
|
+
await withFileLock(path$1, async () => {
|
|
1356
|
+
await promises.appendFile(path$1, payload, "utf8");
|
|
1357
|
+
written = true;
|
|
1358
|
+
});
|
|
1359
|
+
};
|
|
1360
|
+
try {
|
|
1361
|
+
await attempt();
|
|
1362
|
+
} catch (cause) {
|
|
1363
|
+
if (written || cause.code !== "ENOENT") throw cause;
|
|
1364
|
+
await attempt();
|
|
1365
|
+
}
|
|
1366
|
+
}
|
|
1367
|
+
async function rewriteLockedSession(path, transform) {
|
|
1368
|
+
await withFileLock(path, async () => {
|
|
1369
|
+
let raw;
|
|
1370
|
+
try {
|
|
1371
|
+
raw = await promises.readFile(path, "utf8");
|
|
1372
|
+
} catch {
|
|
1373
|
+
return;
|
|
1374
|
+
}
|
|
1375
|
+
const lines = raw.split("\n").filter((line) => line.length > 0);
|
|
1376
|
+
const next = transform(lines);
|
|
1377
|
+
if (next === void 0) return;
|
|
1378
|
+
await replaceFileAtomic(path, next);
|
|
1379
|
+
});
|
|
1380
|
+
}
|
|
1381
|
+
async function compactSessionFile(cwd, agentId, maxTurns) {
|
|
1382
|
+
const path = sessionFilePath(cwd, agentId);
|
|
1383
|
+
if (!fs.existsSync(path)) return;
|
|
1384
|
+
await rewriteLockedSession(
|
|
1385
|
+
path,
|
|
1386
|
+
(lines) => lines.length <= maxTurns * 2 ? void 0 : `${lines.slice(-maxTurns).join("\n")}
|
|
1387
|
+
`
|
|
1388
|
+
);
|
|
1389
|
+
}
|
|
1390
|
+
async function truncateSessionTo(cwd, agentId, keepCount) {
|
|
1391
|
+
const path = sessionFilePath(cwd, agentId);
|
|
1392
|
+
if (!fs.existsSync(path)) return 0;
|
|
1393
|
+
let kept = 0;
|
|
1394
|
+
await rewriteLockedSession(path, (lines) => {
|
|
1395
|
+
const keep = Math.max(0, Math.min(keepCount, lines.length));
|
|
1396
|
+
kept = keep;
|
|
1397
|
+
if (keep === lines.length) return void 0;
|
|
1398
|
+
return keep === 0 ? "" : `${lines.slice(0, keep).join("\n")}
|
|
1399
|
+
`;
|
|
1400
|
+
});
|
|
1401
|
+
return kept;
|
|
1402
|
+
}
|
|
1403
|
+
var VALID_ROLES;
|
|
1404
|
+
var init_agent_session_store = __esm({
|
|
1405
|
+
"src/internal/runtime/session/agent-session-store.ts"() {
|
|
1406
|
+
init_atomic_write();
|
|
1407
|
+
init_file_lock();
|
|
1408
|
+
init_security();
|
|
1409
|
+
VALID_ROLES = /* @__PURE__ */ new Set([
|
|
1410
|
+
"user",
|
|
1411
|
+
"assistant",
|
|
1412
|
+
"system",
|
|
1413
|
+
"tool_call",
|
|
1414
|
+
"tool_result"
|
|
1415
|
+
]);
|
|
1416
|
+
}
|
|
1417
|
+
});
|
|
1418
|
+
|
|
1419
|
+
// src/internal/persistence/objective-coerce.ts
|
|
1420
|
+
function coerceOptions(raw) {
|
|
1421
|
+
if (typeof raw !== "object" || raw === null) return void 0;
|
|
1422
|
+
const o = raw;
|
|
1423
|
+
const out = {};
|
|
1424
|
+
if (typeof o.maxRuns === "number") out.maxRuns = o.maxRuns;
|
|
1425
|
+
if (typeof o.judgeModel === "string") out.judgeModel = o.judgeModel;
|
|
1426
|
+
if (typeof o.prompt === "string") out.prompt = o.prompt;
|
|
1427
|
+
return Object.keys(out).length > 0 ? out : void 0;
|
|
1428
|
+
}
|
|
1429
|
+
function coerceObjectiveRecord(raw) {
|
|
1430
|
+
if (typeof raw !== "object" || raw === null) return void 0;
|
|
1431
|
+
const o = raw;
|
|
1432
|
+
if (o._schemaVersion !== 1) return void 0;
|
|
1433
|
+
if (typeof o.objective !== "string") return void 0;
|
|
1434
|
+
if (typeof o.runsUsed !== "number") return void 0;
|
|
1435
|
+
if (typeof o.status !== "string" || !STATUSES.includes(o.status))
|
|
1436
|
+
return void 0;
|
|
1437
|
+
const options = coerceOptions(o.options);
|
|
1438
|
+
return {
|
|
1439
|
+
_schemaVersion: 1,
|
|
1440
|
+
objective: o.objective,
|
|
1441
|
+
...options !== void 0 ? { options } : {},
|
|
1442
|
+
status: o.status,
|
|
1443
|
+
runsUsed: o.runsUsed
|
|
1444
|
+
};
|
|
1445
|
+
}
|
|
1446
|
+
var STATUSES;
|
|
1447
|
+
var init_objective_coerce = __esm({
|
|
1448
|
+
"src/internal/persistence/objective-coerce.ts"() {
|
|
1449
|
+
STATUSES = ["active", "done", "paused"];
|
|
1450
|
+
}
|
|
1451
|
+
});
|
|
1452
|
+
|
|
1453
|
+
// src/internal/persistence/pagination.ts
|
|
1454
|
+
function paginate(items, opts) {
|
|
1455
|
+
if (opts === void 0 || opts.offset === void 0 && opts.limit === void 0) return items;
|
|
1456
|
+
const start = Math.max(0, opts.offset ?? 0);
|
|
1457
|
+
const end = opts.limit === void 0 ? items.length : start + Math.max(0, opts.limit);
|
|
1458
|
+
return items.slice(start, end);
|
|
1459
|
+
}
|
|
1460
|
+
var init_pagination = __esm({
|
|
1461
|
+
"src/internal/persistence/pagination.ts"() {
|
|
1462
|
+
}
|
|
1463
|
+
});
|
|
1464
|
+
|
|
1465
|
+
// src/internal/persistence/session-meta.ts
|
|
1466
|
+
function applyMetaPatch(current, patch) {
|
|
1467
|
+
const next = {};
|
|
1468
|
+
if (current.title !== void 0) next.title = current.title;
|
|
1469
|
+
if (current.tag !== void 0) next.tag = current.tag;
|
|
1470
|
+
if (patch.title === null) delete next.title;
|
|
1471
|
+
else if (patch.title !== void 0) next.title = patch.title;
|
|
1472
|
+
if (patch.tag === null) delete next.tag;
|
|
1473
|
+
else if (patch.tag !== void 0) next.tag = patch.tag;
|
|
1474
|
+
return next;
|
|
1475
|
+
}
|
|
1476
|
+
function coerceSessionMeta(raw) {
|
|
1477
|
+
if (typeof raw !== "object" || raw === null) return void 0;
|
|
1478
|
+
const obj = raw;
|
|
1479
|
+
const meta = {};
|
|
1480
|
+
if (typeof obj.title === "string") meta.title = obj.title;
|
|
1481
|
+
if (typeof obj.tag === "string") meta.tag = obj.tag;
|
|
1482
|
+
return meta.title === void 0 && meta.tag === void 0 ? void 0 : meta;
|
|
1483
|
+
}
|
|
1484
|
+
var init_session_meta = __esm({
|
|
1485
|
+
"src/internal/persistence/session-meta.ts"() {
|
|
1486
|
+
}
|
|
1487
|
+
});
|
|
1488
|
+
function toStoredMessage(record) {
|
|
1489
|
+
return {
|
|
1490
|
+
role: record.role,
|
|
1491
|
+
content: record.text,
|
|
1492
|
+
at: record.at
|
|
1493
|
+
};
|
|
1494
|
+
}
|
|
1495
|
+
function toRecord(message) {
|
|
1496
|
+
return { role: message.role, text: message.content, at: message.at ?? Date.now() };
|
|
1497
|
+
}
|
|
1498
|
+
var FileSystemConversationStorage;
|
|
1499
|
+
var init_conversation_storage_fs = __esm({
|
|
1500
|
+
"src/internal/persistence/conversation-storage-fs.ts"() {
|
|
1501
|
+
init_agent_session_store();
|
|
1502
|
+
init_security();
|
|
1503
|
+
init_path_guard();
|
|
1504
|
+
init_file_lock();
|
|
1505
|
+
init_objective_coerce();
|
|
1506
|
+
init_pagination();
|
|
1507
|
+
init_session_meta();
|
|
1508
|
+
FileSystemConversationStorage = class {
|
|
1509
|
+
#root;
|
|
1510
|
+
constructor(opts = {}) {
|
|
1511
|
+
this.#root = opts.root ?? process.cwd();
|
|
1512
|
+
}
|
|
1513
|
+
/** Exposed for tests + diagnostics. The path is sanitized at use sites. */
|
|
1514
|
+
get root() {
|
|
1515
|
+
return this.#root;
|
|
1516
|
+
}
|
|
1517
|
+
async getMessages(conversationId, opts) {
|
|
1518
|
+
const records = await readAllPersistedMessages(this.#root, conversationId);
|
|
1519
|
+
const all = records.map(toStoredMessage);
|
|
1520
|
+
return paginate(all, opts);
|
|
1521
|
+
}
|
|
1522
|
+
async appendMessage(conversationId, message) {
|
|
1523
|
+
await appendAnyPersistedMessage(this.#root, conversationId, toRecord(message));
|
|
1524
|
+
}
|
|
1525
|
+
async appendMessages(conversationId, messages) {
|
|
1526
|
+
await appendPersistedMessages(this.#root, conversationId, messages.map(toRecord));
|
|
1527
|
+
}
|
|
1528
|
+
async truncateConversation(conversationId, keepCount) {
|
|
1529
|
+
return truncateSessionTo(this.#root, conversationId, keepCount);
|
|
1530
|
+
}
|
|
1531
|
+
async deleteConversation(conversationId) {
|
|
1532
|
+
const safe2 = sanitizeIdentifier(conversationId, { maxLen: 128 });
|
|
1533
|
+
const dirPath = safePathJoin(this.#root, ".theokit", "agents", safe2);
|
|
1534
|
+
await promises.rm(dirPath, { recursive: true, force: true });
|
|
1535
|
+
}
|
|
1536
|
+
async deleteScope(prefix) {
|
|
1537
|
+
const ids = await this.listConversationIds();
|
|
1538
|
+
const matching = ids.filter((id) => id.startsWith(prefix));
|
|
1539
|
+
for (const id of matching) await this.deleteConversation(id);
|
|
1540
|
+
return matching.length;
|
|
1541
|
+
}
|
|
1542
|
+
async listConversationIds(opts = {}) {
|
|
1543
|
+
const agentsRoot = safePathJoin(this.#root, ".theokit", "agents");
|
|
1544
|
+
let entries;
|
|
1545
|
+
try {
|
|
1546
|
+
entries = await promises.readdir(agentsRoot);
|
|
1547
|
+
} catch (cause) {
|
|
1548
|
+
if (cause.code === "ENOENT") return [];
|
|
1549
|
+
throw cause;
|
|
1550
|
+
}
|
|
1551
|
+
if (opts.limit !== void 0) return entries.slice(0, opts.limit);
|
|
1552
|
+
return entries;
|
|
1553
|
+
}
|
|
1554
|
+
async compact(conversationId, maxTurns) {
|
|
1555
|
+
await compactSessionFile(this.#root, conversationId, maxTurns);
|
|
1556
|
+
}
|
|
1557
|
+
// SE4 — session metadata persisted as a per-conversation sidecar
|
|
1558
|
+
// `<root>/.theokit/agents/<safeId>/session.json` (same sanitized perimeter as
|
|
1559
|
+
// the transcript). Kept separate from messages.jsonl so a title/tag write does
|
|
1560
|
+
// not rewrite the append-only log.
|
|
1561
|
+
#metaPath(conversationId) {
|
|
1562
|
+
const safe2 = sanitizeIdentifier(conversationId, { maxLen: 128 });
|
|
1563
|
+
return safePathJoin(this.#root, ".theokit", "agents", safe2, "session.json");
|
|
1564
|
+
}
|
|
1565
|
+
async getSessionMeta(conversationId) {
|
|
1566
|
+
try {
|
|
1567
|
+
const raw = await promises.readFile(this.#metaPath(conversationId), "utf8");
|
|
1568
|
+
return coerceSessionMeta(JSON.parse(raw));
|
|
1569
|
+
} catch (cause) {
|
|
1570
|
+
if (cause.code === "ENOENT") return void 0;
|
|
1571
|
+
throw cause;
|
|
1572
|
+
}
|
|
1573
|
+
}
|
|
1574
|
+
async setSessionMeta(conversationId, patch) {
|
|
1575
|
+
const metaPath = this.#metaPath(conversationId);
|
|
1576
|
+
await promises.mkdir(path.dirname(metaPath), { recursive: true });
|
|
1577
|
+
await withFileLock(metaPath, async () => {
|
|
1578
|
+
let current = {};
|
|
1579
|
+
try {
|
|
1580
|
+
current = coerceSessionMeta(JSON.parse(await promises.readFile(metaPath, "utf8"))) ?? {};
|
|
1581
|
+
} catch (cause) {
|
|
1582
|
+
if (cause.code !== "ENOENT") throw cause;
|
|
1583
|
+
}
|
|
1584
|
+
const next = applyMetaPatch(current, patch);
|
|
1585
|
+
await promises.writeFile(metaPath, redactSecrets(JSON.stringify(next)), "utf8");
|
|
1586
|
+
});
|
|
1587
|
+
}
|
|
1588
|
+
// SE33 — the durable objective is kept in `objective.json` beside the
|
|
1589
|
+
// transcript (separate from messages.jsonl + session.json). Uses the TOTAL
|
|
1590
|
+
// `safeFilenameForId` (not `sanitizeIdentifier`) so a caller-supplied
|
|
1591
|
+
// `threadId` with exotic characters (e.g. "user@example.com") hashes to a
|
|
1592
|
+
// deterministic dir instead of throwing — honoring the objective methods'
|
|
1593
|
+
// never-throw contract (ADR D6). Conforming ids pass through unchanged, so
|
|
1594
|
+
// the objective still sits beside the transcript for the normal case.
|
|
1595
|
+
#objectivePath(conversationId) {
|
|
1596
|
+
const safe2 = safeFilenameForId(conversationId, { maxLen: 128 });
|
|
1597
|
+
return safePathJoin(this.#root, ".theokit", "agents", safe2, "objective.json");
|
|
1598
|
+
}
|
|
1599
|
+
async getObjectiveRecord(conversationId) {
|
|
1600
|
+
try {
|
|
1601
|
+
const raw = await promises.readFile(this.#objectivePath(conversationId), "utf8");
|
|
1602
|
+
return coerceObjectiveRecord(JSON.parse(raw));
|
|
1603
|
+
} catch (cause) {
|
|
1604
|
+
if (cause.code === "ENOENT") return void 0;
|
|
1605
|
+
throw cause;
|
|
1606
|
+
}
|
|
1607
|
+
}
|
|
1608
|
+
async setObjectiveRecord(conversationId, record) {
|
|
1609
|
+
const path$1 = this.#objectivePath(conversationId);
|
|
1610
|
+
if (record === null) {
|
|
1611
|
+
await promises.rm(path$1, { force: true });
|
|
1612
|
+
return;
|
|
1613
|
+
}
|
|
1614
|
+
await promises.mkdir(path.dirname(path$1), { recursive: true });
|
|
1615
|
+
await withFileLock(path$1, async () => {
|
|
1616
|
+
await promises.writeFile(path$1, redactSecrets(JSON.stringify(record)), "utf8");
|
|
1617
|
+
});
|
|
1618
|
+
}
|
|
1619
|
+
// SE33 (HIGH-1 fix) — atomic read-modify-write: the read that feeds `mutate`
|
|
1620
|
+
// happens INSIDE the same file lock as the write, so two concurrent progress
|
|
1621
|
+
// write-backs on one thread cannot both read a stale `runsUsed` and drop turns.
|
|
1622
|
+
async updateObjectiveRecord(conversationId, mutate) {
|
|
1623
|
+
const path$1 = this.#objectivePath(conversationId);
|
|
1624
|
+
await promises.mkdir(path.dirname(path$1), { recursive: true });
|
|
1625
|
+
await withFileLock(path$1, async () => {
|
|
1626
|
+
let current;
|
|
1627
|
+
try {
|
|
1628
|
+
current = coerceObjectiveRecord(JSON.parse(await promises.readFile(path$1, "utf8")));
|
|
1629
|
+
} catch (cause) {
|
|
1630
|
+
if (cause.code !== "ENOENT") throw cause;
|
|
1631
|
+
}
|
|
1632
|
+
const next = mutate(current);
|
|
1633
|
+
if (next === void 0) return;
|
|
1634
|
+
if (next === null) {
|
|
1635
|
+
await promises.rm(path$1, { force: true });
|
|
1636
|
+
return;
|
|
1637
|
+
}
|
|
1638
|
+
await promises.writeFile(path$1, redactSecrets(JSON.stringify(next)), "utf8");
|
|
1639
|
+
});
|
|
1640
|
+
}
|
|
1641
|
+
async dispose() {
|
|
1642
|
+
}
|
|
1643
|
+
};
|
|
1644
|
+
}
|
|
1645
|
+
});
|
|
1646
|
+
async function withToolWhitelist(whitelist, fn) {
|
|
1647
|
+
return toolWhitelistStore.run(whitelist, fn);
|
|
1648
|
+
}
|
|
1649
|
+
function currentToolWhitelist() {
|
|
1650
|
+
return toolWhitelistStore.getStore();
|
|
1651
|
+
}
|
|
1652
|
+
function checkToolWhitelist(toolName) {
|
|
1653
|
+
const whitelist = currentToolWhitelist();
|
|
1654
|
+
if (whitelist === void 0) return { allowed: true };
|
|
1655
|
+
if (!whitelist.has(toolName)) {
|
|
1656
|
+
return {
|
|
1657
|
+
allowed: false,
|
|
1658
|
+
reason: `Tool "${toolName}" not available in this fork context`
|
|
1659
|
+
};
|
|
1660
|
+
}
|
|
1661
|
+
return { allowed: true };
|
|
1662
|
+
}
|
|
1663
|
+
var toolWhitelistStore;
|
|
1664
|
+
var init_async_local_storage = __esm({
|
|
1665
|
+
"src/internal/runtime/concurrency/async-local-storage.ts"() {
|
|
1666
|
+
toolWhitelistStore = new async_hooks.AsyncLocalStorage();
|
|
1667
|
+
}
|
|
1668
|
+
});
|
|
1669
|
+
|
|
1670
|
+
// src/internal/runtime/concurrency/async-semaphore.ts
|
|
1671
|
+
function createSemaphore(permits) {
|
|
1672
|
+
if (!Number.isInteger(permits) || permits < 1) {
|
|
1673
|
+
throw new ConfigurationError(
|
|
1674
|
+
`async-semaphore: permits must be a positive integer, got ${permits}`,
|
|
1675
|
+
{ code: "invalid_concurrency" }
|
|
1676
|
+
);
|
|
1089
1677
|
}
|
|
1090
1678
|
let active = 0;
|
|
1091
1679
|
const queue = [];
|
|
@@ -1428,6 +2016,151 @@ var init_credential_pool_context = __esm({
|
|
|
1428
2016
|
}
|
|
1429
2017
|
});
|
|
1430
2018
|
|
|
2019
|
+
// src/internal/runtime/objective/objective-store.ts
|
|
2020
|
+
function canPersist(a) {
|
|
2021
|
+
return typeof a.getObjectiveRecord === "function" && typeof a.setObjectiveRecord === "function";
|
|
2022
|
+
}
|
|
2023
|
+
async function mutateObjective(adapter, conversationId, mutate) {
|
|
2024
|
+
if (typeof adapter.updateObjectiveRecord === "function") {
|
|
2025
|
+
await adapter.updateObjectiveRecord(conversationId, mutate);
|
|
2026
|
+
return;
|
|
2027
|
+
}
|
|
2028
|
+
const current = await adapter.getObjectiveRecord(conversationId);
|
|
2029
|
+
const next = mutate(current);
|
|
2030
|
+
if (next === void 0) return;
|
|
2031
|
+
await adapter.setObjectiveRecord(conversationId, next);
|
|
2032
|
+
}
|
|
2033
|
+
async function getObjective(adapter, conversationId) {
|
|
2034
|
+
if (!canPersist(adapter)) return void 0;
|
|
2035
|
+
return adapter.getObjectiveRecord(conversationId);
|
|
2036
|
+
}
|
|
2037
|
+
async function setObjective(adapter, conversationId, objective, options) {
|
|
2038
|
+
if (!canPersist(adapter)) return;
|
|
2039
|
+
const record = {
|
|
2040
|
+
_schemaVersion: 1,
|
|
2041
|
+
objective,
|
|
2042
|
+
...options !== void 0 ? { options } : {},
|
|
2043
|
+
status: "active",
|
|
2044
|
+
runsUsed: 0
|
|
2045
|
+
};
|
|
2046
|
+
await adapter.setObjectiveRecord(conversationId, record);
|
|
2047
|
+
}
|
|
2048
|
+
async function updateObjectiveOptions(adapter, conversationId, patch) {
|
|
2049
|
+
if (!canPersist(adapter)) return;
|
|
2050
|
+
await mutateObjective(
|
|
2051
|
+
adapter,
|
|
2052
|
+
conversationId,
|
|
2053
|
+
(current) => current === void 0 ? void 0 : { ...current, options: { ...current.options, ...patch } }
|
|
2054
|
+
);
|
|
2055
|
+
}
|
|
2056
|
+
async function writeObjectiveProgress(adapter, conversationId, progress) {
|
|
2057
|
+
if (!canPersist(adapter)) return;
|
|
2058
|
+
await mutateObjective(
|
|
2059
|
+
adapter,
|
|
2060
|
+
conversationId,
|
|
2061
|
+
(current) => current === void 0 ? void 0 : { ...current, runsUsed: progress.runsUsed, status: progress.status }
|
|
2062
|
+
);
|
|
2063
|
+
}
|
|
2064
|
+
async function clearObjective(adapter, conversationId) {
|
|
2065
|
+
if (!canPersist(adapter)) return;
|
|
2066
|
+
await adapter.setObjectiveRecord(conversationId, null);
|
|
2067
|
+
}
|
|
2068
|
+
var init_objective_store = __esm({
|
|
2069
|
+
"src/internal/runtime/objective/objective-store.ts"() {
|
|
2070
|
+
}
|
|
2071
|
+
});
|
|
2072
|
+
|
|
2073
|
+
// src/internal/runtime/local-agent/local-agent-goal-extensions.ts
|
|
2074
|
+
var local_agent_goal_extensions_exports = {};
|
|
2075
|
+
__export(local_agent_goal_extensions_exports, {
|
|
2076
|
+
formatObjectiveProjection: () => formatObjectiveProjection,
|
|
2077
|
+
localAgentClearObjective: () => localAgentClearObjective,
|
|
2078
|
+
localAgentGetObjective: () => localAgentGetObjective,
|
|
2079
|
+
localAgentSetObjective: () => localAgentSetObjective,
|
|
2080
|
+
localAgentUpdateObjectiveOptions: () => localAgentUpdateObjectiveOptions,
|
|
2081
|
+
persistDurableProgress: () => persistDurableProgress,
|
|
2082
|
+
resolveCurrentObjectiveText: () => resolveCurrentObjectiveText,
|
|
2083
|
+
resolveDurableRun: () => resolveDurableRun
|
|
2084
|
+
});
|
|
2085
|
+
function resolveAdapter(handle) {
|
|
2086
|
+
return typeof handle === "string" ? new FileSystemConversationStorage({ root: handle }) : handle;
|
|
2087
|
+
}
|
|
2088
|
+
function assertValidGoalOptions(options) {
|
|
2089
|
+
if (options.maxRuns !== void 0 && (!Number.isInteger(options.maxRuns) || options.maxRuns <= 0)) {
|
|
2090
|
+
throw new ConfigurationError(`maxRuns must be a positive integer, got ${options.maxRuns}`, {
|
|
2091
|
+
code: "invalid_objective_max_runs"
|
|
2092
|
+
});
|
|
2093
|
+
}
|
|
2094
|
+
}
|
|
2095
|
+
async function localAgentSetObjective(handle, objective, opts) {
|
|
2096
|
+
const { threadId, ...options } = opts;
|
|
2097
|
+
assertValidGoalOptions(options);
|
|
2098
|
+
await setObjective(
|
|
2099
|
+
resolveAdapter(handle),
|
|
2100
|
+
threadId,
|
|
2101
|
+
objective,
|
|
2102
|
+
Object.keys(options).length > 0 ? options : void 0
|
|
2103
|
+
);
|
|
2104
|
+
}
|
|
2105
|
+
async function localAgentGetObjective(handle, opts) {
|
|
2106
|
+
return getObjective(resolveAdapter(handle), opts.threadId);
|
|
2107
|
+
}
|
|
2108
|
+
async function localAgentUpdateObjectiveOptions(handle, opts) {
|
|
2109
|
+
const { threadId, ...patch } = opts;
|
|
2110
|
+
assertValidGoalOptions(patch);
|
|
2111
|
+
await updateObjectiveOptions(resolveAdapter(handle), threadId, patch);
|
|
2112
|
+
}
|
|
2113
|
+
async function localAgentClearObjective(handle, opts) {
|
|
2114
|
+
await clearObjective(resolveAdapter(handle), opts.threadId);
|
|
2115
|
+
}
|
|
2116
|
+
async function resolveCurrentObjectiveText(handle, threadId) {
|
|
2117
|
+
const record = await getObjective(resolveAdapter(handle), threadId);
|
|
2118
|
+
return record?.status === "active" ? record.objective : void 0;
|
|
2119
|
+
}
|
|
2120
|
+
function formatObjectiveProjection(objective, assembled) {
|
|
2121
|
+
const signal = `<current-objective>
|
|
2122
|
+
${objective}
|
|
2123
|
+
</current-objective>`;
|
|
2124
|
+
return assembled === void 0 || assembled.length === 0 ? signal : `${signal}
|
|
2125
|
+
|
|
2126
|
+
${assembled}`;
|
|
2127
|
+
}
|
|
2128
|
+
async function resolveDurableRun(handle, goalConfig, threadId, callerOptions) {
|
|
2129
|
+
const record = await getObjective(resolveAdapter(handle), threadId);
|
|
2130
|
+
if (record === void 0) return { kind: "none" };
|
|
2131
|
+
const judgeModel = callerOptions?.judgeModel ?? record.options?.judgeModel ?? goalConfig?.judgeModel;
|
|
2132
|
+
if (judgeModel === void 0) return { kind: "inert" };
|
|
2133
|
+
const totalBudget = record.options?.maxRuns ?? goalConfig?.maxRuns ?? DEFAULT_MAX_RUNS;
|
|
2134
|
+
const remaining = Math.max(0, totalBudget - record.runsUsed);
|
|
2135
|
+
if (remaining === 0) return { kind: "exhausted" };
|
|
2136
|
+
const perCall = callerOptions?.maxTurns;
|
|
2137
|
+
const maxTurns = perCall !== void 0 ? Math.min(perCall, remaining) : remaining;
|
|
2138
|
+
return {
|
|
2139
|
+
kind: "run",
|
|
2140
|
+
goal: record.objective,
|
|
2141
|
+
options: { ...callerOptions, maxTurns, judgeModel }
|
|
2142
|
+
};
|
|
2143
|
+
}
|
|
2144
|
+
async function persistDurableProgress(handle, threadId, result) {
|
|
2145
|
+
const adapter = resolveAdapter(handle);
|
|
2146
|
+
const record = await getObjective(adapter, threadId);
|
|
2147
|
+
if (record === void 0) return;
|
|
2148
|
+
const status = result.status === "completed" ? "done" : result.status === "paused" ? "paused" : "active";
|
|
2149
|
+
await writeObjectiveProgress(adapter, threadId, {
|
|
2150
|
+
runsUsed: record.runsUsed + result.turnsUsed,
|
|
2151
|
+
status
|
|
2152
|
+
});
|
|
2153
|
+
}
|
|
2154
|
+
var DEFAULT_MAX_RUNS;
|
|
2155
|
+
var init_local_agent_goal_extensions = __esm({
|
|
2156
|
+
"src/internal/runtime/local-agent/local-agent-goal-extensions.ts"() {
|
|
2157
|
+
init_errors();
|
|
2158
|
+
init_conversation_storage_fs();
|
|
2159
|
+
init_objective_store();
|
|
2160
|
+
DEFAULT_MAX_RUNS = 20;
|
|
2161
|
+
}
|
|
2162
|
+
});
|
|
2163
|
+
|
|
1431
2164
|
// src/internal/personality/context.ts
|
|
1432
2165
|
var context_exports = {};
|
|
1433
2166
|
__export(context_exports, {
|
|
@@ -3218,154 +3951,9 @@ function structurableAnswerOrThrow(result, GenerateObjectError2) {
|
|
|
3218
3951
|
// src/internal/runtime/cloud/cloud-agent.ts
|
|
3219
3952
|
init_errors();
|
|
3220
3953
|
init_cwd_mutex();
|
|
3954
|
+
init_path_guard();
|
|
3221
3955
|
|
|
3222
|
-
// src/internal/
|
|
3223
|
-
init_errors();
|
|
3224
|
-
var PathTraversalError = class extends ConfigurationError {
|
|
3225
|
-
name = "PathTraversalError";
|
|
3226
|
-
constructor(input, resolvedPath) {
|
|
3227
|
-
super(`Path traversal attempt: ${input} \u2192 ${resolvedPath}`, {
|
|
3228
|
-
code: "path_traversal"
|
|
3229
|
-
});
|
|
3230
|
-
}
|
|
3231
|
-
};
|
|
3232
|
-
function safePathJoin(base, ...parts) {
|
|
3233
|
-
if (base === "") {
|
|
3234
|
-
throw new Error("safePathJoin: base must be non-empty");
|
|
3235
|
-
}
|
|
3236
|
-
rejectNulAndControlChars(base, "base");
|
|
3237
|
-
for (const part of parts) {
|
|
3238
|
-
rejectNulAndControlChars(part, "path segment");
|
|
3239
|
-
}
|
|
3240
|
-
const baseResolved = path.resolve(base);
|
|
3241
|
-
const target = path.resolve(base, ...parts);
|
|
3242
|
-
if (target !== baseResolved && !target.startsWith(baseResolved + path.sep)) {
|
|
3243
|
-
throw new PathTraversalError(parts.join("/"), target);
|
|
3244
|
-
}
|
|
3245
|
-
return target;
|
|
3246
|
-
}
|
|
3247
|
-
function rejectNulAndControlChars(input, role) {
|
|
3248
|
-
for (let i = 0; i < input.length; i++) {
|
|
3249
|
-
const code = input.charCodeAt(i);
|
|
3250
|
-
if (code === 0 || code >= 1 && code <= 31 || code === 127) {
|
|
3251
|
-
const label = code === 0 ? "<nul-byte>" : `<control-char-0x${code.toString(16)}>`;
|
|
3252
|
-
throw new PathTraversalError(`${role}: ${input}`, label);
|
|
3253
|
-
}
|
|
3254
|
-
}
|
|
3255
|
-
}
|
|
3256
|
-
function assertNoSymlinkEscape(path$1, base) {
|
|
3257
|
-
rejectNulAndControlChars(path$1, "path");
|
|
3258
|
-
rejectNulAndControlChars(base, "base");
|
|
3259
|
-
let baseResolved;
|
|
3260
|
-
try {
|
|
3261
|
-
baseResolved = fs.realpathSync(base);
|
|
3262
|
-
} catch {
|
|
3263
|
-
baseResolved = path.resolve(base);
|
|
3264
|
-
}
|
|
3265
|
-
const resolved = realpathOfDeepestExisting(path$1);
|
|
3266
|
-
if (resolved === void 0) return;
|
|
3267
|
-
if (resolved !== baseResolved && !resolved.startsWith(baseResolved + path.sep)) {
|
|
3268
|
-
throw new PathTraversalError(`symlink ${path$1}`, resolved);
|
|
3269
|
-
}
|
|
3270
|
-
}
|
|
3271
|
-
function realpathOfDeepestExisting(path$1) {
|
|
3272
|
-
try {
|
|
3273
|
-
return fs.realpathSync(path$1);
|
|
3274
|
-
} catch {
|
|
3275
|
-
}
|
|
3276
|
-
try {
|
|
3277
|
-
const stat6 = fs.lstatSync(path$1);
|
|
3278
|
-
if (stat6.isSymbolicLink()) {
|
|
3279
|
-
const target = fs.readlinkSync(path$1);
|
|
3280
|
-
const parentReal = realpathOfDeepestExisting(path.dirname(path$1));
|
|
3281
|
-
const parentBase = parentReal ?? path.dirname(path$1);
|
|
3282
|
-
return path.resolve(parentBase, target);
|
|
3283
|
-
}
|
|
3284
|
-
} catch {
|
|
3285
|
-
}
|
|
3286
|
-
let cursor = path.dirname(path$1);
|
|
3287
|
-
let suffix = path$1.slice(cursor.length);
|
|
3288
|
-
while (cursor !== path.dirname(cursor)) {
|
|
3289
|
-
try {
|
|
3290
|
-
const real = fs.realpathSync(cursor);
|
|
3291
|
-
return path.resolve(real, `.${suffix}`);
|
|
3292
|
-
} catch {
|
|
3293
|
-
suffix = path$1.slice(path.dirname(cursor).length);
|
|
3294
|
-
cursor = path.dirname(cursor);
|
|
3295
|
-
}
|
|
3296
|
-
}
|
|
3297
|
-
return void 0;
|
|
3298
|
-
}
|
|
3299
|
-
var IDENTIFIER_PATTERN = /^[a-z0-9][a-z0-9\-_]*$/i;
|
|
3300
|
-
function validateArtifactPath(input) {
|
|
3301
|
-
rejectKnownPrefixVectors(input);
|
|
3302
|
-
const normalized = decodeAndNormalize(input);
|
|
3303
|
-
rejectParentTraversal(input, normalized);
|
|
3304
|
-
}
|
|
3305
|
-
function rejectKnownPrefixVectors(input) {
|
|
3306
|
-
if (input.includes("\0")) {
|
|
3307
|
-
throw new PathTraversalError(input, "<nul-byte>");
|
|
3308
|
-
}
|
|
3309
|
-
if (input.startsWith("/") || input.startsWith("~")) {
|
|
3310
|
-
throw new PathTraversalError(input, input);
|
|
3311
|
-
}
|
|
3312
|
-
if (/^[A-Za-z]:[\\/]?/.test(input)) {
|
|
3313
|
-
throw new PathTraversalError(input, input);
|
|
3314
|
-
}
|
|
3315
|
-
}
|
|
3316
|
-
function decodeAndNormalize(input) {
|
|
3317
|
-
let decoded = input;
|
|
3318
|
-
for (let i = 0; i < 2; i += 1) {
|
|
3319
|
-
try {
|
|
3320
|
-
const next = decodeURIComponent(decoded);
|
|
3321
|
-
if (next === decoded) break;
|
|
3322
|
-
decoded = next;
|
|
3323
|
-
} catch {
|
|
3324
|
-
throw new PathTraversalError(input, "<malformed-url-encoding>");
|
|
3325
|
-
}
|
|
3326
|
-
}
|
|
3327
|
-
return decoded.replace(/\\/g, "/");
|
|
3328
|
-
}
|
|
3329
|
-
function rejectParentTraversal(input, normalized) {
|
|
3330
|
-
for (const segment of normalized.split("/")) {
|
|
3331
|
-
if (segment === ".." || segment === "..%00") {
|
|
3332
|
-
throw new PathTraversalError(input, normalized);
|
|
3333
|
-
}
|
|
3334
|
-
}
|
|
3335
|
-
if (normalized.includes("..")) {
|
|
3336
|
-
throw new PathTraversalError(input, normalized);
|
|
3337
|
-
}
|
|
3338
|
-
}
|
|
3339
|
-
function sanitizeIdentifier(input, options) {
|
|
3340
|
-
const maxLen = options?.maxLen ?? 64;
|
|
3341
|
-
if (input.length === 0 || input.length > maxLen) {
|
|
3342
|
-
throw new ConfigurationError(`Identifier length out of range (1-${maxLen}): "${input}"`, {
|
|
3343
|
-
code: "invalid_identifier"
|
|
3344
|
-
});
|
|
3345
|
-
}
|
|
3346
|
-
rejectNulAndControlChars(input, "identifier");
|
|
3347
|
-
if (!IDENTIFIER_PATTERN.test(input)) {
|
|
3348
|
-
throw new ConfigurationError(`Identifier contains invalid characters: "${input}"`, {
|
|
3349
|
-
code: "invalid_identifier"
|
|
3350
|
-
});
|
|
3351
|
-
}
|
|
3352
|
-
return input.toLowerCase();
|
|
3353
|
-
}
|
|
3354
|
-
function safeFilenameForId(id, options) {
|
|
3355
|
-
if (id.length === 0) {
|
|
3356
|
-
throw new ConfigurationError("Filename id must be a non-empty string", {
|
|
3357
|
-
code: "invalid_filename_id"
|
|
3358
|
-
});
|
|
3359
|
-
}
|
|
3360
|
-
const maxLen = options?.maxLen;
|
|
3361
|
-
const lower = id.toLowerCase();
|
|
3362
|
-
if (lower.length <= maxLen && IDENTIFIER_PATTERN.test(lower)) {
|
|
3363
|
-
return lower;
|
|
3364
|
-
}
|
|
3365
|
-
return `h-${crypto.createHash("sha256").update(id).digest("hex").slice(0, 16)}`;
|
|
3366
|
-
}
|
|
3367
|
-
|
|
3368
|
-
// src/internal/runtime/model-selection.ts
|
|
3956
|
+
// src/internal/runtime/model-selection.ts
|
|
3369
3957
|
init_errors();
|
|
3370
3958
|
function normalizeModel(m) {
|
|
3371
3959
|
if (m === void 0 || typeof m !== "string") return m;
|
|
@@ -3853,8 +4441,8 @@ function serializeMemory2(memory) {
|
|
|
3853
4441
|
return result;
|
|
3854
4442
|
}
|
|
3855
4443
|
|
|
3856
|
-
// src/internal/
|
|
3857
|
-
|
|
4444
|
+
// src/internal/runtime/fixtures/fixture-responder.ts
|
|
4445
|
+
init_security();
|
|
3858
4446
|
|
|
3859
4447
|
// src/internal/runtime/fixtures/fixture-events.ts
|
|
3860
4448
|
function assistantOnlyConversation(text) {
|
|
@@ -3973,6 +4561,10 @@ function sanitizeMcpName(name) {
|
|
|
3973
4561
|
// src/internal/memory/storage/markdown-store.ts
|
|
3974
4562
|
init_atomic_write();
|
|
3975
4563
|
init_cwd_mutex();
|
|
4564
|
+
|
|
4565
|
+
// src/internal/memory/types.ts
|
|
4566
|
+
init_path_guard();
|
|
4567
|
+
init_security();
|
|
3976
4568
|
function legacyMemoryJsonPath(cwd, config) {
|
|
3977
4569
|
if (config.storePath !== void 0) {
|
|
3978
4570
|
return path.resolve(cwd, config.storePath);
|
|
@@ -5300,68 +5892,7 @@ init_cwd_mutex();
|
|
|
5300
5892
|
|
|
5301
5893
|
// src/internal/personality/store.ts
|
|
5302
5894
|
init_atomic_write();
|
|
5303
|
-
|
|
5304
|
-
// src/internal/persistence/file-lock.ts
|
|
5305
|
-
init_cwd_mutex();
|
|
5306
|
-
var cached;
|
|
5307
|
-
var warnedMissing = false;
|
|
5308
|
-
var warnedStructural = false;
|
|
5309
|
-
async function getProperLockfile() {
|
|
5310
|
-
if (cached !== void 0) return cached;
|
|
5311
|
-
try {
|
|
5312
|
-
const mod = await import('proper-lockfile');
|
|
5313
|
-
if (!validateLockModule(mod)) {
|
|
5314
|
-
if (!warnedStructural) {
|
|
5315
|
-
warnedStructural = true;
|
|
5316
|
-
process.stderr.write(
|
|
5317
|
-
"[theokit-sdk] proper-lockfile: imported module does NOT expose the expected `lock`/`unlock` API surface. This may indicate a supply-chain compromise or an incompatible major version. Falling back to in-process mutex (no cross-process safety). Reinstall with: pnpm add proper-lockfile@^11\n"
|
|
5318
|
-
);
|
|
5319
|
-
}
|
|
5320
|
-
cached = null;
|
|
5321
|
-
return cached;
|
|
5322
|
-
}
|
|
5323
|
-
cached = mod;
|
|
5324
|
-
} catch {
|
|
5325
|
-
cached = null;
|
|
5326
|
-
}
|
|
5327
|
-
return cached;
|
|
5328
|
-
}
|
|
5329
|
-
function validateLockModule(mod) {
|
|
5330
|
-
if (mod === null || mod === void 0 || typeof mod !== "object") return false;
|
|
5331
|
-
const m = mod;
|
|
5332
|
-
return typeof m.lock === "function" && typeof m.unlock === "function";
|
|
5333
|
-
}
|
|
5334
|
-
async function withFileLock(path, fn, options) {
|
|
5335
|
-
const lib = await getProperLockfile();
|
|
5336
|
-
if (lib === null) {
|
|
5337
|
-
if (!warnedMissing) {
|
|
5338
|
-
warnedMissing = true;
|
|
5339
|
-
process.stderr.write(
|
|
5340
|
-
"[theokit-sdk] proper-lockfile not installed; cross-process file lock unavailable. Install with: pnpm add proper-lockfile\n"
|
|
5341
|
-
);
|
|
5342
|
-
}
|
|
5343
|
-
return withCwdMutex(`file-lock:${path}`, fn);
|
|
5344
|
-
}
|
|
5345
|
-
return withCwdMutex(`file-lock:${path}`, async () => {
|
|
5346
|
-
const release = await lib.lock(path, {
|
|
5347
|
-
// EC-1: companion lockfile, target path may not exist yet.
|
|
5348
|
-
lockfilePath: `${path}.lock`,
|
|
5349
|
-
realpath: false,
|
|
5350
|
-
stale: 3e4,
|
|
5351
|
-
retries: {
|
|
5352
|
-
retries: 5,
|
|
5353
|
-
factor: 1.5,
|
|
5354
|
-
minTimeout: 100,
|
|
5355
|
-
maxTimeout: 5e3
|
|
5356
|
-
}
|
|
5357
|
-
});
|
|
5358
|
-
try {
|
|
5359
|
-
return await fn();
|
|
5360
|
-
} finally {
|
|
5361
|
-
await release();
|
|
5362
|
-
}
|
|
5363
|
-
});
|
|
5364
|
-
}
|
|
5895
|
+
init_file_lock();
|
|
5365
5896
|
var THEOKIT_DIR_NAME = ".theokit";
|
|
5366
5897
|
function getTheokitHome(cwd) {
|
|
5367
5898
|
const override = process.env.THEOKIT_HOME?.trim();
|
|
@@ -5803,6 +6334,9 @@ var HISTOGRAM_NAMES = {
|
|
|
5803
6334
|
/** M3 #66 — count of finishes where the provider omitted usage (silent undercount). */
|
|
5804
6335
|
LLM_USAGE_MISSING: "theokit_llm_usage_missing"
|
|
5805
6336
|
};
|
|
6337
|
+
|
|
6338
|
+
// src/internal/telemetry/tracer.ts
|
|
6339
|
+
init_security();
|
|
5806
6340
|
function safeRequire(moduleName) {
|
|
5807
6341
|
try {
|
|
5808
6342
|
const r = module$1.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('cron.cjs', document.baseURI).href)));
|
|
@@ -6282,415 +6816,163 @@ function spawnAndCollect(options) {
|
|
|
6282
6816
|
if (options.stdin !== void 0 && child.stdin !== null) {
|
|
6283
6817
|
child.stdin.end(options.stdin);
|
|
6284
6818
|
}
|
|
6285
|
-
});
|
|
6286
|
-
}
|
|
6287
|
-
|
|
6288
|
-
// src/internal/runtime/hooks/hooks-executor.ts
|
|
6289
|
-
init_hooks_source();
|
|
6290
|
-
var HooksExecutor = class {
|
|
6291
|
-
constructor(cwd) {
|
|
6292
|
-
this.cwd = cwd;
|
|
6293
|
-
}
|
|
6294
|
-
cwd;
|
|
6295
|
-
config = {};
|
|
6296
|
-
async initialize(settingSourcesIncludeProject) {
|
|
6297
|
-
if (!settingSourcesIncludeProject) {
|
|
6298
|
-
this.config = {};
|
|
6299
|
-
return;
|
|
6300
|
-
}
|
|
6301
|
-
this.config = await loadHookConfig(this.cwd);
|
|
6302
|
-
}
|
|
6303
|
-
/** Fire every hook registered for `event` and aggregate the decisions. */
|
|
6304
|
-
async run(payload) {
|
|
6305
|
-
const commands = this.commandsFor(payload.event, payload.tool);
|
|
6306
|
-
if (commands.length === 0) return { decisions: [], blocked: false };
|
|
6307
|
-
const decisions = [];
|
|
6308
|
-
for (const command of commands) {
|
|
6309
|
-
const decision = await this.executeOne(command, payload);
|
|
6310
|
-
decisions.push(decision);
|
|
6311
|
-
if (decision.decision === "deny") {
|
|
6312
|
-
const result = {
|
|
6313
|
-
decisions,
|
|
6314
|
-
blocked: true
|
|
6315
|
-
};
|
|
6316
|
-
if (decision.reason !== void 0) result.reason = decision.reason;
|
|
6317
|
-
return result;
|
|
6318
|
-
}
|
|
6319
|
-
}
|
|
6320
|
-
return { decisions, blocked: false };
|
|
6321
|
-
}
|
|
6322
|
-
commandsFor(event, tool) {
|
|
6323
|
-
const list = this.config.hooks?.[event] ?? [];
|
|
6324
|
-
if (tool === void 0) return list;
|
|
6325
|
-
return list.filter((entry) => {
|
|
6326
|
-
if (entry.matcher === void 0) return true;
|
|
6327
|
-
try {
|
|
6328
|
-
return new RegExp(entry.matcher).test(tool);
|
|
6329
|
-
} catch {
|
|
6330
|
-
return entry.matcher === tool;
|
|
6331
|
-
}
|
|
6332
|
-
});
|
|
6333
|
-
}
|
|
6334
|
-
async executeOne(command, payload) {
|
|
6335
|
-
const timeoutMs = command.timeoutMs ?? 3e4;
|
|
6336
|
-
const result = await spawnAndCollect({
|
|
6337
|
-
command: "sh",
|
|
6338
|
-
args: ["-c", command.command],
|
|
6339
|
-
cwd: this.cwd,
|
|
6340
|
-
timeoutMs,
|
|
6341
|
-
stdin: JSON.stringify(payload)
|
|
6342
|
-
});
|
|
6343
|
-
if (result.timedOut) {
|
|
6344
|
-
return { decision: "deny", reason: `Hook timed out after ${timeoutMs}ms` };
|
|
6345
|
-
}
|
|
6346
|
-
if (result.spawnError !== void 0) {
|
|
6347
|
-
return { decision: "deny", reason: `Hook spawn failed: ${result.spawnError.message}` };
|
|
6348
|
-
}
|
|
6349
|
-
if (result.exitCode !== 0) {
|
|
6350
|
-
return {
|
|
6351
|
-
decision: "deny",
|
|
6352
|
-
reason: result.stderr.trim().length > 0 ? result.stderr.trim() : `Hook exited with code ${result.exitCode}`
|
|
6353
|
-
};
|
|
6354
|
-
}
|
|
6355
|
-
return parseDecisionFromStdout(result.stdout);
|
|
6356
|
-
}
|
|
6357
|
-
};
|
|
6358
|
-
function parseDecisionFromStdout(stdout) {
|
|
6359
|
-
const trimmed = stdout.trim();
|
|
6360
|
-
if (trimmed.length === 0) return { decision: "allow" };
|
|
6361
|
-
try {
|
|
6362
|
-
const parsed = JSON.parse(trimmed);
|
|
6363
|
-
if (parsed.decision === "deny" || parsed.decision === "feedback") {
|
|
6364
|
-
const result = { decision: parsed.decision };
|
|
6365
|
-
if (parsed.reason !== void 0) result.reason = parsed.reason;
|
|
6366
|
-
if (parsed.feedback !== void 0) result.feedback = parsed.feedback;
|
|
6367
|
-
return result;
|
|
6368
|
-
}
|
|
6369
|
-
if (parsed.decision === "allow") return { decision: "allow" };
|
|
6370
|
-
} catch {
|
|
6371
|
-
return { decision: "feedback", feedback: trimmed };
|
|
6372
|
-
}
|
|
6373
|
-
return { decision: "allow" };
|
|
6374
|
-
}
|
|
6375
|
-
|
|
6376
|
-
// src/internal/memory/storage/session-summary-writer.ts
|
|
6377
|
-
init_atomic_write();
|
|
6378
|
-
var MAX_TURN_CHARS = 2e3;
|
|
6379
|
-
function sessionsDir(cwd) {
|
|
6380
|
-
return path.join(memoryDir(cwd), "sessions");
|
|
6381
|
-
}
|
|
6382
|
-
function sessionSummaryPath(cwd, runId) {
|
|
6383
|
-
return path.join(sessionsDir(cwd), `${safeFilenameForId(runId, { maxLen: 128 })}.md`);
|
|
6384
|
-
}
|
|
6385
|
-
function truncate(text) {
|
|
6386
|
-
if (text.length <= MAX_TURN_CHARS) return text;
|
|
6387
|
-
return `${text.slice(0, MAX_TURN_CHARS)}\u2026`;
|
|
6388
|
-
}
|
|
6389
|
-
async function writeSessionSummary(input) {
|
|
6390
|
-
if (input.status !== "finished") return;
|
|
6391
|
-
const path = sessionSummaryPath(input.cwd, input.runId);
|
|
6392
|
-
await promises.mkdir(sessionsDir(input.cwd), { recursive: true });
|
|
6393
|
-
const safeUser = redactSecrets(truncate(input.userText));
|
|
6394
|
-
const safeAssistant = redactSecrets(truncate(input.assistantText));
|
|
6395
|
-
const iso = new Date(input.at).toISOString();
|
|
6396
|
-
const body = [
|
|
6397
|
-
"---",
|
|
6398
|
-
`runId: ${input.runId}`,
|
|
6399
|
-
`agentId: ${input.agentId}`,
|
|
6400
|
-
`at: ${iso}`,
|
|
6401
|
-
`status: ${input.status}`,
|
|
6402
|
-
"---",
|
|
6403
|
-
"",
|
|
6404
|
-
"## User",
|
|
6405
|
-
"",
|
|
6406
|
-
safeUser,
|
|
6407
|
-
"",
|
|
6408
|
-
"## Assistant",
|
|
6409
|
-
"",
|
|
6410
|
-
safeAssistant,
|
|
6411
|
-
""
|
|
6412
|
-
].join("\n");
|
|
6413
|
-
await replaceFileAtomic(path, body);
|
|
6414
|
-
}
|
|
6415
|
-
|
|
6416
|
-
// src/internal/runtime/memory/memory-path-selector.ts
|
|
6417
|
-
var PORT_MEMORY_PATH_ENV_VAR = "THEOKIT_PORT_MEMORY_PATH";
|
|
6418
|
-
function shouldUsePortMemoryPath() {
|
|
6419
|
-
const env = globalThis.process?.env;
|
|
6420
|
-
if (env === void 0) return false;
|
|
6421
|
-
const val = env[PORT_MEMORY_PATH_ENV_VAR];
|
|
6422
|
-
return val === "1" || val === "true";
|
|
6423
|
-
}
|
|
6424
|
-
function resolveMemoryProviderForLoop(consumerSupplied, defaultAdapter, portPathEnabled) {
|
|
6425
|
-
if (consumerSupplied !== void 0) return consumerSupplied;
|
|
6426
|
-
if (portPathEnabled) return defaultAdapter;
|
|
6427
|
-
return void 0;
|
|
6428
|
-
}
|
|
6429
|
-
function resolveMemoryToolsForLoop(legacyTools, portPathEnabled) {
|
|
6430
|
-
if (portPathEnabled) return void 0;
|
|
6431
|
-
return legacyTools;
|
|
6432
|
-
}
|
|
6433
|
-
function resolveActiveMemorySummaryForSend(legacySummary, portPathEnabled) {
|
|
6434
|
-
if (portPathEnabled) return void 0;
|
|
6435
|
-
return legacySummary;
|
|
6436
|
-
}
|
|
6437
|
-
|
|
6438
|
-
// src/internal/runtime/session/agent-session-store.ts
|
|
6439
|
-
init_atomic_write();
|
|
6440
|
-
var VALID_ROLES = /* @__PURE__ */ new Set([
|
|
6441
|
-
"user",
|
|
6442
|
-
"assistant",
|
|
6443
|
-
"system",
|
|
6444
|
-
"tool_call",
|
|
6445
|
-
"tool_result"
|
|
6446
|
-
]);
|
|
6447
|
-
function sessionFilePath(cwd, agentId) {
|
|
6448
|
-
const safe2 = sanitizeIdentifier(agentId, { maxLen: 128 });
|
|
6449
|
-
return safePathJoin(cwd, ".theokit", "agents", safe2, "messages.jsonl");
|
|
6450
|
-
}
|
|
6451
|
-
async function readJsonlLines(cwd, agentId) {
|
|
6452
|
-
const path = sessionFilePath(cwd, agentId);
|
|
6453
|
-
try {
|
|
6454
|
-
const raw = await promises.readFile(path, "utf8");
|
|
6455
|
-
return raw.split("\n").filter((line) => line.length > 0);
|
|
6456
|
-
} catch {
|
|
6457
|
-
return [];
|
|
6458
|
-
}
|
|
6459
|
-
}
|
|
6460
|
-
function warnMalformed(agentId, line) {
|
|
6461
|
-
process.stderr.write(
|
|
6462
|
-
`[theokit-sdk] skipping malformed line in messages.jsonl (${agentId}): ${line.slice(0, 80)}...
|
|
6463
|
-
`
|
|
6464
|
-
);
|
|
6465
|
-
}
|
|
6466
|
-
function hydrateSessionLine(parsed) {
|
|
6467
|
-
if (typeof parsed.text !== "string" || parsed.role === void 0) return void 0;
|
|
6468
|
-
if (parsed.role === "user" || parsed.role === "assistant") {
|
|
6469
|
-
return { role: parsed.role, text: parsed.text };
|
|
6470
|
-
}
|
|
6471
|
-
if (parsed.role === "tool_call" || parsed.role === "tool_result") {
|
|
6472
|
-
const label = parsed.role === "tool_call" ? "tool call" : "tool result";
|
|
6473
|
-
return { role: "assistant", text: `[${label}] ${parsed.text}` };
|
|
6474
|
-
}
|
|
6475
|
-
return void 0;
|
|
6476
|
-
}
|
|
6477
|
-
async function readSessionFile(cwd, agentId) {
|
|
6478
|
-
const lines = await readJsonlLines(cwd, agentId);
|
|
6479
|
-
const messages = [];
|
|
6480
|
-
for (const line of lines) {
|
|
6481
|
-
try {
|
|
6482
|
-
const msg = hydrateSessionLine(JSON.parse(line));
|
|
6483
|
-
if (msg !== void 0) messages.push(msg);
|
|
6484
|
-
} catch {
|
|
6485
|
-
warnMalformed(agentId, line);
|
|
6486
|
-
}
|
|
6487
|
-
}
|
|
6488
|
-
return messages;
|
|
6489
|
-
}
|
|
6490
|
-
async function readAllPersistedMessages(cwd, agentId) {
|
|
6491
|
-
const lines = await readJsonlLines(cwd, agentId);
|
|
6492
|
-
const messages = [];
|
|
6493
|
-
for (const line of lines) {
|
|
6494
|
-
try {
|
|
6495
|
-
const parsed = JSON.parse(line);
|
|
6496
|
-
if (parsed.role !== void 0 && VALID_ROLES.has(parsed.role) && typeof parsed.text === "string") {
|
|
6497
|
-
messages.push({
|
|
6498
|
-
role: parsed.role,
|
|
6499
|
-
text: parsed.text,
|
|
6500
|
-
at: typeof parsed.at === "number" ? parsed.at : Date.now()
|
|
6501
|
-
});
|
|
6502
|
-
}
|
|
6503
|
-
} catch {
|
|
6504
|
-
warnMalformed(agentId, line);
|
|
6505
|
-
}
|
|
6506
|
-
}
|
|
6507
|
-
return messages;
|
|
6508
|
-
}
|
|
6509
|
-
async function appendAnyPersistedMessage(cwd, agentId, record) {
|
|
6510
|
-
await appendPersistedMessages(cwd, agentId, [record]);
|
|
6511
|
-
}
|
|
6512
|
-
async function appendPersistedMessages(cwd, agentId, records) {
|
|
6513
|
-
if (records.length === 0) return;
|
|
6514
|
-
const path$1 = sessionFilePath(cwd, agentId);
|
|
6515
|
-
const payload = records.map((r) => `${redactSecrets(JSON.stringify(r))}
|
|
6516
|
-
`).join("");
|
|
6517
|
-
const dir = path.dirname(path$1);
|
|
6518
|
-
let written = false;
|
|
6519
|
-
const attempt = async () => {
|
|
6520
|
-
await promises.mkdir(dir, { recursive: true });
|
|
6521
|
-
await withFileLock(path$1, async () => {
|
|
6522
|
-
await promises.appendFile(path$1, payload, "utf8");
|
|
6523
|
-
written = true;
|
|
6524
|
-
});
|
|
6525
|
-
};
|
|
6526
|
-
try {
|
|
6527
|
-
await attempt();
|
|
6528
|
-
} catch (cause) {
|
|
6529
|
-
if (written || cause.code !== "ENOENT") throw cause;
|
|
6530
|
-
await attempt();
|
|
6531
|
-
}
|
|
6532
|
-
}
|
|
6533
|
-
async function rewriteLockedSession(path, transform) {
|
|
6534
|
-
await withFileLock(path, async () => {
|
|
6535
|
-
let raw;
|
|
6536
|
-
try {
|
|
6537
|
-
raw = await promises.readFile(path, "utf8");
|
|
6538
|
-
} catch {
|
|
6539
|
-
return;
|
|
6540
|
-
}
|
|
6541
|
-
const lines = raw.split("\n").filter((line) => line.length > 0);
|
|
6542
|
-
const next = transform(lines);
|
|
6543
|
-
if (next === void 0) return;
|
|
6544
|
-
await replaceFileAtomic(path, next);
|
|
6545
|
-
});
|
|
6546
|
-
}
|
|
6547
|
-
async function compactSessionFile(cwd, agentId, maxTurns) {
|
|
6548
|
-
const path = sessionFilePath(cwd, agentId);
|
|
6549
|
-
if (!fs.existsSync(path)) return;
|
|
6550
|
-
await rewriteLockedSession(
|
|
6551
|
-
path,
|
|
6552
|
-
(lines) => lines.length <= maxTurns * 2 ? void 0 : `${lines.slice(-maxTurns).join("\n")}
|
|
6553
|
-
`
|
|
6554
|
-
);
|
|
6555
|
-
}
|
|
6556
|
-
async function truncateSessionTo(cwd, agentId, keepCount) {
|
|
6557
|
-
const path = sessionFilePath(cwd, agentId);
|
|
6558
|
-
if (!fs.existsSync(path)) return 0;
|
|
6559
|
-
let kept = 0;
|
|
6560
|
-
await rewriteLockedSession(path, (lines) => {
|
|
6561
|
-
const keep = Math.max(0, Math.min(keepCount, lines.length));
|
|
6562
|
-
kept = keep;
|
|
6563
|
-
if (keep === lines.length) return void 0;
|
|
6564
|
-
return keep === 0 ? "" : `${lines.slice(0, keep).join("\n")}
|
|
6565
|
-
`;
|
|
6566
|
-
});
|
|
6567
|
-
return kept;
|
|
6568
|
-
}
|
|
6569
|
-
|
|
6570
|
-
// src/internal/persistence/pagination.ts
|
|
6571
|
-
function paginate(items, opts) {
|
|
6572
|
-
if (opts === void 0 || opts.offset === void 0 && opts.limit === void 0) return items;
|
|
6573
|
-
const start = Math.max(0, opts.offset ?? 0);
|
|
6574
|
-
const end = opts.limit === void 0 ? items.length : start + Math.max(0, opts.limit);
|
|
6575
|
-
return items.slice(start, end);
|
|
6576
|
-
}
|
|
6577
|
-
|
|
6578
|
-
// src/internal/persistence/session-meta.ts
|
|
6579
|
-
function applyMetaPatch(current, patch) {
|
|
6580
|
-
const next = {};
|
|
6581
|
-
if (current.title !== void 0) next.title = current.title;
|
|
6582
|
-
if (current.tag !== void 0) next.tag = current.tag;
|
|
6583
|
-
if (patch.title === null) delete next.title;
|
|
6584
|
-
else if (patch.title !== void 0) next.title = patch.title;
|
|
6585
|
-
if (patch.tag === null) delete next.tag;
|
|
6586
|
-
else if (patch.tag !== void 0) next.tag = patch.tag;
|
|
6587
|
-
return next;
|
|
6588
|
-
}
|
|
6589
|
-
function coerceSessionMeta(raw) {
|
|
6590
|
-
if (typeof raw !== "object" || raw === null) return void 0;
|
|
6591
|
-
const obj = raw;
|
|
6592
|
-
const meta = {};
|
|
6593
|
-
if (typeof obj.title === "string") meta.title = obj.title;
|
|
6594
|
-
if (typeof obj.tag === "string") meta.tag = obj.tag;
|
|
6595
|
-
return meta.title === void 0 && meta.tag === void 0 ? void 0 : meta;
|
|
6819
|
+
});
|
|
6596
6820
|
}
|
|
6597
6821
|
|
|
6598
|
-
// src/internal/
|
|
6599
|
-
|
|
6600
|
-
|
|
6601
|
-
constructor(
|
|
6602
|
-
this
|
|
6603
|
-
}
|
|
6604
|
-
/** Exposed for tests + diagnostics. The path is sanitized at use sites. */
|
|
6605
|
-
get root() {
|
|
6606
|
-
return this.#root;
|
|
6607
|
-
}
|
|
6608
|
-
async getMessages(conversationId, opts) {
|
|
6609
|
-
const records = await readAllPersistedMessages(this.#root, conversationId);
|
|
6610
|
-
const all = records.map(toStoredMessage);
|
|
6611
|
-
return paginate(all, opts);
|
|
6612
|
-
}
|
|
6613
|
-
async appendMessage(conversationId, message) {
|
|
6614
|
-
await appendAnyPersistedMessage(this.#root, conversationId, toRecord(message));
|
|
6615
|
-
}
|
|
6616
|
-
async appendMessages(conversationId, messages) {
|
|
6617
|
-
await appendPersistedMessages(this.#root, conversationId, messages.map(toRecord));
|
|
6618
|
-
}
|
|
6619
|
-
async truncateConversation(conversationId, keepCount) {
|
|
6620
|
-
return truncateSessionTo(this.#root, conversationId, keepCount);
|
|
6621
|
-
}
|
|
6622
|
-
async deleteConversation(conversationId) {
|
|
6623
|
-
const safe2 = sanitizeIdentifier(conversationId, { maxLen: 128 });
|
|
6624
|
-
const dirPath = safePathJoin(this.#root, ".theokit", "agents", safe2);
|
|
6625
|
-
await promises.rm(dirPath, { recursive: true, force: true });
|
|
6626
|
-
}
|
|
6627
|
-
async deleteScope(prefix) {
|
|
6628
|
-
const ids = await this.listConversationIds();
|
|
6629
|
-
const matching = ids.filter((id) => id.startsWith(prefix));
|
|
6630
|
-
for (const id of matching) await this.deleteConversation(id);
|
|
6631
|
-
return matching.length;
|
|
6822
|
+
// src/internal/runtime/hooks/hooks-executor.ts
|
|
6823
|
+
init_hooks_source();
|
|
6824
|
+
var HooksExecutor = class {
|
|
6825
|
+
constructor(cwd) {
|
|
6826
|
+
this.cwd = cwd;
|
|
6632
6827
|
}
|
|
6633
|
-
|
|
6634
|
-
|
|
6635
|
-
|
|
6636
|
-
|
|
6637
|
-
|
|
6638
|
-
|
|
6639
|
-
if (cause.code === "ENOENT") return [];
|
|
6640
|
-
throw cause;
|
|
6828
|
+
cwd;
|
|
6829
|
+
config = {};
|
|
6830
|
+
async initialize(settingSourcesIncludeProject) {
|
|
6831
|
+
if (!settingSourcesIncludeProject) {
|
|
6832
|
+
this.config = {};
|
|
6833
|
+
return;
|
|
6641
6834
|
}
|
|
6642
|
-
|
|
6643
|
-
return entries;
|
|
6644
|
-
}
|
|
6645
|
-
async compact(conversationId, maxTurns) {
|
|
6646
|
-
await compactSessionFile(this.#root, conversationId, maxTurns);
|
|
6647
|
-
}
|
|
6648
|
-
// SE4 — session metadata persisted as a per-conversation sidecar
|
|
6649
|
-
// `<root>/.theokit/agents/<safeId>/session.json` (same sanitized perimeter as
|
|
6650
|
-
// the transcript). Kept separate from messages.jsonl so a title/tag write does
|
|
6651
|
-
// not rewrite the append-only log.
|
|
6652
|
-
#metaPath(conversationId) {
|
|
6653
|
-
const safe2 = sanitizeIdentifier(conversationId, { maxLen: 128 });
|
|
6654
|
-
return safePathJoin(this.#root, ".theokit", "agents", safe2, "session.json");
|
|
6835
|
+
this.config = await loadHookConfig(this.cwd);
|
|
6655
6836
|
}
|
|
6656
|
-
|
|
6657
|
-
|
|
6658
|
-
|
|
6659
|
-
|
|
6660
|
-
|
|
6661
|
-
|
|
6662
|
-
|
|
6837
|
+
/** Fire every hook registered for `event` and aggregate the decisions. */
|
|
6838
|
+
async run(payload) {
|
|
6839
|
+
const commands = this.commandsFor(payload.event, payload.tool);
|
|
6840
|
+
if (commands.length === 0) return { decisions: [], blocked: false };
|
|
6841
|
+
const decisions = [];
|
|
6842
|
+
for (const command of commands) {
|
|
6843
|
+
const decision = await this.executeOne(command, payload);
|
|
6844
|
+
decisions.push(decision);
|
|
6845
|
+
if (decision.decision === "deny") {
|
|
6846
|
+
const result = {
|
|
6847
|
+
decisions,
|
|
6848
|
+
blocked: true
|
|
6849
|
+
};
|
|
6850
|
+
if (decision.reason !== void 0) result.reason = decision.reason;
|
|
6851
|
+
return result;
|
|
6852
|
+
}
|
|
6663
6853
|
}
|
|
6854
|
+
return { decisions, blocked: false };
|
|
6664
6855
|
}
|
|
6665
|
-
|
|
6666
|
-
const
|
|
6667
|
-
|
|
6668
|
-
|
|
6669
|
-
|
|
6856
|
+
commandsFor(event, tool) {
|
|
6857
|
+
const list = this.config.hooks?.[event] ?? [];
|
|
6858
|
+
if (tool === void 0) return list;
|
|
6859
|
+
return list.filter((entry) => {
|
|
6860
|
+
if (entry.matcher === void 0) return true;
|
|
6670
6861
|
try {
|
|
6671
|
-
|
|
6672
|
-
} catch
|
|
6673
|
-
|
|
6862
|
+
return new RegExp(entry.matcher).test(tool);
|
|
6863
|
+
} catch {
|
|
6864
|
+
return entry.matcher === tool;
|
|
6674
6865
|
}
|
|
6675
|
-
const next = applyMetaPatch(current, patch);
|
|
6676
|
-
await promises.writeFile(metaPath, redactSecrets(JSON.stringify(next)), "utf8");
|
|
6677
6866
|
});
|
|
6678
6867
|
}
|
|
6679
|
-
async
|
|
6868
|
+
async executeOne(command, payload) {
|
|
6869
|
+
const timeoutMs = command.timeoutMs ?? 3e4;
|
|
6870
|
+
const result = await spawnAndCollect({
|
|
6871
|
+
command: "sh",
|
|
6872
|
+
args: ["-c", command.command],
|
|
6873
|
+
cwd: this.cwd,
|
|
6874
|
+
timeoutMs,
|
|
6875
|
+
stdin: JSON.stringify(payload)
|
|
6876
|
+
});
|
|
6877
|
+
if (result.timedOut) {
|
|
6878
|
+
return { decision: "deny", reason: `Hook timed out after ${timeoutMs}ms` };
|
|
6879
|
+
}
|
|
6880
|
+
if (result.spawnError !== void 0) {
|
|
6881
|
+
return { decision: "deny", reason: `Hook spawn failed: ${result.spawnError.message}` };
|
|
6882
|
+
}
|
|
6883
|
+
if (result.exitCode !== 0) {
|
|
6884
|
+
return {
|
|
6885
|
+
decision: "deny",
|
|
6886
|
+
reason: result.stderr.trim().length > 0 ? result.stderr.trim() : `Hook exited with code ${result.exitCode}`
|
|
6887
|
+
};
|
|
6888
|
+
}
|
|
6889
|
+
return parseDecisionFromStdout(result.stdout);
|
|
6680
6890
|
}
|
|
6681
6891
|
};
|
|
6682
|
-
function
|
|
6683
|
-
|
|
6684
|
-
|
|
6685
|
-
|
|
6686
|
-
|
|
6687
|
-
|
|
6892
|
+
function parseDecisionFromStdout(stdout) {
|
|
6893
|
+
const trimmed = stdout.trim();
|
|
6894
|
+
if (trimmed.length === 0) return { decision: "allow" };
|
|
6895
|
+
try {
|
|
6896
|
+
const parsed = JSON.parse(trimmed);
|
|
6897
|
+
if (parsed.decision === "deny" || parsed.decision === "feedback") {
|
|
6898
|
+
const result = { decision: parsed.decision };
|
|
6899
|
+
if (parsed.reason !== void 0) result.reason = parsed.reason;
|
|
6900
|
+
if (parsed.feedback !== void 0) result.feedback = parsed.feedback;
|
|
6901
|
+
return result;
|
|
6902
|
+
}
|
|
6903
|
+
if (parsed.decision === "allow") return { decision: "allow" };
|
|
6904
|
+
} catch {
|
|
6905
|
+
return { decision: "feedback", feedback: trimmed };
|
|
6906
|
+
}
|
|
6907
|
+
return { decision: "allow" };
|
|
6688
6908
|
}
|
|
6689
|
-
|
|
6690
|
-
|
|
6909
|
+
|
|
6910
|
+
// src/internal/memory/storage/session-summary-writer.ts
|
|
6911
|
+
init_atomic_write();
|
|
6912
|
+
init_path_guard();
|
|
6913
|
+
var MAX_TURN_CHARS = 2e3;
|
|
6914
|
+
function sessionsDir(cwd) {
|
|
6915
|
+
return path.join(memoryDir(cwd), "sessions");
|
|
6916
|
+
}
|
|
6917
|
+
function sessionSummaryPath(cwd, runId) {
|
|
6918
|
+
return path.join(sessionsDir(cwd), `${safeFilenameForId(runId, { maxLen: 128 })}.md`);
|
|
6919
|
+
}
|
|
6920
|
+
function truncate(text) {
|
|
6921
|
+
if (text.length <= MAX_TURN_CHARS) return text;
|
|
6922
|
+
return `${text.slice(0, MAX_TURN_CHARS)}\u2026`;
|
|
6923
|
+
}
|
|
6924
|
+
async function writeSessionSummary(input) {
|
|
6925
|
+
if (input.status !== "finished") return;
|
|
6926
|
+
const path = sessionSummaryPath(input.cwd, input.runId);
|
|
6927
|
+
await promises.mkdir(sessionsDir(input.cwd), { recursive: true });
|
|
6928
|
+
const safeUser = redactSecrets(truncate(input.userText));
|
|
6929
|
+
const safeAssistant = redactSecrets(truncate(input.assistantText));
|
|
6930
|
+
const iso = new Date(input.at).toISOString();
|
|
6931
|
+
const body = [
|
|
6932
|
+
"---",
|
|
6933
|
+
`runId: ${input.runId}`,
|
|
6934
|
+
`agentId: ${input.agentId}`,
|
|
6935
|
+
`at: ${iso}`,
|
|
6936
|
+
`status: ${input.status}`,
|
|
6937
|
+
"---",
|
|
6938
|
+
"",
|
|
6939
|
+
"## User",
|
|
6940
|
+
"",
|
|
6941
|
+
safeUser,
|
|
6942
|
+
"",
|
|
6943
|
+
"## Assistant",
|
|
6944
|
+
"",
|
|
6945
|
+
safeAssistant,
|
|
6946
|
+
""
|
|
6947
|
+
].join("\n");
|
|
6948
|
+
await replaceFileAtomic(path, body);
|
|
6949
|
+
}
|
|
6950
|
+
|
|
6951
|
+
// src/internal/runtime/memory/memory-path-selector.ts
|
|
6952
|
+
var PORT_MEMORY_PATH_ENV_VAR = "THEOKIT_PORT_MEMORY_PATH";
|
|
6953
|
+
function shouldUsePortMemoryPath() {
|
|
6954
|
+
const env = globalThis.process?.env;
|
|
6955
|
+
if (env === void 0) return false;
|
|
6956
|
+
const val = env[PORT_MEMORY_PATH_ENV_VAR];
|
|
6957
|
+
return val === "1" || val === "true";
|
|
6958
|
+
}
|
|
6959
|
+
function resolveMemoryProviderForLoop(consumerSupplied, defaultAdapter, portPathEnabled) {
|
|
6960
|
+
if (consumerSupplied !== void 0) return consumerSupplied;
|
|
6961
|
+
if (portPathEnabled) return defaultAdapter;
|
|
6962
|
+
return void 0;
|
|
6963
|
+
}
|
|
6964
|
+
function resolveMemoryToolsForLoop(legacyTools, portPathEnabled) {
|
|
6965
|
+
if (portPathEnabled) return void 0;
|
|
6966
|
+
return legacyTools;
|
|
6967
|
+
}
|
|
6968
|
+
function resolveActiveMemorySummaryForSend(legacySummary, portPathEnabled) {
|
|
6969
|
+
if (portPathEnabled) return void 0;
|
|
6970
|
+
return legacySummary;
|
|
6691
6971
|
}
|
|
6692
6972
|
|
|
6693
6973
|
// src/internal/runtime/session/agent-session.ts
|
|
6974
|
+
init_conversation_storage_fs();
|
|
6975
|
+
init_agent_session_store();
|
|
6694
6976
|
var DEFAULT_MAX_TURNS = 200;
|
|
6695
6977
|
var COMPACTION_CHECK_INTERVAL = 50;
|
|
6696
6978
|
var sessions = /* @__PURE__ */ new Map();
|
|
@@ -7130,6 +7412,7 @@ function parseFrontmatterFields(frontmatter) {
|
|
|
7130
7412
|
|
|
7131
7413
|
// src/internal/runtime/skills/discover-skills.ts
|
|
7132
7414
|
init_errors();
|
|
7415
|
+
init_path_guard();
|
|
7133
7416
|
|
|
7134
7417
|
// src/internal/runtime/skills/skill-frontmatter.ts
|
|
7135
7418
|
init_errors();
|
|
@@ -8450,6 +8733,7 @@ function tokenizeContent(content) {
|
|
|
8450
8733
|
// src/internal/runtime/plugins/plugins-manager.ts
|
|
8451
8734
|
init_errors();
|
|
8452
8735
|
init_markdown_config_loader();
|
|
8736
|
+
init_path_guard();
|
|
8453
8737
|
init_hooks_source();
|
|
8454
8738
|
var PluginFrontmatterSchema = zod.z.object({
|
|
8455
8739
|
/** Plugin identifier; defaults to folder name if omitted. */
|
|
@@ -10909,6 +11193,7 @@ function registerBuiltins() {
|
|
|
10909
11193
|
init_errors();
|
|
10910
11194
|
|
|
10911
11195
|
// src/internal/error-mappers/shared.ts
|
|
11196
|
+
init_security();
|
|
10912
11197
|
var RAW_MAX_BYTES = 2048;
|
|
10913
11198
|
function parseRetryAfter(headers) {
|
|
10914
11199
|
if (headers === void 0) return void 0;
|
|
@@ -13040,6 +13325,7 @@ function selectTransport(profile, apiKey) {
|
|
|
13040
13325
|
|
|
13041
13326
|
// src/internal/mcp/client.ts
|
|
13042
13327
|
init_errors();
|
|
13328
|
+
init_path_guard();
|
|
13043
13329
|
function createMcpClient(name, config, fetchImpl = fetch) {
|
|
13044
13330
|
if (isStdio(config)) return new StdioMcpClient(name, config);
|
|
13045
13331
|
return new HttpMcpClient(name, config, fetchImpl);
|
|
@@ -13749,6 +14035,9 @@ async function readProjectMcpServers(cwd) {
|
|
|
13749
14035
|
}
|
|
13750
14036
|
}
|
|
13751
14037
|
|
|
14038
|
+
// src/internal/runtime/local-agent/local-agent.ts
|
|
14039
|
+
init_local_agent_goal_extensions();
|
|
14040
|
+
|
|
13752
14041
|
// src/internal/runtime/local-agent/local-agent-invalidate.ts
|
|
13753
14042
|
async function applyDeferredInvalidation(agentId, pending, refresh) {
|
|
13754
14043
|
process.stderr.write(
|
|
@@ -16079,16 +16368,49 @@ async function localAgentUsePersonality(args) {
|
|
|
16079
16368
|
init_local_agent_plugins();
|
|
16080
16369
|
|
|
16081
16370
|
// src/internal/runtime/local-agent/local-agent-runtime-extensions.ts
|
|
16082
|
-
function
|
|
16371
|
+
function pausedReason(kind, threadId) {
|
|
16372
|
+
switch (kind) {
|
|
16373
|
+
case "none":
|
|
16374
|
+
return `no durable objective set for thread "${threadId}" \u2014 call setObjective() first`;
|
|
16375
|
+
case "inert":
|
|
16376
|
+
return `durable objective for thread "${threadId}" is inert \u2014 no judge resolved (set a judge to activate)`;
|
|
16377
|
+
case "exhausted":
|
|
16378
|
+
return `durable objective for thread "${threadId}" exhausted its run budget \u2014 raise maxRuns to resume`;
|
|
16379
|
+
}
|
|
16380
|
+
}
|
|
16381
|
+
function localAgentRunUntil(agent, goal, options, durable) {
|
|
16083
16382
|
async function* wrap() {
|
|
16084
16383
|
const { runUntilImpl: runUntilImpl2 } = await Promise.resolve().then(() => (init_run_until(), run_until_exports));
|
|
16085
|
-
const
|
|
16086
|
-
|
|
16087
|
-
|
|
16088
|
-
|
|
16089
|
-
|
|
16384
|
+
const buildDeps = async () => {
|
|
16385
|
+
const { judgeCallImpl: judgeCallImpl2 } = await Promise.resolve().then(() => (init_judge_call(), judge_call_exports));
|
|
16386
|
+
const { getAgentFacade: getAgentFacade2 } = await Promise.resolve().then(() => (init_agent_factory_registry(), agent_factory_registry_exports));
|
|
16387
|
+
const create = getAgentFacade2().create;
|
|
16388
|
+
return {
|
|
16389
|
+
judge: (ctx, opts) => judgeCallImpl2(ctx, opts, { create })
|
|
16390
|
+
};
|
|
16090
16391
|
};
|
|
16091
|
-
|
|
16392
|
+
if (goal !== void 0) {
|
|
16393
|
+
return yield* runUntilImpl2(agent, goal, options, await buildDeps());
|
|
16394
|
+
}
|
|
16395
|
+
const threadId = options?.threadId;
|
|
16396
|
+
if (durable === void 0 || threadId === void 0) {
|
|
16397
|
+
const reason = "runUntil() called with no goal and no threadId \u2014 nothing to resolve a durable objective from";
|
|
16398
|
+
yield { type: "status_change", status: "paused", reason };
|
|
16399
|
+
return { status: "paused", turnsUsed: 0, finalResponse: void 0 };
|
|
16400
|
+
}
|
|
16401
|
+
const { resolveDurableRun: resolveDurableRun2, persistDurableProgress: persistDurableProgress2 } = await Promise.resolve().then(() => (init_local_agent_goal_extensions(), local_agent_goal_extensions_exports));
|
|
16402
|
+
const resolved = await resolveDurableRun2(durable.handle, durable.goalConfig, threadId, options);
|
|
16403
|
+
if (resolved.kind !== "run") {
|
|
16404
|
+
yield {
|
|
16405
|
+
type: "status_change",
|
|
16406
|
+
status: "paused",
|
|
16407
|
+
reason: pausedReason(resolved.kind, threadId)
|
|
16408
|
+
};
|
|
16409
|
+
return { status: "paused", turnsUsed: 0, finalResponse: void 0 };
|
|
16410
|
+
}
|
|
16411
|
+
const result = yield* runUntilImpl2(agent, resolved.goal, resolved.options, await buildDeps());
|
|
16412
|
+
await persistDurableProgress2(durable.handle, threadId, result);
|
|
16413
|
+
return result;
|
|
16092
16414
|
}
|
|
16093
16415
|
return wrap();
|
|
16094
16416
|
}
|
|
@@ -16154,6 +16476,44 @@ function ponyfillAny(signals) {
|
|
|
16154
16476
|
return ctrl.signal;
|
|
16155
16477
|
}
|
|
16156
16478
|
|
|
16479
|
+
// src/internal/runtime/lifecycle/wrap-completion-check-run.ts
|
|
16480
|
+
function wrapRunWithCompletionCheck(args) {
|
|
16481
|
+
const check = args.completionCheck;
|
|
16482
|
+
if (check === void 0) return args.run;
|
|
16483
|
+
const compute = async () => {
|
|
16484
|
+
const result = await args.run.wait();
|
|
16485
|
+
if (result.status !== "finished" || result.result === void 0) return result;
|
|
16486
|
+
const judgeOpts = {};
|
|
16487
|
+
if (check.judgeModel !== void 0) judgeOpts.judgeModel = check.judgeModel;
|
|
16488
|
+
if (check.apiKey !== void 0) judgeOpts.apiKey = check.apiKey;
|
|
16489
|
+
const verdict = await args.deps.judge(
|
|
16490
|
+
{ goal: check.criteria, lastResponse: result.result },
|
|
16491
|
+
judgeOpts
|
|
16492
|
+
);
|
|
16493
|
+
const complete = !verdict.parseFailed && verdict.verdict === "done";
|
|
16494
|
+
emitRunEvent(args.onRunEvent, {
|
|
16495
|
+
type: "completion_check",
|
|
16496
|
+
complete,
|
|
16497
|
+
reason: verdict.reason
|
|
16498
|
+
});
|
|
16499
|
+
return {
|
|
16500
|
+
...result,
|
|
16501
|
+
completionCheck: { complete, reason: verdict.reason, parseFailed: verdict.parseFailed }
|
|
16502
|
+
};
|
|
16503
|
+
};
|
|
16504
|
+
let judged;
|
|
16505
|
+
const wrappedWait = () => {
|
|
16506
|
+
judged ??= compute();
|
|
16507
|
+
return judged;
|
|
16508
|
+
};
|
|
16509
|
+
return new Proxy(args.run, {
|
|
16510
|
+
get(target, prop, receiver) {
|
|
16511
|
+
if (prop === "wait") return wrappedWait;
|
|
16512
|
+
return Reflect.get(target, prop, receiver);
|
|
16513
|
+
}
|
|
16514
|
+
});
|
|
16515
|
+
}
|
|
16516
|
+
|
|
16157
16517
|
// src/internal/runtime/processors/run-processors.ts
|
|
16158
16518
|
var ProcessorAbort = class {
|
|
16159
16519
|
constructor(processorId, reason) {
|
|
@@ -16293,6 +16653,9 @@ function wrapRunWithOutputProcessors(args) {
|
|
|
16293
16653
|
});
|
|
16294
16654
|
}
|
|
16295
16655
|
|
|
16656
|
+
// src/internal/runtime/local-agent/local-agent-send.ts
|
|
16657
|
+
init_local_agent_goal_extensions();
|
|
16658
|
+
|
|
16296
16659
|
// src/internal/runtime/local-agent/local-agent-memory-hooks.ts
|
|
16297
16660
|
var DEFAULT_MAX_RECALL_BYTES = 16e3;
|
|
16298
16661
|
async function applyPreUserSendHook(args) {
|
|
@@ -16408,6 +16771,11 @@ async function executeSendLocked(inputs, message, options) {
|
|
|
16408
16771
|
memoryFacts,
|
|
16409
16772
|
activeMemorySummary
|
|
16410
16773
|
);
|
|
16774
|
+
const projectedSystemPrompt = await projectCurrentObjective(
|
|
16775
|
+
inputs.storageHandle,
|
|
16776
|
+
options.objectiveThreadId,
|
|
16777
|
+
assembledSystemPrompt
|
|
16778
|
+
);
|
|
16411
16779
|
const composedOptions = {
|
|
16412
16780
|
...options,
|
|
16413
16781
|
signal: anySignal([options.signal, inputs.lifecycleAbortController.signal])
|
|
@@ -16415,7 +16783,7 @@ async function executeSendLocked(inputs, message, options) {
|
|
|
16415
16783
|
const run = await inputs.dispatchRun(
|
|
16416
16784
|
adaptedMessage,
|
|
16417
16785
|
composedOptions,
|
|
16418
|
-
|
|
16786
|
+
projectedSystemPrompt,
|
|
16419
16787
|
memoryFacts,
|
|
16420
16788
|
priorMessages,
|
|
16421
16789
|
memoryTools,
|
|
@@ -16428,18 +16796,43 @@ async function executeSendLocked(inputs, message, options) {
|
|
|
16428
16796
|
agentId: inputs.agentId,
|
|
16429
16797
|
onRunEvent: options.onRunEvent
|
|
16430
16798
|
}) : run;
|
|
16431
|
-
|
|
16799
|
+
const hookedRun = wrapRunWithPostReplyHook({
|
|
16432
16800
|
pluginManager: inputs.pluginManagerCode,
|
|
16433
16801
|
agentId: inputs.agentId,
|
|
16434
16802
|
options: inputs.options,
|
|
16435
16803
|
run: processedRun,
|
|
16436
16804
|
userText
|
|
16437
16805
|
});
|
|
16806
|
+
return wrapRunWithCompletionCheck({
|
|
16807
|
+
run: hookedRun,
|
|
16808
|
+
completionCheck: options.completionCheck,
|
|
16809
|
+
onRunEvent: options.onRunEvent,
|
|
16810
|
+
deps: buildCompletionCheckDeps()
|
|
16811
|
+
});
|
|
16812
|
+
}
|
|
16813
|
+
function buildCompletionCheckDeps() {
|
|
16814
|
+
return {
|
|
16815
|
+
judge: async (ctx, opts) => {
|
|
16816
|
+
const { judgeCallImpl: judgeCallImpl2 } = await Promise.resolve().then(() => (init_judge_call(), judge_call_exports));
|
|
16817
|
+
const { getAgentFacade: getAgentFacade2 } = await Promise.resolve().then(() => (init_agent_factory_registry(), agent_factory_registry_exports));
|
|
16818
|
+
return judgeCallImpl2(ctx, opts, { create: getAgentFacade2().create });
|
|
16819
|
+
}
|
|
16820
|
+
};
|
|
16438
16821
|
}
|
|
16439
16822
|
function readMemoryForSend(workspaceCwd, memoryConfig) {
|
|
16440
16823
|
if (memoryConfig?.enabled !== true) return Promise.resolve([]);
|
|
16441
16824
|
return safeCall(() => readMemoryFacts(workspaceCwd, memoryConfig), [], "memory read");
|
|
16442
16825
|
}
|
|
16826
|
+
async function projectCurrentObjective(storageHandle, objectiveThreadId, assembled) {
|
|
16827
|
+
if (objectiveThreadId === void 0) return assembled;
|
|
16828
|
+
const objective = await safeCall(
|
|
16829
|
+
() => resolveCurrentObjectiveText(storageHandle, objectiveThreadId),
|
|
16830
|
+
void 0,
|
|
16831
|
+
"objective projection"
|
|
16832
|
+
);
|
|
16833
|
+
if (objective === void 0) return assembled;
|
|
16834
|
+
return formatObjectiveProjection(objective, assembled);
|
|
16835
|
+
}
|
|
16443
16836
|
|
|
16444
16837
|
// src/internal/runtime/local-agent/local-agent-task-wrap.ts
|
|
16445
16838
|
init_registry();
|
|
@@ -16802,20 +17195,33 @@ var LocalAgent = class {
|
|
|
16802
17195
|
...opts !== void 0 ? { opts } : {}
|
|
16803
17196
|
});
|
|
16804
17197
|
}
|
|
17198
|
+
// biome-ignore format: G8 budget — artifact stubs (local agents have no artifacts); kept 1-line each.
|
|
16805
17199
|
listArtifacts() {
|
|
16806
17200
|
return Promise.resolve([]);
|
|
16807
17201
|
}
|
|
17202
|
+
// biome-ignore format: G8 budget — see listArtifacts.
|
|
16808
17203
|
downloadArtifact(_path) {
|
|
16809
|
-
return Promise.reject(
|
|
16810
|
-
new UnsupportedRunOperationError(
|
|
16811
|
-
"Artifacts are not supported for local agents",
|
|
16812
|
-
"downloadArtifact"
|
|
16813
|
-
)
|
|
16814
|
-
);
|
|
17204
|
+
return Promise.reject(new UnsupportedRunOperationError("Artifacts are not supported for local agents", "downloadArtifact"));
|
|
16815
17205
|
}
|
|
16816
17206
|
// biome-ignore format: G8 budget — both methods delegate to `local-agent-runtime-extensions.ts`; signatures kept as 1-line each.
|
|
16817
17207
|
runUntil(goal, options) {
|
|
16818
|
-
return localAgentRunUntil(this, goal, options);
|
|
17208
|
+
return localAgentRunUntil(this, goal, options, { handle: this.storageHandle(), goalConfig: this.options.goal });
|
|
17209
|
+
}
|
|
17210
|
+
// biome-ignore format: SE33 G8 budget — objective methods delegate to `local-agent-goal-extensions.ts`.
|
|
17211
|
+
setObjective(objective, opts) {
|
|
17212
|
+
return localAgentSetObjective(this.storageHandle(), objective, opts);
|
|
17213
|
+
}
|
|
17214
|
+
// biome-ignore format: SE33 G8 budget — see setObjective above.
|
|
17215
|
+
getObjective(opts) {
|
|
17216
|
+
return localAgentGetObjective(this.storageHandle(), opts);
|
|
17217
|
+
}
|
|
17218
|
+
// biome-ignore format: SE33 G8 budget — see setObjective above.
|
|
17219
|
+
updateObjectiveOptions(opts) {
|
|
17220
|
+
return localAgentUpdateObjectiveOptions(this.storageHandle(), opts);
|
|
17221
|
+
}
|
|
17222
|
+
// biome-ignore format: SE33 G8 budget — see setObjective above.
|
|
17223
|
+
clearObjective(opts) {
|
|
17224
|
+
return localAgentClearObjective(this.storageHandle(), opts);
|
|
16819
17225
|
}
|
|
16820
17226
|
// biome-ignore format: G8 budget — see runUntil comment above.
|
|
16821
17227
|
fork(options) {
|
|
@@ -17363,15 +17769,31 @@ setAgentFacade({
|
|
|
17363
17769
|
// src/cron.ts
|
|
17364
17770
|
init_errors();
|
|
17365
17771
|
|
|
17772
|
+
// src/internal/cron/fire-handler.ts
|
|
17773
|
+
init_registry();
|
|
17774
|
+
|
|
17366
17775
|
// src/internal/cron/run-job.ts
|
|
17367
17776
|
init_errors();
|
|
17368
17777
|
init_agent_factory_registry();
|
|
17369
17778
|
async function runCronJob(job) {
|
|
17370
|
-
if (job.
|
|
17371
|
-
if (job.
|
|
17372
|
-
|
|
17373
|
-
|
|
17374
|
-
|
|
17779
|
+
if (job.workflow !== void 0) return job.workflow.run(job.inputData);
|
|
17780
|
+
if (job.agent !== void 0) return runWithEphemeralAgent(job.agent, requireMessage(job));
|
|
17781
|
+
if (job.agentId !== void 0) return runWithExistingAgent(job.agentId, requireMessage(job));
|
|
17782
|
+
throw new ConfigurationError(
|
|
17783
|
+
`Cron job ${job.id} has no target (agent, agentId, or workflow) \u2014 cannot run.`,
|
|
17784
|
+
{ code: "cron_no_target" }
|
|
17785
|
+
);
|
|
17786
|
+
}
|
|
17787
|
+
function isAgentRun(outcome) {
|
|
17788
|
+
return typeof outcome.wait === "function";
|
|
17789
|
+
}
|
|
17790
|
+
function requireMessage(job) {
|
|
17791
|
+
if (job.message === void 0) {
|
|
17792
|
+
throw new ConfigurationError(`Cron job ${job.id} is an agent target but has no message.`, {
|
|
17793
|
+
code: "cron_missing_message"
|
|
17794
|
+
});
|
|
17795
|
+
}
|
|
17796
|
+
return job.message;
|
|
17375
17797
|
}
|
|
17376
17798
|
async function runWithExistingAgent(agentId, message) {
|
|
17377
17799
|
const info = await getAgentFacade().get(agentId).catch(() => void 0);
|
|
@@ -17389,6 +17811,37 @@ async function runWithEphemeralAgent(baseOptions, message) {
|
|
|
17389
17811
|
return agent.send(message);
|
|
17390
17812
|
}
|
|
17391
17813
|
|
|
17814
|
+
// src/internal/cron/fire-handler.ts
|
|
17815
|
+
async function fireCronJobAsTask(job) {
|
|
17816
|
+
const fireTs = Date.now();
|
|
17817
|
+
const taskId = `cron-${job.id}-${fireTs}`;
|
|
17818
|
+
try {
|
|
17819
|
+
await submit({
|
|
17820
|
+
kind: "cron",
|
|
17821
|
+
id: taskId,
|
|
17822
|
+
allowReservedPrefix: true,
|
|
17823
|
+
meta: { jobId: job.id, jobName: job.name, schedule: job.cron, firedAt: fireTs },
|
|
17824
|
+
work: async (ctx) => {
|
|
17825
|
+
const outcome = await runCronJob(job);
|
|
17826
|
+
if (isAgentRun(outcome)) {
|
|
17827
|
+
ctx.signal.addEventListener("abort", () => void outcome.cancel().catch(() => {
|
|
17828
|
+
}), {
|
|
17829
|
+
once: true
|
|
17830
|
+
});
|
|
17831
|
+
const result = await outcome.wait();
|
|
17832
|
+
ctx.emit({ status: result.status, runId: outcome.id });
|
|
17833
|
+
return { status: result.status, runId: outcome.id };
|
|
17834
|
+
}
|
|
17835
|
+
ctx.emit({ status: outcome.status, runId: outcome.id });
|
|
17836
|
+
return { status: outcome.status, runId: outcome.id };
|
|
17837
|
+
}
|
|
17838
|
+
});
|
|
17839
|
+
} catch {
|
|
17840
|
+
const outcome = await runCronJob(job);
|
|
17841
|
+
if (isAgentRun(outcome)) await outcome.wait();
|
|
17842
|
+
}
|
|
17843
|
+
}
|
|
17844
|
+
|
|
17392
17845
|
// src/internal/cron/store.ts
|
|
17393
17846
|
var jobs = /* @__PURE__ */ new Map();
|
|
17394
17847
|
function listJobs() {
|
|
@@ -17579,7 +18032,6 @@ function estimateNextRunAt(_cron, _timezone) {
|
|
|
17579
18032
|
}
|
|
17580
18033
|
|
|
17581
18034
|
// src/cron.ts
|
|
17582
|
-
init_registry();
|
|
17583
18035
|
var Cron = class {
|
|
17584
18036
|
constructor() {
|
|
17585
18037
|
}
|
|
@@ -17646,7 +18098,8 @@ var Cron = class {
|
|
|
17646
18098
|
return updateJobStatus(jobId, false);
|
|
17647
18099
|
}
|
|
17648
18100
|
/**
|
|
17649
|
-
* Manually trigger a cron job off-schedule. Returns the resulting `Run
|
|
18101
|
+
* Manually trigger a cron job off-schedule. Returns the resulting `Run`
|
|
18102
|
+
* (agent target) or `WorkflowRun` (workflow target — SE35).
|
|
17650
18103
|
*
|
|
17651
18104
|
* @public
|
|
17652
18105
|
*/
|
|
@@ -17663,30 +18116,7 @@ var Cron = class {
|
|
|
17663
18116
|
* @public
|
|
17664
18117
|
*/
|
|
17665
18118
|
static start(options = {}) {
|
|
17666
|
-
setCronFireHandler(
|
|
17667
|
-
const fireTs = Date.now();
|
|
17668
|
-
const taskId = `cron-${job.id}-${fireTs}`;
|
|
17669
|
-
try {
|
|
17670
|
-
await submit({
|
|
17671
|
-
kind: "cron",
|
|
17672
|
-
id: taskId,
|
|
17673
|
-
allowReservedPrefix: true,
|
|
17674
|
-
meta: { jobId: job.id, jobName: job.name, schedule: job.cron, firedAt: fireTs },
|
|
17675
|
-
work: async (ctx) => {
|
|
17676
|
-
const run = await runCronJob(job);
|
|
17677
|
-
ctx.signal.addEventListener("abort", () => void run.cancel().catch(() => {
|
|
17678
|
-
}), {
|
|
17679
|
-
once: true
|
|
17680
|
-
});
|
|
17681
|
-
const result = await run.wait();
|
|
17682
|
-
ctx.emit({ status: result.status, runId: run.id });
|
|
17683
|
-
return { status: result.status, runId: run.id };
|
|
17684
|
-
}
|
|
17685
|
-
});
|
|
17686
|
-
} catch {
|
|
17687
|
-
await runCronJob(job).then((run) => run.wait());
|
|
17688
|
-
}
|
|
17689
|
-
});
|
|
18119
|
+
setCronFireHandler(fireCronJobAsTask);
|
|
17690
18120
|
startScheduler(options.cwd);
|
|
17691
18121
|
return Promise.resolve();
|
|
17692
18122
|
}
|
|
@@ -17710,17 +18140,7 @@ var Cron = class {
|
|
|
17710
18140
|
}
|
|
17711
18141
|
};
|
|
17712
18142
|
async function createCronJob(options) {
|
|
17713
|
-
|
|
17714
|
-
throw new ConfigurationError(
|
|
17715
|
-
"agent and agentId are mutually exclusive \u2014 pass either agent (ephemeral) or agentId (reuse).",
|
|
17716
|
-
{ code: "cron_agent_exclusive" }
|
|
17717
|
-
);
|
|
17718
|
-
}
|
|
17719
|
-
if (options.agent === void 0 && options.agentId === void 0) {
|
|
17720
|
-
throw new ConfigurationError("Cron job requires either agent or agentId", {
|
|
17721
|
-
code: "cron_missing_agent"
|
|
17722
|
-
});
|
|
17723
|
-
}
|
|
18143
|
+
validateCronTarget(options);
|
|
17724
18144
|
validateCronExpression(options.cron);
|
|
17725
18145
|
const timezone = options.timezone ?? "UTC";
|
|
17726
18146
|
validateTimezone(timezone);
|
|
@@ -17731,20 +18151,51 @@ async function createCronJob(options) {
|
|
|
17731
18151
|
id: generateCronId(),
|
|
17732
18152
|
cron: options.cron,
|
|
17733
18153
|
timezone,
|
|
17734
|
-
message: options.message,
|
|
17735
18154
|
enabled: options.enabled ?? true,
|
|
17736
18155
|
status: options.enabled === false ? "paused" : "scheduled",
|
|
17737
18156
|
runtime,
|
|
17738
18157
|
createdAt: now,
|
|
17739
18158
|
nextRunAt: estimateNextRunAt(options.cron),
|
|
17740
18159
|
...options.name !== void 0 ? { name: options.name } : {},
|
|
18160
|
+
...options.message !== void 0 ? { message: options.message } : {},
|
|
17741
18161
|
...options.agent !== void 0 ? { agent: options.agent } : {},
|
|
17742
|
-
...options.agentId !== void 0 ? { agentId: options.agentId } : {}
|
|
18162
|
+
...options.agentId !== void 0 ? { agentId: options.agentId } : {},
|
|
18163
|
+
...options.workflow !== void 0 ? { workflow: options.workflow } : {},
|
|
18164
|
+
...options.inputData !== void 0 ? { inputData: options.inputData } : {}
|
|
17743
18165
|
};
|
|
17744
18166
|
upsertJob(job);
|
|
17745
18167
|
return job;
|
|
17746
18168
|
}
|
|
18169
|
+
function validateCronTarget(options) {
|
|
18170
|
+
const targets = [options.agent, options.agentId, options.workflow].filter(
|
|
18171
|
+
(t) => t !== void 0
|
|
18172
|
+
).length;
|
|
18173
|
+
if (targets > 1) {
|
|
18174
|
+
throw new ConfigurationError(
|
|
18175
|
+
"agent, agentId, and workflow are mutually exclusive \u2014 pass exactly one target.",
|
|
18176
|
+
{ code: "cron_ambiguous_target" }
|
|
18177
|
+
);
|
|
18178
|
+
}
|
|
18179
|
+
if (targets === 0) {
|
|
18180
|
+
throw new ConfigurationError(
|
|
18181
|
+
"Cron job requires exactly one target: agent, agentId, or workflow.",
|
|
18182
|
+
{ code: "cron_no_target" }
|
|
18183
|
+
);
|
|
18184
|
+
}
|
|
18185
|
+
if (options.workflow !== void 0 && options.message !== void 0) {
|
|
18186
|
+
throw new ConfigurationError(
|
|
18187
|
+
"A workflow cron target takes inputData, not a message \u2014 remove `message`.",
|
|
18188
|
+
{ code: "cron_workflow_message" }
|
|
18189
|
+
);
|
|
18190
|
+
}
|
|
18191
|
+
if (options.workflow === void 0 && options.message === void 0) {
|
|
18192
|
+
throw new ConfigurationError("An agent cron target requires a message.", {
|
|
18193
|
+
code: "cron_missing_message"
|
|
18194
|
+
});
|
|
18195
|
+
}
|
|
18196
|
+
}
|
|
17747
18197
|
function detectRuntime(options) {
|
|
18198
|
+
if (options.workflow !== void 0) return "local";
|
|
17748
18199
|
if (options.agentId !== void 0) {
|
|
17749
18200
|
return options.agentId.startsWith("bc-") ? "cloud" : "local";
|
|
17750
18201
|
}
|