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

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.
Files changed (33) hide show
  1. package/dist/agent/loop.d.ts +4 -2
  2. package/dist/{app-H7RYDMD3.js → app-UWIEIWBQ.js} +3 -3
  3. package/dist/{chunk-3BHKW5A7.js → chunk-4RR5VVX4.js} +8 -2
  4. package/dist/{chunk-NIKCPCJG.js → chunk-DCNUBQCH.js} +826 -273
  5. package/dist/{chunk-KBHWS7M6.js → chunk-KQMKY7C5.js} +4 -0
  6. package/dist/chunk-LKQTBL3R.js +107 -0
  7. package/dist/cli-main.js +20 -13
  8. package/dist/cli.js +24 -5
  9. package/dist/config/schema.d.ts +4 -0
  10. package/dist/context/manager.d.ts +9 -0
  11. package/dist/desktop/main.js +1170 -532
  12. package/dist/desktop-renderer/assets/{index-CYd5JkTR.js → index-C0t1nGih.js} +2 -2
  13. package/dist/desktop-renderer/assets/{interactive-terminal-j52Ty3QO.js → interactive-terminal-BJqe96mZ.js} +1 -1
  14. package/dist/desktop-renderer/index.html +1 -1
  15. package/dist/goal/classifier.d.ts +2 -0
  16. package/dist/goal/orchestrator.d.ts +18 -0
  17. package/dist/goal/planner.d.ts +2 -0
  18. package/dist/goal/store.d.ts +2 -0
  19. package/dist/goal/types.d.ts +8 -0
  20. package/dist/harness/journal.d.ts +2 -0
  21. package/dist/jobs/registry.d.ts +6 -0
  22. package/dist/{load-SLSL3IWR.js → load-F4AYLCF6.js} +1 -1
  23. package/dist/loop/orchestrator.d.ts +18 -0
  24. package/dist/loop/types.d.ts +7 -0
  25. package/dist/production.d.ts +17 -0
  26. package/dist/sdk/index.js +4 -4
  27. package/dist/session/tree.d.ts +2 -0
  28. package/dist/ui/session.d.ts +9 -0
  29. package/dist/utils/heap.d.ts +58 -0
  30. package/dist/utils/memory-restart.d.ts +38 -3
  31. package/dist/utils/rotation-census.d.ts +36 -0
  32. package/package.json +1 -1
  33. package/dist/chunk-CIVPKHF4.js +0 -53
@@ -127,6 +127,10 @@ var FlavorConfigSchema = z.object({
127
127
  maxTokens: z.number().int().positive().max(Number.MAX_SAFE_INTEGER).default(5e5),
128
128
  isolation: z.literal("auto").default("auto")
129
129
  }).prefault({}),
