@probelabs/probe 0.6.0-rc125 → 0.6.0-rc126

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
 
@@ -47313,6 +47560,8 @@ var init_ProbeAgent = __esm({
47313
47560
  "src/agent/ProbeAgent.js"() {
47314
47561
  "use strict";
47315
47562
  init_tokenCounter();
47563
+ init_InMemoryStorageAdapter();
47564
+ init_HookManager();
47316
47565
  init_tools2();
47317
47566
  init_common();
47318
47567
  init_probeTool();
@@ -47345,6 +47594,8 @@ var init_ProbeAgent = __esm({
47345
47594
  * @param {string} [options.mcpConfigPath] - Path to MCP configuration file
47346
47595
  * @param {Object} [options.mcpConfig] - MCP configuration object (overrides mcpConfigPath)
47347
47596
  * @param {Array} [options.mcpServers] - Deprecated, use mcpConfig instead
47597
+ * @param {Object} [options.storageAdapter] - Custom storage adapter for history management
47598
+ * @param {Object} [options.hooks] - Hook callbacks for events (e.g., {'tool:start': callback})
47348
47599
  */
47349
47600
  constructor(options = {}) {
47350
47601
  this.sessionId = options.sessionId || randomUUID4();
@@ -47358,6 +47609,13 @@ var init_ProbeAgent = __esm({
47358
47609
  this.maxResponseTokens = options.maxResponseTokens || parseInt(process.env.MAX_RESPONSE_TOKENS || "0", 10) || null;
47359
47610
  this.maxIterations = options.maxIterations || null;
47360
47611
  this.disableMermaidValidation = !!options.disableMermaidValidation;
47612
+ this.storageAdapter = options.storageAdapter || new InMemoryStorageAdapter();
47613
+ this.hooks = new HookManager();
47614
+ if (options.hooks) {
47615
+ for (const [hookName, callback] of Object.entries(options.hooks)) {
47616
+ this.hooks.on(hookName, callback);
47617
+ }
47618
+ }
47361
47619
  this.enableBash = !!options.enableBash;
47362
47620
  this.bashConfig = options.bashConfig || {};
47363
47621
  if (options.allowedFolders && Array.isArray(options.allowedFolders)) {
@@ -47391,9 +47649,23 @@ var init_ProbeAgent = __esm({
47391
47649
  }
47392
47650
  /**
47393
47651
  * Initialize the agent asynchronously (must be called after constructor)
47394
- * This method initializes MCP and merges MCP tools into the tool list
47652
+ * This method initializes MCP and merges MCP tools into the tool list, and loads history from storage
47395
47653
  */
47396
47654
  async initialize() {
47655
+ try {
47656
+ const history = await this.storageAdapter.loadHistory(this.sessionId);
47657
+ this.history = history;
47658
+ if (this.debug && history.length > 0) {
47659
+ console.log(`[DEBUG] Loaded ${history.length} messages from storage for session ${this.sessionId}`);
47660
+ }
47661
+ await this.hooks.emit(HOOK_TYPES.STORAGE_LOAD, {
47662
+ sessionId: this.sessionId,
47663
+ messages: history
47664
+ });
47665
+ } catch (error) {
47666
+ console.error(`[ERROR] Failed to load history from storage:`, error);
47667
+ this.history = [];
47668
+ }
47397
47669
  if (this.enableMcp && !this._mcpInitialized) {
47398
47670
  this._mcpInitialized = true;
47399
47671
  try {
@@ -47426,6 +47698,10 @@ var init_ProbeAgent = __esm({
47426
47698
  this.mcpBridge = null;
47427
47699
  }
47428
47700
  }
47701
+ await this.hooks.emit(HOOK_TYPES.AGENT_INITIALIZED, {
47702
+ sessionId: this.sessionId,
47703
+ agent: this
47704
+ });
47429
47705
  }
47430
47706
  /**
47431
47707
  * Initialize tools with configuration
@@ -47477,7 +47753,7 @@ var init_ProbeAgent = __esm({
47477
47753
  }
47478
47754
  const anthropicApiKey = process.env.ANTHROPIC_API_KEY;
47479
47755
  const openaiApiKey = process.env.OPENAI_API_KEY;
47480
- const googleApiKey = process.env.GOOGLE_API_KEY;
47756
+ const googleApiKey = process.env.GOOGLE_GENERATIVE_AI_API_KEY || process.env.GOOGLE_API_KEY;
47481
47757
  const awsAccessKeyId = process.env.AWS_ACCESS_KEY_ID;
47482
47758
  const awsSecretAccessKey = process.env.AWS_SECRET_ACCESS_KEY;
47483
47759
  const awsRegion = process.env.AWS_REGION;
@@ -47525,7 +47801,7 @@ var init_ProbeAgent = __esm({
47525
47801
  } else if (awsAccessKeyId && awsSecretAccessKey && awsRegion || awsApiKey) {
47526
47802
  this.initializeBedrockModel(awsAccessKeyId, awsSecretAccessKey, awsRegion, awsSessionToken, awsApiKey, awsBedrockBaseUrl, modelName);
47527
47803
  } 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.");
47804
+ 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
47805
  }
47530
47806
  }
47531
47807
  /**
@@ -48163,6 +48439,12 @@ You are working with a repository located at: ${searchDirectory}
48163
48439
  options = schemaOrOptions || {};
48164
48440
  }
48165
48441
  try {
48442
+ const oldHistoryLength = this.history.length;
48443
+ await this.hooks.emit(HOOK_TYPES.MESSAGE_USER, {
48444
+ sessionId: this.sessionId,
48445
+ message,
48446
+ images
48447
+ });
48166
48448
  const systemMessage = await this.getSystemMessage();
48167
48449
  let userMessage = { role: "user", content: message.trim() };
48168
48450
  if (images && images.length > 0) {
@@ -48248,6 +48530,9 @@ You are working with a repository located at: ${searchDirectory}
48248
48530
  });
48249
48531
  for await (const delta of result.textStream) {
48250
48532
  assistantResponseContent += delta;
48533
+ if (options.onStream) {
48534
+ options.onStream(delta);
48535
+ }
48251
48536
  }
48252
48537
  const usage = await result.usage;
48253
48538
  if (usage) {
@@ -48312,6 +48597,13 @@ You are working with a repository located at: ${searchDirectory}
48312
48597
  const validation = attemptCompletionSchema.safeParse(params);
48313
48598
  if (validation.success) {
48314
48599
  finalResult = validation.data.result;
48600
+ if (options.onStream && finalResult) {
48601
+ const chunkSize = 50;
48602
+ for (let i = 0; i < finalResult.length; i += chunkSize) {
48603
+ const chunk = finalResult.slice(i, Math.min(i + chunkSize, finalResult.length));
48604
+ options.onStream(chunk);
48605
+ }
48606
+ }
48315
48607
  if (this.debug) console.log(`[DEBUG] Task completed successfully with result: ${finalResult.substring(0, 100)}...`);
48316
48608
  } else {
48317
48609
  console.error(`[ERROR] Invalid attempt_completion parameters:`, validation.error);
@@ -48386,7 +48678,9 @@ ${toolResultContent}
48386
48678
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
48387
48679
  name: toolName,
48388
48680
  args: toolParams,
48389
- status: "started"
48681
+ status: "started",
48682
+ pauseStream: true
48683
+ // Signal to pause text streaming
48390
48684
  });
48391
48685
  const executeToolCall = async () => {
48392
48686
  if (toolName === "delegate") {
@@ -48570,6 +48864,18 @@ IMPORTANT: When using <attempt_complete>, this must be the ONLY content in your
48570
48864
  }
48571
48865
  }
48572
48866
  this.tokenCounter.updateHistory(this.history);
48867
+ try {
48868
+ const messagesToSave = currentMessages.slice(oldHistoryLength);
48869
+ for (const message2 of messagesToSave) {
48870
+ await this.storageAdapter.saveMessage(this.sessionId, message2);
48871
+ await this.hooks.emit(HOOK_TYPES.STORAGE_SAVE, {
48872
+ sessionId: this.sessionId,
48873
+ message: message2
48874
+ });
48875
+ }
48876
+ } catch (error) {
48877
+ console.error(`[ERROR] Failed to save messages to storage:`, error);
48878
+ }
48573
48879
  const reachedMaxIterations = currentIteration >= maxIterations && !completionAttempted;
48574
48880
  if (options.schema && !options._schemaFormatted && !completionAttempted && !reachedMaxIterations) {
48575
48881
  if (this.debug) {
@@ -48934,10 +49240,18 @@ Convert your previous response content into actual JSON data that follows this s
48934
49240
  /**
48935
49241
  * Clear conversation history and reset counters
48936
49242
  */
48937
- clearHistory() {
49243
+ async clearHistory() {
49244
+ try {
49245
+ await this.storageAdapter.clearHistory(this.sessionId);
49246
+ } catch (error) {
49247
+ console.error(`[ERROR] Failed to clear history in storage:`, error);
49248
+ }
48938
49249
  this.history = [];
48939
49250
  this.tokenCounter.clear();
48940
49251
  clearToolExecutionData(this.sessionId);
49252
+ await this.hooks.emit(HOOK_TYPES.STORAGE_CLEAR, {
49253
+ sessionId: this.sessionId
49254
+ });
48941
49255
  if (this.debug) {
48942
49256
  console.log(`[DEBUG] Cleared conversation history and reset counters for session ${this.sessionId}`);
48943
49257
  }
@@ -0,0 +1,49 @@
1
+ import { StorageAdapter } from './StorageAdapter.js';
2
+
3
+ /**
4
+ * Default in-memory storage adapter
5
+ * This is the default behavior - stores history in a Map in memory
6
+ */
7
+ export class InMemoryStorageAdapter extends StorageAdapter {
8
+ constructor() {
9
+ super();
10
+ this.sessions = new Map(); // sessionId -> {messages: [], metadata: {}}
11
+ }
12
+
13
+ async loadHistory(sessionId) {
14
+ const session = this.sessions.get(sessionId);
15
+ return session ? session.messages : [];
16
+ }
17
+
18
+ async saveMessage(sessionId, message) {
19
+ if (!this.sessions.has(sessionId)) {
20
+ this.sessions.set(sessionId, {
21
+ messages: [],
22
+ metadata: {
23
+ createdAt: new Date().toISOString(),
24
+ lastActivity: new Date().toISOString()
25
+ }
26
+ });
27
+ }
28
+
29
+ const session = this.sessions.get(sessionId);
30
+ session.messages.push(message);
31
+ session.metadata.lastActivity = new Date().toISOString();
32
+ }
33
+
34
+ async clearHistory(sessionId) {
35
+ this.sessions.delete(sessionId);
36
+ }
37
+
38
+ async getSessionMetadata(sessionId) {
39
+ const session = this.sessions.get(sessionId);
40
+ return session ? session.metadata : null;
41
+ }
42
+
43
+ async updateSessionActivity(sessionId) {
44
+ const session = this.sessions.get(sessionId);
45
+ if (session) {
46
+ session.metadata.lastActivity = new Date().toISOString();
47
+ }
48
+ }
49
+ }
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Base class for storage adapters
3
+ * Implement this interface to provide custom storage backends for ProbeAgent history
4
+ */
5
+ export class StorageAdapter {
6
+ /**
7
+ * Load conversation history for a session
8
+ * @param {string} sessionId - Session identifier
9
+ * @returns {Promise<Array<Object>>} Array of message objects with {role, content, ...}
10
+ */
11
+ async loadHistory(sessionId) {
12
+ throw new Error('StorageAdapter.loadHistory() must be implemented by subclass');
13
+ }
14
+
15
+ /**
16
+ * Save a message to storage
17
+ * @param {string} sessionId - Session identifier
18
+ * @param {Object} message - Message object { role, content, ... }
19
+ * @returns {Promise<void>}
20
+ */
21
+ async saveMessage(sessionId, message) {
22
+ throw new Error('StorageAdapter.saveMessage() must be implemented by subclass');
23
+ }
24
+
25
+ /**
26
+ * Clear history for a session
27
+ * @param {string} sessionId - Session identifier
28
+ * @returns {Promise<void>}
29
+ */
30
+ async clearHistory(sessionId) {
31
+ throw new Error('StorageAdapter.clearHistory() must be implemented by subclass');
32
+ }
33
+
34
+ /**
35
+ * Get session metadata (optional)
36
+ * @param {string} sessionId - Session identifier
37
+ * @returns {Promise<Object|null>} Session metadata or null
38
+ */
39
+ async getSessionMetadata(sessionId) {
40
+ return null;
41
+ }
42
+
43
+ /**
44
+ * Update session activity timestamp (optional)
45
+ * @param {string} sessionId - Session identifier
46
+ * @returns {Promise<void>}
47
+ */
48
+ async updateSessionActivity(sessionId) {
49
+ // Optional - implement if you want to track session activity
50
+ }
51
+ }
@@ -0,0 +1,2 @@
1
+ export { StorageAdapter } from './StorageAdapter.js';
2
+ export { InMemoryStorageAdapter } from './InMemoryStorageAdapter.js';
package/build/index.js CHANGED
@@ -36,6 +36,8 @@ import { bashTool } from './tools/bash.js';
36
36
  import { ProbeAgent } from './agent/ProbeAgent.js';
37
37
  import { SimpleTelemetry, SimpleAppTracer, initializeSimpleTelemetryFromOptions } from './agent/simpleTelemetry.js';
38
38
  import { listFilesToolInstance, searchFilesToolInstance } from './agent/probeTool.js';
39
+ import { StorageAdapter, InMemoryStorageAdapter } from './agent/storage/index.js';
40
+ import { HookManager, HOOK_TYPES } from './agent/hooks/index.js';
39
41
 
40
42
  export {
41
43
  search,
@@ -50,6 +52,12 @@ export {
50
52
  DEFAULT_SYSTEM_MESSAGE,
51
53
  // Export AI Agent (NEW!)
52
54
  ProbeAgent,
55
+ // Export storage adapters
56
+ StorageAdapter,
57
+ InMemoryStorageAdapter,
58
+ // Export hooks
59
+ HookManager,
60
+ HOOK_TYPES,
53
61
  // Export telemetry classes
54
62
  SimpleTelemetry,
55
63
  SimpleAppTracer,