@botbotgo/agent-harness 0.0.340 → 0.0.342
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/cli/chat-stream.js +48 -0
- package/dist/contracts/workspace.d.ts +10 -0
- package/dist/package-version.d.ts +2 -2
- package/dist/package-version.js +2 -2
- package/dist/runtime/adapter/flow/execution-context.js +3 -2
- package/dist/runtime/adapter/flow/stream-runtime.d.ts +6 -0
- package/dist/runtime/adapter/flow/stream-runtime.js +54 -15
- package/dist/runtime/adapter/invocation-result.js +111 -9
- package/dist/runtime/adapter/local-tool-invocation.js +21 -1
- package/dist/runtime/adapter/middleware/context-hygiene.d.ts +5 -0
- package/dist/runtime/adapter/middleware/context-hygiene.js +83 -0
- package/dist/runtime/adapter/middleware-assembly.d.ts +11 -0
- package/dist/runtime/adapter/middleware-assembly.js +154 -178
- package/dist/runtime/adapter/model/invocation-request.js +39 -1
- package/dist/runtime/adapter/runtime-adapter-support.js +33 -3
- package/dist/runtime/adapter/stream-event-projection.js +6 -5
- package/dist/runtime/adapter/tool/builtin-middleware-tools.d.ts +7 -0
- package/dist/runtime/adapter/tool/builtin-middleware-tools.js +31 -24
- package/dist/runtime/agent-runtime-adapter.d.ts +3 -2
- package/dist/runtime/agent-runtime-adapter.js +128 -9
- package/dist/runtime/agent-runtime-assembly.d.ts +1 -0
- package/dist/runtime/agent-runtime-assembly.js +10 -2
- package/dist/runtime/harness/run/inspection.js +4 -5
- package/dist/runtime/harness/run/stream-run.js +232 -48
- package/dist/runtime/parsing/output-parsing.d.ts +1 -1
- package/dist/runtime/parsing/output-parsing.js +1 -1
- package/dist/runtime/parsing/output-recovery.d.ts +9 -0
- package/dist/runtime/parsing/output-recovery.js +46 -1
- package/dist/runtime/support/compiled-binding.d.ts +5 -0
- package/dist/runtime/support/compiled-binding.js +12 -0
- package/dist/workspace/agent-binding-compiler.js +8 -0
- package/dist/workspace/object-loader.js +6 -0
- package/package.json +1 -1
|
@@ -1,102 +1,118 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { createMemoryMiddleware, createPatchToolCallsMiddleware, createSkillsMiddleware, createSubAgentMiddleware, createSummarizationMiddleware, StateBackend, } from "deepagents";
|
|
3
|
-
import { createAgent, humanInTheLoopMiddleware } from "langchain";
|
|
1
|
+
import { AIMessage, HumanMessage } from "@langchain/core/messages";
|
|
2
|
+
import { createMemoryMiddleware, createFilesystemMiddleware, createPatchToolCallsMiddleware, createSkillsMiddleware, createSubAgentMiddleware, createSummarizationMiddleware, StateBackend, } from "deepagents";
|
|
3
|
+
import { createAgent, humanInTheLoopMiddleware, todoListMiddleware } from "langchain";
|
|
4
4
|
import { createBuiltinMiddlewareTools } from "./tool/builtin-middleware-tools.js";
|
|
5
5
|
import { compileInterruptOn } from "./tool/interrupt-policy.js";
|
|
6
6
|
import { extractToolFallbackContext, extractVisibleOutput } from "../parsing/output-parsing.js";
|
|
7
7
|
import { isRecord } from "../../utils/object.js";
|
|
8
8
|
import { resolveDeclaredMiddleware } from "./tool/declared-middleware.js";
|
|
9
9
|
import { UPSTREAM_SESSION_CONFIG_KEY } from "./upstream-configurable-keys.js";
|
|
10
|
-
import { bindingHasLangChainSubagentSupport, bindingHasMiddlewareKind, getBindingExecutionKind, getBindingGeneralPurposeAgent, getBindingDeepAgentSubagents, getBindingInterruptCompatibilityRules, getBindingMiddlewareConfigs, getBindingMemorySources, getBindingPrimaryModel, getBindingPrimaryTools, getBindingSkills, getBindingSubagents, getBindingTaskDescription, isDeepAgentBinding, isLangChainBinding, } from "../support/compiled-binding.js";
|
|
10
|
+
import { bindingHasLangChainSubagentSupport, bindingHasMiddlewareKind, getBindingBuiltinToolsConfig, getBindingExecutionKind, getBindingGeneralPurposeAgent, getBindingDeepAgentSubagents, getBindingInterruptCompatibilityRules, getBindingMiddlewareConfigs, getBindingMemorySources, getBindingPrimaryModel, getBindingPrimaryTools, getBindingSkills, getBindingSubagents, getBindingTaskDescription, isDeepAgentBinding, isLangChainBinding, } from "../support/compiled-binding.js";
|
|
11
11
|
import { materializeDeepAgentSkillSourcePaths } from "./compat/deepagent-compat.js";
|
|
12
12
|
import { DEFAULT_SUBAGENT_PROMPT } from "../prompts/runtime-prompts.js";
|
|
13
|
-
import {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
modelFacingToOriginal: new Map(),
|
|
19
|
-
};
|
|
20
|
-
function readPlanStateSummaryCounts(summary) {
|
|
21
|
-
if (typeof summary !== "object" || summary === null) {
|
|
22
|
-
return null;
|
|
13
|
+
import { createContextHygieneMiddleware } from "./middleware/context-hygiene.js";
|
|
14
|
+
const INVALID_TOOL_MESSAGE_BLOCK_TYPES = new Set(["tool_use", "thinking", "redacted_thinking"]);
|
|
15
|
+
function extractDeepAgentTaskContent(result) {
|
|
16
|
+
if (typeof result !== "object" || result === null) {
|
|
17
|
+
return undefined;
|
|
23
18
|
}
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
if (!hasAny) {
|
|
28
|
-
return null;
|
|
19
|
+
const messages = result.messages;
|
|
20
|
+
if (!Array.isArray(messages) || messages.length === 0) {
|
|
21
|
+
return undefined;
|
|
29
22
|
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
};
|
|
34
|
-
}
|
|
35
|
-
function hasIncompletePlanTodos(value) {
|
|
36
|
-
if (!Array.isArray(value)) {
|
|
37
|
-
return null;
|
|
23
|
+
const lastMessage = messages[messages.length - 1];
|
|
24
|
+
if (typeof lastMessage !== "object" || lastMessage === null) {
|
|
25
|
+
return undefined;
|
|
38
26
|
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
27
|
+
if ("content" in lastMessage) {
|
|
28
|
+
return lastMessage.content;
|
|
29
|
+
}
|
|
30
|
+
if ("kwargs" in lastMessage) {
|
|
31
|
+
return (lastMessage.kwargs)?.content;
|
|
32
|
+
}
|
|
33
|
+
return undefined;
|
|
46
34
|
}
|
|
47
|
-
function
|
|
48
|
-
if (
|
|
49
|
-
return
|
|
35
|
+
export function extractSubagentRequestText(state) {
|
|
36
|
+
if (!isRecord(state)) {
|
|
37
|
+
return "";
|
|
50
38
|
}
|
|
51
|
-
|
|
52
|
-
|
|
39
|
+
const messages = Array.isArray(state.messages) ? state.messages : [];
|
|
40
|
+
const lastMessage = messages.length > 0 ? messages[messages.length - 1] : undefined;
|
|
41
|
+
if (!isRecord(lastMessage)) {
|
|
42
|
+
return "";
|
|
53
43
|
}
|
|
54
|
-
const
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
return summaryCounts.pending > 0 || summaryCounts.inProgress > 0;
|
|
44
|
+
const content = lastMessage.content;
|
|
45
|
+
if (typeof content === "string") {
|
|
46
|
+
return content;
|
|
58
47
|
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
48
|
+
if (Array.isArray(content)) {
|
|
49
|
+
return content
|
|
50
|
+
.map((block) => {
|
|
51
|
+
if (typeof block === "string") {
|
|
52
|
+
return block;
|
|
53
|
+
}
|
|
54
|
+
if (isRecord(block) && typeof block.text === "string") {
|
|
55
|
+
return block.text;
|
|
56
|
+
}
|
|
57
|
+
return "";
|
|
58
|
+
})
|
|
59
|
+
.filter((part) => part.length > 0)
|
|
60
|
+
.join("\n");
|
|
62
61
|
}
|
|
63
|
-
|
|
64
|
-
typed.summary,
|
|
65
|
-
typed.output,
|
|
66
|
-
typed.content,
|
|
67
|
-
typed.update,
|
|
68
|
-
typed.data,
|
|
69
|
-
typed.messages,
|
|
70
|
-
];
|
|
71
|
-
return nestedCandidates.some((candidate) => hasIncompletePlanStateInValue(candidate));
|
|
62
|
+
return "";
|
|
72
63
|
}
|
|
73
|
-
function
|
|
74
|
-
return
|
|
64
|
+
export function wrapRequestResultAsSubagentResponse(result) {
|
|
65
|
+
return {
|
|
66
|
+
messages: [new AIMessage({ content: result.output || "Task completed" })],
|
|
67
|
+
...(result.structuredResponse !== undefined ? { structuredResponse: result.structuredResponse } : {}),
|
|
68
|
+
};
|
|
75
69
|
}
|
|
76
|
-
function
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
}
|
|
81
|
-
if (state.emittedToolError) {
|
|
82
|
-
return "Delegated investigation encountered a tool failure before the plan completed.";
|
|
83
|
-
}
|
|
84
|
-
if (state.openTaskDelegations > 0) {
|
|
85
|
-
return "Delegated investigation did not finish before control returned to the parent agent.";
|
|
86
|
-
}
|
|
87
|
-
return "Delegated investigation ended before the plan was completed.";
|
|
70
|
+
function shouldUseControlledBuiltinSubagent(subagent) {
|
|
71
|
+
return subagent.builtinTools?.filesystem === false || subagent.builtinTools?.todos === false;
|
|
72
|
+
}
|
|
73
|
+
function createFallbackDeepAgentBackend() {
|
|
74
|
+
return new StateBackend({});
|
|
88
75
|
}
|
|
89
|
-
function
|
|
90
|
-
|
|
76
|
+
async function buildControlledBuiltinSubagent(input) {
|
|
77
|
+
const resolvedModel = input.subagent.model ? await input.resolveModel(input.subagent.model) : undefined;
|
|
78
|
+
const resolvedTools = input.subagent.tools ? input.resolveTools(input.subagent.tools, input.binding) : undefined;
|
|
79
|
+
const resolvedSkillPaths = (await materializeDeepAgentSkillSourcePaths({
|
|
80
|
+
workspaceRoot: input.binding?.harnessRuntime.workspaceRoot,
|
|
81
|
+
runtimeRoot: input.binding?.harnessRuntime.runtimeRoot,
|
|
82
|
+
ownerId: `${input.binding?.agent.id ?? "agent"}-${input.subagent.name}`,
|
|
83
|
+
skillPaths: input.subagent.skills,
|
|
84
|
+
})) ?? [];
|
|
85
|
+
const resolvedCustomMiddleware = await resolveDeclaredMiddleware(input.subagent.middleware, input.createDeclaredMiddlewareResolverOptions(input.binding));
|
|
86
|
+
const interruptOn = compileInterruptOn(input.subagent.tools ?? [], input.subagent.interruptOn);
|
|
87
|
+
const backend = input.resolveBackend?.(input.binding) ?? createFallbackDeepAgentBackend();
|
|
88
|
+
const builtinTools = input.subagent.builtinTools ?? {};
|
|
89
|
+
const middleware = [
|
|
90
|
+
...(builtinTools.todos === false ? [] : [todoListMiddleware()]),
|
|
91
|
+
...(resolvedSkillPaths.length > 0 ? [createSkillsMiddleware({ backend: backend, sources: resolvedSkillPaths })] : []),
|
|
92
|
+
...(builtinTools.filesystem === false ? [] : [createFilesystemMiddleware({ backend: backend })]),
|
|
93
|
+
createSummarizationMiddleware({
|
|
94
|
+
model: resolvedModel,
|
|
95
|
+
backend: backend,
|
|
96
|
+
}),
|
|
97
|
+
createPatchToolCallsMiddleware(),
|
|
98
|
+
...resolvedCustomMiddleware,
|
|
99
|
+
...(interruptOn ? [humanInTheLoopMiddleware({ interruptOn })] : []),
|
|
100
|
+
];
|
|
101
|
+
const runnable = createAgent({
|
|
102
|
+
model: resolvedModel,
|
|
103
|
+
systemPrompt: input.subagent.systemPrompt,
|
|
104
|
+
tools: resolvedTools,
|
|
105
|
+
middleware: middleware,
|
|
106
|
+
name: input.subagent.name,
|
|
107
|
+
...(input.subagent.responseFormat !== undefined ? { responseFormat: input.subagent.responseFormat } : {}),
|
|
108
|
+
});
|
|
109
|
+
return {
|
|
110
|
+
name: input.subagent.name,
|
|
111
|
+
description: input.subagent.description,
|
|
112
|
+
systemPrompt: input.subagent.systemPrompt,
|
|
113
|
+
runnable,
|
|
114
|
+
};
|
|
91
115
|
}
|
|
92
|
-
const DELEGATED_FAILURE_PLAN_RECONCILIATION_INSTRUCTION = [
|
|
93
|
-
"Your previous attempt ended with a tool failure while the todo board still had unfinished work.",
|
|
94
|
-
"Do not continue broad investigation from here.",
|
|
95
|
-
"If the failed command had malformed arguments you can correct locally, retry that same command once.",
|
|
96
|
-
"Otherwise, if a todo board already exists, call write_todos again and keep only the tasks that were actually completed in this session.",
|
|
97
|
-
"Remove the unfinished tasks that cannot proceed until the blocker is resolved.",
|
|
98
|
-
"Then return a concise blocker report.",
|
|
99
|
-
].join(" ");
|
|
100
116
|
export function buildBuiltinTaskSubagentMiddleware(input) {
|
|
101
117
|
const { selectedSubagent, builtinBackend, summarizationModel } = input;
|
|
102
118
|
const defaultSubagentMiddleware = [
|
|
@@ -190,6 +206,16 @@ export function resolveBuiltinMiddlewareBackend(input) {
|
|
|
190
206
|
}
|
|
191
207
|
export async function resolveSubagents(input) {
|
|
192
208
|
return Promise.all(input.subagents.map(async (subagent) => {
|
|
209
|
+
if (shouldUseControlledBuiltinSubagent(subagent)) {
|
|
210
|
+
return buildControlledBuiltinSubagent({
|
|
211
|
+
subagent,
|
|
212
|
+
binding: input.binding,
|
|
213
|
+
resolveModel: input.resolveModel,
|
|
214
|
+
resolveTools: input.resolveTools,
|
|
215
|
+
createDeclaredMiddlewareResolverOptions: input.createDeclaredMiddlewareResolverOptions,
|
|
216
|
+
resolveBackend: input.resolveBackend,
|
|
217
|
+
});
|
|
218
|
+
}
|
|
193
219
|
// Only pass DeepAgents-supported subagent fields through to upstream middleware.
|
|
194
220
|
// Harness-only extensions stay internal instead of becoming a second subagent dialect.
|
|
195
221
|
return {
|
|
@@ -256,118 +282,67 @@ export async function invokeBuiltinTaskTool(input) {
|
|
|
256
282
|
configurable: { [UPSTREAM_SESSION_CONFIG_KEY]: `${input.binding.agent.id}:builtin-task` },
|
|
257
283
|
...(input.options?.context ? { context: input.options.context } : {}),
|
|
258
284
|
};
|
|
259
|
-
const
|
|
260
|
-
messages: [
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
if (
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
rootAgentId: input.binding.agent.id,
|
|
276
|
-
countConfiguredToolsForAgentId: (agentId) => agentId === selectedSubagent.name ? resolvedSubagentTools.length : 0,
|
|
277
|
-
toolNameMapping: EMPTY_TOOL_NAME_MAPPING,
|
|
278
|
-
primaryTools: [],
|
|
279
|
-
state: projectionState,
|
|
280
|
-
});
|
|
281
|
-
for (const chunk of projectedChunks) {
|
|
282
|
-
if (chunk.kind !== "tool-result") {
|
|
283
|
-
continue;
|
|
284
|
-
}
|
|
285
|
-
executedToolResults.push({
|
|
286
|
-
toolName: chunk.toolName,
|
|
287
|
-
output: chunk.output,
|
|
288
|
-
isError: chunk.isError,
|
|
289
|
-
});
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
return { projectionState, executedToolResults };
|
|
293
|
-
};
|
|
294
|
-
let { projectionState, executedToolResults } = await runWithStreamInspection();
|
|
295
|
-
if (requiresDelegatedExecutionRecovery(projectionState)) {
|
|
296
|
-
const initialProjectionState = projectionState;
|
|
297
|
-
const initialExecutedToolResults = executedToolResults;
|
|
298
|
-
const initialDeterministicOutput = resolveDeterministicFinalOutput({
|
|
299
|
-
visibleOutput: initialProjectionState.emittedOutput.trim(),
|
|
300
|
-
executedToolResults: initialExecutedToolResults,
|
|
301
|
-
});
|
|
302
|
-
const recoveryInstruction = projectionState.hasIncompletePlanState && projectionState.emittedToolError
|
|
303
|
-
? `${AUTONOMOUS_INVESTIGATION_RECOVERY_INSTRUCTION}\n\n${DELEGATED_FAILURE_PLAN_RECONCILIATION_INSTRUCTION}`
|
|
304
|
-
: AUTONOMOUS_INVESTIGATION_RECOVERY_INSTRUCTION;
|
|
305
|
-
const recovered = await runWithStreamInspection(recoveryInstruction);
|
|
306
|
-
const recoveredDeterministicOutput = resolveDeterministicFinalOutput({
|
|
307
|
-
visibleOutput: recovered.projectionState.emittedOutput.trim(),
|
|
308
|
-
executedToolResults: recovered.executedToolResults,
|
|
309
|
-
});
|
|
310
|
-
const recoveredHasSubstantiveExecution = recoveredDeterministicOutput.length > 0;
|
|
311
|
-
if (recoveredHasSubstantiveExecution) {
|
|
312
|
-
projectionState = recovered.projectionState;
|
|
313
|
-
executedToolResults = recovered.executedToolResults;
|
|
314
|
-
}
|
|
315
|
-
else {
|
|
316
|
-
projectionState = initialProjectionState;
|
|
317
|
-
executedToolResults = initialExecutedToolResults;
|
|
318
|
-
if (initialDeterministicOutput) {
|
|
319
|
-
projectionState = {
|
|
320
|
-
...projectionState,
|
|
321
|
-
emittedOutput: initialDeterministicOutput,
|
|
322
|
-
};
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
}
|
|
326
|
-
if (requiresDelegatedExecutionRecovery(projectionState)) {
|
|
327
|
-
throw new Error(formatDelegatedExecutionBlocker(projectionState));
|
|
328
|
-
}
|
|
329
|
-
if (projectionState.emittedToolError) {
|
|
330
|
-
const blockerMessage = resolveDeterministicFinalOutput({
|
|
331
|
-
visibleOutput: projectionState.emittedOutput.trim(),
|
|
332
|
-
executedToolResults,
|
|
333
|
-
}) || formatDelegatedExecutionBlocker(projectionState);
|
|
334
|
-
if (hasUnresolvedDelegatedExecution(projectionState) || !projectionState.emittedSuccessfulToolResult) {
|
|
335
|
-
throw new Error(blockerMessage);
|
|
336
|
-
}
|
|
337
|
-
}
|
|
338
|
-
const deterministicOutput = resolveDeterministicFinalOutput({
|
|
339
|
-
visibleOutput: projectionState.emittedOutput.trim(),
|
|
340
|
-
executedToolResults,
|
|
285
|
+
const result = await runnable.invoke({
|
|
286
|
+
messages: [new HumanMessage({ content: description })],
|
|
287
|
+
}, invokeConfig);
|
|
288
|
+
const structuredResponse = typeof result === "object" && result !== null && "structuredResponse" in result
|
|
289
|
+
? result.structuredResponse
|
|
290
|
+
: undefined;
|
|
291
|
+
if (structuredResponse !== undefined) {
|
|
292
|
+
return JSON.stringify(structuredResponse);
|
|
293
|
+
}
|
|
294
|
+
const taskContent = extractDeepAgentTaskContent(result);
|
|
295
|
+
if (Array.isArray(taskContent)) {
|
|
296
|
+
const filtered = taskContent.filter((block) => {
|
|
297
|
+
const blockType = typeof block === "object" && block !== null && "type" in block
|
|
298
|
+
? block.type
|
|
299
|
+
: undefined;
|
|
300
|
+
return !(typeof blockType === "string" && INVALID_TOOL_MESSAGE_BLOCK_TYPES.has(blockType));
|
|
341
301
|
});
|
|
342
|
-
if (
|
|
343
|
-
return
|
|
344
|
-
}
|
|
345
|
-
if (projectionState.emittedToolResult) {
|
|
346
|
-
throw new Error("Delegated investigation performed tool work but did not return surfaced findings.");
|
|
302
|
+
if (filtered.length > 0) {
|
|
303
|
+
return filtered;
|
|
347
304
|
}
|
|
348
305
|
}
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
result = await runnable.invoke(buildMessages(AUTONOMOUS_INVESTIGATION_RECOVERY_INSTRUCTION), invokeConfig);
|
|
352
|
-
}
|
|
353
|
-
if (hasIncompletePlanStateInValue(result)) {
|
|
354
|
-
throw new Error(extractVisibleOutput(result) || extractToolFallbackContext(result) || "Delegated investigation ended before the plan was completed.");
|
|
306
|
+
else if (taskContent !== undefined && taskContent !== null && taskContent !== "") {
|
|
307
|
+
return taskContent;
|
|
355
308
|
}
|
|
356
309
|
const visibleOutput = extractVisibleOutput(result);
|
|
310
|
+
if (visibleOutput) {
|
|
311
|
+
return visibleOutput;
|
|
312
|
+
}
|
|
357
313
|
const fallbackOutput = extractToolFallbackContext(result);
|
|
358
|
-
|
|
359
|
-
? result.structuredResponse
|
|
360
|
-
: undefined;
|
|
361
|
-
return visibleOutput || fallbackOutput || (structuredResponse !== undefined ? JSON.stringify(structuredResponse) : "") || JSON.stringify(result);
|
|
314
|
+
return fallbackOutput || "Task completed";
|
|
362
315
|
}
|
|
363
316
|
export async function resolveBuiltinMiddlewareTools(input) {
|
|
364
317
|
const backend = input.resolveBuiltinMiddlewareBackend(input.binding, input.options);
|
|
365
|
-
|
|
318
|
+
const tools = (await createBuiltinMiddlewareTools(backend, {
|
|
366
319
|
includeTaskTool: false,
|
|
367
320
|
workspaceRoot: input.binding.harnessRuntime.workspaceRoot,
|
|
368
321
|
toolRuntimeContext: input.options?.toolRuntimeContext,
|
|
369
322
|
invokeTaskTool: undefined,
|
|
370
|
-
});
|
|
323
|
+
}));
|
|
324
|
+
const builtinTools = getBindingBuiltinToolsConfig(input.binding) ?? {};
|
|
325
|
+
if (builtinTools.todos === false) {
|
|
326
|
+
tools.delete("write_todos");
|
|
327
|
+
tools.delete("read_todos");
|
|
328
|
+
}
|
|
329
|
+
if (builtinTools.filesystem === false) {
|
|
330
|
+
for (const name of [
|
|
331
|
+
"ls",
|
|
332
|
+
"list_files",
|
|
333
|
+
"read_file",
|
|
334
|
+
"write_file",
|
|
335
|
+
"edit_file",
|
|
336
|
+
"glob",
|
|
337
|
+
"grep",
|
|
338
|
+
"search_files",
|
|
339
|
+
"execute",
|
|
340
|
+
"run_command",
|
|
341
|
+
]) {
|
|
342
|
+
tools.delete(name);
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
return tools;
|
|
371
346
|
}
|
|
372
347
|
export async function materializeAutomaticSummarizationMiddleware(input) {
|
|
373
348
|
const primaryModel = getBindingPrimaryModel(input.binding);
|
|
@@ -423,6 +398,7 @@ export async function resolveMiddleware(input) {
|
|
|
423
398
|
? await input.resolveLangChainRuntimeExtensionMiddleware(input.binding)
|
|
424
399
|
: [];
|
|
425
400
|
const middleware = [
|
|
401
|
+
createContextHygieneMiddleware(),
|
|
426
402
|
...declarativeUpstreamMiddleware,
|
|
427
403
|
...runtimeExtensionMiddleware,
|
|
428
404
|
...(input.runtimeAdapterOptions.middlewareResolver ? input.runtimeAdapterOptions.middlewareResolver(input.binding) : []),
|
|
@@ -2,7 +2,7 @@ import { getBindingSkills } from "../../support/compiled-binding.js";
|
|
|
2
2
|
import { extractMessageText, normalizeMessageContent } from "../../../utils/message-content.js";
|
|
3
3
|
import { readSkillMetadata } from "../../skills/skill-metadata.js";
|
|
4
4
|
import { summarizeAssistantText } from "../direct-builtin-utility.js";
|
|
5
|
-
import { renderDurableMemoryContextPrompt, renderSlashCommandSkillInstruction } from "../../prompts/runtime-prompts.js";
|
|
5
|
+
import { renderDurableMemoryContextPrompt, renderSlashCommandSkillInstruction, } from "../../prompts/runtime-prompts.js";
|
|
6
6
|
import { hasExplicitResourceReference, } from "../../harness/system/runtime-memory-policy.js";
|
|
7
7
|
const MAX_HISTORY_ASSISTANT_CHARS = 1600;
|
|
8
8
|
const MAX_HISTORY_ASSISTANT_LINES = 40;
|
|
@@ -125,6 +125,42 @@ function buildContextualFollowUpInstruction(inputText, hasDurableMemory) {
|
|
|
125
125
|
}
|
|
126
126
|
return "Answer the user's current follow-up directly from the recalled context when it remains relevant. If recalled memory conflicts with the current system prompt, runtime policy, recovery instruction, available tool evidence, or the user's current request, ignore the recalled memory for this turn. If the current user turn corrects, revokes, deletes, or replaces recalled memory, treat the user's latest statement as newer than the recalled memory for this turn. Do not frame the reply as a fresh URL or page summary unless the current user turn explicitly includes a new resource to inspect.";
|
|
127
127
|
}
|
|
128
|
+
function isIncidentFollowUpTurn(inputText) {
|
|
129
|
+
const normalized = inputText.trim().toLowerCase();
|
|
130
|
+
if (!normalized || hasExplicitResourceReference(normalized)) {
|
|
131
|
+
return false;
|
|
132
|
+
}
|
|
133
|
+
return /(the rca|deep research.*rca|root cause|go deeper|those issues|these issues|that issue|current incident|kubernetes issues)/i.test(normalized);
|
|
134
|
+
}
|
|
135
|
+
function findLastAssistantText(history) {
|
|
136
|
+
for (let index = history.length - 1; index >= 0; index -= 1) {
|
|
137
|
+
const message = history[index];
|
|
138
|
+
if (message?.role !== "assistant") {
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
const text = extractMessageText(message.content).trim();
|
|
142
|
+
if (text) {
|
|
143
|
+
return compactHistoryText(text);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return undefined;
|
|
147
|
+
}
|
|
148
|
+
function buildIncidentFollowUpInstruction(history, inputText) {
|
|
149
|
+
if (!isIncidentFollowUpTurn(inputText)) {
|
|
150
|
+
return undefined;
|
|
151
|
+
}
|
|
152
|
+
const priorAssistantText = findLastAssistantText(history);
|
|
153
|
+
if (!priorAssistantText) {
|
|
154
|
+
return undefined;
|
|
155
|
+
}
|
|
156
|
+
return [
|
|
157
|
+
"Treat the user's current turn as a follow-up on the current incident, not as a generic background-knowledge request.",
|
|
158
|
+
"Use the immediately prior assistant findings below as the default incident context unless the user explicitly overrides it.",
|
|
159
|
+
"",
|
|
160
|
+
"Prior assistant findings:",
|
|
161
|
+
priorAssistantText,
|
|
162
|
+
].join("\n");
|
|
163
|
+
}
|
|
128
164
|
export function buildSlashCommandSkillInstruction(binding, input) {
|
|
129
165
|
const inputText = extractMessageText(input).trim();
|
|
130
166
|
const match = inputText.match(/^\/([a-z0-9]+(?:-[a-z0-9]+)*)(?:\s+([\s\S]*))?$/i);
|
|
@@ -162,6 +198,7 @@ export function buildInvocationRequest(binding, history, input, options = {}) {
|
|
|
162
198
|
suppressHistoryTurns: Boolean(memoryInstruction) && !hasExplicitResourceReference(inputText),
|
|
163
199
|
});
|
|
164
200
|
const contextualFollowUpInstruction = buildContextualFollowUpInstruction(inputText, Boolean(memoryInstruction));
|
|
201
|
+
const incidentFollowUpInstruction = buildIncidentFollowUpInstruction(history, inputText);
|
|
165
202
|
const conversationLanguage = resolveConversationLanguage(history, inputText);
|
|
166
203
|
const languageInstruction = !inferMessageLanguage(inputText) && conversationLanguage
|
|
167
204
|
? {
|
|
@@ -175,6 +212,7 @@ export function buildInvocationRequest(binding, history, input, options = {}) {
|
|
|
175
212
|
messages: [
|
|
176
213
|
...(memoryInstruction ? [{ role: "system", content: memoryInstruction }] : []),
|
|
177
214
|
...(contextualFollowUpInstruction ? [{ role: "system", content: contextualFollowUpInstruction }] : []),
|
|
215
|
+
...(incidentFollowUpInstruction ? [{ role: "system", content: incidentFollowUpInstruction }] : []),
|
|
178
216
|
...(languageInstruction ? [{ role: "system", content: languageInstruction }] : []),
|
|
179
217
|
...(userInvocableInstruction ? [{ role: "system", content: userInvocableInstruction }] : []),
|
|
180
218
|
...messages,
|
|
@@ -6,8 +6,29 @@ export function truncateLines(lines, maxChars = 12_000) {
|
|
|
6
6
|
}
|
|
7
7
|
return `${joined.slice(0, maxChars - 18)}\n...[truncated]`;
|
|
8
8
|
}
|
|
9
|
+
function findTodosArray(value, depth = 0) {
|
|
10
|
+
if (depth > 3 || !isRecord(value)) {
|
|
11
|
+
return [];
|
|
12
|
+
}
|
|
13
|
+
if (Array.isArray(value.todos)) {
|
|
14
|
+
return value.todos;
|
|
15
|
+
}
|
|
16
|
+
const nestedFromUpdate = findTodosArray(value.update, depth + 1);
|
|
17
|
+
if (nestedFromUpdate.length > 0) {
|
|
18
|
+
return nestedFromUpdate;
|
|
19
|
+
}
|
|
20
|
+
const nestedFromOutput = findTodosArray(value.output, depth + 1);
|
|
21
|
+
if (nestedFromOutput.length > 0) {
|
|
22
|
+
return nestedFromOutput;
|
|
23
|
+
}
|
|
24
|
+
const nestedFromData = findTodosArray(value.data, depth + 1);
|
|
25
|
+
if (nestedFromData.length > 0) {
|
|
26
|
+
return nestedFromData;
|
|
27
|
+
}
|
|
28
|
+
return [];
|
|
29
|
+
}
|
|
9
30
|
export function summarizeBuiltinWriteTodosArgs(args) {
|
|
10
|
-
const todos =
|
|
31
|
+
const todos = findTodosArray(args);
|
|
11
32
|
const items = todos.flatMap((todo) => {
|
|
12
33
|
if (!isRecord(todo)) {
|
|
13
34
|
return [];
|
|
@@ -16,11 +37,20 @@ export function summarizeBuiltinWriteTodosArgs(args) {
|
|
|
16
37
|
? todo.content.trim()
|
|
17
38
|
: typeof todo.description === "string" && todo.description.trim().length > 0
|
|
18
39
|
? todo.description.trim()
|
|
19
|
-
: ""
|
|
40
|
+
: typeof todo.title === "string" && todo.title.trim().length > 0
|
|
41
|
+
? todo.title.trim()
|
|
42
|
+
: typeof todo.name === "string" && todo.name.trim().length > 0
|
|
43
|
+
? todo.name.trim()
|
|
44
|
+
: typeof todo.text === "string" && todo.text.trim().length > 0
|
|
45
|
+
? todo.text.trim()
|
|
46
|
+
: "";
|
|
20
47
|
const status = typeof todo.status === "string" && todo.status.trim().length > 0 ? todo.status.trim() : "pending";
|
|
21
48
|
const metadata = isRecord(todo.metadata) ? todo.metadata : undefined;
|
|
22
49
|
return content ? [{
|
|
23
|
-
...(typeof todo.id === "string" && todo.id.trim().length > 0
|
|
50
|
+
...((typeof todo.id === "string" && todo.id.trim().length > 0)
|
|
51
|
+
|| typeof todo.id === "number"
|
|
52
|
+
? { id: String(todo.id).trim() }
|
|
53
|
+
: {}),
|
|
24
54
|
content,
|
|
25
55
|
status,
|
|
26
56
|
...(typeof todo.ownerAgentId === "string" && todo.ownerAgentId.trim().length > 0 ? { ownerAgentId: todo.ownerAgentId.trim() } : {}),
|
|
@@ -237,17 +237,18 @@ function isInternalRuntimeSpillPathErrorValue(value) {
|
|
|
237
237
|
}
|
|
238
238
|
function recordDelegatedFindings(state, value, source = "tool") {
|
|
239
239
|
if (state.taskDelegationFindingsStack.length === 0) {
|
|
240
|
-
return;
|
|
240
|
+
return false;
|
|
241
241
|
}
|
|
242
242
|
const normalized = normalizeDelegatedFindingsText(value);
|
|
243
243
|
if (!normalized) {
|
|
244
|
-
return;
|
|
244
|
+
return false;
|
|
245
245
|
}
|
|
246
246
|
const current = state.taskDelegationFindingsStack[state.taskDelegationFindingsStack.length - 1] ?? "";
|
|
247
247
|
if (source === "terminal" && current) {
|
|
248
|
-
return;
|
|
248
|
+
return true;
|
|
249
249
|
}
|
|
250
250
|
state.taskDelegationFindingsStack[state.taskDelegationFindingsStack.length - 1] = normalized;
|
|
251
|
+
return true;
|
|
251
252
|
}
|
|
252
253
|
function updateDelegationState(state, event, countConfiguredToolsForAgentId) {
|
|
253
254
|
if (typeof event !== "object" || event === null) {
|
|
@@ -392,8 +393,8 @@ export function projectRuntimeStreamEvent(params) {
|
|
|
392
393
|
if (!allowVisibleContent) {
|
|
393
394
|
const delegatedTerminalOutput = extractTerminalStreamOutput(event);
|
|
394
395
|
if (delegatedTerminalOutput) {
|
|
395
|
-
state.emittedDelegatedTerminalOutput =
|
|
396
|
-
|
|
396
|
+
state.emittedDelegatedTerminalOutput =
|
|
397
|
+
state.emittedDelegatedTerminalOutput || recordDelegatedFindings(state, delegatedTerminalOutput, "terminal");
|
|
397
398
|
}
|
|
398
399
|
}
|
|
399
400
|
if (output && !shouldSuppressVisibleToolCallText(output)) {
|
|
@@ -243,6 +243,13 @@ export declare const BUILTIN_MIDDLEWARE_TOOL_DESCRIPTORS: readonly [{
|
|
|
243
243
|
readonly name: "task";
|
|
244
244
|
readonly description: "Delegate a bounded task to a subagent.";
|
|
245
245
|
}];
|
|
246
|
+
export declare function filterBuiltinMiddlewareToolDescriptors(options?: {
|
|
247
|
+
filesystem?: boolean;
|
|
248
|
+
todos?: boolean;
|
|
249
|
+
}): Array<{
|
|
250
|
+
name: string;
|
|
251
|
+
description: string;
|
|
252
|
+
}>;
|
|
246
253
|
export declare function createBuiltinMiddlewareTools(backend: BuiltinMiddlewareBackend, options: {
|
|
247
254
|
includeTaskTool: boolean;
|
|
248
255
|
invokeTaskTool?: (input: unknown) => Promise<unknown>;
|
|
@@ -4,28 +4,10 @@ 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
|
-
const taskToolSchema = z.
|
|
8
|
-
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
9
|
-
return value;
|
|
10
|
-
}
|
|
11
|
-
return normalizeTaskToolInput(value);
|
|
12
|
-
}, z.object({
|
|
7
|
+
const taskToolSchema = z.object({
|
|
13
8
|
description: z.string(),
|
|
14
9
|
subagent_type: z.string(),
|
|
15
|
-
}).passthrough()
|
|
16
|
-
function normalizeTaskToolInput(typed) {
|
|
17
|
-
const description = typeof typed.description === "string" && typed.description.trim().length > 0
|
|
18
|
-
? typed.description
|
|
19
|
-
: undefined;
|
|
20
|
-
const subagentType = typeof typed.subagent_type === "string" && typed.subagent_type.trim().length > 0
|
|
21
|
-
? typed.subagent_type
|
|
22
|
-
: undefined;
|
|
23
|
-
return {
|
|
24
|
-
...typed,
|
|
25
|
-
...(description !== undefined ? { description } : {}),
|
|
26
|
-
...(subagentType !== undefined ? { subagent_type: subagentType } : {}),
|
|
27
|
-
};
|
|
28
|
-
}
|
|
10
|
+
}).passthrough();
|
|
29
11
|
export const BUILTIN_MIDDLEWARE_TOOL_DESCRIPTORS = [
|
|
30
12
|
{ name: "write_todos", description: "Create and update the runtime todo board for multi-step work." },
|
|
31
13
|
{ name: "read_todos", description: "Read the current runtime todo board." },
|
|
@@ -46,6 +28,30 @@ export const BUILTIN_MIDDLEWARE_TOOL_DESCRIPTORS = [
|
|
|
46
28
|
{ name: "schedule_task", description: "Create, inspect, update, list, and delete system-level scheduled tasks." },
|
|
47
29
|
{ name: "task", description: "Delegate a bounded task to a subagent." },
|
|
48
30
|
];
|
|
31
|
+
export function filterBuiltinMiddlewareToolDescriptors(options) {
|
|
32
|
+
return BUILTIN_MIDDLEWARE_TOOL_DESCRIPTORS.filter((descriptor) => {
|
|
33
|
+
if (options?.todos === false
|
|
34
|
+
&& (descriptor.name === "write_todos" || descriptor.name === "read_todos")) {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
if (options?.filesystem === false
|
|
38
|
+
&& [
|
|
39
|
+
"ls",
|
|
40
|
+
"list_files",
|
|
41
|
+
"read_file",
|
|
42
|
+
"write_file",
|
|
43
|
+
"edit_file",
|
|
44
|
+
"glob",
|
|
45
|
+
"grep",
|
|
46
|
+
"search_files",
|
|
47
|
+
"execute",
|
|
48
|
+
"run_command",
|
|
49
|
+
].includes(descriptor.name)) {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
return true;
|
|
53
|
+
}).map((descriptor) => ({ ...descriptor }));
|
|
54
|
+
}
|
|
49
55
|
function toDisplayContent(content) {
|
|
50
56
|
if (typeof content === "string") {
|
|
51
57
|
return content;
|
|
@@ -214,9 +220,12 @@ export async function createBuiltinMiddlewareTools(backend, options) {
|
|
|
214
220
|
description: "Create and update the runtime todo board for multi-step work.",
|
|
215
221
|
schema: z.object({
|
|
216
222
|
todos: z.array(z.object({
|
|
217
|
-
id: z.string().optional(),
|
|
223
|
+
id: z.union([z.string(), z.number()]).optional(),
|
|
218
224
|
content: z.string().optional(),
|
|
219
225
|
description: z.string().optional(),
|
|
226
|
+
title: z.string().optional(),
|
|
227
|
+
name: z.string().optional(),
|
|
228
|
+
text: z.string().optional(),
|
|
220
229
|
status: z.enum(["pending", "in_progress", "completed", "failed", "cancelled"]).optional(),
|
|
221
230
|
ownerAgentId: z.string().optional(),
|
|
222
231
|
startedAt: z.string().optional(),
|
|
@@ -615,9 +624,7 @@ export async function createBuiltinMiddlewareTools(backend, options) {
|
|
|
615
624
|
name: "task",
|
|
616
625
|
description: "Delegate a bounded task to a subagent.",
|
|
617
626
|
schema: taskToolSchema,
|
|
618
|
-
invoke: async (input
|
|
619
|
-
? normalizeTaskToolInput(input)
|
|
620
|
-
: {}),
|
|
627
|
+
invoke: async (input) => options.invokeTaskTool(input),
|
|
621
628
|
});
|
|
622
629
|
}
|
|
623
630
|
return tools;
|