@botbotgo/agent-harness 0.0.52 → 0.0.53

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.
package/README.md CHANGED
@@ -258,6 +258,8 @@ There are three configuration layers:
258
258
  - reusable object catalogs in `config/*.yaml`
259
259
  - agent assembly in `config/agents/*.yaml`
260
260
 
261
+ The repository-owned default config layer is intentionally full-shaped. The shipped YAML keeps explicit default values for the main runtime knobs so teams can start from concrete config instead of reconstructing adapter defaults from code.
262
+
261
263
  ### `config/workspace.yaml`
262
264
 
263
265
  Use this file for runtime-level policy shared by the whole workspace.
@@ -324,6 +326,10 @@ spec:
324
326
  - kind: Checkpointer
325
327
  name: default
326
328
  checkpointerKind: MemorySaver
329
+ - kind: Checkpointer
330
+ name: sqlite
331
+ checkpointerKind: SqliteSaver
332
+ path: checkpoints.sqlite
327
333
  ```
328
334
 
329
335
  Built-in store kinds today:
@@ -352,6 +358,8 @@ spec:
352
358
  backendKind: CompositeBackend
353
359
  state:
354
360
  kind: VfsSandbox
361
+ rootDir: .
362
+ virtualMode: true
355
363
  timeout: 600
356
364
  routes:
357
365
  /memories/:
@@ -397,12 +405,23 @@ kind: Agent
397
405
  metadata:
398
406
  name: direct
399
407
  spec:
408
+ runtime:
409
+ runRoot: ./.agent
400
410
  execution:
401
411
  backend: langchain-v1
402
412
  modelRef: model/default
413
+ tools: []
414
+ skills: []
415
+ memory: []
416
+ subagents: []
417
+ mcpServers: []
403
418
  config:
404
419
  checkpointer:
405
420
  ref: checkpointer/default
421
+ store:
422
+ ref: store/default
423
+ interruptOn: {}
424
+ middleware: []
406
425
  systemPrompt: Answer simple requests directly.
407
426
  ```
408
427
 
@@ -414,11 +433,17 @@ kind: Agent
414
433
  metadata:
415
434
  name: orchestra
416
435
  spec:
436
+ runtime:
437
+ runRoot: ./.agent
417
438
  execution:
418
439
  backend: deepagent
419
440
  modelRef: model/default
420
441
  memory:
421
442
  - path: config/agent-context.md
443
+ tools: []
444
+ skills: []
445
+ subagents: []
446
+ mcpServers: []
422
447
  config:
423
448
  store:
424
449
  ref: store/default
@@ -426,6 +451,10 @@ spec:
426
451
  ref: checkpointer/default
427
452
  backend:
428
453
  ref: backend/default
