flavor-code 1.4.0-beta.4 → 1.4.0-beta.5

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.
@@ -5628,10 +5628,6 @@ var ContextManager = class _ContextManager {
5628
5628
  }
5629
5629
  return microcompact.changed;
5630
5630
  }
5631
- if (this.#consecutiveAutoCompactFailures >= 3) {
5632
- rollbackMicrocompact();
5633
- return false;
5634
- }
5635
5631
  try {
5636
5632
  const compacted = await this.#compactConversation(signal);
5637
5633
  if (compacted) {
@@ -6095,10 +6091,10 @@ function within(root, candidate) {
6095
6091
 
6096
6092
  // src/context/summarizer.ts
6097
6093
  async function summarizeWithModel(options) {
6098
- const maxAttempts = options.maxPromptTooLongAttempts ?? 3;
6099
- if (!Number.isInteger(maxAttempts) || maxAttempts <= 0) throw new Error("maxPromptTooLongAttempts must be positive");
6100
6094
  options.signal?.throwIfAborted();
6101
6095
  let groups = groupMessagesByApiRound(options.messages);
6096
+ const maxAttempts = options.maxPromptTooLongAttempts ?? Math.max(1, Math.ceil(Math.log2(Math.max(1, groups.length))) + 1);
6097
+ if (!Number.isInteger(maxAttempts) || maxAttempts <= 0) throw new Error("maxPromptTooLongAttempts must be positive");
6102
6098
  let lastOverflow;
6103
6099
  let progress = 20;
6104
6100
  reportProgress(options.onProgress, progress);
@@ -6139,7 +6135,7 @@ async function summarizeWithModel(options) {
6139
6135
  if (terminalError.code !== "context_overflow") throw terminalError;
6140
6136
  lastOverflow = terminalError;
6141
6137
  if (attempt >= maxAttempts || groups.length <= 1) throw terminalError;
6142
- groups = groups.slice(1);
6138
+ groups = groups.slice(Math.max(1, Math.floor(groups.length / 2)));
6143
6139
  }
6144
6140
  throw lastOverflow ?? new Error("Compact summary failed");
6145
6141
  }
@@ -11503,7 +11499,7 @@ var MEMORY_RESTART_EXIT_CODE = 75;
11503
11499
  var MEMORY_RESTART_MARKER_FILE = "memory-restart.json";
11504
11500
  var MEMORY_RESTART_WINDOW_MS = 60 * 60 * 1e3;
11505
11501
  var MEMORY_RESTART_MIN_GAP_MS = 5 * 60 * 1e3;
11506
- var MEMORY_RESTART_CONTINUATION_TTL_MS = 10 * 60 * 1e3;
11502
+ var MEMORY_RESTART_CONTINUATION_TTL_MS = 24 * 60 * 60 * 1e3;
11507
11503
  var rotationActive = false;
11508
11504
  function markMemoryRotation() {
11509
11505
  rotationActive = true;
@@ -11709,7 +11705,7 @@ function extendBudget(input, dimensions, approvedAt) {
11709
11705
  return LoopStateSchema.parse({
11710
11706
  ...state,
11711
11707
  updatedAt: approvedAt,
11712
- budget: { ...state.budget, cycleCheckpoint, tokenCheckpoint, approvals }
11708
+ budget: { ...state.budget, cycleCheckpoint, tokenCheckpoint, approvals: approvals.slice(-64) }
11713
11709
  });
11714
11710
  }
11715
11711
  function rejectBudget(input, terminalReason) {
@@ -11836,6 +11832,7 @@ ${lastCompletedCycle.verification.summary}`;
11836
11832
  });
11837
11833
  let workerError;
11838
11834
  let workerMemoryPressure = false;
11835
+ let workerIterationLimit = false;
11839
11836
  for await (const event of this.#options.runWorker({
11840
11837
  goal,
11841
11838
  cycle,
@@ -11848,8 +11845,9 @@ ${lastCompletedCycle.verification.summary}`;
11848
11845
  cycleOutputTokens += event.outputTokens;
11849
11846
  }
11850
11847
  if (event.type === "error") {
11851
- workerError = event.error.message;
11848
+ workerError = event.error;
11852
11849
  if (event.error.code === "memory_pressure") workerMemoryPressure = true;
11850
+ if (event.error.code === "iteration_limit") workerIterationLimit = true;
11853
11851
  }
11854
11852
  if (event.type === "text") workerText = `${workerText}${event.text}`.slice(-16e3);
11855
11853
  if (this.#options.hallucinationGuard !== void 0) {
@@ -11862,7 +11860,8 @@ ${lastCompletedCycle.verification.summary}`;
11862
11860
  yield { type: "worker-event", event };
11863
11861
  }
11864
11862
  if (workerMemoryPressure) return;
11865
- if (workerError !== void 0) {
11863
+ if (workerError !== void 0 && !workerIterationLimit && !isResumableWorkerError(workerError)) {
11864
+ const status = workerError.code === "cancelled" ? "cancelled" : "failed";
11866
11865
  state = LoopStateSchema.parse({
11867
11866
  ...state,
11868
11867
  budget: {
@@ -11872,8 +11871,8 @@ ${lastCompletedCycle.verification.summary}`;
11872
11871
  outputTokens: state.budget.outputTokens + cycleOutputTokens
11873
11872
  }
11874
11873
  });
11875
- state = await this.#terminal(state, "failed", workerError, now());
11876
- yield { type: "loop-terminal", loopId, status: "failed", reason: workerError };
11874
+ state = await this.#terminal(state, status, workerError.message, now());
11875
+ yield { type: "loop-terminal", loopId, status, reason: workerError.message };
11877
11876
  return;
11878
11877
  }
11879
11878
  state = LoopStateSchema.parse({
@@ -11916,13 +11915,7 @@ ${lastCompletedCycle.verification.summary}`;
11916
11915
  const report = await this.#options.hallucinationGuard.evaluate(goal, workerText);
11917
11916
  await this.#options.onMemoryCheckpoint?.(loopId);
11918
11917
  if (memoryRotationActive()) return;
11919
- if (!report.passed) {
11920
- const guardReason = report.blockingReasons.join("; ") || "Hallucination guard blocked completion.";
11921
- state = await this.#terminal(state, "failed", guardReason, now());
11922
- yield { type: "loop-terminal", loopId, status: "failed", reason: guardReason };
11923
- return;
11924
- }
11925
- guardWarnings = report.warnings;
11918
+ guardWarnings = [...report.blockingReasons, ...report.warnings];
11926
11919
  } catch {
11927
11920
  }
11928
11921
  }
@@ -11944,7 +11937,7 @@ ${lastCompletedCycle.verification.summary}`;
11944
11937
  outputTokens: cycleOutputTokens,
11945
11938
  workspaceFingerprint: fingerprint2,
11946
11939
  verification: evidence
11947
- }],
11940
+ }].slice(-64),
11948
11941
  pendingCycle: null
