@botbotgo/agent-harness 0.0.62 → 0.0.63

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/dist/cli.d.ts ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env node
2
+ type CliIo = {
3
+ cwd?: string;
4
+ stdout?: (message: string) => void;
5
+ stderr?: (message: string) => void;
6
+ };
7
+ export declare function runCli(argv: string[], io?: CliIo): Promise<number>;
8
+ export {};
package/dist/cli.js ADDED
@@ -0,0 +1,91 @@
1
+ #!/usr/bin/env node
2
+ import path from "node:path";
3
+ import { pathToFileURL } from "node:url";
4
+ import { initProject } from "./init-project.js";
5
+ function renderUsage() {
6
+ return `Usage:
7
+ agent-harness init <project-name> [--template deep-research|single-agent] [--provider <provider>] [--model <model>] [--with-web-search|--no-web-search]
8
+ `;
9
+ }
10
+ function isTemplate(value) {
11
+ return value === "deep-research" || value === "single-agent";
12
+ }
13
+ function parseInitOptions(args) {
14
+ const options = {};
15
+ for (let index = 0; index < args.length; index += 1) {
16
+ const arg = args[index];
17
+ if (arg === "--with-web-search") {
18
+ options.withWebSearch = true;
19
+ continue;
20
+ }
21
+ if (arg === "--no-web-search") {
22
+ options.withWebSearch = false;
23
+ continue;
24
+ }
25
+ if (arg === "--template" || arg === "--provider" || arg === "--model") {
26
+ const value = args[index + 1];
27
+ if (!value) {
28
+ return { error: `Missing value for ${arg}` };
29
+ }
30
+ if (arg === "--template") {
31
+ if (!isTemplate(value)) {
32
+ return { error: `Unsupported template: ${value}` };
33
+ }
34
+ options.template = value;
35
+ }
36
+ else if (arg === "--provider") {
37
+ options.provider = value;
38
+ }
39
+ else {
40
+ options.model = value;
41
+ }
42
+ index += 1;
43
+ continue;
44
+ }
45
+ return { error: `Unknown option: ${arg}` };
46
+ }
47
+ return { options };
48
+ }
49
+ export async function runCli(argv, io = {}) {
50
+ const cwd = io.cwd ?? process.cwd();
51
+ const stdout = io.stdout ?? ((message) => process.stdout.write(message));
52
+ const stderr = io.stderr ?? ((message) => process.stderr.write(message));
53
+ const [command, projectName, ...rest] = argv;
54
+ if (command === "init") {
55
+ if (!projectName?.trim()) {
56
+ stderr(renderUsage());
57
+ return 1;
58
+ }
59
+ const parsed = parseInitOptions(rest);
60
+ if (parsed.error) {
61
+ stderr(`${parsed.error}\n`);
62
+ stderr(renderUsage());
63
+ return 1;
64
+ }
65
+ try {
66
+ const projectRoot = path.resolve(cwd, projectName);
67
+ const result = await initProject(projectRoot, projectName, parsed.options);
68
+ stdout(`Created ${result.projectSlug} at ${result.projectRoot}\n`);
69
+ stdout("Next steps:\n");
70
+ stdout(` cd ${projectName}\n`);
71
+ stdout(" npm install\n");
72
+ if ((parsed.options?.provider ?? "openai") === "openai") {
73
+ stdout(" export OPENAI_API_KEY=your_key_here\n");
74
+ }
75
+ stdout(' npm run start -- "Research a topic"\n');
76
+ return 0;
77
+ }
78
+ catch (error) {
79
+ const message = error instanceof Error ? error.message : String(error);
80
+ stderr(`${message}\n`);
81
+ return 1;
82
+ }
83
+ }
84
+ stderr(renderUsage());
85
+ return 1;
86
+ }
87
+ const invokedPath = process.argv[1] ? pathToFileURL(path.resolve(process.argv[1])).href : "";
88
+ if (import.meta.url === invokedPath) {
89
+ const exitCode = await runCli(process.argv.slice(2));
90
+ process.exitCode = exitCode;
91
+ }
@@ -0,0 +1,12 @@
1
+ export type InitTemplate = "deep-research" | "single-agent";
2
+ export type InitProjectOptions = {
3
+ template?: InitTemplate;
4
+ provider?: string;
5
+ model?: string;
6
+ withWebSearch?: boolean;
7
+ };
8
+ export type InitProjectResult = {
9
+ projectRoot: string;
10
+ projectSlug: string;
11
+ };
12
+ export declare function initProject(projectRoot: string, projectName: string, options?: InitProjectOptions): Promise<InitProjectResult>;
@@ -0,0 +1,292 @@
1
+ import path from "node:path";
2
+ import { writeFile } from "node:fs/promises";
3
+ import { AGENT_HARNESS_VERSION } from "./package-version.js";
4
+ import { ensureDir, fileExists } from "./utils/fs.js";
5
+ function toProjectSlug(projectName) {
6
+ const normalized = projectName
7
+ .trim()
8
+ .toLowerCase()
9
+ .replace(/[^a-z0-9]+/g, "-")
10
+ .replace(/^-+|-+$/g, "");
11
+ return normalized || "agent-harness-app";
12
+ }
13
+ function toDisplayName(projectName) {
14
+ return projectName
15
+ .trim()
16
+ .split(/[-_\s]+/)
17
+ .filter(Boolean)
18
+ .map((part) => part.charAt(0).toUpperCase() + part.slice(1))
19
+ .join(" ");
20
+ }
21
+ function resolveOptions(options = {}) {
22
+ return {
23
+ template: options.template ?? "deep-research",
24
+ provider: options.provider?.trim() || "openai",
25
+ model: options.model?.trim() || "gpt-4o-mini",
26
+ withWebSearch: options.withWebSearch ?? true,
27
+ };
28
+ }
29
+ function renderPackageJson(projectSlug, template) {
30
+ return `${JSON.stringify({
31
+ name: projectSlug,
32
+ version: "0.0.1",
33
+ private: true,
34
+ type: "module",
35
+ description: `Starter ${template} application generated by agent-harness init.`,
36
+ scripts: {
37
+ start: "node ./src/run.mjs",
38
+ },
39
+ dependencies: {
40
+ "@botbotgo/agent-harness": `^${AGENT_HARNESS_VERSION}`,
41
+ },
42
+ }, null, 2)}\n`;
43
+ }
44
+ function renderReadme(displayName, projectSlug, options) {
45
+ const webSearchLine = options.withWebSearch
46
+ ? `- ${options.provider} native web search enabled through \`config/tools.yaml\``
47
+ : "- no provider-native web search by default";
48
+ const providerEnvLine = options.provider === "openai" ? "export OPENAI_API_KEY=your_key_here\n" : "";
49
+ const templateLine = options.template === "deep-research"
50
+ ? "- a host research agent plus a specialist analyst subagent"
51
+ : "- a single host research agent for direct iteration";
52
+ return `# ${displayName}
53
+
54
+ This project was scaffolded with \`agent-harness init ${projectSlug}\`.
55
+
56
+ It is a minimal ${options.template} workspace with:
57
+
58
+ ${templateLine}
59
+ ${webSearchLine}
60
+
61
+ ## Run
62
+
63
+ \`\`\`bash
64
+ npm install
65
+ ${providerEnvLine}npm run start -- "Research the latest model serving stack for agent products and summarize the tradeoffs."
66
+ \`\`\`
67
+
68
+ ## Structure
69
+
70
+ - \`src/run.mjs\`: minimal launcher
71
+ - \`config/\`: workspace runtime and agent topology
72
+ - \`resources/\`: place local tools and skills here as your product grows
73
+
74
+ ## Customize
75
+
76
+ - change \`config/models.yaml\` to switch models or providers
77
+ - add local tools under \`resources/tools/\`
78
+ - add product-specific skills under \`resources/skills/\`
79
+ - edit the prompts in \`config/agents/\` to turn this into your own product
80
+ `;
81
+ }
82
+ function renderGitignore() {
83
+ return `node_modules
84
+ .agent
85
+ `;
86
+ }
87
+ function renderWorkspaceYaml() {
88
+ return `apiVersion: agent-harness/v1alpha1
89
+ kind: Runtime
90
+ metadata:
91
+ name: default
92
+ spec:
93
+ runRoot: ./.agent
94
+ `;
95
+ }
96
+ function renderAgentContext(options) {
97
+ const webSearchGuidance = options.withWebSearch
98
+ ? "- Favor recent, attributable sources when the request depends on current information."
99
+ : "- Favor clear internal reasoning and add search or local tools as your product needs evolve.";
100
+ return `# Deep Research Workspace Context
101
+
102
+ - This workspace is a starter for product-oriented research applications.
103
+ ${webSearchGuidance}
104
+ - Synthesize findings clearly, call out uncertainty, and separate facts from inference.
105
+ `;
106
+ }
107
+ function renderModelsYaml(options) {
108
+ return `apiVersion: agent-harness/v1alpha1
109
+ kind: Models
110
+ spec:
111
+ - name: default
112
+ kind: Model
113
+ provider: ${options.provider}
114
+ model: ${options.model}
115
+ temperature: 0.2
116
+ `;
117
+ }
118
+ function renderBackendsYaml() {
119
+ return `apiVersion: agent-harness/v1alpha1
120
+ kind: Backends
121
+ spec:
122
+ - kind: Backend
123
+ name: default
124
+ description: Default DeepAgent backend preset for generated research apps.
125
+ backendKind: CompositeBackend
126
+ state:
127
+ kind: VfsSandbox
128
+ timeout: 600
129
+ routes:
130
+ /memories/:
131
+ kind: StoreBackend
132
+ `;
133
+ }
134
+ function renderToolsYaml(options) {
135
+ if (!options.withWebSearch) {
136
+ return `apiVersion: agent-harness/v1alpha1
137
+ kind: Tools
138
+ spec: []
139
+ `;
140
+ }
141
+ return `apiVersion: agent-harness/v1alpha1
142
+ kind: Tools
143
+ spec:
144
+ - kind: Tool
145
+ name: web-search
146
+ type: provider
147
+ description: ${options.provider} web search tool for current research.
148
+ providerTool:
149
+ provider: ${options.provider}
150
+ tool: webSearch
151
+ args:
152
+ searchContextSize: medium
153
+ `;
154
+ }
155
+ function renderResearchAgentYaml(options) {
156
+ const toolsBlock = options.withWebSearch ? " tools:\n - ref: tool/web-search\n" : " tools: []\n";
157
+ const subagentsBlock = options.template === "deep-research" ? " subagents:\n - ref: agent/research-analyst\n" : " subagents: []\n";
158
+ const prompt = options.withWebSearch
159
+ ? "Break complex research requests into a clear plan, use web search when current information matters, and return a concise synthesis with sources and explicit uncertainty."
160
+ : "Break complex research requests into a clear plan and return a concise synthesis with explicit assumptions and uncertainty.";
161
+ const delegationLine = options.template === "deep-research"
162
+ ? " Delegate detailed investigation to the analyst when useful."
163
+ : "";
164
+ return `apiVersion: agent-harness/v1alpha1
165
+ kind: Agent
166
+ metadata:
167
+ name: research
168
+ description: Host-facing research agent for investigating and synthesizing answers.
169
+ spec:
170
+ execution:
171
+ backend: deepagent
172
+ modelRef: model/default
173
+ ${toolsBlock} skills:
174
+ - path: ./
175
+ ${subagentsBlock} config:
176
+ backend:
177
+ ref: backend/default
178
+ systemPrompt: ${prompt}${delegationLine}
179
+ `;
180
+ }
181
+ function renderResearchAnalystYaml(options) {
182
+ const toolsBlock = options.withWebSearch ? " tools:\n - ref: tool/web-search\n" : " tools: []\n";
183
+ const prompt = options.withWebSearch
184
+ ? "Gather current sources, compare claims carefully, extract the most decision-relevant facts, and return clean notes the host agent can synthesize."
185
+ : "Break down the problem, compare alternatives carefully, extract the most decision-relevant facts, and return clean notes the host agent can synthesize.";
186
+ return `apiVersion: agent-harness/v1alpha1
187
+ kind: Agent
188
+ metadata:
189
+ name: research-analyst
190
+ description: Specialist subagent for source gathering, comparison, and evidence extraction.
191
+ spec:
192
+ execution:
193
+ backend: deepagent
194
+ modelRef: model/default
195
+ ${toolsBlock} skills:
196
+ - path: ./
197
+ config:
198
+ backend:
199
+ ref: backend/default
200
+ systemPrompt: ${prompt}
201
+ `;
202
+ }
203
+ function renderResourcePackageJson(projectSlug) {
204
+ return `${JSON.stringify({
205
+ name: `${projectSlug}-resources`,
206
+ version: "0.0.1",
207
+ private: true,
208
+ type: "module",
209
+ description: "Resource package for a generated agent-harness project.",
210
+ }, null, 2)}\n`;
211
+ }
212
+ function renderSkill(options) {
213
+ const stepTwo = options.withWebSearch
214
+ ? "2. Use `web-search` to collect recent primary or otherwise authoritative sources."
215
+ : "2. Collect the key facts and assumptions needed to answer the request cleanly.";
216
+ return `---
217
+ name: deep-research
218
+ description: Default research workflow for product investigations that need careful synthesis.
219
+ ---
220
+
221
+ # Deep Research
222
+
223
+ Use this skill when the user wants comparisons, source-backed analysis, or a concise brief.
224
+
225
+ ## Workflow
226
+
227
+ 1. Clarify the target question and what "done" looks like.
228
+ ${stepTwo}
229
+ 3. Compare evidence instead of trusting a single source.
230
+ 4. Separate verified facts from inference.
231
+ 5. End with a concise synthesis, explicit caveats, and source links when available.
232
+ `;
233
+ }
234
+ function renderRunScript(options) {
235
+ const defaultInput = options.withWebSearch
236
+ ? "Research the latest model and tooling trends for AI product teams. Return a concise brief with sources and caveats."
237
+ : "Analyze the current architecture direction for this product and return a concise brief with assumptions and caveats.";
238
+ return `import { fileURLToPath } from "node:url";
239
+ import { createAgentHarness, run, stop } from "@botbotgo/agent-harness";
240
+
241
+ const appRoot = fileURLToPath(new URL("..", import.meta.url));
242
+ const input = process.argv.slice(2).join(" ").trim() ||
243
+ ${JSON.stringify(defaultInput)};
244
+
245
+ const runtime = await createAgentHarness(appRoot);
246
+
247
+ try {
248
+ const result = await run(runtime, {
249
+ agentId: "research",
250
+ input,
251
+ });
252
+ process.stdout.write(\`\${result.output}\\n\`);
253
+ } finally {
254
+ await stop(runtime);
255
+ }
256
+ `;
257
+ }
258
+ export async function initProject(projectRoot, projectName, options = {}) {
259
+ if (await fileExists(projectRoot)) {
260
+ throw new Error(`Target path already exists: ${projectRoot}`);
261
+ }
262
+ const resolved = resolveOptions(options);
263
+ const projectSlug = toProjectSlug(projectName);
264
+ const displayName = toDisplayName(projectName) || "Agent Harness App";
265
+ const files = new Map([
266
+ ["package.json", renderPackageJson(projectSlug, resolved.template)],
267
+ [".gitignore", renderGitignore()],
268
+ ["README.md", renderReadme(displayName, projectSlug, resolved)],
269
+ ["src/run.mjs", renderRunScript(resolved)],
270
+ ["config/workspace.yaml", renderWorkspaceYaml()],
271
+ ["config/agent-context.md", renderAgentContext(resolved)],
272
+ ["config/models.yaml", renderModelsYaml(resolved)],
273
+ ["config/backends.yaml", renderBackendsYaml()],
274
+ ["config/tools.yaml", renderToolsYaml(resolved)],
275
+ ["config/agents/research.yaml", renderResearchAgentYaml(resolved)],
276
+ ["resources/package.json", renderResourcePackageJson(projectSlug)],
277
+ ["resources/skills/deep-research/SKILL.md", renderSkill(resolved)],
278
+ ]);
279
+ if (resolved.template === "deep-research") {
280
+ files.set("config/agents/research-analyst.yaml", renderResearchAnalystYaml(resolved));
281
+ }
282
+ await ensureDir(projectRoot);
283
+ for (const [relativePath, content] of files) {
284
+ const filePath = path.join(projectRoot, relativePath);
285
+ await ensureDir(path.dirname(filePath));
286
+ await writeFile(filePath, content, "utf8");
287
+ }
288
+ return {
289
+ projectRoot,
290
+ projectSlug,
291
+ };
292
+ }
@@ -1 +1 @@
1
- export declare const AGENT_HARNESS_VERSION = "0.0.61";
1
+ export declare const AGENT_HARNESS_VERSION = "0.0.62";
@@ -1 +1 @@
1
- export const AGENT_HARNESS_VERSION = "0.0.61";
1
+ export const AGENT_HARNESS_VERSION = "0.0.62";
package/package.json CHANGED
@@ -1,11 +1,14 @@
1
1
  {
2
2
  "name": "@botbotgo/agent-harness",
3
- "version": "0.0.62",
3
+ "version": "0.0.63",
4
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",
8
8
  "types": "./dist/index.d.ts",
9
+ "bin": {
10
+ "agent-harness": "./dist/cli.js"
11
+ },
9
12
  "files": [
10
13
  "dist"
11
14
  ],
@@ -50,7 +53,7 @@
50
53
  "scripts": {
51
54
  "build": "rm -rf dist tsconfig.tsbuildinfo && tsc -p tsconfig.json && cp -R config dist/",
52
55
  "check": "tsc -p tsconfig.json --noEmit",
53
- "test": "vitest run test/hello-file.test.ts 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/stock-research-app-config.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/product-boundary-docs.test.ts test/docs-site.test.ts test/runtime-adapter-regressions.test.ts test/runtime-capabilities.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/yaml-format.test.ts",
56
+ "test": "vitest run test/hello-file.test.ts 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/stock-research-app-config.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/product-boundary-docs.test.ts test/docs-site.test.ts test/runtime-adapter-regressions.test.ts test/runtime-capabilities.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/yaml-format.test.ts test/config-secrets.test.ts test/init-command.test.ts",
54
57
  "test:real-providers": "vitest run test/real-provider-harness.test.ts",
55
58
  "release:prepare": "npm version patch --no-git-tag-version && node ./scripts/sync-example-version.mjs",
56
59
  "release:pack": "npm pack --dry-run",