@botbotgo/agent-harness 0.0.266 → 0.0.268

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
@@ -752,6 +752,7 @@ Important fields:
752
752
  - `concurrency.maxConcurrentRuns`
753
753
  - `routing.defaultAgentId`
754
754
  - `routing.rules`
755
+ - `toolModuleDiscovery.scope`
755
756
  - `maintenance.checkpoints`
756
757
  - `maintenance.records`
757
758
  - `recovery.enabled`
@@ -765,6 +766,11 @@ Important fields:
765
766
  - `maintenance.checkpoints` trims backend checkpoint state used for resume/recovery
766
767
  - `maintenance.records` trims harness-owned terminal session/request records stored in `runtime.sqlite`
767
768
 
769
+ `toolModuleDiscovery.scope` controls how local `resources/tools/`-style module discovery walks tool directories:
770
+
771
+ - `recursive` is the default and keeps scanning nested folders
772
+ - `top-level` limits module discovery to files directly under each tool root while leaving YAML catalogs recursive
773
+
768
774
  When libSQL reports an error against harness runtime persistence, the message is prefixed with the absolute path to `runtime.sqlite`. For constraint-class failures (or whenever you set `AGENT_HARNESS_RUNTIME_SQLITE_DEBUG=1`), the message also includes a truncated copy of the failing SQL so you can tell harness persistence apart from other SQLite databases in the same process.
769
775
 
770
776
  Example:
@@ -776,6 +782,8 @@ metadata:
776
782
  name: default
777
783
  spec:
778
784
  runRoot: ./.agent
785
+ toolModuleDiscovery:
786
+ scope: top-level
779
787
  concurrency:
780
788
  maxConcurrentRuns: 3
781
789
  routing:
package/README.zh.md CHANGED
@@ -724,6 +724,11 @@ await stop(runtime);
724
724
  - `maintenance.checkpoints` 清理后端用于 resume/recovery 的 checkpoint 状态
725
725
  - `maintenance.records` 清理 harness 自己保存在 `runtime.sqlite` 中、已结束的 session/request 记录
726
726
 
727
+ `toolModuleDiscovery.scope` 用来控制本地 `resources/tools/` 这类 tool module 目录的发现范围:
728
+
729
+ - `recursive` 是默认值,会继续扫描嵌套目录
730
+ - `top-level` 只发现每个工具根目录下一层文件,同时保留 YAML catalog 的递归发现
731
+
727
732
  当 libSQL 对 harness 运行时持久化报错时,错误信息会带上 `runtime.sqlite` 的绝对路径。对约束类错误(或设置环境变量 `AGENT_HARNESS_RUNTIME_SQLITE_DEBUG=1` 时),信息中还会包含被截断的失败 SQL,便于在同一进程里区分 harness 与其它 SQLite 数据库。
728
733
 
729
734
  示例:
@@ -735,6 +740,8 @@ metadata:
735
740
  name: default
736
741
  spec:
737
742
  runRoot: ./.agent
743
+ toolModuleDiscovery:
744
+ scope: top-level
738
745
  concurrency:
739
746
  maxConcurrentRuns: 3
740
747
  routing:
