@theokit/sdk 2.27.0 → 2.29.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-COSAehOL.d.ts} +110 -3
- package/dist/{cron-DgEQCJ2i.d.ts → cron-yJoNUZxe.d.cts} +110 -3
- package/dist/cron.cjs +914 -508
- 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 +915 -509
- package/dist/cron.js.map +1 -1
- package/dist/{errors-CbY3pxY7.d.ts → errors-BxMIlgLP.d.ts} +1 -1
- package/dist/{errors-DLMNb4Ka.d.cts → errors-tP-8O-hR.d.cts} +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/filesystem/index.cjs +304 -0
- package/dist/filesystem/index.cjs.map +1 -0
- package/dist/filesystem/index.d.cts +12 -0
- package/dist/filesystem/index.d.ts +12 -0
- package/dist/filesystem/index.js +295 -0
- package/dist/filesystem/index.js.map +1 -0
- package/dist/filesystem/local-filesystem.d.cts +36 -0
- package/dist/filesystem/local-filesystem.d.ts +36 -0
- package/dist/filesystem/types.d.cts +118 -0
- package/dist/filesystem/types.d.ts +118 -0
- package/dist/index.cjs +973 -570
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +13 -7
- package/dist/index.d.ts +13 -7
- package/dist/index.js +972 -568
- 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-q_P0vHlY.d.cts} +71 -2
- package/dist/{run-CdWiihyU.d.ts → run-q_P0vHlY.d.ts} +71 -2
- package/dist/types/agent.d.ts +32 -1
- package/dist/types/conversation-storage.d.ts +23 -0
- 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 +11 -1
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();
|
|
@@ -8859,431 +9374,174 @@ function resolveChildEnv(options = {}) {
|
|
|
8859
9374
|
if (value !== void 0 && inheritsUnderPolicy(name, policy)) base[name] = value;
|
|
8860
9375
|
}
|
|
8861
9376
|
for (const [name, value] of Object.entries(options.overrides ?? {})) {
|
|
8862
|
-
base[name] = value;
|
|
8863
|
-
}
|
|
8864
|
-
return base;
|
|
8865
|
-
}
|
|
8866
|
-
|
|
8867
|
-
// src/internal/runtime/lifecycle/spawn-collect.ts
|
|
8868
|
-
function spawnAndCollect(options) {
|
|
8869
|
-
return new Promise((resolve3) => {
|
|
8870
|
-
const timeoutMs = options.timeoutMs ?? 3e4;
|
|
8871
|
-
const spawnOptions = {
|
|
8872
|
-
cwd: options.cwd,
|
|
8873
|
-
// #54 — scrub secret-like parent env by default; `options.env` still wins.
|
|
8874
|
-
env: resolveChildEnv({ policy: options.envPolicy, overrides: options.env })
|
|
8875
|
-
};
|
|
8876
|
-
const child = child_process.spawn(options.command, options.args ?? [], spawnOptions);
|
|
8877
|
-
let stdout = "";
|
|
8878
|
-
let stderr = "";
|
|
8879
|
-
let timedOut = false;
|
|
8880
|
-
let settled = false;
|
|
8881
|
-
const settle = (result) => {
|
|
8882
|
-
if (settled) return;
|
|
8883
|
-
settled = true;
|
|
8884
|
-
resolve3(result);
|
|
8885
|
-
};
|
|
8886
|
-
const timer = setTimeout(() => {
|
|
8887
|
-
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);
|
|
9377
|
+
base[name] = value;
|
|
9378
|
+
}
|
|
9379
|
+
return base;
|
|
9169
9380
|
}
|
|
9170
9381
|
|
|
9171
|
-
// src/internal/
|
|
9172
|
-
function
|
|
9173
|
-
|
|
9174
|
-
|
|
9175
|
-
|
|
9176
|
-
|
|
9177
|
-
|
|
9178
|
-
|
|
9179
|
-
|
|
9180
|
-
|
|
9181
|
-
|
|
9182
|
-
|
|
9183
|
-
|
|
9184
|
-
|
|
9185
|
-
|
|
9186
|
-
|
|
9187
|
-
|
|
9188
|
-
|
|
9382
|
+
// src/internal/runtime/lifecycle/spawn-collect.ts
|
|
9383
|
+
function spawnAndCollect(options) {
|
|
9384
|
+
return new Promise((resolve3) => {
|
|
9385
|
+
const timeoutMs = options.timeoutMs ?? 3e4;
|
|
9386
|
+
const spawnOptions = {
|
|
9387
|
+
cwd: options.cwd,
|
|
9388
|
+
// #54 — scrub secret-like parent env by default; `options.env` still wins.
|
|
9389
|
+
env: resolveChildEnv({ policy: options.envPolicy, overrides: options.env })
|
|
9390
|
+
};
|
|
9391
|
+
const child = child_process.spawn(options.command, options.args ?? [], spawnOptions);
|
|
9392
|
+
let stdout = "";
|
|
9393
|
+
let stderr = "";
|
|
9394
|
+
let timedOut = false;
|
|
9395
|
+
let settled = false;
|
|
9396
|
+
const settle = (result) => {
|
|
9397
|
+
if (settled) return;
|
|
9398
|
+
settled = true;
|
|
9399
|
+
resolve3(result);
|
|
9400
|
+
};
|
|
9401
|
+
const timer = setTimeout(() => {
|
|
9402
|
+
timedOut = true;
|
|
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) {
|
|
@@ -20062,12 +20440,17 @@ var EventBus = class {
|
|
|
20062
20440
|
|
|
20063
20441
|
// src/index.ts
|
|
20064
20442
|
init_generate_object();
|
|
20443
|
+
init_conversation_storage_fs();
|
|
20065
20444
|
|
|
20066
20445
|
// src/internal/persistence/conversation-storage-memory.ts
|
|
20446
|
+
init_pagination();
|
|
20447
|
+
init_session_meta();
|
|
20067
20448
|
var InMemoryConversationStorage = class {
|
|
20068
20449
|
#store = /* @__PURE__ */ new Map();
|
|
20069
20450
|
// SE4 — session-level metadata (title/tag), kept beside the transcript.
|
|
20070
20451
|
#meta = /* @__PURE__ */ new Map();
|
|
20452
|
+
// SE33 — durable thread-scoped objective, kept beside the transcript.
|
|
20453
|
+
#objectives = /* @__PURE__ */ new Map();
|
|
20071
20454
|
async getMessages(conversationId, opts) {
|
|
20072
20455
|
const existing = this.#store.get(conversationId);
|
|
20073
20456
|
return existing === void 0 ? [] : paginate(existing.slice(), opts);
|
|
@@ -20097,18 +20480,21 @@ var InMemoryConversationStorage = class {
|
|
|
20097
20480
|
async deleteConversation(conversationId) {
|
|
20098
20481
|
this.#store.delete(conversationId);
|
|
20099
20482
|
this.#meta.delete(conversationId);
|
|
20483
|
+
this.#objectives.delete(conversationId);
|
|
20100
20484
|
}
|
|
20101
20485
|
async deleteScope(prefix) {
|
|
20102
20486
|
let n = 0;
|
|
20103
|
-
|
|
20104
|
-
|
|
20105
|
-
|
|
20106
|
-
|
|
20107
|
-
|
|
20108
|
-
|
|
20109
|
-
|
|
20110
|
-
|
|
20111
|
-
|
|
20487
|
+
const ids = /* @__PURE__ */ new Set([
|
|
20488
|
+
...this.#store.keys(),
|
|
20489
|
+
...this.#meta.keys(),
|
|
20490
|
+
...this.#objectives.keys()
|
|
20491
|
+
]);
|
|
20492
|
+
for (const id of ids) {
|
|
20493
|
+
if (!id.startsWith(prefix)) continue;
|
|
20494
|
+
this.#store.delete(id);
|
|
20495
|
+
this.#meta.delete(id);
|
|
20496
|
+
this.#objectives.delete(id);
|
|
20497
|
+
n += 1;
|
|
20112
20498
|
}
|
|
20113
20499
|
return n;
|
|
20114
20500
|
}
|
|
@@ -20125,9 +20511,27 @@ var InMemoryConversationStorage = class {
|
|
|
20125
20511
|
const current = this.#meta.get(conversationId) ?? {};
|
|
20126
20512
|
this.#meta.set(conversationId, applyMetaPatch(current, patch));
|
|
20127
20513
|
}
|
|
20514
|
+
async getObjectiveRecord(conversationId) {
|
|
20515
|
+
const rec = this.#objectives.get(conversationId);
|
|
20516
|
+
return rec === void 0 ? void 0 : { ...rec };
|
|
20517
|
+
}
|
|
20518
|
+
async setObjectiveRecord(conversationId, record) {
|
|
20519
|
+
if (record === null) this.#objectives.delete(conversationId);
|
|
20520
|
+
else this.#objectives.set(conversationId, { ...record });
|
|
20521
|
+
}
|
|
20522
|
+
// SE33 (HIGH-1 fix) — atomic in the single-threaded runtime: the get→mutate→set
|
|
20523
|
+
// runs synchronously, so there is no interleaving window for a concurrent frame.
|
|
20524
|
+
async updateObjectiveRecord(conversationId, mutate) {
|
|
20525
|
+
const current = this.#objectives.get(conversationId);
|
|
20526
|
+
const next = mutate(current === void 0 ? void 0 : { ...current });
|
|
20527
|
+
if (next === void 0) return;
|
|
20528
|
+
if (next === null) this.#objectives.delete(conversationId);
|
|
20529
|
+
else this.#objectives.set(conversationId, { ...next });
|
|
20530
|
+
}
|
|
20128
20531
|
async dispose() {
|
|
20129
20532
|
this.#store.clear();
|
|
20130
20533
|
this.#meta.clear();
|
|
20534
|
+
this.#objectives.clear();
|
|
20131
20535
|
}
|
|
20132
20536
|
};
|
|
20133
20537
|
|
|
@@ -21950,7 +22354,6 @@ exports.AgentBuilder = AgentBuilder;
|
|
|
21950
22354
|
exports.Budget = Budget;
|
|
21951
22355
|
exports.Cron = Cron;
|
|
21952
22356
|
exports.EventBus = EventBus;
|
|
21953
|
-
exports.FileSystemConversationStorage = FileSystemConversationStorage;
|
|
21954
22357
|
exports.InMemoryConversationStorage = InMemoryConversationStorage;
|
|
21955
22358
|
exports.JobQueue = JobQueue;
|
|
21956
22359
|
exports.Memory = Memory;
|