@mondaydotcomorg/atp-server 0.17.14

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 (307) hide show
  1. package/README.md +489 -0
  2. package/dist/aggregator/index.d.ts +59 -0
  3. package/dist/aggregator/index.d.ts.map +1 -0
  4. package/dist/aggregator/index.js +171 -0
  5. package/dist/aggregator/index.js.map +1 -0
  6. package/dist/callback/index.d.ts +98 -0
  7. package/dist/callback/index.d.ts.map +1 -0
  8. package/dist/callback/index.js +136 -0
  9. package/dist/callback/index.js.map +1 -0
  10. package/dist/client-sessions.d.ts +82 -0
  11. package/dist/client-sessions.d.ts.map +1 -0
  12. package/dist/client-sessions.js +174 -0
  13. package/dist/client-sessions.js.map +1 -0
  14. package/dist/controllers/definitions.controller.d.ts +4 -0
  15. package/dist/controllers/definitions.controller.d.ts.map +1 -0
  16. package/dist/controllers/definitions.controller.js +11 -0
  17. package/dist/controllers/definitions.controller.js.map +1 -0
  18. package/dist/controllers/execute.controller.d.ts +18 -0
  19. package/dist/controllers/execute.controller.d.ts.map +1 -0
  20. package/dist/controllers/execute.controller.js +122 -0
  21. package/dist/controllers/execute.controller.js.map +1 -0
  22. package/dist/controllers/info.controller.d.ts +3 -0
  23. package/dist/controllers/info.controller.d.ts.map +1 -0
  24. package/dist/controllers/info.controller.js +13 -0
  25. package/dist/controllers/info.controller.js.map +1 -0
  26. package/dist/controllers/resume.controller.d.ts +11 -0
  27. package/dist/controllers/resume.controller.d.ts.map +1 -0
  28. package/dist/controllers/resume.controller.js +61 -0
  29. package/dist/controllers/resume.controller.js.map +1 -0
  30. package/dist/controllers/search.controller.d.ts +4 -0
  31. package/dist/controllers/search.controller.d.ts.map +1 -0
  32. package/dist/controllers/search.controller.js +7 -0
  33. package/dist/controllers/search.controller.js.map +1 -0
  34. package/dist/controllers/stream.controller.d.ts +19 -0
  35. package/dist/controllers/stream.controller.d.ts.map +1 -0
  36. package/dist/controllers/stream.controller.js +141 -0
  37. package/dist/controllers/stream.controller.js.map +1 -0
  38. package/dist/core/config.d.ts +161 -0
  39. package/dist/core/config.d.ts.map +1 -0
  40. package/dist/core/config.js +7 -0
  41. package/dist/core/config.js.map +1 -0
  42. package/dist/core/http.d.ts +4 -0
  43. package/dist/core/http.d.ts.map +1 -0
  44. package/dist/core/http.js +17 -0
  45. package/dist/core/http.js.map +1 -0
  46. package/dist/create-server.d.ts +120 -0
  47. package/dist/create-server.d.ts.map +1 -0
  48. package/dist/create-server.js +423 -0
  49. package/dist/create-server.js.map +1 -0
  50. package/dist/execution-state/index.d.ts +95 -0
  51. package/dist/execution-state/index.d.ts.map +1 -0
  52. package/dist/execution-state/index.js +128 -0
  53. package/dist/execution-state/index.js.map +1 -0
  54. package/dist/executor/ast-provenance-bridge.d.ts +12 -0
  55. package/dist/executor/ast-provenance-bridge.d.ts.map +1 -0
  56. package/dist/executor/ast-provenance-bridge.js +66 -0
  57. package/dist/executor/ast-provenance-bridge.js.map +1 -0
  58. package/dist/executor/ast-tracking-runtime.d.ts +7 -0
  59. package/dist/executor/ast-tracking-runtime.d.ts.map +1 -0
  60. package/dist/executor/ast-tracking-runtime.js +559 -0
  61. package/dist/executor/ast-tracking-runtime.js.map +1 -0
  62. package/dist/executor/bootstrap-generated.d.ts +32 -0
  63. package/dist/executor/bootstrap-generated.d.ts.map +1 -0
  64. package/dist/executor/bootstrap-generated.js +90 -0
  65. package/dist/executor/bootstrap-generated.js.map +1 -0
  66. package/dist/executor/compiler-config.d.ts +32 -0
  67. package/dist/executor/compiler-config.d.ts.map +1 -0
  68. package/dist/executor/compiler-config.js +99 -0
  69. package/dist/executor/compiler-config.js.map +1 -0
  70. package/dist/executor/constants.d.ts +4 -0
  71. package/dist/executor/constants.d.ts.map +1 -0
  72. package/dist/executor/constants.js +4 -0
  73. package/dist/executor/constants.js.map +1 -0
  74. package/dist/executor/error-handler.d.ts +9 -0
  75. package/dist/executor/error-handler.d.ts.map +1 -0
  76. package/dist/executor/error-handler.js +95 -0
  77. package/dist/executor/error-handler.js.map +1 -0
  78. package/dist/executor/execution-error-handler.d.ts +7 -0
  79. package/dist/executor/execution-error-handler.d.ts.map +1 -0
  80. package/dist/executor/execution-error-handler.js +136 -0
  81. package/dist/executor/execution-error-handler.js.map +1 -0
  82. package/dist/executor/executor.d.ts +20 -0
  83. package/dist/executor/executor.d.ts.map +1 -0
  84. package/dist/executor/executor.js +452 -0
  85. package/dist/executor/executor.js.map +1 -0
  86. package/dist/executor/index.d.ts +4 -0
  87. package/dist/executor/index.d.ts.map +1 -0
  88. package/dist/executor/index.js +3 -0
  89. package/dist/executor/index.js.map +1 -0
  90. package/dist/executor/resume-handler.d.ts +9 -0
  91. package/dist/executor/resume-handler.d.ts.map +1 -0
  92. package/dist/executor/resume-handler.js +22 -0
  93. package/dist/executor/resume-handler.js.map +1 -0
  94. package/dist/executor/sandbox-builder.d.ts +29 -0
  95. package/dist/executor/sandbox-builder.d.ts.map +1 -0
  96. package/dist/executor/sandbox-builder.js +538 -0
  97. package/dist/executor/sandbox-builder.js.map +1 -0
  98. package/dist/executor/sandbox-injector.d.ts +7 -0
  99. package/dist/executor/sandbox-injector.d.ts.map +1 -0
  100. package/dist/executor/sandbox-injector.js +293 -0
  101. package/dist/executor/sandbox-injector.js.map +1 -0
  102. package/dist/executor/types.d.ts +21 -0
  103. package/dist/executor/types.d.ts.map +1 -0
  104. package/dist/executor/types.js +2 -0
  105. package/dist/executor/types.js.map +1 -0
  106. package/dist/explorer/index.d.ts +69 -0
  107. package/dist/explorer/index.d.ts.map +1 -0
  108. package/dist/explorer/index.js +228 -0
  109. package/dist/explorer/index.js.map +1 -0
  110. package/dist/handlers/definitions.handler.d.ts +3 -0
  111. package/dist/handlers/definitions.handler.d.ts.map +1 -0
  112. package/dist/handlers/definitions.handler.js +11 -0
  113. package/dist/handlers/definitions.handler.js.map +1 -0
  114. package/dist/handlers/execute.handler.d.ts +7 -0
  115. package/dist/handlers/execute.handler.d.ts.map +1 -0
  116. package/dist/handlers/execute.handler.js +225 -0
  117. package/dist/handlers/execute.handler.js.map +1 -0
  118. package/dist/handlers/explorer.handler.d.ts +4 -0
  119. package/dist/handlers/explorer.handler.d.ts.map +1 -0
  120. package/dist/handlers/explorer.handler.js +10 -0
  121. package/dist/handlers/explorer.handler.js.map +1 -0
  122. package/dist/handlers/init.handler.d.ts +5 -0
  123. package/dist/handlers/init.handler.d.ts.map +1 -0
  124. package/dist/handlers/init.handler.js +41 -0
  125. package/dist/handlers/init.handler.js.map +1 -0
  126. package/dist/handlers/resume.handler.d.ts +6 -0
  127. package/dist/handlers/resume.handler.d.ts.map +1 -0
  128. package/dist/handlers/resume.handler.js +256 -0
  129. package/dist/handlers/resume.handler.js.map +1 -0
  130. package/dist/handlers/search.handler.d.ts +5 -0
  131. package/dist/handlers/search.handler.d.ts.map +1 -0
  132. package/dist/handlers/search.handler.js +11 -0
  133. package/dist/handlers/search.handler.js.map +1 -0
  134. package/dist/http/request-handler.d.ts +15 -0
  135. package/dist/http/request-handler.d.ts.map +1 -0
  136. package/dist/http/request-handler.js +94 -0
  137. package/dist/http/request-handler.js.map +1 -0
  138. package/dist/http/router.d.ts +4 -0
  139. package/dist/http/router.d.ts.map +1 -0
  140. package/dist/http/router.js +32 -0
  141. package/dist/http/router.js.map +1 -0
  142. package/dist/index.d.ts +10 -0
  143. package/dist/index.d.ts.map +1 -0
  144. package/dist/index.js +8 -0
  145. package/dist/index.js.map +1 -0
  146. package/dist/instrumentation/index.d.ts +5 -0
  147. package/dist/instrumentation/index.d.ts.map +1 -0
  148. package/dist/instrumentation/index.js +5 -0
  149. package/dist/instrumentation/index.js.map +1 -0
  150. package/dist/instrumentation/serializer.d.ts +61 -0
  151. package/dist/instrumentation/serializer.d.ts.map +1 -0
  152. package/dist/instrumentation/serializer.js +334 -0
  153. package/dist/instrumentation/serializer.js.map +1 -0
  154. package/dist/instrumentation/state-manager.d.ts +61 -0
  155. package/dist/instrumentation/state-manager.d.ts.map +1 -0
  156. package/dist/instrumentation/state-manager.js +205 -0
  157. package/dist/instrumentation/state-manager.js.map +1 -0
  158. package/dist/instrumentation/transformer.d.ts +9 -0
  159. package/dist/instrumentation/transformer.d.ts.map +1 -0
  160. package/dist/instrumentation/transformer.js +70 -0
  161. package/dist/instrumentation/transformer.js.map +1 -0
  162. package/dist/instrumentation/types.d.ts +59 -0
  163. package/dist/instrumentation/types.d.ts.map +1 -0
  164. package/dist/instrumentation/types.js +5 -0
  165. package/dist/instrumentation/types.js.map +1 -0
  166. package/dist/middleware/audit.d.ts +18 -0
  167. package/dist/middleware/audit.d.ts.map +1 -0
  168. package/dist/middleware/audit.js +76 -0
  169. package/dist/middleware/audit.js.map +1 -0
  170. package/dist/openapi/index.d.ts +133 -0
  171. package/dist/openapi/index.d.ts.map +1 -0
  172. package/dist/openapi/index.js +235 -0
  173. package/dist/openapi/index.js.map +1 -0
  174. package/dist/openapi-loader.d.ts +87 -0
  175. package/dist/openapi-loader.d.ts.map +1 -0
  176. package/dist/openapi-loader.js +491 -0
  177. package/dist/openapi-loader.js.map +1 -0
  178. package/dist/routes/index.d.ts +21 -0
  179. package/dist/routes/index.d.ts.map +1 -0
  180. package/dist/routes/index.js +47 -0
  181. package/dist/routes/index.js.map +1 -0
  182. package/dist/search/index.d.ts +48 -0
  183. package/dist/search/index.d.ts.map +1 -0
  184. package/dist/search/index.js +156 -0
  185. package/dist/search/index.js.map +1 -0
  186. package/dist/security/index.d.ts +2 -0
  187. package/dist/security/index.d.ts.map +1 -0
  188. package/dist/security/index.js +2 -0
  189. package/dist/security/index.js.map +1 -0
  190. package/dist/shutdown.d.ts +19 -0
  191. package/dist/shutdown.d.ts.map +1 -0
  192. package/dist/shutdown.js +87 -0
  193. package/dist/shutdown.js.map +1 -0
  194. package/dist/utils/banner.d.ts +12 -0
  195. package/dist/utils/banner.d.ts.map +1 -0
  196. package/dist/utils/banner.js +18 -0
  197. package/dist/utils/banner.js.map +1 -0
  198. package/dist/utils/context.d.ts +16 -0
  199. package/dist/utils/context.d.ts.map +1 -0
  200. package/dist/utils/context.js +44 -0
  201. package/dist/utils/context.js.map +1 -0
  202. package/dist/utils/error.d.ts +8 -0
  203. package/dist/utils/error.d.ts.map +1 -0
  204. package/dist/utils/error.js +17 -0
  205. package/dist/utils/error.js.map +1 -0
  206. package/dist/utils/hint-based-instrumentation.d.ts +14 -0
  207. package/dist/utils/hint-based-instrumentation.d.ts.map +1 -0
  208. package/dist/utils/hint-based-instrumentation.js +84 -0
  209. package/dist/utils/hint-based-instrumentation.js.map +1 -0
  210. package/dist/utils/index.d.ts +8 -0
  211. package/dist/utils/index.d.ts.map +1 -0
  212. package/dist/utils/index.js +8 -0
  213. package/dist/utils/index.js.map +1 -0
  214. package/dist/utils/info.d.ts +20 -0
  215. package/dist/utils/info.d.ts.map +1 -0
  216. package/dist/utils/info.js +15 -0
  217. package/dist/utils/info.js.map +1 -0
  218. package/dist/utils/provenance-reattachment.d.ts +32 -0
  219. package/dist/utils/provenance-reattachment.d.ts.map +1 -0
  220. package/dist/utils/provenance-reattachment.js +115 -0
  221. package/dist/utils/provenance-reattachment.js.map +1 -0
  222. package/dist/utils/request.d.ts +21 -0
  223. package/dist/utils/request.d.ts.map +1 -0
  224. package/dist/utils/request.js +44 -0
  225. package/dist/utils/request.js.map +1 -0
  226. package/dist/utils/response.d.ts +30 -0
  227. package/dist/utils/response.d.ts.map +1 -0
  228. package/dist/utils/response.js +53 -0
  229. package/dist/utils/response.js.map +1 -0
  230. package/dist/utils/runtime-types.d.ts +6 -0
  231. package/dist/utils/runtime-types.d.ts.map +1 -0
  232. package/dist/utils/runtime-types.js +14 -0
  233. package/dist/utils/runtime-types.js.map +1 -0
  234. package/dist/utils/schema.d.ts +9 -0
  235. package/dist/utils/schema.d.ts.map +1 -0
  236. package/dist/utils/schema.js +13 -0
  237. package/dist/utils/schema.js.map +1 -0
  238. package/dist/utils/token-emitter.d.ts +21 -0
  239. package/dist/utils/token-emitter.d.ts.map +1 -0
  240. package/dist/utils/token-emitter.js +129 -0
  241. package/dist/utils/token-emitter.js.map +1 -0
  242. package/dist/validator/index.d.ts +36 -0
  243. package/dist/validator/index.d.ts.map +1 -0
  244. package/dist/validator/index.js +224 -0
  245. package/dist/validator/index.js.map +1 -0
  246. package/package.json +68 -0
  247. package/src/aggregator/index.ts +207 -0
  248. package/src/callback/index.ts +191 -0
  249. package/src/client-sessions.ts +234 -0
  250. package/src/controllers/definitions.controller.ts +19 -0
  251. package/src/controllers/execute.controller.ts +166 -0
  252. package/src/controllers/info.controller.ts +14 -0
  253. package/src/controllers/resume.controller.ts +92 -0
  254. package/src/controllers/search.controller.ts +16 -0
  255. package/src/controllers/stream.controller.ts +190 -0
  256. package/src/core/config.ts +180 -0
  257. package/src/core/http.ts +21 -0
  258. package/src/create-server.ts +536 -0
  259. package/src/execution-state/index.ts +204 -0
  260. package/src/executor/ast-provenance-bridge.ts +80 -0
  261. package/src/executor/ast-tracking-runtime.ts +558 -0
  262. package/src/executor/bootstrap-generated.ts +90 -0
  263. package/src/executor/compiler-config.ts +146 -0
  264. package/src/executor/constants.ts +5 -0
  265. package/src/executor/error-handler.ts +118 -0
  266. package/src/executor/execution-error-handler.ts +178 -0
  267. package/src/executor/executor.ts +631 -0
  268. package/src/executor/index.ts +3 -0
  269. package/src/executor/resume-handler.ts +39 -0
  270. package/src/executor/sandbox-builder.ts +684 -0
  271. package/src/executor/sandbox-injector.ts +345 -0
  272. package/src/executor/types.ts +22 -0
  273. package/src/explorer/index.ts +297 -0
  274. package/src/handlers/definitions.handler.ts +13 -0
  275. package/src/handlers/execute.handler.ts +286 -0
  276. package/src/handlers/explorer.handler.ts +18 -0
  277. package/src/handlers/init.handler.ts +53 -0
  278. package/src/handlers/resume.handler.ts +316 -0
  279. package/src/handlers/search.handler.ts +32 -0
  280. package/src/http/request-handler.ts +117 -0
  281. package/src/http/router.ts +29 -0
  282. package/src/index.ts +60 -0
  283. package/src/instrumentation/index.ts +4 -0
  284. package/src/instrumentation/serializer.ts +421 -0
  285. package/src/instrumentation/state-manager.ts +237 -0
  286. package/src/instrumentation/transformer.ts +84 -0
  287. package/src/instrumentation/types.ts +76 -0
  288. package/src/middleware/audit.ts +101 -0
  289. package/src/openapi/index.ts +378 -0
  290. package/src/openapi-loader.ts +744 -0
  291. package/src/routes/index.ts +93 -0
  292. package/src/search/index.ts +216 -0
  293. package/src/security/index.ts +1 -0
  294. package/src/shutdown.ts +108 -0
  295. package/src/utils/banner.ts +25 -0
  296. package/src/utils/context.ts +58 -0
  297. package/src/utils/error.ts +25 -0
  298. package/src/utils/hint-based-instrumentation.ts +99 -0
  299. package/src/utils/index.ts +15 -0
  300. package/src/utils/info.ts +31 -0
  301. package/src/utils/provenance-reattachment.ts +144 -0
  302. package/src/utils/request.ts +53 -0
  303. package/src/utils/response.ts +69 -0
  304. package/src/utils/runtime-types.ts +14 -0
  305. package/src/utils/schema.ts +18 -0
  306. package/src/utils/token-emitter.ts +182 -0
  307. package/src/validator/index.ts +253 -0
