@botbotgo/agent-harness 0.0.102 → 0.0.104

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.
@@ -1,32 +1,28 @@
1
1
  import path from "node:path";
2
2
  import { Command, MemorySaver } from "@langchain/langgraph";
3
- import { HumanMessage } from "@langchain/core/messages";
4
- import { DEFAULT_SUBAGENT_PROMPT, createDeepAgent, createMemoryMiddleware, createPatchToolCallsMiddleware, createSkillsMiddleware, createSummarizationMiddleware, createSubAgentMiddleware, FilesystemBackend, StateBackend, } from "deepagents";
5
- import { createAgent, humanInTheLoopMiddleware } from "langchain";
6
- import { extractToolFallbackContext, extractVisibleOutput, isToolCallParseFailure, STRICT_TOOL_JSON_INSTRUCTION, wrapResolvedModel, } from "./parsing/output-parsing.js";
7
- import { readStreamDelta, } from "./parsing/stream-event-parsing.js";
8
- import { wrapToolForExecution } from "./adapter/tool/tool-hitl.js";
9
- import { resolveDeclaredMiddleware } from "./adapter/tool/declared-middleware.js";
3
+ import { createDeepAgent, FilesystemBackend, } from "deepagents";
4
+ import { createAgent } from "langchain";
5
+ import { wrapResolvedModel, } from "./parsing/output-parsing.js";
10
6
  import { applyDeepAgentDelegationPromptCompatibility, materializeDeepAgentSkillSourcePaths, } from "./adapter/compat/deepagent-compat.js";
11
7
  import { buildToolNameMapping, } from "./adapter/tool/tool-name-mapping.js";
12
- import { createBuiltinMiddlewareTools } from "./adapter/tool/builtin-middleware-tools.js";
13
8
  import { finalizeInvocationResult } from "./adapter/invocation-result.js";
14
9
  import { invokeRuntimeWithLocalTools } from "./adapter/invoke-runtime.js";
15
10
  import { streamRuntimeExecution } from "./adapter/stream-runtime.js";
16
11
  import { buildDeepAgentRunnableConfig } from "./adapter/deepagent-runnable-config.js";
17
12
  import { buildLangChainRunnableConfig } from "./adapter/langchain-runnable-config.js";
18
- import { computeRemainingTimeoutMs, isRetryableProviderError, resolveBindingTimeout, resolveProviderRetryPolicy, resolveStreamIdleTimeout, } from "./adapter/resilience.js";
13
+ import { applyStrictToolJsonInstruction as applyStrictToolJsonInstructionHelper, callRuntimeWithToolParseRecovery as callRuntimeWithToolParseRecoveryHelper, createModelFallbackRunnable as createModelFallbackRunnableHelper, invokeWithProviderRetry as invokeWithProviderRetryHelper, iterateWithTimeout as iterateWithTimeoutHelper, materializeModelStream as materializeModelStreamHelper, RuntimeOperationTimeoutError, withRuntimeTimeout, } from "./adapter/runtime-shell.js";
14
+ import { invokeBuiltinTaskTool as invokeBuiltinTaskToolHelper, resolveAutomaticSummarizationMiddleware as resolveAutomaticSummarizationMiddlewareHelper, resolveBuiltinMiddlewareBackend as resolveBuiltinMiddlewareBackendHelper, resolveBuiltinMiddlewareTools as resolveBuiltinMiddlewareToolsHelper, resolveLangChainAutomaticMiddleware as resolveLangChainAutomaticMiddlewareHelper, resolveMiddleware as resolveMiddlewareHelper, resolveSubagents as resolveSubagentsHelper, } from "./adapter/middleware-assembly.js";
15
+ import { computeRemainingTimeoutMs, resolveBindingTimeout, resolveStreamIdleTimeout, } from "./adapter/resilience.js";
19
16
  import { createResolvedModel } from "./adapter/model/model-providers.js";
20
17
  import { buildInvocationRequest, } from "./adapter/model/invocation-request.js";
21
18
  import { compileInterruptOn } from "./adapter/tool/interrupt-policy.js";
22
- import { asStructuredExecutableTool, hasCallableToolHandler, normalizeResolvedToolSchema, wrapResolvedToolWithModelFacingName, } from "./adapter/tool/resolved-tool.js";
23
- import { instantiateProviderTool } from "./adapter/tool/provider-tool.js";
24
- import { countConfiguredTools, hasConfiguredMiddlewareKind, hasConfiguredSubagentSupport, isObject, isRecord, sleep, } from "./adapter/runtime-adapter-support.js";
19
+ import { countConfiguredTools, } from "./adapter/runtime-adapter-support.js";
20
+ import { buildExecutableToolMap, resolveAdapterTools } from "./adapter/tool-resolution.js";
25
21
  export { applyDeepAgentDelegationPromptCompatibility, materializeDeepAgentSkillSourcePaths, relativizeDeepAgentSkillSourcePaths, shouldRelaxDeepAgentDelegationPrompt, } from "./adapter/compat/deepagent-compat.js";
