@theokit/sdk 4.17.0 → 4.18.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,11 @@
1
1
  # Changelog
2
2
 
3
+ ## 4.17.1
4
+
5
+ ### Patch Changes
6
+
7
+ - fix(session): hydration REPLACES the cache from disk (source of truth) instead of skipping when non-empty — after an invalidation (compact/inject), an in-flight turn repopulating the cache with one message used to pin the parent to a 1-message context until restart (M51 review F4; race test added).
8
+
3
9
  ## 4.17.0
4
10
 
5
11
  ### Minor Changes
@@ -598,7 +598,7 @@ type GoalEvent = {
598
598
  prompt: string;
599
599
  } | {
600
600
  type: "status_change";
601
- status: "active" | "paused" | "completed" | "failed";
601
+ status: "active" | "paused" | "completed" | "failed" | "budget_limited" | "blocked";
602
602
  reason: string;
603
603
  };
604
604
  /**
@@ -608,8 +608,10 @@ type GoalEvent = {
608
608
  * @public
609
609
  */
610
610
  interface GoalResult {
611
- status: "completed" | "failed" | "paused";
611
+ status: "completed" | "failed" | "paused" | "budget_limited" | "blocked";
612
612
  turnsUsed: number;
613
+ /** M55 — tokens somados ao longo do loop (0 quando `usage` esteve ausente — fail-open). */
614
+ tokensUsed: number;
613
615
  finalResponse: string | undefined;
614
616
  }
615
617
  /**
@@ -625,8 +627,14 @@ type RunUntilIterator = AsyncGenerator<GoalEvent, GoalResult, void>;
625
627
  * @public
626
628
  */
