agentlock-shared 0.1.0 → 0.3.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 (171) hide show
  1. package/dist/__tests__/billing.test.d.ts +2 -0
  2. package/dist/__tests__/billing.test.d.ts.map +1 -0
  3. package/dist/__tests__/billing.test.js +31 -0
  4. package/dist/__tests__/billing.test.js.map +1 -0
  5. package/dist/__tests__/crypto.test.js +137 -47
  6. package/dist/__tests__/crypto.test.js.map +1 -1
  7. package/dist/__tests__/dns-pinning.test.d.ts +2 -0
  8. package/dist/__tests__/dns-pinning.test.d.ts.map +1 -0
  9. package/dist/__tests__/dns-pinning.test.js +33 -0
  10. package/dist/__tests__/dns-pinning.test.js.map +1 -0
  11. package/dist/__tests__/llm-classifier-cache-store.test.d.ts +2 -0
  12. package/dist/__tests__/llm-classifier-cache-store.test.d.ts.map +1 -0
  13. package/dist/__tests__/llm-classifier-cache-store.test.js +65 -0
  14. package/dist/__tests__/llm-classifier-cache-store.test.js.map +1 -0
  15. package/dist/__tests__/llm-classifier-cache.test.d.ts +2 -0
  16. package/dist/__tests__/llm-classifier-cache.test.d.ts.map +1 -0
  17. package/dist/__tests__/llm-classifier-cache.test.js +44 -0
  18. package/dist/__tests__/llm-classifier-cache.test.js.map +1 -0
  19. package/dist/__tests__/llm-classifier.test.d.ts +2 -0
  20. package/dist/__tests__/llm-classifier.test.d.ts.map +1 -0
  21. package/dist/__tests__/llm-classifier.test.js +167 -0
  22. package/dist/__tests__/llm-classifier.test.js.map +1 -0
  23. package/dist/__tests__/messaging.test.d.ts +2 -0
  24. package/dist/__tests__/messaging.test.d.ts.map +1 -0
  25. package/dist/__tests__/messaging.test.js +75 -0
  26. package/dist/__tests__/messaging.test.js.map +1 -0
  27. package/dist/__tests__/plans-classifier-limits.test.d.ts +2 -0
  28. package/dist/__tests__/plans-classifier-limits.test.d.ts.map +1 -0
  29. package/dist/__tests__/plans-classifier-limits.test.js +22 -0
  30. package/dist/__tests__/plans-classifier-limits.test.js.map +1 -0
  31. package/dist/__tests__/policy-category-floor.test.d.ts +2 -0
  32. package/dist/__tests__/policy-category-floor.test.d.ts.map +1 -0
  33. package/dist/__tests__/policy-category-floor.test.js +46 -0
  34. package/dist/__tests__/policy-category-floor.test.js.map +1 -0
  35. package/dist/__tests__/policy-claude-bash.test.d.ts +2 -0
  36. package/dist/__tests__/policy-claude-bash.test.d.ts.map +1 -0
  37. package/dist/__tests__/policy-claude-bash.test.js +401 -0
  38. package/dist/__tests__/policy-claude-bash.test.js.map +1 -0
  39. package/dist/__tests__/policy-llm-floor.test.d.ts +2 -0
  40. package/dist/__tests__/policy-llm-floor.test.d.ts.map +1 -0
  41. package/dist/__tests__/policy-llm-floor.test.js +107 -0
  42. package/dist/__tests__/policy-llm-floor.test.js.map +1 -0
  43. package/dist/__tests__/policy-ssh-e2e.test.d.ts +2 -0
  44. package/dist/__tests__/policy-ssh-e2e.test.d.ts.map +1 -0
  45. package/dist/__tests__/policy-ssh-e2e.test.js +89 -0
  46. package/dist/__tests__/policy-ssh-e2e.test.js.map +1 -0
  47. package/dist/__tests__/policy-ssh-sessions.test.d.ts +2 -0
  48. package/dist/__tests__/policy-ssh-sessions.test.d.ts.map +1 -0
  49. package/dist/__tests__/policy-ssh-sessions.test.js +139 -0
  50. package/dist/__tests__/policy-ssh-sessions.test.js.map +1 -0
  51. package/dist/__tests__/policy-ssh.test.d.ts +2 -0
  52. package/dist/__tests__/policy-ssh.test.d.ts.map +1 -0
  53. package/dist/__tests__/policy-ssh.test.js +180 -0
  54. package/dist/__tests__/policy-ssh.test.js.map +1 -0
  55. package/dist/__tests__/policy.test.js +522 -7
  56. package/dist/__tests__/policy.test.js.map +1 -1
  57. package/dist/__tests__/redact.test.js +76 -0
  58. package/dist/__tests__/redact.test.js.map +1 -1
  59. package/dist/__tests__/signing.test.js +89 -0
  60. package/dist/__tests__/signing.test.js.map +1 -1
  61. package/dist/__tests__/ssh-fingerprint.test.d.ts +2 -0
  62. package/dist/__tests__/ssh-fingerprint.test.d.ts.map +1 -0
  63. package/dist/__tests__/ssh-fingerprint.test.js +19 -0
  64. package/dist/__tests__/ssh-fingerprint.test.js.map +1 -0
  65. package/dist/__tests__/vpn-route.test.d.ts +2 -0
  66. package/dist/__tests__/vpn-route.test.d.ts.map +1 -0
  67. package/dist/__tests__/vpn-route.test.js +72 -0
  68. package/dist/__tests__/vpn-route.test.js.map +1 -0
  69. package/dist/__tests__/wireguard.test.d.ts +2 -0
  70. package/dist/__tests__/wireguard.test.d.ts.map +1 -0
  71. package/dist/__tests__/wireguard.test.js +114 -0
  72. package/dist/__tests__/wireguard.test.js.map +1 -0
  73. package/dist/billing.d.ts +12 -0
  74. package/dist/billing.d.ts.map +1 -0
  75. package/dist/billing.js +41 -0
  76. package/dist/billing.js.map +1 -0
  77. package/dist/crypto.d.ts +41 -0
  78. package/dist/crypto.d.ts.map +1 -1
  79. package/dist/crypto.js +208 -6
  80. package/dist/crypto.js.map +1 -1
  81. package/dist/dns-pinning.d.ts +28 -0
  82. package/dist/dns-pinning.d.ts.map +1 -0
  83. package/dist/dns-pinning.js +113 -0
  84. package/dist/dns-pinning.js.map +1 -0
  85. package/dist/index.d.ts +6 -0
  86. package/dist/index.d.ts.map +1 -1
  87. package/dist/index.js +9 -0
  88. package/dist/index.js.map +1 -1
  89. package/dist/llm-classifier-cache-store.d.ts +49 -0
  90. package/dist/llm-classifier-cache-store.d.ts.map +1 -0
  91. package/dist/llm-classifier-cache-store.js +63 -0
  92. package/dist/llm-classifier-cache-store.js.map +1 -0
  93. package/dist/llm-classifier-cache.d.ts +6 -0
  94. package/dist/llm-classifier-cache.d.ts.map +1 -0
  95. package/dist/llm-classifier-cache.js +52 -0
  96. package/dist/llm-classifier-cache.js.map +1 -0
  97. package/dist/llm-classifier.d.ts +29 -0
  98. package/dist/llm-classifier.d.ts.map +1 -0
  99. package/dist/llm-classifier.js +191 -0
  100. package/dist/llm-classifier.js.map +1 -0
  101. package/dist/observability.d.ts +36 -0
  102. package/dist/observability.d.ts.map +1 -0
  103. package/dist/observability.js +75 -0
  104. package/dist/observability.js.map +1 -0
  105. package/dist/plans.d.ts +21 -0
  106. package/dist/plans.d.ts.map +1 -1
  107. package/dist/plans.js +52 -14
  108. package/dist/plans.js.map +1 -1
  109. package/dist/policy.d.ts +173 -3
  110. package/dist/policy.d.ts.map +1 -1
  111. package/dist/policy.js +951 -58
  112. package/dist/policy.js.map +1 -1
  113. package/dist/redact.d.ts.map +1 -1
  114. package/dist/redact.js +104 -7
  115. package/dist/redact.js.map +1 -1
  116. package/dist/regex-safety.d.ts +21 -0
  117. package/dist/regex-safety.d.ts.map +1 -0
  118. package/dist/regex-safety.js +49 -0
  119. package/dist/regex-safety.js.map +1 -0
  120. package/dist/sanitize.d.ts +31 -0
  121. package/dist/sanitize.d.ts.map +1 -0
  122. package/dist/sanitize.js +54 -0
  123. package/dist/sanitize.js.map +1 -0
  124. package/dist/schemas.d.ts +267 -14
  125. package/dist/schemas.d.ts.map +1 -1
  126. package/dist/schemas.js +152 -10
  127. package/dist/schemas.js.map +1 -1
  128. package/dist/signing.d.ts +15 -0
  129. package/dist/signing.d.ts.map +1 -1
  130. package/dist/signing.js +53 -4
  131. package/dist/signing.js.map +1 -1
  132. package/dist/ssh-fingerprint.d.ts +10 -0
  133. package/dist/ssh-fingerprint.d.ts.map +1 -0
  134. package/dist/ssh-fingerprint.js +52 -0
  135. package/dist/ssh-fingerprint.js.map +1 -0
  136. package/dist/ssrf.d.ts +36 -0
  137. package/dist/ssrf.d.ts.map +1 -0
  138. package/dist/ssrf.js +140 -0
  139. package/dist/ssrf.js.map +1 -0
  140. package/dist/types.d.ts +131 -0
  141. package/dist/types.d.ts.map +1 -1
  142. package/dist/wireguard.d.ts +63 -0
  143. package/dist/wireguard.d.ts.map +1 -0
  144. package/dist/wireguard.js +226 -0
  145. package/dist/wireguard.js.map +1 -0
  146. package/package.json +42 -29
  147. package/.turbo/turbo-build.log +0 -4
  148. package/.turbo/turbo-test.log +0 -34
  149. package/dist/__tests__/content-crypto.test.d.ts +0 -2
  150. package/dist/__tests__/content-crypto.test.d.ts.map +0 -1
  151. package/dist/__tests__/content-crypto.test.js +0 -117
  152. package/dist/__tests__/content-crypto.test.js.map +0 -1
  153. package/dist/content-crypto.d.ts +0 -24
  154. package/dist/content-crypto.d.ts.map +0 -1
  155. package/dist/content-crypto.js +0 -58
  156. package/dist/content-crypto.js.map +0 -1
  157. package/src/__tests__/policy.test.ts +0 -88
  158. package/src/__tests__/redact.test.ts +0 -41
  159. package/src/__tests__/signing.test.ts +0 -55
  160. package/src/crypto.ts +0 -87
  161. package/src/index.ts +0 -8
  162. package/src/mcp-catalog.ts +0 -181
  163. package/src/plans.ts +0 -96
  164. package/src/policy.ts +0 -186
  165. package/src/redact.ts +0 -114
  166. package/src/schemas.ts +0 -53
  167. package/src/signing.ts +0 -120
  168. package/src/types.ts +0 -212
  169. package/test-gateway.mjs +0 -47
  170. package/tsconfig.json +0 -10
  171. package/vitest.config.ts +0 -8
