@botbotgo/agent-harness 0.0.42 → 0.0.44

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.
@@ -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,8 +1,9 @@
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
- import { compileAgentMemories, getRuntimeDefaults, resolvePromptValue, resolveRefId } from "./support/workspace-ref-utils.js";
6
+ import { compileAgentMemories, getRuntimeDefaults, getWorkspaceObject, 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. " +
7
8
  "Do not inspect absolute paths outside the workspace, system directories, or unrelated repos by default. " +
8
9
  "Prefer workspace-local tools, relative paths, and the current repository checkout when analyzing code.";
@@ -132,15 +133,50 @@ function resolveBackendConfig(agent) {
132
133
  : undefined;
133
134
  return backendConfig ? { config: backendConfig } : undefined;
134
135
  }
135
- function resolveStoreConfig(agent) {
136
+ function isRefConfig(value) {
137
+ if (typeof value?.ref !== "string" || value.ref.trim().length === 0) {
138
+ return false;
139
+ }
140
+ return Object.keys(value).every((key) => key === "ref");
141
+ }
142
+ function materializeWorkspaceObjectConfig(refs, ref, allowedKinds, ownerLabel) {
143
+ const object = getWorkspaceObject(refs, ref);
144
+ if (!object) {
145
+ throw new Error(`${ownerLabel} references missing object ${ref}`);
146
+ }
147
+ if (!allowedKinds.includes(object.kind)) {
148
+ throw new Error(`${ownerLabel} references ${ref}, but expected one of: ${allowedKinds.join(", ")}`);
149
+ }
150
+ const { id: _id, kind: _kind, ...config } = object.value;
151
+ if (object.kind === "store") {
152
+ const storeKind = typeof config.storeKind === "string" ? config.storeKind : undefined;
153
+ const { storeKind: _storeKind, ...rest } = config;
154
+ return storeKind ? { kind: storeKind, ...rest } : config;
155
+ }
156
+ if (object.kind === "checkpointer" || object.kind === "file-checkpointer" || object.kind === "sqlite-saver") {
157
+ const checkpointerKind = typeof config.checkpointerKind === "string" ? config.checkpointerKind : undefined;
158
+ const { checkpointerKind: _checkpointerKind, ...rest } = config;
159
+ return checkpointerKind ? { kind: checkpointerKind, ...rest } : config;
160
+ }
161
+ return config;
162
+ }
163
+ function resolveStoreConfig(agent, refs) {
136
164
  const inlineStore = typeof agent.deepAgentConfig?.store === "object" && agent.deepAgentConfig.store
137
165
  ? agent.deepAgentConfig.store
138
166
  : typeof agent.langchainAgentConfig?.store === "object" && agent.langchainAgentConfig.store
139
167
  ? agent.langchainAgentConfig.store
140
168
  : undefined;
141
- return inlineStore ? { config: inlineStore } : undefined;
169
+ if (!inlineStore) {
170
+ return undefined;
171
+ }
172
+ if (isRefConfig(inlineStore)) {
173
+ return {
174
+ config: materializeWorkspaceObjectConfig(refs, inlineStore.ref, ["store"], `Agent ${agent.id} store`),
175
+ };
176
+ }
177
+ return { config: inlineStore };
142
178
  }
143
- function resolveCheckpointerConfig(agent) {
179
+ function resolveCheckpointerConfig(agent, refs) {
144
180
  const inlineAgentCheckpointer = typeof agent.deepAgentConfig?.checkpointer === "object" && agent.deepAgentConfig.checkpointer
145
181
  ? agent.deepAgentConfig.checkpointer
146
182
  : typeof agent.deepAgentConfig?.checkpointer === "boolean"
@@ -150,7 +186,18 @@ function resolveCheckpointerConfig(agent) {
150
186
  : typeof agent.langchainAgentConfig?.checkpointer === "boolean"
151
187
  ? agent.langchainAgentConfig.checkpointer
152
188
  : undefined;
153
- return inlineAgentCheckpointer !== undefined ? { config: inlineAgentCheckpointer } : undefined;
189
+ if (inlineAgentCheckpointer === undefined) {
190
+ return undefined;
191
+ }
192
+ if (typeof inlineAgentCheckpointer === "boolean") {
193
+ return { config: inlineAgentCheckpointer };
194
+ }
195
+ if (isRefConfig(inlineAgentCheckpointer)) {
196
+ return {
197
+ config: materializeWorkspaceObjectConfig(refs, inlineAgentCheckpointer.ref, ["checkpointer", "file-checkpointer", "sqlite-saver"], `Agent ${agent.id} checkpointer`),
198
+ };
199
+ }
200
+ return { config: inlineAgentCheckpointer };
154
201
  }
155
202
  export function compileBinding(workspaceRoot, agent, agents, referencedSubagentIds, refs, models, tools) {
156
203
  const internalSubagent = referencedSubagentIds.has(agent.id);
@@ -159,8 +206,8 @@ export function compileBinding(workspaceRoot, agent, agents, referencedSubagentI
159
206
  const compiledAgentMemory = compileAgentMemories(workspaceRoot, agent.memorySources);
160
207
  const compiledAgentModel = requireModel(models, agent.modelRef || (internalSubagent ? "model/default" : ""), agent.id);
161
208
  const backend = resolveBackendConfig(agent);
162
- const store = resolveStoreConfig(agent);
163
- const checkpointer = resolveCheckpointerConfig(agent);
209
+ const store = resolveStoreConfig(agent, refs);
210
+ const checkpointer = resolveCheckpointerConfig(agent, refs);
164
211
  const runRoot = typeof agent.runRoot === "string" && agent.runRoot.trim().length > 0
165
212
  ? path.resolve(workspaceRoot, agent.runRoot)
166
213
  : typeof runtimeDefaults?.runRoot === "string" && runtimeDefaults.runRoot.trim().length > 0
@@ -168,10 +215,21 @@ export function compileBinding(workspaceRoot, agent, agents, referencedSubagentI
168
215
  : path.join(workspaceRoot, "run-data");
169
216
  const base = {
170
217
  agent,
218
+ adapter: {
219
+ kind: agent.executionMode,
220
+ config: agent.executionMode === "deepagent"
221
+ ? {
222
+ deepAgent: true,
223
+ }
224
+ : {
225
+ langchainV1: true,
226
+ },
227
+ },
171
228
  harnessRuntime: {
172
229
  runRoot,
173
230
  workspaceRoot,
174
231
  hostFacing: !internalSubagent,
232
+ capabilities: inferAgentCapabilities(agent),
175
233
  ...(checkpointer ? { checkpointer: checkpointer.config } : {}),
176
234
  ...(store ? { store: store.config } : {}),
177
235
  },
@@ -194,37 +252,50 @@ export function compileBinding(workspaceRoot, agent, agents, referencedSubagentI
194
252
  };
195
253
  return {
196
254
  ...base,
255
+ adapter: {
256
+ kind: "langchain-v1",
257
+ config: {
258
+ params: langchainAgentParams,
259
+ },
260
+ },
197
261
  langchainAgentParams,
198
262
  directAgentParams: langchainAgentParams,
199
263
  };
200
264
  }
265
+ const deepAgentParams = {
266
+ model: compiledAgentModel,
267
+ tools: requireTools(tools, agent.toolRefs, agent.id),
268
+ systemPrompt: resolveSystemPrompt(agent),
269
+ responseFormat: agent.deepAgentConfig?.responseFormat,
270
+ contextSchema: agent.deepAgentConfig?.contextSchema,
271
+ middleware: compileMiddlewareConfigs(agent.deepAgentConfig?.middleware, models, agent.id),
272
+ description: agent.description,
273
+ subagents: agent.subagentRefs.map((ref) => {
274
+ const subagent = agents.get(resolveRefId(ref));
275
+ if (!subagent) {
276
+ throw new Error(`Missing subagent ${ref} for agent ${agent.id}`);
277
+ }
278
+ return buildSubagent(subagent, workspaceRoot, models, tools, compiledAgentSkills, compiledAgentModel, compiledAgentMemory);
279
+ }),
280
+ interruptOn: resolveInterruptOn(agent),
281
+ ...(backend ? { backend: backend.config } : {}),
282
+ ...(store ? { store: store.config } : {}),
283
+ name: resolveAgentRuntimeName(agent),
284
+ memory: compiledAgentMemory,
285
+ skills: compiledAgentSkills,
286
+ generalPurposeAgent: typeof agent.deepAgentConfig?.generalPurposeAgent === "boolean" ? agent.deepAgentConfig.generalPurposeAgent : undefined,
287
+ taskDescription: typeof agent.deepAgentConfig?.taskDescription === "string" && agent.deepAgentConfig.taskDescription.trim()
288
+ ? agent.deepAgentConfig.taskDescription
289
+ : undefined,
290
+ };
201
291
  return {
202
292
  ...base,
203
- deepAgentParams: {
204
- model: compiledAgentModel,
205
- tools: requireTools(tools, agent.toolRefs, agent.id),
206
- systemPrompt: resolveSystemPrompt(agent),
207
- responseFormat: agent.deepAgentConfig?.responseFormat,
208
- contextSchema: agent.deepAgentConfig?.contextSchema,
209
- middleware: compileMiddlewareConfigs(agent.deepAgentConfig?.middleware, models, agent.id),
210
- description: agent.description,
211
- subagents: agent.subagentRefs.map((ref) => {
212
- const subagent = agents.get(resolveRefId(ref));
213
- if (!subagent) {
214
- throw new Error(`Missing subagent ${ref} for agent ${agent.id}`);
215
- }
216
- return buildSubagent(subagent, workspaceRoot, models, tools, compiledAgentSkills, compiledAgentModel, compiledAgentMemory);
217
- }),
218
- interruptOn: resolveInterruptOn(agent),
219
- ...(backend ? { backend: backend.config } : {}),
220
- ...(store ? { store: store.config } : {}),
221
- name: resolveAgentRuntimeName(agent),
222
- memory: compiledAgentMemory,
223
- skills: compiledAgentSkills,
224
- generalPurposeAgent: typeof agent.deepAgentConfig?.generalPurposeAgent === "boolean" ? agent.deepAgentConfig.generalPurposeAgent : undefined,
225
- taskDescription: typeof agent.deepAgentConfig?.taskDescription === "string" && agent.deepAgentConfig.taskDescription.trim()
226
- ? agent.deepAgentConfig.taskDescription
227
- : undefined,
293
+ adapter: {
294
+ kind: "deepagent",
295
+ config: {
296
+ params: deepAgentParams,
297
+ },
228
298
  },
299
+ deepAgentParams,
229
300
  };
230
301
  }
@@ -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,6 +211,37 @@ 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 {
@@ -244,12 +277,16 @@ export function parseAgentItem(item, sourcePath) {
244
277
  const subagentRefs = readRefArray(item.subagents);
245
278
  const subagentPathRefs = readPathArray(item.subagents);
246
279
  const kind = typeof item.kind === "string" ? item.kind : "agent";
247
- const executionMode = String((kind === "langchain-agent" ? "langchain-v1" : undefined) ??
280
+ const executionMode = String(resolveExecutionBackend(item) ??
281
+ (kind === "langchain-agent" ? "langchain-v1" : undefined) ??
248
282
  (kind === "deepagent" ? "deepagent" : undefined) ??
249
283
  "deepagent");
250
284
  return {
251
285
  id: String(item.id),
252
286
  executionMode: executionMode,
287
+ capabilities: readCapabilities(item.capabilities) ?? (executionMode === "deepagent"
288
+ ? { delegation: true, memory: true }
289
+ : { delegation: false, memory: false }),
253
290
  description: String(item.description ?? ""),
254
291
  modelRef: readSingleRef(item.modelRef) ?? "",
255
292
  runRoot: typeof item.runRoot === "string" ? item.runRoot : undefined,
@@ -460,16 +497,26 @@ async function readNamedModelItems(root) {
460
497
  }
461
498
  const parsedDocuments = parseAllDocuments(await readYamlOrJson(filePath));
462
499
  for (const parsedDocument of parsedDocuments) {
463
- const items = normalizeNamedResourceSpec(parsedDocument.toJSON(), "model");
464
- for (const item of items) {
465
- records.push({ item: normalizeYamlItem(item), sourcePath: filePath });
500
+ const document = parsedDocument.toJSON();
501
+ const catalogItems = normalizeNamedResourceSpec(document, "model");
502
+ if (catalogItems.length > 0) {
503
+ for (const item of catalogItems) {
504
+ records.push({ item: normalizeYamlItem(item), sourcePath: filePath });
505
+ }
506
+ continue;
507
+ }
508
+ for (const item of await objectItemsFromDocument(document, filePath)) {
509
+ const normalized = normalizeYamlItem(item);
510
+ if (normalized.kind === "model" && typeof normalized.id === "string" && normalized.id.trim()) {
511
+ records.push({ item: normalized, sourcePath: filePath });
512
+ }
466
513
  }
467
514
  }
468
515
  }
469
516
  return records;
470
517
  }
471
518
  function isAgentKind(kind) {
472
- return kind === "deepagent" || kind === "langchain-agent";
519
+ return kind === "deepagent" || kind === "langchain-agent" || kind === "agent";
473
520
  }
474
521
  async function readConfigAgentItems(configRoot) {
475
522
  const records = await readYamlItems(configRoot, "agents", { recursive: true });
@@ -517,6 +564,10 @@ export async function readToolModuleItems(root) {
517
564
  return records;
518
565
  }
519
566
  function inferExecutionMode(item, current) {
567
+ const explicitExecution = resolveExecutionBackend(item, current);
568
+ if (explicitExecution) {
569
+ return explicitExecution;
570
+ }
520
571
  const kind = typeof item.kind === "string" ? item.kind : typeof current?.kind === "string" ? current.kind : undefined;
521
572
  if (kind === "langchain-agent") {
522
573
  return "langchain-v1";
@@ -0,0 +1,9 @@
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;
8
+ export declare function getAgentSystemPrompt(agent: ParsedAgentObject): string | undefined;
9
+ export declare function hasAgentSystemPrompt(agent: ParsedAgentObject): boolean;
@@ -0,0 +1,41 @@
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
+ }
31
+ export function getAgentSystemPrompt(agent) {
32
+ const deepagentPrompt = typeof agent.deepAgentConfig?.systemPrompt === "string" ? agent.deepAgentConfig.systemPrompt.trim() : "";
33
+ if (deepagentPrompt) {
34
+ return deepagentPrompt;
35
+ }
36
+ const langchainPrompt = typeof agent.langchainAgentConfig?.systemPrompt === "string" ? agent.langchainAgentConfig.systemPrompt.trim() : "";
37
+ return langchainPrompt || undefined;
38
+ }
39
+ export function hasAgentSystemPrompt(agent) {
40
+ return typeof getAgentSystemPrompt(agent) === "string";
41
+ }
@@ -1,7 +1,5 @@
1
+ import { hasAgentSystemPrompt, isDelegationCapableAgent, isMemoryCapableAgent, } from "./support/agent-capabilities.js";
1
2
  const allowedExecutionModes = new Set(["deepagent", "langchain-v1"]);
2
- function hasPromptContent(value) {
3
- return typeof value === "string" && value.trim().length > 0;
4
- }
5
3
  function validateCheckpointerConfig(agent) {
6
4
  const checkpointer = (typeof agent.deepAgentConfig?.checkpointer === "object" && agent.deepAgentConfig.checkpointer) ||
7
5
  (typeof agent.deepAgentConfig?.checkpointer === "boolean" ? agent.deepAgentConfig.checkpointer : undefined) ||
@@ -52,14 +50,14 @@ export function validateAgent(agent) {
52
50
  if (!agent.description.trim()) {
53
51
  throw new Error(`Agent ${agent.id} description must not be empty`);
54
52
  }
55
- if (agent.executionMode === "langchain-v1" && (agent.subagentRefs.length > 0 || agent.subagentPathRefs.length > 0)) {
56
- throw new Error(`Agent ${agent.id} cannot define subagents unless execution.mode is deepagent`);
53
+ if (!isDelegationCapableAgent(agent) && (agent.subagentRefs.length > 0 || agent.subagentPathRefs.length > 0)) {
54
+ throw new Error(`Agent ${agent.id} cannot define subagents unless it uses a delegation-capable backend`);
57
55
  }
58
- if (agent.executionMode === "langchain-v1" && agent.memorySources.length > 0) {
59
- throw new Error(`Agent ${agent.id} cannot define memory unless execution.mode is deepagent`);
56
+ if (!isMemoryCapableAgent(agent) && agent.memorySources.length > 0) {
57
+ throw new Error(`Agent ${agent.id} cannot define memory unless it uses a memory-capable backend`);
60
58
  }
61
- if ((agent.subagentRefs.length > 0 || agent.subagentPathRefs.length > 0) && agent.executionMode !== "deepagent") {
62
- throw new Error(`Agent ${agent.id} must use deepagent execution when subagents are defined`);
59
+ if ((agent.subagentRefs.length > 0 || agent.subagentPathRefs.length > 0) && !isDelegationCapableAgent(agent)) {
60
+ throw new Error(`Agent ${agent.id} must use a delegation-capable backend when subagents are defined`);
63
61
  }
64
62
  validateCheckpointerConfig(agent);
65
63
  validateMiddlewareConfig(agent);
@@ -83,11 +81,11 @@ export function validateTopology(agents) {
83
81
  if (!referencedSubagentIds.has(agent.id)) {
84
82
  continue;
85
83
  }
86
- if (agent.executionMode !== "deepagent") {
87
- throw new Error(`Subagent ${agent.id} must use deepagent execution`);
84
+ if (!isDelegationCapableAgent(agent)) {
85
+ throw new Error(`Subagent ${agent.id} must use a delegation-capable backend`);
88
86
  }
89
- if (!hasPromptContent(agent.deepAgentConfig?.systemPrompt)) {
90
- throw new Error(`Subagent ${agent.id} requires deepagent.systemPrompt`);
87
+ if (!hasAgentSystemPrompt(agent)) {
88
+ throw new Error(`Subagent ${agent.id} requires systemPrompt`);
91
89
  }
92
90
  }
93
91
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@botbotgo/agent-harness",
3
- "version": "0.0.42",
3
+ "version": "0.0.44",
4
4
  "description": "Workspace runtime for multi-agent applications",
5
5
  "type": "module",
6
6
  "packageManager": "npm@10.9.2",
@@ -26,6 +26,11 @@
26
26
  "types": "./dist/tools.d.ts",
27
27
  "import": "./dist/tools.js",
28
28
  "default": "./dist/tools.js"
29
+ },
30
+ "./presentation": {
31
+ "types": "./dist/presentation.d.ts",
32
+ "import": "./dist/presentation.js",
33
+ "default": "./dist/presentation.js"
29
34
  }
30
35
  },
31
36
  "dependencies": {
@@ -50,8 +55,9 @@
50
55
  "scripts": {
51
56
  "build": "rm -rf dist tsconfig.tsbuildinfo && tsc -p tsconfig.json && cp -R config dist/",
52
57
  "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 test/upstream-compat-regressions.test.ts",
58
+ "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 test/embedded-browser-bookmarks.test.ts test/presentation-wallee.test.ts",
54
59
  "test:real-providers": "vitest run test/real-provider-harness.test.ts",
60
+ "test:integration": "npm run build && node scripts/integration-wallee-browser.mjs",
55
61
  "release:prepare": "npm version patch --no-git-tag-version && node ./scripts/sync-example-version.mjs",
56
62
  "release:pack": "npm pack --dry-run",
57
63
  "release:publish": "npm publish --access public --registry https://registry.npmjs.org/"