@probelabs/probe 0.6.0-rc128 → 0.6.0-rc129

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.
package/cjs/index.cjs CHANGED
@@ -54812,7 +54812,7 @@ var init_semantics = __esm({
54812
54812
  return;
54813
54813
  for (const cn of contentNodes) {
54814
54814
  const ch = cn.children || {};
54815
- const inspectTok = (tk) => {
54815
+ const inspectTok = (tk, inQuoted = false) => {
54816
54816
  if (!tk)
54817
54817
  return false;
54818
54818
  const img = String(tk.image || "");
@@ -54822,7 +54822,7 @@ var init_semantics = __esm({
54822
54822
  this.ctx.errors.push({
54823
54823
  line: tk.startLine ?? 1,
54824
54824
  column: col,
54825
- severity: "warning",
54825
+ severity: inQuoted ? "error" : "warning",
54826
54826
  code: "FL-LABEL-BACKTICK",
54827
54827
  message: "Backticks (`\u2026`) inside node labels are not supported by Mermaid.",
54828
54828
  hint: 'Remove the backticks or use quotes instead, e.g., "GITHUB_ACTIONS" and "--cli".',
@@ -54834,12 +54834,12 @@ var init_semantics = __esm({
54834
54834
  };
54835
54835
  const texts = ch.Text || [];
54836
54836
  for (const tk of texts) {
54837
- if (inspectTok(tk))
54837
+ if (inspectTok(tk, false))
54838
54838
  return;
54839
54839
  }
54840
54840
  const qs = ch.QuotedString || [];
54841
54841
  for (const tk of qs) {
54842
- if (inspectTok(tk))
54842
+ if (inspectTok(tk, true))
54843
54843
  return;
54844
54844
  }
54845
54845
  }
@@ -55240,6 +55240,36 @@ function mapFlowchartParserError(err, text) {
55240
55240
  length: len
55241
55241
  };
55242
55242
  }
55243
+ {
55244
+ const caret0 = Math.max(0, column - 1);
55245
+ const openIdx = lineStr.lastIndexOf("[", caret0);
55246
+ if (openIdx !== -1) {
55247
+ const closeIdx = lineStr.indexOf("]", openIdx + 1);
55248
+ const seg = closeIdx !== -1 ? lineStr.slice(openIdx + 1, closeIdx) : lineStr.slice(openIdx + 1);
55249
+ if (seg.includes('"')) {
55250
+ return {
55251
+ line,
55252
+ column,
55253
+ severity: "error",
55254
+ code: "FL-LABEL-QUOTE-IN-UNQUOTED",
55255
+ message: "Quotes are not allowed inside unquoted node labels. Use " for quotes or wrap the entire label in quotes.",
55256
+ hint: 'Example: I[Log "processing N items"] or I["Log \\"processing N items\\""]',
55257
+ length: len
55258
+ };
55259
+ }
55260
+ if (seg.includes("(") || seg.includes(")")) {
55261
+ return {
55262
+ line,
55263
+ column,
55264
+ severity: "error",
55265
+ code: "FL-LABEL-PARENS-UNQUOTED",
55266
+ message: "Parentheses inside an unquoted label are not supported by Mermaid.",
55267
+ hint: 'Wrap the label in quotes, e.g., A["Mark (X)"] \u2014 or replace ( and ) with HTML entities: ( and ).',
55268
+ length: len
55269
+ };
55270
+ }
55271
+ }
55272
+ }
55243
55273
  if (tokType === "QuotedString") {
55244
55274
  return {
55245
55275
  line,
@@ -55272,6 +55302,24 @@ function mapFlowchartParserError(err, text) {
55272
55302
  length: len
55273
55303
  };
55274
55304
  }
55305
+ {
55306
+ const caret0 = Math.max(0, column - 1);
55307
+ const openIdx = lineStr.lastIndexOf("(", caret0);
55308
+ if (openIdx !== -1) {
55309
+ const seg = lineStr.slice(openIdx + 1);
55310
+ if (seg.includes("(") || seg.includes(")")) {
55311
+ return {
55312
+ line,
55313
+ column,
55314
+ severity: "error",
55315
+ code: "FL-LABEL-PARENS-UNQUOTED",
55316
+ message: "Parentheses inside an unquoted label are not supported by Mermaid.",
55317
+ hint: 'Wrap the label in quotes, e.g., A["Mark (X)"] \u2014 or replace ( and ) with HTML entities: ( and ).',
55318
+ length: len
55319
+ };
55320
+ }
55321
+ }
55322
+ }
55275
55323
  const q3 = findInnerQuoteIssue("(");
55276
55324
  if (q3?.kind === "escaped") {
55277
55325
  return { line, column: q3.column, severity: "error", code: "FL-LABEL-ESCAPED-QUOTE", message: 'Escaped quotes (\\") in node labels are not supported by Mermaid. Use " instead.', hint: 'Prefer "He said "Hi"".', length: 2 };
@@ -55293,6 +55341,25 @@ function mapFlowchartParserError(err, text) {
55293
55341
  length: len
55294
55342
  };
55295
55343
  }
55344
+ {
55345
+ const caret0 = Math.max(0, column - 1);
55346
+ const openIdx = lineStr.lastIndexOf("{", caret0);
55347
+ if (openIdx !== -1) {
55348
+ const closeIdx = lineStr.indexOf("}", openIdx + 1);
55349
+ const seg = closeIdx !== -1 ? lineStr.slice(openIdx + 1, closeIdx) : lineStr.slice(openIdx + 1);
55350
+ if (seg.includes("(") || seg.includes(")")) {
55351
+ return {
55352
+ line,
55353
+ column,
55354
+ severity: "error",
55355
+ code: "FL-LABEL-PARENS-UNQUOTED",
55356
+ message: "Parentheses inside an unquoted label are not supported by Mermaid.",
55357
+ hint: 'Wrap the label in quotes, e.g., A["Mark (X)"] \u2014 or replace ( and ) with HTML entities: ( and ).',
55358
+ length: len
55359
+ };
55360
+ }
55361
+ }
55362
+ }
55296
55363
  const q3 = findInnerQuoteIssue("{");
55297
55364
  if (q3?.kind === "escaped") {
55298
55365
  return {
@@ -55631,6 +55698,17 @@ ${br.example}`,
55631
55698
  if (inRule("arrow") && err.name === "NoViableAltException") {
55632
55699
  return { line, column, severity: "error", code: "SE-ARROW-INVALID", message: `Invalid sequence arrow near '${found}'.`, hint: "Use ->, -->, ->>, -->>, -x, --x, -), --), <<->>, or <<-->>", length: len };
55633
55700
  }
55701
+ if ((err.name === "NoViableAltException" || err.name === "MismatchedTokenException") && tokType === "Minus") {
55702
+ return {
55703
+ line,
55704
+ column,
55705
+ severity: "error",
55706
+ code: "SE-BULLET-LINE-UNSUPPORTED",
55707
+ message: "Bullet list lines starting with '-' are not supported in sequence diagrams.",
55708
+ hint: "Wrap free\u2011form text in a note block instead, for example:\nNote over A : Item 1\nNote over A\n - Item 1\n - Item 2\nend note",
55709
+ length: len
55710
+ };
55711
+ }
55634
55712
  if (inRule("noteStmt")) {
55635
55713
  if (err.name === "MismatchedTokenException" && exp("Colon")) {
55636
55714
  return { line, column, severity: "error", code: "SE-NOTE-MALFORMED", message: "Malformed note: missing colon before the note text.", hint: "Example: Note right of Alice: Hello", length: len };
@@ -55788,6 +55866,17 @@ ${br.example}`,
55788
55866
  };
55789
55867
  }
55790
55868
  }
55869
+ if ((err.name === "NotAllInputParsedException" || err.name === "NoViableAltException") && found === "-") {
55870
+ return {
55871
+ line,
55872
+ column,
55873
+ severity: "error",
55874
+ code: "SE-BULLET-LINE-UNSUPPORTED",
55875
+ message: "Bullet list lines starting with '-' are not supported in sequence diagrams.",
55876
+ hint: "Wrap free\u2011form text in a note block, for example:\nNote over A : Item 1\nNote over A\n - Item 1\n - Item 2\nend note",
55877
+ length: len
55878
+ };
55879
+ }
55791
55880
  if ((err.name === "NoViableAltException" || err.name === "NotAllInputParsedException") && tokType === "ElseKeyword") {
55792
55881
  return { line, column, severity: "error", code: "SE-ELSE-OUTSIDE-ALT", message: "'else' is only allowed inside 'alt' blocks.", hint: "Use: alt Condition \u2026 else \u2026 end", length: len };
55793
55882
  }
@@ -55989,6 +56078,30 @@ function validateFlowchart(text, options = {}) {
55989
56078
  return errs;
55990
56079
  },
55991
56080
  postParse: (text2, tokens, _cst, prevErrors) => {
56081
+ {
56082
+ const tks = tokens;
56083
+ const firstByLine = /* @__PURE__ */ new Map();
56084
+ for (const tk of tks) {
56085
+ const ln = tk.startLine ?? 1;
56086
+ const col = tk.startColumn ?? 1;
56087
+ const prev = firstByLine.get(ln);
56088
+ if (!prev || (prev.startColumn ?? Infinity) > col)
56089
+ firstByLine.set(ln, tk);
56090
+ }
56091
+ for (const tk of tks) {
56092
+ if (tk.image === "title" && firstByLine.get(tk.startLine ?? 1) === tk) {
56093
+ prevErrors.push({
56094
+ line: tk.startLine ?? 1,
56095
+ column: tk.startColumn ?? 1,
56096
+ severity: "error",
56097
+ code: "FL-META-UNSUPPORTED",
56098
+ message: "'title' is not supported in flowcharts by the current Mermaid CLI.",
56099
+ hint: 'Use a Markdown heading above the code block, or draw a labeled node at the top (e.g., T["Dependency Relationship"]).',
56100
+ length: tk.image?.length ?? 5
56101
+ });
56102
+ }
56103
+ }
56104
+ }
55992
56105
  const escWarn = detectEscapedQuotes(tokens, {
55993
56106
  code: "FL-LABEL-ESCAPED-QUOTE",
55994
56107
  message: 'Escaped quotes (\\") in node labels are accepted by Mermaid, but using &quot; is preferred for portability.',
@@ -58407,6 +58520,13 @@ function computeFixes(text, errors, level = "safe") {
58407
58520
  const replaced = inner.split('\\"').join("&quot;");
58408
58521
  edits.push({ start: { line: e3.line, column: q1 + 2 }, end: { line: e3.line, column: q22 + 1 }, newText: replaced });
58409
58522
  continue;
58523
+ if (is("FL-META-UNSUPPORTED", e3)) {
58524
+ if (level === "all") {
58525
+ const lineText2 = lineTextAt(text, e3.line);
58526
+ edits.push({ start: { line: e3.line, column: 1 }, end: { line: e3.line + 1, column: 1 }, newText: "" });
58527
+ }
58528
+ continue;
58529
+ }
58410
58530
  }
58411
58531
  }
58412
58532
  }
@@ -58414,6 +58534,12 @@ function computeFixes(text, errors, level = "safe") {
58414
58534
  edits.push(replaceRange(text, at(e3), e3.length ?? 2, "&quot;"));
58415
58535
  continue;
58416
58536
  }
58537
+ if (is("FL-META-UNSUPPORTED", e3)) {
58538
+ if (level === "all") {
58539
+ edits.push({ start: { line: e3.line, column: 1 }, end: { line: e3.line + 1, column: 1 }, newText: "" });
58540
+ }
58541
+ continue;
58542
+ }
58417
58543
  if (is("FL-LABEL-BACKTICK", e3)) {
58418
58544
  edits.push(replaceRange(text, at(e3), e3.length ?? 1, ""));
58419
58545
  continue;
@@ -58463,6 +58589,12 @@ function computeFixes(text, errors, level = "safe") {
58463
58589
  edits.push(replaceRange(text, at(e3), e3.length ?? 1, rep));
58464
58590
  continue;
58465
58591
  }
58592
+ if (is("FL-END-WITHOUT-SUBGRAPH", e3)) {
58593
+ if (level === "all") {
58594
+ edits.push({ start: { line: e3.line, column: 1 }, end: { line: e3.line + 1, column: 1 }, newText: "" });
58595
+ }
58596
+ continue;
58597
+ }
58466
58598
  if (is("FL-LABEL-DOUBLE-IN-DOUBLE", e3)) {
58467
58599
  const lineText = lineTextAt(text, e3.line);
58468
58600
  const caret0 = Math.max(0, e3.column - 1);
@@ -58799,6 +58931,9 @@ function computeFixes(text, errors, level = "safe") {
58799
58931
  continue;
58800
58932
  }
58801
58933
  if (is("FL-QUOTE-UNCLOSED", e3)) {
58934
+ if (patchedLines.has(e3.line)) {
58935
+ continue;
58936
+ }
58802
58937
  if (level === "all") {
58803
58938
  const lineText = lineTextAt(text, e3.line);
58804
58939
  const caret0 = Math.max(0, e3.column - 1);
@@ -58873,7 +59008,7 @@ function computeFixes(text, errors, level = "safe") {
58873
59008
  newInner = ltrim + left + replacedMid + right + rtrim;
58874
59009
  } else {
58875
59010
  const replaced = inner.split("&quot;").join("\0").split('"').join("&quot;").split("\0").join("&quot;");
58876
- newInner = '"' + replaced + '"';
59011
+ newInner = replaced;
58877
59012
  }
58878
59013
  edits.push({ start: { line: e3.line, column: contentStart + 1 }, end: { line: e3.line, column: closeIdx + 1 }, newText: newInner });
58879
59014
  patchedLines.add(e3.line);
@@ -73422,7 +73557,7 @@ var init_ProbeAgent = __esm({
73422
73557
  MAX_HISTORY_MESSAGES = 100;
73423
73558
  SUPPORTED_IMAGE_EXTENSIONS = ["png", "jpg", "jpeg", "webp", "gif", "bmp", "svg"];
73424
73559
  MAX_IMAGE_FILE_SIZE = 20 * 1024 * 1024;
73425
- ProbeAgent = class {
73560
+ ProbeAgent = class _ProbeAgent {
73426
73561
  /**
73427
73562
  * Create a new ProbeAgent instance
73428
73563
  * @param {Object} options - Configuration options
@@ -74304,12 +74439,24 @@ You are working with a repository located at: ${searchDirectory}
74304
74439
  }))
74305
74440
  ];
74306
74441
  }
74307
- let currentMessages = [
74308
- { role: "system", content: systemMessage },
74309
- ...this.history,
74310
- // Include previous conversation history
74311
- userMessage
74312
- ];
74442
+ const hasSystemMessage = this.history.length > 0 && this.history[0].role === "system";
74443
+ let currentMessages;
74444
+ if (hasSystemMessage) {
74445
+ currentMessages = [
74446
+ ...this.history,
74447
+ userMessage
74448
+ ];
74449
+ if (this.debug) {
74450
+ console.log("[DEBUG] Reusing existing system message from history for cache efficiency");
74451
+ }
74452
+ } else {
74453
+ currentMessages = [
74454
+ { role: "system", content: systemMessage },
74455
+ ...this.history,
74456
+ // Include previous conversation history
74457
+ userMessage
74458
+ ];
74459
+ }
74313
74460
  let currentIteration = 0;
74314
74461
  let completionAttempted = false;
74315
74462
  let finalResult = "I was unable to complete your request due to reaching the maximum number of tool iterations.";
@@ -75104,6 +75251,116 @@ Convert your previous response content into actual JSON data that follows this s
75104
75251
  console.log(`[DEBUG] Cleared conversation history and reset counters for session ${this.sessionId}`);
75105
75252
  }
75106
75253
  }
75254
+ /**
75255
+ * Clone this agent's session to create a new agent with shared conversation history
75256
+ * @param {Object} options - Clone options
75257
+ * @param {string} [options.sessionId] - Session ID for the cloned agent (defaults to new UUID)
75258
+ * @param {boolean} [options.stripInternalMessages=true] - Remove internal messages (schema reminders, mermaid fixes, etc.)
75259
+ * @param {boolean} [options.keepSystemMessage=true] - Keep the system message in cloned history
75260
+ * @param {boolean} [options.deepCopy=true] - Deep copy messages to prevent mutations
75261
+ * @param {Object} [options.overrides] - Override any ProbeAgent constructor options
75262
+ * @returns {ProbeAgent} New agent instance with cloned history
75263
+ */
75264
+ clone(options = {}) {
75265
+ const {
75266
+ sessionId = (0, import_crypto5.randomUUID)(),
75267
+ stripInternalMessages = true,
75268
+ keepSystemMessage = true,
75269
+ deepCopy = true,
75270
+ overrides = {}
75271
+ } = options;
75272
+ let clonedHistory = deepCopy ? JSON.parse(JSON.stringify(this.history)) : [...this.history];
75273
+ if (stripInternalMessages) {
75274
+ clonedHistory = this._stripInternalMessages(clonedHistory, keepSystemMessage);
75275
+ }
75276
+ const clonedAgent = new _ProbeAgent({
75277
+ // Copy current agent's config
75278
+ customPrompt: this.customPrompt,
75279
+ promptType: this.promptType,
75280
+ allowEdit: this.allowEdit,
75281
+ path: this.allowedFolders[0],
75282
+ // Use first allowed folder as primary path
75283
+ allowedFolders: [...this.allowedFolders],
75284
+ provider: this.clientApiProvider,
75285
+ model: this.modelName,
75286
+ debug: this.debug,
75287
+ outline: this.outline,
75288
+ maxResponseTokens: this.maxResponseTokens,
75289
+ maxIterations: this.maxIterations,
75290
+ disableMermaidValidation: this.disableMermaidValidation,
75291
+ enableMcp: !!this.mcpBridge,
75292
+ mcpConfig: this.mcpConfig,
75293
+ enableBash: this.enableBash,
75294
+ bashConfig: this.bashConfig,
75295
+ storageAdapter: this.storageAdapter,
75296
+ // Override with any provided options
75297
+ sessionId,
75298
+ ...overrides
75299
+ });
75300
+ clonedAgent.history = clonedHistory;
75301
+ if (this.debug) {
75302
+ console.log(`[DEBUG] Cloned session ${this.sessionId} -> ${sessionId}`);
75303
+ console.log(`[DEBUG] Cloned ${clonedHistory.length} messages (stripInternal: ${stripInternalMessages})`);
75304
+ }
75305
+ return clonedAgent;
75306
+ }
75307
+ /**
75308
+ * Internal method to strip internal/temporary messages from history
75309
+ * Removes: schema reminders, mermaid fix prompts, tool use reminders, etc.
75310
+ * Keeps: system message, user messages, assistant responses, tool results
75311
+ * @private
75312
+ */
75313
+ _stripInternalMessages(history, keepSystemMessage = true) {
75314
+ const filtered = [];
75315
+ for (let i3 = 0; i3 < history.length; i3++) {
75316
+ const message = history[i3];
75317
+ if (message.role === "system") {
75318
+ if (keepSystemMessage) {
75319
+ filtered.push(message);
75320
+ } else if (this.debug) {
75321
+ console.log(`[DEBUG] Removing system message at index ${i3}`);
75322
+ }
75323
+ continue;
75324
+ }
75325
+ if (this._isInternalMessage(message, i3, history)) {
75326
+ if (this.debug) {
75327
+ console.log(`[DEBUG] Stripping internal message at index ${i3}: ${message.role}`);
75328
+ }
75329
+ continue;
75330
+ }
75331
+ filtered.push(message);
75332
+ }
75333
+ return filtered;
75334
+ }
75335
+ /**
75336
+ * Determine if a message is an internal/temporary message
75337
+ * @private
75338
+ */
75339
+ _isInternalMessage(message, index, history) {
75340
+ if (message.role !== "user") {
75341
+ return false;
75342
+ }
75343
+ if (!message.content) {
75344
+ return false;
75345
+ }
75346
+ const content = typeof message.content === "string" ? message.content : JSON.stringify(message.content);
75347
+ if (content.includes("IMPORTANT: A schema was provided") || content.includes("You MUST respond with data that matches this schema") || content.includes("Your response must conform to this schema:")) {
75348
+ return true;
75349
+ }
75350
+ if (content.includes("Please use one of the available tools") && content.includes("or use attempt_completion") && content.includes("Remember: Use proper XML format")) {
75351
+ return true;
75352
+ }
75353
+ if (content.includes("The mermaid diagram in your response has syntax errors") || content.includes("Please fix the mermaid syntax errors") || content.includes("Here is the corrected version:")) {
75354
+ return true;
75355
+ }
75356
+ if (content.includes("Your response does not match the expected JSON schema") || content.includes("Please provide a valid JSON response") || content.includes("Schema validation error:")) {
75357
+ return true;
75358
+ }
75359
+ if (content.includes("When using <attempt_complete>") && content.includes("this must be the ONLY content in your response")) {
75360
+ return true;
75361
+ }
75362
+ return false;
75363
+ }
75107
75364
  /**
75108
75365
  * Clean up resources (including MCP connections)
75109
75366
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@probelabs/probe",
3
- "version": "0.6.0-rc128",
3
+ "version": "0.6.0-rc129",
4
4
  "description": "Node.js wrapper for the probe code search tool",
5
5
  "main": "src/index.js",
6
6
  "module": "src/index.js",
@@ -80,7 +80,7 @@
80
80
  "@opentelemetry/sdk-node": "^0.203.0",
81
81
  "@opentelemetry/sdk-trace-base": "^1.30.0",
82
82
  "@opentelemetry/semantic-conventions": "^1.36.0",
83
- "@probelabs/maid": "^0.0.13",
83
+ "@probelabs/maid": "^0.0.15",
84
84
  "ai": "^5.0.0",
85
85
  "axios": "^1.8.3",
86
86
  "fs-extra": "^11.1.1",
@@ -1152,11 +1152,28 @@ When troubleshooting:
1152
1152
  }
1153
1153
 
1154
1154
  // Initialize conversation with existing history + new user message
1155
- let currentMessages = [
1156
- { role: 'system', content: systemMessage },
1157
- ...this.history, // Include previous conversation history
1158
- userMessage
1159
- ];
1155
+ // If history already contains a system message (from session cloning), reuse it for cache efficiency
1156
+ // Otherwise add a fresh system message
1157
+ const hasSystemMessage = this.history.length > 0 && this.history[0].role === 'system';
1158
+ let currentMessages;
1159
+
1160
+ if (hasSystemMessage) {
1161
+ // Reuse existing system message from history for cache efficiency
1162
+ currentMessages = [
1163
+ ...this.history,
1164
+ userMessage
1165
+ ];
1166
+ if (this.debug) {
1167
+ console.log('[DEBUG] Reusing existing system message from history for cache efficiency');
1168
+ }
1169
+ } else {
1170
+ // Add fresh system message (first call or empty history)
1171
+ currentMessages = [
1172
+ { role: 'system', content: systemMessage },
1173
+ ...this.history, // Include previous conversation history
1174
+ userMessage
1175
+ ];
1176
+ }
1160
1177
 
1161
1178
  let currentIteration = 0;
1162
1179
  let completionAttempted = false;
@@ -2147,6 +2164,163 @@ Convert your previous response content into actual JSON data that follows this s
2147
2164
  }
2148
2165
  }
2149
2166
 
2167
+ /**
2168
+ * Clone this agent's session to create a new agent with shared conversation history
2169
+ * @param {Object} options - Clone options
2170
+ * @param {string} [options.sessionId] - Session ID for the cloned agent (defaults to new UUID)
2171
+ * @param {boolean} [options.stripInternalMessages=true] - Remove internal messages (schema reminders, mermaid fixes, etc.)
2172
+ * @param {boolean} [options.keepSystemMessage=true] - Keep the system message in cloned history
2173
+ * @param {boolean} [options.deepCopy=true] - Deep copy messages to prevent mutations
2174
+ * @param {Object} [options.overrides] - Override any ProbeAgent constructor options
2175
+ * @returns {ProbeAgent} New agent instance with cloned history
2176
+ */
2177
+ clone(options = {}) {
2178
+ const {
2179
+ sessionId = randomUUID(),
2180
+ stripInternalMessages = true,
2181
+ keepSystemMessage = true,
2182
+ deepCopy = true,
2183
+ overrides = {}
2184
+ } = options;
2185
+
2186
+ // Clone the history
2187
+ let clonedHistory = deepCopy
2188
+ ? JSON.parse(JSON.stringify(this.history))
2189
+ : [...this.history];
2190
+
2191
+ // Strip internal messages if requested
2192
+ if (stripInternalMessages) {
2193
+ clonedHistory = this._stripInternalMessages(clonedHistory, keepSystemMessage);
2194
+ }
2195
+
2196
+ // Create new agent with same configuration
2197
+ const clonedAgent = new ProbeAgent({
2198
+ // Copy current agent's config
2199
+ customPrompt: this.customPrompt,
2200
+ promptType: this.promptType,
2201
+ allowEdit: this.allowEdit,
2202
+ path: this.allowedFolders[0], // Use first allowed folder as primary path
2203
+ allowedFolders: [...this.allowedFolders],
2204
+ provider: this.clientApiProvider,
2205
+ model: this.modelName,
2206
+ debug: this.debug,
2207
+ outline: this.outline,
2208
+ maxResponseTokens: this.maxResponseTokens,
2209
+ maxIterations: this.maxIterations,
2210
+ disableMermaidValidation: this.disableMermaidValidation,
2211
+ enableMcp: !!this.mcpBridge,
2212
+ mcpConfig: this.mcpConfig,
2213
+ enableBash: this.enableBash,
2214
+ bashConfig: this.bashConfig,
2215
+ storageAdapter: this.storageAdapter,
2216
+ // Override with any provided options
2217
+ sessionId,
2218
+ ...overrides
2219
+ });
2220
+
2221
+ // Set the cloned history directly (before initialization to avoid overwriting)
2222
+ clonedAgent.history = clonedHistory;
2223
+
2224
+ if (this.debug) {
2225
+ console.log(`[DEBUG] Cloned session ${this.sessionId} -> ${sessionId}`);
2226
+ console.log(`[DEBUG] Cloned ${clonedHistory.length} messages (stripInternal: ${stripInternalMessages})`);
2227
+ }
2228
+
2229
+ return clonedAgent;
2230
+ }
2231
+
2232
+ /**
2233
+ * Internal method to strip internal/temporary messages from history
2234
+ * Removes: schema reminders, mermaid fix prompts, tool use reminders, etc.
2235
+ * Keeps: system message, user messages, assistant responses, tool results
2236
+ * @private
2237
+ */
2238
+ _stripInternalMessages(history, keepSystemMessage = true) {
2239
+ const filtered = [];
2240
+
2241
+ for (let i = 0; i < history.length; i++) {
2242
+ const message = history[i];
2243
+
2244
+ // Handle system message
2245
+ if (message.role === 'system') {
2246
+ if (keepSystemMessage) {
2247
+ filtered.push(message);
2248
+ } else if (this.debug) {
2249
+ console.log(`[DEBUG] Removing system message at index ${i}`);
2250
+ }
2251
+ continue;
2252
+ }
2253
+
2254
+ // Check if this is an internal message that should be stripped
2255
+ if (this._isInternalMessage(message, i, history)) {
2256
+ if (this.debug) {
2257
+ console.log(`[DEBUG] Stripping internal message at index ${i}: ${message.role}`);
2258
+ }
2259
+ continue;
2260
+ }
2261
+
2262
+ // Keep this message
2263
+ filtered.push(message);
2264
+ }
2265
+
2266
+ return filtered;
2267
+ }
2268
+
2269
+ /**
2270
+ * Determine if a message is an internal/temporary message
2271
+ * @private
2272
+ */
2273
+ _isInternalMessage(message, index, history) {
2274
+ if (message.role !== 'user') {
2275
+ return false; // Only user messages can be internal reminders
2276
+ }
2277
+
2278
+ // Handle null/undefined content
2279
+ if (!message.content) {
2280
+ return false;
2281
+ }
2282
+
2283
+ const content = typeof message.content === 'string'
2284
+ ? message.content
2285
+ : JSON.stringify(message.content);
2286
+
2287
+ // Schema reminder messages
2288
+ if (content.includes('IMPORTANT: A schema was provided') ||
2289
+ content.includes('You MUST respond with data that matches this schema') ||
2290
+ content.includes('Your response must conform to this schema:')) {
2291
+ return true;
2292
+ }
2293
+
2294
+ // Tool use reminder messages
2295
+ if (content.includes('Please use one of the available tools') &&
2296
+ content.includes('or use attempt_completion') &&
2297
+ content.includes('Remember: Use proper XML format')) {
2298
+ return true;
2299
+ }
2300
+
2301
+ // Mermaid fix prompts
2302
+ if (content.includes('The mermaid diagram in your response has syntax errors') ||
2303
+ content.includes('Please fix the mermaid syntax errors') ||
2304
+ content.includes('Here is the corrected version:')) {
2305
+ return true;
2306
+ }
2307
+
2308
+ // JSON correction prompts
2309
+ if (content.includes('Your response does not match the expected JSON schema') ||
2310
+ content.includes('Please provide a valid JSON response') ||
2311
+ content.includes('Schema validation error:')) {
2312
+ return true;
2313
+ }
2314
+
2315
+ // Empty attempt_complete reminders
2316
+ if (content.includes('When using <attempt_complete>') &&
2317
+ content.includes('this must be the ONLY content in your response')) {
2318
+ return true;
2319
+ }
2320
+
2321
+ return false;
2322
+ }
2323
+
2150
2324
  /**
2151
2325
  * Clean up resources (including MCP connections)
2152
2326
  */