@@ -474,6 +474,26 @@ export type RunOptions = RunStartOptions | RunDecisionOptions;
474
474
  export type HarnessStreamItem = {
475
475
  type: "event";
476
476
  event: HarnessEvent;
477
+ } | {
478
+ type: "content";
479
+ threadId: string;
480
+ runId: string;
481
+ agentId: string;
482
+ content: string;
483
+ } | {
484
+ type: "content-blocks";
485
+ threadId: string;
486
+ runId: string;
487
+ agentId: string;
488
+ contentBlocks: unknown[];
489
+ } | {
490
+ type: "tool-result";
491
+ threadId: string;
492
+ runId: string;
493
+ agentId: string;
494
+ toolName: string;
495
+ output: unknown;
496
+ isError?: boolean;
477
497
  } | {
478
498
  type: "upstream-event";
479
499
  threadId: string;
@@ -1 +1 @@
1
- export declare const AGENT_HARNESS_VERSION = "0.0.265";
1
+ export declare const AGENT_HARNESS_VERSION = "0.0.267";
@@ -1 +1 @@
1
- export const AGENT_HARNESS_VERSION = "0.0.265";
1
+ export const AGENT_HARNESS_VERSION = "0.0.267";
@@ -175,6 +175,17 @@ export async function* streamHarnessRun(options) {
175
175
  }
176
176
  emitted = true;
177
177
  assistantOutput += normalizedChunk.content;
178
+ yield {
179
+ type: "event",
180
+ event: await options.emit(options.threadId, options.runId, 3, "output.delta", { content: normalizedChunk.content }),
181
+ };
182
+ yield {
183
+ type: "content",
184
+ threadId: options.threadId,
185
+ runId: options.runId,
186
+ agentId: currentAgentId,
187
+ content: normalizedChunk.content,
188
+ };
178
189
  }
179
190
  if (!assistantOutput && toolErrors.length > 0) {
180
191
  assistantOutput = toolErrors.join("\n\n");
@@ -1687,9 +1687,7 @@ export class AgentHarnessRuntime {
1687
1687
  emitSyntheticFallback: (threadId, runId, selectedAgentId, error) => this.runtimeEventOperations.emitSyntheticFallback(threadId, runId, selectedAgentId, error),
1688
1688
  });
1689
1689
  for await (const item of stream) {
1690
- if (item.type === "event" || item.type === "upstream-event" || item.type === "result") {
1691
- yield item;
1692
- }
1690
+ yield item;
1693
1691
  }
1694
1692
  }
1695
1693
  async resume(options) {
@@ -10,7 +10,7 @@ import { validateAgent, validateTopology } from "./validate.js";
10
10
  import { compileBinding } from "./agent-binding-compiler.js";
11
11
  import { discoverSubagents, ensureDiscoverySources } from "./support/discovery.js";
12
12
  import { collectAgentDiscoverySourceRefs, collectToolSourceRefs } from "./support/source-collectors.js";
13
- import { getRoutingDefaultAgentId, getRuntimeResources, getRoutingRules, resolveRefId, } from "./support/workspace-ref-utils.js";
13
+ import { getRoutingDefaultAgentId, getRuntimeResources, getToolModuleDiscoveryConfig, getRoutingRules, resolveRefId, } from "./support/workspace-ref-utils.js";
14
14
  import { hydrateAgentMcpTools, hydrateResourceAndExternalTools } from "./tool-hydration.js";
15
15
  import { traceStartupStage } from "../runtime/startup-tracing.js";
16
16
  import { shouldSkipScanDirectory } from "../utils/fs.js";
@@ -123,7 +123,7 @@ async function resolveConfiguredResources(entries, workspaceRoot) {
123
123
  }
124
124
  return resolved;
125
125
  }
