beddel 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (236) hide show
  1. package/README.md +297 -0
  2. package/dist/agents/agentRegistry.d.ts +68 -0
  3. package/dist/agents/agentRegistry.d.ts.map +1 -0
  4. package/dist/agents/agentRegistry.js +222 -0
  5. package/dist/agents/agentRegistry.js.map +1 -0
  6. package/dist/agents/formatter-agent.d.ts +10 -0
  7. package/dist/agents/formatter-agent.d.ts.map +1 -0
  8. package/dist/agents/formatter-agent.js +49 -0
  9. package/dist/agents/formatter-agent.js.map +1 -0
  10. package/dist/agents/genkit-agent.d.ts +12 -0
  11. package/dist/agents/genkit-agent.d.ts.map +1 -0
  12. package/dist/agents/genkit-agent.js +119 -0
  13. package/dist/agents/genkit-agent.js.map +1 -0
  14. package/dist/agents/i18n-messages.d.ts +17 -0
  15. package/dist/agents/i18n-messages.d.ts.map +1 -0
  16. package/dist/agents/i18n-messages.js +92 -0
  17. package/dist/agents/i18n-messages.js.map +1 -0
  18. package/dist/agents/index.d.ts +10 -0
  19. package/dist/agents/index.d.ts.map +1 -0
  20. package/dist/agents/index.js +26 -0
  21. package/dist/agents/index.js.map +1 -0
  22. package/dist/agents/pipeline.d.ts +15 -0
  23. package/dist/agents/pipeline.d.ts.map +1 -0
  24. package/dist/agents/pipeline.js +45 -0
  25. package/dist/agents/pipeline.js.map +1 -0
  26. package/dist/agents/schema-factory.d.ts +40 -0
  27. package/dist/agents/schema-factory.d.ts.map +1 -0
  28. package/dist/agents/schema-factory.js +121 -0
  29. package/dist/agents/schema-factory.js.map +1 -0
  30. package/dist/agents/translation-validators.d.ts +26 -0
  31. package/dist/agents/translation-validators.d.ts.map +1 -0
  32. package/dist/agents/translation-validators.js +77 -0
  33. package/dist/agents/translation-validators.js.map +1 -0
  34. package/dist/agents/translator-agents.d.ts +184 -0
  35. package/dist/agents/translator-agents.d.ts.map +1 -0
  36. package/dist/agents/translator-agents.js +613 -0
  37. package/dist/agents/translator-agents.js.map +1 -0
  38. package/dist/agents/types/translation.types.d.ts +100 -0
  39. package/dist/agents/types/translation.types.d.ts.map +1 -0
  40. package/dist/agents/types/translation.types.js +3 -0
  41. package/dist/agents/types/translation.types.js.map +1 -0
  42. package/dist/agents/validator-agent.d.ts +42 -0
  43. package/dist/agents/validator-agent.d.ts.map +1 -0
  44. package/dist/agents/validator-agent.js +122 -0
  45. package/dist/agents/validator-agent.js.map +1 -0
  46. package/dist/audit/auditTrail.d.ts +55 -0
  47. package/dist/audit/auditTrail.d.ts.map +1 -0
  48. package/dist/audit/auditTrail.js +93 -0
  49. package/dist/audit/auditTrail.js.map +1 -0
  50. package/dist/compliance/gdprEngine.d.ts +44 -0
  51. package/dist/compliance/gdprEngine.d.ts.map +1 -0
  52. package/dist/compliance/gdprEngine.js +178 -0
  53. package/dist/compliance/gdprEngine.js.map +1 -0
  54. package/dist/compliance/lgpdEngine.d.ts +51 -0
  55. package/dist/compliance/lgpdEngine.d.ts.map +1 -0
  56. package/dist/compliance/lgpdEngine.js +221 -0
  57. package/dist/compliance/lgpdEngine.js.map +1 -0
  58. package/dist/config.d.ts +78 -0
  59. package/dist/config.d.ts.map +1 -0
  60. package/dist/config.js +77 -0
  61. package/dist/config.js.map +1 -0
  62. package/dist/errors.d.ts +17 -0
  63. package/dist/errors.d.ts.map +1 -0
  64. package/dist/errors.js +40 -0
  65. package/dist/errors.js.map +1 -0
  66. package/dist/firebase/tenantManager.d.ts +84 -0
  67. package/dist/firebase/tenantManager.d.ts.map +1 -0
  68. package/dist/firebase/tenantManager.js +378 -0
  69. package/dist/firebase/tenantManager.js.map +1 -0
  70. package/dist/index.d.ts +36 -0
  71. package/dist/index.d.ts.map +1 -0
  72. package/dist/index.js +118 -0
  73. package/dist/index.js.map +1 -0
  74. package/dist/integration/secure-yaml-runtime.d.ts +68 -0
  75. package/dist/integration/secure-yaml-runtime.d.ts.map +1 -0
  76. package/dist/integration/secure-yaml-runtime.js +245 -0
  77. package/dist/integration/secure-yaml-runtime.js.map +1 -0
  78. package/dist/parser/secure-yaml-parser.d.ts +62 -0
  79. package/dist/parser/secure-yaml-parser.d.ts.map +1 -0
  80. package/dist/parser/secure-yaml-parser.js +234 -0
  81. package/dist/parser/secure-yaml-parser.js.map +1 -0
  82. package/dist/performance/autoscaling.d.ts +100 -0
  83. package/dist/performance/autoscaling.d.ts.map +1 -0
  84. package/dist/performance/autoscaling.js +339 -0
  85. package/dist/performance/autoscaling.js.map +1 -0
  86. package/dist/performance/benchmark.d.ts +104 -0
  87. package/dist/performance/benchmark.d.ts.map +1 -0
  88. package/dist/performance/benchmark.js +514 -0
  89. package/dist/performance/benchmark.js.map +1 -0
  90. package/dist/performance/index.d.ts +14 -0
  91. package/dist/performance/index.d.ts.map +1 -0
  92. package/dist/performance/index.js +35 -0
  93. package/dist/performance/index.js.map +1 -0
  94. package/dist/performance/monitor.d.ts +126 -0
  95. package/dist/performance/monitor.d.ts.map +1 -0
  96. package/dist/performance/monitor.js +324 -0
  97. package/dist/performance/monitor.js.map +1 -0
  98. package/dist/performance/streaming.d.ts +82 -0
  99. package/dist/performance/streaming.d.ts.map +1 -0
  100. package/dist/performance/streaming.js +287 -0
  101. package/dist/performance/streaming.js.map +1 -0
  102. package/dist/runtime/audit.d.ts +240 -0
  103. package/dist/runtime/audit.d.ts.map +1 -0
  104. package/dist/runtime/audit.js +641 -0
  105. package/dist/runtime/audit.js.map +1 -0
  106. package/dist/runtime/declarativeAgentRuntime.d.ts +123 -0
  107. package/dist/runtime/declarativeAgentRuntime.d.ts.map +1 -0
  108. package/dist/runtime/declarativeAgentRuntime.js +576 -0
  109. package/dist/runtime/declarativeAgentRuntime.js.map +1 -0
  110. package/dist/runtime/isolatedRuntime.d.ts +119 -0
  111. package/dist/runtime/isolatedRuntime.d.ts.map +1 -0
  112. package/dist/runtime/isolatedRuntime.js +425 -0
  113. package/dist/runtime/isolatedRuntime.js.map +1 -0
  114. package/dist/runtime/schemaCompiler.d.ts +35 -0
  115. package/dist/runtime/schemaCompiler.d.ts.map +1 -0
  116. package/dist/runtime/schemaCompiler.js +151 -0
  117. package/dist/runtime/schemaCompiler.js.map +1 -0
  118. package/dist/runtime/simpleRuntime.d.ts +57 -0
  119. package/dist/runtime/simpleRuntime.d.ts.map +1 -0
  120. package/dist/runtime/simpleRuntime.js +187 -0
  121. package/dist/runtime/simpleRuntime.js.map +1 -0
  122. package/dist/security/dashboard.d.ts +89 -0
  123. package/dist/security/dashboard.d.ts.map +1 -0
  124. package/dist/security/dashboard.js +300 -0
  125. package/dist/security/dashboard.js.map +1 -0
  126. package/dist/security/hardening.d.ts +130 -0
  127. package/dist/security/hardening.d.ts.map +1 -0
  128. package/dist/security/hardening.js +414 -0
  129. package/dist/security/hardening.js.map +1 -0
  130. package/dist/security/index.d.ts +128 -0
  131. package/dist/security/index.d.ts.map +1 -0
  132. package/dist/security/index.js +353 -0
  133. package/dist/security/index.js.map +1 -0
  134. package/dist/security/monitor.d.ts +88 -0
  135. package/dist/security/monitor.d.ts.map +1 -0
  136. package/dist/security/monitor.js +356 -0
  137. package/dist/security/monitor.js.map +1 -0
  138. package/dist/security/scanner.d.ts +104 -0
  139. package/dist/security/scanner.d.ts.map +1 -0
  140. package/dist/security/scanner.js +298 -0
  141. package/dist/security/scanner.js.map +1 -0
  142. package/dist/security/score.d.ts +150 -0
  143. package/dist/security/score.d.ts.map +1 -0
  144. package/dist/security/score.js +983 -0
  145. package/dist/security/score.js.map +1 -0
  146. package/dist/security/test-security.d.ts +22 -0
  147. package/dist/security/test-security.d.ts.map +1 -0
  148. package/dist/security/test-security.js +154 -0
  149. package/dist/security/test-security.js.map +1 -0
  150. package/dist/security/threatDetector.d.ts +39 -0
  151. package/dist/security/threatDetector.d.ts.map +1 -0
  152. package/dist/security/threatDetector.js +354 -0
  153. package/dist/security/threatDetector.js.map +1 -0
  154. package/dist/security/validation.d.ts +69 -0
  155. package/dist/security/validation.d.ts.map +1 -0
  156. package/dist/security/validation.js +286 -0
  157. package/dist/security/validation.js.map +1 -0
  158. package/dist/server/api/clientsRoute.d.ts +9 -0
  159. package/dist/server/api/clientsRoute.d.ts.map +1 -0
  160. package/dist/server/api/clientsRoute.js +71 -0
  161. package/dist/server/api/clientsRoute.js.map +1 -0
  162. package/dist/server/api/endpointsRoute.d.ts +8 -0
  163. package/dist/server/api/endpointsRoute.d.ts.map +1 -0
  164. package/dist/server/api/endpointsRoute.js +76 -0
  165. package/dist/server/api/endpointsRoute.js.map +1 -0
  166. package/dist/server/api/graphql.d.ts +9 -0
  167. package/dist/server/api/graphql.d.ts.map +1 -0
  168. package/dist/server/api/graphql.js +180 -0
  169. package/dist/server/api/graphql.js.map +1 -0
  170. package/dist/server/errors.d.ts +19 -0
  171. package/dist/server/errors.d.ts.map +1 -0
  172. package/dist/server/errors.js +42 -0
  173. package/dist/server/errors.js.map +1 -0
  174. package/dist/server/index.d.ts +7 -0
  175. package/dist/server/index.d.ts.map +1 -0
  176. package/dist/server/index.js +24 -0
  177. package/dist/server/index.js.map +1 -0
  178. package/dist/server/kvStore.d.ts +27 -0
  179. package/dist/server/kvStore.d.ts.map +1 -0
  180. package/dist/server/kvStore.js +128 -0
  181. package/dist/server/kvStore.js.map +1 -0
  182. package/dist/server/runtimeSecurity.d.ts +28 -0
  183. package/dist/server/runtimeSecurity.d.ts.map +1 -0
  184. package/dist/server/runtimeSecurity.js +85 -0
  185. package/dist/server/runtimeSecurity.js.map +1 -0
  186. package/dist/server/types.d.ts +53 -0
  187. package/dist/server/types.d.ts.map +1 -0
  188. package/dist/server/types.js +8 -0
  189. package/dist/server/types.js.map +1 -0
  190. package/dist/types/executionContext.d.ts +16 -0
  191. package/dist/types/executionContext.d.ts.map +1 -0
  192. package/dist/types/executionContext.js +3 -0
  193. package/dist/types/executionContext.js.map +1 -0
  194. package/package.json +77 -0
  195. package/src/agents/agentRegistry.ts +272 -0
  196. package/src/agents/image-agent.yaml +86 -0
  197. package/src/agents/joker-agent.yaml +47 -0
  198. package/src/agents/translator-agent.yaml +80 -0
  199. package/src/audit/auditTrail.ts +134 -0
  200. package/src/compliance/gdprEngine.ts +209 -0
  201. package/src/compliance/lgpdEngine.ts +268 -0
  202. package/src/config.ts +179 -0
  203. package/src/errors.ts +35 -0
  204. package/src/firebase/tenantManager.ts +443 -0
  205. package/src/index.ts +125 -0
  206. package/src/integration/secure-yaml-runtime.ts +341 -0
  207. package/src/parser/secure-yaml-parser.ts +273 -0
  208. package/src/performance/autoscaling.ts +495 -0
  209. package/src/performance/benchmark.ts +644 -0
  210. package/src/performance/index.ts +34 -0
  211. package/src/performance/monitor.ts +469 -0
  212. package/src/performance/streaming.ts +317 -0
  213. package/src/runtime/audit.ts +907 -0
  214. package/src/runtime/declarativeAgentRuntime.ts +836 -0
  215. package/src/runtime/isolatedRuntime.ts +572 -0
  216. package/src/runtime/schemaCompiler.ts +228 -0
  217. package/src/runtime/simpleRuntime.ts +201 -0
  218. package/src/security/dashboard.ts +462 -0
  219. package/src/security/hardening.ts +560 -0
  220. package/src/security/index.ts +439 -0
  221. package/src/security/monitor.ts +490 -0
  222. package/src/security/scanner.ts +368 -0
  223. package/src/security/score.ts +1138 -0
  224. package/src/security/threatDetector.ts +481 -0
  225. package/src/security/validation.ts +365 -0
  226. package/src/server/api/clientsRoute.ts +92 -0
  227. package/src/server/api/endpointsRoute.ts +97 -0
  228. package/src/server/api/graphql.ts +249 -0
  229. package/src/server/errors.ts +38 -0
  230. package/src/server/index.ts +6 -0
  231. package/src/server/kvStore.ts +152 -0
  232. package/src/server/runtimeSecurity.ts +102 -0
  233. package/src/server/types.ts +60 -0
  234. package/src/types/executionContext.ts +16 -0
  235. package/tools/seed.ts +365 -0
  236. package/tools/test-endpoints.ts +174 -0
