@theokit/sdk 2.24.0 → 2.25.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/eval.cjs CHANGED
@@ -3457,7 +3457,8 @@ function stripSecretsFromOptions(options) {
3457
3457
  local: serializeLocal(options.local),
3458
3458
  cloud: serializeCloud(options.cloud),
3459
3459
  memory: serializeMemory(options.memory),
3460
- skills: serializeEnabledList(options.skills),
3460
+ // SE22 — a SkillsResolver function isn't serializable; persist only the static form.
3461
+ skills: serializeEnabledList(typeof options.skills === "function" ? void 0 : options.skills),
3461
3462
  // Code-`Plugin` objects are closures and cannot be persisted (like custom
3462
3463
  // tools); only the named-enable settings form is serialized.
3463
3464
  plugins: serializeEnabledList(asPluginsSettings(options.plugins)),
@@ -3765,6 +3766,7 @@ function serializeCloud2(cloud) {
3765
3766
  return result;
3766
3767
  }
3767
3768
  function serializeSkills(skills) {
3769
+ if (typeof skills === "function") return void 0;
3768
3770
  if (skills?.enabled === void 0 || skills.enabled.length === 0) return void 0;
3769
3771
  return { enabled: [...skills.enabled] };
3770
3772
  }
@@ -5236,6 +5238,7 @@ init_errors();
5236
5238
  function validateCloudToolParity(options) {
5237
5239
  if (options.cloud === void 0) return;
5238
5240
  rejectFunctionSystemPrompt(options);
5241
+ rejectFunctionSkills(options);
5239
5242
  rejectStdioMcpLocalPaths(options);
5240
5243
  }
5241
5244
  function rejectFunctionSystemPrompt(options) {
@@ -5246,6 +5249,14 @@ function rejectFunctionSystemPrompt(options) {
5246
5249
  );
5247
5250
  }
5248
5251
  }
