@botbotgo/agent-harness 0.0.298 → 0.0.299

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.
Files changed (166) hide show
  1. package/README.md +77 -37
  2. package/README.zh.md +79 -30
  3. package/dist/acp.d.ts +3 -0
  4. package/dist/acp.js +10 -2
  5. package/dist/api.d.ts +14 -2
  6. package/dist/api.js +19 -3
  7. package/dist/cli.d.ts +18 -1
  8. package/dist/cli.js +1408 -319
  9. package/dist/client/acp.d.ts +9 -3
  10. package/dist/client/acp.js +55 -1
  11. package/dist/client/in-process.d.ts +5 -2
  12. package/dist/client/in-process.js +4 -6
  13. package/dist/client/index.d.ts +1 -1
  14. package/dist/client/types.d.ts +6 -5
  15. package/dist/config/agents/direct.yaml +7 -17
  16. package/dist/config/agents/orchestra.yaml +9 -65
  17. package/dist/config/catalogs/embedding-models.yaml +1 -1
  18. package/dist/config/catalogs/stores.yaml +1 -1
  19. package/dist/config/knowledge/knowledge-runtime.yaml +36 -2
  20. package/dist/config/knowledge/procedural-memory-runtime.yaml +78 -0
  21. package/dist/config/{catalogs/models.yaml → models.yaml} +2 -2
  22. package/dist/config/prompts/direct-system.md +16 -0
  23. package/dist/config/prompts/orchestra-system.md +62 -0
  24. package/dist/config/prompts/routing-system.md +14 -0
  25. package/dist/config/runtime/runtime-memory.yaml +39 -5
  26. package/dist/config/runtime/workspace.yaml +7 -16
  27. package/dist/contracts/runtime.d.ts +242 -1
  28. package/dist/contracts/workspace.d.ts +2 -0
  29. package/dist/index.d.ts +5 -3
  30. package/dist/index.js +2 -1
  31. package/dist/init-project.js +178 -33
  32. package/dist/knowledge/contracts.d.ts +5 -0
  33. package/dist/knowledge/module.d.ts +5 -0
  34. package/dist/knowledge/module.js +340 -18
  35. package/dist/package-version.d.ts +1 -1
  36. package/dist/package-version.js +1 -1
  37. package/dist/persistence/file-store.d.ts +5 -1
  38. package/dist/persistence/file-store.js +16 -0
  39. package/dist/persistence/sqlite-store.d.ts +4 -1
  40. package/dist/persistence/sqlite-store.js +88 -14
  41. package/dist/persistence/types.d.ts +4 -1
  42. package/dist/procedural/config.d.ts +63 -0
  43. package/dist/procedural/config.js +125 -0
  44. package/dist/procedural/index.d.ts +2 -0
  45. package/dist/procedural/index.js +1 -0
  46. package/dist/protocol/ag-ui/http.d.ts +3 -0
  47. package/dist/protocol/ag-ui/http.js +10 -0
  48. package/dist/request-events.d.ts +63 -0
  49. package/dist/request-events.js +400 -0
  50. package/dist/resource/isolation.js +11 -0
  51. package/dist/resource/resource-impl.d.ts +1 -0
  52. package/dist/resource/resource-impl.js +103 -12
  53. package/dist/resources/init-templates/agent-context/deep-research.md +5 -0
  54. package/dist/resources/init-templates/prompts/research-analyst-basic.md +1 -0
  55. package/dist/resources/init-templates/prompts/research-analyst-web-search.md +1 -0
  56. package/dist/resources/init-templates/prompts/research-host-deep-research-basic.md +1 -0
  57. package/dist/resources/init-templates/prompts/research-host-deep-research-web-search.md +1 -0
  58. package/dist/resources/init-templates/prompts/research-host-single-agent-basic.md +1 -0
  59. package/dist/resources/init-templates/prompts/research-host-single-agent-web-search.md +1 -0
  60. package/dist/resources/prompts/runtime/browser-capability-disclaimer-recovery.md +1 -0
  61. package/dist/resources/prompts/runtime/default-subagent.md +2 -0
  62. package/dist/resources/prompts/runtime/durable-memory-context.md +7 -0
  63. package/dist/resources/prompts/runtime/execution-with-tool-evidence-retry.md +1 -0
  64. package/dist/resources/prompts/runtime/execution-with-tool-evidence.md +1 -0
  65. package/dist/resources/prompts/runtime/invalid-tool-selection-recovery.md +1 -0
  66. package/dist/resources/prompts/runtime/memory-manager.md +31 -0
  67. package/dist/resources/prompts/runtime/memory-mutation-reconciliation.md +22 -0
  68. package/dist/resources/prompts/runtime/slash-command-skill.md +6 -0
  69. package/dist/resources/prompts/runtime/strict-tool-json.md +1 -0
  70. package/dist/resources/prompts/runtime/workspace-boundary-guidance.md +3 -0
  71. package/dist/resources/prompts/runtime/workspace-relative-path.md +1 -0
  72. package/dist/resources/prompts/runtime/write-todos-descriptive-content.md +1 -0
  73. package/dist/resources/prompts/runtime/write-todos-full-entry.md +1 -0
  74. package/dist/resources/prompts/runtime/write-todos-non-empty-initial-list.md +1 -0
  75. package/dist/resources/tools/_runtime_tool_helpers.mjs +152 -0
  76. package/dist/resources/tools/cancel_request.mjs +21 -0
  77. package/dist/resources/tools/fetch_url.mjs +23 -0
  78. package/dist/resources/tools/http_request.mjs +30 -0
  79. package/dist/resources/tools/inspect_approvals.mjs +27 -0
  80. package/dist/resources/tools/inspect_artifacts.mjs +21 -0
  81. package/dist/resources/tools/inspect_events.mjs +21 -0
  82. package/dist/resources/tools/inspect_requests.mjs +27 -0
  83. package/dist/resources/tools/inspect_sessions.mjs +21 -0
  84. package/dist/resources/tools/list_files.mjs +27 -0
  85. package/dist/resources/tools/read_artifact.mjs +22 -0
  86. package/dist/resources/tools/request_approval.mjs +27 -0
  87. package/dist/resources/tools/run_command.mjs +21 -0
  88. package/dist/resources/tools/schedule_task.mjs +76 -0
  89. package/dist/resources/tools/search_files.mjs +47 -0
  90. package/dist/resources/tools/send_message.mjs +23 -0
  91. package/dist/runtime/adapter/direct-builtin-utility.d.ts +1 -0
  92. package/dist/runtime/adapter/direct-builtin-utility.js +90 -0
  93. package/dist/runtime/adapter/flow/execution-context.d.ts +1 -1
  94. package/dist/runtime/adapter/flow/execution-context.js +1 -1
  95. package/dist/runtime/adapter/flow/invocation-flow.d.ts +1 -0
  96. package/dist/runtime/adapter/flow/invocation-flow.js +9 -1
  97. package/dist/runtime/adapter/flow/invoke-runtime.d.ts +1 -1
  98. package/dist/runtime/adapter/flow/stream-runtime.d.ts +5 -1
  99. package/dist/runtime/adapter/flow/stream-runtime.js +556 -35
  100. package/dist/runtime/adapter/invocation-result.js +3 -2
  101. package/dist/runtime/adapter/local-tool-invocation.d.ts +1 -1
  102. package/dist/runtime/adapter/local-tool-invocation.js +28 -4
  103. package/dist/runtime/adapter/middleware-assembly.js +3 -1
  104. package/dist/runtime/adapter/model/invocation-request.d.ts +4 -1
  105. package/dist/runtime/adapter/model/invocation-request.js +138 -16
  106. package/dist/runtime/adapter/model/message-assembly.js +2 -6
  107. package/dist/runtime/adapter/model/model-providers.js +103 -5
  108. package/dist/runtime/adapter/resilience.js +17 -2
  109. package/dist/runtime/adapter/runtime-adapter-support.d.ts +11 -7
  110. package/dist/runtime/adapter/runtime-adapter-support.js +39 -5
  111. package/dist/runtime/adapter/tool/builtin-middleware-tools.d.ts +63 -1
  112. package/dist/runtime/adapter/tool/builtin-middleware-tools.js +193 -21
  113. package/dist/runtime/adapter/tool/tool-arguments.d.ts +3 -1
  114. package/dist/runtime/adapter/tool/tool-arguments.js +52 -17
  115. package/dist/runtime/adapter/tool-resolution.d.ts +1 -0
  116. package/dist/runtime/adapter/tool-resolution.js +4 -2
  117. package/dist/runtime/agent-runtime-adapter.d.ts +27 -0
  118. package/dist/runtime/agent-runtime-adapter.js +163 -11
  119. package/dist/runtime/harness/events/event-bus.d.ts +1 -0
  120. package/dist/runtime/harness/events/event-bus.js +3 -0
  121. package/dist/runtime/harness/events/event-sink.d.ts +3 -0
  122. package/dist/runtime/harness/events/event-sink.js +16 -7
  123. package/dist/runtime/harness/events/streaming.d.ts +18 -1
  124. package/dist/runtime/harness/events/streaming.js +23 -10
  125. package/dist/runtime/harness/run/inspection.js +26 -5
  126. package/dist/runtime/harness/run/stream-run.d.ts +13 -4
  127. package/dist/runtime/harness/run/stream-run.js +448 -4
  128. package/dist/runtime/harness/run/surface-semantics.js +7 -34
  129. package/dist/runtime/harness/system/runtime-memory-manager.d.ts +3 -0
  130. package/dist/runtime/harness/system/runtime-memory-manager.js +384 -69
  131. package/dist/runtime/harness/system/runtime-memory-policy.d.ts +20 -1
  132. package/dist/runtime/harness/system/runtime-memory-policy.js +65 -17
  133. package/dist/runtime/harness/system/runtime-memory-records.js +100 -0
  134. package/dist/runtime/harness/system/runtime-memory-sync.js +2 -2
  135. package/dist/runtime/harness/system/store.d.ts +4 -0
  136. package/dist/runtime/harness/system/store.js +153 -0
  137. package/dist/runtime/harness.d.ts +9 -1
  138. package/dist/runtime/harness.js +141 -7
  139. package/dist/runtime/maintenance/sqlite-checkpoint-saver.d.ts +8 -3
  140. package/dist/runtime/maintenance/sqlite-checkpoint-saver.js +152 -53
  141. package/dist/runtime/parsing/output-parsing.d.ts +10 -2
  142. package/dist/runtime/parsing/output-parsing.js +223 -16
  143. package/dist/runtime/parsing/stream-event-parsing.d.ts +7 -0
  144. package/dist/runtime/parsing/stream-event-parsing.js +51 -1
  145. package/dist/runtime/scheduling/system-schedule-manager.d.ts +41 -0
  146. package/dist/runtime/scheduling/system-schedule-manager.js +532 -0
  147. package/dist/runtime/support/embedding-models.d.ts +1 -1
  148. package/dist/runtime/support/embedding-models.js +5 -2
  149. package/dist/runtime/support/runtime-factories.js +1 -1
  150. package/dist/runtime/support/runtime-layout.d.ts +3 -0
  151. package/dist/runtime/support/runtime-layout.js +10 -1
  152. package/dist/runtime/support/runtime-prompts.d.ts +30 -0
  153. package/dist/runtime/support/runtime-prompts.js +55 -0
  154. package/dist/runtime/support/vector-stores.d.ts +1 -1
  155. package/dist/runtime/support/vector-stores.js +5 -2
  156. package/dist/upstream-events.js +8 -7
  157. package/dist/utils/bundled-text.d.ts +3 -0
  158. package/dist/utils/bundled-text.js +25 -0
  159. package/dist/utils/id.js +3 -2
  160. package/dist/workspace/agent-binding-compiler.js +53 -13
  161. package/dist/workspace/object-loader.js +64 -2
  162. package/dist/workspace/support/workspace-ref-utils.d.ts +2 -1
  163. package/dist/workspace/support/workspace-ref-utils.js +24 -5
  164. package/dist/workspace/yaml-object-reader.d.ts +1 -0
  165. package/dist/workspace/yaml-object-reader.js +95 -17
  166. package/package.json +11 -5