130
+ goal: z.object({
131
+ maxRounds: z.number().int().positive().max(1e4).default(100),
132
+ maxStallStreak: z.number().int().positive().max(100).default(3)
133
+ }).prefault({}),
130
134
  context: z.object({
131
135
  windowTokens: z.number().int().positive().default(2e5),
132
136
  reservedOutputTokens: z.number().int().nonnegative().default(2e4),
@@ -0,0 +1,107 @@
1
+ #!/usr/bin/env node
2
+ import { createRequire } from "node:module"; const require = createRequire(import.meta.url);
3
+
4
+ // src/utils/memory-restart.ts
5
+ import { join } from "path";
6
+ var MEMORY_RESTART_EXIT_CODE = 75;
7
+ var MEMORY_RESTART_MARKER_FILE = "memory-restart.json";
8
+ var MEMORY_RESTART_MAX_ATTEMPTS = 24;
9
+ var MEMORY_RESTART_WINDOW_MS = 60 * 60 * 1e3;
10
+ var MEMORY_RESTART_MIN_GAP_MS = 5 * 60 * 1e3;
11
+ var MEMORY_RESTART_CONTINUATION_TTL_MS = 10 * 60 * 1e3;
12
+ var SESSION_ID_PATTERN = /^session-[A-Za-z0-9_-]+$/u;
13
+ var rotationActive = false;
14
+ function markMemoryRotation() {
15
+ rotationActive = true;
16
+ }
17
+ function memoryRotationActive() {
18
+ return rotationActive;
19
+ }
20
+ function memoryRestartMarkerPath(workspace) {
21
+ return join(workspace, ".flavor", "tmp", MEMORY_RESTART_MARKER_FILE);
22
+ }
23
+ function nextMemoryRestartMarker(previous, sessionId, now, continuation) {
24
+ const previousWindowStart = previous?.windowStartedAt ?? previous?.requestedAt;
25
+ const reusable = previous !== void 0 && previous.sessionId === sessionId && previousWindowStart !== void 0 && now.getTime() - Date.parse(previousWindowStart) < MEMORY_RESTART_WINDOW_MS && Number.isInteger(previous.attempts) && previous.attempts > 0;
26
+ return {
27
+ sessionId,
28
+ windowStartedAt: reusable ? previousWindowStart : now.toISOString(),
29
+ requestedAt: now.toISOString(),
30
+ attempts: reusable ? previous.attempts + 1 : 1,
31
+ ...continuation === void 0 ? {} : { continuation }
32
+ };
33
+ }
34
+ function consumedMemoryRestartMarker(marker) {
35
+ return {
36
+ sessionId: marker.sessionId,
37
+ ...marker.windowStartedAt === void 0 ? {} : { windowStartedAt: marker.windowStartedAt },
38
+ requestedAt: marker.requestedAt,
39
+ attempts: marker.attempts
40
+ };
41
+ }
42
+ function rotationCooldownActive(marker, sessionId, now) {
43
+ return marker !== void 0 && marker.sessionId === sessionId && now.getTime() - Date.parse(marker.requestedAt) < MEMORY_RESTART_MIN_GAP_MS;
44
+ }
45
+ function pendingContinuation(marker, sessionId, now) {
46
+ if (marker === void 0 || marker.sessionId !== sessionId) return void 0;
47
+ if (now.getTime() - Date.parse(marker.requestedAt) > MEMORY_RESTART_CONTINUATION_TTL_MS) return void 0;
48
+ const continuation = marker.continuation;
49
+ if (continuation === void 0) return void 0;
50
+ if (continuation.kind !== "loop" && continuation.kind !== "goal") return void 0;
51
+ if (typeof continuation.id !== "string" || continuation.id.length === 0) return void 0;
52
+ return continuation;
53
+ }
54
+ function shouldRelaunchForMemoryRestart(marker) {
55
+ return marker !== void 0 && SESSION_ID_PATTERN.test(marker.sessionId) && Number.isInteger(marker.attempts) && marker.attempts >= 1 && marker.attempts <= MEMORY_RESTART_MAX_ATTEMPTS;
56
+ }
57
+ function memoryRestartArgs(argv, marker) {
58
+ if (!shouldRelaunchForMemoryRestart(marker)) return void 0;
59
+ const stripped = [];
60
+ for (let index = 0; index < argv.length; index += 1) {
61
+ const argument = argv[index];
62
+ if (argument === "--resume") {
63
+ if (index + 1 < argv.length && !argv[index + 1].startsWith("-")) index += 1;
64
+ continue;
65
+ }
66
+ if (argument.startsWith("--resume=")) continue;
67
+ if (argument === "--memory-restart") continue;
68
+ stripped.push(argument);
69
+ }
70
+ return [...stripped, "--resume", marker.sessionId, "--memory-restart"];
71
+ }
72
+ function parseMemoryRestartMarker(json) {
73
+ try {
74
+ const value = JSON.parse(json);
75
+ if (typeof value.sessionId !== "string" || typeof value.requestedAt !== "string" || typeof value.attempts !== "number") {
76
+ return void 0;
77
+ }
78
+ const continuation = value.continuation;
79
+ if (continuation !== void 0 && (typeof continuation !== "object" || continuation === null || continuation.kind !== "loop" && continuation.kind !== "goal" || typeof continuation.id !== "string")) {
80
+ return void 0;
81
+ }
82
+ const windowStartedAt = value.windowStartedAt;
83
+ if (windowStartedAt !== void 0 && typeof windowStartedAt !== "string") return void 0;
84
+ return {
85
+ sessionId: value.sessionId,
86
+ ...windowStartedAt === void 0 ? {} : { windowStartedAt },
87
+ requestedAt: value.requestedAt,
88
+ attempts: value.attempts,
89
+ ...continuation === void 0 ? {} : { continuation }
90
+ };
91
+ } catch {
92
+ return void 0;
93
+ }
94
+ }
95
+
96
+ export {
97
+ MEMORY_RESTART_EXIT_CODE,
98
+ markMemoryRotation,
99
+ memoryRotationActive,
100
+ memoryRestartMarkerPath,
101
+ nextMemoryRestartMarker,
102
+ consumedMemoryRestartMarker,
103
+ rotationCooldownActive,
104
+ pendingContinuation,
105
+ memoryRestartArgs,
106
+ parseMemoryRestartMarker
107
+ };
package/dist/cli-main.js CHANGED
@@ -3,7 +3,7 @@ import { createRequire } from "node:module"; const require = createRequire(impor
3
3
  import {
4
4
  FlavorRpcServer,
5
5
  TraceRecorder
6
- } from "./chunk-3BHKW5A7.js";
6
+ } from "./chunk-4RR5VVX4.js";
7
7
  import {
8
8
  staticTaskLines
9
9
  } from "./chunk-2BF7CVWZ.js";
@@ -29,8 +29,10 @@ import {
29
29
  redactErrorText,
30
30
  runDoctor,
31
31
  runPalBroker
32
- } from "./chunk-NIKCPCJG.js";
33
- import "./chunk-CIVPKHF4.js";
32
+ } from "./chunk-DCNUBQCH.js";
33
+ import {
34
+ MEMORY_RESTART_EXIT_CODE
35
+ } from "./chunk-LKQTBL3R.js";
34
36
  import {
35
37
  McpServerConfigSchema,
36
38
  McpServerNameSchema,
@@ -39,7 +41,7 @@ import {
39
41
  readRecoverableFile,
40
42
  setProjectSkillDisabled,
41
43
  updateProtectedFile
42
- } from "./chunk-KBHWS7M6.js";
44
+ } from "./chunk-KQMKY7C5.js";
43
45
  import {
44
46
  gt,
45
47
  isValidVersion
@@ -728,7 +730,7 @@ async function runEvaluationFile(path, outputPath) {
728
730
  })),