@@ -0,0 +1,684 @@
1
+ import type {
2
+ ExecutionConfig,
3
+ APIGroupConfig,
4
+ ClientToolDefinition,
5
+ ToolMetadata,
6
+ } from '@mondaydotcomorg/atp-protocol';
7
+ import {
8
+ ToolOperationType,
9
+ ToolSensitivityLevel,
10
+ ProvenanceMode,
11
+ CallbackType,
12
+ ToolOperation,
13
+ } from '@mondaydotcomorg/atp-protocol';
14
+ import {
15
+ llm,
16
+ cache,
17
+ log,
18
+ approval,
19
+ embedding,
20
+ setCurrentExecutionId,
21
+ clearCurrentExecutionId,
22
+ setVectorStoreExecutionId,
23
+ clearVectorStoreExecutionId,
24
+ isPauseError,
25
+ pauseForCallback,
26
+ nextSequenceNumber,
27
+ getCachedResult,
28
+ } from '@mondaydotcomorg/atp-runtime';
29
+ import type { RuntimeContext } from './types.js';
30
+ import {
31
+ createProvenanceProxy,
32
+ getProvenance,
33
+ SecurityPolicyEngine,
34
+ ProvenanceSource,
35
+ registerProvenanceMetadata,
36
+ } from '@mondaydotcomorg/atp-provenance';
37
+ import { ReaderPermissions } from '@mondaydotcomorg/atp-server';
38
+ import { getHintMap, reattachProvenanceFromHints } from '../utils/provenance-reattachment.js';
39
+ import { createASTProvenanceChecker } from './ast-provenance-bridge.js';
40
+
41
+ export class SandboxBuilder {
42
+ private policyEngine: SecurityPolicyEngine | null = null;
43
+
44
+ constructor(private apiGroups: APIGroupConfig[]) {}
45
+
46
+ createSandbox(
47
+ context: RuntimeContext,
48
+ config: ExecutionConfig,
49
+ logger: ReturnType<typeof log.child>,
50
+ executionId: string,
51
+ policyEngine?: SecurityPolicyEngine,
52
+ clientTools?: ClientToolDefinition[]
53
+ ): Record<string, unknown> {
54
+ this.policyEngine = policyEngine || null;
55
+ const clientId = context.clientId || 'default';
56
+
57
+ const sandbox: Record<string, unknown> = {
58
+ module: { exports: {} },
59
+ exports: {},
60
+ require: () => {
61
+ throw new Error('require() is not allowed in sandbox');
62
+ },
63
+
64
+ atp: {
65
+ approval: {
66
+ request: async (message: string, approvalContext?: Record<string, unknown>) => {
67
+ context.approvalCallCount++;
68
+ logger.debug('Approval request from sandbox', { message });
69
+ setCurrentExecutionId(executionId);
70
+ try {
71
+ return await approval.request(message, approvalContext);
72
+ } finally {
73
+ clearCurrentExecutionId();
74
+ }
75
+ },
76
+ },
77
+ llm: {
78
+ call: async (options: {
79
+ prompt: string;
80
+ context?: Record<string, unknown>;
81
+ model?: string;
82
+ temperature?: number;
83
+ systemPrompt?: string;
84
+ }) => {
85
+ if (!config.allowLLMCalls) {
86
+ throw new Error('LLM calls are not allowed in this execution');
87
+ }
88
+ if (++context.llmCallCount > config.maxLLMCalls) {
89
+ throw new Error(`Exceeded max LLM calls: ${config.maxLLMCalls}`);
90
+ }
91
+ logger.debug('LLM call from sandbox', {
92
+ promptLength: options.prompt.length,
93
+ model: options.model,
94
+ });
95
+
96
+ if (config.customLLMHandler) {
97
+ return await config.customLLMHandler(options.prompt, options);
98
+ }
99
+
100
+ setCurrentExecutionId(executionId);
101
+ try {
102
+ return await llm.call(options);
103
+ } finally {
104
+ clearCurrentExecutionId();
105
+ }
106
+ },
107
+ extract: async (options: {
108
+ prompt: string;
109
+ schema: unknown;
110
+ context?: Record<string, unknown>;
111
+ }) => {
112
+ if (!config.allowLLMCalls) {
113
+ throw new Error('LLM calls are not allowed in this execution');
114
+ }
115
+ if (++context.llmCallCount > config.maxLLMCalls) {
116
+ throw new Error(`Exceeded max LLM calls: ${config.maxLLMCalls}`);
117
+ }
118
+ logger.debug('LLM extract from sandbox');
119
+ setCurrentExecutionId(executionId);
120
+ try {
121
+ return await llm.extract(options);
122
+ } finally {
123
+ clearCurrentExecutionId();
124
+ }
125
+ },
126
+ classify: async (options: {
127
+ text: string;
128
+ categories: string[];
129
+ context?: Record<string, unknown>;
130
+ }) => {
131
+ if (!config.allowLLMCalls) {
132
+ throw new Error('LLM calls are not allowed in this execution');
133
+ }
134
+ if (++context.llmCallCount > config.maxLLMCalls) {
135
+ throw new Error(`Exceeded max LLM calls: ${config.maxLLMCalls}`);
136
+ }
137
+ logger.debug('LLM classify from sandbox');
138
+ setCurrentExecutionId(executionId);
139
+ try {
140
+ return await llm.classify(options);
141
+ } finally {
142
+ clearCurrentExecutionId();
143
+ }
144
+ },
145
+ },
146
+ cache: {
147
+ get: async (key: string) => {
148
+ const scopedKey = `client:${clientId}:cache:${key}`;
149
+ setCurrentExecutionId(executionId);
150
+ try {
151
+ return await cache.get(scopedKey);
152
+ } finally {
153
+ clearCurrentExecutionId();
154
+ }
155
+ },
156
+ set: async (key: string, value: unknown, ttl?: number) => {
157
+ const scopedKey = `client:${clientId}:cache:${key}`;
158
+ setCurrentExecutionId(executionId);
159
+ try {
160
+ return await cache.set(scopedKey, value, ttl);
161
+ } finally {
162
+ clearCurrentExecutionId();
163
+ }
164
+ },
165
+ delete: async (key: string) => {
166
+ const scopedKey = `client:${clientId}:cache:${key}`;
167
+ setCurrentExecutionId(executionId);
168
+ try {
169
+ return await cache.delete(scopedKey);
170
+ } finally {
171
+ clearCurrentExecutionId();
172
+ }
173
+ },
174
+ has: async (key: string) => {
175
+ const scopedKey = `client:${clientId}:cache:${key}`;
176
+ setCurrentExecutionId(executionId);
177
+ try {
178
+ return await cache.has(scopedKey);
179
+ } finally {
180
+ clearCurrentExecutionId();
181
+ }
182
+ },
183
+ },
184
+ embedding: {
185
+ embed: async (text: string, options?: Record<string, unknown>) => {
186
+ logger.debug('Embedding request from sandbox', { textLength: text.length });
187
+ setCurrentExecutionId(executionId);
188
+ setVectorStoreExecutionId(executionId);
189
+ try {
190
+ return await embedding.embed(text, options);
191
+ } finally {
192
+ clearCurrentExecutionId();
193
+ clearVectorStoreExecutionId();
194
+ }
195
+ },
196
+ search: async (query: string, options?: Record<string, unknown>) => {
197
+ logger.debug('Embedding search from sandbox', { query });
198
+ setCurrentExecutionId(executionId);
199
+ setVectorStoreExecutionId(executionId);
200
+ try {
201
+ return await embedding.search(query, options);
202
+ } finally {
203
+ clearCurrentExecutionId();
204
+ clearVectorStoreExecutionId();
205
+ }
206
+ },
207
+ },
208
+ progress: {
209
+ report: (message: string, fraction: number) => {
210
+ logger.debug('Progress report from sandbox', { message, fraction });
211
+ if (config.progressCallback) {
212
+ config.progressCallback(message, fraction);
213
+ }
214
+ },
215
+ },
216
+ },
217
+
218
+ api: this.createAPIFunctionsWithClientTools(logger, executionId, config, clientTools),
219
+
220
+ console: {
221
+ log: (...args: unknown[]) => {
222
+ const message = args.join(' ');
223
+ logger.debug(`[Sandbox console] ${message}`);
224
+ context.logs.push(`LOG: ${message}`);
225
+ },
226
+ error: (...args: unknown[]) => {
227
+ const message = args.join(' ');
228
+ logger.error(`[Sandbox console] ${message}`);
229
+ context.logs.push(`ERROR: ${message}`);
230
+ },
231
+ warn: (...args: unknown[]) => {
232
+ const message = args.join(' ');
233
+ logger.warn(`[Sandbox console] ${message}`);
234
+ context.logs.push(`WARN: ${message}`);
235
+ },
236
+ },
237
+
238
+ JSON: JSON,
239
+ Math: Math,
240
+ Date: Date,
241
+ Array: Array,
242
+ Object: Object,
243
+ String: String,
244
+ Number: Number,
245
+ Boolean: Boolean,
246
+ Promise: Promise,
247
+ setTimeout: setTimeout,
248
+ setInterval: setInterval,
249
+ clearTimeout: clearTimeout,
250
+ clearInterval: clearInterval,
251
+ };
252
+
253
+ if (
254
+ config.provenanceMode === ProvenanceMode.PROXY ||
255
+ config.provenanceMode === ProvenanceMode.AST
256
+ ) {
257
+ sandbox.__getProvenance = getProvenance;
258
+ }
259
+
260
+ return sandbox;
261
+ }
262
+
263
+ private createAPIFunctions(
264
+ logger: ReturnType<typeof log.child>,
265
+ executionId: string,
266
+ config: ExecutionConfig
267
+ ): Record<string, unknown> {
268
+ const api: Record<string, unknown> = {};
269
+
270
+ for (const group of this.apiGroups) {
271
+ if (group.functions) {
272
+ const groupObj = this.getOrCreateNestedGroup(api, group.name);
273
+
274
+ for (const func of group.functions) {
275
+ const handler = func.handler;
276
+ const metadata = func.metadata;
277
+
278
+ groupObj[func.name] = async (input: unknown) => {
279
+ logger.info(`API function called: ${group.name}.${func.name}`, {
280
+ inputType: typeof input,
281
+ hasMetadata: !!metadata,
282
+ provenanceMode: config.provenanceMode || ProvenanceMode.NONE,
283
+ inputKeys: input && typeof input === 'object' ? Object.keys(input) : [],
284
+ inputPreview: JSON.stringify(input)?.substring(0, 200) || 'undefined',
285
+ });
286
+
287
+ // In AST mode, recursively unwrap tainted primitives and register their provenance
288
+ if (
289
+ config.provenanceMode === ProvenanceMode.AST &&
290
+ input &&
291
+ typeof input === 'object'
292
+ ) {
293
+ logger.info('Checking for tainted values to unwrap', {
294
+ tool: func.name,
295
+ inputKeys: Object.keys(input),
296
+ });
297
+
298
+ function unwrapTaintedValues(obj: any, visited = new WeakSet<object>()): any {
299
+ if (obj === null || obj === undefined) return obj;
300
+
301
+ // Check if this is a wrapped tainted primitive
302
+ if (typeof obj === 'object' && '__tainted_value' in obj && '__prov_meta' in obj) {
303
+ const taintedVal = obj.__tainted_value;
304
+ const provMeta = obj.__prov_meta;
305
+
306
+ logger.info('FOUND wrapped tainted value!', {
307
+ taintedValType: typeof taintedVal,
308
+ taintedValIsString: typeof taintedVal === 'string',
309
+ taintedValIsNumber: typeof taintedVal === 'number',
310
+ taintedValIsObject: typeof taintedVal === 'object',
311
+ valuePreview:
312
+ typeof taintedVal === 'string' || typeof taintedVal === 'number'
313
+ ? String(taintedVal).substring(0, 30)
314
+ : '[OBJECT: ' + Object.keys(taintedVal || {}).join(',') + ']',
315
+ hasProvMeta: !!provMeta,
316
+ });
317
+
318
+ // Register the provenance so host-side checks can find it
319
+ if (provMeta && provMeta.source) {
320
+ registerProvenanceMetadata(
321
+ `tainted:${String(taintedVal)}`,
322
+ {
323
+ id: `tainted:${String(taintedVal)}`,
324
+ source: provMeta.source,
325
+ readers: provMeta.readers || { type: 'public' },
326
+ dependencies: provMeta.deps || provMeta.dependencies || [],
327
+ },
328
+ executionId
329
+ );
330
+ logger.info('Unwrapped and registered tainted primitive', {
331
+ tool: func.name,
332
+ valuePreview: String(taintedVal).substring(0, 30),
333
+ source: provMeta.source?.type,
334
+ executionId: executionId,
335
+ });
336
+ }
337
+
338
+ // Recursively unwrap in case taintedVal contains more wrapped values
339
+ return unwrapTaintedValues(taintedVal, visited);
340
+ }
341
+
342
+ // Recursively unwrap objects/arrays
343
+ if (typeof obj === 'object') {
344
+ if (visited.has(obj)) return obj;
345
+ visited.add(obj);
346
+
347
+ if (Array.isArray(obj)) {
348
+ return obj.map((item) => unwrapTaintedValues(item, visited));
349
+ } else {
350
+ const unwrapped: Record<string, unknown> = {};
351
+ for (const [key, value] of Object.entries(obj)) {
352
+ unwrapped[key] = unwrapTaintedValues(value, visited);
353
+ }
354
+ return unwrapped;
355
+ }
356
+ }
357
+
358
+ return obj;
359
+ }
360
+
361
+ input = unwrapTaintedValues(input);
362
+ logger.info('After unwrapping', {
363
+ tool: func.name,
364
+ inputPreview: JSON.stringify(input).substring(0, 200),
365
+ });
366
+ }
367
+
368
+ // Re-attach provenance from hints before policy checks
369
+ const hintMap = getHintMap(executionId);
370
+ if (hintMap && hintMap.size > 0 && input && typeof input === 'object') {
371
+ try {
372
+ reattachProvenanceFromHints(input as Record<string, unknown>, hintMap);
373
+ logger.debug('Provenance re-attached from hints', {
374
+ tool: func.name,
375
+ group: group.name,
376
+ hintsAvailable: hintMap.size,
377
+ });
378
+ } catch (error) {
379
+ logger.warn('Failed to re-attach provenance from hints', { error });
380
+ }
381
+ }
382
+
383
+ if (
384
+ this.policyEngine &&
385
+ config.provenanceMode &&
386
+ config.provenanceMode !== ProvenanceMode.NONE
387
+ ) {
388
+ logger.debug('Checking security policies', {
389
+ tool: func.name,
390
+ group: group.name,
391
+ hasPolicyEngine: !!this.policyEngine,
392
+ provenanceMode: config.provenanceMode,
393
+ });
394
+
395
+ try {
396
+ await this.policyEngine.checkTool(
397
+ func.name,
398
+ group.name,
399
+ input as Record<string, unknown>
400
+ );
401
+ logger.debug('Security policies passed', { tool: func.name, group: group.name });
402
+ } catch (error) {
403
+ logger.error('Security policy denied tool execution', {
404
+ tool: func.name,
405
+ group: group.name,
406
+ error: error instanceof Error ? error.message : String(error),
407
+ });
408
+ throw error;
409
+ }
410
+ }
411
+
412
+ const isDestructive = metadata?.operationType === ToolOperationType.DESTRUCTIVE;
413
+ const isSensitive = metadata?.sensitivityLevel === ToolSensitivityLevel.SENSITIVE;
414
+ const needsApproval = metadata?.requiresApproval || isDestructive || isSensitive;
415
+
416
+ if (needsApproval) {
417
+ let operationDescription = 'operation';
418
+ if (isDestructive) operationDescription = 'destructive operation';
419
+ else if (isSensitive) operationDescription = 'sensitive operation';
420
+
421
+ const approvalMessage = `Approve ${operationDescription}: ${func.name}`;
422
+
423
+ setCurrentExecutionId(executionId);
424
+ try {
425
+ const approvalResult = await approval.request(approvalMessage, {
426
+ tool: func.name,
427
+ group: group.name,
428
+ params: input,
429
+ metadata: metadata,
430
+ });
431
+
432
+ if (!approvalResult || !approvalResult.approved) {
433
+ throw new Error(`Operation ${func.name} denied by user`);
434
+ }
435
+
436
+ logger.info(`Tool approved by user: ${group.name}.${func.name}`, {
437
+ operationType: metadata?.operationType,
438
+ sensitivityLevel: metadata?.sensitivityLevel,
439
+ });
440
+ } catch (error) {
441
+ clearCurrentExecutionId();
442
+ if (isPauseError(error)) {
443
+ throw error;
444
+ }
445
+ throw error;
446
+ }
447
+ clearCurrentExecutionId();
448
+ }
449
+
450
+ const result = await handler(input);
451
+
452
+ if (config.provenanceMode === ProvenanceMode.PROXY) {
453
+ let readers: ReaderPermissions = { type: 'public' };
454
+
455
+ if (
456
+ metadata?.sensitivityLevel === ToolSensitivityLevel.SENSITIVE ||
457
+ metadata?.operationType === ToolOperationType.DESTRUCTIVE
458
+ ) {
459
+ const inputEmail =
460
+ (input as any)?.email || (input as any)?.user || (input as any)?.userId;
461
+ if (inputEmail && typeof inputEmail === 'string') {
462
+ readers = {
463
+ type: 'restricted',
464
+ readers: [inputEmail],
465
+ };
466
+ } else {
467
+ readers = {
468
+ type: 'restricted',
469
+ readers: [`tool:${func.name}`],
470
+ };
471
+ }
472
+ }
473
+
474
+ return createProvenanceProxy(
475
+ result,
476
+ {
477
+ type: ProvenanceSource.TOOL,
478
+ toolName: func.name,
479
+ apiGroup: group.name,
480
+ timestamp: Date.now(),
481
+ },
482
+ readers
483
+ );
484
+ } else if (config.provenanceMode === ProvenanceMode.AST) {
485
+ let readers: ReaderPermissions = { type: 'public' };
486
+
487
+ if (
488
+ metadata?.sensitivityLevel === ToolSensitivityLevel.SENSITIVE ||
489
+ metadata?.operationType === ToolOperationType.DESTRUCTIVE
490
+ ) {
491
+ const inputEmail =
492
+ (input as any)?.email || (input as any)?.user || (input as any)?.userId;
493
+ if (inputEmail && typeof inputEmail === 'string') {
494
+ readers = {
495
+ type: 'restricted',
496
+ readers: [inputEmail],
497
+ };
498
+ } else {
499
+ readers = {
500
+ type: 'restricted',
501
+ readers: [`tool:${func.name}`],
502
+ };
503
+ }
504
+ }
505
+
506
+ return createProvenanceProxy(
507
+ result,
508
+ {
509
+ type: ProvenanceSource.TOOL,
510
+ toolName: func.name,
511
+ apiGroup: group.name,
512
+ timestamp: Date.now(),
513
+ },
514
+ readers
515
+ );
516
+ }
517
+
518
+ return result;
519
+ };
520
+ }
521
+ }
522
+ }
523
+
524
+ return api;
525
+ }
526
+
527
+ /**
528
+ * Creates API functions combining both server tools and client tools
529
+ */
530
+ private createAPIFunctionsWithClientTools(
531
+ logger: ReturnType<typeof log.child>,
532
+ executionId: string,
533
+ config: ExecutionConfig,
534
+ clientTools?: ClientToolDefinition[]
535
+ ): Record<string, unknown> {
536
+ const api = this.createAPIFunctions(logger, executionId, config);
537
+
538
+ if (clientTools && clientTools.length > 0) {
539
+ const clientToolFunctions = this.createClientToolFunctions(
540
+ clientTools,
541
+ logger,
542
+ executionId,
543
+ config
544
+ );
545
+
546
+ for (const [namespace, functions] of Object.entries(clientToolFunctions)) {
547
+ if (api[namespace]) {
548
+ Object.assign(api[namespace], functions);
549
+ } else {
550
+ api[namespace] = functions;
551
+ }
552
+ }
553
+ }
554
+
555
+ return api;
556
+ }
557
+
558
+ /**
559
+ * Creates API functions for client-provided tools that trigger pause/resume
560
+ */
561
+ private createClientToolFunctions(
562
+ clientTools: ClientToolDefinition[],
563
+ logger: ReturnType<typeof log.child>,
564
+ executionId: string,
565
+ config: ExecutionConfig
566
+ ): Record<string, Record<string, unknown>> {
567
+ const api: Record<string, Record<string, unknown>> = {};
568
+
569
+ for (const tool of clientTools) {
570
+ const namespace = tool.namespace || 'client';
571
+
572
+ if (!api[namespace]) {
573
+ api[namespace] = {};
574
+ }
575
+
576
+ const toolName = tool.name;
577
+ const metadata = tool.metadata;
578
+ const provenanceMode = config.provenanceMode || ProvenanceMode.NONE;
579
+ const policyEngine = this.policyEngine;
580
+
581
+ api[namespace][toolName] = async (input: unknown) => {
582
+ setCurrentExecutionId(executionId);
583
+
584
+ try {
585
+ const currentSequence = nextSequenceNumber();
586
+
587
+ const cachedResult = getCachedResult(currentSequence);
588
+ if (cachedResult !== undefined) {
589
+ if (cachedResult && typeof cachedResult === 'object' && (cachedResult as any).__error) {
590
+ throw new Error((cachedResult as any).message);
591
+ }
592
+ return cachedResult;
593
+ }
594
+
595
+ // Re-attach provenance from hints before policy checks
596
+ const hintMap = getHintMap(executionId);
597
+ if (hintMap && hintMap.size > 0 && input && typeof input === 'object') {
598
+ try {
599
+ reattachProvenanceFromHints(input as Record<string, unknown>, hintMap);
600
+ } catch (error) {
601
+ // Silent fail - re-attachment is best-effort
602
+ }
603
+ }
604
+
605
+ if (policyEngine && provenanceMode !== ProvenanceMode.NONE) {
606
+ await policyEngine.checkTool(toolName, namespace, input as Record<string, unknown>);
607
+ }
608
+
609
+ if (metadata) {
610
+ const isDestructive = metadata.operationType === ToolOperationType.DESTRUCTIVE;
611
+ const isSensitive = metadata.sensitivityLevel === ToolSensitivityLevel.SENSITIVE;
612
+ const needsApproval = metadata.requiresApproval || isDestructive || isSensitive;
613
+
614
+ if (needsApproval) {
615
+ const operationDescription = isDestructive
616
+ ? 'destructive operation'
617
+ : isSensitive
618
+ ? 'sensitive operation'
619
+ : 'operation';
620
+ const approvalMessage = `Approve client tool ${operationDescription}: ${toolName}`;
621
+
622
+ const approvalResult = await approval.request(approvalMessage, {
623
+ tool: toolName,
624
+ namespace,
625
+ params: input,
626
+ metadata: metadata,
627
+ isClientTool: true,
628
+ });
629
+
630
+ if (!approvalResult || !approvalResult.approved) {
631
+ throw new Error(`Client tool ${toolName} denied by user`);
632
+ }
633
+ }
634
+ }
635
+
636
+ pauseForCallback(CallbackType.TOOL, ToolOperation.CALL, {
637
+ toolName,
638
+ namespace,
639
+ input,
640
+ sequenceNumber: currentSequence,
641
+ });
642
+
643
+ throw new Error('Tool execution should have paused');
644
+ } finally {
645
+ clearCurrentExecutionId();
646
+ }
647
+ };
648
+ }
649
+
650
+ return api;
651
+ }
652
+
653
+ /**
654
+ * Get or create nested group object from hierarchical path.
655
+ * Supports hierarchical group names like "github/readOnly/repos"
656
+ * Creates nested structure: api.github.readOnly.repos
657
+ * @param api - Root API object
658
+ * @param groupName - Group name (may contain / for hierarchy)
659
+ * @returns The deepest nested object for the group
660
+ */
661
+ private getOrCreateNestedGroup(
662
+ api: Record<string, unknown>,
663
+ groupName: string
664
+ ): Record<string, unknown> {
665
+ if (!groupName.includes('/')) {
666
+ if (!api[groupName]) {
667
+ api[groupName] = {};
668
+ }
669
+ return api[groupName] as Record<string, unknown>;
670
+ }
671
+
672
+ const parts = groupName.split('/');
673
+ let current = api;
674
+
675
+ for (const part of parts) {
676
+ if (!current[part]) {
677
+ current[part] = {};
678
+ }
679
+ current = current[part] as Record<string, unknown>;
680
+ }
681
+
682
+ return current;
683
+ }
684
+ }