@theokit/sdk 2.1.0 → 2.3.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/index.cjs CHANGED
@@ -2799,6 +2799,71 @@ var init_agent_factory_registry = __esm({
2799
2799
  }
2800
2800
  });
2801
2801
 
2802
+ // src/internal/runtime/lifecycle/run-to-completion.ts
2803
+ var run_to_completion_exports = {};
2804
+ __export(run_to_completion_exports, {
2805
+ classifyRound: () => classifyRound,
2806
+ runToCompletionImpl: () => runToCompletionImpl
2807
+ });
2808
+ function isEmptyRound(result) {
2809
+ return (result.result ?? "").trim() === "";
2810
+ }
2811
+ function classifyRound(result, round, maxRounds, emptyStreak) {
2812
+ if (result.stoppedAtIterationLimit !== true) return "done";
2813
+ if (isEmptyRound(result) && emptyStreak >= 1) return "no_progress";
2814
+ if (round >= maxRounds) return "step_limit";
2815
+ return "continue";
2816
+ }
2817
+ function addUsage(acc, u) {
2818
+ if (u === void 0) return acc;
2819
+ const inputTokens = (acc?.inputTokens ?? 0) + u.inputTokens;
2820
+ const outputTokens = (acc?.outputTokens ?? 0) + u.outputTokens;
2821
+ const sumOpt = (a, b) => a === void 0 && b === void 0 ? void 0 : (a ?? 0) + (b ?? 0);
2822
+ return {
2823
+ inputTokens,
2824
+ outputTokens,
2825
+ totalTokens: inputTokens + outputTokens,
2826
+ cacheReadTokens: sumOpt(acc?.cacheReadTokens, u.cacheReadTokens),
2827
+ cacheWriteTokens: sumOpt(acc?.cacheWriteTokens, u.cacheWriteTokens),
2828
+ reasoningTokens: sumOpt(acc?.reasoningTokens, u.reasoningTokens)
2829
+ };
2830
+ }
2831
+ function buildResult(terminal, rounds, lastResult, usage) {
2832
+ return { terminal, rounds, lastResult, ...usage !== void 0 ? { usage } : {} };
2833
+ }
2834
+ async function stepRound(agent, prompt, sendOptions, round, maxRounds, state4) {
2835
+ const run = await agent.send(prompt, sendOptions);
2836
+ const result = await run.wait();
2837
+ const usage = addUsage(state4.usage, result.usage);
2838
+ const decision = classifyRound(result, round, maxRounds, state4.emptyStreak);
2839
+ if (decision !== "continue") return { terminal: buildResult(decision, round, result, usage) };
2840
+ const emptyStreak = isEmptyRound(result) ? state4.emptyStreak + 1 : 0;
2841
+ return { next: { usage, emptyStreak }, lastResult: result };
2842
+ }
2843
+ async function runToCompletionImpl(agent, message, options) {
2844
+ const maxRounds = options?.maxRounds ?? DEFAULT_MAX_ROUNDS;
2845
+ const continuationPrompt = options?.continuationPrompt ?? DEFAULT_CONTINUATION_PROMPT;
2846
+ const { onTruncated, signal, sendOptions } = options ?? {};
2847
+ let state4 = { usage: void 0, emptyStreak: 0 };
2848
+ for (let round = 0; ; round += 1) {
2849
+ const prompt = round === 0 ? message : continuationPrompt;
2850
+ const outcome = await stepRound(agent, prompt, sendOptions, round, maxRounds, state4);
2851
+ if ("terminal" in outcome) return outcome.terminal;
2852
+ state4 = outcome.next;
2853
+ await onTruncated?.({ round });
2854
+ if (signal?.aborted === true) {
2855
+ return buildResult("step_limit", round, outcome.lastResult, state4.usage);
2856
+ }
2857
+ }
2858
+ }
2859
+ var DEFAULT_MAX_ROUNDS, DEFAULT_CONTINUATION_PROMPT;
2860
+ var init_run_to_completion = __esm({
2861
+ "src/internal/runtime/lifecycle/run-to-completion.ts"() {
2862
+ DEFAULT_MAX_ROUNDS = 5;
2863
+ DEFAULT_CONTINUATION_PROMPT = "Continue from where you left off and finish the task. If it is already complete, give the final answer.";
2864
+ }
2865
+ });
2866
+
2802
2867
  // src/internal/runtime/lifecycle/fork-agent.ts
2803
2868
  var fork_agent_exports = {};
