@theokit/sdk 2.18.1 → 2.20.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 CHANGED
@@ -1,5 +1,19 @@
1
1
  # Changelog
2
2
 
3
+ ## 2.20.0
4
+
5
+ ### Minor Changes
6
+
7
+ - **M21 — `GenerateObjectOptions.structuringModel`.** An optional separate model for the structured-extraction step: when set, `model` first produces a free-text reasoned answer (phase 1), then `structuringModel` extracts the schema-matched object by calling the synthetic `output` tool over that answer (phase 2). Lets a large model reason while a cheap fast model structures. Absent ⇒ today's single-model flow (backward-compatible). Proven by a golden test asserting two distinct model ids in the run.
8
+ - **M22 — `createSkill()` + `SkillsSettings.skillsDir` / `.inline`.** `createSkill({ name, description, instructions })` defines a skill in TypeScript without a `SKILL.md` file; pass code-defined skills via `skills.inline` (they surface in `list()` + the `<skills>` block alongside filesystem skills, overriding a file skill of the same name). `skills.skillsDir` discovers skills from a custom directory instead of `<cwd>/.theokit/skills`. Both compose with the per-request enabled-name resolver.
9
+ - **M23 — `normalizeSchema()`.** Converts a schema from Zod (default), JSON Schema (passthrough), ArkType (`.toJsonSchema()`), or Valibot (via the optional `@valibot/to-json-schema` peer) to the internal JSON Schema the synthetic `output` tool uses. Zod stays the default and the documented recommendation; thin adapter, uniform parse-failure handling. A golden test per provider.
10
+
11
+ ## 2.19.0
12
+
13
+ ### Minor Changes
14
+
15
+ - `GenerateObjectOptions.errorStrategy` (`"throw" | "return-partial" | "return-raw"`, default `"throw"`) — controls what `Agent.generateObject` does when the model's output still fails schema validation after all retries. `"return-raw"` resolves with the raw unvalidated input; `"return-partial"` salvages best-effort (object schemas keep only fields that individually validate). Additive + backward-compatible (M14).
16
+
3
17
  ## 2.18.1
4
18
 
5
19
  ### Patch Changes
