theokit 0.47.0 → 0.48.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/dist/{actions-virtual-module-PUYSUA5O.js → actions-virtual-module-2VVLVXHY.js} +3 -3
- package/dist/{actions-virtual-module-Z76RNOJC.js → actions-virtual-module-Z5LXAX7E.js} +5 -4
- package/dist/actions-virtual-module-Z5LXAX7E.js.map +1 -0
- package/dist/{agent-3VDG5XK5.js → agent-Y3JKRROD.js} +2 -2
- package/dist/{app-typed-client-QYMZWXAW.js → app-typed-client-N4I4RBLT.js} +3 -3
- package/dist/{app-typed-client-VLT4FBKE.js → app-typed-client-NI3CM2FA.js} +5 -4
- package/dist/app-typed-client-NI3CM2FA.js.map +1 -0
- package/dist/boot/index.js +1 -1
- package/dist/{build-6KKFX33J.js → build-SJQDRD2B.js} +2 -2
- package/dist/chunk-6E5WKMDS.js +67 -0
- package/dist/chunk-6E5WKMDS.js.map +1 -0
- package/dist/{chunk-FZG3JYB3.js → chunk-7ZOQN7UH.js} +1 -1
- package/dist/{chunk-U3H53IIN.js → chunk-FO55VGMQ.js} +3 -3
- package/dist/{chunk-ELDTTO6P.js → chunk-GEHX6CAE.js} +2 -2
- package/dist/{chunk-7SABX4DJ.js → chunk-IZ3TIL6K.js} +28 -2
- package/dist/{chunk-7SABX4DJ.js.map → chunk-IZ3TIL6K.js.map} +1 -1
- package/dist/{chunk-B7XHGEDH.js → chunk-O5W7XSUX.js} +36 -36
- package/dist/chunk-O5W7XSUX.js.map +1 -0
- package/dist/{chunk-EIRQM55C.js → chunk-OJLL4YD6.js} +5 -46
- package/dist/chunk-OJLL4YD6.js.map +1 -0
- package/dist/{chunk-XTM32FF6.js → chunk-VQIUTCK2.js} +28 -2
- package/dist/{chunk-XTM32FF6.js.map → chunk-VQIUTCK2.js.map} +1 -1
- package/dist/{chunk-VPV7WABO.js → chunk-VTJMND23.js} +2 -2
- package/dist/{chunk-37ZO643S.js → chunk-XALU5LCU.js} +3 -3
- package/dist/{chunk-SEELKQUM.js → chunk-Y2KHTD2W.js} +6 -2
- package/dist/chunk-Y2KHTD2W.js.map +1 -0
- package/dist/{chunk-4TLLWXWH.js → chunk-YRRWQDKK.js} +11 -9
- package/dist/chunk-YRRWQDKK.js.map +1 -0
- package/dist/{chunk-OGU2MIHC.js → chunk-ZALJIZH2.js} +6 -6
- package/dist/cli/index.js +27 -5
- package/dist/cli/index.js.map +1 -1
- package/dist/{dev-4NLBCCLU.js → dev-6TWDBX6G.js} +5 -5
- package/dist/doctor-Y53QTUSP.js +56 -0
- package/dist/doctor-Y53QTUSP.js.map +1 -0
- package/dist/index.js +6 -5
- package/dist/index.js.map +1 -1
- package/dist/{internal-api-C5UJG3E2.js → internal-api-REGN3WMX.js} +8 -6
- package/dist/{internal-api-XAQ7H73N.js → internal-api-XQA2L663.js} +3 -3
- package/dist/{mcp-TJIUDFY2.js → mcp-CO4AGAXV.js} +2 -2
- package/dist/server/agent/index.d.ts +90 -1
- package/dist/server/agent/index.js +14 -2
- package/dist/server/cost/index.d.ts +1 -0
- package/dist/server/cost/index.js +3 -1
- package/dist/server/http/index.js +2 -2
- package/dist/server/index.d.ts +496 -3
- package/dist/server/index.js +486 -7
- package/dist/server/index.js.map +1 -1
- package/dist/{server-error-to-envelope-UM7DEVM4.js → server-error-to-envelope-2MQOME5Q.js} +2 -2
- package/dist/{server-error-to-envelope-J5NFZGCQ.js → server-error-to-envelope-4TGCDQ2S.js} +2 -2
- package/dist/sessions-gc-H4WC4IXB.js +38 -0
- package/dist/sessions-gc-H4WC4IXB.js.map +1 -0
- package/dist/{start-XPQ4EJ6J.js → start-IO3ZYGEX.js} +4 -4
- package/dist/vite-plugin/index.js +6 -5
- package/dist/{vite-plugin-CD67YAD5.js → vite-plugin-NQ6F7FGV.js} +5 -5
- package/package.json +5 -5
- package/dist/actions-virtual-module-Z76RNOJC.js.map +0 -1
- package/dist/app-typed-client-VLT4FBKE.js.map +0 -1
- package/dist/chunk-4TLLWXWH.js.map +0 -1
- package/dist/chunk-B7XHGEDH.js.map +0 -1
- package/dist/chunk-EIRQM55C.js.map +0 -1
- package/dist/chunk-SEELKQUM.js.map +0 -1
- /package/dist/{actions-virtual-module-PUYSUA5O.js.map → actions-virtual-module-2VVLVXHY.js.map} +0 -0
- /package/dist/{agent-3VDG5XK5.js.map → agent-Y3JKRROD.js.map} +0 -0
- /package/dist/{app-typed-client-QYMZWXAW.js.map → app-typed-client-N4I4RBLT.js.map} +0 -0
- /package/dist/{build-6KKFX33J.js.map → build-SJQDRD2B.js.map} +0 -0
- /package/dist/{chunk-FZG3JYB3.js.map → chunk-7ZOQN7UH.js.map} +0 -0
- /package/dist/{chunk-U3H53IIN.js.map → chunk-FO55VGMQ.js.map} +0 -0
- /package/dist/{chunk-ELDTTO6P.js.map → chunk-GEHX6CAE.js.map} +0 -0
- /package/dist/{chunk-VPV7WABO.js.map → chunk-VTJMND23.js.map} +0 -0
- /package/dist/{chunk-37ZO643S.js.map → chunk-XALU5LCU.js.map} +0 -0
- /package/dist/{chunk-OGU2MIHC.js.map → chunk-ZALJIZH2.js.map} +0 -0
- /package/dist/{dev-4NLBCCLU.js.map → dev-6TWDBX6G.js.map} +0 -0
- /package/dist/{internal-api-C5UJG3E2.js.map → internal-api-REGN3WMX.js.map} +0 -0
- /package/dist/{internal-api-XAQ7H73N.js.map → internal-api-XQA2L663.js.map} +0 -0
- /package/dist/{mcp-TJIUDFY2.js.map → mcp-CO4AGAXV.js.map} +0 -0
- /package/dist/{server-error-to-envelope-UM7DEVM4.js.map → server-error-to-envelope-2MQOME5Q.js.map} +0 -0
- /package/dist/{server-error-to-envelope-J5NFZGCQ.js.map → server-error-to-envelope-4TGCDQ2S.js.map} +0 -0
- /package/dist/{start-XPQ4EJ6J.js.map → start-IO3ZYGEX.js.map} +0 -0
- /package/dist/{vite-plugin-CD67YAD5.js.map → vite-plugin-NQ6F7FGV.js.map} +0 -0
package/dist/server/index.js
CHANGED
|
@@ -61,7 +61,7 @@ import {
|
|
|
61
61
|
resolveTransformer,
|
|
62
62
|
superjsonTransformer,
|
|
63
63
|
validateRouteInput
|
|
64
|
-
} from "../chunk-
|
|
64
|
+
} from "../chunk-FO55VGMQ.js";
|
|
65
65
|
import {
|
|
66
66
|
ActionError,
|
|
67
67
|
ActionInputError,
|
|
@@ -88,7 +88,7 @@ import {
|
|
|
88
88
|
runMiddlewareAndContext,
|
|
89
89
|
sendError,
|
|
90
90
|
sendJson
|
|
91
|
-
} from "../chunk-
|
|
91
|
+
} from "../chunk-XALU5LCU.js";
|
|
92
92
|
import {
|
|
93
93
|
DuplicateContextKeyError,
|
|
94
94
|
createOutbox,
|
|
@@ -176,7 +176,7 @@ import {
|
|
|
176
176
|
TheoError,
|
|
177
177
|
fromUnknown,
|
|
178
178
|
serverErrorToEnvelope
|
|
179
|
-
} from "../chunk-
|
|
179
|
+
} from "../chunk-IZ3TIL6K.js";
|
|
180
180
|
import {
|
|
181
181
|
CodeModePermissionDeniedError,
|
|
182
182
|
InProcessApprovalRequiredError,
|
|
@@ -191,13 +191,20 @@ import {
|
|
|
191
191
|
parseChannelPath,
|
|
192
192
|
serveMcpStdio,
|
|
193
193
|
streamAgentTurnInProcess
|
|
194
|
-
} from "../chunk-
|
|
194
|
+
} from "../chunk-7ZOQN7UH.js";
|
|
195
195
|
import {
|
|
196
196
|
buildResourceDescriptors,
|
|
197
197
|
defineAppResource,
|
|
198
198
|
extractAppResources,
|
|
199
199
|
readAppResource
|
|
200
200
|
} from "../chunk-YOIVCCZT.js";
|
|
201
|
+
import {
|
|
202
|
+
listProviders,
|
|
203
|
+
registerProvider,
|
|
204
|
+
resetProviderRegistry,
|
|
205
|
+
resolveProvider,
|
|
206
|
+
tryResolveProvider
|
|
207
|
+
} from "../chunk-6E5WKMDS.js";
|
|
201
208
|
import {
|
|
202
209
|
AuthRequiredError,
|
|
203
210
|
_resetKeyCacheForTests,
|
|
@@ -239,9 +246,10 @@ import {
|
|
|
239
246
|
} from "../chunk-JA2ZAT5Q.js";
|
|
240
247
|
import {
|
|
241
248
|
InMemoryUsageStorage,
|
|
249
|
+
InMemoryUsageStorage2,
|
|
242
250
|
trackAgentRun,
|
|
243
251
|
trackAgentTools
|
|
244
|
-
} from "../chunk-
|
|
252
|
+
} from "../chunk-Y2KHTD2W.js";
|
|
245
253
|
import {
|
|
246
254
|
CRON_MANIFEST_SCHEMA_VERSION,
|
|
247
255
|
DuplicateCronNameError,
|
|
@@ -788,8 +796,8 @@ function createCacheEngine(opts) {
|
|
|
788
796
|
function claimAndRun(key, fn, options, skipWrite) {
|
|
789
797
|
let resolveOuter;
|
|
790
798
|
let rejectOuter;
|
|
791
|
-
const outerPromise = new Promise((
|
|
792
|
-
resolveOuter =
|
|
799
|
+
const outerPromise = new Promise((resolve3, reject) => {
|
|
800
|
+
resolveOuter = resolve3;
|
|
793
801
|
rejectOuter = reject;
|
|
794
802
|
});
|
|
795
803
|
void outerPromise.catch(() => {
|
|
@@ -1178,6 +1186,458 @@ var CTX_WRITERS = {
|
|
|
1178
1186
|
jobsQueue: "jobBackend"
|
|
1179
1187
|
};
|
|
1180
1188
|
|
|
1189
|
+
// src/config/layered-config.ts
|
|
1190
|
+
import { foldLayers, verifyLayerOrdering } from "@theokit/agents";
|
|
1191
|
+
var LayerOutOfOrderError = class extends Error {
|
|
1192
|
+
name = "LayerOutOfOrderError";
|
|
1193
|
+
};
|
|
1194
|
+
var LayeredConfig = {
|
|
1195
|
+
/**
|
|
1196
|
+
* Fold the chain, validate the result, and report where everything came from.
|
|
1197
|
+
*
|
|
1198
|
+
* The schema is applied AFTER folding, never per layer. Validating each layer separately would
|
|
1199
|
+
* force every file to be complete, which defeats layering: a project override that sets one key
|
|
1200
|
+
* would have to restate the whole config.
|
|
1201
|
+
*
|
|
1202
|
+
* @throws {LayerOutOfOrderError} when a layer does not outrank the one before it.
|
|
1203
|
+
*/
|
|
1204
|
+
resolve(input) {
|
|
1205
|
+
assertOrdering(input.layers);
|
|
1206
|
+
const accumulating = input.accumulatingKeys ?? [];
|
|
1207
|
+
const folded = foldLayers(input.layers, accumulating);
|
|
1208
|
+
const value = input.schema.parse(folded);
|
|
1209
|
+
return {
|
|
1210
|
+
value,
|
|
1211
|
+
provenancePerKey: buildProvenance(input.layers, accumulating),
|
|
1212
|
+
precedenceReport: buildReport(input.layers)
|
|
1213
|
+
};
|
|
1214
|
+
}
|
|
1215
|
+
};
|
|
1216
|
+
function assertOrdering(layers) {
|
|
1217
|
+
try {
|
|
1218
|
+
verifyLayerOrdering(layers);
|
|
1219
|
+
} catch (error) {
|
|
1220
|
+
throw new LayerOutOfOrderError(error.message);
|
|
1221
|
+
}
|
|
1222
|
+
}
|
|
1223
|
+
function contributorsOf(layers, key) {
|
|
1224
|
+
return layers.filter((layer) => key in layer.values).map((layer) => layer.layer);
|
|
1225
|
+
}
|
|
1226
|
+
function buildProvenance(layers, accumulating) {
|
|
1227
|
+
const keys = new Set(layers.flatMap((layer) => Object.keys(layer.values)));
|
|
1228
|
+
const provenance = {};
|
|
1229
|
+
for (const key of keys) {
|
|
1230
|
+
const contributors = contributorsOf(layers, key);
|
|
1231
|
+
const winner = contributors.at(-1);
|
|
1232
|
+
if (winner === void 0) continue;
|
|
1233
|
+
provenance[key] = accumulating.includes(key) ? contributors.join(", ") : winner;
|
|
1234
|
+
}
|
|
1235
|
+
return provenance;
|
|
1236
|
+
}
|
|
1237
|
+
function buildReport(layers) {
|
|
1238
|
+
const declared = layers.map((layer) => layer.layer);
|
|
1239
|
+
const contributed = new Set(
|
|
1240
|
+
layers.filter((layer) => Object.keys(layer.values).length > 0).map((layer) => layer.layer)
|
|
1241
|
+
);
|
|
1242
|
+
const measured = declared.filter((name) => contributed.has(name));
|
|
1243
|
+
const declaredButSilent = declared.filter((name) => !contributed.has(name));
|
|
1244
|
+
return {
|
|
1245
|
+
declared,
|
|
1246
|
+
measured,
|
|
1247
|
+
declaredButSilent,
|
|
1248
|
+
diverges: declaredButSilent.length > 0
|
|
1249
|
+
};
|
|
1250
|
+
}
|
|
1251
|
+
|
|
1252
|
+
// src/config/trust-store.ts
|
|
1253
|
+
import { chmodSync, existsSync as existsSync2, mkdirSync, readFileSync as readFileSync2, statSync as statSync2 } from "fs";
|
|
1254
|
+
import { dirname } from "path";
|
|
1255
|
+
import { resolveTrustPosture } from "@theokit/agents";
|
|
1256
|
+
import { atomicWriteJson, withFileLock } from "@theokit/agents/persistence";
|
|
1257
|
+
var TrustStorePermissionsError = class extends Error {
|
|
1258
|
+
constructor(file, mode) {
|
|
1259
|
+
super(
|
|
1260
|
+
`trust store ${file} is mode ${mode.toString(8)} \u2014 group or world writable. This file decides which directories may run shell hooks, so a writable store is a way to grant that to yourself. Refused rather than repaired: tightening it silently would hide that something changed the mode. Fix with \`chmod 600 ${file}\`.`
|
|
1261
|
+
);
|
|
1262
|
+
this.file = file;
|
|
1263
|
+
this.mode = mode;
|
|
1264
|
+
}
|
|
1265
|
+
file;
|
|
1266
|
+
mode;
|
|
1267
|
+
name = "TrustStorePermissionsError";
|
|
1268
|
+
};
|
|
1269
|
+
var FORBIDDEN_WRITE_BITS = 18;
|
|
1270
|
+
var TrustStore = class {
|
|
1271
|
+
constructor(file) {
|
|
1272
|
+
this.file = file;
|
|
1273
|
+
}
|
|
1274
|
+
file;
|
|
1275
|
+
/**
|
|
1276
|
+
* Read the store, refusing a file other users can write.
|
|
1277
|
+
*
|
|
1278
|
+
* A missing store is not an error — it is a machine that has trusted nothing yet, which is the
|
|
1279
|
+
* correct starting state and the safe one.
|
|
1280
|
+
*/
|
|
1281
|
+
read() {
|
|
1282
|
+
if (!existsSync2(this.file)) return [];
|
|
1283
|
+
this.assertSafePermissions();
|
|
1284
|
+
const parsed = JSON.parse(readFileSync2(this.file, "utf8"));
|
|
1285
|
+
return parsed.records;
|
|
1286
|
+
}
|
|
1287
|
+
/**
|
|
1288
|
+
* Record a decision about `path`, replacing any previous one for it.
|
|
1289
|
+
*
|
|
1290
|
+
* `decidedAt` and `decidedBy` are ARGUMENTS, not derived here (DIP): the clock and the identity
|
|
1291
|
+
* belong to the caller, and baking `new Date()` in would make every assertion about the record
|
|
1292
|
+
* depend on when the test ran.
|
|
1293
|
+
*
|
|
1294
|
+
* ASYNC because both `withFileLock` and `atomicWriteJson` are. Measured, not assumed: the first
|
|
1295
|
+
* draft called them synchronously and `trust()` returned before the bytes landed, so an immediate
|
|
1296
|
+
* `read()` saw an empty store. Same shape as the M71 pointer bug, and same cause — the SDK's
|
|
1297
|
+
* `.d.ts` does not declare these, so nothing at compile time says they return a Promise
|
|
1298
|
+
* (usetheodev/theokit-sdk#280).
|
|
1299
|
+
*/
|
|
1300
|
+
async trust(record) {
|
|
1301
|
+
mkdirSync(dirname(this.file), { recursive: true });
|
|
1302
|
+
await withFileLock(
|
|
1303
|
+
this.file,
|
|
1304
|
+
async () => {
|
|
1305
|
+
const existing = existsSync2(this.file) ? this.read() : [];
|
|
1306
|
+
const records = [...existing.filter((r) => r.path !== record.path), record];
|
|
1307
|
+
await atomicWriteJson(this.file, {
|
|
1308
|
+
version: 1,
|
|
1309
|
+
records
|
|
1310
|
+
});
|
|
1311
|
+
chmodSync(this.file, 384);
|
|
1312
|
+
}
|
|
1313
|
+
);
|
|
1314
|
+
}
|
|
1315
|
+
/**
|
|
1316
|
+
* The recorded posture for `path`, or an UNTRUSTED posture when nothing was recorded.
|
|
1317
|
+
*
|
|
1318
|
+
* Absence resolves to untrusted, never to "unknown, proceed". A store that answered "I do not
|
|
1319
|
+
* know" would push the decision back to the caller, and the caller asking is what the store
|
|
1320
|
+
* exists to answer.
|
|
1321
|
+
*/
|
|
1322
|
+
postureFor(path, capabilities) {
|
|
1323
|
+
const record = this.read().find((r) => r.path === path);
|
|
1324
|
+
return resolveTrustPosture({
|
|
1325
|
+
capabilities: [...capabilities],
|
|
1326
|
+
isTrusted: () => record?.trusted === true
|
|
1327
|
+
});
|
|
1328
|
+
}
|
|
1329
|
+
assertSafePermissions() {
|
|
1330
|
+
const mode = statSync2(this.file).mode & 511;
|
|
1331
|
+
if ((mode & FORBIDDEN_WRITE_BITS) !== 0) {
|
|
1332
|
+
throw new TrustStorePermissionsError(this.file, mode);
|
|
1333
|
+
}
|
|
1334
|
+
}
|
|
1335
|
+
};
|
|
1336
|
+
|
|
1337
|
+
// src/config/custom-commands.ts
|
|
1338
|
+
import { readFileSync as readFileSync3, readdirSync, statSync as statSync3 } from "fs";
|
|
1339
|
+
import { basename, extname, join } from "path";
|
|
1340
|
+
|
|
1341
|
+
// src/config/frontmatter.ts
|
|
1342
|
+
var FENCE = "---";
|
|
1343
|
+
function splitFrontmatter(raw) {
|
|
1344
|
+
const lines = raw.split("\n");
|
|
1345
|
+
if (lines[0]?.trim() !== FENCE) return { frontmatter: [], body: raw };
|
|
1346
|
+
const closing = lines.indexOf(FENCE, 1);
|
|
1347
|
+
if (closing === -1) return void 0;
|
|
1348
|
+
return { frontmatter: lines.slice(1, closing), body: lines.slice(closing + 1).join("\n") };
|
|
1349
|
+
}
|
|
1350
|
+
function frontmatterValue(frontmatter, key) {
|
|
1351
|
+
const prefix = `${key}:`;
|
|
1352
|
+
for (const line of frontmatter) {
|
|
1353
|
+
if (!line.startsWith(prefix)) continue;
|
|
1354
|
+
const value = line.slice(prefix.length).trim();
|
|
1355
|
+
const unquoted = /^(["'])(.*)\1$/.exec(value);
|
|
1356
|
+
return unquoted?.[2] ?? value;
|
|
1357
|
+
}
|
|
1358
|
+
return void 0;
|
|
1359
|
+
}
|
|
1360
|
+
|
|
1361
|
+
// src/config/custom-commands.ts
|
|
1362
|
+
var COMMANDS_DIR = join(".theokit", "commands");
|
|
1363
|
+
var IGNORE_WARNING = () => void 0;
|
|
1364
|
+
function loadCustomCommands(input) {
|
|
1365
|
+
const warn = input.onWarn ?? IGNORE_WARNING;
|
|
1366
|
+
const loaded = /* @__PURE__ */ new Map();
|
|
1367
|
+
if (input.homeDir !== void 0) {
|
|
1368
|
+
for (const command of readCommandsDir(join(input.homeDir, COMMANDS_DIR), "user", warn)) {
|
|
1369
|
+
loaded.set(command.name, command);
|
|
1370
|
+
}
|
|
1371
|
+
}
|
|
1372
|
+
if (input.projectDir !== void 0) {
|
|
1373
|
+
if (!input.projectTrusted) {
|
|
1374
|
+
const pending = readCommandsDir(
|
|
1375
|
+
join(input.projectDir, COMMANDS_DIR),
|
|
1376
|
+
"project",
|
|
1377
|
+
IGNORE_WARNING
|
|
1378
|
+
);
|
|
1379
|
+
if (pending.length > 0) {
|
|
1380
|
+
warn(
|
|
1381
|
+
`${String(pending.length)} project command(s) found but the directory is not trusted \u2014 none were loaded. A command is a prompt that runs on your behalf.`
|
|
1382
|
+
);
|
|
1383
|
+
}
|
|
1384
|
+
} else {
|
|
1385
|
+
for (const command of readCommandsDir(
|
|
1386
|
+
join(input.projectDir, COMMANDS_DIR),
|
|
1387
|
+
"project",
|
|
1388
|
+
warn
|
|
1389
|
+
)) {
|
|
1390
|
+
const shadowed = loaded.get(command.name);
|
|
1391
|
+
if (shadowed !== void 0) {
|
|
1392
|
+
warn(
|
|
1393
|
+
`project command "${command.name}" overrides the user-level one at ${shadowed.path}.`
|
|
1394
|
+
);
|
|
1395
|
+
}
|
|
1396
|
+
loaded.set(command.name, command);
|
|
1397
|
+
}
|
|
1398
|
+
}
|
|
1399
|
+
}
|
|
1400
|
+
const builtins = new Set(input.builtinNames ?? []);
|
|
1401
|
+
const shadowedBuiltins = [...loaded.keys()].filter((name) => builtins.has(name));
|
|
1402
|
+
for (const name of shadowedBuiltins) {
|
|
1403
|
+
warn(
|
|
1404
|
+
`custom command "${name}" has the same name as a builtin. This loader does not choose between them \u2014 the router decides, because only it knows what its builtins do.`
|
|
1405
|
+
);
|
|
1406
|
+
}
|
|
1407
|
+
return {
|
|
1408
|
+
// Project first in the returned order, matching the precedence a reader would expect.
|
|
1409
|
+
commands: [...loaded.values()].sort(byProjectThenName),
|
|
1410
|
+
shadowedBuiltins
|
|
1411
|
+
};
|
|
1412
|
+
}
|
|
1413
|
+
function byProjectThenName(a, b) {
|
|
1414
|
+
if (a.source !== b.source) return a.source === "project" ? -1 : 1;
|
|
1415
|
+
return a.name.localeCompare(b.name);
|
|
1416
|
+
}
|
|
1417
|
+
function readCommandsDir(dir, source, warn) {
|
|
1418
|
+
let entries;
|
|
1419
|
+
try {
|
|
1420
|
+
entries = readdirSync(dir);
|
|
1421
|
+
} catch {
|
|
1422
|
+
return [];
|
|
1423
|
+
}
|
|
1424
|
+
entries.sort((a, b) => a.localeCompare(b));
|
|
1425
|
+
const commands = [];
|
|
1426
|
+
for (const entry of entries) {
|
|
1427
|
+
if (extname(entry) !== ".md") continue;
|
|
1428
|
+
const path = join(dir, entry);
|
|
1429
|
+
try {
|
|
1430
|
+
if (!statSync3(path).isFile()) continue;
|
|
1431
|
+
} catch {
|
|
1432
|
+
continue;
|
|
1433
|
+
}
|
|
1434
|
+
const command = parseCommandFile(path, entry, source, warn);
|
|
1435
|
+
if (command !== void 0) commands.push(command);
|
|
1436
|
+
}
|
|
1437
|
+
return commands;
|
|
1438
|
+
}
|
|
1439
|
+
function parseCommandFile(path, entry, source, warn) {
|
|
1440
|
+
let raw;
|
|
1441
|
+
try {
|
|
1442
|
+
raw = readFileSync3(path, "utf8");
|
|
1443
|
+
} catch {
|
|
1444
|
+
return void 0;
|
|
1445
|
+
}
|
|
1446
|
+
const parsed = splitFrontmatter(raw);
|
|
1447
|
+
if (parsed === void 0) {
|
|
1448
|
+
warn(`command frontmatter never closes, file skipped: ${path}`);
|
|
1449
|
+
return void 0;
|
|
1450
|
+
}
|
|
1451
|
+
const description = frontmatterValue(parsed.frontmatter, "description");
|
|
1452
|
+
return {
|
|
1453
|
+
name: basename(entry, ".md"),
|
|
1454
|
+
...description !== void 0 && { description },
|
|
1455
|
+
body: parsed.body,
|
|
1456
|
+
source,
|
|
1457
|
+
path
|
|
1458
|
+
};
|
|
1459
|
+
}
|
|
1460
|
+
|
|
1461
|
+
// src/config/instruction-tree.ts
|
|
1462
|
+
import { readFileSync as readFileSync4, readdirSync as readdirSync2, statSync as statSync4 } from "fs";
|
|
1463
|
+
import { join as join2, relative, resolve as resolve2 } from "path";
|
|
1464
|
+
import { assertNoSymlinkEscape } from "@theokit/agents";
|
|
1465
|
+
var DEFAULT_FILE_NAMES = ["THEO.md", "AGENTS.md"];
|
|
1466
|
+
var IGNORE_WARNING2 = () => void 0;
|
|
1467
|
+
function loadInstructionTree(input) {
|
|
1468
|
+
const warn = input.onWarn ?? IGNORE_WARNING2;
|
|
1469
|
+
const fileNames = input.fileNames ?? DEFAULT_FILE_NAMES;
|
|
1470
|
+
const cwd = resolve2(input.cwd);
|
|
1471
|
+
const blocks = [];
|
|
1472
|
+
const seenInodes = /* @__PURE__ */ new Set();
|
|
1473
|
+
let chars = 0;
|
|
1474
|
+
const visit = (dir, depth) => {
|
|
1475
|
+
if (depth > input.budget.maxDepth) return false;
|
|
1476
|
+
let entries;
|
|
1477
|
+
try {
|
|
1478
|
+
entries = readdirSync2(dir);
|
|
1479
|
+
} catch {
|
|
1480
|
+
return false;
|
|
1481
|
+
}
|
|
1482
|
+
for (const { entry, path, stats } of filesBeforeDirectories(dir, entries)) {
|
|
1483
|
+
const inode = `${String(stats.dev)}:${String(stats.ino)}`;
|
|
1484
|
+
if (seenInodes.has(inode)) continue;
|
|
1485
|
+
seenInodes.add(inode);
|
|
1486
|
+
if (stats.isDirectory()) {
|
|
1487
|
+
if (visit(path, depth + 1)) return true;
|
|
1488
|
+
continue;
|
|
1489
|
+
}
|
|
1490
|
+
if (!fileNames.includes(entry)) continue;
|
|
1491
|
+
try {
|
|
1492
|
+
;
|
|
1493
|
+
assertNoSymlinkEscape(path, cwd);
|
|
1494
|
+
} catch {
|
|
1495
|
+
warn(`instruction file escapes the project root and was skipped: ${relative(cwd, path)}`);
|
|
1496
|
+
continue;
|
|
1497
|
+
}
|
|
1498
|
+
if (blocks.length >= input.budget.maxFiles) {
|
|
1499
|
+
warn(`instruction budget: stopped at ${String(input.budget.maxFiles)} files`);
|
|
1500
|
+
return true;
|
|
1501
|
+
}
|
|
1502
|
+
const parsed = parseInstructionFile(path, cwd, warn);
|
|
1503
|
+
if (parsed === void 0) continue;
|
|
1504
|
+
if (chars + parsed.content.length > input.budget.maxChars) {
|
|
1505
|
+
warn(`instruction budget: stopped at ${String(input.budget.maxChars)} characters`);
|
|
1506
|
+
return true;
|
|
1507
|
+
}
|
|
1508
|
+
chars += parsed.content.length;
|
|
1509
|
+
blocks.push(parsed);
|
|
1510
|
+
}
|
|
1511
|
+
return false;
|
|
1512
|
+
};
|
|
1513
|
+
let truncated = false;
|
|
1514
|
+
for (const root of input.roots) {
|
|
1515
|
+
truncated = visit(resolve2(cwd, root), 0);
|
|
1516
|
+
if (truncated) break;
|
|
1517
|
+
}
|
|
1518
|
+
return { blocks, truncated, count: blocks.length };
|
|
1519
|
+
}
|
|
1520
|
+
function filesBeforeDirectories(dir, entries) {
|
|
1521
|
+
const found = [];
|
|
1522
|
+
for (const entry of [...entries].sort((a, b) => a.localeCompare(b))) {
|
|
1523
|
+
const path = join2(dir, entry);
|
|
1524
|
+
try {
|
|
1525
|
+
found.push({ entry, path, stats: statSync4(path) });
|
|
1526
|
+
} catch {
|
|
1527
|
+
continue;
|
|
1528
|
+
}
|
|
1529
|
+
}
|
|
1530
|
+
return [
|
|
1531
|
+
...found.filter((item) => !item.stats.isDirectory()),
|
|
1532
|
+
...found.filter((item) => item.stats.isDirectory())
|
|
1533
|
+
];
|
|
1534
|
+
}
|
|
1535
|
+
function parseInstructionFile(path, cwd, warn) {
|
|
1536
|
+
let raw;
|
|
1537
|
+
try {
|
|
1538
|
+
raw = readFileSync4(path, "utf8");
|
|
1539
|
+
} catch {
|
|
1540
|
+
return void 0;
|
|
1541
|
+
}
|
|
1542
|
+
const rel = relative(cwd, path);
|
|
1543
|
+
const parsed = splitFrontmatter(raw);
|
|
1544
|
+
if (parsed === void 0) {
|
|
1545
|
+
warn(`instruction frontmatter never closes, file skipped: ${rel}`);
|
|
1546
|
+
return void 0;
|
|
1547
|
+
}
|
|
1548
|
+
return {
|
|
1549
|
+
path: rel,
|
|
1550
|
+
content: parsed.body,
|
|
1551
|
+
scopes: parsePathsScope(parsed.frontmatter)
|
|
1552
|
+
};
|
|
1553
|
+
}
|
|
1554
|
+
function parsePathsScope(frontmatter) {
|
|
1555
|
+
const scopes = [];
|
|
1556
|
+
let inPaths = false;
|
|
1557
|
+
for (const line of frontmatter) {
|
|
1558
|
+
if (/^paths\s*:/.test(line)) {
|
|
1559
|
+
inPaths = true;
|
|
1560
|
+
const inline = line.slice(line.indexOf(":") + 1).trim();
|
|
1561
|
+
if (inline.startsWith("[")) {
|
|
1562
|
+
return inline.slice(1, inline.lastIndexOf("]")).split(",").map((item2) => item2.trim().replaceAll(/^["']|["']$/g, "")).filter((item2) => item2.length > 0);
|
|
1563
|
+
}
|
|
1564
|
+
continue;
|
|
1565
|
+
}
|
|
1566
|
+
if (!inPaths) continue;
|
|
1567
|
+
const item = /^ {0,32}- {0,32}(\S.*)$/.exec(line);
|
|
1568
|
+
if (item?.[1] === void 0) break;
|
|
1569
|
+
scopes.push(item[1].trim().replaceAll(/^["']|["']$/g, ""));
|
|
1570
|
+
}
|
|
1571
|
+
return scopes;
|
|
1572
|
+
}
|
|
1573
|
+
|
|
1574
|
+
// src/config/compose-instructions.ts
|
|
1575
|
+
var DEFAULT_SEPARATOR = "\n\n";
|
|
1576
|
+
var IGNORE_WARNING3 = () => void 0;
|
|
1577
|
+
function composeInstructions(base, sources, options) {
|
|
1578
|
+
const warn = options.onWarn ?? IGNORE_WARNING3;
|
|
1579
|
+
const separator = options.separator ?? DEFAULT_SEPARATOR;
|
|
1580
|
+
if (base.length >= options.maxChars) {
|
|
1581
|
+
warn(
|
|
1582
|
+
`instruction budget: the base prompt alone is ${String(base.length)} characters against a ceiling of ${String(options.maxChars)}. It was truncated and every source was dropped.`
|
|
1583
|
+
);
|
|
1584
|
+
return {
|
|
1585
|
+
text: base.slice(0, options.maxChars),
|
|
1586
|
+
dropped: sources.map((source) => source.name),
|
|
1587
|
+
trimmed: "base"
|
|
1588
|
+
};
|
|
1589
|
+
}
|
|
1590
|
+
const kept = [...sources];
|
|
1591
|
+
const dropped = [];
|
|
1592
|
+
let trimmed;
|
|
1593
|
+
const lengthOf = (list) => [base, ...list.map((source) => source.content)].join(separator).length;
|
|
1594
|
+
while (kept.length > 0 && lengthOf(kept) > options.maxChars) {
|
|
1595
|
+
const last = kept.at(-1);
|
|
1596
|
+
if (last === void 0) break;
|
|
1597
|
+
const withoutLast = kept.slice(0, -1);
|
|
1598
|
+
const room = options.maxChars - lengthOf(withoutLast) - separator.length;
|
|
1599
|
+
if (room > 0 && trimmed === void 0) {
|
|
1600
|
+
kept[kept.length - 1] = { ...last, content: last.content.slice(0, room) };
|
|
1601
|
+
trimmed = last.name;
|
|
1602
|
+
warn(
|
|
1603
|
+
`instruction budget: "${last.name}" was trimmed to ${String(room)} characters to fit the ${String(options.maxChars)}-character ceiling.`
|
|
1604
|
+
);
|
|
1605
|
+
break;
|
|
1606
|
+
}
|
|
1607
|
+
kept.pop();
|
|
1608
|
+
dropped.push(last.name);
|
|
1609
|
+
warn(`instruction budget: "${last.name}" was dropped to fit the ceiling.`);
|
|
1610
|
+
}
|
|
1611
|
+
return {
|
|
1612
|
+
text: [base, ...kept.map((source) => source.content)].join(separator),
|
|
1613
|
+
dropped,
|
|
1614
|
+
...trimmed !== void 0 && { trimmed }
|
|
1615
|
+
};
|
|
1616
|
+
}
|
|
1617
|
+
|
|
1618
|
+
// src/config/context-pressure.ts
|
|
1619
|
+
import { resolveEffectiveContextWindow } from "@theokit/agents";
|
|
1620
|
+
var DEFAULT_CONTEXT_PRESSURE_THRESHOLDS = {
|
|
1621
|
+
warn: 0.75,
|
|
1622
|
+
critical: 0.9
|
|
1623
|
+
};
|
|
1624
|
+
var ContextPressureThresholdError = class extends Error {
|
|
1625
|
+
name = "ContextPressureThresholdError";
|
|
1626
|
+
};
|
|
1627
|
+
function contextPressure(usedTokens, effectiveWindow, thresholds = DEFAULT_CONTEXT_PRESSURE_THRESHOLDS) {
|
|
1628
|
+
if (thresholds.warn >= thresholds.critical) {
|
|
1629
|
+
throw new ContextPressureThresholdError(
|
|
1630
|
+
`context pressure: \`warn\` (${String(thresholds.warn)}) must be below \`critical\` (${String(thresholds.critical)}), otherwise one of the two levels can never be reached.`
|
|
1631
|
+
);
|
|
1632
|
+
}
|
|
1633
|
+
if (!Number.isFinite(effectiveWindow) || effectiveWindow <= 0) return "ok";
|
|
1634
|
+
if (!Number.isFinite(usedTokens) || usedTokens <= 0) return "ok";
|
|
1635
|
+
const ratio = usedTokens / effectiveWindow;
|
|
1636
|
+
if (ratio >= thresholds.critical) return "critical";
|
|
1637
|
+
if (ratio >= thresholds.warn) return "warn";
|
|
1638
|
+
return "ok";
|
|
1639
|
+
}
|
|
1640
|
+
|
|
1181
1641
|
// src/server/index.ts
|
|
1182
1642
|
if (typeof globalThis !== "undefined") {
|
|
1183
1643
|
const WARN_KEY = "__theokit_server_umbrella_warn_emitted__";
|
|
@@ -1212,7 +1672,9 @@ export {
|
|
|
1212
1672
|
ChannelManager,
|
|
1213
1673
|
CodeModePermissionDeniedError,
|
|
1214
1674
|
ConsoleObservabilityAdapter,
|
|
1675
|
+
ContextPressureThresholdError,
|
|
1215
1676
|
CsrfReadinessStore,
|
|
1677
|
+
DEFAULT_CONTEXT_PRESSURE_THRESHOLDS,
|
|
1216
1678
|
DEFAULT_CSP,
|
|
1217
1679
|
DEFAULT_EXCLUDED_QUERY_PARAMS,
|
|
1218
1680
|
DEFAULT_MAX_BODY_BYTES,
|
|
@@ -1233,7 +1695,10 @@ export {
|
|
|
1233
1695
|
InvalidPluginShapeError,
|
|
1234
1696
|
JOB_MANIFEST_SCHEMA_VERSION,
|
|
1235
1697
|
JsonStdoutSink,
|
|
1698
|
+
LayerOutOfOrderError,
|
|
1699
|
+
LayeredConfig,
|
|
1236
1700
|
MAX_ERROR_HTML_BYTES,
|
|
1701
|
+
InMemoryUsageStorage2 as NeutralUsageStorage,
|
|
1237
1702
|
NodeAcpTransport,
|
|
1238
1703
|
NonRetryableError,
|
|
1239
1704
|
NoopObservabilityAdapter,
|
|
@@ -1252,6 +1717,8 @@ export {
|
|
|
1252
1717
|
TRACE_PARENT_HEADER,
|
|
1253
1718
|
TheoCloudObservabilityAdapter,
|
|
1254
1719
|
TheoError,
|
|
1720
|
+
TrustStore,
|
|
1721
|
+
TrustStorePermissionsError,
|
|
1255
1722
|
_resetCacheEngine,
|
|
1256
1723
|
_resetEnvCache,
|
|
1257
1724
|
_resetKeyCacheForTests,
|
|
@@ -1272,6 +1739,8 @@ export {
|
|
|
1272
1739
|
clearOidcCache,
|
|
1273
1740
|
compilePattern,
|
|
1274
1741
|
compileRouteRules,
|
|
1742
|
+
composeInstructions,
|
|
1743
|
+
contextPressure,
|
|
1275
1744
|
convertToAwsCron,
|
|
1276
1745
|
createACPTool,
|
|
1277
1746
|
createCacheEngine,
|
|
@@ -1317,6 +1786,7 @@ export {
|
|
|
1317
1786
|
deserializeResponse,
|
|
1318
1787
|
discoverOidcProvider,
|
|
1319
1788
|
dispatchWebhook,
|
|
1789
|
+
resolveEffectiveContextWindow as effectiveContextWindow,
|
|
1320
1790
|
encrypt,
|
|
1321
1791
|
enforceCsrf,
|
|
1322
1792
|
envelopeCodeToStatus,
|
|
@@ -1329,6 +1799,7 @@ export {
|
|
|
1329
1799
|
extractUniversalIssues,
|
|
1330
1800
|
findSuggestion,
|
|
1331
1801
|
fromUnknown,
|
|
1802
|
+
frontmatterValue,
|
|
1332
1803
|
generateBackupCodes,
|
|
1333
1804
|
generateManifest,
|
|
1334
1805
|
generateNonce,
|
|
@@ -1356,8 +1827,11 @@ export {
|
|
|
1356
1827
|
isInputError,
|
|
1357
1828
|
jsonTransformer,
|
|
1358
1829
|
levenshtein,
|
|
1830
|
+
listProviders,
|
|
1831
|
+
loadCustomCommands,
|
|
1359
1832
|
loadCustomErrorPages,
|
|
1360
1833
|
loadEnv,
|
|
1834
|
+
loadInstructionTree,
|
|
1361
1835
|
loadManifest,
|
|
1362
1836
|
logRequest,
|
|
1363
1837
|
matchDisallowed,
|
|
@@ -1376,8 +1850,11 @@ export {
|
|
|
1376
1850
|
readAppResource,
|
|
1377
1851
|
readRawBody,
|
|
1378
1852
|
recordAttempt,
|
|
1853
|
+
registerProvider,
|
|
1379
1854
|
requireAuth,
|
|
1855
|
+
resetProviderRegistry,
|
|
1380
1856
|
resolveAdapter,
|
|
1857
|
+
resolveProvider,
|
|
1381
1858
|
resolveRouteRule,
|
|
1382
1859
|
resolveTransformer,
|
|
1383
1860
|
revalidatePath,
|
|
@@ -1405,6 +1882,7 @@ export {
|
|
|
1405
1882
|
serveStaticFile,
|
|
1406
1883
|
serverErrorToEnvelope,
|
|
1407
1884
|
setCookie,
|
|
1885
|
+
splitFrontmatter,
|
|
1408
1886
|
streamAgentTurnInProcess,
|
|
1409
1887
|
superjsonTransformer,
|
|
1410
1888
|
timingSafeEqual,
|
|
@@ -1416,6 +1894,7 @@ export {
|
|
|
1416
1894
|
translateCronToCloudflare,
|
|
1417
1895
|
translateCronToDeno,
|
|
1418
1896
|
translateCronToVercel,
|
|
1897
|
+
tryResolveProvider,
|
|
1419
1898
|
uiMessageStreamResponse,
|
|
1420
1899
|
updateTag,
|
|
1421
1900
|
useDatabase,
|