@botbotgo/agent-harness 0.0.37 → 0.0.39

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 (38) hide show
  1. package/README.md +79 -137
  2. package/dist/api.d.ts +15 -12
  3. package/dist/api.js +25 -41
  4. package/dist/config/embedding-model.yaml +2 -2
  5. package/dist/config/workspace.yaml +37 -3
  6. package/dist/contracts/types.d.ts +17 -7
  7. package/dist/index.d.ts +1 -1
  8. package/dist/index.js +1 -1
  9. package/dist/mcp.d.ts +8 -3
  10. package/dist/mcp.js +10 -11
  11. package/dist/package-version.d.ts +1 -1
  12. package/dist/package-version.js +1 -1
  13. package/dist/resource/resource-impl.d.ts +2 -1
  14. package/dist/resource/resource-impl.js +16 -11
  15. package/dist/resource/resource.d.ts +1 -1
  16. package/dist/resource/resource.js +1 -1
  17. package/dist/runtime/agent-runtime-adapter.js +4 -2
  18. package/dist/runtime/event-bus.d.ts +5 -4
  19. package/dist/runtime/event-bus.js +7 -9
  20. package/dist/runtime/event-sink.d.ts +9 -0
  21. package/dist/runtime/event-sink.js +35 -0
  22. package/dist/runtime/harness.d.ts +21 -12
  23. package/dist/runtime/harness.js +81 -31
  24. package/dist/runtime/index.d.ts +3 -1
  25. package/dist/runtime/index.js +2 -1
  26. package/dist/runtime/parsing/stream-event-parsing.d.ts +2 -0
  27. package/dist/runtime/parsing/stream-event-parsing.js +35 -3
  28. package/dist/runtime/support/harness-support.d.ts +1 -0
  29. package/dist/runtime/support/harness-support.js +38 -4
  30. package/dist/runtime/thread-memory-sync.d.ts +4 -2
  31. package/dist/runtime/thread-memory-sync.js +10 -1
  32. package/dist/workspace/agent-binding-compiler.js +0 -3
  33. package/dist/workspace/compile.js +0 -1
  34. package/dist/workspace/support/discovery.js +11 -8
  35. package/dist/workspace/support/source-collectors.js +1 -1
  36. package/dist/workspace/support/workspace-ref-utils.d.ts +19 -0
  37. package/dist/workspace/support/workspace-ref-utils.js +112 -6
  38. package/package.json +3 -3
@@ -50,6 +50,31 @@ function summarizeStepValue(value) {
50
50
  function humanizeEventName(name) {
51
51
  return name.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/[._-]+/g, " ").replace(/\s+/g, " ").trim().toLowerCase();
52
52
  }
