openfox 0.2.3 → 1.0.0

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 (64) hide show
  1. package/dist/agent-defaults/builder.agent.md +3 -7
  2. package/dist/agent-defaults/code-reviewer.agent.md +1 -2
  3. package/dist/agent-defaults/debugger.agent.md +1 -2
  4. package/dist/agent-defaults/planner.agent.md +2 -9
  5. package/dist/agent-defaults/test-generator.agent.md +1 -1
  6. package/dist/agent-defaults/verifier.agent.md +4 -5
  7. package/dist/ask-3RK5YJZE.js +19 -0
  8. package/dist/auto-compaction-7LNZ5J3W.js +23 -0
  9. package/dist/{branch.api-WDHKZJS6.js → branch.api-XADFZZAA.js} +2 -2
  10. package/dist/chat-handler-T7SLWZ4E.js +201 -0
  11. package/dist/chunk-22CTURMH.js +128 -0
  12. package/dist/chunk-2KSCZSXQ.js +680 -0
  13. package/dist/{chunk-YMUIRZUI.js → chunk-2TBNG7CS.js} +12 -12
  14. package/dist/{chunk-4Z2OZ6NX.js → chunk-3EHGGBWE.js} +29 -2
  15. package/dist/{chunk-L475ND2Z.js → chunk-3UQACISJ.js} +162 -52
  16. package/dist/{chunk-AF7XS4OI.js → chunk-5YQAXUKO.js} +4 -1
  17. package/dist/{chunk-MTX5Y3V3.js → chunk-7IOZFJBW.js} +2 -2
  18. package/dist/chunk-BU2ZYOEA.js +1067 -0
  19. package/dist/chunk-M2C5GEQR.js +137 -0
  20. package/dist/{chunk-U62ZECSQ.js → chunk-PNBH3RAX.js} +2 -2
  21. package/dist/chunk-QDEKU5RL.js +75 -0
  22. package/dist/chunk-QYP5HQV4.js +438 -0
  23. package/dist/{chunk-3WOUSUW7.js → chunk-RAZQTQK2.js} +1353 -1134
  24. package/dist/chunk-S4QKTRGJ.js +343 -0
  25. package/dist/{chunk-ARFK3RZW.js → chunk-TPT6HP4H.js} +1 -1
  26. package/dist/chunk-TVQOONDR.js +75 -0
  27. package/dist/{chunk-5ZUU23XF.js → chunk-U6BUT37A.js} +286 -33
  28. package/dist/chunk-VZXXJKSQ.js +314 -0
  29. package/dist/{chunk-26NRL3HX.js → chunk-XFXOSPYH.js} +3 -2
  30. package/dist/{chunk-N2XYX4ZA.js → chunk-XKFPU2FA.js} +13 -2
  31. package/dist/{chunk-FUUYYDVG.js → chunk-YB2574C6.js} +2841 -4507
  32. package/dist/cli/dev.js +4 -3
  33. package/dist/cli/index.js +4 -3
  34. package/dist/{config-BMXSDOZB.js → config-GIFCEJDU.js} +11 -6
  35. package/dist/events-G6CKHDNZ.js +114 -0
  36. package/dist/folding-O52L65VN.js +37 -0
  37. package/dist/{init-PLRTRSDC.js → init-MKTUIGZU.js} +46 -13
  38. package/dist/orchestrator-UGXX7JRQ.js +39 -0
  39. package/dist/package.json +9 -2
  40. package/dist/{paths-4BPXWB2W.js → paths-MM76AWYB.js} +2 -2
  41. package/dist/processor-KLHKDILM.js +236 -0
  42. package/dist/{project-creator-CWPKRIKI.js → project-creator-KAQXMZCT.js} +9 -8
  43. package/dist/projects-GSMMEVSO.js +19 -0
  44. package/dist/{protocol-BFFz3WM0.d.ts → protocol-BZ6OIASP.d.ts} +71 -50
  45. package/dist/protocol-WQENDK72.js +88 -0
  46. package/dist/{provider-ETVY64A2.js → provider-4KTWU6IV.js} +41 -9
  47. package/dist/runtime-config-2XJJIMSC.js +9 -0
  48. package/dist/{serve-TL34LE77.js → serve-UK6B6G2O.js} +38 -18
  49. package/dist/server/index.d.ts +81 -27
  50. package/dist/server/index.js +19 -11
  51. package/dist/{settings-D5O3TMLD.js → settings-NCLN4JDZ.js} +4 -4
  52. package/dist/shared/index.d.ts +2 -2
  53. package/dist/shared/index.js +1 -1
  54. package/dist/tools-SGBQE736.js +48 -0
  55. package/dist/vision-fallback-63VPBSEN.js +22 -0
  56. package/dist/web/assets/{index-DHmZmhnR.css → index-BsaaC0X0.css} +32 -1
  57. package/dist/web/assets/index-CvO7QkGh.js +141 -0
  58. package/dist/web/index.html +2 -2
  59. package/dist/web/sounds/typing.mp3 +0 -0
  60. package/dist/workflow-defaults/default.workflow.json +1 -1
  61. package/package.json +9 -2
  62. package/dist/chunk-L4XDA3V7.js +0 -218
  63. package/dist/tools-QMIIDJTV.js +0 -48
  64. package/dist/web/assets/index-CAD7Iv_d.js +0 -122
