@flink-app/flink 1.0.0 → 2.0.0-alpha.49

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 (109) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/cli/build.ts +8 -1
  3. package/cli/run.ts +8 -1
  4. package/dist/cli/build.js +8 -1
  5. package/dist/cli/run.js +8 -1
  6. package/dist/src/FlinkApp.d.ts +33 -0
  7. package/dist/src/FlinkApp.js +247 -27
  8. package/dist/src/FlinkContext.d.ts +21 -0
  9. package/dist/src/FlinkHttpHandler.d.ts +90 -1
  10. package/dist/src/TypeScriptCompiler.d.ts +42 -0
  11. package/dist/src/TypeScriptCompiler.js +366 -8
  12. package/dist/src/TypeScriptUtils.js +4 -0
  13. package/dist/src/ai/AgentRunner.d.ts +39 -0
  14. package/dist/src/ai/AgentRunner.js +625 -0
  15. package/dist/src/ai/FlinkAgent.d.ts +446 -0
  16. package/dist/src/ai/FlinkAgent.js +633 -0
  17. package/dist/src/ai/FlinkTool.d.ts +37 -0
  18. package/dist/src/ai/FlinkTool.js +2 -0
  19. package/dist/src/ai/LLMAdapter.d.ts +119 -0
  20. package/dist/src/ai/LLMAdapter.js +2 -0
  21. package/dist/src/ai/SubAgentExecutor.d.ts +36 -0
  22. package/dist/src/ai/SubAgentExecutor.js +220 -0
  23. package/dist/src/ai/ToolExecutor.d.ts +35 -0
  24. package/dist/src/ai/ToolExecutor.js +237 -0
  25. package/dist/src/ai/index.d.ts +5 -0
  26. package/dist/src/ai/index.js +21 -0
  27. package/dist/src/handlers/StreamWriterFactory.d.ts +20 -0
  28. package/dist/src/handlers/StreamWriterFactory.js +83 -0
  29. package/dist/src/index.d.ts +4 -0
  30. package/dist/src/index.js +4 -0
  31. package/dist/src/utils.d.ts +30 -0
  32. package/dist/src/utils.js +52 -0
  33. package/package.json +14 -2
  34. package/readme.md +425 -0
  35. package/spec/AgentDuplicateDetection.spec.ts +112 -0
  36. package/spec/AgentRunner.spec.ts +527 -0
  37. package/spec/ConversationHooks.spec.ts +290 -0
  38. package/spec/FlinkAgent.spec.ts +310 -0
  39. package/spec/FlinkApp.onError.spec.ts +1 -2
  40. package/spec/StreamingIntegration.spec.ts +138 -0
  41. package/spec/SubAgentSupport.spec.ts +941 -0
  42. package/spec/ToolExecutor.spec.ts +360 -0
  43. package/spec/mock-project/dist/spec/mock-project/src/handlers/GetCar.js +57 -0
  44. package/spec/mock-project/dist/spec/mock-project/src/handlers/GetCar2.js +59 -0
  45. package/spec/mock-project/dist/spec/mock-project/src/handlers/GetCarWithArraySchema.js +53 -0
  46. package/spec/mock-project/dist/spec/mock-project/src/handlers/GetCarWithArraySchema2.js +53 -0
  47. package/spec/mock-project/dist/spec/mock-project/src/handlers/GetCarWithArraySchema3.js +53 -0
  48. package/spec/mock-project/dist/spec/mock-project/src/handlers/GetCarWithLiteralSchema.js +55 -0
  49. package/spec/mock-project/dist/spec/mock-project/src/handlers/GetCarWithLiteralSchema2.js +55 -0
  50. package/spec/mock-project/dist/spec/mock-project/src/handlers/GetCarWithSchemaInFile.js +58 -0
  51. package/spec/mock-project/dist/spec/mock-project/src/handlers/GetCarWithSchemaInFile2.js +58 -0
  52. package/spec/mock-project/dist/spec/mock-project/src/handlers/ManuallyAddedHandler.js +53 -0
  53. package/spec/mock-project/dist/spec/mock-project/src/handlers/ManuallyAddedHandler2.js +55 -0
  54. package/spec/mock-project/dist/spec/mock-project/src/handlers/PatchCar.js +58 -0
  55. package/spec/mock-project/dist/spec/mock-project/src/handlers/PatchOnboardingSession.js +76 -0
  56. package/spec/mock-project/dist/spec/mock-project/src/handlers/PatchOrderWithComplexTypes.js +58 -0
  57. package/spec/mock-project/dist/spec/mock-project/src/handlers/PatchProductWithIntersection.js +59 -0
  58. package/spec/mock-project/dist/spec/mock-project/src/handlers/PatchUserWithUnion.js +59 -0
  59. package/spec/mock-project/dist/spec/mock-project/src/handlers/PostCar.js +55 -0
  60. package/spec/mock-project/dist/spec/mock-project/src/handlers/PostLogin.js +56 -0
  61. package/spec/mock-project/dist/spec/mock-project/src/handlers/PostLogout.js +55 -0
  62. package/spec/mock-project/dist/spec/mock-project/src/handlers/PutCar.js +55 -0
  63. package/spec/mock-project/dist/spec/mock-project/src/index.js +83 -0
  64. package/spec/mock-project/dist/spec/mock-project/src/repos/CarRepo.js +26 -0
  65. package/spec/mock-project/dist/spec/mock-project/src/schemas/Car.js +2 -0
  66. package/spec/mock-project/dist/spec/mock-project/src/schemas/DefaultExportSchema.js +2 -0
  67. package/spec/mock-project/dist/spec/mock-project/src/schemas/FileWithTwoSchemas.js +2 -0
  68. package/spec/mock-project/dist/src/FlinkApp.js +1012 -0
  69. package/spec/mock-project/dist/src/FlinkContext.js +2 -0
  70. package/spec/mock-project/dist/src/FlinkErrors.js +143 -0
  71. package/spec/mock-project/dist/src/FlinkHttpHandler.js +47 -0
  72. package/spec/mock-project/dist/src/FlinkJob.js +2 -0
  73. package/spec/mock-project/dist/src/FlinkLog.js +26 -0
  74. package/spec/mock-project/dist/src/FlinkPlugin.js +2 -0
  75. package/spec/mock-project/dist/src/FlinkRepo.js +224 -0
  76. package/spec/mock-project/dist/src/FlinkResponse.js +2 -0
  77. package/spec/mock-project/dist/src/ai/AgentExecutor.js +279 -0
  78. package/spec/mock-project/dist/src/ai/AgentRunner.js +625 -0
  79. package/spec/mock-project/dist/src/ai/FlinkAgent.js +633 -0
  80. package/spec/mock-project/dist/src/ai/FlinkTool.js +2 -0
  81. package/spec/mock-project/dist/src/ai/LLMAdapter.js +2 -0
  82. package/spec/mock-project/dist/src/ai/SubAgentExecutor.js +220 -0
  83. package/spec/mock-project/dist/src/ai/ToolExecutor.js +237 -0
  84. package/spec/mock-project/dist/src/auth/FlinkAuthPlugin.js +2 -0
  85. package/spec/mock-project/dist/src/auth/FlinkAuthUser.js +2 -0
  86. package/spec/mock-project/dist/src/handlers/StreamWriterFactory.js +83 -0
  87. package/spec/mock-project/dist/src/index.js +17 -69
  88. package/spec/mock-project/dist/src/mock-data-generator.js +9 -0
  89. package/spec/mock-project/dist/src/utils.js +290 -0
  90. package/spec/mock-project/tsconfig.json +6 -1
  91. package/spec/testHelpers.ts +49 -0
  92. package/spec/utils.caseConversion.spec.ts +80 -0
  93. package/spec/utils.spec.ts +13 -13
  94. package/src/FlinkApp.ts +251 -7
  95. package/src/FlinkContext.ts +22 -0
  96. package/src/FlinkHttpHandler.ts +100 -2
  97. package/src/TypeScriptCompiler.ts +420 -9
  98. package/src/TypeScriptUtils.ts +5 -0
  99. package/src/ai/AgentRunner.ts +549 -0
  100. package/src/ai/FlinkAgent.ts +770 -0
  101. package/src/ai/FlinkTool.ts +40 -0
  102. package/src/ai/LLMAdapter.ts +96 -0
  103. package/src/ai/SubAgentExecutor.ts +199 -0
  104. package/src/ai/ToolExecutor.ts +193 -0
  105. package/src/ai/index.ts +5 -0
  106. package/src/handlers/StreamWriterFactory.ts +84 -0
  107. package/src/index.ts +4 -0
  108. package/src/utils.ts +52 -0
  109. package/tsconfig.json +6 -1
