@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.
- package/CHANGELOG.md +12 -0
- package/cli/build.ts +8 -1
- package/cli/run.ts +8 -1
- package/dist/cli/build.js +8 -1
- package/dist/cli/run.js +8 -1
- package/dist/src/FlinkApp.d.ts +33 -0
- package/dist/src/FlinkApp.js +247 -27
- package/dist/src/FlinkContext.d.ts +21 -0
- package/dist/src/FlinkHttpHandler.d.ts +90 -1
- package/dist/src/TypeScriptCompiler.d.ts +42 -0
- package/dist/src/TypeScriptCompiler.js +366 -8
- package/dist/src/TypeScriptUtils.js +4 -0
- package/dist/src/ai/AgentRunner.d.ts +39 -0
- package/dist/src/ai/AgentRunner.js +625 -0
- package/dist/src/ai/FlinkAgent.d.ts +446 -0
- package/dist/src/ai/FlinkAgent.js +633 -0
- package/dist/src/ai/FlinkTool.d.ts +37 -0
- package/dist/src/ai/FlinkTool.js +2 -0
- package/dist/src/ai/LLMAdapter.d.ts +119 -0
- package/dist/src/ai/LLMAdapter.js +2 -0
- package/dist/src/ai/SubAgentExecutor.d.ts +36 -0
- package/dist/src/ai/SubAgentExecutor.js +220 -0
- package/dist/src/ai/ToolExecutor.d.ts +35 -0
- package/dist/src/ai/ToolExecutor.js +237 -0
- package/dist/src/ai/index.d.ts +5 -0
- package/dist/src/ai/index.js +21 -0
- package/dist/src/handlers/StreamWriterFactory.d.ts +20 -0
- package/dist/src/handlers/StreamWriterFactory.js +83 -0
- package/dist/src/index.d.ts +4 -0
- package/dist/src/index.js +4 -0
- package/dist/src/utils.d.ts +30 -0
- package/dist/src/utils.js +52 -0
- package/package.json +14 -2
- package/readme.md +425 -0
- package/spec/AgentDuplicateDetection.spec.ts +112 -0
- package/spec/AgentRunner.spec.ts +527 -0
- package/spec/ConversationHooks.spec.ts +290 -0
- package/spec/FlinkAgent.spec.ts +310 -0
- package/spec/FlinkApp.onError.spec.ts +1 -2
- package/spec/StreamingIntegration.spec.ts +138 -0
- package/spec/SubAgentSupport.spec.ts +941 -0
- package/spec/ToolExecutor.spec.ts +360 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/GetCar.js +57 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/GetCar2.js +59 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/GetCarWithArraySchema.js +53 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/GetCarWithArraySchema2.js +53 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/GetCarWithArraySchema3.js +53 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/GetCarWithLiteralSchema.js +55 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/GetCarWithLiteralSchema2.js +55 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/GetCarWithSchemaInFile.js +58 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/GetCarWithSchemaInFile2.js +58 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/ManuallyAddedHandler.js +53 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/ManuallyAddedHandler2.js +55 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/PatchCar.js +58 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/PatchOnboardingSession.js +76 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/PatchOrderWithComplexTypes.js +58 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/PatchProductWithIntersection.js +59 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/PatchUserWithUnion.js +59 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/PostCar.js +55 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/PostLogin.js +56 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/PostLogout.js +55 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/PutCar.js +55 -0
- package/spec/mock-project/dist/spec/mock-project/src/index.js +83 -0
- package/spec/mock-project/dist/spec/mock-project/src/repos/CarRepo.js +26 -0
- package/spec/mock-project/dist/spec/mock-project/src/schemas/Car.js +2 -0
- package/spec/mock-project/dist/spec/mock-project/src/schemas/DefaultExportSchema.js +2 -0
- package/spec/mock-project/dist/spec/mock-project/src/schemas/FileWithTwoSchemas.js +2 -0
- package/spec/mock-project/dist/src/FlinkApp.js +1012 -0
- package/spec/mock-project/dist/src/FlinkContext.js +2 -0
- package/spec/mock-project/dist/src/FlinkErrors.js +143 -0
- package/spec/mock-project/dist/src/FlinkHttpHandler.js +47 -0
- package/spec/mock-project/dist/src/FlinkJob.js +2 -0
- package/spec/mock-project/dist/src/FlinkLog.js +26 -0
- package/spec/mock-project/dist/src/FlinkPlugin.js +2 -0
- package/spec/mock-project/dist/src/FlinkRepo.js +224 -0
- package/spec/mock-project/dist/src/FlinkResponse.js +2 -0
- package/spec/mock-project/dist/src/ai/AgentExecutor.js +279 -0
- package/spec/mock-project/dist/src/ai/AgentRunner.js +625 -0
- package/spec/mock-project/dist/src/ai/FlinkAgent.js +633 -0
- package/spec/mock-project/dist/src/ai/FlinkTool.js +2 -0
- package/spec/mock-project/dist/src/ai/LLMAdapter.js +2 -0
- package/spec/mock-project/dist/src/ai/SubAgentExecutor.js +220 -0
- package/spec/mock-project/dist/src/ai/ToolExecutor.js +237 -0
- package/spec/mock-project/dist/src/auth/FlinkAuthPlugin.js +2 -0
- package/spec/mock-project/dist/src/auth/FlinkAuthUser.js +2 -0
- package/spec/mock-project/dist/src/handlers/StreamWriterFactory.js +83 -0
- package/spec/mock-project/dist/src/index.js +17 -69
- package/spec/mock-project/dist/src/mock-data-generator.js +9 -0
- package/spec/mock-project/dist/src/utils.js +290 -0
- package/spec/mock-project/tsconfig.json +6 -1
- package/spec/testHelpers.ts +49 -0
- package/spec/utils.caseConversion.spec.ts +80 -0
- package/spec/utils.spec.ts +13 -13
- package/src/FlinkApp.ts +251 -7
- package/src/FlinkContext.ts +22 -0
- package/src/FlinkHttpHandler.ts +100 -2
- package/src/TypeScriptCompiler.ts +420 -9
- package/src/TypeScriptUtils.ts +5 -0
- package/src/ai/AgentRunner.ts +549 -0
- package/src/ai/FlinkAgent.ts +770 -0
- package/src/ai/FlinkTool.ts +40 -0
- package/src/ai/LLMAdapter.ts +96 -0
- package/src/ai/SubAgentExecutor.ts +199 -0
- package/src/ai/ToolExecutor.ts +193 -0
- package/src/ai/index.ts +5 -0
- package/src/handlers/StreamWriterFactory.ts +84 -0
- package/src/index.ts +4 -0
- package/src/utils.ts +52 -0
- 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,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;
|