package/dist/cli/dev.js CHANGED
@@ -1,13 +1,14 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  runCli
4
- } from "../chunk-YMUIRZUI.js";
4
+ } from "../chunk-2TBNG7CS.js";
5
5
  import {
6
6
  logger
7
- } from "../chunk-U62ZECSQ.js";
7
+ } from "../chunk-PNBH3RAX.js";
8
8
 
9
9
  // src/cli/dev.ts
10
- runCli({ mode: "development" }).catch((error) => {
10
+ var mode = process.env["OPENFOX_MODE"] ?? "development";
11
+ runCli({ mode }).catch((error) => {
11
12
  logger.error("CLI fatal error", { error: error instanceof Error ? error.message : String(error) });
12
13
  process.exit(1);
13
14
  });
package/dist/cli/index.js CHANGED
@@ -1,13 +1,14 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  runCli
4
- } from "../chunk-YMUIRZUI.js";
4
+ } from "../chunk-2TBNG7CS.js";
5
5
  import {
6
6
  logger
7
- } from "../chunk-U62ZECSQ.js";
7
+ } from "../chunk-PNBH3RAX.js";
8
8
 
9
9
  // src/cli/index.ts
10
- runCli({ mode: "production" }).catch((error) => {
10
+ var mode = process.env["OPENFOX_MODE"] ?? "production";
11
+ runCli({ mode }).catch((error) => {
11
12
  logger.error("CLI fatal error", { error: error instanceof Error ? error.message : String(error) });
12
13
  process.exit(1);
13
14
  });
@@ -4,6 +4,8 @@ import {
4
4
  configFileExists,
5
5
  getActiveProvider,
6
6
  getDefaultModel,
7
+ getDefaultVisionFallback,
8
+ getVisionFallback,
7
9
  loadGlobalConfig,
8
10
  mergeConfigs,
9
11
  migrateConfig,
@@ -11,17 +13,20 @@ import {
11
13
  saveGlobalConfig,
12
14
  setDefaultModelSelection,
13
15
  trySmartDefaults
14
- } from "./chunk-L475ND2Z.js";
15
- import "./chunk-5ZUU23XF.js";
16
- import "./chunk-26NRL3HX.js";
17
- import "./chunk-U62ZECSQ.js";
18
- import "./chunk-AF7XS4OI.js";
16
+ } from "./chunk-3UQACISJ.js";
17
+ import "./chunk-U6BUT37A.js";
18
+ import "./chunk-XFXOSPYH.js";
19
+ import "./chunk-5YQAXUKO.js";
20
+ import "./chunk-M2C5GEQR.js";
21
+ import "./chunk-PNBH3RAX.js";
19
22
  export {
20
23
  activateProvider,
21
24
  addProvider,
22
25
  configFileExists,
23
26
  getActiveProvider,
24
27
  getDefaultModel,
28
+ getDefaultVisionFallback,
29
+ getVisionFallback,
25
30
  loadGlobalConfig,
26
31
  mergeConfigs,
27
32
  migrateConfig,
@@ -30,4 +35,4 @@ export {
30
35
  setDefaultModelSelection,
31
36
  trySmartDefaults
32
37
  };
33
- //# sourceMappingURL=config-BMXSDOZB.js.map
38
+ //# sourceMappingURL=config-GIFCEJDU.js.map
@@ -0,0 +1,114 @@
1
+ import {
2
+ EventStore,
3
+ compactContext,
4
+ createEvent,
5
+ emitAssistantMessageStart,
6
+ emitChatDone,
7
+ emitChatError,
8
+ emitContextCompacted,
9
+ emitContextState,
10
+ emitCriteriaSet,
11
+ emitCriterionUpdated,
12
+ emitFileRead,
13
+ emitFormatRetry,
14
+ emitMessageDelta,
15
+ emitMessageDone,
16
+ emitMessageThinking,
17
+ emitModeChanged,
18
+ emitPhaseChanged,
19
+ emitRunningChanged,
20
+ emitSessionInitialized,
21
+ emitTodosUpdated,
22
+ emitToolCall,
23
+ emitToolOutput,
24
+ emitToolPreparing,
25
+ emitToolResult,
26
+ emitTurnSnapshot,
27
+ emitUserMessage,
28
+ getContextMessages,
29
+ getCurrentContextWindowId,
30
+ getCurrentWindowMessages,
31
+ getEventStore,
32
+ getReadFilesCache,
33
+ getRecentUserPromptsForSession,
34
+ getSessionState,
35
+ initEventStore,
36
+ isFileInCache,
37
+ isStoredEvent,
38
+ isTurnEvent
39
+ } from "./chunk-BU2ZYOEA.js";
40
+ import {
41
+ buildContextMessagesFromEventHistory,
42
+ buildContextMessagesFromMessages,
43
+ buildContextMessagesFromStoredEvents,
44
+ buildMessagesFromStoredEvents,
45
+ buildSnapshot,
46
+ buildSnapshotFromSessionState,
47
+ foldContextState,
48
+ foldCriteria,
49
+ foldIsRunning,
50
+ foldMode,
51
+ foldPhase,
52
+ foldSessionState,
53
+ foldTodos,
54
+ foldTurnEventsToSnapshotMessages,
55
+ getMessagesForWindow
56
+ } from "./chunk-2KSCZSXQ.js";
57
+ import "./chunk-3EHGGBWE.js";
58
+ import "./chunk-TVQOONDR.js";
59
+ import "./chunk-PNBH3RAX.js";
60
+ export {
61
+ EventStore,
62
+ buildContextMessagesFromEventHistory,
63
+ buildContextMessagesFromMessages,
64
+ buildContextMessagesFromStoredEvents,
65
+ buildMessagesFromStoredEvents,
66
+ buildSnapshot,
67
+ buildSnapshotFromSessionState,
68
+ compactContext,
69
+ createEvent,
70
+ emitAssistantMessageStart,
71
+ emitChatDone,
72
+ emitChatError,
73
+ emitContextCompacted,
74
+ emitContextState,
75
+ emitCriteriaSet,
76
+ emitCriterionUpdated,
77
+ emitFileRead,
78
+ emitFormatRetry,
79
+ emitMessageDelta,
80
+ emitMessageDone,
81
+ emitMessageThinking,
82
+ emitModeChanged,
83
+ emitPhaseChanged,
84
+ emitRunningChanged,
85
+ emitSessionInitialized,
86
+ emitTodosUpdated,
87
+ emitToolCall,
88
+ emitToolOutput,
89
+ emitToolPreparing,
90
+ emitToolResult,
91
+ emitTurnSnapshot,
92
+ emitUserMessage,
93
+ foldContextState,
94
+ foldCriteria,
95
+ foldIsRunning,
96
+ foldMode,
97
+ foldPhase,
98
+ foldSessionState,
99
+ foldTodos,
100
+ foldTurnEventsToSnapshotMessages,
101
+ getContextMessages,
102
+ getCurrentContextWindowId,
103
+ getCurrentWindowMessages,
104
+ getEventStore,
105
+ getMessagesForWindow,
106
+ getReadFilesCache,
107
+ getRecentUserPromptsForSession,
108
+ getSessionState,
109
+ initEventStore,
110
+ isFileInCache,
111
+ isStoredEvent,
112
+ isTurnEvent
113
+ };
114
+ //# sourceMappingURL=events-G6CKHDNZ.js.map
@@ -0,0 +1,37 @@
1
+ import {
2
+ buildContextMessagesFromEventHistory,
3
+ buildContextMessagesFromMessages,
4
+ buildContextMessagesFromStoredEvents,
5
+ buildMessagesFromStoredEvents,
6
+ buildSnapshot,
7
+ buildSnapshotFromSessionState,
8
+ foldContextState,
9
+ foldCriteria,
10
+ foldIsRunning,
11
+ foldMode,
12
+ foldPendingConfirmations,
13
+ foldPhase,
14
+ foldSessionState,
15
+ foldTodos,
16
+ foldTurnEventsToSnapshotMessages,
17
+ getMessagesForWindow
18
+ } from "./chunk-2KSCZSXQ.js";
19
+ export {
20
+ buildContextMessagesFromEventHistory,
21
+ buildContextMessagesFromMessages,
22
+ buildContextMessagesFromStoredEvents,
23
+ buildMessagesFromStoredEvents,
24
+ buildSnapshot,
25
+ buildSnapshotFromSessionState,
26
+ foldContextState,
27
+ foldCriteria,
28
+ foldIsRunning,
29
+ foldMode,
30
+ foldPendingConfirmations,
31
+ foldPhase,
32
+ foldSessionState,
33
+ foldTodos,
34
+ foldTurnEventsToSnapshotMessages,
35
+ getMessagesForWindow
36
+ };
37
+ //# sourceMappingURL=folding-O52L65VN.js.map
@@ -1,14 +1,15 @@
1
1
  import {
2
2
  addProvider,
3
3
  saveGlobalConfig
4
- } from "./chunk-L475ND2Z.js";
4
+ } from "./chunk-3UQACISJ.js";
5
5
  import {
6
6
  detectBackend,
7
7
  detectModel
8
- } from "./chunk-5ZUU23XF.js";
9
- import "./chunk-26NRL3HX.js";
10
- import "./chunk-U62ZECSQ.js";
11
- import "./chunk-AF7XS4OI.js";
8
+ } from "./chunk-U6BUT37A.js";
9
+ import "./chunk-XFXOSPYH.js";
10
+ import "./chunk-5YQAXUKO.js";
11
+ import "./chunk-M2C5GEQR.js";
12
+ import "./chunk-PNBH3RAX.js";
12
13
 
13
14
  // src/cli/init.ts
14
15
  import { select, text, spinner, outro, confirm } from "@clack/prompts";
@@ -21,11 +22,14 @@ var LLM_OPTIONS = [
21
22
  function createBaseConfig() {
22
23
  return {
23
24
  providers: [],
25
+ defaultModelSelection: void 0,
24
26
  activeProviderId: void 0,
27
+ activeWorkflowId: void 0,
25
28
  server: { port: 10369, host: "127.0.0.1", openBrowser: true },
26
29
  logging: { level: "info" },
27
30
  database: { path: "" },
28
- workspace: { workdir: process.cwd() }
31
+ workspace: { workdir: process.cwd() },
32
+ visionFallback: { enabled: false, url: "http://localhost:11434", model: "qwen3-vl:2b", timeout: 120 }
29
33
  };
30
34
  }
31
35
  async function promptNetworkAccessibility() {
@@ -95,7 +99,7 @@ async function runInitWithSelect(mode, existingConfig) {
95
99
  name: "Default",
96
100
  url: found.url,
97
101
  backend: found.backend,
98
- maxContext: 2e5,
102
+ models: [],
99
103
  isActive: true
100
104
  });
101
105
  } else {
@@ -129,10 +133,10 @@ async function runInitWithSelect(mode, existingConfig) {
129
133
  name: "Default",
130
134
  url,
131
135
  backend,
132
- maxContext: 2e5,
136
+ models: [],
133
137
  isActive: true
134
138
  });
135
- const { setDefaultModelSelection } = await import("./config-BMXSDOZB.js");
139
+ const { setDefaultModelSelection } = await import("./config-GIFCEJDU.js");
136
140
  config = setDefaultModelSelection(config, config.providers[config.providers.length - 1].id, model ?? "auto");
137
141
  } catch {
138
142
  s2.stop("Server isn't available");
@@ -153,10 +157,10 @@ async function runInitWithSelect(mode, existingConfig) {
153
157
  name: "Default",
154
158
  url,
155
159
  backend: "auto",
156
- maxContext: 2e5,
160
+ models: [],
157
161
  isActive: true
158
162
  });