53
+ function readToolErrorText(value) {
54
+ if (typeof value === "string") {
55
+ return value;
56
+ }
57
+ if (value instanceof Error) {
58
+ return value.message;
59
+ }
60
+ if (typeof value !== "object" || !value) {
61
+ return "";
62
+ }
63
+ const record = value;
64
+ for (const candidate of [record.error, record.message, record.detail, record.details]) {
65
+ if (typeof candidate === "string" && candidate.trim()) {
66
+ return candidate;
67
+ }
68
+ }
69
+ return "";
70
+ }
71
+ function isErrorLikeToolOutput(value) {
72
+ const message = readToolErrorText(value).trim();
73
+ if (!message) {
74
+ return false;
75
+ }
76
+ return /(^|\b)(error|exception|failed|failure|denied|timed out|timeout|not permitted|eperm|eacces)(\b|:)/i.test(message);
77
+ }
53
78
  export function extractTerminalStreamOutput(event) {
54
79
  if (typeof event !== "object" || !event)
55
80
  return "";
@@ -109,6 +134,10 @@ export function extractAgentStep(event) {
109
134
  const outputSummary = formatStepValue(typed.data?.output, 80);
110
135
  return outputSummary ? `tool ${name || "tool"} done ${outputSummary}` : `tool ${name || "tool"} done`;
111
136
  }
137
+ if (typed.event === "on_tool_error" || (typed.event === "on_chain_error" && typed.run_type === "tool")) {
138
+ const errorSummary = formatStepValue(typed.data?.error ?? typed.data?.output, 80);
139
+ return errorSummary ? `tool ${name || "tool"} error ${errorSummary}` : `tool ${name || "tool"} error`;
140
+ }
112
141
  if (typed.event === "on_chat_model_start") {
113
142
  const inputTools = typed.data?.input?.tools;
114
143
  const tools = typed.invocation_params?.tools ?? inputTools ?? [];
@@ -148,14 +177,17 @@ export function extractToolResult(event) {
148
177
  return null;
149
178
  const typed = event;
150
179
  const isToolEnd = typed.event === "on_tool_end" || (typed.event === "on_chain_end" && typed.run_type === "tool");
180
+ const isToolError = typed.event === "on_tool_error" || (typed.event === "on_chain_error" && typed.run_type === "tool");
151
181
  const toolName = typeof typed.name === "string" ? typed.name : "";
152
- if (!isToolEnd || !toolName) {
182
+ if ((!isToolEnd && !isToolError) || !toolName) {
153
183
  return null;
154
184
  }
155
- const rawOutput = typed.data?.output;
185
+ const rawOutput = isToolError ? typed.data?.error ?? typed.data?.output : typed.data?.output;
186
+ const normalizedOutput = typeof rawOutput === "string" ? parseMaybeJson(rawOutput) : rawOutput;
156
187
  return {
157
188
  toolName,
158
- output: typeof rawOutput === "string" ? parseMaybeJson(rawOutput) : rawOutput,
189
+ output: normalizedOutput,
190
+ isError: isToolError || isErrorLikeToolOutput(normalizedOutput),
159
191
  };
160
192
  }
161
193
  export function extractInterruptPayload(event) {
@@ -1,5 +1,6 @@
1
1
  import type { ApprovalRecord, HarnessEvent, WorkspaceBundle } from "../../contracts/types.js";
2
2
  export declare function renderRuntimeFailure(error: unknown): string;
3
+ export declare function renderToolFailure(toolName: string, output: unknown): string;
3
4
  export declare function parseInterruptContent(content: string): {
4
5
  toolName?: string;
5
6
  toolId?: string;
@@ -3,6 +3,33 @@ export function renderRuntimeFailure(error) {
3
3
  const message = error instanceof Error ? error.message : String(error);
4
4
  return `runtime_error=${message}`.trim();
5
5
  }
6
+ function readToolErrorMessage(output) {
7
+ if (typeof output === "string" && output.trim()) {
8
+ return output.trim();
9
+ }
10
+ if (output instanceof Error) {
11
+ return output.message;
12
+ }
13
+ if (typeof output !== "object" || !output) {
14
+ return "";
15
+ }
16
+ const record = output;
17
+ for (const candidate of [record.error, record.message, record.detail, record.details]) {
18
+ if (typeof candidate === "string" && candidate.trim()) {
19
+ return candidate.trim();
20
+ }
21
+ }
22
+ try {
23
+ return JSON.stringify(output);
24
+ }
25
+ catch {
26
+ return String(output);
27
+ }
28
+ }
29
+ export function renderToolFailure(toolName, output) {
30
+ const detail = readToolErrorMessage(output);
31
+ return detail ? `Tool ${toolName} failed: ${detail}` : `Tool ${toolName} failed.`;
32
+ }
6
33
  export function parseInterruptContent(content) {
7
34
  try {
8
35
  const raw = JSON.parse(content);
@@ -106,11 +133,18 @@ export function inferRoutingBindings(workspace) {
106
133
  const directBinding = hostBindings.find((binding) => binding.agent.id === "direct");
107
134
  const langchainHost = hostBindings.find((binding) => binding.agent.executionMode === "langchain-v1" && binding.agent.id !== researchBinding?.agent.id);
108
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];
109
139
  const deepagentWithSubagents = deepagentHosts.find((binding) => (binding.deepAgentParams?.subagents.length ?? 0) > 0) ??
110
140
  deepagentHosts[0];
111
- const primaryBinding = directBinding ?? langchainHost ?? hostBindings[0];
112
- const secondaryBinding = deepagentWithSubagents && deepagentWithSubagents.agent.id !== primaryBinding?.agent.id
113
- ? deepagentWithSubagents
114
- : hostBindings.find((binding) => binding.agent.id !== primaryBinding?.agent.id);
141
+ const primaryBinding = defaultDeepagentHost ?? directBinding ?? langchainHost ?? hostBindings[0];
142
+ const secondaryBinding = langchainHost && langchainHost.agent.id !== primaryBinding?.agent.id
143
+ ? langchainHost
144
+ : directBinding && directBinding.agent.id !== primaryBinding?.agent.id
145
+ ? directBinding
146
+ : deepagentWithSubagents && deepagentWithSubagents.agent.id !== primaryBinding?.agent.id
147
+ ? deepagentWithSubagents
148
+ : hostBindings.find((binding) => binding.agent.id !== primaryBinding?.agent.id);
115
149
  return { primaryBinding, secondaryBinding, researchBinding, hostBindings };
116
150
  }
@@ -1,12 +1,14 @@
1
- import type { HarnessEvent } from "../contracts/types.js";
1
+ import type { HarnessEvent, HarnessEventProjection } from "../contracts/types.js";
2
2
  import type { FilePersistence } from "../persistence/file-store.js";
3
- export declare class ThreadMemorySync {
3
+ export declare class ThreadMemorySync implements HarnessEventProjection {
4
4
  private readonly persistence;
5
5
  private readonly store?;
6
6
  private readonly pending;
7
+ readonly name = "thread-memory-sync";
7
8
  constructor(persistence: FilePersistence, store?: {
8
9
  put: (namespace: string[], key: string, value: Record<string, any>) => Promise<void>;
9
10
  } | undefined);
11
+ shouldHandle(event: HarnessEvent): boolean;
10
12
  handleEvent(event: HarnessEvent): Promise<void>;
11
13
  private syncThread;
12
14
  close(): Promise<void>;
@@ -42,16 +42,25 @@ function renderOpenApprovalsMarkdown(approvals) {
42
42
  }
43
43
  return lines.join("\n");
44
44
  }
45
+ const THREAD_MEMORY_EVENT_TYPES = new Set([
46
+ "run.state.changed",
47
+ "approval.resolved",
48
+ "approval.requested",
49
+ ]);
45
50
  export class ThreadMemorySync {
46
51
  persistence;
47
52
  store;
48
53
  pending = new Set();
54
+ name = "thread-memory-sync";
49
55
  constructor(persistence, store) {
50
56
  this.persistence = persistence;
51
57
  this.store = store;
52
58
  }
59
+ shouldHandle(event) {
60
+ return THREAD_MEMORY_EVENT_TYPES.has(event.eventType);
61
+ }
53
62
  async handleEvent(event) {
54
- if (event.eventType !== "run.state.changed" && event.eventType !== "approval.resolved" && event.eventType !== "approval.requested") {
63
+ if (!this.shouldHandle(event)) {
55
64
  return;
56
65
  }
57
66
  const task = this.syncThread(event.threadId)
@@ -52,9 +52,6 @@ function resolveAgentRuntimeName(agent) {
52
52
  return baseName;
53
53
  }
54
54
  const normalizedSourcePath = agent.sourcePath.split(path.sep).join("/");
55
- if (normalizedSourcePath.includes("/packages/builtins/")) {
56
- return `builtin.${baseName}`;
57
- }
58
55
  if (normalizedSourcePath.includes("/packages/framework/")) {
59
56
  return `core.${baseName}`;
60
57
  }
@@ -87,7 +87,6 @@ export async function loadWorkspace(workspaceRoot, options = {}) {
87
87
  return {
88
88
  workspaceRoot,
89
89
  resourceSources: [...toolSourceRefs],
90
- builtinSources: [...toolSourceRefs],
91
90
  refs: loaded.refs,
92
91
  embeddings,
93
92
  mcpServers,
@@ -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.37",
4
- "description": "Agent Harness framework package",
3
+ "version": "0.0.39",
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",