@prbe.ai/electron-sdk 0.1.6 → 0.1.8

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/dist/index.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  // src/agent.ts
2
- import * as fs2 from "fs";
3
- import * as path4 from "path";
4
- import * as os from "os";
2
+ import * as fs3 from "fs";
3
+ import * as path5 from "path";
4
+ import * as os2 from "os";
5
5
  import { randomUUID as randomUUID5 } from "crypto";
6
6
 
7
7
  // src/models.ts
@@ -180,7 +180,7 @@ var PRBEAgentState = class extends EventEmitter {
180
180
  this.emit("status" /* STATUS */);
181
181
  }
182
182
  }
183
- completeInvestigation(report, summary) {
183
+ completeInvestigation(report, summary, ticketId) {
184
184
  if (this.events.length > 0) {
185
185
  this.events[this.events.length - 1].isCompleted = true;
186
186
  }
@@ -190,6 +190,9 @@ var PRBEAgentState = class extends EventEmitter {
190
190
  query: this.currentQuery,
191
191
  report,
192
192
  summary,
193
+ ticketId,
194
+ events: [...this.events],
195
+ resolvedInteractions: [...this.resolvedInteractions],
193
196
  completedAt: /* @__PURE__ */ new Date()
194
197
  });
195
198
  this.report = report;
@@ -265,11 +268,12 @@ var PRBEAgentState = class extends EventEmitter {
265
268
  }
266
269
  }
267
270
  // ---------- CR state mutations ----------