@@ -0,0 +1,52 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.buildCacheKey = buildCacheKey;
4
+ const crypto_1 = require("crypto");
5
+ /**
6
+ * Shape-keyed cache key for LLM risk classifications. Keys are derived
7
+ * from the SHAPE of the payload, not its values — see the Phase 1 doc
8
+ * for the rationale. The actual storage of cached entries lives in
9
+ * `llm-classifier-cache-store.ts`.
10
+ *
11
+ * Properties:
12
+ *
13
+ * 1. **Privacy**: payload values never enter the cache key.
14
+ * 2. **Efficiency**: two calls to `notion.delete_page` with different page
15
+ * IDs resolve to the same cache entry.
16
+ * 3. **Stability**: key ordering doesn't matter, so `{a:1,b:2}` and
17
+ * `{b:2,a:1}` hit the same entry.
18
+ */
19
+ /**
20
+ * Recursively extract the "shape" of a value: key names and nested
21
+ * structure, with all leaf values replaced by a generic sentinel.
22
+ */
23
+ function extractShape(value, depth = 0) {
24
+ if (depth > 3)
25
+ return '<deep>';
26
+ if (value === null)
27
+ return null;
28
+ if (value === undefined)
29
+ return undefined;
30
+ if (Array.isArray(value)) {
31
+ return value.length > 0 ? [extractShape(value[0], depth + 1)] : [];
32
+ }
33
+ if (typeof value === 'object') {
34
+ const obj = value;
35
+ const sorted = Object.keys(obj).sort();
36
+ const out = {};
37
+ for (const k of sorted) {
38
+ out[k] = extractShape(obj[k], depth + 1);
39
+ }
40
+ return out;
41
+ }
42
+ return '<value>';
43
+ }
44
+ /**
45
+ * Compute a stable cache key from the (lowercased) tool name and the
46
+ * payload shape. Output is a 32-char hex digest.
47
+ */
48
+ function buildCacheKey(tool, payload) {
49
+ const normalized = `${tool.toLowerCase()}|${JSON.stringify(extractShape(payload))}`;
50
+ return (0, crypto_1.createHash)('sha256').update(normalized).digest('hex').slice(0, 32);
51
+ }
52
+ //# sourceMappingURL=llm-classifier-cache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"llm-classifier-cache.js","sourceRoot":"","sources":["../src/llm-classifier-cache.ts"],"names":[],"mappings":";;AA4CA,sCAMC;AAlDD,mCAAoC;AAEpC;;;;;;;;;;;;;GAaG;AAEH;;;GAGG;AACH,SAAS,YAAY,CAAC,KAAc,EAAE,KAAK,GAAG,CAAC;IAC7C,IAAI,KAAK,GAAG,CAAC;QAAE,OAAO,QAAQ,CAAC;IAC/B,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IAChC,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IAC1C,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IACrE,CAAC;IACD,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,MAAM,GAAG,GAAG,KAAgC,CAAC;QAC7C,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;QACvC,MAAM,GAAG,GAA4B,EAAE,CAAC;QACxC,KAAK,MAAM,CAAC,IAAI,MAAM,EAAE,CAAC;YACvB,GAAG,CAAC,CAAC,CAAC,GAAG,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,KAAK,GAAG,CAAC,CAAC,CAAC;QAC3C,CAAC;QACD,OAAO,GAAG,CAAC;IACb,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED;;;GAGG;AACH,SAAgB,aAAa,CAC3B,IAAY,EACZ,OAAgC;IAEhC,MAAM,UAAU,GAAG,GAAG,IAAI,CAAC,WAAW,EAAE,IAAI,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC;IACpF,OAAO,IAAA,mBAAU,EAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AAC5E,CAAC"}
@@ -0,0 +1,29 @@
1
+ import type { LLMClassification } from './types.js';
2
+ export interface ClassifyOptions {
3
+ /** Hard cap on how long to wait for the LLM. Defaults to 800ms. */
4
+ timeoutMs?: number;
5
+ }
6
+ type ClassifierFn = (tool: string, payload: Record<string, unknown>) => Promise<Omit<LLMClassification, 'cached'> | null>;
7
+ /** Test-only: replace the classifier implementation. Pass null to reset. */
8
+ export declare function __setClassifierForTest(fn: ClassifierFn | null): void;
9
+ /**
10
+ * Strip values from a payload, keeping only keys and type descriptors.
11
+ * Separate from the cache-key version because we want slightly more detail
12
+ * for the LLM (it sees type names, not just `<string>`).
13
+ */
14
+ /**
15
+ * Exported for unit-test verification of the privacy guarantee. Not part of
16
+ * the stable public API — treat as internal.
17
+ * @internal
18
+ */
19
+ export declare function shapeOnly(value: unknown, depth?: number): unknown;
20
+ /**
21
+ * Classify a tool call using the LLM. Returns null if the classifier is
22
+ * disabled (no API key), fails, or times out. Results are cached by
23
+ * tool + payload shape for the default TTL (24h).
24
+ *
25
+ * This function NEVER throws — all errors are caught and turned into null.
26
+ */
27
+ export declare function classifyWithLLM(tool: string, payload: Record<string, unknown>, options?: ClassifyOptions): Promise<LLMClassification | null>;
28
+ export {};
29
+ //# sourceMappingURL=llm-classifier.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"llm-classifier.d.ts","sourceRoot":"","sources":["../src/llm-classifier.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAmCpD,MAAM,WAAW,eAAe;IAC9B,mEAAmE;IACnE,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AASD,KAAK,YAAY,GAAG,CAClB,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAC7B,OAAO,CAAC,IAAI,CAAC,iBAAiB,EAAE,QAAQ,CAAC,GAAG,IAAI,CAAC,CAAC;AAIvD,4EAA4E;AAC5E,wBAAgB,sBAAsB,CAAC,EAAE,EAAE,YAAY,GAAG,IAAI,GAAG,IAAI,CAEpE;AAmED;;;;GAIG;AAOH;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,SAAI,GAAG,OAAO,CAiB5D;AAMD;;;;;;GAMG;AACH,wBAAsB,eAAe,CACnC,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAChC,OAAO,GAAE,eAAoB,GAC5B,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC,CAkDnC"}
@@ -0,0 +1,191 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.__setClassifierForTest = __setClassifierForTest;
4
+ exports.shapeOnly = shapeOnly;
5
+ exports.classifyWithLLM = classifyWithLLM;
6
+ const zod_1 = require("zod");
7
+ const llm_classifier_cache_js_1 = require("./llm-classifier-cache.js");
8
+ const llm_classifier_cache_store_js_1 = require("./llm-classifier-cache-store.js");
9
+ /**
10
+ * LLM-based risk classifier. Called by the gateway when the server-side
11
+ * static category floor (`getCategoryFloor`) returns `read` — i.e. the tool
12
+ * name and payload don't match any of the hardcoded financial/admin
13
+ * patterns. The LLM provides a second opinion on what the tool is doing
14
+ * based on its name and payload shape.
15
+ *
16
+ * Security & privacy:
17
+ * - Only the TOOL NAME and PAYLOAD SHAPE (keys, types, nested structure) are
18
+ * sent to the LLM. Actual payload values never leave the server.
19
+ * - The LLM's output is advisory. The policy layer applies
20
+ * `max(declared, staticFloor, llmFloor)` — the LLM can only UPGRADE a
21
+ * classification, never downgrade.
22
+ * - All calls are hard-capped at 800ms. If the LLM is slow, unreachable, or
23
+ * errors for any reason, this function returns `null` and the gateway
24
+ * falls back to the static floor alone.
25
+ * - Caching is by shape, so identical tool-call patterns resolve to the same
26
+ * entry regardless of actual values. Cache is in-memory (per-process) in
27
+ * this slice; a persistent store follows in a later phase.
28
+ */
29
+ // ---------------------------------------------------------------------------
30
+ // Classifier I/O schema
31
+ // ---------------------------------------------------------------------------
32
+ const ClassificationSchema = zod_1.z.object({
33
+ category: zod_1.z.enum(['read', 'write', 'financial', 'admin']),
34
+ reason: zod_1.z.string().min(1).max(200),
35
+ confidence: zod_1.z.number().min(0).max(1),
36
+ });
37
+ let testClassifier = null;
38
+ /** Test-only: replace the classifier implementation. Pass null to reset. */
39
+ function __setClassifierForTest(fn) {
40
+ testClassifier = fn;
41
+ }
42
+ // ---------------------------------------------------------------------------
43
+ // Default classifier — OpenRouter via Vercel AI SDK
44
+ // ---------------------------------------------------------------------------
45
+ const MODEL_ID = 'google/gemini-2.5-flash-lite';
46
+ const SYSTEM_PROMPT = `You are a security classifier for an AI agent proxy. Your job is to look at a tool name and payload shape and classify the action into one of four risk categories:
47
+
48
+ - "read": retrieves or inspects data without modifying anything (lookups, lists, snapshots, queries)
49
+ - "write": creates, updates, or deletes data in a standard application context (create_page, update_record, delete_item, post_message)
50
+ - "financial": moves money or handles payment/billing (charges, refunds, invoices, subscriptions, payouts, wire transfers)
51
+ - "admin": privileged administrative actions (user management, permission changes, key rotation, database drops, MFA resets, workspace deletion)
52
+
53
+ Rules:
54
+ 1. When in doubt between two categories, pick the MORE restrictive one. Security over convenience.
55
+ 2. Base your decision primarily on the TOOL NAME — it is the strongest signal.
56
+ 3. The payload shape is provided as a hint, but contains no actual values (only type descriptors).
57
+ 4. If the tool name clearly suggests a read (list_*, get_*, fetch_*, search_*, show_*, describe_*), return "read".
58
+ 5. If the tool name mentions money, payments, billing, or financial verbs (charge, refund, transfer, payout), return "financial".
59
+ 6. If the tool name mentions user management, permissions, keys, or destructive schema changes, return "admin".
60
+ 7. Otherwise, if the tool name suggests any mutation (create, update, delete, send, post, modify), return "write".
61
+ 8. Keep "reason" under 200 characters. Be terse and specific.
62
+
63
+ Return a JSON object with fields: category, reason, and confidence (a number between 0.0 and 1.0).`;
64
+ async function defaultClassifier(tool, payload) {
65
+ const apiKey = process.env.OPENROUTER_API_KEY;
66
+ if (!apiKey)
67
+ return null;
68
+ // Dynamic import so packages/shared doesn't force-load the AI SDK at module
69
+ // init time — keeps startup fast when the classifier is disabled.
70
+ // v6 structured-output pattern: generateText + Output.object({ schema }).
71
+ const [{ createOpenRouter }, { generateText, Output }] = await Promise.all([
72
+ import('@openrouter/ai-sdk-provider'),
73
+ import('ai'),
74
+ ]);
75
+ const openrouter = createOpenRouter({ apiKey });
76
+ // Send ONLY the tool name and payload shape — values are stripped in the
77
+ // prompt construction below.
78
+ const payloadShape = shapeOnly(payload);
79
+ const userPrompt = `Tool: ${tool}\nPayload shape: ${JSON.stringify(payloadShape)}`;
80
+ // v6 structured output: pass the Zod schema via `output: Output.object({ schema })`.
81
+ // The parsed + validated value is returned on the `output` property of the result.
82
+ const { output } = await generateText({
83
+ model: openrouter(MODEL_ID),
84
+ output: Output.object({ schema: ClassificationSchema }),
85
+ system: SYSTEM_PROMPT,
86
+ prompt: userPrompt,
87
+ });
88
+ return {
89
+ category: output.category,
90
+ reason: output.reason,
91
+ confidence: output.confidence,
92
+ model: MODEL_ID,
93
+ };
94
+ }
95
+ /**
96
+ * Strip values from a payload, keeping only keys and type descriptors.
97
+ * Separate from the cache-key version because we want slightly more detail
98
+ * for the LLM (it sees type names, not just `<string>`).
99
+ */
100
+ // TODO: `shapeOnly` here and `extractShape` in llm-classifier-cache.ts share
101
+ // ~95% of their logic (recursive walk, depth cap, key sorting, array
102
+ // handling). The only meaningful difference is the leaf format: this one
103
+ // uses `<${typeof value}>` for the LLM, the cache version uses `<value>`
104
+ // for dedup. Consolidate into a shared `walkShape(value, leafFormatter)`
105
+ // helper when adding support for `Map`/`Set`/`Date` so both stay in sync.
106
+ /**
107
+ * Exported for unit-test verification of the privacy guarantee. Not part of
108
+ * the stable public API — treat as internal.
109
+ * @internal
110
+ */
111
+ function shapeOnly(value, depth = 0) {
112
+ if (depth > 3)
113
+ return '<deep>';
114
+ if (value === null)
115
+ return null;
116
+ if (value === undefined)
117
+ return undefined;
118
+ if (Array.isArray(value)) {
119
+ return value.length > 0 ? [shapeOnly(value[0], depth + 1)] : [];
120
+ }
121
+ if (typeof value === 'object') {
122
+ const obj = value;
123
+ const sorted = Object.keys(obj).sort();
124
+ const out = {};
125
+ for (const k of sorted) {
126
+ out[k] = shapeOnly(obj[k], depth + 1);
127
+ }
128
+ return out;
129
+ }
130
+ return `<${typeof value}>`;
131
+ }
132
+ // ---------------------------------------------------------------------------
133
+ // Public API
134
+ // ---------------------------------------------------------------------------
135
+ /**
136
+ * Classify a tool call using the LLM. Returns null if the classifier is
137
+ * disabled (no API key), fails, or times out. Results are cached by
138
+ * tool + payload shape for the default TTL (24h).
139
+ *
140
+ * This function NEVER throws — all errors are caught and turned into null.
141
+ */
142
+ async function classifyWithLLM(tool, payload, options = {}) {
143
+ const timeoutMs = options.timeoutMs ?? 800;
144
+ // Cache check — the store is async but has its own internal timeout +
145
+ // error handling and will resolve to null on any failure.
146
+ const cacheKey = (0, llm_classifier_cache_js_1.buildCacheKey)(tool, payload);
147
+ const store = (0, llm_classifier_cache_store_js_1.getClassifierCacheStore)();
148
+ const cached = await store.get(cacheKey);
149
+ if (cached) {
150
+ return cached;
151
+ }
152
+ // Select classifier impl
153
+ const impl = testClassifier ?? defaultClassifier;
154
+ // Race the classifier against a hard timeout.
155
+ // We track the timer so we can clear it when the classifier wins, and we
156
+ // attach a noop catch to the classifier promise so an eventual rejection
157
+ // after the race doesn't become an unhandled rejection.
158
+ let timer;
159
+ try {
160
+ const classifierPromise = impl(tool, payload);
161
+ // Swallow any post-race rejection from the losing side.
162
+ classifierPromise.catch(() => { });
163
+ const timeoutPromise = new Promise((resolve) => {
164
+ timer = setTimeout(() => resolve(null), timeoutMs);
165
+ });
166
+ const result = await Promise.race([classifierPromise, timeoutPromise]);
167
+ if (!result)
168
+ return null;
169
+ // Cache and return. We fire-and-forget the set so a slow persistent
170
+ // store never blocks the gateway response. The store swallows errors
171
+ // internally; the `.catch` here is a belt-and-braces guard against
172
+ // an unhandled rejection if a future implementation throws.
173
+ store.set(cacheKey, result).catch(() => { });
174
+ return { ...result, cached: false };
175
+ }
176
+ catch (err) {
177
+ // Log but never throw — graceful fallback to static-floor-only behavior.
178
+ // Bugs (Zod parse failures, typos) still surface here instead of being
179
+ // invisibly dropped.
180
+ console.warn('[llm-classifier] error, falling back to static floor', {
181
+ tool,
182
+ error: err instanceof Error ? err.message : String(err),
183
+ });
184
+ return null;
185
+ }
186
+ finally {
187
+ if (timer)
188
+ clearTimeout(timer);
189
+ }
190
+ }
191
+ //# sourceMappingURL=llm-classifier.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"llm-classifier.js","sourceRoot":"","sources":["../src/llm-classifier.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Light-weight structured logging + tracing helpers shared by web & runner.
3
+ *
4
+ * This is deliberately NOT a full observability stack (no Sentry SDK, no
5
+ * OTEL exporter). It gives us JSON-per-line output that downstream log
6
+ * aggregators (Vercel, Fly.io, Datadog, Axiom…) can parse and index. Drop
7
+ * a real tracer/exporter in later by replacing these functions — every
8
+ * call site uses the exported names, no `console.*` sprinkled around.
9
+ *
10
+ * Design rules:
11
+ * - Never throw. A logger that can crash your request path is worse than
12
+ * a logger that silently no-ops when env is weird.
13
+ * - Never include secrets. Callers should `sanitizeError()` first when the
14
+ * payload comes from an Error object or external service response.
15
+ * - One line per event, minified JSON, stable field names.
16
+ */
17
+ export type LogLevel = 'debug' | 'info' | 'warn' | 'error';
18
+ /**
19
+ * Emit a structured log line. Meant for server-side JS (Runner + Next.js
20
+ * route handlers). Browser code should use the console directly.
21
+ *
22
+ * The `ctx` object should contain scalar identifiers (workspace_id,
23
+ * request_id, agent_id, job_id) — anything else gets JSON.stringified
24
+ * verbatim, so don't pass objects with potential secrets.
25
+ */
26
+ export declare function log(level: LogLevel, message: string, ctx?: Record<string, unknown>): void;
27
+ export declare const logInfo: (msg: string, ctx?: Record<string, unknown>) => void;
28
+ export declare const logWarn: (msg: string, ctx?: Record<string, unknown>) => void;
29
+ export declare const logDebug: (msg: string, ctx?: Record<string, unknown>) => void;
30
+ /**
31
+ * Log an Error (or unknown) under the `error` level with a sanitized
32
+ * message and optional context. Stack traces and connection strings are
33
+ * stripped by `sanitizeError()` before emit.
34
+ */
35
+ export declare function logError(msg: string, err: unknown, ctx?: Record<string, unknown>): void;
36
+ //# sourceMappingURL=observability.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"observability.d.ts","sourceRoot":"","sources":["../src/observability.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAIH,MAAM,MAAM,QAAQ,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;AAW3D;;;;;;;GAOG;AACH,wBAAgB,GAAG,CACjB,KAAK,EAAE,QAAQ,EACf,OAAO,EAAE,MAAM,EACf,GAAG,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GAChC,IAAI,CAmBN;AAED,eAAO,MAAM,OAAO,GAAK,KAAK,MAAM,EAAE,MAAM,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,SAA2B,CAAC;AAC/F,eAAO,MAAM,OAAO,GAAK,KAAK,MAAM,EAAE,MAAM,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,SAA2B,CAAC;AAC/F,eAAO,MAAM,QAAQ,GAAI,KAAK,MAAM,EAAE,MAAM,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,SAA2B,CAAC;AAE/F;;;;GAIG;AACH,wBAAgB,QAAQ,CACtB,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,OAAO,EACZ,GAAG,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GAChC,IAAI,CAEN"}
@@ -0,0 +1,75 @@
1
+ "use strict";
2
+ /**
3
+ * Light-weight structured logging + tracing helpers shared by web & runner.
4
+ *
5
+ * This is deliberately NOT a full observability stack (no Sentry SDK, no
6
+ * OTEL exporter). It gives us JSON-per-line output that downstream log
7
+ * aggregators (Vercel, Fly.io, Datadog, Axiom…) can parse and index. Drop
8
+ * a real tracer/exporter in later by replacing these functions — every
9
+ * call site uses the exported names, no `console.*` sprinkled around.
10
+ *
11
+ * Design rules:
12
+ * - Never throw. A logger that can crash your request path is worse than
13
+ * a logger that silently no-ops when env is weird.
14
+ * - Never include secrets. Callers should `sanitizeError()` first when the
15
+ * payload comes from an Error object or external service response.
16
+ * - One line per event, minified JSON, stable field names.
17
+ */
18
+ Object.defineProperty(exports, "__esModule", { value: true });
19
+ exports.logDebug = exports.logWarn = exports.logInfo = void 0;
20
+ exports.log = log;
21
+ exports.logError = logError;
22
+ const sanitize_js_1 = require("./sanitize.js");
23
+ const LOG_LEVEL_ORDER = {
24
+ debug: 10, info: 20, warn: 30, error: 40,
25
+ };
26
+ function resolveMinLevel() {
27
+ const raw = (typeof process !== 'undefined' && process.env?.LOG_LEVEL) || 'info';
28
+ return LOG_LEVEL_ORDER[raw] ?? LOG_LEVEL_ORDER.info;
29
+ }
30
+ /**
31
+ * Emit a structured log line. Meant for server-side JS (Runner + Next.js
32
+ * route handlers). Browser code should use the console directly.
33
+ *
34
+ * The `ctx` object should contain scalar identifiers (workspace_id,
35
+ * request_id, agent_id, job_id) — anything else gets JSON.stringified
36
+ * verbatim, so don't pass objects with potential secrets.
37
+ */
38
+ function log(level, message, ctx = {}) {
39
+ try {
40
+ if (LOG_LEVEL_ORDER[level] < resolveMinLevel())
41
+ return;
42
+ const line = JSON.stringify({
43
+ ts: new Date().toISOString(),
44
+ level,
45
+ msg: message,
46
+ ...ctx,
47
+ });
48
+ // stderr for warn/error so error aggregators pick them up separately
49
+ // from info/debug; matches Fastify+pino convention.
50
+ if (level === 'warn' || level === 'error') {
51
+ process.stderr.write(line + '\n');
52
+ }
53
+ else {
54
+ process.stdout.write(line + '\n');
55
+ }
56
+ }
57
+ catch {
58
+ /* swallow — logging must never throw */
59
+ }
60
+ }
61
+ const logInfo = (msg, ctx) => log('info', msg, ctx);
62
+ exports.logInfo = logInfo;
63
+ const logWarn = (msg, ctx) => log('warn', msg, ctx);
64
+ exports.logWarn = logWarn;
65
+ const logDebug = (msg, ctx) => log('debug', msg, ctx);
66
+ exports.logDebug = logDebug;
67
+ /**
68
+ * Log an Error (or unknown) under the `error` level with a sanitized
69
+ * message and optional context. Stack traces and connection strings are
70
+ * stripped by `sanitizeError()` before emit.
71
+ */
72
+ function logError(msg, err, ctx = {}) {
73
+ log('error', msg, { ...ctx, error: (0, sanitize_js_1.sanitizeError)(err) });
74
+ }
75
+ //# sourceMappingURL=observability.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"observability.js","sourceRoot":"","sources":["../src/observability.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;GAeG;;;AAuBH,kBAuBC;AAWD,4BAMC;AA7DD,+CAA8C;AAI9C,MAAM,eAAe,GAA6B;IAChD,KAAK,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE;CACzC,CAAC;AAEF,SAAS,eAAe;IACtB,MAAM,GAAG,GAAG,CAAC,OAAO,OAAO,KAAK,WAAW,IAAI,OAAO,CAAC,GAAG,EAAE,SAAS,CAAC,IAAI,MAAM,CAAC;IACjF,OAAO,eAAe,CAAC,GAAe,CAAC,IAAI,eAAe,CAAC,IAAI,CAAC;AAClE,CAAC;AAED;;;;;;;GAOG;AACH,SAAgB,GAAG,CACjB,KAAe,EACf,OAAe,EACf,MAA+B,EAAE;IAEjC,IAAI,CAAC;QACH,IAAI,eAAe,CAAC,KAAK,CAAC,GAAG,eAAe,EAAE;YAAE,OAAO;QACvD,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC;YAC1B,EAAE,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;YAC5B,KAAK;YACL,GAAG,EAAE,OAAO;YACZ,GAAG,GAAG;SACP,CAAC,CAAC;QACH,qEAAqE;QACrE,oDAAoD;QACpD,IAAI,KAAK,KAAK,MAAM,IAAI,KAAK,KAAK,OAAO,EAAE,CAAC;YAC1C,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC;QACpC,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC;QACpC,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,wCAAwC;IAC1C,CAAC;AACH,CAAC;AAEM,MAAM,OAAO,GAAI,CAAC,GAAW,EAAE,GAA6B,EAAE,EAAE,CAAC,GAAG,CAAC,MAAM,EAAG,GAAG,EAAE,GAAG,CAAC,CAAC;AAAlF,QAAA,OAAO,WAA2E;AACxF,MAAM,OAAO,GAAI,CAAC,GAAW,EAAE,GAA6B,EAAE,EAAE,CAAC,GAAG,CAAC,MAAM,EAAG,GAAG,EAAE,GAAG,CAAC,CAAC;AAAlF,QAAA,OAAO,WAA2E;AACxF,MAAM,QAAQ,GAAG,CAAC,GAAW,EAAE,GAA6B,EAAE,EAAE,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;AAAlF,QAAA,QAAQ,YAA0E;AAE/F;;;;GAIG;AACH,SAAgB,QAAQ,CACtB,GAAW,EACX,GAAY,EACZ,MAA+B,EAAE;IAEjC,GAAG,CAAC,OAAO,EAAE,GAAG,EAAE,EAAE,GAAG,GAAG,EAAE,KAAK,EAAE,IAAA,2BAAa,EAAC,GAAG,CAAC,EAAE,CAAC,CAAC;AAC3D,CAAC"}
package/dist/plans.d.ts CHANGED
@@ -4,9 +4,20 @@ export interface PlanLimits {
4
4
  agents: number;
5
5
  credentials: number;
6
6
  members: number;
7
+ policies: number;
7
8
  timelineHistoryDays: number;
8
9
  undoEnabled: boolean;
9
10
  browserSessions: number;
11
+ /**
12
+ * Maximum number of concurrent persistent SSH sessions (`ssh.open`).
13
+ * Free is held to 0 — those plans use one-shot `ssh.run` only. Pro and
14
+ * Team get a small concurrency budget. The TTL per session is enforced
15
+ * separately via {@link sshMaxTtlMinutes}.
16
+ */
17
+ sshSessions: number;
18
+ messagesPerMonth: number;
19
+ messageHistoryDays: number;
20
+ threadsPerAgent: number;
10
21
  }
