@probelabs/probe 0.6.0-rc125 → 0.6.0-rc127

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.
@@ -556,7 +556,7 @@ var init_BytePairEncodingCore = __esm({
556
556
  var chatEnabledModels;
557
557
  var init_modelsChatEnabled_gen = __esm({
558
558
  "node_modules/gpt-tokenizer/esm/modelsChatEnabled.gen.js"() {
559
- chatEnabledModels = ["chatgpt-4o-latest", "codex-mini-latest", "computer-use-preview", "computer-use-preview-2025-03-11", "gpt-3.5", "gpt-3.5-0301", "gpt-3.5-turbo", "gpt-3.5-turbo-0125", "gpt-3.5-turbo-0613", "gpt-3.5-turbo-1106", "gpt-3.5-turbo-16k-0613", "gpt-3.5-turbo-instruct", "gpt-4", "gpt-4-0125-preview", "gpt-4-0314", "gpt-4-0613", "gpt-4-1106-preview", "gpt-4-1106-vision-preview", "gpt-4-32k", "gpt-4-turbo", "gpt-4-turbo-2024-04-09", "gpt-4-turbo-preview", "gpt-4.1", "gpt-4.1-2025-04-14", "gpt-4.1-mini", "gpt-4.1-mini-2025-04-14", "gpt-4.1-nano", "gpt-4.1-nano-2025-04-14", "gpt-4.5-preview", "gpt-4.5-preview-2025-02-27", "gpt-4o", "gpt-4o-2024-05-13", "gpt-4o-2024-08-06", "gpt-4o-2024-11-20", "gpt-4o-audio-preview", "gpt-4o-audio-preview-2024-10-01", "gpt-4o-audio-preview-2024-12-17", "gpt-4o-audio-preview-2025-06-03", "gpt-4o-mini", "gpt-4o-mini-2024-07-18", "gpt-4o-mini-audio-preview", "gpt-4o-mini-audio-preview-2024-12-17", "gpt-4o-mini-search-preview", "gpt-4o-mini-search-preview-2025-03-11", "gpt-4o-search-preview", "gpt-4o-search-preview-2025-03-11", "o1", "o1-2024-12-17", "o1-mini", "o1-mini-2024-09-12", "o1-preview", "o1-preview-2024-09-12", "o1-pro", "o1-pro-2025-03-19", "o3", "o3-2025-04-16", "o3-mini", "o3-mini-2025-01-31", "o3-pro", "o3-pro-2025-06-10", "o4-mini", "o4-mini-2025-04-16"];
559
+ chatEnabledModels = ["chatgpt-4o-latest", "codex-mini-latest", "computer-use-preview", "computer-use-preview-2025-03-11", "gpt-3.5", "gpt-3.5-0301", "gpt-3.5-turbo", "gpt-3.5-turbo-0125", "gpt-3.5-turbo-0613", "gpt-3.5-turbo-1106", "gpt-3.5-turbo-16k-0613", "gpt-3.5-turbo-instruct", "gpt-4", "gpt-4-0125-preview", "gpt-4-0314", "gpt-4-0613", "gpt-4-1106-preview", "gpt-4-1106-vision-preview", "gpt-4-32k", "gpt-4-turbo", "gpt-4-turbo-2024-04-09", "gpt-4-turbo-preview", "gpt-4.1", "gpt-4.1-2025-04-14", "gpt-4.1-mini", "gpt-4.1-mini-2025-04-14", "gpt-4.1-nano", "gpt-4.1-nano-2025-04-14", "gpt-4.5-preview", "gpt-4.5-preview-2025-02-27", "gpt-4o", "gpt-4o-2024-05-13", "gpt-4o-2024-08-06", "gpt-4o-2024-11-20", "gpt-4o-audio-preview", "gpt-4o-audio-preview-2024-10-01", "gpt-4o-audio-preview-2024-12-17", "gpt-4o-audio-preview-2025-06-03", "gpt-4o-mini", "gpt-4o-mini-2024-07-18", "gpt-4o-mini-audio-preview", "gpt-4o-mini-audio-preview-2024-12-17", "gpt-4o-mini-search-preview", "gpt-4o-mini-search-preview-2025-03-11", "gpt-4o-search-preview", "gpt-4o-search-preview-2025-03-11", "gpt-5", "gpt-5-2025-08-07", "gpt-5-chat-latest", "gpt-5-codex", "gpt-5-mini", "gpt-5-mini-2025-08-07", "gpt-5-nano", "gpt-5-nano-2025-08-07", "gpt-5-pro", "gpt-5-pro-2025-10-06", "gpt-audio", "gpt-audio-2025-08-28", "gpt-audio-mini", "gpt-audio-mini-2025-10-06", "gpt-oss-120b", "gpt-oss-20b", "o1", "o1-2024-12-17", "o1-mini", "o1-mini-2024-09-12", "o1-preview", "o1-preview-2024-09-12", "o1-pro", "o1-pro-2025-03-19", "o3", "o3-2025-04-16", "o3-deep-research", "o3-deep-research-2025-06-26", "o3-mini", "o3-mini-2025-01-31", "o3-pro", "o3-pro-2025-06-10", "o4-mini", "o4-mini-2025-04-16", "o4-mini-deep-research", "o4-mini-deep-research-2025-06-26"];
560
560
  }
561
561
  });
562
562
 
@@ -952,8 +952,9 @@ var init_GptEncoding = __esm({
952
952
  * Returns tokens assuming the 'gpt-3.5-turbo-0301' / 'gpt-4-0314' format.
953
953
  * Based on OpenAI's guidelines: https://github.com/openai/openai-python/blob/main/chatml.md
954
954
  * Also mentioned in section 6 of this document: https://github.com/openai/openai-cookbook/blob/main/examples/How_to_count_tokens_with_tiktoken.ipynb
955
+ * @param encodeOptions Options controlling how special tokens are handled.
955
956
  */
956
- *encodeChatGenerator(chat, model = this.modelName) {
957
+ *encodeChatGenerator(chat, model = this.modelName, encodeOptions) {
957
958
  if (!model) {
958
959
  throw new Error("Model name must be provided either during initialization or passed in to the method.");
959
960
  }
@@ -979,12 +980,12 @@ var init_GptEncoding = __esm({
979
980
  if (encodedRoleSeparator.length > 0) {
980
981
  yield encodedRoleSeparator;
981
982
  }
982
- yield* this.encodeGenerator(content);
983
+ yield* this.encodeGenerator(content, encodeOptions);
983
984
  yield [chatEndToken];
984
985
  yield encodedMessageSeparator;
985
986
  }
986
987
  yield [chatStartToken];
987
- yield* this.encodeGenerator("assistant");
988
+ yield* this.encodeGenerator("assistant", encodeOptions);
988
989
  if (encodedRoleSeparator.length > 0) {
989
990
  yield encodedRoleSeparator;
990
991
  }
@@ -995,15 +996,20 @@ var init_GptEncoding = __esm({
995
996
  * Returns tokens assuming the 'gpt-3.5-turbo-0301' / 'gpt-4-0314' format.
996
997
  * Based on OpenAI's guidelines: https://github.com/openai/openai-python/blob/main/chatml.md
997
998
  * Also mentioned in section 6 of this document: https://github.com/openai/openai-cookbook/blob/main/examples/How_to_count_tokens_with_tiktoken.ipynb
999
+ * @param encodeOptions Options controlling how special tokens are handled.
998
1000
  */
999
- encodeChat(chat, model = this.modelName) {
1000
- return [...this.encodeChatGenerator(chat, model)].flat();
1001
+ encodeChat(chat, model = this.modelName, encodeOptions) {
1002
+ return [...this.encodeChatGenerator(chat, model, encodeOptions)].flat();
1001
1003
  }
1002
1004
  /**
1005
+ * Checks whether the provided input stays within the provided token limit.
1006
+ * @param input The string or chat messages to evaluate.
1007
+ * @param tokenLimit The maximum allowed number of tokens.
1008
+ * @param encodeOptions Options controlling how special tokens are handled.
1003
1009
  * @returns {false | number} false if token limit is exceeded, otherwise the number of tokens
1004
1010
  */
1005
- isWithinTokenLimit(input, tokenLimit) {
1006
- const tokenGenerator = typeof input === "string" ? this.encodeGenerator(input) : this.encodeChatGenerator(input);
1011
+ isWithinTokenLimit(input, tokenLimit, encodeOptions) {
1012
+ const tokenGenerator = typeof input === "string" ? this.encodeGenerator(input, encodeOptions) : this.encodeChatGenerator(input, void 0, encodeOptions);
1007
1013
  let count = 0;
1008
1014
  for (const tokens of tokenGenerator) {
1009
1015
  count += tokens.length;
@@ -1015,6 +1021,8 @@ var init_GptEncoding = __esm({
1015
1021
  }
1016
1022
  /**
1017
1023
  * Counts the number of tokens in the input.
1024
+ * @param input The string or chat messages to evaluate.
1025
+ * @param encodeOptions Options controlling how special tokens are handled.
1018
1026
  * @returns {number} The number of tokens.
1019
1027
  */
1020
1028
  countTokens(input, encodeOptions) {
@@ -1028,7 +1036,7 @@ var init_GptEncoding = __esm({
1028
1036
  }
1029
1037
  return this.bytePairEncodingCoreProcessor.countNative(input, specialTokenConfig.allowedSpecial);
1030
1038
  }
1031
- const tokenGenerator = this.encodeChatGenerator(input);
1039
+ const tokenGenerator = this.encodeChatGenerator(input, void 0, encodeOptions);
1032
1040
  let count = 0;
1033
1041
  for (const tokens of tokenGenerator) {
1034
1042
  count += tokens.length;
@@ -1466,6 +1474,226 @@ var init_tokenCounter = __esm({
1466
1474
  }
1467
1475
  });
1468
1476
 
1477
+ // src/agent/storage/StorageAdapter.js
1478
+ var StorageAdapter;
1479
+ var init_StorageAdapter = __esm({
1480
+ "src/agent/storage/StorageAdapter.js"() {
1481
+ "use strict";
1482
+ StorageAdapter = class {
1483
+ /**
1484
+ * Load conversation history for a session
1485
+ * @param {string} sessionId - Session identifier
1486
+ * @returns {Promise<Array<Object>>} Array of message objects with {role, content, ...}
1487
+ */
1488
+ async loadHistory(sessionId) {
1489
+ throw new Error("StorageAdapter.loadHistory() must be implemented by subclass");
1490
+ }
1491
+ /**
1492
+ * Save a message to storage
1493
+ * @param {string} sessionId - Session identifier
1494
+ * @param {Object} message - Message object { role, content, ... }
1495
+ * @returns {Promise<void>}
1496
+ */
1497
+ async saveMessage(sessionId, message) {
1498
+ throw new Error("StorageAdapter.saveMessage() must be implemented by subclass");
1499
+ }
1500
+ /**
1501
+ * Clear history for a session
1502
+ * @param {string} sessionId - Session identifier
1503
+ * @returns {Promise<void>}
1504
+ */
1505
+ async clearHistory(sessionId) {
1506
+ throw new Error("StorageAdapter.clearHistory() must be implemented by subclass");
1507
+ }
1508
+ /**
1509
+ * Get session metadata (optional)
1510
+ * @param {string} sessionId - Session identifier
1511
+ * @returns {Promise<Object|null>} Session metadata or null
1512
+ */
1513
+ async getSessionMetadata(sessionId) {
1514
+ return null;
1515
+ }
1516
+ /**
1517
+ * Update session activity timestamp (optional)
1518
+ * @param {string} sessionId - Session identifier
1519
+ * @returns {Promise<void>}
1520
+ */
1521
+ async updateSessionActivity(sessionId) {
1522
+ }
1523
+ };
1524
+ }
1525
+ });
1526
+
1527
+ // src/agent/storage/InMemoryStorageAdapter.js
1528
+ var InMemoryStorageAdapter;
1529
+ var init_InMemoryStorageAdapter = __esm({
1530
+ "src/agent/storage/InMemoryStorageAdapter.js"() {
1531
+ "use strict";
1532
+ init_StorageAdapter();
1533
+ InMemoryStorageAdapter = class extends StorageAdapter {
1534
+ constructor() {
1535
+ super();
1536
+ this.sessions = /* @__PURE__ */ new Map();
1537
+ }
1538
+ async loadHistory(sessionId) {
1539
+ const session = this.sessions.get(sessionId);
1540
+ return session ? session.messages : [];
1541
+ }
1542
+ async saveMessage(sessionId, message) {
1543
+ if (!this.sessions.has(sessionId)) {
1544
+ this.sessions.set(sessionId, {
1545
+ messages: [],
1546
+ metadata: {
1547
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
1548
+ lastActivity: (/* @__PURE__ */ new Date()).toISOString()
1549
+ }
1550
+ });
1551
+ }
1552
+ const session = this.sessions.get(sessionId);
1553
+ session.messages.push(message);
1554
+ session.metadata.lastActivity = (/* @__PURE__ */ new Date()).toISOString();
1555
+ }
1556
+ async clearHistory(sessionId) {
1557
+ this.sessions.delete(sessionId);
1558
+ }
1559
+ async getSessionMetadata(sessionId) {
1560
+ const session = this.sessions.get(sessionId);
1561
+ return session ? session.metadata : null;
1562
+ }
1563
+ async updateSessionActivity(sessionId) {
1564
+ const session = this.sessions.get(sessionId);
1565
+ if (session) {
1566
+ session.metadata.lastActivity = (/* @__PURE__ */ new Date()).toISOString();
1567
+ }
1568
+ }
1569
+ };
1570
+ }
1571
+ });
1572
+
1573
+ // src/agent/hooks/HookManager.js
1574
+ var HookManager, HOOK_TYPES;
1575
+ var init_HookManager = __esm({
1576
+ "src/agent/hooks/HookManager.js"() {
1577
+ "use strict";
1578
+ HookManager = class {
1579
+ constructor() {
1580
+ this.hooks = /* @__PURE__ */ new Map();
1581
+ }
1582
+ /**
1583
+ * Register a hook callback
1584
+ * @param {string} hookName - Name of the hook
1585
+ * @param {Function} callback - Callback function
1586
+ * @returns {Function} Unregister function
1587
+ */
1588
+ on(hookName, callback) {
1589
+ if (!this.hooks.has(hookName)) {
1590
+ this.hooks.set(hookName, /* @__PURE__ */ new Set());
1591
+ }
1592
+ this.hooks.get(hookName).add(callback);
1593
+ return () => this.off(hookName, callback);
1594
+ }
1595
+ /**
1596
+ * Register a one-time hook callback
1597
+ * @param {string} hookName - Name of the hook
1598
+ * @param {Function} callback - Callback function
1599
+ * @returns {Function} Unregister function
1600
+ */
1601
+ once(hookName, callback) {
1602
+ const wrappedCallback = async (data) => {
1603
+ this.off(hookName, wrappedCallback);
1604
+ await callback(data);
1605
+ };
1606
+ return this.on(hookName, wrappedCallback);
1607
+ }
1608
+ /**
1609
+ * Unregister a hook callback
1610
+ * @param {string} hookName - Name of the hook
1611
+ * @param {Function} callback - Callback function
1612
+ */
1613
+ off(hookName, callback) {
1614
+ const callbacks = this.hooks.get(hookName);
1615
+ if (callbacks) {
1616
+ callbacks.delete(callback);
1617
+ }
1618
+ }
1619
+ /**
1620
+ * Emit a hook event
1621
+ * @param {string} hookName - Name of the hook
1622
+ * @param {any} data - Data to pass to callbacks
1623
+ * @returns {Promise<void>}
1624
+ */
1625
+ async emit(hookName, data) {
1626
+ const callbacks = this.hooks.get(hookName);
1627
+ if (!callbacks || callbacks.size === 0) return;
1628
+ const promises = Array.from(callbacks).map((callback) => {
1629
+ try {
1630
+ return Promise.resolve(callback(data));
1631
+ } catch (error) {
1632
+ return Promise.reject(error);
1633
+ }
1634
+ });
1635
+ const results = await Promise.allSettled(promises);
1636
+ results.forEach((result, index) => {
1637
+ if (result.status === "rejected") {
1638
+ console.error(`[HookManager] Error in hook "${hookName}" (callback ${index + 1}):`, result.reason);
1639
+ }
1640
+ });
1641
+ }
1642
+ /**
1643
+ * Clear all hooks or hooks for a specific event
1644
+ * @param {string} [hookName] - Optional hook name to clear
1645
+ */
1646
+ clear(hookName) {
1647
+ if (hookName) {
1648
+ this.hooks.delete(hookName);
1649
+ } else {
1650
+ this.hooks.clear();
1651
+ }
1652
+ }
1653
+ /**
1654
+ * Get list of registered hook names
1655
+ * @returns {string[]} Array of hook names
1656
+ */
1657
+ getHookNames() {
1658
+ return Array.from(this.hooks.keys());
1659
+ }
1660
+ /**
1661
+ * Get number of callbacks for a hook
1662
+ * @param {string} hookName - Name of the hook
1663
+ * @returns {number} Number of callbacks
1664
+ */
1665
+ getCallbackCount(hookName) {
1666
+ const callbacks = this.hooks.get(hookName);
1667
+ return callbacks ? callbacks.size : 0;
1668
+ }
1669
+ };
1670
+ HOOK_TYPES = {
1671
+ // Lifecycle hooks
1672
+ AGENT_INITIALIZED: "agent:initialized",
1673
+ AGENT_CLEANUP: "agent:cleanup",
1674
+ // Message hooks
1675
+ MESSAGE_USER: "message:user",
1676
+ MESSAGE_ASSISTANT: "message:assistant",
1677
+ MESSAGE_SYSTEM: "message:system",
1678
+ // Tool execution hooks
1679
+ TOOL_START: "tool:start",
1680
+ TOOL_END: "tool:end",
1681
+ TOOL_ERROR: "tool:error",
1682
+ // AI streaming hooks
1683
+ AI_STREAM_START: "ai:stream:start",
1684
+ AI_STREAM_DELTA: "ai:stream:delta",
1685
+ AI_STREAM_END: "ai:stream:end",
1686
+ // Storage hooks
1687
+ STORAGE_LOAD: "storage:load",
1688
+ STORAGE_SAVE: "storage:save",
1689
+ STORAGE_CLEAR: "storage:clear",
1690
+ // Iteration hooks
1691
+ ITERATION_START: "iteration:start",
1692
+ ITERATION_END: "iteration:end"
1693
+ };
1694
+ }
1695
+ });
1696
+
1469
1697
  // src/directory-resolver.js
1470
1698
  import path from "path";
1471
1699
  import os from "os";
@@ -16121,6 +16349,23 @@ var init_probeTool = __esm({
16121
16349
  }
16122
16350
  });
16123
16351
 
16352
+ // src/agent/storage/index.js
16353
+ var init_storage = __esm({
16354
+ "src/agent/storage/index.js"() {
16355
+ "use strict";
16356
+ init_StorageAdapter();
16357
+ init_InMemoryStorageAdapter();
16358
+ }
16359
+ });
16360
+
16361
+ // src/agent/hooks/index.js
16362
+ var init_hooks = __esm({
16363
+ "src/agent/hooks/index.js"() {
16364
+ "use strict";
16365
+ init_HookManager();
16366
+ }
16367
+ });
16368
+
16124
16369
  // src/index.js
16125
16370
  var init_index = __esm({
16126
16371
  "src/index.js"() {
@@ -16140,6 +16385,8 @@ var init_index = __esm({
16140
16385
  init_ProbeAgent();
16141
16386
  init_simpleTelemetry();
16142
16387
  init_probeTool();
16388
+ init_storage();
16389
+ init_hooks();
16143
16390
  }
16144
16391
  });
16145
16392
 
@@ -28104,7 +28351,6 @@ var init_parser2 = __esm({
28104
28351
  this.linkStyleStatement = this.RULE("linkStyleStatement", () => {
28105
28352
  this.CONSUME(LinkStyleKeyword);
28106
28353
  this.SUBRULE(this.linkStyleIndexList);
28107
- this.OPTION1(() => this.CONSUME1(Newline));
28108
28354
  this.SUBRULE(this.linkStylePairs);
28109
28355
  this.OPTION2(() => this.CONSUME2(Newline));
28110
28356
  });
@@ -28568,7 +28814,7 @@ var init_semantics = __esm({
28568
28814
  }
28569
28815
  const fnTok = ch.fn?.[0];
28570
28816
  if (!fnTok) {
28571
- this.ctx.errors.push({ line: modeTok2?.startLine ?? 1, column: modeTok2?.startColumn ?? 1, severity: "error", code: "FL-CLICK-CALL-NAME-MISSING", message: "'click \u2026 call' requires a function name.", hint: 'Example: click A call doThing() "Tooltip"' });
28817
+ this.ctx.errors.push({ line: modeTok2?.startLine ?? 1, column: modeTok2?.startColumn ?? 1, severity: "error", code: "FL-CLICK-CALL-NAME-MISSING", message: "'click \u2026 call' requires a function name.", hint: "Example: click A call doThing()" });
28572
28818
  }
28573
28819
  const tipTok = ch.tooltip?.[0];
28574
28820
  if (tipTok) {
@@ -28625,7 +28871,7 @@ var init_semantics = __esm({
28625
28871
  severity: "error",
28626
28872
  code: "FL-CLICK-CALL-NAME-MISSING",
28627
28873
  message: "'click \u2026 call' requires a function name.",
28628
- hint: 'Example: click A call doThing() "Tooltip"'
28874
+ hint: "Example: click A call doThing()"
28629
28875
  });
28630
28876
  }
28631
28877
  return;
@@ -28706,6 +28952,8 @@ var init_semantics = __esm({
28706
28952
  const linksHere = Array.isArray(ctx.link) ? ctx.link.length : 0;
28707
28953
  if (linksHere > 0)
28708
28954
  this.edgeCount += linksHere;
28955
+ if (ctx.link)
28956
+ ctx.link.forEach((ln) => this.visit(ln));
28709
28957
  }
28710
28958
  // Edge attribute object statements must target a known edge id
28711
28959
  // Edge attribute statements are parsed as nodeStatements with a typed attrObject and no links.
@@ -28765,7 +29013,7 @@ var init_semantics = __esm({
28765
29013
  severity: "error",
28766
29014
  code: "FL-TYPED-SHAPE-UNKNOWN",
28767
29015
  message: `Unknown shape '${v}' in '@{ shape: \u2026 }'.`,
28768
- hint: "Use one of: rect, round, stadium, subroutine, circle, cylinder, diamond, trapezoid, parallelogram, hexagon, lean-l, lean-r, icon, image"
29016
+ hint: 'Use one of: rect, rounded, stadium, subroutine, circle, cylinder, diamond, trapezoid, parallelogram, hexagon, "lean-l", "lean-r", icon, image'
28769
29017
  });
28770
29018
  } else {
28771
29019
  const supportedByCli = /* @__PURE__ */ new Set(["rect", "round", "rounded", "diamond", "circle", "cylinder", "stadium", "subroutine", "lean-l", "lean-r"]);
@@ -28967,8 +29215,8 @@ var init_semantics = __esm({
28967
29215
  column: t.startColumn ?? 1,
28968
29216
  severity: "error",
28969
29217
  code: "FL-LABEL-PARENS-UNQUOTED",
28970
- message: "Parentheses inside an unquoted label are not supported by Mermaid. Wrap the label in quotes.",
28971
- hint: 'Example: A["Calls func(arg)"]'
29218
+ message: "Parentheses inside an unquoted label are not supported by Mermaid.",
29219
+ hint: 'Wrap the label in quotes, e.g., A["Mark (X)"] \u2014 or replace ( and ) with HTML entities: &#40; and &#41;'
28972
29220
  });
28973
29221
  }
28974
29222
  }
@@ -29016,6 +29264,29 @@ var init_semantics = __esm({
29016
29264
  }
29017
29265
  }
29018
29266
  }
29267
+ // Validate link specifics that Mermaid CLI enforces but our grammar may parse loosely
29268
+ link(ctx) {
29269
+ const inline = ctx.linkTextInline?.[0];
29270
+ if (inline) {
29271
+ const ch = inline.children || {};
29272
+ const parts = [].concat(ch.Identifier || []).concat(ch.Text || []).concat(ch.NumberLiteral || []).concat(ch.Pipe || []);
29273
+ if (parts.length) {
29274
+ const raw = parts.map((t) => String(t.image || "")).join("").trim();
29275
+ if (raw === "x" || raw === "o") {
29276
+ const p = parts[0];
29277
+ this.ctx.errors.push({
29278
+ line: p.startLine ?? 1,
29279
+ column: p.startColumn ?? 1,
29280
+ severity: "error",
29281
+ code: "FL-LINK-UNSUPPORTED-MARKER",
29282
+ message: `Unsupported one-sided link marker '${raw}'. Use symmetric '${raw}--${raw}' or a plain arrow with a label.`,
29283
+ hint: raw === "x" ? "Example: A x--x B (or) A --> B and label it: A --|Skipped|--> B" : "Example: A o--o B (or) A --> B",
29284
+ length: p.image?.length ?? 1
29285
+ });
29286
+ }
29287
+ }
29288
+ }
29289
+ }
29019
29290
  checkBackticksInContent(contentNodes) {
29020
29291
  if (!contentNodes)
29021
29292
  return;
@@ -29367,6 +29638,31 @@ function mapFlowchartParserError(err, text) {
29367
29638
  }
29368
29639
  }
29369
29640
  }
29641
+ if (isInRule(err, "linkStylePairs") && tokType === "Newline") {
29642
+ const nextLine = Math.min(allLines.length, line + 1);
29643
+ const nxt = allLines[nextLine - 1] || "";
29644
+ const first2 = (nxt.match(/\S/) || { index: 0 }).index || 0;
29645
+ return {
29646
+ line: nextLine,
29647
+ column: Math.max(1, first2 + 1),
29648
+ severity: "error",
29649
+ code: "FL-LINKSTYLE-MULTILINE",
29650
+ message: "'linkStyle' styles must be on the same line as the indices.",
29651
+ hint: "Example: linkStyle 0,1 stroke:#f00,stroke-width:2px",
29652
+ length: 1
29653
+ };
29654
+ }
29655
+ if ((isInRule(err, "linkStyleIndexList") || isInRule(err, "linkStyleStatement")) && tokType === "Colon") {
29656
+ return {
29657
+ line,
29658
+ column,
29659
+ severity: "error",
29660
+ code: "FL-LINKSTYLE-RANGE-NOT-SUPPORTED",
29661
+ message: "Ranges in 'linkStyle' indices are not supported. Use comma-separated indices.",
29662
+ hint: "Example: linkStyle 0,1 stroke:#f00,stroke-width:2px",
29663
+ length: len
29664
+ };
29665
+ }
29370
29666
  if (tokType === "QuotedString") {
29371
29667
  const context = err?.context;
29372
29668
  const inLinkRule = context?.ruleStack?.includes("linkTextInline") || context?.ruleStack?.includes("link") || false;
@@ -29413,6 +29709,17 @@ function mapFlowchartParserError(err, text) {
29413
29709
  }
29414
29710
  if (isInRule(err, "nodeShape") && err.name === "MismatchedTokenException") {
29415
29711
  if (expecting(err, "SquareClose")) {
29712
+ if (tokType === "RoundOpen" || tokType === "RoundClose") {
29713
+ return {
29714
+ line,
29715
+ column,
29716
+ severity: "error",
29717
+ code: "FL-LABEL-PARENS-UNQUOTED",
29718
+ message: "Parentheses inside an unquoted label are not supported by Mermaid.",
29719
+ hint: 'Wrap the label in quotes, e.g., A["Mark (X)"] \u2014 or replace ( and ) with HTML entities: &#40; and &#41;.',
29720
+ length: len
29721
+ };
29722
+ }
29416
29723
  if (tokType === "QuotedString") {
29417
29724
  return {
29418
29725
  line,
@@ -29908,6 +30215,32 @@ ${br.example}`,
29908
30215
  break;
29909
30216
  }
29910
30217
  }
30218
+ if (blk.label === "box" && openIdx !== -1) {
30219
+ let endIdx = -1;
30220
+ for (let i = openIdx + 1; i < lines2.length; i++) {
30221
+ const raw = lines2[i] || "";
30222
+ const ind = raw.match(/^(\s*)/)?.[1] || "";
30223
+ if (/^\s*end\s*$/.test(raw) && ind.length <= openIndent.length) {
30224
+ endIdx = i;
30225
+ break;
30226
+ }
30227
+ }
30228
+ if (endIdx !== -1) {
30229
+ const body = lines2.slice(openIdx + 1, endIdx).map((s) => (s || "").trim());
30230
+ const hasMsgWithActivation = body.some((s) => /->/.test(s) && /[+-]/.test(s));
30231
+ if (hasMsgWithActivation) {
30232
+ return {
30233
+ line: openIdx + 1,
30234
+ column: 1,
30235
+ severity: "error",
30236
+ code: "SE-BOX-EMPTY",
30237
+ message: "Box block has no participant/actor declarations. Use 'rect' to group messages visually.",
30238
+ hint: "Replace 'box' with 'rect' if you want to group messages:\nrect rgb(240, 240, 255)\n A->>B: Message\n Note over A: Info\nend",
30239
+ length: 3
30240
+ };
30241
+ }
30242
+ }
30243
+ }
29911
30244
  let caretLine = line;
29912
30245
  if (openIdx !== -1) {
29913
30246
  caretLine = lines2.length;
@@ -31247,7 +31580,7 @@ var init_validate3 = __esm({
31247
31580
  function tokenize4(text) {
31248
31581
  return ClassLexer.tokenize(text);
31249
31582
  }
31250
- var Identifier3, NumberLiteral4, ClassDiagramKeyword, DirectionKw, Direction2, ClassKw, AsKw, NoteKw, ForKw, RelCompToAgg, RelAggToComp, RelCompBoth, RelAggBoth, LollipopLeft, LollipopRight, RelExtends, RelComposition, RelAggregation, RelDependency, RelRealization, RelAssociation, RelDependencyLeft, RelRealizationLeft, RelExtendsRight, InvalidRelArrow, LCurly, RCurly, LParen2, RParen2, Colon4, Comma3, Visibility, LTlt, GTgt, GenericAngle, QuotedString4, BacktickName, SquareOpen2, SquareClose2, Comment4, WhiteSpace4, Newline4, allTokens4, ClassLexer;
31583
+ var Identifier3, NumberLiteral4, ClassDiagramKeyword, DirectionKw, Direction2, TitleKw, NamespaceKw, ClassKw, InterfaceKw, AsKw, NoteKw, ForKw, RelCompToAgg, RelAggToComp, RelCompBoth, RelAggBoth, LollipopLeft, LollipopRight, RelExtends, RelComposition, RelAggregation, RelDependency, RelRealization, RelAssociation, RelDependencyLeft, RelRealizationLeft, RelExtendsRight, InvalidRelArrow, LCurly, RCurly, LParen2, RParen2, Colon4, Comma3, Visibility, LTlt, GTgt, GenericAngle, QuotedString4, BacktickName, SquareOpen2, SquareClose2, Comment4, WhiteSpace4, Newline4, allTokens4, ClassLexer;
31251
31584
  var init_lexer5 = __esm({
31252
31585
  "node_modules/@probelabs/maid/out/diagrams/class/lexer.js"() {
31253
31586
  init_api5();
@@ -31256,7 +31589,10 @@ var init_lexer5 = __esm({
31256
31589
  ClassDiagramKeyword = createToken({ name: "ClassDiagramKeyword", pattern: /classDiagram/, longer_alt: Identifier3 });
31257
31590
  DirectionKw = createToken({ name: "DirectionKw", pattern: /direction/, longer_alt: Identifier3 });
31258
31591
  Direction2 = createToken({ name: "Direction", pattern: /LR|RL|TB|BT|TD/, longer_alt: Identifier3 });
31592
+ TitleKw = createToken({ name: "TitleKw", pattern: /title/, longer_alt: Identifier3 });
31593
+ NamespaceKw = createToken({ name: "NamespaceKw", pattern: /namespace/, longer_alt: Identifier3 });
31259
31594
  ClassKw = createToken({ name: "ClassKw", pattern: /class/, longer_alt: Identifier3 });
31595
+ InterfaceKw = createToken({ name: "InterfaceKw", pattern: /interface\b/, longer_alt: Identifier3 });
31260
31596
  AsKw = createToken({ name: "AsKw", pattern: /as/, longer_alt: Identifier3 });
31261
31597
  NoteKw = createToken({ name: "NoteKw", pattern: /note/, longer_alt: Identifier3 });
31262
31598
  ForKw = createToken({ name: "ForKw", pattern: /for/, longer_alt: Identifier3 });
@@ -31299,6 +31635,9 @@ var init_lexer5 = __esm({
31299
31635
  // Keywords
31300
31636
  ClassDiagramKeyword,
31301
31637
  DirectionKw,
31638
+ TitleKw,
31639
+ NamespaceKw,
31640
+ InterfaceKw,
31302
31641
  ClassKw,
31303
31642
  AsKw,
31304
31643
  NoteKw,
@@ -31367,8 +31706,11 @@ var init_parser5 = __esm({
31367
31706
  });
31368
31707
  this.statement = this.RULE("statement", () => {
31369
31708
  this.OR([
31709
+ { ALT: () => this.SUBRULE(this.titleStmt) },
31370
31710
  { ALT: () => this.SUBRULE(this.directionStmt) },
31711
+ { ALT: () => this.SUBRULE(this.namespaceStmt) },
31371
31712
  { ALT: () => this.SUBRULE(this.classLine) },
31713
+ { ALT: () => this.SUBRULE(this.interfaceLine) },
31372
31714
  { ALT: () => this.SUBRULE(this.relationStmt) },
31373
31715
  { ALT: () => this.SUBRULE(this.noteStmt) },
31374
31716
  { ALT: () => this.SUBRULE(this.memberAssignStmt) },
@@ -31380,6 +31722,43 @@ var init_parser5 = __esm({
31380
31722
  this.CONSUME(Direction2);
31381
31723
  this.OPTION(() => this.CONSUME(Newline4));
31382
31724
  });
31725
+ this.titleStmt = this.RULE("titleStmt", () => {
31726
+ this.CONSUME(TitleKw);
31727
+ this.OR([
31728
+ { ALT: () => this.CONSUME(QuotedString4) },
31729
+ {
31730
+ ALT: () => {
31731
+ this.AT_LEAST_ONE(() => {
31732
+ this.OR2([
31733
+ { ALT: () => this.CONSUME(Identifier3) },
31734
+ { ALT: () => this.CONSUME(NumberLiteral4) }
31735
+ ]);
31736
+ });
31737
+ }
31738
+ }
31739
+ ]);
31740
+ this.OPTION(() => this.CONSUME(Newline4));
31741
+ });
31742
+ this.namespaceStmt = this.RULE("namespaceStmt", () => {
31743
+ this.CONSUME(NamespaceKw);
31744
+ this.OR([
31745
+ { ALT: () => this.CONSUME(QuotedString4) },
31746
+ { ALT: () => this.CONSUME(Identifier3) }
31747
+ ]);
31748
+ this.CONSUME(LCurly);
31749
+ this.MANY(() => {
31750
+ this.OR2([
31751
+ { ALT: () => this.CONSUME(Newline4) },
31752
+ { ALT: () => this.SUBRULE(this.classLine) },
31753
+ { ALT: () => this.SUBRULE(this.interfaceLine) },
31754
+ { ALT: () => this.SUBRULE(this.relationStmt) },
31755
+ { ALT: () => this.SUBRULE(this.noteStmt) },
31756
+ { ALT: () => this.SUBRULE(this.memberAssignStmt) }
31757
+ ]);
31758
+ });
31759
+ this.CONSUME(RCurly);
31760
+ this.OPTION(() => this.CONSUME2(Newline4));
31761
+ });
31383
31762
  this.classLine = this.RULE("classLine", () => {
31384
31763
  this.CONSUME(ClassKw);
31385
31764
  this.SUBRULE(this.classRef);
@@ -31418,6 +31797,44 @@ var init_parser5 = __esm({
31418
31797
  }
31419
31798
  ]);
31420
31799
  });
31800
+ this.interfaceLine = this.RULE("interfaceLine", () => {
31801
+ this.CONSUME(InterfaceKw);
31802
+ this.SUBRULE(this.classRef);
31803
+ this.OR([
31804
+ {
31805
+ ALT: () => {
31806
+ this.CONSUME(LCurly);
31807
+ this.MANY(() => {
31808
+ this.OR2([
31809
+ { ALT: () => this.CONSUME3(Newline4) },
31810
+ { ALT: () => this.SUBRULE(this.memberLineStmt) }
31811
+ ]);
31812
+ });
31813
+ this.CONSUME(RCurly);
31814
+ this.OPTION(() => this.CONSUME(Newline4));
31815
+ }
31816
+ },
31817
+ {
31818
+ ALT: () => {
31819
+ this.OPTION1(() => {
31820
+ this.CONSUME(SquareOpen2);
31821
+ this.CONSUME(QuotedString4);
31822
+ this.CONSUME(SquareClose2);
31823
+ });
31824
+ this.OPTION2(() => {
31825
+ this.CONSUME(LTlt);
31826
+ this.CONSUME2(Identifier3);
31827
+ this.CONSUME(GTgt);
31828
+ });
31829
+ this.OPTION3(() => {
31830
+ this.CONSUME(AsKw);
31831
+ this.CONSUME3(Identifier3);
31832
+ });
31833
+ this.OPTION4(() => this.CONSUME2(Newline4));
31834
+ }
31835
+ }
31836
+ ]);
31837
+ });
31421
31838
  this.memberAssignStmt = this.RULE("memberAssignStmt", () => {
31422
31839
  this.SUBRULE(this.classRef);
31423
31840
  this.CONSUME(Colon4);
@@ -31587,6 +32004,31 @@ function validateClass(text, _options = {}) {
31587
32004
  length: b.image?.length ?? 1
31588
32005
  });
31589
32006
  }
32007
+ if (a.tokenType === NamespaceKw && b.tokenType === QuotedString4) {
32008
+ errs.push({
32009
+ line: b.startLine ?? 1,
32010
+ column: b.startColumn ?? 1,
32011
+ severity: "error",
32012
+ code: "CL-NAMESPACE-NAME-QUOTED",
32013
+ message: "Quoted namespace names are not supported by mermaid.js. Use an unquoted identifier.",
32014
+ hint: 'Change: namespace "ProbeAgent Core" { ... } \u2192 namespace ProbeAgentCore { ... }',
32015
+ length: b.image?.length ?? 1
32016
+ });
32017
+ }
32018
+ if (a.tokenType === InterfaceKw) {
32019
+ const prevToken = i > 0 ? tokList[i - 1] : null;
32020
+ if (!prevToken || prevToken.tokenType !== LTlt) {
32021
+ errs.push({
32022
+ line: a.startLine ?? 1,
32023
+ column: a.startColumn ?? 1,
32024
+ severity: "error",
32025
+ code: "CL-INTERFACE-KEYWORD-UNSUPPORTED",
32026
+ message: 'The "interface" keyword is not supported by mermaid.js. Use the <<interface>> annotation instead.',
32027
+ hint: 'Auto-fix converts to "class". Then manually add: <<interface>> ClassName after the class definition',
32028
+ length: a.image?.length ?? 9
32029
+ });
32030
+ }
32031
+ }
31590
32032
  }
31591
32033
  for (const tk of tokList) {
31592
32034
  if (tk.tokenType === InvalidRelArrow) {
@@ -31643,10 +32085,13 @@ function validateClass(text, _options = {}) {
31643
32085
  const has2 = (code, line) => (prev || []).some((e) => e.code === code && e.line === line && e.severity === "error");
31644
32086
  const lines = src.split(/\r?\n/);
31645
32087
  const classDeclOpen = [];
32088
+ const namespaceDeclOpen = [];
31646
32089
  for (let i = 0; i < lines.length; i++) {
31647
32090
  const raw = lines[i] || "";
31648
32091
  if (/^\s*class\b.*\{\s*$/.test(raw))
31649
32092
  classDeclOpen.push(i + 1);
32093
+ if (/^\s*namespace\b.*\{\s*$/.test(raw))
32094
+ namespaceDeclOpen.push(i + 1);
31650
32095
  }
31651
32096
  if (classDeclOpen.length > 0) {
31652
32097
  const hasClose = lines.some((l) => /\}/.test(l));
@@ -31655,6 +32100,13 @@ function validateClass(text, _options = {}) {
31655
32100
  errors.push({ line: Math.max(1, lines.length), column: 1, severity: "error", code: "CL-BLOCK-MISSING-RBRACE", message: "Missing '}' to close class block.", hint: "Close the block: class Foo { ... }" });
31656
32101
  }
31657
32102
  }
32103
+ if (namespaceDeclOpen.length > 0) {
32104
+ const hasClose = lines.some((l) => /\}/.test(l));
32105
+ if (!hasClose && !has2("CL-NAMESPACE-MISSING-RBRACE", Math.max(1, lines.length))) {
32106
+ const last2 = namespaceDeclOpen[namespaceDeclOpen.length - 1];
32107
+ errors.push({ line: Math.max(1, lines.length), column: 1, severity: "error", code: "CL-NAMESPACE-MISSING-RBRACE", message: "Missing '}' to close namespace block.", hint: 'Close the block: namespace "Name" { ... }' });
32108
+ }
32109
+ }
31658
32110
  return errors;
31659
32111
  }
31660
32112
  });
@@ -32309,6 +32761,10 @@ function computeFixes(text, errors, level = "safe") {
32309
32761
  edits.push(replaceRange(text, at(e), e.length ?? 2, "-->"));
32310
32762
  continue;
32311
32763
  }
32764
+ if (is("FL-LINK-UNSUPPORTED-MARKER", e)) {
32765
+ edits.push(replaceRange(text, at(e), e.length ?? 1, ""));
32766
+ continue;
32767
+ }
32312
32768
  if (is("FL-EDGE-LABEL-QUOTED", e)) {
32313
32769
  const lineText = lineTextAt(text, e.line);
32314
32770
  const col = Math.max(0, e.column - 1);
@@ -32381,6 +32837,29 @@ function computeFixes(text, errors, level = "safe") {
32381
32837
  }
32382
32838
  continue;
32383
32839
  }
32840
+ if (is("CL-NAMESPACE-NAME-QUOTED", e)) {
32841
+ const lineText = lineTextAt(text, e.line);
32842
+ const nsIdx = lineText.indexOf("namespace");
32843
+ const startSearch = nsIdx >= 0 ? nsIdx + 9 : 0;
32844
+ const q1 = lineText.indexOf('"', startSearch);
32845
+ if (q1 !== -1) {
32846
+ const q2 = lineText.indexOf('"', q1 + 1);
32847
+ if (q2 > q1) {
32848
+ const namespaceName = lineText.slice(q1 + 1, q2);
32849
+ const validIdentifier = namespaceName.replace(/[^A-Za-z0-9_]/g, "");
32850
+ edits.push(replaceRange(text, { line: e.line, column: q1 + 1 }, q2 - q1 + 1, validIdentifier));
32851
+ }
32852
+ }
32853
+ continue;
32854
+ }
32855
+ if (is("CL-INTERFACE-KEYWORD-UNSUPPORTED", e)) {
32856
+ const lineText = lineTextAt(text, e.line);
32857
+ const ifIdx = lineText.indexOf("interface");
32858
+ if (ifIdx !== -1) {
32859
+ edits.push(replaceRange(text, { line: e.line, column: ifIdx + 1 }, 9, "class"));
32860
+ }
32861
+ continue;
32862
+ }
32384
32863
  if (is("FL-LABEL-ESCAPED-QUOTE", e)) {
32385
32864
  const lineText = lineTextAt(text, e.line);
32386
32865
  const caret0 = Math.max(0, e.column - 1);
@@ -32528,6 +33007,42 @@ function computeFixes(text, errors, level = "safe") {
32528
33007
  const msg = e.message || "";
32529
33008
  const lineText = lineTextAt(text, e.line);
32530
33009
  const caret0 = Math.max(0, e.column - 1);
33010
+ {
33011
+ const openIdx = lineText.indexOf("([");
33012
+ if (openIdx !== -1) {
33013
+ const badClose = lineText.indexOf("})", openIdx + 2);
33014
+ if (badClose !== -1) {
33015
+ edits.push({ start: { line: e.line, column: badClose + 1 }, end: { line: e.line, column: badClose + 2 }, newText: "]" });
33016
+ patchedLines.add(e.line);
33017
+ continue;
33018
+ }
33019
+ }
33020
+ }
33021
+ {
33022
+ const openIdx = lineText.indexOf("[(");
33023
+ if (openIdx !== -1) {
33024
+ const closePair = lineText.indexOf("))", openIdx + 2);
33025
+ if (closePair !== -1) {
33026
+ edits.push({ start: { line: e.line, column: closePair + 2 }, end: { line: e.line, column: closePair + 3 }, newText: "]" });
33027
+ patchedLines.add(e.line);
33028
+ continue;
33029
+ }
33030
+ }
33031
+ }
33032
+ {
33033
+ const openPair = lineText.indexOf("([");
33034
+ if (openPair !== -1) {
33035
+ const badClose = lineText.indexOf("})", openPair + 2);
33036
+ if (badClose !== -1) {
33037
+ edits.push({ start: { line: e.line, column: badClose + 1 }, end: { line: e.line, column: badClose + 2 }, newText: "]" });
33038
+ continue;
33039
+ }
33040
+ }
33041
+ }
33042
+ if (/\(\[/.test(lineText) && lineText.indexOf("})", Math.max(0, caret0 - 1)) !== -1) {
33043
+ edits.push(replaceRange(text, at(e), e.length ?? 1, "]"));
33044
+ continue;
33045
+ }
32531
33046
  if (msg.includes("opened '('") && msg.includes("closed with ']'")) {
32532
33047
  const openIdx = lineText.lastIndexOf("(", caret0);
32533
33048
  if (openIdx !== -1) {
@@ -32600,7 +33115,36 @@ function computeFixes(text, errors, level = "safe") {
32600
33115
  const caret0 = Math.max(0, e.column - 1);
32601
33116
  const msg = e.message || "";
32602
33117
  const bracketMatch = msg.match(/Unclosed '(.+?)'/);
32603
- const expectedOpener = bracketMatch ? bracketMatch[1] : null;
33118
+ const expectedOpener = bracketMatch ? (bracketMatch[1] || "").trim() : null;
33119
+ if (expectedOpener === "((") {
33120
+ if (level === "all") {
33121
+ const openIdx = lineText.lastIndexOf("((", caret0);
33122
+ if (openIdx !== -1) {
33123
+ const contentStart = openIdx + 2;
33124
+ const picks = [];
33125
+ const pushIdx2 = (i) => {
33126
+ if (i >= 0)
33127
+ picks.push(i);
33128
+ };
33129
+ pushIdx2(lineText.indexOf("-", contentStart));
33130
+ pushIdx2(lineText.indexOf("=", contentStart));
33131
+ pushIdx2(lineText.indexOf(".", contentStart));
33132
+ pushIdx2(lineText.indexOf("|", contentStart));
33133
+ let insertIdx = picks.length ? Math.min(...picks) : lineText.length;
33134
+ const before = lineText.slice(0, openIdx);
33135
+ const m = before.match(/([A-Za-z0-9_]+)\s*$/);
33136
+ const inferred = m ? m[1] : "";
33137
+ if (inferred) {
33138
+ edits.push({ start: { line: e.line, column: contentStart + 1 }, end: { line: e.line, column: insertIdx + 1 }, newText: inferred + "))" });
33139
+ patchedLines.add(e.line);
33140
+ continue;
33141
+ }
33142
+ patchedLines.add(e.line);
33143
+ continue;
33144
+ }
33145
+ }
33146
+ continue;
33147
+ }
32604
33148
  const bracketMap = {
32605
33149
  "[": "]",
32606
33150
  "{": "}",
@@ -32686,6 +33230,14 @@ function computeFixes(text, errors, level = "safe") {
32686
33230
  patchedLines.add(e.line);
32687
33231
  continue;
32688
33232
  }
33233
+ if (innerSeg.includes("(") || innerSeg.includes(")")) {
33234
+ const replaced = innerSeg.replace(/\(/g, "&#40;").replace(/\)/g, "&#41;");
33235
+ if (replaced !== innerSeg) {
33236
+ edits.push({ start: { line: e.line, column: opened.idx + opened.len + 1 }, end: { line: e.line, column: closerIdx + 1 }, newText: replaced });
33237
+ patchedLines.add(e.line);
33238
+ continue;
33239
+ }
33240
+ }
32689
33241
  }
32690
33242
  }
32691
33243
  if (patchedLines.has(e.line)) {
@@ -32694,9 +33246,35 @@ function computeFixes(text, errors, level = "safe") {
32694
33246
  let closer = "]";
32695
33247
  if (opened)
32696
33248
  closer = opened.close;
32697
- const avail = lineText.slice(caret0);
32698
- const replaceLen = Math.min(closer.length, Math.max(1, avail.length));
32699
- edits.push({ start: { line: e.line, column: caret0 + 1 }, end: { line: e.line, column: caret0 + 1 + replaceLen }, newText: closer });
33249
+ if (closer === "]") {
33250
+ const openIdxSq = lineText.lastIndexOf("[", caret0);
33251
+ if (openIdxSq !== -1) {
33252
+ const picks = [];
33253
+ const pushIdx2 = (i) => {
33254
+ if (i >= 0)
33255
+ picks.push(i);
33256
+ };
33257
+ pushIdx2(lineText.indexOf("-", openIdxSq + 1));
33258
+ pushIdx2(lineText.indexOf("=", openIdxSq + 1));
33259
+ pushIdx2(lineText.indexOf(".", openIdxSq + 1));
33260
+ pushIdx2(lineText.indexOf("|", openIdxSq + 1));
33261
+ let ins = picks.length ? Math.min(...picks) : lineText.length;
33262
+ let tl = ins - 1;
33263
+ while (tl >= 0 && /\s/.test(lineText[tl]))
33264
+ tl--;
33265
+ const startCol2 = tl + 1 + 1;
33266
+ const endCol2 = ins + 1;
33267
+ edits.push({ start: { line: e.line, column: startCol2 }, end: { line: e.line, column: endCol2 }, newText: closer });
33268
+ } else {
33269
+ const avail = lineText.slice(caret0);
33270
+ const replaceLen = Math.min(closer.length, Math.max(1, avail.length));
33271
+ edits.push({ start: { line: e.line, column: caret0 + 1 }, end: { line: e.line, column: caret0 + 1 + replaceLen }, newText: closer });
33272
+ }
33273
+ } else {
33274
+ const avail = lineText.slice(caret0);
33275
+ const replaceLen = Math.min(closer.length, Math.max(1, avail.length));
33276
+ edits.push({ start: { line: e.line, column: caret0 + 1 }, end: { line: e.line, column: caret0 + 1 + replaceLen }, newText: closer });
33277
+ }
32700
33278
  }
32701
33279
  continue;
32702
33280
  }
@@ -32828,9 +33406,11 @@ function computeFixes(text, errors, level = "safe") {
32828
33406
  if (core.length >= 2 && isSlashPair(left, right)) {
32829
33407
  break;
32830
33408
  }
32831
- const newInner = '"' + inner + '"';
32832
- edits.push({ start: { line: e.line, column: contentStart + 1 }, end: { line: e.line, column: closeIdx + 1 }, newText: newInner });
32833
- patchedLines.add(e.line);
33409
+ const replaced = inner.replace(/\(/g, "&#40;").replace(/\)/g, "&#41;");
33410
+ if (replaced !== inner) {
33411
+ edits.push({ start: { line: e.line, column: contentStart + 1 }, end: { line: e.line, column: closeIdx + 1 }, newText: replaced });
33412
+ patchedLines.add(e.line);
33413
+ }
32834
33414
  break;
32835
33415
  }
32836
33416
  searchStart = openIdx + 1;
@@ -32987,14 +33567,33 @@ function computeFixes(text, errors, level = "safe") {
32987
33567
  continue;
32988
33568
  }
32989
33569
  if (is("SE-BOX-EMPTY", e)) {
32990
- const lines = text.split(/\r?\n/);
32991
- const boxIdx = Math.max(0, e.line - 1);
32992
- const boxLine = lines[boxIdx] || "";
32993
- const labelMatch = /^\s*box\s+(.+)$/.exec(boxLine);
32994
- if (labelMatch) {
32995
- const indent = boxLine.match(/^\s*/)?.[0] || "";
32996
- const newLine = `${indent}rect rgb(240, 240, 255)`;
32997
- edits.push({ start: { line: e.line, column: 1 }, end: { line: e.line, column: boxLine.length + 1 }, newText: newLine });
33570
+ if (level === "all") {
33571
+ const lines = text.split(/\r?\n/);
33572
+ const boxIdx = Math.max(0, e.line - 1);
33573
+ const boxLine = lines[boxIdx] || "";
33574
+ const openIndent = (boxLine.match(/^(\s*)/)?.[1] || "").length;
33575
+ let endIdx = -1;
33576
+ for (let i = boxIdx + 1; i < lines.length; i++) {
33577
+ const raw = lines[i] || "";
33578
+ const ind = (raw.match(/^(\s*)/)?.[1] || "").length;
33579
+ if (/^\s*end\s*$/.test(raw) && ind <= openIndent) {
33580
+ endIdx = i;
33581
+ break;
33582
+ }
33583
+ }
33584
+ let hasMsgWithAct = false;
33585
+ if (endIdx !== -1) {
33586
+ const body = lines.slice(boxIdx + 1, endIdx).map((s) => (s || "").trim());
33587
+ hasMsgWithAct = body.some((s) => /->/.test(s) && /[+-]/.test(s));
33588
+ }
33589
+ if (!hasMsgWithAct) {
33590
+ const labelMatch = /^\s*box\s+(.+)$/.exec(boxLine);
33591
+ if (labelMatch) {
33592
+ const indent = boxLine.match(/^\s*/)?.[0] || "";
33593
+ const newLine = `${indent}rect rgb(240, 240, 255)`;
33594
+ edits.push({ start: { line: e.line, column: 1 }, end: { line: e.line, column: boxLine.length + 1 }, newText: newLine });
33595
+ }
33596
+ }
32998
33597
  }
32999
33598
  continue;
33000
33599
  }
@@ -33270,6 +33869,41 @@ ${indent}${tail}` });
33270
33869
  }
33271
33870
  }
33272
33871
  edits.push(insertAt(text, { line: insIdx + 1, column: 1 }, `${openIndent}}
33872
+ `));
33873
+ continue;
33874
+ }
33875
+ if (is("CL-NAMESPACE-MISSING-RBRACE", e)) {
33876
+ const lines = text.split(/\r?\n/);
33877
+ const curIdx = Math.max(0, e.line - 1);
33878
+ const openerRe = /^(\s*)namespace\b.*\{\s*$/;
33879
+ let openIdx = -1;
33880
+ let openIndent = "";
33881
+ for (let i = curIdx; i >= 0; i--) {
33882
+ const m = openerRe.exec(lines[i] || "");
33883
+ if (m) {
33884
+ openIdx = i;
33885
+ openIndent = m[1] || "";
33886
+ break;
33887
+ }
33888
+ }
33889
+ if (openIdx === -1) {
33890
+ const indent = inferIndentFromLine(lines[curIdx] || "");
33891
+ edits.push(insertAt(text, { line: curIdx + 1, column: 1 }, `${indent}}
33892
+ `));
33893
+ continue;
33894
+ }
33895
+ let insIdx = lines.length;
33896
+ for (let i = openIdx + 1; i < lines.length; i++) {
33897
+ const raw = lines[i] || "";
33898
+ if (raw.trim() === "")
33899
+ continue;
33900
+ const ind = inferIndentFromLine(raw);
33901
+ if (ind.length <= openIndent.length) {
33902
+ insIdx = i;
33903
+ break;
33904
+ }
33905
+ }
33906
+ edits.push(insertAt(text, { line: insIdx + 1, column: 1 }, `${openIndent}}
33273
33907
  `));
33274
33908
  continue;
33275
33909
  }
@@ -47313,6 +47947,8 @@ var init_ProbeAgent = __esm({
47313
47947
  "src/agent/ProbeAgent.js"() {
47314
47948
  "use strict";
47315
47949
  init_tokenCounter();
47950
+ init_InMemoryStorageAdapter();
47951
+ init_HookManager();
47316
47952
  init_tools2();
47317
47953
  init_common();
47318
47954
  init_probeTool();
@@ -47345,6 +47981,8 @@ var init_ProbeAgent = __esm({
47345
47981
  * @param {string} [options.mcpConfigPath] - Path to MCP configuration file
47346
47982
  * @param {Object} [options.mcpConfig] - MCP configuration object (overrides mcpConfigPath)
47347
47983
  * @param {Array} [options.mcpServers] - Deprecated, use mcpConfig instead
47984
+ * @param {Object} [options.storageAdapter] - Custom storage adapter for history management
47985
+ * @param {Object} [options.hooks] - Hook callbacks for events (e.g., {'tool:start': callback})
47348
47986
  */
47349
47987
  constructor(options = {}) {
47350
47988
  this.sessionId = options.sessionId || randomUUID4();
@@ -47358,6 +47996,13 @@ var init_ProbeAgent = __esm({
47358
47996
  this.maxResponseTokens = options.maxResponseTokens || parseInt(process.env.MAX_RESPONSE_TOKENS || "0", 10) || null;
47359
47997
  this.maxIterations = options.maxIterations || null;
47360
47998
  this.disableMermaidValidation = !!options.disableMermaidValidation;
47999
+ this.storageAdapter = options.storageAdapter || new InMemoryStorageAdapter();
48000
+ this.hooks = new HookManager();
48001
+ if (options.hooks) {
48002
+ for (const [hookName, callback] of Object.entries(options.hooks)) {
48003
+ this.hooks.on(hookName, callback);
48004
+ }
48005
+ }
47361
48006
  this.enableBash = !!options.enableBash;
47362
48007
  this.bashConfig = options.bashConfig || {};
47363
48008
  if (options.allowedFolders && Array.isArray(options.allowedFolders)) {
@@ -47391,9 +48036,23 @@ var init_ProbeAgent = __esm({
47391
48036
  }
47392
48037
  /**
47393
48038
  * Initialize the agent asynchronously (must be called after constructor)
47394
- * This method initializes MCP and merges MCP tools into the tool list
48039
+ * This method initializes MCP and merges MCP tools into the tool list, and loads history from storage
47395
48040
  */
47396
48041
  async initialize() {
48042
+ try {
48043
+ const history = await this.storageAdapter.loadHistory(this.sessionId);
48044
+ this.history = history;
48045
+ if (this.debug && history.length > 0) {
48046
+ console.log(`[DEBUG] Loaded ${history.length} messages from storage for session ${this.sessionId}`);
48047
+ }
48048
+ await this.hooks.emit(HOOK_TYPES.STORAGE_LOAD, {
48049
+ sessionId: this.sessionId,
48050
+ messages: history
48051
+ });
48052
+ } catch (error) {
48053
+ console.error(`[ERROR] Failed to load history from storage:`, error);
48054
+ this.history = [];
48055
+ }
47397
48056
  if (this.enableMcp && !this._mcpInitialized) {
47398
48057
  this._mcpInitialized = true;
47399
48058
  try {
@@ -47426,6 +48085,10 @@ var init_ProbeAgent = __esm({
47426
48085
  this.mcpBridge = null;
47427
48086
  }
47428
48087
  }
48088
+ await this.hooks.emit(HOOK_TYPES.AGENT_INITIALIZED, {
48089
+ sessionId: this.sessionId,
48090
+ agent: this
48091
+ });
47429
48092
  }
47430
48093
  /**
47431
48094
  * Initialize tools with configuration
@@ -47477,7 +48140,7 @@ var init_ProbeAgent = __esm({
47477
48140
  }
47478
48141
  const anthropicApiKey = process.env.ANTHROPIC_API_KEY;
47479
48142
  const openaiApiKey = process.env.OPENAI_API_KEY;
47480
- const googleApiKey = process.env.GOOGLE_API_KEY;
48143
+ const googleApiKey = process.env.GOOGLE_GENERATIVE_AI_API_KEY || process.env.GOOGLE_API_KEY;
47481
48144
  const awsAccessKeyId = process.env.AWS_ACCESS_KEY_ID;
47482
48145
  const awsSecretAccessKey = process.env.AWS_SECRET_ACCESS_KEY;
47483
48146
  const awsRegion = process.env.AWS_REGION;
@@ -47525,7 +48188,7 @@ var init_ProbeAgent = __esm({
47525
48188
  } else if (awsAccessKeyId && awsSecretAccessKey && awsRegion || awsApiKey) {
47526
48189
  this.initializeBedrockModel(awsAccessKeyId, awsSecretAccessKey, awsRegion, awsSessionToken, awsApiKey, awsBedrockBaseUrl, modelName);
47527
48190
  } else {
47528
- throw new Error("No API key provided. Please set ANTHROPIC_API_KEY, OPENAI_API_KEY, GOOGLE_API_KEY, AWS credentials (AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_REGION), or AWS_BEDROCK_API_KEY environment variables.");
48191
+ throw new Error("No API key provided. Please set ANTHROPIC_API_KEY, OPENAI_API_KEY, GOOGLE_GENERATIVE_AI_API_KEY (or GOOGLE_API_KEY), AWS credentials (AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_REGION), or AWS_BEDROCK_API_KEY environment variables.");
47529
48192
  }
47530
48193
  }
47531
48194
  /**
@@ -48163,6 +48826,12 @@ You are working with a repository located at: ${searchDirectory}
48163
48826
  options = schemaOrOptions || {};
48164
48827
  }
48165
48828
  try {
48829
+ const oldHistoryLength = this.history.length;
48830
+ await this.hooks.emit(HOOK_TYPES.MESSAGE_USER, {
48831
+ sessionId: this.sessionId,
48832
+ message,
48833
+ images
48834
+ });
48166
48835
  const systemMessage = await this.getSystemMessage();
48167
48836
  let userMessage = { role: "user", content: message.trim() };
48168
48837
  if (images && images.length > 0) {
@@ -48248,6 +48917,9 @@ You are working with a repository located at: ${searchDirectory}
48248
48917
  });
48249
48918
  for await (const delta of result.textStream) {
48250
48919
  assistantResponseContent += delta;
48920
+ if (options.onStream) {
48921
+ options.onStream(delta);
48922
+ }
48251
48923
  }
48252
48924
  const usage = await result.usage;
48253
48925
  if (usage) {
@@ -48312,6 +48984,13 @@ You are working with a repository located at: ${searchDirectory}
48312
48984
  const validation = attemptCompletionSchema.safeParse(params);
48313
48985
  if (validation.success) {
48314
48986
  finalResult = validation.data.result;
48987
+ if (options.onStream && finalResult) {
48988
+ const chunkSize = 50;
48989
+ for (let i = 0; i < finalResult.length; i += chunkSize) {
48990
+ const chunk = finalResult.slice(i, Math.min(i + chunkSize, finalResult.length));
48991
+ options.onStream(chunk);
48992
+ }
48993
+ }
48315
48994
  if (this.debug) console.log(`[DEBUG] Task completed successfully with result: ${finalResult.substring(0, 100)}...`);
48316
48995
  } else {
48317
48996
  console.error(`[ERROR] Invalid attempt_completion parameters:`, validation.error);
@@ -48386,7 +49065,9 @@ ${toolResultContent}
48386
49065
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
48387
49066
  name: toolName,
48388
49067
  args: toolParams,
48389
- status: "started"
49068
+ status: "started",
49069
+ pauseStream: true
49070
+ // Signal to pause text streaming
48390
49071
  });
48391
49072
  const executeToolCall = async () => {
48392
49073
  if (toolName === "delegate") {
@@ -48570,6 +49251,18 @@ IMPORTANT: When using <attempt_complete>, this must be the ONLY content in your
48570
49251
  }
48571
49252
  }
48572
49253
  this.tokenCounter.updateHistory(this.history);
49254
+ try {
49255
+ const messagesToSave = currentMessages.slice(oldHistoryLength);
49256
+ for (const message2 of messagesToSave) {
49257
+ await this.storageAdapter.saveMessage(this.sessionId, message2);
49258
+ await this.hooks.emit(HOOK_TYPES.STORAGE_SAVE, {
49259
+ sessionId: this.sessionId,
49260
+ message: message2
49261
+ });
49262
+ }
49263
+ } catch (error) {
49264
+ console.error(`[ERROR] Failed to save messages to storage:`, error);
49265
+ }
48573
49266
  const reachedMaxIterations = currentIteration >= maxIterations && !completionAttempted;
48574
49267
  if (options.schema && !options._schemaFormatted && !completionAttempted && !reachedMaxIterations) {
48575
49268
  if (this.debug) {
@@ -48934,10 +49627,18 @@ Convert your previous response content into actual JSON data that follows this s
48934
49627
  /**
48935
49628
  * Clear conversation history and reset counters
48936
49629
  */
48937
- clearHistory() {
49630
+ async clearHistory() {
49631
+ try {
49632
+ await this.storageAdapter.clearHistory(this.sessionId);
49633
+ } catch (error) {
49634
+ console.error(`[ERROR] Failed to clear history in storage:`, error);
49635
+ }
48938
49636
  this.history = [];
48939
49637
  this.tokenCounter.clear();
48940
49638
  clearToolExecutionData(this.sessionId);
49639
+ await this.hooks.emit(HOOK_TYPES.STORAGE_CLEAR, {
49640
+ sessionId: this.sessionId
49641
+ });
48941
49642
  if (this.debug) {
48942
49643
  console.log(`[DEBUG] Cleared conversation history and reset counters for session ${this.sessionId}`);
48943
49644
  }