@phigon/observer 8.0.1

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.
Files changed (284) hide show
  1. package/README.md +303 -0
  2. package/dist/bin/guardian-runner-darwin-amd64 +0 -0
  3. package/dist/bin/guardian-runner-darwin-arm64 +0 -0
  4. package/dist/bin/guardian-runner-linux-amd64 +0 -0
  5. package/dist/bin/guardian-runner-linux-arm64 +0 -0
  6. package/dist/bin/guardian-runner-windows-amd64.exe +0 -0
  7. package/dist/bin/guardian-runner-windows-arm64.exe +0 -0
  8. package/dist/src/action-policy.d.ts +38 -0
  9. package/dist/src/action-policy.d.ts.map +1 -0
  10. package/dist/src/action-policy.js +269 -0
  11. package/dist/src/action-policy.js.map +1 -0
  12. package/dist/src/activity/adapters/claude.d.ts +3 -0
  13. package/dist/src/activity/adapters/claude.d.ts.map +1 -0
  14. package/dist/src/activity/adapters/claude.js +89 -0
  15. package/dist/src/activity/adapters/claude.js.map +1 -0
  16. package/dist/src/activity/adapters/codex.d.ts +3 -0
  17. package/dist/src/activity/adapters/codex.d.ts.map +1 -0
  18. package/dist/src/activity/adapters/codex.js +277 -0
  19. package/dist/src/activity/adapters/codex.js.map +1 -0
  20. package/dist/src/activity/adapters/copilot.d.ts +2 -0
  21. package/dist/src/activity/adapters/copilot.d.ts.map +1 -0
  22. package/dist/src/activity/adapters/copilot.js +4 -0
  23. package/dist/src/activity/adapters/copilot.js.map +1 -0
  24. package/dist/src/activity/adapters/generic.d.ts +2 -0
  25. package/dist/src/activity/adapters/generic.d.ts.map +1 -0
  26. package/dist/src/activity/adapters/generic.js +4 -0
  27. package/dist/src/activity/adapters/generic.js.map +1 -0
  28. package/dist/src/activity/adapters/hermes.d.ts +2 -0
  29. package/dist/src/activity/adapters/hermes.d.ts.map +1 -0
  30. package/dist/src/activity/adapters/hermes.js +4 -0
  31. package/dist/src/activity/adapters/hermes.js.map +1 -0
  32. package/dist/src/activity/adapters/openai-compatible.d.ts +4 -0
  33. package/dist/src/activity/adapters/openai-compatible.d.ts.map +1 -0
  34. package/dist/src/activity/adapters/openai-compatible.js +116 -0
  35. package/dist/src/activity/adapters/openai-compatible.js.map +1 -0
  36. package/dist/src/activity/adapters/openclaw.d.ts +2 -0
  37. package/dist/src/activity/adapters/openclaw.d.ts.map +1 -0
  38. package/dist/src/activity/adapters/openclaw.js +4 -0
  39. package/dist/src/activity/adapters/openclaw.js.map +1 -0
  40. package/dist/src/activity/adapters/opencode.d.ts +3 -0
  41. package/dist/src/activity/adapters/opencode.d.ts.map +1 -0
  42. package/dist/src/activity/adapters/opencode.js +137 -0
  43. package/dist/src/activity/adapters/opencode.js.map +1 -0
  44. package/dist/src/activity/bridges.d.ts +7 -0
  45. package/dist/src/activity/bridges.d.ts.map +1 -0
  46. package/dist/src/activity/bridges.js +17 -0
  47. package/dist/src/activity/bridges.js.map +1 -0
  48. package/dist/src/activity/collector.d.ts +90 -0
  49. package/dist/src/activity/collector.d.ts.map +1 -0
  50. package/dist/src/activity/collector.js +706 -0
  51. package/dist/src/activity/collector.js.map +1 -0
  52. package/dist/src/activity/index.d.ts +14 -0
  53. package/dist/src/activity/index.d.ts.map +1 -0
  54. package/dist/src/activity/index.js +14 -0
  55. package/dist/src/activity/index.js.map +1 -0
  56. package/dist/src/activity/ingest.d.ts +15 -0
  57. package/dist/src/activity/ingest.d.ts.map +1 -0
  58. package/dist/src/activity/ingest.js +103 -0
  59. package/dist/src/activity/ingest.js.map +1 -0
  60. package/dist/src/activity/native-capabilities.d.ts +14 -0
  61. package/dist/src/activity/native-capabilities.d.ts.map +1 -0
  62. package/dist/src/activity/native-capabilities.js +85 -0
  63. package/dist/src/activity/native-capabilities.js.map +1 -0
  64. package/dist/src/activity/normalize.d.ts +17 -0
  65. package/dist/src/activity/normalize.d.ts.map +1 -0
  66. package/dist/src/activity/normalize.js +416 -0
  67. package/dist/src/activity/normalize.js.map +1 -0
  68. package/dist/src/activity/opencode-sqlite-source.d.ts +19 -0
  69. package/dist/src/activity/opencode-sqlite-source.d.ts.map +1 -0
  70. package/dist/src/activity/opencode-sqlite-source.js +305 -0
  71. package/dist/src/activity/opencode-sqlite-source.js.map +1 -0
  72. package/dist/src/activity/policy.d.ts +48 -0
  73. package/dist/src/activity/policy.d.ts.map +1 -0
  74. package/dist/src/activity/policy.js +436 -0
  75. package/dist/src/activity/policy.js.map +1 -0
  76. package/dist/src/activity/record.d.ts +7 -0
  77. package/dist/src/activity/record.d.ts.map +1 -0
  78. package/dist/src/activity/record.js +61 -0
  79. package/dist/src/activity/record.js.map +1 -0
  80. package/dist/src/activity/source-reader.d.ts +46 -0
  81. package/dist/src/activity/source-reader.d.ts.map +1 -0
  82. package/dist/src/activity/source-reader.js +594 -0
  83. package/dist/src/activity/source-reader.js.map +1 -0
  84. package/dist/src/activity/types.d.ts +21 -0
  85. package/dist/src/activity/types.d.ts.map +1 -0
  86. package/dist/src/activity/types.js +2 -0
  87. package/dist/src/activity/types.js.map +1 -0
  88. package/dist/src/agent-action-executor.d.ts +21 -0
  89. package/dist/src/agent-action-executor.d.ts.map +1 -0
  90. package/dist/src/agent-action-executor.js +248 -0
  91. package/dist/src/agent-action-executor.js.map +1 -0
  92. package/dist/src/agentic-guardian.d.ts +50 -0
  93. package/dist/src/agentic-guardian.d.ts.map +1 -0
  94. package/dist/src/agentic-guardian.js +220 -0
  95. package/dist/src/agentic-guardian.js.map +1 -0
  96. package/dist/src/atomic-replace.d.ts +4 -0
  97. package/dist/src/atomic-replace.d.ts.map +1 -0
  98. package/dist/src/atomic-replace.js +18 -0
  99. package/dist/src/atomic-replace.js.map +1 -0
  100. package/dist/src/cli.d.ts +11 -0
  101. package/dist/src/cli.d.ts.map +1 -0
  102. package/dist/src/cli.js +1244 -0
  103. package/dist/src/cli.js.map +1 -0
  104. package/dist/src/config.d.ts +68 -0
  105. package/dist/src/config.d.ts.map +1 -0
  106. package/dist/src/config.js +260 -0
  107. package/dist/src/config.js.map +1 -0
  108. package/dist/src/credential-store.d.ts +22 -0
  109. package/dist/src/credential-store.d.ts.map +1 -0
  110. package/dist/src/credential-store.js +91 -0
  111. package/dist/src/credential-store.js.map +1 -0
  112. package/dist/src/custom-agent-registry.d.ts +26 -0
  113. package/dist/src/custom-agent-registry.d.ts.map +1 -0
  114. package/dist/src/custom-agent-registry.js +288 -0
  115. package/dist/src/custom-agent-registry.js.map +1 -0
  116. package/dist/src/discovery/adapters/claude.d.ts +3 -0
  117. package/dist/src/discovery/adapters/claude.d.ts.map +1 -0
  118. package/dist/src/discovery/adapters/claude.js +26 -0
  119. package/dist/src/discovery/adapters/claude.js.map +1 -0
  120. package/dist/src/discovery/adapters/codex.d.ts +3 -0
  121. package/dist/src/discovery/adapters/codex.d.ts.map +1 -0
  122. package/dist/src/discovery/adapters/codex.js +23 -0
  123. package/dist/src/discovery/adapters/codex.js.map +1 -0
  124. package/dist/src/discovery/adapters/custom.d.ts +4 -0
  125. package/dist/src/discovery/adapters/custom.d.ts.map +1 -0
  126. package/dist/src/discovery/adapters/custom.js +24 -0
  127. package/dist/src/discovery/adapters/custom.js.map +1 -0
  128. package/dist/src/discovery/adapters/generic.d.ts +3 -0
  129. package/dist/src/discovery/adapters/generic.d.ts.map +1 -0
  130. package/dist/src/discovery/adapters/generic.js +126 -0
  131. package/dist/src/discovery/adapters/generic.js.map +1 -0
  132. package/dist/src/discovery/adapters/hermes.d.ts +3 -0
  133. package/dist/src/discovery/adapters/hermes.d.ts.map +1 -0
  134. package/dist/src/discovery/adapters/hermes.js +22 -0
  135. package/dist/src/discovery/adapters/hermes.js.map +1 -0
  136. package/dist/src/discovery/adapters/openclaw.d.ts +3 -0
  137. package/dist/src/discovery/adapters/openclaw.d.ts.map +1 -0
  138. package/dist/src/discovery/adapters/openclaw.js +22 -0
  139. package/dist/src/discovery/adapters/openclaw.js.map +1 -0
  140. package/dist/src/discovery/engine.d.ts +17 -0
  141. package/dist/src/discovery/engine.d.ts.map +1 -0
  142. package/dist/src/discovery/engine.js +164 -0
  143. package/dist/src/discovery/engine.js.map +1 -0
  144. package/dist/src/discovery/evidence.d.ts +29 -0
  145. package/dist/src/discovery/evidence.d.ts.map +1 -0
  146. package/dist/src/discovery/evidence.js +2 -0
  147. package/dist/src/discovery/evidence.js.map +1 -0
  148. package/dist/src/discovery/index.d.ts +11 -0
  149. package/dist/src/discovery/index.d.ts.map +1 -0
  150. package/dist/src/discovery/index.js +11 -0
  151. package/dist/src/discovery/index.js.map +1 -0
  152. package/dist/src/discovery/known-agent.d.ts +17 -0
  153. package/dist/src/discovery/known-agent.d.ts.map +1 -0
  154. package/dist/src/discovery/known-agent.js +193 -0
  155. package/dist/src/discovery/known-agent.js.map +1 -0
  156. package/dist/src/discovery/process-identity.d.ts +19 -0
  157. package/dist/src/discovery/process-identity.d.ts.map +1 -0
  158. package/dist/src/discovery/process-identity.js +73 -0
  159. package/dist/src/discovery/process-identity.js.map +1 -0
  160. package/dist/src/discovery/system-evidence.d.ts +9 -0
  161. package/dist/src/discovery/system-evidence.d.ts.map +1 -0
  162. package/dist/src/discovery/system-evidence.js +270 -0
  163. package/dist/src/discovery/system-evidence.js.map +1 -0
  164. package/dist/src/discovery/types.d.ts +21 -0
  165. package/dist/src/discovery/types.d.ts.map +1 -0
  166. package/dist/src/discovery/types.js +2 -0
  167. package/dist/src/discovery/types.js.map +1 -0
  168. package/dist/src/event-queue.d.ts +32 -0
  169. package/dist/src/event-queue.d.ts.map +1 -0
  170. package/dist/src/event-queue.js +125 -0
  171. package/dist/src/event-queue.js.map +1 -0
  172. package/dist/src/event-spool.d.ts +19 -0
  173. package/dist/src/event-spool.d.ts.map +1 -0
  174. package/dist/src/event-spool.js +350 -0
  175. package/dist/src/event-spool.js.map +1 -0
  176. package/dist/src/generated/phigon/v1/action_pb.d.ts +223 -0
  177. package/dist/src/generated/phigon/v1/action_pb.d.ts.map +1 -0
  178. package/dist/src/generated/phigon/v1/action_pb.js +96 -0
  179. package/dist/src/generated/phigon/v1/action_pb.js.map +1 -0
  180. package/dist/src/generated/phigon/v1/common_pb.d.ts +255 -0
  181. package/dist/src/generated/phigon/v1/common_pb.d.ts.map +1 -0
  182. package/dist/src/generated/phigon/v1/common_pb.js +242 -0
  183. package/dist/src/generated/phigon/v1/common_pb.js.map +1 -0
  184. package/dist/src/generated/phigon/v1/history_sync_pb.d.ts +119 -0
  185. package/dist/src/generated/phigon/v1/history_sync_pb.d.ts.map +1 -0
  186. package/dist/src/generated/phigon/v1/history_sync_pb.js +46 -0
  187. package/dist/src/generated/phigon/v1/history_sync_pb.js.map +1 -0
  188. package/dist/src/generated/phigon/v1/identity_pb.d.ts +235 -0
  189. package/dist/src/generated/phigon/v1/identity_pb.d.ts.map +1 -0
  190. package/dist/src/generated/phigon/v1/identity_pb.js +31 -0
  191. package/dist/src/generated/phigon/v1/identity_pb.js.map +1 -0
  192. package/dist/src/generated/phigon/v1/observer_service_pb.d.ts +362 -0
  193. package/dist/src/generated/phigon/v1/observer_service_pb.d.ts.map +1 -0
  194. package/dist/src/generated/phigon/v1/observer_service_pb.js +120 -0
  195. package/dist/src/generated/phigon/v1/observer_service_pb.js.map +1 -0
  196. package/dist/src/generated/phigon/v1/policy_pb.d.ts +456 -0
  197. package/dist/src/generated/phigon/v1/policy_pb.d.ts.map +1 -0
  198. package/dist/src/generated/phigon/v1/policy_pb.js +161 -0
  199. package/dist/src/generated/phigon/v1/policy_pb.js.map +1 -0
  200. package/dist/src/generated/phigon/v1/supervision_pb.d.ts +849 -0
  201. package/dist/src/generated/phigon/v1/supervision_pb.d.ts.map +1 -0
  202. package/dist/src/generated/phigon/v1/supervision_pb.js +384 -0
  203. package/dist/src/generated/phigon/v1/supervision_pb.js.map +1 -0
  204. package/dist/src/generated/phigon/v1/telemetry_pb.d.ts +761 -0
  205. package/dist/src/generated/phigon/v1/telemetry_pb.d.ts.map +1 -0
  206. package/dist/src/generated/phigon/v1/telemetry_pb.js +309 -0
  207. package/dist/src/generated/phigon/v1/telemetry_pb.js.map +1 -0
  208. package/dist/src/guardian-containment.d.ts +30 -0
  209. package/dist/src/guardian-containment.d.ts.map +1 -0
  210. package/dist/src/guardian-containment.js +113 -0
  211. package/dist/src/guardian-containment.js.map +1 -0
  212. package/dist/src/guardian-decision-journal.d.ts +29 -0
  213. package/dist/src/guardian-decision-journal.d.ts.map +1 -0
  214. package/dist/src/guardian-decision-journal.js +55 -0
  215. package/dist/src/guardian-decision-journal.js.map +1 -0
  216. package/dist/src/guardian-execution-manifest.d.ts +29 -0
  217. package/dist/src/guardian-execution-manifest.d.ts.map +1 -0
  218. package/dist/src/guardian-execution-manifest.js +95 -0
  219. package/dist/src/guardian-execution-manifest.js.map +1 -0
  220. package/dist/src/guardian-hosted-client.d.ts +10 -0
  221. package/dist/src/guardian-hosted-client.d.ts.map +1 -0
  222. package/dist/src/guardian-hosted-client.js +45 -0
  223. package/dist/src/guardian-hosted-client.js.map +1 -0
  224. package/dist/src/guardian-managed-exec.d.ts +21 -0
  225. package/dist/src/guardian-managed-exec.d.ts.map +1 -0
  226. package/dist/src/guardian-managed-exec.js +249 -0
  227. package/dist/src/guardian-managed-exec.js.map +1 -0
  228. package/dist/src/guardian-model-bridge.d.ts +13 -0
  229. package/dist/src/guardian-model-bridge.d.ts.map +1 -0
  230. package/dist/src/guardian-model-bridge.js +87 -0
  231. package/dist/src/guardian-model-bridge.js.map +1 -0
  232. package/dist/src/guardian-native.d.ts +3 -0
  233. package/dist/src/guardian-native.d.ts.map +1 -0
  234. package/dist/src/guardian-native.js +13 -0
  235. package/dist/src/guardian-native.js.map +1 -0
  236. package/dist/src/guardian-process-monitor.d.ts +20 -0
  237. package/dist/src/guardian-process-monitor.d.ts.map +1 -0
  238. package/dist/src/guardian-process-monitor.js +98 -0
  239. package/dist/src/guardian-process-monitor.js.map +1 -0
  240. package/dist/src/health.d.ts +35 -0
  241. package/dist/src/health.d.ts.map +1 -0
  242. package/dist/src/health.js +41 -0
  243. package/dist/src/health.js.map +1 -0
  244. package/dist/src/historical-identity-store.d.ts +20 -0
  245. package/dist/src/historical-identity-store.d.ts.map +1 -0
  246. package/dist/src/historical-identity-store.js +391 -0
  247. package/dist/src/historical-identity-store.js.map +1 -0
  248. package/dist/src/index.d.ts +14 -0
  249. package/dist/src/index.d.ts.map +1 -0
  250. package/dist/src/index.js +14 -0
  251. package/dist/src/index.js.map +1 -0
  252. package/dist/src/installation.d.ts +2 -0
  253. package/dist/src/installation.d.ts.map +1 -0
  254. package/dist/src/installation.js +44 -0
  255. package/dist/src/installation.js.map +1 -0
  256. package/dist/src/machine-command-executor.d.ts +28 -0
  257. package/dist/src/machine-command-executor.d.ts.map +1 -0
  258. package/dist/src/machine-command-executor.js +166 -0
  259. package/dist/src/machine-command-executor.js.map +1 -0
  260. package/dist/src/pair.d.ts +21 -0
  261. package/dist/src/pair.d.ts.map +1 -0
  262. package/dist/src/pair.js +125 -0
  263. package/dist/src/pair.js.map +1 -0
  264. package/dist/src/remove-machine.d.ts +15 -0
  265. package/dist/src/remove-machine.d.ts.map +1 -0
  266. package/dist/src/remove-machine.js +77 -0
  267. package/dist/src/remove-machine.js.map +1 -0
  268. package/dist/src/stream-client.d.ts +42 -0
  269. package/dist/src/stream-client.d.ts.map +1 -0
  270. package/dist/src/stream-client.js +867 -0
  271. package/dist/src/stream-client.js.map +1 -0
  272. package/dist/src/stream-state.d.ts +13 -0
  273. package/dist/src/stream-state.d.ts.map +1 -0
  274. package/dist/src/stream-state.js +128 -0
  275. package/dist/src/stream-state.js.map +1 -0
  276. package/dist/src/windows-process.d.ts +7 -0
  277. package/dist/src/windows-process.d.ts.map +1 -0
  278. package/dist/src/windows-process.js +17 -0
  279. package/dist/src/windows-process.js.map +1 -0
  280. package/package.json +78 -0
  281. package/scripts/install.mjs +308 -0
  282. package/scripts/managed-service.mjs +132 -0
  283. package/scripts/verify-guardian-release.mjs +42 -0
  284. package/scripts/windows-app-control.mjs +117 -0
