@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
@@ -1,4 +1,5 @@
1
1
  import { readSkillMetadata } from "./support/skill-metadata.js";
2
+ import { getBindingPrimaryTools } from "./support/compiled-binding.js";
2
3
  function listHostBindings(workspace) {
3
4
  return Array.from(workspace.bindings.values()).filter((binding) => binding.harnessRuntime.hostFacing);
4
5
  }
@@ -31,7 +32,7 @@ export function listAgentTools(workspace, agentId) {
31
32
  if (!binding) {
32
33
  return [];
33
34
  }
34
- return dedupeTools(binding.langchainAgentParams?.tools ?? binding.deepAgentParams?.tools ?? []);
35
+ return dedupeTools(getBindingPrimaryTools(binding));
35
36
  }
36
37
  export function listAgentSkills(workspace, agentId) {
37
38
  const binding = findAgentBinding(workspace, agentId);
@@ -0,0 +1,15 @@
1
+ import type { CompiledAgentBinding, CompiledModel, CompiledTool, DeepAgentParams, LangChainAgentParams } from "../../contracts/types.js";
2
+ export declare function getBindingAdapterKind(binding: CompiledAgentBinding): string;
3
+ export declare function getBindingAdapterConfig(binding: CompiledAgentBinding): Record<string, unknown>;
4
+ export declare function getBindingLangChainParams(binding: CompiledAgentBinding): LangChainAgentParams | undefined;
5
+ export declare function getBindingDeepAgentParams(binding: CompiledAgentBinding): DeepAgentParams | undefined;
6
+ export declare function isLangChainBinding(binding: CompiledAgentBinding): boolean;
7
+ export declare function isDeepAgentBinding(binding: CompiledAgentBinding): boolean;
8
+ export declare function getBindingPrimaryTools(binding: CompiledAgentBinding): CompiledTool[];
9
+ export declare function getBindingPrimaryModel(binding: CompiledAgentBinding): CompiledModel | undefined;
10
+ export declare function getBindingSystemPrompt(binding: CompiledAgentBinding): string | undefined;
11
+ export declare function getBindingMiddlewareConfigs(binding: CompiledAgentBinding): Array<Record<string, unknown>> | undefined;
12
+ export declare function getBindingInterruptCompatibilityRules(binding: CompiledAgentBinding): Record<string, boolean | object> | undefined;
13
+ export declare function getBindingModelInit(binding: CompiledAgentBinding): Record<string, unknown> | undefined;
14
+ export declare function getBindingStoreConfig(binding: CompiledAgentBinding): Record<string, unknown> | undefined;
15
+ export declare function bindingHasSubagents(binding: CompiledAgentBinding): boolean;
@@ -0,0 +1,56 @@
1
+ function asRecord(value) {
2
+ return typeof value === "object" && value !== null && !Array.isArray(value)
3
+ ? value
4
+ : undefined;
5
+ }
6
+ export function getBindingAdapterKind(binding) {
7
+ return binding.adapter?.kind ?? binding.agent.executionMode;
8
+ }
9
+ export function getBindingAdapterConfig(binding) {
10
+ return binding.adapter?.config ?? {};
11
+ }
12
+ export function getBindingLangChainParams(binding) {
13
+ const adapterParams = asRecord(getBindingAdapterConfig(binding).params);
14
+ if (getBindingAdapterKind(binding) === "langchain-v1" && adapterParams) {
15
+ return adapterParams;
16
+ }
17
+ return binding.langchainAgentParams;
18
+ }
19
+ export function getBindingDeepAgentParams(binding) {
20
+ const adapterParams = asRecord(getBindingAdapterConfig(binding).params);
21
+ if (getBindingAdapterKind(binding) === "deepagent" && adapterParams) {
22
+ return adapterParams;
23
+ }
24
+ return binding.deepAgentParams;
25
+ }
26
+ export function isLangChainBinding(binding) {
27
+ return getBindingAdapterKind(binding) === "langchain-v1" || Boolean(binding.langchainAgentParams);
28
+ }
29
+ export function isDeepAgentBinding(binding) {
30
+ return getBindingAdapterKind(binding) === "deepagent" || Boolean(binding.deepAgentParams);
31
+ }
32
+ export function getBindingPrimaryTools(binding) {
33
+ return binding.langchainAgentParams?.tools ?? binding.deepAgentParams?.tools ?? [];
34
+ }
35
+ export function getBindingPrimaryModel(binding) {
36
+ return binding.langchainAgentParams?.model ?? binding.deepAgentParams?.model;
37
+ }
38
+ export function getBindingSystemPrompt(binding) {
39
+ return binding.langchainAgentParams?.systemPrompt ?? binding.deepAgentParams?.systemPrompt;
40
+ }
41
+ export function getBindingMiddlewareConfigs(binding) {
42
+ return binding.langchainAgentParams?.middleware ?? binding.deepAgentParams?.middleware;
43
+ }
44
+ export function getBindingInterruptCompatibilityRules(binding) {
45
+ return binding.deepAgentParams?.interruptOn ??
46
+ binding.agent.langchainAgentConfig?.interruptOn;
47
+ }
48
+ export function getBindingModelInit(binding) {
49
+ return getBindingPrimaryModel(binding)?.init;
50
+ }
51
+ export function getBindingStoreConfig(binding) {
52
+ return binding.deepAgentParams?.store ?? binding.harnessRuntime.store;
53
+ }
54
+ export function bindingHasSubagents(binding) {
55
+ return (binding.deepAgentParams?.subagents.length ?? 0) > 0;
56
+ }
@@ -1,4 +1,4 @@
1
- import type { ApprovalRecord, HarnessEvent, WorkspaceBundle } from "../../contracts/types.js";
1
+ import type { HarnessEvent, InternalApprovalRecord, WorkspaceBundle } from "../../contracts/types.js";
2
2
  export declare function renderRuntimeFailure(error: unknown): string;
3
3
  export declare function renderToolFailure(toolName: string, output: unknown): string;
4
4
  export declare function parseInterruptContent(content: string): {
@@ -19,7 +19,7 @@ export declare function heuristicRoute(input: string, primaryBinding?: {
19
19
  };
20
20
  }): string;
21
21
  export declare function createHarnessEvent(threadId: string, runId: string, sequence: number, eventType: string, payload: Record<string, unknown>, source?: HarnessEvent["source"]): HarnessEvent;
22
- export declare function createPendingApproval(threadId: string, runId: string, checkpointRef: string, input: string, interruptContent?: string): ApprovalRecord;
22
+ export declare function createPendingApproval(threadId: string, runId: string, checkpointRef: string, input: string, interruptContent?: string): InternalApprovalRecord;
23
23
  export declare function inferRoutingBindings(workspace: WorkspaceBundle): {
24
24
  primaryBinding: import("../../contracts/types.js").CompiledAgentBinding;
25
25
  secondaryBinding: import("../../contracts/types.js").CompiledAgentBinding | undefined;
@@ -1,4 +1,5 @@
1
1
  import { createPersistentId } from "../../utils/id.js";
2
+ import { isDelegationCapableBinding } from "../../workspace/support/agent-capabilities.js";
2
3
  export function renderRuntimeFailure(error) {
3
4
  const message = error instanceof Error ? error.message : String(error);
4
5
  return `runtime_error=${message}`.trim();
@@ -131,20 +132,21 @@ export function inferRoutingBindings(workspace) {
131
132
  const hostBindings = Array.from(workspace.bindings.values()).filter((binding) => binding.harnessRuntime.hostFacing);
132
133
  const researchBinding = hostBindings.find((binding) => binding.agent.id === "research-lite" || binding.agent.id === "research");
133
134
  const directBinding = hostBindings.find((binding) => binding.agent.id === "direct");
134
- const langchainHost = hostBindings.find((binding) => binding.agent.executionMode === "langchain-v1" && binding.agent.id !== researchBinding?.agent.id);
135
- const deepagentHosts = hostBindings.filter((binding) => binding.agent.executionMode === "deepagent");
136
- const defaultDeepagentHost = deepagentHosts.find((binding) => binding.agent.id === "orchestra") ??
137
- deepagentHosts.find((binding) => (binding.deepAgentParams?.subagents.length ?? 0) > 0) ??
138
- deepagentHosts[0];
139
- const deepagentWithSubagents = deepagentHosts.find((binding) => (binding.deepAgentParams?.subagents.length ?? 0) > 0) ??
140
- deepagentHosts[0];
141
- const primaryBinding = defaultDeepagentHost ?? directBinding ?? langchainHost ?? hostBindings[0];
142
- const secondaryBinding = langchainHost && langchainHost.agent.id !== primaryBinding?.agent.id
143
- ? langchainHost
135
+ const delegationHosts = hostBindings.filter((binding) => isDelegationCapableBinding(binding));
136
+ const lightweightHosts = hostBindings.filter((binding) => !isDelegationCapableBinding(binding));
137
+ const defaultOrchestratingHost = hostBindings.find((binding) => binding.agent.id === "orchestra") ??
138
+ delegationHosts.find((binding) => (binding.deepAgentParams?.subagents.length ?? 0) > 0) ??
139
+ delegationHosts[0];
140
+ const delegationPreferredSecondary = delegationHosts.find((binding) => (binding.deepAgentParams?.subagents.length ?? 0) > 0) ??
141
+ delegationHosts[0];
142
+ const genericLightweightHost = lightweightHosts.find((binding) => binding.agent.id !== researchBinding?.agent.id);
143
+ const primaryBinding = defaultOrchestratingHost ?? directBinding ?? genericLightweightHost ?? hostBindings[0];
144
+ const secondaryBinding = genericLightweightHost && genericLightweightHost.agent.id !== primaryBinding?.agent.id
145
+ ? genericLightweightHost
144
146
  : directBinding && directBinding.agent.id !== primaryBinding?.agent.id
145
147
  ? directBinding
146
- : deepagentWithSubagents && deepagentWithSubagents.agent.id !== primaryBinding?.agent.id
147
- ? deepagentWithSubagents
148
+ : delegationPreferredSecondary && delegationPreferredSecondary.agent.id !== primaryBinding?.agent.id
149
+ ? delegationPreferredSecondary
148
150
  : hostBindings.find((binding) => binding.agent.id !== primaryBinding?.agent.id);
149
151
  return { primaryBinding, secondaryBinding, researchBinding, hostBindings };
150
152
  }
@@ -1,3 +1,3 @@
1
1
  import { type StoreLike } from "../store.js";
2
2
  export declare function createStoreForConfig(storeConfig: Record<string, unknown>, runRoot: string): StoreLike;
3
- export declare function createCheckpointerForConfig(checkpointerConfig: Record<string, unknown>, runRoot: string): unknown;
3
+ export declare function createCheckpointerForConfig(checkpointerConfig: Record<string, unknown> | boolean, runRoot: string): unknown;
@@ -22,6 +22,9 @@ export function createStoreForConfig(storeConfig, runRoot) {
22
22
  }
23
23
  }
24
24
  export function createCheckpointerForConfig(checkpointerConfig, runRoot) {
25
+ if (typeof checkpointerConfig === "boolean") {
26
+ return checkpointerConfig;
27
+ }
25
28
  const kind = typeof checkpointerConfig.kind === "string" ? checkpointerConfig.kind : "FileCheckpointer";
26
29
  switch (kind) {
27
30
  case "MemorySaver":
@@ -1,6 +1,7 @@
1
1
  import path from "node:path";
2
2
  import { getSkillInheritancePolicy, resolveToolTargets } from "../extensions.js";
3
3
  import { compileModel, compileTool } from "./resource-compilers.js";
4
+ import { inferAgentCapabilities } from "./support/agent-capabilities.js";
4
5
  import { discoverSkillPaths } from "./support/discovery.js";
5
6
  import { compileAgentMemories, getRuntimeDefaults, resolvePromptValue, resolveRefId } from "./support/workspace-ref-utils.js";
6
7
  const WORKSPACE_BOUNDARY_GUIDANCE = "Keep repository and file exploration bounded to the current workspace root unless the user explicitly asks for broader host or filesystem access. " +
@@ -133,21 +134,24 @@ function resolveBackendConfig(agent) {
133
134
  return backendConfig ? { config: backendConfig } : undefined;
134
135
  }
135
136
  function resolveStoreConfig(agent) {
136
- if (agent.executionMode !== "deepagent") {
137
- return undefined;
138
- }
139
137
  const inlineStore = typeof agent.deepAgentConfig?.store === "object" && agent.deepAgentConfig.store
140
138
  ? agent.deepAgentConfig.store
141
- : undefined;
139
+ : typeof agent.langchainAgentConfig?.store === "object" && agent.langchainAgentConfig.store
140
+ ? agent.langchainAgentConfig.store
141
+ : undefined;
142
142
  return inlineStore ? { config: inlineStore } : undefined;
143
143
  }
144
144
  function resolveCheckpointerConfig(agent) {
145
145
  const inlineAgentCheckpointer = typeof agent.deepAgentConfig?.checkpointer === "object" && agent.deepAgentConfig.checkpointer
146
146
  ? agent.deepAgentConfig.checkpointer
147
- : typeof agent.langchainAgentConfig?.checkpointer === "object" && agent.langchainAgentConfig.checkpointer
148
- ? agent.langchainAgentConfig.checkpointer
149
- : undefined;
150
- return inlineAgentCheckpointer ? { config: inlineAgentCheckpointer } : undefined;
147
+ : typeof agent.deepAgentConfig?.checkpointer === "boolean"
148
+ ? agent.deepAgentConfig.checkpointer
149
+ : typeof agent.langchainAgentConfig?.checkpointer === "object" && agent.langchainAgentConfig.checkpointer
150
+ ? agent.langchainAgentConfig.checkpointer
151
+ : typeof agent.langchainAgentConfig?.checkpointer === "boolean"
152
+ ? agent.langchainAgentConfig.checkpointer
153
+ : undefined;
154
+ return inlineAgentCheckpointer !== undefined ? { config: inlineAgentCheckpointer } : undefined;
151
155
  }
152
156
  export function compileBinding(workspaceRoot, agent, agents, referencedSubagentIds, refs, models, tools) {
153
157
  const internalSubagent = referencedSubagentIds.has(agent.id);
@@ -165,10 +169,21 @@ export function compileBinding(workspaceRoot, agent, agents, referencedSubagentI
165
169
  : path.join(workspaceRoot, "run-data");
166
170
  const base = {
167
171
  agent,
172
+ adapter: {
173
+ kind: agent.executionMode,
174
+ config: agent.executionMode === "deepagent"
175
+ ? {
176
+ deepAgent: true,
177
+ }
178
+ : {
179
+ langchainV1: true,
180
+ },
181
+ },
168
182
  harnessRuntime: {
169
183
  runRoot,
170
184
  workspaceRoot,
171
185
  hostFacing: !internalSubagent,
186
+ capabilities: inferAgentCapabilities(agent),
172
187
  ...(checkpointer ? { checkpointer: checkpointer.config } : {}),
173
188
  ...(store ? { store: store.config } : {}),
174
189
  },
@@ -191,37 +206,50 @@ export function compileBinding(workspaceRoot, agent, agents, referencedSubagentI
191
206
  };
192
207
  return {
193
208
  ...base,
209
+ adapter: {
210
+ kind: "langchain-v1",
211
+ config: {
212
+ params: langchainAgentParams,
213
+ },
214
+ },
194
215
  langchainAgentParams,
195
216
  directAgentParams: langchainAgentParams,
196
217
  };
197
218
  }
219
+ const deepAgentParams = {
220
+ model: compiledAgentModel,
221
+ tools: requireTools(tools, agent.toolRefs, agent.id),
222
+ systemPrompt: resolveSystemPrompt(agent),
223
+ responseFormat: agent.deepAgentConfig?.responseFormat,
224
+ contextSchema: agent.deepAgentConfig?.contextSchema,
225
+ middleware: compileMiddlewareConfigs(agent.deepAgentConfig?.middleware, models, agent.id),
226
+ description: agent.description,
227
+ subagents: agent.subagentRefs.map((ref) => {
228
+ const subagent = agents.get(resolveRefId(ref));
229
+ if (!subagent) {
230
+ throw new Error(`Missing subagent ${ref} for agent ${agent.id}`);
231
+ }
232
+ return buildSubagent(subagent, workspaceRoot, models, tools, compiledAgentSkills, compiledAgentModel, compiledAgentMemory);
233
+ }),
234
+ interruptOn: resolveInterruptOn(agent),
235
+ ...(backend ? { backend: backend.config } : {}),
236
+ ...(store ? { store: store.config } : {}),
237
+ name: resolveAgentRuntimeName(agent),
238
+ memory: compiledAgentMemory,
239
+ skills: compiledAgentSkills,
240
+ generalPurposeAgent: typeof agent.deepAgentConfig?.generalPurposeAgent === "boolean" ? agent.deepAgentConfig.generalPurposeAgent : undefined,
241
+ taskDescription: typeof agent.deepAgentConfig?.taskDescription === "string" && agent.deepAgentConfig.taskDescription.trim()
242
+ ? agent.deepAgentConfig.taskDescription
243
+ : undefined,
244
+ };
198
245
  return {
199
246
  ...base,
200
- deepAgentParams: {
201
- model: compiledAgentModel,
202
- tools: requireTools(tools, agent.toolRefs, agent.id),
203
- systemPrompt: resolveSystemPrompt(agent),
204
- responseFormat: agent.deepAgentConfig?.responseFormat,
205
- contextSchema: agent.deepAgentConfig?.contextSchema,
206
- middleware: compileMiddlewareConfigs(agent.deepAgentConfig?.middleware, models, agent.id),
207
- description: agent.description,
208
- subagents: agent.subagentRefs.map((ref) => {
209
- const subagent = agents.get(resolveRefId(ref));
210
- if (!subagent) {
211
- throw new Error(`Missing subagent ${ref} for agent ${agent.id}`);
212
- }
213
- return buildSubagent(subagent, workspaceRoot, models, tools, compiledAgentSkills, compiledAgentModel, compiledAgentMemory);
214
- }),
215
- interruptOn: resolveInterruptOn(agent),
216
- ...(backend ? { backend: backend.config } : {}),
217
- ...(store ? { store: store.config } : {}),
218
- name: resolveAgentRuntimeName(agent),
219
- memory: compiledAgentMemory,
220
- skills: compiledAgentSkills,
221
- generalPurposeAgent: typeof agent.deepAgentConfig?.generalPurposeAgent === "boolean" ? agent.deepAgentConfig.generalPurposeAgent : undefined,
222
- taskDescription: typeof agent.deepAgentConfig?.taskDescription === "string" && agent.deepAgentConfig.taskDescription.trim()
223
- ? agent.deepAgentConfig.taskDescription
224
- : undefined,
247
+ adapter: {
248
+ kind: "deepagent",
249
+ config: {
250
+ params: deepAgentParams,
251
+ },
225
252
  },
253
+ deepAgentParams,
226
254
  };
227
255
  }
@@ -88,6 +88,8 @@ function normalizeNamedResourceSpec(document, kind) {
88
88
  }
89
89
  function normalizeKind(kind) {
90
90
  switch (kind) {
91
+ case "Agent":
92
+ return "agent";
91
93
  case "LangChainAgent":
92
94
  return "langchain-agent";
93
95
  case "DeepAgent":
@@ -209,11 +211,44 @@ function readObjectArray(items) {
209
211
  .map((item) => ({ ...item }));
210
212
  return records.length > 0 ? records : undefined;
211
213
  }
214
+ function readCapabilities(value) {
215
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
216
+ return undefined;
217
+ }
218
+ const typed = value;
219
+ const capabilities = {
220
+ ...(typeof typed.delegation === "boolean" ? { delegation: typed.delegation } : {}),
221
+ ...(typeof typed.memory === "boolean" ? { memory: typed.memory } : {}),
222
+ };
223
+ return Object.keys(capabilities).length > 0 ? capabilities : undefined;
224
+ }
225
+ function readExecutionConfig(value) {
226
+ return typeof value === "object" && value !== null && !Array.isArray(value)
227
+ ? { ...value }
228
+ : undefined;
229
+ }
230
+ function resolveExecutionBackend(item, current) {
231
+ const execution = readExecutionConfig(item.execution) ?? readExecutionConfig(current?.execution);
232
+ const backend = typeof execution?.backend === "string"
233
+ ? execution.backend.trim().toLowerCase()
234
+ : typeof execution?.mode === "string"
235
+ ? execution.mode.trim().toLowerCase()
236
+ : undefined;
237
+ if (backend === "langchain-v1" || backend === "langchain" || backend === "langchain-agent") {
238
+ return "langchain-v1";
239
+ }
240
+ if (backend === "deepagent" || backend === "deepagents") {
241
+ return "deepagent";
242
+ }
243
+ return undefined;
244
+ }
212
245
  function readSharedAgentConfig(item) {
213
246
  const middleware = readMiddlewareArray(item.middleware);
214
247
  return {
215
248
  ...(typeof item.systemPrompt === "string" ? { systemPrompt: item.systemPrompt } : {}),
216
- ...(typeof item.checkpointer === "object" && item.checkpointer ? { checkpointer: item.checkpointer } : {}),
249
+ ...((typeof item.checkpointer === "object" && item.checkpointer) || typeof item.checkpointer === "boolean"
250
+ ? { checkpointer: item.checkpointer }
251
+ : {}),
217
252
  ...(typeof item.interruptOn === "object" && item.interruptOn ? { interruptOn: item.interruptOn } : {}),
218
253
  ...(item.stateSchema !== undefined ? { stateSchema: item.stateSchema } : {}),
219
254
  ...(item.responseFormat !== undefined ? { responseFormat: item.responseFormat } : {}),
@@ -224,7 +259,10 @@ function readSharedAgentConfig(item) {
224
259
  };
225
260
  }
226
261
  function readLangchainAgentConfig(item) {
227
- return readSharedAgentConfig(item);
262
+ return {
263
+ ...readSharedAgentConfig(item),
264
+ ...(typeof item.store === "object" && item.store ? { store: item.store } : {}),
265
+ };
228
266
  }
229
267
  function readDeepAgentConfig(item) {
230
268
  return {
@@ -239,12 +277,16 @@ export function parseAgentItem(item, sourcePath) {
239
277
  const subagentRefs = readRefArray(item.subagents);
240
278
  const subagentPathRefs = readPathArray(item.subagents);
241
279
  const kind = typeof item.kind === "string" ? item.kind : "agent";
242
- const executionMode = String((kind === "langchain-agent" ? "langchain-v1" : undefined) ??
280
+ const executionMode = String(resolveExecutionBackend(item) ??
281
+ (kind === "langchain-agent" ? "langchain-v1" : undefined) ??
243
282
  (kind === "deepagent" ? "deepagent" : undefined) ??
244
283
  "deepagent");
245
284
  return {
246
285
  id: String(item.id),
247
286
  executionMode: executionMode,
287
+ capabilities: readCapabilities(item.capabilities) ?? (executionMode === "deepagent"
288
+ ? { delegation: true, memory: true }
289
+ : { delegation: false, memory: false }),
248
290
  description: String(item.description ?? ""),
249
291
  modelRef: readSingleRef(item.modelRef) ?? "",
250
292
  runRoot: typeof item.runRoot === "string" ? item.runRoot : undefined,
@@ -464,7 +506,7 @@ async function readNamedModelItems(root) {
464
506
  return records;
465
507
  }
466
508
  function isAgentKind(kind) {
467
- return kind === "deepagent" || kind === "langchain-agent";
509
+ return kind === "deepagent" || kind === "langchain-agent" || kind === "agent";
468
510
  }
469
511
  async function readConfigAgentItems(configRoot) {
470
512
  const records = await readYamlItems(configRoot, "agents", { recursive: true });
@@ -512,6 +554,10 @@ export async function readToolModuleItems(root) {
512
554
  return records;
513
555
  }
514
556
  function inferExecutionMode(item, current) {
557
+ const explicitExecution = resolveExecutionBackend(item, current);
558
+ if (explicitExecution) {
559
+ return explicitExecution;
560
+ }
515
561
  const kind = typeof item.kind === "string" ? item.kind : typeof current?.kind === "string" ? current.kind : undefined;
516
562
  if (kind === "langchain-agent") {
517
563
  return "langchain-v1";
@@ -0,0 +1,7 @@
1
+ import type { CompiledAgentBinding, ParsedAgentObject, RuntimeCapabilities } from "../../contracts/types.js";
2
+ export declare function inferAgentCapabilities(agent: ParsedAgentObject): RuntimeCapabilities;
3
+ export declare function inferBindingCapabilities(binding: CompiledAgentBinding): RuntimeCapabilities;
4
+ export declare function isDelegationCapableAgent(agent: ParsedAgentObject): boolean;
5
+ export declare function isMemoryCapableAgent(agent: ParsedAgentObject): boolean;
6
+ export declare function isDelegationCapableBinding(binding: CompiledAgentBinding): boolean;
7
+ export declare function isMemoryCapableBinding(binding: CompiledAgentBinding): boolean;
@@ -0,0 +1,30 @@
1
+ function normalizeCapabilities(capabilities) {
2
+ return {
3
+ delegation: capabilities?.delegation === true,
4
+ memory: capabilities?.memory === true,
5
+ };
6
+ }
7
+ export function inferAgentCapabilities(agent) {
8
+ if (agent.capabilities) {
9
+ return normalizeCapabilities(agent.capabilities);
10
+ }
11
+ return {
12
+ delegation: agent.executionMode === "deepagent",
13
+ memory: agent.executionMode === "deepagent",
14
+ };
15
+ }
16
+ export function inferBindingCapabilities(binding) {
17
+ return normalizeCapabilities(binding.harnessRuntime.capabilities ?? binding.agent.capabilities ?? inferAgentCapabilities(binding.agent));
18
+ }
19
+ export function isDelegationCapableAgent(agent) {
20
+ return inferAgentCapabilities(agent).delegation === true;
21
+ }
22
+ export function isMemoryCapableAgent(agent) {
23
+ return inferAgentCapabilities(agent).memory === true;
24
+ }
25
+ export function isDelegationCapableBinding(binding) {
26
+ return inferBindingCapabilities(binding).delegation === true;
27
+ }
28
+ export function isMemoryCapableBinding(binding) {
29
+ return inferBindingCapabilities(binding).memory === true;
30
+ }
@@ -21,10 +21,14 @@ export type RecoveryConfig = {
21
21
  resumeResumingRunsOnStartup: boolean;
22
22
  maxRecoveryAttempts: number;
23
23
  };
24
+ export type ConcurrencyConfig = {
25
+ maxConcurrentRuns?: number;
26
+ };
24
27
  export declare function getWorkspaceObject(refs: Map<string, WorkspaceObject | ParsedAgentObject>, ref: string | undefined): WorkspaceObject | undefined;
25
28
  export declare function getRuntimeDefaults(refs: Map<string, WorkspaceObject | ParsedAgentObject>): Record<string, unknown> | undefined;
26
29
  export declare function getRuntimeRecoveryConfig(refs: Map<string, WorkspaceObject | ParsedAgentObject>): RuntimeRecoveryConfig;
27
30
  export declare function getRecoveryConfig(refs: Map<string, WorkspaceObject | ParsedAgentObject>): RecoveryConfig;
31
+ export declare function getConcurrencyConfig(refs: Map<string, WorkspaceObject | ParsedAgentObject>): ConcurrencyConfig;
28
32
  export declare function getRoutingSystemPrompt(refs: Map<string, WorkspaceObject | ParsedAgentObject>): string | undefined;
29
33
  export declare function getRoutingDefaultAgentId(refs: Map<string, WorkspaceObject | ParsedAgentObject>): string | undefined;
30
34
  export declare function isModelRoutingEnabled(refs: Map<string, WorkspaceObject | ParsedAgentObject>): boolean;
@@ -47,10 +47,24 @@ export function getRecoveryConfig(refs) {
47
47
  : 3;
48
48
  return {
49
49
  enabled: recovery.enabled !== false,
50
- resumeResumingRunsOnStartup: recovery.resumeResumingRunsOnStartup !== false,
50
+ resumeResumingRunsOnStartup: typeof recovery.resumeResumingRunsOnStartup === "boolean"
51
+ ? recovery.resumeResumingRunsOnStartup
52
+ : recovery.resumeOnStartup !== false,
51
53
  maxRecoveryAttempts,
52
54
  };
53
55
  }
56
+ export function getConcurrencyConfig(refs) {
57
+ const runtimeDefaults = getRuntimeDefaults(refs);
58
+ const concurrency = typeof runtimeDefaults?.concurrency === "object" && runtimeDefaults.concurrency
59
+ ? runtimeDefaults.concurrency
60
+ : {};
61
+ const maxConcurrentRuns = typeof concurrency.maxConcurrentRuns === "number" &&
62
+ Number.isFinite(concurrency.maxConcurrentRuns) &&
63
+ concurrency.maxConcurrentRuns > 0
64
+ ? Math.floor(concurrency.maxConcurrentRuns)
65
+ : undefined;
66
+ return { maxConcurrentRuns };
67
+ }
54
68
  export function getRoutingSystemPrompt(refs) {
55
69
  const routing = getRoutingObject(refs);
56
70
  return typeof routing?.systemPrompt === "string" && routing.systemPrompt.trim() ? routing.systemPrompt : undefined;
@@ -1,10 +1,16 @@
1
+ import { isDelegationCapableAgent, isMemoryCapableAgent } from "./support/agent-capabilities.js";
1
2
  const allowedExecutionModes = new Set(["deepagent", "langchain-v1"]);
2
3
  function hasPromptContent(value) {
3
4
  return typeof value === "string" && value.trim().length > 0;
4
5
  }
5
6
  function validateCheckpointerConfig(agent) {
6
7
  const checkpointer = (typeof agent.deepAgentConfig?.checkpointer === "object" && agent.deepAgentConfig.checkpointer) ||
8
+ (typeof agent.deepAgentConfig?.checkpointer === "boolean" ? agent.deepAgentConfig.checkpointer : undefined) ||
9
+ (typeof agent.langchainAgentConfig?.checkpointer === "boolean" ? agent.langchainAgentConfig.checkpointer : undefined) ||
7
10
  (typeof agent.langchainAgentConfig?.checkpointer === "object" && agent.langchainAgentConfig.checkpointer);
11
+ if (typeof checkpointer === "boolean") {
12
+ return;
13
+ }
8
14
  if (!checkpointer) {
9
15
  return;
10
16
  }
@@ -47,14 +53,14 @@ export function validateAgent(agent) {
47
53
  if (!agent.description.trim()) {
48
54
  throw new Error(`Agent ${agent.id} description must not be empty`);
49
55
  }
50
- if (agent.executionMode === "langchain-v1" && (agent.subagentRefs.length > 0 || agent.subagentPathRefs.length > 0)) {
51
- throw new Error(`Agent ${agent.id} cannot define subagents unless execution.mode is deepagent`);
56
+ if (!isDelegationCapableAgent(agent) && (agent.subagentRefs.length > 0 || agent.subagentPathRefs.length > 0)) {
57
+ throw new Error(`Agent ${agent.id} cannot define subagents unless it uses a delegation-capable backend`);
52
58
  }
53
- if (agent.executionMode === "langchain-v1" && agent.memorySources.length > 0) {
54
- throw new Error(`Agent ${agent.id} cannot define memory unless execution.mode is deepagent`);
59
+ if (!isMemoryCapableAgent(agent) && agent.memorySources.length > 0) {
60
+ throw new Error(`Agent ${agent.id} cannot define memory unless it uses a memory-capable backend`);
55
61
  }
56
- if ((agent.subagentRefs.length > 0 || agent.subagentPathRefs.length > 0) && agent.executionMode !== "deepagent") {
57
- throw new Error(`Agent ${agent.id} must use deepagent execution when subagents are defined`);
62
+ if ((agent.subagentRefs.length > 0 || agent.subagentPathRefs.length > 0) && !isDelegationCapableAgent(agent)) {
63
+ throw new Error(`Agent ${agent.id} must use a delegation-capable backend when subagents are defined`);
58
64
  }
59
65
  validateCheckpointerConfig(agent);
60
66
  validateMiddlewareConfig(agent);
@@ -78,8 +84,8 @@ export function validateTopology(agents) {
78
84
  if (!referencedSubagentIds.has(agent.id)) {
79
85
  continue;
80
86
  }
81
- if (agent.executionMode !== "deepagent") {
82
- throw new Error(`Subagent ${agent.id} must use deepagent execution`);
87
+ if (!isDelegationCapableAgent(agent)) {
88
+ throw new Error(`Subagent ${agent.id} must use a delegation-capable backend`);
83
89
  }
84
90
  if (!hasPromptContent(agent.deepAgentConfig?.systemPrompt)) {
85
91
  throw new Error(`Subagent ${agent.id} requires deepagent.systemPrompt`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@botbotgo/agent-harness",
3
- "version": "0.0.41",
3
+ "version": "0.0.43",
4
4
  "description": "Workspace runtime for multi-agent applications",
5
5
  "type": "module",
6
6
  "packageManager": "npm@10.9.2",
@@ -50,7 +50,7 @@
50
50
  "scripts": {
51
51
  "build": "rm -rf dist tsconfig.tsbuildinfo && tsc -p tsconfig.json && cp -R config dist/",
52
52
  "check": "tsc -p tsconfig.json --noEmit",
53
- "test": "vitest run test/public-api.test.ts test/resource-optional-provider.test.ts test/resource-isolation.test.ts test/stock-research-app-load-harness.test.ts test/stock-research-app-run.test.ts test/release-workflow.test.ts test/release-version.test.ts test/gitignore.test.ts test/package-lock.test.ts test/readme.test.ts test/runtime-adapter-regressions.test.ts test/runtime-recovery.test.ts test/tool-extension-gaps.test.ts test/checkpoint-maintenance.test.ts test/llamaindex-dependency-compat.test.ts test/skill-standard.test.ts test/routing-config.test.ts test/workspace-compat-regressions.test.ts",
53
+ "test": "vitest run test/public-api.test.ts test/resource-optional-provider.test.ts test/resource-isolation.test.ts test/stock-research-app-load-harness.test.ts test/stock-research-app-run.test.ts test/release-workflow.test.ts test/release-version.test.ts test/gitignore.test.ts test/package-lock.test.ts test/readme.test.ts test/runtime-adapter-regressions.test.ts test/runtime-recovery.test.ts test/tool-extension-gaps.test.ts test/checkpoint-maintenance.test.ts test/llamaindex-dependency-compat.test.ts test/skill-standard.test.ts test/routing-config.test.ts test/workspace-compat-regressions.test.ts test/upstream-compat-regressions.test.ts",
54
54
  "test:real-providers": "vitest run test/real-provider-harness.test.ts",
55
55
  "release:prepare": "npm version patch --no-git-tag-version && node ./scripts/sync-example-version.mjs",
56
56
  "release:pack": "npm pack --dry-run",