454
+ interruptOn: {}
455
+ middleware: []
456
+ generalPurposeAgent: true
457
+ taskDescription: Complete delegated sidecar work and return concise results.
429
458
  ```
430
459
 
431
460
  Client-configurable agent fields include:
@@ -457,6 +486,8 @@ Client-configurable agent fields include:
457
486
 
458
487
  For backend-specific agent options, prefer passing the upstream concept directly inside `spec.execution.config`. The loader keeps a small stable product shape, but it also preserves adapter-facing passthrough fields so new LangChain v1 or DeepAgents parameters can flow into adapters without expanding the public API surface.
459
488
 
489
+ Upstream feature coverage is tracked in [docs/upstream-feature-matrix.md](docs/upstream-feature-matrix.md).
490
+
460
491
  ### `resources/`
461
492
 
462
493
  Use `resources/` for executable local extensions:
@@ -477,6 +508,7 @@ SKILL packages are discovered from `resources/skills/` and attached to agents th
477
508
  - upstream LangChain v1 and DeepAgents concepts should be expressed as directly as possible in YAML
478
509
  - recovery, approvals, threads, runs, and events are runtime concepts, not backend-specific escape hatches
479
510
  - backend implementation details should stay internal unless product requirements force exposure
511
+ - new LangChain v1 or DeepAgents public config should land in YAML passthrough and compatibility tests before adding new public runtime APIs
480
512
 
481
513
  In short: `agent-harness` is a public runtime contract generic enough to survive backend changes, while the deep execution semantics stay upstream.
482
514
 
@@ -495,5 +527,3 @@ Primary exports:
495
527
  - `createToolMcpServer`
496
528
  - `serveToolsOverStdio`
497
529
  - `stop`
498
-
499
- `AgentHarnessRuntime` is the concrete runtime class behind the public facade.
@@ -9,7 +9,10 @@ metadata:
9
9
  # agent-harness feature: human-readable summary for inventory and UI.
10
10
  description: Manual low-latency host for direct answers and lightweight inventory lookup. Do not use it as the default executor for tool-heavy or specialist-shaped tasks.
11
11
  spec:
12
- runtime: {}
12
+ runtime:
13
+ # agent-harness feature: host-level run data placement override.
14
+ # This matches the workspace default and keeps the default config shape explicit.
15
+ runRoot: ./.agent
13
16
  # =====================
14
17
  # Runtime Agent Features
15
18
  # =====================
@@ -20,6 +23,16 @@ spec:
20
23
  # This should point at a cheap, fast, general-purpose chat model, because `direct` is intended
21
24
  # to be the low-latency path for simple requests.
22
25
  modelRef: model/default
26
+ # Upstream execution feature: direct host starts with no attached explicit tools by default.
27
+ tools: []
28
+ # Upstream execution feature: direct host starts with no attached local skill packages by default.
29
+ skills: []
30
+ # Upstream execution feature: direct host does not bootstrap project memory by default.
31
+ memory: []
32
+ # Upstream execution feature: direct host does not predeclare specialist subagents by default.
33
+ subagents: []
34
+ # Upstream execution feature: direct host does not attach MCP servers by default.
35
+ mcpServers: []
23
36
  config:
24
37
  # Runtime execution feature: checkpointer config passed into the selected backend adapter.
25
38
  # Even the lightweight direct path can benefit from resumable state during interactive use.
@@ -27,6 +40,14 @@ spec:
27
40
  # `path` is only used by `FileCheckpointer` and `SqliteSaver`; omit it for `MemorySaver`.
28
41
  checkpointer:
29
42
  ref: checkpointer/default
43
+ # Upstream execution feature: LangGraph store available to middleware and runtime context hooks.
44
+ # The default direct host keeps this enabled so middleware can use the same durable store surface as other hosts.
45
+ store:
46
+ ref: store/default
47
+ # Upstream execution feature: no declarative HITL tool routing by default.
48
+ interruptOn: {}
49
+ # Upstream execution feature: no extra declarative middleware beyond the runtime's automatic injections.
50
+ middleware: []
30
51
  # Upstream execution feature: system prompt for the lightweight direct-response host.
31
52
  # This prompt should keep the agent focused on:
32
53
  # - answering simple requests in one turn
@@ -9,7 +9,10 @@ metadata:
9
9
  # agent-harness feature: human-readable summary for inventory and UI.
10
10
  description: Default execution host. Answer directly when possible, use local tools and skills first, and delegate only when a specialist is a better fit. Not a reflex delegation-only planner.
11
11
  spec:
12
- runtime: {}
12
+ runtime:
13
+ # agent-harness feature: host-level run data placement override.
14
+ # This matches the workspace default and keeps the default config shape explicit.
15
+ runRoot: ./.agent
13
16
  # =====================
14
17
  # Runtime Agent Features
15
18
  # =====================
@@ -29,6 +32,14 @@ spec:
29
32
  # Updating these files changes future agent constructions, but they are still bootstrap inputs rather than
30
33
  # self-updating runtime memory.
31
34
  - path: config/agent-context.md
35
+ # Upstream execution feature: top-level host starts with no extra direct tool refs beyond discovered workspace tools.
36
+ tools: []
37
+ # Upstream execution feature: top-level host starts with no explicit skill package refs in the default workspace.
38
+ skills: []
39
+ # Upstream execution feature: specialist topology is empty in the repository default and can be filled in YAML.
40
+ subagents: []
41
+ # Upstream execution feature: host-level MCP servers are opt-in and empty by default.
42
+ mcpServers: []
32
43
  config:
33
44
  # Runtime execution feature: checkpointer config passed into the selected backend adapter.
34
45
  # This persists resumable graph state for this agent.
@@ -52,6 +63,14 @@ spec:
52
63
  # The harness injects the resolved store/checkpointer instances, but the backend topology itself stays upstream-shaped.
53
64
  backend:
54
65
  ref: backend/default
66
+ # Upstream execution feature: no extra declarative HITL rules by default.
67
+ interruptOn: {}
68
+ # Upstream execution feature: no extra declarative middleware beyond upstream deepagents defaults by default.
69
+ middleware: []
70
+ # Upstream execution feature: keep the built-in general-purpose subagent enabled.
71
+ generalPurposeAgent: true
72
+ # Upstream execution feature: bias the general-purpose subagent toward bounded sidecar work and context isolation.
73
+ taskDescription: Complete delegated sidecar work and return concise results without bloating the parent context.
55
74
  # Upstream execution feature: system prompt for the orchestration host.
56
75
  # This becomes the top-level instruction block for the selected execution backend and should hold the
57
76
  # agent's durable role, priorities, and behavioral guardrails rather than bulky project facts.
@@ -10,6 +10,8 @@ spec:
10
10
  backendKind: CompositeBackend
11
11
  state:
12
12
  kind: VfsSandbox
13
+ rootDir: .
14
+ virtualMode: true
13
15
  timeout: 600
14
16
  routes:
15
17
  /memories/:
@@ -15,3 +15,10 @@ spec:
15
15
  name: default
16
16
  description: Default in-memory checkpointer preset for lightweight local development.
17
17
  checkpointerKind: MemorySaver
18
+
19
+ # agent-harness feature: reusable sqlite checkpointer preset for longer-lived local runs.
20
+ - kind: Checkpointer
21
+ name: sqlite
22
+ description: Default sqlite-backed checkpointer preset for durable local graph state and maintenance sweeps.
23
+ checkpointerKind: SqliteSaver
24
+ path: checkpoints.sqlite
@@ -196,7 +196,6 @@ export type CompiledAgentBinding = {
196
196
  config: Record<string, unknown>;
197
197
  };
198
198
  langchainAgentParams?: LangChainAgentParams;
199
- directAgentParams?: LangChainAgentParams;
200
199
  deepAgentParams?: DeepAgentParams;
201
200
  harnessRuntime: {
202
201
  runRoot: string;
@@ -1 +1 @@
1
- export declare const AGENT_HARNESS_VERSION = "0.0.51";
1
+ export declare const AGENT_HARNESS_VERSION = "0.0.52";
@@ -1 +1 @@
1
- export const AGENT_HARNESS_VERSION = "0.0.51";
1
+ export const AGENT_HARNESS_VERSION = "0.0.52";
@@ -1,4 +1,12 @@
1
1
  import type { CompiledAgentBinding, CompiledModel } from "../contracts/types.js";
2
+ type MiddlewareConfig = Record<string, unknown>;
3
+ type MiddlewareFactoryContext = {
4
+ config: MiddlewareConfig;
5
+ resolveModel: (model: CompiledModel) => Promise<unknown>;
6
+ };
7
+ type MiddlewareFactory = (context: MiddlewareFactoryContext) => Promise<unknown | unknown[] | null | undefined>;
8
+ export declare const DECLARATIVE_MIDDLEWARE_REGISTRY: Record<string, MiddlewareFactory>;
9
+ export declare const SUPPORTED_DECLARATIVE_MIDDLEWARE_KINDS: readonly string[];
2
10
  export declare function resolveDeclaredMiddleware(middlewareConfigs: unknown[] | undefined, options: {
3
11
  resolveModel: (model: CompiledModel) => Promise<unknown>;
4
12
  resolveCustom?: (input: {
@@ -9,3 +17,4 @@ export declare function resolveDeclaredMiddleware(middlewareConfigs: unknown[] |
9
17
  }) => unknown | unknown[] | null | undefined | Promise<unknown | unknown[] | null | undefined>;
10
18
  binding?: CompiledAgentBinding;
11
19
  }): Promise<unknown[]>;
20
+ export {};
@@ -25,6 +25,52 @@ async function resolveReferencedModelList(values, options) {
25
25
  }
26
26
  return Promise.all(values.map((value) => resolveReferencedModel(value, options)));
27
27
  }
28
+ async function createSummarizationMiddleware({ config, resolveModel }) {
29
+ const runtimeConfig = { ...config };
30
+ runtimeConfig.model = await resolveReferencedModel(runtimeConfig.model, { resolveModel });
31
+ if (runtimeConfig.model === undefined) {
32
+ throw new Error("summarization middleware requires model or modelRef");
33
+ }
34
+ return summarizationMiddleware(runtimeConfig);
35
+ }
36
+ async function createLlmToolSelectorMiddleware({ config, resolveModel }) {
37
+ const runtimeConfig = { ...config };
38
+ runtimeConfig.model = await resolveReferencedModel(runtimeConfig.model, { resolveModel });
39
+ return llmToolSelectorMiddleware(runtimeConfig);
40
+ }
41
+ async function createModelFallbackDeclarativeMiddleware({ config, resolveModel }) {
42
+ const fallbackModels = (await resolveReferencedModelList(config.fallbackModels, { resolveModel })) ??
43
+ (await resolveReferencedModelList(config.models, { resolveModel }));
44
+ if (!fallbackModels || fallbackModels.length === 0) {
45
+ throw new Error("modelFallback middleware requires fallbackModels or models");
46
+ }
47
+ return modelFallbackMiddleware(...fallbackModels);
48
+ }
49
+ async function createPiiDeclarativeMiddleware({ config }) {
50
+ const piiType = typeof config.piiType === "string" ? config.piiType : undefined;
51
+ if (!piiType) {
52
+ throw new Error("pii middleware requires piiType");
53
+ }
54
+ const { piiType: _piiType, ...piiOptions } = config;
55
+ return piiMiddleware(piiType, piiOptions);
56
+ }
57
+ export const DECLARATIVE_MIDDLEWARE_REGISTRY = {
58
+ summarization: createSummarizationMiddleware,
59
+ llmToolSelector: createLlmToolSelectorMiddleware,
60
+ modelRetry: async ({ config }) => modelRetryMiddleware(config),
61
+ modelFallback: createModelFallbackDeclarativeMiddleware,
62
+ toolRetry: async ({ config }) => toolRetryMiddleware(config),
63
+ toolCallLimit: async ({ config }) => toolCallLimitMiddleware(config),
64
+ modelCallLimit: async ({ config }) => modelCallLimitMiddleware(config),
65
+ todoList: async ({ config }) => todoListMiddleware(config),
66
+ contextEditing: async ({ config }) => contextEditingMiddleware(config),
67
+ toolEmulator: async ({ config }) => toolEmulatorMiddleware(config),
68
+ humanInTheLoop: async ({ config }) => humanInTheLoopMiddleware(config),
69
+ openAIModeration: async ({ config }) => openAIModerationMiddleware(config),
70
+ pii: createPiiDeclarativeMiddleware,
71
+ anthropicPromptCaching: async ({ config }) => anthropicPromptCachingMiddleware(config),
72
+ };
73
+ export const SUPPORTED_DECLARATIVE_MIDDLEWARE_KINDS = Object.freeze(Object.keys(DECLARATIVE_MIDDLEWARE_REGISTRY).sort());
28
74
  export async function resolveDeclaredMiddleware(middlewareConfigs, options) {
29
75
  const resolved = [];
30
76
  for (const rawConfig of middlewareConfigs ?? []) {
@@ -34,87 +80,38 @@ export async function resolveDeclaredMiddleware(middlewareConfigs, options) {
34
80
  }
35
81
  const kind = requireKind(config);
36
82
  const runtimeConfig = omitKind(config);
37
- switch (kind) {
38
- case "summarization": {
39
- runtimeConfig.model = await resolveReferencedModel(runtimeConfig.model, options);
40
- if (runtimeConfig.model === undefined) {
41
- throw new Error("summarization middleware requires model or modelRef");
42
- }
43
- resolved.push(summarizationMiddleware(runtimeConfig));
44
- break;
83
+ const registeredFactory = DECLARATIVE_MIDDLEWARE_REGISTRY[kind];
84
+ if (registeredFactory) {
85
+ const resolvedBuiltin = await registeredFactory({
86
+ config: runtimeConfig,
87
+ resolveModel: options.resolveModel,
88
+ });
89
+ if (Array.isArray(resolvedBuiltin)) {
90
+ resolved.push(...resolvedBuiltin);
91
+ continue;
45
92
  }
46
- case "llmToolSelector":
47
- runtimeConfig.model = await resolveReferencedModel(runtimeConfig.model, options);
48
- resolved.push(llmToolSelectorMiddleware(runtimeConfig));
49
- break;
50
- case "modelRetry":
51
- resolved.push(modelRetryMiddleware(runtimeConfig));
52
- break;
53
- case "modelFallback": {
54
- const fallbackModels = (await resolveReferencedModelList(runtimeConfig.fallbackModels, options)) ??
55
- (await resolveReferencedModelList(runtimeConfig.models, options));
56
- if (!fallbackModels || fallbackModels.length === 0) {
57
- throw new Error("modelFallback middleware requires fallbackModels or models");
58
- }
59
- resolved.push(modelFallbackMiddleware(...fallbackModels));
60
- break;
61
- }
62
- case "toolRetry":
63
- resolved.push(toolRetryMiddleware(runtimeConfig));
64
- break;
65
- case "toolCallLimit":
66
- resolved.push(toolCallLimitMiddleware(runtimeConfig));
67
- break;
68
- case "modelCallLimit":
69
- resolved.push(modelCallLimitMiddleware(runtimeConfig));
70
- break;
71
- case "todoList":
72
- resolved.push(todoListMiddleware(runtimeConfig));
73
- break;
74
- case "contextEditing":
75
- resolved.push(contextEditingMiddleware(runtimeConfig));
76
- break;
77
- case "toolEmulator":
78
- resolved.push(toolEmulatorMiddleware(runtimeConfig));
79
- break;
80
- case "humanInTheLoop":
81
- resolved.push(humanInTheLoopMiddleware(runtimeConfig));
82
- break;
83
- case "openAIModeration":
84
- resolved.push(openAIModerationMiddleware(runtimeConfig));
85
- break;
86
- case "pii": {
87
- const piiType = typeof runtimeConfig.piiType === "string" ? runtimeConfig.piiType : undefined;
88
- if (!piiType) {
89
- throw new Error("pii middleware requires piiType");
90
- }
91
- const { piiType: _piiType, ...piiOptions } = runtimeConfig;
92
- resolved.push(piiMiddleware(piiType, piiOptions));
93
- break;
94
- }
95
- case "anthropicPromptCaching":
96
- resolved.push(anthropicPromptCachingMiddleware(runtimeConfig));
97
- break;
98
- default: {
99
- const customResolved = options.resolveCustom
100
- ? await options.resolveCustom({
101
- kind,
102
- config: runtimeConfig,
103
- binding: options.binding,
104
- resolveModel: options.resolveModel,
105
- })
106
- : undefined;
107
- if (Array.isArray(customResolved)) {
108
- resolved.push(...customResolved);
109
- break;
110
- }
111
- if (customResolved) {
112
- resolved.push(customResolved);
113
- break;
114
- }
115
- throw new Error(`Unsupported declarative middleware kind ${kind}`);
93
+ if (resolvedBuiltin) {
94
+ resolved.push(resolvedBuiltin);
95
+ continue;
116
96
  }
117
97
  }
98
+ const customResolved = options.resolveCustom
99
+ ? await options.resolveCustom({
100
+ kind,
101
+ config: runtimeConfig,
102
+ binding: options.binding,
103
+ resolveModel: options.resolveModel,
104
+ })
105
+ : undefined;
106
+ if (Array.isArray(customResolved)) {
107
+ resolved.push(...customResolved);
108
+ continue;
109
+ }
110
+ if (customResolved) {
111
+ resolved.push(customResolved);
112
+ continue;
113
+ }
114
+ throw new Error(`Unsupported declarative middleware kind ${kind}`);
118
115
  }
119
116
  return resolved;
120
117
  }
@@ -294,7 +294,6 @@ export function compileBinding(workspaceRoot, agent, agents, referencedSubagentI
294
294
  },
295
295
  },
296
296
  langchainAgentParams,
297
- directAgentParams: langchainAgentParams,
298
297
  };
299
298
  }
300
299
  const deepAgentParams = {
@@ -242,11 +242,12 @@ function readPassthroughConfig(item, consumedKeys) {
242
242
  }
243
243
  function resolveExecutionBackend(item, current) {
244
244
  const execution = readExecutionConfig(item.execution) ?? readExecutionConfig(current?.execution);
245
+ if (typeof execution?.mode === "string" && execution.mode.trim().length > 0) {
246
+ throw new Error("Agent execution.mode is no longer supported; use execution.backend");
247
+ }
245
248
  const backend = typeof execution?.backend === "string"
246
249
  ? execution.backend.trim().toLowerCase()
247
- : typeof execution?.mode === "string"
248
- ? execution.mode.trim().toLowerCase()
249
- : undefined;
250
+ : undefined;
250
251
  if (backend === "langchain-v1") {
251
252
  return "langchain-v1";
252
253
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@botbotgo/agent-harness",
3
- "version": "0.0.52",
3
+ "version": "0.0.53",
4
4
  "description": "Workspace runtime for multi-agent applications",
5
5
  "type": "module",
6
6
  "packageManager": "npm@10.9.2",