26
22
  export { buildAuthOmittingFetch, normalizeOpenAICompatibleInit } from "./adapter/compat/openai-compatible.js";
27
23
  export { buildToolNameMapping, createModelFacingToolNameCandidates, createModelFacingToolNameLookupCandidates, resolveModelFacingToolName, sanitizeToolNameForModel, } from "./adapter/tool/tool-name-mapping.js";
28
24
  export { computeRemainingTimeoutMs, isRetryableProviderError, resolveBindingTimeout, resolveProviderRetryPolicy, resolveStreamIdleTimeout, resolveTimeoutMs, } from "./adapter/resilience.js";
29
- import { getBindingAdapterKind, getBindingDeepAgentParams, getBindingInterruptCompatibilityRules, getBindingLangChainParams, getBindingMiddlewareConfigs, getBindingPrimaryModel, getBindingPrimaryTools, getBindingSystemPrompt, isDeepAgentBinding, isLangChainBinding, } from "./support/compiled-binding.js";
25
+ import { getBindingAdapterKind, getBindingDeepAgentParams, getBindingInterruptCompatibilityRules, getBindingLangChainParams, getBindingPrimaryModel, getBindingPrimaryTools, getBindingSystemPrompt, isDeepAgentBinding, isLangChainBinding, } from "./support/compiled-binding.js";
30
26
  const AGENT_INTERRUPT_SENTINEL_PREFIX = "__agent_harness_interrupt__:";
