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.
@@ -10,7 +10,7 @@ import {
10
10
  parseMemoryRestartMarker,
11
11
  pendingContinuation,
12
12
  rotationCooldownActive
13
- } from "./chunk-LKQTBL3R.js";
13
+ } from "./chunk-N5MXHJKD.js";
14
14
  import {
15
15
  PERMISSION_MODES,
16
16
  PermissionModeSchema,
@@ -1301,6 +1301,11 @@ function appendThinkingText(previous, delta) {
1301
1301
  // src/ui/transcript.ts
1302
1302
  var MAX_TRANSCRIPT_TURNS = 200;
1303
1303
  var MAX_TRANSCRIPT_CHARS = 2e6;
1304
+ var MAX_ACTIVE_TRANSCRIPT_BLOCKS = 160;
1305
+ var MAX_ACTIVE_ASSISTANT_CHARS = 64e3;
1306
+ var MAX_TRANSCRIPT_BLOCK_TEXT_CHARS = 16e3;
1307
+ var MAX_TRANSCRIPT_TOOL_VALUE_CHARS = 16e3;
1308
+ var MAX_TRANSCRIPT_PRESENTATION_CHARS = 32e3;
1304
1309
  function createTranscriptState() {
1305
1310
  return { completed: [], nextId: 1 };
1306
1311
  }
@@ -1661,11 +1666,9 @@ function applyTaskSnapshot(turn, snapshot4, includeTerminal = true) {
1661
1666
  else blocks[index] = block;
1662
1667
  }
1663
1668
  return {
1664
- ...turn,
1669
+ ...withBlocks(turn, blocks),
1665
1670
  taskSnapshot: snapshot4,
1666
- suppressedTaskIds: [...suppressed],
1667
- blocks,
1668
- statusLines: blocks.filter((block) => block.kind === "status").map((block) => block.text)
1671
+ suppressedTaskIds: [...suppressed]
1669
1672
  };
1670
1673
  }
1671
1674
  function terminalTaskIds(snapshot4) {
@@ -1679,13 +1682,10 @@ function upsertStatus(state, block) {
1679
1682
  if (state.active === void 0) return state;
1680
1683
  const blocks = [...state.active.blocks];
1681
1684
  const index = blocks.findIndex((item) => item.kind === "status" && item.id === block.id);
1682
- if (index < 0) blocks.push(block);
1683
- else blocks[index] = block;
1684
- return { ...state, active: {
1685
- ...state.active,
1686
- statusLines: blocks.filter((item) => item.kind === "status").map((item) => item.text),
1687
- blocks
1688
- } };
1685
+ const bounded2 = boundedTranscriptBlock(block);
1686
+ if (index < 0) blocks.push(bounded2);
1687
+ else blocks[index] = bounded2;
1688
+ return { ...state, active: withBlocks(state.active, blocks) };
1689
1689
  }
1690
1690
  function finishActive(state) {
1691
1691
  if (state.active === void 0) return state;
@@ -1709,16 +1709,12 @@ function attachThinking(turn, text) {
1709
1709
  ...block,
1710
1710
  thinkingText: appendThinkingText(block.thinkingText, text)
1711
1711
  };
1712
- return { ...turn, blocks };
1712
+ return withBlocks(turn, blocks);
1713
1713
  }
1714
1714
  function withoutModelActivity(turn, id) {
1715
1715
  const blocks = turn.blocks.filter((block) => block.kind !== "status" || block.activity !== "model" || id !== void 0 && block.id !== id);
1716
1716
  if (blocks.length === turn.blocks.length) return turn;
1717
- return {
1718
- ...turn,
1719
- blocks,
1720
- statusLines: blocks.filter((block) => block.kind === "status").map((block) => block.text)
1721
- };
1717
+ return withBlocks(turn, blocks);
1722
1718
  }
1723
1719
  function restoreTranscriptState(state) {
1724
1720
  const completed = state.completed.map(cloneTurn);
@@ -1820,15 +1816,18 @@ function cloneTurn(turn) {
1820
1816
  function upsertTurnStatus(turn, block) {
1821
1817
  const blocks = [...turn.blocks];
1822
1818
  const index = blocks.findIndex((item) => item.kind === "status" && item.id === block.id);
1823
- if (index < 0) blocks.push(block);
1824
- else blocks[index] = block;
1819
+ const bounded2 = boundedTranscriptBlock(block);
1820
+ if (index < 0) blocks.push(bounded2);
1821
+ else blocks[index] = bounded2;
1825
1822
  return withBlocks(turn, blocks);
1826
1823
  }
1827
1824
  function withBlocks(turn, blocks) {
1825
+ const bounded2 = blocks.length <= MAX_ACTIVE_TRANSCRIPT_BLOCKS ? blocks : blocks.slice(-MAX_ACTIVE_TRANSCRIPT_BLOCKS);
1828
1826
  return {
1829
1827
  ...turn,
1830
- blocks,
1831
- statusLines: blocks.filter((block) => block.kind === "status").map((block) => block.text)
1828
+ assistantText: tailText(turn.assistantText, MAX_ACTIVE_ASSISTANT_CHARS),
1829
+ blocks: bounded2,
1830
+ statusLines: bounded2.filter((block) => block.kind === "status").map((block) => block.text)
1832
1831
  };
1833
1832
  }
1834
1833
  function appendLine(text, line) {
@@ -1842,19 +1841,95 @@ function stripRetryBlocks(turn) {
1842
1841
  if (block.id.startsWith("structured-retry:")) return false;
1843
1842
  return true;
1844
1843
  });
1845
- return {
1846
- ...turn,
1847
- blocks,
1848
- statusLines: blocks.filter((item) => item.kind === "status").map((item) => item.text)
1849
- };
1844
+ return withBlocks(turn, blocks);
1850
1845
  }
1851
1846
  function addText(turn, text, onNewLine = false) {
1852
- const assistantText = onNewLine ? appendLine(turn.assistantText, text) : turn.assistantText + text;
1847
+ const assistantText = tailText(
1848
+ onNewLine ? appendLine(turn.assistantText, text) : turn.assistantText + text,
1849
+ MAX_ACTIVE_ASSISTANT_CHARS
1850
+ );
1853
1851
  const blocks = [...turn.blocks];
1854
1852
  const last = blocks[blocks.length - 1];
1855
- if (!onNewLine && last?.kind === "text") blocks[blocks.length - 1] = { kind: "text", text: last.text + text };
1856
- else blocks.push({ kind: "text", text });
1857
- return { ...turn, assistantText, blocks };
1853
+ if (!onNewLine && last?.kind === "text") {
1854
+ blocks[blocks.length - 1] = { kind: "text", text: tailText(last.text + text, MAX_TRANSCRIPT_BLOCK_TEXT_CHARS) };
1855
+ } else {
1856
+ blocks.push({ kind: "text", text: tailText(text, MAX_TRANSCRIPT_BLOCK_TEXT_CHARS) });
1857
+ }
1858
+ return withBlocks({ ...turn, assistantText }, blocks);
1859
+ }
1860
+ function boundedTranscriptBlock(block) {
1861
+ const presentation = block.presentation === void 0 ? void 0 : boundedTranscriptPresentation(block.presentation);
1862
+ const tool = block.tool === void 0 ? void 0 : {
1863
+ name: block.tool.name,
1864
+ input: boundedTranscriptValue(block.tool.input),
1865
+ ...block.tool.result === void 0 ? {} : {
1866
+ result: {
1867
+ ok: block.tool.result.ok,
1868
+ ...block.tool.result.output === void 0 ? {} : { output: boundedTranscriptValue(block.tool.result.output) },
1869
+ ...block.tool.result.content === void 0 ? {} : {
1870
+ content: tailText(block.tool.result.content, MAX_TRANSCRIPT_TOOL_VALUE_CHARS)
1871
+ },
1872
+ ...block.tool.result.presentation === void 0 ? {} : {
1873
+ presentation: boundedTranscriptPresentation(block.tool.result.presentation)
1874
+ },
1875
+ ...block.tool.result.additionalContext === void 0 ? {} : {
1876
+ additionalContext: block.tool.result.additionalContext.slice(-20).map((item) => tailText(item, MAX_TRANSCRIPT_TOOL_VALUE_CHARS))
1877
+ },
1878
+ ...block.tool.result.error === void 0 ? {} : { error: {
1879
+ code: block.tool.result.error.code,
1880
+ message: tailText(block.tool.result.error.message, MAX_TRANSCRIPT_BLOCK_TEXT_CHARS)
1881
+ } }
1882
+ }
1883
+ }
1884
+ };
1885
+ return {
1886
+ ...block,
1887
+ text: tailText(block.text, MAX_TRANSCRIPT_BLOCK_TEXT_CHARS),
1888
+ ...block.hint === void 0 ? {} : { hint: tailText(block.hint, MAX_TRANSCRIPT_BLOCK_TEXT_CHARS) },
1889
+ ...block.details === void 0 ? {} : { details: tailText(block.details, MAX_TRANSCRIPT_BLOCK_TEXT_CHARS) },
1890
+ ...presentation === void 0 ? {} : { presentation },
1891
+ ...tool === void 0 ? {} : { tool }
1892
+ };
1893
+ }
1894
+ function boundedTranscriptPresentation(presentation) {
1895
+ return jsonSize(presentation) <= MAX_TRANSCRIPT_PRESENTATION_CHARS ? presentation : {
1896
+ kind: "generic",
1897
+ title: "Tool output",
1898
+ summary: "Large presentation omitted from the live transcript; the model-facing result remains available in context."
1899
+ };
1900
+ }
1901
+ function boundedTranscriptValue(value) {
1902
+ const chars = jsonSize(value);
1903
+ if (chars <= MAX_TRANSCRIPT_TOOL_VALUE_CHARS) return value;
1904
+ const record = typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
1905
+ const summary2 = { truncated: true, originalChars: chars };
1906
+ for (const key of ["path", "command", "id", "name", "exitCode", "bytes", "replacements"]) {
1907
+ const item = record?.[key];
1908
+ if (typeof item === "string") summary2[key] = tailText(item, 1e3);
1909
+ else if (typeof item === "number" || typeof item === "boolean" || item === null) summary2[key] = item;
1910
+ }
1911
+ return summary2;
1912
+ }
1913
+ function jsonSize(value) {
1914
+ try {
1915
+ return JSON.stringify(value)?.length ?? 0;
1916
+ } catch {
1917
+ return Number.POSITIVE_INFINITY;
1918
+ }
1919
+ }
1920
+ function tailText(text, limit) {
1921
+ if (text.length <= limit) return text;
1922
+ const marker = `\u2026 ${text.length - limit} earlier characters omitted \u2026
1923
+ `;
1924
+ return marker.length >= limit ? text.slice(-limit) : marker + text.slice(-(limit - marker.length));
1925
+ }
1926
+ function transcriptCensus(state) {
1927
+ const turns = [...state.completed, ...state.active === void 0 ? [] : [state.active]];
1928
+ return {
1929
+ turns: turns.length,
1930
+ blocks: turns.reduce((total, turn) => total + turn.blocks.length, 0),
1931
+ chars: jsonSize(state)
1932
+ };
1858
1933
  }
1859
1934
 
1860
1935
  // src/session/checkpoint.ts
@@ -3621,6 +3696,11 @@ var HELP = [
3621
3696
  "/co-work <pal> <goal> | status [id] | cancel <id> [reason]"
3622
3697
  ].join("\n");
3623
3698
  var MAX_STOP_DENIALS = 2;
3699
+ var RESUMABLE_TURN_BOUNDARIES = /* @__PURE__ */ new Set([
3700
+ "iteration_limit",
3701
+ "output_limit",
3702
+ "context_overflow"
3703
+ ]);
3624
3704
  var FlavorSession = class {
3625
3705
  #services;
3626
3706
  #active;
@@ -3852,6 +3932,7 @@ var FlavorSession = class {
3852
3932
  if (!initial) this.#outputQueuedSubmission(submission);
3853
3933
  initial = false;
3854
3934
  await this.#runSubmission(submission);
3935
+ if (this.#services.rotationPending?.() === true) return;
3855
3936
  pending.push(...this.#drainMessages("steer"), ...this.#drainMessages("followUp"));
3856
3937
  }
3857
3938
  }
@@ -3875,6 +3956,7 @@ var FlavorSession = class {
3875
3956
  this.#activeSubmission = submission;
3876
3957
  this.#interrupted = false;
3877
3958
  let outcome = "completed";
3959
+ let resumableBoundary;
3878
3960
  let assistantSummary = "";
3879
3961
  let deliverables = [];
3880
3962
  try {
@@ -3911,7 +3993,10 @@ var FlavorSession = class {
3911
3993
  this.#services.output(event);
3912
3994
  if (event.type === "text") assistantSummary = `${assistantSummary}${event.text}`.slice(-2e3);
3913
3995
  if (event.type === "deliverables") deliverables = event.files.slice(0, 100);
3914
- if (event.type === "error") outcome = "failed";
3996
+ if (event.type === "error") {
3997
+ outcome = "failed";
3998
+ if (RESUMABLE_TURN_BOUNDARIES.has(event.error.code)) resumableBoundary = event.error.code;
3999
+ }
3915
4000
  }
3916
4001
  if (controller.signal.aborted) outcome = "cancelled";
3917
4002
  } catch (error) {
@@ -3952,6 +4037,13 @@ var FlavorSession = class {
3952
4037
  }
3953
4038
  }
3954
4039
  try {
4040
+ if (resumableBoundary !== void 0 && !this.#closed && !controller.signal.aborted) {
4041
+ const attempt = (submission.iterationContinuation ?? 0) + 1;
4042
+ const text = `[automatic continuation after ${resumableBoundary} boundary; segment ${attempt}]
4043
+ 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.`;
4044
+ this.#enqueueMessage("followUp", { text, displayText: text, iterationContinuation: attempt });
4045
+ this.#notice(`Agent ${resumableBoundary} boundary reached; durable continuation ${attempt} queued.`);
4046
+ }
3955
4047
  if (stopDecision?.decision === "deny" && typeof stopDecision.reason === "string" && stopDecision.reason.length > 0 && !this.#closed && outcome !== "cancelled" && outcome !== "denied") {
3956
4048
  if (this.#stopDenials < MAX_STOP_DENIALS) {
3957
4049
  this.#stopDenials += 1;
@@ -4148,7 +4240,7 @@ Address this now (for example run the required verification or fix), then finish
4148
4240
  });
4149
4241
  }
4150
4242
  #resumeDurableQueue() {
4151
- if (this.#durableResumeStarted || this.#closed || this.active || this.#pendingSubmissions > 0) return;
4243
+ if (this.#durableResumeStarted || this.#closed || this.active || this.#pendingSubmissions > 0 || this.#services.rotationPending?.() === true) return;
4152
4244
  if (!this.#queue.hasPending) return;
4153
4245
  this.#durableResumeStarted = true;
4154
4246
  const submissions = [...this.#drainMessages("steer"), ...this.#drainMessages("followUp")];
@@ -4299,6 +4391,7 @@ function recoveredSubmission(value) {
4299
4391
  if (typeof input.text !== "string" || input.text.trim().length === 0) return void 0;
4300
4392
  if (typeof input.displayText !== "string") return void 0;
4301
4393
  if (input.controlOnly !== void 0 && typeof input.controlOnly !== "boolean") return void 0;
4394
+ if (input.iterationContinuation !== void 0 && (!Number.isSafeInteger(input.iterationContinuation) || input.iterationContinuation < 1)) return void 0;
4302
4395
  if (input.coWorkPlanningKey !== void 0 && typeof input.coWorkPlanningKey !== "string") return void 0;
4303
4396
  if (input.remoteOrigin !== void 0) {
4304
4397
  const origin = input.remoteOrigin;
@@ -6763,6 +6856,7 @@ import { mkdirSync as mkdirSync4, readFileSync as readFileSync3, renameSync as r
6763
6856
  import { createHash as createHash18, randomUUID as randomUUID20 } from "crypto";
6764
6857
  import { homedir as homedir3, release as osRelease, tmpdir as tmpdir4, version as osVersion } from "os";
6765
6858
  import { basename as basename7, dirname as dirname17, join as join30, relative as relative22, resolve as resolve31, sep as sep22 } from "path";
6859
+ import { performance } from "perf_hooks";
6766
6860
 
6767
6861
  // src/agent/subagents.ts
6768
6862
  import { z as z9 } from "zod";
@@ -7772,10 +7866,6 @@ var ContextManager = class _ContextManager {
7772
7866
  }
7773
7867
  return microcompact.changed;
7774
7868
  }
7775
- if (this.#consecutiveAutoCompactFailures >= 3) {
7776
- rollbackMicrocompact();
7777
- return false;
7778
- }
7779
7869
  try {
7780
7870
  const compacted = await this.#compactConversation(signal);
7781
7871
  if (compacted) {
@@ -8239,10 +8329,10 @@ function within(root, candidate) {
8239
8329
 
8240
8330
  // src/context/summarizer.ts
8241
8331
  async function summarizeWithModel(options) {
8242
- const maxAttempts = options.maxPromptTooLongAttempts ?? 3;
8243
- if (!Number.isInteger(maxAttempts) || maxAttempts <= 0) throw new Error("maxPromptTooLongAttempts must be positive");
8244
8332
  options.signal?.throwIfAborted();
8245
8333
  let groups = groupMessagesByApiRound(options.messages);
8334
+ const maxAttempts = options.maxPromptTooLongAttempts ?? Math.max(1, Math.ceil(Math.log2(Math.max(1, groups.length))) + 1);
8335
+ if (!Number.isInteger(maxAttempts) || maxAttempts <= 0) throw new Error("maxPromptTooLongAttempts must be positive");
8246
8336
  let lastOverflow;
8247
8337
  let progress = 20;
8248
8338
  reportProgress(options.onProgress, progress);
@@ -8283,7 +8373,7 @@ async function summarizeWithModel(options) {
8283
8373
  if (terminalError.code !== "context_overflow") throw terminalError;
8284
8374
  lastOverflow = terminalError;
8285
8375
  if (attempt >= maxAttempts || groups.length <= 1) throw terminalError;
8286
- groups = groups.slice(1);
8376
+ groups = groups.slice(Math.max(1, Math.floor(groups.length / 2)));
8287
8377
  }
8288
8378
  throw lastOverflow ?? new Error("Compact summary failed");
8289
8379
  }
@@ -12663,7 +12753,7 @@ function extendBudget(input, dimensions, approvedAt) {
12663
12753
  return LoopStateSchema.parse({
12664
12754
  ...state,
12665
12755
  updatedAt: approvedAt,
12666
- budget: { ...state.budget, cycleCheckpoint, tokenCheckpoint, approvals }
12756
+ budget: { ...state.budget, cycleCheckpoint, tokenCheckpoint, approvals: approvals.slice(-64) }
12667
12757
  });
12668
12758
  }
12669
12759
  function rejectBudget(input, terminalReason) {
@@ -12790,6 +12880,7 @@ ${lastCompletedCycle.verification.summary}`;
12790
12880
  });
12791
12881
  let workerError;
12792
12882
  let workerMemoryPressure = false;
12883
+ let workerIterationLimit = false;
12793
12884
  for await (const event of this.#options.runWorker({
12794
12885
  goal,
12795
12886
  cycle,
@@ -12802,8 +12893,9 @@ ${lastCompletedCycle.verification.summary}`;
12802
12893
  cycleOutputTokens += event.outputTokens;
12803
12894
  }
12804
12895
  if (event.type === "error") {
12805
- workerError = event.error.message;
12896
+ workerError = event.error;
12806
12897
  if (event.error.code === "memory_pressure") workerMemoryPressure = true;
12898
+ if (event.error.code === "iteration_limit") workerIterationLimit = true;
12807
12899
  }
12808
12900
  if (event.type === "text") workerText = `${workerText}${event.text}`.slice(-16e3);
12809
12901
  if (this.#options.hallucinationGuard !== void 0) {
@@ -12816,7 +12908,8 @@ ${lastCompletedCycle.verification.summary}`;
12816
12908
  yield { type: "worker-event", event };
12817
12909
  }
12818
12910
  if (workerMemoryPressure) return;
12819
- if (workerError !== void 0) {
12911
+ if (workerError !== void 0 && !workerIterationLimit && !isResumableWorkerError(workerError)) {
12912
+ const status = workerError.code === "cancelled" ? "cancelled" : "failed";
12820
12913
  state = LoopStateSchema.parse({
12821
12914
  ...state,
12822
12915
  budget: {
@@ -12826,8 +12919,8 @@ ${lastCompletedCycle.verification.summary}`;
12826
12919
  outputTokens: state.budget.outputTokens + cycleOutputTokens
12827
12920
  }
12828
12921
  });
12829
- state = await this.#terminal(state, "failed", workerError, now());
12830
- yield { type: "loop-terminal", loopId, status: "failed", reason: workerError };
12922
+ state = await this.#terminal(state, status, workerError.message, now());
12923
+ yield { type: "loop-terminal", loopId, status, reason: workerError.message };
12831
12924
  return;
12832
12925
  }
12833
12926
  state = LoopStateSchema.parse({
@@ -12870,13 +12963,7 @@ ${lastCompletedCycle.verification.summary}`;
12870
12963
  const report = await this.#options.hallucinationGuard.evaluate(goal, workerText);
12871
12964
  await this.#options.onMemoryCheckpoint?.(loopId);
12872
12965
  if (memoryRotationActive()) return;
12873
- if (!report.passed) {
12874
- const guardReason = report.blockingReasons.join("; ") || "Hallucination guard blocked completion.";
12875
- state = await this.#terminal(state, "failed", guardReason, now());
12876
- yield { type: "loop-terminal", loopId, status: "failed", reason: guardReason };
12877
- return;
12878
- }
12879
- guardWarnings = report.warnings;
12966
+ guardWarnings = [...report.blockingReasons, ...report.warnings];
12880
12967
  } catch {
12881
12968
  }
12882
12969
  }
@@ -12898,7 +12985,7 @@ ${lastCompletedCycle.verification.summary}`;
12898
12985
  outputTokens: cycleOutputTokens,
12899
12986
  workspaceFingerprint: fingerprint2,
12900
12987
  verification: evidence
12901
- }],
12988
+ }].slice(-64),
12902
12989
  pendingCycle: null
12903
12990
  });
12904
12991
  await this.#options.persistence.save(state);
@@ -12927,8 +13014,8 @@ ${evidence.summary}`;
12927
13014
  repeatedFailures = failureSignature === previousFailureSignature ? repeatedFailures + 1 : 1;
12928
13015
  previousFailureSignature = failureSignature;
12929
13016
  previousVerification = evidence;
12930
- if (repeatedFailures >= 3) {
12931
- const reason = "The same verification failure repeated three times without material workspace progress.";
13017
+ if (this.#options.maxNoProgressCycles !== void 0 && repeatedFailures >= this.#options.maxNoProgressCycles) {
13018
+ const reason = `The same verification failure repeated ${this.#options.maxNoProgressCycles} times without material workspace progress.`;
12932
13019
  state = await this.#terminal(state, "no_progress", reason, now());
12933
13020
  yield { type: "loop-terminal", loopId, status: "no_progress", reason };
12934
13021
  return;
@@ -12989,6 +13076,9 @@ ${evidence.summary}`;
12989
13076
  return state;
12990
13077
  }
12991
13078
  };
13079
+ function isResumableWorkerError(error) {
13080
+ return error.code !== "authentication" && error.code !== "model_not_found" && error.code !== "cancelled" && error.code !== "memory_pressure";
13081
+ }
12992
13082
  function countTrailingFailureSignature(cycles, signature) {
12993
13083
  let count = 0;
12994
13084
  for (let index = cycles.length - 1; index >= 0; index -= 1) {
@@ -13477,26 +13567,37 @@ var GoalOrchestrator = class {
13477
13567
  let plan;
13478
13568
  let planPath;
13479
13569
  if (state.plan === null) {
13480
- try {
13481
- signal.throwIfAborted();
13482
- await memoryCheckpoint();
13483
- plan = await runPlanner({
13484
- registry: this.#options.registry,
13485
- modelId: this.#options.plannerModelId,
13486
- objective: state.objective,
13487
- signal,
13488
- onProgress: memoryCheckpoint
13489
- });
13490
- planPath = await writePlanFile(workspace, plan);
13491
- await persistState({ phase: "executing", plan, planPath, contractHash: contractHash(state.objective, plan) });
13492
- yield { type: "goal-plan-created", plan, planPath };
13493
- } catch (error) {
13494
- if (memoryRotationActive()) return;
13495
- const reason = `Goal planning failed: ${message(error)}`;
13496
- await persistState({ phase: "complete", status: "failed" });
13497
- yield { type: "goal-plan-failed", reason };
13498
- yield { type: "goal-failed", reason };
13499
- return;
13570
+ let planningAttempt = 0;
13571
+ while (true) {
13572
+ try {
13573
+ signal.throwIfAborted();
13574
+ await memoryCheckpoint();
13575
+ plan = await runPlanner({
13576
+ registry: this.#options.registry,
13577
+ modelId: this.#options.plannerModelId,
13578
+ objective: state.objective,
13579
+ signal,
13580
+ onProgress: memoryCheckpoint
13581
+ });
13582
+ planPath = await writePlanFile(workspace, plan);
13583
+ await persistState({ phase: "executing", plan, planPath, contractHash: contractHash(state.objective, plan) });
13584
+ yield { type: "goal-plan-created", plan, planPath };
13585
+ break;
13586
+ } catch (error) {
13587
+ if (memoryRotationActive()) return;
13588
+ const normalized = normalizeProviderError(error);
13589
+ if (!isResumableStageError(normalized)) {
13590
+ const reason = `Goal planning failed: ${normalized.message}`;
13591
+ await persistState({ phase: "complete", status: "failed" });
13592
+ yield { type: "goal-plan-failed", reason };
13593
+ yield { type: "goal-failed", reason };
13594
+ return;
13595
+ }
13596
+ planningAttempt += 1;
13597
+ const delayMs = stageRetryDelay(this.#options, planningAttempt);
13598
+ yield { type: "goal-stage-retry", stage: "planning", attempt: planningAttempt, reason: normalized.message, delayMs };
13599
+ await waitForStageRetry(delayMs, signal);
13600
+ }
13500
13601
  }
13501
13602
  } else {
13502
13603
  plan = state.plan;
@@ -13507,7 +13608,7 @@ var GoalOrchestrator = class {
13507
13608
  let priorFingerprint = state.gapFingerprint;
13508
13609
  let stallStreak = state.stallStreak;
13509
13610
  const startRound = resumeStartRound(state);
13510
- for (let round = startRound; round <= this.#options.maxRounds; round++) {
13611
+ for (let round = startRound; this.#options.maxRounds === void 0 || round <= this.#options.maxRounds; round++) {
13511
13612
  signal.throwIfAborted();
13512
13613
  await this.#options.onRoundBoundary?.(state.id);
13513
13614
  if (memoryRotationActive()) return;
@@ -13527,6 +13628,7 @@ var GoalOrchestrator = class {
13527
13628
  yield { type: "goal-worker-start", round };
13528
13629
  let workerError;
13529
13630
  let workerMemoryPressure = false;
13631
+ let workerIterationLimit = false;
13530
13632
  try {
13531
13633
  for await (const event of this.#options.runWorker({
13532
13634
  goal: state.objective,
@@ -13539,18 +13641,19 @@ var GoalOrchestrator = class {
13539
13641
  yield { type: "goal-worker-event", round, event };
13540
13642
  if (event.type === "text") finalResponse = `${finalResponse}${event.text}`.slice(-16e3);
13541
13643
  if (event.type === "error") {
13542
- workerError = event.error.message;
13644
+ workerError = event.error;
13543
13645
  if (event.error.code === "memory_pressure") workerMemoryPressure = true;
13646
+ if (event.error.code === "iteration_limit") workerIterationLimit = true;
13544
13647
  break;
13545
13648
  }
13546
13649
  }
13547
13650
  } catch (error) {
13548
13651
  if (memoryRotationActive()) return;
13549
- workerError = message(error);
13652
+ workerError = { code: "unknown", message: message(error) };
13550
13653
  }
13551
13654
  if (workerMemoryPressure) return;
13552
- if (workerError !== void 0) {
13553
- const reason = `Worker error in round ${round}: ${workerError}`;
13655
+ if (workerError !== void 0 && !workerIterationLimit && !isResumableWorkerError2(workerError)) {
13656
+ const reason = `Worker error in round ${round}: ${workerError.message}`;
13554
13657
  await persistState({ phase: "complete", status: "failed" });
13555
13658
  yield { type: "goal-failed", reason };
13556
13659
  return;
@@ -13580,7 +13683,7 @@ var GoalOrchestrator = class {
13580
13683
  round,
13581
13684
  workspaceDiffHash: evidence.workspaceDiffHash,
13582
13685
  ...hostVerification === void 0 ? {} : { hostVerification }
13583
- }] });
13686
+ }].slice(-64) });
13584
13687
  let outcome;
13585
13688
  if (plan.kind === "code-change" && hostVerification !== void 0 && !hostVerification.passed) {
13586
13689
  if (hostVerification.commands.length === 0) {
@@ -13594,21 +13697,32 @@ var GoalOrchestrator = class {
13594
13697
  fingerprint: createHash11("sha256").update(hostVerification.summary).digest("hex").slice(0, 16)
13595
13698
  };
13596
13699
  }
13597
- } else try {
13598
- outcome = await runClassifier(evidence, plan, {
13599
- registry: this.#options.registry,
13600
- modelId: this.#options.classifierModelId,
13601
- skepticCount: this.#options.skepticCount,
13602
- workspace,
13603
- signal,
13604
- onProgress: memoryCheckpoint
13605
- });
13606
- } catch (error) {
13607
- if (memoryRotationActive()) return;
13608
- outcome = {
13609
- type: "blocked",
13610
- reason: `Classifier infrastructure error: ${message(error)}`
13611
- };
13700
+ } else {
13701
+ let classifierAttempt = 0;
13702
+ while (true) {
13703
+ try {
13704
+ outcome = await runClassifier(evidence, plan, {
13705
+ registry: this.#options.registry,
13706
+ modelId: this.#options.classifierModelId,
13707
+ skepticCount: this.#options.skepticCount,
13708
+ workspace,
13709
+ signal,
13710
+ onProgress: memoryCheckpoint
13711
+ });
13712
+ break;
13713
+ } catch (error) {
13714
+ if (memoryRotationActive()) return;
13715
+ const normalized = normalizeProviderError(error);
13716
+ if (!isResumableStageError(normalized)) {
13717
+ outcome = { type: "blocked", reason: `Classifier infrastructure error: ${normalized.message}` };
13718
+ break;
13719
+ }
13720
+ classifierAttempt += 1;
13721
+ const delayMs = stageRetryDelay(this.#options, classifierAttempt);
13722
+ yield { type: "goal-stage-retry", stage: "classification", attempt: classifierAttempt, reason: normalized.message, delayMs };
13723
+ await waitForStageRetry(delayMs, signal);
13724
+ }
13725
+ }
13612
13726
  }
13613
13727
  await persistState({ verifyRounds: round, pendingVerification: null });
13614
13728
  yield {
@@ -13630,7 +13744,7 @@ var GoalOrchestrator = class {
13630
13744
  }
13631
13745
  if (outcome.fingerprint === priorFingerprint) {
13632
13746
  stallStreak++;
13633
- if (stallStreak >= this.#options.maxStallStreak) {
13747
+ if (this.#options.maxStallStreak !== void 0 && stallStreak >= this.#options.maxStallStreak) {
13634
13748
  await persistState({
13635
13749
  phase: "complete",
13636
13750
  status: "failed",
@@ -13676,6 +13790,31 @@ function resumeStartRound(state) {
13676
13790
  }
13677
13791
  return state.verifyRounds >= state.workerRounds ? state.workerRounds + 1 : Math.max(state.workerRounds, 1);
13678
13792
  }
13793
+ function isResumableWorkerError2(error) {
13794
+ return error.code !== "authentication" && error.code !== "model_not_found" && error.code !== "cancelled" && error.code !== "memory_pressure";
13795
+ }
13796
+ function isResumableStageError(error) {
13797
+ return error.code !== "authentication" && error.code !== "model_not_found" && error.code !== "cancelled" && error.code !== "memory_pressure";
13798
+ }
13799
+ function stageRetryDelay(options, attempt) {
13800
+ const delayMs = options.stageRetryDelayMs?.(attempt) ?? Math.min(6e4, 1e3 * 2 ** Math.min(attempt - 1, 6));
13801
+ return Number.isSafeInteger(delayMs) && delayMs >= 0 ? delayMs : 1e3;
13802
+ }
13803
+ function waitForStageRetry(delayMs, signal) {
13804
+ signal.throwIfAborted();
13805
+ if (delayMs === 0) return Promise.resolve();
13806
+ return new Promise((resolvePromise, reject) => {
13807
+ const onAbort = () => {
13808
+ clearTimeout(timer);
13809
+ reject(signal.reason instanceof Error ? signal.reason : new Error("Goal cancelled"));
13810
+ };
13811
+ const timer = setTimeout(() => {
13812
+ signal.removeEventListener("abort", onAbort);
13813
+ resolvePromise();
13814
+ }, delayMs);
13815
+ signal.addEventListener("abort", onAbort, { once: true });
13816
+ });
13817
+ }
13679
13818
  async function writePlanFile(workspace, plan) {
13680
13819
  const dir = join17(workspace, ".flavor");
13681
13820
  const planPath = join17(dir, "goal-plan.md");
@@ -14056,6 +14195,7 @@ function isCode4(error, code) {
14056
14195
  import { readFile as readFile14 } from "fs/promises";
14057
14196
  import { join as join21 } from "path";
14058
14197
  var SCRIPT_PRIORITY = ["test", "typecheck", "lint", "build", "smoke:install"];
14198
+ var MAX_PERSISTED_VERIFICATION_STREAM_BYTES = 1024;
14059
14199
  async function inferVerificationPlan(workspace) {
14060
14200
  const commands = [];
14061
14201
  const seen = /* @__PURE__ */ new Set();
@@ -14091,9 +14231,9 @@ async function runVerificationPlan(plan, workspace, signal, executionEnvironment
14091
14231
  command: item.command,
14092
14232
  args: item.args,
14093
14233
  exitCode: result.exitCode,
14094
- stdout: result.stdout,
14095
- stderr: result.stderr,
14096
- truncated: result.truncated
14234
+ stdout: boundedEvidenceTail(result.stdout),
14235
+ stderr: boundedEvidenceTail(result.stderr),
14236
+ truncated: result.truncated || Buffer.byteLength(result.stdout, "utf8") > MAX_PERSISTED_VERIFICATION_STREAM_BYTES || Buffer.byteLength(result.stderr, "utf8") > MAX_PERSISTED_VERIFICATION_STREAM_BYTES
14097
14237
  });
14098
14238
  if (result.exitCode !== 0) {
14099
14239
  return { passed: false, commands, summary: `${item.label} failed with exit code ${String(result.exitCode)}.` };
@@ -14101,6 +14241,14 @@ async function runVerificationPlan(plan, workspace, signal, executionEnvironment
14101
14241
  }
14102
14242
  return { passed: true, commands, summary: `Passed ${commands.length} deterministic verification command(s).` };
14103
14243
  }
14244
+ function boundedEvidenceTail(value) {
14245
+ const encoded = Buffer.from(value, "utf8");
14246
+ if (encoded.byteLength <= MAX_PERSISTED_VERIFICATION_STREAM_BYTES) return value;
14247
+ const prefix = Buffer.from("\u2026\n", "utf8");
14248
+ let start = encoded.byteLength - (MAX_PERSISTED_VERIFICATION_STREAM_BYTES - prefix.byteLength);
14249
+ while (start < encoded.byteLength && (encoded[start] & 192) === 128) start += 1;
14250
+ return `${prefix.toString("utf8")}${encoded.subarray(start).toString("utf8")}`;
14251
+ }
14104
14252
  function npmScript(name) {
14105
14253
  return name === "test" ? { label: name, command: "npm", args: ["test"] } : { label: name, command: "npm", args: ["run", name] };
14106
14254
  }
@@ -14194,6 +14342,35 @@ function normalizeToolCallInput(input, maxDepth = 3) {
14194
14342
  throw new Error(`Tool-call input must decode to an object; received ${kind}`);
14195
14343
  }
14196
14344
 
14345
+ // src/utils/abort.ts
14346
+ function waitWithSignal(operation, signal) {
14347
+ if (signal === void 0) return operation;
14348
+ return new Promise((resolve32, reject) => {
14349
+ const abort2 = () => reject(signal.reason);
14350
+ if (signal.aborted) abort2();
14351
+ else signal.addEventListener("abort", abort2, { once: true });
14352
+ operation.then(resolve32, reject).finally(() => signal.removeEventListener("abort", abort2));
14353
+ });
14354
+ }
14355
+ function createScopedAbortSignal(parent) {
14356
+ const controller = new AbortController();
14357
+ let disposed = false;
14358
+ const abort2 = () => {
14359
+ if (!controller.signal.aborted) controller.abort(parent?.reason);
14360
+ };
14361
+ if (parent?.aborted) abort2();
14362
+ else parent?.addEventListener("abort", abort2, { once: true });
14363
+ return {
14364
+ signal: controller.signal,
14365
+ dispose: () => {
14366
+ if (disposed) return;
14367
+ disposed = true;
14368
+ parent?.removeEventListener("abort", abort2);
14369
+ abort2();
14370
+ }
14371
+ };
14372
+ }
14373
+
14197
14374
  // src/models/anthropic.ts
14198
14375
  var CLAUDE_CLIENT_HEADERS = {
14199
14376
  "User-Agent": "claude-cli/2.1.0 (external, cli)",
@@ -14316,6 +14493,7 @@ var AnthropicModelAdapter = class {
14316
14493
  void appendUsageLog(line);
14317
14494
  }
14318
14495
  async *stream(request) {
14496
+ const requestAbort = createScopedAbortSignal(request.signal);
14319
14497
  let inputTokens = 0;
14320
14498
  let outputTokens = 0;
14321
14499
  let hasUsage = false;
@@ -14439,13 +14617,13 @@ var AnthropicModelAdapter = class {
14439
14617
  };
14440
14618
  let stream;
14441
14619
  try {
14442
- stream = await this.client.messages.create(body, { signal: request.signal });
14620
+ stream = await this.client.messages.create(body, { signal: requestAbort.signal });
14443
14621
  } catch (error) {
14444
14622
  if (this.thinkingBudget > 0 && !this.#thinkingRejected && isThinkingParamRejected(error)) {
14445
14623
  this.#thinkingRejected = true;
14446
14624
  delete body.thinking;
14447
14625
  stripThinkingBlocks(body.messages);
14448
- stream = await this.client.messages.create(body, { signal: request.signal });
14626
+ stream = await this.client.messages.create(body, { signal: requestAbort.signal });
14449
14627
  } else {
14450
14628
  throw error;
14451
14629
  }
@@ -14557,6 +14735,8 @@ var AnthropicModelAdapter = class {
14557
14735
  };
14558
14736
  }
14559
14737
  yield { type: "error", error: normalizeProviderError(error) };
14738
+ } finally {
14739
+ requestAbort.dispose();
14560
14740
  }
14561
14741
  }
14562
14742
  };
@@ -14701,6 +14881,7 @@ var OpenAIModelAdapter = class {
14701
14881
  void appendUsageLog(line);
14702
14882
  }
14703
14883
  async *stream(request) {
14884
+ const requestAbort = createScopedAbortSignal(request.signal);
14704
14885
  const callIds = /* @__PURE__ */ new Map();
14705
14886
  const pendingCalls = /* @__PURE__ */ new Map();
14706
14887
  const emittedCalls = /* @__PURE__ */ new Set();
@@ -14717,7 +14898,7 @@ var OpenAIModelAdapter = class {
14717
14898
  strict: tool.strict ?? true
14718
14899
  }))
14719
14900
  };
14720
- const stream = this.client.responses.stream(body, { signal: request.signal });
14901
+ const stream = this.client.responses.stream(body, { signal: requestAbort.signal });
14721
14902
  for await (const event of stream) {
14722
14903
  if (event.type === "response.output_item.added" && event.item?.type === "function_call" && event.output_index !== void 0 && event.item.call_id) {
14723
14904
  callIds.set(event.output_index, event.item.call_id);
@@ -14811,6 +14992,8 @@ var OpenAIModelAdapter = class {
14811
14992
  }
14812
14993
  } catch (error) {
14813
14994
  yield { type: "error", error: normalizeProviderError(error) };
14995
+ } finally {
14996
+ requestAbort.dispose();
14814
14997
  }
14815
14998
  }
14816
14999
  };
@@ -18219,19 +18402,6 @@ import { isIP } from "net";
18219
18402
  import { request as httpRequest } from "http";
18220
18403
  import { request as httpsRequest } from "https";
18221
18404
  import { z as z25 } from "zod";
18222
-
18223
- // src/utils/abort.ts
18224
- function waitWithSignal(operation, signal) {
18225
- if (signal === void 0) return operation;
18226
- return new Promise((resolve32, reject) => {
18227
- const abort2 = () => reject(signal.reason);
18228
- if (signal.aborted) abort2();
18229
- else signal.addEventListener("abort", abort2, { once: true });
18230
- operation.then(resolve32, reject).finally(() => signal.removeEventListener("abort", abort2));
18231
- });
18232
- }
18233
-
18234
- // src/tools/web.ts
18235
18405
  var WebFetchInput = z25.object({
18236
18406
  url: z25.string().url().max(8192),
18237
18407
  timeoutMs: z25.coerce.number().int().positive().max(12e4).optional(),
@@ -24063,6 +24233,18 @@ async function createProductionRuntime(options) {
24063
24233
  holders.sessionTree = { entries: sessionHistory.nodeCount };
24064
24234
  } catch {
24065
24235
  }
24236
+ try {
24237
+ const transcript = transcriptCensus(timelineState);
24238
+ holders.transcript = { entries: transcript.blocks, chars: transcript.chars };
24239
+ holders.transcriptTurns = { entries: transcript.turns };
24240
+ } catch {
24241
+ }
24242
+ try {
24243
+ holders.userTiming = {
24244
+ entries: performance.getEntriesByType("measure").length + performance.getEntriesByType("mark").length
24245
+ };
24246
+ } catch {
24247
+ }
24066
24248
  return holders;
24067
24249
  };
24068
24250
  const rotateForHeap = (continuation, reading) => {
@@ -24120,21 +24302,9 @@ async function createProductionRuntime(options) {
24120
24302
  await verifierEnvironment?.dispose();
24121
24303
  }
24122
24304
  },
24123
- confirmBudget: async (state, dimensions, signal) => {
24124
- if (options.approvalPolicy === "deny") return "unavailable";
24125
- const reached = dimensions.map((dimension) => dimension === "cycles" ? `${state.budget.cyclesUsed} cycles` : `${state.budget.inputTokens + state.budget.outputTokens} tokens`).join(" and ");
24126
- const next = dimensions.map((dimension) => dimension === "cycles" ? `${state.budget.cycleCheckpoint + state.config.cycleStep} cycles` : `${state.budget.tokenCheckpoint + state.config.tokenStep} tokens`).join(" and ");
24127
- const latestVerification = state.cycles.at(-1)?.verification.summary ?? "No host verification evidence yet.";
24128
- const answers = await questions.ask([{
24129
- header: "Loop budget",
24130
- question: `Loop ${state.loopId} reached ${reached}. Latest verification: ${latestVerification} Continue until the next checkpoint (${next})?`,
24131
- options: [
24132
- { label: "Continue", description: "Extend only the reached budget tranche and keep looping." },
24133
- { label: "Stop", description: "End this loop as budget exhausted." }
24134
- ]
24135
- }], signal);
24136
- return answers[0] === "Continue" ? "approved" : "rejected";
24137
- },
24305
+ // Cycle/token values are telemetry checkpoint cadence, not an unattended
24306
+ // execution cap. The user can still cancel explicitly at any time.
24307
+ confirmBudget: async () => "approved",
24138
24308
  fingerprint: workspaceFingerprint,
24139
24309
  idFactory: () => `loop-${(/* @__PURE__ */ new Date()).toISOString().replace(/[^0-9]/g, "").slice(0, 17)}-${randomUUID20().slice(0, 8)}`
24140
24310
  });
@@ -24145,8 +24315,9 @@ async function createProductionRuntime(options) {
24145
24315
  plannerModelId: mainModel,
24146
24316
  classifierModelId: mainModel,
24147
24317
  skepticCount: 3,
24148
- maxRounds: config.goal.maxRounds,
24149
- maxStallStreak: config.goal.maxStallStreak,
24318
+ // Production goals run until verified, explicitly cancelled, or blocked
24319
+ // by a non-recoverable external condition. Numeric config values remain
24320
+ // accepted for compatibility but no longer terminate autonomous work.
24150
24321
  persistence: goalStore,
24151
24322
  onRoundBoundary: (goalId) => maybeRotateAtBoundary({ kind: "goal", id: goalId }),
24152
24323
  onMemoryCheckpoint: (goalId) => maybeRotateAtBoundary({ kind: "goal", id: goalId }),
@@ -24287,6 +24458,7 @@ async function createProductionRuntime(options) {
24287
24458
  () => rotateForHeap(activeLongTask ?? { kind: "goal", id: goalId }, currentPressureReading())
24288
24459
  ),
24289
24460
  continuation: () => consumePendingContinuation(),
24461
+ rotationPending: () => memoryRotationActive(),
24290
24462
  mcp: async (command, signal) => {
24291
24463
  signal.throwIfAborted();
24292
24464
  const manager = await mcpReady;
@@ -24844,6 +25016,8 @@ async function* loopSessionEvents(events, hooks) {
24844
25016
  totalInputTokens,
24845
25017
  totalOutputTokens
24846
25018
  };
25019
+ } else if (event.event.type === "error" && isResumableLongTaskError(event.event.error.code)) {
25020
+ yield { type: "warning", message: `Loop worker segment ended at ${event.event.error.code}; checkpointing and verifying before continuing.` };
24847
25021
  } else if (event.event.type !== "done") yield event.event;
24848
25022
  continue;
24849
25023
  }
@@ -24898,7 +25072,7 @@ function loopProgressEvent(event) {
24898
25072
  loopId: event.loopId,
24899
25073
  phase: "budget",
24900
25074
  state: "info",
24901
- message: `Confirmation required for ${event.dimensions.join(" and ")} budget.`
25075
+ message: `${event.dimensions.join(" and ")} checkpoint reached; continuing automatically.`
24902
25076
  };
24903
25077
  }
24904
25078
  return {
@@ -24915,6 +25089,9 @@ function terminalProgressState(status) {
24915
25089
  if (status === "needs_human") return "info";
24916
25090
  return "failed";
24917
25091
  }
25092
+ function isResumableLongTaskError(code) {
25093
+ return code !== "authentication" && code !== "model_not_found" && code !== "cancelled" && code !== "memory_pressure";
25094
+ }
24918
25095
  async function workspaceFingerprint(workspace) {
24919
25096
  const hash = createHash18("sha256");
24920
25097
  const diff = await execFileNoThrow(
@@ -25574,6 +25751,13 @@ async function* goalSessionEvents(events) {
25574
25751
  yield { type: "notice", message: `Resuming goal ${event.goalId} at round ${event.round} after a heap rotation.` };
25575
25752
  continue;
25576
25753
  }
25754
+ if (event.type === "goal-stage-retry") {
25755
+ yield {
25756
+ type: "warning",
25757
+ message: `Goal ${event.stage} attempt ${event.attempt} hit a recoverable boundary (${event.reason}); retrying in ${event.delayMs}ms.`
25758
+ };
25759
+ continue;
25760
+ }
25577
25761
  if (event.type === "goal-plan-created") {
25578
25762
  yield { type: "notice", message: `Goal plan created (${event.plan.kind}) with ${event.plan.criteria.length} acceptance criteria.` };
25579
25763
  yield { type: "notice", message: `Plan file: ${event.planPath}` };
@@ -25600,6 +25784,8 @@ async function* goalSessionEvents(events) {
25600
25784
  totalInputTokens: event.event.usage.inputTokens,
25601
25785
  totalOutputTokens: event.event.usage.outputTokens
25602
25786
  };
25787
+ } else if (event.event.type === "error" && isResumableLongTaskError(event.event.error.code)) {
25788
+ yield { type: "warning", message: `Goal worker segment ended at ${event.event.error.code}; checkpointing and verifying before continuing.` };
25603
25789
  } else {
25604
25790
  yield event.event;
25605
25791
  }