@ai-setting/roy-agent-core 1.5.91 → 1.5.93
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/env/agent/index.js +4 -3
- package/dist/env/event-source/index.js +3 -3
- package/dist/env/index.js +12 -11
- package/dist/env/prompt/index.js +2 -2
- package/dist/env/task/delegate/index.js +2 -2
- package/dist/env/task/index.js +3 -3
- package/dist/env/task/plugins/index.js +2 -2
- package/dist/env/tool/built-in/index.js +11 -1
- package/dist/env/tool/index.js +14 -3
- package/dist/env/workflow/engine/index.js +2 -2
- package/dist/env/workflow/index.js +3 -3
- package/dist/index.js +29 -111
- package/dist/shared/@ai-setting/{roy-agent-core-spzb03na.js → roy-agent-core-09xaxtpf.js} +5 -2
- package/dist/shared/@ai-setting/{roy-agent-core-55br4t6v.js → roy-agent-core-27d4js2f.js} +34 -7
- package/dist/shared/@ai-setting/{roy-agent-core-9amq4epa.js → roy-agent-core-2pz9075g.js} +2 -2
- package/dist/shared/@ai-setting/{roy-agent-core-w1s8yrm5.js → roy-agent-core-2vnw9wbx.js} +59 -18
- package/dist/shared/@ai-setting/roy-agent-core-5h49ct1a.js +135 -0
- package/dist/shared/@ai-setting/{roy-agent-core-5j649pb1.js → roy-agent-core-8f37pvct.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-snvsn60t.js → roy-agent-core-9z1mem2n.js} +44 -6
- package/dist/shared/@ai-setting/roy-agent-core-cy8azvpb.js +13 -0
- package/dist/shared/@ai-setting/{roy-agent-core-97tjdb2r.js → roy-agent-core-dj18eyyk.js} +29 -3
- package/dist/shared/@ai-setting/{roy-agent-core-q7ffkt1e.js → roy-agent-core-eqcbn89s.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-vady4w02.js → roy-agent-core-hfgqwf5w.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-jf865m5b.js → roy-agent-core-jhdv14ks.js} +11 -139
- package/dist/shared/@ai-setting/{roy-agent-core-7svhckhz.js → roy-agent-core-mwb6918x.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-svcr4gjw.js → roy-agent-core-mx9vq7b2.js} +1 -1
- package/dist/shared/@ai-setting/{roy-agent-core-at05dk0z.js → roy-agent-core-ss92p0r6.js} +1 -1
- package/dist/shared/@ai-setting/roy-agent-core-t7rw68q8.js +185 -0
- package/dist/shared/@ai-setting/{roy-agent-core-b1vkkcbr.js → roy-agent-core-x65fyjaw.js} +1 -1
- package/dist/utils/temp-file-manager/index.js +10 -0
- package/package.json +1 -1
- /package/dist/shared/@ai-setting/{roy-agent-core-pt7as39r.js → roy-agent-core-wjmwwc4a.js} +0 -0
|
@@ -25,6 +25,28 @@ import {
|
|
|
25
25
|
} from "./roy-agent-core-fs0mn2jk.js";
|
|
26
26
|
|
|
27
27
|
// src/env/workflow/plugins/workflow-json-output-plugin.ts
|
|
28
|
+
function extractLastAssistantText(messages) {
|
|
29
|
+
if (!messages || messages.length === 0) {
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
for (let i = messages.length - 1;i >= 0; i--) {
|
|
33
|
+
const msg = messages[i];
|
|
34
|
+
if (msg.role !== "assistant") {
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
37
|
+
const content = msg.content;
|
|
38
|
+
if (typeof content === "string") {
|
|
39
|
+
return content.trim() || undefined;
|
|
40
|
+
}
|
|
41
|
+
if (Array.isArray(content)) {
|
|
42
|
+
const textParts = content.filter((p) => typeof p === "object" && p !== null && p.type === "text").map((p) => p.text);
|
|
43
|
+
const combined = textParts.join(`
|
|
44
|
+
`).trim();
|
|
45
|
+
return combined || undefined;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
28
50
|
function registerWorkflowJsonOutputPlugin(options = undefined) {
|
|
29
51
|
if (registered) {
|
|
30
52
|
return;
|
|
@@ -61,34 +83,53 @@ async function runWorkflowJsonOutputExtraction(hookCtx) {
|
|
|
61
83
|
logger.warn("Workflow JSON extraction: agentComponent not registered. " + "Plugin cannot invoke json-extract sub-agent. " + "Ensure registerWorkflowJsonOutputPlugin({ agentComponent }) was called.");
|
|
62
84
|
return;
|
|
63
85
|
}
|
|
64
|
-
const
|
|
65
|
-
if (!
|
|
66
|
-
logger.warn("Workflow JSON extraction: no
|
|
86
|
+
const assistantText = extractLastAssistantText(hookCtx.messages);
|
|
87
|
+
if (!assistantText) {
|
|
88
|
+
logger.warn("Workflow JSON extraction: no assistant text found in main agent output. " + "The agent must produce a final text response (plain or markdown).");
|
|
67
89
|
return;
|
|
68
90
|
}
|
|
91
|
+
const submitTool = createSubmitJsonOutputTool(schema, hookCtx);
|
|
92
|
+
const submitToolAsExtra = {
|
|
93
|
+
name: submitTool.name,
|
|
94
|
+
description: submitTool.description ?? "",
|
|
95
|
+
parameters: submitTool.parameters,
|
|
96
|
+
execute: submitTool.execute
|
|
97
|
+
};
|
|
98
|
+
const userQuery = [
|
|
99
|
+
"The main agent has produced the following final response.",
|
|
100
|
+
"Extract the structured JSON from it and submit via workflow_submit_output.",
|
|
101
|
+
"",
|
|
102
|
+
"--- START of main agent response ---",
|
|
103
|
+
assistantText,
|
|
104
|
+
"--- END of main agent response ---",
|
|
105
|
+
"",
|
|
106
|
+
"Your ONLY job: call workflow_submit_output with the extracted JSON.",
|
|
107
|
+
"Do NOT output any text — just call the tool."
|
|
108
|
+
].join(`
|
|
109
|
+
`);
|
|
110
|
+
const messagesOverride = [
|
|
111
|
+
{
|
|
112
|
+
role: "user",
|
|
113
|
+
content: userQuery
|
|
114
|
+
}
|
|
115
|
+
];
|
|
116
|
+
const extractContext = {
|
|
117
|
+
messages: messagesOverride,
|
|
118
|
+
extraTools: [submitToolAsExtra],
|
|
119
|
+
persistSession: false,
|
|
120
|
+
abort: hookCtx.context.abort,
|
|
121
|
+
toolChoice: "required"
|
|
122
|
+
};
|
|
69
123
|
try {
|
|
70
|
-
const submitTool = createSubmitJsonOutputTool(schema, hookCtx);
|
|
71
|
-
const submitToolAsExtra = {
|
|
72
|
-
name: submitTool.name,
|
|
73
|
-
description: submitTool.description ?? "",
|
|
74
|
-
parameters: submitTool.parameters,
|
|
75
|
-
execute: submitTool.execute
|
|
76
|
-
};
|
|
77
|
-
const extractContext = {
|
|
78
|
-
sessionId,
|
|
79
|
-
extraTools: [submitToolAsExtra],
|
|
80
|
-
persistSession: false,
|
|
81
|
-
abort: hookCtx.context.abort
|
|
82
|
-
};
|
|
83
124
|
const result = await agentComponentRef.run("json-extract", "", extractContext);
|
|
84
125
|
if (result.error) {
|
|
85
126
|
logger.warn(`Workflow JSON extraction: json-extract sub-agent returned error: ${result.error}`);
|
|
86
127
|
return;
|
|
87
128
|
}
|
|
88
129
|
if (hookCtx.structuredOutputExtracted) {
|
|
89
|
-
logger.info("Workflow JSON output extracted via json-extract sub-agent " + `(iterations=${result.iterations}, toolCalls=${result.toolCalls?.length ?? 0})`);
|
|
130
|
+
logger.info("Workflow JSON output extracted via json-extract sub-agent (clean override) " + `(iterations=${result.iterations}, toolCalls=${result.toolCalls?.length ?? 0})`);
|
|
90
131
|
} else {
|
|
91
|
-
logger.warn("Workflow JSON extraction: json-extract sub-agent did not call workflow_submit_output. " + "
|
|
132
|
+
logger.warn("Workflow JSON extraction: json-extract sub-agent did not call workflow_submit_output. " + "toolChoice=required failed to force tool call — investigate provider support.");
|
|
92
133
|
}
|
|
93
134
|
} catch (error) {
|
|
94
135
|
logger.error(`Workflow JSON extraction: json-extract sub-agent threw: ${error instanceof Error ? error.message : String(error)}`);
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createLogger,
|
|
3
|
+
init_logger
|
|
4
|
+
} from "./roy-agent-core-vyygk314.js";
|
|
5
|
+
|
|
6
|
+
// src/utils/temp-file-manager/index.ts
|
|
7
|
+
init_logger();
|
|
8
|
+
import * as fs from "node:fs";
|
|
9
|
+
import * as path from "node:path";
|
|
10
|
+
import * as os from "node:os";
|
|
11
|
+
import * as crypto from "node:crypto";
|
|
12
|
+
var logger = createLogger("temp-file-manager");
|
|
13
|
+
|
|
14
|
+
class TempFileManager {
|
|
15
|
+
static _instance = null;
|
|
16
|
+
_files = new Set;
|
|
17
|
+
_hooksRegistered = false;
|
|
18
|
+
static FILE_PREFIX = "roy-agent-truncation";
|
|
19
|
+
static FILE_EXT = ".txt";
|
|
20
|
+
static LARGE_FILE_THRESHOLD = 10 * 1024 * 1024;
|
|
21
|
+
static STALE_FILE_AGE_MS = 24 * 60 * 60 * 1000;
|
|
22
|
+
constructor() {
|
|
23
|
+
this.sweepStaleFiles().catch((err) => {
|
|
24
|
+
logger.warn(`[TempFileManager] Stale file sweep failed: ${err}`);
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
static getInstance() {
|
|
28
|
+
if (!TempFileManager._instance) {
|
|
29
|
+
TempFileManager._instance = new TempFileManager;
|
|
30
|
+
}
|
|
31
|
+
return TempFileManager._instance;
|
|
32
|
+
}
|
|
33
|
+
static _resetForTesting() {
|
|
34
|
+
if (TempFileManager._instance) {
|
|
35
|
+
TempFileManager._instance.cleanupAll().catch(() => {});
|
|
36
|
+
}
|
|
37
|
+
TempFileManager._instance = null;
|
|
38
|
+
}
|
|
39
|
+
async writeTempFile(content, prefix = "default") {
|
|
40
|
+
this.ensureHooksRegistered();
|
|
41
|
+
if (content.length > TempFileManager.LARGE_FILE_THRESHOLD) {
|
|
42
|
+
logger.warn(`[TempFileManager] Writing large temp file: ${content.length} bytes (> ${TempFileManager.LARGE_FILE_THRESHOLD} bytes). Consider increasing maxOutputSize or filtering output.`);
|
|
43
|
+
}
|
|
44
|
+
const sanitizedPrefix = this.sanitizePrefix(prefix);
|
|
45
|
+
const uuid = crypto.randomUUID();
|
|
46
|
+
const filename = `${TempFileManager.FILE_PREFIX}-${process.pid}-${uuid}-${sanitizedPrefix}${TempFileManager.FILE_EXT}`;
|
|
47
|
+
const filePath = path.join(os.tmpdir(), filename);
|
|
48
|
+
await fs.promises.writeFile(filePath, content, "utf-8");
|
|
49
|
+
this._files.add(filePath);
|
|
50
|
+
logger.debug(`[TempFileManager] Created temp file: ${filePath} (${content.length} bytes)`);
|
|
51
|
+
return filePath;
|
|
52
|
+
}
|
|
53
|
+
async cleanupAll() {
|
|
54
|
+
const files = Array.from(this._files);
|
|
55
|
+
this._files.clear();
|
|
56
|
+
const results = await Promise.allSettled(files.map(async (filePath) => {
|
|
57
|
+
try {
|
|
58
|
+
await fs.promises.unlink(filePath);
|
|
59
|
+
} catch (err) {
|
|
60
|
+
if (err.code !== "ENOENT") {
|
|
61
|
+
logger.warn(`[TempFileManager] Failed to delete ${filePath}: ${err.message}`);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}));
|
|
65
|
+
const succeeded = results.filter((r) => r.status === "fulfilled").length;
|
|
66
|
+
logger.debug(`[TempFileManager] Cleanup complete: ${succeeded}/${files.length} files deleted`);
|
|
67
|
+
}
|
|
68
|
+
getActiveFileCount() {
|
|
69
|
+
return this._files.size;
|
|
70
|
+
}
|
|
71
|
+
async sweepStaleFiles(maxAgeMs = TempFileManager.STALE_FILE_AGE_MS) {
|
|
72
|
+
const tmpDir = os.tmpdir();
|
|
73
|
+
const now = Date.now();
|
|
74
|
+
let deletedCount = 0;
|
|
75
|
+
let entries;
|
|
76
|
+
try {
|
|
77
|
+
entries = await fs.promises.readdir(tmpDir);
|
|
78
|
+
} catch (err) {
|
|
79
|
+
logger.warn(`[TempFileManager] Failed to read tmpdir ${tmpDir}: ${err.message}`);
|
|
80
|
+
return 0;
|
|
81
|
+
}
|
|
82
|
+
for (const entry of entries) {
|
|
83
|
+
if (!entry.startsWith(`${TempFileManager.FILE_PREFIX}-`))
|
|
84
|
+
continue;
|
|
85
|
+
const filePath = path.join(tmpDir, entry);
|
|
86
|
+
try {
|
|
87
|
+
const stat = await fs.promises.stat(filePath);
|
|
88
|
+
const age = now - stat.mtimeMs;
|
|
89
|
+
if (age > maxAgeMs) {
|
|
90
|
+
await fs.promises.unlink(filePath);
|
|
91
|
+
deletedCount++;
|
|
92
|
+
}
|
|
93
|
+
} catch (err) {
|
|
94
|
+
if (err.code !== "ENOENT") {
|
|
95
|
+
logger.warn(`[TempFileManager] Failed to sweep ${filePath}: ${err.message}`);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
if (deletedCount > 0) {
|
|
100
|
+
logger.info(`[TempFileManager] Swept ${deletedCount} stale files from ${tmpDir}`);
|
|
101
|
+
}
|
|
102
|
+
return deletedCount;
|
|
103
|
+
}
|
|
104
|
+
ensureHooksRegistered() {
|
|
105
|
+
if (this._hooksRegistered)
|
|
106
|
+
return;
|
|
107
|
+
if (typeof process === "undefined" || !process.on) {
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
const cleanup = () => {
|
|
111
|
+
for (const filePath of this._files) {
|
|
112
|
+
try {
|
|
113
|
+
fs.unlinkSync(filePath);
|
|
114
|
+
} catch (err) {}
|
|
115
|
+
}
|
|
116
|
+
this._files.clear();
|
|
117
|
+
};
|
|
118
|
+
process.on("exit", cleanup);
|
|
119
|
+
process.on("SIGINT", () => {
|
|
120
|
+
cleanup();
|
|
121
|
+
process.exit(130);
|
|
122
|
+
});
|
|
123
|
+
process.on("SIGTERM", () => {
|
|
124
|
+
cleanup();
|
|
125
|
+
process.exit(143);
|
|
126
|
+
});
|
|
127
|
+
this._hooksRegistered = true;
|
|
128
|
+
logger.debug("[TempFileManager] Process exit hooks registered");
|
|
129
|
+
}
|
|
130
|
+
sanitizePrefix(prefix) {
|
|
131
|
+
return prefix.replace(/[\/\\<>:"|?*\x00-\x1f]/g, "-").replace(/\s+/g, "-").substring(0, 32) || "default";
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export { TempFileManager };
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import {
|
|
2
|
-
AgentComponentAdapter,
|
|
3
|
-
init_agent_component_adapter
|
|
4
|
-
} from "./roy-agent-core-w1s8yrm5.js";
|
|
5
1
|
import {
|
|
6
2
|
buildWorkflowNodeMetadata,
|
|
7
3
|
getWorkflowNodeIdFromMetadata,
|
|
8
4
|
init_workflow_message_metadata,
|
|
9
5
|
patchAgentSessionIdOnMessage
|
|
10
6
|
} from "./roy-agent-core-6atd905e.js";
|
|
7
|
+
import {
|
|
8
|
+
AgentComponentAdapter,
|
|
9
|
+
init_agent_component_adapter
|
|
10
|
+
} from "./roy-agent-core-2vnw9wbx.js";
|
|
11
11
|
import {
|
|
12
12
|
TemplateResolver,
|
|
13
13
|
init_template_resolver
|
|
@@ -892,6 +892,32 @@ var init_extract_agent_json_output = __esm(() => {
|
|
|
892
892
|
});
|
|
893
893
|
|
|
894
894
|
// src/env/workflow/utils/build-agent-node-output.ts
|
|
895
|
+
function extractSchemaFieldNames(schema) {
|
|
896
|
+
if (!schema)
|
|
897
|
+
return [];
|
|
898
|
+
const props = schema.properties;
|
|
899
|
+
if (!props || typeof props !== "object")
|
|
900
|
+
return [];
|
|
901
|
+
return Object.keys(props);
|
|
902
|
+
}
|
|
903
|
+
function defaultValueForField(fieldName) {
|
|
904
|
+
const lower = fieldName.toLowerCase();
|
|
905
|
+
if (lower === "score" || lower.endsWith("_score"))
|
|
906
|
+
return 0;
|
|
907
|
+
if (lower === "findings" || lower.endsWith("_list") || lower.endsWith("s"))
|
|
908
|
+
return [];
|
|
909
|
+
if (lower.includes("count") || lower.includes("total"))
|
|
910
|
+
return 0;
|
|
911
|
+
if (lower === "dimension")
|
|
912
|
+
return "unknown";
|
|
913
|
+
if (lower === "verdict")
|
|
914
|
+
return "unknown";
|
|
915
|
+
if (lower.includes("summary") || lower.includes("description") || lower.includes("report"))
|
|
916
|
+
return "";
|
|
917
|
+
if (lower.includes("markdown"))
|
|
918
|
+
return "";
|
|
919
|
+
return "";
|
|
920
|
+
}
|
|
895
921
|
function buildAgentNodeOutput(agentResult, outputConfig) {
|
|
896
922
|
if (outputConfig.mode === "json") {
|
|
897
923
|
const schema = outputConfig.schema;
|
|
@@ -908,12 +934,24 @@ function buildAgentNodeOutput(agentResult, outputConfig) {
|
|
|
908
934
|
};
|
|
909
935
|
}
|
|
910
936
|
const rawText = typeof agentResult.output === "string" ? agentResult.output : JSON.stringify(agentResult.output ?? {});
|
|
937
|
+
const schemaFields = extractSchemaFieldNames(schema);
|
|
938
|
+
const placeholderResult = {
|
|
939
|
+
_extraction_failed: true,
|
|
940
|
+
_raw_output: rawText,
|
|
941
|
+
_extraction_reason: "JSON extraction failed (json-extract sub-agent did not call workflow_submit_output)"
|
|
942
|
+
};
|
|
943
|
+
for (const fieldName of schemaFields) {
|
|
944
|
+
if (!(fieldName in placeholderResult)) {
|
|
945
|
+
placeholderResult[fieldName] = defaultValueForField(fieldName);
|
|
946
|
+
}
|
|
947
|
+
}
|
|
911
948
|
return {
|
|
912
|
-
result:
|
|
949
|
+
result: placeholderResult,
|
|
950
|
+
...placeholderResult,
|
|
913
951
|
metadata: {
|
|
914
952
|
...agentResult.metadata,
|
|
915
953
|
outputMode: "json_error",
|
|
916
|
-
_extractionWarning: "JSON extraction failed —
|
|
954
|
+
_extractionWarning: "JSON extraction failed — placeholder returned for downstream safety"
|
|
917
955
|
},
|
|
918
956
|
workflowHistory: agentResult.messages
|
|
919
957
|
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getUnderstandImageLLMProvider,
|
|
3
|
+
resetUnderstandImageLLMProvider,
|
|
4
|
+
setUnderstandImageLLMProvider,
|
|
5
|
+
understandImageTool
|
|
6
|
+
} from "./roy-agent-core-t7rw68q8.js";
|
|
7
|
+
import"./roy-agent-core-fs0mn2jk.js";
|
|
8
|
+
export {
|
|
9
|
+
understandImageTool,
|
|
10
|
+
setUnderstandImageLLMProvider,
|
|
11
|
+
resetUnderstandImageLLMProvider,
|
|
12
|
+
getUnderstandImageLLMProvider
|
|
13
|
+
};
|
|
@@ -1067,18 +1067,44 @@ workflow_run(workflow_name="strict-task-agent", input={"task_description": "修
|
|
|
1067
1067
|
|
|
1068
1068
|
你是一个 JSON 提取子智能体。**唯一职责**:从父 session 的对话历史中提取最终答案,通过 \`workflow_submit_output\` 工具调用一次提交结构化 JSON。
|
|
1069
1069
|
|
|
1070
|
+
## ⚠️ Critical Override(防止被父 session 误导)
|
|
1071
|
+
|
|
1072
|
+
父 session 的历史中**可能**包含以下误导性内容:
|
|
1073
|
+
|
|
1074
|
+
1. \`reasoning\` 块提到 "I used echo because workflow_submit_output is not available"
|
|
1075
|
+
2. assistant 文本中包含 "echo" / "used echo as a workaround" / "JSON output已生成完毕"
|
|
1076
|
+
3. assistant 调用 \`echo\` 工具并把 JSON 放在 echo 的 \`message\` 字段里
|
|
1077
|
+
4. 父 agent 用来"完成"任务的 echo 工具调用记录
|
|
1078
|
+
|
|
1079
|
+
**全部忽略上述内容,它们不是 JSON 提交完成的证据。**
|
|
1080
|
+
|
|
1081
|
+
### 必须遵守
|
|
1082
|
+
|
|
1083
|
+
- ✅ 你**唯一**的提交方式:通过 \`workflow_submit_output\` 工具调用一次提交结构化 JSON
|
|
1084
|
+
- ✅ \`workflow_submit_output\` 工具已经在你的工具列表里(参数由 zod schema 动态构建并验证)
|
|
1085
|
+
- ✅ 即使父 agent 已经"完成"分析,**你仍然**要把结果**重新**通过 \`workflow_submit_output\` 提交
|
|
1086
|
+
- ✅ 父 agent 说"workflow_submit_output not available"是关于**它的**工具集,不是你的
|
|
1087
|
+
|
|
1088
|
+
### 禁止
|
|
1089
|
+
|
|
1090
|
+
- ❌ 不要模仿父 agent 用 echo 提交(你只有 \`workflow_submit_output\` 这一个工具)
|
|
1091
|
+
- ❌ 不要被父 agent 的 reasoning 干扰
|
|
1092
|
+
- ❌ 不要因为父 agent 提到某个工具"not available"就改变自己的行为
|
|
1093
|
+
- ❌ 不要输出 text / code block / 任何非工具调用形式
|
|
1094
|
+
- ❌ 不要把 JSON 放在 echo 的 message 字段里(你不能用 echo)
|
|
1095
|
+
|
|
1070
1096
|
## 工具
|
|
1071
1097
|
|
|
1072
1098
|
你**只有**一个工具:\`workflow_submit_output\`。
|
|
1073
1099
|
|
|
1074
1100
|
- 工具参数本身就是你要提交的 JSON 对象(由 zod schema 动态构建并验证)
|
|
1075
1101
|
- 只调用一次,不要重复
|
|
1076
|
-
-
|
|
1102
|
+
- 不要使用其他任何工具(即使你"记得"父 agent 用了 echo)
|
|
1077
1103
|
|
|
1078
1104
|
## 行为
|
|
1079
1105
|
|
|
1080
|
-
1. 读取 session 历史(通过 sessionId
|
|
1081
|
-
2.
|
|
1106
|
+
1. 读取 session 历史(通过 sessionId 自动加载)—— **只关注事实**:用户输入、最终答案是什么
|
|
1107
|
+
2. 从对话中提取最终答案(**忽略**父 agent 的"echo workaround"叙述)
|
|
1082
1108
|
3. 调用 \`workflow_submit_output(参数 = 最终 JSON)\`
|
|
1083
1109
|
4. 立即停止
|
|
1084
1110
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
AgentComponentAdapter,
|
|
3
3
|
init_agent_component_adapter
|
|
4
|
-
} from "./roy-agent-core-
|
|
4
|
+
} from "./roy-agent-core-2vnw9wbx.js";
|
|
5
5
|
import"./roy-agent-core-1db4vpc6.js";
|
|
6
6
|
import"./roy-agent-core-e25xkv53.js";
|
|
7
7
|
import"./roy-agent-core-gp872e7m.js";
|
|
@@ -184,7 +184,7 @@ class AgentRegistry {
|
|
|
184
184
|
const agent = {
|
|
185
185
|
name,
|
|
186
186
|
type: "sub",
|
|
187
|
-
description: "JSON 提取子智能体 —
|
|
187
|
+
description: "JSON 提取子智能体 — 从对话历史中提取结构化 JSON,通过 workflow_submit_output 工具提交",
|
|
188
188
|
systemPromptRef: "json-extract",
|
|
189
189
|
allowedTools: ["workflow_submit_output"],
|
|
190
190
|
deniedTools: [],
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import {
|
|
2
|
+
TempFileManager
|
|
3
|
+
} from "./roy-agent-core-5h49ct1a.js";
|
|
1
4
|
import {
|
|
2
5
|
AUTO_TASK_METADATA_BEGIN,
|
|
3
6
|
AUTO_TASK_METADATA_END
|
|
@@ -181,138 +184,7 @@ var TOOL_CONFIG_REGISTRATION = {
|
|
|
181
184
|
// src/env/tool/tool-component.ts
|
|
182
185
|
init_workflow_hil();
|
|
183
186
|
init_env_context();
|
|
184
|
-
|
|
185
|
-
// src/utils/temp-file-manager.ts
|
|
186
|
-
init_logger();
|
|
187
|
-
import * as fs from "node:fs";
|
|
188
|
-
import * as path from "node:path";
|
|
189
|
-
import * as os from "node:os";
|
|
190
|
-
import * as crypto from "node:crypto";
|
|
191
|
-
var logger = createLogger("temp-file-manager");
|
|
192
|
-
|
|
193
|
-
class TempFileManager {
|
|
194
|
-
static _instance = null;
|
|
195
|
-
_files = new Set;
|
|
196
|
-
_hooksRegistered = false;
|
|
197
|
-
static FILE_PREFIX = "roy-agent-truncation";
|
|
198
|
-
static FILE_EXT = ".txt";
|
|
199
|
-
static LARGE_FILE_THRESHOLD = 10 * 1024 * 1024;
|
|
200
|
-
static STALE_FILE_AGE_MS = 24 * 60 * 60 * 1000;
|
|
201
|
-
constructor() {
|
|
202
|
-
this.sweepStaleFiles().catch((err) => {
|
|
203
|
-
logger.warn(`[TempFileManager] Stale file sweep failed: ${err}`);
|
|
204
|
-
});
|
|
205
|
-
}
|
|
206
|
-
static getInstance() {
|
|
207
|
-
if (!TempFileManager._instance) {
|
|
208
|
-
TempFileManager._instance = new TempFileManager;
|
|
209
|
-
}
|
|
210
|
-
return TempFileManager._instance;
|
|
211
|
-
}
|
|
212
|
-
static _resetForTesting() {
|
|
213
|
-
if (TempFileManager._instance) {
|
|
214
|
-
TempFileManager._instance.cleanupAll().catch(() => {});
|
|
215
|
-
}
|
|
216
|
-
TempFileManager._instance = null;
|
|
217
|
-
}
|
|
218
|
-
async writeTempFile(content, prefix = "default") {
|
|
219
|
-
this.ensureHooksRegistered();
|
|
220
|
-
if (content.length > TempFileManager.LARGE_FILE_THRESHOLD) {
|
|
221
|
-
logger.warn(`[TempFileManager] Writing large temp file: ${content.length} bytes (> ${TempFileManager.LARGE_FILE_THRESHOLD} bytes). Consider increasing maxOutputSize or filtering output.`);
|
|
222
|
-
}
|
|
223
|
-
const sanitizedPrefix = this.sanitizePrefix(prefix);
|
|
224
|
-
const uuid = crypto.randomUUID();
|
|
225
|
-
const filename = `${TempFileManager.FILE_PREFIX}-${process.pid}-${uuid}-${sanitizedPrefix}${TempFileManager.FILE_EXT}`;
|
|
226
|
-
const filePath = path.join(os.tmpdir(), filename);
|
|
227
|
-
await fs.promises.writeFile(filePath, content, "utf-8");
|
|
228
|
-
this._files.add(filePath);
|
|
229
|
-
logger.debug(`[TempFileManager] Created temp file: ${filePath} (${content.length} bytes)`);
|
|
230
|
-
return filePath;
|
|
231
|
-
}
|
|
232
|
-
async cleanupAll() {
|
|
233
|
-
const files = Array.from(this._files);
|
|
234
|
-
this._files.clear();
|
|
235
|
-
const results = await Promise.allSettled(files.map(async (filePath) => {
|
|
236
|
-
try {
|
|
237
|
-
await fs.promises.unlink(filePath);
|
|
238
|
-
} catch (err) {
|
|
239
|
-
if (err.code !== "ENOENT") {
|
|
240
|
-
logger.warn(`[TempFileManager] Failed to delete ${filePath}: ${err.message}`);
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
}));
|
|
244
|
-
const succeeded = results.filter((r) => r.status === "fulfilled").length;
|
|
245
|
-
logger.debug(`[TempFileManager] Cleanup complete: ${succeeded}/${files.length} files deleted`);
|
|
246
|
-
}
|
|
247
|
-
getActiveFileCount() {
|
|
248
|
-
return this._files.size;
|
|
249
|
-
}
|
|
250
|
-
async sweepStaleFiles(maxAgeMs = TempFileManager.STALE_FILE_AGE_MS) {
|
|
251
|
-
const tmpDir = os.tmpdir();
|
|
252
|
-
const now = Date.now();
|
|
253
|
-
let deletedCount = 0;
|
|
254
|
-
let entries;
|
|
255
|
-
try {
|
|
256
|
-
entries = await fs.promises.readdir(tmpDir);
|
|
257
|
-
} catch (err) {
|
|
258
|
-
logger.warn(`[TempFileManager] Failed to read tmpdir ${tmpDir}: ${err.message}`);
|
|
259
|
-
return 0;
|
|
260
|
-
}
|
|
261
|
-
for (const entry of entries) {
|
|
262
|
-
if (!entry.startsWith(`${TempFileManager.FILE_PREFIX}-`))
|
|
263
|
-
continue;
|
|
264
|
-
const filePath = path.join(tmpDir, entry);
|
|
265
|
-
try {
|
|
266
|
-
const stat = await fs.promises.stat(filePath);
|
|
267
|
-
const age = now - stat.mtimeMs;
|
|
268
|
-
if (age > maxAgeMs) {
|
|
269
|
-
await fs.promises.unlink(filePath);
|
|
270
|
-
deletedCount++;
|
|
271
|
-
}
|
|
272
|
-
} catch (err) {
|
|
273
|
-
if (err.code !== "ENOENT") {
|
|
274
|
-
logger.warn(`[TempFileManager] Failed to sweep ${filePath}: ${err.message}`);
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
if (deletedCount > 0) {
|
|
279
|
-
logger.info(`[TempFileManager] Swept ${deletedCount} stale files from ${tmpDir}`);
|
|
280
|
-
}
|
|
281
|
-
return deletedCount;
|
|
282
|
-
}
|
|
283
|
-
ensureHooksRegistered() {
|
|
284
|
-
if (this._hooksRegistered)
|
|
285
|
-
return;
|
|
286
|
-
if (typeof process === "undefined" || !process.on) {
|
|
287
|
-
return;
|
|
288
|
-
}
|
|
289
|
-
const cleanup = () => {
|
|
290
|
-
for (const filePath of this._files) {
|
|
291
|
-
try {
|
|
292
|
-
fs.unlinkSync(filePath);
|
|
293
|
-
} catch (err) {}
|
|
294
|
-
}
|
|
295
|
-
this._files.clear();
|
|
296
|
-
};
|
|
297
|
-
process.on("exit", cleanup);
|
|
298
|
-
process.on("SIGINT", () => {
|
|
299
|
-
cleanup();
|
|
300
|
-
process.exit(130);
|
|
301
|
-
});
|
|
302
|
-
process.on("SIGTERM", () => {
|
|
303
|
-
cleanup();
|
|
304
|
-
process.exit(143);
|
|
305
|
-
});
|
|
306
|
-
this._hooksRegistered = true;
|
|
307
|
-
logger.debug("[TempFileManager] Process exit hooks registered");
|
|
308
|
-
}
|
|
309
|
-
sanitizePrefix(prefix) {
|
|
310
|
-
return prefix.replace(/[\/\\<>:"|?*\x00-\x1f]/g, "-").replace(/\s+/g, "-").substring(0, 32) || "default";
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
// src/env/tool/tool-component.ts
|
|
315
|
-
var logger2 = createLogger("tool");
|
|
187
|
+
var logger = createLogger("tool");
|
|
316
188
|
var configComponentInstance = null;
|
|
317
189
|
function setToolConfigComponent(component) {
|
|
318
190
|
configComponentInstance = component;
|
|
@@ -438,7 +310,7 @@ class ToolComponent extends BaseComponent {
|
|
|
438
310
|
if (this.config?.builtInTools?.enabled !== false) {
|
|
439
311
|
await this.loadBuiltInTools();
|
|
440
312
|
}
|
|
441
|
-
|
|
313
|
+
logger.info(`ToolComponent initialized with ${this.getToolCount()} tools`);
|
|
442
314
|
}
|
|
443
315
|
async registerConfig(options) {
|
|
444
316
|
const configComponent = options.configComponent;
|
|
@@ -536,7 +408,7 @@ class ToolComponent extends BaseComponent {
|
|
|
536
408
|
});
|
|
537
409
|
}
|
|
538
410
|
onConfigChange(event) {
|
|
539
|
-
|
|
411
|
+
logger.info(`Tool config changed: ${event.key}`, {
|
|
540
412
|
oldValue: event.oldValue,
|
|
541
413
|
newValue: event.newValue
|
|
542
414
|
});
|
|
@@ -554,7 +426,7 @@ class ToolComponent extends BaseComponent {
|
|
|
554
426
|
source,
|
|
555
427
|
enabled: true
|
|
556
428
|
});
|
|
557
|
-
|
|
429
|
+
logger.debug(`Registered tool: ${tool.name}`);
|
|
558
430
|
globalHookManager.execute(ToolHookPoints.AFTER_REGISTER, { tool });
|
|
559
431
|
}
|
|
560
432
|
registerMany(tools, source = "dynamic") {
|
|
@@ -643,9 +515,9 @@ class ToolComponent extends BaseComponent {
|
|
|
643
515
|
⚠️ 输出被截断(原 ${truncationMeta.original_output_size} 字符,截断到 ${truncationMeta.truncated_output_size} 字符)
|
|
644
516
|
\uD83D\uDCC1 完整内容保存到: ${tempFilePath}
|
|
645
517
|
\uD83D\uDD0D 使用 read_file 工具按需读取`;
|
|
646
|
-
|
|
518
|
+
logger.debug(`[tool-component] Truncated output saved to temp file: ${tempFilePath} (${truncationMeta.original_output_size} bytes)`);
|
|
647
519
|
} catch (err) {
|
|
648
|
-
|
|
520
|
+
logger.warn(`[tool-component] Failed to write temp file for truncated output: ${err}`);
|
|
649
521
|
}
|
|
650
522
|
}
|
|
651
523
|
}
|
|
@@ -690,9 +562,9 @@ class ToolComponent extends BaseComponent {
|
|
|
690
562
|
const { getAllBuiltInTools } = await import("../../env/tool/built-in/index.js");
|
|
691
563
|
const tools = getAllBuiltInTools();
|
|
692
564
|
this.registerMany(tools, "built-in");
|
|
693
|
-
|
|
565
|
+
logger.info(`Loaded ${tools.length} built-in tools`);
|
|
694
566
|
} catch (err) {
|
|
695
|
-
|
|
567
|
+
logger.warn(`Failed to load built-in tools: ${err.message}`);
|
|
696
568
|
}
|
|
697
569
|
}
|
|
698
570
|
getHookPoints() {
|