159
- const { setDefaultModelSelection } = await import("./config-BMXSDOZB.js");
163
+ const { setDefaultModelSelection } = await import("./config-GIFCEJDU.js");
160
164
  config = setDefaultModelSelection(config, config.providers[config.providers.length - 1].id, "auto");
161
165
  }
162
166
  }
@@ -172,8 +176,37 @@ async function runInitWithSelect(mode, existingConfig) {
172
176
  }
173
177
  });
174
178
  config.workspace = { workdir: String(workdirChoice).replace(/\/$/, "") };
179
+ const visionChoice = await confirm({
180
+ message: "Configure vision fallback for non-vision models?",
181
+ initialValue: false
182
+ });
183
+ if (visionChoice === true) {
184
+ const visionUrl = await text({
185
+ message: "Vision fallback server URL",
186
+ placeholder: "http://localhost:11434",
187
+ initialValue: config.visionFallback?.url || "http://localhost:11434",
188
+ validate: (value) => {
189
+ if (!value || value.length === 0) return "URL is required";
190
+ if (!value.startsWith("http")) return "Must start with http://";
191
+ }
192
+ });
193
+ const visionModel = await text({
194
+ message: "Vision fallback model name",
195
+ placeholder: "qwen3-vl:2b",
196
+ initialValue: config.visionFallback?.model || "qwen3-vl:2b",
197
+ validate: (value) => {
198
+ if (!value || value.length === 0) return "Model name is required";
199
+ }
200
+ });
201
+ config.visionFallback = {
202
+ enabled: true,
203
+ url: String(visionUrl),
204
+ model: String(visionModel),
205
+ timeout: 120
206
+ };
207
+ }
175
208
  if (config.providers.length > 0 && !config.defaultModelSelection) {
176
- const { setDefaultModelSelection } = await import("./config-BMXSDOZB.js");
209
+ const { setDefaultModelSelection } = await import("./config-GIFCEJDU.js");
177
210
  config = setDefaultModelSelection(config, config.providers[0].id, "auto");
178
211
  }