627
629
  interface GoalOptions {
628
- /** Hard cap on iterations. Default `20`. */
630
+ /** Hard cap on iterations (safety net against runaway). Default `20`. */
629
631
  maxTurns?: number;
632
+ /**
633
+ * M55 — token budget (Codex ext/goal parity, tool.rs:454-465). Soma `run.wait().usage.totalTokens`
634
+ * por turno; ao cruzar, o loop para com status `budget_limited`. Omitido ⇒ ilimitado (só maxTurns).
635
+ * `usage` ausente nunca estoura o budget (fail-open).
636
+ */
637
+ tokenBudget?: number;
630
638
  /** Bail after N consecutive judge parse failures. Default `3` (ADR D121). */
631
639
  maxConsecutiveJudgeFailures?: number;
632
640
  /** Judge model identifier. Default `"openai/gpt-4o-mini"` (ADR D119). */
@@ -598,7 +598,7 @@ type GoalEvent = {
598
598
  prompt: string;
599
599
  } | {
600
600
  type: "status_change";
601
- status: "active" | "paused" | "completed" | "failed";
601
+ status: "active" | "paused" | "completed" | "failed" | "budget_limited" | "blocked";
602
602
  reason: string;
603
603
  };
604
604
  /**
@@ -608,8 +608,10 @@ type GoalEvent = {
608
608
  * @public
609
609
  */
610
610
  interface GoalResult {
611
- status: "completed" | "failed" | "paused";
611
+ status: "completed" | "failed" | "paused" | "budget_limited" | "blocked";
612
612
  turnsUsed: number;
613
+ /** M55 — tokens somados ao longo do loop (0 quando `usage` esteve ausente — fail-open). */
614
+ tokensUsed: number;
613
615
  finalResponse: string | undefined;
614
616
  }
615
617
  /**
@@ -625,8 +627,14 @@ type RunUntilIterator = AsyncGenerator<GoalEvent, GoalResult, void>;
625
627
  * @public
626
628
  */
627
629
  interface GoalOptions {
628
- /** Hard cap on iterations. Default `20`. */
630
+ /** Hard cap on iterations (safety net against runaway). Default `20`. */
629
631
  maxTurns?: number;
632
+ /**
633
+ * M55 — token budget (Codex ext/goal parity, tool.rs:454-465). Soma `run.wait().usage.totalTokens`
634
+ * por turno; ao cruzar, o loop para com status `budget_limited`. Omitido ⇒ ilimitado (só maxTurns).
635
+ * `usage` ausente nunca estoura o budget (fail-open).
636
+ */
637
+ tokenBudget?: number;
630
638
  /** Bail after N consecutive judge parse failures. Default `3` (ADR D121). */
631
639
  maxConsecutiveJudgeFailures?: number;
632
640
  /** Judge model identifier. Default `"openai/gpt-4o-mini"` (ADR D119). */
package/dist/cron.cjs CHANGED
@@ -5948,9 +5948,7 @@ async function hydrateSession(agentId, loc) {
5948
5948
  hydratedKeys.add(key);
5949
5949
  const persisted = await readSessionMessages(loc.store, agentId);
5950
5950
  if (persisted.length === 0) return;
5951
- if (!sessions.has(agentId) || sessions.get(agentId)?.length === 0) {
5952
- sessions.set(agentId, persisted);
5953
- }
5951
+ sessions.set(agentId, persisted);
5954
5952
  }
5955
5953
  async function flushSessionWrites() {
5956
5954
  while (pendingWrites.size > 0) {
@@ -6258,10 +6256,12 @@ __export(run_until_exports, {
6258
6256
  async function* runUntilImpl(agent, goal, options, deps) {
6259
6257
  const maxTurns = options?.maxTurns ?? 20;
6260
6258
  const maxFails = options?.maxConsecutiveJudgeFailures ?? 3;
6259
+ const tokenBudget = options?.tokenBudget;
6261
6260
  const signal = options?.signal;
6262
6261
  const isAborted = () => signal !== void 0 && signal.aborted;
6263
6262
  let turn = 0;
6264
6263
  let consecutiveFailures = 0;
6264
+ let tokensUsed = 0;
6265
6265
  let lastResponse = "";
6266
6266
  if (isAborted()) {
6267
6267
  yield {
@@ -6269,13 +6269,13 @@ async function* runUntilImpl(agent, goal, options, deps) {
6269
6269
  status: "paused",
6270
6270
  reason: "aborted via AbortSignal before first turn"
6271
6271
  };
6272
- return { status: "paused", turnsUsed: 0, finalResponse: void 0 };
6272
+ return { status: "paused", turnsUsed: 0, tokensUsed, finalResponse: void 0 };
6273
6273
  }
6274
6274
  yield { type: "status_change", status: "active", reason: "Goal started" };
6275
6275
  while (turn < maxTurns) {
6276
6276
  if (isAborted()) {
6277
6277
  yield { type: "status_change", status: "paused", reason: "aborted via AbortSignal" };
6278
- return { status: "paused", turnsUsed: turn, finalResponse: lastResponse || void 0 };
6278
+ return { status: "paused", turnsUsed: turn, tokensUsed, finalResponse: lastResponse || void 0 };
6279
6279
  }
6280
6280
  turn += 1;
6281
6281
  yield { type: "turn_start", turn, goal };
@@ -6283,6 +6283,8 @@ async function* runUntilImpl(agent, goal, options, deps) {
6283
6283
  const run = await agent.send(continuationPrompt);
6284
6284
  const result = await run.wait();
6285
6285
  lastResponse = result.result ?? "";
6286
+ const turnTokens = result.usage?.totalTokens;
6287
+ if (typeof turnTokens === "number") tokensUsed += turnTokens;
6286
6288
  yield { type: "agent_response", turn, content: lastResponse };
6287
6289
  const judgeOpts = {};
6288
6290
  if (options?.judgeModel !== void 0) judgeOpts.judgeModel = options.judgeModel;
@@ -6308,6 +6310,7 @@ async function* runUntilImpl(agent, goal, options, deps) {
6308
6310
  return {
6309
6311
  status: "failed",
6310
6312
  turnsUsed: turn,
6313
+ tokensUsed,
6311
6314
  finalResponse: lastResponse || void 0
6312
6315
  };
6313
6316
  }
@@ -6316,7 +6319,7 @@ async function* runUntilImpl(agent, goal, options, deps) {
6316
6319
  }
6317
6320
  if (judgment.verdict === "done") {
6318
6321
  yield { type: "status_change", status: "completed", reason: judgment.reason };
6319
- return { status: "completed", turnsUsed: turn, finalResponse: lastResponse || void 0 };
6322
+ return { status: "completed", turnsUsed: turn, tokensUsed, finalResponse: lastResponse || void 0 };
6320
6323
  }
6321
6324
  if (judgment.verdict === "skipped") {
6322
6325
  yield {
@@ -6324,7 +6327,15 @@ async function* runUntilImpl(agent, goal, options, deps) {
6324
6327
  status: "completed",
6325
6328
  reason: `skipped: ${judgment.reason}`
6326
6329
  };
6327
- return { status: "completed", turnsUsed: turn, finalResponse: lastResponse || void 0 };
6330
+ return { status: "completed", turnsUsed: turn, tokensUsed, finalResponse: lastResponse || void 0 };
6331
+ }
6332
+ if (tokenBudget !== void 0 && tokensUsed >= tokenBudget) {
6333
+ yield {
6334
+ type: "status_change",
6335
+ status: "budget_limited",
6336
+ reason: `token budget (${tokenBudget}) reached: ${tokensUsed} used`
6337
+ };
6338
+ return { status: "budget_limited", turnsUsed: turn, tokensUsed, finalResponse: lastResponse || void 0 };
6328
6339
  }
6329
6340
  yield { type: "continuation", turn, prompt: continuationPrompt };
6330
6341
  }
@@ -6333,13 +6344,32 @@ async function* runUntilImpl(agent, goal, options, deps) {
6333
6344
  status: "failed",
6334
6345
  reason: `max turns (${maxTurns}) exhausted`
6335
6346
  };
6336
- return { status: "failed", turnsUsed: turn, finalResponse: lastResponse || void 0 };
6347
+ return { status: "failed", turnsUsed: turn, tokensUsed, finalResponse: lastResponse || void 0 };
6337
6348
  }
6338
6349
  function composeContinuation(goal, lastResponse) {
6339
- return `Continue working toward the goal: ${goal}
6340
-
6341
- Your last response was:
6342
- ${lastResponse.slice(0, 1e3)}`;
6350
+ return [
6351
+ "Continue working toward the active goal.",
6352
+ "",
6353
+ `<objective>
6354
+ ${goal}
6355
+ </objective>`,
6356
+ "",
6357
+ "Continuation behavior:",
6358
+ "- This goal persists across turns. Keep the full objective intact; if it cannot be finished now,",
6359
+ " make concrete progress toward the real requested end state and do not redefine success around a",
6360
+ " smaller or easier task.",
6361
+ "",
6362
+ "Work from evidence:",
6363
+ "- Use the current worktree and external state as authoritative. Inspect the current state before",
6364
+ " relying on it. Improve, replace, or remove existing work as needed to satisfy the actual objective.",
6365
+ "",
6366
+ "Completion audit:",
6367
+ "- Before deciding the goal is achieved, treat completion as unproven and verify it against the actual",
6368
+ " current state, requirement by requirement. Treat uncertain or indirect evidence as not achieved.",
6369
+ "",
6370
+ `Your last response was:
6371
+ ${lastResponse.slice(0, 1e3)}`
6372
+ ].join("\n");
6343
6373
  }
6344
6374
  var init_run_until = __esm({
6345
6375
  "src/internal/runtime/lifecycle/run-until.ts"() {
@@ -18583,7 +18613,7 @@ function localAgentRunUntil(agent, goal, options) {
18583
18613
  status: "paused",
18584
18614
  reason: "runUntil() requires an explicit goal (durable objectives removed in v4.0)"
18585
18615
  };
18586
- return { status: "paused", turnsUsed: 0, finalResponse: void 0 };
18616
+ return { status: "paused", turnsUsed: 0, tokensUsed: 0, finalResponse: void 0 };
18587
18617
  }
18588
18618
  return yield* runUntilImpl2(agent, goal, options, await buildDeps());
18589
18619
  }