@prbe.ai/electron-sdk 0.1.18 → 0.1.21

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,5 +1,5 @@
1
1
  // package.json
2
- var version = "0.1.18";
2
+ var version = "0.1.21";
3
3
 
4
4
  // src/agent.ts
5
5
  import * as fs2 from "fs";
@@ -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) => {
@@ -51,6 +54,8 @@ var ToolName = /* @__PURE__ */ ((ToolName2) => {
51
54
  ToolName2["CLIENT_ASK_USER"] = "client_ask_user";
52
55
  ToolName2["CLIENT_BASH_EXECUTE"] = "client_bash_execute";
53
56
  ToolName2["CLIENT_MESSAGE_USER"] = "client_message_user";
57
+ ToolName2["SDK_MESSAGE_USER"] = "sdk_message_user";
58
+ ToolName2["SDK_ASK_USER"] = "sdk_ask_user";
54
59
  return ToolName2;
55
60
  })(ToolName || {});
56
61
  var UserIdentifierType = /* @__PURE__ */ ((UserIdentifierType2) => {
@@ -82,6 +87,7 @@ var PRBEAgentStatusType = /* @__PURE__ */ ((PRBEAgentStatusType2) => {
82
87
  PRBEAgentStatusType2["COMPLETED"] = "completed";
83
88
  PRBEAgentStatusType2["ERROR"] = "error";
84
89
  PRBEAgentStatusType2["AWAITING_INTERACTION"] = "awaiting_interaction";
90
+ PRBEAgentStatusType2["PRIVACY_SANITIZING"] = "privacy_sanitizing";
85
91
  return PRBEAgentStatusType2;
86
92
  })(PRBEAgentStatusType || {});
87
93
  var PRBEAgentErrorType = /* @__PURE__ */ ((PRBEAgentErrorType2) => {
@@ -121,7 +127,9 @@ var InvestigationConnection = class {
121
127
  if (!raw) return;
122
128
  try {
123
129
  const msg = JSON.parse(raw);
124
- this.onMessage(msg);
130
+ Promise.resolve(this.onMessage(msg)).catch((err) => {
131
+ this.onError(`Message handler error: ${err instanceof Error ? err.message : String(err)}`);
132
+ });
125
133
  } catch {
126
134
  }
127
135
  };
@@ -147,10 +155,14 @@ var InvestigationConnection = class {
147
155
  return false;
148
156
  }
149
157
  }
150
- sendConversationMessage(content) {
158
+ sendConversationMessage(content, role, label) {
159
+ const metadata = {};
160
+ if (role) metadata["role"] = role;
161
+ if (label) metadata["label"] = label;
151
162
  this.send({
152
163
  type: "conversation_message" /* CONVERSATION_MESSAGE */,
153
- content
164
+ content,
165
+ ...Object.keys(metadata).length > 0 ? { metadata } : {}
154
166
  });
155
167
  }
156
168
  sendToolResult(callId, toolName, result, metadata) {
@@ -213,6 +225,7 @@ var PRBEAgentState = class extends EventEmitter {
213
225
  resolvedInteractions = [];
214
226
  agentMessage;
215
227
  conversationHistory = [];
228
+ isPrivacySanitizing = false;
216
229
  // Completed user investigations (history)
217
230
  completedInvestigations = [];
218
231
  // Background investigations (context requests, external requests, etc.)
@@ -310,6 +323,7 @@ var PRBEAgentState = class extends EventEmitter {
310
323
  });
311
324
  this.report = report;
312
325
  this.isInvestigating = false;
326
+ this.isPrivacySanitizing = false;
313
327
  this.emit("complete" /* COMPLETE */, { report });
314
328
  this.emit("status" /* STATUS */);
315
329
  }
@@ -317,9 +331,14 @@ var PRBEAgentState = class extends EventEmitter {
317
331
  this.appendEvent(`Error: ${message}`);
318
332
  this.investigationError = message;
319
333
  this.isInvestigating = false;
334
+ this.isPrivacySanitizing = false;
320
335
  this.emit("error" /* ERROR */, { message });
321
336
  this.emit("status" /* STATUS */);
322
337
  }
338
+ setPrivacySanitizing(value) {
339
+ this.isPrivacySanitizing = value;
340
+ this.emit("status" /* STATUS */);
341
+ }
323
342
  // ---------- Interaction state mutations ----------
324
343
  setPendingInteraction(payload) {
325
344
  this.pendingInteraction = payload;
@@ -500,6 +519,7 @@ var InteractionType = /* @__PURE__ */ ((InteractionType2) => {
500
519
  InteractionType2["ASK_QUESTION"] = "ask_question";
501
520
  InteractionType2["REQUEST_PERMISSION"] = "request_permission";
502
521
  InteractionType2["REQUEST_PATH_ACCESS"] = "request_path_access";
522
+ InteractionType2["REVIEW_SANITIZED_OUTPUT"] = "review_sanitized_output";
503
523
  return InteractionType2;
504
524
  })(InteractionType || {});
505
525
  var InvestigationSource = /* @__PURE__ */ ((InvestigationSource2) => {
@@ -1384,7 +1404,7 @@ var AskUserTool = class {
1384
1404
  }
1385
1405
  get declaration() {
1386
1406
  return {
1387
- name: "client_ask_user" /* CLIENT_ASK_USER */,
1407
+ name: "sdk_ask_user" /* SDK_ASK_USER */,
1388
1408
  description: "Ask the user a question and wait for their response. Use this when you need clarification or additional information from the user to continue the investigation.",
1389
1409
  interactive: true,
1390
1410
  parameters: [
@@ -1413,7 +1433,8 @@ var AskUserTool = class {
1413
1433
  const response = await this.requester.requestUserInteraction({
1414
1434
  type: "ask_question" /* ASK_QUESTION */,
1415
1435
  interactionId: randomUUID3(),
1416
- question: reason
1436
+ question,
1437
+ context: reason
1417
1438
  });
1418
1439
  const askResponse = response;
1419
1440
  return askResponse.answer;
@@ -1623,6 +1644,10 @@ var BashExecuteTool = class {
1623
1644
  return `Error: working directory '${cwdArg}' is outside auto-approved directories`;
1624
1645
  }
1625
1646
  cwd = resolved;
1647
+ } else if (this.autoApprovedDirs.length > 0) {
1648
+ cwd = this.autoApprovedDirs[0];
1649
+ } else {
1650
+ return "Error: no approved directories configured and no working directory specified";
1626
1651
  }
1627
1652
  const isSafe = this.isCommandSafe(command);
1628
1653
  if (!isSafe) {
@@ -1818,7 +1843,7 @@ var PRBEAgent = class _PRBEAgent {
1818
1843
  }
1819
1844
  this.registry.register(
1820
1845
  new PRBEClosureTool(
1821
- "client_message_user",
1846
+ "sdk_message_user" /* SDK_MESSAGE_USER */,
1822
1847
  "Send a message to the user.",
1823
1848
  [{ name: "message", type: "STRING" /* STRING */, description: "Message for the user", required: true }],
1824
1849
  async (args) => {
@@ -1886,8 +1911,8 @@ var PRBEAgent = class _PRBEAgent {
1886
1911
  get investigationSource() {
1887
1912
  return this.currentInvestigationSource;
1888
1913
  }
1889
- sendConversationMessage(content) {
1890
- this.activeConnection?.sendConversationMessage(content);
1914
+ sendConversationMessage(content, role, label) {
1915
+ this.activeConnection?.sendConversationMessage(content, role, label);
1891
1916
  }
1892
1917
  async requestUserInteraction(payload) {
1893
1918
  if (!this.interactionHandler) {
@@ -1896,6 +1921,19 @@ var PRBEAgent = class _PRBEAgent {
1896
1921
  "No interaction handler configured"
1897
1922
  );
1898
1923
  }
1924
+ if (payload.type === "request_permission" /* REQUEST_PERMISSION */) {
1925
+ const p = payload;
1926
+ const content = p.reason ? `${p.command}
1927
+
1928
+ ${p.reason}` : p.command;
1929
+ this.sendConversationMessage(content, "agent" /* Agent */, p.action);
1930
+ } else if (payload.type === "request_path_access" /* REQUEST_PATH_ACCESS */) {
1931
+ const p = payload;
1932
+ const content = p.reason ? `${p.path}
1933
+
1934
+ ${p.reason}` : p.path;
1935
+ this.sendConversationMessage(content, "agent" /* Agent */, "Request path access");
1936
+ }
1899
1937
  if (this.currentInvestigationSource !== "user" /* USER */ && this.currentBackgroundId) {
1900
1938
  this.state.setBackgroundPendingInteraction(this.currentBackgroundId, payload);
1901
1939
  } else {
@@ -2041,6 +2079,14 @@ var PRBEAgent = class _PRBEAgent {
2041
2079
  this.state.failInvestigation(status.message);
2042
2080
  }
2043
2081
  break;
2082
+ case "privacy_sanitizing" /* PRIVACY_SANITIZING */:
2083
+ if (isBackground && opts.sourceId) {
2084
+ this.state.appendBackgroundEvent(opts.sourceId, "Sanitizing data for privacy review...");
2085
+ } else {
2086
+ this.state.appendEvent("Sanitizing data for privacy review...");
2087
+ }
2088
+ this.state.setPrivacySanitizing(true);
2089
+ break;
2044
2090
  }
2045
2091
  };
2046
2092
  const result = await this.connectToProxy(
@@ -2133,6 +2179,30 @@ var PRBEAgent = class _PRBEAgent {
2133
2179
  return null;
2134
2180
  }
2135
2181
  }
2182
+ async fetchSanitizedFile(url) {
2183
+ const res = await fetch(url, {
2184
+ headers: { "X-API-Key": this.config.apiKey }
2185
+ });
2186
+ if (!res.ok) throw new Error(`Failed to fetch sanitized file: ${res.status}`);
2187
+ const buffer = Buffer.from(await res.arrayBuffer());
2188
+ if (!_PRBEAgent.isTextContent(buffer)) {
2189
+ return { isText: false, url };
2190
+ }
2191
+ return { isText: true, content: buffer.toString("utf-8"), url };
2192
+ }
2193
+ /**
2194
+ * Detect whether a buffer contains text by checking for null bytes
2195
+ * and verifying the content is valid UTF-8.
2196
+ */
2197
+ static isTextContent(buffer) {
2198
+ const checkLength = Math.min(buffer.length, 8192);
2199
+ for (let i = 0; i < checkLength; i++) {
2200
+ if (buffer[i] === 0) return false;
2201
+ }
2202
+ const decoded = buffer.toString("utf-8");
2203
+ const reEncoded = Buffer.from(decoded, "utf-8");
2204
+ return buffer.length === reEncoded.length;
2205
+ }
2136
2206
  stopPolling() {
2137
2207
  if (this.pollingTimer !== null) {
2138
2208
  clearInterval(this.pollingTimer);
@@ -2222,8 +2292,8 @@ var PRBEAgent = class _PRBEAgent {
2222
2292
  uploadBaseUrl = url;
2223
2293
  }),
2224
2294
  (message) => {
2225
- if (!isCancelled()) emit({ type: "error" /* ERROR */, message });
2226
- finish(null);
2295
+ if (!resolved && !isCancelled()) emit({ type: "error" /* ERROR */, message });
2296
+ if (!resolved) finish(null);
2227
2297
  },
2228
2298
  () => {
2229
2299
  if (!resolved) {
@@ -2256,6 +2326,8 @@ var PRBEAgent = class _PRBEAgent {
2256
2326
  }));
2257
2327
  const startMetadata = {
2258
2328
  agent_id: this.agentID,
2329
+ sdk_name: "electron",
2330
+ supports_chat: true,
2259
2331
  custom_tools: toolDeclarations,
2260
2332
  platform: os.platform(),
2261
2333
  os_version: os.release(),
@@ -2304,7 +2376,12 @@ var PRBEAgent = class _PRBEAgent {
2304
2376
  const args = this.extractArgs(msg.metadata);
2305
2377
  emit({ type: "tool_call" /* TOOL_CALL */, name: toolName, label: msg.content ?? `Running ${toolName}` });
2306
2378
  this.pendingFlaggedFiles = [];
2307
- const toolResult = redactPII(await this.registry.execute(toolName, args));
2379
+ let toolResult;
2380
+ try {
2381
+ toolResult = redactPII(await this.registry.execute(toolName, args));
2382
+ } catch (err) {
2383
+ toolResult = `Error: Tool execution failed: ${err instanceof Error ? err.message : String(err)}`;
2384
+ }
2308
2385
  emit({ type: "observation" /* OBSERVATION */, text: toolResult.substring(0, 200) });
2309
2386
  let resultMetadata;
2310
2387
  const uploadBaseUrl = getUploadBaseUrl();
@@ -2366,6 +2443,41 @@ var PRBEAgent = class _PRBEAgent {
2366
2443
  }
2367
2444
  break;
2368
2445
  }
2446
+ case "privacy_sanitizing" /* PRIVACY_SANITIZING */: {
2447
+ emit({ type: "privacy_sanitizing" /* PRIVACY_SANITIZING */ });
2448
+ break;
2449
+ }
2450
+ case "privacy_review" /* PRIVACY_REVIEW */: {
2451
+ const sanitizedText = msg.content ?? "";
2452
+ const issues = msg.metadata?.issues ?? [];
2453
+ const files = msg.metadata?.files ?? [];
2454
+ const summary = msg.metadata?.summary ?? "";
2455
+ const payload = {
2456
+ type: "review_sanitized_output" /* REVIEW_SANITIZED_OUTPUT */,
2457
+ interactionId: randomUUID5(),
2458
+ sanitizedAnalysis: sanitizedText,
2459
+ files,
2460
+ summary,
2461
+ issues
2462
+ };
2463
+ try {
2464
+ const response = await this.requestUserInteraction(payload);
2465
+ const reviewResponse = response;
2466
+ conn.send({
2467
+ type: "privacy_review_response" /* PRIVACY_REVIEW_RESPONSE */,
2468
+ metadata: {
2469
+ approved: reviewResponse.approved,
2470
+ ...reviewResponse.editedText ? { editedText: reviewResponse.editedText } : {}
2471
+ }
2472
+ });
2473
+ } catch {
2474
+ conn.send({
2475
+ type: "privacy_review_response" /* PRIVACY_REVIEW_RESPONSE */,
2476
+ metadata: { approved: false }
2477
+ });
2478
+ }
2479
+ break;
2480
+ }
2369
2481
  case "ping" /* PING */:
2370
2482
  conn.sendPong();
2371
2483
  break;
@@ -2468,6 +2580,7 @@ var PRBEAgent = class _PRBEAgent {
2468
2580
  // src/serialization.ts
2469
2581
  var DEFAULT_PRBE_STATE = {
2470
2582
  isInvestigating: false,
2583
+ isPrivacySanitizing: false,
2471
2584
  events: [],
2472
2585
  report: "",
2473
2586
  summary: "",
@@ -2506,6 +2619,7 @@ function serializeBackgroundInvestigation(bg) {
2506
2619
  function serializePRBEState(state) {
2507
2620
  return {
2508
2621
  isInvestigating: state.isInvestigating,
2622
+ isPrivacySanitizing: state.isPrivacySanitizing,
2509
2623
  events: state.events,
2510
2624
  report: state.report,
2511
2625
  summary: state.summary,