@botbotgo/agent-harness 0.0.38 → 0.0.40

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.
@@ -4,11 +4,14 @@ import { defaultResourceConfigRoot, defaultResourceSkillsRoot } from "../../reso
4
4
  import { validateSkillMetadata } from "../../runtime/support/skill-metadata.js";
5
5
  import { ensureExternalResourceSource, isDirectoryPath, isExternalSourceLocator, resolveExternalResourcePath, resolveResourcePackageRoot, } from "../../resource/sources.js";
6
6
  import { parseAgentItem, readYamlItems } from "../object-loader.js";
7
- function resolveBuiltinPath(kind, ref) {
8
- const normalized = ref.replace(/^builtin:\/\//, "").replace(/^\/+/, "");
7
+ function isBundledResourceRef(ref) {
8
+ return ref.startsWith("resource://") || ref.startsWith("builtin://");
9
+ }
10
+ function resolveBundledResourcePath(kind, ref) {
11
+ const normalized = ref.replace(/^(resource|builtin):\/\//, "").replace(/^\/+/, "");
9
12
  if (kind === "skills") {
10
13
  if (!normalized.startsWith("skills")) {
11
- throw new Error(`Unsupported builtin skill discovery path ${ref}`);
14
+ throw new Error(`Unsupported resource skill discovery path ${ref}`);
12
15
  }
13
16
  const suffix = normalized.replace(/^skills\/?/, "");
14
17
  const root = defaultResourceSkillsRoot();
@@ -28,7 +31,7 @@ function resolveBuiltinPath(kind, ref) {
28
31
  return candidate;
29
32
  }
30
33
  if (!normalized.startsWith("agents")) {
31
- throw new Error(`Unsupported builtin subagent discovery path ${ref}`);
34
+ throw new Error(`Unsupported resource subagent discovery path ${ref}`);
32
35
  }
33
36
  const suffix = normalized.replace(/^agents\/?/, "");
34
37
  const root = path.join(defaultResourceConfigRoot(), "agents");
@@ -69,8 +72,8 @@ export async function ensureDiscoverySources(locators, workspaceRoot) {
69
72
  .map((locator) => ensureExternalResourceSource(locator, workspaceRoot)));
70
73
  }
71
74
  export function resolveDiscoveryRoot(ref, workspaceRoot, kind) {
72
- if (ref.startsWith("builtin://")) {
73
- return resolveBuiltinPath(kind, ref);
75
+ if (isBundledResourceRef(ref)) {
76
+ return resolveBundledResourcePath(kind, ref);
74
77
  }
75
78
  if (isExternalSourceLocator(ref)) {
76
79
  return preferPackageConvention(resolveExternalResourcePath(ref, workspaceRoot), kind);
@@ -78,8 +81,8 @@ export function resolveDiscoveryRoot(ref, workspaceRoot, kind) {
78
81
  return preferPackageConvention(path.resolve(workspaceRoot, ref), kind);
79
82
  }
80
83
  async function resolveDiscoveryRootAsync(ref, workspaceRoot, kind) {
81
- if (ref.startsWith("builtin://")) {
82
- return resolveBuiltinPath(kind, ref);
84
+ if (isBundledResourceRef(ref)) {
85
+ return resolveBundledResourcePath(kind, ref);
83
86
  }
84
87
  if (isExternalSourceLocator(ref)) {
85
88
  return preferPackageConvention(await ensureExternalResourceSource(ref, workspaceRoot), kind);
@@ -1,6 +1,6 @@
1
1
  import { isExternalSourceLocator } from "../../resource/sources.js";
2
2
  export function collectToolSourceRefs(tools, agents, options) {
3
- const refs = new Set(options.resourceSources ?? options.builtinSources ?? []);
3
+ const refs = new Set(options.resourceSources ?? []);
4
4
  for (const tool of tools.values()) {
5
5
  for (const ref of tool.bundleRefs) {
6
6
  if (isExternalSourceLocator(ref)) {
@@ -1,7 +1,26 @@
1
1
  import type { ParsedAgentObject, WorkspaceObject } from "../../contracts/types.js";
2
+ export type RoutingRule = {
3
+ agentId: string;
4
+ equals?: string[];
5
+ startsWith?: string[];
6
+ contains?: string[];
7
+ regex?: string[];
8
+ minLength?: number;
9
+ maxLength?: number;
10
+ minLines?: number;
11
+ maxLines?: number;
12
+ hasThreadId?: boolean;
13
+ caseSensitive?: boolean;
14
+ };
2
15
  export declare function getWorkspaceObject(refs: Map<string, WorkspaceObject | ParsedAgentObject>, ref: string | undefined): WorkspaceObject | undefined;
3
16
  export declare function getRuntimeDefaults(refs: Map<string, WorkspaceObject | ParsedAgentObject>): Record<string, unknown> | undefined;
4
17
  export declare function getRoutingSystemPrompt(refs: Map<string, WorkspaceObject | ParsedAgentObject>): string | undefined;
18
+ export declare function getRoutingDefaultAgentId(refs: Map<string, WorkspaceObject | ParsedAgentObject>): string | undefined;
19
+ export declare function isModelRoutingEnabled(refs: Map<string, WorkspaceObject | ParsedAgentObject>): boolean;
20
+ export declare function getRoutingRules(refs: Map<string, WorkspaceObject | ParsedAgentObject>): RoutingRule[];
21
+ export declare function matchRoutingRule(input: string, rule: RoutingRule, options?: {
22
+ threadId?: string;
23
+ }): boolean;
5
24
  export declare function resolvePromptValue(promptConfig: unknown): string | undefined;
6
25
  export declare function resolveRefId(ref: string): string;
7
26
  export declare function resolvePathList(refs: Map<string, WorkspaceObject | ParsedAgentObject>, workspaceRoot: string, refList: string[]): string[];
@@ -1,4 +1,10 @@
1
1
  import path from "node:path";
2
+ function getRoutingObject(refs) {
3
+ const runtimeDefaults = getRuntimeDefaults(refs);
4
+ return typeof runtimeDefaults?.routing === "object" && runtimeDefaults.routing
5
+ ? runtimeDefaults.routing
6
+ : undefined;
7
+ }
2
8
  export function getWorkspaceObject(refs, ref) {
3
9
  if (!ref) {
4
10
  return undefined;
@@ -20,12 +26,108 @@ export function getRuntimeDefaults(refs) {
20
26
  return runtimes[0].value;
21
27
  }
22
28
  export function getRoutingSystemPrompt(refs) {
23
- const runtimeDefaults = getRuntimeDefaults(refs);
24
- const routing = typeof runtimeDefaults?.routing === "object" && runtimeDefaults.routing
25
- ? runtimeDefaults.routing
26
- : undefined;
29
+ const routing = getRoutingObject(refs);
27
30
  return typeof routing?.systemPrompt === "string" && routing.systemPrompt.trim() ? routing.systemPrompt : undefined;
28
31
  }
32
+ export function getRoutingDefaultAgentId(refs) {
33
+ const routing = getRoutingObject(refs);
34
+ return typeof routing?.defaultAgentId === "string" && routing.defaultAgentId.trim()
35
+ ? routing.defaultAgentId.trim()
36
+ : undefined;
37
+ }
38
+ export function isModelRoutingEnabled(refs) {
39
+ const routing = getRoutingObject(refs);
40
+ return routing?.modelRouting === true;
41
+ }
42
+ function toStringList(value) {
43
+ if (typeof value === "string" && value.trim()) {
44
+ return [value];
45
+ }
46
+ if (!Array.isArray(value)) {
47
+ return undefined;
48
+ }
49
+ const items = value
50
+ .filter((item) => typeof item === "string" && item.trim().length > 0)
51
+ .map((item) => item.trim());
52
+ return items.length > 0 ? items : undefined;
53
+ }
54
+ function toPositiveNumber(value) {
55
+ return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : undefined;
56
+ }
57
+ export function getRoutingRules(refs) {
58
+ const routing = getRoutingObject(refs);
59
+ if (!Array.isArray(routing?.rules)) {
60
+ return [];
61
+ }
62
+ return routing.rules.flatMap((rule, index) => {
63
+ if (typeof rule !== "object" || rule === null || Array.isArray(rule)) {
64
+ throw new Error(`Runtime routing.rules[${index}] must be an object`);
65
+ }
66
+ const typed = rule;
67
+ if (typeof typed.agentId !== "string" || !typed.agentId.trim()) {
68
+ throw new Error(`Runtime routing.rules[${index}] requires a non-empty agentId`);
69
+ }
70
+ const regex = toStringList(typed.regex);
71
+ if (regex) {
72
+ for (const pattern of regex) {
73
+ try {
74
+ new RegExp(pattern);
75
+ }
76
+ catch (error) {
77
+ const detail = error instanceof Error ? error.message : String(error);
78
+ throw new Error(`Runtime routing.rules[${index}] has invalid regex ${JSON.stringify(pattern)}: ${detail}`);
79
+ }
80
+ }
81
+ }
82
+ return [{
83
+ agentId: typed.agentId.trim(),
84
+ equals: toStringList(typed.equals),
85
+ startsWith: toStringList(typed.startsWith),
86
+ contains: toStringList(typed.contains),
87
+ regex,
88
+ minLength: toPositiveNumber(typed.minLength),
89
+ maxLength: toPositiveNumber(typed.maxLength),
90
+ minLines: toPositiveNumber(typed.minLines),
91
+ maxLines: toPositiveNumber(typed.maxLines),
92
+ hasThreadId: typeof typed.hasThreadId === "boolean" ? typed.hasThreadId : undefined,
93
+ caseSensitive: typed.caseSensitive === true,
94
+ }];
95
+ });
96
+ }
97
+ export function matchRoutingRule(input, rule, options = {}) {
98
+ const text = input.trim();
99
+ const source = rule.caseSensitive ? text : text.toLowerCase();
100
+ const lineCount = text ? text.split(/\n+/).length : 0;
101
+ const normalize = (value) => (rule.caseSensitive ? value : value.toLowerCase());
102
+ if (rule.hasThreadId !== undefined && rule.hasThreadId !== Boolean(options.threadId)) {
103
+ return false;
104
+ }
105
+ if (rule.minLength !== undefined && text.length < rule.minLength) {
106
+ return false;
107
+ }
108
+ if (rule.maxLength !== undefined && text.length > rule.maxLength) {
109
+ return false;
110
+ }
111
+ if (rule.minLines !== undefined && lineCount < rule.minLines) {
112
+ return false;
113
+ }
114
+ if (rule.maxLines !== undefined && lineCount > rule.maxLines) {
115
+ return false;
116
+ }
117
+ if (rule.equals && !rule.equals.some((candidate) => source === normalize(candidate.trim()))) {
118
+ return false;
119
+ }
120
+ if (rule.startsWith && !rule.startsWith.some((candidate) => source.startsWith(normalize(candidate.trim())))) {
121
+ return false;
122
+ }
123
+ if (rule.contains && !rule.contains.some((candidate) => source.includes(normalize(candidate.trim())))) {
124
+ return false;
125
+ }
126
+ if (rule.regex && !rule.regex.some((pattern) => new RegExp(pattern, rule.caseSensitive ? undefined : "i").test(text))) {
127
+ return false;
128
+ }
129
+ return true;
130
+ }
29
131
  export function resolvePromptValue(promptConfig) {
30
132
  if (typeof promptConfig === "string" && promptConfig.trim()) {
31
133
  return promptConfig;
@@ -33,10 +135,14 @@ export function resolvePromptValue(promptConfig) {
33
135
  return undefined;
34
136
  }
35
137
  export function resolveRefId(ref) {
36
- if (ref.startsWith("builtin/")) {
138
+ const [namespace, ...rest] = ref.split("/");
139
+ if (rest.length === 0) {
37
140
  return ref;
38
141
  }
39
- return ref.split("/").slice(1).join("/");
142
+ if (["agent", "embedding-model", "mcp", "model", "object", "prompt", "runtime", "tool", "vector-store"].includes(namespace)) {
143
+ return rest.join("/");
144
+ }
145
+ return ref;
40
146
  }
41
147
  export function resolvePathList(refs, workspaceRoot, refList) {
42
148
  return refList.map((ref) => {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@botbotgo/agent-harness",
3
- "version": "0.0.38",
4
- "description": "Agent Harness framework package",
3
+ "version": "0.0.40",
4
+ "description": "Workspace runtime for multi-agent applications",
5
5
  "type": "module",
6
6
  "packageManager": "npm@10.9.2",
7
7
  "main": "./dist/index.js",
@@ -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/tool-extension-gaps.test.ts test/checkpoint-maintenance.test.ts test/llamaindex-dependency-compat.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/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",
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",