@@ -0,0 +1,119 @@
1
+ /**
2
+ * Generic message format for LLM conversations
3
+ * Compatible with most LLM providers (Anthropic, OpenAI, etc.)
4
+ */
5
+ export type LLMMessage = {
6
+ role: "system";
7
+ content: string;
8
+ } | {
9
+ role: "user";
10
+ content: string | LLMContentBlock[];
11
+ } | {
12
+ role: "assistant";
13
+ content: string | LLMContentBlock[];
14
+ };
15
+ export type LLMContentBlock = {
16
+ type: "text";
17
+ text: string;
18
+ } | {
19
+ type: "tool_use";
20
+ id: string;
21
+ name: string;
22
+ input: any;
23
+ } | {
24
+ type: "tool_result";
25
+ tool_use_id: string;
26
+ content: string;
27
+ is_error?: boolean;
28
+ };
29
+ /**
30
+ * Standard Flink tool schema format
31
+ * This is the format generated by ToolExecutor.getToolSchema()
32
+ * Adapters convert this to provider-specific formats
33
+ */
34
+ export interface FlinkToolSchema {
35
+ /**
36
+ * Tool name (kebab-case identifier)
37
+ */
38
+ name: string;
39
+ /**
40
+ * Human-readable description for the LLM
41
+ */
42
+ description: string;
43
+ /**
44
+ * JSON Schema for tool input validation
45
+ * Generated from Zod schema via z.toJSONSchema()
46
+ */
47
+ inputSchema: Record<string, any>;
48
+ }
49
+ /**
50
+ * LLM Adapter interface for provider abstraction
51
+ * Enables support for multiple AI providers (Anthropic, OpenAI, Mistral, etc.)
52
+ *
53
+ * All adapters use streaming as the primary method since:
54
+ * - All modern LLM providers support streaming
55
+ * - Better user experience (time-to-first-token <500ms)
56
+ * - Prevents HTTP timeouts for long responses
57
+ * - FlinkAgent's lazy generator pattern allows both streaming and awaiting final result
58
+ *
59
+ * @template ToolSchema - The tool schema format accepted by this adapter (defaults to FlinkToolSchema)
60
+ */
61
+ export interface LLMAdapter<ToolSchema = FlinkToolSchema> {
62
+ /**
63
+ * Optional debug flag to enable detailed logging
64
+ * When enabled, adapters will log:
65
+ * - Full request parameters sent to the LLM provider
66
+ * - Tool call decisions made by the LLM
67
+ * - Token usage and performance metrics
68
+ * - Error details and response metadata
69
+ *
70
+ * Useful for development and troubleshooting.
71
+ * Default: false
72
+ */
73
+ debug?: boolean;
74
+ /**
75
+ * Stream a completion with tool calling support
76
+ * Returns async generator for streaming responses
77
+ *
78
+ * Even if you want non-streaming UX, use this method and await the final result:
79
+ * ```typescript
80
+ * const response = agent.execute({ message: "Hello" });
81
+ * const result = await response.result; // Non-streaming consumption
82
+ * ```
83
+ */
84
+ stream(params: {
85
+ /**
86
+ * Instructions that define agent behavior and personality.
87
+ * Adapters should prepend this as a system message to the conversation.
88
+ * Following Vercel AI SDK pattern.
89
+ */
90
+ instructions: string;
91
+ messages: LLMMessage[];
92
+ tools: ToolSchema[];
93
+ maxTokens: number;
94
+ temperature: number;
95
+ }): AsyncGenerator<LLMStreamChunk>;
96
+ }
97
+ /**
98
+ * Streaming chunk types
99
+ */
100
+ export type LLMStreamChunk = {
101
+ type: "text";
102
+ delta: string;
103
+ } | {
104
+ type: "tool_call";
105
+ toolCall: {
106
+ id: string;
107
+ name: string;
108
+ input: any;
109
+ };
110
+ } | {
111
+ type: "usage";
112
+ usage: {
113
+ inputTokens: number;
114
+ outputTokens: number;
115
+ };
116
+ } | {
117
+ type: "done";
118
+ stopReason: string;
119
+ };
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,36 @@
1
+ import { FlinkContext } from "../FlinkContext";
2
+ import { ToolResult } from "./FlinkTool";
3
+ import { AgentExecuteResult, AgentExecuteContext } from "./FlinkAgent";
4
+ /**
5
+ * Special tool executor that wraps a sub-agent
6
+ * Automatically created for each agent reference in parent agent's `agents` array
7
+ */
8
+ export declare class SubAgentExecutor<Ctx extends FlinkContext> {
9
+ private ctx;
10
+ private subAgentInstanceName;
11
+ private subAgentId;
12
+ constructor(subAgentInstanceName: string, ctx: Ctx);
13
+ /**
14
+ * Execute the sub-agent with the given input
15
+ */
16
+ execute(input: any, user?: any, parentContext?: AgentExecuteContext, // NEW: parent agent context
17
+ userPermissions?: string[]): Promise<ToolResult<AgentExecuteResult>>;
18
+ /**
19
+ * Generate tool schema for this sub-agent
20
+ * LLM sees this as a regular tool
21
+ */
22
+ getToolSchema(): any;
23
+ /**
24
+ * Format sub-agent result for AI consumption
25
+ */
26
+ formatResultForAI(result: ToolResult<AgentExecuteResult>): string;
27
+ /**
28
+ * Check permissions - sub-agents handle their own permissions
29
+ */
30
+ checkPermissions(_user?: any, _input?: any, _userPermissions?: string[]): Promise<boolean>;
31
+ /**
32
+ * Mark this as a sub-agent executor
33
+ */
34
+ isSubAgentExecutor(): boolean;
35
+ getSubAgentId(): string;
36
+ }
@@ -0,0 +1,220 @@
1
+ "use strict";
2
+ var __assign = (this && this.__assign) || function () {
3
+ __assign = Object.assign || function(t) {
4
+ for (var s, i = 1, n = arguments.length; i < n; i++) {
5
+ s = arguments[i];
6
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
7
+ t[p] = s[p];
8
+ }
9
+ return t;
10
+ };
11
+ return __assign.apply(this, arguments);
12
+ };
13
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
14
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
15
+ return new (P || (P = Promise))(function (resolve, reject) {
16
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
17
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
18
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
19
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
20
+ });
21
+ };
22
+ var __generator = (this && this.__generator) || function (thisArg, body) {
23
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
24
+ return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
25
+ function verb(n) { return function (v) { return step([n, v]); }; }
26
+ function step(op) {
27
+ if (f) throw new TypeError("Generator is already executing.");
28
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
29
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
30
+ if (y = 0, t) op = [op[0] & 2, t.value];
31
+ switch (op[0]) {
32
+ case 0: case 1: t = op; break;
33
+ case 4: _.label++; return { value: op[1], done: false };
34
+ case 5: _.label++; y = op[1]; op = [0]; continue;
35
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
36
+ default:
37
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
38
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
39
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
40
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
41
+ if (t[2]) _.ops.pop();
42
+ _.trys.pop(); continue;
43
+ }
44
+ op = body.call(thisArg, _);
45
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
46
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
47
+ }
48
+ };
49
+ Object.defineProperty(exports, "__esModule", { value: true });
50
+ exports.SubAgentExecutor = void 0;
51
+ var zod_1 = require("zod");
52
+ var FlinkLog_1 = require("../FlinkLog");
53
+ var utils_1 = require("../utils");
54
+ var uuid_1 = require("uuid");
55
+ /**
56
+ * Special tool executor that wraps a sub-agent
57
+ * Automatically created for each agent reference in parent agent's `agents` array
58
+ */
59
+ var SubAgentExecutor = /** @class */ (function () {
60
+ function SubAgentExecutor(subAgentInstanceName, ctx) {
61
+ this.ctx = ctx;
62
+ this.subAgentInstanceName = subAgentInstanceName;
63
+ // Convert instance name to kebab-case for consistency with agent IDs
64
+ this.subAgentId = (0, utils_1.toKebabCase)(subAgentInstanceName);
65
+ }
66
+ /**
67
+ * Execute the sub-agent with the given input
68
+ */
69
+ SubAgentExecutor.prototype.execute = function (input, user, parentContext, // NEW: parent agent context
70
+ userPermissions // Resolved permissions from auth plugin
71
+ ) {
72
+ return __awaiter(this, void 0, void 0, function () {
73
+ var agents, subAgent, boundAgent, strategy, conversationId, response, result, err_1;
74
+ var _a;
75
+ return __generator(this, function (_b) {
76
+ switch (_b.label) {
77
+ case 0:
78
+ _b.trys.push([0, 2, , 3]);
79
+ agents = this.ctx.agents;
80
+ if (!agents) {
81
+ return [2 /*return*/, {
82
+ success: false,
83
+ error: "Agents not available in context",
84
+ code: "NO_AGENTS",
85
+ }];
86
+ }
87
+ subAgent = agents[this.subAgentInstanceName];
88
+ if (!subAgent) {
89
+ return [2 /*return*/, {
90
+ success: false,
91
+ error: "Sub-agent ".concat(this.subAgentInstanceName, " not found"),
92
+ code: "AGENT_NOT_FOUND",
93
+ }];
94
+ }
95
+ boundAgent = subAgent;
96
+ if (user) {
97
+ boundAgent = boundAgent.withUser(user);
98
+ }
99
+ if (userPermissions) {
100
+ boundAgent = boundAgent.withPermissions(userPermissions);
101
+ }
102
+ strategy = subAgent.conversationStrategy || "inherit";
103
+ conversationId = void 0;
104
+ if (strategy === "inherit") {
105
+ // Use parent's conversation ID
106
+ conversationId = parentContext === null || parentContext === void 0 ? void 0 : parentContext.conversationId;
107
+ }
108
+ else if (strategy === "independent") {
109
+ // Create nested conversation
110
+ conversationId = (parentContext === null || parentContext === void 0 ? void 0 : parentContext.conversationId)
111
+ ? "".concat(parentContext.conversationId, ":").concat(this.subAgentInstanceName, ":").concat((0, utils_1.generateShortId)())
112
+ : (0, uuid_1.v4)();
113
+ }
114
+ else {
115
+ // strategy === "none"
116
+ conversationId = undefined;
117
+ }
118
+ FlinkLog_1.log.debug("Delegating to sub-agent ".concat(this.subAgentInstanceName, " from parent ").concat((parentContext === null || parentContext === void 0 ? void 0 : parentContext.agentId) || "unknown", ", ") +
119
+ "strategy: \"".concat(strategy, "\", conversationId: ").concat(conversationId || "none"));
120
+ response = boundAgent.run({
121
+ message: input.query || input.message || JSON.stringify(input),
122
+ user: user,
123
+ userPermissions: userPermissions,
124
+ conversationId: conversationId,
125
+ metadata: __assign(__assign({}, input.metadata), { parentConversationId: parentContext === null || parentContext === void 0 ? void 0 : parentContext.conversationId, parentAgentId: parentContext === null || parentContext === void 0 ? void 0 : parentContext.agentId, parentMetadata: parentContext === null || parentContext === void 0 ? void 0 : parentContext.metadata, isSubAgentCall: true, subAgentDepth: ((_a = parentContext === null || parentContext === void 0 ? void 0 : parentContext.subAgentDepth) !== null && _a !== void 0 ? _a : 0) + 1 }),
126
+ });
127
+ return [4 /*yield*/, response.result];
128
+ case 1:
129
+ result = _b.sent();
130
+ FlinkLog_1.log.debug("Sub-agent ".concat(this.subAgentInstanceName, " completed with ").concat(result.stepsUsed, " steps"));
131
+ return [2 /*return*/, {
132
+ success: true,
133
+ data: result,
134
+ }];
135
+ case 2:
136
+ err_1 = _b.sent();
137
+ FlinkLog_1.log.error("Sub-agent ".concat(this.subAgentInstanceName, " execution failed:"), err_1.message);
138
+ return [2 /*return*/, {
139
+ success: false,
140
+ error: "Sub-agent execution failed: ".concat(err_1.message),
141
+ code: "SUB_AGENT_ERROR",
142
+ }];
143
+ case 3: return [2 /*return*/];
144
+ }
145
+ });
146
+ });
147
+ };
148
+ /**
149
+ * Generate tool schema for this sub-agent
150
+ * LLM sees this as a regular tool
151
+ */
152
+ SubAgentExecutor.prototype.getToolSchema = function () {
153
+ var agents = this.ctx.agents;
154
+ if (!(agents === null || agents === void 0 ? void 0 : agents[this.subAgentInstanceName])) {
155
+ throw new Error("Missing subagent: " + this.subAgentInstanceName);
156
+ }
157
+ var subAgent = agents === null || agents === void 0 ? void 0 : agents[this.subAgentInstanceName];
158
+ var description = subAgent ? "Delegate to ".concat(this.subAgentId, ": ").concat(subAgent.description) : "Delegate to ".concat(this.subAgentId);
159
+ // Simple schema - accepts a query string
160
+ var inputSchema = zod_1.z.object({
161
+ query: zod_1.z.string().describe("The question or task to send to the specialist agent"),
162
+ });
163
+ // Convert kebab-case ID to snake_case for tool name (ask_car_agent, not ask_car-agent)
164
+ var toolName = "ask_".concat((0, utils_1.toSnakeCase)(this.subAgentId));
165
+ return {
166
+ name: toolName,
167
+ description: description,
168
+ input_schema: inputSchema.schema
169
+ ? inputSchema.schema()
170
+ : {
171
+ type: "object",
172
+ properties: {
173
+ query: { type: "string" },
174
+ },
175
+ required: ["query"],
176
+ },
177
+ };
178
+ };
179
+ /**
180
+ * Format sub-agent result for AI consumption
181
+ */
182
+ SubAgentExecutor.prototype.formatResultForAI = function (result) {
183
+ var _a, _b;
184
+ if (result.success) {
185
+ var agentResult = result.data;
186
+ // Return the agent's final message as the tool result
187
+ return JSON.stringify({
188
+ answer: agentResult.message,
189
+ stepsUsed: agentResult.stepsUsed,
190
+ tokensUsed: (((_a = agentResult.usage) === null || _a === void 0 ? void 0 : _a.inputTokens) || 0) + (((_b = agentResult.usage) === null || _b === void 0 ? void 0 : _b.outputTokens) || 0),
191
+ });
192
+ }
193
+ else {
194
+ return "Error: ".concat(result.error).concat(result.code ? " (".concat(result.code, ")") : "");
195
+ }
196
+ };
197
+ /**
198
+ * Check permissions - sub-agents handle their own permissions
199
+ */
200
+ SubAgentExecutor.prototype.checkPermissions = function (_user, _input, _userPermissions) {
201
+ return __awaiter(this, void 0, void 0, function () {
202
+ return __generator(this, function (_a) {
203
+ // Sub-agents manage their own permissions
204
+ // The sub-agent's permissions will be checked when executed
205
+ return [2 /*return*/, true];
206
+ });
207
+ });
208
+ };
209
+ /**
210
+ * Mark this as a sub-agent executor
211
+ */
212
+ SubAgentExecutor.prototype.isSubAgentExecutor = function () {
213
+ return true;
214
+ };
215
+ SubAgentExecutor.prototype.getSubAgentId = function () {
216
+ return this.subAgentId;
217
+ };
218
+ return SubAgentExecutor;
219
+ }());
220
+ exports.SubAgentExecutor = SubAgentExecutor;
@@ -0,0 +1,35 @@
1
+ import { FlinkContext } from "../FlinkContext";
2
+ import { FlinkTool, FlinkToolProps, ToolResult } from "./FlinkTool";
3
+ import { FlinkToolSchema } from "./LLMAdapter";
4
+ export declare class ToolExecutor<Ctx extends FlinkContext> {
5
+ private toolProps;
6
+ private toolFn;
7
+ private ctx;
8
+ constructor(toolProps: FlinkToolProps, toolFn: FlinkTool<Ctx, any, any>, ctx: Ctx);
9
+ execute(input: any, user?: any, userPermissions?: string[]): Promise<ToolResult<any>>;
10
+ getToolSchema(): FlinkToolSchema;
11
+ /**
12
+ * Fallback schema generation if toJSONSchema is not available
13
+ */
14
+ private fallbackSchema;
15
+ /**
16
+ * Get tool result for AI consumption
17
+ * Formats ToolResult into string for AI context
18
+ */
19
+ formatResultForAI(result: ToolResult<any>): string;
20
+ /**
21
+ * Check if user has permission to use this tool
22
+ * Used by AgentRunner to filter tools before showing to LLM
23
+ *
24
+ * @param user - User object
25
+ * @param input - Tool input (for function-based permissions)
26
+ * @param userPermissions - Optional resolved permissions from auth plugin (preferred)
27
+ */
28
+ checkPermissions(user?: any, input?: any, userPermissions?: string[]): Promise<boolean>;
29
+ private checkPermissionsInternal;
30
+ /**
31
+ * Format Zod validation errors into LLM-friendly error messages
32
+ * Provides specific guidance on what's missing or incorrect
33
+ */
34
+ private formatValidationError;
35
+ }
@@ -0,0 +1,237 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __generator = (this && this.__generator) || function (thisArg, body) {
12
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
13
+ return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
14
+ function verb(n) { return function (v) { return step([n, v]); }; }
15
+ function step(op) {
16
+ if (f) throw new TypeError("Generator is already executing.");
17
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
18
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
19
+ if (y = 0, t) op = [op[0] & 2, t.value];
20
+ switch (op[0]) {
21
+ case 0: case 1: t = op; break;
22
+ case 4: _.label++; return { value: op[1], done: false };
23
+ case 5: _.label++; y = op[1]; op = [0]; continue;
24
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
25
+ default:
26
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
27
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
28
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
29
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
30
+ if (t[2]) _.ops.pop();
31
+ _.trys.pop(); continue;
32
+ }
33
+ op = body.call(thisArg, _);
34
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
35
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
36
+ }
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.ToolExecutor = void 0;
40
+ var FlinkLog_1 = require("../FlinkLog");
41
+ var ToolExecutor = /** @class */ (function () {
42
+ function ToolExecutor(toolProps, toolFn, ctx) {
43
+ this.toolProps = toolProps;
44
+ this.toolFn = toolFn;
45
+ this.ctx = ctx;
46
+ }
47
+ ToolExecutor.prototype.execute = function (input, user, userPermissions) {
48
+ return __awaiter(this, void 0, void 0, function () {
49
+ var hasPermission, validatedInput, errorDetails, result, err_1, validatedData;
50
+ return __generator(this, function (_a) {
51
+ switch (_a.label) {
52
+ case 0:
53
+ if (!this.toolProps.permissions) return [3 /*break*/, 2];
54
+ return [4 /*yield*/, this.checkPermissionsInternal(input, user, userPermissions)];
55
+ case 1:
56
+ hasPermission = _a.sent();
57
+ if (!hasPermission) {
58
+ return [2 /*return*/, {
59
+ success: false,
60
+ error: "Permission denied for tool ".concat(this.toolProps.id),
61
+ code: "PERMISSION_DENIED",
62
+ }];
63
+ }
64
+ _a.label = 2;
65
+ case 2:
66
+ try {
67
+ validatedInput = this.toolProps.inputSchema.parse(input);
68
+ }
69
+ catch (err) {
70
+ FlinkLog_1.log.warn("Tool ".concat(this.toolProps.id, " input validation failed:"), err.message);
71
+ errorDetails = this.formatValidationError(err, input);
72
+ return [2 /*return*/, {
73
+ success: false,
74
+ error: "Invalid input for tool '".concat(this.toolProps.id, "': ").concat(errorDetails),
75
+ code: "VALIDATION_ERROR",
76
+ }];
77
+ }
78
+ // 3. Execute tool
79
+ FlinkLog_1.log.debug("Executing tool ".concat(this.toolProps.id));
80
+ _a.label = 3;
81
+ case 3:
82
+ _a.trys.push([3, 5, , 6]);
83
+ return [4 /*yield*/, this.toolFn({
84
+ input: validatedInput,
85
+ ctx: this.ctx,
86
+ user: user,
87
+ })];
88
+ case 4:
89
+ result = _a.sent();
90
+ return [3 /*break*/, 6];
91
+ case 5:
92
+ err_1 = _a.sent();
93
+ FlinkLog_1.log.error("Tool ".concat(this.toolProps.id, " threw error:"), err_1.message);
94
+ return [2 /*return*/, {
95
+ success: false,
96
+ error: "Tool execution failed: ".concat(err_1.message),
97
+ code: "EXECUTION_ERROR",
98
+ }];
99
+ case 6:
100
+ // 4. Handle error results
101
+ if (!result.success) {
102
+ FlinkLog_1.log.warn("Tool ".concat(this.toolProps.id, " returned error:"), result.error);
103
+ return [2 /*return*/, result]; // Return error result as-is
104
+ }
105
+ // 5. Output validation (if schema provided)
106
+ if (this.toolProps.outputSchema) {
107
+ try {
108
+ validatedData = this.toolProps.outputSchema.parse(result.data);
109
+ return [2 /*return*/, { success: true, data: validatedData }];
110
+ }
111
+ catch (err) {
112
+ FlinkLog_1.log.error("Tool ".concat(this.toolProps.id, " output validation failed:"), err.message);
113
+ return [2 /*return*/, {
114
+ success: false,
115
+ error: "Invalid output from tool ".concat(this.toolProps.id, ": ").concat(err.message),
116
+ code: "OUTPUT_VALIDATION_ERROR",
117
+ }];
118
+ }
119
+ }
120
+ return [2 /*return*/, result];
121
+ }
122
+ });
123
+ });
124
+ };
125
+ ToolExecutor.prototype.getToolSchema = function () {
126
+ // Use Zod 4's built-in z.toJSONSchema()
127
+ var zodSchema = this.toolProps.inputSchema;
128
+ var z = require("zod");
129
+ return {
130
+ name: this.toolProps.id,
131
+ description: this.toolProps.description,
132
+ inputSchema: z.toJSONSchema ? z.toJSONSchema(zodSchema) : this.fallbackSchema(zodSchema),
133
+ };
134
+ };
135
+ /**
136
+ * Fallback schema generation if toJSONSchema is not available
137
+ */
138
+ ToolExecutor.prototype.fallbackSchema = function (zodSchema) {
139
+ // Try to use _def to extract basic schema info
140
+ FlinkLog_1.log.warn("Tool ".concat(this.toolProps.id, ": z.toJSONSchema() not available, using fallback schema generation"));
141
+ return {
142
+ type: "object",
143
+ properties: {},
144
+ required: [],
145
+ };
146
+ };
147
+ /**
148
+ * Get tool result for AI consumption
149
+ * Formats ToolResult into string for AI context
150
+ */
151
+ ToolExecutor.prototype.formatResultForAI = function (result) {
152
+ if (result.success) {
153
+ return JSON.stringify(result.data);
154
+ }
155
+ else {
156
+ return "Error: ".concat(result.error).concat(result.code ? " (".concat(result.code, ")") : "");
157
+ }
158
+ };
159
+ /**
160
+ * Check if user has permission to use this tool
161
+ * Used by AgentRunner to filter tools before showing to LLM
162
+ *
163
+ * @param user - User object
164
+ * @param input - Tool input (for function-based permissions)
165
+ * @param userPermissions - Optional resolved permissions from auth plugin (preferred)
166
+ */
167
+ ToolExecutor.prototype.checkPermissions = function (user, input, userPermissions) {
168
+ return __awaiter(this, void 0, void 0, function () {
169
+ var perms, effectivePerms, requiredPerms;
170
+ return __generator(this, function (_a) {
171
+ switch (_a.label) {
172
+ case 0:
173
+ perms = this.toolProps.permissions;
174
+ if (!perms)
175
+ return [2 /*return*/, true];
176
+ if (!(typeof perms === "function")) return [3 /*break*/, 2];
177
+ return [4 /*yield*/, perms(input !== null && input !== void 0 ? input : {}, user)];
178
+ case 1: return [2 /*return*/, _a.sent()];
179
+ case 2:
180
+ if (!user)
181
+ return [2 /*return*/, false];
182
+ effectivePerms = userPermissions || user.permissions || [];
183
+ requiredPerms = Array.isArray(perms) ? perms : [perms];
184
+ return [2 /*return*/, requiredPerms.every(function (p) { return effectivePerms.includes(p); })];
185
+ }
186
+ });
187
+ });
188
+ };
189
+ ToolExecutor.prototype.checkPermissionsInternal = function (input, user, userPermissions) {
190
+ return __awaiter(this, void 0, void 0, function () {
191
+ return __generator(this, function (_a) {
192
+ return [2 /*return*/, this.checkPermissions(user, input, userPermissions)];
193
+ });
194
+ });
195
+ };
196
+ /**
197
+ * Format Zod validation errors into LLM-friendly error messages
198
+ * Provides specific guidance on what's missing or incorrect
199
+ */
200
+ ToolExecutor.prototype.formatValidationError = function (err, receivedInput) {
201
+ // Check if it's a Zod error with issues array
202
+ if (err.issues && Array.isArray(err.issues)) {
203
+ var issues = err.issues.map(function (issue) {
204
+ var path = issue.path.join('.');
205
+ var field = path || 'input';
206
+ if (issue.code === 'invalid_type') {
207
+ if (issue.received === 'undefined') {
208
+ return "Missing required field '".concat(field, "' (expected ").concat(issue.expected, ")");
209
+ }
210
+ return "Field '".concat(field, "' has wrong type: expected ").concat(issue.expected, ", got ").concat(issue.received);
211
+ }
212
+ if (issue.code === 'too_small') {
213
+ if (issue.type === 'string') {
214
+ return "Field '".concat(field, "' is too short (minimum ").concat(issue.minimum, " characters)");
215
+ }
216
+ return "Field '".concat(field, "' is too small (minimum ").concat(issue.minimum, ")");
217
+ }
218
+ if (issue.code === 'too_big') {
219
+ if (issue.type === 'string') {
220
+ return "Field '".concat(field, "' is too long (maximum ").concat(issue.maximum, " characters)");
221
+ }
222
+ return "Field '".concat(field, "' is too large (maximum ").concat(issue.maximum, ")");
223
+ }
224
+ // Generic fallback
225
+ return "".concat(field, ": ").concat(issue.message);
226
+ });
227
+ var inputInfo = Object.keys(receivedInput || {}).length === 0
228
+ ? 'You provided an empty object {}.'
229
+ : "You provided: ".concat(JSON.stringify(receivedInput));
230
+ return "".concat(issues.join('; '), ". ").concat(inputInfo);
231
+ }
232
+ // Fallback for non-Zod errors
233
+ return err.message || 'Unknown validation error';
234
+ };
235
+ return ToolExecutor;
236
+ }());
237
+ exports.ToolExecutor = ToolExecutor;
@@ -0,0 +1,5 @@
1
+ export * from "./FlinkTool";
2
+ export * from "./FlinkAgent";
3
+ export * from "./ToolExecutor";
4
+ export * from "./AgentRunner";
5
+ export * from "./LLMAdapter";