openfox 2.0.0-beta.5 → 2.0.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.
Files changed (48) hide show
  1. package/dist/{chat-handler-BM6EBFKS.js → chat-handler-27FY5TEH.js} +14 -27
  2. package/dist/{chunk-CLGRPDZT.js → chunk-3FFHHYWP.js} +174 -90
  3. package/dist/{chunk-HGAMIPFO.js → chunk-7FDY3EOH.js} +18 -176
  4. package/dist/{chunk-V6DYJV5R.js → chunk-7TTEGAO6.js} +12 -10
  5. package/dist/{chunk-T4HV6GS5.js → chunk-BVHFMAVN.js} +1 -1
  6. package/dist/{chunk-OBD6D5ZO.js → chunk-CDDMHSZF.js} +4 -4
  7. package/dist/{chunk-HHAJ3QF6.js → chunk-EMJGF3A7.js} +9 -24
  8. package/dist/{chunk-DFWGWULB.js → chunk-FRCLNAE2.js} +2935 -3127
  9. package/dist/{chunk-RF2JGDSS.js → chunk-IGSZUXQP.js} +3 -3
  10. package/dist/{chunk-UH27FDT7.js → chunk-NPHYEUYE.js} +124 -12
  11. package/dist/{chunk-NNEHZT3V.js → chunk-QUSUEN5R.js} +3 -3
  12. package/dist/{chunk-I67T2E5K.js → chunk-R3EK3ODJ.js} +6 -6
  13. package/dist/{chunk-X36JIWSF.js → chunk-RBJ25FIR.js} +135 -147
  14. package/dist/{chunk-PSJIT5LZ.js → chunk-RFNEDBVO.js} +4 -4
  15. package/dist/chunk-RI6GAMNP.js +13 -0
  16. package/dist/{chunk-XL7SW5B4.js → chunk-XJEOP6XU.js} +5 -4
  17. package/dist/cli/dev.js +1 -1
  18. package/dist/cli/index.js +1 -1
  19. package/dist/compactor-TDNRM3A7.js +9 -0
  20. package/dist/{config-YU7NYZYG.js → config-VBBMTXJZ.js} +5 -6
  21. package/dist/{events-T37KTNCX.js → events-PQ3KTI5H.js} +8 -7
  22. package/dist/{folding-IIKWAGG2.js → folding-CXORTBAU.js} +2 -2
  23. package/dist/{orchestrator-JTUHXF7C.js → orchestrator-QDCVUBCL.js} +18 -22
  24. package/dist/package.json +1 -1
  25. package/dist/{processor-66YBBQ6B.js → processor-K4B7QRO2.js} +8 -9
  26. package/dist/{protocol-ALBHLBBF.js → protocol-CN24IKQN.js} +3 -5
  27. package/dist/{protocol-CiYlonA5.d.ts → protocol-De-Pzgry.d.ts} +10 -0
  28. package/dist/{provider-HTJEB3ON.js → provider-NRWSSO55.js} +9 -10
  29. package/dist/{serve-EUEQ6ERI.js → serve-J6EM3G6N.js} +25 -26
  30. package/dist/server/index.d.ts +1 -6
  31. package/dist/server/index.js +15 -16
  32. package/dist/{settings-VRAOAPLA.js → settings-6XX56F3F.js} +2 -2
  33. package/dist/shared/index.d.ts +2 -2
  34. package/dist/shared/index.js +1 -1
  35. package/dist/{tools-56LACLKH.js → tools-GCANYSB7.js} +11 -13
  36. package/dist/web/assets/{index-C5gaXnFj.css → index-aa3jRAZ9.css} +2 -2
  37. package/dist/web/assets/index-ntDiC8b_.js +299 -0
  38. package/dist/web/index.html +2 -2
  39. package/dist/web/sw.js +1 -1
  40. package/dist/workflow-defaults/default.workflow.json +3 -3
  41. package/package.json +1 -1
  42. package/dist/auto-compaction-2ZAYDXGF.js +0 -27
  43. package/dist/chunk-CGZHLM3K.js +0 -70
  44. package/dist/chunk-SZKEGZIB.js +0 -140
  45. package/dist/runtime-config-NPSPMRDA.js +0 -9
  46. package/dist/vision-fallback-3MIZZ6NQ.js +0 -22
  47. package/dist/web/assets/index-DQ2Vv-Yl.js +0 -317
  48. /package/dist/web/{workbox-7fc22fbe.js → workbox-8c29f6e4.js} +0 -0