11949
11942
  });
11950
11943
  await this.#options.persistence.save(state);
@@ -11973,8 +11966,8 @@ ${evidence.summary}`;
11973
11966
  repeatedFailures = failureSignature === previousFailureSignature ? repeatedFailures + 1 : 1;
11974
11967
  previousFailureSignature = failureSignature;
11975
11968
  previousVerification = evidence;
11976
- if (repeatedFailures >= 3) {
11977
- const reason = "The same verification failure repeated three times without material workspace progress.";
11969
+ if (this.#options.maxNoProgressCycles !== void 0 && repeatedFailures >= this.#options.maxNoProgressCycles) {
11970
+ const reason = `The same verification failure repeated ${this.#options.maxNoProgressCycles} times without material workspace progress.`;
11978
11971
  state = await this.#terminal(state, "no_progress", reason, now());
11979
11972
  yield { type: "loop-terminal", loopId, status: "no_progress", reason };
11980
11973
  return;
@@ -12035,6 +12028,9 @@ ${evidence.summary}`;
12035
12028
  return state;
12036
12029
  }
12037
12030
  };
12031
+ function isResumableWorkerError(error) {
12032
+ return error.code !== "authentication" && error.code !== "model_not_found" && error.code !== "cancelled" && error.code !== "memory_pressure";
12033
+ }
12038
12034
  function countTrailingFailureSignature(cycles, signature) {
12039
12035
  let count = 0;
12040
12036
  for (let index = cycles.length - 1; index >= 0; index -= 1) {
@@ -12523,26 +12519,37 @@ var GoalOrchestrator = class {
12523
12519
  let plan;
12524
12520
  let planPath2;
12525
12521
  if (state.plan === null) {
12526
- try {
12527
- signal.throwIfAborted();
12528
- await memoryCheckpoint();
12529
- plan = await runPlanner({
12530
- registry: this.#options.registry,
12531
- modelId: this.#options.plannerModelId,
12532
- objective: state.objective,
12533
- signal,
12534
- onProgress: memoryCheckpoint
12535
- });
12536
- planPath2 = await writePlanFile(workspace, plan);
12537
- await persistState({ phase: "executing", plan, planPath: planPath2, contractHash: contractHash(state.objective, plan) });
12538
- yield { type: "goal-plan-created", plan, planPath: planPath2 };
12539
- } catch (error) {
12540
- if (memoryRotationActive()) return;
12541
- const reason = `Goal planning failed: ${message(error)}`;
12542
- await persistState({ phase: "complete", status: "failed" });
12543
- yield { type: "goal-plan-failed", reason };
12544
- yield { type: "goal-failed", reason };
12545
- return;
12522
+ let planningAttempt = 0;
12523
+ while (true) {
12524
+ try {
12525
+ signal.throwIfAborted();
12526
+ await memoryCheckpoint();
12527
+ plan = await runPlanner({
12528
+ registry: this.#options.registry,
12529
+ modelId: this.#options.plannerModelId,
12530
+ objective: state.objective,
12531
+ signal,
12532
+ onProgress: memoryCheckpoint
12533
+ });
12534
+ planPath2 = await writePlanFile(workspace, plan);
12535
+ await persistState({ phase: "executing", plan, planPath: planPath2, contractHash: contractHash(state.objective, plan) });
12536
+ yield { type: "goal-plan-created", plan, planPath: planPath2 };
12537
+ break;
12538
+ } catch (error) {
12539
+ if (memoryRotationActive()) return;
12540
+ const normalized = normalizeProviderError(error);
12541
+ if (!isResumableStageError(normalized)) {
12542
+ const reason = `Goal planning failed: ${normalized.message}`;
12543
+ await persistState({ phase: "complete", status: "failed" });
12544
+ yield { type: "goal-plan-failed", reason };
12545
+ yield { type: "goal-failed", reason };
12546
+ return;
12547
+ }
12548
+ planningAttempt += 1;
12549
+ const delayMs = stageRetryDelay(this.#options, planningAttempt);
12550
+ yield { type: "goal-stage-retry", stage: "planning", attempt: planningAttempt, reason: normalized.message, delayMs };
12551
+ await waitForStageRetry(delayMs, signal);
12552
+ }
12546
12553
  }
12547
12554
  } else {
12548
12555
  plan = state.plan;
@@ -12553,7 +12560,7 @@ var GoalOrchestrator = class {
12553
12560
  let priorFingerprint = state.gapFingerprint;
12554
12561
  let stallStreak = state.stallStreak;
12555
12562
  const startRound = resumeStartRound(state);
12556
- for (let round = startRound; round <= this.#options.maxRounds; round++) {
12563
+ for (let round = startRound; this.#options.maxRounds === void 0 || round <= this.#options.maxRounds; round++) {
12557
12564
  signal.throwIfAborted();
12558
12565
  await this.#options.onRoundBoundary?.(state.id);
12559
12566
  if (memoryRotationActive()) return;
@@ -12573,6 +12580,7 @@ var GoalOrchestrator = class {
12573
12580
  yield { type: "goal-worker-start", round };
12574
12581
  let workerError;
12575
12582
  let workerMemoryPressure = false;
12583
+ let workerIterationLimit = false;
12576
12584
  try {
12577
12585
  for await (const event of this.#options.runWorker({
12578
12586
  goal: state.objective,
@@ -12585,18 +12593,19 @@ var GoalOrchestrator = class {
12585
12593
  yield { type: "goal-worker-event", round, event };
12586
12594
  if (event.type === "text") finalResponse = `${finalResponse}${event.text}`.slice(-16e3);
12587
12595
  if (event.type === "error") {
12588
- workerError = event.error.message;
12596
+ workerError = event.error;
12589
12597
  if (event.error.code === "memory_pressure") workerMemoryPressure = true;
12598
+ if (event.error.code === "iteration_limit") workerIterationLimit = true;
12590
12599
  break;
12591
12600
  }
12592
12601
  }
12593
12602
  } catch (error) {
12594
12603
  if (memoryRotationActive()) return;
12595
- workerError = message(error);
12604
+ workerError = { code: "unknown", message: message(error) };
12596
12605
  }
12597
12606
  if (workerMemoryPressure) return;
12598
- if (workerError !== void 0) {
12599
- const reason = `Worker error in round ${round}: ${workerError}`;
12607
+ if (workerError !== void 0 && !workerIterationLimit && !isResumableWorkerError2(workerError)) {
12608
+ const reason = `Worker error in round ${round}: ${workerError.message}`;
12600
12609
  await persistState({ phase: "complete", status: "failed" });
12601
12610
  yield { type: "goal-failed", reason };
12602
12611
  return;
@@ -12626,7 +12635,7 @@ var GoalOrchestrator = class {
12626
12635
  round,
12627
12636
  workspaceDiffHash: evidence.workspaceDiffHash,
12628
12637
  ...hostVerification === void 0 ? {} : { hostVerification }
12629
- }] });
12638
+ }].slice(-64) });
12630
12639
  let outcome;
12631
12640
  if (plan.kind === "code-change" && hostVerification !== void 0 && !hostVerification.passed) {
12632
12641
  if (hostVerification.commands.length === 0) {
@@ -12640,21 +12649,32 @@ var GoalOrchestrator = class {
12640
12649
  fingerprint: createHash11("sha256").update(hostVerification.summary).digest("hex").slice(0, 16)
12641
12650
  };
12642
12651
  }
12643
- } else try {
12644
- outcome = await runClassifier(evidence, plan, {
12645
- registry: this.#options.registry,
12646
- modelId: this.#options.classifierModelId,
12647
- skepticCount: this.#options.skepticCount,
12648
- workspace,
12649
- signal,
12650
- onProgress: memoryCheckpoint
12651
- });
12652
- } catch (error) {
12653
- if (memoryRotationActive()) return;
12654
- outcome = {
12655
- type: "blocked",
12656
- reason: `Classifier infrastructure error: ${message(error)}`
12657
- };
12652
+ } else {
12653
+ let classifierAttempt = 0;
12654
+ while (true) {
12655
+ try {
12656
+ outcome = await runClassifier(evidence, plan, {
12657
+ registry: this.#options.registry,
12658
+ modelId: this.#options.classifierModelId,
12659
+ skepticCount: this.#options.skepticCount,
12660
+ workspace,
12661
+ signal,
12662
+ onProgress: memoryCheckpoint
12663
+ });
12664
+ break;
12665
+ } catch (error) {
12666
+ if (memoryRotationActive()) return;
12667
+ const normalized = normalizeProviderError(error);
12668
+ if (!isResumableStageError(normalized)) {
12669
+ outcome = { type: "blocked", reason: `Classifier infrastructure error: ${normalized.message}` };
12670
+ break;
12671
+ }
12672
+ classifierAttempt += 1;
12673
+ const delayMs = stageRetryDelay(this.#options, classifierAttempt);
12674
+ yield { type: "goal-stage-retry", stage: "classification", attempt: classifierAttempt, reason: normalized.message, delayMs };
12675
+ await waitForStageRetry(delayMs, signal);
12676
+ }
12677
+ }
12658
12678
  }
12659
12679
  await persistState({ verifyRounds: round, pendingVerification: null });
12660
12680
  yield {
@@ -12676,7 +12696,7 @@ var GoalOrchestrator = class {
12676
12696
  }
12677
12697
  if (outcome.fingerprint === priorFingerprint) {
12678
12698
  stallStreak++;
12679
- if (stallStreak >= this.#options.maxStallStreak) {
12699
+ if (this.#options.maxStallStreak !== void 0 && stallStreak >= this.#options.maxStallStreak) {
12680
12700
  await persistState({
12681
12701
  phase: "complete",
12682
12702
  status: "failed",
@@ -12722,6 +12742,31 @@ function resumeStartRound(state) {
12722
12742
  }
12723
12743
  return state.verifyRounds >= state.workerRounds ? state.workerRounds + 1 : Math.max(state.workerRounds, 1);
12724
12744
  }
12745
+ function isResumableWorkerError2(error) {
12746
+ return error.code !== "authentication" && error.code !== "model_not_found" && error.code !== "cancelled" && error.code !== "memory_pressure";
12747
+ }
12748
+ function isResumableStageError(error) {
12749
+ return error.code !== "authentication" && error.code !== "model_not_found" && error.code !== "cancelled" && error.code !== "memory_pressure";
12750
+ }
12751
+ function stageRetryDelay(options, attempt) {
12752
+ const delayMs = options.stageRetryDelayMs?.(attempt) ?? Math.min(6e4, 1e3 * 2 ** Math.min(attempt - 1, 6));
12753
+ return Number.isSafeInteger(delayMs) && delayMs >= 0 ? delayMs : 1e3;
12754
+ }
12755
+ function waitForStageRetry(delayMs, signal) {
12756
+ signal.throwIfAborted();
12757
+ if (delayMs === 0) return Promise.resolve();
12758
+ return new Promise((resolvePromise, reject) => {
12759
+ const onAbort = () => {
12760
+ clearTimeout(timer);
12761
+ reject(signal.reason instanceof Error ? signal.reason : new Error("Goal cancelled"));
12762
+ };
12763
+ const timer = setTimeout(() => {
12764
+ signal.removeEventListener("abort", onAbort);
12765
+ resolvePromise();
12766
+ }, delayMs);
12767
+ signal.addEventListener("abort", onAbort, { once: true });
12768
+ });
12769
+ }
12725
12770
  async function writePlanFile(workspace, plan) {
12726
12771
  const dir = join16(workspace, ".flavor");
12727
12772
  const planPath2 = join16(dir, "goal-plan.md");
@@ -13868,6 +13913,7 @@ function incompleteUtf8SuffixLength(buffer) {
13868
13913
 
13869
13914
  // src/loop/verifier.ts
13870
13915
  var SCRIPT_PRIORITY = ["test", "typecheck", "lint", "build", "smoke:install"];
13916
+ var MAX_PERSISTED_VERIFICATION_STREAM_BYTES = 1024;
13871
13917
  async function inferVerificationPlan(workspace) {
13872
13918
  const commands = [];
13873
13919
  const seen = /* @__PURE__ */ new Set();
@@ -13903,9 +13949,9 @@ async function runVerificationPlan(plan, workspace, signal, executionEnvironment
13903
13949
  command: item.command,
13904
13950
  args: item.args,
13905
13951
  exitCode: result.exitCode,
13906
- stdout: result.stdout,
13907
- stderr: result.stderr,
13908
- truncated: result.truncated
13952
+ stdout: boundedEvidenceTail(result.stdout),
13953
+ stderr: boundedEvidenceTail(result.stderr),
13954
+ truncated: result.truncated || Buffer.byteLength(result.stdout, "utf8") > MAX_PERSISTED_VERIFICATION_STREAM_BYTES || Buffer.byteLength(result.stderr, "utf8") > MAX_PERSISTED_VERIFICATION_STREAM_BYTES
13909
13955
  });
13910
13956
  if (result.exitCode !== 0) {
13911
13957
  return { passed: false, commands, summary: `${item.label} failed with exit code ${String(result.exitCode)}.` };
@@ -13913,6 +13959,14 @@ async function runVerificationPlan(plan, workspace, signal, executionEnvironment
13913
13959
  }
13914
13960
  return { passed: true, commands, summary: `Passed ${commands.length} deterministic verification command(s).` };
13915
13961
  }
13962
+ function boundedEvidenceTail(value) {
13963
+ const encoded = Buffer.from(value, "utf8");
13964
+ if (encoded.byteLength <= MAX_PERSISTED_VERIFICATION_STREAM_BYTES) return value;
13965
+ const prefix = Buffer.from("\u2026\n", "utf8");
13966
+ let start = encoded.byteLength - (MAX_PERSISTED_VERIFICATION_STREAM_BYTES - prefix.byteLength);
13967
+ while (start < encoded.byteLength && (encoded[start] & 192) === 128) start += 1;
13968
+ return `${prefix.toString("utf8")}${encoded.subarray(start).toString("utf8")}`;
13969
+ }
13916
13970
  function npmScript(name) {
13917
13971
  return name === "test" ? { label: name, command: "npm", args: ["test"] } : { label: name, command: "npm", args: ["run", name] };
13918
13972
  }
@@ -14071,6 +14125,35 @@ var AuditLogger = class {
14071
14125
  }
14072
14126
  };
14073
14127
 
14128
+ // src/utils/abort.ts
14129
+ function waitWithSignal(operation, signal) {
14130
+ if (signal === void 0) return operation;
14131
+ return new Promise((resolve44, reject) => {
14132
+ const abort2 = () => reject(signal.reason);
14133
+ if (signal.aborted) abort2();
14134
+ else signal.addEventListener("abort", abort2, { once: true });
14135
+ operation.then(resolve44, reject).finally(() => signal.removeEventListener("abort", abort2));
14136
+ });
14137
+ }
14138
+ function createScopedAbortSignal(parent) {
14139
+ const controller2 = new AbortController();
14140
+ let disposed = false;
14141
+ const abort2 = () => {
14142
+ if (!controller2.signal.aborted) controller2.abort(parent?.reason);
14143
+ };
14144
+ if (parent?.aborted) abort2();
14145
+ else parent?.addEventListener("abort", abort2, { once: true });
14146
+ return {
14147
+ signal: controller2.signal,
14148
+ dispose: () => {
14149
+ if (disposed) return;
14150
+ disposed = true;
14151
+ parent?.removeEventListener("abort", abort2);
14152
+ abort2();
14153
+ }
14154
+ };
14155
+ }
14156
+
14074
14157
  // src/models/anthropic.ts
14075
14158
  var CLAUDE_CLIENT_HEADERS = {
14076
14159
  "User-Agent": "claude-cli/2.1.0 (external, cli)",
@@ -14193,6 +14276,7 @@ var AnthropicModelAdapter = class {
14193
14276
  void appendUsageLog(line);
14194
14277
  }
14195
14278
  async *stream(request) {
14279
+ const requestAbort = createScopedAbortSignal(request.signal);
14196
14280
  let inputTokens = 0;
14197
14281
  let outputTokens = 0;
14198
14282
  let hasUsage = false;
@@ -14316,13 +14400,13 @@ var AnthropicModelAdapter = class {
14316
14400
  };
14317
14401
  let stream;
14318
14402
  try {
14319
- stream = await this.client.messages.create(body, { signal: request.signal });
14403
+ stream = await this.client.messages.create(body, { signal: requestAbort.signal });
14320
14404
  } catch (error) {
14321
14405
  if (this.thinkingBudget > 0 && !this.#thinkingRejected && isThinkingParamRejected(error)) {
14322
14406
  this.#thinkingRejected = true;
14323
14407
  delete body.thinking;
14324
14408
  stripThinkingBlocks(body.messages);
14325
- stream = await this.client.messages.create(body, { signal: request.signal });
14409
+ stream = await this.client.messages.create(body, { signal: requestAbort.signal });
14326
14410
  } else {
14327
14411
  throw error;
14328
14412
  }
@@ -14434,6 +14518,8 @@ var AnthropicModelAdapter = class {
14434
14518
  };
14435
14519
  }
14436
14520
  yield { type: "error", error: normalizeProviderError(error) };
14521
+ } finally {
14522
+ requestAbort.dispose();
14437
14523
  }
14438
14524
  }
14439
14525
  };
@@ -14578,6 +14664,7 @@ var OpenAIModelAdapter = class {
14578
14664
  void appendUsageLog(line);
14579
14665
  }
14580
14666
  async *stream(request) {
14667
+ const requestAbort = createScopedAbortSignal(request.signal);
14581
14668
  const callIds = /* @__PURE__ */ new Map();
14582
14669
  const pendingCalls = /* @__PURE__ */ new Map();
14583
14670
  const emittedCalls = /* @__PURE__ */ new Set();
@@ -14594,7 +14681,7 @@ var OpenAIModelAdapter = class {
14594
14681
  strict: tool.strict ?? true
14595
14682
  }))
14596
14683
  };
14597
- const stream = this.client.responses.stream(body, { signal: request.signal });
14684
+ const stream = this.client.responses.stream(body, { signal: requestAbort.signal });
14598
14685
  for await (const event of stream) {
14599
14686
  if (event.type === "response.output_item.added" && event.item?.type === "function_call" && event.output_index !== void 0 && event.item.call_id) {
14600
14687
  callIds.set(event.output_index, event.item.call_id);
@@ -14688,6 +14775,8 @@ var OpenAIModelAdapter = class {
14688
14775
  }
14689
14776
  } catch (error) {
14690
14777
  yield { type: "error", error: normalizeProviderError(error) };
14778
+ } finally {
14779
+ requestAbort.dispose();
14691
14780
  }
14692
14781
  }
14693
14782
  };
@@ -16183,6 +16272,11 @@ function appendThinkingText(previous, delta) {
16183
16272
  // src/ui/transcript.ts
16184
16273
  var MAX_TRANSCRIPT_TURNS = 200;
16185
16274
  var MAX_TRANSCRIPT_CHARS = 2e6;
16275
+ var MAX_ACTIVE_TRANSCRIPT_BLOCKS = 160;
16276
+ var MAX_ACTIVE_ASSISTANT_CHARS = 64e3;
16277
+ var MAX_TRANSCRIPT_BLOCK_TEXT_CHARS = 16e3;
16278
+ var MAX_TRANSCRIPT_TOOL_VALUE_CHARS = 16e3;
16279
+ var MAX_TRANSCRIPT_PRESENTATION_CHARS = 32e3;
16186
16280
  function createTranscriptState() {
16187
16281
  return { completed: [], nextId: 1 };
16188
16282
  }
@@ -16543,11 +16637,9 @@ function applyTaskSnapshot(turn, snapshot4, includeTerminal = true) {
16543
16637
  else blocks[index] = block;
16544
16638
  }
16545
16639
  return {
16546
- ...turn,
16640
+ ...withBlocks(turn, blocks),
16547
16641
  taskSnapshot: snapshot4,
16548
- suppressedTaskIds: [...suppressed],
16549
- blocks,
16550
- statusLines: blocks.filter((block) => block.kind === "status").map((block) => block.text)
16642
+ suppressedTaskIds: [...suppressed]
16551
16643
  };
16552
16644
  }
16553
16645
  function terminalTaskIds(snapshot4) {
@@ -16561,13 +16653,10 @@ function upsertStatus(state, block) {
16561
16653
  if (state.active === void 0) return state;
16562
16654
  const blocks = [...state.active.blocks];
16563
16655
  const index = blocks.findIndex((item) => item.kind === "status" && item.id === block.id);
16564
- if (index < 0) blocks.push(block);
16565
- else blocks[index] = block;
16566
- return { ...state, active: {
16567
- ...state.active,
16568
- statusLines: blocks.filter((item) => item.kind === "status").map((item) => item.text),
16569
- blocks
16570
- } };
16656
+ const bounded2 = boundedTranscriptBlock(block);
16657
+ if (index < 0) blocks.push(bounded2);
16658
+ else blocks[index] = bounded2;
16659
+ return { ...state, active: withBlocks(state.active, blocks) };
16571
16660
  }
16572
16661
  function finishActive(state) {
16573
16662
  if (state.active === void 0) return state;
@@ -16591,16 +16680,12 @@ function attachThinking(turn, text) {
16591
16680
  ...block,
16592
16681
  thinkingText: appendThinkingText(block.thinkingText, text)
16593
16682
  };
16594
- return { ...turn, blocks };
16683
+ return withBlocks(turn, blocks);
16595
16684
  }
16596
16685
  function withoutModelActivity(turn, id) {
16597
16686
  const blocks = turn.blocks.filter((block) => block.kind !== "status" || block.activity !== "model" || id !== void 0 && block.id !== id);
16598
16687
  if (blocks.length === turn.blocks.length) return turn;
16599
- return {
16600
- ...turn,
16601
- blocks,
16602
- statusLines: blocks.filter((block) => block.kind === "status").map((block) => block.text)
16603
- };
16688
+ return withBlocks(turn, blocks);
16604
16689
  }
16605
16690
  function restoreTranscriptState(state) {
16606
16691
  const completed = state.completed.map(cloneTurn);
@@ -16702,15 +16787,18 @@ function cloneTurn(turn) {
16702
16787
  function upsertTurnStatus(turn, block) {
16703
16788
  const blocks = [...turn.blocks];
16704
16789
  const index = blocks.findIndex((item) => item.kind === "status" && item.id === block.id);
16705
- if (index < 0) blocks.push(block);
16706
- else blocks[index] = block;
16790
+ const bounded2 = boundedTranscriptBlock(block);
16791
+ if (index < 0) blocks.push(bounded2);
16792
+ else blocks[index] = bounded2;
16707
16793
  return withBlocks(turn, blocks);
16708
16794
  }
16709
16795
  function withBlocks(turn, blocks) {
16796
+ const bounded2 = blocks.length <= MAX_ACTIVE_TRANSCRIPT_BLOCKS ? blocks : blocks.slice(-MAX_ACTIVE_TRANSCRIPT_BLOCKS);
16710
16797
  return {
16711
16798
  ...turn,
16712
- blocks,
16713
- statusLines: blocks.filter((block) => block.kind === "status").map((block) => block.text)
16799
+ assistantText: tailText(turn.assistantText, MAX_ACTIVE_ASSISTANT_CHARS),
16800
+ blocks: bounded2,
16801
+ statusLines: bounded2.filter((block) => block.kind === "status").map((block) => block.text)
16714
16802
  };
16715
16803
  }
16716
16804
  function appendLine(text, line) {
@@ -16724,19 +16812,95 @@ function stripRetryBlocks(turn) {
16724
16812
  if (block.id.startsWith("structured-retry:")) return false;
16725
16813
  return true;
16726
16814
  });
16727
- return {
16728
- ...turn,
16729
- blocks,
16730
- statusLines: blocks.filter((item) => item.kind === "status").map((item) => item.text)
16731
- };
16815
+ return withBlocks(turn, blocks);
16732
16816
  }
16733
16817
  function addText(turn, text, onNewLine = false) {
16734
- const assistantText = onNewLine ? appendLine(turn.assistantText, text) : turn.assistantText + text;
16818
+ const assistantText = tailText(
16819
+ onNewLine ? appendLine(turn.assistantText, text) : turn.assistantText + text,
16820
+ MAX_ACTIVE_ASSISTANT_CHARS
16821
+ );
16735
16822
  const blocks = [...turn.blocks];
16736
16823
  const last = blocks[blocks.length - 1];
16737
- if (!onNewLine && last?.kind === "text") blocks[blocks.length - 1] = { kind: "text", text: last.text + text };
16738
- else blocks.push({ kind: "text", text });
16739
- return { ...turn, assistantText, blocks };
16824
+ if (!onNewLine && last?.kind === "text") {
16825
+ blocks[blocks.length - 1] = { kind: "text", text: tailText(last.text + text, MAX_TRANSCRIPT_BLOCK_TEXT_CHARS) };
16826
+ } else {
16827
+ blocks.push({ kind: "text", text: tailText(text, MAX_TRANSCRIPT_BLOCK_TEXT_CHARS) });
16828
+ }
16829
+ return withBlocks({ ...turn, assistantText }, blocks);
16830
+ }
16831
+ function boundedTranscriptBlock(block) {
16832
+ const presentation = block.presentation === void 0 ? void 0 : boundedTranscriptPresentation(block.presentation);
16833
+ const tool = block.tool === void 0 ? void 0 : {
16834
+ name: block.tool.name,
16835
+ input: boundedTranscriptValue(block.tool.input),
16836
+ ...block.tool.result === void 0 ? {} : {
16837
+ result: {
16838
+ ok: block.tool.result.ok,
16839
+ ...block.tool.result.output === void 0 ? {} : { output: boundedTranscriptValue(block.tool.result.output) },
16840
+ ...block.tool.result.content === void 0 ? {} : {
16841
+ content: tailText(block.tool.result.content, MAX_TRANSCRIPT_TOOL_VALUE_CHARS)
16842
+ },
16843
+ ...block.tool.result.presentation === void 0 ? {} : {
16844
+ presentation: boundedTranscriptPresentation(block.tool.result.presentation)
16845
+ },
16846
+ ...block.tool.result.additionalContext === void 0 ? {} : {
16847
+ additionalContext: block.tool.result.additionalContext.slice(-20).map((item) => tailText(item, MAX_TRANSCRIPT_TOOL_VALUE_CHARS))
16848
+ },
16849
+ ...block.tool.result.error === void 0 ? {} : { error: {
16850
+ code: block.tool.result.error.code,
16851
+ message: tailText(block.tool.result.error.message, MAX_TRANSCRIPT_BLOCK_TEXT_CHARS)
16852
+ } }
16853
+ }
16854
+ }
16855
+ };
16856
+ return {
16857
+ ...block,
16858
+ text: tailText(block.text, MAX_TRANSCRIPT_BLOCK_TEXT_CHARS),
16859
+ ...block.hint === void 0 ? {} : { hint: tailText(block.hint, MAX_TRANSCRIPT_BLOCK_TEXT_CHARS) },
16860
+ ...block.details === void 0 ? {} : { details: tailText(block.details, MAX_TRANSCRIPT_BLOCK_TEXT_CHARS) },
16861
+ ...presentation === void 0 ? {} : { presentation },
16862
+ ...tool === void 0 ? {} : { tool }
16863
+ };
16864
+ }
16865
+ function boundedTranscriptPresentation(presentation) {
16866
+ return jsonSize(presentation) <= MAX_TRANSCRIPT_PRESENTATION_CHARS ? presentation : {
16867
+ kind: "generic",
16868
+ title: "Tool output",
16869
+ summary: "Large presentation omitted from the live transcript; the model-facing result remains available in context."
16870
+ };
16871
+ }
16872
+ function boundedTranscriptValue(value) {
16873
+ const chars = jsonSize(value);
16874
+ if (chars <= MAX_TRANSCRIPT_TOOL_VALUE_CHARS) return value;
16875
+ const record3 = typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
16876
+ const summary2 = { truncated: true, originalChars: chars };
16877
+ for (const key of ["path", "command", "id", "name", "exitCode", "bytes", "replacements"]) {
16878
+ const item = record3?.[key];
16879
+ if (typeof item === "string") summary2[key] = tailText(item, 1e3);
16880
+ else if (typeof item === "number" || typeof item === "boolean" || item === null) summary2[key] = item;
16881
+ }
16882
+ return summary2;
16883
+ }
16884
+ function jsonSize(value) {
16885
+ try {
16886
+ return JSON.stringify(value)?.length ?? 0;
16887
+ } catch {
16888
+ return Number.POSITIVE_INFINITY;
16889
+ }
16890
+ }
16891
+ function tailText(text, limit) {
16892
+ if (text.length <= limit) return text;
16893
+ const marker = `\u2026 ${text.length - limit} earlier characters omitted \u2026
16894
+ `;
16895
+ return marker.length >= limit ? text.slice(-limit) : marker + text.slice(-(limit - marker.length));
16896
+ }
16897
+ function transcriptCensus(state) {
16898
+ const turns = [...state.completed, ...state.active === void 0 ? [] : [state.active]];
16899
+ return {
16900
+ turns: turns.length,
16901
+ blocks: turns.reduce((total, turn) => total + turn.blocks.length, 0),
16902
+ chars: jsonSize(state)
16903
+ };
16740
16904
  }
16741
16905
 
16742
16906
  // src/session/store.ts
@@ -19721,19 +19885,6 @@ import { isIP } from "net";
19721
19885
  import { request as httpRequest } from "http";
19722
19886
  import { request as httpsRequest } from "https";
19723
19887
  import { z as z31 } from "zod";
19724
-
19725
- // src/utils/abort.ts
19726
- function waitWithSignal(operation, signal) {
19727
- if (signal === void 0) return operation;
19728
- return new Promise((resolve44, reject) => {
19729
- const abort2 = () => reject(signal.reason);
19730
- if (signal.aborted) abort2();
19731
- else signal.addEventListener("abort", abort2, { once: true });
19732
- operation.then(resolve44, reject).finally(() => signal.removeEventListener("abort", abort2));
19733
- });
19734
- }
19735
-
19736
- // src/tools/web.ts
19737
19888
  var WebFetchInput = z31.object({
19738
19889
  url: z31.string().url().max(8192),
19739
19890
  timeoutMs: z31.coerce.number().int().positive().max(12e4).optional(),
@@ -22246,6 +22397,11 @@ var HELP = [
22246
22397
  "/co-work <pal> <goal> | status [id] | cancel <id> [reason]"
22247
22398
  ].join("\n");
22248
22399
  var MAX_STOP_DENIALS = 2;
22400
+ var RESUMABLE_TURN_BOUNDARIES = /* @__PURE__ */ new Set([
22401
+ "iteration_limit",
22402
+ "output_limit",
22403
+ "context_overflow"
22404
+ ]);
22249
22405
  var FlavorSession = class {
22250
22406
  #services;
22251
22407
  #active;
@@ -22477,6 +22633,7 @@ var FlavorSession = class {
22477
22633
  if (!initial) this.#outputQueuedSubmission(submission);
22478
22634
  initial = false;
22479
22635
  await this.#runSubmission(submission);
22636
+ if (this.#services.rotationPending?.() === true) return;
22480
22637
  pending2.push(...this.#drainMessages("steer"), ...this.#drainMessages("followUp"));
22481
22638
  }
22482
22639
  }
@@ -22500,6 +22657,7 @@ var FlavorSession = class {
22500
22657
  this.#activeSubmission = submission;
22501
22658
  this.#interrupted = false;
22502
22659
  let outcome = "completed";
22660
+ let resumableBoundary;
22503
22661
  let assistantSummary = "";
22504
22662
  let deliverables = [];
22505
22663
  try {
@@ -22536,7 +22694,10 @@ var FlavorSession = class {
22536
22694
  this.#services.output(event);
22537
22695
  if (event.type === "text") assistantSummary = `${assistantSummary}${event.text}`.slice(-2e3);
22538
22696
  if (event.type === "deliverables") deliverables = event.files.slice(0, 100);
22539
- if (event.type === "error") outcome = "failed";
22697
+ if (event.type === "error") {
22698
+ outcome = "failed";
22699
+ if (RESUMABLE_TURN_BOUNDARIES.has(event.error.code)) resumableBoundary = event.error.code;
22700
+ }
22540
22701
  }
22541
22702
  if (controller2.signal.aborted) outcome = "cancelled";
22542
22703
  } catch (error) {
@@ -22577,6 +22738,13 @@ var FlavorSession = class {
22577
22738
  }
22578
22739
  }
22579
22740
  try {
22741
+ if (resumableBoundary !== void 0 && !this.#closed && !controller2.signal.aborted) {
22742
+ const attempt = (submission.iterationContinuation ?? 0) + 1;
22743
+ const text = `[automatic continuation after ${resumableBoundary} boundary; segment ${attempt}]
22744
+ Continue the unfinished task from the current conversation and workspace state. Do not repeat completed operations. Inspect existing results, finish the remaining work, and verify it before stopping.`;
22745
+ this.#enqueueMessage("followUp", { text, displayText: text, iterationContinuation: attempt });
22746
+ this.#notice(`Agent ${resumableBoundary} boundary reached; durable continuation ${attempt} queued.`);
22747
+ }
22580
22748
  if (stopDecision?.decision === "deny" && typeof stopDecision.reason === "string" && stopDecision.reason.length > 0 && !this.#closed && outcome !== "cancelled" && outcome !== "denied") {
22581
22749
  if (this.#stopDenials < MAX_STOP_DENIALS) {
22582
22750
  this.#stopDenials += 1;
@@ -22773,7 +22941,7 @@ Address this now (for example run the required verification or fix), then finish
22773
22941
  });
22774
22942
  }
22775
22943
  #resumeDurableQueue() {
22776
- if (this.#durableResumeStarted || this.#closed || this.active || this.#pendingSubmissions > 0) return;
22944
+ if (this.#durableResumeStarted || this.#closed || this.active || this.#pendingSubmissions > 0 || this.#services.rotationPending?.() === true) return;
22777
22945
  if (!this.#queue.hasPending) return;
22778
22946
  this.#durableResumeStarted = true;
22779
22947
  const submissions = [...this.#drainMessages("steer"), ...this.#drainMessages("followUp")];
@@ -22924,6 +23092,7 @@ function recoveredSubmission(value) {
22924
23092
  if (typeof input.text !== "string" || input.text.trim().length === 0) return void 0;
22925
23093
  if (typeof input.displayText !== "string") return void 0;
22926
23094
  if (input.controlOnly !== void 0 && typeof input.controlOnly !== "boolean") return void 0;
23095
+ if (input.iterationContinuation !== void 0 && (!Number.isSafeInteger(input.iterationContinuation) || input.iterationContinuation < 1)) return void 0;
22927
23096
  if (input.coWorkPlanningKey !== void 0 && typeof input.coWorkPlanningKey !== "string") return void 0;
22928
23097
  if (input.remoteOrigin !== void 0) {
22929
23098
  const origin = input.remoteOrigin;
@@ -27701,6 +27870,12 @@ async function createProductionRuntime(options) {
27701
27870
  holders.sessionTree = { entries: sessionHistory.nodeCount };
27702
27871
  } catch {
27703
27872
  }
27873
+ try {
27874
+ const transcript = transcriptCensus(timelineState);
27875
+ holders.transcript = { entries: transcript.blocks, chars: transcript.chars };
27876
+ holders.transcriptTurns = { entries: transcript.turns };
27877
+ } catch {
27878
+ }
27704
27879
  return holders;
27705
27880
  };
27706
27881
  const rotateForHeap = (continuation, reading) => {
@@ -27758,21 +27933,9 @@ async function createProductionRuntime(options) {
27758
27933
  await verifierEnvironment?.dispose();
27759
27934
  }
27760
27935
  },
27761
- confirmBudget: async (state, dimensions, signal) => {
27762
- if (options.approvalPolicy === "deny") return "unavailable";
27763
- const reached = dimensions.map((dimension) => dimension === "cycles" ? `${state.budget.cyclesUsed} cycles` : `${state.budget.inputTokens + state.budget.outputTokens} tokens`).join(" and ");
27764
- const next = dimensions.map((dimension) => dimension === "cycles" ? `${state.budget.cycleCheckpoint + state.config.cycleStep} cycles` : `${state.budget.tokenCheckpoint + state.config.tokenStep} tokens`).join(" and ");
27765
- const latestVerification = state.cycles.at(-1)?.verification.summary ?? "No host verification evidence yet.";
27766
- const answers = await questions.ask([{
27767
- header: "Loop budget",
27768
- question: `Loop ${state.loopId} reached ${reached}. Latest verification: ${latestVerification} Continue until the next checkpoint (${next})?`,
27769
- options: [
27770
- { label: "Continue", description: "Extend only the reached budget tranche and keep looping." },
27771
- { label: "Stop", description: "End this loop as budget exhausted." }
27772
- ]
27773
- }], signal);
27774
- return answers[0] === "Continue" ? "approved" : "rejected";
27775
- },
27936
+ // Cycle/token values are telemetry checkpoint cadence, not an unattended
27937
+ // execution cap. The user can still cancel explicitly at any time.
27938
+ confirmBudget: async () => "approved",
27776
27939
  fingerprint: workspaceFingerprint,
27777
27940
  idFactory: () => `loop-${(/* @__PURE__ */ new Date()).toISOString().replace(/[^0-9]/g, "").slice(0, 17)}-${randomUUID25().slice(0, 8)}`
27778
27941
  });
@@ -27783,8 +27946,9 @@ async function createProductionRuntime(options) {
27783
27946
  plannerModelId: mainModel,
27784
27947
  classifierModelId: mainModel,
27785
27948
  skepticCount: 3,
27786
- maxRounds: config.goal.maxRounds,
27787
- maxStallStreak: config.goal.maxStallStreak,
27949
+ // Production goals run until verified, explicitly cancelled, or blocked
27950
+ // by a non-recoverable external condition. Numeric config values remain
27951
+ // accepted for compatibility but no longer terminate autonomous work.
27788
27952
  persistence: goalStore,
27789
27953
  onRoundBoundary: (goalId) => maybeRotateAtBoundary({ kind: "goal", id: goalId }),
27790
27954
  onMemoryCheckpoint: (goalId) => maybeRotateAtBoundary({ kind: "goal", id: goalId }),
@@ -27925,6 +28089,7 @@ async function createProductionRuntime(options) {
27925
28089
  () => rotateForHeap(activeLongTask ?? { kind: "goal", id: goalId }, currentPressureReading())
27926
28090
  ),
27927
28091
  continuation: () => consumePendingContinuation(),
28092
+ rotationPending: () => memoryRotationActive(),
27928
28093
  mcp: async (command, signal) => {
27929
28094
  signal.throwIfAborted();
27930
28095
  const manager = await mcpReady;
@@ -28482,6 +28647,8 @@ async function* loopSessionEvents(events, hooks) {
28482
28647
  totalInputTokens,
28483
28648
  totalOutputTokens
28484
28649
  };
28650
+ } else if (event.event.type === "error" && isResumableLongTaskError(event.event.error.code)) {
28651
+ yield { type: "warning", message: `Loop worker segment ended at ${event.event.error.code}; checkpointing and verifying before continuing.` };
28485
28652
  } else if (event.event.type !== "done") yield event.event;
28486
28653
  continue;
28487
28654
  }
@@ -28536,7 +28703,7 @@ function loopProgressEvent(event) {
28536
28703
  loopId: event.loopId,
28537
28704
  phase: "budget",
28538
28705
  state: "info",
28539
- message: `Confirmation required for ${event.dimensions.join(" and ")} budget.`
28706
+ message: `${event.dimensions.join(" and ")} checkpoint reached; continuing automatically.`
28540
28707
  };
28541
28708
  }
28542
28709
  return {
@@ -28553,6 +28720,9 @@ function terminalProgressState(status) {
28553
28720
  if (status === "needs_human") return "info";
28554
28721
  return "failed";
28555
28722
  }
28723
+ function isResumableLongTaskError(code) {
28724
+ return code !== "authentication" && code !== "model_not_found" && code !== "cancelled" && code !== "memory_pressure";
28725
+ }
28556
28726
  async function workspaceFingerprint(workspace) {
28557
28727
  const hash = createHash21("sha256");
28558
28728
  const diff = await execFileNoThrow(
@@ -29212,6 +29382,13 @@ async function* goalSessionEvents(events) {
29212
29382
  yield { type: "notice", message: `Resuming goal ${event.goalId} at round ${event.round} after a heap rotation.` };
29213
29383
  continue;
29214
29384
  }
29385
+ if (event.type === "goal-stage-retry") {
29386
+ yield {
29387
+ type: "warning",
29388
+ message: `Goal ${event.stage} attempt ${event.attempt} hit a recoverable boundary (${event.reason}); retrying in ${event.delayMs}ms.`
29389
+ };
29390
+ continue;
29391
+ }
29215
29392
  if (event.type === "goal-plan-created") {
29216
29393
  yield { type: "notice", message: `Goal plan created (${event.plan.kind}) with ${event.plan.criteria.length} acceptance criteria.` };
29217
29394
  yield { type: "notice", message: `Plan file: ${event.planPath}` };
@@ -29238,6 +29415,8 @@ async function* goalSessionEvents(events) {
29238
29415
  totalInputTokens: event.event.usage.inputTokens,
29239
29416
  totalOutputTokens: event.event.usage.outputTokens
29240
29417
  };
29418
+ } else if (event.event.type === "error" && isResumableLongTaskError(event.event.error.code)) {
29419
+ yield { type: "warning", message: `Goal worker segment ended at ${event.event.error.code}; checkpointing and verifying before continuing.` };
29241
29420
  } else {
29242
29421
  yield event.event;
29243
29422
  }