126
- async function registerAttachedResourceTools(tools, resourceRoot) {
126
+ async function registerAttachedResourceTools(tools, resourceRoot, toolModuleDiscoveryScope) {
127
127
  const toolsRoot = path.join(resourceRoot, "tools");
128
128
  for (const { item, sourcePath } of await readYamlItems(toolsRoot, undefined, { recursive: true })) {
129
129
  const parsed = parseToolObject({
@@ -134,7 +134,7 @@ async function registerAttachedResourceTools(tools, resourceRoot) {
134
134
  });
135
135
  tools.set(parsed.id, parsed);
136
136
  }
137
- for (const { item, sourcePath } of await readToolModuleItems(toolsRoot)) {
137
+ for (const { item, sourcePath } of await readToolModuleItems(toolsRoot, { scope: toolModuleDiscoveryScope })) {
138
138
  const parsed = parseToolObject({
139
139
  id: String(item.id),
140
140
  kind: "tool",
@@ -221,6 +221,7 @@ export async function loadWorkspace(workspaceRoot, options = {}) {
221
221
  loaded.refs.set(`agent/${agent.id}`, agent);
222
222
  }
223
223
  const { embeddings, mcpServers, models, vectorStores, tools } = collectParsedResources(loaded.refs);
224
+ const toolModuleDiscoveryConfig = getToolModuleDiscoveryConfig(loaded.refs);
224
225
  await traceStartupStage("workspace.hydrate.agentMcpTools", () => hydrateAgentMcpTools(loaded.agents, mcpServers, tools), {
225
226
  workspaceRoot,
226
227
  agentCount: loaded.agents.length,
@@ -234,7 +235,7 @@ export async function loadWorkspace(workspaceRoot, options = {}) {
234
235
  configuredResourceCount: configuredResources.length,
235
236
  });
236
237
  for (const resource of resolvedConfiguredResources) {
237
- await traceStartupStage("workspace.register.attachedResourceTools", () => registerAttachedResourceTools(tools, resource.root), {
238
+ await traceStartupStage("workspace.register.attachedResourceTools", () => registerAttachedResourceTools(tools, resource.root, toolModuleDiscoveryConfig.scope), {
238
239
  workspaceRoot,
239
240
  resourceRoot: resource.root,
240
241
  });
@@ -259,7 +260,7 @@ export async function loadWorkspace(workspaceRoot, options = {}) {
259
260
  workspaceRoot,
260
261
  externalResourceCount: externalResources.length,
261
262
  });
262
- await traceStartupStage("workspace.hydrate.externalTools", () => hydrateResourceAndExternalTools(tools, externalResources, workspaceRoot), {
263
+ await traceStartupStage("workspace.hydrate.externalTools", () => hydrateResourceAndExternalTools(tools, externalResources, workspaceRoot, toolModuleDiscoveryConfig.scope), {
263
264
  workspaceRoot,
264
265
  externalResourceCount: externalResources.length,
265
266
  });
@@ -1,4 +1,5 @@
1
1
  import type { ParsedAgentObject, WorkspaceLoadOptions, WorkspaceObject } from "../contracts/types.js";
2
+ import type { ToolModuleDiscoveryScope } from "./support/workspace-ref-utils.js";
2
3
  export { normalizeYamlItem, readYamlItems } from "./yaml-object-reader.js";
3
4
  type RefMap = Map<string, WorkspaceObject | ParsedAgentObject>;
4
5
  export type WorkspaceObjects = {
@@ -7,7 +8,9 @@ export type WorkspaceObjects = {
7
8
  };
8
9
  export declare function conventionalPackageRoots(root: string, relativeDir: "tools" | "skills"): string[];
9
10
  export declare function parseAgentItem(item: Record<string, unknown>, sourcePath: string): ParsedAgentObject;
10
- export declare function readToolModuleItems(root: string): Promise<Array<{
11
+ export declare function readToolModuleItems(root: string, options?: {
12
+ scope?: ToolModuleDiscoveryScope;
13
+ }): Promise<Array<{
11
14
  item: Record<string, unknown>;
12
15
  sourcePath: string;
13
16
  }>>;
@@ -613,7 +613,14 @@ async function loadModuleAgentsForRoot(root, mergedAgents) {
613
613
  }
614
614
  }
615
615
  }
616
- async function loadConventionalObjectsForRoot(root, mergedObjects) {
616
+ function getMergedToolModuleDiscoveryScope(mergedObjects) {
617
+ const runtimeDefaults = mergedObjects.get("runtime/default")?.item;
618
+ const toolModuleDiscovery = typeof runtimeDefaults?.toolModuleDiscovery === "object" && runtimeDefaults.toolModuleDiscovery
619
+ ? runtimeDefaults.toolModuleDiscovery
620
+ : {};
621
+ return toolModuleDiscovery.scope === "top-level" ? "top-level" : "recursive";
622
+ }
623
+ async function loadConventionalObjectsForRoot(root, mergedObjects, toolModuleDiscoveryScope) {
617
624
  for (const directory of CONVENTIONAL_OBJECT_DIRECTORIES) {
618
625
  for (const objectRoot of conventionalDirectoryRoots(root, directory)) {
619
626
  for (const { item, sourcePath } of await readYamlItemsIgnoringNodeModules(objectRoot)) {
@@ -623,7 +630,7 @@ async function loadConventionalObjectsForRoot(root, mergedObjects) {
623
630
  }
624
631
  mergeWorkspaceObjectRecord(mergedObjects, workspaceObject, item, sourcePath);
625
632
  }
626
- for (const { item, sourcePath } of await readToolModuleItems(objectRoot)) {
633
+ for (const { item, sourcePath } of await readToolModuleItems(objectRoot, { scope: toolModuleDiscoveryScope })) {
627
634
  const workspaceObject = parseWorkspaceObject(item, sourcePath);
628
635
  if (!workspaceObject) {
629
636
  continue;
@@ -785,10 +792,11 @@ async function loadRootObjects(root, mergedObjects) {
785
792
  function isAgentKind(kind) {
786
793
  return kind === "agent";
787
794
  }
788
- export async function readToolModuleItems(root) {
795
+ export async function readToolModuleItems(root, options = {}) {
789
796
  if (!(await fileExists(root))) {
790
797
  return [];
791
798
  }
799
+ const scope = options.scope === "top-level" ? "top-level" : "recursive";
792
800
  const files = [];
793
801
  const pending = [root];
794
802
  while (pending.length > 0) {
@@ -798,7 +806,7 @@ export async function readToolModuleItems(root) {
798
806
  const entryPath = path.join(current, entry.name);
799
807
  const entryType = await resolveScanEntryType(entryPath, entry);
800
808
  if (entryType === "directory") {
801
- if (shouldSkipScanDirectory(entry.name)) {
809
+ if (shouldSkipScanDirectory(entry.name) || scope === "top-level") {
802
810
  continue;
803
811
  }
804
812
  pending.push(entryPath);
@@ -886,7 +894,7 @@ export async function loadWorkspaceObjects(workspaceRoot, options = {}) {
886
894
  const configRoot = conventionalConfigRoot(root) ?? root;
887
895
  await loadConfigYamlForRoot(root, configRoot, mergedAgents, mergedObjects);
888
896
  await loadModuleAgentsForRoot(root, mergedAgents);
889
- await loadConventionalObjectsForRoot(root, mergedObjects);
897
+ await loadConventionalObjectsForRoot(root, mergedObjects, getMergedToolModuleDiscoveryScope(mergedObjects));
890
898
  await loadModuleObjectsForRoot(root, mergedObjects);
891
899
  await loadRootObjects(root, mergedObjects);
892
900
  }
@@ -31,9 +31,14 @@ export type ProviderRetryConfig = {
31
31
  export type ResilienceConfig = {
32
32
  providerRetries: ProviderRetryConfig;
33
33
  };
34
+ export type ToolModuleDiscoveryScope = "recursive" | "top-level";
35
+ export type ToolModuleDiscoveryConfig = {
36
+ scope: ToolModuleDiscoveryScope;
37
+ };
34
38
  export declare function getWorkspaceObject(refs: Map<string, WorkspaceObject | ParsedAgentObject>, ref: string | undefined): WorkspaceObject | undefined;
35
39
  export declare function getRuntimeDefaults(refs: Map<string, WorkspaceObject | ParsedAgentObject>): Record<string, unknown> | undefined;
36
40
  export declare function getRuntimeResources(refs: Map<string, WorkspaceObject | ParsedAgentObject>): string[];
41
+ export declare function getToolModuleDiscoveryConfig(refs: Map<string, WorkspaceObject | ParsedAgentObject>): ToolModuleDiscoveryConfig;
37
42
  export declare function getRuntimeMemoryDefaults(refs: Map<string, WorkspaceObject | ParsedAgentObject>): Record<string, unknown> | undefined;
38
43
  export declare function getRecoveryConfig(refs: Map<string, WorkspaceObject | ParsedAgentObject>): RecoveryConfig;
39
44
  export declare function getConcurrencyConfig(refs: Map<string, WorkspaceObject | ParsedAgentObject>): ConcurrencyConfig;
@@ -35,6 +35,15 @@ export function getRuntimeResources(refs) {
35
35
  .filter((value) => typeof value === "string" && value.trim().length > 0)
36
36
  .map((value) => value.trim());
37
37
  }
38
+ export function getToolModuleDiscoveryConfig(refs) {
39
+ const runtimeDefaults = getRuntimeDefaults(refs);
40
+ const toolModuleDiscovery = typeof runtimeDefaults?.toolModuleDiscovery === "object" && runtimeDefaults.toolModuleDiscovery
41
+ ? runtimeDefaults.toolModuleDiscovery
42
+ : {};
43
+ return {
44
+ scope: toolModuleDiscovery.scope === "top-level" ? "top-level" : "recursive",
45
+ };
46
+ }
38
47
  export function getRuntimeMemoryDefaults(refs) {
39
48
  const runtimeMemories = Array.from(refs.values()).filter((object) => !("executionMode" in object) && object.kind === "runtime-memory");
40
49
  if (runtimeMemories.length === 0) {
@@ -1,3 +1,4 @@
1
1
  import type { ParsedAgentObject, ParsedMcpServerObject, ParsedToolObject } from "../contracts/types.js";
2
- export declare function hydrateResourceAndExternalTools(tools: Map<string, ParsedToolObject>, toolSourceRefs: string[], workspaceRoot: string): Promise<void>;
2
+ import type { ToolModuleDiscoveryScope } from "./support/workspace-ref-utils.js";
3
+ export declare function hydrateResourceAndExternalTools(tools: Map<string, ParsedToolObject>, toolSourceRefs: string[], workspaceRoot: string, toolModuleDiscoveryScope?: ToolModuleDiscoveryScope): Promise<void>;
3
4
  export declare function hydrateAgentMcpTools(agents: ParsedAgentObject[], mcpServers: Map<string, ParsedMcpServerObject>, tools: Map<string, ParsedToolObject>): Promise<void>;
@@ -144,11 +144,11 @@ function mergeReferencedMcpServer(referencedServer, item, agentId, name, sourceP
144
144
  sourcePath,
145
145
  };
146
146
  }
147
- async function hydrateExternalToolSource(tools, source, workspaceRoot) {
147
+ async function hydrateExternalToolSource(tools, source, workspaceRoot, toolModuleDiscoveryScope) {
148
148
  const externalRoot = await ensureExternalResourceSource(source, workspaceRoot);
149
149
  const discoveredToolRefs = [];
150
150
  const sourcePrefix = `external.${createHash("sha256").update(source).digest("hex").slice(0, 12)}`;
151
- for (const { item, sourcePath } of await readToolModuleItems(path.join(externalRoot, "tools"))) {
151
+ for (const { item, sourcePath } of await readToolModuleItems(path.join(externalRoot, "tools"), { scope: toolModuleDiscoveryScope })) {
152
152
  const toolId = typeof item.id === "string" ? item.id : undefined;
153
153
  if (!toolId) {
154
154
  continue;
@@ -179,10 +179,10 @@ async function hydrateExternalToolSource(tools, source, workspaceRoot) {
179
179
  });
180
180
  }
181
181
  }
182
- export async function hydrateResourceAndExternalTools(tools, toolSourceRefs, workspaceRoot) {
182
+ export async function hydrateResourceAndExternalTools(tools, toolSourceRefs, workspaceRoot, toolModuleDiscoveryScope = "recursive") {
183
183
  for (const source of toolSourceRefs) {
184
184
  if (isExternalSourceLocator(source)) {
185
- await hydrateExternalToolSource(tools, source, workspaceRoot);
185
+ await hydrateExternalToolSource(tools, source, workspaceRoot, toolModuleDiscoveryScope);
186
186
  }
187
187
  }
188
188
  for (const resourceTool of await listResourceTools(toolSourceRefs, workspaceRoot)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@botbotgo/agent-harness",
3
- "version": "0.0.266",
3
+ "version": "0.0.268",
4
4
  "description": "Workspace runtime for multi-agent applications",
5
5
  "license": "MIT",
6
6
  "type": "module",