@@ -2,11 +2,11 @@ import {
2
2
  clearModelCache,
3
3
  createLLMClient,
4
4
  detectModel
5
- } from "./chunk-HHAJ3QF6.js";
5
+ } from "./chunk-EMJGF3A7.js";
6
6
  import {
7
7
  detectBackend,
8
8
  getModelProfile
9
- } from "./chunk-HGAMIPFO.js";
9
+ } from "./chunk-7FDY3EOH.js";
10
10
  import {
11
11
  logger
12
12
  } from "./chunk-K44MW7JJ.js";
@@ -511,4 +511,4 @@ export {
511
511
  parseDefaultModelSelection,
512
512
  createProviderManager
513
513
  };
514
- //# sourceMappingURL=chunk-RF2JGDSS.js.map
514
+ //# sourceMappingURL=chunk-IGSZUXQP.js.map
@@ -1,6 +1,3 @@
1
- import {
2
- getDatabase
3
- } from "./chunk-FBGWG4N6.js";
4
1
  import {
5
2
  buildContextMessagesFromEventHistory,
6
3
  buildMessagesFromStoredEvents,
@@ -8,10 +5,10 @@ import {
8
5
  foldContextState,
9
6
  foldSessionState,
10
7
  spreadOptionalMessageFields
11
- } from "./chunk-XL7SW5B4.js";
8
+ } from "./chunk-XJEOP6XU.js";
12
9
  import {
13
- getRuntimeConfig
14
- } from "./chunk-CGZHLM3K.js";
10
+ getDatabase
11
+ } from "./chunk-FBGWG4N6.js";
15
12
  import {
16
13
  logger
17
14
  } from "./chunk-K44MW7JJ.js";
