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

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