@@ -0,0 +1,867 @@
1
+ import { Client, Metadata, credentials, status, } from "@grpc/grpc-js";
2
+ import { createHash, randomBytes } from "node:crypto";
3
+ import { readFileSync } from "node:fs";
4
+ import { clone, create, fromBinary, toBinary, } from "@bufbuild/protobuf";
5
+ import { timestampFromDate } from "@bufbuild/protobuf/wkt";
6
+ import { ActionCapabilityManifestSchema, AgentActionResultSchema, RemoteActionResultState, } from "./generated/phigon/v1/action_pb.js";
7
+ import { ActionPolicySupersededError, FileActionPolicyStore, } from "./action-policy.js";
8
+ import { LocalPolicyEngine } from "./activity/policy.js";
9
+ import { FileCredentialStore, } from "./credential-store.js";
10
+ import { FileBackedEventQueue } from "./event-spool.js";
11
+ import { HeartbeatSchema, } from "./generated/phigon/v1/identity_pb.js";
12
+ import { HistorySyncResultSchema, HistorySyncResultState, } from "./generated/phigon/v1/history_sync_pb.js";
13
+ import { ResumeCursorSchema, PolicyReceiptSchema, PolicyReceiptState, StreamErrorCode, StreamTelemetryRequestSchema, StreamTelemetryResponseSchema, } from "./generated/phigon/v1/observer_service_pb.js";
14
+ import { ActionGuardrailPolicySchema, GuardrailIntervention, ManagedAction, } from "./generated/phigon/v1/policy_pb.js";
15
+ import { ActivityKind, PrivacyClass, } from "./generated/phigon/v1/common_pb.js";
16
+ import { EventEnvelopeSchema, VendorEventSchema, } from "./generated/phigon/v1/telemetry_pb.js";
17
+ import { StreamStateStore } from "./stream-state.js";
18
+ const streamMethod = "/phigon.v1.ObserverService/StreamTelemetry";
19
+ const maxMessageBytes = 1 << 20;
20
+ const policyReceiptRefreshMs = 5 * 60 * 1_000;
21
+ export class ObserverStreamClient {
22
+ #config;
23
+ #credentialStore;
24
+ #stateStore;
25
+ #queue;
26
+ #actionPolicyStore;
27
+ #onStatus;
28
+ #onPolicy;
29
+ #onCursorReset;
30
+ #onAcknowledgement;
31
+ #actionExecutor;
32
+ #onHistorySync;
33
+ #reconnects = 0;
34
+ #acknowledgements = 0;
35
+ #lastAcknowledgedSequence = 0n;
36
+ #pumpEvents;
37
+ #latestInventory;
38
+ #sendInventory;
39
+ #readySequence;
40
+ #readyWaiters = new Set();
41
+ constructor(options) {
42
+ this.#config = options.config;
43
+ this.#credentialStore =
44
+ options.credentialStore ??
45
+ new FileCredentialStore(options.config.dataDirectory);
46
+ this.#stateStore =
47
+ options.stateStore ??
48
+ new StreamStateStore(options.config.dataDirectory);
49
+ this.#queue =
50
+ options.queue ??
51
+ new FileBackedEventQueue(options.config.dataDirectory, options.config.activityQueueCapacity);
52
+ this.#actionPolicyStore = options.actionPolicyStore ??
53
+ new FileActionPolicyStore(options.config.dataDirectory);
54
+ this.#onStatus = options.onStatus ?? (() => undefined);
55
+ this.#onPolicy = options.onPolicy ?? (() => undefined);
56
+ this.#onCursorReset = options.onCursorReset ?? (() => undefined);
57
+ this.#onAcknowledgement =
58
+ options.onAcknowledgement ?? (() => undefined);
59
+ this.#actionExecutor = options.actionExecutor;
60
+ this.#onHistorySync = options.onHistorySync;
61
+ }
62
+ enqueue(event, coalesceKey) {
63
+ if (!this.#queue.canEnqueue(coalesceKey)) {
64
+ throw new Error("Telemetry delivery queue is full; source collection stopped " +
65
+ "before an unacknowledged event could be discarded.");
66
+ }
67
+ this.#queue.enqueue(event, event.sequence, coalesceKey);
68
+ this.#pumpEvents?.();
69
+ }
70
+ updateInventory(inventory) {
71
+ this.#latestInventory = inventory;
72
+ this.#sendInventory?.(inventory);
73
+ }
74
+ async waitUntilReady(signal) {
75
+ if (this.#readySequence !== undefined)
76
+ return this.#readySequence;
77
+ return await new Promise((resolve, reject) => {
78
+ const ready = (sequence) => {
79
+ signal?.removeEventListener("abort", aborted);
80
+ resolve(sequence);
81
+ };
82
+ const aborted = () => {
83
+ this.#readyWaiters.delete(ready);
84
+ reject(new Error("Telemetry readiness wait was aborted."));
85
+ };
86
+ if (signal?.aborted === true) {
87
+ aborted();
88
+ return;
89
+ }
90
+ this.#readyWaiters.add(ready);
91
+ signal?.addEventListener("abort", aborted, { once: true });
92
+ });
93
+ }
94
+ async run(signal) {
95
+ let backoffMs = 250;
96
+ while (!signal.aborted) {
97
+ try {
98
+ await this.#connectOnce(signal);
99
+ backoffMs = 250;
100
+ }
101
+ catch (error) {
102
+ if (signal.aborted)
103
+ return;
104
+ if (isServiceError(error) && error.code === status.UNAUTHENTICATED) {
105
+ throw new Error("Machine credential was rejected by the control plane.");
106
+ }
107
+ if (error instanceof PermanentTelemetryError)
108
+ throw error;
109
+ this.#reconnects++;
110
+ this.#onStatus(`Telemetry disconnected (${telemetryErrorSummary(error)}); ` +
111
+ `retrying in ${backoffMs}ms.`);
112
+ await abortableDelay(backoffMs, signal);
113
+ backoffMs = Math.min(backoffMs * 2, 10_000);
114
+ }
115
+ }
116
+ }
117
+ metrics() {
118
+ return Object.freeze({
119
+ reconnects: this.#reconnects,
120
+ acknowledgements: this.#acknowledgements,
121
+ lastAcknowledgedSequence: this.#lastAcknowledgedSequence,
122
+ queue: this.#queue.metrics(),
123
+ });
124
+ }
125
+ async #connectOnce(signal) {
126
+ const credential = await this.#credentialStore.load();
127
+ if (credential === null) {
128
+ throw new Error("Observer is not paired. Run the pair command first.");
129
+ }
130
+ if (this.#queue instanceof FileBackedEventQueue) {
131
+ this.#queue.restore(credential.tenantId, credential.machineId, credential.credential);
132
+ }
133
+ const spooledAcknowledgement = this.#queue.metrics().acknowledged;
134
+ const state = await this.#stateStore.load(credential.machineId, this.#queue instanceof FileBackedEventQueue ? spooledAcknowledgement : undefined);
135
+ if (state.recoveryPending)
136
+ this.#onStatus("Local stream cursor is unreadable. Waiting for the authenticated server checkpoint before resuming collection.");
137
+ const localAcknowledgement = spooledAcknowledgement > state.acknowledgedSequence
138
+ ? spooledAcknowledgement
139
+ : state.acknowledgedSequence;
140
+ if (localAcknowledgement > state.acknowledgedSequence) {
141
+ await this.#stateStore.save(credential.machineId, localAcknowledgement);
142
+ }
143
+ this.#lastAcknowledgedSequence = localAcknowledgement;
144
+ this.#queue.reconcileAcknowledged(localAcknowledgement);
145
+ const client = new Client(this.#config.grpcUrl.host, channelCredentials(this.#config), {
146
+ "grpc.max_receive_message_length": maxMessageBytes,
147
+ "grpc.max_send_message_length": maxMessageBytes,
148
+ });
149
+ const metadata = new Metadata();
150
+ metadata.set("authorization", `Bearer ${credential.credential}`);
151
+ metadata.set("traceparent", createTraceParent());
152
+ const stream = client.makeBidiStreamRequest(streamMethod, (value) => Buffer.from(toBinary(StreamTelemetryRequestSchema, value)), (value) => fromBinary(StreamTelemetryResponseSchema, value), metadata);
153
+ this.#onStatus("Telemetry stream connected.");
154
+ const abort = () => stream.cancel();
155
+ signal.addEventListener("abort", abort, { once: true });
156
+ let heartbeat;
157
+ let saveState = Promise.resolve();
158
+ let actionPolicyUpdates = Promise.resolve();
159
+ let actionExecutions = Promise.resolve();
160
+ let historySyncExecutions = Promise.resolve();
161
+ const activeHistorySyncJobs = new Set();
162
+ const connectionAbort = new AbortController();
163
+ let sentThroughSequence = localAcknowledgement;
164
+ const inFlightSequences = new Set();
165
+ let waitingForDrain = false;
166
+ let activePolicy;
167
+ let activeActionPolicy;
168
+ let lastPolicyReceiptAt = 0;
169
+ let acknowledgedResume;
170
+ let resumeComplete = false;
171
+ const pumpEvents = () => {
172
+ if (!resumeComplete ||
173
+ activePolicy === undefined ||
174
+ waitingForDrain ||
175
+ signal.aborted)
176
+ return;
177
+ const available = this.#config.activityMaxInFlightEvents -
178
+ inFlightSequences.size;
179
+ if (available < 1)
180
+ return;
181
+ for (const event of this.#queue.pendingAfter(sentThroughSequence, available)) {
182
+ const sanitized = sanitizeEventForPolicy(event, activePolicy);
183
+ const writable = stream.write(create(StreamTelemetryRequestSchema, {
184
+ payload: { case: "event", value: sanitized },
185
+ }));
186
+ sentThroughSequence = event.sequence;
187
+ inFlightSequences.add(event.sequence);
188
+ if (!writable) {
189
+ waitingForDrain = true;
190
+ stream.once("drain", () => {
191
+ waitingForDrain = false;
192
+ pumpEvents();
193
+ });
194
+ break;
195
+ }
196
+ }
197
+ };
198
+ const completeResume = () => {
199
+ if (resumeComplete ||
200
+ activePolicy === undefined ||
201
+ acknowledgedResume === undefined)
202
+ return;
203
+ resumeComplete = true;
204
+ const queuedHighWatermark = this.#queue.latestSequence();
205
+ this.#markReady(queuedHighWatermark !== undefined &&
206
+ queuedHighWatermark > acknowledgedResume
207
+ ? queuedHighWatermark
208
+ : acknowledgedResume);
209
+ sendHeartbeat();
210
+ pumpEvents();
211
+ };
212
+ const sendActionCapabilities = () => {
213
+ if (!resumeComplete || signal.aborted)
214
+ return;
215
+ const actionManifest = this.#actionExecutor?.manifest() ?? {
216
+ enabled: false,
217
+ executorVersion: "observer-agent-actions-unavailable-v1",
218
+ actions: [],
219
+ };
220
+ stream.write(create(StreamTelemetryRequestSchema, {
221
+ payload: {
222
+ case: "actionCapabilities",
223
+ value: create(ActionCapabilityManifestSchema, {
224
+ machineId: credential.machineId,
225
+ ...actionManifest,
226
+ observedAt: timestampFromDate(new Date()),
227
+ }),
228
+ },
229
+ }));
230
+ };
231
+ const sendHeartbeat = () => {
232
+ if (!resumeComplete || signal.aborted)
233
+ return;
234
+ const queueMetrics = this.#queue.metrics();
235
+ stream.write(create(StreamTelemetryRequestSchema, {
236
+ payload: {
237
+ case: "heartbeat",
238
+ value: create(HeartbeatSchema, {
239
+ machineId: credential.machineId,
240
+ observedAt: timestampFromDate(new Date()),
241
+ uptimeSeconds: BigInt(Math.floor(process.uptime())),
242
+ bufferedEvents: queueMetrics.depth,
243
+ lastSequence: this.#queue.latestSequence() ??
244
+ queueMetrics.acknowledged,
245
+ queueCapacity: queueMetrics.capacity,
246
+ droppedEvents: BigInt(queueMetrics.dropped),
247
+ coalescedEvents: BigInt(queueMetrics.coalesced),
248
+ acknowledgedSequence: queueMetrics.acknowledged,
249
+ }),
250
+ },
251
+ }));
252
+ sendActionCapabilities();
253
+ if (activeActionPolicy !== undefined &&
254
+ Date.now() - lastPolicyReceiptAt >= policyReceiptRefreshMs) {
255
+ sendPolicyReceipt(activeActionPolicy, PolicyReceiptState.APPLIED);
256
+ }
257
+ };
258
+ const sendPolicyReceipt = (policy, state) => {
259
+ stream.write(create(StreamTelemetryRequestSchema, {
260
+ payload: {
261
+ case: "policyReceipt",
262
+ value: create(PolicyReceiptSchema, {
263
+ machineId: credential.machineId,
264
+ policyDigest: policy.policyDigest,
265
+ state,
266
+ gatewayVersion: "observer-action-gateway-v2",
267
+ capabilities: state === PolicyReceiptState.APPLIED
268
+ ? [...managedGatewayCapabilities]
269
+ : [],
270
+ containmentEnabled: state === PolicyReceiptState.APPLIED,
271
+ containmentCapabilities: state === PolicyReceiptState.APPLIED
272
+ ? [...guardianContainmentCapabilities]
273
+ : [],
274
+ ...(state === PolicyReceiptState.REJECTED
275
+ ? { detail: "Local policy validation or persistence failed." }
276
+ : {}),
277
+ receivedAt: timestampFromDate(new Date()),
278
+ }),
279
+ },
280
+ }));
281
+ lastPolicyReceiptAt = Date.now();
282
+ };
283
+ const sendInventory = (inventory) => {
284
+ stream.write(create(StreamTelemetryRequestSchema, {
285
+ payload: { case: "inventory", value: inventory },
286
+ }));
287
+ };
288
+ try {
289
+ await new Promise((resolve, reject) => {
290
+ const persistAcknowledgement = (sequence, commitSourceCheckpoint) => {
291
+ const pendingSave = saveState.then(async () => {
292
+ if (commitSourceCheckpoint) {
293
+ await this.#onAcknowledgement(sequence);
294
+ }
295
+ await this.#stateStore.save(credential.machineId, sequence);
296
+ });
297
+ saveState = pendingSave;
298
+ void pendingSave.catch((error) => {
299
+ reject(error);
300
+ stream.cancel();
301
+ });
302
+ };
303
+ stream.on("data", (response) => {
304
+ if (response.payload.case === "historySyncCommand") {
305
+ const command = response.payload.value;
306
+ const commandKey = `${command.jobId}:${command.idempotencyKey}`;
307
+ if (activeHistorySyncJobs.has(commandKey))
308
+ return;
309
+ activeHistorySyncJobs.add(commandKey);
310
+ historySyncExecutions = historySyncExecutions.then(async () => {
311
+ try {
312
+ let receipt;
313
+ try {
314
+ receipt = this.#onHistorySync === undefined
315
+ ? rejectedHistorySyncReceipt(command, credential.machineId, "Full-history sync is not available in this observer.")
316
+ : await this.#onHistorySync(command);
317
+ }
318
+ catch (error) {
319
+ receipt = failedHistorySyncReceipt(command, credential.machineId, safeStatusDetail(error instanceof Error ? error.message : "History sync failed."));
320
+ }
321
+ if (receipt.state === HistorySyncResultState.SUCCEEDED &&
322
+ receipt.throughSequence > this.#lastAcknowledgedSequence) {
323
+ await this.#waitForAcknowledgement(receipt.throughSequence, connectionAbort.signal);
324
+ }
325
+ if (connectionAbort.signal.aborted || signal.aborted)
326
+ return;
327
+ stream.write(create(StreamTelemetryRequestSchema, {
328
+ payload: { case: "historySyncResult", value: receipt },
329
+ }));
330
+ }
331
+ finally {
332
+ activeHistorySyncJobs.delete(commandKey);
333
+ }
334
+ });
335
+ void historySyncExecutions.catch((error) => {
336
+ reject(error);
337
+ stream.cancel();
338
+ });
339
+ return;
340
+ }
341
+ if (response.payload.case === "actionCommand") {
342
+ const command = response.payload.value;
343
+ actionExecutions = actionExecutions.then(async () => {
344
+ const receipt = this.#actionExecutor === undefined
345
+ ? disabledActionReceipt(command, credential.machineId)
346
+ : await this.#actionExecutor.execute(command);
347
+ if (signal.aborted)
348
+ return;
349
+ stream.write(create(StreamTelemetryRequestSchema, {
350
+ payload: { case: "actionResult", value: receipt },
351
+ }));
352
+ });
353
+ void actionExecutions.catch((error) => {
354
+ reject(error);
355
+ stream.cancel();
356
+ });
357
+ return;
358
+ }
359
+ if (response.payload.case === "policy") {
360
+ const snapshot = response.payload.value;
361
+ if (snapshot.machineId !== credential.machineId ||
362
+ snapshot.tenantId !== credential.tenantId ||
363
+ snapshot.collection === undefined) {
364
+ reject(new PermanentTelemetryError("Control plane returned an invalid collection policy."));
365
+ stream.cancel();
366
+ return;
367
+ }
368
+ const collectionPolicy = snapshot.collection;
369
+ activePolicy = collectionPolicy;
370
+ try {
371
+ this.#queue.replacePending((event) => sanitizeEventForPolicy(event, collectionPolicy));
372
+ }
373
+ catch (error) {
374
+ reject(error);
375
+ stream.cancel();
376
+ return;
377
+ }
378
+ this.#onPolicy(collectionPolicy);
379
+ let generation;
380
+ try {
381
+ activeActionPolicy = undefined;
382
+ generation = this.#actionPolicyStore.invalidate(credential.machineId);
383
+ }
384
+ catch (error) {
385
+ reject(error);
386
+ stream.cancel();
387
+ return;
388
+ }
389
+ if (snapshot.actionGuardrails !== undefined) {
390
+ const actionPolicy = clone(ActionGuardrailPolicySchema, snapshot.actionGuardrails);
391
+ actionPolicyUpdates = actionPolicyUpdates.then(async () => {
392
+ try {
393
+ await this.#actionPolicyStore.save(credential.machineId, actionPolicy, generation);
394
+ }
395
+ catch (error) {
396
+ if (error instanceof ActionPolicySupersededError)
397
+ return;
398
+ if (!signal.aborted) {
399
+ sendPolicyReceipt(actionPolicy, PolicyReceiptState.REJECTED);
400
+ completeResume();
401
+ pumpEvents();
402
+ }
403
+ return;
404
+ }
405
+ if (signal.aborted)
406
+ return;
407
+ activeActionPolicy = actionPolicy;
408
+ sendPolicyReceipt(actionPolicy, PolicyReceiptState.APPLIED);
409
+ completeResume();
410
+ pumpEvents();
411
+ });
412
+ }
413
+ else {
414
+ actionPolicyUpdates = actionPolicyUpdates.then(() => {
415
+ if (signal.aborted)
416
+ return;
417
+ completeResume();
418
+ pumpEvents();
419
+ });
420
+ }
421
+ void actionPolicyUpdates.catch((error) => {
422
+ reject(error);
423
+ stream.cancel();
424
+ });
425
+ return;
426
+ }
427
+ if (response.payload.case === "acknowledgement") {
428
+ if (response.payload.value.machineId !== credential.machineId) {
429
+ reject(new PermanentTelemetryError("Control plane acknowledgement belongs to a different machine."));
430
+ stream.cancel();
431
+ return;
432
+ }
433
+ const sequence = response.payload.value.throughSequence;
434
+ if (state.recoveryPending && acknowledgedResume === undefined && sequence === this.#lastAcknowledgedSequence) {
435
+ persistAcknowledgement(sequence, false);
436
+ }
437
+ acknowledgedResume ??= sequence;
438
+ if (sequence < this.#lastAcknowledgedSequence) {
439
+ try {
440
+ this.#queue.reconcileAcknowledged(sequence);
441
+ }
442
+ catch (error) {
443
+ reject(error);
444
+ stream.cancel();
445
+ return;
446
+ }
447
+ this.#lastAcknowledgedSequence = sequence;
448
+ sentThroughSequence = sequence;
449
+ inFlightSequences.clear();
450
+ persistAcknowledgement(sequence, false);
451
+ this.#onCursorReset(sequence, this.#queue.latestSequence() ?? sequence);
452
+ completeResume();
453
+ pumpEvents();
454
+ return;
455
+ }
456
+ if (sequence > this.#lastAcknowledgedSequence) {
457
+ this.#lastAcknowledgedSequence = sequence;
458
+ this.#acknowledgements++;
459
+ try {
460
+ this.#queue.acknowledge(sequence);
461
+ }
462
+ catch (error) {
463
+ reject(error);
464
+ stream.cancel();
465
+ return;
466
+ }
467
+ for (const sent of inFlightSequences) {
468
+ if (sent <= sequence)
469
+ inFlightSequences.delete(sent);
470
+ }
471
+ persistAcknowledgement(sequence, true);
472
+ }
473
+ completeResume();
474
+ pumpEvents();
475
+ return;
476
+ }
477
+ if (response.payload.case === "error") {
478
+ const streamError = response.payload.value;
479
+ const message = `Control plane rejected telemetry: ${streamError.message}`;
480
+ if (!streamError.retryable &&
481
+ streamError.rejectedSequence > 0n &&
482
+ (streamError.code === StreamErrorCode.INVALID_EVENT ||
483
+ streamError.code === StreamErrorCode.POLICY_VIOLATION)) {
484
+ const rejectedSequence = streamError.rejectedSequence;
485
+ try {
486
+ this.#queue.discard(rejectedSequence);
487
+ }
488
+ catch (error) {
489
+ reject(error);
490
+ stream.cancel();
491
+ return;
492
+ }
493
+ inFlightSequences.delete(rejectedSequence);
494
+ persistAcknowledgement(rejectedSequence, true);
495
+ this.#onStatus(`${message} Quarantined sequence ${rejectedSequence}.`);
496
+ pumpEvents();
497
+ return;
498
+ }
499
+ reject(streamError.retryable
500
+ ? new Error(message)
501
+ : new PermanentTelemetryError(message));
502
+ stream.cancel();
503
+ }
504
+ });
505
+ stream.once("error", (error) => {
506
+ if (signal.aborted && error.code === status.CANCELLED) {
507
+ resolve();
508
+ return;
509
+ }
510
+ reject(error);
511
+ });
512
+ stream.once("end", resolve);
513
+ stream.write(create(StreamTelemetryRequestSchema, {
514
+ payload: {
515
+ case: "resume",
516
+ value: create(ResumeCursorSchema, {
517
+ machineId: credential.machineId,
518
+ acknowledgedSequence: localAcknowledgement,
519
+ }),
520
+ },
521
+ }));
522
+ sendActionCapabilities();
523
+ this.#sendInventory = sendInventory;
524
+ if (this.#latestInventory !== undefined) {
525
+ sendInventory(this.#latestInventory);
526
+ }
527
+ this.#pumpEvents = pumpEvents;
528
+ heartbeat = setInterval(sendHeartbeat, this.#config.heartbeatIntervalMs);
529
+ });
530
+ connectionAbort.abort();
531
+ await Promise.all([
532
+ saveState,
533
+ actionPolicyUpdates,
534
+ actionExecutions,
535
+ historySyncExecutions,
536
+ ]);
537
+ }
538
+ finally {
539
+ connectionAbort.abort();
540
+ if (this.#pumpEvents === pumpEvents) {
541
+ this.#pumpEvents = undefined;
542
+ }
543
+ if (this.#sendInventory === sendInventory) {
544
+ this.#sendInventory = undefined;
545
+ }
546
+ if (heartbeat !== undefined)
547
+ clearInterval(heartbeat);
548
+ signal.removeEventListener("abort", abort);
549
+ stream.end();
550
+ client.close();
551
+ }
552
+ }
553
+ async #waitForAcknowledgement(sequence, signal) {
554
+ while (this.#lastAcknowledgedSequence < sequence) {
555
+ if (signal.aborted) {
556
+ throw new Error("Telemetry disconnected before history upload was acknowledged.");
557
+ }
558
+ await abortableDelay(100, signal);
559
+ }
560
+ }
561
+ #markReady(sequence) {
562
+ this.#readySequence = sequence;
563
+ for (const ready of this.#readyWaiters)
564
+ ready(sequence);
565
+ this.#readyWaiters.clear();
566
+ }
567
+ }
568
+ function rejectedHistorySyncReceipt(command, machineID, detail) {
569
+ return create(HistorySyncResultSchema, {
570
+ jobId: command.jobId,
571
+ machineId: machineID,
572
+ idempotencyKey: command.idempotencyKey,
573
+ state: HistorySyncResultState.REJECTED,
574
+ detail,
575
+ observedAt: timestampFromDate(new Date()),
576
+ });
577
+ }
578
+ function failedHistorySyncReceipt(command, machineID, detail) {
579
+ return create(HistorySyncResultSchema, {
580
+ jobId: command.jobId,
581
+ machineId: machineID,
582
+ idempotencyKey: command.idempotencyKey,
583
+ state: HistorySyncResultState.FAILED,
584
+ detail,
585
+ observedAt: timestampFromDate(new Date()),
586
+ });
587
+ }
588
+ function disabledActionReceipt(command, machineID) {
589
+ return create(AgentActionResultSchema, {
590
+ actionId: command.actionId,
591
+ idempotencyKey: command.idempotencyKey,
592
+ machineId: machineID,
593
+ state: RemoteActionResultState.REJECTED,
594
+ resultCode: "executor_disabled",
595
+ detail: "Machine owner has not enabled remote agent actions.",
596
+ observedAt: timestampFromDate(new Date()),
597
+ targetStillRunning: true,
598
+ });
599
+ }
600
+ const managedGatewayCapabilities = Object.freeze([
601
+ ManagedAction.FILE_READ,
602
+ ManagedAction.FILE_WRITE,
603
+ ManagedAction.FILE_DELETE,
604
+ ManagedAction.COMMAND_EXECUTE,
605
+ ManagedAction.PROCESS_LAUNCH,
606
+ ManagedAction.NETWORK_CONNECT,
607
+ ManagedAction.BROWSER_NAVIGATE,
608
+ ManagedAction.CREDENTIAL_ACCESS,
609
+ ManagedAction.AGENT_START,
610
+ ManagedAction.MODEL_USE,
611
+ ]);
612
+ const guardianContainmentCapabilities = Object.freeze([
613
+ GuardrailIntervention.STOP_AGENT,
614
+ GuardrailIntervention.KILL_AGENT,
615
+ ]);
616
+ function createTraceParent() {
617
+ return `00-${randomBytes(16).toString("hex")}-${randomBytes(8).toString("hex")}-01`;
618
+ }
619
+ class PermanentTelemetryError extends Error {
620
+ }
621
+ function telemetryErrorSummary(error) {
622
+ if (isServiceError(error)) {
623
+ const codeName = Object.entries(status).find(([, value]) => typeof value === "number" && value === error.code)?.[0];
624
+ const detail = safeStatusDetail(error.details || error.message);
625
+ return `${codeName ?? `gRPC ${error.code}`}: ${detail}`;
626
+ }
627
+ if (error instanceof Error)
628
+ return safeStatusDetail(error.message);
629
+ return "unknown transport error";
630
+ }
631
+ function safeStatusDetail(value) {
632
+ const detail = value.replace(/[\r\n\t]+/gu, " ").trim();
633
+ if (!detail)
634
+ return "no error detail";
635
+ return detail.length <= 180 ? detail : `${detail.slice(0, 177)}...`;
636
+ }
637
+ export function sanitizeEventForPolicy(event, policy) {
638
+ const metadataCategory = disabledMetadataCategory(event, policy);
639
+ if (metadataCategory !== undefined) {
640
+ return policyRedactedMetadataEvent(event, metadataCategory);
641
+ }
642
+ const sanitized = clone(EventEnvelopeSchema, event);
643
+ const policyEngine = new LocalPolicyEngine(policy);
644
+ switch (sanitized.payload.case) {
645
+ case "activity": {
646
+ const activity = sanitized.payload.value;
647
+ const contentAllowed = (activity.kind === ActivityKind.PROMPT &&
648
+ policy.promptContent) ||
649
+ (activity.kind === ActivityKind.RESPONSE &&
650
+ policy.responseContent) ||
651
+ (activity.kind === ActivityKind.REASONING &&
652
+ policy.reasoningContent);
653
+ if (!contentAllowed) {
654
+ delete activity.content;
655
+ activity.contentRedacted = true;
656
+ }
657
+ for (const resource of activity.resources) {
658
+ sanitizeResource(resource, policy);
659
+ }
660
+ break;
661
+ }
662
+ case "tool": {
663
+ const tool = sanitized.payload.value;
664
+ if (!policy.commandArguments) {
665
+ delete tool.argumentsJson;
666
+ tool.argumentsRedacted = true;
667
+ }
668
+ else if (tool.argumentsJson !== undefined) {
669
+ const value = sanitizeQueuedJSON(policyEngine, tool.argumentsJson, "commandArguments");
670
+ if (value.value === undefined)
671
+ delete tool.argumentsJson;
672
+ else
673
+ tool.argumentsJson = value.value;
674
+ tool.argumentsRedacted ||= value.redacted || value.truncated;
675
+ }
676
+ if (!policy.toolOutput) {
677
+ delete tool.output;
678
+ tool.outputRedacted = true;
679
+ }
680
+ else if (tool.output !== undefined) {
681
+ const value = sanitizeQueuedJSON(policyEngine, tool.output, "toolOutput");
682
+ if (value.value === undefined)
683
+ delete tool.output;
684
+ else
685
+ tool.output = value.value;
686
+ tool.outputRedacted ||= value.redacted || value.truncated;
687
+ }
688
+ for (const resource of tool.resources) {
689
+ sanitizeResource(resource, policy);
690
+ }
691
+ break;
692
+ }
693
+ case "terminal": {
694
+ const terminal = sanitized.payload.value;
695
+ if (!policy.commandArguments)
696
+ delete terminal.arguments;
697
+ if (!policy.terminalOutput) {
698
+ delete terminal.output;
699
+ terminal.outputRedacted = true;
700
+ }
701
+ break;
702
+ }
703
+ case "filesystem": {
704
+ const filesystem = sanitized.payload.value;
705
+ if (!policy.fileContent) {
706
+ delete filesystem.content;
707
+ filesystem.contentRedacted = true;
708
+ }
709
+ if (filesystem.resource !== undefined) {
710
+ sanitizeResource(filesystem.resource, policy);
711
+ }
712
+ break;
713
+ }
714
+ case "browser": {
715
+ const browser = sanitized.payload.value;
716
+ if (!policy.browserUrls)
717
+ delete browser.url;
718
+ if (!policy.browserContent) {
719
+ delete browser.target;
720
+ browser.contentRedacted = true;
721
+ }
722
+ break;
723
+ }
724
+ case "git": {
725
+ const git = sanitized.payload.value;
726
+ if (!policy.gitPatchContent) {
727
+ delete git.patch;
728
+ git.patchRedacted = true;
729
+ }
730
+ break;
731
+ }
732
+ case "vendor": {
733
+ const vendor = sanitized.payload.value;
734
+ if (!policy.vendorPayload) {
735
+ delete vendor.payloadJson;
736
+ vendor.payloadOmitted = true;
737
+ vendor.payloadRedacted = true;
738
+ vendor.payloadTruncated = false;
739
+ vendor.redactedFields = [];
740
+ }
741
+ else if (vendor.payloadJson !== undefined) {
742
+ const value = sanitizeQueuedVendor(policyEngine, vendor.payloadJson);
743
+ if (value.value === undefined) {
744
+ delete vendor.payloadJson;
745
+ vendor.payloadOmitted = true;
746
+ }
747
+ else {
748
+ vendor.payloadJson = value.value;
749
+ }
750
+ vendor.payloadRedacted ||= value.redacted;
751
+ vendor.payloadTruncated ||= value.truncated;
752
+ vendor.redactedFields = Array.from(new Set([
753
+ ...vendor.redactedFields,
754
+ ...value.redactedFields,
755
+ ])).slice(0, 256);
756
+ }
757
+ break;
758
+ }
759
+ case "process": {
760
+ const processEvent = sanitized.payload.value;
761
+ if (!policy.processArguments) {
762
+ delete processEvent.arguments;
763
+ processEvent.argumentsRedacted = true;
764
+ }
765
+ break;
766
+ }
767
+ case "lifecycle":
768
+ case undefined:
769
+ break;
770
+ }
771
+ return sanitized;
772
+ }
773
+ function disabledMetadataCategory(event, policy) {
774
+ switch (event.payload.case) {
775
+ case "process":
776
+ return policy.processMetadata ? undefined : "process";
777
+ case "filesystem":
778
+ return policy.fileMetadata ? undefined : "filesystem";
779
+ case "browser":
780
+ return policy.browserMetadata ? undefined : "browser";
781
+ default:
782
+ return undefined;
783
+ }
784
+ }
785
+ function policyRedactedMetadataEvent(event, category) {
786
+ const sanitized = clone(EventEnvelopeSchema, event);
787
+ sanitized.privacyClass = PrivacyClass.SYSTEM_METADATA;
788
+ sanitized.payload = {
789
+ case: "vendor",
790
+ value: create(VendorEventSchema, {
791
+ source: "phigon-observer",
792
+ vendorType: `${category}-metadata-redacted`,
793
+ sourceRecordDigest: createHash("sha256")
794
+ .update(toBinary(EventEnvelopeSchema, event))
795
+ .digest("hex"),
796
+ payloadRedacted: true,
797
+ payloadOmitted: true,
798
+ }),
799
+ };
800
+ return sanitized;
801
+ }
802
+ function sanitizeQueuedJSON(policy, value, category) {
803
+ try {
804
+ return policy.json(JSON.parse(value), category);
805
+ }
806
+ catch {
807
+ return {
808
+ value: undefined,
809
+ redacted: true,
810
+ truncated: false,
811
+ };
812
+ }
813
+ }
814
+ function sanitizeQueuedVendor(policy, value) {
815
+ try {
816
+ return policy.vendorRecord(JSON.parse(value));
817
+ }
818
+ catch {
819
+ return {
820
+ value: undefined,
821
+ redacted: true,
822
+ truncated: false,
823
+ redactedFields: [],
824
+ };
825
+ }
826
+ }
827
+ function sanitizeResource(resource, policy) {
828
+ const identifierAllowed = ((resource.kind === "file" ||
829
+ resource.kind === "directory" ||
830
+ resource.kind === "repository") &&
831
+ policy.filePaths) ||
832
+ ((resource.kind === "url" || resource.kind === "origin") &&
833
+ policy.browserUrls) ||
834
+ (resource.kind === "network" && policy.networkEndpoints);
835
+ if (!identifierAllowed)
836
+ delete resource.identifier;
837
+ }
838
+ function channelCredentials(config) {
839
+ if (config.grpcUrl.protocol === "https:") {
840
+ const rootCertificates = config.tlsCaFile === undefined
841
+ ? undefined
842
+ : readFileSync(config.tlsCaFile);
843
+ const privateKey = config.tlsClientKeyFile === undefined
844
+ ? undefined
845
+ : readFileSync(config.tlsClientKeyFile);
846
+ const certificateChain = config.tlsClientCertFile === undefined
847
+ ? undefined
848
+ : readFileSync(config.tlsClientCertFile);
849
+ return credentials.createSsl(rootCertificates, privateKey, certificateChain);
850
+ }
851
+ return credentials.createInsecure();
852
+ }
853
+ function isServiceError(error) {
854
+ return error instanceof Error && "code" in error;
855
+ }
856
+ async function abortableDelay(milliseconds, signal) {
857
+ if (signal.aborted)
858
+ return;
859
+ await new Promise((resolve) => {
860
+ const timeout = setTimeout(resolve, milliseconds);
861
+ signal.addEventListener("abort", () => {
862
+ clearTimeout(timeout);
863
+ resolve();
864
+ }, { once: true });
865
+ });
866
+ }
867
+ //# sourceMappingURL=stream-client.js.map