729
731
  ...parsed.maxTokens === void 0 ? {} : { maxTokens: parsed.maxTokens }
730
732
  };
731
- const configured = await import("./load-SLSL3IWR.js").then(({ loadConfig: loadConfig2 }) => loadConfig2({ cwd: workspace, home: process.env.USERPROFILE ?? process.env.HOME ?? workspace }));
733
+ const configured = await import("./load-F4AYLCF6.js").then(({ loadConfig: loadConfig2 }) => loadConfig2({ cwd: workspace, home: process.env.USERPROFILE ?? process.env.HOME ?? workspace }));
732
734
  const environment = createExecutionEnvironment(workspace, configured.config.execution) ?? new LocalExecutionEnvironment();
733
735
  let report;
734
736
  try {
@@ -752,7 +754,7 @@ async function runEvaluationFile(path, outputPath) {
752
754
 
753
755
  // src/cli.tsx
754
756
  function createProgram(dependencies = {}) {
755
- const program = new Command().name("flavor").description("Interactive coding agent").version(packageVersion()).option("-p, --print <prompt>", "run one prompt without the interactive UI").option("--resume [session-id]", "resume a saved session (latest when id is omitted)").option("--mode <mode>", "runtime mode: interactive or rpc").option("--workspace <path>", "workspace path (RPC mode)").option("--rpc-approvals", "allow an RPC client to resolve interactive tool approvals").option("--rpc-streamed-writes", "stream proposed file writes to an RPC client before committing them").option("--trace <path>", "write a redacted JSONL execution trace").option("--pal-name <alias>", "name this interactive Flavor instance for /pals and /chat").addOption(new Option("--pals-broker <address>").hideHelp());
757
+ const program = new Command().name("flavor").description("Interactive coding agent").version(packageVersion()).option("-p, --print <prompt>", "run one prompt without the interactive UI").option("--resume [session-id]", "resume a saved session (latest when id is omitted)").option("--mode <mode>", "runtime mode: interactive or rpc").option("--workspace <path>", "workspace path (RPC mode)").option("--rpc-approvals", "allow an RPC client to resolve interactive tool approvals").option("--rpc-streamed-writes", "stream proposed file writes to an RPC client before committing them").option("--trace <path>", "write a redacted JSONL execution trace").option("--pal-name <alias>", "name this interactive Flavor instance for /pals and /chat").addOption(new Option("--memory-restart").hideHelp()).addOption(new Option("--pals-broker <address>").hideHelp());
756
758
  program.command("init [directory]").description("Initialize Flavor project files in a directory (defaults to cwd)").action(async (directory) => {
757
759
  const cwd = directory ? resolve3(directory) : process.cwd();
758
760
  try {
@@ -869,7 +871,7 @@ function createProgram(dependencies = {}) {
869
871
  return;
870
872
  }
871
873
  if (options.print !== void 0) {
872
- process.exitCode = await runPrint(options.print, {}, resumeSession);
874
+ process.exitCode = await runPrint(options.print, {}, resumeSession, options.memoryRestart === true);
873
875
  return;
874
876
  }
875
877
  if (!(dependencies.isTTY?.() ?? process.stdin.isTTY)) {
@@ -898,7 +900,7 @@ async function runInteractiveCli(props) {
898
900
  const [{ render, AlternateScreen }, { createElement }, { App }] = await Promise.all([
899
901
  import("./claude-ink-ECZ6QF3P.js"),
900
902
  import("react"),
901
- import("./app-H7RYDMD3.js")
903
+ import("./app-UWIEIWBQ.js")
902
904
  ]);
903
905
  const instance = await render(createElement(
904
906
  AlternateScreen,
@@ -961,7 +963,7 @@ async function runRpcMode(options) {
961
963
  });
962
964
  await server.start();
963
965
  await recorder?.close();
964
- return 0;
966
+ return process.exitCode === MEMORY_RESTART_EXIT_CODE ? MEMORY_RESTART_EXIT_CODE : 0;
965
967
  } catch (error) {
966
968
  await recorder?.close().catch(() => void 0);
967
969
  process.stderr.write(`rpc: ${safeError(error)}
@@ -972,7 +974,7 @@ async function runRpcMode(options) {
972
974
  function setInteractiveProcessTitle(target = process) {
973
975
  target.title = "Flavor Code";
974
976
  }
975
- async function runPrint(prompt, dependencies = {}, resumeSession) {
977
+ async function runPrint(prompt, dependencies = {}, resumeSession, memoryRestart = false) {
976
978
  let code = 0;
977
979
  let runtime;
978
980
  const stdout = dependencies.stdout ?? ((text) => process.stdout.write(text));
@@ -1004,7 +1006,11 @@ async function runPrint(prompt, dependencies = {}, resumeSession) {
1004
1006
  }
1005
1007
  try {
1006
1008
  await runtime.session.start();
1007
- await runtime.session.submit(prompt);
1009
+ if (runtime.session.rotationContinuationResumed) {
1010
+ await runtime.session.whenIdle();
1011
+ } else if (!memoryRestart) {
1012
+ await runtime.session.submit(prompt);
1013
+ }
1008
1014
  } catch (error) {
1009
1015
  stderr(`runtime: ${safeError(error)}
1010
1016
  `);
@@ -1025,8 +1031,9 @@ async function runPrint(prompt, dependencies = {}, resumeSession) {
1025
1031
  code = 1;
1026
1032
  }
1027
1033
  }
1028
- if (code === 0) stdout("\n");
1029
- return code;
1034
+ const restarting = process.exitCode === MEMORY_RESTART_EXIT_CODE;
1035
+ if (code === 0 && !restarting) stdout("\n");
1036
+ return restarting ? MEMORY_RESTART_EXIT_CODE : code;
1030
1037
  }
1031
1038
  function safeError(error) {
1032
1039
  return redactErrorText(message(error));
package/dist/cli.js CHANGED
@@ -5,19 +5,23 @@ import {
5
5
  memoryRestartArgs,
6
6
  memoryRestartMarkerPath,
7
7
  parseMemoryRestartMarker
8
- } from "./chunk-CIVPKHF4.js";
8
+ } from "./chunk-LKQTBL3R.js";
9
9
  import "./chunk-S32JRDZK.js";
10
10
 
11
11
  // src/launcher.ts
12
12
  import { spawn } from "child_process";
13
13
  import { realpathSync, readFileSync } from "fs";
14
+ import { totalmem } from "os";
14
15
  import { fileURLToPath } from "url";
15
16
  function needsRelaunch(runtime) {
16
17
  if (!runtime.execArgv.some((argument) => argument === "--report-on-fatalerror")) return true;
17
- return !runtime.execArgv.some((argument) => argument.startsWith("--heapsnapshot-near-heap-limit"));
18
+ if (!runtime.execArgv.some((argument) => argument.startsWith("--heapsnapshot-near-heap-limit"))) return true;
19
+ return !runtime.execArgv.some((argument) => argument === "--expose-gc");
18
20
  }
21
+ var HEAP_HEADROOM_MB = 8192;
22
+ var HEAP_HEADROOM_MIN_TOTALMEM = 12 * 1024 * 1024 * 1024;
19
23
  function cliMainArguments(mainPath, argv) {
20
- return [
24
+ const flags = [
21
25
  // Fatal V8 errors (heap OOM, native crashes) bypass crash-guard.ts and
22
26
  // leave only an unusable native stack. Ask Node to dump a diagnostic
23
27
  // report next to the working directory so the next crash has a scene.
@@ -27,9 +31,24 @@ function cliMainArguments(mainPath, argv) {
27
31
  // Capture one heap snapshot as the heap nears its limit so the next
28
32
  // crash leaves a .heapsnapshot retainer trail next to the report.
29
33
  "--heapsnapshot-near-heap-limit=1",
30
- mainPath,
31
- ...argv
34
+ // The heap watermarks are GC-verified: heapUsed counts uncollected
35
+ // garbage, and stopping turns or rotating on garbage alone would waste
36
+ // work. Exposing gc lets the watermark measure the live set instead.
37
+ "--expose-gc"
32
38
  ];
39
+ const processHeapFlag = process.execArgv.find((argument) => argument.startsWith("--max-old-space-size"));
40
+ const argvHeapFlag = argv.find((argument) => argument.startsWith("--max-old-space-size"));
41
+ const userHeapFlag = processHeapFlag ?? argvHeapFlag;
42
+ if (userHeapFlag !== void 0) {
43
+ flags.push(userHeapFlag);
44
+ } else {
45
+ const constrained = process.constrainedMemory?.() ?? 0;
46
+ const availableLimit = constrained > 0 ? Math.min(totalmem(), constrained) : totalmem();
47
+ if (availableLimit >= HEAP_HEADROOM_MIN_TOTALMEM) {
48
+ flags.push(`--max-old-space-size=${HEAP_HEADROOM_MB}`);
49
+ }
50
+ }
51
+ return [...flags, mainPath, ...argv.filter((argument) => argument !== argvHeapFlag)];
33
52
  }
34
53
  async function launchCli() {
35
54
  const mainUrl = new URL("./cli-main.js", import.meta.url);
@@ -164,6 +164,10 @@ export declare const FlavorConfigSchema: z.ZodObject<{
164
164
  maxTokens: z.ZodDefault<z.ZodNumber>;
165
165
  isolation: z.ZodDefault<z.ZodLiteral<"auto">>;
166
166
  }, z.core.$strip>>;
167
+ goal: z.ZodPrefault<z.ZodObject<{
168
+ maxRounds: z.ZodDefault<z.ZodNumber>;
169
+ maxStallStreak: z.ZodDefault<z.ZodNumber>;
170
+ }, z.core.$strip>>;
167
171
  context: z.ZodPrefault<z.ZodObject<{
168
172
  windowTokens: z.ZodDefault<z.ZodNumber>;
169
173
  reservedOutputTokens: z.ZodDefault<z.ZodNumber>;
@@ -97,6 +97,15 @@ export declare class ContextManager {
97
97
  restore(snapshot: ContextSnapshot): void;
98
98
  messagesForModel(): ModelMessage[];
99
99
  estimatedTokens(): number;
100
+ /** Retained sizes for the rotation census: what this context holds right now. */
101
+ census(): {
102
+ messages: number;
103
+ messageChars: number;
104
+ visibilityEntries: number;
105
+ visibilityChars: number;
106
+ transientEntries: number;
107
+ transientChars: number;
108
+ };
100
109
  needsCompaction(): boolean;
101
110
  get lastRecordedInputTokens(): number | undefined;
102
111
  get consecutiveAutoCompactFailures(): number;