2804
2869
  __export(fork_agent_exports, {
@@ -2887,10 +2952,10 @@ var RingBuffer;
2887
2952
  var init_ring_buffer = __esm({
2888
2953
  "src/internal/task/ring-buffer.ts"() {
2889
2954
  RingBuffer = class {
2890
- constructor(cap) {
2891
- this.cap = cap;
2892
- if (!Number.isInteger(cap) || cap < 1) {
2893
- throw new Error(`RingBuffer capacity must be a positive integer, got ${cap}`);
2955
+ constructor(cap2) {
2956
+ this.cap = cap2;
2957
+ if (!Number.isInteger(cap2) || cap2 < 1) {
2958
+ throw new Error(`RingBuffer capacity must be a positive integer, got ${cap2}`);
2894
2959
  }
2895
2960
  }
2896
2961
  cap;
@@ -6807,8 +6872,7 @@ var FixtureRunBase = class {
6807
6872
  if (status === "error" && this.script.errorDetail !== void 0) {
6808
6873
  base.error = this.script.errorDetail;
6809
6874
  }
6810
- if (this.script.usage !== void 0) base.usage = this.script.usage;
6811
- if (this.script.cost !== void 0) base.cost = this.script.cost;
6875
+ applyScriptMetrics(base, this.script);
6812
6876
  return this.extendRunResult(applyExtraRunFields(base, this.script));
6813
6877
  }
6814
6878
  /** Subclasses override to attach runtime-specific fields (e.g. cloud git info). */
@@ -6842,6 +6906,11 @@ function makeNotifier() {
6842
6906
  });
6843
6907
  return { promise, resolve: resolve3 };
6844
6908
  }
6909
+ function applyScriptMetrics(base, script) {
6910
+ if (script.usage !== void 0) base.usage = script.usage;
6911
+ if (script.cost !== void 0) base.cost = script.cost;
6912
+ if (script.stoppedAtIterationLimit === true) base.stoppedAtIterationLimit = true;
6913
+ }
6845
6914
 
6846
6915
  // src/internal/runtime/cloud/cloud-run.ts
6847
6916
  function createCloudRun(options) {
@@ -7350,6 +7419,18 @@ var CloudAgent = class {
7350
7419
  "fork"
7351
7420
  );
7352
7421
  }
7422
+ /**
7423
+ * The continuation driver re-sends against a stateful local session; the
7424
+ * cloud runtime manages its own continuation policy server-side (M1 Phase 3).
7425
+ *
7426
+ * @public
7427
+ */
7428
+ runToCompletion() {
7429
+ throw new exports.UnsupportedRunOperationError(
7430
+ "Agent.runToCompletion() is not supported on cloud agents. Cloud runtime manages continuation server-side. Use a local agent.",
7431
+ "runToCompletion"
7432
+ );
7433
+ }
7353
7434
  /**
7354
7435
  * Personality presets require consistent server-side enforcement that
7355
7436
  * the cloud runtime (pre-release) does not yet provide. Reject explicitly
@@ -10121,7 +10202,7 @@ async function loadPluginManifestFromJson(manifestPath, folderName) {
10121
10202
  const record = parsed;
10122
10203
  const name = typeof record.name === "string" ? record.name : folderName;
10123
10204
  const version = typeof record.version === "string" ? record.version : "0.0.0";
10124
- const capabilities = Array.isArray(record.capabilities) ? record.capabilities.filter((cap) => typeof cap === "string") : [];
10205
+ const capabilities = Array.isArray(record.capabilities) ? record.capabilities.filter((cap2) => typeof cap2 === "string") : [];
10125
10206
  const source = manifestPath.slice(manifestPath.indexOf(".theokit/"));
10126
10207
  const metadata = { name, version, capabilities, source };
10127
10208
  if (typeof record.entry === "string") metadata.entry = record.entry;
@@ -10444,6 +10525,9 @@ var LocalRun = class extends FixtureRunBase {
10444
10525
  }
10445
10526
  };
10446
10527
 
10528
+ // src/internal/runtime/local-agent/real-local-run.ts
10529
+ init_errors();
10530
+
10447
10531
  // src/internal/runtime/budget/budget.ts
10448
10532
  var IterationBudget = class {
10449
10533
  #remaining;
@@ -11668,6 +11752,7 @@ async function runAgentLoop(inputs) {
11668
11752
  const ctx = await initLoopContext(inputs);
11669
11753
  ctxRef = ctx;
11670
11754
  const budget = inputs.budget ?? new IterationBudget({ maxIterations: inputs.maxIterations ?? 8 });
11755
+ let lastTurnDecision;
11671
11756
  while (budget.shouldContinue()) {
11672
11757
  if (inputs.budgetTracker !== void 0) {
11673
11758
  const decision2 = evaluateBudgetGate(inputs.budgetTracker);
@@ -11676,18 +11761,26 @@ async function runAgentLoop(inputs) {
11676
11761
  if (decision2.detail !== void 0) {
11677
11762
  ctx.error = { message: decision2.detail, code: decision2.reason ?? "budget" };
11678
11763
  }
11764
+ if (decision2.reason === "iteration_limit") {
11765
+ ctx.stoppedAtIterationLimit = true;
11766
+ }
11679
11767
  break;
11680
11768
  }
11681
11769
  }
11682
11770
  const usingGrace = budget.remaining <= 0 && !budget.graceCallUsed;
11683
11771
  if (usingGrace) budget.useGraceCall();
11684
11772
  const decision = await runIteration(inputs, ctx);
11773
+ lastTurnDecision = decision;
11685
11774
  if (decision === "done") break;
11686
11775
  if (decision === "error") {
11687
11776
  ctx.finalStatus = "error";
11688
11777
  break;
11689
11778
  }
11690
11779
  budget.consume();
11780
+ inputs.budgetTracker?.nextIteration?.();
11781
+ }
11782
+ if (lastTurnDecision === "continue" && budget.shouldContinue() === false) {
11783
+ ctx.stoppedAtIterationLimit = true;
11691
11784
  }
11692
11785
  if (budget.shouldContinue() === false && ctx.finalStatus === "finished" && ctx.finalText === "") {
11693
11786
  ctx.finalStatus = "error";
@@ -11718,7 +11811,8 @@ async function runAgentLoop(inputs) {
11718
11811
  conversation: ctx.conversation,
11719
11812
  ...usage !== void 0 ? { usage } : {},
11720
11813
  ...cost !== void 0 ? { cost } : {},
11721
- ...ctx.error !== void 0 ? { error: ctx.error } : {}
11814
+ ...ctx.error !== void 0 ? { error: ctx.error } : {},
11815
+ ...ctx.stoppedAtIterationLimit === true ? { stoppedAtIterationLimit: true } : {}
11722
11816
  };
11723
11817
  } finally {
11724
11818
  if (ctxRef !== void 0 && ctxRef.memoryProviderHandle !== void 0 && inputs.memoryProvider !== void 0) {
@@ -11775,8 +11869,8 @@ function handleToolErrorContinuation(inputs, ctx, toolResults) {
11775
11869
  const hasError = toolResults.some((part) => part.type === "tool_result" && part.isError === true);
11776
11870
  if (hasError) {
11777
11871
  ctx._consecutiveToolErrors = (ctx._consecutiveToolErrors ?? 0) + 1;
11778
- const cap = inputs.maxConsecutiveToolErrors ?? 3;
11779
- if (ctx._consecutiveToolErrors >= cap) return "error";
11872
+ const cap2 = inputs.maxConsecutiveToolErrors ?? 3;
11873
+ if (ctx._consecutiveToolErrors >= cap2) return "error";
11780
11874
  return "continue";
11781
11875
  }
11782
11876
  ctx._consecutiveToolErrors = 0;
@@ -14153,6 +14247,13 @@ function resolveRunProvider(options) {
14153
14247
  return { primary, effectiveModelId };
14154
14248
  }
14155
14249
  function buildLoopInputs(options, runId, userText) {
14250
+ const maxIterations = options.sendOptions.maxIterations;
14251
+ if (maxIterations !== void 0 && (!Number.isInteger(maxIterations) || maxIterations < 1)) {
14252
+ throw new exports.ConfigurationError(
14253
+ `SendOptions.maxIterations must be a positive integer, got ${maxIterations}`,
14254
+ { code: "invalid_max_iterations" }
14255
+ );
14256
+ }
14156
14257
  const { primary, effectiveModelId } = resolveRunProvider(options);
14157
14258
  const fallback = options.agentOptions.providers?.fallback;
14158
14259
  const apiKeys = options.agentOptions.providers?.apiKeys;
@@ -14191,6 +14292,9 @@ function buildLoopInputs(options, runId, userText) {
14191
14292
  // D318 — forward SendOptions.signal to the agent loop so streamLlmTurn
14192
14293
  // can attach it to the LLM `fetch({ signal })` call.
14193
14294
  ...options.sendOptions.signal !== void 0 ? { signal: options.sendOptions.signal } : {},
14295
+ // M1-2: per-send iteration ceiling (validated above). The loop reads
14296
+ // inputs.maxIterations (default 8 when unset).
14297
+ ...maxIterations !== void 0 ? { maxIterations } : {},
14194
14298
  // D315-D317 — tool lifecycle hooks (cost tracking + audit + retry/alert)
14195
14299
  ...options.agentOptions.onToolStart !== void 0 ? { onToolStart: options.agentOptions.onToolStart } : {},
14196
14300
  ...options.agentOptions.onToolEnd !== void 0 ? { onToolEnd: options.agentOptions.onToolEnd } : {},
@@ -14322,6 +14426,7 @@ var RealLocalRun = class extends FixtureRunBase {
14322
14426
  if (output.result.length > 0) this.script.result = output.result;
14323
14427
  if (output.usage !== void 0) this.script.usage = output.usage;
14324
14428
  if (output.cost !== void 0) this.script.cost = output.cost;
14429
+ if (output.stoppedAtIterationLimit === true) this.script.stoppedAtIterationLimit = true;
14325
14430
  if (output.error !== void 0 && this.script.errorDetail === void 0) {
14326
14431
  this.script.errorDetail = {
14327
14432
  message: output.error.message,
@@ -15259,7 +15364,7 @@ var MEMORY_GET_SCHEMA = {
15259
15364
  };
15260
15365
  var DEFAULT_MAX_TOTAL_CHARS = 16384;
15261
15366
  function createMemorySearchTool(opts) {
15262
- const cap = opts.maxTotalChars ?? DEFAULT_MAX_TOTAL_CHARS;
15367
+ const cap2 = opts.maxTotalChars ?? DEFAULT_MAX_TOTAL_CHARS;
15263
15368
  return {
15264
15369
  name: "memory_search",
15265
15370
  description: SEARCH_DESCRIPTION,
@@ -15276,7 +15381,7 @@ function createMemorySearchTool(opts) {
15276
15381
  ...sources !== void 0 ? { sources } : {}
15277
15382
  };
15278
15383
  const hits = await opts.index.search(query, searchOptions);
15279
- return JSON.stringify(capByTotalChars(hits, cap));
15384
+ return JSON.stringify(capByTotalChars(hits, cap2));
15280
15385
  }
15281
15386
  };
15282
15387
  }
@@ -15844,6 +15949,13 @@ function localAgentRunUntil(agent, goal, options) {
15844
15949
  }
15845
15950
  return wrap();
15846
15951
  }
15952
+ function localAgentRunToCompletion(agent, message, options) {
15953
+ async function run() {
15954
+ const { runToCompletionImpl: runToCompletionImpl2 } = await Promise.resolve().then(() => (init_run_to_completion(), run_to_completion_exports));
15955
+ return runToCompletionImpl2({ send: (m, o) => agent.send(m, o) }, message, options);
15956
+ }
15957
+ return run();
15958
+ }
15847
15959
  async function localAgentFork(parent, options) {
15848
15960
  const { forkAgentImpl: forkAgentImpl2 } = await Promise.resolve().then(() => (init_fork_agent(), fork_agent_exports));
15849
15961
  const { getAgentFacade: getAgentFacade2 } = await Promise.resolve().then(() => (init_agent_factory_registry(), agent_factory_registry_exports));
@@ -15907,8 +16019,8 @@ async function applyPreUserSendHook(args) {
15907
16019
  ...args.options.memoryContext !== void 0 ? { memoryContext: args.options.memoryContext } : {},
15908
16020
  ...args.sendOptions.signal !== void 0 ? { signal: args.sendOptions.signal } : {}
15909
16021
  };
15910
- const cap = args.options.maxRecallContextBytes ?? DEFAULT_MAX_RECALL_BYTES;
15911
- const recalled = await args.pluginManager.runPreUserSendHooks(ctx, cap);
16022
+ const cap2 = args.options.maxRecallContextBytes ?? DEFAULT_MAX_RECALL_BYTES;
16023
+ const recalled = await args.pluginManager.runPreUserSendHooks(ctx, cap2);
15912
16024
  if (recalled === void 0 || recalled.length === 0) return args.original;
15913
16025
  const wrapped = `<memory-context>
15914
16026
  ${recalled}
@@ -16384,6 +16496,10 @@ var LocalAgent = class {
16384
16496
  fork(options) {
16385
16497
  return localAgentFork({ agentId: this.agentId, options: this.options, personalitySlugSnapshot: this.personalityStore.active(this.agentId) }, options);
16386
16498
  }
16499
+ // biome-ignore format: G8 budget — see runUntil comment above.
16500
+ runToCompletion(message, options) {
16501
+ return localAgentRunToCompletion(this, message, options);
16502
+ }
16387
16503
  };
16388
16504
  function resolveCwd(cwd) {
16389
16505
  return (Array.isArray(cwd) ? cwd[0] : cwd) ?? process.cwd();
@@ -17899,6 +18015,77 @@ function createCounterBudgetTracker(options = {}) {
17899
18015
  };
17900
18016
  }
17901
18017
 
18018
+ // src/internal/runtime/context/replay-history.ts
18019
+ var CHARS_PER_TOKEN = 4;
18020
+ var DEFAULT_RESERVE_TOKENS = 8e3;
18021
+ function finiteOr(value, fallback) {
18022
+ return Number.isFinite(value) ? value : fallback;
18023
+ }
18024
+ function charBudget(options) {
18025
+ const window = finiteOr(options.contextWindowTokens, 0);
18026
+ const reserve = finiteOr(options.reserveTokens ?? DEFAULT_RESERVE_TOKENS, DEFAULT_RESERVE_TOKENS);
18027
+ return Math.max(0, window - reserve) * CHARS_PER_TOKEN;
18028
+ }
18029
+ function assistantText2(event) {
18030
+ return event.message.content.filter((block) => block.type === "text").map((block) => block.text).join("");
18031
+ }
18032
+ function stringifyPayload(value) {
18033
+ if (value === void 0) return "";
18034
+ return typeof value === "string" ? value : JSON.stringify(value) ?? "";
18035
+ }
18036
+ function cap(content, perItemCap) {
18037
+ return truncateWithMarker(content, Math.max(0, perItemCap)).finalContent;
18038
+ }
18039
+ function mapEvent(event, perItemCap) {
18040
+ if (event.type === "assistant") {
18041
+ const text = assistantText2(event);
18042
+ return text.length > 0 ? { message: { role: "assistant", content: cap(text, perItemCap) } } : null;
18043
+ }
18044
+ if (event.type === "tool_call") {
18045
+ if (event.status === "running") {
18046
+ const content2 = cap(`[tool_call ${event.name}] ${stringifyPayload(event.args)}`, perItemCap);
18047
+ return { message: { role: "tool_call", content: content2 }, pairId: event.call_id };
18048
+ }
18049
+ const content = cap(
18050
+ `[tool_result ${event.name}] ${stringifyPayload(event.result)}`,
18051
+ perItemCap
18052
+ );
18053
+ return { message: { role: "tool_result", content }, pairId: event.call_id };
18054
+ }
18055
+ return null;
18056
+ }
18057
+ function evictionIndices(turns) {
18058
+ const head = turns[0];
18059
+ if (head?.pairId === void 0) return [0];
18060
+ const indices = [];
18061
+ for (let i = 0; i < turns.length; i += 1) {
18062
+ if (turns[i]?.pairId === head.pairId) indices.push(i);
18063
+ }
18064
+ return indices;
18065
+ }
18066
+ function totalChars(turns) {
18067
+ return turns.reduce((n, t) => n + t.message.content.length, 0);
18068
+ }
18069
+ function trimToBudget(turns, budgetChars) {
18070
+ const kept = [...turns];
18071
+ while (kept.length > 1 && totalChars(kept) > budgetChars) {
18072
+ const evict = evictionIndices(kept);
18073
+ if (kept.length - evict.length < 1) break;
18074
+ for (const idx of evict.sort((a, b) => b - a)) kept.splice(idx, 1);
18075
+ }
18076
+ return kept;
18077
+ }
18078
+ function buildReplayHistory(base, events, options) {
18079
+ const budgetChars = charBudget(options);
18080
+ const perItemCap = Math.max(0, options.perItemCap ?? Math.floor(budgetChars / 2));
18081
+ const turns = base.map((message) => ({ message }));
18082
+ for (const event of events) {
18083
+ const turn = mapEvent(event, perItemCap);
18084
+ if (turn !== null) turns.push(turn);
18085
+ }
18086
+ return trimToBudget(turns, budgetChars).map((t) => t.message);
18087
+ }
18088
+
17902
18089
  // src/internal/runtime/memory/memory-provider-noop.ts
17903
18090
  var NOOP_ADAPTER_ID = "noop";
17904
18091
  var NOOP_CAPABILITIES = {
@@ -19283,6 +19470,7 @@ exports.Security = Security;
19283
19470
  exports.Task = Task;
19284
19471
  exports.Theokit = Theokit;
19285
19472
  exports.UsageAccumulator = UsageAccumulator;
19473
+ exports.buildReplayHistory = buildReplayHistory;
19286
19474
  exports.chargeAndCheckThresholds = chargeAndCheckThresholds;
19287
19475
  exports.computeCost = computeCost;
19288
19476
  exports.createAgentFactory = createAgentFactory;