@empiricalrun/test-gen 0.57.2 → 0.59.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 (147) hide show
  1. package/CHANGELOG.md +69 -0
  2. package/dist/actions/index.d.ts.map +1 -1
  3. package/dist/actions/skill.js +2 -2
  4. package/dist/actions/utils/index.js +2 -3
  5. package/dist/agent/browsing/index.js +1 -2
  6. package/dist/agent/browsing/run.d.ts +3 -1
  7. package/dist/agent/browsing/run.d.ts.map +1 -1
  8. package/dist/agent/browsing/run.js +24 -27
  9. package/dist/agent/browsing/utils.d.ts +1 -14
  10. package/dist/agent/browsing/utils.d.ts.map +1 -1
  11. package/dist/agent/browsing/utils.js +10 -67
  12. package/dist/agent/chat/agent-loop.d.ts +6 -3
  13. package/dist/agent/chat/agent-loop.d.ts.map +1 -1
  14. package/dist/agent/chat/agent-loop.js +43 -27
  15. package/dist/agent/chat/exports.d.ts +7 -8
  16. package/dist/agent/chat/exports.d.ts.map +1 -1
  17. package/dist/agent/chat/exports.js +14 -34
  18. package/dist/agent/chat/index.d.ts +1 -1
  19. package/dist/agent/chat/index.d.ts.map +1 -1
  20. package/dist/agent/chat/index.js +20 -10
  21. package/dist/agent/chat/models.d.ts +6 -0
  22. package/dist/agent/chat/models.d.ts.map +1 -0
  23. package/dist/agent/chat/models.js +37 -0
  24. package/dist/agent/chat/prompt.d.ts +2 -1
  25. package/dist/agent/chat/prompt.d.ts.map +1 -1
  26. package/dist/agent/chat/prompt.js +40 -12
  27. package/dist/agent/chat/repo.d.ts +2 -1
  28. package/dist/agent/chat/repo.d.ts.map +1 -1
  29. package/dist/agent/chat/repo.js +32 -36
  30. package/dist/agent/chat/state.d.ts +15 -7
  31. package/dist/agent/chat/state.d.ts.map +1 -1
  32. package/dist/agent/chat/state.js +95 -32
  33. package/dist/agent/chat/types.d.ts +0 -1
  34. package/dist/agent/chat/types.d.ts.map +1 -1
  35. package/dist/agent/codegen/create-test-block.js +1 -2
  36. package/dist/agent/codegen/fix-ts-errors.js +1 -2
  37. package/dist/agent/codegen/generate-code-apply-changes.js +2 -3
  38. package/dist/agent/codegen/lexical-scoped-vars.js +1 -2
  39. package/dist/agent/codegen/repo-edit.js +2 -3
  40. package/dist/agent/codegen/run.js +2 -3
  41. package/dist/agent/codegen/skills-retriever.d.ts +3 -3
  42. package/dist/agent/codegen/skills-retriever.d.ts.map +1 -1
  43. package/dist/agent/codegen/skills-retriever.js +2 -2
  44. package/dist/agent/codegen/test-update-feedback.js +1 -2
  45. package/dist/agent/codegen/update-flow.js +4 -5
  46. package/dist/agent/codegen/use-skill.js +1 -2
  47. package/dist/agent/codegen/utils.js +9 -10
  48. package/dist/agent/cua/computer.js +3 -4
  49. package/dist/agent/cua/index.js +2 -3
  50. package/dist/agent/cua/model.js +1 -2
  51. package/dist/agent/diagnosis-agent/index.js +2 -3
  52. package/dist/agent/diagnosis-agent/strict-mode-violation.js +1 -2
  53. package/dist/agent/enrich-prompt/index.d.ts +1 -1
  54. package/dist/agent/enrich-prompt/index.d.ts.map +1 -1
  55. package/dist/agent/enrich-prompt/utils.js +1 -2
  56. package/dist/agent/infer-agent/index.js +1 -2
  57. package/dist/agent/master/action-tool-calls.js +3 -3
  58. package/dist/agent/master/browser-tests/index.spec.js +6 -6
  59. package/dist/agent/master/element-annotation.js +2 -3
  60. package/dist/agent/master/execute-browser-action.d.ts +1 -1
  61. package/dist/agent/master/execute-browser-action.d.ts.map +1 -1
  62. package/dist/agent/master/execute-browser-action.js +1 -2
  63. package/dist/agent/master/execute-skill-action.js +1 -2
  64. package/dist/agent/master/icon-descriptor/index.js +6 -7
  65. package/dist/agent/master/icon-descriptor/normalize-svg.js +1 -2
  66. package/dist/agent/master/next-action.js +1 -2
  67. package/dist/agent/master/planner.d.ts.map +1 -1
  68. package/dist/agent/master/planner.js +1 -2
  69. package/dist/agent/master/run.js +3 -3
  70. package/dist/agent/master/scroller.js +1 -2
  71. package/dist/agent/master/with-hints.d.ts +1 -1
  72. package/dist/agent/master/with-hints.d.ts.map +1 -1
  73. package/dist/agent/planner/run-time-planner.d.ts.map +1 -1
  74. package/dist/agent/planner/run-time-planner.js +1 -2
  75. package/dist/agent/planner/run.js +1 -2
  76. package/dist/bin/index.js +4 -0
  77. package/dist/bin/logger/index.js +2 -2
  78. package/dist/bin/utils/context.js +5 -6
  79. package/dist/bin/utils/fs/index.d.ts.map +1 -1
  80. package/dist/bin/utils/fs/index.js +4 -5
  81. package/dist/bin/utils/index.d.ts +1 -0
  82. package/dist/bin/utils/index.d.ts.map +1 -1
  83. package/dist/bin/utils/index.js +2 -3
  84. package/dist/bin/utils/platform/web/index.d.ts.map +1 -1
  85. package/dist/bin/utils/platform/web/index.js +21 -21
  86. package/dist/bin/utils/scenarios/index.js +3 -4
  87. package/dist/constants/index.js +1 -1
  88. package/dist/file/server.js +2 -2
  89. package/dist/human-in-the-loop/cli.js +1 -2
  90. package/dist/human-in-the-loop/ipc.js +2 -3
  91. package/dist/index.d.ts +1 -0
  92. package/dist/index.d.ts.map +1 -1
  93. package/dist/index.js +4 -2
  94. package/dist/prompts/lib/ts-transformer.js +17 -7
  95. package/dist/reporter/index.js +3 -3
  96. package/dist/reporter/lib.js +1 -1
  97. package/dist/session/index.js +6 -7
  98. package/dist/test-build/index.js +3 -4
  99. package/dist/tool-call-service/index.d.ts +3 -3
  100. package/dist/tool-call-service/index.d.ts.map +1 -1
  101. package/dist/tool-call-service/index.js +51 -71
  102. package/dist/tool-call-service/utils.d.ts +10 -0
  103. package/dist/tool-call-service/utils.d.ts.map +1 -0
  104. package/dist/tool-call-service/utils.js +23 -0
  105. package/dist/tools/commit-and-create-pr.js +2 -2
  106. package/dist/tools/download-build.d.ts +9 -0
  107. package/dist/tools/download-build.d.ts.map +1 -1
  108. package/dist/tools/download-build.js +5 -4
  109. package/dist/tools/grep/ripgrep/index.js +2 -3
  110. package/dist/tools/grep/ripgrep/types.d.ts +0 -2
  111. package/dist/tools/grep/ripgrep/types.d.ts.map +1 -1
  112. package/dist/tools/str_replace_editor.js +3 -3
  113. package/dist/tools/test-gen-browser.d.ts.map +1 -1
  114. package/dist/tools/test-gen-browser.js +8 -13
  115. package/dist/tools/test-run-fetcher/index.js +2 -2
  116. package/dist/tools/test-run.d.ts.map +1 -1
  117. package/dist/tools/test-run.js +8 -13
  118. package/dist/tools/utils/index.js +2 -3
  119. package/dist/types/index.d.ts +8 -0
  120. package/dist/types/index.d.ts.map +1 -1
  121. package/dist/uploader/index.js +6 -6
  122. package/dist/uploader/utils.d.ts.map +1 -1
  123. package/dist/uploader/utils.js +2 -2
  124. package/dist/utils/checkpoint.d.ts.map +1 -1
  125. package/dist/utils/checkpoint.js +4 -3
  126. package/dist/utils/env.js +1 -2
  127. package/dist/utils/exec.d.ts +2 -2
  128. package/dist/utils/exec.d.ts.map +1 -1
  129. package/dist/utils/exec.js +7 -6
  130. package/dist/utils/file-tree.d.ts +3 -0
  131. package/dist/utils/file-tree.d.ts.map +1 -0
  132. package/dist/utils/file-tree.js +42 -0
  133. package/dist/utils/file.js +1 -2
  134. package/dist/utils/git.js +7 -8
  135. package/dist/utils/html.d.ts.map +1 -1
  136. package/dist/utils/pw-test.js +1 -2
  137. package/dist/utils/repo-tree.d.ts +2 -1
  138. package/dist/utils/repo-tree.d.ts.map +1 -1
  139. package/dist/utils/repo-tree.js +62 -53
  140. package/dist/utils/slug.d.ts.map +1 -1
  141. package/dist/utils/slug.js +1 -1
  142. package/dist/utils/string.js +1 -2
  143. package/package.json +5 -5
  144. package/tsconfig.tsbuildinfo +1 -0
  145. package/dist/agent/chat/model.d.ts +0 -4
  146. package/dist/agent/chat/model.d.ts.map +0 -1
  147. package/dist/agent/chat/model.js +0 -17