@@ -104,6 +101,18 @@ var EventStore = class {
104
101
  CREATE INDEX IF NOT EXISTS idx_events_session_type
105
102
  ON events(session_id, event_type)
106
103
  `);
104
+ this.db.exec(`
105
+ CREATE TABLE IF NOT EXISTS tombstones (
106
+ session_id TEXT NOT NULL,
107
+ seq INTEGER NOT NULL,
108
+ timestamp INTEGER NOT NULL,
109
+ UNIQUE(session_id, seq)
110
+ )
111
+ `);
112
+ this.db.exec(`
113
+ CREATE INDEX IF NOT EXISTS idx_tombstones_session_seq
114
+ ON tombstones(session_id, seq)
115
+ `);
107
116
  }
108
117
  // --------------------------------------------------------------------------
109
118
  // Append
@@ -182,10 +191,41 @@ var EventStore = class {
182
191
  * Get all events for a session, optionally starting from a specific seq
183
192
  */
184
193
  getEvents(sessionId, fromSeq) {
185
- const query = fromSeq !== void 0 ? `SELECT * FROM events WHERE session_id = ? AND seq >= ? ORDER BY seq` : `SELECT * FROM events WHERE session_id = ? ORDER BY seq`;
194
+ const query = fromSeq !== void 0 ? `SELECT e.* FROM events e
195
+ LEFT JOIN tombstones t ON e.session_id = t.session_id AND e.seq = t.seq
196
+ WHERE e.session_id = ? AND e.seq >= ? AND t.seq IS NULL
197
+ ORDER BY e.seq` : `SELECT e.* FROM events e
198
+ LEFT JOIN tombstones t ON e.session_id = t.session_id AND e.seq = t.seq
199
+ WHERE e.session_id = ? AND t.seq IS NULL
200
+ ORDER BY e.seq`;
186
201
  const rows = fromSeq !== void 0 ? this.db.prepare(query).all(sessionId, fromSeq) : this.db.prepare(query).all(sessionId);
187
202
  return rows.map((row) => this.rowToStoredEvent(row));
188
203
  }
204
+ /**
205
+ * Soft-delete (tombstone) events by sequence number.
206
+ * Tombstoned events are hidden from getEvents but remain in the database.
207
+ *
208
+ * @param sessionId - The session ID
209
+ * @param seqs - Array of sequence numbers to tombstone
210
+ * @returns The number of events tombstoned
211
+ */
212
+ tombstoneEvents(sessionId, seqs) {
213
+ if (seqs.length === 0) return 0;
214
+ const timestamp = Date.now();
215
+ const insert = this.db.prepare(
216
+ `INSERT OR IGNORE INTO tombstones (session_id, seq, timestamp)
217
+ VALUES (?, ?, ?)`
218
+ );
219
+ let count = 0;
220
+ const transaction = this.db.transaction(() => {
221
+ for (const seq of seqs) {
222
+ const result = insert.run(sessionId, seq, timestamp);
223
+ count += result.changes;
224
+ }
225
+ });
226
+ transaction();
227
+ return count;
228
+ }
189
229
  /**
190
230
  * Get the latest sequence number for a session
191
231
  */
@@ -791,6 +831,70 @@ function mapSessionSummaryRow(row) {
791
831
  };
792
832
  }
793
833
 
834
+ // src/server/config.ts
835
+ import { z } from "zod";
836
+ var backendSchema = z.enum(["auto", "vllm", "sglang", "ollama", "llamacpp"]).default("auto");
837
+ var envSchema = z.object({
838
+ // New env var name, with fallback to old name for backward compatibility
839
+ OPENFOX_LLM_URL: z.string().url().optional(),
840
+ OPENFOX_VLLM_URL: z.string().url().optional(),
841
+ OPENFOX_BACKEND: backendSchema,
842
+ OPENFOX_MODEL_NAME: z.string().default("qwen3.5-122b-int4-autoround"),
843
+ OPENFOX_MAX_CONTEXT: z.coerce.number().default(2e5),
844
+ OPENFOX_PORT: z.coerce.number().default(10369),
845
+ OPENFOX_HOST: z.string().optional(),
846
+ OPENFOX_WORKDIR: z.string().optional(),
847
+ OPENFOX_DB_PATH: z.string().default("./openfox.db"),
848
+ OPENFOX_LOG_LEVEL: z.enum(["debug", "info", "warn", "error"]).default("info"),
849
+ OPENFOX_DISABLE_THINKING: z.coerce.boolean().default(false),
850
+ OPENFOX_DEV: z.coerce.boolean().default(false)
851
+ });
852
+ function loadConfig() {
853
+ const env = envSchema.parse(process.env);
854
+ const llmUrl = env.OPENFOX_LLM_URL ?? env.OPENFOX_VLLM_URL ?? "http://localhost:8000/v1";
855
+ const workdir = env.OPENFOX_WORKDIR ?? process.cwd();
856
+ return {
857
+ llm: {
858
+ baseUrl: llmUrl,
859
+ model: env.OPENFOX_MODEL_NAME,
860
+ timeout: 3e5,
861
+ // 5 minutes (deprecated, kept for backward compatibility)
862
+ idleTimeout: 3e5,
863
+ // 5 minutes of inactivity
864
+ backend: env.OPENFOX_BACKEND,
865
+ disableThinking: env.OPENFOX_DISABLE_THINKING
866
+ },
867
+ context: {
868
+ maxTokens: env.OPENFOX_MAX_CONTEXT,
869
+ compactionThreshold: 0.85,
870
+ compactionTarget: 0.6
871
+ },
872
+ agent: {
873
+ maxIterations: 10,
874
+ maxConsecutiveFailures: 3,
875
+ toolTimeout: 3e5
876
+ },
877
+ server: {
878
+ port: env.OPENFOX_PORT,
879
+ ...env.OPENFOX_HOST !== void 0 ? { host: env.OPENFOX_HOST } : {}
880
+ },
881
+ database: {
882
+ path: env.OPENFOX_DB_PATH
883
+ },
884
+ mode: env.OPENFOX_DEV ? "development" : "production",
885
+ workdir
886
+ };
887
+ }
888
+
889
+ // src/server/runtime-config.ts
890
+ var runtimeConfig = null;
891
+ function setRuntimeConfig(config) {
892
+ runtimeConfig = config;
893
+ }
894
+ function getRuntimeConfig() {
895
+ return runtimeConfig ?? loadConfig();
896
+ }
897
+
794
898
  // src/server/events/session.ts
795
899
  function toSnapshotMessage(message) {
796
900
  return {
@@ -873,6 +977,10 @@ function getCurrentContextWindowId(sessionId) {
873
977
  const contextResult = foldContextState(events, "");
874
978
  return contextResult.currentContextWindowId || void 0;
875
979
  }
980
+ function getCurrentWindowMessageOptions(sessionId) {
981
+ const contextWindowId = getCurrentContextWindowId(sessionId);
982
+ return contextWindowId ? { contextWindowId } : void 0;
983
+ }
876
984
  function getReadFilesCache(sessionId) {
877
985
  const state = getSessionState(sessionId);
878
986
  return state?.readFiles ?? [];
@@ -1097,11 +1205,11 @@ function emitChatError(sessionId, error, recoverable) {
1097
1205
  data: { error, recoverable }
1098
1206
  });
1099
1207
  }
1100
- function emitFormatRetry(sessionId, attempt, maxAttempts) {
1208
+ function emitPatternRetry(sessionId, messageId, pattern, field, attempt, maxAttempts, matchedContent) {
1101
1209
  const eventStore = getEventStore();
1102
1210
  eventStore.append(sessionId, {
1103
- type: "format.retry",
1104
- data: { attempt, maxAttempts }
1211
+ type: "pattern.retry",
1212
+ data: { messageId, pattern, field, attempt, maxAttempts, matchedContent }
1105
1213
  });
1106
1214
  }
1107
1215
  function emitTurnSnapshot(sessionId, snapshot) {
@@ -1227,10 +1335,14 @@ export {
1227
1335
  listSessions,
1228
1336
  listSessionsByProject,
1229
1337
  deleteSession,
1338
+ loadConfig,
1339
+ setRuntimeConfig,
1340
+ getRuntimeConfig,
1230
1341
  getSessionState,
1231
1342
  getCurrentWindowMessages,
1232
1343
  getContextMessages,
1233
1344
  getCurrentContextWindowId,
1345
+ getCurrentWindowMessageOptions,
1234
1346
  getReadFilesCache,
1235
1347
  isFileInCache,
1236
1348
  emitSessionInitialized,
@@ -1255,10 +1367,10 @@ export {
1255
1367
  emitContextState,
1256
1368
  emitChatDone,
1257
1369
  emitChatError,
1258
- emitFormatRetry,
1370
+ emitPatternRetry,
1259
1371
  emitTurnSnapshot,
1260
1372
  truncateSessionMessages,
1261
1373
  compactContext,
1262
1374
  getRecentUserPromptsForSession
1263
1375
  };
1264
- //# sourceMappingURL=chunk-UH27FDT7.js.map
1376
+ //# sourceMappingURL=chunk-NPHYEUYE.js.map
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  detectModel
3
- } from "./chunk-HHAJ3QF6.js";
3
+ } from "./chunk-EMJGF3A7.js";
4
4
  import {
5
5
  detectBackend
6
- } from "./chunk-HGAMIPFO.js";
6
+ } from "./chunk-7FDY3EOH.js";
7
7
  import {
8
8
  getGlobalConfigPath
9
9
  } from "./chunk-CQGTEGKL.js";
@@ -435,4 +435,4 @@ export {
435
435
  activateProvider,
436
436
  mergeConfigs
437
437
  };
438
- //# sourceMappingURL=chunk-NNEHZT3V.js.map
438
+ //# sourceMappingURL=chunk-QUSUEN5R.js.map
@@ -33,7 +33,7 @@ Options:
33
33
  }
34
34
  async function runNetworkSetup(mode) {
35
35
  const { loadAuthConfig, saveAuthConfig, encryptPassword } = await import("./auth-56SIRACI.js");
36
- const { saveGlobalConfig } = await import("./config-YU7NYZYG.js");
36
+ const { saveGlobalConfig } = await import("./config-VBBMTXJZ.js");
37
37
  const { getAuthKeyPath } = await import("./paths-X46PPOI2.js");
38
38
  const existingAuth = await loadAuthConfig(mode);
39
39
  if (existingAuth) {
@@ -94,7 +94,7 @@ async function runNetworkSetup(mode) {
94
94
  console.log("\u2713 Configuration saved!\n");
95
95
  }
96
96
  async function runConfig(mode) {
97
- const { loadGlobalConfig, getActiveProvider, getDefaultModel } = await import("./config-YU7NYZYG.js");
97
+ const { loadGlobalConfig, getActiveProvider, getDefaultModel } = await import("./config-VBBMTXJZ.js");
98
98
  const { getGlobalConfigPath } = await import("./paths-X46PPOI2.js");
99
99
  const config = await loadGlobalConfig(mode);
100
100
  const configPath = getGlobalConfigPath(mode);
@@ -151,7 +151,7 @@ async function runCli(options) {
151
151
  break;
152
152
  }
153
153
  case "provider": {
154
- const { runProviderCommand } = await import("./provider-HTJEB3ON.js");
154
+ const { runProviderCommand } = await import("./provider-NRWSSO55.js");
155
155
  const [, subcommand] = positionals;
156
156
  await runProviderCommand(mode, subcommand);
157
157
  break;
@@ -188,12 +188,12 @@ async function runCli(options) {
188
188
  break;
189
189
  }
190
190
  default: {
191
- const { configFileExists } = await import("./config-YU7NYZYG.js");
191
+ const { configFileExists } = await import("./config-VBBMTXJZ.js");
192
192
  const configExists = await configFileExists(mode);
193
193
  if (!configExists) {
194
194
  await runNetworkSetup(mode);
195
195
  }
196
- const { runServe } = await import("./serve-EUEQ6ERI.js");
196
+ const { runServe } = await import("./serve-J6EM3G6N.js");
197
197
  const serveOptions = { mode };
198
198
  if (values.port) serveOptions.port = parseInt(values.port);
199
199
  if (values["no-browser"] === true) serveOptions.openBrowser = false;
@@ -205,4 +205,4 @@ async function runCli(options) {
205
205
  export {
206
206
  runCli
207
207
  };
208
- //# sourceMappingURL=chunk-I67T2E5K.js.map
208
+ //# sourceMappingURL=chunk-R3EK3ODJ.js.map
@@ -1,57 +1,78 @@
1
1
  import {
2
- BUILDER_KICKOFF_PROMPT,
3
2
  PathAccessDeniedError,
4
3
  TurnMetrics,
5
- VERIFIER_KICKOFF_PROMPT,
4
+ WORKFLOW_KICKOFF_PROMPT,
6
5
  assembleAgentRequest,
7
6
  buildAgentReminder,
7
+ createAssemblyResult,
8
8
  createChatDoneEvent,
9
9
  createMessageStartEvent,
10
- executeSubAgent,
11
10
  findAgentById,
11
+ getAllInstructions,
12
+ getConversationMessages,
13
+ getEnabledSkillMetadata,
12
14
  getSubAgents,
13
15
  getToolRegistryForAgent,
14
16
  loadAllAgentsDefault,
17
+ processEventsForConversation,
15
18
  runTopLevelAgentLoop
16
- } from "./chunk-DFWGWULB.js";
19
+ } from "./chunk-FRCLNAE2.js";
17
20
  import {
18
21
  getCurrentContextWindowId,
19
- getEventStore
20
- } from "./chunk-UH27FDT7.js";
22
+ getCurrentWindowMessageOptions,
23
+ getEventStore,
24
+ getRuntimeConfig
25
+ } from "./chunk-NPHYEUYE.js";
21
26
  import {
22
27
  buildSnapshotFromSessionState
23
- } from "./chunk-XL7SW5B4.js";
28
+ } from "./chunk-XJEOP6XU.js";
24
29
  import {
25
30
  logger
26
31
  } from "./chunk-K44MW7JJ.js";
32
+ import {
33
+ getGlobalConfigDir
34
+ } from "./chunk-CQGTEGKL.js";
27
35
 
28
- // src/server/sub-agents/verifier-helpers.ts
29
- var MAX_CONSECUTIVE_VERIFIER_NUDGES = 10;
30
- var VERIFIER_STALL_REASON = "Verifier stopped repeatedly before terminalizing verification after repeated nudges.";
31
- function getCriteriaAwaitingVerification(criteria) {
32
- return criteria.filter((criterion) => criterion.status.type === "completed");
33
- }
34
- function buildVerifierNudgeContent(criteria) {
35
- const ids = criteria.map((criterion) => criterion.id).join(", ");
36
- return `You stopped before finalizing verification. ${criteria.length} criteria still need a terminal verification result. Use criterion with action "pass" or "fail" for each remaining criterion: ${ids}.`;
37
- }
38
- function buildVerifierRestartContent(criteria) {
39
- const ids = criteria.map((criterion) => criterion.id).join(", ");
40
- return `${VERIFIER_STALL_REASON} Leaving remaining criteria unchanged so verification can restart in a fresh window: ${ids}.`;
41
- }
42
- function createVerifierNudgeConfig() {
43
- return {
44
- maxConsecutiveNudges: MAX_CONSECUTIVE_VERIFIER_NUDGES,
45
- getCriteriaAwaiting: getCriteriaAwaitingVerification,
46
- buildNudgeContent: buildVerifierNudgeContent,
47
- buildRestartContent: buildVerifierRestartContent
48
- };
36
+ // src/server/chat/dynamic-context.ts
37
+ import { createHash } from "crypto";
38
+ function computeDynamicContextHash(instructionContent, skills) {
39
+ const dynamicInputs = JSON.stringify({
40
+ instructions: instructionContent,
41
+ skills: skills.map((s) => s.id).sort()
42
+ });
43
+ return createHash("sha256").update(dynamicInputs).digest("hex");
49
44
  }
50
45
 
51
46
  // src/server/chat/orchestrator.ts
52
- function getCurrentWindowMessageOptions(sessionId) {
53
- const contextWindowId = getCurrentContextWindowId(sessionId);
54
- return contextWindowId ? { contextWindowId } : void 0;
47
+ async function buildRetryPatterns() {
48
+ const { getSetting, SETTINGS_KEYS } = await import("./settings-6XX56F3F.js");
49
+ const raw = getSetting(SETTINGS_KEYS.RETRY_PATTERNS);
50
+ if (!raw) {
51
+ const oldXmlProtection = getSetting("llm.disableXmlProtection");
52
+ if (oldXmlProtection !== null) {
53
+ const disabled = oldXmlProtection === "true";
54
+ return {
55
+ retryPatterns: disabled ? [] : [{ field: "both", pattern: "<(tool_call|function=|/tool_call|parameter=)", action: "retry", active: true }],
56
+ maxRetriesPerTurn: 10
57
+ };
58
+ }
59
+ return { retryPatterns: [], maxRetriesPerTurn: 10 };
60
+ }
61
+ try {
62
+ const parsed = JSON.parse(raw);
63
+ return {
64
+ retryPatterns: Array.isArray(parsed.patterns) ? parsed.patterns : [],
65
+ maxRetriesPerTurn: typeof parsed.maxRetriesPerTurn === "number" ? parsed.maxRetriesPerTurn : 10
66
+ };
67
+ } catch {
68
+ return { retryPatterns: [], maxRetriesPerTurn: 10 };
69
+ }
70
+ }
71
+ function buildGetConversationMessages(sessionId, llmClient, append) {
72
+ return async () => {
73
+ const processedEvents = await processEventsForConversation(sessionId, llmClient, (event) => append(event));
74
+ return getConversationMessages({ type: "toplevel", sessionId }, { events: processedEvents });
75
+ };
55
76
  }
56
77
  function resolveStatsIdentity(options) {
57
78
  const model = options.llmClient.getModel();
@@ -75,13 +96,11 @@ async function runChatTurn(options) {
75
96
  const session = sessionManager.requireSession(sessionId);
76
97
  const mode = session.mode;
77
98
  logger.debug("Starting chat turn", { sessionId, mode });
99
+ sessionManager.setRunning(sessionId, true);
100
+ const append = (event) => eventStore.append(sessionId, event);
78
101
  const turnMetrics = new TurnMetrics();
79
102
  try {
80
- if (mode === "builder") {
81
- await runBuilderTurn(options, turnMetrics);
82
- } else {
83
- await runGenericAgentTurn(options, turnMetrics, mode);
84
- }
103
+ await runAgentTurn(options, turnMetrics, mode, append);
85
104
  const snapshot = buildSnapshot(sessionManager, sessionId, turnMetrics.buildStats(statsIdentity, mode));
86
105
  const snapshotEvent = eventStore.append(sessionId, { type: "turn.snapshot", data: snapshot });
87
106
  const deletedCount = eventStore.cleanupOldEvents(sessionId);
@@ -147,18 +166,30 @@ async function runChatTurn(options) {
147
166
  eventStore.append(sessionId, { type: "running.changed", data: { isRunning: false } });
148
167
  }
149
168
  }
169
+ function windowHasReminder(sessionId, windowId) {
170
+ const eventStore = getEventStore();
171
+ const events = eventStore.getEvents(sessionId);
172
+ return events.some((event) => {
173
+ if (event.type !== "message.start") return false;
174
+ const data = event.data;
175
+ return data.contextWindowId === windowId && data.messageKind === "auto-prompt" && typeof data.content === "string" && data.content.includes("<system-reminder>");
176
+ });
177
+ }
150
178
  function injectModeReminderIfNeeded(sessionManager, sessionId, agentId, allAgents, _onMessage) {
151
179
  const eventStore = getEventStore();
152
180
  const session = sessionManager.requireSession(sessionId);
181
+ const currentWindowId = getCurrentContextWindowId(sessionId);
153
182
  const lastModeReminder = session.executionState?.lastModeWithReminder;
154
183
  if (lastModeReminder === agentId) {
155
- return;
184
+ if (!currentWindowId || windowHasReminder(sessionId, currentWindowId)) {
185
+ return;
186
+ }
156
187
  }
157
188
  const agentDef = findAgentById(agentId, allAgents);
158
189
  if (!agentDef) return;
159
190
  const reminderContent = buildAgentReminder(agentDef);
160
191
  const reminderMsgId = crypto.randomUUID();
161
- const currentWindowMessageOptions = getCurrentContextWindowId(sessionId) ? { contextWindowId: getCurrentContextWindowId(sessionId) } : void 0;
192
+ const currentWindowMessageOptions = currentWindowId ? { contextWindowId: currentWindowId } : void 0;
162
193
  eventStore.append(sessionId, {
163
194
  type: "message.start",
164
195
  data: {
@@ -183,130 +214,88 @@ function injectModeReminderIfNeeded(sessionManager, sessionId, agentId, allAgent
183
214
  lastModeWithReminder: agentId
184
215
  });
185
216
  }
186
- async function runGenericAgentTurn(options, turnMetrics, agentId) {
217
+ async function runAgentTurn(options, turnMetrics, agentId, append, callbacks) {
187
218
  const statsIdentity = resolveStatsIdentity(options);
188
219
  const allAgents = await loadAllAgentsDefault();
189
220
  injectModeReminderIfNeeded(options.sessionManager, options.sessionId, agentId, allAgents, options.onMessage);
190
221
  const agentDef = findAgentById(agentId, allAgents) ?? findAgentById("planner", allAgents);
191
222
  const subAgentDefs = getSubAgents(allAgents);
192
- await runTopLevelAgentLoop(
223
+ const { content: instructionContent } = await getAllInstructions(
224
+ options.sessionManager.requireSession(options.sessionId).workdir,
225
+ options.sessionManager.requireSession(options.sessionId).projectId
226
+ );
227
+ const runtimeConfig = getRuntimeConfig();
228
+ const configDir = getGlobalConfigDir(runtimeConfig.mode ?? "production");
229
+ const skills = await getEnabledSkillMetadata(configDir, runtimeConfig.workdir);
230
+ return runTopLevelAgentLoop(
193
231
  {
194
232
  mode: agentId,
233
+ append,
234
+ ...await buildRetryPatterns(),
195
235
  sessionManager: options.sessionManager,
196
236
  sessionId: options.sessionId,
197
237
  llmClient: options.llmClient,
198
238
  statsIdentity,
199
239
  signal: options.signal,
200
240
  onMessage: options.onMessage,
201
- assembleRequest: (input) => assembleAgentRequest({ ...input, agentDef, subAgentDefs, modelName: options.llmClient.getModel() }),
202
- getToolRegistry: () => getToolRegistryForAgent(agentDef)
203
- },
204
- turnMetrics
205
- );
206
- }
207
- function filterToolRegistryForStepDone(baseRegistry, _injectStepDone) {
208
- return baseRegistry;
209
- }
210
- async function runBuilderTurn(options, turnMetrics) {
211
- const { sessionManager, sessionId } = options;
212
- const statsIdentity = resolveStatsIdentity(options);
213
- const eventStore = getEventStore();
214
- const allAgents = await loadAllAgentsDefault();
215
- injectModeReminderIfNeeded(options.sessionManager, options.sessionId, "builder", allAgents, options.onMessage);
216
- const builderDef = findAgentById("builder", allAgents);
217
- const subAgentDefs = getSubAgents(allAgents);
218
- let stepDoneCalled = false;
219
- return {
220
- ...await runTopLevelAgentLoop(
221
- {
222
- mode: "builder",
223
- sessionManager,
224
- sessionId,
225
- llmClient: options.llmClient,
226
- statsIdentity,
227
- signal: options.signal,
228
- onMessage: options.onMessage,
229
- assembleRequest: (input) => assembleAgentRequest({
230
- ...input,
231
- agentDef: builderDef,
232
- subAgentDefs,
233
- modelName: options.llmClient.getModel()
234
- }),
235
- getToolRegistry: () => {
236
- const baseRegistry = getToolRegistryForAgent(builderDef);
237
- return filterToolRegistryForStepDone(baseRegistry, options.injectStepDone === true);
238
- },
239
- onToolExecuted: (toolCall, toolResult) => {
240
- if (toolCall.name === "step_done" && toolResult.success) {
241
- stepDoneCalled = true;
242
- }
243
- if (toolResult.success && ["write_file", "edit_file"].includes(toolCall.name)) {
244
- const path = toolCall.arguments["path"];
245
- sessionManager.addModifiedFile(sessionId, path);
241
+ assembleRequest: (input) => {
242
+ const cached = options.sessionManager.getCachedPrompt(options.sessionId);
243
+ if (cached) {
244
+ const currentHash = computeDynamicContextHash(instructionContent ?? "", skills);
245
+ if (cached.hash !== currentHash) {
246
+ options.sessionManager.setDynamicContextChanged(options.sessionId, true);
246
247
  }
247
- },
248
- injectKickoff: () => {
249
- if (options.injectBuilderKickoff !== true) return;
250
- const session = sessionManager.requireSession(sessionId);
251
- const currentWindowMessageOptions = getCurrentContextWindowId(sessionId) ? { contextWindowId: getCurrentContextWindowId(sessionId) } : void 0;
252
- const events = eventStore.getEvents(sessionId);
253
- const hasBuilderKickoff = events.some((e) => {
254
- if (e.type !== "message.start") return false;
255
- const data = e.data;
256
- return data.messageKind === "auto-prompt" && data.content?.includes("fulfil the");
248
+ return createAssemblyResult({
249
+ systemPrompt: cached.systemPrompt,
250
+ messages: input.messages,
251
+ injectedFiles: input.injectedFiles,
252
+ requestTools: input.promptTools,
253
+ toolChoice: input.toolChoice,
254
+ disableThinking: false
257
255
  });
258
- if (!hasBuilderKickoff) {
259
- const kickoffMsgId = crypto.randomUUID();
260
- const kickoffContent = BUILDER_KICKOFF_PROMPT(session.criteria.length);
261
- const msgMetadata = { type: "workflow", name: "Workflow", color: "#f59e0b" };
262
- eventStore.append(
263
- sessionId,
264
- createMessageStartEvent(kickoffMsgId, "user", kickoffContent, {
265
- ...currentWindowMessageOptions ?? {},
266
- isSystemGenerated: true,
267
- messageKind: "auto-prompt",
268
- metadata: msgMetadata
269
- })
270
- );
271
- eventStore.append(sessionId, { type: "message.done", data: { messageId: kickoffMsgId } });
272
- }
273
256
  }
257
+ const result = assembleAgentRequest({
258
+ ...input,
259
+ agentDef,
260
+ subAgentDefs,
261
+ modelName: options.llmClient.getModel()
262
+ });
263
+ const hash = computeDynamicContextHash(instructionContent ?? "", skills);
264
+ options.sessionManager.setCachedPrompt(options.sessionId, result.systemPrompt, hash);
265
+ return result;
274
266
  },
275
- turnMetrics
276
- ),
277
- stepDoneCalled
278
- };
267
+ getToolRegistry: () => getToolRegistryForAgent(agentDef),
268
+ getConversationMessages: buildGetConversationMessages(options.sessionId, options.llmClient, append),
269
+ injectModeReminder: () => injectModeReminderIfNeeded(options.sessionManager, options.sessionId, agentId, allAgents, options.onMessage),
270
+ ...callbacks?.injectKickoff ? { injectKickoff: callbacks.injectKickoff } : {},
271
+ ...callbacks?.onToolExecuted ? { onToolExecuted: callbacks.onToolExecuted } : {}
272
+ },
273
+ turnMetrics
274
+ );
279
275
  }
280
- async function runVerifierTurn(options, turnMetrics) {
281
- const { sessionManager, sessionId, llmClient, signal, onMessage } = options;
282
- const statsIdentity = resolveStatsIdentity(options);
276
+ function injectWorkflowKickoffIfNeeded(sessionManager, sessionId, eventStore) {
283
277
  const session = sessionManager.requireSession(sessionId);
284
- const toVerify = session.criteria.filter((c) => c.status.type === "completed");
285
- if (toVerify.length === 0) {
286
- logger.debug("Nothing to verify", { sessionId });
287
- return { allPassed: true, failed: [] };
288
- }
289
- const allAgents = await loadAllAgentsDefault();
290
- const verifierDef = findAgentById("verifier", allAgents);
291
- const toolRegistry = getToolRegistryForAgent(verifierDef);
292
- const result = await executeSubAgent({
293
- subAgentType: "verifier",
294
- prompt: VERIFIER_KICKOFF_PROMPT,
295
- sessionManager,
296
- sessionId,
297
- llmClient,
298
- toolRegistry,
299
- turnMetrics,
300
- statsIdentity,
301
- signal,
302
- onMessage,
303
- nudgeConfig: createVerifierNudgeConfig()
278
+ const currentWindowMessageOptions = getCurrentContextWindowId(sessionId) ? { contextWindowId: getCurrentContextWindowId(sessionId) } : void 0;
279
+ const events = eventStore.getEvents(sessionId);
280
+ const hasKickoff = events.some((e) => {
281
+ if (e.type !== "message.start") return false;
282
+ const data = e.data;
283
+ return data.messageKind === "auto-prompt" && data.content?.includes("fulfil the");
304
284
  });
305
- return {
306
- allPassed: result.allPassed ?? true,
307
- failed: result.failed ?? [],
308
- content: result.content
309
- };
285
+ if (!hasKickoff) {
286
+ const kickoffMsgId = crypto.randomUUID();
287
+ const kickoffContent = WORKFLOW_KICKOFF_PROMPT(session.criteria.length);
288
+ eventStore.append(
289
+ sessionId,
290
+ createMessageStartEvent(kickoffMsgId, "user", kickoffContent, {
291
+ ...currentWindowMessageOptions ?? {},
292
+ isSystemGenerated: true,
293
+ messageKind: "auto-prompt",
294
+ metadata: { type: "workflow", name: "Workflow", color: "#f59e0b" }
295
+ })
296
+ );
297
+ eventStore.append(sessionId, { type: "message.done", data: { messageId: kickoffMsgId } });
298
+ }
310
299
  }
311
300
  function buildSnapshot(sessionManager, sessionId, _lastStats) {
312
301
  const eventStore = getEventStore();
@@ -323,10 +312,9 @@ function buildSnapshot(sessionManager, sessionId, _lastStats) {
323
312
  }
324
313
 
325
314
  export {
326
- createVerifierNudgeConfig,
315
+ computeDynamicContextHash,
327
316
  runChatTurn,
328
- filterToolRegistryForStepDone,
329
- runBuilderTurn,
330
- runVerifierTurn
317
+ runAgentTurn,
318
+ injectWorkflowKickoffIfNeeded
331
319
  };
332
- //# sourceMappingURL=chunk-X36JIWSF.js.map
320
+ //# sourceMappingURL=chunk-RBJ25FIR.js.map