@@ -0,0 +1,836 @@
1
+ /**
2
+ * Declarative Agent Runtime - YAML Interpreter for Beddel Declarative Protocol
3
+ * Safely interprets declarative YAML agent definitions without dynamic code execution
4
+ */
5
+
6
+ import * as yaml from "js-yaml";
7
+ import { experimental_generateImage, generateText } from "ai";
8
+ import { createGoogleGenerativeAI } from "@ai-sdk/google";
9
+ import { type ZodTypeAny } from "zod";
10
+ import { ExecutionContext } from "../types/executionContext";
11
+ import {
12
+ DeclarativeSchemaCompiler,
13
+ DeclarativeSchemaValidationError,
14
+ type DeclarativeSchemaPhase,
15
+ } from "./schemaCompiler";
16
+
17
+ export interface YamlAgentDefinition {
18
+ agent: {
19
+ id: string;
20
+ version: string;
21
+ protocol: string;
22
+ };
23
+ metadata: {
24
+ name: string;
25
+ description: string;
26
+ category: string;
27
+ route?: string;
28
+ };
29
+ schema: {
30
+ input: any;
31
+ output: any;
32
+ };
33
+ logic: {
34
+ variables?: Array<{
35
+ name: string;
36
+ type: string;
37
+ init: string;
38
+ }>;
39
+ workflow: Array<{
40
+ name: string;
41
+ type: string;
42
+ action: {
43
+ type: string;
44
+ output?: Record<string, any>;
45
+ [key: string]: any;
46
+ };
47
+ }>;
48
+ };
49
+ output?: {
50
+ schema?: any;
51
+ };
52
+ }
53
+
54
+ export interface YamlAgentInterpreterOptions {
55
+ yamlContent: string;
56
+ input: Record<string, any>;
57
+ props: Record<string, string>;
58
+ context: ExecutionContext;
59
+ }
60
+
61
+ export type YamlExecutionResult = Record<string, any>;
62
+
63
+ /**
64
+ * Safe declarative YAML interpreter - no dynamic code execution
65
+ */
66
+ export class DeclarativeAgentInterpreter {
67
+ private readonly MAX_VARIABLE_SIZE = 1024; // 1KB max variable size
68
+ private readonly MAX_WORKFLOW_STEPS = 100; // Prevent infinite loops
69
+ private readonly MAX_OUTPUT_SIZE = 5 * 1024 * 1024; // 5MB max output to accommodate image payloads
70
+ private readonly GEMINI_MODEL = "models/gemini-2.5-flash";
71
+ private readonly GEMINI_IMAGE_MODEL = "imagen-4.0-fast-generate-001";
72
+ private readonly SUPPORTED_TRANSLATION_LANGUAGES = ["pt", "en", "es", "fr"];
73
+ private readonly schemaCompiler = new DeclarativeSchemaCompiler();
74
+
75
+ /**
76
+ * Interpret declarative YAML agent definition
77
+ */
78
+ public async interpret(
79
+ options: YamlAgentInterpreterOptions
80
+ ): Promise<YamlExecutionResult> {
81
+ const startTime = Date.now();
82
+
83
+ try {
84
+ // Parse and validate YAML
85
+ const agent = this.parseYaml(options.yamlContent);
86
+ this.validateAgentDefinition(agent);
87
+
88
+ // Compile schemas and validate input up front
89
+ const schemas = this.buildSchemaSet(agent);
90
+ const validatedInput = this.validateAgainstSchema(
91
+ options.input,
92
+ schemas.input,
93
+ "input",
94
+ options.context
95
+ );
96
+
97
+ const executionOptions: YamlAgentInterpreterOptions = {
98
+ ...options,
99
+ input: validatedInput,
100
+ };
101
+
102
+ // Execute declarative logic
103
+ const result = await this.executeWorkflow(agent, executionOptions);
104
+
105
+ // Validate output
106
+ const validatedOutput = this.validateAgainstSchema(
107
+ result,
108
+ schemas.output,
109
+ "output",
110
+ options.context
111
+ );
112
+ this.enforceOutputSize(validatedOutput);
113
+
114
+ const executionTime = Date.now() - startTime;
115
+ options.context.log(`Declarative agent executed in ${executionTime}ms`);
116
+
117
+ return validatedOutput;
118
+ } catch (error) {
119
+ const executionTime = Date.now() - startTime;
120
+ options.context.log(`Declarative agent execution failed: ${error}`);
121
+ options.context.setError(
122
+ error instanceof Error
123
+ ? error.message
124
+ : "Unknown declarative agent error"
125
+ );
126
+ throw error;
127
+ }
128
+ }
129
+
130
+ /**
131
+ * Parse and validate YAML content
132
+ */
133
+ private parseYaml(yamlContent: string): YamlAgentDefinition {
134
+ try {
135
+ const parsed = yaml.load(yamlContent) as YamlAgentDefinition;
136
+
137
+ if (!parsed || typeof parsed !== "object") {
138
+ throw new Error("Invalid YAML: expected object");
139
+ }
140
+
141
+ if (!parsed.agent || !parsed.logic || !parsed.schema) {
142
+ throw new Error("Invalid agent definition: missing required sections");
143
+ }
144
+
145
+ return parsed;
146
+ } catch (error) {
147
+ throw new Error(`YAML parsing failed: ${error}`);
148
+ }
149
+ }
150
+
151
+ /**
152
+ * Validate agent definition structure
153
+ */
154
+ private validateAgentDefinition(agent: YamlAgentDefinition): void {
155
+ // Validate protocol version
156
+ if (agent.agent.protocol !== "beddel-declarative-protocol/v2.0") {
157
+ throw new Error(`Unsupported protocol: ${agent.agent.protocol}`);
158
+ }
159
+
160
+ // Validate schema
161
+ if (!agent.schema.input || !agent.schema.output) {
162
+ throw new Error("Invalid schema: missing input or output definition");
163
+ }
164
+
165
+ // Validate workflow
166
+ if (
167
+ !Array.isArray(agent.logic.workflow) ||
168
+ agent.logic.workflow.length === 0
169
+ ) {
170
+ throw new Error("Invalid workflow: must be non-empty array");
171
+ }
172
+
173
+ if (agent.logic.workflow.length > this.MAX_WORKFLOW_STEPS) {
174
+ throw new Error(
175
+ `Workflow too complex: max ${this.MAX_WORKFLOW_STEPS} steps allowed`
176
+ );
177
+ }
178
+ }
179
+
180
+ private buildSchemaSet(agent: YamlAgentDefinition): {
181
+ input: ZodTypeAny;
182
+ output: ZodTypeAny;
183
+ } {
184
+ return {
185
+ input: this.schemaCompiler.compile(agent.schema.input, "schema.input"),
186
+ output: this.schemaCompiler.compile(agent.schema.output, "schema.output"),
187
+ };
188
+ }
189
+
190
+ private validateAgainstSchema(
191
+ data: unknown,
192
+ schema: ZodTypeAny,
193
+ phase: DeclarativeSchemaPhase,
194
+ context: ExecutionContext
195
+ ): any {
196
+ const validationResult = schema.safeParse(data);
197
+ if (!validationResult.success) {
198
+ const issues = validationResult.error.issues;
199
+ const issueSummary = issues
200
+ .map((issue) => `${issue.path.join(".") || "root"}: ${issue.message}`)
201
+ .join("; ");
202
+ const label = phase === "input" ? "Input" : "Output";
203
+ const message = `${label} validation failed: ${issueSummary}`;
204
+ context.setError(message);
205
+ throw new DeclarativeSchemaValidationError(message, phase, issues);
206
+ }
207
+
208
+ return validationResult.data;
209
+ }
210
+
211
+ private enforceOutputSize(output: any): void {
212
+ const outputSize = JSON.stringify(output).length;
213
+ if (outputSize > this.MAX_OUTPUT_SIZE) {
214
+ throw new Error(
215
+ `Output size exceeds maximum allowed: ${outputSize} > ${this.MAX_OUTPUT_SIZE}`
216
+ );
217
+ }
218
+ }
219
+
220
+ /**
221
+ * Execute declarative workflow
222
+ */
223
+ private async executeWorkflow(
224
+ agent: YamlAgentDefinition,
225
+ options: YamlAgentInterpreterOptions
226
+ ): Promise<YamlExecutionResult> {
227
+ const variables = new Map<string, any>();
228
+ let output: any = undefined;
229
+
230
+ // Initialize variables
231
+ if (agent.logic.variables) {
232
+ for (const variable of agent.logic.variables) {
233
+ this.validateVariable(variable);
234
+ const value = this.evaluateValue(variable.init, variables);
235
+ variables.set(variable.name, value);
236
+ }
237
+ }
238
+
239
+ // Execute workflow steps
240
+ for (const step of agent.logic.workflow) {
241
+ output = await this.executeWorkflowStep(step, variables, options);
242
+ }
243
+
244
+ return output;
245
+ }
246
+
247
+ /**
248
+ * Execute single workflow step
249
+ */
250
+ private async executeWorkflowStep(
251
+ step: any,
252
+ variables: Map<string, any>,
253
+ options: YamlAgentInterpreterOptions
254
+ ): Promise<any> {
255
+ options.context.log(`Executing workflow step: ${step.name} (${step.type})`);
256
+
257
+ switch (step.type) {
258
+ case "output-generator":
259
+ return this.executeOutputGenerator(step, variables, options);
260
+ case "genkit-joke":
261
+ return this.executeGenkitJoke(step, variables, options);
262
+ case "genkit-translation":
263
+ return this.executeGenkitTranslation(step, variables, options);
264
+ case "genkit-image":
265
+ return this.executeGenkitImage(step, variables, options);
266
+ default:
267
+ throw new Error(`Unsupported workflow step type: ${step.type}`);
268
+ }
269
+ }
270
+
271
+ /**
272
+ * Execute output generator step
273
+ */
274
+ private executeOutputGenerator(
275
+ step: any,
276
+ variables: Map<string, any>,
277
+ options: YamlAgentInterpreterOptions
278
+ ): any {
279
+ if (step.action?.type !== "generate" || !step.action.output) {
280
+ throw new Error("Invalid output generator configuration");
281
+ }
282
+
283
+ // Build output object
284
+ const output: any = {};
285
+
286
+ // Debug: Log available variables
287
+ options.context.log(
288
+ `Output generator: Available variables: ${Array.from(variables.keys()).join(", ")}`
289
+ );
290
+
291
+ for (const [key, valueExpr] of Object.entries(step.action.output)) {
292
+ if (typeof valueExpr === "string" && valueExpr.startsWith("$")) {
293
+ try {
294
+ const reference = valueExpr.substring(1);
295
+ options.context.log(
296
+ `Output generator: Resolving reference ${valueExpr} -> ${reference}`
297
+ );
298
+ const resolved = this.resolveReference(reference, variables);
299
+ output[key] = resolved;
300
+ options.context.log(
301
+ `Output generator: Resolved ${key} = ${typeof resolved === "string" ? resolved.substring(0, 50) + "..." : JSON.stringify(resolved).substring(0, 100)}`
302
+ );
303
+ } catch (error) {
304
+ options.context.log(
305
+ `Output generator: Failed to resolve ${valueExpr}: ${error instanceof Error ? error.message : String(error)}`
306
+ );
307
+ throw error;
308
+ }
309
+ } else {
310
+ output[key] = valueExpr;
311
+ }
312
+ }
313
+
314
+ options.context.log(
315
+ `Output generator: Final output keys: ${Object.keys(output).join(", ")}`
316
+ );
317
+
318
+ return output;
319
+ }
320
+
321
+ /**
322
+ * Execute Gemini Flash text helper for the joke agent
323
+ */
324
+ private async executeGenkitJoke(
325
+ step: any,
326
+ variables: Map<string, any>,
327
+ options: YamlAgentInterpreterOptions
328
+ ): Promise<any> {
329
+ const prompt =
330
+ typeof step.action?.prompt === "string" && step.action.prompt.trim().length
331
+ ? step.action.prompt.trim()
332
+ : "Conte uma piada curta e original em português.";
333
+ const temperature =
334
+ typeof step.action?.temperature === "number"
335
+ ? step.action.temperature
336
+ : 0.8;
337
+ const maxTokens =
338
+ typeof step.action?.maxTokens === "number"
339
+ ? step.action.maxTokens
340
+ : undefined;
341
+ const resultVar =
342
+ typeof step.action?.result === "string" && step.action.result.length > 0
343
+ ? step.action.result
344
+ : "jokerResult";
345
+
346
+ const joke = await this.callGeminiFlashText(
347
+ { prompt, temperature, maxTokens },
348
+ options.props,
349
+ options.context
350
+ );
351
+
352
+ variables.set(resultVar, joke);
353
+ return joke;
354
+ }
355
+
356
+ /**
357
+ * Execute translation step backed by Gemini Flash
358
+ */
359
+ private async executeGenkitTranslation(
360
+ step: any,
361
+ variables: Map<string, any>,
362
+ options: YamlAgentInterpreterOptions
363
+ ): Promise<any> {
364
+ const texto = options.input?.texto;
365
+ const idiomaOrigem = options.input?.idioma_origem;
366
+ const idiomaDestino = options.input?.idioma_destino;
367
+
368
+ const resultVar =
369
+ typeof step.action?.result === "string" && step.action.result.length > 0
370
+ ? step.action.result
371
+ : "translationResult";
372
+
373
+ const translation = await this.callGeminiFlashTranslation(
374
+ {
375
+ texto,
376
+ idioma_origem: idiomaOrigem,
377
+ idioma_destino: idiomaDestino,
378
+ promptTemplate:
379
+ typeof step.action?.promptTemplate === "string"
380
+ ? step.action.promptTemplate
381
+ : undefined,
382
+ },
383
+ options.props,
384
+ options.context
385
+ );
386
+
387
+ variables.set(resultVar, translation);
388
+ return translation;
389
+ }
390
+
391
+ /**
392
+ * Execute image generation step backed by Gemini Flash
393
+ */
394
+ private async executeGenkitImage(
395
+ step: any,
396
+ variables: Map<string, any>,
397
+ options: YamlAgentInterpreterOptions
398
+ ): Promise<any> {
399
+ const descricao =
400
+ typeof options.input?.descricao === "string"
401
+ ? options.input.descricao.trim()
402
+ : "";
403
+ const estilo =
404
+ typeof options.input?.estilo === "string"
405
+ ? options.input.estilo.trim()
406
+ : "";
407
+ const resolucaoInput =
408
+ typeof options.input?.resolucao === "string"
409
+ ? options.input.resolucao.trim()
410
+ : "";
411
+
412
+ if (!descricao) {
413
+ throw new Error("Missing required image input: descricao");
414
+ }
415
+ if (!estilo) {
416
+ throw new Error("Missing required image input: estilo");
417
+ }
418
+ if (!resolucaoInput) {
419
+ throw new Error("Missing required image input: resolucao");
420
+ }
421
+
422
+ const promptTemplate =
423
+ typeof step.action?.promptTemplate === "string" &&
424
+ step.action.promptTemplate.trim().length > 0
425
+ ? step.action.promptTemplate
426
+ : "Gere uma imagem detalhada no estilo {{estilo}} baseada na descrição: {{descricao}}";
427
+
428
+ const prompt = promptTemplate
429
+ .replace(/{{descricao}}/g, descricao)
430
+ .replace(/{{estilo}}/g, estilo)
431
+ .trim();
432
+
433
+ const resultVar =
434
+ typeof step.action?.result === "string" && step.action.result.length > 0
435
+ ? step.action.result
436
+ : "imageResult";
437
+
438
+ const imageResult = await this.callGeminiFlashImage(
439
+ {
440
+ prompt,
441
+ estilo,
442
+ resolucao: resolucaoInput,
443
+ },
444
+ options.props,
445
+ options.context
446
+ );
447
+
448
+ variables.set(resultVar, imageResult);
449
+ options.context.log(
450
+ `Image generator: Saved result in variable '${resultVar}' with keys: ${Object.keys(imageResult).join(", ")}`
451
+ );
452
+ options.context.log(
453
+ `Image generator: imageResult.image_url exists: ${!!imageResult?.image_url}`
454
+ );
455
+ return imageResult;
456
+ }
457
+
458
+ /**
459
+ * Evaluate value expression
460
+ */
461
+ private evaluateValue(expr: string, variables: Map<string, any>): any {
462
+ // Handle string literals
463
+ if (expr.startsWith('"') && expr.endsWith('"')) {
464
+ if (expr.length - 2 > this.MAX_VARIABLE_SIZE) {
465
+ throw new Error("Variable initialization exceeds maximum size");
466
+ }
467
+ return expr.slice(1, -1);
468
+ }
469
+
470
+ if (expr.startsWith("'") && expr.endsWith("'")) {
471
+ if (expr.length - 2 > this.MAX_VARIABLE_SIZE) {
472
+ throw new Error("Variable initialization exceeds maximum size");
473
+ }
474
+ return expr.slice(1, -1);
475
+ }
476
+
477
+ if (expr.length > this.MAX_VARIABLE_SIZE) {
478
+ throw new Error("Variable initialization exceeds maximum size");
479
+ }
480
+
481
+ // Handle boolean literals
482
+ if (expr === "true") return true;
483
+ if (expr === "false") return false;
484
+
485
+ // Handle null literal
486
+ if (expr === "null") return null;
487
+
488
+ // Handle variable references
489
+ if (expr.startsWith("$")) {
490
+ return this.resolveReference(expr.substring(1), variables);
491
+ }
492
+
493
+ // Handle numbers
494
+ if (/^-?\d+$/.test(expr)) return parseInt(expr, 10);
495
+ if (/^-?\d+\.\d+$/.test(expr)) return parseFloat(expr);
496
+
497
+ throw new Error(`Unsupported value expression: ${expr}`);
498
+ }
499
+
500
+ /**
501
+ * Validate variable declaration
502
+ */
503
+ private validateVariable(variable: any): void {
504
+ if (!variable.name || !variable.type) {
505
+ throw new Error("Invalid variable declaration: missing name or type");
506
+ }
507
+
508
+ if (!["string", "number", "boolean", "object"].includes(variable.type)) {
509
+ throw new Error(`Unsupported variable type: ${variable.type}`);
510
+ }
511
+ }
512
+
513
+ /**
514
+ * Resolve variable reference, including nested properties (e.g., foo.bar.baz)
515
+ */
516
+ private resolveReference(
517
+ reference: string,
518
+ variables: Map<string, any>
519
+ ): any {
520
+ const [varName, ...pathSegments] = reference.split(".");
521
+ let value = variables.get(varName);
522
+ if (value === undefined) {
523
+ throw new Error(`Undefined variable referenced: ${varName}`);
524
+ }
525
+
526
+ for (const segment of pathSegments) {
527
+ if (value == null || typeof value !== "object") {
528
+ throw new Error(
529
+ `Cannot resolve path '${reference}': segment '${segment}' is invalid`
530
+ );
531
+ }
532
+ value = value[segment];
533
+ }
534
+
535
+ return value;
536
+ }
537
+
538
+ /**
539
+ * Ensure we have a Gemini API key before calling Genkit helpers
540
+ */
541
+ private ensureGeminiApiKey(props: Record<string, string>): string {
542
+ const apiKey = props?.gemini_api_key?.trim();
543
+ if (!apiKey) {
544
+ throw new Error(
545
+ "Missing required prop: gemini_api_key. Configure a valid Gemini API key before calling this agent."
546
+ );
547
+ }
548
+ return apiKey;
549
+ }
550
+
551
+ private createGeminiModel(props: Record<string, string>) {
552
+ const apiKey = this.ensureGeminiApiKey(props);
553
+ const google = createGoogleGenerativeAI({ apiKey });
554
+ const model = google(this.GEMINI_MODEL);
555
+ return model;
556
+ }
557
+
558
+ private createGeminiImageModel(props: Record<string, string>) {
559
+ const apiKey = this.ensureGeminiApiKey(props);
560
+ const google = createGoogleGenerativeAI({ apiKey });
561
+ return google.image(this.GEMINI_IMAGE_MODEL);
562
+ }
563
+
564
+ private async callGeminiFlashText(
565
+ params: {
566
+ prompt: string;
567
+ temperature?: number;
568
+ maxTokens?: number;
569
+ },
570
+ props: Record<string, string>,
571
+ context: ExecutionContext
572
+ ): Promise<{
573
+ texto: string;
574
+ metadados: {
575
+ modelo_utilizado: string;
576
+ tempo_processamento: number;
577
+ temperature: number;
578
+ max_tokens: number | null;
579
+ prompt_utilizado: string;
580
+ };
581
+ }> {
582
+ const prompt = params.prompt?.trim();
583
+ if (!prompt) {
584
+ throw new Error("Gemini Flash text helper requires a prompt");
585
+ }
586
+
587
+ const temperature =
588
+ typeof params.temperature === "number" ? params.temperature : 0.7;
589
+ const maxTokens =
590
+ typeof params.maxTokens === "number" ? params.maxTokens : undefined;
591
+
592
+ const model = this.createGeminiModel(props);
593
+ const startTime = Date.now();
594
+
595
+ try {
596
+ context.log(
597
+ `Invoking Gemini Flash text helper (temperature=${temperature}, maxTokens=${
598
+ typeof maxTokens === "number" ? maxTokens : "provider-default"
599
+ })`
600
+ );
601
+ const generationOptions: Record<string, any> = {
602
+ model,
603
+ prompt,
604
+ temperature,
605
+ };
606
+ if (typeof maxTokens === "number") {
607
+ generationOptions.maxOutputTokens = maxTokens;
608
+ }
609
+ const { text, content } = await generateText(
610
+ generationOptions as any
611
+ );
612
+ const contentText = content
613
+ ? content
614
+ .map((part: any) =>
615
+ typeof part?.text === "string" ? part.text : ""
616
+ )
617
+ .join(" ")
618
+ .trim()
619
+ : "";
620
+ const finalText = (text || "").trim() || contentText || "";
621
+ if (!finalText) {
622
+ throw new Error("Gemini Flash text helper returned empty response");
623
+ }
624
+
625
+ return {
626
+ texto: finalText,
627
+ metadados: {
628
+ modelo_utilizado: this.GEMINI_MODEL,
629
+ tempo_processamento: Date.now() - startTime,
630
+ temperature,
631
+ max_tokens: typeof maxTokens === "number" ? maxTokens : null,
632
+ prompt_utilizado: prompt,
633
+ },
634
+ };
635
+ } catch (error) {
636
+ const message =
637
+ error instanceof Error ? error.message : "Unknown Gemini Flash error";
638
+ context.log(`Gemini Flash text helper failed: ${message}`);
639
+ context.setError(message);
640
+ throw error;
641
+ }
642
+ }
643
+
644
+ private async callGeminiFlashTranslation(
645
+ params: {
646
+ texto?: string;
647
+ idioma_origem?: string;
648
+ idioma_destino?: string;
649
+ promptTemplate?: string;
650
+ },
651
+ props: Record<string, string>,
652
+ context: ExecutionContext
653
+ ): Promise<{
654
+ texto_traduzido: string;
655
+ metadados: {
656
+ modelo_utilizado: string;
657
+ tempo_processamento: number;
658
+ confianca: number;
659
+ idiomas_suportados: string[];
660
+ idiomas_solicitados: {
661
+ origem: string;
662
+ destino: string;
663
+ };
664
+ prompt_utilizado: string;
665
+ };
666
+ }> {
667
+ const texto = params.texto?.trim();
668
+ const idiomaOrigem = params.idioma_origem?.trim().toLowerCase();
669
+ const idiomaDestino = params.idioma_destino?.trim().toLowerCase();
670
+
671
+ if (!texto || !idiomaOrigem || !idiomaDestino) {
672
+ throw new Error(
673
+ "Missing required translation parameters: texto, idioma_origem, idioma_destino"
674
+ );
675
+ }
676
+
677
+ if (idiomaOrigem === idiomaDestino) {
678
+ return {
679
+ texto_traduzido: texto,
680
+ metadados: {
681
+ modelo_utilizado: this.GEMINI_MODEL,
682
+ tempo_processamento: 0,
683
+ confianca: 1,
684
+ idiomas_suportados: this.SUPPORTED_TRANSLATION_LANGUAGES,
685
+ idiomas_solicitados: {
686
+ origem: idiomaOrigem,
687
+ destino: idiomaDestino,
688
+ },
689
+ prompt_utilizado: "Bypass: idiomas de origem e destino são iguais",
690
+ },
691
+ };
692
+ }
693
+
694
+ const template =
695
+ params.promptTemplate && params.promptTemplate.trim().length > 0
696
+ ? params.promptTemplate
697
+ : `Traduza o texto abaixo de {{idioma_origem}} para {{idioma_destino}}.
698
+ Responda somente com o texto traduzido sem comentários adicionais.
699
+
700
+ Texto:
701
+ {{texto}}`;
702
+
703
+ const prompt = template
704
+ .replace(/{{texto}}/g, texto)
705
+ .replace(/{{idioma_origem}}/g, idiomaOrigem)
706
+ .replace(/{{idioma_destino}}/g, idiomaDestino)
707
+ .trim();
708
+
709
+ const model = this.createGeminiModel(props);
710
+ const startTime = Date.now();
711
+
712
+ try {
713
+ context.log(
714
+ `Invoking Gemini Flash translation helper (${idiomaOrigem}->${idiomaDestino})`
715
+ );
716
+ const { text, content } = await generateText({
717
+ model,
718
+ prompt,
719
+ temperature: 0.2,
720
+ });
721
+
722
+ const contentText = content
723
+ ? content
724
+ .map((part: any) =>
725
+ typeof part?.text === "string" ? part.text : ""
726
+ )
727
+ .join(" ")
728
+ .trim()
729
+ : "";
730
+ const translatedText = (text || "").trim() || contentText || "";
731
+ if (!translatedText) {
732
+ throw new Error("Gemini Flash translation returned empty response");
733
+ }
734
+
735
+ return {
736
+ texto_traduzido: translatedText,
737
+ metadados: {
738
+ modelo_utilizado: this.GEMINI_MODEL,
739
+ tempo_processamento: Date.now() - startTime,
740
+ confianca: 0.85,
741
+ idiomas_suportados: this.SUPPORTED_TRANSLATION_LANGUAGES,
742
+ idiomas_solicitados: {
743
+ origem: idiomaOrigem,
744
+ destino: idiomaDestino,
745
+ },
746
+ prompt_utilizado: prompt,
747
+ },
748
+ };
749
+ } catch (error) {
750
+ const message =
751
+ error instanceof Error ? error.message : "Unknown translation error";
752
+ context.log(`Gemini Flash translation failed: ${message}`);
753
+ context.setError(message);
754
+ throw error;
755
+ }
756
+ }
757
+
758
+ private async callGeminiFlashImage(
759
+ params: { prompt: string; estilo: string; resolucao: string },
760
+ props: Record<string, string>,
761
+ context: ExecutionContext
762
+ ): Promise<{
763
+ image_url: string;
764
+ image_base64: string;
765
+ media_type: string;
766
+ prompt_utilizado: string;
767
+ metadados: {
768
+ modelo_utilizado: string;
769
+ tempo_processamento: number;
770
+ estilo: string;
771
+ resolucao: string;
772
+ };
773
+ }> {
774
+ const prompt = params.prompt?.trim();
775
+ const estilo = params.estilo?.trim();
776
+ const resolucao = params.resolucao?.trim();
777
+
778
+ if (!prompt) {
779
+ throw new Error("Gemini Flash image helper requires a prompt");
780
+ }
781
+ if (!estilo) {
782
+ throw new Error("Gemini Flash image helper requires an estilo value");
783
+ }
784
+ if (!resolucao || !/^\d+x\d+$/.test(resolucao)) {
785
+ throw new Error(
786
+ "Gemini Flash image helper requires uma resolução no formato LARGURAxALTURA (ex: 1024x1024)"
787
+ );
788
+ }
789
+
790
+ const model = this.createGeminiImageModel(props);
791
+ const startTime = Date.now();
792
+
793
+ try {
794
+ context.log(
795
+ `Invoking Gemini Flash image helper (estilo=${estilo}, resolucao=${resolucao})`
796
+ );
797
+ const result = await experimental_generateImage({
798
+ model,
799
+ prompt,
800
+ size: resolucao as `${number}x${number}`,
801
+ });
802
+
803
+ const image = result.image;
804
+ if (!image?.base64 || !image.mediaType) {
805
+ throw new Error("Gemini Flash image helper returned an invalid file");
806
+ }
807
+
808
+ const normalizedBase64 = image.base64.replace(/\s+/g, "");
809
+ const imageUrl = `data:${image.mediaType};base64,${normalizedBase64}`;
810
+
811
+ return {
812
+ image_url: imageUrl,
813
+ image_base64: normalizedBase64,
814
+ media_type: image.mediaType,
815
+ prompt_utilizado: prompt,
816
+ metadados: {
817
+ modelo_utilizado: this.GEMINI_IMAGE_MODEL,
818
+ tempo_processamento: Date.now() - startTime,
819
+ estilo,
820
+ resolucao,
821
+ },
822
+ };
823
+ } catch (error) {
824
+ const message =
825
+ error instanceof Error ? error.message : "Unknown image error";
826
+ context.log(`Gemini Flash image helper failed: ${message}`);
827
+ context.setError(message);
828
+ throw error;
829
+ }
830
+ }
831
+ }
832
+
833
+ // Singleton instance
834
+ export const declarativeInterpreter = new DeclarativeAgentInterpreter();
835
+
836
+ export default DeclarativeAgentInterpreter;