@@ -6807,23 +6807,27 @@ var init_skills_manager = __esm({
6807
6807
  "src/internal/runtime/skills/skills-manager.ts"() {
6808
6808
  init_discover_skills();
6809
6809
  SkillsManager = class {
6810
- constructor(cwd, _enabled, settingSourcesIncludeProject) {
6810
+ constructor(cwd, _enabled, settingSourcesIncludeProject, skillsDir, inline) {
6811
6811
  this.cwd = cwd;
6812
6812
  this.settingSourcesIncludeProject = settingSourcesIncludeProject;
6813
+ this.skillsDir = skillsDir;
6814
+ this.inline = inline;
6813
6815
  }
6814
6816
  cwd;
6815
6817
  settingSourcesIncludeProject;
6818
+ skillsDir;
6819
+ inline;
6816
6820
  skills = [];
6817
6821
  async initialize() {
6818
6822
  if (!this.settingSourcesIncludeProject) {
6819
- this.skills = [];
6823
+ this.skills = this.mergeInline([]);
6820
6824
  return;
6821
6825
  }
6822
6826
  await this.refresh();
6823
6827
  }
6824
6828
  async refresh() {
6825
- const skillsRoot = path.join(this.cwd, ".theokit", "skills");
6826
- this.skills = await discoverSkills(skillsRoot, {
6829
+ const skillsRoot = this.skillsDir ?? path.join(this.cwd, ".theokit", "skills");
6830
+ const discovered = await discoverSkills(skillsRoot, {
6827
6831
  onInvalidSkill: (info) => {
6828
6832
  process.stderr.write(
6829
6833
  `[theokit-sdk] skill ${info.name} skipped (${info.code}): ${info.message}
@@ -6831,6 +6835,13 @@ var init_skills_manager = __esm({
6831
6835
  );
6832
6836
  }
6833
6837
  });
6838
+ this.skills = this.mergeInline(discovered);
6839
+ }
6840
+ /** M22 — merge inline skills over discovered ones; inline wins on a name conflict. */
6841
+ mergeInline(discovered) {
6842
+ if (this.inline === void 0 || this.inline.length === 0) return discovered;
6843
+ const inlineNames = new Set(this.inline.map((s) => s.name));
6844
+ return [...discovered.filter((s) => !inlineNames.has(s.name)), ...this.inline];
6834
6845
  }
6835
6846
  list() {
6836
6847
  return Promise.resolve(this.skills);
@@ -6877,7 +6888,10 @@ function bootstrapSubmanagers(args) {
6877
6888
  out.skillsManager = new SkillsManager(
6878
6889
  args.workspaceCwd,
6879
6890
  args.options.skills?.enabled,
6880
- args.settingSourcesIncludeProject
6891
+ args.settingSourcesIncludeProject,
6892
+ // M22 — custom skills directory + inline (code-defined) skills.
6893
+ args.options.skills?.skillsDir,
6894
+ args.options.skills?.inline
6881
6895
  );
6882
6896
  const localSkills = out.skillsManager;
6883
6897
  out.skills = { list: () => localSkills.list() };
@@ -7863,22 +7877,23 @@ async function executeTool(inputs, resolved, call) {
7863
7877
  return { stdout: "", stderr: `Unknown tool ${call.name}`, exitCode: 127 };
7864
7878
  }
7865
7879
  if (resolved.origin === "shell") return runShellTool(inputs, call);
7866
- if (resolved.origin === "memory") return runMemoryTool(resolved, call);
7867
- if (resolved.origin === "custom") return runCustomTool(resolved, call, inputs.signal);
7880
+ if (resolved.origin === "memory") return runMemoryTool(resolved, call, inputs.context);
7881
+ if (resolved.origin === "custom")
7882
+ return runCustomTool(resolved, call, inputs.signal, inputs.context);
7868
7883
  return runMcpTool(inputs, resolved, call);
7869
7884
  }
7870
- async function runMemoryTool(resolved, call) {
7871
- return runHandlerTool("memory", resolved.memoryHandler, call);
7885
+ async function runMemoryTool(resolved, call, context) {
7886
+ return runHandlerTool("memory", resolved.memoryHandler, call, void 0, context);
7872
7887
  }
7873
- async function runCustomTool(resolved, call, signal) {
7874
- return runHandlerTool("custom", resolved.customHandler, call, signal);
7888
+ async function runCustomTool(resolved, call, signal, context) {
7889
+ return runHandlerTool("custom", resolved.customHandler, call, signal, context);
7875
7890
  }
7876
- async function runHandlerTool(kind, handler, call, signal) {
7891
+ async function runHandlerTool(kind, handler, call, signal, context) {
7877
7892
  if (handler === void 0) {
7878
7893
  return { stdout: "", stderr: `${kind} tool ${call.name} has no handler`, exitCode: 127 };
7879
7894
  }
7880
7895
  try {
7881
- const stdout = await handler(call.input, { signal });
7896
+ const stdout = await handler(call.input, { signal, context });
7882
7897
  return { stdout, stderr: "", exitCode: 0 };
7883
7898
  } catch (cause) {
7884
7899
  const message = cause instanceof Error ? cause.message : String(cause);
@@ -12383,6 +12398,9 @@ function buildLoopInputs(options, runId, userText) {
12383
12398
  // D318 — forward SendOptions.signal to the agent loop so streamLlmTurn
12384
12399
  // can attach it to the LLM `fetch({ signal })` call.
12385
12400
  ...options.sendOptions.signal !== void 0 ? { signal: options.sendOptions.signal } : {},
12401
+ // M7 — forward SendOptions.context to the loop so every tool handler receives
12402
+ // it on `ctx.context` (shared run config set once, e.g. projectRoot).
12403
+ ...options.sendOptions.context !== void 0 ? { context: options.sendOptions.context } : {},
12386
12404
  // #58 / #57 — forward the per-tool timeout + tool-result guard so a consumer
12387
12405
  // can enable them via SendOptions (not only internal AgentLoopInputs).
12388
12406
  ...options.sendOptions.perToolTimeoutMs !== void 0 ? { perToolTimeoutMs: options.sendOptions.perToolTimeoutMs } : {},
@@ -17153,6 +17171,37 @@ __export(generate_object_exports, {
17153
17171
  GenerateObjectError: () => GenerateObjectError,
17154
17172
  generateObjectImpl: () => generateObjectImpl
17155
17173
  });
17174
+ function salvagePartial(schema, raw) {
17175
+ if (typeof raw !== "object" || raw === null) return raw;
17176
+ const shape = schema.shape;
17177
+ if (shape === void 0 || typeof shape !== "object") return raw;
17178
+ const rawObj = raw;
17179
+ const out = {};
17180
+ for (const [key, fieldSchema] of Object.entries(shape)) {
17181
+ if (typeof fieldSchema?.safeParse !== "function") continue;
17182
+ const parsed = fieldSchema.safeParse(rawObj[key]);
17183
+ if (parsed.success) out[key] = parsed.data;
17184
+ }
17185
+ return out;
17186
+ }
17187
+ async function runReasoningPhase(options, deps) {
17188
+ const reasoningOptions = {
17189
+ model: options.model,
17190
+ local: options.local,
17191
+ ...options.systemPrompt !== void 0 ? { systemPrompt: options.systemPrompt } : {},
17192
+ ...options.apiKey !== void 0 ? { apiKey: options.apiKey } : {},
17193
+ ...options.providers !== void 0 ? { providers: options.providers } : {}
17194
+ };
17195
+ const reasoningAgent = await deps.create(reasoningOptions);
17196
+ try {
17197
+ const run = await reasoningAgent.send(options.prompt);
17198
+ const result = await run.wait();
17199
+ const text = result.result;
17200
+ return typeof text === "string" ? text : options.prompt;
17201
+ } finally {
17202
+ await disposeAndDeleteTransient(reasoningAgent, deps.delete);
17203
+ }
17204
+ }
17156
17205
  async function generateObjectImpl(options, deps) {
17157
17206
  const { jsonSchema, maxRetries, initialUsage } = setupStructuredOutput(
17158
17207
  options.schema,
@@ -17175,8 +17224,11 @@ async function generateObjectImpl(options, deps) {
17175
17224
  }
17176
17225
  throw new CaptureSentinel(input);
17177
17226
  });
17227
+ const reasoningText = options.structuringModel !== void 0 ? await runReasoningPhase(options, deps) : void 0;
17228
+ const structuringModel = options.structuringModel ?? options.model;
17229
+ const structuringPrompt = reasoningText ?? options.prompt;
17178
17230
  const agentOptions = buildTransientAgentOptions({
17179
- model: options.model,
17231
+ model: structuringModel,
17180
17232
  local: options.local,
17181
17233
  outputTool,
17182
17234
  ...options.systemPrompt !== void 0 ? { systemPrompt: options.systemPrompt } : {},
@@ -17185,7 +17237,7 @@ async function generateObjectImpl(options, deps) {
17185
17237
  });
17186
17238
  const agent = await deps.create(agentOptions);
17187
17239
  try {
17188
- const userMessage = buildToolPrompt(options.prompt);
17240
+ const userMessage = buildToolPrompt(structuringPrompt);
17189
17241
  let lastParseError;
17190
17242
  for (let attempt = 0; attempt <= maxRetries; attempt += 1) {
17191
17243
  capturedRaw = void 0;
@@ -17219,6 +17271,22 @@ async function generateObjectImpl(options, deps) {
17219
17271
  }
17220
17272
  lastParseError = parsed.error;
17221
17273
  }
17274
+ if (options.errorStrategy === "return-raw") {
17275
+ return {
17276
+ object: capturedRaw,
17277
+ raw: capturedRaw,
17278
+ usage: lastUsage,
17279
+ finishReason: "tool_use"
17280
+ };
17281
+ }
17282
+ if (options.errorStrategy === "return-partial") {
17283
+ return {
17284
+ object: salvagePartial(options.schema, capturedRaw),
17285
+ raw: capturedRaw,
17286
+ usage: lastUsage,
17287
+ finishReason: "tool_use"
17288
+ };
17289
+ }
17222
17290
  throw new GenerateObjectError(
17223
17291
  "parse_failed",
17224
17292
  "Schema parse failed after all retries.",