@@ -1,38 +1,18 @@
1
1
  "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || function (mod) {
19
- if (mod && mod.__esModule) return mod;
20
- var result = {};
21
- if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
- __setModuleDefault(result, mod);
23
- return result;
24
- };
25
2
  Object.defineProperty(exports, "__esModule", { value: true });
26
- exports.saveToDisk = exports.loadChatState = exports.CURRENT_CHAT_STATE_VERSION = exports.createChatStateForMessages = exports.createChatState = exports.createChatModel = exports.chatStateFromModel = exports.chatAgentLoop = void 0;
3
+ exports.SUPPORTED_CHAT_MODELS = exports.migrateChatState = exports.LATEST_CHAT_STATE_VERSION = exports.getLatestDownloadBuildUrl = exports.defaultModel = exports.createChatStateForMessages = exports.createChatState = exports.createChatModel = exports.chatStateFromModel = exports.chatAgentLoop = exports.CHAT_STATE_VERSIONS_MIGRATIONS_MAP = void 0;
4
+ const chat_1 = require("@empiricalrun/llm/chat");
5
+ Object.defineProperty(exports, "createChatModel", { enumerable: true, get: function () { return chat_1.createChatModel; } });
27
6
  const agent_loop_1 = require("./agent-loop");
28
7
  Object.defineProperty(exports, "chatAgentLoop", { enumerable: true, get: function () { return agent_loop_1.chatAgentLoop; } });
