@gt-fe/eap-runtime 0.0.33 → 0.0.35
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/dist/api/handlers/invoke.handler.d.ts +0 -1
- package/dist/api/handlers/invoke.handler.js +22 -5
- package/dist/api/handlers/stream.handler.d.ts +0 -1
- package/dist/api/handlers/stream.handler.js +30 -23
- package/dist/common/types.d.ts +4 -3
- package/dist/graph/adapters/langgraph.adapter.js +181 -86
- package/dist/graph/adapters/subgraph-compiler.js +25 -0
- package/dist/graph/contracts/executable-graph.d.ts +9 -0
- package/dist/graph/execution-trace-context.d.ts +3 -0
- package/dist/graph/nodes/llm-node.d.ts +7 -2
- package/dist/graph/nodes/llm-node.js +11 -4
- package/dist/graph/nodes/tool-node.d.ts +4 -0
- package/dist/graph/nodes/tool-node.js +132 -4
- package/dist/providers/http-llm-provider.js +27 -10
- package/dist/registry/graph-loader.js +10 -1
- package/dist/runtime/agent-runtime.d.ts +7 -0
- package/dist/runtime/agent-runtime.js +91 -50
- package/dist/runtime/types.d.ts +1 -1
- package/dist/session/persist-turn-messages.d.ts +10 -2
- package/dist/session/persist-turn-messages.js +35 -10
- package/dist/sse/events/index.d.ts +6 -4
- package/dist/sse/events/index.js +3 -1
- package/dist/sse/events/reasoning-events.d.ts +24 -2
- package/dist/sse/events/reasoning-events.js +3 -1
- package/dist/sse/events/skill-events.d.ts +2 -0
- package/dist/task/contracts/task-manager.contract.d.ts +2 -2
- package/dist/task/contracts/task-manager.contract.js +1 -1
- package/dist/task/index.d.ts +2 -0
- package/dist/task/index.js +1 -0
- package/dist/task/task-abort.d.ts +14 -0
- package/dist/task/task-abort.js +33 -0
- package/dist/task/task-timeout.d.ts +11 -0
- package/dist/task/task-timeout.js +40 -4
- package/dist/task/task.service.js +11 -5
- package/dist/tool/index.d.ts +2 -1
- package/dist/tool/index.js +2 -1
- package/dist/tool/mcp-tool-exposure.d.ts +113 -0
- package/dist/tool/mcp-tool-exposure.js +479 -0
- package/dist/tool/system-prompt-builder.d.ts +13 -10
- package/dist/tool/system-prompt-builder.js +18 -32
- package/package.json +1 -1
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
* @see requirements.md Requirement 7 AC4
|
|
11
11
|
*/
|
|
12
12
|
import { DEFAULT_GRAPH_TIMEOUT_MS } from '../common/types.js';
|
|
13
|
+
import { TaskAbortError } from './task-abort.js';
|
|
13
14
|
// ─────────────────────────────────────────────
|
|
14
15
|
// Default Constants
|
|
15
16
|
// ─────────────────────────────────────────────
|
|
@@ -31,6 +32,11 @@ const MAX_TIMEOUT_MS = 3_600_000;
|
|
|
31
32
|
*/
|
|
32
33
|
export class TaskTimeoutManager {
|
|
33
34
|
entries = new Map();
|
|
35
|
+
/**
|
|
36
|
+
* startTimer 之前到达的停止。startTimer 消费后返回已 abort 的 signal,且不调用 onTimeout。
|
|
37
|
+
* clearTimer 只在计时器已经启动时清掉残留,避免把还没被 startTimer 看到的停止丢掉。
|
|
38
|
+
*/
|
|
39
|
+
pendingAborts = new Map();
|
|
34
40
|
defaultTimeoutMs;
|
|
35
41
|
constructor(options) {
|
|
36
42
|
this.defaultTimeoutMs = options?.defaultTimeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
@@ -49,12 +55,22 @@ export class TaskTimeoutManager {
|
|
|
49
55
|
throw new Error(`Timeout timer already exists for task '${taskId}'. Call clearTimer() first.`);
|
|
50
56
|
}
|
|
51
57
|
const effectiveTimeout = Math.min(Math.max(timeoutMs ?? this.defaultTimeoutMs, 0), MAX_TIMEOUT_MS);
|
|
58
|
+
const pendingKind = this.pendingAborts.get(taskId);
|
|
59
|
+
if (pendingKind) {
|
|
60
|
+
this.pendingAborts.delete(taskId);
|
|
61
|
+
const already = new AbortController();
|
|
62
|
+
already.abort(new TaskAbortError(taskId, pendingKind, effectiveTimeout));
|
|
63
|
+
return already.signal;
|
|
64
|
+
}
|
|
52
65
|
const abortController = new AbortController();
|
|
53
66
|
const timer = setTimeout(() => {
|
|
54
|
-
//
|
|
55
|
-
|
|
67
|
+
// abort() 已取走条目时不再标 timeout
|
|
68
|
+
if (!this.entries.has(taskId))
|
|
69
|
+
return;
|
|
56
70
|
this.entries.delete(taskId);
|
|
57
|
-
|
|
71
|
+
if (!abortController.signal.aborted) {
|
|
72
|
+
abortController.abort(new TaskAbortError(taskId, 'timeout', effectiveTimeout));
|
|
73
|
+
}
|
|
58
74
|
void onTimeout().catch(() => {
|
|
59
75
|
// onTimeout 回调的错误由调用方处理,这里静默忽略
|
|
60
76
|
});
|
|
@@ -63,9 +79,27 @@ export class TaskTimeoutManager {
|
|
|
63
79
|
if (timer.unref) {
|
|
64
80
|
timer.unref();
|
|
65
81
|
}
|
|
66
|
-
this.entries.set(taskId, { timer, abortController });
|
|
82
|
+
this.entries.set(taskId, { timer, abortController, timeoutMs: effectiveTimeout });
|
|
67
83
|
return abortController.signal;
|
|
68
84
|
}
|
|
85
|
+
/**
|
|
86
|
+
* 停止这一轮。清掉计时器,不调用 onTimeout。
|
|
87
|
+
* 计时器还没启动时记下 kind,等 startTimer 返回已 abort 的 signal。
|
|
88
|
+
*/
|
|
89
|
+
abort(taskId, kind = 'cancelled') {
|
|
90
|
+
this.pendingAborts.delete(taskId);
|
|
91
|
+
const entry = this.entries.get(taskId);
|
|
92
|
+
if (!entry) {
|
|
93
|
+
this.pendingAborts.set(taskId, kind);
|
|
94
|
+
return true;
|
|
95
|
+
}
|
|
96
|
+
clearTimeout(entry.timer);
|
|
97
|
+
this.entries.delete(taskId);
|
|
98
|
+
if (!entry.abortController.signal.aborted) {
|
|
99
|
+
entry.abortController.abort(new TaskAbortError(taskId, kind, entry.timeoutMs));
|
|
100
|
+
}
|
|
101
|
+
return true;
|
|
102
|
+
}
|
|
69
103
|
/**
|
|
70
104
|
* 取消超时计时器(Task 正常完成时调用)
|
|
71
105
|
*
|
|
@@ -79,6 +113,7 @@ export class TaskTimeoutManager {
|
|
|
79
113
|
}
|
|
80
114
|
clearTimeout(entry.timer);
|
|
81
115
|
this.entries.delete(taskId);
|
|
116
|
+
this.pendingAborts.delete(taskId);
|
|
82
117
|
return true;
|
|
83
118
|
}
|
|
84
119
|
/**
|
|
@@ -113,5 +148,6 @@ export class TaskTimeoutManager {
|
|
|
113
148
|
entry.abortController.abort(new Error(`TaskTimeoutManager disposed, task '${taskId}' aborted`));
|
|
114
149
|
}
|
|
115
150
|
this.entries.clear();
|
|
151
|
+
this.pendingAborts.clear();
|
|
116
152
|
}
|
|
117
153
|
}
|
|
@@ -16,16 +16,17 @@ import { TaskProgressTracker } from './task-progress-tracker.js';
|
|
|
16
16
|
* 合法状态流转映射
|
|
17
17
|
*
|
|
18
18
|
* pending → running
|
|
19
|
-
* running → success | failed | timeout | token_exhausted
|
|
19
|
+
* running → success | failed | timeout | token_exhausted | cancelled | waiting_hitl
|
|
20
20
|
*/
|
|
21
21
|
const VALID_TRANSITIONS = {
|
|
22
|
-
pending: ['running'],
|
|
23
|
-
running: ['success', 'failed', 'timeout', 'token_exhausted', 'waiting_hitl'],
|
|
24
|
-
waiting_hitl: ['running', 'timeout', 'failed'],
|
|
22
|
+
pending: ['running', 'cancelled'],
|
|
23
|
+
running: ['success', 'failed', 'timeout', 'token_exhausted', 'cancelled', 'waiting_hitl'],
|
|
24
|
+
waiting_hitl: ['running', 'timeout', 'failed', 'cancelled'],
|
|
25
25
|
success: [],
|
|
26
26
|
failed: [],
|
|
27
27
|
timeout: [],
|
|
28
28
|
token_exhausted: [],
|
|
29
|
+
cancelled: [],
|
|
29
30
|
};
|
|
30
31
|
// ─────────────────────────────────────────────
|
|
31
32
|
// In-Memory Repository (Default)
|
|
@@ -128,7 +129,11 @@ export class TaskService {
|
|
|
128
129
|
if (status === 'waiting_hitl' && output !== undefined) {
|
|
129
130
|
task.output = output;
|
|
130
131
|
}
|
|
131
|
-
if (status === 'success' ||
|
|
132
|
+
if (status === 'success' ||
|
|
133
|
+
status === 'failed' ||
|
|
134
|
+
status === 'timeout' ||
|
|
135
|
+
status === 'token_exhausted' ||
|
|
136
|
+
status === 'cancelled') {
|
|
132
137
|
task.completedAt = now;
|
|
133
138
|
if (output !== undefined) {
|
|
134
139
|
task.output = output;
|
|
@@ -215,6 +220,7 @@ export class TaskService {
|
|
|
215
220
|
case 'failed':
|
|
216
221
|
case 'timeout':
|
|
217
222
|
case 'token_exhausted':
|
|
223
|
+
case 'cancelled':
|
|
218
224
|
return 100;
|
|
219
225
|
default:
|
|
220
226
|
return 0;
|
package/dist/tool/index.d.ts
CHANGED
|
@@ -22,4 +22,5 @@ export { expandSkillScriptRefs, isSkillEntryFile, EAP_SKILL_DIR_ENV, } from './s
|
|
|
22
22
|
export { ToolResultInjector, type ToolMessage, } from './tool-result-injector.js';
|
|
23
23
|
export { ToolEventEmitter } from './tool-event-emitter.js';
|
|
24
24
|
export { ToolConfirmationInterceptor } from './tool-confirmation.js';
|
|
25
|
-
export {
|
|
25
|
+
export { TOOL_SEARCH_NAME, buildMcpToolIndex, deferMcpToolsEnabled, resolveModelToolSurface, runToolSearch, } from './mcp-tool-exposure.js';
|
|
26
|
+
export { SystemPromptBuilder, naturalTimeWindows, buildSkillCatalog, catalogEntryTitle, spokenName, loadedToolsToApiTools, toLoadedSkillMap, filterLoadedToolsForSkill, type PromptBuildContext, type PromptSection, type AssembledSystemPrompt, type ApiToolDefinition, type LoadedSkillInfo, type LoadedSkillMap, } from './system-prompt-builder.js';
|
package/dist/tool/index.js
CHANGED
|
@@ -21,4 +21,5 @@ export { expandSkillScriptRefs, isSkillEntryFile, EAP_SKILL_DIR_ENV, } from './s
|
|
|
21
21
|
export { ToolResultInjector, } from './tool-result-injector.js';
|
|
22
22
|
export { ToolEventEmitter } from './tool-event-emitter.js';
|
|
23
23
|
export { ToolConfirmationInterceptor } from './tool-confirmation.js';
|
|
24
|
-
export {
|
|
24
|
+
export { TOOL_SEARCH_NAME, buildMcpToolIndex, deferMcpToolsEnabled, resolveModelToolSurface, runToolSearch, } from './mcp-tool-exposure.js';
|
|
25
|
+
export { SystemPromptBuilder, naturalTimeWindows, buildSkillCatalog, catalogEntryTitle, spokenName, loadedToolsToApiTools, toLoadedSkillMap, filterLoadedToolsForSkill, } from './system-prompt-builder.js';
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 每轮发给模型的 function schema 按工具来源决定。
|
|
3
|
+
*
|
|
4
|
+
* builtin(execution.source=builtin)以及 type 为 code / http 的工具,始终带完整参数。
|
|
5
|
+
* type=mcp 且不是 builtin 的,默认不带参数;名称和一行用途写入 system 段 mcp-tools。
|
|
6
|
+
* 点名,或没点名就调用,都不执行;名字记入本会话,下一次请求带上当前参数后再打外部系统。
|
|
7
|
+
* 没点名的那句只给模型。员工看模型正文和真正的工具结果(含飞书报错、用户拒绝),不另写未执行。
|
|
8
|
+
* 新会话不沿用。没有 sessionId 时按 taskId。EAP_DEFER_MCP_TOOLS=0 关闭,恢复为全部 schema 都下发。
|
|
9
|
+
*/
|
|
10
|
+
import type { GraphMessage } from '../graph/contracts/executable-graph.js';
|
|
11
|
+
import type { LLMNodeConfig } from '../graph/nodes/llm-node.js';
|
|
12
|
+
import type { LoadedToolDefinition, LoadedToolMap } from './loaded-tool-definition.js';
|
|
13
|
+
import { type ApiToolDefinition } from './system-prompt-builder.js';
|
|
14
|
+
export declare const TOOL_SEARCH_NAME = "tool_search";
|
|
15
|
+
export declare function deferMcpToolsEnabled(env?: Record<string, string | undefined>): boolean;
|
|
16
|
+
export declare function isDeferredMcpTool(tool: LoadedToolDefinition): boolean;
|
|
17
|
+
export declare function hasDeferredMcp(loadedTools: LoadedToolMap | undefined): boolean;
|
|
18
|
+
/** 具名 toolChoice 指向的函数。auto / none / required 没有名字。 */
|
|
19
|
+
export declare function forcedToolName(toolChoice: LLMNodeConfig['toolChoice'] | unknown): string | undefined;
|
|
20
|
+
/** 有 sessionId 用会话,否则用 taskId。空字符串表示无处可记。 */
|
|
21
|
+
export declare function exposureScopeId(input: {
|
|
22
|
+
sessionId?: unknown;
|
|
23
|
+
taskId?: unknown;
|
|
24
|
+
}): string;
|
|
25
|
+
export declare function exposedNamesForScope(names: unknown, storedScopeId: unknown, scopeId: string): string[];
|
|
26
|
+
export declare function mergeExposed(current: readonly string[], additions: readonly string[]): string[];
|
|
27
|
+
export interface ModelToolSurface {
|
|
28
|
+
apiTools: ApiToolDefinition[];
|
|
29
|
+
/** 段 mcp-tools 的正文。没有要推迟的 MCP、或本轮 disableTools、或开关关闭时缺省。 */
|
|
30
|
+
mcpToolIndex?: string;
|
|
31
|
+
/** 本轮是否按来源推迟了 MCP 参数。 */
|
|
32
|
+
deferred: boolean;
|
|
33
|
+
}
|
|
34
|
+
export declare function resolveModelToolSurface(input: {
|
|
35
|
+
loadedTools: LoadedToolMap | undefined;
|
|
36
|
+
disableTools: boolean;
|
|
37
|
+
exposedNames: readonly string[];
|
|
38
|
+
forcedName?: string;
|
|
39
|
+
env?: Record<string, string | undefined>;
|
|
40
|
+
}): ModelToolSurface;
|
|
41
|
+
/** 全部推迟中的 MCP:名称 + 现有 description,不截断,不含参数 schema。 */
|
|
42
|
+
export declare function buildMcpToolIndex(loadedTools: LoadedToolMap | undefined): string | undefined;
|
|
43
|
+
export declare function toolSearchApiTool(): ApiToolDefinition;
|
|
44
|
+
export interface ToolSearchOutcome {
|
|
45
|
+
content: string;
|
|
46
|
+
/** 这些 MCP 名字的完整参数从下一次模型调用开始带上。 */
|
|
47
|
+
expose: string[];
|
|
48
|
+
}
|
|
49
|
+
export declare function unloadedCallToolContent(name: string): string;
|
|
50
|
+
export declare function isUnloadedDeferredCall(tool: LoadedToolDefinition | undefined, sentToolNames: readonly string[] | undefined): boolean;
|
|
51
|
+
export declare function runToolSearch(args: Record<string, unknown> | undefined, loadedTools: LoadedToolMap | undefined, alreadyInTools?: readonly string[]): ToolSearchOutcome;
|
|
52
|
+
export declare function partitionToolSearchCalls<T extends {
|
|
53
|
+
name: string;
|
|
54
|
+
}>(calls: T[], loadedTools: LoadedToolMap | undefined, env?: Record<string, string | undefined>): {
|
|
55
|
+
deferActive: boolean;
|
|
56
|
+
intercept: boolean;
|
|
57
|
+
search: T[];
|
|
58
|
+
business: T[];
|
|
59
|
+
};
|
|
60
|
+
/** tool_search 点名,以及本轮调用过的 MCP,记入本会话。直接调用这一次不执行。 */
|
|
61
|
+
export declare function mcpNamesToExpose(loadedTools: LoadedToolMap | undefined, searchExpose: readonly string[], businessNames: readonly string[]): string[];
|
|
62
|
+
export interface VisibleToolFact {
|
|
63
|
+
name?: string;
|
|
64
|
+
toolName?: string;
|
|
65
|
+
content?: unknown;
|
|
66
|
+
success?: boolean;
|
|
67
|
+
}
|
|
68
|
+
/** 发给模型的历史。不改会话存档。只处理这次 tools 里没有的名字。 */
|
|
69
|
+
export declare function projectHistoryForModelTools(messages: GraphMessage[], toolNames: ReadonlySet<string>): GraphMessage[];
|
|
70
|
+
/**
|
|
71
|
+
* 员工可见正文。未点名、只报了名,都不改模型的话。
|
|
72
|
+
* 用户拒绝仍补一行。飞书自己的成功或错误不改。
|
|
73
|
+
*/
|
|
74
|
+
export declare function correctEmployeeVisibleReply(visible: string, tools: readonly VisibleToolFact[]): string;
|
|
75
|
+
/**
|
|
76
|
+
* 还有未点名的 MCP 时,这段模型正文先攒着,等这条消息落定再决定发不发给员工。
|
|
77
|
+
* 全部已经在本次 tools 里,或本次没有 tools,仍逐 token 推。
|
|
78
|
+
*/
|
|
79
|
+
export declare function holdEmployeeTextForUnnamedMcp(loadedTools: LoadedToolMap | undefined, sentToolNames: readonly string[]): boolean;
|
|
80
|
+
/**
|
|
81
|
+
* 这条模型消息还不能当员工结论:带着没点名的调用,或这一次只点了名。
|
|
82
|
+
* 没有工具调用时不丢,交给回合结束时的闸门。
|
|
83
|
+
*/
|
|
84
|
+
export declare function shouldDropUnsettledEmployeeText(calls: readonly {
|
|
85
|
+
name: string;
|
|
86
|
+
}[] | undefined, loadedTools: LoadedToolMap | undefined, sentToolNames: readonly string[] | undefined): boolean;
|
|
87
|
+
export interface EmployeeHeldEvent {
|
|
88
|
+
type: string;
|
|
89
|
+
payload: Record<string, unknown>;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* 没点名的工具事件和 tool_search 不进员工 SSE。
|
|
93
|
+
* 判定:`modelOnly`,或工具名是 `tool_search`,或正文以 `未调用 ` 开头。
|
|
94
|
+
*/
|
|
95
|
+
export declare function takeEmployeeVisibleEvents(eventType: string, payload: unknown): EmployeeHeldEvent[];
|
|
96
|
+
export interface EmployeeTurnClose {
|
|
97
|
+
visible: string;
|
|
98
|
+
/** 用户拒绝时补在最后的那几行。带开头换行。没有则为空。 */
|
|
99
|
+
tailChunk: string;
|
|
100
|
+
}
|
|
101
|
+
export declare function employeeTurnClose(raw: string, tools: readonly VisibleToolFact[]): EmployeeTurnClose;
|
|
102
|
+
/** 回合结束时要补发的 response.chunk。final 仍是 injected。 */
|
|
103
|
+
export declare function employeeStreamClose(input: {
|
|
104
|
+
raw: string;
|
|
105
|
+
tools: readonly VisibleToolFact[];
|
|
106
|
+
suffix?: string;
|
|
107
|
+
}): {
|
|
108
|
+
chunks: string[];
|
|
109
|
+
persistCorrected: boolean;
|
|
110
|
+
};
|
|
111
|
+
export declare function toolFactsSinceLastUser(messages: readonly unknown[]): VisibleToolFact[];
|
|
112
|
+
/** 本轮最后一条助手正文。图若停在 tool 节点,不用工具正文当员工结论。 */
|
|
113
|
+
export declare function lastAssistantTextSinceLastUser(messages: readonly unknown[]): string;
|