@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/index.cjs
CHANGED
|
@@ -916,7 +916,7 @@ function safeFilenameForId(id, options) {
|
|
|
916
916
|
code: "invalid_filename_id"
|
|
917
917
|
});
|
|
918
918
|
}
|
|
919
|
-
const maxLen = options?.maxLen;
|
|
919
|
+
const maxLen = options?.maxLen ?? 128;
|
|
920
920
|
const lower = id.toLowerCase();
|
|
921
921
|
if (lower.length <= maxLen && IDENTIFIER_PATTERN.test(lower)) {
|
|
922
922
|
return lower;
|
|
@@ -1113,6 +1113,72 @@ var init_markdown_store = __esm({
|
|
|
1113
1113
|
}
|
|
1114
1114
|
});
|
|
1115
1115
|
|
|
1116
|
+
// src/internal/persistence/file-lock.ts
|
|
1117
|
+
async function getProperLockfile() {
|
|
1118
|
+
if (cached !== void 0) return cached;
|
|
1119
|
+
try {
|
|
1120
|
+
const mod = await import('proper-lockfile');
|
|
1121
|
+
if (!validateLockModule(mod)) {
|
|
1122
|
+
if (!warnedStructural) {
|
|
1123
|
+
warnedStructural = true;
|
|
1124
|
+
process.stderr.write(
|
|
1125
|
+
"[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"
|
|
1126
|
+
);
|
|
1127
|
+
}
|
|
1128
|
+
cached = null;
|
|
1129
|
+
return cached;
|
|
1130
|
+
}
|
|
1131
|
+
cached = mod;
|
|
1132
|
+
} catch {
|
|
1133
|
+
cached = null;
|
|
1134
|
+
}
|
|
1135
|
+
return cached;
|
|
1136
|
+
}
|
|
1137
|
+
function validateLockModule(mod) {
|
|
1138
|
+
if (mod === null || mod === void 0 || typeof mod !== "object") return false;
|
|
1139
|
+
const m = mod;
|
|
1140
|
+
return typeof m.lock === "function" && typeof m.unlock === "function";
|
|
1141
|
+
}
|
|
1142
|
+
async function withFileLock(path, fn, options) {
|
|
1143
|
+
const lib = await getProperLockfile();
|
|
1144
|
+
if (lib === null) {
|
|
1145
|
+
if (!warnedMissing) {
|
|
1146
|
+
warnedMissing = true;
|
|
1147
|
+
process.stderr.write(
|
|
1148
|
+
"[theokit-sdk] proper-lockfile not installed; cross-process file lock unavailable. Install with: pnpm add proper-lockfile\n"
|
|
1149
|
+
);
|
|
1150
|
+
}
|
|
1151
|
+
return withCwdMutex(`file-lock:${path}`, fn);
|
|
1152
|
+
}
|
|
1153
|
+
return withCwdMutex(`file-lock:${path}`, async () => {
|
|
1154
|
+
const release = await lib.lock(path, {
|
|
1155
|
+
// EC-1: companion lockfile, target path may not exist yet.
|
|
1156
|
+
lockfilePath: `${path}.lock`,
|
|
1157
|
+
realpath: false,
|
|
1158
|
+
stale: 3e4,
|
|
1159
|
+
retries: {
|
|
1160
|
+
retries: 5,
|
|
1161
|
+
factor: 1.5,
|
|
1162
|
+
minTimeout: 100,
|
|
1163
|
+
maxTimeout: 5e3
|
|
1164
|
+
}
|
|
1165
|
+
});
|
|
1166
|
+
try {
|
|
1167
|
+
return await fn();
|
|
1168
|
+
} finally {
|
|
1169
|
+
await release();
|
|
1170
|
+
}
|
|
1171
|
+
});
|
|
1172
|
+
}
|
|
1173
|
+
var cached, warnedMissing, warnedStructural;
|
|
1174
|
+
var init_file_lock = __esm({
|
|
1175
|
+
"src/internal/persistence/file-lock.ts"() {
|
|
1176
|
+
init_cwd_mutex();
|
|
1177
|
+
warnedMissing = false;
|
|
1178
|
+
warnedStructural = false;
|
|
1179
|
+
}
|
|
1180
|
+
});
|
|
1181
|
+
|
|
1116
1182
|
// src/internal/runtime/context/yaml-frontmatter.ts
|
|
1117
1183
|
function parseSimpleYaml(text) {
|
|
1118
1184
|
const fields = {};
|
|
@@ -1367,94 +1433,459 @@ var init_session_summary_writer = __esm({
|
|
|
1367
1433
|
MAX_TURN_CHARS = 2e3;
|
|
1368
1434
|
}
|
|
1369
1435
|
});
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
function currentToolWhitelist() {
|
|
1374
|
-
return toolWhitelistStore.getStore();
|
|
1436
|
+
function sessionFilePath(cwd, agentId) {
|
|
1437
|
+
const safe2 = sanitizeIdentifier(agentId, { maxLen: 128 });
|
|
1438
|
+
return safePathJoin(cwd, ".theokit", "agents", safe2, "messages.jsonl");
|
|
1375
1439
|
}
|
|
1376
|
-
function
|
|
1377
|
-
const
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
return
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
};
|
|
1440
|
+
async function readJsonlLines(cwd, agentId) {
|
|
1441
|
+
const path = sessionFilePath(cwd, agentId);
|
|
1442
|
+
try {
|
|
1443
|
+
const raw = await promises.readFile(path, "utf8");
|
|
1444
|
+
return raw.split("\n").filter((line) => line.length > 0);
|
|
1445
|
+
} catch {
|
|
1446
|
+
return [];
|
|
1384
1447
|
}
|
|
1385
|
-
return { allowed: true };
|
|
1386
1448
|
}
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1449
|
+
function warnMalformed(agentId, line) {
|
|
1450
|
+
process.stderr.write(
|
|
1451
|
+
`[theokit-sdk] skipping malformed line in messages.jsonl (${agentId}): ${line.slice(0, 80)}...
|
|
1452
|
+
`
|
|
1453
|
+
);
|
|
1454
|
+
}
|
|
1455
|
+
function hydrateSessionLine(parsed) {
|
|
1456
|
+
if (typeof parsed.text !== "string" || parsed.role === void 0) return void 0;
|
|
1457
|
+
if (parsed.role === "user" || parsed.role === "assistant") {
|
|
1458
|
+
return { role: parsed.role, text: parsed.text };
|
|
1391
1459
|
}
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
function createSemaphore(permits) {
|
|
1396
|
-
if (!Number.isInteger(permits) || permits < 1) {
|
|
1397
|
-
throw new exports.ConfigurationError(
|
|
1398
|
-
`async-semaphore: permits must be a positive integer, got ${permits}`,
|
|
1399
|
-
{ code: "invalid_concurrency" }
|
|
1400
|
-
);
|
|
1460
|
+
if (parsed.role === "tool_call" || parsed.role === "tool_result") {
|
|
1461
|
+
const label = parsed.role === "tool_call" ? "tool call" : "tool result";
|
|
1462
|
+
return { role: "assistant", text: `[${label}] ${parsed.text}` };
|
|
1401
1463
|
}
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1464
|
+
return void 0;
|
|
1465
|
+
}
|
|
1466
|
+
async function readSessionFile(cwd, agentId) {
|
|
1467
|
+
const lines = await readJsonlLines(cwd, agentId);
|
|
1468
|
+
const messages = [];
|
|
1469
|
+
for (const line of lines) {
|
|
1470
|
+
try {
|
|
1471
|
+
const msg = hydrateSessionLine(JSON.parse(line));
|
|
1472
|
+
if (msg !== void 0) messages.push(msg);
|
|
1473
|
+
} catch {
|
|
1474
|
+
warnMalformed(agentId, line);
|
|
1411
1475
|
}
|
|
1412
1476
|
}
|
|
1413
|
-
return
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
}
|
|
1477
|
+
return messages;
|
|
1478
|
+
}
|
|
1479
|
+
async function readAllPersistedMessages(cwd, agentId) {
|
|
1480
|
+
const lines = await readJsonlLines(cwd, agentId);
|
|
1481
|
+
const messages = [];
|
|
1482
|
+
for (const line of lines) {
|
|
1483
|
+
try {
|
|
1484
|
+
const parsed = JSON.parse(line);
|
|
1485
|
+
if (parsed.role !== void 0 && VALID_ROLES.has(parsed.role) && typeof parsed.text === "string") {
|
|
1486
|
+
messages.push({
|
|
1487
|
+
role: parsed.role,
|
|
1488
|
+
text: parsed.text,
|
|
1489
|
+
at: typeof parsed.at === "number" ? parsed.at : Date.now()
|
|
1490
|
+
});
|
|
1491
|
+
}
|
|
1492
|
+
} catch {
|
|
1493
|
+
warnMalformed(agentId, line);
|
|
1428
1494
|
}
|
|
1495
|
+
}
|
|
1496
|
+
return messages;
|
|
1497
|
+
}
|
|
1498
|
+
async function appendAnyPersistedMessage(cwd, agentId, record) {
|
|
1499
|
+
await appendPersistedMessages(cwd, agentId, [record]);
|
|
1500
|
+
}
|
|
1501
|
+
async function appendPersistedMessages(cwd, agentId, records) {
|
|
1502
|
+
if (records.length === 0) return;
|
|
1503
|
+
const path$1 = sessionFilePath(cwd, agentId);
|
|
1504
|
+
const payload = records.map((r) => `${redactSecrets(JSON.stringify(r))}
|
|
1505
|
+
`).join("");
|
|
1506
|
+
const dir = path.dirname(path$1);
|
|
1507
|
+
let written = false;
|
|
1508
|
+
const attempt = async () => {
|
|
1509
|
+
await promises.mkdir(dir, { recursive: true });
|
|
1510
|
+
await withFileLock(path$1, async () => {
|
|
1511
|
+
await promises.appendFile(path$1, payload, "utf8");
|
|
1512
|
+
written = true;
|
|
1513
|
+
});
|
|
1429
1514
|
};
|
|
1515
|
+
try {
|
|
1516
|
+
await attempt();
|
|
1517
|
+
} catch (cause) {
|
|
1518
|
+
if (written || cause.code !== "ENOENT") throw cause;
|
|
1519
|
+
await attempt();
|
|
1520
|
+
}
|
|
1430
1521
|
}
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1522
|
+
async function rewriteLockedSession(path, transform) {
|
|
1523
|
+
await withFileLock(path, async () => {
|
|
1524
|
+
let raw;
|
|
1525
|
+
try {
|
|
1526
|
+
raw = await promises.readFile(path, "utf8");
|
|
1527
|
+
} catch {
|
|
1528
|
+
return;
|
|
1529
|
+
}
|
|
1530
|
+
const lines = raw.split("\n").filter((line) => line.length > 0);
|
|
1531
|
+
const next = transform(lines);
|
|
1532
|
+
if (next === void 0) return;
|
|
1533
|
+
await replaceFileAtomic(path, next);
|
|
1534
|
+
});
|
|
1535
|
+
}
|
|
1536
|
+
async function compactSessionFile(cwd, agentId, maxTurns) {
|
|
1537
|
+
const path = sessionFilePath(cwd, agentId);
|
|
1538
|
+
if (!fs.existsSync(path)) return;
|
|
1539
|
+
await rewriteLockedSession(
|
|
1540
|
+
path,
|
|
1541
|
+
(lines) => lines.length <= maxTurns * 2 ? void 0 : `${lines.slice(-maxTurns).join("\n")}
|
|
1542
|
+
`
|
|
1543
|
+
);
|
|
1544
|
+
}
|
|
1545
|
+
async function truncateSessionTo(cwd, agentId, keepCount) {
|
|
1546
|
+
const path = sessionFilePath(cwd, agentId);
|
|
1547
|
+
if (!fs.existsSync(path)) return 0;
|
|
1548
|
+
let kept = 0;
|
|
1549
|
+
await rewriteLockedSession(path, (lines) => {
|
|
1550
|
+
const keep = Math.max(0, Math.min(keepCount, lines.length));
|
|
1551
|
+
kept = keep;
|
|
1552
|
+
if (keep === lines.length) return void 0;
|
|
1553
|
+
return keep === 0 ? "" : `${lines.slice(0, keep).join("\n")}
|
|
1554
|
+
`;
|
|
1555
|
+
});
|
|
1556
|
+
return kept;
|
|
1557
|
+
}
|
|
1558
|
+
var VALID_ROLES;
|
|
1559
|
+
var init_agent_session_store = __esm({
|
|
1560
|
+
"src/internal/runtime/session/agent-session-store.ts"() {
|
|
1561
|
+
init_atomic_write();
|
|
1562
|
+
init_file_lock();
|
|
1563
|
+
init_security();
|
|
1564
|
+
VALID_ROLES = /* @__PURE__ */ new Set([
|
|
1565
|
+
"user",
|
|
1566
|
+
"assistant",
|
|
1567
|
+
"system",
|
|
1568
|
+
"tool_call",
|
|
1569
|
+
"tool_result"
|
|
1570
|
+
]);
|
|
1434
1571
|
}
|
|
1435
1572
|
});
|
|
1436
1573
|
|
|
1437
|
-
// src/internal/
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1574
|
+
// src/internal/persistence/objective-coerce.ts
|
|
1575
|
+
function coerceOptions(raw) {
|
|
1576
|
+
if (typeof raw !== "object" || raw === null) return void 0;
|
|
1577
|
+
const o = raw;
|
|
1578
|
+
const out = {};
|
|
1579
|
+
if (typeof o.maxRuns === "number") out.maxRuns = o.maxRuns;
|
|
1580
|
+
if (typeof o.judgeModel === "string") out.judgeModel = o.judgeModel;
|
|
1581
|
+
if (typeof o.prompt === "string") out.prompt = o.prompt;
|
|
1582
|
+
return Object.keys(out).length > 0 ? out : void 0;
|
|
1583
|
+
}
|
|
1584
|
+
function coerceObjectiveRecord(raw) {
|
|
1585
|
+
if (typeof raw !== "object" || raw === null) return void 0;
|
|
1586
|
+
const o = raw;
|
|
1587
|
+
if (o._schemaVersion !== 1) return void 0;
|
|
1588
|
+
if (typeof o.objective !== "string") return void 0;
|
|
1589
|
+
if (typeof o.runsUsed !== "number") return void 0;
|
|
1590
|
+
if (typeof o.status !== "string" || !STATUSES.includes(o.status))
|
|
1591
|
+
return void 0;
|
|
1592
|
+
const options = coerceOptions(o.options);
|
|
1593
|
+
return {
|
|
1594
|
+
_schemaVersion: 1,
|
|
1595
|
+
objective: o.objective,
|
|
1596
|
+
...options !== void 0 ? { options } : {},
|
|
1597
|
+
status: o.status,
|
|
1598
|
+
runsUsed: o.runsUsed
|
|
1599
|
+
};
|
|
1600
|
+
}
|
|
1601
|
+
var STATUSES;
|
|
1602
|
+
var init_objective_coerce = __esm({
|
|
1603
|
+
"src/internal/persistence/objective-coerce.ts"() {
|
|
1604
|
+
STATUSES = ["active", "done", "paused"];
|
|
1450
1605
|
}
|
|
1451
1606
|
});
|
|
1452
1607
|
|
|
1453
|
-
// src/internal/
|
|
1454
|
-
function
|
|
1455
|
-
|
|
1456
|
-
const
|
|
1457
|
-
const
|
|
1608
|
+
// src/internal/persistence/pagination.ts
|
|
1609
|
+
function paginate(items, opts) {
|
|
1610
|
+
if (opts === void 0 || opts.offset === void 0 && opts.limit === void 0) return items;
|
|
1611
|
+
const start = Math.max(0, opts.offset ?? 0);
|
|
1612
|
+
const end = opts.limit === void 0 ? items.length : start + Math.max(0, opts.limit);
|
|
1613
|
+
return items.slice(start, end);
|
|
1614
|
+
}
|
|
1615
|
+
var init_pagination = __esm({
|
|
1616
|
+
"src/internal/persistence/pagination.ts"() {
|
|
1617
|
+
}
|
|
1618
|
+
});
|
|
1619
|
+
|
|
1620
|
+
// src/internal/persistence/session-meta.ts
|
|
1621
|
+
function applyMetaPatch(current, patch) {
|
|
1622
|
+
const next = {};
|
|
1623
|
+
if (current.title !== void 0) next.title = current.title;
|
|
1624
|
+
if (current.tag !== void 0) next.tag = current.tag;
|
|
1625
|
+
if (patch.title === null) delete next.title;
|
|
1626
|
+
else if (patch.title !== void 0) next.title = patch.title;
|
|
1627
|
+
if (patch.tag === null) delete next.tag;
|
|
1628
|
+
else if (patch.tag !== void 0) next.tag = patch.tag;
|
|
1629
|
+
return next;
|
|
1630
|
+
}
|
|
1631
|
+
function coerceSessionMeta(raw) {
|
|
1632
|
+
if (typeof raw !== "object" || raw === null) return void 0;
|
|
1633
|
+
const obj = raw;
|
|
1634
|
+
const meta = {};
|
|
1635
|
+
if (typeof obj.title === "string") meta.title = obj.title;
|
|
1636
|
+
if (typeof obj.tag === "string") meta.tag = obj.tag;
|
|
1637
|
+
return meta.title === void 0 && meta.tag === void 0 ? void 0 : meta;
|
|
1638
|
+
}
|
|
1639
|
+
var init_session_meta = __esm({
|
|
1640
|
+
"src/internal/persistence/session-meta.ts"() {
|
|
1641
|
+
}
|
|
1642
|
+
});
|
|
1643
|
+
function toStoredMessage(record) {
|
|
1644
|
+
return {
|
|
1645
|
+
role: record.role,
|
|
1646
|
+
content: record.text,
|
|
1647
|
+
at: record.at
|
|
1648
|
+
};
|
|
1649
|
+
}
|
|
1650
|
+
function toRecord(message) {
|
|
1651
|
+
return { role: message.role, text: message.content, at: message.at ?? Date.now() };
|
|
1652
|
+
}
|
|
1653
|
+
exports.FileSystemConversationStorage = void 0;
|
|
1654
|
+
var init_conversation_storage_fs = __esm({
|
|
1655
|
+
"src/internal/persistence/conversation-storage-fs.ts"() {
|
|
1656
|
+
init_agent_session_store();
|
|
1657
|
+
init_security();
|
|
1658
|
+
init_path_guard();
|
|
1659
|
+
init_file_lock();
|
|
1660
|
+
init_objective_coerce();
|
|
1661
|
+
init_pagination();
|
|
1662
|
+
init_session_meta();
|
|
1663
|
+
exports.FileSystemConversationStorage = class {
|
|
1664
|
+
#root;
|
|
1665
|
+
constructor(opts = {}) {
|
|
1666
|
+
this.#root = opts.root ?? process.cwd();
|
|
1667
|
+
}
|
|
1668
|
+
/** Exposed for tests + diagnostics. The path is sanitized at use sites. */
|
|
1669
|
+
get root() {
|
|
1670
|
+
return this.#root;
|
|
1671
|
+
}
|
|
1672
|
+
async getMessages(conversationId, opts) {
|
|
1673
|
+
const records = await readAllPersistedMessages(this.#root, conversationId);
|
|
1674
|
+
const all = records.map(toStoredMessage);
|
|
1675
|
+
return paginate(all, opts);
|
|
1676
|
+
}
|
|
1677
|
+
async appendMessage(conversationId, message) {
|
|
1678
|
+
await appendAnyPersistedMessage(this.#root, conversationId, toRecord(message));
|
|
1679
|
+
}
|
|
1680
|
+
async appendMessages(conversationId, messages) {
|
|
1681
|
+
await appendPersistedMessages(this.#root, conversationId, messages.map(toRecord));
|
|
1682
|
+
}
|
|
1683
|
+
async truncateConversation(conversationId, keepCount) {
|
|
1684
|
+
return truncateSessionTo(this.#root, conversationId, keepCount);
|
|
1685
|
+
}
|
|
1686
|
+
async deleteConversation(conversationId) {
|
|
1687
|
+
const safe2 = sanitizeIdentifier(conversationId, { maxLen: 128 });
|
|
1688
|
+
const dirPath = safePathJoin(this.#root, ".theokit", "agents", safe2);
|
|
1689
|
+
await promises.rm(dirPath, { recursive: true, force: true });
|
|
1690
|
+
}
|
|
1691
|
+
async deleteScope(prefix) {
|
|
1692
|
+
const ids = await this.listConversationIds();
|
|
1693
|
+
const matching = ids.filter((id) => id.startsWith(prefix));
|
|
1694
|
+
for (const id of matching) await this.deleteConversation(id);
|
|
1695
|
+
return matching.length;
|
|
1696
|
+
}
|
|
1697
|
+
async listConversationIds(opts = {}) {
|
|
1698
|
+
const agentsRoot = safePathJoin(this.#root, ".theokit", "agents");
|
|
1699
|
+
let entries;
|
|
1700
|
+
try {
|
|
1701
|
+
entries = await promises.readdir(agentsRoot);
|
|
1702
|
+
} catch (cause) {
|
|
1703
|
+
if (cause.code === "ENOENT") return [];
|
|
1704
|
+
throw cause;
|
|
1705
|
+
}
|
|
1706
|
+
if (opts.limit !== void 0) return entries.slice(0, opts.limit);
|
|
1707
|
+
return entries;
|
|
1708
|
+
}
|
|
1709
|
+
async compact(conversationId, maxTurns) {
|
|
1710
|
+
await compactSessionFile(this.#root, conversationId, maxTurns);
|
|
1711
|
+
}
|
|
1712
|
+
// SE4 — session metadata persisted as a per-conversation sidecar
|
|
1713
|
+
// `<root>/.theokit/agents/<safeId>/session.json` (same sanitized perimeter as
|
|
1714
|
+
// the transcript). Kept separate from messages.jsonl so a title/tag write does
|
|
1715
|
+
// not rewrite the append-only log.
|
|
1716
|
+
#metaPath(conversationId) {
|
|
1717
|
+
const safe2 = sanitizeIdentifier(conversationId, { maxLen: 128 });
|
|
1718
|
+
return safePathJoin(this.#root, ".theokit", "agents", safe2, "session.json");
|
|
1719
|
+
}
|
|
1720
|
+
async getSessionMeta(conversationId) {
|
|
1721
|
+
try {
|
|
1722
|
+
const raw = await promises.readFile(this.#metaPath(conversationId), "utf8");
|
|
1723
|
+
return coerceSessionMeta(JSON.parse(raw));
|
|
1724
|
+
} catch (cause) {
|
|
1725
|
+
if (cause.code === "ENOENT") return void 0;
|
|
1726
|
+
throw cause;
|
|
1727
|
+
}
|
|
1728
|
+
}
|
|
1729
|
+
async setSessionMeta(conversationId, patch) {
|
|
1730
|
+
const metaPath = this.#metaPath(conversationId);
|
|
1731
|
+
await promises.mkdir(path.dirname(metaPath), { recursive: true });
|
|
1732
|
+
await withFileLock(metaPath, async () => {
|
|
1733
|
+
let current = {};
|
|
1734
|
+
try {
|
|
1735
|
+
current = coerceSessionMeta(JSON.parse(await promises.readFile(metaPath, "utf8"))) ?? {};
|
|
1736
|
+
} catch (cause) {
|
|
1737
|
+
if (cause.code !== "ENOENT") throw cause;
|
|
1738
|
+
}
|
|
1739
|
+
const next = applyMetaPatch(current, patch);
|
|
1740
|
+
await promises.writeFile(metaPath, redactSecrets(JSON.stringify(next)), "utf8");
|
|
1741
|
+
});
|
|
1742
|
+
}
|
|
1743
|
+
// SE33 — the durable objective is kept in `objective.json` beside the
|
|
1744
|
+
// transcript (separate from messages.jsonl + session.json). Uses the TOTAL
|
|
1745
|
+
// `safeFilenameForId` (not `sanitizeIdentifier`) so a caller-supplied
|
|
1746
|
+
// `threadId` with exotic characters (e.g. "user@example.com") hashes to a
|
|
1747
|
+
// deterministic dir instead of throwing — honoring the objective methods'
|
|
1748
|
+
// never-throw contract (ADR D6). Conforming ids pass through unchanged, so
|
|
1749
|
+
// the objective still sits beside the transcript for the normal case.
|
|
1750
|
+
#objectivePath(conversationId) {
|
|
1751
|
+
const safe2 = safeFilenameForId(conversationId, { maxLen: 128 });
|
|
1752
|
+
return safePathJoin(this.#root, ".theokit", "agents", safe2, "objective.json");
|
|
1753
|
+
}
|
|
1754
|
+
async getObjectiveRecord(conversationId) {
|
|
1755
|
+
try {
|
|
1756
|
+
const raw = await promises.readFile(this.#objectivePath(conversationId), "utf8");
|
|
1757
|
+
return coerceObjectiveRecord(JSON.parse(raw));
|
|
1758
|
+
} catch (cause) {
|
|
1759
|
+
if (cause.code === "ENOENT") return void 0;
|
|
1760
|
+
throw cause;
|
|
1761
|
+
}
|
|
1762
|
+
}
|
|
1763
|
+
async setObjectiveRecord(conversationId, record) {
|
|
1764
|
+
const path$1 = this.#objectivePath(conversationId);
|
|
1765
|
+
if (record === null) {
|
|
1766
|
+
await promises.rm(path$1, { force: true });
|
|
1767
|
+
return;
|
|
1768
|
+
}
|
|
1769
|
+
await promises.mkdir(path.dirname(path$1), { recursive: true });
|
|
1770
|
+
await withFileLock(path$1, async () => {
|
|
1771
|
+
await promises.writeFile(path$1, redactSecrets(JSON.stringify(record)), "utf8");
|
|
1772
|
+
});
|
|
1773
|
+
}
|
|
1774
|
+
// SE33 (HIGH-1 fix) — atomic read-modify-write: the read that feeds `mutate`
|
|
1775
|
+
// happens INSIDE the same file lock as the write, so two concurrent progress
|
|
1776
|
+
// write-backs on one thread cannot both read a stale `runsUsed` and drop turns.
|
|
1777
|
+
async updateObjectiveRecord(conversationId, mutate) {
|
|
1778
|
+
const path$1 = this.#objectivePath(conversationId);
|
|
1779
|
+
await promises.mkdir(path.dirname(path$1), { recursive: true });
|
|
1780
|
+
await withFileLock(path$1, async () => {
|
|
1781
|
+
let current;
|
|
1782
|
+
try {
|
|
1783
|
+
current = coerceObjectiveRecord(JSON.parse(await promises.readFile(path$1, "utf8")));
|
|
1784
|
+
} catch (cause) {
|
|
1785
|
+
if (cause.code !== "ENOENT") throw cause;
|
|
1786
|
+
}
|
|
1787
|
+
const next = mutate(current);
|
|
1788
|
+
if (next === void 0) return;
|
|
1789
|
+
if (next === null) {
|
|
1790
|
+
await promises.rm(path$1, { force: true });
|
|
1791
|
+
return;
|
|
1792
|
+
}
|
|
1793
|
+
await promises.writeFile(path$1, redactSecrets(JSON.stringify(next)), "utf8");
|
|
1794
|
+
});
|
|
1795
|
+
}
|
|
1796
|
+
async dispose() {
|
|
1797
|
+
}
|
|
1798
|
+
};
|
|
1799
|
+
}
|
|
1800
|
+
});
|
|
1801
|
+
async function withToolWhitelist(whitelist, fn) {
|
|
1802
|
+
return toolWhitelistStore.run(whitelist, fn);
|
|
1803
|
+
}
|
|
1804
|
+
function currentToolWhitelist() {
|
|
1805
|
+
return toolWhitelistStore.getStore();
|
|
1806
|
+
}
|
|
1807
|
+
function checkToolWhitelist(toolName) {
|
|
1808
|
+
const whitelist = currentToolWhitelist();
|
|
1809
|
+
if (whitelist === void 0) return { allowed: true };
|
|
1810
|
+
if (!whitelist.has(toolName)) {
|
|
1811
|
+
return {
|
|
1812
|
+
allowed: false,
|
|
1813
|
+
reason: `Tool "${toolName}" not available in this fork context`
|
|
1814
|
+
};
|
|
1815
|
+
}
|
|
1816
|
+
return { allowed: true };
|
|
1817
|
+
}
|
|
1818
|
+
var toolWhitelistStore;
|
|
1819
|
+
var init_async_local_storage = __esm({
|
|
1820
|
+
"src/internal/runtime/concurrency/async-local-storage.ts"() {
|
|
1821
|
+
toolWhitelistStore = new async_hooks.AsyncLocalStorage();
|
|
1822
|
+
}
|
|
1823
|
+
});
|
|
1824
|
+
|
|
1825
|
+
// src/internal/runtime/concurrency/async-semaphore.ts
|
|
1826
|
+
function createSemaphore(permits) {
|
|
1827
|
+
if (!Number.isInteger(permits) || permits < 1) {
|
|
1828
|
+
throw new exports.ConfigurationError(
|
|
1829
|
+
`async-semaphore: permits must be a positive integer, got ${permits}`,
|
|
1830
|
+
{ code: "invalid_concurrency" }
|
|
1831
|
+
);
|
|
1832
|
+
}
|
|
1833
|
+
let active = 0;
|
|
1834
|
+
const queue = [];
|
|
1835
|
+
function tryGrant() {
|
|
1836
|
+
if (active < permits && queue.length > 0) {
|
|
1837
|
+
const resolve3 = queue.shift();
|
|
1838
|
+
if (resolve3 !== void 0) {
|
|
1839
|
+
active += 1;
|
|
1840
|
+
resolve3();
|
|
1841
|
+
}
|
|
1842
|
+
}
|
|
1843
|
+
}
|
|
1844
|
+
return {
|
|
1845
|
+
inFlight: () => active,
|
|
1846
|
+
pending: () => queue.length + active,
|
|
1847
|
+
async acquire() {
|
|
1848
|
+
await new Promise((resolve3) => {
|
|
1849
|
+
queue.push(resolve3);
|
|
1850
|
+
tryGrant();
|
|
1851
|
+
});
|
|
1852
|
+
let released = false;
|
|
1853
|
+
return () => {
|
|
1854
|
+
if (released) return;
|
|
1855
|
+
released = true;
|
|
1856
|
+
active -= 1;
|
|
1857
|
+
tryGrant();
|
|
1858
|
+
};
|
|
1859
|
+
}
|
|
1860
|
+
};
|
|
1861
|
+
}
|
|
1862
|
+
var init_async_semaphore = __esm({
|
|
1863
|
+
"src/internal/runtime/concurrency/async-semaphore.ts"() {
|
|
1864
|
+
init_errors();
|
|
1865
|
+
}
|
|
1866
|
+
});
|
|
1867
|
+
|
|
1868
|
+
// src/internal/llm/credential-pool-types.ts
|
|
1869
|
+
var COOLDOWN_MS, DEFAULT_COOLDOWN_MS;
|
|
1870
|
+
var init_credential_pool_types = __esm({
|
|
1871
|
+
"src/internal/llm/credential-pool-types.ts"() {
|
|
1872
|
+
COOLDOWN_MS = {
|
|
1873
|
+
401: 5 * 60 * 1e3,
|
|
1874
|
+
// 5 minutes — OAuth refresh can recover quickly
|
|
1875
|
+
402: 60 * 60 * 1e3,
|
|
1876
|
+
// 1 hour — billing quota typically hourly+
|
|
1877
|
+
429: 60 * 60 * 1e3
|
|
1878
|
+
// 1 hour — daily-rate-limit windows
|
|
1879
|
+
};
|
|
1880
|
+
DEFAULT_COOLDOWN_MS = 60 * 60 * 1e3;
|
|
1881
|
+
}
|
|
1882
|
+
});
|
|
1883
|
+
|
|
1884
|
+
// src/internal/llm/retry.ts
|
|
1885
|
+
function computeBackoffMs(opts) {
|
|
1886
|
+
const base = opts.baseMs ?? DEFAULT_BASE_MS;
|
|
1887
|
+
const cap2 = opts.capMs ?? DEFAULT_CAP_MS;
|
|
1888
|
+
const rng = opts.rng ?? Math.random;
|
|
1458
1889
|
if (opts.retryAfterMs !== void 0 && opts.retryAfterMs >= 0) {
|
|
1459
1890
|
return Math.max(base, Math.min(cap2, opts.retryAfterMs));
|
|
1460
1891
|
}
|
|
@@ -1740,6 +2171,151 @@ var init_credential_pool_context = __esm({
|
|
|
1740
2171
|
}
|
|
1741
2172
|
});
|
|
1742
2173
|
|
|
2174
|
+
// src/internal/runtime/objective/objective-store.ts
|
|
2175
|
+
function canPersist(a) {
|
|
2176
|
+
return typeof a.getObjectiveRecord === "function" && typeof a.setObjectiveRecord === "function";
|
|
2177
|
+
}
|
|
2178
|
+
async function mutateObjective(adapter, conversationId, mutate) {
|
|
2179
|
+
if (typeof adapter.updateObjectiveRecord === "function") {
|
|
2180
|
+
await adapter.updateObjectiveRecord(conversationId, mutate);
|
|
2181
|
+
return;
|
|
2182
|
+
}
|
|
2183
|
+
const current = await adapter.getObjectiveRecord(conversationId);
|
|
2184
|
+
const next = mutate(current);
|
|
2185
|
+
if (next === void 0) return;
|
|
2186
|
+
await adapter.setObjectiveRecord(conversationId, next);
|
|
2187
|
+
}
|
|
2188
|
+
async function getObjective(adapter, conversationId) {
|
|
2189
|
+
if (!canPersist(adapter)) return void 0;
|
|
2190
|
+
return adapter.getObjectiveRecord(conversationId);
|
|
2191
|
+
}
|
|
2192
|
+
async function setObjective(adapter, conversationId, objective, options) {
|
|
2193
|
+
if (!canPersist(adapter)) return;
|
|
2194
|
+
const record = {
|
|
2195
|
+
_schemaVersion: 1,
|
|
2196
|
+
objective,
|
|
2197
|
+
...options !== void 0 ? { options } : {},
|
|
2198
|
+
status: "active",
|
|
2199
|
+
runsUsed: 0
|
|
2200
|
+
};
|
|
2201
|
+
await adapter.setObjectiveRecord(conversationId, record);
|
|
2202
|
+
}
|
|
2203
|
+
async function updateObjectiveOptions(adapter, conversationId, patch) {
|
|
2204
|
+
if (!canPersist(adapter)) return;
|
|
2205
|
+
await mutateObjective(
|
|
2206
|
+
adapter,
|
|
2207
|
+
conversationId,
|
|
2208
|
+
(current) => current === void 0 ? void 0 : { ...current, options: { ...current.options, ...patch } }
|
|
2209
|
+
);
|
|
2210
|
+
}
|
|
2211
|
+
async function writeObjectiveProgress(adapter, conversationId, progress) {
|
|
2212
|
+
if (!canPersist(adapter)) return;
|
|
2213
|
+
await mutateObjective(
|
|
2214
|
+
adapter,
|
|
2215
|
+
conversationId,
|
|
2216
|
+
(current) => current === void 0 ? void 0 : { ...current, runsUsed: progress.runsUsed, status: progress.status }
|
|
2217
|
+
);
|
|
2218
|
+
}
|
|
2219
|
+
async function clearObjective(adapter, conversationId) {
|
|
2220
|
+
if (!canPersist(adapter)) return;
|
|
2221
|
+
await adapter.setObjectiveRecord(conversationId, null);
|
|
2222
|
+
}
|
|
2223
|
+
var init_objective_store = __esm({
|
|
2224
|
+
"src/internal/runtime/objective/objective-store.ts"() {
|
|
2225
|
+
}
|
|
2226
|
+
});
|
|
2227
|
+
|
|
2228
|
+
// src/internal/runtime/local-agent/local-agent-goal-extensions.ts
|
|
2229
|
+
var local_agent_goal_extensions_exports = {};
|
|
2230
|
+
__export(local_agent_goal_extensions_exports, {
|
|
2231
|
+
formatObjectiveProjection: () => formatObjectiveProjection,
|
|
2232
|
+
localAgentClearObjective: () => localAgentClearObjective,
|
|
2233
|
+
localAgentGetObjective: () => localAgentGetObjective,
|
|
2234
|
+
localAgentSetObjective: () => localAgentSetObjective,
|
|
2235
|
+
localAgentUpdateObjectiveOptions: () => localAgentUpdateObjectiveOptions,
|
|
2236
|
+
persistDurableProgress: () => persistDurableProgress,
|
|
2237
|
+
resolveCurrentObjectiveText: () => resolveCurrentObjectiveText,
|
|
2238
|
+
resolveDurableRun: () => resolveDurableRun
|
|
2239
|
+
});
|
|
2240
|
+
function resolveAdapter(handle) {
|
|
2241
|
+
return typeof handle === "string" ? new exports.FileSystemConversationStorage({ root: handle }) : handle;
|
|
2242
|
+
}
|
|
2243
|
+
function assertValidGoalOptions(options) {
|
|
2244
|
+
if (options.maxRuns !== void 0 && (!Number.isInteger(options.maxRuns) || options.maxRuns <= 0)) {
|
|
2245
|
+
throw new exports.ConfigurationError(`maxRuns must be a positive integer, got ${options.maxRuns}`, {
|
|
2246
|
+
code: "invalid_objective_max_runs"
|
|
2247
|
+
});
|
|
2248
|
+
}
|
|
2249
|
+
}
|
|
2250
|
+
async function localAgentSetObjective(handle, objective, opts) {
|
|
2251
|
+
const { threadId, ...options } = opts;
|
|
2252
|
+
assertValidGoalOptions(options);
|
|
2253
|
+
await setObjective(
|
|
2254
|
+
resolveAdapter(handle),
|
|
2255
|
+
threadId,
|
|
2256
|
+
objective,
|
|
2257
|
+
Object.keys(options).length > 0 ? options : void 0
|
|
2258
|
+
);
|
|
2259
|
+
}
|
|
2260
|
+
async function localAgentGetObjective(handle, opts) {
|
|
2261
|
+
return getObjective(resolveAdapter(handle), opts.threadId);
|
|
2262
|
+
}
|
|
2263
|
+
async function localAgentUpdateObjectiveOptions(handle, opts) {
|
|
2264
|
+
const { threadId, ...patch } = opts;
|
|
2265
|
+
assertValidGoalOptions(patch);
|
|
2266
|
+
await updateObjectiveOptions(resolveAdapter(handle), threadId, patch);
|
|
2267
|
+
}
|
|
2268
|
+
async function localAgentClearObjective(handle, opts) {
|
|
2269
|
+
await clearObjective(resolveAdapter(handle), opts.threadId);
|
|
2270
|
+
}
|
|
2271
|
+
async function resolveCurrentObjectiveText(handle, threadId) {
|
|
2272
|
+
const record = await getObjective(resolveAdapter(handle), threadId);
|
|
2273
|
+
return record?.status === "active" ? record.objective : void 0;
|
|
2274
|
+
}
|
|
2275
|
+
function formatObjectiveProjection(objective, assembled) {
|
|
2276
|
+
const signal = `<current-objective>
|
|
2277
|
+
${objective}
|
|
2278
|
+
</current-objective>`;
|
|
2279
|
+
return assembled === void 0 || assembled.length === 0 ? signal : `${signal}
|
|
2280
|
+
|
|
2281
|
+
${assembled}`;
|
|
2282
|
+
}
|
|
2283
|
+
async function resolveDurableRun(handle, goalConfig, threadId, callerOptions) {
|
|
2284
|
+
const record = await getObjective(resolveAdapter(handle), threadId);
|
|
2285
|
+
if (record === void 0) return { kind: "none" };
|
|
2286
|
+
const judgeModel = callerOptions?.judgeModel ?? record.options?.judgeModel ?? goalConfig?.judgeModel;
|
|
2287
|
+
if (judgeModel === void 0) return { kind: "inert" };
|
|
2288
|
+
const totalBudget = record.options?.maxRuns ?? goalConfig?.maxRuns ?? DEFAULT_MAX_RUNS;
|
|
2289
|
+
const remaining = Math.max(0, totalBudget - record.runsUsed);
|
|
2290
|
+
if (remaining === 0) return { kind: "exhausted" };
|
|
2291
|
+
const perCall = callerOptions?.maxTurns;
|
|
2292
|
+
const maxTurns = perCall !== void 0 ? Math.min(perCall, remaining) : remaining;
|
|
2293
|
+
return {
|
|
2294
|
+
kind: "run",
|
|
2295
|
+
goal: record.objective,
|
|
2296
|
+
options: { ...callerOptions, maxTurns, judgeModel }
|
|
2297
|
+
};
|
|
2298
|
+
}
|
|
2299
|
+
async function persistDurableProgress(handle, threadId, result) {
|
|
2300
|
+
const adapter = resolveAdapter(handle);
|
|
2301
|
+
const record = await getObjective(adapter, threadId);
|
|
2302
|
+
if (record === void 0) return;
|
|
2303
|
+
const status = result.status === "completed" ? "done" : result.status === "paused" ? "paused" : "active";
|
|
2304
|
+
await writeObjectiveProgress(adapter, threadId, {
|
|
2305
|
+
runsUsed: record.runsUsed + result.turnsUsed,
|
|
2306
|
+
status
|
|
2307
|
+
});
|
|
2308
|
+
}
|
|
2309
|
+
var DEFAULT_MAX_RUNS;
|
|
2310
|
+
var init_local_agent_goal_extensions = __esm({
|
|
2311
|
+
"src/internal/runtime/local-agent/local-agent-goal-extensions.ts"() {
|
|
2312
|
+
init_errors();
|
|
2313
|
+
init_conversation_storage_fs();
|
|
2314
|
+
init_objective_store();
|
|
2315
|
+
DEFAULT_MAX_RUNS = 20;
|
|
2316
|
+
}
|
|
2317
|
+
});
|
|
2318
|
+
|
|
1743
2319
|
// src/internal/memory/embedding-cache.ts
|
|
1744
2320
|
var LruEmbeddingCache, globalEmbeddingCache;
|
|
1745
2321
|
var init_embedding_cache = __esm({
|
|
@@ -7923,68 +8499,7 @@ init_cwd_mutex();
|
|
|
7923
8499
|
|
|
7924
8500
|
// src/internal/personality/store.ts
|
|
7925
8501
|
init_atomic_write();
|
|
7926
|
-
|
|
7927
|
-
// src/internal/persistence/file-lock.ts
|
|
7928
|
-
init_cwd_mutex();
|
|
7929
|
-
var cached;
|
|
7930
|
-
var warnedMissing = false;
|
|
7931
|
-
var warnedStructural = false;
|
|
7932
|
-
async function getProperLockfile() {
|
|
7933
|
-
if (cached !== void 0) return cached;
|
|
7934
|
-
try {
|
|
7935
|
-
const mod = await import('proper-lockfile');
|
|
7936
|
-
if (!validateLockModule(mod)) {
|
|
7937
|
-
if (!warnedStructural) {
|
|
7938
|
-
warnedStructural = true;
|
|
7939
|
-
process.stderr.write(
|
|
7940
|
-
"[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"
|
|
7941
|
-
);
|
|
7942
|
-
}
|
|
7943
|
-
cached = null;
|
|
7944
|
-
return cached;
|
|
7945
|
-
}
|
|
7946
|
-
cached = mod;
|
|
7947
|
-
} catch {
|
|
7948
|
-
cached = null;
|
|
7949
|
-
}
|
|
7950
|
-
return cached;
|
|
7951
|
-
}
|
|
7952
|
-
function validateLockModule(mod) {
|
|
7953
|
-
if (mod === null || mod === void 0 || typeof mod !== "object") return false;
|
|
7954
|
-
const m = mod;
|
|
7955
|
-
return typeof m.lock === "function" && typeof m.unlock === "function";
|
|
7956
|
-
}
|
|
7957
|
-
async function withFileLock(path, fn, options) {
|
|
7958
|
-
const lib = await getProperLockfile();
|
|
7959
|
-
if (lib === null) {
|
|
7960
|
-
if (!warnedMissing) {
|
|
7961
|
-
warnedMissing = true;
|
|
7962
|
-
process.stderr.write(
|
|
7963
|
-
"[theokit-sdk] proper-lockfile not installed; cross-process file lock unavailable. Install with: pnpm add proper-lockfile\n"
|
|
7964
|
-
);
|
|
7965
|
-
}
|
|
7966
|
-
return withCwdMutex(`file-lock:${path}`, fn);
|
|
7967
|
-
}
|
|
7968
|
-
return withCwdMutex(`file-lock:${path}`, async () => {
|
|
7969
|
-
const release = await lib.lock(path, {
|
|
7970
|
-
// EC-1: companion lockfile, target path may not exist yet.
|
|
7971
|
-
lockfilePath: `${path}.lock`,
|
|
7972
|
-
realpath: false,
|
|
7973
|
-
stale: 3e4,
|
|
7974
|
-
retries: {
|
|
7975
|
-
retries: 5,
|
|
7976
|
-
factor: 1.5,
|
|
7977
|
-
minTimeout: 100,
|
|
7978
|
-
maxTimeout: 5e3
|
|
7979
|
-
}
|
|
7980
|
-
});
|
|
7981
|
-
try {
|
|
7982
|
-
return await fn();
|
|
7983
|
-
} finally {
|
|
7984
|
-
await release();
|
|
7985
|
-
}
|
|
7986
|
-
});
|
|
7987
|
-
}
|
|
8502
|
+
init_file_lock();
|
|
7988
8503
|
var THEOKIT_DIR_NAME = ".theokit";
|
|
7989
8504
|
function getTheokitHome(cwd) {
|
|
7990
8505
|
const override = process.env.THEOKIT_HOME?.trim();
|
|
@@ -8885,405 +9400,148 @@ function spawnAndCollect(options) {
|
|
|
8885
9400
|
};
|
|
8886
9401
|
const timer = setTimeout(() => {
|
|
8887
9402
|
timedOut = true;
|
|
8888
|
-
try {
|
|
8889
|
-
child.kill("SIGKILL");
|
|
8890
|
-
} catch {
|
|
8891
|
-
}
|
|
8892
|
-
settle({ stdout, stderr, exitCode: null, timedOut: true });
|
|
8893
|
-
}, timeoutMs);
|
|
8894
|
-
child.stdout?.on("data", (chunk) => {
|
|
8895
|
-
stdout += chunk.toString("utf8");
|
|
8896
|
-
});
|
|
8897
|
-
child.stderr?.on("data", (chunk) => {
|
|
8898
|
-
stderr += chunk.toString("utf8");
|
|
8899
|
-
});
|
|
8900
|
-
child.on("error", (cause) => {
|
|
8901
|
-
clearTimeout(timer);
|
|
8902
|
-
settle({ stdout, stderr, exitCode: -1, timedOut, spawnError: cause });
|
|
8903
|
-
});
|
|
8904
|
-
child.on("close", (code) => {
|
|
8905
|
-
clearTimeout(timer);
|
|
8906
|
-
settle({ stdout, stderr, exitCode: code, timedOut });
|
|
8907
|
-
});
|
|
8908
|
-
if (options.stdin !== void 0 && child.stdin !== null) {
|
|
8909
|
-
child.stdin.end(options.stdin);
|
|
8910
|
-
}
|
|
8911
|
-
});
|
|
8912
|
-
}
|
|
8913
|
-
|
|
8914
|
-
// src/internal/runtime/hooks/hooks-executor.ts
|
|
8915
|
-
init_hooks_source();
|
|
8916
|
-
var HooksExecutor = class {
|
|
8917
|
-
constructor(cwd) {
|
|
8918
|
-
this.cwd = cwd;
|
|
8919
|
-
}
|
|
8920
|
-
cwd;
|
|
8921
|
-
config = {};
|
|
8922
|
-
async initialize(settingSourcesIncludeProject) {
|
|
8923
|
-
if (!settingSourcesIncludeProject) {
|
|
8924
|
-
this.config = {};
|
|
8925
|
-
return;
|
|
8926
|
-
}
|
|
8927
|
-
this.config = await loadHookConfig(this.cwd);
|
|
8928
|
-
}
|
|
8929
|
-
/** Fire every hook registered for `event` and aggregate the decisions. */
|
|
8930
|
-
async run(payload) {
|
|
8931
|
-
const commands = this.commandsFor(payload.event, payload.tool);
|
|
8932
|
-
if (commands.length === 0) return { decisions: [], blocked: false };
|
|
8933
|
-
const decisions = [];
|
|
8934
|
-
for (const command of commands) {
|
|
8935
|
-
const decision = await this.executeOne(command, payload);
|
|
8936
|
-
decisions.push(decision);
|
|
8937
|
-
if (decision.decision === "deny") {
|
|
8938
|
-
const result = {
|
|
8939
|
-
decisions,
|
|
8940
|
-
blocked: true
|
|
8941
|
-
};
|
|
8942
|
-
if (decision.reason !== void 0) result.reason = decision.reason;
|
|
8943
|
-
return result;
|
|
8944
|
-
}
|
|
8945
|
-
}
|
|
8946
|
-
return { decisions, blocked: false };
|
|
8947
|
-
}
|
|
8948
|
-
commandsFor(event, tool) {
|
|
8949
|
-
const list2 = this.config.hooks?.[event] ?? [];
|
|
8950
|
-
if (tool === void 0) return list2;
|
|
8951
|
-
return list2.filter((entry) => {
|
|
8952
|
-
if (entry.matcher === void 0) return true;
|
|
8953
|
-
try {
|
|
8954
|
-
return new RegExp(entry.matcher).test(tool);
|
|
8955
|
-
} catch {
|
|
8956
|
-
return entry.matcher === tool;
|
|
8957
|
-
}
|
|
8958
|
-
});
|
|
8959
|
-
}
|
|
8960
|
-
async executeOne(command, payload) {
|
|
8961
|
-
const timeoutMs = command.timeoutMs ?? 3e4;
|
|
8962
|
-
const result = await spawnAndCollect({
|
|
8963
|
-
command: "sh",
|
|
8964
|
-
args: ["-c", command.command],
|
|
8965
|
-
cwd: this.cwd,
|
|
8966
|
-
timeoutMs,
|
|
8967
|
-
stdin: JSON.stringify(payload)
|
|
8968
|
-
});
|
|
8969
|
-
if (result.timedOut) {
|
|
8970
|
-
return { decision: "deny", reason: `Hook timed out after ${timeoutMs}ms` };
|
|
8971
|
-
}
|
|
8972
|
-
if (result.spawnError !== void 0) {
|
|
8973
|
-
return { decision: "deny", reason: `Hook spawn failed: ${result.spawnError.message}` };
|
|
8974
|
-
}
|
|
8975
|
-
if (result.exitCode !== 0) {
|
|
8976
|
-
return {
|
|
8977
|
-
decision: "deny",
|
|
8978
|
-
reason: result.stderr.trim().length > 0 ? result.stderr.trim() : `Hook exited with code ${result.exitCode}`
|
|
8979
|
-
};
|
|
8980
|
-
}
|
|
8981
|
-
return parseDecisionFromStdout(result.stdout);
|
|
8982
|
-
}
|
|
8983
|
-
};
|
|
8984
|
-
function parseDecisionFromStdout(stdout) {
|
|
8985
|
-
const trimmed = stdout.trim();
|
|
8986
|
-
if (trimmed.length === 0) return { decision: "allow" };
|
|
8987
|
-
try {
|
|
8988
|
-
const parsed = JSON.parse(trimmed);
|
|
8989
|
-
if (parsed.decision === "deny" || parsed.decision === "feedback") {
|
|
8990
|
-
const result = { decision: parsed.decision };
|
|
8991
|
-
if (parsed.reason !== void 0) result.reason = parsed.reason;
|
|
8992
|
-
if (parsed.feedback !== void 0) result.feedback = parsed.feedback;
|
|
8993
|
-
return result;
|
|
8994
|
-
}
|
|
8995
|
-
if (parsed.decision === "allow") return { decision: "allow" };
|
|
8996
|
-
} catch {
|
|
8997
|
-
return { decision: "feedback", feedback: trimmed };
|
|
8998
|
-
}
|
|
8999
|
-
return { decision: "allow" };
|
|
9000
|
-
}
|
|
9001
|
-
|
|
9002
|
-
// src/internal/runtime/lifecycle/post-run-lifecycle.ts
|
|
9003
|
-
init_session_summary_writer();
|
|
9004
|
-
|
|
9005
|
-
// src/internal/runtime/memory/memory-path-selector.ts
|
|
9006
|
-
var PORT_MEMORY_PATH_ENV_VAR = "THEOKIT_PORT_MEMORY_PATH";
|
|
9007
|
-
function shouldUsePortMemoryPath() {
|
|
9008
|
-
const env = globalThis.process?.env;
|
|
9009
|
-
if (env === void 0) return false;
|
|
9010
|
-
const val = env[PORT_MEMORY_PATH_ENV_VAR];
|
|
9011
|
-
return val === "1" || val === "true";
|
|
9012
|
-
}
|
|
9013
|
-
function resolveMemoryProviderForLoop(consumerSupplied, defaultAdapter, portPathEnabled) {
|
|
9014
|
-
if (consumerSupplied !== void 0) return consumerSupplied;
|
|
9015
|
-
if (portPathEnabled) return defaultAdapter;
|
|
9016
|
-
return void 0;
|
|
9017
|
-
}
|
|
9018
|
-
function resolveMemoryToolsForLoop(legacyTools, portPathEnabled) {
|
|
9019
|
-
if (portPathEnabled) return void 0;
|
|
9020
|
-
return legacyTools;
|
|
9021
|
-
}
|
|
9022
|
-
function resolveActiveMemorySummaryForSend(legacySummary, portPathEnabled) {
|
|
9023
|
-
if (portPathEnabled) return void 0;
|
|
9024
|
-
return legacySummary;
|
|
9025
|
-
}
|
|
9026
|
-
|
|
9027
|
-
// src/internal/runtime/session/agent-session-store.ts
|
|
9028
|
-
init_atomic_write();
|
|
9029
|
-
init_security();
|
|
9030
|
-
var VALID_ROLES = /* @__PURE__ */ new Set([
|
|
9031
|
-
"user",
|
|
9032
|
-
"assistant",
|
|
9033
|
-
"system",
|
|
9034
|
-
"tool_call",
|
|
9035
|
-
"tool_result"
|
|
9036
|
-
]);
|
|
9037
|
-
function sessionFilePath(cwd, agentId) {
|
|
9038
|
-
const safe2 = sanitizeIdentifier(agentId, { maxLen: 128 });
|
|
9039
|
-
return safePathJoin(cwd, ".theokit", "agents", safe2, "messages.jsonl");
|
|
9040
|
-
}
|
|
9041
|
-
async function readJsonlLines(cwd, agentId) {
|
|
9042
|
-
const path = sessionFilePath(cwd, agentId);
|
|
9043
|
-
try {
|
|
9044
|
-
const raw = await promises.readFile(path, "utf8");
|
|
9045
|
-
return raw.split("\n").filter((line) => line.length > 0);
|
|
9046
|
-
} catch {
|
|
9047
|
-
return [];
|
|
9048
|
-
}
|
|
9049
|
-
}
|
|
9050
|
-
function warnMalformed(agentId, line) {
|
|
9051
|
-
process.stderr.write(
|
|
9052
|
-
`[theokit-sdk] skipping malformed line in messages.jsonl (${agentId}): ${line.slice(0, 80)}...
|
|
9053
|
-
`
|
|
9054
|
-
);
|
|
9055
|
-
}
|
|
9056
|
-
function hydrateSessionLine(parsed) {
|
|
9057
|
-
if (typeof parsed.text !== "string" || parsed.role === void 0) return void 0;
|
|
9058
|
-
if (parsed.role === "user" || parsed.role === "assistant") {
|
|
9059
|
-
return { role: parsed.role, text: parsed.text };
|
|
9060
|
-
}
|
|
9061
|
-
if (parsed.role === "tool_call" || parsed.role === "tool_result") {
|
|
9062
|
-
const label = parsed.role === "tool_call" ? "tool call" : "tool result";
|
|
9063
|
-
return { role: "assistant", text: `[${label}] ${parsed.text}` };
|
|
9064
|
-
}
|
|
9065
|
-
return void 0;
|
|
9066
|
-
}
|
|
9067
|
-
async function readSessionFile(cwd, agentId) {
|
|
9068
|
-
const lines = await readJsonlLines(cwd, agentId);
|
|
9069
|
-
const messages = [];
|
|
9070
|
-
for (const line of lines) {
|
|
9071
|
-
try {
|
|
9072
|
-
const msg = hydrateSessionLine(JSON.parse(line));
|
|
9073
|
-
if (msg !== void 0) messages.push(msg);
|
|
9074
|
-
} catch {
|
|
9075
|
-
warnMalformed(agentId, line);
|
|
9076
|
-
}
|
|
9077
|
-
}
|
|
9078
|
-
return messages;
|
|
9079
|
-
}
|
|
9080
|
-
async function readAllPersistedMessages(cwd, agentId) {
|
|
9081
|
-
const lines = await readJsonlLines(cwd, agentId);
|
|
9082
|
-
const messages = [];
|
|
9083
|
-
for (const line of lines) {
|
|
9084
|
-
try {
|
|
9085
|
-
const parsed = JSON.parse(line);
|
|
9086
|
-
if (parsed.role !== void 0 && VALID_ROLES.has(parsed.role) && typeof parsed.text === "string") {
|
|
9087
|
-
messages.push({
|
|
9088
|
-
role: parsed.role,
|
|
9089
|
-
text: parsed.text,
|
|
9090
|
-
at: typeof parsed.at === "number" ? parsed.at : Date.now()
|
|
9091
|
-
});
|
|
9092
|
-
}
|
|
9093
|
-
} catch {
|
|
9094
|
-
warnMalformed(agentId, line);
|
|
9095
|
-
}
|
|
9096
|
-
}
|
|
9097
|
-
return messages;
|
|
9098
|
-
}
|
|
9099
|
-
async function appendAnyPersistedMessage(cwd, agentId, record) {
|
|
9100
|
-
await appendPersistedMessages(cwd, agentId, [record]);
|
|
9101
|
-
}
|
|
9102
|
-
async function appendPersistedMessages(cwd, agentId, records) {
|
|
9103
|
-
if (records.length === 0) return;
|
|
9104
|
-
const path$1 = sessionFilePath(cwd, agentId);
|
|
9105
|
-
const payload = records.map((r) => `${redactSecrets(JSON.stringify(r))}
|
|
9106
|
-
`).join("");
|
|
9107
|
-
const dir = path.dirname(path$1);
|
|
9108
|
-
let written = false;
|
|
9109
|
-
const attempt = async () => {
|
|
9110
|
-
await promises.mkdir(dir, { recursive: true });
|
|
9111
|
-
await withFileLock(path$1, async () => {
|
|
9112
|
-
await promises.appendFile(path$1, payload, "utf8");
|
|
9113
|
-
written = true;
|
|
9114
|
-
});
|
|
9115
|
-
};
|
|
9116
|
-
try {
|
|
9117
|
-
await attempt();
|
|
9118
|
-
} catch (cause) {
|
|
9119
|
-
if (written || cause.code !== "ENOENT") throw cause;
|
|
9120
|
-
await attempt();
|
|
9121
|
-
}
|
|
9122
|
-
}
|
|
9123
|
-
async function rewriteLockedSession(path, transform) {
|
|
9124
|
-
await withFileLock(path, async () => {
|
|
9125
|
-
let raw;
|
|
9126
|
-
try {
|
|
9127
|
-
raw = await promises.readFile(path, "utf8");
|
|
9128
|
-
} catch {
|
|
9129
|
-
return;
|
|
9130
|
-
}
|
|
9131
|
-
const lines = raw.split("\n").filter((line) => line.length > 0);
|
|
9132
|
-
const next = transform(lines);
|
|
9133
|
-
if (next === void 0) return;
|
|
9134
|
-
await replaceFileAtomic(path, next);
|
|
9135
|
-
});
|
|
9136
|
-
}
|
|
9137
|
-
async function compactSessionFile(cwd, agentId, maxTurns) {
|
|
9138
|
-
const path = sessionFilePath(cwd, agentId);
|
|
9139
|
-
if (!fs.existsSync(path)) return;
|
|
9140
|
-
await rewriteLockedSession(
|
|
9141
|
-
path,
|
|
9142
|
-
(lines) => lines.length <= maxTurns * 2 ? void 0 : `${lines.slice(-maxTurns).join("\n")}
|
|
9143
|
-
`
|
|
9144
|
-
);
|
|
9145
|
-
}
|
|
9146
|
-
async function truncateSessionTo(cwd, agentId, keepCount) {
|
|
9147
|
-
const path = sessionFilePath(cwd, agentId);
|
|
9148
|
-
if (!fs.existsSync(path)) return 0;
|
|
9149
|
-
let kept = 0;
|
|
9150
|
-
await rewriteLockedSession(path, (lines) => {
|
|
9151
|
-
const keep = Math.max(0, Math.min(keepCount, lines.length));
|
|
9152
|
-
kept = keep;
|
|
9153
|
-
if (keep === lines.length) return void 0;
|
|
9154
|
-
return keep === 0 ? "" : `${lines.slice(0, keep).join("\n")}
|
|
9155
|
-
`;
|
|
9156
|
-
});
|
|
9157
|
-
return kept;
|
|
9158
|
-
}
|
|
9159
|
-
|
|
9160
|
-
// src/internal/persistence/conversation-storage-fs.ts
|
|
9161
|
-
init_security();
|
|
9162
|
-
|
|
9163
|
-
// src/internal/persistence/pagination.ts
|
|
9164
|
-
function paginate(items, opts) {
|
|
9165
|
-
if (opts === void 0 || opts.offset === void 0 && opts.limit === void 0) return items;
|
|
9166
|
-
const start = Math.max(0, opts.offset ?? 0);
|
|
9167
|
-
const end = opts.limit === void 0 ? items.length : start + Math.max(0, opts.limit);
|
|
9168
|
-
return items.slice(start, end);
|
|
9169
|
-
}
|
|
9170
|
-
|
|
9171
|
-
// src/internal/persistence/session-meta.ts
|
|
9172
|
-
function applyMetaPatch(current, patch) {
|
|
9173
|
-
const next = {};
|
|
9174
|
-
if (current.title !== void 0) next.title = current.title;
|
|
9175
|
-
if (current.tag !== void 0) next.tag = current.tag;
|
|
9176
|
-
if (patch.title === null) delete next.title;
|
|
9177
|
-
else if (patch.title !== void 0) next.title = patch.title;
|
|
9178
|
-
if (patch.tag === null) delete next.tag;
|
|
9179
|
-
else if (patch.tag !== void 0) next.tag = patch.tag;
|
|
9180
|
-
return next;
|
|
9181
|
-
}
|
|
9182
|
-
function coerceSessionMeta(raw) {
|
|
9183
|
-
if (typeof raw !== "object" || raw === null) return void 0;
|
|
9184
|
-
const obj = raw;
|
|
9185
|
-
const meta = {};
|
|
9186
|
-
if (typeof obj.title === "string") meta.title = obj.title;
|
|
9187
|
-
if (typeof obj.tag === "string") meta.tag = obj.tag;
|
|
9188
|
-
return meta.title === void 0 && meta.tag === void 0 ? void 0 : meta;
|
|
9403
|
+
try {
|
|
9404
|
+
child.kill("SIGKILL");
|
|
9405
|
+
} catch {
|
|
9406
|
+
}
|
|
9407
|
+
settle({ stdout, stderr, exitCode: null, timedOut: true });
|
|
9408
|
+
}, timeoutMs);
|
|
9409
|
+
child.stdout?.on("data", (chunk) => {
|
|
9410
|
+
stdout += chunk.toString("utf8");
|
|
9411
|
+
});
|
|
9412
|
+
child.stderr?.on("data", (chunk) => {
|
|
9413
|
+
stderr += chunk.toString("utf8");
|
|
9414
|
+
});
|
|
9415
|
+
child.on("error", (cause) => {
|
|
9416
|
+
clearTimeout(timer);
|
|
9417
|
+
settle({ stdout, stderr, exitCode: -1, timedOut, spawnError: cause });
|
|
9418
|
+
});
|
|
9419
|
+
child.on("close", (code) => {
|
|
9420
|
+
clearTimeout(timer);
|
|
9421
|
+
settle({ stdout, stderr, exitCode: code, timedOut });
|
|
9422
|
+
});
|
|
9423
|
+
if (options.stdin !== void 0 && child.stdin !== null) {
|
|
9424
|
+
child.stdin.end(options.stdin);
|
|
9425
|
+
}
|
|
9426
|
+
});
|
|
9189
9427
|
}
|
|
9190
9428
|
|
|
9191
|
-
// src/internal/
|
|
9192
|
-
|
|
9193
|
-
|
|
9194
|
-
constructor(
|
|
9195
|
-
this
|
|
9196
|
-
}
|
|
9197
|
-
/** Exposed for tests + diagnostics. The path is sanitized at use sites. */
|
|
9198
|
-
get root() {
|
|
9199
|
-
return this.#root;
|
|
9200
|
-
}
|
|
9201
|
-
async getMessages(conversationId, opts) {
|
|
9202
|
-
const records = await readAllPersistedMessages(this.#root, conversationId);
|
|
9203
|
-
const all = records.map(toStoredMessage);
|
|
9204
|
-
return paginate(all, opts);
|
|
9205
|
-
}
|
|
9206
|
-
async appendMessage(conversationId, message) {
|
|
9207
|
-
await appendAnyPersistedMessage(this.#root, conversationId, toRecord(message));
|
|
9208
|
-
}
|
|
9209
|
-
async appendMessages(conversationId, messages) {
|
|
9210
|
-
await appendPersistedMessages(this.#root, conversationId, messages.map(toRecord));
|
|
9211
|
-
}
|
|
9212
|
-
async truncateConversation(conversationId, keepCount) {
|
|
9213
|
-
return truncateSessionTo(this.#root, conversationId, keepCount);
|
|
9214
|
-
}
|
|
9215
|
-
async deleteConversation(conversationId) {
|
|
9216
|
-
const safe2 = sanitizeIdentifier(conversationId, { maxLen: 128 });
|
|
9217
|
-
const dirPath = safePathJoin(this.#root, ".theokit", "agents", safe2);
|
|
9218
|
-
await promises.rm(dirPath, { recursive: true, force: true });
|
|
9429
|
+
// src/internal/runtime/hooks/hooks-executor.ts
|
|
9430
|
+
init_hooks_source();
|
|
9431
|
+
var HooksExecutor = class {
|
|
9432
|
+
constructor(cwd) {
|
|
9433
|
+
this.cwd = cwd;
|
|
9219
9434
|
}
|
|
9220
|
-
|
|
9221
|
-
|
|
9222
|
-
|
|
9223
|
-
|
|
9224
|
-
|
|
9225
|
-
|
|
9226
|
-
async listConversationIds(opts = {}) {
|
|
9227
|
-
const agentsRoot = safePathJoin(this.#root, ".theokit", "agents");
|
|
9228
|
-
let entries;
|
|
9229
|
-
try {
|
|
9230
|
-
entries = await promises.readdir(agentsRoot);
|
|
9231
|
-
} catch (cause) {
|
|
9232
|
-
if (cause.code === "ENOENT") return [];
|
|
9233
|
-
throw cause;
|
|
9435
|
+
cwd;
|
|
9436
|
+
config = {};
|
|
9437
|
+
async initialize(settingSourcesIncludeProject) {
|
|
9438
|
+
if (!settingSourcesIncludeProject) {
|
|
9439
|
+
this.config = {};
|
|
9440
|
+
return;
|
|
9234
9441
|
}
|
|
9235
|
-
|
|
9236
|
-
return entries;
|
|
9237
|
-
}
|
|
9238
|
-
async compact(conversationId, maxTurns) {
|
|
9239
|
-
await compactSessionFile(this.#root, conversationId, maxTurns);
|
|
9240
|
-
}
|
|
9241
|
-
// SE4 — session metadata persisted as a per-conversation sidecar
|
|
9242
|
-
// `<root>/.theokit/agents/<safeId>/session.json` (same sanitized perimeter as
|
|
9243
|
-
// the transcript). Kept separate from messages.jsonl so a title/tag write does
|
|
9244
|
-
// not rewrite the append-only log.
|
|
9245
|
-
#metaPath(conversationId) {
|
|
9246
|
-
const safe2 = sanitizeIdentifier(conversationId, { maxLen: 128 });
|
|
9247
|
-
return safePathJoin(this.#root, ".theokit", "agents", safe2, "session.json");
|
|
9442
|
+
this.config = await loadHookConfig(this.cwd);
|
|
9248
9443
|
}
|
|
9249
|
-
|
|
9250
|
-
|
|
9251
|
-
|
|
9252
|
-
|
|
9253
|
-
|
|
9254
|
-
|
|
9255
|
-
|
|
9444
|
+
/** Fire every hook registered for `event` and aggregate the decisions. */
|
|
9445
|
+
async run(payload) {
|
|
9446
|
+
const commands = this.commandsFor(payload.event, payload.tool);
|
|
9447
|
+
if (commands.length === 0) return { decisions: [], blocked: false };
|
|
9448
|
+
const decisions = [];
|
|
9449
|
+
for (const command of commands) {
|
|
9450
|
+
const decision = await this.executeOne(command, payload);
|
|
9451
|
+
decisions.push(decision);
|
|
9452
|
+
if (decision.decision === "deny") {
|
|
9453
|
+
const result = {
|
|
9454
|
+
decisions,
|
|
9455
|
+
blocked: true
|
|
9456
|
+
};
|
|
9457
|
+
if (decision.reason !== void 0) result.reason = decision.reason;
|
|
9458
|
+
return result;
|
|
9459
|
+
}
|
|
9256
9460
|
}
|
|
9461
|
+
return { decisions, blocked: false };
|
|
9257
9462
|
}
|
|
9258
|
-
|
|
9259
|
-
const
|
|
9260
|
-
|
|
9261
|
-
|
|
9262
|
-
|
|
9463
|
+
commandsFor(event, tool) {
|
|
9464
|
+
const list2 = this.config.hooks?.[event] ?? [];
|
|
9465
|
+
if (tool === void 0) return list2;
|
|
9466
|
+
return list2.filter((entry) => {
|
|
9467
|
+
if (entry.matcher === void 0) return true;
|
|
9263
9468
|
try {
|
|
9264
|
-
|
|
9265
|
-
} catch
|
|
9266
|
-
|
|
9469
|
+
return new RegExp(entry.matcher).test(tool);
|
|
9470
|
+
} catch {
|
|
9471
|
+
return entry.matcher === tool;
|
|
9267
9472
|
}
|
|
9268
|
-
const next = applyMetaPatch(current, patch);
|
|
9269
|
-
await promises.writeFile(metaPath, redactSecrets(JSON.stringify(next)), "utf8");
|
|
9270
9473
|
});
|
|
9271
9474
|
}
|
|
9272
|
-
async
|
|
9475
|
+
async executeOne(command, payload) {
|
|
9476
|
+
const timeoutMs = command.timeoutMs ?? 3e4;
|
|
9477
|
+
const result = await spawnAndCollect({
|
|
9478
|
+
command: "sh",
|
|
9479
|
+
args: ["-c", command.command],
|
|
9480
|
+
cwd: this.cwd,
|
|
9481
|
+
timeoutMs,
|
|
9482
|
+
stdin: JSON.stringify(payload)
|
|
9483
|
+
});
|
|
9484
|
+
if (result.timedOut) {
|
|
9485
|
+
return { decision: "deny", reason: `Hook timed out after ${timeoutMs}ms` };
|
|
9486
|
+
}
|
|
9487
|
+
if (result.spawnError !== void 0) {
|
|
9488
|
+
return { decision: "deny", reason: `Hook spawn failed: ${result.spawnError.message}` };
|
|
9489
|
+
}
|
|
9490
|
+
if (result.exitCode !== 0) {
|
|
9491
|
+
return {
|
|
9492
|
+
decision: "deny",
|
|
9493
|
+
reason: result.stderr.trim().length > 0 ? result.stderr.trim() : `Hook exited with code ${result.exitCode}`
|
|
9494
|
+
};
|
|
9495
|
+
}
|
|
9496
|
+
return parseDecisionFromStdout(result.stdout);
|
|
9273
9497
|
}
|
|
9274
9498
|
};
|
|
9275
|
-
function
|
|
9276
|
-
|
|
9277
|
-
|
|
9278
|
-
|
|
9279
|
-
|
|
9280
|
-
|
|
9499
|
+
function parseDecisionFromStdout(stdout) {
|
|
9500
|
+
const trimmed = stdout.trim();
|
|
9501
|
+
if (trimmed.length === 0) return { decision: "allow" };
|
|
9502
|
+
try {
|
|
9503
|
+
const parsed = JSON.parse(trimmed);
|
|
9504
|
+
if (parsed.decision === "deny" || parsed.decision === "feedback") {
|
|
9505
|
+
const result = { decision: parsed.decision };
|
|
9506
|
+
if (parsed.reason !== void 0) result.reason = parsed.reason;
|
|
9507
|
+
if (parsed.feedback !== void 0) result.feedback = parsed.feedback;
|
|
9508
|
+
return result;
|
|
9509
|
+
}
|
|
9510
|
+
if (parsed.decision === "allow") return { decision: "allow" };
|
|
9511
|
+
} catch {
|
|
9512
|
+
return { decision: "feedback", feedback: trimmed };
|
|
9513
|
+
}
|
|
9514
|
+
return { decision: "allow" };
|
|
9281
9515
|
}
|
|
9282
|
-
|
|
9283
|
-
|
|
9516
|
+
|
|
9517
|
+
// src/internal/runtime/lifecycle/post-run-lifecycle.ts
|
|
9518
|
+
init_session_summary_writer();
|
|
9519
|
+
|
|
9520
|
+
// src/internal/runtime/memory/memory-path-selector.ts
|
|
9521
|
+
var PORT_MEMORY_PATH_ENV_VAR = "THEOKIT_PORT_MEMORY_PATH";
|
|
9522
|
+
function shouldUsePortMemoryPath() {
|
|
9523
|
+
const env = globalThis.process?.env;
|
|
9524
|
+
if (env === void 0) return false;
|
|
9525
|
+
const val = env[PORT_MEMORY_PATH_ENV_VAR];
|
|
9526
|
+
return val === "1" || val === "true";
|
|
9527
|
+
}
|
|
9528
|
+
function resolveMemoryProviderForLoop(consumerSupplied, defaultAdapter, portPathEnabled) {
|
|
9529
|
+
if (consumerSupplied !== void 0) return consumerSupplied;
|
|
9530
|
+
if (portPathEnabled) return defaultAdapter;
|
|
9531
|
+
return void 0;
|
|
9532
|
+
}
|
|
9533
|
+
function resolveMemoryToolsForLoop(legacyTools, portPathEnabled) {
|
|
9534
|
+
if (portPathEnabled) return void 0;
|
|
9535
|
+
return legacyTools;
|
|
9536
|
+
}
|
|
9537
|
+
function resolveActiveMemorySummaryForSend(legacySummary, portPathEnabled) {
|
|
9538
|
+
if (portPathEnabled) return void 0;
|
|
9539
|
+
return legacySummary;
|
|
9284
9540
|
}
|
|
9285
9541
|
|
|
9286
9542
|
// src/internal/runtime/session/agent-session.ts
|
|
9543
|
+
init_conversation_storage_fs();
|
|
9544
|
+
init_agent_session_store();
|
|
9287
9545
|
var DEFAULT_MAX_TURNS = 200;
|
|
9288
9546
|
var COMPACTION_CHECK_INTERVAL = 50;
|
|
9289
9547
|
var sessions = /* @__PURE__ */ new Map();
|
|
@@ -9299,7 +9557,7 @@ function resolveStorage(cwdOrStorage) {
|
|
|
9299
9557
|
if (existing !== void 0) {
|
|
9300
9558
|
return { adapter: existing, key: `cwd:${cwdOrStorage}` };
|
|
9301
9559
|
}
|
|
9302
|
-
const fresh = new FileSystemConversationStorage({ root: cwdOrStorage });
|
|
9560
|
+
const fresh = new exports.FileSystemConversationStorage({ root: cwdOrStorage });
|
|
9303
9561
|
fsAdapterByCwd.set(cwdOrStorage, fresh);
|
|
9304
9562
|
return { adapter: fresh, key: `cwd:${cwdOrStorage}` };
|
|
9305
9563
|
}
|
|
@@ -9361,7 +9619,7 @@ async function hydrateSession(agentId, cwdOrStorage) {
|
|
|
9361
9619
|
}
|
|
9362
9620
|
}
|
|
9363
9621
|
async function readPersistedForCache(adapter, agentId) {
|
|
9364
|
-
if (adapter instanceof FileSystemConversationStorage) {
|
|
9622
|
+
if (adapter instanceof exports.FileSystemConversationStorage) {
|
|
9365
9623
|
return readSessionFile(adapter.root, agentId);
|
|
9366
9624
|
}
|
|
9367
9625
|
const records = await adapter.getMessages(agentId);
|
|
@@ -9391,7 +9649,7 @@ async function compactSession(agentId, cwdOrStorage) {
|
|
|
9391
9649
|
const { adapter, key: storageId } = resolveStorage(cwdOrStorage);
|
|
9392
9650
|
const key2 = sessionKey(agentId, storageId);
|
|
9393
9651
|
const chained = (pendingAppends.get(key2) ?? Promise.resolve()).then(async () => {
|
|
9394
|
-
if (adapter instanceof FileSystemConversationStorage) {
|
|
9652
|
+
if (adapter instanceof exports.FileSystemConversationStorage) {
|
|
9395
9653
|
await compactSessionFile(adapter.root, agentId, DEFAULT_MAX_TURNS);
|
|
9396
9654
|
} else if (adapter.compact !== void 0) {
|
|
9397
9655
|
await adapter.compact(agentId, DEFAULT_MAX_TURNS);
|
|
@@ -16360,6 +16618,9 @@ async function readProjectMcpServers(cwd) {
|
|
|
16360
16618
|
}
|
|
16361
16619
|
}
|
|
16362
16620
|
|
|
16621
|
+
// src/internal/runtime/local-agent/local-agent.ts
|
|
16622
|
+
init_local_agent_goal_extensions();
|
|
16623
|
+
|
|
16363
16624
|
// src/internal/runtime/local-agent/local-agent-invalidate.ts
|
|
16364
16625
|
async function applyDeferredInvalidation(agentId, pending, refresh) {
|
|
16365
16626
|
process.stderr.write(
|
|
@@ -17742,16 +18003,49 @@ async function localAgentUsePersonality(args) {
|
|
|
17742
18003
|
init_local_agent_plugins();
|
|
17743
18004
|
|
|
17744
18005
|
// src/internal/runtime/local-agent/local-agent-runtime-extensions.ts
|
|
17745
|
-
function
|
|
18006
|
+
function pausedReason(kind, threadId) {
|
|
18007
|
+
switch (kind) {
|
|
18008
|
+
case "none":
|
|
18009
|
+
return `no durable objective set for thread "${threadId}" \u2014 call setObjective() first`;
|
|
18010
|
+
case "inert":
|
|
18011
|
+
return `durable objective for thread "${threadId}" is inert \u2014 no judge resolved (set a judge to activate)`;
|
|
18012
|
+
case "exhausted":
|
|
18013
|
+
return `durable objective for thread "${threadId}" exhausted its run budget \u2014 raise maxRuns to resume`;
|
|
18014
|
+
}
|
|
18015
|
+
}
|
|
18016
|
+
function localAgentRunUntil(agent, goal, options, durable) {
|
|
17746
18017
|
async function* wrap() {
|
|
17747
18018
|
const { runUntilImpl: runUntilImpl2 } = await Promise.resolve().then(() => (init_run_until(), run_until_exports));
|
|
17748
|
-
const
|
|
17749
|
-
|
|
17750
|
-
|
|
17751
|
-
|
|
17752
|
-
|
|
18019
|
+
const buildDeps = async () => {
|
|
18020
|
+
const { judgeCallImpl: judgeCallImpl2 } = await Promise.resolve().then(() => (init_judge_call(), judge_call_exports));
|
|
18021
|
+
const { getAgentFacade: getAgentFacade2 } = await Promise.resolve().then(() => (init_agent_factory_registry(), agent_factory_registry_exports));
|
|
18022
|
+
const create = getAgentFacade2().create;
|
|
18023
|
+
return {
|
|
18024
|
+
judge: (ctx, opts) => judgeCallImpl2(ctx, opts, { create })
|
|
18025
|
+
};
|
|
17753
18026
|
};
|
|
17754
|
-
|
|
18027
|
+
if (goal !== void 0) {
|
|
18028
|
+
return yield* runUntilImpl2(agent, goal, options, await buildDeps());
|
|
18029
|
+
}
|
|
18030
|
+
const threadId = options?.threadId;
|
|
18031
|
+
if (durable === void 0 || threadId === void 0) {
|
|
18032
|
+
const reason = "runUntil() called with no goal and no threadId \u2014 nothing to resolve a durable objective from";
|
|
18033
|
+
yield { type: "status_change", status: "paused", reason };
|
|
18034
|
+
return { status: "paused", turnsUsed: 0, finalResponse: void 0 };
|
|
18035
|
+
}
|
|
18036
|
+
const { resolveDurableRun: resolveDurableRun2, persistDurableProgress: persistDurableProgress2 } = await Promise.resolve().then(() => (init_local_agent_goal_extensions(), local_agent_goal_extensions_exports));
|
|
18037
|
+
const resolved = await resolveDurableRun2(durable.handle, durable.goalConfig, threadId, options);
|
|
18038
|
+
if (resolved.kind !== "run") {
|
|
18039
|
+
yield {
|
|
18040
|
+
type: "status_change",
|
|
18041
|
+
status: "paused",
|
|
18042
|
+
reason: pausedReason(resolved.kind, threadId)
|
|
18043
|
+
};
|
|
18044
|
+
return { status: "paused", turnsUsed: 0, finalResponse: void 0 };
|
|
18045
|
+
}
|
|
18046
|
+
const result = yield* runUntilImpl2(agent, resolved.goal, resolved.options, await buildDeps());
|
|
18047
|
+
await persistDurableProgress2(durable.handle, threadId, result);
|
|
18048
|
+
return result;
|
|
17755
18049
|
}
|
|
17756
18050
|
return wrap();
|
|
17757
18051
|
}
|
|
@@ -17817,6 +18111,44 @@ function ponyfillAny(signals) {
|
|
|
17817
18111
|
return ctrl.signal;
|
|
17818
18112
|
}
|
|
17819
18113
|
|
|
18114
|
+
// src/internal/runtime/lifecycle/wrap-completion-check-run.ts
|
|
18115
|
+
function wrapRunWithCompletionCheck(args) {
|
|
18116
|
+
const check = args.completionCheck;
|
|
18117
|
+
if (check === void 0) return args.run;
|
|
18118
|
+
const compute = async () => {
|
|
18119
|
+
const result = await args.run.wait();
|
|
18120
|
+
if (result.status !== "finished" || result.result === void 0) return result;
|
|
18121
|
+
const judgeOpts = {};
|
|
18122
|
+
if (check.judgeModel !== void 0) judgeOpts.judgeModel = check.judgeModel;
|
|
18123
|
+
if (check.apiKey !== void 0) judgeOpts.apiKey = check.apiKey;
|
|
18124
|
+
const verdict = await args.deps.judge(
|
|
18125
|
+
{ goal: check.criteria, lastResponse: result.result },
|
|
18126
|
+
judgeOpts
|
|
18127
|
+
);
|
|
18128
|
+
const complete = !verdict.parseFailed && verdict.verdict === "done";
|
|
18129
|
+
emitRunEvent(args.onRunEvent, {
|
|
18130
|
+
type: "completion_check",
|
|
18131
|
+
complete,
|
|
18132
|
+
reason: verdict.reason
|
|
18133
|
+
});
|
|
18134
|
+
return {
|
|
18135
|
+
...result,
|
|
18136
|
+
completionCheck: { complete, reason: verdict.reason, parseFailed: verdict.parseFailed }
|
|
18137
|
+
};
|
|
18138
|
+
};
|
|
18139
|
+
let judged;
|
|
18140
|
+
const wrappedWait = () => {
|
|
18141
|
+
judged ??= compute();
|
|
18142
|
+
return judged;
|
|
18143
|
+
};
|
|
18144
|
+
return new Proxy(args.run, {
|
|
18145
|
+
get(target, prop, receiver) {
|
|
18146
|
+
if (prop === "wait") return wrappedWait;
|
|
18147
|
+
return Reflect.get(target, prop, receiver);
|
|
18148
|
+
}
|
|
18149
|
+
});
|
|
18150
|
+
}
|
|
18151
|
+
|
|
17820
18152
|
// src/internal/runtime/processors/run-processors.ts
|
|
17821
18153
|
var ProcessorAbort = class {
|
|
17822
18154
|
constructor(processorId, reason) {
|
|
@@ -17956,6 +18288,9 @@ function wrapRunWithOutputProcessors(args) {
|
|
|
17956
18288
|
});
|
|
17957
18289
|
}
|
|
17958
18290
|
|
|
18291
|
+
// src/internal/runtime/local-agent/local-agent-send.ts
|
|
18292
|
+
init_local_agent_goal_extensions();
|
|
18293
|
+
|
|
17959
18294
|
// src/internal/runtime/local-agent/local-agent-memory-hooks.ts
|
|
17960
18295
|
var DEFAULT_MAX_RECALL_BYTES = 16e3;
|
|
17961
18296
|
async function applyPreUserSendHook(args) {
|
|
@@ -18071,6 +18406,11 @@ async function executeSendLocked(inputs, message, options) {
|
|
|
18071
18406
|
memoryFacts,
|
|
18072
18407
|
activeMemorySummary
|
|
18073
18408
|
);
|
|
18409
|
+
const projectedSystemPrompt = await projectCurrentObjective(
|
|
18410
|
+
inputs.storageHandle,
|
|
18411
|
+
options.objectiveThreadId,
|
|
18412
|
+
assembledSystemPrompt
|
|
18413
|
+
);
|
|
18074
18414
|
const composedOptions = {
|
|
18075
18415
|
...options,
|
|
18076
18416
|
signal: anySignal([options.signal, inputs.lifecycleAbortController.signal])
|
|
@@ -18078,7 +18418,7 @@ async function executeSendLocked(inputs, message, options) {
|
|
|
18078
18418
|
const run = await inputs.dispatchRun(
|
|
18079
18419
|
adaptedMessage,
|
|
18080
18420
|
composedOptions,
|
|
18081
|
-
|
|
18421
|
+
projectedSystemPrompt,
|
|
18082
18422
|
memoryFacts,
|
|
18083
18423
|
priorMessages,
|
|
18084
18424
|
memoryTools,
|
|
@@ -18091,18 +18431,43 @@ async function executeSendLocked(inputs, message, options) {
|
|
|
18091
18431
|
agentId: inputs.agentId,
|
|
18092
18432
|
onRunEvent: options.onRunEvent
|
|
18093
18433
|
}) : run;
|
|
18094
|
-
|
|
18434
|
+
const hookedRun = wrapRunWithPostReplyHook({
|
|
18095
18435
|
pluginManager: inputs.pluginManagerCode,
|
|
18096
18436
|
agentId: inputs.agentId,
|
|
18097
18437
|
options: inputs.options,
|
|
18098
18438
|
run: processedRun,
|
|
18099
18439
|
userText
|
|
18100
18440
|
});
|
|
18441
|
+
return wrapRunWithCompletionCheck({
|
|
18442
|
+
run: hookedRun,
|
|
18443
|
+
completionCheck: options.completionCheck,
|
|
18444
|
+
onRunEvent: options.onRunEvent,
|
|
18445
|
+
deps: buildCompletionCheckDeps()
|
|
18446
|
+
});
|
|
18447
|
+
}
|
|
18448
|
+
function buildCompletionCheckDeps() {
|
|
18449
|
+
return {
|
|
18450
|
+
judge: async (ctx, opts) => {
|
|
18451
|
+
const { judgeCallImpl: judgeCallImpl2 } = await Promise.resolve().then(() => (init_judge_call(), judge_call_exports));
|
|
18452
|
+
const { getAgentFacade: getAgentFacade2 } = await Promise.resolve().then(() => (init_agent_factory_registry(), agent_factory_registry_exports));
|
|
18453
|
+
return judgeCallImpl2(ctx, opts, { create: getAgentFacade2().create });
|
|
18454
|
+
}
|
|
18455
|
+
};
|
|
18101
18456
|
}
|
|
18102
18457
|
function readMemoryForSend(workspaceCwd, memoryConfig) {
|
|
18103
18458
|
if (memoryConfig?.enabled !== true) return Promise.resolve([]);
|
|
18104
18459
|
return safeCall(() => readMemoryFacts(workspaceCwd, memoryConfig), [], "memory read");
|
|
18105
18460
|
}
|
|
18461
|
+
async function projectCurrentObjective(storageHandle, objectiveThreadId, assembled) {
|
|
18462
|
+
if (objectiveThreadId === void 0) return assembled;
|
|
18463
|
+
const objective = await safeCall(
|
|
18464
|
+
() => resolveCurrentObjectiveText(storageHandle, objectiveThreadId),
|
|
18465
|
+
void 0,
|
|
18466
|
+
"objective projection"
|
|
18467
|
+
);
|
|
18468
|
+
if (objective === void 0) return assembled;
|
|
18469
|
+
return formatObjectiveProjection(objective, assembled);
|
|
18470
|
+
}
|
|
18106
18471
|
|
|
18107
18472
|
// src/internal/runtime/local-agent/local-agent-task-wrap.ts
|
|
18108
18473
|
init_registry();
|
|
@@ -18465,20 +18830,33 @@ var LocalAgent = class {
|
|
|
18465
18830
|
...opts !== void 0 ? { opts } : {}
|
|
18466
18831
|
});
|
|
18467
18832
|
}
|
|
18833
|
+
// biome-ignore format: G8 budget — artifact stubs (local agents have no artifacts); kept 1-line each.
|
|
18468
18834
|
listArtifacts() {
|
|
18469
18835
|
return Promise.resolve([]);
|
|
18470
18836
|
}
|
|
18837
|
+
// biome-ignore format: G8 budget — see listArtifacts.
|
|
18471
18838
|
downloadArtifact(_path) {
|
|
18472
|
-
return Promise.reject(
|
|
18473
|
-
new exports.UnsupportedRunOperationError(
|
|
18474
|
-
"Artifacts are not supported for local agents",
|
|
18475
|
-
"downloadArtifact"
|
|
18476
|
-
)
|
|
18477
|
-
);
|
|
18839
|
+
return Promise.reject(new exports.UnsupportedRunOperationError("Artifacts are not supported for local agents", "downloadArtifact"));
|
|
18478
18840
|
}
|
|
18479
18841
|
// biome-ignore format: G8 budget — both methods delegate to `local-agent-runtime-extensions.ts`; signatures kept as 1-line each.
|
|
18480
18842
|
runUntil(goal, options) {
|
|
18481
|
-
return localAgentRunUntil(this, goal, options);
|
|
18843
|
+
return localAgentRunUntil(this, goal, options, { handle: this.storageHandle(), goalConfig: this.options.goal });
|
|
18844
|
+
}
|
|
18845
|
+
// biome-ignore format: SE33 G8 budget — objective methods delegate to `local-agent-goal-extensions.ts`.
|
|
18846
|
+
setObjective(objective, opts) {
|
|
18847
|
+
return localAgentSetObjective(this.storageHandle(), objective, opts);
|
|
18848
|
+
}
|
|
18849
|
+
// biome-ignore format: SE33 G8 budget — see setObjective above.
|
|
18850
|
+
getObjective(opts) {
|
|
18851
|
+
return localAgentGetObjective(this.storageHandle(), opts);
|
|
18852
|
+
}
|
|
18853
|
+
// biome-ignore format: SE33 G8 budget — see setObjective above.
|
|
18854
|
+
updateObjectiveOptions(opts) {
|
|
18855
|
+
return localAgentUpdateObjectiveOptions(this.storageHandle(), opts);
|
|
18856
|
+
}
|
|
18857
|
+
// biome-ignore format: SE33 G8 budget — see setObjective above.
|
|
18858
|
+
clearObjective(opts) {
|
|
18859
|
+
return localAgentClearObjective(this.storageHandle(), opts);
|
|
18482
18860
|
}
|
|
18483
18861
|
// biome-ignore format: G8 budget — see runUntil comment above.
|
|
18484
18862
|
fork(options) {
|
|
@@ -19514,15 +19892,31 @@ function createSkill(spec) {
|
|
|
19514
19892
|
// src/cron.ts
|
|
19515
19893
|
init_errors();
|
|
19516
19894
|
|
|
19895
|
+
// src/internal/cron/fire-handler.ts
|
|
19896
|
+
init_registry();
|
|
19897
|
+
|
|
19517
19898
|
// src/internal/cron/run-job.ts
|
|
19518
19899
|
init_errors();
|
|
19519
19900
|
init_agent_factory_registry();
|
|
19520
19901
|
async function runCronJob(job) {
|
|
19521
|
-
if (job.
|
|
19522
|
-
if (job.
|
|
19523
|
-
|
|
19524
|
-
|
|
19525
|
-
|
|
19902
|
+
if (job.workflow !== void 0) return job.workflow.run(job.inputData);
|
|
19903
|
+
if (job.agent !== void 0) return runWithEphemeralAgent(job.agent, requireMessage(job));
|
|
19904
|
+
if (job.agentId !== void 0) return runWithExistingAgent(job.agentId, requireMessage(job));
|
|
19905
|
+
throw new exports.ConfigurationError(
|
|
19906
|
+
`Cron job ${job.id} has no target (agent, agentId, or workflow) \u2014 cannot run.`,
|
|
19907
|
+
{ code: "cron_no_target" }
|
|
19908
|
+
);
|
|
19909
|
+
}
|
|
19910
|
+
function isAgentRun(outcome) {
|
|
19911
|
+
return typeof outcome.wait === "function";
|
|
19912
|
+
}
|
|
19913
|
+
function requireMessage(job) {
|
|
19914
|
+
if (job.message === void 0) {
|
|
19915
|
+
throw new exports.ConfigurationError(`Cron job ${job.id} is an agent target but has no message.`, {
|
|
19916
|
+
code: "cron_missing_message"
|
|
19917
|
+
});
|
|
19918
|
+
}
|
|
19919
|
+
return job.message;
|
|
19526
19920
|
}
|
|
19527
19921
|
async function runWithExistingAgent(agentId, message) {
|
|
19528
19922
|
const info = await getAgentFacade().get(agentId).catch(() => void 0);
|
|
@@ -19540,6 +19934,37 @@ async function runWithEphemeralAgent(baseOptions, message) {
|
|
|
19540
19934
|
return agent.send(message);
|
|
19541
19935
|
}
|
|
19542
19936
|
|
|
19937
|
+
// src/internal/cron/fire-handler.ts
|
|
19938
|
+
async function fireCronJobAsTask(job) {
|
|
19939
|
+
const fireTs = Date.now();
|
|
19940
|
+
const taskId = `cron-${job.id}-${fireTs}`;
|
|
19941
|
+
try {
|
|
19942
|
+
await submit({
|
|
19943
|
+
kind: "cron",
|
|
19944
|
+
id: taskId,
|
|
19945
|
+
allowReservedPrefix: true,
|
|
19946
|
+
meta: { jobId: job.id, jobName: job.name, schedule: job.cron, firedAt: fireTs },
|
|
19947
|
+
work: async (ctx) => {
|
|
19948
|
+
const outcome = await runCronJob(job);
|
|
19949
|
+
if (isAgentRun(outcome)) {
|
|
19950
|
+
ctx.signal.addEventListener("abort", () => void outcome.cancel().catch(() => {
|
|
19951
|
+
}), {
|
|
19952
|
+
once: true
|
|
19953
|
+
});
|
|
19954
|
+
const result = await outcome.wait();
|
|
19955
|
+
ctx.emit({ status: result.status, runId: outcome.id });
|
|
19956
|
+
return { status: result.status, runId: outcome.id };
|
|
19957
|
+
}
|
|
19958
|
+
ctx.emit({ status: outcome.status, runId: outcome.id });
|
|
19959
|
+
return { status: outcome.status, runId: outcome.id };
|
|
19960
|
+
}
|
|
19961
|
+
});
|
|
19962
|
+
} catch {
|
|
19963
|
+
const outcome = await runCronJob(job);
|
|
19964
|
+
if (isAgentRun(outcome)) await outcome.wait();
|
|
19965
|
+
}
|
|
19966
|
+
}
|
|
19967
|
+
|
|
19543
19968
|
// src/internal/cron/store.ts
|
|
19544
19969
|
var jobs = /* @__PURE__ */ new Map();
|
|
19545
19970
|
function listJobs() {
|
|
@@ -19730,7 +20155,6 @@ function estimateNextRunAt(_cron, _timezone) {
|
|
|
19730
20155
|
}
|
|
19731
20156
|
|
|
19732
20157
|
// src/cron.ts
|
|
19733
|
-
init_registry();
|
|
19734
20158
|
var Cron = class {
|
|
19735
20159
|
constructor() {
|
|
19736
20160
|
}
|
|
@@ -19797,7 +20221,8 @@ var Cron = class {
|
|
|
19797
20221
|
return updateJobStatus(jobId, false);
|
|
19798
20222
|
}
|
|
19799
20223
|
/**
|
|
19800
|
-
* Manually trigger a cron job off-schedule. Returns the resulting `Run
|
|
20224
|
+
* Manually trigger a cron job off-schedule. Returns the resulting `Run`
|
|
20225
|
+
* (agent target) or `WorkflowRun` (workflow target — SE35).
|
|
19801
20226
|
*
|
|
19802
20227
|
* @public
|
|
19803
20228
|
*/
|
|
@@ -19814,30 +20239,7 @@ var Cron = class {
|
|
|
19814
20239
|
* @public
|
|
19815
20240
|
*/
|
|
19816
20241
|
static start(options = {}) {
|
|
19817
|
-
setCronFireHandler(
|
|
19818
|
-
const fireTs = Date.now();
|
|
19819
|
-
const taskId = `cron-${job.id}-${fireTs}`;
|
|
19820
|
-
try {
|
|
19821
|
-
await submit({
|
|
19822
|
-
kind: "cron",
|
|
19823
|
-
id: taskId,
|
|
19824
|
-
allowReservedPrefix: true,
|
|
19825
|
-
meta: { jobId: job.id, jobName: job.name, schedule: job.cron, firedAt: fireTs },
|
|
19826
|
-
work: async (ctx) => {
|
|
19827
|
-
const run = await runCronJob(job);
|
|
19828
|
-
ctx.signal.addEventListener("abort", () => void run.cancel().catch(() => {
|
|
19829
|
-
}), {
|
|
19830
|
-
once: true
|
|
19831
|
-
});
|
|
19832
|
-
const result = await run.wait();
|
|
19833
|
-
ctx.emit({ status: result.status, runId: run.id });
|
|
19834
|
-
return { status: result.status, runId: run.id };
|
|
19835
|
-
}
|
|
19836
|
-
});
|
|
19837
|
-
} catch {
|
|
19838
|
-
await runCronJob(job).then((run) => run.wait());
|
|
19839
|
-
}
|
|
19840
|
-
});
|
|
20242
|
+
setCronFireHandler(fireCronJobAsTask);
|
|
19841
20243
|
startScheduler(options.cwd);
|
|
19842
20244
|
return Promise.resolve();
|
|
19843
20245
|
}
|
|
@@ -19861,17 +20263,7 @@ var Cron = class {
|
|
|
19861
20263
|
}
|
|
19862
20264
|
};
|
|
19863
20265
|
async function createCronJob(options) {
|
|
19864
|
-
|
|
19865
|
-
throw new exports.ConfigurationError(
|
|
19866
|
-
"agent and agentId are mutually exclusive \u2014 pass either agent (ephemeral) or agentId (reuse).",
|
|
19867
|
-
{ code: "cron_agent_exclusive" }
|
|
19868
|
-
);
|
|
19869
|
-
}
|
|
19870
|
-
if (options.agent === void 0 && options.agentId === void 0) {
|
|
19871
|
-
throw new exports.ConfigurationError("Cron job requires either agent or agentId", {
|
|
19872
|
-
code: "cron_missing_agent"
|
|
19873
|
-
});
|
|
19874
|
-
}
|
|
20266
|
+
validateCronTarget(options);
|
|
19875
20267
|
validateCronExpression(options.cron);
|
|
19876
20268
|
const timezone = options.timezone ?? "UTC";
|
|
19877
20269
|
validateTimezone(timezone);
|
|
@@ -19882,20 +20274,51 @@ async function createCronJob(options) {
|
|
|
19882
20274
|
id: generateCronId(),
|
|
19883
20275
|
cron: options.cron,
|
|
19884
20276
|
timezone,
|
|
19885
|
-
message: options.message,
|
|
19886
20277
|
enabled: options.enabled ?? true,
|
|
19887
20278
|
status: options.enabled === false ? "paused" : "scheduled",
|
|
19888
20279
|
runtime,
|
|
19889
20280
|
createdAt: now,
|
|
19890
20281
|
nextRunAt: estimateNextRunAt(options.cron),
|
|
19891
20282
|
...options.name !== void 0 ? { name: options.name } : {},
|
|
20283
|
+
...options.message !== void 0 ? { message: options.message } : {},
|
|
19892
20284
|
...options.agent !== void 0 ? { agent: options.agent } : {},
|
|
19893
|
-
...options.agentId !== void 0 ? { agentId: options.agentId } : {}
|
|
20285
|
+
...options.agentId !== void 0 ? { agentId: options.agentId } : {},
|
|
20286
|
+
...options.workflow !== void 0 ? { workflow: options.workflow } : {},
|
|
20287
|
+
...options.inputData !== void 0 ? { inputData: options.inputData } : {}
|
|
19894
20288
|
};
|
|
19895
20289
|
upsertJob(job);
|
|
19896
20290
|
return job;
|
|
19897
20291
|
}
|
|
20292
|
+
function validateCronTarget(options) {
|
|
20293
|
+
const targets = [options.agent, options.agentId, options.workflow].filter(
|
|
20294
|
+
(t) => t !== void 0
|
|
20295
|
+
).length;
|
|
20296
|
+
if (targets > 1) {
|
|
20297
|
+
throw new exports.ConfigurationError(
|
|
20298
|
+
"agent, agentId, and workflow are mutually exclusive \u2014 pass exactly one target.",
|
|
20299
|
+
{ code: "cron_ambiguous_target" }
|
|
20300
|
+
);
|
|
20301
|
+
}
|
|
20302
|
+
if (targets === 0) {
|
|
20303
|
+
throw new exports.ConfigurationError(
|
|
20304
|
+
"Cron job requires exactly one target: agent, agentId, or workflow.",
|
|
20305
|
+
{ code: "cron_no_target" }
|
|
20306
|
+
);
|
|
20307
|
+
}
|
|
20308
|
+
if (options.workflow !== void 0 && options.message !== void 0) {
|
|
20309
|
+
throw new exports.ConfigurationError(
|
|
20310
|
+
"A workflow cron target takes inputData, not a message \u2014 remove `message`.",
|
|
20311
|
+
{ code: "cron_workflow_message" }
|
|
20312
|
+
);
|
|
20313
|
+
}
|
|
20314
|
+
if (options.workflow === void 0 && options.message === void 0) {
|
|
20315
|
+
throw new exports.ConfigurationError("An agent cron target requires a message.", {
|
|
20316
|
+
code: "cron_missing_message"
|
|
20317
|
+
});
|
|
20318
|
+
}
|
|
20319
|
+
}
|
|
19898
20320
|
function detectRuntime(options) {
|
|
20321
|
+
if (options.workflow !== void 0) return "local";
|
|
19899
20322
|
if (options.agentId !== void 0) {
|
|
19900
20323
|
return options.agentId.startsWith("bc-") ? "cloud" : "local";
|
|
19901
20324
|
}
|
|
@@ -20062,12 +20485,17 @@ var EventBus = class {
|
|
|
20062
20485
|
|
|
20063
20486
|
// src/index.ts
|
|
20064
20487
|
init_generate_object();
|
|
20488
|
+
init_conversation_storage_fs();
|
|
20065
20489
|
|
|
20066
20490
|
// src/internal/persistence/conversation-storage-memory.ts
|
|
20491
|
+
init_pagination();
|
|
20492
|
+
init_session_meta();
|
|
20067
20493
|
var InMemoryConversationStorage = class {
|
|
20068
20494
|
#store = /* @__PURE__ */ new Map();
|
|
20069
20495
|
// SE4 — session-level metadata (title/tag), kept beside the transcript.
|
|
20070
20496
|
#meta = /* @__PURE__ */ new Map();
|
|
20497
|
+
// SE33 — durable thread-scoped objective, kept beside the transcript.
|
|
20498
|
+
#objectives = /* @__PURE__ */ new Map();
|
|
20071
20499
|
async getMessages(conversationId, opts) {
|
|
20072
20500
|
const existing = this.#store.get(conversationId);
|
|
20073
20501
|
return existing === void 0 ? [] : paginate(existing.slice(), opts);
|
|
@@ -20097,18 +20525,21 @@ var InMemoryConversationStorage = class {
|
|
|
20097
20525
|
async deleteConversation(conversationId) {
|
|
20098
20526
|
this.#store.delete(conversationId);
|
|
20099
20527
|
this.#meta.delete(conversationId);
|
|
20528
|
+
this.#objectives.delete(conversationId);
|
|
20100
20529
|
}
|
|
20101
20530
|
async deleteScope(prefix) {
|
|
20102
20531
|
let n = 0;
|
|
20103
|
-
|
|
20104
|
-
|
|
20105
|
-
|
|
20106
|
-
|
|
20107
|
-
|
|
20108
|
-
|
|
20109
|
-
|
|
20110
|
-
|
|
20111
|
-
|
|
20532
|
+
const ids = /* @__PURE__ */ new Set([
|
|
20533
|
+
...this.#store.keys(),
|
|
20534
|
+
...this.#meta.keys(),
|
|
20535
|
+
...this.#objectives.keys()
|
|
20536
|
+
]);
|
|
20537
|
+
for (const id of ids) {
|
|
20538
|
+
if (!id.startsWith(prefix)) continue;
|
|
20539
|
+
this.#store.delete(id);
|
|
20540
|
+
this.#meta.delete(id);
|
|
20541
|
+
this.#objectives.delete(id);
|
|
20542
|
+
n += 1;
|
|
20112
20543
|
}
|
|
20113
20544
|
return n;
|
|
20114
20545
|
}
|
|
@@ -20125,9 +20556,27 @@ var InMemoryConversationStorage = class {
|
|
|
20125
20556
|
const current = this.#meta.get(conversationId) ?? {};
|
|
20126
20557
|
this.#meta.set(conversationId, applyMetaPatch(current, patch));
|
|
20127
20558
|
}
|
|
20559
|
+
async getObjectiveRecord(conversationId) {
|
|
20560
|
+
const rec = this.#objectives.get(conversationId);
|
|
20561
|
+
return rec === void 0 ? void 0 : { ...rec };
|
|
20562
|
+
}
|
|
20563
|
+
async setObjectiveRecord(conversationId, record) {
|
|
20564
|
+
if (record === null) this.#objectives.delete(conversationId);
|
|
20565
|
+
else this.#objectives.set(conversationId, { ...record });
|
|
20566
|
+
}
|
|
20567
|
+
// SE33 (HIGH-1 fix) — atomic in the single-threaded runtime: the get→mutate→set
|
|
20568
|
+
// runs synchronously, so there is no interleaving window for a concurrent frame.
|
|
20569
|
+
async updateObjectiveRecord(conversationId, mutate) {
|
|
20570
|
+
const current = this.#objectives.get(conversationId);
|
|
20571
|
+
const next = mutate(current === void 0 ? void 0 : { ...current });
|
|
20572
|
+
if (next === void 0) return;
|
|
20573
|
+
if (next === null) this.#objectives.delete(conversationId);
|
|
20574
|
+
else this.#objectives.set(conversationId, { ...next });
|
|
20575
|
+
}
|
|
20128
20576
|
async dispose() {
|
|
20129
20577
|
this.#store.clear();
|
|
20130
20578
|
this.#meta.clear();
|
|
20579
|
+
this.#objectives.clear();
|
|
20131
20580
|
}
|
|
20132
20581
|
};
|
|
20133
20582
|
|
|
@@ -21950,7 +22399,6 @@ exports.AgentBuilder = AgentBuilder;
|
|
|
21950
22399
|
exports.Budget = Budget;
|
|
21951
22400
|
exports.Cron = Cron;
|
|
21952
22401
|
exports.EventBus = EventBus;
|
|
21953
|
-
exports.FileSystemConversationStorage = FileSystemConversationStorage;
|
|
21954
22402
|
exports.InMemoryConversationStorage = InMemoryConversationStorage;
|
|
21955
22403
|
exports.JobQueue = JobQueue;
|
|
21956
22404
|
exports.Memory = Memory;
|