@flowdot.ai/guardian-agent 0.1.0

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 (188) hide show
  1. package/LICENSE +40 -0
  2. package/README.md +281 -0
  3. package/ROADMAP.md +109 -0
  4. package/dist/audit/attestor.d.ts +102 -0
  5. package/dist/audit/attestor.d.ts.map +1 -0
  6. package/dist/audit/attestor.js +103 -0
  7. package/dist/audit/attestor.js.map +1 -0
  8. package/dist/audit/chain.d.ts +30 -0
  9. package/dist/audit/chain.d.ts.map +1 -0
  10. package/dist/audit/chain.js +65 -0
  11. package/dist/audit/chain.js.map +1 -0
  12. package/dist/audit/correlation.d.ts +114 -0
  13. package/dist/audit/correlation.d.ts.map +1 -0
  14. package/dist/audit/correlation.js +259 -0
  15. package/dist/audit/correlation.js.map +1 -0
  16. package/dist/audit/index.d.ts +13 -0
  17. package/dist/audit/index.d.ts.map +1 -0
  18. package/dist/audit/index.js +8 -0
  19. package/dist/audit/index.js.map +1 -0
  20. package/dist/audit/reader.d.ts +30 -0
  21. package/dist/audit/reader.d.ts.map +1 -0
  22. package/dist/audit/reader.js +85 -0
  23. package/dist/audit/reader.js.map +1 -0
  24. package/dist/audit/signature.d.ts +39 -0
  25. package/dist/audit/signature.d.ts.map +1 -0
  26. package/dist/audit/signature.js +73 -0
  27. package/dist/audit/signature.js.map +1 -0
  28. package/dist/audit/stats.d.ts +106 -0
  29. package/dist/audit/stats.d.ts.map +1 -0
  30. package/dist/audit/stats.js +196 -0
  31. package/dist/audit/stats.js.map +1 -0
  32. package/dist/audit/writer.d.ts +96 -0
  33. package/dist/audit/writer.d.ts.map +1 -0
  34. package/dist/audit/writer.js +263 -0
  35. package/dist/audit/writer.js.map +1 -0
  36. package/dist/cli/guardian-baseline.d.ts +42 -0
  37. package/dist/cli/guardian-baseline.d.ts.map +1 -0
  38. package/dist/cli/guardian-baseline.js +265 -0
  39. package/dist/cli/guardian-baseline.js.map +1 -0
  40. package/dist/cli/guardian-correlator.d.ts +47 -0
  41. package/dist/cli/guardian-correlator.d.ts.map +1 -0
  42. package/dist/cli/guardian-correlator.js +217 -0
  43. package/dist/cli/guardian-correlator.js.map +1 -0
  44. package/dist/cli/guardian-verify.d.ts +30 -0
  45. package/dist/cli/guardian-verify.d.ts.map +1 -0
  46. package/dist/cli/guardian-verify.js +149 -0
  47. package/dist/cli/guardian-verify.js.map +1 -0
  48. package/dist/errors.d.ts +28 -0
  49. package/dist/errors.d.ts.map +1 -0
  50. package/dist/errors.js +40 -0
  51. package/dist/errors.js.map +1 -0
  52. package/dist/estop/heartbeat.d.ts +94 -0
  53. package/dist/estop/heartbeat.d.ts.map +1 -0
  54. package/dist/estop/heartbeat.js +135 -0
  55. package/dist/estop/heartbeat.js.map +1 -0
  56. package/dist/estop/hub.d.ts +76 -0
  57. package/dist/estop/hub.d.ts.map +1 -0
  58. package/dist/estop/hub.js +167 -0
  59. package/dist/estop/hub.js.map +1 -0
  60. package/dist/estop/index.d.ts +12 -0
  61. package/dist/estop/index.d.ts.map +1 -0
  62. package/dist/estop/index.js +6 -0
  63. package/dist/estop/index.js.map +1 -0
  64. package/dist/estop/local.d.ts +31 -0
  65. package/dist/estop/local.d.ts.map +1 -0
  66. package/dist/estop/local.js +101 -0
  67. package/dist/estop/local.js.map +1 -0
  68. package/dist/estop/middleware.d.ts +36 -0
  69. package/dist/estop/middleware.d.ts.map +1 -0
  70. package/dist/estop/middleware.js +40 -0
  71. package/dist/estop/middleware.js.map +1 -0
  72. package/dist/estop/poller.d.ts +36 -0
  73. package/dist/estop/poller.d.ts.map +1 -0
  74. package/dist/estop/poller.js +85 -0
  75. package/dist/estop/poller.js.map +1 -0
  76. package/dist/estop/types.d.ts +31 -0
  77. package/dist/estop/types.d.ts.map +1 -0
  78. package/dist/estop/types.js +5 -0
  79. package/dist/estop/types.js.map +1 -0
  80. package/dist/gate/async-callback.d.ts +27 -0
  81. package/dist/gate/async-callback.d.ts.map +1 -0
  82. package/dist/gate/async-callback.js +79 -0
  83. package/dist/gate/async-callback.js.map +1 -0
  84. package/dist/gate/cli.d.ts +29 -0
  85. package/dist/gate/cli.d.ts.map +1 -0
  86. package/dist/gate/cli.js +83 -0
  87. package/dist/gate/cli.js.map +1 -0
  88. package/dist/gate/data-channel.d.ts +41 -0
  89. package/dist/gate/data-channel.d.ts.map +1 -0
  90. package/dist/gate/data-channel.js +132 -0
  91. package/dist/gate/data-channel.js.map +1 -0
  92. package/dist/gate/index.d.ts +13 -0
  93. package/dist/gate/index.d.ts.map +1 -0
  94. package/dist/gate/index.js +7 -0
  95. package/dist/gate/index.js.map +1 -0
  96. package/dist/gate/options.d.ts +90 -0
  97. package/dist/gate/options.d.ts.map +1 -0
  98. package/dist/gate/options.js +131 -0
  99. package/dist/gate/options.js.map +1 -0
  100. package/dist/gate/programmatic.d.ts +9 -0
  101. package/dist/gate/programmatic.d.ts.map +1 -0
  102. package/dist/gate/programmatic.js +20 -0
  103. package/dist/gate/programmatic.js.map +1 -0
  104. package/dist/gate/two-key.d.ts +90 -0
  105. package/dist/gate/two-key.d.ts.map +1 -0
  106. package/dist/gate/two-key.js +78 -0
  107. package/dist/gate/two-key.js.map +1 -0
  108. package/dist/gate/types.d.ts +25 -0
  109. package/dist/gate/types.d.ts.map +1 -0
  110. package/dist/gate/types.js +5 -0
  111. package/dist/gate/types.js.map +1 -0
  112. package/dist/index.d.ts +33 -0
  113. package/dist/index.d.ts.map +1 -0
  114. package/dist/index.js +26 -0
  115. package/dist/index.js.map +1 -0
  116. package/dist/notify/console.d.ts +13 -0
  117. package/dist/notify/console.d.ts.map +1 -0
  118. package/dist/notify/console.js +27 -0
  119. package/dist/notify/console.js.map +1 -0
  120. package/dist/notify/index.d.ts +8 -0
  121. package/dist/notify/index.d.ts.map +1 -0
  122. package/dist/notify/index.js +4 -0
  123. package/dist/notify/index.js.map +1 -0
  124. package/dist/notify/multi.d.ts +14 -0
  125. package/dist/notify/multi.d.ts.map +1 -0
  126. package/dist/notify/multi.js +22 -0
  127. package/dist/notify/multi.js.map +1 -0
  128. package/dist/notify/types.d.ts +21 -0
  129. package/dist/notify/types.d.ts.map +1 -0
  130. package/dist/notify/types.js +5 -0
  131. package/dist/notify/types.js.map +1 -0
  132. package/dist/notify/webhook.d.ts +21 -0
  133. package/dist/notify/webhook.d.ts.map +1 -0
  134. package/dist/notify/webhook.js +37 -0
  135. package/dist/notify/webhook.js.map +1 -0
  136. package/dist/policy/attribution.d.ts +61 -0
  137. package/dist/policy/attribution.d.ts.map +1 -0
  138. package/dist/policy/attribution.js +116 -0
  139. package/dist/policy/attribution.js.map +1 -0
  140. package/dist/policy/evaluator.d.ts +36 -0
  141. package/dist/policy/evaluator.d.ts.map +1 -0
  142. package/dist/policy/evaluator.js +211 -0
  143. package/dist/policy/evaluator.js.map +1 -0
  144. package/dist/policy/index.d.ts +11 -0
  145. package/dist/policy/index.d.ts.map +1 -0
  146. package/dist/policy/index.js +7 -0
  147. package/dist/policy/index.js.map +1 -0
  148. package/dist/policy/integrity.d.ts +17 -0
  149. package/dist/policy/integrity.d.ts.map +1 -0
  150. package/dist/policy/integrity.js +31 -0
  151. package/dist/policy/integrity.js.map +1 -0
  152. package/dist/policy/loader.d.ts +9 -0
  153. package/dist/policy/loader.d.ts.map +1 -0
  154. package/dist/policy/loader.js +124 -0
  155. package/dist/policy/loader.js.map +1 -0
  156. package/dist/policy/site-key.d.ts +22 -0
  157. package/dist/policy/site-key.d.ts.map +1 -0
  158. package/dist/policy/site-key.js +48 -0
  159. package/dist/policy/site-key.js.map +1 -0
  160. package/dist/policy/store.d.ts +45 -0
  161. package/dist/policy/store.d.ts.map +1 -0
  162. package/dist/policy/store.js +223 -0
  163. package/dist/policy/store.js.map +1 -0
  164. package/dist/policy/types.d.ts +72 -0
  165. package/dist/policy/types.d.ts.map +1 -0
  166. package/dist/policy/types.js +5 -0
  167. package/dist/policy/types.js.map +1 -0
  168. package/dist/runtime/capability.d.ts +125 -0
  169. package/dist/runtime/capability.d.ts.map +1 -0
  170. package/dist/runtime/capability.js +121 -0
  171. package/dist/runtime/capability.js.map +1 -0
  172. package/dist/runtime/honeytokens.d.ts +104 -0
  173. package/dist/runtime/honeytokens.d.ts.map +1 -0
  174. package/dist/runtime/honeytokens.js +115 -0
  175. package/dist/runtime/honeytokens.js.map +1 -0
  176. package/dist/runtime/multi-rate-limiter.d.ts +90 -0
  177. package/dist/runtime/multi-rate-limiter.d.ts.map +1 -0
  178. package/dist/runtime/multi-rate-limiter.js +133 -0
  179. package/dist/runtime/multi-rate-limiter.js.map +1 -0
  180. package/dist/runtime/runtime.d.ts +94 -0
  181. package/dist/runtime/runtime.d.ts.map +1 -0
  182. package/dist/runtime/runtime.js +276 -0
  183. package/dist/runtime/runtime.js.map +1 -0
  184. package/dist/types.d.ts +97 -0
  185. package/dist/types.d.ts.map +1 -0
  186. package/dist/types.js +5 -0
  187. package/dist/types.js.map +1 -0
  188. package/package.json +83 -0