31
27
  const UPSTREAM_BUILTIN_MIDDLEWARE_TOOL_NAMES = Object.freeze([
32
28
  "write_todos",
@@ -39,18 +35,6 @@ const UPSTREAM_BUILTIN_MIDDLEWARE_TOOL_NAMES = Object.freeze([
39
35
  "execute",
40
36
  "task",
41
37
  ]);
42
- class RuntimeOperationTimeoutError extends Error {
43
- operation;
44
- timeoutMs;
45
- stage;
46
- constructor(operation, timeoutMs, stage = operation.includes("stream") ? "stream" : "invoke") {
47
- super(`${operation} timed out after ${timeoutMs}ms`);
48
- this.operation = operation;
49
- this.timeoutMs = timeoutMs;
50
- this.stage = stage;
51
- this.name = "RuntimeOperationTimeoutError";
52
- }
53
- }
54
38
  export class AgentRuntimeAdapter {
55
39
  options;
56
40
  modelCache = new Map();
@@ -67,144 +51,22 @@ export class AgentRuntimeAdapter {
67
51
  });
68
52
  }
69
53
  async invokeWithProviderRetry(binding, operation) {
70
- const retryPolicy = resolveProviderRetryPolicy(binding);
71
- let lastError;
72
- for (let attempt = 1; attempt <= retryPolicy.maxAttempts; attempt += 1) {
73
- try {
74
- return await operation();
75
- }
76
- catch (error) {
77
- lastError = error;
78
- if (attempt >= retryPolicy.maxAttempts || !isRetryableProviderError(binding, error)) {
79
- throw error;
80
- }
81
- if (retryPolicy.backoffMs > 0) {
82
- await sleep(retryPolicy.backoffMs);
83
- }
84
- }
85
- }
86
- throw lastError instanceof Error ? lastError : new Error(String(lastError));
54
+ return invokeWithProviderRetryHelper(binding, operation);
87
55
  }
88
56
  async withTimeout(producer, timeoutMs, operation, stage = operation.includes("stream") ? "stream" : "invoke") {
89
- if (!timeoutMs) {
90
- return Promise.resolve(producer());
91
- }
92
- return new Promise((resolve, reject) => {
93
- const timer = setTimeout(() => reject(new RuntimeOperationTimeoutError(operation, timeoutMs, stage)), timeoutMs);
94
- Promise.resolve(producer()).then((value) => {
95
- clearTimeout(timer);
96
- resolve(value);
97
- }, (error) => {
98
- clearTimeout(timer);
99
- reject(error);
100
- });
101
- });
57
+ return withRuntimeTimeout(producer, timeoutMs, operation, stage);
102
58
  }
103
59
  async *iterateWithTimeout(iterable, timeoutMs, operation, deadlineAt, deadlineTimeoutMs) {
104
- const iterator = iterable[Symbol.asyncIterator]();
105
- try {
106
- for (;;) {
107
- const effectiveTimeoutMs = computeRemainingTimeoutMs(deadlineAt, timeoutMs);
108
- if (effectiveTimeoutMs !== undefined && effectiveTimeoutMs <= 0) {
109
- throw new RuntimeOperationTimeoutError(operation, deadlineTimeoutMs ?? timeoutMs ?? 0, "invoke");
110
- }
111
- let next;
112
- try {
113
- next = await this.withTimeout(() => iterator.next(), effectiveTimeoutMs, operation, "stream");
114
- }
115
- catch (error) {
116
- if (error instanceof RuntimeOperationTimeoutError &&
117
- deadlineAt &&
118
- deadlineTimeoutMs &&
119
- effectiveTimeoutMs !== timeoutMs) {
120
- throw new RuntimeOperationTimeoutError(operation, deadlineTimeoutMs, "invoke");
121
- }
122
- throw error;
123
- }
124
- if (next.done) {
125
- return;
126
- }
127
- yield next.value;
128
- }
129
- }
130
- finally {
131
- if (typeof iterator.return === "function") {
132
- const returnResult = iterator.return();
133
- if (returnResult && typeof returnResult.then === "function") {
134
- void returnResult.catch(() => undefined);
135
- }
136
- }
137
- }
60
+ yield* iterateWithTimeoutHelper(iterable, timeoutMs, operation, deadlineAt, deadlineTimeoutMs);
138
61
  }
139
62
  async materializeModelStream(streamFactory, input, config) {
140
- const stream = await streamFactory(input, config);
141
- let content = "";
142
- for await (const chunk of stream) {
143
- const delta = readStreamDelta(chunk) || extractVisibleOutput(chunk);
144
- if (delta) {
145
- content += delta;
146
- }
147
- }
148
- return { content };
63
+ return materializeModelStreamHelper(streamFactory, input, config);
149
64
  }
150
65
  createModelFallbackRunnable(model) {
151
- return {
152
- invoke: async (input, config) => {
153
- const request = typeof input === "object" && input !== null && "messages" in input
154
- ? input.messages
155
- : input;
156
- if (typeof model.invoke === "function") {
157
- return model.invoke(request, config);
158
- }
159
- if (typeof model.stream === "function") {
160
- return this.materializeModelStream(model.stream.bind(model), request, config);
161
- }
162
- throw new Error("Resolved model must define invoke or stream.");
163
- },
164
- stream: async (input, config) => {
165
- if (typeof model.stream === "function") {
166
- const request = typeof input === "object" && input !== null && "messages" in input
167
- ? input.messages
168
- : input;
169
- return model.stream(request, config);
170
- }
171
- if (typeof model.invoke === "function") {
172
- const request = typeof input === "object" && input !== null && "messages" in input
173
- ? input.messages
174
- : input;
175
- const result = await model.invoke(request, config);
176
- const text = extractVisibleOutput(result);
177
- async function* singleChunk() {
178
- yield { content: text };
179
- }
180
- return singleChunk();
181
- }
182
- throw new Error("Resolved model must define invoke or stream.");
183
- },
184
- };
66
+ return createModelFallbackRunnableHelper(model);
185
67
  }
186
68
  applyStrictToolJsonInstruction(binding) {
187
- if (isLangChainBinding(binding)) {
188
- const params = getBindingLangChainParams(binding);
189
- return {
190
- ...binding,
191
- langchainAgentParams: {
192
- ...params,
193
- systemPrompt: [params.systemPrompt, STRICT_TOOL_JSON_INSTRUCTION].filter(Boolean).join("\n\n"),
194
- },
195
- };
196
- }
197
- if (isDeepAgentBinding(binding)) {
198
- const params = getBindingDeepAgentParams(binding);
199
- return {
200
- ...binding,
201
- deepAgentParams: {
202
- ...params,
203
- systemPrompt: [params.systemPrompt, STRICT_TOOL_JSON_INSTRUCTION].filter(Boolean).join("\n\n"),
204
- },
205
- };
206
- }
207
- return binding;
69
+ return applyStrictToolJsonInstructionHelper(binding);
208
70
  }
209
71
  async resolveModel(model) {
210
72
  const cacheKey = this.getModelCacheKey(model);
@@ -225,20 +87,10 @@ export class AgentRuntimeAdapter {
225
87
  }
226
88
  }
227
89
  resolveTools(tools, binding) {
228
- const resolved = this.options.toolResolver ? this.options.toolResolver(tools.map((tool) => tool.id), binding) : [];
229
- const toolNameMapping = buildToolNameMapping(tools);
230
- return tools.flatMap((compiledTool, index) => {
231
- const resolvedTool = resolved[index] ?? (compiledTool.type === "provider" ? instantiateProviderTool(compiledTool) : undefined);
232
- if (resolvedTool === undefined) {
233
- return [];
234
- }
235
- const wrappedTool = wrapToolForExecution(resolvedTool, compiledTool, binding);
236
- const modelFacingName = toolNameMapping.originalToModelFacing.get(compiledTool.name) ?? compiledTool.name;
237
- const structuredTool = asStructuredExecutableTool(wrappedTool, modelFacingName, compiledTool.description);
238
- if (structuredTool !== wrappedTool) {
239
- return structuredTool;
240
- }
241
- return modelFacingName === compiledTool.name ? wrappedTool : wrapResolvedToolWithModelFacingName(wrappedTool, modelFacingName);
90
+ return resolveAdapterTools({
91
+ tools,
92
+ binding,
93
+ resolveToolValues: this.options.toolResolver,
242
94
  });
243
95
  }
244
96
  resolveFilesystemBackend(binding) {
@@ -259,24 +111,12 @@ export class AgentRuntimeAdapter {
259
111
  });
260
112
  }
261
113
  resolveBuiltinMiddlewareBackend(binding, options = {}) {
262
- const runtimeState = {
263
- ...(options.state ?? {}),
264
- ...(isRecord(options.files) ? { files: options.files } : {}),
265
- };
266
- const runtimeLike = {
267
- state: runtimeState,
268
- store: this.options.storeResolver?.(binding),
269
- };
270
- const configuredBackend = isDeepAgentBinding(binding)
271
- ? this.options.backendResolver?.(binding)
272
- : this.resolveFilesystemBackend(binding);
273
- if (typeof configuredBackend === "function") {
274
- return configuredBackend(runtimeLike);
275
- }
276
- if (configuredBackend) {
277
- return configuredBackend;
278
- }
279
- return new StateBackend(runtimeLike);
114
+ return resolveBuiltinMiddlewareBackendHelper({
115
+ binding,
116
+ runtimeAdapterOptions: this.options,
117
+ resolveFilesystemBackend: (currentBinding) => this.resolveFilesystemBackend(currentBinding),
118
+ options,
119
+ });
280
120
  }
281
121
  createDeclaredMiddlewareResolverOptions(binding) {
282
122
  return {
@@ -294,149 +134,57 @@ export class AgentRuntimeAdapter {
294
134
  };
295
135
  }
296
136
  async invokeBuiltinTaskTool(binding, input, options = {}) {
297
- if (!isDeepAgentBinding(binding)) {
298
- throw new Error("The built-in task tool is only available for deepagent bindings.");
299
- }
300
- const params = getBindingDeepAgentParams(binding);
301
- if (!params) {
302
- throw new Error(`Agent ${binding.agent.id} has no deepagent params`);
303
- }
304
- const typedInput = isObject(input) ? input : {};
305
- const description = typeof typedInput.description === "string" ? typedInput.description : "";
306
- const subagentType = typeof typedInput.subagent_type === "string" ? typedInput.subagent_type : "";
307
- const builtinBackend = this.resolveBuiltinMiddlewareBackend(binding, options);
308
- const resolvedSubagents = await this.resolveSubagents(params.subagents, binding);
309
- const selectedSubagent = resolvedSubagents.find((subagent) => subagent.name === subagentType);
310
- if (!selectedSubagent) {
311
- const allowed = [
312
- ...resolvedSubagents.map((subagent) => subagent.name),
313
- ...(params.generalPurposeAgent ? ["general-purpose"] : []),
314
- ];
315
- throw new Error(`Error: invoked agent of type ${subagentType}, the only allowed types are ${allowed.map((name) => `\`${name}\``).join(", ")}`);
316
- }
317
- const summarizationModel = selectedSubagent.model
318
- ? await this.resolveModel(selectedSubagent.model)
319
- : await this.resolveModel(params.model);
320
- const middleware = [
321
- ...(selectedSubagent.skills?.length
322
- ? [createSkillsMiddleware({ backend: builtinBackend, sources: selectedSubagent.skills })]
323
- : []),
324
- ...(selectedSubagent.memory?.length
325
- ? [createMemoryMiddleware({ backend: builtinBackend, sources: selectedSubagent.memory })]
326
- : []),
327
- ...(selectedSubagent.middleware ??
328
- [
329
- createPatchToolCallsMiddleware(),
330
- createSummarizationMiddleware({
331
- model: summarizationModel,
332
- backend: builtinBackend,
333
- }),
334
- ]),
335
- ...(selectedSubagent.interruptOn
336
- ? [humanInTheLoopMiddleware({
337
- interruptOn: compileInterruptOn(selectedSubagent.tools ?? [], selectedSubagent.interruptOn),
338
- })]
339
- : []),
340
- ];
341
- const runnable = createAgent({
342
- model: (selectedSubagent.model ?? (await this.resolveModel(params.model))),
343
- tools: (selectedSubagent.tools ?? this.resolveTools(params.tools, binding)),
344
- systemPrompt: selectedSubagent.systemPrompt ?? DEFAULT_SUBAGENT_PROMPT,
345
- middleware: middleware,
346
- responseFormat: selectedSubagent.responseFormat,
347
- contextSchema: selectedSubagent.contextSchema,
348
- name: selectedSubagent.name,
349
- description: selectedSubagent.description,
137
+ return invokeBuiltinTaskToolHelper({
138
+ binding,
139
+ toolInput: input,
140
+ options,
141
+ resolveBuiltinMiddlewareBackend: (currentBinding, currentOptions) => this.resolveBuiltinMiddlewareBackend(currentBinding, currentOptions),
142
+ resolveSubagents: (subagents, currentBinding) => this.resolveSubagents(subagents, currentBinding),
143
+ resolveModel: (model) => this.resolveModel(model),
144
+ resolveTools: (tools, currentBinding) => this.resolveTools(tools, currentBinding),
350
145
  });
351
- const result = await runnable.invoke({ messages: [new HumanMessage({ content: description })] }, { configurable: { thread_id: `${binding.agent.id}:builtin-task` }, ...(options.context ? { context: options.context } : {}) });
352
- const visibleOutput = extractVisibleOutput(result);
353
- const fallbackOutput = extractToolFallbackContext(result);
354
- return visibleOutput || fallbackOutput || JSON.stringify(result);
355
146
  }
356
147
  async resolveBuiltinMiddlewareTools(binding, options = {}) {
357
- const backend = this.resolveBuiltinMiddlewareBackend(binding, options);
358
- return createBuiltinMiddlewareTools(backend, {
359
- includeTaskTool: isDeepAgentBinding(binding),
360
- invokeTaskTool: isDeepAgentBinding(binding)
361
- ? async (input) => this.invokeBuiltinTaskTool(binding, input, options)
362
- : undefined,
148
+ return resolveBuiltinMiddlewareToolsHelper({
149
+ binding,
150
+ options,
151
+ resolveBuiltinMiddlewareBackend: (currentBinding, currentOptions) => this.resolveBuiltinMiddlewareBackend(currentBinding, currentOptions),
152
+ invokeBuiltinTaskTool: (currentBinding, toolInput, currentOptions) => this.invokeBuiltinTaskTool(currentBinding, toolInput, currentOptions),
363
153
  });
364
154
  }
365
155
  async resolveAutomaticSummarizationMiddleware(binding) {
366
- if (hasConfiguredMiddlewareKind(binding, "summarization")) {
367
- return [];
368
- }
369
- const primaryModel = getBindingPrimaryModel(binding);
370
- if (!primaryModel) {
371
- return [];
372
- }
373
- return resolveDeclaredMiddleware([{ kind: "summarization", model: primaryModel }], this.createDeclaredMiddlewareResolverOptions(binding));
156
+ return resolveAutomaticSummarizationMiddlewareHelper({
157
+ binding,
158
+ createDeclaredMiddlewareResolverOptions: (currentBinding) => this.createDeclaredMiddlewareResolverOptions(currentBinding),
159
+ });
374
160
  }
375
161
  async resolveLangChainAutomaticMiddleware(binding) {
376
- const params = getBindingLangChainParams(binding);
377
- if (!params) {
378
- return [];
379
- }
380
- const compatibleParams = applyDeepAgentDelegationPromptCompatibility(params.model, params);
381
- const automaticMiddleware = [];
382
- automaticMiddleware.push(createPatchToolCallsMiddleware());
383
- automaticMiddleware.push(...(await this.resolveAutomaticSummarizationMiddleware(binding)));
384
- if ((compatibleParams.skills?.length ?? 0) > 0) {
385
- automaticMiddleware.push(createSkillsMiddleware({
386
- backend: this.resolveFilesystemBackend(binding),
387
- sources: compatibleParams.skills,
388
- }));
389
- }
390
- if ((compatibleParams.memory?.length ?? 0) > 0) {
391
- automaticMiddleware.push(createMemoryMiddleware({
392
- backend: this.resolveFilesystemBackend(binding),
393
- sources: compatibleParams.memory,
394
- }));
395
- }
396
- if (hasConfiguredSubagentSupport(binding)) {
397
- automaticMiddleware.push(createSubAgentMiddleware({
398
- defaultModel: (await this.resolveModel(compatibleParams.model)),
399
- defaultTools: this.resolveTools(compatibleParams.tools, binding),
400
- defaultInterruptOn: getBindingInterruptCompatibilityRules(binding),
401
- subagents: (await this.resolveSubagents(compatibleParams.subagents ?? [], binding)),
402
- generalPurposeAgent: compatibleParams.generalPurposeAgent,
403
- taskDescription: compatibleParams.taskDescription ?? null,
404
- }));
405
- }
406
- return automaticMiddleware;
162
+ return resolveLangChainAutomaticMiddlewareHelper({
163
+ binding,
164
+ resolveAutomaticSummarizationMiddleware: (currentBinding) => this.resolveAutomaticSummarizationMiddleware(currentBinding),
165
+ resolveFilesystemBackend: (currentBinding) => this.resolveFilesystemBackend(currentBinding),
166
+ resolveModel: (model) => this.resolveModel(model),
167
+ resolveTools: (tools, currentBinding) => this.resolveTools(tools, currentBinding),
168
+ resolveSubagents: (subagents, currentBinding) => this.resolveSubagents(subagents, currentBinding),
169
+ });
407
170
  }
408
171
  async resolveMiddleware(binding, interruptOn) {
409
- const declarativeMiddleware = await resolveDeclaredMiddleware(getBindingMiddlewareConfigs(binding), this.createDeclaredMiddlewareResolverOptions(binding));
410
- const automaticMiddleware = isLangChainBinding(binding)
411
- ? await this.resolveLangChainAutomaticMiddleware(binding)
412
- : [];
413
- const middleware = [
414
- ...declarativeMiddleware,
415
- ...automaticMiddleware,
416
- ...(this.options.middlewareResolver ? this.options.middlewareResolver(binding) : []),
417
- ];
418
- if (interruptOn && Object.keys(interruptOn).length > 0) {
419
- middleware.push(humanInTheLoopMiddleware({ interruptOn }));
420
- }
421
- return middleware;
172
+ return resolveMiddlewareHelper({
173
+ binding,
174
+ interruptOn,
175
+ runtimeAdapterOptions: this.options,
176
+ createDeclaredMiddlewareResolverOptions: (currentBinding) => this.createDeclaredMiddlewareResolverOptions(currentBinding),
177
+ resolveLangChainAutomaticMiddleware: (currentBinding) => this.resolveLangChainAutomaticMiddleware(currentBinding),
178
+ });
422
179
  }
423
180
  async resolveSubagents(subagents, binding) {
424
- return Promise.all(subagents.map(async (subagent) => ({
425
- ...subagent,
426
- ...(subagent.passthrough ?? {}),
427
- model: subagent.model ? (await this.resolveModel(subagent.model)) : undefined,
428
- tools: subagent.tools ? this.resolveTools(subagent.tools) : undefined,
429
- skills: await materializeDeepAgentSkillSourcePaths({
430
- workspaceRoot: binding?.harnessRuntime.workspaceRoot,
431
- runRoot: binding?.harnessRuntime.runRoot,
432
- ownerId: `${binding?.agent.id ?? "agent"}-${subagent.name}`,
433
- skillPaths: subagent.skills,
434
- }),
435
- interruptOn: compileInterruptOn(subagent.tools ?? [], subagent.interruptOn),
436
- responseFormat: subagent.responseFormat,
437
- contextSchema: subagent.contextSchema,
438
- middleware: (await resolveDeclaredMiddleware(subagent.middleware, this.createDeclaredMiddlewareResolverOptions(binding))),
439
- })));
181
+ return resolveSubagentsHelper({
182
+ subagents,
183
+ binding,
184
+ resolveModel: (model) => this.resolveModel(model),
185
+ resolveTools: (tools, currentBinding) => this.resolveTools(tools, currentBinding),
186
+ createDeclaredMiddlewareResolverOptions: (currentBinding) => this.createDeclaredMiddlewareResolverOptions(currentBinding),
187
+ });
440
188
  }
441
189
  async createLangChainRunnable(binding, options = {}) {
442
190
  const params = getBindingLangChainParams(binding);
@@ -517,50 +265,22 @@ export class AgentRuntimeAdapter {
517
265
  });
518
266
  };
519
267
  const callRuntimeWithToolParseRecovery = async (activeRequest) => {
520
- try {
521
- return await callRuntime(binding, activeRequest);
522
- }
523
- catch (error) {
524
- if (resumePayload !== undefined || !isToolCallParseFailure(error)) {
525
- throw error;
526
- }
527
- return callRuntime(this.applyStrictToolJsonInstruction(binding), activeRequest);
528
- }
268
+ return callRuntimeWithToolParseRecoveryHelper({
269
+ binding,
270
+ request: activeRequest,
271
+ resumePayload,
272
+ callRuntime,
273
+ });
529
274
  };
530
275
  const primaryTools = getBindingPrimaryTools(binding);
531
276
  const resolvedTools = this.resolveTools(primaryTools, binding);
532
277
  const toolNameMapping = buildToolNameMapping(primaryTools);
533
- const executableTools = new Map();
534
- for (let index = 0; index < primaryTools.length; index += 1) {
535
- const compiledTool = primaryTools[index];
536
- const resolvedTool = resolvedTools[index];
537
- if (!compiledTool || !resolvedTool || !hasCallableToolHandler(resolvedTool)) {
538
- continue;
539
- }
540
- const handler = async (toolInput) => {
541
- const callable = typeof resolvedTool.invoke === "function"
542
- ? resolvedTool.invoke
543
- : typeof resolvedTool.call === "function"
544
- ? resolvedTool.call
545
- : resolvedTool.func;
546
- if (!callable) {
547
- throw new Error(`Tool ${compiledTool.name} has no callable handler.`);
548
- }
549
- return Promise.resolve(callable.call(resolvedTool, toolInput, options.context ? { context: options.context } : undefined));
550
- };
551
- const modelFacingName = toolNameMapping.originalToModelFacing.get(compiledTool.name) ?? compiledTool.name;
552
- const normalizedSchema = normalizeResolvedToolSchema(resolvedTool);
553
- executableTools.set(modelFacingName, {
554
- name: compiledTool.name,
555
- schema: normalizedSchema,
556
- invoke: handler,
557
- });
558
- executableTools.set(compiledTool.name, {
559
- name: compiledTool.name,
560
- schema: normalizedSchema,
561
- invoke: handler,
562
- });
563
- }
278
+ const executableTools = buildExecutableToolMap({
279
+ primaryTools,
280
+ resolvedTools,
281
+ toolNameMapping,
282
+ context: options.context,
283
+ });
564
284
  const builtinExecutableTools = await this.resolveBuiltinMiddlewareTools(binding, options);
565
285
  const localOrUpstreamInvocation = await invokeRuntimeWithLocalTools({
566
286
  binding,
@@ -0,0 +1,32 @@
1
+ import type { RunResult, ThreadSummary } from "../../../contracts/types.js";
2
+ import type { RuntimePersistence } from "../../../persistence/types.js";
3
+ import type { ConcurrencyConfig, RecoveryConfig } from "../../../workspace/support/workspace-ref-utils.js";
4
+ type Startable = {
5
+ start(): Promise<void>;
6
+ };
7
+ export declare function initializeHarnessRuntime(input: {
8
+ persistence: RuntimePersistence;
9
+ healthMonitor: Startable | null;
10
+ }): Promise<void>;
11
+ export declare function recoverStartupRuns(input: {
12
+ recoveryConfig: RecoveryConfig;
13
+ persistence: RuntimePersistence;
14
+ createStartupRecoveryContext: () => unknown;
15
+ reclaimExpiredClaimedRuns: (nowIso?: string) => Promise<void>;
16
+ }): Promise<void>;
17
+ export declare function reclaimExpiredClaimedRuns(input: {
18
+ persistence: RuntimePersistence;
19
+ setRunStateAndEmit: (threadId: string, runId: string, sequence: number, state: RunResult["state"], options: {
20
+ previousState: string | null;
21
+ checkpointRef?: string | null;
22
+ error?: string;
23
+ }) => Promise<unknown>;
24
+ emit: (threadId: string, runId: string, sequence: number, eventType: string, payload: Record<string, unknown>) => Promise<unknown>;
25
+ concurrencyConfig: ConcurrencyConfig;
26
+ getActiveRunSlots: () => number;
27
+ }, nowIso?: string): Promise<void>;
28
+ export declare function isStaleRunningRun(input: {
29
+ persistence: RuntimePersistence;
30
+ concurrencyConfig: ConcurrencyConfig;
31
+ }, thread: ThreadSummary, nowMs?: number): Promise<boolean>;
32
+ export {};
@@ -0,0 +1,23 @@
1
+ export async function initializeHarnessRuntime(input) {
2
+ await input.persistence.initialize();
3
+ await input.healthMonitor?.start();
4
+ }
5
+ export async function recoverStartupRuns(input) {
6
+ void input;
7
+ }
8
+ export async function reclaimExpiredClaimedRuns(input, nowIso = new Date().toISOString()) {
9
+ void input;
10
+ void nowIso;
11
+ }
12
+ export async function isStaleRunningRun(input, thread, nowMs = Date.now()) {
13
+ const control = await input.persistence.getRunControl(thread.latestRunId);
14
+ const heartbeatAt = control?.heartbeatAt;
15
+ if (!heartbeatAt) {
16
+ return true;
17
+ }
18
+ const heartbeatAtMs = Date.parse(heartbeatAt);
19
+ if (!Number.isFinite(heartbeatAtMs)) {
20
+ return true;
21
+ }
22
+ return nowMs - heartbeatAtMs >= input.concurrencyConfig.heartbeatTimeoutMs;
23
+ }
@@ -0,0 +1,21 @@
1
+ import type { ApprovalRecord, ThreadRecord, ThreadSummary } from "../../../contracts/types.js";
2
+ import type { RuntimePersistence } from "../../../persistence/types.js";
3
+ export declare function getThreadRecord(input: {
4
+ persistence: RuntimePersistence;
5
+ getSession: (threadId: string) => Promise<ThreadSummary | null>;
6
+ }, threadId: string): Promise<ThreadRecord | null>;
7
+ export declare function listPublicApprovals(input: {
8
+ persistence: RuntimePersistence;
9
+ }, filter?: {
10
+ status?: ApprovalRecord["status"];
11
+ threadId?: string;
12
+ runId?: string;
13
+ }): Promise<ApprovalRecord[]>;
14
+ export declare function getPublicApproval(input: {
15
+ persistence: RuntimePersistence;
16
+ }, approvalId: string): Promise<ApprovalRecord | null>;
17
+ export declare function deleteThreadRecord(input: {
18
+ getThread: (threadId: string) => Promise<ThreadRecord | null>;
19
+ deleteThread: (threadId: string) => Promise<boolean>;
20
+ deleteThreadCheckpoints: (threadId: string) => Promise<void>;
21
+ }, threadId: string): Promise<boolean>;
@@ -0,0 +1,59 @@
1
+ import { isTerminalRunState, toPublicApprovalRecord } from "./helpers.js";
2
+ export async function getThreadRecord(input, threadId) {
3
+ const [threadSummary, meta, messages, runs] = await Promise.all([
4
+ input.getSession(threadId),
5
+ input.persistence.getThreadMeta(threadId),
6
+ input.persistence.listThreadMessages(threadId, 200),
7
+ input.persistence.listThreadRuns(threadId),
8
+ ]);
9
+ if (!threadSummary || !meta) {
10
+ return null;
11
+ }
12
+ const latestRunId = threadSummary.latestRunId;
13
+ const latestApprovals = await input.persistence.getRunApprovals(threadId, latestRunId);
14
+ const pendingApproval = latestApprovals
15
+ .filter((approval) => approval.status === "pending")
16
+ .sort((left, right) => right.requestedAt.localeCompare(left.requestedAt))[0];
17
+ return {
18
+ threadId,
19
+ entryAgentId: meta.entryAgentId,
20
+ currentState: threadSummary.status,
21
+ latestRunId,
22
+ createdAt: meta.createdAt,
23
+ updatedAt: threadSummary.updatedAt,
24
+ messages,
25
+ runs,
26
+ pendingDecision: pendingApproval
27
+ ? {
28
+ approvalId: pendingApproval.approvalId,
29
+ pendingActionId: pendingApproval.pendingActionId,
30
+ toolName: pendingApproval.toolName,
31
+ allowedDecisions: pendingApproval.allowedDecisions,
32
+ requestedAt: pendingApproval.requestedAt,
33
+ }
34
+ : undefined,
35
+ };
36
+ }
37
+ export async function listPublicApprovals(input, filter) {
38
+ const approvals = await input.persistence.listApprovals(filter);
39
+ return approvals.map((approval) => toPublicApprovalRecord(approval));
40
+ }
41
+ export async function getPublicApproval(input, approvalId) {
42
+ const approval = await input.persistence.getApproval(approvalId);
43
+ return approval ? toPublicApprovalRecord(approval) : null;
44
+ }
45
+ export async function deleteThreadRecord(input, threadId) {
46
+ const thread = await input.getThread(threadId);
47
+ if (!thread) {
48
+ return false;
49
+ }
50
+ const activeRun = thread.runs.find((run) => !isTerminalRunState(run.state));
51
+ if (activeRun) {
52
+ throw new Error(`Cannot delete thread ${threadId} while run ${activeRun.runId} is ${activeRun.state}`);
53
+ }
54
+ const deleted = await input.deleteThread(threadId);
55
+ if (deleted) {
56
+ await input.deleteThreadCheckpoints(threadId);
57
+ }
58
+ return deleted;
59
+ }