@prbe.ai/electron-sdk 0.1.17 → 0.1.19

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.js CHANGED
@@ -72,12 +72,12 @@ __export(src_exports, {
72
72
  module.exports = __toCommonJS(src_exports);
73
73
 
74
74
  // package.json
75
- var version = "0.1.17";
75
+ var version = "0.1.19";
76
76
 
77
77
  // src/agent.ts
78
- var fs3 = __toESM(require("fs"));
79
- var path5 = __toESM(require("path"));
80
- var os2 = __toESM(require("os"));
78
+ var fs2 = __toESM(require("fs"));
79
+ var path4 = __toESM(require("path"));
80
+ var os = __toESM(require("os"));
81
81
  var import_crypto5 = require("crypto");
82
82
 
83
83
  // src/models.ts
@@ -98,6 +98,9 @@ var WSMessageType = /* @__PURE__ */ ((WSMessageType2) => {
98
98
  WSMessageType2["COMPLETE"] = "complete";
99
99
  WSMessageType2["ERROR"] = "error";
100
100
  WSMessageType2["PING"] = "ping";
101
+ WSMessageType2["PRIVACY_SANITIZING"] = "privacy_sanitizing";
102
+ WSMessageType2["PRIVACY_REVIEW"] = "privacy_review";
103
+ WSMessageType2["PRIVACY_REVIEW_RESPONSE"] = "privacy_review_response";
101
104
  return WSMessageType2;
102
105
  })(WSMessageType || {});
103
106
  var ConversationRole = /* @__PURE__ */ ((ConversationRole3) => {
@@ -155,6 +158,7 @@ var PRBEAgentStatusType = /* @__PURE__ */ ((PRBEAgentStatusType2) => {
155
158
  PRBEAgentStatusType2["COMPLETED"] = "completed";
156
159
  PRBEAgentStatusType2["ERROR"] = "error";
157
160
  PRBEAgentStatusType2["AWAITING_INTERACTION"] = "awaiting_interaction";
161
+ PRBEAgentStatusType2["PRIVACY_SANITIZING"] = "privacy_sanitizing";
158
162
  return PRBEAgentStatusType2;
159
163
  })(PRBEAgentStatusType || {});
160
164
  var PRBEAgentErrorType = /* @__PURE__ */ ((PRBEAgentErrorType2) => {
@@ -220,10 +224,14 @@ var InvestigationConnection = class {
220
224
  return false;
221
225
  }
222
226
  }
223
- sendConversationMessage(content) {
227
+ sendConversationMessage(content, role, label) {
228
+ const metadata = {};
229
+ if (role) metadata["role"] = role;
230
+ if (label) metadata["label"] = label;
224
231
  this.send({
225
232
  type: "conversation_message" /* CONVERSATION_MESSAGE */,
226
- content
233
+ content,
234
+ ...Object.keys(metadata).length > 0 ? { metadata } : {}
227
235
  });
228
236
  }
229
237
  sendToolResult(callId, toolName, result, metadata) {
@@ -265,8 +273,8 @@ var PRBEStateEvent = /* @__PURE__ */ ((PRBEStateEvent2) => {
265
273
  PRBEStateEvent2["EVENT"] = "event";
266
274
  PRBEStateEvent2["COMPLETE"] = "complete";
267
275
  PRBEStateEvent2["ERROR"] = "error";
268
- PRBEStateEvent2["CR_START"] = "cr-start";
269
- PRBEStateEvent2["CR_COMPLETE"] = "cr-complete";
276
+ PRBEStateEvent2["BACKGROUND_START"] = "background-start";
277
+ PRBEStateEvent2["BACKGROUND_COMPLETE"] = "background-complete";
270
278
  PRBEStateEvent2["TICKETS_CHANGED"] = "tickets-changed";
271
279
  PRBEStateEvent2["TICKET_INFO"] = "ticket-info";
272
280
  PRBEStateEvent2["INTERACTION_REQUESTED"] = "interaction-requested";
@@ -286,20 +294,23 @@ var PRBEAgentState = class extends import_events.EventEmitter {
286
294
  resolvedInteractions = [];
287
295
  agentMessage;
288
296
  conversationHistory = [];
297
+ isPrivacySanitizing = false;
289
298
  // Completed user investigations (history)
290
299
  completedInvestigations = [];
291
- // Background context requests
292
- activeCRs = /* @__PURE__ */ new Map();
293
- completedCRs = [];
300
+ // Background investigations (context requests, external requests, etc.)
301
+ activeBackgroundInvestigations = /* @__PURE__ */ new Map();
302
+ completedBackgroundInvestigations = [];
294
303
  // Tracked tickets
295
304
  trackedSessionIDs = [];
296
305
  ticketInfo = [];
306
+ // Agent history
307
+ agentHistory = [];
297
308
  // Computed
298
309
  get hasActiveWork() {
299
- return this.isInvestigating || this.activeCRs.size > 0;
310
+ return this.isInvestigating || this.activeBackgroundInvestigations.size > 0;
300
311
  }
301
- get activeCRCount() {
302
- return this.activeCRs.size;
312
+ get activeBackgroundCount() {
313
+ return this.activeBackgroundInvestigations.size;
303
314
  }
304
315
  get isActive() {
305
316
  return this.isInvestigating || this.report.length > 0 || this.investigationError != null;
@@ -321,6 +332,12 @@ var PRBEAgentState = class extends import_events.EventEmitter {
321
332
  this.conversationHistory.push(entry);
322
333
  this.emit("status" /* STATUS */);
323
334
  }
335
+ appendBackgroundConversation(backgroundId, entry) {
336
+ const bg = this.activeBackgroundInvestigations.get(backgroundId);
337
+ if (!bg) return;
338
+ bg.conversationHistory.push(entry);
339
+ this.emit("status" /* STATUS */);
340
+ }
324
341
  resetInvestigation() {
325
342
  this.isInvestigating = false;
326
343
  this.events = [];
@@ -375,6 +392,7 @@ var PRBEAgentState = class extends import_events.EventEmitter {
375
392
  });
376
393
  this.report = report;
377
394
  this.isInvestigating = false;
395
+ this.isPrivacySanitizing = false;
378
396
  this.emit("complete" /* COMPLETE */, { report });
379
397
  this.emit("status" /* STATUS */);
380
398
  }
@@ -382,9 +400,14 @@ var PRBEAgentState = class extends import_events.EventEmitter {
382
400
  this.appendEvent(`Error: ${message}`);
383
401
  this.investigationError = message;
384
402
  this.isInvestigating = false;
403
+ this.isPrivacySanitizing = false;
385
404
  this.emit("error" /* ERROR */, { message });
386
405
  this.emit("status" /* STATUS */);
387
406
  }
407
+ setPrivacySanitizing(value) {
408
+ this.isPrivacySanitizing = value;
409
+ this.emit("status" /* STATUS */);
410
+ }
388
411
  // ---------- Interaction state mutations ----------
389
412
  setPendingInteraction(payload) {
390
413
  this.pendingInteraction = payload;
@@ -396,17 +419,17 @@ var PRBEAgentState = class extends import_events.EventEmitter {
396
419
  this.emit("interaction-resolved" /* INTERACTION_RESOLVED */);
397
420
  this.emit("status" /* STATUS */);
398
421
  }
399
- setCRPendingInteraction(crID, payload) {
400
- const cr = this.activeCRs.get(crID);
401
- if (!cr) return;
402
- cr.pendingInteraction = payload;
422
+ setBackgroundPendingInteraction(backgroundId, payload) {
423
+ const bg = this.activeBackgroundInvestigations.get(backgroundId);
424
+ if (!bg) return;
425
+ bg.pendingInteraction = payload;
403
426
  this.emit("interaction-requested" /* INTERACTION_REQUESTED */, payload);
404
427
  this.emit("status" /* STATUS */);
405
428
  }
406
- clearCRPendingInteraction(crID) {
407
- const cr = this.activeCRs.get(crID);
408
- if (!cr) return;
409
- cr.pendingInteraction = void 0;
429
+ clearBackgroundPendingInteraction(backgroundId) {
430
+ const bg = this.activeBackgroundInvestigations.get(backgroundId);
431
+ if (!bg) return;
432
+ bg.pendingInteraction = void 0;
410
433
  this.emit("interaction-resolved" /* INTERACTION_RESOLVED */);
411
434
  this.emit("status" /* STATUS */);
412
435
  }
@@ -422,18 +445,18 @@ var PRBEAgentState = class extends import_events.EventEmitter {
422
445
  this.emit("interaction-resolved" /* INTERACTION_RESOLVED */);
423
446
  this.emit("status" /* STATUS */);
424
447
  }
425
- resolveCRInteraction(crID, response) {
426
- const cr = this.activeCRs.get(crID);
427
- if (!cr || !cr.pendingInteraction) return;
428
- const resolved = cr.resolvedInteractions ?? [];
448
+ resolveBackgroundInteraction(backgroundId, response) {
449
+ const bg = this.activeBackgroundInvestigations.get(backgroundId);
450
+ if (!bg || !bg.pendingInteraction) return;
451
+ const resolved = bg.resolvedInteractions ?? [];
429
452
  resolved.push({
430
- interactionId: cr.pendingInteraction.interactionId,
431
- payload: cr.pendingInteraction,
453
+ interactionId: bg.pendingInteraction.interactionId,
454
+ payload: bg.pendingInteraction,
432
455
  response,
433
- eventIndex: cr.events.length
456
+ eventIndex: bg.events.length
434
457
  });
435
- cr.resolvedInteractions = resolved;
436
- cr.pendingInteraction = void 0;
458
+ bg.resolvedInteractions = resolved;
459
+ bg.pendingInteraction = void 0;
437
460
  this.emit("interaction-resolved" /* INTERACTION_RESOLVED */);
438
461
  this.emit("status" /* STATUS */);
439
462
  }
@@ -442,10 +465,10 @@ var PRBEAgentState = class extends import_events.EventEmitter {
442
465
  this.emit("agent-message" /* AGENT_MESSAGE */, { message });
443
466
  this.emit("status" /* STATUS */);
444
467
  }
445
- setCRAgentMessage(crID, message) {
446
- const cr = this.activeCRs.get(crID);
447
- if (!cr) return;
448
- cr.agentMessage = message;
468
+ setBackgroundAgentMessage(backgroundId, message) {
469
+ const bg = this.activeBackgroundInvestigations.get(backgroundId);
470
+ if (!bg) return;
471
+ bg.agentMessage = message;
449
472
  this.emit("agent-message" /* AGENT_MESSAGE */, { message });
450
473
  this.emit("status" /* STATUS */);
451
474
  }
@@ -456,14 +479,17 @@ var PRBEAgentState = class extends import_events.EventEmitter {
456
479
  this.emit("status" /* STATUS */);
457
480
  }
458
481
  }
459
- // ---------- CR state mutations ----------
460
- beginCR(id, query, slug, ticketId) {
461
- const cr = {
482
+ // ---------- Background investigation state mutations ----------
483
+ beginBackgroundInvestigation(id, query, slug, ticketId, source, sourceDetail) {
484
+ const bg = {
462
485
  id,
463
486
  query,
464
487
  slug,
465
488
  ticketId,
489
+ source,
490
+ sourceDetail,
466
491
  events: [],
492
+ conversationHistory: [],
467
493
  resolvedInteractions: [],
468
494
  isRunning: true,
469
495
  isCompleted: false,
@@ -472,20 +498,20 @@ var PRBEAgentState = class extends import_events.EventEmitter {
472
498
  summary: "",
473
499
  startedAt: /* @__PURE__ */ new Date()
474
500
  };
475
- this.activeCRs.set(id, cr);
476
- this.emit("cr-start" /* CR_START */, cr);
501
+ this.activeBackgroundInvestigations.set(id, bg);
502
+ this.emit("background-start" /* BACKGROUND_START */, bg);
477
503
  this.emit("status" /* STATUS */);
478
504
  }
479
- appendCREvent(crID, label, detail, completed = false) {
480
- const cr = this.activeCRs.get(crID);
481
- if (!cr) return;
482
- if (cr.events.length > 0) {
483
- const last = cr.events[cr.events.length - 1];
505
+ appendBackgroundEvent(backgroundId, label, detail, completed = false) {
506
+ const bg = this.activeBackgroundInvestigations.get(backgroundId);
507
+ if (!bg) return;
508
+ if (bg.events.length > 0) {
509
+ const last = bg.events[bg.events.length - 1];
484
510
  if (!last.isCompleted && !completed) {
485
511
  last.isCompleted = true;
486
512
  }
487
513
  }
488
- cr.events.push({
514
+ bg.events.push({
489
515
  id: (0, import_crypto.randomUUID)(),
490
516
  label,
491
517
  detail,
@@ -494,47 +520,47 @@ var PRBEAgentState = class extends import_events.EventEmitter {
494
520
  });
495
521
  this.emit("status" /* STATUS */);
496
522
  }
497
- attachCRObservation(crID, text) {
498
- const cr = this.activeCRs.get(crID);
499
- if (!cr || cr.events.length === 0) return;
500
- cr.events[cr.events.length - 1].detail = text;
523
+ attachBackgroundObservation(backgroundId, text) {
524
+ const bg = this.activeBackgroundInvestigations.get(backgroundId);
525
+ if (!bg || bg.events.length === 0) return;
526
+ bg.events[bg.events.length - 1].detail = text;
501
527
  this.emit("status" /* STATUS */);
502
528
  }
503
- completeCR(id, report) {
504
- const cr = this.activeCRs.get(id);
505
- if (!cr) return;
506
- this.activeCRs.delete(id);
507
- if (cr.events.length > 0) {
508
- cr.events[cr.events.length - 1].isCompleted = true;
529
+ completeBackgroundInvestigation(id, report) {
530
+ const bg = this.activeBackgroundInvestigations.get(id);
531
+ if (!bg) return;
532
+ this.activeBackgroundInvestigations.delete(id);
533
+ if (bg.events.length > 0) {
534
+ bg.events[bg.events.length - 1].isCompleted = true;
509
535
  }
510
- cr.events.push({
536
+ bg.events.push({
511
537
  id: (0, import_crypto.randomUUID)(),
512
538
  label: "Done",
513
539
  isCompleted: true,
514
540
  isExpanded: false
515
541
  });
516
- cr.isRunning = false;
517
- cr.isCompleted = true;
518
- cr.report = report;
519
- this.completedCRs.unshift(cr);
520
- this.emit("cr-complete" /* CR_COMPLETE */, cr);
542
+ bg.isRunning = false;
543
+ bg.isCompleted = true;
544
+ bg.report = report;
545
+ this.completedBackgroundInvestigations.unshift(bg);
546
+ this.emit("background-complete" /* BACKGROUND_COMPLETE */, bg);
521
547
  this.emit("status" /* STATUS */);
522
548
  }
523
- failCR(id, message) {
524
- const cr = this.activeCRs.get(id);
525
- if (!cr) return;
526
- this.activeCRs.delete(id);
527
- cr.events.push({
549
+ failBackgroundInvestigation(id, message) {
550
+ const bg = this.activeBackgroundInvestigations.get(id);
551
+ if (!bg) return;
552
+ this.activeBackgroundInvestigations.delete(id);
553
+ bg.events.push({
528
554
  id: (0, import_crypto.randomUUID)(),
529
555
  label: `Error: ${message}`,
530
556
  isCompleted: false,
531
557
  isExpanded: false
532
558
  });
533
- cr.isRunning = false;
534
- cr.isFailed = true;
535
- cr.errorMessage = message;
536
- this.completedCRs.unshift(cr);
537
- this.emit("cr-complete" /* CR_COMPLETE */, cr);
559
+ bg.isRunning = false;
560
+ bg.isFailed = true;
561
+ bg.errorMessage = message;
562
+ this.completedBackgroundInvestigations.unshift(bg);
563
+ this.emit("background-complete" /* BACKGROUND_COMPLETE */, bg);
538
564
  this.emit("status" /* STATUS */);
539
565
  }
540
566
  // ---------- Tickets ----------
@@ -548,6 +574,10 @@ var PRBEAgentState = class extends import_events.EventEmitter {
548
574
  this.emit("ticket-info" /* TICKET_INFO */, info);
549
575
  this.emit("status" /* STATUS */);
550
576
  }
577
+ updateAgentHistory(tickets) {
578
+ this.agentHistory = tickets;
579
+ this.emit("status" /* STATUS */);
580
+ }
551
581
  };
552
582
 
553
583
  // src/tools/index.ts
@@ -558,11 +588,13 @@ var InteractionType = /* @__PURE__ */ ((InteractionType2) => {
558
588
  InteractionType2["ASK_QUESTION"] = "ask_question";
559
589
  InteractionType2["REQUEST_PERMISSION"] = "request_permission";
560
590
  InteractionType2["REQUEST_PATH_ACCESS"] = "request_path_access";
591
+ InteractionType2["REVIEW_SANITIZED_OUTPUT"] = "review_sanitized_output";
561
592
  return InteractionType2;
562
593
  })(InteractionType || {});
563
594
  var InvestigationSource = /* @__PURE__ */ ((InvestigationSource2) => {
564
595
  InvestigationSource2["USER"] = "user";
565
596
  InvestigationSource2["CONTEXT_REQUEST"] = "context_request";
597
+ InvestigationSource2["EXTERNAL_REQUEST"] = "external_request";
566
598
  return InvestigationSource2;
567
599
  })(InvestigationSource || {});
568
600
 
@@ -1470,7 +1502,8 @@ var AskUserTool = class {
1470
1502
  const response = await this.requester.requestUserInteraction({
1471
1503
  type: "ask_question" /* ASK_QUESTION */,
1472
1504
  interactionId: (0, import_crypto3.randomUUID)(),
1473
- question: reason
1505
+ question,
1506
+ context: reason
1474
1507
  });
1475
1508
  const askResponse = response;
1476
1509
  return askResponse.answer;
@@ -1680,6 +1713,10 @@ var BashExecuteTool = class {
1680
1713
  return `Error: working directory '${cwdArg}' is outside auto-approved directories`;
1681
1714
  }
1682
1715
  cwd = resolved;
1716
+ } else if (this.autoApprovedDirs.length > 0) {
1717
+ cwd = this.autoApprovedDirs[0];
1718
+ } else {
1719
+ return "Error: no approved directories configured and no working directory specified";
1683
1720
  }
1684
1721
  const isSafe = this.isCommandSafe(command);
1685
1722
  if (!isSafe) {
@@ -1770,197 +1807,20 @@ var BashExecuteTool = class {
1770
1807
  }
1771
1808
  };
1772
1809
 
1773
- // src/history.ts
1774
- var fs2 = __toESM(require("fs"));
1775
- var path4 = __toESM(require("path"));
1776
- var os = __toESM(require("os"));
1777
- var crypto = __toESM(require("crypto"));
1778
- var HKDF_SALT = Buffer.from("prbe-history-encryption-salt", "utf-8");
1779
- var HKDF_INFO = Buffer.from("prbe-history-v1", "utf-8");
1780
- function getAppDataDir() {
1810
+ // src/agent.ts
1811
+ function getPersistencePath() {
1781
1812
  const appData = process.env["APPDATA"] || (process.platform === "darwin" ? path4.join(os.homedir(), "Library", "Application Support") : path4.join(os.homedir(), ".local", "share"));
1782
- return path4.join(appData, "prbe-agent");
1783
- }
1784
- function getHistoryDir() {
1785
- const dir = path4.join(getAppDataDir(), "history" /* HISTORY_DIR */);
1813
+ const dir = path4.join(appData, "prbe-agent");
1786
1814
  if (!fs2.existsSync(dir)) {
1787
1815
  fs2.mkdirSync(dir, { recursive: true });
1788
1816
  }
1789
- return dir;
1790
- }
1791
- function deriveKey(apiKey) {
1792
- return Buffer.from(
1793
- crypto.hkdfSync(
1794
- "sha256",
1795
- Buffer.from(apiKey, "utf-8"),
1796
- HKDF_SALT,
1797
- HKDF_INFO,
1798
- 32 /* KEY_LENGTH */
1799
- )
1800
- );
1801
- }
1802
- function encrypt(plaintext, key) {
1803
- const iv = crypto.randomBytes(12 /* IV_LENGTH */);
1804
- const cipher = crypto.createCipheriv(
1805
- "aes-256-gcm" /* ALGORITHM */,
1806
- key,
1807
- iv,
1808
- { authTagLength: 16 /* AUTH_TAG_LENGTH */ }
1809
- );
1810
- const encrypted = Buffer.concat([cipher.update(plaintext, "utf-8"), cipher.final()]);
1811
- const authTag = cipher.getAuthTag();
1812
- return Buffer.concat([iv, authTag, encrypted]);
1813
- }
1814
- function decrypt(data, key) {
1815
- const iv = data.subarray(0, 12 /* IV_LENGTH */);
1816
- const authTag = data.subarray(
1817
- 12 /* IV_LENGTH */,
1818
- 12 /* IV_LENGTH */ + 16 /* AUTH_TAG_LENGTH */
1819
- );
1820
- const ciphertext = data.subarray(
1821
- 12 /* IV_LENGTH */ + 16 /* AUTH_TAG_LENGTH */
1822
- );
1823
- const decipher = crypto.createDecipheriv(
1824
- "aes-256-gcm" /* ALGORITHM */,
1825
- key,
1826
- iv,
1827
- { authTagLength: 16 /* AUTH_TAG_LENGTH */ }
1828
- );
1829
- decipher.setAuthTag(authTag);
1830
- return decipher.update(ciphertext) + decipher.final("utf-8");
1831
- }
1832
- var HistoryStore = class {
1833
- key;
1834
- constructor(apiKey) {
1835
- this.key = deriveKey(apiKey);
1836
- }
1837
- load() {
1838
- const investigations = [];
1839
- const crs = [];
1840
- try {
1841
- const dir = getHistoryDir();
1842
- let files;
1843
- try {
1844
- files = fs2.readdirSync(dir);
1845
- } catch {
1846
- return { investigations, crs };
1847
- }
1848
- for (const filename of files) {
1849
- try {
1850
- const filePath = path4.join(dir, filename);
1851
- const raw = fs2.readFileSync(filePath);
1852
- const json = decrypt(raw, this.key);
1853
- if (filename.startsWith("inv-") && filename.endsWith(".json")) {
1854
- const item = JSON.parse(json);
1855
- investigations.push({
1856
- ...item,
1857
- completedAt: new Date(item.completedAt)
1858
- });
1859
- } else if (filename.startsWith("cr-") && filename.endsWith(".json")) {
1860
- const item = JSON.parse(json);
1861
- crs.push({
1862
- ...item,
1863
- startedAt: new Date(item.startedAt),
1864
- resolvedInteractions: item.resolvedInteractions ?? []
1865
- });
1866
- }
1867
- } catch {
1868
- console.warn(`[PRBEAgent] Skipping unreadable history file: ${filename}`);
1869
- }
1870
- }
1871
- } catch {
1872
- }
1873
- investigations.sort(
1874
- (a, b) => b.completedAt.getTime() - a.completedAt.getTime()
1875
- );
1876
- crs.sort((a, b) => b.startedAt.getTime() - a.startedAt.getTime());
1877
- return { investigations, crs };
1878
- }
1879
- save(investigations, crs) {
1880
- try {
1881
- const dir = getHistoryDir();
1882
- const desiredFiles = /* @__PURE__ */ new Set();
1883
- for (const inv of investigations) {
1884
- const filename = `inv-${inv.id}.json`;
1885
- desiredFiles.add(filename);
1886
- const data = {
1887
- id: inv.id,
1888
- query: inv.query,
1889
- report: inv.report,
1890
- summary: inv.summary,
1891
- ticketId: inv.ticketId,
1892
- events: inv.events,
1893
- resolvedInteractions: inv.resolvedInteractions,
1894
- conversationHistory: inv.conversationHistory,
1895
- completedAt: inv.completedAt.toISOString()
1896
- };
1897
- fs2.writeFileSync(
1898
- path4.join(dir, filename),
1899
- encrypt(JSON.stringify(data), this.key)
1900
- );
1901
- }
1902
- for (const cr of crs) {
1903
- const filename = `cr-${cr.id}.json`;
1904
- desiredFiles.add(filename);
1905
- const data = {
1906
- id: cr.id,
1907
- query: cr.query,
1908
- slug: cr.slug,
1909
- ticketId: cr.ticketId,
1910
- events: cr.events,
1911
- isRunning: cr.isRunning,
1912
- isCompleted: cr.isCompleted,
1913
- isFailed: cr.isFailed,
1914
- report: cr.report,
1915
- summary: cr.summary,
1916
- errorMessage: cr.errorMessage,
1917
- startedAt: cr.startedAt.toISOString(),
1918
- pendingInteraction: cr.pendingInteraction,
1919
- resolvedInteractions: cr.resolvedInteractions ?? []
1920
- };
1921
- fs2.writeFileSync(
1922
- path4.join(dir, filename),
1923
- encrypt(JSON.stringify(data), this.key)
1924
- );
1925
- }
1926
- try {
1927
- const existing = fs2.readdirSync(dir);
1928
- for (const filename of existing) {
1929
- if (!desiredFiles.has(filename)) {
1930
- fs2.unlinkSync(path4.join(dir, filename));
1931
- }
1932
- }
1933
- } catch {
1934
- }
1935
- } catch {
1936
- console.error("[PRBEAgent] Failed to save investigation history");
1937
- }
1938
- }
1939
- static clear() {
1940
- try {
1941
- const dir = path4.join(getAppDataDir(), "history" /* HISTORY_DIR */);
1942
- if (fs2.existsSync(dir)) {
1943
- fs2.rmSync(dir, { recursive: true, force: true });
1944
- }
1945
- } catch {
1946
- }
1947
- }
1948
- };
1949
-
1950
- // src/agent.ts
1951
- function getPersistencePath() {
1952
- const appData = process.env["APPDATA"] || (process.platform === "darwin" ? path5.join(os2.homedir(), "Library", "Application Support") : path5.join(os2.homedir(), ".local", "share"));
1953
- const dir = path5.join(appData, "prbe-agent");
1954
- if (!fs3.existsSync(dir)) {
1955
- fs3.mkdirSync(dir, { recursive: true });
1956
- }
1957
- return path5.join(dir, "agent-state.json");
1817
+ return path4.join(dir, "agent-state.json");
1958
1818
  }
1959
1819
  function loadPersistedData() {
1960
1820
  try {
1961
1821
  const filePath = getPersistencePath();
1962
- if (fs3.existsSync(filePath)) {
1963
- const raw = fs3.readFileSync(filePath, "utf-8");
1822
+ if (fs2.existsSync(filePath)) {
1823
+ const raw = fs2.readFileSync(filePath, "utf-8");
1964
1824
  return JSON.parse(raw);
1965
1825
  }
1966
1826
  } catch {
@@ -1970,7 +1830,7 @@ function loadPersistedData() {
1970
1830
  function savePersistedData(data) {
1971
1831
  try {
1972
1832
  const filePath = getPersistencePath();
1973
- fs3.writeFileSync(filePath, JSON.stringify(data, null, 2), "utf-8");
1833
+ fs2.writeFileSync(filePath, JSON.stringify(data, null, 2), "utf-8");
1974
1834
  } catch {
1975
1835
  console.error("[PRBEAgent] Failed to save persisted data");
1976
1836
  }
@@ -1990,8 +1850,7 @@ var PRBEAgent = class _PRBEAgent {
1990
1850
  persistedData;
1991
1851
  fetchAbortController = null;
1992
1852
  currentInvestigationSource = "user" /* USER */;
1993
- currentCRId = null;
1994
- historyStore;
1853
+ currentBackgroundId = null;
1995
1854
  /** Files flagged during the current tool call — uploaded immediately after the tool returns. */
1996
1855
  pendingFlaggedFiles = [];
1997
1856
  // ---------- User Identifier ----------
@@ -2007,30 +1866,6 @@ var PRBEAgent = class _PRBEAgent {
2007
1866
  savePersistedData(this.persistedData);
2008
1867
  return newID;
2009
1868
  }
2010
- get trackedSessionIDs() {
2011
- return this.persistedData.sessionIds ?? [];
2012
- }
2013
- set trackedSessionIDs(ids) {
2014
- this.persistedData.sessionIds = ids;
2015
- savePersistedData(this.persistedData);
2016
- this.state.updateTrackedSessionIDs(ids);
2017
- this.syncPolling(ids.length > 0);
2018
- }
2019
- syncPolling(hasSessions) {
2020
- if (this.config.backgroundPolling && hasSessions) {
2021
- if (this.pollingTimer === null) {
2022
- this.startPolling();
2023
- }
2024
- } else if (!hasSessions) {
2025
- this.stopPolling();
2026
- }
2027
- }
2028
- addTrackedSession(id) {
2029
- const ids = this.trackedSessionIDs;
2030
- if (!ids.includes(id)) {
2031
- this.trackedSessionIDs = [...ids, id];
2032
- }
2033
- }
2034
1869
  // ---------- Constructor ----------
2035
1870
  constructor(config) {
2036
1871
  this.config = {
@@ -2050,11 +1885,7 @@ var PRBEAgent = class _PRBEAgent {
2050
1885
  this.state = new PRBEAgentState();
2051
1886
  this.logCapture = new PRBELogCapture(this.config.maxLogEntries);
2052
1887
  this.persistedData = loadPersistedData();
2053
- this.historyStore = new HistoryStore(this.config.apiKey);
2054
1888
  void this.agentID;
2055
- const history = this.historyStore.load();
2056
- this.state.completedInvestigations = history.investigations;
2057
- this.state.completedCRs = history.crs;
2058
1889
  const roots = this.config.autoApprovedDirs;
2059
1890
  const requester = this.interactionHandler ? this : void 0;
2060
1891
  const grantedPaths = this.grantedPaths;
@@ -2086,8 +1917,8 @@ var PRBEAgent = class _PRBEAgent {
2086
1917
  [{ name: "message", type: "STRING" /* STRING */, description: "Message for the user", required: true }],
2087
1918
  async (args) => {
2088
1919
  const message = args["message"] ?? "";
2089
- if (this.currentInvestigationSource === "context_request" /* CONTEXT_REQUEST */ && this.currentCRId) {
2090
- this.state.setCRAgentMessage(this.currentCRId, message);
1920
+ if (this.currentInvestigationSource !== "user" /* USER */ && this.currentBackgroundId) {
1921
+ this.state.setBackgroundAgentMessage(this.currentBackgroundId, message);
2091
1922
  } else {
2092
1923
  this.state.setAgentMessage(message);
2093
1924
  }
@@ -2104,9 +1935,8 @@ var PRBEAgent = class _PRBEAgent {
2104
1935
  if (config.ipcMain) {
2105
1936
  this.hookRendererLogs(config.ipcMain, config.rendererLogChannel ?? "prbe-renderer-log");
2106
1937
  }
2107
- const existingSessions = this.trackedSessionIDs;
2108
- if (existingSessions.length > 0) {
2109
- this.trackedSessionIDs = existingSessions;
1938
+ if (this.config.backgroundPolling) {
1939
+ this.startPolling();
2110
1940
  }
2111
1941
  }
2112
1942
  // ---------- Log integration ----------
@@ -2150,8 +1980,8 @@ var PRBEAgent = class _PRBEAgent {
2150
1980
  get investigationSource() {
2151
1981
  return this.currentInvestigationSource;
2152
1982
  }
2153
- sendConversationMessage(content) {
2154
- this.activeConnection?.sendConversationMessage(content);
1983
+ sendConversationMessage(content, role, label) {
1984
+ this.activeConnection?.sendConversationMessage(content, role, label);
2155
1985
  }
2156
1986
  async requestUserInteraction(payload) {
2157
1987
  if (!this.interactionHandler) {
@@ -2160,15 +1990,31 @@ var PRBEAgent = class _PRBEAgent {
2160
1990
  "No interaction handler configured"
2161
1991
  );
2162
1992
  }
2163
- if (this.currentInvestigationSource === "context_request" /* CONTEXT_REQUEST */ && this.currentCRId) {
2164
- this.state.setCRPendingInteraction(this.currentCRId, payload);
1993
+ if (payload.type === "request_permission" /* REQUEST_PERMISSION */) {
1994
+ const p = payload;
1995
+ const content = p.reason ? `${p.command}
1996
+
1997
+ ${p.reason}` : p.command;
1998
+ this.sendConversationMessage(content, "agent" /* Agent */, p.action);
1999
+ } else if (payload.type === "request_path_access" /* REQUEST_PATH_ACCESS */) {
2000
+ const p = payload;
2001
+ const content = p.reason ? `${p.path}
2002
+
2003
+ ${p.reason}` : p.path;
2004
+ this.sendConversationMessage(content, "agent" /* Agent */, "Request path access");
2005
+ } else if (payload.type === "ask_question" /* ASK_QUESTION */) {
2006
+ const p = payload;
2007
+ this.sendConversationMessage(p.question, "agent" /* Agent */);
2008
+ }
2009
+ if (this.currentInvestigationSource !== "user" /* USER */ && this.currentBackgroundId) {
2010
+ this.state.setBackgroundPendingInteraction(this.currentBackgroundId, payload);
2165
2011
  } else {
2166
2012
  this.state.setPendingInteraction(payload);
2167
2013
  }
2168
2014
  try {
2169
2015
  const response = await this.interactionHandler.handleInteraction(payload);
2170
- if (this.currentInvestigationSource === "context_request" /* CONTEXT_REQUEST */ && this.currentCRId) {
2171
- this.state.resolveCRInteraction(this.currentCRId, response);
2016
+ if (this.currentInvestigationSource !== "user" /* USER */ && this.currentBackgroundId) {
2017
+ this.state.resolveBackgroundInteraction(this.currentBackgroundId, response);
2172
2018
  } else {
2173
2019
  this.state.resolveInteraction(response);
2174
2020
  }
@@ -2181,8 +2027,8 @@ var PRBEAgent = class _PRBEAgent {
2181
2027
  }
2182
2028
  return response;
2183
2029
  } catch (err) {
2184
- if (this.currentInvestigationSource === "context_request" /* CONTEXT_REQUEST */ && this.currentCRId) {
2185
- this.state.clearCRPendingInteraction(this.currentCRId);
2030
+ if (this.currentInvestigationSource !== "user" /* USER */ && this.currentBackgroundId) {
2031
+ this.state.clearBackgroundPendingInteraction(this.currentBackgroundId);
2186
2032
  } else {
2187
2033
  this.state.clearPendingInteraction();
2188
2034
  }
@@ -2254,58 +2100,100 @@ var PRBEAgent = class _PRBEAgent {
2254
2100
  new PRBEClosureTool(name, description, parameters, handler, options)
2255
2101
  );
2256
2102
  }
2257
- /**
2258
- * User-initiated investigation. Updates `state` events/report directly.
2259
- */
2260
- async investigate(query, contextRequestID) {
2261
- this.userCancelled = false;
2262
- this.currentInvestigationSource = "user" /* USER */;
2263
- this.currentCRId = null;
2264
- this.state.beginInvestigation(query);
2103
+ // ---------- Unified Investigation ----------
2104
+ async runInvestigation(opts) {
2105
+ this.currentInvestigationSource = opts.source;
2106
+ this.currentBackgroundId = opts.sourceId ?? null;
2107
+ const isBackground = opts.source !== "user" /* USER */;
2265
2108
  const emitter = (status) => {
2266
2109
  switch (status.type) {
2267
2110
  case "started" /* STARTED */:
2268
- this.state.appendEvent("Starting investigation...");
2111
+ if (isBackground && opts.sourceId) {
2112
+ this.state.appendBackgroundEvent(opts.sourceId, "Starting investigation...");
2113
+ } else {
2114
+ this.state.appendEvent("Starting investigation...");
2115
+ }
2269
2116
  break;
2270
2117
  case "thinking" /* THINKING */:
2271
2118
  break;
2272
2119
  case "tool_call" /* TOOL_CALL */:
2273
- this.state.appendEvent(status.label);
2120
+ if (isBackground && opts.sourceId) {
2121
+ this.state.appendBackgroundEvent(opts.sourceId, status.label);
2122
+ } else {
2123
+ this.state.appendEvent(status.label);
2124
+ }
2274
2125
  break;
2275
2126
  case "observation" /* OBSERVATION */:
2276
- this.state.attachObservation(status.text);
2127
+ if (isBackground && opts.sourceId) {
2128
+ this.state.attachBackgroundObservation(opts.sourceId, status.text);
2129
+ } else {
2130
+ this.state.attachObservation(status.text);
2131
+ }
2277
2132
  break;
2278
2133
  case "thought" /* THOUGHT */:
2279
- this.state.appendEvent("Thinking", status.text);
2134
+ if (isBackground && opts.sourceId) {
2135
+ this.state.appendBackgroundEvent(opts.sourceId, "Thinking", status.text);
2136
+ } else {
2137
+ this.state.appendEvent("Thinking", status.text);
2138
+ }
2280
2139
  break;
2281
2140
  case "completed" /* COMPLETED */:
2282
- this.state.completeInvestigation(status.report, status.ticketId);
2283
- this.historyStore.save(this.state.completedInvestigations, this.state.completedCRs);
2141
+ if (isBackground && opts.sourceId) {
2142
+ this.state.completeBackgroundInvestigation(opts.sourceId, status.report);
2143
+ } else {
2144
+ this.state.completeInvestigation(status.report, status.ticketId);
2145
+ }
2284
2146
  break;
2285
2147
  case "error" /* ERROR */:
2286
- this.state.failInvestigation(status.message);
2148
+ if (isBackground && opts.sourceId) {
2149
+ this.state.failBackgroundInvestigation(opts.sourceId, status.message);
2150
+ } else {
2151
+ this.state.failInvestigation(status.message);
2152
+ }
2153
+ break;
2154
+ case "privacy_sanitizing" /* PRIVACY_SANITIZING */:
2155
+ if (isBackground && opts.sourceId) {
2156
+ this.state.appendBackgroundEvent(opts.sourceId, "Sanitizing data for privacy review...");
2157
+ } else {
2158
+ this.state.appendEvent("Sanitizing data for privacy review...");
2159
+ }
2160
+ this.state.setPrivacySanitizing(true);
2287
2161
  break;
2288
2162
  }
2289
2163
  };
2290
2164
  const result = await this.connectToProxy(
2291
- query,
2292
- contextRequestID,
2165
+ opts.query,
2166
+ opts.contextRequestId,
2167
+ opts.externalRequestId,
2168
+ opts.externalRequestSource,
2169
+ opts.externalRequestSourceDetail,
2293
2170
  emitter,
2294
- () => this.userCancelled
2171
+ opts.cancellable ? () => this.userCancelled : () => false,
2172
+ opts.ticketId
2295
2173
  );
2296
2174
  this.currentInvestigationSource = "user" /* USER */;
2297
- this.currentCRId = null;
2298
- if (result?.sessionId) {
2299
- this.addTrackedSession(result.sessionId);
2300
- } else if (!result) {
2301
- if (this.state.isInvestigating) {
2302
- const message = this.userCancelled ? "Investigation cancelled" : "Investigation ended unexpectedly";
2303
- this.state.failInvestigation(message);
2304
- }
2175
+ this.currentBackgroundId = null;
2176
+ return result;
2177
+ }
2178
+ /**
2179
+ * User-initiated investigation. Updates `state` events/report directly.
2180
+ */
2181
+ async investigate(query, contextRequestID) {
2182
+ this.userCancelled = false;
2183
+ this.state.beginInvestigation(query);
2184
+ const result = await this.runInvestigation({
2185
+ query,
2186
+ source: "user" /* USER */,
2187
+ contextRequestId: contextRequestID,
2188
+ cancellable: true
2189
+ });
2190
+ if (!result && this.state.isInvestigating) {
2191
+ const message = this.userCancelled ? "Investigation cancelled" : "Investigation ended unexpectedly";
2192
+ this.state.failInvestigation(message);
2305
2193
  }
2306
2194
  }
2307
2195
  /**
2308
- * Cancel user-initiated investigation only (does not cancel background CRs).
2196
+ * Cancel user-initiated investigation only (does not cancel background investigations).
2309
2197
  */
2310
2198
  cancelInvestigation() {
2311
2199
  this.userCancelled = true;
@@ -2327,41 +2215,21 @@ var PRBEAgent = class _PRBEAgent {
2327
2215
  this.stopPolling();
2328
2216
  }
2329
2217
  /**
2330
- * Poll the backend for context requests on tracked tickets.
2331
- * Resolves session IDs → ticket IDs first, then polls with ticket IDs.
2218
+ * Poll the backend for context requests and external requests.
2332
2219
  */
2333
2220
  async poll() {
2334
- const sessionIDs = this.trackedSessionIDs;
2335
- if (sessionIDs.length === 0) return null;
2336
2221
  try {
2337
- const resolved = await this.resolveSessions(sessionIDs);
2338
- const returnedSessionIDs = new Set(
2339
- resolved.tickets.flatMap((t) => t.session_ids)
2340
- );
2341
- const resolvedSessionIDs = new Set(
2342
- resolved.tickets.filter((t) => t.status === "resolved").flatMap((t) => t.session_ids)
2343
- );
2344
- const survivingSessions = sessionIDs.filter(
2345
- (id) => returnedSessionIDs.has(id) && !resolvedSessionIDs.has(id)
2346
- );
2347
- if (survivingSessions.length !== sessionIDs.length) {
2348
- this.trackedSessionIDs = survivingSessions;
2222
+ const request = { agent_id: this.agentID };
2223
+ const response = await this.post("/api/agent/poll", request);
2224
+ for (const cr of response.context_requests) {
2225
+ if (!cr.is_active) continue;
2226
+ if (this.state.activeBackgroundInvestigations.has(cr.id)) continue;
2227
+ await this.investigateForCR(cr, cr.ticket_id);
2349
2228
  }
2350
- const ticketIDs = resolved.tickets.filter((t) => t.status !== "resolved").map((t) => t.ticket_id);
2351
- if (ticketIDs.length === 0) return { tickets: [] };
2352
- const request = {
2353
- agent_id: this.agentID,
2354
- ticket_ids: ticketIDs
2355
- };
2356
- const response = await this.post(
2357
- "/api/agent/poll",
2358
- request
2359
- );
2360
- for (const ticket of response.tickets) {
2361
- for (const cr of ticket.context_requests) {
2362
- if (!cr.is_active) continue;
2363
- await this.investigateForCR(cr, ticket.ticket_id);
2364
- }
2229
+ for (const er of response.external_requests) {
2230
+ if (!er.is_active) continue;
2231
+ if (this.state.activeBackgroundInvestigations.has(er.id)) continue;
2232
+ await this.investigateForER(er);
2365
2233
  }
2366
2234
  return response;
2367
2235
  } catch {
@@ -2369,27 +2237,44 @@ var PRBEAgent = class _PRBEAgent {
2369
2237
  }
2370
2238
  }
2371
2239
  /**
2372
- * Fetch ticket display info for all tracked sessions.
2373
- * Resolves session IDs → ticket IDs first, then fetches ticket info.
2240
+ * Fetch agent history (tickets + sessions) from the backend.
2374
2241
  */
2375
- async fetchTicketInfo() {
2376
- const sessionIDs = this.trackedSessionIDs;
2377
- if (sessionIDs.length === 0) return [];
2242
+ async fetchHistory() {
2378
2243
  try {
2379
- const resolved = await this.resolveSessions(sessionIDs);
2380
- const ticketIDs = resolved.tickets.map((t) => t.ticket_id);
2381
- if (ticketIDs.length === 0) return [];
2382
- const request = { ticket_ids: ticketIDs };
2383
2244
  const response = await this.post(
2384
- "/api/agent/tickets",
2385
- request
2245
+ "/api/agent/history",
2246
+ { agent_id: this.agentID }
2386
2247
  );
2387
- this.state.updateTicketInfo(response.tickets);
2388
- return response.tickets;
2248
+ this.state.updateAgentHistory(response.tickets);
2249
+ return response;
2389
2250
  } catch {
2390
- return [];
2251
+ return null;
2391
2252
  }
2392
2253
  }
2254
+ async fetchSanitizedFile(url) {
2255
+ const res = await fetch(url, {
2256
+ headers: { "X-API-Key": this.config.apiKey }
2257
+ });
2258
+ if (!res.ok) throw new Error(`Failed to fetch sanitized file: ${res.status}`);
2259
+ const buffer = Buffer.from(await res.arrayBuffer());
2260
+ if (!_PRBEAgent.isTextContent(buffer)) {
2261
+ return { isText: false, url };
2262
+ }
2263
+ return { isText: true, content: buffer.toString("utf-8"), url };
2264
+ }
2265
+ /**
2266
+ * Detect whether a buffer contains text by checking for null bytes
2267
+ * and verifying the content is valid UTF-8.
2268
+ */
2269
+ static isTextContent(buffer) {
2270
+ const checkLength = Math.min(buffer.length, 8192);
2271
+ for (let i = 0; i < checkLength; i++) {
2272
+ if (buffer[i] === 0) return false;
2273
+ }
2274
+ const decoded = buffer.toString("utf-8");
2275
+ const reEncoded = Buffer.from(decoded, "utf-8");
2276
+ return buffer.length === reEncoded.length;
2277
+ }
2393
2278
  stopPolling() {
2394
2279
  if (this.pollingTimer !== null) {
2395
2280
  clearInterval(this.pollingTimer);
@@ -2397,7 +2282,7 @@ var PRBEAgent = class _PRBEAgent {
2397
2282
  }
2398
2283
  }
2399
2284
  resumePolling() {
2400
- if (this.trackedSessionIDs.length === 0) return;
2285
+ if (!this.config.backgroundPolling) return;
2401
2286
  this.startPolling();
2402
2287
  }
2403
2288
  /**
@@ -2415,13 +2300,11 @@ var PRBEAgent = class _PRBEAgent {
2415
2300
  this.stopPolling();
2416
2301
  this.persistedData = {};
2417
2302
  savePersistedData(this.persistedData);
2418
- HistoryStore.clear();
2419
2303
  this.state.resetInvestigation();
2420
2304
  this.state.completedInvestigations = [];
2421
- this.state.activeCRs.clear();
2422
- this.state.completedCRs = [];
2423
- this.state.trackedSessionIDs = [];
2424
- this.state.ticketInfo = [];
2305
+ this.state.activeBackgroundInvestigations.clear();
2306
+ this.state.completedBackgroundInvestigations = [];
2307
+ this.state.agentHistory = [];
2425
2308
  this.state.emit("status" /* STATUS */);
2426
2309
  }
2427
2310
  /**
@@ -2430,61 +2313,38 @@ var PRBEAgent = class _PRBEAgent {
2430
2313
  static clearPersistedData() {
2431
2314
  try {
2432
2315
  const filePath = getPersistencePath();
2433
- if (fs3.existsSync(filePath)) {
2434
- fs3.unlinkSync(filePath);
2316
+ if (fs2.existsSync(filePath)) {
2317
+ fs2.unlinkSync(filePath);
2435
2318
  }
2436
- HistoryStore.clear();
2437
2319
  } catch {
2438
2320
  }
2439
2321
  }
2440
- // ---------- CR Investigation ----------
2322
+ // ---------- Background Investigation ----------
2441
2323
  async investigateForCR(cr, ticketId) {
2442
- const crID = cr.id;
2443
- this.currentInvestigationSource = "context_request" /* CONTEXT_REQUEST */;
2444
- this.currentCRId = crID;
2445
- this.state.beginCR(crID, cr.query, cr.slug ?? void 0, ticketId);
2446
- const emitter = (status) => {
2447
- switch (status.type) {
2448
- case "started" /* STARTED */:
2449
- this.state.appendCREvent(crID, "Starting investigation...");
2450
- break;
2451
- case "thinking" /* THINKING */:
2452
- break;
2453
- case "tool_call" /* TOOL_CALL */:
2454
- this.state.appendCREvent(crID, status.label);
2455
- break;
2456
- case "observation" /* OBSERVATION */:
2457
- this.state.attachCRObservation(crID, status.text);
2458
- break;
2459
- case "thought" /* THOUGHT */:
2460
- this.state.appendCREvent(crID, "Thinking", status.text);
2461
- break;
2462
- case "completed" /* COMPLETED */:
2463
- this.state.completeCR(crID, status.report);
2464
- this.historyStore.save(this.state.completedInvestigations, this.state.completedCRs);
2465
- break;
2466
- case "error" /* ERROR */:
2467
- this.state.failCR(crID, status.message);
2468
- this.historyStore.save(this.state.completedInvestigations, this.state.completedCRs);
2469
- break;
2470
- }
2471
- };
2472
- const result = await this.connectToProxy(
2473
- cr.query,
2474
- crID,
2475
- emitter,
2476
- () => false,
2477
- // CRs are not user-cancellable
2478
- ticketId
2479
- );
2480
- this.currentInvestigationSource = "user" /* USER */;
2481
- this.currentCRId = null;
2482
- if (result?.sessionId) {
2483
- this.addTrackedSession(result.sessionId);
2484
- }
2324
+ this.state.beginBackgroundInvestigation(cr.id, cr.query, cr.slug ?? void 0, ticketId);
2325
+ await this.runInvestigation({
2326
+ query: cr.query,
2327
+ source: "context_request" /* CONTEXT_REQUEST */,
2328
+ sourceId: cr.id,
2329
+ contextRequestId: cr.id,
2330
+ ticketId,
2331
+ cancellable: false
2332
+ });
2333
+ }
2334
+ async investigateForER(er) {
2335
+ this.state.beginBackgroundInvestigation(er.id, er.query, void 0, void 0, er.source, er.source_detail);
2336
+ await this.runInvestigation({
2337
+ query: er.query,
2338
+ source: "external_request" /* EXTERNAL_REQUEST */,
2339
+ sourceId: er.id,
2340
+ externalRequestId: er.id,
2341
+ externalRequestSource: er.source,
2342
+ externalRequestSourceDetail: er.source_detail,
2343
+ cancellable: false
2344
+ });
2485
2345
  }
2486
2346
  // ---------- WebSocket Investigation ----------
2487
- connectToProxy(query, contextRequestID, emit, isCancelled, ticketId) {
2347
+ connectToProxy(query, contextRequestID, externalRequestId, externalRequestSource, externalRequestSourceDetail, emit, isCancelled, ticketId) {
2488
2348
  return new Promise((resolve2) => {
2489
2349
  const wsUrl = `${MIDDLEWARE_URL}/api/agent/client/ws`;
2490
2350
  let uploadBaseUrl;
@@ -2504,8 +2364,8 @@ var PRBEAgent = class _PRBEAgent {
2504
2364
  uploadBaseUrl = url;
2505
2365
  }),
2506
2366
  (message) => {
2507
- if (!isCancelled()) emit({ type: "error" /* ERROR */, message });
2508
- finish(null);
2367
+ if (!resolved && !isCancelled()) emit({ type: "error" /* ERROR */, message });
2368
+ if (!resolved) finish(null);
2509
2369
  },
2510
2370
  () => {
2511
2371
  if (!resolved) {
@@ -2539,11 +2399,14 @@ var PRBEAgent = class _PRBEAgent {
2539
2399
  const startMetadata = {
2540
2400
  agent_id: this.agentID,
2541
2401
  custom_tools: toolDeclarations,
2542
- platform: os2.platform(),
2543
- os_version: os2.release(),
2544
- arch: os2.arch()
2402
+ platform: os.platform(),
2403
+ os_version: os.release(),
2404
+ arch: os.arch()
2545
2405
  };
2546
2406
  if (contextRequestID) startMetadata["context_request_id"] = contextRequestID;
2407
+ if (externalRequestId) startMetadata["external_request_id"] = externalRequestId;
2408
+ if (externalRequestSource) startMetadata["external_request_source"] = externalRequestSource;
2409
+ if (externalRequestSourceDetail) startMetadata["external_request_source_detail"] = externalRequestSourceDetail;
2547
2410
  if (ticketId) startMetadata["ticket_id"] = ticketId;
2548
2411
  if (this.appDataPath) startMetadata["app_data_path"] = this.appDataPath;
2549
2412
  const enrichedMetadata = { ...this.sessionMetadata };
@@ -2591,7 +2454,7 @@ var PRBEAgent = class _PRBEAgent {
2591
2454
  const uploadPrefix = this.extractUploadPrefix(uploadBaseUrl);
2592
2455
  const uploadedRefs = [];
2593
2456
  for (const file of this.pendingFlaggedFiles) {
2594
- const filename = path5.basename(file.originalPath);
2457
+ const filename = path4.basename(file.originalPath);
2595
2458
  const safeName = encodeURIComponent(filename);
2596
2459
  const storagePath = `${uploadPrefix}/${safeName}`;
2597
2460
  uploadedRefs.push({
@@ -2636,8 +2499,47 @@ var PRBEAgent = class _PRBEAgent {
2636
2499
  case "conversation_update" /* CONVERSATION_UPDATE */: {
2637
2500
  try {
2638
2501
  const entry = JSON.parse(msg.content ?? "{}");
2639
- this.state.appendConversation(entry);
2502
+ if (this.currentInvestigationSource !== "user" /* USER */ && this.currentBackgroundId) {
2503
+ this.state.appendBackgroundConversation(this.currentBackgroundId, entry);
2504
+ } else {
2505
+ this.state.appendConversation(entry);
2506
+ }
2507
+ } catch {
2508
+ }
2509
+ break;
2510
+ }
2511
+ case "privacy_sanitizing" /* PRIVACY_SANITIZING */: {
2512
+ emit({ type: "privacy_sanitizing" /* PRIVACY_SANITIZING */ });
2513
+ break;
2514
+ }
2515
+ case "privacy_review" /* PRIVACY_REVIEW */: {
2516
+ const sanitizedText = msg.content ?? "";
2517
+ const issues = msg.metadata?.issues ?? [];
2518
+ const files = msg.metadata?.files ?? [];
2519
+ const summary = msg.metadata?.summary ?? "";
2520
+ const payload = {
2521
+ type: "review_sanitized_output" /* REVIEW_SANITIZED_OUTPUT */,
2522
+ interactionId: (0, import_crypto5.randomUUID)(),
2523
+ sanitizedAnalysis: sanitizedText,
2524
+ files,
2525
+ summary,
2526
+ issues
2527
+ };
2528
+ try {
2529
+ const response = await this.requestUserInteraction(payload);
2530
+ const reviewResponse = response;
2531
+ conn.send({
2532
+ type: "privacy_review_response" /* PRIVACY_REVIEW_RESPONSE */,
2533
+ metadata: {
2534
+ approved: reviewResponse.approved,
2535
+ ...reviewResponse.editedText ? { editedText: reviewResponse.editedText } : {}
2536
+ }
2537
+ });
2640
2538
  } catch {
2539
+ conn.send({
2540
+ type: "privacy_review_response" /* PRIVACY_REVIEW_RESPONSE */,
2541
+ metadata: { approved: false }
2542
+ });
2641
2543
  }
2642
2544
  break;
2643
2545
  }
@@ -2694,24 +2596,9 @@ var PRBEAgent = class _PRBEAgent {
2694
2596
  startPolling() {
2695
2597
  this.stopPolling();
2696
2598
  this.pollingTimer = setInterval(() => {
2697
- void this.poll().then(() => {
2698
- if (this.trackedSessionIDs.length === 0) {
2699
- this.stopPolling();
2700
- }
2701
- });
2599
+ void this.poll();
2702
2600
  }, this.config.pollingInterval);
2703
2601
  }
2704
- // ---------- Session Resolution ----------
2705
- async resolveSessions(sessionIDs) {
2706
- const request = {
2707
- agent_id: this.agentID,
2708
- session_ids: sessionIDs
2709
- };
2710
- return this.post(
2711
- "/api/agent/resolve-sessions",
2712
- request
2713
- );
2714
- }
2715
2602
  // ---------- Networking ----------
2716
2603
  /**
2717
2604
  * Abort all in-flight fetch requests to prevent orphaned DNS lookups
@@ -2758,6 +2645,7 @@ var PRBEAgent = class _PRBEAgent {
2758
2645
  // src/serialization.ts
2759
2646
  var DEFAULT_PRBE_STATE = {
2760
2647
  isInvestigating: false,
2648
+ isPrivacySanitizing: false,
2761
2649
  events: [],
2762
2650
  report: "",
2763
2651
  summary: "",
@@ -2765,34 +2653,38 @@ var DEFAULT_PRBE_STATE = {
2765
2653
  resolvedInteractions: [],
2766
2654
  conversationHistory: [],
2767
2655
  completedInvestigations: [],
2768
- activeCRs: [],
2769
- completedCRs: [],
2770
- trackedSessionIDs: [],
2656
+ activeBackgroundInvestigations: [],
2657
+ completedBackgroundInvestigations: [],
2771
2658
  ticketInfo: [],
2659
+ agentHistory: [],
2772
2660
  hasActiveWork: false
2773
2661
  };
2774
- function serializeCR(cr) {
2662
+ function serializeBackgroundInvestigation(bg) {
2775
2663
  return {
2776
- id: cr.id,
2777
- query: cr.query,
2778
- slug: cr.slug,
2779
- ticketId: cr.ticketId,
2780
- events: cr.events,
2781
- isRunning: cr.isRunning,
2782
- isCompleted: cr.isCompleted,
2783
- isFailed: cr.isFailed,
2784
- report: cr.report,
2785
- summary: cr.summary,
2786
- errorMessage: cr.errorMessage,
2787
- agentMessage: cr.agentMessage,
2788
- startedAt: cr.startedAt.toISOString(),
2789
- pendingInteraction: cr.pendingInteraction,
2790
- resolvedInteractions: cr.resolvedInteractions ?? []
2664
+ id: bg.id,
2665
+ query: bg.query,
2666
+ slug: bg.slug,
2667
+ ticketId: bg.ticketId,
2668
+ source: bg.source,
2669
+ sourceDetail: bg.sourceDetail,
2670
+ events: bg.events,
2671
+ isRunning: bg.isRunning,
2672
+ isCompleted: bg.isCompleted,
2673
+ isFailed: bg.isFailed,
2674
+ report: bg.report,
2675
+ summary: bg.summary,
2676
+ errorMessage: bg.errorMessage,
2677
+ agentMessage: bg.agentMessage,
2678
+ startedAt: bg.startedAt.toISOString(),
2679
+ pendingInteraction: bg.pendingInteraction,
2680
+ resolvedInteractions: bg.resolvedInteractions ?? [],
2681
+ conversationHistory: bg.conversationHistory ?? []
2791
2682
  };
2792
2683
  }
2793
2684
  function serializePRBEState(state) {
2794
2685
  return {
2795
2686
  isInvestigating: state.isInvestigating,
2687
+ isPrivacySanitizing: state.isPrivacySanitizing,
2796
2688
  events: state.events,
2797
2689
  report: state.report,
2798
2690
  summary: state.summary,
@@ -2813,10 +2705,10 @@ function serializePRBEState(state) {
2813
2705
  conversationHistory: inv.conversationHistory,
2814
2706
  completedAt: inv.completedAt.toISOString()
2815
2707
  })),
2816
- activeCRs: Array.from(state.activeCRs.values()).map(serializeCR),
2817
- completedCRs: state.completedCRs.map(serializeCR),
2818
- trackedSessionIDs: state.trackedSessionIDs,
2708
+ activeBackgroundInvestigations: Array.from(state.activeBackgroundInvestigations.values()).map(serializeBackgroundInvestigation),
2709
+ completedBackgroundInvestigations: state.completedBackgroundInvestigations.map(serializeBackgroundInvestigation),
2819
2710
  ticketInfo: state.ticketInfo,
2711
+ agentHistory: state.agentHistory,
2820
2712
  hasActiveWork: state.hasActiveWork
2821
2713
  };
2822
2714
  }