@@ -3,6 +3,22 @@ import { createModelFacingToolNameLookupCandidates, resolveModelFacingToolName }
3
3
  import { canReplayToolCallsLocally } from "./tool/tool-replay.js";
4
4
  import { extractToolCallsFromResult, normalizeToolArgsForSchema, stringifyToolOutput } from "./tool/tool-arguments.js";
5
5
  import { extractMemoryCandidatesFromToolOutput } from "../harness/system/runtime-memory-candidates.js";
6
+ const TOOL_FOLLOW_UP_INSTRUCTION = "One or more tool results are already available in this conversation. Answer the user's current request directly from the existing context and tool results. Do not ask the user to repeat inputs that are already present above.";
7
+ function extractLatestUserInput(request) {
8
+ const typedRequest = request;
9
+ const messages = Array.isArray(typedRequest.messages) ? typedRequest.messages : [];
10
+ for (let index = messages.length - 1; index >= 0; index -= 1) {
11
+ const candidate = messages[index];
12
+ if (candidate?.role !== "user" || typeof candidate.content !== "string") {
13
+ continue;
14
+ }
15
+ const normalized = candidate.content.trim();
16
+ if (normalized.length > 0) {
17
+ return normalized;
18
+ }
19
+ }
20
+ return undefined;
21
+ }
6
22
  export async function runLocalToolInvocationLoop({ binding, request, primaryTools, toolNameMapping, executableTools, builtinExecutableTools, callRuntimeWithToolParseRecovery, }) {
7
23
  const executedToolResults = [];
8
24
  let activeRequest = request;
@@ -32,9 +48,15 @@ export async function runLocalToolInvocationLoop({ binding, request, primaryTool
32
48
  throw new Error(`Tool-calling loop exceeded the maximum of ${maxToolIterations} iterations`);
33
49
  }
34
50
  const resultMessages = result.messages;
35
- const nextMessages = Array.isArray(resultMessages)
36
- ? [...resultMessages]
37
- : [...currentMessages];
51
+ const nextMessages = [...currentMessages];
52
+ if (Array.isArray(resultMessages) && resultMessages.length > 0) {
53
+ nextMessages.push(...resultMessages);
54
+ }
55
+ const latestUserInput = extractLatestUserInput(activeRequest);
56
+ nextMessages.push({
57
+ role: "system",
58
+ content: TOOL_FOLLOW_UP_INSTRUCTION,
59
+ });
38
60
  for (let toolIndex = 0; toolIndex < toolCalls.length; toolIndex += 1) {
39
61
  const toolCall = toolCalls[toolIndex];
40
62
  const resolvedToolName = resolveModelFacingToolName(toolCall.name, toolNameMapping, primaryTools);
@@ -49,7 +71,9 @@ export async function runLocalToolInvocationLoop({ binding, request, primaryTool
49
71
  throw new Error(`Tool ${toolCall.name} is not configured for this agent.`);
50
72
  }
51
73
  const compiledTool = toolCatalog.get(toolCall.name) ?? toolCatalog.get(resolvedToolName);
52
- const normalizedArgs = normalizeToolArgsForSchema(toolCall.args, activeExecutable.schema, toolCall.rawArgsInput);
74
+ const normalizedArgs = normalizeToolArgsForSchema(toolCall.args, activeExecutable.schema, toolCall.rawArgsInput, {
75
+ latestUserInput,
76
+ });
53
77
  const toolResult = await activeExecutable.invoke(normalizedArgs);
54
78
  const memoryCandidates = compiledTool ? extractMemoryCandidatesFromToolOutput(compiledTool, toolResult) : [];
55
79
  executedToolResults.push({
@@ -1,5 +1,5 @@
1
1
  import { HumanMessage } from "@langchain/core/messages";
2
- import { DEFAULT_SUBAGENT_PROMPT, createMemoryMiddleware, createPatchToolCallsMiddleware, createSkillsMiddleware, createSubAgentMiddleware, createSummarizationMiddleware, StateBackend, } from "deepagents";
2
+ import { createMemoryMiddleware, createPatchToolCallsMiddleware, createSkillsMiddleware, createSubAgentMiddleware, createSummarizationMiddleware, StateBackend, } from "deepagents";
3
3
  import { createAgent, humanInTheLoopMiddleware } from "langchain";
4
4
  import { createBuiltinMiddlewareTools } from "./tool/builtin-middleware-tools.js";
5
5
  import { compileInterruptOn } from "./tool/interrupt-policy.js";
@@ -9,6 +9,7 @@ import { resolveDeclaredMiddleware } from "./tool/declared-middleware.js";
9
9
  import { UPSTREAM_SESSION_CONFIG_KEY } from "./upstream-configurable-keys.js";
10
10
  import { bindingHasLangChainSubagentSupport, bindingHasMiddlewareKind, getBindingExecutionKind, getBindingGeneralPurposeAgent, getBindingInterruptCompatibilityRules, getBindingMiddlewareConfigs, getBindingMemorySources, getBindingPrimaryModel, getBindingPrimaryTools, getBindingSkills, getBindingSubagents, getBindingTaskDescription, isDeepAgentBinding, isLangChainBinding, } from "../support/compiled-binding.js";
11
11
  import { applyDeepAgentDelegationPromptCompatibility, materializeDeepAgentSkillSourcePaths } from "./compat/deepagent-compat.js";
12
+ import { DEFAULT_SUBAGENT_PROMPT } from "../support/runtime-prompts.js";
12
13
  export function buildBuiltinTaskSubagentMiddleware(input) {
13
14
  const { selectedSubagent, builtinBackend, summarizationModel } = input;
14
15
  const defaultSubagentMiddleware = [
@@ -180,6 +181,7 @@ export async function resolveBuiltinMiddlewareTools(input) {
180
181
  const backend = input.resolveBuiltinMiddlewareBackend(input.binding, input.options);
181
182
  return createBuiltinMiddlewareTools(backend, {
182
183
  includeTaskTool: isDeepAgentBinding(input.binding),
184
+ workspaceRoot: input.binding.harnessRuntime.workspaceRoot,
183
185
  invokeTaskTool: isDeepAgentBinding(input.binding)
184
186
  ? async (toolInput) => input.invokeBuiltinTaskTool(input.binding, toolInput, input.options)
185
187
  : undefined,
@@ -1,5 +1,8 @@
1
1
  import type { CompiledAgentBinding, MessageContent, TranscriptMessage } from "../../../contracts/types.js";
2
- export declare function buildAgentMessages(history: TranscriptMessage[], input: MessageContent): Array<{
2
+ export declare function buildAgentMessages(history: TranscriptMessage[], input: MessageContent, options?: {
3
+ suppressExplicitResourceTurns?: boolean;
4
+ suppressAssistantTurns?: boolean;
5
+ }): Array<{
3
6
  role: string;
4
7
  content: MessageContent;
5
8
  }>;
@@ -1,12 +1,127 @@
1
1
  import { getBindingSkills } from "../../support/compiled-binding.js";
2
2
  import { extractMessageText, normalizeMessageContent } from "../../../utils/message-content.js";
3
3
  import { readSkillMetadata } from "../../support/skill-metadata.js";
4
- export function buildAgentMessages(history, input) {
4
+ import { summarizeAssistantText } from "../direct-builtin-utility.js";
5
+ import { renderDurableMemoryContextPrompt, renderSlashCommandSkillInstruction } from "../../support/runtime-prompts.js";
6
+ import { hasExplicitResourceReference, } from "../../harness/system/runtime-memory-policy.js";
7
+ const MAX_HISTORY_ASSISTANT_CHARS = 1600;
8
+ const MAX_HISTORY_ASSISTANT_LINES = 40;
9
+ function compactHistoryText(text) {
10
+ const normalized = text.trim();
11
+ if (!normalized) {
12
+ return normalized;
13
+ }
14
+ const summarized = summarizeAssistantText(normalized);
15
+ if (summarized !== normalized) {
16
+ return summarized;
17
+ }
18
+ const lines = normalized.split(/\r?\n/);
19
+ if (normalized.length <= MAX_HISTORY_ASSISTANT_CHARS && lines.length <= MAX_HISTORY_ASSISTANT_LINES) {
20
+ return normalized;
21
+ }
22
+ const head = normalized.slice(0, 1200).trimEnd();
23
+ const tail = normalized.slice(-240).trimStart();
24
+ const omittedChars = Math.max(normalized.length - head.length - tail.length, 0);
25
+ return `${head}\n... [${omittedChars} characters omitted from prior assistant output] ...\n${tail}`;
26
+ }
27
+ function compactHistoryContent(role, content) {
28
+ const normalized = normalizeMessageContent(content);
29
+ if (role !== "assistant" || typeof normalized !== "string") {
30
+ return normalized;
31
+ }
32
+ return compactHistoryText(normalized);
33
+ }
34
+ function renderHistoryTurn(turn) {
35
+ const dropsAssistant = turn.some((item) => {
36
+ if (item.role !== "assistant" || typeof item.content !== "string") {
37
+ return false;
38
+ }
39
+ const compacted = compactHistoryContent(item.role, item.content);
40
+ return typeof compacted === "string" && compacted !== normalizeMessageContent(item.content);
41
+ });
42
+ if (dropsAssistant) {
43
+ return [];
44
+ }
45
+ return turn.flatMap((item) => {
46
+ const content = compactHistoryContent(item.role, item.content);
47
+ if (item.role === "assistant" && typeof item.content === "string" && typeof content === "string" && content !== normalizeMessageContent(item.content)) {
48
+ return [];
49
+ }
50
+ return [{ role: item.role, content }];
51
+ });
52
+ }
53
+ function dropAssistantMessages(turn) {
54
+ return turn.filter((item) => item.role !== "assistant");
55
+ }
56
+ function inferMessageLanguage(text) {
57
+ const normalized = text
58
+ .replace(/https?:\/\/\S+/giu, " ")
59
+ .trim();
60
+ if (!normalized) {
61
+ return undefined;
62
+ }
63
+ if (/[\p{Script=Han}]/u.test(normalized)) {
64
+ return "zh";
65
+ }
66
+ if (/[A-Za-z]/u.test(normalized)) {
67
+ return "en";
68
+ }
69
+ return undefined;
70
+ }
71
+ function selectRelevantHistoryTurns(groupedHistory, inputText, options = {}) {
72
+ if (groupedHistory.length === 0) {
73
+ return [];
74
+ }
75
+ if (hasExplicitResourceReference(inputText)) {
76
+ return [];
77
+ }
78
+ return groupedHistory
79
+ .slice(-4)
80
+ .filter((turn) => !options.suppressExplicitResourceTurns || !turn.some((item) => item.role === "user" && hasExplicitResourceReference(extractMessageText(item.content))))
81
+ .map((turn) => options.suppressAssistantTurns ? dropAssistantMessages(turn) : turn)
82
+ .filter((turn) => turn.length > 0)
83
+ .slice(-4);
84
+ }
85
+ export function buildAgentMessages(history, input, options = {}) {
86
+ const inputText = extractMessageText(input).trim();
87
+ const groupedHistory = history.reduce((groups, item) => {
88
+ const current = groups.at(-1);
89
+ if (current && current[0]?.requestId === item.requestId) {
90
+ current.push(item);
91
+ return groups;
92
+ }
93
+ groups.push([item]);
94
+ return groups;
95
+ }, []);
96
+ const selectedTurns = selectRelevantHistoryTurns(groupedHistory, inputText, options);
5
97
  return [
6
- ...history.map((item) => ({ role: item.role, content: normalizeMessageContent(item.content) })),
98
+ ...selectedTurns.flatMap((turn) => renderHistoryTurn(turn)),
7
99
  { role: "user", content: normalizeMessageContent(input) },
8
100
  ];
9
101
  }
102
+ function resolveConversationLanguage(history, inputText) {
103
+ const direct = inferMessageLanguage(inputText);
104
+ if (direct) {
105
+ return direct;
106
+ }
107
+ for (let index = history.length - 1; index >= 0; index -= 1) {
108
+ const message = history[index];
109
+ if (message?.role !== "user") {
110
+ continue;
111
+ }
112
+ const detected = inferMessageLanguage(extractMessageText(message.content));
113
+ if (detected) {
114
+ return detected;
115
+ }
116
+ }
117
+ return undefined;
118
+ }
119
+ function buildContextualFollowUpInstruction(inputText, hasDurableMemory) {
120
+ if (!hasDurableMemory || hasExplicitResourceReference(inputText)) {
121
+ return undefined;
122
+ }
123
+ return "Answer the user's current follow-up directly from the recalled context. 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.";
124
+ }
10
125
  export function buildSlashCommandSkillInstruction(binding, input) {
11
126
  const inputText = extractMessageText(input).trim();
12
127
  const match = inputText.match(/^\/([a-z0-9]+(?:-[a-z0-9]+)*)(?:\s+([\s\S]*))?$/i);
@@ -25,31 +140,38 @@ export function buildSlashCommandSkillInstruction(binding, input) {
25
140
  const dryRunHint = /\s--dry-run(?:\s|$)/.test(` ${argumentText} `)
26
141
  ? "This invocation includes `--dry-run`. Perform the real fetch or inspection steps needed for dry-run output. Do not return hypothetical or mock results."
27
142
  : undefined;
28
- return [
29
- `This user message is an explicit command-style invocation of the ${skillQualifier} \`${metadata.name}\`.`,
30
- `Read the skill file for \`${metadata.name}\` before taking action, then follow its documented phases and constraints exactly.`,
31
- `You must use the \`${metadata.name}\` skill for this request and follow its documented workflow.`,
32
- `Treat everything after \`/${metadata.name}\` as the skill argument string: ${argumentText ? JSON.stringify(argumentText) : '""'}.`,
33
- "Do not answer with a generic explanation of what the skill would do. Execute the skill workflow using the available tools unless the skill instructions explicitly require confirmation before acting.",
143
+ return renderSlashCommandSkillInstruction({
144
+ skillQualifier,
145
+ skillName: metadata.name,
146
+ argumentText,
34
147
  dryRunHint,
35
- ].filter((line) => typeof line === "string" && line.length > 0).join("\n");
148
+ });
36
149
  }
37
150
  export function buildInvocationRequest(binding, history, input, options = {}) {
151
+ const inputText = extractMessageText(input).trim();
38
152
  const userInvocableInstruction = buildSlashCommandSkillInstruction(binding, input);
39
- const messages = buildAgentMessages(history, input);
40
153
  const memoryInstruction = typeof options.memoryContext === "string" && options.memoryContext.trim().length > 0
41
- ? [
42
- "Relevant durable memory was retrieved for this run.",
43
- "Use it when it is still applicable, but prefer fresher direct evidence if there is any conflict.",
44
- "",
45
- options.memoryContext.trim(),
46
- ].join("\n")
154
+ ? renderDurableMemoryContextPrompt(options.memoryContext)
155
+ : undefined;
156
+ const messages = buildAgentMessages(history, input, {
157
+ suppressExplicitResourceTurns: Boolean(memoryInstruction) && !hasExplicitResourceReference(inputText),
158
+ suppressAssistantTurns: Boolean(memoryInstruction) && !hasExplicitResourceReference(inputText),
159
+ });
160
+ const contextualFollowUpInstruction = buildContextualFollowUpInstruction(inputText, Boolean(memoryInstruction));
161
+ const conversationLanguage = resolveConversationLanguage(history, inputText);
162
+ const languageInstruction = !inferMessageLanguage(inputText) && conversationLanguage
163
+ ? {
164
+ zh: "Reply in Chinese for this turn.",
165
+ en: "Reply in English for this turn.",
166
+ }[conversationLanguage]
47
167
  : undefined;
48
168
  return {
49
169
  ...(options.state ?? {}),
50
170
  ...(options.files ? { files: options.files } : {}),
51
171
  messages: [
52
172
  ...(memoryInstruction ? [{ role: "system", content: memoryInstruction }] : []),
173
+ ...(contextualFollowUpInstruction ? [{ role: "system", content: contextualFollowUpInstruction }] : []),
174
+ ...(languageInstruction ? [{ role: "system", content: languageInstruction }] : []),
53
175
  ...(userInvocableInstruction ? [{ role: "system", content: userInvocableInstruction }] : []),
54
176
  ...messages,
55
177
  ],
@@ -1,4 +1,5 @@
1
1
  import { buildAgentMessages, buildSlashCommandSkillInstruction } from "./invocation-request.js";
2
+ import { renderDurableMemoryContextPrompt } from "../../support/runtime-prompts.js";
2
3
  export function buildStateSnapshot(result) {
3
4
  const snapshot = { ...result };
4
5
  delete snapshot.messages;
@@ -15,12 +16,7 @@ export function buildRawModelMessages(binding, systemPrompt, history, input, mem
15
16
  if (memoryContext?.trim()) {
16
17
  messages.push({
17
18
  role: "system",
18
- content: [
19
- "Relevant durable memory was retrieved for this run.",
20
- "Use it when it still applies, but prefer fresher direct evidence if there is any conflict.",
21
- "",
22
- memoryContext.trim(),
23
- ].join("\n"),
19
+ content: renderDurableMemoryContextPrompt(memoryContext),
24
20
  });
25
21
  }
26
22
  const userInvocableInstruction = buildSlashCommandSkillInstruction(binding, input);
@@ -1,5 +1,6 @@
1
1
  import { ChatAnthropic } from "@langchain/anthropic";
2
2
  import { ChatGoogle } from "@langchain/google";
3
+ import { ToolMessage } from "@langchain/core/messages";
3
4
  import { ChatOllama } from "@langchain/ollama";
4
5
  import { ChatOpenAI } from "@langchain/openai";
5
6
  import { AIMessage } from "langchain";
@@ -151,6 +152,103 @@ function formatStructuredMessage(value) {
151
152
  }
152
153
  return content ? `${role}:\n${content}` : "";
153
154
  }
155
+ function readToolMessageMetadata(value) {
156
+ if (typeof value !== "object" || value === null) {
157
+ return {};
158
+ }
159
+ const typed = value;
160
+ const kwargs = typeof typed.kwargs === "object" && typed.kwargs !== null ? typed.kwargs : undefined;
161
+ const lcKwargs = typeof typed.lc_kwargs === "object" && typed.lc_kwargs !== null ? typed.lc_kwargs : undefined;
162
+ const name = typeof typed.name === "string"
163
+ ? typed.name
164
+ : typeof kwargs?.name === "string"
165
+ ? kwargs.name
166
+ : typeof lcKwargs?.name === "string"
167
+ ? lcKwargs.name
168
+ : undefined;
169
+ const toolCallId = typeof typed.tool_call_id === "string"
170
+ ? typed.tool_call_id
171
+ : typeof kwargs?.tool_call_id === "string"
172
+ ? kwargs.tool_call_id
173
+ : typeof lcKwargs?.tool_call_id === "string"
174
+ ? lcKwargs.tool_call_id
175
+ : undefined;
176
+ return { name, toolCallId };
177
+ }
178
+ function normalizeReadFileToolContent(name, content) {
179
+ if (name !== "read_file") {
180
+ return content;
181
+ }
182
+ const lines = content.split("\n");
183
+ const nonEmptyLines = lines.filter((line) => line.trim().length > 0);
184
+ if (nonEmptyLines.length === 0 || !nonEmptyLines.every((line) => /^\d+\t/.test(line))) {
185
+ return content;
186
+ }
187
+ return lines.map((line) => line.replace(/^\d+\t/, "")).join("\n");
188
+ }
189
+ function normalizeProviderFacingToolMessage(message) {
190
+ if (mapMessageRole(message) !== "TOOL") {
191
+ return message;
192
+ }
193
+ if (typeof message !== "object" || message === null) {
194
+ return message;
195
+ }
196
+ const { name, toolCallId } = readToolMessageMetadata(message);
197
+ const normalizedContent = normalizeReadFileToolContent(name, readPromptContent(message));
198
+ const messageType = readMessageType(message);
199
+ if (messageType === "tool" && toolCallId) {
200
+ return new ToolMessage({
201
+ ...(name ? { name } : {}),
202
+ tool_call_id: toolCallId,
203
+ content: normalizedContent,
204
+ });
205
+ }
206
+ return {
207
+ ...message,
208
+ content: normalizedContent,
209
+ };
210
+ }
211
+ function normalizeProviderFacingInput(input) {
212
+ if (Array.isArray(input)) {
213
+ return input.map((message) => normalizeProviderFacingToolMessage(message));
214
+ }
215
+ if (typeof input === "object" && input !== null && Array.isArray(input.messages)) {
216
+ return {
217
+ ...input,
218
+ messages: normalizeProviderFacingInput(input.messages),
219
+ };
220
+ }
221
+ return input;
222
+ }
223
+ function createProviderToolMessageCompatModel(model) {
224
+ return new Proxy(model, {
225
+ has(target, prop) {
226
+ if (prop === "bindTools" || prop === "invoke" || prop === "stream" || prop === "withConfig") {
227
+ return true;
228
+ }
229
+ return prop in target;
230
+ },
231
+ get(target, prop, receiver) {
232
+ if (prop === "bindTools") {
233
+ return (tools) => {
234
+ const bound = target.bindTools.call(target, tools);
235
+ return createProviderToolMessageCompatModel(bound);
236
+ };
237
+ }
238
+ if (prop === "invoke") {
239
+ return (input, config) => target.invoke.call(target, normalizeProviderFacingInput(input), config);
240
+ }
241
+ if (prop === "stream") {
242
+ return (input, config) => target.stream.call(target, normalizeProviderFacingInput(input), config);
243
+ }
244
+ if (prop === "withConfig" && typeof target.withConfig === "function") {
245
+ return (config) => createProviderToolMessageCompatModel(target.withConfig.call(target, config));
246
+ }
247
+ const member = Reflect.get(target, prop, receiver);
248
+ return typeof member === "function" ? member.bind(target) : member;
249
+ },
250
+ });
251
+ }
154
252
  function stringifyNodeLlamaCppInput(input) {
155
253
  if (typeof input === "string") {
156
254
  return input;
@@ -325,19 +423,19 @@ export async function createResolvedModel(model, modelResolver) {
325
423
  return modelResolver(model.id);
326
424
  }
327
425
  if (model.provider === "ollama") {
328
- return new ChatOllama({ model: model.model, ...model.init });
426
+ return createProviderToolMessageCompatModel(new ChatOllama({ model: model.model, ...model.init }));
329
427
  }
330
428
  if (model.provider === "openai-compatible") {
331
- return new ChatOpenAI({ model: model.model, ...normalizeOpenAICompatibleInit(model.init) });
429
+ return createProviderToolMessageCompatModel(new ChatOpenAI({ model: model.model, ...normalizeOpenAICompatibleInit(model.init) }));
332
430
  }
333
431
  if (model.provider === "openai") {
334
- return new ChatOpenAI({ model: model.model, ...model.init });
432
+ return createProviderToolMessageCompatModel(new ChatOpenAI({ model: model.model, ...model.init }));
335
433
  }
336
434
  if (model.provider === "anthropic") {
337
- return new ChatAnthropic({ model: model.model, ...model.init });
435
+ return createProviderToolMessageCompatModel(new ChatAnthropic({ model: model.model, ...model.init }));
338
436
  }
339
437
  if (model.provider === "google" || model.provider === "google-genai" || model.provider === "gemini") {
340
- return new ChatGoogle({ model: model.model, ...model.init });
438
+ return createProviderToolMessageCompatModel(new ChatGoogle({ model: model.model, ...model.init }));
341
439
  }
342
440
  if (model.provider === "node-llama-cpp" || model.provider === "llama-cpp") {
343
441
  return createNodeLlamaCppModel(model);
@@ -1,4 +1,4 @@
1
- import { getBindingModelInit } from "../support/compiled-binding.js";
1
+ import { getBindingModelInit, getBindingPrimaryModel } from "../support/compiled-binding.js";
2
2
  export function resolveTimeoutMs(value) {
3
3
  return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : undefined;
4
4
  }
@@ -20,12 +20,23 @@ export function resolveStreamIdleTimeout(binding) {
20
20
  if (configuredIdleTimeout) {
21
21
  return configuredIdleTimeout;
22
22
  }
23
+ const primaryModel = getBindingPrimaryModel(binding);
24
+ if (primaryModel?.provider === "ollama") {
25
+ return 180_000;
26
+ }
23
27
  const invokeTimeout = resolveBindingTimeout(binding);
24
28
  if (invokeTimeout) {
25
29
  return Math.min(invokeTimeout, 60_000);
26
30
  }
27
31
  return 60_000;
28
32
  }
33
+ const BUILTIN_RETRYABLE_PROVIDER_MESSAGES = [
34
+ "unexpected eof",
35
+ ];
36
+ function isEmptyFinalAiMessageError(error) {
37
+ const message = error instanceof Error ? error.message : String(error);
38
+ return message.toLowerCase().startsWith("empty_final_ai_message:");
39
+ }
29
40
  export function resolveProviderRetryPolicy(binding) {
30
41
  const resilience = typeof binding.harnessRuntime.resilience === "object" && binding.harnessRuntime.resilience
31
42
  ? binding.harnessRuntime.resilience
@@ -53,8 +64,12 @@ export function resolveProviderRetryPolicy(binding) {
53
64
  };
54
65
  }
55
66
  export function isRetryableProviderError(binding, error) {
67
+ if (isEmptyFinalAiMessageError(error)) {
68
+ return true;
69
+ }
56
70
  const message = error instanceof Error ? error.message : String(error);
57
71
  const normalized = message.toLowerCase();
58
72
  const { retryableMessages } = resolveProviderRetryPolicy(binding);
59
- return retryableMessages.some((candidate) => normalized.includes(candidate.toLowerCase()));
73
+ return [...BUILTIN_RETRYABLE_PROVIDER_MESSAGES, ...retryableMessages]
74
+ .some((candidate) => normalized.includes(candidate.toLowerCase()));
60
75
  }
@@ -1,12 +1,16 @@
1
+ import type { RequestPlanItem, RequestPlanState, RequestPlanSummary } from "../../contracts/types.js";
1
2
  export type BuiltinTodoSnapshot = {
2
- total: number;
3
- pending: number;
4
- completed: number;
5
- items: Array<{
6
- content: string;
7
- status: string;
8
- }>;
3
+ items: RequestPlanItem[];
4
+ summary: RequestPlanSummary;
9
5
  };
10
6
  export declare function truncateLines(lines: string[], maxChars?: number): string;
11
7
  export declare function summarizeBuiltinWriteTodosArgs(args: Record<string, unknown>): BuiltinTodoSnapshot;
8
+ export declare function isLowSignalTodoContent(content: string): boolean;
12
9
  export declare function formatBuiltinTodoSnapshot(snapshot: BuiltinTodoSnapshot): string;
10
+ export declare function buildRequestPlanState(input: {
11
+ sessionId: string;
12
+ requestId: string;
13
+ updatedAt: string;
14
+ version?: number;
15
+ snapshot: BuiltinTodoSnapshot;
16
+ }): RequestPlanState;
@@ -18,22 +18,56 @@ export function summarizeBuiltinWriteTodosArgs(args) {
18
18
  ? todo.description.trim()
19
19
  : "";
20
20
  const status = typeof todo.status === "string" && todo.status.trim().length > 0 ? todo.status.trim() : "pending";
21
- return content ? [{ content, status }] : [];
21
+ const metadata = isRecord(todo.metadata) ? todo.metadata : undefined;
22
+ return content ? [{
23
+ ...(typeof todo.id === "string" && todo.id.trim().length > 0 ? { id: todo.id.trim() } : {}),
24
+ content,
25
+ status,
26
+ ...(typeof todo.ownerAgentId === "string" && todo.ownerAgentId.trim().length > 0 ? { ownerAgentId: todo.ownerAgentId.trim() } : {}),
27
+ ...(typeof todo.startedAt === "string" && todo.startedAt.trim().length > 0 ? { startedAt: todo.startedAt.trim() } : {}),
28
+ ...(typeof todo.endedAt === "string" && todo.endedAt.trim().length > 0 ? { endedAt: todo.endedAt.trim() } : {}),
29
+ ...(todo.result !== undefined ? { result: todo.result } : {}),
30
+ ...(metadata ? { metadata } : {}),
31
+ }] : [];
22
32
  });
23
- return {
33
+ const summary = {
24
34
  total: items.length,
25
- pending: items.filter((item) => item.status !== "completed").length,
35
+ pending: items.filter((item) => item.status === "pending").length,
36
+ inProgress: items.filter((item) => item.status === "in_progress").length,
26
37
  completed: items.filter((item) => item.status === "completed").length,
38
+ failed: items.filter((item) => item.status === "failed").length,
39
+ cancelled: items.filter((item) => item.status === "cancelled").length,
40
+ blocked: items.filter((item) => item.status === "blocked").length,
41
+ };
42
+ return {
27
43
  items,
44
+ summary,
28
45
  };
29
46
  }
47
+ export function isLowSignalTodoContent(content) {
48
+ const normalized = content.trim().toLowerCase();
49
+ if (!normalized) {
50
+ return true;
51
+ }
52
+ return /^\d+$/.test(normalized) || /^step\s*\d+$/.test(normalized) || /^todo\s*\d+$/.test(normalized);
53
+ }
30
54
  export function formatBuiltinTodoSnapshot(snapshot) {
31
- if (snapshot.total === 0) {
55
+ if (snapshot.summary.total === 0) {
32
56
  return "No todos tracked.";
33
57
  }
34
58
  const lines = [
35
- `Tracked ${snapshot.total} todo item(s): ${snapshot.pending} pending, ${snapshot.completed} completed.`,
59
+ `Tracked ${snapshot.summary.total} todo item(s): ${snapshot.summary.pending + snapshot.summary.inProgress + snapshot.summary.blocked} pending, ${snapshot.summary.completed} completed.`,
36
60
  ...snapshot.items.map((item, index) => `${index + 1}. [${item.status}] ${item.content}`),
37
61
  ];
38
62
  return truncateLines(lines);
39
63
  }
64
+ export function buildRequestPlanState(input) {
65
+ return {
66
+ sessionId: input.sessionId,
67
+ requestId: input.requestId,
68
+ version: input.version ?? 1,
69
+ updatedAt: input.updatedAt,
70
+ items: input.snapshot.items,
71
+ summary: input.snapshot.summary,
72
+ };
73
+ }
@@ -1,3 +1,4 @@
1
+ import type { RuntimeScheduleManageInput, RuntimeScheduleManageResult } from "../../../contracts/types.js";
1
2
  export type BuiltinMiddlewareBackend = {
2
3
  lsInfo?: (path: string) => Promise<Array<{
3
4
  path: string;
@@ -179,13 +180,74 @@ export type BuiltinMiddlewareBackend = {
179
180
  id?: string;
180
181
  error?: string;
181
182
  };
183
+ manageSchedule?: (input: RuntimeScheduleManageInput) => Promise<RuntimeScheduleManageResult> | RuntimeScheduleManageResult;
182
184
  };
183
185
  export type BuiltinExecutableTool = {
184
186
  name: string;
187
+ description?: string;
185
188
  schema: unknown;
186
- invoke: (input: unknown) => Promise<unknown>;
189
+ invoke: (input: unknown, config?: Record<string, unknown>) => Promise<unknown>;
187
190
  };
191
+ export declare const BUILTIN_MIDDLEWARE_TOOL_DESCRIPTORS: readonly [{
192
+ readonly name: "write_todos";
193
+ readonly description: "Create and update the runtime todo board for multi-step work.";
194
+ }, {
195
+ readonly name: "read_todos";
196
+ readonly description: "Read the current runtime todo board.";
197
+ }, {
198
+ readonly name: "ls";
199
+ readonly description: "List files in a directory.";
200
+ }, {
201
+ readonly name: "list_files";
202
+ readonly description: "List files in a directory.";
203
+ }, {
204
+ readonly name: "read_file";
205
+ readonly description: "Read a file from the workspace filesystem.";
206
+ }, {
207
+ readonly name: "write_file";
208
+ readonly description: "Write a file in the workspace filesystem.";
209
+ }, {
210
+ readonly name: "edit_file";
211
+ readonly description: "Replace exact text inside a workspace file.";
212
+ }, {
213
+ readonly name: "glob";
214
+ readonly description: "Find files matching a glob pattern.";
215
+ }, {
216
+ readonly name: "grep";
217
+ readonly description: "Search for text across workspace files.";
218
+ }, {
219
+ readonly name: "search_files";
220
+ readonly description: "Search for text across workspace files.";
221
+ }, {
222
+ readonly name: "execute";
223
+ readonly description: "Run a shell command in the workspace sandbox.";
224
+ }, {
225
+ readonly name: "run_command";
226
+ readonly description: "Run a shell command in the workspace sandbox.";
227
+ }, {
228
+ readonly name: "fetch_url";
229
+ readonly description: "Fetch a URL and return the response body.";
230
+ }, {
231
+ readonly name: "http_request";
232
+ readonly description: "Send a structured HTTP request.";
233
+ }, {
234
+ readonly name: "send_message";
235
+ readonly description: "Send a message through the configured backend.";
236
+ }, {
237
+ readonly name: "request_approval";
238
+ readonly description: "Request an approval decision from the runtime.";
239
+ }, {
240
+ readonly name: "schedule_task";
241
+ readonly description: "Create, inspect, update, list, and delete system-level scheduled tasks.";
242
+ }, {
243
+ readonly name: "task";
244
+ readonly description: "Delegate a bounded task to a subagent.";
245
+ }, {
246
+ readonly name: "delegate_task";
247
+ readonly description: "Delegate a bounded task to a subagent.";
248
+ }];
188
249
  export declare function createBuiltinMiddlewareTools(backend: BuiltinMiddlewareBackend, options: {
189
250
  includeTaskTool: boolean;
190
251
  invokeTaskTool?: (input: unknown) => Promise<unknown>;
252
+ workspaceRoot?: string;
191
253
  }): Promise<Map<string, BuiltinExecutableTool>>;