@gt-fe/eap-runtime 0.0.30 → 0.0.31
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.js +9 -5
- package/dist/api/handlers/stream.handler.js +15 -4
- package/dist/common/index.d.ts +1 -0
- package/dist/common/index.js +1 -0
- package/dist/common/user-root.d.ts +8 -0
- package/dist/common/user-root.js +19 -0
- package/dist/graph/adapters/langgraph.adapter.js +30 -3
- package/dist/graph/adapters/skill-read.js +13 -4
- package/dist/graph/contracts/executable-graph.d.ts +7 -1
- package/dist/graph/nodes/tool-node.d.ts +4 -1
- package/dist/graph/nodes/tool-node.js +15 -1
- package/dist/registry/deployment-snapshot.d.ts +1 -1
- package/dist/registry/deployment-snapshot.js +6 -3
- package/dist/registry/skill-tree.d.ts +1 -1
- package/dist/registry/skill-tree.js +11 -6
- package/dist/runtime/agent-runtime.js +36 -5
- package/dist/session/persist-turn-messages.d.ts +13 -6
- package/dist/session/persist-turn-messages.js +4 -0
- package/dist/sse/events/tool-events.d.ts +29 -0
- package/dist/tool/harness-command.d.ts +1 -1
- package/dist/tool/harness-command.js +8 -7
- package/dist/tool/harness-primitives.d.ts +3 -3
- package/dist/tool/harness-primitives.js +185 -60
- package/dist/tool/index.d.ts +2 -0
- package/dist/tool/index.js +2 -0
- package/dist/tool/inject-public-artifact-links.d.ts +32 -0
- package/dist/tool/inject-public-artifact-links.js +142 -0
- package/dist/tool/static-artifact-url.d.ts +66 -0
- package/dist/tool/static-artifact-url.js +561 -0
- package/dist/tool/system-prompt-builder.js +8 -4
- package/dist/tool/tool-event-emitter.d.ts +2 -1
- package/dist/tool/tool-event-emitter.js +30 -18
- package/dist/tool/tool-executor.interface.d.ts +8 -1
- package/dist/tool/tool-result-injector.d.ts +3 -3
- package/dist/tool/tool-result-injector.js +7 -3
- package/package.json +2 -2
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
*/
|
|
17
17
|
import { randomUUID } from 'node:crypto';
|
|
18
18
|
import { buildHitlWaitingTaskOutput, loadModelFacingHistory, messageRecordsToGraphMessages, persistGraphMessageBaseline, turnGraphMessagesAfterCompression, persistHitlWaitingPlaceholder, persistTurnTranscript, persistUserTurnMessage, resolveCheckpointThreadId, sessionVisibleGraphMessages, } from '../../session/persist-turn-messages.js';
|
|
19
|
+
import { applyInjectedAssistant, collectPublicArtifactLinks, injectPublicArtifactLinks, } from '../../tool/inject-public-artifact-links.js';
|
|
19
20
|
import { SessionBudgetExhaustedError } from '../../token/session-budget-checker.js';
|
|
20
21
|
import { TokenBudgetExhaustedError } from '../../token/token-budget-guard.js';
|
|
21
22
|
import { recordTaskTokenUsages, tokenUsagesFromLlmChannel, } from '../../token/record-task-token-usage.js';
|
|
@@ -380,21 +381,24 @@ export class InvokeOrchestrator {
|
|
|
380
381
|
},
|
|
381
382
|
]);
|
|
382
383
|
}
|
|
383
|
-
// ─── Step 7: 完成 Task (running → success) ───
|
|
384
|
-
await this.taskService.updateStatus(task.id, 'success', graphResult.output);
|
|
385
384
|
// ─── Step 7b: 持久化本轮 transcript(统一顺序:assistant tool_calls → tool → assistant 文本)───
|
|
386
385
|
// 切点是入图 messages 基线(压缩 `__set` 后 Graph 短于 Session 全文)。user 已在 4b 写入。
|
|
387
386
|
const allMessages = Array.isArray(graphResult.finalState?.messages)
|
|
388
387
|
? graphResult.finalState.messages
|
|
389
388
|
: [];
|
|
390
389
|
const newMessages = sessionVisibleGraphMessages(allMessages.slice(persistBaseline), finishPoints);
|
|
390
|
+
const links = collectPublicArtifactLinks(allMessages);
|
|
391
|
+
const { injected } = injectPublicArtifactLinks(graphResult.output ?? '', links);
|
|
392
|
+
const persisted = applyInjectedAssistant(newMessages, injected, links);
|
|
393
|
+
// ─── Step 7: 完成 Task (running → success) ───
|
|
394
|
+
await this.taskService.updateStatus(task.id, 'success', injected);
|
|
391
395
|
const duration = Date.now() - startTime;
|
|
392
396
|
await persistTurnTranscript(this.messageService, {
|
|
393
397
|
sessionId,
|
|
394
398
|
taskId: task.id,
|
|
395
399
|
correlationId,
|
|
396
|
-
messages:
|
|
397
|
-
fallbackAssistantContent:
|
|
400
|
+
messages: persisted,
|
|
401
|
+
fallbackAssistantContent: injected,
|
|
398
402
|
assistantTokenCount: tokenUsage.output,
|
|
399
403
|
assistantDurationMs: duration,
|
|
400
404
|
});
|
|
@@ -404,7 +408,7 @@ export class InvokeOrchestrator {
|
|
|
404
408
|
return this.buildResponse(traceId, {
|
|
405
409
|
taskId: task.id,
|
|
406
410
|
sessionId,
|
|
407
|
-
output:
|
|
411
|
+
output: injected,
|
|
408
412
|
tokenUsage,
|
|
409
413
|
duration,
|
|
410
414
|
status: 'success',
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
* @see requirements.md Requirement 11 AC2
|
|
21
21
|
*/
|
|
22
22
|
import { buildHitlWaitingTaskOutput, buildTurnTranscriptFromStreamEvents, loadModelFacingHistory, turnGraphMessagesAfterCompression, extractStreamReasoning, applyStreamTextDelta, createStreamAssistantBuckets, visibleStreamAssistantText, streamAssistantTexts, messageRecordsToGraphMessages, persistHitlWaitingPlaceholder, persistTurnTranscript, persistUserTurnMessage, appendTerminalToolResult, } from '../../session/persist-turn-messages.js';
|
|
23
|
+
import { collectPublicArtifactLinks, collectPublicArtifactLinksFromEvent, injectPublicArtifactLinks, mergeArtifactLinks, } from '../../tool/inject-public-artifact-links.js';
|
|
23
24
|
import { SessionBudgetExhaustedError } from '../../token/session-budget-checker.js';
|
|
24
25
|
import { TokenBudgetExhaustedError } from '../../token/token-budget-guard.js';
|
|
25
26
|
import { recordTaskTokenUsages } from '../../token/record-task-token-usage.js';
|
|
@@ -268,6 +269,7 @@ export class StreamOrchestrator {
|
|
|
268
269
|
let assistantReasoningContent;
|
|
269
270
|
const pendingToolCalls = [];
|
|
270
271
|
const toolResults = [];
|
|
272
|
+
const artifactLinks = [];
|
|
271
273
|
// ─── Step 6: Graph.stream() 流式执行 ───
|
|
272
274
|
try {
|
|
273
275
|
const eventStream = runWithExecutionTrace({ abortSignal }, () => this.graphEngine.stream(graph, {
|
|
@@ -326,6 +328,7 @@ export class StreamOrchestrator {
|
|
|
326
328
|
}
|
|
327
329
|
}
|
|
328
330
|
appendTerminalToolResult(runtimeEvent.type, runtimeEvent.payload, toolResults);
|
|
331
|
+
collectPublicArtifactLinksFromEvent(runtimeEvent.type, runtimeEvent.payload, artifactLinks);
|
|
329
332
|
const streamedReasoning = extractStreamReasoning(runtimeEvent.type, runtimeEvent.payload);
|
|
330
333
|
if (streamedReasoning)
|
|
331
334
|
assistantReasoningContent = streamedReasoning;
|
|
@@ -387,10 +390,18 @@ export class StreamOrchestrator {
|
|
|
387
390
|
this.timeoutManager?.clearTimer(taskId);
|
|
388
391
|
}
|
|
389
392
|
// ─── Step 7: 发送结束事件序列 ───
|
|
390
|
-
// Emit: response.final(带完整 assistant 文本,供客户端兜底)
|
|
391
393
|
const tokenStats = this.tokenStatsService.getTaskStats(taskId);
|
|
394
|
+
const visible = visibleStreamAssistantText(assistantBuckets);
|
|
395
|
+
const links = mergeArtifactLinks(artifactLinks, collectPublicArtifactLinks(toolResults));
|
|
396
|
+
const { injected, suffix } = injectPublicArtifactLinks(visible, links);
|
|
397
|
+
if (suffix) {
|
|
398
|
+
emitter.emit(this.buildSSEEvent('response.chunk', correlationId, taskId, {
|
|
399
|
+
content: suffix,
|
|
400
|
+
format: 'text',
|
|
401
|
+
}));
|
|
402
|
+
}
|
|
392
403
|
emitter.emit(this.buildSSEEvent('response.final', correlationId, taskId, {
|
|
393
|
-
content:
|
|
404
|
+
content: injected,
|
|
394
405
|
format: 'text',
|
|
395
406
|
}));
|
|
396
407
|
const totalTokens = (tokenStats.total.input ?? 0) + (tokenStats.total.output ?? 0);
|
|
@@ -424,8 +435,8 @@ export class StreamOrchestrator {
|
|
|
424
435
|
messages: buildTurnTranscriptFromStreamEvents({
|
|
425
436
|
pendingToolCalls,
|
|
426
437
|
toolResults,
|
|
427
|
-
assistantTexts: streamAssistantTexts(assistantBuckets),
|
|
428
|
-
assistantText:
|
|
438
|
+
assistantTexts: suffix ? undefined : streamAssistantTexts(assistantBuckets),
|
|
439
|
+
assistantText: injected,
|
|
429
440
|
finishPoints: graph.finishPoints,
|
|
430
441
|
toolCallReasoningContent,
|
|
431
442
|
assistantReasoningContent,
|
package/dist/common/index.d.ts
CHANGED
package/dist/common/index.js
CHANGED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export declare const MISSING_USER_DIR = "\u7F3A\u5C11\u7528\u6237\u76EE\u5F55\uFF0C\u62D2\u7EDD\u8BE5\u64CD\u4F5C";
|
|
2
|
+
/** `agents/<code>/users/`:与 `<ver>` 平级,不是版本目录。 */
|
|
3
|
+
export declare const USERS_DIR_NAME = "users";
|
|
4
|
+
/**
|
|
5
|
+
* jail 写根:`dirname(agentDir)/users/<userId>`。
|
|
6
|
+
* `agentDir` 仍是版本目录 `agents/<code>/<ver>`。
|
|
7
|
+
*/
|
|
8
|
+
export declare function userRoot(agentDir: string, userId: string): string;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
export const MISSING_USER_DIR = '缺少用户目录,拒绝该操作';
|
|
3
|
+
/** `agents/<code>/users/`:与 `<ver>` 平级,不是版本目录。 */
|
|
4
|
+
export const USERS_DIR_NAME = 'users';
|
|
5
|
+
/**
|
|
6
|
+
* jail 写根:`dirname(agentDir)/users/<userId>`。
|
|
7
|
+
* `agentDir` 仍是版本目录 `agents/<code>/<ver>`。
|
|
8
|
+
*/
|
|
9
|
+
export function userRoot(agentDir, userId) {
|
|
10
|
+
const dir = agentDir.trim();
|
|
11
|
+
const uid = userId.trim();
|
|
12
|
+
if (!dir || !uid) {
|
|
13
|
+
throw new Error(MISSING_USER_DIR);
|
|
14
|
+
}
|
|
15
|
+
if (uid === '.' || uid === '..' || /[\\/]/.test(uid)) {
|
|
16
|
+
throw new Error('非法用户标识');
|
|
17
|
+
}
|
|
18
|
+
return path.resolve(path.dirname(dir), USERS_DIR_NAME, uid);
|
|
19
|
+
}
|
|
@@ -374,6 +374,8 @@ export class LangGraphAdapter {
|
|
|
374
374
|
...(progress.toolset ? { toolset: progress.toolset } : {}),
|
|
375
375
|
content: progress.content,
|
|
376
376
|
success: true,
|
|
377
|
+
...(progress.file ? { file: progress.file } : {}),
|
|
378
|
+
...(progress.files?.length ? { files: progress.files } : {}),
|
|
377
379
|
...loc,
|
|
378
380
|
},
|
|
379
381
|
});
|
|
@@ -390,6 +392,9 @@ export class LangGraphAdapter {
|
|
|
390
392
|
...(progress.toolset ? { toolset: progress.toolset } : {}),
|
|
391
393
|
error: progress.error ?? 'Unknown error',
|
|
392
394
|
willRetry: progress.willRetry === true,
|
|
395
|
+
...(progress.content ? { content: progress.content } : {}),
|
|
396
|
+
...(progress.file ? { file: progress.file } : {}),
|
|
397
|
+
...(progress.files?.length ? { files: progress.files } : {}),
|
|
393
398
|
...loc,
|
|
394
399
|
},
|
|
395
400
|
});
|
|
@@ -1592,10 +1597,20 @@ export class LangGraphAdapter {
|
|
|
1592
1597
|
continue;
|
|
1593
1598
|
}
|
|
1594
1599
|
if (msg.role === 'tool') {
|
|
1595
|
-
// tool.result / tool.error 由 Tool
|
|
1600
|
+
// tool.result / tool.error 由 Tool 节点旁路发出(已带 file/files);此处再补 file.created
|
|
1596
1601
|
const raw = typeof msg.content === 'string' ? msg.content : '';
|
|
1597
|
-
const
|
|
1598
|
-
|
|
1602
|
+
const files = [
|
|
1603
|
+
...(msg.file ? [msg.file] : []),
|
|
1604
|
+
...(msg.files ?? []),
|
|
1605
|
+
];
|
|
1606
|
+
if (files.length === 0) {
|
|
1607
|
+
const parsed = fileArtifactFromToolOutput(raw);
|
|
1608
|
+
if (parsed)
|
|
1609
|
+
files.push(parsed);
|
|
1610
|
+
}
|
|
1611
|
+
for (const file of files) {
|
|
1612
|
+
if (!file?.url)
|
|
1613
|
+
continue;
|
|
1599
1614
|
yield {
|
|
1600
1615
|
type: 'file.created',
|
|
1601
1616
|
taskId: undefined,
|
|
@@ -1662,6 +1677,7 @@ export class LangGraphAdapter {
|
|
|
1662
1677
|
success: typeof m['success'] === 'boolean' ? m['success'] : undefined,
|
|
1663
1678
|
reasoningContent: typeof m['reasoningContent'] === 'string' ? m['reasoningContent'] : undefined,
|
|
1664
1679
|
file: readFileArtifact(m['file']),
|
|
1680
|
+
files: readFileArtifactList(m['files']),
|
|
1665
1681
|
};
|
|
1666
1682
|
});
|
|
1667
1683
|
}
|
|
@@ -1679,3 +1695,14 @@ function readFileArtifact(value) {
|
|
|
1679
1695
|
url: rec.url,
|
|
1680
1696
|
};
|
|
1681
1697
|
}
|
|
1698
|
+
function readFileArtifactList(value) {
|
|
1699
|
+
if (!Array.isArray(value))
|
|
1700
|
+
return undefined;
|
|
1701
|
+
const out = [];
|
|
1702
|
+
for (const item of value) {
|
|
1703
|
+
const file = readFileArtifact(item);
|
|
1704
|
+
if (file)
|
|
1705
|
+
out.push(file);
|
|
1706
|
+
}
|
|
1707
|
+
return out.length > 0 ? out : undefined;
|
|
1708
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { realpathSync } from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
+
import { userRoot } from '../../common/user-root.js';
|
|
3
4
|
function tryRealpath(p) {
|
|
4
5
|
try {
|
|
5
6
|
return realpathSync(p);
|
|
@@ -49,12 +50,20 @@ export function classifySkillSpace(bundleOrFilePath, opts) {
|
|
|
49
50
|
return 'agent';
|
|
50
51
|
const uid = opts?.userId?.trim();
|
|
51
52
|
if (uid) {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
53
|
+
try {
|
|
54
|
+
const user = normalizeForMatch(path.join(userRoot(agentDir, uid), 'skills'));
|
|
55
|
+
if (target === user || target.startsWith(`${user}/`))
|
|
56
|
+
return 'user';
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
// 非法 userId 时不按用户槽判定
|
|
60
|
+
}
|
|
55
61
|
}
|
|
56
|
-
|
|
62
|
+
const usersRoot = normalizeForMatch(path.join(path.dirname(agentDir), 'users'));
|
|
63
|
+
if ((target === usersRoot || target.startsWith(`${usersRoot}/`)) &&
|
|
64
|
+
/(^|\/)skills(\/|$)/.test(target.slice(usersRoot.length))) {
|
|
57
65
|
return 'user';
|
|
66
|
+
}
|
|
58
67
|
return 'local';
|
|
59
68
|
}
|
|
60
69
|
if (/(^|\/)preinstall\/skills(\/|$)/i.test(target))
|
|
@@ -276,13 +276,19 @@ export interface GraphMessage {
|
|
|
276
276
|
* 多轮请求必须原样回传,否则上游会 400。
|
|
277
277
|
*/
|
|
278
278
|
reasoningContent?: string;
|
|
279
|
-
/**
|
|
279
|
+
/** 工具产出的文件;SSE tool.result / tool.error 与 file.created 共用,不进模型正文 */
|
|
280
280
|
file?: {
|
|
281
281
|
fileName: string;
|
|
282
282
|
fileType: string;
|
|
283
283
|
size: number;
|
|
284
284
|
url: string;
|
|
285
285
|
};
|
|
286
|
+
files?: Array<{
|
|
287
|
+
fileName: string;
|
|
288
|
+
fileType: string;
|
|
289
|
+
size: number;
|
|
290
|
+
url: string;
|
|
291
|
+
}>;
|
|
286
292
|
}
|
|
287
293
|
/** Tool 调用信息 */
|
|
288
294
|
export interface ToolCallInfo {
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* @see requirements.md Requirement 8 AC1, AC3, AC5
|
|
11
11
|
*/
|
|
12
12
|
import type { NodeRetryConfig } from '../contracts/executable-graph.js';
|
|
13
|
-
import type { AuthContext } from '../../tool/tool-executor.interface.js';
|
|
13
|
+
import type { AuthContext, ToolCallResult } from '../../tool/tool-executor.interface.js';
|
|
14
14
|
import type { ToolOrchestrator } from '../../tool/tool-orchestrator.js';
|
|
15
15
|
import type { ToolResultInjector } from '../../tool/tool-result-injector.js';
|
|
16
16
|
import type { SkillDirRef } from '../adapters/skill-read.js';
|
|
@@ -41,6 +41,9 @@ export interface ToolNodeProgress {
|
|
|
41
41
|
arguments?: Record<string, unknown>;
|
|
42
42
|
content?: string;
|
|
43
43
|
error?: string;
|
|
44
|
+
/** 与 ToolCallResult.file / files 同形;completed 与失败终态都带 */
|
|
45
|
+
file?: ToolCallResult['file'];
|
|
46
|
+
files?: ToolCallResult['files'];
|
|
44
47
|
executionTime?: number;
|
|
45
48
|
node?: string;
|
|
46
49
|
skillId?: string;
|
|
@@ -19,9 +19,17 @@ import { resolveCheckpointThreadId } from '../../session/persist-turn-messages.j
|
|
|
19
19
|
import { mcpToolsetCode, toolTraceIdentity } from '../../tool/mcp-list.js';
|
|
20
20
|
import { resolveExecutionTrace, warnTraceSpanFailure, } from '../execution-trace-context.js';
|
|
21
21
|
import { emitRuntimeLog } from '../../common/logger.js';
|
|
22
|
+
import { toolResultHasArtifacts } from '../../tool/inject-public-artifact-links.js';
|
|
22
23
|
import { decideBeforeTool, } from '../../execution/interceptors.js';
|
|
23
24
|
import { isRetryableToolFailure, resolveToolRetryConfig, sleepToolRetry, toolRetryDelayMs, } from '../../tool/tool-retry.js';
|
|
24
25
|
import { isIsolatedSkillState } from '../adapters/skill-isolate.js';
|
|
26
|
+
function toolArtifactFields(result) {
|
|
27
|
+
return {
|
|
28
|
+
...(result.content ? { content: result.content } : {}),
|
|
29
|
+
...(result.file ? { file: result.file } : {}),
|
|
30
|
+
...(result.files?.length ? { files: result.files } : {}),
|
|
31
|
+
};
|
|
32
|
+
}
|
|
25
33
|
function skillDirsFromState(state) {
|
|
26
34
|
const defs = state['skillDefinitions'];
|
|
27
35
|
return Object.values(defs ?? {})
|
|
@@ -302,6 +310,7 @@ export class ToolNode {
|
|
|
302
310
|
status: 'completed',
|
|
303
311
|
arguments: req.arguments,
|
|
304
312
|
content: result.content,
|
|
313
|
+
...toolArtifactFields(result),
|
|
305
314
|
executionTime,
|
|
306
315
|
...(skillIdentity?.skillSpace ? { skillSpace: skillIdentity.skillSpace } : {}),
|
|
307
316
|
...(skillIdentity?.skillVersion ? { skillVersion: skillIdentity.skillVersion } : {}),
|
|
@@ -317,6 +326,7 @@ export class ToolNode {
|
|
|
317
326
|
status: 'error',
|
|
318
327
|
arguments: req.arguments,
|
|
319
328
|
error: result.error ?? 'unknown error',
|
|
329
|
+
...toolArtifactFields(result),
|
|
320
330
|
executionTime,
|
|
321
331
|
});
|
|
322
332
|
}
|
|
@@ -421,12 +431,15 @@ export class ToolNode {
|
|
|
421
431
|
role: 'tool',
|
|
422
432
|
content: result.success
|
|
423
433
|
? result.content
|
|
424
|
-
:
|
|
434
|
+
: toolResultHasArtifacts(result) && result.content
|
|
435
|
+
? `${result.content}\n[Tool Error] ${result.error ?? 'unknown error'}`
|
|
436
|
+
: `[Tool Error] ${result.error ?? 'unknown error'}`,
|
|
425
437
|
// 以下字段符合 LLM function-calling 协议(ToolMessage 接口)
|
|
426
438
|
toolCallId: result.toolCallId,
|
|
427
439
|
toolName: result.toolName,
|
|
428
440
|
success: result.success,
|
|
429
441
|
...('file' in result && result.file ? { file: result.file } : {}),
|
|
442
|
+
...('files' in result && result.files?.length ? { files: result.files } : {}),
|
|
430
443
|
}));
|
|
431
444
|
const lastSpanId = [...spanIds].reverse().find((id) => Boolean(id));
|
|
432
445
|
return {
|
|
@@ -477,6 +490,7 @@ export class ToolNode {
|
|
|
477
490
|
status: 'error',
|
|
478
491
|
arguments: req.arguments,
|
|
479
492
|
error: last.error ?? 'unknown error',
|
|
493
|
+
...toolArtifactFields(last),
|
|
480
494
|
willRetry: true,
|
|
481
495
|
});
|
|
482
496
|
await sleepToolRetry(toolRetryDelayMs(attempt, retry.backoff));
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* 目录(Agent 自包含,无全局共享层):
|
|
5
5
|
* {root}/agents/<agentCode>/<version>/preinstall/tools/<toolsetCode>/
|
|
6
6
|
* {root}/agents/<agentCode>/<version>/preinstall/skills/<skillCode>/
|
|
7
|
-
* {root}/agents/<agentCode
|
|
7
|
+
* {root}/agents/<agentCode>/users/<userId>/skills/<skillCode>/
|
|
8
8
|
*
|
|
9
9
|
* 命名:agentCode / toolsetCode / skillCode / toolName 均为语义化小写标识;
|
|
10
10
|
* Skill 目录无版本层:包内必含 SKILL.md(Agent Skills)。
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* 目录(Agent 自包含,无全局共享层):
|
|
5
5
|
* {root}/agents/<agentCode>/<version>/preinstall/tools/<toolsetCode>/
|
|
6
6
|
* {root}/agents/<agentCode>/<version>/preinstall/skills/<skillCode>/
|
|
7
|
-
* {root}/agents/<agentCode
|
|
7
|
+
* {root}/agents/<agentCode>/users/<userId>/skills/<skillCode>/
|
|
8
8
|
*
|
|
9
9
|
* 命名:agentCode / toolsetCode / skillCode / toolName 均为语义化小写标识;
|
|
10
10
|
* Skill 目录无版本层:包内必含 SKILL.md(Agent Skills)。
|
|
@@ -18,6 +18,7 @@ import { findSkillEntryFile } from './skill-tree.js';
|
|
|
18
18
|
import { preloadMissing, preloadPackageCorrupt, } from './preload-errors.js';
|
|
19
19
|
import { isRequireChecksum, resolvePreloadBehaviorFlags, } from './preload-flags.js';
|
|
20
20
|
import { emitRuntimeLog } from '../common/logger.js';
|
|
21
|
+
import { userRoot, USERS_DIR_NAME } from '../common/user-root.js';
|
|
21
22
|
async function pathExists(p) {
|
|
22
23
|
try {
|
|
23
24
|
await access(p, fsConstants.F_OK);
|
|
@@ -141,7 +142,9 @@ async function pickLatestVersionDir(parent) {
|
|
|
141
142
|
if (!(await pathExists(parent)))
|
|
142
143
|
return null;
|
|
143
144
|
const entries = await readdir(parent, { withFileTypes: true });
|
|
144
|
-
const dirs = entries
|
|
145
|
+
const dirs = entries
|
|
146
|
+
.filter((e) => e.isDirectory() && e.name !== USERS_DIR_NAME)
|
|
147
|
+
.map((e) => e.name);
|
|
145
148
|
if (dirs.includes('current')) {
|
|
146
149
|
return path.join(parent, 'current');
|
|
147
150
|
}
|
|
@@ -534,7 +537,7 @@ export class FileSystemDeploymentSnapshot {
|
|
|
534
537
|
// 用户装配 → 开发态作者源 extraRoots → Agent 预装
|
|
535
538
|
const candidates = [];
|
|
536
539
|
if (userId != null && userId !== '') {
|
|
537
|
-
candidates.push(path.join(agentDir,
|
|
540
|
+
candidates.push(path.join(userRoot(agentDir, userId), 'skills', ref.skillId));
|
|
538
541
|
}
|
|
539
542
|
for (const root of extraRoots) {
|
|
540
543
|
if (root)
|
|
@@ -25,6 +25,6 @@ export declare function inspectAgentSkillTree(agentDir: string, userId?: string,
|
|
|
25
25
|
*/
|
|
26
26
|
export declare function watchPreinstallSkillRoot(preinstallRoot: string, onChange: () => void, debounceMs?: number): () => void;
|
|
27
27
|
export declare function watchSkillRoots(roots: string[], onChange: () => void, debounceMs?: number): () => void;
|
|
28
|
-
export declare function watchAgentSkillDirs(agentDir: string, onChange: () => void, debounceMs?: number, extraRoots?: string[]): () => void;
|
|
28
|
+
export declare function watchAgentSkillDirs(agentDir: string, onChange: () => void, debounceMs?: number, extraRoots?: string[], userId?: string): () => void;
|
|
29
29
|
/** EAP_SKILL_HOT_RELOAD:dev 默认开;server/desktop 默认关 */
|
|
30
30
|
export declare function resolveSkillHotReload(deploymentContext: string, explicit?: boolean): boolean;
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Agent 自带 Skill 目录扫描(§3.1)
|
|
3
3
|
*
|
|
4
4
|
* agents/<code>/<ver>/preinstall/skills/<skillCode>/
|
|
5
|
-
* agents/<code
|
|
5
|
+
* agents/<code>/users/<userId>/skills/<skillCode>/ 同名覆盖(写根与 <ver> 平级)
|
|
6
6
|
*
|
|
7
7
|
* 热加载用 stamp:目录/文件 mtime 变化则缓存失效。
|
|
8
8
|
*/
|
|
@@ -11,6 +11,7 @@ import { watch } from 'node:fs';
|
|
|
11
11
|
import { constants as fsConstants } from 'node:fs';
|
|
12
12
|
import path from 'node:path';
|
|
13
13
|
import { SKILL_ENTRY_CANDIDATES } from '@gt-fe/eap-contracts';
|
|
14
|
+
import { userRoot } from '../common/user-root.js';
|
|
14
15
|
const SKILL_CODE = /^[a-z0-9][a-z0-9-]*$/;
|
|
15
16
|
async function exists(p) {
|
|
16
17
|
try {
|
|
@@ -75,7 +76,7 @@ export async function readSkillAssembly(agentDir, userId) {
|
|
|
75
76
|
const candidates = [];
|
|
76
77
|
const uid = userId?.trim();
|
|
77
78
|
if (uid)
|
|
78
|
-
candidates.push(path.join(agentDir,
|
|
79
|
+
candidates.push(path.join(userRoot(agentDir, uid), '.assembly.json'));
|
|
79
80
|
candidates.push(path.join(agentDir, '.assembly.json'), path.join(agentDir, 'assembly.json'));
|
|
80
81
|
for (const file of candidates) {
|
|
81
82
|
const parsed = await readAssemblyFile(file);
|
|
@@ -102,7 +103,7 @@ export async function skillAssemblyStamp(agentDir, userId) {
|
|
|
102
103
|
const candidates = [];
|
|
103
104
|
const uid = userId?.trim();
|
|
104
105
|
if (uid)
|
|
105
|
-
candidates.push(path.join(agentDir,
|
|
106
|
+
candidates.push(path.join(userRoot(agentDir, uid), '.assembly.json'));
|
|
106
107
|
candidates.push(path.join(agentDir, '.assembly.json'), path.join(agentDir, 'assembly.json'));
|
|
107
108
|
let latest = 0;
|
|
108
109
|
for (const file of candidates) {
|
|
@@ -114,7 +115,7 @@ export function preinstallSkillsDir(agentDir) {
|
|
|
114
115
|
return path.join(agentDir, 'preinstall', 'skills');
|
|
115
116
|
}
|
|
116
117
|
export function userSkillsDir(agentDir, userId) {
|
|
117
|
-
return path.join(agentDir,
|
|
118
|
+
return path.join(userRoot(agentDir, userId), 'skills');
|
|
118
119
|
}
|
|
119
120
|
export async function inspectAgentSkillTree(agentDir, userId, extraRoots = []) {
|
|
120
121
|
const roots = [preinstallSkillsDir(agentDir), ...extraRoots.filter(Boolean)];
|
|
@@ -205,8 +206,12 @@ export function watchSkillRoots(roots, onChange, debounceMs = 200) {
|
|
|
205
206
|
watcher.close();
|
|
206
207
|
};
|
|
207
208
|
}
|
|
208
|
-
export function watchAgentSkillDirs(agentDir, onChange, debounceMs = 200, extraRoots = []) {
|
|
209
|
-
|
|
209
|
+
export function watchAgentSkillDirs(agentDir, onChange, debounceMs = 200, extraRoots = [], userId) {
|
|
210
|
+
const roots = [preinstallSkillsDir(agentDir), ...extraRoots];
|
|
211
|
+
const uid = userId?.trim();
|
|
212
|
+
if (uid)
|
|
213
|
+
roots.push(userSkillsDir(agentDir, uid));
|
|
214
|
+
return watchSkillRoots(roots, onChange, debounceMs);
|
|
210
215
|
}
|
|
211
216
|
/** EAP_SKILL_HOT_RELOAD:dev 默认开;server/desktop 默认关 */
|
|
212
217
|
export function resolveSkillHotReload(deploymentContext, explicit) {
|
|
@@ -16,6 +16,7 @@ import { toLoadedSkillMap } from '../tool/system-prompt-builder.js';
|
|
|
16
16
|
import { assembleLoadedTools } from '../tool/harness-primitives.js';
|
|
17
17
|
import { createFileLlmTurnLogFromEnv, } from '../debug/llm-turn-log.js';
|
|
18
18
|
import { buildHitlWaitingTaskOutput, buildTurnTranscriptFromStreamEvents, loadModelFacingHistory, turnGraphMessagesAfterCompression, extractHitlThreadId, extractStreamReasoning, applyStreamTextDelta, createStreamAssistantBuckets, visibleStreamAssistantText, streamAssistantTexts, messageRecordsToGraphMessages, persistHitlWaitingPlaceholder, persistTurnTranscript, persistUserTurnMessage, resolveCheckpointThreadId, appendTerminalToolResult, } from '../session/persist-turn-messages.js';
|
|
19
|
+
import { collectPublicArtifactLinks, collectPublicArtifactLinksFromEvent, injectPublicArtifactLinks, mergeArtifactLinks, } from '../tool/inject-public-artifact-links.js';
|
|
19
20
|
import { iterSessionCompression, withCompressionSummaryModel, } from '../session/apply-session-compression.js';
|
|
20
21
|
import { applySessionMemory } from '../session/apply-session-memory.js';
|
|
21
22
|
import { buildSessionTitleUserPayload, invokeSessionTitle, } from '../session/session-title.js';
|
|
@@ -378,6 +379,7 @@ export class AgentRuntime {
|
|
|
378
379
|
let assistantReasoningContent;
|
|
379
380
|
const pendingToolCalls = [];
|
|
380
381
|
const toolResults = [];
|
|
382
|
+
const artifactLinks = [];
|
|
381
383
|
const nodeTokenUsages = [];
|
|
382
384
|
try {
|
|
383
385
|
for await (const runtimeEvent of runWithExecutionTrace({ abortSignal }, () => this.deps.graphEngine.stream(graph, {
|
|
@@ -421,6 +423,7 @@ export class AgentRuntime {
|
|
|
421
423
|
}
|
|
422
424
|
// 终态 tool.result / tool.error 回填历史;willRetry 的 error 不进 transcript
|
|
423
425
|
appendTerminalToolResult(runtimeEvent.type, runtimeEvent.payload, toolResults);
|
|
426
|
+
collectPublicArtifactLinksFromEvent(runtimeEvent.type, runtimeEvent.payload, artifactLinks);
|
|
424
427
|
const streamedReasoning = extractStreamReasoning(runtimeEvent.type, runtimeEvent.payload);
|
|
425
428
|
if (streamedReasoning)
|
|
426
429
|
assistantReasoningContent = streamedReasoning;
|
|
@@ -433,6 +436,19 @@ export class AgentRuntime {
|
|
|
433
436
|
await recordTaskTokenUsages(this.deps.taskService, task.id, nodeTokenUsages);
|
|
434
437
|
await this.deps.taskService.updateStatus(task.id, 'success');
|
|
435
438
|
const tokenSummary = summarizeTokenUsages(nodeTokenUsages);
|
|
439
|
+
const visible = visibleStreamAssistantText(assistantBuckets);
|
|
440
|
+
const links = mergeArtifactLinks(artifactLinks, collectPublicArtifactLinks(toolResults));
|
|
441
|
+
const { injected, suffix } = injectPublicArtifactLinks(visible, links);
|
|
442
|
+
if (suffix) {
|
|
443
|
+
yield this.toSSE('response.chunk', correlationId, task.id, {
|
|
444
|
+
content: suffix,
|
|
445
|
+
format: 'text',
|
|
446
|
+
});
|
|
447
|
+
}
|
|
448
|
+
yield this.toSSE('response.final', correlationId, task.id, {
|
|
449
|
+
content: injected,
|
|
450
|
+
format: 'text',
|
|
451
|
+
});
|
|
436
452
|
await persistTurnTranscript(this.deps.messageService, {
|
|
437
453
|
sessionId,
|
|
438
454
|
taskId: task.id,
|
|
@@ -440,8 +456,8 @@ export class AgentRuntime {
|
|
|
440
456
|
messages: buildTurnTranscriptFromStreamEvents({
|
|
441
457
|
pendingToolCalls,
|
|
442
458
|
toolResults,
|
|
443
|
-
assistantTexts: streamAssistantTexts(assistantBuckets),
|
|
444
|
-
assistantText:
|
|
459
|
+
assistantTexts: suffix ? undefined : streamAssistantTexts(assistantBuckets),
|
|
460
|
+
assistantText: injected,
|
|
445
461
|
finishPoints: graph.finishPoints,
|
|
446
462
|
toolCallReasoningContent,
|
|
447
463
|
assistantReasoningContent,
|
|
@@ -621,6 +637,7 @@ export class AgentRuntime {
|
|
|
621
637
|
let assistantReasoningContent;
|
|
622
638
|
const pendingToolCalls = [];
|
|
623
639
|
const toolResults = [];
|
|
640
|
+
const artifactLinks = [];
|
|
624
641
|
const nodeTokenUsages = [];
|
|
625
642
|
try {
|
|
626
643
|
for await (const runtimeEvent of runWithExecutionTrace({ abortSignal, abortDeadlineMs: continueState.abortDeadlineMs }, () => this.deps.graphEngine.stream(graph, continueState))) {
|
|
@@ -638,6 +655,7 @@ export class AgentRuntime {
|
|
|
638
655
|
}
|
|
639
656
|
}
|
|
640
657
|
appendTerminalToolResult(runtimeEvent.type, runtimeEvent.payload, toolResults);
|
|
658
|
+
collectPublicArtifactLinksFromEvent(runtimeEvent.type, runtimeEvent.payload, artifactLinks);
|
|
641
659
|
const streamedReasoning = extractStreamReasoning(runtimeEvent.type, runtimeEvent.payload);
|
|
642
660
|
if (streamedReasoning)
|
|
643
661
|
assistantReasoningContent = streamedReasoning;
|
|
@@ -647,8 +665,21 @@ export class AgentRuntime {
|
|
|
647
665
|
yield this.runtimeEventToSSE(runtimeEvent, correlationId, taskId);
|
|
648
666
|
}
|
|
649
667
|
}
|
|
668
|
+
const visible = visibleStreamAssistantText(assistantBuckets);
|
|
669
|
+
const links = mergeArtifactLinks(artifactLinks, collectPublicArtifactLinks(toolResults));
|
|
670
|
+
const { injected, suffix } = injectPublicArtifactLinks(visible, links);
|
|
671
|
+
if (suffix) {
|
|
672
|
+
yield this.toSSE('response.chunk', correlationId, taskId, {
|
|
673
|
+
content: suffix,
|
|
674
|
+
format: 'text',
|
|
675
|
+
});
|
|
676
|
+
}
|
|
677
|
+
yield this.toSSE('response.final', correlationId, taskId, {
|
|
678
|
+
content: injected,
|
|
679
|
+
format: 'text',
|
|
680
|
+
});
|
|
650
681
|
await recordTaskTokenUsages(this.deps.taskService, taskId, nodeTokenUsages);
|
|
651
|
-
await this.deps.taskService.updateStatus(taskId, 'success',
|
|
682
|
+
await this.deps.taskService.updateStatus(taskId, 'success', injected || undefined);
|
|
652
683
|
const tokenSummary = summarizeTokenUsages(nodeTokenUsages);
|
|
653
684
|
if (sessionId) {
|
|
654
685
|
await persistTurnTranscript(this.deps.messageService, {
|
|
@@ -658,8 +689,8 @@ export class AgentRuntime {
|
|
|
658
689
|
messages: buildTurnTranscriptFromStreamEvents({
|
|
659
690
|
pendingToolCalls,
|
|
660
691
|
toolResults,
|
|
661
|
-
assistantTexts: streamAssistantTexts(assistantBuckets),
|
|
662
|
-
assistantText:
|
|
692
|
+
assistantTexts: suffix ? undefined : streamAssistantTexts(assistantBuckets),
|
|
693
|
+
assistantText: injected,
|
|
663
694
|
finishPoints: graph.finishPoints,
|
|
664
695
|
toolCallReasoningContent,
|
|
665
696
|
assistantReasoningContent,
|
|
@@ -91,12 +91,7 @@ export declare function sessionVisibleGraphMessages(messages: GraphMessage[], fi
|
|
|
91
91
|
export declare function extractStreamReasoning(eventType: string, payload: unknown): string | undefined;
|
|
92
92
|
/** 落盘 / 回填历史只收终态:result,或不会再试的 error。willRetry 的 error 只给直播 UI。 */
|
|
93
93
|
export declare function isTerminalToolStreamEvent(eventType: string, payload: unknown): boolean;
|
|
94
|
-
export declare function appendTerminalToolResult(eventType: string, payload: unknown, bucket:
|
|
95
|
-
toolCallId: string;
|
|
96
|
-
toolName: string;
|
|
97
|
-
content: unknown;
|
|
98
|
-
success: boolean;
|
|
99
|
-
}>): void;
|
|
94
|
+
export declare function appendTerminalToolResult(eventType: string, payload: unknown, bucket: StreamToolResult[]): void;
|
|
100
95
|
/** 历史 MessageRecord → GraphMessage(保留 tool 元数据与 reasoning) */
|
|
101
96
|
export declare function messageRecordsToGraphMessages(records: MessageRecord[]): GraphMessage[];
|
|
102
97
|
type CheckpointLoad = {
|
|
@@ -203,6 +198,18 @@ export type StreamToolResult = {
|
|
|
203
198
|
toolName: string;
|
|
204
199
|
content: unknown;
|
|
205
200
|
success: boolean;
|
|
201
|
+
file?: {
|
|
202
|
+
fileName: string;
|
|
203
|
+
fileType: string;
|
|
204
|
+
size: number;
|
|
205
|
+
url: string;
|
|
206
|
+
};
|
|
207
|
+
files?: Array<{
|
|
208
|
+
fileName: string;
|
|
209
|
+
fileType: string;
|
|
210
|
+
size: number;
|
|
211
|
+
url: string;
|
|
212
|
+
}>;
|
|
206
213
|
};
|
|
207
214
|
/**
|
|
208
215
|
* 将 stream 侧累加的 tool 事件规范为与 invoke GraphState 增量同序的 messages:
|
|
@@ -242,6 +242,8 @@ export function appendTerminalToolResult(eventType, payload, bucket) {
|
|
|
242
242
|
toolName: p.toolName,
|
|
243
243
|
content: p.content ?? p.error,
|
|
244
244
|
success: eventType === 'tool.result' && p.success !== false,
|
|
245
|
+
...(p.file ? { file: p.file } : {}),
|
|
246
|
+
...(p.files?.length ? { files: p.files } : {}),
|
|
245
247
|
});
|
|
246
248
|
}
|
|
247
249
|
/** 历史 MessageRecord → GraphMessage(保留 tool 元数据与 reasoning) */
|
|
@@ -525,6 +527,8 @@ export function buildTurnTranscriptFromStreamEvents(params) {
|
|
|
525
527
|
toolCallId: tr.toolCallId,
|
|
526
528
|
toolName: tr.toolName,
|
|
527
529
|
success: tr.success,
|
|
530
|
+
...(tr.file ? { file: tr.file } : {}),
|
|
531
|
+
...(tr.files?.length ? { files: tr.files } : {}),
|
|
528
532
|
});
|
|
529
533
|
}
|
|
530
534
|
if (texts.length > 0) {
|