@@ -0,0 +1,40 @@
1
+ /**
2
+ * createEStopMiddleware — Express/Connect/Fastify-compatible middleware that
3
+ * returns HTTP 423 Locked when the resolved user is currently pressed.
4
+ * SPEC §5.4.
5
+ *
6
+ * Framework-agnostic: the middleware signature is `(req, res, next)` which
7
+ * Express, Connect, and Fastify (with middleware mode) all accept.
8
+ */
9
+ export function createEStopMiddleware(hub, options) {
10
+ const exclude = options.exclude;
11
+ const bodyFactory = options.lockedResponseBody ?? defaultLockedBody;
12
+ return async (req, res, next) => {
13
+ if (exclude && exclude(req)) {
14
+ return next();
15
+ }
16
+ const userId = options.resolveUserId(req);
17
+ if (userId === null) {
18
+ return next();
19
+ }
20
+ const pressed = await hub.isPressed(userId);
21
+ if (!pressed) {
22
+ return next();
23
+ }
24
+ const state = await hub.status(userId);
25
+ res.statusCode = 423;
26
+ res.setHeader('content-type', 'application/json');
27
+ res.end(JSON.stringify(bodyFactory({ pressedAt: state.pressedAt }, userId)));
28
+ };
29
+ }
30
+ function defaultLockedBody(state, _userId) {
31
+ // pressedAt is always defined when middleware reaches this — the 423 path
32
+ // is only taken when hub.isPressed() returned true, which implies a
33
+ // press state row exists. Treated as required to keep coverage clean.
34
+ return {
35
+ error: 'estop_active',
36
+ message: 'An emergency stop is active for your account. Outbound actions are blocked until cleared.',
37
+ pressed_at: state.pressedAt,
38
+ };
39
+ }
40
+ //# sourceMappingURL=middleware.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"middleware.js","sourceRoot":"","sources":["../../src/estop/middleware.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AA+BH,MAAM,UAAU,qBAAqB,CACnC,GAAa,EACb,OAA+B;IAE/B,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;IAChC,MAAM,WAAW,GAAG,OAAO,CAAC,kBAAkB,IAAI,iBAAiB,CAAC;IAEpE,OAAO,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;QAC9B,IAAI,OAAO,IAAI,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;YAC5B,OAAO,IAAI,EAAE,CAAC;QAChB,CAAC;QACD,MAAM,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC;QAC1C,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;YACpB,OAAO,IAAI,EAAE,CAAC;QAChB,CAAC;QACD,MAAM,OAAO,GAAG,MAAM,GAAG,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;QAC5C,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,OAAO,IAAI,EAAE,CAAC;QAChB,CAAC;QACD,MAAM,KAAK,GAAG,MAAM,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QACvC,GAAG,CAAC,UAAU,GAAG,GAAG,CAAC;QACrB,GAAG,CAAC,SAAS,CAAC,cAAc,EAAE,kBAAkB,CAAC,CAAC;QAClD,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC,EAAE,SAAS,EAAE,KAAK,CAAC,SAAS,EAAE,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC;IAC/E,CAAC,CAAC;AACJ,CAAC;AAED,SAAS,iBAAiB,CACxB,KAA6B,EAC7B,OAAe;IAEf,0EAA0E;IAC1E,oEAAoE;IACpE,sEAAsE;IACtE,OAAO;QACL,KAAK,EAAE,cAAc;QACrB,OAAO,EACL,2FAA2F;QAC7F,UAAU,EAAE,KAAK,CAAC,SAAS;KAC5B,CAAC;AACJ,CAAC"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * createEStopPoller — pull-based safety net. SPEC §5.4.
3
+ *
4
+ * Polls a status endpoint every N seconds; fires `onPress` / `onClear` on
5
+ * transitions. Belt-and-braces alongside push notifications (FlowDot's
6
+ * comms_daemon_commands fan-out).
7
+ */
8
+ import type { EStopState } from './types.js';
9
+ export interface EStopPollerOptions {
10
+ /** GET endpoint returning an EStopState as JSON. */
11
+ statusUrl: string;
12
+ onPress: (state: EStopState) => void | Promise<void>;
13
+ onClear: (state: EStopState) => void | Promise<void>;
14
+ /** Poll interval. SPEC §5.4 default = 5000. */
15
+ intervalMs?: number;
16
+ /** Headers for auth. */
17
+ headers?: Record<string, string>;
18
+ /** Override fetch for testing. */
19
+ fetch?: typeof fetch;
20
+ /** Callback for poll errors. Default: no-op (logging is host's responsibility). */
21
+ onError?: (err: unknown) => void;
22
+ }
23
+ export declare class EStopPoller {
24
+ private readonly options;
25
+ private readonly fetchImpl;
26
+ private timer;
27
+ private lastPressed;
28
+ private running;
29
+ constructor(options: EStopPollerOptions);
30
+ start(): void;
31
+ stop(): Promise<void>;
32
+ /** Single poll iteration. Exposed for tests. */
33
+ poll(): Promise<void>;
34
+ }
35
+ export declare function createEStopPoller(options: EStopPollerOptions): EStopPoller;
36
+ //# sourceMappingURL=poller.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"poller.d.ts","sourceRoot":"","sources":["../../src/estop/poller.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C,MAAM,WAAW,kBAAkB;IACjC,oDAAoD;IACpD,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACrD,OAAO,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACrD,+CAA+C;IAC/C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,wBAAwB;IACxB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,kCAAkC;IAClC,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;IACrB,mFAAmF;IACnF,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,IAAI,CAAC;CAClC;AAID,qBAAa,WAAW;IACtB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAqB;IAC7C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAe;IACzC,OAAO,CAAC,KAAK,CAA+C;IAC5D,OAAO,CAAC,WAAW,CAAkC;IACrD,OAAO,CAAC,OAAO,CAAS;gBAEZ,OAAO,EAAE,kBAAkB;IAKvC,KAAK,IAAI,IAAI;IASP,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAS3B,gDAAgD;IAC1C,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;CA8B5B;AAED,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,kBAAkB,GAAG,WAAW,CAE1E"}
@@ -0,0 +1,85 @@
1
+ /**
2
+ * createEStopPoller — pull-based safety net. SPEC §5.4.
3
+ *
4
+ * Polls a status endpoint every N seconds; fires `onPress` / `onClear` on
5
+ * transitions. Belt-and-braces alongside push notifications (FlowDot's
6
+ * comms_daemon_commands fan-out).
7
+ */
8
+ const DEFAULT_INTERVAL_MS = 5000;
9
+ export class EStopPoller {
10
+ options;
11
+ fetchImpl;
12
+ timer = null;
13
+ lastPressed = undefined;
14
+ running = false;
15
+ constructor(options) {
16
+ this.options = options;
17
+ this.fetchImpl = options.fetch ?? fetch;
18
+ }
19
+ start() {
20
+ if (this.running)
21
+ return;
22
+ this.running = true;
23
+ const interval = this.options.intervalMs ?? DEFAULT_INTERVAL_MS;
24
+ // Kick off immediately so transitions are observed without waiting one tick.
25
+ void this.poll();
26
+ this.timer = setInterval(() => void this.poll(), interval);
27
+ }
28
+ async stop() {
29
+ if (!this.running)
30
+ return;
31
+ this.running = false;
32
+ if (this.timer) {
33
+ clearInterval(this.timer);
34
+ this.timer = null;
35
+ }
36
+ }
37
+ /** Single poll iteration. Exposed for tests. */
38
+ async poll() {
39
+ try {
40
+ const resp = await this.fetchImpl(this.options.statusUrl, {
41
+ headers: this.options.headers,
42
+ });
43
+ if (!resp.ok) {
44
+ this.options.onError?.(new Error(`status_${resp.status}`));
45
+ return;
46
+ }
47
+ const body = (await resp.json());
48
+ if (!isEStopState(body)) {
49
+ this.options.onError?.(new Error('invalid_state_shape'));
50
+ return;
51
+ }
52
+ const previous = this.lastPressed;
53
+ this.lastPressed = body.pressed;
54
+ if (previous === undefined) {
55
+ // First poll: never fire on the initial observation.
56
+ return;
57
+ }
58
+ if (!previous && body.pressed) {
59
+ await this.options.onPress(body);
60
+ }
61
+ else if (previous && !body.pressed) {
62
+ await this.options.onClear(body);
63
+ }
64
+ }
65
+ catch (err) {
66
+ this.options.onError?.(err);
67
+ }
68
+ }
69
+ }
70
+ export function createEStopPoller(options) {
71
+ return new EStopPoller(options);
72
+ }
73
+ function isEStopState(v) {
74
+ if (typeof v !== 'object' || v === null)
75
+ return false;
76
+ const obj = v;
77
+ if (typeof obj.pressed !== 'boolean')
78
+ return false;
79
+ if (obj.pressedAt !== undefined && typeof obj.pressedAt !== 'string')
80
+ return false;
81
+ if (obj.clearedAt !== undefined && typeof obj.clearedAt !== 'string')
82
+ return false;
83
+ return true;
84
+ }
85
+ //# sourceMappingURL=poller.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"poller.js","sourceRoot":"","sources":["../../src/estop/poller.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * EStop types. SPEC §5.
3
+ */
4
+ import type { AuditRecordInitiator } from '../types.js';
5
+ export interface EStopPressOptions {
6
+ reason: string;
7
+ operatorId?: string;
8
+ initiator?: AuditRecordInitiator;
9
+ /** Free-form structured details (IP, user-agent, etc.) recorded on the audit row. */
10
+ detail?: Record<string, unknown>;
11
+ }
12
+ export interface EStopClearOptions {
13
+ operatorId?: string;
14
+ initiator?: AuditRecordInitiator;
15
+ detail?: Record<string, unknown>;
16
+ }
17
+ export interface EStopState {
18
+ pressed: boolean;
19
+ pressedAt?: string;
20
+ pressedReason?: string;
21
+ pressedOperatorId?: string;
22
+ clearedAt?: string;
23
+ }
24
+ export interface EStopPressResult {
25
+ state: EStopState;
26
+ }
27
+ export interface EStopClearResult {
28
+ state: EStopState;
29
+ authRequired?: boolean;
30
+ }
31
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/estop/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAExD,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,oBAAoB,CAAC;IACjC,qFAAqF;IACrF,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClC;AAED,MAAM,WAAW,iBAAiB;IAChC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,oBAAoB,CAAC;IACjC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClC;AAED,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,UAAU,CAAC;CACnB;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,UAAU,CAAC;IAClB,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * EStop types. SPEC §5.
3
+ */
4
+ export {};
5
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/estop/types.ts"],"names":[],"mappings":"AAAA;;GAEG"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * asyncCallbackGate — POSTs the GateRequest to a callback URL, awaits a JSON
3
+ * GateResponse. SPEC §4.3.
4
+ */
5
+ import type { ApprovalGate } from './types.js';
6
+ export interface AsyncCallbackGateOptions {
7
+ /** Endpoint URL to POST GateRequest payloads at. */
8
+ url: string;
9
+ /** Default timeout in ms. May be overridden per-request via `timeout_ms`. */
10
+ timeoutMs?: number;
11
+ /** Additional headers (e.g., Authorization). */
12
+ headers?: Record<string, string>;
13
+ /** Override fetch (for testing). */
14
+ fetch?: typeof fetch;
15
+ }
16
+ /**
17
+ * Build an async-callback gate. The returned function POSTs the
18
+ * GateRequest as JSON and parses the response.
19
+ *
20
+ * Failure modes — all yield `decision: 'deny'` with reason in the response:
21
+ * - Network error or non-2xx HTTP status
22
+ * - Timeout (per-request or default)
23
+ * - JSON parse failure
24
+ * - Response shape doesn't match GateResponse
25
+ */
26
+ export declare function asyncCallbackGate(options: AsyncCallbackGateOptions): ApprovalGate;
27
+ //# sourceMappingURL=async-callback.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"async-callback.d.ts","sourceRoot":"","sources":["../../src/gate/async-callback.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,YAAY,EAA6B,MAAM,YAAY,CAAC;AAE1E,MAAM,WAAW,wBAAwB;IACvC,oDAAoD;IACpD,GAAG,EAAE,MAAM,CAAC;IACZ,6EAA6E;IAC7E,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gDAAgD;IAChD,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,oCAAoC;IACpC,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;CACtB;AAID;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,wBAAwB,GAAG,YAAY,CAcjF"}
@@ -0,0 +1,79 @@
1
+ /**
2
+ * asyncCallbackGate — POSTs the GateRequest to a callback URL, awaits a JSON
3
+ * GateResponse. SPEC §4.3.
4
+ */
5
+ const DEFAULT_TIMEOUT_MS = 600_000; // 10 minutes (matches SPEC §4.6 default)
6
+ /**
7
+ * Build an async-callback gate. The returned function POSTs the
8
+ * GateRequest as JSON and parses the response.
9
+ *
10
+ * Failure modes — all yield `decision: 'deny'` with reason in the response:
11
+ * - Network error or non-2xx HTTP status
12
+ * - Timeout (per-request or default)
13
+ * - JSON parse failure
14
+ * - Response shape doesn't match GateResponse
15
+ */
16
+ export function asyncCallbackGate(options) {
17
+ const fetchImpl = options.fetch ?? fetch;
18
+ const defaultTimeout = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
19
+ const baseHeaders = options.headers ?? {};
20
+ return async (request) => {
21
+ const timeoutMs = request.timeout_ms ?? defaultTimeout;
22
+ const controller = new AbortController();
23
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
24
+ const result = await runRequest(fetchImpl, options.url, request, controller, baseHeaders);
25
+ clearTimeout(timer);
26
+ return result;
27
+ };
28
+ }
29
+ async function runRequest(fetchImpl, url, request, controller, baseHeaders) {
30
+ try {
31
+ const resp = await fetchImpl(url, {
32
+ method: 'POST',
33
+ headers: { 'content-type': 'application/json', ...baseHeaders },
34
+ body: JSON.stringify(request),
35
+ signal: controller.signal,
36
+ });
37
+ if (!resp.ok) {
38
+ return denyWithReason(request, `callback_status_${resp.status}`);
39
+ }
40
+ const body = (await resp.json());
41
+ if (!isGateResponse(body)) {
42
+ return denyWithReason(request, 'callback_invalid_response');
43
+ }
44
+ return body;
45
+ }
46
+ catch (err) {
47
+ if (err instanceof Error && err.name === 'AbortError') {
48
+ return denyWithReason(request, 'gate_timeout');
49
+ }
50
+ const msg = err instanceof Error ? err.message : String(err);
51
+ return denyWithReason(request, `callback_error:${msg}`);
52
+ }
53
+ }
54
+ function denyWithReason(request, reason) {
55
+ return {
56
+ decision: 'deny',
57
+ reason,
58
+ granularity: request.granularity,
59
+ };
60
+ }
61
+ function isGateResponse(v) {
62
+ if (typeof v !== 'object' || v === null)
63
+ return false;
64
+ const obj = v;
65
+ const validDecisions = ['allow', 'allow_session', 'allow_forever', 'deny', 'ban_forever'];
66
+ if (typeof obj.decision !== 'string' || !validDecisions.includes(obj.decision)) {
67
+ return false;
68
+ }
69
+ const validGranularities = ['tool', 'toolkit', 'category'];
70
+ if (typeof obj.granularity !== 'string' || !validGranularities.includes(obj.granularity)) {
71
+ return false;
72
+ }
73
+ if (obj.reason !== undefined && typeof obj.reason !== 'string')
74
+ return false;
75
+ if (obj.operator_id !== undefined && typeof obj.operator_id !== 'string')
76
+ return false;
77
+ return true;
78
+ }
79
+ //# sourceMappingURL=async-callback.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"async-callback.js","sourceRoot":"","sources":["../../src/gate/async-callback.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * cliApprovalGate — synchronous-feeling stdin prompt. SPEC §4.3.
3
+ */
4
+ import type { ApprovalGate, GateResponse } from './types.js';
5
+ export interface CliGateOptions {
6
+ /** Read line source. Defaults to process.stdin. */
7
+ input?: NodeJS.ReadableStream;
8
+ /** Write target for prompt output. Defaults to process.stderr. */
9
+ output?: NodeJS.WritableStream;
10
+ /** Identifier recorded on every gate response. */
11
+ operatorId?: string;
12
+ }
13
+ /**
14
+ * Build a CLI approval gate. The returned function is reusable across many
15
+ * calls but is not concurrency-safe: only one prompt at a time.
16
+ */
17
+ export declare function cliApprovalGate(options?: CliGateOptions): ApprovalGate;
18
+ /**
19
+ * Parse the user's input. Accepts:
20
+ * 1 / once / allow → allow
21
+ * 2 / session → allow_session
22
+ * 3 / forever / always → allow_forever
23
+ * 4 / deny / no → deny
24
+ * 5 / ban / never → ban_forever
25
+ *
26
+ * Any other input falls through to `deny` (fail-closed).
27
+ */
28
+ export declare function parseCliAnswer(answer: string): GateResponse['decision'];
29
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../src/gate/cli.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,OAAO,KAAK,EAAE,YAAY,EAAe,YAAY,EAAE,MAAM,YAAY,CAAC;AAE1E,MAAM,WAAW,cAAc;IAC7B,mDAAmD;IACnD,KAAK,CAAC,EAAE,MAAM,CAAC,cAAc,CAAC;IAC9B,kEAAkE;IAClE,MAAM,CAAC,EAAE,MAAM,CAAC,cAAc,CAAC;IAC/B,kDAAkD;IAClD,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,OAAO,GAAE,cAAmB,GAAG,YAAY,CAqB1E;AAsBD;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,YAAY,CAAC,UAAU,CAAC,CAgBvE"}
@@ -0,0 +1,83 @@
1
+ /**
2
+ * cliApprovalGate — synchronous-feeling stdin prompt. SPEC §4.3.
3
+ */
4
+ import { createInterface } from 'node:readline';
5
+ /**
6
+ * Build a CLI approval gate. The returned function is reusable across many
7
+ * calls but is not concurrency-safe: only one prompt at a time.
8
+ */
9
+ export function cliApprovalGate(options = {}) {
10
+ const input = options.input ?? process.stdin;
11
+ const output = options.output ?? process.stderr;
12
+ const operatorId = options.operatorId;
13
+ return async (request) => {
14
+ const rl = createInterface({ input, output });
15
+ try {
16
+ writePrompt(output, request);
17
+ const answer = await prompt(rl, '> ');
18
+ const decision = parseCliAnswer(answer.trim());
19
+ const out = {
20
+ decision,
21
+ granularity: request.granularity,
22
+ };
23
+ if (operatorId !== undefined)
24
+ out.operator_id = operatorId;
25
+ return out;
26
+ }
27
+ finally {
28
+ rl.close();
29
+ }
30
+ };
31
+ }
32
+ function writePrompt(out, request) {
33
+ const lines = [
34
+ '',
35
+ '── guardian-agent approval required ──',
36
+ `Tool: ${request.tool_name}`,
37
+ `Agent: ${request.agent_id}`,
38
+ `Session: ${request.session_id}`,
39
+ `Args: ${JSON.stringify(request.tool_args)}`,
40
+ ];
41
+ if (request.model) {
42
+ lines.push(`Model: ${request.model.provider}/${request.model.id}`);
43
+ }
44
+ if (request.context) {
45
+ lines.push(`Context: ${request.context}`);
46
+ }
47
+ lines.push(`Choose: 1=once, 2=session, 3=forever, 4=deny, 5=ban`);
48
+ lines.push('');
49
+ out.write(lines.join('\n') + '\n');
50
+ }
51
+ /**
52
+ * Parse the user's input. Accepts:
53
+ * 1 / once / allow → allow
54
+ * 2 / session → allow_session
55
+ * 3 / forever / always → allow_forever
56
+ * 4 / deny / no → deny
57
+ * 5 / ban / never → ban_forever
58
+ *
59
+ * Any other input falls through to `deny` (fail-closed).
60
+ */
61
+ export function parseCliAnswer(answer) {
62
+ const a = answer.toLowerCase();
63
+ if (a === '1' || a === 'once' || a === 'allow' || a === 'y' || a === 'yes') {
64
+ return 'allow';
65
+ }
66
+ if (a === '2' || a === 'session') {
67
+ return 'allow_session';
68
+ }
69
+ if (a === '3' || a === 'forever' || a === 'always' || a === 'always_allow') {
70
+ return 'allow_forever';
71
+ }
72
+ if (a === '5' || a === 'ban' || a === 'never' || a === 'ban_forever') {
73
+ return 'ban_forever';
74
+ }
75
+ // Default: deny (fail-closed). Includes `4`, `deny`, `no`, and unknown input.
76
+ return 'deny';
77
+ }
78
+ function prompt(rl, question) {
79
+ return new Promise((resolve) => {
80
+ rl.question(question, (answer) => resolve(answer));
81
+ });
82
+ }
83
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../../src/gate/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,41 @@
1
+ /**
2
+ * dataChannelGate — frame encode/decode for LiveKit-style data-channel
3
+ * transports. SPEC §4.3 / §4.4.
4
+ *
5
+ * Wire shape (matches FlowDot's voice/live agent worker contract):
6
+ * { kind: 'tool_permission_request', requestId, toolName, toolArgs, ... }
7
+ * { kind: 'tool_permission_response', requestId, decision, granularity, ... }
8
+ *
9
+ * The library does not own the transport; it provides:
10
+ * - `encodeRequest(GateRequest)` → Uint8Array frame to send on the channel
11
+ * - `decodeResponse(Uint8Array)` → GateResponse (or null on parse failure)
12
+ * - `dataChannelGate(send, on)` → ApprovalGate wired to a channel handle
13
+ */
14
+ import type { ApprovalGate, GateRequest, GateResponse } from './types.js';
15
+ export type DataChannelSend = (frame: Uint8Array) => void | Promise<void>;
16
+ export type DataChannelOnResponse = (handler: (frame: Uint8Array) => void) => () => void;
17
+ export interface DataChannelGateOptions {
18
+ send: DataChannelSend;
19
+ /** Subscribe to incoming frames; returns an unsubscribe function. */
20
+ onResponse: DataChannelOnResponse;
21
+ /** Default per-call timeout in ms. SPEC §4.6 default = 600_000. */
22
+ timeoutMs?: number;
23
+ }
24
+ /** Encode a GateRequest to a wire frame (UTF-8 JSON). */
25
+ export declare function encodeRequest(request: GateRequest): Uint8Array;
26
+ /**
27
+ * Decode a wire frame. Returns null if the frame is not a valid
28
+ * tool_permission_response for any request.
29
+ */
30
+ export declare function decodeResponse(frame: Uint8Array): {
31
+ requestId: string;
32
+ response: GateResponse;
33
+ } | null;
34
+ /**
35
+ * Build a data-channel approval gate. The runtime sends a request frame and
36
+ * waits for a matching response frame (matched by `event_id` / `requestId`).
37
+ *
38
+ * On timeout: returns a `deny` response with reason 'gate_timeout' per SPEC §4.6.
39
+ */
40
+ export declare function dataChannelGate(options: DataChannelGateOptions): ApprovalGate;
41
+ //# sourceMappingURL=data-channel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"data-channel.d.ts","sourceRoot":"","sources":["../../src/gate/data-channel.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE1E,MAAM,MAAM,eAAe,GAAG,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AAC1E,MAAM,MAAM,qBAAqB,GAAG,CAClC,OAAO,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,KACjC,MAAM,IAAI,CAAC;AAEhB,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,eAAe,CAAC;IACtB,qEAAqE;IACrE,UAAU,EAAE,qBAAqB,CAAC;IAClC,mEAAmE;IACnE,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAMD,yDAAyD;AACzD,wBAAgB,aAAa,CAAC,OAAO,EAAE,WAAW,GAAG,UAAU,CAe9D;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAC5B,KAAK,EAAE,UAAU,GAChB;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,YAAY,CAAA;CAAE,GAAG,IAAI,CA8BtD;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,sBAAsB,GAAG,YAAY,CA0D7E"}
@@ -0,0 +1,132 @@
1
+ /**
2
+ * dataChannelGate — frame encode/decode for LiveKit-style data-channel
3
+ * transports. SPEC §4.3 / §4.4.
4
+ *
5
+ * Wire shape (matches FlowDot's voice/live agent worker contract):
6
+ * { kind: 'tool_permission_request', requestId, toolName, toolArgs, ... }
7
+ * { kind: 'tool_permission_response', requestId, decision, granularity, ... }
8
+ *
9
+ * The library does not own the transport; it provides:
10
+ * - `encodeRequest(GateRequest)` → Uint8Array frame to send on the channel
11
+ * - `decodeResponse(Uint8Array)` → GateResponse (or null on parse failure)
12
+ * - `dataChannelGate(send, on)` → ApprovalGate wired to a channel handle
13
+ */
14
+ const DEFAULT_TIMEOUT_MS = 600_000;
15
+ const encoder = new TextEncoder();
16
+ const decoder = new TextDecoder('utf-8');
17
+ /** Encode a GateRequest to a wire frame (UTF-8 JSON). */
18
+ export function encodeRequest(request) {
19
+ return encoder.encode(JSON.stringify({
20
+ kind: 'tool_permission_request',
21
+ requestId: request.event_id,
22
+ toolName: request.tool_name,
23
+ toolArgs: request.tool_args,
24
+ agentId: request.agent_id,
25
+ sessionId: request.session_id,
26
+ granularity: request.granularity,
27
+ ...(request.model === undefined ? {} : { model: request.model }),
28
+ ...(request.context === undefined ? {} : { context: request.context }),
29
+ ...(request.timeout_ms === undefined ? {} : { timeoutMs: request.timeout_ms }),
30
+ }));
31
+ }
32
+ /**
33
+ * Decode a wire frame. Returns null if the frame is not a valid
34
+ * tool_permission_response for any request.
35
+ */
36
+ export function decodeResponse(frame) {
37
+ let parsed;
38
+ try {
39
+ parsed = JSON.parse(decoder.decode(frame));
40
+ }
41
+ catch {
42
+ return null;
43
+ }
44
+ if (typeof parsed !== 'object' || parsed === null)
45
+ return null;
46
+ const obj = parsed;
47
+ if (obj.kind !== 'tool_permission_response')
48
+ return null;
49
+ if (typeof obj.requestId !== 'string')
50
+ return null;
51
+ const validDecisions = ['allow', 'allow_session', 'allow_forever', 'deny', 'ban_forever'];
52
+ if (typeof obj.decision !== 'string' || !validDecisions.includes(obj.decision)) {
53
+ return null;
54
+ }
55
+ const validGranularities = ['tool', 'toolkit', 'category'];
56
+ if (typeof obj.granularity !== 'string' || !validGranularities.includes(obj.granularity)) {
57
+ return null;
58
+ }
59
+ if (obj.reason !== undefined && typeof obj.reason !== 'string')
60
+ return null;
61
+ if (obj.operatorId !== undefined && typeof obj.operatorId !== 'string')
62
+ return null;
63
+ const response = {
64
+ decision: obj.decision,
65
+ granularity: obj.granularity,
66
+ };
67
+ if (typeof obj.reason === 'string')
68
+ response.reason = obj.reason;
69
+ if (typeof obj.operatorId === 'string')
70
+ response.operator_id = obj.operatorId;
71
+ return { requestId: obj.requestId, response };
72
+ }
73
+ /**
74
+ * Build a data-channel approval gate. The runtime sends a request frame and
75
+ * waits for a matching response frame (matched by `event_id` / `requestId`).
76
+ *
77
+ * On timeout: returns a `deny` response with reason 'gate_timeout' per SPEC §4.6.
78
+ */
79
+ export function dataChannelGate(options) {
80
+ const defaultTimeout = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
81
+ const pending = new Map();
82
+ const unsubscribe = options.onResponse((frame) => {
83
+ const decoded = decodeResponse(frame);
84
+ if (!decoded)
85
+ return;
86
+ const resolver = pending.get(decoded.requestId);
87
+ if (resolver) {
88
+ pending.delete(decoded.requestId);
89
+ resolver(decoded.response);
90
+ }
91
+ });
92
+ const gate = async (request) => {
93
+ const timeoutMs = request.timeout_ms ?? defaultTimeout;
94
+ return new Promise((resolve) => {
95
+ const timer = setTimeout(() => {
96
+ pending.delete(request.event_id);
97
+ resolve({
98
+ decision: 'deny',
99
+ reason: 'gate_timeout',
100
+ granularity: request.granularity,
101
+ });
102
+ }, timeoutMs);
103
+ pending.set(request.event_id, (response) => {
104
+ clearTimeout(timer);
105
+ resolve(response);
106
+ });
107
+ // Fire the request frame. Synchronous AND asynchronous failures both
108
+ // surface as deny with reason; wrap via Promise.resolve().then(...)
109
+ // (catches sync throws via the inner function).
110
+ void Promise.resolve()
111
+ .then(() => options.send(encodeRequest(request)))
112
+ .catch((err) => {
113
+ clearTimeout(timer);
114
+ pending.delete(request.event_id);
115
+ const msg = err instanceof Error ? err.message : String(err);
116
+ resolve({
117
+ decision: 'deny',
118
+ reason: `data_channel_send_error:${msg}`,
119
+ granularity: request.granularity,
120
+ });
121
+ });
122
+ });
123
+ };
124
+ // Expose a dispose method on the gate for callers that want to detach the
125
+ // underlying subscription. Attach as a non-enumerable property.
126
+ Object.defineProperty(gate, 'dispose', {
127
+ value: unsubscribe,
128
+ enumerable: false,
129
+ });
130
+ return gate;
131
+ }
132
+ //# sourceMappingURL=data-channel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"data-channel.js","sourceRoot":"","sources":["../../src/gate/data-channel.ts"],"names":[],"mappings":"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"}