@botbotgo/agent-harness 0.0.41 → 0.0.43

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 (30) hide show
  1. package/README.md +105 -86
  2. package/dist/config/agents/direct.yaml +8 -7
  3. package/dist/config/agents/orchestra.yaml +8 -6
  4. package/dist/contracts/types.d.ts +35 -2
  5. package/dist/package-version.d.ts +1 -1
  6. package/dist/package-version.js +1 -1
  7. package/dist/persistence/file-store.d.ts +8 -6
  8. package/dist/persistence/file-store.js +2 -0
  9. package/dist/runtime/agent-runtime-adapter.d.ts +1 -1
  10. package/dist/runtime/agent-runtime-adapter.js +68 -80
  11. package/dist/runtime/checkpoint-maintenance.js +1 -1
  12. package/dist/runtime/declared-middleware.d.ts +8 -1
  13. package/dist/runtime/declared-middleware.js +18 -1
  14. package/dist/runtime/harness.d.ts +6 -0
  15. package/dist/runtime/harness.js +390 -256
  16. package/dist/runtime/inventory.js +2 -1
  17. package/dist/runtime/support/compiled-binding.d.ts +15 -0
  18. package/dist/runtime/support/compiled-binding.js +56 -0
  19. package/dist/runtime/support/harness-support.d.ts +2 -2
  20. package/dist/runtime/support/harness-support.js +14 -12
  21. package/dist/runtime/support/runtime-factories.d.ts +1 -1
  22. package/dist/runtime/support/runtime-factories.js +3 -0
  23. package/dist/workspace/agent-binding-compiler.js +61 -33
  24. package/dist/workspace/object-loader.js +50 -4
  25. package/dist/workspace/support/agent-capabilities.d.ts +7 -0
  26. package/dist/workspace/support/agent-capabilities.js +30 -0
  27. package/dist/workspace/support/workspace-ref-utils.d.ts +4 -0
  28. package/dist/workspace/support/workspace-ref-utils.js +15 -1
  29. package/dist/workspace/validate.js +14 -8
  30. package/package.json +2 -2
