@superblocksteam/gateway 2.0.155-next.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 (183) hide show
  1. package/LICENSE.txt +87 -0
  2. package/README.md +170 -0
  3. package/dist/agents/resolve-orchestrator-url.d.ts +22 -0
  4. package/dist/agents/resolve-orchestrator-url.js +38 -0
  5. package/dist/agents/resolve-orchestrator-url.js.map +1 -0
  6. package/dist/capabilities/import-prompt.d.ts +11 -0
  7. package/dist/capabilities/import-prompt.js +96 -0
  8. package/dist/capabilities/import-prompt.js.map +1 -0
  9. package/dist/capabilities/lifecycle.d.ts +73 -0
  10. package/dist/capabilities/lifecycle.js +2531 -0
  11. package/dist/capabilities/lifecycle.js.map +1 -0
  12. package/dist/capabilities/persisted-progress.d.ts +46 -0
  13. package/dist/capabilities/persisted-progress.js +246 -0
  14. package/dist/capabilities/persisted-progress.js.map +1 -0
  15. package/dist/capabilities/types.d.ts +446 -0
  16. package/dist/capabilities/types.js +47 -0
  17. package/dist/capabilities/types.js.map +1 -0
  18. package/dist/capture/browser-contract.d.ts +36 -0
  19. package/dist/capture/browser-contract.js +24 -0
  20. package/dist/capture/browser-contract.js.map +1 -0
  21. package/dist/capture/browser-instructions.d.ts +9 -0
  22. package/dist/capture/browser-instructions.js +17 -0
  23. package/dist/capture/browser-instructions.js.map +1 -0
  24. package/dist/capture/capture-library-probe.d.ts +55 -0
  25. package/dist/capture/capture-library-probe.js +177 -0
  26. package/dist/capture/capture-library-probe.js.map +1 -0
  27. package/dist/capture/capture-library.d.ts +52 -0
  28. package/dist/capture/capture-library.js +249 -0
  29. package/dist/capture/capture-library.js.map +1 -0
  30. package/dist/capture/classify-library-paint.d.ts +21 -0
  31. package/dist/capture/classify-library-paint.js +13 -0
  32. package/dist/capture/classify-library-paint.js.map +1 -0
  33. package/dist/capture/host-mcp-scan.d.ts +16 -0
  34. package/dist/capture/host-mcp-scan.js +64 -0
  35. package/dist/capture/host-mcp-scan.js.map +1 -0
  36. package/dist/capture/library-parent-script.d.ts +34 -0
  37. package/dist/capture/library-parent-script.js +82 -0
  38. package/dist/capture/library-parent-script.js.map +1 -0
  39. package/dist/capture/mode.d.ts +22 -0
  40. package/dist/capture/mode.js +45 -0
  41. package/dist/capture/mode.js.map +1 -0
  42. package/dist/config.d.ts +141 -0
  43. package/dist/config.js +109 -0
  44. package/dist/config.js.map +1 -0
  45. package/dist/debug.d.ts +2 -0
  46. package/dist/debug.js +9 -0
  47. package/dist/debug.js.map +1 -0
  48. package/dist/deps.d.ts +36 -0
  49. package/dist/deps.js +2 -0
  50. package/dist/deps.js.map +1 -0
  51. package/dist/events/cursor.d.ts +43 -0
  52. package/dist/events/cursor.js +78 -0
  53. package/dist/events/cursor.js.map +1 -0
  54. package/dist/events/memory-event-store.d.ts +34 -0
  55. package/dist/events/memory-event-store.js +110 -0
  56. package/dist/events/memory-event-store.js.map +1 -0
  57. package/dist/events/merge.d.ts +23 -0
  58. package/dist/events/merge.js +97 -0
  59. package/dist/events/merge.js.map +1 -0
  60. package/dist/events/normalized-collector.d.ts +62 -0
  61. package/dist/events/normalized-collector.js +156 -0
  62. package/dist/events/normalized-collector.js.map +1 -0
  63. package/dist/events/schema.d.ts +9 -0
  64. package/dist/events/schema.js +93 -0
  65. package/dist/events/schema.js.map +1 -0
  66. package/dist/events/snapshot.d.ts +32 -0
  67. package/dist/events/snapshot.js +57 -0
  68. package/dist/events/snapshot.js.map +1 -0
  69. package/dist/events/stream-key.d.ts +2 -0
  70. package/dist/events/stream-key.js +31 -0
  71. package/dist/events/stream-key.js.map +1 -0
  72. package/dist/events/types.d.ts +179 -0
  73. package/dist/events/types.js +66 -0
  74. package/dist/events/types.js.map +1 -0
  75. package/dist/health/readiness.d.ts +99 -0
  76. package/dist/health/readiness.js +202 -0
  77. package/dist/health/readiness.js.map +1 -0
  78. package/dist/index.d.ts +10 -0
  79. package/dist/index.js +10 -0
  80. package/dist/index.js.map +1 -0
  81. package/dist/integrations/filters.d.ts +8 -0
  82. package/dist/integrations/filters.js +40 -0
  83. package/dist/integrations/filters.js.map +1 -0
  84. package/dist/integrations/map.d.ts +26 -0
  85. package/dist/integrations/map.js +43 -0
  86. package/dist/integrations/map.js.map +1 -0
  87. package/dist/integrations/profile-key.d.ts +19 -0
  88. package/dist/integrations/profile-key.js +35 -0
  89. package/dist/integrations/profile-key.js.map +1 -0
  90. package/dist/main.d.ts +2 -0
  91. package/dist/main.js +5 -0
  92. package/dist/main.js.map +1 -0
  93. package/dist/orchestrator/client.d.ts +62 -0
  94. package/dist/orchestrator/client.js +160 -0
  95. package/dist/orchestrator/client.js.map +1 -0
  96. package/dist/playwright/ensure-chromium.d.ts +26 -0
  97. package/dist/playwright/ensure-chromium.js +60 -0
  98. package/dist/playwright/ensure-chromium.js.map +1 -0
  99. package/dist/preview/capture-screenshot.d.ts +52 -0
  100. package/dist/preview/capture-screenshot.js +158 -0
  101. package/dist/preview/capture-screenshot.js.map +1 -0
  102. package/dist/preview/viewer-url.d.ts +82 -0
  103. package/dist/preview/viewer-url.js +128 -0
  104. package/dist/preview/viewer-url.js.map +1 -0
  105. package/dist/process/fault-barrier.d.ts +55 -0
  106. package/dist/process/fault-barrier.js +113 -0
  107. package/dist/process/fault-barrier.js.map +1 -0
  108. package/dist/resume/memory-progress-store.d.ts +39 -0
  109. package/dist/resume/memory-progress-store.js +82 -0
  110. package/dist/resume/memory-progress-store.js.map +1 -0
  111. package/dist/resume/memory-recent-app-store.d.ts +14 -0
  112. package/dist/resume/memory-recent-app-store.js +27 -0
  113. package/dist/resume/memory-recent-app-store.js.map +1 -0
  114. package/dist/resume/memory-turn-store.d.ts +18 -0
  115. package/dist/resume/memory-turn-store.js +73 -0
  116. package/dist/resume/memory-turn-store.js.map +1 -0
  117. package/dist/resume/progress-key.d.ts +21 -0
  118. package/dist/resume/progress-key.js +58 -0
  119. package/dist/resume/progress-key.js.map +1 -0
  120. package/dist/resume/stores.d.ts +14 -0
  121. package/dist/resume/stores.js +18 -0
  122. package/dist/resume/stores.js.map +1 -0
  123. package/dist/resume/types.d.ts +124 -0
  124. package/dist/resume/types.js +13 -0
  125. package/dist/resume/types.js.map +1 -0
  126. package/dist/sabs/agent-facing-text.d.ts +33 -0
  127. package/dist/sabs/agent-facing-text.js +125 -0
  128. package/dist/sabs/agent-facing-text.js.map +1 -0
  129. package/dist/sabs/browser-context.d.ts +53 -0
  130. package/dist/sabs/browser-context.js +91 -0
  131. package/dist/sabs/browser-context.js.map +1 -0
  132. package/dist/sabs/editor-client-methods.d.ts +147 -0
  133. package/dist/sabs/editor-client-methods.js +715 -0
  134. package/dist/sabs/editor-client-methods.js.map +1 -0
  135. package/dist/sabs/recent-tool-runs.d.ts +14 -0
  136. package/dist/sabs/recent-tool-runs.js +61 -0
  137. package/dist/sabs/recent-tool-runs.js.map +1 -0
  138. package/dist/sabs/session-peer.d.ts +351 -0
  139. package/dist/sabs/session-peer.js +2 -0
  140. package/dist/sabs/session-peer.js.map +1 -0
  141. package/dist/sabs/turn-collector.d.ts +152 -0
  142. package/dist/sabs/turn-collector.js +573 -0
  143. package/dist/sabs/turn-collector.js.map +1 -0
  144. package/dist/sabs/websocket-session-peer.d.ts +514 -0
  145. package/dist/sabs/websocket-session-peer.js +2487 -0
  146. package/dist/sabs/websocket-session-peer.js.map +1 -0
  147. package/dist/security/constant-time.d.ts +9 -0
  148. package/dist/security/constant-time.js +13 -0
  149. package/dist/security/constant-time.js.map +1 -0
  150. package/dist/server/client.d.ts +371 -0
  151. package/dist/server/client.js +572 -0
  152. package/dist/server/client.js.map +1 -0
  153. package/dist/server/identity-reads.d.ts +76 -0
  154. package/dist/server/identity-reads.js +209 -0
  155. package/dist/server/identity-reads.js.map +1 -0
  156. package/dist/start.d.ts +14 -0
  157. package/dist/start.js +75 -0
  158. package/dist/start.js.map +1 -0
  159. package/dist/telemetry/metrics.d.ts +32 -0
  160. package/dist/telemetry/metrics.js +57 -0
  161. package/dist/telemetry/metrics.js.map +1 -0
  162. package/dist/transports/mcp/admin-tools.d.ts +47 -0
  163. package/dist/transports/mcp/admin-tools.js +164 -0
  164. package/dist/transports/mcp/admin-tools.js.map +1 -0
  165. package/dist/transports/mcp/app-status-html.d.ts +15 -0
  166. package/dist/transports/mcp/app-status-html.js +563 -0
  167. package/dist/transports/mcp/app-status-html.js.map +1 -0
  168. package/dist/transports/mcp/decision-elicitation.d.ts +74 -0
  169. package/dist/transports/mcp/decision-elicitation.js +233 -0
  170. package/dist/transports/mcp/decision-elicitation.js.map +1 -0
  171. package/dist/transports/mcp/dispatch-probe.d.ts +24 -0
  172. package/dist/transports/mcp/dispatch-probe.js +48 -0
  173. package/dist/transports/mcp/dispatch-probe.js.map +1 -0
  174. package/dist/transports/mcp/format-tool-content.d.ts +25 -0
  175. package/dist/transports/mcp/format-tool-content.js +48 -0
  176. package/dist/transports/mcp/format-tool-content.js.map +1 -0
  177. package/dist/transports/mcp/mount.d.ts +9 -0
  178. package/dist/transports/mcp/mount.js +469 -0
  179. package/dist/transports/mcp/mount.js.map +1 -0
  180. package/dist/transports/mcp/progress-notifier.d.ts +39 -0
  181. package/dist/transports/mcp/progress-notifier.js +52 -0
  182. package/dist/transports/mcp/progress-notifier.js.map +1 -0
  183. package/package.json +72 -0
