@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.mjs CHANGED
@@ -1,10 +1,10 @@
1
1
  // package.json
2
- var version = "0.1.17";
2
+ var version = "0.1.19";
3
3
 
4
4
  // src/agent.ts
5
- import * as fs3 from "fs";
6
- import * as path5 from "path";
7
- import * as os2 from "os";
5
+ import * as fs2 from "fs";
6
+ import * as path4 from "path";
7
+ import * as os from "os";
8
8
  import { randomUUID as randomUUID5 } from "crypto";
9
9
 
10
10
  // src/models.ts
@@ -25,6 +25,9 @@ var WSMessageType = /* @__PURE__ */ ((WSMessageType2) => {
25
25
  WSMessageType2["COMPLETE"] = "complete";
26
26
  WSMessageType2["ERROR"] = "error";
27
27
  WSMessageType2["PING"] = "ping";
28
+ WSMessageType2["PRIVACY_SANITIZING"] = "privacy_sanitizing";
29
+ WSMessageType2["PRIVACY_REVIEW"] = "privacy_review";
30
+ WSMessageType2["PRIVACY_REVIEW_RESPONSE"] = "privacy_review_response";
28
31
  return WSMessageType2;
29
32
  })(WSMessageType || {});
30
33
  var ConversationRole = /* @__PURE__ */ ((ConversationRole3) => {
@@ -82,6 +85,7 @@ var PRBEAgentStatusType = /* @__PURE__ */ ((PRBEAgentStatusType2) => {
82
85
  PRBEAgentStatusType2["COMPLETED"] = "completed";
83
86
  PRBEAgentStatusType2["ERROR"] = "error";
84
87
  PRBEAgentStatusType2["AWAITING_INTERACTION"] = "awaiting_interaction";
88
+ PRBEAgentStatusType2["PRIVACY_SANITIZING"] = "privacy_sanitizing";
85
89
  return PRBEAgentStatusType2;
86
90
  })(PRBEAgentStatusType || {});
87
91
  var PRBEAgentErrorType = /* @__PURE__ */ ((PRBEAgentErrorType2) => {
@@ -147,10 +151,14 @@ var InvestigationConnection = class {
147
151
  return false;
148
152
  }
149
153
  }
150
- sendConversationMessage(content) {
154
+ sendConversationMessage(content, role, label) {
155
+ const metadata = {};
156
+ if (role) metadata["role"] = role;
157
+ if (label) metadata["label"] = label;
151
158
  this.send({
152
159
  type: "conversation_message" /* CONVERSATION_MESSAGE */,
153
- content
160
+ content,
161
+ ...Object.keys(metadata).length > 0 ? { metadata } : {}
154
162
  });
155
163
  }
156
164
  sendToolResult(callId, toolName, result, metadata) {
@@ -192,8 +200,8 @@ var PRBEStateEvent = /* @__PURE__ */ ((PRBEStateEvent2) => {
192
200
  PRBEStateEvent2["EVENT"] = "event";
193
201
  PRBEStateEvent2["COMPLETE"] = "complete";
194
202
  PRBEStateEvent2["ERROR"] = "error";
195
- PRBEStateEvent2["CR_START"] = "cr-start";
196
- PRBEStateEvent2["CR_COMPLETE"] = "cr-complete";
203
+ PRBEStateEvent2["BACKGROUND_START"] = "background-start";
204
+ PRBEStateEvent2["BACKGROUND_COMPLETE"] = "background-complete";
197
205
  PRBEStateEvent2["TICKETS_CHANGED"] = "tickets-changed";
198
206
  PRBEStateEvent2["TICKET_INFO"] = "ticket-info";
199
207
  PRBEStateEvent2["INTERACTION_REQUESTED"] = "interaction-requested";
@@ -213,20 +221,23 @@ var PRBEAgentState = class extends EventEmitter {
213
221
  resolvedInteractions = [];
214
222
  agentMessage;
215
223
  conversationHistory = [];
224
+ isPrivacySanitizing = false;
216
225
  // Completed user investigations (history)
217
226
  completedInvestigations = [];
218
- // Background context requests
219
- activeCRs = /* @__PURE__ */ new Map();
220
- completedCRs = [];
227
+ // Background investigations (context requests, external requests, etc.)
228
+ activeBackgroundInvestigations = /* @__PURE__ */ new Map();
229
+ completedBackgroundInvestigations = [];
221
230
  // Tracked tickets
222
231
  trackedSessionIDs = [];
223
232
  ticketInfo = [];
233
+ // Agent history
234
+ agentHistory = [];
224
235
  // Computed
225
236
  get hasActiveWork() {
226
- return this.isInvestigating || this.activeCRs.size > 0;
237
+ return this.isInvestigating || this.activeBackgroundInvestigations.size > 0;
227
238
  }
228
- get activeCRCount() {
229
- return this.activeCRs.size;
239
+ get activeBackgroundCount() {
240
+ return this.activeBackgroundInvestigations.size;
230
241
  }
231
242
  get isActive() {
232
243
  return this.isInvestigating || this.report.length > 0 || this.investigationError != null;
@@ -248,6 +259,12 @@ var PRBEAgentState = class extends EventEmitter {
248
259
  this.conversationHistory.push(entry);
249
260
  this.emit("status" /* STATUS */);
250
261
  }
262
+ appendBackgroundConversation(backgroundId, entry) {
263
+ const bg = this.activeBackgroundInvestigations.get(backgroundId);
264
+ if (!bg) return;
265
+ bg.conversationHistory.push(entry);
266
+ this.emit("status" /* STATUS */);
267
+ }
251
268
  resetInvestigation() {
252
269
  this.isInvestigating = false;
253
270
  this.events = [];
@@ -302,6 +319,7 @@ var PRBEAgentState = class extends EventEmitter {
302
319
  });
303
320
  this.report = report;
304
321
  this.isInvestigating = false;
322
+ this.isPrivacySanitizing = false;
305
323
  this.emit("complete" /* COMPLETE */, { report });
306
324
  this.emit("status" /* STATUS */);
307
325
  }
@@ -309,9 +327,14 @@ var PRBEAgentState = class extends EventEmitter {
309
327
  this.appendEvent(`Error: ${message}`);
310
328
  this.investigationError = message;
311
329
  this.isInvestigating = false;
330
+ this.isPrivacySanitizing = false;
312
331
  this.emit("error" /* ERROR */, { message });
313
332
  this.emit("status" /* STATUS */);
314
333
  }
334
+ setPrivacySanitizing(value) {
335
+ this.isPrivacySanitizing = value;
336
+ this.emit("status" /* STATUS */);
337
+ }
315
338
  // ---------- Interaction state mutations ----------
316
339
  setPendingInteraction(payload) {
317
340
  this.pendingInteraction = payload;
@@ -323,17 +346,17 @@ var PRBEAgentState = class extends EventEmitter {
323
346
  this.emit("interaction-resolved" /* INTERACTION_RESOLVED */);
324
347
  this.emit("status" /* STATUS */);
325
348
  }
326
- setCRPendingInteraction(crID, payload) {
327
- const cr = this.activeCRs.get(crID);
328
- if (!cr) return;
329
- cr.pendingInteraction = payload;
349
+ setBackgroundPendingInteraction(backgroundId, payload) {
350
+ const bg = this.activeBackgroundInvestigations.get(backgroundId);
351
+ if (!bg) return;
352
+ bg.pendingInteraction = payload;
330
353
  this.emit("interaction-requested" /* INTERACTION_REQUESTED */, payload);
331
354
  this.emit("status" /* STATUS */);
332
355
  }
333
- clearCRPendingInteraction(crID) {
334
- const cr = this.activeCRs.get(crID);
335
- if (!cr) return;
336
- cr.pendingInteraction = void 0;
356
+ clearBackgroundPendingInteraction(backgroundId) {
357
+ const bg = this.activeBackgroundInvestigations.get(backgroundId);
358
+ if (!bg) return;
359
+ bg.pendingInteraction = void 0;
337
360
  this.emit("interaction-resolved" /* INTERACTION_RESOLVED */);
338
361
  this.emit("status" /* STATUS */);
339
362
  }
@@ -349,18 +372,18 @@ var PRBEAgentState = class extends EventEmitter {
349
372
  this.emit("interaction-resolved" /* INTERACTION_RESOLVED */);
350
373
  this.emit("status" /* STATUS */);
351
374
  }
352
- resolveCRInteraction(crID, response) {
353
- const cr = this.activeCRs.get(crID);
354
- if (!cr || !cr.pendingInteraction) return;
355
- const resolved = cr.resolvedInteractions ?? [];
375
+ resolveBackgroundInteraction(backgroundId, response) {
376
+ const bg = this.activeBackgroundInvestigations.get(backgroundId);
377
+ if (!bg || !bg.pendingInteraction) return;
378
+ const resolved = bg.resolvedInteractions ?? [];
356
379
  resolved.push({
357
- interactionId: cr.pendingInteraction.interactionId,
358
- payload: cr.pendingInteraction,
380
+ interactionId: bg.pendingInteraction.interactionId,
381
+ payload: bg.pendingInteraction,
359
382
  response,
360
- eventIndex: cr.events.length
383
+ eventIndex: bg.events.length
361
384
  });
362
- cr.resolvedInteractions = resolved;
363
- cr.pendingInteraction = void 0;
385
+ bg.resolvedInteractions = resolved;
386
+ bg.pendingInteraction = void 0;
364
387
  this.emit("interaction-resolved" /* INTERACTION_RESOLVED */);
365
388
  this.emit("status" /* STATUS */);
366
389
  }
@@ -369,10 +392,10 @@ var PRBEAgentState = class extends EventEmitter {
369
392
  this.emit("agent-message" /* AGENT_MESSAGE */, { message });
370
393
  this.emit("status" /* STATUS */);
371
394
  }
372
- setCRAgentMessage(crID, message) {
373
- const cr = this.activeCRs.get(crID);
374
- if (!cr) return;
375
- cr.agentMessage = message;
395
+ setBackgroundAgentMessage(backgroundId, message) {
396
+ const bg = this.activeBackgroundInvestigations.get(backgroundId);
397
+ if (!bg) return;
398
+ bg.agentMessage = message;
376
399
  this.emit("agent-message" /* AGENT_MESSAGE */, { message });
377
400
  this.emit("status" /* STATUS */);
378
401
  }
@@ -383,14 +406,17 @@ var PRBEAgentState = class extends EventEmitter {
383
406
  this.emit("status" /* STATUS */);
384
407
  }
385
408
  }
386
- // ---------- CR state mutations ----------
387
- beginCR(id, query, slug, ticketId) {
388
- const cr = {
409
+ // ---------- Background investigation state mutations ----------
410
+ beginBackgroundInvestigation(id, query, slug, ticketId, source, sourceDetail) {
411
+ const bg = {
389
412
  id,
390
413
  query,
391
414
  slug,
392
415
  ticketId,
416
+ source,
417
+ sourceDetail,
393
418
  events: [],
419
+ conversationHistory: [],
394
420
  resolvedInteractions: [],
395
421
  isRunning: true,
396
422
  isCompleted: false,
@@ -399,20 +425,20 @@ var PRBEAgentState = class extends EventEmitter {
399
425
  summary: "",
400
426
  startedAt: /* @__PURE__ */ new Date()
401
427
  };
402
- this.activeCRs.set(id, cr);
403
- this.emit("cr-start" /* CR_START */, cr);
428
+ this.activeBackgroundInvestigations.set(id, bg);
429
+ this.emit("background-start" /* BACKGROUND_START */, bg);
404
430
  this.emit("status" /* STATUS */);
405
431
  }
406
- appendCREvent(crID, label, detail, completed = false) {
407
- const cr = this.activeCRs.get(crID);
408
- if (!cr) return;
409
- if (cr.events.length > 0) {
410
- const last = cr.events[cr.events.length - 1];
432
+ appendBackgroundEvent(backgroundId, label, detail, completed = false) {
433
+ const bg = this.activeBackgroundInvestigations.get(backgroundId);
434
+ if (!bg) return;
435
+ if (bg.events.length > 0) {
436
+ const last = bg.events[bg.events.length - 1];
411
437
  if (!last.isCompleted && !completed) {
412
438
  last.isCompleted = true;
413
439
  }
414
440
  }
415
- cr.events.push({
441
+ bg.events.push({
416
442
  id: randomUUID(),
417
443
  label,
418
444
  detail,
@@ -421,47 +447,47 @@ var PRBEAgentState = class extends EventEmitter {
421
447
  });
422
448
  this.emit("status" /* STATUS */);
423
449
  }
424
- attachCRObservation(crID, text) {
425
- const cr = this.activeCRs.get(crID);
426
- if (!cr || cr.events.length === 0) return;
427
- cr.events[cr.events.length - 1].detail = text;
450
+ attachBackgroundObservation(backgroundId, text) {
451
+ const bg = this.activeBackgroundInvestigations.get(backgroundId);
452
+ if (!bg || bg.events.length === 0) return;
453
+ bg.events[bg.events.length - 1].detail = text;
428
454
  this.emit("status" /* STATUS */);
429
455
  }
430
- completeCR(id, report) {
431
- const cr = this.activeCRs.get(id);
432
- if (!cr) return;
433
- this.activeCRs.delete(id);
434
- if (cr.events.length > 0) {
435
- cr.events[cr.events.length - 1].isCompleted = true;
456
+ completeBackgroundInvestigation(id, report) {
457
+ const bg = this.activeBackgroundInvestigations.get(id);
458
+ if (!bg) return;
459
+ this.activeBackgroundInvestigations.delete(id);
460
+ if (bg.events.length > 0) {
461
+ bg.events[bg.events.length - 1].isCompleted = true;
436
462
  }
437
- cr.events.push({
463
+ bg.events.push({
438
464
  id: randomUUID(),
439
465
  label: "Done",
440
466
  isCompleted: true,
441
467
  isExpanded: false
442
468
  });
443
- cr.isRunning = false;
444
- cr.isCompleted = true;
445
- cr.report = report;
446
- this.completedCRs.unshift(cr);
447
- this.emit("cr-complete" /* CR_COMPLETE */, cr);
469
+ bg.isRunning = false;
470
+ bg.isCompleted = true;
471
+ bg.report = report;
472
+ this.completedBackgroundInvestigations.unshift(bg);
473
+ this.emit("background-complete" /* BACKGROUND_COMPLETE */, bg);
448
474
  this.emit("status" /* STATUS */);
449
475
  }
450
- failCR(id, message) {
451
- const cr = this.activeCRs.get(id);
452
- if (!cr) return;
453
- this.activeCRs.delete(id);
454
- cr.events.push({
476
+ failBackgroundInvestigation(id, message) {
477
+ const bg = this.activeBackgroundInvestigations.get(id);
478
+ if (!bg) return;
479
+ this.activeBackgroundInvestigations.delete(id);
480
+ bg.events.push({
455
481
  id: randomUUID(),
456
482
  label: `Error: ${message}`,
457
483
  isCompleted: false,
458
484
  isExpanded: false
459
485
  });
460
- cr.isRunning = false;
461
- cr.isFailed = true;
462
- cr.errorMessage = message;
463
- this.completedCRs.unshift(cr);
464
- this.emit("cr-complete" /* CR_COMPLETE */, cr);
486
+ bg.isRunning = false;
487
+ bg.isFailed = true;
488
+ bg.errorMessage = message;
489
+ this.completedBackgroundInvestigations.unshift(bg);
490
+ this.emit("background-complete" /* BACKGROUND_COMPLETE */, bg);
465
491
  this.emit("status" /* STATUS */);
466
492
  }
467
493
  // ---------- Tickets ----------
@@ -475,6 +501,10 @@ var PRBEAgentState = class extends EventEmitter {
475
501
  this.emit("ticket-info" /* TICKET_INFO */, info);
476
502
  this.emit("status" /* STATUS */);
477
503
  }
504
+ updateAgentHistory(tickets) {
505
+ this.agentHistory = tickets;
506
+ this.emit("status" /* STATUS */);
507
+ }
478
508
  };
479
509
 
480
510
  // src/tools/index.ts
@@ -485,11 +515,13 @@ var InteractionType = /* @__PURE__ */ ((InteractionType2) => {
485
515
  InteractionType2["ASK_QUESTION"] = "ask_question";
486
516
  InteractionType2["REQUEST_PERMISSION"] = "request_permission";
487
517
  InteractionType2["REQUEST_PATH_ACCESS"] = "request_path_access";
518
+ InteractionType2["REVIEW_SANITIZED_OUTPUT"] = "review_sanitized_output";
488
519
  return InteractionType2;
489
520
  })(InteractionType || {});
490
521
  var InvestigationSource = /* @__PURE__ */ ((InvestigationSource2) => {
491
522
  InvestigationSource2["USER"] = "user";
492
523
  InvestigationSource2["CONTEXT_REQUEST"] = "context_request";
524
+ InvestigationSource2["EXTERNAL_REQUEST"] = "external_request";
493
525
  return InvestigationSource2;
494
526
  })(InvestigationSource || {});
495
527
 
@@ -1397,7 +1429,8 @@ var AskUserTool = class {
1397
1429
  const response = await this.requester.requestUserInteraction({
1398
1430
  type: "ask_question" /* ASK_QUESTION */,
1399
1431
  interactionId: randomUUID3(),
1400
- question: reason
1432
+ question,
1433
+ context: reason
1401
1434
  });
1402
1435
  const askResponse = response;
1403
1436
  return askResponse.answer;
@@ -1607,6 +1640,10 @@ var BashExecuteTool = class {
1607
1640
  return `Error: working directory '${cwdArg}' is outside auto-approved directories`;
1608
1641
  }
1609
1642
  cwd = resolved;
1643
+ } else if (this.autoApprovedDirs.length > 0) {
1644
+ cwd = this.autoApprovedDirs[0];
1645
+ } else {
1646
+ return "Error: no approved directories configured and no working directory specified";
1610
1647
  }
1611
1648
  const isSafe = this.isCommandSafe(command);
1612
1649
  if (!isSafe) {
@@ -1697,197 +1734,20 @@ var BashExecuteTool = class {
1697
1734
  }
1698
1735
  };
1699
1736
 
1700
- // src/history.ts
1701
- import * as fs2 from "fs";
1702
- import * as path4 from "path";
1703
- import * as os from "os";
1704
- import * as crypto from "crypto";
1705
- var HKDF_SALT = Buffer.from("prbe-history-encryption-salt", "utf-8");
1706
- var HKDF_INFO = Buffer.from("prbe-history-v1", "utf-8");
1707
- function getAppDataDir() {
1737
+ // src/agent.ts
1738
+ function getPersistencePath() {
1708
1739
  const appData = process.env["APPDATA"] || (process.platform === "darwin" ? path4.join(os.homedir(), "Library", "Application Support") : path4.join(os.homedir(), ".local", "share"));
1709
- return path4.join(appData, "prbe-agent");
1710
- }
1711
- function getHistoryDir() {
1712
- const dir = path4.join(getAppDataDir(), "history" /* HISTORY_DIR */);
1740
+ const dir = path4.join(appData, "prbe-agent");
1713
1741
  if (!fs2.existsSync(dir)) {
1714
1742
  fs2.mkdirSync(dir, { recursive: true });
1715
1743
  }
1716
- return dir;
1717
- }
1718
- function deriveKey(apiKey) {
1719
- return Buffer.from(
1720
- crypto.hkdfSync(
1721
- "sha256",
1722
- Buffer.from(apiKey, "utf-8"),
1723
- HKDF_SALT,
1724
- HKDF_INFO,
1725
- 32 /* KEY_LENGTH */
1726
- )
1727
- );
1728
- }
1729
- function encrypt(plaintext, key) {
1730
- const iv = crypto.randomBytes(12 /* IV_LENGTH */);
1731
- const cipher = crypto.createCipheriv(
1732
- "aes-256-gcm" /* ALGORITHM */,
1733
- key,
1734
- iv,
1735
- { authTagLength: 16 /* AUTH_TAG_LENGTH */ }
1736
- );
1737
- const encrypted = Buffer.concat([cipher.update(plaintext, "utf-8"), cipher.final()]);
1738
- const authTag = cipher.getAuthTag();
1739
- return Buffer.concat([iv, authTag, encrypted]);
1740
- }
1741
- function decrypt(data, key) {
1742
- const iv = data.subarray(0, 12 /* IV_LENGTH */);
1743
- const authTag = data.subarray(
1744
- 12 /* IV_LENGTH */,
1745
- 12 /* IV_LENGTH */ + 16 /* AUTH_TAG_LENGTH */
1746
- );
1747
- const ciphertext = data.subarray(
1748
- 12 /* IV_LENGTH */ + 16 /* AUTH_TAG_LENGTH */
1749
- );
1750
- const decipher = crypto.createDecipheriv(
1751
- "aes-256-gcm" /* ALGORITHM */,
1752
- key,
1753
- iv,
1754
- { authTagLength: 16 /* AUTH_TAG_LENGTH */ }
1755
- );
1756
- decipher.setAuthTag(authTag);
1757
- return decipher.update(ciphertext) + decipher.final("utf-8");
1758
- }
1759
- var HistoryStore = class {
1760
- key;
1761
- constructor(apiKey) {
1762
- this.key = deriveKey(apiKey);
1763
- }
1764
- load() {
1765
- const investigations = [];
1766
- const crs = [];
1767
- try {
1768
- const dir = getHistoryDir();
1769
- let files;
1770
- try {
1771
- files = fs2.readdirSync(dir);
1772
- } catch {
1773
- return { investigations, crs };
1774
- }
1775
- for (const filename of files) {
1776
- try {
1777
- const filePath = path4.join(dir, filename);
1778
- const raw = fs2.readFileSync(filePath);
1779
- const json = decrypt(raw, this.key);
1780
- if (filename.startsWith("inv-") && filename.endsWith(".json")) {
1781
- const item = JSON.parse(json);
1782
- investigations.push({
1783
- ...item,
1784
- completedAt: new Date(item.completedAt)
1785
- });
1786
- } else if (filename.startsWith("cr-") && filename.endsWith(".json")) {
1787
- const item = JSON.parse(json);
1788
- crs.push({
1789
- ...item,
1790
- startedAt: new Date(item.startedAt),
1791
- resolvedInteractions: item.resolvedInteractions ?? []
1792
- });
1793
- }
1794
- } catch {
1795
- console.warn(`[PRBEAgent] Skipping unreadable history file: ${filename}`);
1796
- }
1797
- }
1798
- } catch {
1799
- }
1800
- investigations.sort(
1801
- (a, b) => b.completedAt.getTime() - a.completedAt.getTime()
1802
- );
1803
- crs.sort((a, b) => b.startedAt.getTime() - a.startedAt.getTime());
1804
- return { investigations, crs };
1805
- }
1806
- save(investigations, crs) {
1807
- try {
1808
- const dir = getHistoryDir();
1809
- const desiredFiles = /* @__PURE__ */ new Set();
1810
- for (const inv of investigations) {
1811
- const filename = `inv-${inv.id}.json`;
1812
- desiredFiles.add(filename);
1813
- const data = {
1814
- id: inv.id,
1815
- query: inv.query,
1816
- report: inv.report,
1817
- summary: inv.summary,
1818
- ticketId: inv.ticketId,
1819
- events: inv.events,
1820
- resolvedInteractions: inv.resolvedInteractions,
1821
- conversationHistory: inv.conversationHistory,
1822
- completedAt: inv.completedAt.toISOString()
1823
- };
1824
- fs2.writeFileSync(
1825
- path4.join(dir, filename),
1826
- encrypt(JSON.stringify(data), this.key)
1827
- );
1828
- }
1829
- for (const cr of crs) {
1830
- const filename = `cr-${cr.id}.json`;
1831
- desiredFiles.add(filename);
1832
- const data = {
1833
- id: cr.id,
1834
- query: cr.query,
1835
- slug: cr.slug,
1836
- ticketId: cr.ticketId,
1837
- events: cr.events,
1838
- isRunning: cr.isRunning,
1839
- isCompleted: cr.isCompleted,
1840
- isFailed: cr.isFailed,
1841
- report: cr.report,
1842
- summary: cr.summary,
1843
- errorMessage: cr.errorMessage,
1844
- startedAt: cr.startedAt.toISOString(),
1845
- pendingInteraction: cr.pendingInteraction,
1846
- resolvedInteractions: cr.resolvedInteractions ?? []
1847
- };
1848
- fs2.writeFileSync(
1849
- path4.join(dir, filename),
1850
- encrypt(JSON.stringify(data), this.key)
1851
- );
1852
- }
1853
- try {
1854
- const existing = fs2.readdirSync(dir);
1855
- for (const filename of existing) {
1856
- if (!desiredFiles.has(filename)) {
1857
- fs2.unlinkSync(path4.join(dir, filename));
1858
- }
1859
- }
1860
- } catch {
1861
- }
1862
- } catch {
1863
- console.error("[PRBEAgent] Failed to save investigation history");
1864
- }
1865
- }
1866
- static clear() {
1867
- try {
1868
- const dir = path4.join(getAppDataDir(), "history" /* HISTORY_DIR */);
1869
- if (fs2.existsSync(dir)) {
1870
- fs2.rmSync(dir, { recursive: true, force: true });
1871
- }
1872
- } catch {
1873
- }
1874
- }
1875
- };
1876
-
1877
- // src/agent.ts
1878
- function getPersistencePath() {
1879
- const appData = process.env["APPDATA"] || (process.platform === "darwin" ? path5.join(os2.homedir(), "Library", "Application Support") : path5.join(os2.homedir(), ".local", "share"));
1880
- const dir = path5.join(appData, "prbe-agent");
1881
- if (!fs3.existsSync(dir)) {
1882
- fs3.mkdirSync(dir, { recursive: true });
1883
- }
1884
- return path5.join(dir, "agent-state.json");
1744
+ return path4.join(dir, "agent-state.json");
1885
1745
  }
1886
1746
  function loadPersistedData() {
1887
1747
  try {
1888
1748
  const filePath = getPersistencePath();
1889
- if (fs3.existsSync(filePath)) {
1890
- const raw = fs3.readFileSync(filePath, "utf-8");
1749
+ if (fs2.existsSync(filePath)) {
1750
+ const raw = fs2.readFileSync(filePath, "utf-8");
1891
1751
  return JSON.parse(raw);
1892
1752
  }
1893
1753
  } catch {
@@ -1897,7 +1757,7 @@ function loadPersistedData() {
1897
1757
  function savePersistedData(data) {
1898
1758
  try {
1899
1759
  const filePath = getPersistencePath();
1900
- fs3.writeFileSync(filePath, JSON.stringify(data, null, 2), "utf-8");
1760
+ fs2.writeFileSync(filePath, JSON.stringify(data, null, 2), "utf-8");
1901
1761
  } catch {
1902
1762
  console.error("[PRBEAgent] Failed to save persisted data");
1903
1763
  }
@@ -1917,8 +1777,7 @@ var PRBEAgent = class _PRBEAgent {
1917
1777
  persistedData;
1918
1778
  fetchAbortController = null;
1919
1779
  currentInvestigationSource = "user" /* USER */;
1920
- currentCRId = null;
1921
- historyStore;
1780
+ currentBackgroundId = null;
1922
1781
  /** Files flagged during the current tool call — uploaded immediately after the tool returns. */
1923
1782
  pendingFlaggedFiles = [];
1924
1783
  // ---------- User Identifier ----------
@@ -1934,30 +1793,6 @@ var PRBEAgent = class _PRBEAgent {
1934
1793
  savePersistedData(this.persistedData);
1935
1794
  return newID;
1936
1795
  }
1937
- get trackedSessionIDs() {
1938
- return this.persistedData.sessionIds ?? [];
1939
- }
1940
- set trackedSessionIDs(ids) {
1941
- this.persistedData.sessionIds = ids;
1942
- savePersistedData(this.persistedData);
1943
- this.state.updateTrackedSessionIDs(ids);
1944
- this.syncPolling(ids.length > 0);
1945
- }
1946
- syncPolling(hasSessions) {
1947
- if (this.config.backgroundPolling && hasSessions) {
1948
- if (this.pollingTimer === null) {
1949
- this.startPolling();
1950
- }
1951
- } else if (!hasSessions) {
1952
- this.stopPolling();
1953
- }
1954
- }
1955
- addTrackedSession(id) {
1956
- const ids = this.trackedSessionIDs;
1957
- if (!ids.includes(id)) {
1958
- this.trackedSessionIDs = [...ids, id];
1959
- }
1960
- }
1961
1796
  // ---------- Constructor ----------
1962
1797
  constructor(config) {
1963
1798
  this.config = {
@@ -1977,11 +1812,7 @@ var PRBEAgent = class _PRBEAgent {
1977
1812
  this.state = new PRBEAgentState();
1978
1813
  this.logCapture = new PRBELogCapture(this.config.maxLogEntries);
1979
1814
  this.persistedData = loadPersistedData();
1980
- this.historyStore = new HistoryStore(this.config.apiKey);
1981
1815
  void this.agentID;
1982
- const history = this.historyStore.load();
1983
- this.state.completedInvestigations = history.investigations;
1984
- this.state.completedCRs = history.crs;
1985
1816
  const roots = this.config.autoApprovedDirs;
1986
1817
  const requester = this.interactionHandler ? this : void 0;
1987
1818
  const grantedPaths = this.grantedPaths;
@@ -2013,8 +1844,8 @@ var PRBEAgent = class _PRBEAgent {
2013
1844
  [{ name: "message", type: "STRING" /* STRING */, description: "Message for the user", required: true }],
2014
1845
  async (args) => {
2015
1846
  const message = args["message"] ?? "";
2016
- if (this.currentInvestigationSource === "context_request" /* CONTEXT_REQUEST */ && this.currentCRId) {
2017
- this.state.setCRAgentMessage(this.currentCRId, message);
1847
+ if (this.currentInvestigationSource !== "user" /* USER */ && this.currentBackgroundId) {
1848
+ this.state.setBackgroundAgentMessage(this.currentBackgroundId, message);
2018
1849
  } else {
2019
1850
  this.state.setAgentMessage(message);
2020
1851
  }
@@ -2031,9 +1862,8 @@ var PRBEAgent = class _PRBEAgent {
2031
1862
  if (config.ipcMain) {
2032
1863
  this.hookRendererLogs(config.ipcMain, config.rendererLogChannel ?? "prbe-renderer-log");
2033
1864
  }
2034
- const existingSessions = this.trackedSessionIDs;
2035
- if (existingSessions.length > 0) {
2036
- this.trackedSessionIDs = existingSessions;
1865
+ if (this.config.backgroundPolling) {
1866
+ this.startPolling();
2037
1867
  }
2038
1868
  }
2039
1869
  // ---------- Log integration ----------
@@ -2077,8 +1907,8 @@ var PRBEAgent = class _PRBEAgent {
2077
1907
  get investigationSource() {
2078
1908
  return this.currentInvestigationSource;
2079
1909
  }
2080
- sendConversationMessage(content) {
2081
- this.activeConnection?.sendConversationMessage(content);
1910
+ sendConversationMessage(content, role, label) {
1911
+ this.activeConnection?.sendConversationMessage(content, role, label);
2082
1912
  }
2083
1913
  async requestUserInteraction(payload) {
2084
1914
  if (!this.interactionHandler) {
@@ -2087,15 +1917,31 @@ var PRBEAgent = class _PRBEAgent {
2087
1917
  "No interaction handler configured"
2088
1918
  );
2089
1919
  }
2090
- if (this.currentInvestigationSource === "context_request" /* CONTEXT_REQUEST */ && this.currentCRId) {
2091
- this.state.setCRPendingInteraction(this.currentCRId, payload);
1920
+ if (payload.type === "request_permission" /* REQUEST_PERMISSION */) {
1921
+ const p = payload;
1922
+ const content = p.reason ? `${p.command}
1923
+
1924
+ ${p.reason}` : p.command;
1925
+ this.sendConversationMessage(content, "agent" /* Agent */, p.action);
1926
+ } else if (payload.type === "request_path_access" /* REQUEST_PATH_ACCESS */) {
1927
+ const p = payload;
1928
+ const content = p.reason ? `${p.path}
1929
+
1930
+ ${p.reason}` : p.path;
1931
+ this.sendConversationMessage(content, "agent" /* Agent */, "Request path access");
1932
+ } else if (payload.type === "ask_question" /* ASK_QUESTION */) {
1933
+ const p = payload;
1934
+ this.sendConversationMessage(p.question, "agent" /* Agent */);
1935
+ }
1936
+ if (this.currentInvestigationSource !== "user" /* USER */ && this.currentBackgroundId) {
1937
+ this.state.setBackgroundPendingInteraction(this.currentBackgroundId, payload);
2092
1938
  } else {
2093
1939
  this.state.setPendingInteraction(payload);
2094
1940
  }
2095
1941
  try {
2096
1942
  const response = await this.interactionHandler.handleInteraction(payload);
2097
- if (this.currentInvestigationSource === "context_request" /* CONTEXT_REQUEST */ && this.currentCRId) {
2098
- this.state.resolveCRInteraction(this.currentCRId, response);
1943
+ if (this.currentInvestigationSource !== "user" /* USER */ && this.currentBackgroundId) {
1944
+ this.state.resolveBackgroundInteraction(this.currentBackgroundId, response);
2099
1945
  } else {
2100
1946
  this.state.resolveInteraction(response);
2101
1947
  }
@@ -2108,8 +1954,8 @@ var PRBEAgent = class _PRBEAgent {
2108
1954
  }
2109
1955
  return response;
2110
1956
  } catch (err) {
2111
- if (this.currentInvestigationSource === "context_request" /* CONTEXT_REQUEST */ && this.currentCRId) {
2112
- this.state.clearCRPendingInteraction(this.currentCRId);
1957
+ if (this.currentInvestigationSource !== "user" /* USER */ && this.currentBackgroundId) {
1958
+ this.state.clearBackgroundPendingInteraction(this.currentBackgroundId);
2113
1959
  } else {
2114
1960
  this.state.clearPendingInteraction();
2115
1961
  }
@@ -2181,58 +2027,100 @@ var PRBEAgent = class _PRBEAgent {
2181
2027
  new PRBEClosureTool(name, description, parameters, handler, options)
2182
2028
  );
2183
2029
  }
2184
- /**
2185
- * User-initiated investigation. Updates `state` events/report directly.
2186
- */
2187
- async investigate(query, contextRequestID) {
2188
- this.userCancelled = false;
2189
- this.currentInvestigationSource = "user" /* USER */;
2190
- this.currentCRId = null;
2191
- this.state.beginInvestigation(query);
2030
+ // ---------- Unified Investigation ----------
2031
+ async runInvestigation(opts) {
2032
+ this.currentInvestigationSource = opts.source;
2033
+ this.currentBackgroundId = opts.sourceId ?? null;
2034
+ const isBackground = opts.source !== "user" /* USER */;
2192
2035
  const emitter = (status) => {
2193
2036
  switch (status.type) {
2194
2037
  case "started" /* STARTED */:
2195
- this.state.appendEvent("Starting investigation...");
2038
+ if (isBackground && opts.sourceId) {
2039
+ this.state.appendBackgroundEvent(opts.sourceId, "Starting investigation...");
2040
+ } else {
2041
+ this.state.appendEvent("Starting investigation...");
2042
+ }
2196
2043
  break;
2197
2044
  case "thinking" /* THINKING */:
2198
2045
  break;
2199
2046
  case "tool_call" /* TOOL_CALL */:
2200
- this.state.appendEvent(status.label);
2047
+ if (isBackground && opts.sourceId) {
2048
+ this.state.appendBackgroundEvent(opts.sourceId, status.label);
2049
+ } else {
2050
+ this.state.appendEvent(status.label);
2051
+ }
2201
2052
  break;
2202
2053
  case "observation" /* OBSERVATION */:
2203
- this.state.attachObservation(status.text);
2054
+ if (isBackground && opts.sourceId) {
2055
+ this.state.attachBackgroundObservation(opts.sourceId, status.text);
2056
+ } else {
2057
+ this.state.attachObservation(status.text);
2058
+ }
2204
2059
  break;
2205
2060
  case "thought" /* THOUGHT */:
2206
- this.state.appendEvent("Thinking", status.text);
2061
+ if (isBackground && opts.sourceId) {
2062
+ this.state.appendBackgroundEvent(opts.sourceId, "Thinking", status.text);
2063
+ } else {
2064
+ this.state.appendEvent("Thinking", status.text);
2065
+ }
2207
2066
  break;
2208
2067
  case "completed" /* COMPLETED */:
2209
- this.state.completeInvestigation(status.report, status.ticketId);
2210
- this.historyStore.save(this.state.completedInvestigations, this.state.completedCRs);
2068
+ if (isBackground && opts.sourceId) {
2069
+ this.state.completeBackgroundInvestigation(opts.sourceId, status.report);
2070
+ } else {
2071
+ this.state.completeInvestigation(status.report, status.ticketId);
2072
+ }
2211
2073
  break;
2212
2074
  case "error" /* ERROR */:
2213
- this.state.failInvestigation(status.message);
2075
+ if (isBackground && opts.sourceId) {
2076
+ this.state.failBackgroundInvestigation(opts.sourceId, status.message);
2077
+ } else {
2078
+ this.state.failInvestigation(status.message);
2079
+ }
2080
+ break;
2081
+ case "privacy_sanitizing" /* PRIVACY_SANITIZING */:
2082
+ if (isBackground && opts.sourceId) {
2083
+ this.state.appendBackgroundEvent(opts.sourceId, "Sanitizing data for privacy review...");
2084
+ } else {
2085
+ this.state.appendEvent("Sanitizing data for privacy review...");
2086
+ }
2087
+ this.state.setPrivacySanitizing(true);
2214
2088
  break;
2215
2089
  }
2216
2090
  };
2217
2091
  const result = await this.connectToProxy(
2218
- query,
2219
- contextRequestID,
2092
+ opts.query,
2093
+ opts.contextRequestId,
2094
+ opts.externalRequestId,
2095
+ opts.externalRequestSource,
2096
+ opts.externalRequestSourceDetail,
2220
2097
  emitter,
2221
- () => this.userCancelled
2098
+ opts.cancellable ? () => this.userCancelled : () => false,
2099
+ opts.ticketId
2222
2100
  );
2223
2101
  this.currentInvestigationSource = "user" /* USER */;
2224
- this.currentCRId = null;
2225
- if (result?.sessionId) {
2226
- this.addTrackedSession(result.sessionId);
2227
- } else if (!result) {
2228
- if (this.state.isInvestigating) {
2229
- const message = this.userCancelled ? "Investigation cancelled" : "Investigation ended unexpectedly";
2230
- this.state.failInvestigation(message);
2231
- }
2102
+ this.currentBackgroundId = null;
2103
+ return result;
2104
+ }
2105
+ /**
2106
+ * User-initiated investigation. Updates `state` events/report directly.
2107
+ */
2108
+ async investigate(query, contextRequestID) {
2109
+ this.userCancelled = false;
2110
+ this.state.beginInvestigation(query);
2111
+ const result = await this.runInvestigation({
2112
+ query,
2113
+ source: "user" /* USER */,
2114
+ contextRequestId: contextRequestID,
2115
+ cancellable: true
2116
+ });
2117
+ if (!result && this.state.isInvestigating) {
2118
+ const message = this.userCancelled ? "Investigation cancelled" : "Investigation ended unexpectedly";
2119
+ this.state.failInvestigation(message);
2232
2120
  }
2233
2121
  }
2234
2122
  /**
2235
- * Cancel user-initiated investigation only (does not cancel background CRs).
2123
+ * Cancel user-initiated investigation only (does not cancel background investigations).
2236
2124
  */
2237
2125
  cancelInvestigation() {
2238
2126
  this.userCancelled = true;
@@ -2254,41 +2142,21 @@ var PRBEAgent = class _PRBEAgent {
2254
2142
  this.stopPolling();
2255
2143
  }
2256
2144
  /**
2257
- * Poll the backend for context requests on tracked tickets.
2258
- * Resolves session IDs → ticket IDs first, then polls with ticket IDs.
2145
+ * Poll the backend for context requests and external requests.
2259
2146
  */
2260
2147
  async poll() {
2261
- const sessionIDs = this.trackedSessionIDs;
2262
- if (sessionIDs.length === 0) return null;
2263
2148
  try {
2264
- const resolved = await this.resolveSessions(sessionIDs);
2265
- const returnedSessionIDs = new Set(
2266
- resolved.tickets.flatMap((t) => t.session_ids)
2267
- );
2268
- const resolvedSessionIDs = new Set(
2269
- resolved.tickets.filter((t) => t.status === "resolved").flatMap((t) => t.session_ids)
2270
- );
2271
- const survivingSessions = sessionIDs.filter(
2272
- (id) => returnedSessionIDs.has(id) && !resolvedSessionIDs.has(id)
2273
- );
2274
- if (survivingSessions.length !== sessionIDs.length) {
2275
- this.trackedSessionIDs = survivingSessions;
2149
+ const request = { agent_id: this.agentID };
2150
+ const response = await this.post("/api/agent/poll", request);
2151
+ for (const cr of response.context_requests) {
2152
+ if (!cr.is_active) continue;
2153
+ if (this.state.activeBackgroundInvestigations.has(cr.id)) continue;
2154
+ await this.investigateForCR(cr, cr.ticket_id);
2276
2155
  }
2277
- const ticketIDs = resolved.tickets.filter((t) => t.status !== "resolved").map((t) => t.ticket_id);
2278
- if (ticketIDs.length === 0) return { tickets: [] };
2279
- const request = {
2280
- agent_id: this.agentID,
2281
- ticket_ids: ticketIDs
2282
- };
2283
- const response = await this.post(
2284
- "/api/agent/poll",
2285
- request
2286
- );
2287
- for (const ticket of response.tickets) {
2288
- for (const cr of ticket.context_requests) {
2289
- if (!cr.is_active) continue;
2290
- await this.investigateForCR(cr, ticket.ticket_id);
2291
- }
2156
+ for (const er of response.external_requests) {
2157
+ if (!er.is_active) continue;
2158
+ if (this.state.activeBackgroundInvestigations.has(er.id)) continue;
2159
+ await this.investigateForER(er);
2292
2160
  }
2293
2161
  return response;
2294
2162
  } catch {
@@ -2296,27 +2164,44 @@ var PRBEAgent = class _PRBEAgent {
2296
2164
  }
2297
2165
  }
2298
2166
  /**
2299
- * Fetch ticket display info for all tracked sessions.
2300
- * Resolves session IDs → ticket IDs first, then fetches ticket info.
2167
+ * Fetch agent history (tickets + sessions) from the backend.
2301
2168
  */
2302
- async fetchTicketInfo() {
2303
- const sessionIDs = this.trackedSessionIDs;
2304
- if (sessionIDs.length === 0) return [];
2169
+ async fetchHistory() {
2305
2170
  try {
2306
- const resolved = await this.resolveSessions(sessionIDs);
2307
- const ticketIDs = resolved.tickets.map((t) => t.ticket_id);
2308
- if (ticketIDs.length === 0) return [];
2309
- const request = { ticket_ids: ticketIDs };
2310
2171
  const response = await this.post(
2311
- "/api/agent/tickets",
2312
- request
2172
+ "/api/agent/history",
2173
+ { agent_id: this.agentID }
2313
2174
  );
2314
- this.state.updateTicketInfo(response.tickets);
2315
- return response.tickets;
2175
+ this.state.updateAgentHistory(response.tickets);
2176
+ return response;
2316
2177
  } catch {
2317
- return [];
2178
+ return null;
2318
2179
  }
2319
2180
  }
2181
+ async fetchSanitizedFile(url) {
2182
+ const res = await fetch(url, {
2183
+ headers: { "X-API-Key": this.config.apiKey }
2184
+ });
2185
+ if (!res.ok) throw new Error(`Failed to fetch sanitized file: ${res.status}`);
2186
+ const buffer = Buffer.from(await res.arrayBuffer());
2187
+ if (!_PRBEAgent.isTextContent(buffer)) {
2188
+ return { isText: false, url };
2189
+ }
2190
+ return { isText: true, content: buffer.toString("utf-8"), url };
2191
+ }
2192
+ /**
2193
+ * Detect whether a buffer contains text by checking for null bytes
2194
+ * and verifying the content is valid UTF-8.
2195
+ */
2196
+ static isTextContent(buffer) {
2197
+ const checkLength = Math.min(buffer.length, 8192);
2198
+ for (let i = 0; i < checkLength; i++) {
2199
+ if (buffer[i] === 0) return false;
2200
+ }
2201
+ const decoded = buffer.toString("utf-8");
2202
+ const reEncoded = Buffer.from(decoded, "utf-8");
2203
+ return buffer.length === reEncoded.length;
2204
+ }
2320
2205
  stopPolling() {
2321
2206
  if (this.pollingTimer !== null) {
2322
2207
  clearInterval(this.pollingTimer);
@@ -2324,7 +2209,7 @@ var PRBEAgent = class _PRBEAgent {
2324
2209
  }
2325
2210
  }
2326
2211
  resumePolling() {
2327
- if (this.trackedSessionIDs.length === 0) return;
2212
+ if (!this.config.backgroundPolling) return;
2328
2213
  this.startPolling();
2329
2214
  }
2330
2215
  /**
@@ -2342,13 +2227,11 @@ var PRBEAgent = class _PRBEAgent {
2342
2227
  this.stopPolling();
2343
2228
  this.persistedData = {};
2344
2229
  savePersistedData(this.persistedData);
2345
- HistoryStore.clear();
2346
2230
  this.state.resetInvestigation();
2347
2231
  this.state.completedInvestigations = [];
2348
- this.state.activeCRs.clear();
2349
- this.state.completedCRs = [];
2350
- this.state.trackedSessionIDs = [];
2351
- this.state.ticketInfo = [];
2232
+ this.state.activeBackgroundInvestigations.clear();
2233
+ this.state.completedBackgroundInvestigations = [];
2234
+ this.state.agentHistory = [];
2352
2235
  this.state.emit("status" /* STATUS */);
2353
2236
  }
2354
2237
  /**
@@ -2357,61 +2240,38 @@ var PRBEAgent = class _PRBEAgent {
2357
2240
  static clearPersistedData() {
2358
2241
  try {
2359
2242
  const filePath = getPersistencePath();
2360
- if (fs3.existsSync(filePath)) {
2361
- fs3.unlinkSync(filePath);
2243
+ if (fs2.existsSync(filePath)) {
2244
+ fs2.unlinkSync(filePath);
2362
2245
  }
2363
- HistoryStore.clear();
2364
2246
  } catch {
2365
2247
  }
2366
2248
  }
2367
- // ---------- CR Investigation ----------
2249
+ // ---------- Background Investigation ----------
2368
2250
  async investigateForCR(cr, ticketId) {
2369
- const crID = cr.id;
2370
- this.currentInvestigationSource = "context_request" /* CONTEXT_REQUEST */;
2371
- this.currentCRId = crID;
2372
- this.state.beginCR(crID, cr.query, cr.slug ?? void 0, ticketId);
2373
- const emitter = (status) => {
2374
- switch (status.type) {
2375
- case "started" /* STARTED */:
2376
- this.state.appendCREvent(crID, "Starting investigation...");
2377
- break;
2378
- case "thinking" /* THINKING */:
2379
- break;
2380
- case "tool_call" /* TOOL_CALL */:
2381
- this.state.appendCREvent(crID, status.label);
2382
- break;
2383
- case "observation" /* OBSERVATION */:
2384
- this.state.attachCRObservation(crID, status.text);
2385
- break;
2386
- case "thought" /* THOUGHT */:
2387
- this.state.appendCREvent(crID, "Thinking", status.text);
2388
- break;
2389
- case "completed" /* COMPLETED */:
2390
- this.state.completeCR(crID, status.report);
2391
- this.historyStore.save(this.state.completedInvestigations, this.state.completedCRs);
2392
- break;
2393
- case "error" /* ERROR */:
2394
- this.state.failCR(crID, status.message);
2395
- this.historyStore.save(this.state.completedInvestigations, this.state.completedCRs);
2396
- break;
2397
- }
2398
- };
2399
- const result = await this.connectToProxy(
2400
- cr.query,
2401
- crID,
2402
- emitter,
2403
- () => false,
2404
- // CRs are not user-cancellable
2405
- ticketId
2406
- );
2407
- this.currentInvestigationSource = "user" /* USER */;
2408
- this.currentCRId = null;
2409
- if (result?.sessionId) {
2410
- this.addTrackedSession(result.sessionId);
2411
- }
2251
+ this.state.beginBackgroundInvestigation(cr.id, cr.query, cr.slug ?? void 0, ticketId);
2252
+ await this.runInvestigation({
2253
+ query: cr.query,
2254
+ source: "context_request" /* CONTEXT_REQUEST */,
2255
+ sourceId: cr.id,
2256
+ contextRequestId: cr.id,
2257
+ ticketId,
2258
+ cancellable: false
2259
+ });
2260
+ }
2261
+ async investigateForER(er) {
2262
+ this.state.beginBackgroundInvestigation(er.id, er.query, void 0, void 0, er.source, er.source_detail);
2263
+ await this.runInvestigation({
2264
+ query: er.query,
2265
+ source: "external_request" /* EXTERNAL_REQUEST */,
2266
+ sourceId: er.id,
2267
+ externalRequestId: er.id,
2268
+ externalRequestSource: er.source,
2269
+ externalRequestSourceDetail: er.source_detail,
2270
+ cancellable: false
2271
+ });
2412
2272
  }
2413
2273
  // ---------- WebSocket Investigation ----------
2414
- connectToProxy(query, contextRequestID, emit, isCancelled, ticketId) {
2274
+ connectToProxy(query, contextRequestID, externalRequestId, externalRequestSource, externalRequestSourceDetail, emit, isCancelled, ticketId) {
2415
2275
  return new Promise((resolve2) => {
2416
2276
  const wsUrl = `${MIDDLEWARE_URL}/api/agent/client/ws`;
2417
2277
  let uploadBaseUrl;
@@ -2431,8 +2291,8 @@ var PRBEAgent = class _PRBEAgent {
2431
2291
  uploadBaseUrl = url;
2432
2292
  }),
2433
2293
  (message) => {
2434
- if (!isCancelled()) emit({ type: "error" /* ERROR */, message });
2435
- finish(null);
2294
+ if (!resolved && !isCancelled()) emit({ type: "error" /* ERROR */, message });
2295
+ if (!resolved) finish(null);
2436
2296
  },
2437
2297
  () => {
2438
2298
  if (!resolved) {
@@ -2466,11 +2326,14 @@ var PRBEAgent = class _PRBEAgent {
2466
2326
  const startMetadata = {
2467
2327
  agent_id: this.agentID,
2468
2328
  custom_tools: toolDeclarations,
2469
- platform: os2.platform(),
2470
- os_version: os2.release(),
2471
- arch: os2.arch()
2329
+ platform: os.platform(),
2330
+ os_version: os.release(),
2331
+ arch: os.arch()
2472
2332
  };
2473
2333
  if (contextRequestID) startMetadata["context_request_id"] = contextRequestID;
2334
+ if (externalRequestId) startMetadata["external_request_id"] = externalRequestId;
2335
+ if (externalRequestSource) startMetadata["external_request_source"] = externalRequestSource;
2336
+ if (externalRequestSourceDetail) startMetadata["external_request_source_detail"] = externalRequestSourceDetail;
2474
2337
  if (ticketId) startMetadata["ticket_id"] = ticketId;
2475
2338
  if (this.appDataPath) startMetadata["app_data_path"] = this.appDataPath;
2476
2339
  const enrichedMetadata = { ...this.sessionMetadata };
@@ -2518,7 +2381,7 @@ var PRBEAgent = class _PRBEAgent {
2518
2381
  const uploadPrefix = this.extractUploadPrefix(uploadBaseUrl);
2519
2382
  const uploadedRefs = [];
2520
2383
  for (const file of this.pendingFlaggedFiles) {
2521
- const filename = path5.basename(file.originalPath);
2384
+ const filename = path4.basename(file.originalPath);
2522
2385
  const safeName = encodeURIComponent(filename);
2523
2386
  const storagePath = `${uploadPrefix}/${safeName}`;
2524
2387
  uploadedRefs.push({
@@ -2563,8 +2426,47 @@ var PRBEAgent = class _PRBEAgent {
2563
2426
  case "conversation_update" /* CONVERSATION_UPDATE */: {
2564
2427
  try {
2565
2428
  const entry = JSON.parse(msg.content ?? "{}");
2566
- this.state.appendConversation(entry);
2429
+ if (this.currentInvestigationSource !== "user" /* USER */ && this.currentBackgroundId) {
2430
+ this.state.appendBackgroundConversation(this.currentBackgroundId, entry);
2431
+ } else {
2432
+ this.state.appendConversation(entry);
2433
+ }
2434
+ } catch {
2435
+ }
2436
+ break;
2437
+ }
2438
+ case "privacy_sanitizing" /* PRIVACY_SANITIZING */: {
2439
+ emit({ type: "privacy_sanitizing" /* PRIVACY_SANITIZING */ });
2440
+ break;
2441
+ }
2442
+ case "privacy_review" /* PRIVACY_REVIEW */: {
2443
+ const sanitizedText = msg.content ?? "";
2444
+ const issues = msg.metadata?.issues ?? [];
2445
+ const files = msg.metadata?.files ?? [];
2446
+ const summary = msg.metadata?.summary ?? "";
2447
+ const payload = {
2448
+ type: "review_sanitized_output" /* REVIEW_SANITIZED_OUTPUT */,
2449
+ interactionId: randomUUID5(),
2450
+ sanitizedAnalysis: sanitizedText,
2451
+ files,
2452
+ summary,
2453
+ issues
2454
+ };
2455
+ try {
2456
+ const response = await this.requestUserInteraction(payload);
2457
+ const reviewResponse = response;
2458
+ conn.send({
2459
+ type: "privacy_review_response" /* PRIVACY_REVIEW_RESPONSE */,
2460
+ metadata: {
2461
+ approved: reviewResponse.approved,
2462
+ ...reviewResponse.editedText ? { editedText: reviewResponse.editedText } : {}
2463
+ }
2464
+ });
2567
2465
  } catch {
2466
+ conn.send({
2467
+ type: "privacy_review_response" /* PRIVACY_REVIEW_RESPONSE */,
2468
+ metadata: { approved: false }
2469
+ });
2568
2470
  }
2569
2471
  break;
2570
2472
  }
@@ -2621,24 +2523,9 @@ var PRBEAgent = class _PRBEAgent {
2621
2523
  startPolling() {
2622
2524
  this.stopPolling();
2623
2525
  this.pollingTimer = setInterval(() => {
2624
- void this.poll().then(() => {
2625
- if (this.trackedSessionIDs.length === 0) {
2626
- this.stopPolling();
2627
- }
2628
- });
2526
+ void this.poll();
2629
2527
  }, this.config.pollingInterval);
2630
2528
  }
2631
- // ---------- Session Resolution ----------
2632
- async resolveSessions(sessionIDs) {
2633
- const request = {
2634
- agent_id: this.agentID,
2635
- session_ids: sessionIDs
2636
- };
2637
- return this.post(
2638
- "/api/agent/resolve-sessions",
2639
- request
2640
- );
2641
- }
2642
2529
  // ---------- Networking ----------
2643
2530
  /**
2644
2531
  * Abort all in-flight fetch requests to prevent orphaned DNS lookups
@@ -2685,6 +2572,7 @@ var PRBEAgent = class _PRBEAgent {
2685
2572
  // src/serialization.ts
2686
2573
  var DEFAULT_PRBE_STATE = {
2687
2574
  isInvestigating: false,
2575
+ isPrivacySanitizing: false,
2688
2576
  events: [],
2689
2577
  report: "",
2690
2578
  summary: "",
@@ -2692,34 +2580,38 @@ var DEFAULT_PRBE_STATE = {
2692
2580
  resolvedInteractions: [],
2693
2581
  conversationHistory: [],
2694
2582
  completedInvestigations: [],
2695
- activeCRs: [],
2696
- completedCRs: [],
2697
- trackedSessionIDs: [],
2583
+ activeBackgroundInvestigations: [],
2584
+ completedBackgroundInvestigations: [],
2698
2585
  ticketInfo: [],
2586
+ agentHistory: [],
2699
2587
  hasActiveWork: false
2700
2588
  };
2701
- function serializeCR(cr) {
2589
+ function serializeBackgroundInvestigation(bg) {
2702
2590
  return {
2703
- id: cr.id,
2704
- query: cr.query,
2705
- slug: cr.slug,
2706
- ticketId: cr.ticketId,
2707
- events: cr.events,
2708
- isRunning: cr.isRunning,
2709
- isCompleted: cr.isCompleted,
2710
- isFailed: cr.isFailed,
2711
- report: cr.report,
2712
- summary: cr.summary,
2713
- errorMessage: cr.errorMessage,
2714
- agentMessage: cr.agentMessage,
2715
- startedAt: cr.startedAt.toISOString(),
2716
- pendingInteraction: cr.pendingInteraction,
2717
- resolvedInteractions: cr.resolvedInteractions ?? []
2591
+ id: bg.id,
2592
+ query: bg.query,
2593
+ slug: bg.slug,
2594
+ ticketId: bg.ticketId,
2595
+ source: bg.source,
2596
+ sourceDetail: bg.sourceDetail,
2597
+ events: bg.events,
2598
+ isRunning: bg.isRunning,
2599
+ isCompleted: bg.isCompleted,
2600
+ isFailed: bg.isFailed,
2601
+ report: bg.report,
2602
+ summary: bg.summary,
2603
+ errorMessage: bg.errorMessage,
2604
+ agentMessage: bg.agentMessage,
2605
+ startedAt: bg.startedAt.toISOString(),
2606
+ pendingInteraction: bg.pendingInteraction,
2607
+ resolvedInteractions: bg.resolvedInteractions ?? [],
2608
+ conversationHistory: bg.conversationHistory ?? []
2718
2609
  };
2719
2610
  }
2720
2611
  function serializePRBEState(state) {
2721
2612
  return {
2722
2613
  isInvestigating: state.isInvestigating,
2614
+ isPrivacySanitizing: state.isPrivacySanitizing,
2723
2615
  events: state.events,
2724
2616
  report: state.report,
2725
2617
  summary: state.summary,
@@ -2740,10 +2632,10 @@ function serializePRBEState(state) {
2740
2632
  conversationHistory: inv.conversationHistory,
2741
2633
  completedAt: inv.completedAt.toISOString()
2742
2634
  })),
2743
- activeCRs: Array.from(state.activeCRs.values()).map(serializeCR),
2744
- completedCRs: state.completedCRs.map(serializeCR),
2745
- trackedSessionIDs: state.trackedSessionIDs,
2635
+ activeBackgroundInvestigations: Array.from(state.activeBackgroundInvestigations.values()).map(serializeBackgroundInvestigation),
2636
+ completedBackgroundInvestigations: state.completedBackgroundInvestigations.map(serializeBackgroundInvestigation),
2746
2637
  ticketInfo: state.ticketInfo,
2638
+ agentHistory: state.agentHistory,
2747
2639
  hasActiveWork: state.hasActiveWork
2748
2640
  };
2749
2641
  }