@@ -12,6 +12,10 @@ import { computeIncrementalOutput, extractAgentStep, extractInterruptPayload, ex
12
12
  import { wrapToolForExecution } from "./tool-hitl.js";
13
13
  import { resolveDeclaredMiddleware } from "./declared-middleware.js";
14
14
  import { extractMessageText, normalizeMessageContent } from "../utils/message-content.js";
15
+ import { getBindingDeepAgentParams, getBindingInterruptCompatibilityRules, getBindingLangChainParams, getBindingMiddlewareConfigs, getBindingModelInit, getBindingPrimaryModel, getBindingPrimaryTools, getBindingSystemPrompt, isDeepAgentBinding, isLangChainBinding, } from "./support/compiled-binding.js";
16
+ function countConfiguredTools(binding) {
17
+ return getBindingPrimaryTools(binding).length;
18
+ }
15
19
  const AGENT_INTERRUPT_SENTINEL_PREFIX = "__agent_harness_interrupt__:";
16
20
  const MODEL_SAFE_TOOL_NAME_PATTERN = /^[a-zA-Z0-9_-]+$/;
17
21
  class RuntimeOperationTimeoutError extends Error {
@@ -139,6 +143,11 @@ function hasCallableToolHandler(value) {
139
143
  const typed = value;
140
144
  return typeof typed.invoke === "function" || typeof typed.call === "function" || typeof typed.func === "function";
141
145
  }
146
+ function asRecord(value) {
147
+ return typeof value === "object" && value !== null && !Array.isArray(value)
148
+ ? { ...value }
149
+ : undefined;
150
+ }
142
151
  function normalizeResolvedToolSchema(resolvedTool) {
143
152
  const schema = resolvedTool.schema;
144
153
  if (schema && typeof schema.parse === "function" && "_def" in schema) {
@@ -163,38 +172,16 @@ function asStructuredExecutableTool(resolvedTool, modelFacingName, description)
163
172
  schema: normalizeResolvedToolSchema(resolvedTool),
164
173
  });
165
174
  }
166
- function countConfiguredTools(binding) {
167
- return binding.langchainAgentParams?.tools.length ?? binding.deepAgentParams?.tools.length ?? 0;
168
- }
169
175
  export class AgentRuntimeAdapter {
170
176
  options;
171
177
  constructor(options = {}) {
172
178
  this.options = options;
173
179
  }
174
- canUseSimpleDeepAgentFastPath(binding) {
175
- const params = binding.deepAgentParams;
176
- if (!params) {
177
- return false;
178
- }
179
- if ((params.subagents?.length ?? 0) > 0) {
180
- return false;
181
- }
182
- if ((params.memory?.length ?? 0) > 0) {
183
- return false;
184
- }
185
- if ((params.skills?.length ?? 0) > 0) {
186
- return false;
187
- }
188
- if (params.backend || params.store) {
189
- return false;
190
- }
191
- return true;
192
- }
193
180
  resolveBindingTimeout(binding) {
194
- return resolveTimeoutMs(binding.langchainAgentParams?.model.init.timeout ?? binding.deepAgentParams?.model.init.timeout);
181
+ return resolveTimeoutMs(getBindingModelInit(binding)?.timeout);
195
182
  }
196
183
  resolveStreamIdleTimeout(binding) {
197
- const configuredIdleTimeout = resolveTimeoutMs(binding.langchainAgentParams?.model.init.streamIdleTimeout ?? binding.deepAgentParams?.model.init.streamIdleTimeout);
184
+ const configuredIdleTimeout = resolveTimeoutMs(getBindingModelInit(binding)?.streamIdleTimeout);
198
185
  if (configuredIdleTimeout) {
199
186
  return configuredIdleTimeout;
200
187
  }
@@ -303,35 +290,38 @@ export class AgentRuntimeAdapter {
303
290
  };
304
291
  }
305
292
  applyStrictToolJsonInstruction(binding) {
306
- if (binding.langchainAgentParams) {
293
+ if (isLangChainBinding(binding)) {
294
+ const params = getBindingLangChainParams(binding);
307
295
  return {
308
296
  ...binding,
309
297
  langchainAgentParams: {
310
- ...binding.langchainAgentParams,
311
- systemPrompt: [binding.langchainAgentParams.systemPrompt, STRICT_TOOL_JSON_INSTRUCTION].filter(Boolean).join("\n\n"),
298
+ ...params,
299
+ systemPrompt: [params.systemPrompt, STRICT_TOOL_JSON_INSTRUCTION].filter(Boolean).join("\n\n"),
312
300
  },
313
301
  };
314
302
  }
315
- if (binding.deepAgentParams) {
303
+ if (isDeepAgentBinding(binding)) {
304
+ const params = getBindingDeepAgentParams(binding);
316
305
  return {
317
306
  ...binding,
318
307
  deepAgentParams: {
319
- ...binding.deepAgentParams,
320
- systemPrompt: [binding.deepAgentParams.systemPrompt, STRICT_TOOL_JSON_INSTRUCTION].filter(Boolean).join("\n\n"),
308
+ ...params,
309
+ systemPrompt: [params.systemPrompt, STRICT_TOOL_JSON_INSTRUCTION].filter(Boolean).join("\n\n"),
321
310
  },
322
311
  };
323
312
  }
324
313
  return binding;
325
314
  }
326
315
  async synthesizeDeepAgentAnswer(binding, input, result) {
327
- if (!binding.deepAgentParams) {
316
+ const params = getBindingDeepAgentParams(binding);
317
+ if (!params) {
328
318
  return "";
329
319
  }
330
320
  const toolContext = extractToolFallbackContext(result);
331
321
  if (!toolContext) {
332
322
  return "";
333
323
  }
334
- const model = (await this.resolveModel(binding.deepAgentParams.model));
324
+ const model = (await this.resolveModel(params.model));
335
325
  if (!model?.invoke) {
336
326
  return "";
337
327
  }
@@ -384,6 +374,14 @@ export class AgentRuntimeAdapter {
384
374
  messages: this.buildAgentMessages(history, input),
385
375
  };
386
376
  }
377
+ buildStateSnapshot(result) {
378
+ const snapshot = { ...result };
379
+ delete snapshot.messages;
380
+ delete snapshot.__interrupt__;
381
+ delete snapshot.structuredResponse;
382
+ delete snapshot.files;
383
+ return Object.keys(snapshot).length > 0 ? snapshot : undefined;
384
+ }
387
385
  buildRawModelMessages(systemPrompt, history, input) {
388
386
  const messages = [];
389
387
  if (systemPrompt) {
@@ -443,15 +441,13 @@ export class AgentRuntimeAdapter {
443
441
  return compiled.size > 0 ? Object.fromEntries(compiled.entries()) : undefined;
444
442
  }
445
443
  resolveInterruptOn(binding) {
446
- if (binding.deepAgentParams) {
447
- return this.compileInterruptOn(binding.deepAgentParams.tools, binding.deepAgentParams.interruptOn);
448
- }
449
- return this.compileInterruptOn(binding.langchainAgentParams?.tools ?? [], binding.agent.langchainAgentConfig?.interruptOn);
444
+ return this.compileInterruptOn(getBindingPrimaryTools(binding), getBindingInterruptCompatibilityRules(binding));
450
445
  }
451
446
  async resolveMiddleware(binding, interruptOn) {
452
- const declarativeMiddleware = await resolveDeclaredMiddleware(binding.langchainAgentParams?.middleware ??
453
- binding.deepAgentParams?.middleware, {
447
+ const declarativeMiddleware = await resolveDeclaredMiddleware(getBindingMiddlewareConfigs(binding), {
454
448
  resolveModel: (model) => this.resolveModel(model),
449
+ resolveCustom: this.options.declaredMiddlewareResolver,
450
+ binding,
455
451
  });
456
452
  const middleware = [
457
453
  ...declarativeMiddleware,
@@ -485,7 +481,7 @@ export class AgentRuntimeAdapter {
485
481
  .replaceAll("{{secondaryAgentId}}", secondaryBinding.agent.id)
486
482
  .replaceAll("{{secondaryDescription}}", secondaryBinding.agent.description);
487
483
  }
488
- async resolveSubagents(subagents) {
484
+ async resolveSubagents(subagents, binding) {
489
485
  return Promise.all(subagents.map(async (subagent) => ({
490
486
  ...subagent,
491
487
  model: subagent.model ? (await this.resolveModel(subagent.model)) : undefined,
@@ -495,38 +491,14 @@ export class AgentRuntimeAdapter {
495
491
  contextSchema: subagent.contextSchema,
496
492
  middleware: (await resolveDeclaredMiddleware(subagent.middleware, {
497
493
  resolveModel: (model) => this.resolveModel(model),
494
+ resolveCustom: this.options.declaredMiddlewareResolver,
495
+ binding,
498
496
  })),
499
497
  })));
500
498
  }
501
499
  async create(binding) {
502
- if (binding.langchainAgentParams) {
503
- const interruptOn = this.resolveInterruptOn(binding);
504
- const model = (await this.resolveModel(binding.langchainAgentParams.model));
505
- const tools = this.resolveTools(binding.langchainAgentParams.tools, binding);
506
- if (tools.length > 0 && typeof model.bindTools !== "function") {
507
- throw new Error(`Agent ${binding.agent.id} configures ${tools.length} tool(s), but resolved model ${binding.langchainAgentParams.model.id} does not support tool binding.`);
508
- }
509
- return createAgent({
510
- model: model,
511
- tools: tools,
512
- systemPrompt: binding.langchainAgentParams.systemPrompt,
513
- stateSchema: binding.langchainAgentParams.stateSchema,
514
- responseFormat: binding.langchainAgentParams.responseFormat,
515
- contextSchema: binding.langchainAgentParams.contextSchema,
516
- middleware: (await this.resolveMiddleware(binding, interruptOn)),
517
- checkpointer: this.resolveCheckpointer(binding),
518
- store: this.options.storeResolver?.(binding),
519
- includeAgentName: binding.langchainAgentParams.includeAgentName,
520
- version: binding.langchainAgentParams.version,
521
- name: binding.langchainAgentParams.name,
522
- description: binding.langchainAgentParams.description,
523
- });
524
- }
525
- const params = binding.deepAgentParams;
526
- if (!params) {
527
- throw new Error(`Agent ${binding.agent.id} has no runnable params`);
528
- }
529
- if (this.canUseSimpleDeepAgentFastPath(binding)) {
500
+ if (isLangChainBinding(binding)) {
501
+ const params = getBindingLangChainParams(binding);
530
502
  const interruptOn = this.resolveInterruptOn(binding);
531
503
  const model = (await this.resolveModel(params.model));
532
504
  const tools = this.resolveTools(params.tools, binding);
@@ -537,12 +509,22 @@ export class AgentRuntimeAdapter {
537
509
  model: model,
538
510
  tools: tools,
539
511
  systemPrompt: params.systemPrompt,
512
+ stateSchema: params.stateSchema,
540
513
  responseFormat: params.responseFormat,
541
514
  contextSchema: params.contextSchema,
542
515
  middleware: (await this.resolveMiddleware(binding, interruptOn)),
543
516
  checkpointer: this.resolveCheckpointer(binding),
517
+ store: this.options.storeResolver?.(binding),
518
+ includeAgentName: params.includeAgentName,
519
+ version: params.version,
520
+ name: params.name,
521
+ description: params.description,
544
522
  });
545
523
  }
524
+ const params = getBindingDeepAgentParams(binding);
525
+ if (!params) {
526
+ throw new Error(`Agent ${binding.agent.id} has no runnable params`);
527
+ }
546
528
  const deepAgentConfig = {
547
529
  model: (await this.resolveModel(params.model)),
548
530
  tools: this.resolveTools(params.tools, binding),
@@ -550,7 +532,7 @@ export class AgentRuntimeAdapter {
550
532
  responseFormat: params.responseFormat,
551
533
  contextSchema: params.contextSchema,
552
534
  middleware: (await this.resolveMiddleware(binding)),
553
- subagents: (await this.resolveSubagents(params.subagents)),
535
+ subagents: (await this.resolveSubagents(params.subagents, binding)),
554
536
  checkpointer: this.resolveCheckpointer(binding),
555
537
  store: this.options.storeResolver?.(binding),
556
538
  backend: this.options.backendResolver?.(binding),
@@ -564,10 +546,8 @@ export class AgentRuntimeAdapter {
564
546
  return createDeepAgent(deepAgentConfig);
565
547
  }
566
548
  async route(input, primaryBinding, secondaryBinding, options = {}) {
567
- const routeModelConfig = primaryBinding.langchainAgentParams?.model ??
568
- primaryBinding.deepAgentParams?.model ??
569
- secondaryBinding.langchainAgentParams?.model ??
570
- secondaryBinding.deepAgentParams?.model;
549
+ const routeModelConfig = getBindingPrimaryModel(primaryBinding) ??
550
+ getBindingPrimaryModel(secondaryBinding);
571
551
  if (!routeModelConfig) {
572
552
  throw new Error("No router model configuration available");
573
553
  }
@@ -619,13 +599,21 @@ export class AgentRuntimeAdapter {
619
599
  throw new Error(emptyAssistantMessageFailure);
620
600
  }
621
601
  const output = visibleOutput || synthesizedOutput || toolFallback || JSON.stringify(result, null, 2);
602
+ const finalMessageText = sanitizeVisibleText(output);
622
603
  return {
623
604
  threadId,
624
605
  runId,
625
606
  agentId: binding.agent.id,
626
607
  state: Array.isArray(result.__interrupt__) && result.__interrupt__.length > 0 ? "waiting_for_approval" : "completed",
627
608
  interruptContent,
628
- output: sanitizeVisibleText(output),
609
+ output: finalMessageText,
610
+ finalMessageText,
611
+ metadata: {
612
+ ...(result.structuredResponse !== undefined ? { structuredResponse: result.structuredResponse } : {}),
613
+ ...(asRecord(result.files) ? { files: asRecord(result.files) } : {}),
614
+ ...(this.buildStateSnapshot(result) ? { stateSnapshot: this.buildStateSnapshot(result) } : {}),
615
+ upstreamResult: result,
616
+ },
629
617
  };
630
618
  }
631
619
  async *stream(binding, input, threadId, history = [], options = {}) {
@@ -633,9 +621,9 @@ export class AgentRuntimeAdapter {
633
621
  const invokeTimeoutMs = this.resolveBindingTimeout(binding);
634
622
  const streamIdleTimeoutMs = this.resolveStreamIdleTimeout(binding);
635
623
  const streamDeadlineAt = invokeTimeoutMs ? Date.now() + invokeTimeoutMs : undefined;
636
- if (binding.langchainAgentParams) {
637
- const langchainParams = binding.langchainAgentParams;
638
- const resolvedModel = (await this.resolveModel(binding.langchainAgentParams.model));
624
+ if (isLangChainBinding(binding)) {
625
+ const langchainParams = getBindingLangChainParams(binding);
626
+ const resolvedModel = (await this.resolveModel(langchainParams.model));
639
627
  const tools = this.resolveTools(langchainParams.tools, binding);
640
628
  const canUseDirectModelStream = tools.length === 0 || typeof resolvedModel.bindTools !== "function";
641
629
  const model = canUseDirectModelStream
@@ -647,7 +635,7 @@ export class AgentRuntimeAdapter {
647
635
  // agent loop and only adds an extra model round-trip before the runnable path.
648
636
  if (canUseDirectModelStream && typeof model.stream === "function") {
649
637
  let emitted = false;
650
- const stream = await this.withTimeout(() => model.stream(this.buildRawModelMessages(langchainParams.systemPrompt, history, input)), computeRemainingTimeoutMs(streamDeadlineAt, invokeTimeoutMs), "model stream start", "stream");
638
+ const stream = await this.withTimeout(() => model.stream(this.buildRawModelMessages(getBindingSystemPrompt(binding), history, input)), computeRemainingTimeoutMs(streamDeadlineAt, invokeTimeoutMs), "model stream start", "stream");
651
639
  for await (const chunk of this.iterateWithTimeout(stream, streamIdleTimeoutMs, "model stream", streamDeadlineAt, invokeTimeoutMs)) {
652
640
  const delta = readStreamDelta(chunk);
653
641
  if (delta) {
@@ -669,7 +657,7 @@ export class AgentRuntimeAdapter {
669
657
  const request = this.buildInvocationRequest(history, input, options);
670
658
  if (typeof runnable.streamEvents === "function") {
671
659
  const events = await this.withTimeout(() => runnable.streamEvents(request, { configurable: { thread_id: threadId }, version: "v2", ...(options.context ? { context: options.context } : {}) }), computeRemainingTimeoutMs(streamDeadlineAt, invokeTimeoutMs), "agent streamEvents start", "stream");
672
- const allowVisibleStreamDeltas = Boolean(binding.langchainAgentParams);
660
+ const allowVisibleStreamDeltas = isLangChainBinding(binding);
673
661
  let emittedOutput = "";
674
662
  let emittedToolError = false;
675
663
  const seenTerminalOutputs = new Set();
@@ -694,7 +682,7 @@ export class AgentRuntimeAdapter {
694
682
  }
695
683
  }
696
684
  }
697
- if (binding.deepAgentParams) {
685
+ if (isDeepAgentBinding(binding)) {
698
686
  const stateStreamOutput = extractStateStreamOutput(event);
699
687
  if (stateStreamOutput) {
700
688
  const nextOutput = computeIncrementalOutput(emittedOutput, sanitizeVisibleText(stateStreamOutput));
@@ -734,7 +722,7 @@ export class AgentRuntimeAdapter {
734
722
  return;
735
723
  }
736
724
  }
737
- if (binding.langchainAgentParams && typeof runnable.stream === "function") {
725
+ if (isLangChainBinding(binding) && typeof runnable.stream === "function") {
738
726
  const stream = await this.withTimeout(() => runnable.stream(request, { configurable: { thread_id: threadId } }), computeRemainingTimeoutMs(streamDeadlineAt, invokeTimeoutMs), "agent stream start", "stream");
739
727
  let emitted = false;
740
728
  for await (const chunk of this.iterateWithTimeout(stream, streamIdleTimeoutMs, "agent stream", streamDeadlineAt, invokeTimeoutMs)) {
@@ -46,7 +46,7 @@ export function readCheckpointMaintenanceConfig(workspace) {
46
46
  }
47
47
  function resolveSqliteCheckpointPath(binding) {
48
48
  const config = binding.harnessRuntime.checkpointer;
49
- if (!config) {
49
+ if (!config || typeof config === "boolean") {
50
50
  return null;
51
51
  }
52
52
  const kind = typeof config.kind === "string" ? config.kind : "FileCheckpointer";
@@ -1,4 +1,11 @@
1
- import type { CompiledModel } from "../contracts/types.js";
1
+ import type { CompiledAgentBinding, CompiledModel } from "../contracts/types.js";
2
2
  export declare function resolveDeclaredMiddleware(middlewareConfigs: unknown[] | undefined, options: {
3
3
  resolveModel: (model: CompiledModel) => Promise<unknown>;
4
+ resolveCustom?: (input: {
5
+ kind: string;
6
+ config: Record<string, unknown>;
7
+ binding?: CompiledAgentBinding;
8
+ resolveModel: (model: CompiledModel) => Promise<unknown>;
9
+ }) => unknown | unknown[] | null | undefined | Promise<unknown | unknown[] | null | undefined>;
10
+ binding?: CompiledAgentBinding;
4
11
  }): Promise<unknown[]>;
@@ -83,8 +83,25 @@ export async function resolveDeclaredMiddleware(middlewareConfigs, options) {
83
83
  case "anthropicPromptCaching":
84
84
  resolved.push(anthropicPromptCachingMiddleware(runtimeConfig));
85
85
  break;
86
- default:
86
+ default: {
87
+ const customResolved = options.resolveCustom
88
+ ? await options.resolveCustom({
89
+ kind,
90
+ config: runtimeConfig,
91
+ binding: options.binding,
92
+ resolveModel: options.resolveModel,
93
+ })
94
+ : undefined;
95
+ if (Array.isArray(customResolved)) {
96
+ resolved.push(...customResolved);
97
+ break;
98
+ }
99
+ if (customResolved) {
100
+ resolved.push(customResolved);
101
+ break;
102
+ }
87
103
  throw new Error(`Unsupported declarative middleware kind ${kind}`);
104
+ }
88
105
  }
89
106
  }
90
107
  return resolved;
@@ -21,6 +21,11 @@ export declare class AgentHarnessRuntime {
21
21
  private readonly resolvedRuntimeAdapterOptions;
22
22
  private readonly checkpointMaintenance;
23
23
  private readonly recoveryConfig;
24
+ private readonly concurrencyConfig;
25
+ private activeRunSlots;
26
+ private readonly pendingRunSlots;
27
+ private toPublicApprovalRecord;
28
+ private normalizeInvocationEnvelope;
24
29
  private listHostBindings;
25
30
  private defaultRunRoot;
26
31
  private heuristicRoute;
@@ -68,6 +73,7 @@ export declare class AgentHarnessRuntime {
68
73
  private resolveApprovalRecord;
69
74
  private isDecisionRun;
70
75
  private notifyListener;
76
+ private acquireRunSlot;
71
77
  private dispatchRunListeners;
72
78
  run(options: RunOptions): Promise<RunResult>;
73
79
  streamEvents(options: RunStartOptions): AsyncGenerator<HarnessStreamItem>;