179
212
  await saveGlobalConfig(mode, config);
@@ -182,4 +215,4 @@ async function runInitWithSelect(mode, existingConfig) {
182
215
  export {
183
216
  runInitWithSelect
184
217
  };
185
- //# sourceMappingURL=init-PLRTRSDC.js.map
218
+ //# sourceMappingURL=init-MKTUIGZU.js.map
@@ -0,0 +1,39 @@
1
+ import {
2
+ filterToolRegistryForStepDone,
3
+ runBuilderTurn,
4
+ runChatTurn,
5
+ runVerifierTurn
6
+ } from "./chunk-VZXXJKSQ.js";
7
+ import {
8
+ TurnMetrics,
9
+ createChatDoneEvent,
10
+ createMessageDoneEvent,
11
+ createMessageStartEvent,
12
+ createToolCallEvent,
13
+ createToolResultEvent
14
+ } from "./chunk-YB2574C6.js";
15
+ import "./chunk-BU2ZYOEA.js";
16
+ import "./chunk-2KSCZSXQ.js";
17
+ import "./chunk-S4QKTRGJ.js";
18
+ import "./chunk-22CTURMH.js";
19
+ import "./chunk-7IOZFJBW.js";
20
+ import "./chunk-XKFPU2FA.js";
21
+ import "./chunk-3EHGGBWE.js";
22
+ import "./chunk-TPT6HP4H.js";
23
+ import "./chunk-TVQOONDR.js";
24
+ import "./chunk-XFXOSPYH.js";
25
+ import "./chunk-5YQAXUKO.js";
26
+ import "./chunk-PNBH3RAX.js";
27
+ export {
28
+ TurnMetrics,
29
+ createChatDoneEvent,
30
+ createMessageDoneEvent,
31
+ createMessageStartEvent,
32
+ createToolCallEvent,
33
+ createToolResultEvent,
34
+ filterToolRegistryForStepDone,
35
+ runBuilderTurn,
36
+ runChatTurn,
37
+ runVerifierTurn
38
+ };
39
+ //# sourceMappingURL=orchestrator-UGXX7JRQ.js.map
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openfox",
3
- "version": "0.2.3",
3
+ "version": "1.0.0",
4
4
  "description": "Local-LLM-first agentic coding assistant",
5
5
  "type": "module",
6
6
  "bin": {
@@ -34,6 +34,9 @@
34
34
  "test": "vitest run --reporter=dot --silent=passed-only && vitest run --reporter=dot --silent=passed-only --config e2e/vitest.config.ts",
35
35
  "test:unit": "vitest run --reporter=dot --silent=passed-only",
36
36
  "test:e2e": "vitest run --reporter=dot --silent=passed-only --config e2e/vitest.config.ts",
37
+ "test:playwright": "playwright test --config=e2e-playwright/playwright.config.ts",
38
+ "test:playwright:ui": "playwright test --config=e2e-playwright/playwright.config.ts --ui",
39
+ "test:playwright:headed": "playwright test --config=e2e-playwright/playwright.config.ts --headed",
37
40
  "test:cov": "vitest run --reporter=dot --silent=passed-only --coverage && vitest run --reporter=dot --silent=passed-only --config e2e/vitest.config.ts --coverage",
38
41
  "test:cov:unit": "vitest run --reporter=dot --silent=passed-only --coverage",
39
42
  "test:cov:e2e": "vitest run --reporter=dot --silent=passed-only --config e2e/vitest.config.ts --coverage",
@@ -59,12 +62,12 @@
59
62
  "gray-matter": "^4.0.3",
60
63
  "html2canvas": "^1.4.1",
61
64
  "ignore": "^7.0.5",
65
+ "node-pty": "^1.1.0",
62
66
  "open": "^11.0.0",
63
67
  "openai": "^6.33.0",
64
68
  "pyright": "^1.1.408",
65
69
  "react-markdown": "^10.1.0",
66
70
  "react-syntax-highlighter": "^16.1.1",
67
- "react-virtuoso": "4.18.3",
68
71
  "remark-gfm": "^4.0.1",
69
72
  "sql-language-server": "^1.7.1",
70
73
  "turndown": "^7.2.2",
@@ -82,6 +85,10 @@
82
85
  },
83
86
  "devDependencies": {
84
87
  "@eslint/js": "^10.0.1",
88
+ "@playwright/test": "^1.58.2",
89
+ "@testing-library/jest-dom": "^6.9.1",
90
+ "@testing-library/react": "^16.3.2",
91
+ "@testing-library/user-event": "^14.6.1",
85
92
  "@types/better-sqlite3": "^7.6.13",
86
93
  "@types/node": "^22.19.15",
87
94
  "@types/react": "^19.0.8",
@@ -5,7 +5,7 @@ import {
5
5
  getGlobalConfigPath,
6
6
  getGlobalDataDir,
7
7
  getSkillsDir
8
- } from "./chunk-AF7XS4OI.js";
8
+ } from "./chunk-5YQAXUKO.js";
9
9
  export {
10
10
  ensureDataDirExists,
11
11
  getDatabasePath,
@@ -14,4 +14,4 @@ export {
14
14
  getGlobalDataDir,
15
15
  getSkillsDir
16
16
  };
17
- //# sourceMappingURL=paths-4BPXWB2W.js.map
17
+ //# sourceMappingURL=paths-MM76AWYB.js.map
@@ -0,0 +1,236 @@
1
+ import {
2
+ generateSessionName,
3
+ needsNameGeneration
4
+ } from "./chunk-QDEKU5RL.js";
5
+ import {
6
+ getEventStore,
7
+ updateSessionMetadata
8
+ } from "./chunk-BU2ZYOEA.js";
9
+ import {
10
+ buildMessagesFromStoredEvents,
11
+ foldPendingConfirmations
12
+ } from "./chunk-2KSCZSXQ.js";
13
+ import {
14
+ createChatMessageMessage,
15
+ createContextStateMessage,
16
+ createSessionRunningMessage,
17
+ createSessionStateMessage
18
+ } from "./chunk-S4QKTRGJ.js";
19
+ import "./chunk-3EHGGBWE.js";
20
+ import "./chunk-TPT6HP4H.js";
21
+ import "./chunk-TVQOONDR.js";
22
+ import {
23
+ logger
24
+ } from "./chunk-PNBH3RAX.js";
25
+
26
+ // src/server/queue/processor.ts
27
+ var QueueProcessor = class {
28
+ deps;
29
+ unsubscribe = null;
30
+ activeAgents = /* @__PURE__ */ new Map();
31
+ constructor(deps) {
32
+ this.deps = deps;
33
+ }
34
+ start() {
35
+ if (this.unsubscribe) {
36
+ logger.warn("QueueProcessor already started");
37
+ return;
38
+ }
39
+ this.unsubscribe = this.deps.sessionManager.subscribe((event) => {
40
+ if (event.type === "queue_added") {
41
+ this.handleQueueAdded(event.sessionId);
42
+ } else if (event.type === "running_changed" && !event.isRunning) {
43
+ this.handleTurnDone(event.sessionId);
44
+ }
45
+ });
46
+ logger.debug("QueueProcessor started");
47
+ }
48
+ stop() {
49
+ if (this.unsubscribe) {
50
+ this.unsubscribe();
51
+ this.unsubscribe = null;
52
+ }
53
+ for (const controller of this.activeAgents.values()) {
54
+ controller.abort();
55
+ }
56
+ this.activeAgents.clear();
57
+ logger.debug("QueueProcessor stopped");
58
+ }
59
+ abortSession(sessionId) {
60
+ const controller = this.activeAgents.get(sessionId);
61
+ if (controller) {
62
+ controller.abort();
63
+ this.activeAgents.delete(sessionId);
64
+ return true;
65
+ }
66
+ return false;
67
+ }
68
+ handleQueueAdded(sessionId) {
69
+ const { sessionManager } = this.deps;
70
+ const session = sessionManager.getSession(sessionId);
71
+ if (!session) return;
72
+ if (session.isRunning) {
73
+ logger.debug("Session is running, not starting new turn", { sessionId });
74
+ return;
75
+ }
76
+ if (!sessionManager.hasQueuedMessages(sessionId)) {
77
+ logger.debug("No queued messages", { sessionId });
78
+ return;
79
+ }
80
+ this.startTurn(sessionId);
81
+ }
82
+ handleTurnDone(sessionId) {
83
+ logger.debug("Turn done, checking for more queued messages", { sessionId });
84
+ const { sessionManager } = this.deps;
85
+ if (!sessionManager.hasQueuedMessages(sessionId)) {
86
+ logger.debug("No more queued messages", { sessionId });
87
+ return;
88
+ }
89
+ this.startTurn(sessionId);
90
+ }
91
+ startTurn(sessionId) {
92
+ const { sessionManager, broadcastForSession } = this.deps;
93
+ logger.info("Starting turn from queue processor", { sessionId });
94
+ const session = sessionManager.getSession(sessionId);
95
+ if (!session || session.isRunning) {
96
+ logger.warn("Cannot start turn: session not found or already running", { sessionId });
97
+ return;
98
+ }
99
+ const queue = sessionManager.getQueueState(sessionId);
100
+ if (queue.length === 0) {
101
+ logger.warn("Cannot start turn: queue is empty", { sessionId });
102
+ return;
103
+ }
104
+ const controller = new AbortController();
105
+ this.activeAgents.set(sessionId, controller);
106
+ sessionManager.setRunning(sessionId, true);
107
+ broadcastForSession(sessionId, createSessionRunningMessage(true));
108
+ const nextAsap = queue.find((m) => m.mode === "asap") ?? queue[0];
109
+ if (nextAsap) {
110
+ sessionManager.cancelQueuedMessage(sessionId, nextAsap.queueId);
111
+ const userMessage = sessionManager.addMessage(sessionId, {
112
+ role: "user",
113
+ content: nextAsap.content,
114
+ ...nextAsap.attachments ? { attachments: nextAsap.attachments } : {}
115
+ });
116
+ broadcastForSession(sessionId, createChatMessageMessage(userMessage));
117
+ logger.debug("Added queued message to session", { sessionId, queueId: nextAsap.queueId, messageId: userMessage.id });
118
+ const messageCount = this.getSessionMessageCount(sessionId);
119
+ const currentSession = sessionManager.getSession(sessionId);
120
+ logger.debug("Session name generation check (queue)", {
121
+ sessionId,
122
+ title: currentSession?.metadata.title,
123
+ messageCount,
124
+ needsGeneration: currentSession ? needsNameGeneration(currentSession.metadata.title, messageCount) : false
125
+ });
126
+ if (currentSession && needsNameGeneration(currentSession.metadata.title, messageCount)) {
127
+ const eventStore = getEventStore();
128
+ generateSessionName({
129
+ userMessage: nextAsap.content,
130
+ llmClient: this.deps.getLLMClient(),
131
+ signal: controller.signal
132
+ }).then((result) => {
133
+ logger.debug("Session name generation result (queue)", {
134
+ sessionId,
135
+ success: result.success,
136
+ name: result.name,
137
+ error: result.error
138
+ });
139
+ if (result.success && result.name) {
140
+ updateSessionMetadata(sessionId, { title: result.name });
141
+ eventStore.append(sessionId, {
142
+ type: "session.name_generated",
143
+ data: { name: result.name }
144
+ });
145
+ const updatedSession = sessionManager.getSession(sessionId);
146
+ if (updatedSession) {
147
+ const events = eventStore.getEvents(sessionId);
148
+ const messages = buildMessagesFromStoredEvents(events);
149
+ const pendingConfirmations = foldPendingConfirmations(events);
150
+ broadcastForSession(sessionId, createSessionStateMessage(updatedSession, messages, pendingConfirmations));
151
+ }
152
+ }
153
+ }).catch((error) => {
154
+ logger.error("Session name generation failed (queue)", {
155
+ sessionId,
156
+ error: error instanceof Error ? error.message : String(error)
157
+ });
158
+ });
159
+ }
160
+ }
161
+ this.runTurn(sessionId, controller);
162
+ }
163
+ async runTurn(sessionId, controller) {
164
+ const { sessionManager, getLLMClient, getActiveProvider, broadcastForSession, providerManager } = this.deps;
165
+ const session = sessionManager.getSession(sessionId);
166
+ if (session?.providerId && session.providerModel) {
167
+ const currentActiveProviderId = providerManager.getActiveProviderId();
168
+ const currentModel = providerManager.getCurrentModel();
169
+ if (currentActiveProviderId !== session.providerId || currentModel !== session.providerModel) {
170
+ logger.info("Switching to session provider", {
171
+ sessionId,
172
+ fromProvider: currentActiveProviderId,
173
+ fromModel: currentModel,
174
+ toProvider: session.providerId,
175
+ toModel: session.providerModel
176
+ });
177
+ const result = await providerManager.activateProvider(session.providerId, { model: session.providerModel });
178
+ if (!result.success) {
179
+ logger.error("Failed to activate session provider", { sessionId, error: result.error });
180
+ }
181
+ }
182
+ }
183
+ const llmClient = getLLMClient();
184
+ const provider = getActiveProvider?.();
185
+ const statsIdentity = {
186
+ providerId: provider?.id ?? `provider:${llmClient.getModel()}`,
187
+ providerName: provider?.name ?? "Unknown Provider",
188
+ backend: provider?.backend ?? llmClient.getBackend(),
189
+ model: llmClient.getModel()
190
+ };
191
+ const { runChatTurn } = await import("./orchestrator-UGXX7JRQ.js");
192
+ runChatTurn({
193
+ sessionManager,
194
+ sessionId,
195
+ llmClient,
196
+ statsIdentity,
197
+ signal: controller.signal,
198
+ onMessage: (msg) => broadcastForSession(sessionId, msg)
199
+ }).catch((error) => {
200
+ if (error instanceof Error && error.message === "Aborted") {
201
+ return;
202
+ }
203
+ logger.error("QueueProcessor turn error", { sessionId, error });
204
+ }).finally(() => {
205
+ this.activeAgents.delete(sessionId);
206
+ const session2 = this.deps.sessionManager.getSession(sessionId);
207
+ if (!session2) return;
208
+ const hasMore = sessionManager.hasQueuedMessages(sessionId);
209
+ if (!hasMore) {
210
+ sessionManager.setRunning(sessionId, false);
211
+ const contextState = sessionManager.getContextState(sessionId);
212
+ broadcastForSession(sessionId, createContextStateMessage(contextState));
213
+ return;
214
+ }
215
+ this.startTurn(sessionId);
216
+ });
217
+ }
218
+ getSessionMessageCount(sessionId) {
219
+ const eventStore = getEventStore();
220
+ const events = eventStore.getEvents(sessionId);
221
+ let count = 0;
222
+ for (const event of events) {
223
+ if (event.type === "message.start") {
224
+ const data = event.data;
225
+ if (data.role === "user") {
226
+ count++;
227
+ }
228
+ }
229
+ }
230
+ return count;
231
+ }
232
+ };
233
+ export {
234
+ QueueProcessor
235
+ };
236
+ //# sourceMappingURL=processor-KLHKDILM.js.map