11
22
  export interface PlanDefinition extends PlanLimits {
12
23
  id: PlanId;
@@ -19,6 +30,16 @@ export interface PlanDefinition extends PlanLimits {
19
30
  export declare const PLANS: Record<PlanId, PlanDefinition>;
20
31
  export declare function getPlanLimits(plan: string): PlanLimits;
21
32
  export declare function canUndo(plan: string): boolean;
33
+ /**
34
+ * Maximum SSH session TTL (minutes) allowed per plan.
35
+ *
36
+ * Longer TTLs widen the blast radius if a session is hijacked or forgotten,
37
+ * so we cap conservatively. Team gets the industry-standard "up to one
38
+ * hour" window; Free/Pro are held to the shorter 20-minute cap —
39
+ * agents that need longer operations should re-open a session via
40
+ * approval rather than keep one idling.
41
+ */
42
+ export declare function sshMaxTtlMinutes(plan: string): number;
22
43
  /** Map a Stripe price ID back to a plan ID */
23
44
  export declare function planFromPriceId(priceId: string): PlanId | null;
24
45
  //# sourceMappingURL=plans.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"plans.d.ts","sourceRoot":"","sources":["../src/plans.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,MAAM,GAAG,MAAM,GAAG,KAAK,GAAG,MAAM,CAAC;AAE7C,MAAM,WAAW,UAAU;IACzB,eAAe,EAAE,MAAM,CAAC;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,WAAW,EAAE,OAAO,CAAC;IACrB,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,cAAe,SAAQ,UAAU;IAChD,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,iBAAiB,EAAE,MAAM,CAAC;CAC3B;AAED,eAAO,MAAM,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CA8CvC,CAAC;AAEX,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,CAYtD;AAED,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAE7C;AAED,8CAA8C;AAC9C,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAO9D"}
1
+ {"version":3,"file":"plans.d.ts","sourceRoot":"","sources":["../src/plans.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,MAAM,GAAG,MAAM,GAAG,KAAK,GAAG,MAAM,CAAC;AAE7C,MAAM,WAAW,UAAU;IACzB,eAAe,EAAE,MAAM,CAAC;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,WAAW,EAAE,OAAO,CAAC;IACrB,eAAe,EAAE,MAAM,CAAC;IACxB;;;;;OAKG;IACH,WAAW,EAAE,MAAM,CAAC;IACpB,gBAAgB,EAAE,MAAM,CAAC;IACzB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,cAAe,SAAQ,UAAU;IAChD,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,iBAAiB,EAAE,MAAM,CAAC;CAC3B;AAED,eAAO,MAAM,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CA6DvC,CAAC;AAEX,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,CAgBtD;AAED,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAE7C;AAED;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CASrD;AAED,8CAA8C;AAC9C,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAO9D"}
package/dist/plans.js CHANGED
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.PLANS = void 0;
4
4
  exports.getPlanLimits = getPlanLimits;
5
5
  exports.canUndo = canUndo;
6
+ exports.sshMaxTtlMinutes = sshMaxTtlMinutes;
6
7
  exports.planFromPriceId = planFromPriceId;
7
8
  exports.PLANS = {
8
9
  free: {
@@ -12,28 +13,38 @@ exports.PLANS = {
12
13
  yearlyPrice: 0,
13
14
  stripePriceMonthly: '',
14
15
  stripePriceYearly: '',
15
- actionsPerMonth: 1000,
16
- agents: 3,
16
+ actionsPerMonth: 100,
17
+ agents: 1,
17
18
  credentials: 2,
18
19
  members: 1,
20
+ policies: 3,
19
21
  timelineHistoryDays: 14,
20
22
  undoEnabled: false,
21
23
  browserSessions: 0,
24
+ sshSessions: 0,
25
+ messagesPerMonth: 100,
26
+ messageHistoryDays: 7,
27
+ threadsPerAgent: 5,
22
28
  },
23
29
  pro: {
24
30
  id: 'pro',
25
31
  name: 'Pro',
26
32
  monthlyPrice: 900,
27
- yearlyPrice: 7900,
33
+ yearlyPrice: 7550,
28
34
  stripePriceMonthly: 'price_1T6b3m2NRlIkxMrBZ9bmEDYE',
29
- stripePriceYearly: 'price_1T6b3m2NRlIkxMrBo2yx01sJ',
30
- actionsPerMonth: Infinity,
31
- agents: 10,
32
- credentials: 25,
33
- members: 5,
35
+ stripePriceYearly: 'price_1TQTJy2NRlIkxMrBhcSh8E8i',
36
+ actionsPerMonth: 2000,
37
+ agents: 5,
38
+ credentials: 15,
39
+ members: 3,
40
+ policies: 10,
34
41
  timelineHistoryDays: 90,
35
42
  undoEnabled: true,
36
43
  browserSessions: 2,
44
+ sshSessions: 2,
45
+ messagesPerMonth: 5000,
46
+ messageHistoryDays: 30,
47
+ threadsPerAgent: 50,
37
48
  },
38
49
  team: {
39
50
  id: 'team',
@@ -42,32 +53,59 @@ exports.PLANS = {
42
53
  yearlyPrice: 41000,
43
54
  stripePriceMonthly: 'price_1T6b3n2NRlIkxMrBstkfVECC',
44
55
  stripePriceYearly: 'price_1T6b3o2NRlIkxMrBzHoQKPjz',
45
- actionsPerMonth: Infinity,
46
- agents: 50,
56
+ actionsPerMonth: 10000,
57
+ agents: 20,
47
58
  credentials: Infinity,
48
- members: 25,
59
+ members: 10,
60
+ policies: 50,
49
61
  timelineHistoryDays: 365,
50
62
  undoEnabled: true,
51
63
  browserSessions: 5,
64
+ sshSessions: 5,
65
+ messagesPerMonth: Infinity,
66
+ messageHistoryDays: 90,
67
+ threadsPerAgent: Infinity,
52
68
  },
53
69
  };
54
70
  function getPlanLimits(plan) {
55
- const def = exports.PLANS[plan];
56
- if (!def)
57
- return exports.PLANS.free;
71
+ const def = exports.PLANS[plan] ?? exports.PLANS.free;
58
72
  return {
59
73
  actionsPerMonth: def.actionsPerMonth,
60
74
  agents: def.agents,
61
75
  credentials: def.credentials,
62
76
  members: def.members,
77
+ policies: def.policies,
63
78
  timelineHistoryDays: def.timelineHistoryDays,
64
79
  undoEnabled: def.undoEnabled,
65
80
  browserSessions: def.browserSessions,
81
+ sshSessions: def.sshSessions,
82
+ messagesPerMonth: def.messagesPerMonth,
83
+ messageHistoryDays: def.messageHistoryDays,
84
+ threadsPerAgent: def.threadsPerAgent,
66
85
  };
67
86
  }
68
87
  function canUndo(plan) {
69
88
  return getPlanLimits(plan).undoEnabled;
70
89
  }
90
+ /**
91
+ * Maximum SSH session TTL (minutes) allowed per plan.
92
+ *
93
+ * Longer TTLs widen the blast radius if a session is hijacked or forgotten,
94
+ * so we cap conservatively. Team gets the industry-standard "up to one
95
+ * hour" window; Free/Pro are held to the shorter 20-minute cap —
96
+ * agents that need longer operations should re-open a session via
97
+ * approval rather than keep one idling.
98
+ */
99
+ function sshMaxTtlMinutes(plan) {
100
+ switch (plan) {
101
+ case 'team':
102
+ return 60;
103
+ case 'pro':
104
+ case 'free':
105
+ default:
106
+ return 20;
107
+ }
108
+ }
71
109
  /** Map a Stripe price ID back to a plan ID */
72
110
  function planFromPriceId(priceId) {
73
111
  for (const plan of Object.values(exports.PLANS)) {
package/dist/plans.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"plans.js","sourceRoot":"","sources":["../src/plans.ts"],"names":[],"mappings":";;;AAqEA,sCAYC;AAED,0BAEC;AAGD,0CAOC;AA1EY,QAAA,KAAK,GAAmC;IACnD,IAAI,EAAE;QACJ,EAAE,EAAE,MAAM;QACV,IAAI,EAAE,MAAM;QACZ,YAAY,EAAE,CAAC;QACf,WAAW,EAAE,CAAC;QACd,kBAAkB,EAAE,EAAE;QACtB,iBAAiB,EAAE,EAAE;QACrB,eAAe,EAAE,IAAI;QACrB,MAAM,EAAE,CAAC;QACT,WAAW,EAAE,CAAC;QACd,OAAO,EAAE,CAAC;QACV,mBAAmB,EAAE,EAAE;QACvB,WAAW,EAAE,KAAK;QAClB,eAAe,EAAE,CAAC;KACnB;IACD,GAAG,EAAE;QACH,EAAE,EAAE,KAAK;QACT,IAAI,EAAE,KAAK;QACX,YAAY,EAAE,GAAG;QACjB,WAAW,EAAE,IAAI;QACjB,kBAAkB,EAAE,gCAAgC;QACpD,iBAAiB,EAAE,gCAAgC;QACnD,eAAe,EAAE,QAAQ;QACzB,MAAM,EAAE,EAAE;QACV,WAAW,EAAE,EAAE;QACf,OAAO,EAAE,CAAC;QACV,mBAAmB,EAAE,EAAE;QACvB,WAAW,EAAE,IAAI;QACjB,eAAe,EAAE,CAAC;KACnB;IACD,IAAI,EAAE;QACJ,EAAE,EAAE,MAAM;QACV,IAAI,EAAE,MAAM;QACZ,YAAY,EAAE,IAAI;QAClB,WAAW,EAAE,KAAK;QAClB,kBAAkB,EAAE,gCAAgC;QACpD,iBAAiB,EAAE,gCAAgC;QACnD,eAAe,EAAE,QAAQ;QACzB,MAAM,EAAE,EAAE;QACV,WAAW,EAAE,QAAQ;QACrB,OAAO,EAAE,EAAE;QACX,mBAAmB,EAAE,GAAG;QACxB,WAAW,EAAE,IAAI;QACjB,eAAe,EAAE,CAAC;KACnB;CACO,CAAC;AAEX,SAAgB,aAAa,CAAC,IAAY;IACxC,MAAM,GAAG,GAAG,aAAK,CAAC,IAAc,CAAC,CAAC;IAClC,IAAI,CAAC,GAAG;QAAE,OAAO,aAAK,CAAC,IAAI,CAAC;IAC5B,OAAO;QACL,eAAe,EAAE,GAAG,CAAC,eAAe;QACpC,MAAM,EAAE,GAAG,CAAC,MAAM;QAClB,WAAW,EAAE,GAAG,CAAC,WAAW;QAC5B,OAAO,EAAE,GAAG,CAAC,OAAO;QACpB,mBAAmB,EAAE,GAAG,CAAC,mBAAmB;QAC5C,WAAW,EAAE,GAAG,CAAC,WAAW;QAC5B,eAAe,EAAE,GAAG,CAAC,eAAe;KACrC,CAAC;AACJ,CAAC;AAED,SAAgB,OAAO,CAAC,IAAY;IAClC,OAAO,aAAa,CAAC,IAAI,CAAC,CAAC,WAAW,CAAC;AACzC,CAAC;AAED,8CAA8C;AAC9C,SAAgB,eAAe,CAAC,OAAe;IAC7C,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,MAAM,CAAC,aAAK,CAAC,EAAE,CAAC;QACxC,IAAI,IAAI,CAAC,kBAAkB,KAAK,OAAO,IAAI,IAAI,CAAC,iBAAiB,KAAK,OAAO,EAAE,CAAC;YAC9E,OAAO,IAAI,CAAC,EAAE,CAAC;QACjB,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
1
+ {"version":3,"file":"plans.js","sourceRoot":"","sources":["../src/plans.ts"],"names":[],"mappings":"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"}