268
- beginCR(id, query, slug) {
271
+ beginCR(id, query, slug, ticketId) {
269
272
  const cr = {
270
273
  id,
271
274
  query,
272
275
  slug,
276
+ ticketId,
273
277
  events: [],
274
278
  resolvedInteractions: [],
275
279
  isRunning: true,
@@ -395,8 +399,13 @@ var PRBEToolRegistry = class {
395
399
  var PRBEClosureTool = class {
396
400
  declaration;
397
401
  handler;
398
- constructor(name, description, parameters, handler) {
399
- this.declaration = { name, description, parameters };
402
+ constructor(name, description, parameters, handler, options) {
403
+ this.declaration = {
404
+ name,
405
+ description,
406
+ parameters,
407
+ ...options?.interactive ? { interactive: true } : {}
408
+ };
400
409
  this.handler = handler;
401
410
  }
402
411
  async execute(args) {
@@ -1499,20 +1508,196 @@ var BashExecuteTool = class {
1499
1508
  }
1500
1509
  };
1501
1510
 
1502
- // src/agent.ts
1503
- function getPersistencePath() {
1511
+ // src/history.ts
1512
+ import * as fs2 from "fs";
1513
+ import * as path4 from "path";
1514
+ import * as os from "os";
1515
+ import * as crypto from "crypto";
1516
+ var HKDF_SALT = Buffer.from("prbe-history-encryption-salt", "utf-8");
1517
+ var HKDF_INFO = Buffer.from("prbe-history-v1", "utf-8");
1518
+ function getAppDataDir() {
1504
1519
  const appData = process.env["APPDATA"] || (process.platform === "darwin" ? path4.join(os.homedir(), "Library", "Application Support") : path4.join(os.homedir(), ".local", "share"));
1505
- const dir = path4.join(appData, "prbe-agent");
1520
+ return path4.join(appData, "prbe-agent");
1521
+ }
1522
+ function getHistoryDir() {
1523
+ const dir = path4.join(getAppDataDir(), "history" /* HISTORY_DIR */);
1506
1524
  if (!fs2.existsSync(dir)) {
1507
1525
  fs2.mkdirSync(dir, { recursive: true });
1508
1526
  }
1509
- return path4.join(dir, "agent-state.json");
1527
+ return dir;
1528
+ }
1529
+ function deriveKey(apiKey) {
1530
+ return Buffer.from(
1531
+ crypto.hkdfSync(
1532
+ "sha256",
1533
+ Buffer.from(apiKey, "utf-8"),
1534
+ HKDF_SALT,
1535
+ HKDF_INFO,
1536
+ 32 /* KEY_LENGTH */
1537
+ )
1538
+ );
1539
+ }
1540
+ function encrypt(plaintext, key) {
1541
+ const iv = crypto.randomBytes(12 /* IV_LENGTH */);
1542
+ const cipher = crypto.createCipheriv(
1543
+ "aes-256-gcm" /* ALGORITHM */,
1544
+ key,
1545
+ iv,
1546
+ { authTagLength: 16 /* AUTH_TAG_LENGTH */ }
1547
+ );
1548
+ const encrypted = Buffer.concat([cipher.update(plaintext, "utf-8"), cipher.final()]);
1549
+ const authTag = cipher.getAuthTag();
1550
+ return Buffer.concat([iv, authTag, encrypted]);
1551
+ }
1552
+ function decrypt(data, key) {
1553
+ const iv = data.subarray(0, 12 /* IV_LENGTH */);
1554
+ const authTag = data.subarray(
1555
+ 12 /* IV_LENGTH */,
1556
+ 12 /* IV_LENGTH */ + 16 /* AUTH_TAG_LENGTH */
1557
+ );
1558
+ const ciphertext = data.subarray(
1559
+ 12 /* IV_LENGTH */ + 16 /* AUTH_TAG_LENGTH */
1560
+ );
1561
+ const decipher = crypto.createDecipheriv(
1562
+ "aes-256-gcm" /* ALGORITHM */,
1563
+ key,
1564
+ iv,
1565
+ { authTagLength: 16 /* AUTH_TAG_LENGTH */ }
1566
+ );
1567
+ decipher.setAuthTag(authTag);
1568
+ return decipher.update(ciphertext) + decipher.final("utf-8");
1569
+ }
1570
+ var HistoryStore = class {
1571
+ key;
1572
+ constructor(apiKey) {
1573
+ this.key = deriveKey(apiKey);
1574
+ }
1575
+ load() {
1576
+ const investigations = [];
1577
+ const crs = [];
1578
+ try {
1579
+ const dir = getHistoryDir();
1580
+ let files;
1581
+ try {
1582
+ files = fs2.readdirSync(dir);
1583
+ } catch {
1584
+ return { investigations, crs };
1585
+ }
1586
+ for (const filename of files) {
1587
+ try {
1588
+ const filePath = path4.join(dir, filename);
1589
+ const raw = fs2.readFileSync(filePath);
1590
+ const json = decrypt(raw, this.key);
1591
+ if (filename.startsWith("inv-") && filename.endsWith(".json")) {
1592
+ const item = JSON.parse(json);
1593
+ investigations.push({
1594
+ ...item,
1595
+ completedAt: new Date(item.completedAt)
1596
+ });
1597
+ } else if (filename.startsWith("cr-") && filename.endsWith(".json")) {
1598
+ const item = JSON.parse(json);
1599
+ crs.push({
1600
+ ...item,
1601
+ startedAt: new Date(item.startedAt),
1602
+ resolvedInteractions: item.resolvedInteractions ?? []
1603
+ });
1604
+ }
1605
+ } catch {
1606
+ console.warn(`[PRBEAgent] Skipping unreadable history file: ${filename}`);
1607
+ }
1608
+ }
1609
+ } catch {
1610
+ }
1611
+ investigations.sort(
1612
+ (a, b) => b.completedAt.getTime() - a.completedAt.getTime()
1613
+ );
1614
+ crs.sort((a, b) => b.startedAt.getTime() - a.startedAt.getTime());
1615
+ return { investigations, crs };
1616
+ }
1617
+ save(investigations, crs) {
1618
+ try {
1619
+ const dir = getHistoryDir();
1620
+ const desiredFiles = /* @__PURE__ */ new Set();
1621
+ for (const inv of investigations) {
1622
+ const filename = `inv-${inv.id}.json`;
1623
+ desiredFiles.add(filename);
1624
+ const data = {
1625
+ id: inv.id,
1626
+ query: inv.query,
1627
+ report: inv.report,
1628
+ summary: inv.summary,
1629
+ ticketId: inv.ticketId,
1630
+ events: inv.events,
1631
+ resolvedInteractions: inv.resolvedInteractions,
1632
+ completedAt: inv.completedAt.toISOString()
1633
+ };
1634
+ fs2.writeFileSync(
1635
+ path4.join(dir, filename),
1636
+ encrypt(JSON.stringify(data), this.key)
1637
+ );
1638
+ }
1639
+ for (const cr of crs) {
1640
+ const filename = `cr-${cr.id}.json`;
1641
+ desiredFiles.add(filename);
1642
+ const data = {
1643
+ id: cr.id,
1644
+ query: cr.query,
1645
+ slug: cr.slug,
1646
+ ticketId: cr.ticketId,
1647
+ events: cr.events,
1648
+ isRunning: cr.isRunning,
1649
+ isCompleted: cr.isCompleted,
1650
+ isFailed: cr.isFailed,
1651
+ report: cr.report,
1652
+ summary: cr.summary,
1653
+ errorMessage: cr.errorMessage,
1654
+ startedAt: cr.startedAt.toISOString(),
1655
+ pendingInteraction: cr.pendingInteraction,
1656
+ resolvedInteractions: cr.resolvedInteractions ?? []
1657
+ };
1658
+ fs2.writeFileSync(
1659
+ path4.join(dir, filename),
1660
+ encrypt(JSON.stringify(data), this.key)
1661
+ );
1662
+ }
1663
+ try {
1664
+ const existing = fs2.readdirSync(dir);
1665
+ for (const filename of existing) {
1666
+ if (!desiredFiles.has(filename)) {
1667
+ fs2.unlinkSync(path4.join(dir, filename));
1668
+ }
1669
+ }
1670
+ } catch {
1671
+ }
1672
+ } catch {
1673
+ console.error("[PRBEAgent] Failed to save investigation history");
1674
+ }
1675
+ }
1676
+ static clear() {
1677
+ try {
1678
+ const dir = path4.join(getAppDataDir(), "history" /* HISTORY_DIR */);
1679
+ if (fs2.existsSync(dir)) {
1680
+ fs2.rmSync(dir, { recursive: true, force: true });
1681
+ }
1682
+ } catch {
1683
+ }
1684
+ }
1685
+ };
1686
+
1687
+ // src/agent.ts
1688
+ function getPersistencePath() {
1689
+ const appData = process.env["APPDATA"] || (process.platform === "darwin" ? path5.join(os2.homedir(), "Library", "Application Support") : path5.join(os2.homedir(), ".local", "share"));
1690
+ const dir = path5.join(appData, "prbe-agent");
1691
+ if (!fs3.existsSync(dir)) {
1692
+ fs3.mkdirSync(dir, { recursive: true });
1693
+ }
1694
+ return path5.join(dir, "agent-state.json");
1510
1695
  }
1511
1696
  function loadPersistedData() {
1512
1697
  try {
1513
1698
  const filePath = getPersistencePath();
1514
- if (fs2.existsSync(filePath)) {
1515
- const raw = fs2.readFileSync(filePath, "utf-8");
1699
+ if (fs3.existsSync(filePath)) {
1700
+ const raw = fs3.readFileSync(filePath, "utf-8");
1516
1701
  return JSON.parse(raw);
1517
1702
  }
1518
1703
  } catch {
@@ -1522,7 +1707,7 @@ function loadPersistedData() {
1522
1707
  function savePersistedData(data) {
1523
1708
  try {
1524
1709
  const filePath = getPersistencePath();
1525
- fs2.writeFileSync(filePath, JSON.stringify(data, null, 2), "utf-8");
1710
+ fs3.writeFileSync(filePath, JSON.stringify(data, null, 2), "utf-8");
1526
1711
  } catch {
1527
1712
  console.error("[PRBEAgent] Failed to save persisted data");
1528
1713
  }
@@ -1542,6 +1727,7 @@ var PRBEAgent = class _PRBEAgent {
1542
1727
  fetchAbortController = null;
1543
1728
  currentInvestigationSource = "user" /* USER */;
1544
1729
  currentCRId = null;
1730
+ historyStore;
1545
1731
  /** Files flagged during the current tool call — uploaded immediately after the tool returns. */
1546
1732
  pendingFlaggedFiles = [];
1547
1733
  // ---------- Persistence ----------
@@ -1603,6 +1789,10 @@ var PRBEAgent = class _PRBEAgent {
1603
1789
  this.state = new PRBEAgentState();
1604
1790
  this.logCapture = new PRBELogCapture(this.config.maxLogEntries);
1605
1791
  this.persistedData = loadPersistedData();
1792
+ this.historyStore = new HistoryStore(this.config.apiKey);
1793
+ const history = this.historyStore.load();
1794
+ this.state.completedInvestigations = history.investigations;
1795
+ this.state.completedCRs = history.crs;
1606
1796
  const roots = this.config.autoApprovedDirs;
1607
1797
  const requester = this.interactionHandler ? this : void 0;
1608
1798
  const grantedPaths = this.grantedPaths;
@@ -1723,9 +1913,9 @@ var PRBEAgent = class _PRBEAgent {
1723
1913
  /**
1724
1914
  * Register a custom tool that the middleware can invoke during investigations.
1725
1915
  */
1726
- registerTool(name, description, parameters, handler) {
1916
+ registerTool(name, description, parameters, handler, options) {
1727
1917
  this.registry.register(
1728
- new PRBEClosureTool(name, description, parameters, handler)
1918
+ new PRBEClosureTool(name, description, parameters, handler, options)
1729
1919
  );
1730
1920
  }
1731
1921
  /**
@@ -1753,7 +1943,8 @@ var PRBEAgent = class _PRBEAgent {
1753
1943
  this.state.appendEvent("Thinking", status.text);
1754
1944
  break;
1755
1945
  case "completed" /* COMPLETED */:
1756
- this.state.completeInvestigation(status.report, status.userSummary);
1946
+ this.state.completeInvestigation(status.report, status.userSummary, status.ticketId);
1947
+ this.historyStore.save(this.state.completedInvestigations, this.state.completedCRs);
1757
1948
  break;
1758
1949
  case "error" /* ERROR */:
1759
1950
  this.state.failInvestigation(status.message);
@@ -1895,6 +2086,7 @@ var PRBEAgent = class _PRBEAgent {
1895
2086
  this.stopPolling();
1896
2087
  this.persistedData = {};
1897
2088
  savePersistedData(this.persistedData);
2089
+ HistoryStore.clear();
1898
2090
  this.state.resetInvestigation();
1899
2091
  this.state.completedInvestigations = [];
1900
2092
  this.state.activeCRs.clear();
@@ -1909,9 +2101,10 @@ var PRBEAgent = class _PRBEAgent {
1909
2101
  static clearPersistedData() {
1910
2102
  try {
1911
2103
  const filePath = getPersistencePath();
1912
- if (fs2.existsSync(filePath)) {
1913
- fs2.unlinkSync(filePath);
2104
+ if (fs3.existsSync(filePath)) {
2105
+ fs3.unlinkSync(filePath);
1914
2106
  }
2107
+ HistoryStore.clear();
1915
2108
  } catch {
1916
2109
  }
1917
2110
  }
@@ -1920,7 +2113,7 @@ var PRBEAgent = class _PRBEAgent {
1920
2113
  const crID = cr.id;
1921
2114
  this.currentInvestigationSource = "context_request" /* CONTEXT_REQUEST */;
1922
2115
  this.currentCRId = crID;
1923
- this.state.beginCR(crID, cr.query, cr.slug ?? void 0);
2116
+ this.state.beginCR(crID, cr.query, cr.slug ?? void 0, ticketId);
1924
2117
  const emitter = (status) => {
1925
2118
  switch (status.type) {
1926
2119
  case "started" /* STARTED */:
@@ -1939,9 +2132,11 @@ var PRBEAgent = class _PRBEAgent {
1939
2132
  break;
1940
2133
  case "completed" /* COMPLETED */:
1941
2134
  this.state.completeCR(crID, status.report, status.userSummary);
2135
+ this.historyStore.save(this.state.completedInvestigations, this.state.completedCRs);
1942
2136
  break;
1943
2137
  case "error" /* ERROR */:
1944
2138
  this.state.failCR(crID, status.message);
2139
+ this.historyStore.save(this.state.completedInvestigations, this.state.completedCRs);
1945
2140
  break;
1946
2141
  }
1947
2142
  };
@@ -2080,7 +2275,7 @@ var PRBEAgent = class _PRBEAgent {
2080
2275
  const uploadPrefix = this.extractUploadPrefix(uploadBaseUrl);
2081
2276
  const uploadedRefs = [];
2082
2277
  for (const file of this.pendingFlaggedFiles) {
2083
- const filename = path4.basename(file.originalPath);
2278
+ const filename = path5.basename(file.originalPath);
2084
2279
  const safeName = encodeURIComponent(filename);
2085
2280
  const storagePath = `${uploadPrefix}/${safeName}`;
2086
2281
  uploadedRefs.push({
@@ -2136,7 +2331,8 @@ var PRBEAgent = class _PRBEAgent {
2136
2331
  emit({
2137
2332
  type: "completed" /* COMPLETED */,
2138
2333
  report,
2139
- userSummary
2334
+ userSummary,
2335
+ ticketId: ticketId2
2140
2336
  });
2141
2337
  ws.close(1e3, "Complete");
2142
2338
  finish({ report, userSummary, ticketId: ticketId2 });
@@ -2319,6 +2515,7 @@ function serializeCR(cr) {
2319
2515
  id: cr.id,
2320
2516
  query: cr.query,
2321
2517
  slug: cr.slug,
2518
+ ticketId: cr.ticketId,
2322
2519
  events: cr.events,
2323
2520
  isRunning: cr.isRunning,
2324
2521
  isCompleted: cr.isCompleted,
@@ -2346,6 +2543,9 @@ function serializePRBEState(state) {
2346
2543
  query: inv.query,
2347
2544
  report: inv.report,
2348
2545
  summary: inv.summary,
2546
+ ticketId: inv.ticketId,
2547
+ events: inv.events,
2548
+ resolvedInteractions: inv.resolvedInteractions,
2349
2549
  completedAt: inv.completedAt.toISOString()
2350
2550
  })),
2351
2551
  activeCRs: Array.from(state.activeCRs.values()).map(serializeCR),