5252
+ function rejectFunctionSkills(options) {
5253
+ if (typeof options.skills === "function") {
5254
+ throw new ConfigurationError(
5255
+ "Cloud agents require skills as a static settings object. SkillsResolver functions can't run on PaaS \u2014 resolve to a SkillsSettings object before Agent.create() or move the dynamic logic into a hook rule.",
5256
+ { code: "cloud_incompatible_function_resolver" }
5257
+ );
5258
+ }
5259
+ }
5249
5260
  function rejectStdioMcpLocalPaths(options) {
5250
5261
  if (options.mcpServers === void 0) return;
5251
5262
  for (const [name, config] of Object.entries(options.mcpServers)) {
@@ -7102,9 +7113,223 @@ function parseFrontmatterFields(frontmatter) {
7102
7113
  return out;
7103
7114
  }
7104
7115
 
7116
+ // src/internal/runtime/skills/discover-skills.ts
7117
+ init_errors();
7118
+
7119
+ // src/internal/runtime/skills/skill-frontmatter.ts
7120
+ init_errors();
7121
+ init_yaml_frontmatter();
7122
+ function asString(v) {
7123
+ return typeof v === "string" ? v : void 0;
7124
+ }
7125
+ function toStringFields(raw) {
7126
+ const out = {};
7127
+ for (const [k, v] of Object.entries(raw)) out[k] = asString(v);
7128
+ return out;
7129
+ }
7130
+ function parseSkillFrontmatter(raw, fallbackName) {
7131
+ const fields = extractAndParseFrontmatter(raw, fallbackName);
7132
+ const name = resolveName(fields, fallbackName);
7133
+ ensureRequiredFields(fields, name);
7134
+ return buildFrontmatter(fields, name);
7135
+ }
7136
+ function stripSkillFrontmatter(raw) {
7137
+ const match = /^---\s*\n[\s\S]*?\n---\s*\n/.exec(raw);
7138
+ return (match === null ? raw : raw.slice(match[0].length)).trim();
7139
+ }
7140
+ function extractAndParseFrontmatter(raw, fallbackName) {
7141
+ const match = /^---\s*\n([\s\S]*?)\n---\s*\n/.exec(raw);
7142
+ if (match === null) {
7143
+ throw new ConfigurationError(`Skill ${fallbackName} is missing frontmatter`, {
7144
+ code: "missing_frontmatter"
7145
+ });
7146
+ }
7147
+ const frontmatter = match[1] ?? "";
7148
+ try {
7149
+ return toStringFields(parseSimpleYaml(frontmatter));
7150
+ } catch (cause) {
7151
+ const detail = cause instanceof Error ? cause.message : String(cause);
7152
+ throw new ConfigurationError(
7153
+ `Skill ${fallbackName} has malformed YAML frontmatter: ${detail}`,
7154
+ { code: "schema_invalid", cause }
7155
+ );
7156
+ }
7157
+ }
7158
+ function resolveName(fields, fallbackName) {
7159
+ if (hasContent(fields.name)) return fields.name;
7160
+ if (hasContent(fallbackName)) return fallbackName;
7161
+ throw new ConfigurationError("Skill at unknown path is missing required field: name", {
7162
+ code: "schema_invalid"
7163
+ });
7164
+ }
7165
+ function ensureRequiredFields(fields, name) {
7166
+ if (!hasContent(fields.description)) {
7167
+ throw new ConfigurationError(`Skill ${name} is missing required field: description`, {
7168
+ code: "schema_invalid"
7169
+ });
7170
+ }
7171
+ }
7172
+ function buildFrontmatter(fields, name) {
7173
+ const description = fields.description;
7174
+ if (description === void 0) {
7175
+ throw new ConfigurationError(`Skill ${name} missing description`, { code: "schema_invalid" });
7176
+ }
7177
+ const result = { name, description };
7178
+ if (hasContent(fields.category)) result.category = fields.category;
7179
+ const deps = parseDependencies(fields.dependencies);
7180
+ if (deps !== void 0) result.dependencies = deps;
7181
+ return result;
7182
+ }
7183
+ function parseDependencies(raw) {
7184
+ if (!hasContent(raw)) return void 0;
7185
+ const deps = raw.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
7186
+ return deps.length > 0 ? deps : void 0;
7187
+ }
7188
+ function hasContent(value) {
7189
+ return value !== void 0 && value.trim().length > 0;
7190
+ }
7191
+
7192
+ // src/internal/runtime/skills/discover-skills.ts
7193
+ async function discoverSkills(dir, options) {
7194
+ let entries;
7195
+ try {
7196
+ entries = await readWorkspaceDir(dir, "skills_read_error", "skills directory");
7197
+ } catch {
7198
+ return [];
7199
+ }
7200
+ const skills = [];
7201
+ for (const entry of entries) {
7202
+ if (!entry.isDirectory()) continue;
7203
+ let skillDir;
7204
+ try {
7205
+ skillDir = safePathJoin(dir, entry.name);
7206
+ assertNoSymlinkEscape(skillDir, dir);
7207
+ } catch {
7208
+ continue;
7209
+ }
7210
+ const skillPath = path.join(skillDir, "SKILL.md");
7211
+ let raw;
7212
+ try {
7213
+ raw = await promises.readFile(skillPath, "utf8");
7214
+ } catch {
7215
+ continue;
7216
+ }
7217
+ const skill = tryParseSkill(raw, entry.name, skillPath, options);
7218
+ if (skill !== void 0) skills.push(skill);
7219
+ }
7220
+ return skills;
7221
+ }
7222
+ function tryParseSkill(raw, fallbackName, source, options) {
7223
+ try {
7224
+ const frontmatter = parseSkillFrontmatter(raw, fallbackName);
7225
+ const skill = {
7226
+ name: frontmatter.name,
7227
+ description: frontmatter.description,
7228
+ source
7229
+ };
7230
+ if (frontmatter.category !== void 0) skill.category = frontmatter.category;
7231
+ if (frontmatter.dependencies !== void 0) skill.dependencies = frontmatter.dependencies;
7232
+ return skill;
7233
+ } catch (cause) {
7234
+ if (cause instanceof ConfigurationError) {
7235
+ options?.onInvalidSkill?.({
7236
+ name: fallbackName,
7237
+ source,
7238
+ code: cause.code ?? "unknown",
7239
+ message: cause.message
7240
+ });
7241
+ return void 0;
7242
+ }
7243
+ throw cause;
7244
+ }
7245
+ }
7246
+
7247
+ // src/internal/runtime/skills/skills-manager.ts
7248
+ var SkillsManager = class {
7249
+ constructor(cwd, _enabled, settingSourcesIncludeProject, skillsDir, inline) {
7250
+ this.cwd = cwd;
7251
+ this.settingSourcesIncludeProject = settingSourcesIncludeProject;
7252
+ this.skillsDir = skillsDir;
7253
+ this.inline = inline;
7254
+ }
7255
+ cwd;
7256
+ settingSourcesIncludeProject;
7257
+ skillsDir;
7258
+ inline;
7259
+ skills = [];
7260
+ async initialize() {
7261
+ if (!this.settingSourcesIncludeProject) {
7262
+ this.skills = this.mergeInline([]);
7263
+ return;
7264
+ }
7265
+ await this.refresh();
7266
+ }
7267
+ async refresh() {
7268
+ const skillsRoot = this.skillsDir ?? path.join(this.cwd, ".theokit", "skills");
7269
+ const discovered = await discoverSkills(skillsRoot, {
7270
+ onInvalidSkill: (info) => {
7271
+ process.stderr.write(
7272
+ `[theokit-sdk] skill ${info.name} skipped (${info.code}): ${info.message}
7273
+ `
7274
+ );
7275
+ }
7276
+ });
7277
+ this.skills = this.mergeInline(discovered);
7278
+ }
7279
+ /** M22 — merge inline skills over discovered ones; inline wins on a name conflict. */
7280
+ mergeInline(discovered) {
7281
+ if (this.inline === void 0 || this.inline.length === 0) return discovered;
7282
+ const inlineNames = new Set(this.inline.map((s) => s.name));
7283
+ return [...discovered.filter((s) => !inlineNames.has(s.name)), ...this.inline];
7284
+ }
7285
+ list() {
7286
+ return Promise.resolve(this.skills);
7287
+ }
7288
+ /**
7289
+ * SE20 — resolve a skill by name INCLUDING its body. Inline (`createSkill`)
7290
+ * skills carry `instructions` on the object; filesystem skills read the body
7291
+ * from their `source` SKILL.md (frontmatter stripped). `undefined` when no
7292
+ * enabled skill matches (malformed skills were already excluded at discovery).
7293
+ */
7294
+ async get(name) {
7295
+ const skill = this.skills.find((s) => s.name === name);
7296
+ if (skill === void 0) return void 0;
7297
+ const instructions = typeof skill.instructions === "string" ? skill.instructions : stripSkillFrontmatter(await promises.readFile(skill.source, "utf8"));
7298
+ const references = skill.references;
7299
+ return {
7300
+ name: skill.name,
7301
+ description: skill.description,
7302
+ instructions,
7303
+ ...references !== void 0 ? { references } : {}
7304
+ };
7305
+ }
7306
+ };
7307
+
7105
7308
  // src/internal/runtime/system-prompt/local-assembly.ts
7106
- async function buildSystemPromptContext(inputs, userText, memoryFacts) {
7107
- const skills = inputs.skillsManager !== void 0 ? await inputs.skillsManager.list() : [];
7309
+ async function resolveSendSkills(inputs, userText, memoryFacts) {
7310
+ const skills = inputs.options.skills;
7311
+ if (typeof skills !== "function") {
7312
+ return { manager: inputs.skillsManager, autoInject: skills?.autoInject ?? true };
7313
+ }
7314
+ const settings = await skills({
7315
+ agentId: inputs.agentId,
7316
+ cwd: inputs.workspaceCwd,
7317
+ model: inputs.model,
7318
+ userMessage: userText,
7319
+ memory: memoryFacts.map((fact) => ({ text: fact.text }))
7320
+ });
7321
+ const manager = new SkillsManager(
7322
+ inputs.workspaceCwd,
7323
+ settings.enabled,
7324
+ inputs.settingSourcesIncludeProject,
7325
+ settings.skillsDir,
7326
+ settings.inline
7327
+ );
7328
+ await manager.initialize();
7329
+ return { manager, autoInject: settings.autoInject ?? true };
7330
+ }
7331
+ async function buildSystemPromptContext(inputs, userText, memoryFacts, manager = inputs.skillsManager) {
7332
+ const skills = manager !== void 0 ? await manager.list() : [];
7108
7333
  return {
7109
7334
  agentId: inputs.agentId,
7110
7335
  cwd: inputs.workspaceCwd,
@@ -7115,10 +7340,11 @@ async function buildSystemPromptContext(inputs, userText, memoryFacts) {
7115
7340
  };
7116
7341
  }
7117
7342
  async function buildAssemblyContext(inputs, userText, baseSystemPrompt, memoryFacts, activeMemorySummary) {
7118
- const baseCtx = await buildSystemPromptContext(inputs, userText, memoryFacts);
7343
+ const resolved = await resolveSendSkills(inputs, userText, memoryFacts);
7344
+ const baseCtx = await buildSystemPromptContext(inputs, userText, memoryFacts, resolved.manager);
7119
7345
  const assemblyCtx = {
7120
7346
  ...baseCtx,
7121
- skillsAutoInject: inputs.options.skills?.autoInject ?? true,
7347
+ skillsAutoInject: resolved.autoInject,
7122
7348
  memoryAutoInject: inputs.options.memory?.autoInject ?? true
7123
7349
  };
7124
7350
  if (baseSystemPrompt !== void 0) assemblyCtx.baseSystemPrompt = baseSystemPrompt;
@@ -8370,176 +8596,6 @@ async function loadPluginManifestFromMarkdown(pluginsRoot, folderName) {
8370
8596
  return metadata;
8371
8597
  }
8372
8598
 
8373
- // src/internal/runtime/skills/discover-skills.ts
8374
- init_errors();
8375
-
8376
- // src/internal/runtime/skills/skill-frontmatter.ts
8377
- init_errors();
8378
- init_yaml_frontmatter();
8379
- function asString(v) {
8380
- return typeof v === "string" ? v : void 0;
8381
- }
8382
- function toStringFields(raw) {
8383
- const out = {};
8384
- for (const [k, v] of Object.entries(raw)) out[k] = asString(v);
8385
- return out;
8386
- }
8387
- function parseSkillFrontmatter(raw, fallbackName) {
8388
- const fields = extractAndParseFrontmatter(raw, fallbackName);
8389
- const name = resolveName(fields, fallbackName);
8390
- ensureRequiredFields(fields, name);
8391
- return buildFrontmatter(fields, name);
8392
- }
8393
- function extractAndParseFrontmatter(raw, fallbackName) {
8394
- const match = /^---\s*\n([\s\S]*?)\n---\s*\n/.exec(raw);
8395
- if (match === null) {
8396
- throw new ConfigurationError(`Skill ${fallbackName} is missing frontmatter`, {
8397
- code: "missing_frontmatter"
8398
- });
8399
- }
8400
- const frontmatter = match[1] ?? "";
8401
- try {
8402
- return toStringFields(parseSimpleYaml(frontmatter));
8403
- } catch (cause) {
8404
- const detail = cause instanceof Error ? cause.message : String(cause);
8405
- throw new ConfigurationError(
8406
- `Skill ${fallbackName} has malformed YAML frontmatter: ${detail}`,
8407
- { code: "schema_invalid", cause }
8408
- );
8409
- }
8410
- }
8411
- function resolveName(fields, fallbackName) {
8412
- if (hasContent(fields.name)) return fields.name;
8413
- if (hasContent(fallbackName)) return fallbackName;
8414
- throw new ConfigurationError("Skill at unknown path is missing required field: name", {
8415
- code: "schema_invalid"
8416
- });
8417
- }
8418
- function ensureRequiredFields(fields, name) {
8419
- if (!hasContent(fields.description)) {
8420
- throw new ConfigurationError(`Skill ${name} is missing required field: description`, {
8421
- code: "schema_invalid"
8422
- });
8423
- }
8424
- }
8425
- function buildFrontmatter(fields, name) {
8426
- const description = fields.description;
8427
- if (description === void 0) {
8428
- throw new ConfigurationError(`Skill ${name} missing description`, { code: "schema_invalid" });
8429
- }
8430
- const result = { name, description };
8431
- if (hasContent(fields.category)) result.category = fields.category;
8432
- const deps = parseDependencies(fields.dependencies);
8433
- if (deps !== void 0) result.dependencies = deps;
8434
- return result;
8435
- }
8436
- function parseDependencies(raw) {
8437
- if (!hasContent(raw)) return void 0;
8438
- const deps = raw.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
8439
- return deps.length > 0 ? deps : void 0;
8440
- }
8441
- function hasContent(value) {
8442
- return value !== void 0 && value.trim().length > 0;
8443
- }
8444
-
8445
- // src/internal/runtime/skills/discover-skills.ts
8446
- async function discoverSkills(dir, options) {
8447
- let entries;
8448
- try {
8449
- entries = await readWorkspaceDir(dir, "skills_read_error", "skills directory");
8450
- } catch {
8451
- return [];
8452
- }
8453
- const skills = [];
8454
- for (const entry of entries) {
8455
- if (!entry.isDirectory()) continue;
8456
- let skillDir;
8457
- try {
8458
- skillDir = safePathJoin(dir, entry.name);
8459
- assertNoSymlinkEscape(skillDir, dir);
8460
- } catch {
8461
- continue;
8462
- }
8463
- const skillPath = path.join(skillDir, "SKILL.md");
8464
- let raw;
8465
- try {
8466
- raw = await promises.readFile(skillPath, "utf8");
8467
- } catch {
8468
- continue;
8469
- }
8470
- const skill = tryParseSkill(raw, entry.name, skillPath, options);
8471
- if (skill !== void 0) skills.push(skill);
8472
- }
8473
- return skills;
8474
- }
8475
- function tryParseSkill(raw, fallbackName, source, options) {
8476
- try {
8477
- const frontmatter = parseSkillFrontmatter(raw, fallbackName);
8478
- const skill = {
8479
- name: frontmatter.name,
8480
- description: frontmatter.description,
8481
- source
8482
- };
8483
- if (frontmatter.category !== void 0) skill.category = frontmatter.category;
8484
- if (frontmatter.dependencies !== void 0) skill.dependencies = frontmatter.dependencies;
8485
- return skill;
8486
- } catch (cause) {
8487
- if (cause instanceof ConfigurationError) {
8488
- options?.onInvalidSkill?.({
8489
- name: fallbackName,
8490
- source,
8491
- code: cause.code ?? "unknown",
8492
- message: cause.message
8493
- });
8494
- return void 0;
8495
- }
8496
- throw cause;
8497
- }
8498
- }
8499
-
8500
- // src/internal/runtime/skills/skills-manager.ts
8501
- var SkillsManager = class {
8502
- constructor(cwd, _enabled, settingSourcesIncludeProject, skillsDir, inline) {
8503
- this.cwd = cwd;
8504
- this.settingSourcesIncludeProject = settingSourcesIncludeProject;
8505
- this.skillsDir = skillsDir;
8506
- this.inline = inline;
8507
- }
8508
- cwd;
8509
- settingSourcesIncludeProject;
8510
- skillsDir;
8511
- inline;
8512
- skills = [];
8513
- async initialize() {
8514
- if (!this.settingSourcesIncludeProject) {
8515
- this.skills = this.mergeInline([]);
8516
- return;
8517
- }
8518
- await this.refresh();
8519
- }
8520
- async refresh() {
8521
- const skillsRoot = this.skillsDir ?? path.join(this.cwd, ".theokit", "skills");
8522
- const discovered = await discoverSkills(skillsRoot, {
8523
- onInvalidSkill: (info) => {
8524
- process.stderr.write(
8525
- `[theokit-sdk] skill ${info.name} skipped (${info.code}): ${info.message}
8526
- `
8527
- );
8528
- }
8529
- });
8530
- this.skills = this.mergeInline(discovered);
8531
- }
8532
- /** M22 — merge inline skills over discovered ones; inline wins on a name conflict. */
8533
- mergeInline(discovered) {
8534
- if (this.inline === void 0 || this.inline.length === 0) return discovered;
8535
- const inlineNames = new Set(this.inline.map((s) => s.name));
8536
- return [...discovered.filter((s) => !inlineNames.has(s.name)), ...this.inline];
8537
- }
8538
- list() {
8539
- return Promise.resolve(this.skills);
8540
- }
8541
- };
8542
-
8543
8599
  // src/internal/runtime/local-agent/local-agent-bootstrap.ts
8544
8600
  function registerLocalAgent(args) {
8545
8601
  registerAgent({
@@ -8575,16 +8631,23 @@ function bootstrapSubmanagers(args) {
8575
8631
  );
8576
8632
  }
8577
8633
  if (args.options.skills !== void 0 || args.settingSourcesIncludeProject) {
8634
+ const staticSkills = typeof args.options.skills === "function" ? void 0 : args.options.skills;
8578
8635
  out.skillsManager = new SkillsManager(
8579
8636
  args.workspaceCwd,
8580
- args.options.skills?.enabled,
8637
+ staticSkills?.enabled,
8581
8638
  args.settingSourcesIncludeProject,
8582
8639
  // M22 — custom skills directory + inline (code-defined) skills.
8583
- args.options.skills?.skillsDir,
8584
- args.options.skills?.inline
8640
+ staticSkills?.skillsDir,
8641
+ staticSkills?.inline
8585
8642
  );
8586
8643
  const localSkills = out.skillsManager;
8587
- out.skills = { list: () => localSkills.list() };
8644
+ out.skills = {
8645
+ // Project to the public shape (name + description only). Inline skills carry
8646
+ // their body + references on the object; `list()` must never leak them —
8647
+ // the body is reachable exclusively through `get()`.
8648
+ list: async () => (await localSkills.list()).map((s) => ({ name: s.name, description: s.description })),
8649
+ get: (name) => localSkills.get(name)
8650
+ };
8588
8651
  }
8589
8652
  if (args.options.plugins !== void 0 || args.settingSourcesIncludePlugins) {
8590
8653
  out.pluginsManager = new PluginsManager(
@@ -16430,7 +16493,7 @@ var LocalAgent = class {
16430
16493
  }
16431
16494
  // biome-ignore format: G8 budget — thin accessor for the assembly inputs.
16432
16495
  assemblyInputs() {
16433
- return { agentId: this.agentId, workspaceCwd: this.workspaceCwd, model: this.model, options: this.options, context: this.context, skillsManager: this.skillsManager, systemPromptPipeline: this.systemPromptPipeline };
16496
+ return { agentId: this.agentId, workspaceCwd: this.workspaceCwd, model: this.model, options: this.options, context: this.context, skillsManager: this.skillsManager, settingSourcesIncludeProject: this.settingSourcesIncludeProject, systemPromptPipeline: this.systemPromptPipeline };
16434
16497
  }
16435
16498
  async resolveSystemPrompt(userText, options, memoryFacts) {
16436
16499
  const base = await resolveSystemPromptForSend(