29
- const model_1 = require("./model");
30
- Object.defineProperty(exports, "createChatModel", { enumerable: true, get: function () { return model_1.createChatModel; } });
31
- const State = __importStar(require("./state"));
32
- const { createChatState, createChatStateForMessages, chatStateFromModel, loadChatState, saveToDisk, CURRENT_CHAT_STATE_VERSION, } = State;
33
- exports.createChatState = createChatState;
34
- exports.createChatStateForMessages = createChatStateForMessages;
35
- exports.chatStateFromModel = chatStateFromModel;
36
- exports.loadChatState = loadChatState;
37
- exports.saveToDisk = saveToDisk;
38
- exports.CURRENT_CHAT_STATE_VERSION = CURRENT_CHAT_STATE_VERSION;
8
+ const models_1 = require("./models");
9
+ Object.defineProperty(exports, "defaultModel", { enumerable: true, get: function () { return models_1.defaultModel; } });
10
+ Object.defineProperty(exports, "SUPPORTED_CHAT_MODELS", { enumerable: true, get: function () { return models_1.SUPPORTED_CHAT_MODELS; } });
11
+ const state_1 = require("./state");
12
+ Object.defineProperty(exports, "CHAT_STATE_VERSIONS_MIGRATIONS_MAP", { enumerable: true, get: function () { return state_1.CHAT_STATE_VERSIONS_MIGRATIONS_MAP; } });
13
+ Object.defineProperty(exports, "chatStateFromModel", { enumerable: true, get: function () { return state_1.chatStateFromModel; } });
14
+ Object.defineProperty(exports, "createChatState", { enumerable: true, get: function () { return state_1.createChatState; } });
15
+ Object.defineProperty(exports, "createChatStateForMessages", { enumerable: true, get: function () { return state_1.createChatStateForMessages; } });
16
+ Object.defineProperty(exports, "getLatestDownloadBuildUrl", { enumerable: true, get: function () { return state_1.getLatestDownloadBuildUrl; } });
17
+ Object.defineProperty(exports, "LATEST_CHAT_STATE_VERSION", { enumerable: true, get: function () { return state_1.LATEST_CHAT_STATE_VERSION; } });
18
+ Object.defineProperty(exports, "migrateChatState", { enumerable: true, get: function () { return state_1.migrateChatState; } });
@@ -1,4 +1,4 @@
1
- import { SupportedChatModels } from "./types";
1
+ import { SupportedChatModels } from "@empiricalrun/llm/chat";
2
2
  export declare function runChatAgentForCLI({ useDiskForChatState, selectedModel, initialPromptContent, }: {
3
3
  selectedModel: SupportedChatModels;
4
4
  useDiskForChatState: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/agent/chat/index.ts"],"names":[],"mappings":"AAUA,OAAO,EAAoB,mBAAmB,EAAE,MAAM,SAAS,CAAC;AAiBhE,wBAAsB,kBAAkB,CAAC,EACvC,mBAAmB,EACnB,aAAa,EACb,oBAAoB,GACrB,EAAE;IACD,aAAa,EAAE,mBAAmB,CAAC;IACnC,mBAAmB,EAAE,OAAO,CAAC;IAC7B,oBAAoB,EAAE,MAAM,GAAG,SAAS,CAAC;CAC1C,iBA8FA;AA0BD,wBAAsB,wBAAwB,CAAC,EAC7C,aAAa,EACb,aAAa,GACd,EAAE;IACD,aAAa,EAAE,mBAAmB,CAAC;IACnC,aAAa,EAAE,MAAM,CAAC;CACvB,iBA8CA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/agent/chat/index.ts"],"names":[],"mappings":"AACA,OAAO,EAGL,mBAAmB,EACpB,MAAM,wBAAwB,CAAC;AAmChC,wBAAsB,kBAAkB,CAAC,EACvC,mBAAmB,EACnB,aAAa,EACb,oBAAoB,GACrB,EAAE;IACD,aAAa,EAAE,mBAAmB,CAAC;IACnC,mBAAmB,EAAE,OAAO,CAAC;IAC7B,oBAAoB,EAAE,MAAM,GAAG,SAAS,CAAC;CAC1C,iBAiGA;AAuBD,wBAAsB,wBAAwB,CAAC,EAC7C,aAAa,EACb,aAAa,GACd,EAAE;IACD,aAAa,EAAE,mBAAmB,CAAC;IACnC,aAAa,EAAE,MAAM,CAAC;CACvB,iBAsDA"}
@@ -1,21 +1,24 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.runChatAgentForDashboard = exports.runChatAgentForCLI = void 0;
3
+ exports.runChatAgentForCLI = runChatAgentForCLI;
4
+ exports.runChatAgentForDashboard = runChatAgentForDashboard;
4
5
  const llm_1 = require("@empiricalrun/llm");
6
+ const chat_1 = require("@empiricalrun/llm/chat");
5
7
  const picocolors_1 = require("picocolors");
6
8
  const human_in_the_loop_1 = require("../../human-in-the-loop");
7
9
  const tool_call_service_1 = require("../../tool-call-service");
10
+ const file_tree_1 = require("../../utils/file-tree");
8
11
  const git_1 = require("../../utils/git");
9
12
  const agent_loop_1 = require("./agent-loop");
10
- const model_1 = require("./model");
11
13
  const state_1 = require("./state");
14
+ const DASHBOARD_DOMAIN = process.env.DASHBOARD_DOMAIN || "https://dash.empirical.run";
12
15
  function stopCriteria(userPrompt) {
13
16
  return userPrompt?.toLowerCase() === "stop";
14
17
  }
15
18
  function concludeAgent(chatModel, useDiskForChatState, selectedModel) {
16
19
  console.log(`\n${(0, picocolors_1.gray)("Usage summary -> " + chatModel.getUsageSummary())}`);
17
20
  if (useDiskForChatState) {
18
- (0, state_1.saveToDisk)(chatModel.messages, selectedModel);
21
+ (0, state_1.saveToDisk)(chatModel.messages, selectedModel, chatModel.askUserForInput);
19
22
  }
20
23
  }
21
24
  async function runChatAgentForCLI({ useDiskForChatState, selectedModel, initialPromptContent, }) {
@@ -28,7 +31,7 @@ async function runChatAgentForCLI({ useDiskForChatState, selectedModel, initialP
28
31
  const branchName = `branch-${randomId}`;
29
32
  await (0, git_1.checkoutBranch)(branchName);
30
33
  let messagesLoadedFromDisk = chatState?.messages || [];
31
- let chatModel = (0, model_1.createChatModel)(messagesLoadedFromDisk, selectedModel);
34
+ let chatModel = (0, chat_1.createChatModel)(messagesLoadedFromDisk, selectedModel);
32
35
  if (initialPromptContent && chatModel.messages.length === 0) {
33
36
  chatModel.pushUserMessage(initialPromptContent);
34
37
  chatModel.askUserForInput = false;
@@ -52,7 +55,7 @@ async function runChatAgentForCLI({ useDiskForChatState, selectedModel, initialP
52
55
  let userPrompt;
53
56
  let reporterFunc = async (chatState, latest) => {
54
57
  if (useDiskForChatState) {
55
- (0, state_1.saveToDisk)(chatState.messages, selectedModel);
58
+ (0, state_1.saveToDisk)(chatState.messages, selectedModel, chatState.askUserForInput);
56
59
  }
57
60
  if (latest) {
58
61
  console.log(`${(0, picocolors_1.blue)(latest.role)}: ${latest.textMessage}`);
@@ -89,12 +92,15 @@ async function runChatAgentForCLI({ useDiskForChatState, selectedModel, initialP
89
92
  else {
90
93
  // TODO: Should we pass a loader function? That would allow us to show a spinner
91
94
  const toolCallService = new tool_call_service_1.ToolCallService(null, selectedModel, branchName);
95
+ const fileInfo = await (0, file_tree_1.getFileInfoFromFS)(process.cwd());
92
96
  await (0, agent_loop_1.chatAgentLoop)({
93
97
  chatModel,
94
98
  selectedModel,
95
99
  reporter: reporterFunc,
96
100
  trace,
97
101
  toolCallService,
102
+ fileInfo,
103
+ isToolExecutionRemote: false,
98
104
  });
99
105
  }
100
106
  }
@@ -107,8 +113,6 @@ async function runChatAgentForCLI({ useDiskForChatState, selectedModel, initialP
107
113
  const usageSummary = chatModel.getUsageSummary();
108
114
  console.log(`\n${(0, picocolors_1.gray)("Usage summary -> " + usageSummary)}`);
109
115
  }
110
- exports.runChatAgentForCLI = runChatAgentForCLI;
111
- const DASHBOARD_DOMAIN = process.env.DASHBOARD_DOMAIN || "https://dash.empirical.run";
112
116
  async function getChatSessionFromDashboard(chatSessionId) {
113
117
  const response = await fetch(`${DASHBOARD_DOMAIN}/api/chat-sessions/${chatSessionId}`, {
114
118
  headers: {
@@ -124,7 +128,11 @@ async function getChatSessionFromDashboard(chatSessionId) {
124
128
  }
125
129
  async function runChatAgentForDashboard({ chatSessionId, selectedModel, }) {
126
130
  const chatSession = await getChatSessionFromDashboard(chatSessionId);
127
- const chatState = chatSession.chat_state;
131
+ let chatState = chatSession.chat_state;
132
+ // If not already canonical, migrate to canonical format
133
+ if (!chatState.version || chatState.version !== state_1.LATEST_CHAT_STATE_VERSION) {
134
+ chatState = (0, state_1.migrateChatState)(chatState);
135
+ }
128
136
  const branchName = chatSession.branch_name;
129
137
  const trace = llm_1.langfuseInstance?.trace({
130
138
  id: chatSession.langfuse_trace_id,
@@ -137,7 +145,7 @@ async function runChatAgentForDashboard({ chatSessionId, selectedModel, }) {
137
145
  });
138
146
  const toolCallService = new tool_call_service_1.ToolCallService(chatSessionId, selectedModel, branchName);
139
147
  await (0, git_1.checkoutBranch)(branchName);
140
- let chatModel = (0, model_1.createChatModel)(chatState.messages, selectedModel);
148
+ let chatModel = (0, chat_1.createChatModel)(chatState.messages, selectedModel);
141
149
  let reporterFunc = async (chatState, latest) => {
142
150
  const response = await fetch(`${DASHBOARD_DOMAIN}/api/chat-sessions/${chatSessionId}`, {
143
151
  method: "PATCH",
@@ -153,12 +161,14 @@ async function runChatAgentForDashboard({ chatSessionId, selectedModel, }) {
153
161
  const data = await response.json();
154
162
  console.log(`Patch request sent for chat session: ${JSON.stringify(data)}`);
155
163
  };
164
+ const fileInfo = await (0, file_tree_1.getFileInfoFromFS)(process.cwd());
156
165
  await (0, agent_loop_1.chatAgentLoop)({
157
166
  chatModel,
158
167
  selectedModel,
159
168
  reporter: reporterFunc,
160
169
  trace,
161
170
  toolCallService,
171
+ fileInfo,
172
+ isToolExecutionRemote: false,
162
173
  });
163
174
  }
164
- exports.runChatAgentForDashboard = runChatAgentForDashboard;
@@ -0,0 +1,6 @@
1
+ import type { ModelInfo } from "@empiricalrun/shared-types";
2
+ export declare const SUPPORTED_CHAT_MODELS: readonly ModelInfo[];
3
+ export type SupportedChatModels = (typeof SUPPORTED_CHAT_MODELS)[number]["id"];
4
+ export declare const defaultModel: SupportedChatModels;
5
+ export declare const modelLabels: Record<SupportedChatModels, string>;
6
+ //# sourceMappingURL=models.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"models.d.ts","sourceRoot":"","sources":["../../../src/agent/chat/models.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAC;AAE5D,eAAO,MAAM,qBAAqB,EAAE,SAAS,SAAS,EAqB5C,CAAC;AAEX,MAAM,MAAM,mBAAmB,GAAG,CAAC,OAAO,qBAAqB,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC;AAW/E,eAAO,MAAM,YAAY,EAAE,mBAA6C,CAAC;AAEzE,eAAO,MAAM,WAAW,EAAE,MAAM,CAAC,mBAAmB,EAAE,MAAM,CAOzD,CAAC"}
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.modelLabels = exports.defaultModel = exports.SUPPORTED_CHAT_MODELS = void 0;
4
+ exports.SUPPORTED_CHAT_MODELS = [
5
+ {
6
+ id: "gemini-2.5-pro-preview-03-25",
7
+ label: "Gemini 2.5 Pro",
8
+ provider: "google",
9
+ },
10
+ {
11
+ id: "o4-mini-2025-04-16",
12
+ label: "OpenAI O4 Mini",
13
+ provider: "openai",
14
+ },
15
+ {
16
+ id: "claude-3-7-sonnet-20250219",
17
+ label: "Claude 3.7 Sonnet",
18
+ provider: "claude",
19
+ },
20
+ {
21
+ id: "claude-3-5-sonnet-20241022",
22
+ label: "Claude 3.5 Sonnet",
23
+ provider: "claude",
24
+ },
25
+ ];
26
+ const DEFAULT_CHAT_MODEL_ID = "gemini-2.5-pro-preview-03-25";
27
+ function getDefaultChatModelId() {
28
+ if (!exports.SUPPORTED_CHAT_MODELS.some((m) => m.id === DEFAULT_CHAT_MODEL_ID)) {
29
+ throw new Error("Default chat model is not in SUPPORTED_CHAT_MODELS");
30
+ }
31
+ return DEFAULT_CHAT_MODEL_ID;
32
+ }
33
+ exports.defaultModel = getDefaultChatModelId();
34
+ exports.modelLabels = exports.SUPPORTED_CHAT_MODELS.reduce((acc, model) => ({
35
+ ...acc,
36
+ [model.id]: model.label,
37
+ }), {});
@@ -1,2 +1,3 @@
1
- export declare function buildSystemPrompt(): Promise<string>;
1
+ import { FileInfo } from "../../types";
2
+ export declare function buildSystemPrompt(fileInfo: FileInfo): Promise<string>;
2
3
  //# sourceMappingURL=prompt.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"prompt.d.ts","sourceRoot":"","sources":["../../../src/agent/chat/prompt.ts"],"names":[],"mappings":"AAEA,wBAAsB,iBAAiB,oBAoEtC"}
1
+ {"version":3,"file":"prompt.d.ts","sourceRoot":"","sources":["../../../src/agent/chat/prompt.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAGvC,wBAAsB,iBAAiB,CAAC,QAAQ,EAAE,QAAQ,mBAiGzD"}
@@ -1,9 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.buildSystemPrompt = void 0;
3
+ exports.buildSystemPrompt = buildSystemPrompt;
4
4
  const repo_1 = require("./repo");
5
- async function buildSystemPrompt() {
6
- const repoContext = await (0, repo_1.getRepoContext)();
5
+ async function buildSystemPrompt(fileInfo) {
6
+ const repoContext = await (0, repo_1.getRepoInfoPrompt)(fileInfo);
7
7
  return `
8
8
  You are a helpful assistant that can answer questions and help with tasks related to writing
9
9
  and maintaining Playwright tests.
@@ -52,23 +52,51 @@ The position of the comment is important: the browser agent will look for this c
52
52
  the actual code to click on the login button. If you are fixing a failing test, your comment should be
53
53
  around the failing line of code, so that it can be replaced/modified.
54
54
 
55
- # Rules for fixing Playwright tests
55
+ # Proactiveness
56
+
57
+ 1. You are allowed to be proactive, but ONLY for read-only tool calls: like searching for content, reading files, fetching data from tools, and
58
+ running Playwright tests.
59
+ 2. For any read-write tool calls (e.g. modifying any file), you should share your plan and get the user's approval before proceeding.
60
+
61
+ # Rules to follow
56
62
 
57
63
  You must follow these rules while adding new tests or modifying existing tests. There can be exceptions to these rules, but
58
64
  ONLY when explicitly asked for by the user.
59
65
 
60
- 1. Do not add any conditional logic or try catch blocks in a test. A good test deterministically tests a user scenario
61
- 2. Trust Playwright's ability to auto-wait while taking actions on elements. For example, do not add checks on locator.isVisible() before clicking on it: Playwright already does this
62
- 3. Do not add waitForTimeout or waitForLoadState in a test. Playwright will automatically wait for the page to load.
63
- 4. You can't delete some steps from the test to make it pass. The test needs to accomplish its objective (which is to validate a particular user scenario)
66
+ 1. You can't delete some steps from the test to make it pass. The test needs to accomplish its objective (which is to validate a particular user scenario)
67
+ 2. Do not add any conditional logic or try catch blocks in a test. A good test deterministically tests a user scenario
68
+ 3. Trust Playwright's ability to auto-wait while taking actions on elements. For example, do not add checks on locator.isVisible() before clicking on it: Playwright already does this
69
+ 4. Do not add waitForTimeout or waitForLoadState in a test. Playwright will automatically wait for the page to load.
70
+ 5. Try/catch blocks are a code smell for tests: you should not use them.
71
+ 6. Do not use then() or catch() syntax in a test. Use async/await only
64
72
 
65
- # Proactiveness
73
+ There are few exceptions to these rules. BEFORE applying any of the following exceptions, you MUST share your plan with the user and get their approval.
74
+
75
+ ## Exceptions for conditional logic
76
+
77
+ There are few exceptions where you can add conditional logic to a test. If the application UI reveals some UI elements on certain conditions, we can add conditional logic.
66
78
 
67
- You are allowed to be proactive, but ONLY for read-only actions, like searching for content, reading files, fetching data from tools, and
68
- running Playwright tests. For any read-write actions (e.g. modifying any file), you should share your plan and get the user's approval before proceeding.
79
+ For example, a form view shows a "Save" button only when the form is dirty. In this case, we will have to check if the "Save" button is visible before clicking on it. To do this,
80
+ follow this pattern:
81
+
82
+ \`\`\`
83
+ const saveButton = page.getByRole('button', { name: 'Save' });
84
+ if (await saveButton.isVisible()) {
85
+ await saveButton.click();
86
+ }
87
+ \`\`\`
88
+
89
+ Note that locator.isVisible() DOES NOT wait for the element to be visible. If the element in question shows up after a delay, we have no option but to add a waitForTimeout.
90
+
91
+ \`\`\`
92
+ const saveButton = page.getByRole('button', { name: 'Save' });
93
+ await page.waitForTimeout(100); // Wait for the element to be visible -- only if necessary.
94
+ if (await saveButton.isVisible()) {
95
+ await saveButton.click();
96
+ }
97
+ \`\`\`
69
98
 
70
99
  # Repo context
71
100
  ${repoContext}
72
101
  `;
73
102
  }
74
- exports.buildSystemPrompt = buildSystemPrompt;
@@ -1,2 +1,3 @@
1
- export declare function getRepoContext(): Promise<string>;
1
+ import { FileInfo } from "../../types";
2
+ export declare function getRepoInfoPrompt(directory: FileInfo): Promise<string>;
2
3
  //# sourceMappingURL=repo.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"repo.d.ts","sourceRoot":"","sources":["../../../src/agent/chat/repo.ts"],"names":[],"mappings":"AAqCA,wBAAsB,cAAc,oBAyCnC"}
1
+ {"version":3,"file":"repo.d.ts","sourceRoot":"","sources":["../../../src/agent/chat/repo.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAwCvC,wBAAsB,iBAAiB,CAAC,SAAS,EAAE,QAAQ,mBAyC1D"}
@@ -3,28 +3,28 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.getRepoContext = void 0;
7
- const fs_1 = __importDefault(require("fs"));
6
+ exports.getRepoInfoPrompt = getRepoInfoPrompt;
8
7
  const path_1 = __importDefault(require("path"));
9
8
  const repo_tree_1 = require("../../utils/repo-tree");
10
- const utils_1 = require("../browsing/utils");
11
- async function getAllMarkdownFiles() {
12
- const dir = path_1.default.join(process.cwd(), ".empiricalrun");
13
- if (!fs_1.default.existsSync(dir)) {
9
+ async function getAllMarkdownFiles(directory) {
10
+ if (!directory.isDirectory) {
14
11
  return [];
15
12
  }
16
- const files = fs_1.default.readdirSync(dir);
17
- return files
18
- .filter((file) => file.endsWith(".md"))
19
- .map((file) => {
13
+ const files = directory.children.find((child) => child.isDirectory && child.path === ".empiricalrun")?.children;
14
+ if (!files) {
15
+ return [];
16
+ }
17
+ return Promise.all(files
18
+ .filter((file) => file.isFile && file.path.endsWith(".md"))
19
+ .map(async (file) => {
20
20
  return {
21
- name: file,
22
- content: fs_1.default.readFileSync(path_1.default.join(dir, file), "utf8"),
21
+ name: path_1.default.basename(file.path),
22
+ content: await file.getContent(),
23
23
  };
24
- });
24
+ }));
25
25
  }
26
- async function knowledgeContext() {
27
- const mdFiles = await getAllMarkdownFiles();
26
+ async function knowledgeContext(directory) {
27
+ const mdFiles = await getAllMarkdownFiles(directory);
28
28
  const knowledge = mdFiles.map((file) => {
29
29
  return `
30
30
  <knowledge_file>
@@ -37,35 +37,32 @@ async function knowledgeContext() {
37
37
  });
38
38
  return knowledge.join("\n");
39
39
  }
40
- async function getRepoContext() {
40
+ async function getRepoInfoPrompt(directory) {
41
41
  let REPO_CONTEXT_PROMPT = `
42
42
  You are running as a CLI tool inside the directory of the repo that has Playwright tests.
43
43
 
44
44
  Here is the repo directory structure:
45
45
 
46
- ${(0, repo_tree_1.generateAsciiTree)(process.cwd())}
46
+ ${(0, repo_tree_1.generateAsciiTree)(directory)}
47
47
 
48
48
  While specifying paths to files, use relative paths from the current working directory. For example:
49
49
  - Correct path: "tests/lesson.spec.ts"
50
- - Incorrect path: "/repo/tests/lesson.spec.ts" or "${path_1.default.basename(process.cwd())}/tests/lesson.spec.ts"
50
+ - Incorrect path: "/repo/tests/lesson.spec.ts" or "${directory.name}/tests/lesson.spec.ts"
51
51
  `;
52
- try {
53
- const playwrightConfig = await (0, utils_1.readPlaywrightConfig)(process.cwd());
54
- const validProjectNames = await (0, utils_1.getValidProjectNames)(playwrightConfig);
55
- if (validProjectNames.length > 0) {
56
- REPO_CONTEXT_PROMPT += `
57
- ## Playwright configuration
58
-
59
- This repo is configured with these Playwright projects (in the playwright.config.ts file):
60
- ${validProjectNames.map((name) => `- ${name}`).join("\n")}
61
-
62
- `;
63
- }
64
- }
65
- catch (error) {
66
- console.warn("Failed to read playwright config", error);
67
- }
68
- const knowledge = await knowledgeContext();
52
+ // try {
53
+ // const playwrightConfig = await readPlaywrightConfig(process.cwd());
54
+ // const validProjectNames = await getValidProjectNames(playwrightConfig);
55
+ // if (validProjectNames.length > 0) {
56
+ // REPO_CONTEXT_PROMPT += `
57
+ // ## Playwright configuration
58
+ // This repo is configured with these Playwright projects (in the playwright.config.ts file):
59
+ // ${validProjectNames.map((name) => `- ${name}`).join("\n")}
60
+ // `;
61
+ // }
62
+ // } catch (error) {
63
+ // console.warn("Failed to read playwright config", error);
64
+ // }
65
+ const knowledge = await knowledgeContext(directory);
69
66
  if (knowledge.length > 0) {
70
67
  REPO_CONTEXT_PROMPT += `
71
68
  ## Repo-specific knowledge
@@ -78,4 +75,3 @@ ${knowledge}
78
75
  }
79
76
  return REPO_CONTEXT_PROMPT;
80
77
  }
81
- exports.getRepoContext = getRepoContext;
@@ -1,15 +1,23 @@
1
- import { IChatModel } from "@empiricalrun/llm/chat";
2
- import { SupportedChatModels } from "./types";
3
- export declare const CURRENT_CHAT_STATE_VERSION = "20250327.1";
1
+ import { IChatModel, SupportedChatModels } from "@empiricalrun/llm/chat";
2
+ import { CanonicalMessage, ChatState } from "@empiricalrun/shared-types";
3
+ export declare const CHAT_STATE_VERSIONS_MIGRATIONS_MAP: Record<string, (state: any) => any>;
4
+ export declare const LATEST_CHAT_STATE_VERSION = "0.1";
4
5
  export declare const CHAT_STATE_PATH: string;
5
6
  export type ChatStateOnDisk<T> = {
6
7
  version: string;
7
8
  model: SupportedChatModels;
8
9
  messages: T[];
10
+ askUserForInput: boolean;
9
11
  };
10
- export declare function createChatState(userPrompt: string, existingState: ChatStateOnDisk<any> | undefined, selectedModel: SupportedChatModels): ChatStateOnDisk<unknown>;
11
- export declare function createChatStateForMessages<T>(messages: any, selectedModel: SupportedChatModels): ChatStateOnDisk<T>;
12
+ export declare function createChatState(userPrompt: string | undefined, existingState: ChatStateOnDisk<any> | undefined, selectedModel: SupportedChatModels): ChatStateOnDisk<unknown>;
13
+ export declare function createChatStateForMessages<T>(messages: any, selectedModel: SupportedChatModels, askUserForInput: boolean): ChatStateOnDisk<T>;
12
14
  export declare function chatStateFromModel<T>(chatModel: IChatModel<T>, selectedModel: SupportedChatModels): ChatStateOnDisk<unknown>;
13
- export declare function loadChatState<T>(): ChatStateOnDisk<T> | undefined;
14
- export declare function saveToDisk<T>(messages: Array<T>, selectedModel: SupportedChatModels): void;
15
+ export declare function loadChatState(): ChatState | undefined;
16
+ /**
17
+ * Migrates a chat state object from an old version to the latest version.
18
+ * Add migration logic for each version as needed.
19
+ */
20
+ export declare function migrateChatState<T = any>(oldState: any): ChatStateOnDisk<T>;
21
+ export declare function saveToDisk<T>(messages: Array<T>, selectedModel: SupportedChatModels, askUserForInput: boolean): void;
22
+ export declare function getLatestDownloadBuildUrl(messages: CanonicalMessage[]): string | null;
15
23
  //# sourceMappingURL=state.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../../../src/agent/chat/state.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAKpD,OAAO,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AAE9C,eAAO,MAAM,0BAA0B,eAAe,CAAC;AAEvD,eAAO,MAAM,eAAe,QAI3B,CAAC;AAEF,MAAM,MAAM,eAAe,CAAC,CAAC,IAAI;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,mBAAmB,CAAC;IAC3B,QAAQ,EAAE,CAAC,EAAE,CAAC;CACf,CAAC;AAEF,wBAAgB,eAAe,CAC7B,UAAU,EAAE,MAAM,EAClB,aAAa,EAAE,eAAe,CAAC,GAAG,CAAC,GAAG,SAAS,EAC/C,aAAa,EAAE,mBAAmB,4BAMnC;AAED,wBAAgB,0BAA0B,CAAC,CAAC,EAC1C,QAAQ,EAAE,GAAG,EACb,aAAa,EAAE,mBAAmB,GACjC,eAAe,CAAC,CAAC,CAAC,CAOpB;AAED,wBAAgB,kBAAkB,CAAC,CAAC,EAClC,SAAS,EAAE,UAAU,CAAC,CAAC,CAAC,EACxB,aAAa,EAAE,mBAAmB,4BAGnC;AAED,wBAAgB,aAAa,CAAC,CAAC,KAAK,eAAe,CAAC,CAAC,CAAC,GAAG,SAAS,CAajE;AAED,wBAAgB,UAAU,CAAC,CAAC,EAC1B,QAAQ,EAAE,KAAK,CAAC,CAAC,CAAC,EAClB,aAAa,EAAE,mBAAmB,QAsBnC"}
1
+ {"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../../../src/agent/chat/state.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,UAAU,EACV,mBAAmB,EACpB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,gBAAgB,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAC;AA+BzE,eAAO,MAAM,kCAAkC,EAAE,MAAM,CACrD,MAAM,EACN,CAAC,KAAK,EAAE,GAAG,KAAK,GAAG,CAIpB,CAAC;AAEF,eAAO,MAAM,yBAAyB,QAAQ,CAAC;AAE/C,eAAO,MAAM,eAAe,QAI3B,CAAC;AAEF,MAAM,MAAM,eAAe,CAAC,CAAC,IAAI;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,mBAAmB,CAAC;IAC3B,QAAQ,EAAE,CAAC,EAAE,CAAC;IACd,eAAe,EAAE,OAAO,CAAC;CAC1B,CAAC;AAEF,wBAAgB,eAAe,CAC7B,UAAU,EAAE,MAAM,GAAG,SAAS,EAC9B,aAAa,EAAE,eAAe,CAAC,GAAG,CAAC,GAAG,SAAS,EAC/C,aAAa,EAAE,mBAAmB,4BAYnC;AAED,wBAAgB,0BAA0B,CAAC,CAAC,EAC1C,QAAQ,EAAE,GAAG,EACb,aAAa,EAAE,mBAAmB,EAClC,eAAe,EAAE,OAAO,GACvB,eAAe,CAAC,CAAC,CAAC,CAQpB;AAED,wBAAgB,kBAAkB,CAAC,CAAC,EAClC,SAAS,EAAE,UAAU,CAAC,CAAC,CAAC,EACxB,aAAa,EAAE,mBAAmB,4BAOnC;AAED,wBAAgB,aAAa,IAAI,SAAS,GAAG,SAAS,CAarD;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,GAAG,GAAG,EAAE,QAAQ,EAAE,GAAG,GAAG,eAAe,CAAC,CAAC,CAAC,CAqB3E;AAED,wBAAgB,UAAU,CAAC,CAAC,EAC1B,QAAQ,EAAE,KAAK,CAAC,CAAC,CAAC,EAClB,aAAa,EAAE,mBAAmB,EAClC,eAAe,EAAE,OAAO,QAezB;AAED,wBAAgB,yBAAyB,CACvC,QAAQ,EAAE,gBAAgB,EAAE,GAC3B,MAAM,GAAG,IAAI,CAqBf"}
@@ -3,64 +3,127 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.saveToDisk = exports.loadChatState = exports.chatStateFromModel = exports.createChatStateForMessages = exports.createChatState = exports.CHAT_STATE_PATH = exports.CURRENT_CHAT_STATE_VERSION = void 0;
6
+ exports.CHAT_STATE_PATH = exports.LATEST_CHAT_STATE_VERSION = exports.CHAT_STATE_VERSIONS_MIGRATIONS_MAP = void 0;
7
+ exports.createChatState = createChatState;
8
+ exports.createChatStateForMessages = createChatStateForMessages;
9
+ exports.chatStateFromModel = chatStateFromModel;
10
+ exports.loadChatState = loadChatState;
11
+ exports.migrateChatState = migrateChatState;
12
+ exports.saveToDisk = saveToDisk;
13
+ exports.getLatestDownloadBuildUrl = getLatestDownloadBuildUrl;
14
+ const chat_1 = require("@empiricalrun/llm/chat");
7
15
  const fs_1 = __importDefault(require("fs"));
8
16
  const path_1 = __importDefault(require("path"));
9
- const model_1 = require("./model");
10
- exports.CURRENT_CHAT_STATE_VERSION = "20250327.1";
17
+ // Migration wrapper for v20250327.1 -> v0.1 chat state versions
18
+ // v20250327.1 was model-specific, but v0.1 is canonical
19
+ function migrateToV01(oldState) {
20
+ if (oldState.model &&
21
+ typeof oldState.model === "string" &&
22
+ Array.isArray(oldState.messages)) {
23
+ const provider = (0, chat_1.getProviderForModel)(oldState.model);
24
+ if (provider === "google") {
25
+ return {
26
+ ...oldState,
27
+ version: "0.1",
28
+ messages: oldState.messages.map(chat_1.geminiToCanonical),
29
+ };
30
+ }
31
+ else {
32
+ throw new Error(`Unsupported state for migration with model: ${oldState.model} and version: ${oldState.version}`);
33
+ }
34
+ }
35
+ // If not Gemini or not matching, return the old state
36
+ return oldState;
37
+ }
38
+ exports.CHAT_STATE_VERSIONS_MIGRATIONS_MAP = {
39
+ "20250327.1": migrateToV01,
40
+ "0.1": (state) => state,
41
+ };
42
+ exports.LATEST_CHAT_STATE_VERSION = "0.1";
11
43
  exports.CHAT_STATE_PATH = path_1.default.join(process.cwd(), ".empiricalrun", "last-chat.json");
12
44
  function createChatState(userPrompt, existingState, selectedModel) {
13
45
  const messages = existingState?.messages || [];
14
- const chatModel = (0, model_1.createChatModel)(messages, selectedModel);
15
- chatModel.pushUserMessage(userPrompt);
16
- return createChatStateForMessages(chatModel.messages, selectedModel);
46
+ const chatModel = (0, chat_1.createChatModel)(messages, selectedModel);
47
+ if (userPrompt) {
48
+ chatModel.pushUserMessage(userPrompt);
49
+ }
50
+ return createChatStateForMessages(chatModel.messages, selectedModel, chatModel.askUserForInput);
17
51
  }
18
- exports.createChatState = createChatState;
19
- function createChatStateForMessages(messages, selectedModel) {
52
+ function createChatStateForMessages(messages, selectedModel, askUserForInput) {
20
53
  // TODO: Add better types for messages
21
54
  return {
22
- version: exports.CURRENT_CHAT_STATE_VERSION,
55
+ version: exports.LATEST_CHAT_STATE_VERSION,
23
56
  model: selectedModel,
24
57
  messages: messages,
58
+ askUserForInput: askUserForInput,
25
59
  };
26
60
  }
27
- exports.createChatStateForMessages = createChatStateForMessages;
28
61
  function chatStateFromModel(chatModel, selectedModel) {
29
- return createChatStateForMessages(chatModel.messages, selectedModel);
62
+ return createChatStateForMessages(chatModel.messages, selectedModel, chatModel.askUserForInput);
30
63
  }
31
- exports.chatStateFromModel = chatStateFromModel;
32
64
  function loadChatState() {
33
65
  if (!fs_1.default.existsSync(exports.CHAT_STATE_PATH)) {
34
66
  return undefined;
35
67
  }
36
68
  const raw = fs_1.default.readFileSync(exports.CHAT_STATE_PATH, "utf8");
37
- const state = JSON.parse(raw);
38
- if (state.version !== exports.CURRENT_CHAT_STATE_VERSION) {
39
- throw new Error(`Unsupported chat state v${state.version}. Expected v${exports.CURRENT_CHAT_STATE_VERSION}.`);
69
+ let state = JSON.parse(raw);
70
+ // Always migrate to the latest version after loading
71
+ const migratedState = migrateChatState(state);
72
+ // Only save if migration actually changed the state
73
+ if (JSON.stringify(state) !== JSON.stringify(migratedState)) {
74
+ fs_1.default.writeFileSync(exports.CHAT_STATE_PATH, JSON.stringify(migratedState, null, 2));
40
75
  }
41
- return state;
76
+ return migratedState;
42
77
  }
43
- exports.loadChatState = loadChatState;
44
- function saveToDisk(messages, selectedModel) {
45
- const statePath = exports.CHAT_STATE_PATH;
46
- let existingState = {
47
- version: exports.CURRENT_CHAT_STATE_VERSION,
48
- model: selectedModel,
49
- messages: [],
78
+ /**
79
+ * Migrates a chat state object from an old version to the latest version.
80
+ * Add migration logic for each version as needed.
81
+ */
82
+ function migrateChatState(oldState) {
83
+ if (!oldState || Object.keys(oldState).length === 0) {
84
+ return oldState;
85
+ }
86
+ if (!oldState.version) {
87
+ throw new Error("No version found in chat state");
88
+ }
89
+ if (!exports.CHAT_STATE_VERSIONS_MIGRATIONS_MAP[oldState.version]) {
90
+ throw new Error(`No migration function found for version: ${oldState.version}`);
91
+ }
92
+ if (oldState.version === exports.LATEST_CHAT_STATE_VERSION) {
93
+ return oldState;
94
+ }
95
+ const migrateFn = exports.CHAT_STATE_VERSIONS_MIGRATIONS_MAP[oldState.version];
96
+ const migrated = migrateFn(oldState);
97
+ return {
98
+ version: exports.LATEST_CHAT_STATE_VERSION,
99
+ ...migrated,
50
100
  };
101
+ }
102
+ function saveToDisk(messages, selectedModel, askUserForInput) {
103
+ const statePath = exports.CHAT_STATE_PATH;
51
104
  // Ensure directory exists before trying to read/write
52
105
  const dirname = path_1.default.dirname(statePath);
53
106
  if (!fs_1.default.existsSync(dirname)) {
54
107
  fs_1.default.mkdirSync(dirname, { recursive: true });
55
108
  }
56
- if (fs_1.default.existsSync(statePath)) {
57
- existingState = JSON.parse(fs_1.default.readFileSync(statePath, "utf8"));
58
- }
59
- const newState = {
60
- ...existingState,
61
- messages: messages,
62
- model: selectedModel,
63
- };
109
+ // Use the helper to build the new state
110
+ const newState = createChatStateForMessages(messages, selectedModel, askUserForInput);
64
111
  fs_1.default.writeFileSync(statePath, JSON.stringify(newState, null, 2));
65
112
  }
66
- exports.saveToDisk = saveToDisk;
113
+ function getLatestDownloadBuildUrl(messages) {
114
+ const toolCallMessage = messages
115
+ .sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime())
116
+ .find((p) => p.parts.find((p) => {
117
+ if ("toolCall" in p) {
118
+ return p.toolCall?.name === "downloadBuild";
119
+ }
120
+ return false;
121
+ }));
122
+ if (!toolCallMessage)
123
+ return null;
124
+ const toolCallPart = toolCallMessage.parts.find((p) => "toolCall" in p && p.toolCall?.name === "downloadBuild");
125
+ if (!toolCallPart || !("toolCall" in toolCallPart))
126
+ return null;
127
+ const input = toolCallPart.toolCall.input;
128
+ return input.buildUrl;
129
+ }
@@ -1,5 +1,4 @@
1
1
  import { ChatStateOnDisk } from "./state";
2
- export type SupportedChatModels = "claude-3-7-sonnet-20250219" | "claude-3-5-sonnet-20241022" | "gemini-2.5-pro-preview-03-25" | "o4-mini-2025-04-16";
3
2
  type LatestMessage = {
4
3
  role: string;
5
4
  textMessage: string;