@botbotgo/agent-harness 0.0.361 → 0.0.362
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/README.md +2 -0
- package/README.zh.md +2 -0
- package/dist/config/catalogs/response-formats.yaml +43 -0
- package/dist/config/runtime/workspace.yaml +8 -0
- package/dist/contracts/runtime-requests.d.ts +19 -0
- package/dist/contracts/workspace.d.ts +6 -0
- package/dist/package-version.d.ts +2 -2
- package/dist/package-version.js +2 -2
- package/dist/projections/request-events.d.ts +1 -0
- package/dist/projections/request-events.js +97 -45
- package/dist/protocol/acp/harness-client.js +2 -3
- package/dist/runtime/adapter/flow/stream-runtime.js +117 -94
- package/dist/runtime/adapter/middleware-assembly.js +25 -3
- package/dist/runtime/adapter/tool/builtin-middleware-tools.d.ts +5 -0
- package/dist/runtime/adapter/tool/builtin-middleware-tools.js +30 -6
- package/dist/runtime/agent-runtime-adapter.d.ts +1 -0
- package/dist/runtime/agent-runtime-adapter.js +108 -17
- package/dist/runtime/harness/events/streaming.js +2 -3
- package/dist/workspace/agent-binding-compiler.js +90 -12
- package/dist/workspace/compile.js +1 -0
- package/dist/workspace/framework-contract-validation.d.ts +2 -1
- package/dist/workspace/framework-contract-validation.js +77 -5
- package/dist/workspace/object-loader.js +9 -0
- package/dist/workspace/support/workspace-ref-utils.d.ts +1 -0
- package/dist/workspace/support/workspace-ref-utils.js +40 -0
- package/dist/workspace/yaml-object-reader.js +13 -9
- package/package.json +1 -1
|
@@ -4,10 +4,33 @@ import { isSandboxBackend } from "deepagents";
|
|
|
4
4
|
import { isRecord } from "../../../utils/object.js";
|
|
5
5
|
import { formatBuiltinTodoSnapshot, isLowSignalTodoContent, summarizeBuiltinWriteTodosArgs, truncateLines } from "../runtime-adapter-support.js";
|
|
6
6
|
import { maybePersistLargeToolOutput, resolveToolRuntimeContext } from "./tool-output-artifacts.js";
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
function buildTaskToolDescription(subagents) {
|
|
8
|
+
const lines = [
|
|
9
|
+
"Delegate a bounded task to the subagent whose declared description best matches the task.",
|
|
10
|
+
"Use this only when a subagent is a better fit than the current agent. Set subagent_type to exactly one listed subagent name.",
|
|
11
|
+
];
|
|
12
|
+
const available = (subagents ?? [])
|
|
13
|
+
.filter((subagent) => subagent.name.trim().length > 0)
|
|
14
|
+
.map((subagent) => {
|
|
15
|
+
const description = subagent.description.trim() || "No description provided.";
|
|
16
|
+
return `- ${subagent.name}: ${description}`;
|
|
17
|
+
});
|
|
18
|
+
return available.length > 0
|
|
19
|
+
? [...lines, "", "Available subagents:", ...available].join("\n")
|
|
20
|
+
: lines.join(" ");
|
|
21
|
+
}
|
|
22
|
+
function buildTaskToolSchema(subagents) {
|
|
23
|
+
const names = (subagents ?? [])
|
|
24
|
+
.map((subagent) => subagent.name.trim())
|
|
25
|
+
.filter((name) => name.length > 0);
|
|
26
|
+
const subagentTypeSchema = names.length > 0
|
|
27
|
+
? z.enum(names)
|
|
28
|
+
: z.string();
|
|
29
|
+
return z.object({
|
|
30
|
+
description: z.string().describe("Concrete bounded task for the selected subagent to perform."),
|
|
31
|
+
subagent_type: subagentTypeSchema.describe("Exact name of the selected subagent."),
|
|
32
|
+
}).passthrough();
|
|
33
|
+
}
|
|
11
34
|
export const BUILTIN_MIDDLEWARE_TOOL_DESCRIPTORS = [
|
|
12
35
|
{ name: "write_todos", description: "Create and update the runtime todo board for multi-step work." },
|
|
13
36
|
{ name: "read_todos", description: "Read the current runtime todo board." },
|
|
@@ -638,10 +661,11 @@ export async function createBuiltinMiddlewareTools(backend, options) {
|
|
|
638
661
|
},
|
|
639
662
|
});
|
|
640
663
|
if (options.includeTaskTool && options.invokeTaskTool) {
|
|
664
|
+
const description = buildTaskToolDescription(options.taskSubagents);
|
|
641
665
|
tools.set("task", {
|
|
642
666
|
name: "task",
|
|
643
|
-
description
|
|
644
|
-
schema:
|
|
667
|
+
description,
|
|
668
|
+
schema: buildTaskToolSchema(options.taskSubagents),
|
|
645
669
|
invoke: async (input) => options.invokeTaskTool(input),
|
|
646
670
|
});
|
|
647
671
|
}
|
|
@@ -58,6 +58,7 @@ export declare class AgentRuntimeAdapter {
|
|
|
58
58
|
memoryContext?: string;
|
|
59
59
|
}): Promise<RequestResult>;
|
|
60
60
|
private tryDelegateWithCompactRouter;
|
|
61
|
+
private buildCompactDelegationReport;
|
|
61
62
|
stream(binding: CompiledAgentBinding, input: MessageContent, sessionId: string, history?: TranscriptMessage[], options?: {
|
|
62
63
|
context?: Record<string, unknown>;
|
|
63
64
|
state?: Record<string, unknown>;
|
|
@@ -112,6 +112,15 @@ function parseCompactRouterSelection(value, subagentNames) {
|
|
|
112
112
|
if (subagentNames.has(trimmed)) {
|
|
113
113
|
return { subagentType: trimmed };
|
|
114
114
|
}
|
|
115
|
+
const lowered = trimmed.toLowerCase();
|
|
116
|
+
const relaxedMatch = Array.from(subagentNames).find((name) => {
|
|
117
|
+
const lowerName = name.toLowerCase();
|
|
118
|
+
return lowerName.length > 0
|
|
119
|
+
&& (trimmed === name || lowered.includes(lowerName));
|
|
120
|
+
});
|
|
121
|
+
if (relaxedMatch) {
|
|
122
|
+
return { subagentType: relaxedMatch };
|
|
123
|
+
}
|
|
115
124
|
const parsed = parseFirstJsonObject(trimmed);
|
|
116
125
|
const toolCall = salvageJsonToolCalls(trimmed).at(0);
|
|
117
126
|
const payload = typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)
|
|
@@ -714,9 +723,10 @@ export class AgentRuntimeAdapter {
|
|
|
714
723
|
requestId,
|
|
715
724
|
});
|
|
716
725
|
if (compactDelegation) {
|
|
717
|
-
const
|
|
726
|
+
const compactReport = this.buildCompactDelegationReport(compactDelegation);
|
|
727
|
+
const output = JSON.stringify(compactDelegation.delegatedSubagentType === null
|
|
718
728
|
? compactDelegation.toolOutput
|
|
719
|
-
:
|
|
729
|
+
: compactReport);
|
|
720
730
|
const delegatedToolResults = Array.isArray(compactDelegation.delegatedResult?.metadata?.executedToolResults)
|
|
721
731
|
? compactDelegation.delegatedResult.metadata.executedToolResults
|
|
722
732
|
: [];
|
|
@@ -813,10 +823,13 @@ export class AgentRuntimeAdapter {
|
|
|
813
823
|
const subagentCatalog = subagents
|
|
814
824
|
.map((subagent) => `- ${subagent.name}: ${subagent.description}`)
|
|
815
825
|
.join("\n");
|
|
826
|
+
const routingPolicy = getBindingSystemPrompt(binding);
|
|
816
827
|
const prompt = [
|
|
817
828
|
primaryModel.init?.think === false ? "/no_think" : "",
|
|
818
829
|
"You are selecting a subagent for a delegation-only agent.",
|
|
819
830
|
"Choose exactly one listed subagent when it can responsibly handle the request.",
|
|
831
|
+
routingPolicy ? "Agent routing policy:" : "",
|
|
832
|
+
routingPolicy ?? "",
|
|
820
833
|
"Return only JSON with this shape:",
|
|
821
834
|
"{\"subagent_type\":\"<listed subagent name>\"}",
|
|
822
835
|
"If no listed subagent can handle the request, return only:",
|
|
@@ -881,6 +894,7 @@ export class AgentRuntimeAdapter {
|
|
|
881
894
|
if (selection?.refusedReason) {
|
|
882
895
|
return {
|
|
883
896
|
toolOutput: selection.refusedReason,
|
|
897
|
+
delegatedSubagentType: null,
|
|
884
898
|
delegatedResult: {
|
|
885
899
|
sessionId,
|
|
886
900
|
requestId,
|
|
@@ -891,7 +905,16 @@ export class AgentRuntimeAdapter {
|
|
|
891
905
|
},
|
|
892
906
|
};
|
|
893
907
|
}
|
|
894
|
-
|
|
908
|
+
let subagentType = selection?.subagentType ?? "";
|
|
909
|
+
if (!subagentNames.has(subagentType)) {
|
|
910
|
+
const fallbackSubagent = subagentNames.values().next().value;
|
|
911
|
+
if (typeof fallbackSubagent === "string" && fallbackSubagent) {
|
|
912
|
+
subagentType = fallbackSubagent;
|
|
913
|
+
}
|
|
914
|
+
else {
|
|
915
|
+
return null;
|
|
916
|
+
}
|
|
917
|
+
}
|
|
895
918
|
if (!subagentNames.has(subagentType)) {
|
|
896
919
|
return null;
|
|
897
920
|
}
|
|
@@ -905,15 +928,49 @@ export class AgentRuntimeAdapter {
|
|
|
905
928
|
files: options.files,
|
|
906
929
|
memoryContext: options.memoryContext,
|
|
907
930
|
});
|
|
908
|
-
let delegatedResult
|
|
931
|
+
let delegatedResult;
|
|
932
|
+
try {
|
|
933
|
+
delegatedResult = await runDelegatedRequest(requestText);
|
|
934
|
+
}
|
|
935
|
+
catch (error) {
|
|
936
|
+
const output = error instanceof Error ? error.message : String(error);
|
|
937
|
+
return {
|
|
938
|
+
toolOutput: output,
|
|
939
|
+
delegatedSubagentType: subagentType,
|
|
940
|
+
delegatedResult: {
|
|
941
|
+
sessionId,
|
|
942
|
+
requestId,
|
|
943
|
+
agentId: selectedBinding.agent.id,
|
|
944
|
+
state: "failed",
|
|
945
|
+
output,
|
|
946
|
+
finalMessageText: output,
|
|
947
|
+
},
|
|
948
|
+
};
|
|
949
|
+
}
|
|
909
950
|
const targetRequiresExecutionToolEvidence = getBindingPrimaryTools(selectedBinding).length > 0;
|
|
910
951
|
if (targetRequiresExecutionToolEvidence && !hasDelegatedExecutionToolEvidence(delegatedResult)) {
|
|
911
|
-
|
|
952
|
+
try {
|
|
953
|
+
delegatedResult = await runDelegatedRequest([requestText, EXECUTION_WITH_TOOL_EVIDENCE_RETRY_INSTRUCTION].filter(Boolean).join("\n\n"), ":tool-evidence-retry");
|
|
954
|
+
}
|
|
955
|
+
catch (error) {
|
|
956
|
+
const output = error instanceof Error ? error.message : String(error);
|
|
957
|
+
return {
|
|
958
|
+
toolOutput: output,
|
|
959
|
+
delegatedSubagentType: subagentType,
|
|
960
|
+
delegatedResult: {
|
|
961
|
+
...delegatedResult,
|
|
962
|
+
state: "failed",
|
|
963
|
+
output,
|
|
964
|
+
finalMessageText: output,
|
|
965
|
+
},
|
|
966
|
+
};
|
|
967
|
+
}
|
|
912
968
|
}
|
|
913
969
|
if (targetRequiresExecutionToolEvidence && !hasDelegatedExecutionToolEvidence(delegatedResult)) {
|
|
914
970
|
const output = `runtime_error=Delegated agent ${selectedBinding.agent.id} completed without tool execution evidence.`;
|
|
915
971
|
return {
|
|
916
972
|
toolOutput: output,
|
|
973
|
+
delegatedSubagentType: subagentType,
|
|
917
974
|
delegatedResult: {
|
|
918
975
|
...delegatedResult,
|
|
919
976
|
state: "failed",
|
|
@@ -928,6 +985,7 @@ export class AgentRuntimeAdapter {
|
|
|
928
985
|
const output = "runtime_error=Delegated agent ended before producing required plan evidence.";
|
|
929
986
|
return {
|
|
930
987
|
toolOutput: output,
|
|
988
|
+
delegatedSubagentType: subagentType,
|
|
931
989
|
delegatedResult: {
|
|
932
990
|
...delegatedResult,
|
|
933
991
|
state: "failed",
|
|
@@ -936,7 +994,48 @@ export class AgentRuntimeAdapter {
|
|
|
936
994
|
},
|
|
937
995
|
};
|
|
938
996
|
}
|
|
939
|
-
return {
|
|
997
|
+
return {
|
|
998
|
+
toolOutput: resolveDelegatedResultOutput(delegatedResult),
|
|
999
|
+
delegatedSubagentType: subagentType,
|
|
1000
|
+
delegatedResult,
|
|
1001
|
+
};
|
|
1002
|
+
}
|
|
1003
|
+
buildCompactDelegationReport(compactDelegation) {
|
|
1004
|
+
const delegatedSubagentType = compactDelegation.delegatedSubagentType ?? "unknown";
|
|
1005
|
+
const delegatedOutput = typeof compactDelegation.toolOutput === "string"
|
|
1006
|
+
? [compactDelegation.toolOutput]
|
|
1007
|
+
: [];
|
|
1008
|
+
const delegatedToolNames = Array.isArray(compactDelegation.delegatedResult?.metadata?.executedToolResults)
|
|
1009
|
+
? compactDelegation.delegatedResult.metadata.executedToolResults
|
|
1010
|
+
.filter((toolResult) => toolResult?.toolName)
|
|
1011
|
+
.map((toolResult) => toolResult.toolName)
|
|
1012
|
+
: [];
|
|
1013
|
+
const state = compactDelegation.delegatedResult?.state === "failed" ? "failed" : "completed";
|
|
1014
|
+
return {
|
|
1015
|
+
status: state,
|
|
1016
|
+
routing: [
|
|
1017
|
+
`1) 路由判断: 已选择 sub-agent = ${delegatedSubagentType}(基于请求语义)。`,
|
|
1018
|
+
],
|
|
1019
|
+
plan: [
|
|
1020
|
+
"1) 选择具备匹配能力的子代理并创建委托任务。",
|
|
1021
|
+
"2) 执行子代理的原生工作流并收集委托工具输出。",
|
|
1022
|
+
"3) 合成汇总报告并返回给用户。",
|
|
1023
|
+
],
|
|
1024
|
+
execution: [
|
|
1025
|
+
`1) 调用 task 工具,目标子代理:${delegatedSubagentType}`,
|
|
1026
|
+
delegatedToolNames.length > 0
|
|
1027
|
+
? `2) 子代理返回工具证据:${[...new Set(delegatedToolNames)].join(", ")}`
|
|
1028
|
+
: "2) 子代理返回文本结果。",
|
|
1029
|
+
"3) 产出主编排汇总并返回结构化结果。",
|
|
1030
|
+
],
|
|
1031
|
+
summary: [
|
|
1032
|
+
`已完成子代理 ${delegatedSubagentType} 委托执行。`,
|
|
1033
|
+
],
|
|
1034
|
+
findings: delegatedOutput.length > 0 ? delegatedOutput.slice(0, 3) : ["none"],
|
|
1035
|
+
blockers: state === "failed" ? ["子代理执行未能完成。"] : ["none"],
|
|
1036
|
+
nextActions: ["如需更深入,可继续追问该次委托的细节。"],
|
|
1037
|
+
report: delegatedOutput.join("\n") || "委托已完成,未返回附加报告。",
|
|
1038
|
+
};
|
|
940
1039
|
}
|
|
941
1040
|
async *stream(binding, input, sessionId, history = [], options = {}) {
|
|
942
1041
|
const directListing = await this.tryHandleDirectWorkspaceListing(binding, input, {
|
|
@@ -968,25 +1067,17 @@ export class AgentRuntimeAdapter {
|
|
|
968
1067
|
requestId: options.requestId,
|
|
969
1068
|
});
|
|
970
1069
|
if (compactDelegation) {
|
|
971
|
-
const
|
|
1070
|
+
const compactReport = this.buildCompactDelegationReport(compactDelegation);
|
|
972
1071
|
yield {
|
|
973
1072
|
kind: "tool-result",
|
|
974
1073
|
toolName: "task",
|
|
975
1074
|
output: compactDelegation.toolOutput,
|
|
976
1075
|
};
|
|
977
|
-
for (const toolResult of delegatedToolResults) {
|
|
978
|
-
yield {
|
|
979
|
-
kind: "tool-result",
|
|
980
|
-
toolName: toolResult.toolName,
|
|
981
|
-
output: toolResult.output,
|
|
982
|
-
isError: toolResult.isError,
|
|
983
|
-
};
|
|
984
|
-
}
|
|
985
1076
|
yield {
|
|
986
1077
|
kind: "content",
|
|
987
|
-
content:
|
|
1078
|
+
content: JSON.stringify(compactDelegation.delegatedSubagentType === null
|
|
988
1079
|
? compactDelegation.toolOutput
|
|
989
|
-
:
|
|
1080
|
+
: compactReport),
|
|
990
1081
|
};
|
|
991
1082
|
return;
|
|
992
1083
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createFallbackRequestResultFromLatestEvent, mergeRequestResultOutput } from "../run/helpers.js";
|
|
2
|
-
import { applyRequestStreamItemToSnapshot, createInitialRequestEventSnapshot,
|
|
2
|
+
import { applyRequestStreamItemToSnapshot, createInitialRequestEventSnapshot, toRequestDataEvents, } from "../../../projections/request-events.js";
|
|
3
3
|
export async function emitOutputDeltaAndCreateItem(emit, sessionId, requestId, agentId, content) {
|
|
4
4
|
await emit(sessionId, requestId, 3, "output.delta", {
|
|
5
5
|
content,
|
|
@@ -71,8 +71,7 @@ export async function dispatchRequestListeners(stream, listeners, options) {
|
|
|
71
71
|
else if (item.type === "content") {
|
|
72
72
|
output += item.content;
|
|
73
73
|
}
|
|
74
|
-
const dataEvent
|
|
75
|
-
if (dataEvent) {
|
|
74
|
+
for (const dataEvent of toRequestDataEvents(item)) {
|
|
76
75
|
await notifyIfPresent(listeners.dataListener, dataEvent);
|
|
77
76
|
}
|
|
78
77
|
await notifyIfPresent(listeners.eventListener, snapshot);
|
|
@@ -5,7 +5,7 @@ import { compileModel, compileTool } from "./resource-compilers.js";
|
|
|
5
5
|
import { inferAgentCapabilities } from "./support/agent-capabilities.js";
|
|
6
6
|
import { getAgentExecutionConfigValue, getAgentExecutionObject, getAgentExecutionString } from "./support/agent-execution-config.js";
|
|
7
7
|
import { discoverSkillPaths } from "./support/discovery.js";
|
|
8
|
-
import { compileAgentMemories, getProceduralMemoryDefaults, getResilienceConfig, getRuntimeDefaults, getRuntimeMemoryDefaults, getRuntimeStorageRoots, getWorkspaceObject, resolvePromptValue, resolveRefId, } from "./support/workspace-ref-utils.js";
|
|
8
|
+
import { compileAgentMemories, getProceduralMemoryDefaults, getResilienceConfig, getRuntimeAgentDefaults, getRuntimeDefaults, getRuntimeMemoryDefaults, getRuntimeStorageRoots, getWorkspaceObject, resolvePromptValue, resolveRefId, } from "./support/workspace-ref-utils.js";
|
|
9
9
|
import { WORKSPACE_BOUNDARY_GUIDANCE } from "../runtime/prompts/runtime-prompts.js";
|
|
10
10
|
function requireSkills(pathEntries, workspaceRoot) {
|
|
11
11
|
return Array.from(new Set(discoverSkillPaths(pathEntries, workspaceRoot)));
|
|
@@ -166,8 +166,8 @@ export function requireTools(tools, bindings, ownerId) {
|
|
|
166
166
|
}
|
|
167
167
|
return Array.from(deduped.values());
|
|
168
168
|
}
|
|
169
|
-
function buildSubagent(agent, workspaceRoot, models, tools, parentSkills, parentModel) {
|
|
170
|
-
const execution = compileExecutionCore(agent, workspaceRoot, models, tools);
|
|
169
|
+
function buildSubagent(agent, workspaceRoot, refs, models, tools, parentSkills, parentModel) {
|
|
170
|
+
const execution = compileExecutionCore(agent, workspaceRoot, refs, models, tools);
|
|
171
171
|
return {
|
|
172
172
|
agentId: agent.id,
|
|
173
173
|
name: resolveAgentRuntimeName(agent),
|
|
@@ -196,6 +196,84 @@ function resolveInterruptOn(agent) {
|
|
|
196
196
|
function resolveResponseFormat(agent) {
|
|
197
197
|
return getAgentExecutionConfigValue(agent, "responseFormat");
|
|
198
198
|
}
|
|
199
|
+
function normalizeResponseFormatRef(ref) {
|
|
200
|
+
return ref.startsWith("response-format/") ? ref : `response-format/${ref}`;
|
|
201
|
+
}
|
|
202
|
+
function resolveResponseFormatObject(refs, ref, ownerLabel) {
|
|
203
|
+
if (!ref) {
|
|
204
|
+
return undefined;
|
|
205
|
+
}
|
|
206
|
+
const object = getWorkspaceObject(refs, normalizeResponseFormatRef(ref));
|
|
207
|
+
if (!object) {
|
|
208
|
+
throw new Error(`${ownerLabel} responseFormatRef references missing object ${ref}`);
|
|
209
|
+
}
|
|
210
|
+
if (object.kind !== "response-format") {
|
|
211
|
+
throw new Error(`${ownerLabel} responseFormatRef references ${ref}, but expected response-format`);
|
|
212
|
+
}
|
|
213
|
+
if (!("format" in object.value)) {
|
|
214
|
+
throw new Error(`${ownerLabel} responseFormatRef ${ref} must define format`);
|
|
215
|
+
}
|
|
216
|
+
return object.value.format;
|
|
217
|
+
}
|
|
218
|
+
function mergeResponseFormats(base, override) {
|
|
219
|
+
if (override === undefined) {
|
|
220
|
+
return base;
|
|
221
|
+
}
|
|
222
|
+
if (typeof base === "object" &&
|
|
223
|
+
base &&
|
|
224
|
+
typeof override === "object" &&
|
|
225
|
+
override &&
|
|
226
|
+
!Array.isArray(base) &&
|
|
227
|
+
!Array.isArray(override)) {
|
|
228
|
+
const merged = { ...base };
|
|
229
|
+
for (const [key, value] of Object.entries(override)) {
|
|
230
|
+
if (key === "required" && Array.isArray(merged.required) && Array.isArray(value)) {
|
|
231
|
+
merged.required = Array.from(new Set([...merged.required, ...value]));
|
|
232
|
+
continue;
|
|
233
|
+
}
|
|
234
|
+
if (key === "properties" &&
|
|
235
|
+
typeof merged.properties === "object" &&
|
|
236
|
+
merged.properties &&
|
|
237
|
+
!Array.isArray(merged.properties) &&
|
|
238
|
+
typeof value === "object" &&
|
|
239
|
+
value &&
|
|
240
|
+
!Array.isArray(value)) {
|
|
241
|
+
merged.properties = {
|
|
242
|
+
...merged.properties,
|
|
243
|
+
...value,
|
|
244
|
+
};
|
|
245
|
+
continue;
|
|
246
|
+
}
|
|
247
|
+
merged[key] = key in merged ? mergeResponseFormats(merged[key], value) : value;
|
|
248
|
+
}
|
|
249
|
+
return merged;
|
|
250
|
+
}
|
|
251
|
+
return override;
|
|
252
|
+
}
|
|
253
|
+
function resolveInheritedResponseFormat(agent, refs) {
|
|
254
|
+
const defaults = getRuntimeAgentDefaults(refs);
|
|
255
|
+
if (defaults && "responseFormat" in defaults) {
|
|
256
|
+
return defaults.responseFormat === null ? undefined : defaults.responseFormat;
|
|
257
|
+
}
|
|
258
|
+
const defaultRef = typeof defaults?.responseFormatRef === "string" ? defaults.responseFormatRef.trim() : "";
|
|
259
|
+
if (defaultRef) {
|
|
260
|
+
return resolveResponseFormatObject(refs, defaultRef, "Runtime defaults.agent.config");
|
|
261
|
+
}
|
|
262
|
+
return undefined;
|
|
263
|
+
}
|
|
264
|
+
function resolveEffectiveResponseFormat(agent, refs) {
|
|
265
|
+
const explicitResponseFormat = resolveResponseFormat(agent);
|
|
266
|
+
if (explicitResponseFormat !== undefined) {
|
|
267
|
+
return explicitResponseFormat === null
|
|
268
|
+
? undefined
|
|
269
|
+
: mergeResponseFormats(resolveInheritedResponseFormat(agent, refs), explicitResponseFormat);
|
|
270
|
+
}
|
|
271
|
+
const explicitRef = getAgentExecutionConfigValue(agent, "responseFormatRef");
|
|
272
|
+
if (typeof explicitRef === "string" && explicitRef.trim().length > 0) {
|
|
273
|
+
return resolveResponseFormatObject(refs, explicitRef.trim(), `Agent ${agent.id}`);
|
|
274
|
+
}
|
|
275
|
+
return resolveInheritedResponseFormat(agent, refs);
|
|
276
|
+
}
|
|
199
277
|
function resolveContextSchema(agent) {
|
|
200
278
|
return getAgentExecutionConfigValue(agent, "contextSchema");
|
|
201
279
|
}
|
|
@@ -216,13 +294,13 @@ function resolvePassthrough(agent) {
|
|
|
216
294
|
const passthrough = getAgentExecutionObject(agent, "passthrough");
|
|
217
295
|
return passthrough ? { ...passthrough } : undefined;
|
|
218
296
|
}
|
|
219
|
-
function compileSubagents(agent, agents, workspaceRoot, models, tools, compiledAgentSkills, compiledAgentModel) {
|
|
297
|
+
function compileSubagents(agent, agents, workspaceRoot, refs, models, tools, compiledAgentSkills, compiledAgentModel) {
|
|
220
298
|
return agent.subagentRefs.map((ref) => {
|
|
221
299
|
const subagent = agents.get(resolveRefId(ref));
|
|
222
300
|
if (!subagent) {
|
|
223
301
|
throw new Error(`Missing subagent ${ref} for agent ${agent.id}`);
|
|
224
302
|
}
|
|
225
|
-
return buildSubagent(subagent, workspaceRoot, models, tools, compiledAgentSkills, compiledAgentModel);
|
|
303
|
+
return buildSubagent(subagent, workspaceRoot, refs, models, tools, compiledAgentSkills, compiledAgentModel);
|
|
226
304
|
});
|
|
227
305
|
}
|
|
228
306
|
function compileAsyncSubagents(agent) {
|
|
@@ -234,18 +312,18 @@ function compileAsyncSubagents(agent) {
|
|
|
234
312
|
...(subagent.headers ? { headers: { ...subagent.headers } } : {}),
|
|
235
313
|
}));
|
|
236
314
|
}
|
|
237
|
-
function compileDeepAgentSubagents(agent, agents, workspaceRoot, models, tools, compiledAgentSkills, compiledAgentModel) {
|
|
315
|
+
function compileDeepAgentSubagents(agent, agents, workspaceRoot, refs, models, tools, compiledAgentSkills, compiledAgentModel) {
|
|
238
316
|
return [
|
|
239
|
-
...compileSubagents(agent, agents, workspaceRoot, models, tools, compiledAgentSkills, compiledAgentModel),
|
|
317
|
+
...compileSubagents(agent, agents, workspaceRoot, refs, models, tools, compiledAgentSkills, compiledAgentModel),
|
|
240
318
|
...compileAsyncSubagents(agent),
|
|
241
319
|
];
|
|
242
320
|
}
|
|
243
|
-
function compileExecutionCore(agent, workspaceRoot, models, tools) {
|
|
321
|
+
function compileExecutionCore(agent, workspaceRoot, refs, models, tools) {
|
|
244
322
|
return {
|
|
245
323
|
model: requireModel(models, agent.modelRef, agent.id),
|
|
246
324
|
tools: requireTools(tools, getAgentToolBindings(agent), agent.id),
|
|
247
325
|
systemPrompt: resolveSystemPrompt(agent, workspaceRoot),
|
|
248
|
-
responseFormat:
|
|
326
|
+
responseFormat: resolveEffectiveResponseFormat(agent, refs),
|
|
249
327
|
contextSchema: resolveContextSchema(agent),
|
|
250
328
|
middleware: resolveCompiledMiddleware(agent, models),
|
|
251
329
|
};
|
|
@@ -395,7 +473,7 @@ export function compileBinding(workspaceRoot, agent, agents, referencedSubagentI
|
|
|
395
473
|
const executionCore = compileExecutionCore({
|
|
396
474
|
...agent,
|
|
397
475
|
modelRef: agent.modelRef || (internalSubagent ? "model/default" : ""),
|
|
398
|
-
}, workspaceRoot, models, tools);
|
|
476
|
+
}, workspaceRoot, refs, models, tools);
|
|
399
477
|
const passthrough = resolvePassthrough(agent);
|
|
400
478
|
const compiledAgentModel = executionCore.model;
|
|
401
479
|
const backend = resolveBackendConfig(agent, refs);
|
|
@@ -442,7 +520,7 @@ export function compileBinding(workspaceRoot, agent, agents, referencedSubagentI
|
|
|
442
520
|
passthrough,
|
|
443
521
|
interruptOn: resolveInterruptOn(agent),
|
|
444
522
|
filesystem: compiledFilesystemConfig,
|
|
445
|
-
subagents: compileSubagents(agent, agents, workspaceRoot, models, tools, compiledAgentSkills, compiledAgentModel),
|
|
523
|
+
subagents: compileSubagents(agent, agents, workspaceRoot, refs, models, tools, compiledAgentSkills, compiledAgentModel),
|
|
446
524
|
memory: compiledAgentMemory,
|
|
447
525
|
skills: compiledAgentSkills,
|
|
448
526
|
generalPurposeAgent: getAgentExecutionConfigValue(agent, "generalPurposeAgent", { executionMode: "langchain-v1" }),
|
|
@@ -491,7 +569,7 @@ export function compileBinding(workspaceRoot, agent, agents, referencedSubagentI
|
|
|
491
569
|
responseFormat: executionCore.responseFormat,
|
|
492
570
|
contextSchema: executionCore.contextSchema,
|
|
493
571
|
middleware: executionCore.middleware,
|
|
494
|
-
subagents: compileDeepAgentSubagents(agent, agents, workspaceRoot, models, tools, compiledAgentSkills, compiledAgentModel),
|
|
572
|
+
subagents: compileDeepAgentSubagents(agent, agents, workspaceRoot, refs, models, tools, compiledAgentSkills, compiledAgentModel),
|
|
495
573
|
interruptOn: resolveInterruptOn(agent),
|
|
496
574
|
...(backend ? { backend: backend.config } : {}),
|
|
497
575
|
...(store ? { store: store.config } : {}),
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ParsedAgentObject, ParsedToolObject } from "../contracts/types.js";
|
|
1
|
+
import type { ParsedAgentObject, ParsedToolObject, WorkspaceObject } from "../contracts/types.js";
|
|
2
2
|
export type FrameworkContractValidationMode = "off" | "warn" | "error";
|
|
3
3
|
export declare function resolveFrameworkContractValidationMode(mode: FrameworkContractValidationMode | undefined): FrameworkContractValidationMode;
|
|
4
4
|
export declare function validateFrameworkContracts(input: {
|
|
@@ -6,5 +6,6 @@ export declare function validateFrameworkContracts(input: {
|
|
|
6
6
|
tools: Map<string, ParsedToolObject>;
|
|
7
7
|
skillRegistry: Map<string, string>;
|
|
8
8
|
ownedRoots: string[];
|
|
9
|
+
refs?: Map<string, WorkspaceObject | ParsedAgentObject>;
|
|
9
10
|
mode?: FrameworkContractValidationMode;
|
|
10
11
|
}): void;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
2
|
import { validateSkillMetadata } from "../runtime/skills/skill-metadata.js";
|
|
3
3
|
import { getAgentExecutionConfigValue } from "./support/agent-execution-config.js";
|
|
4
|
-
import { resolvePromptValue } from "./support/workspace-ref-utils.js";
|
|
4
|
+
import { getRuntimeAgentDefaults, getWorkspaceObject, resolvePromptValue } from "./support/workspace-ref-utils.js";
|
|
5
5
|
const FORBIDDEN_GENERAL_PURPOSE_SUBAGENT_NAME = "general-purpose";
|
|
6
6
|
const FRAMEWORK_AGENT_TOOL_NAMES = new Set(["task"]);
|
|
7
7
|
const FRAMEWORK_EXECUTION_TOOL_NAMES = new Set(["write_todos", "read_todos"]);
|
|
@@ -87,6 +87,78 @@ function readObject(value) {
|
|
|
87
87
|
? value
|
|
88
88
|
: undefined;
|
|
89
89
|
}
|
|
90
|
+
function normalizeResponseFormatRef(ref) {
|
|
91
|
+
return ref.startsWith("response-format/") ? ref : `response-format/${ref}`;
|
|
92
|
+
}
|
|
93
|
+
function resolveResponseFormatRef(refs, ref) {
|
|
94
|
+
if (!refs || !ref) {
|
|
95
|
+
return undefined;
|
|
96
|
+
}
|
|
97
|
+
const object = getWorkspaceObject(refs, normalizeResponseFormatRef(ref));
|
|
98
|
+
if (!object || object.kind !== "response-format") {
|
|
99
|
+
return undefined;
|
|
100
|
+
}
|
|
101
|
+
return object.value.format;
|
|
102
|
+
}
|
|
103
|
+
function mergeResponseFormats(base, override) {
|
|
104
|
+
if (override === undefined) {
|
|
105
|
+
return base;
|
|
106
|
+
}
|
|
107
|
+
if (typeof base === "object" &&
|
|
108
|
+
base &&
|
|
109
|
+
typeof override === "object" &&
|
|
110
|
+
override &&
|
|
111
|
+
!Array.isArray(base) &&
|
|
112
|
+
!Array.isArray(override)) {
|
|
113
|
+
const merged = { ...base };
|
|
114
|
+
for (const [key, value] of Object.entries(override)) {
|
|
115
|
+
if (key === "required" && Array.isArray(merged.required) && Array.isArray(value)) {
|
|
116
|
+
merged.required = Array.from(new Set([...merged.required, ...value]));
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
if (key === "properties" &&
|
|
120
|
+
typeof merged.properties === "object" &&
|
|
121
|
+
merged.properties &&
|
|
122
|
+
!Array.isArray(merged.properties) &&
|
|
123
|
+
typeof value === "object" &&
|
|
124
|
+
value &&
|
|
125
|
+
!Array.isArray(value)) {
|
|
126
|
+
merged.properties = {
|
|
127
|
+
...merged.properties,
|
|
128
|
+
...value,
|
|
129
|
+
};
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
merged[key] = key in merged ? mergeResponseFormats(merged[key], value) : value;
|
|
133
|
+
}
|
|
134
|
+
return merged;
|
|
135
|
+
}
|
|
136
|
+
return override;
|
|
137
|
+
}
|
|
138
|
+
function resolveInheritedResponseFormat(agent, refs) {
|
|
139
|
+
if (!refs) {
|
|
140
|
+
return undefined;
|
|
141
|
+
}
|
|
142
|
+
const defaults = getRuntimeAgentDefaults(refs);
|
|
143
|
+
if (defaults && "responseFormat" in defaults) {
|
|
144
|
+
return defaults.responseFormat === null ? undefined : defaults.responseFormat;
|
|
145
|
+
}
|
|
146
|
+
const defaultRef = typeof defaults?.responseFormatRef === "string" ? defaults.responseFormatRef.trim() : "";
|
|
147
|
+
return defaultRef ? resolveResponseFormatRef(refs, defaultRef) : undefined;
|
|
148
|
+
}
|
|
149
|
+
function resolveEffectiveResponseFormat(agent, refs) {
|
|
150
|
+
const explicitResponseFormat = getAgentExecutionConfigValue(agent, "responseFormat");
|
|
151
|
+
if (explicitResponseFormat !== undefined) {
|
|
152
|
+
return explicitResponseFormat === null
|
|
153
|
+
? undefined
|
|
154
|
+
: mergeResponseFormats(resolveInheritedResponseFormat(agent, refs), explicitResponseFormat);
|
|
155
|
+
}
|
|
156
|
+
const explicitRef = getAgentExecutionConfigValue(agent, "responseFormatRef");
|
|
157
|
+
if (typeof explicitRef === "string" && explicitRef.trim().length > 0) {
|
|
158
|
+
return resolveResponseFormatRef(refs, explicitRef.trim());
|
|
159
|
+
}
|
|
160
|
+
return resolveInheritedResponseFormat(agent, refs);
|
|
161
|
+
}
|
|
90
162
|
function validateResponseFormatTerminalStatus(agent, responseFormat, issues) {
|
|
91
163
|
const schema = readObject(responseFormat);
|
|
92
164
|
const properties = readObject(schema?.properties);
|
|
@@ -101,7 +173,7 @@ function validateResponseFormatTerminalStatus(agent, responseFormat, issues) {
|
|
|
101
173
|
addIssue(issues, "agent.response_format.incomplete_terminal_status_enum", `Agent ${agent.id} responseFormat status enum must include completed, blocked, failed, and refused.`);
|
|
102
174
|
}
|
|
103
175
|
}
|
|
104
|
-
function validateAgentContract(agent, referencedSubagentIds, tools, issues) {
|
|
176
|
+
function validateAgentContract(agent, referencedSubagentIds, tools, refs, issues) {
|
|
105
177
|
const description = agent.description.trim();
|
|
106
178
|
const systemPrompt = resolvePromptValue(getAgentExecutionConfigValue(agent, "systemPrompt"), path.dirname(agent.sourcePath));
|
|
107
179
|
const ownsDelegation = agent.subagentRefs.length > 0 || agent.subagentPathRefs.length > 0 || (agent.asyncSubagents?.length ?? 0) > 0;
|
|
@@ -109,7 +181,7 @@ function validateAgentContract(agent, referencedSubagentIds, tools, issues) {
|
|
|
109
181
|
const hasTools = agent.toolRefs.length > 0
|
|
110
182
|
|| (agent.toolBindings?.length ?? 0) > 0
|
|
111
183
|
|| (agent.inlineTools?.length ?? 0) > 0;
|
|
112
|
-
const responseFormat =
|
|
184
|
+
const responseFormat = resolveEffectiveResponseFormat(agent, refs);
|
|
113
185
|
const builtinTools = readBuiltinToolsConfig(agent);
|
|
114
186
|
const executionContract = readExecutionContractConfig(agent);
|
|
115
187
|
const localSubagentNames = [
|
|
@@ -156,7 +228,7 @@ function validateAgentContract(agent, referencedSubagentIds, tools, issues) {
|
|
|
156
228
|
addIssue(issues, "agent.subagent.missing_prompt", `Subagent ${agent.id} should define a systemPrompt that makes its operating boundary and output contract explicit.`);
|
|
157
229
|
}
|
|
158
230
|
if (agent.executionMode === "deepagent" && hasTools && responseFormat === undefined) {
|
|
159
|
-
addIssue(issues, "agent.subagent.deepagent.missing_response_format", `DeepAgents subagent ${agent.id} exposes tools, so it should define
|
|
231
|
+
addIssue(issues, "agent.subagent.deepagent.missing_response_format", `DeepAgents subagent ${agent.id} exposes tools, so it should inherit or define a responseFormat to guarantee a stable task result for its parent agent.`);
|
|
160
232
|
}
|
|
161
233
|
if (agent.executionMode === "deepagent" && hasTools && responseFormat !== undefined) {
|
|
162
234
|
validateResponseFormatTerminalStatus(agent, responseFormat, issues);
|
|
@@ -200,7 +272,7 @@ export function validateFrameworkContracts(input) {
|
|
|
200
272
|
if (!isWorkspaceOwnedPath(agent.sourcePath, input.ownedRoots)) {
|
|
201
273
|
continue;
|
|
202
274
|
}
|
|
203
|
-
validateAgentContract(agent, referencedSubagentIds, input.tools, issues);
|
|
275
|
+
validateAgentContract(agent, referencedSubagentIds, input.tools, input.refs, issues);
|
|
204
276
|
}
|
|
205
277
|
for (const [skillName, skillRoot] of input.skillRegistry) {
|
|
206
278
|
if (!isWorkspaceOwnedPath(skillRoot, input.ownedRoots)) {
|
|
@@ -16,6 +16,7 @@ const CONSUMED_AGENT_CONFIG_KEYS = [
|
|
|
16
16
|
"interruptOn",
|
|
17
17
|
"stateSchema",
|
|
18
18
|
"responseFormat",
|
|
19
|
+
"responseFormatRef",
|
|
19
20
|
"contextSchema",
|
|
20
21
|
"includeAgentName",
|
|
21
22
|
"version",
|
|
@@ -56,6 +57,7 @@ const MIGRATED_AGENT_CONFIG_KEYS = [
|
|
|
56
57
|
"interruptOn",
|
|
57
58
|
"stateSchema",
|
|
58
59
|
"responseFormat",
|
|
60
|
+
"responseFormatRef",
|
|
59
61
|
"contextSchema",
|
|
60
62
|
"includeAgentName",
|
|
61
63
|
"version",
|
|
@@ -264,6 +266,13 @@ function readSharedAgentConfig(config) {
|
|
|
264
266
|
...(typeof config.interruptOn === "object" && config.interruptOn ? { interruptOn: config.interruptOn } : {}),
|
|
265
267
|
...(config.stateSchema !== undefined ? { stateSchema: config.stateSchema } : {}),
|
|
266
268
|
...(config.responseFormat !== undefined ? { responseFormat: config.responseFormat } : {}),
|
|
269
|
+
...(typeof config.responseFormatRef === "string" && config.responseFormatRef.trim()
|
|
270
|
+
? {
|
|
271
|
+
responseFormatRef: config.responseFormatRef.startsWith("response-format/")
|
|
272
|
+
? config.responseFormatRef
|
|
273
|
+
: `response-format/${config.responseFormatRef.trim()}`,
|
|
274
|
+
}
|
|
275
|
+
: {}),
|
|
267
276
|
...(config.contextSchema !== undefined ? { contextSchema: config.contextSchema } : {}),
|
|
268
277
|
...(config.interactionMode === "stream" || config.interactionMode === "invoke" ? { interactionMode: config.interactionMode } : {}),
|
|
269
278
|
...(config.includeAgentName === "inline" ? { includeAgentName: "inline" } : {}),
|
|
@@ -37,6 +37,7 @@ export type ToolModuleDiscoveryConfig = {
|
|
|
37
37
|
};
|
|
38
38
|
export declare function getWorkspaceObject(refs: Map<string, WorkspaceObject | ParsedAgentObject>, ref: string | undefined): WorkspaceObject | undefined;
|
|
39
39
|
export declare function getRuntimeDefaults(refs: Map<string, WorkspaceObject | ParsedAgentObject>): Record<string, unknown> | undefined;
|
|
40
|
+
export declare function getRuntimeAgentDefaults(refs: Map<string, WorkspaceObject | ParsedAgentObject>): Record<string, unknown> | undefined;
|
|
40
41
|
export type RuntimeStorageRoots = {
|
|
41
42
|
applicationRoot: string;
|
|
42
43
|
dataRoot: string;
|