@@ -0,0 +1,82 @@
1
+ import { cursorKey, lastActivityKey, liveEditKey } from "./progress-key.js";
2
+ import { DEFAULT_LIVE_EDIT_POINTER_TTL_MS, DEFAULT_PROGRESS_CURSOR_TTL_MS, } from "./types.js";
3
+ /**
4
+ * Cap on how many cursors and pointers a single process will hold.
5
+ *
6
+ * Redis expires keys on its own; a plain Map does not, and a gateway that ran
7
+ * for a week would hold one entry per (caller, application) it had ever
8
+ * served. Insertion order is iteration order for a Map, so the oldest write
9
+ * is the one dropped — a caller evicted this way re-reads the retained
10
+ * history rather than losing anything, which is the same outcome as its
11
+ * cursor having expired.
12
+ */
13
+ export const MAX_MEMORY_PROGRESS_ENTRIES = 2_048;
14
+ /**
15
+ * Local-only fallback, matching the other in-memory stores.
16
+ *
17
+ * Not a second source of truth: two replicas with private maps would hand the
18
+ * same caller two different positions in the same stream, which is exactly
19
+ * the divergence Redis is required outside local to prevent.
20
+ */
21
+ export class MemoryProgressStore {
22
+ cursors = new Map();
23
+ lastActivity = new Map();
24
+ liveEdits = new Map();
25
+ async getCursor(key) {
26
+ return read(this.cursors, cursorKey(key));
27
+ }
28
+ async getLastActivityAtMs(key) {
29
+ return read(this.lastActivity, lastActivityKey(key));
30
+ }
31
+ async getLiveEditId(key) {
32
+ return read(this.liveEdits, liveEditKey(key));
33
+ }
34
+ async setCursor(input) {
35
+ write(this.cursors, cursorKey(input), input.record, {
36
+ ttlMs: input.ttlMs ?? DEFAULT_PROGRESS_CURSOR_TTL_MS,
37
+ });
38
+ }
39
+ async setLastActivityAtMs(input) {
40
+ // Same retention as a cursor: past that, there is no history left to
41
+ // measure a quiet spell against anyway.
42
+ write(this.lastActivity, lastActivityKey(input), input.atMs, {
43
+ ttlMs: input.ttlMs ?? DEFAULT_PROGRESS_CURSOR_TTL_MS,
44
+ });
45
+ }
46
+ async setLiveEditId(input) {
47
+ write(this.liveEdits, liveEditKey(input), input.liveEditId, {
48
+ ttlMs: input.ttlMs ?? DEFAULT_LIVE_EDIT_POINTER_TTL_MS,
49
+ });
50
+ }
51
+ }
52
+ function read(entries, key) {
53
+ const entry = entries.get(key);
54
+ if (!entry) {
55
+ return undefined;
56
+ }
57
+ if (entry.expiresAtMs <= Date.now()) {
58
+ entries.delete(key);
59
+ return undefined;
60
+ }
61
+ return entry.value;
62
+ }
63
+ function write(entries, key, value, options) {
64
+ const now = Date.now();
65
+ for (const [candidate, entry] of entries) {
66
+ if (entry.expiresAtMs <= now) {
67
+ entries.delete(candidate);
68
+ }
69
+ }
70
+ // Re-inserting moves the key to the end, so a cursor being written is never
71
+ // the next one evicted.
72
+ entries.delete(key);
73
+ entries.set(key, { expiresAtMs: now + options.ttlMs, value });
74
+ while (entries.size > MAX_MEMORY_PROGRESS_ENTRIES) {
75
+ const oldest = entries.keys().next();
76
+ if (oldest.done) {
77
+ return;
78
+ }
79
+ entries.delete(oldest.value);
80
+ }
81
+ }
82
+ //# sourceMappingURL=memory-progress-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memory-progress-store.js","sourceRoot":"","sources":["../../src/resume/memory-progress-store.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import { type RecentApp, type RecentAppStore } from "./types.js";
2
+ export declare class MemoryRecentAppStore implements RecentAppStore {
3
+ private readonly byPrincipal;
4
+ get(input: {
5
+ organizationId: string;
6
+ userId: string;
7
+ }): Promise<RecentApp | undefined>;
8
+ set(input: {
9
+ applicationId: string;
10
+ organizationId: string;
11
+ ttlMs?: number;
12
+ userId: string;
13
+ }): Promise<void>;
14
+ }
@@ -0,0 +1,27 @@
1
+ import { DEFAULT_RECENT_APP_TTL_MS, } from "./types.js";
2
+ function principalKey(organizationId, userId) {
3
+ return `${organizationId}:${userId}`;
4
+ }
5
+ export class MemoryRecentAppStore {
6
+ byPrincipal = new Map();
7
+ async get(input) {
8
+ const key = principalKey(input.organizationId, input.userId);
9
+ const entry = this.byPrincipal.get(key);
10
+ if (!entry) {
11
+ return undefined;
12
+ }
13
+ if (entry.expiresAtMs <= Date.now()) {
14
+ this.byPrincipal.delete(key);
15
+ return undefined;
16
+ }
17
+ return { applicationId: entry.applicationId };
18
+ }
19
+ async set(input) {
20
+ const ttlMs = input.ttlMs ?? DEFAULT_RECENT_APP_TTL_MS;
21
+ this.byPrincipal.set(principalKey(input.organizationId, input.userId), {
22
+ applicationId: input.applicationId,
23
+ expiresAtMs: Date.now() + ttlMs,
24
+ });
25
+ }
26
+ }
27
+ //# sourceMappingURL=memory-recent-app-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memory-recent-app-store.js","sourceRoot":"","sources":["../../src/resume/memory-recent-app-store.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,yBAAyB,GAC1B,MAAM,YAAY,CAAC;AAOpB,SAAS,YAAY,CAAC,cAAsB,EAAE,MAAc;IAC1D,OAAO,GAAG,cAAc,IAAI,MAAM,EAAE,CAAC;AACvC,CAAC;AAED,MAAM,OAAO,oBAAoB;IACd,WAAW,GAAG,IAAI,GAAG,EAAgC,CAAC;IAEvE,KAAK,CAAC,GAAG,CAAC,KAGT;QACC,MAAM,GAAG,GAAG,YAAY,CAAC,KAAK,CAAC,cAAc,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;QAC7D,MAAM,KAAK,GAAG,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACxC,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,OAAO,SAAS,CAAC;QACnB,CAAC;QACD,IAAI,KAAK,CAAC,WAAW,IAAI,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC;YACpC,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YAC7B,OAAO,SAAS,CAAC;QACnB,CAAC;QACD,OAAO,EAAE,aAAa,EAAE,KAAK,CAAC,aAAa,EAAE,CAAC;IAChD,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,KAKT;QACC,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,IAAI,yBAAyB,CAAC;QACvD,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,YAAY,CAAC,KAAK,CAAC,cAAc,EAAE,KAAK,CAAC,MAAM,CAAC,EAAE;YACrE,aAAa,EAAE,KAAK,CAAC,aAAa;YAClC,WAAW,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK;SAChC,CAAC,CAAC;IACL,CAAC;CACF"}
@@ -0,0 +1,18 @@
1
+ import { type SuspendedTurn, type TurnStore } from "./types.js";
2
+ export declare class MemoryTurnStore implements TurnStore {
3
+ private readonly byId;
4
+ private readonly byIdempotency;
5
+ create(input: {
6
+ authUrl: string;
7
+ callbackToken?: string;
8
+ idempotencyKey: string;
9
+ reason: SuspendedTurn["reason"];
10
+ ttlMs?: number;
11
+ turnId?: string;
12
+ }): Promise<SuspendedTurn>;
13
+ get(turnId: string): Promise<SuspendedTurn | undefined>;
14
+ getByIdempotencyKey(idempotencyKey: string): Promise<SuspendedTurn | undefined>;
15
+ complete(turnId: string, callbackToken: string): Promise<SuspendedTurn | undefined>;
16
+ }
17
+ /** Stable Redis-safe segment for caller-controlled idempotency keys. */
18
+ export declare function hashIdempotencyKey(idempotencyKey: string): string;
@@ -0,0 +1,73 @@
1
+ import { createHash, randomUUID } from "node:crypto";
2
+ import { constantTimeEquals } from "../security/constant-time.js";
3
+ import { DEFAULT_TURN_TTL_MS, } from "./types.js";
4
+ export class MemoryTurnStore {
5
+ byId = new Map();
6
+ byIdempotency = new Map();
7
+ async create(input) {
8
+ const existingId = this.byIdempotency.get(input.idempotencyKey);
9
+ if (existingId) {
10
+ const existing = await this.get(existingId);
11
+ if (existing && existing.status !== "expired") {
12
+ return existing;
13
+ }
14
+ }
15
+ const ttlMs = input.ttlMs ?? DEFAULT_TURN_TTL_MS;
16
+ const now = Date.now();
17
+ const turn = {
18
+ authUrl: input.authUrl,
19
+ callbackToken: input.callbackToken ?? randomUUID(),
20
+ createdAtMs: now,
21
+ idempotencyKey: input.idempotencyKey,
22
+ reason: input.reason,
23
+ status: "pending",
24
+ turnId: input.turnId ?? randomUUID(),
25
+ };
26
+ this.byId.set(turn.turnId, { expiresAtMs: now + ttlMs, turn });
27
+ this.byIdempotency.set(input.idempotencyKey, turn.turnId);
28
+ return turn;
29
+ }
30
+ async get(turnId) {
31
+ const entry = this.byId.get(turnId);
32
+ if (!entry) {
33
+ return undefined;
34
+ }
35
+ if (Date.now() >= entry.expiresAtMs) {
36
+ this.byId.delete(turnId);
37
+ this.byIdempotency.delete(entry.turn.idempotencyKey);
38
+ return { ...entry.turn, status: "expired" };
39
+ }
40
+ return entry.turn;
41
+ }
42
+ async getByIdempotencyKey(idempotencyKey) {
43
+ const turnId = this.byIdempotency.get(idempotencyKey);
44
+ if (!turnId) {
45
+ return undefined;
46
+ }
47
+ return this.get(turnId);
48
+ }
49
+ async complete(turnId, callbackToken) {
50
+ const entry = this.byId.get(turnId);
51
+ if (!entry) {
52
+ return undefined;
53
+ }
54
+ if (Date.now() >= entry.expiresAtMs) {
55
+ this.byId.delete(turnId);
56
+ this.byIdempotency.delete(entry.turn.idempotencyKey);
57
+ return { ...entry.turn, status: "expired" };
58
+ }
59
+ if (!constantTimeEquals(entry.turn.callbackToken, callbackToken)) {
60
+ return undefined;
61
+ }
62
+ if (entry.turn.status === "expired") {
63
+ return entry.turn;
64
+ }
65
+ entry.turn = { ...entry.turn, status: "ready" };
66
+ return entry.turn;
67
+ }
68
+ }
69
+ /** Stable Redis-safe segment for caller-controlled idempotency keys. */
70
+ export function hashIdempotencyKey(idempotencyKey) {
71
+ return createHash("sha256").update(idempotencyKey).digest("hex");
72
+ }
73
+ //# sourceMappingURL=memory-turn-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memory-turn-store.js","sourceRoot":"","sources":["../../src/resume/memory-turn-store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAErD,OAAO,EAAE,kBAAkB,EAAE,MAAM,8BAA8B,CAAC;AAClE,OAAO,EAGL,mBAAmB,GACpB,MAAM,YAAY,CAAC;AAOpB,MAAM,OAAO,eAAe;IACT,IAAI,GAAG,IAAI,GAAG,EAAuB,CAAC;IACtC,aAAa,GAAG,IAAI,GAAG,EAAkB,CAAC;IAE3D,KAAK,CAAC,MAAM,CAAC,KAOZ;QACC,MAAM,UAAU,GAAG,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC;QAChE,IAAI,UAAU,EAAE,CAAC;YACf,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;YAC5C,IAAI,QAAQ,IAAI,QAAQ,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;gBAC9C,OAAO,QAAQ,CAAC;YAClB,CAAC;QACH,CAAC;QAED,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,IAAI,mBAAmB,CAAC;QACjD,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,MAAM,IAAI,GAAkB;YAC1B,OAAO,EAAE,KAAK,CAAC,OAAO;YACtB,aAAa,EAAE,KAAK,CAAC,aAAa,IAAI,UAAU,EAAE;YAClD,WAAW,EAAE,GAAG;YAChB,cAAc,EAAE,KAAK,CAAC,cAAc;YACpC,MAAM,EAAE,KAAK,CAAC,MAAM;YACpB,MAAM,EAAE,SAAS;YACjB,MAAM,EAAE,KAAK,CAAC,MAAM,IAAI,UAAU,EAAE;SACrC,CAAC;QACF,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,WAAW,EAAE,GAAG,GAAG,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QAC/D,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,KAAK,CAAC,cAAc,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;QAC1D,OAAO,IAAI,CAAC;IACd,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,MAAc;QACtB,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QACpC,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,OAAO,SAAS,CAAC;QACnB,CAAC;QACD,IAAI,IAAI,CAAC,GAAG,EAAE,IAAI,KAAK,CAAC,WAAW,EAAE,CAAC;YACpC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;YACzB,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;YACrD,OAAO,EAAE,GAAG,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;QAC9C,CAAC;QACD,OAAO,KAAK,CAAC,IAAI,CAAC;IACpB,CAAC;IAED,KAAK,CAAC,mBAAmB,CACvB,cAAsB;QAEtB,MAAM,MAAM,GAAG,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;QACtD,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,OAAO,SAAS,CAAC;QACnB,CAAC;QACD,OAAO,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IAC1B,CAAC;IAED,KAAK,CAAC,QAAQ,CACZ,MAAc,EACd,aAAqB;QAErB,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QACpC,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,OAAO,SAAS,CAAC;QACnB,CAAC;QACD,IAAI,IAAI,CAAC,GAAG,EAAE,IAAI,KAAK,CAAC,WAAW,EAAE,CAAC;YACpC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;YACzB,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;YACrD,OAAO,EAAE,GAAG,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;QAC9C,CAAC;QACD,IAAI,CAAC,kBAAkB,CAAC,KAAK,CAAC,IAAI,CAAC,aAAa,EAAE,aAAa,CAAC,EAAE,CAAC;YACjE,OAAO,SAAS,CAAC;QACnB,CAAC;QACD,IAAI,KAAK,CAAC,IAAI,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;YACpC,OAAO,KAAK,CAAC,IAAI,CAAC;QACpB,CAAC;QACD,KAAK,CAAC,IAAI,GAAG,EAAE,GAAG,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC;QAChD,OAAO,KAAK,CAAC,IAAI,CAAC;IACpB,CAAC;CACF;AAED,wEAAwE;AACxE,MAAM,UAAU,kBAAkB,CAAC,cAAsB;IACvD,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AACnE,CAAC"}
@@ -0,0 +1,21 @@
1
+ import type { AppScopeKey, ProgressCursorKey } from "./types.js";
2
+ export declare const CURSOR_KEY_PREFIX = "gateway:progress:cursor:";
3
+ export declare const LAST_ACTIVITY_KEY_PREFIX = "gateway:progress:last-activity:";
4
+ export declare const LIVE_EDIT_KEY_PREFIX = "gateway:progress:live-edit:";
5
+ /**
6
+ * One caller's cursor for one application.
7
+ *
8
+ * The channel stays in the clear: it is one of three constant strings, it
9
+ * cannot identify anybody, and being able to tell an MCP cursor from a Slack
10
+ * one by looking at a key is worth more than the nothing it discloses.
11
+ */
12
+ export declare function cursorKey(key: ProgressCursorKey): string;
13
+ /**
14
+ * When an application's build last said anything.
15
+ *
16
+ * App-scoped rather than per-caller: whether a build has gone quiet is a fact
17
+ * about the build, and keying it by reader would let two callers disagree
18
+ * about the same turn at the same instant.
19
+ */
20
+ export declare function lastActivityKey(key: AppScopeKey): string;
21
+ export declare function liveEditKey(key: AppScopeKey): string;
@@ -0,0 +1,58 @@
1
+ import { createHash } from "node:crypto";
2
+ export const CURSOR_KEY_PREFIX = "gateway:progress:cursor:";
3
+ export const LAST_ACTIVITY_KEY_PREFIX = "gateway:progress:last-activity:";
4
+ export const LIVE_EDIT_KEY_PREFIX = "gateway:progress:live-edit:";
5
+ /**
6
+ * Collapses the identifying parts of a key to a fixed-width digest.
7
+ *
8
+ * Two things at once. It bounds the key: organization, application and caller
9
+ * identifiers are all caller-influenced and none has a length limit here, so
10
+ * a raw key would be as long as whatever was sent. And it keeps the caller's
11
+ * identity out of storage — a Slack user id or an OAuth subject in a Redis
12
+ * key is a record of who used the product, readable by anyone who can list
13
+ * keys, for a value that only ever needs to be compared for equality.
14
+ */
15
+ function digest(parts) {
16
+ const hash = createHash("sha256");
17
+ for (const part of parts) {
18
+ // Length-prefixed for the same reason the event stream keys are: the
19
+ // parts come from claims and command bodies, so no separator is safe to
20
+ // assume absent, and `a|b` + `c` must not address `a` + `b|c`.
21
+ hash.update(`${part.length}:${part}`);
22
+ }
23
+ return hash.digest("hex");
24
+ }
25
+ /**
26
+ * One caller's cursor for one application.
27
+ *
28
+ * The channel stays in the clear: it is one of three constant strings, it
29
+ * cannot identify anybody, and being able to tell an MCP cursor from a Slack
30
+ * one by looking at a key is worth more than the nothing it discloses.
31
+ */
32
+ export function cursorKey(key) {
33
+ return `${CURSOR_KEY_PREFIX}${key.caller.channel}:${digest([
34
+ key.organizationId,
35
+ key.applicationId,
36
+ key.caller.id,
37
+ ])}`;
38
+ }
39
+ /**
40
+ * When an application's build last said anything.
41
+ *
42
+ * App-scoped rather than per-caller: whether a build has gone quiet is a fact
43
+ * about the build, and keying it by reader would let two callers disagree
44
+ * about the same turn at the same instant.
45
+ */
46
+ export function lastActivityKey(key) {
47
+ return `${LAST_ACTIVITY_KEY_PREFIX}${digest([
48
+ key.organizationId,
49
+ key.applicationId,
50
+ ])}`;
51
+ }
52
+ export function liveEditKey(key) {
53
+ return `${LIVE_EDIT_KEY_PREFIX}${digest([
54
+ key.organizationId,
55
+ key.applicationId,
56
+ ])}`;
57
+ }
58
+ //# sourceMappingURL=progress-key.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"progress-key.js","sourceRoot":"","sources":["../../src/resume/progress-key.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAIzC,MAAM,CAAC,MAAM,iBAAiB,GAAG,0BAA0B,CAAC;AAC5D,MAAM,CAAC,MAAM,wBAAwB,GAAG,iCAAiC,CAAC;AAC1E,MAAM,CAAC,MAAM,oBAAoB,GAAG,6BAA6B,CAAC;AAElE;;;;;;;;;GASG;AACH,SAAS,MAAM,CAAC,KAAwB;IACtC,MAAM,IAAI,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC;IAClC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,qEAAqE;QACrE,wEAAwE;QACxE,+DAA+D;QAC/D,IAAI,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC,MAAM,IAAI,IAAI,EAAE,CAAC,CAAC;IACxC,CAAC;IACD,OAAO,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAC5B,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,SAAS,CAAC,GAAsB;IAC9C,OAAO,GAAG,iBAAiB,GAAG,GAAG,CAAC,MAAM,CAAC,OAAO,IAAI,MAAM,CAAC;QACzD,GAAG,CAAC,cAAc;QAClB,GAAG,CAAC,aAAa;QACjB,GAAG,CAAC,MAAM,CAAC,EAAE;KACd,CAAC,EAAE,CAAC;AACP,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,eAAe,CAAC,GAAgB;IAC9C,OAAO,GAAG,wBAAwB,GAAG,MAAM,CAAC;QAC1C,GAAG,CAAC,cAAc;QAClB,GAAG,CAAC,aAAa;KAClB,CAAC,EAAE,CAAC;AACP,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,GAAgB;IAC1C,OAAO,GAAG,oBAAoB,GAAG,MAAM,CAAC;QACtC,GAAG,CAAC,cAAc;QAClB,GAAG,CAAC,aAAa;KAClB,CAAC,EAAE,CAAC;AACP,CAAC"}
@@ -0,0 +1,14 @@
1
+ import type { EventStore } from "../events/types.js";
2
+ import type { ProgressStore, RecentAppStore, TurnStore } from "./types.js";
3
+ export type Stores = {
4
+ close: () => Promise<void>;
5
+ events: EventStore;
6
+ progressCursors: ProgressStore;
7
+ recentApps: RecentAppStore;
8
+ turns: TurnStore;
9
+ };
10
+ /**
11
+ * In-process stores for the local-agent gateway. Suspended turns and progress
12
+ * cursors do not survive a restart.
13
+ */
14
+ export declare function createStores(): Promise<Stores>;
@@ -0,0 +1,18 @@
1
+ import { MemoryEventStore } from "../events/memory-event-store.js";
2
+ import { MemoryProgressStore } from "./memory-progress-store.js";
3
+ import { MemoryRecentAppStore } from "./memory-recent-app-store.js";
4
+ import { MemoryTurnStore } from "./memory-turn-store.js";
5
+ /**
6
+ * In-process stores for the local-agent gateway. Suspended turns and progress
7
+ * cursors do not survive a restart.
8
+ */
9
+ export async function createStores() {
10
+ return {
11
+ close: async () => undefined,
12
+ events: new MemoryEventStore(),
13
+ progressCursors: new MemoryProgressStore(),
14
+ recentApps: new MemoryRecentAppStore(),
15
+ turns: new MemoryTurnStore(),
16
+ };
17
+ }
18
+ //# sourceMappingURL=stores.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stores.js","sourceRoot":"","sources":["../../src/resume/stores.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AAEnE,OAAO,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AACjE,OAAO,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAC;AACpE,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAWzD;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY;IAChC,OAAO;QACL,KAAK,EAAE,KAAK,IAAI,EAAE,CAAC,SAAS;QAC5B,MAAM,EAAE,IAAI,gBAAgB,EAAE;QAC9B,eAAe,EAAE,IAAI,mBAAmB,EAAE;QAC1C,UAAU,EAAE,IAAI,oBAAoB,EAAE;QACtC,KAAK,EAAE,IAAI,eAAe,EAAE;KAC7B,CAAC;AACJ,CAAC"}
@@ -0,0 +1,124 @@
1
+ import type { EventCursor } from "../events/cursor.js";
2
+ export type TurnStatus = "pending" | "ready" | "expired";
3
+ export type SuspendedTurn = {
4
+ authUrl: string;
5
+ callbackToken: string;
6
+ createdAtMs: number;
7
+ idempotencyKey: string;
8
+ reason: "idp_step_up";
9
+ status: TurnStatus;
10
+ turnId: string;
11
+ };
12
+ export type TurnStore = {
13
+ complete(turnId: string, callbackToken: string): Promise<SuspendedTurn | undefined>;
14
+ create(input: {
15
+ authUrl: string;
16
+ callbackToken?: string;
17
+ idempotencyKey: string;
18
+ reason: SuspendedTurn["reason"];
19
+ ttlMs?: number;
20
+ turnId?: string;
21
+ }): Promise<SuspendedTurn>;
22
+ get(turnId: string): Promise<SuspendedTurn | undefined>;
23
+ getByIdempotencyKey(idempotencyKey: string): Promise<SuspendedTurn | undefined>;
24
+ };
25
+ export type RecentApp = {
26
+ applicationId: string;
27
+ };
28
+ export type RecentAppStore = {
29
+ get(input: {
30
+ organizationId: string;
31
+ userId: string;
32
+ }): Promise<RecentApp | undefined>;
33
+ set(input: {
34
+ applicationId: string;
35
+ organizationId: string;
36
+ ttlMs?: number;
37
+ userId: string;
38
+ }): Promise<void>;
39
+ };
40
+ /**
41
+ * Which channel a caller reached the gateway through. Part of a cursor's
42
+ * identity: two hosts polling the same build are two readers, and neither may
43
+ * consume the other's position.
44
+ */
45
+ export type CallerChannel = "http" | "mcp";
46
+ /**
47
+ * A caller, as far as resume is concerned.
48
+ *
49
+ * `id` is whatever the transport can name the caller by that stays the same
50
+ * across its reconnects: a CLI user id or MCP session id. It is hashed
51
+ * before it reaches storage and never appears in a log line or a metric
52
+ * label, so nothing here needs to be, or may be, a name or an address.
53
+ */
54
+ export type CallerRef = {
55
+ channel: CallerChannel;
56
+ id: string;
57
+ };
58
+ export type ProgressCursorKey = {
59
+ applicationId: string;
60
+ caller: CallerRef;
61
+ organizationId: string;
62
+ };
63
+ export type AppScopeKey = {
64
+ applicationId: string;
65
+ organizationId: string;
66
+ };
67
+ /**
68
+ * One caller's place in a live edit's history.
69
+ *
70
+ * The cursor itself is the public part, handed back to the caller. The stall
71
+ * bookkeeping is not: it exists so a long-quiet hint fires once per quiet
72
+ * spell rather than on every poll, and a caller that edited it could only
73
+ * silence or spam its own hint.
74
+ */
75
+ export type ProgressCursorRecord = {
76
+ cursor: EventCursor;
77
+ /**
78
+ * When this caller was last told the build had gone quiet, cleared as soon
79
+ * as it hears anything new.
80
+ */
81
+ stallHintedAtMs?: number;
82
+ };
83
+ /**
84
+ * Where a resumed reader picks up.
85
+ *
86
+ * Facts about the application — which live edit is serving it, when its build
87
+ * last said anything — are app-scoped, because any caller may ask them and
88
+ * every caller must get the same answer. How far a reader has got is the one
89
+ * per-caller thing here.
90
+ *
91
+ * Reads never consume. Two callers resuming the same build advance only their
92
+ * own cursor, so neither can take events away from the other.
93
+ */
94
+ export type ProgressStore = {
95
+ getCursor(key: ProgressCursorKey): Promise<ProgressCursorRecord | undefined>;
96
+ /** When this application's build last emitted activity, if it has. */
97
+ getLastActivityAtMs(key: AppScopeKey): Promise<number | undefined>;
98
+ /** The live edit currently serving an application, if one is known. */
99
+ getLiveEditId(key: AppScopeKey): Promise<string | undefined>;
100
+ setCursor(input: ProgressCursorKey & {
101
+ record: ProgressCursorRecord;
102
+ ttlMs?: number;
103
+ }): Promise<void>;
104
+ setLastActivityAtMs(input: AppScopeKey & {
105
+ atMs: number;
106
+ ttlMs?: number;
107
+ }): Promise<void>;
108
+ setLiveEditId(input: AppScopeKey & {
109
+ liveEditId: string;
110
+ ttlMs?: number;
111
+ }): Promise<void>;
112
+ };
113
+ export declare const DEFAULT_TURN_TTL_MS = 180000;
114
+ export declare const DEFAULT_RECENT_APP_TTL_MS = 86400000;
115
+ /**
116
+ * How long a caller's cursor outlives its last read.
117
+ *
118
+ * Matched to the event stream's own retention rather than to a session: a
119
+ * cursor into a stream the gateway has already reclaimed can only produce a
120
+ * stale read, and keeping it costs a key per caller per app for nothing.
121
+ */
122
+ export declare const DEFAULT_PROGRESS_CURSOR_TTL_MS = 3600000;
123
+ /** How long an application's live-edit pointer survives without a write. */
124
+ export declare const DEFAULT_LIVE_EDIT_POINTER_TTL_MS = 86400000;
@@ -0,0 +1,13 @@
1
+ export const DEFAULT_TURN_TTL_MS = 180_000;
2
+ export const DEFAULT_RECENT_APP_TTL_MS = 86_400_000;
3
+ /**
4
+ * How long a caller's cursor outlives its last read.
5
+ *
6
+ * Matched to the event stream's own retention rather than to a session: a
7
+ * cursor into a stream the gateway has already reclaimed can only produce a
8
+ * stale read, and keeping it costs a key per caller per app for nothing.
9
+ */
10
+ export const DEFAULT_PROGRESS_CURSOR_TTL_MS = 3_600_000;
11
+ /** How long an application's live-edit pointer survives without a write. */
12
+ export const DEFAULT_LIVE_EDIT_POINTER_TTL_MS = 86_400_000;
13
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/resume/types.ts"],"names":[],"mappings":"AA8HA,MAAM,CAAC,MAAM,mBAAmB,GAAG,OAAO,CAAC;AAC3C,MAAM,CAAC,MAAM,yBAAyB,GAAG,UAAU,CAAC;AAEpD;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,8BAA8B,GAAG,SAAS,CAAC;AAExD,4EAA4E;AAC5E,MAAM,CAAC,MAAM,gCAAgC,GAAG,UAAU,CAAC"}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Sanitize text that may be echoed into Claude / MCP tool results.
3
+ *
4
+ * Downstream orchestrator and integration payloads can carry secrets, control
5
+ * characters, and prompt-injection wrappers. Strip those before any agent sees
6
+ * the string.
7
+ */
8
+ /**
9
+ * Removes C0 control characters (keeps tab/LF/CR) so logs and prompts stay printable.
10
+ * Implemented without a control-character regex so eslint no-control-regex stays clean.
11
+ */
12
+ export declare function stripUnsafeChars(text: string): string;
13
+ /**
14
+ * Replaces common secret-shaped substrings with a fixed redaction token.
15
+ */
16
+ export declare function scrubSecrets(text: string): string;
17
+ /**
18
+ * Full agent-facing sanitize: scrub secrets, strip controls, neutralize backticks.
19
+ */
20
+ export declare function sanitizeAgentFacingText(text: string): string;
21
+ /**
22
+ * Deep-sanitize string leaves in tool results returned to Clark / MCP.
23
+ *
24
+ * A secret-named field is redacted whole, since the value need not look like a
25
+ * secret to be one, and a wrapper around it is no safer than the bare value: an
26
+ * object or array under `password` is replaced rather than walked, or its leaves
27
+ * would pass under names of their own that read as harmless.
28
+ *
29
+ * Booleans are the exception. A flag such as `hasPassword` reports whether a
30
+ * credential exists rather than carrying it, and redacting it hides state Clark
31
+ * needs without hiding a secret.
32
+ */
33
+ export declare function sanitizeAgentFacingValue(value: unknown): unknown;
@@ -0,0 +1,125 @@
1
+ /**
2
+ * Sanitize text that may be echoed into Claude / MCP tool results.
3
+ *
4
+ * Downstream orchestrator and integration payloads can carry secrets, control
5
+ * characters, and prompt-injection wrappers. Strip those before any agent sees
6
+ * the string.
7
+ */
8
+ /** JWT / opaque bearer-ish tokens and common vendor API key shapes. */
9
+ const SECRET_PATTERNS = [
10
+ /\beyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\b/g,
11
+ // Vendor keys often embed hyphens inside the secret (sk-proj-..., sk-ant-...).
12
+ /\b(?:sk|rk|pk)[-_][A-Za-z0-9_-]{16,}\b/gi,
13
+ /\bsbp_[A-Za-z0-9]{16,}\b/g,
14
+ /\bAKIA[0-9A-Z]{16}\b/g,
15
+ /\bBearer\s+[A-Za-z0-9._~+/=-]{16,}\b/gi,
16
+ // Plain assignments: password=... / api_key: ...
17
+ /(?<=(?:password|passwd|secret|token|api[_-]?key)\s*[:=]\s*)["']?[^\s"',;]{8,}["']?/gi,
18
+ // JSON-ish quoted keys: "password": "..."
19
+ /(?<=["'](?:password|passwd|secret|token|api[_-]?key)["']\s*:\s*)["'][^"']{4,}["']/gi,
20
+ ];
21
+ const SECRET_FIELD_KEYS = new Set([
22
+ "api_key",
23
+ "apikey",
24
+ "authorization",
25
+ "password",
26
+ "passwd",
27
+ "secret",
28
+ "token",
29
+ ]);
30
+ /**
31
+ * Field names that end in a credential word but carry a cursor or a count.
32
+ * Redacting these would hide the paging state Clark needs to keep listing.
33
+ */
34
+ const CURSOR_FIELD_KEYS = new Set([
35
+ "continuationtoken",
36
+ "nextpagetoken",
37
+ "nexttoken",
38
+ "pagetoken",
39
+ "pagingtoken",
40
+ "resumetoken",
41
+ "synctoken",
42
+ "tokencount",
43
+ ]);
44
+ const REDACTED = "[REDACTED]";
45
+ /**
46
+ * Removes C0 control characters (keeps tab/LF/CR) so logs and prompts stay printable.
47
+ * Implemented without a control-character regex so eslint no-control-regex stays clean.
48
+ */
49
+ export function stripUnsafeChars(text) {
50
+ let out = "";
51
+ for (let i = 0; i < text.length; i += 1) {
52
+ const code = text.charCodeAt(i);
53
+ if (code === 9 || code === 10 || code === 13) {
54
+ out += text[i];
55
+ continue;
56
+ }
57
+ if (code < 32 || code === 127) {
58
+ continue;
59
+ }
60
+ out += text[i];
61
+ }
62
+ return out;
63
+ }
64
+ /**
65
+ * Replaces common secret-shaped substrings with a fixed redaction token.
66
+ */
67
+ export function scrubSecrets(text) {
68
+ let next = text;
69
+ for (const pattern of SECRET_PATTERNS) {
70
+ next = next.replace(pattern, REDACTED);
71
+ }
72
+ return next;
73
+ }
74
+ /**
75
+ * Full agent-facing sanitize: scrub secrets, strip controls, neutralize backticks.
76
+ */
77
+ export function sanitizeAgentFacingText(text) {
78
+ return scrubSecrets(stripUnsafeChars(text)).replace(/`/g, "'");
79
+ }
80
+ function isSecretFieldKey(key) {
81
+ const normalized = key.toLowerCase().replace(/[^a-z0-9]/g, "");
82
+ if (CURSOR_FIELD_KEYS.has(normalized)) {
83
+ return false;
84
+ }
85
+ return (SECRET_FIELD_KEYS.has(key.toLowerCase()) ||
86
+ SECRET_FIELD_KEYS.has(normalized) ||
87
+ normalized.endsWith("password") ||
88
+ normalized.endsWith("secret") ||
89
+ normalized.endsWith("token") ||
90
+ normalized.endsWith("apikey"));
91
+ }
92
+ /**
93
+ * Deep-sanitize string leaves in tool results returned to Clark / MCP.
94
+ *
95
+ * A secret-named field is redacted whole, since the value need not look like a
96
+ * secret to be one, and a wrapper around it is no safer than the bare value: an
97
+ * object or array under `password` is replaced rather than walked, or its leaves
98
+ * would pass under names of their own that read as harmless.
99
+ *
100
+ * Booleans are the exception. A flag such as `hasPassword` reports whether a
101
+ * credential exists rather than carrying it, and redacting it hides state Clark
102
+ * needs without hiding a secret.
103
+ */
104
+ export function sanitizeAgentFacingValue(value) {
105
+ if (typeof value === "string") {
106
+ return sanitizeAgentFacingText(value);
107
+ }
108
+ if (Array.isArray(value)) {
109
+ return value.map((entry) => sanitizeAgentFacingValue(entry));
110
+ }
111
+ if (value && typeof value === "object") {
112
+ const out = {};
113
+ for (const [key, entry] of Object.entries(value)) {
114
+ const carriesNoSecret = typeof entry === "boolean" || entry == null;
115
+ if (!carriesNoSecret && isSecretFieldKey(key)) {
116
+ out[key] = REDACTED;
117
+ continue;
118
+ }
119
+ out[key] = sanitizeAgentFacingValue(entry);
120
+ }
121
+ return out;
122
+ }
123
+ return value;
124
+ }
125
+ //# sourceMappingURL=agent-facing-text.js.map